@silverbulletmd/silverbullet 2.5.3 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +4 -6
  2. package/client/asset_bundle/bundle.ts +3 -9
  3. package/client/data/datastore.ts +4 -5
  4. package/client/markdown_parser/constants.ts +4 -2
  5. package/client/plugos/hooks/code_widget.ts +3 -5
  6. package/client/plugos/hooks/command.ts +54 -24
  7. package/client/plugos/hooks/document_editor.ts +10 -12
  8. package/client/plugos/hooks/event.ts +33 -36
  9. package/client/plugos/hooks/mq.ts +17 -17
  10. package/client/plugos/hooks/plug_namespace.ts +3 -5
  11. package/client/plugos/hooks/slash_command.ts +15 -28
  12. package/client/plugos/hooks/syscall.ts +3 -3
  13. package/client/plugos/manifest_cache.ts +22 -15
  14. package/client/plugos/plug.ts +7 -7
  15. package/client/plugos/plug_compile.ts +119 -67
  16. package/client/plugos/protocol.ts +28 -28
  17. package/client/plugos/proxy_fetch.ts +7 -6
  18. package/client/plugos/sandboxes/worker_sandbox.ts +26 -15
  19. package/client/plugos/syscalls/asset.ts +1 -3
  20. package/client/plugos/syscalls/code_widget.ts +1 -3
  21. package/client/plugos/syscalls/config.ts +10 -7
  22. package/client/plugos/syscalls/datastore.ts +1 -1
  23. package/client/plugos/syscalls/editor.ts +199 -60
  24. package/client/plugos/syscalls/event.ts +9 -12
  25. package/client/plugos/syscalls/fetch.ts +30 -22
  26. package/client/plugos/syscalls/index.ts +11 -2
  27. package/client/plugos/syscalls/jsonschema.ts +54 -32
  28. package/client/plugos/syscalls/language.ts +9 -5
  29. package/client/plugos/syscalls/markdown.ts +29 -7
  30. package/client/plugos/syscalls/mq.ts +3 -11
  31. package/client/plugos/syscalls/service_registry.ts +1 -4
  32. package/client/plugos/syscalls/shell.ts +2 -5
  33. package/client/plugos/syscalls/sync.ts +69 -60
  34. package/client/plugos/syscalls/system.ts +15 -3
  35. package/client/plugos/system.ts +18 -20
  36. package/client/plugos/types.ts +25 -0
  37. package/client/plugos/util.ts +20 -0
  38. package/client/plugos/worker_runtime.ts +4 -3
  39. package/client/space_lua/aggregates.ts +633 -60
  40. package/client/space_lua/ast.ts +21 -9
  41. package/client/space_lua/ast_narrow.ts +4 -2
  42. package/client/space_lua/eval.ts +842 -536
  43. package/client/space_lua/labels.ts +6 -11
  44. package/client/space_lua/numeric.ts +5 -8
  45. package/client/space_lua/parse.ts +290 -169
  46. package/client/space_lua/query_collection.ts +213 -149
  47. package/client/space_lua/render_lua_markdown.ts +369 -0
  48. package/client/space_lua/rp.ts +5 -4
  49. package/client/space_lua/runtime.ts +245 -142
  50. package/client/space_lua/sliq_null.ts +6 -0
  51. package/client/space_lua/stdlib/format.ts +34 -20
  52. package/client/space_lua/stdlib/js.ts +3 -7
  53. package/client/space_lua/stdlib/load.ts +1 -3
  54. package/client/space_lua/stdlib/math.ts +15 -14
  55. package/client/space_lua/stdlib/net.ts +25 -15
  56. package/client/space_lua/stdlib/os.ts +76 -85
  57. package/client/space_lua/stdlib/pattern.ts +28 -35
  58. package/client/space_lua/stdlib/prng.ts +15 -12
  59. package/client/space_lua/stdlib/space_lua.ts +16 -17
  60. package/client/space_lua/stdlib/string.ts +7 -17
  61. package/client/space_lua/stdlib/string_pack.ts +23 -19
  62. package/client/space_lua/stdlib/table.ts +5 -9
  63. package/client/space_lua/stdlib.ts +20 -30
  64. package/client/space_lua/tonumber.ts +79 -40
  65. package/client/space_lua/util.ts +14 -10
  66. package/dist/plug-compile.js +87 -42
  67. package/package.json +24 -22
  68. package/plug-api/constants.ts +20 -0
  69. package/plug-api/lib/async.ts +19 -6
  70. package/plug-api/lib/crypto.ts +5 -6
  71. package/plug-api/lib/dates.ts +15 -7
  72. package/plug-api/lib/json.ts +10 -4
  73. package/plug-api/lib/ref.ts +27 -20
  74. package/plug-api/lib/resolve.ts +7 -11
  75. package/plug-api/lib/shortcut.ts +16 -0
  76. package/plug-api/lib/tags.ts +13 -4
  77. package/plug-api/lib/transclusion.ts +6 -17
  78. package/plug-api/lib/tree.ts +146 -43
  79. package/plug-api/lib/yaml.ts +25 -15
  80. package/plug-api/syscalls/asset.ts +1 -1
  81. package/plug-api/syscalls/config.ts +38 -4
  82. package/plug-api/syscalls/editor.ts +169 -14
  83. package/plug-api/syscalls/index.ts +18 -0
  84. package/plug-api/syscalls/jsonschema.ts +1 -3
  85. package/plug-api/syscalls/lua.ts +3 -9
  86. package/plug-api/syscalls/mq.ts +1 -4
  87. package/plug-api/syscalls/shell.ts +4 -1
  88. package/plug-api/syscalls/space.ts +3 -10
  89. package/plug-api/syscalls/system.ts +16 -4
  90. package/plug-api/syscalls/yaml.ts +2 -6
  91. package/plug-api/types/client.ts +16 -1
  92. package/plug-api/types/event.ts +6 -4
  93. package/plug-api/types/manifest.ts +71 -9
  94. package/plugs/builtin_plugs.ts +3 -3
  95. package/client/plugos/sandboxes/web_worker_sandbox.ts +0 -17
  96. package/dist/worker_runtime_bundle.js +0 -233
@@ -1,5 +1,3 @@
1
- import { deepClone } from "@silverbulletmd/silverbullet/lib/json";
2
-
3
1
  export type ParseTree = {
4
2
  type?: string; // undefined === text node
5
3
  from?: number;
@@ -21,7 +19,7 @@ export function addParentPointers(tree: ParseTree) {
21
19
  }
22
20
 
23
21
  export function removeParentPointers(tree: ParseTree) {
24
- delete tree.parent;
22
+ tree.parent = undefined;
25
23
  if (!tree.children) {
26
24
  return;
27
25
  }
@@ -51,38 +49,53 @@ export function collectNodesOfType(
51
49
  return collectNodesMatching(tree, (n) => n.type === nodeType);
52
50
  }
53
51
 
54
- export function collectNodesMatching(
52
+ function collectNodesMatchingInternal(
55
53
  tree: ParseTree,
56
54
  matchFn: (tree: ParseTree) => boolean,
57
- ): ParseTree[] {
55
+ results: ParseTree[],
56
+ ): void {
58
57
  if (matchFn(tree)) {
59
- return [tree];
58
+ results.push(tree);
59
+ return;
60
60
  }
61
- let results: ParseTree[] = [];
62
61
  if (tree.children) {
63
62
  for (const child of tree.children) {
64
- results = [...results, ...collectNodesMatching(child, matchFn)];
63
+ collectNodesMatchingInternal(child, matchFn, results);
65
64
  }
66
65
  }
66
+ }
67
+
68
+ export function collectNodesMatching(
69
+ tree: ParseTree,
70
+ matchFn: (tree: ParseTree) => boolean,
71
+ ): ParseTree[] {
72
+ const results: ParseTree[] = [];
73
+ collectNodesMatchingInternal(tree, matchFn, results);
67
74
  return results;
68
75
  }
69
76
 
70
- export async function collectNodesMatchingAsync(
77
+ async function collectNodesMatchingAsyncInternal(
71
78
  tree: ParseTree,
72
79
  matchFn: (tree: ParseTree) => Promise<boolean>,
73
- ): Promise<ParseTree[]> {
80
+ results: ParseTree[],
81
+ ): Promise<void> {
74
82
  if (await matchFn(tree)) {
75
- return [tree];
83
+ results.push(tree);
84
+ return;
76
85
  }
77
- let results: ParseTree[] = [];
78
86
  if (tree.children) {
79
87
  for (const child of tree.children) {
80
- results = [
81
- ...results,
82
- ...await collectNodesMatchingAsync(child, matchFn),
83
- ];
88
+ await collectNodesMatchingAsyncInternal(child, matchFn, results);
84
89
  }
85
90
  }
91
+ }
92
+
93
+ export async function collectNodesMatchingAsync(
94
+ tree: ParseTree,
95
+ matchFn: (tree: ParseTree) => Promise<boolean>,
96
+ ): Promise<ParseTree[]> {
97
+ const results: ParseTree[] = [];
98
+ await collectNodesMatchingAsyncInternal(tree, matchFn, results);
86
99
  return results;
87
100
  }
88
101
 
@@ -92,19 +105,22 @@ export function replaceNodesMatching(
92
105
  substituteFn: (tree: ParseTree) => ParseTree | null | undefined,
93
106
  ) {
94
107
  if (tree?.children) {
95
- const children = tree.children.slice();
96
- for (const child of children) {
108
+ let i = 0;
109
+ while (i < tree.children.length) {
110
+ const child = tree.children[i];
97
111
  const subst = substituteFn(child);
98
112
  if (subst !== undefined) {
99
- const pos = tree.children.indexOf(child);
100
113
  if (subst) {
101
- tree.children.splice(pos, 1, subst);
114
+ tree.children[i] = subst;
115
+ i++;
102
116
  } else {
103
117
  // null = delete
104
- tree.children.splice(pos, 1);
118
+ tree.children.splice(i, 1);
119
+ // don't increment i — next child shifted into this position
105
120
  }
106
121
  } else {
107
122
  replaceNodesMatching(child, substituteFn);
123
+ i++;
108
124
  }
109
125
  }
110
126
  }
@@ -115,19 +131,21 @@ export async function replaceNodesMatchingAsync(
115
131
  substituteFn: (tree: ParseTree) => Promise<ParseTree | null | undefined>,
116
132
  ) {
117
133
  if (tree.children) {
118
- const children = tree.children.slice();
119
- for (const child of children) {
134
+ let i = 0;
135
+ while (i < tree.children.length) {
136
+ const child = tree.children[i];
120
137
  const subst = await substituteFn(child);
121
138
  if (subst !== undefined) {
122
- const pos = tree.children.indexOf(child);
123
139
  if (subst) {
124
- tree.children.splice(pos, 1, subst);
140
+ tree.children[i] = subst;
141
+ i++;
125
142
  } else {
126
143
  // null = delete
127
- tree.children.splice(pos, 1);
144
+ tree.children.splice(i, 1);
128
145
  }
129
146
  } else {
130
147
  await replaceNodesMatchingAsync(child, substituteFn);
148
+ i++;
131
149
  }
132
150
  }
133
151
  }
@@ -137,36 +155,119 @@ export function findNodeMatching(
137
155
  tree: ParseTree,
138
156
  matchFn: (tree: ParseTree) => boolean,
139
157
  ): ParseTree | null {
140
- return collectNodesMatching(tree, matchFn)[0];
158
+ if (matchFn(tree)) {
159
+ return tree;
160
+ }
161
+ if (tree.children) {
162
+ for (const child of tree.children) {
163
+ const result = findNodeMatching(child, matchFn);
164
+ if (result) {
165
+ return result;
166
+ }
167
+ }
168
+ }
169
+ return null;
141
170
  }
142
171
 
143
172
  export function findNodeOfType(
144
173
  tree: ParseTree,
145
174
  nodeType: string,
146
175
  ): ParseTree | null {
147
- return collectNodesMatching(tree, (n) => n.type === nodeType)[0];
176
+ if (tree.type === nodeType) {
177
+ return tree;
178
+ }
179
+ if (tree.children) {
180
+ for (const child of tree.children) {
181
+ const result = findNodeOfType(child, nodeType);
182
+ if (result) {
183
+ return result;
184
+ }
185
+ }
186
+ }
187
+ return null;
148
188
  }
149
189
 
150
190
  export function traverseTree(
151
191
  tree: ParseTree,
152
- // Return value = should stop traversal?
192
+ // Return value = should stop traversal into children?
153
193
  matchFn: (tree: ParseTree) => boolean,
194
+ // Just log errors on exceptions, but don't stop traversal
195
+ catchVisitorErrors = false,
154
196
  ): void {
155
- // Do a collect, but ignore the result
156
- collectNodesMatching(tree, matchFn);
197
+ let stop = false;
198
+ if (catchVisitorErrors) {
199
+ try {
200
+ stop = matchFn(tree);
201
+ } catch (e: any) {
202
+ console.error(
203
+ `traverseTree visitor failed at node ${tree.type}@${tree.from}:`,
204
+ e?.stack ?? e,
205
+ );
206
+ return;
207
+ }
208
+ } else {
209
+ stop = matchFn(tree);
210
+ }
211
+ if (stop) {
212
+ return;
213
+ }
214
+ if (tree.children) {
215
+ for (const child of tree.children) {
216
+ traverseTree(child, matchFn, catchVisitorErrors);
217
+ }
218
+ }
157
219
  }
158
220
 
159
221
  export async function traverseTreeAsync(
160
222
  tree: ParseTree,
161
- // Return value = should stop traversal?
223
+ // Return value = should stop traversal into children?
162
224
  matchFn: (tree: ParseTree) => Promise<boolean>,
225
+ catchVisitorErrors = false,
163
226
  ): Promise<void> {
164
- // Do a collect, but ignore the result
165
- await collectNodesMatchingAsync(tree, matchFn);
227
+ let stop = false;
228
+ if (catchVisitorErrors) {
229
+ try {
230
+ stop = await matchFn(tree);
231
+ } catch (e: any) {
232
+ console.error(
233
+ `traverseTreeAsync visitor failed at node ${tree.type}@${tree.from}:`,
234
+ e?.stack ?? e,
235
+ );
236
+ return;
237
+ }
238
+ } else {
239
+ stop = await matchFn(tree);
240
+ }
241
+ if (stop) {
242
+ return;
243
+ }
244
+ if (tree.children) {
245
+ for (const child of tree.children) {
246
+ await traverseTreeAsync(child, matchFn, catchVisitorErrors);
247
+ }
248
+ }
166
249
  }
167
250
 
168
251
  export function cloneTree(tree: ParseTree): ParseTree {
169
- return deepClone(tree, ["parent"]);
252
+ if (tree.text !== undefined) {
253
+ return {
254
+ from: tree.from,
255
+ to: tree.to,
256
+ text: tree.text,
257
+ };
258
+ }
259
+ const clone: ParseTree = {
260
+ type: tree.type,
261
+ from: tree.from,
262
+ to: tree.to,
263
+ };
264
+ if (tree.children) {
265
+ clone.children = new Array(tree.children.length);
266
+ for (let i = 0; i < tree.children.length; i++) {
267
+ clone.children[i] = cloneTree(tree.children[i]);
268
+ }
269
+ }
270
+ return clone;
170
271
  }
171
272
 
172
273
  // Finds non-text node at position
@@ -243,21 +344,23 @@ export function renderToText(tree?: ParseTree): string {
243
344
  if (!tree) {
244
345
  return "";
245
346
  }
246
- const pieces: string[] = [];
247
347
  if (tree.text !== undefined) {
248
348
  return tree.text;
249
349
  }
250
- for (const child of tree.children!) {
251
- pieces.push(renderToText(child));
350
+ const children = tree.children!;
351
+ if (children.length === 1) {
352
+ return renderToText(children[0]);
353
+ }
354
+ let result = "";
355
+ for (const child of children) {
356
+ result += renderToText(child);
252
357
  }
253
- return pieces.join("");
358
+ return result;
254
359
  }
255
360
 
256
361
  export function cleanTree(tree: ParseTree, omitTrimmable = true): ParseTree {
257
362
  if (tree.type === "⚠") {
258
- throw new Error(
259
- `Parse error at pos ${tree.from}`,
260
- );
363
+ throw new Error(`Parse error at pos ${tree.from}`);
261
364
  }
262
365
  if (tree.text !== undefined) {
263
366
  return tree;
@@ -272,7 +375,7 @@ export function cleanTree(tree: ParseTree, omitTrimmable = true): ParseTree {
272
375
  if (node.type && node.type !== "Comment") {
273
376
  ast.children!.push(cleanTree(node, omitTrimmable));
274
377
  }
275
- if (node.text && (omitTrimmable && node.text.trim() || !omitTrimmable)) {
378
+ if (node.text && ((omitTrimmable && node.text.trim()) || !omitTrimmable)) {
276
379
  ast.children!.push(node);
277
380
  }
278
381
  }
@@ -25,7 +25,9 @@ function serializeToYamlScalar(
25
25
  // Simple strings without special chars/meaning don't need quotes
26
26
  return value;
27
27
  } else if (
28
- typeof value === "number" || typeof value === "boolean" || value === null
28
+ typeof value === "number" ||
29
+ typeof value === "boolean" ||
30
+ value === null
29
31
  ) {
30
32
  return String(value);
31
33
  }
@@ -46,10 +48,15 @@ function serializeToYamlValue(
46
48
  // Determine indentation for list items (base + 2 spaces)
47
49
  const itemIndentation = `${baseIndentation} `;
48
50
  // Format each item recursively, preceded by '- ' marker
49
- return "\n" +
50
- value.map((item) =>
51
- `${itemIndentation}- ${serializeToYamlValue(item, itemIndentation)}`
52
- ).join("\n");
51
+ return (
52
+ "\n" +
53
+ value
54
+ .map(
55
+ (item) =>
56
+ `${itemIndentation}- ${serializeToYamlValue(item, itemIndentation)}`,
57
+ )
58
+ .join("\n")
59
+ );
53
60
  // Note: serializeToYamlValue is used recursively here to handle nested arrays/objects if needed in future
54
61
  // However, the current `applyMinimalSetKeyPatches` only handles top-level keys.
55
62
  } else if (typeof value === "object" && value !== null) {
@@ -58,22 +65,25 @@ function serializeToYamlValue(
58
65
  const itemIndentation = `${baseIndentation} `;
59
66
  const entries = Object.entries(value);
60
67
  if (entries.length === 0) return "{}"; // Flow style empty objects
61
- return "\n" +
62
- entries.map(([key, val]) =>
63
- `${itemIndentation}${key}: ${
64
- serializeToYamlValue(val, itemIndentation)
65
- }`
66
- ).join("\n");
68
+ return (
69
+ "\n" +
70
+ entries
71
+ .map(
72
+ ([key, val]) =>
73
+ `${itemIndentation}${key}: ${serializeToYamlValue(
74
+ val,
75
+ itemIndentation,
76
+ )}`,
77
+ )
78
+ .join("\n")
79
+ );
67
80
  } else {
68
81
  // Handle scalars using the dedicated function
69
82
  return serializeToYamlScalar(value);
70
83
  }
71
84
  }
72
85
 
73
- export function applyPatches(
74
- yamlString: string,
75
- patches: YamlPatch[],
76
- ): string {
86
+ export function applyPatches(yamlString: string, patches: YamlPatch[]): string {
77
87
  let currentYaml = yamlString;
78
88
 
79
89
  for (const patch of patches) {
@@ -15,7 +15,7 @@ export async function readAsset(
15
15
  name: string,
16
16
  encoding: "utf8" | "dataurl" = "utf8",
17
17
  ): Promise<string> {
18
- const dataUrl = await syscall("asset.readAsset", plugName, name) as string;
18
+ const dataUrl = (await syscall("asset.readAsset", plugName, name)) as string;
19
19
  switch (encoding) {
20
20
  case "utf8":
21
21
  return new TextDecoder().decode(base64DecodeDataUrl(dataUrl));
@@ -31,10 +31,7 @@ export function set<T>(
31
31
  /**
32
32
  * Inserts a config value into an array
33
33
  */
34
- export function insert<T>(
35
- path: string | string[],
36
- value: T,
37
- ): Promise<void> {
34
+ export function insert<T>(path: string | string[], value: T): Promise<void> {
38
35
  return syscall("config.insert", path, value);
39
36
  }
40
37
 
@@ -56,3 +53,40 @@ export function has(path: string): Promise<boolean> {
56
53
  export function define(key: string, schema: any): Promise<void> {
57
54
  return syscall("config.define", key, schema);
58
55
  }
56
+
57
+ /**
58
+ * Defines (or updates) a UI category for the configuration manager. Categories
59
+ * are referenced by name from a schema field's `ui.category` and are sorted in
60
+ * ascending `order` (default 0); unknown categories fall to the bottom in
61
+ * alphabetical order.
62
+ */
63
+ export function defineCategory(
64
+ definition: { name: string; description?: string; order?: number },
65
+ ): Promise<void> {
66
+ return syscall("config.defineCategory", definition);
67
+ }
68
+
69
+ /**
70
+ * Gets all config values as a single object.
71
+ * @returns The entire config values object
72
+ */
73
+ export function getValues(): Promise<Record<string, any>> {
74
+ return syscall("config.getValues");
75
+ }
76
+
77
+ /**
78
+ * Gets all defined config schemas.
79
+ * @returns The schema definitions object
80
+ */
81
+ export function getSchemas(): Promise<Record<string, any>> {
82
+ return syscall("config.getSchemas");
83
+ }
84
+
85
+ /**
86
+ * Gets all registered UI categories for the configuration manager.
87
+ */
88
+ export function getCategories(): Promise<
89
+ Record<string, { name: string; description?: string; order?: number }>
90
+ > {
91
+ return syscall("config.getCategories");
92
+ }
@@ -1,5 +1,9 @@
1
1
  import { syscall } from "../syscall.ts";
2
- import type { FilterOption, UploadFile } from "../../plug-api/types/client.ts";
2
+ import type {
3
+ FilterOption,
4
+ NotificationType,
5
+ UploadFile,
6
+ } from "../../plug-api/types/client.ts";
3
7
  import type { Path, Ref } from "../lib/ref.ts";
4
8
  import type { PageMeta } from "../../plug-api/types/index.ts";
5
9
 
@@ -77,9 +81,11 @@ export function getCursor(): Promise<number> {
77
81
  /**
78
82
  * Returns the line number and column number of the cursor in the editor
79
83
  */
80
- export function getSelection(): Promise<
81
- { from: number; to: number; text: string }
82
- > {
84
+ export function getSelection(): Promise<{
85
+ from: number;
86
+ to: number;
87
+ text: string;
88
+ }> {
83
89
  return syscall("editor.getSelection");
84
90
  }
85
91
 
@@ -218,7 +224,7 @@ export function uploadFile(
218
224
  */
219
225
  export function flashNotification(
220
226
  message: string,
221
- type: "info" | "error" = "info",
227
+ type: NotificationType = "info",
222
228
  ): Promise<void> {
223
229
  return syscall("editor.flashNotification", message, type);
224
230
  }
@@ -259,12 +265,17 @@ export function showPanel(
259
265
  * Hides a panel in the editor
260
266
  * @param id the location of the panel to hide
261
267
  */
262
- export function hidePanel(
263
- id: "lhs" | "rhs" | "bhs" | "modal",
264
- ): Promise<void> {
268
+ export function hidePanel(id: "lhs" | "rhs" | "bhs" | "modal"): Promise<void> {
265
269
  return syscall("editor.hidePanel", id);
266
270
  }
267
271
 
272
+ /**
273
+ * Returns focus to the main editor.
274
+ */
275
+ export function focus(): Promise<void> {
276
+ return syscall("editor.focus");
277
+ }
278
+
268
279
  export function showProgress(
269
280
  progressPercentage?: number,
270
281
  progressType?: string,
@@ -360,9 +371,7 @@ export function prompt(
360
371
  * @param message the message to show in the confirmation dialog
361
372
  * @returns
362
373
  */
363
- export function confirm(
364
- message: string,
365
- ): Promise<boolean> {
374
+ export function confirm(message: string): Promise<boolean> {
366
375
  return syscall("editor.confirm", message);
367
376
  }
368
377
 
@@ -371,9 +380,7 @@ export function confirm(
371
380
  * @param message the message to show in the confirmation dialog
372
381
  * @returns
373
382
  */
374
- export function alert(
375
- message: string,
376
- ): Promise<boolean> {
383
+ export function alert(message: string): Promise<boolean> {
377
384
  return syscall("editor.alert", message);
378
385
  }
379
386
 
@@ -466,6 +473,27 @@ export function deleteLine(): Promise<void> {
466
473
  return syscall("editor.deleteLine");
467
474
  }
468
475
 
476
+ /**
477
+ * Select the entire document
478
+ */
479
+ export function selectAll(): Promise<void> {
480
+ return syscall("editor.selectAll");
481
+ }
482
+
483
+ /**
484
+ * Increase indentation of the current line or selection
485
+ */
486
+ export function indentMore(): Promise<void> {
487
+ return syscall("editor.indentMore");
488
+ }
489
+
490
+ /**
491
+ * Decrease indentation of the current line or selection
492
+ */
493
+ export function indentLess(): Promise<void> {
494
+ return syscall("editor.indentLess");
495
+ }
496
+
469
497
  /**
470
498
  * Comment or uncomment the current line in the editor
471
499
  */
@@ -481,6 +509,133 @@ export function moveLineDown(): Promise<void> {
481
509
  return syscall("editor.moveLineDown");
482
510
  }
483
511
 
512
+ // Cursor motion syscalls (wrappers around @codemirror/commands functions)
513
+ export function cursorCharLeft(): Promise<void> {
514
+ return syscall("editor.cursorCharLeft");
515
+ }
516
+ export function cursorCharRight(): Promise<void> {
517
+ return syscall("editor.cursorCharRight");
518
+ }
519
+ export function cursorGroupLeft(): Promise<void> {
520
+ return syscall("editor.cursorGroupLeft");
521
+ }
522
+ export function cursorGroupRight(): Promise<void> {
523
+ return syscall("editor.cursorGroupRight");
524
+ }
525
+ export function cursorLineBoundaryLeft(): Promise<void> {
526
+ return syscall("editor.cursorLineBoundaryLeft");
527
+ }
528
+ export function cursorLineBoundaryRight(): Promise<void> {
529
+ return syscall("editor.cursorLineBoundaryRight");
530
+ }
531
+ export function cursorLineStart(): Promise<void> {
532
+ return syscall("editor.cursorLineStart");
533
+ }
534
+ export function cursorLineEnd(): Promise<void> {
535
+ return syscall("editor.cursorLineEnd");
536
+ }
537
+ export function cursorDocStart(): Promise<void> {
538
+ return syscall("editor.cursorDocStart");
539
+ }
540
+ export function cursorDocEnd(): Promise<void> {
541
+ return syscall("editor.cursorDocEnd");
542
+ }
543
+ export function cursorLineUp(): Promise<void> {
544
+ return syscall("editor.cursorLineUp");
545
+ }
546
+ export function cursorLineDown(): Promise<void> {
547
+ return syscall("editor.cursorLineDown");
548
+ }
549
+ export function cursorPageUp(): Promise<void> {
550
+ return syscall("editor.cursorPageUp");
551
+ }
552
+ export function cursorPageDown(): Promise<void> {
553
+ return syscall("editor.cursorPageDown");
554
+ }
555
+
556
+ // Selection-extending motions
557
+ export function selectCharLeft(): Promise<void> {
558
+ return syscall("editor.selectCharLeft");
559
+ }
560
+ export function selectCharRight(): Promise<void> {
561
+ return syscall("editor.selectCharRight");
562
+ }
563
+ export function selectGroupLeft(): Promise<void> {
564
+ return syscall("editor.selectGroupLeft");
565
+ }
566
+ export function selectGroupRight(): Promise<void> {
567
+ return syscall("editor.selectGroupRight");
568
+ }
569
+ export function selectLineBoundaryLeft(): Promise<void> {
570
+ return syscall("editor.selectLineBoundaryLeft");
571
+ }
572
+ export function selectLineBoundaryRight(): Promise<void> {
573
+ return syscall("editor.selectLineBoundaryRight");
574
+ }
575
+ export function selectLineStart(): Promise<void> {
576
+ return syscall("editor.selectLineStart");
577
+ }
578
+ export function selectLineEnd(): Promise<void> {
579
+ return syscall("editor.selectLineEnd");
580
+ }
581
+ export function selectDocStart(): Promise<void> {
582
+ return syscall("editor.selectDocStart");
583
+ }
584
+ export function selectDocEnd(): Promise<void> {
585
+ return syscall("editor.selectDocEnd");
586
+ }
587
+ export function selectLineUp(): Promise<void> {
588
+ return syscall("editor.selectLineUp");
589
+ }
590
+ export function selectLineDown(): Promise<void> {
591
+ return syscall("editor.selectLineDown");
592
+ }
593
+ export function selectPageUp(): Promise<void> {
594
+ return syscall("editor.selectPageUp");
595
+ }
596
+ export function selectPageDown(): Promise<void> {
597
+ return syscall("editor.selectPageDown");
598
+ }
599
+
600
+ // Delete
601
+ export function deleteCharBackward(): Promise<void> {
602
+ return syscall("editor.deleteCharBackward");
603
+ }
604
+ export function deleteCharForward(): Promise<void> {
605
+ return syscall("editor.deleteCharForward");
606
+ }
607
+ export function deleteGroupBackward(): Promise<void> {
608
+ return syscall("editor.deleteGroupBackward");
609
+ }
610
+ export function deleteGroupForward(): Promise<void> {
611
+ return syscall("editor.deleteGroupForward");
612
+ }
613
+ export function deleteLineBoundaryBackward(): Promise<void> {
614
+ return syscall("editor.deleteLineBoundaryBackward");
615
+ }
616
+ export function deleteLineBoundaryForward(): Promise<void> {
617
+ return syscall("editor.deleteLineBoundaryForward");
618
+ }
619
+ export function transposeChars(): Promise<void> {
620
+ return syscall("editor.transposeChars");
621
+ }
622
+
623
+ // Enter: accepts completion if popup open, else inserts newline
624
+ export function insertNewline(): Promise<void> {
625
+ return syscall("editor.insertNewline");
626
+ }
627
+
628
+ // Completion popup
629
+ export function acceptCompletion(): Promise<boolean> {
630
+ return syscall("editor.acceptCompletion");
631
+ }
632
+ export function startCompletion(): Promise<void> {
633
+ return syscall("editor.startCompletion");
634
+ }
635
+ export function closeCompletion(): Promise<void> {
636
+ return syscall("editor.closeCompletion");
637
+ }
638
+
484
639
  // Vim-mode specific syscalls
485
640
 
486
641
  /**
@@ -75,3 +75,21 @@ export function deleteObject(
75
75
  ): Promise<void> {
76
76
  return syscall("index.deleteObject", page, tag, ref);
77
77
  }
78
+
79
+ export type {
80
+ AnchorHit,
81
+ ResolveAnchorResult,
82
+ } from "../../plugs/index/types.ts";
83
+ import type { ResolveAnchorResult } from "../../plugs/index/types.ts";
84
+
85
+ /**
86
+ * Resolves a `$name` anchor to its host. Returns `ok: true` with page +
87
+ * hostTag + range on success, or `ok: false` with `missing`/`duplicate`
88
+ * reason. When `page` is provided, the lookup is filtered to that page.
89
+ */
90
+ export function resolveAnchor(
91
+ name: string,
92
+ page?: string,
93
+ ): Promise<ResolveAnchorResult> {
94
+ return syscall("index.resolveAnchor", name, page);
95
+ }