@jxsuite/studio 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (419) hide show
  1. package/canvas.html +21 -0
  2. package/dist/chunks/Overlay.dev-qfjzkms8.js +23 -0
  3. package/dist/chunks/{Overlay.dev-cszx7yn5.js.map → Overlay.dev-qfjzkms8.js.map} +1 -1
  4. package/dist/chunks/{adapter-z8fkmbmv.js → adapter-qfnesn5v.js} +2 -2
  5. package/dist/chunks/{cf-settings-mmyv436d.js → cf-settings-73sh5z4p.js} +3 -3
  6. package/dist/chunks/{components-0hzentwq.js → components-2bpqp4mq.js} +5 -7
  7. package/dist/chunks/{components-0hzentwq.js.map → components-2bpqp4mq.js.map} +1 -1
  8. package/dist/chunks/content-source-34gnkbnn.js +38 -0
  9. package/dist/chunks/content-source-34gnkbnn.js.map +9 -0
  10. package/dist/chunks/convert-to-repeater-t8kxre5s.js +258 -0
  11. package/dist/chunks/convert-to-repeater-t8kxre5s.js.map +10 -0
  12. package/dist/chunks/extension-sections-6mqydz35.js +301 -0
  13. package/dist/chunks/extension-sections-6mqydz35.js.map +11 -0
  14. package/dist/chunks/files-8kn321h1.js +70 -0
  15. package/dist/chunks/files-8kn321h1.js.map +9 -0
  16. package/dist/chunks/{format-host-2c188n41.js → format-host-mkwsmt8d.js} +3 -3
  17. package/dist/chunks/git-panel-tjbr191d.js +51 -0
  18. package/dist/chunks/git-panel-tjbr191d.js.map +9 -0
  19. package/dist/chunks/{index-baeg2x3y.js → index-7pxchcce.js} +4 -3
  20. package/dist/chunks/{index-baeg2x3y.js.map → index-7pxchcce.js.map} +3 -3
  21. package/dist/chunks/{jx-validate-qx8degj7.js → jx-validate-v6qgknrs.js} +3 -3
  22. package/dist/chunks/layers-panel-87pb25km.js +58 -0
  23. package/dist/chunks/layers-panel-87pb25km.js.map +9 -0
  24. package/dist/chunks/{monaco-setup-zjdp9zdw.js → monaco-setup-e0yh25j4.js} +4 -75
  25. package/dist/chunks/{monaco-setup-zjdp9zdw.js.map → monaco-setup-e0yh25j4.js.map} +3 -4
  26. package/dist/chunks/publish-panel-m0fcgdrt.js +21 -0
  27. package/dist/chunks/publish-panel-m0fcgdrt.js.map +9 -0
  28. package/dist/chunks/quick-search-rhs6cbsb.js +53 -0
  29. package/dist/chunks/quick-search-rhs6cbsb.js.map +9 -0
  30. package/dist/chunks/right-panel-zpdxd98t.js +37 -0
  31. package/dist/chunks/right-panel-zpdxd98t.js.map +9 -0
  32. package/dist/chunks/section-registry-zmckaxf4.js +30 -0
  33. package/dist/chunks/section-registry-zmckaxf4.js.map +9 -0
  34. package/dist/chunks/sp-overlay.dev-9ran0sfq.js +18 -0
  35. package/dist/chunks/sp-overlay.dev-9ran0sfq.js.map +9 -0
  36. package/dist/chunks/sp-popover.dev-rebmdncr.js +9 -0
  37. package/dist/chunks/sp-popover.dev-rebmdncr.js.map +9 -0
  38. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js +15 -0
  39. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js.map +9 -0
  40. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js +15 -0
  41. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js.map +9 -0
  42. package/dist/chunks/{sp-tray.dev-pb2fsd65.js → sp-tray.dev-ey59vfh7.js} +10 -8
  43. package/dist/chunks/{sp-tray.dev-pb2fsd65.js.map → sp-tray.dev-ey59vfh7.js.map} +2 -2
  44. package/dist/chunks/{studio-9qr6fq7p.js → studio-0rqqhrdy.js} +10 -378
  45. package/dist/chunks/{studio-9qr6fq7p.js.map → studio-0rqqhrdy.js.map} +4 -14
  46. package/dist/chunks/{studio-5x6tqqec.js → studio-2sj1jj3v.js} +10 -8
  47. package/dist/chunks/{studio-5x6tqqec.js.map → studio-2sj1jj3v.js.map} +2 -2
  48. package/dist/chunks/{studio-h3fw0wn8.js → studio-3fk1ac9z.js} +6 -4
  49. package/dist/chunks/{studio-h3fw0wn8.js.map → studio-3fk1ac9z.js.map} +2 -2
  50. package/dist/chunks/studio-41exgzhq.js +6690 -0
  51. package/dist/chunks/studio-41exgzhq.js.map +38 -0
  52. package/dist/chunks/{studio-7v2bpweq.js → studio-4mqb9a0j.js} +6 -4
  53. package/dist/chunks/{studio-7v2bpweq.js.map → studio-4mqb9a0j.js.map} +2 -2
  54. package/dist/chunks/studio-50k77cca.js +1787 -0
  55. package/dist/chunks/studio-50k77cca.js.map +18 -0
  56. package/dist/chunks/{studio-56v3zwk6.js → studio-6a0h7sne.js} +10 -8
  57. package/dist/chunks/{studio-56v3zwk6.js.map → studio-6a0h7sne.js.map} +2 -2
  58. package/dist/chunks/studio-6f0am8f2.js +53 -0
  59. package/dist/chunks/studio-6f0am8f2.js.map +10 -0
  60. package/dist/chunks/studio-6nmjc5qk.js +7002 -0
  61. package/dist/chunks/studio-6nmjc5qk.js.map +32 -0
  62. package/dist/chunks/studio-7afd21nw.js +67 -0
  63. package/dist/chunks/studio-7afd21nw.js.map +10 -0
  64. package/dist/chunks/{studio-xze4zrs1.js → studio-7bmdvzne.js} +2 -2
  65. package/dist/chunks/studio-7bmdvzne.js.map +10 -0
  66. package/dist/chunks/studio-8jpzqk5s.js +128 -0
  67. package/dist/chunks/studio-8jpzqk5s.js.map +10 -0
  68. package/dist/chunks/studio-94m77tgk.js +1482 -0
  69. package/dist/chunks/studio-94m77tgk.js.map +13 -0
  70. package/dist/chunks/studio-9je6w5sq.js +1000 -0
  71. package/dist/chunks/studio-9je6w5sq.js.map +16 -0
  72. package/dist/chunks/{studio-w7c6kvk7.js → studio-a7pzveq1.js} +2 -2
  73. package/dist/chunks/{studio-97qcgvdn.js → studio-adn0z1k3.js} +14 -8
  74. package/dist/chunks/{studio-97qcgvdn.js.map → studio-adn0z1k3.js.map} +3 -3
  75. package/dist/chunks/{studio-kr88ryr7.js → studio-bsxxx3x2.js} +17 -15
  76. package/dist/chunks/{studio-kr88ryr7.js.map → studio-bsxxx3x2.js.map} +7 -6
  77. package/dist/chunks/studio-bw5dz6km.js +608 -0
  78. package/dist/chunks/studio-bw5dz6km.js.map +21 -0
  79. package/dist/chunks/studio-c6s5getc.js +4232 -0
  80. package/dist/chunks/studio-c6s5getc.js.map +39 -0
  81. package/dist/chunks/studio-cjm7fdjf.js +451 -0
  82. package/dist/chunks/studio-cjm7fdjf.js.map +14 -0
  83. package/dist/chunks/{studio-cx91mnh8.js → studio-ckwz4d3x.js} +53 -53
  84. package/dist/chunks/{studio-cx91mnh8.js.map → studio-ckwz4d3x.js.map} +5 -5
  85. package/dist/chunks/studio-dg7zyt5x.js +28 -0
  86. package/dist/chunks/studio-dg7zyt5x.js.map +10 -0
  87. package/dist/chunks/studio-dzcyvvvz.js +166 -0
  88. package/dist/chunks/studio-dzcyvvvz.js.map +10 -0
  89. package/dist/chunks/{studio-7kqe41sa.js → studio-evkr5d3b.js} +15 -7
  90. package/dist/chunks/{studio-7kqe41sa.js.map → studio-evkr5d3b.js.map} +5 -4
  91. package/dist/chunks/studio-f12h6177.js +411 -0
  92. package/dist/chunks/studio-f12h6177.js.map +13 -0
  93. package/dist/chunks/studio-hbp1s8m9.js +1817 -0
  94. package/dist/chunks/studio-hbp1s8m9.js.map +14 -0
  95. package/dist/chunks/studio-k2sxjph5.js +15295 -0
  96. package/dist/chunks/studio-k2sxjph5.js.map +235 -0
  97. package/dist/chunks/studio-myje51t3.js +385 -0
  98. package/dist/chunks/studio-myje51t3.js.map +12 -0
  99. package/dist/chunks/studio-na6rwzhw.js +353 -0
  100. package/dist/chunks/studio-na6rwzhw.js.map +11 -0
  101. package/dist/chunks/{studio-4k0m4fmp.js → studio-p471gg5n.js} +126 -3
  102. package/dist/chunks/studio-p471gg5n.js.map +14 -0
  103. package/dist/chunks/{studio-9bjwf0d4.js → studio-rgwcpast.js} +5 -46
  104. package/dist/chunks/{studio-9bjwf0d4.js.map → studio-rgwcpast.js.map} +4 -5
  105. package/dist/chunks/{studio-hhj92gcr.js → studio-s519zc99.js} +3 -3
  106. package/dist/chunks/studio-sas7mt3k.js +75 -0
  107. package/dist/chunks/studio-sas7mt3k.js.map +10 -0
  108. package/dist/chunks/studio-t5wqwzdh.js +74 -0
  109. package/dist/chunks/studio-t5wqwzdh.js.map +10 -0
  110. package/dist/chunks/{studio-9qpspwfn.js → studio-temv5bw4.js} +5 -5
  111. package/dist/chunks/{studio-9qpspwfn.js.map → studio-temv5bw4.js.map} +1 -1
  112. package/dist/chunks/{studio-qkvt37c0.js → studio-tr20m8g8.js} +669 -18
  113. package/dist/chunks/studio-tr20m8g8.js.map +9 -0
  114. package/dist/chunks/{studio-ygzav7z5.js → studio-v96qfnww.js} +5514 -4291
  115. package/dist/chunks/studio-v96qfnww.js.map +54 -0
  116. package/dist/chunks/studio-vcetc91k.js +659 -0
  117. package/dist/chunks/studio-vcetc91k.js.map +10 -0
  118. package/dist/chunks/{studio-n9zmpxjg.js → studio-vmymjefp.js} +2972 -4937
  119. package/dist/chunks/studio-vmymjefp.js.map +98 -0
  120. package/dist/chunks/{studio-bghqfmea.js → studio-wwseqyw0.js} +2 -2
  121. package/dist/chunks/studio-x9hmy5kr.js +390 -0
  122. package/dist/chunks/studio-x9hmy5kr.js.map +10 -0
  123. package/dist/chunks/studio-xa0ce0n7.js +335 -0
  124. package/dist/chunks/studio-xa0ce0n7.js.map +10 -0
  125. package/dist/chunks/studio-xm3dbxvn.js +378 -0
  126. package/dist/chunks/studio-xm3dbxvn.js.map +19 -0
  127. package/dist/chunks/{studio-nqw433kv.js → studio-xmvbhg31.js} +2 -2
  128. package/dist/chunks/studio-zv37yrfa.js +1054 -0
  129. package/dist/chunks/studio-zv37yrfa.js.map +13 -0
  130. package/dist/chunks/{ws-client-q7hkz3j9.js → ws-client-654ts66j.js} +6 -4
  131. package/dist/chunks/ws-client-654ts66j.js.map +11 -0
  132. package/dist/iframe-entry.js +668 -230
  133. package/dist/studio.js +23711 -35185
  134. package/index.html +35 -4685
  135. package/package.json +17 -15
  136. package/src/about/about-modal.ts +42 -11
  137. package/src/browse/library-commands.ts +240 -0
  138. package/src/browse/library-layouts.ts +369 -0
  139. package/src/browse/library-model.ts +396 -0
  140. package/src/browse/library-pane.ts +1049 -0
  141. package/src/browse/library-preview.ts +252 -0
  142. package/src/browse/library-source.ts +150 -0
  143. package/src/canvas/canvas-helpers.ts +33 -9
  144. package/src/canvas/canvas-live-render.ts +45 -29
  145. package/src/canvas/canvas-patcher.ts +151 -31
  146. package/src/canvas/canvas-perf.ts +46 -7
  147. package/src/canvas/canvas-render.ts +1092 -220
  148. package/src/canvas/canvas-surface.ts +240 -0
  149. package/src/canvas/canvas-utils.ts +1166 -124
  150. package/src/canvas/iframe-drop.ts +2 -1
  151. package/src/canvas/iframe-entry.ts +241 -4
  152. package/src/canvas/iframe-host.ts +1226 -143
  153. package/src/canvas/iframe-inline-edit.ts +31 -0
  154. package/src/canvas/iframe-interaction.ts +76 -16
  155. package/src/canvas/iframe-keys.ts +107 -45
  156. package/src/canvas/iframe-overlay.ts +38 -1
  157. package/src/canvas/iframe-protocol.ts +137 -2
  158. package/src/canvas/iframe-render.ts +70 -2
  159. package/src/canvas/iframe-slash.ts +6 -0
  160. package/src/canvas/path-mapping.ts +56 -6
  161. package/src/canvas/surface-registry.ts +292 -0
  162. package/src/collab/collab-commands.ts +244 -0
  163. package/src/collab/collab-session.ts +198 -32
  164. package/src/collab/collab-state.ts +28 -2
  165. package/src/collab/presence-chips.ts +100 -9
  166. package/src/commands/active-registry.ts +42 -0
  167. package/src/commands/app-commands.ts +176 -0
  168. package/src/commands/budget.ts +158 -0
  169. package/src/commands/command-args.ts +253 -0
  170. package/src/commands/context.ts +387 -0
  171. package/src/commands/defaults.ts +580 -0
  172. package/src/commands/keymap.ts +571 -0
  173. package/src/commands/levels.ts +345 -0
  174. package/src/commands/live-context.ts +193 -0
  175. package/src/commands/reference.ts +227 -0
  176. package/src/commands/registry.ts +293 -0
  177. package/src/component-props.ts +3 -2
  178. package/src/content/draft-state.ts +106 -0
  179. package/src/content/entry-commands.ts +282 -0
  180. package/src/content/entry-editor.ts +327 -0
  181. package/src/content/entry-fields.ts +116 -0
  182. package/src/content/entry-model.ts +175 -0
  183. package/src/editor/canvas-context-menu.ts +17 -11
  184. package/src/editor/canvas-slash-bridge.ts +11 -4
  185. package/src/editor/context-menu.ts +804 -204
  186. package/src/editor/convert-to-component.ts +31 -10
  187. package/src/editor/convert-to-repeater.ts +3 -2
  188. package/src/editor/inline-edit-apply.ts +5 -5
  189. package/src/editor/inline-edit.ts +86 -50
  190. package/src/editor/insert-zone-action.ts +1 -1
  191. package/src/editor/shortcuts.ts +1043 -329
  192. package/src/editor/slash-menu.ts +1 -0
  193. package/src/files/elements.ts +166 -0
  194. package/src/files/file-ops.ts +178 -31
  195. package/src/files/files.ts +898 -268
  196. package/src/files/fs-events.ts +21 -0
  197. package/src/files/media-meta.ts +203 -0
  198. package/src/files/media-paths.ts +141 -0
  199. package/src/files/media-upload.ts +7 -3
  200. package/src/files/media-usage.ts +189 -0
  201. package/src/github/github-auth.ts +235 -33
  202. package/src/github/github-publish.ts +80 -21
  203. package/src/grid/grid-controller.ts +152 -12
  204. package/src/grid/grid-layout.ts +225 -20
  205. package/src/grid/grid-open.ts +120 -0
  206. package/src/grid/grid-panel.ts +554 -39
  207. package/src/grid/grid-source.ts +27 -1
  208. package/src/grid/redirects-grid.ts +599 -0
  209. package/src/grid/redirects.ts +398 -0
  210. package/src/grid/sources/content-source.ts +108 -2
  211. package/src/i18n/i18n-commands.ts +322 -0
  212. package/src/new-project/add-repo-modal.ts +8 -10
  213. package/src/new-project/import-tab.ts +34 -9
  214. package/src/new-project/location-fields.ts +9 -1
  215. package/src/new-project/new-project-modal.ts +270 -262
  216. package/src/packages/jxsuite-update.ts +97 -48
  217. package/src/packages/pull-package-sync.ts +18 -9
  218. package/src/page-params.ts +2 -1
  219. package/src/panels/activity-bar.ts +152 -91
  220. package/src/panels/activity-panel.ts +481 -0
  221. package/src/panels/ai-chat/chat-markdown.ts +8 -3
  222. package/src/panels/ai-chat/chat-view.ts +245 -18
  223. package/src/panels/ai-chat/composer.ts +31 -5
  224. package/src/panels/ai-chat/sessions-view.ts +17 -10
  225. package/src/panels/ai-panel.ts +279 -120
  226. package/src/panels/block-action-bar.ts +1115 -224
  227. package/src/panels/bottom-dock.ts +337 -0
  228. package/src/panels/canvas-dnd-bridge.ts +37 -8
  229. package/src/panels/chat-panel.ts +29 -12
  230. package/src/panels/data-explorer.ts +254 -112
  231. package/src/panels/data-grid.ts +10 -4
  232. package/src/panels/dnd.ts +28 -51
  233. package/src/panels/editors.ts +434 -107
  234. package/src/panels/elements-panel.ts +66 -2
  235. package/src/panels/empty-state.ts +112 -0
  236. package/src/panels/events-panel.ts +596 -82
  237. package/src/panels/formula-workspace.ts +408 -218
  238. package/src/panels/frontmatter-fields.ts +51 -10
  239. package/src/panels/frontmatter-panel.ts +357 -79
  240. package/src/panels/git-panel.ts +375 -164
  241. package/src/panels/head-panel.ts +791 -85
  242. package/src/panels/i18n-panel.ts +592 -0
  243. package/src/panels/imports-panel.ts +95 -85
  244. package/src/panels/jump-bar.ts +584 -0
  245. package/src/panels/layers-panel.ts +1106 -328
  246. package/src/panels/left-panel.ts +109 -204
  247. package/src/panels/navigator-panels.ts +131 -0
  248. package/src/panels/overlays.ts +7 -2
  249. package/src/panels/pane-context.ts +1477 -0
  250. package/src/panels/pane-grid.ts +406 -0
  251. package/src/panels/panel-registry.ts +340 -0
  252. package/src/panels/panel-scheduler.ts +75 -0
  253. package/src/panels/problems-panel.ts +234 -0
  254. package/src/panels/properties-panel.ts +946 -1139
  255. package/src/panels/provenance.ts +258 -0
  256. package/src/panels/quick-search.ts +791 -122
  257. package/src/panels/right-panel.ts +228 -102
  258. package/src/panels/seo-modal.ts +395 -0
  259. package/src/panels/settings-pane.ts +203 -0
  260. package/src/panels/signals-panel.ts +354 -137
  261. package/src/panels/statement-editor.ts +41 -25
  262. package/src/panels/statusbar.ts +342 -106
  263. package/src/panels/style-panel.ts +974 -463
  264. package/src/panels/style-utils.ts +3 -3
  265. package/src/panels/stylebook-doc.ts +4 -2
  266. package/src/panels/stylebook-layers-panel.ts +3 -2
  267. package/src/panels/stylebook-panel.ts +51 -26
  268. package/src/panels/tab-strip.ts +1058 -78
  269. package/src/panels/target-line.ts +274 -0
  270. package/src/panels/toolbar.ts +631 -484
  271. package/src/panels/welcome-screen.ts +301 -198
  272. package/src/platform-errors.ts +26 -3
  273. package/src/platform.ts +96 -2
  274. package/src/platforms/cloud.ts +71 -8
  275. package/src/platforms/devserver.ts +92 -29
  276. package/src/publish/deploy-checklist.ts +383 -0
  277. package/src/publish/pages-service.ts +10 -1
  278. package/src/publish/publish-commands.ts +218 -0
  279. package/src/publish/publish-panel.ts +110 -16
  280. package/src/recent-projects.ts +3 -2
  281. package/src/services/a11y-report.ts +505 -0
  282. package/src/services/ai-models.ts +75 -2
  283. package/src/services/ai-project-tools.ts +29 -10
  284. package/src/services/ai-system-prompt.ts +53 -14
  285. package/src/services/ai-tools.ts +39 -3
  286. package/src/services/ai-writes.ts +154 -0
  287. package/src/services/announce.ts +97 -0
  288. package/src/services/automation.ts +424 -175
  289. package/src/services/cem-export.ts +9 -4
  290. package/src/services/clock.ts +61 -0
  291. package/src/services/document-assistant.ts +36 -8
  292. package/src/services/idle.ts +179 -0
  293. package/src/services/import-client.ts +26 -0
  294. package/src/services/monaco-buffer.ts +627 -0
  295. package/src/services/monaco-lazy.ts +36 -0
  296. package/src/services/notify.ts +271 -0
  297. package/src/services/profile.ts +155 -0
  298. package/src/services/references.ts +0 -0
  299. package/src/services/token-lint.ts +3 -2
  300. package/src/services/tool-executor.ts +53 -7
  301. package/src/services/trusted-types.ts +123 -0
  302. package/src/settings/contexts-section.ts +427 -0
  303. package/src/settings/contributed-section.ts +149 -11
  304. package/src/settings/css-vars-editor.ts +306 -349
  305. package/src/settings/defs-editor.ts +93 -34
  306. package/src/settings/dependencies-editor.ts +7 -7
  307. package/src/settings/extension-sections.ts +37 -5
  308. package/src/settings/general-settings.ts +204 -121
  309. package/src/settings/locales-section.ts +347 -0
  310. package/src/settings/preferences-accounts.ts +140 -0
  311. package/src/settings/preferences-dialog.ts +620 -0
  312. package/src/settings/preferences-keymap.ts +270 -0
  313. package/src/settings/project-sections.ts +260 -0
  314. package/src/settings/section-registry.ts +169 -0
  315. package/src/settings/settings-document.ts +385 -0
  316. package/src/shell.ts +1549 -0
  317. package/src/site-context.ts +45 -20
  318. package/src/state.ts +14 -275
  319. package/src/store.ts +88 -43
  320. package/src/studio.ts +790 -346
  321. package/src/style/live-preview.ts +36 -0
  322. package/src/style/project-styles.ts +349 -0
  323. package/src/style/token-ref.ts +123 -0
  324. package/src/tabs/patch-ops.ts +16 -4
  325. package/src/tabs/project-config.ts +416 -0
  326. package/src/tabs/selection.ts +323 -0
  327. package/src/tabs/tab.ts +182 -47
  328. package/src/tabs/transact.ts +209 -28
  329. package/src/types.ts +68 -13
  330. package/src/ui/ai-credentials-form.ts +39 -35
  331. package/src/ui/ai-managed-connect.ts +113 -0
  332. package/src/ui/dynamic-slot.ts +224 -81
  333. package/src/ui/expression-editor.ts +71 -63
  334. package/src/ui/field-input.ts +4 -6
  335. package/src/ui/field-row.ts +215 -38
  336. package/src/ui/form-controls.ts +194 -7
  337. package/src/ui/layers.ts +492 -69
  338. package/src/ui/media-picker.ts +115 -25
  339. package/src/ui/panel-resize.ts +109 -91
  340. package/src/ui/progress-modal.ts +131 -50
  341. package/src/ui/regions.ts +462 -0
  342. package/src/ui/schema-form.ts +229 -45
  343. package/src/ui/spectrum.ts +24 -8
  344. package/src/ui/timing.ts +49 -0
  345. package/src/ui/value-selector.ts +20 -0
  346. package/src/ui/value-source.ts +348 -0
  347. package/src/ui/virtual-window.ts +400 -0
  348. package/src/utils/edit-display.ts +3 -2
  349. package/src/utils/inherited-style.ts +67 -37
  350. package/src/view.ts +64 -78
  351. package/src/workspace/pane-derive.ts +1258 -0
  352. package/src/workspace/session.ts +317 -0
  353. package/src/workspace/workspace.ts +1345 -23
  354. package/styles/canvas.css +653 -0
  355. package/styles/forced-colors.css +106 -0
  356. package/styles/inspector.css +1454 -0
  357. package/styles/overlays.css +2704 -0
  358. package/styles/panels.css +1701 -0
  359. package/styles/shell.css +1423 -0
  360. package/styles/tokens.css +151 -0
  361. package/dist/chunks/Overlay.dev-cszx7yn5.js +0 -22
  362. package/dist/chunks/extension-sections-qct0gt8f.js +0 -531
  363. package/dist/chunks/extension-sections-qct0gt8f.js.map +0 -12
  364. package/dist/chunks/files-02zn5y24.js +0 -50
  365. package/dist/chunks/files-02zn5y24.js.map +0 -9
  366. package/dist/chunks/git-panel-mbb0y0hz.js +0 -28
  367. package/dist/chunks/git-panel-mbb0y0hz.js.map +0 -9
  368. package/dist/chunks/layers-panel-9qekmqk1.js +0 -26
  369. package/dist/chunks/layers-panel-9qekmqk1.js.map +0 -9
  370. package/dist/chunks/settings-modal-gj0hrah4.js +0 -29
  371. package/dist/chunks/settings-modal-gj0hrah4.js.map +0 -9
  372. package/dist/chunks/sp-overlay.dev-earn5as8.js +0 -17
  373. package/dist/chunks/sp-overlay.dev-earn5as8.js.map +0 -9
  374. package/dist/chunks/sp-popover.dev-frxtfejn.js +0 -8
  375. package/dist/chunks/sp-popover.dev-frxtfejn.js.map +0 -9
  376. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js +0 -14
  377. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js.map +0 -9
  378. package/dist/chunks/sp-tooltip.dev-a9fywesm.js +0 -21
  379. package/dist/chunks/sp-tooltip.dev-a9fywesm.js.map +0 -10
  380. package/dist/chunks/studio-4k0m4fmp.js.map +0 -13
  381. package/dist/chunks/studio-568nx4t5.js +0 -469
  382. package/dist/chunks/studio-568nx4t5.js.map +0 -14
  383. package/dist/chunks/studio-9smfzqhc.js +0 -19
  384. package/dist/chunks/studio-9smfzqhc.js.map +0 -10
  385. package/dist/chunks/studio-asvf0j9e.js +0 -429
  386. package/dist/chunks/studio-asvf0j9e.js.map +0 -13
  387. package/dist/chunks/studio-d77qm3pk.js +0 -3427
  388. package/dist/chunks/studio-d77qm3pk.js.map +0 -26
  389. package/dist/chunks/studio-j6gqyjf4.js +0 -1854
  390. package/dist/chunks/studio-j6gqyjf4.js.map +0 -18
  391. package/dist/chunks/studio-n9zmpxjg.js.map +0 -115
  392. package/dist/chunks/studio-qkvt37c0.js.map +0 -9
  393. package/dist/chunks/studio-rq0vxb19.js +0 -3744
  394. package/dist/chunks/studio-rq0vxb19.js.map +0 -22
  395. package/dist/chunks/studio-wd6ewdvd.js +0 -853
  396. package/dist/chunks/studio-wd6ewdvd.js.map +0 -12
  397. package/dist/chunks/studio-xq0t7n5t.js +0 -140
  398. package/dist/chunks/studio-xq0t7n5t.js.map +0 -10
  399. package/dist/chunks/studio-xze4zrs1.js.map +0 -10
  400. package/dist/chunks/studio-ygzav7z5.js.map +0 -46
  401. package/dist/chunks/studio-zmy7241e.js +0 -399
  402. package/dist/chunks/studio-zmy7241e.js.map +0 -12
  403. package/dist/chunks/ws-client-q7hkz3j9.js.map +0 -11
  404. package/src/browse/browse-modal.ts +0 -69
  405. package/src/browse/browse.ts +0 -904
  406. package/src/canvas/canvas-diff.ts +0 -216
  407. package/src/canvas/nested-site-style.ts +0 -58
  408. package/src/new-project/design-fields.ts +0 -264
  409. package/src/new-project/templates.ts +0 -61
  410. package/src/panels/tab-bar.ts +0 -548
  411. package/src/settings/settings-modal.ts +0 -203
  412. /package/dist/chunks/{adapter-z8fkmbmv.js.map → adapter-qfnesn5v.js.map} +0 -0
  413. /package/dist/chunks/{cf-settings-mmyv436d.js.map → cf-settings-73sh5z4p.js.map} +0 -0
  414. /package/dist/chunks/{format-host-2c188n41.js.map → format-host-mkwsmt8d.js.map} +0 -0
  415. /package/dist/chunks/{jx-validate-qx8degj7.js.map → jx-validate-v6qgknrs.js.map} +0 -0
  416. /package/dist/chunks/{studio-w7c6kvk7.js.map → studio-a7pzveq1.js.map} +0 -0
  417. /package/dist/chunks/{studio-hhj92gcr.js.map → studio-s519zc99.js.map} +0 -0
  418. /package/dist/chunks/{studio-bghqfmea.js.map → studio-wwseqyw0.js.map} +0 -0
  419. /package/dist/chunks/{studio-nqw433kv.js.map → studio-xmvbhg31.js.map} +0 -0
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Command-args.ts — loud coercion for the arguments a command record declares.
3
+ *
4
+ * Every scriptable command carries an `args` JSON Schema (spec §13.1, plan §13.3): the palette
5
+ * prompts from it, the AI tool's parameters are it, and `scripts/check-shot-contract.ts` validates
6
+ * every manifest step against it in the `checks` job. This module is the RUNTIME half of that same
7
+ * declaration — the four lines inside `run` that turn `Record<string, unknown>` into the typed
8
+ * value the implementation wants.
9
+ *
10
+ * Two rules, and they are the reason this is a module rather than twelve copies of `String(x)`:
11
+ *
12
+ * 1. **Reject loudly, never clamp.** An unknown panel id, a mode the tab cannot enter, a selection
13
+ * path that is not in the document — each throws. A no-op would photograph the previous state
14
+ * and a docs build would accept it; a clamp would photograph a state the caller did not ask for.
15
+ * Plan §13.4 makes this a tested property.
16
+ * 2. **The failure names the fix.** `{@link enumArg}` prints the declared values, which is what makes
17
+ * §13.5's headline failure — _manifest shot "properties-bar" names panel "head"; the registry
18
+ * declares "page"_ — a sentence a reader can act on rather than a stack trace. Nothing else in
19
+ * this file matters as much as that message.
20
+ *
21
+ * `RangeError` (not a bespoke class) because `services/profile.ts` already throws it for exactly
22
+ * this — an id outside a declared set — and one refusal shape is easier to catch than two.
23
+ */
24
+
25
+ /** Arguments as they arrive from a palette prompt, a manifest step or an AI tool call. */
26
+ export type CommandArgValues = Record<string, unknown>;
27
+
28
+ /** `command "<id>" argument "<key>": <problem>` — the one sentence shape every failure here uses. */
29
+ function refuse(commandId: string, key: string, problem: string): RangeError {
30
+ return new RangeError(`command "${commandId}" argument "${key}": ${problem}`);
31
+ }
32
+
33
+ /** How a received value is quoted back to the caller. Strings keep their quotes; nothing is elided. */
34
+ function describe(value: unknown): string {
35
+ if (value === undefined) {
36
+ return "missing";
37
+ }
38
+ return typeof value === "string" ? `"${value}"` : `${typeof value} ${JSON.stringify(value)}`;
39
+ }
40
+
41
+ /** A required string argument. */
42
+ export function stringArg(commandId: string, args: CommandArgValues, key: string): string {
43
+ const value = args[key];
44
+ if (typeof value !== "string" || value === "") {
45
+ throw refuse(commandId, key, `expected a non-empty string, got ${describe(value)}`);
46
+ }
47
+ return value;
48
+ }
49
+
50
+ /** An optional string argument — `undefined` when absent, never coerced from another type. */
51
+ export function optionalStringArg(
52
+ commandId: string,
53
+ args: CommandArgValues,
54
+ key: string,
55
+ ): string | undefined {
56
+ if (args[key] === undefined) {
57
+ return undefined;
58
+ }
59
+ return stringArg(commandId, args, key);
60
+ }
61
+
62
+ /** A required finite number argument. `NaN` and `Infinity` are refusals, not values. */
63
+ export function numberArg(commandId: string, args: CommandArgValues, key: string): number {
64
+ const value = args[key];
65
+ if (typeof value !== "number" || !Number.isFinite(value)) {
66
+ throw refuse(commandId, key, `expected a finite number, got ${describe(value)}`);
67
+ }
68
+ return value;
69
+ }
70
+
71
+ /**
72
+ * A required number argument inside a closed interval.
73
+ *
74
+ * REJECTS out of range rather than clamping, which is the difference between a shot that fails and
75
+ * a shot that photographs 500% when it asked for 1000% — the second is a wrong picture the docs
76
+ * build accepts. The interactive controls still clamp: dragging a slider past its end is a gesture
77
+ * with an obvious meaning, whereas a caller naming a number outside the range is stating something
78
+ * untrue about the app.
79
+ */
80
+ export function boundedNumberArg(
81
+ commandId: string,
82
+ args: CommandArgValues,
83
+ key: string,
84
+ minimum: number,
85
+ maximum: number,
86
+ ): number {
87
+ const value = numberArg(commandId, args, key);
88
+ if (value < minimum || value > maximum) {
89
+ throw refuse(commandId, key, `${value} is outside the supported range ${minimum}–${maximum}`);
90
+ }
91
+ return value;
92
+ }
93
+
94
+ /** A required boolean argument. A setter's whole point is that `false` is a value, so no default. */
95
+ export function booleanArg(commandId: string, args: CommandArgValues, key: string): boolean {
96
+ const value = args[key];
97
+ if (typeof value !== "boolean") {
98
+ throw refuse(commandId, key, `expected a boolean, got ${describe(value)}`);
99
+ }
100
+ return value;
101
+ }
102
+
103
+ /**
104
+ * A required argument drawn from a declared set.
105
+ *
106
+ * The `declared:` clause is the point — see the module header. It is printed in full rather than
107
+ * truncated: these sets are panel ids and tab ids, never long enough to be a wall, and a reader
108
+ * comparing a stale id against the current list needs the whole list.
109
+ */
110
+ export function enumArg<T extends string>(
111
+ commandId: string,
112
+ args: CommandArgValues,
113
+ key: string,
114
+ declared: readonly T[],
115
+ ): T {
116
+ const value = args[key];
117
+ if (typeof value !== "string" || !(declared as readonly string[]).includes(value)) {
118
+ throw refuse(
119
+ commandId,
120
+ key,
121
+ `${describe(value)} is not declared — declared: ${declared.join(", ")}`,
122
+ );
123
+ }
124
+ return value as T;
125
+ }
126
+
127
+ /**
128
+ * A document path (`JxPath`) argument: an array of strings and numbers.
129
+ *
130
+ * The empty array is the document root and therefore VALID — `selection.set { path: [] }` is how a
131
+ * shot selects the root element, and treating `[]` as "no path" would silently deselect instead.
132
+ */
133
+ export function pathArg(
134
+ commandId: string,
135
+ args: CommandArgValues,
136
+ key: string,
137
+ ): (string | number)[] {
138
+ const value = args[key];
139
+ if (
140
+ !Array.isArray(value) ||
141
+ !value.every((s) => typeof s === "string" || typeof s === "number")
142
+ ) {
143
+ throw refuse(
144
+ commandId,
145
+ key,
146
+ `expected an array of path segments (strings or numbers), got ${describe(value)}`,
147
+ );
148
+ }
149
+ return value as (string | number)[];
150
+ }
151
+
152
+ /** A path argument that may be `null` — "select nothing" is a state a user can be in. */
153
+ export function nullablePathArg(
154
+ commandId: string,
155
+ args: CommandArgValues,
156
+ key: string,
157
+ ): (string | number)[] | null {
158
+ if (args[key] === null) {
159
+ return null;
160
+ }
161
+ return pathArg(commandId, args, key);
162
+ }
163
+
164
+ /**
165
+ * A LIST of document paths — the whole selection set (§6.5).
166
+ *
167
+ * Every element is validated as a path, so a caller that passes one bare path by mistake
168
+ * (`["children", 0]` rather than `[["children", 0]]`) is refused by name instead of selecting two
169
+ * nodes called "children" and "0". `[]` is legal and means "select nothing", which is the same
170
+ * thing `selection.set { path: null }` means.
171
+ */
172
+ export function pathListArg(
173
+ commandId: string,
174
+ args: CommandArgValues,
175
+ key: string,
176
+ ): (string | number)[][] {
177
+ const value = args[key];
178
+ if (!Array.isArray(value)) {
179
+ throw refuse(commandId, key, `expected an array of document paths, got ${describe(value)}`);
180
+ }
181
+ return value.map((entry, i) => {
182
+ if (
183
+ !Array.isArray(entry) ||
184
+ !entry.every((s) => typeof s === "string" || typeof s === "number")
185
+ ) {
186
+ throw refuse(
187
+ commandId,
188
+ key,
189
+ `entry ${i} is not a document path — expected an array of segments, got ${describe(entry)}`,
190
+ );
191
+ }
192
+ return entry as (string | number)[];
193
+ });
194
+ }
195
+
196
+ // ─── Schema fragments ─────────────────────────────────────────────────────────
197
+ // The declarations above are enforced at RUN time; these are the same facts in the form the palette
198
+ // Prompt, the AI tool's parameter list and Lane 1's static check read. Keeping the pair adjacent is
199
+ // Deliberate: a schema that disagrees with its coercion is the defect this module exists to prevent.
200
+
201
+ /** `{ type: "object", properties, required, additionalProperties: false }` in one call. */
202
+ export function argsSchema(
203
+ properties: Record<string, object>,
204
+ required: readonly string[] = Object.keys(properties),
205
+ ): object {
206
+ return {
207
+ additionalProperties: false,
208
+ properties,
209
+ required: [...required],
210
+ type: "object",
211
+ };
212
+ }
213
+
214
+ /** A string property constrained to a declared set, with the human sentence the palette shows. */
215
+ export function enumProperty(declared: readonly string[], description: string): object {
216
+ return { description, enum: [...declared], type: "string" };
217
+ }
218
+
219
+ /** A number property, optionally bounded — the bounds a control already enforces, written down. */
220
+ export function numberProperty(
221
+ description: string,
222
+ bounds: { minimum?: number; maximum?: number } = {},
223
+ ): object {
224
+ return { description, type: "number", ...bounds };
225
+ }
226
+
227
+ /** A boolean property. Named `open`/`enabled`/… by the caller; the description carries the sense. */
228
+ export function booleanProperty(description: string): object {
229
+ return { description, type: "boolean" };
230
+ }
231
+
232
+ /** A `JxPath` property — an array of string keys and numeric indexes. */
233
+ export function pathProperty(description: string, nullable = false): object {
234
+ const path = {
235
+ items: { type: ["string", "number"] },
236
+ type: "array",
237
+ };
238
+ return nullable ? { description, oneOf: [path, { type: "null" }] } : { description, ...path };
239
+ }
240
+
241
+ /** A property holding a LIST of `JxPath`s — an array of arrays of segments. */
242
+ export function pathListProperty(description: string): object {
243
+ return {
244
+ description,
245
+ items: { items: { type: ["string", "number"] }, type: "array" },
246
+ type: "array",
247
+ };
248
+ }
249
+
250
+ /** A free-form string property. */
251
+ export function stringProperty(description: string): object {
252
+ return { description, type: "string" };
253
+ }
@@ -0,0 +1,387 @@
1
+ /**
2
+ * Context.ts — the record every `when` / `enablement` predicate closes over.
3
+ *
4
+ * One flat-ish record of the facts a command can be gated on (UX-REDESIGN-PLAN §5.2). Predicates
5
+ * are plain closures over this record — the shape `services/gated-registry.ts` already ships for
6
+ * the AI's tools — not a serialisable string DSL. §13 rejects the DSL explicitly: a `"project.open
7
+ * && editor.kind == 'canvas'"` grammar needs a tokenizer, a parser and a reactive evaluator to buy
8
+ * serialisability nothing in Studio consumes.
9
+ *
10
+ * This module owns the SHAPE and a pure builder. It deliberately imports nothing from the state
11
+ * modules: the registry takes a `getContext()` thunk, so wiring the live sources (the reactive
12
+ * `shell` record, `workspace`, `activeTab`) is a next-wave change to one function, and every test
13
+ * here builds its own context with {@link makeContext} instead of standing up the app.
14
+ *
15
+ * `capability.*` mirrors the Platform Abstraction Layer so cloud / desktop / dev-server differences
16
+ * stop being `if (platform.x)` scattered across templates and become one `when` clause per
17
+ * command.
18
+ *
19
+ * {@link keyScopeStack} lives here for the same reason: it is a pure function of this record, so
20
+ * the keyboard's "where am I" question is answered by the same facts every `when` predicate reads,
21
+ * rather than by a second set of `if (canvasMode === …)` branches in the listener.
22
+ */
23
+
24
+ import type { KeyScope } from "./levels";
25
+ import type { JxPath } from "../state";
26
+
27
+ /** PAL-derived capability keys. One `when` clause replaces a scattered `if (platform.x)`. */
28
+ export const CAPABILITIES = [
29
+ "gitClone",
30
+ "importSite",
31
+ "openProjectInNewWindow",
32
+ "dataRows",
33
+ "windowControls",
34
+ "findReferences",
35
+ ] as const;
36
+
37
+ export type Capability = (typeof CAPABILITIES)[number];
38
+
39
+ /** Which shell region owns focus. Drives the keyboard scope stack and F6 region cycling. */
40
+ export type FocusRegion =
41
+ | "rail"
42
+ | "navigator"
43
+ | "pane"
44
+ | "inspector"
45
+ | "dock"
46
+ | "status"
47
+ | "palette";
48
+
49
+ /** The editor kind the focused pane is rendering. */
50
+ export type EditorKind =
51
+ | "canvas"
52
+ | "grid"
53
+ | "code"
54
+ | "diff"
55
+ | "library"
56
+ | "config"
57
+ | "entry"
58
+ | "none";
59
+
60
+ /** The Canvas view axis — one control, three values (§4.2). */
61
+ export type CanvasView = "edit" | "design" | "preview";
62
+
63
+ /*
64
+ * ─── Modes → the two axes ────────────────────────────────────────────────────
65
+ *
66
+ * `canvasMode` is ONE string conflating two orthogonal questions: WHICH EDITOR is open, and (for
67
+ * the Canvas editor only) WHICH VIEW of it. Both maps live here, beside the types they produce and
68
+ * beside `keyScopeStack`, because they are the same fact and there must be exactly one place to add
69
+ * an entry to.
70
+ *
71
+ * They used to be two copies — one in `tabs/tab.ts` driving the pane model, one in
72
+ * `commands/live-context.ts` driving every `when` predicate and the keyboard — each falling through
73
+ * to its own `?? "canvas"` default. Neither learned about `settings` when Project Settings became a
74
+ * document, so a JSON configuration object resolved into the CANVAS key scope: ⌘V over Project
75
+ * Settings ran `edit.paste`, which inserted an element node at the root of `project.json`, through
76
+ * the transaction log, and saved it.
77
+ */
78
+
79
+ /** The `canvasMode` strings, mapped onto the editor each one actually names. */
80
+ const EDITOR_KIND_BY_MODE: Readonly<Record<string, EditorKind>> = {
81
+ design: "canvas",
82
+ edit: "canvas",
83
+ /* The Entry editor — `content/entry-editor.ts`'s ENTRY_MODE. A form over ONE content entry's
84
+ frontmatter, typed by its collection's schema; the same document the canvas edits the body of,
85
+ which is why it is a mode of that tab rather than a tab of its own. */
86
+ entry: "entry",
87
+ "git-diff": "diff",
88
+ grid: "grid",
89
+ manage: "library",
90
+ preview: "canvas",
91
+ /* Project Settings — `settings/settings-document.ts`'s SETTINGS_MODE. A form over `project.json`,
92
+ which is a configuration object and not a document tree; `stylebook` (Project Styles) is the
93
+ second editor over that same document and answers `config` for the same reason. */
94
+ settings: "config",
95
+ source: "code",
96
+ stylebook: "config",
97
+ };
98
+
99
+ /**
100
+ * The editor kind a mode string names.
101
+ *
102
+ * An unknown mode reads as Canvas, and that default is only for modes STUDIO DOES NOT OWN: a format
103
+ * declares its own list (`format.studio.modes`) and every mode declared that way is a view of the
104
+ * artboard. A mode Studio itself introduces is not covered by it and belongs in the map above —
105
+ * which is what `settings` was not.
106
+ */
107
+ /**
108
+ * Human names for the editor kinds, so no surface prints an enum at a reader — and so two surfaces
109
+ * cannot print DIFFERENT words for the same kind. The status bar and the pane context bar each had
110
+ * their own copy, and they disagreed: one said "Stylebook", the wire value, where the other said
111
+ * "Project Styles". §16.2 exists to stop the shell contradicting itself about what is on screen.
112
+ */
113
+ export const EDITOR_KIND_LABELS: Readonly<Record<EditorKind, string>> = {
114
+ canvas: "Canvas",
115
+ code: "Code",
116
+ config: "Project Styles",
117
+ diff: "Diff",
118
+ entry: "Entry",
119
+ grid: "Grid",
120
+ library: "Library",
121
+ none: "None",
122
+ };
123
+
124
+ export function editorKindForMode(mode: string): EditorKind {
125
+ return EDITOR_KIND_BY_MODE[mode] ?? "canvas";
126
+ }
127
+
128
+ /** Only the three canvas modes carry a view. */
129
+ const CANVAS_VIEW_BY_MODE: Readonly<Record<string, CanvasView>> = {
130
+ design: "design",
131
+ edit: "edit",
132
+ preview: "preview",
133
+ };
134
+
135
+ /**
136
+ * The Canvas view a mode names — the neutral "design" for a mode that has none.
137
+ *
138
+ * Meaningful only where {@link editorKindForMode} answers `"canvas"`; every reader pairs the two.
139
+ */
140
+ export function canvasViewForMode(mode: string): CanvasView {
141
+ return CANVAS_VIEW_BY_MODE[mode] ?? "design";
142
+ }
143
+
144
+ /** Facts a command may be gated on. Every field is a plain value; predicates read, never write. */
145
+ export interface CommandContext {
146
+ project: {
147
+ open: boolean;
148
+ isSite: boolean;
149
+ isRepo: boolean;
150
+ /** The project declares two or more locales — the gate on every `i18n.*` verb and surface. */
151
+ isMultilingual: boolean;
152
+ };
153
+ git: {
154
+ ahead: number;
155
+ behind: number;
156
+ dirtyCount: number;
157
+ };
158
+ document: {
159
+ open: boolean;
160
+ dirty: boolean;
161
+ /** Format class of the open document — "json", "md", "css", … */
162
+ mode: string;
163
+ canUndo: boolean;
164
+ canRedo: boolean;
165
+ };
166
+ editor: {
167
+ kind: EditorKind;
168
+ };
169
+ canvas: {
170
+ view: CanvasView;
171
+ };
172
+ pane: {
173
+ count: number;
174
+ derived: boolean;
175
+ };
176
+ selection: {
177
+ count: number;
178
+ /**
179
+ * The selected document paths, in selection order — `[]` when nothing is selected (§6.5).
180
+ *
181
+ * The context is what `probe.state()` answers with, so this is how a script, a screenshot step
182
+ * or the assistant READS a multi-selection back. `count` is its length; the last entry is the
183
+ * primary, which every other fact in this group describes.
184
+ */
185
+ paths: JxPath[];
186
+ /** Tag or node kind of the primary selection, "" when nothing is selected. */
187
+ kind: string;
188
+ isRoot: boolean;
189
+ isComponentInstance: boolean;
190
+ isLayoutNode: boolean;
191
+ /**
192
+ * Whether the selection IS a repeater — an `$prototype: "Array"` pseudo-element.
193
+ *
194
+ * A fact rather than a verb's precondition, in the idiom of `isComponentInstance`: a repeater
195
+ * has no child list (its content is the single `map` template), so several structural verbs
196
+ * mean nothing on one, and each of them used to re-derive it from the node it could not see.
197
+ */
198
+ isRepeater: boolean;
199
+ };
200
+ /**
201
+ * Whether a live text caret owns the keyboard. Derived host-side in `iframe-host.ts` from the
202
+ * editStart / selectionChanged / editEnd messages the canvas bridge already sends — this is the
203
+ * fact that stops ⌘C/⌘X/⌘V being stolen from a writer mid-sentence.
204
+ */
205
+ caret: {
206
+ active: boolean;
207
+ /**
208
+ * Whether that caret is the CANVAS's, rather than a parent-realm text field's.
209
+ *
210
+ * `active` folds both together on purpose: for {@link keyScopeStack} they are the same fact —
211
+ * something is being typed into, so element-level chords must not fire. For a record they are
212
+ * not. `format.bold` acts on a run of text inside the selected node and is meaningless while
213
+ * the caret is in the Inspector's href field or the link popover's own URL box; a record gated
214
+ * on `active` alone would fire there, and `mod+k` would re-mount the popover the author is
215
+ * typing into.
216
+ *
217
+ * Sourced from the bridge alone (`isCaretActive()`), never from `isTextEntryFocused()`.
218
+ */
219
+ inCanvas: boolean;
220
+ };
221
+ focus: {
222
+ region: FocusRegion;
223
+ };
224
+ modal: {
225
+ open: boolean;
226
+ };
227
+ collab: {
228
+ attached: boolean;
229
+ readOnly: boolean;
230
+ sourceCanonical: boolean;
231
+ };
232
+ ai: {
233
+ configured: boolean;
234
+ streaming: boolean;
235
+ };
236
+ capability: Record<Capability, boolean>;
237
+ }
238
+
239
+ /** A partial context, one group at a time — what tests and call sites actually write. */
240
+ export type CommandContextPatch = {
241
+ [K in keyof CommandContext]?: Partial<CommandContext[K]>;
242
+ };
243
+
244
+ /**
245
+ * The zero context: no project, no document, no selection, no capabilities.
246
+ *
247
+ * This is the honest cold-start state, which makes it the right default for the `when` predicates
248
+ * to be tested against — a command that is enabled here has said so deliberately.
249
+ */
250
+ /**
251
+ * The three selection predicates every node-editing verb is gated on.
252
+ *
253
+ * Here rather than in one verb module because two of them define records: `editor/shortcuts.ts`'s
254
+ * chord table and `editor/context-menu.ts`'s element family. Importing one from the other made a
255
+ * cycle (`shortcuts` already imports `copyNode`/`cutNode` from `context-menu`), and re-declaring
256
+ * them is how "an element selection" comes to mean two different things in two menus.
257
+ */
258
+
259
+ /** The editor showing a document TREE. Every verb that addresses a node needs this. */
260
+ export const inCanvas = (ctx: CommandContext) => ctx.editor.kind === "canvas";
261
+
262
+ /** Something is selected — including the document root, which is a selection of one. */
263
+ export const hasSelection = (ctx: CommandContext) => inCanvas(ctx) && ctx.selection.count > 0;
264
+
265
+ /** A selection that is not the document element, i.e. a node with a parent to act relative to. */
266
+ export const hasElementSelection = (ctx: CommandContext) =>
267
+ hasSelection(ctx) && !ctx.selection.isRoot;
268
+
269
+ export function emptyContext(): CommandContext {
270
+ return {
271
+ project: { open: false, isSite: false, isRepo: false, isMultilingual: false },
272
+ git: { ahead: 0, behind: 0, dirtyCount: 0 },
273
+ document: { open: false, dirty: false, mode: "", canUndo: false, canRedo: false },
274
+ editor: { kind: "none" },
275
+ canvas: { view: "design" },
276
+ pane: { count: 1, derived: false },
277
+ selection: {
278
+ count: 0,
279
+ paths: [],
280
+ kind: "",
281
+ isRoot: false,
282
+ isComponentInstance: false,
283
+ isLayoutNode: false,
284
+ isRepeater: false,
285
+ },
286
+ caret: { active: false, inCanvas: false },
287
+ focus: { region: "pane" },
288
+ modal: { open: false },
289
+ collab: { attached: false, readOnly: false, sourceCanonical: false },
290
+ ai: { configured: false, streaming: false },
291
+ capability: {
292
+ gitClone: false,
293
+ importSite: false,
294
+ openProjectInNewWindow: false,
295
+ dataRows: false,
296
+ windowControls: false,
297
+ findReferences: false,
298
+ },
299
+ };
300
+ }
301
+
302
+ /**
303
+ * Build a context by overriding groups of {@link emptyContext}.
304
+ *
305
+ * Group-level merge, not a deep one: `makeContext({ selection: { count: 1 } })` keeps the other
306
+ * selection fields at their empty defaults. One level is all the record has, and a general deep
307
+ * merge would be a second thing to get right.
308
+ */
309
+ export function makeContext(patch: CommandContextPatch = {}): CommandContext {
310
+ const base = emptyContext();
311
+ for (const key of Object.keys(patch) as (keyof CommandContext)[]) {
312
+ const group = patch[key];
313
+ if (group) {
314
+ Object.assign(base[key], group);
315
+ }
316
+ }
317
+ return base;
318
+ }
319
+
320
+ /**
321
+ * An overlay owns the keyboard outright.
322
+ *
323
+ * This is what replaces `shortcuts.ts`'s blanket `if (isModalOpen()) return`. Expressing it as a
324
+ * stack rather than a `!ctx.modal.open` term repeated in fourteen `when` predicates keeps the rule
325
+ * in ONE place — and a dialog that swallows every click has to swallow every chord too, including
326
+ * ones registered next week by a surface that never heard of dialogs. `palette` is the scope
327
+ * overlay-owned bindings register in; nothing outside it resolves while one is up.
328
+ */
329
+ const MODAL_STACK: readonly KeyScope[] = ["palette"];
330
+
331
+ /** A live caret shadows the canvas: app-level chords still fire, element-level ones do not. */
332
+ const CARET_STACK: readonly KeyScope[] = ["caret", "global"];
333
+
334
+ /** Focus outside the pane grid. Dock-scoped bindings, then the app's. */
335
+ const DOCK_STACK: readonly KeyScope[] = ["dock", "global"];
336
+
337
+ const CANVAS_STACK: readonly KeyScope[] = ["canvas", "global"];
338
+ const GRID_STACK: readonly KeyScope[] = ["grid", "global"];
339
+ const CODE_STACK: readonly KeyScope[] = ["code", "global"];
340
+
341
+ /** No engine owns the keyboard — only app-level chords are live. */
342
+ const GLOBAL_STACK: readonly KeyScope[] = ["global"];
343
+
344
+ /**
345
+ * The scope stack for the current context, narrowest scope first.
346
+ *
347
+ * The ladder is `caret > grid/code engine > focused dock > global` (plan §5.3), and the whole point
348
+ * is that a scope which is not on the stack cannot fire AT ALL. Three hand-written guards collapse
349
+ * into it:
350
+ *
351
+ * - The blanket modal return → the `palette`-only stack;
352
+ * - The `canvasMode === "grid" && !["o","p","s","w","z","Z"].includes(key)` allow-list → the `grid`
353
+ * stack, whose six survivors are precisely the `global`-scoped commands;
354
+ * - The caret guard → the `caret` stack, which drops `canvas` so ⌘C/⌘X/⌘V/Delete stay off a sentence
355
+ * being typed while ⌘S still saves.
356
+ *
357
+ * Preview and the non-editing surfaces (the diff view, the media library, the stylebook) get the
358
+ * bare `global` stack. Preview draws no overlays and posts no hits, so a selection carried in from
359
+ * Design is invisible: every element-level chord there — destructive or not — acts on something the
360
+ * author cannot see. That is one rule where `shortcuts.ts` had two ad-hoc refusal sets which
361
+ * disagreed with each other (⌘X was refused, ⌘C was not).
362
+ */
363
+ export function keyScopeStack(ctx: CommandContext): readonly KeyScope[] {
364
+ if (ctx.modal.open) {
365
+ return MODAL_STACK;
366
+ }
367
+ if (ctx.caret.active) {
368
+ return CARET_STACK;
369
+ }
370
+ if (ctx.focus.region !== "pane") {
371
+ return DOCK_STACK;
372
+ }
373
+ switch (ctx.editor.kind) {
374
+ case "grid": {
375
+ return GRID_STACK;
376
+ }
377
+ case "code": {
378
+ return CODE_STACK;
379
+ }
380
+ case "canvas": {
381
+ return ctx.canvas.view === "preview" ? GLOBAL_STACK : CANVAS_STACK;
382
+ }
383
+ default: {
384
+ return GLOBAL_STACK;
385
+ }
386
+ }
387
+ }