+110
-81
@@ -13,7 +13,7 @@ import { FilterOption } from "./types.ts";
|
||||
import { ensureSettingsAndIndex } from "../common/util.ts";
|
||||
import { EventHook } from "../plugos/hooks/event.ts";
|
||||
import { AppCommand } from "./hooks/command.ts";
|
||||
import { PathPageNavigator } from "./navigator.ts";
|
||||
import { PageState, PathPageNavigator } from "./navigator.ts";
|
||||
|
||||
import { AppViewState, BuiltinSettings } from "./types.ts";
|
||||
|
||||
@@ -32,10 +32,9 @@ import { SyncStatus } from "../common/spaces/sync.ts";
|
||||
import { HttpSpacePrimitives } from "../common/spaces/http_space_primitives.ts";
|
||||
import { FallbackSpacePrimitives } from "../common/spaces/fallback_space_primitives.ts";
|
||||
import { FilteredSpacePrimitives } from "../common/spaces/filtered_space_primitives.ts";
|
||||
import { validatePageName } from "$sb/lib/page.ts";
|
||||
import { encodePageRef, validatePageName } from "$sb/lib/page.ts";
|
||||
import { ClientSystem } from "./client_system.ts";
|
||||
import { createEditorState } from "./editor_state.ts";
|
||||
import { OpenPages } from "./open_pages.ts";
|
||||
import { MainUI } from "./editor_ui.tsx";
|
||||
import { cleanPageRef } from "$sb/lib/resolve.ts";
|
||||
import { SpacePrimitives } from "../common/spaces/space_primitives.ts";
|
||||
@@ -55,6 +54,7 @@ import {
|
||||
import { LimitedMap } from "$sb/lib/limited_map.ts";
|
||||
import { renderHandlebarsTemplate } from "../common/syscalls/handlebars.ts";
|
||||
import { buildQueryFunctions } from "../common/query_functions.ts";
|
||||
import { PageRef } from "$sb/lib/page.ts";
|
||||
const frontMatterRegex = /^---\n(([^\n]|\n)*?)---\n/;
|
||||
|
||||
const autoSaveInterval = 1000;
|
||||
@@ -71,6 +71,8 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
// history.scrollRestoration = "manual";
|
||||
|
||||
export class Client {
|
||||
system!: ClientSystem;
|
||||
editorView!: EditorView;
|
||||
@@ -113,7 +115,6 @@ export class Client {
|
||||
eventHook!: EventHook;
|
||||
|
||||
ui!: MainUI;
|
||||
openPages!: OpenPages;
|
||||
stateDataStore!: DataStore;
|
||||
spaceDataStore!: DataStore;
|
||||
mq!: DataStoreMQ;
|
||||
@@ -192,8 +193,6 @@ export class Client {
|
||||
parent: document.getElementById("sb-editor")!,
|
||||
});
|
||||
|
||||
this.openPages = new OpenPages(this);
|
||||
|
||||
this.focus();
|
||||
|
||||
await this.system.init();
|
||||
@@ -312,71 +311,98 @@ export class Client {
|
||||
);
|
||||
}
|
||||
|
||||
private navigateWithinPage(pageState: PageState) {
|
||||
// Did we end up doing anything in terms of internal navigation?
|
||||
let adjustedPosition = false;
|
||||
|
||||
// Was a particular scroll position persisted?
|
||||
if (pageState.scrollTop !== undefined) {
|
||||
setTimeout(() => {
|
||||
console.log("Kicking off scroll to", pageState.scrollTop);
|
||||
this.editorView.scrollDOM.scrollTop = pageState.scrollTop!;
|
||||
});
|
||||
adjustedPosition = true;
|
||||
}
|
||||
|
||||
// Was a particular cursor/selection set?
|
||||
if (pageState.selection?.anchor && !pageState.pos && !pageState.anchor) { // Only do this if we got a specific cursor position
|
||||
console.log("Changing cursor position to", pageState.selection);
|
||||
this.editorView.dispatch({
|
||||
selection: pageState.selection,
|
||||
});
|
||||
adjustedPosition = true;
|
||||
}
|
||||
|
||||
// Was there a pos or anchor set?
|
||||
let pos: number | undefined = pageState.pos;
|
||||
if (pageState.anchor) {
|
||||
console.log("Navigating to anchor", pageState.anchor);
|
||||
const pageText = this.editorView.state.sliceDoc();
|
||||
|
||||
pos = pageText.indexOf(`$${pageState.anchor}`);
|
||||
|
||||
if (pos === -1) {
|
||||
return this.flashNotification(
|
||||
`Could not find anchor $${pageState.anchor}`,
|
||||
"error",
|
||||
);
|
||||
}
|
||||
|
||||
adjustedPosition = true;
|
||||
}
|
||||
if (pos !== undefined) {
|
||||
// setTimeout(() => {
|
||||
console.log("Doing this pos set to", pos);
|
||||
this.editorView.dispatch({
|
||||
selection: { anchor: pos! },
|
||||
effects: EditorView.scrollIntoView(pos!, {
|
||||
y: "start",
|
||||
yMargin: 5,
|
||||
}),
|
||||
});
|
||||
adjustedPosition = true;
|
||||
// });
|
||||
}
|
||||
|
||||
// If not: just put the cursor at the top of the page, right after the frontmatter
|
||||
if (!adjustedPosition) {
|
||||
// Somewhat ad-hoc way to determine if the document contains frontmatter and if so, putting the cursor _after it_.
|
||||
const pageText = this.editorView.state.sliceDoc();
|
||||
|
||||
// Default the cursor to be at position 0
|
||||
let initialCursorPos = 0;
|
||||
const match = frontMatterRegex.exec(pageText);
|
||||
if (match) {
|
||||
// Frontmatter found, put cursor after it
|
||||
initialCursorPos = match[0].length;
|
||||
}
|
||||
// By default scroll to the top
|
||||
console.log("Scrolling to place after frontmatter", initialCursorPos);
|
||||
this.editorView.scrollDOM.scrollTop = 0;
|
||||
this.editorView.dispatch({
|
||||
selection: { anchor: initialCursorPos },
|
||||
// And then scroll down if required
|
||||
scrollIntoView: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private initNavigator() {
|
||||
this.pageNavigator = new PathPageNavigator(
|
||||
cleanPageRef(renderHandlebarsTemplate(this.settings.indexPage, {}, {})),
|
||||
);
|
||||
this.pageNavigator = new PathPageNavigator(this);
|
||||
|
||||
this.pageNavigator.subscribe(
|
||||
async (pageName, pos: number | string | undefined) => {
|
||||
console.log("Now navigating to", pageName);
|
||||
this.pageNavigator.subscribe(async (pageState) => {
|
||||
console.log("Now navigating to", pageState);
|
||||
|
||||
const stateRestored = await this.loadPage(pageName, pos === undefined);
|
||||
if (pos) {
|
||||
if (typeof pos === "string") {
|
||||
console.log("Navigating to anchor", pos);
|
||||
await this.loadPage(pageState.page);
|
||||
|
||||
// We're going to look up the anchor through a API invocation
|
||||
const matchingAnchor = await this.system.system.localSyscall(
|
||||
"system.invokeFunction",
|
||||
[
|
||||
"index.getObjectByRef",
|
||||
pageName,
|
||||
"anchor",
|
||||
`${pageName}$${pos}`,
|
||||
],
|
||||
);
|
||||
// Setup scroll position, cursor position, etc
|
||||
this.navigateWithinPage(pageState);
|
||||
|
||||
if (!matchingAnchor) {
|
||||
return this.flashNotification(
|
||||
`Could not find anchor $${pos}`,
|
||||
"error",
|
||||
);
|
||||
} else {
|
||||
pos = matchingAnchor.pos as number;
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.editorView.dispatch({
|
||||
selection: { anchor: pos as number },
|
||||
effects: EditorView.scrollIntoView(pos as number, {
|
||||
y: "start",
|
||||
yMargin: 5,
|
||||
}),
|
||||
});
|
||||
});
|
||||
} else if (!stateRestored) {
|
||||
// Somewhat ad-hoc way to determine if the document contains frontmatter and if so, putting the cursor _after it_.
|
||||
const pageText = this.editorView.state.sliceDoc();
|
||||
|
||||
// Default the cursor to be at position 0
|
||||
let initialCursorPos = 0;
|
||||
const match = frontMatterRegex.exec(pageText);
|
||||
if (match) {
|
||||
// Frontmatter found, put cursor after it
|
||||
initialCursorPos = match[0].length;
|
||||
}
|
||||
// By default scroll to the top
|
||||
this.editorView.scrollDOM.scrollTop = 0;
|
||||
this.editorView.dispatch({
|
||||
selection: { anchor: initialCursorPos },
|
||||
// And then scroll down if required
|
||||
scrollIntoView: true,
|
||||
});
|
||||
}
|
||||
await this.stateDataStore.set(["client", "lastOpenedPage"], pageName);
|
||||
},
|
||||
);
|
||||
await this.stateDataStore.set(
|
||||
["client", "lastOpenedPage"],
|
||||
pageState.page,
|
||||
);
|
||||
});
|
||||
|
||||
if (location.hash === "#boot") {
|
||||
(async () => {
|
||||
@@ -760,7 +786,7 @@ export class Client {
|
||||
|
||||
if (this.currentPage) {
|
||||
// And update the editor if a page is loaded
|
||||
this.openPages.saveState(this.currentPage);
|
||||
// this.openPages.saveState(this.currentPage);
|
||||
|
||||
editorView.setState(
|
||||
createEditorState(
|
||||
@@ -776,7 +802,7 @@ export class Client {
|
||||
);
|
||||
}
|
||||
|
||||
this.openPages.restoreState(this.currentPage);
|
||||
// this.openPages.restoreState(this.currentPage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -865,35 +891,41 @@ export class Client {
|
||||
}
|
||||
|
||||
async navigate(
|
||||
name: string,
|
||||
pos?: number | string,
|
||||
pageRef: PageRef,
|
||||
replaceState = false,
|
||||
newWindow = false,
|
||||
) {
|
||||
if (!name) {
|
||||
name = cleanPageRef(
|
||||
if (!pageRef.page) {
|
||||
pageRef.page = cleanPageRef(
|
||||
renderHandlebarsTemplate(this.settings.indexPage, {}, {}),
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const pagePart = name.split(/[@$]/)[0];
|
||||
validatePageName(pagePart);
|
||||
validatePageName(pageRef.page);
|
||||
} catch (e: any) {
|
||||
return this.flashNotification(e.message, "error");
|
||||
}
|
||||
|
||||
if (newWindow) {
|
||||
const win = window.open(`${location.origin}/${name}`, "_blank");
|
||||
const win = window.open(
|
||||
`${location.origin}/${encodePageRef(pageRef)}`,
|
||||
"_blank",
|
||||
);
|
||||
if (win) {
|
||||
win.focus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
await this.pageNavigator!.navigate(name, pos, replaceState);
|
||||
|
||||
await this.pageNavigator!.navigate(
|
||||
pageRef,
|
||||
replaceState,
|
||||
);
|
||||
this.focus();
|
||||
}
|
||||
|
||||
async loadPage(pageName: string, restoreState = true): Promise<boolean> {
|
||||
async loadPage(pageName: string) {
|
||||
const loadingDifferentPage = pageName !== this.currentPage;
|
||||
const editorView = this.editorView;
|
||||
const previousPage = this.currentPage;
|
||||
@@ -902,7 +934,7 @@ export class Client {
|
||||
|
||||
// Persist current page state and nicely close page
|
||||
if (previousPage) {
|
||||
this.openPages.saveState(previousPage);
|
||||
// this.openPages.saveState(previousPage);
|
||||
this.space.unwatchPage(previousPage);
|
||||
if (previousPage !== pageName) {
|
||||
await this.save(true);
|
||||
@@ -972,7 +1004,6 @@ export class Client {
|
||||
if (editorView.contentDOM) {
|
||||
this.tweakEditorDOM(editorView.contentDOM);
|
||||
}
|
||||
const stateRestored = restoreState && this.openPages.restoreState(pageName);
|
||||
this.space.watchPage(pageName);
|
||||
|
||||
// Note: these events are dispatched asynchronously deliberately (not waiting for results)
|
||||
@@ -986,8 +1017,6 @@ export class Client {
|
||||
console.error,
|
||||
);
|
||||
}
|
||||
|
||||
return stateRestored;
|
||||
}
|
||||
|
||||
tweakEditorDOM(contentDOM: HTMLElement) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { renderMarkdownToHtml } from "../../plugs/markdown/markdown_render.ts";
|
||||
import { resolveAttachmentPath } from "$sb/lib/resolve.ts";
|
||||
import { parse } from "../../common/markdown_parser/parse_tree.ts";
|
||||
import buildMarkdown from "../../common/markdown_parser/parser.ts";
|
||||
import { parsePageRef } from "$sb/lib/page.ts";
|
||||
|
||||
const activeWidgets = new Set<MarkdownWidget>();
|
||||
|
||||
@@ -152,12 +153,8 @@ export class MarkdownWidget extends WidgetType {
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const [pageName, pos] = el.dataset.ref!.split(/[$@]/);
|
||||
if (pos && pos.match(/^\d+$/)) {
|
||||
this.client.navigate(pageName, +pos);
|
||||
} else {
|
||||
this.client.navigate(pageName, pos);
|
||||
}
|
||||
const pageRef = parsePageRef(el.dataset.ref!);
|
||||
this.client.navigate(pageRef);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
LinkWidget,
|
||||
} from "./util.ts";
|
||||
import { resolvePath } from "$sb/lib/resolve.ts";
|
||||
import { parsePageRef } from "$sb/lib/page.ts";
|
||||
|
||||
/**
|
||||
* Plugin to hide path prefix when the cursor is not inside.
|
||||
@@ -30,7 +31,7 @@ export function cleanWikiLinkPlugin(client: Client) {
|
||||
|
||||
let pageExists = !client.fullSyncCompleted;
|
||||
let cleanPage = page;
|
||||
cleanPage = page.split(/[@$]/)[0];
|
||||
cleanPage = parsePageRef(page).page;
|
||||
cleanPage = resolvePath(client.currentPage!, cleanPage);
|
||||
const lowerCasePageName = cleanPage.toLowerCase();
|
||||
for (const pageName of client.allKnownPages) {
|
||||
|
||||
+2
-3
@@ -21,7 +21,6 @@ import type { Client } from "./client.ts";
|
||||
import { Panel } from "./components/panel.tsx";
|
||||
import { h } from "./deps.ts";
|
||||
import { sleep } from "$sb/lib/async.ts";
|
||||
import { template } from "https://esm.sh/v132/handlebars@4.7.7/runtime.d.ts";
|
||||
|
||||
export class MainUI {
|
||||
viewState: AppViewState = initialViewState;
|
||||
@@ -112,7 +111,7 @@ export class MainUI {
|
||||
});
|
||||
if (page) {
|
||||
safeRun(async () => {
|
||||
await client.navigate(page);
|
||||
await client.navigate({ page });
|
||||
});
|
||||
}
|
||||
}}
|
||||
@@ -246,7 +245,7 @@ export class MainUI {
|
||||
icon: HomeIcon,
|
||||
description: `Go to the index page (Alt-h)`,
|
||||
callback: () => {
|
||||
client.navigate("", 0);
|
||||
client.navigate({ page: "", pos: 0 });
|
||||
// And let's make sure all panels are closed
|
||||
dispatch({ type: "hide-filterbox" });
|
||||
},
|
||||
|
||||
+112
-53
@@ -1,43 +1,77 @@
|
||||
import { safeRun } from "../common/util.ts";
|
||||
import { PageRef, parsePageRef } from "$sb/lib/page.ts";
|
||||
import { Client } from "./client.ts";
|
||||
import { cleanPageRef } from "$sb/lib/resolve.ts";
|
||||
import { renderHandlebarsTemplate } from "../common/syscalls/handlebars.ts";
|
||||
|
||||
function encodePageUrl(name: string): string {
|
||||
return name;
|
||||
}
|
||||
|
||||
function decodePageUrl(url: string): string {
|
||||
return url;
|
||||
}
|
||||
export type PageState = PageRef & {
|
||||
scrollTop?: number;
|
||||
selection?: {
|
||||
anchor: number;
|
||||
head?: number;
|
||||
};
|
||||
};
|
||||
|
||||
export class PathPageNavigator {
|
||||
navigationResolve?: () => void;
|
||||
root: string;
|
||||
indexPage: string;
|
||||
|
||||
constructor(readonly indexPage: string, readonly root: string = "") {}
|
||||
openPages = new Map<string, PageState>();
|
||||
|
||||
constructor(
|
||||
private client: Client,
|
||||
) {
|
||||
this.root = "";
|
||||
this.indexPage = cleanPageRef(
|
||||
renderHandlebarsTemplate(client.settings.indexPage, {}, {}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates the client to the given page, this involves:
|
||||
* - Patching the current popstate with current state
|
||||
* - Pushing the new state
|
||||
* - Dispatching a popstate event
|
||||
* @param pageRef to navigate to
|
||||
* @param replaceState whether to update the state in place (rather than to push a new state)
|
||||
*/
|
||||
async navigate(
|
||||
page: string,
|
||||
pos?: number | string | undefined,
|
||||
pageRef: PageRef,
|
||||
replaceState = false,
|
||||
) {
|
||||
let encodedPage = encodePageUrl(page);
|
||||
if (page === this.indexPage) {
|
||||
encodedPage = "";
|
||||
if (pageRef.page === this.indexPage) {
|
||||
pageRef.page = "";
|
||||
}
|
||||
if (replaceState) {
|
||||
const currentState = this.buildCurrentPageState();
|
||||
// No need to keep pos and anchor if we already have scrollTop and selection
|
||||
const cleanState = { ...currentState, pos: undefined, anchor: undefined };
|
||||
this.openPages.set(currentState.page || this.indexPage, cleanState);
|
||||
if (!replaceState) {
|
||||
console.log("Updating current state", currentState);
|
||||
window.history.replaceState(
|
||||
{ page },
|
||||
page,
|
||||
`${this.root}/${encodedPage}`,
|
||||
cleanState,
|
||||
"",
|
||||
`${this.root}/${currentState.page}`,
|
||||
);
|
||||
console.log("Pushing new state", pageRef);
|
||||
window.history.pushState(
|
||||
pageRef,
|
||||
"",
|
||||
`${this.root}/${pageRef.page}`,
|
||||
);
|
||||
} else {
|
||||
window.history.pushState(
|
||||
{ page },
|
||||
page,
|
||||
`${this.root}/${encodedPage}`,
|
||||
// console.log("Replacing state", pageRef);
|
||||
window.history.replaceState(
|
||||
pageRef,
|
||||
"",
|
||||
`${this.root}/${pageRef.page}`,
|
||||
);
|
||||
}
|
||||
// console.log("Explicitly dispatching the popstate", pageRef);
|
||||
globalThis.dispatchEvent(
|
||||
new PopStateEvent("popstate", {
|
||||
state: { page, pos },
|
||||
state: pageRef,
|
||||
}),
|
||||
);
|
||||
await new Promise<void>((resolve) => {
|
||||
@@ -46,52 +80,77 @@ export class PathPageNavigator {
|
||||
this.navigationResolve = undefined;
|
||||
}
|
||||
|
||||
buildCurrentPageState(): PageState {
|
||||
const pageState: PageState = this.parseURI();
|
||||
const mainSelection = this.client.editorView.state.selection.main;
|
||||
pageState.scrollTop = this.client.editorView.scrollDOM.scrollTop;
|
||||
pageState.selection = {
|
||||
head: mainSelection.head,
|
||||
anchor: mainSelection.anchor,
|
||||
};
|
||||
return pageState;
|
||||
}
|
||||
|
||||
subscribe(
|
||||
pageLoadCallback: (
|
||||
pageName: string,
|
||||
pos: number | string | undefined,
|
||||
pageState: PageState,
|
||||
) => Promise<void>,
|
||||
): void {
|
||||
const cb = (event?: PopStateEvent) => {
|
||||
const gotoPage = this.getCurrentPage();
|
||||
if (!gotoPage) {
|
||||
return;
|
||||
}
|
||||
const cb = (event: PopStateEvent) => {
|
||||
safeRun(async () => {
|
||||
await pageLoadCallback(
|
||||
this.getCurrentPage(),
|
||||
event?.state?.pos,
|
||||
);
|
||||
const popState = event.state;
|
||||
if (popState) {
|
||||
// This is the usual case
|
||||
if (!popState.page) {
|
||||
popState.page = this.indexPage;
|
||||
}
|
||||
if (
|
||||
popState.anchor === undefined && popState.pos === undefined &&
|
||||
popState.selection === undefined &&
|
||||
popState.scrollTop === undefined
|
||||
) {
|
||||
// Pretty low-context popstate, so let's leverage openPages
|
||||
const openPage = this.openPages.get(popState.page);
|
||||
if (openPage) {
|
||||
console.log("Pulling open page state", openPage);
|
||||
popState.selection = openPage.selection;
|
||||
popState.scrollTop = openPage.scrollTop;
|
||||
}
|
||||
}
|
||||
console.log("Got popstate state, using", popState);
|
||||
await pageLoadCallback(popState);
|
||||
} else {
|
||||
// This occurs when the page is loaded completely fresh with no browser history around it
|
||||
// console.log("Got null state so using", this.parseURI());
|
||||
const pageRef = this.parseURI();
|
||||
if (!pageRef.page) {
|
||||
pageRef.page = this.indexPage;
|
||||
}
|
||||
await pageLoadCallback(pageRef);
|
||||
}
|
||||
if (this.navigationResolve) {
|
||||
this.navigationResolve();
|
||||
}
|
||||
});
|
||||
};
|
||||
globalThis.addEventListener("popstate", cb);
|
||||
cb();
|
||||
|
||||
cb(
|
||||
new PopStateEvent("popstate", {
|
||||
state: this.buildCurrentPageState(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
decodeURI(): [string, number | string] {
|
||||
const [page, pos] = decodeURI(
|
||||
parseURI(): PageRef {
|
||||
const pageRef = parsePageRef(decodeURI(
|
||||
location.pathname.substring(this.root.length + 1),
|
||||
).split(/[@$]/);
|
||||
if (pos) {
|
||||
if (pos.match(/^\d+$/)) {
|
||||
return [page, +pos];
|
||||
} else {
|
||||
return [page, pos];
|
||||
}
|
||||
} else {
|
||||
return [page, 0];
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
getCurrentPage(): string {
|
||||
return decodePageUrl(this.decodeURI()[0]) || this.indexPage;
|
||||
}
|
||||
// if (!pageRef.page) {
|
||||
// pageRef.page = this.indexPage;
|
||||
// }
|
||||
|
||||
getCurrentPos(): number | string {
|
||||
// console.log("Pos", this.decodeURI()[1]);
|
||||
return this.decodeURI()[1];
|
||||
return pageRef;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import { Client } from "./client.ts";
|
||||
import { EditorSelection } from "./deps.ts";
|
||||
|
||||
class PageState {
|
||||
constructor(
|
||||
readonly scrollTop: number,
|
||||
readonly selection: EditorSelection,
|
||||
) {}
|
||||
}
|
||||
|
||||
export class OpenPages {
|
||||
openPages = new Map<string, PageState>();
|
||||
|
||||
constructor(private client: Client) {}
|
||||
|
||||
restoreState(pageName: string): boolean {
|
||||
const pageState = this.openPages.get(pageName);
|
||||
const editorView = this.client.editorView;
|
||||
if (pageState) {
|
||||
// Restore state
|
||||
try {
|
||||
editorView.dispatch({
|
||||
selection: pageState.selection,
|
||||
// scrollIntoView: true,
|
||||
});
|
||||
} catch {
|
||||
// This is fine, just go to the top
|
||||
editorView.dispatch({
|
||||
selection: { anchor: 0 },
|
||||
scrollIntoView: true,
|
||||
});
|
||||
}
|
||||
setTimeout(() => {
|
||||
// Next tick, to allow the editor to process the render
|
||||
editorView.scrollDOM.scrollTop = pageState.scrollTop;
|
||||
});
|
||||
} else {
|
||||
editorView.scrollDOM.scrollTop = 0;
|
||||
editorView.dispatch({
|
||||
selection: { anchor: 0 },
|
||||
scrollIntoView: true,
|
||||
});
|
||||
}
|
||||
this.client.focus();
|
||||
return !!pageState;
|
||||
}
|
||||
|
||||
saveState(currentPage: string) {
|
||||
this.openPages.set(
|
||||
currentPage,
|
||||
new PageState(
|
||||
this.client.editorView.scrollDOM.scrollTop,
|
||||
this.client.editorView.state.selection,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import { SysCallMapping } from "../../plugos/system.ts";
|
||||
import type { FilterOption } from "../types.ts";
|
||||
import { UploadFile } from "../../plug-api/types.ts";
|
||||
import { PageRef } from "$sb/lib/page.ts";
|
||||
|
||||
export function editorSyscalls(client: Client): SysCallMapping {
|
||||
const syscalls: SysCallMapping = {
|
||||
@@ -33,12 +34,14 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
||||
},
|
||||
"editor.navigate": async (
|
||||
_ctx,
|
||||
name: string,
|
||||
pos: number | string,
|
||||
pageRef: PageRef | string,
|
||||
replaceState = false,
|
||||
newWindow = false,
|
||||
) => {
|
||||
await client.navigate(name, pos, replaceState, newWindow);
|
||||
if (typeof pageRef === "string") {
|
||||
pageRef = { page: pageRef };
|
||||
}
|
||||
await client.navigate(pageRef, replaceState, newWindow);
|
||||
},
|
||||
"editor.reloadPage": async () => {
|
||||
await client.reloadPage();
|
||||
|
||||
@@ -23,10 +23,10 @@ export function spaceSyscalls(editor: Client): SysCallMapping {
|
||||
"space.deletePage": async (_ctx, name: string) => {
|
||||
// If we're deleting the current page, navigate to the index page
|
||||
if (editor.currentPage === name) {
|
||||
await editor.navigate("");
|
||||
await editor.navigate({ page: "" });
|
||||
}
|
||||
// Remove page from open pages in editor
|
||||
editor.openPages.openPages.delete(name);
|
||||
// editor.openPages.openPages.delete(name);
|
||||
console.log("Deleting page");
|
||||
await editor.space.deletePage(name);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user