@agent-native/core 0.84.2 → 0.84.4

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 (44) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +14 -0
  3. package/corpus/core/package.json +2 -1
  4. package/corpus/core/src/agent/production-agent.ts +9 -2
  5. package/corpus/core/src/agent/run-loop-with-resume.ts +38 -8
  6. package/corpus/core/src/agent/thread-data-builder.ts +6 -0
  7. package/corpus/core/src/client/agent-chat-adapter.ts +208 -9
  8. package/corpus/core/src/client/sharing/ShareButton.tsx +70 -21
  9. package/corpus/core/src/client/sse-event-processor.ts +15 -0
  10. package/corpus/templates/analytics/AGENTS.md +1 -1
  11. package/corpus/templates/design/app/pages/DesignEditor.tsx +129 -100
  12. package/corpus/templates/design/changelog/2026-06-30-agent-chat-now-reports-saved-design-generations-clearly-when.md +6 -0
  13. package/corpus/templates/design/changelog/2026-06-30-apply-styles-now-appears-only-for-localhost-visual-edit-scre.md +6 -0
  14. package/corpus/templates/design/changelog/2026-06-30-share-options-now-make-export-and-agent-handoff-easier-to-no.md +6 -0
  15. package/corpus/templates/design/changelog/2026-07-01-share-general-access-menu-stays-open-when-choosing-organization.md +6 -0
  16. package/dist/agent/production-agent.d.ts.map +1 -1
  17. package/dist/agent/production-agent.js +9 -2
  18. package/dist/agent/production-agent.js.map +1 -1
  19. package/dist/agent/run-loop-with-resume.d.ts +2 -2
  20. package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
  21. package/dist/agent/run-loop-with-resume.js +31 -8
  22. package/dist/agent/run-loop-with-resume.js.map +1 -1
  23. package/dist/agent/thread-data-builder.d.ts +2 -0
  24. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  25. package/dist/agent/thread-data-builder.js +5 -0
  26. package/dist/agent/thread-data-builder.js.map +1 -1
  27. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  28. package/dist/client/agent-chat-adapter.js +179 -8
  29. package/dist/client/agent-chat-adapter.js.map +1 -1
  30. package/dist/client/sharing/ShareButton.d.ts +6 -0
  31. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  32. package/dist/client/sharing/ShareButton.js +25 -11
  33. package/dist/client/sharing/ShareButton.js.map +1 -1
  34. package/dist/client/sse-event-processor.d.ts +4 -0
  35. package/dist/client/sse-event-processor.d.ts.map +1 -1
  36. package/dist/client/sse-event-processor.js +13 -0
  37. package/dist/client/sse-event-processor.js.map +1 -1
  38. package/dist/collab/routes.d.ts +1 -1
  39. package/dist/notifications/routes.d.ts +1 -1
  40. package/dist/observability/routes.d.ts +5 -5
  41. package/dist/resources/handlers.d.ts +2 -2
  42. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  43. package/dist/server/transcribe-voice.d.ts +1 -1
  44. package/package.json +2 -1
@@ -15,6 +15,7 @@ import {
15
15
  import { useQueryClient } from "@tanstack/react-query";
16
16
  import {
17
17
  useCallback,
18
+ type ComponentPropsWithoutRef,
18
19
  useEffect,
19
20
  useId,
20
21
  useMemo,
@@ -69,6 +70,10 @@ export interface ShareButtonProps {
69
70
  /** Where to render share links in the popover. Defaults to the bottom,
70
71
  * matching the historical Google-Docs-style share dialog. */
71
72
  shareUrlPlacement?: "top" | "bottom";
73
+ /** Whether to render copyable share URL fields. Defaults to true. */
74
+ showShareLinks?: boolean;
75
+ /** Whether to render the bottom Done button. Defaults to true. */
76
+ showDoneButton?: boolean;
72
77
  /** Optional placeholder shown in the share-URL slot when `shareUrl` is
73
78
  * undefined. Use this to explain *why* there's no link yet (e.g. "Publish
74
79
  * this form to get a public response link") instead of leaving the slot
@@ -99,6 +104,8 @@ export interface ShareButtonProps {
99
104
  generalAccessLabel?: ReactNode;
100
105
  /** Optional note rendered between general access and the copyable link. */
101
106
  accessNote?: ReactNode;
107
+ /** Optional host-rendered footer for compact app-specific share actions. */
108
+ shareFooterContent?: ReactNode;
102
109
  /** Optional Notion-style organization access control. When present, the
103
110
  * share panel exposes a "Hide in search" switch under Advanced for org
104
111
  * visibility. */
@@ -171,6 +178,8 @@ const BUTTON_GHOST_ICON = cn(
171
178
  );
172
179
  const SHARE_POPOVER_SURFACE =
173
180
  "border border-border bg-popover text-popover-foreground";
181
+ const SHARE_NESTED_OVERLAY_ATTR = "data-agent-native-share-overlay";
182
+ const SHARE_NESTED_OVERLAY_Z = "z-[100020]";
174
183
  const MEMBER_SUGGESTION_LIMIT = 25;
175
184
  const MEMBER_SEARCH_DEBOUNCE_MS = 140;
176
185
 
@@ -219,6 +228,28 @@ const ROLE_OPTIONS: Array<{ value: Role; label: string; description: string }> =
219
228
  },
220
229
  ];
221
230
 
231
+ type SharePopoverInteractOutsideEvent = Parameters<
232
+ NonNullable<
233
+ ComponentPropsWithoutRef<typeof PopoverContent>["onInteractOutside"]
234
+ >
235
+ >[0];
236
+
237
+ function isShareNestedOverlayTarget(target: EventTarget | null): boolean {
238
+ return (
239
+ target instanceof Element &&
240
+ target.closest(`[${SHARE_NESTED_OVERLAY_ATTR}]`) !== null
241
+ );
242
+ }
243
+
244
+ function handleSharePopoverInteractOutside(
245
+ event: SharePopoverInteractOutsideEvent,
246
+ ) {
247
+ const originalTarget = event.detail.originalEvent.target;
248
+ if (isShareNestedOverlayTarget(originalTarget)) {
249
+ event.preventDefault();
250
+ }
251
+ }
252
+
222
253
  /**
223
254
  * Framework share control. Renders a shadcn-outline-styled trigger that
224
255
  * opens a Google-Docs-style popover anchored beneath it. Uses Tailwind
@@ -344,12 +375,14 @@ export function ShareButton(props: ShareButtonProps) {
344
375
  <PopoverContent
345
376
  align="end"
346
377
  sideOffset={6}
378
+ data-agent-native-share-overlay=""
347
379
  className={cn(
348
380
  "z-[2000] w-[min(460px,92vw)] rounded-lg p-4 shadow-lg",
349
381
  SHARE_POPOVER_SURFACE,
350
382
  props.popoverClassName,
351
383
  )}
352
384
  onOpenAutoFocus={(e) => e.preventDefault()}
385
+ onInteractOutside={handleSharePopoverInteractOutside}
353
386
  >
354
387
  <SharePanel
355
388
  {...props}
@@ -622,9 +655,11 @@ function SharePanel(
622
655
  </>
623
656
  );
624
657
  const showShareLinks =
625
- Boolean(props.shareUrl) ||
626
- Boolean(props.shareUrlPlaceholder) ||
627
- Boolean(props.secondaryShareUrl);
658
+ (props.showShareLinks ?? true) &&
659
+ (Boolean(props.shareUrl) ||
660
+ Boolean(props.shareUrlPlaceholder) ||
661
+ Boolean(props.secondaryShareUrl));
662
+ const showDoneButton = props.showDoneButton ?? true;
628
663
  const shareUrlPlacement = props.shareUrlPlacement ?? "bottom";
629
664
  const extraTabs = props.shareTabs?.tabs ?? [];
630
665
  const hasTabs = extraTabs.length > 0;
@@ -821,11 +856,13 @@ function SharePanel(
821
856
  <div className="mb-4 h-7 rounded-md bg-muted animate-pulse" />
822
857
  <div className="mb-2 text-sm font-semibold">{generalAccessLabel}</div>
823
858
  <div className="mb-4 h-9 rounded-md bg-muted animate-pulse" />
824
- <div className="mt-2 flex justify-end">
825
- <button type="button" onClick={onClose} className={BUTTON_PRIMARY_SM}>
826
- Done
827
- </button>
828
- </div>
859
+ {showDoneButton ? (
860
+ <div className="mt-2 flex justify-end">
861
+ <button type="button" onClick={onClose} className={BUTTON_PRIMARY_SM}>
862
+ Done
863
+ </button>
864
+ </div>
865
+ ) : null}
829
866
  </div>
830
867
  ) : (
831
868
  <div>
@@ -990,15 +1027,19 @@ function SharePanel(
990
1027
 
991
1028
  {showShareLinks && shareUrlPlacement === "bottom" ? shareLinks : null}
992
1029
 
993
- <div className="mt-2 flex justify-end">
994
- <button
995
- type="button"
996
- onClick={handleDone}
997
- className={BUTTON_PRIMARY_SM}
998
- >
999
- Done
1000
- </button>
1001
- </div>
1030
+ {props.shareFooterContent}
1031
+
1032
+ {showDoneButton ? (
1033
+ <div className="mt-2 flex justify-end">
1034
+ <button
1035
+ type="button"
1036
+ onClick={handleDone}
1037
+ className={BUTTON_PRIMARY_SM}
1038
+ >
1039
+ Done
1040
+ </button>
1041
+ </div>
1042
+ ) : null}
1002
1043
  </div>
1003
1044
  );
1004
1045
 
@@ -1076,8 +1117,13 @@ function AdvancedAccessPopover({
1076
1117
  <PopoverContent
1077
1118
  align="start"
1078
1119
  sideOffset={6}
1120
+ data-agent-native-share-overlay=""
1079
1121
  onOpenAutoFocus={(event) => event.preventDefault()}
1080
- className={cn("z-[2300] w-72 p-3 shadow-lg", SHARE_POPOVER_SURFACE)}
1122
+ className={cn(
1123
+ SHARE_NESTED_OVERLAY_Z,
1124
+ "w-72 p-3 shadow-lg",
1125
+ SHARE_POPOVER_SURFACE,
1126
+ )}
1081
1127
  >
1082
1128
  <div className="space-y-3">
1083
1129
  <div>
@@ -1291,9 +1337,11 @@ function MemberAutocomplete({
1291
1337
  <PopoverContent
1292
1338
  align="start"
1293
1339
  sideOffset={4}
1340
+ data-agent-native-share-overlay=""
1294
1341
  onOpenAutoFocus={(event) => event.preventDefault()}
1295
1342
  className={cn(
1296
- "z-[2200] w-[var(--radix-popper-anchor-width)] min-w-[18rem] rounded-md p-1 shadow-lg",
1343
+ SHARE_NESTED_OVERLAY_Z,
1344
+ "w-[var(--radix-popper-anchor-width)] min-w-[18rem] rounded-md p-1 shadow-lg",
1297
1345
  SHARE_POPOVER_SURFACE,
1298
1346
  )}
1299
1347
  >
@@ -1441,8 +1489,7 @@ function CopyLinkField({
1441
1489
  // Radix Select wrappers styled like shadcn Select (no native <select> anywhere)
1442
1490
  // ---------------------------------------------------------------------------
1443
1491
 
1444
- const selectContentClass =
1445
- "z-[2100] min-w-[12rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0";
1492
+ const selectContentClass = `${SHARE_NESTED_OVERLAY_Z} min-w-[12rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0`;
1446
1493
  const selectItemClass =
1447
1494
  "relative flex w-full cursor-pointer select-none items-start gap-2 rounded-sm py-2 ps-8 pe-3 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
1448
1495
 
@@ -1517,6 +1564,7 @@ function RoleSelect(props: {
1517
1564
  </Select.Trigger>
1518
1565
  <Select.Portal>
1519
1566
  <Select.Content
1567
+ data-agent-native-share-overlay=""
1520
1568
  className={selectContentClass}
1521
1569
  position="popper"
1522
1570
  sideOffset={4}
@@ -1563,6 +1611,7 @@ function VisibilitySelect(props: {
1563
1611
  </Select.Trigger>
1564
1612
  <Select.Portal>
1565
1613
  <Select.Content
1614
+ data-agent-native-share-overlay=""
1566
1615
  className={selectContentClass}
1567
1616
  position="popper"
1568
1617
  sideOffset={4}
@@ -18,6 +18,8 @@ export type ContentPart =
18
18
  argsText: string;
19
19
  args: Record<string, string>;
20
20
  result?: string;
21
+ isError?: boolean;
22
+ completedSideEffect?: boolean;
21
23
  mcpApp?: AgentMcpAppPayload;
22
24
  chatUI?: ActionChatUIConfig;
23
25
  activity?: boolean;
@@ -45,6 +47,8 @@ export interface SSEEvent {
45
47
  label?: string;
46
48
  input?: Record<string, string>;
47
49
  result?: string;
50
+ isError?: boolean;
51
+ completedSideEffect?: boolean;
48
52
  mcpApp?: AgentMcpAppPayload;
49
53
  chatUI?: ActionChatUIConfig;
50
54
  /** Stable key the client echoes back in `approvedToolCalls` to approve a
@@ -525,6 +529,10 @@ export function processEvent(
525
529
  const part = content[doneIdx];
526
530
  if (part.type === "tool-call") {
527
531
  part.result = ev.result ?? "";
532
+ if (ev.isError !== undefined) part.isError = ev.isError;
533
+ if (ev.completedSideEffect !== undefined) {
534
+ part.completedSideEffect = ev.completedSideEffect;
535
+ }
528
536
  if (ev.mcpApp) part.mcpApp = ev.mcpApp;
529
537
  if (ev.chatUI) part.chatUI = ev.chatUI;
530
538
  }
@@ -851,6 +859,13 @@ export async function* readSSEStream(
851
859
  label: runningToolLabel(tool),
852
860
  tool,
853
861
  });
862
+ } else if (ev.type === "tool_done") {
863
+ const tool = ev.tool ?? "unknown";
864
+ for (let i = activityTrail.length - 1; i >= 0; i--) {
865
+ if (activityTrail[i]?.tool === tool) {
866
+ activityTrail.splice(i, 1);
867
+ }
868
+ }
854
869
  }
855
870
 
856
871
  const { action, result, autoContinue } = processEvent(
@@ -137,7 +137,7 @@ details live in `.agents/skills/`.
137
137
  name the metrics and the server generates the validated SQL/config for every
138
138
  panel in ONE fast call. Do NOT hand-author big `update-dashboard` configs
139
139
  panel-by-panel or loop `update-dashboard` — streaming a giant multi-panel
140
- argument inside the ~40s budget fails and thrashes. Unknown metric keys are
140
+ argument across timeout boundaries fails and thrashes. Unknown metric keys are
141
141
  skipped and reported; per-panel SQL validates independently; existing
142
142
  dashboards append by default (`overwrite: true` replaces). Report the returned
143
143
  `panelCount` as proof-of-done.
@@ -117,6 +117,7 @@ import {
117
117
  IconExternalLink,
118
118
  IconCircleCheck,
119
119
  IconTerminal2,
120
+ IconLink,
120
121
  } from "@tabler/icons-react";
121
122
  import { useQueryClient } from "@tanstack/react-query";
122
123
  import {
@@ -214,7 +215,6 @@ import {
214
215
  } from "@/components/ui/alert-dialog";
215
216
  import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
216
217
  import { Button } from "@/components/ui/button";
217
- import { Checkbox } from "@/components/ui/checkbox";
218
218
  import {
219
219
  Dialog,
220
220
  DialogContent,
@@ -245,7 +245,6 @@ import {
245
245
  } from "@/components/ui/popover";
246
246
  import { Spinner } from "@/components/ui/spinner";
247
247
  import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
248
- import { Textarea } from "@/components/ui/textarea";
249
248
  import {
250
249
  Tooltip,
251
250
  TooltipContent,
@@ -996,6 +995,22 @@ export function formatPendingVisualStylePrompt(args: {
996
995
  .join("\n");
997
996
  }
998
997
 
998
+ export function shouldShowPendingVisualStyleApply(args: {
999
+ edits: readonly PendingVisualStyleEdit[];
1000
+ screenSourceTypes: ReadonlyMap<string, unknown>;
1001
+ fallbackSourceType?: unknown;
1002
+ }): boolean {
1003
+ return (
1004
+ args.edits.length > 0 &&
1005
+ args.edits.every(
1006
+ (edit) =>
1007
+ normalizeDesignSourceType(
1008
+ args.screenSourceTypes.get(edit.screenId) ?? args.fallbackSourceType,
1009
+ ) === "localhost",
1010
+ )
1011
+ );
1012
+ }
1013
+
999
1014
  interface DesignData {
1000
1015
  id: string;
1001
1016
  title: string;
@@ -5155,8 +5170,8 @@ export default function DesignEditor() {
5155
5170
  null,
5156
5171
  );
5157
5172
  const [codingHandoffLoading, setCodingHandoffLoading] = useState(false);
5158
- const [downloadZipInstead, setDownloadZipInstead] = useState(false);
5159
- const [codingHandoffDetail, setCodingHandoffDetail] = useState("");
5173
+ const [shareLinkCopied, setShareLinkCopied] = useState(false);
5174
+ const shareLinkCopiedResetRef = useRef<number | null>(null);
5160
5175
  const [, setPatchProof] = useState<PatchProofState | null>(null);
5161
5176
  const pendingFileSavesRef = useRef<Record<string, FileContentSaveRequest>>(
5162
5177
  {},
@@ -5351,6 +5366,13 @@ export default function DesignEditor() {
5351
5366
  if (!id || typeof window === "undefined") return undefined;
5352
5367
  return getDesignEditorShareUrl(id, window.location.origin, appBasePath());
5353
5368
  }, [id]);
5369
+ useEffect(() => {
5370
+ return () => {
5371
+ if (shareLinkCopiedResetRef.current !== null) {
5372
+ window.clearTimeout(shareLinkCopiedResetRef.current);
5373
+ }
5374
+ };
5375
+ }, []);
5354
5376
  const {
5355
5377
  designSystems,
5356
5378
  defaultSystem,
@@ -12530,17 +12552,13 @@ export default function DesignEditor() {
12530
12552
 
12531
12553
  const getCodingHandoffClipboardText = useCallback(
12532
12554
  (result: CodingHandoffResult | null) => {
12533
- const base =
12534
- typeof result?.clipboardText === "string"
12535
- ? result.clipboardText
12536
- : typeof result?.prompt === "string"
12537
- ? result.prompt
12538
- : "";
12539
- const detail = codingHandoffDetail.trim();
12540
- if (!base || !detail) return base;
12541
- return `${base}\n\nAdditional implementation detail:\n${detail}`;
12555
+ return typeof result?.clipboardText === "string"
12556
+ ? result.clipboardText
12557
+ : typeof result?.prompt === "string"
12558
+ ? result.prompt
12559
+ : "";
12542
12560
  },
12543
- [codingHandoffDetail],
12561
+ [],
12544
12562
  );
12545
12563
 
12546
12564
  const handleCopyCodingHandoff = useCallback(async () => {
@@ -12558,6 +12576,24 @@ export default function DesignEditor() {
12558
12576
  }
12559
12577
  }, [ensureCodingHandoff, getCodingHandoffClipboardText, t]);
12560
12578
 
12579
+ const handleCopyShareLink = useCallback(async () => {
12580
+ if (!editorShareUrl) return;
12581
+ try {
12582
+ await navigator.clipboard.writeText(editorShareUrl);
12583
+ setShareLinkCopied(true);
12584
+ if (shareLinkCopiedResetRef.current !== null) {
12585
+ window.clearTimeout(shareLinkCopiedResetRef.current);
12586
+ }
12587
+ shareLinkCopiedResetRef.current = window.setTimeout(() => {
12588
+ setShareLinkCopied(false);
12589
+ shareLinkCopiedResetRef.current = null;
12590
+ }, 1400);
12591
+ toast.success("Share link copied" /* i18n-ignore share copy toast */);
12592
+ } catch {
12593
+ toast.error(t("designEditor.toasts.clipboardBlocked"));
12594
+ }
12595
+ }, [editorShareUrl, t]);
12596
+
12561
12597
  const hasPendingVisualStyleEdits = pendingVisualStyleEdits.length > 0;
12562
12598
  useBeforeUnload(
12563
12599
  useCallback(
@@ -12597,6 +12633,29 @@ export default function DesignEditor() {
12597
12633
  () => getPendingVisualStylePropertyCount(pendingVisualStyleEdits),
12598
12634
  [pendingVisualStyleEdits],
12599
12635
  );
12636
+ const pendingVisualStyleScreenSourceTypes = useMemo(
12637
+ () =>
12638
+ new Map<string, unknown>(
12639
+ overviewScreens.map((screen) => [
12640
+ screen.id,
12641
+ screen.sourceType ?? designSourceType,
12642
+ ]),
12643
+ ),
12644
+ [designSourceType, overviewScreens],
12645
+ );
12646
+ const showPendingVisualStyleApply = useMemo(
12647
+ () =>
12648
+ shouldShowPendingVisualStyleApply({
12649
+ edits: pendingVisualStyleEdits,
12650
+ screenSourceTypes: pendingVisualStyleScreenSourceTypes,
12651
+ fallbackSourceType: designSourceType,
12652
+ }),
12653
+ [
12654
+ designSourceType,
12655
+ pendingVisualStyleEdits,
12656
+ pendingVisualStyleScreenSourceTypes,
12657
+ ],
12658
+ );
12600
12659
  const pendingVisualStylePrompt = useMemo(
12601
12660
  () =>
12602
12661
  formatPendingVisualStylePrompt({
@@ -12703,22 +12762,6 @@ export default function DesignEditor() {
12703
12762
  });
12704
12763
  }, [exportZipMutation, fallbackExportName, id, t, triggerBlobDownload]);
12705
12764
 
12706
- const handleDownloadHandoffZip = useCallback(async () => {
12707
- const result = await ensureCodingHandoff();
12708
- if (!result?.zipUrl) {
12709
- toast.error(t("designEditor.toasts.zipCreateError"));
12710
- return;
12711
- }
12712
- const a = document.createElement("a");
12713
- a.href = result.zipUrl;
12714
- a.download = fallbackExportName("zip", "agent-handoff");
12715
- a.rel = "noopener";
12716
- document.body.appendChild(a);
12717
- a.click();
12718
- a.remove();
12719
- toast.success(t("designEditor.toasts.zipDownloaded"));
12720
- }, [ensureCodingHandoff, fallbackExportName, t]);
12721
-
12722
12765
  const handleDownloadPng = useCallback(
12723
12766
  async (settings?: Partial<ExportSettingsValue>) => {
12724
12767
  if (pngExportingRef.current) return;
@@ -12925,14 +12968,6 @@ ${serializedHtml}
12925
12968
  [handleDownloadPng, handleDownloadSvg],
12926
12969
  );
12927
12970
 
12928
- const handleSendToPrimaryAction = useCallback(() => {
12929
- if (downloadZipInstead) {
12930
- void handleDownloadHandoffZip();
12931
- return;
12932
- }
12933
- void handleCopyCodingHandoff();
12934
- }, [downloadZipInstead, handleCopyCodingHandoff, handleDownloadHandoffZip]);
12935
-
12936
12971
  const shareExportOptions: Array<{
12937
12972
  value: ShareExportFormat;
12938
12973
  title: string;
@@ -13096,84 +13131,72 @@ ${serializedHtml}
13096
13131
  <div className="flex flex-wrap items-center gap-2">
13097
13132
  <Button
13098
13133
  type="button"
13099
- onClick={handleSendToPrimaryAction}
13100
- disabled={
13101
- downloadZipInstead
13102
- ? !activeFile || codingHandoffLoading
13103
- : codingHandoffLoading
13104
- }
13134
+ onClick={() => void handleCopyCodingHandoff()}
13135
+ disabled={codingHandoffLoading}
13105
13136
  className="h-8 gap-1.5 rounded-md px-3 text-[12px]"
13106
13137
  >
13107
- {downloadZipInstead ? (
13108
- <IconArchive className="size-3.5" />
13109
- ) : (
13110
- <IconClipboard className="size-3.5" />
13111
- )}
13112
- {
13113
- downloadZipInstead
13114
- ? t("designEditor.downloadZip")
13115
- : "Copy agent prompt" /* i18n-ignore share send action */
13116
- }
13138
+ <IconClipboard className="size-3.5" />
13139
+ {"Copy agent prompt" /* i18n-ignore share send action */}
13117
13140
  </Button>
13118
13141
  </div>
13119
-
13120
- <div className="space-y-3">
13121
- <div className="flex items-start gap-2.5">
13122
- <Checkbox
13123
- checked={downloadZipInstead}
13124
- onCheckedChange={(checked) =>
13125
- setDownloadZipInstead(checked === true)
13126
- }
13127
- className="mt-0.5"
13128
- />
13129
- <div className="min-w-0">
13130
- <div className="text-[12px] font-medium text-foreground">
13131
- {"Download zip instead" /* i18n-ignore share send option */}
13132
- </div>
13133
- <div className="mt-0.5 !text-[11px] leading-4 text-muted-foreground">
13134
- {
13135
- "For agents without the Design connector, drop the bundle into your agent's chat manually." /* i18n-ignore share send option description */
13136
- }
13137
- </div>
13138
- </div>
13139
- </div>
13140
-
13141
- <div className="space-y-1.5">
13142
- <label className="text-[12px] font-medium text-foreground">
13143
- {
13144
- "Give the agent more detail on what to implement" /* i18n-ignore share send detail label */
13145
- }{" "}
13146
- <span className="font-normal text-muted-foreground">
13147
- {"(optional)" /* i18n-ignore optional label */}
13148
- </span>
13149
- </label>
13150
- <Textarea
13151
- value={codingHandoffDetail}
13152
- onChange={(event) => setCodingHandoffDetail(event.target.value)}
13153
- placeholder={activeFile?.filename ?? "Add implementation notes..."}
13154
- className="min-h-20 resize-none rounded-md bg-[var(--design-editor-control-bg)] text-[12px]"
13155
- />
13156
- </div>
13157
- </div>
13158
13142
  </div>
13159
13143
  );
13144
+ const shareLinkFooter = (
13145
+ <div className="mt-3 flex flex-wrap items-center gap-2 border-t border-[var(--design-editor-panel-divider-color)] pt-3">
13146
+ <Button
13147
+ type="button"
13148
+ onClick={() => void handleCopyShareLink()}
13149
+ disabled={!editorShareUrl}
13150
+ className="h-8 min-w-[8.75rem] gap-1.5 rounded-md px-3 text-[12px]"
13151
+ >
13152
+ {shareLinkCopied ? (
13153
+ <IconCheck className="size-3.5" />
13154
+ ) : (
13155
+ <IconClipboard className="size-3.5" />
13156
+ )}
13157
+ {
13158
+ shareLinkCopied
13159
+ ? "Copied" /* i18n-ignore share copy action copied */
13160
+ : "Copy share link" /* i18n-ignore share copy action */
13161
+ }
13162
+ </Button>
13163
+ </div>
13164
+ );
13165
+ const designShareTabLabelClassName =
13166
+ "inline-flex items-center justify-center gap-1.5";
13160
13167
  const designSharePopoverClassName =
13161
13168
  "z-[100010] !w-[min(620px,calc(100vw-32px))] !p-3 " +
13162
- "[&_[role=tablist]]:!inline-flex [&_[role=tablist]]:!w-fit [&_[role=tablist]]:!self-start [&_[role=tablist]]:justify-start [&_[role=tablist]]:gap-0.5 [&_[role=tablist]]:rounded-none [&_[role=tablist]]:bg-transparent [&_[role=tablist]]:p-0 " +
13163
- "[&_[role=tab]]:!h-6 [&_[role=tab]]:!flex-none [&_[role=tab]]:rounded-md [&_[role=tab]]:px-2 [&_[role=tab]]:!text-[11px] [&_[role=tab]]:font-semibold [&_[role=tab]]:shadow-none [&_[role=tab]]:ring-0 " +
13164
- "[&_[role=tab]:hover]:bg-[var(--design-editor-panel-raised-bg)] [&_[role=tab]:hover]:text-foreground [&_[role=tab][aria-selected=true]]:bg-[var(--design-editor-panel-raised-bg)] [&_[role=tab][aria-selected=true]]:text-foreground [&_[role=tab][aria-selected=true]]:ring-0";
13169
+ "[&_[role=tablist]]:!inline-flex [&_[role=tablist]]:!w-fit [&_[role=tablist]]:!self-start [&_[role=tablist]]:justify-start [&_[role=tablist]]:gap-1 [&_[role=tablist]]:rounded-lg [&_[role=tablist]]:border [&_[role=tablist]]:border-[var(--design-editor-panel-divider-color)] [&_[role=tablist]]:bg-[var(--design-editor-panel-raised-bg)] [&_[role=tablist]]:p-1 " +
13170
+ "[&_[role=tab]]:!h-8 [&_[role=tab]]:!flex-none [&_[role=tab]]:rounded-md [&_[role=tab]]:px-3 [&_[role=tab]]:!text-[12px] [&_[role=tab]]:font-semibold [&_[role=tab]]:shadow-none [&_[role=tab]]:ring-0 " +
13171
+ "[&_[role=tab]:hover]:bg-white/70 dark:[&_[role=tab]:hover]:bg-[var(--design-editor-control-bg)] [&_[role=tab]:hover]:text-foreground " +
13172
+ "[&_[role=tab][aria-selected=true]]:bg-white dark:[&_[role=tab][aria-selected=true]]:bg-[var(--design-editor-control-bg)] [&_[role=tab][aria-selected=true]]:text-foreground [&_[role=tab][aria-selected=true]]:shadow-sm [&_[role=tab][aria-selected=true]]:ring-1 [&_[role=tab][aria-selected=true]]:ring-[var(--design-editor-control-border)]";
13165
13173
  const designShareTabs = {
13166
- shareLabel: "Share link" /* i18n-ignore share tab label */,
13174
+ shareLabel: (
13175
+ <span className={designShareTabLabelClassName}>
13176
+ <IconLink className="size-3.5" />
13177
+ {"Share link" /* i18n-ignore share tab label */}
13178
+ </span>
13179
+ ),
13167
13180
  defaultValue: "share",
13168
13181
  tabs: [
13169
13182
  {
13170
13183
  value: "export",
13171
- label: t("designEditor.export"),
13184
+ label: (
13185
+ <span className={designShareTabLabelClassName}>
13186
+ <IconFileExport className="size-3.5" />
13187
+ {t("designEditor.export")}
13188
+ </span>
13189
+ ),
13172
13190
  content: shareExportTab,
13173
13191
  },
13174
13192
  {
13175
13193
  value: "send",
13176
- label: "Send to agent" /* i18n-ignore share tab label */,
13194
+ label: (
13195
+ <span className={designShareTabLabelClassName}>
13196
+ <IconTerminal2 className="size-3.5" />
13197
+ {"Send to agent" /* i18n-ignore share tab label */}
13198
+ </span>
13199
+ ),
13177
13200
  content: shareSendToTab,
13178
13201
  },
13179
13202
  ],
@@ -15480,6 +15503,9 @@ ${serializedHtml}
15480
15503
  shareUrl={editorShareUrl}
15481
15504
  shareUrlLabel={t("designEditor.shareEditorLink")}
15482
15505
  shareUrlDescription={t("designEditor.shareEditorLinkDescription")}
15506
+ showShareLinks={false}
15507
+ showDoneButton={false}
15508
+ shareFooterContent={shareLinkFooter}
15483
15509
  shareTabs={designShareTabs}
15484
15510
  popoverClassName={designSharePopoverClassName}
15485
15511
  triggerClassName="h-8 rounded-md !border-[var(--design-editor-accent-color)] !bg-[var(--design-editor-accent-color)] px-3 text-sm !text-[var(--design-editor-accent-contrast-color)] shadow-none hover:!border-[var(--design-editor-accent-hover-color)] hover:!bg-[var(--design-editor-accent-hover-color)] hover:!text-[var(--design-editor-accent-contrast-color)] focus-visible:ring-[var(--design-editor-accent-color)] [&_svg]:!text-[var(--design-editor-accent-contrast-color)]"
@@ -15879,6 +15905,9 @@ ${serializedHtml}
15879
15905
  shareUrlDescription={t(
15880
15906
  "designEditor.shareEditorLinkDescription",
15881
15907
  )}
15908
+ showShareLinks={false}
15909
+ showDoneButton={false}
15910
+ shareFooterContent={shareLinkFooter}
15882
15911
  shareTabs={designShareTabs}
15883
15912
  popoverClassName={designSharePopoverClassName}
15884
15913
  triggerClassName="h-8 rounded-md !border-[var(--design-editor-accent-color)] !bg-[var(--design-editor-accent-color)] px-3 !text-[var(--design-editor-accent-contrast-color)] shadow-none hover:!border-[var(--design-editor-accent-hover-color)] hover:!bg-[var(--design-editor-accent-hover-color)] hover:!text-[var(--design-editor-accent-contrast-color)] focus-visible:ring-[var(--design-editor-accent-color)] [&_svg]:!text-[var(--design-editor-accent-contrast-color)]"
@@ -16196,7 +16225,7 @@ ${serializedHtml}
16196
16225
  }}
16197
16226
  />
16198
16227
  )}
16199
- {pendingVisualStyleEdits.length > 0 ? (
16228
+ {showPendingVisualStyleApply ? (
16200
16229
  <div className="pointer-events-none absolute bottom-5 right-5 z-[70] flex items-end">
16201
16230
  <DropdownMenu>
16202
16231
  <DropdownMenuTrigger asChild>
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-06-30
4
+ ---
5
+
6
+ Agent chat now reports saved Design generations clearly when only the final assistant note times out.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-06-30
4
+ ---
5
+
6
+ Apply styles now appears only for localhost visual-edit screens.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-06-30
4
+ ---
5
+
6
+ Share options now make link sharing, export, and agent handoff easier to notice while keeping the dialog cleaner.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-01
4
+ ---
5
+
6
+ Share general access options now appear above the dialog when switching a design from private to organization access.