babavoss 0.0.1 → 0.0.2

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 (158) hide show
  1. package/NOTICE +7 -0
  2. package/bin/voss.ts +48 -0
  3. package/gui/babavoss-web.js +234 -0
  4. package/gui/chunk-5hpp1ypv.js +11710 -0
  5. package/gui/chunk-9rq662fd.js +8627 -0
  6. package/gui/chunk-k3cm8j6r.js +433 -0
  7. package/gui/chunk-pk09y98y.js +50 -0
  8. package/gui/chunk-smz02qa6.js +186 -0
  9. package/gui/chunk-wwqypxre.js +49 -0
  10. package/gui/gui.js +2015 -0
  11. package/gui/react-compiler-runtime.js +39 -0
  12. package/gui/react-dom-client.js +21 -0
  13. package/gui/react-dom.js +44 -0
  14. package/gui/react-jsx-runtime.js +19 -0
  15. package/gui/react.js +105 -0
  16. package/gui/theme.css +3053 -0
  17. package/index.ts +15 -0
  18. package/package.json +50 -4
  19. package/src/baba/check.ts +90 -0
  20. package/src/baba/config.ts +261 -0
  21. package/src/baba/find.ts +12 -0
  22. package/src/baba/init.ts +176 -0
  23. package/src/baba/node.ts +483 -0
  24. package/src/baba/project.ts +63 -0
  25. package/src/baba/registry.ts +35 -0
  26. package/src/baba/worker.ts +55 -0
  27. package/src/bench/index.ts +6 -0
  28. package/src/bench/measure.ts +132 -0
  29. package/src/bench/scenarios.ts +136 -0
  30. package/src/build/builder.ts +74 -0
  31. package/src/build/failure.ts +78 -0
  32. package/src/build/guard.ts +85 -0
  33. package/src/build/mdx-register.ts +3 -0
  34. package/src/build/mdx.ts +38 -0
  35. package/src/build/project.ts +38 -0
  36. package/src/build/views.ts +146 -0
  37. package/src/builder/main.ts +29 -0
  38. package/src/desktop/bob.ts +76 -0
  39. package/src/desktop/desktop.css +111 -0
  40. package/src/desktop/icons.ts +50 -0
  41. package/src/desktop/index.ts +323 -0
  42. package/src/desktop/routes.ts +98 -0
  43. package/src/desktop/view.tsx +673 -0
  44. package/src/door/core.ts +384 -0
  45. package/src/ecs/baba.ts +431 -0
  46. package/src/ecs/codec.ts +334 -0
  47. package/src/ecs/handles.ts +91 -0
  48. package/src/ecs/replica.ts +150 -0
  49. package/src/ecs/runtime.ts +603 -0
  50. package/src/ecs/scheduler.ts +75 -0
  51. package/src/ecs/snapshot.ts +102 -0
  52. package/src/ecs/state.ts +759 -0
  53. package/src/ecs/system.ts +256 -0
  54. package/src/ecs/table.ts +420 -0
  55. package/src/ecs/testbed.ts +97 -0
  56. package/src/exec/host.ts +177 -0
  57. package/src/exec/main.ts +98 -0
  58. package/src/exec/watch.ts +7 -0
  59. package/src/exec/wire.ts +29 -0
  60. package/src/generated/build.ts +4 -0
  61. package/src/gui/css.d.ts +1 -0
  62. package/src/gui/gui.tsx +245 -0
  63. package/src/gui/index.ts +51 -0
  64. package/src/gui/inspector.tsx +47 -0
  65. package/src/gui/levels.tsx +73 -0
  66. package/src/gui/promptware.tsx +68 -0
  67. package/src/gui/runner.tsx +118 -0
  68. package/src/gui/theme.css +498 -0
  69. package/src/gui/theme.ts +25 -0
  70. package/src/gui/wizard.tsx +227 -0
  71. package/src/guide/add-a-desktop.mdx +100 -0
  72. package/src/guide/compose-an-interface.mdx +84 -0
  73. package/src/guide/index.ts +13 -0
  74. package/src/guide/reach-outside.mdx +112 -0
  75. package/src/guide/spec-a-system.mdx +93 -0
  76. package/src/guide/systems-together.mdx +72 -0
  77. package/src/guide/write-a-system.mdx +183 -0
  78. package/src/guide/write-promptware.mdx +90 -0
  79. package/src/http/server.ts +310 -0
  80. package/src/kernel/build.ts +21 -0
  81. package/src/kernel/builder.ts +105 -0
  82. package/src/kernel/children.ts +117 -0
  83. package/src/kernel/context.ts +90 -0
  84. package/src/kernel/lock.ts +46 -0
  85. package/src/kernel/names.ts +14 -0
  86. package/src/kernel/schema.ts +130 -0
  87. package/src/kernel/where.ts +12 -0
  88. package/src/kit/index.ts +232 -0
  89. package/src/maker/system.ts +213 -0
  90. package/src/mcp/daemon.ts +61 -0
  91. package/src/mcp/main.ts +208 -0
  92. package/src/mcp/rpc.ts +64 -0
  93. package/src/mcp/tools.ts +125 -0
  94. package/src/prompt/evals.ts +42 -0
  95. package/src/prompt/index.ts +242 -0
  96. package/src/prompt/jsx-dev-runtime.ts +1 -0
  97. package/src/prompt/jsx-runtime.ts +49 -0
  98. package/src/prompt/mdx.d.ts +1 -0
  99. package/src/promptware/compile.ts +183 -0
  100. package/src/promptware/define.ts +16 -0
  101. package/src/promptware/disk.ts +72 -0
  102. package/src/promptware/markdown.d.ts +6 -0
  103. package/src/promptware/sync.ts +437 -0
  104. package/src/promptware/system.ts +215 -0
  105. package/src/runtime/bridge.ts +85 -0
  106. package/src/runtime/connect.ts +54 -0
  107. package/src/runtime/env.ts +35 -0
  108. package/src/runtime/harness.ts +80 -0
  109. package/src/runtime/main.ts +119 -0
  110. package/src/runtime/worker.ts +33 -0
  111. package/src/server/edge.ts +332 -0
  112. package/src/server/main.ts +45 -0
  113. package/src/server/messages.ts +97 -0
  114. package/src/server/protocol.ts +37 -0
  115. package/src/services/args.ts +45 -0
  116. package/src/services/exec.ts +69 -0
  117. package/src/services/fs.ts +139 -0
  118. package/src/services/http.ts +30 -0
  119. package/src/services/index.ts +113 -0
  120. package/src/services/secrets.ts +18 -0
  121. package/src/shell/address.ts +21 -0
  122. package/src/shell/args.ts +219 -0
  123. package/src/shell/client.ts +107 -0
  124. package/src/shell/codes.ts +26 -0
  125. package/src/shell/positional.ts +20 -0
  126. package/src/shell/run.ts +470 -0
  127. package/src/shell/service.ts +167 -0
  128. package/src/shell/state.ts +204 -0
  129. package/src/spec/adapters.ts +72 -0
  130. package/src/spec/diff.ts +26 -0
  131. package/src/spec/files.ts +17 -0
  132. package/src/spec/index.ts +155 -0
  133. package/src/spec/run.ts +97 -0
  134. package/src/spec/take.ts +54 -0
  135. package/src/test/index.ts +8 -0
  136. package/src/test/prove.ts +56 -0
  137. package/src/test/records.ts +23 -0
  138. package/src/test/specs.ts +56 -0
  139. package/src/test/steps.ts +100 -0
  140. package/src/test/voss-dir.ts +17 -0
  141. package/src/transport/messages.ts +110 -0
  142. package/src/transport/transport.ts +62 -0
  143. package/src/wall/probe.ts +67 -0
  144. package/src/wall/profile.ts +103 -0
  145. package/src/wall/spawn.ts +59 -0
  146. package/src/web/app.tsx +53 -0
  147. package/src/web/core.tsx +140 -0
  148. package/src/web/form.ts +155 -0
  149. package/src/web/hooks.ts +135 -0
  150. package/src/web/index.tsx +17 -0
  151. package/src/web/list.ts +19 -0
  152. package/src/web/maker.tsx +766 -0
  153. package/src/web/objects.tsx +213 -0
  154. package/src/web/socket.ts +84 -0
  155. package/src/web/state.tsx +69 -0
  156. package/src/web/store.ts +221 -0
  157. package/src/web/ui.tsx +135 -0
  158. package/README.md +0 -5
package/gui/gui.js ADDED
@@ -0,0 +1,2015 @@
1
+ import {
2
+ require_client
3
+ } from "./chunk-5hpp1ypv.js";
4
+ import"./chunk-smz02qa6.js";
5
+ import {
6
+ ApiError,
7
+ Button,
8
+ Desktop,
9
+ DesktopFrame,
10
+ FieldError,
11
+ Input,
12
+ Maker,
13
+ Row,
14
+ Rows,
15
+ ScopeProvider,
16
+ Select,
17
+ Table,
18
+ Textarea,
19
+ accept,
20
+ api,
21
+ apiHeaders,
22
+ canonicalPath,
23
+ collect,
24
+ describeType,
25
+ effect,
26
+ formatFailure,
27
+ isApps,
28
+ matchRoute,
29
+ plan,
30
+ resource,
31
+ s,
32
+ system,
33
+ tableOf,
34
+ useBaba,
35
+ useBabaState,
36
+ useResource,
37
+ useScope
38
+ } from "./chunk-9rq662fd.js";
39
+ import {
40
+ require_jsx_runtime
41
+ } from "./chunk-pk09y98y.js";
42
+ import {
43
+ require_react
44
+ } from "./chunk-k3cm8j6r.js";
45
+ import {
46
+ __toESM
47
+ } from "./chunk-wwqypxre.js";
48
+
49
+ // src/gui/gui.tsx
50
+ var import_react6 = __toESM(require_react(), 1);
51
+ var import_client = __toESM(require_client(), 1);
52
+
53
+ // src/desktop/icons.ts
54
+ var iconTags = ["path", "circle", "ellipse", "line", "polyline", "polygon", "rect"];
55
+ var geometry = new Set(["d", "cx", "cy", "r", "rx", "ry", "x", "y", "x1", "y1", "x2", "y2", "width", "height", "points"]);
56
+ var keep = (pairs) => Object.fromEntries([...pairs].filter((m) => geometry.has(m[1])).map((m) => [m[1], m[2]]));
57
+ var isTag = (t) => iconTags.includes(t);
58
+ var iconName = /^[a-z0-9]+(-[a-z0-9]+)*$/;
59
+ function shapesOfSvg(text) {
60
+ const out = [];
61
+ for (const m of text.matchAll(/<([a-z]+)\b([^>]*?)\/?>/g))
62
+ if (isTag(m[1]))
63
+ out.push({ tag: m[1], attrs: keep(m[2].matchAll(/([a-z0-9-]+)="([^"]*)"/g)) });
64
+ return out;
65
+ }
66
+ function shapesOfModule(text) {
67
+ const out = [];
68
+ for (const m of text.matchAll(/\[\s*"([a-z]+)"\s*,\s*\{([^}]*)\}\s*\]/g))
69
+ if (isTag(m[1]))
70
+ out.push({ tag: m[1], attrs: keep(m[2].matchAll(/([a-z0-9]+)\s*:\s*"([^"]*)"/g)) });
71
+ return out;
72
+ }
73
+ var iconSources = (name) => [
74
+ { path: `.baba/node_modules/lucide-static/icons/${name}.svg`, parse: shapesOfSvg },
75
+ { path: `.baba/node_modules/lucide-react/dist/esm/icons/${name}.mjs`, parse: shapesOfModule }
76
+ ];
77
+ async function readIcon(name, fs) {
78
+ if (!iconName.test(name))
79
+ return [];
80
+ for (const source of iconSources(name)) {
81
+ let text;
82
+ try {
83
+ text = await fs.read(source.path);
84
+ } catch {
85
+ continue;
86
+ }
87
+ const shapes = source.parse(text);
88
+ if (shapes.length)
89
+ return shapes;
90
+ }
91
+ return [];
92
+ }
93
+
94
+ // src/desktop/index.ts
95
+ var themes = ["system", "light", "dark"];
96
+ var SESSION_IDLE = 24 * 60 * 60 * 1000;
97
+ var sessionNames = [
98
+ "oak",
99
+ "ash",
100
+ "elm",
101
+ "fir",
102
+ "yew",
103
+ "pine",
104
+ "birch",
105
+ "cedar",
106
+ "maple",
107
+ "willow",
108
+ "alder",
109
+ "aspen",
110
+ "beech",
111
+ "hazel",
112
+ "holly",
113
+ "larch",
114
+ "linden",
115
+ "rowan",
116
+ "spruce",
117
+ "poplar",
118
+ "olive",
119
+ "palm",
120
+ "plum",
121
+ "pear",
122
+ "fig",
123
+ "lime",
124
+ "teak",
125
+ "ebony",
126
+ "walnut",
127
+ "chestnut",
128
+ "cypress",
129
+ "juniper",
130
+ "laurel",
131
+ "magnolia",
132
+ "myrtle",
133
+ "sequoia",
134
+ "sycamore",
135
+ "acacia",
136
+ "baobab",
137
+ "ginkgo"
138
+ ];
139
+ function desktop(options) {
140
+ const catalog = (options.apps ?? options.surfaces ?? []).map((v, order) => ({ key: v.key, title: v.title, group: v.group ?? "", icon: v.icon ?? "", many: v.many ?? false, order, routes: [...v.routes ?? []] }));
141
+ if (new Set(catalog.map((v) => v.key)).size !== catalog.length || catalog.some((v) => !/^[a-z][a-z0-9-]*$/.test(v.key)))
142
+ throw new Error("desktop app keys must be unique lowercase names");
143
+ const sessionArg = { session: s.optional(s.string()) };
144
+ const routeArg = { ...sessionArg, app: s.string(), path: s.optional(s.string()), fresh: s.optional(s.boolean()) };
145
+ const windowArg = { ...sessionArg, window: s.optional(s.entity("desktopWindow")) };
146
+ const appShape = { key: s.key(s.string()), title: s.string(), group: s.string(), icon: s.string(), many: s.boolean(), order: s.integer(), routes: s.optional(s.array(s.string())) };
147
+ const sessionShape = {
148
+ key: s.key(s.string()),
149
+ selected: s.nullable(s.entity()),
150
+ theme: s.enum(...themes),
151
+ touched: s.optional(s.integer())
152
+ };
153
+ const settingsShape = { theme: s.enum(...themes) };
154
+ const windowShape = { session: s.entity(), app: s.entity(), path: s.string(), title: s.string(), memory: s.object({}, { open: true }) };
155
+ return system({
156
+ name: "desktop",
157
+ model: {
158
+ desktopApp: appShape,
159
+ desktopSession: sessionShape,
160
+ desktopWindow: windowShape,
161
+ desktopAsk: accept(routeArg),
162
+ desktopAnswer: { session: s.nullable(s.entity()), error: s.nullable(s.string()) },
163
+ desktopIcon: { name: s.string(), shapes: s.array(s.object({ tag: s.enum(...iconTags), attrs: s.object({}, { open: true }) })) },
164
+ desktopSettings: resource(settingsShape, { theme: "system" })
165
+ },
166
+ effects: {
167
+ desktopIconRead: effect({
168
+ summary: "a Lucide icon's shapes, read from the baba's lucide-static or lucide-react; none when neither has it",
169
+ args: s.object({ name: s.string() }),
170
+ retry: 30000,
171
+ result: s.object({ name: s.string(), shapes: s.array(s.object({ tag: s.enum(...iconTags), attrs: s.object({}, { open: true }) })) }),
172
+ run: async ({ name }, ctx) => ({ name, shapes: await readIcon(name, ctx.fs) })
173
+ })
174
+ }
175
+ }, (desktop2, p) => {
176
+ const { desktopApp: app, desktopSession: session, desktopWindow: window, desktopAsk: ask, desktopAnswer: answer, desktopIcon: icon, desktopSettings: settings, desktopIconRead: iconReadEffect } = desktop2;
177
+ const touchedAt = (w, e) => w.get(e, session)?.touched ?? 0;
178
+ function current(w) {
179
+ let best;
180
+ for (const r of w.query(session))
181
+ if (best === undefined || touchedAt(w, r.entity) >= touchedAt(w, best))
182
+ best = r.entity;
183
+ return best;
184
+ }
185
+ function mint(w) {
186
+ const taken = new Set(w.query(session).map((r) => r.desktopSession.key));
187
+ let key = sessionNames.find((n) => !taken.has(n));
188
+ for (let i = 2;key === undefined; i++)
189
+ key = sessionNames.map((n) => `${n}-${i}`).find((n) => !taken.has(n));
190
+ return w.spawn(session, { key, selected: null, theme: "system", touched: w.now });
191
+ }
192
+ const touch = (w, e) => {
193
+ w.add(e, session, { ...w.get(e, session), touched: w.now });
194
+ return e;
195
+ };
196
+ function ensure(w, key) {
197
+ if (key === undefined)
198
+ return current(w) ?? mint(w);
199
+ if (!key.trim())
200
+ throw new Error("session must have a name");
201
+ return w.find(session, key) ?? w.spawn(session, { key, selected: null, theme: "system", touched: w.now });
202
+ }
203
+ const windowsOf = (w, ws) => w.query(window).filter((r) => r.desktopWindow.session === ws);
204
+ function sync(w) {
205
+ for (const item of catalog) {
206
+ const e = w.find(app, item.key);
207
+ if (e === undefined)
208
+ w.spawn(app, item);
209
+ else if (JSON.stringify(w.get(e, app)) !== JSON.stringify(item))
210
+ w.add(e, app, item);
211
+ }
212
+ for (const r of w.query(app))
213
+ if (!catalog.some((v) => v.key === r.desktopApp.key))
214
+ w.despawn(r.entity);
215
+ const seen = new Set;
216
+ for (const r of [...w.query(window)].reverse()) {
217
+ const at = `${r.desktopWindow.session}:${r.desktopWindow.app}`;
218
+ const many = w.get(r.desktopWindow.app, app)?.many ?? false;
219
+ if (!w.has(r.desktopWindow.app, app) || !w.has(r.desktopWindow.session, session) || !many && seen.has(at)) {
220
+ w.despawn(r.entity);
221
+ continue;
222
+ }
223
+ seen.add(at);
224
+ const routes = w.get(r.desktopWindow.app, app)?.routes ?? [];
225
+ if (routes.length && !matchRoute(routes, r.desktopWindow.path))
226
+ w.add(r.entity, window, { ...r.desktopWindow, path: "/" });
227
+ }
228
+ const now = current(w);
229
+ for (const r of w.query(session)) {
230
+ const v = r.desktopSession;
231
+ if (v.theme !== "system") {
232
+ if (w.get(settings).theme === "system")
233
+ w.set(settings, { theme: v.theme });
234
+ w.add(r.entity, session, { ...v, theme: "system" });
235
+ }
236
+ if (v.selected !== null && !w.has(v.selected, window))
237
+ w.add(r.entity, session, { ...w.get(r.entity, session), selected: null });
238
+ else if (v.selected === null && r.entity !== now && w.now - (v.touched ?? 0) > SESSION_IDLE && !windowsOf(w, r.entity).length)
239
+ w.despawn(r.entity);
240
+ }
241
+ }
242
+ function show(w, e, win) {
243
+ w.add(e, session, { ...w.get(e, session), selected: win, touched: w.now });
244
+ }
245
+ function open(w, asked) {
246
+ const item = catalog.find((v) => v.key === asked.app);
247
+ if (!item)
248
+ throw new Error(`unknown desktop app ${asked.app}`);
249
+ if (asked.path !== undefined && (!asked.path.startsWith("/") || asked.path.startsWith("//")))
250
+ throw new Error("an app path must start with one slash");
251
+ const a = asked.path === undefined ? asked : { ...asked, path: canonicalPath(asked.path) };
252
+ if (a.path !== undefined && item.routes.length && !matchRoute(item.routes, a.path))
253
+ throw new Error(`${a.path} is not a path of ${a.app}; its routes: ${item.routes.join(", ")}`);
254
+ sync(w);
255
+ const e = ensure(w, a.session), target = w.find(app, item.key);
256
+ const ws = w.get(e, session);
257
+ const wins = windowsOf(w, e).filter((r) => r.desktopWindow.app === target);
258
+ let win = a.path !== undefined ? wins.find((r) => r.desktopWindow.path === a.path) : undefined;
259
+ if (!win && !(a.fresh && item.many))
260
+ win = wins.find((r) => r.entity === ws.selected) ?? wins.at(-1);
261
+ if (win) {
262
+ if (a.path !== undefined && win.desktopWindow.path !== a.path)
263
+ w.add(win.entity, window, { ...win.desktopWindow, path: a.path });
264
+ show(w, e, win.entity);
265
+ } else {
266
+ show(w, e, w.spawn(window, { session: e, app: target, path: a.path ?? "/", title: "", memory: {} }));
267
+ }
268
+ return e;
269
+ }
270
+ function windowOf(w, e, id) {
271
+ const ws = w.get(e, session);
272
+ const target = id ?? ws.selected;
273
+ if (target === null || target === undefined)
274
+ throw new Error(`no window is selected in ${ws.key}`);
275
+ const v = w.get(target, window);
276
+ if (!v || v.session !== e)
277
+ throw new Error(`${target} is not a window of ${ws.key}`);
278
+ return { entity: target, desktopWindow: v };
279
+ }
280
+ return [
281
+ p.on(iconReadEffect, {
282
+ for: (w) => w.query(app).filter((r) => iconName.test(r.desktopApp.icon) && w.get(r.entity, icon)?.name !== r.desktopApp.icon).map((r) => [r.entity, { name: r.desktopApp.icon }]),
283
+ done: (w, e, r) => {
284
+ if (e !== null && w.has(e, app))
285
+ w.add(e, icon, { name: r.name, shapes: r.shapes });
286
+ }
287
+ }),
288
+ p.step("reconcile", (w) => {
289
+ if (w.query(session).length)
290
+ sync(w);
291
+ }),
292
+ p.step("requests", (w) => {
293
+ for (const r of w.query(ask)) {
294
+ try {
295
+ w.add(r.entity, answer, { session: open(w, r.desktopAsk), error: null });
296
+ } catch (error) {
297
+ w.add(r.entity, answer, { session: null, error: String(error) });
298
+ }
299
+ w.remove(r.entity, ask);
300
+ }
301
+ }),
302
+ p.action("desktop-open", {
303
+ summary: "open an app at a path: the window already there, else the app's current window, else a new one; `fresh` opens another where the app allows many",
304
+ args: routeArg,
305
+ result: s.entity(),
306
+ run: (w, a) => open(w, a)
307
+ }),
308
+ p.action("desktop-select", {
309
+ summary: "bring a window to the front",
310
+ args: { ...sessionArg, window: s.entity("desktopWindow") },
311
+ result: s.entity(),
312
+ run: (w, a) => {
313
+ sync(w);
314
+ const e = ensure(w, a.session);
315
+ const win = windowOf(w, e, a.window);
316
+ show(w, e, win.entity);
317
+ return e;
318
+ }
319
+ }),
320
+ p.action("desktop-close", {
321
+ summary: "close a window, the selected one unless named; closing the one in front brings the window to its left forward, else the one to its right, else nothing, and the page shows its launcher",
322
+ args: windowArg,
323
+ result: s.entity(),
324
+ run: (w, a) => {
325
+ sync(w);
326
+ const e = ensure(w, a.session);
327
+ const ws = w.get(e, session);
328
+ const win = windowOf(w, e, a.window);
329
+ const order = windowsOf(w, e), at = order.findIndex((r) => r.entity === win.entity);
330
+ const next = order[at - 1] ?? order[at + 1];
331
+ w.despawn(win.entity);
332
+ w.add(e, session, { ...ws, selected: ws.selected === win.entity ? next?.entity ?? null : ws.selected, touched: w.now });
333
+ return e;
334
+ }
335
+ }),
336
+ p.action("desktop-title", {
337
+ summary: "name a window after what its app shows, the selected one unless named",
338
+ args: { ...windowArg, title: s.string() },
339
+ result: s.entity(),
340
+ run: (w, a) => {
341
+ sync(w);
342
+ const e = ensure(w, a.session);
343
+ const win = windowOf(w, e, a.window);
344
+ if (win.desktopWindow.title !== a.title)
345
+ w.add(win.entity, window, { ...win.desktopWindow, title: a.title });
346
+ return win.entity;
347
+ }
348
+ }),
349
+ p.action("desktop-remember", {
350
+ summary: "keep a value on a window for its app, the selected window unless named; null forgets the key",
351
+ args: { ...windowArg, key: s.string(), value: s.unknown() },
352
+ result: s.entity(),
353
+ run: (w, a) => {
354
+ sync(w);
355
+ const e = ensure(w, a.session);
356
+ const win = windowOf(w, e, a.window);
357
+ const memory = { ...win.desktopWindow.memory };
358
+ if (a.value === null || a.value === undefined)
359
+ delete memory[a.key];
360
+ else
361
+ memory[a.key] = a.value;
362
+ w.add(win.entity, window, { ...win.desktopWindow, memory });
363
+ return win.entity;
364
+ }
365
+ }),
366
+ p.action("desktop-enter", {
367
+ summary: "enter a session: the session named, made if it is missing, else a new one with a name of its own; a tab enters on load and on focus, so the session it is on is the current one",
368
+ args: sessionArg,
369
+ result: s.entity(),
370
+ run: (w, a) => {
371
+ sync(w);
372
+ return touch(w, a.session === undefined ? mint(w) : ensure(w, a.session));
373
+ }
374
+ }),
375
+ p.action("desktop-leave", {
376
+ summary: "end a session: its windows close and the session goes; the current one unless named",
377
+ args: sessionArg,
378
+ result: s.entity(),
379
+ run: (w, a) => {
380
+ sync(w);
381
+ const e = ensure(w, a.session);
382
+ for (const r of windowsOf(w, e))
383
+ w.despawn(r.entity);
384
+ w.despawn(e);
385
+ return e;
386
+ }
387
+ }),
388
+ p.action("desktop-settings", {
389
+ summary: "set the baba's theme, the same on every page",
390
+ args: { ...sessionArg, theme: s.optional(s.enum(...themes)) },
391
+ run: (w, a) => {
392
+ sync(w);
393
+ if (a.theme)
394
+ w.set(settings, { theme: a.theme });
395
+ }
396
+ }),
397
+ p.query("desktop-state", {
398
+ summary: "a session, the current one unless named: its record, apps and windows; every session beside it, last touched first; the theme",
399
+ args: sessionArg,
400
+ result: s.object({
401
+ entity: s.nullable(s.entity()),
402
+ session: s.nullable(s.object(sessionShape)),
403
+ apps: s.array(s.object({ entity: s.entity(), ...appShape })),
404
+ windows: s.array(s.object({ entity: s.entity(), ...windowShape })),
405
+ sessions: s.array(s.object({ entity: s.entity(), key: s.string(), touched: s.integer(), windows: s.integer(), front: s.nullable(s.string()) })),
406
+ theme: s.enum(...themes)
407
+ }),
408
+ read: (w, a) => {
409
+ const e = a.session === undefined ? current(w) : w.find(session, a.session);
410
+ const sessions = w.query(session).map((r) => {
411
+ const wins = windowsOf(w, r.entity), front = wins.find((o) => o.entity === r.desktopSession.selected);
412
+ const label = front ? front.desktopWindow.title || (front.desktopWindow.path === "/" ? w.get(front.desktopWindow.app, app)?.title ?? null : front.desktopWindow.path) : null;
413
+ return { entity: r.entity, key: r.desktopSession.key, touched: r.desktopSession.touched ?? 0, windows: wins.length, front: label };
414
+ }).sort((x, y) => y.touched - x.touched || y.entity - x.entity);
415
+ return { entity: e ?? null, session: e === undefined ? null : w.get(e, session), apps: w.query(app).map((r) => ({ entity: r.entity, ...r.desktopApp })), windows: e === undefined ? [] : windowsOf(w, e).map((r) => ({ entity: r.entity, ...r.desktopWindow })), sessions, theme: w.get(settings).theme };
416
+ }
417
+ })
418
+ ];
419
+ });
420
+ }
421
+ var shell = desktop({});
422
+
423
+ // src/gui/inspector.tsx
424
+ var import_react = __toESM(require_react(), 1);
425
+ var jsx_runtime = __toESM(require_jsx_runtime(), 1);
426
+ function Inspector({ manifest }) {
427
+ const { w, raw, ready, round } = useBabaState();
428
+ const [filter, setFilter] = import_react.useState("");
429
+ if (!ready)
430
+ return /* @__PURE__ */ jsx_runtime.jsx("p", {
431
+ className: "bv-muted",
432
+ children: "Reading the state…"
433
+ });
434
+ const names = Object.keys(manifest.components);
435
+ const wanted = filter.trim();
436
+ const entities = w.all().filter((e) => !wanted || (wanted in w.of(e)));
437
+ const resources = Object.entries(manifest.resources).map(([n, r]) => ({ name: n, system: r.owner, value: JSON.stringify(w.get({ kind: "resource", name: n })) }));
438
+ return /* @__PURE__ */ jsx_runtime.jsxs("div", {
439
+ children: [
440
+ /* @__PURE__ */ jsx_runtime.jsxs("p", {
441
+ className: "bv-muted",
442
+ children: [
443
+ "round ",
444
+ round,
445
+ " · ",
446
+ w.all().length,
447
+ " entities · ",
448
+ w.jobs.length,
449
+ " jobs"
450
+ ]
451
+ }),
452
+ resources.length > 0 && /* @__PURE__ */ jsx_runtime.jsxs("section", {
453
+ className: "gui-section",
454
+ children: [
455
+ /* @__PURE__ */ jsx_runtime.jsx("h2", {
456
+ children: "Resources"
457
+ }),
458
+ /* @__PURE__ */ jsx_runtime.jsx(Table, {
459
+ columns: ["name", "system", "value"],
460
+ rows: resources
461
+ })
462
+ ]
463
+ }),
464
+ /* @__PURE__ */ jsx_runtime.jsxs("section", {
465
+ className: "gui-section",
466
+ children: [
467
+ /* @__PURE__ */ jsx_runtime.jsx("h2", {
468
+ children: "Entities"
469
+ }),
470
+ /* @__PURE__ */ jsx_runtime.jsxs("div", {
471
+ className: "gui-filter",
472
+ children: [
473
+ /* @__PURE__ */ jsx_runtime.jsx(Input, {
474
+ list: "gui-components",
475
+ placeholder: "with component…",
476
+ value: filter,
477
+ onChange: (e) => setFilter(e.target.value)
478
+ }),
479
+ /* @__PURE__ */ jsx_runtime.jsx("datalist", {
480
+ id: "gui-components",
481
+ children: names.map((n) => /* @__PURE__ */ jsx_runtime.jsx("option", {
482
+ value: n
483
+ }, n))
484
+ }),
485
+ wanted && /* @__PURE__ */ jsx_runtime.jsx(Button, {
486
+ variant: "quiet",
487
+ onClick: () => setFilter(""),
488
+ children: "clear"
489
+ })
490
+ ]
491
+ }),
492
+ entities.length === 0 && /* @__PURE__ */ jsx_runtime.jsxs("p", {
493
+ className: "bv-muted",
494
+ children: [
495
+ "No entity",
496
+ wanted ? ` with ${wanted}` : "",
497
+ "."
498
+ ]
499
+ }),
500
+ entities.map((e) => /* @__PURE__ */ jsx_runtime.jsxs("div", {
501
+ className: "gui-entity",
502
+ children: [
503
+ /* @__PURE__ */ jsx_runtime.jsxs("code", {
504
+ children: [
505
+ "#",
506
+ e
507
+ ]
508
+ }),
509
+ /* @__PURE__ */ jsx_runtime.jsxs("div", {
510
+ className: "gui-parts",
511
+ children: [
512
+ Object.entries(w.of(e)).map(([n, v]) => /* @__PURE__ */ jsx_runtime.jsxs("span", {
513
+ className: "gui-part",
514
+ children: [
515
+ /* @__PURE__ */ jsx_runtime.jsx("b", {
516
+ children: n
517
+ }),
518
+ /* @__PURE__ */ jsx_runtime.jsx("pre", {
519
+ children: v === true ? "" : JSON.stringify(v)
520
+ })
521
+ ]
522
+ }, n)),
523
+ w.jobs.filter((j) => j.entity === e).map((j) => /* @__PURE__ */ jsx_runtime.jsxs("span", {
524
+ className: "gui-part bv-muted",
525
+ children: [
526
+ /* @__PURE__ */ jsx_runtime.jsx("b", {
527
+ children: j.effect
528
+ }),
529
+ /* @__PURE__ */ jsx_runtime.jsx("pre", {
530
+ children: j.status
531
+ })
532
+ ]
533
+ }, j.effect))
534
+ ]
535
+ }),
536
+ /* @__PURE__ */ jsx_runtime.jsx(Button, {
537
+ variant: "quiet",
538
+ onClick: () => void raw({ op: "despawn", entity: e }),
539
+ children: "despawn"
540
+ })
541
+ ]
542
+ }, e))
543
+ ]
544
+ }),
545
+ w.jobs.length > 0 && /* @__PURE__ */ jsx_runtime.jsxs("section", {
546
+ className: "gui-section",
547
+ children: [
548
+ /* @__PURE__ */ jsx_runtime.jsx("h2", {
549
+ children: "Jobs"
550
+ }),
551
+ /* @__PURE__ */ jsx_runtime.jsx(Table, {
552
+ columns: ["effect", "entity", "args", "status"],
553
+ rows: w.jobs.map((j) => ({ effect: j.effect, entity: j.entity ?? "baba", args: JSON.stringify(j.args), status: j.status }))
554
+ })
555
+ ]
556
+ })
557
+ ]
558
+ });
559
+ }
560
+
561
+ // src/gui/runner.tsx
562
+ var import_react2 = __toESM(require_react(), 1);
563
+ var jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
564
+ function Runner({ manifest }) {
565
+ const [selected, setSelected] = import_react2.useState(null);
566
+ const calls = {};
567
+ for (const [n, d] of Object.entries(manifest.actions))
568
+ calls[n] = { kind: "fire", summary: d.summary, args: d.args, result: d.result };
569
+ for (const [n, q] of Object.entries(manifest.queries))
570
+ calls[n] = { kind: "read", summary: q.summary, args: q.args, result: q.result };
571
+ const names = Object.keys(calls);
572
+ if (!selected) {
573
+ return /* @__PURE__ */ jsx_runtime2.jsxs("div", {
574
+ className: "bv-runner",
575
+ children: [
576
+ names.length === 0 && /* @__PURE__ */ jsx_runtime2.jsx("p", {
577
+ className: "bv-muted",
578
+ children: "The baba declares no actions or queries."
579
+ }),
580
+ /* @__PURE__ */ jsx_runtime2.jsx(Rows, {
581
+ children: names.map((n) => /* @__PURE__ */ jsx_runtime2.jsx(Row, {
582
+ hint: `${calls[n].kind === "fire" ? "action" : "query"} · ${calls[n].summary}`,
583
+ onClick: () => setSelected(n),
584
+ children: /* @__PURE__ */ jsx_runtime2.jsx("code", {
585
+ children: n
586
+ })
587
+ }, n))
588
+ })
589
+ ]
590
+ });
591
+ }
592
+ const def = calls[selected];
593
+ return /* @__PURE__ */ jsx_runtime2.jsxs("div", {
594
+ className: "bv-runner",
595
+ children: [
596
+ /* @__PURE__ */ jsx_runtime2.jsx("div", {
597
+ className: "bv-runner-nav",
598
+ children: /* @__PURE__ */ jsx_runtime2.jsx(Button, {
599
+ variant: "quiet",
600
+ onClick: () => setSelected(null),
601
+ children: "← All"
602
+ })
603
+ }),
604
+ /* @__PURE__ */ jsx_runtime2.jsx(Form, {
605
+ name: selected,
606
+ def
607
+ }, selected)
608
+ ]
609
+ });
610
+ }
611
+ function Control({ id, f, value, set, invalid }) {
612
+ const text = typeof value === "string" ? value : "";
613
+ const common = { id, "aria-invalid": invalid || undefined, "aria-required": f.required || undefined };
614
+ switch (f.kind) {
615
+ case "boolean":
616
+ return /* @__PURE__ */ jsx_runtime2.jsx("input", {
617
+ type: "checkbox",
618
+ className: "bv-check",
619
+ ...common,
620
+ checked: value === true,
621
+ onChange: (e) => set(e.target.checked)
622
+ });
623
+ case "enum":
624
+ return /* @__PURE__ */ jsx_runtime2.jsxs(Select, {
625
+ ...common,
626
+ value: text,
627
+ onChange: (e) => set(e.target.value),
628
+ children: [
629
+ /* @__PURE__ */ jsx_runtime2.jsx("option", {
630
+ value: "",
631
+ children: f.required ? "Choose…" : "(none)"
632
+ }),
633
+ f.options.map((o) => /* @__PURE__ */ jsx_runtime2.jsx("option", {
634
+ value: o,
635
+ children: o
636
+ }, o))
637
+ ]
638
+ });
639
+ case "number":
640
+ case "integer":
641
+ return /* @__PURE__ */ jsx_runtime2.jsx(Input, {
642
+ ...common,
643
+ type: "number",
644
+ step: f.kind === "integer" ? 1 : "any",
645
+ value: text,
646
+ onChange: (e) => set(e.target.value)
647
+ });
648
+ case "list":
649
+ return /* @__PURE__ */ jsx_runtime2.jsx(Textarea, {
650
+ ...common,
651
+ rows: 3,
652
+ placeholder: `one ${f.item} per line`,
653
+ value: text,
654
+ onChange: (e) => set(e.target.value)
655
+ });
656
+ case "json":
657
+ return /* @__PURE__ */ jsx_runtime2.jsx(Textarea, {
658
+ ...common,
659
+ rows: 4,
660
+ className: "bv-mono",
661
+ placeholder: "JSON",
662
+ value: text,
663
+ onChange: (e) => set(e.target.value)
664
+ });
665
+ default:
666
+ return /* @__PURE__ */ jsx_runtime2.jsx(Input, {
667
+ ...common,
668
+ value: text,
669
+ onChange: (e) => set(e.target.value)
670
+ });
671
+ }
672
+ }
673
+ function Form({ name, def }) {
674
+ const scope = useScope();
675
+ const uid = import_react2.useId();
676
+ const p = plan(def.args);
677
+ const [values, setValues] = import_react2.useState({});
678
+ const [outcome, setOutcome] = import_react2.useState({ state: "idle" });
679
+ const run = async (e) => {
680
+ e?.preventDefault();
681
+ let args;
682
+ try {
683
+ args = collect(p, values);
684
+ } catch (err) {
685
+ setOutcome({ state: "failed", error: err });
686
+ return;
687
+ }
688
+ setOutcome({ state: "running" });
689
+ const t = performance.now();
690
+ try {
691
+ const value = await api(def.kind, scope, name, args);
692
+ setOutcome({ state: "done", value, ms: Math.round(performance.now() - t) });
693
+ } catch (err) {
694
+ setOutcome({ state: "failed", error: err });
695
+ }
696
+ };
697
+ const bad = outcome.state === "failed" && outcome.error instanceof FieldError ? outcome.error.field : null;
698
+ return /* @__PURE__ */ jsx_runtime2.jsxs("form", {
699
+ className: "bv-form",
700
+ onSubmit: run,
701
+ children: [
702
+ /* @__PURE__ */ jsx_runtime2.jsxs("div", {
703
+ className: "bv-form-head",
704
+ children: [
705
+ /* @__PURE__ */ jsx_runtime2.jsx("code", {
706
+ className: "bv-form-name",
707
+ children: name
708
+ }),
709
+ /* @__PURE__ */ jsx_runtime2.jsxs("span", {
710
+ className: "bv-muted",
711
+ children: [
712
+ def.kind === "fire" ? "action" : "query",
713
+ " · ",
714
+ def.summary
715
+ ]
716
+ })
717
+ ]
718
+ }),
719
+ p.fields.length === 0 && /* @__PURE__ */ jsx_runtime2.jsx("p", {
720
+ className: "bv-muted",
721
+ children: "No arguments."
722
+ }),
723
+ p.fields.map((f) => /* @__PURE__ */ jsx_runtime2.jsxs("div", {
724
+ className: f.kind === "boolean" ? "bv-field bv-field-inline" : "bv-field",
725
+ children: [
726
+ /* @__PURE__ */ jsx_runtime2.jsxs("label", {
727
+ htmlFor: `${uid}-${f.name}`,
728
+ children: [
729
+ f.name,
730
+ f.required && /* @__PURE__ */ jsx_runtime2.jsx("span", {
731
+ className: "bv-required",
732
+ title: "required",
733
+ children: "*"
734
+ }),
735
+ /* @__PURE__ */ jsx_runtime2.jsx("span", {
736
+ className: "bv-type",
737
+ children: describeType(f)
738
+ })
739
+ ]
740
+ }),
741
+ /* @__PURE__ */ jsx_runtime2.jsx(Control, {
742
+ id: `${uid}-${f.name}`,
743
+ f,
744
+ value: values[f.name],
745
+ set: (v) => setValues((s2) => ({ ...s2, [f.name]: v })),
746
+ invalid: bad === f.name
747
+ }),
748
+ f.description && /* @__PURE__ */ jsx_runtime2.jsx("small", {
749
+ className: "bv-muted",
750
+ children: f.description
751
+ })
752
+ ]
753
+ }, f.name)),
754
+ /* @__PURE__ */ jsx_runtime2.jsxs("div", {
755
+ className: "bv-form-actions",
756
+ children: [
757
+ /* @__PURE__ */ jsx_runtime2.jsx(Button, {
758
+ type: "submit",
759
+ variant: "primary",
760
+ disabled: outcome.state === "running",
761
+ children: outcome.state === "running" ? "Running…" : def.kind === "fire" ? "Run" : "Read"
762
+ }),
763
+ outcome.state === "done" && /* @__PURE__ */ jsx_runtime2.jsxs("span", {
764
+ className: "bv-muted",
765
+ children: [
766
+ outcome.ms,
767
+ " ms"
768
+ ]
769
+ })
770
+ ]
771
+ }),
772
+ outcome.state === "failed" && /* @__PURE__ */ jsx_runtime2.jsx(ErrorText, {
773
+ error: outcome.error
774
+ }),
775
+ outcome.state === "done" && /* @__PURE__ */ jsx_runtime2.jsx(Output, {
776
+ value: outcome.value
777
+ })
778
+ ]
779
+ });
780
+ }
781
+ function ErrorText({ error }) {
782
+ if (error instanceof ApiError)
783
+ return /* @__PURE__ */ jsx_runtime2.jsxs("p", {
784
+ className: "bv-error",
785
+ role: "alert",
786
+ children: [
787
+ /* @__PURE__ */ jsx_runtime2.jsx("code", {
788
+ className: "bv-code",
789
+ children: error.code
790
+ }),
791
+ " ",
792
+ error.message
793
+ ]
794
+ });
795
+ if (error instanceof FieldError)
796
+ return /* @__PURE__ */ jsx_runtime2.jsx("p", {
797
+ className: "bv-error",
798
+ role: "alert",
799
+ children: error.message
800
+ });
801
+ return /* @__PURE__ */ jsx_runtime2.jsx("p", {
802
+ className: "bv-error",
803
+ role: "alert",
804
+ children: error instanceof Error ? error.message : String(error)
805
+ });
806
+ }
807
+ function Output({ value }) {
808
+ const t = tableOf(value);
809
+ if (t)
810
+ return /* @__PURE__ */ jsx_runtime2.jsx("div", {
811
+ className: "bv-output",
812
+ children: /* @__PURE__ */ jsx_runtime2.jsx(Table, {
813
+ columns: t.columns,
814
+ rows: t.rows
815
+ })
816
+ });
817
+ if (value === undefined || value === null)
818
+ return /* @__PURE__ */ jsx_runtime2.jsx("div", {
819
+ className: "bv-output bv-muted",
820
+ children: "Done; nothing came back."
821
+ });
822
+ return /* @__PURE__ */ jsx_runtime2.jsx("pre", {
823
+ className: "bv-output bv-json",
824
+ children: JSON.stringify(value, null, 2)
825
+ });
826
+ }
827
+
828
+ // src/gui/promptware.tsx
829
+ var import_react3 = __toESM(require_react(), 1);
830
+ var jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
831
+ function Promptware({ manifest }) {
832
+ const { fire, read, ready, round } = useBabaState();
833
+ const [plan2, setPlan] = import_react3.useState(null);
834
+ const [error, setError] = import_react3.useState(null);
835
+ import_react3.useEffect(() => {
836
+ if (ready)
837
+ read("promptware-plan").then((p) => setPlan(p), setError);
838
+ }, [ready, round]);
839
+ if (!("pwStatus" in manifest.resources))
840
+ return /* @__PURE__ */ jsx_runtime3.jsx("p", {
841
+ className: "bv-muted",
842
+ children: "This baba has no promptware system."
843
+ });
844
+ if (!ready)
845
+ return /* @__PURE__ */ jsx_runtime3.jsx("p", {
846
+ className: "bv-muted",
847
+ children: "Reading the state…"
848
+ });
849
+ const act = (name, args) => {
850
+ setError(null);
851
+ return fire(name, args).catch((e) => {
852
+ setError(e);
853
+ throw e;
854
+ });
855
+ };
856
+ return /* @__PURE__ */ jsx_runtime3.jsxs("div", {
857
+ children: [
858
+ error !== null && /* @__PURE__ */ jsx_runtime3.jsx(ErrorText, {
859
+ error
860
+ }),
861
+ /* @__PURE__ */ jsx_runtime3.jsx(Declared, {
862
+ manifest
863
+ }),
864
+ /* @__PURE__ */ jsx_runtime3.jsxs("section", {
865
+ className: "gui-section",
866
+ children: [
867
+ /* @__PURE__ */ jsx_runtime3.jsx("h2", {
868
+ children: "Outputs"
869
+ }),
870
+ /* @__PURE__ */ jsx_runtime3.jsxs("div", {
871
+ className: "bv-form-actions",
872
+ children: [
873
+ /* @__PURE__ */ jsx_runtime3.jsx(Button, {
874
+ variant: "primary",
875
+ onClick: () => void act("promptware-sync", {}).catch(() => {}),
876
+ children: "Sync now"
877
+ }),
878
+ plan2 && /* @__PURE__ */ jsx_runtime3.jsxs("span", {
879
+ className: "bv-muted",
880
+ children: [
881
+ plan2.pending ? `${plan2.pending} to write` : "up to date",
882
+ " · ",
883
+ plan2.auto ? "writes on every change" : "writes on sync",
884
+ plan2.lastSync ? ` · synced ${new Date(plan2.lastSync).toLocaleTimeString()}` : ""
885
+ ]
886
+ })
887
+ ]
888
+ }),
889
+ plan2?.error && /* @__PURE__ */ jsx_runtime3.jsx("p", {
890
+ className: "bv-error",
891
+ children: plan2.error
892
+ }),
893
+ plan2 && plan2.outputs.length > 0 && /* @__PURE__ */ jsx_runtime3.jsx(Table, {
894
+ columns: ["path", "harness", "status", "action", "reason"],
895
+ rows: plan2.outputs.map((o) => ({ ...o, action: o.action ?? "" }))
896
+ })
897
+ ]
898
+ }),
899
+ /* @__PURE__ */ jsx_runtime3.jsx(Harness, {
900
+ manifest
901
+ })
902
+ ]
903
+ });
904
+ }
905
+ function Declared({ manifest }) {
906
+ const pw = manifest.promptware;
907
+ const skills = Object.entries(pw.skills);
908
+ return /* @__PURE__ */ jsx_runtime3.jsxs("section", {
909
+ className: "gui-section",
910
+ children: [
911
+ /* @__PURE__ */ jsx_runtime3.jsx("h2", {
912
+ children: "Promptware"
913
+ }),
914
+ !pw.context && skills.length === 0 && pw.hooks.length === 0 && /* @__PURE__ */ jsx_runtime3.jsx("p", {
915
+ className: "bv-muted",
916
+ children: "Nothing declared yet: give the baba a `context(...)`, a `skill(...)` or a `hook(...)` under `promptware`."
917
+ }),
918
+ pw.context && /* @__PURE__ */ jsx_runtime3.jsxs("p", {
919
+ className: "bv-muted",
920
+ children: [
921
+ pw.context.length,
922
+ " characters of context, ",
923
+ pw.hooks.length,
924
+ " hook",
925
+ pw.hooks.length === 1 ? "" : "s",
926
+ "."
927
+ ]
928
+ }),
929
+ pw.hooks.length > 0 && /* @__PURE__ */ jsx_runtime3.jsx(Rows, {
930
+ children: pw.hooks.map((h, i) => /* @__PURE__ */ jsx_runtime3.jsx(Row, {
931
+ hint: `${h.on}${h.match ? ` on ${h.match}` : ""}${h.strict ? " · strict" : ""}`,
932
+ children: /* @__PURE__ */ jsx_runtime3.jsx("code", {
933
+ children: h.action
934
+ })
935
+ }, i))
936
+ }),
937
+ skills.length > 0 && /* @__PURE__ */ jsx_runtime3.jsx(Rows, {
938
+ children: skills.map(([k, sk]) => /* @__PURE__ */ jsx_runtime3.jsx(Row, {
939
+ hint: sk.description,
940
+ children: /* @__PURE__ */ jsx_runtime3.jsx("code", {
941
+ children: k
942
+ })
943
+ }, k))
944
+ }),
945
+ /* @__PURE__ */ jsx_runtime3.jsx("p", {
946
+ className: "bv-muted",
947
+ children: "The promptware is the baba's code: edit it under .baba/promptware/; the files below follow."
948
+ })
949
+ ]
950
+ });
951
+ }
952
+ function Harness({ manifest }) {
953
+ const h = manifest.harness;
954
+ return /* @__PURE__ */ jsx_runtime3.jsxs("section", {
955
+ className: "gui-section",
956
+ children: [
957
+ /* @__PURE__ */ jsx_runtime3.jsx("h2", {
958
+ children: "Harness"
959
+ }),
960
+ /* @__PURE__ */ jsx_runtime3.jsxs(Rows, {
961
+ children: [
962
+ /* @__PURE__ */ jsx_runtime3.jsx(Row, {
963
+ hint: h.claude.enabled ? `permissions: ${h.claude.permissions.join(", ")}` : "off",
964
+ children: /* @__PURE__ */ jsx_runtime3.jsx("code", {
965
+ children: "claude"
966
+ })
967
+ }),
968
+ /* @__PURE__ */ jsx_runtime3.jsx(Row, {
969
+ hint: h.codex.enabled ? "on" : "off",
970
+ children: /* @__PURE__ */ jsx_runtime3.jsx("code", {
971
+ children: "codex"
972
+ })
973
+ }),
974
+ /* @__PURE__ */ jsx_runtime3.jsx(Row, {
975
+ hint: `${h.auto ? "writes on every change" : "writes on sync"}${h.table ? " · the contract as a table" : ""}`,
976
+ children: /* @__PURE__ */ jsx_runtime3.jsx("code", {
977
+ children: "files"
978
+ })
979
+ })
980
+ ]
981
+ }),
982
+ /* @__PURE__ */ jsx_runtime3.jsxs("p", {
983
+ className: "bv-muted",
984
+ children: [
985
+ "Declared in .baba/index.ts: ",
986
+ /* @__PURE__ */ jsx_runtime3.jsx("code", {
987
+ children: "baba({ systems, promptware, harness })"
988
+ }),
989
+ "."
990
+ ]
991
+ })
992
+ ]
993
+ });
994
+ }
995
+
996
+ // src/gui/levels.tsx
997
+ var import_react5 = __toESM(require_react(), 1);
998
+
999
+ // src/gui/theme.ts
1000
+ var themeKey = "babavoss.theme";
1001
+ var early = `try{var t=localStorage.getItem(${JSON.stringify(themeKey)});if(t==="light"||t==="dark")document.documentElement.dataset.theme=t}catch(e){}`;
1002
+ function theme() {
1003
+ try {
1004
+ const v = localStorage.getItem(themeKey);
1005
+ if (v === "light" || v === "dark")
1006
+ return v;
1007
+ } catch {}
1008
+ return "system";
1009
+ }
1010
+
1011
+ // src/gui/wizard.tsx
1012
+ var import_react4 = __toESM(require_react(), 1);
1013
+ var jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
1014
+ var getJson = async (path) => {
1015
+ const r = await fetch(path, { headers: apiHeaders() });
1016
+ const j = await r.json();
1017
+ if (!r.ok || j.error)
1018
+ throw new Error(j.error?.message ?? r.statusText);
1019
+ return j;
1020
+ };
1021
+ var postJson = async (path, body) => {
1022
+ const r = await fetch(path, { method: "POST", headers: { "content-type": "application/json", ...apiHeaders() }, body: JSON.stringify(body) });
1023
+ const j = await r.json();
1024
+ if (!r.ok || j.error)
1025
+ throw new Error(j.error?.message ?? r.statusText);
1026
+ return j;
1027
+ };
1028
+ var tilde = (path, home) => home && (path === home || path.startsWith(home + "/")) ? "~" + path.slice(home.length) : path;
1029
+ var short = (path, home, max = 52) => {
1030
+ const t = tilde(path, home);
1031
+ if (t.length <= max)
1032
+ return t;
1033
+ const parts = t.split("/");
1034
+ const head = parts[0] === "" ? "" : parts[0];
1035
+ for (let keep2 = 3;keep2 >= 1; keep2--) {
1036
+ const s2 = `${head}/…/${parts.slice(-keep2).join("/")}`;
1037
+ if (s2.length <= max || keep2 === 1)
1038
+ return s2;
1039
+ }
1040
+ return t;
1041
+ };
1042
+ var slug = (s2) => s2.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
1043
+ var isTyping = (e) => e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement || e.target instanceof HTMLSelectElement;
1044
+ var Svg = ({ children, size = 16 }) => /* @__PURE__ */ jsx_runtime4.jsx("svg", {
1045
+ width: size,
1046
+ height: size,
1047
+ viewBox: "0 0 16 16",
1048
+ fill: "none",
1049
+ stroke: "currentColor",
1050
+ strokeWidth: "1.4",
1051
+ strokeLinecap: "round",
1052
+ strokeLinejoin: "round",
1053
+ "aria-hidden": "true",
1054
+ children
1055
+ });
1056
+ var FolderGlyph = () => /* @__PURE__ */ jsx_runtime4.jsx(Svg, {
1057
+ children: /* @__PURE__ */ jsx_runtime4.jsx("path", {
1058
+ d: "M2 4.5c0-.6.4-1 1-1h3l1.5 1.5H13c.6 0 1 .4 1 1V12c0 .6-.4 1-1 1H3c-.6 0-1-.4-1-1z"
1059
+ })
1060
+ });
1061
+ var BabaGlyph = () => /* @__PURE__ */ jsx_runtime4.jsxs(Svg, {
1062
+ children: [
1063
+ /* @__PURE__ */ jsx_runtime4.jsx("rect", {
1064
+ x: "2.5",
1065
+ y: "2.5",
1066
+ width: "11",
1067
+ height: "11",
1068
+ rx: "3"
1069
+ }),
1070
+ /* @__PURE__ */ jsx_runtime4.jsx("circle", {
1071
+ cx: "8",
1072
+ cy: "8",
1073
+ r: "2",
1074
+ fill: "currentColor",
1075
+ stroke: "none"
1076
+ })
1077
+ ]
1078
+ });
1079
+ var Up = () => /* @__PURE__ */ jsx_runtime4.jsx(Svg, {
1080
+ children: /* @__PURE__ */ jsx_runtime4.jsx("path", {
1081
+ d: "M8 13V3.5M4 7l4-4 4 4"
1082
+ })
1083
+ });
1084
+ var Chevron = () => /* @__PURE__ */ jsx_runtime4.jsx(Svg, {
1085
+ size: 14,
1086
+ children: /* @__PURE__ */ jsx_runtime4.jsx("path", {
1087
+ d: "M6 4l4 4-4 4"
1088
+ })
1089
+ });
1090
+ var Home = () => /* @__PURE__ */ jsx_runtime4.jsx(Svg, {
1091
+ size: 14,
1092
+ children: /* @__PURE__ */ jsx_runtime4.jsx("path", {
1093
+ d: "M2.5 7.5L8 3l5.5 4.5V13H10V9.5H6V13H2.5z"
1094
+ })
1095
+ });
1096
+ var Search = () => /* @__PURE__ */ jsx_runtime4.jsxs(Svg, {
1097
+ size: 14,
1098
+ children: [
1099
+ /* @__PURE__ */ jsx_runtime4.jsx("circle", {
1100
+ cx: "7",
1101
+ cy: "7",
1102
+ r: "4"
1103
+ }),
1104
+ /* @__PURE__ */ jsx_runtime4.jsx("path", {
1105
+ d: "M10 10l3 3"
1106
+ })
1107
+ ]
1108
+ });
1109
+ var Spinner = () => /* @__PURE__ */ jsx_runtime4.jsx("span", {
1110
+ className: "pk-spin",
1111
+ "aria-hidden": "true"
1112
+ });
1113
+ function useInView(list, active) {
1114
+ import_react4.useLayoutEffect(() => {
1115
+ list.current?.querySelector(`[data-i="${active}"]`)?.scrollIntoView({ block: "nearest" });
1116
+ }, [list, active]);
1117
+ }
1118
+ function Folders({ home, start, onBack, onOpened, onVerb }) {
1119
+ const [at, setAt] = import_react4.useState(null);
1120
+ const [loading, setLoading] = import_react4.useState(true);
1121
+ const [error, setError] = import_react4.useState(null);
1122
+ const [busy, setBusy] = import_react4.useState(null);
1123
+ const [filter, setFilter] = import_react4.useState("");
1124
+ const [active, setActive] = import_react4.useState(0);
1125
+ const [name, setName] = import_react4.useState("");
1126
+ const [title, setTitle] = import_react4.useState("");
1127
+ const list = import_react4.useRef(null);
1128
+ const filterRef = import_react4.useRef(null);
1129
+ const homeDir = at?.home || home;
1130
+ const walk = (path) => {
1131
+ setLoading(true);
1132
+ getJson(`/api/dirs?path=${encodeURIComponent(path)}`).then((b) => {
1133
+ const from = at?.path;
1134
+ const back = from ? b.dirs.findIndex((d) => d.path === from) : -1;
1135
+ setAt(b);
1136
+ setError(null);
1137
+ setFilter("");
1138
+ setName("");
1139
+ setTitle("");
1140
+ onVerb?.(b.known ? "open" : b.baba ? "remember" : "make");
1141
+ if (!(document.activeElement instanceof HTMLInputElement) || document.activeElement === filterRef.current)
1142
+ filterRef.current?.focus();
1143
+ setActive(back >= 0 ? back + (b.parent ? 1 : 0) : 0);
1144
+ }, (e) => setError(String(e instanceof Error ? e.message : e))).finally(() => setLoading(false));
1145
+ };
1146
+ import_react4.useEffect(() => {
1147
+ walk(start ?? "");
1148
+ filterRef.current?.focus();
1149
+ }, []);
1150
+ const words = filter.toLowerCase().split(/\s+/).filter(Boolean);
1151
+ const dirs = (at?.dirs ?? []).filter((d) => words.every((w) => d.name.toLowerCase().includes(w)));
1152
+ const rows = [
1153
+ ...at?.parent && !words.length ? [{ kind: "up", path: at.parent }] : [],
1154
+ ...dirs.map((d) => ({ kind: "dir", d }))
1155
+ ];
1156
+ const cur = Math.min(active, Math.max(0, rows.length - 1));
1157
+ useInView(list, cur);
1158
+ const act = async () => {
1159
+ if (!at || busy)
1160
+ return;
1161
+ if (at.known) {
1162
+ const p = await getJson("/api/projects");
1163
+ const own = p.projects.find((x) => x.dir === at.path);
1164
+ if (own)
1165
+ onOpened(own.name);
1166
+ return;
1167
+ }
1168
+ const init = !at.baba;
1169
+ setBusy(init ? "Making the baba and installing its packages…" : "Opening the baba…");
1170
+ setError(null);
1171
+ try {
1172
+ const made = await postJson("/api/projects/add", { dir: at.path, init, ...name.trim() ? { name: name.trim() } : {}, ...title.trim() ? { title: title.trim() } : {} });
1173
+ onOpened(made.name);
1174
+ } catch (e) {
1175
+ setError(String(e instanceof Error ? e.message : e));
1176
+ setBusy(null);
1177
+ }
1178
+ };
1179
+ const enter = (i) => {
1180
+ const r = rows[i];
1181
+ if (r)
1182
+ walk(r.kind === "up" ? r.path : r.d.path);
1183
+ };
1184
+ import_react4.useEffect(() => {
1185
+ const key = (e) => {
1186
+ if (busy || document.querySelector(".desktop-launcher"))
1187
+ return;
1188
+ const inField = isTyping(e) && e.target !== filterRef.current;
1189
+ if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
1190
+ e.preventDefault();
1191
+ act();
1192
+ return;
1193
+ }
1194
+ if (e.key === "Escape") {
1195
+ e.preventDefault();
1196
+ if (inField)
1197
+ e.target.blur();
1198
+ else if (filter)
1199
+ setFilter("");
1200
+ else
1201
+ onBack();
1202
+ return;
1203
+ }
1204
+ if (inField)
1205
+ return;
1206
+ if (e.key === "ArrowDown") {
1207
+ e.preventDefault();
1208
+ setActive(Math.min(rows.length - 1, cur + 1));
1209
+ } else if (e.key === "ArrowUp") {
1210
+ e.preventDefault();
1211
+ setActive(Math.max(0, cur - 1));
1212
+ } else if (e.key === "Enter") {
1213
+ e.preventDefault();
1214
+ enter(cur);
1215
+ } else if (e.key === "ArrowRight" && rows[cur]?.kind === "dir") {
1216
+ e.preventDefault();
1217
+ enter(cur);
1218
+ } else if (e.key === "Backspace" && !filter || e.key === "ArrowLeft" && !filter) {
1219
+ if (at?.parent) {
1220
+ e.preventDefault();
1221
+ walk(at.parent);
1222
+ }
1223
+ } else if (e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey && document.activeElement !== filterRef.current)
1224
+ filterRef.current?.focus();
1225
+ };
1226
+ addEventListener("keydown", key);
1227
+ return () => removeEventListener("keydown", key);
1228
+ });
1229
+ const crumbs = import_react4.useMemo(() => {
1230
+ if (!at)
1231
+ return [];
1232
+ const t = tilde(at.path, homeDir);
1233
+ const parts = t.split("/").filter(Boolean);
1234
+ const root = t.startsWith("~") ? homeDir : "";
1235
+ return parts.map((part, i) => ({ label: part, path: part === "~" && i === 0 ? homeDir : t.startsWith("~") ? root + "/" + parts.slice(1, i + 1).join("/") : "/" + parts.slice(0, i + 1).join("/") }));
1236
+ }, [at, homeDir]);
1237
+ const shown = crumbs.length > 5 ? [crumbs[0], { label: "…", path: crumbs[crumbs.length - 4].path }, ...crumbs.slice(-3)] : crumbs;
1238
+ const here = at ? short(at.path, homeDir, 44) : "";
1239
+ const leaf = at ? at.path.split("/").filter(Boolean).pop() ?? "" : "";
1240
+ return /* @__PURE__ */ jsx_runtime4.jsxs("div", {
1241
+ className: "pk-pane pk-fade",
1242
+ children: [
1243
+ /* @__PURE__ */ jsx_runtime4.jsxs("header", {
1244
+ className: "pk-strip",
1245
+ children: [
1246
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1247
+ className: "pk-strip-title",
1248
+ children: "New baba"
1249
+ }),
1250
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1251
+ className: "pk-gap"
1252
+ }),
1253
+ loading && /* @__PURE__ */ jsx_runtime4.jsx(Spinner, {})
1254
+ ]
1255
+ }),
1256
+ /* @__PURE__ */ jsx_runtime4.jsxs("div", {
1257
+ className: "pk-path",
1258
+ children: [
1259
+ /* @__PURE__ */ jsx_runtime4.jsxs("nav", {
1260
+ className: "pk-crumbs",
1261
+ "aria-label": "Folder",
1262
+ children: [
1263
+ !crumbs.length && /* @__PURE__ */ jsx_runtime4.jsx("span", {
1264
+ className: "pk-crumb is-here",
1265
+ children: "/"
1266
+ }),
1267
+ shown.map((c, i) => /* @__PURE__ */ jsx_runtime4.jsxs("span", {
1268
+ className: "pk-crumb-wrap",
1269
+ children: [
1270
+ i > 0 && /* @__PURE__ */ jsx_runtime4.jsx("span", {
1271
+ className: "pk-crumb-sep",
1272
+ children: "/"
1273
+ }),
1274
+ c.label === "…" ? /* @__PURE__ */ jsx_runtime4.jsx("button", {
1275
+ type: "button",
1276
+ className: "pk-crumb",
1277
+ onClick: () => walk(c.path),
1278
+ title: tilde(c.path, homeDir),
1279
+ children: "…"
1280
+ }) : i === shown.length - 1 ? /* @__PURE__ */ jsx_runtime4.jsx("span", {
1281
+ className: "pk-crumb is-here",
1282
+ children: c.label === "~" ? /* @__PURE__ */ jsx_runtime4.jsx(Home, {}) : c.label
1283
+ }) : /* @__PURE__ */ jsx_runtime4.jsx("button", {
1284
+ type: "button",
1285
+ className: "pk-crumb",
1286
+ onClick: () => walk(c.path),
1287
+ title: tilde(c.path, homeDir),
1288
+ children: c.label === "~" ? /* @__PURE__ */ jsx_runtime4.jsx(Home, {}) : c.label
1289
+ })
1290
+ ]
1291
+ }, c.path))
1292
+ ]
1293
+ }),
1294
+ /* @__PURE__ */ jsx_runtime4.jsxs("label", {
1295
+ className: "pk-filter",
1296
+ children: [
1297
+ /* @__PURE__ */ jsx_runtime4.jsx(Search, {}),
1298
+ /* @__PURE__ */ jsx_runtime4.jsx("input", {
1299
+ ref: filterRef,
1300
+ value: filter,
1301
+ placeholder: "Filter",
1302
+ "aria-label": "Filter folders",
1303
+ spellCheck: false,
1304
+ onChange: (e) => {
1305
+ setFilter(e.target.value);
1306
+ setActive(0);
1307
+ }
1308
+ })
1309
+ ]
1310
+ })
1311
+ ]
1312
+ }),
1313
+ /* @__PURE__ */ jsx_runtime4.jsxs("ol", {
1314
+ ref: list,
1315
+ className: loading && at ? "pk-list pk-folders is-loading" : "pk-list pk-folders",
1316
+ role: "listbox",
1317
+ "aria-label": "Folders",
1318
+ children: [
1319
+ !at && !error && [0, 1, 2, 3, 4].map((i) => /* @__PURE__ */ jsx_runtime4.jsxs("li", {
1320
+ className: "pk-folder is-ghost",
1321
+ children: [
1322
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1323
+ className: "pk-folder-icon",
1324
+ children: /* @__PURE__ */ jsx_runtime4.jsx(FolderGlyph, {})
1325
+ }),
1326
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1327
+ className: "pk-ghost-line",
1328
+ style: { width: `${30 + i * 17 % 35}%` }
1329
+ })
1330
+ ]
1331
+ }, i)),
1332
+ rows.map((r, i) => r.kind === "up" ? /* @__PURE__ */ jsx_runtime4.jsx("li", {
1333
+ "data-i": i,
1334
+ role: "option",
1335
+ "aria-selected": i === cur,
1336
+ children: /* @__PURE__ */ jsx_runtime4.jsxs("button", {
1337
+ type: "button",
1338
+ className: i === cur ? "pk-folder is-active is-up" : "pk-folder is-up",
1339
+ onClick: () => enter(i),
1340
+ onMouseEnter: () => setActive(i),
1341
+ children: [
1342
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1343
+ className: "pk-folder-icon",
1344
+ children: /* @__PURE__ */ jsx_runtime4.jsx(Up, {})
1345
+ }),
1346
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1347
+ className: "pk-folder-name",
1348
+ children: tilde(r.path, homeDir) === "~" ? "Home" : r.path.split("/").filter(Boolean).pop() ?? "/"
1349
+ }),
1350
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1351
+ className: "pk-folder-note",
1352
+ children: "up"
1353
+ })
1354
+ ]
1355
+ })
1356
+ }, "..") : /* @__PURE__ */ jsx_runtime4.jsx("li", {
1357
+ "data-i": i,
1358
+ role: "option",
1359
+ "aria-selected": i === cur,
1360
+ children: /* @__PURE__ */ jsx_runtime4.jsxs("button", {
1361
+ type: "button",
1362
+ className: `pk-folder${i === cur ? " is-active" : ""}${r.d.baba ? " is-baba" : ""}`,
1363
+ onClick: () => enter(i),
1364
+ onMouseEnter: () => setActive(i),
1365
+ children: [
1366
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1367
+ className: "pk-folder-icon",
1368
+ children: r.d.baba ? /* @__PURE__ */ jsx_runtime4.jsx(BabaGlyph, {}) : /* @__PURE__ */ jsx_runtime4.jsx(FolderGlyph, {})
1369
+ }),
1370
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1371
+ className: "pk-folder-name",
1372
+ children: /* @__PURE__ */ jsx_runtime4.jsx(Marked, {
1373
+ text: r.d.name,
1374
+ words
1375
+ })
1376
+ }),
1377
+ r.d.known ? /* @__PURE__ */ jsx_runtime4.jsx("span", {
1378
+ className: "pk-pill is-known",
1379
+ children: "remembered"
1380
+ }) : r.d.baba ? /* @__PURE__ */ jsx_runtime4.jsx("span", {
1381
+ className: "pk-pill",
1382
+ children: "baba"
1383
+ }) : null,
1384
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1385
+ className: "pk-go",
1386
+ children: /* @__PURE__ */ jsx_runtime4.jsx(Chevron, {})
1387
+ })
1388
+ ]
1389
+ })
1390
+ }, r.d.path)),
1391
+ at && rows.length === 0 && /* @__PURE__ */ jsx_runtime4.jsx("li", {
1392
+ className: "pk-none",
1393
+ children: words.length ? /* @__PURE__ */ jsx_runtime4.jsxs(jsx_runtime4.Fragment, {
1394
+ children: [
1395
+ "No folder matches “",
1396
+ filter,
1397
+ "”."
1398
+ ]
1399
+ }) : /* @__PURE__ */ jsx_runtime4.jsx(jsx_runtime4.Fragment, {
1400
+ children: "No folders in here. You can make the baba right here."
1401
+ })
1402
+ })
1403
+ ]
1404
+ }),
1405
+ error && /* @__PURE__ */ jsx_runtime4.jsx("p", {
1406
+ className: "pk-alert",
1407
+ role: "alert",
1408
+ children: error
1409
+ }),
1410
+ /* @__PURE__ */ jsx_runtime4.jsxs("footer", {
1411
+ className: `pk-dock${at?.known ? " is-known" : at?.baba ? " is-baba" : ""}`,
1412
+ children: [
1413
+ /* @__PURE__ */ jsx_runtime4.jsxs("div", {
1414
+ className: "pk-dock-line is-say",
1415
+ children: [
1416
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1417
+ className: "pk-dock-icon",
1418
+ children: at?.baba ? /* @__PURE__ */ jsx_runtime4.jsx(BabaGlyph, {}) : /* @__PURE__ */ jsx_runtime4.jsx(FolderGlyph, {})
1419
+ }),
1420
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1421
+ className: "pk-dock-text",
1422
+ children: !at ? "…" : at.known ? /* @__PURE__ */ jsx_runtime4.jsxs(jsx_runtime4.Fragment, {
1423
+ children: [
1424
+ "Voss already knows the baba in ",
1425
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1426
+ className: "pk-here",
1427
+ title: at?.path,
1428
+ children: here
1429
+ }),
1430
+ "."
1431
+ ]
1432
+ }) : at.baba ? /* @__PURE__ */ jsx_runtime4.jsxs(jsx_runtime4.Fragment, {
1433
+ children: [
1434
+ "There is a baba in ",
1435
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1436
+ className: "pk-here",
1437
+ title: at?.path,
1438
+ children: here
1439
+ }),
1440
+ ". Remember it to open it here."
1441
+ ]
1442
+ }) : /* @__PURE__ */ jsx_runtime4.jsxs(jsx_runtime4.Fragment, {
1443
+ children: [
1444
+ "A new baba goes in ",
1445
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1446
+ className: "pk-here",
1447
+ title: at?.path,
1448
+ children: here
1449
+ }),
1450
+ ", as ",
1451
+ /* @__PURE__ */ jsx_runtime4.jsx("code", {
1452
+ children: ".baba/"
1453
+ }),
1454
+ " beside what is there."
1455
+ ]
1456
+ })
1457
+ })
1458
+ ]
1459
+ }),
1460
+ /* @__PURE__ */ jsx_runtime4.jsxs("div", {
1461
+ className: "pk-dock-line",
1462
+ children: [
1463
+ at && !at.baba && /* @__PURE__ */ jsx_runtime4.jsxs(jsx_runtime4.Fragment, {
1464
+ children: [
1465
+ /* @__PURE__ */ jsx_runtime4.jsxs("label", {
1466
+ className: "pk-field",
1467
+ children: [
1468
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1469
+ children: "Name"
1470
+ }),
1471
+ /* @__PURE__ */ jsx_runtime4.jsx("input", {
1472
+ value: name,
1473
+ placeholder: slug(leaf) || "baba",
1474
+ spellCheck: false,
1475
+ disabled: !!busy,
1476
+ onChange: (e) => setName(e.target.value),
1477
+ onKeyDown: (e) => {
1478
+ if (e.key === "Enter") {
1479
+ e.preventDefault();
1480
+ act();
1481
+ }
1482
+ }
1483
+ })
1484
+ ]
1485
+ }),
1486
+ /* @__PURE__ */ jsx_runtime4.jsxs("label", {
1487
+ className: "pk-field is-wide",
1488
+ children: [
1489
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1490
+ children: "Title"
1491
+ }),
1492
+ /* @__PURE__ */ jsx_runtime4.jsx("input", {
1493
+ value: title,
1494
+ placeholder: name.trim() || slug(leaf) || "baba",
1495
+ disabled: !!busy,
1496
+ onChange: (e) => setTitle(e.target.value),
1497
+ onKeyDown: (e) => {
1498
+ if (e.key === "Enter") {
1499
+ e.preventDefault();
1500
+ act();
1501
+ }
1502
+ }
1503
+ })
1504
+ ]
1505
+ })
1506
+ ]
1507
+ }),
1508
+ busy && /* @__PURE__ */ jsx_runtime4.jsxs("span", {
1509
+ className: "pk-busy",
1510
+ children: [
1511
+ /* @__PURE__ */ jsx_runtime4.jsx(Spinner, {}),
1512
+ busy
1513
+ ]
1514
+ }),
1515
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
1516
+ className: "pk-gap"
1517
+ }),
1518
+ /* @__PURE__ */ jsx_runtime4.jsxs("button", {
1519
+ type: "button",
1520
+ className: "pk-primary",
1521
+ disabled: !at || !!busy || loading,
1522
+ onClick: () => void act(),
1523
+ children: [
1524
+ !at ? "Make a baba here" : at.known ? "Open it" : at.baba ? "Remember this baba" : "Make a baba here",
1525
+ /* @__PURE__ */ jsx_runtime4.jsx("kbd", {
1526
+ className: "pk-kbd is-on-primary",
1527
+ children: "⌘↵"
1528
+ })
1529
+ ]
1530
+ })
1531
+ ]
1532
+ })
1533
+ ]
1534
+ })
1535
+ ]
1536
+ });
1537
+ }
1538
+ function Marked({ text, words }) {
1539
+ if (!words.length)
1540
+ return /* @__PURE__ */ jsx_runtime4.jsx(jsx_runtime4.Fragment, {
1541
+ children: text
1542
+ });
1543
+ const lower = text.toLowerCase();
1544
+ const hit = new Array(text.length).fill(false);
1545
+ for (const w of words) {
1546
+ let i2 = lower.indexOf(w);
1547
+ while (i2 >= 0) {
1548
+ for (let k = i2;k < i2 + w.length; k++)
1549
+ hit[k] = true;
1550
+ i2 = lower.indexOf(w, i2 + w.length);
1551
+ }
1552
+ }
1553
+ const out = [];
1554
+ let i = 0;
1555
+ while (i < text.length) {
1556
+ const on = hit[i];
1557
+ let j = i;
1558
+ while (j < text.length && hit[j] === on)
1559
+ j++;
1560
+ out.push(on ? /* @__PURE__ */ jsx_runtime4.jsx("mark", {
1561
+ children: text.slice(i, j)
1562
+ }, i) : text.slice(i, j));
1563
+ i = j;
1564
+ }
1565
+ return /* @__PURE__ */ jsx_runtime4.jsx(jsx_runtime4.Fragment, {
1566
+ children: out
1567
+ });
1568
+ }
1569
+
1570
+ // src/gui/levels.tsx
1571
+ var jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
1572
+ function useProjects() {
1573
+ const [state, setState] = import_react5.useState({ projects: null, home: "" });
1574
+ import_react5.useEffect(() => {
1575
+ let live = true;
1576
+ getJson("/api/projects").then((j) => {
1577
+ if (live)
1578
+ setState({ projects: j.projects, home: j.home ?? "" });
1579
+ }, () => {
1580
+ if (live)
1581
+ setState({ projects: [], home: "" });
1582
+ });
1583
+ return () => {
1584
+ live = false;
1585
+ };
1586
+ }, []);
1587
+ return state;
1588
+ }
1589
+ var href = (name) => `/${encodeURIComponent(name)}/`;
1590
+ var plus = [{ tag: "path", attrs: { d: "M5 12h14" } }, { tag: "path", attrs: { d: "M12 5v14" } }];
1591
+ var eyes = [{ tag: "circle", attrs: { cx: "8.1", cy: "12", r: "4.3" } }, { tag: "circle", attrs: { cx: "17.2", cy: "12", r: "3" } }];
1592
+ var NEW = "new";
1593
+ var babaKey = (name) => `baba:${name}`;
1594
+ function pickerApps(projects) {
1595
+ const byName = new Map(projects.map((p) => [p.name, p]));
1596
+ const rows = projects.map((p, i) => {
1597
+ const parent = p.parent ? byName.get(p.parent) : undefined;
1598
+ return { entity: 10 + i, key: babaKey(p.name), title: p.title, group: parent ? `In ${parent.title}` : "Babas", icon: "", many: false, order: i, drawing: eyes };
1599
+ });
1600
+ rows.push({ entity: 2, key: NEW, title: "New baba", group: "Make", icon: "plus", many: false, order: projects.length, drawing: plus });
1601
+ return rows;
1602
+ }
1603
+ var wizard = { entity: 3, session: 1, app: 2, path: "/", title: "", memory: {} };
1604
+ function Root() {
1605
+ const { projects, home } = useProjects();
1606
+ const [open, setOpen] = import_react5.useState(false);
1607
+ const apps = import_react5.useMemo(() => pickerApps(projects ?? []), [projects]);
1608
+ const picture = {
1609
+ session: { entity: 1, key: "main", selected: open ? wizard.entity : null, theme: "system" },
1610
+ theme: theme(),
1611
+ apps,
1612
+ windows: open ? [wizard] : []
1613
+ };
1614
+ const hand = { fire: (name, args) => {
1615
+ const a = args ?? {};
1616
+ if (name === "desktop-open") {
1617
+ if (a.app === NEW)
1618
+ setOpen(true);
1619
+ else if (a.app?.startsWith("baba:"))
1620
+ location.assign(href(a.app.slice(5)));
1621
+ } else if (name === "desktop-select")
1622
+ setOpen(a.window === wizard.entity);
1623
+ else if (name === "desktop-close")
1624
+ setOpen(false);
1625
+ } };
1626
+ return /* @__PURE__ */ jsx_runtime5.jsx("div", {
1627
+ className: "gui-view",
1628
+ children: /* @__PURE__ */ jsx_runtime5.jsx(DesktopFrame, {
1629
+ picture,
1630
+ title: "voss",
1631
+ hand,
1632
+ render: () => /* @__PURE__ */ jsx_runtime5.jsx(Folders, {
1633
+ home,
1634
+ onBack: () => setOpen(false),
1635
+ onOpened: (name) => location.assign(href(name))
1636
+ })
1637
+ })
1638
+ });
1639
+ }
1640
+
1641
+ // src/kernel/names.ts
1642
+ var buildName = (b) => b.dev ? "source" : b.version || b.sha || "unstamped";
1643
+ function tellApart(a, b) {
1644
+ const na = buildName(a), nb = buildName(b);
1645
+ if (na !== nb)
1646
+ return [na, nb];
1647
+ const when = (x) => x.at ? `${buildName(x)} built ${x.at.slice(11, 19)}Z` : buildName(x);
1648
+ return [when(a), when(b)];
1649
+ }
1650
+
1651
+ // src/gui/gui.tsx
1652
+ var jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
1653
+ var scope = document.querySelector('meta[name="voss-scope"]')?.getAttribute("content") ?? "";
1654
+ async function describe(name, since, signal) {
1655
+ const r = await fetch(`/api/describe/${encodeURIComponent(name)}${since ? `?since=${since}` : ""}`, { signal, headers: apiHeaders() });
1656
+ const d = await r.json();
1657
+ if (!r.ok)
1658
+ throw new Error(d.error?.message ?? r.statusText);
1659
+ return d;
1660
+ }
1661
+ function useDescribed(name) {
1662
+ const [d, setD] = import_react6.useState(null);
1663
+ const [failed, setFailed] = import_react6.useState(null);
1664
+ import_react6.useEffect(() => {
1665
+ const ctl = new AbortController;
1666
+ (async () => {
1667
+ let since = 0;
1668
+ while (!ctl.signal.aborted) {
1669
+ try {
1670
+ const next = await describe(name, since, ctl.signal);
1671
+ since = next.gen;
1672
+ setD(next);
1673
+ setFailed(null);
1674
+ } catch (e) {
1675
+ if (ctl.signal.aborted)
1676
+ return;
1677
+ setFailed(e instanceof Error ? e.message : String(e));
1678
+ since = 0;
1679
+ await new Promise((r) => setTimeout(r, 2000));
1680
+ }
1681
+ }
1682
+ })();
1683
+ return () => ctl.abort();
1684
+ }, [name]);
1685
+ return { d, failed };
1686
+ }
1687
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
1688
+ function Stale() {
1689
+ const [b, setB] = import_react6.useState(null);
1690
+ const [restarting, setRestarting] = import_react6.useState(false);
1691
+ import_react6.useEffect(() => {
1692
+ let live = true;
1693
+ const tick = async () => {
1694
+ try {
1695
+ const r = await fetch("/api/voss", { headers: apiHeaders() });
1696
+ if (r.ok) {
1697
+ const j = await r.json();
1698
+ if (live)
1699
+ setB(j);
1700
+ }
1701
+ } catch {}
1702
+ };
1703
+ tick();
1704
+ const t = setInterval(() => void tick(), 1e4);
1705
+ return () => {
1706
+ live = false;
1707
+ clearInterval(t);
1708
+ };
1709
+ }, []);
1710
+ if (!b?.stale || !b.installed)
1711
+ return null;
1712
+ const [installed, running] = tellApart(b.installed, b);
1713
+ const restart = async () => {
1714
+ setRestarting(true);
1715
+ try {
1716
+ await fetch("/api/voss/restart", { method: "POST", headers: { "content-type": "application/json", ...apiHeaders() }, body: "{}" });
1717
+ } catch {}
1718
+ const until = Date.now() + 90000;
1719
+ while (Date.now() < until) {
1720
+ await sleep(1000);
1721
+ try {
1722
+ const r = await fetch("/api/voss", { headers: apiHeaders() });
1723
+ if (r.status === 401) {
1724
+ location.reload();
1725
+ return;
1726
+ }
1727
+ if (r.ok && (await r.json()).pid !== b.pid) {
1728
+ location.reload();
1729
+ return;
1730
+ }
1731
+ } catch {}
1732
+ }
1733
+ setRestarting(false);
1734
+ };
1735
+ return /* @__PURE__ */ jsx_runtime6.jsx("div", {
1736
+ className: "gui-stale",
1737
+ role: "status",
1738
+ children: restarting ? /* @__PURE__ */ jsx_runtime6.jsx("span", {
1739
+ children: "Restarting voss…"
1740
+ }) : /* @__PURE__ */ jsx_runtime6.jsxs(jsx_runtime6.Fragment, {
1741
+ children: [
1742
+ /* @__PURE__ */ jsx_runtime6.jsxs("span", {
1743
+ children: [
1744
+ "voss ",
1745
+ /* @__PURE__ */ jsx_runtime6.jsx("b", {
1746
+ children: installed
1747
+ }),
1748
+ " is installed; this page runs on ",
1749
+ /* @__PURE__ */ jsx_runtime6.jsx("b", {
1750
+ children: running
1751
+ }),
1752
+ ". Changes on disk wait for the restart."
1753
+ ]
1754
+ }),
1755
+ /* @__PURE__ */ jsx_runtime6.jsx(Button, {
1756
+ onClick: () => void restart(),
1757
+ children: "Restart to update"
1758
+ })
1759
+ ]
1760
+ })
1761
+ });
1762
+ }
1763
+ var moved = { inspect: "state", world: "state", run: "contract", proofs: "maker" };
1764
+ function Host() {
1765
+ const { d, failed } = useDescribed(scope);
1766
+ const parts = location.pathname.split("/").filter(Boolean);
1767
+ const to = d && parts.length === 2 && !(d.manifest?.apps ?? []).some((a) => a.key === parts[1]) ? moved[parts[1]] : undefined;
1768
+ const [, moveOn] = import_react6.useState(0);
1769
+ import_react6.useEffect(() => {
1770
+ if (to) {
1771
+ history.replaceState(null, "", `/${encodeURIComponent(scope)}/${to}${location.search}${location.hash}`);
1772
+ moveOn((n) => n + 1);
1773
+ }
1774
+ }, [to]);
1775
+ if (to)
1776
+ return null;
1777
+ if (!d)
1778
+ return /* @__PURE__ */ jsx_runtime6.jsx("div", {
1779
+ className: "gui-page",
1780
+ children: /* @__PURE__ */ jsx_runtime6.jsx("p", {
1781
+ className: "bv-muted",
1782
+ children: failed ? `${scope} did not answer: ${failed}` : "…"
1783
+ })
1784
+ });
1785
+ if (!d.manifest)
1786
+ return /* @__PURE__ */ jsx_runtime6.jsx("div", {
1787
+ className: "gui-page",
1788
+ children: /* @__PURE__ */ jsx_runtime6.jsxs("p", {
1789
+ className: "bv-error",
1790
+ children: [
1791
+ d.title,
1792
+ " is not running.",
1793
+ d.error ? ` ${d.error}` : ""
1794
+ ]
1795
+ })
1796
+ });
1797
+ return /* @__PURE__ */ jsx_runtime6.jsx(ShellPage, {
1798
+ d
1799
+ });
1800
+ }
1801
+ function Failure({ f }) {
1802
+ return /* @__PURE__ */ jsx_runtime6.jsxs("div", {
1803
+ className: "gui-notice",
1804
+ role: "alert",
1805
+ children: [
1806
+ /* @__PURE__ */ jsx_runtime6.jsxs("strong", {
1807
+ children: [
1808
+ "The interface did not ",
1809
+ f.stage === "build" ? "build" : f.stage === "load" ? "load" : "render",
1810
+ "."
1811
+ ]
1812
+ }),
1813
+ /* @__PURE__ */ jsx_runtime6.jsx("pre", {
1814
+ className: "gui-failure",
1815
+ children: formatFailure(f)
1816
+ })
1817
+ ]
1818
+ });
1819
+ }
1820
+ var loaded = new Map;
1821
+ async function loadView(name, v) {
1822
+ const have = loaded.get(v);
1823
+ if (have)
1824
+ return have;
1825
+ const base = `/view/${encodeURIComponent(name)}/${v}/`;
1826
+ const res = await fetch(base, { headers: apiHeaders() });
1827
+ const index = await res.json();
1828
+ if (!res.ok || !index.entry)
1829
+ throw new Error(index.error?.message ?? res.statusText);
1830
+ const mod = await import(base + index.entry);
1831
+ const def = mod.default ?? mod.view;
1832
+ if (!def || !isApps(def) && typeof def.shell !== "function")
1833
+ throw new Error("interface.tsx has no default export made by compose()");
1834
+ const css = (index.css ?? []).map((p) => base + p);
1835
+ const l = isApps(def) ? { View: null, apps: def.apps, css } : { View: def.shell, apps: {}, css };
1836
+ loaded.set(v, l);
1837
+ return l;
1838
+ }
1839
+ function useInterface(d) {
1840
+ const v = d.view && !d.view.error ? d.view.v : undefined;
1841
+ const [view, setView] = import_react6.useState(() => v !== undefined ? loaded.get(v) ?? null : null);
1842
+ const [failure, setFailure] = import_react6.useState(null);
1843
+ import_react6.useEffect(() => {
1844
+ if (v === undefined)
1845
+ return;
1846
+ let live = true;
1847
+ loadView(d.name, v).then((l) => {
1848
+ if (live) {
1849
+ setView(l);
1850
+ setFailure(null);
1851
+ }
1852
+ }, (e) => {
1853
+ if (live)
1854
+ setFailure({ stage: "load", message: e instanceof Error ? e.message : String(e) });
1855
+ });
1856
+ return () => {
1857
+ live = false;
1858
+ };
1859
+ }, [d.name, v]);
1860
+ import_react6.useEffect(() => {
1861
+ if (!view)
1862
+ return;
1863
+ const links = view.css.map((href2) => {
1864
+ const l = document.createElement("link");
1865
+ l.rel = "stylesheet";
1866
+ l.href = href2;
1867
+ document.head.appendChild(l);
1868
+ return l;
1869
+ });
1870
+ return () => {
1871
+ for (const l of links)
1872
+ l.remove();
1873
+ };
1874
+ }, [view]);
1875
+ return { view, failure: d.view?.error ?? failure };
1876
+ }
1877
+ var useShellSystem = (apps) => {
1878
+ const key = JSON.stringify(apps ?? []);
1879
+ return import_react6.useMemo(() => desktop({ apps: apps ?? [] }), [key]);
1880
+ };
1881
+ function ShellPage({ d }) {
1882
+ const { view, failure } = useInterface(d);
1883
+ const system2 = useShellSystem(d.manifest?.apps);
1884
+ const manifest = d.manifest;
1885
+ const components = import_react6.useMemo(() => {
1886
+ const pending = () => failure ? /* @__PURE__ */ jsx_runtime6.jsx(Failure, {
1887
+ f: failure
1888
+ }) : /* @__PURE__ */ jsx_runtime6.jsx("p", {
1889
+ className: "desktop-empty bv-muted",
1890
+ children: "…"
1891
+ });
1892
+ const out = {
1893
+ maker: ({ path, navigate, setTitle }) => /* @__PURE__ */ jsx_runtime6.jsx(Maker, {
1894
+ specs: [],
1895
+ title: d.title,
1896
+ path,
1897
+ navigate,
1898
+ setTitle
1899
+ }),
1900
+ state: () => /* @__PURE__ */ jsx_runtime6.jsxs("section", {
1901
+ className: "gui-app",
1902
+ children: [
1903
+ d.error && /* @__PURE__ */ jsx_runtime6.jsxs("p", {
1904
+ className: "bv-error",
1905
+ children: [
1906
+ "The code did not load; the last generation serves: ",
1907
+ d.error
1908
+ ]
1909
+ }),
1910
+ /* @__PURE__ */ jsx_runtime6.jsx(Inspector, {
1911
+ manifest
1912
+ })
1913
+ ]
1914
+ }),
1915
+ contract: () => /* @__PURE__ */ jsx_runtime6.jsx("section", {
1916
+ className: "gui-app",
1917
+ children: /* @__PURE__ */ jsx_runtime6.jsx(Runner, {
1918
+ manifest
1919
+ })
1920
+ }),
1921
+ promptware: () => /* @__PURE__ */ jsx_runtime6.jsx("section", {
1922
+ className: "gui-app",
1923
+ children: /* @__PURE__ */ jsx_runtime6.jsx(Promptware, {
1924
+ manifest
1925
+ })
1926
+ }),
1927
+ settings: Settings
1928
+ };
1929
+ for (const sc of manifest.apps ?? []) {
1930
+ if (sc.key in out)
1931
+ continue;
1932
+ out[sc.key] = sc.key === "view" && !view?.apps[sc.key] ? view?.View ? view.View : !d.view ? () => /* @__PURE__ */ jsx_runtime6.jsx("p", {
1933
+ className: "desktop-empty bv-muted",
1934
+ children: "This baba has no interface.tsx yet: its apps are voss's."
1935
+ }) : pending : pending;
1936
+ }
1937
+ for (const [k, v] of Object.entries(view?.apps ?? {}))
1938
+ out[k] = v;
1939
+ return out;
1940
+ }, [view, failure, system2, manifest, d.error, d.view, d.title]);
1941
+ return /* @__PURE__ */ jsx_runtime6.jsx("div", {
1942
+ className: "gui-view",
1943
+ children: /* @__PURE__ */ jsx_runtime6.jsx(ViewBoundary, {
1944
+ fallback: (f) => /* @__PURE__ */ jsx_runtime6.jsx("div", {
1945
+ className: "gui-page",
1946
+ children: /* @__PURE__ */ jsx_runtime6.jsx(Failure, {
1947
+ f
1948
+ })
1949
+ }),
1950
+ children: /* @__PURE__ */ jsx_runtime6.jsx(Desktop, {
1951
+ system: system2,
1952
+ apps: components,
1953
+ title: d.title,
1954
+ root: { label: "voss", href: "/" }
1955
+ })
1956
+ })
1957
+ }, view ? "loaded" : "loading");
1958
+ }
1959
+ function Settings() {
1960
+ const system2 = import_react6.useMemo(() => desktop({}), []);
1961
+ const current = useResource(system2.desktopSettings)?.theme ?? "system";
1962
+ const { fire } = useBaba();
1963
+ return /* @__PURE__ */ jsx_runtime6.jsxs("section", {
1964
+ className: "gui-app",
1965
+ children: [
1966
+ /* @__PURE__ */ jsx_runtime6.jsx("h2", {
1967
+ children: "Theme"
1968
+ }),
1969
+ /* @__PURE__ */ jsx_runtime6.jsx("p", {
1970
+ className: "bv-muted",
1971
+ children: "The theme every page of this baba shows, kept in its state."
1972
+ }),
1973
+ /* @__PURE__ */ jsx_runtime6.jsx("div", {
1974
+ className: "gui-choices",
1975
+ role: "radiogroup",
1976
+ "aria-label": "Theme",
1977
+ children: themes.map((t) => /* @__PURE__ */ jsx_runtime6.jsx(Button, {
1978
+ variant: t === current ? "default" : "quiet",
1979
+ role: "radio",
1980
+ "aria-checked": t === current,
1981
+ onClick: () => void fire("desktop-settings", { theme: t }),
1982
+ children: t
1983
+ }, t))
1984
+ }),
1985
+ /* @__PURE__ */ jsx_runtime6.jsx("h2", {
1986
+ children: "voss"
1987
+ }),
1988
+ /* @__PURE__ */ jsx_runtime6.jsx("p", {
1989
+ children: /* @__PURE__ */ jsx_runtime6.jsx("a", {
1990
+ href: "/",
1991
+ children: "All babas"
1992
+ })
1993
+ })
1994
+ ]
1995
+ });
1996
+ }
1997
+
1998
+ class ViewBoundary extends import_react6.Component {
1999
+ state = { failure: null };
2000
+ static getDerivedStateFromError(err) {
2001
+ return { failure: { stage: "render", message: err instanceof Error ? err.message : String(err), detail: err instanceof Error ? err.stack : undefined } };
2002
+ }
2003
+ render() {
2004
+ return this.state.failure ? this.props.fallback(this.state.failure) : this.props.children;
2005
+ }
2006
+ }
2007
+ import_client.createRoot(document.getElementById("root")).render(/* @__PURE__ */ jsx_runtime6.jsxs(import_react6.StrictMode, {
2008
+ children: [
2009
+ scope ? /* @__PURE__ */ jsx_runtime6.jsx(ScopeProvider, {
2010
+ value: scope,
2011
+ children: /* @__PURE__ */ jsx_runtime6.jsx(Host, {})
2012
+ }) : /* @__PURE__ */ jsx_runtime6.jsx(Root, {}),
2013
+ /* @__PURE__ */ jsx_runtime6.jsx(Stale, {})
2014
+ ]
2015
+ }));