makersclaw 0.6.0 → 0.8.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 (152) hide show
  1. package/README.md +56 -9
  2. package/package.json +1 -1
  3. package/r/annotate.json +4 -4
  4. package/r/approval-request.json +3 -3
  5. package/r/audio-player.json +3 -3
  6. package/r/auth-form.json +2 -2
  7. package/r/board.json +4 -4
  8. package/r/calendar-view.json +4 -4
  9. package/r/cart.json +3 -3
  10. package/r/chart.json +6 -6
  11. package/r/chat.json +3 -3
  12. package/r/code-editor.json +4 -4
  13. package/r/comment-box.json +4 -4
  14. package/r/contact-section.json +3 -3
  15. package/r/core.json +10 -4
  16. package/r/csv-import.json +4 -4
  17. package/r/data-table.json +3 -3
  18. package/r/diagram.json +3 -3
  19. package/r/doc-editor.json +4 -4
  20. package/r/doc-page.json +4 -4
  21. package/r/email-render.json +2 -2
  22. package/r/faq.json +2 -2
  23. package/r/file-upload.json +3 -3
  24. package/r/flow-canvas.json +4 -4
  25. package/r/footer.json +3 -3
  26. package/r/gantt.json +4 -4
  27. package/r/job-panel.json +3 -3
  28. package/r/json-editor.json +4 -4
  29. package/r/map.json +5 -5
  30. package/r/menubar.json +2 -2
  31. package/r/mind-map.json +4 -4
  32. package/r/navigation-menu.json +2 -2
  33. package/r/order-detail.json +2 -2
  34. package/r/org-chart.json +4 -4
  35. package/r/page-builder.json +6 -6
  36. package/r/payment-methods.json +2 -2
  37. package/r/pdf-document.json +2 -2
  38. package/r/pdf-viewer.json +6 -6
  39. package/r/product-detail.json +3 -3
  40. package/r/product-grid.json +4 -4
  41. package/r/record-form.json +3 -3
  42. package/r/record-notes.json +5 -5
  43. package/r/registry.json +126 -121
  44. package/r/search-dialog.json +3 -3
  45. package/r/settings-list.json +3 -3
  46. package/r/setup-checklist.json +3 -3
  47. package/r/share-dialog.json +3 -3
  48. package/r/sidebar.json +3 -3
  49. package/r/signature-pad.json +3 -3
  50. package/r/sketch-canvas.json +5 -5
  51. package/r/spreadsheet.json +3 -3
  52. package/r/stat-cards.json +4 -4
  53. package/r/task-list.json +4 -4
  54. package/r/task-model.json +3 -3
  55. package/r/testimonials.json +2 -2
  56. package/r/transaction-list.json +3 -3
  57. package/r/whiteboard.json +4 -4
  58. package/registry/blocks/annotate/README.md +24 -12
  59. package/registry/blocks/approval-request/README.md +15 -8
  60. package/registry/blocks/audio-player/README.md +17 -10
  61. package/registry/blocks/board/README.md +13 -9
  62. package/registry/blocks/board/board.jsx +1 -1
  63. package/registry/blocks/calendar-view/README.md +28 -4
  64. package/registry/blocks/cart/README.md +14 -10
  65. package/registry/blocks/chart/README.md +14 -7
  66. package/registry/blocks/chart/cartesian.jsx +1 -1
  67. package/registry/blocks/chart/core.jsx +33 -23
  68. package/registry/blocks/chart/pie.jsx +1 -1
  69. package/registry/blocks/chat/README.md +47 -14
  70. package/registry/blocks/chat/demo.jsx +2 -2
  71. package/registry/blocks/code-editor/README.md +4 -0
  72. package/registry/blocks/code-editor/code-editor.jsx +1 -1
  73. package/registry/blocks/comment-box/README.md +20 -13
  74. package/registry/blocks/contact-section/README.md +7 -6
  75. package/registry/blocks/csv-import/README.md +34 -12
  76. package/registry/blocks/data-table/README.md +41 -8
  77. package/registry/blocks/diagram/diagram.jsx +1 -1
  78. package/registry/blocks/doc-editor/README.md +18 -6
  79. package/registry/blocks/doc-editor/doc-editor.jsx +1 -1
  80. package/registry/blocks/doc-page/README.md +16 -6
  81. package/registry/blocks/file-upload/README.md +7 -1
  82. package/registry/blocks/flow-canvas/README.md +30 -12
  83. package/registry/blocks/flow-canvas/flow-canvas.jsx +1 -1
  84. package/registry/blocks/footer/README.md +7 -4
  85. package/registry/blocks/gantt/README.md +18 -12
  86. package/registry/blocks/job-panel/README.md +1 -1
  87. package/registry/blocks/json-editor/README.md +16 -4
  88. package/registry/blocks/map/README.md +19 -3
  89. package/registry/blocks/map/map-view.jsx +1 -1
  90. package/registry/blocks/map/maplibre.js +1 -1
  91. package/registry/blocks/mind-map/README.md +20 -7
  92. package/registry/blocks/org-chart/README.md +14 -9
  93. package/registry/blocks/page-builder/README.md +14 -7
  94. package/registry/blocks/page-builder/demo.jsx +3 -3
  95. package/registry/blocks/page-builder/page-builder.jsx +1 -1
  96. package/registry/blocks/page-builder/page-view.jsx +1 -1
  97. package/registry/blocks/page-builder/page.js +1 -1
  98. package/registry/blocks/pdf-viewer/README.md +26 -21
  99. package/registry/blocks/pdf-viewer/pdf-viewer.jsx +1 -1
  100. package/registry/blocks/pdf-viewer/pdfjs.js +1 -1
  101. package/registry/blocks/pdf-viewer/ssr-shim.js +1 -1
  102. package/registry/blocks/product-detail/README.md +13 -7
  103. package/registry/blocks/product-detail/demo.jsx +1 -1
  104. package/registry/blocks/product-grid/README.md +12 -10
  105. package/registry/blocks/record-form/README.md +26 -9
  106. package/registry/blocks/record-notes/README.md +42 -30
  107. package/registry/blocks/search-dialog/README.md +38 -7
  108. package/registry/blocks/settings-list/README.md +29 -17
  109. package/registry/blocks/setup-checklist/README.md +12 -14
  110. package/registry/blocks/share-dialog/README.md +1 -1
  111. package/registry/blocks/sidebar/README.md +23 -13
  112. package/registry/blocks/signature-pad/README.md +15 -4
  113. package/registry/blocks/sketch-canvas/README.md +25 -10
  114. package/registry/blocks/sketch-canvas/sketch-canvas.jsx +1 -1
  115. package/registry/blocks/sketch-canvas/ssr-shim.js +3 -3
  116. package/registry/blocks/spreadsheet/README.md +17 -8
  117. package/registry/blocks/stat-cards/README.md +17 -8
  118. package/registry/blocks/task-list/README.md +14 -11
  119. package/registry/blocks/task-model/README.md +3 -3
  120. package/registry/blocks/transaction-list/README.md +15 -7
  121. package/registry/blocks/whiteboard/README.md +34 -22
  122. package/src/cli.mjs +33 -6
  123. package/src/install.mjs +285 -26
  124. package/src/registry.mjs +109 -1
  125. package/templates/next/browser/app/auto-refresh.jsx +67 -0
  126. package/templates/next/browser/app/layout.jsx +94 -0
  127. package/templates/next/browser/app/page.jsx +60 -0
  128. package/templates/next/browser/app/s/[key]/page.jsx +95 -0
  129. package/templates/next/browser/lib/actions.js +16 -0
  130. package/templates/next/browser/lib/data.js +43 -0
  131. package/templates/next/files/README.md +1100 -89
  132. package/templates/next/files/app/frame.js +128 -4
  133. package/templates/next/files/app/shell.jsx +54 -37
  134. package/templates/next/files/check/run.mjs +1514 -45
  135. package/templates/next/files/components/ui/index.js +1 -0
  136. package/templates/next/files/components/ui/item.jsx +2 -2
  137. package/templates/next/files/components/ui/layout.jsx +307 -0
  138. package/templates/next/files/components/ui/notice.jsx +1 -1
  139. package/templates/next/files/components/ui/separator.jsx +1 -1
  140. package/templates/next/files/lib/actions.js +170 -51
  141. package/templates/next/files/lib/brain-server.js +96 -0
  142. package/templates/next/files/lib/brain.js +413 -0
  143. package/templates/next/files/lib/claw.js +15 -1
  144. package/templates/next/files/lib/data.js +119 -29
  145. package/templates/next/files/lib/db.js +97 -17
  146. package/templates/next/files/lib/door.js +680 -0
  147. package/templates/next/files/lib/hooks.js +71 -2
  148. package/templates/next/files/lib/platform.js +93 -4
  149. package/templates/next/files/next.config.mjs +36 -2
  150. package/templates/next/files/surfaces.js +3 -2
  151. package/templates/next/template.json +18 -3
  152. package/templates/next/files/schema.sql +0 -22
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { claw, publishDesk, publishStatus } from "./claw";
5
- import { ask, cancelJob, openRequest, requests as readRequests, startJob, stopRequest } from "./platform";
5
+ import { ask, cancelJob, llm, openRequest, requests as readRequests, startJob, stopRequest } from "./platform";
6
6
 
7
7
  // The React side of the _claw API, without any look: fetching, polling,
8
8
  // streaming and cleanup. A surface renders what comes back and owns every
@@ -17,7 +17,9 @@ import { ask, cancelJob, openRequest, requests as readRequests, startJob, stopRe
17
17
  // a request: the conversation lives in the desk's agent column, and the
18
18
  // app follows only its status. `useRequests` is the same requests seen
19
19
  // from a surface: the asks in flight on this app, so an empty table can
20
- // show the work coming instead of a blank.
20
+ // show the work coming instead of a blank. `useLlm` (0.8.0) is neither:
21
+ // one call to a model, answered in the page, for text work on what the
22
+ // screen holds — nothing to follow, nothing on the desk.
21
23
  //
22
24
  // While the gateway has no _claw routes yet (`unavailable`), polls back
23
25
  // off to once a minute so a preview that is still being wired does not
@@ -672,3 +674,70 @@ export function useJob() {
672
674
 
673
675
  return { ...state, start, cancel, reset, open };
674
676
  }
677
+
678
+ /** What useLlm shows with no call in flight and nothing to show. */
679
+ const QUIET = { loading: false, result: null, error: null, unavailable: false };
680
+
681
+ /**
682
+ * One model call from a component (lib/platform.js, llm). `run(input,
683
+ * opts)` takes llm()'s arguments and resolves llm()'s answer, so a caller
684
+ * that needs the refusal's `code` reads it there; the state is for the
685
+ * screen:
686
+ *
687
+ * loading a call is out
688
+ * result the answer when it was ok: { ok: true, text } or { ok: true,
689
+ * data } (null while loading and after a failure)
690
+ * error the failure's words (null while loading, after an answer,
691
+ * and on a claw with no door)
692
+ * unavailable the platform has no model door yet (a claw from before
693
+ * it) or there is no network: show the quiet "not connected
694
+ * yet", as for useClaw's `unavailable`. Then `error` stays
695
+ * null, so a refusal and a missing door are never both set
696
+ *
697
+ * A run supersedes the one before it: that one is aborted, resolves
698
+ * { ok: false, aborted: true } and changes nothing, so a slow answer never
699
+ * lands over a newer one. The hook owns the signal (one in opts is
700
+ * replaced). Unmounting aborts the call in flight. Nothing is called until
701
+ * `run` is.
702
+ *
703
+ * const { run, loading, result, error, unavailable } = useLlm();
704
+ * run(`Translate to French: ${text}`, { task: "translate" });
705
+ * …{loading ? <Spinner /> : result?.text}
706
+ */
707
+ export function useLlm() {
708
+ const [state, setState] = useState(QUIET);
709
+ const abortRef = useRef(null);
710
+ const runRef = useRef(0); // the run that owns the state
711
+ const aliveRef = useRef(true);
712
+
713
+ // React runs this cleanup and mounts the hook again in development (its
714
+ // double mount; a fast refresh does the same): a call cut off by that
715
+ // cleanup is closed quietly in run(), below, once the answer comes back.
716
+ useEffect(() => {
717
+ aliveRef.current = true;
718
+ return () => {
719
+ aliveRef.current = false;
720
+ abortRef.current?.abort();
721
+ };
722
+ }, []);
723
+
724
+ const run = useCallback(async (input, opts) => {
725
+ abortRef.current?.abort();
726
+ const controller = new AbortController();
727
+ abortRef.current = controller;
728
+ const mine = ++runRef.current;
729
+ setState({ loading: true, result: null, error: null, unavailable: false });
730
+ const answer = await llm(input, { ...opts, signal: controller.signal });
731
+ if (runRef.current !== mine || !aliveRef.current) return answer; // a newer run owns the state, or the page is gone
732
+ if (controller.signal.aborted) {
733
+ setState(QUIET); // cut off by an unmount the hook came back from, with nothing started since
734
+ return answer;
735
+ }
736
+ if (answer.ok) setState({ loading: false, result: answer, error: null, unavailable: false });
737
+ else if (answer.unavailable) setState({ loading: false, result: null, error: null, unavailable: true });
738
+ else setState({ loading: false, result: null, error: answer.error || "the model did not answer", unavailable: false });
739
+ return answer;
740
+ }, []);
741
+
742
+ return { ...state, run };
743
+ }
@@ -8,7 +8,9 @@ import { claw, clawBasePath, clawStream, openInDesk, publishDesk, reportError }
8
8
  // unavailable, code? }; nothing throws. `unavailable: true` (404 or no
9
9
  // network) means the gateway has no such route yet — render a quiet "not
10
10
  // connected yet" state, not an error. `code` names a refusal a caller
11
- // can act on (`not_your_thread`, `needs_answer`, `queue_full`).
11
+ // can act on (`not_your_thread`, `needs_answer`, `queue_full`). `llm` is
12
+ // the one that resolves the platform's answer as it is — { ok: true, text }
13
+ // or { ok: true, data } — with no { status, data } around it.
12
14
  //
13
15
  // What lives here: who the app is (`me`), the error relay
14
16
  // (`reportError`), and ASKING — a screen asks its agent for work with a
@@ -20,8 +22,12 @@ import { claw, clawBasePath, clawStream, openInDesk, publishDesk, reportError }
20
22
  // Activity as tabs on the window, and nothing here lists chats, runs or
21
23
  // activity. `startJob` / `cancelJob` are the 0.3.x door to the same work,
22
24
  // streamed into the app; they stay for a claw that has no _claw/ask yet.
23
- // `claw(path, opts)` and `clawStream` in lib/claw.js are the generic
24
- // doors for the rest of §5.
25
+ // And CALLING A MODEL (0.8.0): `llm` is one shot of text work on what is
26
+ // on screen — sort these, draft that, translate this — answered in the
27
+ // page, with no thread, no tools and no request on the desk. It is the
28
+ // opposite of `ask`, which hands work to the app's agent. `claw(path,
29
+ // opts)` and `clawStream` in lib/claw.js are the generic doors for the
30
+ // rest of §5.
25
31
 
26
32
  /** GET _claw/me → { artifact: { id, name, vehicle, status, home },
27
33
  * user: { id, email }, blueprint, desk_origin } */
@@ -150,8 +156,91 @@ export function cancelJob(sessionId) {
150
156
  return claw("cancel", { method: "POST", body: { session_id: sessionId } });
151
157
  }
152
158
 
159
+ /**
160
+ * Call a model: POST _claw/llm. One shot of text work on what the screen
161
+ * holds — sort, draft, translate, summarise, pull a field out — answered
162
+ * here and now. Work that needs tools, the workspace or more than one
163
+ * step is `ask()`'s: that is the app's agent. The app never names a model
164
+ * (`tier` picks the size) and never holds a key; the platform meters the
165
+ * call to the member who made it.
166
+ *
167
+ * input what to work on: a string, or a conversation, [{ role:
168
+ * "user" | "assistant", content }] — at most 100 messages,
169
+ * none empty, the last one the user's (required)
170
+ * system how to go about it (at most 8,000 characters)
171
+ * schema a JSON Schema for the answer (an object, at most 16 KB
172
+ * serialised): the answer comes back as `data`, checked
173
+ * against it, not as `text`. A miss is sent back to the
174
+ * model once; a second is `schema_mismatch`. No `pattern`,
175
+ * `patternProperties`, `$ref`, `$defs` or `definitions`
176
+ * (bad_input); nesting at most 20 levels (bad_input); at
177
+ * most 1,000 parts, each object, `enum` value and listed
178
+ * type (too_large). A reply too large to check against the
179
+ * schema is a miss. `data` keeps keys the schema does not
180
+ * name
181
+ * maxTokens the answer's ceiling, a whole number (default 1,024; over
182
+ * 4,096 is cut to 4,096). Sent as `max_tokens`
183
+ * tier "fast" (the default) or "smart", for the harder job
184
+ * task a short label for the usage line, /^[a-z0-9-]{1,40}$/
185
+ * signal an AbortSignal (an abort resolves { ok: false, aborted:
186
+ * true })
187
+ *
188
+ * → { ok: true, text } no schema
189
+ * { ok: true, data } with a schema
190
+ * { ok: false, error, code } a refusal, passed through as the platform
191
+ * sent it (`status` and `unavailable: false` come with it). `code` is
192
+ * one of, with its status:
193
+ * for the member (show `error` as it is) —
194
+ * no_credits 402 (the workspace wallet, a suspension, or the
195
+ * Free plan's daily limit), member_cap 402 (the member's monthly
196
+ * cap), app_cap 402 (the app's weekly cap), rate_limited 429
197
+ * (30 calls a minute per member), model_failed 502, timeout 504
198
+ * (60 s, a schema retry included; a call the clock cuts is still
199
+ * charged), try_again 503, failed 500, no_member_identity 401,
200
+ * not_a_member 403;
201
+ * for the developer (`error` is not for the member) —
202
+ * bad_input 400, too_large 413 (the body over 128 KB, or a field
203
+ * over its cap), schema_mismatch 422, cross_origin 403,
204
+ * not_configured 503
205
+ * { ok: false, unavailable: true } a claw from before this door (a 404
206
+ * that names no refusal) or no network: render a quiet "not
207
+ * connected yet", as for every door
208
+ *
209
+ * Nothing throws. An empty input asks nothing of the platform.
210
+ */
211
+ export async function llm(input, opts) {
212
+ const { system, schema, maxTokens, tier, task, signal } = opts ?? {};
213
+ const said = typeof input === "string" ? input.trim() !== "" : Array.isArray(input) && input.length > 0;
214
+ if (!said) return refused("nothing to send");
215
+ const body = {
216
+ input,
217
+ system: typeof system === "string" && system ? system : undefined,
218
+ schema: schema && typeof schema === "object" ? schema : undefined,
219
+ max_tokens: typeof maxTokens === "number" ? maxTokens : undefined,
220
+ tier: tier || undefined,
221
+ task: task || undefined,
222
+ };
223
+ // claw() would read a body it cannot serialise as "not reachable".
224
+ try {
225
+ JSON.stringify(body);
226
+ } catch {
227
+ return refused("that cannot be sent as JSON");
228
+ }
229
+ const r = await claw("llm", { method: "POST", body, signal });
230
+ if (!r.ok) return r;
231
+ const answer = r.data;
232
+ if (answer && typeof answer === "object" && !Array.isArray(answer) && typeof answer.ok === "boolean") return answer;
233
+ return { ok: false, status: r.status, error: "the platform answered something unexpected", unavailable: false };
234
+ }
235
+
236
+ /** What llm() resolves for an input it will not send: the platform's own
237
+ * word for it, with no request made. */
238
+ function refused(error) {
239
+ return { ok: false, status: 0, error, code: "bad_input", unavailable: false };
240
+ }
241
+
153
242
  export { reportError };
154
243
 
155
- export const platform = { me, ask, requests, stopRequest, openRequest, surfaceOfPath, startJob, cancelJob, reportError };
244
+ export const platform = { me, ask, llm, requests, stopRequest, openRequest, surfaceOfPath, startJob, cancelJob, reportError };
156
245
 
157
246
  export default platform;
@@ -1,3 +1,22 @@
1
+ import { readFileSync } from "node:fs";
2
+
3
+ // The app's mode, from its manifest (makersclaw.json, beside this file).
4
+ // A browser app builds to files (output: "export"): the platform serves
5
+ // them, and its data door answers their reads and writes. A server app
6
+ // runs `next start`. Absent or unreadable is a server app, as every app
7
+ // before 0.7.0 is (none has a `mode`); only the exact word "browser"
8
+ // makes an export. `makersclaw mode` is what changes the key, never an
9
+ // edit here.
10
+ function mode() {
11
+ try {
12
+ const m = JSON.parse(readFileSync(new URL("./makersclaw.json", import.meta.url), "utf8"));
13
+ return m && m.mode === "browser" ? "browser" : "server";
14
+ } catch {
15
+ return "server";
16
+ }
17
+ }
18
+ const BROWSER = mode() === "browser";
19
+
1
20
  // The dashboard reaches this app at /p/<label>/ through the workspace proxy,
2
21
  // so every link and asset must carry that prefix. product_run sets
3
22
  // CLAW_BASE_PATH at build AND start time; leave it unset to run at /.
@@ -17,7 +36,8 @@
17
36
  // (src/lib/products.ts productAllowedOrigins): the product's own FQDN in
18
37
  // production, the dashboard's dev origin under a path prefix. Hardcoding a
19
38
  // localhost origin here ships an app that works in dev and refuses every
20
- // write in production.
39
+ // write in production. A browser app has no Server Actions, so it takes
40
+ // none: its writes go to the platform's data door (lib/door.js).
21
41
  const allowedOrigins = (process.env.CLAW_ALLOWED_ORIGINS || "")
22
42
  .split(",")
23
43
  .map((o) => o.trim())
@@ -28,6 +48,8 @@ const allowedOrigins = (process.env.CLAW_ALLOWED_ORIGINS || "")
28
48
  // lookups) whose Origin host is not localhost or listed here; Next 15 only
29
49
  // warned. The preview's browser is on the product host, so without this
30
50
  // every such request answers 403. Hostnames only: Next compares no port.
51
+ // Both modes: a browser app still previews on `next dev` while it is
52
+ // built, so CLAW_ALLOWED_ORIGINS stays its preview's rule.
31
53
  const allowedDevOrigins = [
32
54
  ...new Set(
33
55
  allowedOrigins
@@ -42,6 +64,12 @@ const allowedDevOrigins = [
42
64
  // the production build the platform made, serves and reads as its proof
43
65
  // (.next/BUILD_ID). check/run.mjs says more.
44
66
  //
67
+ // Under output: "export" a custom distDir is where the export goes, and
68
+ // the build then works in .next anyway (Next 16, build/index.js). So a
69
+ // browser app is built with the default: the build in .next, the files
70
+ // in out/, which is what the platform publishes. Its check builds in a
71
+ // copy of the app, never in the tree.
72
+ //
45
73
  // CLAW_TURBOPACK_ROOT is set by `npm run check` alone, when it builds a
46
74
  // copy of an app folder it cannot write (check/run.mjs buildSite). The
47
75
  // copy's node_modules is a symlink back to the app's, and Turbopack (Next
@@ -65,6 +93,11 @@ const turbopackRoot = process.env.CLAW_TURBOPACK_ROOT || "";
65
93
  // turns off, the dev overlay's /__nextjs_attach-nodejs-inspector (it
66
94
  // opens a Node debugger on loopback); only the platform can wall that
67
95
  // (CHANGELOG, 0.6.0).
96
+ //
97
+ // Browser mode adds three: output "export" (every page a file); images
98
+ // unoptimized, a guard, since the export has no image server (nothing in
99
+ // the template uses next/image); and trailingSlash false, so a page is
100
+ // s/<key>.html, the file the platform's router looks for.
68
101
  const nextConfig = {
69
102
  basePath: process.env.CLAW_BASE_PATH || "",
70
103
  distDir: process.env.CLAW_DIST_DIR || ".next",
@@ -72,11 +105,12 @@ const nextConfig = {
72
105
  CLAW_BASE_PATH: process.env.CLAW_BASE_PATH || "",
73
106
  },
74
107
  agentRules: false,
108
+ ...(BROWSER ? { output: "export", images: { unoptimized: true }, trailingSlash: false } : {}),
75
109
  ...(allowedDevOrigins.length > 0 ? { allowedDevOrigins } : {}),
76
110
  ...(turbopackRoot ? { turbopack: { root: turbopackRoot }, outputFileTracingRoot: turbopackRoot } : {}),
77
111
  experimental: {
78
112
  mcpServer: false,
79
- ...(allowedOrigins.length > 0 ? { serverActions: { allowedOrigins } } : {}),
113
+ ...(!BROWSER && allowedOrigins.length > 0 ? { serverActions: { allowedOrigins } } : {}),
80
114
  },
81
115
  };
82
116
 
@@ -7,7 +7,8 @@
7
7
  * label what the nav shows
8
8
  * kind optional, free text, one or two words for the checklist
9
9
  * ("board", "inbox", "report") — not an enum, nothing keys on it
10
- * source the table the surface reads, when it reads one
10
+ * source the table the surface reads, when it reads one: its bare name
11
+ * ("investors"), which the app's own schema answers
11
12
  * home true on the one surface / opens; otherwise the first entry
12
13
  * component the surface's component. LEAVE IT OUT while the surface is
13
14
  * still planned: the nav shows it as planned and its page says
@@ -27,7 +28,7 @@
27
28
  // A domain surface, once built, looks like this:
28
29
  //
29
30
  // import Investors from "./surfaces/investors";
30
- // { key: "investors", label: "Investors", kind: "board", source: "public.investors", home: true, component: Investors },
31
+ // { key: "investors", label: "Investors", kind: "board", source: "investors", home: true, component: Investors },
31
32
 
32
33
  const surfaces = [];
33
34
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "next",
3
3
  "title": "Makersclaw app (Next.js 16)",
4
- "description": "The behaviour every app shares, the parts every app is assembled from, and none of the app's own look — the colours are the workspace's. It carries the data layer (lib/data.js, lib/db.js), the write actions that log activity (lib/actions.js), the _claw platform client with its identity wrapper, the ask bridge (a screen's ask becomes a request, answered in the desk's agent column) and the hooks that follow requests (lib/claw.js, lib/platform.js, lib/hooks.js), routing (/s/<key>), the request proxy (proxy.js), error boundaries, Tailwind v4 mapped to the workspace theme (app/globals.css + _claw/theme.css), the kit (components/ui), the parts bound to the data layer and to the live requests (components/parts) and `npm run check`. The app designs the inside of the App tab on top: app/shell.jsx and every domain surface, assembled from the kit and the blocks with tokens only.",
4
+ "description": "The behaviour every app shares, the parts every app is assembled from, and none of the app's own look — the colours are the workspace's. It carries the data layer (lib/data.js, lib/db.js), the write actions that log activity (lib/actions.js), the company brain through the platform's data door (lib/brain.js from the browser, lib/brain-server.js for server reads), the app's own tables through the same door from the browser (lib/door.js and its hooks; a browser app's lib/data.js and lib/actions.js re-export it), the _claw platform client with its identity wrapper, the ask bridge (a screen's ask becomes a request, answered in the desk's agent column) and the hooks that follow requests (lib/claw.js, lib/platform.js, lib/hooks.js), routing (/s/<key>), the request proxy (proxy.js), error boundaries, Tailwind v4 mapped to the workspace theme (app/globals.css + _claw/theme.css), the kit (components/ui), the parts bound to the data layer and to the live requests (components/parts) and `npm run check`. The app designs the inside of the App tab on top: app/shell.jsx and every domain surface, assembled from the kit and the blocks with tokens only.",
5
5
  "files": {
6
6
  "fixed": [
7
7
  "app/globals.css",
@@ -24,11 +24,18 @@
24
24
  "next.config.mjs",
25
25
  "postcss.config.mjs"
26
26
  ],
27
- "docs": ["README.md", "surfaces/README.md", "schema.sql"],
27
+ "docs": ["README.md", "surfaces/README.md"],
28
28
  "starter": ["app/shell.jsx", "surfaces.js"],
29
29
  "merged": ["package.json"],
30
30
  "retired": ["middleware.js"]
31
31
  },
32
+ "modes": {
33
+ "browser": {
34
+ "overlay": "browser",
35
+ "drop": ["proxy.js", "app/claw-alive/route.js", "lib/db.js", "lib/brain-server.js"],
36
+ "dropScripts": ["start"]
37
+ }
38
+ },
32
39
  "components": [
33
40
  {
34
41
  "name": "button",
@@ -194,7 +201,7 @@
194
201
  "name": "item",
195
202
  "type": "component",
196
203
  "title": "Item, ItemGroup",
197
- "description": "The list row: media (an avatar, an image) or an icon, title, description, meta on the right, actions; href makes the whole row a link, onClick a button, neither static — actions still click on their own. aria-current, onClick, target and rel go on the row's link or button (aria-current fills the row); other props on the row. size default (56px) · sm (44px) · lg (64px); variant plain · outline · muted; layout row · stacked (a card for a grid: icon or media on top, title, description, meta at the foot, actions top-right, full cell height). ItemGroup stacks them with a rule between each (variant plain · outline).",
204
+ "description": "The list row: media (an avatar, an image) or an icon, title, description, meta on the right, actions; href makes the whole row a link, onClick a button, neither a plain row — actions still click on their own. aria-current, onClick, target and rel go on the row's link or button (aria-current fills the row); other props on the row. size default (56px) · sm (44px) · lg (64px); variant plain · outline · muted; layout row · stacked (a card for a grid: icon or media on top, title, description, meta at the foot, actions top-right, full cell height). ItemGroup stacks them with a rule between each (variant plain · outline).",
198
205
  "tags": ["item", "list", "row", "list item", "contact", "file", "settings row"],
199
206
  "files": ["components/ui/item.jsx"]
200
207
  },
@@ -326,6 +333,14 @@
326
333
  "tags": ["scroll", "scroll area", "overflow", "list", "carousel", "rail"],
327
334
  "files": ["components/ui/scroll-area.jsx"]
328
335
  },
336
+ {
337
+ "name": "layout",
338
+ "type": "component",
339
+ "title": "AppLayout, DocumentLayout",
340
+ "description": "Who owns the window's height, and who scrolls. AppLayout is the app frame, the one viewport owner (the outermost; one inside another fills its slot instead): header (the top bar, in view), navigation (a rail from md, navigationWidth px, 176; below md a Sheet behind the menu button, navigationLabel), the content (contentMode scroll: its region scrolls · fill: a bounded column for a canvas or a grid that scrolls itself; contentLabel, contentClassName for spacing, scrollKey = the route to scroll back to the top), inspector (beside the content from inspectorAt md · lg · xl, inspectorWidth px, 360, 240 to 560; below it the content and the inspector share one scroll in scroll mode, and in fill mode the inspector is a bounded pane under the content), footer (under the content, in view), height viewport · parent. In print it lets go. DocumentLayout is a page that flows, for print and export: toolbar (held at the top, never printed), width prose · page (A4) · letter · full, paper, align start · center. Nothing in an app sizes itself by the window: npm run check refuses min-h-dvh, h-screen, 100vh and h-[80vh] outside the kit.",
341
+ "tags": ["layout", "app frame", "shell", "viewport", "scroll", "inspector", "sidebar", "navigation", "footer", "fill", "canvas", "document", "print", "export", "page"],
342
+ "files": ["components/ui/layout.jsx"]
343
+ },
329
344
  {
330
345
  "name": "toast",
331
346
  "type": "component",
@@ -1,22 +0,0 @@
1
- -- Reference only. artifact_scaffold creates this table in the workspace
2
- -- database when it copies the template; nothing in the app runs this file.
3
- -- (docs/ARTIFACT-CONTRACTS.md §4)
4
- --
5
- -- Every write the app makes (lib/actions.js → lib/db.js logActivity) and
6
- -- every action the artifact's agent takes (its brief tells it to insert
7
- -- here with the sql verb) lands in this table, keyed by the artifact id
8
- -- product_run passes as CLAW_ARTIFACT_ID. The Activity tab the OS draws on
9
- -- the app window reads it (§9b), and so can any surface via loadActivity.
10
-
11
- create table if not exists public.artifact_activity (
12
- id bigserial primary key,
13
- artifact_id uuid not null,
14
- at timestamptz not null default now(),
15
- action text not null, -- found, drafted, sent, replied, skipped, edited…
16
- surface text, -- surface key
17
- row_ref text, -- id in the surface's source table
18
- detail text, -- one sentence
19
- data jsonb not null default '{}'::jsonb,
20
- by text not null default 'agent' -- 'agent' | 'user:<email>' | 'run:<id>'
21
- );
22
- create index if not exists artifact_activity_artifact_at on public.artifact_activity (artifact_id, at desc);