@agent-native/core 0.92.7 → 0.92.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/docs/content/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/ar-SA/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/ar-SA/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/ar-SA/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/de-DE/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/de-DE/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/de-DE/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/es-ES/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/es-ES/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/es-ES/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/fr-FR/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/fr-FR/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/fr-FR/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/hi-IN/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/hi-IN/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/hi-IN/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/ja-JP/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/ja-JP/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/ja-JP/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/ko-KR/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/ko-KR/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/ko-KR/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/pt-BR/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/pt-BR/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/pt-BR/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/zh-CN/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/zh-CN/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/zh-CN/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/zh-TW/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/zh-TW/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/zh-TW/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/pure-agent-apps.mdx +22 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/engine/builtin.ts +1 -1
- package/corpus/core/src/agent/model-config.ts +30 -30
- package/corpus/core/src/agent/production-agent.ts +129 -7
- package/corpus/core/src/cli/design-connect.ts +438 -83
- package/corpus/core/src/cli/recap.ts +2 -2
- package/corpus/core/src/client/AgentPanel.tsx +8 -1
- package/corpus/core/src/client/agent-chat-adapter.ts +12 -7
- package/corpus/core/src/client/composer/TiptapComposer.tsx +1 -1
- package/corpus/core/src/client/index.ts +4 -0
- package/corpus/core/src/client/org/TeamPage.tsx +41 -26
- package/corpus/core/src/client/use-action.ts +113 -1
- package/corpus/core/src/db/client.ts +12 -2
- package/corpus/core/src/db/create-get-db.ts +4 -1
- package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +1 -1
- package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +1 -1
- package/corpus/core/src/server/transcribe-voice.ts +1 -1
- package/corpus/core/src/usage/store.ts +13 -4
- package/corpus/core/src/vite/client.ts +38 -1
- package/corpus/templates/analytics/AGENTS.md +3 -0
- package/corpus/templates/analytics/actions/get-data-program.ts +78 -0
- package/corpus/templates/analytics/actions/list-data-programs.ts +49 -0
- package/corpus/templates/analytics/app/global.css +11 -0
- package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +163 -17
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/analytics/app/i18n-data.ts +20 -0
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/ChartCard.tsx +96 -3
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +3 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +61 -4
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +40 -2
- package/corpus/templates/analytics/changelog/2026-07-09-analytics-no-longer-forgets-successful-data-queries-when-a-l.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-dashboard-charts-keep-consistent-spacing-when-they-stack-ver.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-dashboard-charts-tables-and-extensions-can-be-selected-and-d.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-long-running-analytics-requests-now-recover-automatically-wh.md +6 -0
- package/corpus/templates/analytics/server/db/index.ts +6 -0
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +1 -7
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +102 -15
- package/corpus/templates/content/app/hooks/use-content-database.ts +52 -0
- package/corpus/templates/content/app/hooks/use-document-properties.ts +50 -6
- package/corpus/templates/content/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/content/app/i18n-data.ts +28 -0
- package/corpus/templates/content/changelog/2026-07-09-notion-conflict-warnings-no-longer-flash-during-normal-synce.md +6 -0
- package/corpus/templates/content/changelog/2026-07-09-property-menus-and-delete-confirmations-now-open-visibly-and.md +6 -0
- package/corpus/templates/content/server/lib/notion-sync.ts +130 -28
- package/corpus/templates/design/actions/add-breakpoint.ts +48 -68
- package/corpus/templates/design/actions/add-localhost-screens.ts +496 -61
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +2 -18
- package/corpus/templates/design/actions/apply-design-token-edit.ts +51 -82
- package/corpus/templates/design/actions/apply-tweaks.ts +63 -41
- package/corpus/templates/design/actions/capture-design-state.ts +2 -2
- package/corpus/templates/design/actions/connect-localhost.ts +58 -12
- package/corpus/templates/design/actions/create-component.ts +2 -18
- package/corpus/templates/design/actions/create-design-branch.ts +38 -18
- package/corpus/templates/design/actions/create-fusion-app.ts +15 -11
- package/corpus/templates/design/actions/delete-file.ts +19 -34
- package/corpus/templates/design/actions/deploy-design-preview.ts +59 -27
- package/corpus/templates/design/actions/deploy-fusion-app.ts +22 -15
- package/corpus/templates/design/actions/export-pdf.ts +2 -1
- package/corpus/templates/design/actions/export-zip.ts +9 -3
- package/corpus/templates/design/actions/generate-design.ts +107 -70
- package/corpus/templates/design/actions/get-component-details.ts +2 -18
- package/corpus/templates/design/actions/get-design-branch-diff.ts +2 -3
- package/corpus/templates/design/actions/get-design-surface-index.ts +2 -18
- package/corpus/templates/design/actions/get-design.ts +2 -1
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +7 -1
- package/corpus/templates/design/actions/import-design-tokens.ts +114 -76
- package/corpus/templates/design/actions/index-components.ts +2 -18
- package/corpus/templates/design/actions/list-design-source-capabilities.ts +23 -28
- package/corpus/templates/design/actions/list-local-files.ts +9 -2
- package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +82 -98
- package/corpus/templates/design/actions/open-component-source.ts +2 -18
- package/corpus/templates/design/actions/open-visual-edit.ts +19 -1
- package/corpus/templates/design/actions/present-design-variants.ts +70 -70
- package/corpus/templates/design/actions/preview-component-prop-edit.ts +4 -14
- package/corpus/templates/design/actions/read-local-file.ts +9 -2
- package/corpus/templates/design/actions/remove-breakpoint.ts +40 -61
- package/corpus/templates/design/actions/rename-screen.ts +425 -0
- package/corpus/templates/design/actions/request-localhost-write-consent.ts +12 -2
- package/corpus/templates/design/actions/revoke-localhost-write-consent.ts +9 -2
- package/corpus/templates/design/actions/sync-fusion-app.ts +56 -39
- package/corpus/templates/design/actions/update-design.ts +376 -61
- package/corpus/templates/design/actions/update-file.ts +455 -188
- package/corpus/templates/design/actions/write-local-file.ts +10 -2
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +685 -608
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +43 -15
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +576 -2015
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +62 -14
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +263 -25
- package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +34 -0
- package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +40 -0
- package/corpus/templates/design/app/components/design/design-canvas/creation.ts +47 -0
- package/corpus/templates/design/app/components/design/design-canvas/element-payload.ts +27 -0
- package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +62 -0
- package/corpus/templates/design/app/components/design/design-canvas/external-preview.ts +137 -0
- package/corpus/templates/design/app/components/design/design-canvas/file-drop.ts +10 -0
- package/corpus/templates/design/app/components/design/design-canvas/hit-test.ts +23 -0
- package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +23 -0
- package/corpus/templates/design/app/components/design/design-canvas/iframe-pan.ts +177 -0
- package/corpus/templates/design/app/components/design/design-canvas/motion-types.ts +7 -0
- package/corpus/templates/design/app/components/design/design-canvas/pending-text-edit.ts +62 -0
- package/corpus/templates/design/app/components/design/index.ts +2 -2
- package/corpus/templates/design/app/components/design/multi-screen/canvas-tools.ts +100 -0
- package/corpus/templates/design/app/components/design/multi-screen/coordinate-transforms.ts +81 -0
- package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +23 -13
- package/corpus/templates/design/app/components/design/multi-screen/culling.ts +238 -19
- package/corpus/templates/design/app/components/design/multi-screen/draft-primitives.ts +424 -0
- package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +341 -0
- package/corpus/templates/design/app/components/design/multi-screen/iframe-targeting.ts +52 -0
- package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +62 -0
- package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +283 -0
- package/corpus/templates/design/app/components/design/multi-screen/screen-content-cache.ts +272 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +5 -1
- package/corpus/templates/design/app/components/design/multi-screen/wheel-gesture-state.ts +12 -0
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +325 -84
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +15 -1
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +119 -47
- package/corpus/templates/design/app/i18n/zh-TW.ts +9 -0
- package/corpus/templates/design/app/i18n-data.ts +223 -0
- package/corpus/templates/design/app/lib/design-save-outbox.ts +379 -0
- package/corpus/templates/design/app/lib/png-clipboard.ts +113 -0
- package/corpus/templates/design/app/pages/design-editor/canvas-primitives.ts +229 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +918 -0
- package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +43 -0
- package/corpus/templates/design/app/pages/design-editor/cross-screen-text-color.ts +336 -0
- package/corpus/templates/design/app/pages/design-editor/data-operations.ts +270 -0
- package/corpus/templates/design/app/pages/design-editor/dom-guards.ts +24 -0
- package/corpus/templates/design/app/pages/design-editor/dom-utils.ts +11 -0
- package/corpus/templates/design/app/pages/design-editor/editor-session.ts +20 -0
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +408 -0
- package/corpus/templates/design/app/pages/design-editor/export-capture.ts +115 -0
- package/corpus/templates/design/app/pages/design-editor/geometry-persistence.ts +93 -0
- package/corpus/templates/design/app/pages/design-editor/history.ts +169 -0
- package/corpus/templates/design/app/pages/design-editor/layout-operations.ts +185 -0
- package/corpus/templates/design/app/pages/design-editor/motion-state.ts +309 -0
- package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +438 -0
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +510 -0
- package/corpus/templates/design/app/pages/design-editor/portable-style.ts +115 -0
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +337 -0
- package/corpus/templates/design/app/pages/design-editor/style-utils.ts +5 -0
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +92 -0
- package/corpus/templates/design/app/pages/design-editor/tweak-save.ts +87 -0
- package/corpus/templates/design/app/pages/design-editor/types.ts +36 -0
- package/corpus/templates/design/app/routes/design.$id.tsx +11 -1
- package/corpus/templates/design/changelog/2026-07-09-design-edits-canvas-positions-tools-and-tab-context-now-pers.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-figma-style-shortcuts-drawing-annotations-and-cross-screen-d.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-hand-and-scale-tools-now-keep-their-active-toolbar-identity-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-large-multi-screen-designs-now-stay-responsive-during-long-c.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-layer-drags-now-land-exactly-where-the-insertion-indicator-s.md +6 -0
- package/corpus/templates/design/server/db/index.ts +2 -52
- package/corpus/templates/design/server/db/schema.ts +16 -0
- package/corpus/templates/design/server/lib/design-data-access.ts +70 -0
- package/corpus/templates/design/server/lib/design-data-mutation.ts +249 -0
- package/corpus/templates/design/server/lib/fusion-screens.ts +89 -76
- package/corpus/templates/design/server/lib/import-design-files.ts +76 -35
- package/corpus/templates/design/server/lib/verify-write-grant.ts +7 -2
- package/corpus/templates/design/server/plugins/db.ts +19 -0
- package/corpus/templates/design/server/source-workspace.ts +12 -13
- package/corpus/templates/design/shared/resolve-tweaks.ts +15 -0
- package/corpus/templates/design/shared/screen-rename.ts +42 -0
- package/corpus/templates/design/shared/source-mode.ts +84 -4
- package/dist/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builtin.js +1 -1
- package/dist/agent/engine/builtin.js.map +1 -1
- package/dist/agent/model-config.d.ts +9 -9
- package/dist/agent/model-config.d.ts.map +1 -1
- package/dist/agent/model-config.js +30 -30
- package/dist/agent/model-config.js.map +1 -1
- package/dist/agent/production-agent.d.ts +60 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +106 -7
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/design-connect.d.ts +30 -1
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +359 -75
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/recap.js +2 -2
- package/dist/cli/recap.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +6 -1
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +10 -5
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/composer/TiptapComposer.js +1 -1
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/org/TeamPage.d.ts.map +1 -1
- package/dist/client/org/TeamPage.js +6 -5
- package/dist/client/org/TeamPage.js.map +1 -1
- package/dist/client/use-action.d.ts +32 -0
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +68 -1
- package/dist/client/use-action.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/client.d.ts +3 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +7 -2
- package/dist/db/client.js.map +1 -1
- package/dist/db/create-get-db.d.ts.map +1 -1
- package/dist/db/create-get-db.js +2 -2
- package/dist/db/create-get-db.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/scripts/agent-engines/manage-agent-engine.js +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.js +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.js +1 -1
- package/dist/server/transcribe-voice.js.map +1 -1
- package/dist/usage/store.d.ts.map +1 -1
- package/dist/usage/store.js +13 -4
- package/dist/usage/store.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +36 -1
- package/dist/vite/client.js.map +1 -1
- package/docs/content/a2a-protocol.mdx +12 -0
- package/docs/content/locales/ar-SA/a2a-protocol.mdx +12 -0
- package/docs/content/locales/ar-SA/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/ar-SA/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/de-DE/a2a-protocol.mdx +12 -0
- package/docs/content/locales/de-DE/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/de-DE/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/es-ES/a2a-protocol.mdx +12 -0
- package/docs/content/locales/es-ES/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/es-ES/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/fr-FR/a2a-protocol.mdx +12 -0
- package/docs/content/locales/fr-FR/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/fr-FR/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/hi-IN/a2a-protocol.mdx +12 -0
- package/docs/content/locales/hi-IN/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/hi-IN/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/ja-JP/a2a-protocol.mdx +12 -0
- package/docs/content/locales/ja-JP/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/ja-JP/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/ko-KR/a2a-protocol.mdx +12 -0
- package/docs/content/locales/ko-KR/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/ko-KR/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/pt-BR/a2a-protocol.mdx +12 -0
- package/docs/content/locales/pt-BR/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/pt-BR/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/zh-CN/a2a-protocol.mdx +12 -0
- package/docs/content/locales/zh-CN/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/zh-CN/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/zh-TW/a2a-protocol.mdx +12 -0
- package/docs/content/locales/zh-TW/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/zh-TW/pure-agent-apps.mdx +4 -0
- package/docs/content/pr-visual-recap.mdx +3 -3
- package/docs/content/pure-agent-apps.mdx +22 -0
- package/package.json +2 -2
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
getRequestUserEmail,
|
|
5
5
|
} from "@agent-native/core/server/request-context";
|
|
6
6
|
import { assertAccess } from "@agent-native/core/sharing";
|
|
7
|
-
import { and, eq } from "drizzle-orm";
|
|
7
|
+
import { and, eq, isNull } from "drizzle-orm";
|
|
8
8
|
import { nanoid } from "nanoid";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
|
|
@@ -53,6 +53,9 @@ export default defineAction({
|
|
|
53
53
|
and(
|
|
54
54
|
eq(schema.designLocalhostConnections.id, connectionId),
|
|
55
55
|
eq(schema.designLocalhostConnections.ownerEmail, ownerEmail),
|
|
56
|
+
orgId
|
|
57
|
+
? eq(schema.designLocalhostConnections.orgId, orgId)
|
|
58
|
+
: isNull(schema.designLocalhostConnections.orgId),
|
|
56
59
|
),
|
|
57
60
|
)
|
|
58
61
|
.limit(1);
|
|
@@ -93,6 +96,9 @@ export default defineAction({
|
|
|
93
96
|
eq(schema.designLocalhostWriteGrants.designId, designId),
|
|
94
97
|
eq(schema.designLocalhostWriteGrants.connectionId, connectionId),
|
|
95
98
|
eq(schema.designLocalhostWriteGrants.ownerEmail, ownerEmail),
|
|
99
|
+
orgId
|
|
100
|
+
? eq(schema.designLocalhostWriteGrants.orgId, orgId)
|
|
101
|
+
: isNull(schema.designLocalhostWriteGrants.orgId),
|
|
96
102
|
),
|
|
97
103
|
)
|
|
98
104
|
.limit(1);
|
|
@@ -11,12 +11,17 @@ import {
|
|
|
11
11
|
extractDocumentColors,
|
|
12
12
|
extractDocumentFonts,
|
|
13
13
|
} from "@agent-native/core/server/design-token-utils";
|
|
14
|
-
import { assertAccess
|
|
14
|
+
import { assertAccess } from "@agent-native/core/sharing";
|
|
15
15
|
import { eq } from "drizzle-orm";
|
|
16
|
+
import { nanoid } from "nanoid";
|
|
16
17
|
import { z } from "zod";
|
|
17
18
|
|
|
18
19
|
import { getDb, schema } from "../server/db/index.js";
|
|
19
20
|
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
21
|
+
import {
|
|
22
|
+
mutateDesignData,
|
|
23
|
+
type DesignDataRecord,
|
|
24
|
+
} from "../server/lib/design-data-mutation.js";
|
|
20
25
|
import {
|
|
21
26
|
isSafeCssTokenValue,
|
|
22
27
|
isSafeCssVarName,
|
|
@@ -39,6 +44,31 @@ interface ImportedDesignToken {
|
|
|
39
44
|
source: string;
|
|
40
45
|
}
|
|
41
46
|
|
|
47
|
+
type TweakDef = Parameters<typeof resolveTweaksToCssVars>[0][number];
|
|
48
|
+
type TweakSelections = Record<string, string | number | boolean>;
|
|
49
|
+
|
|
50
|
+
function readTweaks(data: DesignDataRecord): TweakDef[] {
|
|
51
|
+
return Array.isArray(data.tweaks) ? (data.tweaks as TweakDef[]) : [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function readSelections(data: DesignDataRecord): TweakSelections {
|
|
55
|
+
return data.tweakSelections &&
|
|
56
|
+
typeof data.tweakSelections === "object" &&
|
|
57
|
+
!Array.isArray(data.tweakSelections)
|
|
58
|
+
? (data.tweakSelections as TweakSelections)
|
|
59
|
+
: {};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function selectionKeyForCssVar(tweaks: TweakDef[], cssVar: string): string {
|
|
63
|
+
return tweaks.find((tweak) => tweak.cssVar === cssVar)?.id ?? cssVar;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function tokenSourceLabel(token: ImportedDesignToken, sourceLabel: string) {
|
|
67
|
+
return token.source === "CSS variables" || token.source === "Colors"
|
|
68
|
+
? sourceLabel
|
|
69
|
+
: token.source;
|
|
70
|
+
}
|
|
71
|
+
|
|
42
72
|
const tokenFileSchema = z.object({
|
|
43
73
|
filename: z.string().trim().min(1).describe("File name or relative path"),
|
|
44
74
|
content: z.string().describe("Raw text content"),
|
|
@@ -421,85 +451,93 @@ export default defineAction({
|
|
|
421
451
|
};
|
|
422
452
|
}
|
|
423
453
|
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
|
|
454
|
+
const importId = nanoid();
|
|
455
|
+
const { data: persistedData } = await mutateDesignData({
|
|
456
|
+
designId,
|
|
457
|
+
mutate: (prevData, { updatedAt }) => {
|
|
458
|
+
const tweaks = readTweaks(prevData);
|
|
459
|
+
const nextSelections = { ...readSelections(prevData) };
|
|
460
|
+
for (const token of safeTokens) {
|
|
461
|
+
nextSelections[selectionKeyForCssVar(tweaks, token.cssVar)] =
|
|
462
|
+
token.value;
|
|
433
463
|
}
|
|
434
|
-
} catch {
|
|
435
|
-
// Stale/invalid JSON — preserve the write with a fresh object.
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
464
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
? (prevData.tweakSelections as Record<
|
|
453
|
-
string,
|
|
454
|
-
string | number | boolean
|
|
455
|
-
>)
|
|
456
|
-
: {};
|
|
457
|
-
const nextSelections = { ...prevSelections };
|
|
458
|
-
|
|
459
|
-
for (const token of safeTokens) {
|
|
460
|
-
const tweakId = cssVarToTweakId.get(token.cssVar);
|
|
461
|
-
nextSelections[tweakId ?? token.cssVar] = token.value;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
const previousSources =
|
|
465
|
-
prevData.tokenImportSources &&
|
|
466
|
-
typeof prevData.tokenImportSources === "object" &&
|
|
467
|
-
!Array.isArray(prevData.tokenImportSources)
|
|
468
|
-
? (prevData.tokenImportSources as Record<string, string>)
|
|
469
|
-
: {};
|
|
470
|
-
const tokenImportSources = { ...previousSources };
|
|
471
|
-
for (const token of safeTokens) {
|
|
472
|
-
tokenImportSources[token.cssVar] =
|
|
473
|
-
token.source === "CSS variables" || token.source === "Colors"
|
|
474
|
-
? sourceLabel
|
|
475
|
-
: token.source;
|
|
476
|
-
}
|
|
465
|
+
const previousSources =
|
|
466
|
+
prevData.tokenImportSources &&
|
|
467
|
+
typeof prevData.tokenImportSources === "object" &&
|
|
468
|
+
!Array.isArray(prevData.tokenImportSources)
|
|
469
|
+
? (prevData.tokenImportSources as Record<string, string>)
|
|
470
|
+
: {};
|
|
471
|
+
const tokenImportSources = { ...previousSources };
|
|
472
|
+
for (const token of safeTokens) {
|
|
473
|
+
tokenImportSources[token.cssVar] = tokenSourceLabel(
|
|
474
|
+
token,
|
|
475
|
+
sourceLabel,
|
|
476
|
+
);
|
|
477
|
+
}
|
|
477
478
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
479
|
+
const previousImports = Array.isArray(prevData.tokenImports)
|
|
480
|
+
? (prevData.tokenImports as unknown[])
|
|
481
|
+
: [];
|
|
482
|
+
const alreadyAppended = previousImports.some(
|
|
483
|
+
(entry) =>
|
|
484
|
+
entry !== null &&
|
|
485
|
+
typeof entry === "object" &&
|
|
486
|
+
(entry as { id?: unknown }).id === importId,
|
|
487
|
+
);
|
|
488
|
+
const tokenImports = alreadyAppended
|
|
489
|
+
? previousImports
|
|
490
|
+
: [
|
|
491
|
+
...previousImports.slice(-9),
|
|
492
|
+
{
|
|
493
|
+
id: importId,
|
|
494
|
+
source,
|
|
495
|
+
sourceLabel,
|
|
496
|
+
tokenCount: safeTokens.length,
|
|
497
|
+
filesAnalyzed,
|
|
498
|
+
importedAt: updatedAt,
|
|
499
|
+
},
|
|
500
|
+
];
|
|
501
|
+
|
|
502
|
+
return {
|
|
503
|
+
...prevData,
|
|
504
|
+
tweakSelections: nextSelections,
|
|
505
|
+
tokenImportSources,
|
|
506
|
+
tokenImports,
|
|
507
|
+
tweaksAppliedAt: updatedAt,
|
|
508
|
+
};
|
|
509
|
+
},
|
|
510
|
+
isApplied: (data) => {
|
|
511
|
+
const tweaks = readTweaks(data);
|
|
512
|
+
const selections = readSelections(data);
|
|
513
|
+
const sources =
|
|
514
|
+
data.tokenImportSources &&
|
|
515
|
+
typeof data.tokenImportSources === "object" &&
|
|
516
|
+
!Array.isArray(data.tokenImportSources)
|
|
517
|
+
? (data.tokenImportSources as Record<string, string>)
|
|
518
|
+
: {};
|
|
519
|
+
const imports = Array.isArray(data.tokenImports)
|
|
520
|
+
? data.tokenImports
|
|
521
|
+
: [];
|
|
522
|
+
return (
|
|
523
|
+
safeTokens.every(
|
|
524
|
+
(token) =>
|
|
525
|
+
selections[selectionKeyForCssVar(tweaks, token.cssVar)] ===
|
|
526
|
+
token.value &&
|
|
527
|
+
sources[token.cssVar] === tokenSourceLabel(token, sourceLabel),
|
|
528
|
+
) &&
|
|
529
|
+
imports.some(
|
|
530
|
+
(entry) =>
|
|
531
|
+
entry !== null &&
|
|
532
|
+
typeof entry === "object" &&
|
|
533
|
+
(entry as { id?: unknown }).id === importId,
|
|
534
|
+
)
|
|
535
|
+
);
|
|
536
|
+
},
|
|
537
|
+
});
|
|
498
538
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
.set({ data: JSON.stringify(nextData), updatedAt: now })
|
|
502
|
-
.where(eq(schema.designs.id, designId));
|
|
539
|
+
const tweaks = readTweaks(persistedData);
|
|
540
|
+
const nextSelections = readSelections(persistedData);
|
|
503
541
|
|
|
504
542
|
return {
|
|
505
543
|
designId,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
type ComponentInstance,
|
|
35
35
|
} from "../shared/component-model.js";
|
|
36
36
|
import { hasCapability } from "../shared/design-source-capabilities.js";
|
|
37
|
-
import {
|
|
37
|
+
import { designSourceTypeFromData } from "../shared/source-mode.js";
|
|
38
38
|
|
|
39
39
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
40
40
|
|
|
@@ -89,24 +89,8 @@ export default defineAction({
|
|
|
89
89
|
const access = await assertAccess("design", designId, "editor");
|
|
90
90
|
|
|
91
91
|
// ── Source type + capability check ──────────────────────────────────────
|
|
92
|
-
let rawSourceType: unknown = "inline";
|
|
93
92
|
const rawData = (access.resource as { data?: unknown }).data;
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
const parsed: unknown = JSON.parse(rawData);
|
|
97
|
-
if (
|
|
98
|
-
parsed !== null &&
|
|
99
|
-
typeof parsed === "object" &&
|
|
100
|
-
"sourceType" in (parsed as object)
|
|
101
|
-
) {
|
|
102
|
-
rawSourceType = (parsed as { sourceType: unknown }).sourceType;
|
|
103
|
-
}
|
|
104
|
-
} catch {
|
|
105
|
-
// Stale JSON — default to inline.
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const sourceType = normalizeDesignSourceType(rawSourceType) ?? "inline";
|
|
93
|
+
const sourceType = designSourceTypeFromData(rawData);
|
|
110
94
|
const caps = resolveSourceCapabilities(sourceType);
|
|
111
95
|
|
|
112
96
|
// Real-app sources gate on `indexComponents`. For inline designs this
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import {
|
|
3
|
+
getRequestOrgId,
|
|
4
|
+
getRequestUserEmail,
|
|
5
|
+
} from "@agent-native/core/server/request-context";
|
|
2
6
|
import { resolveAccess } from "@agent-native/core/sharing";
|
|
3
|
-
import { eq } from "drizzle-orm";
|
|
7
|
+
import { and, eq, isNull } from "drizzle-orm";
|
|
4
8
|
import { z } from "zod";
|
|
5
9
|
|
|
6
10
|
import { getDb, schema } from "../server/db/index.js";
|
|
7
11
|
import { resolveSourceCapabilities } from "../shared/capability-resolver.js";
|
|
8
12
|
import { DESIGN_CAPABILITY_NAMES } from "../shared/design-source-capabilities.js";
|
|
9
13
|
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
10
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
designConnectionIdFromData,
|
|
16
|
+
designSourceTypeFromData,
|
|
17
|
+
} from "../shared/source-mode.js";
|
|
11
18
|
|
|
12
19
|
export default defineAction({
|
|
13
20
|
description:
|
|
@@ -36,25 +43,8 @@ export default defineAction({
|
|
|
36
43
|
// Resolve source type from the design's data blob. The `sourceType`
|
|
37
44
|
// field lives inside `designs.data` as a JSON key (set by connect-localhost
|
|
38
45
|
// and the fusion upgrade flow), falling back to "inline" when absent.
|
|
39
|
-
let rawSourceType: unknown = "inline";
|
|
40
46
|
const rawData = (access.resource as { data?: unknown }).data;
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
const parsed: unknown = JSON.parse(rawData);
|
|
44
|
-
if (
|
|
45
|
-
parsed !== null &&
|
|
46
|
-
typeof parsed === "object" &&
|
|
47
|
-
!Array.isArray(parsed) &&
|
|
48
|
-
"sourceType" in (parsed as object)
|
|
49
|
-
) {
|
|
50
|
-
rawSourceType = (parsed as { sourceType: unknown }).sourceType;
|
|
51
|
-
}
|
|
52
|
-
} catch {
|
|
53
|
-
// Stale/invalid JSON — default to "inline".
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const sourceType = normalizeDesignSourceType(rawSourceType) ?? "inline";
|
|
47
|
+
const sourceType = designSourceTypeFromData(rawData);
|
|
58
48
|
const capabilities = resolveSourceCapabilities(sourceType);
|
|
59
49
|
|
|
60
50
|
// Collect active localhost capabilities from the design's stored connection,
|
|
@@ -65,20 +55,25 @@ export default defineAction({
|
|
|
65
55
|
try {
|
|
66
56
|
const rawDesignData =
|
|
67
57
|
typeof rawData === "string" ? JSON.parse(rawData) : {};
|
|
68
|
-
const connectionId =
|
|
69
|
-
typeof rawDesignData === "object" &&
|
|
70
|
-
rawDesignData !== null &&
|
|
71
|
-
"connectionId" in rawDesignData
|
|
72
|
-
? String(rawDesignData.connectionId)
|
|
73
|
-
: undefined;
|
|
58
|
+
const connectionId = designConnectionIdFromData(rawDesignData);
|
|
74
59
|
|
|
75
|
-
|
|
60
|
+
const ownerEmail = getRequestUserEmail();
|
|
61
|
+
const orgId = getRequestOrgId() ?? null;
|
|
62
|
+
if (connectionId && ownerEmail) {
|
|
76
63
|
const [conn] = await db
|
|
77
64
|
.select({
|
|
78
65
|
capabilities: schema.designLocalhostConnections.capabilities,
|
|
79
66
|
})
|
|
80
67
|
.from(schema.designLocalhostConnections)
|
|
81
|
-
.where(
|
|
68
|
+
.where(
|
|
69
|
+
and(
|
|
70
|
+
eq(schema.designLocalhostConnections.id, connectionId),
|
|
71
|
+
eq(schema.designLocalhostConnections.ownerEmail, ownerEmail),
|
|
72
|
+
orgId
|
|
73
|
+
? eq(schema.designLocalhostConnections.orgId, orgId)
|
|
74
|
+
: isNull(schema.designLocalhostConnections.orgId),
|
|
75
|
+
),
|
|
76
|
+
)
|
|
82
77
|
.limit(1);
|
|
83
78
|
|
|
84
79
|
if (conn?.capabilities) {
|
|
@@ -13,9 +13,12 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { defineAction } from "@agent-native/core";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
getRequestOrgId,
|
|
18
|
+
getRequestUserEmail,
|
|
19
|
+
} from "@agent-native/core/server/request-context";
|
|
17
20
|
import { assertAccess } from "@agent-native/core/sharing";
|
|
18
|
-
import { and, eq } from "drizzle-orm";
|
|
21
|
+
import { and, eq, isNull } from "drizzle-orm";
|
|
19
22
|
import { z } from "zod";
|
|
20
23
|
|
|
21
24
|
import { getDb, schema } from "../server/db/index.js";
|
|
@@ -76,6 +79,7 @@ export default defineAction({
|
|
|
76
79
|
|
|
77
80
|
const ownerEmail = getRequestUserEmail();
|
|
78
81
|
if (!ownerEmail) throw new Error("no authenticated user");
|
|
82
|
+
const orgId = getRequestOrgId() ?? null;
|
|
79
83
|
|
|
80
84
|
const db = getDb();
|
|
81
85
|
const [connection] = await db
|
|
@@ -88,6 +92,9 @@ export default defineAction({
|
|
|
88
92
|
and(
|
|
89
93
|
eq(schema.designLocalhostConnections.id, connectionId),
|
|
90
94
|
eq(schema.designLocalhostConnections.ownerEmail, ownerEmail),
|
|
95
|
+
orgId
|
|
96
|
+
? eq(schema.designLocalhostConnections.orgId, orgId)
|
|
97
|
+
: isNull(schema.designLocalhostConnections.orgId),
|
|
91
98
|
),
|
|
92
99
|
)
|
|
93
100
|
.limit(1);
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* coordinates exactly (no clamping).
|
|
14
14
|
* 3. Inserts the fragments as direct <body> children of a new __board__.html
|
|
15
15
|
* design file (or merges into an existing one if somehow already present).
|
|
16
|
-
* 4.
|
|
17
|
-
* 5.
|
|
16
|
+
* 4. Reserves one stable file id in designs.data, then upserts the board file.
|
|
17
|
+
* 5. Finalizes boardFileId and nulls boardObjects through a retryable mutation.
|
|
18
18
|
*
|
|
19
19
|
* ## Contract
|
|
20
20
|
*
|
|
@@ -37,6 +37,7 @@ import { nanoid } from "nanoid";
|
|
|
37
37
|
import { z } from "zod";
|
|
38
38
|
|
|
39
39
|
import { getDb, schema } from "../server/db/index.js";
|
|
40
|
+
import { mutateDesignData } from "../server/lib/design-data-mutation.js";
|
|
40
41
|
import {
|
|
41
42
|
BOARD_FILENAME,
|
|
42
43
|
backfillBoardPrimitiveMarkers,
|
|
@@ -64,29 +65,39 @@ export default defineAction({
|
|
|
64
65
|
const db = getDb();
|
|
65
66
|
|
|
66
67
|
// ── 1. Idempotency guard ──────────────────────────────────────────────
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
.
|
|
72
|
-
.
|
|
73
|
-
|
|
68
|
+
// Reserve one stable file id without clearing boardObjects. If the process
|
|
69
|
+
// stops between phases, the next call resumes the same reservation.
|
|
70
|
+
const [preexistingBoardFile] = await db
|
|
71
|
+
.select({ id: schema.designFiles.id })
|
|
72
|
+
.from(schema.designFiles)
|
|
73
|
+
.where(
|
|
74
|
+
and(
|
|
75
|
+
eq(schema.designFiles.designId, designId),
|
|
76
|
+
eq(schema.designFiles.filename, BOARD_FILENAME),
|
|
77
|
+
),
|
|
78
|
+
)
|
|
74
79
|
.limit(1);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
const proposedBoardFileId = preexistingBoardFile?.id ?? nanoid();
|
|
81
|
+
const reservation = await mutateDesignData({
|
|
82
|
+
designId,
|
|
83
|
+
mutate: (current) => {
|
|
84
|
+
if (
|
|
85
|
+
(typeof current.boardFileId === "string" && current.boardFileId) ||
|
|
86
|
+
(typeof current.boardFileMigrationId === "string" &&
|
|
87
|
+
current.boardFileMigrationId)
|
|
88
|
+
) {
|
|
89
|
+
return current;
|
|
90
|
+
}
|
|
91
|
+
return { ...current, boardFileMigrationId: proposedBoardFileId };
|
|
92
|
+
},
|
|
93
|
+
isApplied: (current) =>
|
|
94
|
+
Boolean(
|
|
95
|
+
(typeof current.boardFileId === "string" && current.boardFileId) ||
|
|
96
|
+
(typeof current.boardFileMigrationId === "string" &&
|
|
97
|
+
current.boardFileMigrationId),
|
|
98
|
+
),
|
|
99
|
+
});
|
|
100
|
+
const parsed = reservation.data;
|
|
90
101
|
|
|
91
102
|
if (
|
|
92
103
|
typeof parsed["boardFileId"] === "string" &&
|
|
@@ -176,68 +187,32 @@ export default defineAction({
|
|
|
176
187
|
|
|
177
188
|
const now = new Date().toISOString();
|
|
178
189
|
|
|
179
|
-
// ── 4. Upsert the board file
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
190
|
+
// ── 4. Upsert the board file, then finalize the reservation ────────
|
|
191
|
+
const reservationId =
|
|
192
|
+
typeof parsed.boardFileMigrationId === "string" &&
|
|
193
|
+
parsed.boardFileMigrationId
|
|
194
|
+
? parsed.boardFileMigrationId
|
|
195
|
+
: proposedBoardFileId;
|
|
196
|
+
const [existingBoardFile] = await db
|
|
197
|
+
.select({ id: schema.designFiles.id })
|
|
198
|
+
.from(schema.designFiles)
|
|
199
|
+
.where(
|
|
200
|
+
and(
|
|
201
|
+
eq(schema.designFiles.designId, designId),
|
|
202
|
+
eq(schema.designFiles.filename, BOARD_FILENAME),
|
|
203
|
+
),
|
|
204
|
+
)
|
|
205
|
+
.limit(1);
|
|
206
|
+
const boardFileId = existingBoardFile?.id ?? reservationId;
|
|
195
207
|
|
|
196
|
-
|
|
208
|
+
if (existingBoardFile) {
|
|
209
|
+
await db
|
|
210
|
+
.update(schema.designFiles)
|
|
211
|
+
.set({ content: boardHtml, updatedAt: now })
|
|
212
|
+
.where(eq(schema.designFiles.id, boardFileId));
|
|
213
|
+
} else {
|
|
197
214
|
try {
|
|
198
|
-
|
|
199
|
-
freshParsed =
|
|
200
|
-
raw && typeof raw === "object" && !Array.isArray(raw)
|
|
201
|
-
? (raw as Record<string, unknown>)
|
|
202
|
-
: {};
|
|
203
|
-
} catch {
|
|
204
|
-
freshParsed = {};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Double-check inside the transaction.
|
|
208
|
-
if (
|
|
209
|
-
typeof freshParsed["boardFileId"] === "string" &&
|
|
210
|
-
freshParsed["boardFileId"].length > 0
|
|
211
|
-
) {
|
|
212
|
-
boardFileId = freshParsed["boardFileId"] as string;
|
|
213
|
-
return; // Another concurrent call already finished — skip.
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Check if a __board__.html file was somehow already created (shouldn't
|
|
217
|
-
// happen in normal flow, but be defensive).
|
|
218
|
-
const [existingBoardFile] = await tx
|
|
219
|
-
.select({ id: schema.designFiles.id })
|
|
220
|
-
.from(schema.designFiles)
|
|
221
|
-
.where(
|
|
222
|
-
and(
|
|
223
|
-
eq(schema.designFiles.designId, designId),
|
|
224
|
-
eq(schema.designFiles.filename, BOARD_FILENAME),
|
|
225
|
-
),
|
|
226
|
-
)
|
|
227
|
-
.limit(1);
|
|
228
|
-
|
|
229
|
-
if (existingBoardFile) {
|
|
230
|
-
// Board file exists but boardFileId was not set — link it and update
|
|
231
|
-
// its content.
|
|
232
|
-
boardFileId = existingBoardFile.id;
|
|
233
|
-
await tx
|
|
234
|
-
.update(schema.designFiles)
|
|
235
|
-
.set({ content: boardHtml, updatedAt: now })
|
|
236
|
-
.where(eq(schema.designFiles.id, boardFileId));
|
|
237
|
-
} else {
|
|
238
|
-
// Create the board file.
|
|
239
|
-
boardFileId = nanoid();
|
|
240
|
-
await tx.insert(schema.designFiles).values({
|
|
215
|
+
await db.insert(schema.designFiles).values({
|
|
241
216
|
id: boardFileId,
|
|
242
217
|
designId,
|
|
243
218
|
filename: BOARD_FILENAME,
|
|
@@ -246,22 +221,31 @@ export default defineAction({
|
|
|
246
221
|
createdAt: now,
|
|
247
222
|
updatedAt: now,
|
|
248
223
|
});
|
|
224
|
+
} catch (error) {
|
|
225
|
+
const [concurrentBoardFile] = await db
|
|
226
|
+
.select({ id: schema.designFiles.id })
|
|
227
|
+
.from(schema.designFiles)
|
|
228
|
+
.where(eq(schema.designFiles.id, boardFileId))
|
|
229
|
+
.limit(1);
|
|
230
|
+
if (!concurrentBoardFile) throw error;
|
|
249
231
|
}
|
|
232
|
+
}
|
|
250
233
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
boardFileId
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
234
|
+
await mutateDesignData({
|
|
235
|
+
designId,
|
|
236
|
+
mutate: (current) => {
|
|
237
|
+
if (typeof current.boardFileId === "string" && current.boardFileId) {
|
|
238
|
+
return current;
|
|
239
|
+
}
|
|
240
|
+
const next: Record<string, unknown> = {
|
|
241
|
+
...current,
|
|
242
|
+
boardFileId,
|
|
243
|
+
boardObjects: null,
|
|
244
|
+
};
|
|
245
|
+
delete next.boardFileMigrationId;
|
|
246
|
+
return next;
|
|
247
|
+
},
|
|
248
|
+
isApplied: (current) => current.boardFileId === boardFileId,
|
|
265
249
|
});
|
|
266
250
|
|
|
267
251
|
// ── 5. Seed collab state for the new board file ───────────────────────
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
componentNodeIdMatches,
|
|
42
42
|
} from "../shared/component-model.js";
|
|
43
43
|
import { hasCapability } from "../shared/design-source-capabilities.js";
|
|
44
|
-
import {
|
|
44
|
+
import { designSourceTypeFromData } from "../shared/source-mode.js";
|
|
45
45
|
|
|
46
46
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
47
47
|
|
|
@@ -90,24 +90,8 @@ export default defineAction({
|
|
|
90
90
|
if (!access) throw new Error("Design not found");
|
|
91
91
|
|
|
92
92
|
// ── Source type + capabilities ───────────────────────────────────────────
|
|
93
|
-
let rawSourceType: unknown = "inline";
|
|
94
93
|
const rawData = (access.resource as { data?: unknown }).data;
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
const parsed: unknown = JSON.parse(rawData);
|
|
98
|
-
if (
|
|
99
|
-
parsed !== null &&
|
|
100
|
-
typeof parsed === "object" &&
|
|
101
|
-
"sourceType" in (parsed as object)
|
|
102
|
-
) {
|
|
103
|
-
rawSourceType = (parsed as { sourceType: unknown }).sourceType;
|
|
104
|
-
}
|
|
105
|
-
} catch {
|
|
106
|
-
// Default to inline.
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const sourceType = normalizeDesignSourceType(rawSourceType) ?? "inline";
|
|
94
|
+
const sourceType = designSourceTypeFromData(rawData);
|
|
111
95
|
const caps = resolveSourceCapabilities(sourceType);
|
|
112
96
|
const canResolveToFile = hasCapability(caps, "resolveNodeToFile");
|
|
113
97
|
|