@fugood/bricks-ctor 2.24.9 → 2.24.11

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/compile/index.ts CHANGED
@@ -4,7 +4,7 @@ import upperFirst from 'lodash/upperFirst'
4
4
  import snakeCase from 'lodash/snakeCase'
5
5
  import omit from 'lodash/omit'
6
6
  import { parse as parseAST } from 'acorn'
7
- import type { ExportNamedDeclaration, FunctionDeclaration } from 'acorn'
7
+ import type { BlockStatement, ExportNamedDeclaration, FunctionDeclaration } from 'acorn'
8
8
  import escodegen from 'escodegen'
9
9
  import { makeSeededId } from '../utils/id'
10
10
  import { generateCalulationMap } from './util'
@@ -97,17 +97,28 @@ const compileProperty = (property, errorReference: string, result = {}) => {
97
97
  const compileScriptCalculationCode = (code = '') => {
98
98
  try {
99
99
  const program = parseAST(code, { sourceType: 'module', ecmaVersion: 2020 })
100
- // export function main() { ... }
101
- const declarationBody = (
102
- (program.body[0] as ExportNamedDeclaration).declaration as FunctionDeclaration
103
- )?.body
104
- return escodegen.generate(declarationBody, {
105
- format: {
106
- indent: { style: ' ' },
107
- semicolons: false,
100
+ // The stored config holds the bare function body, which codegen re-wraps as
101
+ // `export function main() { <code> }`. Unwrap it back to that body here.
102
+ let block = ((program.body[0] as ExportNamedDeclaration).declaration as FunctionDeclaration)
103
+ ?.body as BlockStatement | undefined
104
+ if (!block) return code || ''
105
+ // Earlier versions emitted the whole BlockStatement (braces included), so every
106
+ // compile -> codegen round-trip nested the body in one more `{ }`. Emit the inner
107
+ // statements instead, collapsing any wrapper blocks previous round-trips added so
108
+ // existing over-wrapped sandboxes heal on the next compile.
109
+ while (block.body.length === 1 && block.body[0].type === 'BlockStatement') {
110
+ block = block.body[0] as BlockStatement
111
+ }
112
+ return escodegen.generate(
113
+ { type: 'Program', body: block.body },
114
+ {
115
+ format: {
116
+ indent: { style: ' ' },
117
+ semicolons: false,
118
+ },
119
+ comment: true,
108
120
  },
109
- comment: true,
110
- })
121
+ )
111
122
  } catch {
112
123
  return code || ''
113
124
  }
@@ -709,6 +720,17 @@ const compileAutomation = (automationMap: AutomationMap) =>
709
720
  }),
710
721
  )
711
722
 
723
+ const buildDefaultExpandedState = (subspace: Subspace) => ({
724
+ brick: false,
725
+ generator: true,
726
+ canvas: (subspace.canvases || []).reduce((acc, canvas) => {
727
+ if (canvas?.id) acc[canvas.id] = false
728
+ return acc
729
+ }, {}),
730
+ property_bank: false,
731
+ property_bank_calc: true,
732
+ })
733
+
712
734
  export const compile = async (app: Application) => {
713
735
  await new Promise((resolve) => setImmediate(resolve, 0))
714
736
  const timestamp = Date.now()
@@ -789,7 +811,7 @@ export const compile = async (app: Application) => {
789
811
  property_bank: !subspace.unexpanded.data,
790
812
  property_bank_calc: !subspace.unexpanded.dataCalculation,
791
813
  }
792
- : undefined,
814
+ : buildDefaultExpandedState(subspace),
793
815
  layout: {
794
816
  width: subspace.layout?.width,
795
817
  height: subspace.layout?.height,
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@fugood/bricks-ctor",
3
- "version": "2.24.9",
3
+ "version": "2.24.11",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "typecheck": "tsc --noEmit",
7
7
  "build": "bun scripts/build.js"
8
8
  },
9
9
  "dependencies": {
10
- "@fugood/bricks-cli": "^2.24.9",
10
+ "@fugood/bricks-cli": "^2.24.11",
11
11
  "@huggingface/gguf": "^0.3.2",
12
12
  "@iarna/toml": "^3.0.0",
13
13
  "@modelcontextprotocol/sdk": "^1.15.0",
@@ -25,6 +25,7 @@ The primary way to orchestrate multi-step flows. A single event can contain an a
25
25
  - Use `waitAsync: true` to await async actions before the next step
26
26
  - Use `dataParams` + `mapping` to pass event data downstream
27
27
  - This is the "glue" that wires generators, state, and UI together
28
+ - For Generator result UI: await Generator, write done/version Data; route/current Data stays identity.
28
29
 
29
30
  Sequential `PROPERTY_BANK` / `PROPERTY_BANK_EXPRESSION` actions in one chain read the data values that existed when the chain started. If a later action needs to read what an earlier action wrote, set `waitAsync: true` on the earlier action.
30
31
 
@@ -87,6 +87,17 @@ const triggerCalc: EventAction = {
87
87
  - When a **later action reads the calc's outputs**, set `waitAsync: true` on the `PROPERTY_BANK_COMMAND` action itself — it awaits the full calc chain including output writes.
88
88
  - A dataParam's `value` acts as an execution gate: `{ input: () => d, value: false }` skips that trigger (combine with `mapping` for conditional runs).
89
89
 
90
+ ## One trigger source per result panel
91
+
92
+ A panel that shows the result of an async action (Generator HTTP/LLM response, request telemetry) reads from a calc whose **trigger should come from a single source**: either the live async outlet, or a completion marker the action chain writes — not both.
93
+
94
+ - **Outlet-triggered** (`{ data: () => dResponse, trigger: true }`): the calc reruns as outlets land. Use when each outlet is only written once its value is final.
95
+ - **Marker-triggered**: run the generator with `waitAsync: true`, then write a `done` Data the calc triggers on. Use when several outlets settle separately and the panel must wait for all of them.
96
+
97
+ Wiring both at once — triggering on the live outlet _and_ gating on a separately-written marker — lets the calc run before the outlets have settled (it renders `undefined`/stale) while the marker path masks it intermittently. A panel that updates "sometimes" usually has two competing triggers: pick one, and order it with `waitAsync` so the trigger fires after the values it reads are written.
98
+
99
+ Scope the panel's calc to the **single value it derives** (the formatted display string), and write sibling status labels — running/done, progress, selected route — imperatively in the event chain with `PROPERTY_BANK`. A calc rewrites _every_ one of its outputs on each run, so a return that omits a key writes `undefined` to that output Data (see [Field Rules](#field-rules-defaults-and-constraints)): folding status labels into a multi-output result calc resets them to `undefined` on any run that returns only the derived value. If a calc genuinely must drive several outputs, return all of their keys every run.
100
+
90
101
  ## Script Sandbox
91
102
 
92
103
  Scripts run in `use strict` mode as a function body — top-level `return` returns the calc result. No `fetch`, `XMLHttpRequest`, or `require` in any mode: I/O belongs to Generators.
@@ -220,6 +231,8 @@ const appendHistory: DataCalculationScript = {
220
231
  | `PROPERTY_BANK_COMMAND` does nothing | Auto calc + `trigger: false` input, or `input` doesn't reference an input Data of the calc | Command a `trigger: true` input (auto) or any input (manual) |
221
232
  | Compile error `Not allow duplicate set property id...` | Auto mode with same Data as input and output | Use `triggerMode: 'manual'`, or split into separate Data |
222
233
  | Calc reads stale value written earlier in the same chain | Missing `waitAsync: true` on the preceding write | Set `waitAsync: true` on the write action |
234
+ | Result/telemetry panel shows `undefined` or stale data intermittently | Calc triggered by two sources at once (live outlet + a separately-written marker) | Trigger from one source — see [One trigger source per result panel](#one-trigger-source-per-result-panel) |
235
+ | Sibling status Data (selected/progress) resets to `undefined` after a calc runs | Multi-output result calc whose return omitted those keys on that run | Scope the calc to one output and write status labels imperatively, or return every output key each run — see [One trigger source per result panel](#one-trigger-source-per-result-panel) |
223
236
  | Works in Simulator, fails on device | V8 vs Hermes/JSC engine difference | Verify on device (Path 2); avoid engine-sensitive parsing |
224
237
  | `console.log` shows nothing | Console only emits during DevTools debug sessions | Attach DevTools, or write debug values to an output Data |
225
238
  | `Promise`/`setTimeout` undefined, or `Async mode is required` error | `enableAsync: false` | Set `enableAsync: true` |
@@ -31,6 +31,7 @@ const mediaListGenerator: GeneratorMediaFlow = {
31
31
  description: '',
32
32
  property: {
33
33
  boxId: 'promo-box-id',
34
+ passcode: 'box-read-only-passcode',
34
35
  },
35
36
  outlets: {
36
37
  files: () => promoFilesData, // Array of file objects
@@ -40,6 +41,12 @@ const mediaListGenerator: GeneratorMediaFlow = {
40
41
  }
41
42
  ```
42
43
 
44
+ When hand-writing `GENERATOR_MEDIA_FLOW`, do not use a bare `boxId`. The editor
45
+ flow creates the required authorization, but manual config must include either
46
+ `property.passcode` from a Media Box passcode or `property.accessToken`.
47
+ Without one, devices cannot read the box and the generator will report an
48
+ authorization error.
49
+
43
50
  ## Property Kinds for Media
44
51
 
45
52
  Link Data properties to Media Flow for asset selection:
@@ -26,6 +26,15 @@ What does **not** count as done:
26
26
 
27
27
  If any item is unmet, the work is mid-iteration. Say so explicitly to the user; offer a precise list of what remains.
28
28
 
29
+ ## Debug loop discipline
30
+
31
+ Before cycling on a runtime or Automation bug, establish what you can observe. A green or red Automation alone is often not enough to explain a race or branch miss.
32
+
33
+ - Use one repeatable probe at a time: DevTools state/storage/runtime reads, network/console output, Automation screenshots, or a targeted screenshot after a known event.
34
+ - If branch execution or race timing is unclear, add the smallest temporary log/trace point in the relevant data-calculation script, event handler, or generator path, then compile and rerun the same probe.
35
+ - Do not bounce between CLI, DevTools, and Automation without a hypothesis. State what signal will prove or disprove the next fix before changing code again.
36
+ - Remove temporary traces before declaring done, unless the user asked for persistent diagnostics or the trace is intentionally part of the fix.
37
+
29
38
  ## Path 1 — Electron preview (no device required)
30
39
 
31
40
  The default loop. Always available; deterministic; no device wear; safe for side-effecting flows because nothing real fires.
@@ -2,7 +2,7 @@
2
2
  name: bricks-design
3
3
  description: >-
4
4
  Visual design discipline for Applications and Subspaces — type, palette, asset
5
- acquisition, design language, system commitment, visual rhythm, brand. TRIGGER
5
+ acquisition, design language, DESIGN.md system, visual rhythm, brand. TRIGGER
6
6
  for visual / aesthetic / system / style / brand-asset work even when named in
7
7
  product terms — slideshow, pitch deck, explainer, kiosk, signage, menu board,
8
8
  lobby, wayfinding, retail, museum, transit; translate or rebuild from Figma /
@@ -40,7 +40,8 @@ How to inspect, by source type:
40
40
  - **Website / docs site URL** — drive a browser automation tool (browser-MCP, Playwright/Puppeteer-style MCP server, `agent-browser`-equivalent skill, or any host-provided browser tool). Visit the home page, every primary navigation entry, every page the user explicitly named, and every page that looks like it carries the brand's visual signature (hero, product, gallery, customers). Capture a full-page screenshot of each. Then read each screenshot back via the host's image-reading capability so you actually *see* it. If no browser automation is available, ask the user for screenshots before proceeding.
41
41
  - **Figma / design-tool link** — use a Figma-MCP / design-tool MCP if available to enumerate every frame and image-export each; otherwise ask the user for PNG exports of every frame. Inspect each image.
42
42
  - **HTML project on disk** — serve and screenshot every route via a browser tool, the same way as a public URL. Don't infer visuals from the source HTML/CSS.
43
- - **PDF / brand book / slide deck** — read every page via the host's PDF-reading capability (page-by-page if the document is long). Do not summarize from a text extraction.
43
+ - **PDF / brand book / slide deck** — read every page via the host's PDF-reading capability (page-by-page if the document is long). Do not summarize from a text extraction. If the host only exposes a cover preview or has no PDF capability, use an installed PDF skill when available. If no PDF skill or local PDF tool is installed and you cannot inspect the PDF fully, stop and respond in chat with the limitation instead of designing from partial evidence. Never proceed from only page 1.
44
+ - **Large rendered PDF pages** — do not hand the vision tool 4-13 MB page PNGs and call the PDF unreadable. Downsample to inspection-sized JPEG/PNG previews, split long PDFs into page batches, and keep a page coverage checklist so every page is either inspected or explicitly marked inaccessible.
44
45
  - **Local images / screenshots** — read each one via the image-reading tool.
45
46
  - **Video walkthrough** — ask the user to provide key-frame screenshots, or to describe each state explicitly. Do not claim to "watch" a video you cannot actually frame-step.
46
47
 
@@ -55,7 +56,7 @@ Pass-based delivery, not sprint-to-finished. Full discipline in [`references/wor
55
56
  - Branded brief → [`references/when-the-brief-is-branded.md`](references/when-the-brief-is-branded.md) (Media Flow protocol).
56
57
  - Figma / HTML / screenshot input → [`references/translating-inputs.md`](references/translating-inputs.md).
57
58
  - Presentation / slideshow / intro / explainer / storyboard → [`references/presentation-and-slideshow.md`](references/presentation-and-slideshow.md) (Canvas-graph shape decision, hero continuity).
58
- 2. **Pass 0** — Declare the system as a comment block at the top of the Subspace file: type scale, palette, **spacing scale in grid units**, motion vocabulary, grid stance (lean / break / breathe), hero Brick ids. The grid substrate is given by the runtime (Truth #6); the design decisions sit on top.
59
+ 2. **Pass 0** — Write the project's [`DESIGN.md`](references/design-md.md): named tokens in the frontmatter (type scale, palette, **spacing scale in grid units**, motion vocabulary, grid stance, hero Brick ids) plus Deployment Context and the Visual Theme section. The grid substrate is given by the runtime (Truth #6); the design decisions sit on top. The Subspace file carries only a one-line pointer to DESIGN.md; every Brick cites its tokens by name.
59
60
  3. **Pass 1 — Showcase Canvas lockdown.** Build one Canvas fully lit, verify, show the user. This is the cheapest moment to redirect; do not build the full Canvas graph before sign-off.
60
61
  4. **Pass 2** — Build out the full Canvas graph with hero ids shared across Canvases (Truth #3 as narrative principle). Mid-review checkpoint partway through.
61
62
  5. **Pass 3** — Polish: spacing-scale adherence, density rhythm, Standby Transition vocabulary consistent, multilingual fits.
@@ -79,7 +80,7 @@ The load-bearing laws. Each is unpacked in [`references/architecture-truths.md`]
79
80
 
80
81
  ## Commit to a design language
81
82
 
82
- A BRICKS Application that doesn't commit to a design language reads as generic regardless of how well it runs. After Direction Advisor (or directly, when the brief is concrete enough), pick a visual language from [`references/design-languages.md`](references/design-languages.md) — Swiss Editorial / Kenya Hara emptiness / Field.io motion poetics / Brutalist web / Y2K futurist-retro / etc. — and write its system declaration as a comment block at the top of the Subspace file. Every subsequent choice cites the block; adding a value not in the block requires updating the block first. That's what makes the system structural rather than aspirational.
83
+ A BRICKS Application that doesn't commit to a design language reads as generic regardless of how well it runs. After Direction Advisor (or directly, when the brief is concrete enough), pick a visual language from [`references/design-languages.md`](references/design-languages.md) — Swiss Editorial / Kenya Hara emptiness / Field.io motion poetics / Brutalist web / Y2K futurist-retro / etc. — and commit it as named tokens in the project's [`DESIGN.md`](references/design-md.md). Every subsequent choice cites a token by name; adding a value not already in the tokens requires adding the token first. That's what makes the system structural rather than aspirational.
83
84
 
84
85
  The commitment principle: when unsure, do *more* of what defines the chosen language, not less. A style executed at 30% reads as hesitant; at 80% it reads as deliberate. Soften signature moves and you've abandoned the language for nowhere.
85
86
 
@@ -98,9 +99,9 @@ Two reference files cover the second-order rules. Read them when the design is n
98
99
 
99
100
  ## Asset discipline
100
101
 
101
- Brand work succeeds or fails on assets, not on colors. Logo, product photography, UI screenshots, scene photography, and motion assets are first-class — colors and fonts are auxiliary. Acquire them through the protocol in [`references/when-the-brief-is-branded.md`](references/when-the-brief-is-branded.md): five steps, the 5-10-2-8 quality bar with five named scoring dimensions, three fallback paths per asset category, and structural enforcement that Bricks reference Media Flow Data via DataLink rather than embed, redraw, or skip.
102
+ Brand work succeeds or fails on assets, not on colors. Logo, product photography, UI screenshots, scene photography, and motion assets are first-class — colors and fonts are auxiliary. Acquire them through the protocol in [`references/when-the-brief-is-branded.md`](references/when-the-brief-is-branded.md): five steps, the 5-10-2-8 quality bar with five named scoring dimensions, three fallback paths per asset category, and structural enforcement that Bricks reference Media Flow Data via DataLink rather than embed, redraw, or skip. The acquired assets, scores, and bindings land in the **Assets & Brand** sections of [`DESIGN.md`](references/design-md.md) — one artifact, not a separate brand spec.
102
103
 
103
- When an asset category genuinely cannot be acquired and the user has no plan to provide one, the priority is: stop and ask (always for logo) → compose the host environment's image / motion generators (canvas-design / imagen / generative MCP), brand-reference-anchored, scored at the same bar → labeled placeholder with the gap tracked in `brand-spec.md`. **This skill does not duplicate generative or composition skills — it composes them.** Where the host has none, surface the constraint to the user; don't silently degrade.
104
+ When an asset category genuinely cannot be acquired and the user has no plan to provide one, the priority is: stop and ask (always for logo) → compose the host environment's image / motion generators (canvas-design / imagen / generative MCP), brand-reference-anchored, scored at the same bar → labeled placeholder with the gap tracked under DESIGN.md's **Gaps & placeholders**. **This skill does not duplicate generative or composition skills — it composes them.** Where the host has none, surface the constraint to the user; don't silently degrade.
104
105
 
105
106
  ## Anti-slop, terse
106
107
 
@@ -130,7 +131,7 @@ Ship a one-paragraph trade-off note alongside so the user can pick or blend with
130
131
 
131
132
  A single hero-Canvas screenshot is **not** done. A "looks roughly like the reference" handwave is **not** done. A claim of done without screenshots in evidence is **not** done. If you have not produced and reviewed a screenshot of every Canvas, you are still mid-iteration and must say so explicitly to the user.
132
133
 
133
- **Self-critique pass before declaring done** — every Canvas scored on 5 dimensions (system commitment / visual hierarchy / craft / functional fit / originality), anti-slop top-10 swept clean, < 8 scores either fixed or surfaced as accepted trade-offs. See [`references/design-critique.md`](references/design-critique.md). Verification proves it runs; critique proves it's good — both required.
134
+ **Self-critique pass before declaring done** — every Canvas scored on 5 dimensions (system commitment / visual hierarchy / craft / functional fit / originality), checked section by section against [`DESIGN.md`](references/design-md.md) (does every colour, gap, and motion in the render trace to a declared token?), anti-slop top-10 swept clean, < 8 scores either fixed or surfaced as accepted trade-offs. See [`references/design-critique.md`](references/design-critique.md). Verification proves it runs; critique proves it's good — both required.
134
135
 
135
136
  For the toolchain itself — `compile` / `preview` MCP usage, `bun preview` flags, Project Automation cases, on-device DevTools setup, the Path 1/2/3 decision rule, and per-deployment-shape verification checklists — see the `bricks-ctor` skill's `rules/verification-toolchain.md`.
136
137
 
@@ -150,6 +151,7 @@ Interaction / flow / journey / affordance / feedback / recovery / accessibility
150
151
 
151
152
  | When you need to... | Read |
152
153
  |---|---|
154
+ | Write or update the project's design-system artifact (tokens, schema, brand merge) | [`references/design-md.md`](references/design-md.md) |
153
155
  | Decide when to ask vs build; pace the work across passes | [`references/workflow.md`](references/workflow.md) |
154
156
  | Deepen any of the 10 truths | [`references/architecture-truths.md`](references/architecture-truths.md) |
155
157
  | Tune for performance / offline | [`references/performance.md`](references/performance.md) |
@@ -20,19 +20,19 @@ If a dimension cannot be scored ≥ 8 within the deployment's constraints, surfa
20
20
 
21
21
  ### 1. System commitment
22
22
 
23
- *Does the work cite the declared system (the comment block at the top of the Subspace), and does deviation have rationale?*
23
+ *Does the work cite the declared system (the named tokens in `DESIGN.md`), and does deviation have rationale?*
24
24
 
25
25
  | Score | Rubric |
26
26
  |---|---|
27
- | 9–10 | Every brick property points back to a Data token, an `ApplicationFont` entry, or the system block. Deviations carry a one-line comment explaining why. |
27
+ | 9–10 | Every brick property points back to a DESIGN.md token, a Data token, or an `ApplicationFont` entry. Deviations carry a one-line comment explaining why. |
28
28
  | 7–8 | Direction is correct; 1–2 hardcoded values that should be tokens, or 1–2 deviations without rationale. |
29
29
  | 5–6 | Visible drift — a third type family slipped in, a fourth color appeared mid-flow, or a brick redrew an asset that exists in Media Flow. |
30
- | 3–4 | The system block is decorative; the work doesn't follow it. |
31
- | 1–2 | No system block, or the block contradicts the work. |
30
+ | 3–4 | DESIGN.md is decorative; the work doesn't follow it. |
31
+ | 1–2 | No DESIGN.md, or its tokens contradict the work. |
32
32
 
33
33
  **Audit moves:**
34
- - Open the system block. Pick five values from it (a color, a type size, a Standby duration, a margin, a Brick template). For each, check that the work uses it consistently.
35
- - Pick five values *from* the work (a `backgroundColor` literal, a `fontSize`, a Standby easing, a hex code in a Rect, a brick template choice). For each, check that the value traces back to the system block or has rationale.
34
+ - Open DESIGN.md. Pick five tokens from it (a color, a type size, a Standby duration, a margin, a Brick template). For each, check that the work uses it consistently.
35
+ - Pick five values *from* the work (a `backgroundColor` literal, a `fontSize`, a Standby easing, a hex code in a Rect, a brick template choice). For each, check that the value traces back to a DESIGN.md token or has rationale.
36
36
  - If signature moves of the chosen design language (per [`design-languages.md`](design-languages.md)) are absent, score drops regardless of token-discipline. Swiss Editorial without numbered folios and hairline rules is not Swiss Editorial; it's "vaguely minimal."
37
37
 
38
38
  ### 2. Visual hierarchy
@@ -67,7 +67,7 @@ If a dimension cannot be scored ≥ 8 within the deployment's constraints, surfa
67
67
 
68
68
  **Audit moves:**
69
69
  - **Grid stance match.** Off-grid placements should serve the declared language (Brutalist break, Sagmeister theatrical, Risograph hand-placed). Off-grid that drifted in accidentally — flag and snap. A Swiss Editorial Canvas with a Brick at `(3.5, 11.2)` is a tell.
70
- - **Spacing-scale adherence.** Open the style declaration block. Read the declared spacing scale (e.g., `2 / 4 / 8 / 16 gu`). Pick ten inter-Brick gaps across the Canvas — every one must come from that set. Gaps of 7 / 9 / 11 / 13 across siblings = arithmetic drift, the agent picked values ad-hoc instead of from the scale. Snap to the nearest scale value or update the scale (and re-check everywhere).
70
+ - **Spacing-scale adherence.** Open DESIGN.md. Read the declared `spacing` tokens (e.g., `gap-s 2 / gap-m 4 / gap-l 8 / margin 8`). Pick ten inter-Brick gaps across the Canvas — every one must come from that set. Gaps of 7 / 9 / 11 / 13 across siblings = arithmetic drift, the agent picked values ad-hoc instead of from the scale. Snap to the nearest scale value or update the scale (and re-check everywhere).
71
71
  - List every distinct colour used in the Canvas. > 4 = deduction (excluding intentional photography palette spreads).
72
72
  - List every distinct type family. > 2 = deduction.
73
73
  - List every distinct Standby easing / duration combination. > 3 = deduction.
@@ -155,7 +155,7 @@ Walk this list every time. Each is a pattern that consistently slips into agent-
155
155
  **Tell:** a brick's `pressable: 'enabled'` carries a press scale-down animation, but the deployment is a 75" no-touch signage panel. **Why bad:** the affordance promises something the hardware cannot deliver; the design reads as "ported from web." **Fix:** drop hover on no-touch; convert to a Switch driven by a non-pointer signal (sensor, timer, peripheral Generator) if the visual intent matters.
156
156
 
157
157
  ### 10. AI-generated brand imagery without a real brand-reference anchor
158
- **Tell:** a generated "brand-appropriate" hero image that nobody at the brand has ever seen, produced from a prompt like "modern tech product on a gradient." **Why bad:** generation without anchoring produces uncanny-valley assets that feel adjacent to the brand without being the brand. **Fix:** anchor every generation on at least one verified brand asset (logo, real photo, official color sample); apply 5-10-2-8; log generation metadata in `brand-spec.md`. (See [Media Flow protocol § Creating assets when missing](when-the-brief-is-branded.md#creating-assets-when-missing).)
158
+ **Tell:** a generated "brand-appropriate" hero image that nobody at the brand has ever seen, produced from a prompt like "modern tech product on a gradient." **Why bad:** generation without anchoring produces uncanny-valley assets that feel adjacent to the brand without being the brand. **Fix:** anchor every generation on at least one verified brand asset (logo, real photo, official color sample); apply 5-10-2-8; log generation metadata in DESIGN.md's Assets & Brand. (See [Media Flow protocol § Creating assets when missing](when-the-brief-is-branded.md#creating-assets-when-missing).)
159
159
 
160
160
  ## Bonus failure modes (BRICKS-specific, less common)
161
161
 
@@ -35,25 +35,31 @@ If you find yourself softening signature moves to "make it more accessible" —
35
35
  3. **Build a 3-cell preview** — three minimal Subspaces (one Canvas each) the user can render. Don't produce finished work; the preview is a chooser.
36
36
  4. **Once the user picks, drop out of Advisor mode** and continue the workflow rooted in that direction.
37
37
 
38
- ## Style declaration ritual
39
-
40
- After the direction is picked, write the committed system as a comment block at the top of the Subspace file. Every subsequent choice should cite this block. Adding a value not in the block requires updating the block first — that's what makes the system structural rather than aspirational.
41
-
42
- ```ts
43
- /**
44
- * System: Swiss Editorial (Pentagram lineage)
45
- * Archetype: glance
46
- * Type: <Brand Sans> 4 / 8 / 16 gu · single family · weight contrast
47
- * Color: ink #1A1A1A on cream #F5F0E6 · accent #FF3C00 (callouts only)
48
- * Spacing: 2 / 4 / 8 / 16 gu (use only these for inter-Brick gaps)
49
- * Grid stance: lean (strict alignment, generous margins 4–8 gu)
50
- * Motion: ease-out 280ms entrance · ease-in 180ms exit · no loops
51
- * Heroes: logo (id `brand-logo`) · folio (id `folio-num`)
52
- * Asset emphasis: logo + 1 hero photograph per canvas · no decorative imagery
53
- */
38
+ ## Style declaration ritual — into DESIGN.md
39
+
40
+ After the direction is picked, commit the system as named tokens in the project's [`DESIGN.md`](design-md.md) frontmatter (full schema there). Every subsequent choice cites a token by name; adding a value not already in the tokens requires adding the token first — that's what makes the system structural rather than aspirational.
41
+
42
+ ```yaml
43
+ system: Swiss Editorial (Pentagram lineage)
44
+ archetype: glance
45
+ colors:
46
+ ink: "#1A1A1A" # body text never pure black
47
+ ground: "#F5F0E6" # default Canvas background
48
+ accent: "#FF3C00" # callouts only one per Canvas
49
+ type: # sizes in grid units
50
+ display: { family: "<Brand Sans>", size: 16, weight: 400 }
51
+ heading: { family: "<Brand Sans>", size: 8, weight: 400 }
52
+ body: { family: "<Brand Sans>", size: 4, weight: 400 }
53
+ spacing: { gap-s: 2, gap-m: 4, gap-l: 8, margin: 8 } # inter-Brick gaps come only from here
54
+ grid: lean # strict alignment, generous margins
55
+ motion:
56
+ entrance: { ease: ease-out, ms: 280 }
57
+ exit: { ease: ease-in, ms: 180 }
58
+ loops: none
59
+ heroes: [brand-logo, folio-num] # persist across Canvases → auto-tween (Truth #3)
54
60
  ```
55
61
 
56
- The grid itself is given by the runtime (Truth #6). What the block commits to is the set of design decisions on top of the grid: type scale, palette, **spacing scale in grid units** (small enumeration so inter-Brick gaps stay consistent across many siblings), grid stance, motion vocabulary, and hero Brick ids. The comment is not for the runtime; it is for *you*, when you next look at the file and consider adding a third colour or a fourth gap value. The block's existence is what holds the line.
62
+ The grid itself is given by the runtime (Truth #6). What the tokens commit to is the set of design decisions on top of the grid: type scale, palette, **spacing scale in grid units** (small enumeration so inter-Brick gaps stay consistent across many siblings), grid stance, motion vocabulary, and hero Brick ids. DESIGN.md is not for the runtime; it is for *you*, when you next consider adding a third colour or a fourth gap value. Its existence is what holds the line.
57
63
 
58
64
  ---
59
65
 
@@ -262,4 +268,4 @@ When the user picks an awkward combination ("transact + Sagmeister"), surface th
262
268
 
263
269
  The 10 directions are anchors. Real work often blends — "Kenya Hara emptiness with one Risograph spot-color accent for the call-to-action" is a legitimate combination if executed deliberately. What's not legitimate is blending three to dilute commitment.
264
270
 
265
- When inventing a direction not in the library: write its system declaration block as if it were one of the entries above (pitch / flagship / keywords / fits / BRICKS execution / wrong-for). If you can't, the direction isn't real yet — keep working on it before committing.
271
+ When inventing a direction not in the library: write its system declaration into DESIGN.md as if it were one of the entries above (pitch / flagship / keywords / fits / BRICKS execution / wrong-for). If you can't, the direction isn't real yet — keep working on it before committing.
@@ -0,0 +1,225 @@
1
+ # DESIGN.md — the project's design-system artifact
2
+
3
+ Every BRICKS design project commits its system to a single durable file: **`DESIGN.md`**, at the project root. It is the source of truth for type, palette, spacing, motion, brand assets, and deployment context — written once in Pass 0, cited by every Brick afterward, and read back at the top of every later session.
4
+
5
+ The format rests on four ideas:
6
+
7
+ 1. **One named, durable markdown artifact** — not a system scattered across code comments that dies with the file. Markdown is the format an agent reads best; there is nothing to parse.
8
+ 2. **Named tokens in YAML frontmatter, referenced by name in the prose.** Declare `accent` once; write `{colors.accent}` everywhere. The body never restates a raw hex or size — one place to change, no drift.
9
+ 3. **Real design depth, not surface tokens.** Each token carries its *role* (which colour is CTA vs. body vs. hairline) and the system carries its *signature moves* (the one gesture that makes it recognisable) and the *why* behind each rule.
10
+ 4. **An agent guide** that tells the next agent how to *use* the file.
11
+
12
+ The nine-section schema below is the web-design convention reframed for BRICKS. BRICKS is a fixed-frame, grid-substrate, state-machine runtime, so three of the nine canonical sections are reframed rather than copied — see the mapping table. Importing web habits (breakpoints, scroll, CSS elevation) through this file is the fastest way to fight the runtime.
13
+
14
+ ## DESIGN.md replaces two older artifacts
15
+
16
+ This file is the merge point for what earlier versions of this skill kept apart:
17
+
18
+ - The **style-declaration comment block** at the top of the Subspace file → its tokens move into the DESIGN.md frontmatter. The Subspace comment shrinks to a one-line pointer (see [§ The Subspace pointer](#the-subspace-pointer)).
19
+ - **`brand-spec.md`** → its asset bindings, score log, and gaps become the **Assets & Brand** sections of DESIGN.md. One artifact, not two. (The acquisition *protocol* still lives in [`when-the-brief-is-branded.md`](when-the-brief-is-branded.md); DESIGN.md is where its output lands.)
20
+
21
+ A project has exactly one DESIGN.md. A multi-Subspace Application shares it; per-Subspace deviations are noted as scoped overrides inside it, never as a competing second file.
22
+
23
+ ## The nine sections, mapped to BRICKS
24
+
25
+ | Web section | BRICKS section | Why it changes |
26
+ |---|---|---|
27
+ | Visual Theme & Atmosphere | **1. Visual Theme & Atmosphere** | Unchanged in spirit — adds the picked visual language × interaction archetype. |
28
+ | Color Palette & Roles | **2. Colour Palette & Roles** | Colours used in 2+ places become theme-token Data bound via DataLink (Truth #2). |
29
+ | Typography Rules | **3. Typography** | Sizes are **grid units**, not px. Families are `ApplicationFont` entries with a fallback ladder. |
30
+ | Component Stylings | **4. Brick & Generator Stylings** | Bricks and Generators, not DOM components. Pressable affordance must read as visually distinct; Generators are headless/async (Truth #5). |
31
+ | Layout Principles | **5. Grid, Spacing & Rhythm** | Frames are grid units (Truth #6). No scroll, overflow, or reflow (Truth #10). |
32
+ | Depth & Elevation | **6. Motion, Standby & Depth** | No CSS z-index / box-shadow model. Depth = Brick draw order + Standby entrance offsets (Truth #7) + shared-id auto-tween (Truth #3). |
33
+ | Do's and Don'ts | **7. Do's & Don'ts** | Each rule tied to what it protects; folds in this system's anti-slop. |
34
+ | Responsive Behavior | **8. Hardware & Orientation** | **Not breakpoints.** Fixed target resolution + orientation; fleet variants are separate Canvas sets or device-scoped Data, never media queries. |
35
+ | Agent Prompt Guide | **9. Agent Guide** | How to cite tokens, add tokens, and verify against this file. |
36
+
37
+ Two BRICKS-native sections have no web equivalent and sit up front: **Deployment Context** (captures Priority #0 durably) and **Assets & Brand** (the merged `brand-spec.md`).
38
+
39
+ ## Token-reference discipline — the load-bearing habit
40
+
41
+ This is what separates a system from an accumulation:
42
+
43
+ - **Declare every reusable value as a named token in the frontmatter.** Reference it by name in the prose and in your reasoning: `{colors.ink}`, `{type.display}`, `{spacing.gap-m}`, `{heroes.brand-logo}`.
44
+ - **Never restate a raw value in the body.** Writing `#1A1A1A` inline instead of `{colors.ink}` forks the source of truth; the next edit updates one and not the other.
45
+ - **Adding a value not already in the tokens requires adding the token first.** Want a third colour or a fourth gap? Edit the frontmatter, then use it. The "first" is the whole mechanism — it forces a deliberate pause where averaging-toward-generic would otherwise sneak a value in.
46
+ - **Sizes and gaps are in grid units (`gu`), never pixels.** The grid substrate is given by the runtime (Truth #6); the design sits on top of it.
47
+ - **Shared colours (2+ uses) become theme-token Data** bound via DataLink, with the token name matching the DESIGN.md key. Single-use decorative colours may stay hardcoded (Truth #2 is loose by design).
48
+
49
+ ## The template
50
+
51
+ Copy this verbatim into `DESIGN.md` and fill it. Every section is required; an empty section is a declared gap, not an omission.
52
+
53
+ ````markdown
54
+ ---
55
+ name: <Application name> — design system
56
+ system: <visual language, e.g. Swiss Editorial (Pentagram lineage)>
57
+ archetype: <glance | browse | interact | transact | monitor | dwell>
58
+
59
+ deployment:
60
+ hardware: <size · resolution · orientation · touch? · viewing distance>
61
+ scene: <where the screen lives, what the user does in front of it>
62
+ network: <always-on | intermittent | offline>
63
+ languages: [<en>, <...>]
64
+ peripherals: [<camera | BLE | MQTT | NFC | payment | printer | sensors | none>]
65
+ watchdog: <fleet-managed | single-instance>
66
+
67
+ colors: # role in the comment; hex is the value
68
+ ink: "#1A1A1A" # body text — deep, never pure black
69
+ ground: "#F5F0E6" # default Canvas background
70
+ accent: "#FF3C00" # callouts only — at most one per Canvas
71
+ hairline: "#E3E0D6" # 1gu rules and card edges
72
+ # forbidden: [<colours the brand explicitly never uses>]
73
+
74
+ type: # sizes in grid units (gu)
75
+ display: { family: "<Brand Sans>", size: 16, weight: 300, tracking: -0.02 }
76
+ heading: { family: "<Brand Sans>", size: 8, weight: 400, tracking: 0 }
77
+ body: { family: "<Brand Sans>", size: 4, weight: 400, tracking: 0 }
78
+ caption: { family: "<Brand Sans>", size: 3, weight: 400, tracking: 0 }
79
+
80
+ spacing: # inter-Brick gaps come ONLY from this set (gu)
81
+ gap-s: 2
82
+ gap-m: 4
83
+ gap-l: 8
84
+ margin: 8
85
+
86
+ grid: lean # lean (strict align, generous margin) | break | breathe
87
+
88
+ motion: # Standby Transition vocabulary (Truth #7)
89
+ entrance: { standbyMode: "<offset, e.g. translateY 4gu>", ease: ease-out, ms: 280, stagger: 40 }
90
+ exit: { ease: ease-in, ms: 180 }
91
+ loops: <none | only on {brick-id}>
92
+
93
+ heroes: # Brick ids reused across Canvases → runtime auto-tweens them (Truth #3)
94
+ - brand-logo
95
+ - folio-num
96
+
97
+ fonts: # ApplicationFont entries → Application.applicationSettings.fonts
98
+ - { name: "<Brand Sans>", url: "<media-flow-or-cdn-url>", md5: "<optional>" }
99
+ ---
100
+
101
+ # <Application name> — Design System
102
+ > Captured: <YYYY-MM-DD> · Sources: <reference pages / Figma / brand book> · Coverage: <complete | partial | inferred>
103
+
104
+ ## Deployment Context
105
+ The five Priority #0 facts in prose, plus peripherals and watchdog. Every visual
106
+ decision below is downstream of these. If any of the first five was assumed rather
107
+ than confirmed, mark it **ASSUMED** here so the next agent knows what to re-check.
108
+
109
+ ## 1. Visual Theme & Atmosphere
110
+ What the system *says* in two or three sentences. Name the picked **visual language**
111
+ and **interaction archetype**, then the **signature moves** — the one or two gestures
112
+ that make this system recognisable at a glance (e.g. "oversized folio numerals + a
113
+ single hairline rule per Canvas"). When unsure, commit *harder* to these, not softer.
114
+
115
+ **Key characteristics:** 4–6 bullets, each a concrete, checkable rule.
116
+
117
+ ## 2. Colour Palette & Roles
118
+ Each token by name with its job. Reference `{colors.<name>}` — never the raw hex.
119
+ - `{colors.ink}` — body text on `{colors.ground}`. Deep charcoal, never pure black.
120
+ - `{colors.accent}` — callouts and the single emphasis per Canvas. Never a body colour.
121
+ - `{colors.hairline}` — 1gu rules, card edges.
122
+ - Forbidden: <colours the brand never uses>, if any.
123
+ Shared colours bind as theme-token Data; note which here.
124
+
125
+ ## 3. Typography
126
+ - Families and the **fallback ladder** — what to substitute when the licensed font is
127
+ absent (e.g. "Söhne → Inter weight 300 + tracking -0.02"). Declared as `fonts` entries.
128
+ - Each role (`{type.display}` … `{type.caption}`) with size in gu, weight, tracking,
129
+ and *where it is used*. Multilingual note: which scripts the families cover.
130
+
131
+ ## 4. Brick & Generator Stylings
132
+ - **Pressable affordance** — how a tile that responds to a press looks different from a
133
+ decorative one. A pressable that looks identical to décor is a bug. (Wiring lives in
134
+ `bricks-ux`; the *visual* distinction lives here.)
135
+ - **Hero Bricks** — the ids in `heroes`, what each is, and how it reads across Canvases.
136
+ - **Generator-driven components** — list / chart / feed surfaces fed by a headless,
137
+ async Generator (Truth #5). Note their loading, empty, and error appearance.
138
+ - **Media-bound Bricks** — every brand visual reads from Media Flow via DataLink (see
139
+ Assets & Brand); none is drawn, embedded, or set to a literal path.
140
+
141
+ ## 5. Grid, Spacing & Rhythm
142
+ - **Grid stance** `{grid}` — lean / break / breathe, and what that means in margins.
143
+ - **Spacing scale** — inter-Brick gaps come only from `{spacing.*}`. Ten gaps of
144
+ 7/9/11/13 across siblings is arithmetic drift; snap to the scale.
145
+ - **Density rhythm** — how consecutive Canvases vary (not three identical layouts).
146
+ - **No scroll, overflow, or reflow** (Truth #10). What does not fit does not show;
147
+ design within the Canvas at the target resolution.
148
+
149
+ ## 6. Motion, Standby & Depth
150
+ - **Standby Transition** `{motion.entrance}` / `{motion.exit}` — every visible Brick
151
+ gets an entrance offset; the runtime auto-tweens in-flight motion (Truth #7).
152
+ - **Shared-id auto-tween** — heroes keep their id across Canvases so position/size
153
+ animate for free (Truth #3). This is the highest-leverage polish technique.
154
+ - **Depth** is Brick draw order, not a z/shadow model. State the stacking intent.
155
+ - **Snap-cuts** are reserved for emphasis (alarms), never routine navigation.
156
+
157
+ ## 7. Do's & Don'ts
158
+ Each tied to what it protects. Examples:
159
+ - **Do** reserve `{colors.accent}` for one emphasis per Canvas — sparingness is the signal.
160
+ - **Don't** soften the signature moves "to be safe" — at 30% the language reads hesitant.
161
+ - **Don't** introduce a gap outside `{spacing.*}` — add the token first or don't add it.
162
+
163
+ ## 8. Hardware & Orientation
164
+ Not breakpoints — BRICKS frames are fixed.
165
+ - **Target** resolution and orientation (from Deployment Context). The design is built
166
+ for this exact frame.
167
+ - **Viewing distance → minimum legible size** in gu, especially for `glance` signage.
168
+ - **Fleet variants** — if the same Application runs on differing resolutions, say how:
169
+ a separate Canvas set, or device-scoped Data — *not* a media query or reflow.
170
+ - **Language fit** — longest-string behaviour; the design must hold without overflow.
171
+
172
+ ## 9. Agent Guide
173
+ - Reference tokens by name (`{colors.accent}`, `{spacing.gap-m}`); never inline a raw value.
174
+ - To add any value, add the token to the frontmatter first, then use it.
175
+ - Default body to `{type.body}`; default gap to `{spacing.gap-m}`.
176
+ - The Subspace's top comment points here; do not re-declare the system inline.
177
+ - Before declaring done, the design-critique pass cites this file section by section,
178
+ and every Canvas screenshot is compared against the tokens here.
179
+
180
+ ## Assets & Brand
181
+ (Merged brand spec — populated via the protocol in `when-the-brief-is-branded.md`.)
182
+
183
+ ### First-class assets
184
+ - **Logo** — light / dark / mono → Media Flow IDs → Data keys (`brandLogoDark` …),
185
+ clearspace, minimum size, source.
186
+ - **Product / UI / scene photography** (per brand type) → IDs → Data keys, each with
187
+ its 5-10-2-8 score `[R_ P_ B_ C_ N_ = _/10]`.
188
+ - **Motion** — brand reel / animated logo → IDs → Data keys.
189
+
190
+ ### Voice & tone
191
+ Register, capitalization, banned phrases — a constraint on every user-visible Data string.
192
+
193
+ ### Gaps & placeholders
194
+ `<category>: <why missing> → <ask user | generate-with-anchor | accept placeholder> [as of <date>]`
195
+
196
+ ### Score log
197
+ `<asset>: R_ P_ B_ C_ N_ = _/10` for every non-logo asset.
198
+ ````
199
+
200
+ ## The Subspace pointer
201
+
202
+ The Subspace file no longer carries the full system as a comment. It carries a one-line pointer so a reader knows where the system lives and what it is at a glance:
203
+
204
+ ```ts
205
+ /** System: Swiss Editorial · archetype: glance · tokens + assets in ../DESIGN.md */
206
+ ```
207
+
208
+ If a Subspace genuinely needs a scoped deviation (a darker ground for a single monitoring Subspace, say), note the override inside DESIGN.md under that Subspace's name and point the comment at it — never fork a second system silently.
209
+
210
+ ## How DESIGN.md threads through the workflow
211
+
212
+ - **Pass 0** writes DESIGN.md: frontmatter tokens + Deployment Context + section 1 at minimum. ([`workflow.md`](workflow.md))
213
+ - **Picking a visual language** fills sections 1–3 and `system`/`archetype`. ([`design-languages.md`](design-languages.md))
214
+ - **Branded work** fills Assets & Brand and the colour/type/voice tokens. ([`when-the-brief-is-branded.md`](when-the-brief-is-branded.md))
215
+ - **Variations** are alternate token sets, expressed as a flat preset map keyed to these tokens, not a second DESIGN.md. ([`variations-and-tweaks.md`](variations-and-tweaks.md))
216
+ - **Verification & critique** read DESIGN.md back and check the rendered Canvases against it section by section. ([`design-critique.md`](design-critique.md))
217
+
218
+ ## Anti-patterns specific to DESIGN.md
219
+
220
+ - **Inlining raw values in the body** instead of `{token}` references — forks the source of truth.
221
+ - **A breakpoint table** in section 8 — that is a web habit; BRICKS frames are fixed (Truth #10).
222
+ - **A CSS-style elevation model** in section 6 — depth is draw order + Standby, not z-index/shadow.
223
+ - **Two artifacts** — a `brand-spec.md` *and* a DESIGN.md, or per-Subspace system blocks competing with the file. One project, one DESIGN.md.
224
+ - **Writing DESIGN.md after the Canvases** — it is a Pass 0 commitment that the build cites, not a doc written to describe finished work.
225
+ - **Tokens with no roles** — `accent: "#FF3C00"` with no note on what it is for is a swatch, not a system. The role is the design depth.
@@ -20,13 +20,18 @@ How to inspect, by source type:
20
20
  | Public URL (website / docs / product page) | Drive a browser automation tool — browser-MCP, Playwright/Puppeteer-style MCP, an `agent-browser`-equivalent skill, or any host-provided browser tool — to navigate the page and capture full-page screenshots. Then read each screenshot back through the host's image-reading capability. `WebFetch` and similar text extractors are **not** visual inspection; use them only as navigation aids to enumerate which pages to visit. |
21
21
  | Figma / design-tool URL | Use a Figma-MCP / design-tool MCP if present to enumerate frames and image-export each. Otherwise ask the user for PNG exports of every relevant frame. |
22
22
  | HTML project on disk | Serve and screenshot every route via a browser tool, the same as a public URL. Do not infer visuals by reading source HTML/CSS — the rendered output is what you need. |
23
- | PDF / brand book / slide deck | Read every page through the host's PDF capability, page-by-page when the document is long. Cover all pages, not just the first few. |
23
+ | PDF / brand book / slide deck | Read every page through the host's PDF capability, page-by-page when the document is long. Cover all pages, not just the first few. If the host only shows a cover preview or has no PDF capability, use an installed PDF skill when available. If no PDF skill or local PDF tool is installed and you cannot inspect the PDF fully, stop and respond in chat with the limitation instead of designing from partial evidence. |
24
24
  | Local images / screenshots | Read each via the image tool. |
25
25
  | Video walkthrough | Ask the user for key-frame screenshots, or to describe each state. Do not claim to have "watched" a video you cannot frame-step. |
26
26
  | Live competitor product / app | Run it (or have the user run it and capture screen recordings); inspect the screenshots/states. |
27
27
 
28
28
  If a reference is partially inaccessible (paywalled page, unauthenticated content, broken link), name the gap explicitly. Don't extrapolate from the visible portion.
29
29
 
30
+ For PDF-derived page images, keep the artifacts sized for inspection. A giant
31
+ 4-13 MB PNG per page is a tool failure waiting to happen, not a useful
32
+ handoff. Prefer downsampled JPEG/PNG previews, page batches, and a simple
33
+ coverage checklist (`seen pages: 1-8 of 8`) before design work starts.
34
+
30
35
  Inspection comes before everything else in this file. The translation table below assumes you have already seen every relevant frame.
31
36
 
32
37
  ## A render is one state, not an Application
@@ -128,7 +133,7 @@ When the source contains brand binaries, you are inheriting them — but only at
128
133
  - A redrawn-in-Figma logo or product silhouette in the source design is a tell that the source designer didn't have the real asset either — start the asset hunt from scratch, don't carry the redraw across.
129
134
  - Embedded brand fonts may not be license-cleared for embedded display use; verify the license and switch to a documented alternative if the constraint isn't met.
130
135
 
131
- Score everything against 5-10-2-8 before binding. If the source's brand binaries are unclear, low-resolution, or stylized substitutes, surface as a gap in `brand-spec.md` and either acquire properly or — for non-logo categories — escalate to brand-reference-anchored generation per [`when-the-brief-is-branded.md`](when-the-brief-is-branded.md). **Don't ship Sketch-Brick imitations.**
136
+ Score everything against 5-10-2-8 before binding. If the source's brand binaries are unclear, low-resolution, or stylized substitutes, surface as a gap under DESIGN.md's **Gaps & placeholders** and either acquire properly or — for non-logo categories — escalate to brand-reference-anchored generation per [`when-the-brief-is-branded.md`](when-the-brief-is-branded.md). **Don't ship Sketch-Brick imitations.**
132
137
 
133
138
  ### Brand colors and tokens
134
139
 
@@ -136,6 +141,8 @@ Score everything against 5-10-2-8 before binding. If the source's brand binaries
136
141
  - One-off decorative color → hardcode (Truth #2's loose convention).
137
142
  - Token system from a brand book (light / dark / accent / semantic) → expose as Data tokens; switch theme by writing to the token Data values.
138
143
 
144
+ Extracted colors, type, and spacing become the named tokens in [`DESIGN.md`](design-md.md) (referenced by name thereafter), so the translated system is captured once rather than re-derived per Canvas.
145
+
139
146
  ### Long copy / paragraphs
140
147
 
141
148
  - A signage screen at 5 m has time for one claim. If the source design has paragraphs of body copy, the design is wrong for the deployment, not the runtime.
@@ -26,7 +26,7 @@ Use when variations differ only on values: palette, copy, asset paths, density f
26
26
  **Shape:**
27
27
 
28
28
  - One Canvas / Subspace. All variant-specific values are Data with initial constants.
29
- - Source has a `VARIATION_PRESETS` map near the top of the Subspace define file. Each preset is a flat map of token values. Keep the map flat — nested config trees make per-preset diffs noisy.
29
+ - Source has a `VARIATION_PRESETS` map near the top of the Subspace define file. Each preset is a flat map of the DESIGN.md token keys (`ground`, `ink`, `accent`, `gap-m`, …) — a variation is one alternate token set, not a second DESIGN.md. Keep the map flat — nested config trees make per-preset diffs noisy.
30
30
  - Active preset is selected by a single line near the top: `const ACTIVE = PRESETS.bauhaus`. Agent flips this line, runs preview, captures screenshot, repeats.
31
31
  - All hero Bricks keep **the same id** across all presets — preserves Shared Brick auto-tween if the user later wants to A/B them at runtime in a settings Canvas (different use case, but the door stays open).
32
32
 
@@ -128,9 +128,9 @@ The bar exists because cosmetic-quality drift is invisible until shipped, then o
128
128
  | **Composition / craft** | Lighting, framing, background all consistent with the rest of the asset set | Mixed lighting across "set"; orphan background colors; visible artifacts |
129
129
  | **Narrative weight** | Carries one story alone; can be a hero on its own merits | Only works as decoration / filler; needs surrounding chrome to read |
130
130
 
131
- **Logging the scores** in `brand-spec.md` (per category) is what makes the bar real. If you didn't log a score, you didn't apply the bar.
131
+ **Logging the scores** under DESIGN.md's **Score log** (per category) is what makes the bar real. If you didn't log a score, you didn't apply the bar.
132
132
 
133
- ### Step 5 — Bind into Media Flow + persist `brand-spec.md`
133
+ ### Step 5 — Bind into Media Flow + record in DESIGN.md
134
134
 
135
135
  Upload acquired assets into a Media Flow workspace (use the host's Media Flow MCP tools where available — `list_media_workspaces`, `get_media_workspace`, `media_box_upload`, `update_media_file_meta`). Reference each from the Subspace via Data with the appropriate `kind`:
136
136
 
@@ -145,17 +145,17 @@ Bricks bind via DataLink: `Image.property.source = linkData(() => brandLogoData)
145
145
 
146
146
  Fonts: declare each as an `ApplicationFont` entry in `Application.applicationSettings.fonts` (`name`, `url`, optional `md5`). Reference by name in Brick `property.fontFamily`.
147
147
 
148
- `brand-spec.md` lives next to the Subspace. Use this template verbatim — every section is required, even the gap section:
148
+ The brand spec is **not** a separate file — it is the **Assets & Brand** sections of the project's [`DESIGN.md`](design-md.md), alongside the colour/type/voice tokens. Use this structure verbatim under DESIGN.md's `## Assets & Brand` heading — every section is required, even the gap section:
149
149
 
150
150
  ```markdown
151
- # Brand spec — <brand name>
151
+ ## Assets & Brand
152
152
  > Captured: <YYYY-MM-DD>
153
153
  > Sources: <list>
154
154
  > Coverage: <complete / partial / inferred>
155
155
 
156
- ## First-class assets
156
+ ### First-class assets
157
157
 
158
- ### Logo
158
+ #### Logo
159
159
  - Primary (dark ground): <Media Flow ID> → Data: brandLogoDark
160
160
  - Primary (light ground): <Media Flow ID> → Data: brandLogoLight
161
161
  - Mono / reverse: <Media Flow ID> → Data: brandLogoMono
@@ -163,57 +163,56 @@ Fonts: declare each as an `ApplicationFont` entry in `Application.applicationSet
163
163
  - Minimum size: <e.g., 96 px wide>
164
164
  - Source: <press-kit URL / inline-svg from <url> / file from user>
165
165
 
166
- ### Product photography (if physical brand)
166
+ #### Product photography (if physical brand)
167
167
  - Hero front: <Media Flow ID> → Data: productHero (3000×2000, transparent) [score: R9 P9 B8 C9 N9]
168
168
  - Detail crop A: ... [score: ...]
169
169
  - Scene context: ... [score: ...]
170
170
 
171
- ### UI screenshots (if digital brand)
171
+ #### UI screenshots (if digital brand)
172
172
  - Home: ... [score: ...]
173
173
  - Core feature A: ... [score: ...]
174
174
 
175
- ### Scene / lifestyle (if relevant)
175
+ #### Scene / lifestyle (if relevant)
176
176
  - ...
177
177
 
178
- ### Motion
178
+ #### Motion
179
179
  - Brand reel clip: <Media Flow ID> → Data: brandReel (1920×1080, 12s, h264)
180
180
  - Animated logo: <Media Flow ID> → Data: brandLogoLottie
181
181
 
182
- ## Auxiliary
182
+ ### Auxiliary
183
183
 
184
- ### Colors (theme tokens — exposed as Data when shared)
185
- - --brand-primary #XXXXXX OKLCH(...) source: <where>
186
- - --brand-accent #XXXXXX
187
- - --brand-neutral-100 / -900
188
- - Forbidden: <colors the brand explicitly does not use>
184
+ #### Colours
185
+ The palette lives in the DESIGN.md frontmatter `colors` tokens (shared ones bound as theme-token Data). Record provenance and constraints here:
186
+ - `{colors.primary}` / `{colors.accent}` / neutrals — source: <where each was sampled / documented>
187
+ - Forbidden: <colours the brand explicitly does not use>
189
188
 
190
- ### Fonts (ApplicationFont entries)
191
- - Display: <family / weights / file location / license>
192
- - Body: <family / weights / file location / license>
189
+ #### Fonts (ApplicationFont entries)
190
+ - Display: <family / weights / file location / license> → frontmatter `type.display` + `fonts`
191
+ - Body: <family / weights / file location / license> → frontmatter `type.body` + `fonts`
193
192
 
194
- ### Voice & tone
193
+ #### Voice & tone
195
194
  - Register: <e.g., confident, terse, never effusive>
196
195
  - Capitalization: <e.g., sentence case>
197
196
  - Banned phrases: <e.g., "innovative", "revolutionary", "AI-powered">
198
197
 
199
- ### Signature details
198
+ #### Signature details
200
199
  - <One or two design moves the brand is known for — a specific corner radius, a hairline rule, a particular typographic treatment.>
201
200
 
202
- ## Gaps & placeholders
201
+ ### Gaps & placeholders
203
202
  - <category>: <why missing> → <action: ask user / AI generate / accept placeholder> [as of <date>]
204
203
 
205
- ## Score log (for non-logo assets)
204
+ ### Score log (for non-logo assets)
206
205
  - <asset>: R<n> P<n> B<n> C<n> N<n> = <avg>/10
207
206
  ```
208
207
 
209
208
  ## Structural enforcement — assets are referenced, not redrawn
210
209
 
211
- Once `brand-spec.md` exists, the discipline that keeps the design honest is structural, not behavioural. Encode it in the build:
210
+ Once DESIGN.md's Assets & Brand sections are populated, the discipline that keeps the design honest is structural, not behavioural. Encode it in the build:
212
211
 
213
212
  - **Every brand visual** is a Brick reading from a Media Flow Data via DataLink. No `Image.property.source` set to a literal URL or path. No `Sketch` Brick drawing a "logo-shaped" graphic. No `Svg` Brick reproducing the logo as a path. The pattern enforces "you cannot ship a redrawn logo because the project structure doesn't allow it."
214
213
  - **Brand colors used in 2+ places** become theme-token Data, bound via DataLink. Single-use decorative colors can stay hardcoded (Truth #2's loose convention).
215
214
  - **Theme tokens are the single source of truth.** Want to add a new color? Add a Data entry first, then bind. The "first" is what makes it a system instead of an accumulation.
216
- - **Every Brick that uses a brand asset** has its source traceable to a `brand-spec.md` line. Reviewing the spec is reviewing the design.
215
+ - **Every Brick that uses a brand asset** has its source traceable to a DESIGN.md Assets & Brand line. Reviewing DESIGN.md is reviewing the design.
217
216
 
218
217
  ## Creating assets when missing
219
218
 
@@ -221,13 +220,13 @@ When a category has no acquirable real asset and the user has no plan to provide
221
220
 
222
221
  1. **Stop and ask.** For logo, this is the only allowed option.
223
222
  2. **Compose the host environment's generative tools.** If a canvas-design / imagen / image-generation MCP / motion-generation MCP / Banana-style tool is available in the host, use it — *anchored on a verified brand reference*. This skill does not duplicate them; it composes them.
224
- 3. **Accept a labeled placeholder Brick** with the gap tracked in `brand-spec.md`. Use this when generation is unavailable or known to fail for the asset type (e.g., generating a "real product photo" for a niche industrial product almost always fails).
223
+ 3. **Accept a labeled placeholder Brick** with the gap tracked under DESIGN.md's **Gaps & placeholders**. Use this when generation is unavailable or known to fail for the asset type (e.g., generating a "real product photo" for a niche industrial product almost always fails).
225
224
 
226
225
  ### Generative discipline (when option 2)
227
226
 
228
227
  - **Anchor on real brand material.** Feed at least one verified brand asset — the logo, a verified product photo, a brand color sample — as conditioning to the generator. Generation from "the brand's general vibe" produces uncanny-valley assets that feel adjacent to the brand without being the brand. Almost always recognized as fake by reviewers.
229
228
  - **Generate at the 5-10-2-8 quantity bar.** 8–10 candidates per slot, scored on the same 5 dimensions, keep 2.
230
- - **Persist generation metadata** in `brand-spec.md`: prompt, seed (where supported), reference assets used, model version. Reproducibility matters when the user later asks for a refresh in the same style.
229
+ - **Persist generation metadata** under DESIGN.md's Assets & Brand: prompt, seed (where supported), reference assets used, model version. Reproducibility matters when the user later asks for a refresh in the same style.
231
230
  - **Never generate logos, wordmarks, or named characters.** These are identity, not imagery — generation produces look-alikes that legal will reject.
232
231
  - **Avoid AI-design tells.** Gradient orbs as "AI". Glassy translucent panels with no source. Generic happy-diverse-people-in-a-bright-office. Tech-y blue-purple gradients as backgrounds. Float / hover dot patterns. Watch for these in generated output and re-generate or discard.
233
232
  - **Motion generation is high-failure.** For Lottie / Rive / video, expect to discard 9 of 10 outputs. Budget the iteration; don't ship the first plausible result.
@@ -255,7 +254,7 @@ Some BRICKS deployments are for venues with no prior brand work — small clinic
255
254
 
256
255
  - Skip the protocol; admit the gap up front.
257
256
  - Switch into Direction Advisor mode ([`when-the-brief-is-vague.md`](when-the-brief-is-vague.md)) and propose 3 differentiated visual directions.
258
- - The picked direction *becomes* the brand spec for this Application. Persist the picked direction's tokens, motion language, photography style guidelines, and (if any) generated assets via the same `brand-spec.md` template — even when invented from scratch. Treat it as a real spec, not a working note. The next person to touch the deployment will need it.
257
+ - The picked direction *becomes* the brand spec for this Application. Persist the picked direction's tokens, motion language, photography style guidelines, and (if any) generated assets in DESIGN.md (tokens + Assets & Brand) — even when invented from scratch. Treat it as a real spec, not a working note. The next person to touch the deployment will need it.
259
258
 
260
259
  ## Voice & tone as a constraint on user-visible Data
261
260
 
@@ -274,11 +273,11 @@ You cannot move from this protocol back into design until all of the following a
274
273
  1. Logo — present in Media Flow with light + dark + mono where applicable; bound via Data.
275
274
  2. Product / UI / scene category appropriate to the brand type — present, scored ≥ 8/10 on each kept asset, bound via Data.
276
275
  3. Motion category — present where the brand has motion identity; absent and noted otherwise.
277
- 4. Colors — palette declared as theme-token Data (for shared colors) and / or as `brand-spec.md` entries.
276
+ 4. Colors — palette declared as DESIGN.md `colors` tokens (shared ones bound as theme-token Data).
278
277
  5. Fonts — declared as `ApplicationFont` entries; license confirmed.
279
278
  6. Voice & tone — captured as constraint or marked TBD with stakeholder + date.
280
279
  7. Gaps — every missing or placeholder asset listed under "Gaps & placeholders" with action and date.
281
280
  8. Score log — every non-logo asset scored on the 5 dimensions and logged.
282
- 9. Spec committed — `brand-spec.md` exists alongside the Subspace; not in a chat scrollback or a working file that will be lost.
281
+ 9. Spec committed — DESIGN.md's Assets & Brand sections are filled in the project root; not in a chat scrollback or a working file that will be lost.
283
282
 
284
283
  If any item is incomplete, name it in your reply and either resolve it or pause for user input. Don't proceed silently.
@@ -61,31 +61,27 @@ When you do ask, cover these axes — in one batch, omitting any already pinned:
61
61
 
62
62
  Don't sprint to a finished Subspace. Stage the work so wrong direction is caught at the cheapest point.
63
63
 
64
- ### Pass 0 — Deployment context + style declaration
64
+ ### Pass 0 — Deployment context + DESIGN.md
65
65
 
66
- Confirm Priority #0 verbatim. Write the style declaration block at the top of the Subspace file before placing any Brick:
66
+ Confirm Priority #0 verbatim, then write the project's **`DESIGN.md`** before placing any Brick. Full schema and template in [`design-md.md`](design-md.md); at Pass 0 you need at minimum:
67
+
68
+ - **Frontmatter tokens** — `system`, `archetype`, the `colors` / `type` (sizes in grid units) / `spacing` (grid units) / `motion` / `grid` / `heroes` maps. Each token carries its role in a comment.
69
+ - **Deployment Context** — the five Priority #0 facts in prose, with any assumed value flagged `ASSUMED`.
70
+ - **Visual Theme & Atmosphere** (section 1) — what the system says and its one or two signature moves.
71
+
72
+ The grid substrate is given (Truth #6). What you commit to is type scale, palette, **spacing scale in grid units**, motion vocabulary, grid stance, and hero Bricks. The spacing scale is a small enumeration (3–5 values); inter-Brick gaps come from this set by name (`{spacing.gap-m}`), not arbitrary integers. DESIGN.md keeps the agent's arithmetic consistent across many Bricks and is what the design-critique pass cites.
73
+
74
+ The Subspace file itself carries only a one-line pointer:
67
75
 
68
76
  ```ts
69
- /**
70
- * System: <picked design language, e.g., Swiss Editorial>
71
- * Archetype: <interaction archetype, e.g., glance>
72
- * Type: <font family> · <size scale in grid units, e.g., 2 / 4 / 8 / 16 gu>
73
- * Color: <ground> on <ink> · accent <#hex> (used only for …)
74
- * Spacing: <scale in grid units, e.g., 2 / 4 / 8 / 16 gu>
75
- * Grid stance: lean / break / breathe
76
- * Motion: <Standby easing + ms> entrance · <ms> exit · loops only on <element>
77
- * Heroes: <Brick id(s) that persist across Canvases via auto-tween>
78
- * Asset emphasis: <real photography / type-only / hero video / …>
79
- */
77
+ /** System: <picked design language> · archetype: <archetype> · tokens + assets in ../DESIGN.md */
80
78
  ```
81
79
 
82
- The grid substrate is given (Truth #6). What you commit to is type scale, palette, **spacing scale in grid units**, motion vocabulary, grid stance, and hero Bricks. The spacing scale is a small enumeration (3–5 values); inter-Brick gaps come from this set, not arbitrary integers. This block keeps the agent's arithmetic consistent across many Bricks and is what the design-critique pass cites.
83
-
84
80
  ### Pass 1 — Showcase Canvas (the lockdown moment)
85
81
 
86
82
  Build **one** Canvas, fully lit, that represents the work at its highest fidelity. Choose the hardest Canvas — the boot screen, the most content-dense state, or the moment that carries the brand most. Run through Verification (compile + screenshot + view back). Show the user.
87
83
 
88
- This is the cheapest point to discover the direction is wrong. If the user wants the type bigger, the photography swapped, the rhythm slower, the language different — fix the Canvas and the style declaration, not twelve Canvases.
84
+ This is the cheapest point to discover the direction is wrong. If the user wants the type bigger, the photography swapped, the rhythm slower, the language different — fix the Canvas and the DESIGN.md tokens, not twelve Canvases.
89
85
 
90
86
  **Do not** build the full Canvas graph before this checkpoint. The temptation is real because state-machine work feels productive; resist it. Sign-off on one Canvas first.
91
87
 
@@ -131,4 +127,4 @@ Don't compress passes on:
131
127
  - **Asking one question at a time.** Burns volleys, drains user energy.
132
128
  - **Asking nothing and inferring.** The five Priority #0 items are not inferrable; trying to is the most expensive mistake.
133
129
  - **Declaring done after Pass 2.** Pass 4 critique is what separates "runs" from "good."
134
- - **Re-declaring the style block on every iteration.** If you find yourself updating five values at once mid-Pass-3, you're rebuilding the system — back up to Pass 1.
130
+ - **Rewriting DESIGN.md tokens on every iteration.** If you find yourself updating five tokens at once mid-Pass-3, you're rebuilding the system — back up to Pass 1.
@@ -2,6 +2,7 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2
2
  import { readFile } from 'node:fs/promises'
3
3
  import { z } from 'zod'
4
4
  import { sh } from '../_shell'
5
+ import { appendSimulatorInspectionGuidance } from './simulator-guidance'
5
6
 
6
7
  // Disable ANSI color codes from spawned tools so MCP text output stays readable
7
8
  // (the host renders raw text; escape sequences leak into the result otherwise).
@@ -78,6 +79,7 @@ export function register(server: McpServer, projectDir: string) {
78
79
  log = [stdout, stderr].filter(Boolean).join('\n')
79
80
  error = true
80
81
  }
82
+ if (!error) log = appendSimulatorInspectionGuidance(log, { testId, testTitleLike })
81
83
  let screenshotBase64: string | null = null
82
84
  if (!error && responseImage) {
83
85
  const toolsDir = `${dirname}/..`
@@ -0,0 +1,31 @@
1
+ const quoteShellArg = (value: string) => `'${value.replace(/'/g, `'\\''`)}'`
2
+
3
+ const buildInteractiveSimulatorCommand = ({
4
+ testId,
5
+ testTitleLike,
6
+ }: {
7
+ testId?: string
8
+ testTitleLike?: string
9
+ } = {}) => {
10
+ const args = ['bun preview']
11
+ if (testId) args.push('--test-id', quoteShellArg(testId))
12
+ if (testTitleLike) args.push('--test-title-like', quoteShellArg(testTitleLike))
13
+ return args.join(' ')
14
+ }
15
+
16
+ export function appendSimulatorInspectionGuidance(
17
+ log: string,
18
+ options: { testId?: string; testTitleLike?: string } = {},
19
+ ) {
20
+ return [
21
+ log.trimEnd(),
22
+ '',
23
+ 'Inspection guidance:',
24
+ `- For interactive debugging of this same Preview runtime, run \`${buildInteractiveSimulatorCommand(options)}\` from the project directory. It exposes CDP on localhost:19852 by default and writes .bricks/devtools.json for CLI discovery.`,
25
+ '- Use `bricks devtools -p 19852 ...` from the project directory to inspect the Simulator. Plain `bricks devtools` may inspect another device on the default port.',
26
+ '- Useful commands: `bricks devtools -p 19852 brick tree`, `brick query <selector>`, `input tap <x> <y>`, `network list`, `storage data-bank get <S_xxxx>`, and `runtime eval "automation.list()" -j`.',
27
+ '- Prefer this Preview CDP/runtime path over a separate Playwright browser when validating BRICKS app state, logs, input, storage, or network behavior.',
28
+ '- If the `bricks-cli` skill is unavailable, run `bricks devtools --help`; the CLI help is authoritative.',
29
+ '',
30
+ ].join('\n')
31
+ }
package/tools/pull.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { readdir, readFile, unlink, writeFile } from 'node:fs/promises'
1
+ import { mkdir, readdir, readFile, unlink, writeFile } from 'node:fs/promises'
2
2
  import { existsSync } from 'node:fs'
3
- import { join, relative } from 'node:path'
3
+ import { dirname, join, relative } from 'node:path'
4
4
  import { format } from 'oxfmt'
5
5
  import { sh } from './_shell'
6
6
  import { extractCliErrorMessage } from './_cli-error'
@@ -86,7 +86,11 @@ const branchName = isModule
86
86
  ? 'BRICKS_PROJECT_try-pull-module'
87
87
  : 'BRICKS_PROJECT_try-pull-application'
88
88
 
89
+ let landingBranch = ''
89
90
  if (isGitRepo && !force) {
91
+ landingBranch = (await sh`cd ${cwd} && git branch --show-current`.text()).trim()
92
+ if (!landingBranch) throw new Error('Cannot pull from a detached HEAD')
93
+
90
94
  console.log(`Checking commit ${baseCommitId}...`)
91
95
  const found = (await sh`cd ${cwd} && git rev-list -1 ${baseCommitId}`.nothrow().text())
92
96
  .trim()
@@ -100,8 +104,8 @@ if (isGitRepo && !force) {
100
104
 
101
105
  // When the base commit isn't reachable in this clone (server stored a
102
106
  // nanoid, or the commit was pruned), fall back to forking from current
103
- // HEAD. The downstream merge into main collapses both paths into the
104
- // same result, just with different merge bases.
107
+ // HEAD. The downstream merge into the starting branch collapses both paths
108
+ // into the same result, just with different merge bases.
105
109
  if (found) {
106
110
  await sh`cd ${cwd} && git checkout -b ${branchName} ${baseCommitId}`.nothrow()
107
111
  } else {
@@ -143,7 +147,9 @@ await Promise.all(
143
147
  const result = await format(file.name, file.input, oxfmtConfig)
144
148
  content = result.code
145
149
  }
146
- return writeFile(`${cwd}/${file.name}`, content)
150
+ const target = join(cwd, file.name)
151
+ await mkdir(dirname(target), { recursive: true })
152
+ return writeFile(target, content)
147
153
  }),
148
154
  )
149
155
 
@@ -160,11 +166,11 @@ if (isGitRepo) {
160
166
  await sh`cd ${cwd} && git ${commitArgs}`
161
167
  }
162
168
  if (!force) {
163
- // Land the pulled commits on main with a single 3-way merge using
169
+ // Land the pulled commits on the starting branch with a single 3-way merge using
164
170
  // baseCommit as the merge base. The user doesn't have to manage a side
165
- // branch, and conflicts (if any) land in the working tree on main where
171
+ // branch, and conflicts (if any) land in the working tree on the starting branch where
166
172
  // auto-compile surfaces them as typecheck errors to resolve in-place.
167
- await sh`cd ${cwd} && git checkout main`
173
+ await sh`cd ${cwd} && git checkout ${landingBranch}`
168
174
  const mergeResult = await sh`cd ${cwd} && git merge ${branchName} --no-edit`.nothrow()
169
175
  if (mergeResult.exitCode !== 0) {
170
176
  // Conflict markers are in the working tree — commit them so the tree
@@ -174,7 +180,7 @@ if (isGitRepo) {
174
180
  await sh`cd ${cwd} && git add .`
175
181
  const conflictArgs = await buildCommitArgs(
176
182
  cwd,
177
- ['chore(project): merge with conflicts (resolve in main)'],
183
+ [`chore(project): merge with conflicts (resolve in ${landingBranch})`],
178
184
  ['--no-verify'],
179
185
  )
180
186
  await sh`cd ${cwd} && git ${conflictArgs}`
@@ -269,6 +269,7 @@ Default property:
269
269
  y?: number | DataLink
270
270
  width?: number | DataLink
271
271
  height?: number | DataLink
272
+ type?: string | DataLink
272
273
  standbyMode?: 'custom' | 'top' | 'bottom' | 'left' | 'right' | DataLink
273
274
  standbyFrame?: DataLink | {}
274
275
  standbyOpacity?: number | DataLink
@@ -319,6 +320,7 @@ Default property:
319
320
  }
320
321
  show?: string | DataLink
321
322
  pressToOpenDetail?: boolean | DataLink
323
+ pressToBackList?: boolean | DataLink
322
324
  }
323
325
  >
324
326
  | DataLink
@@ -346,6 +348,7 @@ Default property:
346
348
  y?: number | DataLink
347
349
  width?: number | DataLink
348
350
  height?: number | DataLink
351
+ type?: string | DataLink
349
352
  standbyMode?: 'custom' | 'top' | 'bottom' | 'left' | 'right' | DataLink
350
353
  standbyFrame?: DataLink | {}
351
354
  standbyOpacity?: number | DataLink
@@ -395,6 +398,7 @@ Default property:
395
398
  renderOutOfViewport?: boolean | DataLink
396
399
  }
397
400
  show?: string | DataLink
401
+ pressToOpenDetail?: boolean | DataLink
398
402
  pressToBackList?: boolean | DataLink
399
403
  }
400
404
  >
@@ -191,9 +191,11 @@ Default property:
191
191
  /* A stroke was just committed (drawn or added programmatically) */
192
192
  onStrokeEnd?: Array<EventAction<string & keyof TemplateEventPropsMap['Sketch']['onStrokeEnd']>>
193
193
  /* The canvas was cleared */
194
- onClear?: Array<EventAction>
194
+ onClear?: Array<EventAction<string & keyof TemplateEventPropsMap['Sketch']['onClear']>>
195
195
  /* Sketch state changed (any commit, undo, redo, clear, or import) */
196
- onStateChange?: Array<EventAction>
196
+ onStateChange?: Array<
197
+ EventAction<string & keyof TemplateEventPropsMap['Sketch']['onStateChange']>
198
+ >
197
199
  /* Active tool changed */
198
200
  onToolChange?: Array<
199
201
  EventAction<string & keyof TemplateEventPropsMap['Sketch']['onToolChange']>
@@ -63,7 +63,13 @@ Default property:
63
63
  "muted": false,
64
64
  "progressUpdateInterval": 1000,
65
65
  "replayTimeout": 500,
66
- "renderMode": "auto"
66
+ "renderMode": "auto",
67
+ "showSubtitles": true,
68
+ "subtitleColor": "#FFFFFF",
69
+ "subtitleBackgroundColor": "rgba(0, 0, 0, 0.6)",
70
+ "subtitleFontSize": 2,
71
+ "subtitlePosition": "bottom",
72
+ "subtitleTextAlign": "center"
67
73
  }
68
74
  */
69
75
  property?: BrickBasicProperty & {
@@ -106,6 +112,57 @@ Default property:
106
112
  replayTimeout?: number | DataLink
107
113
  /* Use what view type for render video (Auto / Texture or Surface). Notice: Although using surface has better performance, it also affects the Animation & Standby Transition used by itself */
108
114
  renderMode?: 'auto' | 'texture' | 'surface' | DataLink
115
+ /* The subtitle file path list or single path (SRT/WebVTT), index-matched to the video path. Used when no inline subtitles are set for that video. */
116
+ subtitlePath?:
117
+ | string
118
+ | DataLink
119
+ | DataLink
120
+ | {
121
+ url?: string | DataLink
122
+ }
123
+ | Array<
124
+ | DataLink
125
+ | {
126
+ url?: string | DataLink
127
+ }
128
+ | string
129
+ | DataLink
130
+ | DataLink
131
+ >
132
+ | DataLink
133
+ | DataLink
134
+ /* Inline subtitles per video, index-matched to the video path: each entry holds a `cues` list of { start, end, text } (start/end in seconds). Takes precedence over the subtitle file for that video. */
135
+ subtitles?:
136
+ | Array<
137
+ | DataLink
138
+ | {
139
+ cues?:
140
+ | Array<
141
+ | DataLink
142
+ | {
143
+ start?: number | DataLink
144
+ end?: number | DataLink
145
+ text?: string | DataLink
146
+ }
147
+ >
148
+ | DataLink
149
+ }
150
+ >
151
+ | DataLink
152
+ /* Render the subtitle overlay. Turn off to only trigger subtitle events without rendering text */
153
+ showSubtitles?: boolean | DataLink
154
+ /* Subtitle text color */
155
+ subtitleColor?: string | DataLink
156
+ /* Subtitle background color */
157
+ subtitleBackgroundColor?: string | DataLink
158
+ /* Subtitle font size, in grid units (same as Brick Text) */
159
+ subtitleFontSize?: number | DataLink
160
+ /* Subtitle font family */
161
+ subtitleFontFamily?: string | DataLink
162
+ /* Subtitle vertical position */
163
+ subtitlePosition?: 'bottom' | 'top' | DataLink
164
+ /* Subtitle text alignment */
165
+ subtitleTextAlign?: 'left' | 'center' | 'right' | DataLink
109
166
  }
110
167
  events?: BrickBasicEvents & {
111
168
  /* Event of the brick press */
@@ -130,6 +187,14 @@ Default property:
130
187
  onError?: Array<EventAction>
131
188
  /* Event of the video progress interval */
132
189
  onProgress?: Array<EventAction<string & keyof TemplateEventPropsMap['Video']['onProgress']>>
190
+ /* Event when a subtitle cue starts (appears) during playback */
191
+ subtitleCueEnter?: Array<
192
+ EventAction<string & keyof TemplateEventPropsMap['Video']['subtitleCueEnter']>
193
+ >
194
+ /* Event when a subtitle cue ends (disappears) during playback */
195
+ subtitleCueExit?: Array<
196
+ EventAction<string & keyof TemplateEventPropsMap['Video']['subtitleCueExit']>
197
+ >
133
198
  }
134
199
  outlets?: {
135
200
  /* Brick is pressing */
@@ -149,6 +214,8 @@ Default property:
149
214
  onLoad?: AnimationOrGetter
150
215
  onError?: AnimationOrGetter
151
216
  onProgress?: AnimationOrGetter
217
+ subtitleCueEnter?: AnimationOrGetter
218
+ subtitleCueExit?: AnimationOrGetter
152
219
  }
153
220
  }
154
221
 
@@ -157,7 +157,12 @@ Default property:
157
157
  /* Event name to listen on SSE */
158
158
  eventName?: string | DataLink | Array<string | DataLink> | DataLink | DataLink
159
159
  }
160
- events?: {}
160
+ events?: {
161
+ /* Event triggered when the HTTP request receives a response */
162
+ onResponse?: Array<EventAction<string & keyof TemplateEventPropsMap['Http']['onResponse']>>
163
+ /* Event triggered when the HTTP request or SSE stream reports an error */
164
+ onError?: Array<EventAction<string & keyof TemplateEventPropsMap['Http']['onError']>>
165
+ }
161
166
  outlets?: {
162
167
  /* Response for HTTP request */
163
168
  response?: () => Data<string | { [key: string]: any }>
@@ -33,7 +33,7 @@ Default property:
33
33
  property?: {
34
34
  /* Start tick on generator initialized */
35
35
  init?: boolean | DataLink
36
- /* Interval of second for countdown */
36
+ /* Tick interval in milliseconds for countdown */
37
37
  interval?: number | DataLink
38
38
  /* Initial value of countdown */
39
39
  countdownStartValue?: number | DataLink
package/utils/data.ts CHANGED
@@ -85,7 +85,7 @@ type SystemDataName =
85
85
  type SystemDataInfo = {
86
86
  name: SystemDataName
87
87
  id: string
88
- type: 'string' | 'number' | 'bool' | 'boolean' | 'array' | 'object' | 'any'
88
+ type: Data['type']
89
89
  title?: string
90
90
  description?: string
91
91
  schema?: object
@@ -16,6 +16,13 @@ export const templateEventPropsMap = {
16
16
  Video: {
17
17
  nextVideo: { BRICK_VIDEO_NEXT_INDEX: 'number' },
18
18
  onProgress: { BRICK_VIDEO_CURRENT_TIME: 'number', BRICK_VIDEO_PLAYABLE_DURATION: 'number' },
19
+ subtitleCueEnter: {
20
+ BRICK_VIDEO_SUBTITLE_TEXT: 'string',
21
+ BRICK_VIDEO_SUBTITLE_INDEX: 'number',
22
+ BRICK_VIDEO_SUBTITLE_START: 'number',
23
+ BRICK_VIDEO_SUBTITLE_END: 'number',
24
+ },
25
+ subtitleCueExit: { BRICK_VIDEO_SUBTITLE_TEXT: 'string', BRICK_VIDEO_SUBTITLE_INDEX: 'number' },
19
26
  },
20
27
  VideoStreaming: {},
21
28
  Qrcode: {},
@@ -142,7 +149,9 @@ export const templateEventPropsMap = {
142
149
  },
143
150
  },
144
151
  Sketch: {
145
- onStrokeEnd: { BRICK_SKETCH_STROKE_COUNT: 'number' },
152
+ onStrokeEnd: { BRICK_SKETCH_STROKE_COUNT: 'number', BRICK_SKETCH_STATE: '{}' },
153
+ onClear: { BRICK_SKETCH_STATE: '{}' },
154
+ onStateChange: { BRICK_SKETCH_STATE: '{}' },
146
155
  onToolChange: { BRICK_SKETCH_TOOL: 'string' },
147
156
  onExportImage: { BRICK_SKETCH_IMAGE_URI: 'string' },
148
157
  },
@@ -267,7 +276,14 @@ export const templateEventPropsMap = {
267
276
  subscribeDataOnConnectError: { GENERATOR_DATA_BANK_ERROR: '{ [key: string]: any }' },
268
277
  },
269
278
  Graphql: { subscriptionOnConnectionError: { GENERATOR_GRAPHQL_ERROR: '{ [key: string]: any }' } },
270
- Http: {},
279
+ Http: {
280
+ onResponse: {
281
+ GENERATOR_HTTP_RESPONSE: 'any',
282
+ GENERATOR_HTTP_RESPONSE_DETAILS:
283
+ '{ ok?: boolean redirected?: boolean type?: string status?: number statusText?: string headers?: { [key: string]: any } body?: any event?: string id?: string data?: any [key: string]: any }',
284
+ },
285
+ onError: { GENERATOR_HTTP_ERROR: 'string' },
286
+ },
271
287
  SoundPlayer: { onLoadError: { GENERATOR_SOUND_PLAYER_ERROR: 'string' } },
272
288
  Keyboard: {
273
289
  onDown: {