@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,119 +1,99 @@
1
1
  /// <reference lib="dom" />
2
2
  /**
3
- * CSS Variables editor — form-based editor for managing design tokens. Colors are scheme-aware
4
- * (per-scheme overrides in `@--dark`-style blocks, spec §9.5); sizes/spacing are media-aware; fonts
5
- * are neither.
3
+ * The design-token editor — the token half of **Project Styles** (plan §9.4).
4
+ *
5
+ * Every token in the project's root `style` block, grouped by what it names, each editable in place
6
+ * and each able to carry a different value in any **rendering context** the project declares. The
7
+ * model — groups, tokens, contexts, overrides — lives in {@link file://../style/project-styles.ts}
8
+ * so the catalogue, the canvas and this form all read the same vocabulary; this module is the form
9
+ * over it.
10
+ *
11
+ * Three things it does NOT do, each on purpose:
12
+ *
13
+ * - **It does not define a context.** Breakpoints and colour schemes are declared once, in Project
14
+ * Settings › Contexts, and this level only overrides against them (§2 principle 5). Where nothing
15
+ * is declared it says so and routes there.
16
+ * - **It does not change the file format.** Overrides are the same `"@--ctx": { "--token": … }`
17
+ * blocks the compiler already reads, and an emptied block is removed rather than left behind.
18
+ * - **It does not re-render the canvas.** An edit is pushed to every live canvas in place through
19
+ * {@link pushProjectStylesToCanvas} — which is what makes "tune a token and watch the page
20
+ * change" true of the specimen canvas as well as the page one.
6
21
  */
7
22
 
8
23
  import { html, render as litRender, nothing } from "lit-html";
9
24
  import { ref } from "lit-html/directives/ref.js";
25
+ import { classMap } from "lit-html/directives/class-map.js";
26
+ import { errorMessage } from "@jxsuite/schema/parse";
27
+ import { notify } from "../services/notify";
10
28
  import { projectState } from "../store";
11
29
  import { getEffectiveMedia, updateSiteConfig } from "../site-context";
12
- import { postSiteStyleToLiveHosts } from "../canvas/iframe-host";
13
- import { isSchemeQuery, schemeOfQuery } from "../utils/canvas-media";
14
- import { friendlyNameToVar, varDisplayName } from "../utils/studio-utils";
30
+ import {
31
+ addableContexts,
32
+ groupTokens,
33
+ listTokenContexts,
34
+ readTokenOverride,
35
+ writeTokenOverride,
36
+ } from "../style/project-styles";
37
+ import { renderTokenChip, resolveTokenValue, tokenRefName } from "../style/token-ref";
38
+ import { pushProjectStylesToCanvas } from "../style/live-preview";
39
+ import { friendlyNameToVar } from "../utils/studio-utils";
15
40
 
16
41
  import type { JxStyle } from "@jxsuite/schema/types";
42
+ import type { ProjectToken, TokenContext, TokenGroup, TokenGroupId } from "../style/project-styles";
43
+ import type { TemplateResult } from "lit-html";
44
+
45
+ /**
46
+ * What each group's add row hints with — an example friendly name and an example value. These are
47
+ * the form's illustrations, not the model's data: they teach the shape of an answer and are never
48
+ * written anywhere.
49
+ */
50
+ const ADD_ROW_HINTS: Readonly<Record<TokenGroupId, { name: string; value: string }>> = {
51
+ color: { name: "Primary Blue", value: "#3b82f6" },
52
+ font: { name: "Body Serif", value: "'Georgia', serif" },
53
+ other: { name: "Custom Var", value: "value" },
54
+ size: { name: "Spacing Large", value: "32px" },
55
+ };
56
+
57
+ /** The colour a `<input type="color">` shows when the token's value is not a hex literal. */
58
+ const COLOR_INPUT_FALLBACK = "#3b82f6";
17
59
 
18
- /** A declared scheme query in $media: the block key name plus the scheme it targets. */
19
- export interface SchemeEntry {
20
- name: string;
21
- scheme: "light" | "dark";
60
+ /** The same, for an override row: a scheme override is usually a darker value than the base. */
61
+ const OVERRIDE_INPUT_FALLBACK = "#111111";
62
+
63
+ /** What one token row needs from the surrounding form — bound once per render, not per row. */
64
+ interface TokenFormCtx {
65
+ /** The project's root style, mutated in place and persisted through {@link updateSiteConfig}. */
66
+ rootStyle: JxStyle;
67
+ /** Every declared rendering context, schemes first ({@link listTokenContexts}). */
68
+ contexts: TokenContext[];
69
+ /** Commit the current style without re-rendering the form. */
70
+ save: () => void;
71
+ /** Commit and re-render — for anything that changes which rows exist. */
72
+ commit: () => void;
22
73
  }
23
74
 
24
- /** @param {HTMLElement} container */
75
+ /**
76
+ * Render the token editor into a container.
77
+ *
78
+ * @param {HTMLElement} container
79
+ */
25
80
  export function renderCssVarsEditor(container: HTMLElement) {
26
81
  const config = projectState?.projectConfig || {};
27
- const rootStyle = config.style || {};
28
- const media = getEffectiveMedia(config.$media);
29
-
30
- /**
31
- * @type {{
32
- * color: [string, string | number][];
33
- * font: [string, string | number][];
34
- * size: [string, string | number][];
35
- * other: [string, string | number][];
36
- * }}
37
- */
38
- const groups: {
39
- color: [string, string | number][];
40
- font: [string, string | number][];
41
- size: [string, string | number][];
42
- other: [string, string | number][];
43
- } = { color: [], font: [], other: [], size: [] };
44
- for (const [k, v] of Object.entries(rootStyle)) {
45
- if (!k.startsWith("--")) {
46
- continue;
47
- }
48
- if (typeof v !== "string" && typeof v !== "number") {
49
- continue;
50
- }
51
- if (k.startsWith("--color")) {
52
- groups.color.push([k, v]);
53
- } else if (k.startsWith("--font")) {
54
- groups.font.push([k, v]);
55
- } else if (k.startsWith("--size") || k.startsWith("--spacing") || k.startsWith("--radius")) {
56
- groups.size.push([k, v]);
57
- } else {
58
- groups.other.push([k, v]);
59
- }
60
- }
61
-
62
- const mediaNames = media
63
- ? Object.keys(media).filter((m) => m !== "--" && !isSchemeQuery(String(media[m])))
64
- : [];
65
- const schemeEntries: SchemeEntry[] = media
66
- ? Object.entries(media).flatMap(([name, query]) => {
67
- const scheme = schemeOfQuery(String(query));
68
- return scheme ? [{ name, scheme }] : [];
69
- })
70
- : [];
82
+ const rootStyle = (config.style || {}) as JxStyle;
83
+ const contexts = listTokenContexts(getEffectiveMedia(config.$media));
84
+ const hasScheme = contexts.some((c) => c.kind === "scheme");
71
85
 
72
86
  const save = () => {
73
87
  void updateSiteConfig({ style: { ...rootStyle } });
74
- // Live canvas feedback: replace the site-style sheet in place — no re-render needed.
75
- postSiteStyleToLiveHosts();
88
+ pushProjectStylesToCanvas();
76
89
  };
77
90
 
78
- const updateVar = (name: string, val: string) => {
79
- rootStyle[name] = val;
80
- save();
81
- };
82
-
83
- // Write/clear a per-scheme token override in the scheme's `@--name` block (spec §9.5). An empty
84
- // Value clears the override (and drops the block when it empties) — the token inherits the base.
85
- const updateSchemeVar = (schemeName: string, varName: string, val: string) => {
86
- const key = `@${schemeName}`;
87
- if (val) {
88
- const block = (rootStyle[key] ??= {}) as Record<string, unknown>;
89
- block[varName] = val;
90
- } else {
91
- const block = rootStyle[key];
92
- if (block && typeof block === "object") {
93
- delete (block as Record<string, unknown>)[varName];
94
- if (Object.keys(block).length === 0) {
95
- delete rootStyle[key];
96
- }
97
- }
98
- }
91
+ const commit = () => {
99
92
  save();
100
93
  renderCssVarsEditor(container);
101
94
  };
102
95
 
103
- // Without a declared scheme query nothing ever shows the scheme UI (here or in the tab bar) —
104
- // This is the opt-in affordance for existing projects. Re-render after the async config update
105
- // Lands (unlike style edits, $media is not mutated in place).
106
- const enableDarkScheme = () => {
107
- void updateSiteConfig({
108
- $media: { ...config.$media, "--dark": "(prefers-color-scheme: dark)" },
109
- }).then(() => renderCssVarsEditor(container));
110
- };
111
-
112
- const deleteVar = (name: string) => {
113
- delete rootStyle[name];
114
- save();
115
- renderCssVarsEditor(container);
116
- };
96
+ const ctx: TokenFormCtx = { commit, contexts, rootStyle, save };
117
97
 
118
98
  const addVar = (prefix: string, friendlyName: string, val: string) => {
119
99
  const varName = friendlyNameToVar(friendlyName, prefix);
@@ -121,31 +101,35 @@ export function renderCssVarsEditor(container: HTMLElement) {
121
101
  return;
122
102
  }
123
103
  rootStyle[varName] = val;
124
- save();
125
- renderCssVarsEditor(container);
104
+ commit();
105
+ };
106
+
107
+ /*
108
+ * Without a declared scheme query there is nothing to override, so this section can only point at
109
+ * the place a scheme is DEFINED. It used to define one itself — a button here appended
110
+ * `'--dark': '(prefers-color-scheme: dark)'` to `$media` without ever using the word breakpoint,
111
+ * which made this form the fourth and least discoverable definition site for a map whose other
112
+ * three lived in the wizard, Overview and Properties › Media. The lazy import breaks the
113
+ * css-vars-editor ↔ section-registry cycle.
114
+ */
115
+ const manageContexts = () => {
116
+ void import("./section-registry")
117
+ .then(({ setSettingsSection }) => setSettingsSection("contexts"))
118
+ .catch((error: unknown) => {
119
+ notify.error(`Could not open Settings › Contexts — ${errorMessage(error)}`, {
120
+ source: "Settings",
121
+ });
122
+ });
126
123
  };
127
124
 
128
125
  const tpl = html`
129
126
  <div class="settings-section">
130
127
  <h3 class="settings-section-title">CSS Variables</h3>
131
-
132
- ${renderColorSection(
133
- groups.color,
134
- updateVar,
135
- deleteVar,
136
- addVar,
137
- rootStyle,
138
- schemeEntries,
139
- updateSchemeVar,
140
- enableDarkScheme,
128
+ ${groupTokens(rootStyle).map(({ group, tokens }) =>
129
+ group.id === "other" && tokens.length === 0
130
+ ? nothing
131
+ : renderGroup(group, tokens, ctx, hasScheme, addVar, manageContexts),
141
132
  )}
142
- ${renderFontSection(groups.font, updateVar, deleteVar, addVar)}
143
- ${renderSizeSection(groups.size, updateVar, deleteVar, addVar, rootStyle, mediaNames)}
144
- ${
145
- groups.other.length > 0
146
- ? renderOtherSection(groups.other, updateVar, deleteVar, addVar, rootStyle, mediaNames)
147
- : nothing
148
- }
149
133
  </div>
150
134
  `;
151
135
 
@@ -153,62 +137,42 @@ export function renderCssVarsEditor(container: HTMLElement) {
153
137
  }
154
138
 
155
139
  /**
156
- * @param {[string, string | number][]} vars
157
- * @param {(name: string, val: string) => void} updateVar
158
- * @param {(name: string) => void} deleteVar
140
+ * One group: its heading, its token rows, its add row, and — for Colors with no scheme declared —
141
+ * the sentence that says where a scheme comes from.
142
+ *
143
+ * @param {TokenGroup} group
144
+ * @param {ProjectToken[]} tokens
145
+ * @param {TokenFormCtx} ctx
146
+ * @param {boolean} hasScheme
159
147
  * @param {(prefix: string, friendlyName: string, val: string) => void} addVar
148
+ * @param {() => void} manageContexts
160
149
  */
161
- function renderColorSection(
162
- vars: [string, string | number][],
163
- updateVar: (name: string, val: string) => void,
164
- deleteVar: (name: string) => void,
150
+ function renderGroup(
151
+ group: TokenGroup,
152
+ tokens: ProjectToken[],
153
+ ctx: TokenFormCtx,
154
+ hasScheme: boolean,
165
155
  addVar: (prefix: string, friendlyName: string, val: string) => void,
166
- rootStyle: JxStyle,
167
- schemeEntries: SchemeEntry[],
168
- updateSchemeVar: (schemeName: string, varName: string, val: string) => void,
169
- enableDarkScheme: () => void,
170
- ) {
156
+ manageContexts: () => void,
157
+ ): TemplateResult {
171
158
  return html`
172
159
  <div class="css-vars-group">
173
- <h4 class="css-vars-group-title">Colors</h4>
174
- ${vars.map(
175
- ([name, val]) => html`
176
- <div class="css-var-row">
177
- <div class="css-var-swatch" style="background:${val}">
178
- <input
179
- type="color"
180
- .value=${val && String(val).startsWith("#") ? val : "#3b82f6"}
181
- @input=${(e: Event) => updateVar(name, (e.target as HTMLInputElement).value)}
182
- />
183
- </div>
184
- <span class="css-var-name">${varDisplayName(name, "--color-")}</span>
185
- <sp-textfield
186
- size="s"
187
- .value=${String(val)}
188
- @change=${(e: Event) => updateVar(name, (e.target as HTMLInputElement).value)}
189
- style="flex:1;max-width:160px"
190
- ></sp-textfield>
191
- <sp-action-button quiet size="s" @click=${() => deleteVar(name)}>
192
- <sp-icon-delete slot="icon"></sp-icon-delete>
193
- </sp-action-button>
194
- </div>
195
- ${schemeEntries.map((entry) =>
196
- renderSchemeOverride(name, rootStyle, entry, updateSchemeVar),
197
- )}
198
- `,
199
- )}
200
- ${renderAddRow("--color-", "Primary Blue", "#3b82f6", addVar)}
160
+ <h4 class="css-vars-group-title">${group.title}</h4>
161
+ ${tokens.map((token) => renderTokenRow(token, group, ctx))} ${renderAddRow(group, addVar)}
201
162
  ${
202
- schemeEntries.length === 0
163
+ group.id === "color" && !hasScheme
203
164
  ? html`
204
- <sp-action-button
205
- size="s"
206
- class="css-vars-enable-dark"
207
- title="Add a dark color scheme: declares --dark in $media so every color token can carry a dark value"
208
- @click=${enableDarkScheme}
209
- >
210
- Enable dark scheme
211
- </sp-action-button>
165
+ <p class="settings-field-desc">
166
+ No colour scheme is defined yet, so these tokens have one value each.
167
+ <sp-action-button
168
+ size="s"
169
+ quiet
170
+ title="Define a colour scheme in Project Settings › Contexts"
171
+ @click=${manageContexts}
172
+ >
173
+ Manage contexts…
174
+ </sp-action-button>
175
+ </p>
212
176
  `
213
177
  : nothing
214
178
  }
@@ -217,241 +181,234 @@ function renderColorSection(
217
181
  }
218
182
 
219
183
  /**
220
- * One per-scheme override row under a color token: swatch + field bound to the scheme block's
221
- * value. Empty shows "inherits" (the base value applies); clearing deletes the override.
184
+ * One token: its swatch (colours only), its name, its value field, the chip it wears when that
185
+ * value follows another token, its delete button — then its font preview and its overrides.
222
186
  *
223
- * @param {string} varName
224
- * @param {JxStyle} rootStyle
225
- * @param {SchemeEntry} entry
226
- * @param {(schemeName: string, varName: string, val: string) => void} updateSchemeVar
187
+ * @param {ProjectToken} token
188
+ * @param {TokenGroup} group
189
+ * @param {TokenFormCtx} ctx
227
190
  */
228
- function renderSchemeOverride(
229
- varName: string,
230
- rootStyle: JxStyle,
231
- entry: SchemeEntry,
232
- updateSchemeVar: (schemeName: string, varName: string, val: string) => void,
233
- ) {
234
- const block = rootStyle[`@${entry.name}`];
235
- const current =
236
- block && typeof block === "object" ? (block as Record<string, unknown>)[varName] : undefined;
237
- const label = entry.scheme === "dark" ? "Dark" : "Light";
191
+ function renderTokenRow(token: ProjectToken, group: TokenGroup, ctx: TokenFormCtx): TemplateResult {
192
+ const { rootStyle } = ctx;
193
+ const isColor = group.id === "color";
194
+ const refName = tokenRefName(token.value);
195
+ const resolved = resolveTokenValue(rootStyle, token.value);
196
+
197
+ const updateVar = (val: string) => {
198
+ rootStyle[token.name] = val;
199
+ ctx.save();
200
+ };
201
+ const deleteVar = () => {
202
+ delete rootStyle[token.name];
203
+ ctx.commit();
204
+ };
205
+
238
206
  return html`
239
- <div class="css-var-media-row css-var-scheme-row">
240
- <span class="css-var-media-label">${label}</span>
241
- <div class="css-var-swatch" style="background:${current ?? "transparent"}">
242
- <input
243
- type="color"
244
- .value=${current && String(current).startsWith("#") ? String(current) : "#111111"}
245
- @input=${(e: Event) =>
246
- updateSchemeVar(entry.name, varName, (e.target as HTMLInputElement).value)}
247
- />
248
- </div>
207
+ <div class="css-var-row">
208
+ ${isColor ? renderSwatch(resolved, COLOR_INPUT_FALLBACK, updateVar) : nothing}
209
+ <span class="css-var-name">${token.label}</span>
249
210
  <sp-textfield
250
211
  size="s"
251
- placeholder="inherits"
252
- .value=${current == null ? "" : String(current)}
253
- @change=${(e: Event) =>
254
- updateSchemeVar(entry.name, varName, (e.target as HTMLInputElement).value)}
255
- style="flex:1;max-width:160px"
212
+ .value=${String(token.value)}
213
+ @change=${(e: Event) => updateVar((e.target as HTMLInputElement).value)}
214
+ style=${valueFieldStyle(group)}
256
215
  ></sp-textfield>
216
+ ${refName ? renderTokenChip(refName, resolved, { swatch: isColor }) : nothing}
217
+ <sp-action-button quiet size="s" title="Delete ${token.label}" @click=${deleteVar}>
218
+ <sp-icon-delete slot="icon"></sp-icon-delete>
219
+ </sp-action-button>
257
220
  </div>
221
+ ${
222
+ group.id === "font"
223
+ ? html`<div class="css-var-font-preview" style="font-family:${String(token.value)}">
224
+ The quick brown fox jumps over the lazy dog
225
+ </div>`
226
+ : nothing
227
+ }
228
+ ${renderOverrides(token, group, ctx)}
258
229
  `;
259
230
  }
260
231
 
261
232
  /**
262
- * @param {[string, string | number][]} vars
263
- * @param {(name: string, val: string) => void} updateVar
264
- * @param {(name: string) => void} deleteVar
265
- * @param {(prefix: string, friendlyName: string, val: string) => void} addVar
233
+ * How wide the value field is — colours and sizes are short values beside a swatch or a unit, fonts
234
+ * and free-form tokens are long ones. The predecessor said the same thing four times.
235
+ *
236
+ * @param {TokenGroup} group
266
237
  */
267
- function renderFontSection(
268
- vars: [string, string | number][],
269
- updateVar: (name: string, val: string) => void,
270
- deleteVar: (name: string) => void,
271
- addVar: (prefix: string, friendlyName: string, val: string) => void,
272
- ) {
273
- return html`
274
- <div class="css-vars-group">
275
- <h4 class="css-vars-group-title">Fonts</h4>
276
- ${vars.map(
277
- ([name, val]) => html`
278
- <div class="css-var-row">
279
- <span class="css-var-name">${varDisplayName(name, "--font-")}</span>
280
- <sp-textfield
281
- size="s"
282
- .value=${String(val)}
283
- @change=${(e: Event) => updateVar(name, (e.target as HTMLInputElement).value)}
284
- style="flex:1"
285
- ></sp-textfield>
286
- <sp-action-button quiet size="s" @click=${() => deleteVar(name)}>
287
- <sp-icon-delete slot="icon"></sp-icon-delete>
288
- </sp-action-button>
289
- </div>
290
- <div class="css-var-font-preview" style="font-family:${val}">
291
- The quick brown fox jumps over the lazy dog
292
- </div>
293
- `,
294
- )}
295
- ${renderAddRow("--font-", "Body Serif", "'Georgia', serif", addVar)}
296
- </div>
297
- `;
238
+ function valueFieldStyle(group: TokenGroup): string {
239
+ if (group.id === "color") {
240
+ return "flex:1;max-width:160px";
241
+ }
242
+ if (group.id === "size") {
243
+ return "max-width:120px";
244
+ }
245
+ return "flex:1";
298
246
  }
299
247
 
300
248
  /**
301
- * @param {[string, string | number][]} vars
302
- * @param {(name: string, val: string) => void} updateVar
303
- * @param {(name: string) => void} deleteVar
304
- * @param {(prefix: string, friendlyName: string, val: string) => void} addVar
305
- * @param {JxStyle} rootStyle
306
- * @param {string[]} mediaNames
249
+ * The colour well: a background showing the value the token RESOLVES to (so an alias token is not a
250
+ * blank square) over a native colour input.
251
+ *
252
+ * @param {string | number | undefined} resolved
253
+ * @param {string} fallback — what the native input shows when the value is not a hex literal
254
+ * @param {(val: string) => void} onInput
307
255
  */
308
- function renderSizeSection(
309
- vars: [string, string | number][],
310
- updateVar: (name: string, val: string) => void,
311
- deleteVar: (name: string) => void,
312
- addVar: (prefix: string, friendlyName: string, val: string) => void,
313
- rootStyle: JxStyle,
314
- mediaNames: string[],
315
- ) {
256
+ function renderSwatch(
257
+ resolved: string | number | undefined,
258
+ fallback: string,
259
+ onInput: (val: string) => void,
260
+ ): TemplateResult {
261
+ const shown = resolved === undefined ? "transparent" : String(resolved);
316
262
  return html`
317
- <div class="css-vars-group">
318
- <h4 class="css-vars-group-title">Sizes &amp; Spacing</h4>
319
- ${vars.map(
320
- ([name, val]) => html`
321
- <div class="css-var-row">
322
- <span class="css-var-name"
323
- >${
324
- varDisplayName(name, "--size-") ||
325
- varDisplayName(name, "--spacing-") ||
326
- varDisplayName(name, "--radius-") ||
327
- name
328
- }</span
329
- >
330
- <sp-textfield
331
- size="s"
332
- .value=${String(val)}
333
- @change=${(e: Event) => updateVar(name, (e.target as HTMLInputElement).value)}
334
- style="max-width:120px"
335
- ></sp-textfield>
336
- <sp-action-button quiet size="s" @click=${() => deleteVar(name)}>
337
- <sp-icon-delete slot="icon"></sp-icon-delete>
338
- </sp-action-button>
339
- </div>
340
- ${mediaNames.length > 0 ? renderMediaOverrides(name, rootStyle, mediaNames) : nothing}
341
- `,
342
- )}
343
- ${renderAddRow("--size-", "Spacing Large", "32px", addVar)}
263
+ <div class="css-var-swatch" style="background:${shown}">
264
+ <input
265
+ type="color"
266
+ .value=${shown.startsWith("#") ? shown : fallback}
267
+ @input=${(e: Event) => onInput((e.target as HTMLInputElement).value)}
268
+ />
344
269
  </div>
345
270
  `;
346
271
  }
347
272
 
348
273
  /**
349
- * @param {[string, string | number][]} vars
350
- * @param {(name: string, val: string) => void} updateVar
351
- * @param {(name: string) => void} deleteVar
352
- * @param {(prefix: string, friendlyName: string, val: string) => void} addVar
353
- * @param {JxStyle} rootStyle
354
- * @param {string[]} mediaNames
274
+ * A token's per-context values: one row per context it is overridden in, plus — for a colour, once
275
+ * a scheme exists — a row per scheme whether or not it carries a value, because "what is this
276
+ * colour in dark mode" is a question a palette is always answering. Then the add affordance.
277
+ *
278
+ * The add affordance is the change. Before it, a row appeared only for a token that already had an
279
+ * `@media` block, so the first override for any token could only be written by hand in
280
+ * `project.json` — the form could edit an override it could not create.
281
+ *
282
+ * @param {ProjectToken} token
283
+ * @param {TokenGroup} group
284
+ * @param {TokenFormCtx} ctx
355
285
  */
356
- function renderOtherSection(
357
- vars: [string, string | number][],
358
- updateVar: (name: string, val: string) => void,
359
- deleteVar: (name: string) => void,
360
- addVar: (prefix: string, friendlyName: string, val: string) => void,
361
- rootStyle: JxStyle,
362
- mediaNames: string[],
363
- ) {
286
+ function renderOverrides(
287
+ token: ProjectToken,
288
+ group: TokenGroup,
289
+ ctx: TokenFormCtx,
290
+ ): TemplateResult | typeof nothing {
291
+ const { contexts, rootStyle } = ctx;
292
+ const isColor = group.id === "color";
293
+ const shown = contexts.filter(
294
+ (context) =>
295
+ (isColor && context.kind === "scheme") ||
296
+ readTokenOverride(rootStyle, context, token.name) !== undefined,
297
+ );
298
+ /*
299
+ * A token with no value has nothing to give a context, so the picker is not offered rather than
300
+ * offered and silently inert — writing an empty override is how one is CLEARED.
301
+ */
302
+ const addable =
303
+ String(token.value) === "" ? [] : addableContexts(rootStyle, contexts, token.name, shown);
304
+
305
+ if (shown.length === 0 && addable.length === 0) {
306
+ return nothing;
307
+ }
308
+
309
+ const setOverride = (context: TokenContext, val: string) => {
310
+ writeTokenOverride(rootStyle, context, token.name, val);
311
+ ctx.commit();
312
+ };
313
+
364
314
  return html`
365
- <div class="css-vars-group">
366
- <h4 class="css-vars-group-title">Other</h4>
367
- ${vars.map(
368
- ([name, val]) => html`
369
- <div class="css-var-row">
370
- <span class="css-var-name">${name}</span>
371
- <sp-textfield
372
- size="s"
373
- .value=${String(val)}
374
- @change=${(e: Event) => updateVar(name, (e.target as HTMLInputElement).value)}
375
- style="flex:1"
376
- ></sp-textfield>
377
- <sp-action-button quiet size="s" @click=${() => deleteVar(name)}>
378
- <sp-icon-delete slot="icon"></sp-icon-delete>
379
- </sp-action-button>
380
- </div>
381
- ${mediaNames.length > 0 ? renderMediaOverrides(name, rootStyle, mediaNames) : nothing}
382
- `,
383
- )}
384
- ${renderAddRow("--", "Custom Var", "value", addVar)}
315
+ <div class="css-var-media-overrides">
316
+ ${shown.map((context) => renderOverrideRow(token, context, isColor, rootStyle, setOverride))}
317
+ ${
318
+ addable.length === 0
319
+ ? nothing
320
+ : html`
321
+ <div class="css-var-override-add">
322
+ <sp-picker
323
+ size="s"
324
+ quiet
325
+ label="Add override"
326
+ placeholder="Add override…"
327
+ title="Give ${token.label} a different value in one rendering context"
328
+ @change=${(e: Event) => {
329
+ const name = (e.target as HTMLInputElement).value;
330
+ const context = addable.find((c) => c.name === name);
331
+ if (context) {
332
+ setOverride(context, String(token.value));
333
+ }
334
+ }}
335
+ >
336
+ ${addable.map(
337
+ (context) =>
338
+ html`<sp-menu-item value=${context.name}>${context.label}</sp-menu-item>`,
339
+ )}
340
+ </sp-picker>
341
+ </div>
342
+ `
343
+ }
385
344
  </div>
386
345
  `;
387
346
  }
388
347
 
389
348
  /**
390
- * @param {string} varName
349
+ * One override row. Empty means "inherits the base value", and clearing a row removes the override
350
+ * (and the block, once it empties) — one write path for a scheme and a breakpoint alike.
351
+ *
352
+ * @param {ProjectToken} token
353
+ * @param {TokenContext} context
354
+ * @param {boolean} isColor
391
355
  * @param {JxStyle} rootStyle
392
- * @param {string[]} mediaNames
356
+ * @param {(context: TokenContext, val: string) => void} setOverride
393
357
  */
394
- function renderMediaOverrides(varName: string, rootStyle: JxStyle, mediaNames: string[]) {
395
- const overrides = [];
396
- for (const mediaName of mediaNames) {
397
- const mediaKey = `@${mediaName}`;
398
- const mediaBlock = rootStyle[mediaKey];
399
- if (mediaBlock && typeof mediaBlock === "object" && mediaBlock[varName]) {
400
- overrides.push({ mediaName, value: mediaBlock[varName] });
401
- }
402
- }
403
-
404
- if (overrides.length === 0) {
405
- return nothing;
406
- }
407
-
358
+ function renderOverrideRow(
359
+ token: ProjectToken,
360
+ context: TokenContext,
361
+ isColor: boolean,
362
+ rootStyle: JxStyle,
363
+ setOverride: (context: TokenContext, val: string) => void,
364
+ ): TemplateResult {
365
+ const current = readTokenOverride(rootStyle, context, token.name);
408
366
  return html`
409
- <div class="css-var-media-overrides">
410
- ${overrides.map(
411
- (o) => html`
412
- <div class="css-var-media-row">
413
- <span class="css-var-media-label">@${o.mediaName}</span>
414
- <sp-textfield
415
- size="s"
416
- .value=${String(o.value)}
417
- @change=${(e: Event) => {
418
- if (!rootStyle[`@${o.mediaName}`]) {
419
- rootStyle[`@${o.mediaName}`] = {};
420
- }
421
- (rootStyle[`@${o.mediaName}`] as Record<string, unknown>)[varName] = (
422
- e.target as HTMLInputElement
423
- ).value;
424
- void updateSiteConfig({ style: { ...rootStyle } });
425
- }}
426
- style="max-width:120px"
427
- ></sp-textfield>
428
- </div>
429
- `,
430
- )}
367
+ <div
368
+ class=${classMap({
369
+ "css-var-media-row": true,
370
+ "css-var-scheme-row": context.kind === "scheme",
371
+ })}
372
+ >
373
+ <span class="css-var-media-label">${context.label}</span>
374
+ ${
375
+ isColor
376
+ ? renderSwatch(resolveTokenValue(rootStyle, current), OVERRIDE_INPUT_FALLBACK, (val) =>
377
+ setOverride(context, val),
378
+ )
379
+ : nothing
380
+ }
381
+ <sp-textfield
382
+ size="s"
383
+ placeholder="inherits"
384
+ .value=${current === undefined ? "" : String(current)}
385
+ @change=${(e: Event) => setOverride(context, (e.target as HTMLInputElement).value)}
386
+ style=${isColor ? "flex:1;max-width:160px" : "max-width:120px"}
387
+ ></sp-textfield>
431
388
  </div>
432
389
  `;
433
390
  }
434
391
 
435
392
  /**
436
- * @param {string} prefix
437
- * @param {string} placeholder
438
- * @param {string} valuePlaceholder
393
+ * The group's "add a token" row: a friendly name, a value, and the button that slugs the one into a
394
+ * variable name under the group's prefix.
395
+ *
396
+ * @param {TokenGroup} group
439
397
  * @param {(prefix: string, friendlyName: string, val: string) => void} addVar
440
398
  */
441
399
  function renderAddRow(
442
- prefix: string,
443
- placeholder: string,
444
- valuePlaceholder: string,
400
+ group: TokenGroup,
445
401
  addVar: (prefix: string, friendlyName: string, val: string) => void,
446
- ) {
402
+ ): TemplateResult {
447
403
  let nameEl: HTMLInputElement | null = null;
448
404
  let valEl: HTMLInputElement | null = null;
405
+ const hint = ADD_ROW_HINTS[group.id];
449
406
 
450
407
  return html`
451
408
  <div class="css-var-add-row">
452
409
  <sp-textfield
453
410
  size="s"
454
- placeholder=${placeholder}
411
+ placeholder=${hint.name}
455
412
  ${ref((el) => {
456
413
  if (el) {
457
414
  nameEl = el as HTMLInputElement;
@@ -460,7 +417,7 @@ function renderAddRow(
460
417
  ></sp-textfield>
461
418
  <sp-textfield
462
419
  size="s"
463
- placeholder=${valuePlaceholder}
420
+ placeholder=${hint.value}
464
421
  ${ref((el) => {
465
422
  if (el) {
466
423
  valEl = el as HTMLInputElement;
@@ -471,7 +428,7 @@ function renderAddRow(
471
428
  size="s"
472
429
  @click=${() => {
473
430
  if (nameEl && valEl) {
474
- addVar(prefix, nameEl.value, valEl.value);
431
+ addVar(group.prefix, nameEl.value, valEl.value);
475
432
  }
476
433
  }}
477
434
  >Add</sp-action-button