@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
@@ -1,84 +1,113 @@
1
1
  /// <reference lib="dom" />
2
2
  /**
3
- * On project open, compare the project's `@jxsuite/*` dependency ranges to the version this Studio
4
- * build embeds (`VERSION`). If the project is behind, prompt the user to bump them to match and —
5
- * on confirm — rewrite the ranges and reinstall. The target is the embedded version (not npm
6
- * latest) so a project always lines up with the runtime/compiler the running app actually uses.
3
+ * On project open, offer to bring the project's `@jxsuite/*` dependencies up to their newest
4
+ * PUBLISHED versions — each package to its own latest, asked of the npm registry.
5
+ *
6
+ * It used to target `VERSION`, the version this Studio build embeds, and bump every `@jxsuite/*`
7
+ * range to `^VERSION` in one move. That was right exactly once: when the whole suite released
8
+ * together, so "the version Studio is" and "the newest version of each package" were the same
9
+ * string. They are not any more — the packages release on their own cadences, and `@jxsuite/parser`
10
+ * being at 1.2.0 while Studio is 2.0.1 is the normal case, not a fault. Targeting the embedded
11
+ * version therefore proposed a version that may never have been published, for a package whose real
12
+ * latest it had not looked at.
13
+ *
14
+ * **Studio's own copies are not involved.** The app resolves `@jxsuite/*` from its own install
15
+ * (that is the hermetic host rule the schema loader enforces), so the project's ranges govern `jx
16
+ * build` and the project's types — not the running app. There is no reason for them to match
17
+ * Studio, and pinning them to it was the residue of an assumption that is no longer true.
18
+ *
19
+ * The registry lookup is `platform.outdatedPackages()`, the same seam the dependencies editor uses.
20
+ * A host that does not offer it (the cloud session, which manages dependencies server-side) simply
21
+ * gets no prompt: without the registry there is no honest target, and guessing is what this
22
+ * replaced.
7
23
  */
8
24
 
9
25
  import { html } from "lit-html";
10
26
  import { getPlatform } from "../platform";
11
- import { VERSION } from "../version";
27
+ import { shouldInstallAutomation } from "../services/automation";
12
28
  import { showConfirmDialog } from "../ui/layers";
13
29
  import { showProgressModal } from "../ui/progress-modal";
14
- import { statusMessage } from "../panels/statusbar";
15
- import { isComparable, isUpgrade } from "./semver";
16
- import type { PackageInfo } from "../types";
30
+ import { notify } from "../services/notify";
31
+ import { isUpgrade } from "./semver";
17
32
 
18
33
  const JXSUITE_PREFIX = "@jxsuite/";
19
34
 
20
35
  export interface JxsuiteUpdate {
21
36
  name: string;
37
+ /** The range pinned in package.json, e.g. `^1.2.0`. */
22
38
  current: string;
39
+ /** THIS package's newest published version — not a suite-wide number. */
40
+ latest: string;
23
41
  dev: boolean;
24
42
  }
25
43
 
26
44
  /**
27
- * Compare the project's @jxsuite/* deps to the embedded version. Returns the packages behind the
28
- * target plus the target version, or null when there's nothing to do (dev build, no project, or all
29
- * already current/ahead).
45
+ * The project's `@jxsuite/*` dependencies that are behind their own newest published version.
46
+ *
47
+ * Empty when there is nothing to do, when the host cannot reach the registry, or when it offers no
48
+ * lookup at all. Every failure path is empty rather than thrown: this runs on project open, and a
49
+ * registry that is unreachable on a train is not an error the author needs to see.
30
50
  */
31
- export async function checkJxsuiteUpdate(): Promise<{
32
- target: string;
33
- outdated: JxsuiteUpdate[];
34
- } | null> {
35
- const target = VERSION;
36
- if (!isComparable(target)) {
37
- return null; // "dev" / non-semver build — no reliable target
38
- }
51
+ export async function checkJxsuiteUpdate(): Promise<JxsuiteUpdate[]> {
39
52
  const platform = getPlatform();
40
- let pkgs: PackageInfo[];
53
+ if (!platform.outdatedPackages) {
54
+ return [];
55
+ }
56
+ let reported;
41
57
  try {
42
- pkgs = await platform.listPackages();
58
+ reported = await platform.outdatedPackages();
43
59
  } catch {
44
- return null;
60
+ return [];
45
61
  }
46
62
  const outdated: JxsuiteUpdate[] = [];
47
- for (const p of pkgs) {
48
- if (!p.name.startsWith(JXSUITE_PREFIX) || !isComparable(p.version)) {
49
- continue;
50
- }
51
- if (isUpgrade(p.version, target)) {
52
- outdated.push({ current: p.version, dev: Boolean(p.dev), name: p.name });
63
+ for (const p of reported) {
64
+ /*
65
+ * `isUpgrade`, not merely "differs from latest". `outdatedPackages` reports any difference, and
66
+ * a project deliberately pinned AHEAD of the registry — a prerelease, or a range bumped before
67
+ * the publish landed — would otherwise be offered a downgrade described as an update.
68
+ */
69
+ if (p.name.startsWith(JXSUITE_PREFIX) && isUpgrade(p.current, p.latest)) {
70
+ outdated.push({ current: p.current, dev: Boolean(p.dev), latest: p.latest, name: p.name });
53
71
  }
54
72
  }
55
- return outdated.length > 0 ? { outdated, target } : null;
73
+ return outdated;
56
74
  }
57
75
 
58
- function dismissKey(root: string, target: string): string {
59
- return `jx:jxsuite-update-dismissed:${root}:${target}`;
76
+ /**
77
+ * The dismissal is remembered against the exact set of versions declined.
78
+ *
79
+ * It used to be keyed on the single target version, which no longer exists. Keying on the set means
80
+ * a later publish of any one package asks again — which is the behaviour you want from "not now",
81
+ * and the reason this is not keyed on the project alone.
82
+ */
83
+ function dismissKey(root: string, outdated: JxsuiteUpdate[]): string {
84
+ const signature = outdated
85
+ .map((p) => `${p.name}@${p.latest}`)
86
+ .toSorted()
87
+ .join(",");
88
+ return `jx:jxsuite-update-dismissed:${root}:${signature}`;
60
89
  }
61
90
 
62
- function isDismissed(root: string, target: string): boolean {
91
+ function isDismissed(root: string, outdated: JxsuiteUpdate[]): boolean {
63
92
  try {
64
- return localStorage.getItem(dismissKey(root, target)) === "1";
93
+ return localStorage.getItem(dismissKey(root, outdated)) === "1";
65
94
  } catch {
66
95
  return false;
67
96
  }
68
97
  }
69
98
 
70
- function setDismissed(root: string, target: string): void {
99
+ function setDismissed(root: string, outdated: JxsuiteUpdate[]): void {
71
100
  try {
72
- localStorage.setItem(dismissKey(root, target), "1");
101
+ localStorage.setItem(dismissKey(root, outdated), "1");
73
102
  } catch {
74
103
  /* Ignore storage errors */
75
104
  }
76
105
  }
77
106
 
78
- /** Apply a set of @jxsuite bumps to `^target` and reinstall, behind a progress modal. */
79
- export async function applyJxsuiteUpdate(outdated: JxsuiteUpdate[], target: string): Promise<void> {
107
+ /** Pin each package to `^<its own latest>` and reinstall, behind a progress modal. */
108
+ export async function applyJxsuiteUpdate(outdated: JxsuiteUpdate[]): Promise<void> {
80
109
  const platform = getPlatform();
81
- if (!platform.setPackageVersions) {
110
+ if (!platform.setPackageVersions || outdated.length === 0) {
82
111
  return;
83
112
  }
84
113
  const progress = showProgressModal({
@@ -87,11 +116,13 @@ export async function applyJxsuiteUpdate(outdated: JxsuiteUpdate[], target: stri
87
116
  });
88
117
  try {
89
118
  const result = await platform.setPackageVersions(
90
- outdated.map((p) => ({ dev: p.dev, name: p.name, version: `^${target}` })),
119
+ // Per package. One shared `^${target}` for all of them is the bug this module was refactored
120
+ // To remove.
121
+ outdated.map((p) => ({ dev: p.dev, name: p.name, version: `^${p.latest}` })),
91
122
  );
92
123
  if (result.ok) {
93
124
  progress.done();
94
- statusMessage(`Updated ${outdated.length} @jxsuite package(s) to ${target}`);
125
+ notify.success(`Updated ${outdated.length} @jxsuite package(s) to their latest versions.`);
95
126
  } else {
96
127
  progress.fail(result.log ?? "Update failed");
97
128
  }
@@ -101,22 +132,40 @@ export async function applyJxsuiteUpdate(outdated: JxsuiteUpdate[], target: stri
101
132
  }
102
133
 
103
134
  /**
104
- * Prompt to update @jxsuite packages on open. Skips when there's nothing to do or the user already
105
- * declined this exact target for this project (remembered in localStorage).
135
+ * Prompt to update on open. Skips when there is nothing to do or the user already declined this
136
+ * exact set of versions for this project (remembered in localStorage).
106
137
  */
107
138
  export async function maybePromptJxsuiteUpdate(projectRoot: string): Promise<void> {
139
+ // Automation/screenshot runs open projects read-only to drive the canvas — the same rule
140
+ // Ensure-deps.ts states, and for the same reason: confirming here calls `setPackageVersions`,
141
+ // Which rewrites the opened project's package.json.
142
+ //
143
+ // It also has to hold when nobody confirms anything. `showConfirmDialog` renders an
144
+ // `<sp-dialog-wrapper open underlay>`, and an underlay swallows every pointer event across the
145
+ // Viewport — so a prompt raised at boot means every subsequent click in a shot lands in a scrim.
146
+ // That is not hypothetical: it put this dialog into the middle of 33 committed screenshots,
147
+ // Including docs/images/hero.png, which is the jxsuite.com marketing hero.
148
+ //
149
+ // Correct pins are NOT sufficient on their own. `outdatedPackages` compares the range's base
150
+ // Version against the registry's `latest` (packages/server/src/packages.ts: `latest ===
151
+ // StripRange(p.version)`), not whether the range resolves it — so a project pinned `^1.4.1` is
152
+ // "outdated" the moment 1.4.2 publishes, and every starter shot would be scrimmed again by the
153
+ // Next patch release of any @jxsuite package.
154
+ if (shouldInstallAutomation(location.search)) {
155
+ return;
156
+ }
108
157
  const platform = getPlatform();
109
158
  if (!platform.setPackageVersions) {
110
159
  return;
111
160
  }
112
- const check = await checkJxsuiteUpdate();
113
- if (!check || isDismissed(projectRoot, check.target)) {
161
+ const outdated = await checkJxsuiteUpdate();
162
+ if (outdated.length === 0 || isDismissed(projectRoot, outdated)) {
114
163
  return;
115
164
  }
116
- const list = check.outdated.map((p) => `${p.name} ${p.current} → ^${check.target}`).join("\n");
165
+ const list = outdated.map((p) => `${p.name} ${p.current} → ^${p.latest}`).join("\n");
117
166
  const confirmed = await showConfirmDialog(
118
167
  "Update @jxsuite packages?",
119
- html`This project uses older @jxsuite packages. Update them to match Studio ${check.target}?
168
+ html`Newer versions of these packages have been published. Update the project to them?
120
169
  <br /><br /><span
121
170
  style="font-size:var(--spectrum-font-size-75, 12px);color:var(--fg-dim);white-space:pre-line"
122
171
  >${list}</span
@@ -124,8 +173,8 @@ export async function maybePromptJxsuiteUpdate(projectRoot: string): Promise<voi
124
173
  { cancelLabel: "Not now", confirmLabel: "Update" },
125
174
  );
126
175
  if (!confirmed) {
127
- setDismissed(projectRoot, check.target);
176
+ setDismissed(projectRoot, outdated);
128
177
  return;
129
178
  }
130
- await applyJxsuiteUpdate(check.outdated, check.target);
179
+ await applyJxsuiteUpdate(outdated);
131
180
  }
@@ -13,7 +13,7 @@ import { errorMessage } from "@jxsuite/schema/parse";
13
13
  import { getPlatform } from "../platform";
14
14
  import { showConfirmDialog } from "../ui/layers";
15
15
  import { showProgressModal } from "../ui/progress-modal";
16
- import { statusMessage } from "../panels/statusbar";
16
+ import { notify } from "../services/notify";
17
17
  import { applyJxsuiteUpdate, checkJxsuiteUpdate } from "./jxsuite-update";
18
18
  import type { GitStatusResult } from "../types";
19
19
 
@@ -192,15 +192,15 @@ async function applyDiscard(plan: PackagePullPlan): Promise<void> {
192
192
  */
193
193
  async function syncPackagesAfterPull(): Promise<void> {
194
194
  const platform = getPlatform();
195
- let check: Awaited<ReturnType<typeof checkJxsuiteUpdate>> = null;
195
+ let outdated: Awaited<ReturnType<typeof checkJxsuiteUpdate>> = [];
196
196
  try {
197
- check = await checkJxsuiteUpdate();
197
+ outdated = await checkJxsuiteUpdate();
198
198
  } catch {
199
- check = null;
199
+ outdated = [];
200
200
  }
201
- if (check && platform.setPackageVersions) {
201
+ if (outdated.length > 0 && platform.setPackageVersions) {
202
202
  // No re-prompt: the discarded local changes came from an update the user already accepted.
203
- await applyJxsuiteUpdate(check.outdated, check.target);
203
+ await applyJxsuiteUpdate(outdated);
204
204
  return;
205
205
  }
206
206
  if (!platform.installDependencies) {
@@ -241,7 +241,9 @@ async function discardPullAndSync(plan: PackagePullPlan): Promise<void> {
241
241
  await syncPackagesAfterPull();
242
242
  throw error;
243
243
  }
244
- statusMessage("Local package updates were superseded by pulled changes");
244
+ notify.warn("Local package updates were superseded by pulled changes.", {
245
+ source: "Source Control",
246
+ });
245
247
  await syncPackagesAfterPull();
246
248
  }
247
249
 
@@ -349,10 +351,17 @@ export async function autoSyncProjectOnOpen(): Promise<void> {
349
351
  if (!status?.isRepo || (status.remotes?.length ?? 0) === 0) {
350
352
  return;
351
353
  }
352
- statusMessage("Syncing project…");
354
+ notify.info("Syncing project…", { key: "project.sync", source: "Source Control" });
353
355
  try {
354
356
  await pullWithPackageSync();
355
357
  } catch (error) {
356
- statusMessage(`Sync skipped: ${errorMessage(error)}`);
358
+ // A skipped sync leaves the project behind its remote, which is a state to FIX rather than
359
+ // A moment to read about — it stays listed until a pull succeeds.
360
+ notify.warn("The project could not be synced with its remote.", {
361
+ detail: errorMessage(error),
362
+ key: "project.sync",
363
+ source: "Source Control",
364
+ tier: "problem",
365
+ });
357
366
  }
358
367
  }
@@ -249,7 +249,8 @@ function pushValue(out: ParamValues, param: string, value: unknown) {
249
249
  return;
250
250
  }
251
251
  const str = String(value);
252
- const list = (out[param] ??= []);
252
+ out[param] ??= [];
253
+ const list = out[param];
253
254
  if (!list.includes(str)) {
254
255
  list.push(str);
255
256
  }
@@ -1,31 +1,53 @@
1
1
  /// <reference lib="dom" />
2
- /** Activity bar — tab icons for switching left panel views. */
2
+ /**
3
+ * Activity bar — the rail, rendered FROM the panel registry.
4
+ *
5
+ * There is no list of panels in this file. `railGroups()` returns every rail-able record grouped by
6
+ * level and filtered by its own `when`, and the rail draws whatever that says: two groups with a
7
+ * divider between them, PROJECT above and DOCUMENT below (plan §3.2 ②). Adding a panel is a
8
+ * `registerPanel()` call in the module that owns it, and it appears here; there is nothing to
9
+ * update in step.
10
+ *
11
+ * **Every rail button opens the Navigator, and the grouping is by LEVEL.** It briefly spanned two
12
+ * docks so Problems could keep a button while its body was drawn at the bottom; that cost a
13
+ * per-dock branch here, in {@link isRailPanelShowing} and in `focusPanel`, and it pointed left at
14
+ * something that opens below. Problems is `rail: false` now.
15
+ *
16
+ * The predecessor was an eight-item array literal — `{icon, label, value}` — whose `label` reached
17
+ * the screen only as a `title` attribute. Thirteen icons in this shell had no name but a hover
18
+ * tooltip, which is the accessibility failure §2 principle 6 names outright: **every rail button
19
+ * now carries an 11px text label under its icon**, and the rail is 56px wide to hold it.
20
+ */
3
21
 
4
22
  import { html, render as litRender, nothing } from "lit-html";
5
- import { activityBar, renderOnly } from "../store";
23
+ import { activityBar } from "../store";
6
24
  import { effect, effectScope } from "../reactivity";
7
- import { activeTab } from "../workspace/workspace";
8
- import { applyPanelCollapse, view } from "../view";
9
- import { openSettingsModal } from "../settings/settings-modal";
10
- import { openAboutModal } from "../about/about-modal";
25
+ import { requireNavigatorPanelId, shell, toggleActivityTab } from "../shell";
11
26
  import { refreshGitStatus } from "./git-panel";
27
+ import { panelContext, railGroups } from "./panel-registry";
28
+ import { registerNavigatorPanels } from "./navigator-panels";
29
+ import type { PanelRecord } from "./panel-registry";
30
+ import type { CommandContext } from "../commands/context";
12
31
  import type { EffectScope } from "@vue/reactivity";
32
+ import { activeRegistry } from "../commands/active-registry";
13
33
  import type { TemplateResult } from "lit-html";
14
34
 
15
35
  let _scope: EffectScope | null = null;
16
36
 
17
37
  export function mount() {
38
+ // The rail is a rendering of the registry, so the registry has to exist before the first paint.
39
+ // Idempotent, and the Navigator dock calls it too — whichever mounts first wins.
40
+ registerNavigatorPanels();
18
41
  _scope = effectScope();
19
42
  _scope.run(() => {
20
43
  effect(() => {
21
- const tab = activeTab.value;
22
- if (tab) {
23
- const gs = tab.session.ui.gitStatus;
24
- // The badge needs a status once. A refresh that already failed must not re-arm this: the
25
- // Effect re-runs on the very state the failure writes, so retrying here spins.
26
- if (!gs && !tab.session.ui.gitLoading && !tab.session.ui.gitError) {
27
- void refreshGitStatus();
28
- }
44
+ // Source control is PROJECT state, so the badge is fetched and drawn without reference to
45
+ // Any tab. Sourcing it from `activeTab` is what made it vanish when the last tab closed.
46
+ // The badge needs a status once. A refresh that already failed must not re-arm this: the
47
+ // Effect re-runs on the very state the failure writes, so retrying here spins.
48
+ const { error, loading, status } = shell.git;
49
+ if (!status && !loading && !error) {
50
+ void refreshGitStatus();
29
51
  }
30
52
  renderActivityBar();
31
53
  });
@@ -63,99 +85,138 @@ const gitBranchIcon = (s: string) => html`
63
85
  */
64
86
  export function tabIcon(tag: string, size?: string) {
65
87
  const m: Record<string, (s: string) => TemplateResult> = {
66
- "sp-icon-artboard": (s: string) =>
67
- html`<sp-icon-artboard slot="icon" size=${s}></sp-icon-artboard>`,
68
88
  "sp-icon-box": (s: string) => html`<sp-icon-box slot="icon" size=${s}></sp-icon-box>`,
69
- "sp-icon-brackets": (s: string) =>
70
- html`<sp-icon-brackets slot="icon" size=${s}></sp-icon-brackets>`,
71
- "sp-icon-brush": (s: string) => html`<sp-icon-brush slot="icon" size=${s}></sp-icon-brush>`,
72
- "sp-icon-chat": (s: string) => html`<sp-icon-chat slot="icon" size=${s}></sp-icon-chat>`,
73
89
  "sp-icon-data": (s: string) => html`<sp-icon-data slot="icon" size=${s}></sp-icon-data>`,
74
- "sp-icon-event": (s: string) => html`<sp-icon-event slot="icon" size=${s}></sp-icon-event>`,
75
- "sp-icon-file-single-web-page": (s: string) =>
76
- html`<sp-icon-file-single-web-page slot="icon" size=${s}></sp-icon-file-single-web-page>`,
77
90
  "sp-icon-folder": (s: string) => html`<sp-icon-folder slot="icon" size=${s}></sp-icon-folder>`,
78
91
  "sp-icon-git-branch": gitBranchIcon,
79
92
  "sp-icon-layers": (s: string) => html`<sp-icon-layers slot="icon" size=${s}></sp-icon-layers>`,
80
- "sp-icon-properties": (s: string) =>
81
- html`<sp-icon-properties slot="icon" size=${s}></sp-icon-properties>`,
93
+ "sp-icon-search": (s: string) => html`<sp-icon-search slot="icon" size=${s}></sp-icon-search>`,
82
94
  "sp-icon-view-all-tags": (s: string) =>
83
95
  html`<sp-icon-view-all-tags slot="icon" size=${s}></sp-icon-view-all-tags>`,
84
- "sp-icon-view-grid": (s: string) =>
85
- html`<sp-icon-view-grid slot="icon" size=${s}></sp-icon-view-grid>`,
86
96
  };
87
97
  const fn = m[tag];
88
98
  return fn ? fn(size || "s") : nothing;
89
99
  }
90
100
 
91
- export function renderActivityBar() {
92
- const tab = activeTab.value;
93
- const { leftTab } = view;
94
- const gitFileCount = tab?.session.ui.gitStatus?.files?.length || 0;
95
- const tabs = [
96
- { icon: "sp-icon-folder", label: "Files", value: "files" },
97
- { icon: "sp-icon-layers", label: "Layers", value: "layers" },
98
- { icon: "sp-icon-box", label: "Imports", value: "imports" },
99
- { icon: "sp-icon-view-grid", label: "Elements", value: "blocks" },
100
- { icon: "sp-icon-brackets", label: "State", value: "state" },
101
- { icon: "sp-icon-data", label: "Data", value: "data" },
102
- { icon: "sp-icon-view-all-tags", label: "Document", value: "head" },
103
- { icon: "sp-icon-git-branch", label: "Source Control", value: "git" },
104
- ];
105
- const tpl = html`
106
- <sp-tabs
107
- selected=${view.leftPanelCollapsed ? "" : leftTab}
108
- direction="vertical"
109
- quiet
110
- @change=${(e: Event) => {
111
- const clicked = (e.target as HTMLElement & { selected: string }).selected;
112
- if (clicked === view.leftTab && !view.leftPanelCollapsed) {
113
- view.leftPanelCollapsed = true;
114
- applyPanelCollapse();
115
- } else {
116
- view.leftTab = clicked;
117
- view.leftPanelCollapsed = false;
118
- applyPanelCollapse();
119
- renderOnly("leftPanel");
120
- }
121
- renderActivityBar();
101
+ /**
102
+ * Whether a rail button is showing what it names — its dock open, on its tab.
103
+ *
104
+ * The rail spans two docks (plan §7.2 puts Problems in the Bottom dock and its badge on the rail),
105
+ * so "selected" is a question about the panel's OWN host, not about `shell.leftTab`. Reading only
106
+ * the Navigator here is what would draw Problems as unselected while it is on screen.
107
+ *
108
+ * @param {PanelRecord} panel
109
+ */
110
+ export function isRailPanelShowing(panel: PanelRecord): boolean {
111
+ return !shell.docks.left.collapsed && shell.leftTab === panel.id;
112
+ }
113
+
114
+ /**
115
+ * Reveal (or collapse) what a rail button names.
116
+ *
117
+ * There is no per-dock branch, because every rail button opens the Navigator. There WAS one — the
118
+ * rail spanned two docks so that Problems could keep a button while its body was drawn in the
119
+ * Bottom dock — and it cost a branch here, a branch in {@link isRailPanelShowing} and a third in
120
+ * `focusPanel`, all to make one button behave like the other seven. A control on the left that
121
+ * opens something at the bottom is also a lie about where it will take you. Problems is `rail:
122
+ * false` now and is addressed by `view.setBottomTab`, like the three tabs beside it.
123
+ *
124
+ * @param {PanelRecord} panel
125
+ */
126
+ export function toggleRailPanel(panel: PanelRecord): void {
127
+ // `PanelRecord.id` is a `string` because the same registry hosts the Bottom dock's panels; the
128
+ // Rail only ever draws Navigator ones, and `requireNavigatorPanelId` is the shared lock that says
129
+ // So out loud rather than letting an undeclared id reach `shell.leftTab` (which is how the
130
+ // Outline's empty state spent three phases opening a panel that did not exist).
131
+ toggleActivityTab(requireNavigatorPanelId(panel.id, "the Navigator rail"));
132
+ }
133
+
134
+ /**
135
+ * One rail button.
136
+ *
137
+ * The label is REAL TEXT, not a `title` attribute — so it is the accessible name, it is readable
138
+ * without a pointer, and it survives a screenshot. `aria-pressed` states the toggle-focus semantics
139
+ * honestly: re-picking the open panel collapses its dock (see {@link toggleRailPanel}), which is a
140
+ * two-state control, not a one-way selection.
141
+ *
142
+ * `title` survives as a TOOLTIP only. Because the button has text content, that text is its
143
+ * accessible name and the attribute adds no second announcement (the reason the old `sp-tab` could
144
+ * not carry both) — it exists to restore the full string for "Source Control", which 56px ellipses.
145
+ * An icon whose only name is a tooltip is what principle 6 bans; a tooltip beside a label is not.
146
+ */
147
+ function railButton(panel: PanelRecord, ctx: CommandContext): TemplateResult {
148
+ const badge = panel.badge?.(ctx) ?? null;
149
+ const selected = isRailPanelShowing(panel);
150
+ return html`
151
+ <button
152
+ type="button"
153
+ class="rail-item${selected ? " selected" : ""}"
154
+ data-panel=${panel.id}
155
+ aria-pressed=${selected}
156
+ title=${panel.title}
157
+ @click=${() => {
158
+ // No repaint call: the rail, the Navigator and the Bottom dock all track `shell`, so
159
+ // Selecting a panel is one state write.
160
+ toggleRailPanel(panel);
122
161
  }}
123
162
  >
124
- ${tabs.map(
125
- (t) => html`
126
- <sp-tab value=${t.value} title=${t.label} aria-label=${t.label}>
127
- ${tabIcon(t.icon, "m")}
128
- ${
129
- t.value === "git" && gitFileCount > 0
130
- ? html`<span class="activity-badge">${gitFileCount}</span>`
131
- : nothing
132
- }
133
- </sp-tab>
163
+ <span class="rail-icon">${tabIcon(panel.icon, "m")}</span>
164
+ <span class="rail-label">${panel.title}</span>
165
+ ${badge ? html`<span class="activity-badge">${badge}</span>` : nothing}
166
+ </button>
167
+ `;
168
+ }
169
+
170
+ /**
171
+ * The rail foot: **Preferences**, and only Preferences (plan §3.2 ②).
172
+ *
173
+ * It was two hand-authored buttons calling two module functions directly — the exact shape §2
174
+ * principle 1 bans, and the reason the gear opened PROJECT configuration from an application-level
175
+ * slot. Preferences is the application's own settings and follows the author between projects, so
176
+ * it is the one that earns the pinned position; project configuration is `settings.open`, which
177
+ * lives in the ⬢ menu and the palette because it is scoped to a project; and About is `help.about`,
178
+ * reachable by name.
179
+ *
180
+ * Rendered FROM the record, so the label and the chord in its tooltip cannot drift from ⌘,.
181
+ */
182
+ function railFooterTpl(): TemplateResult {
183
+ const registry = activeRegistry();
184
+ const command = registry?.get("app.preferences");
185
+ if (!registry || !command || !registry.isVisible("app.preferences")) {
186
+ return html`${nothing}`;
187
+ }
188
+ const chord = registry.keymap.formatBinding("app.preferences");
189
+ return html`
190
+ <button
191
+ type="button"
192
+ class="rail-item"
193
+ data-command="app.preferences"
194
+ title=${chord ? `${command.title} (${chord})` : command.title}
195
+ @click=${() => {
196
+ void registry.run("app.preferences");
197
+ }}
198
+ >
199
+ <span class="rail-icon"><sp-icon-settings size="m"></sp-icon-settings></span>
200
+ <span class="rail-label">Preferences</span>
201
+ </button>
202
+ `;
203
+ }
204
+
205
+ export function renderActivityBar() {
206
+ const ctx = panelContext();
207
+ const groups = railGroups(ctx);
208
+ const tpl = html`
209
+ <nav class="rail-groups" aria-label="Navigator panels">
210
+ ${groups.map(
211
+ (group, index) => html`
212
+ ${index === 0 ? nothing : html`<div class="rail-divider" role="separator"></div>`}
213
+ <div class="rail-group" role="group" aria-label=${group.label}>
214
+ ${group.panels.map((panel) => railButton(panel, ctx))}
215
+ </div>
134
216
  `,
135
217
  )}
136
- </sp-tabs>
137
- <div
138
- style="margin-top:auto;padding:8px 0;display:flex;flex-direction:column;align-items:center;gap:4px"
139
- >
140
- <sp-action-button
141
- quiet
142
- size="m"
143
- title="About"
144
- aria-label="About"
145
- @click=${() => openAboutModal()}
146
- >
147
- <sp-icon-info slot="icon"></sp-icon-info>
148
- </sp-action-button>
149
- <sp-action-button
150
- quiet
151
- size="m"
152
- title="Settings"
153
- aria-label="Settings"
154
- @click=${() => openSettingsModal()}
155
- >
156
- <sp-icon-settings slot="icon"></sp-icon-settings>
157
- </sp-action-button>
158
- </div>
218
+ </nav>
219
+ <div class="rail-footer">${railFooterTpl()}</div>
159
220
  `;
160
221
  litRender(tpl, activityBar);
161
222
  }