@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
@@ -7,21 +7,67 @@ import {
7
7
  unfoldCode,
8
8
  } from "@codemirror/language";
9
9
  import {
10
+ cursorCharLeft,
11
+ cursorCharRight,
12
+ cursorDocEnd,
13
+ cursorDocStart,
14
+ cursorGroupLeft,
15
+ cursorGroupRight,
16
+ cursorLineBoundaryLeft,
17
+ cursorLineBoundaryRight,
18
+ cursorLineDown,
19
+ cursorLineEnd,
20
+ cursorLineStart,
21
+ cursorLineUp,
22
+ cursorPageDown,
23
+ cursorPageUp,
24
+ deleteCharBackward,
25
+ deleteCharForward,
26
+ deleteGroupBackward,
27
+ deleteGroupForward,
10
28
  deleteLine,
29
+ deleteLineBoundaryBackward,
30
+ deleteLineBoundaryForward,
31
+ indentLess,
32
+ indentMore,
11
33
  insertNewline,
12
34
  insertNewlineAndIndent,
13
35
  moveLineDown,
14
36
  moveLineUp,
15
37
  redo,
38
+ selectAll,
39
+ selectCharLeft,
40
+ selectCharRight,
41
+ selectDocEnd,
42
+ selectDocStart,
43
+ selectGroupLeft,
44
+ selectGroupRight,
45
+ selectLineBoundaryLeft,
46
+ selectLineBoundaryRight,
47
+ selectLineDown,
48
+ selectLineEnd,
49
+ selectLineStart,
50
+ selectLineUp,
51
+ selectPageDown,
52
+ selectPageUp,
16
53
  toggleComment,
54
+ transposeChars,
17
55
  undo,
18
56
  } from "@codemirror/commands";
57
+ import {
58
+ acceptCompletion,
59
+ closeCompletion,
60
+ moveCompletionSelection,
61
+ startCompletion,
62
+ } from "@codemirror/autocomplete";
19
63
  import type { Transaction } from "@codemirror/state";
20
64
  import { EditorView } from "@codemirror/view";
21
- import { getCM as vimGetCm, Vim } from "@replit/codemirror-vim";
65
+ import { getVimModule } from "../../vim_loader.ts";
22
66
  import type { SysCallMapping } from "../system.ts";
23
67
  import type {
24
68
  FilterOption,
69
+ NotificationAction,
70
+ NotificationType,
25
71
  UploadFile,
26
72
  } from "@silverbulletmd/silverbullet/type/client";
27
73
  import { openSearchPanel } from "@codemirror/search";
@@ -48,11 +94,11 @@ export function editorSyscalls(client: Client): SysCallMapping {
48
94
  return client.currentPath();
49
95
  },
50
96
  "editor.getCurrentEditor": (): string => {
51
- return client.documentEditor?.name || "page";
97
+ return client.contentManager.documentEditor?.name || "page";
52
98
  },
53
99
  "editor.getRecentlyOpenedPages": (): PageMeta[] => {
54
- return client.ui.viewState.allPages.sort((a, b) =>
55
- (b.lastOpened || 0) - (a.lastOpened || 0)
100
+ return client.ui.viewState.allPages.sort(
101
+ (a, b) => (b.lastOpened || 0) - (a.lastOpened || 0),
56
102
  );
57
103
  },
58
104
  "editor.getText": () => {
@@ -68,12 +114,14 @@ export function editorSyscalls(client: Client): SysCallMapping {
68
114
  const line = client.editorView.state.doc.lineAt(pos);
69
115
  return {
70
116
  ...line,
71
- textWithCursor: line.text.slice(0, pos - line.from) + "|^|" +
117
+ textWithCursor:
118
+ line.text.slice(0, pos - line.from) +
119
+ "|^|" +
72
120
  line.text.slice(pos - line.from),
73
121
  };
74
122
  },
75
123
  "editor.setText": (_ctx, newText: string, shouldIsolateHistory = false) => {
76
- client.setEditorText(newText, shouldIsolateHistory);
124
+ client.contentManager.setEditorText(newText, shouldIsolateHistory);
77
125
  },
78
126
  "editor.getCursor": (): number => {
79
127
  return client.editorView.state.selection.main.from;
@@ -127,7 +175,7 @@ export function editorSyscalls(client: Client): SysCallMapping {
127
175
 
128
176
  legacyRef.kind ??= "page";
129
177
 
130
- let details: Ref["details"] ;
178
+ let details: Ref["details"];
131
179
 
132
180
  if (typeof legacyRef.pos === "number") {
133
181
  details = {
@@ -160,13 +208,9 @@ export function editorSyscalls(client: Client): SysCallMapping {
160
208
  // some validation library. Didn't want to use jsonschemas here tho.
161
209
  // Ideally this would be moved into a function too
162
210
  if (!isValidPath(ref.path) && ref.path !== "") {
163
- throw new Error(
164
- "Path passed in ref to `editor.navigate` is invalid",
165
- );
211
+ throw new Error("Path passed in ref to `editor.navigate` is invalid");
166
212
  } else if (typeof ref.meta !== "boolean" && ref.meta !== undefined) {
167
- throw new Error(
168
- "ref.meta has to be of type `boolean`",
169
- );
213
+ throw new Error("ref.meta has to be of type `boolean`");
170
214
  } else if (ref.details !== undefined && typeof ref.details !== "object") {
171
215
  throw new Error(
172
216
  "ref.details has to be of type `object` or `undefined`",
@@ -181,17 +225,15 @@ export function editorSyscalls(client: Client): SysCallMapping {
181
225
  }
182
226
 
183
227
  if (
184
- ref.details?.type === "position" && typeof ref.details.pos !== "number"
228
+ ref.details?.type === "position" &&
229
+ typeof ref.details.pos !== "number"
185
230
  ) {
186
- throw new Error(
187
- "ref.details.pos has to be of type `number`",
188
- );
231
+ throw new Error("ref.details.pos has to be of type `number`");
189
232
  } else if (
190
- ref.details?.type === "header" && typeof ref.details.header !== "string"
233
+ ref.details?.type === "header" &&
234
+ typeof ref.details.header !== "string"
191
235
  ) {
192
- throw new Error(
193
- "ref.details.header has to be of type `string`",
194
- );
236
+ throw new Error("ref.details.header has to be of type `string`");
195
237
  } else if (
196
238
  ref.details?.type === "linecolumn" &&
197
239
  typeof ref.details.line !== "number" &&
@@ -214,14 +256,12 @@ export function editorSyscalls(client: Client): SysCallMapping {
214
256
  client.rebuildEditorState();
215
257
  },
216
258
  "editor.reloadConfigAndCommands": async () => {
217
- await client.clientSystem.system.localSyscall(
218
- "system.loadScripts",
219
- [],
220
- );
259
+ await client.clientSystem.system.localSyscall("system.loadScripts", []);
221
260
  await client.clientSystem.system.localSyscall(
222
261
  "system.loadSpaceStyles",
223
262
  [],
224
263
  );
264
+ client.reconfigureLanguage();
225
265
  },
226
266
  "editor.invokeCommand": (_ctx, name: string, args?: string[]) => {
227
267
  return client.runCommandByName(name, args);
@@ -288,15 +328,19 @@ export function editorSyscalls(client: Client): SysCallMapping {
288
328
  reject(e);
289
329
  };
290
330
 
331
+ input.style.display = "none";
332
+ document.body.appendChild(input);
291
333
  input.click();
334
+ setTimeout(() => document.body.removeChild(input), 1000);
292
335
  });
293
336
  },
294
337
  "editor.flashNotification": (
295
338
  _ctx,
296
339
  message: string,
297
- type: "error" | "info" = "info",
340
+ type: NotificationType = "info",
341
+ options?: { timeout?: number; actions?: NotificationAction[] },
298
342
  ) => {
299
- client.flashNotification(message, type);
343
+ client.ui.flashNotification(message, type, options);
300
344
  },
301
345
  "editor.filterBox": (
302
346
  _ctx,
@@ -305,7 +349,7 @@ export function editorSyscalls(client: Client): SysCallMapping {
305
349
  helpText = "",
306
350
  placeHolder = "",
307
351
  ): Promise<FilterOption | undefined> => {
308
- return client.filterBox(label, options, helpText, placeHolder);
352
+ return client.ui.filterBox(label, options, helpText, placeHolder);
309
353
  },
310
354
  "editor.showPanel": (
311
355
  _ctx,
@@ -324,6 +368,9 @@ export function editorSyscalls(client: Client): SysCallMapping {
324
368
  client.editorView.dispatch({});
325
369
  });
326
370
  },
371
+ "editor.focus": () => {
372
+ client.focus();
373
+ },
327
374
  "editor.hidePanel": (_ctx, id: string) => {
328
375
  client.ui.viewDispatch({
329
376
  type: "hide-panel",
@@ -339,7 +386,7 @@ export function editorSyscalls(client: Client): SysCallMapping {
339
386
  progressPercentage?: number,
340
387
  progressType?: "sync" | "index",
341
388
  ) => {
342
- client.showProgress(progressPercentage, progressType);
389
+ client.ui.showProgress(progressPercentage, progressType);
343
390
  },
344
391
  "editor.insertAtPos": (
345
392
  _ctx,
@@ -351,7 +398,8 @@ export function editorSyscalls(client: Client): SysCallMapping {
351
398
  if (cursorPlaceHolder) {
352
399
  cursorPlaceholderPos = text.indexOf("|^|");
353
400
  if (cursorPlaceholderPos !== -1) {
354
- text = text.slice(0, cursorPlaceholderPos) +
401
+ text =
402
+ text.slice(0, cursorPlaceholderPos) +
355
403
  text.slice(cursorPlaceholderPos + 3);
356
404
  } else {
357
405
  cursorPlaceHolder = false;
@@ -369,9 +417,7 @@ export function editorSyscalls(client: Client): SysCallMapping {
369
417
  selection: {
370
418
  anchor: cursorPos,
371
419
  },
372
- effects: [
373
- EditorView.scrollIntoView(cursorPos),
374
- ],
420
+ effects: [EditorView.scrollIntoView(cursorPos)],
375
421
  });
376
422
  }
377
423
  },
@@ -385,7 +431,8 @@ export function editorSyscalls(client: Client): SysCallMapping {
385
431
  let cursorPlaceholderPos = -1;
386
432
  if (cursorPlaceHolder) {
387
433
  cursorPlaceholderPos = text.indexOf("|^|");
388
- text = text.slice(0, cursorPlaceholderPos) +
434
+ text =
435
+ text.slice(0, cursorPlaceholderPos) +
389
436
  text.slice(cursorPlaceholderPos + 3);
390
437
  }
391
438
  client.editorView.dispatch({
@@ -401,9 +448,7 @@ export function editorSyscalls(client: Client): SysCallMapping {
401
448
  selection: {
402
449
  anchor: cursorPos,
403
450
  },
404
- effects: [
405
- EditorView.scrollIntoView(cursorPos),
406
- ],
451
+ effects: [EditorView.scrollIntoView(cursorPos)],
407
452
  });
408
453
  }
409
454
  },
@@ -416,12 +461,9 @@ export function editorSyscalls(client: Client): SysCallMapping {
416
461
  if (center) {
417
462
  client.editorView.dispatch({
418
463
  effects: [
419
- EditorView.scrollIntoView(
420
- pos,
421
- {
422
- y: "center",
423
- },
424
- ),
464
+ EditorView.scrollIntoView(pos, {
465
+ y: "center",
466
+ }),
425
467
  ],
426
468
  });
427
469
  }
@@ -459,7 +501,8 @@ export function editorSyscalls(client: Client): SysCallMapping {
459
501
  const from = editorView.state.selection.main.from;
460
502
  const cursorPlaceholderPos = text.indexOf("|^|");
461
503
  if (cursorPlaceHolder && cursorPlaceholderPos !== -1) {
462
- text = text.slice(0, cursorPlaceholderPos) +
504
+ text =
505
+ text.slice(0, cursorPlaceholderPos) +
463
506
  text.slice(cursorPlaceholderPos + 3);
464
507
  } else {
465
508
  cursorPlaceHolder = false;
@@ -485,10 +528,10 @@ export function editorSyscalls(client: Client): SysCallMapping {
485
528
  message: string,
486
529
  defaultValue = "",
487
530
  ): Promise<string | undefined> => {
488
- return client.prompt(message, defaultValue);
531
+ return client.ui.prompt(message, defaultValue);
489
532
  },
490
533
  "editor.confirm": (_ctx, message: string): Promise<boolean> => {
491
- return client.confirm(message);
534
+ return client.ui.confirm(message);
492
535
  },
493
536
  "editor.alert": (_ctx, message: string) => {
494
537
  alert(message);
@@ -502,17 +545,26 @@ export function editorSyscalls(client: Client): SysCallMapping {
502
545
  key,
503
546
  value,
504
547
  });
505
- client.reloadEditor();
548
+ void client.reloadEditor();
506
549
  },
507
550
  "editor.vimEx": (_ctx, exCommand: string) => {
508
- const cm = vimGetCm(client.editorView);
551
+ const vimMod = getVimModule();
552
+ if (!vimMod) {
553
+ throw new Error("Vim module not loaded.");
554
+ }
555
+ const cm = vimMod.getCM(client.editorView);
509
556
  if (cm?.state.vim) {
510
- return Vim.handleEx(cm as any, exCommand);
557
+ return vimMod.Vim.handleEx(cm as any, exCommand);
511
558
  } else {
512
559
  throw new Error("Vim mode not active or not initialized.");
513
560
  }
514
561
  },
515
562
  "editor.configureVimMode": () => {
563
+ const vimMod = getVimModule();
564
+ if (!vimMod) {
565
+ throw new Error("Vim module not loaded.");
566
+ }
567
+ const { Vim } = vimMod;
516
568
  // Override the default "o" binding to be more intelligent and follow the markdown editor's behavior
517
569
  Vim.mapCommand("o", "action", "newline-continue-markup", {}, {});
518
570
  Vim.mapCommand("O", "action", "back-newline-continue-markup", {}, {});
@@ -566,20 +618,18 @@ export function editorSyscalls(client: Client): SysCallMapping {
566
618
  }
567
619
  });
568
620
  config.map?.forEach(({ map, to, mode }) => {
569
- console.log(
570
- `Mapping ${map} to ${to} for ${mode ?? "normal"}`,
571
- );
621
+ console.log(`Mapping ${map} to ${to} for ${mode ?? "normal"}`);
572
622
  Vim.map(map, to, mode ?? "normal");
573
623
  });
574
624
  config.noremap?.forEach(({ map, to, mode }) => {
575
- console.log(
576
- `Noremapping ${map} to ${to} for ${mode ?? "normal"}`,
577
- );
625
+ console.log(`Noremapping ${map} to ${to} for ${mode ?? "normal"}`);
578
626
  Vim.noremap(map, to, mode ?? "normal");
579
627
  });
580
628
  config.commands?.forEach(({ ex, command }) => {
581
629
  console.log(`Mapping command '${command}' to Ex ${ex}`);
582
- Vim.defineEx(ex, "", () => client.runCommandByName(command));
630
+ Vim.defineEx(ex, "", (_, params) =>
631
+ client.runCommandByName(command, params.args),
632
+ );
583
633
  });
584
634
  } else {
585
635
  console.log("No vim config found");
@@ -592,11 +642,29 @@ export function editorSyscalls(client: Client): SysCallMapping {
592
642
  client.startPageNavigate(mode);
593
643
  },
594
644
  "editor.openCommandPalette": () => {
595
- client.startCommandPalette();
645
+ void client.startCommandPalette();
596
646
  },
597
647
  "editor.deleteLine": () => {
598
648
  deleteLine(client.editorView);
599
649
  },
650
+ "editor.selectAll": () => {
651
+ return selectAll({
652
+ state: client.editorView.state,
653
+ dispatch: client.editorView.dispatch,
654
+ });
655
+ },
656
+ "editor.indentMore": () => {
657
+ return indentMore({
658
+ state: client.editorView.state,
659
+ dispatch: client.editorView.dispatch,
660
+ });
661
+ },
662
+ "editor.indentLess": () => {
663
+ return indentLess({
664
+ state: client.editorView.state,
665
+ dispatch: client.editorView.dispatch,
666
+ });
667
+ },
600
668
  "editor.toggleComment": () => {
601
669
  return toggleComment({
602
670
  state: client.editorView.state,
@@ -637,6 +705,77 @@ export function editorSyscalls(client: Client): SysCallMapping {
637
705
  "editor.redo": () => {
638
706
  return redo(client.editorView);
639
707
  },
708
+ // Cursor motion syscalls
709
+ "editor.cursorCharLeft": () => cursorCharLeft(client.editorView),
710
+ "editor.cursorCharRight": () => cursorCharRight(client.editorView),
711
+ "editor.cursorGroupLeft": () => cursorGroupLeft(client.editorView),
712
+ "editor.cursorGroupRight": () => cursorGroupRight(client.editorView),
713
+ "editor.cursorLineBoundaryLeft": () =>
714
+ cursorLineBoundaryLeft(client.editorView),
715
+ "editor.cursorLineBoundaryRight": () =>
716
+ cursorLineBoundaryRight(client.editorView),
717
+ "editor.cursorLineStart": () => cursorLineStart(client.editorView),
718
+ "editor.cursorLineEnd": () => cursorLineEnd(client.editorView),
719
+ "editor.cursorDocStart": () => cursorDocStart(client.editorView),
720
+ "editor.cursorDocEnd": () => cursorDocEnd(client.editorView),
721
+ // Cursor motions that also navigate the completion popup if it's open
722
+ "editor.cursorLineUp": () => {
723
+ const view = client.editorView;
724
+ if (moveCompletionSelection(false)(view)) return true;
725
+ return cursorLineUp(view);
726
+ },
727
+ "editor.cursorLineDown": () => {
728
+ const view = client.editorView;
729
+ if (moveCompletionSelection(true)(view)) return true;
730
+ return cursorLineDown(view);
731
+ },
732
+ "editor.cursorPageUp": () => {
733
+ const view = client.editorView;
734
+ if (moveCompletionSelection(false, "page")(view)) return true;
735
+ return cursorPageUp(view);
736
+ },
737
+ "editor.cursorPageDown": () => {
738
+ const view = client.editorView;
739
+ if (moveCompletionSelection(true, "page")(view)) return true;
740
+ return cursorPageDown(view);
741
+ },
742
+ // Selection-extending motions
743
+ "editor.selectCharLeft": () => selectCharLeft(client.editorView),
744
+ "editor.selectCharRight": () => selectCharRight(client.editorView),
745
+ "editor.selectGroupLeft": () => selectGroupLeft(client.editorView),
746
+ "editor.selectGroupRight": () => selectGroupRight(client.editorView),
747
+ "editor.selectLineBoundaryLeft": () =>
748
+ selectLineBoundaryLeft(client.editorView),
749
+ "editor.selectLineBoundaryRight": () =>
750
+ selectLineBoundaryRight(client.editorView),
751
+ "editor.selectLineStart": () => selectLineStart(client.editorView),
752
+ "editor.selectLineEnd": () => selectLineEnd(client.editorView),
753
+ "editor.selectDocStart": () => selectDocStart(client.editorView),
754
+ "editor.selectDocEnd": () => selectDocEnd(client.editorView),
755
+ "editor.selectLineUp": () => selectLineUp(client.editorView),
756
+ "editor.selectLineDown": () => selectLineDown(client.editorView),
757
+ "editor.selectPageUp": () => selectPageUp(client.editorView),
758
+ "editor.selectPageDown": () => selectPageDown(client.editorView),
759
+ // Delete
760
+ "editor.deleteCharBackward": () => deleteCharBackward(client.editorView),
761
+ "editor.deleteCharForward": () => deleteCharForward(client.editorView),
762
+ "editor.deleteGroupBackward": () => deleteGroupBackward(client.editorView),
763
+ "editor.deleteGroupForward": () => deleteGroupForward(client.editorView),
764
+ "editor.deleteLineBoundaryBackward": () =>
765
+ deleteLineBoundaryBackward(client.editorView),
766
+ "editor.deleteLineBoundaryForward": () =>
767
+ deleteLineBoundaryForward(client.editorView),
768
+ "editor.transposeChars": () => transposeChars(client.editorView),
769
+ // Enter: accept completion if popup is open, else newline-and-indent
770
+ "editor.insertNewline": () => {
771
+ const view = client.editorView;
772
+ if (acceptCompletion(view)) return true;
773
+ return insertNewlineAndIndent(view);
774
+ },
775
+ // Completion popup control
776
+ "editor.acceptCompletion": () => acceptCompletion(client.editorView),
777
+ "editor.startCompletion": () => startCompletion(client.editorView),
778
+ "editor.closeCompletion": () => closeCompletion(client.editorView),
640
779
  "editor.openSearchPanel": () => {
641
780
  openSearchPanel(client.editorView);
642
781
  },
@@ -651,13 +790,13 @@ export function editorSyscalls(client: Client): SysCallMapping {
651
790
  }
652
791
  } catch (e) {
653
792
  console.error(e);
654
- client.flashNotification(`Could not copy to clipboard: ${e}`);
793
+ client.ui.flashNotification(`Could not copy to clipboard: ${e}`);
655
794
  }
656
795
  },
657
796
  "editor.sendMessage": (_ctx, type: string, data: any) => {
658
- if (!client.isDocumentEditor()) return;
797
+ if (!client.contentManager.isDocumentEditor()) return;
659
798
 
660
- client.documentEditor.sendPublicMessage({
799
+ client.contentManager.documentEditor.sendPublicMessage({
661
800
  type,
662
801
  data,
663
802
  });
@@ -18,19 +18,16 @@ export function eventSyscalls(
18
18
  /**
19
19
  * Define a Lua event listener
20
20
  */
21
- "event.listen": (
22
- _ctx,
23
- def: EventSubscription,
24
- ) => {
21
+ "event.listen": (_ctx, def: EventSubscription) => {
25
22
  // console.log("Registering Lua event listener: ", def.name);
26
- client.config.insert([
27
- "eventListeners",
28
- def.name,
29
- ], async (...args: any[]) => {
30
- // Convert return value to JS
31
- const val = await def.run(...args);
32
- return luaValueToJS(val, LuaStackFrame.lostFrame);
33
- });
23
+ client.config.insert(
24
+ ["eventListeners", def.name],
25
+ async (...args: any[]) => {
26
+ // Convert return value to JS
27
+ const val = await def.run(...args);
28
+ return luaValueToJS(val, LuaStackFrame.lostFrame);
29
+ },
30
+ );
34
31
  },
35
32
  };
36
33
  }
@@ -12,9 +12,7 @@ import {
12
12
  } from "@silverbulletmd/silverbullet/lib/crypto";
13
13
  import { fsEndpoint } from "../../spaces/constants.ts";
14
14
 
15
- export function sandboxFetchSyscalls(
16
- client: Client,
17
- ): SysCallMapping {
15
+ export function sandboxFetchSyscalls(client: Client): SysCallMapping {
18
16
  return {
19
17
  // For use in Lua
20
18
  "http.request": async (
@@ -25,17 +23,18 @@ export function sandboxFetchSyscalls(
25
23
  console.warn("Deprecated: use net.proxyFetch() instead");
26
24
  // JSONify any non-serializable body
27
25
  if (
28
- options?.body && typeof options.body !== "string" &&
26
+ options?.body &&
27
+ typeof options.body !== "string" &&
29
28
  !(options.body instanceof Uint8Array)
30
29
  ) {
31
30
  options.body = JSON.stringify(options.body);
32
31
  }
33
32
  const fetchOptions = options
34
33
  ? {
35
- method: options.method,
36
- headers: {} as Record<string, string>,
37
- body: options.body,
38
- }
34
+ method: options.method,
35
+ headers: {} as Record<string, string>,
36
+ body: options.body,
37
+ }
39
38
  : {};
40
39
 
41
40
  fetchOptions.headers = buildProxyHeaders(options?.headers);
@@ -45,19 +44,27 @@ export function sandboxFetchSyscalls(
45
44
  fetchOptions,
46
45
  );
47
46
  // Do sensible things with the body based on the content type
47
+ // Read as ArrayBuffer first to safely handle empty responses (e.g.
48
+ // PUT/DELETE returning 204 with Content-Type: application/json).
49
+ // resp.arrayBuffer() never throws on an empty body, whereas
50
+ // resp.json() would throw a SyntaxError.
51
+ const rawBytes = new Uint8Array(await resp.arrayBuffer());
48
52
  let body: any;
49
- const contentTypeHeader = options.responseEncoding ||
53
+ const contentTypeHeader =
54
+ options.responseEncoding ||
50
55
  resp.headers.get("x-proxy-header-content-type");
51
56
  const statusCode = +(resp.headers.get("x-proxy-status-code") || "200");
52
- if (contentTypeHeader?.startsWith("application/json")) {
53
- body = await resp.json();
57
+ if (rawBytes.length === 0) {
58
+ body = null;
59
+ } else if (contentTypeHeader?.startsWith("application/json")) {
60
+ body = JSON.parse(new TextDecoder().decode(rawBytes));
54
61
  } else if (
55
62
  contentTypeHeader?.startsWith("application/xml") ||
56
63
  contentTypeHeader?.startsWith("text/")
57
64
  ) {
58
- body = await resp.text();
65
+ body = new TextDecoder().decode(rawBytes);
59
66
  } else {
60
- body = new Uint8Array(await resp.arrayBuffer());
67
+ body = rawBytes;
61
68
  }
62
69
  return {
63
70
  ok: resp.ok,
@@ -74,10 +81,10 @@ export function sandboxFetchSyscalls(
74
81
  // console.log("Got sandbox fetch ", url, op);
75
82
  const fetchOptions = options
76
83
  ? {
77
- method: options.method,
78
- headers: options.headers,
79
- body: options.base64Body && base64Decode(options.base64Body),
80
- }
84
+ method: options.method,
85
+ headers: options.headers,
86
+ body: options.base64Body && base64Decode(options.base64Body),
87
+ }
81
88
  : {};
82
89
  fetchOptions.headers = buildProxyHeaders(options?.headers);
83
90
  const resp = await client.httpSpacePrimitives.authenticatedFetch(
@@ -100,8 +107,11 @@ export function sandboxFetchSyscalls(
100
107
  function buildProxyUrl(client: Client, url: string) {
101
108
  url = url.replace(/^https?:\/\//, "");
102
109
  // Strip off the /.fs and replace with /.proxy
103
- return client.httpSpacePrimitives.url.slice(0, -fsEndpoint.length) +
104
- "/.proxy/" + url;
110
+ return (
111
+ client.httpSpacePrimitives.url.slice(0, -fsEndpoint.length) +
112
+ "/.proxy/" +
113
+ url
114
+ );
105
115
  }
106
116
 
107
117
  function buildProxyHeaders(headers?: Record<string, any>): Record<string, any> {
@@ -115,9 +125,7 @@ function buildProxyHeaders(headers?: Record<string, any>): Record<string, any> {
115
125
  return newHeaders;
116
126
  }
117
127
 
118
- function extractProxyHeaders(
119
- headers: Headers,
120
- ): Record<string, any> {
128
+ function extractProxyHeaders(headers: Headers): Record<string, any> {
121
129
  const newHeaders: Record<string, any> = {};
122
130
  for (const [key, value] of headers.entries()) {
123
131
  if (key.toLowerCase().startsWith("x-proxy-header-")) {
@@ -20,7 +20,16 @@ export function indexSyscalls(
20
20
  "index.tag": (_ctx, tagName: string): LuaQueryCollection => {
21
21
  return objectIndex.tag(tagName);
22
22
  },
23
- "index.ensureFullIndex": (_ctx) => {
23
+ "index.contentPages": (): LuaQueryCollection => {
24
+ return objectIndex.contentPages();
25
+ },
26
+ "index.metaPages": (): LuaQueryCollection => {
27
+ return objectIndex.metaPages();
28
+ },
29
+ "index.aggregates": (): LuaQueryCollection => {
30
+ return objectIndex.aggregates();
31
+ },
32
+ "index.ensureFullIndex": () => {
24
33
  return objectIndex.ensureFullIndex(client.space);
25
34
  },
26
35
  "index.reindexSpace": () => {
@@ -58,7 +67,7 @@ export function indexSyscalls(
58
67
  tag: string,
59
68
  ref: string,
60
69
  ): Promise<ObjectValue | undefined> => {
61
- return objectIndex.getObjectByRef(ref, page, tag);
70
+ return objectIndex.getObjectByRef(page, tag, ref);
62
71
  },
63
72
  "index.queryLuaObjects": (
64
73
  _ctx,