@agent-native/core 0.136.4 → 0.137.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/clips/.agents/skills/recording/SKILL.md +53 -0
- package/corpus/templates/clips/actions/import-loom-recording.ts +7 -0
- package/corpus/templates/clips/actions/lib/loom-import-job.ts +24 -4
- package/corpus/templates/clips/app/components/import-menu.tsx +109 -0
- package/corpus/templates/clips/app/components/library/library-grid.tsx +8 -18
- package/corpus/templates/clips/app/components/library/library-layout.tsx +14 -50
- package/corpus/templates/clips/app/components/library/sort-menu.tsx +20 -6
- package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +10 -34
- package/corpus/templates/clips/app/routes/_app.library._index.tsx +0 -2
- package/corpus/templates/clips/changelog/2026-08-03-restart-during-a-recording-now-immediately-starts-a-fresh-ta.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-04-cleaner-library-controls-with-an-icon-only-sort-button.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-04-upload-and-loom-imports-now-live-under-one-import-menu.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +81 -30
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +436 -158
- package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +14 -1
- package/corpus/templates/clips/server/plugins/auth.ts +9 -0
- package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +13 -0
- package/corpus/templates/content/actions/_database-utils.ts +6 -0
- package/corpus/templates/content/actions/bind-content-database-source-field.ts +273 -97
- package/corpus/templates/content/actions/change-content-database-source-role.ts +65 -29
- package/corpus/templates/content/actions/configure-document-property.ts +113 -32
- package/corpus/templates/content/actions/delete-content-database.ts +5 -7
- package/corpus/templates/content/actions/delete-document-property.ts +109 -62
- package/corpus/templates/content/actions/delete-document.ts +178 -36
- package/corpus/templates/content/actions/duplicate-database-item.ts +27 -0
- package/corpus/templates/content/actions/duplicate-database-items.ts +31 -1
- package/corpus/templates/content/actions/permanently-delete-document.ts +4 -6
- package/corpus/templates/content/actions/remove-database-items.ts +13 -0
- package/corpus/templates/content/actions/set-document-property.ts +69 -1
- package/corpus/templates/content/actions/upsert-database-item-by-key.ts +814 -0
- package/corpus/templates/content/parity/matrix.md +1 -1
- package/corpus/templates/content/parity/matrix.ts +1 -0
- package/corpus/templates/content/server/db/schema.ts +27 -0
- package/corpus/templates/content/server/plugins/db.ts +24 -0
- package/corpus/templates/design/app/components/design/BreakpointBar.tsx +122 -114
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +2 -2
- package/corpus/templates/design/app/components/design/EditPanel.tsx +68 -34
- package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +78 -4
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +8 -0
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +5 -2
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +10 -9
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +31 -69
- package/corpus/templates/design/app/components/design/inspector/design-icons.tsx +80 -106
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +4 -0
- package/corpus/templates/design/app/hooks/use-editor-preferences.ts +75 -0
- package/corpus/templates/design/app/i18n-data.ts +8 -0
- package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +61 -0
- package/corpus/templates/design/app/pages/design-editor/editor-preferences.ts +98 -0
- package/corpus/templates/design/app/pages/design-editor/nudge-intent.ts +491 -0
- package/corpus/templates/design/app/pages/design-editor/paste-placement.ts +133 -0
- package/corpus/templates/design/app/pages/design-editor/png-export-render.ts +8 -0
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +5 -12
- package/corpus/templates/design/changelog/2026-08-03-arrow-keys-now-reorder-a-layer-inside-an-auto-layout-frame.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-auto-layout-flow-icons-show-the-direction-at-a-glance-instea.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-auto-layout-icons-share-one-outlined-square-style-so-flow-ga.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-export-has-a-named-preview-section-you-can-expand-replacing-.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-export-preview-shows-the-selected-element-instead-of-the-who.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-inspector-sections-show-an-expand-indicator-so-it-is-clear-w.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-paste-lands-inside-a-selected-frame-instead-of-beside-it.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-the-add-breakpoint-button-sits-outside-the-breakpoint-select.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-the-agent-s-follow-up-questions-keep-space-below-the-continu.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-the-agent-s-follow-up-questions-share-the-chat-panel-s-backg.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-the-export-panel-no-longer-stacks-a-duplicate-export-block-o.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-the-text-tool-uses-a-t-icon-matching-the-letter-it-inserts.md +6 -0
- package/corpus/templates/factory/.env.example +17 -0
- package/corpus/templates/factory/AGENTS.md +27 -17
- package/corpus/templates/factory/README.md +19 -9
- package/corpus/templates/factory/actions/get-slack-feedback-context.ts +90 -0
- package/corpus/templates/factory/actions/get-triage-config.ts +14 -3
- package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +386 -0
- package/corpus/templates/factory/actions/list-factory-automations.ts +53 -0
- package/corpus/templates/factory/actions/list-triage-items.ts +7 -2
- package/corpus/templates/factory/actions/poll-github-sources.ts +203 -0
- package/corpus/templates/factory/actions/poll-sentry-errors.ts +161 -0
- package/corpus/templates/factory/actions/poll-slack-channel.ts +4 -2
- package/corpus/templates/factory/actions/run-factory-automation.ts +42 -0
- package/corpus/templates/factory/actions/save-factory-automation.ts +69 -0
- package/corpus/templates/factory/actions/save-triage-config.ts +28 -2
- package/corpus/templates/factory/actions/start-builder-for-item.ts +411 -0
- package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +14 -3
- package/corpus/templates/factory/app/components/ui/checkbox.tsx +1 -0
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +7 -0
- package/corpus/templates/factory/app/i18n/en-US.ts +35 -1
- package/corpus/templates/factory/app/routes/factory.tsx +462 -9
- package/corpus/templates/factory/changelog/2026-08-04-added-organization-owned-factory-automations-for-slack-bug-t.md +6 -0
- package/corpus/templates/factory/changelog/2026-08-04-automation-editor-controls-and-model-picker.md +6 -0
- package/corpus/templates/factory/netlify.toml +2 -2
- package/corpus/templates/factory/server/connectors/credentials.ts +6 -5
- package/corpus/templates/factory/server/connectors/slack.ts +133 -5
- package/corpus/templates/factory/server/db/schema.ts +6 -0
- package/corpus/templates/factory/server/lib/require-factory-automation.ts +65 -0
- package/corpus/templates/factory/server/plugins/agent-chat.ts +29 -11
- package/corpus/templates/factory/server/plugins/auth.ts +1 -0
- package/corpus/templates/factory/server/plugins/factory-migrations.ts +12 -0
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +218 -67
- package/corpus/templates/factory/server/triage/builder-executor.ts +4 -0
- package/corpus/templates/factory/server/triage/contracts.ts +14 -1
- package/corpus/templates/factory/server/triage/github-client.ts +404 -0
- package/corpus/templates/factory/server/triage/metadata.ts +44 -0
- package/corpus/templates/factory/server/triage/pr-babysit.ts +5 -5
- package/corpus/templates/factory/server/triage/pr-policy.ts +144 -0
- package/corpus/templates/factory/server/triage/sentry-client.ts +180 -0
- package/corpus/templates/factory/server/triage/slack-client.ts +43 -0
- package/corpus/templates/factory/server/triage/slack-poller.ts +9 -2
- package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +27 -0
- package/corpus/templates/plan/.agents/skills/visual-plan/references/canvas.md +14 -4
- package/corpus/templates/plan/.agents/skills/visual-plan/references/wireframe.md +7 -0
- package/corpus/templates/plan/.agents/skills/visual-recap/references/wireframe.md +7 -0
- package/corpus/templates/plan/actions/create-plan-design.ts +8 -6
- package/corpus/templates/plan/actions/create-prototype-plan.ts +8 -6
- package/corpus/templates/plan/actions/create-ui-plan.ts +7 -7
- package/corpus/templates/plan/actions/get-plan-blocks.ts +7 -1
- package/corpus/templates/plan/actions/validate-plan-input.ts +39 -0
- package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +167 -11
- package/corpus/templates/plan/app/i18n/en-US.ts +1 -0
- package/corpus/templates/plan/changelog/2026-08-04-canvas-zoom-controls-now-explain-the-command-ctrl-plus-scrol.md +6 -0
- package/corpus/templates/plan/changelog/2026-08-04-pinch-to-zoom-is-now-supported-on-touchscreens-in-visual-pla.md +6 -0
- package/corpus/templates/plan/shared/plan-content.ts +17 -2
- package/dist/cli/skills-content/canvas.d.ts +3 -3
- package/dist/cli/skills-content/canvas.js +16 -5
- package/dist/cli/skills-content/visual-plan-skill.d.ts +1 -1
- package/dist/cli/skills-content/visual-plan-skill.js +27 -0
- package/dist/cli/skills-content/wireframe.d.ts +2 -2
- package/dist/cli/skills-content/wireframe.js +7 -0
- package/dist/client/AgentPanel.js +23 -8
- package/dist/client/api-path.d.ts +6 -0
- package/dist/client/api-path.js +55 -0
- package/dist/client/onboarding/FirstRunOnboarding.d.ts +3 -0
- package/dist/client/onboarding/FirstRunOnboarding.js +93 -0
- package/dist/client/onboarding/first-run-enabled.d.ts +2 -0
- package/dist/client/onboarding/first-run-enabled.js +9 -0
- package/dist/client/onboarding/index.d.ts +2 -1
- package/dist/client/onboarding/index.js +1 -0
- package/dist/client/onboarding/use-onboarding.d.ts +6 -1
- package/dist/client/onboarding/use-onboarding.js +32 -1
- package/dist/client/route-state.js +10 -0
- package/dist/client/settings/SecretsSection.js +32 -8
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/connections/catalog.d.ts +4 -4
- package/dist/connections/catalog.js +3 -3
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +1 -1
- package/dist/localization/default-messages.d.ts +5 -0
- package/dist/localization/default-messages.js +5 -0
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/observability/routes.d.ts +3 -3
- package/dist/onboarding/app-profile.d.ts +4 -0
- package/dist/onboarding/app-profile.js +378 -0
- package/dist/onboarding/index.d.ts +2 -1
- package/dist/onboarding/index.js +1 -0
- package/dist/onboarding/plugin.d.ts +2 -0
- package/dist/onboarding/plugin.js +21 -1
- package/dist/onboarding/types.d.ts +19 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
- package/dist/provider-api/actions/provider-api.d.ts +12 -12
- package/dist/secrets/routes.d.ts +2 -2
- package/dist/secrets/routes.js +41 -17
- package/dist/server/auth.js +21 -1
- package/dist/server/better-auth-instance.d.ts +3 -2
- package/dist/server/better-auth-instance.js +9 -7
- package/dist/server/email-markdown.js +3 -8
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/first-run-onboarding.d.ts +3 -0
- package/dist/shared/first-run-onboarding.js +3 -0
- package/dist/triggers/index.d.ts +3 -0
- package/dist/triggers/index.js +5 -0
- package/docs/content/cross-app-sso.mdx +26 -0
- package/package.json +1 -1
|
@@ -292,6 +292,11 @@ folding framework chrome into the product UI.
|
|
|
292
292
|
\`content.prototype\`, and rely on the top visual tabs to switch between them.
|
|
293
293
|
When both surfaces are present, open the Wireframes tab by default; the
|
|
294
294
|
prototype remains available as the interactive follow-up view.
|
|
295
|
+
- **Default to wireframes.** A clean, minimal UI, a high UX bar, or references
|
|
296
|
+
to Linear/Vercel describe the content and density bar; they do not request
|
|
297
|
+
full-fidelity design mode. Use renderer-owned wireframes unless the user
|
|
298
|
+
explicitly asks for branded, pixel-accurate, production-like, or full visual
|
|
299
|
+
design. This keeps every canvas screen inspectable and its full content visible.
|
|
295
300
|
- **Prototype-first** when the user asks to operate the UI or when interaction is
|
|
296
301
|
the main question. Use \`create-prototype-plan\`, which still preserves static
|
|
297
302
|
mocks where useful.
|
|
@@ -354,6 +359,28 @@ For a worked example of the bar — a great UI-first plan and \`/visual-plan\`,
|
|
|
354
359
|
the anti-patterns to avoid — READ \`references/exemplar.md\` in this skill
|
|
355
360
|
directory before authoring a plan.
|
|
356
361
|
|
|
362
|
+
## Authoring invariants
|
|
363
|
+
|
|
364
|
+
Treat these as data-integrity checks, not optional polish:
|
|
365
|
+
|
|
366
|
+
- \`content\` is a complete replacement. Pass either \`content\` or the mode's
|
|
367
|
+
convenience arrays (\`screens\`/\`transitions\` or \`states\`/\`components\`),
|
|
368
|
+
never both. The create actions reject mixed sources so a second payload cannot
|
|
369
|
+
silently discard CSS, frames, or document blocks.
|
|
370
|
+
- A design screen's scoped \`css\` is part of the artifact. Keep it on both the
|
|
371
|
+
prototype screen and its matching canvas frame, and use renderer-owned
|
|
372
|
+
\`--wf-*\` tokens for portable color and typography.
|
|
373
|
+
- Rich-text \`data.markdown\` must contain actual runtime line breaks. Do not
|
|
374
|
+
hand a plan a one-line Markdown value containing literal \`\\n\` escape text,
|
|
375
|
+
which renders the whole section as one heading. Escaped newlines are fine in
|
|
376
|
+
code examples when the surrounding Markdown still has real line breaks.
|
|
377
|
+
- Canvas artboards do not scroll. Keep wireframe HTML in natural flow and set a
|
|
378
|
+
larger frame \`height\` when a screen exceeds the surface preset; preserve the
|
|
379
|
+
surface width and inspect the bottom edge at default zoom before handoff.
|
|
380
|
+
- After every hosted write, re-read the structured content and inspect the live
|
|
381
|
+
Plan surface. A valid JSON payload is not proof that CSS loaded or Markdown
|
|
382
|
+
rendered into the intended heading, paragraph, and list structure.
|
|
383
|
+
|
|
357
384
|
## Tool Guidance
|
|
358
385
|
|
|
359
386
|
- \`create-visual-plan\`: start one structured visual plan per agent task/run, or
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const WIREFRAME_QUALITY_CORE = "<!-- SHARED-CORE:wireframe-quality START -->\n\n**A wireframe is an HTML mockup. The renderer owns the look; you write the\ncontent.** Set `data.html` to a self-contained, semantic HTML fragment of the\nscreen and set `data.surface`. The renderer owns the surface footprint/aspect,\nthe dark/light theme, the hand-drawn font, and the rough.js sketch overlay \u2014 you\nnever write `<html>`/`<body>`/`<script>`/`<style>` tags or any\nwidth/height/coordinates. You write real HTML layout and real product\ncontent; the renderer styles and roughens it.\n\n**A wireframe block's data is an HTML screen plus a surface:**\n\n```json\n{\n \"surface\": \"browser\",\n \"html\": \"<div style=\\\"display:flex;flex-direction:column;gap:10px;padding:16px;height:100%\\\"><h1>Sign in</h1><p class=\\\"wf-muted\\\">Use your work email to continue.</p><div class=\\\"wf-card\\\" style=\\\"display:flex;flex-direction:column;gap:10px\\\"><label>Email<input value=\\\"jane@acme.co\\\" /></label><label>Password<input value=\\\"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\\\" /></label><label style=\\\"display:flex;align-items:center;gap:8px\\\"><input type=\\\"checkbox\\\" checked /> Remember me</label><button class=\\\"primary\\\">Sign in</button></div><a href=\\\"#\\\">Forgot password?</a></div>\"\n}\n```\n\n**Write PLAIN semantic HTML and let the renderer style it.** Bare elements\n(`h1`/`h2`/`h3`, `p`, `button`, `input`, `<input type=\"checkbox\">`, `a`, `hr`)\nare auto-themed \u2014 no classes needed. Helper classes carry the rest:\n\n- `.wf-card` / `.wf-box` \u2014 a bordered, padded container (a panel, a list item).\n- `.wf-pill` / `.wf-chip` \u2014 a rounded tag or filter; add `.accent`\n (`<span class=\"wf-pill accent\">`) for the accent-filled variant.\n- `.wf-muted` \u2014 secondary/muted text (or use `<small>`).\n- `button.primary` or any element with `[data-primary]` \u2014 the accent-filled\n primary button.\n\n**No decorative shadows around mockups.** Do not put `box-shadow`, `filter:\ndrop-shadow(...)`, Tailwind `shadow-*` classes, or other fake depth effects on a\nwireframe frame, root container, `.wf-card` / `.wf-box`, or canvas artboard.\nMockups should read as flat, bordered surfaces; use spacing, borders, labels,\nand annotations for separation. Only show a shadow when the real product UI\nalready has that shadow and it is essential to the change being reviewed.\n\n**Use renderer icons, not visible icon words.** For icon-only buttons or leading\nicons inside fields, chips, menu items, and toolbars, write an empty marker such\nas `<span data-icon=\"mail\" aria-label=\"Email\"></span>` or\n`<i data-icon=\"lock\"></i>`. The renderer replaces it with a Tabler-style SVG and\nthe `.wf-icon` class sizes it to the surrounding text. Supported names and\naliases: `mail`/`email`, `lock`/`password`, `search`, `plus`/`add`, `x`/`close`,\n`check`, `chevronDown`, `chevronUp`, `chevronLeft`, `chevronRight`, `dots`/`more`,\n`chevron`/`caret`/`dropdown` (down chevron), `user`, `settings`, `calendar`,\n`bell`, `send`, `edit`, `arrowLeft`, and `arrowRight`. Do not put visible words\nlike \"email\", \"lock\", \"search\", \"chevron\", or \"more\" where the product UI would\nshow an icon; use text only when it is a real label a user would read.\n\n**Use the `--wf-*` tokens for any custom color, never hex.** The renderer flips\nthese on light/dark, so reading them is what keeps a mockup correct in both\nthemes. For any inline border, background, or text color, reference a token:\n`style=\"border:1.4px solid var(--wf-line)\"`. The tokens are `--wf-ink` (text),\n`--wf-muted` (secondary text), `--wf-line` (borders/dividers), `--wf-paper`\n(page background), `--wf-card` (container surface), `--wf-accent` /\n`--wf-accent-fg` / `--wf-accent-soft` (brand action), `--wf-warn`, `--wf-ok`,\nand `--wf-radius`. Never hard-code a hex color and never set `font-family` \u2014 the\nrenderer owns the sketch/clean font.\n\n**Never use host/Tailwind theme classes in wireframe HTML.** Classes such as\n`bg-white`, `bg-zinc-50`, `bg-slate-950`, `text-zinc-950`,\n`text-slate-400`, `border-zinc-200`, `hover:bg-slate-800`, `shadow-xl`,\nor arbitrary color utilities like `bg-[#fff]` leak the host app's CSS into the\nmockup and can make dark-mode canvas frames unreadable. Use bare semantic\nelements, `.wf-*` helper classes, and `--wf-*` color tokens instead. Before\npublishing, scan every wireframe `class` and `style` attribute: if a class sets\nbackground, text, border, ring, fill, stroke, gradient, placeholder, decoration,\nor shadow color, rewrite it to renderer tokens or remove it. Layout-only classes\nare still discouraged; inline flex/grid styles are safer and easier to review.\n\n**Keep Rough.js sparse.** The renderer sketches the outer frame, standard\n`.wf-*` primitives, controls, and inline border dividers by default. Do not add\n`data-rough` to broad root wrappers, dialog shells, page panels, grid cells, or\nnested containers unless that single container is the visual point. Use\n`data-rough` only for a deliberate one-off shape. If a mockup starts looking\nlike stacked/overlapping sketch lines, remove rough targets from parent\ncontainers and let backgrounds plus spacing separate the surfaces.\n\n**Use literal CSS lengths for spacing.** The `--wf-*` tokens are for colors and\nrenderer-owned visual styling, not layout spacing. Do not use guessed spacing\ntokens such as `var(--wf-space-4)`, Tailwind spacing classes, or theme spacing\nvariables inside wireframe HTML; if a token is unavailable in the Plan renderer,\npadding collapses and content hugs the border. Use explicit CSS lengths for\nlayout: `padding:16px`, `gap:12px`, `margin-top:18px`, `minmax(0,1fr)`.\n\n**Lay out with inline `style` flex/grid.** You write the real layout \u2014\n`display:flex; flex-direction:column; gap:10px; padding:16px` and so on \u2014 and the\nrenderer never repositions anything. Compose the actual product: reproduce the\ncurrent screen, then show the modification. Real labels, real counts, real dates,\nreal button text grounded in the screen you read; not lorem or gray bars.\n\n**Surface presets \u2014 match the real footprint, never default to desktop+mobile.**\nPick the `surface` that matches what the user will actually see:\n\n- `browser`: a web page that needs a browser chrome frame around it.\n- `desktop`: a full desktop app page or app shell.\n- `mobile`: a phone screen, only when the work is genuinely mobile.\n- `popover`: a small floating menu, dropdown, or inline popover.\n- `panel`: a side panel, inspector, or sidebar widget.\n\nA sidebar popover renders as a small surface, not a desktop page and a phone\nframe. Do not emit `desktop` + `mobile` variants unless responsive behavior\nactually changes the layout. For a component or widget, show one broader\napp-context frame only when placement affects understanding, then the focused\ncomponent states.\n\n**Model the actual component shell for small surfaces.** A rendered UI change\nbelongs in a wireframe; reserve `diagram` for architecture, dependency, state,\nor data-flow relationships. Popovers, dropdown menus, command palettes, and\ncontext menus use `surface: \"popover\"` unless the surrounding page placement is\nthe point of the change. Dialogs, sheets, inspectors, sidebars, and long\nproperty panels use the matching `panel` / `desktop` surface as appropriate.\nShow the real chrome: trigger or anchor when it matters, title/header row,\ntop-right actions, separators, fields, options, selected states, body content,\nand footer actions that are visible in the workflow.\n\n**Modify, don't redesign.** When the task changes an existing screen, reproduce\nthe current screen's real layout and footprint FIRST, then change only the delta\nand call it out with a single annotation. Do not restack the page into a new\nlayout. For net-new surfaces, compose from the real app shell. Inspect the\nactual app components before drawing an existing product: sidebar density,\ntoolbar actions, overflow menus, property panels, and framework chrome should\nmatch the product unless the plan intentionally changes them.\n\n**Keep product screens pure.** A product wireframe shows the app state a user\nwould actually see. Do not embed file contracts, architecture arrows, repo pills,\nmode explanations, or implementation callouts inside the screen just to explain\nthe plan. Put those in canvas annotations, a separate diagram, or the document\nbody. Secondary UI such as properties, history, sync, export, or agent controls\nshould appear where the real product would put them: an overflow popover, sheet,\npanel, or separate framework sidebar state, not a generic permanent right\ninspector unless that inspector is the actual design.\n\n**Classify mockup scope before implementation.** Before turning a plan mockup\ninto source code, decide whether each artboard represents the whole page/app\nshell, a route body inside an existing shell, or a component/sub-surface. If an\nartboard includes navigation, sidebars, auth banners, or a signup/login form,\nmap those pieces to the real shared shell/auth components instead of nesting the\nentire mockup inside the current page. When a mockup references the product's\nstandard signup/login page, find and reuse that existing implementation; do not\napproximate it from the wireframe.\n\n**Zoom in on sub-surfaces, don't redraw the page.** For a small sub-surface (a\npopover, menu, dialog, toast), show the full screen once, then add a small\nseparate artboard whose `html` contains ONLY that sub-surface \u2014 do not re-draw\nthe whole page around it, and do not scale a duplicate up. Pick the matching\n`surface` (e.g. `popover`) so the footprint is right; never widen a popover to\npage width.\n\n**Loading / skeleton states.** Set `data.skeleton: true` on the wireframe and\nfill the `html` with neutral, textless placeholder geometry \u2014 boxes and bars\nbuilt as `<div>`s with `background:var(--wf-line)` and explicit heights/widths,\nno labels or copy. The renderer drops borders, sketch, and color into the\nskeleton register automatically. Never escape to a `custom-html` document block\nto fake a loader.\n\n**Editing an existing mockup.** In hosted mode, to change one element, text, or\ncolor in an existing html mockup, do not regenerate the frame \u2014 call\n`update-visual-plan` with\n`contentPatches: [{ op: \"patch-wireframe-html\", blockId, edits: [{ find,\nreplace }] }]`. Each `find` is a unique snippet of the current html (read it\nfirst with `get-visual-plan`); set `all: true` on an edit to replace every\noccurrence. The result is re-sanitized. In local-files privacy mode, do not call\nhosted Plan tools; edit the local MDX source directly and rerun the local\ncheck/serve or verify command for `<plan-dir>`.\n\n**Choose the outer frame deliberately.** Wireframe and diagram data accept\n`frame: \"auto\" | \"show\" | \"hide\"` in block data (`<Screen frame=\"hide\">` in\nMDX wireframes, `<Diagram frame=\"hide\">` for MDX diagrams). Leave it unset or\n`auto` when the host context should decide: Plan and recap surfaces default to a\ndrawn outer frame; docs surfaces default to no outer frame. Use `show` for\nstandalone product screens, before/after recap comparisons, screenshot-like\nartifacts, and visuals that need containment from surrounding prose. Use `hide`\nwhen a docs page, tab, column, card, canvas artboard, or the visual's own\ninternal chrome already supplies the boundary. Do not use `hide` to compensate\nfor cramped content; fix the layout instead.\n\n**Inner padding and borders still matter.** Always wrap HTML wireframe content\nin a root container with real inner padding before drawing cards, fields, pills,\nlabels, or controls. Use at least 14-16px of padding, `box-sizing: border-box`,\n`height: 100%`, and `gap` between child rows on the root node itself so the\nfirst row never sits flush against the screen edge. Do not rely on padding on a\nnested page section as the first visible inset; the outermost element must\ncreate the breathing room. Keep text away from borders: every container, field,\nbutton, menu item, and annotation needs enough padding and line-height to read\ncleanly in the rendered Plan view.\n\n**For feature-cloud or abundance visuals, optimize the composition over line-by-line\nreading.** Some marketing/product sections need to feel like a large surface area\nof capability rather than a precise app workflow. In those cases, use one padded\nroot with a short headline and a dense, aesthetic cloud of short feature labels,\nchips, rings, or columns. Vary scale and opacity with tokens, cluster by meaning,\nand let many labels be glanceable rather than individually essential. Do not\nforce dozens of features into equal cards with long wrapped sentences; that\nusually creates a messy unreadable mockup.\n\n**Lay out children safely so they never collide.** Use HTML flex/grid with\n`gap`, `min-width: 0`, and sensible overflow. Avoid negative margins, absolute\npositioning, or fixed child widths that can collide when the renderer switches\nbetween light/dark, sketch/clean, or different zoom levels.\n\n**Do not wrap intentionally single-line labels.** For toolbars, tab rails,\nbreadcrumbs, chip/filter rows, branch and file names, file chips, and code\nfilenames \u2014 any deliberately single-line row \u2014 do not let long text wrap. Put\n`white-space: nowrap` on the row (and `overflow: hidden; text-overflow: ellipsis`\non the individual labels that can grow), so the wireframe demonstrates the actual\nlayout behavior instead of producing ugly stacked or vertical text. Use\nhorizontally scrollable or clipped rails for overflow.\n\n**Fill the frame; keep labels short.** Each artboard is a fixed-size surface \u2014 compose enough realistic HTML to fill it top to bottom with even vertical rhythm; never leave a large empty band. On desktop/app-shell sidebars, let the nav stack flex to fill (`flex:1`) and add any persistent bottom action/status after it so the rail reads complete in taller frames. On mobile especially, flow real rows down the whole screen (status bar, header, then list/detail content) rather than a header floating above a gap. Keep every label short enough to sit on one line within its column \u2014 shorten the copy rather than relying on the frame to absorb it (long labels wrap or clip).\n\n**Persistent chrome bars span the full frame width.** Top bars, app headers,\ntoolbars, and bottom tab/nav bars are full-width chrome, not centered content.\nLay each one out as a single flex row that fills the frame\n(`style=\"display:flex;align-items:center;width:100%\"`) and push trailing actions\nto the right edge with a flex spacer (`<div style=\"flex:1\"></div>`) between the\nleading group and the trailing group \u2014 never center a bar inside a narrow,\ncentered block, and never let it collapse to the width of its contents. In a\nBefore/After pair the bar stays full-width in BOTH states even when one state has\nfewer controls; the spacer absorbs the difference so the remaining controls hold\ntheir edge alignment instead of sliding to the center.\n\n**Pin bottom bars to the bottom of the frame.** For mobile tab bars, footers, and\nany persistent bottom action row, make the frame itself a flex column at\n`height:100%` (`style=\"display:flex;flex-direction:column;height:100%\"`), give the\nscrolling body `flex:1` so it absorbs the slack, and place the bar as the LAST\nchild of the frame (or set `margin-top:auto` on it). The bar then sits flush at\nthe bottom of the surface instead of floating directly under the content with an\nempty band beneath it.\n\n**Before / after must be comparable.** When showing a state change, preserve the\nunchanged controls in both states so the reviewer can see exactly what moved or\nappeared; do not show an added control as a generic box floating elsewhere in\nthe surface. Place the new/changed affordance where the implementation puts it \u2014\nfor example, a new `Edit with AI` action in a popover header belongs in the\ntop-right header slot, aligned with the title, not in the body or footer. Use\nthe same frame size, scale, outer padding, border radius, and visual density on\nboth sides unless the change itself alters those properties, and let the frame\nheight fit the content rather than leaving a tall empty lower half.\n\n**Name the states with the column header, never inside the frame.** For\ndocument-body wireframes (recaps), put the two\nstates in a `columns` block and set each column's `label` to `Before` and\n`After` \u2014 the renderer draws that label as an `h4` heading above each frame. Do\nNOT bake a `Before`/`After` pill, title, or heading into the wireframe `html`: a\nlabel placed inside reads as part of the product UI, lands in a random corner,\nand clutters the comparison. The column header is the one and only place the\nstate name belongs. On a canvas, place the two state artboards as neighbors with\nframe labels \u2014 never encode Before/After inside the html.\n\n**Let the surface choose side-by-side vs. stacked.** For document-body\nwireframes (recaps), the `columns` renderer lays\nnarrow surfaces (`mobile`, `popover`, `panel`) out side by side, and\nautomatically stacks wide surfaces (`desktop`, `browser`) vertically at full\ndocument width so a large frame is never crushed into a half-width column and\ncropped. Author both wireframes with the real `surface` and the matching\n`Before`/`After` column labels; do not hand-stack the pair into separate\ntop-level wireframes or duplicate the state name as body content.\n\n**Good example \u2014 a contacts list, surface `browser`.** A small, real screen\ncomposed from the helper classes and tokens, layout in inline flex, no fonts or\nhex colors:\n\n```html\n<div\n style=\"display:flex;flex-direction:column;gap:12px;padding:16px;height:100%\"\n>\n <div style=\"display:flex;align-items:center;justify-content:space-between\">\n <h1>Contacts</h1>\n <button class=\"primary\">New contact</button>\n </div>\n <div style=\"display:flex;gap:6px\">\n <span class=\"wf-pill accent\">All 128</span>\n <span class=\"wf-pill\">Favorites</span>\n <span class=\"wf-pill\">Archived</span>\n </div>\n <div\n class=\"wf-card\"\n style=\"display:flex;flex-direction:column;gap:0;padding:0\"\n >\n <div\n style=\"display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1.4px solid var(--wf-line)\"\n >\n <div\n style=\"width:32px;height:32px;border-radius:999px;background:var(--wf-accent-soft)\"\n ></div>\n <div style=\"flex:1\">\n <strong>Jane Cooper</strong><br /><small>jane@acme.co</small>\n </div>\n <span class=\"wf-pill\">Lead</span>\n </div>\n <div style=\"display:flex;align-items:center;gap:10px;padding:10px 12px\">\n <div\n style=\"width:32px;height:32px;border-radius:999px;background:var(--wf-accent-soft)\"\n ></div>\n <div style=\"flex:1\">\n <strong>Marcus Lee</strong><br /><small>marcus@globex.io</small>\n </div>\n <span class=\"wf-pill\">Customer</span>\n </div>\n </div>\n</div>\n```\n\n<!-- SHARED-CORE:wireframe-quality END -->";
|
|
2
|
-
export declare const WIREFRAME_REFERENCE_MD = "# HTML wireframe quality \u2014 single source of truth\n\nThis file is the canonical quality bar for HTML wireframes / `<Screen>` /\n`WireframeBlock` content, shared word for word by `/visual-plan` and\n`/visual-recap`. Read it in full before authoring ANY wireframe; do not\nauthor wireframes from memory or paraphrase these rules per command.\n\n<!-- SHARED-CORE:wireframe-quality START -->\n\n**A wireframe is an HTML mockup. The renderer owns the look; you write the\ncontent.** Set `data.html` to a self-contained, semantic HTML fragment of the\nscreen and set `data.surface`. The renderer owns the surface footprint/aspect,\nthe dark/light theme, the hand-drawn font, and the rough.js sketch overlay \u2014 you\nnever write `<html>`/`<body>`/`<script>`/`<style>` tags or any\nwidth/height/coordinates. You write real HTML layout and real product\ncontent; the renderer styles and roughens it.\n\n**A wireframe block's data is an HTML screen plus a surface:**\n\n```json\n{\n \"surface\": \"browser\",\n \"html\": \"<div style=\\\"display:flex;flex-direction:column;gap:10px;padding:16px;height:100%\\\"><h1>Sign in</h1><p class=\\\"wf-muted\\\">Use your work email to continue.</p><div class=\\\"wf-card\\\" style=\\\"display:flex;flex-direction:column;gap:10px\\\"><label>Email<input value=\\\"jane@acme.co\\\" /></label><label>Password<input value=\\\"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\\\" /></label><label style=\\\"display:flex;align-items:center;gap:8px\\\"><input type=\\\"checkbox\\\" checked /> Remember me</label><button class=\\\"primary\\\">Sign in</button></div><a href=\\\"#\\\">Forgot password?</a></div>\"\n}\n```\n\n**Write PLAIN semantic HTML and let the renderer style it.** Bare elements\n(`h1`/`h2`/`h3`, `p`, `button`, `input`, `<input type=\"checkbox\">`, `a`, `hr`)\nare auto-themed \u2014 no classes needed. Helper classes carry the rest:\n\n- `.wf-card` / `.wf-box` \u2014 a bordered, padded container (a panel, a list item).\n- `.wf-pill` / `.wf-chip` \u2014 a rounded tag or filter; add `.accent`\n (`<span class=\"wf-pill accent\">`) for the accent-filled variant.\n- `.wf-muted` \u2014 secondary/muted text (or use `<small>`).\n- `button.primary` or any element with `[data-primary]` \u2014 the accent-filled\n primary button.\n\n**No decorative shadows around mockups.** Do not put `box-shadow`, `filter:\ndrop-shadow(...)`, Tailwind `shadow-*` classes, or other fake depth effects on a\nwireframe frame, root container, `.wf-card` / `.wf-box`, or canvas artboard.\nMockups should read as flat, bordered surfaces; use spacing, borders, labels,\nand annotations for separation. Only show a shadow when the real product UI\nalready has that shadow and it is essential to the change being reviewed.\n\n**Use renderer icons, not visible icon words.** For icon-only buttons or leading\nicons inside fields, chips, menu items, and toolbars, write an empty marker such\nas `<span data-icon=\"mail\" aria-label=\"Email\"></span>` or\n`<i data-icon=\"lock\"></i>`. The renderer replaces it with a Tabler-style SVG and\nthe `.wf-icon` class sizes it to the surrounding text. Supported names and\naliases: `mail`/`email`, `lock`/`password`, `search`, `plus`/`add`, `x`/`close`,\n`check`, `chevronDown`, `chevronUp`, `chevronLeft`, `chevronRight`, `dots`/`more`,\n`chevron`/`caret`/`dropdown` (down chevron), `user`, `settings`, `calendar`,\n`bell`, `send`, `edit`, `arrowLeft`, and `arrowRight`. Do not put visible words\nlike \"email\", \"lock\", \"search\", \"chevron\", or \"more\" where the product UI would\nshow an icon; use text only when it is a real label a user would read.\n\n**Use the `--wf-*` tokens for any custom color, never hex.** The renderer flips\nthese on light/dark, so reading them is what keeps a mockup correct in both\nthemes. For any inline border, background, or text color, reference a token:\n`style=\"border:1.4px solid var(--wf-line)\"`. The tokens are `--wf-ink` (text),\n`--wf-muted` (secondary text), `--wf-line` (borders/dividers), `--wf-paper`\n(page background), `--wf-card` (container surface), `--wf-accent` /\n`--wf-accent-fg` / `--wf-accent-soft` (brand action), `--wf-warn`, `--wf-ok`,\nand `--wf-radius`. Never hard-code a hex color and never set `font-family` \u2014 the\nrenderer owns the sketch/clean font.\n\n**Never use host/Tailwind theme classes in wireframe HTML.** Classes such as\n`bg-white`, `bg-zinc-50`, `bg-slate-950`, `text-zinc-950`,\n`text-slate-400`, `border-zinc-200`, `hover:bg-slate-800`, `shadow-xl`,\nor arbitrary color utilities like `bg-[#fff]` leak the host app's CSS into the\nmockup and can make dark-mode canvas frames unreadable. Use bare semantic\nelements, `.wf-*` helper classes, and `--wf-*` color tokens instead. Before\npublishing, scan every wireframe `class` and `style` attribute: if a class sets\nbackground, text, border, ring, fill, stroke, gradient, placeholder, decoration,\nor shadow color, rewrite it to renderer tokens or remove it. Layout-only classes\nare still discouraged; inline flex/grid styles are safer and easier to review.\n\n**Keep Rough.js sparse.** The renderer sketches the outer frame, standard\n`.wf-*` primitives, controls, and inline border dividers by default. Do not add\n`data-rough` to broad root wrappers, dialog shells, page panels, grid cells, or\nnested containers unless that single container is the visual point. Use\n`data-rough` only for a deliberate one-off shape. If a mockup starts looking\nlike stacked/overlapping sketch lines, remove rough targets from parent\ncontainers and let backgrounds plus spacing separate the surfaces.\n\n**Use literal CSS lengths for spacing.** The `--wf-*` tokens are for colors and\nrenderer-owned visual styling, not layout spacing. Do not use guessed spacing\ntokens such as `var(--wf-space-4)`, Tailwind spacing classes, or theme spacing\nvariables inside wireframe HTML; if a token is unavailable in the Plan renderer,\npadding collapses and content hugs the border. Use explicit CSS lengths for\nlayout: `padding:16px`, `gap:12px`, `margin-top:18px`, `minmax(0,1fr)`.\n\n**Lay out with inline `style` flex/grid.** You write the real layout \u2014\n`display:flex; flex-direction:column; gap:10px; padding:16px` and so on \u2014 and the\nrenderer never repositions anything. Compose the actual product: reproduce the\ncurrent screen, then show the modification. Real labels, real counts, real dates,\nreal button text grounded in the screen you read; not lorem or gray bars.\n\n**Surface presets \u2014 match the real footprint, never default to desktop+mobile.**\nPick the `surface` that matches what the user will actually see:\n\n- `browser`: a web page that needs a browser chrome frame around it.\n- `desktop`: a full desktop app page or app shell.\n- `mobile`: a phone screen, only when the work is genuinely mobile.\n- `popover`: a small floating menu, dropdown, or inline popover.\n- `panel`: a side panel, inspector, or sidebar widget.\n\nA sidebar popover renders as a small surface, not a desktop page and a phone\nframe. Do not emit `desktop` + `mobile` variants unless responsive behavior\nactually changes the layout. For a component or widget, show one broader\napp-context frame only when placement affects understanding, then the focused\ncomponent states.\n\n**Model the actual component shell for small surfaces.** A rendered UI change\nbelongs in a wireframe; reserve `diagram` for architecture, dependency, state,\nor data-flow relationships. Popovers, dropdown menus, command palettes, and\ncontext menus use `surface: \"popover\"` unless the surrounding page placement is\nthe point of the change. Dialogs, sheets, inspectors, sidebars, and long\nproperty panels use the matching `panel` / `desktop` surface as appropriate.\nShow the real chrome: trigger or anchor when it matters, title/header row,\ntop-right actions, separators, fields, options, selected states, body content,\nand footer actions that are visible in the workflow.\n\n**Modify, don't redesign.** When the task changes an existing screen, reproduce\nthe current screen's real layout and footprint FIRST, then change only the delta\nand call it out with a single annotation. Do not restack the page into a new\nlayout. For net-new surfaces, compose from the real app shell. Inspect the\nactual app components before drawing an existing product: sidebar density,\ntoolbar actions, overflow menus, property panels, and framework chrome should\nmatch the product unless the plan intentionally changes them.\n\n**Keep product screens pure.** A product wireframe shows the app state a user\nwould actually see. Do not embed file contracts, architecture arrows, repo pills,\nmode explanations, or implementation callouts inside the screen just to explain\nthe plan. Put those in canvas annotations, a separate diagram, or the document\nbody. Secondary UI such as properties, history, sync, export, or agent controls\nshould appear where the real product would put them: an overflow popover, sheet,\npanel, or separate framework sidebar state, not a generic permanent right\ninspector unless that inspector is the actual design.\n\n**Classify mockup scope before implementation.** Before turning a plan mockup\ninto source code, decide whether each artboard represents the whole page/app\nshell, a route body inside an existing shell, or a component/sub-surface. If an\nartboard includes navigation, sidebars, auth banners, or a signup/login form,\nmap those pieces to the real shared shell/auth components instead of nesting the\nentire mockup inside the current page. When a mockup references the product's\nstandard signup/login page, find and reuse that existing implementation; do not\napproximate it from the wireframe.\n\n**Zoom in on sub-surfaces, don't redraw the page.** For a small sub-surface (a\npopover, menu, dialog, toast), show the full screen once, then add a small\nseparate artboard whose `html` contains ONLY that sub-surface \u2014 do not re-draw\nthe whole page around it, and do not scale a duplicate up. Pick the matching\n`surface` (e.g. `popover`) so the footprint is right; never widen a popover to\npage width.\n\n**Loading / skeleton states.** Set `data.skeleton: true` on the wireframe and\nfill the `html` with neutral, textless placeholder geometry \u2014 boxes and bars\nbuilt as `<div>`s with `background:var(--wf-line)` and explicit heights/widths,\nno labels or copy. The renderer drops borders, sketch, and color into the\nskeleton register automatically. Never escape to a `custom-html` document block\nto fake a loader.\n\n**Editing an existing mockup.** In hosted mode, to change one element, text, or\ncolor in an existing html mockup, do not regenerate the frame \u2014 call\n`update-visual-plan` with\n`contentPatches: [{ op: \"patch-wireframe-html\", blockId, edits: [{ find,\nreplace }] }]`. Each `find` is a unique snippet of the current html (read it\nfirst with `get-visual-plan`); set `all: true` on an edit to replace every\noccurrence. The result is re-sanitized. In local-files privacy mode, do not call\nhosted Plan tools; edit the local MDX source directly and rerun the local\ncheck/serve or verify command for `<plan-dir>`.\n\n**Choose the outer frame deliberately.** Wireframe and diagram data accept\n`frame: \"auto\" | \"show\" | \"hide\"` in block data (`<Screen frame=\"hide\">` in\nMDX wireframes, `<Diagram frame=\"hide\">` for MDX diagrams). Leave it unset or\n`auto` when the host context should decide: Plan and recap surfaces default to a\ndrawn outer frame; docs surfaces default to no outer frame. Use `show` for\nstandalone product screens, before/after recap comparisons, screenshot-like\nartifacts, and visuals that need containment from surrounding prose. Use `hide`\nwhen a docs page, tab, column, card, canvas artboard, or the visual's own\ninternal chrome already supplies the boundary. Do not use `hide` to compensate\nfor cramped content; fix the layout instead.\n\n**Inner padding and borders still matter.** Always wrap HTML wireframe content\nin a root container with real inner padding before drawing cards, fields, pills,\nlabels, or controls. Use at least 14-16px of padding, `box-sizing: border-box`,\n`height: 100%`, and `gap` between child rows on the root node itself so the\nfirst row never sits flush against the screen edge. Do not rely on padding on a\nnested page section as the first visible inset; the outermost element must\ncreate the breathing room. Keep text away from borders: every container, field,\nbutton, menu item, and annotation needs enough padding and line-height to read\ncleanly in the rendered Plan view.\n\n**For feature-cloud or abundance visuals, optimize the composition over line-by-line\nreading.** Some marketing/product sections need to feel like a large surface area\nof capability rather than a precise app workflow. In those cases, use one padded\nroot with a short headline and a dense, aesthetic cloud of short feature labels,\nchips, rings, or columns. Vary scale and opacity with tokens, cluster by meaning,\nand let many labels be glanceable rather than individually essential. Do not\nforce dozens of features into equal cards with long wrapped sentences; that\nusually creates a messy unreadable mockup.\n\n**Lay out children safely so they never collide.** Use HTML flex/grid with\n`gap`, `min-width: 0`, and sensible overflow. Avoid negative margins, absolute\npositioning, or fixed child widths that can collide when the renderer switches\nbetween light/dark, sketch/clean, or different zoom levels.\n\n**Do not wrap intentionally single-line labels.** For toolbars, tab rails,\nbreadcrumbs, chip/filter rows, branch and file names, file chips, and code\nfilenames \u2014 any deliberately single-line row \u2014 do not let long text wrap. Put\n`white-space: nowrap` on the row (and `overflow: hidden; text-overflow: ellipsis`\non the individual labels that can grow), so the wireframe demonstrates the actual\nlayout behavior instead of producing ugly stacked or vertical text. Use\nhorizontally scrollable or clipped rails for overflow.\n\n**Fill the frame; keep labels short.** Each artboard is a fixed-size surface \u2014 compose enough realistic HTML to fill it top to bottom with even vertical rhythm; never leave a large empty band. On desktop/app-shell sidebars, let the nav stack flex to fill (`flex:1`) and add any persistent bottom action/status after it so the rail reads complete in taller frames. On mobile especially, flow real rows down the whole screen (status bar, header, then list/detail content) rather than a header floating above a gap. Keep every label short enough to sit on one line within its column \u2014 shorten the copy rather than relying on the frame to absorb it (long labels wrap or clip).\n\n**Persistent chrome bars span the full frame width.** Top bars, app headers,\ntoolbars, and bottom tab/nav bars are full-width chrome, not centered content.\nLay each one out as a single flex row that fills the frame\n(`style=\"display:flex;align-items:center;width:100%\"`) and push trailing actions\nto the right edge with a flex spacer (`<div style=\"flex:1\"></div>`) between the\nleading group and the trailing group \u2014 never center a bar inside a narrow,\ncentered block, and never let it collapse to the width of its contents. In a\nBefore/After pair the bar stays full-width in BOTH states even when one state has\nfewer controls; the spacer absorbs the difference so the remaining controls hold\ntheir edge alignment instead of sliding to the center.\n\n**Pin bottom bars to the bottom of the frame.** For mobile tab bars, footers, and\nany persistent bottom action row, make the frame itself a flex column at\n`height:100%` (`style=\"display:flex;flex-direction:column;height:100%\"`), give the\nscrolling body `flex:1` so it absorbs the slack, and place the bar as the LAST\nchild of the frame (or set `margin-top:auto` on it). The bar then sits flush at\nthe bottom of the surface instead of floating directly under the content with an\nempty band beneath it.\n\n**Before / after must be comparable.** When showing a state change, preserve the\nunchanged controls in both states so the reviewer can see exactly what moved or\nappeared; do not show an added control as a generic box floating elsewhere in\nthe surface. Place the new/changed affordance where the implementation puts it \u2014\nfor example, a new `Edit with AI` action in a popover header belongs in the\ntop-right header slot, aligned with the title, not in the body or footer. Use\nthe same frame size, scale, outer padding, border radius, and visual density on\nboth sides unless the change itself alters those properties, and let the frame\nheight fit the content rather than leaving a tall empty lower half.\n\n**Name the states with the column header, never inside the frame.** For\ndocument-body wireframes (recaps), put the two\nstates in a `columns` block and set each column's `label` to `Before` and\n`After` \u2014 the renderer draws that label as an `h4` heading above each frame. Do\nNOT bake a `Before`/`After` pill, title, or heading into the wireframe `html`: a\nlabel placed inside reads as part of the product UI, lands in a random corner,\nand clutters the comparison. The column header is the one and only place the\nstate name belongs. On a canvas, place the two state artboards as neighbors with\nframe labels \u2014 never encode Before/After inside the html.\n\n**Let the surface choose side-by-side vs. stacked.** For document-body\nwireframes (recaps), the `columns` renderer lays\nnarrow surfaces (`mobile`, `popover`, `panel`) out side by side, and\nautomatically stacks wide surfaces (`desktop`, `browser`) vertically at full\ndocument width so a large frame is never crushed into a half-width column and\ncropped. Author both wireframes with the real `surface` and the matching\n`Before`/`After` column labels; do not hand-stack the pair into separate\ntop-level wireframes or duplicate the state name as body content.\n\n**Good example \u2014 a contacts list, surface `browser`.** A small, real screen\ncomposed from the helper classes and tokens, layout in inline flex, no fonts or\nhex colors:\n\n```html\n<div\n style=\"display:flex;flex-direction:column;gap:12px;padding:16px;height:100%\"\n>\n <div style=\"display:flex;align-items:center;justify-content:space-between\">\n <h1>Contacts</h1>\n <button class=\"primary\">New contact</button>\n </div>\n <div style=\"display:flex;gap:6px\">\n <span class=\"wf-pill accent\">All 128</span>\n <span class=\"wf-pill\">Favorites</span>\n <span class=\"wf-pill\">Archived</span>\n </div>\n <div\n class=\"wf-card\"\n style=\"display:flex;flex-direction:column;gap:0;padding:0\"\n >\n <div\n style=\"display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1.4px solid var(--wf-line)\"\n >\n <div\n style=\"width:32px;height:32px;border-radius:999px;background:var(--wf-accent-soft)\"\n ></div>\n <div style=\"flex:1\">\n <strong>Jane Cooper</strong><br /><small>jane@acme.co</small>\n </div>\n <span class=\"wf-pill\">Lead</span>\n </div>\n <div style=\"display:flex;align-items:center;gap:10px;padding:10px 12px\">\n <div\n style=\"width:32px;height:32px;border-radius:999px;background:var(--wf-accent-soft)\"\n ></div>\n <div style=\"flex:1\">\n <strong>Marcus Lee</strong><br /><small>marcus@globex.io</small>\n </div>\n <span class=\"wf-pill\">Customer</span>\n </div>\n </div>\n</div>\n```\n\n<!-- SHARED-CORE:wireframe-quality END -->\n";
|
|
1
|
+
export declare const WIREFRAME_QUALITY_CORE = "<!-- SHARED-CORE:wireframe-quality START -->\n\n**A wireframe is an HTML mockup. The renderer owns the look; you write the\ncontent.** Set `data.html` to a self-contained, semantic HTML fragment of the\nscreen and set `data.surface`. The renderer owns the surface footprint/aspect,\nthe dark/light theme, the hand-drawn font, and the rough.js sketch overlay \u2014 you\nnever write `<html>`/`<body>`/`<script>`/`<style>` tags or any\nwidth/height/coordinates. You write real HTML layout and real product\ncontent; the renderer styles and roughens it.\n\n**A wireframe block's data is an HTML screen plus a surface:**\n\n```json\n{\n \"surface\": \"browser\",\n \"html\": \"<div style=\\\"display:flex;flex-direction:column;gap:10px;padding:16px;height:100%\\\"><h1>Sign in</h1><p class=\\\"wf-muted\\\">Use your work email to continue.</p><div class=\\\"wf-card\\\" style=\\\"display:flex;flex-direction:column;gap:10px\\\"><label>Email<input value=\\\"jane@acme.co\\\" /></label><label>Password<input value=\\\"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\\\" /></label><label style=\\\"display:flex;align-items:center;gap:8px\\\"><input type=\\\"checkbox\\\" checked /> Remember me</label><button class=\\\"primary\\\">Sign in</button></div><a href=\\\"#\\\">Forgot password?</a></div>\"\n}\n```\n\n**Write PLAIN semantic HTML and let the renderer style it.** Bare elements\n(`h1`/`h2`/`h3`, `p`, `button`, `input`, `<input type=\"checkbox\">`, `a`, `hr`)\nare auto-themed \u2014 no classes needed. Helper classes carry the rest:\n\n- `.wf-card` / `.wf-box` \u2014 a bordered, padded container (a panel, a list item).\n- `.wf-pill` / `.wf-chip` \u2014 a rounded tag or filter; add `.accent`\n (`<span class=\"wf-pill accent\">`) for the accent-filled variant.\n- `.wf-muted` \u2014 secondary/muted text (or use `<small>`).\n- `button.primary` or any element with `[data-primary]` \u2014 the accent-filled\n primary button.\n\n**No decorative shadows around mockups.** Do not put `box-shadow`, `filter:\ndrop-shadow(...)`, Tailwind `shadow-*` classes, or other fake depth effects on a\nwireframe frame, root container, `.wf-card` / `.wf-box`, or canvas artboard.\nMockups should read as flat, bordered surfaces; use spacing, borders, labels,\nand annotations for separation. Only show a shadow when the real product UI\nalready has that shadow and it is essential to the change being reviewed.\n\n**Use renderer icons, not visible icon words.** For icon-only buttons or leading\nicons inside fields, chips, menu items, and toolbars, write an empty marker such\nas `<span data-icon=\"mail\" aria-label=\"Email\"></span>` or\n`<i data-icon=\"lock\"></i>`. The renderer replaces it with a Tabler-style SVG and\nthe `.wf-icon` class sizes it to the surrounding text. Supported names and\naliases: `mail`/`email`, `lock`/`password`, `search`, `plus`/`add`, `x`/`close`,\n`check`, `chevronDown`, `chevronUp`, `chevronLeft`, `chevronRight`, `dots`/`more`,\n`chevron`/`caret`/`dropdown` (down chevron), `user`, `settings`, `calendar`,\n`bell`, `send`, `edit`, `arrowLeft`, and `arrowRight`. Do not put visible words\nlike \"email\", \"lock\", \"search\", \"chevron\", or \"more\" where the product UI would\nshow an icon; use text only when it is a real label a user would read.\n\n**Use the `--wf-*` tokens for any custom color, never hex.** The renderer flips\nthese on light/dark, so reading them is what keeps a mockup correct in both\nthemes. For any inline border, background, or text color, reference a token:\n`style=\"border:1.4px solid var(--wf-line)\"`. The tokens are `--wf-ink` (text),\n`--wf-muted` (secondary text), `--wf-line` (borders/dividers), `--wf-paper`\n(page background), `--wf-card` (container surface), `--wf-accent` /\n`--wf-accent-fg` / `--wf-accent-soft` (brand action), `--wf-warn`, `--wf-ok`,\nand `--wf-radius`. Never hard-code a hex color and never set `font-family` \u2014 the\nrenderer owns the sketch/clean font.\n\n**Never use host/Tailwind theme classes in wireframe HTML.** Classes such as\n`bg-white`, `bg-zinc-50`, `bg-slate-950`, `text-zinc-950`,\n`text-slate-400`, `border-zinc-200`, `hover:bg-slate-800`, `shadow-xl`,\nor arbitrary color utilities like `bg-[#fff]` leak the host app's CSS into the\nmockup and can make dark-mode canvas frames unreadable. Use bare semantic\nelements, `.wf-*` helper classes, and `--wf-*` color tokens instead. Before\npublishing, scan every wireframe `class` and `style` attribute: if a class sets\nbackground, text, border, ring, fill, stroke, gradient, placeholder, decoration,\nor shadow color, rewrite it to renderer tokens or remove it. Layout-only classes\nare still discouraged; inline flex/grid styles are safer and easier to review.\n\n**Keep Rough.js sparse.** The renderer sketches the outer frame, standard\n`.wf-*` primitives, controls, and inline border dividers by default. Do not add\n`data-rough` to broad root wrappers, dialog shells, page panels, grid cells, or\nnested containers unless that single container is the visual point. Use\n`data-rough` only for a deliberate one-off shape. If a mockup starts looking\nlike stacked/overlapping sketch lines, remove rough targets from parent\ncontainers and let backgrounds plus spacing separate the surfaces.\n\n**Use literal CSS lengths for spacing.** The `--wf-*` tokens are for colors and\nrenderer-owned visual styling, not layout spacing. Do not use guessed spacing\ntokens such as `var(--wf-space-4)`, Tailwind spacing classes, or theme spacing\nvariables inside wireframe HTML; if a token is unavailable in the Plan renderer,\npadding collapses and content hugs the border. Use explicit CSS lengths for\nlayout: `padding:16px`, `gap:12px`, `margin-top:18px`, `minmax(0,1fr)`.\n\n**Lay out with inline `style` flex/grid.** You write the real layout \u2014\n`display:flex; flex-direction:column; gap:10px; padding:16px` and so on \u2014 and the\nrenderer never repositions anything. Compose the actual product: reproduce the\ncurrent screen, then show the modification. Real labels, real counts, real dates,\nreal button text grounded in the screen you read; not lorem or gray bars.\n\n**Surface presets \u2014 match the real footprint, never default to desktop+mobile.**\nPick the `surface` that matches what the user will actually see:\n\n- `browser`: a web page that needs a browser chrome frame around it.\n- `desktop`: a full desktop app page or app shell.\n- `mobile`: a phone screen, only when the work is genuinely mobile.\n- `popover`: a small floating menu, dropdown, or inline popover.\n- `panel`: a side panel, inspector, or sidebar widget.\n\nA sidebar popover renders as a small surface, not a desktop page and a phone\nframe. Do not emit `desktop` + `mobile` variants unless responsive behavior\nactually changes the layout. For a component or widget, show one broader\napp-context frame only when placement affects understanding, then the focused\ncomponent states.\n\n**Model the actual component shell for small surfaces.** A rendered UI change\nbelongs in a wireframe; reserve `diagram` for architecture, dependency, state,\nor data-flow relationships. Popovers, dropdown menus, command palettes, and\ncontext menus use `surface: \"popover\"` unless the surrounding page placement is\nthe point of the change. Dialogs, sheets, inspectors, sidebars, and long\nproperty panels use the matching `panel` / `desktop` surface as appropriate.\nShow the real chrome: trigger or anchor when it matters, title/header row,\ntop-right actions, separators, fields, options, selected states, body content,\nand footer actions that are visible in the workflow.\n\n**Modify, don't redesign.** When the task changes an existing screen, reproduce\nthe current screen's real layout and footprint FIRST, then change only the delta\nand call it out with a single annotation. Do not restack the page into a new\nlayout. For net-new surfaces, compose from the real app shell. Inspect the\nactual app components before drawing an existing product: sidebar density,\ntoolbar actions, overflow menus, property panels, and framework chrome should\nmatch the product unless the plan intentionally changes them.\n\n**Keep product screens pure.** A product wireframe shows the app state a user\nwould actually see. Do not embed file contracts, architecture arrows, repo pills,\nmode explanations, or implementation callouts inside the screen just to explain\nthe plan. Put those in canvas annotations, a separate diagram, or the document\nbody. Secondary UI such as properties, history, sync, export, or agent controls\nshould appear where the real product would put them: an overflow popover, sheet,\npanel, or separate framework sidebar state, not a generic permanent right\ninspector unless that inspector is the actual design.\n\n**Classify mockup scope before implementation.** Before turning a plan mockup\ninto source code, decide whether each artboard represents the whole page/app\nshell, a route body inside an existing shell, or a component/sub-surface. If an\nartboard includes navigation, sidebars, auth banners, or a signup/login form,\nmap those pieces to the real shared shell/auth components instead of nesting the\nentire mockup inside the current page. When a mockup references the product's\nstandard signup/login page, find and reuse that existing implementation; do not\napproximate it from the wireframe.\n\n**Zoom in on sub-surfaces, don't redraw the page.** For a small sub-surface (a\npopover, menu, dialog, toast), show the full screen once, then add a small\nseparate artboard whose `html` contains ONLY that sub-surface \u2014 do not re-draw\nthe whole page around it, and do not scale a duplicate up. Pick the matching\n`surface` (e.g. `popover`) so the footprint is right; never widen a popover to\npage width.\n\n**Loading / skeleton states.** Set `data.skeleton: true` on the wireframe and\nfill the `html` with neutral, textless placeholder geometry \u2014 boxes and bars\nbuilt as `<div>`s with `background:var(--wf-line)` and explicit heights/widths,\nno labels or copy. The renderer drops borders, sketch, and color into the\nskeleton register automatically. Never escape to a `custom-html` document block\nto fake a loader.\n\n**Editing an existing mockup.** In hosted mode, to change one element, text, or\ncolor in an existing html mockup, do not regenerate the frame \u2014 call\n`update-visual-plan` with\n`contentPatches: [{ op: \"patch-wireframe-html\", blockId, edits: [{ find,\nreplace }] }]`. Each `find` is a unique snippet of the current html (read it\nfirst with `get-visual-plan`); set `all: true` on an edit to replace every\noccurrence. The result is re-sanitized. In local-files privacy mode, do not call\nhosted Plan tools; edit the local MDX source directly and rerun the local\ncheck/serve or verify command for `<plan-dir>`.\n\n**Choose the outer frame deliberately.** Wireframe and diagram data accept\n`frame: \"auto\" | \"show\" | \"hide\"` in block data (`<Screen frame=\"hide\">` in\nMDX wireframes, `<Diagram frame=\"hide\">` for MDX diagrams). Leave it unset or\n`auto` when the host context should decide: Plan and recap surfaces default to a\ndrawn outer frame; docs surfaces default to no outer frame. Use `show` for\nstandalone product screens, before/after recap comparisons, screenshot-like\nartifacts, and visuals that need containment from surrounding prose. Use `hide`\nwhen a docs page, tab, column, card, canvas artboard, or the visual's own\ninternal chrome already supplies the boundary. Do not use `hide` to compensate\nfor cramped content; fix the layout instead.\n\n**Inner padding and borders still matter.** Always wrap HTML wireframe content\nin a root container with real inner padding before drawing cards, fields, pills,\nlabels, or controls. Use at least 14-16px of padding, `box-sizing: border-box`,\n`height: 100%`, and `gap` between child rows on the root node itself so the\nfirst row never sits flush against the screen edge. Do not rely on padding on a\nnested page section as the first visible inset; the outermost element must\ncreate the breathing room. Keep text away from borders: every container, field,\nbutton, menu item, and annotation needs enough padding and line-height to read\ncleanly in the rendered Plan view.\n\n**Center page-like content inside broad surfaces.** For browser or desktop\nscreens, let the outer root provide the full-width frame and padding, then keep\nthe page body in a centered wrapper such as\n`width:100%;max-width:720px;margin-inline:auto`. Full-width app bars may span\nthe root, but onboarding, auth, settings, and other page-like content should\nnot hug the left border unless the real product does.\n\n**For feature-cloud or abundance visuals, optimize the composition over line-by-line\nreading.** Some marketing/product sections need to feel like a large surface area\nof capability rather than a precise app workflow. In those cases, use one padded\nroot with a short headline and a dense, aesthetic cloud of short feature labels,\nchips, rings, or columns. Vary scale and opacity with tokens, cluster by meaning,\nand let many labels be glanceable rather than individually essential. Do not\nforce dozens of features into equal cards with long wrapped sentences; that\nusually creates a messy unreadable mockup.\n\n**Lay out children safely so they never collide.** Use HTML flex/grid with\n`gap`, `min-width: 0`, and sensible overflow. Avoid negative margins, absolute\npositioning, or fixed child widths that can collide when the renderer switches\nbetween light/dark, sketch/clean, or different zoom levels.\n\n**Do not wrap intentionally single-line labels.** For toolbars, tab rails,\nbreadcrumbs, chip/filter rows, branch and file names, file chips, and code\nfilenames \u2014 any deliberately single-line row \u2014 do not let long text wrap. Put\n`white-space: nowrap` on the row (and `overflow: hidden; text-overflow: ellipsis`\non the individual labels that can grow), so the wireframe demonstrates the actual\nlayout behavior instead of producing ugly stacked or vertical text. Use\nhorizontally scrollable or clipped rails for overflow.\n\n**Fill the frame; keep labels short.** Each artboard is a fixed-size surface \u2014 compose enough realistic HTML to fill it top to bottom with even vertical rhythm; never leave a large empty band. On desktop/app-shell sidebars, let the nav stack flex to fill (`flex:1`) and add any persistent bottom action/status after it so the rail reads complete in taller frames. On mobile especially, flow real rows down the whole screen (status bar, header, then list/detail content) rather than a header floating above a gap. Keep every label short enough to sit on one line within its column \u2014 shorten the copy rather than relying on the frame to absorb it (long labels wrap or clip).\n\n**Persistent chrome bars span the full frame width.** Top bars, app headers,\ntoolbars, and bottom tab/nav bars are full-width chrome, not centered content.\nLay each one out as a single flex row that fills the frame\n(`style=\"display:flex;align-items:center;width:100%\"`) and push trailing actions\nto the right edge with a flex spacer (`<div style=\"flex:1\"></div>`) between the\nleading group and the trailing group \u2014 never center a bar inside a narrow,\ncentered block, and never let it collapse to the width of its contents. In a\nBefore/After pair the bar stays full-width in BOTH states even when one state has\nfewer controls; the spacer absorbs the difference so the remaining controls hold\ntheir edge alignment instead of sliding to the center.\n\n**Pin bottom bars to the bottom of the frame.** For mobile tab bars, footers, and\nany persistent bottom action row, make the frame itself a flex column at\n`height:100%` (`style=\"display:flex;flex-direction:column;height:100%\"`), give the\nscrolling body `flex:1` so it absorbs the slack, and place the bar as the LAST\nchild of the frame (or set `margin-top:auto` on it). The bar then sits flush at\nthe bottom of the surface instead of floating directly under the content with an\nempty band beneath it.\n\n**Before / after must be comparable.** When showing a state change, preserve the\nunchanged controls in both states so the reviewer can see exactly what moved or\nappeared; do not show an added control as a generic box floating elsewhere in\nthe surface. Place the new/changed affordance where the implementation puts it \u2014\nfor example, a new `Edit with AI` action in a popover header belongs in the\ntop-right header slot, aligned with the title, not in the body or footer. Use\nthe same frame size, scale, outer padding, border radius, and visual density on\nboth sides unless the change itself alters those properties, and let the frame\nheight fit the content rather than leaving a tall empty lower half.\n\n**Name the states with the column header, never inside the frame.** For\ndocument-body wireframes (recaps), put the two\nstates in a `columns` block and set each column's `label` to `Before` and\n`After` \u2014 the renderer draws that label as an `h4` heading above each frame. Do\nNOT bake a `Before`/`After` pill, title, or heading into the wireframe `html`: a\nlabel placed inside reads as part of the product UI, lands in a random corner,\nand clutters the comparison. The column header is the one and only place the\nstate name belongs. On a canvas, place the two state artboards as neighbors with\nframe labels \u2014 never encode Before/After inside the html.\n\n**Let the surface choose side-by-side vs. stacked.** For document-body\nwireframes (recaps), the `columns` renderer lays\nnarrow surfaces (`mobile`, `popover`, `panel`) out side by side, and\nautomatically stacks wide surfaces (`desktop`, `browser`) vertically at full\ndocument width so a large frame is never crushed into a half-width column and\ncropped. Author both wireframes with the real `surface` and the matching\n`Before`/`After` column labels; do not hand-stack the pair into separate\ntop-level wireframes or duplicate the state name as body content.\n\n**Good example \u2014 a contacts list, surface `browser`.** A small, real screen\ncomposed from the helper classes and tokens, layout in inline flex, no fonts or\nhex colors:\n\n```html\n<div\n style=\"display:flex;flex-direction:column;gap:12px;padding:16px;height:100%\"\n>\n <div style=\"display:flex;align-items:center;justify-content:space-between\">\n <h1>Contacts</h1>\n <button class=\"primary\">New contact</button>\n </div>\n <div style=\"display:flex;gap:6px\">\n <span class=\"wf-pill accent\">All 128</span>\n <span class=\"wf-pill\">Favorites</span>\n <span class=\"wf-pill\">Archived</span>\n </div>\n <div\n class=\"wf-card\"\n style=\"display:flex;flex-direction:column;gap:0;padding:0\"\n >\n <div\n style=\"display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1.4px solid var(--wf-line)\"\n >\n <div\n style=\"width:32px;height:32px;border-radius:999px;background:var(--wf-accent-soft)\"\n ></div>\n <div style=\"flex:1\">\n <strong>Jane Cooper</strong><br /><small>jane@acme.co</small>\n </div>\n <span class=\"wf-pill\">Lead</span>\n </div>\n <div style=\"display:flex;align-items:center;gap:10px;padding:10px 12px\">\n <div\n style=\"width:32px;height:32px;border-radius:999px;background:var(--wf-accent-soft)\"\n ></div>\n <div style=\"flex:1\">\n <strong>Marcus Lee</strong><br /><small>marcus@globex.io</small>\n </div>\n <span class=\"wf-pill\">Customer</span>\n </div>\n </div>\n</div>\n```\n\n<!-- SHARED-CORE:wireframe-quality END -->";
|
|
2
|
+
export declare const WIREFRAME_REFERENCE_MD = "# HTML wireframe quality \u2014 single source of truth\n\nThis file is the canonical quality bar for HTML wireframes / `<Screen>` /\n`WireframeBlock` content, shared word for word by `/visual-plan` and\n`/visual-recap`. Read it in full before authoring ANY wireframe; do not\nauthor wireframes from memory or paraphrase these rules per command.\n\n<!-- SHARED-CORE:wireframe-quality START -->\n\n**A wireframe is an HTML mockup. The renderer owns the look; you write the\ncontent.** Set `data.html` to a self-contained, semantic HTML fragment of the\nscreen and set `data.surface`. The renderer owns the surface footprint/aspect,\nthe dark/light theme, the hand-drawn font, and the rough.js sketch overlay \u2014 you\nnever write `<html>`/`<body>`/`<script>`/`<style>` tags or any\nwidth/height/coordinates. You write real HTML layout and real product\ncontent; the renderer styles and roughens it.\n\n**A wireframe block's data is an HTML screen plus a surface:**\n\n```json\n{\n \"surface\": \"browser\",\n \"html\": \"<div style=\\\"display:flex;flex-direction:column;gap:10px;padding:16px;height:100%\\\"><h1>Sign in</h1><p class=\\\"wf-muted\\\">Use your work email to continue.</p><div class=\\\"wf-card\\\" style=\\\"display:flex;flex-direction:column;gap:10px\\\"><label>Email<input value=\\\"jane@acme.co\\\" /></label><label>Password<input value=\\\"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\\\" /></label><label style=\\\"display:flex;align-items:center;gap:8px\\\"><input type=\\\"checkbox\\\" checked /> Remember me</label><button class=\\\"primary\\\">Sign in</button></div><a href=\\\"#\\\">Forgot password?</a></div>\"\n}\n```\n\n**Write PLAIN semantic HTML and let the renderer style it.** Bare elements\n(`h1`/`h2`/`h3`, `p`, `button`, `input`, `<input type=\"checkbox\">`, `a`, `hr`)\nare auto-themed \u2014 no classes needed. Helper classes carry the rest:\n\n- `.wf-card` / `.wf-box` \u2014 a bordered, padded container (a panel, a list item).\n- `.wf-pill` / `.wf-chip` \u2014 a rounded tag or filter; add `.accent`\n (`<span class=\"wf-pill accent\">`) for the accent-filled variant.\n- `.wf-muted` \u2014 secondary/muted text (or use `<small>`).\n- `button.primary` or any element with `[data-primary]` \u2014 the accent-filled\n primary button.\n\n**No decorative shadows around mockups.** Do not put `box-shadow`, `filter:\ndrop-shadow(...)`, Tailwind `shadow-*` classes, or other fake depth effects on a\nwireframe frame, root container, `.wf-card` / `.wf-box`, or canvas artboard.\nMockups should read as flat, bordered surfaces; use spacing, borders, labels,\nand annotations for separation. Only show a shadow when the real product UI\nalready has that shadow and it is essential to the change being reviewed.\n\n**Use renderer icons, not visible icon words.** For icon-only buttons or leading\nicons inside fields, chips, menu items, and toolbars, write an empty marker such\nas `<span data-icon=\"mail\" aria-label=\"Email\"></span>` or\n`<i data-icon=\"lock\"></i>`. The renderer replaces it with a Tabler-style SVG and\nthe `.wf-icon` class sizes it to the surrounding text. Supported names and\naliases: `mail`/`email`, `lock`/`password`, `search`, `plus`/`add`, `x`/`close`,\n`check`, `chevronDown`, `chevronUp`, `chevronLeft`, `chevronRight`, `dots`/`more`,\n`chevron`/`caret`/`dropdown` (down chevron), `user`, `settings`, `calendar`,\n`bell`, `send`, `edit`, `arrowLeft`, and `arrowRight`. Do not put visible words\nlike \"email\", \"lock\", \"search\", \"chevron\", or \"more\" where the product UI would\nshow an icon; use text only when it is a real label a user would read.\n\n**Use the `--wf-*` tokens for any custom color, never hex.** The renderer flips\nthese on light/dark, so reading them is what keeps a mockup correct in both\nthemes. For any inline border, background, or text color, reference a token:\n`style=\"border:1.4px solid var(--wf-line)\"`. The tokens are `--wf-ink` (text),\n`--wf-muted` (secondary text), `--wf-line` (borders/dividers), `--wf-paper`\n(page background), `--wf-card` (container surface), `--wf-accent` /\n`--wf-accent-fg` / `--wf-accent-soft` (brand action), `--wf-warn`, `--wf-ok`,\nand `--wf-radius`. Never hard-code a hex color and never set `font-family` \u2014 the\nrenderer owns the sketch/clean font.\n\n**Never use host/Tailwind theme classes in wireframe HTML.** Classes such as\n`bg-white`, `bg-zinc-50`, `bg-slate-950`, `text-zinc-950`,\n`text-slate-400`, `border-zinc-200`, `hover:bg-slate-800`, `shadow-xl`,\nor arbitrary color utilities like `bg-[#fff]` leak the host app's CSS into the\nmockup and can make dark-mode canvas frames unreadable. Use bare semantic\nelements, `.wf-*` helper classes, and `--wf-*` color tokens instead. Before\npublishing, scan every wireframe `class` and `style` attribute: if a class sets\nbackground, text, border, ring, fill, stroke, gradient, placeholder, decoration,\nor shadow color, rewrite it to renderer tokens or remove it. Layout-only classes\nare still discouraged; inline flex/grid styles are safer and easier to review.\n\n**Keep Rough.js sparse.** The renderer sketches the outer frame, standard\n`.wf-*` primitives, controls, and inline border dividers by default. Do not add\n`data-rough` to broad root wrappers, dialog shells, page panels, grid cells, or\nnested containers unless that single container is the visual point. Use\n`data-rough` only for a deliberate one-off shape. If a mockup starts looking\nlike stacked/overlapping sketch lines, remove rough targets from parent\ncontainers and let backgrounds plus spacing separate the surfaces.\n\n**Use literal CSS lengths for spacing.** The `--wf-*` tokens are for colors and\nrenderer-owned visual styling, not layout spacing. Do not use guessed spacing\ntokens such as `var(--wf-space-4)`, Tailwind spacing classes, or theme spacing\nvariables inside wireframe HTML; if a token is unavailable in the Plan renderer,\npadding collapses and content hugs the border. Use explicit CSS lengths for\nlayout: `padding:16px`, `gap:12px`, `margin-top:18px`, `minmax(0,1fr)`.\n\n**Lay out with inline `style` flex/grid.** You write the real layout \u2014\n`display:flex; flex-direction:column; gap:10px; padding:16px` and so on \u2014 and the\nrenderer never repositions anything. Compose the actual product: reproduce the\ncurrent screen, then show the modification. Real labels, real counts, real dates,\nreal button text grounded in the screen you read; not lorem or gray bars.\n\n**Surface presets \u2014 match the real footprint, never default to desktop+mobile.**\nPick the `surface` that matches what the user will actually see:\n\n- `browser`: a web page that needs a browser chrome frame around it.\n- `desktop`: a full desktop app page or app shell.\n- `mobile`: a phone screen, only when the work is genuinely mobile.\n- `popover`: a small floating menu, dropdown, or inline popover.\n- `panel`: a side panel, inspector, or sidebar widget.\n\nA sidebar popover renders as a small surface, not a desktop page and a phone\nframe. Do not emit `desktop` + `mobile` variants unless responsive behavior\nactually changes the layout. For a component or widget, show one broader\napp-context frame only when placement affects understanding, then the focused\ncomponent states.\n\n**Model the actual component shell for small surfaces.** A rendered UI change\nbelongs in a wireframe; reserve `diagram` for architecture, dependency, state,\nor data-flow relationships. Popovers, dropdown menus, command palettes, and\ncontext menus use `surface: \"popover\"` unless the surrounding page placement is\nthe point of the change. Dialogs, sheets, inspectors, sidebars, and long\nproperty panels use the matching `panel` / `desktop` surface as appropriate.\nShow the real chrome: trigger or anchor when it matters, title/header row,\ntop-right actions, separators, fields, options, selected states, body content,\nand footer actions that are visible in the workflow.\n\n**Modify, don't redesign.** When the task changes an existing screen, reproduce\nthe current screen's real layout and footprint FIRST, then change only the delta\nand call it out with a single annotation. Do not restack the page into a new\nlayout. For net-new surfaces, compose from the real app shell. Inspect the\nactual app components before drawing an existing product: sidebar density,\ntoolbar actions, overflow menus, property panels, and framework chrome should\nmatch the product unless the plan intentionally changes them.\n\n**Keep product screens pure.** A product wireframe shows the app state a user\nwould actually see. Do not embed file contracts, architecture arrows, repo pills,\nmode explanations, or implementation callouts inside the screen just to explain\nthe plan. Put those in canvas annotations, a separate diagram, or the document\nbody. Secondary UI such as properties, history, sync, export, or agent controls\nshould appear where the real product would put them: an overflow popover, sheet,\npanel, or separate framework sidebar state, not a generic permanent right\ninspector unless that inspector is the actual design.\n\n**Classify mockup scope before implementation.** Before turning a plan mockup\ninto source code, decide whether each artboard represents the whole page/app\nshell, a route body inside an existing shell, or a component/sub-surface. If an\nartboard includes navigation, sidebars, auth banners, or a signup/login form,\nmap those pieces to the real shared shell/auth components instead of nesting the\nentire mockup inside the current page. When a mockup references the product's\nstandard signup/login page, find and reuse that existing implementation; do not\napproximate it from the wireframe.\n\n**Zoom in on sub-surfaces, don't redraw the page.** For a small sub-surface (a\npopover, menu, dialog, toast), show the full screen once, then add a small\nseparate artboard whose `html` contains ONLY that sub-surface \u2014 do not re-draw\nthe whole page around it, and do not scale a duplicate up. Pick the matching\n`surface` (e.g. `popover`) so the footprint is right; never widen a popover to\npage width.\n\n**Loading / skeleton states.** Set `data.skeleton: true` on the wireframe and\nfill the `html` with neutral, textless placeholder geometry \u2014 boxes and bars\nbuilt as `<div>`s with `background:var(--wf-line)` and explicit heights/widths,\nno labels or copy. The renderer drops borders, sketch, and color into the\nskeleton register automatically. Never escape to a `custom-html` document block\nto fake a loader.\n\n**Editing an existing mockup.** In hosted mode, to change one element, text, or\ncolor in an existing html mockup, do not regenerate the frame \u2014 call\n`update-visual-plan` with\n`contentPatches: [{ op: \"patch-wireframe-html\", blockId, edits: [{ find,\nreplace }] }]`. Each `find` is a unique snippet of the current html (read it\nfirst with `get-visual-plan`); set `all: true` on an edit to replace every\noccurrence. The result is re-sanitized. In local-files privacy mode, do not call\nhosted Plan tools; edit the local MDX source directly and rerun the local\ncheck/serve or verify command for `<plan-dir>`.\n\n**Choose the outer frame deliberately.** Wireframe and diagram data accept\n`frame: \"auto\" | \"show\" | \"hide\"` in block data (`<Screen frame=\"hide\">` in\nMDX wireframes, `<Diagram frame=\"hide\">` for MDX diagrams). Leave it unset or\n`auto` when the host context should decide: Plan and recap surfaces default to a\ndrawn outer frame; docs surfaces default to no outer frame. Use `show` for\nstandalone product screens, before/after recap comparisons, screenshot-like\nartifacts, and visuals that need containment from surrounding prose. Use `hide`\nwhen a docs page, tab, column, card, canvas artboard, or the visual's own\ninternal chrome already supplies the boundary. Do not use `hide` to compensate\nfor cramped content; fix the layout instead.\n\n**Inner padding and borders still matter.** Always wrap HTML wireframe content\nin a root container with real inner padding before drawing cards, fields, pills,\nlabels, or controls. Use at least 14-16px of padding, `box-sizing: border-box`,\n`height: 100%`, and `gap` between child rows on the root node itself so the\nfirst row never sits flush against the screen edge. Do not rely on padding on a\nnested page section as the first visible inset; the outermost element must\ncreate the breathing room. Keep text away from borders: every container, field,\nbutton, menu item, and annotation needs enough padding and line-height to read\ncleanly in the rendered Plan view.\n\n**Center page-like content inside broad surfaces.** For browser or desktop\nscreens, let the outer root provide the full-width frame and padding, then keep\nthe page body in a centered wrapper such as\n`width:100%;max-width:720px;margin-inline:auto`. Full-width app bars may span\nthe root, but onboarding, auth, settings, and other page-like content should\nnot hug the left border unless the real product does.\n\n**For feature-cloud or abundance visuals, optimize the composition over line-by-line\nreading.** Some marketing/product sections need to feel like a large surface area\nof capability rather than a precise app workflow. In those cases, use one padded\nroot with a short headline and a dense, aesthetic cloud of short feature labels,\nchips, rings, or columns. Vary scale and opacity with tokens, cluster by meaning,\nand let many labels be glanceable rather than individually essential. Do not\nforce dozens of features into equal cards with long wrapped sentences; that\nusually creates a messy unreadable mockup.\n\n**Lay out children safely so they never collide.** Use HTML flex/grid with\n`gap`, `min-width: 0`, and sensible overflow. Avoid negative margins, absolute\npositioning, or fixed child widths that can collide when the renderer switches\nbetween light/dark, sketch/clean, or different zoom levels.\n\n**Do not wrap intentionally single-line labels.** For toolbars, tab rails,\nbreadcrumbs, chip/filter rows, branch and file names, file chips, and code\nfilenames \u2014 any deliberately single-line row \u2014 do not let long text wrap. Put\n`white-space: nowrap` on the row (and `overflow: hidden; text-overflow: ellipsis`\non the individual labels that can grow), so the wireframe demonstrates the actual\nlayout behavior instead of producing ugly stacked or vertical text. Use\nhorizontally scrollable or clipped rails for overflow.\n\n**Fill the frame; keep labels short.** Each artboard is a fixed-size surface \u2014 compose enough realistic HTML to fill it top to bottom with even vertical rhythm; never leave a large empty band. On desktop/app-shell sidebars, let the nav stack flex to fill (`flex:1`) and add any persistent bottom action/status after it so the rail reads complete in taller frames. On mobile especially, flow real rows down the whole screen (status bar, header, then list/detail content) rather than a header floating above a gap. Keep every label short enough to sit on one line within its column \u2014 shorten the copy rather than relying on the frame to absorb it (long labels wrap or clip).\n\n**Persistent chrome bars span the full frame width.** Top bars, app headers,\ntoolbars, and bottom tab/nav bars are full-width chrome, not centered content.\nLay each one out as a single flex row that fills the frame\n(`style=\"display:flex;align-items:center;width:100%\"`) and push trailing actions\nto the right edge with a flex spacer (`<div style=\"flex:1\"></div>`) between the\nleading group and the trailing group \u2014 never center a bar inside a narrow,\ncentered block, and never let it collapse to the width of its contents. In a\nBefore/After pair the bar stays full-width in BOTH states even when one state has\nfewer controls; the spacer absorbs the difference so the remaining controls hold\ntheir edge alignment instead of sliding to the center.\n\n**Pin bottom bars to the bottom of the frame.** For mobile tab bars, footers, and\nany persistent bottom action row, make the frame itself a flex column at\n`height:100%` (`style=\"display:flex;flex-direction:column;height:100%\"`), give the\nscrolling body `flex:1` so it absorbs the slack, and place the bar as the LAST\nchild of the frame (or set `margin-top:auto` on it). The bar then sits flush at\nthe bottom of the surface instead of floating directly under the content with an\nempty band beneath it.\n\n**Before / after must be comparable.** When showing a state change, preserve the\nunchanged controls in both states so the reviewer can see exactly what moved or\nappeared; do not show an added control as a generic box floating elsewhere in\nthe surface. Place the new/changed affordance where the implementation puts it \u2014\nfor example, a new `Edit with AI` action in a popover header belongs in the\ntop-right header slot, aligned with the title, not in the body or footer. Use\nthe same frame size, scale, outer padding, border radius, and visual density on\nboth sides unless the change itself alters those properties, and let the frame\nheight fit the content rather than leaving a tall empty lower half.\n\n**Name the states with the column header, never inside the frame.** For\ndocument-body wireframes (recaps), put the two\nstates in a `columns` block and set each column's `label` to `Before` and\n`After` \u2014 the renderer draws that label as an `h4` heading above each frame. Do\nNOT bake a `Before`/`After` pill, title, or heading into the wireframe `html`: a\nlabel placed inside reads as part of the product UI, lands in a random corner,\nand clutters the comparison. The column header is the one and only place the\nstate name belongs. On a canvas, place the two state artboards as neighbors with\nframe labels \u2014 never encode Before/After inside the html.\n\n**Let the surface choose side-by-side vs. stacked.** For document-body\nwireframes (recaps), the `columns` renderer lays\nnarrow surfaces (`mobile`, `popover`, `panel`) out side by side, and\nautomatically stacks wide surfaces (`desktop`, `browser`) vertically at full\ndocument width so a large frame is never crushed into a half-width column and\ncropped. Author both wireframes with the real `surface` and the matching\n`Before`/`After` column labels; do not hand-stack the pair into separate\ntop-level wireframes or duplicate the state name as body content.\n\n**Good example \u2014 a contacts list, surface `browser`.** A small, real screen\ncomposed from the helper classes and tokens, layout in inline flex, no fonts or\nhex colors:\n\n```html\n<div\n style=\"display:flex;flex-direction:column;gap:12px;padding:16px;height:100%\"\n>\n <div style=\"display:flex;align-items:center;justify-content:space-between\">\n <h1>Contacts</h1>\n <button class=\"primary\">New contact</button>\n </div>\n <div style=\"display:flex;gap:6px\">\n <span class=\"wf-pill accent\">All 128</span>\n <span class=\"wf-pill\">Favorites</span>\n <span class=\"wf-pill\">Archived</span>\n </div>\n <div\n class=\"wf-card\"\n style=\"display:flex;flex-direction:column;gap:0;padding:0\"\n >\n <div\n style=\"display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1.4px solid var(--wf-line)\"\n >\n <div\n style=\"width:32px;height:32px;border-radius:999px;background:var(--wf-accent-soft)\"\n ></div>\n <div style=\"flex:1\">\n <strong>Jane Cooper</strong><br /><small>jane@acme.co</small>\n </div>\n <span class=\"wf-pill\">Lead</span>\n </div>\n <div style=\"display:flex;align-items:center;gap:10px;padding:10px 12px\">\n <div\n style=\"width:32px;height:32px;border-radius:999px;background:var(--wf-accent-soft)\"\n ></div>\n <div style=\"flex:1\">\n <strong>Marcus Lee</strong><br /><small>marcus@globex.io</small>\n </div>\n <span class=\"wf-pill\">Customer</span>\n </div>\n </div>\n</div>\n```\n\n<!-- SHARED-CORE:wireframe-quality END -->\n";
|
|
3
3
|
export declare const WIREFRAME_REFERENCE_POINTER = "UI recap/plan wireframes must meet a strict quality bar \u2014 full-width chrome,\npinned bottom bars, real product content, before/after comparability, the right\n`surface` preset, `--wf-*` tokens instead of hex, and no `<html>`/`<style>`/font\ntags. Before authoring ANY wireframe / `<Screen>` / `WireframeBlock`, READ\n`references/wireframe.md` in this skill directory \u2014 it is the single source of\ntruth for HTML wireframe quality, shared word for word with `/visual-plan`\nand `/visual-recap`. Do not author wireframes from memory.";
|
|
4
4
|
//# sourceMappingURL=wireframe.d.ts.map
|
|
@@ -199,6 +199,13 @@ create the breathing room. Keep text away from borders: every container, field,
|
|
|
199
199
|
button, menu item, and annotation needs enough padding and line-height to read
|
|
200
200
|
cleanly in the rendered Plan view.
|
|
201
201
|
|
|
202
|
+
**Center page-like content inside broad surfaces.** For browser or desktop
|
|
203
|
+
screens, let the outer root provide the full-width frame and padding, then keep
|
|
204
|
+
the page body in a centered wrapper such as
|
|
205
|
+
\`width:100%;max-width:720px;margin-inline:auto\`. Full-width app bars may span
|
|
206
|
+
the root, but onboarding, auth, settings, and other page-like content should
|
|
207
|
+
not hug the left border unless the real product does.
|
|
208
|
+
|
|
202
209
|
**For feature-cloud or abundance visuals, optimize the composition over line-by-line
|
|
203
210
|
reading.** Some marketing/product sections need to feel like a large surface area
|
|
204
211
|
of capability rather than a precise app workflow. In those cases, use one padded
|
|
@@ -42,7 +42,7 @@ import { Link, useLocation, useNavigate } from "react-router";
|
|
|
42
42
|
import { withBuilderUtmTrackingParams } from "../shared/builder-link-tracking.js";
|
|
43
43
|
import { consumeAgentSidebarUrlOpenOverride, dispatchAgentSidebarStateChange, getInitialAgentSidebarOpen, setAgentSidebarOpenPreference, subscribeAgentSidebarUrlChanges, SIDEBAR_STATE_CHANGE_EVENT, } from "./agent-sidebar-state.js";
|
|
44
44
|
import { trackEvent } from "./analytics.js";
|
|
45
|
-
import { agentNativePath, appPath } from "./api-path.js";
|
|
45
|
+
import { agentNativePath, appPath, isWorkspaceAppPath } from "./api-path.js";
|
|
46
46
|
import { readClientAppState } from "./application-state.js";
|
|
47
47
|
import { assistantUiRecoverableRenderErrorKind } from "./assistant-ui-recovery.js";
|
|
48
48
|
import { shouldParentFrameOwnAgentPanel } from "./builder-frame.js";
|
|
@@ -51,6 +51,7 @@ import { fetchBuilderStatus } from "./client-status-requests.js";
|
|
|
51
51
|
import { RealtimeVoiceModeProvider } from "./composer/index.js";
|
|
52
52
|
import { getFramePostMessageTargetOrigin, isTrustedFrameMessage, } from "./frame.js";
|
|
53
53
|
import { useT } from "./i18n.js";
|
|
54
|
+
import { isFirstRunOnboardingEnabled } from "./onboarding/first-run-enabled.js";
|
|
54
55
|
import { recoverFromStaleChunkError } from "./route-chunk-recovery.js";
|
|
55
56
|
import { AgentNativeRouteWarmup } from "./route-warmup.js";
|
|
56
57
|
import { withBuilderConnectTrackingParams } from "./settings/useBuilderStatus.js";
|
|
@@ -103,6 +104,9 @@ const SettingsPanel = lazy(() => import("./settings/index.js").then((m) => ({
|
|
|
103
104
|
const OnboardingPanel = lazy(() => import("./onboarding/OnboardingPanel.js").then((m) => ({
|
|
104
105
|
default: m.OnboardingPanel,
|
|
105
106
|
})));
|
|
107
|
+
const FirstRunOnboarding = lazy(() => import("./onboarding/FirstRunOnboarding.js").then((m) => ({
|
|
108
|
+
default: m.FirstRunOnboarding,
|
|
109
|
+
})));
|
|
106
110
|
// Lazy-load SetupButton — the header entry-point that re-opens the
|
|
107
111
|
// onboarding panel after the user has dismissed it.
|
|
108
112
|
const SetupButton = lazy(() => import("./onboarding/SetupButton.js").then((m) => ({
|
|
@@ -113,6 +117,8 @@ const SetupButton = lazy(() => import("./onboarding/SetupButton.js").then((m) =>
|
|
|
113
117
|
// GitHub, etc.) is surfaced in better places (the settings panel and the
|
|
114
118
|
// per-feature setup affordances). Keep this off; do not re-enable globally.
|
|
115
119
|
const SHOW_ONBOARDING = false;
|
|
120
|
+
const SHOW_FIRST_RUN_ONBOARDING = isFirstRunOnboardingEnabled();
|
|
121
|
+
const AgentSidebarOnboardingContext = React.createContext(false);
|
|
116
122
|
const CLI_STORAGE_KEY = "agent-native-cli-command";
|
|
117
123
|
const CLI_DEFAULT = "claude";
|
|
118
124
|
const EXEC_MODE_KEY = "agent-native-exec-mode";
|
|
@@ -385,6 +391,7 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
|
|
|
385
391
|
const t = useT();
|
|
386
392
|
const navigate = useNavigate();
|
|
387
393
|
const mounted = useClientOnly();
|
|
394
|
+
const insideAgentSidebar = React.useContext(AgentSidebarOnboardingContext);
|
|
388
395
|
const feedbackEnabled = resolveFeedbackUrl(undefined, mounted ? undefined : null) !== null;
|
|
389
396
|
const keyPrefix = storageKey ? `:${storageKey}` : "";
|
|
390
397
|
const execModeKey = `${EXEC_MODE_KEY}${keyPrefix}`;
|
|
@@ -863,7 +870,7 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
|
|
|
863
870
|
`[data-agent-fullscreen='true'] .agent-plan-mode-callout{` +
|
|
864
871
|
`max-width:${FULLSCREEN_CHAT_COLUMN_MAX_PX}px;` +
|
|
865
872
|
`margin-left:auto;margin-right:auto;width:100%;}`,
|
|
866
|
-
} }), SHOW_ONBOARDING && mounted && (_jsx(Suspense, { fallback: null, children: _jsx(OnboardingPanel, {}) })), _jsx("div", { className: cn("flex flex-col min-h-0", mode === "chat" ? "flex-1" : "shrink-0"), children: mounted && (_jsx(Suspense, { fallback: _jsx(ChatLoadingSkeleton, { renderHeader: showHeader ? renderChatHeader : undefined, centerComposerWhenEmpty: assistantChatProps.centerComposerWhenEmpty, composerSlot: assistantChatProps.composerSlot, composerAreaClassName: assistantChatProps.composerAreaClassName, composerLayoutVariant: assistantChatProps.composerLayoutVariant }), children: _jsx(MultiTabAssistantChatLazy, { ...assistantChatProps, agentChatSurface: effectiveAgentChatSurface, apiUrl: apiUrl, showHeader: false, renderHeader: showHeader ? renderChatHeader : undefined, showTabBar: showTabBar, renderOverlay: showPageNewChatButton && !showHeader
|
|
873
|
+
} }), SHOW_ONBOARDING && mounted && (_jsx(Suspense, { fallback: null, children: _jsx(OnboardingPanel, {}) })), SHOW_FIRST_RUN_ONBOARDING && mounted && !insideAgentSidebar && (_jsx(Suspense, { fallback: null, children: _jsx(FirstRunOnboarding, {}) })), _jsx("div", { className: cn("flex flex-col min-h-0", mode === "chat" ? "flex-1" : "shrink-0"), children: mounted && (_jsx(Suspense, { fallback: _jsx(ChatLoadingSkeleton, { renderHeader: showHeader ? renderChatHeader : undefined, centerComposerWhenEmpty: assistantChatProps.centerComposerWhenEmpty, composerSlot: assistantChatProps.composerSlot, composerAreaClassName: assistantChatProps.composerAreaClassName, composerLayoutVariant: assistantChatProps.composerLayoutVariant }), children: _jsx(MultiTabAssistantChatLazy, { ...assistantChatProps, agentChatSurface: effectiveAgentChatSurface, apiUrl: apiUrl, showHeader: false, renderHeader: showHeader ? renderChatHeader : undefined, showTabBar: showTabBar, renderOverlay: showPageNewChatButton && !showHeader
|
|
867
874
|
? renderPageChatOverlay
|
|
868
875
|
: undefined, contentHidden: mode !== "chat", emptyStateText: emptyStateText, emptyStateAddon: emptyStateAddon, suggestions: suggestions, dynamicSuggestions: dynamicSuggestions, onSwitchToCli: () => switchMode("cli"), execMode: execMode, onExecModeChange: switchExecMode, storageKey: storageKey, restoreActiveThread: restoreActiveThread, scope: scope, showScopeBadge: showScopeBadge, browserTabId: browserTabId, threadUrlSync: threadUrlSync }) })) }), canUseCodeTools
|
|
869
876
|
? mode === "cli" &&
|
|
@@ -1139,7 +1146,12 @@ function URLSync({ browserTabId }) {
|
|
|
1139
1146
|
// Replace rather than push so repeated agent URL updates don't
|
|
1140
1147
|
// clutter the history stack and can't trigger extra remounts from
|
|
1141
1148
|
// router navigation lifecycle.
|
|
1142
|
-
|
|
1149
|
+
if (isWorkspaceAppPath(url)) {
|
|
1150
|
+
window.location.replace(url);
|
|
1151
|
+
}
|
|
1152
|
+
else {
|
|
1153
|
+
window.setTimeout(() => navigate(url, { replace: true }), 0);
|
|
1154
|
+
}
|
|
1143
1155
|
}
|
|
1144
1156
|
catch {
|
|
1145
1157
|
// Malformed command — ignore.
|
|
@@ -1723,11 +1735,14 @@ export function AgentSidebar({ children, emptyStateText = "How can I help you?",
|
|
|
1723
1735
|
: undefined, "data-agent-sidebar-layout": panelLayout, "data-agent-sidebar-position": position, "data-agent-sidebar-state": panelOpen ? "open" : "closed", "data-agent-sidebar-resizing": isResizing ? "true" : undefined, "data-agent-sidebar-chat-handoff": chatViewTransitionHandoff ? "true" : undefined, style: chatViewTransition
|
|
1724
1736
|
? getAgentChatViewTransitionStyle(panelStyle)
|
|
1725
1737
|
: panelStyle, inert: sidebarAnimationEnabled && !panelOpen ? true : undefined, "aria-hidden": sidebarAnimationEnabled && !panelOpen ? true : undefined, children: _jsx("div", { className: "agent-sidebar-panel-inner flex min-h-0 flex-1 flex-col", children: _jsx(AgentPanel, { emptyStateText: emptyStateText, suggestions: suggestions, dynamicSuggestions: dynamicSuggestions, composerToolbarSlot: composerToolbarSlot, composerSlot: composerSlot, onComposerTextChange: onComposerTextChange, imageModelMenu: imageModelMenu, threadFooterSlot: threadFooterSlot, missingApiKeySetupLayout: "sidebar", onCollapse: () => setOpenPersisted(false), isFullscreen: effectiveFullscreen, onToggleFullscreen: isMobile ? undefined : (onFullscreenRequest ?? toggleFullscreen), storageKey: storageKey, scope: scope, showScopeBadge: showScopeBadge, browserTabId: browserTabId, threadUrlSync: threadUrlSync, agentPageHref: agentPageHref, allowSettingsMode: false, chatOnly: true }) }) }), showResizeHandle && isLeft && (_jsx(ResizeHandle, { position: position, onDrag: handleDrag, onResizeStart: handleResizeStart, onResizeEnd: handleResizeEnd }))] })) : null;
|
|
1726
|
-
return (_jsx(RealtimeVoiceModeProvider, { browserTabId: browserTabId, children: _jsxs("div", { className: "agent-sidebar-shell flex min-w-0 flex-1 h-screen overflow-hidden", "data-agent-sidebar-position": position, "data-agent-sidebar-resizing": isResizing ? "true" : undefined, children: [_jsx(AgentNativeRouteWarmup, {}), isMobile &&
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1738
|
+
return (_jsx(AgentSidebarOnboardingContext.Provider, { value: true, children: _jsxs(RealtimeVoiceModeProvider, { browserTabId: browserTabId, children: [SHOW_FIRST_RUN_ONBOARDING && (_jsx(Suspense, { fallback: null, children: _jsx(FirstRunOnboarding, {}) })), _jsxs("div", { className: "agent-sidebar-shell flex min-w-0 flex-1 h-screen overflow-hidden", "data-agent-sidebar-position": position, "data-agent-sidebar-resizing": isResizing ? "true" : undefined, children: [_jsx(AgentNativeRouteWarmup, {}), isMobile &&
|
|
1739
|
+
!presentationMode &&
|
|
1740
|
+
(mobileAnimationEnabled ? shouldRenderPanel : open) && (_jsx("div", { className: cn("agent-sidebar-backdrop fixed inset-0 bg-foreground/40", mobileAnimationEnabled && !panelOpen && "pointer-events-none"), "data-agent-sidebar-animation": mobileAnimationEnabled ? "mobile" : undefined, "data-agent-sidebar-state": panelOpen ? "open" : "closed", style: { zIndex: SIDEBAR_OVERLAY_Z_INDEX - 1 }, onClick: () => setOpenPersisted(false) })), shouldMountPanel ? _jsx(URLSync, { browserTabId: browserTabId }) : null, isLeft && !presentationMode ? sidebar : null, _jsx("div", { className: "agent-sidebar-main-surface flex flex-1 flex-col overflow-auto min-w-0", "data-agent-sidebar-main-position": position, "data-agent-sidebar-main-state": !isMobile &&
|
|
1741
|
+
!effectiveFullscreen &&
|
|
1742
|
+
!presentationMode &&
|
|
1743
|
+
panelOpen
|
|
1744
|
+
? "open"
|
|
1745
|
+
: "closed", "data-agent-sidebar-resizing": isResizing ? "true" : undefined, children: _jsx(ScreenRefreshBoundary, { children: children }) }), !isLeft && !presentationMode ? sidebar : null] })] }) }));
|
|
1731
1746
|
}
|
|
1732
1747
|
/**
|
|
1733
1748
|
* Focus the agent chat composer input.
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
export declare function appBasePath(): string;
|
|
2
|
+
/**
|
|
3
|
+
* Returns true for a same-origin path mounted at a sibling workspace app.
|
|
4
|
+
* React Router treats root paths as local to its basename, so these targets
|
|
5
|
+
* must use the browser location instead of the app-local router.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isWorkspaceAppPath(path: string): boolean;
|
|
2
8
|
export declare function appPath(path: string): string;
|
|
3
9
|
export declare function appApiPath(path: string): string;
|
|
4
10
|
export declare function agentNativePath(path: string): string;
|
package/dist/client/api-path.js
CHANGED
|
@@ -88,6 +88,61 @@ export function appBasePath() {
|
|
|
88
88
|
// app build into another. Prefer the live mount path when they disagree.
|
|
89
89
|
return derived || workspacePathBasePath() || configured;
|
|
90
90
|
}
|
|
91
|
+
function workspaceAppMountPaths() {
|
|
92
|
+
const raw = clientEnv()?.VITE_AGENT_NATIVE_WORKSPACE_APPS_JSON;
|
|
93
|
+
if (typeof raw !== "string" || !raw.trim())
|
|
94
|
+
return null;
|
|
95
|
+
try {
|
|
96
|
+
const parsed = JSON.parse(raw);
|
|
97
|
+
const entries = Array.isArray(parsed)
|
|
98
|
+
? parsed
|
|
99
|
+
: parsed && typeof parsed === "object" && "apps" in parsed
|
|
100
|
+
? parsed.apps
|
|
101
|
+
: null;
|
|
102
|
+
if (!Array.isArray(entries))
|
|
103
|
+
return null;
|
|
104
|
+
const paths = entries
|
|
105
|
+
.map((entry) => {
|
|
106
|
+
if (!entry || typeof entry !== "object")
|
|
107
|
+
return null;
|
|
108
|
+
const record = entry;
|
|
109
|
+
const rawPath = typeof record.path === "string"
|
|
110
|
+
? record.path
|
|
111
|
+
: typeof record.id === "string"
|
|
112
|
+
? `/${record.id}`
|
|
113
|
+
: null;
|
|
114
|
+
return rawPath?.startsWith("/") ? normalizeBasePath(rawPath) : null;
|
|
115
|
+
})
|
|
116
|
+
.filter((path) => Boolean(path));
|
|
117
|
+
return paths.length ? new Set(paths) : null;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// coercion-ok: malformed manifests cannot authorize cross-app navigation
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Returns true for a same-origin path mounted at a sibling workspace app.
|
|
126
|
+
* React Router treats root paths as local to its basename, so these targets
|
|
127
|
+
* must use the browser location instead of the app-local router.
|
|
128
|
+
*/
|
|
129
|
+
export function isWorkspaceAppPath(path) {
|
|
130
|
+
if (typeof window === "undefined" || !path.startsWith("/"))
|
|
131
|
+
return false;
|
|
132
|
+
if (!isWorkspaceRuntime())
|
|
133
|
+
return false;
|
|
134
|
+
const targetPath = path.split(/[?#]/, 1)[0] || "/";
|
|
135
|
+
const basePath = appBasePath();
|
|
136
|
+
if (!basePath)
|
|
137
|
+
return false;
|
|
138
|
+
if (targetPath === basePath || targetPath.startsWith(`${basePath}/`)) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
const mounts = workspaceAppMountPaths();
|
|
142
|
+
if (!mounts)
|
|
143
|
+
return false;
|
|
144
|
+
return [...mounts].some((mount) => targetPath === mount || targetPath.startsWith(`${mount}/`));
|
|
145
|
+
}
|
|
91
146
|
export function appPath(path) {
|
|
92
147
|
if (!path.startsWith("/"))
|
|
93
148
|
return path;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Skeleton } from "@agent-native/toolkit/ui/skeleton";
|
|
3
|
+
import { IconArrowRight, IconBrandGithub, IconCheck, IconExternalLink, IconInfoCircle, IconKey, IconLoader2, } from "@tabler/icons-react";
|
|
4
|
+
import React, { useMemo, useState } from "react";
|
|
5
|
+
import { appPath } from "../api-path.js";
|
|
6
|
+
import { Tooltip, TooltipContent, TooltipTrigger, } from "../components/ui/tooltip.js";
|
|
7
|
+
import { useBuilderConnectFlow } from "../settings/useBuilderStatus.js";
|
|
8
|
+
import { cn } from "../utils.js";
|
|
9
|
+
import { useOnboarding } from "./use-onboarding.js";
|
|
10
|
+
const BUILDER_MORE_SERVICES = [
|
|
11
|
+
"Voice input",
|
|
12
|
+
"Background agents",
|
|
13
|
+
"Image generation",
|
|
14
|
+
"Video generation",
|
|
15
|
+
"Connected agents",
|
|
16
|
+
"Hosting and deployment",
|
|
17
|
+
"Browser automation",
|
|
18
|
+
"Embeddings",
|
|
19
|
+
];
|
|
20
|
+
export function FirstRunOnboarding() {
|
|
21
|
+
const { firstRun, loading, error, profile, completeFirstRun } = useOnboarding();
|
|
22
|
+
const [screen, setScreen] = useState("intro");
|
|
23
|
+
const showReady = () => setScreen("ready");
|
|
24
|
+
const connectFlow = useBuilderConnectFlow({
|
|
25
|
+
enabled: firstRun,
|
|
26
|
+
trackingSource: "first_run_onboarding",
|
|
27
|
+
trackingFlow: "connect_llm",
|
|
28
|
+
onConnected: showReady,
|
|
29
|
+
});
|
|
30
|
+
if (!firstRun)
|
|
31
|
+
return null;
|
|
32
|
+
if (error) {
|
|
33
|
+
return (_jsx(OnboardingShell, { profile: profile, screen: "choice", children: _jsxs("div", { className: "mx-auto flex w-full max-w-md flex-col items-center gap-4 text-center", children: [_jsx("h1", { className: "text-xl font-semibold tracking-[-0.03em]", children: "Setup is almost ready." }), _jsx("p", { className: "text-sm text-muted-foreground", children: "We could not load the connection options yet." }), _jsx("button", { type: "button", className: primaryButtonClass, onClick: () => window.location.reload(), children: "Try again" })] }) }));
|
|
34
|
+
}
|
|
35
|
+
if (loading || !profile) {
|
|
36
|
+
return _jsx(OnboardingSkeleton, {});
|
|
37
|
+
}
|
|
38
|
+
const builderCapabilities = profile.capabilities.filter((capability) => capability.builderIncluded);
|
|
39
|
+
const handleBuilder = () => {
|
|
40
|
+
if (connectFlow.hasFetchedStatus && connectFlow.configured) {
|
|
41
|
+
showReady();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
setScreen("connecting");
|
|
45
|
+
connectFlow.start({
|
|
46
|
+
trackingSource: "first_run_onboarding",
|
|
47
|
+
trackingFlow: "connect_llm",
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
const handleOpenSettings = async () => {
|
|
51
|
+
window.dispatchEvent(new CustomEvent("agent-panel:open-settings", {
|
|
52
|
+
detail: { section: "connections" },
|
|
53
|
+
}));
|
|
54
|
+
await completeFirstRun();
|
|
55
|
+
};
|
|
56
|
+
const handleFinish = async () => {
|
|
57
|
+
await completeFirstRun();
|
|
58
|
+
};
|
|
59
|
+
if (screen === "intro") {
|
|
60
|
+
return (_jsx(OnboardingShell, { profile: profile, screen: "intro", children: _jsxs("div", { className: "mx-auto flex w-full max-w-lg flex-col items-center text-center", children: [_jsxs("h1", { className: "text-3xl font-semibold tracking-[-0.05em] sm:text-4xl", children: ["Free forever.", _jsx("br", {}), _jsx("span", { className: "text-primary", children: "Open source for life." })] }), _jsxs("div", { className: "mt-7 grid w-full gap-2 text-left sm:grid-cols-3", children: [_jsxs("div", { className: "rounded-lg border border-border bg-card px-3 py-3", children: [_jsx("p", { className: "text-xs font-medium", children: "Fully customizable" }), _jsx("p", { className: "mt-1 text-[11px] leading-4 text-muted-foreground", children: "Change the UI, code, and behavior." })] }), _jsxs("div", { className: "rounded-lg border border-border bg-card px-3 py-3", children: [_jsx("p", { className: "text-xs font-medium", children: "Bring your own keys" }), _jsx("p", { className: "mt-1 text-[11px] leading-4 text-muted-foreground", children: "Use your own providers and accounts." })] }), _jsxs("div", { className: "rounded-lg border border-border bg-card px-3 py-3", children: [_jsx("p", { className: "text-xs font-medium", children: "Build your own" }), _jsx("p", { className: "mt-1 text-[11px] leading-4 text-muted-foreground", children: "Mix and match toolkit pieces in your own apps." })] })] }), _jsxs("button", { type: "button", className: cn(primaryButtonClass, "mt-6"), onClick: () => setScreen("choice"), children: ["Continue", _jsx(IconArrowRight, { size: 15 })] }), _jsxs("a", { href: "https://github.com/builderio/agent-native", target: "_blank", rel: "noreferrer", className: "mt-3 inline-flex items-center gap-1.5 text-xs text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [_jsx(IconBrandGithub, { size: 14 }), _jsx("span", { children: "View source" }), _jsx(IconExternalLink, { size: 13 })] })] }) }));
|
|
61
|
+
}
|
|
62
|
+
if (screen === "choice") {
|
|
63
|
+
return (_jsx(OnboardingShell, { profile: profile, screen: "choice", children: _jsxs("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-4", children: [_jsx("h1", { className: "text-center text-xl font-semibold tracking-[-0.04em] sm:text-2xl", children: "Choose your setup." }), _jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [_jsxs("section", { className: "rounded-xl border border-primary/50 bg-primary/[0.06] p-4 shadow-sm", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-sm font-semibold", children: "Connect Builder.io" }), _jsxs("p", { className: "mt-1 max-w-xs text-xs leading-5 text-muted-foreground", children: ["One click connects", " ", _jsx("a", { href: "https://www.builder.io/", target: "_blank", rel: "noreferrer", className: "text-foreground underline decoration-border underline-offset-2 hover:decoration-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: "Builder.io's free tier" }), " ", "with the services this app needs."] })] }), _jsx(IconArrowRight, { className: "mt-0.5 text-primary", size: 17 })] }), _jsxs("div", { className: "mt-5 border-t border-primary/15 pt-3", children: [_jsx("p", { className: "text-[11px] font-medium text-muted-foreground", children: "Includes on the free tier" }), _jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-1.5 gap-y-1 text-[11px]", children: [builderCapabilities.map((capability, index) => (_jsxs(React.Fragment, { children: [index > 0 && (_jsx("span", { "aria-hidden": "true", className: "text-muted-foreground", children: "\u00B7" })), _jsx("span", { children: capability.label })] }, capability.id))), _jsx("span", { "aria-hidden": "true", className: "text-muted-foreground", children: "\u00B7" }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "text-primary underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", "aria-label": `See ${BUILDER_MORE_SERVICES.length} more Builder.io services`, children: ["+", BUILDER_MORE_SERVICES.length, " more"] }) }), _jsxs(TooltipContent, { side: "top", className: "max-w-sm text-xs", children: [_jsx("p", { className: "font-medium", children: "Also included with Builder.io" }), _jsx("p", { className: "mt-1 leading-5", children: BUILDER_MORE_SERVICES.join(" · ") })] })] })] })] }), _jsxs("button", { type: "button", "data-testid": "first-run-connect-builder", className: cn(primaryButtonClass, "mt-5 w-full"), onClick: handleBuilder, children: ["Connect Builder.io", _jsx(IconArrowRight, { size: 15 })] })] }), _jsxs("div", { role: "button", tabIndex: 0, "aria-label": "Use my own keys", "data-testid": "first-run-use-own-keys", className: "rounded-xl border border-border bg-card p-4 text-left transition-colors hover:border-foreground/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: () => setScreen("manual"), onKeyDown: (event) => {
|
|
64
|
+
if (event.key !== "Enter" && event.key !== " ")
|
|
65
|
+
return;
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
setScreen("manual");
|
|
68
|
+
}, children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-sm font-semibold", children: "Use my own keys" }), _jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "See what this app needs" })] }), _jsx(IconKey, { className: "text-muted-foreground", size: 17 })] }), _jsx(CapabilityList, { capabilities: profile.capabilities, compact: true, className: "mt-5 border-t border-border pt-3" })] })] })] }) }));
|
|
69
|
+
}
|
|
70
|
+
if (screen === "manual") {
|
|
71
|
+
return (_jsx(OnboardingShell, { profile: profile, screen: "choice", children: _jsxs("div", { className: "mx-auto flex w-full max-w-xl flex-col gap-4", children: [_jsxs("div", { children: [_jsx("button", { type: "button", className: "mb-4 text-xs text-muted-foreground hover:text-foreground", onClick: () => setScreen("choice"), children: "Back" }), _jsx("h1", { className: "text-xl font-semibold tracking-[-0.04em] sm:text-2xl", children: "Your keys" })] }), _jsxs("div", { className: "rounded-xl border border-border bg-card p-4", children: [_jsx(CapabilityList, { capabilities: profile.capabilities }), _jsxs("div", { className: "mt-5 flex flex-col-reverse gap-2 border-t border-border pt-4 sm:flex-row sm:justify-between", children: [_jsx("button", { type: "button", className: secondaryButtonClass, onClick: () => setScreen("choice"), children: "Back" }), _jsxs("button", { type: "button", className: primaryButtonClass, onClick: handleOpenSettings, children: ["Open key settings", _jsx(IconArrowRight, { size: 15 })] })] })] })] }) }));
|
|
72
|
+
}
|
|
73
|
+
if (screen === "connecting") {
|
|
74
|
+
return (_jsx(OnboardingShell, { profile: profile, screen: "choice", children: _jsxs("div", { className: "mx-auto flex w-full max-w-md flex-col items-center text-center", children: [_jsx("div", { className: "flex size-10 items-center justify-center rounded-full bg-primary/10 text-primary", children: _jsx(IconLoader2, { className: "animate-spin", size: 19 }) }), _jsx("h1", { className: "mt-5 text-xl font-semibold tracking-[-0.04em]", children: "Connecting Builder.io" }), _jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Finish the one-click connection in the new window." }), _jsxs("div", { className: "mt-7 w-full rounded-xl border border-border bg-card p-4 text-left", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx(Skeleton, { className: "h-3 w-28" }), _jsx(Skeleton, { className: "h-5 w-16 rounded-full" })] }), _jsx(Skeleton, { className: "mt-4 h-8 w-full" }), _jsxs("div", { className: "mt-3 grid grid-cols-3 gap-2", children: [_jsx(Skeleton, { className: "h-7 w-full" }), _jsx(Skeleton, { className: "h-7 w-full" }), _jsx(Skeleton, { className: "h-7 w-full" })] })] }), connectFlow.error && (_jsxs("div", { className: "mt-4 flex flex-col items-center gap-2", children: [_jsx("p", { className: "text-xs text-destructive", children: connectFlow.error }), _jsx("button", { type: "button", className: secondaryButtonClass, onClick: () => setScreen("choice"), children: "Try again" })] }))] }) }));
|
|
75
|
+
}
|
|
76
|
+
return (_jsx(OnboardingShell, { profile: profile, screen: "choice", children: _jsxs("div", { className: "mx-auto flex w-full max-w-md flex-col items-center text-center", children: [_jsx("div", { className: "flex size-10 items-center justify-center rounded-full bg-primary/10 text-primary", children: _jsx(IconCheck, { size: 20 }) }), _jsx("h1", { className: "mt-5 text-xl font-semibold tracking-[-0.04em]", children: "Ready to go." }), _jsxs("button", { type: "button", "data-testid": "first-run-open-app", className: cn(primaryButtonClass, "mt-7"), onClick: handleFinish, children: ["Open app", _jsx(IconArrowRight, { size: 15 })] })] }) }));
|
|
77
|
+
}
|
|
78
|
+
function OnboardingShell({ profile, screen, children, }) {
|
|
79
|
+
return (_jsxs("div", { className: "fixed inset-0 z-[100] flex min-h-screen flex-col bg-background text-foreground", "data-onboarding-screen": screen, role: "dialog", "aria-modal": "true", "aria-label": `${profile?.appName ?? "Your app"} setup`, children: [_jsxs("header", { className: "flex items-center justify-between border-b border-border px-5 py-4 text-xs text-muted-foreground sm:px-8", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("img", { src: appPath("/agent-native-icon-dark.svg"), alt: "", "aria-hidden": "true", className: "h-4 w-auto shrink-0" }), _jsx("span", { className: "font-medium text-foreground", children: profile?.appName ?? "Your app" })] }), _jsx("span", { children: screen === "intro" ? "1 / 2" : "2 / 2" })] }), _jsx("div", { className: "h-0.5 bg-muted", children: _jsx("div", { className: "h-full bg-primary transition-[width] duration-200", style: { width: screen === "intro" ? "50%" : "100%" } }) }), _jsx("main", { className: "flex min-h-0 flex-1 items-center overflow-y-auto px-5 py-10 sm:px-8", children: _jsx("div", { className: "mx-auto w-full max-w-3xl", children: children }) })] }));
|
|
80
|
+
}
|
|
81
|
+
function OnboardingSkeleton() {
|
|
82
|
+
return (_jsxs("div", { className: "fixed inset-0 z-[100] flex min-h-screen flex-col bg-background px-5 py-5 sm:px-8", "data-onboarding-loading": "true", "aria-busy": "true", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Skeleton, { className: "h-4 w-20" }), _jsx(Skeleton, { className: "h-3 w-8" })] }), _jsx(Skeleton, { className: "mt-5 h-0.5 w-full" }), _jsxs("div", { className: "mx-auto flex w-full max-w-lg flex-1 flex-col items-center justify-center", children: [_jsx(Skeleton, { className: "h-10 w-64" }), _jsx(Skeleton, { className: "mt-3 h-10 w-52" }), _jsx(Skeleton, { className: "mt-7 h-3 w-44" }), _jsx(Skeleton, { className: "mt-7 h-10 w-24 rounded-lg" })] })] }));
|
|
83
|
+
}
|
|
84
|
+
function CapabilityList({ capabilities, compact = false, className, }) {
|
|
85
|
+
const visibleCapabilities = useMemo(() => (compact ? capabilities.slice(0, 4) : capabilities), [capabilities, compact]);
|
|
86
|
+
return (_jsxs("div", { className: cn("grid", className), children: [!compact && (_jsx("p", { className: "mb-2 text-xs font-medium text-muted-foreground", children: "Keys and connections" })), _jsx("div", { className: "divide-y divide-border", children: visibleCapabilities.map((capability) => (_jsx(CapabilityRow, { capability: capability, compact: compact }, capability.id))) })] }));
|
|
87
|
+
}
|
|
88
|
+
function CapabilityRow({ capability, compact, }) {
|
|
89
|
+
return (_jsxs("div", { className: cn("flex items-start justify-between gap-3", compact ? "py-2" : "py-3"), children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: cn("font-medium", compact ? "text-[11px]" : "text-sm"), children: capability.label }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": `Why ${capability.label} is needed`, className: "inline-flex size-4 items-center justify-center rounded-full text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: _jsx(IconInfoCircle, { size: 13 }) }) }), _jsx(TooltipContent, { side: "top", className: "max-w-xs text-xs", children: capability.why })] })] }), _jsx("p", { className: "mt-1 text-[11px] leading-4 text-muted-foreground", children: capability.keySummary })] }), _jsx("span", { className: cn("shrink-0 text-[10px] uppercase tracking-[0.08em]", capability.required ? "text-primary" : "text-muted-foreground"), children: capability.required ? "Required" : "Optional" })] }));
|
|
90
|
+
}
|
|
91
|
+
const primaryButtonClass = "inline-flex min-h-9 items-center justify-center gap-2 rounded-lg bg-primary px-4 text-xs font-medium text-primary-foreground shadow-sm transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
|
|
92
|
+
const secondaryButtonClass = "inline-flex min-h-9 items-center justify-center gap-2 rounded-lg border border-border bg-background px-4 text-xs font-medium text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
|
|
93
|
+
//# sourceMappingURL=FirstRunOnboarding.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const FIRST_RUN_ONBOARDING_ENV_KEY = "VITE_AGENT_NATIVE_FIRST_RUN_ONBOARDING";
|
|
2
|
+
export function isFirstRunOnboardingEnabled(env = (import.meta.env ??
|
|
3
|
+
{})) {
|
|
4
|
+
const value = env[FIRST_RUN_ONBOARDING_ENV_KEY];
|
|
5
|
+
return (value === true ||
|
|
6
|
+
(typeof value === "string" &&
|
|
7
|
+
["1", "true"].includes(value.trim().toLowerCase())));
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=first-run-enabled.js.map
|
|
@@ -8,6 +8,7 @@ export { useOnboardingPreviewMode, ONBOARDING_PREVIEW_STORAGE_KEY, } from "./use
|
|
|
8
8
|
export { OnboardingPanel } from "./OnboardingPanel.js";
|
|
9
9
|
export { OnboardingBanner } from "./OnboardingBanner.js";
|
|
10
10
|
export { SetupButton } from "./SetupButton.js";
|
|
11
|
-
export
|
|
11
|
+
export { FirstRunOnboarding } from "./FirstRunOnboarding.js";
|
|
12
|
+
export type { OnboardingStep, OnboardingMethod, OnboardingMethodBadge, OnboardingFormField, OnboardingStepStatus, OnboardingCapability, OnboardingAppProfile, } from "../../onboarding/types.js";
|
|
12
13
|
export { registerOnboardingStep, listOnboardingSteps, } from "../../onboarding/registry.js";
|
|
13
14
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -8,5 +8,6 @@ export { useOnboardingPreviewMode, ONBOARDING_PREVIEW_STORAGE_KEY, } from "./use
|
|
|
8
8
|
export { OnboardingPanel } from "./OnboardingPanel.js";
|
|
9
9
|
export { OnboardingBanner } from "./OnboardingBanner.js";
|
|
10
10
|
export { SetupButton } from "./SetupButton.js";
|
|
11
|
+
export { FirstRunOnboarding } from "./FirstRunOnboarding.js";
|
|
11
12
|
export { registerOnboardingStep, listOnboardingSteps, } from "../../onboarding/registry.js";
|
|
12
13
|
//# sourceMappingURL=index.js.map
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
* No polling — onboarding state changes are user-driven, so a poll loop just
|
|
7
7
|
* burns the DB and amplifies transient network errors.
|
|
8
8
|
*/
|
|
9
|
-
import type { OnboardingMethod, OnboardingStepStatus } from "../../onboarding/types.js";
|
|
9
|
+
import type { OnboardingAppProfile, OnboardingMethod, OnboardingStepStatus } from "../../onboarding/types.js";
|
|
10
10
|
export interface UseOnboardingResult {
|
|
11
11
|
steps: OnboardingStepStatus[];
|
|
12
|
+
profile: OnboardingAppProfile | null;
|
|
12
13
|
loading: boolean;
|
|
13
14
|
error: string | null;
|
|
14
15
|
/** Active step = first required+incomplete, else first incomplete. */
|
|
@@ -27,6 +28,10 @@ export interface UseOnboardingResult {
|
|
|
27
28
|
dismiss: () => Promise<void>;
|
|
28
29
|
/** Re-open the panel after dismissal. */
|
|
29
30
|
reopen: () => Promise<void>;
|
|
31
|
+
/** True until the post-signup full-screen flow is completed. */
|
|
32
|
+
firstRun: boolean;
|
|
33
|
+
/** Clear the post-signup full-screen flow marker. */
|
|
34
|
+
completeFirstRun: () => Promise<void>;
|
|
30
35
|
}
|
|
31
36
|
export declare function useOnboarding(options?: {
|
|
32
37
|
preview?: boolean;
|