diff --git a/packages/plugos-silverbullet-syscall/system.ts b/packages/plugos-silverbullet-syscall/system.ts index 3b12969a..709a226e 100644 --- a/packages/plugos-silverbullet-syscall/system.ts +++ b/packages/plugos-silverbullet-syscall/system.ts @@ -1,3 +1,4 @@ +import type { CommandDef } from "@silverbulletmd/web/hooks/command"; import { syscall } from "./syscall"; export async function invokeFunction( @@ -8,10 +9,16 @@ export async function invokeFunction( return syscall("system.invokeFunction", env, name, ...args); } +// Only available on the client export async function invokeCommand(name: string): Promise { return syscall("system.invokeCommand", name); } +// Only available on the client +export async function listCommands(): Promise<{ [key: string]: CommandDef }> { + return syscall("system.listCommands"); +} + export async function getVersion(): Promise { return syscall("system.getVersion"); } diff --git a/packages/plugs/core/command.ts b/packages/plugs/core/command.ts new file mode 100644 index 00000000..486d8a67 --- /dev/null +++ b/packages/plugs/core/command.ts @@ -0,0 +1,20 @@ +import { queryPrefix } from "@silverbulletmd/plugos-silverbullet-syscall"; +import { matchBefore } from "@silverbulletmd/plugos-silverbullet-syscall/editor"; +import { listCommands } from "@silverbulletmd/plugos-silverbullet-syscall/system"; +import { applyQuery, QueryProviderEvent } from "../query/engine"; + +export async function commandComplete() { + let prefix = await matchBefore("\\{\\[[^\\]]*"); + if (!prefix) { + return null; + } + let allCommands = await listCommands(); + + return { + from: prefix.from + 2, + options: Object.keys(allCommands).map((commandName) => ({ + label: commandName, + type: "command", + })), + }; +} diff --git a/packages/plugs/core/core.plug.yaml b/packages/plugs/core/core.plug.yaml index 72022f0c..39909aaf 100644 --- a/packages/plugs/core/core.plug.yaml +++ b/packages/plugs/core/core.plug.yaml @@ -74,6 +74,12 @@ functions: events: - page:complete + # Commands + commandComplete: + path: "./command.ts:commandComplete" + events: + - page:complete + # Item indexing indexItem: path: "./item.ts:indexItems" diff --git a/packages/plugs/core/navigate.ts b/packages/plugs/core/navigate.ts index 4e7d6404..c92286d5 100644 --- a/packages/plugs/core/navigate.ts +++ b/packages/plugs/core/navigate.ts @@ -11,6 +11,14 @@ import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markd import { nodeAtPos, ParseTree } from "@silverbulletmd/common/tree"; import { invokeCommand } from "@silverbulletmd/plugos-silverbullet-syscall/system"; +// Checks if the URL contains a protocol, if so keeps it, otherwise assumes an attachment +function patchUrl(url: string): string { + if (url.indexOf("://") === -1) { + return `attachment/${url}`; + } + return url; +} + async function actionClickOrActionEnter(mdTree: ParseTree | null) { if (!mdTree) { return; @@ -33,10 +41,10 @@ async function actionClickOrActionEnter(mdTree: ParseTree | null) { break; case "URL": case "NakedURL": - await openUrl(mdTree.children![0].text!); + await openUrl(patchUrl(mdTree.children![0].text!)); break; case "Link": - const url = mdTree.children![4].children![0].text!; + const url = patchUrl(mdTree.children![4].children![0].text!); if (url.length <= 1) { return flashNotification("Empty link, ignoring", "error"); } diff --git a/packages/plugs/markdown/util.ts b/packages/plugs/markdown/util.ts index 659fda2a..400ad28e 100644 --- a/packages/plugs/markdown/util.ts +++ b/packages/plugs/markdown/util.ts @@ -41,6 +41,13 @@ export async function cleanMarkdown( text: `__${n.children![0].text}__`, }; } + if (n.type === "URL") { + const url = n.children![0].text!; + if (url.indexOf("://") === -1) { + n.children![0].text = `attachment/${url}`; + } + console.log("Link", url); + } if (n.type === "FencedCode") { let codeInfoNode = findNodeOfType(n, "CodeInfo"); if (!codeInfoNode) { diff --git a/packages/web/editor_paste.ts b/packages/web/editor_paste.ts index 4bf9c728..550ee566 100644 --- a/packages/web/editor_paste.ts +++ b/packages/web/editor_paste.ts @@ -119,9 +119,9 @@ export function attachmentExtension(editor: Editor) { return; } await editor.space.writeAttachment(finalFileName, data!); - let attachmentMarkdown = `[${finalFileName}](attachment/${finalFileName})`; + let attachmentMarkdown = `[${finalFileName}](${finalFileName})`; if (mimeType.startsWith("image/")) { - attachmentMarkdown = `![](attachment/${finalFileName})`; + attachmentMarkdown = `![](${finalFileName})`; } editor.editorView!.dispatch({ changes: [ diff --git a/packages/web/inline_image.ts b/packages/web/inline_image.ts index 55c2ebbd..7114a687 100644 --- a/packages/web/inline_image.ts +++ b/packages/web/inline_image.ts @@ -20,7 +20,11 @@ class InlineImageWidget extends WidgetType { toDOM() { const img = document.createElement("img"); - img.src = this.url; + if (this.url.startsWith("http")) { + img.src = this.url; + } else { + img.src = `attachment/${this.url}`; + } img.alt = this.title; img.title = this.title; img.style.display = "block"; diff --git a/packages/web/syscalls/system.ts b/packages/web/syscalls/system.ts index 70274add..e80935e4 100644 --- a/packages/web/syscalls/system.ts +++ b/packages/web/syscalls/system.ts @@ -1,5 +1,6 @@ import { SysCallMapping } from "@plugos/plugos/system"; import type { Editor } from "../editor"; +import { AppCommand, CommandDef } from "../hooks/command"; import { version } from "../package.json"; export function systemSyscalls(editor: Editor): SysCallMapping { @@ -23,6 +24,15 @@ export function systemSyscalls(editor: Editor): SysCallMapping { "system.invokeCommand": async (ctx, name: string) => { return editor.runCommandByName(name); }, + "system.listCommands": async ( + ctx + ): Promise<{ [key: string]: CommandDef }> => { + let allCommands: { [key: string]: CommandDef } = {}; + for (let [cmd, def] of editor.commandHook.editorCommands) { + allCommands[cmd] = def.command; + } + return allCommands; + }, "system.getVersion": async () => { return version; }, diff --git a/website/CHANGELOG.md b/website/CHANGELOG.md index 71b132e7..a79f4f41 100644 --- a/website/CHANGELOG.md +++ b/website/CHANGELOG.md @@ -3,7 +3,9 @@ An attempt at documenting of the changes/new features introduced in each release --- ## 0.0.34 +* Change to attachment handling: the `attachment/` prefix for links and images is no longer used, if you have links to attachments in your notes, you will need to remove the `attachment/` prefix manually. Sorry about that. * Improved styling for completion (especially slash commands) +* Completion for commands using the (undocumented) `{[Command Syntax]}` — yep, that exists. --- diff --git a/website/Silver Bullet.md b/website/Silver Bullet.md index fc3e3507..7ac29b0e 100644 --- a/website/Silver Bullet.md +++ b/website/Silver Bullet.md @@ -3,7 +3,7 @@ Silver Bullet (SB) is highly-extensible, [open source](https://github.com/silver Here is a screenshot: -![Silver Bullet PWA screenshot](attachment/silverbullet-pwa.png) +![Silver Bullet PWA screenshot](silverbullet-pwa.png) At its core, SB is a Markdown editor that stores _pages_ (notes) as plain markdown files in a folder referred to as a _space_. Pages can be cross-linked using the `[[link to other page]]` syntax. However, once you leverage its various extensions (called _plugs_) it can feel more like a _knowledge platform_, allowing you to annotate, combine and query your accumulated knowledge in creative ways, specific to you. To get a good feel for it, [watch this video](https://youtu.be/RYdc3UF9gok). diff --git a/website/silverbullet-pwa.png b/website/silverbullet-pwa.png index e6ff371b..2d4ee6bc 100644 Binary files a/website/silverbullet-pwa.png and b/website/silverbullet-pwa.png differ