@silverbulletmd/silverbullet 2.9.0 → 2.10.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 (63) hide show
  1. package/README.md +36 -23
  2. package/client/plugos/hooks/syscall.ts +24 -10
  3. package/client/plugos/syscalls/asset.ts +61 -23
  4. package/client/plugos/syscalls/clientStore.ts +32 -6
  5. package/client/plugos/syscalls/client_code_widget.ts +9 -4
  6. package/client/plugos/syscalls/code_widget.ts +26 -11
  7. package/client/plugos/syscalls/config.ts +146 -30
  8. package/client/plugos/syscalls/datastore.ts +146 -40
  9. package/client/plugos/syscalls/editor.ts +1432 -711
  10. package/client/plugos/syscalls/event.ts +46 -13
  11. package/client/plugos/syscalls/fetch.ts +123 -82
  12. package/client/plugos/syscalls/index.ts +242 -116
  13. package/client/plugos/syscalls/jsonschema.ts +107 -8
  14. package/client/plugos/syscalls/language.ts +38 -12
  15. package/client/plugos/syscalls/markdown.ts +166 -50
  16. package/client/plugos/syscalls/mq.ts +93 -20
  17. package/client/plugos/syscalls/schema_introspection.ts +33 -0
  18. package/client/plugos/syscalls/service_registry.ts +74 -20
  19. package/client/plugos/syscalls/shell.ts +51 -20
  20. package/client/plugos/syscalls/space.ts +169 -113
  21. package/client/plugos/syscalls/sync.ts +50 -19
  22. package/client/plugos/syscalls/system.ts +236 -139
  23. package/client/plugos/system.ts +21 -5
  24. package/client/space_lua/api_documentation.ts +107 -0
  25. package/client/space_lua/ast.ts +13 -0
  26. package/client/space_lua/eval.ts +12 -4
  27. package/client/space_lua/parse.ts +243 -108
  28. package/client/space_lua/pretty_print.ts +128 -54
  29. package/client/space_lua/query_collection.ts +4 -4
  30. package/client/space_lua/render_widget.ts +156 -0
  31. package/client/space_lua/runtime.ts +83 -22
  32. package/client/space_lua/stdlib/crypto.ts +8 -3
  33. package/client/space_lua/stdlib/encoding.ts +27 -9
  34. package/client/space_lua/stdlib/js.ts +137 -22
  35. package/client/space_lua/stdlib/load.ts +24 -15
  36. package/client/space_lua/stdlib/math.ts +323 -118
  37. package/client/space_lua/stdlib/net.ts +57 -9
  38. package/client/space_lua/stdlib/os.ts +111 -44
  39. package/client/space_lua/stdlib/space_lua.ts +344 -20
  40. package/client/space_lua/stdlib/string.ts +282 -104
  41. package/client/space_lua/stdlib/string_pack.ts +58 -19
  42. package/client/space_lua/stdlib/table.ts +195 -40
  43. package/client/space_lua/stdlib.ts +335 -93
  44. package/client/space_lua/syscalls.ts +270 -0
  45. package/dist/plug-compile.js +1 -1
  46. package/package.json +3 -3
  47. package/plug-api/lib/ref.ts +96 -0
  48. package/plug-api/lib/shortcut.ts +2 -1
  49. package/plug-api/syscalls/config.ts +5 -3
  50. package/plug-api/syscalls/editor.ts +16 -0
  51. package/plug-api/syscalls/index.ts +17 -1
  52. package/plug-api/syscalls/jsonschema.ts +9 -0
  53. package/plug-api/syscalls/lua.ts +7 -0
  54. package/plug-api/syscalls/shell.ts +0 -1
  55. package/plug-api/syscalls/system.ts +7 -0
  56. package/plug-api/system_mock.ts +2 -2
  57. package/plug-api/types/index.ts +52 -1
  58. package/plug-api/types/manifest.ts +22 -4
  59. package/plug-api/ui/cx.ts +1 -3
  60. package/plug-api/ui/index.ts +3 -0
  61. package/plug-api/ui/panel_styles.ts +4 -3
  62. package/plug-api/ui/slugify.ts +44 -0
  63. package/client/plugos/syscalls/lua.ts +0 -81
@@ -0,0 +1,270 @@
1
+ import type { ASTCtx, LuaBlock, LuaExpression } from "./ast.ts";
2
+ import { evalExpression } from "./eval.ts";
3
+ import { parseBlock, parseExpressionString } from "./parse.ts";
4
+ import {
5
+ type PrintOptions,
6
+ prettyPrintBlock,
7
+ prettyPrintExpression,
8
+ } from "./pretty_print.ts";
9
+ import {
10
+ isILuaFunction,
11
+ LuaEnv,
12
+ LuaStackFrame,
13
+ LuaTable,
14
+ luaGet,
15
+ luaKeys,
16
+ luaToString,
17
+ luaTypeOf,
18
+ luaValueToJS,
19
+ } from "./runtime.ts";
20
+ import { buildThreadLocalEnv } from "../space_lua_api.ts";
21
+ import type { SysCallMapping, System } from "../plugos/system.ts";
22
+ import { isSendable } from "../plugos/util.ts";
23
+ import type {
24
+ LuaFunctionInfo,
25
+ LuaPropertyInspection,
26
+ LuaValueInspection,
27
+ } from "../../plug-api/types/index.ts";
28
+ import { encodeRef } from "../../plug-api/lib/ref.ts";
29
+ import { resolveASTReference } from "../space_lua.ts";
30
+
31
+ async function inspectValue(
32
+ value: unknown,
33
+ path: string[],
34
+ ): Promise<Omit<LuaValueInspection, "properties">> {
35
+ const type = value instanceof LuaEnv ? "table" : await luaTypeOf(value);
36
+ if (!isILuaFunction(value)) {
37
+ return { type };
38
+ }
39
+ const functionInfo: LuaFunctionInfo = {
40
+ ...(value.info ?? { kind: "builtin" }),
41
+ name: value.info?.name ?? path.join("."),
42
+ };
43
+ const definitionRef =
44
+ functionInfo.kind === "lua" && functionInfo.source
45
+ ? resolveASTReference(functionInfo.source as ASTCtx)
46
+ : null;
47
+ return {
48
+ type,
49
+ functionInfo,
50
+ definition: definitionRef ? encodeRef(definitionRef) : undefined,
51
+ };
52
+ }
53
+
54
+ async function inspectLuaPath(
55
+ env: LuaEnv,
56
+ path: string[],
57
+ ): Promise<LuaValueInspection | null> {
58
+ const sf = LuaStackFrame.createWithGlobalEnv(env);
59
+ let value: unknown = env;
60
+ for (const key of path) {
61
+ value = await luaGet(value, key, null, sf);
62
+ if (value === null || value === undefined) {
63
+ return null;
64
+ }
65
+ }
66
+
67
+ const properties: LuaPropertyInspection[] = [];
68
+ const keys =
69
+ value instanceof LuaEnv
70
+ ? value.keys()
71
+ : value instanceof LuaTable ||
72
+ Array.isArray(value) ||
73
+ (typeof value === "object" && value !== null)
74
+ ? luaKeys(value)
75
+ : [];
76
+ for (const key of [...new Set(keys)]
77
+ .filter((candidate): candidate is string => typeof candidate === "string")
78
+ .sort()) {
79
+ const child = await luaGet(value, key, null, sf);
80
+ if (child === null || child === undefined) continue;
81
+ properties.push({
82
+ key,
83
+ ...(await inspectValue(child, [...path, key])),
84
+ });
85
+ }
86
+
87
+ return {
88
+ ...(await inspectValue(value, path)),
89
+ properties,
90
+ };
91
+ }
92
+
93
+ export function luaSyscalls(
94
+ system: System<any>,
95
+ getEnvironment: () => LuaEnv,
96
+ ): SysCallMapping {
97
+ return {
98
+ "lua.parseBlock": {
99
+ callback: (_ctx, code: string): LuaBlock => parseBlock(code),
100
+ description:
101
+ "Parses a Space Lua chunk and returns its AST. Blocks retain comments in source order with their exact text, kind, and source range.",
102
+ parameters: [
103
+ { name: "code", type: "string", description: "Lua code to parse." },
104
+ ],
105
+ returns: [{ type: "table", description: "Parsed Lua block AST." }],
106
+ examples: [
107
+ {
108
+ code: 'local ast = lua.parseBlock("print(\\"Hello\\")")',
109
+ },
110
+ ],
111
+ see: "API/lua",
112
+ },
113
+ // Deprecated alias for `lua.parseBlock`, kept for backwards compatibility.
114
+ "lua.parse": {
115
+ callback: (_ctx, code: string): LuaBlock => parseBlock(code),
116
+ description: "Deprecated alias for lua.parseBlock.",
117
+ parameters: [
118
+ { name: "code", type: "string", description: "Lua code to parse." },
119
+ ],
120
+ returns: [{ type: "table", description: "Parsed Lua block AST." }],
121
+ deprecated: "Use lua.parseBlock instead.",
122
+ see: "API/lua",
123
+ },
124
+ "lua.parseExpression": {
125
+ callback: (_ctx, expression: string): LuaExpression =>
126
+ parseExpressionString(expression),
127
+ description: "Parses a Space Lua expression and returns its AST.",
128
+ parameters: [
129
+ {
130
+ name: "expression",
131
+ type: "string",
132
+ description: "Lua expression to parse.",
133
+ },
134
+ ],
135
+ returns: [{ type: "table", description: "Parsed expression AST." }],
136
+ examples: [
137
+ {
138
+ code: 'local expression = lua.parseExpression("1 + 2 * 3")',
139
+ },
140
+ ],
141
+ see: "API/lua",
142
+ },
143
+ "lua.prettyPrintBlock": {
144
+ callback: (_ctx, block: LuaBlock, opts?: PrintOptions): string =>
145
+ prettyPrintBlock(block, opts),
146
+ description:
147
+ "Pretty-prints a parsed Space Lua block. Comments are preserved while their placement and indentation are normalized.",
148
+ parameters: [
149
+ { name: "block", type: "table", description: "Parsed block AST." },
150
+ {
151
+ name: "options",
152
+ type: "table",
153
+ description:
154
+ "Formatting options: `indentWidth`, `quote`, and `trailingComma`.",
155
+ optional: true,
156
+ },
157
+ ],
158
+ returns: [{ type: "string", description: "Formatted Lua source." }],
159
+ examples: [
160
+ {
161
+ code: 'local formatted = lua.prettyPrintBlock(lua.parseBlock("if a then return 1 end"))',
162
+ },
163
+ ],
164
+ see: "API/lua",
165
+ },
166
+ "lua.prettyPrintExpression": {
167
+ callback: (
168
+ _ctx,
169
+ expression: LuaExpression,
170
+ opts?: PrintOptions,
171
+ ): string => prettyPrintExpression(expression, opts),
172
+ description: "Pretty-prints a parsed Space Lua expression.",
173
+ parameters: [
174
+ {
175
+ name: "expression",
176
+ type: "table",
177
+ description: "Parsed expression AST.",
178
+ },
179
+ {
180
+ name: "options",
181
+ type: "table",
182
+ description:
183
+ "Formatting options: `indentWidth`, `quote`, and `trailingComma`.",
184
+ optional: true,
185
+ },
186
+ ],
187
+ returns: [{ type: "string", description: "Formatted Lua source." }],
188
+ examples: [
189
+ {
190
+ code: 'local formatted = lua.prettyPrintExpression(lua.parseExpression("{a=1,b=2}"))',
191
+ },
192
+ ],
193
+ see: "API/lua",
194
+ },
195
+ /**
196
+ * Evaluates a Lua expression and returns the result as a JavaScript value
197
+ * @param _ctx
198
+ * @param expression
199
+ * @returns
200
+ */
201
+ "lua.evalExpression": {
202
+ callback: async (_ctx, expression: string) => {
203
+ try {
204
+ const ast = parseExpressionString(expression);
205
+ const globalEnv = getEnvironment();
206
+ const env = await buildThreadLocalEnv(system, globalEnv);
207
+ const sf = new LuaStackFrame(env, null);
208
+ const luaResult = await evalExpression(ast, globalEnv, sf);
209
+ const jsResult = luaValueToJS(luaResult, sf);
210
+ if (isSendable(jsResult)) {
211
+ return jsResult;
212
+ } else {
213
+ // This may evaluate to e.g. a function, which is not sendable, in this case we'll console.warn and return a stringified version of the result
214
+ console.warn(
215
+ "Lua eval result is not sendable, returning stringified version",
216
+ jsResult,
217
+ );
218
+ return luaToString(luaResult);
219
+ }
220
+ } catch (e: any) {
221
+ console.error("Lua eval error: ", e.message, e.sf?.astCtx);
222
+ throw e;
223
+ }
224
+ },
225
+ description: "Evaluates a Space Lua expression.",
226
+ parameters: [
227
+ {
228
+ name: "expression",
229
+ type: "string",
230
+ description: "Lua expression to evaluate.",
231
+ },
232
+ ],
233
+ returns: [{ description: "Evaluated result." }],
234
+ examples: [
235
+ {
236
+ code: 'local result = lua.evalExpression("1 + 2 * 3")\nprint(result)',
237
+ },
238
+ ],
239
+ see: "API/lua",
240
+ },
241
+ "lua.inspect": {
242
+ callback: (_ctx, path: string[] = []) =>
243
+ inspectLuaPath(getEnvironment(), path),
244
+ description:
245
+ "Inspects a value in the live Space Lua environment and returns serializable type, function, definition, and property metadata.",
246
+ parameters: [
247
+ {
248
+ name: "path",
249
+ type: "table",
250
+ description:
251
+ "Sequence of property names from the global environment; omit to inspect globals.",
252
+ optional: true,
253
+ },
254
+ ],
255
+ returns: [
256
+ {
257
+ type: "table|nil",
258
+ description:
259
+ "Inspection metadata, or nil when the requested path does not exist.",
260
+ },
261
+ ],
262
+ examples: [
263
+ {
264
+ code: 'local info = lua.inspect({"editor", "getText"})',
265
+ },
266
+ ],
267
+ see: "API/lua",
268
+ },
269
+ };
270
+ }
@@ -4,7 +4,7 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // version.ts
7
- var version = "2.9.0";
7
+ var version = "2.10.0";
8
8
 
9
9
  // client/plugos/plug_compile.ts
10
10
  import * as path from "node:path";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silverbulletmd/silverbullet",
3
- "version": "2.9.0",
3
+ "version": "2.10.0",
4
4
  "type": "module",
5
5
  "description": "A self-hosted, web-based note taking app",
6
6
  "publishConfig": {
@@ -63,8 +63,8 @@
63
63
  "fmt:check": "biome format .",
64
64
  "bench": "vitest bench",
65
65
  "index-stats": "tsx plugs/index/index_stats.ts",
66
- "test:e2e": "npx playwright test",
67
- "test:e2e:headed": "npx playwright test --headed"
66
+ "test:e2e": "npx playwright test --project=chromium",
67
+ "test:e2e:headed": "npx playwright test --project=chromium --headed"
68
68
  },
69
69
  "peerDependencies": {
70
70
  "preact": "^10.25.0"
@@ -140,6 +140,102 @@ export function parseToRef(stringRef: string): Ref | null {
140
140
  return ref;
141
141
  }
142
142
 
143
+ /**
144
+ * Coerces a ref-or-string (including the legacy `{ page, pos, header }` shape)
145
+ * into a {@link Ref} and validates its structure, throwing on malformed input.
146
+ * Used wherever an external caller (e.g. a syscall) hands in a ref that may be a
147
+ * string or a legacy object.
148
+ */
149
+ export function coerceAndValidateRef(ref: Ref | string): Ref {
150
+ if (typeof ref === "string") {
151
+ const parsedRef = parseToRef(ref);
152
+ if (!parsedRef) {
153
+ throw new Error("Unable to parse string as ref");
154
+ }
155
+ ref = parsedRef;
156
+ }
157
+
158
+ if (
159
+ // @ts-expect-error: Legacy support
160
+ ref.page !== undefined
161
+ ) {
162
+ console.warn(
163
+ "You are using legacy navigation syntax (`{ page, pos, header }`), this will be phased out in the future",
164
+ );
165
+
166
+ const legacyRef = ref as unknown as {
167
+ kind: "page" | "document";
168
+ page: string;
169
+ pos?: number | { line: number; column: number };
170
+ header?: string;
171
+ meta?: boolean;
172
+ };
173
+
174
+ legacyRef.kind ??= "page";
175
+
176
+ let details: Ref["details"];
177
+
178
+ if (typeof legacyRef.pos === "number") {
179
+ details = { type: "position", pos: legacyRef.pos };
180
+ } else if (legacyRef.pos) {
181
+ details = {
182
+ type: "linecolumn",
183
+ line: legacyRef.pos.line,
184
+ column: legacyRef.pos.column,
185
+ };
186
+ } else if (legacyRef.header) {
187
+ details = { type: "header", header: legacyRef.header };
188
+ }
189
+
190
+ ref = {
191
+ path: (legacyRef.kind === "page"
192
+ ? `${legacyRef.page}.md`
193
+ : legacyRef.page) as Path,
194
+ details,
195
+ meta: legacyRef.meta,
196
+ };
197
+ }
198
+
199
+ if (!isValidPath(ref.path) && ref.path !== "") {
200
+ throw new Error("Path passed in ref is invalid");
201
+ } else if (typeof ref.meta !== "boolean" && ref.meta !== undefined) {
202
+ throw new Error("ref.meta has to be of type `boolean`");
203
+ } else if (ref.details !== undefined && typeof ref.details !== "object") {
204
+ throw new Error("ref.details has to be of type `object` or `undefined`");
205
+ } else if (
206
+ ref.details &&
207
+ !["position", "linecolumn", "header", "anchor"].includes(ref.details.type)
208
+ ) {
209
+ throw new Error(
210
+ "ref.details.type has to be 'position', 'linecolumn', 'header' or 'anchor'",
211
+ );
212
+ }
213
+
214
+ if (ref.details?.type === "position" && typeof ref.details.pos !== "number") {
215
+ throw new Error("ref.details.pos has to be of type `number`");
216
+ } else if (
217
+ ref.details?.type === "header" &&
218
+ typeof ref.details.header !== "string"
219
+ ) {
220
+ throw new Error("ref.details.header has to be of type `string`");
221
+ } else if (
222
+ ref.details?.type === "linecolumn" &&
223
+ typeof ref.details.line !== "number" &&
224
+ typeof ref.details.column !== "number"
225
+ ) {
226
+ throw new Error(
227
+ "ref.details.line and ref.details.column has to be of type `number`",
228
+ );
229
+ } else if (
230
+ ref.details?.type === "anchor" &&
231
+ typeof ref.details.name !== "string"
232
+ ) {
233
+ throw new Error("ref.details.name has to be of type `string`");
234
+ }
235
+
236
+ return ref;
237
+ }
238
+
143
239
  /**
144
240
  * The inverse of {@link parseToRef}, encodes a ref object into a reference string.
145
241
  * It tries to produce the shortest valid representation
@@ -1,4 +1,5 @@
1
- export const isMacLike = typeof navigator !== "undefined" &&
1
+ export const isMacLike =
2
+ typeof navigator !== "undefined" &&
2
3
  /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);
3
4
 
4
5
  export function prettifyShortcut(shortcut: string): string {
@@ -60,9 +60,11 @@ export function define(key: string, schema: any): Promise<void> {
60
60
  * ascending `order` (default 0); unknown categories fall to the bottom in
61
61
  * alphabetical order.
62
62
  */
63
- export function defineCategory(
64
- definition: { name: string; description?: string; order?: number },
65
- ): Promise<void> {
63
+ export function defineCategory(definition: {
64
+ name: string;
65
+ description?: string;
66
+ order?: number;
67
+ }): Promise<void> {
66
68
  return syscall("config.defineCategory", definition);
67
69
  }
68
70
 
@@ -129,6 +129,22 @@ export function navigate(
129
129
  return syscall("editor.navigate", ref, replaceState, newWindow);
130
130
  }
131
131
 
132
+ /**
133
+ * Opens the specified page reference, restoring the previous cursor and scroll
134
+ * position for that page when one is remembered from the current session. Like
135
+ * navigate(), an explicit pointer in the ref (e.g. `#header`, `@123`) still wins.
136
+ * @param ref the page reference to open
137
+ * @param replaceState whether to replace the current history state in the browser history
138
+ * @param newWindow whether to open the page in a new window
139
+ */
140
+ export function open(
141
+ ref: Ref | string,
142
+ replaceState = false,
143
+ newWindow = false,
144
+ ): Promise<void> {
145
+ return syscall("editor.open", ref, replaceState, newWindow);
146
+ }
147
+
132
148
  /**
133
149
  * Opens the page navigator
134
150
  * @param mode the mode to open the navigator in
@@ -4,7 +4,6 @@ import type {
4
4
  } from "../../client/space_lua/query_collection.ts";
5
5
  import { syscall } from "@silverbulletmd/silverbullet/syscall";
6
6
  import type { ObjectValue } from "../../plug-api/types/index.ts";
7
-
8
7
  /**
9
8
  * Exposes the SilverBullet object indexing system
10
9
  * @module
@@ -200,3 +199,20 @@ export function resolveAnchor(
200
199
  ): Promise<ResolveAnchorResult> {
201
200
  return syscall("index.resolveAnchor", name, page);
202
201
  }
202
+
203
+ /**
204
+ * Returns a map of tag name → raw JSON Schema for every defined object-type /
205
+ * [[Tag]] that declares a schema. Tags without a schema are omitted.
206
+ * Use it to discover what attributes a tag's objects carry before querying them.
207
+ */
208
+ export function describeSchema(): Promise<Record<string, unknown>> {
209
+ return syscall("index.describeSchema");
210
+ }
211
+
212
+ /**
213
+ * Returns the raw JSON Schema for a single tag, or null if the tag is not
214
+ * defined or has no schema.
215
+ */
216
+ export function tagSchema(tagName: string): Promise<unknown | null> {
217
+ return syscall("index.tagSchema", tagName);
218
+ }
@@ -21,3 +21,12 @@ export function validateObject(
21
21
  export function validateSchema(schema: any): Promise<string | undefined> {
22
22
  return syscall("jsonschema.validateSchema", schema);
23
23
  }
24
+
25
+ /**
26
+ * Infers a best-effort JSON schema from a sample value's shape.
27
+ * @param object the sample value to infer a schema from
28
+ * @returns an inferred JSON schema (draft 2020-12), marked `x-inferred`
29
+ */
30
+ export function inferFromObject(object: any): Promise<any> {
31
+ return syscall("jsonschema.inferFromObject", object);
32
+ }
@@ -1,6 +1,7 @@
1
1
  import { syscall } from "../syscall.ts";
2
2
  import type { LuaBlock, LuaExpression } from "../../client/space_lua/ast.ts";
3
3
  import type { PrintOptions } from "../../client/space_lua/pretty_print.ts";
4
+ import type { LuaValueInspection } from "../types/index.ts";
4
5
 
5
6
  export function parseBlock(code: string): Promise<LuaBlock> {
6
7
  return syscall("lua.parseBlock", code);
@@ -32,3 +33,9 @@ export function prettyPrintExpression(
32
33
  ): Promise<string> {
33
34
  return syscall("lua.prettyPrintExpression", expression, opts);
34
35
  }
36
+
37
+ export function inspect(
38
+ path: string[] = [],
39
+ ): Promise<LuaValueInspection | null> {
40
+ return syscall("lua.inspect", path);
41
+ }
@@ -14,4 +14,3 @@ export function run(
14
14
  ): Promise<{ stdout: string; stderr: string; code: number }> {
15
15
  return syscall("shell.run", cmd, args, stdin);
16
16
  }
17
-
@@ -51,6 +51,13 @@ export function reloadPlugs(): Promise<void> {
51
51
  return syscall("system.reloadPlugs");
52
52
  }
53
53
 
54
+ /**
55
+ * Make edited-on-disk state live and wait until the client is ready again.
56
+ */
57
+ export function reboot(): Promise<void> {
58
+ return syscall("system.reboot");
59
+ }
60
+
54
61
  /**
55
62
  * Load (or reload) a single plug from a space file path.
56
63
  */
@@ -12,7 +12,7 @@ import { eventSyscalls } from "../client/plugos/syscalls/event.ts";
12
12
  import { indexSyscalls } from "../client/plugos/syscalls/index.ts";
13
13
  import { jsonschemaSyscalls } from "../client/plugos/syscalls/jsonschema.ts";
14
14
  import { languageSyscalls } from "../client/plugos/syscalls/language.ts";
15
- import { luaSyscalls } from "../client/plugos/syscalls/lua.ts";
15
+ import { luaSyscalls } from "../client/space_lua/syscalls.ts";
16
16
  import { markdownSyscalls } from "../client/plugos/syscalls/markdown.ts";
17
17
  import { mqSyscalls } from "../client/plugos/syscalls/mq.ts";
18
18
  import {
@@ -64,7 +64,7 @@ export function createMockSystem() {
64
64
  languageSyscalls(),
65
65
  jsonschemaSyscalls(),
66
66
  indexSyscalls(objectIndex, clientMock),
67
- luaSyscalls(clientSystemMock),
67
+ luaSyscalls(system, () => clientSystemMock.spaceLuaEnv.env),
68
68
  mqSyscalls(mq),
69
69
  dataStoreReadSyscalls(ds, clientSystemMock),
70
70
  dataStoreWriteSyscalls(ds),
@@ -36,11 +36,62 @@ export type DocumentMeta = ObjectValue<
36
36
  extension: string;
37
37
  } & Record<string, any>
38
38
  >;
39
- export type SyscallMeta = {
39
+ export type SyscallMeta = LuaFunctionDocumentation & {
40
40
  name: string;
41
41
  requiredPermissions: string[];
42
42
  argCount: number;
43
43
  };
44
+
45
+ export type LuaFunctionParameterDocumentation = {
46
+ name: string;
47
+ type?: string;
48
+ description?: string;
49
+ optional?: boolean;
50
+ };
51
+
52
+ export type LuaFunctionReturnDocumentation = {
53
+ type?: string;
54
+ description?: string;
55
+ };
56
+
57
+ export type LuaFunctionExampleDocumentation = {
58
+ code: string;
59
+ description?: string;
60
+ language?: string;
61
+ };
62
+
63
+ /** Structured documentation shared by Lua functions, built-ins and syscalls. */
64
+ export type LuaFunctionDocumentation = {
65
+ description?: string;
66
+ parameters?: LuaFunctionParameterDocumentation[];
67
+ returns?: LuaFunctionReturnDocumentation[];
68
+ /** Full signatures, used for overloaded or otherwise non-standard functions. */
69
+ signatures?: string[];
70
+ examples?: LuaFunctionExampleDocumentation[];
71
+ deprecated?: string | boolean;
72
+ /** Page or page anchor containing the full documentation. */
73
+ see?: string;
74
+ };
75
+
76
+ export type LuaFunctionInfo = LuaFunctionDocumentation & {
77
+ name?: string;
78
+ kind: "lua" | "builtin" | "syscall";
79
+ source?: Record<string, unknown>;
80
+ };
81
+
82
+ export type LuaPropertyInspection = {
83
+ key: string;
84
+ type: string;
85
+ functionInfo?: LuaFunctionInfo;
86
+ definition?: string;
87
+ };
88
+
89
+ export type LuaValueInspection = {
90
+ type: string;
91
+ functionInfo?: LuaFunctionInfo;
92
+ definition?: string;
93
+ properties: LuaPropertyInspection[];
94
+ };
44
95
  /**
45
96
  * An ObjectValue that can be indexed by the `index` plug, needs to have a minimum of
46
97
  * of two fields:
@@ -1,5 +1,6 @@
1
1
  import type { Manifest as PlugosManifest } from "../../client/plugos/types.ts";
2
2
  import type { NamespaceOperation } from "./namespace.ts";
3
+ import type { LuaFunctionDocumentation } from "./index.ts";
3
4
 
4
5
  export type CodeWidgetT = {
5
6
  codeWidget?: string;
@@ -12,10 +13,23 @@ export type CodeWidgetT = {
12
13
  * Quit, About, etc.) instead of a command-dispatching item.
13
14
  */
14
15
  export type PredefinedMenuKind =
15
- | "quit" | "about" | "services" | "hide" | "hideOthers" | "showAll"
16
- | "closeWindow" | "minimize" | "maximize" | "fullscreen"
16
+ | "quit"
17
+ | "about"
18
+ | "services"
19
+ | "hide"
20
+ | "hideOthers"
21
+ | "showAll"
22
+ | "closeWindow"
23
+ | "minimize"
24
+ | "maximize"
25
+ | "fullscreen"
17
26
  | "separator"
18
- | "undo" | "redo" | "cut" | "copy" | "paste" | "selectAll";
27
+ | "undo"
28
+ | "redo"
29
+ | "cut"
30
+ | "copy"
31
+ | "paste"
32
+ | "selectAll";
19
33
 
20
34
  /**
21
35
  * A single native-menu placement for a command or submenu.
@@ -130,7 +144,11 @@ export type SlashCommandHookT = {
130
144
  };
131
145
 
132
146
  export type SyscallHookT = {
133
- syscall?: string;
147
+ syscall?:
148
+ | string
149
+ | ({
150
+ name: string;
151
+ } & LuaFunctionDocumentation);
134
152
  };
135
153
 
136
154
  export type DocumentEditorT = {
package/plug-api/ui/cx.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  /** Join class-name parts, dropping falsy values. */
2
- export function cx(
3
- ...parts: Array<string | false | null | undefined>
4
- ): string {
2
+ export function cx(...parts: Array<string | false | null | undefined>): string {
5
3
  return parts.filter(Boolean).join(" ");
6
4
  }
@@ -14,6 +14,9 @@ export { Badge } from "./badge.tsx";
14
14
  export type { BadgeProps } from "./badge.tsx";
15
15
  export { Progress } from "./progress.tsx";
16
16
  export type { ProgressProps } from "./progress.tsx";
17
+ export { UrlPrefixInput } from "./url_prefix_input.tsx";
18
+ export type { UrlPrefixInputProps } from "./url_prefix_input.tsx";
19
+ export { normalizePrefix, prefixFromName, slugify } from "./slugify.ts";
17
20
  export { cx } from "./cx.ts";
18
21
  export { panelStyles } from "./panel_styles.ts";
19
22
  export type { PanelStylesOptions } from "./panel_styles.ts";