@agent-native/core 0.92.9 → 0.92.11

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.
Files changed (345) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +29 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/production-agent.ts +181 -15
  5. package/corpus/core/src/agent/run-manager.ts +31 -4
  6. package/corpus/core/src/agent/run-store.ts +80 -0
  7. package/corpus/core/src/cli/design-connect.ts +433 -151
  8. package/corpus/core/src/cli/skills.ts +14 -0
  9. package/corpus/core/src/client/AssistantChat.tsx +25 -0
  10. package/corpus/core/src/client/MultiTabAssistantChat.tsx +40 -6
  11. package/corpus/core/src/client/agent-chat.ts +165 -1
  12. package/corpus/core/src/client/chat/index.ts +9 -1
  13. package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +38 -2
  14. package/corpus/core/src/client/index.ts +5 -0
  15. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
  16. package/corpus/core/src/collab/awareness-store.ts +23 -12
  17. package/corpus/core/src/collab/client.ts +16 -5
  18. package/corpus/core/src/collab/index.ts +4 -0
  19. package/corpus/core/src/collab/recent-edits.ts +65 -2
  20. package/corpus/core/src/deploy/build.ts +133 -5
  21. package/corpus/core/src/index.browser.ts +1 -0
  22. package/corpus/core/src/server/agent-chat-plugin.ts +98 -11
  23. package/corpus/core/src/server/builder-design-systems.ts +30 -4
  24. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +1 -1
  25. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +12 -6
  26. package/corpus/templates/clips/AGENTS.md +4 -0
  27. package/corpus/templates/clips/actions/create-recording.ts +2 -0
  28. package/corpus/templates/clips/actions/lib/create-recording-schema.ts +5 -3
  29. package/corpus/templates/clips/actions/request-transcript.ts +102 -33
  30. package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -0
  31. package/corpus/templates/clips/app/components/player/share-dialog.tsx +59 -19
  32. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +48 -4
  33. package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
  34. package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
  35. package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
  36. package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
  37. package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
  38. package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
  39. package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
  40. package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
  41. package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
  42. package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
  43. package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
  44. package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
  45. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -37
  46. package/corpus/templates/clips/app/routes/record.tsx +17 -4
  47. package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
  48. package/corpus/templates/clips/changelog/2026-07-09-desktop-popover-sits-closer-to-the-menu-bar.md +6 -0
  49. package/corpus/templates/clips/changelog/2026-07-09-desktop-recording-optimization-progress-stays-visible-until-the-clip-opens.md +6 -0
  50. package/corpus/templates/clips/changelog/2026-07-09-fixed-large-video-file-uploads-failing-before-their-resumabl.md +6 -0
  51. package/corpus/templates/clips/changelog/2026-07-09-fixed-new-video-uploads-defaulting-to-private.md +6 -0
  52. package/corpus/templates/clips/changelog/2026-07-09-long-recordings-now-get-enough-time-to-download-their-media-.md +6 -0
  53. package/corpus/templates/clips/changelog/2026-07-09-meeting-notes-no-longer-lowers-your-microphone-volume-for-ot.md +6 -0
  54. package/corpus/templates/clips/changelog/2026-07-09-on-mobile-tapping-a-playing-clip-reveals-playback-controls-w.md +6 -0
  55. package/corpus/templates/clips/changelog/2026-07-09-regenerate-any-clip-transcript-from-its-saved-recording-or-r.md +6 -0
  56. package/corpus/templates/clips/changelog/2026-07-09-slack-previews-now-show-the-clip-length-alongside-its-descri.md +6 -0
  57. package/corpus/templates/clips/desktop/src/app.tsx +12 -2
  58. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +8 -1
  59. package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -5
  60. package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +5 -5
  61. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +3 -0
  62. package/corpus/templates/clips/desktop/src/styles.css +6 -3
  63. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +29 -11
  64. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
  65. package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +103 -21
  66. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +117 -39
  67. package/corpus/templates/clips/server/lib/slack-unfurls.ts +20 -1
  68. package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +10 -4
  69. package/corpus/templates/clips/shared/recording-core.ts +12 -0
  70. package/corpus/templates/content/AGENTS.md +7 -6
  71. package/corpus/templates/content/actions/_document-flush.ts +132 -17
  72. package/corpus/templates/content/actions/_notion-action-utils.ts +15 -0
  73. package/corpus/templates/content/actions/create-and-link-notion-page.ts +2 -0
  74. package/corpus/templates/content/actions/link-notion-page.ts +2 -0
  75. package/corpus/templates/content/actions/pull-document.ts +5 -7
  76. package/corpus/templates/content/actions/pull-notion-page.ts +2 -0
  77. package/corpus/templates/content/actions/push-notion-page.ts +10 -0
  78. package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +2 -0
  79. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +88 -23
  80. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +32 -2
  81. package/corpus/templates/content/app/hooks/use-notion.ts +6 -6
  82. package/corpus/templates/content/app/i18n/zh-TW.ts +1 -0
  83. package/corpus/templates/content/app/i18n-data.ts +18 -0
  84. package/corpus/templates/content/changelog/2026-07-09-notion-conflict-choices-and-version-restores-preserve-docume.md +6 -0
  85. package/corpus/templates/content/server/lib/notion-sync.ts +80 -64
  86. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +29 -2
  87. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +86 -0
  88. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +57 -0
  89. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +11 -0
  90. package/corpus/templates/design/AGENTS.md +45 -10
  91. package/corpus/templates/design/actions/add-localhost-screens.ts +66 -13
  92. package/corpus/templates/design/actions/apply-component-prop-edit.ts +45 -36
  93. package/corpus/templates/design/actions/apply-motion-edit.ts +25 -31
  94. package/corpus/templates/design/actions/apply-shader-fill.ts +29 -42
  95. package/corpus/templates/design/actions/detach-component-instance.ts +337 -0
  96. package/corpus/templates/design/actions/export-design-as-figma-svg.ts +219 -0
  97. package/corpus/templates/design/actions/generate-design.ts +79 -12
  98. package/corpus/templates/design/actions/generate-screens.ts +57 -12
  99. package/corpus/templates/design/actions/get-figma-styles.ts +115 -0
  100. package/corpus/templates/design/actions/go-to-main-component.ts +254 -0
  101. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +4 -3
  102. package/corpus/templates/design/actions/import-design-source.ts +4 -31
  103. package/corpus/templates/design/actions/import-figma-clipboard.ts +134 -0
  104. package/corpus/templates/design/actions/import-figma-frame.ts +95 -0
  105. package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -1
  106. package/corpus/templates/design/actions/list-design-components.ts +104 -0
  107. package/corpus/templates/design/actions/list-figma-library-assets.ts +1 -24
  108. package/corpus/templates/design/actions/open-component-source.ts +2 -4
  109. package/corpus/templates/design/actions/present-design-variants.ts +412 -16
  110. package/corpus/templates/design/actions/preview-source-edit.ts +14 -1
  111. package/corpus/templates/design/actions/run-design-audit.ts +25 -3
  112. package/corpus/templates/design/actions/show-design-questions.ts +10 -0
  113. package/corpus/templates/design/actions/swap-component-instance.ts +544 -0
  114. package/corpus/templates/design/actions/take-design-screenshot.ts +45 -60
  115. package/corpus/templates/design/actions/write-local-file.ts +25 -1
  116. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +152 -0
  117. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1077 -58
  118. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +5 -5
  119. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +73 -21
  120. package/corpus/templates/design/app/components/design/EditPanel.tsx +16 -11
  121. package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +364 -0
  122. package/corpus/templates/design/app/components/design/LayersPanel.tsx +180 -53
  123. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +0 -2
  124. package/corpus/templates/design/app/components/design/MotionDock.tsx +144 -21
  125. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1152 -167
  126. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +27 -5
  127. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +23 -2
  128. package/corpus/templates/design/app/components/design/TokensPanel.tsx +63 -49
  129. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1293 -54
  130. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +7 -0
  131. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +6 -1
  132. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +23 -6
  133. package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +7 -0
  134. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -0
  135. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +14 -2
  136. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +37 -2
  137. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open-guard.ts +25 -0
  138. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +19 -1
  139. package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +43 -5
  140. package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +27 -2
  141. package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +10 -1
  142. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +19 -2
  143. package/corpus/templates/design/app/components/design/design-canvas/annotation-snapshot.ts +265 -0
  144. package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +16 -5
  145. package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +16 -2
  146. package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +7 -2
  147. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +3 -1
  148. package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +78 -8
  149. package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +451 -73
  150. package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +12 -4
  151. package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +398 -272
  152. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +41 -7
  153. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +16 -3
  154. package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +24 -4
  155. package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +222 -4
  156. package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +171 -78
  157. package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +45 -5
  158. package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +299 -28
  159. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +120 -150
  160. package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +53 -1
  161. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +284 -117
  162. package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +81 -0
  163. package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +19 -14
  164. package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +7 -0
  165. package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +155 -0
  166. package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +268 -46
  167. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +350 -31
  168. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +13 -4
  169. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +233 -23
  170. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +72 -2
  171. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +51 -11
  172. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +84 -11
  173. package/corpus/templates/design/app/components/design/inspector/index.ts +3 -0
  174. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +552 -0
  175. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +157 -0
  176. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +53 -0
  177. package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +184 -0
  178. package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +403 -26
  179. package/corpus/templates/design/app/components/design/multi-screen/types.ts +20 -1
  180. package/corpus/templates/design/app/components/design/types.ts +33 -0
  181. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +59 -7
  182. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +119 -17
  183. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +275 -63
  184. package/corpus/templates/design/app/hooks/use-question-flow.ts +14 -3
  185. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +67 -10
  186. package/corpus/templates/design/app/i18n/zh-TW.ts +91 -1
  187. package/corpus/templates/design/app/i18n-data.ts +1217 -10
  188. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +577 -0
  189. package/corpus/templates/design/app/lib/agent-chat.ts +23 -0
  190. package/corpus/templates/design/app/lib/design-import.ts +24 -0
  191. package/corpus/templates/design/app/lib/figma-clipboard.ts +157 -0
  192. package/corpus/templates/design/app/lib/figma-svg-copy.ts +221 -0
  193. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +143 -30
  194. package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +15 -0
  195. package/corpus/templates/design/app/pages/design-editor/editor-session.ts +33 -0
  196. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +38 -0
  197. package/corpus/templates/design/app/pages/design-editor/overview-annotation-context.ts +157 -0
  198. package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +62 -6
  199. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +245 -0
  200. package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +586 -0
  201. package/corpus/templates/design/app/pages/design-editor/screen-auto-layout.ts +169 -0
  202. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +201 -1
  203. package/corpus/templates/design/changelog/2026-07-09-added-underline-strikethrough-and-text-case-uppercase-lowerc.md +6 -0
  204. package/corpus/templates/design/changelog/2026-07-09-annotate-now-draws-across-the-all-screens-canvas-without-swi.md +6 -0
  205. package/corpus/templates/design/changelog/2026-07-09-auto-layout-children-can-now-be-dragged-into-freeform-screen.md +6 -0
  206. package/corpus/templates/design/changelog/2026-07-09-board-shapes-now-stay-visible-and-nest-correctly-across-the-.md +6 -0
  207. package/corpus/templates/design/changelog/2026-07-09-component-source-navigation-now-opens-reliably-from-the-insp.md +6 -0
  208. package/corpus/templates/design/changelog/2026-07-09-cross-screen-local-react-layer-moves-now-hand-off-exact-sour.md +6 -0
  209. package/corpus/templates/design/changelog/2026-07-09-cross-screen-moves-undo-and-redo-now-keep-both-screens-stabl.md +6 -0
  210. package/corpus/templates/design/changelog/2026-07-09-detach-a-component-instance-into-plain-editable-eleme.md +6 -0
  211. package/corpus/templates/design/changelog/2026-07-09-dragging-layers-into-auto-layout-now-respects-ignore-auto-la.md +6 -0
  212. package/corpus/templates/design/changelog/2026-07-09-drawing-annotations-sent-to-the-agent-now-include-a-rendered.md +6 -0
  213. package/corpus/templates/design/changelog/2026-07-09-editor-sidebars-now-use-figma-matched-default-widths-for-a-r.md +6 -0
  214. package/corpus/templates/design/changelog/2026-07-09-empty-frames-and-rectangles-now-expose-full-auto-layout-cont.md +6 -0
  215. package/corpus/templates/design/changelog/2026-07-09-export-any-design-screen-as-a-real-vector-svg-for-figma-with.md +6 -0
  216. package/corpus/templates/design/changelog/2026-07-09-grid-auto-layout-now-uses-true-responsive-rows-and-columns-w.md +6 -0
  217. package/corpus/templates/design/changelog/2026-07-09-hidden-and-locked-screens-now-behave-consistently-between-la.md +6 -0
  218. package/corpus/templates/design/changelog/2026-07-09-image-fills-now-preserve-valid-urls-and-layered-fit-settings.md +6 -0
  219. package/corpus/templates/design/changelog/2026-07-09-import-figma-frames-as-pixel-accurate-design-screens-via-a-s.md +6 -0
  220. package/corpus/templates/design/changelog/2026-07-09-inspect-code-now-hides-internal-styling-metadata-and-wraps-l.md +6 -0
  221. package/corpus/templates/design/changelog/2026-07-09-inspector-blur-and-shadow-effects-now-preserve-other-effects.md +6 -0
  222. package/corpus/templates/design/changelog/2026-07-09-jump-to-a-components-other-instances-from-the-inspecto.md +6 -0
  223. package/corpus/templates/design/changelog/2026-07-09-keyboard-shortcuts-are-now-discoverable-in-a-figma-style-bot.md +6 -0
  224. package/corpus/templates/design/changelog/2026-07-09-layer-selection-now-keeps-cmd-or-ctrl-toggles-and-shift-rang.md +6 -0
  225. package/corpus/templates/design/changelog/2026-07-09-layers-dropped-into-auto-layout-now-participate-correctly-in.md +6 -0
  226. package/corpus/templates/design/changelog/2026-07-09-local-live-edit-screens-now-enter-and-leave-full-view-withou.md +6 -0
  227. package/corpus/templates/design/changelog/2026-07-09-local-preview-urls-now-live-in-the-inspector-instead-of-taki.md +6 -0
  228. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-can-now-be-hidden-and-locked-from-layers-.md +6 -0
  229. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-now-keep-exact-source-provenance-and-supp.md +6 -0
  230. package/corpus/templates/design/changelog/2026-07-09-local-react-visual-editing-now-loads-reliably-and-shows-live.md +6 -0
  231. package/corpus/templates/design/changelog/2026-07-09-paste-from-figma-cmd-v-now-imports-exact-editable-nodes-when.md +6 -0
  232. package/corpus/templates/design/changelog/2026-07-09-pressing-escape-after-selecting-a-screen-now-reveals-page-pr.md +6 -0
  233. package/corpus/templates/design/changelog/2026-07-09-right-click-overlapping-or-nested-objects-to-choose-the-exac.md +6 -0
  234. package/corpus/templates/design/changelog/2026-07-09-scale-edge-stretch-and-center-constraints-now-preserve-layer.md +6 -0
  235. package/corpus/templates/design/changelog/2026-07-09-screen-rows-can-now-be-reordered-in-layers-with-matching-can.md +6 -0
  236. package/corpus/templates/design/changelog/2026-07-09-screen-selection-stays-responsive-in-designs-with-hundreds-o.md +6 -0
  237. package/corpus/templates/design/changelog/2026-07-09-shift-a-now-enables-auto-layout-on-a-selected-screen-with-fl.md +6 -0
  238. package/corpus/templates/design/changelog/2026-07-09-swap-a-component-instance-for-another-from-the-libra.md +6 -0
  239. package/corpus/templates/design/changelog/2026-07-10-drawing-annotations-now-stay-available-to-retry-when-the.md +6 -0
  240. package/corpus/templates/design/e2e/global-setup.ts +3 -6
  241. package/corpus/templates/design/e2e/helpers.ts +27 -6
  242. package/corpus/templates/design/server/lib/design-to-figma-svg.ts +1412 -0
  243. package/corpus/templates/design/server/lib/figma-clipboard-match.ts +196 -0
  244. package/corpus/templates/design/server/lib/figma-node-import.ts +311 -0
  245. package/corpus/templates/design/server/lib/figma-node-to-html.ts +1334 -0
  246. package/corpus/templates/design/server/lib/figma-paste-fallback.ts +64 -0
  247. package/corpus/templates/design/server/lib/playwright-runtime.ts +78 -0
  248. package/corpus/templates/design/server/plugins/db.ts +41 -0
  249. package/corpus/templates/design/server/source-workspace.ts +64 -0
  250. package/corpus/templates/design/shared/board-file.ts +13 -2
  251. package/corpus/templates/design/shared/canvas-math.ts +231 -33
  252. package/corpus/templates/design/shared/code-layer.ts +122 -8
  253. package/corpus/templates/design/shared/component-library.ts +161 -0
  254. package/corpus/templates/design/shared/db-conflict.ts +27 -0
  255. package/corpus/templates/design/shared/design-source-capabilities.ts +2 -1
  256. package/corpus/templates/design/shared/figma-url.ts +149 -0
  257. package/corpus/templates/design/shared/pen-path.ts +16 -7
  258. package/corpus/templates/design/shared/source-mode.ts +1 -0
  259. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +7 -19
  260. package/corpus/templates/plan/changelog/2026-07-09-canvas-panning-now-keeps-zoom-fixed-even-at-the-edges.md +6 -0
  261. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +16 -1
  262. package/corpus/templates/slides/app/context/DeckContext.tsx +311 -70
  263. package/corpus/templates/slides/changelog/2026-07-09-fixed-the-slide-rail-going-permanently-stale-during-a-long-a.md +6 -0
  264. package/dist/agent/production-agent.d.ts +79 -6
  265. package/dist/agent/production-agent.d.ts.map +1 -1
  266. package/dist/agent/production-agent.js +161 -14
  267. package/dist/agent/production-agent.js.map +1 -1
  268. package/dist/agent/run-manager.d.ts.map +1 -1
  269. package/dist/agent/run-manager.js +29 -5
  270. package/dist/agent/run-manager.js.map +1 -1
  271. package/dist/agent/run-store.d.ts +46 -0
  272. package/dist/agent/run-store.d.ts.map +1 -1
  273. package/dist/agent/run-store.js +61 -0
  274. package/dist/agent/run-store.js.map +1 -1
  275. package/dist/cli/design-connect.d.ts +6 -0
  276. package/dist/cli/design-connect.d.ts.map +1 -1
  277. package/dist/cli/design-connect.js +336 -139
  278. package/dist/cli/design-connect.js.map +1 -1
  279. package/dist/cli/skills.d.ts.map +1 -1
  280. package/dist/cli/skills.js +14 -0
  281. package/dist/cli/skills.js.map +1 -1
  282. package/dist/client/AssistantChat.d.ts +2 -0
  283. package/dist/client/AssistantChat.d.ts.map +1 -1
  284. package/dist/client/AssistantChat.js +19 -3
  285. package/dist/client/AssistantChat.js.map +1 -1
  286. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  287. package/dist/client/MultiTabAssistantChat.js +38 -18
  288. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  289. package/dist/client/agent-chat.d.ts +60 -0
  290. package/dist/client/agent-chat.d.ts.map +1 -1
  291. package/dist/client/agent-chat.js +122 -1
  292. package/dist/client/agent-chat.js.map +1 -1
  293. package/dist/client/chat/index.d.ts +1 -1
  294. package/dist/client/chat/index.d.ts.map +1 -1
  295. package/dist/client/chat/index.js +1 -1
  296. package/dist/client/chat/index.js.map +1 -1
  297. package/dist/client/extensions/EmbeddedExtension.d.ts +12 -0
  298. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  299. package/dist/client/extensions/EmbeddedExtension.js +36 -2
  300. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  301. package/dist/client/index.d.ts +1 -1
  302. package/dist/client/index.d.ts.map +1 -1
  303. package/dist/client/index.js +1 -1
  304. package/dist/client/index.js.map +1 -1
  305. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  306. package/dist/client/rich-markdown-editor/useCollabReconcile.js +18 -6
  307. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  308. package/dist/collab/awareness-store.d.ts +5 -0
  309. package/dist/collab/awareness-store.d.ts.map +1 -1
  310. package/dist/collab/awareness-store.js +19 -12
  311. package/dist/collab/awareness-store.js.map +1 -1
  312. package/dist/collab/awareness.d.ts +2 -2
  313. package/dist/collab/awareness.d.ts.map +1 -1
  314. package/dist/collab/client.d.ts.map +1 -1
  315. package/dist/collab/client.js +13 -5
  316. package/dist/collab/client.js.map +1 -1
  317. package/dist/collab/index.d.ts +1 -0
  318. package/dist/collab/index.d.ts.map +1 -1
  319. package/dist/collab/index.js +1 -0
  320. package/dist/collab/index.js.map +1 -1
  321. package/dist/collab/recent-edits.d.ts +3 -1
  322. package/dist/collab/recent-edits.d.ts.map +1 -1
  323. package/dist/collab/recent-edits.js +57 -2
  324. package/dist/collab/recent-edits.js.map +1 -1
  325. package/dist/collab/routes.d.ts +1 -1
  326. package/dist/deploy/build.d.ts +24 -0
  327. package/dist/deploy/build.d.ts.map +1 -1
  328. package/dist/deploy/build.js +102 -5
  329. package/dist/deploy/build.js.map +1 -1
  330. package/dist/index.browser.d.ts +1 -1
  331. package/dist/index.browser.d.ts.map +1 -1
  332. package/dist/index.browser.js +1 -1
  333. package/dist/index.browser.js.map +1 -1
  334. package/dist/notifications/routes.d.ts +1 -1
  335. package/dist/observability/routes.d.ts +5 -5
  336. package/dist/progress/routes.d.ts +1 -1
  337. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  338. package/dist/server/agent-chat-plugin.js +87 -10
  339. package/dist/server/agent-chat-plugin.js.map +1 -1
  340. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  341. package/dist/server/builder-design-systems.d.ts +15 -0
  342. package/dist/server/builder-design-systems.d.ts.map +1 -1
  343. package/dist/server/builder-design-systems.js +9 -4
  344. package/dist/server/builder-design-systems.js.map +1 -1
  345. package/package.json +1 -1
@@ -19,7 +19,7 @@ export function DesignEditorSkeleton({
19
19
  <div className="flex h-full overflow-hidden bg-[var(--design-editor-canvas-bg)]">
20
20
  {!embedded && (
21
21
  <aside className="relative flex min-h-0 shrink-0 border-r border-[var(--design-editor-panel-divider-color)] bg-[var(--design-editor-panel-bg)]">
22
- <div className="flex w-[52px] shrink-0 flex-col items-center p-3">
22
+ <div className="flex w-[57px] shrink-0 flex-col items-center p-3">
23
23
  <Skeleton className={`size-8 rounded-md ${panelGhost}`} />
24
24
  <div className="mt-8 flex w-full flex-col items-center gap-3">
25
25
  <Skeleton className={`size-8 rounded-lg ${panelGhost}`} />
@@ -27,7 +27,7 @@ export function DesignEditorSkeleton({
27
27
  <Skeleton className={`size-8 rounded-lg ${panelGhost}`} />
28
28
  </div>
29
29
  </div>
30
- <div className="flex w-[320px] min-w-0 flex-col bg-[var(--design-editor-panel-bg)]">
30
+ <div className="flex w-[280px] min-w-0 flex-col bg-[var(--design-editor-panel-bg)]">
31
31
  <div className="border-b border-[var(--design-editor-panel-divider-color)] p-3">
32
32
  <Skeleton className={`h-5 w-28 rounded ${panelGhost}`} />
33
33
  </div>
@@ -60,8 +60,8 @@ export function DesignEditorSkeleton({
60
60
  return (
61
61
  <div className="flex h-full overflow-hidden bg-background">
62
62
  {!embedded && (
63
- <aside className="hidden w-80 shrink-0 bg-[var(--design-editor-panel-bg)] lg:flex">
64
- <div className="flex w-[52px] shrink-0 flex-col items-center p-3">
63
+ <aside className="hidden w-[337px] shrink-0 bg-[var(--design-editor-panel-bg)] lg:flex">
64
+ <div className="flex w-[57px] shrink-0 flex-col items-center p-3">
65
65
  <Skeleton className={`size-8 rounded-md ${panelGhost}`} />
66
66
  <Skeleton className={`mt-8 h-40 w-full rounded-lg ${panelGhost}`} />
67
67
  </div>
@@ -89,7 +89,7 @@ export function DesignEditorSkeleton({
89
89
  </main>
90
90
 
91
91
  {!embedded && (
92
- <aside className="hidden w-80 shrink-0 flex-col bg-[var(--design-editor-panel-bg)] lg:flex">
92
+ <aside className="hidden w-[240px] shrink-0 flex-col bg-[var(--design-editor-panel-bg)] lg:flex">
93
93
  <div className="flex h-12 shrink-0 items-center justify-end px-3">
94
94
  <Skeleton className={`h-6 w-40 rounded-md ${panelGhost}`} />
95
95
  </div>
@@ -271,7 +271,12 @@ function useSlotInstalls(slotId: string) {
271
271
  `/_agent-native/slots/${encodeURIComponent(slotId)}/installs`,
272
272
  ),
273
273
  );
274
- if (!res.ok) return [];
274
+ // Surface fetch failures as a query error instead of swallowing them as
275
+ // an empty list — an empty list renders identically to "nothing
276
+ // installed", which hides real server/network errors from the user.
277
+ if (!res.ok) {
278
+ throw new Error(`Failed to load installed extensions: ${res.status}`);
279
+ }
275
280
  return res.json();
276
281
  },
277
282
  placeholderData: (prev) => prev,
@@ -288,13 +293,59 @@ function useAvailableExtensions(slotId: string) {
288
293
  `/_agent-native/slots/${encodeURIComponent(slotId)}/available`,
289
294
  ),
290
295
  );
291
- if (!res.ok) return [];
296
+ // See the matching note in useSlotInstalls above — don't mask a fetch
297
+ // failure as "no extensions available".
298
+ if (!res.ok) {
299
+ throw new Error(`Failed to load available extensions: ${res.status}`);
300
+ }
292
301
  return res.json();
293
302
  },
294
303
  placeholderData: (prev) => prev,
295
304
  });
296
305
  }
297
306
 
307
+ /**
308
+ * POSTs the install request for `extensionId` and waits for both the
309
+ * "installed" and "available" slot queries to fully refetch before
310
+ * resolving.
311
+ *
312
+ * Exported so a test can verify the ordering directly: the previous
313
+ * implementation invalidated the queries without awaiting them, so the
314
+ * component's `finally` block re-enabled the Install button (by clearing
315
+ * `installingId`) while the "Available" list still listed the
316
+ * just-installed extension — a race that let a fast double-click fire a
317
+ * second install request before the list caught up. Awaiting here means the
318
+ * caller doesn't regain control (and hasn't cleared its "installing" state)
319
+ * until the lists are provably current.
320
+ */
321
+ export async function installExtensionRequest(
322
+ slotId: string,
323
+ extensionId: string,
324
+ queryClient: {
325
+ invalidateQueries: (options: { queryKey: unknown[] }) => Promise<unknown>;
326
+ },
327
+ ): Promise<void> {
328
+ const res = await fetch(
329
+ agentNativePath(
330
+ `/_agent-native/slots/${encodeURIComponent(slotId)}/install`,
331
+ ),
332
+ {
333
+ method: "POST",
334
+ headers: { "Content-Type": "application/json" },
335
+ body: JSON.stringify({ extensionId }),
336
+ },
337
+ );
338
+ if (!res.ok) throw new Error(`Install failed: ${res.status}`);
339
+ await Promise.all([
340
+ queryClient.invalidateQueries({
341
+ queryKey: ["design-editor-extension-slot"],
342
+ }),
343
+ queryClient.invalidateQueries({
344
+ queryKey: ["design-editor-extension-slot-available"],
345
+ }),
346
+ ]);
347
+ }
348
+
298
349
  // ─── First-party extension rows ───────────────────────────────────────────────
299
350
 
300
351
  interface FirstPartyRowProps {
@@ -1390,8 +1441,13 @@ export function DesignExtensionsPanel({
1390
1441
  null,
1391
1442
  );
1392
1443
  const slotId = DESIGN_EDITOR_EXTENSION_SLOT_ID;
1393
- const { data: installs = [], isLoading } = useSlotInstalls(slotId);
1394
- const { data: available = [] } = useAvailableExtensions(slotId);
1444
+ const {
1445
+ data: installs = [],
1446
+ isLoading,
1447
+ isError: installsErrored,
1448
+ } = useSlotInstalls(slotId);
1449
+ const { data: available = [], isError: availableErrored } =
1450
+ useAvailableExtensions(slotId);
1395
1451
  const installedIds = useMemo(
1396
1452
  () => new Set(installs.map((install) => install.extensionId)),
1397
1453
  [installs],
@@ -1420,23 +1476,12 @@ export function DesignExtensionsPanel({
1420
1476
  const installExtension = async (extensionId: string) => {
1421
1477
  setInstallingId(extensionId);
1422
1478
  try {
1423
- const res = await fetch(
1424
- agentNativePath(
1425
- `/_agent-native/slots/${encodeURIComponent(slotId)}/install`,
1426
- ),
1427
- {
1428
- method: "POST",
1429
- headers: { "Content-Type": "application/json" },
1430
- body: JSON.stringify({ extensionId }),
1431
- },
1432
- );
1433
- if (!res.ok) throw new Error(`Install failed: ${res.status}`);
1434
- queryClient.invalidateQueries({
1435
- queryKey: ["design-editor-extension-slot"],
1436
- });
1437
- queryClient.invalidateQueries({
1438
- queryKey: ["design-editor-extension-slot-available"],
1439
- });
1479
+ // Awaits both slot queries' refetch, not just firing invalidation —
1480
+ // see installExtensionRequest's doc comment for the duplicate-install
1481
+ // race this closes: installingId (and therefore the disabled Install
1482
+ // button) must stay set until the "Available" list has actually
1483
+ // dropped this extension, not just until the POST resolves.
1484
+ await installExtensionRequest(slotId, extensionId, queryClient);
1440
1485
  } catch {
1441
1486
  toast.error(t("designEditor.extensionsInstallError"));
1442
1487
  } finally {
@@ -1570,6 +1615,13 @@ export function DesignExtensionsPanel({
1570
1615
  />
1571
1616
  </div>
1572
1617
 
1618
+ {(installsErrored || availableErrored) && (
1619
+ <p className="mb-3 rounded border border-destructive/30 bg-destructive/5 px-2 py-1.5 text-[10px] leading-snug text-destructive">
1620
+ Couldn&apos;t load extensions. Built-in tools are still available
1621
+ below — check your connection and try again.
1622
+ </p>
1623
+ )}
1624
+
1573
1625
  {isLoading ? (
1574
1626
  <div className="space-y-3">
1575
1627
  <Skeleton className="h-12 rounded-md" />
@@ -170,7 +170,6 @@ import {
170
170
  componentNameForElementInfo,
171
171
  cssElementSize,
172
172
  displayLabel,
173
- elementHasLayoutChildren,
174
173
  elementIsComponentSelection,
175
174
  horizontalToJustify,
176
175
  inferElementSizing,
@@ -198,7 +197,6 @@ import {
198
197
  } from "./edit-panel/field-primitives";
199
198
  import {
200
199
  averageGradientOpacity,
201
- buildFillRows,
202
200
  buildGradientLayer,
203
201
  DEFAULT_EXPORT_SETTINGS,
204
202
  defaultGradientLayer,
@@ -321,13 +319,10 @@ import {
321
319
  type ExportSettingsValue,
322
320
  type FrameSizePreset,
323
321
  type FrameSizePresetCategoryKey,
324
- imageFillToBackgroundStyles,
325
322
  InteractionStatePanel,
326
323
  type ActiveInteractionState,
327
324
  type DesignFillRow,
328
- type DesignFillRowPatch,
329
325
  type DesignGradientStop,
330
- type DesignGradientStopPatch,
331
326
  type DesignGradientType,
332
327
  type ImageFillValue,
333
328
  type MotionKeyframeCssProperty,
@@ -459,6 +454,8 @@ interface EditPanelProps {
459
454
  * and an Edit component action.
460
455
  */
461
456
  componentNodeId?: string;
457
+ /** Increment to open the selected component's Swap instance picker. */
458
+ componentSwapPickerRequest?: number;
462
459
  /**
463
460
  * Source capabilities for the current design. Used to gate the Edit
464
461
  * component / jump-to-source affordances. When absent all writes default
@@ -1153,12 +1150,17 @@ function PageProperties({
1153
1150
  backgroundRepeat={styles.backgroundRepeat}
1154
1151
  backgroundPosition={styles.backgroundPosition}
1155
1152
  onBackgroundImageChange={(v) => onStyleChange("backgroundImage", v)}
1156
- onImageFillChange={(value) =>
1157
- commitStylePatch(
1158
- imageFillToBackgroundStyles(value),
1159
- onStyleChange,
1160
- onStylesChange,
1161
- )
1153
+ // Layer-index-aware: ColorInput merges the edited image into the
1154
+ // correct backgroundImage/backgroundSize/backgroundRepeat/
1155
+ // backgroundPosition index and hands back the full four-property
1156
+ // patch here, already preserving every other stacked
1157
+ // gradient/image layer (same pattern as FillProperties' base fill
1158
+ // row — see fill-properties.tsx). The single-layer
1159
+ // `onImageFillChange` this previously used always overwrote the
1160
+ // *whole* background stack via `imageFillToBackgroundStyles`,
1161
+ // silently wiping any other stacked background layer.
1162
+ onImageFillLayerChange={(patch) =>
1163
+ commitStylePatch(patch, onStyleChange, onStylesChange)
1162
1164
  }
1163
1165
  blendMode={styles.backgroundBlendMode || "normal"}
1164
1166
  onBlendModeChange={(v) => onStyleChange("backgroundBlendMode", v)}
@@ -1351,6 +1353,7 @@ export const EditPanel = memo(function EditPanel({
1351
1353
  onComponentPropApplied,
1352
1354
  reviewPanelProps,
1353
1355
  componentNodeId,
1356
+ componentSwapPickerRequest,
1354
1357
  sourceCapabilities = [],
1355
1358
  onCreateComponent,
1356
1359
  selectedElementAlreadyComponent = false,
@@ -1761,6 +1764,7 @@ export const EditPanel = memo(function EditPanel({
1761
1764
  activeContent={activeContent}
1762
1765
  activeFileUpdatedAt={activeFileUpdatedAt}
1763
1766
  nodeId={componentNodeId}
1767
+ swapPickerRequest={componentSwapPickerRequest}
1764
1768
  onComponentPropApplied={onComponentPropApplied}
1765
1769
  sourceCapabilities={sourceCapabilities}
1766
1770
  />
@@ -1805,6 +1809,7 @@ export const EditPanel = memo(function EditPanel({
1805
1809
  <PositionLayoutProperties
1806
1810
  element={inspectorElement}
1807
1811
  onStyleChange={onStyleChange}
1812
+ onStylesChange={onStylesChange}
1808
1813
  onAlignSelection={onAlignSelection}
1809
1814
  motionKeyframeContext={motionKeyframeFieldContext}
1810
1815
  breakpointOverrideContext={breakpointOverrideFieldContext}
@@ -0,0 +1,364 @@
1
+ import { useT } from "@agent-native/core/client";
2
+ import {
3
+ IconArrowUpRight,
4
+ IconCircle,
5
+ IconFrame,
6
+ IconHandStop,
7
+ IconLine,
8
+ IconMessage,
9
+ IconPencil,
10
+ IconPointer,
11
+ IconScale,
12
+ IconScribble,
13
+ IconSquare,
14
+ IconTypography,
15
+ IconX,
16
+ } from "@tabler/icons-react";
17
+ import { useEffect, useMemo, useRef, useState } from "react";
18
+
19
+ import { createCoreCommands } from "@/components/design/code-workbench/commands";
20
+ import {
21
+ DESIGN_SHORTCUT_CATEGORIES,
22
+ DESIGN_SHORTCUTS,
23
+ type DesignShortcutCategory,
24
+ formatShortcutKeycaps,
25
+ } from "@/components/design/keyboard-shortcuts";
26
+ import { Button } from "@/components/ui/button";
27
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
28
+
29
+ interface KeyboardShortcutsPanelProps {
30
+ onClose: () => void;
31
+ }
32
+
33
+ const TOOL_ICON_BY_SHORTCUT: Record<string, typeof IconPointer> = {
34
+ "move-tool": IconPointer,
35
+ "frame-tool": IconFrame,
36
+ "text-tool": IconTypography,
37
+ "pen-tool": IconPencil,
38
+ "hand-tool": IconHandStop,
39
+ "scale-tool": IconScale,
40
+ "comment-tool": IconMessage,
41
+ "draw-tool": IconScribble,
42
+ rectangle: IconSquare,
43
+ ellipse: IconCircle,
44
+ line: IconLine,
45
+ arrow: IconArrowUpRight,
46
+ };
47
+
48
+ const ESSENTIAL_DESCRIPTION_KEY_BY_SHORTCUT = {
49
+ "toggle-ui": "designEditor.keyboardShortcuts.descriptions.toggleUi",
50
+ undo: "designEditor.keyboardShortcuts.descriptions.undo",
51
+ redo: "designEditor.keyboardShortcuts.descriptions.redo",
52
+ } as const;
53
+
54
+ const CODE_CATEGORY_BY_COMMAND: Record<string, DesignShortcutCategory> = {
55
+ "workbench.save": "edit",
56
+ "workbench.saveAll": "edit",
57
+ "workbench.quickOpen": "edit",
58
+ "workbench.commandPalette": "edit",
59
+ "workbench.search": "edit",
60
+ "workbench.explorer": "view",
61
+ "workbench.toggleSidebar": "view",
62
+ "workbench.nextTab": "selection",
63
+ "workbench.previousTab": "selection",
64
+ "editor.gotoSymbol": "selection",
65
+ "editor.gotoLine": "selection",
66
+ };
67
+
68
+ const ACCESSIBLE_KEY_NAME_BY_TOKEN: Record<string, string> = {
69
+ alt: "alt",
70
+ arrowdown: "arrowDown",
71
+ arrowleft: "arrowLeft",
72
+ arrowright: "arrowRight",
73
+ arrowup: "arrowUp",
74
+ backspace: "backspace",
75
+ ctrl: "control",
76
+ delete: "delete",
77
+ enter: "enter",
78
+ shift: "shift",
79
+ tab: "tab",
80
+ "?": "questionMark",
81
+ "\\": "backslash",
82
+ "=": "equals",
83
+ "-": "minus",
84
+ "[": "leftBracket",
85
+ "]": "rightBracket",
86
+ };
87
+
88
+ function KeycapGroup({
89
+ binding,
90
+ applePlatform,
91
+ }: {
92
+ binding: string;
93
+ applePlatform: boolean;
94
+ }) {
95
+ return (
96
+ <span
97
+ data-keycap-group={binding}
98
+ aria-hidden="true"
99
+ className="inline-flex items-center gap-0.5 whitespace-nowrap"
100
+ >
101
+ {formatShortcutKeycaps(binding, applePlatform).map((keycap, index) => (
102
+ <kbd
103
+ key={`${keycap}-${index}`}
104
+ aria-hidden="true"
105
+ className="inline-flex min-w-5 items-center justify-center rounded-[3px] border border-neutral-500/70 bg-neutral-700/80 px-1 py-0.5 font-sans text-[10px] font-medium leading-none text-neutral-100 shadow-[0_1px_0_rgba(255,255,255,0.1)]"
106
+ >
107
+ {keycap}
108
+ </kbd>
109
+ ))}
110
+ </span>
111
+ );
112
+ }
113
+
114
+ function ShortcutBindings({ bindings }: { bindings: readonly string[] }) {
115
+ const t = useT();
116
+ const applePlatform =
117
+ typeof navigator !== "undefined" &&
118
+ /Mac|iPhone|iPad/.test(navigator.platform);
119
+ const accessibleBindings = bindings.map((binding) =>
120
+ binding
121
+ .split("+")
122
+ .map((rawToken) => {
123
+ const token = rawToken.toLowerCase();
124
+ if (token === "$mod") {
125
+ return t(
126
+ `designEditor.keyboardShortcuts.keys.${applePlatform ? "command" : "control"}`,
127
+ );
128
+ }
129
+ if (token === "alt" && applePlatform) {
130
+ return t("designEditor.keyboardShortcuts.keys.option");
131
+ }
132
+ const keyName = ACCESSIBLE_KEY_NAME_BY_TOKEN[token];
133
+ if (keyName) {
134
+ return t(`designEditor.keyboardShortcuts.keys.${keyName}`);
135
+ }
136
+ return rawToken.length === 1 ? rawToken.toLocaleUpperCase() : rawToken;
137
+ })
138
+ .join(" "),
139
+ );
140
+
141
+ return (
142
+ <span
143
+ data-shortcut-bindings
144
+ role="group"
145
+ aria-label={accessibleBindings.join(
146
+ ` ${t("designEditor.keyboardShortcuts.keys.or")} `,
147
+ )}
148
+ className="flex shrink-0 items-center gap-1.5"
149
+ >
150
+ {bindings.map((binding) => (
151
+ <KeycapGroup
152
+ key={binding}
153
+ binding={binding}
154
+ applePlatform={applePlatform}
155
+ />
156
+ ))}
157
+ </span>
158
+ );
159
+ }
160
+
161
+ export function KeyboardShortcutsPanel({
162
+ onClose,
163
+ }: KeyboardShortcutsPanelProps) {
164
+ const t = useT();
165
+ const [category, setCategory] = useState<DesignShortcutCategory>("essential");
166
+ const initialTabRef = useRef<HTMLButtonElement | null>(null);
167
+ const codeShortcuts = useMemo(
168
+ () =>
169
+ createCoreCommands().flatMap((command) => {
170
+ const commandCategory = CODE_CATEGORY_BY_COMMAND[command.id];
171
+ return commandCategory && command.keybindings?.length
172
+ ? [{ ...command, shortcutCategory: commandCategory }]
173
+ : [];
174
+ }),
175
+ [],
176
+ );
177
+
178
+ useEffect(() => {
179
+ // Radix Menu restores focus to its trigger in its own close frame. Wait
180
+ // through that frame before moving focus into the newly-opened dock so
181
+ // menu invocation and the global shortcut share the same focus contract.
182
+ let focusFrame = 0;
183
+ const menuCloseFrame = window.requestAnimationFrame(() => {
184
+ focusFrame = window.requestAnimationFrame(() => {
185
+ initialTabRef.current?.focus({ preventScroll: true });
186
+ });
187
+ });
188
+ return () => {
189
+ window.cancelAnimationFrame(menuCloseFrame);
190
+ if (focusFrame) window.cancelAnimationFrame(focusFrame);
191
+ };
192
+ }, []);
193
+
194
+ return (
195
+ <section
196
+ data-keyboard-shortcuts-panel
197
+ role="region"
198
+ aria-label={t("designEditor.keyboardShortcuts.title")}
199
+ className="absolute inset-x-0 bottom-0 z-[60] flex h-[241px] flex-col border-t border-neutral-700 bg-neutral-900 text-neutral-100 shadow-[0_-12px_28px_rgba(0,0,0,0.28)]"
200
+ >
201
+ <Tabs
202
+ value={category}
203
+ onValueChange={(value) => setCategory(value as DesignShortcutCategory)}
204
+ className="flex min-h-0 flex-1 flex-col"
205
+ >
206
+ <div
207
+ data-shortcuts-tab-row
208
+ className="flex h-[38px] shrink-0 items-center border-b border-neutral-700"
209
+ >
210
+ <div className="min-w-0 flex-1 overflow-x-auto pl-[124px] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
211
+ <TabsList
212
+ data-shortcuts-tab-strip
213
+ className="h-9 w-max justify-start rounded-none bg-transparent p-0"
214
+ >
215
+ {DESIGN_SHORTCUT_CATEGORIES.map((categoryId) => (
216
+ <TabsTrigger
217
+ key={categoryId}
218
+ value={categoryId}
219
+ ref={categoryId === "essential" ? initialTabRef : undefined}
220
+ className="h-9 rounded-none border-x-0 border-b-2 border-t-0 border-transparent bg-transparent px-2.5 text-[11px] font-medium text-neutral-400 shadow-none data-[state=active]:border-blue-400 data-[state=active]:bg-transparent data-[state=active]:text-white data-[state=active]:shadow-none"
221
+ >
222
+ {t(`designEditor.keyboardShortcuts.categories.${categoryId}`)}
223
+ </TabsTrigger>
224
+ ))}
225
+ </TabsList>
226
+ </div>
227
+ <Button
228
+ data-keyboard-shortcuts-close
229
+ type="button"
230
+ variant="ghost"
231
+ size="icon"
232
+ className="ml-2 size-7 shrink-0 text-neutral-300 hover:bg-neutral-700 hover:text-white"
233
+ aria-label={t("designEditor.keyboardShortcuts.close")}
234
+ onClick={onClose}
235
+ >
236
+ <IconX className="size-4" />
237
+ </Button>
238
+ </div>
239
+
240
+ {DESIGN_SHORTCUT_CATEGORIES.map((categoryId) => {
241
+ const designRows = DESIGN_SHORTCUTS.filter((item) =>
242
+ categoryId === "tools"
243
+ ? item.category === "tools" || item.category === "shape"
244
+ : item.category === categoryId,
245
+ );
246
+ const codeRows = codeShortcuts.filter(
247
+ (item) => item.shortcutCategory === categoryId,
248
+ );
249
+ return (
250
+ <TabsContent
251
+ key={categoryId}
252
+ value={categoryId}
253
+ data-shortcuts-tabpanel={categoryId}
254
+ className="m-0 min-h-0 flex-1 overflow-y-auto px-4 py-2 data-[state=inactive]:hidden"
255
+ >
256
+ {categoryId === "essential" ? (
257
+ <div
258
+ data-shortcuts-content-column
259
+ className="mx-auto max-w-[400px]"
260
+ >
261
+ <h2
262
+ data-essential-shortcuts-heading
263
+ className="mb-2 text-[13px] font-semibold text-white"
264
+ >
265
+ {`${t("designEditor.keyboardShortcuts.categories.essential")} ${t("designEditor.keyboardShortcuts.title").toLocaleLowerCase()}`}
266
+ </h2>
267
+ <div className="space-y-6">
268
+ {[
269
+ DESIGN_SHORTCUTS.find((item) => item.id === "toggle-ui"),
270
+ DESIGN_SHORTCUTS.find((item) => item.id === "undo"),
271
+ DESIGN_SHORTCUTS.find((item) => item.id === "redo"),
272
+ ].map((item, index) =>
273
+ item ? (
274
+ <section
275
+ data-essential-shortcut-card
276
+ data-shortcut-id={item.id}
277
+ key={item.id}
278
+ className="relative pt-7"
279
+ >
280
+ <div className="absolute left-0 top-0">
281
+ <span className="flex size-5 items-center justify-center rounded-full bg-neutral-700 text-[10px] font-semibold text-white">
282
+ {index + 1}
283
+ </span>
284
+ </div>
285
+ <div className="mt-7 flex min-h-[58px] items-center justify-between gap-6 border-b border-neutral-700 pb-3">
286
+ <div className="min-w-0 flex-1">
287
+ <div className="text-[11px] font-medium text-neutral-100">
288
+ {t(item.labelKey)}
289
+ </div>
290
+ <p
291
+ data-essential-shortcut-description
292
+ className="mt-1 text-[10px] leading-4 text-neutral-400"
293
+ >
294
+ {t(
295
+ ESSENTIAL_DESCRIPTION_KEY_BY_SHORTCUT[
296
+ item.id as keyof typeof ESSENTIAL_DESCRIPTION_KEY_BY_SHORTCUT
297
+ ],
298
+ )}
299
+ </p>
300
+ </div>
301
+ <ShortcutBindings bindings={item.bindings} />
302
+ </div>
303
+ </section>
304
+ ) : null,
305
+ )}
306
+ </div>
307
+ </div>
308
+ ) : (
309
+ <div
310
+ data-shortcut-grid={categoryId}
311
+ data-shortcuts-content-column
312
+ className="mx-auto max-w-[400px]"
313
+ >
314
+ {designRows.map((item) => (
315
+ <div
316
+ data-shortcut-id={item.id}
317
+ key={item.id}
318
+ className="flex min-h-8 items-center gap-3 border-b border-neutral-800 py-1"
319
+ >
320
+ <span className="flex min-w-0 flex-1 items-center gap-2 truncate text-[11px] text-neutral-200">
321
+ {(() => {
322
+ const ToolIcon = TOOL_ICON_BY_SHORTCUT[item.id];
323
+ return ToolIcon ? (
324
+ <ToolIcon className="size-3.5 shrink-0 text-neutral-400" />
325
+ ) : null;
326
+ })()}
327
+ <span className="truncate">
328
+ {t(item.labelKey)}
329
+ {item.context === "screen" ? (
330
+ <span className="ml-1.5 rounded bg-neutral-700 px-1 py-0.5 text-[9px] uppercase tracking-wide text-neutral-300">
331
+ {t(
332
+ "designEditor.keyboardShortcuts.screenContext",
333
+ )}
334
+ </span>
335
+ ) : null}
336
+ </span>
337
+ </span>
338
+ <ShortcutBindings bindings={item.bindings} />
339
+ </div>
340
+ ))}
341
+ {codeRows.map((item) => (
342
+ <div
343
+ data-code-shortcut-id={item.id}
344
+ key={item.id}
345
+ className="flex min-h-8 items-center gap-3 border-b border-neutral-800 py-1"
346
+ >
347
+ <span className="min-w-0 flex-1 truncate text-[11px] text-neutral-200">
348
+ {item.title}
349
+ <span className="ml-1.5 rounded bg-blue-500/20 px-1 py-0.5 text-[9px] uppercase tracking-wide text-blue-200">
350
+ {t("designEditor.keyboardShortcuts.codeContext")}
351
+ </span>
352
+ </span>
353
+ <ShortcutBindings bindings={item.keybindings ?? []} />
354
+ </div>
355
+ ))}
356
+ </div>
357
+ )}
358
+ </TabsContent>
359
+ );
360
+ })}
361
+ </Tabs>
362
+ </section>
363
+ );
364
+ }