@agent-native/core 0.163.4 → 0.164.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 +1 -1
- package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +29 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -2
- package/corpus/templates/analytics/netlify.toml +2 -0
- package/corpus/templates/assets/netlify.toml +2 -0
- package/corpus/templates/brain/netlify.toml +2 -0
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +2 -1
- package/corpus/templates/calendar/changelog/2026-08-19-calendar-page-loads-stay-fast-after-idle-periods.md +6 -0
- package/corpus/templates/calendar/netlify.toml +2 -0
- package/corpus/templates/chat/netlify.toml +2 -0
- package/corpus/templates/clips/app/components/player/player-controls.tsx +3 -3
- package/corpus/templates/clips/app/components/player/reaction-emojis.ts +12 -0
- package/corpus/templates/clips/app/components/player/reactions-tray.tsx +90 -9
- package/corpus/templates/clips/app/components/player/scrubber.tsx +43 -10
- package/corpus/templates/clips/app/components/player/video-player.tsx +105 -15
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +1 -2
- package/corpus/templates/clips/app/routes/_index.tsx +10 -2
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +13 -8
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +15 -8
- package/corpus/templates/clips/netlify.toml +2 -0
- package/corpus/templates/clips/server/routes/[...page].head.ts +5 -0
- package/corpus/templates/content/netlify.toml +2 -0
- package/corpus/templates/design/.agents/skills/design-editor-architecture/SKILL.md +103 -0
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +2 -1
- package/corpus/templates/design/AGENTS.md +3 -1
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +3 -2
- package/corpus/templates/design/app/components/design/editor/AgentNativeMenuMark.tsx +25 -0
- package/corpus/templates/design/app/components/design/editor/DesignBottomToolbar.tsx +411 -0
- package/corpus/templates/design/app/components/design/editor/DesignCollaborators.tsx +153 -0
- package/corpus/templates/design/app/components/design/editor/DesignWorkspaceRail.tsx +231 -0
- package/corpus/templates/design/app/components/design/editor/MakeRealDialog.tsx +223 -0
- package/corpus/templates/design/app/components/design/editor/PendingScreenDeletionDialog.tsx +58 -0
- package/corpus/templates/design/app/components/design/editor/PendingVisualStyleWarningDialog.tsx +56 -0
- package/corpus/templates/design/app/components/design/editor/ReadOnlyEditorPanel.tsx +23 -0
- package/corpus/templates/design/app/components/design/editor/SaveTemplateDialog.tsx +161 -0
- package/corpus/templates/design/app/components/design/editor/toolbar-controls.tsx +185 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +20913 -0
- package/corpus/templates/design/app/pages/design-editor/command-types.ts +120 -0
- package/corpus/templates/design/app/pages/design-editor/commands/README.md +193 -0
- package/corpus/templates/design/app/pages/design-editor/commands/add-auto-layout.ts +416 -0
- package/corpus/templates/design/app/pages/design-editor/commands/add-screen.ts +112 -0
- package/corpus/templates/design/app/pages/design-editor/commands/align-selection.ts +183 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +243 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +236 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +304 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-pending-visual-styles-with-agent.ts +376 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-to-source.ts +132 -0
- package/corpus/templates/design/app/pages/design-editor/commands/can-move-layer.ts +117 -0
- package/corpus/templates/design/app/pages/design-editor/commands/change-selected-z-index.ts +212 -0
- package/corpus/templates/design/app/pages/design-editor/commands/commit-relative-style-delta-to-selected-layers.ts +176 -0
- package/corpus/templates/design/app/pages/design-editor/commands/commit-styles-to-selected-layers.ts +175 -0
- package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +710 -0
- package/corpus/templates/design/app/pages/design-editor/commands/confirm-make-real.ts +99 -0
- package/corpus/templates/design/app/pages/design-editor/commands/copy-as-figma-svg.ts +95 -0
- package/corpus/templates/design/app/pages/design-editor/commands/copy-selection.ts +156 -0
- package/corpus/templates/design/app/pages/design-editor/commands/create-primitive.ts +291 -0
- package/corpus/templates/design/app/pages/design-editor/commands/create-screen-frame.ts +131 -0
- package/corpus/templates/design/app/pages/design-editor/commands/cross-screen-element-drop.ts +577 -0
- package/corpus/templates/design/app/pages/design-editor/commands/delete-files.ts +364 -0
- package/corpus/templates/design/app/pages/design-editor/commands/delete-selection.ts +521 -0
- package/corpus/templates/design/app/pages/design-editor/commands/detach-instance-menu-action.ts +88 -0
- package/corpus/templates/design/app/pages/design-editor/commands/distribute-selection.ts +114 -0
- package/corpus/templates/design/app/pages/design-editor/commands/download-all-screens-pdf.ts +116 -0
- package/corpus/templates/design/app/pages/design-editor/commands/download-pdf.ts +103 -0
- package/corpus/templates/design/app/pages/design-editor/commands/download-svg.ts +159 -0
- package/corpus/templates/design/app/pages/design-editor/commands/duplicate-screen.ts +201 -0
- package/corpus/templates/design/app/pages/design-editor/commands/duplicate-selection.ts +279 -0
- package/corpus/templates/design/app/pages/design-editor/commands/editor-paste.ts +81 -0
- package/corpus/templates/design/app/pages/design-editor/commands/enter-hotkey.ts +155 -0
- package/corpus/templates/design/app/pages/design-editor/commands/enter-single-screen.ts +168 -0
- package/corpus/templates/design/app/pages/design-editor/commands/escape-hotkey.ts +212 -0
- package/corpus/templates/design/app/pages/design-editor/commands/frame-selection.ts +119 -0
- package/corpus/templates/design/app/pages/design-editor/commands/geometry-commit.ts +169 -0
- package/corpus/templates/design/app/pages/design-editor/commands/get-selected-layer-snapshots.ts +177 -0
- package/corpus/templates/design/app/pages/design-editor/commands/group-selection.ts +127 -0
- package/corpus/templates/design/app/pages/design-editor/commands/iframe-context-menu.ts +121 -0
- package/corpus/templates/design/app/pages/design-editor/commands/import-figma-clipboard-into-design.ts +119 -0
- package/corpus/templates/design/app/pages/design-editor/commands/layer-marquee-selection-change.ts +163 -0
- package/corpus/templates/design/app/pages/design-editor/commands/layer-move-to-screen.ts +331 -0
- package/corpus/templates/design/app/pages/design-editor/commands/layer-move.ts +517 -0
- package/corpus/templates/design/app/pages/design-editor/commands/layer-rename.ts +247 -0
- package/corpus/templates/design/app/pages/design-editor/commands/layer-selection-change.ts +161 -0
- package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +122 -0
- package/corpus/templates/design/app/pages/design-editor/commands/nudge-selection.ts +213 -0
- package/corpus/templates/design/app/pages/design-editor/commands/overview-primitive-reparent.ts +318 -0
- package/corpus/templates/design/app/pages/design-editor/commands/paste-copied-screens.ts +114 -0
- package/corpus/templates/design/app/pages/design-editor/commands/paste-over-selection.ts +70 -0
- package/corpus/templates/design/app/pages/design-editor/commands/paste-selection.ts +436 -0
- package/corpus/templates/design/app/pages/design-editor/commands/paste-to-replace.ts +135 -0
- package/corpus/templates/design/app/pages/design-editor/commands/pasted-image-files.ts +209 -0
- package/corpus/templates/design/app/pages/design-editor/commands/persist-frame-geometry-save.ts +113 -0
- package/corpus/templates/design/app/pages/design-editor/commands/persist-tweak-save.ts +160 -0
- package/corpus/templates/design/app/pages/design-editor/commands/primitive-created.ts +168 -0
- package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-layer-state-edit.ts +149 -0
- package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-structure-edit.ts +200 -0
- package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-text-edit.ts +152 -0
- package/corpus/templates/design/app/pages/design-editor/commands/record-pending-visual-style-edit.ts +247 -0
- package/corpus/templates/design/app/pages/design-editor/commands/redo.ts +1086 -0
- package/corpus/templates/design/app/pages/design-editor/commands/render-png-blob.ts +200 -0
- package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +207 -0
- package/corpus/templates/design/app/pages/design-editor/commands/screen-element-select.ts +262 -0
- package/corpus/templates/design/app/pages/design-editor/commands/screen-text-content-change.ts +216 -0
- package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-duplicate-change.ts +108 -0
- package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-structure-change.ts +280 -0
- package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-style-change.ts +151 -0
- package/corpus/templates/design/app/pages/design-editor/commands/send-overview-annotations.ts +88 -0
- package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-move-semantic-handoff.ts +122 -0
- package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-semantic-handoff.ts +143 -0
- package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-state-semantic-handoff.ts +115 -0
- package/corpus/templates/design/app/pages/design-editor/commands/start-retry-generation.ts +152 -0
- package/corpus/templates/design/app/pages/design-editor/commands/start-sidebar-resize.ts +99 -0
- package/corpus/templates/design/app/pages/design-editor/commands/style-change.ts +152 -0
- package/corpus/templates/design/app/pages/design-editor/commands/styles-change.ts +155 -0
- package/corpus/templates/design/app/pages/design-editor/commands/suggest-auto-layout.ts +125 -0
- package/corpus/templates/design/app/pages/design-editor/commands/text-content-change.ts +201 -0
- package/corpus/templates/design/app/pages/design-editor/commands/tidy-up.ts +123 -0
- package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-hidden.ts +167 -0
- package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-locked.ts +175 -0
- package/corpus/templates/design/app/pages/design-editor/commands/toggle-motion-keyframe.ts +99 -0
- package/corpus/templates/design/app/pages/design-editor/commands/tweak-prompt-submit.ts +84 -0
- package/corpus/templates/design/app/pages/design-editor/commands/undo.ts +906 -0
- package/corpus/templates/design/app/pages/design-editor/commands/ungroup-selection.ts +170 -0
- package/corpus/templates/design/app/pages/design-editor/commands/visual-duplicate-change.ts +119 -0
- package/corpus/templates/design/app/pages/design-editor/commands/visual-structure-change.ts +274 -0
- package/corpus/templates/design/app/pages/design-editor/commands/write-frame-geometry-snapshot.ts +99 -0
- package/corpus/templates/design/app/pages/design-editor/derive/design-breakpoints.ts +56 -0
- package/corpus/templates/design/app/pages/design-editor/derive/overview-screens.ts +146 -0
- package/corpus/templates/design/app/pages/design-editor/domains/use-tweaks.ts +326 -0
- package/corpus/templates/design/app/pages/design-editor/editor-constants.ts +54 -0
- package/corpus/templates/design/app/pages/design-editor/editor-helpers.ts +183 -0
- package/corpus/templates/design/app/pages/design-editor/effects/adopt-db-file-content.ts +270 -0
- package/corpus/templates/design/app/pages/design-editor/effects/mirror-selection-to-agent-chat.ts +134 -0
- package/corpus/templates/design/app/pages/design-editor/effects/motion-autosave.ts +335 -0
- package/corpus/templates/design/app/pages/design-editor/effects/observe-collab-text.ts +183 -0
- package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts +252 -0
- package/corpus/templates/design/app/pages/design-editor/effects/resume-pending-generation.ts +178 -0
- package/corpus/templates/design/app/pages/design-editor/effects/seed-collab-content.ts +157 -0
- package/corpus/templates/design/app/pages/design-editor/preview-html.ts +19 -0
- package/corpus/templates/design/netlify.toml +2 -0
- package/corpus/templates/design/shared/board-file.ts +2 -1
- package/corpus/templates/dispatch/netlify.toml +2 -0
- package/corpus/templates/forms/netlify.toml +2 -0
- package/corpus/templates/mail/netlify.toml +2 -0
- package/corpus/templates/plan/netlify.toml +2 -0
- package/corpus/templates/slides/netlify.toml +2 -0
- package/dist/agent/engine/builder-engine.d.ts +6 -4
- package/dist/agent/engine/builder-engine.js +91 -26
- package/dist/agent/engine/registry.js +16 -0
- package/dist/cli/code-agent-agent-tools.d.ts +2 -0
- package/dist/cli/code-agent-agent-tools.js +188 -0
- package/dist/cli/code-agent-collaboration.d.ts +34 -0
- package/dist/cli/code-agent-collaboration.js +104 -0
- package/dist/cli/code-agent-executor.js +6 -0
- package/dist/cli/code-agent-schedules.d.ts +54 -0
- package/dist/cli/code-agent-schedules.js +198 -0
- package/dist/client/AgentPanel.js +1 -1
- package/dist/client/ConnectBuilderCard.js +5 -2
- package/dist/client/EnvironmentBadge.js +6 -5
- package/dist/client/chat/run-recovery.js +3 -2
- package/dist/client/chat/tool-call-display.js +2 -3
- package/dist/client/components/CodeRequiredDialog.js +4 -2
- package/dist/client/i18n.js +5 -4
- package/dist/client/settings/SettingsPanel.js +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +6 -1
- package/dist/client/settings/useBuilderStatus.js +22 -33
- package/dist/client/transcription/BuilderTranscriptionCta.js +1 -1
- package/dist/code-agents/index.d.ts +2 -0
- package/dist/code-agents/index.js +2 -0
- package/dist/collab/routes.d.ts +1 -1
- package/dist/localization/shared.d.ts +2 -0
- package/dist/localization/shared.js +5 -0
- package/dist/mcp-client/oauth-client.d.ts +1 -0
- package/dist/mcp-client/oauth-client.js +50 -1
- package/dist/observability/routes.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/provider-api.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/auth-marketing-locales.d.ts +7 -0
- package/dist/server/auth-marketing-locales.js +1322 -0
- package/dist/server/auth-marketing.d.ts +1 -0
- package/dist/server/auth-marketing.js +35 -6
- package/dist/server/auth.js +4 -17
- package/dist/server/builder-browser.d.ts +22 -0
- package/dist/server/builder-browser.js +77 -1
- package/dist/server/builder-oauth.d.ts +50 -0
- package/dist/server/builder-oauth.js +263 -0
- package/dist/server/core-routes-plugin.d.ts +11 -0
- package/dist/server/core-routes-plugin.js +291 -415
- package/dist/server/credential-provider.d.ts +1 -1
- package/dist/server/credential-provider.js +1 -1
- package/dist/server/csrf.js +2 -2
- package/dist/server/onboarding-html.js +52 -120
- package/dist/settings/store.d.ts +9 -0
- package/dist/settings/store.js +19 -0
- package/dist/templates/chat/netlify.toml +2 -0
- package/docs/content/deployment.mdx +2 -0
- package/docs/content/multi-app-workspace.mdx +1 -1
- package/package.json +2 -2
- package/src/templates/chat/netlify.toml +2 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { AgentChatMessage } from "@agent-native/core/client/agent-chat";
|
|
2
|
+
import type { PromptComposerSubmitOptions } from "@agent-native/core/client/composer";
|
|
3
|
+
import type { Dispatch, RefObject, SetStateAction } from "react";
|
|
4
|
+
|
|
5
|
+
import type { UploadedFile } from "@/components/editor/PromptDialog";
|
|
6
|
+
import { patchPendingGeneration } from "@/lib/pending-generation";
|
|
7
|
+
import type { RetryablePrompt } from "@/pages/design-editor/command-types";
|
|
8
|
+
import { MAX_GENERATION_ATTEMPTS } from "@/pages/design-editor/editor-constants";
|
|
9
|
+
import {
|
|
10
|
+
designGenerationDirectives,
|
|
11
|
+
designTemplateRefinementDirectives,
|
|
12
|
+
formatUploadedFileContext,
|
|
13
|
+
imageAttachmentsFromUploadedFiles,
|
|
14
|
+
loadDesignSystemGenerationContext,
|
|
15
|
+
} from "@/pages/design-editor/generation-prompt-directives";
|
|
16
|
+
import type { DesignData } from "@/pages/design-editor/types";
|
|
17
|
+
|
|
18
|
+
export interface StartRetryGenerationArgs {
|
|
19
|
+
agentSubmit: (
|
|
20
|
+
message: string,
|
|
21
|
+
context: string,
|
|
22
|
+
options?: Omit<AgentChatMessage, "message" | "context">,
|
|
23
|
+
) => string;
|
|
24
|
+
canEditDesign: boolean;
|
|
25
|
+
clearAutoRetryTimer: () => void;
|
|
26
|
+
clearGenerationCompleteTimer: () => void;
|
|
27
|
+
design: DesignData | null;
|
|
28
|
+
generationModelRef: RefObject<{
|
|
29
|
+
model?: string;
|
|
30
|
+
engine?: string;
|
|
31
|
+
effort?: PromptComposerSubmitOptions["effort"];
|
|
32
|
+
} | null>;
|
|
33
|
+
id: string | undefined;
|
|
34
|
+
setGenerationChatTabId: Dispatch<SetStateAction<string | null>>;
|
|
35
|
+
setGenerationIssue: Dispatch<SetStateAction<string | null>>;
|
|
36
|
+
setHasPendingGeneration: Dispatch<SetStateAction<boolean>>;
|
|
37
|
+
setRetryablePrompt: Dispatch<
|
|
38
|
+
SetStateAction<{
|
|
39
|
+
prompt: string;
|
|
40
|
+
files: UploadedFile[];
|
|
41
|
+
model?: PromptComposerSubmitOptions["model"];
|
|
42
|
+
engine?: PromptComposerSubmitOptions["engine"];
|
|
43
|
+
effort?: PromptComposerSubmitOptions["effort"];
|
|
44
|
+
designSystemId?: string | null;
|
|
45
|
+
attempt?: number;
|
|
46
|
+
source?: string;
|
|
47
|
+
templateId?: string;
|
|
48
|
+
templateBaselineFiles?: Array<{ id: string; contentHash: string }>;
|
|
49
|
+
} | null>
|
|
50
|
+
>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function runStartRetryGeneration(
|
|
54
|
+
{
|
|
55
|
+
agentSubmit,
|
|
56
|
+
canEditDesign,
|
|
57
|
+
clearAutoRetryTimer,
|
|
58
|
+
clearGenerationCompleteTimer,
|
|
59
|
+
design,
|
|
60
|
+
generationModelRef,
|
|
61
|
+
id,
|
|
62
|
+
setGenerationChatTabId,
|
|
63
|
+
setGenerationIssue,
|
|
64
|
+
setHasPendingGeneration,
|
|
65
|
+
setRetryablePrompt,
|
|
66
|
+
}: StartRetryGenerationArgs,
|
|
67
|
+
promptState: RetryablePrompt,
|
|
68
|
+
attempt: number,
|
|
69
|
+
mode: "manual" | "auto",
|
|
70
|
+
) {
|
|
71
|
+
if (!id || !design || !canEditDesign) return;
|
|
72
|
+
clearAutoRetryTimer();
|
|
73
|
+
const fileContext = formatUploadedFileContext(promptState.files);
|
|
74
|
+
const images = imageAttachmentsFromUploadedFiles(promptState.files);
|
|
75
|
+
const designSystemContext = await loadDesignSystemGenerationContext(
|
|
76
|
+
promptState.designSystemId,
|
|
77
|
+
);
|
|
78
|
+
const retryLine =
|
|
79
|
+
mode === "auto"
|
|
80
|
+
? `(Automatically retrying attempt ${attempt} of ${MAX_GENERATION_ATTEMPTS} — the previous attempt did not complete.)`
|
|
81
|
+
: "(Retrying — the previous attempt did not complete.)";
|
|
82
|
+
const context = [
|
|
83
|
+
promptState.templateId
|
|
84
|
+
? `The user picked the "${promptState.source ?? "template"}" template (id: "${promptState.templateId}").`
|
|
85
|
+
: `The user has design "${id}" (title: "${design.title}") open and wants to fill it with design files.`,
|
|
86
|
+
`User request: "${promptState.prompt}"`,
|
|
87
|
+
promptState.designSystemId
|
|
88
|
+
? `Design system id: "${promptState.designSystemId}"`
|
|
89
|
+
: "",
|
|
90
|
+
designSystemContext,
|
|
91
|
+
fileContext,
|
|
92
|
+
"",
|
|
93
|
+
retryLine,
|
|
94
|
+
...(promptState.templateId
|
|
95
|
+
? designTemplateRefinementDirectives(
|
|
96
|
+
id,
|
|
97
|
+
promptState.templateId,
|
|
98
|
+
promptState.designSystemId,
|
|
99
|
+
)
|
|
100
|
+
: designGenerationDirectives(id, promptState.designSystemId)),
|
|
101
|
+
].join("\n");
|
|
102
|
+
clearGenerationCompleteTimer();
|
|
103
|
+
setGenerationIssue(null);
|
|
104
|
+
const startedAt = Date.now();
|
|
105
|
+
patchPendingGeneration(id, {
|
|
106
|
+
prompt: promptState.prompt,
|
|
107
|
+
files: promptState.files,
|
|
108
|
+
title: design.title,
|
|
109
|
+
designSystemId: promptState.designSystemId,
|
|
110
|
+
model: promptState.model,
|
|
111
|
+
engine: promptState.engine,
|
|
112
|
+
effort: promptState.effort,
|
|
113
|
+
source: promptState.source,
|
|
114
|
+
templateId: promptState.templateId,
|
|
115
|
+
templateBaselineFiles: promptState.templateBaselineFiles,
|
|
116
|
+
attempt,
|
|
117
|
+
startedAt,
|
|
118
|
+
});
|
|
119
|
+
setHasPendingGeneration(true);
|
|
120
|
+
setRetryablePrompt(null);
|
|
121
|
+
generationModelRef.current = {
|
|
122
|
+
model: promptState.model,
|
|
123
|
+
engine: promptState.engine,
|
|
124
|
+
effort: promptState.effort,
|
|
125
|
+
};
|
|
126
|
+
const runTabId = agentSubmit(
|
|
127
|
+
`Generate design for "${design.title}": ${promptState.prompt}`,
|
|
128
|
+
context,
|
|
129
|
+
{
|
|
130
|
+
model: promptState.model,
|
|
131
|
+
engine: promptState.engine,
|
|
132
|
+
effort: promptState.effort,
|
|
133
|
+
images,
|
|
134
|
+
},
|
|
135
|
+
);
|
|
136
|
+
setGenerationChatTabId(runTabId);
|
|
137
|
+
patchPendingGeneration(id, {
|
|
138
|
+
prompt: promptState.prompt,
|
|
139
|
+
files: promptState.files,
|
|
140
|
+
title: design.title,
|
|
141
|
+
designSystemId: promptState.designSystemId,
|
|
142
|
+
model: promptState.model,
|
|
143
|
+
engine: promptState.engine,
|
|
144
|
+
effort: promptState.effort,
|
|
145
|
+
source: promptState.source,
|
|
146
|
+
templateId: promptState.templateId,
|
|
147
|
+
templateBaselineFiles: promptState.templateBaselineFiles,
|
|
148
|
+
attempt,
|
|
149
|
+
runTabId,
|
|
150
|
+
startedAt,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Dispatch,
|
|
3
|
+
PointerEvent as ReactPointerEvent,
|
|
4
|
+
RefObject,
|
|
5
|
+
SetStateAction,
|
|
6
|
+
} from "react";
|
|
7
|
+
|
|
8
|
+
import type { DesignLeftPanel } from "@/pages/design-editor/types";
|
|
9
|
+
|
|
10
|
+
export interface StartSidebarResizeArgs {
|
|
11
|
+
activeLeftPanel: DesignLeftPanel;
|
|
12
|
+
leftSidebarContentRef: RefObject<HTMLDivElement | null>;
|
|
13
|
+
leftSidebarWidth: number;
|
|
14
|
+
rightSidebarContentRef: RefObject<HTMLDivElement | null>;
|
|
15
|
+
rightSidebarWidth: number;
|
|
16
|
+
setLeftSidebarWidth: Dispatch<SetStateAction<number>>;
|
|
17
|
+
setRightSidebarWidth: Dispatch<SetStateAction<number>>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function runStartSidebarResize(
|
|
21
|
+
{
|
|
22
|
+
activeLeftPanel,
|
|
23
|
+
leftSidebarContentRef,
|
|
24
|
+
leftSidebarWidth,
|
|
25
|
+
rightSidebarContentRef,
|
|
26
|
+
rightSidebarWidth,
|
|
27
|
+
setLeftSidebarWidth,
|
|
28
|
+
setRightSidebarWidth,
|
|
29
|
+
}: StartSidebarResizeArgs,
|
|
30
|
+
side: "left" | "right",
|
|
31
|
+
event: ReactPointerEvent<HTMLDivElement>,
|
|
32
|
+
) {
|
|
33
|
+
event.preventDefault();
|
|
34
|
+
event.stopPropagation();
|
|
35
|
+
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
36
|
+
const startX = event.clientX;
|
|
37
|
+
const codePanelOpen = side === "left" && activeLeftPanel === "code";
|
|
38
|
+
const startWidth =
|
|
39
|
+
side === "left"
|
|
40
|
+
? codePanelOpen
|
|
41
|
+
? Math.max(leftSidebarWidth, 640)
|
|
42
|
+
: Math.min(leftSidebarWidth, 420)
|
|
43
|
+
: rightSidebarWidth;
|
|
44
|
+
const setWidth = side === "left" ? setLeftSidebarWidth : setRightSidebarWidth;
|
|
45
|
+
const minWidth = side === "left" ? (codePanelOpen ? 520 : 220) : 240;
|
|
46
|
+
const maxWidth = side === "left" ? (codePanelOpen ? 1100 : 420) : 390;
|
|
47
|
+
const target =
|
|
48
|
+
side === "left"
|
|
49
|
+
? leftSidebarContentRef.current
|
|
50
|
+
: rightSidebarContentRef.current;
|
|
51
|
+
const previousTransition = target?.style.transition ?? "";
|
|
52
|
+
if (target) target.style.transition = "none";
|
|
53
|
+
let latestWidth = startWidth;
|
|
54
|
+
const previousCursor = document.body.style.cursor;
|
|
55
|
+
const previousUserSelect = document.body.style.userSelect;
|
|
56
|
+
const dragShield = document.createElement("div");
|
|
57
|
+
dragShield.setAttribute("data-design-sidebar-resize-shield", side);
|
|
58
|
+
dragShield.style.cssText =
|
|
59
|
+
"position:fixed;inset:0;z-index:2147483647;cursor:col-resize;background:transparent;pointer-events:auto;";
|
|
60
|
+
document.body.style.cursor = "col-resize";
|
|
61
|
+
document.body.style.userSelect = "none";
|
|
62
|
+
document.body.appendChild(dragShield);
|
|
63
|
+
|
|
64
|
+
const handleMove = (moveEvent: PointerEvent) => {
|
|
65
|
+
moveEvent.preventDefault();
|
|
66
|
+
const delta =
|
|
67
|
+
side === "left" ? moveEvent.clientX - startX : startX - moveEvent.clientX;
|
|
68
|
+
const next = Math.min(maxWidth, Math.max(minWidth, startWidth + delta));
|
|
69
|
+
latestWidth = next;
|
|
70
|
+
if (target) {
|
|
71
|
+
target.style.width = `${next}px`;
|
|
72
|
+
} else {
|
|
73
|
+
// No ref available (shouldn't normally happen) — fall back to the
|
|
74
|
+
// old per-move state update so resizing still works.
|
|
75
|
+
setWidth(next);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const cleanup = () => {
|
|
79
|
+
dragShield.removeEventListener("pointermove", handleMove);
|
|
80
|
+
dragShield.removeEventListener("pointerup", cleanup);
|
|
81
|
+
dragShield.removeEventListener("pointercancel", cleanup);
|
|
82
|
+
window.removeEventListener("pointermove", handleMove);
|
|
83
|
+
window.removeEventListener("pointerup", cleanup);
|
|
84
|
+
window.removeEventListener("pointercancel", cleanup);
|
|
85
|
+
dragShield.remove();
|
|
86
|
+
document.body.style.cursor = previousCursor;
|
|
87
|
+
document.body.style.userSelect = previousUserSelect;
|
|
88
|
+
if (target) target.style.transition = previousTransition;
|
|
89
|
+
// Commit the final width to state once, on release.
|
|
90
|
+
setWidth(latestWidth);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
dragShield.addEventListener("pointermove", handleMove);
|
|
94
|
+
dragShield.addEventListener("pointerup", cleanup);
|
|
95
|
+
dragShield.addEventListener("pointercancel", cleanup);
|
|
96
|
+
window.addEventListener("pointermove", handleMove);
|
|
97
|
+
window.addEventListener("pointerup", cleanup);
|
|
98
|
+
window.addEventListener("pointercancel", cleanup);
|
|
99
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { InteractionState } from "@shared/interaction-states";
|
|
2
|
+
import type { RefObject } from "react";
|
|
3
|
+
|
|
4
|
+
import type { StyleChangeMeta } from "@/components/design/EditPanel";
|
|
5
|
+
import type { ElementInfo } from "@/components/design/types";
|
|
6
|
+
import type { SelectedLayerTarget } from "@/pages/design-editor/code-layer-state";
|
|
7
|
+
import { shouldSkipVisualStyleCommitForPreview } from "@/pages/design-editor/editor-state";
|
|
8
|
+
|
|
9
|
+
export interface StyleChangeArgs {
|
|
10
|
+
commitInteractionStateStyles: (
|
|
11
|
+
state: InteractionState,
|
|
12
|
+
styles: Record<string, string>,
|
|
13
|
+
) => boolean;
|
|
14
|
+
commitRelativeStyleDeltaToSelectedLayers: (
|
|
15
|
+
property: string,
|
|
16
|
+
delta: number,
|
|
17
|
+
) => boolean;
|
|
18
|
+
commitStylesToSelectedLayers: (styles: Record<string, string>) => boolean;
|
|
19
|
+
commitVisualStyles: (
|
|
20
|
+
selector: string,
|
|
21
|
+
styles: Record<string, string>,
|
|
22
|
+
options?: {
|
|
23
|
+
runtimeApplied?: boolean;
|
|
24
|
+
elementInfo?: ElementInfo;
|
|
25
|
+
originalStyles?: Record<string, string>;
|
|
26
|
+
},
|
|
27
|
+
) => void;
|
|
28
|
+
handleClearBreakpointOverride: (
|
|
29
|
+
property: string,
|
|
30
|
+
maxWidthPx: number,
|
|
31
|
+
) => boolean;
|
|
32
|
+
previewInteractionStateStyles: (
|
|
33
|
+
state: InteractionState,
|
|
34
|
+
styles: Record<string, string>,
|
|
35
|
+
) => void;
|
|
36
|
+
selectedCanvasSelectorCandidates: string[];
|
|
37
|
+
selectedElement: ElementInfo | null;
|
|
38
|
+
selectedLayerTargetsRef: RefObject<SelectedLayerTarget[]>;
|
|
39
|
+
textEditingState: { active: boolean; selector?: string; hasRange?: boolean };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function runStyleChange(
|
|
43
|
+
{
|
|
44
|
+
commitInteractionStateStyles,
|
|
45
|
+
commitRelativeStyleDeltaToSelectedLayers,
|
|
46
|
+
commitStylesToSelectedLayers,
|
|
47
|
+
commitVisualStyles,
|
|
48
|
+
handleClearBreakpointOverride,
|
|
49
|
+
previewInteractionStateStyles,
|
|
50
|
+
selectedCanvasSelectorCandidates,
|
|
51
|
+
selectedElement,
|
|
52
|
+
selectedLayerTargetsRef,
|
|
53
|
+
textEditingState,
|
|
54
|
+
}: StyleChangeArgs,
|
|
55
|
+
property: string,
|
|
56
|
+
value: string,
|
|
57
|
+
meta?: StyleChangeMeta,
|
|
58
|
+
) {
|
|
59
|
+
if (meta?.interactionState) {
|
|
60
|
+
if (meta.phase === "preview") {
|
|
61
|
+
previewInteractionStateStyles(meta.interactionState, {
|
|
62
|
+
[property]: value,
|
|
63
|
+
});
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (
|
|
67
|
+
commitInteractionStateStyles(meta.interactionState, {
|
|
68
|
+
[property]: value,
|
|
69
|
+
})
|
|
70
|
+
) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (meta?.breakpointReset) {
|
|
75
|
+
handleClearBreakpointOverride(
|
|
76
|
+
meta.breakpointReset.property,
|
|
77
|
+
meta.breakpointReset.maxWidthPx,
|
|
78
|
+
);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const selector = selectedElement?.selector ?? "body";
|
|
82
|
+
if (
|
|
83
|
+
textEditingState.active &&
|
|
84
|
+
textEditingState.hasRange &&
|
|
85
|
+
textEditingState.selector === selector
|
|
86
|
+
) {
|
|
87
|
+
const sendStyleChange = (window as any).__designCanvasSendStyle;
|
|
88
|
+
if (typeof sendStyleChange === "function") {
|
|
89
|
+
sendStyleChange(selector, property, value, {
|
|
90
|
+
selectorCandidates: selectedCanvasSelectorCandidates,
|
|
91
|
+
nodeId: selectedElement?.sourceId,
|
|
92
|
+
});
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// PF12: a mid-gesture scrub/color-drag preview tick (ScrubInput's
|
|
97
|
+
// `phase: "preview"`, DesignColorPicker's per-tick `onChange`) is cheap
|
|
98
|
+
// to show live but must NOT run the expensive source commit
|
|
99
|
+
// (projection parse + HTML patch + history entry) on every tick — only
|
|
100
|
+
// the gesture's final "commit" (or a caller that never passes meta at
|
|
101
|
+
// all, e.g. keyboard/agent edits) does that. Route preview ticks
|
|
102
|
+
// through the same cheap iframe postMessage path the text-range case
|
|
103
|
+
// above already uses, and skip commitVisualStyles entirely so there is
|
|
104
|
+
// no source write — and therefore no history entry — for any preview
|
|
105
|
+
// tick. Multi-layer-selection commits (commitStylesToSelectedLayers)
|
|
106
|
+
// have no equivalent cheap multi-element preview channel, so previews
|
|
107
|
+
// for that case conservatively fall through to the existing full-commit
|
|
108
|
+
// behavior below (unchanged from before PF12).
|
|
109
|
+
if (
|
|
110
|
+
shouldSkipVisualStyleCommitForPreview({
|
|
111
|
+
phase: meta?.phase,
|
|
112
|
+
selectedLayerCount: selectedLayerTargetsRef.current.length,
|
|
113
|
+
})
|
|
114
|
+
) {
|
|
115
|
+
const sendStyleChange = (window as any).__designCanvasSendStyle;
|
|
116
|
+
if (typeof sendStyleChange === "function") {
|
|
117
|
+
sendStyleChange(selector, property, value, {
|
|
118
|
+
selectorCandidates: selectedCanvasSelectorCandidates,
|
|
119
|
+
nodeId: selectedElement?.sourceId,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
// No live bridge available for this preview tick (e.g. inactive
|
|
123
|
+
// screen) — nothing cheap to do; wait for the gesture's "commit".
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Mixed-value arrow-step parity (item 7): ScrubInput's own
|
|
127
|
+
// ScrubInputChangeMeta now carries `relativeDelta` (set on a mixed-
|
|
128
|
+
// selection arrow nudge), and EditPanel forwards that meta object
|
|
129
|
+
// straight through to onStyleChange — but StyleChangeMeta (this
|
|
130
|
+
// parameter's declared type) doesn't declare the field yet, so it's
|
|
131
|
+
// read defensively through a local cast rather than a direct property
|
|
132
|
+
// access. This works today (the field is present on the actual object
|
|
133
|
+
// at runtime) and degrades safely to "absent" if that ever changes —
|
|
134
|
+
// either way behavior falls through to the existing absolute-value
|
|
135
|
+
// paths below unchanged. Only routes through the per-node relative
|
|
136
|
+
// path for an actual multi-selection; commitRelativeStyleDeltaToSelectedLayers
|
|
137
|
+
// itself also no-ops (returns false) for a single target.
|
|
138
|
+
const relativeDelta = (meta as { relativeDelta?: number } | undefined)
|
|
139
|
+
?.relativeDelta;
|
|
140
|
+
if (typeof relativeDelta === "number") {
|
|
141
|
+
if (commitRelativeStyleDeltaToSelectedLayers(property, relativeDelta))
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// Page properties render only when there is no concrete DOM element
|
|
145
|
+
// selection. Screen/layer ids can remain in the broader selection ref
|
|
146
|
+
// while Escape exposes Page (especially after overview/breakpoint
|
|
147
|
+
// navigation); never let that stale structural selection hijack a page
|
|
148
|
+
// background/font edit away from the body.
|
|
149
|
+
if (selectedElement && commitStylesToSelectedLayers({ [property]: value }))
|
|
150
|
+
return;
|
|
151
|
+
commitVisualStyles(selector, { [property]: value });
|
|
152
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { InteractionState } from "@shared/interaction-states";
|
|
2
|
+
import type { RefObject } from "react";
|
|
3
|
+
|
|
4
|
+
import type { StyleChangeMeta } from "@/components/design/EditPanel";
|
|
5
|
+
import type { ElementInfo } from "@/components/design/types";
|
|
6
|
+
import type { SelectedLayerTarget } from "@/pages/design-editor/code-layer-state";
|
|
7
|
+
import { shouldSkipVisualStyleCommitForPreview } from "@/pages/design-editor/editor-state";
|
|
8
|
+
|
|
9
|
+
export interface StylesChangeArgs {
|
|
10
|
+
commitInteractionStateStyles: (
|
|
11
|
+
state: InteractionState,
|
|
12
|
+
styles: Record<string, string>,
|
|
13
|
+
) => boolean;
|
|
14
|
+
commitRelativeStyleDeltaToSelectedLayers: (
|
|
15
|
+
property: string,
|
|
16
|
+
delta: number,
|
|
17
|
+
) => boolean;
|
|
18
|
+
commitStylesToSelectedLayers: (styles: Record<string, string>) => boolean;
|
|
19
|
+
commitVisualStyles: (
|
|
20
|
+
selector: string,
|
|
21
|
+
styles: Record<string, string>,
|
|
22
|
+
options?: {
|
|
23
|
+
runtimeApplied?: boolean;
|
|
24
|
+
elementInfo?: ElementInfo;
|
|
25
|
+
originalStyles?: Record<string, string>;
|
|
26
|
+
},
|
|
27
|
+
) => void;
|
|
28
|
+
handleClearBreakpointOverride: (
|
|
29
|
+
property: string,
|
|
30
|
+
maxWidthPx: number,
|
|
31
|
+
) => boolean;
|
|
32
|
+
previewInteractionStateStyles: (
|
|
33
|
+
state: InteractionState,
|
|
34
|
+
styles: Record<string, string>,
|
|
35
|
+
) => void;
|
|
36
|
+
selectedCanvasSelectorCandidates: string[];
|
|
37
|
+
selectedElement: ElementInfo | null;
|
|
38
|
+
selectedLayerTargetsRef: RefObject<SelectedLayerTarget[]>;
|
|
39
|
+
textEditingState: { active: boolean; selector?: string; hasRange?: boolean };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function runStylesChange(
|
|
43
|
+
{
|
|
44
|
+
commitInteractionStateStyles,
|
|
45
|
+
commitRelativeStyleDeltaToSelectedLayers,
|
|
46
|
+
commitStylesToSelectedLayers,
|
|
47
|
+
commitVisualStyles,
|
|
48
|
+
handleClearBreakpointOverride,
|
|
49
|
+
previewInteractionStateStyles,
|
|
50
|
+
selectedCanvasSelectorCandidates,
|
|
51
|
+
selectedElement,
|
|
52
|
+
selectedLayerTargetsRef,
|
|
53
|
+
textEditingState,
|
|
54
|
+
}: StylesChangeArgs,
|
|
55
|
+
styles: Record<string, string>,
|
|
56
|
+
meta?: StyleChangeMeta,
|
|
57
|
+
) {
|
|
58
|
+
// Interaction-states phase 2 — see handleStyleChange's matching branch
|
|
59
|
+
// (and commitInteractionStateStyles's doc comment) for the full
|
|
60
|
+
// contract. Batched form: every property in this one commit lands in
|
|
61
|
+
// the SAME managed-block write (one applyFileContentUpdate call), so a
|
|
62
|
+
// multi-property commit made while a state is active (e.g. a shadow
|
|
63
|
+
// popover's X/Y/blur/spread) is still exactly one history step.
|
|
64
|
+
if (meta?.interactionState) {
|
|
65
|
+
if (meta.phase === "preview") {
|
|
66
|
+
previewInteractionStateStyles(meta.interactionState, styles);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (commitInteractionStateStyles(meta.interactionState, styles)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Item 14 — see handleStyleChange's matching branch for the full
|
|
74
|
+
// breakpointReset contract. EditPanel's BreakpointOverrideIndicator
|
|
75
|
+
// reset currently only fires through onStyleChange (a single
|
|
76
|
+
// property), but StylesChangeHandler shares the same StyleChangeMeta
|
|
77
|
+
// type, so this guards defensively for any batched caller too —
|
|
78
|
+
// breakpointReset only ever targets its own `property`, so only that
|
|
79
|
+
// one key of `styles` is relevant here.
|
|
80
|
+
if (meta?.breakpointReset) {
|
|
81
|
+
handleClearBreakpointOverride(
|
|
82
|
+
meta.breakpointReset.property,
|
|
83
|
+
meta.breakpointReset.maxWidthPx,
|
|
84
|
+
);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const selector = selectedElement?.selector ?? "body";
|
|
88
|
+
const entries = Object.entries(styles).filter(([, value]) => Boolean(value));
|
|
89
|
+
if (entries.length === 0) return;
|
|
90
|
+
// T10: mirror handleStyleChange's text-range routing here. Without
|
|
91
|
+
// this, a multi-property style commit (e.g. EditPanel's typography
|
|
92
|
+
// controls, which batch fontSize/lineHeight/etc into one call) while a
|
|
93
|
+
// text RANGE is selected mid-edit would restyle the whole element
|
|
94
|
+
// instead of just the selected range — handleStyleChange (the
|
|
95
|
+
// single-property path) already special-cases this; handleStylesChange
|
|
96
|
+
// just never got the same treatment.
|
|
97
|
+
if (
|
|
98
|
+
textEditingState.active &&
|
|
99
|
+
textEditingState.hasRange &&
|
|
100
|
+
textEditingState.selector === selector
|
|
101
|
+
) {
|
|
102
|
+
const sendStyleChange = (window as any).__designCanvasSendStyle;
|
|
103
|
+
if (typeof sendStyleChange === "function") {
|
|
104
|
+
entries.forEach(([property, value]) => {
|
|
105
|
+
sendStyleChange(selector, property, value, {
|
|
106
|
+
selectorCandidates: selectedCanvasSelectorCandidates,
|
|
107
|
+
nodeId: selectedElement?.sourceId,
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// PF12: same preview/commit split as handleStyleChange — see its
|
|
114
|
+
// comment for the full undo-safety rationale. A batched multi-property
|
|
115
|
+
// preview tick (e.g. EditPanel's shadow X/Y/blur/spread popover) is
|
|
116
|
+
// still just a live preview: send every property to the cheap iframe
|
|
117
|
+
// bridge and skip the expensive multi-property commitVisualStyles call.
|
|
118
|
+
if (
|
|
119
|
+
shouldSkipVisualStyleCommitForPreview({
|
|
120
|
+
phase: meta?.phase,
|
|
121
|
+
selectedLayerCount: selectedLayerTargetsRef.current.length,
|
|
122
|
+
})
|
|
123
|
+
) {
|
|
124
|
+
const sendStyleChange = (window as any).__designCanvasSendStyle;
|
|
125
|
+
if (typeof sendStyleChange === "function") {
|
|
126
|
+
entries.forEach(([property, value]) => {
|
|
127
|
+
sendStyleChange(selector, property, value, {
|
|
128
|
+
selectorCandidates: selectedCanvasSelectorCandidates,
|
|
129
|
+
nodeId: selectedElement?.sourceId,
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// Mixed-value arrow-step parity (item 7): see handleStyleChange's
|
|
136
|
+
// matching comment for the full defensive-read rationale. A relative
|
|
137
|
+
// delta is inherently single-valued (one scrub gesture on one field),
|
|
138
|
+
// so this only applies when the batched patch has exactly one entry —
|
|
139
|
+
// a multi-property patch (e.g. a shadow popover's X+Y+blur+spread all
|
|
140
|
+
// at once) has no single delta to apply per-node and falls through to
|
|
141
|
+
// the existing absolute-value paths unchanged.
|
|
142
|
+
const relativeDelta = (meta as { relativeDelta?: number } | undefined)
|
|
143
|
+
?.relativeDelta;
|
|
144
|
+
if (typeof relativeDelta === "number" && entries.length === 1) {
|
|
145
|
+
const [singleProperty] = entries[0]!;
|
|
146
|
+
if (commitRelativeStyleDeltaToSelectedLayers(singleProperty, relativeDelta))
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (
|
|
150
|
+
selectedElement &&
|
|
151
|
+
commitStylesToSelectedLayers(Object.fromEntries(entries))
|
|
152
|
+
)
|
|
153
|
+
return;
|
|
154
|
+
commitVisualStyles(selector, Object.fromEntries(entries));
|
|
155
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { CodeLayerNode } from "@shared/code-layer";
|
|
2
|
+
import { buildCodeLayerProjection } from "@shared/code-layer";
|
|
3
|
+
import { sourceContentHash } from "@shared/source-workspace";
|
|
4
|
+
import type { Dispatch, RefObject, SetStateAction } from "react";
|
|
5
|
+
import { toast } from "sonner";
|
|
6
|
+
|
|
7
|
+
import type { AutoLayoutSuggestion } from "@/pages/design-editor/auto-layout-suggestion";
|
|
8
|
+
import {
|
|
9
|
+
hasMeaningfulCssTransform,
|
|
10
|
+
inferAutoLayoutSuggestion,
|
|
11
|
+
} from "@/pages/design-editor/auto-layout-suggestion";
|
|
12
|
+
import type { RuntimeLayerSnapshot } from "@/pages/design-editor/command-types";
|
|
13
|
+
import type { AlignableRect } from "@/pages/design-editor/layout-operations";
|
|
14
|
+
import type { DesignFile } from "@/pages/design-editor/types";
|
|
15
|
+
|
|
16
|
+
export interface SuggestAutoLayoutArgs {
|
|
17
|
+
activeCanvasSourceType: "inline" | "localhost" | "fusion";
|
|
18
|
+
activeFile: DesignFile;
|
|
19
|
+
canEditDesign: boolean;
|
|
20
|
+
designSourceType: "inline" | "localhost" | "fusion";
|
|
21
|
+
getActiveFileSelectedNodeIds: (content: string) => string[];
|
|
22
|
+
getFreshActiveContent: () => string;
|
|
23
|
+
liveComputedLayoutForNode: (nodeId: string) => {
|
|
24
|
+
display: string;
|
|
25
|
+
transform: string;
|
|
26
|
+
rotate: string;
|
|
27
|
+
scale: string;
|
|
28
|
+
} | null;
|
|
29
|
+
rectFromCodeLayerNode: (node: CodeLayerNode) => AlignableRect;
|
|
30
|
+
runtimeLayerSnapshotsById: Record<string, RuntimeLayerSnapshot>;
|
|
31
|
+
setAutoLayoutSuggestionPreview: Dispatch<
|
|
32
|
+
SetStateAction<{
|
|
33
|
+
suggestion: AutoLayoutSuggestion;
|
|
34
|
+
sourceType: "inline" | "localhost";
|
|
35
|
+
contentHash: string;
|
|
36
|
+
screenId: string;
|
|
37
|
+
} | null>
|
|
38
|
+
>;
|
|
39
|
+
t: (key: string, options?: Record<string, unknown>) => string;
|
|
40
|
+
viewModeRef: RefObject<"single" | "overview">;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function runSuggestAutoLayout({
|
|
44
|
+
activeCanvasSourceType,
|
|
45
|
+
activeFile,
|
|
46
|
+
canEditDesign,
|
|
47
|
+
designSourceType,
|
|
48
|
+
getActiveFileSelectedNodeIds,
|
|
49
|
+
getFreshActiveContent,
|
|
50
|
+
liveComputedLayoutForNode,
|
|
51
|
+
rectFromCodeLayerNode,
|
|
52
|
+
runtimeLayerSnapshotsById,
|
|
53
|
+
setAutoLayoutSuggestionPreview,
|
|
54
|
+
t,
|
|
55
|
+
viewModeRef,
|
|
56
|
+
}: SuggestAutoLayoutArgs) {
|
|
57
|
+
if (!canEditDesign || !activeFile || viewModeRef.current !== "single") {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const resolvedSourceType = activeCanvasSourceType ?? designSourceType;
|
|
61
|
+
if (resolvedSourceType !== "inline" && resolvedSourceType !== "localhost") {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const sourceType =
|
|
65
|
+
resolvedSourceType === "localhost" ? "localhost" : "inline";
|
|
66
|
+
const sourceContent =
|
|
67
|
+
sourceType === "localhost"
|
|
68
|
+
? runtimeLayerSnapshotsById[activeFile.id]?.html
|
|
69
|
+
: getFreshActiveContent();
|
|
70
|
+
if (!sourceContent) {
|
|
71
|
+
toast.error(t("designEditor.toasts.reactSourceAnchorsLoading"));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const projection = buildCodeLayerProjection(sourceContent);
|
|
75
|
+
const nodesById = new Map(projection.nodes.map((node) => [node.id, node]));
|
|
76
|
+
const selectedIds = getActiveFileSelectedNodeIds(sourceContent);
|
|
77
|
+
if (selectedIds.length !== 1) {
|
|
78
|
+
toast(t("designEditor.autoLayoutSuggestion.selectContainer"));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const container = nodesById.get(selectedIds[0]!);
|
|
82
|
+
if (!container || container.children.length === 0) {
|
|
83
|
+
toast(t("designEditor.autoLayoutSuggestion.selectContainer"));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const containerMeasured = rectFromCodeLayerNode(container);
|
|
87
|
+
const children = container.children
|
|
88
|
+
.map((childId) => nodesById.get(childId))
|
|
89
|
+
.filter((child): child is CodeLayerNode => Boolean(child))
|
|
90
|
+
.map((child) => {
|
|
91
|
+
const computed = liveComputedLayoutForNode(child.id);
|
|
92
|
+
return {
|
|
93
|
+
...rectFromCodeLayerNode(child),
|
|
94
|
+
transformed: hasMeaningfulCssTransform({
|
|
95
|
+
transform: computed?.transform ?? child.style.transform,
|
|
96
|
+
rotate: computed?.rotate ?? child.style.rotate,
|
|
97
|
+
scale: computed?.scale ?? child.style.scale,
|
|
98
|
+
classes: child.classes,
|
|
99
|
+
}),
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
const suggestion = inferAutoLayoutSuggestion({
|
|
103
|
+
// Child measurements are parent-relative, so the container's own local
|
|
104
|
+
// content box begins at 0,0 even when the container is positioned in its
|
|
105
|
+
// parent. This prevents outer-canvas placement from becoming padding.
|
|
106
|
+
container: {
|
|
107
|
+
id: container.id,
|
|
108
|
+
x: 0,
|
|
109
|
+
y: 0,
|
|
110
|
+
width: containerMeasured.width,
|
|
111
|
+
height: containerMeasured.height,
|
|
112
|
+
},
|
|
113
|
+
children,
|
|
114
|
+
});
|
|
115
|
+
if (!suggestion) {
|
|
116
|
+
toast(t("designEditor.autoLayoutSuggestion.selectContainer"));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
setAutoLayoutSuggestionPreview({
|
|
120
|
+
suggestion,
|
|
121
|
+
sourceType,
|
|
122
|
+
contentHash: sourceContentHash(sourceContent),
|
|
123
|
+
screenId: activeFile.id,
|
|
124
|
+
});
|
|
125
|
+
}
|