@agent-native/core 0.84.53 → 0.84.54

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.
Files changed (66) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +7 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/cli/skills.ts +34 -16
  5. package/corpus/core/src/client/AssistantChat.tsx +140 -114
  6. package/corpus/core/src/client/blocks/library/diagram.config.ts +13 -2
  7. package/corpus/core/src/client/blocks/library/diagram.tsx +27 -3
  8. package/corpus/core/src/client/blocks/library/server-specs.ts +2 -2
  9. package/corpus/core/src/client/blocks/library/wireframe.config.ts +7 -1
  10. package/corpus/core/src/client/blocks/library/wireframe.tsx +42 -3
  11. package/corpus/core/src/client/blocks/types.ts +9 -0
  12. package/corpus/core/src/client/chat/message-components.tsx +1 -1
  13. package/corpus/core/src/styles/agent-native.css +44 -0
  14. package/corpus/core/src/styles/blocks.css +18 -0
  15. package/corpus/templates/plan/.agents/skills/visual-plan/references/document-quality.md +10 -6
  16. package/corpus/templates/plan/.agents/skills/visual-plan/references/wireframe.md +20 -9
  17. package/corpus/templates/plan/.agents/skills/visual-recap/SKILL.md +4 -1
  18. package/corpus/templates/plan/.agents/skills/visual-recap/references/wireframe.md +20 -9
  19. package/corpus/templates/plan/actions/create-ui-plan.ts +1 -1
  20. package/corpus/templates/plan/actions/create-visual-plan.ts +1 -1
  21. package/corpus/templates/plan/actions/create-visual-recap.ts +1 -1
  22. package/corpus/templates/plan/actions/get-plan-blocks.ts +2 -0
  23. package/corpus/templates/plan/app/components/plan/planBlocks.tsx +2 -0
  24. package/corpus/templates/plan/app/components/plan/wireframe/Wireframe.tsx +38 -4
  25. package/corpus/templates/plan/app/components/plan/wireframe/html-artboard.css +18 -0
  26. package/corpus/templates/plan/app/global.css +1 -2
  27. package/corpus/templates/plan/changelog/2026-07-02-plan-chat-keeps-the-composer-anchored-when-connect-ai-appears.md +6 -0
  28. package/corpus/templates/plan/changelog/2026-07-02-wireframe-and-diagram-blocks-can-now-show-or-hide-their-outer-frame.md +6 -0
  29. package/corpus/templates/plan/server/plan-mdx.ts +3 -0
  30. package/corpus/templates/plan/shared/plan-content.ts +8 -0
  31. package/dist/cli/skills.d.ts +3 -3
  32. package/dist/cli/skills.d.ts.map +1 -1
  33. package/dist/cli/skills.js +34 -16
  34. package/dist/cli/skills.js.map +1 -1
  35. package/dist/client/AssistantChat.d.ts.map +1 -1
  36. package/dist/client/AssistantChat.js +32 -25
  37. package/dist/client/AssistantChat.js.map +1 -1
  38. package/dist/client/blocks/library/diagram.config.d.ts +3 -1
  39. package/dist/client/blocks/library/diagram.config.d.ts.map +1 -1
  40. package/dist/client/blocks/library/diagram.config.js +10 -1
  41. package/dist/client/blocks/library/diagram.config.js.map +1 -1
  42. package/dist/client/blocks/library/diagram.d.ts.map +1 -1
  43. package/dist/client/blocks/library/diagram.js +13 -7
  44. package/dist/client/blocks/library/diagram.js.map +1 -1
  45. package/dist/client/blocks/library/server-specs.js +2 -2
  46. package/dist/client/blocks/library/server-specs.js.map +1 -1
  47. package/dist/client/blocks/library/wireframe.config.d.ts +3 -1
  48. package/dist/client/blocks/library/wireframe.config.d.ts.map +1 -1
  49. package/dist/client/blocks/library/wireframe.config.js +4 -0
  50. package/dist/client/blocks/library/wireframe.config.js.map +1 -1
  51. package/dist/client/blocks/library/wireframe.d.ts.map +1 -1
  52. package/dist/client/blocks/library/wireframe.js +20 -9
  53. package/dist/client/blocks/library/wireframe.js.map +1 -1
  54. package/dist/client/blocks/types.d.ts +8 -0
  55. package/dist/client/blocks/types.d.ts.map +1 -1
  56. package/dist/client/blocks/types.js.map +1 -1
  57. package/dist/client/chat/message-components.js +1 -1
  58. package/dist/client/chat/message-components.js.map +1 -1
  59. package/dist/collab/routes.d.ts +1 -1
  60. package/dist/notifications/routes.d.ts +3 -3
  61. package/dist/observability/routes.d.ts +2 -2
  62. package/dist/resources/handlers.d.ts +1 -1
  63. package/dist/server/transcribe-voice.d.ts +1 -1
  64. package/dist/styles/agent-native.css +44 -0
  65. package/dist/styles/blocks.css +18 -0
  66. package/package.json +1 -1
@@ -113,6 +113,7 @@ function ArtboardFrame({
113
113
  skeleton,
114
114
  renderMode,
115
115
  roughOverlay = true,
116
+ showFrame = true,
116
117
  selector,
117
118
  caption,
118
119
  render,
@@ -129,6 +130,7 @@ function ArtboardFrame({
129
130
  skeleton?: boolean;
130
131
  renderMode?: "wireframe" | "design";
131
132
  roughOverlay?: boolean;
133
+ showFrame?: boolean;
132
134
  selector: string;
133
135
  caption?: string;
134
136
  render: (ctx: {
@@ -164,6 +166,9 @@ function ArtboardFrame({
164
166
  const designMode = renderMode === "design";
165
167
  const sketchy = !designMode && style === "sketchy" && !skeleton;
166
168
  const roughEnabled = sketchy && roughOverlay;
169
+ const frameBorder = skeleton
170
+ ? "var(--plan-placeholder-line, var(--plan-line, hsl(var(--border))))"
171
+ : "var(--plan-line, hsl(var(--border)))";
167
172
 
168
173
  useEffect(() => {
169
174
  const element = fitRef.current;
@@ -231,6 +236,7 @@ function ArtboardFrame({
231
236
  ref={ref}
232
237
  className="plan-kit-artboard relative"
233
238
  data-rough-scope="wireframe"
239
+ data-frame={showFrame ? "show" : "hide"}
234
240
  style={{
235
241
  width,
236
242
  // Auto-height by default (content-driven, floored at `minHeight`);
@@ -259,10 +265,19 @@ function ArtboardFrame({
259
265
  >
260
266
  {render({ theme, style })}
261
267
  </div>
268
+ {!roughEnabled && showFrame && (
269
+ <div
270
+ className="pointer-events-none absolute inset-0"
271
+ style={{
272
+ borderRadius: preset.radius,
273
+ border: `1.5px solid ${frameBorder}`,
274
+ }}
275
+ />
276
+ )}
262
277
  <RoughOverlay
263
278
  scopeRef={ref}
264
279
  enabled={roughEnabled}
265
- drawFrame={false}
280
+ drawFrame={showFrame}
266
281
  frameRadius={preset.radius}
267
282
  selector={selector}
268
283
  />
@@ -313,10 +328,12 @@ function WireframeStyleToggleButton() {
313
328
  function HtmlArtboard({
314
329
  data,
315
330
  ctx: _ctx,
331
+ showFrame,
316
332
  compact,
317
333
  }: {
318
334
  data: WireframeData;
319
335
  ctx: BlockRenderContext;
336
+ showFrame: boolean;
320
337
  compact?: boolean;
321
338
  }) {
322
339
  const renderMode = data.renderMode ?? "wireframe";
@@ -350,6 +367,7 @@ function HtmlArtboard({
350
367
  compact={compact}
351
368
  skeleton={data.skeleton}
352
369
  renderMode={renderMode}
370
+ showFrame={showFrame}
353
371
  selector={HTML_ROUGH_SELECTOR}
354
372
  caption={data.caption}
355
373
  render={({ theme, style }) => (
@@ -357,6 +375,7 @@ function HtmlArtboard({
357
375
  className="plan-html-frame"
358
376
  data-theme={theme}
359
377
  data-style={style}
378
+ data-frame={showFrame ? "show" : "hide"}
360
379
  data-render-mode={renderMode}
361
380
  data-plan-design-scope={scopeId}
362
381
  data-skeleton={data.skeleton ? "true" : undefined}
@@ -378,9 +397,11 @@ function HtmlArtboard({
378
397
 
379
398
  function KitArtboard({
380
399
  data,
400
+ showFrame,
381
401
  compact,
382
402
  }: {
383
403
  data: WireframeData;
404
+ showFrame: boolean;
384
405
  compact?: boolean;
385
406
  }) {
386
407
  return (
@@ -388,6 +409,7 @@ function KitArtboard({
388
409
  surface={data.surface}
389
410
  compact={compact}
390
411
  skeleton={data.skeleton}
412
+ showFrame={showFrame}
391
413
  selector="[data-rough]"
392
414
  caption={data.caption}
393
415
  render={({ theme, style }) => (
@@ -430,10 +452,27 @@ function WireframeSurfaceView({
430
452
  ctx: BlockRenderContext;
431
453
  compact?: boolean;
432
454
  }) {
455
+ const showFrame = resolveVisualFrame(data.frame, ctx);
433
456
  if (isHtmlData(data)) {
434
- return <HtmlArtboard data={data} ctx={ctx} compact={compact} />;
457
+ return (
458
+ <HtmlArtboard
459
+ data={data}
460
+ ctx={ctx}
461
+ showFrame={showFrame}
462
+ compact={compact}
463
+ />
464
+ );
435
465
  }
436
- return <KitArtboard data={data} compact={compact} />;
466
+ return <KitArtboard data={data} showFrame={showFrame} compact={compact} />;
467
+ }
468
+
469
+ function resolveVisualFrame(
470
+ frame: WireframeData["frame"],
471
+ ctx: BlockRenderContext,
472
+ ): boolean {
473
+ const resolved =
474
+ frame && frame !== "auto" ? frame : (ctx.visualFrame ?? "show");
475
+ return resolved !== "hide";
437
476
  }
438
477
 
439
478
  /* -------------------------------------------------------------------------- */
@@ -19,6 +19,9 @@ import type { ZodType } from "zod";
19
19
  /** Where a block can be placed in a document. */
20
20
  export type BlockPlacement = "block" | "inline";
21
21
 
22
+ /** How visual blocks decide whether to draw an outer surface frame. */
23
+ export type BlockVisualFrame = "auto" | "show" | "hide";
24
+
22
25
  /**
23
26
  * A serialized MDX/NFM attribute value before the shared `prop()` encoder runs.
24
27
  * `prop()` decides string-vs-JSON encoding; this is just the value domain.
@@ -96,6 +99,12 @@ export interface BlockRenderContext {
96
99
  dialect?: "gfm" | "nfm";
97
100
  /** Document text direction inferred by the host app. */
98
101
  textDirection?: "ltr" | "rtl";
102
+ /**
103
+ * Host default for visual wireframe/diagram frames when block data leaves
104
+ * `frame` unset or set to `auto`. Plans/recaps usually show a frame; docs can
105
+ * hide it by default and let individual blocks opt back in.
106
+ */
107
+ visualFrame?: Exclude<BlockVisualFrame, "auto">;
99
108
  /** Resolve an asset id → displayable URL. */
100
109
  resolveAssetSrc?: (assetId: string) => string | undefined;
101
110
  /** Open the shared asset picker (returns the chosen asset). */
@@ -214,7 +214,7 @@ export function SelectionAttachedPill() {
214
214
  if (length === null || length === 0) return null;
215
215
 
216
216
  return (
217
- <div className="shrink-0 px-3 pt-1.5 -mb-1">
217
+ <div className="agent-selection-attached-pill shrink-0 px-3 pt-1.5 -mb-1">
218
218
  <div className="inline-flex items-center gap-1.5 rounded-full border border-border bg-muted/50 px-2 py-0.5 text-[11px] text-muted-foreground">
219
219
  <IconQuote size={11} />
220
220
  <span>{length.toLocaleString()} chars of selection attached</span>
@@ -682,6 +682,23 @@
682
682
  line-height: 1.75rem;
683
683
  }
684
684
 
685
+ .agent-composer-stack {
686
+ position: relative;
687
+ flex-shrink: 0;
688
+ width: 100%;
689
+ }
690
+
691
+ .agent-composer-stack[data-agent-composer-adjacent-ui="true"],
692
+ .agent-composer-stack:has(.agent-selection-attached-pill) {
693
+ display: flex;
694
+ flex-direction: column;
695
+ gap: 0.5rem;
696
+ }
697
+
698
+ .agent-composer-setup-card {
699
+ width: 100%;
700
+ }
701
+
685
702
  [data-agent-empty-state="centered"] {
686
703
  justify-content: center;
687
704
  }
@@ -691,6 +708,33 @@
691
708
  overflow: visible;
692
709
  }
693
710
 
711
+ [data-agent-empty-state="centered"]
712
+ > .agent-composer-stack:not([data-agent-composer-adjacent-ui="true"]):not(
713
+ :has(.agent-selection-attached-pill)
714
+ )
715
+ > .agent-composer-setup-card {
716
+ position: absolute;
717
+ left: 0;
718
+ right: 0;
719
+ z-index: 3;
720
+ }
721
+
722
+ [data-agent-empty-state="centered"]
723
+ > .agent-composer-stack:not([data-agent-composer-adjacent-ui="true"]):not(
724
+ :has(.agent-selection-attached-pill)
725
+ )
726
+ > .agent-composer-setup-card[data-agent-composer-setup-position="above"] {
727
+ bottom: calc(100% + 0.5rem);
728
+ }
729
+
730
+ [data-agent-empty-state="centered"]
731
+ > .agent-composer-stack:not([data-agent-composer-adjacent-ui="true"]):not(
732
+ :has(.agent-selection-attached-pill)
733
+ )
734
+ > .agent-composer-setup-card[data-agent-composer-setup-position="below"] {
735
+ top: calc(100% + 0.5rem);
736
+ }
737
+
694
738
  [data-agent-empty-state="compact-setup"] > .agent-chat-scroll {
695
739
  flex: 0 0 auto;
696
740
  overflow: visible;
@@ -1290,6 +1290,20 @@
1290
1290
  line-height: 1.4;
1291
1291
  }
1292
1292
 
1293
+ .plan-diagram-frame[data-frame="show"] {
1294
+ border: 1px solid var(--wf-diagram-line);
1295
+ padding: 10px;
1296
+ }
1297
+
1298
+ .plan-diagram-frame[data-frame="show"][data-rough-ready] {
1299
+ border-color: transparent;
1300
+ }
1301
+
1302
+ .plan-diagram-frame[data-frame="hide"] {
1303
+ border: 0;
1304
+ background: transparent;
1305
+ }
1306
+
1293
1307
  .plan-diagram-frame[data-theme="dark"] {
1294
1308
  --wf-diagram-line: color-mix(in srgb, var(--wf-sketch) 72%, var(--wf-line));
1295
1309
  }
@@ -1312,6 +1326,10 @@
1312
1326
  direction: inherit;
1313
1327
  }
1314
1328
 
1329
+ .plan-diagram-frame[data-frame="show"] .plan-diagram-frame-content {
1330
+ padding: 0;
1331
+ }
1332
+
1315
1333
  .plan-diagram-frame-content
1316
1334
  > .diagram-panel:not([data-rough]):not([data-diagram-frame]) {
1317
1335
  --rough-skip: 1;
@@ -109,12 +109,16 @@ so you never emit a block the editor cannot render or round-trip:
109
109
  `--wf-paper`, `--wf-card`, `--wf-accent`, `--wf-accent-soft`, `--wf-warn`, and
110
110
  `--wf-ok`, and switch to Excalifont plus rough.js outlines in sketchy mode. Do not
111
111
  set `font-family` and do not hard-code hex, rgb, or hsl colors in diagram HTML
112
- or CSS. Leave room for the sketch font: keep labels short, give nodes generous
113
- width, and place boundary/annotation labels in unused space instead of over
114
- nodes; labels must not overlap nodes, connectors, or each other. For small
115
- text/SVG changes to an existing HTML diagram, use `patch-diagram-html` with a
116
- unique `find`/`replace` snippet instead of resending the whole `data.html`
117
- string. Use legacy `nodes` / `edges` only for small previews or truly
112
+ or CSS. Choose the outer `frame` intentionally: use `show` when the diagram
113
+ stands alone in a recap, comparison, or prose section; use `hide` when the
114
+ diagram sits inside docs chrome, columns, tabs, cards, a canvas surface, or
115
+ already has visible `.diagram-panel` / `.diagram-box` structure. Leave room
116
+ for the sketch font: keep labels short, give nodes generous width, and place
117
+ boundary/annotation labels in unused space instead of over nodes; labels must
118
+ not overlap nodes, connectors, or each other. For small text/SVG changes to an
119
+ existing HTML diagram, use `patch-diagram-html` with a unique
120
+ `find`/`replace` snippet instead of resending the whole `data.html` string.
121
+ Use legacy `nodes` / `edges` only for small previews or truly
118
122
  sequential flows. In architecture/code plans, prefer a repeated section rhythm:
119
123
  recommendation title, confidence and category badges, code-path evidence, a
120
124
  local before/after or current/target spatial diagram, then concise
@@ -171,15 +171,26 @@ occurrence. The result is re-sanitized. In local-files privacy mode, do not call
171
171
  hosted Plan tools; edit the local MDX source directly and rerun the local
172
172
  check/serve or verify command for `<plan-dir>`.
173
173
 
174
- **Treat the wireframe border as part of the visible design.** Always wrap HTML
175
- wireframe content in a root container with real inner padding before drawing
176
- cards, fields, pills, labels, or controls. Use at least 14-16px of padding,
177
- `box-sizing: border-box`, `height: 100%`, and `gap` between child rows on the
178
- root node itself so the first row never sits flush against the screen border. Do
179
- not rely on padding on a nested page section as the first visible inset; the
180
- outermost element must create the breathing room. Keep text away from borders:
181
- every container, field, button, menu item, and annotation needs enough padding
182
- and line-height to read cleanly in the rendered Plan view.
174
+ **Choose the outer frame deliberately.** Wireframe and diagram data accept
175
+ `frame: "auto" | "show" | "hide"` in block data (`<Screen frame="hide">` in
176
+ MDX wireframes, `<Diagram frame="hide">` for MDX diagrams). Leave it unset or
177
+ `auto` when the host context should decide: Plan and recap surfaces default to a
178
+ drawn outer frame; docs surfaces default to no outer frame. Use `show` for
179
+ standalone product screens, before/after recap comparisons, screenshot-like
180
+ artifacts, and visuals that need containment from surrounding prose. Use `hide`
181
+ when a docs page, tab, column, card, canvas artboard, or the visual's own
182
+ internal chrome already supplies the boundary. Do not use `hide` to compensate
183
+ for cramped content; fix the layout instead.
184
+
185
+ **Inner padding and borders still matter.** Always wrap HTML wireframe content
186
+ in a root container with real inner padding before drawing cards, fields, pills,
187
+ labels, or controls. Use at least 14-16px of padding, `box-sizing: border-box`,
188
+ `height: 100%`, and `gap` between child rows on the root node itself so the
189
+ first row never sits flush against the screen edge. Do not rely on padding on a
190
+ nested page section as the first visible inset; the outermost element must
191
+ create the breathing room. Keep text away from borders: every container, field,
192
+ button, menu item, and annotation needs enough padding and line-height to read
193
+ cleanly in the rendered Plan view.
183
194
 
184
195
  **For feature-cloud or abundance visuals, optimize the composition over line-by-line
185
196
  reading.** Some marketing/product sections need to feel like a large surface area
@@ -377,7 +377,10 @@ tags — resolve every conceptual name to its exact tag + prop schema with the
377
377
  Author diagram HTML/CSS with the renderer-owned `.diagram-*` primitives
378
378
  (`.diagram-panel`, `.diagram-node`, `.diagram-pill`, `[data-rough]`, …) and
379
379
  the same `--wf-*` theme tokens `references/wireframe.md` defines — never
380
- `font-family`, hex, rgb/hsl literals, or one-off dark/light palettes.
380
+ `font-family`, hex, rgb/hsl literals, or one-off dark/light palettes. Choose
381
+ the outer `frame` intentionally: recap diagrams usually benefit from
382
+ `frame: "show"` when they stand alone, but use `frame: "hide"` when columns,
383
+ tabs, a card, or the diagram's own panels already provide the boundary.
381
384
  - **Outcome-first narrative** → `rich-text` for the "what changed and why" prose:
382
385
  the objective the diff served, the key decisions visible in it, and the risks a
383
386
  reviewer should weigh. This is the only place the model writes freely.
@@ -171,15 +171,26 @@ occurrence. The result is re-sanitized. In local-files privacy mode, do not call
171
171
  hosted Plan tools; edit the local MDX source directly and rerun the local
172
172
  check/serve or verify command for `<plan-dir>`.
173
173
 
174
- **Treat the wireframe border as part of the visible design.** Always wrap HTML
175
- wireframe content in a root container with real inner padding before drawing
176
- cards, fields, pills, labels, or controls. Use at least 14-16px of padding,
177
- `box-sizing: border-box`, `height: 100%`, and `gap` between child rows on the
178
- root node itself so the first row never sits flush against the screen border. Do
179
- not rely on padding on a nested page section as the first visible inset; the
180
- outermost element must create the breathing room. Keep text away from borders:
181
- every container, field, button, menu item, and annotation needs enough padding
182
- and line-height to read cleanly in the rendered Plan view.
174
+ **Choose the outer frame deliberately.** Wireframe and diagram data accept
175
+ `frame: "auto" | "show" | "hide"` in block data (`<Screen frame="hide">` in
176
+ MDX wireframes, `<Diagram frame="hide">` for MDX diagrams). Leave it unset or
177
+ `auto` when the host context should decide: Plan and recap surfaces default to a
178
+ drawn outer frame; docs surfaces default to no outer frame. Use `show` for
179
+ standalone product screens, before/after recap comparisons, screenshot-like
180
+ artifacts, and visuals that need containment from surrounding prose. Use `hide`
181
+ when a docs page, tab, column, card, canvas artboard, or the visual's own
182
+ internal chrome already supplies the boundary. Do not use `hide` to compensate
183
+ for cramped content; fix the layout instead.
184
+
185
+ **Inner padding and borders still matter.** Always wrap HTML wireframe content
186
+ in a root container with real inner padding before drawing cards, fields, pills,
187
+ labels, or controls. Use at least 14-16px of padding, `box-sizing: border-box`,
188
+ `height: 100%`, and `gap` between child rows on the root node itself so the
189
+ first row never sits flush against the screen edge. Do not rely on padding on a
190
+ nested page section as the first visible inset; the outermost element must
191
+ create the breathing room. Keep text away from borders: every container, field,
192
+ button, menu item, and annotation needs enough padding and line-height to read
193
+ cleanly in the rendered Plan view.
183
194
 
184
195
  **For feature-cloud or abundance visuals, optimize the composition over line-by-line
185
196
  reading.** Some marketing/product sections need to feel like a large surface area
@@ -72,7 +72,7 @@ export default defineAction({
72
72
  content: planContentSchema
73
73
  .optional()
74
74
  .describe(
75
- "Structured editable UI plan content. Prefer this for app-owned top canvas wireframes (HTML mockups: set the wireframe's data.html to a semantic HTML fragment of the screen and pick a surface — the renderer owns the theme, footprint/aspect, hand-drawn font, and sketch overlay; use --wf-* CSS tokens for any custom color, never hex). Do not use legacy kit-tree screen arrays or nested FrameScreen/Card/Row/Btn-style children for new canvas artboards. Use sketch diagrams, rich text, code blocks (grouped in a vertical tabs block for a file map), annotated code for key files, validation checklists, and bounded custom HTML fragments. Diagram data.html/data.css should use renderer-owned .diagram-* primitives plus --wf-* tokens, not custom fonts or hard-coded hex/rgb/hsl colors, so light/dark and sketchy Excalifont/rough.js modes remain correct. The canvas should carry Claude-style flex/grid wireframe artboards and designer annotations; the document should add implementation substance instead of duplicating the same wireframes. The renderer owns all visual styling; emit lean content, not pixels.",
75
+ 'Structured editable UI plan content. Prefer this for app-owned top canvas wireframes (HTML mockups: set the wireframe\'s data.html to a semantic HTML fragment of the screen and pick a surface — the renderer owns the theme, footprint/aspect, hand-drawn font, and sketch overlay; use --wf-* CSS tokens for any custom color, never hex). Call get-plan-blocks first for visual frame guidance before choosing frame: "show" or frame: "hide". Do not use legacy kit-tree screen arrays or nested FrameScreen/Card/Row/Btn-style children for new canvas artboards. Use sketch diagrams, rich text, code blocks (grouped in a vertical tabs block for a file map), annotated code for key files, validation checklists, and bounded custom HTML fragments. Diagram data.html/data.css should use renderer-owned .diagram-* primitives plus --wf-* tokens, not custom fonts or hard-coded hex/rgb/hsl colors, so light/dark and sketchy Excalifont/rough.js modes remain correct. The canvas should carry Claude-style flex/grid wireframe artboards and designer annotations; the document should add implementation substance instead of duplicating the same wireframes. The renderer owns all visual styling; emit lean content, not pixels.',
76
76
  ),
77
77
  markdown: z
78
78
  .string()
@@ -84,7 +84,7 @@ export default defineAction({
84
84
  content: planContentSchema
85
85
  .optional()
86
86
  .describe(
87
- "Structured editable plan content. Prefer this for rich text, inline diagrams, annotated code, question-form open questions, and optional canvas/prototype UI surfaces. Call the get-plan-blocks tool FIRST for the authoritative block catalog, authoring rules, and style tokens — do not author from memory. Key rules: canvas frames use wireframe data.html/html semantic HTML, not legacy kit-tree screen arrays; use diagram blocks with .diagram-* primitives and --wf-* tokens (no hex/rgb/hsl, no custom fonts); for file maps use annotated-code blocks in a vertical tabs block; put unresolved decisions in a bottom question-form block.",
87
+ "Structured editable plan content. Prefer this for rich text, inline diagrams, annotated code, question-form open questions, and optional canvas/prototype UI surfaces. Call the get-plan-blocks tool FIRST for the authoritative block catalog, visual frame guidance, authoring rules, and style tokens — do not author from memory. Key rules: canvas frames use wireframe data.html/html semantic HTML, not legacy kit-tree screen arrays; use diagram blocks with .diagram-* primitives and --wf-* tokens (no hex/rgb/hsl, no custom fonts); for file maps use annotated-code blocks in a vertical tabs block; put unresolved decisions in a bottom question-form block.",
88
88
  ),
89
89
  markdown: z
90
90
  .string()
@@ -245,7 +245,7 @@ export default defineAction({
245
245
  .describe("Current focus for the review surface."),
246
246
  status: planStatusSchema.optional().default("review"),
247
247
  mdx: planMdxFileSchema.describe(
248
- "Recap source files. Call the get-plan-blocks tool FIRST for the authoritative block catalog, authoring rules, and style tokens — do not author from memory. Key rules: derive all blocks from the real diff only; use diff blocks with line-anchored annotations on key hunks; for UI changes include realistic, non-empty WireframeBlock before/after in a Columns block (labels: Before / After) with visible product text/controls; if canvas.mdx is present, DesignBoard artboards must use Screen html/data.html wireframes, never fresh nested kit-tree children such as FrameScreen/Card/Row/Btn; use .diagram-* primitives and --wf-* tokens in diagrams (no hex/rgb/hsl, no custom fonts); keep API endpoint blocks in single-column flow unless it is an explicit before/after contract comparison.",
248
+ "Recap source files. Call the get-plan-blocks tool FIRST for the authoritative block catalog, visual frame guidance, authoring rules, and style tokens — do not author from memory. Key rules: derive all blocks from the real diff only; use diff blocks with line-anchored annotations on key hunks; for UI changes include realistic, non-empty WireframeBlock before/after in a Columns block (labels: Before / After) with visible product text/controls; if canvas.mdx is present, DesignBoard artboards must use Screen html/data.html wireframes, never fresh nested kit-tree children such as FrameScreen/Card/Row/Btn; use .diagram-* primitives and --wf-* tokens in diagrams (no hex/rgb/hsl, no custom fonts); keep API endpoint blocks in single-column flow unless it is an explicit before/after contract comparison.",
249
249
  ),
250
250
  }),
251
251
  publicAgent: {
@@ -33,6 +33,8 @@ const AUTHORING_RULES_NOTE = `
33
33
 
34
34
  **Wireframes**: set \`data.html\` to a semantic HTML fragment; pick a surface (desktop/mobile/popover/panel/browser). The renderer owns theme, footprint/aspect, Excalifont, and rough.js sketch overlay. Use \`--wf-*\` CSS tokens for any custom color (never hex). Prototype screens use semantic HTML with \`data-goto\` attributes for navigation.
35
35
 
36
+ **Visual frames**: \`wireframe\` and \`diagram\` data accept \`frame: "auto" | "show" | "hide"\`. Leave it unset/\`auto\` when the host context should decide: Plan and recap surfaces default to framed; docs default to unframed. Use \`frame: "show"\` for standalone product screens, before/after recap comparisons, screenshot-like artifacts, and visuals that need containment from surrounding prose. Use \`frame: "hide"\` when a docs page, tab, column, card, canvas artboard, or the visual's own internal chrome already supplies the boundary. Hiding the outer frame must not remove inner padding, meaningful card/field/button borders, or the visual's readable structure.
37
+
36
38
  **Canvas storyboards**: if the user asks for a canvas, storyboard, wireframe, light storyboard, UI flow, screen flow, product flow, mockup, or visual comparison, the primary artifact must be \`content.canvas\` / \`canvas.mdx\` with \`DesignBoard\` artboards containing \`Screen\` HTML wireframes. Each canvas \`Screen\` must carry \`html\` / \`data.html\`; never author fresh nested kit-tree children such as \`<FrameScreen>\`, \`<Card>\`, \`<Row>\`, or \`<Btn>\` inside canvas \`<Screen>\` tags. Kit trees are old-plan compatibility only and often render worse on the pan/zoom canvas than HTML wireframes. Do not use document-body \`diagram\` blocks for the primary UI story. Use \`diagram\` only for architecture, data flow, or implementation mechanics below the canvas, and only after the UI storyboard exists.
37
39
 
38
40
  **Before/After columns**: compose a \`columns\` block from \`<Column>\` CHILDREN — never a \`columns=\` attribute or inline JSON array. Author it as \`<Columns><Column label="Before">…child block(s)…</Column><Column label="After">…child block(s)…</Column></Columns>\`. Each \`<Column>\` wraps real nested blocks (e.g. a \`Wireframe\`); the parser fills in column ids and child-block \`data\` from that markup, whereas a \`columns=\` attribute array leaves them missing and FAILS schema validation. For UI state comparisons put one \`wireframe\` block in each side and label the columns \`Before\` and \`After\`; the renderer draws labels as headings and lays narrow surfaces side by side. Never bake Before/After labels inside the wireframe HTML or hand-stack the pair.
@@ -165,10 +165,12 @@ export function createPlanBlockRenderContext(options: {
165
165
  editingDisabled?: boolean;
166
166
  showCodeAnnotationOverlays?: boolean;
167
167
  codeAnnotationLayout?: BlockRenderContext["codeAnnotationLayout"];
168
+ visualFrame?: BlockRenderContext["visualFrame"];
168
169
  }): BlockRenderContext {
169
170
  const ctx: BlockRenderContext & PlanBlockRenderContextExtras = {
170
171
  dialect: "gfm",
171
172
  textDirection: options.textDirection,
173
+ visualFrame: options.visualFrame ?? "show",
172
174
  showCodeAnnotationOverlays: options.showCodeAnnotationOverlays,
173
175
  codeAnnotationLayout: options.codeAnnotationLayout,
174
176
  onQuestionFormSubmit: options.onVisualQuestionsSubmit,
@@ -121,10 +121,12 @@ export function Wireframe({
121
121
  selectedDesignElementKey?: string | null;
122
122
  onDesignElementSelect?: (selection: DesignElementSelection) => void;
123
123
  }) {
124
+ const showFrame = "frame" in data && data.frame === "hide" ? false : true;
124
125
  if (isHtmlData(data)) {
125
126
  return (
126
127
  <HtmlArtboard
127
128
  data={data}
129
+ showFrame={showFrame}
128
130
  compact={compact}
129
131
  canvasSize={canvasSize}
130
132
  canvasWidth={canvasWidth}
@@ -140,6 +142,7 @@ export function Wireframe({
140
142
  return (
141
143
  <KitWireframe
142
144
  data={data}
145
+ showFrame={showFrame}
143
146
  compact={compact}
144
147
  canvasSize={canvasSize}
145
148
  canvasWidth={canvasWidth}
@@ -167,6 +170,7 @@ function ArtboardFrame({
167
170
  skeleton,
168
171
  renderMode,
169
172
  roughOverlay = true,
173
+ showFrame = true,
170
174
  selector,
171
175
  caption,
172
176
  render,
@@ -178,6 +182,7 @@ function ArtboardFrame({
178
182
  skeleton?: boolean;
179
183
  renderMode?: "wireframe" | "design";
180
184
  roughOverlay?: boolean;
185
+ showFrame?: boolean;
181
186
  selector: string;
182
187
  caption?: string;
183
188
  render: (ctx: {
@@ -250,6 +255,7 @@ function ArtboardFrame({
250
255
  ref={ref}
251
256
  className="plan-kit-artboard relative"
252
257
  data-rough-scope="wireframe"
258
+ data-frame={showFrame ? "show" : "hide"}
253
259
  style={{
254
260
  width,
255
261
  height,
@@ -273,7 +279,7 @@ function ArtboardFrame({
273
279
  are never cut, and a skeleton frame still reads as a frame). Sketchy
274
280
  mode gets its frame from the rough overlay unless the caller needs
275
281
  all borders to stay in the normal scrolling DOM. */}
276
- {!roughEnabled && (
282
+ {!roughEnabled && showFrame && (
277
283
  <div
278
284
  className="pointer-events-none absolute inset-0"
279
285
  style={{
@@ -285,6 +291,7 @@ function ArtboardFrame({
285
291
  <RoughOverlay
286
292
  scopeRef={ref}
287
293
  enabled={roughEnabled}
294
+ drawFrame={showFrame}
288
295
  frameRadius={preset.radius}
289
296
  selector={selector}
290
297
  />
@@ -330,6 +337,7 @@ function WireframeStyleToggleButton() {
330
337
 
331
338
  function HtmlArtboard({
332
339
  data,
340
+ showFrame,
333
341
  compact,
334
342
  canvasSize,
335
343
  canvasWidth,
@@ -340,6 +348,7 @@ function HtmlArtboard({
340
348
  onDesignElementSelect,
341
349
  }: {
342
350
  data: PlanWireframeBlock["data"];
351
+ showFrame: boolean;
343
352
  compact?: boolean;
344
353
  canvasSize?: number;
345
354
  canvasWidth?: number;
@@ -478,6 +487,7 @@ function HtmlArtboard({
478
487
  skeleton={data.skeleton}
479
488
  renderMode={renderMode}
480
489
  roughOverlay={!interactive}
490
+ showFrame={showFrame}
481
491
  selector={HTML_ROUGH_SELECTOR}
482
492
  caption={data.caption}
483
493
  render={({ theme, style }) => (
@@ -486,6 +496,7 @@ function HtmlArtboard({
486
496
  className="plan-html-frame"
487
497
  data-theme={theme}
488
498
  data-style={style}
499
+ data-frame={showFrame ? "show" : "hide"}
489
500
  data-render-mode={renderMode}
490
501
  data-plan-design-scope={scopeId}
491
502
  data-skeleton={data.skeleton ? "true" : undefined}
@@ -531,11 +542,13 @@ export function KitWireframePreview({
531
542
 
532
543
  function KitWireframe({
533
544
  data,
545
+ showFrame = data.frame !== "hide",
534
546
  compact,
535
547
  canvasSize,
536
548
  canvasWidth,
537
549
  }: {
538
550
  data: PlanWireframeBlock["data"];
551
+ showFrame?: boolean;
539
552
  compact?: boolean;
540
553
  canvasSize?: number;
541
554
  canvasWidth?: number;
@@ -547,6 +560,7 @@ function KitWireframe({
547
560
  canvasSize={canvasSize}
548
561
  canvasWidth={canvasWidth}
549
562
  skeleton={data.skeleton}
563
+ showFrame={showFrame}
550
564
  selector="[data-rough]"
551
565
  caption={data.caption}
552
566
  render={({ theme, style }) => (
@@ -588,6 +602,7 @@ export function SketchDiagram({
588
602
  compact?: boolean;
589
603
  }) {
590
604
  const t = useT();
605
+ const showFrame = data.frame !== "hide";
591
606
  if (data.html?.trim()) {
592
607
  return <HtmlDiagram data={data} compact={compact} />;
593
608
  }
@@ -599,6 +614,7 @@ export function SketchDiagram({
599
614
  data={data}
600
615
  compact={compact}
601
616
  markerId={markerId}
617
+ showFrame={showFrame}
602
618
  />
603
619
  );
604
620
  }
@@ -613,7 +629,14 @@ export function SketchDiagram({
613
629
  );
614
630
  }
615
631
  return (
616
- <div className="plan-sketch rounded-[16px] border border-plan-line bg-plan-wireframe p-5">
632
+ <div
633
+ className={cn(
634
+ "plan-sketch",
635
+ showFrame
636
+ ? "rounded-[16px] border border-plan-line bg-plan-wireframe p-5"
637
+ : "p-0",
638
+ )}
639
+ >
617
640
  <div
618
641
  className={cn(
619
642
  "flex gap-3 overflow-x-auto pb-2",
@@ -684,6 +707,7 @@ function HtmlDiagram({
684
707
  const { resolvedTheme } = useTheme();
685
708
  const theme: "light" | "dark" = resolvedTheme === "dark" ? "dark" : "light";
686
709
  const style = useWireframeStyle();
710
+ const showFrame = data.frame !== "hide";
687
711
  const scopeId = useId().replace(/[^a-zA-Z0-9_-]/g, "");
688
712
  const scopeSelector = `[data-plan-diagram-scope="${scopeId}"]`;
689
713
  const safeHtml = useMemo(() => sanitizeDiagramHtml(data.html), [data.html]);
@@ -701,6 +725,7 @@ function HtmlDiagram({
701
725
  className="plan-diagram-frame"
702
726
  data-theme={theme}
703
727
  data-style={style}
728
+ data-frame={showFrame ? "show" : "hide"}
704
729
  data-plan-diagram-scope={scopeId}
705
730
  >
706
731
  {scopedCss && <style>{scopedCss}</style>}
@@ -712,7 +737,7 @@ function HtmlDiagram({
712
737
  <RoughOverlay
713
738
  scopeRef={ref}
714
739
  enabled={style === "sketchy"}
715
- drawFrame={false}
740
+ drawFrame={showFrame}
716
741
  selector={DIAGRAM_ROUGH_SELECTOR}
717
742
  />
718
743
  {data.caption && !compact && (
@@ -726,10 +751,12 @@ function PositionedSketchDiagram({
726
751
  data,
727
752
  compact,
728
753
  markerId,
754
+ showFrame,
729
755
  }: {
730
756
  data: PlanDiagramBlock["data"];
731
757
  compact?: boolean;
732
758
  markerId: string;
759
+ showFrame: boolean;
733
760
  }) {
734
761
  const nodes = (data.nodes ?? []).map((node) => ({
735
762
  ...node,
@@ -743,7 +770,14 @@ function PositionedSketchDiagram({
743
770
  const canvasHeight = compact ? 280 : 430;
744
771
 
745
772
  return (
746
- <div className="plan-sketch rounded-[16px] border border-plan-line bg-plan-wireframe p-5">
773
+ <div
774
+ className={cn(
775
+ "plan-sketch",
776
+ showFrame
777
+ ? "rounded-[16px] border border-plan-line bg-plan-wireframe p-5"
778
+ : "p-0",
779
+ )}
780
+ >
747
781
  <div
748
782
  className="relative overflow-hidden rounded-xl border border-plan-line bg-plan-document"
749
783
  style={{ minHeight: canvasHeight }}