@jxsuite/studio 4.1.0 → 5.0.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 (607) hide show
  1. package/README.md +1 -1
  2. package/data/elements-meta.json +5 -15
  3. package/data/html-meta.json +51 -6
  4. package/dist/chunks/{2020-n6tfwnda.js → 2020-g79n01v9.js} +14 -14
  5. package/dist/chunks/action-group-sp6tna8v.js +118 -0
  6. package/dist/chunks/adapter-nj7tnjfn.js +13 -0
  7. package/dist/chunks/cf-account-picker-fyf63pmy.js +495 -0
  8. package/dist/chunks/cf-settings-71bv5zn9.js +20 -0
  9. package/dist/chunks/color-area-v17df7b8.js +154 -0
  10. package/dist/chunks/color-field-anwybnkn.js +208 -0
  11. package/dist/chunks/color-slider-44ez96fc.js +79 -0
  12. package/dist/chunks/color-xgg3kfw7.js +35 -0
  13. package/dist/chunks/combobox-5e7jev23.js +236 -0
  14. package/dist/chunks/components-2w1f35qs.js +18 -0
  15. package/dist/chunks/content-source-3wqeas45.js +46 -0
  16. package/dist/chunks/convert-to-repeater-d0cgnb6d.js +639 -0
  17. package/dist/chunks/dialog-s890b918.js +85 -0
  18. package/dist/chunks/editor-3qhbg51x.js +35 -0
  19. package/dist/chunks/entry-commands-d6g50j7s.js +42 -0
  20. package/dist/chunks/extension-sections-fy7q4fe9.js +651 -0
  21. package/dist/chunks/file-ops-3btj6st0.js +47 -0
  22. package/dist/chunks/files-ef8fgs3w.js +72 -0
  23. package/dist/chunks/format-host-3c33nwr8.js +40 -0
  24. package/dist/chunks/fs-events-13tj6sex.js +18 -0
  25. package/dist/chunks/git-panel-4wp2rn9b.js +49 -0
  26. package/dist/chunks/icons-yy10wk9r.js +402 -0
  27. package/dist/chunks/iframe-entry-15g3n6xv.js +114 -0
  28. package/dist/chunks/iframe-entry-b27vq81n.js +203 -0
  29. package/dist/chunks/iframe-entry-nbyxee1y.js +154 -0
  30. package/dist/chunks/iframe-host-jgesd924.js +115 -0
  31. package/dist/chunks/{index-wcnt3r9g.js → index-mdmyx6s2.js} +11 -11
  32. package/dist/chunks/index-n4j7qxyr.js +109 -0
  33. package/dist/chunks/{javascript-q8kpmfah.js → javascript-xvrfrpkb.js} +8 -9
  34. package/dist/chunks/{jsonMode-dkr040ga.js → jsonMode-tyx2kj6p.js} +526 -527
  35. package/dist/chunks/jx-validate-j0wxk72w.js +15 -0
  36. package/dist/chunks/layers-1nq5evsh.js +29 -0
  37. package/dist/chunks/layers-panel-ar2gh90g.js +40 -0
  38. package/dist/chunks/listbox-6v2ac08j.js +44 -0
  39. package/dist/chunks/lit-html-m244w9nh.js +17 -0
  40. package/dist/chunks/media-picker-2qbetq73.js +33 -0
  41. package/dist/chunks/menu-q99sy9rf.js +285 -0
  42. package/dist/chunks/menu-rf7n72mf.js +14 -0
  43. package/dist/chunks/{monaco-binding-rj6xpx9y.js → monaco-binding-sw6en4dw.js} +12 -15
  44. package/dist/chunks/monaco-buffer-bhy3hb6m.js +24 -0
  45. package/dist/chunks/{monaco-setup-vx9bhp1d.js → monaco-setup-8r62fj6y.js} +27 -37
  46. package/dist/chunks/notify-628t0anh.js +21 -0
  47. package/dist/chunks/number-field-5cndyft8.js +72 -0
  48. package/dist/chunks/parse-0zx14y15.js +10 -0
  49. package/dist/chunks/platform-ts9cg1a4.js +15 -0
  50. package/dist/chunks/popover-47z3ytva.js +25 -0
  51. package/dist/chunks/publish-panel-98f7sr3g.js +27 -0
  52. package/dist/chunks/quick-search-srzz83k2.js +45 -0
  53. package/dist/chunks/references-n553f0e1.js +26 -0
  54. package/dist/chunks/right-panel-rrgx2dn3.js +45 -0
  55. package/dist/chunks/section-registry-5pq32t0q.js +25 -0
  56. package/dist/chunks/select-2r16ez41.js +49 -0
  57. package/dist/chunks/split-g7nm1rgb.js +191 -0
  58. package/dist/chunks/studio-0hqbpbd0.js +62 -0
  59. package/dist/chunks/studio-18avnhjp.js +2430 -0
  60. package/dist/chunks/{studio-kryvsw6j.js → studio-1k325pwf.js} +48 -48
  61. package/dist/chunks/{studio-msd847zn.js → studio-25hkdrkw.js} +357 -86
  62. package/dist/chunks/studio-26d7myd1.js +258 -0
  63. package/dist/chunks/{studio-4r1pv6zf.js → studio-38emantv.js} +22 -5
  64. package/dist/chunks/studio-3gxv60tn.js +422 -0
  65. package/dist/chunks/{studio-fbf92ckf.js → studio-3hxkd92t.js} +275 -163
  66. package/dist/chunks/studio-4m0haseg.js +185 -0
  67. package/dist/chunks/{studio-ynxwkrzd.js → studio-4wbb9ct0.js} +1953 -1785
  68. package/dist/chunks/{studio-1wagb550.js → studio-5523bpzt.js} +11 -11
  69. package/dist/chunks/{studio-x4qkdat5.js → studio-57mf66w2.js} +1749 -2481
  70. package/dist/chunks/studio-5jy81t3s.js +17463 -0
  71. package/dist/chunks/studio-5rt7sqa0.js +1168 -0
  72. package/dist/chunks/studio-5te5tqng.js +81 -0
  73. package/dist/chunks/{studio-p4ngc046.js → studio-6ewqv390.js} +9 -9
  74. package/dist/chunks/{studio-9ab8gqsm.js → studio-6nzaktvy.js} +7 -7
  75. package/dist/chunks/studio-6xa21ej9.js +28 -0
  76. package/dist/chunks/{studio-5ehhackm.js → studio-727xh0e3.js} +19 -19
  77. package/dist/chunks/{studio-ahn3pdgx.js → studio-79y5sz51.js} +52 -38
  78. package/dist/chunks/{studio-gcye7qka.js → studio-7e11jpa9.js} +21 -21
  79. package/dist/chunks/{studio-q7v10cg2.js → studio-7r42c6ca.js} +17 -7
  80. package/dist/chunks/studio-7xptrg0j.js +954 -0
  81. package/dist/chunks/{studio-cft9r4r7.js → studio-81390n6f.js} +22 -22
  82. package/dist/chunks/{studio-q43a5y9h.js → studio-8jn5yhyn.js} +16 -16
  83. package/dist/chunks/studio-8qtrpzr3.js +839 -0
  84. package/dist/chunks/{studio-dg7zyt5x.js → studio-98b3z07n.js} +1 -1
  85. package/dist/chunks/studio-9xxz1q69.js +1356 -0
  86. package/dist/chunks/studio-akbww0j5.js +13615 -0
  87. package/dist/chunks/{studio-x813zp48.js → studio-arfpe389.js} +30 -30
  88. package/dist/chunks/{studio-trxrabqf.js → studio-az3zd49t.js} +475 -283
  89. package/dist/chunks/studio-cpvb2ed1.js +1171 -0
  90. package/dist/chunks/{studio-f3wqjtk5.js → studio-d0300636.js} +5076 -18
  91. package/dist/chunks/{studio-bgxt93ga.js → studio-d055c37e.js} +13 -13
  92. package/dist/chunks/studio-d1ja2x9r.js +362 -0
  93. package/dist/chunks/{studio-b9b6t1dg.js → studio-e1f24eff.js} +5664 -5666
  94. package/dist/chunks/{studio-3bqpr0zh.js → studio-em9nw8eh.js} +299 -110
  95. package/dist/chunks/studio-erdatzvp.js +1190 -0
  96. package/dist/chunks/{studio-g1wdche0.js → studio-esrxgwh6.js} +80 -80
  97. package/dist/chunks/studio-fabxk6r9.js +2811 -0
  98. package/dist/chunks/{studio-3n4ndrzh.js → studio-h4nvnjvz.js} +303 -303
  99. package/dist/chunks/{studio-dm19q43r.js → studio-hhvs4sdg.js} +10 -10
  100. package/dist/chunks/{studio-q9jd1a60.js → studio-kcgff4r0.js} +15 -18
  101. package/dist/chunks/{studio-685s0ytq.js → studio-nqgawfks.js} +572 -572
  102. package/dist/chunks/{studio-xrapxwwc.js → studio-p1dw8wfw.js} +55 -29
  103. package/dist/chunks/studio-pm6hgy7r.js +4763 -0
  104. package/dist/chunks/studio-q0a3yk4n.js +33 -0
  105. package/dist/chunks/{studio-wcy8trfx.js → studio-q3x06vmj.js} +23 -22
  106. package/dist/chunks/studio-qahxvnan.js +111 -0
  107. package/dist/chunks/{studio-53n8bp04.js → studio-r6xmmtfh.js} +1 -1
  108. package/dist/chunks/{studio-2b946z26.js → studio-rkstz940.js} +3658 -6134
  109. package/dist/chunks/{studio-tvgyw9xw.js → studio-rnyf3f8e.js} +83 -250
  110. package/dist/chunks/studio-rpyah2ew.js +2805 -0
  111. package/dist/chunks/studio-rs76k67r.js +12 -0
  112. package/dist/chunks/{studio-jkftxyvz.js → studio-s6z853aq.js} +5858 -2152
  113. package/dist/chunks/{studio-6yw4y3my.js → studio-sgdc5pfc.js} +8 -8
  114. package/dist/chunks/studio-tb3ev01j.js +763 -0
  115. package/dist/chunks/{studio-t5wqwzdh.js → studio-tjjk7jvk.js} +1 -1
  116. package/dist/chunks/studio-vhv5tgxm.js +51 -0
  117. package/dist/chunks/{studio-qjy2z9bv.js → studio-vz68yn9e.js} +32 -137
  118. package/dist/chunks/studio-wqpngpsk.js +174 -0
  119. package/dist/chunks/{studio-b55y6ewx.js → studio-x3rty6tr.js} +58 -58
  120. package/dist/chunks/studio-xsykpvsh.js +8448 -0
  121. package/dist/chunks/{studio-92ab2jqe.js → studio-y9zedyjn.js} +14 -14
  122. package/dist/chunks/studio-yxa81f45.js +12 -0
  123. package/dist/chunks/swatch-group-t0n6h2rn.js +139 -0
  124. package/dist/chunks/tabs-wbp1cdws.js +156 -0
  125. package/dist/chunks/textfield-abk6qyee.js +73 -0
  126. package/dist/chunks/toast-780pvfbv.js +27 -0
  127. package/dist/chunks/toast-host-3sqv46sn.js +116 -0
  128. package/dist/chunks/toolbar-xbh5bhtr.js +194 -0
  129. package/dist/chunks/tooltip-eptb614w.js +369 -0
  130. package/dist/chunks/tree-a73p0pqm.js +367 -0
  131. package/dist/chunks/{tsMode-76kr53q0.js → tsMode-jy4m86rv.js} +83 -85
  132. package/dist/chunks/workspace-wrew8zk4.js +69 -0
  133. package/dist/chunks/{ws-client-6shc5rze.js → ws-client-ny4zc51r.js} +20 -22
  134. package/dist/iframe-entry.js +1780 -667
  135. package/dist/studio.js +52819 -36480
  136. package/dist/workers/editor.worker.js +1040 -1085
  137. package/dist/workers/json.worker.js +1946 -1991
  138. package/dist/workers/ts.worker.js +4960 -5005
  139. package/index.html +1 -0
  140. package/package.json +18 -52
  141. package/src/about/about-modal.ts +72 -123
  142. package/src/browse/library-commands.ts +20 -49
  143. package/src/browse/library-layouts.ts +87 -218
  144. package/src/browse/library-pane.ts +406 -438
  145. package/src/canvas/canvas-helpers.ts +0 -49
  146. package/src/canvas/canvas-live-render.ts +25 -2
  147. package/src/canvas/canvas-render.ts +666 -231
  148. package/src/canvas/canvas-surface.ts +0 -1
  149. package/src/canvas/canvas-utils.ts +310 -117
  150. package/src/canvas/dialog-path.ts +76 -0
  151. package/src/canvas/dialog-state.ts +67 -0
  152. package/src/canvas/diff-marks.ts +252 -0
  153. package/src/canvas/diff-toolbar.ts +349 -0
  154. package/src/canvas/diff-view.ts +123 -0
  155. package/src/canvas/edit-width-drag.ts +187 -0
  156. package/src/canvas/edit-width.ts +150 -0
  157. package/src/canvas/iframe-entry.ts +125 -8
  158. package/src/canvas/iframe-host.ts +271 -15
  159. package/src/canvas/iframe-interaction.ts +133 -13
  160. package/src/canvas/iframe-patch.ts +7 -1
  161. package/src/canvas/iframe-position.ts +5 -6
  162. package/src/canvas/iframe-protocol.ts +106 -5
  163. package/src/canvas/iframe-render.ts +373 -5
  164. package/src/canvas/iframe-slash.ts +2 -2
  165. package/src/canvas/iframe-subtree.ts +5 -8
  166. package/src/canvas/path-mapping.ts +25 -3
  167. package/src/canvas/popover-path.ts +106 -0
  168. package/src/canvas/popover-state.ts +155 -0
  169. package/src/canvas/surface-registry.ts +21 -10
  170. package/src/collab/presence-chips.ts +51 -73
  171. package/src/commands/app-commands.ts +10 -0
  172. package/src/commands/budget.ts +12 -0
  173. package/src/commands/command-args.ts +369 -6
  174. package/src/commands/defaults.ts +44 -14
  175. package/src/commands/live-context.ts +11 -5
  176. package/src/commands/reference.ts +12 -2
  177. package/src/commands/registry.ts +165 -8
  178. package/src/content/entry-commands.ts +16 -23
  179. package/src/content/entry-editor.ts +144 -101
  180. package/src/editor/canvas-slash-bridge.ts +2 -2
  181. package/src/editor/context-menu.ts +49 -200
  182. package/src/editor/convert-to-component.ts +42 -82
  183. package/src/editor/convert-to-repeater.ts +132 -173
  184. package/src/editor/inline-edit.ts +11 -0
  185. package/src/editor/shortcuts.ts +65 -26
  186. package/src/editor/slash-menu.ts +150 -157
  187. package/src/files/file-ops.ts +117 -11
  188. package/src/files/files.ts +943 -660
  189. package/src/files/json-layout.ts +474 -0
  190. package/src/files/serialize-document.ts +8 -1
  191. package/src/format/convert-file.ts +16 -10
  192. package/src/format/format-host.ts +50 -1
  193. package/src/github/github-auth.ts +71 -29
  194. package/src/github/github-publish.ts +57 -65
  195. package/src/grid/cell-editors.ts +46 -4
  196. package/src/grid/cell-popovers.ts +65 -62
  197. package/src/grid/grid-open.ts +86 -64
  198. package/src/grid/grid-panel.ts +390 -469
  199. package/src/grid/redirects-grid.ts +40 -44
  200. package/src/hosting/layout.ts +2 -0
  201. package/src/i18n/i18n-commands.ts +127 -62
  202. package/src/media/media-pane.ts +83 -133
  203. package/src/new-project/add-repo-modal.ts +125 -176
  204. package/src/new-project/import-tab.ts +181 -252
  205. package/src/new-project/location-fields.ts +93 -160
  206. package/src/new-project/new-project-modal.ts +376 -367
  207. package/src/packages/jxsuite-update.ts +76 -18
  208. package/src/panels/activity-panel.ts +137 -104
  209. package/src/panels/ai-chat/chat-markdown.ts +15 -13
  210. package/src/panels/ai-chat/chat-view.ts +307 -472
  211. package/src/panels/ai-chat/composer.ts +175 -163
  212. package/src/panels/ai-chat/sessions-view.ts +31 -62
  213. package/src/panels/ai-panel.ts +210 -185
  214. package/src/panels/block-action-bar.ts +437 -643
  215. package/src/panels/bottom-dock.ts +112 -47
  216. package/src/panels/chat-panel.ts +19 -14
  217. package/src/panels/data-explorer.ts +141 -102
  218. package/src/panels/data-grid.ts +183 -150
  219. package/src/panels/dnd.ts +75 -20
  220. package/src/panels/editors.ts +26 -19
  221. package/src/panels/elements-panel.ts +243 -186
  222. package/src/panels/empty-state.ts +26 -42
  223. package/src/panels/events-panel.ts +950 -729
  224. package/src/panels/formula-workspace.ts +434 -207
  225. package/src/panels/frontmatter-fields.ts +185 -153
  226. package/src/panels/frontmatter-panel.ts +411 -187
  227. package/src/panels/git-diff-open.ts +148 -0
  228. package/src/panels/git-panel.ts +679 -605
  229. package/src/panels/head-panel.ts +617 -435
  230. package/src/panels/i18n-panel.ts +153 -133
  231. package/src/panels/imports-panel.ts +388 -414
  232. package/src/panels/jump-bar.ts +174 -147
  233. package/src/panels/layers-panel.ts +785 -671
  234. package/src/panels/left-panel.ts +166 -94
  235. package/src/panels/navigator-panels.ts +1 -1
  236. package/src/panels/pane-context.ts +529 -677
  237. package/src/panels/pane-grid.ts +265 -241
  238. package/src/panels/panel-registry.ts +15 -4
  239. package/src/panels/problems-panel.ts +177 -99
  240. package/src/panels/properties-panel.ts +1525 -966
  241. package/src/panels/provenance.ts +31 -113
  242. package/src/panels/quick-search.ts +217 -172
  243. package/src/panels/right-panel.ts +135 -169
  244. package/src/panels/seo-modal.ts +346 -167
  245. package/src/panels/settings-menu.ts +109 -646
  246. package/src/panels/settings-pane.ts +102 -86
  247. package/src/panels/signals-panel.ts +1318 -1172
  248. package/src/panels/statement-editor.ts +556 -428
  249. package/src/panels/style-panel.ts +1367 -630
  250. package/src/panels/style-utils.ts +36 -32
  251. package/src/panels/stylebook-doc.ts +30 -73
  252. package/src/panels/stylebook-layers-panel.ts +202 -109
  253. package/src/panels/stylebook-panel.ts +124 -88
  254. package/src/panels/tab-strip.ts +385 -287
  255. package/src/platforms/cloud.ts +106 -16
  256. package/src/platforms/devserver.ts +13 -0
  257. package/src/publish/deploy-checklist.ts +124 -78
  258. package/src/publish/publish-commands.ts +4 -12
  259. package/src/publish/publish-panel.ts +134 -328
  260. package/src/services/a11y-report.ts +100 -43
  261. package/src/services/ai-command-tools.ts +468 -0
  262. package/src/services/ai-session-store.ts +6 -0
  263. package/src/services/ai-system-prompt.ts +110 -18
  264. package/src/services/ai-tools.ts +28 -143
  265. package/src/services/ai-write-report.ts +174 -0
  266. package/src/services/document-assistant.ts +50 -2
  267. package/src/services/idle.ts +16 -14
  268. package/src/services/live-surfaces.ts +237 -0
  269. package/src/services/model-uri.ts +75 -0
  270. package/src/services/notify.ts +63 -6
  271. package/src/services/popover-report.ts +325 -0
  272. package/src/services/surface-registry.ts +56 -0
  273. package/src/services/tool-executor.ts +6 -0
  274. package/src/settings/contexts-section.ts +301 -299
  275. package/src/settings/contributed-section.ts +361 -222
  276. package/src/settings/css-vars-editor.ts +320 -325
  277. package/src/settings/defs-editor.ts +572 -535
  278. package/src/settings/dependencies-editor.ts +79 -107
  279. package/src/settings/extension-commands.ts +379 -0
  280. package/src/settings/extension-rows.ts +113 -0
  281. package/src/settings/extensions-section.ts +287 -0
  282. package/src/settings/general-settings.ts +130 -181
  283. package/src/settings/head-editor.ts +10 -305
  284. package/src/settings/locales-section.ts +146 -196
  285. package/src/settings/preferences-accounts.ts +1 -1
  286. package/src/settings/preferences-dialog.ts +350 -330
  287. package/src/settings/project-sections.ts +108 -181
  288. package/src/settings/schema-field-ui.ts +11 -399
  289. package/src/settings/section-registry.ts +8 -1
  290. package/src/settings/settings-document.ts +31 -29
  291. package/src/shell/tree.ts +42 -87
  292. package/src/shell.ts +48 -36
  293. package/src/studio.ts +90 -67
  294. package/src/style/token-ref.ts +0 -39
  295. package/src/surfaces/about.json +263 -0
  296. package/src/surfaces/about.ts +163 -0
  297. package/src/surfaces/add-repo.json +486 -0
  298. package/src/surfaces/add-repo.ts +236 -0
  299. package/src/surfaces/ai-chat.json +2185 -0
  300. package/src/surfaces/ai-chat.ts +549 -0
  301. package/src/surfaces/ai-credentials-form.json +332 -0
  302. package/src/surfaces/ai-credentials-form.ts +151 -0
  303. package/src/surfaces/ai-managed-connect.json +108 -0
  304. package/src/surfaces/ai-managed-connect.ts +123 -0
  305. package/src/surfaces/ai-model-picker.json +114 -0
  306. package/src/surfaces/ai-model-picker.ts +139 -0
  307. package/src/surfaces/block-action-bar.json +640 -0
  308. package/src/surfaces/block-action-bar.ts +368 -0
  309. package/src/surfaces/bottom-dock.json +154 -0
  310. package/src/surfaces/bottom-dock.ts +125 -0
  311. package/src/surfaces/canvas-stage.json +444 -0
  312. package/src/surfaces/canvas-stage.ts +253 -0
  313. package/src/surfaces/cf-account-picker.json +300 -0
  314. package/src/surfaces/cf-account-picker.ts +214 -0
  315. package/src/surfaces/commandbar.json +984 -0
  316. package/src/{panels/toolbar.ts → surfaces/commandbar.ts} +414 -389
  317. package/src/surfaces/convert-repeater.json +309 -0
  318. package/src/surfaces/convert-repeater.ts +276 -0
  319. package/src/surfaces/data-actions.json +157 -0
  320. package/src/surfaces/data-actions.ts +115 -0
  321. package/src/surfaces/dialog.json +289 -0
  322. package/src/surfaces/dialog.ts +343 -0
  323. package/src/surfaces/diff-toolbar.json +266 -0
  324. package/src/surfaces/diff-toolbar.ts +139 -0
  325. package/src/surfaces/doc-header.json +739 -0
  326. package/src/surfaces/doc-header.ts +223 -0
  327. package/src/surfaces/empty-state.json +152 -0
  328. package/src/surfaces/empty-state.ts +173 -0
  329. package/src/surfaces/entry-editor.json +281 -0
  330. package/src/surfaces/entry-editor.ts +191 -0
  331. package/src/surfaces/expression-editor.json +977 -0
  332. package/src/surfaces/expression-editor.ts +266 -0
  333. package/src/surfaces/files-panel.json +592 -0
  334. package/src/surfaces/files-panel.ts +301 -0
  335. package/src/surfaces/formula-palette.json +364 -0
  336. package/src/surfaces/formula-palette.ts +340 -0
  337. package/src/surfaces/git-panel.json +1822 -0
  338. package/src/surfaces/git-panel.ts +272 -0
  339. package/src/surfaces/github-auth.json +112 -0
  340. package/src/surfaces/github-auth.ts +139 -0
  341. package/src/surfaces/github-publish.json +206 -0
  342. package/src/surfaces/github-publish.ts +169 -0
  343. package/src/surfaces/grid-cell.json +218 -0
  344. package/src/surfaces/grid-cell.ts +120 -0
  345. package/src/surfaces/grid-open.json +147 -0
  346. package/src/surfaces/grid-open.ts +87 -0
  347. package/src/surfaces/grid-panel.json +706 -0
  348. package/src/surfaces/grid-panel.ts +206 -0
  349. package/src/surfaces/grid-replace.json +179 -0
  350. package/src/surfaces/grid-replace.ts +89 -0
  351. package/src/surfaces/grid-views.json +446 -0
  352. package/src/surfaces/grid-views.ts +172 -0
  353. package/src/surfaces/inspector-dock.json +187 -0
  354. package/src/surfaces/inspector-dock.ts +161 -0
  355. package/src/surfaces/jump-bar.json +243 -0
  356. package/src/surfaces/jump-bar.ts +129 -0
  357. package/src/surfaces/jxsuite-update.json +146 -0
  358. package/src/surfaces/jxsuite-update.ts +149 -0
  359. package/src/surfaces/library-pane.json +1509 -0
  360. package/src/surfaces/library-pane.ts +363 -0
  361. package/src/surfaces/logic-panel.json +1949 -0
  362. package/src/surfaces/logic-panel.ts +369 -0
  363. package/src/surfaces/logic-workspace.json +685 -0
  364. package/src/surfaces/logic-workspace.ts +238 -0
  365. package/src/surfaces/media-browser.json +303 -0
  366. package/src/surfaces/media-browser.ts +145 -0
  367. package/src/surfaces/media-field.json +163 -0
  368. package/src/surfaces/media-field.ts +116 -0
  369. package/src/surfaces/media-pane.json +513 -0
  370. package/src/surfaces/media-pane.ts +197 -0
  371. package/src/surfaces/menu.json +232 -0
  372. package/src/surfaces/menu.ts +375 -0
  373. package/src/surfaces/navigator-dock.json +113 -0
  374. package/src/surfaces/navigator-dock.ts +130 -0
  375. package/src/surfaces/new-project.json +1654 -0
  376. package/src/surfaces/new-project.ts +411 -0
  377. package/src/surfaces/palette.json +535 -0
  378. package/src/surfaces/pane-context.json +1497 -0
  379. package/src/surfaces/pane-context.ts +513 -0
  380. package/src/surfaces/pane-grid.json +188 -0
  381. package/src/surfaces/pane-grid.ts +250 -0
  382. package/src/surfaces/panel-activity.json +563 -0
  383. package/src/surfaces/panel-activity.ts +187 -0
  384. package/src/surfaces/panel-data.json +141 -0
  385. package/src/surfaces/panel-data.ts +156 -0
  386. package/src/surfaces/panel-deploy-checklist.json +283 -0
  387. package/src/surfaces/panel-deploy-checklist.ts +175 -0
  388. package/src/surfaces/panel-elements.json +367 -0
  389. package/src/surfaces/panel-elements.ts +175 -0
  390. package/src/surfaces/panel-i18n.json +569 -0
  391. package/src/surfaces/panel-i18n.ts +212 -0
  392. package/src/surfaces/panel-imports.json +780 -0
  393. package/src/surfaces/panel-imports.ts +209 -0
  394. package/src/surfaces/panel-outline.json +734 -0
  395. package/src/surfaces/panel-outline.ts +326 -0
  396. package/src/surfaces/panel-page.json +1017 -0
  397. package/src/surfaces/panel-page.ts +240 -0
  398. package/src/surfaces/panel-problems.json +497 -0
  399. package/src/surfaces/panel-problems.ts +157 -0
  400. package/src/surfaces/panel-signals.json +1973 -0
  401. package/src/surfaces/panel-signals.ts +436 -0
  402. package/src/surfaces/panel-stylebook-layers.json +317 -0
  403. package/src/surfaces/panel-stylebook-layers.ts +155 -0
  404. package/src/surfaces/preferences.json +850 -0
  405. package/src/surfaces/preferences.ts +332 -0
  406. package/src/surfaces/progress-modal.json +110 -0
  407. package/src/surfaces/progress-modal.ts +150 -0
  408. package/src/surfaces/properties-panel.json +1578 -0
  409. package/src/surfaces/properties-panel.ts +337 -0
  410. package/src/surfaces/publish.json +945 -0
  411. package/src/surfaces/publish.ts +392 -0
  412. package/src/surfaces/push-plan.json +110 -0
  413. package/src/surfaces/push-plan.ts +87 -0
  414. package/src/surfaces/rail.json +255 -0
  415. package/src/surfaces/rail.ts +206 -0
  416. package/src/surfaces/reference-field.json +191 -0
  417. package/src/surfaces/reference-field.ts +168 -0
  418. package/src/surfaces/schema-builder.json +1011 -0
  419. package/src/surfaces/schema-builder.ts +181 -0
  420. package/src/surfaces/schema-form.json +1013 -0
  421. package/src/surfaces/schema-form.ts +287 -0
  422. package/src/surfaces/secret-field.json +60 -0
  423. package/src/surfaces/secret-field.ts +146 -0
  424. package/src/surfaces/seo.json +932 -0
  425. package/src/surfaces/seo.ts +334 -0
  426. package/src/surfaces/settings-contexts.json +538 -0
  427. package/src/surfaces/settings-contexts.ts +196 -0
  428. package/src/surfaces/settings-contributed.json +489 -0
  429. package/src/surfaces/settings-contributed.ts +199 -0
  430. package/src/surfaces/settings-css-vars.json +820 -0
  431. package/src/surfaces/settings-css-vars.ts +199 -0
  432. package/src/surfaces/settings-defs.json +1363 -0
  433. package/src/surfaces/settings-defs.ts +232 -0
  434. package/src/surfaces/settings-deploy.json +108 -0
  435. package/src/surfaces/settings-deploy.ts +135 -0
  436. package/src/surfaces/settings-extensions.json +486 -0
  437. package/src/surfaces/settings-extensions.ts +201 -0
  438. package/src/surfaces/settings-head.json +779 -0
  439. package/src/surfaces/settings-head.ts +397 -0
  440. package/src/surfaces/settings-locales.json +420 -0
  441. package/src/surfaces/settings-locales.ts +137 -0
  442. package/src/surfaces/settings-overview.json +423 -0
  443. package/src/surfaces/settings-overview.ts +218 -0
  444. package/src/surfaces/settings-packages.json +549 -0
  445. package/src/surfaces/settings-packages.ts +158 -0
  446. package/src/surfaces/settings-pane.json +147 -0
  447. package/src/surfaces/settings-pane.ts +150 -0
  448. package/src/surfaces/settings-rawjson.json +104 -0
  449. package/src/surfaces/settings-rawjson.ts +107 -0
  450. package/src/surfaces/shell.json +99 -0
  451. package/src/surfaces/shell.ts +30 -0
  452. package/src/surfaces/slash-menu.json +180 -0
  453. package/src/surfaces/slash-menu.ts +272 -0
  454. package/src/surfaces/statements.json +607 -0
  455. package/src/surfaces/statements.ts +238 -0
  456. package/src/surfaces/statusbar.json +94 -0
  457. package/src/{panels → surfaces}/statusbar.ts +163 -90
  458. package/src/surfaces/style-panel.json +1643 -0
  459. package/src/surfaces/style-panel.ts +338 -0
  460. package/src/surfaces/stylebook-chrome.json +122 -0
  461. package/src/surfaces/stylebook-chrome.ts +122 -0
  462. package/src/surfaces/tab-strip.json +448 -0
  463. package/src/surfaces/tab-strip.ts +200 -0
  464. package/src/surfaces/target-line.json +419 -0
  465. package/src/surfaces/target-line.ts +349 -0
  466. package/src/surfaces/toasts.json +189 -0
  467. package/src/surfaces/welcome.json +571 -0
  468. package/src/surfaces/welcome.ts +338 -0
  469. package/src/tabs/project-config.ts +71 -15
  470. package/src/tabs/tab.ts +52 -1
  471. package/src/types.ts +22 -0
  472. package/src/ui/ai-credentials-form.ts +104 -100
  473. package/src/ui/ai-managed-connect.ts +54 -36
  474. package/src/ui/ai-model-picker.ts +75 -47
  475. package/src/ui/cf-account-picker.ts +76 -141
  476. package/src/ui/color-selector.ts +80 -301
  477. package/src/ui/dynamic-slot.ts +43 -283
  478. package/src/ui/expression-editor.ts +797 -576
  479. package/src/ui/field-input.ts +8 -181
  480. package/src/ui/form-controls.ts +838 -366
  481. package/src/ui/formula-chips.ts +80 -85
  482. package/src/ui/kit.ts +33 -0
  483. package/src/ui/layers.ts +293 -593
  484. package/src/ui/media-picker.ts +203 -284
  485. package/src/ui/panel-resize.ts +251 -64
  486. package/src/ui/popover-surface.ts +153 -0
  487. package/src/ui/progress-modal.ts +22 -60
  488. package/src/ui/regions.ts +13 -7
  489. package/src/ui/schema-form.ts +906 -440
  490. package/src/ui/surface.ts +122 -0
  491. package/src/ui/unit-selector.ts +15 -131
  492. package/src/ui/value-source.ts +14 -0
  493. package/src/ui/virtual-window.ts +88 -9
  494. package/src/utils/canvas-media.ts +72 -0
  495. package/src/utils/edit-display.ts +7 -2
  496. package/src/utils/element-selectors.ts +69 -0
  497. package/src/utils/link-target.ts +2 -2
  498. package/src/utils/studio-utils.ts +0 -10
  499. package/src/view.ts +15 -0
  500. package/src/workspace/pane-derive.ts +36 -14
  501. package/src/workspace/workspace.ts +15 -6
  502. package/styles/canvas.css +72 -467
  503. package/styles/forced-colors.css +48 -82
  504. package/styles/forced-colors.json +41 -0
  505. package/styles/inspector.css +2 -1301
  506. package/styles/overlays.css +96 -2823
  507. package/styles/panels.css +6 -1423
  508. package/styles/shell-frame.css +272 -0
  509. package/styles/shell-frame.json +227 -0
  510. package/styles/shell.css +0 -1568
  511. package/styles/tokens.css +65 -136
  512. package/styles/tokens.json +84 -0
  513. package/dist/chunks/Overlay.dev-etqjwyf9.js +0 -23
  514. package/dist/chunks/adapter-jkcyhgtr.js +0 -14
  515. package/dist/chunks/cf-account-picker-en86v7x7.js +0 -163
  516. package/dist/chunks/cf-settings-45fgyx8y.js +0 -21
  517. package/dist/chunks/components-zmx10t62.js +0 -21
  518. package/dist/chunks/content-source-48xg86wj.js +0 -49
  519. package/dist/chunks/convert-to-repeater-43tfn7cs.js +0 -268
  520. package/dist/chunks/editor-cptq5jp1.js +0 -37
  521. package/dist/chunks/entry-commands-e63f1xc0.js +0 -46
  522. package/dist/chunks/extension-sections-txv08xxg.js +0 -311
  523. package/dist/chunks/file-ops-wbh4hhsx.js +0 -48
  524. package/dist/chunks/files-3kfkwhk4.js +0 -93
  525. package/dist/chunks/flow-qbfgrpeh.js +0 -634
  526. package/dist/chunks/focus-trap.esm-9t2drp0g.js +0 -1025
  527. package/dist/chunks/focus-visible-6eg70z18.js +0 -167
  528. package/dist/chunks/format-host-w16q81tf.js +0 -43
  529. package/dist/chunks/fs-events-hrrg328g.js +0 -21
  530. package/dist/chunks/git-panel-532whr1y.js +0 -59
  531. package/dist/chunks/index-zqxmqxmh.js +0 -109
  532. package/dist/chunks/jx-validate-8er6gbxy.js +0 -19
  533. package/dist/chunks/layers-panel-07hm7g5j.js +0 -67
  534. package/dist/chunks/layers-q75tj2mf.js +0 -45
  535. package/dist/chunks/lit-html-j055mkh8.js +0 -22
  536. package/dist/chunks/monaco-buffer-dqw0xxpx.js +0 -25
  537. package/dist/chunks/notify-yjm79n4s.js +0 -30
  538. package/dist/chunks/parse-24d0gay3.js +0 -19
  539. package/dist/chunks/platform-ba1j1gbh.js +0 -16
  540. package/dist/chunks/publish-panel-v9st96ja.js +0 -29
  541. package/dist/chunks/quick-search-zccjp6gv.js +0 -66
  542. package/dist/chunks/references-3qx8fkyp.js +0 -29
  543. package/dist/chunks/right-panel-vm3b6kwy.js +0 -46
  544. package/dist/chunks/section-registry-sx1ykvkw.js +0 -30
  545. package/dist/chunks/sp-overlay.dev-qgvhnxf4.js +0 -18
  546. package/dist/chunks/sp-popover.dev-n2etmqdp.js +0 -9
  547. package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +0 -15
  548. package/dist/chunks/sp-tooltip.dev-4sastdnw.js +0 -15
  549. package/dist/chunks/sp-tray.dev-tkbscm06.js +0 -199
  550. package/dist/chunks/studio-0a5qrh01.js +0 -3
  551. package/dist/chunks/studio-0bq6eh7v.js +0 -12
  552. package/dist/chunks/studio-18j3bmcz.js +0 -14
  553. package/dist/chunks/studio-1ngbejft.js +0 -181
  554. package/dist/chunks/studio-4b5z1x43.js +0 -424
  555. package/dist/chunks/studio-4n2b5gzb.js +0 -357
  556. package/dist/chunks/studio-4rzyrtjz.js +0 -426
  557. package/dist/chunks/studio-5fx3g9jg.js +0 -4325
  558. package/dist/chunks/studio-5myknm0a.js +0 -1903
  559. package/dist/chunks/studio-7afd21nw.js +0 -67
  560. package/dist/chunks/studio-7nte2hj4.js +0 -613
  561. package/dist/chunks/studio-8jpzqk5s.js +0 -128
  562. package/dist/chunks/studio-8q517dfr.js +0 -24
  563. package/dist/chunks/studio-950k0dx6.js +0 -6939
  564. package/dist/chunks/studio-9n4mrmnb.js +0 -184
  565. package/dist/chunks/studio-a0tswy0w.js +0 -58
  566. package/dist/chunks/studio-ahnzd9rg.js +0 -23
  567. package/dist/chunks/studio-cmb03bg5.js +0 -13890
  568. package/dist/chunks/studio-d1ks410h.js +0 -175
  569. package/dist/chunks/studio-ef49pnzx.js +0 -387
  570. package/dist/chunks/studio-eqq1w638.js +0 -113
  571. package/dist/chunks/studio-ew0djmhg.js +0 -341
  572. package/dist/chunks/studio-gfgb8pe6.js +0 -1652
  573. package/dist/chunks/studio-h4rxscfc.js +0 -10
  574. package/dist/chunks/studio-jc994dnr.js +0 -1066
  575. package/dist/chunks/studio-kyh9avvv.js +0 -28
  576. package/dist/chunks/studio-m3hwn7y1.js +0 -120
  577. package/dist/chunks/studio-mtfnaqs9.js +0 -659
  578. package/dist/chunks/studio-n6j9t10q.js +0 -318
  579. package/dist/chunks/studio-n8b9wqwy.js +0 -291
  580. package/dist/chunks/studio-qcy05dp8.js +0 -1743
  581. package/dist/chunks/studio-qq6jym04.js +0 -53
  582. package/dist/chunks/studio-rdx8jcxy.js +0 -83
  583. package/dist/chunks/studio-rn9ta2mz.js +0 -111
  584. package/dist/chunks/studio-t7qb9db8.js +0 -665
  585. package/dist/chunks/studio-tmc5emrw.js +0 -8
  586. package/dist/chunks/studio-z6nw2ek2.js +0 -18
  587. package/dist/chunks/studio-zczps70q.js +0 -378
  588. package/dist/chunks/studio-zqazsct2.js +0 -1823
  589. package/dist/chunks/workspace-fjx52ryv.js +0 -89
  590. package/src/panels/activity-bar.ts +0 -251
  591. package/src/panels/panel-scheduler.ts +0 -219
  592. package/src/panels/style-inputs.ts +0 -237
  593. package/src/panels/target-line.ts +0 -274
  594. package/src/panels/welcome-screen.ts +0 -346
  595. package/src/ui/button-group.ts +0 -102
  596. package/src/ui/choice-field.ts +0 -72
  597. package/src/ui/field-row.ts +0 -249
  598. package/src/ui/formula-palette.ts +0 -213
  599. package/src/ui/icons.ts +0 -52
  600. package/src/ui/jx-theme.ts +0 -130
  601. package/src/ui/spectrum.ts +0 -340
  602. package/src/ui/value-selector.ts +0 -174
  603. package/src/ui/widgets.ts +0 -172
  604. /package/dist/chunks/{javascript-s56a8dpk.css → javascript-4abzjcy9.css} +0 -0
  605. /package/dist/chunks/{jsonMode-zr9sw0c7.css → jsonMode-vvmpmk34.css} +0 -0
  606. /package/dist/chunks/{monaco-setup-pav68602.css → monaco-setup-8vbyqnhr.css} +0 -0
  607. /package/dist/chunks/{tsMode-ef187txx.css → tsMode-hnxxb73h.css} +0 -0
@@ -25,14 +25,24 @@
25
25
  * press"), and rebindable — as a LAYER over the registry (`settings/preferences-keymap.ts`), so
26
26
  * the sheet stays a projection and the app's own record of a default is never edited.
27
27
  *
28
- * Modality: an `sp-dialog-wrapper` through {@link showDialog}, which is focus-managed and dismissed
29
- * by Escape — not `openModal`'s `inset:40px` blackout. Preferences does not suspend the app.
28
+ * **This module is the FLOW; `surfaces/preferences.json` is what it draws.** Every function below
29
+ * either answers a question about state or changes some, and the answers leave here as strings and
30
+ * booleans — `keyboardEmpty` is a sentence rather than a case name, a row's chord is already
31
+ * formatted, an account's buttons are already decided. Which of those add up to which branch of the
32
+ * document is `surfaces/preferences.ts`'s.
33
+ *
34
+ * **Only the showing section is projected.** `accountsView()` reads three credential stores and
35
+ * `keyboardView()` walks the whole registry, and a repaint of the theme toggle has no business
36
+ * doing either — so each is built when its own section is up and left empty otherwise, exactly as
37
+ * the four `*Tpl()` functions this replaced were called one at a time.
38
+ *
39
+ * Modality: a `jx-dialog` in the dialog layer, which is focus-managed, dismissed by Escape and
40
+ * backed by the platform's own top layer — not the full-viewport blackout the lit modal path used
41
+ * to paint. Preferences does not suspend the app.
30
42
  */
31
43
 
32
- import { html, nothing, render as litRender } from "lit-html";
33
- import { ref } from "lit-html/directives/ref.js";
34
44
  import { CHROME_THEMES, setChromeTheme, shell } from "../shell";
35
- import { showDialog } from "../ui/layers";
45
+ import { layerHost } from "../ui/layers";
36
46
  import { createAiCredentialsForm } from "../ui/ai-credentials-form";
37
47
  import { createManagedConnect } from "../ui/ai-managed-connect";
38
48
  import { activeRegistry } from "../commands/active-registry";
@@ -48,13 +58,21 @@ import {
48
58
  revokeAccount,
49
59
  } from "./preferences-accounts";
50
60
  import { applyKeybindingOverrides, rebindCommand, resetKeybinding } from "./preferences-keymap";
51
- import { overlayRegion } from "../ui/regions";
61
+ import { openPreferencesSurface } from "../surfaces/preferences";
52
62
 
53
- import type { TemplateResult } from "lit-html";
54
63
  import type { ChromeTheme } from "../shell";
55
64
  import type { ShortcutRow } from "../commands/reference";
56
65
  import type { RebindResult } from "./preferences-keymap";
57
66
  import type { AnyCommand, CommandRegistry } from "../commands/registry";
67
+ import type {
68
+ PreferencesAccountRow,
69
+ PreferencesIsland,
70
+ PreferencesKeyGroup,
71
+ PreferencesSectionRow,
72
+ PreferencesSurfaceHandle,
73
+ PreferencesThemeRow,
74
+ PreferencesView,
75
+ } from "../surfaces/preferences";
58
76
 
59
77
  /*
60
78
  * The sections moved to `./preferences-sections`, a leaf with no imports, so the rail's gear menu
@@ -79,15 +97,12 @@ export type { PreferencesSection } from "./preferences-sections";
79
97
 
80
98
  let _section = DEFAULT_PREFERENCES_SECTION;
81
99
 
82
- /** Repaint the open sheet, if one is up. */
83
- let _rerender: (() => void) | null = null;
84
-
85
- /** Dismiss the open sheet, if one is up. */
86
- let _close: (() => void) | null = null;
100
+ /** The mounted sheet, or `null` while none is up. */
101
+ let _surface: PreferencesSurfaceHandle | null = null;
87
102
 
88
103
  /** Whether Preferences is on screen. */
89
104
  export function isPreferencesOpen(): boolean {
90
- return _close !== null;
105
+ return _surface !== null;
91
106
  }
92
107
 
93
108
  /** The section currently showing. Exposed for tests and for the `app.preferences` round-trip. */
@@ -96,7 +111,8 @@ export function preferencesSection(): string {
96
111
  }
97
112
 
98
113
  function repaint(): void {
99
- _rerender?.();
114
+ _surface?.update(view());
115
+ fillIslands();
100
116
  }
101
117
 
102
118
  /** Save/revoke both change what other surfaces show, so they announce as well as repaint. */
@@ -120,10 +136,10 @@ function sectionShown(): void {
120
136
 
121
137
  /** The shared credentials form — draft state lives inside the form's closure. */
122
138
  const credsForm = createAiCredentialsForm({
123
- intro: html`
124
- Any OpenAI-compatible key works. Stored locally on this machine; sent only to the Studio proxy
125
- (never to a third party except your chosen endpoint).
126
- `,
139
+ /* A sentence rather than a template: the form is a document now, and a document renders text
140
+ rather than another module's markup. */
141
+ intro:
142
+ "Any OpenAI-compatible key works. Stored locally on this machine; sent only to the Studio proxy (never to a third party except your chosen endpoint).",
127
143
  // Both buttons keep the sheet open: Preferences is a place, not a wizard step, and the previous
128
144
  // Dialog's habit of vanishing on Save is what made "did that take?" unanswerable.
129
145
  onCancel: repaint,
@@ -134,98 +150,157 @@ const credsForm = createAiCredentialsForm({
134
150
  /** Shared with the New Project modal's gates — see ui/ai-managed-connect.ts. */
135
151
  const managedConnect = createManagedConnect({ requestRender: credentialsChanged });
136
152
 
137
- // ─── Sections ─────────────────────────────────────────────────────────────────
153
+ // ─── The Assistant islands ────────────────────────────────────────────────────
138
154
 
139
- function appearanceTpl(): TemplateResult {
140
- return html`
141
- <div class="prefs-field">
142
- <span class="prefs-field-label">Theme</span>
143
- <sp-action-group compact selects="single" size="s">
144
- ${CHROME_THEMES.map(
145
- (theme) => html`
146
- <sp-action-button
147
- value=${theme}
148
- ?selected=${shell.theme === theme}
149
- @click=${() => {
150
- setChromeTheme(theme as ChromeTheme);
151
- repaint();
152
- }}
153
- >
154
- ${theme === "dark" ? "Dark" : "Light"}
155
- </sp-action-button>
156
- `,
157
- )}
158
- </sp-action-group>
159
- </div>
160
- `;
161
- }
155
+ /*
156
+ * The two elements the Assistant section renders and does not fill.
157
+ *
158
+ * Both belong to a controller that outlives this sheet and is embedded by three other credentials
159
+ * gates, so the document renders an empty box for each and the elements are MOVED into it. Moving a
160
+ * node keeps everything inside it — which is the whole point: the credentials form is three fields
161
+ * a reader is typing into, and a repaint that rebuilt them would take the caret with it.
162
+ */
163
+ let _managedSlot: HTMLElement | null = null;
164
+ let _credsSlot: HTMLElement | null = null;
162
165
 
163
- function assistantTpl(): TemplateResult {
164
- managedConnect.ensureProbe();
165
- return html`<div class="prefs-assistant">${managedConnect.render()} ${credsForm.render()}</div>`;
166
+ /** A slot was created by the document. Remember it, and fill it. */
167
+ function islandCreated(island: PreferencesIsland, host: HTMLElement): void {
168
+ if (island === "managed") {
169
+ _managedSlot = host;
170
+ } else {
171
+ _credsSlot = host;
172
+ }
173
+ fillIslands();
166
174
  }
167
175
 
168
176
  /**
169
- * The buttons on one row.
177
+ * Put each controller's element in its slot, and bring both up to date.
170
178
  *
171
- * A record that declares `actions` decides its own verbs; everything else keeps the original rule —
172
- * a connected credential offers Disconnect and a disconnected one offers nothing. The async ones
173
- * repaint when they settle, because a hosted Reconnect is a round trip through a Cloudflare popup
174
- * and the row must not still be reading "expired" when it comes back.
179
+ * `render()` on either controller is an UPDATE when the surface already exists, so this is also how
180
+ * a repaint reaches them. The parent check is what keeps it from being a rebuild: re-appending a
181
+ * node that is already there would be a move, and a move of a focused field takes the caret.
175
182
  */
176
- function accountActionsTpl(account: ReturnType<typeof listAccounts>[number]) {
177
- if (account.actions) {
178
- return account.actions.map(
179
- (action) => html`
180
- <sp-button
181
- size="s"
182
- data-action=${action.id}
183
- variant=${action.variant ?? nothing}
184
- treatment=${action.variant === "negative" ? "outline" : nothing}
185
- @click=${() => {
186
- void Promise.resolve(action.run()).finally(repaint);
187
- }}
188
- >
189
- ${action.label}
190
- </sp-button>
191
- `,
192
- );
183
+ function fillIslands(): void {
184
+ if (_managedSlot) {
185
+ managedConnect.ensureProbe();
186
+ const element = managedConnect.render();
187
+ if (element === null) {
188
+ _managedSlot.replaceChildren();
189
+ } else if (element.parentNode !== _managedSlot) {
190
+ _managedSlot.replaceChildren(element);
191
+ }
193
192
  }
194
- return account.connected
195
- ? html`
196
- <sp-button
197
- size="s"
198
- variant="negative"
199
- treatment="outline"
200
- @click=${() => {
201
- revokeAccount(account.id);
202
- repaint();
203
- }}
204
- >
205
- Disconnect
206
- </sp-button>
207
- `
208
- : nothing;
193
+ if (_credsSlot) {
194
+ const element = credsForm.render();
195
+ if (element.parentNode !== _credsSlot) {
196
+ _credsSlot.replaceChildren(element);
197
+ }
198
+ }
199
+ }
200
+
201
+ // ─── Sections ─────────────────────────────────────────────────────────────────
202
+
203
+ /** The nav, with the showing row marked as the accessibility tree already marks it. */
204
+ function sectionsView(): PreferencesSectionRow[] {
205
+ return PREFERENCES_SECTIONS.map((section) => ({
206
+ current: section.id === _section ? "true" : "false",
207
+ id: section.id,
208
+ title: section.title,
209
+ }));
209
210
  }
210
211
 
211
- function accountsTpl(): TemplateResult {
212
+ /** The theme segments. `checked` is a string because that is what `aria-checked` takes. */
213
+ function themesView(): PreferencesThemeRow[] {
214
+ return CHROME_THEMES.map((theme) => ({
215
+ checked: shell.theme === theme ? "true" : "false",
216
+ label: theme === "dark" ? "Dark" : "Light",
217
+ value: theme,
218
+ }));
219
+ }
220
+
221
+ function selectSection(id: string): void {
222
+ _section = id;
223
+ // Leaving a section abandons what it was in the middle of: an armed key capture whose listener
224
+ // Is no longer mounted would otherwise swallow the first chord pressed on the author's NEXT
225
+ // Visit.
226
+ resetKeyboardState();
227
+ repaint();
228
+ sectionShown();
229
+ }
230
+
231
+ function setTheme(value: string): void {
232
+ setChromeTheme(value as ChromeTheme);
233
+ repaint();
234
+ }
235
+
236
+ // ─── Accounts ─────────────────────────────────────────────────────────────────
237
+
238
+ /**
239
+ * What each account button does, by the key the row carries.
240
+ *
241
+ * Rebuilt with the rows, because `listAccounts()` mints a fresh closure per call and the one a
242
+ * click must run is the one that was drawn. A key that is no longer in the map is a click on a row
243
+ * that has since been redrawn, which is a no-op rather than an error.
244
+ */
245
+ let _accountRuns = new Map<string, () => void | Promise<void>>();
246
+
247
+ /**
248
+ * The accounts, as rows.
249
+ *
250
+ * A record that declares `actions` decides its own verbs; everything else keeps the original rule —
251
+ * a connected credential offers Disconnect and a disconnected one offers nothing.
252
+ */
253
+ function accountsView(): PreferencesAccountRow[] {
212
254
  // First paint asks the broker what it holds; the answer arrives as a credentials-changed repaint.
213
255
  ensureCfConnection();
214
- return html`
215
- <div class="prefs-accounts">
216
- ${listAccounts().map(
217
- (account) => html`
218
- <div class="prefs-account" data-account=${account.id}>
219
- <div class="prefs-account-text">
220
- <span class="prefs-account-label">${account.label}</span>
221
- <span class="prefs-account-detail">${account.detail}</span>
222
- </div>
223
- <div class="prefs-account-actions">${accountActionsTpl(account)}</div>
224
- </div>
225
- `,
226
- )}
227
- </div>
228
- `;
256
+ const runs = new Map<string, () => void | Promise<void>>();
257
+ const rows = listAccounts().map((account) => {
258
+ const declared =
259
+ account.actions ??
260
+ (account.connected
261
+ ? [
262
+ {
263
+ id: "disconnect",
264
+ label: "Disconnect",
265
+ run: () => {
266
+ revokeAccount(account.id);
267
+ },
268
+ variant: "negative",
269
+ },
270
+ ]
271
+ : []);
272
+ return {
273
+ actions: declared.map((action) => {
274
+ const key = `${account.id}/${action.id}`;
275
+ runs.set(key, action.run);
276
+ return {
277
+ id: action.id,
278
+ key,
279
+ label: action.label,
280
+ variant: action.variant ?? "secondary",
281
+ };
282
+ }),
283
+ detail: account.detail,
284
+ id: account.id,
285
+ label: account.label,
286
+ };
287
+ });
288
+ _accountRuns = runs;
289
+ return rows;
290
+ }
291
+
292
+ /**
293
+ * Run one account button.
294
+ *
295
+ * Every one of them repaints when it settles, because a hosted Reconnect is a round trip through a
296
+ * Cloudflare popup and the row must not still be reading "expired" when it comes back.
297
+ */
298
+ function runAccountAction(key: string): void {
299
+ const run = _accountRuns.get(key);
300
+ if (!run) {
301
+ return;
302
+ }
303
+ void Promise.resolve(run()).finally(repaint);
229
304
  }
230
305
 
231
306
  // ─── Keyboard ─────────────────────────────────────────────────────────────────
@@ -239,8 +314,15 @@ function accountsTpl(): TemplateResult {
239
314
  */
240
315
  type KeyFilter = { kind: "text"; value: string } | { kind: "chord"; chord: string } | null;
241
316
 
242
- /** The next chord goes to the search box, or onto a command. `null` while nothing is listening. */
243
- type KeyCapture = { mode: "search" } | { mode: "rebind"; command: AnyCommand } | null;
317
+ /**
318
+ * The next chord goes to the search box, or onto a command. `null` while nothing is listening.
319
+ *
320
+ * A rebind capture holds the command's ID rather than its record, because the click that armed it
321
+ * arrived from a document and carried a string. The record is looked up when the chord lands, which
322
+ * is also the one moment at which "the registry went away under the open sheet" can be answered
323
+ * honestly rather than asserted against.
324
+ */
325
+ type KeyCapture = { mode: "search" } | { mode: "rebind"; commandId: string } | null;
244
326
 
245
327
  /** Keydowns that are not a chord yet: a modifier held on its way to one. */
246
328
  const MODIFIER_KEYS = new Set(["Meta", "Control", "Alt", "Shift", "CapsLock", "OS"]);
@@ -303,96 +385,87 @@ function onKeyboardKeyDown(event: KeyboardEvent): void {
303
385
  _keyFilter = { kind: "chord", chord };
304
386
  _keyRefusal = null;
305
387
  } else {
306
- const result = rebindCommand(registry, capture.command, chord);
307
- _keyRefusal = result.ok ? null : result;
388
+ const command = commandById(registry, capture.commandId);
389
+ if (command) {
390
+ const result = rebindCommand(registry, command, chord);
391
+ _keyRefusal = result.ok ? null : result;
392
+ }
308
393
  }
309
394
  repaint();
310
395
  }
311
396
 
312
- /** The refusal, and the one thing that can be done about it: go and look at the command holding it. */
313
- function keyRefusalTpl(): TemplateResult | typeof nothing {
314
- if (!_keyRefusal) {
315
- return nothing;
397
+ /** The record behind a row's id, or `undefined` when the registry no longer holds one. */
398
+ function commandById(registry: CommandRegistry, commandId: string): AnyCommand | undefined {
399
+ return [...registry.list()].find((candidate) => candidate.id === commandId);
400
+ }
401
+
402
+ /** Arm or disarm the search box's chord capture. */
403
+ function toggleKeystroke(): void {
404
+ _keyCapture = _keyCapture?.mode === "search" ? null : { mode: "search" };
405
+ _keyRefusal = null;
406
+ repaint();
407
+ }
408
+
409
+ /** Arm or disarm one row's rebind. */
410
+ function toggleRebind(commandId: string): void {
411
+ const capturing = _keyCapture?.mode === "rebind" && _keyCapture.commandId === commandId;
412
+ _keyCapture = capturing ? null : { mode: "rebind", commandId };
413
+ _keyRefusal = null;
414
+ repaint();
415
+ }
416
+
417
+ /** Put a rebound chord back, and forget whatever the last attempt said. */
418
+ function resetBinding(commandId: string): void {
419
+ const registry = activeRegistry();
420
+ if (registry) {
421
+ resetKeybinding(registry, commandId);
316
422
  }
317
- const { conflict } = _keyRefusal;
318
- return html`
319
- <div class="prefs-field">
320
- <sp-help-text variant="negative">${_keyRefusal.reason}</sp-help-text>
321
- ${
322
- conflict
323
- ? html`
324
- <sp-button
325
- size="s"
326
- treatment="outline"
327
- @click=${() => {
328
- _keyFilter = { kind: "text", value: conflict.title };
329
- _keyRefusal = null;
330
- repaint();
331
- }}
332
- >
333
- Show ${conflict.title}
334
- </sp-button>
335
- `
336
- : nothing
337
- }
338
- </div>
339
- `;
423
+ _keyRefusal = null;
424
+ repaint();
340
425
  }
341
426
 
342
- /**
343
- * One binding.
344
- *
345
- * The record is passed in rather than looked up: the rows and the commands come from the same
346
- * `registry.list()` call, so there is no window in which a row can name something the sheet cannot
347
- * resolve, and no assertion pretending so.
348
- */
349
- function keyRowTpl(
350
- row: ShortcutRow,
351
- command: AnyCommand,
352
- registry: CommandRegistry,
353
- ): TemplateResult {
354
- const capturing = _keyCapture?.mode === "rebind" && _keyCapture.command.id === row.commandId;
355
- return html`
356
- <div class="prefs-key" data-command=${row.commandId}>
357
- <kbd class="prefs-key-chord">
358
- ${capturing ? "Press a shortcut…" : registry.keymap.format(row.chord)}
359
- </kbd>
360
- <span class="prefs-key-title">${row.title}${row.overridden ? " — changed" : ""}</span>
361
- <sp-action-button
362
- size="s"
363
- quiet
364
- ?selected=${capturing}
365
- aria-label=${capturing ? `Stop changing ${row.title}` : `Change the shortcut for ${row.title}`}
366
- @click=${() => {
367
- _keyCapture = capturing ? null : { mode: "rebind", command };
368
- _keyRefusal = null;
369
- repaint();
370
- }}
371
- >
372
- ${capturing ? "Cancel" : "Change"}
373
- </sp-action-button>
374
- ${
375
- row.overridden
376
- ? html`
377
- <sp-action-button
378
- size="s"
379
- quiet
380
- aria-label=${`Reset the shortcut for ${row.title}`}
381
- @click=${() => {
382
- resetKeybinding(registry, row.commandId);
383
- _keyRefusal = null;
384
- repaint();
385
- }}
386
- >
387
- Reset
388
- </sp-action-button>
389
- `
390
- : nothing
391
- }
392
- </div>
393
- `;
427
+ /** Go and look at the command holding the chord that was refused. */
428
+ function showConflict(): void {
429
+ const conflict = _keyRefusal?.conflict;
430
+ if (!conflict) {
431
+ return;
432
+ }
433
+ _keyFilter = { kind: "text", value: conflict.title };
434
+ _keyRefusal = null;
435
+ repaint();
436
+ }
437
+
438
+ /** Type into the search box. A keystroke capture is abandoned: the author changed their mind. */
439
+ function setKeyQuery(value: string): void {
440
+ _keyFilter = value ? { kind: "text", value } : null;
441
+ _keyCapture = null;
442
+ repaint();
394
443
  }
395
444
 
445
+ /** The Keyboard half of the view, with nothing to say when its section is not up. */
446
+ type KeyboardView = Pick<
447
+ PreferencesView,
448
+ | "capturingSearch"
449
+ | "conflictLabel"
450
+ | "groups"
451
+ | "keyboardEmpty"
452
+ | "keyQuery"
453
+ | "keystrokeLabel"
454
+ | "refusal"
455
+ | "rowsEmpty"
456
+ >;
457
+
458
+ const KEYBOARD_IDLE: KeyboardView = {
459
+ capturingSearch: false,
460
+ conflictLabel: "",
461
+ groups: [],
462
+ keyboardEmpty: "",
463
+ keyQuery: "",
464
+ keystrokeLabel: "",
465
+ refusal: "",
466
+ rowsEmpty: "",
467
+ };
468
+
396
469
  /**
397
470
  * The keyboard sheet — every live binding, grouped by the scope it is live in.
398
471
  *
@@ -401,94 +474,80 @@ function keyRowTpl(
401
474
  * the same projection rather than patched into its output.
402
475
  *
403
476
  * A command that declares two chords has two rows and rebinding either one leaves it with one — a
404
- * user who asked for ⌥⌘Y is not also asking to keep ⌘Y, and Reset brings both back.
477
+ * user who asked for ⌥⌘Y is not also asking to keep ⌘Y, and Reset brings both back. The two rows
478
+ * share a `commandId` and differ by chord, which is why the row key is both.
405
479
  */
406
- function keyboardTpl(): TemplateResult {
480
+ function keyboardView(): KeyboardView {
407
481
  const registry = activeRegistry();
408
482
  const commands: readonly AnyCommand[] = registry ? [...registry.list()] : [];
409
483
  const rows = registry ? shortcutReference(commands, registry.keymap.overrides()) : [];
410
484
  if (!registry || rows.length === 0) {
411
- return html`<p class="prefs-empty">No commands are registered in this window.</p>`;
485
+ return { ...KEYBOARD_IDLE, keyboardEmpty: "No commands are registered in this window." };
412
486
  }
413
487
  const format = (chord: string) => registry.keymap.format(chord);
414
- const byId = new Map(commands.map((command) => [command.id, command]));
415
- const visible = rows.flatMap((row) => {
416
- const command = byId.get(row.commandId);
417
- return command && keyRowMatches(row, format) ? [{ command, row }] : [];
418
- });
419
- const scopes = [...new Set(visible.map(({ row }) => row.scope))];
488
+ const known = new Set(commands.map((command) => command.id));
489
+ const visible = rows.filter((row) => known.has(row.commandId) && keyRowMatches(row, format));
420
490
  const capturingSearch = _keyCapture?.mode === "search";
421
- return html`
422
- <div class="prefs-keys" @keydown=${onKeyboardKeyDown}>
423
- <div class="prefs-field">
424
- <sp-search
425
- size="s"
426
- placeholder="Find a shortcut…"
427
- .value=${
428
- _keyFilter === null
429
- ? ""
430
- : _keyFilter.kind === "chord"
431
- ? format(_keyFilter.chord)
432
- : _keyFilter.value
433
- }
434
- @input=${(event: Event) => {
435
- const { value } = event.target as HTMLInputElement;
436
- _keyFilter = value ? { kind: "text", value } : null;
437
- _keyCapture = null;
438
- repaint();
439
- }}
440
- @submit=${(event: Event) => event.preventDefault()}
441
- ></sp-search>
442
- <sp-action-button
443
- size="s"
444
- ?selected=${capturingSearch}
445
- @click=${() => {
446
- _keyCapture = capturingSearch ? null : { mode: "search" };
447
- _keyRefusal = null;
448
- repaint();
449
- }}
450
- >
451
- ${capturingSearch ? "Press it now" : "Search by keystroke"}
452
- </sp-action-button>
453
- </div>
454
- ${keyRefusalTpl()}
455
- ${
456
- visible.length === 0
457
- ? html`<p class="prefs-empty">
458
- ${
459
- _keyFilter?.kind === "chord"
460
- ? `Nothing is bound to ${format(_keyFilter.chord)}.`
461
- : "No shortcut matches that."
462
- }
463
- </p>`
464
- : scopes.map(
465
- (scope) => html`
466
- <h4 class="prefs-keys-scope">${SCOPE_LABELS[scope]}</h4>
467
- ${visible
468
- .filter(({ row }) => row.scope === scope)
469
- .map(({ command, row }) => keyRowTpl(row, command, registry))}
470
- `,
471
- )
472
- }
473
- </div>
474
- `;
475
- }
476
-
477
- function sectionBodyTpl(): TemplateResult {
478
- if (_section === "assistant") {
479
- return assistantTpl();
480
- }
481
- if (_section === "accounts") {
482
- return accountsTpl();
491
+ const groups: PreferencesKeyGroup[] = [];
492
+ const byScope = new Map<string, PreferencesKeyGroup>();
493
+ for (const row of visible) {
494
+ const capturing = _keyCapture?.mode === "rebind" && _keyCapture.commandId === row.commandId;
495
+ let group = byScope.get(row.scope);
496
+ if (!group) {
497
+ group = { label: SCOPE_LABELS[row.scope], rows: [], scope: row.scope };
498
+ byScope.set(row.scope, group);
499
+ groups.push(group);
500
+ }
501
+ group.rows.push({
502
+ capturing,
503
+ changeLabel: capturing ? "Cancel" : "Change",
504
+ changeName: capturing ? `Stop changing ${row.title}` : `Change the shortcut for ${row.title}`,
505
+ chord: capturing ? "Press a shortcut…" : format(row.chord),
506
+ commandId: row.commandId,
507
+ key: `${row.commandId}/${row.chord}`,
508
+ overridden: row.overridden,
509
+ resetName: `Reset the shortcut for ${row.title}`,
510
+ title: `${row.title}${row.overridden ? " — changed" : ""}`,
511
+ });
483
512
  }
484
- if (_section === "keyboard") {
485
- return keyboardTpl();
486
- }
487
- return appearanceTpl();
513
+ return {
514
+ capturingSearch,
515
+ conflictLabel: _keyRefusal?.conflict ? `Show ${_keyRefusal.conflict.title}` : "",
516
+ groups,
517
+ keyboardEmpty: "",
518
+ keyQuery:
519
+ _keyFilter === null
520
+ ? ""
521
+ : _keyFilter.kind === "chord"
522
+ ? format(_keyFilter.chord)
523
+ : _keyFilter.value,
524
+ keystrokeLabel: capturingSearch ? "Press it now" : "Search by keystroke",
525
+ refusal: _keyRefusal?.reason ?? "",
526
+ rowsEmpty:
527
+ visible.length > 0
528
+ ? ""
529
+ : _keyFilter?.kind === "chord"
530
+ ? `Nothing is bound to ${format(_keyFilter.chord)}.`
531
+ : "No shortcut matches that.",
532
+ };
488
533
  }
489
534
 
490
535
  // ─── The sheet ────────────────────────────────────────────────────────────────
491
536
 
537
+ /** Everything the document reads, built from the section that is actually showing. */
538
+ function view(): PreferencesView {
539
+ const active = PREFERENCES_SECTIONS.find((candidate) => candidate.id === _section);
540
+ return {
541
+ accounts: _section === "accounts" ? accountsView() : [],
542
+ section: _section,
543
+ sectionBlurb: active?.blurb ?? "",
544
+ sectionTitle: active?.title ?? "",
545
+ sections: sectionsView(),
546
+ themes: themesView(),
547
+ ...(_section === "keyboard" ? keyboardView() : KEYBOARD_IDLE),
548
+ };
549
+ }
550
+
492
551
  /**
493
552
  * Open Preferences, optionally on a named section. Resolves when it is dismissed.
494
553
  *
@@ -504,7 +563,7 @@ export function openPreferences(section?: string): Promise<null> {
504
563
  something it has no reason to believe. */
505
564
  resetCfConnectionCache();
506
565
  _section = section !== undefined && isPreferencesSection(section) ? section : _section;
507
- if (_close) {
566
+ if (_surface) {
508
567
  repaint();
509
568
  sectionShown();
510
569
  return Promise.resolve(null);
@@ -514,84 +573,45 @@ export function openPreferences(section?: string): Promise<null> {
514
573
  credsForm.startEdit();
515
574
  resetKeyboardState();
516
575
  sectionShown();
517
- return showDialog<null>(
518
- (done) => {
519
- let wrapperEl: HTMLElement | null = null;
520
-
521
- function finish() {
522
- _rerender = null;
523
- _close = null;
524
- done(null);
525
- }
526
-
527
- function build(): TemplateResult {
528
- const active = PREFERENCES_SECTIONS.find((candidate) => candidate.id === _section);
529
- return html`
530
- <sp-dialog-wrapper
531
- open
532
- underlay
533
- headline="Preferences"
534
- cancel-label="Close"
535
- size="l"
536
- @cancel=${finish}
537
- @close=${finish}
538
- ${ref((el?: Element) => {
539
- if (el) {
540
- wrapperEl = el as HTMLElement;
541
- }
542
- })}
543
- >
544
- <div class="prefs-sheet">
545
- <nav class="prefs-nav" aria-label="Preferences sections">
546
- ${PREFERENCES_SECTIONS.map(
547
- (candidate) => html`
548
- <button
549
- type="button"
550
- class="prefs-nav-item ${candidate.id === _section ? "active" : ""}"
551
- aria-current=${candidate.id === _section ? "true" : "false"}
552
- @click=${() => {
553
- _section = candidate.id;
554
- // Leaving a section abandons what it was in the middle of: an armed key
555
- // Capture whose listener is no longer mounted would otherwise swallow the
556
- // First chord pressed on the author's NEXT visit.
557
- resetKeyboardState();
558
- repaint();
559
- sectionShown();
560
- }}
561
- >
562
- ${candidate.title}
563
- </button>
564
- `,
565
- )}
566
- </nav>
567
- <section class="prefs-body">
568
- <h3 class="prefs-title">${active?.title ?? ""}</h3>
569
- <p class="prefs-blurb">${active?.blurb ?? ""}</p>
570
- ${sectionBodyTpl()}
571
- </section>
572
- </div>
573
- </sp-dialog-wrapper>
574
- `;
575
- }
576
-
577
- _close = finish;
578
- _rerender = () => {
579
- // Resolved lazily: lit commits element refs before inserting the fragment, so the slot the
580
- // Sheet was rendered into is only reachable once the first render has landed.
581
- const host = wrapperEl?.parentElement;
582
- if (host) {
583
- litRender(build(), host);
576
+ return new Promise<null>((resolve) => {
577
+ /* Guarded rather than trusted to the handle, because closing is what RAISES `onClosed` — so a
578
+ teardown that re-entered through it would call itself until the stack ran out. The close is
579
+ made HERE and not left to the platform: a `close` the flow did not ask for (Escape, or the
580
+ Close button) must also take the document out of the dialog layer, or the surface outlives
581
+ the dialog it drew. */
582
+ let closing = false;
583
+ _surface = openPreferencesSurface({
584
+ layer: layerHost("dialog"),
585
+ onAccountAction: runAccountAction,
586
+ onCaptureKey: onKeyboardKeyDown,
587
+ onClosed: () => {
588
+ if (closing) {
589
+ return;
584
590
  }
585
- };
586
- return build();
587
- },
588
- { region: overlayRegion("dialog", "preferences") },
589
- );
591
+ closing = true;
592
+ const handle = _surface;
593
+ _surface = null;
594
+ _managedSlot = null;
595
+ _credsSlot = null;
596
+ handle?.close();
597
+ resolve(null);
598
+ },
599
+ onIsland: islandCreated,
600
+ onKeyQuery: setKeyQuery,
601
+ onResetBinding: resetBinding,
602
+ onSelectSection: selectSection,
603
+ onSetTheme: setTheme,
604
+ onShowConflict: showConflict,
605
+ onToggleKeystroke: toggleKeystroke,
606
+ onToggleRebind: toggleRebind,
607
+ view: view(),
608
+ });
609
+ });
590
610
  }
591
611
 
592
612
  /** Close Preferences if it is open. Tests, and the shell teardown. */
593
613
  export function closePreferences(): void {
594
- _close?.();
614
+ _surface?.close();
595
615
  }
596
616
 
597
617
  // ─── Command ──────────────────────────────────────────────────────────────────