@bettercms-ai/kit 1.0.0 → 2.0.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 (587) hide show
  1. package/README.md +33 -0
  2. package/dist/adapters/dock-values.d.ts +28 -0
  3. package/dist/adapters/dock-values.js +41 -0
  4. package/dist/adapters/dock-values.js.map +1 -0
  5. package/{adapters → dist/adapters}/icons.js +2 -2
  6. package/dist/adapters/icons.js.map +1 -0
  7. package/{adapters → dist/adapters}/lossy.d.ts +90 -31
  8. package/dist/adapters/lossy.js +414 -0
  9. package/dist/adapters/lossy.js.map +1 -0
  10. package/{adapters → dist/adapters}/prod-types.d.ts +82 -4
  11. package/{adapters → dist/adapters}/prod-types.js +71 -3
  12. package/dist/adapters/prod-types.js.map +1 -0
  13. package/{adapters → dist/adapters}/schema.d.ts +40 -1
  14. package/{adapters → dist/adapters}/schema.js +996 -179
  15. package/dist/adapters/schema.js.map +1 -0
  16. package/{adapters → dist/adapters}/values.d.ts +26 -5
  17. package/{adapters → dist/adapters}/values.js +87 -17
  18. package/dist/adapters/values.js.map +1 -0
  19. package/dist/brand/logo.js +96 -0
  20. package/dist/brand/logo.js.map +1 -0
  21. package/{context.d.ts → dist/context.d.ts} +92 -2
  22. package/{context.js → dist/context.js} +5 -2
  23. package/dist/context.js.map +1 -0
  24. package/{controls → dist/controls}/button.d.ts +19 -5
  25. package/{controls → dist/controls}/button.js +125 -54
  26. package/dist/controls/button.js.map +1 -0
  27. package/{controls → dist/controls}/c1.js +1 -1
  28. package/dist/controls/c1.js.map +1 -0
  29. package/{controls → dist/controls}/c2.js +1 -1
  30. package/dist/controls/c2.js.map +1 -0
  31. package/{controls → dist/controls}/choice.d.ts +7 -2
  32. package/{controls → dist/controls}/choice.js +71 -18
  33. package/dist/controls/choice.js.map +1 -0
  34. package/{controls → dist/controls}/code-register.js +1 -1
  35. package/dist/controls/code-register.js.map +1 -0
  36. package/{controls → dist/controls}/code.js +10 -9
  37. package/dist/controls/code.js.map +1 -0
  38. package/{controls → dist/controls}/color.js +8 -8
  39. package/dist/controls/color.js.map +1 -0
  40. package/{controls → dist/controls}/component-services.d.ts +42 -0
  41. package/dist/controls/component-services.js +7 -0
  42. package/dist/controls/component-services.js.map +1 -0
  43. package/{controls → dist/controls}/date-register.js +1 -1
  44. package/dist/controls/date-register.js.map +1 -0
  45. package/{controls → dist/controls}/date-zone.js +8 -2
  46. package/dist/controls/date-zone.js.map +1 -0
  47. package/{controls → dist/controls}/date.js +32 -24
  48. package/dist/controls/date.js.map +1 -0
  49. package/{controls → dist/controls}/form-picker-register.js +1 -1
  50. package/dist/controls/form-picker-register.js.map +1 -0
  51. package/{controls → dist/controls}/form-picker.js +11 -9
  52. package/dist/controls/form-picker.js.map +1 -0
  53. package/{controls → dist/controls}/icon-register.js +1 -1
  54. package/dist/controls/icon-register.js.map +1 -0
  55. package/{controls → dist/controls}/icon.d.ts +4 -2
  56. package/{controls → dist/controls}/icon.js +32 -27
  57. package/dist/controls/icon.js.map +1 -0
  58. package/{controls → dist/controls}/index.d.ts +1 -1
  59. package/{controls → dist/controls}/index.js +4 -3
  60. package/dist/controls/index.js.map +1 -0
  61. package/{controls → dist/controls}/json-register.js +1 -1
  62. package/dist/controls/json-register.js.map +1 -0
  63. package/{controls → dist/controls}/json.js +13 -3
  64. package/dist/controls/json.js.map +1 -0
  65. package/{controls → dist/controls}/link.js +6 -6
  66. package/dist/controls/link.js.map +1 -0
  67. package/{controls → dist/controls}/location-register.js +1 -1
  68. package/dist/controls/location-register.js.map +1 -0
  69. package/{controls → dist/controls}/location.js +64 -40
  70. package/dist/controls/location.js.map +1 -0
  71. package/{controls → dist/controls}/media-register.js +1 -1
  72. package/dist/controls/media-register.js.map +1 -0
  73. package/{controls → dist/controls}/media-services.d.ts +6 -0
  74. package/dist/controls/media-services.js +0 -0
  75. package/{controls → dist/controls}/media.d.ts +35 -4
  76. package/{controls → dist/controls}/media.js +491 -171
  77. package/dist/controls/media.js.map +1 -0
  78. package/{controls → dist/controls}/number.js +10 -9
  79. package/dist/controls/number.js.map +1 -0
  80. package/{controls → dist/controls}/reference-register.js +1 -1
  81. package/dist/controls/reference-register.js.map +1 -0
  82. package/{controls → dist/controls}/reference-services.d.ts +4 -0
  83. package/dist/controls/reference-services.js +0 -0
  84. package/{controls → dist/controls}/reference.d.ts +1 -1
  85. package/{controls → dist/controls}/reference.js +93 -77
  86. package/dist/controls/reference.js.map +1 -0
  87. package/dist/controls/rich-text/blocks.d.ts +49 -0
  88. package/{controls → dist/controls}/rich-text/blocks.js +471 -144
  89. package/dist/controls/rich-text/blocks.js.map +1 -0
  90. package/{controls → dist/controls}/rich-text/catalog.d.ts +4 -1
  91. package/{controls → dist/controls}/rich-text/catalog.js +75 -65
  92. package/dist/controls/rich-text/catalog.js.map +1 -0
  93. package/{controls → dist/controls}/rich-text/codec.d.ts +9 -1
  94. package/{controls → dist/controls}/rich-text/codec.js +3 -1
  95. package/dist/controls/rich-text/codec.js.map +1 -0
  96. package/{controls → dist/controls}/rich-text/component-card.d.ts +1 -1
  97. package/{controls → dist/controls}/rich-text/component-card.js +417 -99
  98. package/dist/controls/rich-text/component-card.js.map +1 -0
  99. package/dist/controls/rich-text/component-panel.d.ts +47 -0
  100. package/dist/controls/rich-text/component-panel.js +141 -0
  101. package/dist/controls/rich-text/component-panel.js.map +1 -0
  102. package/{controls → dist/controls}/rich-text/control.d.ts +1 -2
  103. package/{controls → dist/controls}/rich-text/control.js +84 -42
  104. package/dist/controls/rich-text/control.js.map +1 -0
  105. package/{controls → dist/controls}/rich-text/dom.d.ts +52 -9
  106. package/{controls → dist/controls}/rich-text/dom.js +379 -50
  107. package/dist/controls/rich-text/dom.js.map +1 -0
  108. package/{controls → dist/controls}/rich-text/editor.d.ts +7 -0
  109. package/{controls → dist/controls}/rich-text/editor.js +148 -48
  110. package/dist/controls/rich-text/editor.js.map +1 -0
  111. package/{controls → dist/controls}/rich-text/floating.d.ts +6 -1
  112. package/{controls → dist/controls}/rich-text/floating.js +35 -18
  113. package/dist/controls/rich-text/floating.js.map +1 -0
  114. package/{controls → dist/controls}/rich-text/index.js +1 -1
  115. package/dist/controls/rich-text/index.js.map +1 -0
  116. package/dist/controls/rich-text/link-form.js +147 -0
  117. package/dist/controls/rich-text/link-form.js.map +1 -0
  118. package/{controls → dist/controls}/rich-text/markdown.js +4 -2
  119. package/dist/controls/rich-text/markdown.js.map +1 -0
  120. package/{controls → dist/controls}/rich-text/ops.js +17 -15
  121. package/dist/controls/rich-text/ops.js.map +1 -0
  122. package/dist/controls/rich-text/plain.d.ts +17 -0
  123. package/dist/controls/rich-text/plain.js +44 -0
  124. package/dist/controls/rich-text/plain.js.map +1 -0
  125. package/{controls → dist/controls}/rich-text/toolbar.d.ts +6 -2
  126. package/{controls → dist/controls}/rich-text/toolbar.js +12 -7
  127. package/dist/controls/rich-text/toolbar.js.map +1 -0
  128. package/{controls → dist/controls}/scale.js +5 -5
  129. package/dist/controls/scale.js.map +1 -0
  130. package/{controls → dist/controls}/slot-register.js +1 -1
  131. package/dist/controls/slot-register.js.map +1 -0
  132. package/{controls → dist/controls}/slot-services.d.ts +25 -0
  133. package/dist/controls/slot-services.js +155 -0
  134. package/dist/controls/slot-services.js.map +1 -0
  135. package/{controls → dist/controls}/slot.js +42 -24
  136. package/dist/controls/slot.js.map +1 -0
  137. package/{controls → dist/controls}/table/index.js +1 -1
  138. package/dist/controls/table/index.js.map +1 -0
  139. package/{controls → dist/controls}/table/prod.js +3 -3
  140. package/dist/controls/table/prod.js.map +1 -0
  141. package/{controls → dist/controls}/table/refs.js +13 -2
  142. package/dist/controls/table/refs.js.map +1 -0
  143. package/{controls → dist/controls}/table/stored.js +1 -1
  144. package/dist/controls/table/stored.js.map +1 -0
  145. package/{controls → dist/controls}/table/table-editor.js +51 -48
  146. package/dist/controls/table/table-editor.js.map +1 -0
  147. package/{controls → dist/controls}/table/table-link.js +3 -3
  148. package/dist/controls/table/table-link.js.map +1 -0
  149. package/{controls → dist/controls}/table/table.js +3 -3
  150. package/dist/controls/table/table.js.map +1 -0
  151. package/{controls → dist/controls}/text.d.ts +2 -2
  152. package/{controls → dist/controls}/text.js +78 -43
  153. package/dist/controls/text.js.map +1 -0
  154. package/dist/controls/unknown-value.d.ts +17 -0
  155. package/dist/controls/unknown-value.js +66 -0
  156. package/dist/controls/unknown-value.js.map +1 -0
  157. package/dist/core/core.d.ts +63 -0
  158. package/dist/core/core.js +171 -0
  159. package/dist/core/core.js.map +1 -0
  160. package/dist/core/facets.d.ts +347 -0
  161. package/dist/core/facets.js +334 -0
  162. package/dist/core/facets.js.map +1 -0
  163. package/dist/core/index.d.ts +3 -0
  164. package/dist/core/index.js +3 -0
  165. package/{engine → dist/engine}/array.js +34 -21
  166. package/dist/engine/array.js.map +1 -0
  167. package/{engine → dist/engine}/bits.js +1 -1
  168. package/dist/engine/bits.js.map +1 -0
  169. package/{engine → dist/engine}/breadcrumb.d.ts +3 -3
  170. package/{engine → dist/engine}/breadcrumb.js +12 -36
  171. package/dist/engine/breadcrumb.js.map +1 -0
  172. package/{engine → dist/engine}/expand.d.ts +2 -4
  173. package/{engine → dist/engine}/expand.js +5 -22
  174. package/dist/engine/expand.js.map +1 -0
  175. package/{engine → dist/engine}/field-list.js +1 -1
  176. package/dist/engine/field-list.js.map +1 -0
  177. package/{engine → dist/engine}/field-shell.js +249 -121
  178. package/dist/engine/field-shell.js.map +1 -0
  179. package/{engine → dist/engine}/frame.d.ts +3 -1
  180. package/{engine → dist/engine}/frame.js +16 -14
  181. package/dist/engine/frame.js.map +1 -0
  182. package/{engine → dist/engine}/group-summary.d.ts +2 -1
  183. package/{engine → dist/engine}/group-summary.js +7 -3
  184. package/dist/engine/group-summary.js.map +1 -0
  185. package/{engine → dist/engine}/grouping.d.ts +19 -5
  186. package/{engine → dist/engine}/grouping.js +193 -21
  187. package/dist/engine/grouping.js.map +1 -0
  188. package/{engine → dist/engine}/item-list.d.ts +11 -1
  189. package/{engine → dist/engine}/item-list.js +35 -13
  190. package/dist/engine/item-list.js.map +1 -0
  191. package/{engine → dist/engine}/kit-field.js +45 -10
  192. package/dist/engine/kit-field.js.map +1 -0
  193. package/{engine → dist/engine}/schema-form.d.ts +27 -2
  194. package/{engine → dist/engine}/schema-form.js +182 -40
  195. package/dist/engine/schema-form.js.map +1 -0
  196. package/{engine → dist/engine}/slot.js +5 -15
  197. package/dist/engine/slot.js.map +1 -0
  198. package/dist/index.js +47 -0
  199. package/dist/index.js.map +1 -0
  200. package/{kit.css → dist/kit.css} +19 -0
  201. package/{lib → dist/lib}/cn.js +7 -4
  202. package/dist/lib/cn.js.map +1 -0
  203. package/{lib → dist/lib}/columns.js +4 -1
  204. package/dist/lib/columns.js.map +1 -0
  205. package/{lib → dist/lib}/frames.js +2 -2
  206. package/dist/lib/frames.js.map +1 -0
  207. package/dist/lib/icon-image.d.ts +10 -0
  208. package/dist/lib/icon-image.js +8 -0
  209. package/dist/lib/icon-image.js.map +1 -0
  210. package/{lib → dist/lib}/icons.js +4 -3
  211. package/dist/lib/icons.js.map +1 -0
  212. package/dist/lib/input-modality.d.ts +11 -0
  213. package/dist/lib/input-modality.js +47 -0
  214. package/dist/lib/input-modality.js.map +1 -0
  215. package/dist/lib/key-event.d.ts +11 -0
  216. package/dist/lib/key-event.js +16 -0
  217. package/dist/lib/key-event.js.map +1 -0
  218. package/{lib → dist/lib}/path.js +4 -2
  219. package/dist/lib/path.js.map +1 -0
  220. package/{lib → dist/lib}/schema.d.ts +34 -3
  221. package/{lib → dist/lib}/schema.js +110 -13
  222. package/dist/lib/schema.js.map +1 -0
  223. package/{lib → dist/lib}/utils.js +1 -1
  224. package/dist/lib/utils.js.map +1 -0
  225. package/{lib → dist/lib}/validate.d.ts +11 -1
  226. package/{lib → dist/lib}/validate.js +85 -11
  227. package/dist/lib/validate.js.map +1 -0
  228. package/dist/library/analysis.d.ts +23 -0
  229. package/dist/library/analysis.js +160 -0
  230. package/dist/library/analysis.js.map +1 -0
  231. package/dist/library/catalog.d.ts +57 -0
  232. package/dist/library/catalog.js +307 -0
  233. package/dist/library/catalog.js.map +1 -0
  234. package/dist/library/coreNotes.d.ts +8 -0
  235. package/dist/library/coreNotes.js +111 -0
  236. package/dist/library/coreNotes.js.map +1 -0
  237. package/dist/library/coverage.d.ts +40 -0
  238. package/dist/library/coverage.js +59 -0
  239. package/dist/library/coverage.js.map +1 -0
  240. package/dist/library/entries/advanced.entries.d.ts +2 -0
  241. package/dist/library/entries/advanced.entries.js +6579 -0
  242. package/dist/library/entries/advanced.entries.js.map +1 -0
  243. package/dist/library/entries/commerce.entries.d.ts +2 -0
  244. package/dist/library/entries/commerce.entries.js +4140 -0
  245. package/dist/library/entries/commerce.entries.js.map +1 -0
  246. package/dist/library/entries/company.entries.d.ts +2 -0
  247. package/dist/library/entries/company.entries.js +2731 -0
  248. package/dist/library/entries/company.entries.js.map +1 -0
  249. package/dist/library/entries/conversion.entries.d.ts +2 -0
  250. package/dist/library/entries/conversion.entries.js +3707 -0
  251. package/dist/library/entries/conversion.entries.js.map +1 -0
  252. package/dist/library/entries/data.entries.d.ts +7 -0
  253. package/dist/library/entries/data.entries.js +4613 -0
  254. package/dist/library/entries/data.entries.js.map +1 -0
  255. package/dist/library/entries/events.entries.d.ts +2 -0
  256. package/dist/library/entries/events.entries.js +3428 -0
  257. package/dist/library/entries/events.entries.js.map +1 -0
  258. package/dist/library/entries/features.entries.d.ts +2 -0
  259. package/dist/library/entries/features.entries.js +3157 -0
  260. package/dist/library/entries/features.entries.js.map +1 -0
  261. package/dist/library/entries/heroes.entries.d.ts +2 -0
  262. package/dist/library/entries/heroes.entries.js +3401 -0
  263. package/dist/library/entries/heroes.entries.js.map +1 -0
  264. package/dist/library/entries/index.d.ts +3 -0
  265. package/dist/library/entries/index.js +35 -0
  266. package/dist/library/entries/index.js.map +1 -0
  267. package/dist/library/entries/listings.entries.d.ts +2 -0
  268. package/dist/library/entries/listings.entries.js +2976 -0
  269. package/dist/library/entries/listings.entries.js.map +1 -0
  270. package/dist/library/entries/longform.entries.d.ts +2 -0
  271. package/dist/library/entries/longform.entries.js +4227 -0
  272. package/dist/library/entries/longform.entries.js.map +1 -0
  273. package/dist/library/entries/media.entries.d.ts +2 -0
  274. package/dist/library/entries/media.entries.js +2781 -0
  275. package/dist/library/entries/media.entries.js.map +1 -0
  276. package/dist/library/entries/navigation.entries.d.ts +2 -0
  277. package/dist/library/entries/navigation.entries.js +4011 -0
  278. package/dist/library/entries/navigation.entries.js.map +1 -0
  279. package/dist/library/entries/pricing.entries.d.ts +2 -0
  280. package/dist/library/entries/pricing.entries.js +2383 -0
  281. package/dist/library/entries/pricing.entries.js.map +1 -0
  282. package/dist/library/entries/social-proof.entries.d.ts +2 -0
  283. package/dist/library/entries/social-proof.entries.js +2297 -0
  284. package/dist/library/entries/social-proof.entries.js.map +1 -0
  285. package/dist/library/examples/bento.d.ts +92 -0
  286. package/dist/library/examples/bento.js +201 -0
  287. package/dist/library/examples/bento.js.map +1 -0
  288. package/dist/library/examples/blocks.d.ts +14 -0
  289. package/dist/library/examples/blocks.js +313 -0
  290. package/dist/library/examples/blocks.js.map +1 -0
  291. package/dist/library/examples/faq.d.ts +28 -0
  292. package/dist/library/examples/faq.js +230 -0
  293. package/dist/library/examples/faq.js.map +1 -0
  294. package/dist/library/examples/hero.d.ts +43 -0
  295. package/dist/library/examples/hero.js +222 -0
  296. package/dist/library/examples/hero.js.map +1 -0
  297. package/dist/library/examples/index.d.ts +36 -0
  298. package/dist/library/examples/index.js +109 -0
  299. package/dist/library/examples/index.js.map +1 -0
  300. package/dist/library/examples/journalStory.d.ts +93 -0
  301. package/dist/library/examples/journalStory.js +338 -0
  302. package/dist/library/examples/journalStory.js.map +1 -0
  303. package/dist/library/examples/megaMenu.d.ts +134 -0
  304. package/dist/library/examples/megaMenu.js +442 -0
  305. package/dist/library/examples/megaMenu.js.map +1 -0
  306. package/dist/library/examples/pricing.d.ts +103 -0
  307. package/dist/library/examples/pricing.js +479 -0
  308. package/dist/library/examples/pricing.js.map +1 -0
  309. package/dist/library/examples/tabs.d.ts +87 -0
  310. package/dist/library/examples/tabs.js +180 -0
  311. package/dist/library/examples/tabs.js.map +1 -0
  312. package/dist/library/export.d.ts +42 -0
  313. package/dist/library/export.js +80 -0
  314. package/dist/library/export.js.map +1 -0
  315. package/dist/library/index.d.ts +13 -0
  316. package/dist/library/index.js +6 -0
  317. package/dist/library/kit-catalog.json +1510 -0
  318. package/dist/library/types.d.ts +42 -0
  319. package/dist/library/types.js +0 -0
  320. package/{schema → dist/schema}/adapters.js +1 -2
  321. package/dist/schema/adapters.js.map +1 -0
  322. package/{schema → dist/schema}/help.d.ts +2 -0
  323. package/{schema → dist/schema}/help.js +6 -4
  324. package/dist/schema/help.js.map +1 -0
  325. package/{schema → dist/schema}/scale.js +1 -1
  326. package/dist/schema/scale.js.map +1 -0
  327. package/{schema → dist/schema}/sources.js +1 -1
  328. package/dist/schema/sources.js.map +1 -0
  329. package/{schema → dist/schema}/table.js +11 -10
  330. package/dist/schema/table.js.map +1 -0
  331. package/{schema → dist/schema}/types.d.ts +29 -1
  332. package/{schema → dist/schema}/types.js +1 -1
  333. package/dist/schema/types.js.map +1 -0
  334. package/dist/styles.css +2 -0
  335. package/dist/table/table-view.d.ts +14 -0
  336. package/dist/table/table-view.js +140 -0
  337. package/dist/table/table-view.js.map +1 -0
  338. package/dist/table/table.css +73 -0
  339. package/dist/testing/corpus.d.ts +63 -0
  340. package/dist/testing/corpus.js +129 -0
  341. package/dist/testing/corpus.js.map +1 -0
  342. package/dist/testing/test-form.d.ts +11 -0
  343. package/dist/testing/test-form.js +38 -0
  344. package/dist/testing/test-form.js.map +1 -0
  345. package/dist/theme.css +345 -0
  346. package/dist/theme.d.ts +22 -0
  347. package/dist/theme.js +43 -0
  348. package/dist/theme.js.map +1 -0
  349. package/dist/tokens.css +542 -0
  350. package/dist/types.js +0 -0
  351. package/{ui → dist/ui}/action-tray.d.ts +1 -1
  352. package/{ui → dist/ui}/action-tray.js +20 -19
  353. package/dist/ui/action-tray.js.map +1 -0
  354. package/dist/ui/autocomplete.d.ts +30 -0
  355. package/dist/ui/autocomplete.js +139 -0
  356. package/dist/ui/autocomplete.js.map +1 -0
  357. package/{ui → dist/ui}/button.d.ts +3 -3
  358. package/{ui → dist/ui}/button.js +6 -7
  359. package/dist/ui/button.js.map +1 -0
  360. package/{ui → dist/ui}/choice-switch.js +1 -1
  361. package/dist/ui/choice-switch.js.map +1 -0
  362. package/{ui → dist/ui}/controls.js +51 -39
  363. package/dist/ui/controls.js.map +1 -0
  364. package/{ui → dist/ui}/display.d.ts +10 -3
  365. package/{ui → dist/ui}/display.js +57 -46
  366. package/dist/ui/display.js.map +1 -0
  367. package/{ui → dist/ui}/expanded-editor-modal.d.ts +2 -2
  368. package/{ui → dist/ui}/expanded-editor-modal.js +6 -6
  369. package/dist/ui/expanded-editor-modal.js.map +1 -0
  370. package/{ui → dist/ui}/field-context.js +1 -1
  371. package/dist/ui/field-context.js.map +1 -0
  372. package/{ui → dist/ui}/floating-panel.d.ts +1 -1
  373. package/{ui → dist/ui}/floating-panel.js +28 -12
  374. package/dist/ui/floating-panel.js.map +1 -0
  375. package/{ui → dist/ui}/index.d.ts +4 -0
  376. package/dist/ui/index.js +18 -0
  377. package/{ui → dist/ui}/input.d.ts +1 -1
  378. package/{ui → dist/ui}/input.js +9 -9
  379. package/dist/ui/input.js.map +1 -0
  380. package/{ui → dist/ui}/mode-bar.js +3 -3
  381. package/dist/ui/mode-bar.js.map +1 -0
  382. package/{ui → dist/ui}/overlay.d.ts +77 -5
  383. package/dist/ui/overlay.js +697 -0
  384. package/dist/ui/overlay.js.map +1 -0
  385. package/{ui → dist/ui}/path-crumbs.js +37 -3
  386. package/dist/ui/path-crumbs.js.map +1 -0
  387. package/{ui → dist/ui}/presence.js +2 -2
  388. package/dist/ui/presence.js.map +1 -0
  389. package/{ui → dist/ui}/select.js +89 -87
  390. package/dist/ui/select.js.map +1 -0
  391. package/{ui → dist/ui}/shortcut-hint.js +1 -1
  392. package/dist/ui/shortcut-hint.js.map +1 -0
  393. package/dist/ui/spinner.d.ts +24 -0
  394. package/dist/ui/spinner.js +42 -0
  395. package/dist/ui/spinner.js.map +1 -0
  396. package/dist/ui/split-button.d.ts +32 -0
  397. package/dist/ui/split-button.js +60 -0
  398. package/dist/ui/split-button.js.map +1 -0
  399. package/{ui → dist/ui}/surfaces.js +1 -1
  400. package/dist/ui/surfaces.js.map +1 -0
  401. package/dist/ui/tab.d.ts +38 -0
  402. package/dist/ui/tab.js +87 -0
  403. package/dist/ui/tab.js.map +1 -0
  404. package/dist/ui/toast.d.ts +20 -0
  405. package/dist/ui/toast.js +115 -0
  406. package/dist/ui/toast.js.map +1 -0
  407. package/{ui → dist/ui}/tooltip-root.d.ts +4 -3
  408. package/{ui → dist/ui}/tooltip-root.js +9 -8
  409. package/dist/ui/tooltip-root.js.map +1 -0
  410. package/{ui → dist/ui}/variants.js +1 -1
  411. package/dist/ui/variants.js.map +1 -0
  412. package/package.json +77 -23
  413. package/adapters/icons.js.map +0 -1
  414. package/adapters/lossy.js +0 -233
  415. package/adapters/lossy.js.map +0 -1
  416. package/adapters/prod-types.js.map +0 -1
  417. package/adapters/schema.js.map +0 -1
  418. package/adapters/values.js.map +0 -1
  419. package/brand/logo.js +0 -9
  420. package/brand/logo.js.map +0 -1
  421. package/context.js.map +0 -1
  422. package/controls/button.js.map +0 -1
  423. package/controls/c1.js.map +0 -1
  424. package/controls/c2.js.map +0 -1
  425. package/controls/choice.js.map +0 -1
  426. package/controls/code-register.js.map +0 -1
  427. package/controls/code.js.map +0 -1
  428. package/controls/color.js.map +0 -1
  429. package/controls/date-register.js.map +0 -1
  430. package/controls/date-zone.js.map +0 -1
  431. package/controls/date.js.map +0 -1
  432. package/controls/form-picker-register.js.map +0 -1
  433. package/controls/form-picker.js.map +0 -1
  434. package/controls/icon-register.js.map +0 -1
  435. package/controls/icon.js.map +0 -1
  436. package/controls/index.js.map +0 -1
  437. package/controls/json-register.js.map +0 -1
  438. package/controls/json.js.map +0 -1
  439. package/controls/link.js.map +0 -1
  440. package/controls/location-register.js.map +0 -1
  441. package/controls/location.js.map +0 -1
  442. package/controls/media-register.js.map +0 -1
  443. package/controls/media.js.map +0 -1
  444. package/controls/number.js.map +0 -1
  445. package/controls/reference-register.js.map +0 -1
  446. package/controls/reference.js.map +0 -1
  447. package/controls/rich-text/blocks.d.ts +0 -28
  448. package/controls/rich-text/blocks.js.map +0 -1
  449. package/controls/rich-text/catalog.js.map +0 -1
  450. package/controls/rich-text/codec.js.map +0 -1
  451. package/controls/rich-text/component-card.js.map +0 -1
  452. package/controls/rich-text/control.js.map +0 -1
  453. package/controls/rich-text/dom.js.map +0 -1
  454. package/controls/rich-text/editor.js.map +0 -1
  455. package/controls/rich-text/floating.js.map +0 -1
  456. package/controls/rich-text/index.js.map +0 -1
  457. package/controls/rich-text/link-form.js +0 -111
  458. package/controls/rich-text/link-form.js.map +0 -1
  459. package/controls/rich-text/markdown.js.map +0 -1
  460. package/controls/rich-text/ops.js.map +0 -1
  461. package/controls/rich-text/toolbar.js.map +0 -1
  462. package/controls/scale.js.map +0 -1
  463. package/controls/slot-register.js.map +0 -1
  464. package/controls/slot-services.js +0 -38
  465. package/controls/slot-services.js.map +0 -1
  466. package/controls/slot.js.map +0 -1
  467. package/controls/table/index.js.map +0 -1
  468. package/controls/table/prod.js.map +0 -1
  469. package/controls/table/refs.js.map +0 -1
  470. package/controls/table/stored.js.map +0 -1
  471. package/controls/table/table-editor.js.map +0 -1
  472. package/controls/table/table-link.js.map +0 -1
  473. package/controls/table/table.js.map +0 -1
  474. package/controls/text.js.map +0 -1
  475. package/engine/array.js.map +0 -1
  476. package/engine/bits.js.map +0 -1
  477. package/engine/breadcrumb.js.map +0 -1
  478. package/engine/expand.js.map +0 -1
  479. package/engine/field-list.js.map +0 -1
  480. package/engine/field-shell.js.map +0 -1
  481. package/engine/frame.js.map +0 -1
  482. package/engine/group-summary.js.map +0 -1
  483. package/engine/grouping.js.map +0 -1
  484. package/engine/item-list.js.map +0 -1
  485. package/engine/kit-field.js.map +0 -1
  486. package/engine/schema-form.js.map +0 -1
  487. package/engine/slot.js.map +0 -1
  488. package/index.js +0 -55
  489. package/index.js.map +0 -1
  490. package/lib/cn.js.map +0 -1
  491. package/lib/columns.js.map +0 -1
  492. package/lib/frames.js.map +0 -1
  493. package/lib/icons.js.map +0 -1
  494. package/lib/path.js.map +0 -1
  495. package/lib/schema.js.map +0 -1
  496. package/lib/utils.js.map +0 -1
  497. package/lib/validate.js.map +0 -1
  498. package/schema/adapters.js.map +0 -1
  499. package/schema/help.js.map +0 -1
  500. package/schema/scale.js.map +0 -1
  501. package/schema/sources.js.map +0 -1
  502. package/schema/table.js.map +0 -1
  503. package/schema/types.js.map +0 -1
  504. package/ui/action-tray.js.map +0 -1
  505. package/ui/button.js.map +0 -1
  506. package/ui/choice-switch.js.map +0 -1
  507. package/ui/controls.js.map +0 -1
  508. package/ui/display.js.map +0 -1
  509. package/ui/expanded-editor-modal.js.map +0 -1
  510. package/ui/field-context.js.map +0 -1
  511. package/ui/floating-panel.js.map +0 -1
  512. package/ui/index.js +0 -13
  513. package/ui/input.js.map +0 -1
  514. package/ui/mode-bar.js.map +0 -1
  515. package/ui/overlay.js +0 -378
  516. package/ui/overlay.js.map +0 -1
  517. package/ui/path-crumbs.js.map +0 -1
  518. package/ui/presence.js.map +0 -1
  519. package/ui/select.js.map +0 -1
  520. package/ui/shortcut-hint.js.map +0 -1
  521. package/ui/surfaces.js.map +0 -1
  522. package/ui/tooltip-root.js.map +0 -1
  523. package/ui/variants.js.map +0 -1
  524. /package/{_virtual → dist/_virtual}/_rolldown/runtime.js +0 -0
  525. /package/{adapters → dist/adapters}/icons.d.ts +0 -0
  526. /package/{brand → dist/brand}/logo.d.ts +0 -0
  527. /package/{controls → dist/controls}/c1.d.ts +0 -0
  528. /package/{controls → dist/controls}/c2.d.ts +0 -0
  529. /package/{controls → dist/controls}/code-register.d.ts +0 -0
  530. /package/{controls → dist/controls}/code.d.ts +0 -0
  531. /package/{controls → dist/controls}/color.d.ts +0 -0
  532. /package/{controls → dist/controls}/date-register.d.ts +0 -0
  533. /package/{controls → dist/controls}/date-zone.d.ts +0 -0
  534. /package/{controls → dist/controls}/date.d.ts +0 -0
  535. /package/{controls → dist/controls}/form-picker-register.d.ts +0 -0
  536. /package/{controls → dist/controls}/form-picker.d.ts +0 -0
  537. /package/{controls → dist/controls}/icon-register.d.ts +0 -0
  538. /package/{controls → dist/controls}/json-register.d.ts +0 -0
  539. /package/{controls → dist/controls}/json.d.ts +0 -0
  540. /package/{controls → dist/controls}/link.d.ts +0 -0
  541. /package/{controls → dist/controls}/location-register.d.ts +0 -0
  542. /package/{controls → dist/controls}/location.d.ts +0 -0
  543. /package/{controls → dist/controls}/media-register.d.ts +0 -0
  544. /package/{controls → dist/controls}/number.d.ts +0 -0
  545. /package/{controls → dist/controls}/reference-register.d.ts +0 -0
  546. /package/{controls → dist/controls}/rich-text/index.d.ts +0 -0
  547. /package/{controls → dist/controls}/rich-text/link-form.d.ts +0 -0
  548. /package/{controls → dist/controls}/rich-text/markdown.d.ts +0 -0
  549. /package/{controls → dist/controls}/rich-text/ops.d.ts +0 -0
  550. /package/{controls → dist/controls}/scale.d.ts +0 -0
  551. /package/{controls → dist/controls}/slot-register.d.ts +0 -0
  552. /package/{controls → dist/controls}/slot.d.ts +0 -0
  553. /package/{controls → dist/controls}/table/index.d.ts +0 -0
  554. /package/{controls → dist/controls}/table/prod.d.ts +0 -0
  555. /package/{controls → dist/controls}/table/refs.d.ts +0 -0
  556. /package/{controls → dist/controls}/table/stored.d.ts +0 -0
  557. /package/{controls → dist/controls}/table/table-editor.d.ts +0 -0
  558. /package/{controls → dist/controls}/table/table-link.d.ts +0 -0
  559. /package/{controls → dist/controls}/table/table.d.ts +0 -0
  560. /package/{engine → dist/engine}/array.d.ts +0 -0
  561. /package/{engine → dist/engine}/bits.d.ts +0 -0
  562. /package/{engine → dist/engine}/field-list.d.ts +0 -0
  563. /package/{engine → dist/engine}/field-shell.d.ts +0 -0
  564. /package/{engine → dist/engine}/kit-field.d.ts +0 -0
  565. /package/{engine → dist/engine}/slot.d.ts +0 -0
  566. /package/{index.d.ts → dist/index.d.ts} +0 -0
  567. /package/{lib → dist/lib}/cn.d.ts +0 -0
  568. /package/{lib → dist/lib}/columns.d.ts +0 -0
  569. /package/{lib → dist/lib}/frames.d.ts +0 -0
  570. /package/{lib → dist/lib}/icons.d.ts +0 -0
  571. /package/{lib → dist/lib}/path.d.ts +0 -0
  572. /package/{lib → dist/lib}/utils.d.ts +0 -0
  573. /package/{schema → dist/schema}/adapters.d.ts +0 -0
  574. /package/{schema → dist/schema}/scale.d.ts +0 -0
  575. /package/{schema → dist/schema}/sources.d.ts +0 -0
  576. /package/{schema → dist/schema}/table.d.ts +0 -0
  577. /package/{types.d.ts → dist/types.d.ts} +0 -0
  578. /package/{ui → dist/ui}/choice-switch.d.ts +0 -0
  579. /package/{ui → dist/ui}/controls.d.ts +0 -0
  580. /package/{ui → dist/ui}/field-context.d.ts +0 -0
  581. /package/{ui → dist/ui}/mode-bar.d.ts +0 -0
  582. /package/{ui → dist/ui}/path-crumbs.d.ts +0 -0
  583. /package/{ui → dist/ui}/presence.d.ts +0 -0
  584. /package/{ui → dist/ui}/select.d.ts +0 -0
  585. /package/{ui → dist/ui}/shortcut-hint.d.ts +0 -0
  586. /package/{ui → dist/ui}/surfaces.d.ts +0 -0
  587. /package/{ui → dist/ui}/variants.d.ts +0 -0
@@ -1,9 +1,10 @@
1
+ import { CATALOG, FEATURES, isHostConfigKey } from "./lossy.js";
1
2
  import { CONTROL_TYPES, HELP_MORE_MAX, ITEM_NAME_RE, UI_CONTROLS } from "./prod-types.js";
2
3
  import { dockIconOf, tablerIconOf } from "./icons.js";
3
- import { CATALOG, FEATURES } from "./lossy.js";
4
+ import { slotChildrenFromStored, slotChildrenToStored } from "../controls/slot-services.js";
4
5
  import { isDataTableProp, tableConfigFromOptions, tableOptionsFromConfig } from "../controls/table/prod.js";
5
- import { buttonFromProd, buttonToProd, datetimeFromProd, datetimeToProd, imageFromProd, imageToProd, linkFromProd, linkToProd, listFromProd, listToProd, ratingFromProd, ratingToProd, referenceFromProd, referenceToProd, scaleFromProd, scaleToProd, videoFromProd, videoToProd, zoneListFromProd, zoneListToProd } from "./values.js";
6
- //#region src/kit/adapters/schema.ts
6
+ import { buttonFromProd, buttonToProd, datetimeFromProd, datetimeToProd, imageFromProd, imageToProd, isCropRect, linkFromProd, linkToProd, listFromProd, listToProd, ratingFromProd, ratingToProd, referenceFromProd, referenceToProd, scaleFromProd, scaleToProd, typedRowsFromProd, typedRowsToProd, videoFromProd, videoToProd, zoneListFromProd, zoneListToProd } from "./values.js";
7
+ //#region src/adapters/schema.ts
7
8
  var isObj = (v) => !!v && typeof v === "object" && !Array.isArray(v);
8
9
  function deepEqual(a, b) {
9
10
  if (a === b) return true;
@@ -14,6 +15,8 @@ function deepEqual(a, b) {
14
15
  var singular = (t) => t.endsWith("ies") ? `${t.slice(0, -3)}y` : t.endsWith("s") && !t.endsWith("ss") ? t.slice(0, -1) : t;
15
16
  var kebab = (s) => s.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "schema";
16
17
  var camel = (s) => s.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
18
+ /** The slug a V2 schema name is stored under (a component's, when the host seeds a /kit block): `textBlock` → `text-block`. */
19
+ var schemaSlugOf = kebab;
17
20
  var capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
18
21
  var ZONE_NAMES = new Set(typeof Intl.supportedValuesOf === "function" ? [...Intl.supportedValuesOf("timeZone"), "UTC"] : ["UTC"]);
19
22
  /** An IANA zone name (isTimeZoneName): ICU's canonical list, or a current name Intl itself accepts (Asia/Kolkata). */
@@ -94,7 +97,7 @@ var PROP_TYPE = {
94
97
  code: "code"
95
98
  };
96
99
  /** Field types whose `config` the backend parses strictly (unknown keys stripped or refused). */
97
- var STRICT_CONFIG = new Set([
100
+ var STRICT_CONFIG = /* @__PURE__ */ new Set([
98
101
  "array",
99
102
  "reference",
100
103
  "multi-reference",
@@ -107,7 +110,9 @@ var STRICT_CONFIG = new Set([
107
110
  "tags",
108
111
  "rating",
109
112
  "code",
110
- "video"
113
+ "video",
114
+ "table",
115
+ "slot"
111
116
  ]);
112
117
  /** V2 list item types → prod `config.itemType` (ARRAY_ITEM_TYPES); a V2 `text` item reads back as `string`. */
113
118
  var PRIM_ITEM = {
@@ -125,6 +130,23 @@ var ENTRY_TARGET = (t) => ![
125
130
  "shopify",
126
131
  "collection"
127
132
  ].includes(t);
133
+ /** The non-entry targets a prop reference stores in config.targets (backend reference-target-config.ts REF_TARGET_KINDS). */
134
+ var REF_TARGET_KINDS = [
135
+ "page",
136
+ "form",
137
+ "collection"
138
+ ];
139
+ /**
140
+ * A prop reference whose only target is Shopify (backend reference-target-config.ts UNSUPPORTED_TARGETS refuses it by
141
+ * name) is stored as locked text holding the id (a list of them: a list of text), so the value stays the plain gid
142
+ * string V2 holds. This lock reason is the marker the read side turns back into V2's reference (allowed deviation
143
+ * "shopify reference").
144
+ */
145
+ var SHOPIFY_REF_LOCK = "A Shopify product id. No Shopify integration backs a reference in BetterCMS yet, so the id is kept as it is.";
146
+ var shopifyOnly = (r) => !!r?.to?.length && r.to.every((t) => t === "shopify");
147
+ /** backend REFERENCE_FILTER_MAX; parameters ($x) and the parent document (^) are refused there (reference_filter_invalid). */
148
+ var REFERENCE_FILTER_MAX = 500;
149
+ var filterKept = (f) => typeof f === "string" && f.trim() !== "" && f.length <= REFERENCE_FILTER_MAX && !/[$^]/.test(f);
128
150
  /** The old rating: a number with ui.control "rating" and these bounds read back as no `numeric` (read side only). */
129
151
  var RATING_CONFIG = {
130
152
  min: 0,
@@ -132,6 +154,12 @@ var RATING_CONFIG = {
132
154
  step: .5
133
155
  };
134
156
  /**
157
+ * A V2 rating sets no step and holds any number on its scale (a 4.8 average). Prod's unset step is 1, which refuses
158
+ * 4.8 (rating_value_invalid), so a rating with no step is written with step 0.1 (≤ RATING_MAX_STOPS stops on a 1–10
159
+ * scale) and that step reads back as none; a prod rating with no step reads back as its real step, 1.
160
+ */
161
+ var V2_RATING_STEP = .1;
162
+ /**
135
163
  * V2 block-picker category (KitCategory, and the core examples' own) → the nearest prod ComponentCategory. A category
136
164
  * with no row has no prod twin; the server defaults the component to "custom".
137
165
  */
@@ -155,7 +183,7 @@ var CATEGORY_TO_PROD = {
155
183
  Layout: "section"
156
184
  };
157
185
  /** The field types a kind:'block' model may hold (backend block-model-mapping.ts FIELD_PRIMITIVE). */
158
- var BLOCK_FIELD_TYPES = new Set([
186
+ var BLOCK_FIELD_TYPES = /* @__PURE__ */ new Set([
159
187
  "text",
160
188
  "longtext",
161
189
  "richtext",
@@ -172,15 +200,20 @@ var BLOCK_FIELD_TYPES = new Set([
172
200
  "datetime",
173
201
  "boolean"
174
202
  ]);
175
- /** A prop's config.minItems / maxItems cap (SLOT_ITEMS_CAP, componentPropDefSchema). */
203
+ /** A slot prop's config.minItems / maxItems cap (SLOT_ITEMS_CAP, componentPropDefSchema). */
176
204
  var SLOT_ITEMS_CAP = 50;
205
+ /** A table prop's rows and an array prop's items: their config.minItems / maxItems cap (LIST_ITEMS_CAP). */
206
+ var LIST_ITEMS_CAP = 500;
207
+ /** backend slotAllowIssues: a slot's config.allow holds 1–50 block names of 1–64 characters, no repeats. */
208
+ var SLOT_ALLOW_MAX = 50;
209
+ var SLOT_ALLOW_NAME_MAX = 64;
177
210
  /** Backend dock-vocabulary.ts SLIDER_MAX_STOPS: a slider's (max − min) / step. */
178
211
  var SLIDER_MAX_STOPS = 1e3;
179
212
  /**
180
213
  * The field types prod's unique flag accepts (uniqueFlagRules): a comparable scalar (UNIQUE_FIELD_TYPES, no two entries
181
214
  * share it; a reference only to one model) or a list (UNIQUE_ITEMS_FIELD_TYPES, no item repeats).
182
215
  */
183
- var UNIQUE_TYPES = new Set([
216
+ var UNIQUE_TYPES = /* @__PURE__ */ new Set([
184
217
  "text",
185
218
  "longtext",
186
219
  "slug",
@@ -197,14 +230,14 @@ var UNIQUE_TYPES = new Set([
197
230
  "multi-reference"
198
231
  ]);
199
232
  /** Prop types whose value is a list (UNIQUE_LIST_PROP_TYPES), and the sub-field types a table row compares (UNIQUE_ROW_PROP_TYPES). */
200
- var UNIQUE_LIST_PROPS = new Set([
233
+ var UNIQUE_LIST_PROPS = /* @__PURE__ */ new Set([
201
234
  "multiselect",
202
235
  "tags",
203
236
  "multi-reference",
204
237
  "table",
205
238
  "array"
206
239
  ]);
207
- var UNIQUE_ROW_PROPS = new Set([
240
+ var UNIQUE_ROW_PROPS = /* @__PURE__ */ new Set([
208
241
  "text",
209
242
  "url",
210
243
  "slug",
@@ -215,19 +248,19 @@ var UNIQUE_ROW_PROPS = new Set([
215
248
  "date"
216
249
  ]);
217
250
  /** Types measured by minLength / maxLength (fields: LENGTH_FIELD_TYPES; props: LENGTH_TWIN), and those that take a pattern. */
218
- var LENGTH_FIELDS = new Set([
251
+ var LENGTH_FIELDS = /* @__PURE__ */ new Set([
219
252
  "text",
220
253
  "longtext",
221
254
  "richtext",
222
255
  "document"
223
256
  ]);
224
- var LENGTH_PROPS = new Set([
257
+ var LENGTH_PROPS = /* @__PURE__ */ new Set([
225
258
  "text",
226
259
  "url",
227
260
  "longtext",
228
261
  "richtext"
229
262
  ]);
230
- var PATTERN_TYPES = new Set([
263
+ var PATTERN_TYPES = /* @__PURE__ */ new Set([
231
264
  "text",
232
265
  "longtext",
233
266
  "url"
@@ -236,7 +269,7 @@ var PATTERN_TYPES = new Set([
236
269
  * Leaf types whose value is an object a dotted preview path may read into (previewPathProblem OBJECT_VALUE_TYPES), and the
237
270
  * keys an image or video value holds (LEAF_VALUE_KEYS). V2 reads `image.alt`; prod stores the key as `altText`.
238
271
  */
239
- var OBJECT_VALUE_TYPES = new Set([
272
+ var OBJECT_VALUE_TYPES = /* @__PURE__ */ new Set([
240
273
  "image",
241
274
  "link",
242
275
  "button",
@@ -268,6 +301,8 @@ var LEAF_VALUE_KEYS = {
268
301
  ]
269
302
  };
270
303
  var IMAGE_PATH_KEY = { alt: "altText" };
304
+ /** The keys of a data table's value a preview path may end on (previewPathProblem `<table>.caption` / `.notes`). */
305
+ var TABLE_PATH_KEYS = ["caption", "notes"];
271
306
  var lose = (ctx, path, feature) => ctx.lossy.push({
272
307
  path,
273
308
  feature,
@@ -279,7 +314,7 @@ var cat = (ctx, path, feature) => ctx.catalog.push({
279
314
  reason: CATALOG[feature]
280
315
  });
281
316
  /** Keys every type keeps, including a field degraded to json. */
282
- var COMMON_KEYS = new Set([
317
+ var COMMON_KEYS = /* @__PURE__ */ new Set([
283
318
  "name",
284
319
  "title",
285
320
  "type",
@@ -296,14 +331,53 @@ var COMMON_KEYS = new Set([
296
331
  "advanced",
297
332
  "localized",
298
333
  "ai",
299
- "placeholder"
334
+ "placeholder",
335
+ "archived"
336
+ ]);
337
+ /** Types a slot setting may not be (backend slotSettingsHardening, and a model slot's SLOT_SETTINGS_NESTING: leaf fields only). */
338
+ var SETTING_NESTING_TYPES = /* @__PURE__ */ new Set([
339
+ "object",
340
+ "group",
341
+ "array",
342
+ "table",
343
+ "slot",
344
+ "form",
345
+ "modular"
300
346
  ]);
301
- function shapeOf(f, lv, ctx, top) {
347
+ /** backend slotSettingsHardening: at most this many per-child settings. */
348
+ var SLOT_SETTINGS_MAX = 20;
349
+ /** backend ITEM_TYPE_NAME (what a row's `_type` holds), and an item type label's cap. */
350
+ var ITEM_TYPE_NAME = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/;
351
+ var ITEM_TYPE_LABEL_MAX = 120;
352
+ function shapeOf(f, lv, ctx, top, nest) {
353
+ const shape = shapeOfField(f, lv, ctx, top);
354
+ if (nest === "setting" && SETTING_NESTING_TYPES.has(shape.type)) return {
355
+ kind: "json",
356
+ type: "json",
357
+ feature: "slotSettings"
358
+ };
359
+ return shape;
360
+ }
361
+ /** backend content.ts keyRuleProblem: keys a resolver would misread (KEY_RESERVED), and a key's format (KEY_FORMAT). */
362
+ var MODEL_RESERVED_KEYS = /* @__PURE__ */ new Set([
363
+ "derived",
364
+ "layout",
365
+ "page"
366
+ ]);
367
+ /** The key a model field is stored under: a reserved or malformed V2 name, renamed. */
368
+ function modelKeyOf(name) {
369
+ let k = name.replace(/^_+/, "").replace(/[^A-Za-z0-9_-]/g, "");
370
+ if (!/^[A-Za-z]/.test(k)) k = `f${k}`;
371
+ return MODEL_RESERVED_KEYS.has(k) ? `${k}Field` : k;
372
+ }
373
+ /** A slot's V2 allow list as the backend keeps it: 1–50 distinct block names of 1–64 characters (slotAllowIssues). */
374
+ var slotAllowOk = (allow) => Array.isArray(allow) && allow.length >= 1 && allow.length <= SLOT_ALLOW_MAX && new Set(allow).size === allow.length && allow.every((n) => typeof n === "string" && n === n.trim() && n.length >= 1 && n.length <= SLOT_ALLOW_NAME_MAX);
375
+ function shapeOfField(f, lv, ctx, top) {
302
376
  const props = ctx.lane === "props";
303
377
  const deep = lv >= 10;
304
378
  const of = Array.isArray(f.of) ? f.of : void 0;
305
- /** a prop reference needs a content model to point at (refTargetNamed): with none it is stored as json */
306
- const noEntry = (r) => props && !(r?.to ?? []).some(ENTRY_TARGET);
379
+ /** a prop reference names a content model or a page / form / collection target (refTargetNamed): with none it is stored as json */
380
+ const noEntry = (r) => props && !(r?.to ?? []).some((t) => ENTRY_TARGET(t) || REF_TARGET_KINDS.includes(t));
307
381
  switch (f.type) {
308
382
  case "object": return deep ? {
309
383
  kind: "json",
@@ -314,11 +388,22 @@ function shapeOf(f, lv, ctx, top) {
314
388
  type: "object"
315
389
  };
316
390
  case "array": {
317
- if (of && of.length > 1 && !props && of.every((i) => i.type === "object")) return {
318
- kind: "modular",
319
- type: "modular",
320
- items: of
321
- };
391
+ if (of && of.length > 1 && of.every((i) => i.type === "object")) {
392
+ if (!props) return {
393
+ kind: "modular",
394
+ type: "modular",
395
+ items: of
396
+ };
397
+ return deep ? {
398
+ kind: "json",
399
+ type: "json",
400
+ feature: "depth>10"
401
+ } : {
402
+ kind: "types",
403
+ type: "table",
404
+ items: of
405
+ };
406
+ }
322
407
  if (!of || of.length !== 1) return {
323
408
  kind: "json",
324
409
  type: "json",
@@ -334,6 +419,13 @@ function shapeOf(f, lv, ctx, top) {
334
419
  type: props ? "table" : "array",
335
420
  item
336
421
  };
422
+ if (item.type === "reference" && shopifyOnly(item.reference)) return {
423
+ kind: "itemType",
424
+ type: "array",
425
+ item,
426
+ itemType: "text",
427
+ shopify: true
428
+ };
337
429
  if (item.type === "reference") return noEntry(item.reference) ? {
338
430
  kind: "json",
339
431
  type: "json",
@@ -356,24 +448,30 @@ function shapeOf(f, lv, ctx, top) {
356
448
  feature: "type:primitive-array"
357
449
  };
358
450
  }
359
- case "slot": return {
360
- kind: "json",
361
- type: "json",
362
- feature: "type:slot"
363
- };
364
- case "table": return props ? {
451
+ case "slot":
452
+ if (!props && !slotAllowOk(f.of?.allow)) return {
453
+ kind: "json",
454
+ type: "json",
455
+ feature: "type:slot"
456
+ };
457
+ return {
458
+ kind: "slot",
459
+ type: "slot"
460
+ };
461
+ case "table": return {
365
462
  kind: "leaf",
366
463
  type: "table"
367
- } : {
368
- kind: "json",
369
- type: "json",
370
- feature: "type:table"
371
464
  };
372
465
  case "reference":
373
466
  if (props && top && f.reference?.to?.length === 1 && f.reference.to[0] === "form" && !f.reference.filter) return {
374
467
  kind: "leaf",
375
468
  type: "form"
376
469
  };
470
+ if (shopifyOnly(f.reference)) return {
471
+ kind: "leaf",
472
+ type: "text",
473
+ shopify: true
474
+ };
377
475
  return noEntry(f.reference) ? {
378
476
  kind: "json",
379
477
  type: "json",
@@ -400,9 +498,9 @@ var trunc = (ctx, at, s, max, feature) => {
400
498
  lose(ctx, at, feature);
401
499
  return s.slice(0, max);
402
500
  };
403
- function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
501
+ function convertField(f, at, lv, ctx, siblings, top, inTable = false, inItem = false, inSetting = false) {
404
502
  const props = ctx.lane === "props";
405
- const shape = shapeOf(f, lv, ctx, top);
503
+ const shape = shapeOf(f, lv, ctx, top, inTable ? "rows" : inItem ? "item" : inSetting ? "setting" : void 0);
406
504
  const node = {
407
505
  key: f.name,
408
506
  label: f.title,
@@ -414,17 +512,28 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
414
512
  if (twins) {
415
513
  node.key = `${f.name}_${twins + 1}`;
416
514
  lose(ctx, `${at}/name`, "duplicate name");
515
+ } else if (!props && modelKeyOf(f.name) !== f.name) {
516
+ node.key = modelKeyOf(f.name);
517
+ lose(ctx, `${at}/name`, "reserved name");
417
518
  }
418
- const nesting = shape.kind === "object" || shape.kind === "zones";
419
- /** a list of items (objects, block models, scalars or entries): ui.itemLabel names one item */
420
- const listy = shape.kind === "zones" || shape.kind === "modular" || shape.kind === "itemType" || shape.kind === "refs";
519
+ const nesting = shape.kind === "object" || shape.kind === "zones" || shape.kind === "types";
520
+ /** a list of items (objects, block models, item types, scalars or entries): ui.itemLabel names one item */
521
+ const listy = shape.kind === "zones" || shape.kind === "modular" || shape.kind === "types" || shape.kind === "itemType" || shape.kind === "refs";
421
522
  const looseConfig = props || !STRICT_CONFIG.has(shape.type);
422
523
  if (shape.kind === "json") lose(ctx, `${at}/type`, shape.feature);
524
+ const shopify = (shape.kind === "leaf" || shape.kind === "itemType") && shape.shopify === true;
525
+ if (shopify) {
526
+ if (f.readOnly !== true) lose(ctx, `${at}/readOnly`, "shopify reference");
527
+ node.readOnly = true;
528
+ node.lockReason = SHOPIFY_REF_LOCK;
529
+ }
423
530
  if (!props && f.type === "url") node.config.inputType = "url";
424
531
  if (shape.type === "datetime" && !props) node.config.includeTime = true;
425
532
  if (shape.kind === "leaf" && shape.type === "table") node.config.mode = "data";
533
+ if (shape.kind === "types") node.config.mode = "types";
534
+ if (shape.kind === "slot" && props) node.config.multiple = true;
426
535
  if (listy) {
427
- const label = f.itemNoun !== void 0 ? capitalize(f.itemNoun) : shape.kind === "zones" && shape.item.title !== conventionItem(f.name, f.title).title ? shape.item.title : void 0;
536
+ const label = f.itemNoun !== void 0 ? capitalize(f.itemNoun) : props && shape.kind === "zones" && shape.item.title !== conventionItem(f.name, f.title).title ? shape.item.title : void 0;
428
537
  if (label !== void 0 && label.length <= 24) node.ui.itemLabel = label;
429
538
  }
430
539
  const devLock = f.access?.edit === "schema.manage" && !!f.lockReason;
@@ -435,6 +544,11 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
435
544
  lose(ctx, p, shape.feature);
436
545
  continue;
437
546
  }
547
+ if (shopify && (k === "reference" || k === "readOnly")) continue;
548
+ if (shopify && (k === "access" || k === "lockReason")) {
549
+ lose(ctx, p, "shopify reference");
550
+ continue;
551
+ }
438
552
  switch (k) {
439
553
  case "name":
440
554
  case "title":
@@ -451,7 +565,7 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
451
565
  else node.ui.placeholder = trunc(ctx, p, f.placeholder, 120, "help>limit");
452
566
  break;
453
567
  case "initialValue":
454
- if (props && (shape.type === "reference" || shape.type === "multi-reference")) cat(ctx, p, "seeded instance");
568
+ if (props && (shape.type === "reference" || shape.type === "multi-reference" || shape.kind === "slot")) cat(ctx, p, "seeded instance");
455
569
  else node.defaultValue = defaultToProd(f, shape, f.initialValue, p, ctx, lv, top);
456
570
  break;
457
571
  case "validation":
@@ -471,7 +585,7 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
471
585
  ofToProd(f, shape, node, p, lv, ctx);
472
586
  break;
473
587
  case "openMode":
474
- if (nesting) node.openMode = f.openMode;
588
+ if (nesting || props && shape.type === "code") node.openMode = f.openMode;
475
589
  else lose(ctx, p, "openMode");
476
590
  break;
477
591
  case "preview":
@@ -509,6 +623,9 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
509
623
  case "localized":
510
624
  node.localized = f.localized;
511
625
  break;
626
+ case "archived":
627
+ if (f.archived) node.archived = true;
628
+ break;
512
629
  case "advanced":
513
630
  node.advanced = f.advanced;
514
631
  break;
@@ -527,7 +644,7 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
527
644
  else lose(ctx, p, "unsupported key");
528
645
  break;
529
646
  case "icon": {
530
- const dock = shape.kind === "object" || shape.kind === "modular" ? dockIconOf(f.icon) : void 0;
647
+ const dock = shape.kind === "object" || shape.kind === "modular" || shape.kind === "types" ? dockIconOf(f.icon) : void 0;
531
648
  if (dock) node.ui.icon = dock;
532
649
  else lose(ctx, p, "icon");
533
650
  break;
@@ -543,13 +660,14 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
543
660
  break;
544
661
  }
545
662
  case "slotSettings":
546
- lose(ctx, p, "slotSettings");
663
+ if (shape.kind === "slot") slotSettingsToProd(f.slotSettings, node, p, lv, ctx);
664
+ else lose(ctx, p, "slotSettings");
547
665
  break;
548
666
  case "itemNoun":
549
667
  if (!listy || node.ui.itemLabel?.toLowerCase() !== f.itemNoun) lose(ctx, p, "itemNoun");
550
668
  break;
551
669
  case "arrayLayout":
552
- if (shape.kind === "zones" || shape.kind === "modular") node.ui.layout = f.arrayLayout;
670
+ if (shape.kind === "zones" || shape.kind === "modular" || shape.kind === "types") node.ui.layout = f.arrayLayout;
553
671
  else if ((shape.kind === "itemType" || shape.kind === "refs") && f.arrayLayout === "grid") node.ui.layout = "gallery";
554
672
  else lose(ctx, p, "layout");
555
673
  break;
@@ -557,6 +675,14 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
557
675
  if (shape.type === "link" || shape.type === "image" || shape.type === "button") node.ui.valueShape = f.valueShape;
558
676
  else lose(ctx, p, "valueShape");
559
677
  break;
678
+ case "linkHref":
679
+ if (typeof f.linkHref === "string" && f.linkHref) node.config.linkHref = f.linkHref;
680
+ else lose(ctx, p, "unsupported key");
681
+ break;
682
+ case "svgOnly":
683
+ if (shape.type === "image" && f.svgOnly === true) node.config.media = "icon";
684
+ else if (f.svgOnly !== false) lose(ctx, p, "unsupported key");
685
+ break;
560
686
  case "timezone":
561
687
  if (shape.type === "date" || shape.type === "datetime") {
562
688
  const tz = f.timezone;
@@ -580,8 +706,14 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
580
706
  default: lose(ctx, p, "unsupported key");
581
707
  }
582
708
  }
709
+ if (props && f.description === void 0) node.helpText = null;
583
710
  if (f.itemNoun === void 0 && shape.kind === "zones" && node.ui.itemLabel !== void 0) cat(ctx, `${at}/itemNoun`, "itemNoun (derived)");
584
711
  placeInSet(f, node, at, ctx, top, siblings);
712
+ if (f.type === "rating" && node.type === "rating" && f.numeric?.step === void 0) {
713
+ const lo = typeof node.config.min === "number" ? node.config.min : 0;
714
+ const stops = ((typeof node.config.max === "number" ? node.config.max : 5) - lo) / V2_RATING_STEP;
715
+ if (Math.abs(stops - Math.round(stops)) < 1e-9 && Math.round(stops) <= 100) node.config.step = V2_RATING_STEP;
716
+ }
585
717
  if (node.ui.control === "slider" && !sliderOk(node.config)) {
586
718
  delete node.ui.control;
587
719
  lose(ctx, `${at}/layout`, "layout");
@@ -606,7 +738,16 @@ function sliderOk({ min, max, step }) {
606
738
  /** Where a field sits: its tab (fields lane) or prop group (props lane). */
607
739
  function placeInSet(f, node, at, ctx, top, siblings) {
608
740
  if (ctx.lane === "props") {
609
- if (f.group !== void 0) lose(ctx, `${at}/group`, "group (props)");
741
+ if (!top) {
742
+ if (f.group !== void 0) {
743
+ if (ctx.keptTabs.has(f.group)) node.ui.tab = { id: f.group };
744
+ else lose(ctx, `${at}/group`, "group (nested)");
745
+ }
746
+ } else {
747
+ const t = ctx.propTabs.get(f);
748
+ if (t?.loss) lose(ctx, `${at}/group`, t.loss);
749
+ if (t?.tab) node.ui.tab = ctx.definedTabs.has(t.tab.id) ? { id: t.tab.id } : (ctx.definedTabs.add(t.tab.id), t.tab);
750
+ }
610
751
  if (f.fieldset === void 0) return;
611
752
  const set = ctx.propSets.get(f.fieldset);
612
753
  if (!set) return lose(ctx, `${at}/fieldset`, "fieldset collision");
@@ -663,7 +804,7 @@ function defaultToProd(f, shape, v, at, ctx, lv, top) {
663
804
  var AS_IS = { k: "as-is" };
664
805
  /** The write side's plan, from the V2 field and the shape it is stored as. */
665
806
  function writePlan(f, shape, lv, ctx, top) {
666
- const kids = (fields) => new Map(fields.map((c) => [c.name, writePlan(c, shapeOf(c, lv + 1, ctx, false), lv + 1, ctx, false)]));
807
+ const kids = (fields, nest) => new Map(fields.map((c) => [c.name, writePlan(c, shapeOf(c, lv + 1, ctx, false, nest), lv + 1, ctx, false)]));
667
808
  switch (shape.kind) {
668
809
  case "json": return AS_IS;
669
810
  case "itemType": return shape.itemType === "image" || shape.itemType === "link" ? {
@@ -677,8 +818,13 @@ function writePlan(f, shape, lv, ctx, top) {
677
818
  case "zones": return {
678
819
  k: "rows",
679
820
  zoned: ctx.lane === "fields",
680
- fields: kids(shape.item.fields ?? [])
821
+ fields: kids(shape.item.fields ?? [], ctx.lane === "props" ? "rows" : void 0)
681
822
  };
823
+ case "types": return {
824
+ k: "types",
825
+ types: new Map(shape.items.map((i) => [i.name, kids(i.fields ?? [], "item")]))
826
+ };
827
+ case "slot": return ctx.lane === "fields" ? { k: "blocks" } : AS_IS;
682
828
  case "modular": {
683
829
  const top1 = (fields) => new Map(fields.map((c) => [c.name, writePlan(c, shapeOf(c, 1, ctx, false), 1, ctx, false)]));
684
830
  return {
@@ -688,6 +834,7 @@ function writePlan(f, shape, lv, ctx, top) {
688
834
  }
689
835
  case "refs": return { k: "refs" };
690
836
  case "leaf":
837
+ if (shape.shopify) return AS_IS;
691
838
  if (f.type === "link" || f.type === "url") return { k: "link" };
692
839
  if (f.type === "button" || f.type === "image") return { k: f.type };
693
840
  if (f.type === "datetime") return {
@@ -706,7 +853,7 @@ function leafPlan(f, prodType) {
706
853
  scale: {
707
854
  min: f.numeric?.min,
708
855
  max: f.numeric?.max,
709
- step: f.numeric?.step
856
+ step: f.numeric?.step ?? V2_RATING_STEP
710
857
  }
711
858
  };
712
859
  if (f.type === "number" && f.scale) return {
@@ -716,11 +863,12 @@ function leafPlan(f, prodType) {
716
863
  return prodType === "number" ? { k: "number" } : AS_IS;
717
864
  }
718
865
  /**
719
- * The read side's plan, from the V2 field read back (`zoned`: lists read from an array's repeatable zone; `modular`:
720
- * lists read from a modular field, whose item fields live in their block models and pass as stored).
866
+ * The read side's plan, from the V2 field read back (`zoned`: lists read from an array's repeatable zone and groups
867
+ * read from its non-repeatable zone; `modular`: lists read from a modular field, whose item fields live in their block
868
+ * models and pass as stored).
721
869
  */
722
- function readPlan(f, prodType, zoned, modular) {
723
- const kids = (fields) => new Map(fields.map((c) => [c.name, readPlan(c, "", zoned, modular)]));
870
+ function readPlan(f, prodType, zoned, modular, types, slots, blocks) {
871
+ const kids = (fields) => new Map(fields.map((c) => [c.name, readPlan(c, "", zoned, modular, types, slots, blocks)]));
724
872
  switch (f.type) {
725
873
  case "link":
726
874
  case "url": return { k: "link" };
@@ -731,16 +879,29 @@ function readPlan(f, prodType, zoned, modular) {
731
879
  zone: fixedZone(f)
732
880
  };
733
881
  case "date": return { k: "date" };
734
- case "reference": return { k: prodType === "form" ? "form" : "ref" };
882
+ case "reference":
883
+ if (shopifyOnly(f.reference)) return AS_IS;
884
+ return { k: prodType === "form" ? "form" : "ref" };
735
885
  case "object": return {
736
886
  k: "object",
887
+ ...zoned.has(f) ? { zoned: true } : {},
737
888
  fields: kids(f.fields ?? [])
738
889
  };
890
+ case "slot":
891
+ if (blocks?.has(f)) return { k: "blocks" };
892
+ return slots?.has(f) ? {
893
+ k: "slot",
894
+ multiple: slots.get(f)
895
+ } : AS_IS;
739
896
  case "array": {
740
897
  if (modular.has(f)) return {
741
898
  k: "modular",
742
899
  types: new Map((Array.isArray(f.of) ? f.of : []).map((i) => [i.name, kids(i.fields ?? [])]))
743
900
  };
901
+ if (types?.has(f)) return {
902
+ k: "types",
903
+ types: new Map((Array.isArray(f.of) ? f.of : []).map((i) => [i.name, kids(i.fields ?? [])]))
904
+ };
744
905
  const item = Array.isArray(f.of) && f.of.length === 1 ? f.of[0] : void 0;
745
906
  if (item?.type === "object") return {
746
907
  k: "rows",
@@ -751,7 +912,7 @@ function readPlan(f, prodType, zoned, modular) {
751
912
  k: "list",
752
913
  item: item.type
753
914
  };
754
- return item?.type === "reference" ? { k: "refs" } : AS_IS;
915
+ return item?.type === "reference" && !shopifyOnly(item.reference) ? { k: "refs" } : AS_IS;
755
916
  }
756
917
  default: return leafPlan(f, prodType);
757
918
  }
@@ -791,7 +952,10 @@ function planToProd(plan, v, lost, at = "") {
791
952
  case "list": return take(listToProd(v, plan.item));
792
953
  case "scale": return take(scaleToProd(v, plan.scale));
793
954
  case "rating": return take(ratingToProd(v, plan.scale));
794
- case "object": return mapKeys(v, plan.fields, (p, x, k) => planToProd(p, x, lost, under(k)));
955
+ case "object": {
956
+ const out = mapKeys(v, plan.fields, (p, x, k) => planToProd(p, x, lost, under(k)));
957
+ return plan.zoned && isObj(out) ? { nonRepeatable: out } : out;
958
+ }
795
959
  case "rows": {
796
960
  if (!Array.isArray(v)) return v;
797
961
  const rows = v.map((row, i) => mapKeys(row, plan.fields, (p, x, k) => planToProd(p, x, lost, `${under(i)}/${k}`)));
@@ -809,24 +973,46 @@ function planToProd(plan, v, lost, at = "") {
809
973
  data: fields ? mapKeys(rest, fields, (p, x, k) => planToProd(p, x, lost, `${under(i)}/${k}`)) : rest
810
974
  };
811
975
  });
976
+ case "blocks": return Array.isArray(v) ? v.map((c) => isObj(c) && typeof c._type === "string" ? {
977
+ ...c,
978
+ _type: kebab(c._type)
979
+ } : c) : v;
980
+ case "slot": {
981
+ const kids = (type) => plan.child?.(type);
982
+ return slotChildrenToStored(plan.family ? childVariantsToTypes(v, plan.family) : v, plan.multiple !== false, (type, values, i) => {
983
+ const fields = kids(type);
984
+ return fields ? mapKeys(values, fields, (p, x, k) => planToProd(p, x, lost, `${under(i)}/${k}`)) : values;
985
+ }, (rel) => lost?.push(under(rel)));
986
+ }
987
+ case "types": return take(typedRowsToProd(v, (row, i) => {
988
+ const fields = plan.types.get(row._type);
989
+ return fields ? mapKeys(row, fields, (p, x, k) => planToProd(p, x, lost, `${under(i)}/${k}`)) : row;
990
+ }));
812
991
  default: return v;
813
992
  }
814
993
  }
815
994
  /** The keys the kit reads back from a stored image object (imageFromProd) and writes again (imageToProd). */
816
- var IMAGE_KIT_KEYS = new Set([
995
+ var IMAGE_KIT_KEYS = /* @__PURE__ */ new Set([
817
996
  "url",
818
997
  "id",
819
998
  "name",
820
999
  "altText",
821
1000
  "focal"
822
1001
  ]);
823
- /** Every key of a stored image the kit cannot hand back: a crop (read back only with the asset's pixel size) or a key it does not model. */
1002
+ /**
1003
+ * Every key of a stored image the kit cannot hand back. A crop rect is read back as it is (imageFromProd), and an older
1004
+ * `{ src, alt }` value reads as its url and alt text, so neither is a loss (QA item 3); a malformed crop, or a key the
1005
+ * kit does not model, is.
1006
+ */
824
1007
  function imageStoredLoss(v, rel, loss) {
825
1008
  if (!isObj(v)) return;
1009
+ const legacy = typeof v.url !== "string" && typeof v.src === "string";
826
1010
  for (const [k, x] of Object.entries(v)) {
827
1011
  if (IMAGE_KIT_KEYS.has(k) || x === void 0) continue;
1012
+ if (legacy && k === "src") continue;
1013
+ if (k === "alt" && typeof x === "string" && v.altText === void 0) continue;
828
1014
  if (k === "crop") {
829
- if (x != null) loss(`${rel}/crop`, "A saved crop is not read back by the kit; editing the image would drop it.");
1015
+ if (x != null && !isCropRect(x)) loss(`${rel}/crop`, "A saved crop the kit can't read; editing the image would drop it.");
830
1016
  } else loss(`${rel}/${k}`, `The image's saved "${k}" is not kept by the kit; editing the image would drop it.`);
831
1017
  }
832
1018
  }
@@ -859,7 +1045,14 @@ function planFromProd(plan, v, loss, rel = "") {
859
1045
  else if (isObj(x) && referenceFromProd(x) === null) loss(`${rel}${Array.isArray(v) ? `/${i}` : ""}` || "(value)", "A stored reference the kit can't read; editing it would drop it.");
860
1046
  }
861
1047
  return referenceFromProd(v);
862
- case "object": return mapKeys(v, plan.fields, (p, x, k) => planFromProd(p, x, loss, `${rel}/${k}`));
1048
+ case "object": {
1049
+ const env = plan.zoned && isObj(v) && "nonRepeatable" in v ? v : void 0;
1050
+ if (env && loss) {
1051
+ for (const k of Object.keys(env)) if (k !== "nonRepeatable" && env[k] !== void 0) loss(`${rel}/${k}`, `The group's saved "${k}" is not one of its fields; editing the group would drop it.`);
1052
+ }
1053
+ const at = env ? `${rel}/nonRepeatable` : rel;
1054
+ return mapKeys(env ? isObj(env.nonRepeatable) ? env.nonRepeatable : {} : v, plan.fields, (p, x, k) => planFromProd(p, x, loss, `${at}/${k}`));
1055
+ }
863
1056
  case "rows": {
864
1057
  const rows = plan.zoned ? zoneListFromProd(v) : v;
865
1058
  return Array.isArray(rows) ? rows.map((row, i) => mapKeys(row, plan.fields, (p, x, k) => planFromProd(p, x, loss, `${rel}/${i}/${k}`))) : rows;
@@ -875,6 +1068,27 @@ function planFromProd(plan, v, loss, rel = "") {
875
1068
  ...back
876
1069
  };
877
1070
  }) : v;
1071
+ case "blocks": return Array.isArray(v) ? v.map((c) => isObj(c) && typeof c._type === "string" ? {
1072
+ ...c,
1073
+ _type: camel(c._type)
1074
+ } : c) : v;
1075
+ case "slot": return slotChildrenFromStored(v, (type, values, i) => {
1076
+ const fields = plan.child?.(type);
1077
+ const read = fields ? mapKeys(values, fields, (p, x, k) => planFromProd(p, x, loss, `${rel}/${i}/${k}`)) : values;
1078
+ const variant = plan.family?.(type)?.current;
1079
+ return variant === void 0 ? read : {
1080
+ ...read,
1081
+ _variant: variant
1082
+ };
1083
+ }, (i, reason) => loss?.(`${rel}/${i}`, reason));
1084
+ case "types": return typedRowsFromProd(v, (row, i) => {
1085
+ const fields = plan.types.get(row._type);
1086
+ if (!fields) {
1087
+ loss?.(`${rel}/${i}/_type`, `A stored row of item type "${row._type}", which this list no longer declares; editing the list would drop it.`);
1088
+ return row;
1089
+ }
1090
+ return mapKeys(row, fields, (p, x, k) => planFromProd(p, x, loss, `${rel}/${i}/${k}`));
1091
+ });
878
1092
  default: return v;
879
1093
  }
880
1094
  }
@@ -895,7 +1109,7 @@ function diffPaths(a, b, path = "", out = []) {
895
1109
  if ((isObj(a) || a === void 0) && (isObj(b) || b === void 0) && (a !== void 0 || b !== void 0)) {
896
1110
  const A = a ?? {};
897
1111
  const B = b ?? {};
898
- for (const k of new Set([...Object.keys(A), ...Object.keys(B)])) diffPaths(A[k], B[k], `${path}/${k}`, out);
1112
+ for (const k of /* @__PURE__ */ new Set([...Object.keys(A), ...Object.keys(B)])) diffPaths(A[k], B[k], `${path}/${k}`, out);
899
1113
  } else if (Array.isArray(a) && Array.isArray(b)) for (let i = 0; i < Math.max(a.length, b.length); i++) diffPaths(a[i], b[i], `${path}/${i}`, out);
900
1114
  else out.push(path);
901
1115
  return out;
@@ -935,17 +1149,21 @@ function validationToProd(f, shape, node, at, ctx, inTable, top) {
935
1149
  case "max": {
936
1150
  const n = v;
937
1151
  const lo = k === "min";
938
- if (lengthy) if (isWhole(n)) set(lo ? "minLength" : "maxLength", n, "validation.length", "minLength", "maxLength");
939
- else lose(ctx, p, "validation.length");
940
- else if (t === "number") {
1152
+ if (lengthy) {
1153
+ if (isWhole(n)) set(lo ? "minLength" : "maxLength", n, "validation.length", "minLength", "maxLength");
1154
+ else lose(ctx, p, "validation.length");
1155
+ } else if (t === "number") {
941
1156
  const b = node.config.validation ??= {};
942
1157
  b[k] = n;
943
1158
  } else if (t === "rating") lose(ctx, p, "rating bounds");
944
- else if (shape.kind === "zones" || shape.kind === "modular" || shape.kind === "itemType") if (props && top ? isWhole(n, lo ? 0 : 1, SLOT_ITEMS_CAP) : isWhole(n)) set(lo ? "minItems" : "maxItems", n, "validation.items", "minItems", "maxItems");
945
- else lose(ctx, p, "validation.items");
946
- else if (shape.kind === "refs" || t === "multiselect" || t === "tags") if (t === "tags" ? isWhole(n, lo ? 0 : 1, 500) : isWhole(n)) set(k, n, "validation.items", "min", "max");
947
- else lose(ctx, p, "validation.items");
948
- else lose(ctx, p, "validation.length");
1159
+ else if (shape.kind === "zones" || shape.kind === "modular" || shape.kind === "types" || shape.kind === "slot" || shape.kind === "itemType") {
1160
+ const cap = shape.kind === "slot" ? SLOT_ITEMS_CAP : LIST_ITEMS_CAP;
1161
+ if (props && top || shape.kind === "slot" ? isWhole(n, lo ? 0 : 1, cap) : isWhole(n)) set(lo ? "minItems" : "maxItems", n, "validation.items", "minItems", "maxItems");
1162
+ else lose(ctx, p, "validation.items");
1163
+ } else if (shape.kind === "refs" || t === "multiselect" || t === "tags") {
1164
+ if (t === "tags" ? isWhole(n, lo ? 0 : 1, 500) : isWhole(n)) set(k, n, "validation.items", "min", "max");
1165
+ else lose(ctx, p, "validation.items");
1166
+ } else lose(ctx, p, "validation.length");
949
1167
  break;
950
1168
  }
951
1169
  case "recommendedMax":
@@ -962,9 +1180,11 @@ function validationToProd(f, shape, node, at, ctx, inTable, top) {
962
1180
  break;
963
1181
  case "unique":
964
1182
  if (val.unique === false) node.unique = false;
965
- else if (props) if (UNIQUE_LIST_PROPS.has(t) || inTable && UNIQUE_ROW_PROPS.has(t)) node.unique = true;
966
- else cat(ctx, p, "not applicable (props)");
967
- else {
1183
+ else if (shape.kind === "types") lose(ctx, p, "validation.unique");
1184
+ else if (props) {
1185
+ if (UNIQUE_LIST_PROPS.has(t) || inTable && UNIQUE_ROW_PROPS.has(t)) node.unique = true;
1186
+ else cat(ctx, p, "not applicable (props)");
1187
+ } else {
968
1188
  const polymorphic = t === "reference" && (f.reference?.to ?? []).filter(ENTRY_TARGET).length > 1;
969
1189
  if (UNIQUE_TYPES.has(t) && !polymorphic) node.unique = true;
970
1190
  else lose(ctx, p, "validation.unique");
@@ -1000,11 +1220,15 @@ function optionsToProd(f, shape, node, at, ctx) {
1000
1220
  label: o.label,
1001
1221
  value: o.value
1002
1222
  };
1003
- if (o.icon !== void 0) if (allIcons) out.icon = icons[i];
1004
- else lose(ctx, `${at}/${i}/icon`, "option icon");
1223
+ if (o.icon !== void 0) {
1224
+ if (allIcons) out.icon = icons[i];
1225
+ else lose(ctx, `${at}/${i}/icon`, "option icon");
1226
+ }
1005
1227
  const d = o.description;
1006
- if (d !== void 0) if (typeof d === "string" && d.trim() !== "" && !/[\r\n]/.test(d) && d.length <= 140) out.description = d;
1007
- else lose(ctx, `${at}/${i}/description`, "option description");
1228
+ if (d !== void 0) {
1229
+ if (typeof d === "string" && d.trim() !== "" && !/[\r\n]/.test(d) && d.length <= 140) out.description = d;
1230
+ else lose(ctx, `${at}/${i}/description`, "option description");
1231
+ }
1008
1232
  for (const k of Object.keys(o)) if (![
1009
1233
  "value",
1010
1234
  "label",
@@ -1019,14 +1243,13 @@ function layoutToProd(f, shape, node, at, ctx) {
1019
1243
  const l = f.layout;
1020
1244
  const t = shape.type;
1021
1245
  const n = f.options?.length ?? 0;
1022
- if (l === "segmented" && t === "select") if (n >= 2 && n <= 4) node.ui.control = "segmented";
1023
- else if (n >= 5) {
1024
- node.ui.control = n <= 6 ? "radio" : "dropdown";
1025
- lose(ctx, at, "segmented>4");
1026
- } else lose(ctx, at, "layout");
1027
- else if (l === "slider" && t === "number" && f.type === "number") node.ui.control = "slider";
1246
+ if (l === "segmented" && t === "select") {
1247
+ if (n >= 2 && n <= 4) node.ui.control = "segmented";
1248
+ else if (n >= 5) cat(ctx, at, "segmented fallback");
1249
+ else lose(ctx, at, "layout");
1250
+ } else if (l === "slider" && t === "number" && f.type === "number") node.ui.control = "slider";
1028
1251
  else if (UI_CONTROLS.includes(l) && l !== "segmented" && l !== "slider" && CONTROL_TYPES[l]?.includes(t)) node.ui.control = l;
1029
- else if (l === "list" && (shape.kind === "zones" || shape.kind === "itemType" || shape.kind === "modular" || shape.kind === "refs") && f.arrayLayout === void 0) node.ui.layout = "rows";
1252
+ else if (l === "list" && (shape.kind === "zones" || shape.kind === "itemType" || shape.kind === "modular" || shape.kind === "types" || shape.kind === "refs") && f.arrayLayout === void 0) node.ui.layout = "rows";
1030
1253
  else lose(ctx, at, "layout");
1031
1254
  }
1032
1255
  /** The prod type a V2 field is stored as, for a preview path reading into it (no loss is reported). */
@@ -1061,6 +1284,7 @@ function previewPathToProd(path, children, ctx) {
1061
1284
  node = next;
1062
1285
  continue;
1063
1286
  }
1287
+ if (node.type === "table") return TABLE_PATH_KEYS.includes(segs[i]) && i === segs.length - 1 ? [...out, segs[i]].join(".") : void 0;
1064
1288
  const type = storedType(node, ctx);
1065
1289
  if (!OBJECT_VALUE_TYPES.has(type)) return void 0;
1066
1290
  const key = type === "image" ? IMAGE_PATH_KEY[segs[i]] ?? segs[i] : segs[i];
@@ -1094,10 +1318,71 @@ function previewToProd(pv, children, at, ctx) {
1094
1318
  }
1095
1319
  return out;
1096
1320
  }
1321
+ /**
1322
+ * A V2 dotted document preview path (`slides.0.heading`, `default.heading`) the backend keeps (componentPreviewRefusals
1323
+ * previewPathOfProps): its head a top-level prop, an index segment stepping into an item of a list, the rest a path
1324
+ * previewPathToProd stores unchanged (a renamed key, `image.alt` → `altText`, has no stored form here).
1325
+ */
1326
+ function documentPreviewPathOk(path, keys, fields, ctx) {
1327
+ const segs = path.split(".");
1328
+ if (segs.length < 2 || !keys.has(segs[0])) return false;
1329
+ const head = fields.find((f) => f.name === segs[0]);
1330
+ if (!head) return false;
1331
+ const polyOf = (f) => f.type === "array" && Array.isArray(f.of) && f.of.length > 1 && f.of.every((o) => o.type === "object") ? f.of : void 0;
1332
+ let node = head;
1333
+ const kept = [segs[0]];
1334
+ let poly = false;
1335
+ for (let i = 1; i < segs.length; i++) {
1336
+ const seg = segs[i];
1337
+ if (/^\d+$/.test(seg) && (node.type === "array" || node.type === "multiselect" || node.type === "tags")) continue;
1338
+ const types = polyOf(node);
1339
+ const kids = types ? types.flatMap((o) => o.fields ?? []) : previewKids(node);
1340
+ if (!kids) {
1341
+ kept.push(...segs.slice(i));
1342
+ break;
1343
+ }
1344
+ const next = kids.find((c) => c.name === seg);
1345
+ if (!next) return false;
1346
+ if (types) poly = true;
1347
+ kept.push(seg);
1348
+ node = next;
1349
+ }
1350
+ if (poly) return true;
1351
+ const clean = kept.join(".");
1352
+ return previewPathToProd(clean, fields, ctx) === clean;
1353
+ }
1354
+ /**
1355
+ * A V2 schema's document-level preview → component.preview (BE componentPreviewSchema): `title` / `subtitle` / `media`
1356
+ * kept when they name a top-level prop key or a dotted path into one (else preview_key_unknown; a catalog note: the row
1357
+ * reads the first text),
1358
+ * `fallback` trimmed and at most COMPONENT_PREVIEW_FALLBACK_MAX, `icon` a dock icon.
1359
+ */
1360
+ function componentPreviewToProd(pv, keys, fields, at, ctx) {
1361
+ const out = {};
1362
+ for (const k of Object.keys(pv)) {
1363
+ const v = pv[k];
1364
+ if (v === void 0) continue;
1365
+ if (k === "title" || k === "subtitle" || k === "media") {
1366
+ if ((keys.has(v) || documentPreviewPathOk(v, keys, fields, ctx)) && v.length <= 64) out[k] = v;
1367
+ else cat(ctx, `${at}/${k}`, "document preview path");
1368
+ } else if (k === "fallback") {
1369
+ const t = v.trim();
1370
+ if (t && t === v && t.length <= 120) out.fallback = v;
1371
+ else lose(ctx, `${at}/${k}`, "help>limit");
1372
+ } else if (k === "icon") {
1373
+ const dock = dockIconOf(v);
1374
+ if (dock) out.icon = dock;
1375
+ else lose(ctx, `${at}/${k}`, "preview icon");
1376
+ } else lose(ctx, `${at}/${k}`, "unsupported key");
1377
+ }
1378
+ return out;
1379
+ }
1097
1380
  /** A list item's name and title: the convention, else ui.itemName / ui.itemTitle (chromeUiProblems). */
1098
1381
  function itemIdentity(item, conv, node, ip, ctx) {
1099
- if (item.name !== conv.name) if (ITEM_NAME_RE.test(item.name)) node.ui.itemName = item.name;
1100
- else lose(ctx, `${ip}/name`, "item identity");
1382
+ if (item.name !== conv.name) {
1383
+ if (ITEM_NAME_RE.test(item.name)) node.ui.itemName = item.name;
1384
+ else lose(ctx, `${ip}/name`, "item identity");
1385
+ }
1101
1386
  if (item.title !== conv.title) {
1102
1387
  const t = item.title;
1103
1388
  if (t === t.trim() && t.length >= 1 && t.length <= 60) node.ui.itemTitle = t;
@@ -1111,6 +1396,21 @@ function ofToProd(f, shape, node, at, lv, ctx) {
1111
1396
  shape.items.forEach((item, i) => blockModelFor(item, `${at}/${i}`, ctx, node));
1112
1397
  return;
1113
1398
  }
1399
+ if (shape.kind === "types") return itemTypesToProd(shape.items, node, at, lv, ctx);
1400
+ if (shape.kind === "slot") {
1401
+ const of = f.of;
1402
+ for (const k of Object.keys(of)) {
1403
+ if (of[k] === void 0) continue;
1404
+ if (k === "allow" && ctx.lane === "fields") {
1405
+ node.config.blockSlugs = of.allow.map(kebab);
1406
+ of.allow.forEach((n, i) => camel(kebab(n)) !== n && lose(ctx, `${at}/allow/${i}`, "type name"));
1407
+ } else if (k === "allow") {
1408
+ if (slotAllowOk(of.allow)) node.config.allow = [...of.allow];
1409
+ else cat(ctx, `${at}/allow`, "slot allow");
1410
+ } else if (k !== "type" || of.type !== "block") lose(ctx, `${at}/${k}`, "unsupported key");
1411
+ }
1412
+ return;
1413
+ }
1114
1414
  const item = shape.item;
1115
1415
  const ip = `${at}/0`;
1116
1416
  if (shape.kind === "zones") {
@@ -1134,15 +1434,96 @@ function ofToProd(f, shape, node, at, lv, ctx) {
1134
1434
  }
1135
1435
  if (shape.kind === "itemType") node.config.itemType = shape.itemType;
1136
1436
  itemIdentity(item, conventionItem(f.name, f.title), node, ip, ctx);
1437
+ const settings = {};
1438
+ const scalars = shape.kind === "itemType" && !shape.shopify && ctx.lane === "props";
1137
1439
  for (const k of Object.keys(item)) {
1138
- if (item[k] === void 0) continue;
1440
+ const v = item[k];
1441
+ if (v === void 0) continue;
1139
1442
  const p = `${ip}/${k}`;
1140
1443
  if (k === "name" || k === "title") continue;
1141
1444
  if (k === "type") {
1445
+ if (shape.kind === "itemType" && shape.shopify) continue;
1142
1446
  if (shape.kind === "itemType" && item.type === "text") lose(ctx, p, "item settings");
1143
1447
  } else if (k === "reference" && shape.kind === "refs") referenceToConfig(item.reference, node, p, ctx);
1448
+ else if (k === "reference" && shape.kind === "itemType" && shape.shopify) continue;
1449
+ else if (scalars && k === "description" && typeof v === "string" && v.trim() === v && v.length >= 1 && v.length <= 200) settings.helpText = v;
1450
+ else if (scalars && k === "localized" && typeof v === "boolean") settings.localized = v;
1451
+ else if (scalars && k === "validation") itemValidationToProd(item, shape.itemType, settings, p, ctx);
1144
1452
  else lose(ctx, p, "item settings");
1145
1453
  }
1454
+ if (nonEmpty(settings)) node.config.item = settings;
1455
+ }
1456
+ /** A scalar list item's V2 validation → config.item: required, a text / rich text item's length, a text item's pattern. */
1457
+ function itemValidationToProd(item, itemType, out, at, ctx) {
1458
+ const val = item.validation;
1459
+ const lengthy = itemType === "text" || itemType === "richtext";
1460
+ for (const k of Object.keys(val)) {
1461
+ const v = val[k];
1462
+ if (v === void 0) continue;
1463
+ if (k === "required" && typeof v === "boolean") out.required = v;
1464
+ else if ((k === "min" || k === "max") && lengthy && isWhole(v)) out[k === "min" ? "minLength" : "maxLength"] = v;
1465
+ else if (k === "pattern" && itemType === "text" && typeof v === "string" && v.length <= 200 && compiles(v)) out.pattern = v;
1466
+ else if (k === "patternMessage" && typeof v === "string" && v.trim() !== "" && v.length <= 200) out.patternMessage = v;
1467
+ else lose(ctx, `${at}/${k}`, "item settings");
1468
+ }
1469
+ if (out.minLength !== void 0 && out.maxLength !== void 0 && out.minLength > out.maxLength) {
1470
+ delete out.minLength;
1471
+ delete out.maxLength;
1472
+ lose(ctx, `${at}/min`, "item settings");
1473
+ }
1474
+ if (out.patternMessage !== void 0 && out.pattern === void 0) {
1475
+ delete out.patternMessage;
1476
+ lose(ctx, `${at}/patternMessage`, "item settings");
1477
+ }
1478
+ }
1479
+ /** A slot's per-child settings → config.slotSettings: leaf sub-fields, at most SLOT_SETTINGS_MAX (slotSettingsHardening). */
1480
+ function slotSettingsToProd(settings, node, at, lv, ctx) {
1481
+ const kept = settings.slice(0, SLOT_SETTINGS_MAX);
1482
+ if (settings.length > SLOT_SETTINGS_MAX) settings.slice(SLOT_SETTINGS_MAX).forEach((_, i) => lose(ctx, `${at}/${SLOT_SETTINGS_MAX + i}`, "slotSettings"));
1483
+ node.slotSettings = kept.map((c, i) => convertField(c, `${at}/${i}`, lv + 1, ctx, kept, false, false, false, true));
1484
+ }
1485
+ /**
1486
+ * A polymorphic list's item types → config.itemTypes `{ name, label, fields }`: the name is what a row's `_type` holds,
1487
+ * the title its label, its icon and row preview `icon` / `preview` (paths over its own fields). One prod cannot name is
1488
+ * not stored, which moves the ones after it up a place, so the whole list is named.
1489
+ */
1490
+ function itemTypesToProd(items, node, at, lv, ctx) {
1491
+ const names = /* @__PURE__ */ new Set();
1492
+ node.itemTypes = [];
1493
+ let dropped = false;
1494
+ items.forEach((item, i) => {
1495
+ const ip = `${at}/${i}`;
1496
+ const fields = item.fields ?? [];
1497
+ const why = !ITEM_TYPE_NAME.test(item.name) || !fields.length ? "item identity" : names.has(item.name) ? "duplicate name" : void 0;
1498
+ if (why) {
1499
+ lose(ctx, ip, why);
1500
+ dropped = true;
1501
+ return;
1502
+ }
1503
+ names.add(item.name);
1504
+ const label = trunc(ctx, `${ip}/title`, item.title, ITEM_TYPE_LABEL_MAX, "item identity");
1505
+ const children = fields.map((c, j) => convertField(c, `${ip}/fields/${j}`, lv + 1, ctx, fields, false, false, true));
1506
+ const type = {
1507
+ name: item.name,
1508
+ label,
1509
+ children
1510
+ };
1511
+ for (const k of Object.keys(item)) {
1512
+ if (item[k] === void 0 || k === "name" || k === "title" || k === "type" || k === "fields") continue;
1513
+ const p = `${ip}/${k}`;
1514
+ if (k === "preview") {
1515
+ const preview = previewToProd(item.preview, fields, p, ctx);
1516
+ if (nonEmpty(preview)) type.preview = preview;
1517
+ } else if (k === "icon") {
1518
+ const dock = dockIconOf(item.icon);
1519
+ if (dock) type.icon = dock;
1520
+ else lose(ctx, p, "item icon");
1521
+ } else if (k === "openMode") cat(ctx, p, "item openMode");
1522
+ else lose(ctx, p, "item identity");
1523
+ }
1524
+ node.itemTypes.push(type);
1525
+ });
1526
+ if (dropped && node.itemTypes.length < items.length && node.itemTypes.length) lose(ctx, at, "item identity");
1146
1527
  }
1147
1528
  /**
1148
1529
  * A modular item type → the kind:'block' model its rows are stored against: the item title is the model name, its fields
@@ -1219,16 +1600,28 @@ function blockModelFor(item, at, ctx, list) {
1219
1600
  ctx.catalog.length = startCat;
1220
1601
  lose(ctx, at, "duplicate name");
1221
1602
  }
1603
+ /**
1604
+ * A reference's targets: entry models → contentModelId / polymorphic + allowedModelSlugs; page, form and collection →
1605
+ * config.targets and the filter → config.filter (backend refTargetKeys, both lanes). Read back, entry models come first,
1606
+ * then the targets.
1607
+ */
1222
1608
  function referenceToConfig(ref, node, at, ctx) {
1223
1609
  const to = ref.to ?? [];
1224
1610
  const entries = to.filter(ENTRY_TARGET);
1225
- if (entries.length < to.length) lose(ctx, entries.length || !to.length ? `${at}/to` : at, "reference target");
1611
+ const targets = to.filter((t) => REF_TARGET_KINDS.includes(t));
1612
+ const kept = entries.length + targets.length;
1613
+ if (kept < to.length) lose(ctx, kept || !to.length ? `${at}/to` : at, "shopify reference");
1614
+ else if (to.join() !== [...entries, ...targets].join()) lose(ctx, `${at}/to`, "reference order");
1226
1615
  if (entries.length === 1) node.config.contentModelId = entries[0];
1227
1616
  else if (entries.length > 1) Object.assign(node.config, {
1228
1617
  polymorphic: true,
1229
1618
  allowedModelSlugs: entries
1230
1619
  });
1231
- if (ref.filter !== void 0) lose(ctx, `${at}/filter`, "reference filter");
1620
+ if (targets.length) node.config.targets = targets;
1621
+ if (ref.filter !== void 0) {
1622
+ if (filterKept(ref.filter)) node.config.filter = ref.filter;
1623
+ else lose(ctx, `${at}/filter`, "reference filter");
1624
+ }
1232
1625
  for (const k of Object.keys(ref)) if (k !== "to" && k !== "filter") lose(ctx, `${at}/${k}`, "unsupported key");
1233
1626
  }
1234
1627
  function numericToProd(f, node, at, ctx) {
@@ -1236,7 +1629,7 @@ function numericToProd(f, node, at, ctx) {
1236
1629
  const n = f.numeric;
1237
1630
  const lo = n.min ?? 0;
1238
1631
  const hi = n.max ?? 5;
1239
- const st = n.step ?? 1;
1632
+ const st = n.step ?? (f.type === "rating" ? V2_RATING_STEP : 1);
1240
1633
  const stops = (hi - lo) / st;
1241
1634
  const ratingOk = (n.min === void 0 || Number.isFinite(n.min) && n.min >= 0) && (n.max === void 0 || isWhole(n.max, 1, 10)) && (n.step === void 0 || Number.isFinite(n.step) && n.step > 0) && lo < hi && Math.abs(stops - Math.round(stops)) < 1e-9 && Math.round(stops) <= 100;
1242
1635
  for (const k of Object.keys(n)) {
@@ -1247,13 +1640,14 @@ function numericToProd(f, node, at, ctx) {
1247
1640
  const u = v;
1248
1641
  if (f.type === "number" && typeof u === "string" && u.trim() !== "" && u.length <= 24) node.config.unit = u;
1249
1642
  else lose(ctx, p, "numeric unit");
1250
- } else if (k === "min" || k === "max" || k === "step") if (f.type === "number") node.config[k] = v;
1251
- else if (ratingOk) node.config[k] = v;
1252
- else lose(ctx, p, "rating bounds");
1253
- else lose(ctx, p, "unsupported key");
1643
+ } else if (k === "min" || k === "max" || k === "step") {
1644
+ if (f.type === "number") node.config[k] = v;
1645
+ else if (ratingOk) node.config[k] = v;
1646
+ else lose(ctx, p, "rating bounds");
1647
+ } else lose(ctx, p, "unsupported key");
1254
1648
  }
1255
1649
  }
1256
- var SCALE_UNIT_SET = new Set([
1650
+ var SCALE_UNIT_SET = /* @__PURE__ */ new Set([
1257
1651
  "px",
1258
1652
  "%",
1259
1653
  "rem",
@@ -1298,13 +1692,16 @@ function scaleToConfig(s, node, at, ctx) {
1298
1692
  const v = s[k];
1299
1693
  if (v === void 0 || k === "steps") continue;
1300
1694
  const p = `${at}/${k}`;
1301
- if (k === "units") if (unitsOk(v)) out.units = [...v];
1302
- else lose(ctx, p, "scale rules");
1303
- else if (k === "defaultUnit") if (SCALE_UNIT_SET.has(v)) out.defaultUnit = s.defaultUnit;
1304
- else lose(ctx, p, "scale rules");
1305
- else if (k === "allowCustom") if (customOk) out.allowCustom = typeof c === "boolean" ? c : JSON.parse(JSON.stringify(c));
1306
- else lose(ctx, p, "scale rules");
1307
- else if (k === "presets") {
1695
+ if (k === "units") {
1696
+ if (unitsOk(v)) out.units = [...v];
1697
+ else lose(ctx, p, "scale rules");
1698
+ } else if (k === "defaultUnit") {
1699
+ if (SCALE_UNIT_SET.has(v)) out.defaultUnit = s.defaultUnit;
1700
+ else lose(ctx, p, "scale rules");
1701
+ } else if (k === "allowCustom") {
1702
+ if (customOk) out.allowCustom = typeof c === "boolean" ? c : JSON.parse(JSON.stringify(c));
1703
+ else lose(ctx, p, "scale rules");
1704
+ } else if (k === "presets") {
1308
1705
  const presets = s.presets;
1309
1706
  if (presets.length <= 20 && presets.every((x) => {
1310
1707
  if (x.label !== void 0 && !textOk(x.label, 24)) return false;
@@ -1322,45 +1719,102 @@ function scaleToConfig(s, node, at, ctx) {
1322
1719
  }
1323
1720
  node.config.scale = out;
1324
1721
  }
1722
+ /** V2 operator → prod showIf op (the legacy `equals` / `notEmpty` forms read as is / is one of / has a value). */
1723
+ var SHOW_IF_OP = {
1724
+ is: "eq",
1725
+ isNot: "neq",
1726
+ in: "in",
1727
+ notIn: "notIn",
1728
+ empty: "empty",
1729
+ notEmpty: "notEmpty",
1730
+ gt: "gt",
1731
+ lt: "lt"
1732
+ };
1733
+ var VALUELESS = /* @__PURE__ */ new Set([
1734
+ "empty",
1735
+ "notEmpty",
1736
+ "isSet"
1737
+ ]);
1325
1738
  function conditionToShowIf(h, at, siblings, ctx, variantOk) {
1326
- const variant = variantOk && h.field === "_variant";
1327
- if (!h.field || !variant && !siblings.some((s) => s.name === h.field)) return void lose(ctx, at, "condition");
1328
- if (Object.keys(h).filter((k) => ![
1739
+ const rules = [];
1740
+ for (const [i, r] of [h, ...h.also ?? []].entries()) {
1741
+ const rule = conditionRule(r, i === 0, siblings, variantOk);
1742
+ if (!rule) return void lose(ctx, at, "condition");
1743
+ rules.push(rule);
1744
+ }
1745
+ if (h.match !== void 0 && h.match !== "all" && h.match !== "any") return void lose(ctx, at, "condition");
1746
+ return {
1747
+ match: h.match ?? "all",
1748
+ rules
1749
+ };
1750
+ }
1751
+ /** One V2 rule as a prod rule, or null when prod cannot hold it (a scope, a non-sibling, a value showIf cannot test). */
1752
+ function conditionRule(r, head, siblings, variantOk) {
1753
+ const variant = variantOk && r.field === "_variant";
1754
+ if (!r.field || r.scope !== void 0 || !variant && !siblings.some((s) => s.name === r.field)) return null;
1755
+ const allowed = head ? [
1756
+ "field",
1757
+ "equals",
1758
+ "notEmpty",
1759
+ "op",
1760
+ "value",
1761
+ "also",
1762
+ "match"
1763
+ ] : [
1329
1764
  "field",
1330
1765
  "equals",
1331
- "notEmpty"
1332
- ].includes(k) && h[k] !== void 0).length || h.equals !== void 0 === (h.notEmpty !== void 0)) return void lose(ctx, at, "condition");
1333
- if (h.notEmpty !== void 0) return h.notEmpty === true ? {
1334
- match: "all",
1335
- rules: [{
1336
- field: h.field,
1337
- op: "notEmpty"
1338
- }]
1339
- } : void lose(ctx, at, "condition");
1340
- const sibType = variant ? void 0 : siblings.find((s) => s.name === h.field)?.type;
1341
- const list = Array.isArray(h.equals) ? h.equals : [h.equals];
1766
+ "notEmpty",
1767
+ "op",
1768
+ "value"
1769
+ ];
1770
+ if (Object.keys(r).some((k) => !allowed.includes(k) && r[k] !== void 0)) return null;
1771
+ if ([
1772
+ r.op !== void 0,
1773
+ r.equals !== void 0,
1774
+ r.notEmpty !== void 0
1775
+ ].filter(Boolean).length !== 1) return null;
1776
+ if (r.notEmpty !== void 0) return r.notEmpty === true ? {
1777
+ field: r.field,
1778
+ op: "notEmpty"
1779
+ } : null;
1780
+ const target = r.op !== void 0 ? r.value : r.equals;
1781
+ const op = r.op !== void 0 ? SHOW_IF_OP[r.op] : Array.isArray(target) ? "in" : "eq";
1782
+ if (!op) return null;
1783
+ if (VALUELESS.has(op)) return r.value === void 0 ? {
1784
+ field: r.field,
1785
+ op
1786
+ } : null;
1787
+ const sibType = variant ? void 0 : siblings.find((s) => s.name === r.field)?.type;
1788
+ if (op === "gt" || op === "lt") return typeof target === "number" && Number.isFinite(target) ? {
1789
+ field: r.field,
1790
+ op,
1791
+ value: String(target)
1792
+ } : null;
1793
+ const list = Array.isArray(target) ? target : [target];
1342
1794
  const strs = [];
1343
1795
  for (const v of list) {
1344
1796
  if (![
1345
1797
  "string",
1346
1798
  "number",
1347
1799
  "boolean"
1348
- ].includes(typeof v)) return void lose(ctx, at, "condition");
1800
+ ].includes(typeof v)) return null;
1349
1801
  const s = String(v);
1350
- if (coerceRuleValue(sibType, s) !== v) return void lose(ctx, at, "condition");
1802
+ if (coerceRuleValue(sibType, s) !== v) return null;
1351
1803
  strs.push(s);
1352
1804
  }
1353
- return {
1354
- match: "all",
1355
- rules: [Array.isArray(h.equals) ? {
1356
- field: h.field,
1357
- op: "in",
1358
- value: strs
1359
- } : {
1360
- field: h.field,
1361
- op: "eq",
1362
- value: strs[0]
1363
- }]
1805
+ if (op === "in" || op === "notIn") return {
1806
+ field: r.field,
1807
+ op,
1808
+ value: strs
1809
+ };
1810
+ return Array.isArray(target) ? strs.length ? {
1811
+ field: r.field,
1812
+ op: op === "eq" ? "in" : "notIn",
1813
+ value: strs
1814
+ } : null : {
1815
+ field: r.field,
1816
+ op,
1817
+ value: strs[0]
1364
1818
  };
1365
1819
  }
1366
1820
  var nonEmpty = (o) => Object.keys(o).length ? o : void 0;
@@ -1379,7 +1833,8 @@ function common(n) {
1379
1833
  "advanced",
1380
1834
  "recommendedMax",
1381
1835
  "openMode",
1382
- "localized"
1836
+ "localized",
1837
+ "archived"
1383
1838
  ]) if (n[k] !== void 0) out[k] = n[k];
1384
1839
  return out;
1385
1840
  }
@@ -1394,6 +1849,7 @@ function emitField(n) {
1394
1849
  if (n.type === "select" && n.options) out.options = n.options;
1395
1850
  if ((n.type === "multiselect" || n.type === "tags") && n.options) config.options = n.options;
1396
1851
  if (n.type === "object") out.fields = (n.children ?? []).map(emitField);
1852
+ if (n.slotSettings) config.slotSettings = n.slotSettings.map(emitField);
1397
1853
  if (n.zoned) {
1398
1854
  const { minItems, maxItems, ...rest } = config;
1399
1855
  out.config = {
@@ -1418,6 +1874,8 @@ function emitProp(n) {
1418
1874
  const config = { ...n.config };
1419
1875
  if (n.options) config.options = n.options;
1420
1876
  if (n.children) config.fields = n.children.map(emitSubField);
1877
+ if (n.itemTypes) config.itemTypes = emitItemTypes(n.itemTypes);
1878
+ if (n.slotSettings) config.slotSettings = n.slotSettings.map(emitSubField);
1421
1879
  if (nonEmpty(config)) out.config = config;
1422
1880
  const ui = nonEmpty(n.ui);
1423
1881
  if (ui) out.ui = ui;
@@ -1431,11 +1889,22 @@ function emitSubField(n) {
1431
1889
  if (n.placeholder !== void 0) out.placeholder = n.placeholder;
1432
1890
  if (n.options) out.options = n.options;
1433
1891
  if (n.children) out.fields = n.children.map(emitSubField);
1434
- if (nonEmpty(n.config)) out.config = { ...n.config };
1892
+ const config = { ...n.config };
1893
+ if (n.itemTypes) config.itemTypes = emitItemTypes(n.itemTypes);
1894
+ if (n.slotSettings) config.slotSettings = n.slotSettings.map(emitSubField);
1895
+ if (nonEmpty(config)) out.config = config;
1435
1896
  const ui = nonEmpty(n.ui);
1436
1897
  if (ui) out.ui = ui;
1437
1898
  return out;
1438
1899
  }
1900
+ /** A polymorphic list's item types as the backend stores them: `{ name, label, fields }` (typesTableHardening). */
1901
+ var emitItemTypes = (types) => types.map((t) => ({
1902
+ name: t.name,
1903
+ label: t.label,
1904
+ fields: t.children.map(emitSubField),
1905
+ ...t.icon ? { icon: t.icon } : {},
1906
+ ...t.preview ? { preview: t.preview } : {}
1907
+ }));
1439
1908
  function fromTypeSchema(v2, target) {
1440
1909
  const ctx = {
1441
1910
  lane: target,
@@ -1448,6 +1917,10 @@ function fromTypeSchema(v2, target) {
1448
1917
  definedGroups: /* @__PURE__ */ new WeakMap(),
1449
1918
  propOrder: /* @__PURE__ */ new Map(),
1450
1919
  groupOrder: false,
1920
+ propTabs: /* @__PURE__ */ new Map(),
1921
+ definedTabs: /* @__PURE__ */ new Set(),
1922
+ keptTabs: /* @__PURE__ */ new Set(),
1923
+ declaredTabs: [],
1451
1924
  blockModels: /* @__PURE__ */ new Map()
1452
1925
  };
1453
1926
  const props = target === "props";
@@ -1471,10 +1944,10 @@ function fromTypeSchema(v2, target) {
1471
1944
  else if (!props && v2.kind !== "entry") cat(ctx, p, v2.kind === "global" ? "kind:global" : v2.kind === "page" ? "kind:page" : v2.kind === "block" ? "kind:block (fields)" : "kind:object");
1472
1945
  break;
1473
1946
  case "icon":
1474
- cat(ctx, p, "block icon");
1947
+ if (!props || !dockIconOf(v2.icon)) cat(ctx, p, "block icon");
1475
1948
  break;
1476
1949
  case "preview":
1477
- cat(ctx, p, "document preview");
1950
+ if (!props) cat(ctx, p, "document preview");
1478
1951
  break;
1479
1952
  case "variants":
1480
1953
  cat(ctx, p, "variants");
@@ -1492,7 +1965,7 @@ function fromTypeSchema(v2, target) {
1492
1965
  }
1493
1966
  }
1494
1967
  if (props) {
1495
- if (v2.groups?.length) lose(ctx, "/groups", "group (props)");
1968
+ propTabsFor(v2, ctx);
1496
1969
  propSetsFor(v2, ctx);
1497
1970
  } else fieldsetsFor(v2, ctx, fieldsets);
1498
1971
  const nodes = v2.fields.map((f, i) => convertField(f, `/fields/${i}`, 1, ctx, v2.fields, true));
@@ -1504,6 +1977,13 @@ function fromTypeSchema(v2, target) {
1504
1977
  };
1505
1978
  if (v2.description !== void 0) component.description = v2.description;
1506
1979
  if (category) component.category = category;
1980
+ const icon = v2.icon === void 0 ? void 0 : dockIconOf(v2.icon);
1981
+ if (icon) component.icon = icon;
1982
+ if (v2.preview !== void 0) {
1983
+ const preview = componentPreviewToProd(v2.preview, new Set(component.props.map((x) => x.key)), v2.fields, "/preview", ctx);
1984
+ if (Object.keys(preview).length) component.preview = preview;
1985
+ }
1986
+ if (ctx.declaredTabs.length) component.tabs = ctx.declaredTabs.map((t) => ({ ...t }));
1507
1987
  return {
1508
1988
  schema: component,
1509
1989
  blockModels: [],
@@ -1533,7 +2013,7 @@ function fieldsetsFor(v2, ctx, out) {
1533
2013
  let shifted = false;
1534
2014
  (v2.groups ?? []).forEach((g, i) => {
1535
2015
  const p = `/groups/${i}`;
1536
- const stored = g.title.slice(0, 20);
2016
+ const stored = g.title.slice(0, 40);
1537
2017
  const folded = stored.trim().toLowerCase();
1538
2018
  const drop = (why) => {
1539
2019
  if (!ctx.tabs.has(g.name) && !ctx.droppedTabs.has(g.name)) ctx.droppedTabs.set(g.name, why);
@@ -1548,7 +2028,7 @@ function fieldsetsFor(v2, ctx, out) {
1548
2028
  name: stored,
1549
2029
  kind: "tab"
1550
2030
  };
1551
- if (g.title.length > 20) lose(ctx, `${p}/title`, "group name>20");
2031
+ if (g.title.length > 40) lose(ctx, `${p}/title`, "group name>40");
1552
2032
  if (g.icon !== void 0) {
1553
2033
  const dock = dockIconOf(g.icon);
1554
2034
  if (dock) tab.icon = dock;
@@ -1585,9 +2065,10 @@ function fieldsetsFor(v2, ctx, out) {
1585
2065
  for (const k of Object.keys(fs)) {
1586
2066
  const v = fs[k];
1587
2067
  if (v === void 0 || k === "name" || k === "title") continue;
1588
- if (k === "description") if (v.length <= 500) set.description = v;
1589
- else lose(ctx, `${p}/${k}`, "help>limit");
1590
- else if (k === "collapsed" || k === "collapsible" || k === "columns" || k === "layout") set[k] = v;
2068
+ if (k === "description") {
2069
+ if (v.length <= 500) set.description = v;
2070
+ else lose(ctx, `${p}/${k}`, "help>limit");
2071
+ } else if (k === "collapsed" || k === "collapsible" || k === "columns" || k === "layout") set[k] = v;
1591
2072
  else lose(ctx, `${p}/${k}`, "unsupported key");
1592
2073
  }
1593
2074
  ctx.sets.set(fs.name, tab);
@@ -1595,25 +2076,146 @@ function fieldsetsFor(v2, ctx, out) {
1595
2076
  });
1596
2077
  }
1597
2078
  /**
2079
+ * Props lane: V2 groups → each top-level prop's `ui.tab`, defined on the first prop in it (label, icon, and `order` when
2080
+ * the V2 order differs from where each tab is first used). A field with no group sits in the V2 default group; a group
2081
+ * not stored puts its fields in the default tab; a fieldset's top-level members share the tab most of them sit in
2082
+ * (prop_group_split_tabs). No groups → no tab anywhere, so a component without tabs stores as before.
2083
+ */
2084
+ function propTabsFor(v2, ctx) {
2085
+ const groups = v2.groups ?? [];
2086
+ const kept = [];
2087
+ const dropped = /* @__PURE__ */ new Map();
2088
+ const labels = /* @__PURE__ */ new Set();
2089
+ groups.forEach((g, i) => {
2090
+ const p = `/groups/${i}`;
2091
+ const drop = (why) => {
2092
+ if (!kept.some((k) => k.g.name === g.name) && !dropped.has(g.name)) dropped.set(g.name, why);
2093
+ lose(ctx, p, why);
2094
+ };
2095
+ const label = g.title.slice(0, 40).trim();
2096
+ const folded = label.toLowerCase();
2097
+ if (kept.some((k) => k.g.name === g.name) || labels.has(folded)) return drop("group name collision");
2098
+ if (kept.length >= 8 || g.name.length < 1 || g.name.length > 40) return drop("prop tab limit");
2099
+ if (!label) return drop("group title");
2100
+ labels.add(folded);
2101
+ const tab = {
2102
+ id: g.name,
2103
+ label
2104
+ };
2105
+ if (label !== g.title.trim()) lose(ctx, `${p}/title`, "group name>40");
2106
+ else if (label !== g.title) lose(ctx, `${p}/title`, "group title");
2107
+ if (g.icon !== void 0) {
2108
+ const dock = dockIconOf(g.icon);
2109
+ if (dock) tab.icon = dock;
2110
+ else lose(ctx, `${p}/icon`, "group icon");
2111
+ }
2112
+ if (i === 0 ? g.default !== true : g.default !== void 0) lose(ctx, `${p}/default`, "group default");
2113
+ for (const k of Object.keys(g)) if (![
2114
+ "name",
2115
+ "title",
2116
+ "icon",
2117
+ "default"
2118
+ ].includes(k) && g[k] !== void 0) lose(ctx, `${p}/${k}`, "unsupported key");
2119
+ kept.push({
2120
+ g,
2121
+ i,
2122
+ tab
2123
+ });
2124
+ });
2125
+ const isKept = (name) => name !== void 0 && kept.some((k) => k.g.name === name);
2126
+ const v2Default = (groups.find((g) => g.default) ?? groups[0])?.name;
2127
+ const fallback = isKept(v2Default) ? v2Default : kept[0]?.g.name;
2128
+ const place = /* @__PURE__ */ new Map();
2129
+ for (const f of v2.fields) {
2130
+ const want = f.group ?? v2Default;
2131
+ if (isKept(want)) place.set(f, { tab: want });
2132
+ else if (f.group !== void 0 || want !== void 0) place.set(f, {
2133
+ tab: fallback,
2134
+ loss: dropped.get(want) ?? "unsupported key"
2135
+ });
2136
+ }
2137
+ if (!kept.length) {
2138
+ for (const [f, t] of place) if (f.group !== void 0) ctx.propTabs.set(f, { loss: t.loss });
2139
+ return;
2140
+ }
2141
+ const tally = /* @__PURE__ */ new Map();
2142
+ for (const f of v2.fields) {
2143
+ if (f.fieldset === void 0) continue;
2144
+ const t = tally.get(f.fieldset) ?? /* @__PURE__ */ new Map();
2145
+ t.set(place.get(f)?.tab, (t.get(place.get(f)?.tab) ?? 0) + 1);
2146
+ tally.set(f.fieldset, t);
2147
+ }
2148
+ for (const f of v2.fields) {
2149
+ const counts = f.fieldset !== void 0 ? tally.get(f.fieldset) : void 0;
2150
+ if (!counts) continue;
2151
+ const best = [...counts.entries()].sort((a, b) => b[1] - a[1])[0][0];
2152
+ const cur = place.get(f);
2153
+ if (cur?.tab !== best) place.set(f, {
2154
+ tab: best,
2155
+ loss: cur?.loss ?? "fieldset spans groups"
2156
+ });
2157
+ }
2158
+ const firstUse = [];
2159
+ for (const f of v2.fields) {
2160
+ const t = place.get(f)?.tab;
2161
+ if (t !== void 0 && !firstUse.includes(t)) firstUse.push(t);
2162
+ }
2163
+ const used = kept.filter((k) => firstUse.includes(k.g.name));
2164
+ const unused = kept.filter((k) => !firstUse.includes(k.g.name));
2165
+ for (const k of kept) ctx.keptTabs.add(k.g.name);
2166
+ const firstGap = groups.findIndex((g) => !kept.some((k) => k.g === g));
2167
+ if (firstGap >= 0 && kept.some((k) => k.i > firstGap)) {
2168
+ const g = groups[firstGap];
2169
+ lose(ctx, "/groups", dropped.get(g.name) ?? "unsupported key");
2170
+ }
2171
+ const ordered = unused.length > 0 || used.some((k, i) => k.g.name !== firstUse[i]);
2172
+ kept.forEach((k, i) => {
2173
+ if (ordered) k.tab.order = Math.min(99, i);
2174
+ });
2175
+ ctx.declaredTabs = unused.map((k) => k.tab);
2176
+ for (const [f, t] of place) {
2177
+ const k = used.find((u) => u.g.name === t.tab);
2178
+ if (k) ctx.propTabs.set(f, {
2179
+ tab: k.tab,
2180
+ loss: t.loss
2181
+ });
2182
+ else if (t.loss) ctx.propTabs.set(f, { loss: t.loss });
2183
+ }
2184
+ }
2185
+ /**
1598
2186
  * Props lane: a V2 fieldset is an inline ui.group, defined on the first prop (at each level, sub-fields too) that uses
1599
2187
  * it. A fieldset no prop uses has nothing to be defined on; when the V2 order differs from where each group is first
1600
2188
  * met (depth first, a prop before its sub-fields: how propsToTypeSchema meets them), every group carries its `order`.
1601
2189
  */
1602
2190
  function propSetsFor(v2, ctx) {
1603
2191
  const sets = v2.fieldsets ?? [];
1604
- if (!sets.length) return;
1605
2192
  const named = new Set(sets.map((s) => s.name));
1606
2193
  const firstSeen = [];
2194
+ const undeclared = [];
1607
2195
  const visit = (fields, lv) => {
1608
2196
  for (const f of fields) {
1609
- if (f.fieldset !== void 0 && named.has(f.fieldset) && !firstSeen.includes(f.fieldset)) firstSeen.push(f.fieldset);
2197
+ if (f.fieldset !== void 0 && !firstSeen.includes(f.fieldset)) {
2198
+ firstSeen.push(f.fieldset);
2199
+ if (!named.has(f.fieldset)) undeclared.push(f.fieldset);
2200
+ }
1610
2201
  if (lv >= 10) continue;
1611
2202
  const kids = previewKids(f);
1612
2203
  if (kids) visit(kids, lv + 1);
1613
2204
  }
1614
2205
  };
1615
2206
  visit(v2.fields, 1);
1616
- ctx.groupOrder = sets.filter((s) => firstSeen.includes(s.name)).some((s, i) => s.name !== firstSeen[i]);
2207
+ const derived = undeclared.map((name) => ({
2208
+ name,
2209
+ title: name
2210
+ }));
2211
+ const declaredUsed = sets.filter((s) => firstSeen.includes(s.name)).length;
2212
+ derived.forEach((s, j) => {
2213
+ ctx.propSets.set(s.name, s);
2214
+ ctx.propOrder.set(s.name, sets.length + j);
2215
+ cat(ctx, `/fieldsets/${declaredUsed + j}`, "fieldset (derived)");
2216
+ });
2217
+ if (!sets.length && !derived.length) return;
2218
+ ctx.groupOrder = [...sets, ...derived].filter((s) => firstSeen.includes(s.name)).some((s, i) => s.name !== firstSeen[i]);
1617
2219
  let gap = false;
1618
2220
  sets.forEach((s, i) => {
1619
2221
  ctx.propSets.set(s.name, s);
@@ -1654,6 +2256,10 @@ function readField(f) {
1654
2256
  delete cfg.options;
1655
2257
  }
1656
2258
  n.fields = f.fields?.map(readField);
2259
+ if (f.type === "array" && !cfg.zones && Array.isArray(cfg.fields)) {
2260
+ n.fields = [...n.fields ?? [], ...cfg.fields.map(readField)];
2261
+ delete cfg.fields;
2262
+ }
1657
2263
  if (cfg.zones) {
1658
2264
  const z = cfg.zones;
1659
2265
  n.zones = {
@@ -1717,12 +2323,32 @@ function scaleFromConfig(s, at, ctx) {
1717
2323
  }));
1718
2324
  return out;
1719
2325
  }
2326
+ /**
2327
+ * Slot children with each `_variant` written as the family member that places it (`_type`): the variant the child's
2328
+ * component already is drops, another member swaps in. A variant no member places stays, and the write names it lost.
2329
+ */
2330
+ function childVariantsToTypes(v, family) {
2331
+ if (!Array.isArray(v)) return v;
2332
+ return v.map((c) => {
2333
+ if (!isObj(c) || typeof c._variant !== "string") return c;
2334
+ const own = family(typeof c._type === "string" ? c._type : "");
2335
+ const { _variant: _gone, ...rest } = c;
2336
+ if (own?.current === c._variant) return rest;
2337
+ const member = own?.member(c._variant);
2338
+ if (!member) return c;
2339
+ return {
2340
+ ...rest,
2341
+ _type: member
2342
+ };
2343
+ });
2344
+ }
1720
2345
  /** The prod children a preview path segment may name below a node (an object's, a list's rows'), or null for a leaf. */
1721
2346
  var readKids = (n) => n.zones ? [...n.zones.nonRepeatable ?? [], ...n.zones.repeatable?.fields ?? []] : n.fields && [
1722
2347
  "object",
1723
2348
  "group",
1724
2349
  "repeater",
1725
- "table"
2350
+ "table",
2351
+ "array"
1726
2352
  ].includes(n.type) ? n.fields : null;
1727
2353
  /** A stored preview path → the V2 path: `image.altText` reads as `image.alt`, the rest as it is. */
1728
2354
  function previewPathFromProd(path, children) {
@@ -1753,12 +2379,26 @@ function previewFromProd(pv, children) {
1753
2379
  return out;
1754
2380
  }
1755
2381
  function showIfToCondition(s, siblings, at, ctx) {
1756
- const drop = (why) => void ctx.onLoss(at, "condition", why);
1757
- if (s.rules.length !== 1) return drop("V2 conditions test one sibling; a multi-rule showIf is dropped (the field always shows).");
1758
- const r = s.rules[0];
2382
+ const conds = [];
2383
+ for (const r of s.rules) {
2384
+ const c = showIfRuleToCondition(r, siblings);
2385
+ if (!c) return void ctx.onLoss(at, "condition", `V2 conditions have no "${r.op}" with this value; the field always shows.`);
2386
+ conds.push(c);
2387
+ }
2388
+ if (!conds.length) return void 0;
2389
+ const [head, ...also] = conds;
2390
+ return {
2391
+ ...head,
2392
+ ...also.length ? { also } : {},
2393
+ ...s.match === "any" && conds.length > 1 ? { match: "any" } : {}
2394
+ };
2395
+ }
2396
+ /** One prod rule as a V2 rule: eq / in / isSet keep V2's legacy forms, the rest name their `op`. */
2397
+ function showIfRuleToCondition(r, siblings) {
1759
2398
  const sib = siblings.find((x) => x.key === r.field);
1760
2399
  const t = sib?.type === "number" ? "number" : sib?.type;
1761
2400
  const read = (v) => coerceRuleValue(t, v);
2401
+ const list = (Array.isArray(r.value) ? r.value : [r.value ?? ""]).map(read);
1762
2402
  switch (r.op) {
1763
2403
  case "eq": return Array.isArray(r.value) ? {
1764
2404
  field: r.field,
@@ -1769,14 +2409,37 @@ function showIfToCondition(s, siblings, at, ctx) {
1769
2409
  };
1770
2410
  case "in": return {
1771
2411
  field: r.field,
1772
- equals: (Array.isArray(r.value) ? r.value : [r.value ?? ""]).map(read)
2412
+ equals: list
1773
2413
  };
1774
2414
  case "isSet":
1775
2415
  case "notEmpty": return {
1776
2416
  field: r.field,
1777
2417
  notEmpty: true
1778
2418
  };
1779
- default: return drop(`V2 conditions have no "${r.op}"; the field always shows.`);
2419
+ case "empty": return {
2420
+ field: r.field,
2421
+ op: "empty"
2422
+ };
2423
+ case "neq": return {
2424
+ field: r.field,
2425
+ op: "isNot",
2426
+ value: Array.isArray(r.value) ? list : read(r.value ?? "")
2427
+ };
2428
+ case "notIn": return {
2429
+ field: r.field,
2430
+ op: "notIn",
2431
+ value: list
2432
+ };
2433
+ case "gt":
2434
+ case "lt": {
2435
+ const n = Number(r.value);
2436
+ return typeof r.value === "string" && r.value.trim() !== "" && Number.isFinite(n) ? {
2437
+ field: r.field,
2438
+ op: r.op,
2439
+ value: n
2440
+ } : void 0;
2441
+ }
2442
+ default: return;
1780
2443
  }
1781
2444
  }
1782
2445
  function v2Type(n, at, ctx) {
@@ -1811,9 +2474,14 @@ function v2Type(n, at, ctx) {
1811
2474
  }
1812
2475
  }
1813
2476
  function readToV2(n, at, siblings, ctx, top, lane) {
1814
- /** a data-table prop (config.mode "data") holds one TableValue: the V2 `table` field, not a list of rows */
1815
- const dataTable = lane === "props" && isDataTableProp(n);
1816
- const type = dataTable ? "table" : v2Type(n, at, ctx);
2477
+ /** a data table holds one TableValue: the V2 `table` field, not a list of rows. A prop says so (config.mode "data");
2478
+ * a field's table is always one (BE tableFieldSchema: `mode` is optional there and can only be "data") */
2479
+ const dataTable = isDataTableProp(n) || lane === "fields" && n.type === "table";
2480
+ /** a slot prop: component instances, read as the V2 slot (its children `{ _key, _type, _settings?, ...props }`) */
2481
+ const slotProp = lane === "props" && n.type === "slot";
2482
+ /** a model slot field: inline children of the kind:'block' models config.blockSlugs names */
2483
+ const blockSlot = lane === "fields" && n.type === "slot";
2484
+ const type = dataTable ? "table" : slotProp || blockSlot ? "slot" : v2Type(n, at, ctx);
1817
2485
  const f = {
1818
2486
  name: n.key,
1819
2487
  title: n.label,
@@ -1822,7 +2490,7 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1822
2490
  const cfg = n.config;
1823
2491
  const val = {};
1824
2492
  const loss = (k, feature, reason) => ctx.onLoss(`${at}/${k}`, feature, reason);
1825
- if (n.helpText !== void 0) f.description = n.helpText;
2493
+ if (typeof n.helpText === "string") f.description = n.helpText;
1826
2494
  if (n.helpMore !== void 0) f.helpMore = n.helpMore;
1827
2495
  if (n.placeholder !== void 0) f.placeholder = n.placeholder;
1828
2496
  if (n.required !== void 0) val.required = n.required;
@@ -1831,9 +2499,18 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1831
2499
  if (n.advanced !== void 0) f.advanced = n.advanced;
1832
2500
  if (n.openMode !== void 0) f.openMode = n.openMode;
1833
2501
  if (n.readOnly === true) f.readOnly = true;
2502
+ if (n.archived === true) f.archived = true;
1834
2503
  if (n.ui.access) f.access = { ...n.ui.access };
1835
2504
  else if (n.readOnly === "developer") f.access = { edit: "schema.manage" };
1836
2505
  if (n.lockReason !== void 0) f.lockReason = n.lockReason;
2506
+ const shopify = n.readOnly === true && n.lockReason === "A Shopify product id. No Shopify integration backs a reference in BetterCMS yet, so the id is kept as it is." && (n.type === "text" || n.type === "array" && n.config.itemType === "text");
2507
+ if (shopify) {
2508
+ delete f.lockReason;
2509
+ if (n.type === "text") {
2510
+ f.type = "reference";
2511
+ f.reference = { to: ["shopify"] };
2512
+ }
2513
+ }
1837
2514
  if (n.localized !== void 0) f.localized = n.localized;
1838
2515
  if (n.ui.hidden !== void 0) f.hidden = n.ui.hidden;
1839
2516
  if (n.ui.rows !== void 0) f.rows = n.ui.rows;
@@ -1850,26 +2527,28 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1850
2527
  if (n.ui.input !== void 0) loss("ui/input", "ui.input", "Custom dock inputs are a dock feature V2 has no key for.");
1851
2528
  if (n.fieldsetId !== void 0) {
1852
2529
  const set = ctx.sets.get(n.fieldsetId);
1853
- if (set?.kind === "tab") if (top) f.group = set.id;
1854
- else loss("fieldsetId", "fieldset", "Only top-level V2 fields sit in a tab; this nested field's tab is dropped.");
1855
- else if (set) {
2530
+ if (set?.kind === "tab") {
2531
+ if (top) f.group = set.id;
2532
+ else loss("fieldsetId", "fieldset", "Only top-level V2 fields sit in a tab; this nested field's tab is dropped.");
2533
+ } else if (set) {
1856
2534
  f.fieldset = set.id;
1857
2535
  if (top && set.tab !== void 0) f.group = set.tab;
1858
2536
  } else loss("fieldsetId", "fieldset", `No fieldset "${n.fieldsetId}" exists on this model; the membership is dropped.`);
1859
2537
  }
1860
2538
  if (lane === "props" && n.ui.group) f.fieldset = n.ui.group.id;
2539
+ if (lane === "props" && n.ui.tab) f.group = n.ui.tab.id;
1861
2540
  for (const k of [
1862
2541
  "target",
1863
2542
  "stale",
1864
2543
  "origin"
1865
2544
  ]) if (n[k] !== void 0) loss(k, k, k === "target" ? "A prop's target (where it writes in the component) is binding, not schema: V2 has no key for it, so the host keeps it." : `V2 has no "${k}" key.`);
1866
- const used = new Set([
2545
+ const used = /* @__PURE__ */ new Set([
1867
2546
  "placeholder",
1868
2547
  "zones",
1869
2548
  "options",
1870
2549
  "fields"
1871
2550
  ]);
1872
- const usedUi = new Set([
2551
+ const usedUi = /* @__PURE__ */ new Set([
1873
2552
  "collapsed",
1874
2553
  "reorderable",
1875
2554
  "input",
@@ -1882,6 +2561,7 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1882
2561
  const use = (...k) => k.forEach((x) => used.add(x));
1883
2562
  const useUi = (...k) => k.forEach((x) => usedUi.add(x));
1884
2563
  if (lane === "props") useUi("group");
2564
+ if (lane === "props") useUi("tab");
1885
2565
  if ([
1886
2566
  "text",
1887
2567
  "longtext",
@@ -1902,6 +2582,10 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1902
2582
  "max",
1903
2583
  "step"
1904
2584
  ]) if (typeof cfg[k] === "number") numeric[k] = cfg[k];
2585
+ if (n.type === "rating") {
2586
+ if (numeric.step === V2_RATING_STEP) delete numeric.step;
2587
+ else if (numeric.step === void 0) numeric.step = 1;
2588
+ }
1905
2589
  const ratingDefault = type === "rating" && n.type === "number" && ratingIsDefault(numeric);
1906
2590
  if (typeof cfg.unit === "string") numeric.unit = cfg.unit;
1907
2591
  if (Object.keys(numeric).length && !ratingDefault) f.numeric = numeric;
@@ -1920,6 +2604,12 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1920
2604
  useUi("control");
1921
2605
  }
1922
2606
  if (n.type === "select") use("multiSelect");
2607
+ if (n.type === "image") {
2608
+ use("media");
2609
+ if (cfg.media === "icon") f.svgOnly = true;
2610
+ }
2611
+ if (typeof cfg.linkHref === "string" && cfg.linkHref) f.linkHref = cfg.linkHref;
2612
+ use("linkHref");
1923
2613
  if ((type === "select" || type === "multiselect" || type === "tags") && n.options) f.options = optionsFromProd(n.options);
1924
2614
  if (type === "multiselect" || type === "tags") {
1925
2615
  if (typeof cfg.min === "number") val.min = cfg.min;
@@ -1937,10 +2627,10 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1937
2627
  if (type === "date" || type === "datetime") use("timezone", "includeTime");
1938
2628
  if ((type === "date" || type === "datetime") && typeof cfg.timezone === "string") f.timezone = cfg.timezone === "site" ? "site" : `fixed:${cfg.timezone}`;
1939
2629
  if ((type === "date" || type === "datetime") && typeof cfg.timezone === "boolean") f.timezone = cfg.timezone;
1940
- if (n.type === "reference" || n.type === "multi-reference" || n.type === "multiReference") use("contentModelId", "polymorphic", "allowedModelSlugs");
2630
+ if (n.type === "reference" || n.type === "multi-reference" || n.type === "multiReference") use("contentModelId", "polymorphic", "allowedModelSlugs", "targets", "filter");
1941
2631
  if (n.type === "reference") {
1942
- const to = cfg.polymorphic ? cfg.allowedModelSlugs ?? [] : cfg.contentModelId ? [cfg.contentModelId] : [];
1943
- if (to.length) f.reference = { to };
2632
+ const ref = referenceFromConfig(cfg);
2633
+ if (ref) f.reference = ref;
1944
2634
  }
1945
2635
  if (n.type === "form") f.reference = { to: ["form"] };
1946
2636
  if (n.type === "object" || n.type === "group") {
@@ -1951,22 +2641,87 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1951
2641
  }
1952
2642
  if (n.type === "array" && n.zones?.nonRepeatable) {
1953
2643
  const nr = n.zones.nonRepeatable;
1954
- const kids = [{
1955
- name: "nonRepeatable",
1956
- title: n.label,
1957
- type: "object",
1958
- fields: nr.map((c, i) => readToV2(c, `${at}/config/zones/nonRepeatable/${i}`, nr, ctx, false, lane))
1959
- }];
1960
- if (n.zones.repeatable) kids.push(listOf(n, n.zones.repeatable.fields, "repeatable", `${at}/config/zones/repeatable`, ctx, lane));
1961
- ctx.onLoss(`${at}/config/zones`, "zone envelope", "A non-repeatable zone reads as an object with nonRepeatable (and repeatable) children, matching its stored {nonRepeatable, repeatable} value.");
1962
- Object.assign(f, {
1963
- type: "object",
1964
- fields: kids
2644
+ const fields = nr.map((c, i) => readToV2(c, `${at}/config/zones/nonRepeatable/${i}`, nr, ctx, false, lane));
2645
+ if (n.zones.repeatable) {
2646
+ const kids = [{
2647
+ name: "nonRepeatable",
2648
+ title: n.label,
2649
+ type: "object",
2650
+ fields
2651
+ }, listOf(n, n.zones.repeatable.fields, "repeatable", `${at}/config/zones/repeatable`, ctx, lane)];
2652
+ ctx.onLoss(`${at}/config/zones`, "zone envelope", "A zone pair reads as an object with nonRepeatable and repeatable children, matching its stored {nonRepeatable, repeatable} value.");
2653
+ Object.assign(f, {
2654
+ type: "object",
2655
+ fields: kids
2656
+ });
2657
+ } else {
2658
+ Object.assign(f, {
2659
+ type: "object",
2660
+ fields
2661
+ });
2662
+ ctx.zoned.add(f);
2663
+ }
2664
+ } else if (slotProp) {
2665
+ const ids = Array.isArray(cfg.componentIds) ? cfg.componentIds.filter((x) => typeof x === "string") : [];
2666
+ const names = Array.isArray(cfg.allow) ? cfg.allow.filter((x) => typeof x === "string") : [];
2667
+ f.of = ids.length ? {
2668
+ type: "block",
2669
+ allow: ids
2670
+ } : names.length ? {
2671
+ type: "block",
2672
+ allow: names
2673
+ } : { type: "block" };
2674
+ ctx.slots.set(f, cfg.multiple === true);
2675
+ const settings = Array.isArray(cfg.slotSettings) ? cfg.slotSettings.map(readProp) : [];
2676
+ if (settings.length) f.slotSettings = settings.map((c, k) => readToV2(c, `${at}/config/slotSettings/${k}`, settings, ctx, false, lane));
2677
+ if (cfg.multiple !== true) {
2678
+ val.max = 1;
2679
+ loss("config/multiple", "slot single", "A single slot holds one component instance; V2 reads it as a slot of at most one block.");
2680
+ } else {
2681
+ if (typeof cfg.minItems === "number") val.min = cfg.minItems;
2682
+ if (typeof cfg.maxItems === "number") val.max = cfg.maxItems;
2683
+ }
2684
+ use("multiple", "componentIds", "allow", "minItems", "maxItems", "slotSettings");
2685
+ } else if (blockSlot) {
2686
+ const slugs = Array.isArray(cfg.blockSlugs) ? cfg.blockSlugs.filter((x) => typeof x === "string") : [];
2687
+ f.of = slugs.length ? {
2688
+ type: "block",
2689
+ allow: slugs.map(camel)
2690
+ } : { type: "block" };
2691
+ ctx.blocks.add(f);
2692
+ const settings = Array.isArray(cfg.slotSettings) ? cfg.slotSettings.map(readField) : [];
2693
+ if (settings.length) f.slotSettings = settings.map((c, k) => readToV2(c, `${at}/config/slotSettings/${k}`, settings, ctx, false, lane));
2694
+ if (typeof cfg.minItems === "number") val.min = cfg.minItems;
2695
+ if (typeof cfg.maxItems === "number") val.max = cfg.maxItems;
2696
+ use("blockSlugs", "minItems", "maxItems", "slotSettings");
2697
+ } else if (lane === "props" && n.type === "table" && cfg.mode === "types") {
2698
+ f.of = (Array.isArray(cfg.itemTypes) ? cfg.itemTypes : []).map((t, j) => {
2699
+ const kids = (t.fields ?? []).map(readProp);
2700
+ const item = {
2701
+ name: t.name,
2702
+ title: t.label ?? t.name,
2703
+ type: "object",
2704
+ fields: kids.map((c, k) => readToV2(c, `${at}/config/itemTypes/${j}/fields/${k}`, kids, ctx, false, lane))
2705
+ };
2706
+ if (t.icon) item.icon = tablerIconOf(t.icon);
2707
+ if (t.preview) item.preview = previewFromProd(t.preview, kids);
2708
+ return item;
1965
2709
  });
2710
+ ctx.types.add(f);
2711
+ if (n.ui.itemLabel !== void 0) f.itemNoun = n.ui.itemLabel.toLowerCase();
2712
+ if (n.ui.icon) f.icon = tablerIconOf(n.ui.icon);
2713
+ const l = n.ui.layout;
2714
+ if (l === "grid" || l === "list") f.arrayLayout = l;
2715
+ else if (l === "rows") f.layout = "list";
2716
+ else if (l) loss("ui/layout", "ui.layout", `V2 has no "${l}" layout for a list of item types.`);
2717
+ if (typeof cfg.minItems === "number") val.min = cfg.minItems;
2718
+ if (typeof cfg.maxItems === "number") val.max = cfg.maxItems;
2719
+ use("mode", "itemTypes", "minItems", "maxItems");
2720
+ useUi("itemLabel", "icon", "layout");
1966
2721
  } else if (dataTable) {
1967
2722
  f.table = tableOptionsFromConfig(cfg);
1968
2723
  use("mode", "columnTypes", "lockColumns", "requireCaption", "allowGroups", "allowFootnotes", "allowNote", "minRows", "maxRows", "maxColumns", "settings", "jsonLd");
1969
- } else if (n.type === "array" && n.zones?.repeatable || n.type === "repeater" || n.type === "table") {
2724
+ } else if (n.type === "array" && (n.zones?.repeatable || !n.zones && !cfg.itemType && n.fields?.length) || n.type === "repeater" || n.type === "table") {
1970
2725
  const list = listOf(n, n.zones?.repeatable?.fields ?? n.fields ?? [], n.key, at, ctx, lane);
1971
2726
  useUi("preview", "icon", "itemLabel", "layout", "itemName", "itemTitle");
1972
2727
  if (n.zones?.repeatable) ctx.zoned.add(f);
@@ -1982,14 +2737,19 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1982
2737
  if (typeof r?.maxItems === "number") val.max = r.maxItems;
1983
2738
  if (n.type === "table") use("minItems", "maxItems");
1984
2739
  } else if (n.type === "array" && cfg.itemType) {
1985
- f.of = [{
2740
+ f.of = [shopify ? {
2741
+ ...itemOf(n),
2742
+ type: "reference",
2743
+ reference: { to: ["shopify"] }
2744
+ } : {
1986
2745
  ...itemOf(n),
1987
- type: cfg.itemType === "text" ? "string" : cfg.itemType
2746
+ type: cfg.itemType === "text" ? "string" : cfg.itemType,
2747
+ ...itemSettingsFromProd(cfg.item)
1988
2748
  }];
1989
2749
  leafListChrome(n, f, loss, "a list of scalars");
1990
2750
  if (typeof cfg.minItems === "number") val.min = cfg.minItems;
1991
2751
  if (typeof cfg.maxItems === "number") val.max = cfg.maxItems;
1992
- use("itemType", "minItems", "maxItems");
2752
+ use("itemType", "minItems", "maxItems", "item");
1993
2753
  useUi("layout", "itemLabel", "itemName", "itemTitle");
1994
2754
  } else if (n.type === "modular") {
1995
2755
  const slugs = cfg.blockSlugs ?? [];
@@ -2026,11 +2786,11 @@ function readToV2(n, at, siblings, ctx, top, lane) {
2026
2786
  use("blockSlugs", "minItems", "maxItems");
2027
2787
  useUi("itemLabel", "icon", "layout", "blocks");
2028
2788
  } else if (n.type === "multi-reference" || n.type === "multiReference") {
2029
- const to = cfg.polymorphic ? cfg.allowedModelSlugs ?? [] : cfg.contentModelId ? [cfg.contentModelId] : [];
2789
+ const ref = referenceFromConfig(cfg);
2030
2790
  f.of = [{
2031
2791
  ...itemOf(n),
2032
2792
  type: "reference",
2033
- ...to.length ? { reference: { to } } : {}
2793
+ ...ref ? { reference: ref } : {}
2034
2794
  }];
2035
2795
  leafListChrome(n, f, loss, "a list of entries");
2036
2796
  if (typeof cfg.min === "number") val.min = cfg.min;
@@ -2039,16 +2799,47 @@ function readToV2(n, at, siblings, ctx, top, lane) {
2039
2799
  useUi("layout", "itemLabel", "itemName", "itemTitle");
2040
2800
  }
2041
2801
  if (n.type === "array" && n.zones?.nonRepeatable && n.zones.repeatable) useUi("preview", "icon", "itemLabel", "layout");
2042
- for (const [k, x] of Object.entries(cfg)) if (x !== void 0 && !used.has(k)) loss(`config/${k}`, "config", `V2 has no "${k}" setting on a ${type} field.`);
2802
+ for (const [k, x] of Object.entries(cfg)) {
2803
+ if (x === void 0 || used.has(k)) continue;
2804
+ if (isHostConfigKey(n.type, k) || isHostConfigKey(type, k)) loss(`config/${k}`, "host config", CATALOG["host config"]);
2805
+ else loss(`config/${k}`, "config", `V2 has no "${k}" setting on a ${type} field.`);
2806
+ }
2043
2807
  for (const [k, x] of Object.entries(n.ui)) if (x !== void 0 && !usedUi.has(k)) loss(`ui/${k}`, `ui.${k}`, `V2 has no ui.${k} on a ${type} field.`);
2044
2808
  if (n.defaultValue !== void 0 || top && ctx.plans) {
2045
- const plan = readPlan(f, n.type, ctx.zoned, ctx.modular);
2809
+ const plan = readPlan(f, n.type, ctx.zoned, ctx.modular, ctx.types, ctx.slots, ctx.blocks);
2046
2810
  if (top) ctx.plans?.set(f.name, plan);
2047
2811
  if (n.defaultValue !== void 0) f.initialValue = planFromProd(plan, n.defaultValue, (rel, reason) => loss(`defaultValue${rel}`, "stored value", reason));
2048
2812
  }
2049
2813
  if (Object.keys(val).length) f.validation = val;
2050
2814
  return f;
2051
2815
  }
2816
+ /** config.item → the V2 item field's description, localized flag and validation (in V2's key order). */
2817
+ function itemSettingsFromProd(item) {
2818
+ if (!isObj(item)) return {};
2819
+ const s = item;
2820
+ const out = {};
2821
+ const val = {};
2822
+ if (s.required !== void 0) val.required = s.required;
2823
+ if (s.minLength !== void 0) val.min = s.minLength;
2824
+ if (s.maxLength !== void 0) val.max = s.maxLength;
2825
+ if (s.pattern !== void 0) val.pattern = s.pattern;
2826
+ if (s.patternMessage !== void 0) val.patternMessage = s.patternMessage;
2827
+ if (Object.keys(val).length) out.validation = val;
2828
+ if (s.localized !== void 0) out.localized = s.localized;
2829
+ if (s.helpText !== void 0) out.description = s.helpText;
2830
+ return out;
2831
+ }
2832
+ /** A reference config → the V2 `reference`: entry models first, then config.targets; config.filter as written. */
2833
+ function referenceFromConfig(cfg) {
2834
+ const entries = cfg.polymorphic ? cfg.allowedModelSlugs ?? [] : cfg.contentModelId ? [cfg.contentModelId] : [];
2835
+ const targets = Array.isArray(cfg.targets) ? cfg.targets.filter((t) => typeof t === "string") : [];
2836
+ const to = [...entries, ...targets];
2837
+ if (!to.length && typeof cfg.filter !== "string") return void 0;
2838
+ return {
2839
+ to,
2840
+ ...typeof cfg.filter === "string" ? { filter: cfg.filter } : {}
2841
+ };
2842
+ }
2052
2843
  /** A list of scalars or entries: its item's name and title (ui.itemName / ui.itemTitle, else the convention). */
2053
2844
  var itemOf = (n) => {
2054
2845
  const conv = conventionItem(n.key, n.label);
@@ -2100,6 +2891,9 @@ function toTypeSchema(model, opts = {}) {
2100
2891
  sets: /* @__PURE__ */ new Map(),
2101
2892
  zoned: /* @__PURE__ */ new WeakSet(),
2102
2893
  modular: /* @__PURE__ */ new WeakSet(),
2894
+ types: /* @__PURE__ */ new WeakSet(),
2895
+ slots: /* @__PURE__ */ new WeakMap(),
2896
+ blocks: /* @__PURE__ */ new WeakSet(),
2103
2897
  blockModels: new Map((opts.blockModels ?? []).map((m) => [m.slug, m])),
2104
2898
  plans: opts.plans
2105
2899
  };
@@ -2156,6 +2950,9 @@ function propsToTypeSchema(component, opts = {}) {
2156
2950
  sets: /* @__PURE__ */ new Map(),
2157
2951
  zoned: /* @__PURE__ */ new WeakSet(),
2158
2952
  modular: /* @__PURE__ */ new WeakSet(),
2953
+ types: /* @__PURE__ */ new WeakSet(),
2954
+ slots: /* @__PURE__ */ new WeakMap(),
2955
+ blocks: /* @__PURE__ */ new WeakSet(),
2159
2956
  blockModels: new Map((opts.blockModels ?? []).map((m) => [m.slug, m])),
2160
2957
  plans: opts.plans
2161
2958
  };
@@ -2186,6 +2983,23 @@ function propsToTypeSchema(component, opts = {}) {
2186
2983
  const at = new Map(fieldsets.map((s, i) => [s.name, i]));
2187
2984
  fieldsets.sort((a, b) => (order.get(a.name) ?? at.get(a.name)) - (order.get(b.name) ?? at.get(b.name)));
2188
2985
  }
2986
+ const groups = [];
2987
+ const tabOrder = /* @__PURE__ */ new Map();
2988
+ for (const t of [...nodes.map((n) => n.ui.tab), ...component.tabs ?? []]) {
2989
+ if (!t || groups.some((g) => g.name === t.id)) continue;
2990
+ const g = {
2991
+ name: t.id,
2992
+ title: t.label ?? t.id
2993
+ };
2994
+ if (t.icon) g.icon = tablerIconOf(t.icon);
2995
+ if (typeof t.order === "number") tabOrder.set(t.id, t.order);
2996
+ groups.push(g);
2997
+ }
2998
+ if (tabOrder.size) {
2999
+ const at = new Map(groups.map((g, i) => [g.name, i]));
3000
+ groups.sort((a, b) => (tabOrder.get(a.name) ?? at.get(a.name)) - (tabOrder.get(b.name) ?? at.get(b.name)));
3001
+ }
3002
+ if (groups.length) groups[0].default = true;
2189
3003
  const out = {
2190
3004
  name: camel(component.slug),
2191
3005
  title: component.name,
@@ -2194,10 +3008,13 @@ function propsToTypeSchema(component, opts = {}) {
2194
3008
  };
2195
3009
  if (component.description !== void 0 && component.description !== null) out.description = component.description;
2196
3010
  if (component.category !== void 0 && component.category !== "custom") out.category = capitalize(component.category);
3011
+ if (component.icon) out.icon = tablerIconOf(component.icon);
3012
+ if (component.preview) out.preview = previewFromProd(component.preview, nodes);
2197
3013
  if (fieldsets.length) out.fieldsets = fieldsets;
3014
+ if (groups.length) out.groups = groups;
2198
3015
  return out;
2199
3016
  }
2200
3017
  //#endregion
2201
- export { CATEGORY_TO_PROD, fromTypeSchema, leafValuePlan, planFromProd, planToProd, propsToTypeSchema, toTypeSchema };
3018
+ export { CATEGORY_TO_PROD, SHOPIFY_REF_LOCK, fromTypeSchema, leafValuePlan, planFromProd, planToProd, propsToTypeSchema, schemaSlugOf, toTypeSchema };
2202
3019
 
2203
3020
  //# sourceMappingURL=schema.js.map