@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
@@ -6,9 +6,8 @@
6
6
  */
7
7
 
8
8
  import { html, nothing } from "lit-html";
9
+ import { displayTagName } from "@jxsuite/schema/guards";
9
10
  import { classMap } from "lit-html/directives/class-map.js";
10
- import { live } from "lit-html/directives/live.js";
11
- import { isRef } from "@jxsuite/schema/guards";
12
11
  import { dynamicRouteParams } from "../page-params";
13
12
  import { projectState } from "../state";
14
13
  import type { JsonValue } from "../types";
@@ -22,12 +21,28 @@ import {
22
21
  } from "../tabs/transact";
23
22
  import { renderFieldRow } from "../ui/field-row";
24
23
  import { rawTextArea, spTextField } from "../ui/field-input";
25
- import { expressionHint, renderExpressionEditor } from "../ui/expression-editor";
24
+ import {
25
+ expressionHint,
26
+ isActionExpression,
27
+ renderExpressionEditor,
28
+ } from "../ui/expression-editor";
29
+ import { renderEmptyState } from "./empty-state";
30
+ import {
31
+ dataTypeLabel,
32
+ expandedDataRows,
33
+ isDataRowExpanded,
34
+ renderDataTreeTemplate,
35
+ setDataRowExpanded,
36
+ unwrapSignal,
37
+ } from "./data-explorer";
38
+ import { openLogicTarget } from "./formula-workspace";
39
+ import { bindableSignalNames } from "./properties-panel";
26
40
  import { renderStatementEditor } from "./statement-editor";
41
+ import { NAVIGATOR_STATEMENTS_REGION } from "../ui/regions";
27
42
  import { livePreviewExpression } from "../services/live-preview";
28
43
  import { renderMediaPicker } from "../ui/media-picker";
29
44
  import { renderOnly } from "../store";
30
- import { registerFormControl, renderForm } from "../ui/schema-form";
45
+ import { renderForm } from "../ui/schema-form";
31
46
  import { resolveContextPointer } from "../services/context-resolver";
32
47
  import type { JsonSchema } from "../ui/schema-form";
33
48
  import type { TabUi } from "../tabs/tab";
@@ -40,22 +55,39 @@ import type {
40
55
  } from "@jxsuite/schema/types";
41
56
  import { fetchPluginSchema, pluginSchemaCache } from "../services/code-services";
42
57
  import { getExtensions, loadExtensions } from "../format/format-host";
58
+ import { optionalStringArg, stringProperty } from "../commands/command-args";
43
59
  import type { TemplateResult } from "lit-html";
60
+ import type { AnyCommand, CommandRegistry } from "../commands/registry";
44
61
 
45
62
  interface SignalsPanelState {
46
63
  document: JxMutableNode;
47
64
  ui?: TabUi | Record<string, unknown>;
48
65
  mode?: string;
49
- selection?: (string | number)[] | null;
50
- canvas?: Record<string, unknown>;
66
+ selection?: (string | number)[][];
67
+ canvas?: Record<string, unknown> | null;
51
68
  _collapsedSignalCats?: Set<string>;
52
69
  documentPath?: string | null | undefined;
53
70
  }
54
71
 
72
+ /**
73
+ * What the State panel's template needs from its host: a repaint, and nothing else.
74
+ *
75
+ * It carried `renderCanvas` and `updateSession` too, and both are gone for the same reason. The
76
+ * canvas hook lost its last caller when the takeovers went and was left threaded from
77
+ * `registerStatePanel`; the session writer lost its two when the Logic buttons started going
78
+ * through `openLogicTarget`, which addresses the focused tab itself. A ctx field with no reader is
79
+ * an invitation to write the wrong thing through it.
80
+ */
55
81
  interface SignalsPanelCtx {
56
82
  renderLeftPanel: () => void;
57
- renderCanvas: () => void;
58
- updateSession: (patch: Record<string, unknown>) => void;
83
+ /**
84
+ * Re-fire automatic `Request` entries and repaint (the Refresh button).
85
+ *
86
+ * Edit and design suppress automatic fetches — a full render re-resolves every state entry, so
87
+ * authoring would refetch constantly — which is why watching a fetched value needs a verb and not
88
+ * just a repaint.
89
+ */
90
+ refreshData?: () => void;
59
91
  }
60
92
 
61
93
  export interface SignalDef {
@@ -90,20 +122,18 @@ export interface SignalDef {
90
122
 
91
123
  // ─── Module-local state ─────────────────────────────────────────────────────
92
124
 
93
- /** Expanded signal editor state (persists across renders). */
94
- let expandedSignal: string | null = null;
125
+ /**
126
+ * The rename that was refused, and why — cleared by the next accepted one.
127
+ *
128
+ * A collision used to be a silent no-op: the field kept the typed name, the document kept the old
129
+ * one, and nothing said which had won. Plan §11.2 asks for "collision-checked rename with a visible
130
+ * error", and half of that had shipped.
131
+ */
132
+ let renameError: { name: string; message: string } | null = null;
95
133
 
96
134
  /** Track which functions have the advanced param editor open. */
97
135
  const advancedParamOpen = new Set();
98
136
 
99
- /** Schema fields whose binding picker is in free-form Custom mode (cleared on commit). */
100
- const bindingCustomOpen = new Set<string>();
101
-
102
- /** Reset binding-control ephemeral UI state (test hook). */
103
- export function resetBindingUiState() {
104
- bindingCustomOpen.clear();
105
- }
106
-
107
137
  /** Default templates for creating new signal definitions. */
108
138
  const DEF_TEMPLATES = {
109
139
  computed: { $compute: "", $deps: [] },
@@ -282,7 +312,7 @@ export function defHint(_name: string, def: SignalDef | null | undefined) {
282
312
  * @param {SignalsPanelState} S
283
313
  */
284
314
  export function isCustomElementDoc(S: SignalsPanelState) {
285
- return (S.document.tagName || "").includes("-");
315
+ return displayTagName(S.document.tagName).includes("-");
286
316
  }
287
317
 
288
318
  /**
@@ -297,7 +327,7 @@ export function collectCssParts(
297
327
  ) {
298
328
  const part = node?.attributes?.part;
299
329
  if (typeof part === "string" && part) {
300
- parts.push({ name: part, tag: node?.tagName || "div" });
330
+ parts.push({ name: part, tag: displayTagName(node?.tagName) || "div" });
301
331
  }
302
332
  if (Array.isArray(node?.children)) {
303
333
  for (const c of node.children) {
@@ -376,12 +406,23 @@ export function resolveDefaultForCanvas(
376
406
 
377
407
  // ─── Simple field row ────────────────────────────────────────────────────────
378
408
 
379
- /** Simple field row for signal editors — vertical stacked layout. */
380
- export function signalFieldRow(label: string, value: string, onChange: (value: string) => void) {
409
+ /**
410
+ * Simple field row for signal editors — vertical stacked layout.
411
+ *
412
+ * `error` paints the row invalid and prints the message in a `role="alert"` line, which is how a
413
+ * refused rename says so instead of silently keeping the old name.
414
+ */
415
+ export function signalFieldRow(
416
+ label: string,
417
+ value: string,
418
+ onChange: (value: string) => void,
419
+ error?: string | undefined,
420
+ ) {
381
421
  return renderFieldRow({
382
422
  prop: label,
383
423
  label,
384
424
  hasValue: false,
425
+ ...(error === undefined ? {} : { error }),
385
426
  // CommitMode "blur": signal fields (rename, src, etc.) commit on blur/Enter only — a debounced
386
427
  // Mid-typing commit would, e.g., rename the signal on every keystroke pause.
387
428
  widget: spTextField(
@@ -415,13 +456,78 @@ function cemTypeText(type: JsonValue | undefined): string {
415
456
 
416
457
  // ─── Left panel: Signals ─────────────────────────────────────────────────────
417
458
 
459
+ /**
460
+ * Add a def from one of the built-in templates under a free name, and expand it for editing. Shared
461
+ * by the "+ Add…" picker and the panel's empty state, so both create the same thing.
462
+ */
463
+ function addTemplateDef(type: string, S: SignalsPanelState, ctx: SignalsPanelCtx) {
464
+ const template = DEF_TEMPLATES[type];
465
+ if (!template) {
466
+ return;
467
+ }
468
+ const nameBase = type === "function" ? "newFunction" : "$newSignal";
469
+ let n = nameBase;
470
+ let i = 1;
471
+ while (S.document.state && S.document.state[n]) {
472
+ n = nameBase + i;
473
+ i += 1;
474
+ }
475
+ transactDoc(activeTab.value, (t) =>
476
+ mutateAddDef(t, n, structuredClone(template) as Record<string, JsonValue>),
477
+ );
478
+ setDataRowExpanded(n, true);
479
+ ctx.renderLeftPanel();
480
+ }
481
+
418
482
  /**
419
483
  * @param {SignalsPanelState} S
420
484
  * @param {SignalsPanelCtx} ctx
421
485
  */
486
+ /**
487
+ * ONE summary slot per row, and the resolved value wins it as soon as there is one.
488
+ *
489
+ * The row wants to say four things about an entry — its category, its name, how it is defined and
490
+ * what it became — and a 240px Navigator fits three. Eliding both summaries to make room produced
491
+ * "recentproje… C… Array(3)": two truncated descriptions and a truncated identity. So the
492
+ * definition hint holds the slot until the canvas resolves a value and then steps aside for it, and
493
+ * the full definition is one click down in fields — which is what the hint was abbreviating.
494
+ *
495
+ * The switch is whether the canvas has reported a scope AT ALL, not whether this entry appears in
496
+ * it. An entry the canvas ran and did not produce is "pending", which is a fact about the value; a
497
+ * panel opened before the canvas has rendered knows nothing about any of them, and guessing
498
+ * "pending" for the whole list there would be a fact about the panel dressed up as one about data.
499
+ *
500
+ * ENTRIES THAT CANNOT HOLD A VALUE never get the column. A function and an assignment expression
501
+ * are things the page DOES, not things it knows, and they are absent from the resolved scope for
502
+ * exactly that reason — so the value column called all eight of a component's `setFilter` handlers
503
+ * "pending", which reads as "still loading" for something that will never load.
504
+ */
505
+ function summary(name: string, def: SignalDef, live: unknown, resolved: boolean) {
506
+ const holdsNoValue =
507
+ defCategory(def) === "function" ||
508
+ (def.$expression != null && isActionExpression(def.$expression));
509
+ if (!resolved || holdsNoValue) {
510
+ const hint = defHint(name, def);
511
+ return html`<span class="signal-hint" title=${hint}>${hint}</span>`;
512
+ }
513
+ return html`<span
514
+ class=${classMap({ "data-pending": unwrapSignal(live) === null, "data-type": true })}
515
+ title="What this resolved to on the canvas"
516
+ >${dataTypeLabel(live)}</span
517
+ >`;
518
+ }
519
+
422
520
  export function renderSignalsTemplate(S: SignalsPanelState, ctx: SignalsPanelCtx) {
423
521
  const defs = S.document.state || {};
424
522
  const entries = Object.entries(defs);
523
+ // What the canvas actually resolved these to. `S.canvas` is the session's canvas record, so this
524
+ // Costs a property read — the panel already had the scope in hand and rendered it in a second
525
+ // Panel anyway.
526
+ const liveScope = (S.canvas?.scope ?? null) as Record<string, unknown> | null;
527
+ const scope = liveScope ?? {};
528
+ // A Refresh is out and the canvas has not answered. Read off the tab, so it survives the repaints
529
+ // Between the press and the `dataScope` that ends it.
530
+ const refreshing = S.canvas?.refreshing === true;
425
531
 
426
532
  // Warm the extensions payload so manifest state classes appear in the add picker (the panel
427
533
  // Re-renders constantly; loadExtensions memoizes, so this is a one-time fetch per project).
@@ -447,7 +553,8 @@ export function renderSignalsTemplate(S: SignalsPanelState, ctx: SignalsPanelCtx
447
553
  { items: groups.function!, key: "function", label: "Functions" },
448
554
  ];
449
555
 
450
- const collapsedCats = (S._collapsedSignalCats ||= new Set());
556
+ S._collapsedSignalCats ||= new Set();
557
+ const collapsedCats = S._collapsedSignalCats;
451
558
 
452
559
  const catTemplates = categories
453
560
  .filter((c) => c.items.length > 0)
@@ -466,18 +573,19 @@ export function renderSignalsTemplate(S: SignalsPanelState, ctx: SignalsPanelCtx
466
573
  }}
467
574
  >
468
575
  ${items.map(([name, def]) => {
469
- const isExpanded: boolean = expandedSignal === name;
576
+ const isExpanded: boolean = isDataRowExpanded(name);
577
+ const live = scope[name];
470
578
  return html`
471
579
  <div
472
580
  class=${classMap({ expanded: isExpanded, "signal-row": true })}
473
581
  @click=${() => {
474
- expandedSignal = isExpanded ? null : name;
582
+ setDataRowExpanded(name, !isExpanded);
475
583
  ctx.renderLeftPanel();
476
584
  }}
477
585
  >
478
586
  <span class="signal-badge ${defCategory(def)}">${defBadgeLabel(def)}</span>
479
587
  <span class="signal-name">${name}</span>
480
- <span class="signal-hint">${defHint(name, def)}</span>
588
+ ${summary(name, def, live, liveScope !== null)}
481
589
  <sp-action-button
482
590
  quiet
483
591
  size="xs"
@@ -494,6 +602,12 @@ export function renderSignalsTemplate(S: SignalsPanelState, ctx: SignalsPanelCtx
494
602
  isExpanded
495
603
  ? html`<div class="signal-editor">
496
604
  ${renderSignalEditorTemplate(S, name, def, ctx)}
605
+ <div class="signal-live">
606
+ <span class="signal-live-label">Resolved to</span>
607
+ <div class="data-tree">
608
+ ${renderDataTreeTemplate(unwrapSignal(live), 0, 5, name)}
609
+ </div>
610
+ </div>
497
611
  </div>`
498
612
  : nothing
499
613
  }
@@ -504,9 +618,46 @@ export function renderSignalsTemplate(S: SignalsPanelState, ctx: SignalsPanelCtx
504
618
  );
505
619
 
506
620
  return html`
507
- <div class="signals-panel">
621
+ <div class=${classMap({ "is-refreshing": refreshing, "signals-panel": true })}>
622
+ ${
623
+ ctx.refreshData && entries.length > 0
624
+ ? html`<div class="data-explorer-toolbar">
625
+ <sp-action-button
626
+ quiet
627
+ size="s"
628
+ class="data-refresh-btn"
629
+ ?disabled=${refreshing}
630
+ @click=${() => {
631
+ ctx.refreshData?.();
632
+ ctx.renderLeftPanel();
633
+ }}
634
+ >
635
+ ${
636
+ refreshing
637
+ ? html`<sp-progress-circle
638
+ slot="icon"
639
+ indeterminate
640
+ size="s"
641
+ label="Refreshing"
642
+ ></sp-progress-circle>`
643
+ : html`<sp-icon-refresh slot="icon"></sp-icon-refresh>`
644
+ }
645
+ ${refreshing ? "Refreshing…" : "Refresh"}
646
+ </sp-action-button>
647
+ </div>`
648
+ : nothing
649
+ }
508
650
  <sp-accordion allow-multiple size="s"> ${catTemplates} </sp-accordion>
509
- ${entries.length === 0 ? html`<div class="empty-state">No state defined</div>` : nothing}
651
+ ${
652
+ entries.length === 0
653
+ ? renderEmptyState({
654
+ actions: [{ label: "Add a value", run: () => addTemplateDef("state", S, ctx) }],
655
+ message:
656
+ "Data lives here — values this page can read, compute or fetch, " +
657
+ "ready to bind to any element.",
658
+ })
659
+ : nothing
660
+ }
510
661
  <div class="signals-add">
511
662
  <sp-picker
512
663
  size="s"
@@ -536,7 +687,7 @@ export function renderSignalsTemplate(S: SignalsPanelState, ctx: SignalsPanelCtx
536
687
  ...cls?.stateDefaults,
537
688
  } as Record<string, JsonValue>),
538
689
  );
539
- expandedSignal = n;
690
+ setDataRowExpanded(n, true);
540
691
  ctx.renderLeftPanel();
541
692
  return;
542
693
  }
@@ -561,7 +712,7 @@ export function renderSignalsTemplate(S: SignalsPanelState, ctx: SignalsPanelCtx
561
712
  },
562
713
  ),
563
714
  );
564
- expandedSignal = n;
715
+ setDataRowExpanded(n, true);
565
716
  if (src) {
566
717
  void fetchPluginSchema(
567
718
  { $prototype: protoName, $src: src },
@@ -577,29 +728,13 @@ export function renderSignalsTemplate(S: SignalsPanelState, ctx: SignalsPanelCtx
577
728
  return;
578
729
  }
579
730
 
580
- const template = DEF_TEMPLATES[type];
581
- if (!template) {
582
- return;
583
- }
584
- const isFunction = type === "function";
585
- const nameBase = isFunction ? "newFunction" : "$newSignal";
586
- let n = nameBase;
587
- let i = 1;
588
- while (S.document.state && S.document.state[n]) {
589
- n = nameBase + i;
590
- i += 1;
591
- }
592
- transactDoc(activeTab.value, (t) =>
593
- mutateAddDef(t, n, structuredClone(template) as Record<string, JsonValue>),
594
- );
595
- expandedSignal = n;
596
- ctx.renderLeftPanel();
731
+ addTemplateDef(type, S, ctx);
597
732
  }}
598
733
  >
599
- <sp-menu-item value="state">State Signal</sp-menu-item>
734
+ <sp-menu-item value="state">Value</sp-menu-item>
600
735
  <sp-menu-item value="computed">Computed</sp-menu-item>
601
736
  <sp-menu-divider></sp-menu-divider>
602
- <sp-menu-item value="request">Fetch (Request)</sp-menu-item>
737
+ <sp-menu-item value="request">Fetch from a URL</sp-menu-item>
603
738
  <sp-menu-item value="localStorage">LocalStorage</sp-menu-item>
604
739
  <sp-menu-item value="sessionStorage">SessionStorage</sp-menu-item>
605
740
  <sp-menu-item value="indexedDB">IndexedDB</sp-menu-item>
@@ -607,7 +742,7 @@ export function renderSignalsTemplate(S: SignalsPanelState, ctx: SignalsPanelCtx
607
742
  <sp-menu-item value="set">Set</sp-menu-item>
608
743
  <sp-menu-item value="map">Map</sp-menu-item>
609
744
  <sp-menu-item value="formData">FormData</sp-menu-item>
610
- <sp-menu-item value="external">External Module…</sp-menu-item>
745
+ <sp-menu-item value="external">From a module…</sp-menu-item>
611
746
  ${
612
747
  projectState?.projectConfig?.imports
613
748
  ? html`<sp-menu-divider></sp-menu-divider>${Object.keys(
@@ -684,13 +819,39 @@ function renderSignalEditorTemplate(
684
819
  }),
685
820
  });
686
821
 
687
- // Name field (common to all)
688
- const nameField = signalFieldRow("Name", name, (v: string) => {
689
- if (v && v !== name && !(S.document.state && S.document.state[v])) {
690
- expandedSignal = v;
691
- transactDoc(activeTab.value, (t) => mutateRenameDef(t, name, v));
692
- }
693
- });
822
+ /*
823
+ * Name field (common to all).
824
+ *
825
+ * Every refusal SAYS SO. A collision silently kept the old name while the field showed the new
826
+ * one, so the panel and the document disagreed and only the canvas could tell you which had won —
827
+ * and an empty name did the same. The expansion follows the rename so the editor you are typing
828
+ * in is still the one on screen afterwards.
829
+ */
830
+ const nameField = signalFieldRow(
831
+ "Name",
832
+ name,
833
+ (v: string) => {
834
+ const next = v.trim();
835
+ if (next === name) {
836
+ renameError = null;
837
+ return;
838
+ }
839
+ if (!next) {
840
+ renameError = { message: "A name is required.", name };
841
+ } else if (S.document.state?.[next]) {
842
+ renameError = { message: `"${next}" is already defined by this document.`, name };
843
+ } else {
844
+ renameError = null;
845
+ // The expansion follows the rename, so the editor you are typing in is still the one on
846
+ // Screen when the row list repaints under the new name.
847
+ setDataRowExpanded(name, false);
848
+ setDataRowExpanded(next, true);
849
+ transactDoc(activeTab.value, (t) => mutateRenameDef(t, name, next));
850
+ }
851
+ ctx.renderLeftPanel();
852
+ },
853
+ renameError?.name === name ? renameError.message : undefined,
854
+ );
694
855
 
695
856
  let fields: TemplateResult | typeof nothing = nothing;
696
857
 
@@ -847,13 +1008,14 @@ function renderSignalEditorTemplate(
847
1008
  quiet
848
1009
  title="Open in formula workspace"
849
1010
  @click=${() => {
850
- ctx.updateSession({
851
- ui: { editingFormula: { defName: name, type: "def" } },
852
- });
853
- ctx.renderCanvas();
1011
+ // One call, because a click is BOTH events: it names the target and it asks for the
1012
+ // Surface. Setting the field alone leaned on the dock's reveal effect, which fires at
1013
+ // Most once per target — so clicking this again after closing the dock was a dead
1014
+ // Click. It also left `editingFunction` set, and that one wins the tie.
1015
+ openLogicTarget({ editing: { defName: name, type: "def" }, surface: "formula" });
854
1016
  }}
855
1017
  >
856
- <sp-icon-full-screen slot="icon"></sp-icon-full-screen>
1018
+ <sp-icon-align-bottom slot="icon"></sp-icon-align-bottom>
857
1019
  </sp-action-button>
858
1020
  </div>
859
1021
  ${renderExpressionEditor(
@@ -1064,10 +1226,11 @@ function renderFunctionFields(
1064
1226
  quiet
1065
1227
  title="Open in code editor"
1066
1228
  @click=${() => {
1067
- ctx.updateSession({
1068
- ui: { editingFunction: { defName: name, type: "def" } },
1229
+ // Target AND reveal, in one call. See the formula button above.
1230
+ openLogicTarget({
1231
+ editing: { defName: name, type: "def" },
1232
+ surface: "function",
1069
1233
  });
1070
- ctx.renderCanvas();
1071
1234
  }}
1072
1235
  >
1073
1236
  <sp-icon-code slot="icon"></sp-icon-code>
@@ -1088,6 +1251,8 @@ function renderFunctionFields(
1088
1251
  {
1089
1252
  allowEventRef: true,
1090
1253
  emits: def.emits ?? [],
1254
+ // This editor is in the Navigator's State panel; the Events tab's is not.
1255
+ region: NAVIGATOR_STATEMENTS_REGION,
1091
1256
  stateDefs: Object.keys(S.document.state || {}),
1092
1257
  stateEntries: S.document.state || {},
1093
1258
  },
@@ -1400,77 +1565,6 @@ function resolveSignalsContextPointer(pointer: string, scope?: Record<string, un
1400
1565
  });
1401
1566
  }
1402
1567
 
1403
- /**
1404
- * Render a binding picker for a `{ $ref }` config value — route params derived from the document
1405
- * path plus a free-form custom ref, with a switch back to a static value.
1406
- */
1407
- function renderBindingControl(opts: {
1408
- refVal: string;
1409
- params: string[];
1410
- fieldKey: string;
1411
- commit: (next: { $ref: string } | undefined) => void;
1412
- rerender: (() => void) | undefined;
1413
- }) {
1414
- const paramRefs = opts.params.map((p) => `#/$params/${p}`);
1415
- const isCustom =
1416
- bindingCustomOpen.has(opts.fieldKey) ||
1417
- (opts.refVal !== "" && !paramRefs.includes(opts.refVal));
1418
- return html`
1419
- <div style="display:flex;flex-direction:column;gap:4px">
1420
- <sp-picker
1421
- size="s"
1422
- .value=${live(isCustom ? "__custom__" : opts.refVal || "__static__")}
1423
- @change=${(e: Event) => {
1424
- const v = (e.target as HTMLInputElement).value;
1425
- if (v === "__custom__") {
1426
- bindingCustomOpen.add(opts.fieldKey);
1427
- opts.rerender?.();
1428
- return;
1429
- }
1430
- bindingCustomOpen.delete(opts.fieldKey);
1431
- opts.commit(v === "__static__" ? undefined : { $ref: v });
1432
- opts.rerender?.();
1433
- }}
1434
- >
1435
- <sp-menu-item value="__static__">Static value</sp-menu-item>
1436
- ${paramRefs.length > 0 ? html`<sp-menu-divider></sp-menu-divider>` : nothing}
1437
- ${paramRefs.map((r) => html`<sp-menu-item value=${r}>${r.slice(2)}</sp-menu-item>`)}
1438
- <sp-menu-divider></sp-menu-divider>
1439
- <sp-menu-item value="__custom__">Custom…</sp-menu-item>
1440
- </sp-picker>
1441
- ${
1442
- isCustom
1443
- ? html`<sp-textfield
1444
- size="s"
1445
- placeholder="#/$params/…"
1446
- .value=${live(opts.refVal)}
1447
- @change=${(e: Event) => {
1448
- const v = (e.target as HTMLInputElement).value.trim();
1449
- if (!v) {
1450
- bindingCustomOpen.delete(opts.fieldKey);
1451
- }
1452
- opts.commit(v ? { $ref: v } : undefined);
1453
- opts.rerender?.();
1454
- }}
1455
- ></sp-textfield>`
1456
- : nothing
1457
- }
1458
- </div>
1459
- `;
1460
- }
1461
-
1462
- // The "binding" control registers here rather than in ui/form-controls.ts because it owns
1463
- // Panel-local ephemeral UI state (bindingCustomOpen) and the route-param picker semantics.
1464
- registerFormControl("binding", ({ key, value, onChange, ctx, rerender }) =>
1465
- renderBindingControl({
1466
- commit: onChange,
1467
- fieldKey: `${ctx.fieldKeyPrefix ?? ""}.${key}`,
1468
- params: ctx.params ?? [],
1469
- refVal: isRef(value) ? value.$ref : "",
1470
- rerender,
1471
- }),
1472
- );
1473
-
1474
1568
  /**
1475
1569
  * Render config form fields from a JSON Schema `properties` object — a thin wrapper over the shared
1476
1570
  * schema-form engine. Skips studio-reserved keys, resolves enum/context refs against the project
@@ -1496,6 +1590,8 @@ export function renderSchemaFieldsTemplate(
1496
1590
  fieldKeyPrefix: name,
1497
1591
  params: dynamicRouteParams(S.documentPath),
1498
1592
  resolvePointer: resolveSignalsContextPointer,
1593
+ // A config value may point at any signal but this one — a def that reads itself is a cycle.
1594
+ signals: bindableSignalNames(S.document).filter((signal) => signal !== name),
1499
1595
  },
1500
1596
  onChange: (patch) => transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, patch)),
1501
1597
  ...(ctx && { rerender: () => ctx.renderLeftPanel() }),
@@ -1565,7 +1661,7 @@ export function renderExternalPrototypeEditorTemplate(
1565
1661
  );
1566
1662
  pluginSchemaCache.delete(`${v}::${def.$prototype}`);
1567
1663
  })}
1568
- ${signalFieldRow("Prototype", def.$prototype || "", (v: string) => {
1664
+ ${signalFieldRow("Kind", def.$prototype || "", (v: string) => {
1569
1665
  transactDoc(activeTab.value, (t) =>
1570
1666
  mutateUpdateDef(t, name, { $prototype: v || undefined }),
1571
1667
  );
@@ -1585,3 +1681,124 @@ export function renderExternalPrototypeEditorTemplate(
1585
1681
  ${schemaContent}
1586
1682
  `;
1587
1683
  }
1684
+
1685
+ // ─── Commands ─────────────────────────────────────────────────────────────────
1686
+
1687
+ /* THE ROW VERB IS `data.expandRow` — one gesture, one command.
1688
+ `state.selectSignal` opened exactly one editor and `data.expandRow` opened any number of value
1689
+ trees, on the same rows, from two panels. Merging the panels merged the verbs, and the survivor
1690
+ is the one that names the state it ends in (`{expanded: false}` collapses, running it twice
1691
+ photographs the same picture) rather than the one whose only argument was a new selection. */
1692
+
1693
+ /** The state entries the open document defines. */
1694
+ function definedSignalNames(): string[] {
1695
+ return Object.keys(activeTab.value?.doc.document?.state ?? {});
1696
+ }
1697
+
1698
+ /* THE VERB NEEDS NOTHING FROM ITS HOST ANY MORE.
1699
+ `SignalsCommandDeps` carried `renderLeftPanel` for `state.selectSignal`, which is gone; the one
1700
+ verb left reveals its own surface through `openLogicTarget`. A dep with no reader is an
1701
+ invitation to write the wrong thing through it, which is why the parameter went with it. */
1702
+
1703
+ /**
1704
+ * Open a state entry's formula in the Bottom dock.
1705
+ *
1706
+ * It used to be an XPath press matching the row's RENDERED NAME, which plan §13's R1 forbids
1707
+ * outright: a panel that starts eliding long names, or grouping differently, breaks a shot by
1708
+ * improving the app. The document defines these names, so the document is what validates them.
1709
+ *
1710
+ * It defaults its target to the one open Data row — the button it replaces is rendered inside that
1711
+ * entry's own editor, so "the one that is open" is what a reader means, and with several open it
1712
+ * asks rather than guesses. It REFUSES a signal with no `$expression`: the workspace edits an
1713
+ * expression tree, and opening it over a plain state entry used to paint an empty canvas takeover.
1714
+ *
1715
+ * It goes through `openLogicTarget` rather than the canvas repaint it used to fire, for the same
1716
+ * reason the panel BUTTONS do. A command is the palette's and the automation's door:
1717
+ * `formula.editDef` and `formula.editEvent` both leave the surface on screen when they return, and
1718
+ * a verb that merely arms a dock the user has collapsed would report success while showing nothing.
1719
+ * The reveal is idempotent.
1720
+ *
1721
+ * @returns {AnyCommand[]}
1722
+ */
1723
+ export function signalsCommands(): AnyCommand[] {
1724
+ /** The named entry, or a refusal listing what the document does define. */
1725
+ function requireDef(commandId: string, name: string): SignalDef {
1726
+ const defs = (activeTab.value?.doc.document?.state ?? {}) as Record<string, SignalDef>;
1727
+ const def = defs[name];
1728
+ if (!def) {
1729
+ const defined = definedSignalNames();
1730
+ throw new RangeError(
1731
+ `command "${commandId}" argument "name": "${name}" is not a state entry this document ` +
1732
+ `defines — it defines: ${defined.length > 0 ? defined.join(", ") : "nothing"}`,
1733
+ );
1734
+ }
1735
+ return def;
1736
+ }
1737
+
1738
+ return [
1739
+ {
1740
+ args: {
1741
+ additionalProperties: false,
1742
+ properties: {
1743
+ defName: stringProperty(
1744
+ "The state entry whose $expression to edit. Defaults to the selected one.",
1745
+ ),
1746
+ },
1747
+ required: [],
1748
+ type: "object",
1749
+ },
1750
+ category: "Document",
1751
+ id: "formula.openWorkspace",
1752
+ level: "document",
1753
+ menus: ["palette"],
1754
+ group: "5_data",
1755
+ requires: "a selected state entry that holds a formula",
1756
+ when: (ctx) => ctx.document.open,
1757
+ run: (_commandCtx, args) => {
1758
+ const named = optionalStringArg("formula.openWorkspace", args, "defName");
1759
+ // With no argument the target is the one open row — and only if there is exactly one.
1760
+ // Multiple rows open is now the normal state of this panel, so "the selected one" has to
1761
+ // Refuse an ambiguous answer rather than pick the first key it happens to enumerate.
1762
+ const open = expandedDataRows();
1763
+ const defName = named ?? (open.length === 1 ? open[0]! : null);
1764
+ if (defName === null) {
1765
+ throw new RangeError(
1766
+ open.length > 1
1767
+ ? `command "formula.openWorkspace" needs a target: ${open.length} Data rows are ` +
1768
+ `open (${open.join(", ")}), so pass "defName"`
1769
+ : `command "formula.openWorkspace" needs a target: pass "defName", or open a state ` +
1770
+ `entry's row first with data.expandRow`,
1771
+ );
1772
+ }
1773
+ const def = requireDef("formula.openWorkspace", defName);
1774
+ if (!def.$expression) {
1775
+ throw new RangeError(
1776
+ `command "formula.openWorkspace" argument "defName": "${defName}" holds no ` +
1777
+ `$expression — the workspace edits formulas, and this entry is not one`,
1778
+ );
1779
+ }
1780
+ // The workspace reads its target off the tab; the Bottom dock's Logic tab is where it
1781
+ // Draws. Nothing on the canvas depends on this field any more.
1782
+ openLogicTarget({ editing: { defName, type: "def" }, surface: "formula" });
1783
+ },
1784
+ title: "Open Formula Workspace",
1785
+ },
1786
+ ];
1787
+ }
1788
+
1789
+ /**
1790
+ * Register the State panel's verbs.
1791
+ *
1792
+ * @param {CommandRegistry} registry
1793
+ */
1794
+ export function registerSignalsCommands(registry: CommandRegistry): void {
1795
+ registry.registerAll(signalsCommands());
1796
+ }
1797
+
1798
+ /* THE STATE PANEL IS GONE, AND ITS EDITOR IS IN DATA — `panels/data-explorer.ts`.
1799
+ It was registered here `rail: false`, waiting for plan §11.2's merge into Data, and the merge did
1800
+ not follow: the button was removed and the editor was left reachable only by typing "State" into
1801
+ the palette. Declaring a state variable — or a component property, which is a state entry with a
1802
+ default — is not an advanced move to hide behind a search box, so `renderSignalsTemplate` is now
1803
+ rendered by the Data panel and there is no second record. A stored `leftTab: "state"` migrates to
1804
+ `data` in `shell.ts`. */