@north-light/crouter 0.3.198 → 0.3.199
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.
- package/dist/api/client.d.ts +18 -18
- package/dist/api/client.js +29 -37
- package/dist/api/dto/broker-ops.d.ts +3 -1
- package/dist/api/dto/broker.d.ts +13 -2
- package/dist/api/dto/broker.js +4 -4
- package/dist/api/dto/human.d.ts +6 -5
- package/dist/api/dto/inbox.d.ts +81 -34
- package/dist/api/dto/nodes.d.ts +8 -2
- package/dist/api/dto/profiles.d.ts +23 -0
- package/dist/api/dto/profiles.js +2 -2
- package/dist/api/dto/worktree.d.ts +3 -5
- package/dist/api/routes.d.ts +3 -5
- package/dist/api/routes.js +4 -6
- package/dist/builtin-memory/00-runtime-base.md +6 -6
- package/dist/builtin-memory/01-spine/01-no-manager.md +1 -1
- package/dist/builtin-memory/02-lifecycle/00-terminal.md +3 -3
- package/dist/builtin-memory/04-orchestration-kernel.md +6 -6
- package/dist/builtin-memory/05-kinds/design/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +2 -2
- package/dist/builtin-memory/05-kinds/review/00-base.md +2 -2
- package/dist/builtin-memory/05-kinds/review/01-orchestrator.md +1 -1
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +1 -1
- package/dist/builtin-memory/insights/capture.md +2 -2
- package/dist/builtin-memory/insights/init.md +3 -3
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/memory-loading.md +1 -1
- package/dist/builtin-memory/internal/nodes-and-canvas.md +2 -2
- package/dist/builtin-memory/internal/plugins.md +59 -2
- package/dist/builtin-memory/internal/storage-tiers.md +3 -1
- package/dist/builtin-memory/spec/roadmap.md +2 -2
- package/dist/builtin-memory/testing.md +1 -1
- package/dist/clients/attach/chrome/bash-jobs.js +3 -3
- package/dist/clients/attach/render/chat-view.d.ts +26 -1
- package/dist/clients/attach/render/chat-view.js +61 -11
- package/dist/clients/attach/render/page-block.d.ts +34 -0
- package/dist/clients/attach/render/page-block.js +226 -0
- package/dist/clients/attach/session/whip.d.ts +2 -0
- package/dist/clients/attach/session/whip.js +2 -0
- package/dist/clients/attach/viewer.js +1419 -993
- package/dist/clients/conversation/projection.js +18 -3
- package/dist/clients/inbox/__tests__/serial/inbox-controller.test.js +9 -6
- package/dist/clients/inbox/__tests__/serial/mount-panel.test.js +13 -21
- package/dist/clients/inbox/controller.js +10 -16
- package/dist/clients/inbox/page-adapter.d.ts +1 -3
- package/dist/clients/inbox/page-adapter.js +3 -4
- package/dist/clients/inbox/resolve.d.ts +1 -1
- package/dist/clients/inbox/resolve.js +3 -3
- package/dist/clients/inbox/review/document-surface.js +3 -1
- package/dist/clients/inbox/review/state.d.ts +2 -1
- package/dist/clients/inbox/review/state.js +5 -16
- package/dist/clients/inbox/tui/input.js +37 -19
- package/dist/clients/inbox/tui/panel.js +5 -31
- package/dist/clients/inbox/tui/render.d.ts +11 -0
- package/dist/clients/inbox/tui/render.js +71 -27
- package/dist/clients/inbox/tui/slots.d.ts +3 -1
- package/dist/clients/inbox/tui/slots.js +16 -12
- package/dist/clients/inbox/tui/types.d.ts +2 -7
- package/dist/clients/inbox/tui.js +2 -2
- package/dist/commands/__tests__/human.test.js +71 -114
- package/dist/commands/__tests__/node-message.test.js +14 -0
- package/dist/commands/attention.js +4 -4
- package/dist/commands/canvas-snapshot.js +1 -1
- package/dist/commands/canvas.js +1 -1
- package/dist/commands/dashboard.js +1 -1
- package/dist/commands/human/{html.d.ts → components.d.ts} +1 -1
- package/dist/commands/human/components.js +77 -0
- package/dist/commands/human/feedback.d.ts +2 -0
- package/dist/commands/human/feedback.js +108 -0
- package/dist/commands/human/prompts.d.ts +13 -5
- package/dist/commands/human/prompts.js +160 -97
- package/dist/commands/human/queue.js +57 -88
- package/dist/commands/human/shared.d.ts +2 -12
- package/dist/commands/human/shared.js +17 -11
- package/dist/commands/human.js +29 -21
- package/dist/commands/memory/origin.js +2 -2
- package/dist/commands/node/bash.js +4 -4
- package/dist/commands/node/inspect.js +3 -3
- package/dist/commands/node/lifecycle.js +1 -1
- package/dist/commands/node/message.js +2 -2
- package/dist/commands/node-worktree.js +8 -8
- package/dist/commands/pkg/browse.js +1 -1
- package/dist/commands/pkg/plugin-manage.js +129 -10
- package/dist/commands/profile/delete.js +41 -15
- package/dist/commands/profile.js +1 -1
- package/dist/commands/push.js +1 -1
- package/dist/commands/surface/node/focus.js +29 -14
- package/dist/commands/surface-edit.js +3 -3
- package/dist/commands/surface-inbox.d.ts +2 -0
- package/dist/commands/{human/inbox.js → surface-inbox.js} +14 -18
- package/dist/commands/surface.js +2 -1
- package/dist/commands/sys/doctor.js +60 -3
- package/dist/commands/sys/feedback.js +79 -25
- package/dist/commands/sys/settings.js +1 -1
- package/dist/core/__tests__/cron-broker-capacity.test.js +1 -1
- package/dist/core/__tests__/extension-abort.test.js +5 -2
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +13 -2
- package/dist/core/__tests__/fixtures/fake-engine.js +47 -3
- package/dist/core/__tests__/human-cancel-guard.test.js +31 -10
- package/dist/core/__tests__/human-deliver.test.js +31 -26
- package/dist/core/__tests__/human-node-not-supervised.test.js +1 -1
- package/dist/core/__tests__/plugin-page-components.test.js +157 -0
- package/dist/core/__tests__/seam/dormancy-release.test.js +40 -0
- package/dist/core/__tests__/serial/flagship-lifecycle.test.js +2 -2
- package/dist/core/__tests__/serial/human-deliver-e2e.test.js +12 -6
- package/dist/core/__tests__/serial/live-mutation.test.js +1 -1
- package/dist/core/__tests__/serial/tmux-surface.test.js +4 -2
- package/dist/core/__tests__/serial/worktree.test.js +24 -198
- package/dist/core/__tests__/session-cycles.test.js +22 -0
- package/dist/core/__tests__/stop-guard.test.js +4 -4
- package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +19 -4
- package/dist/core/bash-jobs.d.ts +6 -0
- package/dist/core/bash-jobs.js +10 -0
- package/dist/core/canvas/__tests__/attention.test.js +8 -5
- package/dist/core/canvas/__tests__/render-remote.test.js +5 -5
- package/dist/core/canvas/attention.js +8 -6
- package/dist/core/canvas/browse/model.d.ts +8 -2
- package/dist/core/canvas/browse/model.js +11 -4
- package/dist/core/canvas/canvas.d.ts +7 -0
- package/dist/core/canvas/canvas.js +37 -0
- package/dist/core/canvas/crons.d.ts +9 -0
- package/dist/core/canvas/crons.js +51 -0
- package/dist/core/canvas/node-order.d.ts +22 -1
- package/dist/core/canvas/node-order.js +26 -1
- package/dist/core/canvas/render-source.d.ts +5 -0
- package/dist/core/canvas/render-source.js +1 -0
- package/dist/core/canvas/types.d.ts +9 -3
- package/dist/core/command-plugins/bundle.d.ts +6 -1
- package/dist/core/command-plugins/bundle.js +26 -13
- package/dist/core/command.js +2 -1
- package/dist/core/config.d.ts +47 -1
- package/dist/core/config.js +137 -4
- package/dist/core/help.js +1 -1
- package/dist/core/human/__tests__/page-catalog.test.js +4 -0
- package/dist/core/human/__tests__/page-tickets.test.js +65 -52
- package/dist/core/human/__tests__/page.test.js +56 -98
- package/dist/core/human/__tests__/serial/inbox-core.test.js +6 -6
- package/dist/core/human/answer-text.js +3 -3
- package/dist/core/human/answer.d.ts +6 -4
- package/dist/core/human/answer.js +4 -10
- package/dist/core/human/component-docs.d.ts +11 -7
- package/dist/core/human/component-docs.js +361 -88
- package/dist/core/human/convention.d.ts +17 -3
- package/dist/core/human/convention.js +36 -5
- package/dist/core/human/feedback-companion.d.ts +20 -0
- package/dist/core/human/feedback-companion.js +98 -0
- package/dist/core/human/feedback.d.ts +65 -0
- package/dist/core/human/feedback.js +116 -0
- package/dist/core/human/page-catalog.d.ts +40 -5
- package/dist/core/human/page-catalog.js +102 -30
- package/dist/core/human/page-errors.d.ts +4 -0
- package/dist/core/human/page-errors.js +7 -0
- package/dist/core/human/page-eval.d.ts +20 -0
- package/dist/core/human/page-eval.js +123 -0
- package/dist/core/human/page-schema.d.ts +23 -191
- package/dist/core/human/page-schema.js +60 -89
- package/dist/core/human/page-synth.d.ts +1 -1
- package/dist/core/human/page-synth.js +15 -18
- package/dist/core/human/page.d.ts +12 -27
- package/dist/core/human/page.js +142 -474
- package/dist/core/human/root.d.ts +6 -2
- package/dist/core/human/root.js +12 -4
- package/dist/core/human/scan.d.ts +3 -4
- package/dist/core/human/scan.js +16 -15
- package/dist/core/human/summary.d.ts +1 -2
- package/dist/core/human/summary.js +2 -2
- package/dist/core/human/tickets.d.ts +20 -14
- package/dist/core/human/tickets.js +91 -31
- package/dist/core/human/types.d.ts +10 -2
- package/dist/core/preview-registry.js +8 -10
- package/dist/core/profiles/default-binding.d.ts +4 -0
- package/dist/core/profiles/default-binding.js +24 -0
- package/dist/core/profiles/deletion-reservation.d.ts +8 -0
- package/dist/core/profiles/deletion-reservation.js +38 -0
- package/dist/core/profiles/manifest.d.ts +3 -0
- package/dist/core/profiles/manifest.js +12 -0
- package/dist/core/profiles/select.js +2 -2
- package/dist/core/review/realize.js +3 -3
- package/dist/core/review/store.d.ts +6 -0
- package/dist/core/review/store.js +31 -0
- package/dist/core/runtime/bearings.d.ts +0 -4
- package/dist/core/runtime/bearings.js +4 -62
- package/dist/core/runtime/bin-contributions.d.ts +44 -0
- package/dist/core/runtime/bin-contributions.js +186 -0
- package/dist/core/runtime/broker/client-registry.d.ts +4 -0
- package/dist/core/runtime/broker/client-registry.js +13 -1
- package/dist/core/runtime/broker/extension-abort.d.ts +1 -1
- package/dist/core/runtime/broker/extension-abort.js +4 -2
- package/dist/core/runtime/broker/fault-retry.js +4 -0
- package/dist/core/runtime/broker/frame-dispatch.d.ts +3 -1
- package/dist/core/runtime/broker/frame-dispatch.js +5 -6
- package/dist/core/runtime/broker/read-ops.d.ts +1 -1
- package/dist/core/runtime/broker/read-ops.js +2 -2
- package/dist/core/runtime/broker/rebind.d.ts +1 -0
- package/dist/core/runtime/broker/rebind.js +6 -1
- package/dist/core/runtime/broker-extension-render.js +19 -0
- package/dist/core/runtime/broker-protocol.d.ts +14 -0
- package/dist/core/runtime/broker.d.ts +1 -1
- package/dist/core/runtime/broker.js +17 -6
- package/dist/core/runtime/front-door-env.d.ts +5 -0
- package/dist/core/runtime/front-door-env.js +16 -0
- package/dist/core/runtime/front-door.d.ts +1 -5
- package/dist/core/runtime/front-door.js +2 -5
- package/dist/core/runtime/host.d.ts +1 -1
- package/dist/core/runtime/invocation.d.ts +6 -0
- package/dist/core/runtime/invocation.js +10 -0
- package/dist/core/runtime/kickoff.js +34 -0
- package/dist/core/runtime/launch.d.ts +2 -6
- package/dist/core/runtime/node-read.js +4 -1
- package/dist/core/runtime/nodes.js +2 -0
- package/dist/core/runtime/promote.d.ts +1 -0
- package/dist/core/runtime/promote.js +3 -1
- package/dist/core/runtime/session-cycles.d.ts +6 -0
- package/dist/core/runtime/session-cycles.js +15 -1
- package/dist/core/runtime/session-visibility.d.ts +17 -8
- package/dist/core/runtime/session-visibility.js +19 -10
- package/dist/core/runtime/spawn-env.d.ts +9 -1
- package/dist/core/runtime/spawn-env.js +62 -2
- package/dist/core/runtime/stop-guard.d.ts +2 -2
- package/dist/core/runtime/stop-guard.js +2 -2
- package/dist/core/runtime/stop-signals.d.ts +1 -1
- package/dist/core/runtime/stop-signals.js +2 -2
- package/dist/core/runtime/tmux-bindings.js +1 -1
- package/dist/core/session-model/session-state.d.ts +4 -3
- package/dist/core/session-model/session-state.js +14 -1
- package/dist/core/user-settings.d.ts +21 -2
- package/dist/core/user-settings.js +32 -11
- package/dist/core/worktree.d.ts +15 -45
- package/dist/core/worktree.js +59 -261
- package/dist/daemon/api/__tests__/seam/profile-delete.test.d.ts +1 -0
- package/dist/daemon/api/__tests__/seam/profile-delete.test.js +386 -0
- package/dist/daemon/api/handlers/bash-jobs.js +1 -1
- package/dist/daemon/api/handlers/broker-ops.js +9 -5
- package/dist/daemon/api/handlers/feedback-comments.d.ts +2 -0
- package/dist/daemon/api/handlers/feedback-comments.js +207 -0
- package/dist/daemon/api/handlers/human.js +24 -20
- package/dist/daemon/api/handlers/inbox.d.ts +10 -0
- package/dist/daemon/api/handlers/inbox.js +39 -115
- package/dist/daemon/api/handlers/profiles.js +11 -29
- package/dist/daemon/api/map.d.ts +6 -1
- package/dist/daemon/api/map.js +23 -0
- package/dist/daemon/api/server.js +3 -1
- package/dist/daemon/companion-retire.d.ts +2 -0
- package/dist/daemon/companion-retire.js +33 -0
- package/dist/daemon/cron-run.js +14 -16
- package/dist/daemon/human/finish.d.ts +12 -7
- package/dist/daemon/human/finish.js +101 -38
- package/dist/daemon/human/sweep.js +5 -1
- package/dist/daemon/profile-delete.d.ts +7 -0
- package/dist/daemon/profile-delete.js +306 -0
- package/dist/daemon/reconcilers/broker-supervision.js +3 -1
- package/dist/daemon/review/deliver.js +3 -3
- package/dist/daemon/review/finish.d.ts +0 -2
- package/dist/daemon/review/finish.js +1 -29
- package/dist/pi-extensions/__tests__/canvas-bash-valve.test.js +56 -1
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +4 -4
- package/dist/pi-extensions/canvas-bash-valve.js +19 -5
- package/dist/pi-extensions/canvas-inbox-watcher.js +1 -1
- package/dist/pi-extensions/canvas-review-boundary.d.ts +4 -0
- package/dist/pi-extensions/canvas-review-boundary.js +18 -9
- package/dist/pi-extensions/canvas-stophook.js +4 -4
- package/dist/shared/generated-context.js +2 -2
- package/dist/types.d.ts +44 -2
- package/dist/types.js +7 -2
- package/package.json +4 -4
- package/runtime.lock.json +5 -33
- package/dist/builtin-memory/init.md +0 -38
- package/dist/builtin-memory/plan.md +0 -19
- package/dist/builtin-memory/spec.md +0 -19
- package/dist/commands/human/doc.d.ts +0 -2
- package/dist/commands/human/doc.js +0 -91
- package/dist/commands/human/html.js +0 -81
- package/dist/commands/human/inbox.d.ts +0 -2
- package/dist/core/human/__tests__/html-markdown.test.js +0 -52
- package/dist/core/human/__tests__/page-render.test.js +0 -66
- package/dist/core/human/html-markdown.d.ts +0 -3
- package/dist/core/human/html-markdown.js +0 -288
- package/dist/core/human/page-render.d.ts +0 -4
- package/dist/core/human/page-render.js +0 -105
- package/dist/pages/bundle.css +0 -1
- package/dist/pages/bundle.js +0 -1778
- package/dist/pages/comments.d.ts +0 -72
- package/dist/pages/comments.js +0 -176
- package/dist/pages/controls.d.ts +0 -98
- package/dist/pages/controls.js +0 -261
- package/dist/pages/elements/cards.d.ts +0 -28
- package/dist/pages/elements/cards.js +0 -915
- package/dist/pages/elements/chart.d.ts +0 -23
- package/dist/pages/elements/chart.js +0 -664
- package/dist/pages/elements/options.d.ts +0 -30
- package/dist/pages/elements/options.js +0 -742
- package/dist/pages/elements/pages.d.ts +0 -30
- package/dist/pages/elements/pages.js +0 -377
- package/dist/pages/elements/slot.d.ts +0 -26
- package/dist/pages/elements/slot.js +0 -112
- package/dist/pages/elements/table.d.ts +0 -36
- package/dist/pages/elements/table.js +0 -1043
- package/dist/pages/elements/text.d.ts +0 -31
- package/dist/pages/elements/text.js +0 -1175
- package/dist/pages/entry.d.ts +0 -8
- package/dist/pages/entry.js +0 -8
- package/dist/pages/host.d.ts +0 -134
- package/dist/pages/host.js +0 -165
- package/dist/pages/readonly.d.ts +0 -24
- package/dist/pages/readonly.js +0 -31
- package/dist/pages/register.d.ts +0 -2
- package/dist/pages/register.js +0 -3
- package/dist/pages/responses.d.ts +0 -37
- package/dist/pages/responses.js +0 -56
- package/dist/pages/slot-config.d.ts +0 -36
- package/dist/pages/slot-config.js +0 -50
- package/dist/pages/types.d.ts +0 -98
- package/dist/pages/types.js +0 -23
- /package/dist/{core/human/__tests__/html-markdown.test.d.ts → commands/__tests__/node-message.test.d.ts} +0 -0
- /package/dist/core/{human/__tests__/page-render.test.d.ts → __tests__/plugin-page-components.test.d.ts} +0 -0
|
@@ -1,161 +1,434 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { pageComponentCatalog } from './page-catalog.js';
|
|
2
|
+
/** A product registers a lowercase-hyphen kind; its JSX tag is the PascalCase derivation
|
|
3
|
+
* (`worker-card` → `<WorkerCard>`), which page-catalog carries as the entry `tag`. */
|
|
4
|
+
function productTags(components) {
|
|
5
|
+
return new Map(pageComponentCatalog(components).filter((entry) => !entry.builtin).map((entry) => [entry.kind, entry.tag]));
|
|
6
|
+
}
|
|
7
|
+
const SLOT_RULES = `Component contract
|
|
8
|
+
- Props are the component's config, spread flat as JSX attributes; the component registers itself with the host at mount and enforces its own response shape.
|
|
9
|
+
- \`id\` is required, must match \`^[A-Za-z0-9_-]{1,64}$\`, and must be unique across the page.
|
|
10
|
+
- When a page has two or more response-bearing components, every one needs a nonempty \`label\`.
|
|
11
|
+
- Config objects and every documented nested object reject unknown fields.
|
|
12
|
+
- The page is rendered once at submit to build the ticket manifest, so every prop reaches it whatever its shape — mapped data, a helper component, a value from \`useState\`. Event handlers are dropped, because a manifest is JSON. The user's answer lands in \`response.json\` keyed by this \`id\`.`;
|
|
13
|
+
const COMMENT_SHAPE = `Comments are \`{id:string (nonempty), text:string, anchor:Anchor}\` and qualify the answer itself. \`Anchor\` is \`{kind:"option",optionId:string}\` or \`{kind:"row",rowId:string}\`; each component accepts only the anchors named next. Feedback on rendered page content — including the question text — is given by highlighting it on the page, never authored into a response.`;
|
|
14
|
+
const DISPLAY_RULES = `Display-only: it is not recorded in the page manifest and contributes nothing to the response. Every display component also takes \`className\` for Tailwind utilities, and standard shadcn props otherwise.`;
|
|
7
15
|
export const BUILTIN_COMPONENT_DOCS = [
|
|
8
16
|
{
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
selectionLine: '
|
|
12
|
-
contract: `\`<
|
|
17
|
+
names: ['Page'],
|
|
18
|
+
group: 'structural',
|
|
19
|
+
selectionLine: '`Page` — the page root. Every module default-exports a component whose root element is `<Page>`; it carries title and subtitle.',
|
|
20
|
+
contract: `\`<Page>\`
|
|
21
|
+
|
|
22
|
+
Structural root. The \`.tsx\` module's default export returns exactly one \`<Page>\`; it takes no \`id\` and contributes no response.
|
|
13
23
|
|
|
14
|
-
|
|
24
|
+
Props
|
|
25
|
+
- \`title\` — required nonempty string. The headline the user sees in their inbox and in the terminal summary.
|
|
26
|
+
- \`subtitle?:string\` — one line under the title.
|
|
15
27
|
|
|
16
|
-
|
|
17
|
-
-
|
|
28
|
+
Rules
|
|
29
|
+
- Children are page content: registry components, plain elements with Tailwind classes, or \`<Step>\` elements for a multi-step page.
|
|
30
|
+
- Never render submit or dismiss chrome — the host owns it.
|
|
31
|
+
|
|
32
|
+
Example
|
|
33
|
+
\`\`\`jsx
|
|
34
|
+
export default function ShipGate() {
|
|
35
|
+
return (
|
|
36
|
+
<Page title="Ship 0.4.0?" subtitle="Two gates left.">
|
|
37
|
+
<UserQuestion id="lane" label="Release lane" body="The audit closes tomorrow." mode="single"
|
|
38
|
+
options={[{ id: 'now', label: 'Ship now' }, { id: 'hold', label: 'Hold for the audit' }]} />
|
|
39
|
+
</Page>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
\`\`\``,
|
|
18
43
|
},
|
|
19
44
|
{
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
selectionLine: '
|
|
23
|
-
contract: `\`<
|
|
45
|
+
names: ['Step'],
|
|
46
|
+
group: 'structural',
|
|
47
|
+
selectionLine: '`Step` — one step of a multi-step page. Use as a direct child of `<Page>` when the user works through content in sequence.',
|
|
48
|
+
contract: `\`<Step>\`
|
|
24
49
|
|
|
25
|
-
|
|
50
|
+
Structural. One step of a multi-step page. It takes no \`id\` and contributes no response.
|
|
26
51
|
|
|
27
|
-
|
|
28
|
-
-
|
|
52
|
+
Props
|
|
53
|
+
- \`title?:string\` — the step heading.
|
|
54
|
+
|
|
55
|
+
Rules
|
|
56
|
+
- \`<Step>\` elements are children of \`<Page>\`; step order is source order, and the ticket manifest records the step count.
|
|
57
|
+
- A single-step page needs no \`<Step>\` at all — put the content directly in \`<Page>\`.
|
|
58
|
+
|
|
59
|
+
Example
|
|
60
|
+
\`\`\`jsx
|
|
61
|
+
<Page title="Migration plan">
|
|
62
|
+
<Step title="What changes"><Card>…</Card></Step>
|
|
63
|
+
<Step title="Approve"><UserQuestion id="go" label="Proceed?" body="Applies on the next deploy." mode="single" options={lanes} /></Step>
|
|
64
|
+
</Page>
|
|
65
|
+
\`\`\``,
|
|
29
66
|
},
|
|
30
67
|
{
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
selectionLine: '
|
|
34
|
-
contract: `\`<
|
|
68
|
+
names: ['UserQuestion'],
|
|
69
|
+
group: 'slot',
|
|
70
|
+
selectionLine: '`UserQuestion` — known-alternative picker (wire kind `options`). Use when the user picks among known alternatives.',
|
|
71
|
+
contract: `\`<UserQuestion>\` — wire kind \`options\`
|
|
35
72
|
|
|
36
73
|
Response-bearing. The response is \`{selectedOptionIds:string[], comments:Comment[], freetext?:string}\`.
|
|
37
74
|
|
|
38
|
-
${
|
|
75
|
+
${SLOT_RULES}
|
|
39
76
|
|
|
40
|
-
|
|
77
|
+
Props
|
|
78
|
+
- \`id\` — required component id.
|
|
41
79
|
- \`options\` — required nonempty array of \`{id:string (nonempty, unique), label:string (nonempty), description?:string}\`.
|
|
42
80
|
- \`mode\` — required: \`single\` or \`multi\`.
|
|
43
|
-
- \`label
|
|
44
|
-
- \`
|
|
81
|
+
- \`label\` — required nonempty string: the question itself, drawn as the bold first line.
|
|
82
|
+
- \`body\` — required nonempty string: markdown rendered above the choices — the context under the question a label cannot carry. Display-only; contributes nothing to the response.
|
|
83
|
+
- \`allowFreetext?:boolean\`, \`freetextLabel?:string\`, \`freetextPlaceholder?:string\` — adds a final "something else" choice, drawn as one more row with its own control and a field to write in rather than as a note on the question. It is exclusive on a \`single\` question (choosing it clears the selection, and choosing an option clears what was written) and just another box on a \`multi\` one. \`freetextLabel\` names that row, default "Something else".
|
|
45
84
|
|
|
46
85
|
Response rules
|
|
47
86
|
- \`selectedOptionIds\` contains unique known option ids. A \`single\` response has at most one selected id and requires exactly one selection or nonempty allowed \`freetext\`; a \`multi\` response requires a selection, comment, or nonempty allowed \`freetext\`.
|
|
48
87
|
- \`freetext\` is accepted only when \`allowFreetext:true\`.
|
|
49
|
-
- ${COMMENT_SHAPE}
|
|
88
|
+
- ${COMMENT_SHAPE} UserQuestion accepts only \`option\` anchors; \`optionId\` must name a configured option.
|
|
89
|
+
|
|
90
|
+
Example
|
|
91
|
+
\`\`\`jsx
|
|
92
|
+
<UserQuestion id="lane" label="Release lane" body="The audit closes tomorrow." mode="single" allowFreetext
|
|
93
|
+
freetextLabel="Something else"
|
|
94
|
+
options={[
|
|
95
|
+
{ id: 'now', label: 'Ship now', description: 'Both gates are green.' },
|
|
96
|
+
{ id: 'hold', label: 'Hold for the audit' },
|
|
97
|
+
]} />
|
|
98
|
+
\`\`\``,
|
|
50
99
|
},
|
|
51
100
|
{
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
selectionLine: '
|
|
55
|
-
contract: `\`<
|
|
101
|
+
names: ['UserText'],
|
|
102
|
+
group: 'slot',
|
|
103
|
+
selectionLine: '`UserText` — writing surface (wire kind `text`). Use when you need prose back from the user — a value they write, or a draft they hand back revised; use `singleLine` for a subject, name, or URL. Prose the user only reads is a plain element, never this.',
|
|
104
|
+
contract: `\`<UserText>\` — wire kind \`text\`
|
|
105
|
+
|
|
106
|
+
Response-bearing. The response is \`{text:string, edited:boolean}\`: the text the user hands back, and whether they changed what you drafted.
|
|
56
107
|
|
|
57
|
-
|
|
108
|
+
It collects prose, it does not present it. Text the user only reads is a plain element with Tailwind classes — \`<p className="whitespace-pre-wrap">\` — which renders the same and asks nothing of them. Reach for it only when the answer you need is the string left in the field. Feedback on the rendered text is given by highlighting it on the page, not authored into the response.
|
|
58
109
|
|
|
59
|
-
${
|
|
110
|
+
${SLOT_RULES}
|
|
60
111
|
|
|
61
|
-
|
|
112
|
+
Props
|
|
113
|
+
- \`id\` — required component id.
|
|
62
114
|
- \`initialText\` — required string, including an empty string.
|
|
63
|
-
- \`label?:string\` —
|
|
64
|
-
- \`
|
|
65
|
-
- \`singleLine
|
|
115
|
+
- \`label?:string\` — the component's label; required by the multi-response page rule.
|
|
116
|
+
- \`placeholder?:string\` — the hint shown while the field is empty.
|
|
117
|
+
- \`singleLine?:boolean\` — swaps the writing surface for one compact form field.
|
|
66
118
|
|
|
67
|
-
|
|
68
|
-
|
|
119
|
+
The field is always writable; there is no read-only mode, and the config rejects an \`editable\` prop.
|
|
120
|
+
|
|
121
|
+
Example
|
|
122
|
+
\`\`\`jsx
|
|
123
|
+
<UserText id="subject" label="Subject line" singleLine initialText="Following up on the audit" />
|
|
124
|
+
\`\`\``,
|
|
69
125
|
},
|
|
70
126
|
{
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
selectionLine: '
|
|
74
|
-
contract: `\`<
|
|
127
|
+
names: ['UserTable'],
|
|
128
|
+
group: 'slot',
|
|
129
|
+
selectionLine: '`UserTable` — selectable rows and columns (wire kind `table`). Use when the user inspects or selects tabular data; for a table the user only reads, `Table` is lighter.',
|
|
130
|
+
contract: `\`<UserTable>\` — wire kind \`table\`
|
|
75
131
|
|
|
76
132
|
Display-only when both selection modes are \`none\`; response-bearing when \`rowSelect\` or \`columnSelect\` is \`single\` or \`multi\`. Its response when selectable is \`{selectedRowIds:string[], selectedColumnIds:string[], comments:Comment[]}\`.
|
|
77
133
|
|
|
78
|
-
${
|
|
134
|
+
${SLOT_RULES}
|
|
79
135
|
|
|
80
|
-
|
|
136
|
+
Props
|
|
137
|
+
- \`id\` — required component id.
|
|
81
138
|
- \`columns\` — required nonempty array of \`{id:string (nonempty, unique), label:string (nonempty), align?:"left"|"right", mono?:boolean}\`.
|
|
82
|
-
- \`rows?:\` array of \`{id:string (nonempty, unique), cells:Record<string,string|number|boolean|null>}\`.
|
|
83
|
-
- \`source?:string\` — path to a local JSON file containing the rows array. At submit it is read, validated as \`rows\`, and embedded; the stored ticket is self-contained and elements never see \`source\`. \`rows\` and \`source\` are mutually exclusive.
|
|
139
|
+
- \`rows?:\` array of \`{id:string (nonempty, unique), cells:Record<string,string|number|boolean|null>}\`. Rows are inline — map your data into this array in the module.
|
|
84
140
|
- \`rowSelect?:"none"|"single"|"multi"\` — defaults to \`none\`.
|
|
85
141
|
- \`columnSelect?:"none"|"single"|"multi"\` — defaults to \`none\`.
|
|
86
|
-
- \`label?:string\` —
|
|
142
|
+
- \`label?:string\` — the component's label; required by the multi-response page rule when the table is selectable.
|
|
143
|
+
- \`body?:string\` — optional markdown rendered above the table. Display-only; it contributes nothing to the response.
|
|
87
144
|
|
|
88
145
|
Response rules when selectable
|
|
89
146
|
- Selected row and column ids are unique and must name configured ids. \`none\` permits no corresponding selected ids; \`single\` permits at most one.
|
|
90
|
-
- ${COMMENT_SHAPE}
|
|
147
|
+
- ${COMMENT_SHAPE} UserTable accepts only \`row\` anchors, which must name configured row ids.
|
|
148
|
+
|
|
149
|
+
Example
|
|
150
|
+
\`\`\`jsx
|
|
151
|
+
<UserTable id="picks" label="Which findings do we fix?" rowSelect="multi"
|
|
152
|
+
columns={[{ id: 'finding', label: 'Finding' }, { id: 'severity', label: 'Severity' }]}
|
|
153
|
+
rows={findings.map((f) => ({ id: f.id, cells: { finding: f.title, severity: f.severity } }))} />
|
|
154
|
+
\`\`\``,
|
|
91
155
|
},
|
|
92
156
|
{
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
selectionLine: '
|
|
96
|
-
contract: `\`<
|
|
157
|
+
names: ['UserCards'],
|
|
158
|
+
group: 'slot',
|
|
159
|
+
selectionLine: '`UserCards` — visual item picker (wire kind `cards`). Use when the user chooses among rich, card-shaped alternatives.',
|
|
160
|
+
contract: `\`<UserCards>\` — wire kind \`cards\`
|
|
97
161
|
|
|
98
|
-
Response-bearing. The response is \`{selectedCardIds:string[],
|
|
162
|
+
Response-bearing. The response is \`{selectedCardIds:string[]}\`. Feedback on a card's content is given by highlighting it on the page, not authored into the response.
|
|
99
163
|
|
|
100
|
-
${
|
|
164
|
+
${SLOT_RULES}
|
|
101
165
|
|
|
102
|
-
|
|
103
|
-
- \`
|
|
104
|
-
- \`
|
|
166
|
+
Props
|
|
167
|
+
- \`id\` — required component id.
|
|
168
|
+
- \`cards?:\` array of \`{id:string (nonempty, unique), title:string (nonempty), subtitle?:string, body?:string, tag?:string}\`. Cards are inline — map your data into this array in the module.
|
|
105
169
|
- \`mode\` — required: \`single\` or \`multi\`.
|
|
106
170
|
- \`columns?:2|3\` — optional layout column count.
|
|
107
|
-
- \`label?:string\` —
|
|
171
|
+
- \`label?:string\` — the component's label; required by the multi-response page rule.
|
|
172
|
+
- \`body?:string\` — optional markdown rendered above the cards. Display-only; it contributes nothing to the response.
|
|
108
173
|
|
|
109
174
|
Response rules
|
|
110
175
|
- \`selectedCardIds\` contains unique configured card ids. \`single\` permits at most one selected card.
|
|
111
|
-
|
|
176
|
+
|
|
177
|
+
Example
|
|
178
|
+
\`\`\`jsx
|
|
179
|
+
<UserCards id="candidate" label="Who do we interview next?" mode="single" columns={3}
|
|
180
|
+
cards={[{ id: 'a', title: 'Dana Whitfield', subtitle: 'Staff eng', tag: 'referral' }]} />
|
|
181
|
+
\`\`\``,
|
|
112
182
|
},
|
|
113
183
|
{
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
selectionLine: '
|
|
117
|
-
contract: `\`<
|
|
184
|
+
names: ['Chart'],
|
|
185
|
+
group: 'slot',
|
|
186
|
+
selectionLine: '`Chart` — data visualization (wire kind `chart`, display-only). Use when a trend or comparison reads better than prose or a table.',
|
|
187
|
+
contract: `\`<Chart>\` — wire kind \`chart\`
|
|
118
188
|
|
|
119
|
-
Display-only; it has no response.
|
|
189
|
+
Display-only; it has no response. It still takes an \`id\` when you want the chart recorded in the ticket manifest.
|
|
120
190
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
Config JSON
|
|
191
|
+
Props
|
|
124
192
|
- \`kind\` — required: \`line\`, \`bar\`, or \`area\`.
|
|
125
193
|
- \`x\` — required nonempty string naming the x field.
|
|
126
194
|
- \`y\` — required nonempty array of \`{field:string (nonempty), label?:string}\` series.
|
|
127
|
-
- \`title?:string\`, \`data?:\` array of JSON row objects —
|
|
128
|
-
- \`
|
|
195
|
+
- \`title?:string\`, \`data?:\` array of JSON row objects. Rows are inline — build them in the module.
|
|
196
|
+
- \`id?:string\` — component id, subject to the same id rules as a response-bearing component when present.
|
|
197
|
+
|
|
198
|
+
Example
|
|
199
|
+
\`\`\`jsx
|
|
200
|
+
<Chart kind="line" title="Build minutes" x="day"
|
|
201
|
+
y={[{ field: 'minutes', label: 'CI minutes' }]}
|
|
202
|
+
data={days.map((d) => ({ day: d.label, minutes: d.minutes }))} />
|
|
203
|
+
\`\`\``,
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
names: ['Card', 'CardHeader', 'CardTitle', 'CardDescription', 'CardContent', 'CardFooter'],
|
|
207
|
+
group: 'display',
|
|
208
|
+
selectionLine: '`Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter` — the standard surface for grouping a block of page content.',
|
|
209
|
+
contract: `\`<Card>\` family — \`Card\`, \`CardHeader\`, \`CardTitle\`, \`CardDescription\`, \`CardContent\`, \`CardFooter\`
|
|
210
|
+
|
|
211
|
+
${DISPLAY_RULES}
|
|
212
|
+
|
|
213
|
+
Props
|
|
214
|
+
- All six take \`className\` and children only; the nesting carries the meaning.
|
|
215
|
+
- Order inside \`<Card>\`: \`<CardHeader>\` (holding \`<CardTitle>\` and optional \`<CardDescription>\`), then \`<CardContent>\`, then optional \`<CardFooter>\`.
|
|
216
|
+
|
|
217
|
+
Example
|
|
218
|
+
\`\`\`jsx
|
|
219
|
+
<Card>
|
|
220
|
+
<CardHeader><CardTitle>Release gates</CardTitle><CardDescription>Two left.</CardDescription></CardHeader>
|
|
221
|
+
<CardContent><Chart kind="bar" x="gate" y={[{ field: 'minutes' }]} data={gates} /></CardContent>
|
|
222
|
+
</Card>
|
|
223
|
+
\`\`\``,
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
names: ['Badge'],
|
|
227
|
+
group: 'display',
|
|
228
|
+
selectionLine: '`Badge` — small status or category chip.',
|
|
229
|
+
contract: `\`<Badge>\`
|
|
230
|
+
|
|
231
|
+
${DISPLAY_RULES}
|
|
232
|
+
|
|
233
|
+
Props
|
|
234
|
+
- \`variant?:"default"|"secondary"|"destructive"|"outline"\`.
|
|
235
|
+
- \`className\`, children.
|
|
236
|
+
|
|
237
|
+
Example
|
|
238
|
+
\`\`\`jsx
|
|
239
|
+
<Badge variant="destructive">blocked</Badge>
|
|
240
|
+
\`\`\``,
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
names: ['Button'],
|
|
244
|
+
group: 'display',
|
|
245
|
+
selectionLine: '`Button` — an in-page control. Never a submit: the host owns submit and dismiss.',
|
|
246
|
+
contract: `\`<Button>\`
|
|
247
|
+
|
|
248
|
+
${DISPLAY_RULES} A page never renders its own submit or dismiss — use it for in-page interaction only (revealing detail, switching a local view).
|
|
249
|
+
|
|
250
|
+
Props — this Button does NOT take shadcn's \`variant\`; a \`variant\` prop is silently ignored.
|
|
251
|
+
- \`tone?:"brand"|"neutral"|"danger"\` — what the action means. Unstated, it resolves to \`brand\` on a solid button and \`neutral\` at every other weight.
|
|
252
|
+
- \`weight?:"solid"|"outline"|"quiet"|"link"\` — how loudly it reads. Default \`solid\`. \`tone="danger"\` has no \`link\` weight — that pair warns and renders untoned.
|
|
253
|
+
- \`size?:"xs"|"sm"|"default"|"lg"\` (icon-only circles: \`"icon-xs"|"icon-sm"|"icon"|"icon-lg"\`). Default \`default\`.
|
|
254
|
+
- \`width?:"auto"|"full"\`. Default \`auto\`.
|
|
255
|
+
- \`loading?:boolean\`, \`onClick?:(event) => void\`, \`disabled?:boolean\`, \`className\`, children.
|
|
256
|
+
|
|
257
|
+
Example
|
|
258
|
+
\`\`\`jsx
|
|
259
|
+
<Button tone="neutral" weight="outline" size="sm" onClick={() => setShowAll(true)}>Show all 12</Button>
|
|
260
|
+
\`\`\``,
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
names: ['Table', 'TableHeader', 'TableBody', 'TableRow', 'TableHead', 'TableCell', 'TableCaption'],
|
|
264
|
+
group: 'display',
|
|
265
|
+
selectionLine: '`Table`, `TableHeader`, `TableBody`, `TableRow`, `TableHead`, `TableCell`, `TableCaption` — read-only tabular layout. Use `UserTable` when the user must select rows or columns.',
|
|
266
|
+
contract: `\`<Table>\` family — \`Table\`, \`TableHeader\`, \`TableBody\`, \`TableRow\`, \`TableHead\`, \`TableCell\`, \`TableCaption\`
|
|
267
|
+
|
|
268
|
+
${DISPLAY_RULES} It is layout only: nothing in it is selectable and nothing reaches the response. For selection, use \`UserTable\`.
|
|
269
|
+
|
|
270
|
+
Props
|
|
271
|
+
- All take \`className\` and children. \`TableHead\` and \`TableCell\` additionally take \`colSpan\`.
|
|
272
|
+
- Structure: \`<Table>\` wraps optional \`<TableCaption>\`, \`<TableHeader>\` (rows of \`<TableHead>\`), and \`<TableBody>\` (rows of \`<TableCell>\`).
|
|
273
|
+
|
|
274
|
+
Example
|
|
275
|
+
\`\`\`jsx
|
|
276
|
+
<Table>
|
|
277
|
+
<TableHeader><TableRow><TableHead>Gate</TableHead><TableHead>State</TableHead></TableRow></TableHeader>
|
|
278
|
+
<TableBody>{gates.map((g) => (
|
|
279
|
+
<TableRow key={g.id}><TableCell>{g.name}</TableCell><TableCell>{g.state}</TableCell></TableRow>
|
|
280
|
+
))}</TableBody>
|
|
281
|
+
</Table>
|
|
282
|
+
\`\`\``,
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
names: ['Tabs', 'TabsList', 'TabsTrigger', 'TabsContent'],
|
|
286
|
+
group: 'display',
|
|
287
|
+
selectionLine: '`Tabs`, `TabsList`, `TabsTrigger`, `TabsContent` — parallel views of the same subject behind one control.',
|
|
288
|
+
contract: `\`<Tabs>\` family — \`Tabs\`, \`TabsList\`, \`TabsTrigger\`, \`TabsContent\`
|
|
289
|
+
|
|
290
|
+
${DISPLAY_RULES} Which tab the user opened is not part of the response.
|
|
291
|
+
|
|
292
|
+
Props
|
|
293
|
+
- \`Tabs\`: \`defaultValue?:string\`, \`value?:string\`, \`onValueChange?:(value:string) => void\`, \`className\`.
|
|
294
|
+
- \`TabsTrigger\` and \`TabsContent\`: \`value\` — required, and the two match by that value.
|
|
295
|
+
- \`TabsList\`: \`className\` and children.
|
|
296
|
+
|
|
297
|
+
Example
|
|
298
|
+
\`\`\`jsx
|
|
299
|
+
<Tabs defaultValue="diff">
|
|
300
|
+
<TabsList><TabsTrigger value="diff">Diff</TabsTrigger><TabsTrigger value="risk">Risk</TabsTrigger></TabsList>
|
|
301
|
+
<TabsContent value="diff">…</TabsContent>
|
|
302
|
+
<TabsContent value="risk">…</TabsContent>
|
|
303
|
+
</Tabs>
|
|
304
|
+
\`\`\``,
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
names: ['Separator'],
|
|
308
|
+
group: 'display',
|
|
309
|
+
selectionLine: '`Separator` — a rule between sections.',
|
|
310
|
+
contract: `\`<Separator>\`
|
|
311
|
+
|
|
312
|
+
${DISPLAY_RULES}
|
|
313
|
+
|
|
314
|
+
Props
|
|
315
|
+
- \`orientation?:"horizontal"|"vertical"\` — defaults to \`horizontal\`.
|
|
316
|
+
- \`className\`.
|
|
317
|
+
|
|
318
|
+
Example
|
|
319
|
+
\`\`\`jsx
|
|
320
|
+
<Separator className="my-4" />
|
|
321
|
+
\`\`\``,
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
names: ['Alert', 'AlertTitle', 'AlertDescription'],
|
|
325
|
+
group: 'display',
|
|
326
|
+
selectionLine: '`Alert`, `AlertTitle`, `AlertDescription` — a callout for the one thing the user must not miss.',
|
|
327
|
+
contract: `\`<Alert>\` family — \`Alert\`, \`AlertTitle\`, \`AlertDescription\`
|
|
328
|
+
|
|
329
|
+
${DISPLAY_RULES}
|
|
330
|
+
|
|
331
|
+
Props
|
|
332
|
+
- \`Alert\`: \`variant?:"default"|"destructive"\`, \`className\`.
|
|
333
|
+
- \`AlertTitle\` and \`AlertDescription\`: \`className\` and children.
|
|
334
|
+
|
|
335
|
+
Example
|
|
336
|
+
\`\`\`jsx
|
|
337
|
+
<Alert variant="destructive">
|
|
338
|
+
<AlertTitle>Migration is irreversible</AlertTitle>
|
|
339
|
+
<AlertDescription>The old rows are dropped, not archived.</AlertDescription>
|
|
340
|
+
</Alert>
|
|
341
|
+
\`\`\``,
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
names: ['Progress'],
|
|
345
|
+
group: 'display',
|
|
346
|
+
selectionLine: '`Progress` — a completion bar for a known ratio.',
|
|
347
|
+
contract: `\`<Progress>\`
|
|
348
|
+
|
|
349
|
+
${DISPLAY_RULES}
|
|
350
|
+
|
|
351
|
+
Props
|
|
352
|
+
- \`value?:number\` — 0 to 100.
|
|
353
|
+
- \`className\`.
|
|
354
|
+
|
|
355
|
+
Example
|
|
356
|
+
\`\`\`jsx
|
|
357
|
+
<Progress value={Math.round((done / total) * 100)} />
|
|
358
|
+
\`\`\``,
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
names: ['Accordion', 'AccordionItem', 'AccordionTrigger', 'AccordionContent'],
|
|
362
|
+
group: 'display',
|
|
363
|
+
selectionLine: '`Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent` — supporting detail the user opens only if they want it.',
|
|
364
|
+
contract: `\`<Accordion>\` family — \`Accordion\`, \`AccordionItem\`, \`AccordionTrigger\`, \`AccordionContent\`
|
|
365
|
+
|
|
366
|
+
${DISPLAY_RULES}
|
|
367
|
+
|
|
368
|
+
Props
|
|
369
|
+
- \`Accordion\`: \`type\` — required: \`single\` or \`multiple\`; \`collapsible?:boolean\`, \`defaultValue?:string|string[]\`, \`className\`.
|
|
370
|
+
- \`AccordionItem\`: \`value\` — required, unique within the accordion.
|
|
371
|
+
- \`AccordionTrigger\` and \`AccordionContent\`: \`className\` and children.
|
|
372
|
+
|
|
373
|
+
Example
|
|
374
|
+
\`\`\`jsx
|
|
375
|
+
<Accordion type="single" collapsible>
|
|
376
|
+
<AccordionItem value="why">
|
|
377
|
+
<AccordionTrigger>Why this is blocked</AccordionTrigger>
|
|
378
|
+
<AccordionContent>The audit job has not run since Tuesday.</AccordionContent>
|
|
379
|
+
</AccordionItem>
|
|
380
|
+
</Accordion>
|
|
381
|
+
\`\`\``,
|
|
129
382
|
},
|
|
130
383
|
];
|
|
131
|
-
const
|
|
132
|
-
/**
|
|
133
|
-
|
|
134
|
-
export
|
|
135
|
-
|
|
384
|
+
const docsByName = new Map(BUILTIN_COMPONENT_DOCS.flatMap((doc) => doc.names.map((name) => [name, doc])));
|
|
385
|
+
/** The structural and response-bearing rows — the ones that decide what a page can ask. `human -h` carries these;
|
|
386
|
+
* the display set is one line there and the full catalog is `human components list`. */
|
|
387
|
+
export const CORE_COMPONENT_SELECTION_LINES = [...BUILTIN_COMPONENT_DOCS.filter((doc) => doc.group !== 'display').map((doc) => doc.selectionLine), '`usePageHost` — reserved host hook. Its product-supplied members are opaque to crouter; consult the owning product documentation.'];
|
|
388
|
+
export function componentDoc(name) {
|
|
389
|
+
return docsByName.get(name);
|
|
136
390
|
}
|
|
137
|
-
export function
|
|
138
|
-
return [...BUILTIN_COMPONENT_DOCS.
|
|
391
|
+
export function componentNames(productComponents) {
|
|
392
|
+
return [...BUILTIN_COMPONENT_DOCS.flatMap((doc) => [...doc.names]), ...productTags(productComponents).values()];
|
|
139
393
|
}
|
|
394
|
+
const GROUP_HEADINGS = {
|
|
395
|
+
structural: 'Structural — every page is built from these.',
|
|
396
|
+
slot: 'Response — each is recorded in the page manifest under its wire kind, and their answers are the page response.',
|
|
397
|
+
display: 'Display — shadcn components with their standard props, plus `className` for Tailwind. They contribute nothing to the response.',
|
|
398
|
+
};
|
|
140
399
|
export function renderComponentCatalog(productComponents) {
|
|
141
|
-
const
|
|
400
|
+
const builtinCount = BUILTIN_COMPONENT_DOCS.reduce((total, doc) => total + doc.names.length, 0);
|
|
401
|
+
const lines = [`<components count="${builtinCount + productComponents.length}">`];
|
|
402
|
+
for (const group of ['structural', 'slot', 'display']) {
|
|
403
|
+
lines.push(GROUP_HEADINGS[group], ...BUILTIN_COMPONENT_DOCS.filter((doc) => doc.group === group).map((doc) => doc.selectionLine), '');
|
|
404
|
+
}
|
|
405
|
+
const tags = productTags(productComponents);
|
|
406
|
+
if (productComponents.length > 0)
|
|
407
|
+
lines.push('Product-registered — registry components this product ships. Write each one under the tag shown; a `(kind …)` suffix names its lowercase-hyphen wire kind.');
|
|
408
|
+
else
|
|
409
|
+
lines.pop();
|
|
142
410
|
for (const component of productComponents) {
|
|
411
|
+
const tag = tags.get(component.kind) ?? component.kind;
|
|
412
|
+
const label = tag === component.kind ? `\`${tag}\`` : `\`${tag}\` (kind \`${component.kind}\`)`;
|
|
143
413
|
if (component.description === undefined && component.useWhen === undefined) {
|
|
144
|
-
lines.push(
|
|
414
|
+
lines.push(`${label} — ${component.display === true ? 'product-registered; display-only' : 'product-registered'}`);
|
|
145
415
|
continue;
|
|
146
416
|
}
|
|
147
417
|
const details = [component.description, component.useWhen === undefined ? undefined : `${component.useWhen[0].toUpperCase()}${component.useWhen.slice(1)}`, ...(component.display === true ? ['Display-only'] : [])].filter((detail) => detail !== undefined);
|
|
148
|
-
lines.push(
|
|
418
|
+
lines.push(`${label} — ${details.join('. ')}`);
|
|
149
419
|
}
|
|
150
420
|
lines.push('</components>');
|
|
151
421
|
return lines.join('\n');
|
|
152
422
|
}
|
|
153
|
-
export function renderComponentContract(
|
|
154
|
-
const builtin = componentDoc(
|
|
423
|
+
export function renderComponentContract(name, productComponents) {
|
|
424
|
+
const builtin = componentDoc(name);
|
|
155
425
|
if (builtin !== undefined)
|
|
156
426
|
return builtin.contract;
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
427
|
+
const tags = productTags(productComponents);
|
|
428
|
+
const productComponent = productComponents.find((component) => component.kind === name || tags.get(component.kind) === name);
|
|
429
|
+
if (productComponent === undefined)
|
|
430
|
+
return undefined;
|
|
431
|
+
const tag = tags.get(productComponent.kind) ?? productComponent.kind;
|
|
432
|
+
const heading = `\`<${tag}>\` — product-registered, wire kind \`${productComponent.kind}\``;
|
|
433
|
+
return `${heading}\n\n${productComponent.doc ?? `product-registered${productComponent.display === true ? '; display-only' : '; config is passed through unvalidated (unvalidated: true)'} — see the product's own docs`}`;
|
|
161
434
|
}
|
|
@@ -1,17 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/** The authored page source path, derived from the persisted dialect. */
|
|
2
|
+
export declare function pagePath(dir: string, dialect?: 'jsx' | 'html'): string;
|
|
3
|
+
/** The compiled module the Gateway evaluates for JSX pages. */
|
|
4
|
+
export declare function pageModulePath(dir: string): string;
|
|
4
5
|
export declare function pageManifestPath(dir: string): string;
|
|
5
6
|
export declare function reviewPath(dir: string): string;
|
|
6
7
|
export declare function responsePath(dir: string): string;
|
|
7
8
|
export declare function progressPath(dir: string): string;
|
|
8
9
|
export declare function claimPath(dir: string): string;
|
|
10
|
+
export declare function replyRoutePath(dir: string): string;
|
|
11
|
+
export interface TicketReplyRoute {
|
|
12
|
+
bridge_node_id: string;
|
|
13
|
+
}
|
|
14
|
+
/** A reply route exists only when completing the ticket must finish a human
|
|
15
|
+
* bridge and fan its result back to subscribers. Its absence is the complete
|
|
16
|
+
* representation of a standalone page. */
|
|
17
|
+
export declare function readTicketReplyRoute(dir: string): TicketReplyRoute | null;
|
|
18
|
+
export declare function bindTicketReplyRoute(dir: string, bridgeNodeId: string): void;
|
|
9
19
|
export type InteractionState = 'pending' | 'claimed' | 'resolved' | 'missing';
|
|
10
20
|
export declare function interactionState(dir: string): InteractionState;
|
|
11
21
|
export declare function isResolved(dir: string): boolean;
|
|
12
22
|
export declare function isClaimed(dir: string): boolean;
|
|
13
23
|
/** Resolve and verify a ticket directory without a registry. */
|
|
14
24
|
export declare function requireTicket(dir: string): string;
|
|
25
|
+
/** Replace one file's contents in a single rename, so a concurrent reader —
|
|
26
|
+
* the tmux inbox watcher, or crtrd serving a ticket read — sees either the old
|
|
27
|
+
* bytes or the new ones and never a truncated file mid-write. */
|
|
28
|
+
export declare function atomicWriteText(path: string, contents: string): void;
|
|
15
29
|
export declare function atomicWriteJson(path: string, value: unknown): void;
|
|
16
30
|
/** Publish one immutable JSON record without replacing an existing winner. */
|
|
17
31
|
export declare function publishJsonExclusive(path: string, value: unknown): boolean;
|
|
@@ -1,12 +1,37 @@
|
|
|
1
|
-
import { existsSync, linkSync, mkdirSync, realpathSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, linkSync, mkdirSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
|
-
/** The authored page source,
|
|
4
|
-
export function pagePath(dir, dialect) { return `${dir}/page.${dialect}`; }
|
|
3
|
+
/** The authored page source path, derived from the persisted dialect. */
|
|
4
|
+
export function pagePath(dir, dialect = 'jsx') { return `${dir}/page.${dialect === 'jsx' ? 'tsx' : 'html'}`; }
|
|
5
|
+
/** The compiled module the Gateway evaluates for JSX pages. */
|
|
6
|
+
export function pageModulePath(dir) { return `${dir}/page.js`; }
|
|
5
7
|
export function pageManifestPath(dir) { return `${dir}/page.json`; }
|
|
6
8
|
export function reviewPath(dir) { return `${dir}/review.json`; }
|
|
7
9
|
export function responsePath(dir) { return `${dir}/response.json`; }
|
|
8
10
|
export function progressPath(dir) { return `${dir}/progress.json`; }
|
|
9
11
|
export function claimPath(dir) { return `${dir}/claim.json`; }
|
|
12
|
+
export function replyRoutePath(dir) { return `${dir}/reply-route.json`; }
|
|
13
|
+
/** A reply route exists only when completing the ticket must finish a human
|
|
14
|
+
* bridge and fan its result back to subscribers. Its absence is the complete
|
|
15
|
+
* representation of a standalone page. */
|
|
16
|
+
export function readTicketReplyRoute(dir) {
|
|
17
|
+
const path = replyRoutePath(dir);
|
|
18
|
+
if (!existsSync(path))
|
|
19
|
+
return null;
|
|
20
|
+
let raw;
|
|
21
|
+
try {
|
|
22
|
+
raw = JSON.parse(readFileSync(path, 'utf8'));
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new Error(`invalid ticket reply route: ${path}`);
|
|
26
|
+
}
|
|
27
|
+
const bridgeNodeId = raw?.bridge_node_id;
|
|
28
|
+
if (typeof bridgeNodeId !== 'string' || bridgeNodeId === '')
|
|
29
|
+
throw new Error(`invalid ticket reply route: ${path}`);
|
|
30
|
+
return { bridge_node_id: bridgeNodeId };
|
|
31
|
+
}
|
|
32
|
+
export function bindTicketReplyRoute(dir, bridgeNodeId) {
|
|
33
|
+
atomicWriteJson(replyRoutePath(dir), { bridge_node_id: bridgeNodeId });
|
|
34
|
+
}
|
|
10
35
|
export function interactionState(dir) {
|
|
11
36
|
if (!existsSync(pageManifestPath(dir)) && !existsSync(reviewPath(dir)))
|
|
12
37
|
return 'missing';
|
|
@@ -24,12 +49,18 @@ export function requireTicket(dir) {
|
|
|
24
49
|
}
|
|
25
50
|
return canonicalDir;
|
|
26
51
|
}
|
|
27
|
-
|
|
52
|
+
/** Replace one file's contents in a single rename, so a concurrent reader —
|
|
53
|
+
* the tmux inbox watcher, or crtrd serving a ticket read — sees either the old
|
|
54
|
+
* bytes or the new ones and never a truncated file mid-write. */
|
|
55
|
+
export function atomicWriteText(path, contents) {
|
|
28
56
|
const tmp = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
29
57
|
mkdirSync(dirname(path), { recursive: true });
|
|
30
|
-
writeFileSync(tmp,
|
|
58
|
+
writeFileSync(tmp, contents, { mode: 0o600 });
|
|
31
59
|
renameSync(tmp, path);
|
|
32
60
|
}
|
|
61
|
+
export function atomicWriteJson(path, value) {
|
|
62
|
+
atomicWriteText(path, `${JSON.stringify(value, null, 2)}\n`);
|
|
63
|
+
}
|
|
33
64
|
/** Publish one immutable JSON record without replacing an existing winner. */
|
|
34
65
|
export function publishJsonExclusive(path, value) {
|
|
35
66
|
const tmp = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
|