@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,345 @@
1
+ /**
2
+ * Levels.ts — the containment taxonomy and the level × placement matrix.
3
+ *
4
+ * Two vocabularies live here, and keeping them apart is the whole point (UX-REDESIGN-PLAN §5.1):
5
+ *
6
+ * - {@link Level} answers WHAT a record acts on. It governs placement and is CI-checked.
7
+ * - {@link KeyScope} answers WHERE a chord is live. It governs keyboard dispatch only.
8
+ *
9
+ * Conflating them is how "position encodes scope" degrades into unenforced prose. The clearest case
10
+ * is inline text formatting: Bold acts on a text range inside the selected node, so its `level` is
11
+ * `selection`, while its `keyScope` is `caret` — which is what makes the chord live only when a
12
+ * caret exists. There is deliberately no `range` level; a fifth level would demand a fifth region
13
+ * and there is none.
14
+ *
15
+ * {@link PLACEMENT_MATRIX} is the normative table: each placement declares the set of levels it
16
+ * admits. Mixed regions are mixed IN THE TABLE, never by prose exemption — the status bar is three
17
+ * separate single-level placements, not one "mixed" one. `scripts/check-command-levels.ts`
18
+ * validates every registered command's `menus` against this table, and `createCommandRegistry`
19
+ * applies the same check at registration so a violation cannot reach a running app either.
20
+ *
21
+ * Panel placements (the two rail groups, the navigator dock body, the bottom dock, the inspector
22
+ * dock) are part of the same matrix in `studio-ui-guidelines.md` §12 and live here too, in
23
+ * {@link PANEL_PLACEMENT_MATRIX}: `registerPanel()` (`panels/panel-registry.ts`) now exists, so a
24
+ * Panel record declares a `level` for the same reason a Command does and is checked the same way.
25
+ * The two tables are separate because their placement VOCABULARIES are disjoint — a command is
26
+ * never "in the rail's upper group", and a panel is never "in the block action bar".
27
+ */
28
+
29
+ /** Containment level — WHAT a record acts on. Governs placement. CI-checked. */
30
+ export const LEVELS = ["application", "project", "document", "selection"] as const;
31
+
32
+ export type Level = (typeof LEVELS)[number];
33
+
34
+ /** Keyboard dispatch scope — WHERE a chord is live. Orthogonal to {@link Level}. */
35
+ export const KEY_SCOPES = ["global", "canvas", "caret", "grid", "code", "dock", "palette"] as const;
36
+
37
+ export type KeyScope = (typeof KEY_SCOPES)[number];
38
+
39
+ /** Command categories. The palette renders rows as "<Category>: <title>". */
40
+ export const CATEGORIES = [
41
+ "File",
42
+ "Edit",
43
+ "Selection",
44
+ "Insert",
45
+ "View",
46
+ "Document",
47
+ "Project",
48
+ "Source Control",
49
+ "Publish",
50
+ "Assistant",
51
+ "Collaborate",
52
+ "Help",
53
+ ] as const;
54
+
55
+ export type Category = (typeof CATEGORIES)[number];
56
+
57
+ /** Every surface a command may declare itself into. `"never"` means keyboard/API only. */
58
+ export const PLACEMENTS = [
59
+ "commandbar/primary",
60
+ "commandbar/overflow",
61
+ "statusbar/project",
62
+ "statusbar/document",
63
+ "statusbar/selection",
64
+ "context/element",
65
+ "context/file",
66
+ "context/layer",
67
+ "context/tab",
68
+ "context/pane",
69
+ "blockbar",
70
+ /**
71
+ * The block action bar's SECOND cluster — inline formatting, shown while the caret is in text.
72
+ *
73
+ * A separate placement rather than a crowd inside `blockbar`, for the reason the status bar is
74
+ * three placements and not one "mixed" region: the bar's verb cluster is capped at five, and
75
+ * eight format verbs sharing that cap would push Bold into a `⋮` menu. Same level, same surface,
76
+ * different budget — which is a row in the table, not an exemption in prose.
77
+ */
78
+ "blockbar/format",
79
+ "outline/row",
80
+ "palette",
81
+ "never",
82
+ ] as const;
83
+
84
+ export type Placement = (typeof PLACEMENTS)[number];
85
+
86
+ /** One row of the matrix: the levels a placement admits, plus why it is drawn that way. */
87
+ export interface PlacementRule {
88
+ /** Levels this placement accepts. A command declaring any other level is a CI failure. */
89
+ admits: readonly Level[];
90
+ /** The reason, printed in the violation message so the fix is obvious from the failure. */
91
+ note: string;
92
+ }
93
+
94
+ /**
95
+ * The level × placement matrix — normative copy, mirrored into `studio-ui-guidelines.md` §12.
96
+ *
97
+ * Read a row as: "this region renders records of these levels, and nothing else."
98
+ */
99
+ export const PLACEMENT_MATRIX: Readonly<Record<Placement, PlacementRule>> = {
100
+ "commandbar/primary": {
101
+ admits: ["application", "document"],
102
+ note: "document only for Save / Undo / Redo / Open in Browser, by frequency; ≤5 total",
103
+ },
104
+ "commandbar/overflow": {
105
+ admits: ["application", "project", "document"],
106
+ note: "never selection — the Command Bar is not a selection surface",
107
+ },
108
+ "statusbar/project": { admits: ["project"], note: "the status bar's left field" },
109
+ "statusbar/document": { admits: ["document"], note: "the status bar's centre field" },
110
+ "statusbar/selection": { admits: ["selection"], note: "the status bar's right field" },
111
+ "context/element": { admits: ["selection"], note: "the canvas element menu acts on a selection" },
112
+ "context/file": { admits: ["project"], note: "a file row addresses the project's file set" },
113
+ "context/layer": { admits: ["selection"], note: "an outline row IS a selection" },
114
+ "context/tab": { admits: ["document"], note: "a tab addresses one document" },
115
+ "context/pane": { admits: ["document"], note: "a pane hosts one document" },
116
+ blockbar: { admits: ["selection"], note: "the floating bar owns selection-scoped verbs" },
117
+ "blockbar/format": {
118
+ admits: ["selection"],
119
+ note: "the bar's inline-format cluster — a range inside the selection is still the selection",
120
+ },
121
+ "outline/row": { admits: ["selection"], note: "row actions act on the row's node" },
122
+ palette: {
123
+ admits: ["application", "project", "document", "selection"],
124
+ note: "the level-agnostic surface; it groups rows by level",
125
+ },
126
+ never: {
127
+ admits: ["application", "project", "document", "selection"],
128
+ note: "keyboard- and API-only; no rendered surface to be misplaced in",
129
+ },
130
+ };
131
+
132
+ // ─── Panels ───────────────────────────────────────────────────────────────────
133
+
134
+ /**
135
+ * Every surface a Panel record may occupy.
136
+ *
137
+ * The rail's two level groups are SEPARATE placements, not one "mixed" region — principle 2's whole
138
+ * point. `navigator` is the dock body below them, which hosts whichever panel the rail selected and
139
+ * prints that panel's level in its header, so it is the one panel placement admitting two levels.
140
+ */
141
+ export const PANEL_PLACEMENTS = [
142
+ "rail/project",
143
+ "rail/document",
144
+ "navigator",
145
+ "inspector",
146
+ "dock.bottom",
147
+ ] as const;
148
+
149
+ export type PanelPlacement = (typeof PANEL_PLACEMENTS)[number];
150
+
151
+ /**
152
+ * The level × placement matrix for Panel records — normative copy, mirrored into
153
+ * `studio-ui-guidelines.md` §12 beside {@link PLACEMENT_MATRIX}.
154
+ *
155
+ * The rail rows are what stop it re-accreting: a panel cannot be filed in the PROJECT group because
156
+ * "it feels project-ish", only because the state it WRITES is the project's (principle 3).
157
+ */
158
+ export const PANEL_PLACEMENT_MATRIX: Readonly<Record<PanelPlacement, PlacementRule>> = {
159
+ "rail/project": {
160
+ admits: ["project"],
161
+ note: "the rail's upper group — Files, Search, Source Control, Problems",
162
+ },
163
+ "rail/document": {
164
+ admits: ["document"],
165
+ note: "the rail's lower group — Outline, Page, Data, Packages",
166
+ },
167
+ navigator: {
168
+ admits: ["project", "document"],
169
+ note: "whichever level the hosted panel declares; the panel header prints it",
170
+ },
171
+ inspector: { admits: ["selection"], note: "the Inspector's tabs are selection surfaces" },
172
+ "dock.bottom": { admits: ["project", "document"], note: "the panel header states which" },
173
+ };
174
+
175
+ /** The subset of a Panel record this check reads. */
176
+ export interface PlaceablePanel {
177
+ id: string;
178
+ level: Level;
179
+ /** The dock hosting the panel's body. */
180
+ dock: "navigator" | "inspector" | "bottom";
181
+ /** `false` for a panel with no rail button — it declares no rail-group placement. */
182
+ rail?: boolean | undefined;
183
+ }
184
+
185
+ /** One rejected (panel, placement) pair. Shaped like {@link PlacementViolation}, keyed by panel. */
186
+ export interface PanelPlacementViolation {
187
+ panelId: string;
188
+ placement: string;
189
+ level: string;
190
+ message: string;
191
+ }
192
+
193
+ /** The dock body placement a panel's `dock` names. */
194
+ function dockPlacement(dock: PlaceablePanel["dock"]): PanelPlacement {
195
+ return dock === "bottom" ? "dock.bottom" : dock;
196
+ }
197
+
198
+ /**
199
+ * Every placement a Panel record occupies: its dock body, plus its rail group when it has a button.
200
+ *
201
+ * The rail group is DERIVED from the level rather than declared, which is what makes "grouped by
202
+ * level with a divider" (§3.2 ②) a property of the data instead of an ordering convention in the
203
+ * rail's template. A rail panel whose level has no group (`application`, `selection`) therefore
204
+ * names a placement the matrix does not contain, and is rejected below.
205
+ */
206
+ export function panelPlacements(panel: PlaceablePanel): string[] {
207
+ const placements: string[] = [dockPlacement(panel.dock)];
208
+ if (panel.rail !== false) {
209
+ placements.push(`rail/${panel.level}`);
210
+ }
211
+ return placements;
212
+ }
213
+
214
+ /** Whether `value` is one of the {@link PANEL_PLACEMENTS}. */
215
+ export function isPanelPlacement(value: string): value is PanelPlacement {
216
+ return (PANEL_PLACEMENTS as readonly string[]).includes(value);
217
+ }
218
+
219
+ /** Check one Panel record against {@link PANEL_PLACEMENT_MATRIX}. */
220
+ export function checkPanelPlacement(panel: PlaceablePanel): PanelPlacementViolation[] {
221
+ const violations: PanelPlacementViolation[] = [];
222
+ if (!isLevel(panel.level)) {
223
+ violations.push({
224
+ panelId: panel.id,
225
+ placement: "—",
226
+ level: String(panel.level),
227
+ message:
228
+ `declares unknown level "${String(panel.level)}" ` +
229
+ `(expected one of: ${LEVELS.join(", ")})`,
230
+ });
231
+ return violations;
232
+ }
233
+ for (const placement of panelPlacements(panel)) {
234
+ if (!isPanelPlacement(placement)) {
235
+ violations.push({
236
+ panelId: panel.id,
237
+ placement,
238
+ level: panel.level,
239
+ message:
240
+ `is level "${panel.level}" and would render in "${placement}", which is not a panel ` +
241
+ `placement — the rail has a group for project and one for document, and nothing else`,
242
+ });
243
+ continue;
244
+ }
245
+ const rule = PANEL_PLACEMENT_MATRIX[placement];
246
+ if (!rule.admits.includes(panel.level)) {
247
+ violations.push({
248
+ panelId: panel.id,
249
+ placement,
250
+ level: panel.level,
251
+ message:
252
+ `is level "${panel.level}" but "${placement}" admits only ` +
253
+ `${rule.admits.join(", ")} — ${rule.note}`,
254
+ });
255
+ }
256
+ }
257
+ return violations;
258
+ }
259
+
260
+ /** Check a whole panel set. Empty result = the set satisfies the matrix. */
261
+ export function checkPanelPlacements(panels: readonly PlaceablePanel[]): PanelPlacementViolation[] {
262
+ return panels.flatMap((panel) => checkPanelPlacement(panel));
263
+ }
264
+
265
+ // ─── Commands ─────────────────────────────────────────────────────────────────
266
+
267
+ /** The subset of a command record the placement check needs. */
268
+ export interface PlaceableRecord {
269
+ id: string;
270
+ level: Level;
271
+ menus?: readonly Placement[] | undefined;
272
+ }
273
+
274
+ /** One rejected (command, placement) pair. */
275
+ export interface PlacementViolation {
276
+ commandId: string;
277
+ placement: string;
278
+ level: string;
279
+ /** Ready-to-print explanation: what was declared, what the row admits, and why. */
280
+ message: string;
281
+ }
282
+
283
+ /** Whether `placement` is one of the {@link PLACEMENTS}. */
284
+ export function isPlacement(value: string): value is Placement {
285
+ return (PLACEMENTS as readonly string[]).includes(value);
286
+ }
287
+
288
+ /** Whether `level` is one of the {@link LEVELS}. */
289
+ export function isLevel(value: string): value is Level {
290
+ return (LEVELS as readonly string[]).includes(value);
291
+ }
292
+
293
+ /** Whether the matrix lets a `level` record render in `placement`. */
294
+ export function placementAdmits(placement: Placement, level: Level): boolean {
295
+ return PLACEMENT_MATRIX[placement].admits.includes(level);
296
+ }
297
+
298
+ /**
299
+ * Check one record's declared placements against the matrix.
300
+ *
301
+ * A record with no `menus` defaults to `["palette"]`, which admits every level, so the default is
302
+ * always legal — a command has to opt IN to a region before it can be misplaced.
303
+ */
304
+ export function checkRecordPlacements(record: PlaceableRecord): PlacementViolation[] {
305
+ const violations: PlacementViolation[] = [];
306
+ if (!isLevel(record.level)) {
307
+ violations.push({
308
+ commandId: record.id,
309
+ placement: "—",
310
+ level: String(record.level),
311
+ message:
312
+ `declares unknown level "${String(record.level)}" ` +
313
+ `(expected one of: ${LEVELS.join(", ")})`,
314
+ });
315
+ return violations;
316
+ }
317
+ for (const placement of record.menus ?? []) {
318
+ if (!isPlacement(placement)) {
319
+ violations.push({
320
+ commandId: record.id,
321
+ placement: String(placement),
322
+ level: record.level,
323
+ message: `declares unknown placement "${String(placement)}"`,
324
+ });
325
+ continue;
326
+ }
327
+ const rule = PLACEMENT_MATRIX[placement];
328
+ if (!rule.admits.includes(record.level)) {
329
+ violations.push({
330
+ commandId: record.id,
331
+ placement,
332
+ level: record.level,
333
+ message:
334
+ `is level "${record.level}" but "${placement}" admits only ` +
335
+ `${rule.admits.join(", ")} — ${rule.note}`,
336
+ });
337
+ }
338
+ }
339
+ return violations;
340
+ }
341
+
342
+ /** Check a whole command set. Empty result = the set satisfies the matrix. */
343
+ export function checkPlacements(records: readonly PlaceableRecord[]): PlacementViolation[] {
344
+ return records.flatMap((record) => checkRecordPlacements(record));
345
+ }
@@ -0,0 +1,193 @@
1
+ /// <reference lib="dom" />
2
+ /**
3
+ * Live-context.ts — the running app's answer to `getContext()`.
4
+ *
5
+ * `context.ts` owns the SHAPE and refuses to import a state module, which is what lets the CI
6
+ * checks load the command set in a bare Bun process. This file is the other half: it reads the
7
+ * three places state actually lives — the reactive `shell` record, `workspace` / `activeTab`, and
8
+ * `projectState` — and projects them onto that shape, once per predicate evaluation.
9
+ *
10
+ * **A fresh snapshot per call, deliberately.** The registry calls `getContext()` inside
11
+ * `visible()`, `isEnabled()` and `run()`, so a surface that repaints from an effect reads
12
+ * `shell.focusRegion`, `activeTab.value` and the tab's session THROUGH this builder while that
13
+ * effect is collecting dependencies — the effect therefore tracks them and re-runs when they
14
+ * change. Caching the record would be the one change that breaks that, and there is nothing to
15
+ * cache: every field is a property read or a `typeof fn === "function"` test.
16
+ *
17
+ * The recorded pitfall for reactive state — build nested collections complete BEFORE inserting
18
+ * them, because writes through a raw ref skip effects — does not bite here for the same reason: the
19
+ * returned record is a plain, complete, throwaway object. It is never made reactive and never
20
+ * mutated after construction.
21
+ *
22
+ * Four facts arrive by injection rather than by import: the canvas mode (owned by `studio.ts`), the
23
+ * caret and modal flags (`canvas/iframe-host.ts`, `ui/layers.ts`) and the platform. Injecting them
24
+ * keeps this module free of the import cycle studio.ts → shortcuts.ts → live-context.ts →
25
+ * studio.ts, and lets a test state a context without standing up the app.
26
+ */
27
+
28
+ import { resolveI18n } from "@jxsuite/schema/locale";
29
+ import { componentRegistry } from "../files/components";
30
+ import { getNodeAtPath, projectState } from "../store";
31
+ import { shell } from "../shell";
32
+ import { activeTab, workspace } from "../workspace/workspace";
33
+ import { primarySelection } from "../tabs/selection";
34
+ import { canRedo, canUndo } from "../tabs/transact";
35
+ import { collabState } from "../collab/collab-state";
36
+
37
+ import { canvasViewForMode, editorKindForMode, emptyContext } from "./context";
38
+ import type { CommandContext, FocusRegion } from "./context";
39
+ import type { StudioPlatform } from "../types";
40
+
41
+ /** Elements that own the keyboard while focused. Matches the old listener's target test exactly. */
42
+ const TEXT_ENTRY =
43
+ "input, textarea, select, sp-textfield, sp-search, sp-number-field, sp-picker, [contenteditable=true]";
44
+
45
+ /**
46
+ * Whether a parent-realm text control has focus.
47
+ *
48
+ * The old listener asked this of `e.target`; asking it of `document.activeElement` is the same
49
+ * answer — a keydown's target IS the focused element, and shadow-DOM retargeting maps the inner
50
+ * `<input>` of an `sp-textfield` onto the host in both cases — but it makes the fact part of the
51
+ * CONTEXT instead of part of the event, so a `when` predicate can read it too.
52
+ */
53
+ export function isTextEntryFocused(doc: Document = document): boolean {
54
+ const el = doc.activeElement;
55
+ return el instanceof HTMLElement && el.matches(TEXT_ENTRY);
56
+ }
57
+
58
+ /** The facts this module cannot reach without an import cycle or a DOM/platform singleton. */
59
+ export interface LiveContextSources {
60
+ /** `studio.ts`'s `getCanvasMode()` — the per-tab preview toggle already composed in. */
61
+ canvasMode: () => string;
62
+ /** `canvas/iframe-host.ts`'s bridge-derived caret flag (P1). */
63
+ isCaretActive: () => boolean;
64
+ /** `ui/layers.ts`'s `isModalOpen()`. */
65
+ isModalOpen: () => boolean;
66
+ /** The registered platform, or null before one is registered. */
67
+ platform: () => StudioPlatform | null;
68
+ /** Whether the assistant has usable credentials (`services/ai-models`). */
69
+ aiConfigured: () => boolean;
70
+ /**
71
+ * Whether the assistant is mid-stream. `panels/ai-panel.ts` keeps its `DocumentAssistant` module-
72
+ * private, so there is nothing to read yet; the caller passes a probe when one exists.
73
+ */
74
+ aiStreaming?: () => boolean;
75
+ }
76
+
77
+ /** `capability.*` is presence-of-method on the PAL — the check every call site does by hand today. */
78
+ function capabilities(platform: StudioPlatform | null): CommandContext["capability"] {
79
+ const has = (key: keyof StudioPlatform) => typeof platform?.[key] === "function";
80
+ return {
81
+ dataRows: has("dataRows"),
82
+ // The real member now, not the `codeService` stand-in it was minted against: the usage query is
83
+ // Its own route over the rename refactor's walker, and cloud computes it server-side.
84
+ findReferences: has("findReferences"),
85
+ gitClone: has("gitClone"),
86
+ importSite: has("importSite"),
87
+ openProjectInNewWindow: has("openProjectInNewWindow"),
88
+ // Multi-window is a desktop-only family; `newWindow` is the one every member implies.
89
+ windowControls: has("newWindow"),
90
+ };
91
+ }
92
+
93
+ /**
94
+ * Build the live `getContext` thunk.
95
+ *
96
+ * @returns A function the registry calls per predicate evaluation.
97
+ */
98
+ export function createLiveContext(sources: LiveContextSources): () => CommandContext {
99
+ return () => {
100
+ const ctx = emptyContext();
101
+ const tab = activeTab.value ?? null;
102
+ const project = projectState;
103
+ const mode = sources.canvasMode();
104
+
105
+ ctx.project.open = project !== null;
106
+ ctx.project.isSite = project?.isSiteProject === true;
107
+ // A project is a repository once git has answered for it. Before the first refresh the honest
108
+ // Answer is "not yet", and Source Control's own commands are the ones that refresh it.
109
+ ctx.project.isRepo = shell.git.status?.isRepo === true;
110
+ /*
111
+ * Two locales, not two entries in the array: the shared resolver canonicalizes and drops what
112
+ * it cannot parse, so `["en", "en", "not_a_tag"]` is one locale and gates nothing on. Reading
113
+ * `.length` off the raw config would open every i18n surface on a project that has none.
114
+ *
115
+ * `@jxsuite/schema/locale` and not `site-context.ts`: everything reachable from
116
+ * `app-commands.ts` has to load in a bare Bun process with no DOM, and that module reaches the
117
+ * platform. This one is `Intl` and string math.
118
+ */
119
+ ctx.project.isMultilingual =
120
+ (resolveI18n(project?.projectConfig ?? {}).i18n?.locales.length ?? 0) > 1;
121
+ ctx.git.ahead = shell.git.status?.ahead ?? 0;
122
+ ctx.git.behind = shell.git.status?.behind ?? 0;
123
+ ctx.git.dirtyCount = shell.git.status?.files.length ?? 0;
124
+
125
+ ctx.document.open = tab !== null;
126
+ ctx.document.dirty = tab?.doc.dirty === true;
127
+ ctx.document.mode = tab?.doc.mode ?? "";
128
+ ctx.document.canUndo = tab ? canUndo(tab) : false;
129
+ ctx.document.canRedo = tab ? canRedo(tab) : false;
130
+
131
+ ctx.editor.kind = tab ? editorKindForMode(mode) : "none";
132
+ ctx.canvas.view = canvasViewForMode(mode);
133
+ /* The grid, counted from the grid. This read spent two phases saying "panes land in P3; until
134
+ then the grid is one pane showing the focused tab" — through P3, which built the pane model,
135
+ and through P8, which gave every pane a live stage. It counted `workspace.tabOrder`, the
136
+ FOCUSED pane's strip, so it could never answer 2 and answered 0 whenever no tab was open,
137
+ while a pane with no tab is still a pane. `derived` was declared and never assigned at all.
138
+ `services/automation.ts` publishes this whole record as `probe.state()`, so the screenshot
139
+ pipeline and the assistant were both reading it.
140
+
141
+ `derived` is ASSIGNED now, and it means "the grid contains a derived pane" — the fact the two
142
+ predicates that need it actually ask. A third phase of a declared-never-assigned context key
143
+ is not caution; it is the `layoutSelection`-with-no-writer shape this codebase keeps
144
+ catching. */
145
+ ctx.pane.count = workspace.panes.length;
146
+ ctx.pane.derived = workspace.panes.some((pane) => pane.derived !== null);
147
+
148
+ const paths = tab?.session.selection ?? [];
149
+ const selection = primarySelection(paths);
150
+ if (selection) {
151
+ const node = getNodeAtPath(tab!.doc.document, selection);
152
+ ctx.selection.count = paths.length;
153
+ ctx.selection.paths = paths.map((path) => [...path]);
154
+ // Every remaining fact describes the PRIMARY — the one node a single-target verb addresses —
155
+ // Except `isRoot`, which is the batch's own gate: a batch containing the document element
156
+ // Cannot be deleted or duplicated any more than that element could be on its own.
157
+ ctx.selection.kind = typeof node?.tagName === "string" ? node.tagName : "";
158
+ // A path of fewer than two segments addresses the document element itself — the same test
159
+ // `shortcuts.ts` spelled as `selection.length >= 2` at four separate call sites.
160
+ ctx.selection.isRoot = paths.some((path) => path.length < 2);
161
+ ctx.selection.isComponentInstance = componentRegistry.some(
162
+ (c) => c.tagName === node?.tagName,
163
+ );
164
+ ctx.selection.isRepeater = node?.$prototype === "Array";
165
+ }
166
+ // Layout chrome is NOT a document selection: the node is not in the open page at all.
167
+ ctx.selection.isLayoutNode = shell.layoutSelection !== null;
168
+
169
+ // A parent-realm text field owns the keyboard exactly as a canvas caret does. Folding both into
170
+ // One key is what lets `keyScopeStack` stay a pure function of the context.
171
+ ctx.caret.active = sources.isCaretActive() || isTextEntryFocused();
172
+ /* …and folding them is wrong for a RECORD, which is why `inCanvas` is the bridge fact alone.
173
+ `format.bold` and `format.link` are `keyScope: "caret"`, so they are live wherever the caret
174
+ stack is — including the link popover's own URL field, where ⌘K would re-mount the popover
175
+ being typed into. The stack asks "is something being typed into"; a record asks "is the
176
+ thing being typed into the page". */
177
+ ctx.caret.inCanvas = sources.isCaretActive();
178
+ ctx.focus.region = shell.focusRegion as FocusRegion;
179
+ ctx.modal.open = sources.isModalOpen();
180
+
181
+ if (tab) {
182
+ const collab = collabState(tab);
183
+ ctx.collab.attached = collab.active;
184
+ ctx.collab.readOnly = collab.readOnly;
185
+ ctx.collab.sourceCanonical = collab.sourceCanonical;
186
+ }
187
+
188
+ ctx.ai.configured = sources.aiConfigured();
189
+ ctx.ai.streaming = sources.aiStreaming?.() ?? false;
190
+ ctx.capability = capabilities(sources.platform());
191
+ return ctx;
192
+ };
193
+ }