1
0

More instant page navigator

This commit is contained in:
Zef Hemel 2023-12-22 15:55:50 +01:00
parent 8a181145ad
commit 3350c7f076
11 changed files with 79 additions and 60 deletions

View File

@ -97,11 +97,6 @@ export async function extractFrontmatter(
return undefined; return undefined;
}); });
if (data.name) {
data.displayName = data.name;
delete data.name;
}
return data; return data;
} }

View File

@ -46,31 +46,31 @@ export async function pageComplete(completeEvent: CompleteEvent) {
const completions: any[] = []; const completions: any[] = [];
if (pageMeta.displayName) { if (pageMeta.displayName) {
completions.push({ completions.push({
label: pageMeta.displayName, label: `${pageMeta.displayName}`,
boost: pageMeta.lastModified, boost: new Date(pageMeta.lastModified).getTime(),
apply: isInTemplateContext apply: isInTemplateContext
? pageMeta.name ? pageMeta.name
: `${pageMeta.name}|${pageMeta.displayName}`, : `${pageMeta.name}|${pageMeta.displayName}`,
detail: "alias", detail: `displayName: ${pageMeta.name}`,
type: "page", type: "page",
}); });
} }
if (Array.isArray(pageMeta.aliases)) { if (Array.isArray(pageMeta.aliases)) {
for (const alias of pageMeta.aliases) { for (const alias of pageMeta.aliases) {
completions.push({ completions.push({
label: alias, label: `${alias}`,
boost: pageMeta.lastModified, boost: new Date(pageMeta.lastModified).getTime(),
apply: isInTemplateContext apply: isInTemplateContext
? pageMeta.name ? pageMeta.name
: `${pageMeta.name}|${alias}`, : `${pageMeta.name}|${alias}`,
detail: "alias", detail: `alias: ${pageMeta.name}`,
type: "page", type: "page",
}); });
} }
} }
completions.push({ completions.push({
label: pageMeta.name, label: `${pageMeta.name}`,
boost: pageMeta.lastModified, boost: new Date(pageMeta.lastModified).getTime(),
type: "page", type: "page",
}); });
return completions; return completions;

View File

@ -137,7 +137,7 @@ export class ServerSystem {
); );
this.listInterval = setInterval(() => { this.listInterval = setInterval(() => {
space.updatePageListCache().catch(console.error); space.updatePageList().catch(console.error);
}, fileListInterval); }, fileListInterval);
eventHook.addLocalListener("file:changed", (path, localChange) => { eventHook.addLocalListener("file:changed", (path, localChange) => {

View File

@ -107,6 +107,9 @@ export class Client {
spaceDataStore!: DataStore; spaceDataStore!: DataStore;
mq!: DataStoreMQ; mq!: DataStoreMQ;
// Used by the "wiki link" highlighter to check if a page exists
public allKnownPages = new Set<string>();
constructor( constructor(
private parent: Element, private parent: Element,
public syncMode = false, public syncMode = false,
@ -226,6 +229,8 @@ export class Client {
console.error("Interval sync error", e); console.error("Interval sync error", e);
} }
}, pageSyncInterval); }, pageSyncInterval);
this.updatePageListCache().catch(console.error);
} }
private initSync() { private initSync() {
@ -235,7 +240,7 @@ export class Client {
// console.log("Operations", operations); // console.log("Operations", operations);
if (operations > 0) { if (operations > 0) {
// Update the page list // Update the page list
await this.space.updatePageListCache(); await this.space.updatePageList();
} }
if (operations !== undefined) { if (operations !== undefined) {
// "sync:success" is called with a number of operations only from syncSpace(), not from syncing individual pages // "sync:success" is called with a number of operations only from syncSpace(), not from syncing individual pages
@ -499,6 +504,28 @@ export class Client {
}, },
); );
// Caching a list of known pages for the wiki_link highlighter (that checks if a page exists)
this.eventHook.addLocalListener("page:saved", (pageName: string) => {
// Make sure this page is in the list of known pages
this.allKnownPages.add(pageName);
});
this.eventHook.addLocalListener("page:deleted", (pageName: string) => {
this.allKnownPages.delete(pageName);
});
this.eventHook.addLocalListener(
"file:listed",
(allFiles: FileMeta[]) => {
// Update list of known pages
this.allKnownPages = new Set(
allFiles.filter((f) => f.name.endsWith(".md")).map((f) =>
f.name.slice(0, -3)
),
);
},
);
this.space.watch(); this.space.watch();
return localSpacePrimitives; return localSpacePrimitives;
@ -582,11 +609,19 @@ export class Client {
); );
} }
async startPageNavigate() { startPageNavigate() {
// Fetch all pages from the index
const pages = await this.system.queryObjects<PageMeta>("page", {});
// Then show the page navigator // Then show the page navigator
this.ui.viewDispatch({ type: "start-navigate", pages }); this.ui.viewDispatch({ type: "start-navigate" });
this.updatePageListCache().catch(console.error);
}
async updatePageListCache() {
console.log("Updating page list cache");
const allPages = await this.system.queryObjects<PageMeta>("page", {});
this.ui.viewDispatch({
type: "update-page-list",
allPages,
});
} }
private progressTimeout?: number; private progressTimeout?: number;
@ -744,6 +779,7 @@ export class Client {
actualResult = result; actualResult = result;
} }
} }
// console.log("Compeltion result", actualResult);
return actualResult; return actualResult;
} }

View File

@ -13,7 +13,7 @@ import { resolvePath } from "$sb/lib/resolve.ts";
/** /**
* Plugin to hide path prefix when the cursor is not inside. * Plugin to hide path prefix when the cursor is not inside.
*/ */
export function cleanWikiLinkPlugin(editor: Client) { export function cleanWikiLinkPlugin(client: Client) {
return decoratorStateField((state) => { return decoratorStateField((state) => {
const widgets: any[] = []; const widgets: any[] = [];
// let parentRange: [number, number]; // let parentRange: [number, number];
@ -28,21 +28,20 @@ export function cleanWikiLinkPlugin(editor: Client) {
if (!match) return; if (!match) return;
const [_fullMatch, page, pipePart, alias] = match; const [_fullMatch, page, pipePart, alias] = match;
const allPages = editor.space.listPages(); let pageExists = !client.fullSyncCompleted;
let pageExists = !editor.fullSyncCompleted;
let cleanPage = page; let cleanPage = page;
cleanPage = page.split(/[@$]/)[0]; cleanPage = page.split(/[@$]/)[0];
cleanPage = resolvePath(editor.currentPage!, cleanPage); cleanPage = resolvePath(client.currentPage!, cleanPage);
const lowerCasePageName = cleanPage.toLowerCase(); const lowerCasePageName = cleanPage.toLowerCase();
for (const pageMeta of allPages) { for (const pageName of client.allKnownPages) {
if (pageMeta.name.toLowerCase() === lowerCasePageName) { if (pageName.toLowerCase() === lowerCasePageName) {
pageExists = true; pageExists = true;
break; break;
} }
} }
if ( if (
cleanPage === "" || cleanPage === "" ||
editor.plugSpaceRemotePrimitives.isLikelyHandled(cleanPage) client.plugSpaceRemotePrimitives.isLikelyHandled(cleanPage)
) { ) {
// Empty page name with local @anchor use or a link to a page that dynamically generated by a plug // Empty page name with local @anchor use or a link to a page that dynamically generated by a plug
pageExists = true; pageExists = true;
@ -90,19 +89,19 @@ export function cleanWikiLinkPlugin(editor: Client) {
callback: (e) => { callback: (e) => {
if (e.altKey) { if (e.altKey) {
// Move cursor into the link // Move cursor into the link
return editor.editorView.dispatch({ return client.editorView.dispatch({
selection: { anchor: from + 2 }, selection: { anchor: from + 2 },
}); });
} }
// Dispatch click event to navigate there without moving the cursor // Dispatch click event to navigate there without moving the cursor
const clickEvent: ClickEvent = { const clickEvent: ClickEvent = {
page: editor.currentPage!, page: client.currentPage!,
ctrlKey: e.ctrlKey, ctrlKey: e.ctrlKey,
metaKey: e.metaKey, metaKey: e.metaKey,
altKey: e.altKey, altKey: e.altKey,
pos: from, pos: from,
}; };
editor.dispatchAppEvent("page:click", clickEvent).catch( client.dispatchAppEvent("page:click", clickEvent).catch(
console.error, console.error,
); );
}, },

View File

@ -57,7 +57,7 @@ export function createEditorState(
// Keyboard shortcuts from SETTINGS take precedense // Keyboard shortcuts from SETTINGS take precedense
if (client.settings?.keyboardShortcuts) { if (client.settings?.keyboardShortcuts) {
for (const shortcut of client.settings.keyboardShortcuts) { for (const shortcut of client.settings.keyboardShortcuts) {
console.info("Configuring keyboard shortcut", shortcut); // console.info("Configuring keyboard shortcut", shortcut);
commandKeyBindings.push({ commandKeyBindings.push({
key: shortcut.key, key: shortcut.key,
mac: shortcut.mac, mac: shortcut.mac,
@ -215,7 +215,7 @@ export function createEditorState(
key: "Ctrl-k", key: "Ctrl-k",
mac: "Cmd-k", mac: "Cmd-k",
run: (): boolean => { run: (): boolean => {
client.startPageNavigate().catch(console.error); client.startPageNavigate();
return true; return true;
}, },
}, },

View File

@ -44,7 +44,7 @@ export class MainUI {
if (ev.touches.length === 2) { if (ev.touches.length === 2) {
ev.stopPropagation(); ev.stopPropagation();
ev.preventDefault(); ev.preventDefault();
client.startPageNavigate().catch(console.error); client.startPageNavigate();
} }
// Launch the command palette using a three-finger tap // Launch the command palette using a three-finger tap
if (ev.touches.length === 3) { if (ev.touches.length === 3) {
@ -251,7 +251,7 @@ export class MainUI {
icon: BookIcon, icon: BookIcon,
description: `Open page (${isMacLike() ? "Cmd-k" : "Ctrl-k"})`, description: `Open page (${isMacLike() ? "Cmd-k" : "Ctrl-k"})`,
callback: () => { callback: () => {
client.startPageNavigate().catch(console.error); client.startPageNavigate();
}, },
}, },
{ {

View File

@ -45,20 +45,26 @@ export default function reducer(
...state, ...state,
syncFailures: action.syncSuccess ? 0 : state.syncFailures + 1, syncFailures: action.syncSuccess ? 0 : state.syncFailures + 1,
}; };
case "start-navigate": { case "update-page-list": {
// Let's move over any "lastOpened" times to the "allPages" list // Let's move over any "lastOpened" times to the "allPages" list
const oldPageMeta = new Map( const oldPageMeta = new Map(
[...state.allPages].map((pm) => [pm.name, pm]), [...state.allPages].map((pm) => [pm.name, pm]),
); );
for (const pageMeta of action.pages) { for (const pageMeta of action.allPages) {
const oldPageMetaItem = oldPageMeta.get(pageMeta.name); const oldPageMetaItem = oldPageMeta.get(pageMeta.name);
if (oldPageMetaItem && oldPageMetaItem.lastOpened) { if (oldPageMetaItem && oldPageMetaItem.lastOpened) {
pageMeta.lastOpened = oldPageMetaItem.lastOpened; pageMeta.lastOpened = oldPageMetaItem.lastOpened;
} }
} }
return {
...state,
allPages: action.allPages,
};
}
case "start-navigate": {
return { return {
...state, ...state,
allPages: action.pages,
showPageNavigator: true, showPageNavigator: true,
showCommandPalette: false, showCommandPalette: false,
showFilterBox: false, showFilterBox: false,

View File

@ -14,9 +14,6 @@ export class Space {
imageHeightCache = new LimitedMap<number>(100); // url -> height imageHeightCache = new LimitedMap<number>(100); // url -> height
widgetHeightCache = new LimitedMap<number>(100); // bodytext -> height widgetHeightCache = new LimitedMap<number>(100); // bodytext -> height
// Note: this is "clean" PageMeta, it doesn't contain custom attributes (it's fetched from the store)
private cachedPageList: PageMeta[] = [];
debouncedImageCacheFlush = throttle(() => { debouncedImageCacheFlush = throttle(() => {
this.ds.set(["cache", "imageHeight"], this.imageHeightCache).catch( this.ds.set(["cache", "imageHeight"], this.imageHeightCache).catch(
console.error, console.error,
@ -72,25 +69,18 @@ export class Space {
this.widgetHeightCache = new LimitedMap(100, widgetCache); this.widgetHeightCache = new LimitedMap(100, widgetCache);
} }
}); });
// eventHook.addLocalListener("file:listed", (files: FileMeta[]) => {
// // console.log("Files listed", files);
// this.cachedPageList = files.filter(this.isListedPage).map(
// fileMetaToPageMeta,
// );
// });
eventHook.addLocalListener("page:deleted", (pageName: string) => { eventHook.addLocalListener("page:deleted", (pageName: string) => {
if (this.watchedPages.has(pageName)) { if (this.watchedPages.has(pageName)) {
// Stop watching deleted pages already // Stop watching deleted pages already
this.watchedPages.delete(pageName); this.watchedPages.delete(pageName);
} }
}); });
this.updatePageListCache().catch(console.error); this.updatePageList().catch(console.error);
} }
public async updatePageListCache() { public async updatePageList() {
console.log("Updating page list cache"); // The only reason to do this is to trigger events
// This will trigger appropriate events automatically await this.fetchPageList();
this.cachedPageList = await this.fetchPageList();
} }
async deletePage(name: string): Promise<void> { async deletePage(name: string): Promise<void> {
@ -104,10 +94,6 @@ export class Space {
); );
} }
listPages(): PageMeta[] {
return this.cachedPageList;
}
async listPlugs(): Promise<FileMeta[]> { async listPlugs(): Promise<FileMeta[]> {
const files = await this.deduplicatedFileList(); const files = await this.deduplicatedFileList();
return files return files
@ -139,10 +125,6 @@ export class Space {
selfUpdate, selfUpdate,
), ),
); );
if (!this.cachedPageList.find((page) => page.name === pageMeta.name)) {
// New page, let's cache it
this.cachedPageList.push(pageMeta);
}
// Note: we don't do very elaborate cache invalidation work here, quite quickly the cache will be flushed anyway // Note: we don't do very elaborate cache invalidation work here, quite quickly the cache will be flushed anyway
return pageMeta; return pageMeta;
} finally { } finally {

View File

@ -382,7 +382,7 @@ export class NoSyncSyncService implements ISyncService {
start() { start() {
setInterval(() => { setInterval(() => {
// Trigger a page upload for change events // Trigger a page upload for change events
this.space.updatePageListCache().catch(console.error); this.space.updatePageList().catch(console.error);
}, spaceSyncInterval); }, spaceSyncInterval);
} }

View File

@ -122,7 +122,8 @@ export type Action =
| { type: "page-changed" } | { type: "page-changed" }
| { type: "page-saved" } | { type: "page-saved" }
| { type: "sync-change"; syncSuccess: boolean } | { type: "sync-change"; syncSuccess: boolean }
| { type: "start-navigate"; pages: PageMeta[] } | { type: "update-page-list"; allPages: PageMeta[] }
| { type: "start-navigate" }
| { type: "stop-navigate" } | { type: "stop-navigate" }
| { | {
type: "update-commands"; type: "update-commands";