@agent-native/core 0.80.10 → 0.81.0
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 +53 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +108 -15
- package/corpus/core/src/cli/design-connect.ts +419 -19
- package/corpus/core/src/client/AssistantChat.tsx +123 -26
- package/corpus/core/src/client/ConnectBuilderCard.tsx +1 -0
- package/corpus/core/src/client/chat/index.ts +1 -0
- package/corpus/core/src/client/chat/run-recovery.tsx +9 -7
- package/corpus/core/src/client/chat-view-transition.ts +33 -2
- package/corpus/core/src/client/composer/PromptComposer.tsx +21 -2
- package/corpus/core/src/client/index.ts +1 -0
- package/corpus/core/src/client/org/OrgSwitcher.tsx +26 -4
- package/corpus/core/src/client/sharing/ShareButton.tsx +3 -3
- package/corpus/core/src/client/sse-event-processor.ts +9 -0
- package/corpus/core/src/client/use-agent-chat-home-handoff.ts +31 -3
- package/corpus/core/src/db/client.ts +29 -2
- package/corpus/core/src/provider-api/index.ts +29 -0
- package/corpus/core/src/server/core-routes-plugin.ts +58 -20
- package/corpus/core/src/styles/agent-native.css +54 -0
- package/corpus/core/src/usage/store.ts +2 -2
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +118 -10
- package/corpus/templates/analytics/app/components/layout/layout-route-policy.ts +7 -0
- package/corpus/templates/analytics/app/global.css +8 -0
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +7 -1
- package/corpus/templates/analytics/app/lib/chat-handoff.ts +34 -0
- package/corpus/templates/analytics/app/pages/Ask.tsx +9 -18
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +2 -2
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +109 -21
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +4 -6
- package/corpus/templates/analytics/changelog/2026-06-30-ask-opens-a-fresh-chat-unless-you-were-just-chatting-and-das.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-dashboard-email-reports-are-more-reliable-and-use-cleaner.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-dashboard-toolbar-controls-use-a-quieter-share-button-and-ve.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-session-replay-rows-show-whole-minute-durations-in-the-playl.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-session-replays-now-render-captured-styling-and-keep-the-eve.md +6 -0
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +16 -3
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +31 -14
- package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +9 -2
- package/corpus/templates/assets/AGENTS.md +4 -0
- package/corpus/templates/assets/actions/_tool-activity.ts +46 -0
- package/corpus/templates/assets/actions/generate-image-batch.ts +26 -5
- package/corpus/templates/assets/actions/generate-image.ts +118 -67
- package/corpus/templates/assets/actions/list-draft-assets.ts +50 -0
- package/corpus/templates/assets/actions/open-asset-picker.ts +9 -0
- package/corpus/templates/assets/actions/rerun-generation-run.ts +52 -31
- package/corpus/templates/assets/app/components/create/RecentDraftsSection.tsx +100 -0
- package/corpus/templates/assets/app/global.css +15 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +4 -0
- package/corpus/templates/assets/app/i18n/zh-TW.ts +7 -0
- package/corpus/templates/assets/app/i18n-data.ts +61 -0
- package/corpus/templates/assets/app/routes/_index.tsx +4 -0
- package/corpus/templates/assets/app/routes/library.tsx +233 -52
- package/corpus/templates/assets/changelog/2026-06-29-image-generation-can-now-render-requested-text-and-respect-b.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-30-image-generation-no-longer-looks-stuck-while-the-provider-is-still-working.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-30-tagged-presets-now-control-the-image-aspect-ratio.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-30-the-assets-picker-now-supports-a-vertical-embedded-layout-fo.md +6 -0
- package/corpus/templates/assets/server/lib/generation.ts +135 -11
- package/corpus/templates/assets/shared/api.ts +4 -0
- package/corpus/templates/clips/actions/finalize-recording.ts +1 -12
- package/corpus/templates/clips/actions/list-ai-requests.ts +60 -0
- package/corpus/templates/clips/app/hooks/use-auto-title.ts +24 -25
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +10 -5
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +6 -8
- package/corpus/templates/clips/changelog/2026-06-30-extension-save-recovery-avoids-false-upload-failures.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-30-longer-chrome-extension-recordings-now-keep-saving-reliably-.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/chrome-extension/src/background.ts +19 -10
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +38 -12
- package/corpus/templates/clips/chrome-extension/src/finalize-recovery.ts +224 -0
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +119 -16
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +16 -0
- package/corpus/templates/clips/server/lib/recording-upload-state.ts +48 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +9 -1
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +63 -13
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/status.get.ts +74 -0
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +32 -0
- package/corpus/templates/design/AGENTS.md +27 -0
- package/corpus/templates/design/CODE_NATIVE_DESIGN_STUDIO_IMPLEMENTATION_PLAN.md +1 -1
- package/corpus/templates/design/DESIGN-STUDIO-PLAN.md +5 -5
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +76 -25
- package/corpus/templates/design/actions/apply-design-state.ts +17 -4
- package/corpus/templates/design/actions/apply-motion-edit.ts +76 -115
- package/corpus/templates/design/actions/apply-shader-fill.ts +148 -56
- package/corpus/templates/design/actions/capture-design-state.ts +27 -4
- package/corpus/templates/design/actions/connect-localhost.ts +59 -22
- package/corpus/templates/design/actions/delete-design.ts +20 -0
- package/corpus/templates/design/actions/export-zip.ts +23 -8
- package/corpus/templates/design/actions/generate-design.ts +53 -1
- package/corpus/templates/design/actions/get-component-details.ts +21 -26
- package/corpus/templates/design/actions/get-motion-timeline.ts +135 -10
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +116 -0
- package/corpus/templates/design/actions/import-design-tokens.ts +521 -0
- package/corpus/templates/design/actions/index-design-tokens.ts +18 -5
- package/corpus/templates/design/actions/insert-design-native-asset.ts +268 -0
- package/corpus/templates/design/actions/insert-figma-library-asset.ts +233 -0
- package/corpus/templates/design/actions/list-design-extensions.ts +32 -10
- package/corpus/templates/design/actions/list-design-native-assets.ts +105 -0
- package/corpus/templates/design/actions/list-design-states.ts +19 -1
- package/corpus/templates/design/actions/list-figma-library-assets.ts +292 -0
- package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +238 -0
- package/corpus/templates/design/actions/navigate.ts +18 -3
- package/corpus/templates/design/actions/open-component-source.ts +7 -2
- package/corpus/templates/design/actions/preview-component-prop-edit.ts +32 -26
- package/corpus/templates/design/actions/preview-shader-fill.ts +9 -11
- package/corpus/templates/design/actions/provider-api-catalog.ts +2 -2
- package/corpus/templates/design/actions/provider-api-docs.ts +1 -1
- package/corpus/templates/design/actions/provider-api-request.ts +3 -3
- package/corpus/templates/design/actions/revoke-localhost-write-consent.ts +48 -0
- package/corpus/templates/design/actions/run-design-audit.ts +74 -10
- package/corpus/templates/design/actions/run-design-extension-action.ts +1 -1
- package/corpus/templates/design/actions/view-screen.ts +1 -1
- package/corpus/templates/design/actions/write-local-file.ts +213 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +244 -3197
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +30 -20
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +795 -289
- package/corpus/templates/design/app/components/design/EditPanel.tsx +635 -234
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +124 -32
- package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +161 -0
- package/corpus/templates/design/app/components/design/MotionDock.tsx +244 -266
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +491 -56
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +55 -67
- package/corpus/templates/design/app/components/design/StatesPanel.tsx +38 -63
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +264 -8
- package/corpus/templates/design/app/components/design/bridge/codegen.ts +125 -0
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +4037 -0
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +62 -0
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +272 -0
- package/corpus/templates/design/app/components/design/bridge/nav.bridge.ts +116 -0
- package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +24 -0
- package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +78 -0
- package/corpus/templates/design/app/components/design/bridge/tsconfig.json +17 -0
- package/corpus/templates/design/app/components/design/bridge/tweak.bridge.ts +28 -0
- package/corpus/templates/design/app/components/design/bridge/zoom.bridge.ts +43 -0
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +45 -16
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +88 -9
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +94 -9
- package/corpus/templates/design/app/components/layout/Layout.tsx +19 -3
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/design/app/global.css +7 -0
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +23 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +22 -0
- package/corpus/templates/design/app/i18n-data.ts +369 -0
- package/corpus/templates/design/app/lib/pending-generation.ts +1 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +3024 -616
- package/corpus/templates/design/app/pages/Index.tsx +5 -1
- package/corpus/templates/design/changelog/2026-06-30-applying-the-one-click-fix-for-a-small-tap-target-accessibil.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-canvas-creation-screen-dragging-undo-redo-and-image-fill-edi.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-component-prop-edits-and-shader-fills-now-preserve-rapid-sam.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-can-browse-figma-library-components-and-insert-them-i.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-now-has-native-components-embedded-media-and-figma-im.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-preview-now-includes-a-publish-app-waitlist-option-fr.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-s-left-rail-now-matches-the-sidebar-chrome-and-uses-t.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-s-tools-create-menu-now-stays-compact-instead-of-rend.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-starter-prompts-now-begin-generating-immediately-and-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-token-edits-now-stay-visible-in-previews-and-token-li.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-drawing-rectangles-on-the-all-screens-canvas-no-longer-flash.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-fixed-code-native-design-studio-edits-so-component-props-mot.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-fixed-visual-editing-hover-overlays-and-spacing-handles-so-s.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-motion-edits-now-save-automatically-without-a-separate-css-a.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-motion-timelines-now-reopen-as-editable-tracks-after-writing.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-new-design-prompts-with-uploaded-screenshots-work-reliably-o.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-screen-previews-in-all-screens-view-now-keep-rounded-outline.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-selection-outlines-now-sit-flush-on-rectangles-drawn-directl.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-share-options-use-a-tighter-design-editor-popover-with-compa.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-collapsed-motion-panel-now-stays-tucked-under-the-left-s.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-design-inspector-now-shows-states-and-review-in-a-tighte.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-sidebar-keeps-the-organization-picker-space-stable-while.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-tokens-can-now-be-imported-from-pasted-notes-code-files-fold.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +94 -2
- package/corpus/templates/design/e2e/helpers.ts +168 -65
- package/corpus/templates/design/package.json +3 -1
- package/corpus/templates/design/server/db/schema.ts +23 -0
- package/corpus/templates/design/server/handlers/uploads.ts +22 -1
- package/corpus/templates/design/server/lib/provider-api.ts +7 -4
- package/corpus/templates/design/server/lib/verify-write-grant.ts +108 -0
- package/corpus/templates/design/server/plugins/db.ts +39 -0
- package/corpus/templates/design/server/register-secrets.ts +38 -0
- package/corpus/templates/design/shared/board-file.ts +228 -0
- package/corpus/templates/design/shared/board-objects.ts +288 -0
- package/corpus/templates/design/shared/code-layer.ts +52 -0
- package/corpus/templates/design/shared/component-model.ts +35 -0
- package/corpus/templates/design/shared/motion-compiler.ts +137 -15
- package/corpus/templates/design/shared/motion-timeline.ts +2 -2
- package/corpus/templates/design/shared/resolve-tweaks.ts +32 -9
- package/corpus/templates/design/tsconfig.json +2 -1
- package/corpus/templates/slides/AGENTS.md +9 -0
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +4 -0
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +4 -24
- package/corpus/templates/slides/app/lib/export-pdf-client.ts +29 -20
- package/corpus/templates/slides/app/lib/export-pptx-client.ts +365 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +12 -9
- package/corpus/templates/slides/app/vite-env.d.ts +18 -0
- package/corpus/templates/slides/changelog/2026-06-30-powerpoint-downloads-now-preserve-the-rendered-slide-layout-.md +6 -0
- package/corpus/templates/slides/changelog/2026-06-30-unavailable-decks-open-in-a-cleaner-full-height-centered-sta.md +6 -0
- package/corpus/templates/slides/package.json +1 -0
- package/corpus/templates/slides/vite.config.ts +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +94 -10
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/design-connect.d.ts +25 -0
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +357 -18
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +14 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +115 -22
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
- package/dist/client/ConnectBuilderCard.js +1 -0
- package/dist/client/ConnectBuilderCard.js.map +1 -1
- package/dist/client/chat/index.d.ts +1 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/chat/run-recovery.d.ts.map +1 -1
- package/dist/client/chat/run-recovery.js +5 -5
- package/dist/client/chat/run-recovery.js.map +1 -1
- package/dist/client/chat-view-transition.d.ts +6 -0
- package/dist/client/chat-view-transition.d.ts.map +1 -1
- package/dist/client/chat-view-transition.js +23 -2
- package/dist/client/chat-view-transition.js.map +1 -1
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +3 -2
- package/dist/client/composer/PromptComposer.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/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +11 -4
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/sharing/ShareButton.js +3 -3
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +10 -0
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts +8 -2
- package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.js +20 -6
- package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/db/client.d.ts +1 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +28 -2
- package/dist/db/client.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/index.d.ts +5 -4
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +28 -0
- package/dist/provider-api/index.js.map +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts +25 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +43 -18
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/styles/agent-native.css +54 -0
- package/dist/usage/store.js +2 -2
- package/dist/usage/store.js.map +1 -1
- package/package.json +1 -1
|
@@ -57,6 +57,7 @@ export type LayersPanelNodeType =
|
|
|
57
57
|
| "image"
|
|
58
58
|
| "code"
|
|
59
59
|
| "element"
|
|
60
|
+
| "board-element"
|
|
60
61
|
| "unknown";
|
|
61
62
|
|
|
62
63
|
export interface LayersPanelNode {
|
|
@@ -151,6 +152,7 @@ export interface LayersPanelProps {
|
|
|
151
152
|
expandedIds: readonly string[];
|
|
152
153
|
searchQuery: string;
|
|
153
154
|
className?: string;
|
|
155
|
+
footer?: ReactNode;
|
|
154
156
|
labels?: Partial<LayersPanelLabels>;
|
|
155
157
|
onSearchQueryChange: (query: string) => void;
|
|
156
158
|
onScreenSelect?: (id: string) => void;
|
|
@@ -168,9 +170,12 @@ export interface LayersPanelProps {
|
|
|
168
170
|
onLeaveLayer?: (id: string) => void;
|
|
169
171
|
onMoveLayer?: (intent: LayersPanelMoveIntent) => void;
|
|
170
172
|
canMoveLayer?: (intent: LayersPanelMoveIntent) => boolean;
|
|
173
|
+
// Board elements — top-level layer nodes projected from the board file.
|
|
174
|
+
// When absent the panel is unchanged.
|
|
175
|
+
boardElements?: LayersPanelNode[];
|
|
171
176
|
}
|
|
172
177
|
|
|
173
|
-
interface FlatLayerRow {
|
|
178
|
+
export interface FlatLayerRow {
|
|
174
179
|
node: LayersPanelNode;
|
|
175
180
|
rowKey: string;
|
|
176
181
|
depth: number;
|
|
@@ -283,14 +288,16 @@ function buildRootNodes({
|
|
|
283
288
|
layers,
|
|
284
289
|
codeLayers,
|
|
285
290
|
elementLayers,
|
|
291
|
+
boardElements,
|
|
286
292
|
labels,
|
|
287
293
|
}: Pick<
|
|
288
294
|
LayersPanelProps,
|
|
289
|
-
"files" | "layers" | "codeLayers" | "elementLayers"
|
|
295
|
+
"files" | "layers" | "codeLayers" | "elementLayers" | "boardElements"
|
|
290
296
|
> & {
|
|
291
297
|
labels: LayersPanelLabels;
|
|
292
298
|
}) {
|
|
293
299
|
const roots: LayersPanelNode[] = [
|
|
300
|
+
...(boardElements ?? []),
|
|
294
301
|
...(files?.map(asFileNode) ?? []),
|
|
295
302
|
...(layers ?? []),
|
|
296
303
|
];
|
|
@@ -412,6 +419,65 @@ function collectAncestorIds(
|
|
|
412
419
|
return Array.from(ancestors);
|
|
413
420
|
}
|
|
414
421
|
|
|
422
|
+
export function getLayerSelectionAnchorFromExternalSelection(args: {
|
|
423
|
+
selectedIds: readonly string[];
|
|
424
|
+
selectableVisibleIds: readonly string[];
|
|
425
|
+
}): string | null {
|
|
426
|
+
const selectableVisibleIdSet = new Set(args.selectableVisibleIds);
|
|
427
|
+
return (
|
|
428
|
+
[...args.selectedIds]
|
|
429
|
+
.reverse()
|
|
430
|
+
.find((id) => selectableVisibleIdSet.has(id)) ?? null
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export function getTreeOrderedLayerIds(
|
|
435
|
+
ids: readonly string[],
|
|
436
|
+
visibleRows: readonly FlatLayerRow[],
|
|
437
|
+
): string[] {
|
|
438
|
+
const idSet = new Set(ids.filter((id) => id && !id.startsWith("__")));
|
|
439
|
+
const orderedIds = visibleRows
|
|
440
|
+
.map((row) => row.node.id)
|
|
441
|
+
.filter((id) => idSet.has(id));
|
|
442
|
+
const orderedIdSet = new Set(orderedIds);
|
|
443
|
+
return [
|
|
444
|
+
...orderedIds,
|
|
445
|
+
...ids.filter((id) => id && !id.startsWith("__") && !orderedIdSet.has(id)),
|
|
446
|
+
];
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export function getDraggedLayerIdsForRows(args: {
|
|
450
|
+
selectedIds: readonly string[];
|
|
451
|
+
nodeId: string;
|
|
452
|
+
visibleRows: readonly FlatLayerRow[];
|
|
453
|
+
}): string[] {
|
|
454
|
+
const rawDraggedIds = args.selectedIds.includes(args.nodeId)
|
|
455
|
+
? getTreeOrderedLayerIds(args.selectedIds, args.visibleRows)
|
|
456
|
+
: [args.nodeId];
|
|
457
|
+
const draggedIdSet = new Set(rawDraggedIds);
|
|
458
|
+
return rawDraggedIds.filter((id) => {
|
|
459
|
+
const rowForId = args.visibleRows.find((row) => row.node.id === id);
|
|
460
|
+
return !rowForId?.ancestorIds.some((ancestorId) =>
|
|
461
|
+
draggedIdSet.has(ancestorId),
|
|
462
|
+
);
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
export function shouldResyncLayerSelectionAnchor(args: {
|
|
467
|
+
selectionSignature: string;
|
|
468
|
+
lastPanelSelectionSignature: string;
|
|
469
|
+
currentAnchor: string | null;
|
|
470
|
+
selectableVisibleIds: readonly string[];
|
|
471
|
+
}) {
|
|
472
|
+
const anchorStillVisible =
|
|
473
|
+
args.currentAnchor !== null &&
|
|
474
|
+
args.selectableVisibleIds.includes(args.currentAnchor);
|
|
475
|
+
return (
|
|
476
|
+
args.selectionSignature !== args.lastPanelSelectionSignature ||
|
|
477
|
+
!anchorStillVisible
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
|
|
415
481
|
function layerCanShowBadge(node: LayersPanelNode) {
|
|
416
482
|
return (
|
|
417
483
|
node.type === "file" ||
|
|
@@ -432,6 +498,7 @@ export function LayersPanel({
|
|
|
432
498
|
expandedIds,
|
|
433
499
|
searchQuery,
|
|
434
500
|
className,
|
|
501
|
+
footer,
|
|
435
502
|
labels: labelsProp,
|
|
436
503
|
onSearchQueryChange,
|
|
437
504
|
onScreenSelect,
|
|
@@ -446,11 +513,13 @@ export function LayersPanel({
|
|
|
446
513
|
onLeaveLayer,
|
|
447
514
|
onMoveLayer,
|
|
448
515
|
canMoveLayer,
|
|
516
|
+
boardElements,
|
|
449
517
|
}: LayersPanelProps) {
|
|
450
518
|
const t = useT();
|
|
451
519
|
const labels = useMemo(() => mergeLabels(labelsProp, t), [labelsProp, t]);
|
|
452
520
|
const selectedIdSet = useMemo(() => new Set(selectedIds), [selectedIds]);
|
|
453
521
|
const selectedIdsRef = useRef<readonly string[]>(selectedIds);
|
|
522
|
+
const lastPanelSelectionSignatureRef = useRef(selectedIds.join("\0"));
|
|
454
523
|
const expandedIdSet = useMemo(() => new Set(expandedIds), [expandedIds]);
|
|
455
524
|
const lastSelectionAnchorRef = useRef<string | null>(selectedIds[0] ?? null);
|
|
456
525
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
|
@@ -469,9 +538,10 @@ export function LayersPanel({
|
|
|
469
538
|
layers,
|
|
470
539
|
codeLayers,
|
|
471
540
|
elementLayers,
|
|
541
|
+
boardElements,
|
|
472
542
|
labels,
|
|
473
543
|
}),
|
|
474
|
-
[codeLayers, elementLayers, files, labels, layers],
|
|
544
|
+
[boardElements, codeLayers, elementLayers, files, labels, layers],
|
|
475
545
|
);
|
|
476
546
|
|
|
477
547
|
const visibleRows = useMemo(() => {
|
|
@@ -486,9 +556,35 @@ export function LayersPanel({
|
|
|
486
556
|
[roots, selectedIdSet],
|
|
487
557
|
);
|
|
488
558
|
|
|
559
|
+
const selectableVisibleIds = useMemo(
|
|
560
|
+
() =>
|
|
561
|
+
visibleRows
|
|
562
|
+
.map(({ node }) => node)
|
|
563
|
+
.filter((node) => node.selectable !== false)
|
|
564
|
+
.map((node) => node.id),
|
|
565
|
+
[visibleRows],
|
|
566
|
+
);
|
|
567
|
+
|
|
489
568
|
useLayoutEffect(() => {
|
|
490
569
|
selectedIdsRef.current = selectedIds;
|
|
491
|
-
|
|
570
|
+
const signature = selectedIds.join("\0");
|
|
571
|
+
if (
|
|
572
|
+
!shouldResyncLayerSelectionAnchor({
|
|
573
|
+
selectionSignature: signature,
|
|
574
|
+
lastPanelSelectionSignature: lastPanelSelectionSignatureRef.current,
|
|
575
|
+
currentAnchor: lastSelectionAnchorRef.current,
|
|
576
|
+
selectableVisibleIds,
|
|
577
|
+
})
|
|
578
|
+
) {
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
lastPanelSelectionSignatureRef.current = signature;
|
|
582
|
+
lastSelectionAnchorRef.current =
|
|
583
|
+
getLayerSelectionAnchorFromExternalSelection({
|
|
584
|
+
selectedIds,
|
|
585
|
+
selectableVisibleIds,
|
|
586
|
+
});
|
|
587
|
+
}, [selectableVisibleIds, selectedIds]);
|
|
492
588
|
|
|
493
589
|
useEffect(() => {
|
|
494
590
|
if (selectedAncestorIds.length === 0) return;
|
|
@@ -522,15 +618,6 @@ export function LayersPanel({
|
|
|
522
618
|
return () => window.cancelAnimationFrame(frame);
|
|
523
619
|
}, [selectedScrollRowKey]);
|
|
524
620
|
|
|
525
|
-
const selectableVisibleIds = useMemo(
|
|
526
|
-
() =>
|
|
527
|
-
visibleRows
|
|
528
|
-
.map(({ node }) => node)
|
|
529
|
-
.filter((node) => node.selectable !== false)
|
|
530
|
-
.map((node) => node.id),
|
|
531
|
-
[visibleRows],
|
|
532
|
-
);
|
|
533
|
-
|
|
534
621
|
const selectNode = useCallback(
|
|
535
622
|
(
|
|
536
623
|
id: string,
|
|
@@ -582,6 +669,7 @@ export function LayersPanel({
|
|
|
582
669
|
if (!options.range) {
|
|
583
670
|
lastSelectionAnchorRef.current = id;
|
|
584
671
|
}
|
|
672
|
+
lastPanelSelectionSignatureRef.current = nextIds.join("\0");
|
|
585
673
|
onSelectionChange(nextIds, { id, selectedIds: nextIds, ...options });
|
|
586
674
|
},
|
|
587
675
|
[onSelectionChange, selectableVisibleIds],
|
|
@@ -838,6 +926,7 @@ export function LayersPanel({
|
|
|
838
926
|
</div>
|
|
839
927
|
)}
|
|
840
928
|
</div>
|
|
929
|
+
{footer ? <div className="shrink-0">{footer}</div> : null}
|
|
841
930
|
</aside>
|
|
842
931
|
);
|
|
843
932
|
}
|
|
@@ -948,12 +1037,15 @@ function LayerRow({
|
|
|
948
1037
|
};
|
|
949
1038
|
|
|
950
1039
|
const handleKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {
|
|
951
|
-
const
|
|
1040
|
+
const getFocusableButtons = () => {
|
|
952
1041
|
const tree = event.currentTarget.closest('[role="tree"]');
|
|
953
|
-
if (!tree) return
|
|
954
|
-
|
|
1042
|
+
if (!tree) return [];
|
|
1043
|
+
return Array.from(
|
|
955
1044
|
tree.querySelectorAll<HTMLButtonElement>("[data-layer-row-button]"),
|
|
956
1045
|
).filter((button) => !button.disabled);
|
|
1046
|
+
};
|
|
1047
|
+
const focusVisibleButton = (nextIndex: number) => {
|
|
1048
|
+
const buttons = getFocusableButtons();
|
|
957
1049
|
const target =
|
|
958
1050
|
buttons[Math.max(0, Math.min(buttons.length - 1, nextIndex))];
|
|
959
1051
|
target?.focus();
|
|
@@ -971,6 +1063,8 @@ function LayerRow({
|
|
|
971
1063
|
const currentIndex = visibleRows.findIndex(
|
|
972
1064
|
(visibleRow) => visibleRow.rowKey === row.rowKey,
|
|
973
1065
|
);
|
|
1066
|
+
const focusableButtons = getFocusableButtons();
|
|
1067
|
+
const currentFocusableIndex = focusableButtons.indexOf(event.currentTarget);
|
|
974
1068
|
|
|
975
1069
|
if (event.key === "Enter" || event.key === " " || event.key === "Space") {
|
|
976
1070
|
event.preventDefault();
|
|
@@ -990,12 +1084,12 @@ function LayerRow({
|
|
|
990
1084
|
}
|
|
991
1085
|
if (event.key === "ArrowDown") {
|
|
992
1086
|
event.preventDefault();
|
|
993
|
-
focusVisibleButton(
|
|
1087
|
+
focusVisibleButton(currentFocusableIndex + 1);
|
|
994
1088
|
return;
|
|
995
1089
|
}
|
|
996
1090
|
if (event.key === "ArrowUp") {
|
|
997
1091
|
event.preventDefault();
|
|
998
|
-
focusVisibleButton(
|
|
1092
|
+
focusVisibleButton(currentFocusableIndex - 1);
|
|
999
1093
|
return;
|
|
1000
1094
|
}
|
|
1001
1095
|
if (event.key === "Home") {
|
|
@@ -1005,7 +1099,7 @@ function LayerRow({
|
|
|
1005
1099
|
}
|
|
1006
1100
|
if (event.key === "End") {
|
|
1007
1101
|
event.preventDefault();
|
|
1008
|
-
focusVisibleButton(
|
|
1102
|
+
focusVisibleButton(focusableButtons.length - 1);
|
|
1009
1103
|
return;
|
|
1010
1104
|
}
|
|
1011
1105
|
if (event.key === "ArrowRight" && hasChildren && !isExpanded) {
|
|
@@ -1015,9 +1109,13 @@ function LayerRow({
|
|
|
1015
1109
|
}
|
|
1016
1110
|
if (event.key === "ArrowRight" && hasChildren && isExpanded) {
|
|
1017
1111
|
event.preventDefault();
|
|
1018
|
-
const
|
|
1112
|
+
const nextButton = focusableButtons[currentFocusableIndex + 1];
|
|
1113
|
+
const childId = nextButton?.dataset.layerNodeId;
|
|
1114
|
+
const child = childId
|
|
1115
|
+
? visibleRows.find((visibleRow) => visibleRow.node.id === childId)
|
|
1116
|
+
: null;
|
|
1019
1117
|
if (child?.ancestorIds.includes(node.id)) {
|
|
1020
|
-
focusVisibleButton(
|
|
1118
|
+
focusVisibleButton(currentFocusableIndex + 1);
|
|
1021
1119
|
}
|
|
1022
1120
|
return;
|
|
1023
1121
|
}
|
|
@@ -1038,17 +1136,10 @@ function LayerRow({
|
|
|
1038
1136
|
event.preventDefault();
|
|
1039
1137
|
return;
|
|
1040
1138
|
}
|
|
1041
|
-
const
|
|
1042
|
-
|
|
1043
|
-
:
|
|
1044
|
-
|
|
1045
|
-
// When a parent is moved, its children move with it automatically.
|
|
1046
|
-
const draggedIdSet = new Set(rawDraggedIds);
|
|
1047
|
-
const draggedIds = rawDraggedIds.filter((id) => {
|
|
1048
|
-
const rowForId = visibleRows.find((r) => r.node.id === id);
|
|
1049
|
-
return !rowForId?.ancestorIds.some((ancestorId) =>
|
|
1050
|
-
draggedIdSet.has(ancestorId),
|
|
1051
|
-
);
|
|
1139
|
+
const draggedIds = getDraggedLayerIdsForRows({
|
|
1140
|
+
selectedIds,
|
|
1141
|
+
nodeId: node.id,
|
|
1142
|
+
visibleRows,
|
|
1052
1143
|
});
|
|
1053
1144
|
event.dataTransfer.effectAllowed = "move";
|
|
1054
1145
|
event.dataTransfer.setData("application/x-design-layer-id", node.id);
|
|
@@ -1477,6 +1568,7 @@ function LayerGlyph({
|
|
|
1477
1568
|
case "component":
|
|
1478
1569
|
case "instance":
|
|
1479
1570
|
return <ComponentLayerGlyph className={cn(common, componentColor)} />;
|
|
1571
|
+
case "board-element":
|
|
1480
1572
|
case "shape":
|
|
1481
1573
|
case "rectangle":
|
|
1482
1574
|
return <RectangleLayerGlyph className={common} />;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { callAction } from "@agent-native/core/client";
|
|
2
|
+
import { IconDeviceFloppy, IconFolderOpen } from "@tabler/icons-react";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
|
|
5
|
+
import { Button } from "@/components/ui/button";
|
|
6
|
+
import {
|
|
7
|
+
Dialog,
|
|
8
|
+
DialogContent,
|
|
9
|
+
DialogDescription,
|
|
10
|
+
DialogFooter,
|
|
11
|
+
DialogHeader,
|
|
12
|
+
DialogTitle,
|
|
13
|
+
} from "@/components/ui/dialog";
|
|
14
|
+
import { Spinner } from "@/components/ui/spinner";
|
|
15
|
+
|
|
16
|
+
export interface LocalhostWriteConsentPayload {
|
|
17
|
+
/** The path being granted write access. */
|
|
18
|
+
rootPath: string;
|
|
19
|
+
/** File(s) about to be written (for display only). */
|
|
20
|
+
files: string[];
|
|
21
|
+
/** Pending callback to invoke after user grants consent. */
|
|
22
|
+
onGranted: (grant: {
|
|
23
|
+
grantId: string;
|
|
24
|
+
bridgeToken: string;
|
|
25
|
+
rootPath: string;
|
|
26
|
+
grantedUntil: string;
|
|
27
|
+
}) => void;
|
|
28
|
+
/** Called when the user cancels. */
|
|
29
|
+
onCancel: () => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface LocalhostWriteConsentDialogProps {
|
|
33
|
+
open: boolean;
|
|
34
|
+
onOpenChange: (open: boolean) => void;
|
|
35
|
+
designId: string;
|
|
36
|
+
connectionId: string;
|
|
37
|
+
payload: LocalhostWriteConsentPayload | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Modal dialog requesting explicit user consent before the agent writes local
|
|
42
|
+
* files. Shows the rootPath that will be granted write access and the specific
|
|
43
|
+
* file(s) about to be modified. The grant expires after 8 hours and is scoped
|
|
44
|
+
* to that folder only.
|
|
45
|
+
*/
|
|
46
|
+
export function LocalhostWriteConsentDialog({
|
|
47
|
+
open,
|
|
48
|
+
onOpenChange,
|
|
49
|
+
designId,
|
|
50
|
+
connectionId,
|
|
51
|
+
payload,
|
|
52
|
+
}: LocalhostWriteConsentDialogProps) {
|
|
53
|
+
const [granting, setGranting] = useState(false);
|
|
54
|
+
|
|
55
|
+
async function handleAllowWrites() {
|
|
56
|
+
if (!payload || !designId || !connectionId) return;
|
|
57
|
+
setGranting(true);
|
|
58
|
+
try {
|
|
59
|
+
const result = await callAction<{
|
|
60
|
+
grantId: string;
|
|
61
|
+
bridgeToken: string;
|
|
62
|
+
rootPath: string;
|
|
63
|
+
grantedUntil: string;
|
|
64
|
+
}>("grant-localhost-write-consent", {
|
|
65
|
+
designId,
|
|
66
|
+
connectionId,
|
|
67
|
+
});
|
|
68
|
+
onOpenChange(false);
|
|
69
|
+
payload.onGranted(result);
|
|
70
|
+
} catch {
|
|
71
|
+
// Silently close; the caller's onCancel path already guards the write.
|
|
72
|
+
onOpenChange(false);
|
|
73
|
+
payload.onCancel();
|
|
74
|
+
} finally {
|
|
75
|
+
setGranting(false);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function handleCancel() {
|
|
80
|
+
payload?.onCancel();
|
|
81
|
+
onOpenChange(false);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!payload) return null;
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Dialog
|
|
88
|
+
open={open}
|
|
89
|
+
onOpenChange={(next) => {
|
|
90
|
+
if (!next) handleCancel();
|
|
91
|
+
else onOpenChange(next);
|
|
92
|
+
}}
|
|
93
|
+
>
|
|
94
|
+
<DialogContent className="max-w-md">
|
|
95
|
+
<DialogHeader>
|
|
96
|
+
<DialogTitle className="flex items-center gap-2">
|
|
97
|
+
<IconDeviceFloppy className="size-4" />
|
|
98
|
+
{"Allow file writes" /* i18n-ignore */}
|
|
99
|
+
</DialogTitle>
|
|
100
|
+
<DialogDescription>
|
|
101
|
+
{
|
|
102
|
+
"The agent wants to write source files on your machine. This access is scoped to the folder below and expires automatically after 8 hours." /* i18n-ignore */
|
|
103
|
+
}
|
|
104
|
+
</DialogDescription>
|
|
105
|
+
</DialogHeader>
|
|
106
|
+
|
|
107
|
+
<div className="space-y-3 py-1 text-sm">
|
|
108
|
+
<div className="rounded-lg border bg-muted/50 px-3 py-2">
|
|
109
|
+
<div className="flex items-center gap-2 text-xs font-medium text-muted-foreground mb-1">
|
|
110
|
+
<IconFolderOpen className="size-3.5 shrink-0" />
|
|
111
|
+
{"Root folder" /* i18n-ignore */}
|
|
112
|
+
</div>
|
|
113
|
+
<code className="break-all text-xs text-foreground">
|
|
114
|
+
{payload.rootPath}
|
|
115
|
+
</code>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
{payload.files.length > 0 && (
|
|
119
|
+
<div className="space-y-1">
|
|
120
|
+
<p className="text-xs font-medium text-muted-foreground">
|
|
121
|
+
{"Files to be written:" /* i18n-ignore */}
|
|
122
|
+
</p>
|
|
123
|
+
<ul className="space-y-0.5 pl-1">
|
|
124
|
+
{payload.files.map((file) => (
|
|
125
|
+
<li key={file} className="flex items-center gap-1.5 text-xs">
|
|
126
|
+
<span className="size-1 rounded-full bg-muted-foreground/50 shrink-0" />
|
|
127
|
+
<code className="break-all">{file}</code>
|
|
128
|
+
</li>
|
|
129
|
+
))}
|
|
130
|
+
</ul>
|
|
131
|
+
</div>
|
|
132
|
+
)}
|
|
133
|
+
|
|
134
|
+
<p className="text-xs text-muted-foreground">
|
|
135
|
+
{
|
|
136
|
+
"Only .html and .css files can be written. Paths outside the root folder are always blocked." /* i18n-ignore */
|
|
137
|
+
}
|
|
138
|
+
</p>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<DialogFooter className="gap-2">
|
|
142
|
+
<Button variant="outline" onClick={handleCancel} disabled={granting}>
|
|
143
|
+
{"Cancel" /* i18n-ignore */}
|
|
144
|
+
</Button>
|
|
145
|
+
<Button onClick={() => void handleAllowWrites()} disabled={granting}>
|
|
146
|
+
{
|
|
147
|
+
granting ? (
|
|
148
|
+
<>
|
|
149
|
+
<Spinner className="mr-2 size-3.5" />
|
|
150
|
+
{"Granting…" /* i18n-ignore */}
|
|
151
|
+
</>
|
|
152
|
+
) : (
|
|
153
|
+
"Allow writes"
|
|
154
|
+
) /* i18n-ignore */
|
|
155
|
+
}
|
|
156
|
+
</Button>
|
|
157
|
+
</DialogFooter>
|
|
158
|
+
</DialogContent>
|
|
159
|
+
</Dialog>
|
|
160
|
+
);
|
|
161
|
+
}
|