diff --git a/plugos/environments/sandbox_worker.ts b/plugos/environments/sandbox_worker.ts index acdb8bb..9bdeb0d 100644 --- a/plugos/environments/sandbox_worker.ts +++ b/plugos/environments/sandbox_worker.ts @@ -37,9 +37,8 @@ self.syscall = async (name: string, ...args: any[]) => { }); }; -function wrapScript(code: string): string { - return `const fn = ${code}; -return fn["default"].apply(null, arguments);`; +function wrapScript(code: string) { + return `return (${code})["default"]`; } self.addEventListener("message", (event: { data: WorkerMessage }) => { @@ -47,7 +46,8 @@ self.addEventListener("message", (event: { data: WorkerMessage }) => { let data = event.data; switch (data.type) { case "load": - loadedFunctions.set(data.name!, new Function(wrapScript(data.code!))); + let fn2 = new Function(wrapScript(data.code!)); + loadedFunctions.set(data.name!, fn2()); postMessage( { type: "inited", diff --git a/plugs/core/materialized_queries.ts b/plugs/core/materialized_queries.ts index cd24fdc..9bab2b8 100644 --- a/plugs/core/materialized_queries.ts +++ b/plugs/core/materialized_queries.ts @@ -79,8 +79,8 @@ export async function updateMaterializedQueriesOnPage(pageName: string) { for (let { key, page, - value: {item, children}, - } of await syscall("index.scanPrefixGlobal", "it:")) { + value: { item, children }, + }; of await syscall("index.scanPrefixGlobal", "it:")) { let [, pos] = key.split(":"); if (!filter || (filter && item.includes(filter))) { results.push(`* [[${page}@${pos}]] ${item}`); diff --git a/plugs/emoji/.gitignore b/plugs/emoji/.gitignore new file mode 100644 index 0000000..cd9aeac --- /dev/null +++ b/plugs/emoji/.gitignore @@ -0,0 +1 @@ +emoji-data.txt diff --git a/plugs/emoji/Makefile b/plugs/emoji/Makefile new file mode 100644 index 0000000..7468c95 --- /dev/null +++ b/plugs/emoji/Makefile @@ -0,0 +1,3 @@ +build: + curl https://unicode.org/Public/emoji/14.0/emoji-test.txt > emoji-data.txt + node build.js diff --git a/plugs/emoji/build.js b/plugs/emoji/build.js new file mode 100644 index 0000000..30106c0 --- /dev/null +++ b/plugs/emoji/build.js @@ -0,0 +1,17 @@ +// Generates emoji.json from emoji-data.txt +const { readFileSync, writeFileSync } = require("fs"); + +const emojiRe = /#\s([^\s]+)\s+E[^\s]+\s+(.+)$/; + +let text = readFileSync("emoji-data.txt", "utf-8"); +const lines = text.split("\n").filter((line) => !line.startsWith("#")); + +let emoji = []; +for (const line of lines) { + let match = emojiRe.exec(line); + if (match) { + emoji.push([match[1], match[2].toLowerCase().replaceAll(/\W+/g, "_")]); + } +} + +writeFileSync("emoji.json", JSON.stringify(emoji)); diff --git a/plugs/emoji/emoji.plug.yaml b/plugs/emoji/emoji.plug.yaml new file mode 100644 index 0000000..2f03abe --- /dev/null +++ b/plugs/emoji/emoji.plug.yaml @@ -0,0 +1,4 @@ +functions: + emojiCompleter: + path: "./emoji.ts:emojiCompleter" + isCompleter: true diff --git a/plugs/emoji/emoji.ts b/plugs/emoji/emoji.ts new file mode 100644 index 0000000..0233a81 --- /dev/null +++ b/plugs/emoji/emoji.ts @@ -0,0 +1,26 @@ +// @ts-ignore +import emojis from "./emoji.json"; +import { syscall } from "../lib/syscall"; + +const emojiMatcher = /\(([^\)]+)\)\s+(.+)$/; + +export async function emojiCompleter() { + let prefix = await syscall("editor.matchBefore", ":[\\w\\s]*"); + if (!prefix) { + return null; + } + const textPrefix = prefix.text.substring(1); // Cut off the initial : + let filteredEmoji = emojis.filter(([_, shortcode]) => + shortcode.includes(textPrefix) + ); + + return { + from: prefix.from, + filter: false, + options: filteredEmoji.map(([emoji, shortcode]) => ({ + detail: shortcode, + label: emoji, + type: "emoji", + })), + }; +} diff --git a/server/api_server.ts b/server/api_server.ts index ac38e5b..9ef4e66 100644 --- a/server/api_server.ts +++ b/server/api_server.ts @@ -86,7 +86,7 @@ export class ExpressServer { res.status(200); res.send(""); } - };) + }) .put(bodyParser.text({ type: "*/*" }), async (req, res) => { let pageName = req.params[0]; console.log("Saving", pageName); diff --git a/server/server.ts b/server/server.ts index 08475ec..79f875d 100755 --- a/server/server.ts +++ b/server/server.ts @@ -18,7 +18,7 @@ let args = yargs(hideBin(process.argv)) if (!args._.length) { console.error("Usage: silverbullet "); - process.exit(1); + process.exit(1); } const pagesPath = args._[0] as string; diff --git a/webapp/components/filter.tsx b/webapp/components/filter.tsx index 5ca4fe0..2efaf9b 100644 --- a/webapp/components/filter.tsx +++ b/webapp/components/filter.tsx @@ -51,6 +51,13 @@ function fuzzyFilter(pattern: string, options: Option[]): Option[] { return matches; } +function simpleFilter(pattern: string, options: Option[]): Option[] { + const lowerPattern = pattern.toLowerCase(); + return options.filter((option) => { + return option.name.toLowerCase().includes(lowerPattern); + }); +} + export function FilterList({ placeholder, options, @@ -59,6 +66,7 @@ export function FilterList({ onKeyPress, allowNew = false, helpText = "", + completePrefix, icon, newHint, }: { @@ -68,6 +76,7 @@ export function FilterList({ onKeyPress?: (key: string, currentText: string) => void; onSelect: (option: Option | undefined) => void; allowNew?: boolean; + completePrefix?: string; helpText: string; newHint?: string; icon?: IconDefinition; @@ -90,7 +99,7 @@ export function FilterList({ if (searchPhrase) { let foundExactMatch = false; - let results = fuzzyFilter(searchPhrase, options); + let results = simpleFilter(searchPhrase, options); results = results.sort(magicSorter); if (allowNew && !foundExactMatch) { results.push({ @@ -139,7 +148,7 @@ export function FilterList({ ref={searchBoxRef} onChange={filterUpdate} onKeyDown={(e: React.KeyboardEvent) => { - // console.log("Key up", e.key); + // console.log("Key up", e); if (onKeyPress) { onKeyPress(e.key, text); } @@ -159,6 +168,12 @@ export function FilterList({ case "Escape": onSelect(undefined); break; + case " ": + if (completePrefix) { + setText(completePrefix); + e.preventDefault(); + } + break; } }} /> diff --git a/webapp/components/page_navigator.tsx b/webapp/components/page_navigator.tsx index 8b3ae3e..c03e1a9 100644 --- a/webapp/components/page_navigator.tsx +++ b/webapp/components/page_navigator.tsx @@ -27,6 +27,11 @@ export function PageNavigator({ orderId: orderId, }); } + let completePrefix: string | undefined = undefined; + if (currentPage && currentPage.includes("/")) { + const pieces = currentPage.split("/"); + completePrefix = pieces.slice(0, pieces.length - 1).join("/") + "/"; + } return ( { onNavigate(opt?.name); }} diff --git a/webapp/hooks/completer.ts b/webapp/hooks/completer.ts index 9268877..6119760 100644 --- a/webapp/hooks/completer.ts +++ b/webapp/hooks/completer.ts @@ -17,23 +17,26 @@ export class CompleterHook implements Hook { continue; } for (const [functionName, functionDef] of Object.entries( - plug.manifest.functions + plug.manifest.functions )) { if (functionDef.isCompleter) { completerPromises.push(plug.invoke(functionName, [])); } } } - let allCompletionResults = await Promise.all(completerPromises); - if (allCompletionResults.length === 1) { - return allCompletionResults[0]; - } else if (allCompletionResults.length > 1) { - console.error( - "Got completion results from multiple sources, cannot deal with that", - allCompletionResults - ); + let actualResult = null; + for (const result of await Promise.all(completerPromises)) { + if (result) { + if (actualResult) { + console.error( + "Got completion results from multiple sources, cannot deal with that" + ); + return null; + } + actualResult = result; + } } - return null; + return actualResult; } apply(system: System): void { diff --git a/webapp/navigator.ts b/webapp/navigator.ts index 66b8e2e..c00fd60 100644 --- a/webapp/navigator.ts +++ b/webapp/navigator.ts @@ -12,12 +12,12 @@ export class PathPageNavigator { navigationResolve?: () => void; async navigate(page: string, pos?: number) { - window.history.pushState( - { page, pos }, - page, - `/${encodePageUrl(page)}${pos ? "@" + pos : ""}` + window.history.pushState({ page, pos }, page, `/${encodePageUrl(page)}`); + window.dispatchEvent( + new PopStateEvent("popstate", { + state: { page, pos }, + }) ); - window.dispatchEvent(new PopStateEvent("popstate")); await new Promise((resolve) => { this.navigationResolve = resolve; }); @@ -27,19 +27,22 @@ export class PathPageNavigator { subscribe( pageLoadCallback: (pageName: string, pos: number) => Promise ): void { - const cb = () => { - const gotoPage = this.getCurrentPage(); - if (!gotoPage) { - return; - } - safeRun(async () => { - await pageLoadCallback(this.getCurrentPage(), this.getCurrentPos()); - if (this.navigationResolve) { - this.navigationResolve(); + const cb = (event?: PopStateEvent) => { + const gotoPage = this.getCurrentPage(); + if (!gotoPage) { + return; } - }); - }; - window.addEventListener("popstate", cb); + safeRun(async () => { + await pageLoadCallback( + this.getCurrentPage(), + event && event.state.pos + ); + if (this.navigationResolve) { + this.navigationResolve(); + } + }); + }; + window.addEventListener("popstate", cb); cb(); }