@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,134 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
|
|
3
|
+
import { SURFACE_PADDING } from "../MultiScreenCanvas";
|
|
4
|
+
import type { PortableStyleSnapshot } from "../types";
|
|
5
|
+
import type {
|
|
6
|
+
CrossScreenDropAxis,
|
|
7
|
+
CrossScreenDropGuide,
|
|
8
|
+
CrossScreenDropMode,
|
|
9
|
+
CrossScreenDropPlacement,
|
|
10
|
+
CrossScreenHitTestAnchorRect,
|
|
11
|
+
CrossScreenHitTestResult,
|
|
12
|
+
FrameGeometry,
|
|
13
|
+
Point,
|
|
14
|
+
} from "./types";
|
|
15
|
+
|
|
16
|
+
export function isFinitePoint(value: unknown): value is Point {
|
|
17
|
+
if (!value || typeof value !== "object") return false;
|
|
18
|
+
const point = value as Record<string, unknown>;
|
|
19
|
+
return Number.isFinite(point.x) && Number.isFinite(point.y);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function isPortableStyleSnapshot(
|
|
23
|
+
value: unknown,
|
|
24
|
+
): value is PortableStyleSnapshot {
|
|
25
|
+
if (!value || typeof value !== "object") return false;
|
|
26
|
+
const snapshot = value as Record<string, unknown>;
|
|
27
|
+
return snapshot.version === 1 && Array.isArray(snapshot.nodes);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function isCrossScreenDropPlacement(
|
|
31
|
+
value: unknown,
|
|
32
|
+
): value is CrossScreenDropPlacement {
|
|
33
|
+
return value === "before" || value === "after" || value === "inside";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function isCrossScreenDropAxis(
|
|
37
|
+
value: unknown,
|
|
38
|
+
): value is CrossScreenDropAxis {
|
|
39
|
+
return value === "x" || value === "y";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function isCrossScreenDropMode(
|
|
43
|
+
value: unknown,
|
|
44
|
+
): value is CrossScreenDropMode {
|
|
45
|
+
return value === "flow-insert" || value === "absolute-container";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function isCrossScreenHitTestAnchorRect(
|
|
49
|
+
value: unknown,
|
|
50
|
+
): value is CrossScreenHitTestAnchorRect {
|
|
51
|
+
if (!value || typeof value !== "object") return false;
|
|
52
|
+
const rect = value as Record<string, unknown>;
|
|
53
|
+
return (
|
|
54
|
+
Number.isFinite(rect.left) &&
|
|
55
|
+
Number.isFinite(rect.top) &&
|
|
56
|
+
Number.isFinite(rect.width) &&
|
|
57
|
+
Number.isFinite(rect.height)
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function getCrossScreenDropGuideForHitTest(args: {
|
|
62
|
+
hit: CrossScreenHitTestResult;
|
|
63
|
+
targetGeometry: FrameGeometry;
|
|
64
|
+
targetMetadata: { width: number; height: number };
|
|
65
|
+
}): CrossScreenDropGuide | null {
|
|
66
|
+
const rect = args.hit.anchorRect;
|
|
67
|
+
if (!rect) return null;
|
|
68
|
+
const placement = args.hit.placement ?? "inside";
|
|
69
|
+
const axis = args.hit.axis ?? "y";
|
|
70
|
+
const scaleX =
|
|
71
|
+
args.targetGeometry.width / Math.max(1, args.targetMetadata.width);
|
|
72
|
+
const scaleY =
|
|
73
|
+
args.targetGeometry.height / Math.max(1, args.targetMetadata.height);
|
|
74
|
+
return {
|
|
75
|
+
placement,
|
|
76
|
+
axis,
|
|
77
|
+
boardRect: {
|
|
78
|
+
x: args.targetGeometry.x + rect.left * scaleX,
|
|
79
|
+
y: args.targetGeometry.y + rect.top * scaleY,
|
|
80
|
+
width: Math.max(1, rect.width * scaleX),
|
|
81
|
+
height: Math.max(1, rect.height * scaleY),
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function getCrossScreenDropGuideStyle(args: {
|
|
87
|
+
guide: CrossScreenDropGuide;
|
|
88
|
+
pan: Point;
|
|
89
|
+
scale: number;
|
|
90
|
+
}): CSSProperties {
|
|
91
|
+
const { boardRect, placement, axis } = args.guide;
|
|
92
|
+
const left = args.pan.x + (SURFACE_PADDING + boardRect.x) * args.scale;
|
|
93
|
+
const top = args.pan.y + (SURFACE_PADDING + boardRect.y) * args.scale;
|
|
94
|
+
const width = Math.max(1, boardRect.width * args.scale);
|
|
95
|
+
const height = Math.max(1, boardRect.height * args.scale);
|
|
96
|
+
|
|
97
|
+
if (placement === "inside") {
|
|
98
|
+
return {
|
|
99
|
+
left,
|
|
100
|
+
top,
|
|
101
|
+
width,
|
|
102
|
+
height,
|
|
103
|
+
border: "2px solid var(--design-editor-accent-color)",
|
|
104
|
+
background:
|
|
105
|
+
"color-mix(in srgb, var(--design-editor-accent-color) 14%, transparent)",
|
|
106
|
+
borderRadius: 2,
|
|
107
|
+
boxShadow: "none",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (axis === "x") {
|
|
112
|
+
const x = placement === "before" ? left : left + width;
|
|
113
|
+
return {
|
|
114
|
+
left: x - 1,
|
|
115
|
+
top,
|
|
116
|
+
width: 2,
|
|
117
|
+
height: Math.max(8, height),
|
|
118
|
+
background: "var(--design-editor-accent-color)",
|
|
119
|
+
borderRadius: 999,
|
|
120
|
+
boxShadow: "0 0 0 1px var(--design-editor-accent-color)",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const y = placement === "before" ? top : top + height;
|
|
125
|
+
return {
|
|
126
|
+
left,
|
|
127
|
+
top: y - 1,
|
|
128
|
+
width: Math.max(8, width),
|
|
129
|
+
height: 2,
|
|
130
|
+
background: "var(--design-editor-accent-color)",
|
|
131
|
+
borderRadius: 999,
|
|
132
|
+
boxShadow: "0 0 0 1px var(--design-editor-accent-color)",
|
|
133
|
+
};
|
|
134
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { getRotatedFrameAABB } from "@shared/canvas-math";
|
|
2
|
+
|
|
3
|
+
import { SURFACE_PADDING } from "../MultiScreenCanvas";
|
|
4
|
+
import type { FrameGeometry, Point } from "./types";
|
|
5
|
+
|
|
6
|
+
// ── Overview viewport culling (PF22) ────────────────────────────────────────
|
|
7
|
+
//
|
|
8
|
+
// Boards with 100+ screens used to render every screen as a full live iframe
|
|
9
|
+
// regardless of whether it was anywhere near the visible viewport. This is a
|
|
10
|
+
// deliberately conservative culling scheme:
|
|
11
|
+
//
|
|
12
|
+
// - Visibility is computed from the *committed* pan/zoom React state (`pan`,
|
|
13
|
+
// `canvasZoom`), never from the imperative per-gesture-frame transform
|
|
14
|
+
// (zoomRef/panRef, mutated by applyViewToDom every wheel/pinch tick — see
|
|
15
|
+
// its comment). Recomputing this per gesture frame would mean re-rendering
|
|
16
|
+
// React during a gesture, exactly what applyViewToDom/scheduleViewCommit
|
|
17
|
+
// were built to avoid.
|
|
18
|
+
// - A generous overscan margin keeps screens "live" well before they
|
|
19
|
+
// physically enter the viewport, so a settled-but-about-to-pan-into-view
|
|
20
|
+
// screen (the debounced commit lags real cursor position by up to
|
|
21
|
+
// ~120ms — see scheduleViewCommit) is already mounted by the time it's
|
|
22
|
+
// reachable.
|
|
23
|
+
// - Two tiers, see computeScreenCullTier: a screen that has never intersected
|
|
24
|
+
// the overscanned viewport this session renders a lightweight placeholder;
|
|
25
|
+
// once visible it mounts real content and never goes back to a placeholder
|
|
26
|
+
// — it only ever gets hidden (Tier B), never unmounted, since iframes lose
|
|
27
|
+
// all internal state (scroll position, form input, Alpine/JS state) on
|
|
28
|
+
// unmount.
|
|
29
|
+
|
|
30
|
+
/** Escape hatch: flip to `false` to fully disable culling in one line if a
|
|
31
|
+
* regression appears — every screen goes back to always rendering full
|
|
32
|
+
* content, matching pre-culling behavior exactly. */
|
|
33
|
+
export const OVERVIEW_CULLING_ENABLED = true;
|
|
34
|
+
|
|
35
|
+
/** How many viewport widths/heights of margin to add around the visible
|
|
36
|
+
* surface, in each direction, before a screen counts as "culled". Generous
|
|
37
|
+
* on purpose: the mission calls for >=1.5x, so screens scrolling into view
|
|
38
|
+
* during an in-flight gesture are already live before the debounced
|
|
39
|
+
* ~120ms view-commit (see scheduleViewCommit) catches up and this
|
|
40
|
+
* recomputes. */
|
|
41
|
+
export const OVERVIEW_CULLING_OVERSCAN_FACTOR = 1.5;
|
|
42
|
+
|
|
43
|
+
export type ScreenCullTier =
|
|
44
|
+
/** Full content (iframe/DesignCanvas) is mounted and rendered normally. */
|
|
45
|
+
| "visible"
|
|
46
|
+
/** Has been visible before this session; content stays mounted (iframes
|
|
47
|
+
* never unmount — see the module doc above) but is skipped from paint via
|
|
48
|
+
* visibility/content-visibility, not display:none or will-change. */
|
|
49
|
+
| "culled"
|
|
50
|
+
/** Has never been visible this session; renders a lightweight placeholder
|
|
51
|
+
* with no iframe/content node at all. */
|
|
52
|
+
| "placeholder";
|
|
53
|
+
|
|
54
|
+
/** The world-space (canvas-space) rectangle currently visible inside the
|
|
55
|
+
* pannable surface, expanded by `overscanFactor` viewport-widths/heights in
|
|
56
|
+
* every direction. Built from the *committed* pan/zoom state, matching the
|
|
57
|
+
* same `translate(pan) scale(zoom/100)` transform applyViewToDom applies to
|
|
58
|
+
* the world layer — see getOverscannedViewportCanvasBounds's callers. */
|
|
59
|
+
export interface OverscannedViewportBounds {
|
|
60
|
+
left: number;
|
|
61
|
+
top: number;
|
|
62
|
+
right: number;
|
|
63
|
+
bottom: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Computes the overscanned world-space viewport rect for culling purposes.
|
|
67
|
+
* `surfaceSize` is the pannable surface's own on-screen size (the
|
|
68
|
+
* `surfaceRef` element's content box, in screen px); `pan`/`zoomPercent` are
|
|
69
|
+
* the committed (not per-gesture-frame) pan/zoom values. Screens are placed
|
|
70
|
+
* in world space with `SURFACE_PADDING` added to their raw x/y (see Screen's
|
|
71
|
+
* wrapper style), so this returns bounds already in that same
|
|
72
|
+
* `SURFACE_PADDING`-relative space — compare directly against
|
|
73
|
+
* `geometry.x`/`geometry.y`-based frame bounds, no further offset needed.
|
|
74
|
+
* Returns `null` when the surface has no measured size yet (e.g. before the
|
|
75
|
+
* first layout pass) — callers should treat that as "cannot determine
|
|
76
|
+
* visibility yet" and fall back to treating everything as visible. */
|
|
77
|
+
export function getOverscannedViewportCanvasBounds(
|
|
78
|
+
surfaceSize: { width: number; height: number },
|
|
79
|
+
pan: Point,
|
|
80
|
+
zoomPercent: number,
|
|
81
|
+
overscanFactor: number = OVERVIEW_CULLING_OVERSCAN_FACTOR,
|
|
82
|
+
): OverscannedViewportBounds | null {
|
|
83
|
+
if (surfaceSize.width <= 0 || surfaceSize.height <= 0) return null;
|
|
84
|
+
const scale = zoomPercent / 100;
|
|
85
|
+
if (!(scale > 0)) return null;
|
|
86
|
+
// Visible world-space rect: screen-space [0, surfaceSize] maps back through
|
|
87
|
+
// the world transform (`screenPoint = pan + worldPoint * scale`) to
|
|
88
|
+
// `worldPoint = (screenPoint - pan) / scale`. This mirrors
|
|
89
|
+
// screenToCanvasPoint's own inverse-transform math but is kept local here
|
|
90
|
+
// (rather than imported) since it operates on the *committed* pan/zoom
|
|
91
|
+
// React state specifically, not the live gesture pan/zoom.
|
|
92
|
+
const visibleLeft = -pan.x / scale;
|
|
93
|
+
const visibleTop = -pan.y / scale;
|
|
94
|
+
const visibleWidth = surfaceSize.width / scale;
|
|
95
|
+
const visibleHeight = surfaceSize.height / scale;
|
|
96
|
+
const overscanX = visibleWidth * overscanFactor;
|
|
97
|
+
const overscanY = visibleHeight * overscanFactor;
|
|
98
|
+
return {
|
|
99
|
+
left: visibleLeft - overscanX - SURFACE_PADDING,
|
|
100
|
+
top: visibleTop - overscanY - SURFACE_PADDING,
|
|
101
|
+
right: visibleLeft + visibleWidth + overscanX - SURFACE_PADDING,
|
|
102
|
+
bottom: visibleTop + visibleHeight + overscanY - SURFACE_PADDING,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** True when `geometry`'s (rotation-aware) bounds intersect the overscanned
|
|
107
|
+
* viewport rect at all — i.e. the screen is not fully outside it. Uses
|
|
108
|
+
* `getRotatedFrameAABB` so a rotated frame's actual on-screen footprint is
|
|
109
|
+
* tested, not its unrotated local rect. */
|
|
110
|
+
export function isFrameWithinOverscannedViewport(
|
|
111
|
+
geometry: FrameGeometry,
|
|
112
|
+
viewport: OverscannedViewportBounds,
|
|
113
|
+
): boolean {
|
|
114
|
+
const bounds = getRotatedFrameAABB(geometry);
|
|
115
|
+
return (
|
|
116
|
+
bounds.right >= viewport.left &&
|
|
117
|
+
bounds.left <= viewport.right &&
|
|
118
|
+
bounds.bottom >= viewport.top &&
|
|
119
|
+
bounds.top <= viewport.bottom
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Item 4 — frame-tool/preset new-screen placement guard. A degenerate camera
|
|
125
|
+
* (corrupted/extreme pan+zoom — see item 5's camera-restore fix) makes
|
|
126
|
+
* getCanvasPoint's screen-to-world conversion blow up (dividing by a near-
|
|
127
|
+
* zero zoom scale), so a click-to-place or drag-to-draw frame gesture can
|
|
128
|
+
* compute world coordinates in the tens of thousands (observed: ±65536-ish)
|
|
129
|
+
* instead of landing near what the user actually clicked. Clamps the
|
|
130
|
+
* proposed geometry's origin to sit within `viewport` (the current
|
|
131
|
+
* OverscannedViewportBounds with overscanFactor 0, i.e. the exact visible
|
|
132
|
+
* world-rect) — centering it there when the proposed origin falls outside —
|
|
133
|
+
* so a bad camera can never fling a new screen to infinity. Only the origin
|
|
134
|
+
* is clamped (not width/height): the frame tool's own min/default size rules
|
|
135
|
+
* already bound those, and centering a same-sized frame preserves the
|
|
136
|
+
* gesture's intended dimensions.
|
|
137
|
+
*/
|
|
138
|
+
export function clampFrameGeometryToViewport(
|
|
139
|
+
geometry: FrameGeometry,
|
|
140
|
+
viewport: OverscannedViewportBounds | null,
|
|
141
|
+
): FrameGeometry {
|
|
142
|
+
if (!viewport) return geometry;
|
|
143
|
+
const viewportWidth = viewport.right - viewport.left;
|
|
144
|
+
const viewportHeight = viewport.bottom - viewport.top;
|
|
145
|
+
if (!(viewportWidth > 0) || !(viewportHeight > 0)) return geometry;
|
|
146
|
+
const isWithin = isFrameWithinOverscannedViewport(geometry, viewport);
|
|
147
|
+
if (isWithin) return geometry;
|
|
148
|
+
return {
|
|
149
|
+
...geometry,
|
|
150
|
+
x: viewport.left + (viewportWidth - geometry.width) / 2,
|
|
151
|
+
y: viewport.top + (viewportHeight - geometry.height) / 2,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Resolves the culling tier for a single screen. `alwaysVisible` covers the
|
|
156
|
+
* mission's "always treated as visible" overrides: the active/board screen
|
|
157
|
+
* and anything in the current selection, regardless of position — Figma
|
|
158
|
+
* itself never culls the object you're actively editing or have selected,
|
|
159
|
+
* and keeping these paths iframe-backed avoids any risk of interrupting
|
|
160
|
+
* in-progress edits/bridge state on the screen the user is looking at.
|
|
161
|
+
* `viewport` is `null` when surface size isn't known yet (initial layout) —
|
|
162
|
+
* treated as visible so nothing is incorrectly culled before we can measure.
|
|
163
|
+
* `hasBeenVisible` should reflect whether this screen id has *ever* been
|
|
164
|
+
* visible this session (see the hasBeenVisibleRef Set in MultiScreenCanvas):
|
|
165
|
+
* once true, a screen can only be "visible" or "culled", never regress to
|
|
166
|
+
* "placeholder". */
|
|
167
|
+
export function computeScreenCullTier({
|
|
168
|
+
geometry,
|
|
169
|
+
viewport,
|
|
170
|
+
alwaysVisible,
|
|
171
|
+
hasBeenVisible,
|
|
172
|
+
}: {
|
|
173
|
+
geometry: FrameGeometry;
|
|
174
|
+
viewport: OverscannedViewportBounds | null;
|
|
175
|
+
alwaysVisible: boolean;
|
|
176
|
+
hasBeenVisible: boolean;
|
|
177
|
+
}): ScreenCullTier {
|
|
178
|
+
if (!OVERVIEW_CULLING_ENABLED) return "visible";
|
|
179
|
+
const isWithinViewport =
|
|
180
|
+
alwaysVisible ||
|
|
181
|
+
!viewport ||
|
|
182
|
+
isFrameWithinOverscannedViewport(geometry, viewport);
|
|
183
|
+
if (isWithinViewport) return "visible";
|
|
184
|
+
return hasBeenVisible ? "culled" : "placeholder";
|
|
185
|
+
}
|
package/corpus/templates/design/app/components/design/multi-screen/gradient-overlay-geometry.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { GradientLinePoint, Point } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The CSS `linear-gradient()` line-length formula (CSS Images ¤3
|
|
5
|
+
* §linear-gradient-syntax): for a box of `width` x `height` and an angle
|
|
6
|
+
* measured clockwise from "up" (matching both this app's `GradientValue`
|
|
7
|
+
* angle convention and the CSS `<angle>` syntax `linear-gradient(Ndeg, ...)`
|
|
8
|
+
* already produces), the gradient line's half-length from the box center is
|
|
9
|
+
* `|width/2 * sin(angle)| + |height/2 * cos(angle)|` — the projection of the
|
|
10
|
+
* box's half-diagonal onto the gradient axis, so the line exactly spans the
|
|
11
|
+
* box from edge to edge (touching whichever corner is furthest along the
|
|
12
|
+
* axis) the same way a browser renders it.
|
|
13
|
+
*/
|
|
14
|
+
export function gradientLineEndpoints(
|
|
15
|
+
angleDeg: number,
|
|
16
|
+
width: number,
|
|
17
|
+
height: number,
|
|
18
|
+
): { start: Point; end: Point } {
|
|
19
|
+
const rad = (angleDeg * Math.PI) / 180;
|
|
20
|
+
// Unit vector pointing from the gradient's start toward its end (0deg = up
|
|
21
|
+
// = -y, clockwise), matching AngleDial's "0 is north, clockwise" mapping.
|
|
22
|
+
const dx = Math.sin(rad);
|
|
23
|
+
const dy = -Math.cos(rad);
|
|
24
|
+
const halfLength = Math.abs((width / 2) * dx) + Math.abs((height / 2) * dy);
|
|
25
|
+
const center = { x: width / 2, y: height / 2 };
|
|
26
|
+
return {
|
|
27
|
+
start: { x: center.x - dx * halfLength, y: center.y - dy * halfLength },
|
|
28
|
+
end: { x: center.x + dx * halfLength, y: center.y + dy * halfLength },
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Projects each stop's 0–100 `position` onto the gradient line's start/end
|
|
33
|
+
* points (linear interpolation), for rendering a round marker per stop. */
|
|
34
|
+
export function gradientStopPoints(
|
|
35
|
+
angleDeg: number,
|
|
36
|
+
width: number,
|
|
37
|
+
height: number,
|
|
38
|
+
stops: ReadonlyArray<{ position: number }>,
|
|
39
|
+
): GradientLinePoint[] {
|
|
40
|
+
const { start, end } = gradientLineEndpoints(angleDeg, width, height);
|
|
41
|
+
return stops.map((stop) => {
|
|
42
|
+
const t = clampGradientT(stop.position / 100);
|
|
43
|
+
return {
|
|
44
|
+
x: start.x + (end.x - start.x) * t,
|
|
45
|
+
y: start.y + (end.y - start.y) * t,
|
|
46
|
+
position: stop.position,
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function clampGradientT(t: number): number {
|
|
52
|
+
if (!Number.isFinite(t)) return 0;
|
|
53
|
+
return Math.max(0, Math.min(1, t));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Inverts a dragged endpoint handle back into an angle: given the box center
|
|
58
|
+
* and the new local point for whichever endpoint is being dragged (`which`),
|
|
59
|
+
* returns the angle (0-360, "up"-is-0 clockwise) whose gradient line passes
|
|
60
|
+
* through that point. Dragging the "start" handle points the axis the
|
|
61
|
+
* opposite way (the start is the *far* end of the line from that point's
|
|
62
|
+
* direction), so it adds 180° after resolving the raw pointer angle.
|
|
63
|
+
*/
|
|
64
|
+
export function angleFromDraggedEndpoint(
|
|
65
|
+
point: Point,
|
|
66
|
+
width: number,
|
|
67
|
+
height: number,
|
|
68
|
+
which: "start" | "end",
|
|
69
|
+
): number {
|
|
70
|
+
const center = { x: width / 2, y: height / 2 };
|
|
71
|
+
const dx = point.x - center.x;
|
|
72
|
+
const dy = point.y - center.y;
|
|
73
|
+
if (dx === 0 && dy === 0) return 0;
|
|
74
|
+
// atan2 gives angle from east (+x), clockwise-positive in screen space
|
|
75
|
+
// (y-down); convert to this app's "0 is north, clockwise" convention by
|
|
76
|
+
// rotating the reference axis by +90deg, matching AngleDial's own mapping.
|
|
77
|
+
let deg = (Math.atan2(dy, dx) * 180) / Math.PI + 90;
|
|
78
|
+
if (which === "start") deg += 180;
|
|
79
|
+
deg = ((deg % 360) + 360) % 360;
|
|
80
|
+
return deg;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Inverts a dragged stop marker back into a 0–100 position: projects the
|
|
85
|
+
* dragged local point onto the (unbounded) gradient line through the box
|
|
86
|
+
* center at `angleDeg`, using the *current* line's start/end as the 0/100
|
|
87
|
+
* reference frame, then clamps to [0, 100]. This mirrors
|
|
88
|
+
* `GradientEditor`'s own `positionFromPointer`, which likewise projects the
|
|
89
|
+
* pointer onto the ramp bar's axis and clamps rather than rejecting
|
|
90
|
+
* off-axis drags — on canvas the equivalent "axis" is the gradient line
|
|
91
|
+
* itself, so a drag that strays perpendicular to the line still tracks the
|
|
92
|
+
* nearest point on it instead of doing nothing.
|
|
93
|
+
*/
|
|
94
|
+
export function stopPercentFromDraggedPoint(
|
|
95
|
+
point: Point,
|
|
96
|
+
angleDeg: number,
|
|
97
|
+
width: number,
|
|
98
|
+
height: number,
|
|
99
|
+
): number {
|
|
100
|
+
const { start, end } = gradientLineEndpoints(angleDeg, width, height);
|
|
101
|
+
const lineDx = end.x - start.x;
|
|
102
|
+
const lineDy = end.y - start.y;
|
|
103
|
+
const lengthSquared = lineDx * lineDx + lineDy * lineDy;
|
|
104
|
+
if (lengthSquared === 0) return 0;
|
|
105
|
+
const t =
|
|
106
|
+
((point.x - start.x) * lineDx + (point.y - start.y) * lineDy) /
|
|
107
|
+
lengthSquared;
|
|
108
|
+
return clampGradientT(t) * 100;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Converts a constant on-screen pixel radius (e.g. a hit-test tolerance
|
|
112
|
+
* that should feel the same size regardless of zoom) into canvas px, the
|
|
113
|
+
* space hitTestPenAnchor/hitTestPenHandle and PenPath geometry operate in.
|
|
114
|
+
* `zoom` is the same 0-based-at-100 percentage used throughout this file
|
|
115
|
+
* (100 = 1:1). Mirrors the `screenPx / (zoom / 100)` conversion already
|
|
116
|
+
* used by the pen tool's close-hit-target radius. */
|
|
117
|
+
export function screenPxToCanvasPx(screenPx: number, zoom: number): number {
|
|
118
|
+
const scale = zoom / 100;
|
|
119
|
+
return scale > 0 ? screenPx / scale : screenPx;
|
|
120
|
+
}
|