1
0

Refactor all the things

This commit is contained in:
Zef Hemel 2023-08-28 17:12:15 +02:00
parent 54d2deea15
commit 5ff1a8bae3
87 changed files with 930 additions and 896 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ node_modules
*.db *.db
test_space test_space
silverbullet silverbullet
.silverbullet.db*

View File

@ -1,4 +1,4 @@
FROM lukechannings/deno:v1.36.1 FROM lukechannings/deno:v1.36.3
# The volume that will keep the space data # The volume that will keep the space data
# Create a volume first: # Create a volume first:
# docker volume create myspace # docker volume create myspace

View File

@ -38,7 +38,7 @@ export async function runPlug(
}); });
if (indexFirst) { if (indexFirst) {
await serverSystem.system.loadedPlugs.get("core")!.invoke( await serverSystem.system.loadedPlugs.get("index")!.invoke(
"reindexSpace", "reindexSpace",
[], [],
); );
@ -53,8 +53,8 @@ export async function runPlug(
} }
const result = await plug.invoke(funcName, args); const result = await plug.invoke(funcName, args);
await serverSystem.close(); await serverSystem.close();
await serverSystem.kvStore?.delete(); serverSystem.denoKv.close();
// await Deno.remove(tempFile); await Deno.remove(tempFile);
serverController.abort(); serverController.abort();
return result; return result;
} else { } else {

View File

@ -95,7 +95,7 @@ To allow outside connections, pass -L 0.0.0.0 as a flag, and put a TLS terminato
system = serverSystem.system; system = serverSystem.system;
if (options.reindex) { if (options.reindex) {
console.log("Reindexing space (requested via --reindex flag)"); console.log("Reindexing space (requested via --reindex flag)");
await serverSystem.system.loadedPlugs.get("core")!.invoke( await serverSystem.system.loadedPlugs.get("index")!.invoke(
"reindexSpace", "reindexSpace",
[], [],
); );

View File

@ -1,5 +1,5 @@
import { syscall } from "$sb/plugos-syscall/syscall.ts"; import { syscall } from "$sb/plugos-syscall/syscall.ts";
import { QueueStats } from "$sb/types.ts"; import { MQStats } from "$sb/types.ts";
export function send(queue: string, body: any) { export function send(queue: string, body: any) {
return syscall("mq.send", queue, body); return syscall("mq.send", queue, body);
@ -17,6 +17,6 @@ export function batchAck(queue: string, ids: string[]) {
return syscall("mq.batchAck", queue, ids); return syscall("mq.batchAck", queue, ids);
} }
export function getQueueStats(queue: string): Promise<QueueStats> { export function getQueueStats(queue: string): Promise<MQStats> {
return syscall("mq.getQueueStats", queue); return syscall("mq.getQueueStats", queue);
} }

View File

@ -1,20 +1,11 @@
import type { CommandDef } from "../../web/hooks/command.ts"; import type { CommandDef } from "../../web/hooks/command.ts";
import { syscall } from "./syscall.ts"; import { syscall } from "./syscall.ts";
export function invoke(
name: string,
...args: any[]
): Promise<any> {
return syscall("system.invoke", name, ...args);
}
// @deprecated use invoke instead
export function invokeFunction( export function invokeFunction(
env: string,
name: string, name: string,
...args: any[] ...args: any[]
): Promise<any> { ): Promise<any> {
return syscall("system.invokeFunction", env, name, ...args); return syscall("system.invokeFunction", name, ...args);
} }
// Only available on the client // Only available on the client

2
plug-api/syscalls.ts Normal file
View File

@ -0,0 +1,2 @@
export * from "$sb/silverbullet-syscall/mod.ts";
export * from "$sb/plugos-syscall/mod.ts";

View File

@ -1,16 +1,21 @@
export type Message = { export type MQMessage = {
id: string; id: string;
queue: string; queue: string;
body: any; body: any;
retries?: number; retries?: number;
}; };
export type QueueStats = { export type MQStats = {
queued: number; queued: number;
processing: number; processing: number;
dlq: number; dlq: number;
}; };
export type MQSubscribeOptions = {
batchSize?: number;
pollInterval?: number;
};
export type FileMeta = { export type FileMeta = {
name: string; name: string;
lastModified: number; lastModified: number;

View File

@ -1,8 +1,8 @@
import { Hook, Manifest } from "../types.ts"; import { Hook, Manifest } from "../types.ts";
import { System } from "../system.ts"; import { System } from "../system.ts";
import { DexieMQ } from "../lib/mq.dexie.ts";
import { fullQueueName } from "../lib/mq_util.ts"; import { fullQueueName } from "../lib/mq_util.ts";
import { Message } from "$sb/types.ts"; import { MQMessage } from "$sb/types.ts";
import { MessageQueue } from "../lib/mq.ts";
type MQSubscription = { type MQSubscription = {
queue: string; queue: string;
@ -17,7 +17,7 @@ export type MQHookT = {
export class MQHook implements Hook<MQHookT> { export class MQHook implements Hook<MQHookT> {
subscriptions: (() => void)[] = []; subscriptions: (() => void)[] = [];
constructor(private system: System<MQHookT>, readonly mq: DexieMQ) { constructor(private system: System<MQHookT>, readonly mq: MessageQueue) {
} }
apply(system: System<MQHookT>): void { apply(system: System<MQHookT>): void {
@ -64,7 +64,7 @@ export class MQHook implements Hook<MQHookT> {
{ {
batchSize: subscriptionDef.batchSize, batchSize: subscriptionDef.batchSize,
}, },
async (messages: Message[]) => { async (messages: MQMessage[]) => {
try { try {
await plug.invoke(name, [messages]); await plug.invoke(name, [messages]);
if (subscriptionDef.autoAck) { if (subscriptionDef.autoAck) {

View File

@ -2,8 +2,8 @@ import { assertEquals } from "../../test_deps.ts";
import { DenoKVStore } from "./kv_store.deno_kv.ts"; import { DenoKVStore } from "./kv_store.deno_kv.ts";
Deno.test("Test KV index", async () => { Deno.test("Test KV index", async () => {
const kv = new DenoKVStore(); const denoKv = await Deno.openKv("test.db");
await kv.init("test.db"); const kv = new DenoKVStore(denoKv);
await kv.set("name", "Peter"); await kv.set("name", "Peter");
assertEquals(await kv.get("name"), "Peter"); assertEquals(await kv.get("name"), "Peter");
@ -52,5 +52,6 @@ Deno.test("Test KV index", async () => {
await kv.deletePrefix(""); await kv.deletePrefix("");
assertEquals(await kv.queryPrefix(""), []); assertEquals(await kv.queryPrefix(""), []);
await kv.delete(); denoKv.close();
await Deno.remove("test.db");
}); });

View File

@ -5,23 +5,7 @@ import { KV, KVStore } from "./kv_store.ts";
const kvBatchSize = 10; const kvBatchSize = 10;
export class DenoKVStore implements KVStore { export class DenoKVStore implements KVStore {
kv!: Deno.Kv; constructor(private kv: Deno.Kv) {
path: string | undefined;
async init(path?: string) {
this.path = path;
this.kv = await Deno.openKv(path);
}
close() {
this.kv.close();
}
async delete() {
this.kv.close();
if (this.path) {
await Deno.remove(this.path);
}
} }
del(key: string): Promise<void> { del(key: string): Promise<void> {

View File

@ -0,0 +1,20 @@
import { sleep } from "../../common/async_util.ts";
import { DenoKvMQ } from "./mq.deno_kv.ts";
Deno.test("Deno MQ", async () => {
const denoKv = await Deno.openKv("test.db");
const mq = new DenoKvMQ(denoKv);
const unsub = mq.subscribe("test", {}, (messages) => {
console.log("Received on test", messages);
});
const unsub2 = mq.subscribe("test2", {}, (messages) => {
console.log("Received on test2", messages);
});
await mq.send("test", "Hello World");
await mq.batchSend("test2", ["Hello World 2", "Hello World 3"]);
// Let's avoid a panic here
await sleep(20);
denoKv.close();
await Deno.remove("test.db");
});

87
plugos/lib/mq.deno_kv.ts Normal file
View File

@ -0,0 +1,87 @@
/// <reference lib="deno.unstable" />
import {
MQMessage,
MQStats,
MQSubscribeOptions,
} from "../../plug-api/types.ts";
import { MessageQueue } from "./mq.ts";
type QueuedMessage = [string, MQMessage];
export class DenoKvMQ implements MessageQueue {
listeners: Map<string, Set<(messages: MQMessage[]) => void | Promise<void>>> =
new Map();
constructor(private kv: Deno.Kv) {
kv.listenQueue(async (message: unknown) => {
const [queue, body] = message as QueuedMessage;
const listeners = this.listeners.get(queue);
if (!listeners) {
return;
}
for (const listener of listeners) {
await Promise.resolve(listener([{ id: "_dummyid", queue, body }]));
}
});
}
// Dummy implementation
getQueueStats(_queue: string): Promise<MQStats> {
return Promise.resolve({
queued: 0,
processing: 0,
dlq: 0,
});
}
// Dummy implementation
getAllQueueStats(): Promise<Record<string, MQStats>> {
return Promise.resolve({});
}
async batchSend(queue: string, bodies: any[]): Promise<void> {
const results = await Promise.all(
bodies.map((body) => this.kv.enqueue([queue, body])),
);
for (const result of results) {
if (!result.ok) {
throw result;
}
}
}
async send(queue: string, body: any): Promise<void> {
const result = await this.kv.enqueue([queue, body]);
if (!result.ok) {
throw result;
}
}
subscribe(
queue: string,
_options: MQSubscribeOptions,
callback: (messages: MQMessage[]) => void | Promise<void>,
): () => void {
const listeners = this.listeners.get(queue);
if (!listeners) {
this.listeners.set(queue, new Set([callback]));
} else {
listeners.add(callback);
}
return () => {
const listeners = this.listeners.get(queue);
if (!listeners) {
return;
}
listeners.delete(callback);
};
}
ack(_queue: string, _id: string): Promise<void> {
// Doesn't apply to this implementation
return Promise.resolve();
}
batchAck(_queue: string, _ids: string[]): Promise<void> {
// Doesn't apply to this implementation
return Promise.resolve();
}
}

View File

@ -1,18 +1,14 @@
import Dexie, { Table } from "dexie"; import Dexie, { Table } from "dexie";
import { Message, QueueStats } from "$sb/types.ts"; import { MQMessage, MQStats, MQSubscribeOptions } from "$sb/types.ts";
import { MessageQueue } from "./mq.ts";
export type ProcessingMessage = Message & { export type ProcessingMessage = MQMessage & {
ts: number; ts: number;
}; };
export type SubscribeOptions = { export class DexieMQ implements MessageQueue {
batchSize?: number;
pollInterval?: number;
};
export class DexieMQ {
db: Dexie; db: Dexie;
queued: Table<Message, [string, string]>; queued: Table<MQMessage, [string, string]>;
processing: Table<ProcessingMessage, [string, string]>; processing: Table<ProcessingMessage, [string, string]>;
dlq: Table<ProcessingMessage, [string, string]>; dlq: Table<ProcessingMessage, [string, string]>;
@ -63,13 +59,15 @@ export class DexieMQ {
return this.batchSend(queue, [body]); return this.batchSend(queue, [body]);
} }
poll(queue: string, maxItems: number): Promise<Message[]> { poll(queue: string, maxItems: number): Promise<MQMessage[]> {
return this.db.transaction( return this.db.transaction(
"rw", "rw",
[this.queued, this.processing], [this.queued, this.processing],
async (tx) => { async (tx) => {
const messages = const messages =
(await tx.table<Message, [string, string]>("queued").where({ queue }) (await tx.table<MQMessage, [string, string]>("queued").where({
queue,
})
.sortBy("id")).slice(0, maxItems); .sortBy("id")).slice(0, maxItems);
const ids: [string, string][] = messages.map((m) => [queue, m.id]); const ids: [string, string][] = messages.map((m) => [queue, m.id]);
await tx.table("queued").bulkDelete(ids); await tx.table("queued").bulkDelete(ids);
@ -93,8 +91,8 @@ export class DexieMQ {
*/ */
subscribe( subscribe(
queue: string, queue: string,
options: SubscribeOptions, options: MQSubscribeOptions,
callback: (messages: Message[]) => Promise<void> | void, callback: (messages: MQMessage[]) => Promise<void> | void,
): () => void { ): () => void {
let running = true; let running = true;
let timeout: number | undefined; let timeout: number | undefined;
@ -219,7 +217,7 @@ export class DexieMQ {
return this.dlq.clear(); return this.dlq.clear();
} }
getQueueStats(queue: string): Promise<QueueStats> { getQueueStats(queue: string): Promise<MQStats> {
return this.db.transaction( return this.db.transaction(
"r", "r",
[this.queued, this.processing, this.dlq], [this.queued, this.processing, this.dlq],
@ -237,8 +235,8 @@ export class DexieMQ {
); );
} }
async getAllQueueStats(): Promise<Record<string, QueueStats>> { async getAllQueueStats(): Promise<Record<string, MQStats>> {
const allStatus: Record<string, QueueStats> = {}; const allStatus: Record<string, MQStats> = {};
await this.db.transaction( await this.db.transaction(
"r", "r",
[this.queued, this.processing, this.dlq], [this.queued, this.processing, this.dlq],

16
plugos/lib/mq.ts Normal file
View File

@ -0,0 +1,16 @@
import { MQMessage, MQStats, MQSubscribeOptions } from "$sb/types.ts";
export interface MessageQueue {
batchSend(queue: string, bodies: any[]): Promise<void>;
send(queue: string, body: any): Promise<void>;
subscribe(
queue: string,
options: MQSubscribeOptions,
callback: (messages: MQMessage[]) => Promise<void> | void,
): () => void;
ack(queue: string, id: string): Promise<void>;
batchAck(queue: string, ids: string[]): Promise<void>;
getQueueStats(queue: string): Promise<MQStats>;
getAllQueueStats(): Promise<Record<string, MQStats>>;
}

View File

@ -1,9 +1,9 @@
import { SysCallMapping } from "../system.ts"; import { SysCallMapping } from "../system.ts";
import { DexieMQ } from "../lib/mq.dexie.ts";
import { fullQueueName } from "../lib/mq_util.ts"; import { fullQueueName } from "../lib/mq_util.ts";
import { MessageQueue } from "../lib/mq.ts";
export function mqSyscalls( export function mqSyscalls(
mq: DexieMQ, mq: MessageQueue,
): SysCallMapping { ): SysCallMapping {
return { return {
"mq.send": (ctx, queue: string, body: any) => { "mq.send": (ctx, queue: string, body: any) => {

View File

@ -1,6 +1,10 @@
// TODO: Figure out how to keep this up-to-date automatically // TODO: Figure out how to keep this up-to-date automatically
export const builtinPlugNames = [ export const builtinPlugNames = [
"core", "editor",
"index",
"sync",
"template",
"plug-manager",
"directive", "directive",
"emoji", "emoji",
"markdown", "markdown",

View File

@ -1,85 +0,0 @@
import { renderToText, replaceNodesMatching } from "$sb/lib/tree.ts";
import { parseMarkdown } from "$sb/silverbullet-syscall/markdown.ts";
import { FileMeta } from "$sb/types.ts";
export const cloudPrefix = "💭 ";
export async function readFileCloud(
name: string,
): Promise<{ data: Uint8Array; meta: FileMeta } | undefined> {
const originalUrl = name.substring(
cloudPrefix.length,
name.length - ".md".length,
);
let url = originalUrl;
if (!url.includes("/")) {
url += "/index";
}
if (!url.startsWith("127.0.0.1")) {
url = `https://${url}`;
} else {
url = `http://${url}`;
}
let text = "";
try {
const r = await fetch(`${encodeURI(url)}.md`);
text = await r.text();
if (!r.ok) {
text = `ERROR: ${text}`;
}
} catch (e: any) {
console.error("ERROR thrown", e.message);
text = `ERROR: ${e.message}`;
}
text = await translateLinksWithPrefix(
text,
`${cloudPrefix}${originalUrl.split("/")[0]}/`,
);
return {
data: new TextEncoder().encode(text),
meta: {
name,
contentType: "text/markdown",
lastModified: 0,
size: text.length,
perm: "ro",
},
};
}
export function writeFileCloud(
name: string,
): Promise<FileMeta> {
console.log("Writing cloud file", name);
return getFileMetaCloud(name);
}
async function translateLinksWithPrefix(
text: string,
prefix: string,
): Promise<string> {
const tree = await parseMarkdown(text);
replaceNodesMatching(tree, (tree) => {
if (tree.type === "WikiLinkPage") {
// Add the prefix in the link text
if (!tree.children![0].text!.startsWith(cloudPrefix)) {
// Only for links that aren't already cloud links
tree.children![0].text = prefix + tree.children![0].text;
}
}
return undefined;
});
text = renderToText(tree);
return text;
}
export function getFileMetaCloud(name: string): Promise<FileMeta> {
return Promise.resolve({
name,
size: 0,
contentType: "text/markdown",
lastModified: 0,
perm: "ro",
});
}

View File

@ -1,515 +0,0 @@
name: core
requiredPermissions:
- fetch
syntax:
Hashtag:
firstCharacters:
- "#"
regex: "#[^#\\d\\s\\[\\]]+\\w+"
className: sb-hashtag
NakedURL:
firstCharacters:
- "h"
regex: "https?:\\/\\/[-a-zA-Z0-9@:%._\\+~#=]{1,256}([-a-zA-Z0-9()@:%_\\+.~#?&=\\/]*)"
className: sb-naked-url
NamedAnchor:
firstCharacters:
- "$"
regex: "\\$[a-zA-Z\\.\\-\\/]+[\\w\\.\\-\\/]*"
className: sb-named-anchor
functions:
setEditorMode:
path: "./editor.ts:setEditorMode"
events:
- editor:init
toggleDarkMode:
path: "./editor.ts:toggleDarkMode"
command:
name: "Editor: Toggle Dark Mode"
centerCursor:
path: "./editor.ts:centerCursorCommand"
command:
name: "Editor: Center Cursor"
key: "Ctrl-Alt-l"
moveToPos:
path: "./editor.ts:moveToPosCommand"
command:
name: "Editor: Move Cursor to Position"
clearPageIndex:
path: "./page.ts:clearPageIndex"
env: server
events:
- page:saved
- page:deleted
pageQueryProvider:
path: ./page.ts:pageQueryProvider
events:
- query:page
parseIndexTextRepublish:
path: "./page.ts:parseIndexTextRepublish"
env: server
events:
- page:index_text
reindexSpaceCommand:
path: "./page.ts:reindexCommand"
command:
name: "Space: Reindex"
processIndexQueue:
path: ./page.ts:processIndexQueue
mqSubscriptions:
- queue: indexQueue
batchSize: 10
autoAck: true
reindexSpace:
path: "./page.ts:reindexSpace"
deletePage:
path: "./page.ts:deletePage"
command:
name: "Page: Delete"
copyPage:
path: "./page.ts:copyPage"
command:
name: "Page: Copy"
syncSpaceCommand:
path: "./sync.ts:syncSpaceCommand"
command:
name: "Sync: Now"
key: "Alt-Shift-s"
mac: "Cmd-Shift-s"
# Attachments
attachmentQueryProvider:
path: ./attachment.ts:attachmentQueryProvider
events:
- query:attachment
# Backlinks
indexLinks:
path: "./page_links.ts:indexLinks"
events:
- page:index
linkQueryProvider:
path: ./page_links.ts:linkQueryProvider
events:
- query:link
pageComplete:
path: "./page.ts:pageComplete"
events:
- editor:complete
attributeComplete:
path: "./attributes.ts:attributeComplete"
events:
- editor:complete
customAttributeCompleter:
path: ./attributes.ts:customAttributeCompleter
events:
- attribute:complete:page
- attribute:complete:task
- attribute:complete:item
- attribute:complete:*
builtinAttributeCompleter:
path: ./attributes.ts:builtinAttributeCompleter
events:
- attribute:complete:page
- attribute:complete:task
- attribute:complete:item
- attribute:complete:*
# Commands
commandComplete:
path: "./command.ts:commandComplete"
events:
- editor:complete
# Item indexing
indexItem:
path: "./item.ts:indexItems"
events:
- page:index
itemQueryProvider:
path: "./item.ts:queryProvider"
events:
- query:item
# Navigation
linkNavigate:
path: "./navigate.ts:linkNavigate"
command:
name: Navigate To page
key: Ctrl-Enter
mac: Cmd-Enter
clickNavigate:
path: "./navigate.ts:clickNavigate"
events:
- page:click
navigateHome:
path: "./navigate.ts:navigateCommand"
command:
name: "Navigate: Home"
key: "Alt-h"
page: ""
# Hashtags
indexTags:
path: "./tags.ts:indexTags"
events:
- page:index
tagComplete:
path: "./tags.ts:tagComplete"
events:
- editor:complete
tagProvider:
path: "./tags.ts:tagProvider"
events:
- query:tag
# Anchors
indexAnchors:
path: "./anchor.ts:indexAnchors"
events:
- page:index
anchorComplete:
path: "./anchor.ts:anchorComplete"
events:
- editor:complete
# Template commands
insertTemplateText:
path: "./template.ts:insertTemplateText"
applyLineReplace:
path: ./template.ts:applyLineReplace
insertFrontMatter:
redirect: insertTemplateText
slashCommand:
name: front-matter
description: Insert page front matter
value: |
---
|^|
---
makeH1:
redirect: applyLineReplace
slashCommand:
name: h1
description: Turn line into h1 header
match: "^#*\\s*"
replace: "# "
makeH2:
redirect: applyLineReplace
slashCommand:
name: h2
description: Turn line into h2 header
match: "^#*\\s*"
replace: "## "
makeH3:
redirect: applyLineReplace
slashCommand:
name: h3
description: Turn line into h3 header
match: "^#*\\s*"
replace: "### "
makeH4:
redirect: applyLineReplace
slashCommand:
name: h4
description: Turn line into h4 header
match: "^#*\\s*"
replace: "#### "
insertCodeBlock:
redirect: insertTemplateText
slashCommand:
name: code
description: Insert code block
value: |
```
|^|
```
newPage:
path: ./page.ts:newPageCommand
command:
name: "Page: New"
key: "Alt-Shift-n"
insertHRTemplate:
redirect: insertTemplateText
slashCommand:
name: hr
description: Insert a horizontal rule
value: "---"
insertTable:
redirect: insertTemplateText
slashCommand:
name: table
description: Insert a table
boost: -1 # Low boost because it's likely not very commonly used
value: |
| Header A | Header B |
|----------|----------|
| Cell A|^| | Cell B |
quickNoteCommand:
path: ./template.ts:quickNoteCommand
command:
name: "Quick Note"
key: "Alt-Shift-n"
priority: 1
dailyNoteCommand:
path: ./template.ts:dailyNoteCommand
command:
name: "Open Daily Note"
key: "Alt-Shift-d"
weeklyNoteCommand:
path: ./template.ts:weeklyNoteCommand
command:
name: "Open Weekly Note"
key: "Alt-Shift-w"
instantiateTemplateCommand:
path: ./template.ts:instantiateTemplateCommand
command:
name: "Template: Instantiate Page"
insertSnippet:
path: ./template.ts:insertSnippet
command:
name: "Template: Insert Snippet"
slashCommand:
name: snippet
description: Insert a snippet
applyPageTemplateCommand:
path: ./template.ts:applyPageTemplateCommand
slashCommand:
name: page-template
description: Apply a page template
insertTodayCommand:
path: "./template.ts:insertTemplateText"
slashCommand:
name: today
description: Insert today's date
value: "{{today}}"
insertTomorrowCommand:
path: "./template.ts:insertTemplateText"
slashCommand:
name: tomorrow
description: Insert tomorrow's date
value: "{{tomorrow}}"
# Text editing commands
quoteSelectionCommand:
path: ./text.ts:quoteSelection
command:
name: "Text: Quote Selection"
key: "Ctrl-Shift-."
mac: "Cmd-Shift-."
listifySelection:
path: ./text.ts:listifySelection
command:
name: "Text: Listify Selection"
key: "Ctrl-Shift-8"
mac: "Cmd-Shift-8"
numberListifySelection:
path: ./text.ts:numberListifySelection
command:
name: "Text: Number Listify Selection"
linkSelection:
path: ./text.ts:linkSelection
command:
name: "Text: Link Selection"
key: "Ctrl-Shift-k"
mac: "Cmd-Shift-k"
bold:
path: ./text.ts:wrapSelection
command:
name: "Text: Bold"
key: "Ctrl-b"
mac: "Cmd-b"
wrapper: "**"
italic:
path: ./text.ts:wrapSelection
command:
name: "Text: Italic"
key: "Ctrl-i"
mac: "Cmd-i"
wrapper: "_"
strikethrough:
path: ./text.ts:wrapSelection
command:
name: "Text: Strikethrough"
key: "Ctrl-Shift-s"
mac: "Cmd-Shift-s"
wrapper: "~~"
marker:
path: ./text.ts:wrapSelection
command:
name: "Text: Marker"
key: "Alt-m"
wrapper: "=="
# Refactoring Commands
extractToPageCommand:
path: ./refactor.ts:extractToPageCommand
command:
name: "Page: Extract"
renamePageCommand:
path: "./refactor.ts:renamePageCommand"
command:
name: "Page: Rename"
mac: Cmd-Alt-r
key: Ctrl-Alt-r
page: ""
renamePrefixCommand:
path: "./refactor.ts:renamePrefixCommand"
command:
name: "Page: Batch Rename Prefix"
# Plug manager
updatePlugsCommand:
path: ./plugmanager.ts:updatePlugsCommand
command:
name: "Plugs: Update"
key: "Ctrl-Shift-p"
mac: "Cmd-Shift-p"
getPlugHTTPS:
path: "./plugmanager.ts:getPlugHTTPS"
events:
- get-plug:https
getPlugGithub:
path: "./plugmanager.ts:getPlugGithub"
events:
- get-plug:github
getPlugGithubRelease:
path: "./plugmanager.ts:getPlugGithubRelease"
events:
- get-plug:ghr
addPlugCommand:
path: ./plugmanager.ts:addPlugCommand
command:
name: "Plugs: Add"
# Debug commands
parseCommand:
path: ./debug.ts:parsePageCommand
command:
name: "Debug: Parse Document"
reloadUICommand:
path: ./debug.ts:reloadUICommand
command:
name: "Debug: Reload UI"
resetClientCommand:
path: ./debug.ts:resetClientCommand
command:
name: "Debug: Reset Client"
versionCommand:
path: ./help.ts:versionCommand
command:
name: "Help: Version"
gettingStartedCommand:
path: ./help.ts:gettingStartedCommand
command:
name: "Help: Getting Started"
accountLogoutCommand:
path: ./account.ts:accountLogoutCommand
command:
name: "Account: Logout"
# Link unfurl infrastructure
unfurlLink:
path: ./link.ts:unfurlCommand
command:
name: "Link: Unfurl"
key: "Ctrl-Shift-u"
mac: "Cmd-Shift-u"
contexts:
- NakedURL
# Title-based link unfurl
titleUnfurlOptions:
path: ./link.ts:titleUnfurlOptions
events:
- unfurl:options
titleUnfurl:
path: ./link.ts:titleUnfurl
events:
- unfurl:title-unfurl
embedWidget:
path: ./embed.ts:embedWidget
codeWidget: embed
# Folding commands
foldCommand:
path: ./editor.ts:foldCommand
command:
name: "Fold: Fold"
mac: "Cmd-Alt-["
key: "Ctrl-Shift-["
unfoldCommand:
path: ./editor.ts:unfoldCommand
command:
name: "Fold: Unfold"
mac: "Cmd-Alt-]"
key: "Ctrl-Shift-]"
toggleFoldCommand:
path: ./editor.ts:toggleFoldCommand
command:
name: "Fold: Toggle Fold"
mac: "Cmd-Alt-f"
key: "Ctrl-Alt-f"
foldAllCommand:
path: ./editor.ts:foldAllCommand
command:
name: "Fold: Fold All"
key: "Ctrl-Alt-["
unfoldAllCommand:
path: ./editor.ts:unfoldAllCommand
command:
name: "Fold: Unfold All"
key: "Ctrl-Alt-]"
# Random stuff
statsCommand:
path: ./stats.ts:statsCommand
command:
name: "Stats: Show"
# Cloud pages
readPageCloud:
path: ./cloud.ts:readFileCloud
pageNamespace:
pattern: "💭 .+"
operation: readFile
writePageCloud:
path: ./cloud.ts:writeFileCloud
pageNamespace:
pattern: "💭 .+"
operation: writeFile
getPageMetaCloud:
path: ./cloud.ts:getFileMetaCloud
pageNamespace:
pattern: "💭 .+"
operation: getFileMeta
# Vim
toggleVimMode:
path: "./vim.ts:toggleVimMode"
command:
name: "Editor: Toggle Vim Mode"
loadVimRc:
path: "./vim.ts:loadVimRc"
command:
name: "Editor: Vim: Load VIMRC"
events:
- editor:modeswitch
brokenLinksCommand:
path: ./broken_links.ts:brokenLinksCommand
command:
name: "Broken Links: Show"

View File

@ -1,4 +1,4 @@
import { editor, markdown, space, sync } from "$sb/silverbullet-syscall/mod.ts"; import { editor, markdown, mq, space, sync } from "$sb/syscalls.ts";
import { import {
ParseTree, ParseTree,
removeParentPointers, removeParentPointers,
@ -7,10 +7,9 @@ import {
} from "$sb/lib/tree.ts"; } from "$sb/lib/tree.ts";
import { renderDirectives } from "./directives.ts"; import { renderDirectives } from "./directives.ts";
import { extractFrontmatter } from "$sb/lib/frontmatter.ts"; import { extractFrontmatter } from "$sb/lib/frontmatter.ts";
import { PageMeta } from "../../web/types.ts"; import type { PageMeta } from "../../web/types.ts";
import { isFederationPath } from "$sb/lib/resolve.ts"; import { isFederationPath } from "$sb/lib/resolve.ts";
import { mq } from "$sb/plugos-syscall/mod.ts"; import { MQMessage } from "$sb/types.ts";
import { Message } from "$sb/types.ts";
import { sleep } from "../../common/async_util.ts"; import { sleep } from "../../common/async_util.ts";
const directiveUpdateQueueName = "directiveUpdateQueue"; const directiveUpdateQueueName = "directiveUpdateQueue";
@ -92,7 +91,7 @@ export async function updateDirectivesInSpaceCommand() {
await editor.flashNotification("Updating of all directives completed!"); await editor.flashNotification("Updating of all directives completed!");
} }
export async function processUpdateQueue(messages: Message[]) { export async function processUpdateQueue(messages: MQMessage[]) {
for (const message of messages) { for (const message of messages) {
const pageName: string = message.body; const pageName: string = message.body;
console.log("Updating directives in page", pageName); console.log("Updating directives in page", pageName);

View File

@ -1,11 +1,11 @@
import { events } from "$sb/plugos-syscall/mod.ts"; import { events } from "$sb/syscalls.ts";
import { CompleteEvent } from "$sb/app_event.ts"; import { CompleteEvent } from "$sb/app_event.ts";
import { buildHandebarOptions } from "./util.ts"; import { buildHandebarOptions } from "./util.ts";
import type { PageMeta } from "../../web/types.ts"; import type { PageMeta } from "../../web/types.ts";
import { import type {
AttributeCompleteEvent, AttributeCompleteEvent,
AttributeCompletion, AttributeCompletion,
} from "../core/attributes.ts"; } from "../index/attributes.ts";
export async function queryComplete(completeEvent: CompleteEvent) { export async function queryComplete(completeEvent: CompleteEvent) {
const querySourceMatch = /#query\s+([\w\-_]*)$/.exec( const querySourceMatch = /#query\s+([\w\-_]*)$/.exec(

View File

@ -2,10 +2,9 @@
// data:page@pos // data:page@pos
import type { IndexTreeEvent, QueryProviderEvent } from "$sb/app_event.ts"; import type { IndexTreeEvent, QueryProviderEvent } from "$sb/app_event.ts";
import { index } from "$sb/silverbullet-syscall/mod.ts"; import { index, YAML } from "$sb/syscalls.ts";
import { collectNodesOfType, findNodeOfType } from "$sb/lib/tree.ts"; import { collectNodesOfType, findNodeOfType } from "$sb/lib/tree.ts";
import { applyQuery, removeQueries } from "$sb/lib/query.ts"; import { applyQuery, removeQueries } from "$sb/lib/query.ts";
import { YAML } from "$sb/plugos-syscall/mod.ts";
export async function indexData({ name, tree }: IndexTreeEvent) { export async function indexData({ name, tree }: IndexTreeEvent) {
const dataObjects: { key: string; value: any }[] = []; const dataObjects: { key: string; value: any }[] = [];

View File

@ -39,7 +39,7 @@ functions:
# Templates # Templates
insertQuery: insertQuery:
redirect: core.insertTemplateText redirect: template.insertTemplateText
slashCommand: slashCommand:
name: query name: query
description: Insert a query description: Insert a query
@ -48,7 +48,7 @@ functions:
<!-- /query --> <!-- /query -->
insertInclude: insertInclude:
redirect: core.insertTemplateText redirect: template.insertTemplateText
slashCommand: slashCommand:
name: include name: include
description: Include another page description: Include another page
@ -57,7 +57,7 @@ functions:
<!-- /include --> <!-- /include -->
insertUseTemplate: insertUseTemplate:
redirect: core.insertTemplateText redirect: template.insertTemplateText
slashCommand: slashCommand:
name: use name: use
description: Use a template description: Use a template
@ -66,7 +66,7 @@ functions:
<!-- /use --> <!-- /use -->
insertUseVerboseTemplate: insertUseVerboseTemplate:
redirect: core.insertTemplateText redirect: template.insertTemplateText
slashCommand: slashCommand:
name: use-verbose name: use-verbose
description: Use a template (verbose mode) description: Use a template (verbose mode)
@ -75,7 +75,7 @@ functions:
<!-- /use-verbose --> <!-- /use-verbose -->
insertEvalTemplate: insertEvalTemplate:
redirect: core.insertTemplateText redirect: template.insertTemplateText
slashCommand: slashCommand:
name: eval name: eval
description: Evaluate a JavaScript expression description: Evaluate a JavaScript expression

View File

@ -1,10 +1,10 @@
// This is some shocking stuff. My profession would kill me for this. // This is some shocking stuff. My profession would kill me for this.
import { YAML } from "$sb/plugos-syscall/mod.ts"; import { YAML } from "$sb/syscalls.ts";
import { ParseTree } from "$sb/lib/tree.ts"; import { ParseTree } from "$sb/lib/tree.ts";
import { jsonToMDTable, renderTemplate } from "./util.ts"; import { jsonToMDTable, renderTemplate } from "./util.ts";
import { PageMeta } from "../../web/types.ts"; import type { PageMeta } from "../../web/types.ts";
import { replaceTemplateVars } from "../core/template.ts"; import { replaceTemplateVars } from "../template/template.ts";
// Enables plugName.functionName(arg1, arg2) syntax in JS expressions // Enables plugName.functionName(arg1, arg2) syntax in JS expressions
function translateJs(js: string): string { function translateJs(js: string): string {
@ -44,7 +44,7 @@ export async function evalDirectiveRenderer(
const result = await (0, eval)( const result = await (0, eval)(
`(async () => { `(async () => {
function invokeFunction(name, ...args) { function invokeFunction(name, ...args) {
return syscall("system.invoke", name, ...args); return syscall("system.invokeFunction", name, ...args);
} }
return ${replaceTemplateVars(translateJs(expression), pageMeta)}; return ${replaceTemplateVars(translateJs(expression), pageMeta)};
})()`, })()`,

View File

@ -1,11 +1,11 @@
import { events } from "$sb/plugos-syscall/mod.ts"; import { events } from "$sb/syscalls.ts";
import { replaceTemplateVars } from "../core/template.ts"; import { replaceTemplateVars } from "../template/template.ts";
import { renderTemplate } from "./util.ts"; import { renderTemplate } from "./util.ts";
import { parseQuery } from "./parser.ts"; import { parseQuery } from "./parser.ts";
import { jsonToMDTable } from "./util.ts"; import { jsonToMDTable } from "./util.ts";
import { ParseTree } from "$sb/lib/tree.ts"; import { ParseTree } from "$sb/lib/tree.ts";
import { PageMeta } from "../../web/types.ts"; import type { PageMeta } from "../../web/types.ts";
export async function queryDirectiveRenderer( export async function queryDirectiveRenderer(
_directive: string, _directive: string,

View File

@ -1,14 +1,9 @@
import { queryRegex } from "$sb/lib/query.ts"; import { queryRegex } from "$sb/lib/query.ts";
import { import { ParseTree, renderToText } from "$sb/lib/tree.ts";
findNodeOfType, import { markdown, space } from "$sb/syscalls.ts";
ParseTree,
renderToText,
traverseTree,
} from "$sb/lib/tree.ts";
import { markdown, space } from "$sb/silverbullet-syscall/mod.ts";
import Handlebars from "handlebars"; import Handlebars from "handlebars";
import { replaceTemplateVars } from "../core/template.ts"; import { replaceTemplateVars } from "../template/template.ts";
import { extractFrontmatter } from "$sb/lib/frontmatter.ts"; import { extractFrontmatter } from "$sb/lib/frontmatter.ts";
import { directiveRegex } from "./directives.ts"; import { directiveRegex } from "./directives.ts";
import { updateDirectives } from "./command.ts"; import { updateDirectives } from "./command.ts";

View File

@ -1,7 +1,7 @@
import Handlebars from "handlebars"; import Handlebars from "handlebars";
import { space } from "$sb/silverbullet-syscall/mod.ts"; import { space } from "$sb/syscalls.ts";
import { PageMeta } from "../../web/types.ts"; import type { PageMeta } from "../../web/types.ts";
import { handlebarHelpers } from "./handlebar_helpers.ts"; import { handlebarHelpers } from "./handlebar_helpers.ts";
const maxWidth = 70; const maxWidth = 70;

View File

@ -1,4 +1,4 @@
import { editor } from "$sb/silverbullet-syscall/mod.ts"; import { editor } from "$sb/syscalls.ts";
export async function accountLogoutCommand() { export async function accountLogoutCommand() {
await editor.openUrl("/.client/logout.html", true); await editor.openUrl("/.client/logout.html", true);

View File

@ -1,9 +1,5 @@
import { traverseTree } from "../../plug-api/lib/tree.ts"; import { traverseTree } from "../../plug-api/lib/tree.ts";
import { import { editor, markdown, space } from "$sb/syscalls.ts";
editor,
markdown,
space,
} from "../../plug-api/silverbullet-syscall/mod.ts";
export async function brokenLinksCommand() { export async function brokenLinksCommand() {
const pageName = "BROKEN LINKS"; const pageName = "BROKEN LINKS";

7
plugs/editor/client.ts Normal file
View File

@ -0,0 +1,7 @@
import { editor } from "$sb/syscalls.ts";
export async function setThinClient(def: any) {
console.log("Setting thin client to", def.value);
await editor.setUiOption("thinClientMode", def.value);
await editor.reloadUI();
}

View File

@ -1,5 +1,5 @@
import { system } from "$sb/silverbullet-syscall/mod.ts"; import { system } from "$sb/syscalls.ts";
import { CompleteEvent } from "../../plug-api/app_event.ts"; import { CompleteEvent } from "$sb/app_event.ts";
export async function commandComplete(completeEvent: CompleteEvent) { export async function commandComplete(completeEvent: CompleteEvent) {
const match = /\{\[([^\]]*)$/.exec(completeEvent.linePrefix); const match = /\{\[([^\]]*)$/.exec(completeEvent.linePrefix);

View File

@ -1,4 +1,4 @@
import { debug, editor, markdown } from "$sb/silverbullet-syscall/mod.ts"; import { debug, editor, markdown } from "$sb/syscalls.ts";
export async function parsePageCommand() { export async function parsePageCommand() {
console.log( console.log(

View File

@ -0,0 +1,240 @@
name: editor
requiredPermissions:
- fetch
syntax:
NakedURL:
firstCharacters:
- "h"
regex: "https?:\\/\\/[-a-zA-Z0-9@:%._\\+~#=]{1,256}([-a-zA-Z0-9()@:%_\\+.~#?&=\\/]*)"
className: sb-naked-url
functions:
setEditorMode:
path: "./editor.ts:setEditorMode"
events:
- editor:init
toggleDarkMode:
path: "./editor.ts:toggleDarkMode"
command:
name: "Editor: Toggle Dark Mode"
# Page operations
deletePage:
path: "./page.ts:deletePage"
command:
name: "Page: Delete"
copyPage:
path: "./page.ts:copyPage"
command:
name: "Page: Copy"
newPage:
path: ./page.ts:newPageCommand
command:
name: "Page: New"
key: "Alt-Shift-n"
# Completion
pageComplete:
path: "./page.ts:pageComplete"
events:
- editor:complete
commandComplete:
path: "./command.ts:commandComplete"
events:
- editor:complete
# Navigation
linkNavigate:
path: "./navigate.ts:linkNavigate"
command:
name: Navigate To page
key: Ctrl-Enter
mac: Cmd-Enter
clickNavigate:
path: "./navigate.ts:clickNavigate"
events:
- page:click
navigateHome:
path: "./navigate.ts:navigateCommand"
command:
name: "Navigate: Home"
key: "Alt-h"
page: ""
# Text editing commands
quoteSelectionCommand:
path: ./text.ts:quoteSelection
command:
name: "Text: Quote Selection"
key: "Ctrl-Shift-."
mac: "Cmd-Shift-."
listifySelection:
path: ./text.ts:listifySelection
command:
name: "Text: Listify Selection"
key: "Ctrl-Shift-8"
mac: "Cmd-Shift-8"
numberListifySelection:
path: ./text.ts:numberListifySelection
command:
name: "Text: Number Listify Selection"
linkSelection:
path: ./text.ts:linkSelection
command:
name: "Text: Link Selection"
key: "Ctrl-Shift-k"
mac: "Cmd-Shift-k"
bold:
path: ./text.ts:wrapSelection
command:
name: "Text: Bold"
key: "Ctrl-b"
mac: "Cmd-b"
wrapper: "**"
italic:
path: ./text.ts:wrapSelection
command:
name: "Text: Italic"
key: "Ctrl-i"
mac: "Cmd-i"
wrapper: "_"
strikethrough:
path: ./text.ts:wrapSelection
command:
name: "Text: Strikethrough"
key: "Ctrl-Shift-s"
mac: "Cmd-Shift-s"
wrapper: "~~"
marker:
path: ./text.ts:wrapSelection
command:
name: "Text: Marker"
key: "Alt-m"
wrapper: "=="
centerCursor:
path: "./editor.ts:centerCursorCommand"
command:
name: "Editor: Center Cursor"
key: "Ctrl-Alt-l"
moveToPos:
path: "./editor.ts:moveToPosCommand"
command:
name: "Editor: Move Cursor to Position"
# Debug commands
parseCommand:
path: ./debug.ts:parsePageCommand
command:
name: "Debug: Parse Document"
# Link unfurl infrastructure
unfurlLink:
path: ./link.ts:unfurlCommand
command:
name: "Link: Unfurl"
key: "Ctrl-Shift-u"
mac: "Cmd-Shift-u"
contexts:
- NakedURL
# Title-based link unfurl
titleUnfurlOptions:
path: ./link.ts:titleUnfurlOptions
events:
- unfurl:options
titleUnfurl:
path: ./link.ts:titleUnfurl
events:
- unfurl:title-unfurl
embedWidget:
path: ./embed.ts:embedWidget
codeWidget: embed
# Folding commands
foldCommand:
path: ./editor.ts:foldCommand
command:
name: "Fold: Fold"
mac: "Cmd-Alt-["
key: "Ctrl-Shift-["
unfoldCommand:
path: ./editor.ts:unfoldCommand
command:
name: "Fold: Unfold"
mac: "Cmd-Alt-]"
key: "Ctrl-Shift-]"
toggleFoldCommand:
path: ./editor.ts:toggleFoldCommand
command:
name: "Fold: Toggle Fold"
mac: "Cmd-Alt-f"
key: "Ctrl-Alt-f"
foldAllCommand:
path: ./editor.ts:foldAllCommand
command:
name: "Fold: Fold All"
key: "Ctrl-Alt-["
unfoldAllCommand:
path: ./editor.ts:unfoldAllCommand
command:
name: "Fold: Unfold All"
key: "Ctrl-Alt-]"
# Vim
toggleVimMode:
path: "./vim.ts:toggleVimMode"
command:
name: "Editor: Toggle Vim Mode"
loadVimRc:
path: "./vim.ts:loadVimRc"
command:
name: "Editor: Vim: Load VIMRC"
events:
- editor:modeswitch
brokenLinksCommand:
path: ./broken_links.ts:brokenLinksCommand
command:
name: "Broken Links: Show"
# Client mode
enableThinClient:
path: ./client.ts:setThinClient
command:
name: "Client: Enable Thin Client"
value: true
disableThinClient:
path: ./client.ts:setThinClient
command:
name: "Client: Disable Thin Client"
value: false
# Random stuff
statsCommand:
path: ./stats.ts:statsCommand
command:
name: "Stats: Show"
reloadUICommand:
path: ./debug.ts:reloadUICommand
command:
name: "Debug: Reload UI"
resetClientCommand:
path: ./debug.ts:resetClientCommand
command:
name: "Debug: Reset Client"
versionCommand:
path: ./help.ts:versionCommand
command:
name: "Help: Version"
gettingStartedCommand:
path: ./help.ts:gettingStartedCommand
command:
name: "Help: Getting Started"
accountLogoutCommand:
path: ./account.ts:accountLogoutCommand
command:
name: "Account: Logout"

View File

@ -1,4 +1,4 @@
import { clientStore, editor } from "$sb/silverbullet-syscall/mod.ts"; import { clientStore, editor } from "$sb/syscalls.ts";
// Run on "editor:init" // Run on "editor:init"
export async function setEditorMode() { export async function setEditorMode() {

View File

@ -1,4 +1,4 @@
import { YAML } from "$sb/plugos-syscall/mod.ts"; import { YAML } from "$sb/syscalls.ts";
import type { WidgetContent } from "$sb/app_event.ts"; import type { WidgetContent } from "$sb/app_event.ts";
type EmbedConfig = { type EmbedConfig = {

View File

@ -1,4 +1,4 @@
import { editor } from "$sb/silverbullet-syscall/mod.ts"; import { editor } from "$sb/syscalls.ts";
import { version } from "../../version.ts"; import { version } from "../../version.ts";
export async function versionCommand() { export async function versionCommand() {

View File

@ -1,6 +1,5 @@
import { nodeAtPos } from "$sb/lib/tree.ts"; import { nodeAtPos } from "$sb/lib/tree.ts";
import { editor, markdown } from "$sb/silverbullet-syscall/mod.ts"; import { editor, events, markdown } from "$sb/syscalls.ts";
import { events } from "$sb/plugos-syscall/mod.ts";
type UnfurlOption = { type UnfurlOption = {
id: string; id: string;

View File

@ -1,5 +1,5 @@
import type { ClickEvent } from "$sb/app_event.ts"; import type { ClickEvent } from "$sb/app_event.ts";
import { editor, markdown, system } from "$sb/silverbullet-syscall/mod.ts"; import { editor, markdown, system } from "$sb/syscalls.ts";
import { import {
addParentPointers, addParentPointers,
findNodeOfType, findNodeOfType,

View File

@ -1,33 +1,9 @@
import type { import type { CompleteEvent } from "$sb/app_event.ts";
CompleteEvent, import { editor, space } from "$sb/syscalls.ts";
IndexEvent,
QueryProviderEvent,
} from "$sb/app_event.ts";
import {
editor,
index,
markdown,
space,
} from "$sb/silverbullet-syscall/mod.ts";
import { events, mq } from "$sb/plugos-syscall/mod.ts";
import { applyQuery } from "$sb/lib/query.ts";
import { invoke } from "$sb/silverbullet-syscall/system.ts";
import type { Message } from "$sb/types.ts";
import { sleep } from "../../common/async_util.ts";
import { cacheFileListing } from "../federation/federation.ts"; import { cacheFileListing } from "../federation/federation.ts";
import type { PageMeta } from "../../web/types.ts"; import type { PageMeta } from "../../web/types.ts";
// Key space:
// meta: => metaJson
export async function pageQueryProvider({
query,
}: QueryProviderEvent): Promise<any[]> {
return applyQuery(query, await space.listPages());
}
export async function deletePage() { export async function deletePage() {
const pageName = await editor.getCurrentPage(); const pageName = await editor.getCurrentPage();
if ( if (
@ -85,12 +61,6 @@ export async function newPageCommand() {
await editor.navigate(pageName); await editor.navigate(pageName);
} }
export async function reindexCommand() {
await editor.flashNotification("Performing full page reindex...");
await reindexSpace();
await editor.flashNotification("Done with page index!");
}
// Completion // Completion
export async function pageComplete(completeEvent: CompleteEvent) { export async function pageComplete(completeEvent: CompleteEvent) {
const match = /\[\[([^\]@:\{}]*)$/.exec(completeEvent.linePrefix); const match = /\[\[([^\]@:\{}]*)$/.exec(completeEvent.linePrefix);
@ -130,51 +100,3 @@ export async function pageComplete(completeEvent: CompleteEvent) {
}), }),
}; };
} }
export async function reindexSpace() {
console.log("Clearing page index...");
await index.clearPageIndex();
// Executed this way to not have to embed the search plug code here
await invoke("search.clearIndex");
const pages = await space.listPages();
// Queue all page names to be indexed
await mq.batchSend("indexQueue", pages.map((page) => page.name));
// Now let's wait for the processing to finish
let queueStats = await mq.getQueueStats("indexQueue");
while (queueStats.queued > 0 || queueStats.processing > 0) {
sleep(1000);
queueStats = await mq.getQueueStats("indexQueue");
}
// And notify the user
console.log("Indexing completed!");
}
export async function processIndexQueue(messages: Message[]) {
for (const message of messages) {
const name: string = message.body;
console.log(`Indexing page ${name}`);
const text = await space.readPage(name);
// console.log("Going to parse markdown");
const parsed = await markdown.parseMarkdown(text);
// console.log("Dispatching ;age:index");
await events.dispatchEvent("page:index", {
name,
tree: parsed,
});
}
}
export async function clearPageIndex(page: string) {
// console.log("Clearing page index for page", page);
await index.clearPageIndexForPage(page);
}
export async function parseIndexTextRepublish({ name, text }: IndexEvent) {
console.log("Reindexing", name);
await events.dispatchEvent("page:index", {
name,
tree: await markdown.parseMarkdown(text),
});
}

View File

@ -1,4 +1,4 @@
import { editor, space } from "$sb/silverbullet-syscall/mod.ts"; import { editor, space } from "$sb/syscalls.ts";
function countWords(str: string): number { function countWords(str: string): number {
const matches = str.match(/[\w\d\'-]+/gi); const matches = str.match(/[\w\d\'-]+/gi);

View File

@ -1,4 +1,4 @@
import { editor } from "$sb/silverbullet-syscall/mod.ts"; import { editor } from "$sb/syscalls.ts";
export async function quoteSelection() { export async function quoteSelection() {
let text = await editor.getText(); let text = await editor.getText();

View File

@ -1,6 +1,5 @@
import { readCodeBlockPage } from "../../plug-api/lib/yaml_page.ts"; import { readCodeBlockPage } from "$sb/lib/yaml_page.ts";
import { editor } from "$sb/silverbullet-syscall/mod.ts"; import { editor, store } from "$sb/syscalls.ts";
import { store } from "$sb/plugos-syscall/mod.ts";
export async function toggleVimMode() { export async function toggleVimMode() {
let vimMode = await store.get("vimMode"); let vimMode = await store.get("vimMode");

View File

@ -1,5 +1,5 @@
import emojis from "./emoji.json" assert { type: "json" }; import emojis from "./emoji.json" assert { type: "json" };
import type { CompleteEvent } from "../../plug-api/app_event.ts"; import type { CompleteEvent } from "$sb/app_event.ts";
export function emojiCompleter({ linePrefix, pos }: CompleteEvent) { export function emojiCompleter({ linePrefix, pos }: CompleteEvent) {
const match = /:([\w]+)$/.exec(linePrefix); const match = /:([\w]+)$/.exec(linePrefix);

View File

@ -1,8 +1,8 @@
import "$sb/lib/fetch.ts"; import "$sb/lib/fetch.ts";
import { federatedPathToUrl } from "$sb/lib/resolve.ts"; import { federatedPathToUrl } from "$sb/lib/resolve.ts";
import { readFederationConfigs } from "./config.ts"; import { readFederationConfigs } from "./config.ts";
import { store } from "$sb/plugos-syscall/mod.ts"; import { store } from "$sb/syscalls.ts";
import { FileMeta } from "$sb/types.ts"; import type { FileMeta } from "$sb/types.ts";
async function responseToFileMeta( async function responseToFileMeta(
r: Response, r: Response,

View File

@ -1,5 +1,5 @@
import { collectNodesOfType } from "$sb/lib/tree.ts"; import { collectNodesOfType } from "$sb/lib/tree.ts";
import { index } from "$sb/silverbullet-syscall/mod.ts"; import { index } from "$sb/syscalls.ts";
import type { CompleteEvent, IndexTreeEvent } from "$sb/app_event.ts"; import type { CompleteEvent, IndexTreeEvent } from "$sb/app_event.ts";
import { removeQueries } from "$sb/lib/query.ts"; import { removeQueries } from "$sb/lib/query.ts";

View File

@ -1,6 +1,6 @@
import { QueryProviderEvent } from "$sb/app_event.ts"; import { QueryProviderEvent } from "$sb/app_event.ts";
import { applyQuery } from "$sb/lib/query.ts"; import { applyQuery } from "$sb/lib/query.ts";
import { space } from "$sb/silverbullet-syscall/mod.ts"; import { space } from "$sb/syscalls.ts";
export async function attachmentQueryProvider({ query }: QueryProviderEvent) { export async function attachmentQueryProvider({ query }: QueryProviderEvent) {
return applyQuery(query, await space.listAttachments()); return applyQuery(query, await space.listAttachments());

View File

@ -1,6 +1,6 @@
import { index } from "$sb/silverbullet-syscall/mod.ts"; import { index } from "$sb/silverbullet-syscall/mod.ts";
import type { CompleteEvent } from "$sb/app_event.ts"; import type { CompleteEvent } from "$sb/app_event.ts";
import { events } from "$sb/plugos-syscall/mod.ts"; import { events } from "$sb/syscalls.ts";
export type AttributeContext = "page" | "item" | "task"; export type AttributeContext = "page" | "item" | "task";

133
plugs/index/index.plug.yaml Normal file
View File

@ -0,0 +1,133 @@
name: index
syntax:
Hashtag:
firstCharacters:
- "#"
regex: "#[^#\\d\\s\\[\\]]+\\w+"
className: sb-hashtag
NamedAnchor:
firstCharacters:
- "$"
regex: "\\$[a-zA-Z\\.\\-\\/]+[\\w\\.\\-\\/]*"
className: sb-named-anchor
functions:
clearPageIndex:
path: "./page.ts:clearPageIndex"
env: server
events:
- page:saved
- page:deleted
pageQueryProvider:
path: ./page.ts:pageQueryProvider
events:
- query:page
parseIndexTextRepublish:
path: "./page.ts:parseIndexTextRepublish"
env: server
events:
- page:index_text
reindexSpaceCommand:
path: "./page.ts:reindexCommand"
command:
name: "Space: Reindex"
processIndexQueue:
path: ./page.ts:processIndexQueue
mqSubscriptions:
- queue: indexQueue
batchSize: 10
autoAck: true
reindexSpace:
path: "./page.ts:reindexSpace"
# Attachments
attachmentQueryProvider:
path: ./attachment.ts:attachmentQueryProvider
events:
- query:attachment
# Backlinks
indexLinks:
path: "./page_links.ts:indexLinks"
events:
- page:index
linkQueryProvider:
path: ./page_links.ts:linkQueryProvider
events:
- query:link
attributeComplete:
path: "./attributes.ts:attributeComplete"
events:
- editor:complete
customAttributeCompleter:
path: ./attributes.ts:customAttributeCompleter
events:
- attribute:complete:page
- attribute:complete:task
- attribute:complete:item
- attribute:complete:*
builtinAttributeCompleter:
path: ./attributes.ts:builtinAttributeCompleter
events:
- attribute:complete:page
- attribute:complete:task
- attribute:complete:item
- attribute:complete:*
# Item indexing
indexItem:
path: "./item.ts:indexItems"
events:
- page:index
itemQueryProvider:
path: "./item.ts:queryProvider"
events:
- query:item
# Anchors
indexAnchors:
path: "./anchor.ts:indexAnchors"
events:
- page:index
anchorComplete:
path: "./anchor.ts:anchorComplete"
events:
- editor:complete
# Hashtags
indexTags:
path: "./tags.ts:indexTags"
events:
- page:index
tagComplete:
path: "./tags.ts:tagComplete"
events:
- editor:complete
tagProvider:
path: "./tags.ts:tagProvider"
events:
- query:tag
renamePageCommand:
path: "./refactor.ts:renamePageCommand"
command:
name: "Page: Rename"
mac: Cmd-Alt-r
key: Ctrl-Alt-r
page: ""
renamePrefixCommand:
path: "./refactor.ts:renamePrefixCommand"
command:
name: "Page: Batch Rename Prefix"
# Refactoring Commands
extractToPageCommand:
path: ./refactor.ts:extractToPageCommand
command:
name: "Page: Extract"

View File

@ -1,6 +1,6 @@
import type { IndexTreeEvent, QueryProviderEvent } from "$sb/app_event.ts"; import type { IndexTreeEvent, QueryProviderEvent } from "$sb/app_event.ts";
import { index } from "$sb/silverbullet-syscall/mod.ts"; import { index } from "$sb/syscalls.ts";
import { collectNodesOfType, ParseTree, renderToText } from "$sb/lib/tree.ts"; import { collectNodesOfType, ParseTree, renderToText } from "$sb/lib/tree.ts";
import { applyQuery, removeQueries } from "$sb/lib/query.ts"; import { applyQuery, removeQueries } from "$sb/lib/query.ts";
import { extractAttributes } from "$sb/lib/attribute.ts"; import { extractAttributes } from "$sb/lib/attribute.ts";

77
plugs/index/page.ts Normal file
View File

@ -0,0 +1,77 @@
import type { IndexEvent, QueryProviderEvent } from "$sb/app_event.ts";
import {
editor,
events,
index,
markdown,
mq,
space,
system,
} from "$sb/syscalls.ts";
import { applyQuery } from "$sb/lib/query.ts";
import type { MQMessage } from "$sb/types.ts";
import { sleep } from "../../common/async_util.ts";
// Key space:
// meta: => metaJson
export async function pageQueryProvider({
query,
}: QueryProviderEvent): Promise<any[]> {
return applyQuery(query, await space.listPages());
}
export async function reindexCommand() {
await editor.flashNotification("Performing full page reindex...");
await reindexSpace();
await editor.flashNotification("Done with page index!");
}
export async function reindexSpace() {
console.log("Clearing page index...");
await index.clearPageIndex();
// Executed this way to not have to embed the search plug code here
await system.invokeFunction("search.clearIndex");
const pages = await space.listPages();
// Queue all page names to be indexed
await mq.batchSend("indexQueue", pages.map((page) => page.name));
// Now let's wait for the processing to finish
let queueStats = await mq.getQueueStats("indexQueue");
while (queueStats.queued > 0 || queueStats.processing > 0) {
sleep(1000);
queueStats = await mq.getQueueStats("indexQueue");
}
// And notify the user
console.log("Indexing completed!");
}
export async function processIndexQueue(messages: MQMessage[]) {
for (const message of messages) {
const name: string = message.body;
console.log(`Indexing page ${name}`);
const text = await space.readPage(name);
// console.log("Going to parse markdown");
const parsed = await markdown.parseMarkdown(text);
// console.log("Dispatching ;age:index");
await events.dispatchEvent("page:index", {
name,
tree: parsed,
});
}
}
export async function clearPageIndex(page: string) {
// console.log("Clearing page index for page", page);
await index.clearPageIndexForPage(page);
}
export async function parseIndexTextRepublish({ name, text }: IndexEvent) {
console.log("Reindexing", name);
await events.dispatchEvent("page:index", {
name,
tree: await markdown.parseMarkdown(text),
});
}

View File

@ -1,4 +1,4 @@
import { index } from "$sb/silverbullet-syscall/mod.ts"; import { index } from "$sb/syscalls.ts";
import { findNodeOfType, traverseTree } from "$sb/lib/tree.ts"; import { findNodeOfType, traverseTree } from "$sb/lib/tree.ts";
import { extractFrontmatter } from "$sb/lib/frontmatter.ts"; import { extractFrontmatter } from "$sb/lib/frontmatter.ts";
import { extractAttributes } from "$sb/lib/attribute.ts"; import { extractAttributes } from "$sb/lib/attribute.ts";

View File

@ -1,4 +1,4 @@
import { editor, space } from "$sb/silverbullet-syscall/mod.ts"; import { editor, space } from "$sb/syscalls.ts";
import { validatePageName } from "$sb/lib/page.ts"; import { validatePageName } from "$sb/lib/page.ts";
import { getBackLinks } from "./page_links.ts"; import { getBackLinks } from "./page_links.ts";

View File

@ -1,12 +1,12 @@
import { collectNodesOfType } from "$sb/lib/tree.ts"; import { collectNodesOfType } from "$sb/lib/tree.ts";
import { index } from "$sb/silverbullet-syscall/mod.ts"; import { index } from "$sb/syscalls.ts";
import type { import type {
CompleteEvent, CompleteEvent,
IndexTreeEvent, IndexTreeEvent,
QueryProviderEvent, QueryProviderEvent,
} from "$sb/app_event.ts"; } from "$sb/app_event.ts";
import { applyQuery, removeQueries } from "$sb/lib/query.ts"; import { applyQuery, removeQueries } from "$sb/lib/query.ts";
import { extractFrontmatter } from "../../plug-api/lib/frontmatter.ts"; import { extractFrontmatter } from "$sb/lib/frontmatter.ts";
// Key space // Key space
// tag:TAG => true (for completion) // tag:TAG => true (for completion)

View File

@ -1,7 +1,6 @@
import { editor } from "$sb/silverbullet-syscall/mod.ts"; import { clientStore, editor } from "$sb/syscalls.ts";
import { readSettings } from "$sb/lib/settings_page.ts"; import { readSettings } from "$sb/lib/settings_page.ts";
import { updateMarkdownPreview } from "./preview.ts"; import { updateMarkdownPreview } from "./preview.ts";
import { clientStore } from "$sb/silverbullet-syscall/mod.ts";
export async function togglePreview() { export async function togglePreview() {
const currentValue = !!(await clientStore.get("enableMarkdownPreview")); const currentValue = !!(await clientStore.get("enableMarkdownPreview"));

View File

@ -10,7 +10,7 @@ import { assertEquals } from "../../test_deps.ts";
Deno.test("Markdown render", async () => { Deno.test("Markdown render", async () => {
const system = new System<any>("server"); const system = new System<any>("server");
await system.load( await system.load(
new URL("../../dist_plug_bundle/_plug/core.plug.js", import.meta.url), new URL("../../dist_plug_bundle/_plug/editor.plug.js", import.meta.url),
createSandbox, createSandbox,
); );
await system.load( await system.load(

View File

@ -1,6 +1,4 @@
import { clientStore, editor, system } from "$sb/silverbullet-syscall/mod.ts"; import { asset, clientStore, editor, markdown, system } from "$sb/syscalls.ts";
import { asset } from "$sb/plugos-syscall/mod.ts";
import { parseMarkdown } from "$sb/silverbullet-syscall/markdown.ts";
import { renderMarkdownToHtml } from "./markdown_render.ts"; import { renderMarkdownToHtml } from "./markdown_render.ts";
import { resolvePath } from "$sb/lib/resolve.ts"; import { resolvePath } from "$sb/lib/resolve.ts";
@ -10,7 +8,7 @@ export async function updateMarkdownPreview() {
} }
const currentPage = await editor.getCurrentPage(); const currentPage = await editor.getCurrentPage();
const text = await editor.getText(); const text = await editor.getText();
const mdTree = await parseMarkdown(text); const mdTree = await markdown.parseMarkdown(text);
// const cleanMd = await cleanMarkdown(text); // const cleanMd = await cleanMarkdown(text);
const css = await asset.readAsset("assets/styles.css"); const css = await asset.readAsset("assets/styles.css");
const js = await asset.readAsset("assets/handler.js"); const js = await asset.readAsset("assets/handler.js");

View File

@ -3,7 +3,7 @@ import {
renderToText, renderToText,
replaceNodesMatching, replaceNodesMatching,
} from "$sb/lib/tree.ts"; } from "$sb/lib/tree.ts";
import { markdown } from "$sb/silverbullet-syscall/mod.ts"; import { markdown } from "$sb/syscalls.ts";
export function encodePageUrl(name: string): string { export function encodePageUrl(name: string): string {
return name; return name;

View File

@ -1,13 +1,13 @@
import { parseMarkdown } from "$sb/silverbullet-syscall/markdown.ts"; import { markdown } from "$sb/syscalls.ts";
import type { WidgetContent } from "$sb/app_event.ts"; import type { WidgetContent } from "$sb/app_event.ts";
import { renderMarkdownToHtml } from "./markdown_render.ts"; import { renderMarkdownToHtml } from "./markdown_render.ts";
export async function markdownWidget( export async function markdownWidget(
bodyText: string, bodyText: string,
): Promise<WidgetContent> { ): Promise<WidgetContent> {
const mdTree = await parseMarkdown(bodyText); const mdTree = await markdown.parseMarkdown(bodyText);
const html = await renderMarkdownToHtml(mdTree, { const html = renderMarkdownToHtml(mdTree, {
smartHardBreak: true, smartHardBreak: true,
}); });
return Promise.resolve({ return Promise.resolve({

View File

@ -0,0 +1,26 @@
name: plug-manager
requiredPermissions:
- fetch
functions:
updatePlugsCommand:
path: ./plugmanager.ts:updatePlugsCommand
command:
name: "Plugs: Update"
key: "Ctrl-Shift-p"
mac: "Cmd-Shift-p"
getPlugHTTPS:
path: "./plugmanager.ts:getPlugHTTPS"
events:
- get-plug:https
getPlugGithub:
path: "./plugmanager.ts:getPlugGithub"
events:
- get-plug:github
getPlugGithubRelease:
path: "./plugmanager.ts:getPlugGithubRelease"
events:
- get-plug:ghr
addPlugCommand:
path: ./plugmanager.ts:addPlugCommand
command:
name: "Plugs: Add"

View File

@ -1,5 +1,4 @@
import { events } from "$sb/plugos-syscall/mod.ts"; import { editor, events, space, system } from "$sb/syscalls.ts";
import { editor, space, system } from "$sb/silverbullet-syscall/mod.ts";
import { readYamlPage } from "$sb/lib/yaml_page.ts"; import { readYamlPage } from "$sb/lib/yaml_page.ts";
import { builtinPlugNames } from "../builtin_plugs.ts"; import { builtinPlugNames } from "../builtin_plugs.ts";

View File

@ -58,7 +58,7 @@ export class SimpleSearchEngine {
const uniqueStemmedWords = [...new Set(stemmedWords)]; const uniqueStemmedWords = [...new Set(stemmedWords)];
const currentIdsArray = await this.index.get(uniqueStemmedWords); const currentIdsArray = await this.index.get(uniqueStemmedWords);
stemmedWords.forEach((stemmedWord, i) => { stemmedWords.forEach((stemmedWord) => {
const currentIds = const currentIds =
currentIdsArray[uniqueStemmedWords.indexOf(stemmedWord)] || []; currentIdsArray[uniqueStemmedWords.indexOf(stemmedWord)] || [];

View File

@ -3,7 +3,7 @@ functions:
indexPage: indexPage:
path: search.ts:indexPage path: search.ts:indexPage
# Only enable in client for now # Only enable in client for now
env: client # env: client
events: events:
- page:index - page:index

View File

@ -1,8 +1,7 @@
import { IndexTreeEvent, QueryProviderEvent } from "$sb/app_event.ts"; import { IndexTreeEvent, QueryProviderEvent } from "$sb/app_event.ts";
import { renderToText } from "$sb/lib/tree.ts"; import { renderToText } from "$sb/lib/tree.ts";
import { store } from "$sb/plugos-syscall/mod.ts";
import { applyQuery } from "$sb/lib/query.ts"; import { applyQuery } from "$sb/lib/query.ts";
import { editor, index } from "$sb/silverbullet-syscall/mod.ts"; import { editor, index, store } from "$sb/syscalls.ts";
import { BatchKVStore, SimpleSearchEngine } from "./engine.ts"; import { BatchKVStore, SimpleSearchEngine } from "./engine.ts";
import { FileMeta } from "$sb/types.ts"; import { FileMeta } from "$sb/types.ts";
@ -26,10 +25,10 @@ class StoreKVStore implements BatchKVStore<string, string[]> {
} }
} }
const engine = new SimpleSearchEngine( const ftsKvStore = new StoreKVStore("fts:");
new StoreKVStore("fts:"), const ftsRevKvStore = new StoreKVStore("fts_rev:");
new StoreKVStore("fts_rev:"),
); const engine = new SimpleSearchEngine(ftsKvStore, ftsRevKvStore);
export async function indexPage({ name, tree }: IndexTreeEvent) { export async function indexPage({ name, tree }: IndexTreeEvent) {
const text = renderToText(tree); const text = renderToText(tree);

View File

@ -1,5 +1,4 @@
import { events } from "$sb/plugos-syscall/mod.ts"; import { editor, events, markdown } from "$sb/syscalls.ts";
import { editor, markdown } from "$sb/silverbullet-syscall/mod.ts";
import { extractFrontmatter } from "$sb/lib/frontmatter.ts"; import { extractFrontmatter } from "$sb/lib/frontmatter.ts";
import { PublishEvent } from "$sb/app_event.ts"; import { PublishEvent } from "$sb/app_event.ts";

View File

@ -0,0 +1,9 @@
name: sync
functions:
syncSpaceCommand:
path: "./sync.ts:syncSpaceCommand"
command:
name: "Sync: Now"
key: "Alt-Shift-s"
mac: "Cmd-Shift-s"

View File

@ -1,4 +1,4 @@
import { editor, sync } from "$sb/silverbullet-syscall/mod.ts"; import { editor, sync } from "$sb/syscalls.ts";
export async function syncSpaceCommand() { export async function syncSpaceCommand() {
await editor.flashNotification("Syncing space..."); await editor.flashNotification("Syncing space...");

View File

@ -4,13 +4,7 @@ import type {
QueryProviderEvent, QueryProviderEvent,
} from "$sb/app_event.ts"; } from "$sb/app_event.ts";
import { import { editor, index, markdown, space, sync } from "$sb/syscalls.ts";
editor,
index,
markdown,
space,
sync,
} from "$sb/silverbullet-syscall/mod.ts";
import { import {
addParentPointers, addParentPointers,
@ -26,7 +20,7 @@ import { applyQuery, removeQueries } from "$sb/lib/query.ts";
import { niceDate } from "$sb/lib/dates.ts"; import { niceDate } from "$sb/lib/dates.ts";
import { extractAttributes } from "$sb/lib/attribute.ts"; import { extractAttributes } from "$sb/lib/attribute.ts";
import { rewritePageRefs } from "$sb/lib/resolve.ts"; import { rewritePageRefs } from "$sb/lib/resolve.ts";
import { indexAttributes } from "../core/attributes.ts"; import { indexAttributes } from "../index/attributes.ts";
export type Task = { export type Task = {
name: string; name: string;

View File

@ -20,7 +20,7 @@ syntax:
backgroundColor: "rgba(22,22,22,0.07)" backgroundColor: "rgba(22,22,22,0.07)"
functions: functions:
turnIntoTask: turnIntoTask:
redirect: core.applyLineReplace redirect: template.applyLineReplace
slashCommand: slashCommand:
name: task name: task
description: Turn into task description: Turn into task

View File

@ -0,0 +1,115 @@
name: template
functions:
# Template commands
insertTemplateText:
path: "./template.ts:insertTemplateText"
applyLineReplace:
path: ./template.ts:applyLineReplace
insertFrontMatter:
redirect: insertTemplateText
slashCommand:
name: front-matter
description: Insert page front matter
value: |
---
|^|
---
makeH1:
redirect: applyLineReplace
slashCommand:
name: h1
description: Turn line into h1 header
match: "^#*\\s*"
replace: "# "
makeH2:
redirect: applyLineReplace
slashCommand:
name: h2
description: Turn line into h2 header
match: "^#*\\s*"
replace: "## "
makeH3:
redirect: applyLineReplace
slashCommand:
name: h3
description: Turn line into h3 header
match: "^#*\\s*"
replace: "### "
makeH4:
redirect: applyLineReplace
slashCommand:
name: h4
description: Turn line into h4 header
match: "^#*\\s*"
replace: "#### "
insertCodeBlock:
redirect: insertTemplateText
slashCommand:
name: code
description: Insert code block
value: |
```
|^|
```
insertHRTemplate:
redirect: insertTemplateText
slashCommand:
name: hr
description: Insert a horizontal rule
value: "---"
insertTable:
redirect: insertTemplateText
slashCommand:
name: table
description: Insert a table
boost: -1 # Low boost because it's likely not very commonly used
value: |
| Header A | Header B |
|----------|----------|
| Cell A|^| | Cell B |
quickNoteCommand:
path: ./template.ts:quickNoteCommand
command:
name: "Quick Note"
key: "Alt-Shift-n"
priority: 1
dailyNoteCommand:
path: ./template.ts:dailyNoteCommand
command:
name: "Open Daily Note"
key: "Alt-Shift-d"
weeklyNoteCommand:
path: ./template.ts:weeklyNoteCommand
command:
name: "Open Weekly Note"
key: "Alt-Shift-w"
instantiateTemplateCommand:
path: ./template.ts:instantiateTemplateCommand
command:
name: "Template: Instantiate Page"
insertSnippet:
path: ./template.ts:insertSnippet
command:
name: "Template: Insert Snippet"
slashCommand:
name: snippet
description: Insert a snippet
applyPageTemplateCommand:
path: ./template.ts:applyPageTemplateCommand
slashCommand:
name: page-template
description: Apply a page template
insertTodayCommand:
path: "./template.ts:insertTemplateText"
slashCommand:
name: today
description: Insert today's date
value: "{{today}}"
insertTomorrowCommand:
path: "./template.ts:insertTemplateText"
slashCommand:
name: tomorrow
description: Insert tomorrow's date
value: "{{tomorrow}}"

View File

@ -1,4 +1,4 @@
import { editor, markdown, space } from "$sb/silverbullet-syscall/mod.ts"; import { editor, markdown, space } from "$sb/syscalls.ts";
import { extractFrontmatter } from "$sb/lib/frontmatter.ts"; import { extractFrontmatter } from "$sb/lib/frontmatter.ts";
import { renderToText } from "$sb/lib/tree.ts"; import { renderToText } from "$sb/lib/tree.ts";
import { niceDate } from "$sb/lib/dates.ts"; import { niceDate } from "$sb/lib/dates.ts";

View File

@ -11,7 +11,6 @@ import { EndpointHook } from "../plugos/hooks/endpoint.ts";
import { EventHook } from "../plugos/hooks/event.ts"; import { EventHook } from "../plugos/hooks/event.ts";
import { MQHook } from "../plugos/hooks/mq.ts"; import { MQHook } from "../plugos/hooks/mq.ts";
import { DenoKVStore } from "../plugos/lib/kv_store.deno_kv.ts"; import { DenoKVStore } from "../plugos/lib/kv_store.deno_kv.ts";
import { DexieMQ } from "../plugos/lib/mq.dexie.ts";
import assetSyscalls from "../plugos/syscalls/asset.ts"; import assetSyscalls from "../plugos/syscalls/asset.ts";
import { eventSyscalls } from "../plugos/syscalls/event.ts"; import { eventSyscalls } from "../plugos/syscalls/event.ts";
import { mqSyscalls } from "../plugos/syscalls/mq.dexie.ts"; import { mqSyscalls } from "../plugos/syscalls/mq.dexie.ts";
@ -19,16 +18,16 @@ import { storeSyscalls } from "../plugos/syscalls/store.ts";
import { System } from "../plugos/system.ts"; import { System } from "../plugos/system.ts";
import { Space } from "../web/space.ts"; import { Space } from "../web/space.ts";
import { debugSyscalls } from "../web/syscalls/debug.ts"; import { debugSyscalls } from "../web/syscalls/debug.ts";
import { pageIndexSyscalls } from "../cli/syscalls/index.ts"; import { pageIndexSyscalls } from "./syscalls/index.ts";
import { markdownSyscalls } from "../web/syscalls/markdown.ts"; import { markdownSyscalls } from "../web/syscalls/markdown.ts";
import { spaceSyscalls } from "../cli/syscalls/space.ts"; import { spaceSyscalls } from "./syscalls/space.ts";
import { systemSyscalls } from "../web/syscalls/system.ts"; import { systemSyscalls } from "../web/syscalls/system.ts";
import { yamlSyscalls } from "../web/syscalls/yaml.ts"; import { yamlSyscalls } from "../web/syscalls/yaml.ts";
import { Application, path } from "./deps.ts"; import { Application, path } from "./deps.ts";
import { sandboxFetchSyscalls } from "../plugos/syscalls/fetch.ts"; import { sandboxFetchSyscalls } from "../plugos/syscalls/fetch.ts";
import { shellSyscalls } from "../plugos/syscalls/shell.deno.ts"; import { shellSyscalls } from "../plugos/syscalls/shell.deno.ts";
import { IDBKeyRange, indexedDB } from "https://esm.sh/fake-indexeddb@4.0.2";
import { SpacePrimitives } from "../common/spaces/space_primitives.ts"; import { SpacePrimitives } from "../common/spaces/space_primitives.ts";
import { DenoKvMQ } from "../plugos/lib/mq.deno_kv.ts";
const fileListInterval = 30 * 1000; // 30s const fileListInterval = 30 * 1000; // 30s
@ -38,6 +37,7 @@ export class ServerSystem {
private requeueInterval?: number; private requeueInterval?: number;
kvStore?: DenoKVStore; kvStore?: DenoKVStore;
listInterval?: number; listInterval?: number;
denoKv!: Deno.Kv;
constructor( constructor(
private baseSpacePrimitives: SpacePrimitives, private baseSpacePrimitives: SpacePrimitives,
@ -56,19 +56,15 @@ export class ServerSystem {
const cronHook = new CronHook(this.system); const cronHook = new CronHook(this.system);
this.system.addHook(cronHook); this.system.addHook(cronHook);
this.kvStore = new DenoKVStore(); this.denoKv = await Deno.openKv(this.dbPath);
await this.kvStore.init(this.dbPath);
this.kvStore = new DenoKVStore(this.denoKv);
// Endpoint hook // Endpoint hook
this.system.addHook(new EndpointHook(this.app, "/_/")); this.system.addHook(new EndpointHook(this.app, "/_/"));
// Use DexieMQ for this, in memory // Use DexieMQ for this, in memory
const mq = new DexieMQ("mq", indexedDB, IDBKeyRange); const mq = new DenoKvMQ(this.denoKv);
this.requeueInterval = setInterval(() => {
// Timeout after 5s, retries 3 times, otherwise drops the message (no DLQ)
mq.requeueTimeouts(5000, 3, true).catch(console.error);
}, 20000); // Look to requeue every 20s
const pageIndexCalls = pageIndexSyscalls(this.kvStore); const pageIndexCalls = pageIndexSyscalls(this.kvStore);
@ -148,9 +144,7 @@ export class ServerSystem {
const tempDir = await Deno.makeTempDir(); const tempDir = await Deno.makeTempDir();
try { try {
for (const { name } of await this.spacePrimitives.fetchFileList()) { for (const { name } of await this.spacePrimitives.fetchFileList()) {
if ( if (name.endsWith(".plug.js")) {
name.endsWith(".plug.js") // && !filePath.includes("search.plug.js")
) {
const plugPath = path.join(tempDir, name); const plugPath = path.join(tempDir, name);
await Deno.mkdir(path.dirname(plugPath), { recursive: true }); await Deno.mkdir(path.dirname(plugPath), { recursive: true });
await Deno.writeFile( await Deno.writeFile(

View File

@ -4,8 +4,8 @@ import { pageIndexSyscalls } from "./index.ts";
Deno.test("Test KV index", async () => { Deno.test("Test KV index", async () => {
const ctx: any = {}; const ctx: any = {};
const kv = new DenoKVStore(); const denoKv = await Deno.openKv("test.db");
await kv.init("test.db"); const kv = new DenoKVStore(denoKv);
const calls = pageIndexSyscalls(kv); const calls = pageIndexSyscalls(kv);
await calls["index.set"](ctx, "page", "test", "value"); await calls["index.set"](ctx, "page", "test", "value");
assertEquals(await calls["index.get"](ctx, "page", "test"), "value"); assertEquals(await calls["index.get"](ctx, "page", "test"), "value");
@ -33,5 +33,6 @@ Deno.test("Test KV index", async () => {
await calls["index.clearPageIndex"](ctx); await calls["index.clearPageIndex"](ctx);
results = await calls["index.queryPrefix"](ctx, ""); results = await calls["index.queryPrefix"](ctx, "");
assertEquals(results.length, 0); assertEquals(results.length, 0);
await kv.delete(); denoKv.close();
await Deno.remove("test.db");
}); });

View File

@ -1,7 +1,8 @@
import { safeRun } from "../common/util.ts"; import { safeRun } from "../common/util.ts";
import { Client } from "./client.ts"; import { Client } from "./client.ts";
const thinClientMode = window.silverBulletConfig.thinClientMode === "on"; const thinClientMode = !!localStorage.getItem("thinClientMode");
safeRun(async () => { safeRun(async () => {
console.log("Booting SilverBullet..."); console.log("Booting SilverBullet...");

View File

@ -47,7 +47,6 @@ declare global {
// Injected via index.html // Injected via index.html
silverBulletConfig: { silverBulletConfig: {
spaceFolderPath: string; spaceFolderPath: string;
thinClientMode: "on" | "off";
}; };
client: Client; client: Client;
} }

View File

@ -35,6 +35,7 @@ import { MQHook } from "../plugos/hooks/mq.ts";
import { mqSyscalls } from "../plugos/syscalls/mq.dexie.ts"; import { mqSyscalls } from "../plugos/syscalls/mq.dexie.ts";
import { indexProxySyscalls } from "./syscalls/index.proxy.ts"; import { indexProxySyscalls } from "./syscalls/index.proxy.ts";
import { storeProxySyscalls } from "./syscalls/store.proxy.ts"; import { storeProxySyscalls } from "./syscalls/store.proxy.ts";
import { mqProxySyscalls } from "./syscalls/mq.proxy.ts";
export class ClientSystem { export class ClientSystem {
commandHook: CommandHook; commandHook: CommandHook;
@ -82,7 +83,9 @@ export class ClientSystem {
this.system.addHook(this.codeWidgetHook); this.system.addHook(this.codeWidgetHook);
// MQ hook // MQ hook
this.system.addHook(new MQHook(this.system, this.mq)); if (!this.thinClientMode) {
this.system.addHook(new MQHook(this.system, this.mq));
}
// Command hook // Command hook
this.commandHook = new CommandHook(); this.commandHook = new CommandHook();
@ -120,17 +123,17 @@ export class ClientSystem {
// console.log("New file list", files); // console.log("New file list", files);
// }); // });
this.eventHook.addLocalListener("file:changed", (file) => { // this.eventHook.addLocalListener("file:changed", (file) => {
console.log("File changed", file); // console.log("File changed", file);
}); // });
this.eventHook.addLocalListener("file:created", (file) => { // this.eventHook.addLocalListener("file:created", (file) => {
console.log("File created", file); // console.log("File created", file);
}); // });
this.eventHook.addLocalListener("file:deleted", (file) => { // this.eventHook.addLocalListener("file:deleted", (file) => {
console.log("File deleted", file); // console.log("File deleted", file);
}); // });
this.registerSyscalls(); this.registerSyscalls();
} }
@ -154,7 +157,7 @@ export class ClientSystem {
markdownSyscalls(buildMarkdown(this.mdExtensions)), markdownSyscalls(buildMarkdown(this.mdExtensions)),
assetSyscalls(this.system), assetSyscalls(this.system),
yamlSyscalls(), yamlSyscalls(),
mqSyscalls(this.mq), this.thinClientMode ? mqProxySyscalls(this.client) : mqSyscalls(this.mq),
storeCalls, storeCalls,
this.indexSyscalls, this.indexSyscalls,
debugSyscalls(), debugSyscalls(),

View File

@ -195,7 +195,7 @@ export class MainUI {
return; return;
} }
console.log("Now renaming page to...", newName); console.log("Now renaming page to...", newName);
await editor.system.system.loadedPlugs.get("core")!.invoke( await editor.system.system.loadedPlugs.get("index")!.invoke(
"renamePageCommand", "renamePageCommand",
[{ page: newName }], [{ page: newName }],
); );

View File

@ -34,14 +34,12 @@
}; };
window.silverBulletConfig = { window.silverBulletConfig = {
// These {{VARIABLES}} are replaced by http_server.ts // These {{VARIABLES}} are replaced by http_server.ts
spaceFolderPath: "{{SPACE_PATH}}", spaceFolderPath: "{{SPACE_PATH}}"
thinClientMode: "{{THIN_CLIENT_MODE}}",
}; };
// But in case these variables aren't replaced by the server, fall back fully static mode (no sync) // But in case these variables aren't replaced by the server, fall back fully static mode (no sync)
if (window.silverBulletConfig.spaceFolderPath.includes("{{")) { if (window.silverBulletConfig.spaceFolderPath.includes("{{")) {
window.silverBulletConfig = { window.silverBulletConfig = {
spaceFolderPath: "", spaceFolderPath: "",
thinClientMode: "off",
}; };
} }
</script> </script>

View File

@ -171,9 +171,20 @@ export function editorSyscalls(editor: Client): SysCallMapping {
return editor.confirm(message); return editor.confirm(message);
}, },
"editor.getUiOption": (_ctx, key: string): any => { "editor.getUiOption": (_ctx, key: string): any => {
if (key === "thinClientMode") {
return !!localStorage.getItem("thinClientMode");
}
return (editor.ui.viewState.uiOptions as any)[key]; return (editor.ui.viewState.uiOptions as any)[key];
}, },
"editor.setUiOption": (_ctx, key: string, value: any) => { "editor.setUiOption": (_ctx, key: string, value: any) => {
if (key === "thinClientMode") {
if (value) {
localStorage.setItem("thinClientMode", "true");
} else {
localStorage.removeItem("thinClientMode");
}
return;
}
editor.ui.viewDispatch({ editor.ui.viewDispatch({
type: "set-ui-option", type: "set-ui-option",
key, key,

13
web/syscalls/mq.proxy.ts Normal file
View File

@ -0,0 +1,13 @@
import { SysCallMapping } from "../../plugos/system.ts";
import { Client } from "../client.ts";
import { proxySyscalls } from "./util.ts";
export function mqProxySyscalls(client: Client): SysCallMapping {
return proxySyscalls(client, [
"mq.send",
"mq.batchSend",
"mq.ack",
"mq.batchAck",
"mq.getQueueStats",
]);
}

View File

@ -10,16 +10,6 @@ export function systemSyscalls(
): SysCallMapping { ): SysCallMapping {
const api: SysCallMapping = { const api: SysCallMapping = {
"system.invokeFunction": ( "system.invokeFunction": (
ctx,
_env: string,
name: string,
...args: any[]
) => {
// For backwards compatibility
// TODO: Remove at some point
return api["system.invoke"](ctx, name, ...args);
},
"system.invoke": (
ctx, ctx,
name: string, name: string,
...args: any[] ...args: any[]
@ -28,6 +18,12 @@ export function systemSyscalls(
throw Error("No plug associated with context"); throw Error("No plug associated with context");
} }
if (name === "server" || name === "client") {
// Backwards compatibility mode (previously there was an 'env' argument)
name = args[0];
args = args.slice(1);
}
let plug: Plug<any> | undefined = ctx.plug; let plug: Plug<any> | undefined = ctx.plug;
if (name.indexOf(".") !== -1) { if (name.indexOf(".") !== -1) {
// plug name in the name // plug name in the name
@ -44,7 +40,7 @@ export function systemSyscalls(
} }
if (functionDef.env && system.env && functionDef.env !== system.env) { if (functionDef.env && system.env && functionDef.env !== system.env) {
// Proxy to another environment // Proxy to another environment
return proxySyscall(editor.remoteSpacePrimitives, name, args); return proxySyscall(ctx, editor.remoteSpacePrimitives, name, args);
} }
return plug.invoke(name, args); return plug.invoke(name, args);
}, },

View File

@ -1,19 +1,21 @@
import { plugCompileCommand } from "../../cmd/plug_compile.ts";
import { HttpSpacePrimitives } from "../../common/spaces/http_space_primitives.ts"; import { HttpSpacePrimitives } from "../../common/spaces/http_space_primitives.ts";
import { SysCallMapping } from "../../plugos/system.ts"; import { SyscallContext, SysCallMapping } from "../../plugos/system.ts";
import { SyscallResponse } from "../../server/rpc.ts"; import { SyscallResponse } from "../../server/rpc.ts";
import { Client } from "../client.ts"; import { Client } from "../client.ts";
export function proxySyscalls(client: Client, names: string[]): SysCallMapping { export function proxySyscalls(client: Client, names: string[]): SysCallMapping {
const syscalls: SysCallMapping = {}; const syscalls: SysCallMapping = {};
for (const name of names) { for (const name of names) {
syscalls[name] = (_ctx, ...args: any[]) => { syscalls[name] = (ctx, ...args: any[]) => {
return proxySyscall(client.remoteSpacePrimitives, name, args); return proxySyscall(ctx, client.remoteSpacePrimitives, name, args);
}; };
} }
return syscalls; return syscalls;
} }
export async function proxySyscall( export async function proxySyscall(
ctx: SyscallContext,
httpSpacePrimitives: HttpSpacePrimitives, httpSpacePrimitives: HttpSpacePrimitives,
name: string, name: string,
args: any[], args: any[],
@ -23,6 +25,7 @@ export async function proxySyscall(
{ {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
ctx: ctx.plug.name,
operation: "syscall", operation: "syscall",
name, name,
args, args,

View File

@ -3,6 +3,12 @@ release.
--- ---
## Next
* Another heavy behind-the-scenes refactoring release, refactoring the large “core” plug into multiple smaller ones, documentation to be updated to reflect this.
* Removed [[Cloud Links]] support in favor of [[Federation]]
---
## 0.3.11 ## 0.3.11
* Cookies set when using SilverBullet's built-in [[Authentication]] are now per domain + port, allowing you to run multiple instances of SB on a single host with different ports without the authentication interfering. * Cookies set when using SilverBullet's built-in [[Authentication]] are now per domain + port, allowing you to run multiple instances of SB on a single host with different ports without the authentication interfering.

View File

@ -1,5 +1 @@
You can access the “markdown web” through SilverBullet directly. The idea of the the markdown web (we really need a better name) is simple: the Internet is a messy place — tracking everywhere, tons of banners and other stuff  lets bring it back to the basics. How about Markdown? Deprecated in favor of [[Federation]]
SilverBullet supports navigating this markdown web via cloud links, theyre simply wiki links that start with “💭 “. For instance: [[💭 silverbullet.md/SilverBullet]]. When you click one of these links, SilverBullet will simply pull in its content and present it to you in read-only mode (and do some clever internal link rewriting). What it does is simply fetch the link via HTTPs and postfix the url with `.md`, so [[💭 silverbullet.md/SilverBullet]] will fetch [this page](https://silverbullet.md/Silver%20Bullet.md). You can access all of the SilverBullet website this way, for instance heres the [[💭 silverbullet.md/CHANGELOG]].
To publish your own content this way, simply create an `index.md` on your host (this will be fetched as the main page), and publish all the rest of your content as `.md` files alongside it. Thats all theres to it.

View File

@ -33,7 +33,7 @@ spaceIgnore: |
plugOverrides: plugOverrides:
core: core:
# Matching this YAML structure: # Matching this YAML structure:
# https://github.com/silverbulletmd/silverbullet/blob/main/plugs/core/core.plug.yaml # https://github.com/silverbulletmd/silverbullet/blob/main/plugs/editor/editor.plug.yaml
# and overriding the "key" for centering the cursor # and overriding the "key" for centering the cursor
functions.centerCursor.command.key: Ctrl-Alt-p functions.centerCursor.command.key: Ctrl-Alt-p
# However, it's even possible to define custom slash commands this way without building a plug (/today-header in this case): # However, it's even possible to define custom slash commands this way without building a plug (/today-header in this case):

View File

@ -57,7 +57,7 @@ A simple example is multiplying numbers:
However, you can also invoke arbitrary plug functions, e.g. the `titleUnfurlOptions` function in the `core` plug: However, you can also invoke arbitrary plug functions, e.g. the `titleUnfurlOptions` function in the `core` plug:
<!-- #eval core.titleUnfurlOptions() --> <!-- #eval editor.titleUnfurlOptions() -->
|id |name | |id |name |
|------------|-------------| |------------|-------------|
|title-unfurl|Extract title| |title-unfurl|Extract title|
@ -65,7 +65,7 @@ However, you can also invoke arbitrary plug functions, e.g. the `titleUnfurlOpti
Optionally, you can use a `render` clause to render the result as a template, similar to [[🔌 Directive/Query]]: Optionally, you can use a `render` clause to render the result as a template, similar to [[🔌 Directive/Query]]:
<!-- #eval core.titleUnfurlOptions() render [[template/debug]] --> <!-- #eval editor.titleUnfurlOptions() render [[template/debug]] -->
id: title-unfurl id: title-unfurl
name: Extract title name: Extract title
--- ---