@agent-native/core 0.90.1 → 0.90.2
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 +14 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/AgentPanel.tsx +1 -1
- package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +11 -6
- package/corpus/core/src/client/use-action.ts +39 -2
- package/corpus/core/src/client/use-db-sync.ts +44 -18
- package/corpus/core/src/db/create-get-db.ts +32 -2
- package/corpus/core/src/server/framework-request-handler.ts +52 -4
- package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +5 -0
- package/corpus/templates/assets/.agents/skills/logo-composite/SKILL.md +34 -6
- package/corpus/templates/assets/AGENTS.md +23 -4
- package/corpus/templates/assets/actions/_generation-preset-settings.ts +48 -0
- package/corpus/templates/assets/actions/_helpers.ts +8 -1
- package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +64 -0
- package/corpus/templates/assets/actions/create-generation-preset.ts +11 -3
- package/corpus/templates/assets/actions/generate-image-batch.ts +7 -2
- package/corpus/templates/assets/actions/generate-image.ts +342 -25
- package/corpus/templates/assets/actions/get-library.ts +4 -3
- package/corpus/templates/assets/actions/navigate.ts +2 -1
- package/corpus/templates/assets/actions/refresh-generation-run.ts +5 -1
- package/corpus/templates/assets/actions/update-asset.ts +1 -0
- package/corpus/templates/assets/actions/update-generation-preset.ts +8 -1
- package/corpus/templates/assets/actions/view-screen.ts +8 -1
- package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +9 -1
- package/corpus/templates/assets/app/hooks/use-image-model-menu.ts +1 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +11 -0
- package/corpus/templates/assets/app/i18n/zh-TW.ts +64 -0
- package/corpus/templates/assets/app/i18n-data.ts +650 -0
- package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +44 -33
- package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1474 -0
- package/corpus/templates/assets/app/routes/library.tsx +43 -1
- package/corpus/templates/assets/server/lib/generation.ts +366 -44
- package/corpus/templates/assets/server/lib/image-processing.ts +252 -0
- package/corpus/templates/assets/server/lib/preset-skeleton.ts +137 -0
- package/corpus/templates/assets/shared/api.ts +47 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -2
- package/corpus/templates/clips/app/components/library/sort-menu.tsx +1 -1
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +114 -33
- package/corpus/templates/clips/app/routes/_app.library._index.tsx +27 -1
- package/corpus/templates/clips/app/routes/record.tsx +157 -68
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +5 -5
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +20 -3
- package/corpus/templates/clips/shared/finalize-recovery.ts +23 -3
- package/corpus/templates/content/AGENTS.md +52 -52
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +188 -8
- package/corpus/templates/content/actions/_builder-docs-client.ts +41 -3
- package/corpus/templates/content/actions/_database-source-utils.ts +998 -160
- package/corpus/templates/content/actions/_database-utils.ts +44 -4
- package/corpus/templates/content/actions/_notion-action-utils.ts +6 -2
- package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -1
- package/corpus/templates/content/actions/attach-content-database-source.ts +85 -12
- package/corpus/templates/content/actions/change-content-database-source-role.ts +9 -1
- package/corpus/templates/content/actions/delete-content-database.ts +1 -0
- package/corpus/templates/content/actions/get-document.ts +11 -2
- package/corpus/templates/content/actions/list-content-databases.ts +57 -9
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +23 -6
- package/corpus/templates/content/actions/refresh-content-database-source.ts +26 -3
- package/corpus/templates/content/actions/restore-content-database.ts +1 -0
- package/corpus/templates/content/actions/update-document.ts +43 -0
- package/corpus/templates/content/app/components/editor/BuilderBodySyncingNotice.tsx +21 -0
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +0 -1
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +70 -28
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +69 -17
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +4 -4
- package/corpus/templates/content/app/components/editor/body-hydration.ts +112 -0
- package/corpus/templates/content/app/components/editor/builder-body-hydration-pump.ts +52 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +9076 -2876
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +41 -1
- package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +36 -11
- package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +21 -1
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +11 -1
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +11 -8
- package/corpus/templates/content/app/global.css +41 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +346 -42
- package/corpus/templates/content/app/hooks/use-db-sync.ts +9 -0
- package/corpus/templates/content/app/hooks/use-documents.ts +47 -0
- package/corpus/templates/content/app/hooks/use-notion.ts +12 -10
- package/corpus/templates/content/app/i18n/zh-TW.ts +35 -0
- package/corpus/templates/content/app/i18n-data.ts +355 -0
- package/corpus/templates/content/app/root.tsx +22 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-database-sources-keep-loading-rows-in-the-background.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-attach-starts-with-usable-rows.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-attachment-now-stays-connected-after-large.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-refreshes-now-show-progress-for-large-docs-blog.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-database-pages-now-delete-to-trash-without-route-errors.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-builder-source-refreshes-recover-from-stalled-background-row.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-builder-sourced-rows-open-with-sync-protection.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-database-rows-and-pages-open-faster-from-recently-loaded-dat.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-preview-panels-close-reliably-narrow-sidebar-row-actions-sta.md +6 -0
- package/corpus/templates/content/changelog/2026-07-06-builder-sync-progress-no-longer-shows-continue.md +6 -0
- package/corpus/templates/content/shared/api.ts +13 -0
- package/corpus/templates/content/shared/builder-mdx.ts +16 -3
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +23 -13
- package/corpus/templates/design/.agents/skills/responsive-breakpoints/SKILL.md +98 -0
- package/corpus/templates/design/.agents/skills/shader-fills/SKILL.md +162 -0
- package/corpus/templates/design/AGENTS.md +9 -2
- package/corpus/templates/design/actions/add-breakpoint.ts +6 -3
- package/corpus/templates/design/actions/apply-a11y-fix.ts +57 -37
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +41 -20
- package/corpus/templates/design/actions/apply-motion-edit.ts +251 -38
- package/corpus/templates/design/actions/apply-shader-fill.ts +90 -66
- package/corpus/templates/design/actions/apply-visual-edit.ts +282 -55
- package/corpus/templates/design/actions/create-component.ts +29 -60
- package/corpus/templates/design/actions/create-file.ts +9 -2
- package/corpus/templates/design/actions/duplicate-design.ts +31 -2
- package/corpus/templates/design/actions/edit-design.ts +36 -38
- package/corpus/templates/design/actions/generate-design-title.ts +87 -0
- package/corpus/templates/design/actions/generate-design.ts +53 -18
- package/corpus/templates/design/actions/generate-screens.ts +108 -4
- package/corpus/templates/design/actions/get-motion-timeline.ts +35 -7
- package/corpus/templates/design/actions/insert-asset.ts +34 -29
- package/corpus/templates/design/actions/insert-design-native-asset.ts +129 -32
- package/corpus/templates/design/actions/insert-figma-library-asset.ts +35 -30
- package/corpus/templates/design/actions/present-design-variants.ts +7 -2
- package/corpus/templates/design/actions/remove-motion-timeline.ts +31 -36
- package/corpus/templates/design/actions/update-file.ts +190 -75
- package/corpus/templates/design/app/components/design/BreakpointBar.tsx +403 -0
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +469 -222
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +708 -53
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +83 -3
- package/corpus/templates/design/app/components/design/EditPanel.tsx +1091 -6948
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +419 -85
- package/corpus/templates/design/app/components/design/MotionDock.tsx +1124 -103
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +2610 -1398
- package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +22 -0
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2182 -235
- package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +187 -11
- package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +376 -1
- package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +746 -0
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +17 -0
- package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +1 -1
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +1 -3
- package/corpus/templates/design/app/components/design/code-workbench/quickinput/QuickInput.tsx +1 -1
- package/corpus/templates/design/app/components/design/edit-panel/appearance-properties.tsx +518 -0
- package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +562 -0
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +763 -0
- package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +94 -0
- package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +780 -0
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +446 -0
- package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +79 -0
- package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +315 -0
- package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +344 -0
- package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +594 -0
- package/corpus/templates/design/app/components/design/edit-panel/frame-presets-panel.tsx +119 -0
- package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +248 -0
- package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +45 -0
- package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +693 -0
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +724 -0
- package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +207 -0
- package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +628 -0
- package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +91 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +649 -0
- package/corpus/templates/design/app/components/design/edit-panel/style-change-types.ts +118 -0
- package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +88 -0
- package/corpus/templates/design/app/components/design/edit-panel/transform-helpers.ts +100 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +117 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +501 -0
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +91 -40
- package/corpus/templates/design/app/components/design/inspector/BreakpointOverrideIndicator.tsx +82 -0
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +189 -87
- package/corpus/templates/design/app/components/design/inspector/GlslShaderPanel.tsx +1097 -0
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +44 -0
- package/corpus/templates/design/app/components/design/inspector/InteractionStatePanel.tsx +258 -0
- package/corpus/templates/design/app/components/design/inspector/MotionKeyframeDiamond.tsx +144 -0
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +99 -9
- package/corpus/templates/design/app/components/design/inspector/frame-size-presets.ts +116 -0
- package/corpus/templates/design/app/components/design/inspector/index.ts +26 -0
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +29 -0
- package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +171 -0
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +220 -0
- package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +35 -0
- package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +134 -0
- package/corpus/templates/design/app/components/design/multi-screen/culling.ts +185 -0
- package/corpus/templates/design/app/components/design/multi-screen/gradient-overlay-geometry.ts +120 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +892 -0
- package/corpus/templates/design/app/components/design/multi-screen/vector-edit-geometry.ts +21 -0
- package/corpus/templates/design/app/components/design/types.ts +15 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +109 -5
- package/corpus/templates/design/app/global.css +8 -8
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +259 -2
- package/corpus/templates/design/app/i18n/zh-TW.ts +163 -9
- package/corpus/templates/design/app/i18n-breakpoints.ts +205 -0
- package/corpus/templates/design/app/i18n-data.ts +1664 -152
- package/corpus/templates/design/app/i18n-motion.ts +563 -0
- package/corpus/templates/design/app/lib/design-import.ts +73 -0
- package/corpus/templates/design/app/pages/Index.tsx +41 -22
- package/corpus/templates/design/changelog/2026-06-29-design-inspector-controls-now-match-figmas-softer-dark-chrome.md +1 -1
- package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +1 -1
- package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +1 -1
- package/corpus/templates/design/changelog/2026-06-30-multi-select-outlines-now-stay-consistent-across-all-screens.md +1 -1
- package/corpus/templates/design/changelog/2026-07-03-canvas-interactions-now-match-figma-rotation-aware-resizing-.md +1 -1
- package/corpus/templates/design/changelog/2026-07-03-layers-panel-matches-figma-top-layer-on-top-drag-with-auto-s.md +1 -1
- package/corpus/templates/design/changelog/2026-07-05-designs-get-ai-generated-names-from-your-prompt.md +6 -0
- package/corpus/templates/design/changelog/2026-07-05-local-code-screens-keep-their-connection-when-duplicated-and.md +6 -0
- package/corpus/templates/design/changelog/2026-07-05-press-a-to-draw-screens-on-the-canvas-inside-a-screen-it-dr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-3d-rotation-and-perspective-controls-on-every-element.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-ai-generated-and-duplicated-screens-now-get-their-editor-sel.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-applying-shaders-and-fills-in-quick-succession-can-no-longer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-breakpoint-frames-in-the-overview-now-support-click-to-selec.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-breakpoint-segments-in-the-inspector-now-show-a-device-icon-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-breakpoint-targeting-now-lives-in-one-compact-control-in-the.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-canvas-edits-can-no-longer-be-overwritten-by-stale-collabora.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-canvas-edits-inside-screens-save-reliably-again.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-cleaner-inspector-removed-the-ask-ai-box-from-the-design-tab.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-clicking-empty-canvas-space-now-deselects-an-element-inside-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-dragging-an-element-onto-a-rectangle-now-nests-it-inside-wit.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-dragging-screens-and-objects-on-the-canvas-is-now-much-smoot.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-drawing-a-shape-or-text-on-the-canvas-no-longer-flashes-away.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-dropping-an-element-between-two-others-inside-a-screen-now-s.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-duplicating-a-screen-now-places-the-copy-exactly-where-you-d.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-edit-gradients-directly-on-elements-inside-screens-scale-str.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-edits-from-collaborators-and-the-agent-now-update-the-canvas.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-a-bug-where-most-style-position-and-text-edits-briefly.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-a-confusing-raw-error-message-when-a-file-upload-fails.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-a-rare-case-where-an-edit-could-be-silently-overwritte.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-inserted-assets-sometimes-disappearing-reappearing-or-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-inspector-value-scrubbing-producing-erratic-numbers-an.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-padding-gap-and-min-max-size-scrubbing-not-updating-th.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-text-stroke-color-turning-black-and-glyphs-disappearin.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-the-collapsed-effects-and-stroke-inspector-sections-sh.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-the-new-design-dialog-closing-unexpectedly-when-using-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-the-typography-section-missing-when-selecting-text-nes.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-full-view-no-longer-flashes-back-to-the-overview-on-entry-no.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-keyframe-animation-timeline-per-property-keyframes-easing-cu.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-moving-restyling-and-scrubbing-values-on-elements-in-ai-gene.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-padding-and-corner-radius-value-scrubbing-now-snaps-to-whole.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-padding-handles-now-only-resize-on-the-handle-line-itself-sh.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-preview-and-edit-hover-focus-and-pressed-states-with-real-cs.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-promoting-an-element-to-a-component-now-works-correctly-for-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-reduced-url-history-churn-and-selection-overlay-flicker-whil.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-removed-the-decorative-dot-before-screen-names-in-the-overvi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-responsive-breakpoints-edit-at-any-width-with-cascading-over.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-screen-labels-no-longer-turn-blue-when-you-hover-inside-the-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-scrolling-a-screen-with-an-element-selected-stays-smooth-on-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-selecting-multiple-elements-and-dragging-now-moves-the-whole.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-selection-borders-handles-and-value-readouts-now-stay-the-sa.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-selection-handles-and-editing-chrome-now-stay-a-constant-siz.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-shader-fills-and-effects-ai-authored-or-preset-glsl-shaders-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-text-drawn-on-the-canvas-now-defaults-to-white-and-inter-in-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-text-dropped-into-a-shape-now-stays-readable-instead-of-disa.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-add-tweak-controls-prompt-no-longer-jumps-to-the-corner-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-padding-value-readout-now-stays-visible-while-you-hover-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-selected-element-attachment-now-clears-from-the-chat-com.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-zoom-menu-is-more-compact-with-tight-rows-and-right-alig.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-undo-and-redo-no-longer-flash-the-screen-changes-now-apply-i.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-zoom-presets-like-zoom-to-50-now-stay-on-the-focused-screen-.md +6 -0
- package/corpus/templates/design/server/lib/import-design-files.ts +11 -2
- package/corpus/templates/design/server/source-workspace.ts +139 -59
- package/corpus/templates/design/shared/board-file.ts +11 -2
- package/corpus/templates/design/shared/breakpoint-media.ts +543 -0
- package/corpus/templates/design/shared/canvas-math.ts +354 -35
- package/corpus/templates/design/shared/code-layer.ts +277 -8
- package/corpus/templates/design/shared/design-state.ts +11 -4
- package/corpus/templates/design/shared/interaction-states.ts +548 -0
- package/corpus/templates/design/shared/motion-compiler.ts +186 -23
- package/corpus/templates/design/shared/motion-easing.ts +294 -0
- package/corpus/templates/design/shared/motion-timeline.ts +400 -34
- package/corpus/templates/design/shared/prompt-title.ts +95 -0
- package/corpus/templates/design/shared/responsive-classes.ts +448 -10
- package/corpus/templates/design/shared/screen-annotation.ts +46 -0
- package/corpus/templates/design/shared/shader-fills.ts +1009 -0
- package/corpus/templates/design/shared/shader-presets.ts +852 -4
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +15 -2
- package/corpus/templates/slides/app/components/editor/ImportButton.tsx +10 -1
- package/corpus/templates/slides/app/lib/upload-response.ts +71 -0
- package/dist/client/AgentPanel.js +1 -1
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +11 -6
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/dist/client/use-action.d.ts +3 -0
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +34 -3
- package/dist/client/use-action.js.map +1 -1
- package/dist/client/use-db-sync.d.ts +4 -0
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +37 -18
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/create-get-db.d.ts +25 -0
- package/dist/db/create-get-db.d.ts.map +1 -1
- package/dist/db/create-get-db.js +25 -2
- package/dist/db/create-get-db.js.map +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +37 -1
- package/dist/server/framework-request-handler.js.map +1 -1
- package/package.json +1 -1
- package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +0 -153
- package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +0 -133
- package/corpus/templates/design/app/pages/DesignEditor.tsx +0 -22924
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frame-tool size presets — the equivalent of Figma's right-panel preset list
|
|
3
|
+
* that replaces the inspector while the Frame tool (F / A) is armed. Each
|
|
4
|
+
* category maps to a `framePresets.categories.<key>` i18n label; preset
|
|
5
|
+
* `name` values are product/device names and are intentionally left as
|
|
6
|
+
* untranslated literals (matching Figma's own behavior).
|
|
7
|
+
*
|
|
8
|
+
* Pure data + a couple of small pure helpers so the shape can be unit tested
|
|
9
|
+
* without mounting EditPanel.
|
|
10
|
+
*
|
|
11
|
+
* i18n-raw-literal-disable-file: every `name` below is a real-world
|
|
12
|
+
* product/device name (iPhone, Android, Instagram Post, etc.) and must stay
|
|
13
|
+
* untranslated in every locale, matching Figma's own preset list behavior.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export type FrameSizePresetCategoryKey =
|
|
17
|
+
| "phone"
|
|
18
|
+
| "tablet"
|
|
19
|
+
| "desktop"
|
|
20
|
+
| "presentation"
|
|
21
|
+
| "watch"
|
|
22
|
+
| "paper"
|
|
23
|
+
| "socialMedia";
|
|
24
|
+
|
|
25
|
+
export interface FrameSizePreset {
|
|
26
|
+
/** Product/device name — kept as a literal, never translated. */
|
|
27
|
+
name: string;
|
|
28
|
+
width: number;
|
|
29
|
+
height: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface FrameSizePresetCategory {
|
|
33
|
+
key: FrameSizePresetCategoryKey;
|
|
34
|
+
presets: FrameSizePreset[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Ordered categories, first-group-expanded-by-default matching Figma (Phone
|
|
39
|
+
* first). Sizes are Figma's current preset list, abbreviated to the most
|
|
40
|
+
* common devices per category.
|
|
41
|
+
*/
|
|
42
|
+
export const FRAME_SIZE_PRESET_CATEGORIES: FrameSizePresetCategory[] = [
|
|
43
|
+
{
|
|
44
|
+
key: "phone",
|
|
45
|
+
presets: [
|
|
46
|
+
{ name: "iPhone 17", width: 402, height: 874 },
|
|
47
|
+
{ name: "iPhone 16 & 17 Pro", width: 402, height: 874 },
|
|
48
|
+
{ name: "iPhone 16", width: 393, height: 852 },
|
|
49
|
+
{ name: "iPhone 16 & 17 Pro Max", width: 440, height: 956 },
|
|
50
|
+
{ name: "iPhone 16 Plus", width: 430, height: 932 },
|
|
51
|
+
{ name: "iPhone Air", width: 420, height: 912 },
|
|
52
|
+
{ name: "iPhone 14 & 15 Pro Max", width: 430, height: 932 },
|
|
53
|
+
{ name: "iPhone 14 & 15 Pro", width: 393, height: 852 },
|
|
54
|
+
{ name: "iPhone 13 & 14", width: 390, height: 844 },
|
|
55
|
+
{ name: "iPhone 14 Plus", width: 428, height: 926 },
|
|
56
|
+
{ name: "Android Compact", width: 412, height: 917 },
|
|
57
|
+
{ name: "Android Medium", width: 700, height: 840 },
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
key: "tablet",
|
|
62
|
+
presets: [
|
|
63
|
+
{ name: "iPad mini 8.3", width: 744, height: 1133 },
|
|
64
|
+
{ name: "Surface Pro 8", width: 1440, height: 960 },
|
|
65
|
+
{ name: 'iPad Pro 11"', width: 834, height: 1194 },
|
|
66
|
+
{ name: 'iPad Pro 12.9"', width: 1024, height: 1366 },
|
|
67
|
+
{ name: "Android Expanded", width: 1280, height: 800 },
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
key: "desktop",
|
|
72
|
+
presets: [
|
|
73
|
+
{ name: "Desktop", width: 1440, height: 1024 },
|
|
74
|
+
{ name: "Wireframe", width: 1440, height: 1024 },
|
|
75
|
+
{ name: "MacBook Air", width: 1280, height: 832 },
|
|
76
|
+
{ name: 'MacBook Pro 14"', width: 1512, height: 982 },
|
|
77
|
+
{ name: 'MacBook Pro 16"', width: 1728, height: 1117 },
|
|
78
|
+
{ name: "TV", width: 1280, height: 720 },
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
key: "presentation",
|
|
83
|
+
presets: [
|
|
84
|
+
{ name: "Slide 16:9", width: 1920, height: 1080 },
|
|
85
|
+
{ name: "Slide 4:3", width: 1024, height: 768 },
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
key: "watch",
|
|
90
|
+
presets: [{ name: "Apple Watch 45mm", width: 198, height: 242 }],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
key: "paper",
|
|
94
|
+
presets: [
|
|
95
|
+
{ name: "A4", width: 595, height: 842 },
|
|
96
|
+
{ name: "A5", width: 420, height: 595 },
|
|
97
|
+
{ name: "Letter", width: 612, height: 792 },
|
|
98
|
+
{ name: "Tabloid", width: 792, height: 1224 },
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
key: "socialMedia",
|
|
103
|
+
presets: [
|
|
104
|
+
{ name: "Instagram Post", width: 1080, height: 1080 },
|
|
105
|
+
{ name: "Instagram Story", width: 1080, height: 1920 },
|
|
106
|
+
{ name: "X Post", width: 1200, height: 675 },
|
|
107
|
+
{ name: "Facebook Cover", width: 820, height: 312 },
|
|
108
|
+
{ name: "LinkedIn Cover", width: 1584, height: 396 },
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
/** Flat list of every preset across all categories — used for lookups/tests. */
|
|
114
|
+
export function allFrameSizePresets(): FrameSizePreset[] {
|
|
115
|
+
return FRAME_SIZE_PRESET_CATEGORIES.flatMap((category) => category.presets);
|
|
116
|
+
}
|
|
@@ -7,6 +7,10 @@ export {
|
|
|
7
7
|
type AlignmentVertical,
|
|
8
8
|
type DistributionAxis,
|
|
9
9
|
} from "./AlignmentMatrix";
|
|
10
|
+
export {
|
|
11
|
+
BreakpointOverrideIndicator,
|
|
12
|
+
type BreakpointOverrideIndicatorProps,
|
|
13
|
+
} from "./BreakpointOverrideIndicator";
|
|
10
14
|
export {
|
|
11
15
|
AutoLayoutMatrix,
|
|
12
16
|
type AutoLayoutDirection,
|
|
@@ -38,8 +42,10 @@ export {
|
|
|
38
42
|
type ExportSettingsValue,
|
|
39
43
|
} from "./ExportSettingsPanel";
|
|
40
44
|
export {
|
|
45
|
+
beginEyedropperPick,
|
|
41
46
|
DesignColorPicker,
|
|
42
47
|
endPointerGesture,
|
|
48
|
+
hasEyeDropperSupport,
|
|
43
49
|
POINTER_GESTURE_IDLE,
|
|
44
50
|
startPointerGesture,
|
|
45
51
|
type DesignColorMode,
|
|
@@ -54,6 +60,13 @@ export {
|
|
|
54
60
|
type DesignPaintType,
|
|
55
61
|
type PointerGestureState,
|
|
56
62
|
} from "./DesignColorPicker";
|
|
63
|
+
export {
|
|
64
|
+
allFrameSizePresets,
|
|
65
|
+
FRAME_SIZE_PRESET_CATEGORIES,
|
|
66
|
+
type FrameSizePreset,
|
|
67
|
+
type FrameSizePresetCategory,
|
|
68
|
+
type FrameSizePresetCategoryKey,
|
|
69
|
+
} from "./frame-size-presets";
|
|
57
70
|
export {
|
|
58
71
|
GradientEditor,
|
|
59
72
|
defaultGradient,
|
|
@@ -73,6 +86,19 @@ export {
|
|
|
73
86
|
type ImageFillValue,
|
|
74
87
|
type ImageFitMode,
|
|
75
88
|
} from "./ImageFillControls";
|
|
89
|
+
export {
|
|
90
|
+
InteractionStateOverrideIndicator,
|
|
91
|
+
InteractionStatePanel,
|
|
92
|
+
type ActiveInteractionState,
|
|
93
|
+
type InteractionState,
|
|
94
|
+
type InteractionStatePanelProps,
|
|
95
|
+
} from "./InteractionStatePanel";
|
|
96
|
+
export {
|
|
97
|
+
MotionKeyframeDiamond,
|
|
98
|
+
motionPropertyHasKeyframe,
|
|
99
|
+
type MotionKeyframeCssProperty,
|
|
100
|
+
type MotionKeyframeDiamondProps,
|
|
101
|
+
} from "./MotionKeyframeDiamond";
|
|
76
102
|
export {
|
|
77
103
|
ShaderFillsPanel,
|
|
78
104
|
descriptorFromPreset,
|
|
@@ -114,6 +114,35 @@ export function normalizeScrubNumber(
|
|
|
114
114
|
return Object.is(next, -0) ? 0 : next;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Whether a field's unit should snap to whole numbers while the user is
|
|
119
|
+
* actively pointer-dragging (scrubbing) it. Px-type fields (padding, gap,
|
|
120
|
+
* position, size, radius, etc.) read as integers in Figma-style editors even
|
|
121
|
+
* though the underlying `precision` option (which also governs *typed* input
|
|
122
|
+
* and keyboard nudges) allows one decimal place so a typed "12.5" still
|
|
123
|
+
* commits legally. Scoped to "px" specifically — unitless fields like
|
|
124
|
+
* line-height (precision-based, fractional by design) and other units (deg,
|
|
125
|
+
* %) are untouched.
|
|
126
|
+
*/
|
|
127
|
+
export function scrubSnapsToInteger(unit: string | undefined): boolean {
|
|
128
|
+
return unit === "px";
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Rounds a live scrub-drag value to a whole number when the field's unit
|
|
133
|
+
* calls for integer-only scrubbing (see `scrubSnapsToInteger`), applied
|
|
134
|
+
* *before* `normalizeScrubNumber`'s own min/max/precision clamp so a
|
|
135
|
+
* following precision clamp (if any) can't reintroduce a fraction. Only the
|
|
136
|
+
* pointer-drag scrub gesture should call this — typed input and keyboard
|
|
137
|
+
* nudges keep their existing `precision`-based rounding untouched.
|
|
138
|
+
*/
|
|
139
|
+
export function roundScrubDragValue(
|
|
140
|
+
value: number,
|
|
141
|
+
unit: string | undefined,
|
|
142
|
+
): number {
|
|
143
|
+
return scrubSnapsToInteger(unit) ? Math.round(value) : value;
|
|
144
|
+
}
|
|
145
|
+
|
|
117
146
|
export function formatScrubValue(
|
|
118
147
|
value: number,
|
|
119
148
|
options: Pick<ScrubExpressionOptions, "precision" | "unit"> = {},
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DistanceGuideBand,
|
|
3
|
+
EqualGapGuide,
|
|
4
|
+
FrameBounds,
|
|
5
|
+
} from "@shared/canvas-math";
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
AlignmentGuide,
|
|
9
|
+
AltHoverMeasurement,
|
|
10
|
+
AltHoverMeasurementLine,
|
|
11
|
+
} from "./types";
|
|
12
|
+
|
|
13
|
+
// Snap/guide recompute runs every rAF-coalesced mousemove during a drag and
|
|
14
|
+
// always returns a freshly-allocated array, so referential-equality bail in
|
|
15
|
+
// setState never fires even when the guides are unchanged frame-to-frame
|
|
16
|
+
// (e.g. holding steady mid-drag, or dragging along an axis with no new
|
|
17
|
+
// alignment). Shallow field-compare avoids the wasted re-render (PF15).
|
|
18
|
+
export function alignmentGuidesEqual(a: AlignmentGuide[], b: AlignmentGuide[]) {
|
|
19
|
+
if (a === b) return true;
|
|
20
|
+
if (a.length !== b.length) return false;
|
|
21
|
+
for (let i = 0; i < a.length; i++) {
|
|
22
|
+
const x = a[i];
|
|
23
|
+
const y = b[i];
|
|
24
|
+
if (
|
|
25
|
+
x.orientation !== y.orientation ||
|
|
26
|
+
x.position !== y.position ||
|
|
27
|
+
x.start !== y.start ||
|
|
28
|
+
x.end !== y.end
|
|
29
|
+
) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function distanceGuideBandEqual(
|
|
37
|
+
a: DistanceGuideBand,
|
|
38
|
+
b: DistanceGuideBand,
|
|
39
|
+
): boolean {
|
|
40
|
+
return (
|
|
41
|
+
a.gapStart === b.gapStart &&
|
|
42
|
+
a.gapEnd === b.gapEnd &&
|
|
43
|
+
a.crossStart === b.crossStart &&
|
|
44
|
+
a.crossEnd === b.crossEnd
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function equalGapGuidesEqual(a: EqualGapGuide[], b: EqualGapGuide[]) {
|
|
49
|
+
if (a === b) return true;
|
|
50
|
+
if (a.length !== b.length) return false;
|
|
51
|
+
for (let i = 0; i < a.length; i++) {
|
|
52
|
+
const x = a[i];
|
|
53
|
+
const y = b[i];
|
|
54
|
+
if (
|
|
55
|
+
x.orientation !== y.orientation ||
|
|
56
|
+
x.gap !== y.gap ||
|
|
57
|
+
!distanceGuideBandEqual(x.bands[0], y.bands[0]) ||
|
|
58
|
+
!distanceGuideBandEqual(x.bands[1], y.bands[1])
|
|
59
|
+
) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function altHoverMeasurementLineEqual(
|
|
67
|
+
a: AltHoverMeasurementLine | null,
|
|
68
|
+
b: AltHoverMeasurementLine | null,
|
|
69
|
+
): boolean {
|
|
70
|
+
if (a === b) return true;
|
|
71
|
+
if (!a || !b) return false;
|
|
72
|
+
return (
|
|
73
|
+
a.orientation === b.orientation &&
|
|
74
|
+
a.gap === b.gap &&
|
|
75
|
+
a.start === b.start &&
|
|
76
|
+
a.end === b.end &&
|
|
77
|
+
a.crossPosition === b.crossPosition &&
|
|
78
|
+
a.overlaps === b.overlaps
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function altHoverMeasurementEqual(
|
|
83
|
+
a: AltHoverMeasurement | null,
|
|
84
|
+
b: AltHoverMeasurement | null,
|
|
85
|
+
): boolean {
|
|
86
|
+
if (a === b) return true;
|
|
87
|
+
if (!a || !b) return false;
|
|
88
|
+
return (
|
|
89
|
+
altHoverMeasurementLineEqual(a.horizontal, b.horizontal) &&
|
|
90
|
+
altHoverMeasurementLineEqual(a.vertical, b.vertical)
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Computes the Figma-style alt-hover measurement lines between a selection's
|
|
95
|
+
* bounding box and a hovered object's bounding box: one horizontal gap (x
|
|
96
|
+
* axis) and one vertical gap (y axis), each with the px distance and the
|
|
97
|
+
* line's placement. Pure/testable — no DOM, no React state. Returns null for
|
|
98
|
+
* an axis where the two boxes overlap (nothing meaningful to measure there),
|
|
99
|
+
* matching Figma's behavior of only showing the line(s) that represent real
|
|
100
|
+
* empty space. */
|
|
101
|
+
export function computeAltHoverMeasurement(
|
|
102
|
+
selectionBounds: FrameBounds,
|
|
103
|
+
hoveredBounds: FrameBounds,
|
|
104
|
+
): AltHoverMeasurement {
|
|
105
|
+
const horizontalOverlap =
|
|
106
|
+
selectionBounds.top < hoveredBounds.bottom &&
|
|
107
|
+
selectionBounds.bottom > hoveredBounds.top;
|
|
108
|
+
const verticalOverlap =
|
|
109
|
+
selectionBounds.left < hoveredBounds.right &&
|
|
110
|
+
selectionBounds.right > hoveredBounds.left;
|
|
111
|
+
|
|
112
|
+
let horizontal: AltHoverMeasurementLine | null = null;
|
|
113
|
+
if (selectionBounds.right <= hoveredBounds.left) {
|
|
114
|
+
horizontal = {
|
|
115
|
+
orientation: "horizontal",
|
|
116
|
+
gap: hoveredBounds.left - selectionBounds.right,
|
|
117
|
+
start: selectionBounds.right,
|
|
118
|
+
end: hoveredBounds.left,
|
|
119
|
+
crossPosition: horizontalOverlap
|
|
120
|
+
? (Math.max(selectionBounds.top, hoveredBounds.top) +
|
|
121
|
+
Math.min(selectionBounds.bottom, hoveredBounds.bottom)) /
|
|
122
|
+
2
|
|
123
|
+
: (selectionBounds.centerY + hoveredBounds.centerY) / 2,
|
|
124
|
+
overlaps: false,
|
|
125
|
+
};
|
|
126
|
+
} else if (hoveredBounds.right <= selectionBounds.left) {
|
|
127
|
+
horizontal = {
|
|
128
|
+
orientation: "horizontal",
|
|
129
|
+
gap: selectionBounds.left - hoveredBounds.right,
|
|
130
|
+
start: hoveredBounds.right,
|
|
131
|
+
end: selectionBounds.left,
|
|
132
|
+
crossPosition: horizontalOverlap
|
|
133
|
+
? (Math.max(selectionBounds.top, hoveredBounds.top) +
|
|
134
|
+
Math.min(selectionBounds.bottom, hoveredBounds.bottom)) /
|
|
135
|
+
2
|
|
136
|
+
: (selectionBounds.centerY + hoveredBounds.centerY) / 2,
|
|
137
|
+
overlaps: false,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let vertical: AltHoverMeasurementLine | null = null;
|
|
142
|
+
if (selectionBounds.bottom <= hoveredBounds.top) {
|
|
143
|
+
vertical = {
|
|
144
|
+
orientation: "vertical",
|
|
145
|
+
gap: hoveredBounds.top - selectionBounds.bottom,
|
|
146
|
+
start: selectionBounds.bottom,
|
|
147
|
+
end: hoveredBounds.top,
|
|
148
|
+
crossPosition: verticalOverlap
|
|
149
|
+
? (Math.max(selectionBounds.left, hoveredBounds.left) +
|
|
150
|
+
Math.min(selectionBounds.right, hoveredBounds.right)) /
|
|
151
|
+
2
|
|
152
|
+
: (selectionBounds.centerX + hoveredBounds.centerX) / 2,
|
|
153
|
+
overlaps: false,
|
|
154
|
+
};
|
|
155
|
+
} else if (hoveredBounds.bottom <= selectionBounds.top) {
|
|
156
|
+
vertical = {
|
|
157
|
+
orientation: "vertical",
|
|
158
|
+
gap: selectionBounds.top - hoveredBounds.bottom,
|
|
159
|
+
start: hoveredBounds.bottom,
|
|
160
|
+
end: selectionBounds.top,
|
|
161
|
+
crossPosition: verticalOverlap
|
|
162
|
+
? (Math.max(selectionBounds.left, hoveredBounds.left) +
|
|
163
|
+
Math.min(selectionBounds.right, hoveredBounds.right)) /
|
|
164
|
+
2
|
|
165
|
+
: (selectionBounds.centerX + hoveredBounds.centerX) / 2,
|
|
166
|
+
overlaps: false,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return { horizontal, vertical };
|
|
171
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
export function hasBoardSurfaceContent(html: string | undefined) {
|
|
2
|
+
if (!html) return false;
|
|
3
|
+
const bodyMatch = html.match(/<body\b[^>]*>([\s\S]*?)<\/body>/i);
|
|
4
|
+
const content = bodyMatch?.[1] ?? html;
|
|
5
|
+
return content.replace(/<!--[\s\S]*?-->/g, "").trim().length > 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const BOARD_SURFACE_RENDER_STYLE = `<style data-agent-native-board-surface-render>html,body{background:transparent!important;background-color:transparent!important;background-image:none!important;}body{margin:0!important;position:relative;overflow:visible;}body>:not([data-agent-native-node-id]):not(style):not(script),body>[data-agent-native-node-id]:not([data-an-primitive]):not([data-agent-native-preserve-styles="true"]):has([data-agent-native-node-id]),body>[data-agent-native-node-id="body"],body>[data-agent-native-node-id="Body"],body>[data-agent-native-layer-name="body"],body>[data-agent-native-layer-name="Body"],body>[data-agent-native-layer-name="<body>"]{background:transparent!important;background-color:transparent!important;background-image:none!important;box-shadow:none!important;}[data-agent-native-board-backdrop-candidate="true"]{display:none!important;pointer-events:none!important;}</style>`;
|
|
9
|
+
export const BOARD_SURFACE_BACKGROUND = "hsl(0 0% 10%)";
|
|
10
|
+
|
|
11
|
+
const BOARD_SURFACE_BACKDROP_MIN_EDGE_PX = 2400;
|
|
12
|
+
const BOARD_SURFACE_BACKDROP_MIN_AREA_PX = 8_000_000;
|
|
13
|
+
const HTML_VOID_TAGS = new Set([
|
|
14
|
+
"area",
|
|
15
|
+
"base",
|
|
16
|
+
"br",
|
|
17
|
+
"col",
|
|
18
|
+
"embed",
|
|
19
|
+
"hr",
|
|
20
|
+
"img",
|
|
21
|
+
"input",
|
|
22
|
+
"link",
|
|
23
|
+
"meta",
|
|
24
|
+
"param",
|
|
25
|
+
"source",
|
|
26
|
+
"track",
|
|
27
|
+
"wbr",
|
|
28
|
+
]);
|
|
29
|
+
const HTML_TAG_RE = /<!--[\s\S]*?-->|<\/?([a-zA-Z][\w:-]*)([^<>]*?)\/?>/g;
|
|
30
|
+
|
|
31
|
+
function getHtmlAttributeValue(tag: string, name: string) {
|
|
32
|
+
const match = tag.match(
|
|
33
|
+
new RegExp(
|
|
34
|
+
`\\s${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s"'=<>]+))`,
|
|
35
|
+
"i",
|
|
36
|
+
),
|
|
37
|
+
);
|
|
38
|
+
return match?.[1] ?? match?.[2] ?? match?.[3] ?? "";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getCssDeclarationValue(style: string, name: string) {
|
|
42
|
+
const match = style.match(
|
|
43
|
+
new RegExp(`(?:^|;)\\s*${name}\\s*:\\s*([^;]+)`, "i"),
|
|
44
|
+
);
|
|
45
|
+
return match?.[1]?.trim() ?? "";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getCssPixelValue(style: string, name: string) {
|
|
49
|
+
const value = getCssDeclarationValue(style, name);
|
|
50
|
+
const match = value.match(/^(-?\d+(?:\.\d+)?)px$/i);
|
|
51
|
+
if (!match?.[1]) return null;
|
|
52
|
+
const parsed = Number(match[1]);
|
|
53
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function parseCssColor(value: string) {
|
|
57
|
+
const trimmed = value.trim().toLowerCase();
|
|
58
|
+
if (!trimmed || trimmed === "transparent") return null;
|
|
59
|
+
const rgb = trimmed.match(
|
|
60
|
+
/^rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)(?:\s*,\s*(\d+(?:\.\d+)?))?\s*\)$/,
|
|
61
|
+
);
|
|
62
|
+
if (rgb?.[1] && rgb[2] && rgb[3]) {
|
|
63
|
+
const alpha = rgb[4] === undefined ? 1 : Number(rgb[4]);
|
|
64
|
+
if (alpha <= 0) return null;
|
|
65
|
+
return [Number(rgb[1]), Number(rgb[2]), Number(rgb[3])] as const;
|
|
66
|
+
}
|
|
67
|
+
const hex = trimmed.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
|
|
68
|
+
if (!hex?.[1]) return null;
|
|
69
|
+
const raw = hex[1];
|
|
70
|
+
const full =
|
|
71
|
+
raw.length === 3
|
|
72
|
+
? raw
|
|
73
|
+
.split("")
|
|
74
|
+
.map((part) => part + part)
|
|
75
|
+
.join("")
|
|
76
|
+
: raw;
|
|
77
|
+
return [
|
|
78
|
+
Number.parseInt(full.slice(0, 2), 16),
|
|
79
|
+
Number.parseInt(full.slice(2, 4), 16),
|
|
80
|
+
Number.parseInt(full.slice(4, 6), 16),
|
|
81
|
+
] as const;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function isNeutralBackdropColor(value: string) {
|
|
85
|
+
const color = parseCssColor(value);
|
|
86
|
+
if (!color) return false;
|
|
87
|
+
const max = Math.max(...color);
|
|
88
|
+
const min = Math.min(...color);
|
|
89
|
+
return min >= 180 && max - min <= 24;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function isAccidentalBoardBackdropTag(tag: string) {
|
|
93
|
+
if (
|
|
94
|
+
getHtmlAttributeValue(tag, "data-agent-native-board-backdrop-candidate")
|
|
95
|
+
) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const primitive = getHtmlAttributeValue(
|
|
99
|
+
tag,
|
|
100
|
+
"data-an-primitive",
|
|
101
|
+
).toLowerCase();
|
|
102
|
+
if (primitive !== "rectangle" && primitive !== "rect") return false;
|
|
103
|
+
const style = getHtmlAttributeValue(tag, "style");
|
|
104
|
+
if (!style) return false;
|
|
105
|
+
const width = getCssPixelValue(style, "width");
|
|
106
|
+
const height = getCssPixelValue(style, "height");
|
|
107
|
+
if (width === null || height === null) return false;
|
|
108
|
+
if (
|
|
109
|
+
width < BOARD_SURFACE_BACKDROP_MIN_EDGE_PX ||
|
|
110
|
+
height < BOARD_SURFACE_BACKDROP_MIN_EDGE_PX ||
|
|
111
|
+
width * height < BOARD_SURFACE_BACKDROP_MIN_AREA_PX
|
|
112
|
+
) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
const background =
|
|
116
|
+
getCssDeclarationValue(style, "background-color") ||
|
|
117
|
+
getCssDeclarationValue(style, "background");
|
|
118
|
+
return isNeutralBackdropColor(background);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function markAccidentalBoardBackdropCandidates(html: string) {
|
|
122
|
+
return html.replace(HTML_TAG_RE, (tag: string, tagName?: string) => {
|
|
123
|
+
if (!tagName || tag.startsWith("</")) return tag;
|
|
124
|
+
if (!isAccidentalBoardBackdropTag(tag)) return tag;
|
|
125
|
+
return tag.replace(
|
|
126
|
+
/\/?>$/,
|
|
127
|
+
(ending) => ` data-agent-native-board-backdrop-candidate="true"${ending}`,
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function findLastHtmlStackTagIndex(
|
|
133
|
+
stack: Array<{ tagName: string; nodeId: string }>,
|
|
134
|
+
tagName: string,
|
|
135
|
+
) {
|
|
136
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
137
|
+
if (stack[i]?.tagName === tagName) return i;
|
|
138
|
+
}
|
|
139
|
+
return -1;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function getCurrentLayerParentNodeId(
|
|
143
|
+
stack: Array<{ tagName: string; nodeId: string }>,
|
|
144
|
+
) {
|
|
145
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
146
|
+
const nodeId = stack[i]?.nodeId;
|
|
147
|
+
if (nodeId) return nodeId;
|
|
148
|
+
}
|
|
149
|
+
return "body";
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function getBoardSurfaceRenderContent(html: string) {
|
|
153
|
+
if (!html) return html;
|
|
154
|
+
const renderHtml = markAccidentalBoardBackdropCandidates(html);
|
|
155
|
+
if (renderHtml.includes("data-agent-native-board-surface-render")) {
|
|
156
|
+
return renderHtml;
|
|
157
|
+
}
|
|
158
|
+
if (/<\/head>/i.test(html)) {
|
|
159
|
+
return renderHtml.replace(
|
|
160
|
+
/<\/head>/i,
|
|
161
|
+
`${BOARD_SURFACE_RENDER_STYLE}</head>`,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
if (/<body\b/i.test(html)) {
|
|
165
|
+
return renderHtml.replace(/<body\b/i, `${BOARD_SURFACE_RENDER_STYLE}<body`);
|
|
166
|
+
}
|
|
167
|
+
return `${BOARD_SURFACE_RENDER_STYLE}${renderHtml}`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Simple djb2-xor string hash, used to build cheap cache keys elsewhere in
|
|
171
|
+
* the multi-screen canvas (board content signatures, primitive-parse cache
|
|
172
|
+
* keys, etc). */
|
|
173
|
+
export function hashString(s: string): string {
|
|
174
|
+
let h = 5381;
|
|
175
|
+
for (let i = 0; i < s.length; i++) {
|
|
176
|
+
// h = h * 33 ^ charCode (djb2 xor variant)
|
|
177
|
+
h = ((h << 5) + h) ^ s.charCodeAt(i);
|
|
178
|
+
h = h >>> 0; // keep as unsigned 32-bit
|
|
179
|
+
}
|
|
180
|
+
return h.toString(16);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function getBoardContentLayerSignature(html: string) {
|
|
184
|
+
const layers: string[] = [];
|
|
185
|
+
const stack: Array<{ tagName: string; nodeId: string }> = [];
|
|
186
|
+
const childCountsByParent = new Map<string, number>();
|
|
187
|
+
|
|
188
|
+
for (const match of html.matchAll(HTML_TAG_RE)) {
|
|
189
|
+
const token = match[0];
|
|
190
|
+
const tagName = match[1]?.toLowerCase();
|
|
191
|
+
if (!tagName) continue;
|
|
192
|
+
|
|
193
|
+
if (token.startsWith("</")) {
|
|
194
|
+
const index = findLastHtmlStackTagIndex(stack, tagName);
|
|
195
|
+
if (index >= 0) stack.splice(index);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const nodeId = getHtmlAttributeValue(token, "data-agent-native-node-id");
|
|
200
|
+
if (nodeId) {
|
|
201
|
+
const parentNodeId = getCurrentLayerParentNodeId(stack);
|
|
202
|
+
const childIndex = childCountsByParent.get(parentNodeId) ?? 0;
|
|
203
|
+
childCountsByParent.set(parentNodeId, childIndex + 1);
|
|
204
|
+
layers.push(`${nodeId}<${parentNodeId}#${childIndex}`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const selfClosing = token.endsWith("/>") || HTML_VOID_TAGS.has(tagName);
|
|
208
|
+
if (!selfClosing) stack.push({ tagName, nodeId });
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return `${layers.length}:${hashString(layers.join("\n"))}`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function getBoardContentKey(args: {
|
|
215
|
+
boardFileId: string;
|
|
216
|
+
boardFileContent: string;
|
|
217
|
+
boardIsActive: boolean;
|
|
218
|
+
}) {
|
|
219
|
+
return `${args.boardFileId}:surface`;
|
|
220
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// During a zoom gesture the constant-size selection chrome is frozen (we don't
|
|
2
|
+
// re-render); on commit it recomputes to its fixed screen size. These transitions
|
|
3
|
+
// are enabled only for that brief settle, so normal selection, resize, and
|
|
4
|
+
// screen-switch geometry stays pinned to the frame.
|
|
5
|
+
export const CHROME_SETTLE_MS = 150;
|
|
6
|
+
const CHROME_OPACITY_TRANSITION = "opacity 150ms ease-out";
|
|
7
|
+
const CHROME_BORDER_SETTLE_TRANSITION = `inset ${CHROME_SETTLE_MS}ms ease-out, border-width ${CHROME_SETTLE_MS}ms ease-out, border-radius ${CHROME_SETTLE_MS}ms ease-out, ${CHROME_OPACITY_TRANSITION}`;
|
|
8
|
+
const SELECTION_BOX_SETTLE_TRANSITION = `border-width ${CHROME_SETTLE_MS}ms ease-out, border-radius ${CHROME_SETTLE_MS}ms ease-out, ${CHROME_OPACITY_TRANSITION}`;
|
|
9
|
+
const CHROME_HANDLE_SETTLE_TRANSITION = `width ${CHROME_SETTLE_MS}ms ease-out, height ${CHROME_SETTLE_MS}ms ease-out, border-width ${CHROME_SETTLE_MS}ms ease-out, top ${CHROME_SETTLE_MS}ms ease-out, bottom ${CHROME_SETTLE_MS}ms ease-out, left ${CHROME_SETTLE_MS}ms ease-out, right ${CHROME_SETTLE_MS}ms ease-out, ${CHROME_OPACITY_TRANSITION}`;
|
|
10
|
+
// Frame header (name + "Full view" button) is counter-scaled via
|
|
11
|
+
// transform to stay a fixed screen size; ease that scale on zoom-settle. opacity
|
|
12
|
+
// is included so the button's hover-fade (transition-opacity) keeps working.
|
|
13
|
+
const CHROME_LABEL_SETTLE_TRANSITION = `transform ${CHROME_SETTLE_MS}ms ease-out, ${CHROME_OPACITY_TRANSITION}`;
|
|
14
|
+
|
|
15
|
+
export function getChromeBorderTransition(chromeSettling: boolean) {
|
|
16
|
+
return chromeSettling
|
|
17
|
+
? CHROME_BORDER_SETTLE_TRANSITION
|
|
18
|
+
: CHROME_OPACITY_TRANSITION;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getSelectionBoxTransition(chromeSettling: boolean) {
|
|
22
|
+
return chromeSettling ? SELECTION_BOX_SETTLE_TRANSITION : "none";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getChromeHandleTransition(chromeSettling: boolean) {
|
|
26
|
+
return chromeSettling
|
|
27
|
+
? CHROME_HANDLE_SETTLE_TRANSITION
|
|
28
|
+
: CHROME_OPACITY_TRANSITION;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getChromeLabelTransition(chromeSettling: boolean) {
|
|
32
|
+
return chromeSettling
|
|
33
|
+
? CHROME_LABEL_SETTLE_TRANSITION
|
|
34
|
+
: CHROME_OPACITY_TRANSITION;
|
|
35
|
+
}
|