@agent-native/core 0.85.5 → 0.85.7
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/AssistantChat.tsx +1 -1
- package/corpus/core/src/client/use-pinch-zoom.ts +76 -11
- package/corpus/core/src/collab/presence.ts +63 -3
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +245 -77
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +190 -13
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +16 -4
- package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +46 -3
- package/corpus/templates/design/AGENTS.md +40 -4
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +20 -73
- package/corpus/templates/design/actions/apply-motion-edit.ts +29 -2
- package/corpus/templates/design/actions/apply-visual-edit.ts +4 -2
- package/corpus/templates/design/actions/connect-localhost.ts +25 -18
- package/corpus/templates/design/actions/edit-design.ts +5 -3
- package/corpus/templates/design/actions/export-pdf.ts +8 -1
- package/corpus/templates/design/actions/export-zip.ts +10 -1
- package/corpus/templates/design/actions/get-motion-timeline.ts +26 -20
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +10 -0
- package/corpus/templates/design/actions/insert-asset.ts +190 -7
- package/corpus/templates/design/actions/open-visual-edit.ts +27 -5
- package/corpus/templates/design/actions/present-design-variants.ts +12 -8
- package/corpus/templates/design/actions/revoke-localhost-write-consent.ts +18 -14
- package/corpus/templates/design/actions/run-design-audit.ts +7 -4
- package/corpus/templates/design/actions/write-local-file.ts +47 -22
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +221 -66
- package/corpus/templates/design/app/components/design/EditPanel.tsx +650 -237
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +526 -127
- package/corpus/templates/design/app/components/design/MotionDock.tsx +234 -46
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1520 -410
- package/corpus/templates/design/app/components/design/StatesPanel.tsx +25 -2
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +640 -72
- package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +605 -0
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +50 -26
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +16 -4
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +90 -103
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +111 -5
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +13 -2
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +42 -2
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +11 -2
- package/corpus/templates/design/app/components/design/types.ts +16 -0
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +24 -6
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +15 -1
- package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +77 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/design/app/i18n-data.ts +18 -0
- package/corpus/templates/design/app/lib/design-import.ts +95 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +4015 -840
- package/corpus/templates/design/changelog/2026-07-02-creating-two-frames-in-a-row-no-longer-freezes-and-reloads-t.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-canvas-interactions-now-match-figma-rotation-aware-resizing-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-design-retries-stalled-screen-edits-with-a-smaller-safer-fol.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-inspector-fixes-mixed-selections-show-mixed-instead-of-wrong.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-keyframe-timeline-works-reliably-drag-keyframes-smoothly-pic.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-layers-panel-matches-figma-top-layer-on-top-drag-with-auto-s.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-much-faster-editor-smooth-dragging-zooming-and-panel-updates.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-pen-tool-refinements-click-the-first-point-to-close-with-a-d.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-text-editing-enter-adds-a-line-break-international-ime-typin.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-undo-and-redo-are-dependable-across-agent-edits-screen-switc.md +6 -0
- package/corpus/templates/design/server/lib/design-export.ts +43 -1
- package/corpus/templates/design/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/design/shared/board-file.ts +25 -5
- package/corpus/templates/design/shared/canvas-math.ts +754 -9
- package/corpus/templates/design/shared/code-layer.ts +304 -26
- package/corpus/templates/design/shared/color-utils.ts +84 -0
- package/corpus/templates/design/shared/design-source-capabilities.ts +16 -7
- package/corpus/templates/design/shared/motion-compiler.ts +75 -31
- package/corpus/templates/design/shared/motion-timeline.ts +335 -0
- package/corpus/templates/design/shared/pen-path.ts +200 -23
- package/dist/client/AssistantChat.js +1 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/use-pinch-zoom.d.ts.map +1 -1
- package/dist/client/use-pinch-zoom.js +76 -11
- package/dist/client/use-pinch-zoom.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/presence.d.ts.map +1 -1
- package/dist/collab/presence.js +54 -3
- package/dist/collab/presence.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/observability/routes.d.ts +2 -2
- package/dist/secrets/routes.d.ts +3 -3
- package/package.json +1 -1
|
@@ -110,6 +110,50 @@ Pick a preset by `projectType`:
|
|
|
110
110
|
`:root` CSS variable. Never hardcode `text-white` / `bg-black` / hex literals
|
|
111
111
|
in the markup — that's what keeps brand + multi-screen consistency automatic.
|
|
112
112
|
|
|
113
|
+
### Type-scale recipe
|
|
114
|
+
|
|
115
|
+
Use this as a starting scale, then adjust to the chosen Aesthetic:
|
|
116
|
+
|
|
117
|
+
- Display: 56-96px · H1: 40-64px · H2: 28-36px · Body: 16-18px · Caption: 12-13px.
|
|
118
|
+
- Each adjacent step should be at least 1.25× the one below it — smaller jumps
|
|
119
|
+
read as "almost the same size" rather than a deliberate hierarchy.
|
|
120
|
+
- A hero/display line should be at least 3× the body size.
|
|
121
|
+
- Line-heights: display/H1 tight at 1.05-1.15, H2/H3 at 1.2-1.3, body relaxed
|
|
122
|
+
at 1.5-1.7.
|
|
123
|
+
- Measure (line length) for body copy: 60-75 characters; constrain with
|
|
124
|
+
`max-width` in `ch` units, not a raw pixel guess.
|
|
125
|
+
|
|
126
|
+
### Section rhythm
|
|
127
|
+
|
|
128
|
+
Pick one section padding value and repeat it for every top-level section on
|
|
129
|
+
the page/screen: 96-128px on desktop, 48-64px on mobile. Don't let each
|
|
130
|
+
section invent its own padding — that's what makes a page feel unplanned.
|
|
131
|
+
Use spacing to encode grouping: gaps *inside* a card or cluster should be
|
|
132
|
+
visibly smaller than the gap *between* cards/sections — if inside-group and
|
|
133
|
+
between-group spacing match, the eye can't tell where one group ends and the
|
|
134
|
+
next begins.
|
|
135
|
+
|
|
136
|
+
### Verifiable contrast pairs
|
|
137
|
+
|
|
138
|
+
Don't just assert "WCAG AA" — check the actual token pairs the design ships.
|
|
139
|
+
The most common real failure is muted text (`--color-text-muted`) directly on
|
|
140
|
+
a card/surface background rather than the page background; verify that pair
|
|
141
|
+
specifically, not just text-on-page. If an accent color doubles as text (a
|
|
142
|
+
link, an active nav item, a price), it usually fails 4.5:1 against typical
|
|
143
|
+
surfaces — add a separate `--color-accent-text` variant tuned for text-on-
|
|
144
|
+
background contrast rather than reusing the decorative accent for copy.
|
|
145
|
+
|
|
146
|
+
### Richer tokens
|
|
147
|
+
|
|
148
|
+
Go beyond the minimal `:root` block in the HTML Structure Requirements below
|
|
149
|
+
when the design needs it — add `--space-section` (see Section rhythm),
|
|
150
|
+
`--color-border`, `--color-accent-text` (see contrast pairs above),
|
|
151
|
+
`--shadow-card`, and a success/warning/danger trio
|
|
152
|
+
(`--color-success` / `--color-warning` / `--color-danger`) once the design has
|
|
153
|
+
status states, alerts, or form validation to express. Keep font tokens as
|
|
154
|
+
placeholders you fill per design (see HTML Structure Requirements) rather than
|
|
155
|
+
hardcoding a concrete family in a shared template.
|
|
156
|
+
|
|
113
157
|
## Building on existing code, screens, or a design system
|
|
114
158
|
|
|
115
159
|
When a design system, tokens, current screens, or a connected codebase already
|
|
@@ -127,7 +171,7 @@ defaults. The banned-defaults list above still applies, plus:
|
|
|
127
171
|
your colors". Keep structure and layout genuinely varied per screen while the
|
|
128
172
|
palette, type, and components stay on-brand.
|
|
129
173
|
|
|
130
|
-
## Generation Workflow — the canonical
|
|
174
|
+
## Generation Workflow — the canonical 5-phase flow
|
|
131
175
|
|
|
132
176
|
This flow mirrors Claude Design's UX: ask → show variants → user picks → refine. Don't skip phases for new designs.
|
|
133
177
|
|
|
@@ -204,12 +248,21 @@ pnpm action generate-design \
|
|
|
204
248
|
|
|
205
249
|
`generate-design` accepts a `--tweaks` array — pass 3-6 of the most impactful knobs bound to CSS custom properties the design's `:root` block actually defines. Surface controls users will actually want to adjust (accent color, density, radius, dark-mode toggle, font choice). Don't ship a generic preset; let the design's structure pick the knobs.
|
|
206
250
|
|
|
207
|
-
### Phase 5 —
|
|
251
|
+
### Phase 5 — Audit, fix, and eyeball before calling it ready
|
|
252
|
+
|
|
253
|
+
Run `run-design-audit` against each screen (`designId` + `fileId`/`filename`).
|
|
254
|
+
It returns `A11yFinding[]` covering missing alt/labels, tap-target size,
|
|
255
|
+
focus-visibility, reduced-motion coverage, and a contrast hint. For every
|
|
256
|
+
`error`-severity finding with `fixAvailable: true`, call `apply-a11y-fix`;
|
|
257
|
+
for findings that aren't auto-fixable (missing alt text, structural issues),
|
|
258
|
+
fix them directly with `edit-design`. **A design with audit errors is not
|
|
259
|
+
ready** — don't report a design as done while `run-design-audit` still
|
|
260
|
+
returns unresolved errors.
|
|
208
261
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
states for app UI, and whether the copy/content
|
|
212
|
-
issues before reporting the design as ready.
|
|
262
|
+
After the audit is clean, do one visual pass by eye at both a mobile width
|
|
263
|
+
(375px) and a desktop width (1280px): check for overflow/clipping, broken
|
|
264
|
+
hierarchy, empty/loading/error states for app UI, and whether the copy/content
|
|
265
|
+
still sounds real. Fix obvious issues before reporting the design as ready.
|
|
213
266
|
|
|
214
267
|
## HTML Structure Requirements
|
|
215
268
|
|
|
@@ -247,8 +300,8 @@ Every `index.html` must include:
|
|
|
247
300
|
--color-surface: #1E293B;
|
|
248
301
|
--color-text: #F8FAFC;
|
|
249
302
|
--color-text-muted: #94A3B8;
|
|
250
|
-
--font-heading: '
|
|
251
|
-
--font-body: '
|
|
303
|
+
--font-heading: '<HEADING_FONT>', sans-serif; /* pick per Font Recommendations below — do not default to Space Grotesk */
|
|
304
|
+
--font-body: '<BODY_FONT>', sans-serif; /* pick a pairing, not a repeat of every other generation */
|
|
252
305
|
--radius: 12px;
|
|
253
306
|
}
|
|
254
307
|
|
|
@@ -484,11 +537,20 @@ to the chosen Aesthetic layer is what keeps designs from sharing a fingerprint:
|
|
|
484
537
|
| Space Grotesk | DM Sans | Modern tech |
|
|
485
538
|
| Playfair Display | Source Sans 3 | Editorial luxury |
|
|
486
539
|
| Sora | Outfit | Clean geometric |
|
|
487
|
-
|
|
|
540
|
+
| Bricolage Grotesque | Schibsted Grotesk | Contemporary startup |
|
|
488
541
|
| Fraunces | Work Sans | Warm editorial |
|
|
489
542
|
| JetBrains Mono | IBM Plex Sans | Developer tool |
|
|
490
|
-
|
|
|
543
|
+
| Unbounded | Sora | Bold statement |
|
|
491
544
|
| Archivo | Nunito Sans | Friendly SaaS |
|
|
545
|
+
| Instrument Serif | Schibsted Grotesk | Editorial minimal |
|
|
546
|
+
|
|
547
|
+
All of the above are served by Google Fonts (`fonts.googleapis.com/css2`). The
|
|
548
|
+
mandatory `<head>` only loads the Google Fonts CDN (see HTML Structure
|
|
549
|
+
Requirements) — Fontshare-only families (Cabinet Grotesk, Satoshi, Clash
|
|
550
|
+
Display, General Sans) are not on Google Fonts and will silently fail to load,
|
|
551
|
+
so the browser falls back to a system sans and quietly reintroduces the exact
|
|
552
|
+
slop this skill bans. Only use a Fontshare family if you also add its
|
|
553
|
+
Fontshare `<link>`/`@import` and confirm it renders.
|
|
492
554
|
|
|
493
555
|
## Multi-screen prototypes & navigation
|
|
494
556
|
|
|
@@ -569,7 +631,122 @@ regeneration is slow, expensive, and regresses unrelated parts.
|
|
|
569
631
|
- Never use `<script>` blocks with raw DOM manipulation — use Alpine.js directives
|
|
570
632
|
- Never inline `onclick="..."` handlers — use `@click`
|
|
571
633
|
- Never use `!important` except in `[x-cloak]`
|
|
572
|
-
- Never use position: fixed for modals — wrap in a portal-like pattern with Alpine.js
|
|
573
634
|
- Never forget `cursor-pointer` on interactive elements
|
|
574
|
-
- Never use `<img>` with placeholder URLs —
|
|
575
|
-
|
|
635
|
+
- Never use `<img>` with placeholder/stock URLs — generate real imagery (see
|
|
636
|
+
Imagery below) or use tokened colored divs/gradients only for pure UI
|
|
637
|
+
chrome (icons, avatars-as-initials, decorative fills), never as a stand-in
|
|
638
|
+
for a hero, product shot, or portrait that should be a generated image
|
|
639
|
+
- Never set font-size below 16px for body text or 12px for labels
|
|
640
|
+
|
|
641
|
+
## Multi-screen consistency contract
|
|
642
|
+
|
|
643
|
+
When a design has more than one screen, the shared system must be
|
|
644
|
+
byte-identical across every screen file, not just similar: the `:root` token
|
|
645
|
+
block, the Google Fonts `<link>`, the nav, and the footer should match exactly
|
|
646
|
+
between `index.html` and every other screen. Before saving a new or edited
|
|
647
|
+
screen, diff its `:root` block against `index.html`'s (or the design system's
|
|
648
|
+
tokens) and reconcile any drift instead of letting each screen accumulate its
|
|
649
|
+
own slightly-different palette. Consistency is not sameness — keep structure
|
|
650
|
+
and layout varied per screen (see "Building on existing code" above) while the
|
|
651
|
+
token layer, typography, nav, and footer stay identical.
|
|
652
|
+
|
|
653
|
+
## Breakpoints & screen states
|
|
654
|
+
|
|
655
|
+
- **Breakpoints**: `add-breakpoint` adds a device-width frame (Mobile 390 /
|
|
656
|
+
Tablet 768 / Desktop 1280, or a custom width) to the design's breakpoint set
|
|
657
|
+
stored in `designs.data`; the Tailwind prefix is derived from the width and
|
|
658
|
+
duplicate widths are ignored. `remove-breakpoint` removes one by id.
|
|
659
|
+
`set-active-breakpoint` sets which frame is the current edit scope — editing
|
|
660
|
+
a layer while `base` is active writes unprefixed Tailwind classes, editing
|
|
661
|
+
while `md` is active writes `md:`-prefixed classes. Always check the active
|
|
662
|
+
breakpoint before making a responsive-only edit so the class lands at the
|
|
663
|
+
right prefix.
|
|
664
|
+
- **Design states**: `create-design-state` creates a named alternate
|
|
665
|
+
DOM/Alpine snapshot (`kind: "state"` — Loading, Empty, Error), a static data
|
|
666
|
+
fixture (`kind: "fixture"`), or a placeholder for a live capture
|
|
667
|
+
(`kind: "capture"`). `apply-design-state` updates an existing state row
|
|
668
|
+
(rename, change breakpoint, update fixture/capture data, set the preview
|
|
669
|
+
reference). `capture-design-state` records a running app's current route,
|
|
670
|
+
props, and API data into a `capture` row — it requires the design's source
|
|
671
|
+
to advertise the `captureState` bridge capability (localhost/fusion); for
|
|
672
|
+
inline designs without a live bridge, use `create-design-state` instead.
|
|
673
|
+
`list-design-states` lists all states/fixtures/captures for a design, and
|
|
674
|
+
`delete-design-state` removes one (irreversible; the design itself is
|
|
675
|
+
unaffected).
|
|
676
|
+
|
|
677
|
+
## Component reuse
|
|
678
|
+
|
|
679
|
+
Before hand-rolling another near-duplicate card/button/nav item, check
|
|
680
|
+
whether the pattern already exists as a recognised component. Once a visual
|
|
681
|
+
pattern repeats 3+ times in a design, promote it: call `create-component` on
|
|
682
|
+
the selected root element to stamp deterministic
|
|
683
|
+
`data-agent-native-component="<Name>"` and `data-agent-native-prop-*`
|
|
684
|
+
annotations, so it becomes a recognised component instance for the canvas
|
|
685
|
+
outline and the Component inspector section. Use `index-components` to scan a
|
|
686
|
+
design's HTML for existing `data-agent-native-component` annotations and
|
|
687
|
+
persist the discovered component list before inventing something that may
|
|
688
|
+
already exist. `get-component-details` returns a selected instance's name,
|
|
689
|
+
props, variants, and source info. `preview-component-prop-edit` previews a
|
|
690
|
+
prop/class change on the canvas without saving; `apply-component-prop-edit`
|
|
691
|
+
persists it. `open-component-source` navigates to the component's source
|
|
692
|
+
location (the design file for inline/Alpine designs, or the resolved external
|
|
693
|
+
file for localhost/fusion sources).
|
|
694
|
+
|
|
695
|
+
## Realistic app-state content
|
|
696
|
+
|
|
697
|
+
For app/product UI (not marketing pages), populate lists and tables with
|
|
698
|
+
plausible mid-life data — not a pristine "just signed up" empty account and
|
|
699
|
+
not obviously fake placeholder rows (avoid "Lorem Ipsum User", "Item 1", "Item
|
|
700
|
+
2"). Include at least one realistically long name/title/label so truncation
|
|
701
|
+
and wrapping behavior is visible. Always design the empty state and a loading
|
|
702
|
+
skeleton for the screen's primary data surface — don't only show the
|
|
703
|
+
happy-path populated state.
|
|
704
|
+
|
|
705
|
+
## Motion craft
|
|
706
|
+
|
|
707
|
+
Push motion the same way you push type and color: on purpose, not as a
|
|
708
|
+
uniform default. Duration bands: 150-250ms for micro-interactions (hover,
|
|
709
|
+
toggle, button press), 300-500ms for panel/sheet/modal transitions, and
|
|
710
|
+
500-800ms reserved for exactly one orchestrated page-load reveal (stagger
|
|
711
|
+
individual elements 60-100ms apart, capped at about 6 staggered elements —
|
|
712
|
+
more than that reads as sluggish, not polished). Ease functions: `ease-out`
|
|
713
|
+
for elements entering, `ease-in` for elements leaving. Animate only
|
|
714
|
+
`transform` and `opacity` for performance; avoid animating `width`/`height`/
|
|
715
|
+
`top`/`left`. Every non-essential animation must respect
|
|
716
|
+
`prefers-reduced-motion` (see the mandatory `<style>` block below). For
|
|
717
|
+
inline/Alpine screens, persist motion as durable timeline metadata: inspect
|
|
718
|
+
the current file's timeline with `get-motion-timeline`, then write changes
|
|
719
|
+
with `apply-motion-edit` using the same `sourceRef`/`fileId` — this is not a
|
|
720
|
+
one-way export, edits stay editable.
|
|
721
|
+
|
|
722
|
+
## Imagery
|
|
723
|
+
|
|
724
|
+
Generate real images for anything a real product would photograph or
|
|
725
|
+
illustrate: hero backgrounds, product shots, portraits/avatars, testimonial
|
|
726
|
+
photos, marketing/editorial imagery. Don't generate images for utility UI —
|
|
727
|
+
icons, data tables, form chrome, and dashboard widgets should stay as
|
|
728
|
+
tokened SVG/CSS, not photos.
|
|
729
|
+
|
|
730
|
+
- **Use the Assets generation tool** (`generate-asset`, or `insert-asset` once
|
|
731
|
+
an asset is chosen) instead of `<img>` placeholder URLs or colored-div
|
|
732
|
+
stand-ins. See the Core Rules image-generation bullet in `AGENTS.md` for the
|
|
733
|
+
full calling convention (default `tier: "fast"`, `callerAppId: "design"`,
|
|
734
|
+
matching `aspectRatio`).
|
|
735
|
+
- **Write image prompts as art direction, not a one-line label.** Specify
|
|
736
|
+
subject, composition, lens/framing, lighting, and palette, and tie the
|
|
737
|
+
palette/mood back to the design's own `:root` tokens so the image reads as
|
|
738
|
+
part of the same system rather than a stock photo dropped in. If a design
|
|
739
|
+
system is linked, fold its `imageStyle.styleDescription` into the prompt
|
|
740
|
+
(see `design-systems` skill) so generated imagery matches the brand's
|
|
741
|
+
established photographic/illustration style.
|
|
742
|
+
- **Default to `tier: "fast"`** (the cheap Gemini Flash "nanobanana"-class
|
|
743
|
+
model) for exploration and every non-final variant. Only request
|
|
744
|
+
`tier: "best"` for the final, user-approved hero image — not for every pass.
|
|
745
|
+
- **Match `aspectRatio` to the layout slot**: `21:9` for a full-bleed hero,
|
|
746
|
+
`4:3` for a card/feature image, `1:1` for an avatar or square thumbnail.
|
|
747
|
+
Mismatched aspect ratios force ugly crops in the browser.
|
|
748
|
+
- **Always write real `alt` text** describing the image's content — never
|
|
749
|
+
leave `alt=""` on a meaningful (non-decorative) image.
|
|
750
|
+
- **Placement is a two-step pass**: call `insert-asset` to place the chosen
|
|
751
|
+
image, then do one `edit-design` pass to adjust surrounding layout/spacing
|
|
752
|
+
if the inserted figure doesn't sit flush with the rest of the design.
|
|
@@ -73,6 +73,12 @@ interface DesignSystemData {
|
|
|
73
73
|
}
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
`imageStyle.styleDescription` is not just metadata — fold it into every
|
|
77
|
+
`generate-asset` prompt for a design linked to this system, alongside the
|
|
78
|
+
subject/composition/lighting direction described in the design-generation
|
|
79
|
+
skill's Imagery section. This is what keeps generated photography/illustration
|
|
80
|
+
on-brand instead of drifting to a generic stock look each time.
|
|
81
|
+
|
|
76
82
|
## Actions
|
|
77
83
|
|
|
78
84
|
### Creating a Design System
|
|
@@ -92,8 +98,8 @@ pnpm action create-design-system \
|
|
|
92
98
|
"textMuted": "#94A3B8"
|
|
93
99
|
},
|
|
94
100
|
"typography": {
|
|
95
|
-
"headingFont": "
|
|
96
|
-
"bodyFont": "
|
|
101
|
+
"headingFont": "<HEADING_FONT>",
|
|
102
|
+
"bodyFont": "<BODY_FONT>",
|
|
97
103
|
"headingWeight": "700",
|
|
98
104
|
"bodyWeight": "400",
|
|
99
105
|
"headingSizes": { "h1": "64px", "h2": "40px", "h3": "28px" }
|
|
@@ -105,6 +111,12 @@ pnpm action create-design-system \
|
|
|
105
111
|
}'
|
|
106
112
|
```
|
|
107
113
|
|
|
114
|
+
`<HEADING_FONT>` / `<BODY_FONT>` are placeholders — pick a real Google Fonts
|
|
115
|
+
pairing per the design-generation skill's Font Recommendations table (or the
|
|
116
|
+
brand's actual extracted fonts) rather than defaulting to Space Grotesk/DM
|
|
117
|
+
Sans every time; that pairing is this skill's own most common convergence
|
|
118
|
+
fingerprint.
|
|
119
|
+
|
|
108
120
|
If this is the user's first design system, it is automatically set as the default.
|
|
109
121
|
|
|
110
122
|
### Reading a Design System
|
|
@@ -257,8 +269,8 @@ When the user provides multiple sources, call all applicable import actions in p
|
|
|
257
269
|
4. **Documents supplement** — presentations may reveal brand colors not in code
|
|
258
270
|
5. **Images inform mood** — color temperature, density, visual style
|
|
259
271
|
6. **Aggregate into DesignSystemData** — merge all extracted tokens, resolve conflicts
|
|
260
|
-
|
|
261
|
-
|
|
272
|
+
7. **Call `create-design-system`** with the combined result
|
|
273
|
+
8. **Link to design** via `update-design --designSystemId`
|
|
262
274
|
|
|
263
275
|
## Applying Design System to Generated HTML
|
|
264
276
|
|
|
@@ -10,6 +10,24 @@ description: >-
|
|
|
10
10
|
|
|
11
11
|
How to export designs and generate handoff documentation for developers converting prototypes to production code.
|
|
12
12
|
|
|
13
|
+
## Other export actions
|
|
14
|
+
|
|
15
|
+
- **SVG**: `export-svg` exports a design project as an SVG document (a
|
|
16
|
+
`foreignObject` wrapper around the standalone HTML), giving agent parity
|
|
17
|
+
with the editor's Download SVG command. The editor's own Download SVG
|
|
18
|
+
command captures the live browser DOM for the most faithful snapshot; use
|
|
19
|
+
the action when you need agent-side SVG export without a live browser.
|
|
20
|
+
- **PNG**: there is no PNG export action. Point the user to the editor's
|
|
21
|
+
download menu (Download PNG) — PNG export is a client-side rasterization of
|
|
22
|
+
the live canvas and is not exposed as an agent action.
|
|
23
|
+
- **Deploy preview**: `deploy-design-preview` triggers a preview deploy for a
|
|
24
|
+
fusion-backed design branch. It requires the design's source to advertise
|
|
25
|
+
the `deployPreview` capability (fusion tier) and Builder.io to be connected;
|
|
26
|
+
a branch must already exist via `create-design-branch`. For inline/localhost
|
|
27
|
+
designs it returns `ctaRequired: true` with a Make-it-real CTA instead of
|
|
28
|
+
faking a deploy. This triggers a *preview* deploy only — production
|
|
29
|
+
publishing goes through the Builder Visual Editor's Publish flow.
|
|
30
|
+
|
|
13
31
|
## Export Formats
|
|
14
32
|
|
|
15
33
|
### HTML Export
|
|
@@ -70,9 +88,30 @@ pnpm action export-pdf --id <designId>
|
|
|
70
88
|
|
|
71
89
|
Returns all design data and files needed for the client to render a PDF.
|
|
72
90
|
|
|
73
|
-
##
|
|
91
|
+
## Coding Handoff
|
|
92
|
+
|
|
93
|
+
When a user wants to convert an Alpine.js + Tailwind prototype into production
|
|
94
|
+
code, use the canonical `export-coding-handoff` action instead of hand-writing
|
|
95
|
+
a handoff message:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pnpm action export-coding-handoff --id <designId>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
This returns tokenized raw and ZIP URLs any external coding agent can fetch,
|
|
102
|
+
plus a ready-to-copy prompt. The bundle reflects the design's **current**
|
|
103
|
+
state — live editor (collab) content plus the user's applied visual tweaks
|
|
104
|
+
resolved into the HTML `:root` — so the generated code matches what the user
|
|
105
|
+
actually tuned, not the original generated tokens. Pass `format: "json"` if the
|
|
106
|
+
receiving agent wants structured data instead of markdown, and `origin` to get
|
|
107
|
+
an absolute raw-code URL for a specific app origin. This is the canonical
|
|
108
|
+
design-to-code tool; prefer it over composing a handoff message by hand.
|
|
109
|
+
|
|
110
|
+
### Manual handoff template (fallback)
|
|
74
111
|
|
|
75
|
-
|
|
112
|
+
Use the template below only when `export-coding-handoff` isn't available or
|
|
113
|
+
the user explicitly wants a hand-composed summary instead of the action's
|
|
114
|
+
bundle. Compose it based on the design's actual HTML/tokens.
|
|
76
115
|
|
|
77
116
|
### Handoff Prompt Template
|
|
78
117
|
|
|
@@ -159,7 +198,11 @@ pnpm action export-html --id [designId]
|
|
|
159
198
|
|
|
160
199
|
### Generating the Handoff
|
|
161
200
|
|
|
162
|
-
When the user asks to "hand off" or "convert to production code"
|
|
201
|
+
When the user asks to "hand off" or "convert to production code", call
|
|
202
|
+
`export-coding-handoff --id <designId>` first (see Coding Handoff above) — it
|
|
203
|
+
already extracts tokens, resolves applied tweaks, and returns a ready-to-copy
|
|
204
|
+
prompt plus fetchable URLs. Only fall back to composing the manual template
|
|
205
|
+
by hand if that action is unavailable:
|
|
163
206
|
|
|
164
207
|
1. Read the design: `get-design --id <designId>`
|
|
165
208
|
2. Extract all CSS custom properties from the HTML
|
|
@@ -39,7 +39,12 @@ patterns live in `.agents/skills/`.
|
|
|
39
39
|
supports our own primitives.
|
|
40
40
|
- For raster image generation, restyling, or editing existing screenshots/photos,
|
|
41
41
|
use the available first-party Assets MCP tool such as `generate-asset` instead
|
|
42
|
-
of placeholders or generic stock imagery.
|
|
42
|
+
of placeholders or generic stock imagery. Default to `tier: "fast"` (the cheap
|
|
43
|
+
Gemini flash "nanobanana"-class model) unless the user explicitly asks for
|
|
44
|
+
best quality; pass `aspectRatio` matching the layout slot (e.g. `21:9` hero,
|
|
45
|
+
`4:3` card, `1:1` avatar); include the linked design system's
|
|
46
|
+
`imageStyle.styleDescription` in the prompt when one is linked; and always
|
|
47
|
+
pass `callerAppId: "design"`. When the Assets picker returns a
|
|
43
48
|
selected asset, preserve `assetId`, `runId`, and URLs verbatim; if a design is
|
|
44
49
|
open, call `insert-asset` with the chosen URL/id, then refine placement with
|
|
45
50
|
`get-design-snapshot` and `edit-design` as needed. If no Assets MCP tool is
|
|
@@ -223,6 +228,33 @@ patterns live in `.agents/skills/`.
|
|
|
223
228
|
with `path`/`url` when visualizing a flow; pass `paths` for a concise route
|
|
224
229
|
list. Then call `navigate --view editor --designId <id> --editorView overview`.
|
|
225
230
|
|
|
231
|
+
## Review, Breakpoints, Screen States & Components
|
|
232
|
+
|
|
233
|
+
- **Review**: `run-design-audit` runs a read-only accessibility audit over a
|
|
234
|
+
design's rendered HTML (missing alt/labels, tap-target size,
|
|
235
|
+
focus-visibility, reduced-motion coverage, a contrast hint) and returns
|
|
236
|
+
`A11yFinding[]`. `apply-a11y-fix` applies one deterministic inline fix for a
|
|
237
|
+
finding (contrast, tap-target size, focus ring) when `fixAvailable: true`.
|
|
238
|
+
`get-design-review` compares two design snapshots/branches and returns a
|
|
239
|
+
file-level visual diff (added/removed/modified). See the `design-generation`
|
|
240
|
+
skill's Phase 5 for when to run these.
|
|
241
|
+
- **Breakpoints**: `add-breakpoint`, `remove-breakpoint`, and
|
|
242
|
+
`set-active-breakpoint` manage the design's device-width frame set and which
|
|
243
|
+
frame new edits target. See the `design-generation` skill's "Breakpoints &
|
|
244
|
+
screen states" section.
|
|
245
|
+
- **Design states**: `create-design-state`, `apply-design-state`,
|
|
246
|
+
`capture-design-state`, `list-design-states`, and `delete-design-state`
|
|
247
|
+
manage named DOM/Alpine states (Loading/Empty/Error), static data fixtures,
|
|
248
|
+
and live app captures. See the same skill section.
|
|
249
|
+
- **Components**: `create-component` promotes a selected element into a
|
|
250
|
+
recognised reusable component; `index-components` scans a design's HTML for
|
|
251
|
+
existing component annotations; `get-component-details`,
|
|
252
|
+
`preview-component-prop-edit`, `apply-component-prop-edit`, and
|
|
253
|
+
`open-component-source` inspect, preview, persist, and navigate to a
|
|
254
|
+
component instance. See the `design-generation` skill's "Component reuse"
|
|
255
|
+
section — promote a 3+ times repeated pattern instead of inventing another
|
|
256
|
+
near-duplicate.
|
|
257
|
+
|
|
226
258
|
## App-Backed Skill Distribution
|
|
227
259
|
|
|
228
260
|
- The preferred hosted install path is
|
|
@@ -246,9 +278,13 @@ patterns live in `.agents/skills/`.
|
|
|
246
278
|
on that same `fileId` for follow-up refinement. The kept variant screen is a
|
|
247
279
|
representative direction, not the final deliverable: use `mode:
|
|
248
280
|
"replace-file"` to replace it with the actual requested app/product UI in the
|
|
249
|
-
chosen visual style.
|
|
250
|
-
|
|
251
|
-
|
|
281
|
+
chosen visual style. Keep the replacement complete but compact: prioritize
|
|
282
|
+
the primary workflow, and if the requested feature list is too large for one
|
|
283
|
+
reliable edit, represent secondary details as visible controls, states, or
|
|
284
|
+
affordances instead of expanding the action input. Do not leave a direction
|
|
285
|
+
board, variant brief, summary card, or prose description as the final screen.
|
|
286
|
+
Do not repeat delete/snapshot cycles, and do not call `generate-design` after
|
|
287
|
+
a variant pick.
|
|
252
288
|
- If inline chat choice buttons are unavailable, the user can tell you the
|
|
253
289
|
preferred screen name. Do not show a separate variant picker or ask them to
|
|
254
290
|
paste a copyable handoff summary.
|
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
* - `classReplace` — replaces one Tailwind class with another on the root node.
|
|
14
14
|
*
|
|
15
15
|
* **Tier B (real-app, localhost / fusion):** prop writes require the
|
|
16
|
-
* `applyEdit` source capability
|
|
17
|
-
*
|
|
16
|
+
* `applyEdit` source capability. Localhost sources have it (design bridge +
|
|
17
|
+
* user write consent); fusion sources gain it after bridge hardening. For
|
|
18
|
+
* sources without it the action returns a `ctaRequired: true` response and
|
|
19
|
+
* does not modify any source.
|
|
18
20
|
*
|
|
19
21
|
* See DESIGN-STUDIO-PLAN.md §6.1, §7 (preview/apply contract), §11 phase 2.
|
|
20
22
|
*/
|
|
@@ -43,11 +45,7 @@ import {
|
|
|
43
45
|
applyVisualEdit,
|
|
44
46
|
buildCodeLayerProjection,
|
|
45
47
|
} from "../shared/code-layer.js";
|
|
46
|
-
import type {
|
|
47
|
-
CodeLayerSource,
|
|
48
|
-
ClassEditIntent,
|
|
49
|
-
StyleEditIntent,
|
|
50
|
-
} from "../shared/code-layer.js";
|
|
48
|
+
import type { CodeLayerSource, ClassEditIntent } from "../shared/code-layer.js";
|
|
51
49
|
import {
|
|
52
50
|
componentNameFor,
|
|
53
51
|
componentNodeIdMatches,
|
|
@@ -342,70 +340,12 @@ export default defineAction({
|
|
|
342
340
|
);
|
|
343
341
|
}
|
|
344
342
|
|
|
345
|
-
// ──
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
let intent: ClassEditIntent | StyleEditIntent;
|
|
352
|
-
|
|
353
|
-
if (edit.kind === "alpineData") {
|
|
354
|
-
// x-data is not a standard CSS property or Tailwind class — we write it
|
|
355
|
-
// as a style-like "attribute" edit by embedding it in the class edit path
|
|
356
|
-
// via the `set` operation on a synthetic class string. Because the HTML
|
|
357
|
-
// patcher writes raw attribute values we use the attribute-set approach
|
|
358
|
-
// that `apply-visual-edit` already supports through the `style` path
|
|
359
|
-
// (targeting `x-data` as a custom property in an inline `style`
|
|
360
|
-
// attribute would corrupt the DOM, so instead we encode the value in a
|
|
361
|
-
// `data-agent-native-alpine-data` attribute and let the bridge pick it
|
|
362
|
-
// up). For maximum compatibility with the existing apply-visual-edit
|
|
363
|
-
// path we use a class operation to manipulate the x-data value through a
|
|
364
|
-
// well-known pattern the bridge understands.
|
|
365
|
-
//
|
|
366
|
-
// The cleanest path is to write `data-agent-native-alpine-data` as an
|
|
367
|
-
// attribute so the iframe bridge can relay it to Alpine as the effective
|
|
368
|
-
// x-data — but since the bridge postMessage layer handles x-data edits
|
|
369
|
-
// on preview, for the persist path we do a direct HTML attribute patch.
|
|
370
|
-
// We accomplish this by treating it as a `style` edit on a sentinel
|
|
371
|
-
// property that the patcher will place as an attribute. However the
|
|
372
|
-
// current patcher only handles CSS properties, so we write the x-data
|
|
373
|
-
// value through the attribute approach: stamp `data-agent-native-prop-x-data`.
|
|
374
|
-
intent = {
|
|
375
|
-
kind: "class",
|
|
376
|
-
target,
|
|
377
|
-
// Use the `set` operation with a minimal token list to mark the node's
|
|
378
|
-
// Alpine data without touching real layout classes. The actual x-data
|
|
379
|
-
// attribute is written below via a direct HTML splice.
|
|
380
|
-
operation: "add",
|
|
381
|
-
className: `data-[x-data=${JSON.stringify(edit.value)}]:hidden`, // sentinel (will not apply visually)
|
|
382
|
-
} as ClassEditIntent;
|
|
383
|
-
// Fall through to the direct HTML splice below.
|
|
384
|
-
} else if (edit.kind === "classReplace") {
|
|
385
|
-
intent = {
|
|
386
|
-
kind: "class",
|
|
387
|
-
target,
|
|
388
|
-
operation: "replace",
|
|
389
|
-
from: edit.from,
|
|
390
|
-
to: edit.to,
|
|
391
|
-
} as ClassEditIntent;
|
|
392
|
-
} else {
|
|
393
|
-
// attribute kind — write as a class add of a data-attribute sentinel so
|
|
394
|
-
// the existing patcher path handles it, then fall through to the direct
|
|
395
|
-
// splice for the actual attribute patch.
|
|
396
|
-
intent = {
|
|
397
|
-
kind: "class",
|
|
398
|
-
target,
|
|
399
|
-
operation: "add",
|
|
400
|
-
className: `data-[prop-${edit.attribute}]:hidden`, // sentinel
|
|
401
|
-
} as ClassEditIntent;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// ── Direct HTML splice for attribute edits (alpineData + attribute) ──────
|
|
405
|
-
// The existing apply-visual-edit patcher is class/style/text focused. For
|
|
406
|
-
// attribute mutations on the component root we splice the raw HTML directly
|
|
407
|
-
// using the node's source span (the same technique the patcher uses for
|
|
408
|
-
// attribute stamping).
|
|
343
|
+
// ── Apply the edit ───────────────────────────────────────────────────────
|
|
344
|
+
// - alpineData / attribute: attribute mutations on the component root are
|
|
345
|
+
// applied with a direct HTML splice using the node's source span — the
|
|
346
|
+
// deterministic patcher is class/style/text focused.
|
|
347
|
+
// - classReplace: routed through the deterministic apply-visual-edit
|
|
348
|
+
// patcher (same seam as all other class edits).
|
|
409
349
|
|
|
410
350
|
let patchedContent = html;
|
|
411
351
|
let changed = false;
|
|
@@ -420,8 +360,15 @@ export default defineAction({
|
|
|
420
360
|
);
|
|
421
361
|
patchedContent = result.content;
|
|
422
362
|
changed = result.changed;
|
|
423
|
-
} else
|
|
424
|
-
//
|
|
363
|
+
} else {
|
|
364
|
+
// classReplace — use the deterministic patcher for class edits.
|
|
365
|
+
const intent: ClassEditIntent = {
|
|
366
|
+
kind: "class",
|
|
367
|
+
target: { nodeId },
|
|
368
|
+
operation: "replace",
|
|
369
|
+
from: edit.from,
|
|
370
|
+
to: edit.to,
|
|
371
|
+
};
|
|
425
372
|
const patch = applyVisualEdit(html, intent, { source: codeLayerSource });
|
|
426
373
|
if (patch.result.status === "applied" && patch.result.changed) {
|
|
427
374
|
patchedContent = patch.content;
|
|
@@ -97,6 +97,18 @@ export function canPatchManagedMotionCss(content: string): boolean {
|
|
|
97
97
|
return /<\s*(?:!doctype|[a-z][a-z0-9:-]*(?:\s|>|\/>))/i.test(content);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
// Composite key for detecting duplicate (targetNodeId, property) track pairs.
|
|
101
|
+
// Uses the ASCII Unit Separator (U+001F) — an escape in source, so the file
|
|
102
|
+
// stays plain text (a literal NUL made tooling treat it as binary). U+001F
|
|
103
|
+
// cannot appear in a valid data-agent-native-node-id or CSS property name
|
|
104
|
+
// (both are validated to CSS-safe identifiers), so it can never collide with
|
|
105
|
+
// real content and remains an unambiguous field delimiter.
|
|
106
|
+
const MOTION_TRACK_KEY_SEPARATOR = "\x1f";
|
|
107
|
+
|
|
108
|
+
export function motionTrackKey(targetNodeId: string, property: string): string {
|
|
109
|
+
return `${targetNodeId}${MOTION_TRACK_KEY_SEPARATOR}${property}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
100
112
|
async function persistFileContent(
|
|
101
113
|
fileId: string,
|
|
102
114
|
designId: string,
|
|
@@ -168,7 +180,10 @@ export default defineAction({
|
|
|
168
180
|
.number()
|
|
169
181
|
.int()
|
|
170
182
|
.positive()
|
|
171
|
-
|
|
183
|
+
// Keep in sync with the MotionDock default (DesignEditor
|
|
184
|
+
// motionDurationMs) and get-motion-timeline's CSS-recovery fallback so
|
|
185
|
+
// an omitted duration means the same thing on every surface.
|
|
186
|
+
.default(1000)
|
|
172
187
|
.describe("Total animation duration in milliseconds."),
|
|
173
188
|
defaultEase: z
|
|
174
189
|
.string()
|
|
@@ -254,9 +269,21 @@ export default defineAction({
|
|
|
254
269
|
|
|
255
270
|
// Reject CSS-injection vectors in caller-supplied track properties,
|
|
256
271
|
// keyframe values, and easing strings before they are compiled into the
|
|
257
|
-
// managed <style> block.
|
|
272
|
+
// managed <style> block. Also reject duplicate (targetNodeId, property)
|
|
273
|
+
// pairs: the compiler derives the animation name from that pair, so a
|
|
274
|
+
// duplicate would silently overwrite the earlier track's keyframes.
|
|
275
|
+
const seenTrackKeys = new Set<string>();
|
|
258
276
|
for (const track of typedTracks) {
|
|
259
277
|
assertSafeMotionCssProperty(track.property, "track.property");
|
|
278
|
+
const trackKey = motionTrackKey(track.targetNodeId, track.property);
|
|
279
|
+
if (seenTrackKeys.has(trackKey)) {
|
|
280
|
+
throw new Error(
|
|
281
|
+
`Duplicate motion track for targetNodeId "${track.targetNodeId}" ` +
|
|
282
|
+
`and property "${track.property}". Each (targetNodeId, property) ` +
|
|
283
|
+
"pair may appear at most once — merge the keyframes into a single track.",
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
seenTrackKeys.add(trackKey);
|
|
260
287
|
for (const kf of track.keyframes) {
|
|
261
288
|
assertSafeMotionCssToken(kf.value, "keyframe value");
|
|
262
289
|
if (kf.ease !== undefined) {
|
|
@@ -106,6 +106,7 @@ function scopeClassIntentToBreakpoint(
|
|
|
106
106
|
prefix,
|
|
107
107
|
operation: "replace",
|
|
108
108
|
utility: intent.to,
|
|
109
|
+
from: intent.from,
|
|
109
110
|
};
|
|
110
111
|
}
|
|
111
112
|
|
|
@@ -474,11 +475,12 @@ export default defineAction({
|
|
|
474
475
|
const patch = applyVisualEdit("", editIntent, {
|
|
475
476
|
source: codeLayerSource,
|
|
476
477
|
});
|
|
478
|
+
// local-file / remote-url sources are not editable here (the engine
|
|
479
|
+
// reports "unsupported"), so no byte counts are returned — a 0/0 pair
|
|
480
|
+
// would misleadingly suggest an empty file was measured.
|
|
477
481
|
return {
|
|
478
482
|
result: patch.result,
|
|
479
483
|
projection: patch.projection,
|
|
480
|
-
bytesBefore: 0,
|
|
481
|
-
bytesAfter: 0,
|
|
482
484
|
};
|
|
483
485
|
}
|
|
484
486
|
|