@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
@@ -8,7 +8,22 @@ import { reactive, toRaw } from "../reactivity";
8
8
  import type { Tab } from "../tabs/tab";
9
9
  import type { CollabAwarenessState } from "@jxsuite/collab/awareness-types";
10
10
 
11
- export type CollabTabStatus = "detached" | "connecting" | "synced" | "offline";
11
+ /**
12
+ * What co-editing is doing for this tab.
13
+ *
14
+ * `"detached"` used to mean three different things at once and say none of them: this build has no
15
+ * collaboration at all, this project has collaboration but this tab is solo, and the attach was
16
+ * tried and it FAILED. §7.4: a freeze is currently indistinguishable from a bug, and so was a
17
+ * broken connection. `"unavailable"` and `"failed"` split the third and first cases out, so the
18
+ * chip can say which one it is.
19
+ */
20
+ export type CollabTabStatus =
21
+ | "unavailable"
22
+ | "detached"
23
+ | "connecting"
24
+ | "synced"
25
+ | "offline"
26
+ | "failed";
12
27
 
13
28
  export interface PeerPresence {
14
29
  clientId: number;
@@ -25,8 +40,18 @@ export interface TabCollabState {
25
40
  /**
26
41
  * True while source holds the canonical lock (someone is co-editing the code view): structural
27
42
  * surfaces soft-freeze and the canvas previews the source reconciler's parses.
43
+ *
44
+ * The freeze is real and it is brief, and until now its only rendering was a grey line for three
45
+ * seconds — which is exactly what a bug looks like. It gets an indicator (§7.4).
28
46
  */
29
47
  sourceCanonical: boolean;
48
+ /**
49
+ * Why the last attach failed, when it did. Empty while `status` is anything but `"failed"`.
50
+ *
51
+ * The attach used to swallow its own exception into `status = "detached"`, so a dead relay, an
52
+ * expired token and a project that simply is not shared all produced the same silence.
53
+ */
54
+ attachError: string;
30
55
  }
31
56
 
32
57
  const states = new WeakMap<Tab, TabCollabState>();
@@ -40,10 +65,11 @@ export function collabState(tab: Tab): TabCollabState {
40
65
  if (!state) {
41
66
  state = reactive({
42
67
  active: false,
68
+ attachError: "",
43
69
  peers: [],
44
70
  readOnly: false,
45
71
  sourceCanonical: false,
46
- status: "detached",
72
+ status: "unavailable",
47
73
  }) as TabCollabState;
48
74
  states.set(key, state);
49
75
  }
@@ -1,13 +1,32 @@
1
1
  /**
2
- * Presence chips — who else is in this co-editing session, plus the sync-status pill that replaces
3
- * the dirty dot for collab tabs. Pure lit templates over collabState(tab); the toolbar includes
4
- * them and its render effect tracks the underlying reactive state.
2
+ * Presence chips — who else is in this co-editing session, the sync-status pill that replaces the
3
+ * dirty dot for collab tabs, and the two states co-editing had no way to announce (§7.4).
4
+ *
5
+ * Pure lit templates over `collabState(tab)`; the toolbar includes them and its render effect
6
+ * tracks the underlying reactive state.
7
+ *
8
+ * **What was invisible.** Three things:
9
+ *
10
+ * - **The source-canonical freeze.** While a peer holds the code view, structural edits are refused.
11
+ * The whole rendering of that was a three-second grey status line the moment you tried — which is
12
+ * precisely what a bug looks like. It gets a persistent indicator, so the refusal has a visible
13
+ * cause standing beside it for as long as it is true.
14
+ * - **Read-only guests.** Their edits applied locally and were dropped at the publish gate. They now
15
+ * get a banner that says so before they type, not a silence after.
16
+ * - **A failed attach.** It set `status = "detached"` — the same value a solo document carries — so a
17
+ * dead relay was indistinguishable from nobody having shared the file. `"failed"` and
18
+ * `"unavailable"` are now separate states with separate sentences.
19
+ *
20
+ * **Undo says what it does.** The Y.UndoManager is constructed with `trackedOrigins:
21
+ * {LOCAL_ORIGIN}`, so ⌘Z reaches your own actions and never a peer's. That is the correct behaviour
22
+ * and it is also surprising, so the status pill's title states it — §13 lists this as adopted
23
+ * precisely because silently undoing someone else's work is worse than not undoing.
5
24
  */
6
25
 
7
26
  import { html, nothing } from "lit-html";
8
27
  import type { TemplateResult } from "lit-html";
9
28
  import { collabState } from "./collab-state";
10
- import type { PeerPresence } from "./collab-state";
29
+ import type { CollabTabStatus, PeerPresence } from "./collab-state";
11
30
  import type { Tab } from "../tabs/tab";
12
31
 
13
32
  function initialOf(peer: PeerPresence): string {
@@ -22,25 +41,71 @@ function titleOf(peer: PeerPresence, docPath: string | null): string {
22
41
  return here ? who : `${who} — ${peer.state.focusedPath ?? "browsing"}`;
23
42
  }
24
43
 
25
- const STATUS_LABEL: Record<string, string> = {
44
+ /** One line per status. Every state says what it is; none of them say nothing. */
45
+ const STATUS_LABEL: Readonly<Record<CollabTabStatus, string>> = {
26
46
  connecting: "Connecting…",
47
+ detached: "Solo",
48
+ failed: "Not connected",
27
49
  offline: "Offline — changes sync on reconnect",
28
50
  synced: "Live",
51
+ unavailable: "",
29
52
  };
30
53
 
31
- /** Chips + status pill for the toolbar; `nothing` while the tab isn't co-editing. */
54
+ /**
55
+ * The sentence behind the pill — including the one undo fact nobody would guess.
56
+ *
57
+ * @param {CollabTabStatus} status
58
+ * @param {string} attachError
59
+ * @returns {string}
60
+ */
61
+ export function statusTitle(status: CollabTabStatus, attachError: string): string {
62
+ if (status === "failed") {
63
+ return (
64
+ `Live collaboration could not start${attachError ? ` — ${attachError}` : ""}. ` +
65
+ "Your edits are saved to this machine as usual."
66
+ );
67
+ }
68
+ if (status === "synced" || status === "offline") {
69
+ return `${STATUS_LABEL[status]}. Undo only takes back your own edits, never a collaborator's.`;
70
+ }
71
+ return STATUS_LABEL[status];
72
+ }
73
+
74
+ /** Chips + status pill for the toolbar; `nothing` while the tab has no collaboration to report. */
32
75
  export function presenceChipsTemplate(tab: Tab | null): TemplateResult | typeof nothing {
33
76
  if (!tab) {
34
77
  return nothing;
35
78
  }
36
79
  const state = collabState(tab);
37
- if (state.status === "detached") {
80
+ /* "unavailable" is the only silent state: this build has no collaboration, so there is nothing to
81
+ be honest ABOUT. Every other state — including solo and failed — says which one it is. */
82
+ if (state.status === "unavailable") {
38
83
  return nothing;
39
84
  }
40
- const label = STATUS_LABEL[state.status] ?? state.status;
85
+ const label = STATUS_LABEL[state.status] || state.status;
41
86
  return html`
42
- <div class="jx-presence" title=${label}>
87
+ <div class="jx-presence" title=${statusTitle(state.status, state.attachError)}>
43
88
  <span class="jx-presence-status" data-status=${state.status}>${label}</span>
89
+ ${
90
+ state.readOnly
91
+ ? html`<span
92
+ class="jx-presence-flag"
93
+ data-flag="read-only"
94
+ title="You have read access to this session. Your edits show here but are not published to the others."
95
+ >Read-only</span
96
+ >`
97
+ : nothing
98
+ }
99
+ ${
100
+ state.sourceCanonical
101
+ ? html`<span
102
+ class="jx-presence-flag"
103
+ data-flag="frozen"
104
+ title="Someone is editing the code view. Structural edits are paused until they stop — this is not an error."
105
+ >Code view held</span
106
+ >`
107
+ : nothing
108
+ }
44
109
  ${state.peers.map(
45
110
  (peer) => html`
46
111
  <span
@@ -58,3 +123,29 @@ export function presenceChipsTemplate(tab: Tab | null): TemplateResult | typeof
58
123
  </div>
59
124
  `;
60
125
  }
126
+
127
+ /**
128
+ * The read-only banner — a standing statement, not an after-the-fact refusal.
129
+ *
130
+ * A guest without write access used to watch their edits apply locally and be dropped silently at
131
+ * the publish gate, which reads as the app losing work. The sentence has to be there before the
132
+ * first keystroke, so it is rendered by the PANE CHROME (`panels/pane-context.ts`) rather than by
133
+ * the toolbar the chips ride in: the chrome is per-pane and per-document, it sits directly above
134
+ * the editing surface, and the stage is offset by the band it lives in — so the banner pushes the
135
+ * document down instead of covering it.
136
+ */
137
+ export function readOnlyBannerTemplate(tab: Tab | null): TemplateResult | typeof nothing {
138
+ if (!tab) {
139
+ return nothing;
140
+ }
141
+ const state = collabState(tab);
142
+ if (!state.active || !state.readOnly) {
143
+ return nothing;
144
+ }
145
+ return html`
146
+ <div class="jx-collab-banner" role="status" data-kind="read-only">
147
+ You have read access to this session. You can explore and edit locally, but your changes are
148
+ not published to the other people in it.
149
+ </div>
150
+ `;
151
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Active-registry.ts — the one registry this window's chrome renders from.
3
+ *
4
+ * `registry.ts` deliberately has no module-level singleton: the context a registry closes over is
5
+ * one window's, the CI checks build their own, and every test wants a registry it can state
6
+ * exactly. That is right, and it leaves one gap — the Command Bar and the Palette are mounted by
7
+ * `studio.ts` BEFORE the bootstrap composes the registry (`toolbar.mount` runs at the top of the
8
+ * file, `createCommandRegistry` near the bottom), so neither surface can be handed one at mount.
9
+ *
10
+ * This is the seam, in the same idiom as `platform.ts`'s `registerPlatform` / `getPlatform` and
11
+ * `canvas/preview-navigate.ts`'s handler slot: the bootstrap sets it once, and the surfaces ask.
12
+ *
13
+ * The holder is a `shallowRef`, which is the load-bearing part. A surface that reads
14
+ * {@link activeRegistry} inside its render effect TRACKS the holder, so the effect re-runs the
15
+ * moment the bootstrap sets it — which is what lets the Command Bar paint an honest empty band at
16
+ * mount and fill in without anyone remembering to call `render()` afterwards.
17
+ */
18
+
19
+ import { shallowRef } from "../reactivity";
20
+ import type { CommandRegistry } from "./registry";
21
+
22
+ const _registry = shallowRef<CommandRegistry | null>(null);
23
+
24
+ /**
25
+ * Publish this window's registry. Called once by the bootstrap, and by tests with their own.
26
+ *
27
+ * Passing `null` clears it, which is what an unmounting test does so the next one starts cold.
28
+ */
29
+ export function setActiveRegistry(registry: CommandRegistry | null): void {
30
+ _registry.value = registry;
31
+ }
32
+
33
+ /**
34
+ * This window's registry, or `null` before the bootstrap has composed it.
35
+ *
36
+ * Returning `null` rather than throwing is deliberate: the surfaces render a skeleton in that
37
+ * window, and a chrome that crashes because it painted one frame early would be a worse bug than
38
+ * the one this seam exists to solve.
39
+ */
40
+ export function activeRegistry(): CommandRegistry | null {
41
+ return _registry.value;
42
+ }
@@ -0,0 +1,176 @@
1
+ /**
2
+ * App-commands.ts — the whole app's command set, in one bare-Bun-importable place.
3
+ *
4
+ * `src/studio.ts` composes the registry a RUNNING window uses, by calling each module's
5
+ * `registerXCommands(registry, deps)` beside the state it writes. That is the right shape for the
6
+ * app and the wrong shape for CI: `scripts/check-command-levels.ts`,
7
+ * `scripts/check-chrome-budget.ts` and `scripts/check-shot-contract.ts` all run in a bare Bun
8
+ * process with no DOM and no bootstrap, and each looks for one export — `defaultCommandSet()`.
9
+ *
10
+ * Without this module those three checks see only `commands/defaults.ts`'s sixteen records, which
11
+ * is why plan §13.5's headline promise was not yet true: a manifest step naming `view.setActivity`
12
+ * with a panel id the registry does not declare sailed through Lane 1, because Lane 1 could not
13
+ * load the record that declares the enum. Every module gathered below imports cleanly with no
14
+ * `document` and no `localStorage` write, which is the ONLY property this file requires of them —
15
+ * and the test beside it asserts exactly that, so a future DOM read at module scope fails here
16
+ * rather than in CI.
17
+ *
18
+ * **Deps are the no-op set.** Nothing here runs a command; the checks read `id`, `level`, `menus`
19
+ * and `args`. Passing real implementations would mean importing the app.
20
+ *
21
+ * **The canvas keyboard is here now.** This note used to say `editor/shortcuts.ts`'s records were
22
+ * deliberately absent because `edit.copy` / `edit.cut` were declared in two places, and that they
23
+ * would join "when that reconciliation lands — one line each, and no script changes". The
24
+ * reconciliation had already landed (`shortcuts.ts`'s handoff table records it as PAID: the
25
+ * clipboard pair is defined once, in `editor/context-menu.ts`, and spread from there), so the
26
+ * deferral outlived its reason by two phases while ten records the app runs stayed invisible to
27
+ * every check and to the generated keyboard sheet. It was one line. `tests/
28
+ * app-commands-composition.test.ts` now asserts the projection from BOTH directions, because a
29
+ * record the app registers and the projection omits is exactly as invisible as the reverse, and
30
+ * only one of the two had a guard.
31
+ */
32
+
33
+ import { defaultCommands, noopCommandDeps } from "./defaults";
34
+ import { panelFocusRoster } from "../panels/navigator-panels";
35
+ import { createCommandRegistry } from "./registry";
36
+ import { emptyContext } from "./context";
37
+ import { DEFAULT_INSPECTOR_TAB, shellViewCommands } from "../shell";
38
+ import { canvasViewCommands } from "../canvas/canvas-utils";
39
+ import { selectionCommands } from "../canvas/canvas-render";
40
+ import { inspectorCommands } from "../panels/properties-panel";
41
+ import { dataExplorerCommands } from "../panels/data-explorer";
42
+ import { seoCommands } from "../panels/seo-modal";
43
+ import { a11yCommands } from "../services/a11y-report";
44
+ import { liveElementCommands } from "../editor/context-menu";
45
+ import { signalsCommands } from "../panels/signals-panel";
46
+ import { formulaEditorCommands } from "../panels/formula-workspace";
47
+ import { styleCommands } from "../panels/style-panel";
48
+ import { gridCommands } from "../grid/grid-open";
49
+ import { settingsCommands } from "../settings/settings-document";
50
+ import { collabCommands } from "../collab/collab-commands";
51
+ import { assistantCommands } from "../panels/ai-panel";
52
+ import { preferencesCommands } from "../settings/preferences-dialog";
53
+ import { aboutCommands } from "../about/about-modal";
54
+ import { libraryCommands } from "../browse/library-commands";
55
+ import { sourceControlCommands } from "../panels/git-panel";
56
+ import { publishCommands } from "../publish/publish-commands";
57
+ import { gridViewCommands } from "../grid/grid-panel";
58
+ import { redirectsCommands } from "../grid/redirects-grid";
59
+ import { contentCommands } from "../content/entry-commands";
60
+ import { i18nCommands } from "../i18n/i18n-commands";
61
+ import { newProjectCommands } from "../new-project/new-project-modal";
62
+ import { canvasCommands } from "../editor/shortcuts";
63
+ import { formatCommands, registerSelectionCommands } from "../panels/block-action-bar";
64
+ import { registerTabCommands } from "../workspace/workspace";
65
+ import { derivationCommands, noopDerivationDeps } from "../workspace/pane-derive";
66
+ import type { AnyCommand } from "./registry";
67
+
68
+ /** A verb set that does nothing — the checks read declarations, never behaviour. */
69
+ const NO_OP = () => {};
70
+
71
+ /**
72
+ * A stage the zoom verbs can be DECLARED against. Reading it is a no-op; running one is not the
73
+ * point here — the checks read `id`, `level`, `menus` and `args`.
74
+ *
75
+ * Exported so its own test can call it. The zoom records only reach a stage when they RUN with a
76
+ * document open, which no check does, so the accessor would otherwise be a function nothing in the
77
+ * suite ever enters — and "nothing calls it" is the shape this repository treats as a defect
78
+ * everywhere else.
79
+ */
80
+ export const noopStage = () => ({
81
+ applyTransform: NO_OP,
82
+ canvasMode: "design",
83
+ panX: 0,
84
+ panY: 0,
85
+ setPan: NO_OP,
86
+ });
87
+
88
+ /**
89
+ * Records that are only reachable through a `registerX(registry, deps)` entry point.
90
+ *
91
+ * Collected by registering them into a throwaway registry and reading it back, rather than by
92
+ * asking those modules to grow a second export. That also means these records go through
93
+ * `register()`'s own duplicate-id, chord-conflict and placement checks on the way in, so this
94
+ * function throws for the same reasons the app's bootstrap would.
95
+ */
96
+ function viaRegistration(): AnyCommand[] {
97
+ const registry = createCommandRegistry({ getContext: emptyContext });
98
+ registerTabCommands(registry, { openFile: NO_OP, openFileInPane: NO_OP });
99
+ registerSelectionCommands(registry, { convertToComponent: NO_OP, navigateToComponent: NO_OP });
100
+ return [...registry.list()];
101
+ }
102
+
103
+ /**
104
+ * Every command the app's registry holds, in bootstrap order.
105
+ *
106
+ * Order matches `studio.ts` so a reader comparing the two files can do it line by line.
107
+ */
108
+ export function appCommandSet(): AnyCommand[] {
109
+ return [
110
+ // The panel roster is the one dependency the checks must supply for real: the ⌘1–8 records are
111
+ // Generated from it, so an empty one would hide eight commands (and their chords) from the
112
+ // Level check, the chrome budget and the generated keyboard sheet alike.
113
+ ...defaultCommands({ ...noopCommandDeps(), panelRoster: panelFocusRoster() }),
114
+ ...viaRegistration(),
115
+ ...derivationCommands(noopDerivationDeps()),
116
+ ...shellViewCommands({ inspectorTab: () => DEFAULT_INSPECTOR_TAB, setInspectorTab: NO_OP }),
117
+ ...canvasViewCommands({
118
+ getCanvasMode: () => "design",
119
+ renderPane: NO_OP,
120
+ setCanvasMode: NO_OP,
121
+ setResolvingOpen: NO_OP,
122
+ }),
123
+ ...selectionCommands(),
124
+ ...inspectorCommands(),
125
+ ...seoCommands(),
126
+ ...a11yCommands(),
127
+ // The element menu's eight verbs. Every one declares `menus: ["context/element", "palette"]`
128
+ // And none reached the palette, because they were registered ONLY into the private registry
129
+ // `editor/context-menu.ts` builds for its popover — a registry whose own docstring said it
130
+ // Existed "until a bootstrap composes every contribution point into a single app-wide
131
+ // Registry". This is that bootstrap; it has existed since P2.
132
+ ...liveElementCommands(),
133
+ ...dataExplorerCommands({ renderLeftPanel: NO_OP }),
134
+ ...signalsCommands(),
135
+ ...formulaEditorCommands(),
136
+ ...gridCommands(),
137
+ ...settingsCommands(),
138
+ ...collabCommands(),
139
+ ...assistantCommands(),
140
+ ...preferencesCommands(),
141
+ ...aboutCommands(),
142
+ ...libraryCommands(),
143
+ ...contentCommands(),
144
+ // The four translation verbs. `i18n.switchLocale` is deliberately not among them: it is a
145
+ // Rendering context, defined in `canvas/canvas-utils.ts` beside the per-tab UI state it writes.
146
+ ...i18nCommands(),
147
+ ...sourceControlCommands(),
148
+ ...publishCommands(),
149
+ ...gridViewCommands(),
150
+ ...redirectsCommands(),
151
+ ...newProjectCommands(),
152
+ ...styleCommands(),
153
+ // Inline formatting — the eight `format.*` records. Registered in the bootstrap beside the bar
154
+ // That renders them, and projected here so the palette, the level check and the generated
155
+ // Keyboard sheet all see the first `caret`-scoped chords the app has ever had.
156
+ ...formatCommands(),
157
+ /* The canvas keyboard's own nine, plus the clipboard pair they spread.
158
+ These were the last records the app registered and this projection did not contain, so ⌘C,
159
+ ⌘X, ⌘V, Enter, the three structural arrows and all three zoom chords were absent from
160
+ `check-command-levels`, from the chrome budget and from the GENERATED keyboard sheet — a
161
+ reader of `docs/studio/interface/shortcuts.md` could not find Copy. The docstring above used
162
+ to defer this on the grounds that `edit.copy` was declared twice; it is not, and has not been
163
+ since the four literal duplicates were paid off (`shortcuts.ts`'s handoff table). It is one
164
+ line, exactly as that note promised. */
165
+ ...canvasCommands(noopStage),
166
+ ];
167
+ }
168
+
169
+ /**
170
+ * The name the three CI checks import by.
171
+ *
172
+ * Kept as an alias rather than renaming the scripts' contract: `commands/defaults.ts` exports the
173
+ * same symbol, so pointing a check at either module is a `--source` flag, and a check written
174
+ * before this file existed keeps working unchanged.
175
+ */
176
+ export const defaultCommandSet = appCommandSet;
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Budget.ts — chrome is earned by frequency and capped by a build check.
3
+ *
4
+ * Plan §2, principle 9: at most five commands may declare `menus: ["commandbar/primary"]`, and at
5
+ * most four tabs per dock. `scripts/check-chrome-budget.ts` fails CI otherwise, which is what turns
6
+ * "we should keep the toolbar small" from a code-review opinion into a build error.
7
+ *
8
+ * The cap is not arbitrary. The toolbar's own CSS already admits it is over budget — a container
9
+ * query at 1140px strips every `.tb-label` rather than conceding there are too many controls — and
10
+ * an unlabelled icon is a control the user has to hover to identify. A hard number is what makes
11
+ * "retire this control, keep its name and its chord" the cheap option.
12
+ *
13
+ * The dock/tab sets used to be a flat declaration — written out from §3.2 because `registerPanel()`
14
+ * did not exist. It does now, so the two `rail/*` rows are a QUERY over `railDeclarations()`
15
+ * ({@link dockTabs}) and adding a ninth rail panel fails this check without anyone remembering to
16
+ * update a list. The two rows that remain declared are the two docks whose tab sets this module
17
+ * cannot see from a bare Bun process: the Inspector's four come from `commands/defaults.ts`'s
18
+ * `INSPECTOR_TABS` (a query in all but name), and the Bottom dock's four are records in
19
+ * `panels/bottom-dock.ts`, which renders lit templates and therefore cannot be imported here.
20
+ * `tests/bottom-dock.test.ts` asserts the declared row and the registered set are the same four
21
+ * titles, so the copy cannot drift — the same bargain `tests/right-panel.test.ts` strikes for the
22
+ * Inspector. Both become real queries the day `scripts/check-chrome-budget.ts` joins them the way
23
+ * it already joins the rail.
24
+ */
25
+
26
+ import { INSPECTOR_TABS } from "./defaults";
27
+
28
+ /** The caps. Raising either one is a design decision, so it happens here, in one place. */
29
+ export const CHROME_BUDGET = {
30
+ /** Commands allowed in the Command Bar's primary verb cluster. */
31
+ commandbarPrimary: 5,
32
+ /** Tabs allowed in any one dock (or rail group). */
33
+ dockTabs: 4,
34
+ /**
35
+ * Verbs allowed in the block action bar's inline-format cluster.
36
+ *
37
+ * Eight, which is the whole vocabulary `data/elements-meta.json` declares — bold, italic,
38
+ * underline, strikethrough, superscript, subscript, code, link. It is a cap and not a count: an
39
+ * element offers the SUBSET its `$inlineActions` names, so the bar never draws eight, and a ninth
40
+ * verb is a design decision that has to be made here rather than by appending to a data file.
41
+ */
42
+ blockbarFormat: 8,
43
+ } as const;
44
+
45
+ /** One tabbed region and what it currently hosts. */
46
+ export interface DockDeclaration {
47
+ /** Region key as §3.2 names it. Rail groups count separately — they are separate placements. */
48
+ dock: string;
49
+ tabs: readonly string[];
50
+ }
51
+
52
+ /**
53
+ * The tabbed regions that are still WRITTEN DOWN rather than observed.
54
+ *
55
+ * The Inspector's row is a query in all but name — `INSPECTOR_TABS` is the list `right-panel.ts`
56
+ * renders and `⌘⇧1–4` address, so a fifth inspector tab fails this check in the commit that adds
57
+ * it. The Bottom dock's row is the same shape: the four ids live in `shell.ts`'s `BOTTOM_TAB_IDS`
58
+ * (so `view.setBottomTab`'s enum can be read in a bare Bun process) and the four RECORDS live in
59
+ * `panels/bottom-dock.ts`. It inherited this cap before it existed, which is why Deploy folded into
60
+ * Activity rather than becoming a fifth tab.
61
+ *
62
+ * The rail's two groups are NOT here. They come from `panels/panel-registry.ts`'s
63
+ * `railDeclarations()` and are joined on by {@link dockTabs} — the registry is the single source of
64
+ * what a panel is called and which group it sits in, and a duplicate list here was the last place
65
+ * those could disagree.
66
+ */
67
+ export const DECLARED_DOCK_TABS: readonly DockDeclaration[] = [
68
+ { dock: "inspector", tabs: INSPECTOR_TABS.map((tab) => tab.title) },
69
+ { dock: "bottom", tabs: ["Problems", "Logic", "Activity"] },
70
+ ];
71
+
72
+ /**
73
+ * Every tabbed region: the declared ones plus the rail groups the panel registry observes.
74
+ *
75
+ * The rails arrive as an ARGUMENT rather than by import so this module keeps the property that lets
76
+ * three CI checks load it in a bare Bun process — `panel-registry.ts` reaches the DOM, the shell
77
+ * record and the workspace. `scripts/check-chrome-budget.ts` is the one place that joins them.
78
+ *
79
+ * @param rails `railDeclarations()`, or `[]` where no registry has been populated.
80
+ */
81
+ export function dockTabs(rails: readonly DockDeclaration[] = []): DockDeclaration[] {
82
+ return [...DECLARED_DOCK_TABS, ...rails];
83
+ }
84
+
85
+ /** The subset of a command record the budget check reads. */
86
+ export interface BudgetableRecord {
87
+ id: string;
88
+ menus?: readonly string[] | undefined;
89
+ }
90
+
91
+ /** One exceeded cap. */
92
+ export interface BudgetViolation {
93
+ /** What was counted: a placement key, or a dock key. */
94
+ subject: string;
95
+ count: number;
96
+ limit: number;
97
+ message: string;
98
+ }
99
+
100
+ /** Commands declaring the primary Command Bar cluster. */
101
+ export function primaryCommandIds(commands: readonly BudgetableRecord[]): string[] {
102
+ return commands
103
+ .filter((command) => (command.menus ?? []).includes("commandbar/primary"))
104
+ .map((command) => command.id);
105
+ }
106
+
107
+ /**
108
+ * Check both caps.
109
+ *
110
+ * @param input.commands Every registered command record.
111
+ * @param input.docks The shell's tabbed regions. Defaults to {@link dockTabs} with no rails.
112
+ */
113
+ export function checkChromeBudget(input: {
114
+ commands: readonly BudgetableRecord[];
115
+ docks?: readonly DockDeclaration[];
116
+ }): BudgetViolation[] {
117
+ const violations: BudgetViolation[] = [];
118
+ const primary = primaryCommandIds(input.commands);
119
+ if (primary.length > CHROME_BUDGET.commandbarPrimary) {
120
+ violations.push({
121
+ subject: "commandbar/primary",
122
+ count: primary.length,
123
+ limit: CHROME_BUDGET.commandbarPrimary,
124
+ message:
125
+ `${primary.length} commands declare menus:["commandbar/primary"], the cap is ` +
126
+ `${CHROME_BUDGET.commandbarPrimary} — ${primary.join(", ")}. Retire one to ` +
127
+ `"commandbar/overflow": it keeps its name, its chord and its palette row.`,
128
+ });
129
+ }
130
+ const formatting = input.commands
131
+ .filter((command) => (command.menus ?? []).includes("blockbar/format"))
132
+ .map((command) => command.id);
133
+ if (formatting.length > CHROME_BUDGET.blockbarFormat) {
134
+ violations.push({
135
+ subject: "blockbar/format",
136
+ count: formatting.length,
137
+ limit: CHROME_BUDGET.blockbarFormat,
138
+ message:
139
+ `${formatting.length} commands declare menus:["blockbar/format"], the cap is ` +
140
+ `${CHROME_BUDGET.blockbarFormat} — ${formatting.join(", ")}. The cluster sits over a text ` +
141
+ `range in a floating bar; a ninth verb belongs in the palette.`,
142
+ });
143
+ }
144
+ for (const dock of input.docks ?? dockTabs()) {
145
+ if (dock.tabs.length > CHROME_BUDGET.dockTabs) {
146
+ violations.push({
147
+ subject: dock.dock,
148
+ count: dock.tabs.length,
149
+ limit: CHROME_BUDGET.dockTabs,
150
+ message:
151
+ `dock "${dock.dock}" declares ${dock.tabs.length} tabs, the cap is ` +
152
+ `${CHROME_BUDGET.dockTabs} — ${dock.tabs.join(", ")}. Fold two together or move one ` +
153
+ `to another dock.`,
154
+ });
155
+ }
156
+ }
157
+ return violations;
158
+ }