@agent-native/core 0.163.4 → 0.164.3
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/components/agents/FeatureFlagsFleetPanel.tsx +78 -19
- package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +29 -0
- package/corpus/templates/analytics/app/lib/use-auto-focus-select.ts +4 -4
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +3 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +6 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -2
- package/corpus/templates/analytics/app/routes/_index.tsx +3 -2
- package/corpus/templates/analytics/app/routes/adhoc.$id.tsx +7 -2
- package/corpus/templates/analytics/app/routes/dashboard.tsx +3 -2
- package/corpus/templates/analytics/app/routes/overview.tsx +3 -2
- package/corpus/templates/analytics/app/routes/traffic.tsx +3 -2
- package/corpus/templates/analytics/changelog/2026-08-18-verify-cross-app-feature-flag-changes.md +6 -0
- package/corpus/templates/analytics/netlify.toml +2 -0
- package/corpus/templates/analytics/server/lib/workspace-feature-flags.ts +196 -48
- package/corpus/templates/analytics/server/plugins/feature-flags.ts +5 -0
- package/corpus/templates/analytics/shared/feature-flags.ts +10 -0
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +5 -1
- package/corpus/templates/assets/app/routes/brand-kits._index.tsx +4 -1
- package/corpus/templates/assets/app/routes/libraries.tsx +4 -1
- package/corpus/templates/assets/app/routes/picker.tsx +4 -1
- 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/_app.wispr.tsx +2 -1
- package/corpus/templates/clips/app/routes/_index.tsx +7 -3
- 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/app/routes/examples.tsx +2 -1
- 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/app/routes/_index.tsx +3 -2
- package/corpus/templates/forms/netlify.toml +2 -0
- package/corpus/templates/mail/app/routes/_index.tsx +3 -2
- 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/corpus/templates/tasks/app/routes/_index.tsx +2 -1
- 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/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/deploy/build.js +10 -4
- 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/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +1 -1
- package/dist/provider-api/actions/provider-api.d.ts +2 -2
- 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/server/ssr-handler.js +13 -4
- package/dist/settings/store.d.ts +9 -0
- package/dist/settings/store.js +19 -0
- package/dist/shared/cache-control.d.ts +22 -0
- package/dist/shared/cache-control.js +25 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -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
package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { setClientAppState } from "@agent-native/core/client/hooks";
|
|
2
|
+
import type { RefObject } from "react";
|
|
3
|
+
|
|
4
|
+
import type { CodeWorkbenchActiveFile } from "@/components/design/code-workbench/CodeWorkbench";
|
|
5
|
+
import type { InspectorTab } from "@/components/design/EditPanel";
|
|
6
|
+
import type { ElementInfo } from "@/components/design/types";
|
|
7
|
+
import type { ResponsiveEditScope } from "@/pages/design-editor/command-types";
|
|
8
|
+
import { DESIGN_SELECTION_ZOOM_SAVE_DELAY_MS } from "@/pages/design-editor/editor-constants";
|
|
9
|
+
import { designSelectionStateKeys } from "@/pages/design-editor/editor-helpers";
|
|
10
|
+
import type {
|
|
11
|
+
DesignData,
|
|
12
|
+
DesignFile,
|
|
13
|
+
DesignLeftPanel,
|
|
14
|
+
DesignTool,
|
|
15
|
+
EditorMode,
|
|
16
|
+
} from "@/pages/design-editor/types";
|
|
17
|
+
|
|
18
|
+
export interface PublishAgentSelectionContextArgs {
|
|
19
|
+
activeBreakpointWidthState: number | undefined;
|
|
20
|
+
activeCodeFile: CodeWorkbenchActiveFile | null;
|
|
21
|
+
activeFile: DesignFile;
|
|
22
|
+
activeInspectorTab: InspectorTab;
|
|
23
|
+
activeLeftPanel: DesignLeftPanel;
|
|
24
|
+
activeTool: DesignTool;
|
|
25
|
+
design: DesignData | null;
|
|
26
|
+
designDataJson: Record<string, unknown>;
|
|
27
|
+
designSelectionOwnerIdRef: RefObject<string>;
|
|
28
|
+
files: DesignFile[];
|
|
29
|
+
hoveredElement: ElementInfo | null;
|
|
30
|
+
id: string | undefined;
|
|
31
|
+
isSignedIn: boolean;
|
|
32
|
+
mode: EditorMode;
|
|
33
|
+
motionDockOpen: boolean;
|
|
34
|
+
pendingPersistedSelectionWriteRef: RefObject<{
|
|
35
|
+
key: string;
|
|
36
|
+
contextKey: string;
|
|
37
|
+
value: Record<string, unknown>;
|
|
38
|
+
} | null>;
|
|
39
|
+
persistedSelectionContextRef: RefObject<string | null>;
|
|
40
|
+
persistedSelectionStateRef: RefObject<string | null>;
|
|
41
|
+
persistedSelectionWriteTimerRef: RefObject<number | null>;
|
|
42
|
+
responsiveEditScope: ResponsiveEditScope;
|
|
43
|
+
selectedElement: ElementInfo | null;
|
|
44
|
+
selectedScreenIds: string[];
|
|
45
|
+
selectedStateId: string | null;
|
|
46
|
+
viewMode: "single" | "overview";
|
|
47
|
+
zoom: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function runPublishAgentSelectionContext({
|
|
51
|
+
activeBreakpointWidthState,
|
|
52
|
+
activeCodeFile,
|
|
53
|
+
activeFile,
|
|
54
|
+
activeInspectorTab,
|
|
55
|
+
activeLeftPanel,
|
|
56
|
+
activeTool,
|
|
57
|
+
design,
|
|
58
|
+
designDataJson,
|
|
59
|
+
designSelectionOwnerIdRef,
|
|
60
|
+
files,
|
|
61
|
+
hoveredElement,
|
|
62
|
+
id,
|
|
63
|
+
isSignedIn,
|
|
64
|
+
mode,
|
|
65
|
+
motionDockOpen,
|
|
66
|
+
pendingPersistedSelectionWriteRef,
|
|
67
|
+
persistedSelectionContextRef,
|
|
68
|
+
persistedSelectionStateRef,
|
|
69
|
+
persistedSelectionWriteTimerRef,
|
|
70
|
+
responsiveEditScope,
|
|
71
|
+
selectedElement,
|
|
72
|
+
selectedScreenIds,
|
|
73
|
+
selectedStateId,
|
|
74
|
+
viewMode,
|
|
75
|
+
zoom,
|
|
76
|
+
}: PublishAgentSelectionContextArgs) {
|
|
77
|
+
if (!id || !isSignedIn) return;
|
|
78
|
+
const selection = {
|
|
79
|
+
designId: id,
|
|
80
|
+
designTitle: design?.title ?? null,
|
|
81
|
+
activeFileId: activeFile?.id ?? null,
|
|
82
|
+
activeFilename: activeFile?.filename ?? null,
|
|
83
|
+
viewMode,
|
|
84
|
+
zoom,
|
|
85
|
+
screens: files.map((file) => ({
|
|
86
|
+
id: file.id,
|
|
87
|
+
filename: file.filename,
|
|
88
|
+
fileType: file.fileType,
|
|
89
|
+
})),
|
|
90
|
+
selectedScreenIds,
|
|
91
|
+
selectedElement,
|
|
92
|
+
hoveredElement,
|
|
93
|
+
mode,
|
|
94
|
+
activeTool,
|
|
95
|
+
inspectorTab: activeInspectorTab,
|
|
96
|
+
leftPanel: activeLeftPanel,
|
|
97
|
+
codeWorkspace: {
|
|
98
|
+
open: activeLeftPanel === "code",
|
|
99
|
+
backendKind: activeCodeFile?.backendKind ?? "virtual-inline",
|
|
100
|
+
activePath: activeCodeFile?.path ?? null,
|
|
101
|
+
activeFileId: activeCodeFile?.fileId ?? null,
|
|
102
|
+
dirty: activeCodeFile?.dirty ?? false,
|
|
103
|
+
versionHash: activeCodeFile?.versionHash ?? null,
|
|
104
|
+
},
|
|
105
|
+
// §8 DesignNavigationState additions — dock + breakpoint context
|
|
106
|
+
dock: { kind: "motion" as const, open: motionDockOpen },
|
|
107
|
+
motion: {
|
|
108
|
+
previewing: false,
|
|
109
|
+
playheadMs: 0,
|
|
110
|
+
timelineId: undefined as string | undefined,
|
|
111
|
+
selectedTrackId: undefined as string | undefined,
|
|
112
|
+
selectedKeyframeId: undefined as string | undefined,
|
|
113
|
+
},
|
|
114
|
+
// §8 breakpoint fields — "auto" = no specific breakpoint focused.
|
|
115
|
+
breakpoint: (activeBreakpointWidthState != null
|
|
116
|
+
? activeBreakpointWidthState < 500
|
|
117
|
+
? "mobile"
|
|
118
|
+
: activeBreakpointWidthState < 1024
|
|
119
|
+
? "tablet"
|
|
120
|
+
: "desktop"
|
|
121
|
+
: "auto") as "auto" | "mobile" | "tablet" | "desktop",
|
|
122
|
+
activeBreakpointId: (() => {
|
|
123
|
+
if (activeBreakpointWidthState == null) return undefined;
|
|
124
|
+
try {
|
|
125
|
+
const raw = (designDataJson as Record<string, unknown>)?.breakpointSet;
|
|
126
|
+
if (
|
|
127
|
+
raw &&
|
|
128
|
+
typeof raw === "object" &&
|
|
129
|
+
Array.isArray((raw as Record<string, unknown>).breakpoints)
|
|
130
|
+
) {
|
|
131
|
+
const bps = (
|
|
132
|
+
raw as { breakpoints: Array<{ id: string; widthPx: number }> }
|
|
133
|
+
).breakpoints;
|
|
134
|
+
return bps.find((b) => b.widthPx === activeBreakpointWidthState)?.id;
|
|
135
|
+
}
|
|
136
|
+
// coercion-ok: an unreadable breakpointSet means "none configured", which the undefined return already expresses.
|
|
137
|
+
} catch {
|
|
138
|
+
// ignore
|
|
139
|
+
}
|
|
140
|
+
return undefined;
|
|
141
|
+
})(),
|
|
142
|
+
responsiveEditScope,
|
|
143
|
+
breakpointSetId: (() => {
|
|
144
|
+
try {
|
|
145
|
+
const raw = (designDataJson as Record<string, unknown>)?.breakpointSet;
|
|
146
|
+
if (raw && typeof raw === "object") {
|
|
147
|
+
return (raw as Record<string, unknown>).id as string | undefined;
|
|
148
|
+
}
|
|
149
|
+
// coercion-ok: an unreadable breakpointSet means "none configured", which the undefined return already expresses.
|
|
150
|
+
} catch {
|
|
151
|
+
// ignore
|
|
152
|
+
}
|
|
153
|
+
return undefined;
|
|
154
|
+
})(),
|
|
155
|
+
// §8 — active design state (null = Default / live view)
|
|
156
|
+
selectedStateId,
|
|
157
|
+
};
|
|
158
|
+
(window as any).__designSelection = selection;
|
|
159
|
+
const persistedSelection = {
|
|
160
|
+
designId: selection.designId,
|
|
161
|
+
designTitle: selection.designTitle,
|
|
162
|
+
activeFileId: selection.activeFileId,
|
|
163
|
+
activeFilename: selection.activeFilename,
|
|
164
|
+
viewMode: selection.viewMode,
|
|
165
|
+
zoom: selection.zoom,
|
|
166
|
+
screens: selection.screens,
|
|
167
|
+
selectedScreenIds: selection.selectedScreenIds,
|
|
168
|
+
selectedElement: selection.selectedElement,
|
|
169
|
+
mode: selection.mode,
|
|
170
|
+
activeTool: selection.activeTool,
|
|
171
|
+
inspectorTab: selection.inspectorTab,
|
|
172
|
+
leftPanel: selection.leftPanel,
|
|
173
|
+
codeWorkspace: selection.codeWorkspace,
|
|
174
|
+
dock: selection.dock,
|
|
175
|
+
motion: selection.motion,
|
|
176
|
+
breakpoint: selection.breakpoint,
|
|
177
|
+
activeBreakpointId: selection.activeBreakpointId,
|
|
178
|
+
responsiveEditScope: selection.responsiveEditScope,
|
|
179
|
+
breakpointSetId: selection.breakpointSetId,
|
|
180
|
+
selectedStateId: selection.selectedStateId,
|
|
181
|
+
ownerId: designSelectionOwnerIdRef.current,
|
|
182
|
+
};
|
|
183
|
+
const persistedKey = JSON.stringify(persistedSelection);
|
|
184
|
+
const { zoom: _zoom, ...persistedContext } = persistedSelection;
|
|
185
|
+
const persistedContextKey = JSON.stringify(persistedContext);
|
|
186
|
+
const writePersistedSelection = (pending: {
|
|
187
|
+
key: string;
|
|
188
|
+
contextKey: string;
|
|
189
|
+
value: Record<string, unknown>;
|
|
190
|
+
}) => {
|
|
191
|
+
persistedSelectionStateRef.current = pending.key;
|
|
192
|
+
persistedSelectionContextRef.current = pending.contextKey;
|
|
193
|
+
for (const key of designSelectionStateKeys()) {
|
|
194
|
+
setClientAppState(key, pending.value, { keepalive: true }).catch(
|
|
195
|
+
() => {},
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
if (persistedSelectionStateRef.current === persistedKey) {
|
|
200
|
+
// A zoom gesture can return to the last persisted value before its
|
|
201
|
+
// trailing write fires. Cancel that now-stale queued intermediate.
|
|
202
|
+
if (persistedSelectionWriteTimerRef.current !== null) {
|
|
203
|
+
window.clearTimeout(persistedSelectionWriteTimerRef.current);
|
|
204
|
+
persistedSelectionWriteTimerRef.current = null;
|
|
205
|
+
}
|
|
206
|
+
pendingPersistedSelectionWriteRef.current = null;
|
|
207
|
+
} else if (persistedSelectionContextRef.current !== persistedContextKey) {
|
|
208
|
+
// Screen/selection/tool/panel changes are agent context and stay
|
|
209
|
+
// immediate. Only zoom-only churn is coalesced below.
|
|
210
|
+
if (persistedSelectionWriteTimerRef.current !== null) {
|
|
211
|
+
window.clearTimeout(persistedSelectionWriteTimerRef.current);
|
|
212
|
+
persistedSelectionWriteTimerRef.current = null;
|
|
213
|
+
}
|
|
214
|
+
pendingPersistedSelectionWriteRef.current = null;
|
|
215
|
+
writePersistedSelection({
|
|
216
|
+
key: persistedKey,
|
|
217
|
+
contextKey: persistedContextKey,
|
|
218
|
+
value: persistedSelection,
|
|
219
|
+
});
|
|
220
|
+
} else if (pendingPersistedSelectionWriteRef.current?.key !== persistedKey) {
|
|
221
|
+
// Wheel/pinch zoom can render at 60+ Hz. Writing both scoped and global
|
|
222
|
+
// application-state keys for every tick creates an avoidable request +
|
|
223
|
+
// sync-event storm; keep the immediate in-memory context above and
|
|
224
|
+
// persist only the settled zoom after a short trailing delay.
|
|
225
|
+
pendingPersistedSelectionWriteRef.current = {
|
|
226
|
+
key: persistedKey,
|
|
227
|
+
contextKey: persistedContextKey,
|
|
228
|
+
value: persistedSelection,
|
|
229
|
+
};
|
|
230
|
+
if (persistedSelectionWriteTimerRef.current !== null) {
|
|
231
|
+
window.clearTimeout(persistedSelectionWriteTimerRef.current);
|
|
232
|
+
}
|
|
233
|
+
persistedSelectionWriteTimerRef.current = window.setTimeout(() => {
|
|
234
|
+
persistedSelectionWriteTimerRef.current = null;
|
|
235
|
+
const pending = pendingPersistedSelectionWriteRef.current;
|
|
236
|
+
pendingPersistedSelectionWriteRef.current = null;
|
|
237
|
+
if (pending) writePersistedSelection(pending);
|
|
238
|
+
}, DESIGN_SELECTION_ZOOM_SAVE_DELAY_MS);
|
|
239
|
+
}
|
|
240
|
+
const el = document.documentElement;
|
|
241
|
+
el.dataset.designId = id;
|
|
242
|
+
if (activeFile?.id) el.dataset.fileId = activeFile.id;
|
|
243
|
+
el.dataset.viewMode = viewMode;
|
|
244
|
+
el.dataset.zoom = String(zoom);
|
|
245
|
+
return () => {
|
|
246
|
+
delete (window as any).__designSelection;
|
|
247
|
+
delete el.dataset.designId;
|
|
248
|
+
delete el.dataset.fileId;
|
|
249
|
+
delete el.dataset.viewMode;
|
|
250
|
+
delete el.dataset.zoom;
|
|
251
|
+
};
|
|
252
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
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 {
|
|
6
|
+
isPendingGenerationStale,
|
|
7
|
+
patchPendingGeneration,
|
|
8
|
+
readPendingGeneration,
|
|
9
|
+
} from "@/lib/pending-generation";
|
|
10
|
+
import {
|
|
11
|
+
designGenerationDirectives,
|
|
12
|
+
designIntakeQuestionDirectives,
|
|
13
|
+
designTemplateRefinementDirectives,
|
|
14
|
+
designVariantGenerationDirectives,
|
|
15
|
+
formatUploadedFileContext,
|
|
16
|
+
imageAttachmentsFromUploadedFiles,
|
|
17
|
+
loadDesignSystemGenerationContext,
|
|
18
|
+
promptRequestsVariantExploration,
|
|
19
|
+
} from "@/pages/design-editor/generation-prompt-directives";
|
|
20
|
+
import type { DesignData, DesignFile } from "@/pages/design-editor/types";
|
|
21
|
+
|
|
22
|
+
export interface ResumePendingGenerationArgs {
|
|
23
|
+
agentSubmit: (
|
|
24
|
+
message: string,
|
|
25
|
+
context: string,
|
|
26
|
+
options?: Omit<AgentChatMessage, "message" | "context">,
|
|
27
|
+
) => string;
|
|
28
|
+
clearGenerationCompleteTimer: () => void;
|
|
29
|
+
design: DesignData | null;
|
|
30
|
+
files: DesignFile[];
|
|
31
|
+
generationModelRef: RefObject<{
|
|
32
|
+
model?: string;
|
|
33
|
+
engine?: string;
|
|
34
|
+
effort?: PromptComposerSubmitOptions["effort"];
|
|
35
|
+
} | null>;
|
|
36
|
+
id: string | undefined;
|
|
37
|
+
markGenerationStale: () => void;
|
|
38
|
+
setGenerationChatTabId: Dispatch<SetStateAction<string | null>>;
|
|
39
|
+
setGenerationIssue: Dispatch<SetStateAction<string | null>>;
|
|
40
|
+
setHasPendingGeneration: Dispatch<SetStateAction<boolean>>;
|
|
41
|
+
trackAgentGeneration: (tabId: string) => void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function runResumePendingGeneration({
|
|
45
|
+
agentSubmit,
|
|
46
|
+
clearGenerationCompleteTimer,
|
|
47
|
+
design,
|
|
48
|
+
files,
|
|
49
|
+
generationModelRef,
|
|
50
|
+
id,
|
|
51
|
+
markGenerationStale,
|
|
52
|
+
setGenerationChatTabId,
|
|
53
|
+
setGenerationIssue,
|
|
54
|
+
setHasPendingGeneration,
|
|
55
|
+
trackAgentGeneration,
|
|
56
|
+
}: ResumePendingGenerationArgs) {
|
|
57
|
+
if (!id || !design) return;
|
|
58
|
+
|
|
59
|
+
const pending = readPendingGeneration(id);
|
|
60
|
+
if (!pending) {
|
|
61
|
+
setHasPendingGeneration(false);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const templateRefinement = Boolean(pending.templateId && files.length > 0);
|
|
65
|
+
if (files.length > 0 && !templateRefinement) return;
|
|
66
|
+
|
|
67
|
+
if (isPendingGenerationStale(pending)) {
|
|
68
|
+
markGenerationStale();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (pending.runTabId) {
|
|
73
|
+
setGenerationIssue(null);
|
|
74
|
+
setHasPendingGeneration(true);
|
|
75
|
+
setGenerationChatTabId(pending.runTabId);
|
|
76
|
+
trackAgentGeneration(pending.runTabId);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const prompt =
|
|
81
|
+
pending.prompt?.trim() || `Create an initial design for ${design.title}.`;
|
|
82
|
+
const uploadedFiles = Array.isArray(pending.files) ? pending.files : [];
|
|
83
|
+
const fileContext = formatUploadedFileContext(uploadedFiles);
|
|
84
|
+
const images = imageAttachmentsFromUploadedFiles(uploadedFiles);
|
|
85
|
+
const sourceContext = pending.source
|
|
86
|
+
? `The user picked the "${pending.source}" template${pending.templateId ? ` (id: "${pending.templateId}")` : ""}.`
|
|
87
|
+
: "The user just created a new empty design.";
|
|
88
|
+
const pendingDesignSystemId =
|
|
89
|
+
pending.designSystemId === undefined
|
|
90
|
+
? design.designSystemId
|
|
91
|
+
: pending.designSystemId;
|
|
92
|
+
|
|
93
|
+
if (pending.autoGenerate === false) {
|
|
94
|
+
setGenerationIssue(null);
|
|
95
|
+
setHasPendingGeneration(true);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let cancelled = false;
|
|
100
|
+
void (async () => {
|
|
101
|
+
const shouldExploreVariants = promptRequestsVariantExploration(prompt);
|
|
102
|
+
// A reference screenshot already answers the questions the intake flow
|
|
103
|
+
// asks. Spending the one turn that can see the image on a questionnaire
|
|
104
|
+
// means the turn that writes HTML never sees it.
|
|
105
|
+
const hasReferenceImages = images.length > 0;
|
|
106
|
+
const shouldSkipQuestions =
|
|
107
|
+
pending.skipQuestions === true ||
|
|
108
|
+
shouldExploreVariants ||
|
|
109
|
+
hasReferenceImages;
|
|
110
|
+
const designSystemContext = await loadDesignSystemGenerationContext(
|
|
111
|
+
pendingDesignSystemId,
|
|
112
|
+
);
|
|
113
|
+
if (cancelled) return;
|
|
114
|
+
const context = [
|
|
115
|
+
sourceContext,
|
|
116
|
+
`Design id: "${id}"`,
|
|
117
|
+
`Design title: "${design.title}"`,
|
|
118
|
+
`User request: "${prompt}"`,
|
|
119
|
+
pendingDesignSystemId
|
|
120
|
+
? `Design system id: "${pendingDesignSystemId}"`
|
|
121
|
+
: "",
|
|
122
|
+
designSystemContext,
|
|
123
|
+
fileContext,
|
|
124
|
+
"",
|
|
125
|
+
...(pending.templateId
|
|
126
|
+
? designTemplateRefinementDirectives(
|
|
127
|
+
id,
|
|
128
|
+
pending.templateId,
|
|
129
|
+
pendingDesignSystemId,
|
|
130
|
+
)
|
|
131
|
+
: shouldExploreVariants
|
|
132
|
+
? designVariantGenerationDirectives(id, pendingDesignSystemId)
|
|
133
|
+
: shouldSkipQuestions
|
|
134
|
+
? designGenerationDirectives(
|
|
135
|
+
id,
|
|
136
|
+
pendingDesignSystemId,
|
|
137
|
+
images.length,
|
|
138
|
+
)
|
|
139
|
+
: designIntakeQuestionDirectives(
|
|
140
|
+
id,
|
|
141
|
+
pendingDesignSystemId,
|
|
142
|
+
images.length,
|
|
143
|
+
)),
|
|
144
|
+
].join("\n");
|
|
145
|
+
|
|
146
|
+
clearGenerationCompleteTimer();
|
|
147
|
+
setGenerationIssue(null);
|
|
148
|
+
generationModelRef.current = {
|
|
149
|
+
model: pending.model,
|
|
150
|
+
engine: pending.engine,
|
|
151
|
+
effort: pending.effort,
|
|
152
|
+
};
|
|
153
|
+
const runTabId = agentSubmit(
|
|
154
|
+
shouldSkipQuestions
|
|
155
|
+
? `Generate design for "${design.title}": ${prompt}`
|
|
156
|
+
: `Create design: ${prompt}`,
|
|
157
|
+
context,
|
|
158
|
+
{
|
|
159
|
+
model: pending.model,
|
|
160
|
+
engine: pending.engine,
|
|
161
|
+
effort: pending.effort,
|
|
162
|
+
newTab: true,
|
|
163
|
+
images,
|
|
164
|
+
},
|
|
165
|
+
);
|
|
166
|
+
setGenerationChatTabId(runTabId);
|
|
167
|
+
patchPendingGeneration(id, {
|
|
168
|
+
runTabId,
|
|
169
|
+
attempt: pending.attempt ?? 1,
|
|
170
|
+
designSystemId: pendingDesignSystemId,
|
|
171
|
+
startedAt: Date.now(),
|
|
172
|
+
});
|
|
173
|
+
setHasPendingGeneration(true);
|
|
174
|
+
})();
|
|
175
|
+
return () => {
|
|
176
|
+
cancelled = true;
|
|
177
|
+
};
|
|
178
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import type { Dispatch, RefObject, SetStateAction } from "react";
|
|
2
|
+
import * as Y from "yjs";
|
|
3
|
+
|
|
4
|
+
import { shouldRebaseCollabDocFromStoredContent } from "@/pages/design-editor/collab-sync";
|
|
5
|
+
import { TAB_ID } from "@/pages/design-editor/editor-session";
|
|
6
|
+
import type { PreviewContentReplaceResult } from "@/pages/design-editor/editor-state";
|
|
7
|
+
import { previewContentReplaceNeedsRenderFallback } from "@/pages/design-editor/editor-state";
|
|
8
|
+
import type { DesignFile } from "@/pages/design-editor/types";
|
|
9
|
+
|
|
10
|
+
export interface SeedCollabContentArgs {
|
|
11
|
+
activeFile: DesignFile;
|
|
12
|
+
activeFileId: string | null;
|
|
13
|
+
collabContentFileIdRef: RefObject<string | null>;
|
|
14
|
+
isSynced: boolean;
|
|
15
|
+
lastAppliedFileUpdatedAtRef: RefObject<string | null>;
|
|
16
|
+
lastLocalContentRef: RefObject<string | null>;
|
|
17
|
+
latestActiveContentRef: RefObject<string | null>;
|
|
18
|
+
pendingLocalFileContentsRef: RefObject<
|
|
19
|
+
Map<
|
|
20
|
+
string,
|
|
21
|
+
{ content: string; startedAt: number; baseUpdatedAt?: string | null }
|
|
22
|
+
>
|
|
23
|
+
>;
|
|
24
|
+
replacePreviewContent: (
|
|
25
|
+
nextContent: string,
|
|
26
|
+
selector?: string | null,
|
|
27
|
+
options?: { forceFullDocument?: boolean },
|
|
28
|
+
) => PreviewContentReplaceResult;
|
|
29
|
+
setCollabContent: Dispatch<SetStateAction<string | null>>;
|
|
30
|
+
setCollabContentFileId: Dispatch<SetStateAction<string | null>>;
|
|
31
|
+
setContentRenderRevision: Dispatch<SetStateAction<number>>;
|
|
32
|
+
undoManagerRef: RefObject<Y.UndoManager | null>;
|
|
33
|
+
ydoc: Y.Doc | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function runSeedCollabContent({
|
|
37
|
+
activeFile,
|
|
38
|
+
activeFileId,
|
|
39
|
+
collabContentFileIdRef,
|
|
40
|
+
isSynced,
|
|
41
|
+
lastAppliedFileUpdatedAtRef,
|
|
42
|
+
lastLocalContentRef,
|
|
43
|
+
latestActiveContentRef,
|
|
44
|
+
pendingLocalFileContentsRef,
|
|
45
|
+
replacePreviewContent,
|
|
46
|
+
setCollabContent,
|
|
47
|
+
setCollabContentFileId,
|
|
48
|
+
setContentRenderRevision,
|
|
49
|
+
undoManagerRef,
|
|
50
|
+
ydoc,
|
|
51
|
+
}: SeedCollabContentArgs) {
|
|
52
|
+
if (!ydoc || !isSynced || !activeFileId) return;
|
|
53
|
+
const fileId = activeFileId;
|
|
54
|
+
const ytext = ydoc.getText("content");
|
|
55
|
+
const text = ytext.toString();
|
|
56
|
+
const pendingLocalContent =
|
|
57
|
+
pendingLocalFileContentsRef.current.get(fileId)?.content;
|
|
58
|
+
if (pendingLocalContent && text !== pendingLocalContent) {
|
|
59
|
+
setCollabContent(pendingLocalContent);
|
|
60
|
+
setCollabContentFileId(fileId);
|
|
61
|
+
lastLocalContentRef.current = pendingLocalContent;
|
|
62
|
+
latestActiveContentRef.current = pendingLocalContent;
|
|
63
|
+
if (
|
|
64
|
+
previewContentReplaceNeedsRenderFallback(
|
|
65
|
+
replacePreviewContent(pendingLocalContent, null, {
|
|
66
|
+
forceFullDocument: true,
|
|
67
|
+
}),
|
|
68
|
+
)
|
|
69
|
+
) {
|
|
70
|
+
setContentRenderRevision((revision) => revision + 1);
|
|
71
|
+
}
|
|
72
|
+
// Full delete+insert with an untracked origin: the UndoManager only
|
|
73
|
+
// tracks LOCAL_EDIT_ORIGIN, so this rewrite is invisible to it. Clear
|
|
74
|
+
// the undo stack so a subsequent Cmd+Z can't replay a tracked delta
|
|
75
|
+
// from before this rewrite against content it no longer matches
|
|
76
|
+
// (see the DE:5135-style mitigation below for the same hazard).
|
|
77
|
+
undoManagerRef.current?.clear(true, false);
|
|
78
|
+
ydoc.transact(() => {
|
|
79
|
+
ytext.delete(0, ytext.length);
|
|
80
|
+
ytext.insert(0, pendingLocalContent);
|
|
81
|
+
}, TAB_ID);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (text.length > 0) {
|
|
85
|
+
const storedContent = activeFile?.content ?? "";
|
|
86
|
+
// §gesture-persistence — a freshly-connected/just-synced doc snapshot
|
|
87
|
+
// has no proven watermark yet in this session. Don't let it outrank
|
|
88
|
+
// SQL merely because it looks like well-formed HTML: rebase from SQL
|
|
89
|
+
// whenever they differ and no baseline has been established (or the
|
|
90
|
+
// live text is outright malformed). See
|
|
91
|
+
// shouldRebaseCollabDocFromStoredContent's doc comment for the full
|
|
92
|
+
// clobber this closes.
|
|
93
|
+
if (
|
|
94
|
+
shouldRebaseCollabDocFromStoredContent({
|
|
95
|
+
liveContent: text,
|
|
96
|
+
storedContent,
|
|
97
|
+
storedUpdatedAt: activeFile?.updatedAt ?? null,
|
|
98
|
+
lastAppliedUpdatedAt: lastAppliedFileUpdatedAtRef.current,
|
|
99
|
+
fileType: activeFile?.fileType ?? "html",
|
|
100
|
+
})
|
|
101
|
+
) {
|
|
102
|
+
setCollabContent(storedContent);
|
|
103
|
+
setCollabContentFileId(fileId);
|
|
104
|
+
lastLocalContentRef.current = storedContent;
|
|
105
|
+
latestActiveContentRef.current = storedContent;
|
|
106
|
+
if (activeFile?.updatedAt) {
|
|
107
|
+
lastAppliedFileUpdatedAtRef.current = activeFile.updatedAt;
|
|
108
|
+
}
|
|
109
|
+
if (
|
|
110
|
+
previewContentReplaceNeedsRenderFallback(
|
|
111
|
+
replacePreviewContent(storedContent, null, {
|
|
112
|
+
forceFullDocument: true,
|
|
113
|
+
}),
|
|
114
|
+
)
|
|
115
|
+
) {
|
|
116
|
+
setContentRenderRevision((revision) => revision + 1);
|
|
117
|
+
}
|
|
118
|
+
// Untracked full rewrite — see clear() note above.
|
|
119
|
+
undoManagerRef.current?.clear(true, false);
|
|
120
|
+
ydoc.transact(() => {
|
|
121
|
+
ytext.delete(0, ytext.length);
|
|
122
|
+
ytext.insert(0, storedContent);
|
|
123
|
+
}, TAB_ID);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Y.Doc snapshots are a render seed, not the SQL source of truth; the
|
|
127
|
+
// reconcile effect below advances the updatedAt watermark only after it
|
|
128
|
+
// confirms or applies the current DB content.
|
|
129
|
+
//
|
|
130
|
+
// Item 5 (edit-flash) root cause: this effect's deps include
|
|
131
|
+
// `activeFile?.content`/`activeFile?.updatedAt`, which change on EVERY
|
|
132
|
+
// save (useActionMutation's default onSuccess invalidates all
|
|
133
|
+
// `["action"]` queries, so `get-design` refetches after every single
|
|
134
|
+
// edit and hands back a new `activeFile` object with a bumped
|
|
135
|
+
// `updatedAt`). That refire lands here even when the Y.Doc's `text`
|
|
136
|
+
// hasn't changed at all since the last time this ran — previously this
|
|
137
|
+
// unconditionally re-adopted `text` and bumped contentRenderRevision on
|
|
138
|
+
// every such refire, forcing a full srcdoc rebuild after nearly every
|
|
139
|
+
// commit. Only touch collab/render state when `text` genuinely differs
|
|
140
|
+
// from what is already reflected.
|
|
141
|
+
if (
|
|
142
|
+
text !== latestActiveContentRef.current ||
|
|
143
|
+
collabContentFileIdRef.current !== fileId
|
|
144
|
+
) {
|
|
145
|
+
setCollabContent(text);
|
|
146
|
+
setCollabContentFileId(fileId);
|
|
147
|
+
latestActiveContentRef.current = text;
|
|
148
|
+
if (
|
|
149
|
+
previewContentReplaceNeedsRenderFallback(
|
|
150
|
+
replacePreviewContent(text, null, { forceFullDocument: true }),
|
|
151
|
+
)
|
|
152
|
+
) {
|
|
153
|
+
setContentRenderRevision((revision) => revision + 1);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function externalPreviewUrlForContent(content: string): string | null {
|
|
2
|
+
const trimmed = content.trim();
|
|
3
|
+
if (!/^https?:\/\//i.test(trimmed)) return null;
|
|
4
|
+
try {
|
|
5
|
+
const url = new URL(trimmed);
|
|
6
|
+
url.hash = "";
|
|
7
|
+
return url.toString();
|
|
8
|
+
} catch {
|
|
9
|
+
// coercion-ok: a non-parseable string is "not an external URL", which is
|
|
10
|
+
// exactly what the null return means to every caller here.
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function fullPreviewHtml(content: string): string {
|
|
16
|
+
const trimmed = content.trim();
|
|
17
|
+
if (/<!doctype html|<html[\s>]/i.test(trimmed)) return content;
|
|
18
|
+
return `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head><body>${content}</body></html>`;
|
|
19
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { withSsrHtmlContentType } from "@agent-native/core/shared";
|
|
1
2
|
import { redirect } from "react-router";
|
|
2
3
|
|
|
3
4
|
export function loader() {
|
|
4
|
-
return redirect("/", 302);
|
|
5
|
+
return withSsrHtmlContentType(redirect("/", 302));
|
|
5
6
|
}
|
|
6
7
|
|
|
7
8
|
export default function ExamplesRedirect() {
|
|
@@ -13,6 +13,8 @@ AGENT_CHAT_DURABLE_BACKGROUND = "true"
|
|
|
13
13
|
|
|
14
14
|
[context.production.environment]
|
|
15
15
|
AGENT_NATIVE_HOSTED_HARNESS = "true"
|
|
16
|
+
AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
|
|
17
|
+
AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
|
|
16
18
|
|
|
17
19
|
# Emit the Netlify background function used by Design's durable agent-chat opt-in.
|
|
18
20
|
# The normal server timeout still protects non-background HTTP handlers.
|
|
@@ -678,7 +678,8 @@ export function stripBoardSurfaceOffsetFromCoord(value: number): number {
|
|
|
678
678
|
* Parent-relative position for a node being reparented INTO a target
|
|
679
679
|
* container, from the two elements' persisted absolute positions in the same
|
|
680
680
|
* coordinate space. This is the pure seam used by
|
|
681
|
-
* `handleOverviewPrimitiveReparent`
|
|
681
|
+
* `handleOverviewPrimitiveReparent`
|
|
682
|
+
* (app/pages/design-editor/commands/overview-primitive-reparent.ts).
|
|
682
683
|
*
|
|
683
684
|
* The flat subtraction below is only correct when both `source` and `target`
|
|
684
685
|
* are already expressed in the SAME coordinate space — historically that
|
|
@@ -15,6 +15,8 @@ AGENT_CHAT_DURABLE_BACKGROUND = "true"
|
|
|
15
15
|
|
|
16
16
|
[context.production.environment]
|
|
17
17
|
AGENT_NATIVE_HOSTED_HARNESS = "true"
|
|
18
|
+
AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
|
|
19
|
+
AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
|
|
18
20
|
|
|
19
21
|
# A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
|
|
20
22
|
# The framework does not impose a default agent-run timeout; if a template sets
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DefaultSpinner } from "@agent-native/core/client/ui";
|
|
2
|
+
import { withSsrHtmlContentType } from "@agent-native/core/shared";
|
|
2
3
|
import { redirect, type LoaderFunctionArgs } from "react-router";
|
|
3
4
|
|
|
4
5
|
import messages from "@/i18n/en-US";
|
|
@@ -11,11 +12,11 @@ function target(url: URL): string {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export function loader({ url }: LoaderFunctionArgs) {
|
|
14
|
-
throw redirect(target(url));
|
|
15
|
+
throw withSsrHtmlContentType(redirect(target(url)), { varyByQuery: true });
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export function clientLoader({ url }: LoaderFunctionArgs) {
|
|
18
|
-
throw redirect(target(url));
|
|
19
|
+
throw withSsrHtmlContentType(redirect(target(url)), { varyByQuery: true });
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export function meta() {
|
|
@@ -12,6 +12,8 @@ NPM_CONFIG_PRODUCTION = "false"
|
|
|
12
12
|
|
|
13
13
|
[context.production.environment]
|
|
14
14
|
AGENT_NATIVE_HOSTED_HARNESS = "true"
|
|
15
|
+
AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
|
|
16
|
+
AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
|
|
15
17
|
|
|
16
18
|
# A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
|
|
17
19
|
# The framework does not impose a default agent-run timeout; if a template sets
|