@agent-native/core 0.95.0 → 0.95.1
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 +2 -2
- package/corpus/core/CHANGELOG.md +10 -0
- package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +13 -0
- package/corpus/core/docs/content/locales/de-DE/template-design.mdx +18 -0
- package/corpus/core/docs/content/locales/es-ES/template-design.mdx +16 -0
- package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +17 -0
- package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +16 -0
- package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +17 -0
- package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +15 -0
- package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +16 -0
- package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +11 -0
- package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +12 -0
- package/corpus/core/docs/content/template-design.mdx +16 -0
- package/corpus/core/package.json +2 -2
- package/corpus/core/src/agent/production-agent.ts +20 -0
- package/corpus/core/src/agent/thread-data-builder.ts +41 -17
- package/corpus/core/src/client/AssistantChat.tsx +250 -25
- package/corpus/core/src/client/chat/tool-call-display.tsx +1 -1
- package/corpus/core/src/client/chat/use-reconnect-reader-owner.ts +28 -0
- package/corpus/core/src/client/guided-questions.tsx +9 -1
- package/corpus/core/src/client/integrations/IntegrationsPanel.tsx +6 -7
- package/corpus/core/src/client/sse-event-processor.ts +90 -21
- package/corpus/core/src/connections/catalog.ts +2 -2
- package/corpus/core/src/deploy/build.ts +3 -0
- package/corpus/core/src/integrations/catalog.ts +2 -2
- package/corpus/core/src/server/auth.ts +1 -1
- package/corpus/core/src/server/credential-provider.ts +7 -3
- package/corpus/core/src/shared/cache-control.ts +10 -4
- package/corpus/templates/analytics/app/lib/data-sources.ts +5 -4
- package/corpus/templates/analytics/changelog/2026-07-10-slack-token-setup-marked-legacy.md +6 -0
- package/corpus/templates/analytics/server/lib/credential-keys.ts +6 -2
- package/corpus/templates/brain/changelog/2026-07-10-slack-backfill-token-marked-legacy.md +6 -0
- package/corpus/templates/brain/server/register-secrets.ts +2 -2
- package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -1
- package/corpus/templates/clips/app/components/library/page-header.tsx +19 -3
- package/corpus/templates/clips/app/routes/_app.library._index.tsx +16 -12
- package/corpus/templates/clips/changelog/2026-07-10-new-recording-appears-once-when-the-library-sidebar-is-open.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-10-slack-token-fallback-guidance.md +6 -0
- package/corpus/templates/clips/server/register-secrets.ts +1 -1
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +0 -66
- package/corpus/templates/content/app/routes/_app.local-files.tsx +7 -47
- package/corpus/templates/content/changelog/2026-07-10-local-folders-now-wait-for-an-explicit-pull-before-content-r.md +6 -0
- package/corpus/templates/content/package.json +1 -1
- package/corpus/templates/design/actions/generate-design.ts +128 -31
- package/corpus/templates/design/actions/present-design-variants.ts +37 -9
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +9 -3
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +32 -10
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +2 -2
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +9 -1
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +7 -5
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
- package/corpus/templates/design/app/global.css +10 -0
- package/corpus/templates/design/app/hooks/use-question-flow.ts +9 -2
- package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/design/app/i18n-data.ts +21 -11
- package/corpus/templates/design/changelog/2026-07-10-editor-panels-are-easier-to-distinguish-and-design-intake-qu.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-interact-mode-now-stays-on-the-full-canvas-matches-edit-visu.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-new-web-designs-open-at-desktop-size-and-adapt-across-mobile.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-tweaks-now-link-to-full-documentation.md +6 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
- package/corpus/templates/mail/app/i18n/ar-SA.ts +3 -3
- package/corpus/templates/mail/app/i18n/de-DE.ts +3 -3
- package/corpus/templates/mail/app/i18n/en-US.ts +4 -3
- package/corpus/templates/mail/app/i18n/es-ES.ts +3 -3
- package/corpus/templates/mail/app/i18n/fr-FR.ts +3 -3
- package/corpus/templates/mail/app/i18n/hi-IN.ts +3 -3
- package/corpus/templates/mail/app/i18n/ja-JP.ts +3 -3
- package/corpus/templates/mail/app/i18n/ko-KR.ts +3 -3
- package/corpus/templates/mail/app/i18n/pt-BR.ts +3 -3
- package/corpus/templates/mail/app/i18n/zh-CN.ts +3 -3
- package/corpus/templates/mail/app/i18n/zh-TW.ts +3 -3
- package/corpus/templates/mail/changelog/2026-07-10-slack-intake-marked-legacy.md +6 -0
- package/corpus/templates/mail/server/lib/env-config.ts +5 -4
- package/corpus/templates/plan/package.json +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +20 -0
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +36 -18
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +3 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +200 -28
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/chat/tool-call-display.js +1 -1
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/chat/use-reconnect-reader-owner.d.ts +11 -0
- package/dist/client/chat/use-reconnect-reader-owner.d.ts.map +1 -0
- package/dist/client/chat/use-reconnect-reader-owner.js +21 -0
- package/dist/client/chat/use-reconnect-reader-owner.js.map +1 -0
- package/dist/client/guided-questions.d.ts.map +1 -1
- package/dist/client/guided-questions.js +5 -1
- package/dist/client/guided-questions.js.map +1 -1
- package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
- package/dist/client/integrations/IntegrationsPanel.js +5 -7
- package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +67 -20
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/connections/catalog.d.ts +2 -2
- package/dist/connections/catalog.js +2 -2
- package/dist/connections/catalog.js.map +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +3 -1
- package/dist/deploy/build.js.map +1 -1
- package/dist/integrations/catalog.js +2 -2
- package/dist/integrations/catalog.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +1 -1
- package/dist/server/auth.js.map +1 -1
- package/dist/server/credential-provider.d.ts +5 -3
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +7 -3
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/shared/cache-control.d.ts +8 -6
- package/dist/shared/cache-control.d.ts.map +1 -1
- package/dist/shared/cache-control.js +8 -3
- package/dist/shared/cache-control.js.map +1 -1
- package/docs/content/locales/ar-SA/template-design.mdx +13 -0
- package/docs/content/locales/de-DE/template-design.mdx +18 -0
- package/docs/content/locales/es-ES/template-design.mdx +16 -0
- package/docs/content/locales/fr-FR/template-design.mdx +17 -0
- package/docs/content/locales/hi-IN/template-design.mdx +16 -0
- package/docs/content/locales/ja-JP/template-design.mdx +17 -0
- package/docs/content/locales/ko-KR/template-design.mdx +15 -0
- package/docs/content/locales/pt-BR/template-design.mdx +16 -0
- package/docs/content/locales/zh-CN/template-design.mdx +11 -0
- package/docs/content/locales/zh-TW/template-design.mdx +12 -0
- package/docs/content/template-design.mdx +16 -0
- package/package.json +2 -2
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
updateGenerationSessionWithSavedFiles,
|
|
34
34
|
} from "../shared/generation-session.js";
|
|
35
35
|
import { assertLockedLayersPreserved } from "../shared/locked-layers.js";
|
|
36
|
+
import { widthToPrefix } from "../shared/responsive-classes.js";
|
|
36
37
|
import { annotateScreenHtmlForPersist } from "../shared/screen-annotation.js";
|
|
37
38
|
|
|
38
39
|
/** Editor deep link so external agents can surface "Open design". */
|
|
@@ -54,6 +55,43 @@ function isRenderableDesignFile(file: {
|
|
|
54
55
|
);
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
type GenerationViewport = "mobile" | "tablet" | "desktop";
|
|
59
|
+
|
|
60
|
+
const DEFAULT_GENERATION_VIEWPORT: GenerationViewport = "desktop";
|
|
61
|
+
const GENERATION_VIEWPORT_SIZES: Record<
|
|
62
|
+
GenerationViewport,
|
|
63
|
+
{ width: number; height: number }
|
|
64
|
+
> = {
|
|
65
|
+
mobile: { width: 390, height: 844 },
|
|
66
|
+
tablet: { width: 768, height: 1024 },
|
|
67
|
+
desktop: { width: 1440, height: 1024 },
|
|
68
|
+
};
|
|
69
|
+
const GENERATED_FRAME_GAP = 96;
|
|
70
|
+
const DEFAULT_RESPONSIVE_BREAKPOINTS = [390, 768, 1440].map((widthPx) => ({
|
|
71
|
+
id: `generated-${widthPx}`,
|
|
72
|
+
label: widthPx === 390 ? "Mobile" : widthPx === 768 ? "Tablet" : "Desktop",
|
|
73
|
+
widthPx,
|
|
74
|
+
prefix: widthToPrefix(widthPx),
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
function hasBreakpointSet(value: unknown): boolean {
|
|
78
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const breakpoints = (value as { breakpoints?: unknown }).breakpoints;
|
|
82
|
+
return Array.isArray(breakpoints) && breakpoints.length > 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function nextGeneratedFrameX(
|
|
86
|
+
frames: ReturnType<typeof parseCanvasFrameGeometryById>,
|
|
87
|
+
): number {
|
|
88
|
+
return Object.values(frames).reduce((furthestRight, frame) => {
|
|
89
|
+
const x = frame.x ?? 0;
|
|
90
|
+
const width = frame.width ?? 0;
|
|
91
|
+
return Math.max(furthestRight, x + width + GENERATED_FRAME_GAP);
|
|
92
|
+
}, 0);
|
|
93
|
+
}
|
|
94
|
+
|
|
57
95
|
function jsonValuesEqual(left: unknown, right: unknown): boolean {
|
|
58
96
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
59
97
|
}
|
|
@@ -149,7 +187,15 @@ const generateDesignAgentParameters = {
|
|
|
149
187
|
canvasFrames: {
|
|
150
188
|
type: "string",
|
|
151
189
|
description:
|
|
152
|
-
"Optional JSON array of overview-canvas placements keyed by filename or fileId."
|
|
190
|
+
"Optional JSON array of overview-canvas placements keyed by filename or fileId. " +
|
|
191
|
+
"Pass explicit x/y/width/height for every generated screen; desktop is 1440x1024.",
|
|
192
|
+
},
|
|
193
|
+
primaryViewport: {
|
|
194
|
+
type: "string",
|
|
195
|
+
enum: ["mobile", "tablet", "desktop"],
|
|
196
|
+
description:
|
|
197
|
+
"The requested primary form factor. Defaults to desktop (1440x1024). " +
|
|
198
|
+
"Set this from the intake answer when no explicit canvasFrames placement is supplied.",
|
|
153
199
|
},
|
|
154
200
|
},
|
|
155
201
|
required: ["designId", "prompt", "files"],
|
|
@@ -171,6 +217,9 @@ const generateDesignAction = defineAction({
|
|
|
171
217
|
"When `designSystemId` is provided, first use `get-design-system` and apply " +
|
|
172
218
|
"its `agentContext` tokens/docs before writing the file content; do not " +
|
|
173
219
|
"treat the id alone as enough design-system context. " +
|
|
220
|
+
"Every web design must be responsive. Use a desktop 1440x1024 primary " +
|
|
221
|
+
"canvas by default, or set `primaryViewport` for an explicitly mobile- or " +
|
|
222
|
+
"tablet-primary design; this action adds responsive editor breakpoints. " +
|
|
174
223
|
"Do not report a design as ready until this action succeeds. " +
|
|
175
224
|
"When adding multiple screens or states, pass canvasFrames with filenames " +
|
|
176
225
|
"and x/y/width/height so the new screens appear placed on the overview canvas.",
|
|
@@ -304,6 +353,14 @@ const generateDesignAction = defineAction({
|
|
|
304
353
|
"Reference each screen by filename or fileId and include x/y/width/height " +
|
|
305
354
|
"from generate-screens regions or your planned canvas layout.",
|
|
306
355
|
),
|
|
356
|
+
primaryViewport: z
|
|
357
|
+
.enum(["mobile", "tablet", "desktop"])
|
|
358
|
+
.optional()
|
|
359
|
+
.default(DEFAULT_GENERATION_VIEWPORT)
|
|
360
|
+
.describe(
|
|
361
|
+
"Primary generated viewport. Defaults to desktop (1440x1024); set " +
|
|
362
|
+
"mobile or tablet only when that is the requested form factor.",
|
|
363
|
+
),
|
|
307
364
|
}),
|
|
308
365
|
mcpApp: {
|
|
309
366
|
compactCatalog: true,
|
|
@@ -323,6 +380,7 @@ const generateDesignAction = defineAction({
|
|
|
323
380
|
projectType,
|
|
324
381
|
tweaks,
|
|
325
382
|
canvasFrames,
|
|
383
|
+
primaryViewport,
|
|
326
384
|
}) => {
|
|
327
385
|
await assertAccess("design", designId, "editor");
|
|
328
386
|
if (designSystemId) {
|
|
@@ -515,7 +573,7 @@ const generateDesignAction = defineAction({
|
|
|
515
573
|
}));
|
|
516
574
|
await mutateDesignData({
|
|
517
575
|
designId,
|
|
518
|
-
mutate: (prevData) => {
|
|
576
|
+
mutate: (prevData, { updatedAt }) => {
|
|
519
577
|
const mergedData: Record<string, unknown> = {
|
|
520
578
|
...prevData,
|
|
521
579
|
lastPrompt: prompt,
|
|
@@ -525,34 +583,74 @@ const generateDesignAction = defineAction({
|
|
|
525
583
|
if (normalizedTweaks !== undefined) {
|
|
526
584
|
mergedData.tweaks = normalizedTweaks;
|
|
527
585
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
? placement.fileId
|
|
546
|
-
: undefined;
|
|
547
|
-
}
|
|
548
|
-
return placement.filename
|
|
549
|
-
? (savedByFilename.get(placement.filename)?.id ??
|
|
550
|
-
existingByName.get(placement.filename)?.id)
|
|
586
|
+
const savedByFileId = new Map(
|
|
587
|
+
savedFiles.map((file) => [file.id, file]),
|
|
588
|
+
);
|
|
589
|
+
const savedByFilename = new Map(
|
|
590
|
+
savedFiles.map((file) => [file.filename, file]),
|
|
591
|
+
);
|
|
592
|
+
const existingByFileId = new Map(
|
|
593
|
+
existingFiles.map((file) => [file.id, file]),
|
|
594
|
+
);
|
|
595
|
+
const merged = mergeCanvasFramePlacements({
|
|
596
|
+
existing: prevData.canvasFrames,
|
|
597
|
+
placements: canvasFrames ?? [],
|
|
598
|
+
resolveFileId: (placement) => {
|
|
599
|
+
if (placement.fileId) {
|
|
600
|
+
return savedByFileId.has(placement.fileId) ||
|
|
601
|
+
existingByFileId.has(placement.fileId)
|
|
602
|
+
? placement.fileId
|
|
551
603
|
: undefined;
|
|
552
|
-
}
|
|
604
|
+
}
|
|
605
|
+
return placement.filename
|
|
606
|
+
? (savedByFilename.get(placement.filename)?.id ??
|
|
607
|
+
existingByName.get(placement.filename)?.id)
|
|
608
|
+
: undefined;
|
|
609
|
+
},
|
|
610
|
+
});
|
|
611
|
+
const viewport = GENERATION_VIEWPORT_SIZES[primaryViewport];
|
|
612
|
+
let nextX = nextGeneratedFrameX(merged.canvasFrames);
|
|
613
|
+
const generationFrames = [...merged.placedFrames];
|
|
614
|
+
for (const file of savedFiles) {
|
|
615
|
+
const source = files.find(
|
|
616
|
+
(candidate) => candidate.filename === file.filename,
|
|
617
|
+
);
|
|
618
|
+
if (!source || !isRenderableDesignFile(source)) continue;
|
|
619
|
+
const current = merged.canvasFrames[file.id] ?? {};
|
|
620
|
+
if (
|
|
621
|
+
current.x !== undefined &&
|
|
622
|
+
current.y !== undefined &&
|
|
623
|
+
current.width !== undefined &&
|
|
624
|
+
current.height !== undefined
|
|
625
|
+
) {
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
const frame = {
|
|
629
|
+
x: current.x ?? nextX,
|
|
630
|
+
y: current.y ?? 0,
|
|
631
|
+
width: current.width ?? viewport.width,
|
|
632
|
+
height: current.height ?? viewport.height,
|
|
633
|
+
z: current.z ?? generationFrames.length,
|
|
634
|
+
...(current.rotation === undefined
|
|
635
|
+
? {}
|
|
636
|
+
: { rotation: current.rotation }),
|
|
637
|
+
};
|
|
638
|
+
merged.canvasFrames[file.id] = frame;
|
|
639
|
+
generationFrames.push({
|
|
640
|
+
fileId: file.id,
|
|
641
|
+
filename: file.filename,
|
|
642
|
+
frame,
|
|
553
643
|
});
|
|
554
|
-
|
|
555
|
-
|
|
644
|
+
nextX = frame.x + frame.width + GENERATED_FRAME_GAP;
|
|
645
|
+
}
|
|
646
|
+
mergedData.canvasFrames = merged.canvasFrames;
|
|
647
|
+
placedFrames = generationFrames;
|
|
648
|
+
if (!hasBreakpointSet(mergedData.breakpointSet)) {
|
|
649
|
+
mergedData.breakpointSet = {
|
|
650
|
+
id: "generated-responsive",
|
|
651
|
+
breakpoints: DEFAULT_RESPONSIVE_BREAKPOINTS,
|
|
652
|
+
};
|
|
653
|
+
mergedData.breakpointSetUpdatedAt = updatedAt;
|
|
556
654
|
}
|
|
557
655
|
return mergedData;
|
|
558
656
|
},
|
|
@@ -566,12 +664,10 @@ const generateDesignAction = defineAction({
|
|
|
566
664
|
) {
|
|
567
665
|
return false;
|
|
568
666
|
}
|
|
569
|
-
if (canvasFrames === undefined) return true;
|
|
570
|
-
|
|
571
667
|
const currentFrames = parseCanvasFrameGeometryById(
|
|
572
668
|
current.canvasFrames,
|
|
573
669
|
);
|
|
574
|
-
|
|
670
|
+
const framesApplied = Boolean(
|
|
575
671
|
placedFrames?.every(({ fileId, frame }) => {
|
|
576
672
|
const currentFrame = currentFrames[fileId];
|
|
577
673
|
return (
|
|
@@ -583,6 +679,7 @@ const generateDesignAction = defineAction({
|
|
|
583
679
|
);
|
|
584
680
|
}),
|
|
585
681
|
);
|
|
682
|
+
return framesApplied && hasBreakpointSet(current.breakpointSet);
|
|
586
683
|
},
|
|
587
684
|
});
|
|
588
685
|
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
type CanvasFramePlacement,
|
|
20
20
|
} from "../shared/canvas-frames.js";
|
|
21
21
|
import { isUniqueConstraintViolation } from "../shared/db-conflict.js";
|
|
22
|
+
import { widthToPrefix } from "../shared/responsive-classes.js";
|
|
22
23
|
import { annotateScreenHtmlForPersist } from "../shared/screen-annotation.js";
|
|
23
24
|
|
|
24
25
|
const VARIANT_GAP = 96;
|
|
@@ -27,8 +28,22 @@ const MOBILE_WIDTH = 390;
|
|
|
27
28
|
const MOBILE_HEIGHT = 844;
|
|
28
29
|
const TABLET_WIDTH = 768;
|
|
29
30
|
const TABLET_HEIGHT = 1024;
|
|
30
|
-
const DESKTOP_WIDTH =
|
|
31
|
-
const DESKTOP_HEIGHT =
|
|
31
|
+
const DESKTOP_WIDTH = 1440;
|
|
32
|
+
const DESKTOP_HEIGHT = 1024;
|
|
33
|
+
const DEFAULT_RESPONSIVE_BREAKPOINTS = [390, 768, 1440].map((widthPx) => ({
|
|
34
|
+
id: `generated-${widthPx}`,
|
|
35
|
+
label: widthPx === 390 ? "Mobile" : widthPx === 768 ? "Tablet" : "Desktop",
|
|
36
|
+
widthPx,
|
|
37
|
+
prefix: widthToPrefix(widthPx),
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
function hasBreakpointSet(value: unknown): boolean {
|
|
41
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const breakpoints = (value as { breakpoints?: unknown }).breakpoints;
|
|
45
|
+
return Array.isArray(breakpoints) && breakpoints.length > 0;
|
|
46
|
+
}
|
|
32
47
|
|
|
33
48
|
function designDeepLink(designId: string): string {
|
|
34
49
|
return buildDeepLink({
|
|
@@ -951,6 +966,15 @@ export default defineAction({
|
|
|
951
966
|
canvasFrames: mergedFrames.canvasFrames,
|
|
952
967
|
screenMetadata: previousMetadata,
|
|
953
968
|
designVariantSets: previousVariantSets,
|
|
969
|
+
...(hasBreakpointSet(current.breakpointSet)
|
|
970
|
+
? {}
|
|
971
|
+
: {
|
|
972
|
+
breakpointSet: {
|
|
973
|
+
id: "generated-responsive",
|
|
974
|
+
breakpoints: DEFAULT_RESPONSIVE_BREAKPOINTS,
|
|
975
|
+
},
|
|
976
|
+
breakpointSetUpdatedAt: updatedAt,
|
|
977
|
+
}),
|
|
954
978
|
updatedAt,
|
|
955
979
|
};
|
|
956
980
|
},
|
|
@@ -968,13 +992,17 @@ export default defineAction({
|
|
|
968
992
|
? variantSets[variantSetId]
|
|
969
993
|
: null;
|
|
970
994
|
const persistedScreens = Array.isArray(set?.screens) ? set.screens : [];
|
|
971
|
-
return
|
|
972
|
-
(
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
995
|
+
return (
|
|
996
|
+
hasBreakpointSet(current.breakpointSet) &&
|
|
997
|
+
screens.every(
|
|
998
|
+
(screen) =>
|
|
999
|
+
isRecord(canvasFrames[screen.id]) &&
|
|
1000
|
+
isRecord(metadata[screen.id]) &&
|
|
1001
|
+
persistedScreens.some(
|
|
1002
|
+
(persisted) =>
|
|
1003
|
+
isRecord(persisted) && persisted.id === screen.id,
|
|
1004
|
+
),
|
|
1005
|
+
)
|
|
978
1006
|
);
|
|
979
1007
|
},
|
|
980
1008
|
});
|
|
@@ -249,9 +249,6 @@ const EDITOR_BRIDGE_VAR_NAMES = [
|
|
|
249
249
|
"--design-editor-component-strong-color",
|
|
250
250
|
"--design-editor-component-contrast-color",
|
|
251
251
|
"--design-editor-measure-color",
|
|
252
|
-
"--background",
|
|
253
|
-
"--foreground",
|
|
254
|
-
"--border",
|
|
255
252
|
];
|
|
256
253
|
|
|
257
254
|
function readEditorBridgeThemeVars(): Record<string, string> {
|
|
@@ -1949,6 +1946,15 @@ export function DesignCanvas({
|
|
|
1949
1946
|
// iframe document here; the bridge replays inspector state after that load.
|
|
1950
1947
|
}, [content, contentKey, runtimeReplacementContent, runtimeReplacementKey]);
|
|
1951
1948
|
|
|
1949
|
+
useEffect(() => {
|
|
1950
|
+
if (!interactMode) return;
|
|
1951
|
+
// Interact renders the authoritative persisted source because the editor
|
|
1952
|
+
// bridge is intentionally absent. Keep that same source as the next
|
|
1953
|
+
// edit-mode baseline so leaving Interact cannot resurrect the older
|
|
1954
|
+
// bridge-managed snapshot and make the design visibly jump backward.
|
|
1955
|
+
setRenderedContent(content);
|
|
1956
|
+
}, [content, interactMode]);
|
|
1957
|
+
|
|
1952
1958
|
usePinchZoom({
|
|
1953
1959
|
containerRef: scrollContainerRef,
|
|
1954
1960
|
zoom,
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
type PenPath,
|
|
43
43
|
} from "@shared/pen-path";
|
|
44
44
|
import {
|
|
45
|
+
IconArrowsMaximize,
|
|
45
46
|
IconCopy,
|
|
46
47
|
IconDots,
|
|
47
48
|
IconHandClick,
|
|
@@ -393,6 +394,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
393
394
|
hiddenScreenIds = EMPTY_SCREEN_IDS,
|
|
394
395
|
lockedScreenIds = EMPTY_SCREEN_IDS,
|
|
395
396
|
fullViewScreenIds,
|
|
397
|
+
interactMode = false,
|
|
396
398
|
activeScreenHasHoveredChild = false,
|
|
397
399
|
hoveredChildScreenId,
|
|
398
400
|
directlyHoveredScreenId,
|
|
@@ -7531,8 +7533,8 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
7531
7533
|
}}
|
|
7532
7534
|
editorChromeScaleX={canvasZoom / 100}
|
|
7533
7535
|
editorChromeScaleY={canvasZoom / 100}
|
|
7534
|
-
editMode={boardEditMode}
|
|
7535
|
-
interactMode={
|
|
7536
|
+
editMode={boardEditMode && !interactMode}
|
|
7537
|
+
interactMode={interactMode}
|
|
7536
7538
|
scaleMode={boardIsActive && effectiveTool === "scale"}
|
|
7537
7539
|
clearSelectionRequest={boardClearSelectionRequest}
|
|
7538
7540
|
selectedSelector={
|
|
@@ -7602,6 +7604,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
7602
7604
|
!isBreakpointSelectionTarget(screen)
|
|
7603
7605
|
}
|
|
7604
7606
|
showFullView={fullViewIdSet.has(screen.id)}
|
|
7607
|
+
interactMode={interactMode}
|
|
7605
7608
|
isDirectlyHovered={screen.id === directlyHoveredScreenId}
|
|
7606
7609
|
isFileDragOver={
|
|
7607
7610
|
fileDragOverFrameId !== null &&
|
|
@@ -8859,6 +8862,7 @@ interface ScreenProps {
|
|
|
8859
8862
|
isSelected: boolean;
|
|
8860
8863
|
isTopScreen: boolean;
|
|
8861
8864
|
showFullView: boolean;
|
|
8865
|
+
interactMode: boolean;
|
|
8862
8866
|
isDirectlyHovered: boolean;
|
|
8863
8867
|
/** True while a native OS file drag is hovering this frame (Figma parity §1). */
|
|
8864
8868
|
isFileDragOver: boolean;
|
|
@@ -8926,6 +8930,7 @@ const Screen = memo(function Screen({
|
|
|
8926
8930
|
isSelected,
|
|
8927
8931
|
isTopScreen,
|
|
8928
8932
|
showFullView,
|
|
8933
|
+
interactMode,
|
|
8929
8934
|
isDirectlyHovered,
|
|
8930
8935
|
isFileDragOver,
|
|
8931
8936
|
hasHoveredChild,
|
|
@@ -9022,9 +9027,12 @@ const Screen = memo(function Screen({
|
|
|
9022
9027
|
const frameLabelHeight = FRAME_LABEL_HEIGHT * chromeScale;
|
|
9023
9028
|
const frameScreenWidth = geometry.width / Math.max(chromeScale, 0.001);
|
|
9024
9029
|
// Keep frame actions inside their own frame so closely spaced screens cannot
|
|
9025
|
-
// cover one another. Narrow frames collapse
|
|
9030
|
+
// cover one another. Narrow frames collapse the action to its familiar icon;
|
|
9026
9031
|
// the accessible name and native tooltip preserve the action's meaning.
|
|
9027
9032
|
const compactFullView = frameScreenWidth < FRAME_HEADER_BUTTON_COMPACT_WIDTH;
|
|
9033
|
+
const frameActionLabel = interactMode
|
|
9034
|
+
? t("multiScreenCanvas.fullView")
|
|
9035
|
+
: t("designEditor.modes.interact");
|
|
9028
9036
|
const labelInfoMaxWidth = Math.max(
|
|
9029
9037
|
64,
|
|
9030
9038
|
frameScreenWidth -
|
|
@@ -9157,8 +9165,8 @@ const Screen = memo(function Screen({
|
|
|
9157
9165
|
transformOrigin: "right center",
|
|
9158
9166
|
transition: getChromeLabelTransition(chromeSettling),
|
|
9159
9167
|
}}
|
|
9160
|
-
aria-label={
|
|
9161
|
-
title={
|
|
9168
|
+
aria-label={frameActionLabel}
|
|
9169
|
+
title={frameActionLabel}
|
|
9162
9170
|
onClick={(event) => onEdit(screen.id, event)}
|
|
9163
9171
|
onMouseDown={(event) => {
|
|
9164
9172
|
event.preventDefault();
|
|
@@ -9167,9 +9175,13 @@ const Screen = memo(function Screen({
|
|
|
9167
9175
|
onMouseEnter={() => updateDirectHover(true)}
|
|
9168
9176
|
onMouseLeave={() => updateDirectHover(false)}
|
|
9169
9177
|
>
|
|
9170
|
-
|
|
9178
|
+
{interactMode ? (
|
|
9179
|
+
<IconArrowsMaximize className="size-3 shrink-0" />
|
|
9180
|
+
) : (
|
|
9181
|
+
<IconHandClick className="size-3 shrink-0" />
|
|
9182
|
+
)}
|
|
9171
9183
|
<span className={cn("truncate", compactFullView && "sr-only")}>
|
|
9172
|
-
{
|
|
9184
|
+
{frameActionLabel}
|
|
9173
9185
|
</span>
|
|
9174
9186
|
</button>
|
|
9175
9187
|
</div>
|
|
@@ -9394,6 +9406,7 @@ const Screen = memo(function Screen({
|
|
|
9394
9406
|
renderBreakpointContent={renderBreakpointContent}
|
|
9395
9407
|
activeBreakpointWidth={screen.activeBreakpointWidth}
|
|
9396
9408
|
isScreenSelected={isSelected}
|
|
9409
|
+
interactMode={interactMode}
|
|
9397
9410
|
penActive={penActive}
|
|
9398
9411
|
creationToolActive={creationToolActive}
|
|
9399
9412
|
cullTier={cullTier}
|
|
@@ -9518,6 +9531,7 @@ function BreakpointPreviewRow({
|
|
|
9518
9531
|
renderBreakpointContent,
|
|
9519
9532
|
activeBreakpointWidth,
|
|
9520
9533
|
isScreenSelected,
|
|
9534
|
+
interactMode,
|
|
9521
9535
|
penActive,
|
|
9522
9536
|
creationToolActive,
|
|
9523
9537
|
cullTier,
|
|
@@ -9559,6 +9573,7 @@ function BreakpointPreviewRow({
|
|
|
9559
9573
|
* mirrors `Screen`'s own `isSelected`, used so a breakpoint frame's chrome
|
|
9560
9574
|
* reads as "part of a selected group" the same way the base frame does. */
|
|
9561
9575
|
isScreenSelected: boolean;
|
|
9576
|
+
interactMode: boolean;
|
|
9562
9577
|
penActive: boolean;
|
|
9563
9578
|
creationToolActive: boolean;
|
|
9564
9579
|
/** Uses the owning screen's exact culling lifecycle: never-seen/evicted
|
|
@@ -9595,6 +9610,9 @@ function BreakpointPreviewRow({
|
|
|
9595
9610
|
canEdit?: boolean;
|
|
9596
9611
|
}) {
|
|
9597
9612
|
const t = useT();
|
|
9613
|
+
const frameActionLabel = interactMode
|
|
9614
|
+
? t("multiScreenCanvas.fullView")
|
|
9615
|
+
: t("designEditor.modes.interact");
|
|
9598
9616
|
const breakpointWidths = screen.breakpointWidths ?? [];
|
|
9599
9617
|
// Place additional frames to the right of the primary, starting after the gap
|
|
9600
9618
|
let offsetX = primaryGeometry.width + BREAKPOINT_FRAME_GAP;
|
|
@@ -9884,8 +9902,8 @@ function BreakpointPreviewRow({
|
|
|
9884
9902
|
transform: `scale(${chromeScale})`,
|
|
9885
9903
|
transformOrigin: "right center",
|
|
9886
9904
|
}}
|
|
9887
|
-
aria-label={
|
|
9888
|
-
title={
|
|
9905
|
+
aria-label={frameActionLabel}
|
|
9906
|
+
title={frameActionLabel}
|
|
9889
9907
|
onClick={(e) => {
|
|
9890
9908
|
e.stopPropagation();
|
|
9891
9909
|
activateThisFrame(e);
|
|
@@ -9896,7 +9914,11 @@ function BreakpointPreviewRow({
|
|
|
9896
9914
|
e.stopPropagation();
|
|
9897
9915
|
}}
|
|
9898
9916
|
>
|
|
9899
|
-
|
|
9917
|
+
{interactMode ? (
|
|
9918
|
+
<IconArrowsMaximize className="size-3" />
|
|
9919
|
+
) : (
|
|
9920
|
+
<IconHandClick className="size-3" />
|
|
9921
|
+
)}
|
|
9900
9922
|
</button>
|
|
9901
9923
|
) : null}
|
|
9902
9924
|
<span
|
|
@@ -340,7 +340,7 @@ function OptionButton({
|
|
|
340
340
|
compact ? "px-2.5 py-1.5" : "px-3 py-2",
|
|
341
341
|
selected
|
|
342
342
|
? "border-[var(--design-editor-accent-color)] bg-[var(--design-editor-selection-color)] text-foreground"
|
|
343
|
-
: "border-[var(--design-editor-control-border)] bg-[var(--design-editor-
|
|
343
|
+
: "border-[var(--design-editor-control-border)] bg-[var(--design-editor-question-option-bg)] text-foreground hover:bg-[var(--design-editor-control-bg)]",
|
|
344
344
|
)}
|
|
345
345
|
>
|
|
346
346
|
<span
|
|
@@ -418,7 +418,7 @@ function ColorOptions({
|
|
|
418
418
|
"group inline-flex min-h-8 min-w-0 max-w-full cursor-pointer items-center gap-2 rounded-md border px-2.5 py-1.5 text-start transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)] focus-visible:ring-offset-0",
|
|
419
419
|
selected
|
|
420
420
|
? "border-[var(--design-editor-accent-color)] bg-[var(--design-editor-selection-color)] text-foreground"
|
|
421
|
-
: "border-[var(--design-editor-control-border)] bg-[var(--design-editor-
|
|
421
|
+
: "border-[var(--design-editor-control-border)] bg-[var(--design-editor-question-option-bg)] text-foreground hover:bg-[var(--design-editor-control-bg)]",
|
|
422
422
|
)}
|
|
423
423
|
>
|
|
424
424
|
<span
|
|
@@ -77,7 +77,15 @@ export function TweaksPanelContent({
|
|
|
77
77
|
data-tweaks-help
|
|
78
78
|
className="mt-2 border-t border-border/60 pt-2 !text-[10px] leading-relaxed text-muted-foreground/70"
|
|
79
79
|
>
|
|
80
|
-
{t("designEditor.tweaksHelp")}
|
|
80
|
+
{t("designEditor.tweaksHelp")}{" "}
|
|
81
|
+
<a
|
|
82
|
+
href="/docs/template-design#tweaks"
|
|
83
|
+
target="_blank"
|
|
84
|
+
rel="noreferrer"
|
|
85
|
+
className="font-medium text-foreground/80 underline-offset-2 hover:text-foreground hover:underline"
|
|
86
|
+
>
|
|
87
|
+
{t("designEditor.tweaksDocs")}
|
|
88
|
+
</a>
|
|
81
89
|
</p>
|
|
82
90
|
</div>
|
|
83
91
|
);
|
|
@@ -910,10 +910,12 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
|
|
|
910
910
|
|
|
911
911
|
function selectionTargetForHit(hit: Element | null): Element | null {
|
|
912
912
|
if (!hit || isDocumentRootElement(hit)) return hit;
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
913
|
+
// Select the deepest element under the pointer on the first click. The
|
|
914
|
+
// bridge can mint a pending node id and build a source-equivalent selector
|
|
915
|
+
// for id-less descendants, so climbing to the nearest tagged ancestor is
|
|
916
|
+
// no longer necessary and makes ordinary list labels select their parent
|
|
917
|
+
// container instead.
|
|
918
|
+
return hit;
|
|
917
919
|
}
|
|
918
920
|
|
|
919
921
|
function freshRuntimeNodeId(prefix: string): string {
|
|
@@ -1929,7 +1931,7 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
|
|
|
1929
1931
|
var transformBadge = document.createElement("div");
|
|
1930
1932
|
transformBadge.setAttribute("data-agent-native-transform-badge", "");
|
|
1931
1933
|
transformBadge.style.cssText =
|
|
1932
|
-
"position:fixed;z-index:100000;display:none;pointer-events:none;border:1px solid
|
|
1934
|
+
"position:fixed;z-index:100000;display:none;pointer-events:none;border:1px solid rgba(255,255,255,0.16);border-radius:4px;background:rgba(24,24,27,0.96);color:rgba(255,255,255,0.96);font:11px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;padding:3px 5px;box-shadow:0 8px 20px rgba(0,0,0,0.28);";
|
|
1933
1935
|
document.body.appendChild(transformBadge);
|
|
1934
1936
|
|
|
1935
1937
|
var spacingBadge = document.createElement("div");
|
|
@@ -130,6 +130,9 @@ export interface MultiScreenCanvasProps {
|
|
|
130
130
|
* transformed directly from the overview canvas. */
|
|
131
131
|
lockedScreenIds?: ReadonlySet<string> | readonly string[];
|
|
132
132
|
fullViewScreenIds?: string[];
|
|
133
|
+
/** Lets every live frame receive native pointer interaction while the
|
|
134
|
+
* overview camera and frame chrome remain available. */
|
|
135
|
+
interactMode?: boolean;
|
|
133
136
|
activeScreenHasHoveredChild?: boolean;
|
|
134
137
|
hoveredChildScreenId?: string | null;
|
|
135
138
|
directlyHoveredScreenId?: string | null;
|
|
@@ -84,6 +84,11 @@
|
|
|
84
84
|
--design-editor-panel-raised-bg: hsl(var(--secondary));
|
|
85
85
|
--design-editor-control-bg: hsl(var(--muted));
|
|
86
86
|
--design-editor-control-border: hsl(var(--border));
|
|
87
|
+
--design-editor-question-option-bg: color-mix(
|
|
88
|
+
in srgb,
|
|
89
|
+
var(--design-editor-control-bg) 58%,
|
|
90
|
+
transparent
|
|
91
|
+
);
|
|
87
92
|
--design-editor-canvas-bg: hsl(0 0% 92%);
|
|
88
93
|
--design-editor-skeleton-canvas-bg: hsl(0 0% 92%);
|
|
89
94
|
--design-editor-skeleton-panel-ghost-bg: color-mix(
|
|
@@ -162,6 +167,11 @@
|
|
|
162
167
|
--design-editor-panel-raised-bg: hsl(0 0% 18%);
|
|
163
168
|
--design-editor-control-bg: hsl(0 0% 18%);
|
|
164
169
|
--design-editor-control-border: hsl(0 0% 24%);
|
|
170
|
+
--design-editor-question-option-bg: color-mix(
|
|
171
|
+
in srgb,
|
|
172
|
+
var(--design-editor-control-bg) 58%,
|
|
173
|
+
transparent
|
|
174
|
+
);
|
|
165
175
|
--design-editor-canvas-bg: hsl(0 0% 10%);
|
|
166
176
|
--design-editor-skeleton-canvas-bg: hsl(0 0% 11%);
|
|
167
177
|
}
|
|
@@ -16,6 +16,9 @@ function designQuestionsStateKey(designId: string | undefined): string {
|
|
|
16
16
|
return designId ? `show-questions:${designId}` : "show-questions";
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
const RESPONSIVE_GENERATION_REQUIREMENTS =
|
|
20
|
+
'Responsive behavior is mandatory for every web design. Read the form-factor answer above: for Desktop or Both/responsive, call generate-design with `primaryViewport: "desktop"` and a 1440x1024 canvas frame; use `primaryViewport: "mobile"` only for an explicitly mobile-primary choice. Use mobile-first responsive CSS, then take desktop and mobile screenshots and fix any overflow before reporting the design complete.';
|
|
21
|
+
|
|
19
22
|
/**
|
|
20
23
|
* Polls design-scoped question state. When the agent writes structured
|
|
21
24
|
* questions, the editor surfaces a full-canvas overlay for only this design.
|
|
@@ -40,6 +43,8 @@ export function useQuestionFlow(
|
|
|
40
43
|
"Answers:",
|
|
41
44
|
formattedAnswers,
|
|
42
45
|
"",
|
|
46
|
+
RESPONSIVE_GENERATION_REQUIREMENTS,
|
|
47
|
+
"",
|
|
43
48
|
designId
|
|
44
49
|
? 'Now continue the design. Honor any answer about variations: if the user asked to explore options, call present-design-variants with 2-5 concise directions using label, description, accentColor, and feature bullets; omit large content HTML when needed because the action can render compact representative screens - wait for their chat pick, delete each unchosen variant screen at most once, call get-design-snapshot exactly once with fileId for the kept screen, then call edit-design exactly once on that same fileId in a bounded pass. Use mode "replace-file" when expanding the representative placeholder into a complete but compact product UI in the chosen direction. Prioritize the primary workflow and render secondary details as visible controls, states, or affordances if the feature list is too large for one reliable edit. Do not repeat delete/snapshot cycles. Do not call generate-design after a variant pick. Stop after the first successful edit-design save. Otherwise call generate-design with one complete, renderable index.html first. Do not ask another question unless a required decision is still genuinely missing.'
|
|
45
50
|
: "Now continue the design. Honor any answer about variations: use variants only if requested; otherwise generate one polished direction.",
|
|
@@ -89,6 +94,8 @@ export function useQuestionFlow(
|
|
|
89
94
|
"Answers:",
|
|
90
95
|
formattedAnswers,
|
|
91
96
|
"",
|
|
97
|
+
RESPONSIVE_GENERATION_REQUIREMENTS,
|
|
98
|
+
"",
|
|
92
99
|
designId
|
|
93
100
|
? 'Now continue the design. Honor any answer about variations: if the user asked to explore options, call present-design-variants with 2-5 concise directions using label, description, accentColor, and feature bullets; omit large content HTML when needed because the action can render compact representative screens - wait for their chat pick, delete each unchosen variant screen at most once, call get-design-snapshot exactly once with fileId for the kept screen, then call edit-design exactly once on that same fileId in a bounded pass. Use mode "replace-file" when expanding the representative placeholder into a complete but compact product UI in the chosen direction. Prioritize the primary workflow and render secondary details as visible controls, states, or affordances if the feature list is too large for one reliable edit. Do not repeat delete/snapshot cycles. Do not call generate-design after a variant pick. Stop after the first successful edit-design save. Otherwise call generate-design with one complete, renderable index.html first. Do not ask another question unless a required decision is still genuinely missing.'
|
|
94
101
|
: "Now continue the design. Honor any answer about variations: use variants only if requested; otherwise generate one polished direction.",
|
|
@@ -105,8 +112,8 @@ export function useQuestionFlow(
|
|
|
105
112
|
sendContinuation(
|
|
106
113
|
"Skip the questions — decide for me.",
|
|
107
114
|
designId
|
|
108
|
-
? `The user skipped the pre-generation questions for design ${designId}. Proceed with reasonable defaults. Generate one polished first direction unless the original prompt explicitly requested options.`
|
|
109
|
-
:
|
|
115
|
+
? `The user skipped the pre-generation questions for design ${designId}. Proceed with reasonable defaults. ${RESPONSIVE_GENERATION_REQUIREMENTS} Generate one polished first direction unless the original prompt explicitly requested options.`
|
|
116
|
+
: `The user skipped the pre-generation questions. Proceed with reasonable defaults. ${RESPONSIVE_GENERATION_REQUIREMENTS} Generate one polished first direction unless the original prompt explicitly requested options.`,
|
|
110
117
|
);
|
|
111
118
|
}, [designId, sendContinuation]);
|
|
112
119
|
|