@agent-native/core 0.84.53 → 0.84.55

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 (83) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +13 -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 +198 -146
  6. package/corpus/core/src/client/agent-chat-adapter.ts +14 -4
  7. package/corpus/core/src/client/blocks/library/diagram.config.ts +13 -2
  8. package/corpus/core/src/client/blocks/library/diagram.tsx +27 -3
  9. package/corpus/core/src/client/blocks/library/server-specs.ts +2 -2
  10. package/corpus/core/src/client/blocks/library/wireframe.config.ts +7 -1
  11. package/corpus/core/src/client/blocks/library/wireframe.tsx +42 -3
  12. package/corpus/core/src/client/blocks/types.ts +9 -0
  13. package/corpus/core/src/client/chat/message-components.tsx +6 -2
  14. package/corpus/core/src/client/sse-event-processor.ts +11 -4
  15. package/corpus/core/src/client/use-agent-engine-configured.ts +54 -12
  16. package/corpus/core/src/server/agent-chat-plugin.ts +9 -5
  17. package/corpus/core/src/styles/agent-native.css +44 -0
  18. package/corpus/core/src/styles/blocks.css +18 -0
  19. package/corpus/templates/plan/.agents/skills/visual-plan/references/document-quality.md +10 -6
  20. package/corpus/templates/plan/.agents/skills/visual-plan/references/wireframe.md +20 -9
  21. package/corpus/templates/plan/.agents/skills/visual-recap/SKILL.md +4 -1
  22. package/corpus/templates/plan/.agents/skills/visual-recap/references/wireframe.md +20 -9
  23. package/corpus/templates/plan/actions/create-ui-plan.ts +1 -1
  24. package/corpus/templates/plan/actions/create-visual-plan.ts +1 -1
  25. package/corpus/templates/plan/actions/create-visual-recap.ts +1 -1
  26. package/corpus/templates/plan/actions/get-plan-blocks.ts +2 -0
  27. package/corpus/templates/plan/app/components/plan/planBlocks.tsx +2 -0
  28. package/corpus/templates/plan/app/components/plan/wireframe/Wireframe.tsx +38 -4
  29. package/corpus/templates/plan/app/components/plan/wireframe/html-artboard.css +18 -0
  30. package/corpus/templates/plan/app/global.css +1 -2
  31. package/corpus/templates/plan/changelog/2026-07-02-plan-chat-keeps-the-composer-anchored-when-connect-ai-appears.md +6 -0
  32. package/corpus/templates/plan/changelog/2026-07-02-wireframe-and-diagram-blocks-can-now-show-or-hide-their-outer-frame.md +6 -0
  33. package/corpus/templates/plan/server/plan-mdx.ts +3 -0
  34. package/corpus/templates/plan/shared/plan-content.ts +8 -0
  35. package/dist/cli/skills.d.ts +3 -3
  36. package/dist/cli/skills.d.ts.map +1 -1
  37. package/dist/cli/skills.js +34 -16
  38. package/dist/cli/skills.js.map +1 -1
  39. package/dist/client/AssistantChat.d.ts.map +1 -1
  40. package/dist/client/AssistantChat.js +65 -45
  41. package/dist/client/AssistantChat.js.map +1 -1
  42. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  43. package/dist/client/agent-chat-adapter.js +12 -2
  44. package/dist/client/agent-chat-adapter.js.map +1 -1
  45. package/dist/client/blocks/library/diagram.config.d.ts +3 -1
  46. package/dist/client/blocks/library/diagram.config.d.ts.map +1 -1
  47. package/dist/client/blocks/library/diagram.config.js +10 -1
  48. package/dist/client/blocks/library/diagram.config.js.map +1 -1
  49. package/dist/client/blocks/library/diagram.d.ts.map +1 -1
  50. package/dist/client/blocks/library/diagram.js +13 -7
  51. package/dist/client/blocks/library/diagram.js.map +1 -1
  52. package/dist/client/blocks/library/server-specs.js +2 -2
  53. package/dist/client/blocks/library/server-specs.js.map +1 -1
  54. package/dist/client/blocks/library/wireframe.config.d.ts +3 -1
  55. package/dist/client/blocks/library/wireframe.config.d.ts.map +1 -1
  56. package/dist/client/blocks/library/wireframe.config.js +4 -0
  57. package/dist/client/blocks/library/wireframe.config.js.map +1 -1
  58. package/dist/client/blocks/library/wireframe.d.ts.map +1 -1
  59. package/dist/client/blocks/library/wireframe.js +20 -9
  60. package/dist/client/blocks/library/wireframe.js.map +1 -1
  61. package/dist/client/blocks/types.d.ts +8 -0
  62. package/dist/client/blocks/types.d.ts.map +1 -1
  63. package/dist/client/blocks/types.js.map +1 -1
  64. package/dist/client/chat/message-components.d.ts.map +1 -1
  65. package/dist/client/chat/message-components.js +6 -2
  66. package/dist/client/chat/message-components.js.map +1 -1
  67. package/dist/client/sse-event-processor.d.ts.map +1 -1
  68. package/dist/client/sse-event-processor.js +9 -4
  69. package/dist/client/sse-event-processor.js.map +1 -1
  70. package/dist/client/use-agent-engine-configured.d.ts +10 -1
  71. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  72. package/dist/client/use-agent-engine-configured.js +35 -10
  73. package/dist/client/use-agent-engine-configured.js.map +1 -1
  74. package/dist/collab/routes.d.ts +1 -1
  75. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  76. package/dist/notifications/routes.d.ts +3 -3
  77. package/dist/observability/routes.d.ts +4 -4
  78. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  79. package/dist/server/agent-chat-plugin.js +9 -2
  80. package/dist/server/agent-chat-plugin.js.map +1 -1
  81. package/dist/styles/agent-native.css +44 -0
  82. package/dist/styles/blocks.css +18 -0
  83. package/package.json +1 -1
package/corpus/README.md CHANGED
@@ -28,4 +28,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
28
28
  ## Generated Counts
29
29
 
30
30
  - core files: 2044
31
- - template files: 5001
31
+ - template files: 5003
@@ -1,5 +1,18 @@
1
1
  # @agent-native/core
2
2
 
3
+ ## 0.84.55
4
+
5
+ ### Patch Changes
6
+
7
+ - ea97dc1: Make agent chat setup, auth, and title state resilient to transient status checks and hidden context payloads.
8
+
9
+ ## 0.84.54
10
+
11
+ ### Patch Changes
12
+
13
+ - a259f96: Keep centered chat composers stable when the Connect AI setup card appears.
14
+ - df3fcfd: Allow wireframe and diagram blocks to opt in or out of their outer visual frame.
15
+
3
16
  ## 0.84.53
4
17
 
5
18
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.84.53",
3
+ "version": "0.84.55",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -858,15 +858,26 @@ occurrence. The result is re-sanitized. In local-files privacy mode, do not call
858
858
  hosted Plan tools; edit the local MDX source directly and rerun the local
859
859
  check/serve or verify command for \`<plan-dir>\`.
860
860
 
861
- **Treat the wireframe border as part of the visible design.** Always wrap HTML
862
- wireframe content in a root container with real inner padding before drawing
863
- cards, fields, pills, labels, or controls. Use at least 14-16px of padding,
864
- \`box-sizing: border-box\`, \`height: 100%\`, and \`gap\` between child rows on the
865
- root node itself so the first row never sits flush against the screen border. Do
866
- not rely on padding on a nested page section as the first visible inset; the
867
- outermost element must create the breathing room. Keep text away from borders:
868
- every container, field, button, menu item, and annotation needs enough padding
869
- and line-height to read cleanly in the rendered Plan view.
861
+ **Choose the outer frame deliberately.** Wireframe and diagram data accept
862
+ \`frame: "auto" | "show" | "hide"\` in block data (\`<Screen frame="hide">\` in
863
+ MDX wireframes, \`<Diagram frame="hide">\` for MDX diagrams). Leave it unset or
864
+ \`auto\` when the host context should decide: Plan and recap surfaces default to a
865
+ drawn outer frame; docs surfaces default to no outer frame. Use \`show\` for
866
+ standalone product screens, before/after recap comparisons, screenshot-like
867
+ artifacts, and visuals that need containment from surrounding prose. Use \`hide\`
868
+ when a docs page, tab, column, card, canvas artboard, or the visual's own
869
+ internal chrome already supplies the boundary. Do not use \`hide\` to compensate
870
+ for cramped content; fix the layout instead.
871
+
872
+ **Inner padding and borders still matter.** Always wrap HTML wireframe content
873
+ in a root container with real inner padding before drawing cards, fields, pills,
874
+ labels, or controls. Use at least 14-16px of padding, \`box-sizing: border-box\`,
875
+ \`height: 100%\`, and \`gap\` between child rows on the root node itself so the
876
+ first row never sits flush against the screen edge. Do not rely on padding on a
877
+ nested page section as the first visible inset; the outermost element must
878
+ create the breathing room. Keep text away from borders: every container, field,
879
+ button, menu item, and annotation needs enough padding and line-height to read
880
+ cleanly in the rendered Plan view.
870
881
 
871
882
  **For feature-cloud or abundance visuals, optimize the composition over line-by-line
872
883
  reading.** Some marketing/product sections need to feel like a large surface area
@@ -1244,12 +1255,16 @@ so you never emit a block the editor cannot render or round-trip:
1244
1255
  \`--wf-paper\`, \`--wf-card\`, \`--wf-accent\`, \`--wf-accent-soft\`, \`--wf-warn\`, and
1245
1256
  \`--wf-ok\`, and switch to Excalifont plus rough.js outlines in sketchy mode. Do not
1246
1257
  set \`font-family\` and do not hard-code hex, rgb, or hsl colors in diagram HTML
1247
- or CSS. Leave room for the sketch font: keep labels short, give nodes generous
1248
- width, and place boundary/annotation labels in unused space instead of over
1249
- nodes; labels must not overlap nodes, connectors, or each other. For small
1250
- text/SVG changes to an existing HTML diagram, use \`patch-diagram-html\` with a
1251
- unique \`find\`/\`replace\` snippet instead of resending the whole \`data.html\`
1252
- string. Use legacy \`nodes\` / \`edges\` only for small previews or truly
1258
+ or CSS. Choose the outer \`frame\` intentionally: use \`show\` when the diagram
1259
+ stands alone in a recap, comparison, or prose section; use \`hide\` when the
1260
+ diagram sits inside docs chrome, columns, tabs, cards, a canvas surface, or
1261
+ already has visible \`.diagram-panel\` / \`.diagram-box\` structure. Leave room
1262
+ for the sketch font: keep labels short, give nodes generous width, and place
1263
+ boundary/annotation labels in unused space instead of over nodes; labels must
1264
+ not overlap nodes, connectors, or each other. For small text/SVG changes to an
1265
+ existing HTML diagram, use \`patch-diagram-html\` with a unique
1266
+ \`find\`/\`replace\` snippet instead of resending the whole \`data.html\` string.
1267
+ Use legacy \`nodes\` / \`edges\` only for small previews or truly
1253
1268
  sequential flows. In architecture/code plans, prefer a repeated section rhythm:
1254
1269
  recommendation title, confidence and category badges, code-path evidence, a
1255
1270
  local before/after or current/target spatial diagram, then concise
@@ -2426,7 +2441,10 @@ tags — resolve every conceptual name to its exact tag + prop schema with the
2426
2441
  Author diagram HTML/CSS with the renderer-owned \`.diagram-*\` primitives
2427
2442
  (\`.diagram-panel\`, \`.diagram-node\`, \`.diagram-pill\`, \`[data-rough]\`, …) and
2428
2443
  the same \`--wf-*\` theme tokens \`references/wireframe.md\` defines — never
2429
- \`font-family\`, hex, rgb/hsl literals, or one-off dark/light palettes.
2444
+ \`font-family\`, hex, rgb/hsl literals, or one-off dark/light palettes. Choose
2445
+ the outer \`frame\` intentionally: recap diagrams usually benefit from
2446
+ \`frame: "show"\` when they stand alone, but use \`frame: "hide"\` when columns,
2447
+ tabs, a card, or the diagram's own panels already provide the boundary.
2430
2448
  - **Outcome-first narrative** → \`rich-text\` for the "what changed and why" prose:
2431
2449
  the objective the diff served, the key decisions visible in it, and the risks a
2432
2450
  reviewer should weigh. This is the only place the model writes freely.
@@ -89,6 +89,7 @@ import {
89
89
  AssistantMessage,
90
90
  SelectionAttachedPill,
91
91
  RunningActivityStatus,
92
+ displayableUserMessageText,
92
93
  } from "./chat/message-components.js";
93
94
  import {
94
95
  repoHasAssistantMessage,
@@ -178,6 +179,8 @@ export {
178
179
 
179
180
  export { displayableUserMessageText } from "./chat/message-components.js";
180
181
 
182
+ type AuthSessionCheckResult = "available" | "missing" | "unknown";
183
+
181
184
  const useBrowserLayoutEffect =
182
185
  typeof window === "undefined" ? useEffect : useLayoutEffect;
183
186
 
@@ -492,13 +495,14 @@ function getMessageText(message: unknown): string {
492
495
  const msg = (message as { message?: unknown })?.message ?? message;
493
496
  const content = (msg as { content?: unknown })?.content;
494
497
  if (Array.isArray(content)) {
495
- return content
496
- .filter((p: any) => p?.type === "text" && typeof p.text === "string")
497
- .map((p: any) => p.text)
498
- .join("\n")
499
- .trim();
498
+ return displayableUserMessageText(
499
+ content
500
+ .filter((p: any) => p?.type === "text" && typeof p.text === "string")
501
+ .map((p: any) => p.text)
502
+ .join("\n"),
503
+ );
500
504
  }
501
- return typeof content === "string" ? content.trim() : "";
505
+ return typeof content === "string" ? displayableUserMessageText(content) : "";
502
506
  }
503
507
 
504
508
  function contentPartFollowKey(part: any): string {
@@ -1263,6 +1267,7 @@ const AssistantChatInner = forwardRef<
1263
1267
  }, [threadRuntime]);
1264
1268
  const agentEngineConfigured = useAgentEngineConfigured(
1265
1269
  providerStatusChecksEnabled,
1270
+ { tabId, threadId },
1266
1271
  );
1267
1272
  const missingApiKey = agentEngineConfigured.missing;
1268
1273
  const isComposerDisabled = missingApiKey || composerDisabled;
@@ -1294,10 +1299,19 @@ const AssistantChatInner = forwardRef<
1294
1299
  setComposerError(LLM_MISSING_CREDENTIALS_MESSAGE);
1295
1300
  setMissingKeyBouncePulse((p) => p + 1);
1296
1301
  if (typeof window !== "undefined") {
1297
- window.dispatchEvent(new Event("agent-chat:missing-api-key"));
1302
+ window.dispatchEvent(
1303
+ new CustomEvent("agent-chat:missing-api-key", {
1304
+ detail: { tabId, threadId },
1305
+ }),
1306
+ );
1298
1307
  }
1299
1308
  return false;
1300
- }, [agentEngineConfigured.state, providerStatusChecksEnabled]);
1309
+ }, [
1310
+ agentEngineConfigured.state,
1311
+ providerStatusChecksEnabled,
1312
+ tabId,
1313
+ threadId,
1314
+ ]);
1301
1315
  const [authError, setAuthError] = useState<{
1302
1316
  sessionExpired?: boolean;
1303
1317
  } | null>(null);
@@ -2443,23 +2457,31 @@ const AssistantChatInner = forwardRef<
2443
2457
  }, []);
2444
2458
 
2445
2459
  // Listen for auth error events from the adapter
2446
- const checkAuthSession = useCallback(async () => {
2447
- try {
2448
- const res = await fetch(agentNativePath("/_agent-native/auth/session"), {
2449
- cache: "no-store",
2450
- });
2451
- if (!res.ok) return false;
2452
- const data = await res.json().catch(() => null);
2453
- const hasSession = !!data && !data.error;
2454
- setAuthSessionAvailable(hasSession);
2455
- if (hasSession) {
2456
- setAuthError(null);
2460
+ const checkAuthSession =
2461
+ useCallback(async (): Promise<AuthSessionCheckResult> => {
2462
+ try {
2463
+ const res = await fetch(
2464
+ agentNativePath("/_agent-native/auth/session"),
2465
+ {
2466
+ cache: "no-store",
2467
+ },
2468
+ );
2469
+ if (!res.ok) {
2470
+ return res.status === 401 || res.status === 403
2471
+ ? "missing"
2472
+ : "unknown";
2473
+ }
2474
+ const data = await res.json().catch(() => null);
2475
+ const hasSession = !!data && !data.error;
2476
+ setAuthSessionAvailable(hasSession);
2477
+ if (hasSession) {
2478
+ setAuthError(null);
2479
+ }
2480
+ return hasSession ? "available" : "missing";
2481
+ } catch {
2482
+ return "unknown";
2457
2483
  }
2458
- return hasSession;
2459
- } catch {
2460
- return false;
2461
- }
2462
- }, []);
2484
+ }, []);
2463
2485
 
2464
2486
  useEffect(() => {
2465
2487
  const handler = (e: Event) => {
@@ -2481,9 +2503,12 @@ const AssistantChatInner = forwardRef<
2481
2503
  ) {
2482
2504
  return;
2483
2505
  }
2484
- setAuthSessionAvailable(false);
2485
- setAuthError({ sessionExpired: detail?.reason === "session-expired" });
2486
- void checkAuthSession();
2506
+ void (async () => {
2507
+ const sessionState = await checkAuthSession();
2508
+ if (sessionState !== "missing") return;
2509
+ setAuthSessionAvailable(false);
2510
+ setAuthError({ sessionExpired: detail?.reason === "session-expired" });
2511
+ })();
2487
2512
  };
2488
2513
  window.addEventListener("agent-chat:auth-error", handler);
2489
2514
  return () => window.removeEventListener("agent-chat:auth-error", handler);
@@ -2499,8 +2524,9 @@ const AssistantChatInner = forwardRef<
2499
2524
  // symptom we want signal on.
2500
2525
  const stuckCapture = window.setTimeout(() => {
2501
2526
  void (async () => {
2502
- const hasSession = await checkAuthSession();
2503
- if (hasSession) return;
2527
+ const sessionState = await checkAuthSession();
2528
+ if (sessionState === "available") return;
2529
+ if (sessionState !== "missing") return;
2504
2530
  if (!shouldCaptureStuckAuthCard) return;
2505
2531
  captureError(new Error("agent-chat:auth_error_card_stuck"), {
2506
2532
  tags: {
@@ -3452,6 +3478,15 @@ const AssistantChatInner = forwardRef<
3452
3478
  queryKey: ["guided-questions"],
3453
3479
  ...(browserTabId ? { browserTabId } : {}),
3454
3480
  });
3481
+ const hasComposerAccessoryAboveStack = Boolean(
3482
+ composerError ||
3483
+ showComposerSlot ||
3484
+ showCenteredEmptyThreadFooterSlot ||
3485
+ (guidedQuestions && guidedQuestions.length > 0) ||
3486
+ showScrollToBottom ||
3487
+ composerContextItems.length > 0 ||
3488
+ showPlanModeCallout,
3489
+ );
3455
3490
 
3456
3491
  // Human-in-the-loop approvals: when the user approves a paused `needsApproval`
3457
3492
  // tool call, re-issue the turn carrying the call's approval key so the server
@@ -3781,9 +3816,9 @@ const AssistantChatInner = forwardRef<
3781
3816
  <RunningActivityStatus label={runningStatusLabel} />
3782
3817
  )}
3783
3818
  {queuedMessages.map((msg) => {
3784
- const displayText = msg.text
3785
- .replace(/<context>[\s\S]*?<\/context>\n?/g, "")
3786
- .trim();
3819
+ const displayText = displayableUserMessageText(
3820
+ msg.text,
3821
+ );
3787
3822
  return (
3788
3823
  <div
3789
3824
  key={msg.id}
@@ -3877,7 +3912,6 @@ const AssistantChatInner = forwardRef<
3877
3912
  onSwitchToAct={handleSwitchToAct}
3878
3913
  />
3879
3914
  )}
3880
- <SelectionAttachedPill />
3881
3915
  {/* Inline attachment / body-size error */}
3882
3916
  {composerError && (
3883
3917
  <div
@@ -3896,124 +3930,142 @@ const AssistantChatInner = forwardRef<
3896
3930
  </button>
3897
3931
  </div>
3898
3932
  )}
3899
- {missingApiKey &&
3900
- !authError &&
3901
- missingApiKeySetupAboveComposer ? (
3902
- <BuilderSetupCard
3903
- onConnected={handleBuilderConnected}
3904
- bouncePulse={missingKeyBouncePulse}
3905
- layout={missingApiKeySetupLayout}
3906
- />
3907
- ) : null}
3908
- {/* Input area */}
3909
- <AgentComposerFrame
3910
- layoutVariant={composerLayoutVariant}
3911
- className={cn(
3912
- composerAreaClassName,
3913
- missingApiKey && "cursor-pointer",
3914
- isComposerDisabled && "opacity-70",
3915
- )}
3916
- onClick={
3917
- missingApiKey
3918
- ? () => setMissingKeyBouncePulse((p) => p + 1)
3919
- : undefined
3933
+ <div
3934
+ className="agent-composer-stack"
3935
+ data-agent-composer-adjacent-ui={
3936
+ hasComposerAccessoryAboveStack ? "true" : undefined
3920
3937
  }
3921
3938
  >
3922
- <ComposerAttachmentPreviewStrip />
3923
- <TiptapComposer
3924
- focusRef={tiptapRef}
3925
- disabled={isComposerDisabled}
3926
- placeholder={
3939
+ <SelectionAttachedPill />
3940
+ {missingApiKey &&
3941
+ !authError &&
3942
+ missingApiKeySetupAboveComposer ? (
3943
+ <div
3944
+ className="agent-composer-setup-card"
3945
+ data-agent-composer-setup-position="above"
3946
+ >
3947
+ <BuilderSetupCard
3948
+ onConnected={handleBuilderConnected}
3949
+ bouncePulse={missingKeyBouncePulse}
3950
+ layout={missingApiKeySetupLayout}
3951
+ />
3952
+ </div>
3953
+ ) : null}
3954
+ {/* Input area */}
3955
+ <AgentComposerFrame
3956
+ layoutVariant={composerLayoutVariant}
3957
+ className={cn(
3958
+ composerAreaClassName,
3959
+ missingApiKey && "cursor-pointer",
3960
+ isComposerDisabled && "opacity-70",
3961
+ )}
3962
+ onClick={
3927
3963
  missingApiKey
3928
- ? missingApiKeySetupAboveComposer
3929
- ? "Connect AI above to start chatting..."
3930
- : "Connect AI below to start chatting..."
3931
- : composerDisabled
3932
- ? (composerDisabledPlaceholder ??
3933
- "Open Desktop to use this chat.")
3934
- : isRunning
3935
- ? queuedMessages.length > 0
3936
- ? `${queuedMessages.length} queued — send a follow-up...`
3937
- : "Send a follow-up..."
3938
- : composerPlaceholder
3939
- }
3940
- onSubmit={
3941
- isRunning || composerContextItems.length > 0
3942
- ? (text, references, attachments, options) =>
3943
- void addToQueue(
3944
- text,
3945
- undefined,
3946
- references.length > 0 ? references : undefined,
3947
- attachments,
3948
- undefined,
3949
- resolveAssistantChatSubmitIntent({
3950
- isRunning,
3951
- requestedIntent: options?.intent,
3952
- }),
3953
- undefined,
3954
- true,
3955
- )
3964
+ ? () => setMissingKeyBouncePulse((p) => p + 1)
3956
3965
  : undefined
3957
3966
  }
3958
- onSlashCommand={onSlashCommand}
3959
- onBeforeSubmit={ensureAgentEngineReadyForSubmit}
3960
- execMode={execMode}
3961
- onExecModeChange={onExecModeChange}
3962
- planModeDisabled={planModeDisabled}
3963
- planModeDisabledReason={planModeDisabledReason}
3964
- selectedModel={selectedModel ?? defaultModel}
3965
- selectedEffort={selectedEffort}
3966
- availableModels={availableModels}
3967
- onModelChange={onModelChange}
3968
- onEffortChange={onEffortChange}
3969
- imageModelMenu={imageModelMenu}
3970
- onConnectProvider={onConnectProvider}
3971
- toolbarSlot={composerToolbarSlot}
3972
- contextItems={composerContextItems}
3973
- onRemoveContextItem={removeComposerContextItem}
3974
- plusMenuMode={plusMenuMode}
3975
- layoutVariant={composerLayoutVariant}
3976
- providerConnectStatusEnabled={providerStatusChecksEnabled}
3977
- draftScope={threadId || tabId}
3978
- interceptBuildRequestsForBuilder
3979
- onAttachmentError={setComposerError}
3980
- extraActionButton={
3981
- contextXRayEnabled ||
3982
- composerExtraActionButton ||
3983
- showRunningInUI ? (
3984
- <>
3985
- {contextXRayEnabled && (
3986
- <ContextMeter threadId={threadId} />
3987
- )}
3988
- {composerExtraActionButton}
3989
- {showRunningInUI && (
3990
- <Tooltip>
3991
- <TooltipTrigger asChild>
3992
- <button
3993
- type="button"
3994
- onClick={stopActiveRun}
3995
- className="shrink-0 flex h-7 w-7 items-center justify-center rounded-md bg-muted text-foreground hover:bg-muted/80"
3996
- >
3997
- <IconPlayerStop className="h-3.5 w-3.5" />
3998
- </button>
3999
- </TooltipTrigger>
4000
- <TooltipContent>Stop generating</TooltipContent>
4001
- </Tooltip>
4002
- )}
4003
- </>
4004
- ) : undefined
4005
- }
4006
- />
4007
- </AgentComposerFrame>
4008
- {missingApiKey &&
4009
- !authError &&
4010
- !missingApiKeySetupAboveComposer ? (
4011
- <BuilderSetupCard
4012
- onConnected={handleBuilderConnected}
4013
- bouncePulse={missingKeyBouncePulse}
4014
- layout={missingApiKeySetupLayout}
4015
- />
4016
- ) : null}
3967
+ >
3968
+ <ComposerAttachmentPreviewStrip />
3969
+ <TiptapComposer
3970
+ focusRef={tiptapRef}
3971
+ disabled={isComposerDisabled}
3972
+ placeholder={
3973
+ missingApiKey
3974
+ ? missingApiKeySetupAboveComposer
3975
+ ? "Connect AI above to start chatting..."
3976
+ : "Connect AI below to start chatting..."
3977
+ : composerDisabled
3978
+ ? (composerDisabledPlaceholder ??
3979
+ "Open Desktop to use this chat.")
3980
+ : isRunning
3981
+ ? queuedMessages.length > 0
3982
+ ? `${queuedMessages.length} queued — send a follow-up...`
3983
+ : "Send a follow-up..."
3984
+ : composerPlaceholder
3985
+ }
3986
+ onSubmit={
3987
+ isRunning || composerContextItems.length > 0
3988
+ ? (text, references, attachments, options) =>
3989
+ void addToQueue(
3990
+ text,
3991
+ undefined,
3992
+ references.length > 0 ? references : undefined,
3993
+ attachments,
3994
+ undefined,
3995
+ resolveAssistantChatSubmitIntent({
3996
+ isRunning,
3997
+ requestedIntent: options?.intent,
3998
+ }),
3999
+ undefined,
4000
+ true,
4001
+ )
4002
+ : undefined
4003
+ }
4004
+ onSlashCommand={onSlashCommand}
4005
+ onBeforeSubmit={ensureAgentEngineReadyForSubmit}
4006
+ execMode={execMode}
4007
+ onExecModeChange={onExecModeChange}
4008
+ planModeDisabled={planModeDisabled}
4009
+ planModeDisabledReason={planModeDisabledReason}
4010
+ selectedModel={selectedModel ?? defaultModel}
4011
+ selectedEffort={selectedEffort}
4012
+ availableModels={availableModels}
4013
+ onModelChange={onModelChange}
4014
+ onEffortChange={onEffortChange}
4015
+ imageModelMenu={imageModelMenu}
4016
+ onConnectProvider={onConnectProvider}
4017
+ toolbarSlot={composerToolbarSlot}
4018
+ contextItems={composerContextItems}
4019
+ onRemoveContextItem={removeComposerContextItem}
4020
+ plusMenuMode={plusMenuMode}
4021
+ layoutVariant={composerLayoutVariant}
4022
+ providerConnectStatusEnabled={providerStatusChecksEnabled}
4023
+ draftScope={threadId || tabId}
4024
+ interceptBuildRequestsForBuilder
4025
+ onAttachmentError={setComposerError}
4026
+ extraActionButton={
4027
+ contextXRayEnabled ||
4028
+ composerExtraActionButton ||
4029
+ showRunningInUI ? (
4030
+ <>
4031
+ {contextXRayEnabled && (
4032
+ <ContextMeter threadId={threadId} />
4033
+ )}
4034
+ {composerExtraActionButton}
4035
+ {showRunningInUI && (
4036
+ <Tooltip>
4037
+ <TooltipTrigger asChild>
4038
+ <button
4039
+ type="button"
4040
+ onClick={stopActiveRun}
4041
+ className="shrink-0 flex h-7 w-7 items-center justify-center rounded-md bg-muted text-foreground hover:bg-muted/80"
4042
+ >
4043
+ <IconPlayerStop className="h-3.5 w-3.5" />
4044
+ </button>
4045
+ </TooltipTrigger>
4046
+ <TooltipContent>Stop generating</TooltipContent>
4047
+ </Tooltip>
4048
+ )}
4049
+ </>
4050
+ ) : undefined
4051
+ }
4052
+ />
4053
+ </AgentComposerFrame>
4054
+ {missingApiKey &&
4055
+ !authError &&
4056
+ !missingApiKeySetupAboveComposer ? (
4057
+ <div
4058
+ className="agent-composer-setup-card"
4059
+ data-agent-composer-setup-position="below"
4060
+ >
4061
+ <BuilderSetupCard
4062
+ onConnected={handleBuilderConnected}
4063
+ bouncePulse={missingKeyBouncePulse}
4064
+ layout={missingApiKeySetupLayout}
4065
+ />
4066
+ </div>
4067
+ ) : null}
4068
+ </div>
4017
4069
  </div>
4018
4070
  </TextStreamingContext.Provider>
4019
4071
  </ChatRunningContext.Provider>
@@ -1526,6 +1526,18 @@ export function createAgentChatAdapter(
1526
1526
  );
1527
1527
  };
1528
1528
 
1529
+ const dispatchMissingApiKey = () => {
1530
+ if (typeof window === "undefined") return;
1531
+ window.dispatchEvent(
1532
+ new CustomEvent("agent-chat:missing-api-key", {
1533
+ detail: {
1534
+ ...(tabId ? { tabId } : {}),
1535
+ ...(threadId ? { threadId } : {}),
1536
+ },
1537
+ }),
1538
+ );
1539
+ };
1540
+
1529
1541
  const tryRecoverAuthOnce = async (): Promise<boolean> => {
1530
1542
  if (authRecoveryAttempted || abortSignal.aborted) return false;
1531
1543
  authRecoveryAttempted = true;
@@ -2418,9 +2430,7 @@ export function createAgentChatAdapter(
2418
2430
  if (isMissingCredentialMessage(body)) {
2419
2431
  const failure = missingCredentialFailure(body);
2420
2432
  if (typeof window !== "undefined") {
2421
- window.dispatchEvent(
2422
- new Event("agent-chat:missing-api-key"),
2423
- );
2433
+ dispatchMissingApiKey();
2424
2434
  window.dispatchEvent(
2425
2435
  new CustomEvent("agent-chat:run-error", {
2426
2436
  detail: { ...failure.runError, tabId },
@@ -2633,7 +2643,7 @@ export function createAgentChatAdapter(
2633
2643
  if (isMissingCredentialMessage(errMsg)) {
2634
2644
  const failure = missingCredentialFailure(errMsg);
2635
2645
  if (typeof window !== "undefined") {
2636
- window.dispatchEvent(new Event("agent-chat:missing-api-key"));
2646
+ dispatchMissingApiKey();
2637
2647
  window.dispatchEvent(
2638
2648
  new CustomEvent("agent-chat:run-error", {
2639
2649
  detail: { ...failure.runError, tabId },
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
 
3
3
  import { childCodeFenceFields, serializeChildCodeFenceFields } from "../mdx.js";
4
- import type { BlockMdxConfig } from "../types.js";
4
+ import type { BlockMdxConfig, BlockVisualFrame } from "../types.js";
5
5
 
6
6
  /**
7
7
  * Pure (React-free) part of the shared `diagram` block: its data schema and MDX
@@ -45,6 +45,8 @@ export interface DiagramData {
45
45
  html?: string;
46
46
  css?: string;
47
47
  caption?: string;
48
+ /** Outer surface frame. `auto` lets the host choose the right default. */
49
+ frame?: BlockVisualFrame;
48
50
  /**
49
51
  * Legacy compatibility path for older/simple node graphs. New plans should use
50
52
  * `html`/`css` when layout quality matters.
@@ -120,6 +122,8 @@ const diagramNoteSchema = z.object({
120
122
  y: z.number().min(0).max(100).optional(),
121
123
  }) as z.ZodType<DiagramNote>;
122
124
 
125
+ const visualFrameSchema = z.enum(["auto", "show", "hide"]);
126
+
123
127
  /**
124
128
  * The block can be a flexible HTML/SVG fragment or a legacy positional
125
129
  * node/edge/note graph, so it ships a custom `Edit` rather than relying on the
@@ -144,6 +148,7 @@ export const diagramSchema = z
144
148
  })
145
149
  .optional(),
146
150
  caption: z.string().trim().max(600).optional(),
151
+ frame: visualFrameSchema.optional(),
147
152
  nodes: z.array(diagramNodeSchema).max(80).optional(),
148
153
  edges: z.array(diagramEdgeSchema).max(120).optional(),
149
154
  notes: z.array(diagramNoteSchema).max(40).optional(),
@@ -167,7 +172,9 @@ function graphDataForAttr(data: DiagramData): DiagramData | undefined {
167
172
  if (data.edges?.length) graph.edges = data.edges;
168
173
  if (data.notes?.length) graph.notes = data.notes;
169
174
  if (Object.keys(graph).length > 0) return graph;
170
- return hasChildFenceData(data) ? undefined : data;
175
+ if (hasChildFenceData(data)) return undefined;
176
+ const { frame: _frame, ...dataForAttr } = data;
177
+ return Object.keys(dataForAttr).length > 0 ? dataForAttr : undefined;
171
178
  }
172
179
 
173
180
  /**
@@ -185,12 +192,16 @@ export const diagramMdx: BlockMdxConfig<DiagramData> = {
185
192
  | Record<string, unknown>
186
193
  | undefined,
187
194
  caption: data.caption,
195
+ frame: data.frame,
188
196
  }),
189
197
  fromAttrs: (attrs) => ({
190
198
  ...(attrs.object<DiagramData>("data") ?? {}),
191
199
  ...(attrs.string("caption") !== undefined
192
200
  ? { caption: attrs.string("caption") }
193
201
  : {}),
202
+ ...(attrs.string("frame") !== undefined
203
+ ? { frame: attrs.string("frame") as BlockVisualFrame }
204
+ : {}),
194
205
  }),
195
206
  serializeChildren: (data) =>
196
207
  serializeChildCodeFenceFields(data, { html: "html", css: "css" }),