@agent-native/core 0.84.25 → 0.84.27
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 +12 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +4 -2
- package/corpus/core/src/agent/run-manager.ts +4 -3
- package/corpus/core/src/agent/types.ts +9 -2
- package/corpus/core/src/client/AssistantChat.tsx +15 -1
- package/corpus/core/src/client/FeedbackButton.tsx +2 -2
- package/corpus/core/src/client/components/PresenceBar.tsx +7 -2
- package/corpus/core/src/client/composer/ComposerPlusMenu.tsx +23 -9
- package/corpus/core/src/client/sharing/ShareButton.tsx +22 -3
- package/corpus/core/src/client/sharing/ShareDialog.tsx +13 -1
- package/corpus/core/src/client/sse-event-processor.ts +49 -20
- package/corpus/core/src/sharing/actions/set-resource-visibility.ts +3 -3
- package/corpus/templates/design/actions/insert-asset.ts +14 -2
- package/corpus/templates/design/actions/insert-design-native-asset.ts +16 -4
- package/corpus/templates/design/actions/insert-figma-library-asset.ts +14 -2
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +14 -0
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +54 -10
- package/corpus/templates/design/app/components/design/EditPanel.tsx +23 -16
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +7 -2
- package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +9 -5
- package/corpus/templates/design/app/components/layout/Header.tsx +44 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +267 -105
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +15 -1
- package/corpus/templates/design/app/pages/Index.tsx +20 -9
- package/corpus/templates/design/app/routes/settings.tsx +1 -1
- package/corpus/templates/design/changelog/2026-07-01-design-editor-polish-makes-zoom-assets-sharing-and-agent-handoffs-clearer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-01-design-qa-fixes-make-exports-permissions-and-tools-clearer.md +6 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +4 -3
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +4 -4
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/types.d.ts +3 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +16 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/FeedbackButton.js +2 -2
- package/dist/client/FeedbackButton.js.map +1 -1
- package/dist/client/components/PresenceBar.d.ts.map +1 -1
- package/dist/client/components/PresenceBar.js +6 -2
- package/dist/client/components/PresenceBar.js.map +1 -1
- package/dist/client/composer/ComposerPlusMenu.d.ts.map +1 -1
- package/dist/client/composer/ComposerPlusMenu.js +15 -7
- package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/client/sharing/ShareButton.js +15 -2
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/sharing/ShareDialog.js +16 -1
- package/dist/client/sharing/ShareDialog.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +42 -16
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +6 -6
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/sharing/actions/set-resource-visibility.js +3 -3
- package/dist/sharing/actions/set-resource-visibility.js.map +1 -1
- package/package.json +1 -1
|
@@ -71,12 +71,9 @@ import { normalizeDesignSourceType } from "@shared/source-mode";
|
|
|
71
71
|
import {
|
|
72
72
|
IconArrowLeft,
|
|
73
73
|
IconArrowUpRight,
|
|
74
|
-
IconArrowsMaximize,
|
|
75
74
|
IconPencil,
|
|
76
75
|
IconMessage,
|
|
77
76
|
IconBrush,
|
|
78
|
-
IconZoomIn,
|
|
79
|
-
IconZoomOut,
|
|
80
77
|
IconDeviceDesktop,
|
|
81
78
|
IconDeviceTablet,
|
|
82
79
|
IconDeviceMobile,
|
|
@@ -118,6 +115,7 @@ import {
|
|
|
118
115
|
IconCircleCheck,
|
|
119
116
|
IconTerminal2,
|
|
120
117
|
IconLink,
|
|
118
|
+
IconLock,
|
|
121
119
|
} from "@tabler/icons-react";
|
|
122
120
|
import { useQueryClient } from "@tanstack/react-query";
|
|
123
121
|
import {
|
|
@@ -306,6 +304,8 @@ const BOARD_SURFACE_SIZE = 131_072;
|
|
|
306
304
|
const LOCALHOST_WRITE_EXTENSIONS = new Set([".html", ".htm", ".css"]);
|
|
307
305
|
const NO_LOCALHOST_WRITE_CONTENT_MESSAGE =
|
|
308
306
|
"No content to write. Open the screen first." /* i18n-ignore */;
|
|
307
|
+
const TWEAK_CONTROLS_EDIT_ACCESS_MESSAGE =
|
|
308
|
+
"You need edit access to add tweak controls." /* i18n-ignore */;
|
|
309
309
|
const FOCUSED_SCREEN_ZOOM = 100;
|
|
310
310
|
export const DEFAULT_OVERVIEW_ZOOM = 60;
|
|
311
311
|
const KEEPALIVE_FILE_SAVE_MAX_BYTES = 60_000;
|
|
@@ -857,6 +857,16 @@ function removeEditorChromeOverlays(root: ParentNode): void {
|
|
|
857
857
|
.forEach((element) => element.remove());
|
|
858
858
|
}
|
|
859
859
|
|
|
860
|
+
function sanitizeSerializedXmlForSvg(value: string): string {
|
|
861
|
+
// SVG opened as XML only knows the five predefined entities. HTML serializers
|
|
862
|
+
// can leave named entities or bare ampersands in foreignObject content; escape
|
|
863
|
+
// those so the downloaded SVG parses cleanly in browsers and editors.
|
|
864
|
+
return value.replace(
|
|
865
|
+
/&(?!(?:amp|lt|gt|quot|apos|#\d+|#x[0-9a-fA-F]+);)/g,
|
|
866
|
+
"&",
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
|
|
860
870
|
/**
|
|
861
871
|
* Resolve the document-space rect of the currently selected element inside the
|
|
862
872
|
* preview iframe so image exports (PNG/SVG) can crop to just that frame instead
|
|
@@ -3741,10 +3751,17 @@ function DesignCollaboratorsMenu({
|
|
|
3741
3751
|
return (
|
|
3742
3752
|
<DropdownMenuItem
|
|
3743
3753
|
key={user.email}
|
|
3744
|
-
onSelect={() => {
|
|
3745
|
-
if (
|
|
3754
|
+
onSelect={(event) => {
|
|
3755
|
+
if (collaborator.isCurrent) {
|
|
3756
|
+
event.preventDefault();
|
|
3757
|
+
return;
|
|
3758
|
+
}
|
|
3759
|
+
onAvatarClick?.(user);
|
|
3746
3760
|
}}
|
|
3747
|
-
className=
|
|
3761
|
+
className={cn(
|
|
3762
|
+
"gap-2",
|
|
3763
|
+
collaborator.isCurrent && "cursor-default",
|
|
3764
|
+
)}
|
|
3748
3765
|
>
|
|
3749
3766
|
<DesignCollaboratorAvatar collaborator={collaborator} />
|
|
3750
3767
|
<span className="min-w-0 flex-1">
|
|
@@ -3755,9 +3772,17 @@ function DesignCollaboratorsMenu({
|
|
|
3755
3772
|
{user.email}
|
|
3756
3773
|
</span>
|
|
3757
3774
|
</span>
|
|
3758
|
-
{
|
|
3775
|
+
{collaborator.isCurrent ? (
|
|
3776
|
+
<span className="text-xs text-muted-foreground">
|
|
3777
|
+
{"You" /* i18n-ignore collaborator row */}
|
|
3778
|
+
</span>
|
|
3779
|
+
) : isFollowing ? (
|
|
3759
3780
|
<IconCheck className="size-3.5 text-[var(--design-editor-accent-color)]" />
|
|
3760
|
-
) :
|
|
3781
|
+
) : (
|
|
3782
|
+
<span className="text-xs text-muted-foreground">
|
|
3783
|
+
{"Follow" /* i18n-ignore collaborator row */}
|
|
3784
|
+
</span>
|
|
3785
|
+
)}
|
|
3761
3786
|
</DropdownMenuItem>
|
|
3762
3787
|
);
|
|
3763
3788
|
})}
|
|
@@ -3766,6 +3791,26 @@ function DesignCollaboratorsMenu({
|
|
|
3766
3791
|
);
|
|
3767
3792
|
}
|
|
3768
3793
|
|
|
3794
|
+
function ReadOnlyEditorPanel({
|
|
3795
|
+
title,
|
|
3796
|
+
description,
|
|
3797
|
+
}: {
|
|
3798
|
+
title: string;
|
|
3799
|
+
description: string;
|
|
3800
|
+
}) {
|
|
3801
|
+
return (
|
|
3802
|
+
<div className="flex min-h-0 flex-1 flex-col items-center justify-center px-5 text-center">
|
|
3803
|
+
<div className="mb-3 flex size-10 items-center justify-center rounded-lg bg-muted text-muted-foreground">
|
|
3804
|
+
<IconLock className="size-5" />
|
|
3805
|
+
</div>
|
|
3806
|
+
<p className="text-sm font-medium text-foreground">{title}</p>
|
|
3807
|
+
<p className="mt-1 max-w-56 text-xs leading-5 text-muted-foreground">
|
|
3808
|
+
{description}
|
|
3809
|
+
</p>
|
|
3810
|
+
</div>
|
|
3811
|
+
);
|
|
3812
|
+
}
|
|
3813
|
+
|
|
3769
3814
|
function externalPreviewUrlForContent(content: string): string | null {
|
|
3770
3815
|
const trimmed = content.trim();
|
|
3771
3816
|
if (!/^https?:\/\//i.test(trimmed)) return null;
|
|
@@ -5821,12 +5866,17 @@ export default function DesignEditor() {
|
|
|
5821
5866
|
const next = titleDraft.trim();
|
|
5822
5867
|
if (!next || next === design?.title) return;
|
|
5823
5868
|
|
|
5869
|
+
const designQueryKey = ["action", "get-design", { id }];
|
|
5870
|
+
const previousDesign = queryClient.getQueryData(designQueryKey);
|
|
5871
|
+
const previousListDesignsQueries = queryClient.getQueriesData({
|
|
5872
|
+
queryKey: ["action", "list-designs"],
|
|
5873
|
+
});
|
|
5824
5874
|
queryClient.setQueryData(["action", "get-design", { id }], (old: any) => {
|
|
5825
5875
|
if (!old || typeof old !== "object") return old;
|
|
5826
5876
|
return { ...old, title: next };
|
|
5827
5877
|
});
|
|
5828
|
-
queryClient.
|
|
5829
|
-
["action", "list-designs"
|
|
5878
|
+
queryClient.setQueriesData(
|
|
5879
|
+
{ queryKey: ["action", "list-designs"] },
|
|
5830
5880
|
(old: any) => {
|
|
5831
5881
|
if (!old) return old;
|
|
5832
5882
|
return {
|
|
@@ -5840,7 +5890,12 @@ export default function DesignEditor() {
|
|
|
5840
5890
|
|
|
5841
5891
|
updateDesignMutation.mutate({ id, title: next } as any, {
|
|
5842
5892
|
onError: () => {
|
|
5893
|
+
queryClient.setQueryData(designQueryKey, previousDesign);
|
|
5894
|
+
for (const [queryKey, data] of previousListDesignsQueries) {
|
|
5895
|
+
queryClient.setQueryData(queryKey, data);
|
|
5896
|
+
}
|
|
5843
5897
|
queryClient.invalidateQueries({ queryKey: ["action", "get-design"] });
|
|
5898
|
+
queryClient.invalidateQueries({ queryKey: ["action", "list-designs"] });
|
|
5844
5899
|
},
|
|
5845
5900
|
});
|
|
5846
5901
|
}, [
|
|
@@ -8976,8 +9031,11 @@ export default function DesignEditor() {
|
|
|
8976
9031
|
files: UploadedFile[],
|
|
8977
9032
|
options: PromptComposerSubmitOptions,
|
|
8978
9033
|
) => {
|
|
8979
|
-
if (!canEditDesign
|
|
9034
|
+
if (!canEditDesign) {
|
|
9035
|
+
toast.error(TWEAK_CONTROLS_EDIT_ACCESS_MESSAGE);
|
|
8980
9036
|
return;
|
|
9037
|
+
}
|
|
9038
|
+
if (!design) return;
|
|
8981
9039
|
const trimmed = prompt.trim();
|
|
8982
9040
|
if (!trimmed) return;
|
|
8983
9041
|
const fileContext = formatUploadedFileContext(files);
|
|
@@ -9010,6 +9068,7 @@ export default function DesignEditor() {
|
|
|
9010
9068
|
context,
|
|
9011
9069
|
submit: true,
|
|
9012
9070
|
openSidebar: true,
|
|
9071
|
+
newTab: true,
|
|
9013
9072
|
model: options.model,
|
|
9014
9073
|
engine: options.engine,
|
|
9015
9074
|
effort: options.effort,
|
|
@@ -9021,10 +9080,8 @@ export default function DesignEditor() {
|
|
|
9021
9080
|
activeFile,
|
|
9022
9081
|
canEditDesign,
|
|
9023
9082
|
design,
|
|
9024
|
-
generating,
|
|
9025
9083
|
handleTweakPromptOpenChange,
|
|
9026
9084
|
id,
|
|
9027
|
-
pendingGenerationActive,
|
|
9028
9085
|
tweakSelections,
|
|
9029
9086
|
tweaks,
|
|
9030
9087
|
],
|
|
@@ -9214,6 +9271,45 @@ export default function DesignEditor() {
|
|
|
9214
9271
|
});
|
|
9215
9272
|
}, [activeFile, design?.title, id, selectedCodeLayerNode, selectedElement]);
|
|
9216
9273
|
|
|
9274
|
+
const handleAssetInserted = useCallback(
|
|
9275
|
+
(selection: {
|
|
9276
|
+
fileId?: string;
|
|
9277
|
+
nodeId?: string;
|
|
9278
|
+
selector?: string;
|
|
9279
|
+
title?: string;
|
|
9280
|
+
}) => {
|
|
9281
|
+
if (selection.fileId) {
|
|
9282
|
+
setActiveFileId(selection.fileId);
|
|
9283
|
+
if (viewModeRef.current === "overview") {
|
|
9284
|
+
setOverviewSelectedScreenIds([selection.fileId]);
|
|
9285
|
+
}
|
|
9286
|
+
}
|
|
9287
|
+
if (selection.nodeId) {
|
|
9288
|
+
setSelectedLayerIdsState([selection.nodeId]);
|
|
9289
|
+
}
|
|
9290
|
+
if (selection.selector || selection.nodeId) {
|
|
9291
|
+
setSelectedElement({
|
|
9292
|
+
tagName: "section",
|
|
9293
|
+
sourceId: selection.nodeId,
|
|
9294
|
+
selector:
|
|
9295
|
+
selection.selector ??
|
|
9296
|
+
`[data-agent-native-node-id="${selection.nodeId}"]`,
|
|
9297
|
+
classes: [],
|
|
9298
|
+
computedStyles: {},
|
|
9299
|
+
boundingRect: { x: 0, y: 0, width: 0, height: 0 },
|
|
9300
|
+
textContent: selection.title,
|
|
9301
|
+
isFlexChild: false,
|
|
9302
|
+
isFlexContainer: false,
|
|
9303
|
+
});
|
|
9304
|
+
}
|
|
9305
|
+
setHoveredElement(null);
|
|
9306
|
+
setHoveredElementScreenId(null);
|
|
9307
|
+
setActiveTool("move");
|
|
9308
|
+
setMode("edit");
|
|
9309
|
+
},
|
|
9310
|
+
[],
|
|
9311
|
+
);
|
|
9312
|
+
|
|
9217
9313
|
const designExtensionContext = useMemo<DesignExtensionSlotContext>(
|
|
9218
9314
|
() => ({
|
|
9219
9315
|
designId: id ?? "",
|
|
@@ -9250,6 +9346,7 @@ export default function DesignEditor() {
|
|
|
9250
9346
|
updatedAt,
|
|
9251
9347
|
});
|
|
9252
9348
|
},
|
|
9349
|
+
onAssetInserted: handleAssetInserted,
|
|
9253
9350
|
}),
|
|
9254
9351
|
[
|
|
9255
9352
|
activeContent,
|
|
@@ -9262,6 +9359,7 @@ export default function DesignEditor() {
|
|
|
9262
9359
|
clearShaderFillPreview,
|
|
9263
9360
|
design?.title,
|
|
9264
9361
|
files,
|
|
9362
|
+
handleAssetInserted,
|
|
9265
9363
|
id,
|
|
9266
9364
|
mode,
|
|
9267
9365
|
overviewSelectedScreenIds,
|
|
@@ -13326,22 +13424,44 @@ export default function DesignEditor() {
|
|
|
13326
13424
|
);
|
|
13327
13425
|
// When an element is selected, crop the export to just that frame.
|
|
13328
13426
|
const cropRect = resolveExportCropRect(doc, selectedElement);
|
|
13329
|
-
|
|
13330
|
-
|
|
13331
|
-
|
|
13332
|
-
|
|
13333
|
-
|
|
13334
|
-
|
|
13335
|
-
|
|
13336
|
-
|
|
13337
|
-
|
|
13338
|
-
|
|
13339
|
-
|
|
13340
|
-
|
|
13341
|
-
|
|
13342
|
-
|
|
13343
|
-
|
|
13344
|
-
|
|
13427
|
+
let canvas: HTMLCanvasElement;
|
|
13428
|
+
try {
|
|
13429
|
+
canvas = await html2canvas(doc.documentElement, {
|
|
13430
|
+
width,
|
|
13431
|
+
height,
|
|
13432
|
+
windowWidth: width,
|
|
13433
|
+
windowHeight: height,
|
|
13434
|
+
scale: exportScale,
|
|
13435
|
+
useCORS: true,
|
|
13436
|
+
foreignObjectRendering: true,
|
|
13437
|
+
backgroundColor: null,
|
|
13438
|
+
onclone: (clonedDocument) => {
|
|
13439
|
+
// Sanitize colors first: it aligns source/clone elements by index,
|
|
13440
|
+
// so remove the editor-chrome overlays only afterwards.
|
|
13441
|
+
sanitizeHtml2CanvasClone(doc, clonedDocument);
|
|
13442
|
+
removeEditorChromeOverlays(clonedDocument);
|
|
13443
|
+
},
|
|
13444
|
+
});
|
|
13445
|
+
} catch (primaryError) {
|
|
13446
|
+
console.warn(
|
|
13447
|
+
"PNG export failed with foreignObjectRendering; retrying canvas renderer:",
|
|
13448
|
+
primaryError,
|
|
13449
|
+
);
|
|
13450
|
+
canvas = await html2canvas(doc.documentElement, {
|
|
13451
|
+
width,
|
|
13452
|
+
height,
|
|
13453
|
+
windowWidth: width,
|
|
13454
|
+
windowHeight: height,
|
|
13455
|
+
scale: exportScale,
|
|
13456
|
+
useCORS: true,
|
|
13457
|
+
foreignObjectRendering: false,
|
|
13458
|
+
backgroundColor: null,
|
|
13459
|
+
onclone: (clonedDocument) => {
|
|
13460
|
+
sanitizeHtml2CanvasClone(doc, clonedDocument);
|
|
13461
|
+
removeEditorChromeOverlays(clonedDocument);
|
|
13462
|
+
},
|
|
13463
|
+
});
|
|
13464
|
+
}
|
|
13345
13465
|
// Render the whole page first, then crop, so ancestor backgrounds show
|
|
13346
13466
|
// through the selected frame exactly as they do on screen.
|
|
13347
13467
|
const outputCanvas = cropRect
|
|
@@ -13465,7 +13585,9 @@ export default function DesignEditor() {
|
|
|
13465
13585
|
body.style.minHeight = `${height}px`;
|
|
13466
13586
|
}
|
|
13467
13587
|
|
|
13468
|
-
const serializedHtml =
|
|
13588
|
+
const serializedHtml = sanitizeSerializedXmlForSvg(
|
|
13589
|
+
new XMLSerializer().serializeToString(clone),
|
|
13590
|
+
);
|
|
13469
13591
|
const safeTitle =
|
|
13470
13592
|
design?.title
|
|
13471
13593
|
?.replace(/&/g, "&")
|
|
@@ -13661,7 +13783,7 @@ ${serializedHtml}
|
|
|
13661
13783
|
type="button"
|
|
13662
13784
|
onClick={selectedShareExportOption.onDownload}
|
|
13663
13785
|
disabled={selectedShareExportOption.disabled}
|
|
13664
|
-
className="h-8 gap-1.5 rounded-md px-3 text-[12px]"
|
|
13786
|
+
className="h-8 gap-1.5 rounded-md bg-[var(--design-editor-accent-color)] px-3 text-[12px] text-[var(--design-editor-accent-contrast-color)] shadow-none hover:bg-[var(--design-editor-accent-hover-color)] hover:text-[var(--design-editor-accent-contrast-color)] disabled:bg-muted disabled:text-muted-foreground"
|
|
13665
13787
|
>
|
|
13666
13788
|
<IconDownload className="size-3.5" />
|
|
13667
13789
|
{"Download" /* i18n-ignore share export action */}
|
|
@@ -15495,6 +15617,20 @@ ${serializedHtml}
|
|
|
15495
15617
|
);
|
|
15496
15618
|
|
|
15497
15619
|
const zoomLabel = `${Math.round(zoom)}%`;
|
|
15620
|
+
const [zoomMenuOpen, setZoomMenuOpen] = useState(false);
|
|
15621
|
+
const [zoomInputValue, setZoomInputValue] = useState(zoomLabel);
|
|
15622
|
+
useEffect(() => {
|
|
15623
|
+
if (!zoomMenuOpen) setZoomInputValue(zoomLabel);
|
|
15624
|
+
}, [zoomLabel, zoomMenuOpen]);
|
|
15625
|
+
const commitZoomInput = useCallback(() => {
|
|
15626
|
+
const next = Number(zoomInputValue.replace("%", "").trim());
|
|
15627
|
+
if (!Number.isFinite(next)) {
|
|
15628
|
+
setZoomInputValue(zoomLabel);
|
|
15629
|
+
return;
|
|
15630
|
+
}
|
|
15631
|
+
setZoom(Math.max(10, Math.min(500, next)));
|
|
15632
|
+
setZoomMenuOpen(false);
|
|
15633
|
+
}, [setZoom, zoomInputValue, zoomLabel]);
|
|
15498
15634
|
|
|
15499
15635
|
const handleTokensApplied = useCallback(
|
|
15500
15636
|
(resolvedCssVars: Record<string, string>) => {
|
|
@@ -15834,8 +15970,8 @@ ${serializedHtml}
|
|
|
15834
15970
|
</span>
|
|
15835
15971
|
);
|
|
15836
15972
|
|
|
15837
|
-
const
|
|
15838
|
-
<DropdownMenu>
|
|
15973
|
+
const renderZoomControl = () => (
|
|
15974
|
+
<DropdownMenu open={zoomMenuOpen} onOpenChange={setZoomMenuOpen}>
|
|
15839
15975
|
<Tooltip>
|
|
15840
15976
|
<TooltipTrigger asChild>
|
|
15841
15977
|
<DropdownMenuTrigger asChild>
|
|
@@ -15851,29 +15987,66 @@ ${serializedHtml}
|
|
|
15851
15987
|
</TooltipTrigger>
|
|
15852
15988
|
<TooltipContent>{t("designEditor.zoom")}</TooltipContent>
|
|
15853
15989
|
</Tooltip>
|
|
15854
|
-
<DropdownMenuContent
|
|
15855
|
-
|
|
15856
|
-
|
|
15857
|
-
|
|
15990
|
+
<DropdownMenuContent
|
|
15991
|
+
align="end"
|
|
15992
|
+
className="w-72 overflow-hidden rounded-xl border-[var(--design-editor-control-border)] bg-[var(--design-editor-panel-bg)] p-0 shadow-2xl"
|
|
15993
|
+
>
|
|
15994
|
+
<div className="p-3">
|
|
15995
|
+
<Input
|
|
15996
|
+
autoFocus
|
|
15997
|
+
value={zoomInputValue}
|
|
15998
|
+
onChange={(event) => setZoomInputValue(event.target.value)}
|
|
15999
|
+
onFocus={(event) => event.currentTarget.select()}
|
|
16000
|
+
onKeyDown={(event) => {
|
|
16001
|
+
event.stopPropagation();
|
|
16002
|
+
if (event.key === "Enter") {
|
|
16003
|
+
event.preventDefault();
|
|
16004
|
+
commitZoomInput();
|
|
16005
|
+
} else if (event.key === "Escape") {
|
|
16006
|
+
event.preventDefault();
|
|
16007
|
+
setZoomInputValue(zoomLabel);
|
|
16008
|
+
setZoomMenuOpen(false);
|
|
16009
|
+
}
|
|
16010
|
+
}}
|
|
16011
|
+
className="h-10 rounded-md border-[var(--design-editor-accent-color)] bg-[var(--design-editor-control-bg)] px-3 text-base font-medium tabular-nums text-foreground shadow-none focus-visible:ring-2 focus-visible:ring-[var(--design-editor-accent-color)]"
|
|
16012
|
+
aria-label={"Zoom percentage" /* i18n-ignore zoom field */}
|
|
16013
|
+
/>
|
|
16014
|
+
</div>
|
|
16015
|
+
<DropdownMenuSeparator />
|
|
16016
|
+
<DropdownMenuItem
|
|
16017
|
+
onClick={handleZoomIn}
|
|
16018
|
+
className="h-12 px-12 text-[15px]"
|
|
16019
|
+
>
|
|
16020
|
+
<span className="flex-1">{"Zoom in" /* i18n-ignore */}</span>
|
|
16021
|
+
<DropdownMenuShortcut>⌘+</DropdownMenuShortcut>
|
|
15858
16022
|
</DropdownMenuItem>
|
|
15859
|
-
<DropdownMenuItem
|
|
15860
|
-
|
|
15861
|
-
|
|
16023
|
+
<DropdownMenuItem
|
|
16024
|
+
onClick={handleZoomOut}
|
|
16025
|
+
className="h-12 px-12 text-[15px]"
|
|
16026
|
+
>
|
|
16027
|
+
<span className="flex-1">{"Zoom out" /* i18n-ignore */}</span>
|
|
16028
|
+
<DropdownMenuShortcut>⌘−</DropdownMenuShortcut>
|
|
15862
16029
|
</DropdownMenuItem>
|
|
15863
|
-
<DropdownMenuItem
|
|
15864
|
-
|
|
15865
|
-
|
|
16030
|
+
<DropdownMenuItem
|
|
16031
|
+
onClick={handleZoomToFit}
|
|
16032
|
+
className="h-12 px-12 text-[15px]"
|
|
16033
|
+
>
|
|
16034
|
+
<span className="flex-1">{"Zoom to fit" /* i18n-ignore */}</span>
|
|
15866
16035
|
<DropdownMenuShortcut>⇧1</DropdownMenuShortcut>
|
|
15867
16036
|
</DropdownMenuItem>
|
|
15868
|
-
|
|
15869
|
-
{ZOOM_PRESETS.map((preset) => (
|
|
16037
|
+
{[50, 100, 200].map((preset) => (
|
|
15870
16038
|
<DropdownMenuItem
|
|
15871
16039
|
key={preset}
|
|
15872
16040
|
onClick={() => setZoom(preset)}
|
|
15873
|
-
className="
|
|
16041
|
+
className="h-12 px-12 text-[15px]"
|
|
15874
16042
|
>
|
|
15875
|
-
<span>
|
|
15876
|
-
|
|
16043
|
+
<span className="flex-1">
|
|
16044
|
+
{"Zoom to " /* i18n-ignore */}
|
|
16045
|
+
{preset}%
|
|
16046
|
+
</span>
|
|
16047
|
+
{preset === 100 ? (
|
|
16048
|
+
<DropdownMenuShortcut>⌘0</DropdownMenuShortcut>
|
|
16049
|
+
) : null}
|
|
15877
16050
|
</DropdownMenuItem>
|
|
15878
16051
|
))}
|
|
15879
16052
|
</DropdownMenuContent>
|
|
@@ -16390,52 +16563,7 @@ ${serializedHtml}
|
|
|
16390
16563
|
</DropdownMenuContent>
|
|
16391
16564
|
</DropdownMenu>
|
|
16392
16565
|
|
|
16393
|
-
{
|
|
16394
|
-
<DropdownMenu>
|
|
16395
|
-
<Tooltip>
|
|
16396
|
-
<TooltipTrigger asChild>
|
|
16397
|
-
<DropdownMenuTrigger asChild>
|
|
16398
|
-
<Button
|
|
16399
|
-
variant="ghost"
|
|
16400
|
-
size="sm"
|
|
16401
|
-
className="h-7 gap-1 px-2 text-xs tabular-nums text-muted-foreground cursor-pointer"
|
|
16402
|
-
>
|
|
16403
|
-
{zoomLabel}
|
|
16404
|
-
<IconChevronDown className="w-3 h-3 opacity-60" />
|
|
16405
|
-
</Button>
|
|
16406
|
-
</DropdownMenuTrigger>
|
|
16407
|
-
</TooltipTrigger>
|
|
16408
|
-
<TooltipContent>{t("designEditor.zoom")}</TooltipContent>
|
|
16409
|
-
</Tooltip>
|
|
16410
|
-
<DropdownMenuContent align="end" className="w-40">
|
|
16411
|
-
<DropdownMenuItem onClick={handleZoomOut}>
|
|
16412
|
-
<IconZoomOut className="mr-2 h-4 w-4" />
|
|
16413
|
-
{t("designEditor.zoomOut")}
|
|
16414
|
-
</DropdownMenuItem>
|
|
16415
|
-
<DropdownMenuItem onClick={handleZoomIn}>
|
|
16416
|
-
<IconZoomIn className="mr-2 h-4 w-4" />
|
|
16417
|
-
{t("designEditor.zoomIn")}
|
|
16418
|
-
</DropdownMenuItem>
|
|
16419
|
-
<DropdownMenuItem onClick={handleZoomToFit}>
|
|
16420
|
-
<IconArrowsMaximize className="mr-2 h-4 w-4" />
|
|
16421
|
-
{"Fit to screen" /* i18n-ignore zoom option */}
|
|
16422
|
-
<DropdownMenuShortcut>⇧1</DropdownMenuShortcut>
|
|
16423
|
-
</DropdownMenuItem>
|
|
16424
|
-
<DropdownMenuSeparator />
|
|
16425
|
-
{ZOOM_PRESETS.map((preset) => (
|
|
16426
|
-
<DropdownMenuItem
|
|
16427
|
-
key={preset}
|
|
16428
|
-
onClick={() => setZoom(preset)}
|
|
16429
|
-
className="justify-between"
|
|
16430
|
-
>
|
|
16431
|
-
<span>{preset}%</span>
|
|
16432
|
-
{Math.round(zoom) === preset && (
|
|
16433
|
-
<IconCheck className="h-4 w-4" />
|
|
16434
|
-
)}
|
|
16435
|
-
</DropdownMenuItem>
|
|
16436
|
-
))}
|
|
16437
|
-
</DropdownMenuContent>
|
|
16438
|
-
</DropdownMenu>
|
|
16566
|
+
{renderZoomControl()}
|
|
16439
16567
|
|
|
16440
16568
|
<div className="mx-1 h-5 w-px bg-border" />
|
|
16441
16569
|
</>
|
|
@@ -16565,7 +16693,16 @@ ${serializedHtml}
|
|
|
16565
16693
|
showScopeBadge={false}
|
|
16566
16694
|
browserTabId={browserTabId}
|
|
16567
16695
|
/>
|
|
16568
|
-
) :
|
|
16696
|
+
) : (
|
|
16697
|
+
<ReadOnlyEditorPanel
|
|
16698
|
+
title={
|
|
16699
|
+
"Agent chat requires editor access" /* i18n-ignore */
|
|
16700
|
+
}
|
|
16701
|
+
description={
|
|
16702
|
+
"Ask an owner for edit access before using the agent to change this design." /* i18n-ignore */
|
|
16703
|
+
}
|
|
16704
|
+
/>
|
|
16705
|
+
)}
|
|
16569
16706
|
</div>
|
|
16570
16707
|
<div
|
|
16571
16708
|
className={cn(
|
|
@@ -16578,7 +16715,16 @@ ${serializedHtml}
|
|
|
16578
16715
|
{t("designEditor.leftRail.assets")}
|
|
16579
16716
|
</h3>
|
|
16580
16717
|
</div>
|
|
16581
|
-
|
|
16718
|
+
{canEditDesign ? (
|
|
16719
|
+
<AssetLibraryPanel context={designExtensionContext} />
|
|
16720
|
+
) : (
|
|
16721
|
+
<ReadOnlyEditorPanel
|
|
16722
|
+
title={"Assets require editor access" /* i18n-ignore */}
|
|
16723
|
+
description={
|
|
16724
|
+
"Ask an owner for edit access before inserting assets into this design." /* i18n-ignore */
|
|
16725
|
+
}
|
|
16726
|
+
/>
|
|
16727
|
+
)}
|
|
16582
16728
|
</div>
|
|
16583
16729
|
<div
|
|
16584
16730
|
className={cn(
|
|
@@ -16586,11 +16732,20 @@ ${serializedHtml}
|
|
|
16586
16732
|
activeLeftPanel === "tools" ? "flex" : "hidden",
|
|
16587
16733
|
)}
|
|
16588
16734
|
>
|
|
16589
|
-
|
|
16590
|
-
|
|
16591
|
-
|
|
16592
|
-
|
|
16593
|
-
|
|
16735
|
+
{canEditDesign ? (
|
|
16736
|
+
<DesignExtensionsPanel
|
|
16737
|
+
context={designExtensionContext}
|
|
16738
|
+
hideAssetLibrary
|
|
16739
|
+
title={t("designEditor.leftRail.tools")}
|
|
16740
|
+
/>
|
|
16741
|
+
) : (
|
|
16742
|
+
<ReadOnlyEditorPanel
|
|
16743
|
+
title={"Tools require editor access" /* i18n-ignore */}
|
|
16744
|
+
description={
|
|
16745
|
+
"Ask an owner for edit access before running tools or creating extensions for this design." /* i18n-ignore */
|
|
16746
|
+
}
|
|
16747
|
+
/>
|
|
16748
|
+
)}
|
|
16594
16749
|
</div>
|
|
16595
16750
|
<div
|
|
16596
16751
|
className={cn(
|
|
@@ -16598,7 +16753,7 @@ ${serializedHtml}
|
|
|
16598
16753
|
activeLeftPanel === "tokens" ? "flex" : "hidden",
|
|
16599
16754
|
)}
|
|
16600
16755
|
>
|
|
16601
|
-
{id ? (
|
|
16756
|
+
{id && canEditDesign ? (
|
|
16602
16757
|
<>
|
|
16603
16758
|
<div className="flex min-h-8 shrink-0 items-center border-b border-border/60 px-3">
|
|
16604
16759
|
<h3 className="min-w-0 flex-1 truncate text-xs font-semibold text-foreground">
|
|
@@ -16612,7 +16767,14 @@ ${serializedHtml}
|
|
|
16612
16767
|
/>
|
|
16613
16768
|
</div>
|
|
16614
16769
|
</>
|
|
16615
|
-
) :
|
|
16770
|
+
) : (
|
|
16771
|
+
<ReadOnlyEditorPanel
|
|
16772
|
+
title={"Tokens require editor access" /* i18n-ignore */}
|
|
16773
|
+
description={
|
|
16774
|
+
"Ask an owner for edit access before importing, creating, or applying tokens." /* i18n-ignore */
|
|
16775
|
+
}
|
|
16776
|
+
/>
|
|
16777
|
+
)}
|
|
16616
16778
|
</div>
|
|
16617
16779
|
</div>
|
|
16618
16780
|
<div
|
|
@@ -17475,7 +17637,7 @@ ${serializedHtml}
|
|
|
17475
17637
|
selectedElements={selectedInspectorElements}
|
|
17476
17638
|
pageStyles={pageStyles}
|
|
17477
17639
|
zoom={zoom}
|
|
17478
|
-
headerTrailing={
|
|
17640
|
+
headerTrailing={renderZoomControl()}
|
|
17479
17641
|
width={rightSidebarWidth}
|
|
17480
17642
|
activeTab={activeInspectorTab}
|
|
17481
17643
|
onActiveTabChange={setActiveInspectorTab}
|
|
@@ -17735,7 +17897,7 @@ ${serializedHtml}
|
|
|
17735
17897
|
title={t("designEditor.tweaksPromptTitle")}
|
|
17736
17898
|
placeholder={t("designEditor.tweaksPlaceholder")}
|
|
17737
17899
|
onSubmit={handleTweakPromptSubmit}
|
|
17738
|
-
loading={
|
|
17900
|
+
loading={false}
|
|
17739
17901
|
anchorRef={tweakPromptAnchorRef}
|
|
17740
17902
|
/>
|
|
17741
17903
|
|
|
@@ -59,6 +59,20 @@ interface BuilderIndexResult {
|
|
|
59
59
|
instructions?: string;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
async function readJsonResponse(res: Response): Promise<any> {
|
|
63
|
+
const text = await res.text();
|
|
64
|
+
if (!text.trim()) return {};
|
|
65
|
+
try {
|
|
66
|
+
return JSON.parse(text);
|
|
67
|
+
} catch {
|
|
68
|
+
return {
|
|
69
|
+
error: res.ok
|
|
70
|
+
? "The server returned an invalid response."
|
|
71
|
+
: text.slice(0, 240),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
62
76
|
export default function DesignSystemSetup() {
|
|
63
77
|
const t = useT();
|
|
64
78
|
const navigate = useNavigate();
|
|
@@ -127,7 +141,7 @@ export default function DesignSystemSetup() {
|
|
|
127
141
|
body,
|
|
128
142
|
},
|
|
129
143
|
);
|
|
130
|
-
const json = await res
|
|
144
|
+
const json = await readJsonResponse(res);
|
|
131
145
|
if (!res.ok || json?.error) {
|
|
132
146
|
throw new Error(json?.error || `Upload failed (${res.status})`);
|
|
133
147
|
}
|
|
@@ -189,6 +189,13 @@ export default function Index() {
|
|
|
189
189
|
});
|
|
190
190
|
}, [filtered]);
|
|
191
191
|
|
|
192
|
+
const handleSearchChange = useCallback((query: string) => {
|
|
193
|
+
setSearch(query);
|
|
194
|
+
setSelectedDesignIds((current) =>
|
|
195
|
+
current.size === 0 ? current : new Set(),
|
|
196
|
+
);
|
|
197
|
+
}, []);
|
|
198
|
+
|
|
192
199
|
const clearSelection = useCallback(() => {
|
|
193
200
|
setSelectedDesignIds(new Set());
|
|
194
201
|
}, []);
|
|
@@ -359,14 +366,18 @@ export default function Index() {
|
|
|
359
366
|
setRenameId(null);
|
|
360
367
|
if (!next) return;
|
|
361
368
|
|
|
362
|
-
queryClient.
|
|
363
|
-
["action", "list-designs"
|
|
364
|
-
(old: any) =>
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
369
|
+
queryClient.setQueriesData(
|
|
370
|
+
{ queryKey: ["action", "list-designs"] },
|
|
371
|
+
(old: any) => {
|
|
372
|
+
if (!old || typeof old !== "object") return old;
|
|
373
|
+
return {
|
|
374
|
+
...old,
|
|
375
|
+
count: old.count ?? (old.designs ?? []).length,
|
|
376
|
+
designs: (old.designs ?? []).map((d: Design) =>
|
|
377
|
+
d.id === id ? { ...d, title: next } : d,
|
|
378
|
+
),
|
|
379
|
+
};
|
|
380
|
+
},
|
|
370
381
|
);
|
|
371
382
|
|
|
372
383
|
updateMutation.mutate({ id, title: next } as any, {
|
|
@@ -397,7 +408,7 @@ export default function Index() {
|
|
|
397
408
|
<IconSearch className="absolute start-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-muted-foreground/70" />
|
|
398
409
|
<Input
|
|
399
410
|
value={search}
|
|
400
|
-
onChange={(e) =>
|
|
411
|
+
onChange={(e) => handleSearchChange(e.target.value)}
|
|
401
412
|
placeholder={t("home.searchPlaceholder")}
|
|
402
413
|
className="ps-8 h-8 w-48 bg-accent/50 border-border text-sm text-foreground/90 placeholder:text-muted-foreground/70"
|
|
403
414
|
/>
|
|
@@ -66,7 +66,7 @@ export default function SettingsRoute() {
|
|
|
66
66
|
<CardContent>
|
|
67
67
|
<button
|
|
68
68
|
type="button"
|
|
69
|
-
className="inline-flex h-9 items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
|
|
69
|
+
className="inline-flex h-9 cursor-pointer items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium text-foreground shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
70
70
|
onClick={() => openAgentSettings()}
|
|
71
71
|
>
|
|
72
72
|
{t("settings.openAgentSettings")}
|