@jxsuite/studio 4.1.0 → 5.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 (612) 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-qwkm9zrf.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-vj72gqtq.js +20 -0
  15. package/dist/chunks/content-source-5mex4x1h.js +46 -0
  16. package/dist/chunks/convert-to-repeater-n64h8znc.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-234hjnqw.js +42 -0
  20. package/dist/chunks/extension-sections-3hpcambp.js +651 -0
  21. package/dist/chunks/file-ops-8yhd83xr.js +47 -0
  22. package/dist/chunks/files-hnxqxtsg.js +72 -0
  23. package/dist/chunks/format-host-779sfp2n.js +40 -0
  24. package/dist/chunks/fs-events-b88z7g4h.js +18 -0
  25. package/dist/chunks/git-panel-xwj8fpab.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-syr0f9sb.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-ff81wthn.js +30 -0
  37. package/dist/chunks/layers-panel-pnnghw12.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-cqhhxhhe.js +33 -0
  41. package/dist/chunks/menu-9p2w46gj.js +15 -0
  42. package/dist/chunks/menu-q99sy9rf.js +285 -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-5zt840sy.js +27 -0
  52. package/dist/chunks/quick-search-3748564r.js +45 -0
  53. package/dist/chunks/references-qvvxej7h.js +26 -0
  54. package/dist/chunks/right-panel-eccz92qt.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-kryvsw6j.js → studio-1k325pwf.js} +48 -48
  60. package/dist/chunks/{studio-b55y6ewx.js → studio-1knfqgy7.js} +58 -58
  61. package/dist/chunks/{studio-2b946z26.js → studio-1z1kcjv4.js} +3628 -6095
  62. package/dist/chunks/studio-3773086z.js +111 -0
  63. package/dist/chunks/{studio-4r1pv6zf.js → studio-38emantv.js} +22 -5
  64. package/dist/chunks/{studio-x813zp48.js → studio-3rz5md06.js} +30 -30
  65. package/dist/chunks/{studio-ynxwkrzd.js → studio-4wbb9ct0.js} +1953 -1785
  66. package/dist/chunks/studio-5te5tqng.js +81 -0
  67. package/dist/chunks/studio-5wt9nvhd.js +4763 -0
  68. package/dist/chunks/{studio-p4ngc046.js → studio-6ewqv390.js} +9 -9
  69. package/dist/chunks/{studio-9ab8gqsm.js → studio-6nzaktvy.js} +7 -7
  70. package/dist/chunks/studio-6xa21ej9.js +28 -0
  71. package/dist/chunks/{studio-5ehhackm.js → studio-727xh0e3.js} +19 -19
  72. package/dist/chunks/{studio-gcye7qka.js → studio-7e11jpa9.js} +21 -21
  73. package/dist/chunks/{studio-q7v10cg2.js → studio-7r42c6ca.js} +17 -7
  74. package/dist/chunks/studio-7xnmgv66.js +13615 -0
  75. package/dist/chunks/studio-8scdew0e.js +258 -0
  76. package/dist/chunks/{studio-fbf92ckf.js → studio-93zafjcm.js} +275 -163
  77. package/dist/chunks/{studio-dg7zyt5x.js → studio-98b3z07n.js} +1 -1
  78. package/dist/chunks/studio-a79k90n9.js +1162 -0
  79. package/dist/chunks/studio-aj37eer3.js +422 -0
  80. package/dist/chunks/studio-b0k5tqgp.js +2432 -0
  81. package/dist/chunks/studio-b4s9eykd.js +2811 -0
  82. package/dist/chunks/{studio-f3wqjtk5.js → studio-d0300636.js} +5076 -18
  83. package/dist/chunks/{studio-b9b6t1dg.js → studio-e1f24eff.js} +5664 -5666
  84. package/dist/chunks/studio-e1pgga3k.js +839 -0
  85. package/dist/chunks/{studio-g1wdche0.js → studio-esrxgwh6.js} +80 -80
  86. package/dist/chunks/{studio-q43a5y9h.js → studio-g3jevyjt.js} +16 -16
  87. package/dist/chunks/studio-gb13095a.js +2806 -0
  88. package/dist/chunks/{studio-3n4ndrzh.js → studio-h4nvnjvz.js} +303 -303
  89. package/dist/chunks/{studio-dm19q43r.js → studio-hhvs4sdg.js} +10 -10
  90. package/dist/chunks/{studio-x4qkdat5.js → studio-js8nv53n.js} +1749 -2481
  91. package/dist/chunks/{studio-qjy2z9bv.js → studio-jxp12rnj.js} +32 -137
  92. package/dist/chunks/{studio-trxrabqf.js → studio-k75n7vhp.js} +527 -283
  93. package/dist/chunks/{studio-q9jd1a60.js → studio-kcgff4r0.js} +15 -18
  94. package/dist/chunks/studio-kkqkcp3d.js +17668 -0
  95. package/dist/chunks/studio-mhhyamxf.js +763 -0
  96. package/dist/chunks/{studio-cft9r4r7.js → studio-nq5yf5mp.js} +22 -22
  97. package/dist/chunks/{studio-685s0ytq.js → studio-nqgawfks.js} +572 -572
  98. package/dist/chunks/studio-nzzpvzx9.js +185 -0
  99. package/dist/chunks/{studio-xrapxwwc.js → studio-p1dw8wfw.js} +55 -29
  100. package/dist/chunks/{studio-3bqpr0zh.js → studio-pxbgypmg.js} +299 -110
  101. package/dist/chunks/studio-q0a3yk4n.js +33 -0
  102. package/dist/chunks/{studio-wcy8trfx.js → studio-q3x06vmj.js} +23 -22
  103. package/dist/chunks/{studio-6yw4y3my.js → studio-qhhm6hbt.js} +8 -8
  104. package/dist/chunks/studio-qk4bfsq2.js +362 -0
  105. package/dist/chunks/{studio-msd847zn.js → studio-qk90fgqm.js} +361 -90
  106. package/dist/chunks/{studio-jkftxyvz.js → studio-r6nvacqs.js} +5861 -2154
  107. package/dist/chunks/{studio-53n8bp04.js → studio-r6xmmtfh.js} +1 -1
  108. package/dist/chunks/{studio-tvgyw9xw.js → studio-rnyf3f8e.js} +83 -250
  109. package/dist/chunks/studio-rs76k67r.js +12 -0
  110. package/dist/chunks/studio-sbjyc8bt.js +954 -0
  111. package/dist/chunks/{studio-1wagb550.js → studio-syg2tadj.js} +11 -11
  112. package/dist/chunks/{studio-t5wqwzdh.js → studio-tjjk7jvk.js} +1 -1
  113. package/dist/chunks/studio-tqgryxwg.js +1171 -0
  114. package/dist/chunks/studio-try13h8c.js +8448 -0
  115. package/dist/chunks/studio-vfq8qpjt.js +1219 -0
  116. package/dist/chunks/studio-vhv5tgxm.js +51 -0
  117. package/dist/chunks/{studio-ahn3pdgx.js → studio-vw7mzp6z.js} +52 -38
  118. package/dist/chunks/studio-wqpngpsk.js +174 -0
  119. package/dist/chunks/studio-wqz6dggm.js +1356 -0
  120. package/dist/chunks/{studio-92ab2jqe.js → studio-y9zedyjn.js} +14 -14
  121. package/dist/chunks/studio-yxa81f45.js +12 -0
  122. package/dist/chunks/studio-zdn2g6dt.js +148 -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-t55jdjbx.js +69 -0
  133. package/dist/chunks/{ws-client-6shc5rze.js → ws-client-ny4zc51r.js} +20 -22
  134. package/dist/iframe-entry.js +1873 -673
  135. package/dist/studio.js +52757 -36535
  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 +333 -113
  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 +404 -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 +177 -10
  178. package/src/commands/run-reported.ts +120 -0
  179. package/src/content/entry-commands.ts +16 -23
  180. package/src/content/entry-editor.ts +144 -101
  181. package/src/editor/canvas-slash-bridge.ts +2 -2
  182. package/src/editor/context-menu.ts +49 -200
  183. package/src/editor/convert-to-component.ts +42 -82
  184. package/src/editor/convert-to-repeater.ts +132 -173
  185. package/src/editor/inline-edit.ts +11 -0
  186. package/src/editor/shortcuts.ts +76 -49
  187. package/src/editor/slash-menu.ts +150 -157
  188. package/src/files/components.ts +39 -0
  189. package/src/files/file-ops.ts +117 -11
  190. package/src/files/files.ts +944 -660
  191. package/src/files/json-layout.ts +474 -0
  192. package/src/files/serialize-document.ts +8 -1
  193. package/src/format/convert-file.ts +16 -10
  194. package/src/format/format-host.ts +50 -1
  195. package/src/github/github-auth.ts +71 -29
  196. package/src/github/github-publish.ts +57 -65
  197. package/src/grid/cell-editors.ts +46 -4
  198. package/src/grid/cell-popovers.ts +65 -62
  199. package/src/grid/grid-open.ts +86 -64
  200. package/src/grid/grid-panel.ts +390 -469
  201. package/src/grid/redirects-grid.ts +40 -44
  202. package/src/hosting/layout.ts +2 -0
  203. package/src/i18n/i18n-commands.ts +137 -62
  204. package/src/media/media-pane.ts +83 -133
  205. package/src/new-project/add-repo-modal.ts +125 -176
  206. package/src/new-project/import-tab.ts +181 -252
  207. package/src/new-project/location-fields.ts +93 -160
  208. package/src/new-project/new-project-modal.ts +376 -367
  209. package/src/packages/jxsuite-update.ts +76 -18
  210. package/src/packages/pull-package-sync.ts +5 -1
  211. package/src/panels/activity-panel.ts +137 -104
  212. package/src/panels/ai-chat/chat-markdown.ts +15 -13
  213. package/src/panels/ai-chat/chat-view.ts +307 -472
  214. package/src/panels/ai-chat/composer.ts +175 -163
  215. package/src/panels/ai-chat/sessions-view.ts +31 -62
  216. package/src/panels/ai-panel.ts +213 -185
  217. package/src/panels/block-action-bar.ts +444 -645
  218. package/src/panels/bottom-dock.ts +112 -47
  219. package/src/panels/chat-panel.ts +19 -14
  220. package/src/panels/data-explorer.ts +141 -102
  221. package/src/panels/data-grid.ts +183 -150
  222. package/src/panels/dnd.ts +75 -20
  223. package/src/panels/editors.ts +26 -19
  224. package/src/panels/elements-panel.ts +243 -186
  225. package/src/panels/empty-state.ts +26 -42
  226. package/src/panels/events-panel.ts +950 -729
  227. package/src/panels/formula-workspace.ts +434 -207
  228. package/src/panels/frontmatter-fields.ts +185 -153
  229. package/src/panels/frontmatter-panel.ts +411 -187
  230. package/src/panels/git-diff-open.ts +148 -0
  231. package/src/panels/git-panel.ts +679 -605
  232. package/src/panels/head-panel.ts +617 -435
  233. package/src/panels/i18n-panel.ts +148 -135
  234. package/src/panels/imports-panel.ts +388 -414
  235. package/src/panels/jump-bar.ts +175 -147
  236. package/src/panels/layers-panel.ts +785 -671
  237. package/src/panels/left-panel.ts +166 -94
  238. package/src/panels/navigator-panels.ts +1 -1
  239. package/src/panels/pane-context.ts +529 -677
  240. package/src/panels/pane-grid.ts +265 -241
  241. package/src/panels/panel-registry.ts +15 -4
  242. package/src/panels/problems-panel.ts +181 -99
  243. package/src/panels/properties-panel.ts +1525 -966
  244. package/src/panels/provenance.ts +31 -113
  245. package/src/panels/quick-search.ts +223 -184
  246. package/src/panels/right-panel.ts +135 -169
  247. package/src/panels/seo-modal.ts +346 -167
  248. package/src/panels/settings-menu.ts +114 -661
  249. package/src/panels/settings-pane.ts +102 -86
  250. package/src/panels/signals-panel.ts +1318 -1172
  251. package/src/panels/statement-editor.ts +556 -428
  252. package/src/panels/style-panel.ts +1367 -630
  253. package/src/panels/style-utils.ts +36 -32
  254. package/src/panels/stylebook-doc.ts +30 -73
  255. package/src/panels/stylebook-layers-panel.ts +202 -109
  256. package/src/panels/stylebook-panel.ts +124 -88
  257. package/src/panels/tab-strip.ts +386 -288
  258. package/src/platforms/cloud.ts +106 -16
  259. package/src/platforms/devserver.ts +13 -0
  260. package/src/publish/deploy-checklist.ts +124 -78
  261. package/src/publish/publish-commands.ts +4 -12
  262. package/src/publish/publish-panel.ts +134 -328
  263. package/src/services/a11y-report.ts +100 -43
  264. package/src/services/ai-command-tools.ts +538 -0
  265. package/src/services/ai-project-tools.ts +8 -2
  266. package/src/services/ai-session-store.ts +6 -0
  267. package/src/services/ai-system-prompt.ts +112 -24
  268. package/src/services/ai-tools.ts +36 -212
  269. package/src/services/ai-write-report.ts +174 -0
  270. package/src/services/document-assistant.ts +59 -8
  271. package/src/services/idle.ts +16 -14
  272. package/src/services/live-surfaces.ts +237 -0
  273. package/src/services/model-uri.ts +75 -0
  274. package/src/services/notify.ts +63 -6
  275. package/src/services/popover-report.ts +325 -0
  276. package/src/services/surface-registry.ts +56 -0
  277. package/src/services/tool-executor.ts +6 -0
  278. package/src/settings/contexts-section.ts +301 -299
  279. package/src/settings/contributed-section.ts +361 -222
  280. package/src/settings/css-vars-editor.ts +320 -325
  281. package/src/settings/defs-editor.ts +572 -535
  282. package/src/settings/dependencies-editor.ts +79 -107
  283. package/src/settings/extension-commands.ts +379 -0
  284. package/src/settings/extension-rows.ts +113 -0
  285. package/src/settings/extensions-section.ts +287 -0
  286. package/src/settings/general-settings.ts +130 -181
  287. package/src/settings/head-editor.ts +10 -305
  288. package/src/settings/locales-section.ts +146 -196
  289. package/src/settings/preferences-accounts.ts +1 -1
  290. package/src/settings/preferences-dialog.ts +350 -330
  291. package/src/settings/project-sections.ts +108 -181
  292. package/src/settings/schema-field-ui.ts +11 -399
  293. package/src/settings/section-registry.ts +8 -1
  294. package/src/settings/settings-document.ts +31 -29
  295. package/src/shell/tree.ts +42 -87
  296. package/src/shell.ts +48 -36
  297. package/src/studio.ts +104 -69
  298. package/src/style/token-ref.ts +0 -39
  299. package/src/surfaces/about.json +263 -0
  300. package/src/surfaces/about.ts +163 -0
  301. package/src/surfaces/add-repo.json +486 -0
  302. package/src/surfaces/add-repo.ts +236 -0
  303. package/src/surfaces/ai-chat.json +2185 -0
  304. package/src/surfaces/ai-chat.ts +549 -0
  305. package/src/surfaces/ai-credentials-form.json +332 -0
  306. package/src/surfaces/ai-credentials-form.ts +151 -0
  307. package/src/surfaces/ai-managed-connect.json +108 -0
  308. package/src/surfaces/ai-managed-connect.ts +123 -0
  309. package/src/surfaces/ai-model-picker.json +114 -0
  310. package/src/surfaces/ai-model-picker.ts +139 -0
  311. package/src/surfaces/block-action-bar.json +640 -0
  312. package/src/surfaces/block-action-bar.ts +368 -0
  313. package/src/surfaces/bottom-dock.json +154 -0
  314. package/src/surfaces/bottom-dock.ts +125 -0
  315. package/src/surfaces/canvas-stage.json +444 -0
  316. package/src/surfaces/canvas-stage.ts +253 -0
  317. package/src/surfaces/cf-account-picker.json +300 -0
  318. package/src/surfaces/cf-account-picker.ts +214 -0
  319. package/src/surfaces/commandbar.json +985 -0
  320. package/src/{panels/toolbar.ts → surfaces/commandbar.ts} +417 -391
  321. package/src/surfaces/convert-repeater.json +309 -0
  322. package/src/surfaces/convert-repeater.ts +276 -0
  323. package/src/surfaces/data-actions.json +157 -0
  324. package/src/surfaces/data-actions.ts +115 -0
  325. package/src/surfaces/dialog.json +289 -0
  326. package/src/surfaces/dialog.ts +343 -0
  327. package/src/surfaces/diff-toolbar.json +266 -0
  328. package/src/surfaces/diff-toolbar.ts +139 -0
  329. package/src/surfaces/doc-header.json +739 -0
  330. package/src/surfaces/doc-header.ts +223 -0
  331. package/src/surfaces/empty-state.json +152 -0
  332. package/src/surfaces/empty-state.ts +173 -0
  333. package/src/surfaces/entry-editor.json +281 -0
  334. package/src/surfaces/entry-editor.ts +191 -0
  335. package/src/surfaces/expression-editor.json +977 -0
  336. package/src/surfaces/expression-editor.ts +266 -0
  337. package/src/surfaces/files-panel.json +592 -0
  338. package/src/surfaces/files-panel.ts +301 -0
  339. package/src/surfaces/formula-palette.json +364 -0
  340. package/src/surfaces/formula-palette.ts +340 -0
  341. package/src/surfaces/git-panel.json +1822 -0
  342. package/src/surfaces/git-panel.ts +272 -0
  343. package/src/surfaces/github-auth.json +112 -0
  344. package/src/surfaces/github-auth.ts +139 -0
  345. package/src/surfaces/github-publish.json +206 -0
  346. package/src/surfaces/github-publish.ts +169 -0
  347. package/src/surfaces/grid-cell.json +218 -0
  348. package/src/surfaces/grid-cell.ts +120 -0
  349. package/src/surfaces/grid-open.json +147 -0
  350. package/src/surfaces/grid-open.ts +87 -0
  351. package/src/surfaces/grid-panel.json +706 -0
  352. package/src/surfaces/grid-panel.ts +206 -0
  353. package/src/surfaces/grid-replace.json +179 -0
  354. package/src/surfaces/grid-replace.ts +89 -0
  355. package/src/surfaces/grid-views.json +446 -0
  356. package/src/surfaces/grid-views.ts +172 -0
  357. package/src/surfaces/inspector-dock.json +187 -0
  358. package/src/surfaces/inspector-dock.ts +161 -0
  359. package/src/surfaces/jump-bar.json +243 -0
  360. package/src/surfaces/jump-bar.ts +129 -0
  361. package/src/surfaces/jxsuite-update.json +146 -0
  362. package/src/surfaces/jxsuite-update.ts +149 -0
  363. package/src/surfaces/library-pane.json +1509 -0
  364. package/src/surfaces/library-pane.ts +363 -0
  365. package/src/surfaces/logic-panel.json +1949 -0
  366. package/src/surfaces/logic-panel.ts +369 -0
  367. package/src/surfaces/logic-workspace.json +685 -0
  368. package/src/surfaces/logic-workspace.ts +238 -0
  369. package/src/surfaces/media-browser.json +303 -0
  370. package/src/surfaces/media-browser.ts +145 -0
  371. package/src/surfaces/media-field.json +163 -0
  372. package/src/surfaces/media-field.ts +116 -0
  373. package/src/surfaces/media-pane.json +513 -0
  374. package/src/surfaces/media-pane.ts +197 -0
  375. package/src/surfaces/menu.json +232 -0
  376. package/src/surfaces/menu.ts +375 -0
  377. package/src/surfaces/navigator-dock.json +113 -0
  378. package/src/surfaces/navigator-dock.ts +130 -0
  379. package/src/surfaces/new-project.json +1654 -0
  380. package/src/surfaces/new-project.ts +411 -0
  381. package/src/surfaces/palette.json +535 -0
  382. package/src/surfaces/pane-context.json +1497 -0
  383. package/src/surfaces/pane-context.ts +513 -0
  384. package/src/surfaces/pane-grid.json +188 -0
  385. package/src/surfaces/pane-grid.ts +250 -0
  386. package/src/surfaces/panel-activity.json +563 -0
  387. package/src/surfaces/panel-activity.ts +187 -0
  388. package/src/surfaces/panel-data.json +141 -0
  389. package/src/surfaces/panel-data.ts +156 -0
  390. package/src/surfaces/panel-deploy-checklist.json +283 -0
  391. package/src/surfaces/panel-deploy-checklist.ts +175 -0
  392. package/src/surfaces/panel-elements.json +367 -0
  393. package/src/surfaces/panel-elements.ts +175 -0
  394. package/src/surfaces/panel-i18n.json +569 -0
  395. package/src/surfaces/panel-i18n.ts +212 -0
  396. package/src/surfaces/panel-imports.json +780 -0
  397. package/src/surfaces/panel-imports.ts +209 -0
  398. package/src/surfaces/panel-outline.json +734 -0
  399. package/src/surfaces/panel-outline.ts +326 -0
  400. package/src/surfaces/panel-page.json +1017 -0
  401. package/src/surfaces/panel-page.ts +240 -0
  402. package/src/surfaces/panel-problems.json +497 -0
  403. package/src/surfaces/panel-problems.ts +157 -0
  404. package/src/surfaces/panel-signals.json +1973 -0
  405. package/src/surfaces/panel-signals.ts +436 -0
  406. package/src/surfaces/panel-stylebook-layers.json +317 -0
  407. package/src/surfaces/panel-stylebook-layers.ts +155 -0
  408. package/src/surfaces/preferences.json +850 -0
  409. package/src/surfaces/preferences.ts +332 -0
  410. package/src/surfaces/progress-modal.json +110 -0
  411. package/src/surfaces/progress-modal.ts +150 -0
  412. package/src/surfaces/properties-panel.json +1578 -0
  413. package/src/surfaces/properties-panel.ts +337 -0
  414. package/src/surfaces/publish.json +945 -0
  415. package/src/surfaces/publish.ts +392 -0
  416. package/src/surfaces/push-plan.json +110 -0
  417. package/src/surfaces/push-plan.ts +87 -0
  418. package/src/surfaces/rail.json +255 -0
  419. package/src/surfaces/rail.ts +206 -0
  420. package/src/surfaces/reference-field.json +191 -0
  421. package/src/surfaces/reference-field.ts +168 -0
  422. package/src/surfaces/schema-builder.json +1011 -0
  423. package/src/surfaces/schema-builder.ts +181 -0
  424. package/src/surfaces/schema-form.json +1013 -0
  425. package/src/surfaces/schema-form.ts +287 -0
  426. package/src/surfaces/secret-field.json +60 -0
  427. package/src/surfaces/secret-field.ts +146 -0
  428. package/src/surfaces/seo.json +932 -0
  429. package/src/surfaces/seo.ts +334 -0
  430. package/src/surfaces/settings-contexts.json +538 -0
  431. package/src/surfaces/settings-contexts.ts +196 -0
  432. package/src/surfaces/settings-contributed.json +489 -0
  433. package/src/surfaces/settings-contributed.ts +199 -0
  434. package/src/surfaces/settings-css-vars.json +820 -0
  435. package/src/surfaces/settings-css-vars.ts +199 -0
  436. package/src/surfaces/settings-defs.json +1363 -0
  437. package/src/surfaces/settings-defs.ts +232 -0
  438. package/src/surfaces/settings-deploy.json +108 -0
  439. package/src/surfaces/settings-deploy.ts +135 -0
  440. package/src/surfaces/settings-extensions.json +486 -0
  441. package/src/surfaces/settings-extensions.ts +201 -0
  442. package/src/surfaces/settings-head.json +779 -0
  443. package/src/surfaces/settings-head.ts +397 -0
  444. package/src/surfaces/settings-locales.json +420 -0
  445. package/src/surfaces/settings-locales.ts +137 -0
  446. package/src/surfaces/settings-overview.json +423 -0
  447. package/src/surfaces/settings-overview.ts +218 -0
  448. package/src/surfaces/settings-packages.json +549 -0
  449. package/src/surfaces/settings-packages.ts +158 -0
  450. package/src/surfaces/settings-pane.json +147 -0
  451. package/src/surfaces/settings-pane.ts +150 -0
  452. package/src/surfaces/settings-rawjson.json +104 -0
  453. package/src/surfaces/settings-rawjson.ts +107 -0
  454. package/src/surfaces/shell.json +99 -0
  455. package/src/surfaces/shell.ts +30 -0
  456. package/src/surfaces/slash-menu.json +180 -0
  457. package/src/surfaces/slash-menu.ts +272 -0
  458. package/src/surfaces/statements.json +607 -0
  459. package/src/surfaces/statements.ts +238 -0
  460. package/src/surfaces/statusbar.json +94 -0
  461. package/src/{panels → surfaces}/statusbar.ts +164 -90
  462. package/src/surfaces/style-panel.json +1643 -0
  463. package/src/surfaces/style-panel.ts +338 -0
  464. package/src/surfaces/stylebook-chrome.json +122 -0
  465. package/src/surfaces/stylebook-chrome.ts +122 -0
  466. package/src/surfaces/tab-strip.json +448 -0
  467. package/src/surfaces/tab-strip.ts +200 -0
  468. package/src/surfaces/target-line.json +419 -0
  469. package/src/surfaces/target-line.ts +349 -0
  470. package/src/surfaces/toasts.json +189 -0
  471. package/src/surfaces/welcome.json +571 -0
  472. package/src/surfaces/welcome.ts +338 -0
  473. package/src/tabs/project-config.ts +111 -21
  474. package/src/tabs/tab.ts +52 -1
  475. package/src/types.ts +22 -0
  476. package/src/ui/ai-credentials-form.ts +104 -100
  477. package/src/ui/ai-managed-connect.ts +54 -36
  478. package/src/ui/ai-model-picker.ts +75 -47
  479. package/src/ui/cf-account-picker.ts +76 -141
  480. package/src/ui/color-selector.ts +80 -301
  481. package/src/ui/dynamic-slot.ts +43 -283
  482. package/src/ui/expression-editor.ts +797 -576
  483. package/src/ui/field-input.ts +8 -181
  484. package/src/ui/form-controls.ts +838 -366
  485. package/src/ui/formula-chips.ts +80 -85
  486. package/src/ui/kit.ts +33 -0
  487. package/src/ui/layers.ts +296 -593
  488. package/src/ui/media-picker.ts +203 -284
  489. package/src/ui/panel-resize.ts +251 -64
  490. package/src/ui/popover-surface.ts +153 -0
  491. package/src/ui/progress-modal.ts +22 -60
  492. package/src/ui/regions.ts +13 -7
  493. package/src/ui/schema-form.ts +906 -440
  494. package/src/ui/surface.ts +122 -0
  495. package/src/ui/unit-selector.ts +15 -131
  496. package/src/ui/value-source.ts +14 -0
  497. package/src/ui/virtual-window.ts +88 -9
  498. package/src/utils/canvas-media.ts +72 -0
  499. package/src/utils/edit-display.ts +7 -2
  500. package/src/utils/element-selectors.ts +69 -0
  501. package/src/utils/link-target.ts +2 -2
  502. package/src/utils/studio-utils.ts +0 -10
  503. package/src/view.ts +15 -0
  504. package/src/workspace/pane-derive.ts +36 -14
  505. package/src/workspace/workspace.ts +75 -6
  506. package/styles/canvas.css +72 -467
  507. package/styles/forced-colors.css +48 -82
  508. package/styles/forced-colors.json +41 -0
  509. package/styles/inspector.css +2 -1301
  510. package/styles/overlays.css +96 -2823
  511. package/styles/panels.css +6 -1423
  512. package/styles/shell-frame.css +272 -0
  513. package/styles/shell-frame.json +227 -0
  514. package/styles/shell.css +0 -1568
  515. package/styles/tokens.css +65 -136
  516. package/styles/tokens.json +84 -0
  517. package/dist/chunks/Overlay.dev-etqjwyf9.js +0 -23
  518. package/dist/chunks/adapter-jkcyhgtr.js +0 -14
  519. package/dist/chunks/cf-account-picker-en86v7x7.js +0 -163
  520. package/dist/chunks/cf-settings-45fgyx8y.js +0 -21
  521. package/dist/chunks/components-zmx10t62.js +0 -21
  522. package/dist/chunks/content-source-48xg86wj.js +0 -49
  523. package/dist/chunks/convert-to-repeater-43tfn7cs.js +0 -268
  524. package/dist/chunks/editor-cptq5jp1.js +0 -37
  525. package/dist/chunks/entry-commands-e63f1xc0.js +0 -46
  526. package/dist/chunks/extension-sections-txv08xxg.js +0 -311
  527. package/dist/chunks/file-ops-wbh4hhsx.js +0 -48
  528. package/dist/chunks/files-3kfkwhk4.js +0 -93
  529. package/dist/chunks/flow-qbfgrpeh.js +0 -634
  530. package/dist/chunks/focus-trap.esm-9t2drp0g.js +0 -1025
  531. package/dist/chunks/focus-visible-6eg70z18.js +0 -167
  532. package/dist/chunks/format-host-w16q81tf.js +0 -43
  533. package/dist/chunks/fs-events-hrrg328g.js +0 -21
  534. package/dist/chunks/git-panel-532whr1y.js +0 -59
  535. package/dist/chunks/index-zqxmqxmh.js +0 -109
  536. package/dist/chunks/jx-validate-8er6gbxy.js +0 -19
  537. package/dist/chunks/layers-panel-07hm7g5j.js +0 -67
  538. package/dist/chunks/layers-q75tj2mf.js +0 -45
  539. package/dist/chunks/lit-html-j055mkh8.js +0 -22
  540. package/dist/chunks/monaco-buffer-dqw0xxpx.js +0 -25
  541. package/dist/chunks/notify-yjm79n4s.js +0 -30
  542. package/dist/chunks/parse-24d0gay3.js +0 -19
  543. package/dist/chunks/platform-ba1j1gbh.js +0 -16
  544. package/dist/chunks/publish-panel-v9st96ja.js +0 -29
  545. package/dist/chunks/quick-search-zccjp6gv.js +0 -66
  546. package/dist/chunks/references-3qx8fkyp.js +0 -29
  547. package/dist/chunks/right-panel-vm3b6kwy.js +0 -46
  548. package/dist/chunks/section-registry-sx1ykvkw.js +0 -30
  549. package/dist/chunks/sp-overlay.dev-qgvhnxf4.js +0 -18
  550. package/dist/chunks/sp-popover.dev-n2etmqdp.js +0 -9
  551. package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +0 -15
  552. package/dist/chunks/sp-tooltip.dev-4sastdnw.js +0 -15
  553. package/dist/chunks/sp-tray.dev-tkbscm06.js +0 -199
  554. package/dist/chunks/studio-0a5qrh01.js +0 -3
  555. package/dist/chunks/studio-0bq6eh7v.js +0 -12
  556. package/dist/chunks/studio-18j3bmcz.js +0 -14
  557. package/dist/chunks/studio-1ngbejft.js +0 -181
  558. package/dist/chunks/studio-4b5z1x43.js +0 -424
  559. package/dist/chunks/studio-4n2b5gzb.js +0 -357
  560. package/dist/chunks/studio-4rzyrtjz.js +0 -426
  561. package/dist/chunks/studio-5fx3g9jg.js +0 -4325
  562. package/dist/chunks/studio-5myknm0a.js +0 -1903
  563. package/dist/chunks/studio-7afd21nw.js +0 -67
  564. package/dist/chunks/studio-7nte2hj4.js +0 -613
  565. package/dist/chunks/studio-8jpzqk5s.js +0 -128
  566. package/dist/chunks/studio-8q517dfr.js +0 -24
  567. package/dist/chunks/studio-950k0dx6.js +0 -6939
  568. package/dist/chunks/studio-9n4mrmnb.js +0 -184
  569. package/dist/chunks/studio-a0tswy0w.js +0 -58
  570. package/dist/chunks/studio-ahnzd9rg.js +0 -23
  571. package/dist/chunks/studio-bgxt93ga.js +0 -62
  572. package/dist/chunks/studio-cmb03bg5.js +0 -13890
  573. package/dist/chunks/studio-d1ks410h.js +0 -175
  574. package/dist/chunks/studio-ef49pnzx.js +0 -387
  575. package/dist/chunks/studio-eqq1w638.js +0 -113
  576. package/dist/chunks/studio-ew0djmhg.js +0 -341
  577. package/dist/chunks/studio-gfgb8pe6.js +0 -1652
  578. package/dist/chunks/studio-h4rxscfc.js +0 -10
  579. package/dist/chunks/studio-jc994dnr.js +0 -1066
  580. package/dist/chunks/studio-kyh9avvv.js +0 -28
  581. package/dist/chunks/studio-m3hwn7y1.js +0 -120
  582. package/dist/chunks/studio-mtfnaqs9.js +0 -659
  583. package/dist/chunks/studio-n6j9t10q.js +0 -318
  584. package/dist/chunks/studio-n8b9wqwy.js +0 -291
  585. package/dist/chunks/studio-qcy05dp8.js +0 -1743
  586. package/dist/chunks/studio-qq6jym04.js +0 -53
  587. package/dist/chunks/studio-rdx8jcxy.js +0 -83
  588. package/dist/chunks/studio-rn9ta2mz.js +0 -111
  589. package/dist/chunks/studio-t7qb9db8.js +0 -665
  590. package/dist/chunks/studio-tmc5emrw.js +0 -8
  591. package/dist/chunks/studio-z6nw2ek2.js +0 -18
  592. package/dist/chunks/studio-zczps70q.js +0 -378
  593. package/dist/chunks/studio-zqazsct2.js +0 -1823
  594. package/dist/chunks/workspace-fjx52ryv.js +0 -89
  595. package/src/panels/activity-bar.ts +0 -251
  596. package/src/panels/panel-scheduler.ts +0 -219
  597. package/src/panels/style-inputs.ts +0 -237
  598. package/src/panels/target-line.ts +0 -274
  599. package/src/panels/welcome-screen.ts +0 -346
  600. package/src/ui/button-group.ts +0 -102
  601. package/src/ui/choice-field.ts +0 -72
  602. package/src/ui/field-row.ts +0 -249
  603. package/src/ui/formula-palette.ts +0 -213
  604. package/src/ui/icons.ts +0 -52
  605. package/src/ui/jx-theme.ts +0 -130
  606. package/src/ui/spectrum.ts +0 -340
  607. package/src/ui/value-selector.ts +0 -174
  608. package/src/ui/widgets.ts +0 -172
  609. /package/dist/chunks/{javascript-s56a8dpk.css → javascript-4abzjcy9.css} +0 -0
  610. /package/dist/chunks/{jsonMode-zr9sw0c7.css → jsonMode-vvmpmk34.css} +0 -0
  611. /package/dist/chunks/{monaco-setup-pav68602.css → monaco-setup-8vbyqnhr.css} +0 -0
  612. /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 ──────────────────────────────────────────────────────────────────