@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.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +14 -0
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/agent/production-agent.ts +9 -2
- package/corpus/core/src/agent/run-loop-with-resume.ts +38 -8
- package/corpus/core/src/agent/thread-data-builder.ts +6 -0
- package/corpus/core/src/client/agent-chat-adapter.ts +208 -9
- package/corpus/core/src/client/sharing/ShareButton.tsx +70 -21
- package/corpus/core/src/client/sse-event-processor.ts +15 -0
- package/corpus/templates/analytics/AGENTS.md +1 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +129 -100
- package/corpus/templates/design/changelog/2026-06-30-agent-chat-now-reports-saved-design-generations-clearly-when.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-apply-styles-now-appears-only-for-localhost-visual-edit-scre.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-share-options-now-make-export-and-agent-handoff-easier-to-no.md +6 -0
- package/corpus/templates/design/changelog/2026-07-01-share-general-access-menu-stays-open-when-choosing-organization.md +6 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +9 -2
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts +2 -2
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +31 -8
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +2 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +5 -0
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +179 -8
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts +6 -0
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +25 -11
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +4 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +13 -0
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- 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
|
-
|
|
626
|
-
Boolean(props.
|
|
627
|
-
|
|
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
|
-
|
|
825
|
-
<
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
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
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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 [
|
|
5159
|
-
const
|
|
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
|
-
|
|
12534
|
-
|
|
12535
|
-
|
|
12536
|
-
|
|
12537
|
-
|
|
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
|
-
[
|
|
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={
|
|
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
|
-
|
|
13108
|
-
|
|
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-
|
|
13163
|
-
"[&_[role=tab]]:!h-
|
|
13164
|
-
"[&_[role=tab]:hover]:bg-
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
{
|
|
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>
|