@jxsuite/studio 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (419) hide show
  1. package/canvas.html +21 -0
  2. package/dist/chunks/Overlay.dev-qfjzkms8.js +23 -0
  3. package/dist/chunks/{Overlay.dev-cszx7yn5.js.map → Overlay.dev-qfjzkms8.js.map} +1 -1
  4. package/dist/chunks/{adapter-z8fkmbmv.js → adapter-qfnesn5v.js} +2 -2
  5. package/dist/chunks/{cf-settings-mmyv436d.js → cf-settings-73sh5z4p.js} +3 -3
  6. package/dist/chunks/{components-0hzentwq.js → components-2bpqp4mq.js} +5 -7
  7. package/dist/chunks/{components-0hzentwq.js.map → components-2bpqp4mq.js.map} +1 -1
  8. package/dist/chunks/content-source-34gnkbnn.js +38 -0
  9. package/dist/chunks/content-source-34gnkbnn.js.map +9 -0
  10. package/dist/chunks/convert-to-repeater-t8kxre5s.js +258 -0
  11. package/dist/chunks/convert-to-repeater-t8kxre5s.js.map +10 -0
  12. package/dist/chunks/extension-sections-6mqydz35.js +301 -0
  13. package/dist/chunks/extension-sections-6mqydz35.js.map +11 -0
  14. package/dist/chunks/files-8kn321h1.js +70 -0
  15. package/dist/chunks/files-8kn321h1.js.map +9 -0
  16. package/dist/chunks/{format-host-2c188n41.js → format-host-mkwsmt8d.js} +3 -3
  17. package/dist/chunks/git-panel-tjbr191d.js +51 -0
  18. package/dist/chunks/git-panel-tjbr191d.js.map +9 -0
  19. package/dist/chunks/{index-baeg2x3y.js → index-7pxchcce.js} +4 -3
  20. package/dist/chunks/{index-baeg2x3y.js.map → index-7pxchcce.js.map} +3 -3
  21. package/dist/chunks/{jx-validate-qx8degj7.js → jx-validate-v6qgknrs.js} +3 -3
  22. package/dist/chunks/layers-panel-87pb25km.js +58 -0
  23. package/dist/chunks/layers-panel-87pb25km.js.map +9 -0
  24. package/dist/chunks/{monaco-setup-zjdp9zdw.js → monaco-setup-e0yh25j4.js} +4 -75
  25. package/dist/chunks/{monaco-setup-zjdp9zdw.js.map → monaco-setup-e0yh25j4.js.map} +3 -4
  26. package/dist/chunks/publish-panel-m0fcgdrt.js +21 -0
  27. package/dist/chunks/publish-panel-m0fcgdrt.js.map +9 -0
  28. package/dist/chunks/quick-search-rhs6cbsb.js +53 -0
  29. package/dist/chunks/quick-search-rhs6cbsb.js.map +9 -0
  30. package/dist/chunks/right-panel-zpdxd98t.js +37 -0
  31. package/dist/chunks/right-panel-zpdxd98t.js.map +9 -0
  32. package/dist/chunks/section-registry-zmckaxf4.js +30 -0
  33. package/dist/chunks/section-registry-zmckaxf4.js.map +9 -0
  34. package/dist/chunks/sp-overlay.dev-9ran0sfq.js +18 -0
  35. package/dist/chunks/sp-overlay.dev-9ran0sfq.js.map +9 -0
  36. package/dist/chunks/sp-popover.dev-rebmdncr.js +9 -0
  37. package/dist/chunks/sp-popover.dev-rebmdncr.js.map +9 -0
  38. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js +15 -0
  39. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js.map +9 -0
  40. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js +15 -0
  41. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js.map +9 -0
  42. package/dist/chunks/{sp-tray.dev-pb2fsd65.js → sp-tray.dev-ey59vfh7.js} +10 -8
  43. package/dist/chunks/{sp-tray.dev-pb2fsd65.js.map → sp-tray.dev-ey59vfh7.js.map} +2 -2
  44. package/dist/chunks/{studio-9qr6fq7p.js → studio-0rqqhrdy.js} +10 -378
  45. package/dist/chunks/{studio-9qr6fq7p.js.map → studio-0rqqhrdy.js.map} +4 -14
  46. package/dist/chunks/{studio-5x6tqqec.js → studio-2sj1jj3v.js} +10 -8
  47. package/dist/chunks/{studio-5x6tqqec.js.map → studio-2sj1jj3v.js.map} +2 -2
  48. package/dist/chunks/{studio-h3fw0wn8.js → studio-3fk1ac9z.js} +6 -4
  49. package/dist/chunks/{studio-h3fw0wn8.js.map → studio-3fk1ac9z.js.map} +2 -2
  50. package/dist/chunks/studio-41exgzhq.js +6690 -0
  51. package/dist/chunks/studio-41exgzhq.js.map +38 -0
  52. package/dist/chunks/{studio-7v2bpweq.js → studio-4mqb9a0j.js} +6 -4
  53. package/dist/chunks/{studio-7v2bpweq.js.map → studio-4mqb9a0j.js.map} +2 -2
  54. package/dist/chunks/studio-50k77cca.js +1787 -0
  55. package/dist/chunks/studio-50k77cca.js.map +18 -0
  56. package/dist/chunks/{studio-56v3zwk6.js → studio-6a0h7sne.js} +10 -8
  57. package/dist/chunks/{studio-56v3zwk6.js.map → studio-6a0h7sne.js.map} +2 -2
  58. package/dist/chunks/studio-6f0am8f2.js +53 -0
  59. package/dist/chunks/studio-6f0am8f2.js.map +10 -0
  60. package/dist/chunks/studio-6nmjc5qk.js +7002 -0
  61. package/dist/chunks/studio-6nmjc5qk.js.map +32 -0
  62. package/dist/chunks/studio-7afd21nw.js +67 -0
  63. package/dist/chunks/studio-7afd21nw.js.map +10 -0
  64. package/dist/chunks/{studio-xze4zrs1.js → studio-7bmdvzne.js} +2 -2
  65. package/dist/chunks/studio-7bmdvzne.js.map +10 -0
  66. package/dist/chunks/studio-8jpzqk5s.js +128 -0
  67. package/dist/chunks/studio-8jpzqk5s.js.map +10 -0
  68. package/dist/chunks/studio-94m77tgk.js +1482 -0
  69. package/dist/chunks/studio-94m77tgk.js.map +13 -0
  70. package/dist/chunks/studio-9je6w5sq.js +1000 -0
  71. package/dist/chunks/studio-9je6w5sq.js.map +16 -0
  72. package/dist/chunks/{studio-w7c6kvk7.js → studio-a7pzveq1.js} +2 -2
  73. package/dist/chunks/{studio-97qcgvdn.js → studio-adn0z1k3.js} +14 -8
  74. package/dist/chunks/{studio-97qcgvdn.js.map → studio-adn0z1k3.js.map} +3 -3
  75. package/dist/chunks/{studio-kr88ryr7.js → studio-bsxxx3x2.js} +17 -15
  76. package/dist/chunks/{studio-kr88ryr7.js.map → studio-bsxxx3x2.js.map} +7 -6
  77. package/dist/chunks/studio-bw5dz6km.js +608 -0
  78. package/dist/chunks/studio-bw5dz6km.js.map +21 -0
  79. package/dist/chunks/studio-c6s5getc.js +4232 -0
  80. package/dist/chunks/studio-c6s5getc.js.map +39 -0
  81. package/dist/chunks/studio-cjm7fdjf.js +451 -0
  82. package/dist/chunks/studio-cjm7fdjf.js.map +14 -0
  83. package/dist/chunks/{studio-cx91mnh8.js → studio-ckwz4d3x.js} +53 -53
  84. package/dist/chunks/{studio-cx91mnh8.js.map → studio-ckwz4d3x.js.map} +5 -5
  85. package/dist/chunks/studio-dg7zyt5x.js +28 -0
  86. package/dist/chunks/studio-dg7zyt5x.js.map +10 -0
  87. package/dist/chunks/studio-dzcyvvvz.js +166 -0
  88. package/dist/chunks/studio-dzcyvvvz.js.map +10 -0
  89. package/dist/chunks/{studio-7kqe41sa.js → studio-evkr5d3b.js} +15 -7
  90. package/dist/chunks/{studio-7kqe41sa.js.map → studio-evkr5d3b.js.map} +5 -4
  91. package/dist/chunks/studio-f12h6177.js +411 -0
  92. package/dist/chunks/studio-f12h6177.js.map +13 -0
  93. package/dist/chunks/studio-hbp1s8m9.js +1817 -0
  94. package/dist/chunks/studio-hbp1s8m9.js.map +14 -0
  95. package/dist/chunks/studio-k2sxjph5.js +15295 -0
  96. package/dist/chunks/studio-k2sxjph5.js.map +235 -0
  97. package/dist/chunks/studio-myje51t3.js +385 -0
  98. package/dist/chunks/studio-myje51t3.js.map +12 -0
  99. package/dist/chunks/studio-na6rwzhw.js +353 -0
  100. package/dist/chunks/studio-na6rwzhw.js.map +11 -0
  101. package/dist/chunks/{studio-4k0m4fmp.js → studio-p471gg5n.js} +126 -3
  102. package/dist/chunks/studio-p471gg5n.js.map +14 -0
  103. package/dist/chunks/{studio-9bjwf0d4.js → studio-rgwcpast.js} +5 -46
  104. package/dist/chunks/{studio-9bjwf0d4.js.map → studio-rgwcpast.js.map} +4 -5
  105. package/dist/chunks/{studio-hhj92gcr.js → studio-s519zc99.js} +3 -3
  106. package/dist/chunks/studio-sas7mt3k.js +75 -0
  107. package/dist/chunks/studio-sas7mt3k.js.map +10 -0
  108. package/dist/chunks/studio-t5wqwzdh.js +74 -0
  109. package/dist/chunks/studio-t5wqwzdh.js.map +10 -0
  110. package/dist/chunks/{studio-9qpspwfn.js → studio-temv5bw4.js} +5 -5
  111. package/dist/chunks/{studio-9qpspwfn.js.map → studio-temv5bw4.js.map} +1 -1
  112. package/dist/chunks/{studio-qkvt37c0.js → studio-tr20m8g8.js} +669 -18
  113. package/dist/chunks/studio-tr20m8g8.js.map +9 -0
  114. package/dist/chunks/{studio-ygzav7z5.js → studio-v96qfnww.js} +5514 -4291
  115. package/dist/chunks/studio-v96qfnww.js.map +54 -0
  116. package/dist/chunks/studio-vcetc91k.js +659 -0
  117. package/dist/chunks/studio-vcetc91k.js.map +10 -0
  118. package/dist/chunks/{studio-n9zmpxjg.js → studio-vmymjefp.js} +2972 -4937
  119. package/dist/chunks/studio-vmymjefp.js.map +98 -0
  120. package/dist/chunks/{studio-bghqfmea.js → studio-wwseqyw0.js} +2 -2
  121. package/dist/chunks/studio-x9hmy5kr.js +390 -0
  122. package/dist/chunks/studio-x9hmy5kr.js.map +10 -0
  123. package/dist/chunks/studio-xa0ce0n7.js +335 -0
  124. package/dist/chunks/studio-xa0ce0n7.js.map +10 -0
  125. package/dist/chunks/studio-xm3dbxvn.js +378 -0
  126. package/dist/chunks/studio-xm3dbxvn.js.map +19 -0
  127. package/dist/chunks/{studio-nqw433kv.js → studio-xmvbhg31.js} +2 -2
  128. package/dist/chunks/studio-zv37yrfa.js +1054 -0
  129. package/dist/chunks/studio-zv37yrfa.js.map +13 -0
  130. package/dist/chunks/{ws-client-q7hkz3j9.js → ws-client-654ts66j.js} +6 -4
  131. package/dist/chunks/ws-client-654ts66j.js.map +11 -0
  132. package/dist/iframe-entry.js +668 -230
  133. package/dist/studio.js +23711 -35185
  134. package/index.html +35 -4685
  135. package/package.json +17 -15
  136. package/src/about/about-modal.ts +42 -11
  137. package/src/browse/library-commands.ts +240 -0
  138. package/src/browse/library-layouts.ts +369 -0
  139. package/src/browse/library-model.ts +396 -0
  140. package/src/browse/library-pane.ts +1049 -0
  141. package/src/browse/library-preview.ts +252 -0
  142. package/src/browse/library-source.ts +150 -0
  143. package/src/canvas/canvas-helpers.ts +33 -9
  144. package/src/canvas/canvas-live-render.ts +45 -29
  145. package/src/canvas/canvas-patcher.ts +151 -31
  146. package/src/canvas/canvas-perf.ts +46 -7
  147. package/src/canvas/canvas-render.ts +1092 -220
  148. package/src/canvas/canvas-surface.ts +240 -0
  149. package/src/canvas/canvas-utils.ts +1166 -124
  150. package/src/canvas/iframe-drop.ts +2 -1
  151. package/src/canvas/iframe-entry.ts +241 -4
  152. package/src/canvas/iframe-host.ts +1226 -143
  153. package/src/canvas/iframe-inline-edit.ts +31 -0
  154. package/src/canvas/iframe-interaction.ts +76 -16
  155. package/src/canvas/iframe-keys.ts +107 -45
  156. package/src/canvas/iframe-overlay.ts +38 -1
  157. package/src/canvas/iframe-protocol.ts +137 -2
  158. package/src/canvas/iframe-render.ts +70 -2
  159. package/src/canvas/iframe-slash.ts +6 -0
  160. package/src/canvas/path-mapping.ts +56 -6
  161. package/src/canvas/surface-registry.ts +292 -0
  162. package/src/collab/collab-commands.ts +244 -0
  163. package/src/collab/collab-session.ts +198 -32
  164. package/src/collab/collab-state.ts +28 -2
  165. package/src/collab/presence-chips.ts +100 -9
  166. package/src/commands/active-registry.ts +42 -0
  167. package/src/commands/app-commands.ts +176 -0
  168. package/src/commands/budget.ts +158 -0
  169. package/src/commands/command-args.ts +253 -0
  170. package/src/commands/context.ts +387 -0
  171. package/src/commands/defaults.ts +580 -0
  172. package/src/commands/keymap.ts +571 -0
  173. package/src/commands/levels.ts +345 -0
  174. package/src/commands/live-context.ts +193 -0
  175. package/src/commands/reference.ts +227 -0
  176. package/src/commands/registry.ts +293 -0
  177. package/src/component-props.ts +3 -2
  178. package/src/content/draft-state.ts +106 -0
  179. package/src/content/entry-commands.ts +282 -0
  180. package/src/content/entry-editor.ts +327 -0
  181. package/src/content/entry-fields.ts +116 -0
  182. package/src/content/entry-model.ts +175 -0
  183. package/src/editor/canvas-context-menu.ts +17 -11
  184. package/src/editor/canvas-slash-bridge.ts +11 -4
  185. package/src/editor/context-menu.ts +804 -204
  186. package/src/editor/convert-to-component.ts +31 -10
  187. package/src/editor/convert-to-repeater.ts +3 -2
  188. package/src/editor/inline-edit-apply.ts +5 -5
  189. package/src/editor/inline-edit.ts +86 -50
  190. package/src/editor/insert-zone-action.ts +1 -1
  191. package/src/editor/shortcuts.ts +1043 -329
  192. package/src/editor/slash-menu.ts +1 -0
  193. package/src/files/elements.ts +166 -0
  194. package/src/files/file-ops.ts +178 -31
  195. package/src/files/files.ts +898 -268
  196. package/src/files/fs-events.ts +21 -0
  197. package/src/files/media-meta.ts +203 -0
  198. package/src/files/media-paths.ts +141 -0
  199. package/src/files/media-upload.ts +7 -3
  200. package/src/files/media-usage.ts +189 -0
  201. package/src/github/github-auth.ts +235 -33
  202. package/src/github/github-publish.ts +80 -21
  203. package/src/grid/grid-controller.ts +152 -12
  204. package/src/grid/grid-layout.ts +225 -20
  205. package/src/grid/grid-open.ts +120 -0
  206. package/src/grid/grid-panel.ts +554 -39
  207. package/src/grid/grid-source.ts +27 -1
  208. package/src/grid/redirects-grid.ts +599 -0
  209. package/src/grid/redirects.ts +398 -0
  210. package/src/grid/sources/content-source.ts +108 -2
  211. package/src/i18n/i18n-commands.ts +322 -0
  212. package/src/new-project/add-repo-modal.ts +8 -10
  213. package/src/new-project/import-tab.ts +34 -9
  214. package/src/new-project/location-fields.ts +9 -1
  215. package/src/new-project/new-project-modal.ts +270 -262
  216. package/src/packages/jxsuite-update.ts +97 -48
  217. package/src/packages/pull-package-sync.ts +18 -9
  218. package/src/page-params.ts +2 -1
  219. package/src/panels/activity-bar.ts +152 -91
  220. package/src/panels/activity-panel.ts +481 -0
  221. package/src/panels/ai-chat/chat-markdown.ts +8 -3
  222. package/src/panels/ai-chat/chat-view.ts +245 -18
  223. package/src/panels/ai-chat/composer.ts +31 -5
  224. package/src/panels/ai-chat/sessions-view.ts +17 -10
  225. package/src/panels/ai-panel.ts +279 -120
  226. package/src/panels/block-action-bar.ts +1115 -224
  227. package/src/panels/bottom-dock.ts +337 -0
  228. package/src/panels/canvas-dnd-bridge.ts +37 -8
  229. package/src/panels/chat-panel.ts +29 -12
  230. package/src/panels/data-explorer.ts +254 -112
  231. package/src/panels/data-grid.ts +10 -4
  232. package/src/panels/dnd.ts +28 -51
  233. package/src/panels/editors.ts +434 -107
  234. package/src/panels/elements-panel.ts +66 -2
  235. package/src/panels/empty-state.ts +112 -0
  236. package/src/panels/events-panel.ts +596 -82
  237. package/src/panels/formula-workspace.ts +408 -218
  238. package/src/panels/frontmatter-fields.ts +51 -10
  239. package/src/panels/frontmatter-panel.ts +357 -79
  240. package/src/panels/git-panel.ts +375 -164
  241. package/src/panels/head-panel.ts +791 -85
  242. package/src/panels/i18n-panel.ts +592 -0
  243. package/src/panels/imports-panel.ts +95 -85
  244. package/src/panels/jump-bar.ts +584 -0
  245. package/src/panels/layers-panel.ts +1106 -328
  246. package/src/panels/left-panel.ts +109 -204
  247. package/src/panels/navigator-panels.ts +131 -0
  248. package/src/panels/overlays.ts +7 -2
  249. package/src/panels/pane-context.ts +1477 -0
  250. package/src/panels/pane-grid.ts +406 -0
  251. package/src/panels/panel-registry.ts +340 -0
  252. package/src/panels/panel-scheduler.ts +75 -0
  253. package/src/panels/problems-panel.ts +234 -0
  254. package/src/panels/properties-panel.ts +946 -1139
  255. package/src/panels/provenance.ts +258 -0
  256. package/src/panels/quick-search.ts +791 -122
  257. package/src/panels/right-panel.ts +228 -102
  258. package/src/panels/seo-modal.ts +395 -0
  259. package/src/panels/settings-pane.ts +203 -0
  260. package/src/panels/signals-panel.ts +354 -137
  261. package/src/panels/statement-editor.ts +41 -25
  262. package/src/panels/statusbar.ts +342 -106
  263. package/src/panels/style-panel.ts +974 -463
  264. package/src/panels/style-utils.ts +3 -3
  265. package/src/panels/stylebook-doc.ts +4 -2
  266. package/src/panels/stylebook-layers-panel.ts +3 -2
  267. package/src/panels/stylebook-panel.ts +51 -26
  268. package/src/panels/tab-strip.ts +1058 -78
  269. package/src/panels/target-line.ts +274 -0
  270. package/src/panels/toolbar.ts +631 -484
  271. package/src/panels/welcome-screen.ts +301 -198
  272. package/src/platform-errors.ts +26 -3
  273. package/src/platform.ts +96 -2
  274. package/src/platforms/cloud.ts +71 -8
  275. package/src/platforms/devserver.ts +92 -29
  276. package/src/publish/deploy-checklist.ts +383 -0
  277. package/src/publish/pages-service.ts +10 -1
  278. package/src/publish/publish-commands.ts +218 -0
  279. package/src/publish/publish-panel.ts +110 -16
  280. package/src/recent-projects.ts +3 -2
  281. package/src/services/a11y-report.ts +505 -0
  282. package/src/services/ai-models.ts +75 -2
  283. package/src/services/ai-project-tools.ts +29 -10
  284. package/src/services/ai-system-prompt.ts +53 -14
  285. package/src/services/ai-tools.ts +39 -3
  286. package/src/services/ai-writes.ts +154 -0
  287. package/src/services/announce.ts +97 -0
  288. package/src/services/automation.ts +424 -175
  289. package/src/services/cem-export.ts +9 -4
  290. package/src/services/clock.ts +61 -0
  291. package/src/services/document-assistant.ts +36 -8
  292. package/src/services/idle.ts +179 -0
  293. package/src/services/import-client.ts +26 -0
  294. package/src/services/monaco-buffer.ts +627 -0
  295. package/src/services/monaco-lazy.ts +36 -0
  296. package/src/services/notify.ts +271 -0
  297. package/src/services/profile.ts +155 -0
  298. package/src/services/references.ts +0 -0
  299. package/src/services/token-lint.ts +3 -2
  300. package/src/services/tool-executor.ts +53 -7
  301. package/src/services/trusted-types.ts +123 -0
  302. package/src/settings/contexts-section.ts +427 -0
  303. package/src/settings/contributed-section.ts +149 -11
  304. package/src/settings/css-vars-editor.ts +306 -349
  305. package/src/settings/defs-editor.ts +93 -34
  306. package/src/settings/dependencies-editor.ts +7 -7
  307. package/src/settings/extension-sections.ts +37 -5
  308. package/src/settings/general-settings.ts +204 -121
  309. package/src/settings/locales-section.ts +347 -0
  310. package/src/settings/preferences-accounts.ts +140 -0
  311. package/src/settings/preferences-dialog.ts +620 -0
  312. package/src/settings/preferences-keymap.ts +270 -0
  313. package/src/settings/project-sections.ts +260 -0
  314. package/src/settings/section-registry.ts +169 -0
  315. package/src/settings/settings-document.ts +385 -0
  316. package/src/shell.ts +1549 -0
  317. package/src/site-context.ts +45 -20
  318. package/src/state.ts +14 -275
  319. package/src/store.ts +88 -43
  320. package/src/studio.ts +790 -346
  321. package/src/style/live-preview.ts +36 -0
  322. package/src/style/project-styles.ts +349 -0
  323. package/src/style/token-ref.ts +123 -0
  324. package/src/tabs/patch-ops.ts +16 -4
  325. package/src/tabs/project-config.ts +416 -0
  326. package/src/tabs/selection.ts +323 -0
  327. package/src/tabs/tab.ts +182 -47
  328. package/src/tabs/transact.ts +209 -28
  329. package/src/types.ts +68 -13
  330. package/src/ui/ai-credentials-form.ts +39 -35
  331. package/src/ui/ai-managed-connect.ts +113 -0
  332. package/src/ui/dynamic-slot.ts +224 -81
  333. package/src/ui/expression-editor.ts +71 -63
  334. package/src/ui/field-input.ts +4 -6
  335. package/src/ui/field-row.ts +215 -38
  336. package/src/ui/form-controls.ts +194 -7
  337. package/src/ui/layers.ts +492 -69
  338. package/src/ui/media-picker.ts +115 -25
  339. package/src/ui/panel-resize.ts +109 -91
  340. package/src/ui/progress-modal.ts +131 -50
  341. package/src/ui/regions.ts +462 -0
  342. package/src/ui/schema-form.ts +229 -45
  343. package/src/ui/spectrum.ts +24 -8
  344. package/src/ui/timing.ts +49 -0
  345. package/src/ui/value-selector.ts +20 -0
  346. package/src/ui/value-source.ts +348 -0
  347. package/src/ui/virtual-window.ts +400 -0
  348. package/src/utils/edit-display.ts +3 -2
  349. package/src/utils/inherited-style.ts +67 -37
  350. package/src/view.ts +64 -78
  351. package/src/workspace/pane-derive.ts +1258 -0
  352. package/src/workspace/session.ts +317 -0
  353. package/src/workspace/workspace.ts +1345 -23
  354. package/styles/canvas.css +653 -0
  355. package/styles/forced-colors.css +106 -0
  356. package/styles/inspector.css +1454 -0
  357. package/styles/overlays.css +2704 -0
  358. package/styles/panels.css +1701 -0
  359. package/styles/shell.css +1423 -0
  360. package/styles/tokens.css +151 -0
  361. package/dist/chunks/Overlay.dev-cszx7yn5.js +0 -22
  362. package/dist/chunks/extension-sections-qct0gt8f.js +0 -531
  363. package/dist/chunks/extension-sections-qct0gt8f.js.map +0 -12
  364. package/dist/chunks/files-02zn5y24.js +0 -50
  365. package/dist/chunks/files-02zn5y24.js.map +0 -9
  366. package/dist/chunks/git-panel-mbb0y0hz.js +0 -28
  367. package/dist/chunks/git-panel-mbb0y0hz.js.map +0 -9
  368. package/dist/chunks/layers-panel-9qekmqk1.js +0 -26
  369. package/dist/chunks/layers-panel-9qekmqk1.js.map +0 -9
  370. package/dist/chunks/settings-modal-gj0hrah4.js +0 -29
  371. package/dist/chunks/settings-modal-gj0hrah4.js.map +0 -9
  372. package/dist/chunks/sp-overlay.dev-earn5as8.js +0 -17
  373. package/dist/chunks/sp-overlay.dev-earn5as8.js.map +0 -9
  374. package/dist/chunks/sp-popover.dev-frxtfejn.js +0 -8
  375. package/dist/chunks/sp-popover.dev-frxtfejn.js.map +0 -9
  376. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js +0 -14
  377. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js.map +0 -9
  378. package/dist/chunks/sp-tooltip.dev-a9fywesm.js +0 -21
  379. package/dist/chunks/sp-tooltip.dev-a9fywesm.js.map +0 -10
  380. package/dist/chunks/studio-4k0m4fmp.js.map +0 -13
  381. package/dist/chunks/studio-568nx4t5.js +0 -469
  382. package/dist/chunks/studio-568nx4t5.js.map +0 -14
  383. package/dist/chunks/studio-9smfzqhc.js +0 -19
  384. package/dist/chunks/studio-9smfzqhc.js.map +0 -10
  385. package/dist/chunks/studio-asvf0j9e.js +0 -429
  386. package/dist/chunks/studio-asvf0j9e.js.map +0 -13
  387. package/dist/chunks/studio-d77qm3pk.js +0 -3427
  388. package/dist/chunks/studio-d77qm3pk.js.map +0 -26
  389. package/dist/chunks/studio-j6gqyjf4.js +0 -1854
  390. package/dist/chunks/studio-j6gqyjf4.js.map +0 -18
  391. package/dist/chunks/studio-n9zmpxjg.js.map +0 -115
  392. package/dist/chunks/studio-qkvt37c0.js.map +0 -9
  393. package/dist/chunks/studio-rq0vxb19.js +0 -3744
  394. package/dist/chunks/studio-rq0vxb19.js.map +0 -22
  395. package/dist/chunks/studio-wd6ewdvd.js +0 -853
  396. package/dist/chunks/studio-wd6ewdvd.js.map +0 -12
  397. package/dist/chunks/studio-xq0t7n5t.js +0 -140
  398. package/dist/chunks/studio-xq0t7n5t.js.map +0 -10
  399. package/dist/chunks/studio-xze4zrs1.js.map +0 -10
  400. package/dist/chunks/studio-ygzav7z5.js.map +0 -46
  401. package/dist/chunks/studio-zmy7241e.js +0 -399
  402. package/dist/chunks/studio-zmy7241e.js.map +0 -12
  403. package/dist/chunks/ws-client-q7hkz3j9.js.map +0 -11
  404. package/src/browse/browse-modal.ts +0 -69
  405. package/src/browse/browse.ts +0 -904
  406. package/src/canvas/canvas-diff.ts +0 -216
  407. package/src/canvas/nested-site-style.ts +0 -58
  408. package/src/new-project/design-fields.ts +0 -264
  409. package/src/new-project/templates.ts +0 -61
  410. package/src/panels/tab-bar.ts +0 -548
  411. package/src/settings/settings-modal.ts +0 -203
  412. /package/dist/chunks/{adapter-z8fkmbmv.js.map → adapter-qfnesn5v.js.map} +0 -0
  413. /package/dist/chunks/{cf-settings-mmyv436d.js.map → cf-settings-73sh5z4p.js.map} +0 -0
  414. /package/dist/chunks/{format-host-2c188n41.js.map → format-host-mkwsmt8d.js.map} +0 -0
  415. /package/dist/chunks/{jx-validate-qx8degj7.js.map → jx-validate-v6qgknrs.js.map} +0 -0
  416. /package/dist/chunks/{studio-w7c6kvk7.js.map → studio-a7pzveq1.js.map} +0 -0
  417. /package/dist/chunks/{studio-hhj92gcr.js.map → studio-s519zc99.js.map} +0 -0
  418. /package/dist/chunks/{studio-bghqfmea.js.map → studio-wwseqyw0.js.map} +0 -0
  419. /package/dist/chunks/{studio-nqw433kv.js.map → studio-xmvbhg31.js.map} +0 -0
@@ -0,0 +1,340 @@
1
+ /// <reference lib="dom" />
2
+ /**
3
+ * Panel-registry.ts — one definition site per panel.
4
+ *
5
+ * A {@link PanelRecord} is to a surface what a `Command` is to an action: its name, the containment
6
+ * level of the state it WRITES, the dock that hosts it, the predicate that decides whether it
7
+ * exists at all, and the function that draws it. The rail, the panel header and the dock budget
8
+ * become RENDERINGS of these records — plan §2 principle 1, applied to regions rather than verbs.
9
+ *
10
+ * It mirrors the shipped `settings/settings-modal.ts` contribution point
11
+ * (`registerSettingsSection`) deliberately: a module-level `Map`, keyed by id, written by the
12
+ * module that OWNS the surface. The one thing it adds is the field that makes the rail derivable —
13
+ *
14
+ * **`level` is required, and it is the level of the state the panel WRITES, not the state it
15
+ * reads** (plan §2 principle 3). Insert reads the project's component registry and writes the
16
+ * document tree, so it is `document`. Files reads documents and writes project files, so it is
17
+ * `project`. That rule, and not intuition, is what settles every contested placement and what stops
18
+ * the rail re-accreting: the two rail groups are two rows of
19
+ * {@link
20
+ * import("../commands/levels").PANEL_PLACEMENT_MATRIX}, checked at registration exactly the
21
+ * way a command's `menus` are.
22
+ *
23
+ * The predecessor was an eight-item array literal inside `panels/activity-bar.ts` — `{icon, label,
24
+ * value}` — beside an eight-branch `if` chain in `panels/left-panel.ts` and a third list in
25
+ * `shell.ts`. Three places to keep in step, none of which recorded what a panel was FOR.
26
+ */
27
+
28
+ import { resolveI18n } from "@jxsuite/schema/locale";
29
+ import { checkPanelPlacement } from "../commands/levels";
30
+ import { emptyContext } from "../commands/context";
31
+ import { projectState } from "../store";
32
+ import { shell } from "../shell";
33
+ import { activeTab } from "../workspace/workspace";
34
+ import type { Level } from "../commands/levels";
35
+ import type { CommandContext } from "../commands/context";
36
+ import type { GitDiffState } from "../types";
37
+ import type { JxMutableNode } from "@jxsuite/schema/types";
38
+ import type { JxPath } from "../state";
39
+ import type { Tab, TabUi } from "../tabs/tab";
40
+ import type { nothing, TemplateResult } from "lit-html";
41
+ import type { renderGitPanel } from "./git-panel";
42
+ import type { renderHeadTemplate } from "./head-panel";
43
+ import type { renderImportsTemplate } from "./imports-panel";
44
+ import type { renderSignalsTemplate } from "./signals-panel";
45
+
46
+ /**
47
+ * The three docks that host panels.
48
+ *
49
+ * A panel's dock is where its BODY is drawn. Every rail button opens the NAVIGATOR, so a record
50
+ * outside it is `rail: false` and is addressed by name instead — `view.setBottomTab` for the four
51
+ * Bottom-dock tabs. The two were briefly independent, for Problems alone, and the cost was a
52
+ * per-dock branch in three separate places plus a left-hand control that opened something along the
53
+ * bottom. The Inspector has no records yet.
54
+ */
55
+ export type PanelDock = "navigator" | "inspector" | "bottom";
56
+
57
+ /**
58
+ * What a Navigator panel is handed that its own module cannot import.
59
+ *
60
+ * This is `left-panel.ts`'s old `LeftPanelCtx`, moved to the registry because it is now the panel
61
+ * CONTRACT rather than one orchestrator's private argument list. `studio.ts` still builds it: the
62
+ * renderers it injects (the file tree, the head panel, the git panel) close over bootstrap state,
63
+ * and injecting them is what keeps `panels/*` free of an import cycle back through `studio.ts`.
64
+ */
65
+ export interface NavigatorPanelDeps {
66
+ getCanvasMode: () => string;
67
+ setCanvasMode: (tab: Tab | null, mode: string) => void;
68
+ // Typed against their implementations so every call site stays checked. These are type-only
69
+ // Imports, so they are erased — the modules themselves are never pulled in through this file.
70
+ renderImportsTemplate: typeof renderImportsTemplate;
71
+ renderFilesTemplate: () => TemplateResult;
72
+ renderSignalsTemplate: typeof renderSignalsTemplate;
73
+ renderHeadTemplate: typeof renderHeadTemplate;
74
+ renderGitPanel: typeof renderGitPanel;
75
+ renderCanvas: () => void;
76
+ /** Canvas re-render that also lets automatic `Request` entries fetch (Data panel Refresh). */
77
+ refreshData: () => void;
78
+ defCategory: (def: unknown) => string;
79
+ defBadgeLabel: (def: unknown) => string;
80
+ navigateToComponent: (path: string) => void;
81
+ webdata: Record<string, unknown>;
82
+ defaultDef: (tag: string) => Record<string, unknown>;
83
+ registerLayersDnD: () => void;
84
+ registerElementsDnD: () => void;
85
+ registerComponentsDnD: () => void;
86
+ setupTreeKeyboard: (tree: HTMLElement) => void;
87
+ registerFileTreeDnD: (ctx: { renderLeftPanel: () => void }) => void;
88
+ setGitDiffState: (state: GitDiffState | null) => void;
89
+ cloneRepository?: () => void;
90
+ }
91
+
92
+ /**
93
+ * The focused document, as a document-level panel reads it. `null` when no tab is open.
94
+ *
95
+ * A `level: "project"` panel must not read this — that is principle 3's corollary, and the reason
96
+ * the Source Control badge used to vanish when the last tab closed. {@link registerPanel} cannot
97
+ * enforce it (a closure's imports are not inspectable), so the contract is stated here and asserted
98
+ * by `tests/panel-registry.test.ts`, which renders every project-level panel with `doc: null`.
99
+ */
100
+ export interface NavigatorDocument {
101
+ document: JxMutableNode;
102
+ mode: string;
103
+ /** The whole selection SET the navigator/inspector panels render against (§6.5). */
104
+ selection: JxPath[];
105
+ canvas: Record<string, unknown> | null;
106
+ content?: { frontmatter?: Record<string, unknown> } | undefined;
107
+ documentPath?: string | null | undefined;
108
+ ui: TabUi;
109
+ }
110
+
111
+ /** Everything a panel's `render` receives. One argument, so a record stays a one-liner. */
112
+ export interface NavigatorPanelContext {
113
+ deps: NavigatorPanelDeps;
114
+ /** The focused document, or `null`. Document-level panels render their empty state on `null`. */
115
+ doc: NavigatorDocument | null;
116
+ /** Repaint the Navigator — `left-panel.ts`'s scheduler, not a synchronous re-render. */
117
+ rerender: () => void;
118
+ }
119
+
120
+ /** A panel's rendered body. `nothing` is legal — an empty panel is a state, not a bug. */
121
+ export type PanelBody = TemplateResult | typeof nothing;
122
+
123
+ /** One panel, defined once. */
124
+ export interface PanelRecord {
125
+ /** Lowercase, stable, and the value `view.setActivity` accepts — renaming one is a shot break. */
126
+ id: string;
127
+ /** The human name. The rail label AND the panel header, so there is one place it is spelled. */
128
+ title: string;
129
+ /** REQUIRED. The level of the state this panel WRITES. Checked against the panel matrix. */
130
+ level: Level;
131
+ dock: PanelDock;
132
+ /** Icon key, resolved through `activity-bar.ts`'s `tabIcon()` map. */
133
+ icon: string;
134
+ /**
135
+ * `false` for a panel with no rail button.
136
+ *
137
+ * Not a hiding mechanism — a rail-less panel is still reachable by `view.setActivity`, by the
138
+ * palette and by another surface linking to it. It is how a record survives the interval between
139
+ * losing its rail slot and gaining its real home (Insert becomes an overlay in P3.5; State folds
140
+ * into Data). Declaring it `false` also removes the panel's `rail/<level>` placement, which is
141
+ * what keeps the rail inside its four-per-group budget.
142
+ */
143
+ rail?: boolean;
144
+ /**
145
+ * The sentence a document-level panel shows instead of its body when no document is open.
146
+ *
147
+ * Said in the words of the thing the panel needs ("Open a page to see the elements it is built
148
+ * from."), so the ring collapses to a sentence rather than to an empty box. Absent means the
149
+ * panel renders with `doc: null` — Insert does, because an element palette is meaningful before
150
+ * you have chosen where to put anything.
151
+ */
152
+ requiresDocument?: string;
153
+ /** Hide entirely — for a surface that does not exist yet. Default: always present. */
154
+ when?: (ctx: CommandContext) => boolean;
155
+ /** The rail badge, e.g. Source Control's changed-file count. `null`/`0` renders nothing. */
156
+ badge?: (ctx: CommandContext) => number | string | null;
157
+ render: (ctx: NavigatorPanelContext) => PanelBody;
158
+ /**
159
+ * Imperative work that needs the painted DOM — drag registrations, tree keyboard wiring.
160
+ *
161
+ * The eight `if (tab === …)` post-render branches `left-panel.ts` carried, moved onto the records
162
+ * that caused them, so adding a panel cannot forget to drain the previous one's registrations.
163
+ */
164
+ afterRender?: (ctx: NavigatorPanelContext, host: HTMLElement) => void;
165
+ }
166
+
167
+ /**
168
+ * The context a panel's `when` and `badge` are evaluated against.
169
+ *
170
+ * A SUBSET of the command context, built from the two records a panel predicate has ever needed:
171
+ * the reactive `shell` (project-level source control, hoisted off `TabUi` for exactly this reason)
172
+ * and `projectState`. Reading it inside a rendering effect is what makes the rail repaint when the
173
+ * working tree changes, because both reads are tracked.
174
+ *
175
+ * **A key a panel gates on has to be here.** A subset is only safe while it covers every predicate
176
+ * actually written: `isMultilingual` was declared, computed in `live-context.ts`, and left out of
177
+ * this one — so the Languages panel's `when` read the `emptyContext()` default, and the Navigator
178
+ * answered "No Navigator panel is registered as i18n" on a project that declares three languages.
179
+ * Nothing failed; the panel was simply never visible.
180
+ *
181
+ * It is deliberately not `createLiveContext()`: that builder needs four facts only `studio.ts` can
182
+ * inject (the canvas mode, the caret, the modal flag, the platform), and a rail that imported the
183
+ * bootstrap to draw a badge would be the import cycle `live-context.ts` documents avoiding. When
184
+ * the bootstrap wires panels the same way it wires commands, this function is what it replaces.
185
+ */
186
+ export function panelContext(): CommandContext {
187
+ const ctx = emptyContext();
188
+ ctx.project.open = projectState !== null;
189
+ ctx.project.isMultilingual =
190
+ (resolveI18n(projectState?.projectConfig ?? {}).i18n?.locales.length ?? 0) > 1;
191
+ ctx.git.dirtyCount = shell.git.status?.files?.length ?? 0;
192
+ ctx.document.open = activeTab.value !== null;
193
+ return ctx;
194
+ }
195
+
196
+ /** `[a-z]` then word characters — the same shape `navigator/panel:<id>` is matched by. */
197
+ const PANEL_ID_PATTERN = /^[a-z][\w-]*$/;
198
+
199
+ const panelRegistry = new Map<string, PanelRecord>();
200
+
201
+ /**
202
+ * Define a panel. Throws on a duplicate id, a malformed id, or a matrix violation.
203
+ *
204
+ * Failing at registration is the same bet `createCommandRegistry.register` makes: a misplaced
205
+ * surface is a design error, and a design error that only shows up as a wrong-looking rail is a
206
+ * design error nobody fixes.
207
+ *
208
+ * @param {PanelRecord} panel
209
+ */
210
+ export function registerPanel(panel: PanelRecord): void {
211
+ if (!PANEL_ID_PATTERN.test(panel.id)) {
212
+ throw new Error(
213
+ `Panel id "${panel.id}" is malformed — expected /^[a-z][\\w-]*$/, because the id is also ` +
214
+ `the region "navigator/panel:${panel.id}" and the value view.setActivity accepts.`,
215
+ );
216
+ }
217
+ if (panelRegistry.has(panel.id)) {
218
+ throw new Error(
219
+ `Panel "${panel.id}" is already registered — a second definition site is the defect this ` +
220
+ `registry exists to prevent.`,
221
+ );
222
+ }
223
+ const violations = checkPanelPlacement({
224
+ id: panel.id,
225
+ level: panel.level,
226
+ dock: panel.dock,
227
+ rail: panel.rail,
228
+ });
229
+ if (violations.length > 0) {
230
+ const detail = violations.map((violation) => ` ✗ ${panel.id} ${violation.message}`).join("\n");
231
+ throw new Error(`Panel "${panel.id}" violates the level × placement matrix:\n${detail}`);
232
+ }
233
+ panelRegistry.set(panel.id, panel);
234
+ }
235
+
236
+ /** Remove a panel — the contributed-extension path, and the reset every test needs. */
237
+ export function unregisterPanel(id: string): void {
238
+ panelRegistry.delete(id);
239
+ }
240
+
241
+ /** Drop every registration. Tests only; the app registers once, at mount. */
242
+ export function resetPanels(): void {
243
+ panelRegistry.clear();
244
+ }
245
+
246
+ /** One panel by id, or `undefined`. */
247
+ export function getPanel(id: string): PanelRecord | undefined {
248
+ return panelRegistry.get(id);
249
+ }
250
+
251
+ /** Every registered panel, in registration order — which is rail order. */
252
+ export function listPanels(dock?: PanelDock): readonly PanelRecord[] {
253
+ const all = [...panelRegistry.values()];
254
+ return dock ? all.filter((panel) => panel.dock === dock) : all;
255
+ }
256
+
257
+ /** Whether a panel's `when` admits it in this context. A record with no `when` always does. */
258
+ export function isPanelVisible(panel: PanelRecord, ctx: CommandContext): boolean {
259
+ return panel.when ? panel.when(ctx) : true;
260
+ }
261
+
262
+ /** One rail group: the level it hosts, its heading, and the panels in it. */
263
+ export interface RailGroup {
264
+ level: Level;
265
+ /** The group's accessible name — "Project" / "Document", the levels said out loud. */
266
+ label: string;
267
+ panels: readonly PanelRecord[];
268
+ }
269
+
270
+ /** The rail's two groups, in shell order. `application` and `selection` have no group. */
271
+ const RAIL_GROUP_LEVELS: readonly { level: Level; label: string }[] = [
272
+ { level: "project", label: "Project" },
273
+ { level: "document", label: "Document" },
274
+ ];
275
+
276
+ /**
277
+ * Every panel that has a rail button, whatever dock draws its body.
278
+ *
279
+ * The rail is NOT the Navigator's tab strip. `panelPlacements()` has always awarded `rail/<level>`
280
+ * on `rail !== false` alone — the dock is a separate placement — and Problems is the panel that
281
+ * proves it: its body is the Bottom dock's first tab (plan §7.2), and it still owns a rail slot and
282
+ * a badge, because "how many things need fixing" is a question you ask without opening anything.
283
+ * Filtering this by `dock === "navigator"` is what would put the same list in two hosts at once.
284
+ */
285
+ function railablePanels(): readonly PanelRecord[] {
286
+ return listPanels().filter((panel) => panel.rail !== false);
287
+ }
288
+
289
+ /**
290
+ * The rail, as data: every rail-able panel, grouped by level, `when`-filtered.
291
+ *
292
+ * Empty groups are dropped, so the divider between them is never drawn against nothing.
293
+ *
294
+ * @param {CommandContext} ctx
295
+ */
296
+ export function railGroups(ctx: CommandContext): RailGroup[] {
297
+ const railable = railablePanels();
298
+ return RAIL_GROUP_LEVELS.map(({ level, label }) => ({
299
+ level,
300
+ label,
301
+ panels: railable.filter((panel) => panel.level === level && isPanelVisible(panel, ctx)),
302
+ })).filter((group) => group.panels.length > 0);
303
+ }
304
+
305
+ /**
306
+ * The rail's panels flattened into rail order — PROJECT group, then DOCUMENT group.
307
+ *
308
+ * This is the order ⌘1–8 follow, and it is derived from the same two rows the rail draws rather
309
+ * than from registration order, so a panel registered by its dock's module (Problems, from
310
+ * `panels/bottom-dock.ts`) still lands in the slot its LEVEL gives it. `when` is not applied: a
311
+ * chord roster that changed length with context would move every chord after the hidden panel.
312
+ */
313
+ export function railPanelSet(): PanelRecord[] {
314
+ const railable = railablePanels();
315
+ return RAIL_GROUP_LEVELS.flatMap(({ level }) =>
316
+ railable.filter((panel) => panel.level === level),
317
+ );
318
+ }
319
+
320
+ /** One tabbed region and what it declares, in `commands/budget.ts`'s shape. */
321
+ export interface PanelDockDeclaration {
322
+ dock: string;
323
+ tabs: readonly string[];
324
+ }
325
+
326
+ /**
327
+ * The rail's two groups as budget declarations — the `rail/*` rows `scripts/check-chrome-budget.ts`
328
+ * counts, observed rather than written down. `commands/budget.ts` no longer carries a copy.
329
+ *
330
+ * `when` is deliberately NOT applied: the budget caps what the shell may GROW to, not what happens
331
+ * to be visible in one context. Search is registered and hidden today, and it still spends its rail
332
+ * slot — which is the point, because its arrival must not silently push the rail to five.
333
+ */
334
+ export function railDeclarations(): PanelDockDeclaration[] {
335
+ const railable = railablePanels();
336
+ return RAIL_GROUP_LEVELS.map(({ level }) => ({
337
+ dock: `rail/${level}`,
338
+ tabs: railable.filter((panel) => panel.level === level).map((panel) => panel.title),
339
+ })).filter((declaration) => declaration.tabs.length > 0);
340
+ }
@@ -33,6 +33,53 @@ export function isTextInput(el: Element | null): boolean {
33
33
  return false;
34
34
  }
35
35
 
36
+ /**
37
+ * Every bound scheduler, so "is any panel still owing a repaint?" has one answer.
38
+ *
39
+ * Condition 2 of the `probe.idle()` predicate (`services/idle.ts`, spec §13.5). Each scheduler
40
+ * already knew whether it had a frame queued or a render withheld; nothing could ask all of them at
41
+ * once. P4.4 makes the withheld case visible to the author too — a panel silently showing stale
42
+ * state is a bug whichever consumer notices it first.
43
+ */
44
+ const boundSchedulers = new Set<TrackedScheduler>();
45
+
46
+ interface TrackedScheduler {
47
+ /** The panel root, for the human-readable blocker string. */
48
+ root: HTMLElement;
49
+ /** A coalescing frame is queued and will paint. */
50
+ hasFrame: () => boolean;
51
+ /** A render was requested and is being WITHHELD because a field in the panel has focus. */
52
+ isDeferring: () => boolean;
53
+ }
54
+
55
+ /** `#right-panel`, `.frontmatter-panel`, `div` — the most specific handle the root offers. */
56
+ function describeRoot(root: HTMLElement): string {
57
+ if (root.id) {
58
+ return `#${root.id}`;
59
+ }
60
+ const first = root.className.split(/\s+/).find(Boolean);
61
+ return first ? `.${first}` : root.tagName.toLowerCase();
62
+ }
63
+
64
+ /**
65
+ * One line per panel that still owes a repaint — empty when every panel's DOM matches its state.
66
+ *
67
+ * A withheld render is reported separately from a queued one because they end differently: a queued
68
+ * frame lands on its own, a withheld one waits for a focusout that may never come. A predicate that
69
+ * conflated them would either hang or lie.
70
+ */
71
+ export function pendingSchedulers(): string[] {
72
+ const blockers: string[] = [];
73
+ for (const entry of boundSchedulers) {
74
+ if (entry.hasFrame()) {
75
+ blockers.push(`${describeRoot(entry.root)} has a frame queued`);
76
+ } else if (entry.isDeferring()) {
77
+ blockers.push(`${describeRoot(entry.root)} is withholding a render (a field has focus)`);
78
+ }
79
+ }
80
+ return blockers;
81
+ }
82
+
36
83
  export interface PanelScheduler {
37
84
  /** Request a render. Coalesced; deferred while a text input in the panel is focused. */
38
85
  schedule: () => void;
@@ -72,6 +119,23 @@ export function createPanelScheduler(opts: {
72
119
 
73
120
  const blocked = () => editing || (blockWhile ? blockWhile() : false);
74
121
 
122
+ /**
123
+ * Publish the withheld render to the person looking at the panel.
124
+ *
125
+ * A deferred render is the panel deciding, correctly, that finishing your sentence matters more
126
+ * than being current — but until P4 it made that trade in silence, so a panel showing state from
127
+ * before your last edit was indistinguishable from a panel that had stopped working. The
128
+ * attribute is the whole mechanism: `styles/overlays.css` hangs the hint off it, so every panel
129
+ * that already routes through this scheduler inherits it without a line of its own.
130
+ */
131
+ function markStale(stale: boolean) {
132
+ if (stale) {
133
+ root.dataset.jxStale = "";
134
+ } else {
135
+ delete root.dataset.jxStale;
136
+ }
137
+ }
138
+
75
139
  function flush() {
76
140
  scheduled = false;
77
141
  rafId = 0;
@@ -81,9 +145,11 @@ export function createPanelScheduler(opts: {
81
145
  // Defer while a field is focused — flushed later by focusout (or the next schedule()).
82
146
  if (blocked()) {
83
147
  pending = true;
148
+ markStale(true);
84
149
  return;
85
150
  }
86
151
  pending = false;
152
+ markStale(false);
87
153
  rendering = true;
88
154
  try {
89
155
  render();
@@ -114,6 +180,13 @@ export function createPanelScheduler(opts: {
114
180
  }
115
181
  }
116
182
 
183
+ const tracked: TrackedScheduler = {
184
+ hasFrame: () => scheduled,
185
+ isDeferring: () => pending,
186
+ root,
187
+ };
188
+ boundSchedulers.add(tracked);
189
+
117
190
  return {
118
191
  bindFocus() {
119
192
  root.addEventListener("focusin", onFocusIn);
@@ -130,6 +203,7 @@ export function createPanelScheduler(opts: {
130
203
  isEditing: () => blocked(),
131
204
  schedule,
132
205
  unbind() {
206
+ boundSchedulers.delete(tracked);
133
207
  root.removeEventListener("focusin", onFocusIn);
134
208
  root.removeEventListener("focusout", onFocusOut);
135
209
  if (rafId) {
@@ -138,6 +212,7 @@ export function createPanelScheduler(opts: {
138
212
  scheduled = false;
139
213
  pending = false;
140
214
  editing = false;
215
+ markStale(false);
141
216
  rendering = false;
142
217
  },
143
218
  };