@hybridlabor-api/aos 4.13.2 → 4.14.1
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/.agents/AGENTS.md +8 -0
- package/.agents/nodes.json +5 -2
- package/.claude/hooks/conventional-commits.mjs +14 -15
- package/.claude/hooks/env-file-protection.mjs +14 -15
- package/.claude/hooks/go-gate.mjs +152 -10
- package/.claude/hooks/go-token.mjs +55 -0
- package/.claude/hooks/memb-inject.mjs +75 -62
- package/.claude/hooks/trail-autostart.mjs +27 -0
- package/.claude/hooks/trail-relay.mjs +1 -0
- package/.claude/settings.json +22 -4
- package/.claude/workflows/startcycle-dispatch.mjs +11 -4
- package/.opencode/plugins/bdb-aos.js +98 -121
- package/.opencode/plugins/lib/trail-autostart.js +38 -0
- package/CLAUDE.md +1 -1
- package/README.de.md +6 -6
- package/README.md +6 -6
- package/README.pt.md +6 -6
- package/THIRD_PARTY_NOTICES.md +19 -3
- package/assets/header-v5.png +0 -0
- package/bin/aos-acp.mjs +211 -0
- package/bin/aos-doctor.mjs +1 -1
- package/bin/aos-uninstall.mjs +2 -2
- package/docs/master-session-acp.md +51 -0
- package/installer.js +314 -42
- package/mcps/mcsc/packages/mcp/server.js +6 -7
- package/package.json +4 -3
- package/scripts/validate-skills.mjs +76 -0
- package/skills/basic/bdbmediastorm/SKILL.md +1 -1
- package/skills/basic/godmode-shipping/SKILL.md +3 -0
- package/skills/basic/master-session/SKILL.md +89 -0
- package/skills/basic/startcycle/SKILL.md +1 -1
- package/skills/basic/startcycle-graph/SKILL.md +2 -2
- package/skills/basic/startcycle-graph-user/SKILL.md +1 -1
- package/skills/basic/teamwork-preview/SKILL.md +1 -1
- package/skills/bdbrainstorm/SKILL.md +7 -1
- package/skills/global_config/agentic-harness-patterns/SKILL.md +257 -0
- package/skills/global_config/agentic-harness-patterns/metadata.json +10 -0
- package/skills/global_config/agentic-harness-patterns/references/agent-orchestration-pattern.md +97 -0
- package/skills/global_config/agentic-harness-patterns/references/bootstrap-sequence-pattern.md +106 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering/compress-pattern.md +78 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering/isolate-pattern.md +82 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering/select-pattern.md +86 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering-pattern.md +29 -0
- package/skills/global_config/agentic-harness-patterns/references/hook-lifecycle-pattern.md +111 -0
- package/skills/global_config/agentic-harness-patterns/references/memory-persistence-pattern.md +109 -0
- package/skills/global_config/agentic-harness-patterns/references/permission-gate-pattern.md +111 -0
- package/skills/global_config/agentic-harness-patterns/references/skill-runtime-pattern.md +104 -0
- package/skills/global_config/agentic-harness-patterns/references/task-decomposition-pattern.md +92 -0
- package/skills/global_config/agentic-harness-patterns/references/tool-registry-pattern.md +101 -0
- package/skills/global_config/agenttrail/SKILL.md +8 -0
- package/skills/global_config/agenttrail/bin/agenttrail.mjs +14 -0
- package/skills/global_config/agenttrail/bin/ensure.mjs +118 -0
- package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +1 -1
- package/skills/global_config/bdb-visual-edit/SKILL.md +51 -0
- package/skills/global_config/bdb-visual-edit/references/vite-react-source-attr.md +59 -0
- package/skills/global_config/bdb-visual-edit/scripts/pick-snippet.js +27 -0
- package/skills/global_config/bdb-visual-edit/scripts/sanitize-element.mjs +123 -0
- package/skills/global_config/factory-collect/SKILL.md +74 -0
- package/skills/global_config/factory-human-digest/SKILL.md +92 -0
- package/skills/global_config/factory-lookback/SKILL.md +95 -0
- package/skills/global_config/factory-review-prs/SKILL.md +63 -0
- package/skills/global_config/git-pr-review/SKILL.md +3 -0
- package/skills/global_config/grilling/SKILL.md +2 -0
- package/skills/global_config/mcsc/SKILL.md +1 -1
- package/skills/global_config/plan-arbiter/SKILL.md +125 -0
- package/skills/global_config/plan-canvas/SKILL.md +62 -5
- package/skills/global_config/plan-canvas/scripts/lib/loopback-guard.js +19 -3
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/README.md +285 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/agent-trail.js +129 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/board-client.js +124 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/demo-plan/canvas.mdx +19 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/demo-plan/plan.mdx +18 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/recap-demo/plan.mdx +72 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/README.md +29 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/architecture.json +30 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/builder/00_architecture.html +14950 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/builder/canvas.mdx +511 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/builder/plan.mdx +208 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/recap/plan.mdx +102 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/standard/plan.md +136 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/signup-storyboard/canvas.mdx +124 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/signup-storyboard/plan.mdx +37 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/index.js +188 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/kit.js +123 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/mdx.js +411 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/render.js +1291 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/architecture/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/architecture/plan.mdx +195 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/architecture/standard.md +95 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/bugfix/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/bugfix/plan.mdx +105 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/bugfix/standard.md +76 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/canvas.mdx +81 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/plan.mdx +145 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/standard.md +76 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/migration/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/migration/plan.mdx +172 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/migration/standard.md +100 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap/plan.mdx +67 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap/standard.md +49 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/canvas.mdx +63 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/plan.mdx +49 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/standard.md +39 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-review/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-review/plan.mdx +118 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-review/standard.md +57 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/release/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/release/plan.mdx +173 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/release/standard.md +96 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/research/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/research/plan.mdx +91 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/research/standard.md +54 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/canvas.mdx +53 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/plan.mdx +225 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/standard.md +111 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/theme.css +472 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/trail.js +216 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/markdown.js +1 -1
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/server.js +37 -4
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/ui.js +125 -29
- package/skills/global_config/plan-canvas/scripts/plan-canvas.js +196 -8
- package/skills/global_config/pr-recap/SKILL.md +47 -0
- package/skills/global_config/pr-recap/scripts/pr-recap.mjs +200 -0
- package/skills/global_config/quick-recap/SKILL.md +55 -0
- package/skills/global_config/stay-within-limits/SKILL.md +85 -0
- package/skills/global_config/triage/SKILL.md +3 -0
- package/skills/global_config/visual-edit/README.md +96 -0
- package/skills/global_config/visual-edit/SKILL.md +615 -0
- package/skills/global_config/visual-plan/README.md +93 -0
- package/skills/global_config/visual-plan/SKILL.md +544 -0
- package/skills/global_config/visual-plan/references/canvas.md +139 -0
- package/skills/global_config/visual-plan/references/connection.md +51 -0
- package/skills/global_config/visual-plan/references/document-quality.md +186 -0
- package/skills/global_config/visual-plan/references/exemplar.md +62 -0
- package/skills/global_config/visual-plan/references/local-files.md +99 -0
- package/skills/global_config/visual-plan/references/wireframe.md +319 -0
- package/skills/global_config/visual-recap/README.md +103 -0
- package/skills/global_config/visual-recap/SKILL.md +560 -0
- package/skills/global_config/visual-recap/references/connection.md +51 -0
- package/skills/global_config/visual-recap/references/local-files.md +99 -0
- package/skills/global_config/visual-recap/references/wireframe.md +319 -0
- package/skills/playbooks/pb-ci-fix/SKILL.md +49 -0
- package/skills/playbooks/pb-event-tracker/SKILL.md +45 -0
- package/skills/playbooks/pb-meeting-actions/SKILL.md +42 -0
- package/skills/playbooks/pb-project-new/SKILL.md +48 -0
- package/skills/playbooks/pb-week-plan/SKILL.md +45 -0
- package/assets/header-v4.jpg +0 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# BDB Plan Builder
|
|
2
|
+
|
|
3
|
+
Renders an Agent-Native-style **plan folder** (local-files format) into ONE
|
|
4
|
+
self-contained HTML file in the BDB look, which `aos-plan-canvas` then opens
|
|
5
|
+
through its normal `.html` artifact path — so annotation, chat and verdict
|
|
6
|
+
already work with no canvas changes.
|
|
7
|
+
|
|
8
|
+
CommonJS, zero dependencies, no network at build time.
|
|
9
|
+
|
|
10
|
+
## Input / output
|
|
11
|
+
|
|
12
|
+
| Path | Role |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `<plan-dir>/plan.mdx` | **required** — frontmatter + document blocks |
|
|
15
|
+
| `<plan-dir>/canvas.mdx` | optional — appended as a "Canvas" section |
|
|
16
|
+
| `<plan-dir>/prototype.mdx` | optional — appended as a "Prototype" section |
|
|
17
|
+
| `<plan-dir>/.plan-state.json` | optional — `title`, `status`/`kind`, `localOnly` |
|
|
18
|
+
| `<plan-dir>/plan.builder.html` | **output** — written next to the plan |
|
|
19
|
+
|
|
20
|
+
The output lives inside the folder because plan-canvas only opens artifacts
|
|
21
|
+
inside the workspace root.
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Build and open in one step
|
|
27
|
+
aos-plan-canvas open <plan-dir> --mode bdb-plan-builder
|
|
28
|
+
aos-plan-canvas open <plan-dir>/plan.mdx --mode bdb-plan-builder --no-open
|
|
29
|
+
|
|
30
|
+
# Then listen for the review verdict
|
|
31
|
+
aos-plan-canvas await <plan-dir>/plan.builder.html
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Rebuild by re-running `open`: edit the MDX, run `open` again. A path with no
|
|
35
|
+
`plan.mdx` exits **2** with the reason on stderr and stdout.
|
|
36
|
+
|
|
37
|
+
Library:
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
const { renderPlanFolder, renderPlanSource, parseMdx } = require('./index');
|
|
41
|
+
|
|
42
|
+
renderPlanFolder('./plans/my-plan'); // { html, warnings, outFile }
|
|
43
|
+
renderPlanSource({ plan, canvas, state }); // { html, warnings }
|
|
44
|
+
parseMdx('<Code code={"x"} />'); // block array
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`renderPlanFolder` reports a missing `plan.mdx` as `error` in the return value;
|
|
48
|
+
it never throws.
|
|
49
|
+
|
|
50
|
+
## Supported tags
|
|
51
|
+
|
|
52
|
+
Tag names are the block-registry MDX names from `visual-plan` / `visual-recap`.
|
|
53
|
+
The lowercase conceptual names are accepted as aliases.
|
|
54
|
+
|
|
55
|
+
| Tag (aliases) | Props read | Renders as |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| `Diagram` (`diagram`) | `data.html`, `data.css`, `data.source`, `data.nodes`, `data.edges`, `frame`, `label` | sandboxed frame for HTML; Mermaid for `source`; table for nodes/edges |
|
|
58
|
+
| `Mermaid` (`mermaid`) | `source`, `code`, `label` | `<pre class="mermaid">` + pinned ESM loader |
|
|
59
|
+
| `FileTree` (`file-tree`) | `entries[]` with `path`, `change`, `note`, `snippet`, `depth` | change-badged monospace tree |
|
|
60
|
+
| `WireframeBlock` (`wireframe`), `Screen` (`screen`) | `html`, `surface`, `css`, `label`, `caption`, `height`, or wireframe-kit children | `html`: sandboxed frame (no `allow-scripts`); kit children: low-fi markup, see [Wireframe kit](#wireframe-kit) |
|
|
61
|
+
| `Diff` (`diff`) | `before`, `after`, `filename`, `language`, `mode`, `summary`, `annotations[]` | split or unified two-pane diff |
|
|
62
|
+
| `Code` (`code`), `AnnotatedCode` (`annotated-code`) | `code`, `filename`, `language`, `annotations[]` | code block + margin notes |
|
|
63
|
+
| `Endpoint` (`endpoint`, `api-endpoint`, `ApiEndpoint`) | `method`, `path`, `params[]`, `examples[]`, children prose | method/path header, param table, JSON examples |
|
|
64
|
+
| `DataModel` (`data-model`) | `entities[].fields[]` with `name`, `type`, `change`, `was`, `note` | nested entity cards |
|
|
65
|
+
| `QuestionForm` (`question-form`) | `title`, `questions[]` with `title`, `mode`, `options[]` | Open Questions card, recommended option marked |
|
|
66
|
+
| `Columns` (`columns`) | `columns[].label` + nested blocks | two-column grid, stacked on phones |
|
|
67
|
+
| `TabsBlock` (`tabs`, `Tabs`) | `tabs[].label` + nested blocks | stacked labelled groups (no click JS — the annotation layer owns clicks) |
|
|
68
|
+
| `Archify` (`archify`) | `src` (relative `.html` inside the plan folder), `label`, `height` | delivered Archify diagram in `<iframe sandbox="allow-scripts">` (never `allow-same-origin`), caption and link row; see [Archify](#archify) |
|
|
69
|
+
| `AgentTrail` (`agent-trail`) | `live` (http(s) URL on localhost/127.0.0.1), `embed` (flag, needs `live`) | static dependency graph of the plan's components (columns by `needs` depth, scaled with CSS to the container width, edges only for stated needs, task progress, expandable tasks) derived from the same folder's `{#id}` headings, `ImplementationMap` and `Checklist`; `live` adds an "Open live agent trail" link, `embed` a sandboxed iframe (`allow-scripts allow-same-origin`); works inside `<Artboard surface="web">`; no components gives a visible card and a warning |
|
|
70
|
+
| `CustomHtml` (`custom-html`) | `html`, `css`, `label`, `height` | sandboxed frame, **no `allow-scripts`** |
|
|
71
|
+
| `RichText` (`rich-text`) | `title`, markdown children | prose |
|
|
72
|
+
| `Callout` | `tone`, `title`, markdown children | bordered card |
|
|
73
|
+
| `Checklist` (`checklist`) | `title`, `items[]` (strings or `{label, checked, note}`), children: `- [x] item` lines or `<Item checked>` tags | checkbox list |
|
|
74
|
+
| `Table` (`table`) | `title`, `columns[]`, `rows[]` (arrays, or objects keyed by column), or a markdown table as children | bordered table, inline markdown in cells |
|
|
75
|
+
| `CodeTabs` (`code-tabs`) | `tabs[]` with `label`, `language`, `code` | one card, every tab stacked under its filename |
|
|
76
|
+
| `Decision` (`decision`) | `title`, `question`, `options[]` (`label`, `detail`, `recommended`), `recommended` (id, label or index), `rationale` or children prose | question, option cards with a `recommended` badge, rationale |
|
|
77
|
+
| `HtmlBlock` (`html-block`) | `html` or children, `title`, `css`, `height` | sandboxed frame, **no `allow-scripts`** (same as `CustomHtml`) |
|
|
78
|
+
| `ImplementationMap` (`implementation-map`) | `title`, `files[]` (`path`, `title`, `note`, `change`, `snippet`), or children lines | file list with change badges; an unparsable `files` shows its escaped raw text in a visible card plus a warning |
|
|
79
|
+
| `Compare` (`compare`) | `before`, `after` (markdown, `{html}`), `beforeLabel`, `afterLabel`, or `<Before>`/`<After>` children, or exactly two child blocks | Before / After two-column comparison |
|
|
80
|
+
| `Json`, `OpenApiSpec` (`openapi`) | `code`/`data`, `spec` | code block |
|
|
81
|
+
| `DesignBoard`, `Section` | children | pass-through (canvas.mdx containers) |
|
|
82
|
+
| `Artboard` | `id`, `label`/`title`, `surface`, `x`, `y`, `width`, `height`, `order`, children | card wrapping the artboard's `Screen`; absolutely positioned when `x`/`y` are set, see [Absolute board layout](#absolute-board-layout) |
|
|
83
|
+
| `Annotation` | `title`, markdown children or `text`, `targetId`, `placement`, `x`, `y` | small muted note with an arrow glyph |
|
|
84
|
+
| `Connector` | `label`/`text` | connector label card |
|
|
85
|
+
|
|
86
|
+
**Unknown tag → visible card.** Any tag not in the table renders as a bordered
|
|
87
|
+
"unsupported block" card showing the tag name plus its escaped raw source, and
|
|
88
|
+
adds a line to `warnings` (also shown in a banner at the top of the page).
|
|
89
|
+
Content is never dropped silently. A block that fails to render becomes an error
|
|
90
|
+
card instead of taking the document down.
|
|
91
|
+
|
|
92
|
+
## Prop syntax
|
|
93
|
+
|
|
94
|
+
`{...}` attribute values are JSON **or** a data-only JavaScript literal:
|
|
95
|
+
unquoted keys, single quotes, trailing commas, comments and `'a' + 'b'` string
|
|
96
|
+
concatenation all parse. Nothing is evaluated; an expression that is neither
|
|
97
|
+
becomes a warning and the raw text is kept.
|
|
98
|
+
|
|
99
|
+
## Wireframe kit
|
|
100
|
+
|
|
101
|
+
A `<Screen>` (or `<FrameScreen>`) without an `html` prop whose children are tags
|
|
102
|
+
renders the kit below as plain markup in the page (no iframe). Low-fi look:
|
|
103
|
+
muted greys on dark, 1px borders, the BDB accent only for `active`/`primary`/done
|
|
104
|
+
states, and the same UI font stack as plan-canvas and the AOS store (no handwriting font). An empty `<Screen>` renders an empty frame.
|
|
105
|
+
|
|
106
|
+
| Tag | Props | Renders |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| `FrameScreen`, `Main`, `Col`, `Row` | `full` | flex containers; `full` fills the remaining space |
|
|
109
|
+
| `Box`, `Card` | `dashed`, `full` | thin bordered container |
|
|
110
|
+
| `Lines` | `n`, `widths[]` (percent) | `n` text bars |
|
|
111
|
+
| `IconSquare` | `active` | small rounded square, accent when active |
|
|
112
|
+
| `Divider`, `StatusBar` | | rule; phone status strip |
|
|
113
|
+
| `TaskRow` | `title`, `done`, `note` | checkbox row |
|
|
114
|
+
| `Text` | `value` (or children), `tone="muted"`, `weight="bold"` | wireframe text |
|
|
115
|
+
| `Title`, `SectionLabel`, `Btn`, `Chips` | `text`, `label`, `label`/`primary`, `items[]` | heading, caps label, button, chip row |
|
|
116
|
+
| `Skeleton` | `lines` + `widths`, or `width`/`height` | placeholder bars |
|
|
117
|
+
|
|
118
|
+
An unknown kit tag renders a small labelled placeholder (`<Name>`) and adds a
|
|
119
|
+
warning. Semantic `<Screen html={...}/>` is unchanged: sandboxed iframe.
|
|
120
|
+
|
|
121
|
+
## Absolute board layout
|
|
122
|
+
|
|
123
|
+
If any `<Artboard>` carries numeric `x` and `y`, the board switches from numbered
|
|
124
|
+
flow rows to an absolute canvas with a dotted grid:
|
|
125
|
+
|
|
126
|
+
- Artboards sit at `x`/`y` with `width`/`height` (defaults from `surface`), sorted in
|
|
127
|
+
the DOM by `order` (shown as a small number chip); the canvas is the bounding box
|
|
128
|
+
of everything plus a margin.
|
|
129
|
+
- The artboard `label` sits above the frame, a `Screen` `caption` under it, and a
|
|
130
|
+
`Section` `title`/`subtitle` becomes a numbered label above its artboards.
|
|
131
|
+
- `Annotation` goes under (`placement="bottom"`, default), above, left or right of
|
|
132
|
+
its `targetId` artboard, or at its own `x`/`y`. Notes for the same target stack.
|
|
133
|
+
- Artboards without coordinates, annotations without a target or position, and
|
|
134
|
+
non-artboard blocks are listed in a tray below the canvas.
|
|
135
|
+
- Connectors are unchanged: only stated `transitions` / `Connector` / `edges`.
|
|
136
|
+
A coordinate-free canvas keeps the flow-row layout.
|
|
137
|
+
|
|
138
|
+
## Visual recap
|
|
139
|
+
|
|
140
|
+
A plan whose frontmatter has `kind: recap` (or whose `.plan-state.json` has
|
|
141
|
+
`"kind": "recap"`) gets a recap header instead of the plain title: the eyebrow
|
|
142
|
+
`VISUAL RECAP`, a large title, a subtitle, and a chip row.
|
|
143
|
+
|
|
144
|
+
| Frontmatter | Chip |
|
|
145
|
+
|---|---|
|
|
146
|
+
| `title`, `subtitle` (or `summary`) | title, subtitle |
|
|
147
|
+
| `pr` | `PR #214` |
|
|
148
|
+
| `branch`, `base` | `branch feat/x → main` |
|
|
149
|
+
| `commit`, `author`, `date` | commit, `by …`, date |
|
|
150
|
+
| `files` | `4 files` |
|
|
151
|
+
| `additions`, `deletions` | `+186`, `−41` |
|
|
152
|
+
|
|
153
|
+
Fields that are missing are skipped. Use `<Compare>` for the Before / After
|
|
154
|
+
comparison:
|
|
155
|
+
|
|
156
|
+
```mdx
|
|
157
|
+
<Compare>
|
|
158
|
+
<Before><Screen surface="mobile">...</Screen></Before>
|
|
159
|
+
<After><Screen surface="mobile">...</Screen></After>
|
|
160
|
+
</Compare>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`<Compare before="..." after="..." />` takes markdown, and a `<Compare>` with
|
|
164
|
+
exactly two child blocks (paragraphs count) pairs them as before and after.
|
|
165
|
+
|
|
166
|
+
## Board view
|
|
167
|
+
|
|
168
|
+
Visual blocks render on a dark, pan/zoomable **board**: numbered rows of
|
|
169
|
+
fixed-width cards (`1 · Auth Entry`), SVG arrows with small muted labels, a
|
|
170
|
+
zoom control bottom-left, and a `VISUAL PLAN` mark in the footer.
|
|
171
|
+
|
|
172
|
+
**Board markup rule** (everything else stays document-style):
|
|
173
|
+
|
|
174
|
+
1. Everything in `canvas.mdx` is a board. `<Section title>` (or a Markdown
|
|
175
|
+
heading) starts a numbered row; `<DesignBoard>` is pass-through.
|
|
176
|
+
2. In `plan.mdx`, a heading tagged `{#board}` (`## Flow {#board}`) hands its
|
|
177
|
+
section, up to the next heading of the same or a higher level, to a board.
|
|
178
|
+
The tag is stripped from the heading text.
|
|
179
|
+
3. A block carrying a `board` prop (`<Mermaid board ... />`) is boarded; a run
|
|
180
|
+
of such blocks shares one board.
|
|
181
|
+
4. A plan with none of these renders exactly as before and ships no board JS.
|
|
182
|
+
|
|
183
|
+
**Arrows** are drawn only from relations the plan states, never inferred:
|
|
184
|
+
`transitions={[{from, to, label}]}` on any container, `<Connector from to
|
|
185
|
+
label />`, or `data.edges` of a node/edge `Diagram` (its nodes become cards).
|
|
186
|
+
`from`/`to` match a card `id` (or `blockId`, or the slug of its title; `source`,
|
|
187
|
+
`target`, `fromId`, `toId` are accepted too). An unknown endpoint adds a warning
|
|
188
|
+
and draws no arrow.
|
|
189
|
+
|
|
190
|
+
**Interaction** (`board-client.js`, inlined, no dependencies): `+`, `-`, `fit`,
|
|
191
|
+
`1:1` buttons and Ctrl/Cmd+wheel zoom with a percentage readout; drag empty
|
|
192
|
+
background or hold Space and drag to pan. The script never listens for clicks
|
|
193
|
+
on cards or text (the annotation layer owns those) and only calls
|
|
194
|
+
`preventDefault` for Ctrl/Cmd+wheel and the Space key over a board. Arrow paths
|
|
195
|
+
are measured in the browser, so without JS the board simply scrolls inside its
|
|
196
|
+
own container and arrows are not drawn (a hidden list keeps the relations
|
|
197
|
+
readable). Below 900px the side navigation is hidden when the page is
|
|
198
|
+
board-only.
|
|
199
|
+
|
|
200
|
+
**Demos** (`aos-plan-canvas open lib/plan-builder/examples/<name> --mode bdb-plan-builder`):
|
|
201
|
+
|
|
202
|
+
- `demo-plan`: 4 screens, 3 transitions, 2 numbered sections, one Mermaid diagram.
|
|
203
|
+
- `signup-storyboard`: 6 absolutely positioned kit artboards, 2 sections, 5 transitions, 2 annotations.
|
|
204
|
+
- `recap-demo`: `kind: recap` header, Before/After, implementation map, table, code tabs.
|
|
205
|
+
|
|
206
|
+
All demo content is invented.
|
|
207
|
+
|
|
208
|
+
## Archify
|
|
209
|
+
|
|
210
|
+
`<Archify src="00_architecture.html" label="System" height={560} />` embeds the
|
|
211
|
+
standalone HTML produced by the `archify` skill. `src` is resolved against the
|
|
212
|
+
plan folder and must stay inside it: `..`, absolute paths, URLs and symlink
|
|
213
|
+
escapes show an error card plus a warning, as does a missing file ("file not
|
|
214
|
+
found"), a non-`.html` file, or a file over 5 MB. The page runs in
|
|
215
|
+
`sandbox="allow-scripts"` only (no same-origin, forms, popups or top
|
|
216
|
+
navigation) and a link row opens the file standalone.
|
|
217
|
+
|
|
218
|
+
## Prototype hint
|
|
219
|
+
|
|
220
|
+
If any `Artboard` or `Screen` has `surface` `web` or `desktop`, or the `plan.mdx`
|
|
221
|
+
frontmatter says `prototype: suggest`, the document ends with one "Suggested next
|
|
222
|
+
step" callout naming those screens and saying a throwaway prototype can be built
|
|
223
|
+
with the `prototype` skill. It is plain escaped text and starts nothing.
|
|
224
|
+
`prototype: skip`, or a plan with no such screens, omits it.
|
|
225
|
+
|
|
226
|
+
## Trail export
|
|
227
|
+
|
|
228
|
+
`aos-plan-canvas trail <plan-dir|plan.mdx> [--out <file>] [--force]` writes an
|
|
229
|
+
agenttrail plan file (`trail.js`) so `aos-trail . --plan <file> --no-open` shows
|
|
230
|
+
the live map after approval. Components: headings tagged `{#id}` and `<Section
|
|
231
|
+
id title>`, in document order across `plan.mdx`, `canvas.mdx`, `prototype.mdx`.
|
|
232
|
+
`needs`: `<Section needs=[...]>` or frontmatter `needs-<id>: a, b`. `files`:
|
|
233
|
+
`<ImplementationMap>` entries. Tasks: `<Checklist>` items (`{#id}` kept, else
|
|
234
|
+
generated). The default output is `production_artifacts/00_execution_plan.md`; it must resolve
|
|
235
|
+
inside the workspace root (cwd), is never overwritten without `--force`, and no
|
|
236
|
+
components means exit 2. Prints `{out, components, tasks, next_step}`.
|
|
237
|
+
|
|
238
|
+
## Templates
|
|
239
|
+
|
|
240
|
+
Starter plans live in `templates/<id>/` (`meta.json`, `plan.mdx`, optional
|
|
241
|
+
`canvas.mdx`, `standard.md`).
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
aos-plan-canvas templates # JSON: id, label, description, useWhen, hasBoard (= has a design canvas)
|
|
245
|
+
aos-plan-canvas new <template-id> <target-dir> # --mode bdb-plan-builder (default) | standard
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Recap templates: `recap` (minimal), `recap-review` (document-style recap for a PR or merge decision: summary, changed areas with FileTree and ImplementationMap, decisions with the rejected alternative, a before/after `Compare`, a commands table plus an explicit Verified / Not verified split, risks) and `recap-board` (design canvas with BEFORE and AFTER artboards, a labelled `change` connector and numbered `Annotation` markers on AFTER, plus a short `plan.mdx` whose numbered change list matches the markers).
|
|
249
|
+
|
|
250
|
+
Builder mode copies `plan.mdx` (and `canvas.mdx` when the template has a board,
|
|
251
|
+
never `meta.json`); standard mode writes `plan.md` from `standard.md`. A
|
|
252
|
+
non-empty target exits **2** and nothing is overwritten; an unknown id exits
|
|
253
|
+
**2** and lists the valid ids. A "board" template includes a design canvas (screens + arrows). `new` never starts the canvas server. The home page (`GET /`) lists templates, open reviews and installed visual skills read-only. A template
|
|
254
|
+
folder without a readable `meta.json` is skipped by `templates`.
|
|
255
|
+
|
|
256
|
+
## Section navigation
|
|
257
|
+
|
|
258
|
+
The left section list has a "Hide sections" toggle in the top bar; the choice is kept in `localStorage` (page works without it). On phone width the list stays visible.
|
|
259
|
+
|
|
260
|
+
## Security
|
|
261
|
+
|
|
262
|
+
- Everything from the plan is escaped (`escapeHtml`, and `renderMarkdown` escapes
|
|
263
|
+
before any inline rule runs).
|
|
264
|
+
- `Archify` is the one sandbox that allows scripts (`allow-scripts`, no same-origin), for a file read from inside the plan folder.
|
|
265
|
+
- Raw HTML — `custom-html`, `HtmlBlock`, and the `html` of wireframe/diagram blocks — only
|
|
266
|
+
reaches `<iframe sandbox srcdoc=...>` **without `allow-scripts`**.
|
|
267
|
+
- Attribute values are JSON-parsed, never evaluated. Template-literal
|
|
268
|
+
interpolation is refused with a warning.
|
|
269
|
+
|
|
270
|
+
## Files
|
|
271
|
+
|
|
272
|
+
| File | Role |
|
|
273
|
+
|---|---|
|
|
274
|
+
| `index.js` | `renderPlanFolder` / `renderPlanSource` / re-export `parseMdx` |
|
|
275
|
+
| `trail.js` | agenttrail plan file from a plan folder |
|
|
276
|
+
| `mdx.js` | frontmatter, headings, prose, JSX-like tags; never throws |
|
|
277
|
+
| `render.js` | block → HTML, flow and absolute boards, recap header, Mermaid loader, page shell |
|
|
278
|
+
| `kit.js` | wireframe kit tags → markup |
|
|
279
|
+
| `board-client.js` | pan/zoom + arrow routing, inlined only when a board exists |
|
|
280
|
+
| `theme.css` | BDB CI theme, inlined into the output |
|
|
281
|
+
| `examples/demo-plan/` | richer demo: plan.mdx + canvas.mdx |
|
|
282
|
+
| `examples/signup-storyboard/`, `examples/recap-demo/` | kit storyboard and recap demos |
|
|
283
|
+
|
|
284
|
+
Adding this directory is also what flips `bdb-plan-builder` to **available** in
|
|
285
|
+
`aos-plan-canvas modes`.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* <AgentTrail /> — static, embeddable view of the plan's multi-agent workflow.
|
|
5
|
+
* Components, needs and tasks come from derive() in trail.js (same plan folder);
|
|
6
|
+
* nothing is parsed twice. The live `aos-trail` app stays the standard; `live`
|
|
7
|
+
* only links to it, and `embed` additionally frames it.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { escapeHtml: esc } = require('../plan-canvas/markdown');
|
|
11
|
+
|
|
12
|
+
const COL_W = 210;
|
|
13
|
+
const COL_GAP = 60;
|
|
14
|
+
const ROW_H = 80;
|
|
15
|
+
const ROW_GAP = 16;
|
|
16
|
+
const PAD = 8;
|
|
17
|
+
const LOCAL_HOSTS = new Set(['localhost', '127.0.0.1']);
|
|
18
|
+
|
|
19
|
+
function localUrl(raw) {
|
|
20
|
+
try {
|
|
21
|
+
const u = new URL(String(raw));
|
|
22
|
+
if (!/^https?:$/.test(u.protocol) || !LOCAL_HOSTS.has(u.hostname) || u.username || u.password) return null;
|
|
23
|
+
return u.href;
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Level = longest chain of stated needs; a cycle edge is ignored rather than looping.
|
|
30
|
+
function levels(components) {
|
|
31
|
+
const byId = new Map(components.map((c) => [c.id, c]));
|
|
32
|
+
const memo = new Map();
|
|
33
|
+
const walking = new Set();
|
|
34
|
+
const level = (c) => {
|
|
35
|
+
if (memo.has(c.id)) return memo.get(c.id);
|
|
36
|
+
walking.add(c.id);
|
|
37
|
+
let n = 0;
|
|
38
|
+
for (const need of c.needs) {
|
|
39
|
+
const dep = byId.get(need);
|
|
40
|
+
if (dep && !walking.has(dep.id)) n = Math.max(n, level(dep) + 1);
|
|
41
|
+
}
|
|
42
|
+
walking.delete(c.id);
|
|
43
|
+
memo.set(c.id, n);
|
|
44
|
+
return n;
|
|
45
|
+
};
|
|
46
|
+
components.forEach(level);
|
|
47
|
+
return memo;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const pct4 = (f) => Number((f * 100).toFixed(3));
|
|
51
|
+
|
|
52
|
+
function note(message) {
|
|
53
|
+
return `<div class="card trail-empty"><div class="label">AgentTrail</div><div>${esc(message)}</div></div>`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function renderAgentTrail(block, ctx) {
|
|
57
|
+
const props = block.props || {};
|
|
58
|
+
let model = [];
|
|
59
|
+
try {
|
|
60
|
+
if (!ctx.dir) throw new Error('needs a plan folder on disk');
|
|
61
|
+
model = require('./trail').derive(ctx.dir).graph;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
ctx.warnings.push(`<AgentTrail> could not read the plan folder: ${error.message}`);
|
|
64
|
+
}
|
|
65
|
+
if (!model.length) {
|
|
66
|
+
ctx.warnings.push('<AgentTrail> no components found; add {#id} headings and needs: lines');
|
|
67
|
+
return note('AgentTrail: no components found - add {#id} headings and needs: lines');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const level = levels(model);
|
|
71
|
+
const rowOf = new Map();
|
|
72
|
+
const pos = new Map();
|
|
73
|
+
for (const c of model) {
|
|
74
|
+
const col = level.get(c.id);
|
|
75
|
+
const row = rowOf.get(col) || 0;
|
|
76
|
+
rowOf.set(col, row + 1);
|
|
77
|
+
pos.set(c.id, { x: PAD + col * (COL_W + COL_GAP), y: PAD + row * (ROW_H + ROW_GAP) });
|
|
78
|
+
}
|
|
79
|
+
const width = PAD * 2 + (Math.max(...level.values()) + 1) * COL_W + Math.max(...level.values()) * COL_GAP;
|
|
80
|
+
const height = PAD * 2 + Math.max(...rowOf.values()) * ROW_H + (Math.max(...rowOf.values()) - 1) * ROW_GAP;
|
|
81
|
+
|
|
82
|
+
const edges = [];
|
|
83
|
+
for (const c of model) {
|
|
84
|
+
for (const need of c.needs) {
|
|
85
|
+
const a = pos.get(need);
|
|
86
|
+
const b = pos.get(c.id);
|
|
87
|
+
if (!a || !b) continue;
|
|
88
|
+
const x1 = a.x + COL_W;
|
|
89
|
+
const y1 = a.y + ROW_H / 2;
|
|
90
|
+
const x2 = b.x;
|
|
91
|
+
const y2 = b.y + ROW_H / 2;
|
|
92
|
+
const mid = (x1 + x2) / 2;
|
|
93
|
+
edges.push(`<path class="trail-edge" data-from="${esc(need)}" data-to="${esc(c.id)}" d="M${x1},${y1} C${mid},${y1} ${mid},${y2} ${x2},${y2}"/>`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const cards = model.map((c) => {
|
|
98
|
+
const done = c.tasks.filter((t) => t.checked).length;
|
|
99
|
+
const total = c.tasks.length;
|
|
100
|
+
const pct = total ? Math.round((done / total) * 100) : 0;
|
|
101
|
+
const tasks = total
|
|
102
|
+
? `<ul class="trail-tasks">${c.tasks.map((t) => `<li class="${t.checked ? 'done' : ''}"><span class="mark" aria-hidden="true">${t.checked ? '✓' : '○'}</span>${esc(t.label)}</li>`).join('')}</ul>`
|
|
103
|
+
: '<div class="trail-tasks note">no tasks</div>';
|
|
104
|
+
const p = pos.get(c.id);
|
|
105
|
+
return `<details class="trail-node" data-id="${esc(c.id)}" style="left:${pct4(p.x / width)}%;top:${p.y}px;width:${pct4(COL_W / width)}%;min-height:${ROW_H}px">` +
|
|
106
|
+
`<summary><span class="trail-id">${esc(c.id)}</span><span class="trail-title" title="${esc(c.title)}">${esc(c.title)}</span>` +
|
|
107
|
+
`<span class="trail-count">${done} of ${total} tasks</span>` +
|
|
108
|
+
`<span class="trail-bar"><span style="width:${pct}%"></span></span></summary>${tasks}</details>`;
|
|
109
|
+
}).join('');
|
|
110
|
+
|
|
111
|
+
let live = '';
|
|
112
|
+
if (props.live !== undefined && props.live !== true) {
|
|
113
|
+
const href = localUrl(props.live);
|
|
114
|
+
if (!href) {
|
|
115
|
+
ctx.warnings.push(`<AgentTrail live="${String(props.live)}"> must be an http(s) URL on localhost or 127.0.0.1; link and iframe omitted`);
|
|
116
|
+
} else {
|
|
117
|
+
live = `<div class="trail-live"><a href="${esc(href)}" target="_blank" rel="noopener">Open live agent trail</a></div>`;
|
|
118
|
+
if (props.embed !== undefined && props.embed !== false) {
|
|
119
|
+
live += `<iframe class="trail-frame" sandbox="allow-scripts allow-same-origin" loading="lazy" title="Live agent trail" src="${esc(href)}"></iframe>`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return '<div class="card trail"><div class="label">agent trail</div>' +
|
|
125
|
+
`<div class="trail-scroll"><div class="trail-stage" style="height:${height}px">` +
|
|
126
|
+
`<svg class="trail-edges" viewBox="0 0 ${width} ${height}" preserveAspectRatio="none" aria-hidden="true">${edges.join('')}</svg>${cards}</div></div>${live}</div>`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = { renderAgentTrail, localUrl };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
var MIN = 0.2, MAX = 2.5;
|
|
3
|
+
document.documentElement.classList.add('js');
|
|
4
|
+
document.querySelectorAll('.board').forEach(function (board) {
|
|
5
|
+
var vp = board.querySelector('.board-viewport');
|
|
6
|
+
var stage = board.querySelector('.board-stage');
|
|
7
|
+
var canvas = board.querySelector('.board-canvas');
|
|
8
|
+
var svg = board.querySelector('.board-edges');
|
|
9
|
+
var readout = board.querySelector('.zoom-readout');
|
|
10
|
+
var scale = 1, space = false, hover = false, drag = null;
|
|
11
|
+
|
|
12
|
+
function route(a, b) {
|
|
13
|
+
var A = { l: a.offsetLeft, t: a.offsetTop, w: a.offsetWidth, h: a.offsetHeight };
|
|
14
|
+
var B = { l: b.offsetLeft, t: b.offsetTop, w: b.offsetWidth, h: b.offsetHeight };
|
|
15
|
+
var x1, y1, x2, y2, mx, my;
|
|
16
|
+
if (B.l >= A.l + A.w || A.l >= B.l + B.w) {
|
|
17
|
+
var right = B.l >= A.l + A.w;
|
|
18
|
+
x1 = right ? A.l + A.w : A.l; y1 = A.t + A.h / 2;
|
|
19
|
+
x2 = right ? B.l : B.l + B.w; y2 = B.t + B.h / 2;
|
|
20
|
+
mx = (x1 + x2) / 2;
|
|
21
|
+
return { d: 'M' + x1 + ' ' + y1 + 'H' + mx + 'V' + y2 + 'H' + x2, x: mx, y: (y1 + y2) / 2 };
|
|
22
|
+
}
|
|
23
|
+
var down = B.t >= A.t + A.h;
|
|
24
|
+
x1 = A.l + A.w / 2; y1 = down ? A.t + A.h : A.t;
|
|
25
|
+
x2 = B.l + B.w / 2; y2 = down ? B.t : B.t + B.h;
|
|
26
|
+
my = (y1 + y2) / 2;
|
|
27
|
+
return { d: 'M' + x1 + ' ' + y1 + 'V' + my + 'H' + x2 + 'V' + y2, x: (x1 + x2) / 2, y: my };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function layout() {
|
|
31
|
+
stage.style.width = canvas.offsetWidth * scale + 'px';
|
|
32
|
+
stage.style.height = canvas.offsetHeight * scale + 'px';
|
|
33
|
+
if (!svg) return;
|
|
34
|
+
svg.setAttribute('width', canvas.offsetWidth);
|
|
35
|
+
svg.setAttribute('height', canvas.offsetHeight);
|
|
36
|
+
svg.querySelectorAll('.edge').forEach(function (g) {
|
|
37
|
+
var a = document.getElementById(g.getAttribute('data-from'));
|
|
38
|
+
var b = document.getElementById(g.getAttribute('data-to'));
|
|
39
|
+
if (!a || !b) return;
|
|
40
|
+
var r = route(a, b);
|
|
41
|
+
g.querySelector('path').setAttribute('d', r.d);
|
|
42
|
+
var label = g.querySelector('text');
|
|
43
|
+
label.setAttribute('x', r.x);
|
|
44
|
+
label.setAttribute('y', r.y - 6);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function zoomTo(next, cx, cy) {
|
|
49
|
+
next = Math.min(MAX, Math.max(MIN, next));
|
|
50
|
+
var k = next / scale, sx = vp.scrollLeft + cx, sy = vp.scrollTop + cy;
|
|
51
|
+
scale = next;
|
|
52
|
+
canvas.style.transform = 'scale(' + next + ')';
|
|
53
|
+
layout();
|
|
54
|
+
vp.scrollLeft = sx * k - cx;
|
|
55
|
+
vp.scrollTop = sy * k - cy;
|
|
56
|
+
readout.textContent = Math.round(next * 100) + '%';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function fit() {
|
|
60
|
+
// Fit the width; a tall board scrolls vertically instead of shrinking to an unreadable scale.
|
|
61
|
+
var byWidth = Math.min(1, vp.clientWidth / canvas.offsetWidth);
|
|
62
|
+
var byHeight = vp.clientHeight / canvas.offsetHeight;
|
|
63
|
+
zoomTo(byHeight < byWidth ? Math.max(byHeight, Math.min(byWidth, 0.45)) : byWidth, 0, 0);
|
|
64
|
+
vp.scrollLeft = 0;
|
|
65
|
+
vp.scrollTop = 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
board.querySelectorAll('[data-zoom]').forEach(function (btn) {
|
|
69
|
+
btn.addEventListener('click', function () {
|
|
70
|
+
var mode = btn.getAttribute('data-zoom');
|
|
71
|
+
var cx = vp.clientWidth / 2, cy = vp.clientHeight / 2;
|
|
72
|
+
if (mode === 'in') zoomTo(scale * 1.25, cx, cy);
|
|
73
|
+
else if (mode === 'out') zoomTo(scale / 1.25, cx, cy);
|
|
74
|
+
else if (mode === 'fit') fit();
|
|
75
|
+
else { zoomTo(1, 0, 0); vp.scrollLeft = 0; vp.scrollTop = 0; }
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
vp.addEventListener('wheel', function (e) {
|
|
80
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
81
|
+
e.preventDefault();
|
|
82
|
+
var r = vp.getBoundingClientRect();
|
|
83
|
+
zoomTo(scale * Math.exp(-e.deltaY * 0.0015), e.clientX - r.left, e.clientY - r.top);
|
|
84
|
+
}, { passive: false });
|
|
85
|
+
|
|
86
|
+
vp.addEventListener('pointerdown', function (e) {
|
|
87
|
+
if (e.button !== 0 || e.offsetX > vp.clientWidth || e.offsetY > vp.clientHeight) return;
|
|
88
|
+
if (!space && e.target.closest('.bcard')) return;
|
|
89
|
+
drag = { x: e.clientX, y: e.clientY, l: vp.scrollLeft, t: vp.scrollTop };
|
|
90
|
+
vp.classList.add('panning');
|
|
91
|
+
vp.setPointerCapture(e.pointerId);
|
|
92
|
+
});
|
|
93
|
+
vp.addEventListener('pointermove', function (e) {
|
|
94
|
+
if (!drag) return;
|
|
95
|
+
vp.scrollLeft = drag.l - (e.clientX - drag.x);
|
|
96
|
+
vp.scrollTop = drag.t - (e.clientY - drag.y);
|
|
97
|
+
});
|
|
98
|
+
function endDrag() { drag = null; vp.classList.remove('panning'); }
|
|
99
|
+
vp.addEventListener('pointerup', endDrag);
|
|
100
|
+
vp.addEventListener('pointercancel', endDrag);
|
|
101
|
+
|
|
102
|
+
board.addEventListener('mouseenter', function () { hover = true; });
|
|
103
|
+
board.addEventListener('mouseleave', function () { hover = false; });
|
|
104
|
+
document.addEventListener('keydown', function (e) {
|
|
105
|
+
if (e.code !== 'Space' || !hover || e.target.isContentEditable || /^(INPUT|TEXTAREA|SELECT|BUTTON)$/.test(e.target.tagName)) return;
|
|
106
|
+
space = true;
|
|
107
|
+
vp.classList.add('space');
|
|
108
|
+
e.preventDefault();
|
|
109
|
+
});
|
|
110
|
+
document.addEventListener('keyup', function (e) {
|
|
111
|
+
if (e.code !== 'Space') return;
|
|
112
|
+
space = false;
|
|
113
|
+
vp.classList.remove('space');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (window.ResizeObserver) {
|
|
117
|
+
var ro = new ResizeObserver(layout);
|
|
118
|
+
ro.observe(canvas);
|
|
119
|
+
board.querySelectorAll('.bcard').forEach(function (c) { ro.observe(c); });
|
|
120
|
+
}
|
|
121
|
+
window.addEventListener('resize', layout);
|
|
122
|
+
fit();
|
|
123
|
+
});
|
|
124
|
+
})();
|
package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/demo-plan/canvas.mdx
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<DesignBoard transitions={[{"from":"entry","to":"check-email","label":"submit email"},{"from":"check-email","to":"home","label":"open link"},{"from":"check-email","to":"expired","label":"link too old"}]}>
|
|
2
|
+
<Section title="Auth Entry">
|
|
3
|
+
<Artboard id="entry" title="Sign in">
|
|
4
|
+
<Screen surface="mobile" html={'<div class="wf-card"><h3>Welcome back</h3><p class="wf-muted">Enter your email</p><p>name@example.com</p><button class="primary">Send link</button></div>'} />
|
|
5
|
+
</Artboard>
|
|
6
|
+
<Artboard id="check-email" title="Check your email">
|
|
7
|
+
<Screen surface="mobile" html={'<div class="wf-card"><h3>Link sent</h3><p class="wf-muted">Open the link on this device.</p></div>'} />
|
|
8
|
+
</Artboard>
|
|
9
|
+
</Section>
|
|
10
|
+
<Section title="Outcome">
|
|
11
|
+
<Artboard id="home" title="Home">
|
|
12
|
+
<Screen surface="mobile" html={'<div class="wf-card"><h3>Signed in</h3><p class="wf-muted">Your projects</p></div>'} />
|
|
13
|
+
</Artboard>
|
|
14
|
+
<Artboard id="expired" title="Link expired">
|
|
15
|
+
<Screen surface="mobile" html={'<div class="wf-card"><h3>This link expired</h3><button class="primary">Send a new link</button></div>'} />
|
|
16
|
+
</Artboard>
|
|
17
|
+
<Annotation>Expired links never reveal whether the email has an account.</Annotation>
|
|
18
|
+
</Section>
|
|
19
|
+
</DesignBoard>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Demo Plan - Sign-in Flow
|
|
3
|
+
status: draft
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Sign-in flow
|
|
7
|
+
|
|
8
|
+
## Goal
|
|
9
|
+
|
|
10
|
+
Let a returning user sign in with an email link, and show a clear error when the link has expired. All data here is invented for the demo.
|
|
11
|
+
|
|
12
|
+
## Sequence
|
|
13
|
+
|
|
14
|
+
<Mermaid label="Sign-in sequence" source={"sequenceDiagram\n participant U as User\n participant A as App\n participant M as Mailer\n U->>A: request link\n A->>M: send link\n M-->>U: email\n U->>A: open link\n A-->>U: signed in"} />
|
|
15
|
+
|
|
16
|
+
## Open questions
|
|
17
|
+
|
|
18
|
+
<QuestionForm title="Open Questions" questions={[{"title":"How long is a link valid?","mode":"single","options":[{"label":"15 minutes","recommended":true},{"label":"1 hour"}]}]} />
|
package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/recap-demo/plan.mdx
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Offline drafts for the notes editor
|
|
3
|
+
subtitle: Drafts now survive a lost connection and sync when the network returns. Invented example.
|
|
4
|
+
kind: recap
|
|
5
|
+
pr: "#214"
|
|
6
|
+
branch: feat/offline-drafts
|
|
7
|
+
base: main
|
|
8
|
+
commit: 4f2a9c1
|
|
9
|
+
files: 4
|
|
10
|
+
additions: 186
|
|
11
|
+
deletions: 41
|
|
12
|
+
author: sample-author
|
|
13
|
+
date: 2026-01-15
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## What changed
|
|
17
|
+
|
|
18
|
+
Typing in the editor no longer stops when the connection drops. Each edit is written to a local queue first and replayed in order once the network is back.
|
|
19
|
+
|
|
20
|
+
## Before and after
|
|
21
|
+
|
|
22
|
+
<Compare>
|
|
23
|
+
<Before>
|
|
24
|
+
<Screen surface="mobile" caption="Connection lost">
|
|
25
|
+
<FrameScreen>
|
|
26
|
+
<Col full>
|
|
27
|
+
<Title text="Untitled note" />
|
|
28
|
+
<Lines n={3} widths={[90, 70, 40]} />
|
|
29
|
+
<Box dashed><Text value="Could not save. Retry" tone="muted" /></Box>
|
|
30
|
+
</Col>
|
|
31
|
+
</FrameScreen>
|
|
32
|
+
</Screen>
|
|
33
|
+
</Before>
|
|
34
|
+
<After>
|
|
35
|
+
<Screen surface="mobile" caption="Connection lost">
|
|
36
|
+
<FrameScreen>
|
|
37
|
+
<Col full>
|
|
38
|
+
<Title text="Untitled note" />
|
|
39
|
+
<Lines n={3} widths={[90, 70, 40]} />
|
|
40
|
+
<Row><IconSquare active /><Text value="Saved on this device" tone="muted" /></Row>
|
|
41
|
+
</Col>
|
|
42
|
+
</FrameScreen>
|
|
43
|
+
</Screen>
|
|
44
|
+
</After>
|
|
45
|
+
</Compare>
|
|
46
|
+
|
|
47
|
+
## Files
|
|
48
|
+
|
|
49
|
+
<ImplementationMap files={[
|
|
50
|
+
{ path: "editor/draft-queue.ts", change: "added", note: "Ordered local queue with replay" },
|
|
51
|
+
{ path: "editor/save.ts", change: "modified", note: "Write to the queue before the network call" },
|
|
52
|
+
{ path: "editor/status-pill.tsx", change: "modified", note: "New 'Saved on this device' state" },
|
|
53
|
+
{ path: "editor/legacy-retry.ts", change: "removed", note: "Replaced by the queue" },
|
|
54
|
+
]} />
|
|
55
|
+
|
|
56
|
+
## Behaviour
|
|
57
|
+
|
|
58
|
+
<Table columns={["State", "Before", "After"]} rows={[
|
|
59
|
+
["Offline typing", "Edits lost on reload", "Edits kept and replayed"],
|
|
60
|
+
["Reconnect", "Manual retry", "Automatic replay in order"],
|
|
61
|
+
]} />
|
|
62
|
+
|
|
63
|
+
<CodeTabs tabs={[
|
|
64
|
+
{ label: "draft-queue.ts", language: "ts", code: "export function enqueue(edit: Edit) {\n queue.push(edit);\n persist(queue);\n}" },
|
|
65
|
+
{ label: "save.ts", language: "ts", code: "await enqueue(edit);\nif (online) await flush();" },
|
|
66
|
+
]} />
|
|
67
|
+
|
|
68
|
+
<Callout tone="note" title="Review focus">
|
|
69
|
+
|
|
70
|
+
Check the replay order when two tabs edit the same note.
|
|
71
|
+
|
|
72
|
+
</Callout>
|