@jxsuite/studio 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (419) hide show
  1. package/canvas.html +21 -0
  2. package/dist/chunks/Overlay.dev-qfjzkms8.js +23 -0
  3. package/dist/chunks/{Overlay.dev-cszx7yn5.js.map → Overlay.dev-qfjzkms8.js.map} +1 -1
  4. package/dist/chunks/{adapter-z8fkmbmv.js → adapter-qfnesn5v.js} +2 -2
  5. package/dist/chunks/{cf-settings-mmyv436d.js → cf-settings-73sh5z4p.js} +3 -3
  6. package/dist/chunks/{components-0hzentwq.js → components-2bpqp4mq.js} +5 -7
  7. package/dist/chunks/{components-0hzentwq.js.map → components-2bpqp4mq.js.map} +1 -1
  8. package/dist/chunks/content-source-34gnkbnn.js +38 -0
  9. package/dist/chunks/content-source-34gnkbnn.js.map +9 -0
  10. package/dist/chunks/convert-to-repeater-t8kxre5s.js +258 -0
  11. package/dist/chunks/convert-to-repeater-t8kxre5s.js.map +10 -0
  12. package/dist/chunks/extension-sections-6mqydz35.js +301 -0
  13. package/dist/chunks/extension-sections-6mqydz35.js.map +11 -0
  14. package/dist/chunks/files-8kn321h1.js +70 -0
  15. package/dist/chunks/files-8kn321h1.js.map +9 -0
  16. package/dist/chunks/{format-host-2c188n41.js → format-host-mkwsmt8d.js} +3 -3
  17. package/dist/chunks/git-panel-tjbr191d.js +51 -0
  18. package/dist/chunks/git-panel-tjbr191d.js.map +9 -0
  19. package/dist/chunks/{index-baeg2x3y.js → index-7pxchcce.js} +4 -3
  20. package/dist/chunks/{index-baeg2x3y.js.map → index-7pxchcce.js.map} +3 -3
  21. package/dist/chunks/{jx-validate-qx8degj7.js → jx-validate-v6qgknrs.js} +3 -3
  22. package/dist/chunks/layers-panel-87pb25km.js +58 -0
  23. package/dist/chunks/layers-panel-87pb25km.js.map +9 -0
  24. package/dist/chunks/{monaco-setup-zjdp9zdw.js → monaco-setup-e0yh25j4.js} +4 -75
  25. package/dist/chunks/{monaco-setup-zjdp9zdw.js.map → monaco-setup-e0yh25j4.js.map} +3 -4
  26. package/dist/chunks/publish-panel-m0fcgdrt.js +21 -0
  27. package/dist/chunks/publish-panel-m0fcgdrt.js.map +9 -0
  28. package/dist/chunks/quick-search-rhs6cbsb.js +53 -0
  29. package/dist/chunks/quick-search-rhs6cbsb.js.map +9 -0
  30. package/dist/chunks/right-panel-zpdxd98t.js +37 -0
  31. package/dist/chunks/right-panel-zpdxd98t.js.map +9 -0
  32. package/dist/chunks/section-registry-zmckaxf4.js +30 -0
  33. package/dist/chunks/section-registry-zmckaxf4.js.map +9 -0
  34. package/dist/chunks/sp-overlay.dev-9ran0sfq.js +18 -0
  35. package/dist/chunks/sp-overlay.dev-9ran0sfq.js.map +9 -0
  36. package/dist/chunks/sp-popover.dev-rebmdncr.js +9 -0
  37. package/dist/chunks/sp-popover.dev-rebmdncr.js.map +9 -0
  38. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js +15 -0
  39. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js.map +9 -0
  40. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js +15 -0
  41. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js.map +9 -0
  42. package/dist/chunks/{sp-tray.dev-pb2fsd65.js → sp-tray.dev-ey59vfh7.js} +10 -8
  43. package/dist/chunks/{sp-tray.dev-pb2fsd65.js.map → sp-tray.dev-ey59vfh7.js.map} +2 -2
  44. package/dist/chunks/{studio-9qr6fq7p.js → studio-0rqqhrdy.js} +10 -378
  45. package/dist/chunks/{studio-9qr6fq7p.js.map → studio-0rqqhrdy.js.map} +4 -14
  46. package/dist/chunks/{studio-5x6tqqec.js → studio-2sj1jj3v.js} +10 -8
  47. package/dist/chunks/{studio-5x6tqqec.js.map → studio-2sj1jj3v.js.map} +2 -2
  48. package/dist/chunks/{studio-h3fw0wn8.js → studio-3fk1ac9z.js} +6 -4
  49. package/dist/chunks/{studio-h3fw0wn8.js.map → studio-3fk1ac9z.js.map} +2 -2
  50. package/dist/chunks/studio-41exgzhq.js +6690 -0
  51. package/dist/chunks/studio-41exgzhq.js.map +38 -0
  52. package/dist/chunks/{studio-7v2bpweq.js → studio-4mqb9a0j.js} +6 -4
  53. package/dist/chunks/{studio-7v2bpweq.js.map → studio-4mqb9a0j.js.map} +2 -2
  54. package/dist/chunks/studio-50k77cca.js +1787 -0
  55. package/dist/chunks/studio-50k77cca.js.map +18 -0
  56. package/dist/chunks/{studio-56v3zwk6.js → studio-6a0h7sne.js} +10 -8
  57. package/dist/chunks/{studio-56v3zwk6.js.map → studio-6a0h7sne.js.map} +2 -2
  58. package/dist/chunks/studio-6f0am8f2.js +53 -0
  59. package/dist/chunks/studio-6f0am8f2.js.map +10 -0
  60. package/dist/chunks/studio-6nmjc5qk.js +7002 -0
  61. package/dist/chunks/studio-6nmjc5qk.js.map +32 -0
  62. package/dist/chunks/studio-7afd21nw.js +67 -0
  63. package/dist/chunks/studio-7afd21nw.js.map +10 -0
  64. package/dist/chunks/{studio-xze4zrs1.js → studio-7bmdvzne.js} +2 -2
  65. package/dist/chunks/studio-7bmdvzne.js.map +10 -0
  66. package/dist/chunks/studio-8jpzqk5s.js +128 -0
  67. package/dist/chunks/studio-8jpzqk5s.js.map +10 -0
  68. package/dist/chunks/studio-94m77tgk.js +1482 -0
  69. package/dist/chunks/studio-94m77tgk.js.map +13 -0
  70. package/dist/chunks/studio-9je6w5sq.js +1000 -0
  71. package/dist/chunks/studio-9je6w5sq.js.map +16 -0
  72. package/dist/chunks/{studio-w7c6kvk7.js → studio-a7pzveq1.js} +2 -2
  73. package/dist/chunks/{studio-97qcgvdn.js → studio-adn0z1k3.js} +14 -8
  74. package/dist/chunks/{studio-97qcgvdn.js.map → studio-adn0z1k3.js.map} +3 -3
  75. package/dist/chunks/{studio-kr88ryr7.js → studio-bsxxx3x2.js} +17 -15
  76. package/dist/chunks/{studio-kr88ryr7.js.map → studio-bsxxx3x2.js.map} +7 -6
  77. package/dist/chunks/studio-bw5dz6km.js +608 -0
  78. package/dist/chunks/studio-bw5dz6km.js.map +21 -0
  79. package/dist/chunks/studio-c6s5getc.js +4232 -0
  80. package/dist/chunks/studio-c6s5getc.js.map +39 -0
  81. package/dist/chunks/studio-cjm7fdjf.js +451 -0
  82. package/dist/chunks/studio-cjm7fdjf.js.map +14 -0
  83. package/dist/chunks/{studio-cx91mnh8.js → studio-ckwz4d3x.js} +53 -53
  84. package/dist/chunks/{studio-cx91mnh8.js.map → studio-ckwz4d3x.js.map} +5 -5
  85. package/dist/chunks/studio-dg7zyt5x.js +28 -0
  86. package/dist/chunks/studio-dg7zyt5x.js.map +10 -0
  87. package/dist/chunks/studio-dzcyvvvz.js +166 -0
  88. package/dist/chunks/studio-dzcyvvvz.js.map +10 -0
  89. package/dist/chunks/{studio-7kqe41sa.js → studio-evkr5d3b.js} +15 -7
  90. package/dist/chunks/{studio-7kqe41sa.js.map → studio-evkr5d3b.js.map} +5 -4
  91. package/dist/chunks/studio-f12h6177.js +411 -0
  92. package/dist/chunks/studio-f12h6177.js.map +13 -0
  93. package/dist/chunks/studio-hbp1s8m9.js +1817 -0
  94. package/dist/chunks/studio-hbp1s8m9.js.map +14 -0
  95. package/dist/chunks/studio-k2sxjph5.js +15295 -0
  96. package/dist/chunks/studio-k2sxjph5.js.map +235 -0
  97. package/dist/chunks/studio-myje51t3.js +385 -0
  98. package/dist/chunks/studio-myje51t3.js.map +12 -0
  99. package/dist/chunks/studio-na6rwzhw.js +353 -0
  100. package/dist/chunks/studio-na6rwzhw.js.map +11 -0
  101. package/dist/chunks/{studio-4k0m4fmp.js → studio-p471gg5n.js} +126 -3
  102. package/dist/chunks/studio-p471gg5n.js.map +14 -0
  103. package/dist/chunks/{studio-9bjwf0d4.js → studio-rgwcpast.js} +5 -46
  104. package/dist/chunks/{studio-9bjwf0d4.js.map → studio-rgwcpast.js.map} +4 -5
  105. package/dist/chunks/{studio-hhj92gcr.js → studio-s519zc99.js} +3 -3
  106. package/dist/chunks/studio-sas7mt3k.js +75 -0
  107. package/dist/chunks/studio-sas7mt3k.js.map +10 -0
  108. package/dist/chunks/studio-t5wqwzdh.js +74 -0
  109. package/dist/chunks/studio-t5wqwzdh.js.map +10 -0
  110. package/dist/chunks/{studio-9qpspwfn.js → studio-temv5bw4.js} +5 -5
  111. package/dist/chunks/{studio-9qpspwfn.js.map → studio-temv5bw4.js.map} +1 -1
  112. package/dist/chunks/{studio-qkvt37c0.js → studio-tr20m8g8.js} +669 -18
  113. package/dist/chunks/studio-tr20m8g8.js.map +9 -0
  114. package/dist/chunks/{studio-ygzav7z5.js → studio-v96qfnww.js} +5514 -4291
  115. package/dist/chunks/studio-v96qfnww.js.map +54 -0
  116. package/dist/chunks/studio-vcetc91k.js +659 -0
  117. package/dist/chunks/studio-vcetc91k.js.map +10 -0
  118. package/dist/chunks/{studio-n9zmpxjg.js → studio-vmymjefp.js} +2972 -4937
  119. package/dist/chunks/studio-vmymjefp.js.map +98 -0
  120. package/dist/chunks/{studio-bghqfmea.js → studio-wwseqyw0.js} +2 -2
  121. package/dist/chunks/studio-x9hmy5kr.js +390 -0
  122. package/dist/chunks/studio-x9hmy5kr.js.map +10 -0
  123. package/dist/chunks/studio-xa0ce0n7.js +335 -0
  124. package/dist/chunks/studio-xa0ce0n7.js.map +10 -0
  125. package/dist/chunks/studio-xm3dbxvn.js +378 -0
  126. package/dist/chunks/studio-xm3dbxvn.js.map +19 -0
  127. package/dist/chunks/{studio-nqw433kv.js → studio-xmvbhg31.js} +2 -2
  128. package/dist/chunks/studio-zv37yrfa.js +1054 -0
  129. package/dist/chunks/studio-zv37yrfa.js.map +13 -0
  130. package/dist/chunks/{ws-client-q7hkz3j9.js → ws-client-654ts66j.js} +6 -4
  131. package/dist/chunks/ws-client-654ts66j.js.map +11 -0
  132. package/dist/iframe-entry.js +668 -230
  133. package/dist/studio.js +23711 -35185
  134. package/index.html +35 -4685
  135. package/package.json +17 -15
  136. package/src/about/about-modal.ts +42 -11
  137. package/src/browse/library-commands.ts +240 -0
  138. package/src/browse/library-layouts.ts +369 -0
  139. package/src/browse/library-model.ts +396 -0
  140. package/src/browse/library-pane.ts +1049 -0
  141. package/src/browse/library-preview.ts +252 -0
  142. package/src/browse/library-source.ts +150 -0
  143. package/src/canvas/canvas-helpers.ts +33 -9
  144. package/src/canvas/canvas-live-render.ts +45 -29
  145. package/src/canvas/canvas-patcher.ts +151 -31
  146. package/src/canvas/canvas-perf.ts +46 -7
  147. package/src/canvas/canvas-render.ts +1092 -220
  148. package/src/canvas/canvas-surface.ts +240 -0
  149. package/src/canvas/canvas-utils.ts +1166 -124
  150. package/src/canvas/iframe-drop.ts +2 -1
  151. package/src/canvas/iframe-entry.ts +241 -4
  152. package/src/canvas/iframe-host.ts +1226 -143
  153. package/src/canvas/iframe-inline-edit.ts +31 -0
  154. package/src/canvas/iframe-interaction.ts +76 -16
  155. package/src/canvas/iframe-keys.ts +107 -45
  156. package/src/canvas/iframe-overlay.ts +38 -1
  157. package/src/canvas/iframe-protocol.ts +137 -2
  158. package/src/canvas/iframe-render.ts +70 -2
  159. package/src/canvas/iframe-slash.ts +6 -0
  160. package/src/canvas/path-mapping.ts +56 -6
  161. package/src/canvas/surface-registry.ts +292 -0
  162. package/src/collab/collab-commands.ts +244 -0
  163. package/src/collab/collab-session.ts +198 -32
  164. package/src/collab/collab-state.ts +28 -2
  165. package/src/collab/presence-chips.ts +100 -9
  166. package/src/commands/active-registry.ts +42 -0
  167. package/src/commands/app-commands.ts +176 -0
  168. package/src/commands/budget.ts +158 -0
  169. package/src/commands/command-args.ts +253 -0
  170. package/src/commands/context.ts +387 -0
  171. package/src/commands/defaults.ts +580 -0
  172. package/src/commands/keymap.ts +571 -0
  173. package/src/commands/levels.ts +345 -0
  174. package/src/commands/live-context.ts +193 -0
  175. package/src/commands/reference.ts +227 -0
  176. package/src/commands/registry.ts +293 -0
  177. package/src/component-props.ts +3 -2
  178. package/src/content/draft-state.ts +106 -0
  179. package/src/content/entry-commands.ts +282 -0
  180. package/src/content/entry-editor.ts +327 -0
  181. package/src/content/entry-fields.ts +116 -0
  182. package/src/content/entry-model.ts +175 -0
  183. package/src/editor/canvas-context-menu.ts +17 -11
  184. package/src/editor/canvas-slash-bridge.ts +11 -4
  185. package/src/editor/context-menu.ts +804 -204
  186. package/src/editor/convert-to-component.ts +31 -10
  187. package/src/editor/convert-to-repeater.ts +3 -2
  188. package/src/editor/inline-edit-apply.ts +5 -5
  189. package/src/editor/inline-edit.ts +86 -50
  190. package/src/editor/insert-zone-action.ts +1 -1
  191. package/src/editor/shortcuts.ts +1043 -329
  192. package/src/editor/slash-menu.ts +1 -0
  193. package/src/files/elements.ts +166 -0
  194. package/src/files/file-ops.ts +178 -31
  195. package/src/files/files.ts +898 -268
  196. package/src/files/fs-events.ts +21 -0
  197. package/src/files/media-meta.ts +203 -0
  198. package/src/files/media-paths.ts +141 -0
  199. package/src/files/media-upload.ts +7 -3
  200. package/src/files/media-usage.ts +189 -0
  201. package/src/github/github-auth.ts +235 -33
  202. package/src/github/github-publish.ts +80 -21
  203. package/src/grid/grid-controller.ts +152 -12
  204. package/src/grid/grid-layout.ts +225 -20
  205. package/src/grid/grid-open.ts +120 -0
  206. package/src/grid/grid-panel.ts +554 -39
  207. package/src/grid/grid-source.ts +27 -1
  208. package/src/grid/redirects-grid.ts +599 -0
  209. package/src/grid/redirects.ts +398 -0
  210. package/src/grid/sources/content-source.ts +108 -2
  211. package/src/i18n/i18n-commands.ts +322 -0
  212. package/src/new-project/add-repo-modal.ts +8 -10
  213. package/src/new-project/import-tab.ts +34 -9
  214. package/src/new-project/location-fields.ts +9 -1
  215. package/src/new-project/new-project-modal.ts +270 -262
  216. package/src/packages/jxsuite-update.ts +97 -48
  217. package/src/packages/pull-package-sync.ts +18 -9
  218. package/src/page-params.ts +2 -1
  219. package/src/panels/activity-bar.ts +152 -91
  220. package/src/panels/activity-panel.ts +481 -0
  221. package/src/panels/ai-chat/chat-markdown.ts +8 -3
  222. package/src/panels/ai-chat/chat-view.ts +245 -18
  223. package/src/panels/ai-chat/composer.ts +31 -5
  224. package/src/panels/ai-chat/sessions-view.ts +17 -10
  225. package/src/panels/ai-panel.ts +279 -120
  226. package/src/panels/block-action-bar.ts +1115 -224
  227. package/src/panels/bottom-dock.ts +337 -0
  228. package/src/panels/canvas-dnd-bridge.ts +37 -8
  229. package/src/panels/chat-panel.ts +29 -12
  230. package/src/panels/data-explorer.ts +254 -112
  231. package/src/panels/data-grid.ts +10 -4
  232. package/src/panels/dnd.ts +28 -51
  233. package/src/panels/editors.ts +434 -107
  234. package/src/panels/elements-panel.ts +66 -2
  235. package/src/panels/empty-state.ts +112 -0
  236. package/src/panels/events-panel.ts +596 -82
  237. package/src/panels/formula-workspace.ts +408 -218
  238. package/src/panels/frontmatter-fields.ts +51 -10
  239. package/src/panels/frontmatter-panel.ts +357 -79
  240. package/src/panels/git-panel.ts +375 -164
  241. package/src/panels/head-panel.ts +791 -85
  242. package/src/panels/i18n-panel.ts +592 -0
  243. package/src/panels/imports-panel.ts +95 -85
  244. package/src/panels/jump-bar.ts +584 -0
  245. package/src/panels/layers-panel.ts +1106 -328
  246. package/src/panels/left-panel.ts +109 -204
  247. package/src/panels/navigator-panels.ts +131 -0
  248. package/src/panels/overlays.ts +7 -2
  249. package/src/panels/pane-context.ts +1477 -0
  250. package/src/panels/pane-grid.ts +406 -0
  251. package/src/panels/panel-registry.ts +340 -0
  252. package/src/panels/panel-scheduler.ts +75 -0
  253. package/src/panels/problems-panel.ts +234 -0
  254. package/src/panels/properties-panel.ts +946 -1139
  255. package/src/panels/provenance.ts +258 -0
  256. package/src/panels/quick-search.ts +791 -122
  257. package/src/panels/right-panel.ts +228 -102
  258. package/src/panels/seo-modal.ts +395 -0
  259. package/src/panels/settings-pane.ts +203 -0
  260. package/src/panels/signals-panel.ts +354 -137
  261. package/src/panels/statement-editor.ts +41 -25
  262. package/src/panels/statusbar.ts +342 -106
  263. package/src/panels/style-panel.ts +974 -463
  264. package/src/panels/style-utils.ts +3 -3
  265. package/src/panels/stylebook-doc.ts +4 -2
  266. package/src/panels/stylebook-layers-panel.ts +3 -2
  267. package/src/panels/stylebook-panel.ts +51 -26
  268. package/src/panels/tab-strip.ts +1058 -78
  269. package/src/panels/target-line.ts +274 -0
  270. package/src/panels/toolbar.ts +631 -484
  271. package/src/panels/welcome-screen.ts +301 -198
  272. package/src/platform-errors.ts +26 -3
  273. package/src/platform.ts +96 -2
  274. package/src/platforms/cloud.ts +71 -8
  275. package/src/platforms/devserver.ts +92 -29
  276. package/src/publish/deploy-checklist.ts +383 -0
  277. package/src/publish/pages-service.ts +10 -1
  278. package/src/publish/publish-commands.ts +218 -0
  279. package/src/publish/publish-panel.ts +110 -16
  280. package/src/recent-projects.ts +3 -2
  281. package/src/services/a11y-report.ts +505 -0
  282. package/src/services/ai-models.ts +75 -2
  283. package/src/services/ai-project-tools.ts +29 -10
  284. package/src/services/ai-system-prompt.ts +53 -14
  285. package/src/services/ai-tools.ts +39 -3
  286. package/src/services/ai-writes.ts +154 -0
  287. package/src/services/announce.ts +97 -0
  288. package/src/services/automation.ts +424 -175
  289. package/src/services/cem-export.ts +9 -4
  290. package/src/services/clock.ts +61 -0
  291. package/src/services/document-assistant.ts +36 -8
  292. package/src/services/idle.ts +179 -0
  293. package/src/services/import-client.ts +26 -0
  294. package/src/services/monaco-buffer.ts +627 -0
  295. package/src/services/monaco-lazy.ts +36 -0
  296. package/src/services/notify.ts +271 -0
  297. package/src/services/profile.ts +155 -0
  298. package/src/services/references.ts +0 -0
  299. package/src/services/token-lint.ts +3 -2
  300. package/src/services/tool-executor.ts +53 -7
  301. package/src/services/trusted-types.ts +123 -0
  302. package/src/settings/contexts-section.ts +427 -0
  303. package/src/settings/contributed-section.ts +149 -11
  304. package/src/settings/css-vars-editor.ts +306 -349
  305. package/src/settings/defs-editor.ts +93 -34
  306. package/src/settings/dependencies-editor.ts +7 -7
  307. package/src/settings/extension-sections.ts +37 -5
  308. package/src/settings/general-settings.ts +204 -121
  309. package/src/settings/locales-section.ts +347 -0
  310. package/src/settings/preferences-accounts.ts +140 -0
  311. package/src/settings/preferences-dialog.ts +620 -0
  312. package/src/settings/preferences-keymap.ts +270 -0
  313. package/src/settings/project-sections.ts +260 -0
  314. package/src/settings/section-registry.ts +169 -0
  315. package/src/settings/settings-document.ts +385 -0
  316. package/src/shell.ts +1549 -0
  317. package/src/site-context.ts +45 -20
  318. package/src/state.ts +14 -275
  319. package/src/store.ts +88 -43
  320. package/src/studio.ts +790 -346
  321. package/src/style/live-preview.ts +36 -0
  322. package/src/style/project-styles.ts +349 -0
  323. package/src/style/token-ref.ts +123 -0
  324. package/src/tabs/patch-ops.ts +16 -4
  325. package/src/tabs/project-config.ts +416 -0
  326. package/src/tabs/selection.ts +323 -0
  327. package/src/tabs/tab.ts +182 -47
  328. package/src/tabs/transact.ts +209 -28
  329. package/src/types.ts +68 -13
  330. package/src/ui/ai-credentials-form.ts +39 -35
  331. package/src/ui/ai-managed-connect.ts +113 -0
  332. package/src/ui/dynamic-slot.ts +224 -81
  333. package/src/ui/expression-editor.ts +71 -63
  334. package/src/ui/field-input.ts +4 -6
  335. package/src/ui/field-row.ts +215 -38
  336. package/src/ui/form-controls.ts +194 -7
  337. package/src/ui/layers.ts +492 -69
  338. package/src/ui/media-picker.ts +115 -25
  339. package/src/ui/panel-resize.ts +109 -91
  340. package/src/ui/progress-modal.ts +131 -50
  341. package/src/ui/regions.ts +462 -0
  342. package/src/ui/schema-form.ts +229 -45
  343. package/src/ui/spectrum.ts +24 -8
  344. package/src/ui/timing.ts +49 -0
  345. package/src/ui/value-selector.ts +20 -0
  346. package/src/ui/value-source.ts +348 -0
  347. package/src/ui/virtual-window.ts +400 -0
  348. package/src/utils/edit-display.ts +3 -2
  349. package/src/utils/inherited-style.ts +67 -37
  350. package/src/view.ts +64 -78
  351. package/src/workspace/pane-derive.ts +1258 -0
  352. package/src/workspace/session.ts +317 -0
  353. package/src/workspace/workspace.ts +1345 -23
  354. package/styles/canvas.css +653 -0
  355. package/styles/forced-colors.css +106 -0
  356. package/styles/inspector.css +1454 -0
  357. package/styles/overlays.css +2704 -0
  358. package/styles/panels.css +1701 -0
  359. package/styles/shell.css +1423 -0
  360. package/styles/tokens.css +151 -0
  361. package/dist/chunks/Overlay.dev-cszx7yn5.js +0 -22
  362. package/dist/chunks/extension-sections-qct0gt8f.js +0 -531
  363. package/dist/chunks/extension-sections-qct0gt8f.js.map +0 -12
  364. package/dist/chunks/files-02zn5y24.js +0 -50
  365. package/dist/chunks/files-02zn5y24.js.map +0 -9
  366. package/dist/chunks/git-panel-mbb0y0hz.js +0 -28
  367. package/dist/chunks/git-panel-mbb0y0hz.js.map +0 -9
  368. package/dist/chunks/layers-panel-9qekmqk1.js +0 -26
  369. package/dist/chunks/layers-panel-9qekmqk1.js.map +0 -9
  370. package/dist/chunks/settings-modal-gj0hrah4.js +0 -29
  371. package/dist/chunks/settings-modal-gj0hrah4.js.map +0 -9
  372. package/dist/chunks/sp-overlay.dev-earn5as8.js +0 -17
  373. package/dist/chunks/sp-overlay.dev-earn5as8.js.map +0 -9
  374. package/dist/chunks/sp-popover.dev-frxtfejn.js +0 -8
  375. package/dist/chunks/sp-popover.dev-frxtfejn.js.map +0 -9
  376. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js +0 -14
  377. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js.map +0 -9
  378. package/dist/chunks/sp-tooltip.dev-a9fywesm.js +0 -21
  379. package/dist/chunks/sp-tooltip.dev-a9fywesm.js.map +0 -10
  380. package/dist/chunks/studio-4k0m4fmp.js.map +0 -13
  381. package/dist/chunks/studio-568nx4t5.js +0 -469
  382. package/dist/chunks/studio-568nx4t5.js.map +0 -14
  383. package/dist/chunks/studio-9smfzqhc.js +0 -19
  384. package/dist/chunks/studio-9smfzqhc.js.map +0 -10
  385. package/dist/chunks/studio-asvf0j9e.js +0 -429
  386. package/dist/chunks/studio-asvf0j9e.js.map +0 -13
  387. package/dist/chunks/studio-d77qm3pk.js +0 -3427
  388. package/dist/chunks/studio-d77qm3pk.js.map +0 -26
  389. package/dist/chunks/studio-j6gqyjf4.js +0 -1854
  390. package/dist/chunks/studio-j6gqyjf4.js.map +0 -18
  391. package/dist/chunks/studio-n9zmpxjg.js.map +0 -115
  392. package/dist/chunks/studio-qkvt37c0.js.map +0 -9
  393. package/dist/chunks/studio-rq0vxb19.js +0 -3744
  394. package/dist/chunks/studio-rq0vxb19.js.map +0 -22
  395. package/dist/chunks/studio-wd6ewdvd.js +0 -853
  396. package/dist/chunks/studio-wd6ewdvd.js.map +0 -12
  397. package/dist/chunks/studio-xq0t7n5t.js +0 -140
  398. package/dist/chunks/studio-xq0t7n5t.js.map +0 -10
  399. package/dist/chunks/studio-xze4zrs1.js.map +0 -10
  400. package/dist/chunks/studio-ygzav7z5.js.map +0 -46
  401. package/dist/chunks/studio-zmy7241e.js +0 -399
  402. package/dist/chunks/studio-zmy7241e.js.map +0 -12
  403. package/dist/chunks/ws-client-q7hkz3j9.js.map +0 -11
  404. package/src/browse/browse-modal.ts +0 -69
  405. package/src/browse/browse.ts +0 -904
  406. package/src/canvas/canvas-diff.ts +0 -216
  407. package/src/canvas/nested-site-style.ts +0 -58
  408. package/src/new-project/design-fields.ts +0 -264
  409. package/src/new-project/templates.ts +0 -61
  410. package/src/panels/tab-bar.ts +0 -548
  411. package/src/settings/settings-modal.ts +0 -203
  412. /package/dist/chunks/{adapter-z8fkmbmv.js.map → adapter-qfnesn5v.js.map} +0 -0
  413. /package/dist/chunks/{cf-settings-mmyv436d.js.map → cf-settings-73sh5z4p.js.map} +0 -0
  414. /package/dist/chunks/{format-host-2c188n41.js.map → format-host-mkwsmt8d.js.map} +0 -0
  415. /package/dist/chunks/{jx-validate-qx8degj7.js.map → jx-validate-v6qgknrs.js.map} +0 -0
  416. /package/dist/chunks/{studio-w7c6kvk7.js.map → studio-a7pzveq1.js.map} +0 -0
  417. /package/dist/chunks/{studio-hhj92gcr.js.map → studio-s519zc99.js.map} +0 -0
  418. /package/dist/chunks/{studio-bghqfmea.js.map → studio-wwseqyw0.js.map} +0 -0
  419. /package/dist/chunks/{studio-nqw433kv.js.map → studio-xmvbhg31.js.map} +0 -0
@@ -1,4 +1,14 @@
1
1
  /// <reference lib="dom" />
2
+ /**
3
+ * The expression editor — one operator row plus an operand editor per slot, nested to any depth.
4
+ *
5
+ * **The layout is CSS, in `styles/inspector.css` (§ "The expression editor").** Every row here used
6
+ * to be an inline `display:flex;…` attribute with a hard `min-width` on the pickers, and no rung of
7
+ * that chain could set `min-width: 0` — so at Inspector width the operand controls refused to
8
+ * shrink and Operator, Target and Value were clipped by the edge of the window. The editor is drawn
9
+ * in the Inspector (~280px), in the Navigator, and in the Bottom dock's Logic tab (very wide), so
10
+ * every row wraps to a stack rather than sizing itself in pixels.
11
+ */
2
12
  import { html, nothing } from "lit-html";
3
13
  import { live } from "lit-html/directives/live.js";
4
14
  import { PURE_METHOD_OPS } from "@jxsuite/runtime/expression";
@@ -7,6 +17,8 @@ import { renderFieldRow } from "./field-row";
7
17
  import { renderFormulaChips } from "./formula-chips";
8
18
  import { applyCatalogPick, calleeEntry, formulaCatalog } from "./formula-catalog";
9
19
  import { openFormulaPalette } from "./formula-palette";
20
+ import { VALUE_SOURCE_LABELS } from "./value-source";
21
+ import { renderEmptyState } from "../panels/empty-state";
10
22
 
11
23
  import type {
12
24
  JxExpressionNode,
@@ -35,6 +47,18 @@ const BINARY_OPS = new Set([
35
47
  "??",
36
48
  ]);
37
49
  const ASSIGN_OPS = new Set(["=", "+=", "-=", "*=", "/="]);
50
+
51
+ /**
52
+ * Does this expression node DO something rather than compute something?
53
+ *
54
+ * An assignment writes a target and yields nothing worth reading; a `+` or a `filter` yields a
55
+ * value. The Data panel needs the distinction to know whether a row has a value column at all —
56
+ * labelling `setBeds0` "pending" reads as "still loading" for a thing that will never load.
57
+ */
58
+ export function isActionExpression(node: unknown): boolean {
59
+ const op = (node as { operator?: unknown } | null | undefined)?.operator;
60
+ return typeof op === "string" && ASSIGN_OPS.has(op);
61
+ }
38
62
  const NO_ARG_OPS = new Set(["pop", "shift"]);
39
63
  const ONE_ARG_OPS = new Set(["push", "unshift"]);
40
64
 
@@ -180,6 +204,10 @@ function operatorInfo(op: string): OperatorInfo {
180
204
  // ─── Operand Mode Detection ─────────────────────────────────────────────────
181
205
 
182
206
  /**
207
+ * Which rung of the value ladder an operand occupies. The three answers are the same three the
208
+ * Properties, Style and Logic tabs give, and they are spelled the same way — `ui/value-source.ts`
209
+ * owns the words, so `lit / $ref / expr` is gone from the operand picker (plan §6.3).
210
+ *
183
211
  * @param {unknown} operand
184
212
  * @returns {"ref" | "expression" | "literal"}
185
213
  */
@@ -315,14 +343,7 @@ function renderValueBadge(preview: EditorPreview | null | undefined, pathKey: st
315
343
  if (text === undefined) {
316
344
  return nothing;
317
345
  }
318
- return html`
319
- <span
320
- class="expr-live-badge"
321
- title=${text}
322
- style="font-family:var(--spectrum-code-font-family, monospace);font-size:10px;line-height:16px;padding:0 5px;border-radius:4px;background:var(--spectrum-gray-200, #323232);color:var(--spectrum-seafoam-900, #35a690);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:160px;flex-shrink:1"
323
- >${text}</span
324
- >
325
- `;
346
+ return html`<span class="expr-live-badge" title=${text}>${text}</span>`;
326
347
  }
327
348
 
328
349
  // ─── Ref Picker ─────────────────────────────────────────────────────────────
@@ -343,10 +364,20 @@ function renderRefPicker(
343
364
  const allRefs = [...stateRefs, ...eventRefs];
344
365
  const isCustom = refVal && !allRefs.includes(refVal);
345
366
 
367
+ // Nothing to pick and nothing already picked: a picker whose only entry is a disabled "No state
368
+ // Defined" is a dead end. Say what a binding is instead, in the shell's one empty-state voice.
369
+ if (allRefs.length === 0 && !refVal) {
370
+ return renderEmptyState({
371
+ compact: true,
372
+ detail: "Add one in the State panel and it shows up here.",
373
+ message: "A binding points at a value this page holds.",
374
+ });
375
+ }
376
+
346
377
  return html`
347
378
  <sp-picker
348
379
  size="s"
349
- style="flex:1"
380
+ class="expr-ref"
350
381
  placeholder="Select…"
351
382
  .value=${live(isCustom ? "__custom__" : refVal || "")}
352
383
  @change=${(e: Event) => {
@@ -357,13 +388,9 @@ function renderRefPicker(
357
388
  onRefChange(val);
358
389
  }}
359
390
  >
360
- ${
361
- stateRefs.length > 0
362
- ? stateRefs.map(
363
- (r) => html`<sp-menu-item value=${r}>${r.replace("#/state/", "")}</sp-menu-item>`,
364
- )
365
- : html`<sp-menu-item disabled>No state defined</sp-menu-item>`
366
- }
391
+ ${stateRefs.map(
392
+ (r) => html`<sp-menu-item value=${r}>${r.replace("#/state/", "")}</sp-menu-item>`,
393
+ )}
367
394
  ${
368
395
  eventRefs.length > 0
369
396
  ? html`
@@ -386,10 +413,10 @@ function renderRefPicker(
386
413
  function renderLiteralEditor(operand: unknown, onChange: (newVal: JxExpressionOperand) => void) {
387
414
  const type = literalType(operand);
388
415
  return html`
389
- <div style="display:flex;gap:4px;align-items:center;flex:1">
416
+ <div class="expr-literal">
390
417
  <sp-picker
391
418
  size="s"
392
- style="min-width:56px"
419
+ class="expr-literal-type"
393
420
  .value=${live(type)}
394
421
  @change=${(e: Event) => {
395
422
  const newType = (e.target as HTMLInputElement).value;
@@ -405,28 +432,26 @@ function renderLiteralEditor(operand: unknown, onChange: (newVal: JxExpressionOp
405
432
  type === "string"
406
433
  ? html`<sp-textfield
407
434
  size="s"
408
- style="flex:1"
435
+ class="expr-literal-value"
409
436
  .value=${live(String(operand ?? ""))}
410
437
  @input=${(e: Event) => onChange((e.target as HTMLInputElement).value)}
411
438
  ></sp-textfield>`
412
439
  : type === "number"
413
440
  ? html`<sp-number-field
414
441
  size="s"
415
- style="flex:1"
442
+ class="expr-literal-value"
416
443
  .value=${live(Number(operand ?? 0))}
417
444
  @change=${(e: Event) => onChange(Number((e.target as HTMLInputElement).value))}
418
445
  ></sp-number-field>`
419
446
  : type === "boolean"
420
447
  ? html`<sp-checkbox
421
448
  size="s"
449
+ class="expr-literal-bool"
422
450
  ?checked=${Boolean(operand)}
423
451
  @change=${(e: Event) => onChange((e.target as HTMLInputElement).checked)}
424
452
  >true</sp-checkbox
425
453
  >`
426
- : html`<span
427
- style="font-size:var(--spectrum-font-size-75, 12px);color:var(--spectrum-gray-600, #808080)"
428
- >null</span
429
- >`
454
+ : html`<span class="expr-literal-null">null</span>`
430
455
  }
431
456
  </div>
432
457
  `;
@@ -465,25 +490,26 @@ export function renderOperandEditor(
465
490
  if (opts.mustBeRef) {
466
491
  const refVal = ((operand as Record<string, unknown> | null)?.$ref as string) ?? "";
467
492
  return html`
468
- <div style="flex:1">${renderRefPicker(refVal, (r) => onChange({ $ref: r }), opts)}</div>
493
+ <div class="expr-operand">${renderRefPicker(refVal, (r) => onChange({ $ref: r }), opts)}</div>
469
494
  `;
470
495
  }
471
496
 
472
497
  const mode = operandMode(operand);
473
498
  return html`
474
- <div style="display:flex;gap:4px;align-items:flex-start;flex:1">
499
+ <div class="expr-operand">
475
500
  <sp-picker
476
501
  size="s"
477
- style="min-width:60px"
502
+ class="expr-operand-mode"
503
+ label="Value source"
478
504
  .value=${live(mode)}
479
505
  @change=${(e: Event) => {
480
506
  const newMode = (e.target as HTMLInputElement).value;
481
507
  onChange(defaultForMode(newMode));
482
508
  }}
483
509
  >
484
- <sp-menu-item value="literal">lit</sp-menu-item>
485
- <sp-menu-item value="ref">$ref</sp-menu-item>
486
- <sp-menu-item value="expression">expr</sp-menu-item>
510
+ <sp-menu-item value="literal">${VALUE_SOURCE_LABELS.literal}</sp-menu-item>
511
+ <sp-menu-item value="ref">${VALUE_SOURCE_LABELS.ref}</sp-menu-item>
512
+ <sp-menu-item value="expression">${VALUE_SOURCE_LABELS.expression}</sp-menu-item>
487
513
  </sp-picker>
488
514
  ${
489
515
  mode === "literal"
@@ -526,13 +552,8 @@ function renderSpliceArgsEditor(
526
552
  <div class="array-object-field">
527
553
  ${safeArgs.map(
528
554
  (arg, idx) => html`
529
- <div
530
- class="array-object-row"
531
- style="display:flex;gap:4px;align-items:center;margin-bottom:4px"
532
- >
533
- <span style="font-size:10px;color:var(--spectrum-gray-600, #808080);min-width:30px">
534
- ${labels[idx] ?? fallbackLabel}
535
- </span>
555
+ <div class="array-object-row">
556
+ <span class="array-object-label">${labels[idx] ?? fallbackLabel}</span>
536
557
  ${renderOperandEditor(
537
558
  arg,
538
559
  (newArg) => {
@@ -614,21 +635,14 @@ export function renderExpressionEditor(
614
635
  const sub = (...segs: (string | number)[]) => [...path, ...segs].join("/");
615
636
  /** Wrap an operand widget with its live value badge. */
616
637
  const withBadge = (widget: unknown, key: string) =>
617
- html`<div style="display:flex;gap:4px;align-items:center;flex:1;min-width:0">
618
- ${widget}${renderValueBadge(preview, key)}
619
- </div>`;
620
-
621
- const nestStyle =
622
- depth > 0
623
- ? "border-left:2px solid var(--spectrum-gray-300, #3c3c3c);margin-left:8px;padding-left:8px;"
624
- : "";
638
+ html`<div class="expr-widget">${widget}${renderValueBadge(preview, key)}</div>`;
625
639
 
626
640
  // The root badge: pure roots show their result; mutating roots' effect shows on target/value.
627
641
  const rootBadge =
628
642
  depth === 0 && preview && !preview.mutating ? renderValueBadge(preview, pathKey) : nothing;
629
643
 
630
644
  return html`
631
- <div class="expression-editor" style=${nestStyle}>
645
+ <div class=${depth > 0 ? "expression-editor expression-editor--nested" : "expression-editor"}>
632
646
  ${
633
647
  depth === 0
634
648
  ? renderFormulaChips(safeNode, opts.onChipSelect ?? (() => {}), { path, preview })
@@ -636,11 +650,7 @@ export function renderExpressionEditor(
636
650
  }
637
651
  ${
638
652
  depth === 0 && preview?.error
639
- ? html`<div
640
- style="font-size:10px;color:var(--spectrum-negative-content-color-default, #f76a63);padding:2px 0"
641
- >
642
- ${preview.error}
643
- </div>`
653
+ ? html`<div class="expr-error">${preview.error}</div>`
644
654
  : nothing
645
655
  }
646
656
  ${renderFieldRow({
@@ -648,9 +658,10 @@ export function renderExpressionEditor(
648
658
  label: "Operator",
649
659
  prop: "operator",
650
660
  widget: html`
651
- <div style="display:flex;gap:4px;align-items:center;flex:1;min-width:0">
661
+ <div class="expr-widget">
652
662
  <sp-picker
653
663
  size="s"
664
+ class="expr-operator"
654
665
  .value=${live(op)}
655
666
  @change=${(e: Event) => {
656
667
  const newOp = (e.target as HTMLInputElement).value;
@@ -748,7 +759,7 @@ export function renderExpressionEditor(
748
759
  ${
749
760
  info.needsValue && info.valueIsNode
750
761
  ? html`
751
- <div style="margin-top:4px">
762
+ <div class="expr-subsection">
752
763
  ${renderFieldRow({
753
764
  hasValue: false,
754
765
  label: "Per-item",
@@ -769,7 +780,7 @@ export function renderExpressionEditor(
769
780
  ${
770
781
  info.spliceArray
771
782
  ? html`
772
- <div style="margin-top:4px">
783
+ <div class="expr-subsection">
773
784
  ${renderFieldRow({
774
785
  hasValue: false,
775
786
  label: "Args",
@@ -791,7 +802,7 @@ export function renderExpressionEditor(
791
802
  ${
792
803
  info.callArgs
793
804
  ? html`
794
- <div style="margin-top:4px">
805
+ <div class="expr-subsection">
795
806
  ${renderFieldRow({
796
807
  hasValue: false,
797
808
  label: "Args",
@@ -867,13 +878,13 @@ function renderSwitchCasesEditor(
867
878
  const setCases = (next: Record<string, unknown>) => onChange({ ...safeNode, cases: next });
868
879
 
869
880
  return html`
870
- <div class="switch-cases" style="margin-top:4px">
881
+ <div class="switch-cases">
871
882
  ${entries.map(
872
883
  ([key, operand]) => html`
873
- <div style="display:flex;gap:4px;align-items:flex-start;margin-bottom:4px">
884
+ <div class="switch-case-row">
874
885
  <sp-textfield
875
886
  size="s"
876
- style="width:80px;flex-shrink:0"
887
+ class="switch-case-key"
877
888
  placeholder="value"
878
889
  .value=${live(key)}
879
890
  @change=${(e: Event) => {
@@ -910,11 +921,8 @@ function renderSwitchCasesEditor(
910
921
  </div>
911
922
  `,
912
923
  )}
913
- <div style="display:flex;gap:4px;align-items:flex-start;margin-bottom:4px">
914
- <span
915
- style="width:80px;flex-shrink:0;font-size:10px;line-height:24px;color:var(--spectrum-gray-600, #808080)"
916
- >default</span
917
- >
924
+ <div class="switch-case-row">
925
+ <span class="switch-case-key switch-case-default">default</span>
918
926
  ${withBadge(
919
927
  renderOperandEditor(safeNode.default, (v) => onChange({ ...safeNode, default: v }), {
920
928
  ...opts,
@@ -20,13 +20,11 @@ import { html } from "lit-html";
20
20
  import type { TemplateResult } from "lit-html";
21
21
  import { live } from "lit-html/directives/live.js";
22
22
  import { keyed } from "lit-html/directives/keyed.js";
23
+ import { LIVE_PREVIEW } from "./timing";
23
24
 
24
25
  /** A lit-renderable value (template or directive result). */
25
26
  type Renderable = unknown;
26
27
 
27
- /** Default debounce (ms) before an in-progress edit is committed to the document. */
28
- export const DEFAULT_DEBOUNCE_MS = 350;
29
-
30
28
  interface DraftEntry {
31
29
  value: string;
32
30
  timer?: ReturnType<typeof setTimeout>;
@@ -167,7 +165,7 @@ export function spTextField(
167
165
  commit: (v: string) => void,
168
166
  opts: FieldOpts = {},
169
167
  ): Renderable {
170
- const ms = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
168
+ const ms = opts.debounceMs ?? LIVE_PREVIEW;
171
169
  const { onInput, onCommit, onKeydown } = makeHandlers(key, ms, commit, opts.commitMode);
172
170
  return keyed(
173
171
  key,
@@ -193,7 +191,7 @@ export function spTextArea(
193
191
  commit: (v: string) => void,
194
192
  opts: FieldOpts = {},
195
193
  ): Renderable {
196
- const ms = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
194
+ const ms = opts.debounceMs ?? LIVE_PREVIEW;
197
195
  const { onInput, onCommit } = makeHandlers(key, ms, commit, opts.commitMode);
198
196
  return keyed(
199
197
  key,
@@ -219,7 +217,7 @@ export function rawTextArea(
219
217
  commit: (v: string) => void,
220
218
  opts: TextAreaOpts = {},
221
219
  ): Renderable {
222
- const ms = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
220
+ const ms = opts.debounceMs ?? LIVE_PREVIEW;
223
221
  const { onInput, onCommit } = makeHandlers(key, ms, commit, opts.commitMode);
224
222
  const style = [
225
223
  `min-height:${opts.minHeight ?? "40px"}`,
@@ -1,27 +1,79 @@
1
1
  /// <reference lib="dom" />
2
2
  /**
3
- * Field-row.js — Universal field row layout for all Studio panels.
3
+ * Field-row.js — the inspector's row vocabulary, in one module.
4
4
  *
5
- * Renders the consistent pattern: indicator dot + label + widget slot. Used by style panel,
6
- * attributes panel, frontmatter panel, signals panel, etc.
5
+ * Three rows, and every panel that draws one imports it from here:
6
+ *
7
+ * - {@link renderFieldRow} — label + widget, plus §7.1's **third notification tier** (an `error` line
8
+ * rendered at the control) and §6.2's **provenance chip** (where this value came from). The chip
9
+ * itself is `panels/provenance.ts`'s: one vocabulary, two cascades, one template.
10
+ * - {@link renderKvRow} — an editable key/value pair with a delete affordance. Custom attributes and
11
+ * custom CSS properties are the same row; they were two.
12
+ * - {@link renderStaticKvRow} — the read-only name/value line. Observed Attributes, CSS Properties
13
+ * and CSS Parts each hand-wrote it with the same inline style declarations.
14
+ *
15
+ * **The error line.** The other two notification tiers are hosted records in `services/notify.ts`:
16
+ * a toast is taken away on a timer, a problem is kept until somebody fixes it. An inline error is
17
+ * neither, because it is not a record at all — it is a property of the value currently in the
18
+ * field, and it lives exactly as long as that value does. Which is the point: a rejected value used
19
+ * to be announced in a three-second grey line at the bottom of the window, hundreds of pixels from
20
+ * the field that rejected it, and by the time you looked down the field had snapped back and the
21
+ * reason was gone.
22
+ *
23
+ * **Validated on commit, never on input** (§7.1). Producers decide a value is bad on `change` or
24
+ * blur, not on `input` — a field cannot spend the middle of every word telling you it is wrong.
7
25
  */
8
26
 
9
27
  import { html, nothing } from "lit-html";
10
28
  import { classMap } from "lit-html/directives/class-map.js";
29
+ import { renderProvenanceChip } from "../panels/provenance";
30
+
31
+ import type { FieldProvenance } from "../panels/provenance";
32
+
33
+ // ─── The field row ───────────────────────────────────────────────────────────
34
+
35
+ /** Options for {@link renderFieldRow}. */
36
+ export interface FieldRowOptions {
37
+ prop: string;
38
+ label: string;
39
+ hasValue: boolean;
40
+ onClear?: () => void;
41
+ widget: unknown;
42
+ /** Rendered after the label inside the label cell (e.g. the dynamic-slot mode button). */
43
+ labelExtra?: unknown;
44
+ span?: number;
45
+ warning?: boolean;
46
+ /**
47
+ * Where this value came from (§6.2). When omitted the row derives the `set`/`default` pair from
48
+ * `hasValue` and `onClear`, which is what every caller meant by them all along; a caller that can
49
+ * answer the richer question passes the answer.
50
+ */
51
+ provenance?: FieldProvenance | undefined;
52
+ /**
53
+ * Why the value in this control is not acceptable — §7.1's inline tier.
54
+ *
55
+ * Rendered under the widget in danger colour with `role="alert"`, so a screen reader announces it
56
+ * when it appears. Presence also marks the row invalid, so the state is legible without reading:
57
+ * `warning` says "this is unusual", `error` says "this is refused".
58
+ *
59
+ * Empty string and `undefined` both mean "no error", so a producer can pass its validator's
60
+ * output straight through without a ternary.
61
+ */
62
+ error?: string | undefined;
63
+ /**
64
+ * How many times this value has been refused in a row — the "message + counter" §7.1 asks for.
65
+ *
66
+ * A repeat count is the difference between "the field is still red from last time" and "it just
67
+ * refused me again", which is otherwise invisible when the second attempt fails the same way.
68
+ * Rendered from 2 up.
69
+ */
70
+ errorCount?: number | undefined;
71
+ }
11
72
 
12
73
  /**
13
- * Render a universal field row with indicator dot, label, and widget.
74
+ * Render a universal field row with a provenance chip, label, widget, and an optional inline error.
14
75
  *
15
- * @param {{
16
- * prop: string;
17
- * label: string;
18
- * hasValue: boolean;
19
- * onClear?: () => void;
20
- * widget: unknown;
21
- * labelExtra?: unknown;
22
- * span?: number;
23
- * warning?: boolean;
24
- * }} opts
76
+ * @param {FieldRowOptions} opts
25
77
  * @returns {import("lit-html").TemplateResult}
26
78
  */
27
79
  export function renderFieldRow({
@@ -33,40 +85,165 @@ export function renderFieldRow({
33
85
  labelExtra,
34
86
  span,
35
87
  warning,
36
- }: {
37
- prop: string;
38
- label: string;
39
- hasValue: boolean;
40
- onClear?: () => void;
41
- widget: unknown;
42
- /** Rendered after the label inside the label cell (e.g. the dynamic-slot mode button). */
43
- labelExtra?: unknown;
44
- span?: number;
45
- warning?: boolean;
46
- }) {
88
+ provenance,
89
+ error,
90
+ errorCount,
91
+ }: FieldRowOptions) {
92
+ const invalid = Boolean(error);
93
+ const chip: FieldProvenance | undefined =
94
+ provenance ??
95
+ (hasValue && onClear ? { onClick: onClear, state: "set", title: `Clear ${prop}` } : undefined);
47
96
  return html`
48
97
  <div
49
- class=${classMap({ "style-row": true, "style-row--warning": Boolean(warning) })}
98
+ class=${classMap({
99
+ "style-row": true,
100
+ "style-row--invalid": invalid,
101
+ "style-row--warning": Boolean(warning) && !invalid,
102
+ })}
50
103
  data-prop=${prop}
51
104
  style=${span === 2 ? "grid-column: 1 / -1" : ""}
52
105
  >
53
106
  <div class="style-row-label">
54
- ${
55
- hasValue && onClear
56
- ? html`<span
57
- class="set-dot"
58
- title="Clear ${prop}"
59
- @click=${(e: Event) => {
60
- e.stopPropagation();
61
- onClear();
62
- }}
63
- ></span>`
64
- : nothing
65
- }
107
+ ${chip ? renderProvenanceChip(prop, chip) : nothing}
66
108
  <sp-field-label size="s" title=${prop}>${label}</sp-field-label>
67
109
  ${labelExtra ?? nothing}
68
110
  </div>
69
111
  ${widget}
112
+ ${
113
+ invalid
114
+ ? html`<p class="style-row-error" role="alert">
115
+ ${error}${
116
+ errorCount !== undefined && errorCount > 1
117
+ ? html`<span class="style-row-error-count">×${errorCount}</span>`
118
+ : nothing
119
+ }
120
+ </p>`
121
+ : nothing
122
+ }
123
+ </div>
124
+ `;
125
+ }
126
+
127
+ // ─── The editable key/value row ──────────────────────────────────────────────
128
+
129
+ /** Options for {@link renderKvRow}. */
130
+ export interface KvRowOptions {
131
+ /** The pair's current key. */
132
+ name: string;
133
+ /** The pair's current value. */
134
+ value: string;
135
+ /** Commit both cells, debounced, with the current contents of each. */
136
+ onCommit: (name: string, value: string) => void;
137
+ /** Remove the pair outright. */
138
+ onDelete: () => void;
139
+ /**
140
+ * The value cell's placeholder, resolved from the key — a CSS property's initial value, say.
141
+ *
142
+ * A resolver rather than a string because the key is editable: typing `display` into the key cell
143
+ * should offer `inline` under it before anything is committed, and only the caller knows the
144
+ * map.
145
+ */
146
+ placeholderFor?: ((name: string) => string) | undefined;
147
+ /** Debounce before {@link KvRowOptions.onCommit}, in ms. */
148
+ debounceMs?: number;
149
+ }
150
+
151
+ /**
152
+ * An editable key/value pair with a delete affordance — the ONE implementation.
153
+ *
154
+ * Custom HTML attributes and custom CSS properties are the same row and were written twice, with
155
+ * different rename behaviour (one committed on `input`, the other on `change`) and different
156
+ * placeholder plumbing. They now differ only in what they pass in.
157
+ *
158
+ * @param {KvRowOptions} opts
159
+ */
160
+ export function renderKvRow({
161
+ name,
162
+ value,
163
+ onCommit,
164
+ onDelete,
165
+ placeholderFor,
166
+ debounceMs = 400,
167
+ }: KvRowOptions) {
168
+ let debounce: ReturnType<typeof setTimeout> | undefined;
169
+ let currentName = name;
170
+ let currentValue = value;
171
+ const commit = () => {
172
+ clearTimeout(debounce);
173
+ debounce = setTimeout(() => onCommit(currentName, currentValue), debounceMs);
174
+ };
175
+ return html`
176
+ <div class="kv-row" data-prop=${name}>
177
+ <sp-textfield
178
+ size="s"
179
+ class="kv-key"
180
+ .value=${name}
181
+ @input=${(e: Event) => {
182
+ currentName = (e.target as HTMLInputElement).value;
183
+ commit();
184
+ }}
185
+ @change=${
186
+ placeholderFor
187
+ ? (e: Event) => {
188
+ const cell = (e.target as HTMLInputElement)
189
+ .closest(".kv-row")
190
+ ?.querySelector(".kv-val");
191
+ cell?.setAttribute(
192
+ "placeholder",
193
+ placeholderFor((e.target as HTMLInputElement).value),
194
+ );
195
+ }
196
+ : nothing
197
+ }
198
+ ></sp-textfield>
199
+ <sp-textfield
200
+ size="s"
201
+ class="kv-val"
202
+ .value=${value}
203
+ placeholder=${placeholderFor ? placeholderFor(name) : ""}
204
+ @input=${(e: Event) => {
205
+ currentValue = (e.target as HTMLInputElement).value;
206
+ commit();
207
+ }}
208
+ ></sp-textfield>
209
+ <sp-action-button size="xs" quiet title="Remove ${name}" @click=${onDelete}>
210
+ <sp-icon-close slot="icon"></sp-icon-close>
211
+ </sp-action-button>
212
+ </div>
213
+ `;
214
+ }
215
+
216
+ // ─── The read-only key/value row ─────────────────────────────────────────────
217
+
218
+ /** Options for {@link renderStaticKvRow}. */
219
+ export interface StaticKvRowOptions {
220
+ /** The thing being named — an attribute, a custom property, a part. Rendered as code. */
221
+ name: string;
222
+ /** What it maps to, rendered immediately after the name ("→ count", "<button>"). */
223
+ detail?: string | undefined;
224
+ /** Its value or type, rendered hard right. */
225
+ value?: string | undefined;
226
+ /** Flags, rendered hard right after the value ("reflects"). */
227
+ tags?: readonly string[] | undefined;
228
+ }
229
+
230
+ /**
231
+ * A read-only name/value line.
232
+ *
233
+ * Observed Attributes, CSS Properties and CSS Parts each hand-wrote this with the same
234
+ * `display:flex;gap:6px;align-items:center;padding:2px 0;font-size:…` inline block — three copies
235
+ * of one row, none of them addressable by a stylesheet, and so none of them able to respond to the
236
+ * theme. They differ only in which of the three slots they fill.
237
+ *
238
+ * @param {StaticKvRowOptions} opts
239
+ */
240
+ export function renderStaticKvRow({ name, detail, value, tags }: StaticKvRowOptions) {
241
+ return html`
242
+ <div class="kv-static-row" data-prop=${name}>
243
+ <code class="kv-static-name">${name}</code>
244
+ ${detail ? html`<span class="kv-static-detail">${detail}</span>` : nothing}
245
+ ${value ? html`<span class="kv-static-value">${value}</span>` : nothing}
246
+ ${(tags ?? []).map((tag) => html`<span class="kv-static-tag">${tag}</span>`)}
70
247
  </div>
71
248
  `;
72
249
  }