efficient-web-builder 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 (340) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +2 -1
  3. package/dist/components/AddBlockRow.d.ts +1 -7
  4. package/dist/components/AddBlockRow.js +1 -1
  5. package/dist/components/AddChildRow.d.ts +1 -6
  6. package/dist/components/AddChildRow.js +1 -1
  7. package/dist/components/AddSidePanel.d.ts +2 -39
  8. package/dist/components/AddSidePanel.js +1 -1
  9. package/dist/components/BlockGapField.js +1 -1
  10. package/dist/components/BlogPostSource.js +1 -1
  11. package/dist/components/CardShadowField.js +1 -1
  12. package/dist/components/ChatSettingsPanel.js +1 -1
  13. package/dist/components/CornersField.d.ts +0 -1
  14. package/dist/components/CornersField.js +1 -1
  15. package/dist/components/DuplicateToPageMenu.js +1 -1
  16. package/dist/components/EditorScreenMount.d.ts +1 -0
  17. package/dist/components/EditorScreenMount.js +1 -0
  18. package/dist/components/EditorStoreProvider.d.ts +1 -0
  19. package/dist/components/EditorStoreProvider.js +1 -0
  20. package/dist/components/HeroImageField.js +1 -1
  21. package/dist/components/HeroSection.d.ts +2 -6
  22. package/dist/components/HeroSection.js +4 -4
  23. package/dist/components/HoverEffectField.js +1 -1
  24. package/dist/components/ImageFocusRow.js +1 -1
  25. package/dist/components/KeepHint.d.ts +5 -0
  26. package/dist/components/KeepHint.js +1 -0
  27. package/dist/components/LocationsGroupedField.js +1 -1
  28. package/dist/components/MediaSidePanel.d.ts +1 -6
  29. package/dist/components/MediaSidePanel.js +1 -1
  30. package/dist/components/MediaUploadField.js +1 -1
  31. package/dist/components/MultiSelectField.d.ts +4 -1
  32. package/dist/components/MultiSelectField.js +1 -1
  33. package/dist/components/NodeContextMenu.d.ts +2 -96
  34. package/dist/components/NodeContextMenu.js +1 -1
  35. package/dist/components/PageEditGuardNotices.d.ts +1 -1
  36. package/dist/components/PageEditGuardNotices.js +1 -1
  37. package/dist/components/PanelToggleIcon.d.ts +1 -3
  38. package/dist/components/PanelToggleIcon.js +1 -1
  39. package/dist/components/PartSettingsPanel.d.ts +5 -0
  40. package/dist/components/PartSettingsPanel.js +1 -1
  41. package/dist/components/PhotographyField.js +1 -1
  42. package/dist/components/PreviewDeviceContext.d.ts +2 -4
  43. package/dist/components/PreviewDeviceContext.js +1 -1
  44. package/dist/components/PreviewPartsContext.d.ts +1 -3
  45. package/dist/components/PreviewPartsContext.js +1 -1
  46. package/dist/components/SearchSelectField.d.ts +3 -1
  47. package/dist/components/SearchSelectField.js +1 -1
  48. package/dist/components/SectionEditor.d.ts +2 -5
  49. package/dist/components/SectionEditor.js +1 -1
  50. package/dist/components/SectionEditorShell.d.ts +1 -1
  51. package/dist/components/SectionEditorShell.js +1 -1
  52. package/dist/components/SectionThemePicker.d.ts +3 -1
  53. package/dist/components/SectionThemePicker.js +1 -1
  54. package/dist/components/ShadowColourField.js +1 -1
  55. package/dist/components/SidePanelHead.d.ts +1 -23
  56. package/dist/components/SidePanelHead.js +1 -1
  57. package/dist/components/SideSpaceField.d.ts +0 -1
  58. package/dist/components/SideSpaceField.js +1 -1
  59. package/dist/components/SiteEditorProvider.d.ts +3 -10
  60. package/dist/components/SiteEditorProvider.js +1 -1
  61. package/dist/components/SiteWidthField.d.ts +0 -1
  62. package/dist/components/SiteWidthField.js +1 -1
  63. package/dist/components/SlotChildActionsContext.d.ts +2 -13
  64. package/dist/components/SlotChildActionsContext.js +1 -1
  65. package/dist/components/SocialLinksPanel.js +1 -1
  66. package/dist/components/SourceStatus.js +1 -1
  67. package/dist/components/StandaloneSectionEditor.d.ts +1 -0
  68. package/dist/components/StandaloneSectionEditor.js +1 -1
  69. package/dist/components/StorefrontDocumentContext.d.ts +2 -67
  70. package/dist/components/StorefrontDocumentContext.js +1 -1
  71. package/dist/components/StorefrontEditor.d.ts +1 -0
  72. package/dist/components/StorefrontEditor.js +1 -1
  73. package/dist/components/StorefrontEditorChrome.d.ts +1 -1
  74. package/dist/components/StorefrontEditorChrome.js +1 -1
  75. package/dist/components/StorefrontLinkField.d.ts +9 -0
  76. package/dist/components/StorefrontLinkField.js +1 -1
  77. package/dist/components/ThemeField.js +1 -1
  78. package/dist/components/TickerSettingsPanel.js +1 -1
  79. package/dist/components/UiIcon.d.ts +2 -10
  80. package/dist/components/UiIcon.js +1 -1
  81. package/dist/components/WebsiteScreenParts.d.ts +2 -0
  82. package/dist/components/WebsiteScreenParts.js +1 -0
  83. package/dist/components/WidthFollowField.js +1 -1
  84. package/dist/components/panel-widths.d.ts +2 -12
  85. package/dist/components/panel-widths.js +1 -1
  86. package/dist/components/storefront-editor.css +125 -986
  87. package/dist/components/useFooterFocus.d.ts +2 -0
  88. package/dist/components/useFooterFocus.js +1 -0
  89. package/dist/components/useLocationsTree.d.ts +6 -0
  90. package/dist/components/useLocationsTree.js +1 -0
  91. package/dist/components/usePartBridge.d.ts +1 -9
  92. package/dist/components/usePartBridge.js +1 -1
  93. package/dist/components/usePinnedChromeSeed.d.ts +2 -0
  94. package/dist/components/usePinnedChromeSeed.js +1 -0
  95. package/dist/components/useSocialSettings.js +1 -1
  96. package/dist/components/useStorefrontBlogPosts.js +1 -1
  97. package/dist/lib/client-only.d.ts +1 -0
  98. package/dist/lib/client-only.js +1 -0
  99. package/dist/lib/content/view-tiers.d.ts +2 -6
  100. package/dist/lib/content/view-tiers.js +1 -1
  101. package/dist/lib/editor-admin-bridge.d.ts +2 -29
  102. package/dist/lib/editor-admin-bridge.js +1 -1
  103. package/dist/lib/editor-backend-error.d.ts +2 -0
  104. package/dist/lib/editor-backend-error.js +1 -0
  105. package/dist/lib/editor-blocks.d.ts +2 -2
  106. package/dist/lib/editor-chrome.d.ts +2 -52
  107. package/dist/lib/editor-chrome.js +1 -1
  108. package/dist/lib/editor-document-adapter.d.ts +3 -51
  109. package/dist/lib/editor-document-adapter.js +1 -1
  110. package/dist/lib/editor-document-addressing.d.ts +2 -0
  111. package/dist/lib/editor-document-addressing.js +1 -0
  112. package/dist/lib/editor-document-contract.d.ts +1 -0
  113. package/dist/lib/editor-document-contract.js +0 -0
  114. package/dist/lib/editor-document-hooks.d.ts +1 -15
  115. package/dist/lib/editor-document-switch.d.ts +2 -0
  116. package/dist/lib/editor-document-switch.js +1 -0
  117. package/dist/lib/editor-failure.d.ts +1 -4
  118. package/dist/lib/editor-failure.js +1 -1
  119. package/dist/lib/editor-host-inbound.d.ts +1 -1
  120. package/dist/lib/editor-host-inbound.js +1 -1
  121. package/dist/lib/editor-host.d.ts +2 -135
  122. package/dist/lib/editor-host.js +1 -1
  123. package/dist/lib/editor-key-scope.d.ts +1 -3
  124. package/dist/lib/editor-key-scope.js +1 -1
  125. package/dist/lib/editor-load-error.d.ts +1 -4
  126. package/dist/lib/editor-load-error.js +1 -1
  127. package/dist/lib/editor-media-panel.d.ts +2 -23
  128. package/dist/lib/editor-media-panel.js +1 -1
  129. package/dist/lib/editor-media-picker.d.ts +2 -12
  130. package/dist/lib/editor-media-picker.js +1 -1
  131. package/dist/lib/editor-page-lock.d.ts +1 -1
  132. package/dist/lib/editor-page-lock.js +1 -1
  133. package/dist/lib/editor-screen.d.ts +2 -0
  134. package/dist/lib/editor-screen.js +1 -0
  135. package/dist/lib/editor-section-templates.d.ts +1 -2
  136. package/dist/lib/editor-section-templates.js +1 -1
  137. package/dist/lib/editor-session-context.d.ts +1 -7
  138. package/dist/lib/editor-session-context.js +1 -1
  139. package/dist/lib/editor-session.d.ts +2 -45
  140. package/dist/lib/editor-session.js +1 -1
  141. package/dist/lib/editor-site-slug.js +1 -1
  142. package/dist/lib/editor-theme.d.ts +1 -1
  143. package/dist/lib/editor-theme.js +1 -1
  144. package/dist/lib/hero-arrows.d.ts +1 -1
  145. package/dist/lib/links/link-filters.d.ts +1 -0
  146. package/dist/lib/links/link-filters.js +0 -0
  147. package/dist/lib/links/link-picker-setup.d.ts +29 -0
  148. package/dist/lib/links/link-picker-setup.js +1 -0
  149. package/dist/lib/puck/ColourControl.d.ts +4 -1
  150. package/dist/lib/puck/ColourControl.js +1 -1
  151. package/dist/lib/puck/add-palette.d.ts +3 -9
  152. package/dist/lib/puck/add-palette.js +1 -1
  153. package/dist/lib/puck/add-target-types.d.ts +1 -0
  154. package/dist/lib/puck/add-target-types.js +0 -0
  155. package/dist/lib/puck/add-target.d.ts +2 -9
  156. package/dist/lib/puck/block-choice.d.ts +2 -0
  157. package/dist/lib/puck/block-choice.js +1 -0
  158. package/dist/lib/puck/block-offer.js +1 -1
  159. package/dist/lib/puck/block-types-in.d.ts +1 -1
  160. package/dist/lib/puck/block-types-in.js +1 -1
  161. package/dist/lib/puck/blocks/general/index.d.ts +2 -2
  162. package/dist/lib/puck/content-tree.d.ts +2 -43
  163. package/dist/lib/puck/content-tree.js +1 -1
  164. package/dist/lib/puck/controls.d.ts +11 -22
  165. package/dist/lib/puck/controls.js +1 -1
  166. package/dist/lib/puck/device-fields.d.ts +2 -24
  167. package/dist/lib/puck/device-fields.js +1 -1
  168. package/dist/lib/puck/field-buckets.d.ts +2 -0
  169. package/dist/lib/puck/field-buckets.js +1 -0
  170. package/dist/lib/puck/field-groups.d.ts +3 -3
  171. package/dist/lib/puck/field-groups.js +1 -1
  172. package/dist/lib/puck/field-markers.d.ts +2 -16
  173. package/dist/lib/puck/field-markers.js +1 -1
  174. package/dist/lib/puck/footer-block-row-name.d.ts +4 -0
  175. package/dist/lib/puck/footer-block-row-name.js +1 -0
  176. package/dist/lib/puck/footer-blocks.d.ts +2 -10
  177. package/dist/lib/puck/footer-blocks.js +1 -1
  178. package/dist/lib/puck/hidable.d.ts +2 -9
  179. package/dist/lib/puck/hidable.js +1 -1
  180. package/dist/lib/puck/palette-sections.d.ts +2 -0
  181. package/dist/lib/puck/palette-sections.js +1 -0
  182. package/dist/lib/puck/panel-fields.d.ts +2 -12
  183. package/dist/lib/puck/panel-fields.js +1 -1
  184. package/dist/lib/puck/panel-no-control-mark.d.ts +2 -7
  185. package/dist/lib/puck/panel-no-control-mark.js +1 -1
  186. package/dist/lib/puck/part-id.d.ts +1 -0
  187. package/dist/lib/puck/part-id.js +1 -0
  188. package/dist/lib/puck/pinned-seed-gate.d.ts +2 -12
  189. package/dist/lib/puck/pinned-seed-gate.js +1 -1
  190. package/dist/lib/puck/preview-click.d.ts +1 -2
  191. package/dist/lib/puck/preview-click.js +1 -1
  192. package/dist/lib/puck/preview-dom.d.ts +1 -6
  193. package/dist/lib/puck/preview-dom.js +1 -1
  194. package/dist/lib/puck/preview-events.d.ts +2 -0
  195. package/dist/lib/puck/preview-events.js +1 -0
  196. package/dist/lib/puck/preview-selection-context.d.ts +2 -19
  197. package/dist/lib/puck/preview-selection-context.js +1 -1
  198. package/dist/lib/puck/section-doc.d.ts +1 -5
  199. package/dist/lib/puck/section-doc.js +1 -1
  200. package/dist/lib/puck/section-groups.d.ts +1 -1
  201. package/dist/lib/puck/slot-child-name.d.ts +2 -8
  202. package/dist/lib/puck/slot-child-name.js +1 -1
  203. package/dist/lib/puck/slot-heal.d.ts +1 -2
  204. package/dist/lib/puck/slot-heal.js +1 -1
  205. package/dist/lib/puck/slot-section-spec.d.ts +1 -0
  206. package/dist/lib/puck/slot-section-spec.js +0 -0
  207. package/dist/lib/puck/slot-specs.d.ts +2 -13
  208. package/dist/lib/puck/style-clipboard.d.ts +1 -19
  209. package/dist/lib/puck/style-clipboard.js +1 -1
  210. package/dist/lib/puck/text-control-kinds.d.ts +2 -13
  211. package/dist/lib/puck/text-control-kinds.js +1 -1
  212. package/dist/lib/puck/text-parts.d.ts +2 -5
  213. package/dist/lib/puck/text-parts.js +1 -1
  214. package/dist/lib/puck/tree-reveal.d.ts +1 -11
  215. package/dist/lib/puck/tree-reveal.js +1 -1
  216. package/dist/lib/puck/use-section-ops.d.ts +2 -25
  217. package/dist/lib/puck/use-section-ops.js +1 -1
  218. package/dist/lib/reel.d.ts +1 -1
  219. package/dist/lib/section-template-types.d.ts +1 -21
  220. package/dist/lib/sections/new-section-node.d.ts +2 -0
  221. package/dist/lib/sections/new-section-node.js +1 -0
  222. package/dist/lib/source-rules/accent-token-name.d.ts +20 -0
  223. package/dist/lib/source-rules/accent-token-name.js +4 -0
  224. package/dist/lib/source-rules/button-corners.d.ts +13 -0
  225. package/dist/lib/source-rules/button-corners.js +2 -0
  226. package/dist/lib/source-rules/editor-chrome-note.d.ts +16 -0
  227. package/dist/lib/source-rules/editor-chrome-note.js +13 -0
  228. package/dist/lib/source-rules/editor-context-menu.d.ts +5 -0
  229. package/dist/lib/source-rules/editor-context-menu.js +4 -0
  230. package/dist/lib/source-rules/editor-puck-drag-off.d.ts +8 -0
  231. package/dist/lib/source-rules/editor-puck-drag-off.js +4 -0
  232. package/dist/lib/source-rules/efficient-easel-version.d.ts +9 -0
  233. package/dist/lib/source-rules/efficient-easel-version.js +2 -0
  234. package/dist/lib/source-rules/every-button-follows-the-button-colour.d.ts +12 -0
  235. package/dist/lib/source-rules/every-button-follows-the-button-colour.js +7 -0
  236. package/dist/lib/source-rules/flat-theme-palette.d.ts +10 -0
  237. package/dist/lib/source-rules/flat-theme-palette.js +8 -0
  238. package/dist/lib/source-rules/full-width-means-full.d.ts +8 -0
  239. package/dist/lib/source-rules/full-width-means-full.js +2 -0
  240. package/dist/lib/source-rules/heading-ceiling-sweep.d.ts +11 -0
  241. package/dist/lib/source-rules/heading-ceiling-sweep.js +8 -0
  242. package/dist/lib/source-rules/label-hints.d.ts +7 -0
  243. package/dist/lib/source-rules/label-hints.js +5 -0
  244. package/dist/lib/source-rules/label-vocabulary.d.ts +14 -0
  245. package/dist/lib/source-rules/label-vocabulary.js +5 -0
  246. package/dist/lib/source-rules/mirrored-rows-agree.d.ts +19 -0
  247. package/dist/lib/source-rules/mirrored-rows-agree.js +10 -0
  248. package/dist/lib/source-rules/no-comment-in-jsx.d.ts +9 -0
  249. package/dist/lib/source-rules/no-comment-in-jsx.js +7 -0
  250. package/dist/lib/source-rules/no-glyph-rows.d.ts +15 -0
  251. package/dist/lib/source-rules/no-glyph-rows.js +4 -0
  252. package/dist/lib/source-rules/one-colour-control.d.ts +7 -0
  253. package/dist/lib/source-rules/one-colour-control.js +2 -0
  254. package/dist/lib/source-rules/one-dropdown.d.ts +10 -0
  255. package/dist/lib/source-rules/one-dropdown.js +4 -0
  256. package/dist/lib/source-rules/one-editable-list.d.ts +16 -0
  257. package/dist/lib/source-rules/one-editable-list.js +6 -0
  258. package/dist/lib/source-rules/one-editor-list.d.ts +7 -0
  259. package/dist/lib/source-rules/one-editor-list.js +3 -0
  260. package/dist/lib/source-rules/one-right-click.d.ts +10 -0
  261. package/dist/lib/source-rules/one-right-click.js +2 -0
  262. package/dist/lib/source-rules/one-switch.d.ts +9 -0
  263. package/dist/lib/source-rules/one-switch.js +7 -0
  264. package/dist/lib/source-rules/one-up-level-link.d.ts +16 -0
  265. package/dist/lib/source-rules/one-up-level-link.js +18 -0
  266. package/dist/lib/source-rules/panel-colour-tokens.d.ts +48 -0
  267. package/dist/lib/source-rules/panel-colour-tokens.js +20 -0
  268. package/dist/lib/source-rules/panel-no-divider-lines.d.ts +14 -0
  269. package/dist/lib/source-rules/panel-no-divider-lines.js +10 -0
  270. package/dist/lib/source-rules/panel-one-arrow.d.ts +21 -0
  271. package/dist/lib/source-rules/panel-one-arrow.js +1 -0
  272. package/dist/lib/source-rules/panel-one-hover.d.ts +8 -0
  273. package/dist/lib/source-rules/panel-one-hover.js +2 -0
  274. package/dist/lib/source-rules/panel-rows-use-the-shared-row.d.ts +23 -0
  275. package/dist/lib/source-rules/panel-rows-use-the-shared-row.js +22 -0
  276. package/dist/lib/source-rules/part-slider-fills-row.d.ts +11 -0
  277. package/dist/lib/source-rules/part-slider-fills-row.js +29 -0
  278. package/dist/lib/source-rules/part-style-naming.d.ts +10 -0
  279. package/dist/lib/source-rules/part-style-naming.js +2 -0
  280. package/dist/lib/source-rules/puck-document-surfaces.d.ts +16 -0
  281. package/dist/lib/source-rules/puck-document-surfaces.js +8 -0
  282. package/dist/lib/source-rules/reduced-motion.d.ts +26 -0
  283. package/dist/lib/source-rules/reduced-motion.js +9 -0
  284. package/dist/lib/source-rules/route-content-width.d.ts +22 -0
  285. package/dist/lib/source-rules/route-content-width.js +5 -0
  286. package/dist/lib/source-rules/rule.d.ts +21 -0
  287. package/dist/lib/source-rules/rule.js +1 -0
  288. package/dist/lib/source-rules/section-theme-preview.d.ts +9 -0
  289. package/dist/lib/source-rules/section-theme-preview.js +8 -0
  290. package/dist/lib/source-rules/site-gutter.d.ts +6 -0
  291. package/dist/lib/source-rules/site-gutter.js +4 -0
  292. package/dist/lib/source-rules/site-square-by-default.d.ts +17 -0
  293. package/dist/lib/source-rules/site-square-by-default.js +3 -0
  294. package/dist/lib/source-rules/site-width.d.ts +11 -0
  295. package/dist/lib/source-rules/site-width.js +7 -0
  296. package/dist/lib/source-rules/size-token-paints.d.ts +8 -0
  297. package/dist/lib/source-rules/size-token-paints.js +11 -0
  298. package/dist/lib/source-rules/social-icons-import.d.ts +6 -0
  299. package/dist/lib/source-rules/social-icons-import.js +6 -0
  300. package/dist/lib/source-rules/source-status-is-shared.d.ts +7 -0
  301. package/dist/lib/source-rules/source-status-is-shared.js +1 -0
  302. package/dist/lib/source-rules/style-face-matches-size.d.ts +10 -0
  303. package/dist/lib/source-rules/style-face-matches-size.js +4 -0
  304. package/dist/lib/source-rules/support/accent-rename-bytes.d.ts +5 -0
  305. package/dist/lib/source-rules/support/accent-rename-bytes.js +1 -0
  306. package/dist/lib/source-rules/support/css-box-model.d.ts +5 -0
  307. package/dist/lib/source-rules/support/css-box-model.js +1 -0
  308. package/dist/lib/source-rules/support/css-length.d.ts +2 -0
  309. package/dist/lib/source-rules/support/css-length.js +1 -0
  310. package/dist/lib/source-rules/support/css-rules.d.ts +12 -0
  311. package/dist/lib/source-rules/support/css-rules.js +1 -0
  312. package/dist/lib/source-rules/support/css-source.d.ts +2 -0
  313. package/dist/lib/source-rules/support/css-source.js +3 -0
  314. package/dist/lib/source-rules/support/label-extraction.d.ts +14 -0
  315. package/dist/lib/source-rules/support/label-extraction.js +5 -0
  316. package/dist/lib/source-rules/support/panel-select-cascade.d.ts +72 -0
  317. package/dist/lib/source-rules/support/panel-select-cascade.js +2 -0
  318. package/dist/lib/source-rules/support/part-slider-call-sites.d.ts +9 -0
  319. package/dist/lib/source-rules/support/part-slider-call-sites.js +17 -0
  320. package/dist/lib/source-rules/support/registry-source.d.ts +14 -0
  321. package/dist/lib/source-rules/support/registry-source.js +3 -0
  322. package/dist/lib/source-rules/support/ts-source.d.ts +2 -0
  323. package/dist/lib/source-rules/support/ts-source.js +3 -0
  324. package/dist/lib/source-rules/theme-mode-controls-gone.d.ts +10 -0
  325. package/dist/lib/source-rules/theme-mode-controls-gone.js +5 -0
  326. package/dist/lib/source-rules/upload-intake-guard.d.ts +11 -0
  327. package/dist/lib/source-rules/upload-intake-guard.js +6 -0
  328. package/dist/lib/source-rules/word-styles.d.ts +8 -0
  329. package/dist/lib/source-rules/word-styles.js +4 -0
  330. package/dist/lib/theme-row-persist.js +1 -1
  331. package/dist/lib/theme-save-message.js +1 -1
  332. package/dist/lib/web-backend.d.ts +4 -59
  333. package/dist/lib/web-backend.js +1 -1
  334. package/dist/lib/website-document-addressing.d.ts +2 -0
  335. package/dist/lib/website-document-addressing.js +1 -0
  336. package/dist/lib/website-host-inbound.d.ts +1 -0
  337. package/dist/lib/website-host-inbound.js +1 -0
  338. package/dist/lib/website-session.d.ts +5 -0
  339. package/dist/lib/website-session.js +1 -0
  340. package/package.json +15 -5
@@ -10,7 +10,7 @@ type Node = {
10
10
  };
11
11
  export declare const sectionGroupZone: (groupId: string) => string;
12
12
  export declare function buildSectionGroup(type: SectionGroupType, id: string, sections?: Node[]): {
13
- type: "SiteHeader" | "SiteFooter";
13
+ type: "SiteFooter" | "SiteHeader";
14
14
  props: {
15
15
  id: string;
16
16
  sections: Node[];
@@ -1,8 +1,2 @@
1
- export type SlotChild = {
2
- type: string;
3
- props: Record<string, unknown>;
4
- };
5
- export declare function slotChildName(child: SlotChild, wheelName?: (code: string) => string | undefined): string;
6
- export declare function slotChildRowName(child: SlotChild, compLabel: (type: string) => string, wheelName?: (code: string) => string | undefined): string;
7
- export declare function slotChildRenameProp(fields: Record<string, unknown> | undefined): "name" | "altName" | null;
8
- export declare function rootRowName(node: SlotChild, fallback: string): string;
1
+ export { rootRowName, slotChildName, slotChildRenameProp, slotChildRowName } from "efficient-easel/screen/lib/puck/slot-child-name";
2
+ export type { SlotChild } from "efficient-easel/screen/lib/puck/slot-child-name";
@@ -1 +1 @@
1
- import{stripMarkup as s}from"../markup.js";import{hidableText as n,textFieldIdentifier as a}from"./controls.js";function m(t,r){const o=n(t.props.name);if(o)return o;const p=n(t.props.altName);if(p)return p;const i=t.props.collection?.name;if(i)return i;const u=t.props.source?.title;if(u)return u;const e=t.props.code;return e?r?.(e)??e:""}function c(t,r,o){return s(m(t,o))||r(t.type)}function x(t){if(!t)return null;for(const r of["name","altName"])if(a(t[r]))return r;return null}function N(t,r){return n(t.props?.name).trim()||r}export{N as rootRowName,m as slotChildName,x as slotChildRenameProp,c as slotChildRowName};
1
+ import{rootRowName as l,slotChildName as m,slotChildRenameProp as t,slotChildRowName as a}from"efficient-easel/screen/lib/puck/slot-child-name";export{l as rootRowName,m as slotChildName,t as slotChildRenameProp,a as slotChildRowName};
@@ -1,2 +1 @@
1
- import type { TreeItem } from "./content-tree.js";
2
- export declare function dedupeSlotChildren(item: TreeItem, slots: string[]): TreeItem["props"] | null;
1
+ export { dedupeSlotChildren } from "efficient-easel/screen/lib/puck/slot-heal";
@@ -1 +1 @@
1
- function u(r,l){let t=!1;const o={...r.props};for(const e of l){const s=Array.isArray(r.props[e])?r.props[e]:[],p=new Set,d=s.filter(c=>{const n=c?.props?.id;return!n||p.has(n)?!1:(p.add(n),!0)});d.length!==s.length&&(o[e]=d,t=!0)}return t?o:null}export{u as dedupeSlotChildren};
1
+ import{dedupeSlotChildren as o}from"efficient-easel/screen/lib/puck/slot-heal";export{o as dedupeSlotChildren};
@@ -0,0 +1 @@
1
+ export type { NodeDefaults, SlotSectionSpec } from "efficient-easel/screen/lib/puck/slot-section-spec";
File without changes
@@ -1,17 +1,6 @@
1
1
  import { type TreeItem } from "./content-tree.js";
2
- export type NodeDefaults = (type: string) => Record<string, unknown>;
3
- export interface SlotSectionSpec {
4
- slot: string;
5
- childType: string;
6
- addLabel: string;
7
- fixed?: boolean;
8
- slider?: boolean;
9
- seeds: (defaultsFor?: NodeDefaults) => Record<string, unknown>[];
10
- legacy?: (props: Record<string, unknown>) => Record<string, unknown>[];
11
- addBlocked?: (children: readonly unknown[], incoming?: unknown) => string | null;
12
- skipSeed?: (props: Record<string, unknown>) => boolean;
13
- notice?: (children: readonly unknown[]) => string | null;
14
- }
2
+ import type { NodeDefaults, SlotSectionSpec } from "./slot-section-spec.js";
3
+ export type { NodeDefaults, SlotSectionSpec } from "./slot-section-spec.js";
15
4
  export declare const SLOT_SECTION_SPECS: Record<string, SlotSectionSpec>;
16
5
  export declare function registerSlotSectionSpecs(specs: Record<string, SlotSectionSpec>): void;
17
6
  export declare function buildSlotSeedChildren(type: string, defaultsFor?: NodeDefaults): {
@@ -1,19 +1 @@
1
- import { type StyleRow } from "efficient-easel/style-clipboard";
2
- import { panelFieldsOf } from "./panel-fields.js";
3
- type Props = Record<string, unknown>;
4
- type NodeLike = {
5
- type: string;
6
- props: Props;
7
- };
8
- type ConfigLike = Parameters<typeof panelFieldsOf>[0];
9
- export declare function siteStyleScope(siteId: string | null | undefined): string;
10
- export declare function shopFieldKind(field: unknown): string | null;
11
- export declare function nodeStyleRows(config: ConfigLike, node: NodeLike): StyleRow[];
12
- export declare function copyNodeStyle(config: ConfigLike, scope: string, node: NodeLike, label: string): void;
13
- export declare function planNodePaste(config: ConfigLike, scope: string, node: NodeLike, label: string): {
14
- props: Props;
15
- changed: boolean;
16
- pasted: number;
17
- message: string;
18
- } | null;
19
- export {};
1
+ export { copyNodeStyle, nodeStyleRows, planNodePaste, shopFieldKind, siteStyleScope } from "efficient-easel/screen/lib/puck/style-clipboard";
@@ -1 +1 @@
1
- import{fieldKind as l,STYLE_KIND as p,copyStyle as f,heldStyle as y,pasteMessage as E,planPaste as S}from"efficient-easel/style-clipboard";import{anchorFields as m,foldMetaOf as _,LINK_FIELD_MARKER as h,MEDIA_FIELD_MARKER as K,TEXT_FIELD_MARKER as R}from"./controls.js";import{deviceMetaOf as A,withoutDeviceGate as a}from"./device-fields.js";import{BUCKET_KEY as c}from"./field-groups.js";import{panelFieldsOf as L}from"./panel-fields.js";const O=new Set([c.Style,c.Layout]),w=new Set(Object.values(c)),d=new Set(Object.keys(m())),D=new Set(["focal","imageFocal"]),M=[K,R,h];function v(t){return`site:${t??""}`}function b(t){if(!t||typeof t!="object")return null;const n=t,e=a(t)??t,o=n[p]??e[p];if(typeof o=="string")return o;const r=n.options??e.options;return Array.isArray(r)?`choice:${r.map(i=>String(i?.value)).sort().join("|")}`:l(e)}function F(t){const n=t,e=a(t);return M.some(o=>o in n||e&&o in e)}function u(t,n){const e=L(t,n)??{},o=[];let r=!1;for(const[s,i]of Object.entries(e)){if(w.has(s)){r=O.has(s);continue}!r||!i||typeof i!="object"||s.startsWith("_")||_(i)||d.has(s)||D.has(s)||F(i)||i.type!=="slot"&&o.push({path:[s],kind:b(i),counted:A(i)?.tier!=="mobile"})}return o}function x(t,n,e,o){f({scope:n,type:e.type,label:o,rows:u(t,e),props:e.props})}function C(t,n,e,o){const r=y(n);if(!r)return null;const s=S(r,{type:e.type,rows:u(t,e),props:e.props});return{...s,message:E(s,r.sourceLabel,o)}}export{x as copyNodeStyle,u as nodeStyleRows,C as planNodePaste,b as shopFieldKind,v as siteStyleScope};
1
+ import{copyNodeStyle as t,nodeStyleRows as d,planNodePaste as l,shopFieldKind as p,siteStyleScope as s}from"efficient-easel/screen/lib/puck/style-clipboard";export{t as copyNodeStyle,d as nodeStyleRows,l as planNodePaste,p as shopFieldKind,s as siteStyleScope};
@@ -1,13 +1,2 @@
1
- export declare const TEXT_CONTROL_KINDS: {
2
- readonly text: "scalar";
3
- readonly textarea: "scalar";
4
- readonly plainText: "scalar";
5
- readonly copyText: "scalar";
6
- readonly plainTextarea: "scalar";
7
- readonly textList: "list";
8
- };
9
- export type TextControlKind = keyof typeof TEXT_CONTROL_KINDS;
10
- export type ScalarTextControlKind = {
11
- [K in TextControlKind]: (typeof TEXT_CONTROL_KINDS)[K] extends "scalar" ? K : never;
12
- }[TextControlKind];
13
- export declare const SCALAR_TEXT_CONTROLS: readonly ScalarTextControlKind[];
1
+ export { SCALAR_TEXT_CONTROLS, TEXT_CONTROL_KINDS } from "efficient-easel/screen/lib/puck/text-control-kinds";
2
+ export type { ScalarTextControlKind, TextControlKind } from "efficient-easel/screen/lib/puck/text-control-kinds";
@@ -1 +1 @@
1
- const t={text:"scalar",textarea:"scalar",plainText:"scalar",copyText:"scalar",plainTextarea:"scalar",textList:"list"},e=Object.entries(t).filter(([,a])=>a==="scalar").map(([a])=>a);export{e as SCALAR_TEXT_CONTROLS,t as TEXT_CONTROL_KINDS};
1
+ import{SCALAR_TEXT_CONTROLS as _,TEXT_CONTROL_KINDS as C}from"efficient-easel/screen/lib/puck/text-control-kinds";export{_ as SCALAR_TEXT_CONTROLS,C as TEXT_CONTROL_KINDS};
@@ -1,3 +1,4 @@
1
+ import { decodePartId, encodePartId } from "./part-id.js";
1
2
  import { type TextFieldMeta } from "./field-markers.js";
2
3
  import { type PartRow } from "./part-rows.js";
3
4
  export declare const PART_ATTR = "data-sf-part";
@@ -9,11 +10,7 @@ export type PartNeedle = {
9
10
  texts: string[];
10
11
  row?: PartRow;
11
12
  };
12
- export declare function encodePartId(nodeId: string, propKey: string | null): string;
13
- export declare function decodePartId(partId: string): {
14
- nodeId: string;
15
- propKey: string | null;
16
- } | null;
13
+ export { decodePartId, encodePartId };
17
14
  export declare function partOpenOn(selectedPartId: string | null, nodeId: string): boolean;
18
15
  export declare function normalizePartText(value: string): string;
19
16
  export declare function holdsAuthoredText(field: unknown): boolean;
@@ -1 +1 @@
1
- import{stripMarkup as y}from"../markup.js";import{LINK_FIELD_MARKER as h,SCAFFOLD_FIELD_MARKER as R,TEXT_FIELD_MARKER as u}from"./field-markers.js";import{readHidable as b}from"./hidable.js";import{SCALAR_TEXT_CONTROLS as P}from"./text-control-kinds.js";import{isStructuralTextProp as A}from"./neutralization-contracts.js";import{partRowTexts as g}from"./part-rows.js";const C="data-sf-part",k="data-sf-part-selected",z="data-sf-part-kind",T="::",_=2;function X(t,e){return`${t}${T}${e??""}`}function L(t){const e=t.indexOf(T);return e<=0?null:{nodeId:t.slice(0,e),propKey:t.slice(e+T.length)||null}}function $(t,e){if(!t)return!1;const r=L(t);return!!r?.propKey&&r.nodeId===e}function E(t){return y(t).replace(/\s+/gu,"")}function l(t,e,r){const n=t[u];if(n){if(n.control==="textList"){const o=n.itemKey??"text";if(!Array.isArray(e))return;for(const c of e){const i=c?.[o];typeof i=="string"&&r.push(i)}return}const{text:s}=b(e);s?r.push(s):n.placeholder&&r.push(n.placeholder);return}if(!t[h]){if(t.type==="text"||t.type==="textarea"){typeof e=="string"&&e&&r.push(e);return}if(t.arrayFields&&Array.isArray(e)){for(const s of e)for(const[o,c]of Object.entries(t.arrayFields))l(c,s?.[o],r);return}if(t.objectFields&&e&&typeof e=="object")for(const[s,o]of Object.entries(t.objectFields))l(o,e[s],r)}}function H(t){if(!t||typeof t!="object")return!1;const e=t;return e[h]?!1:!!e[u]||e.type==="text"||e.type==="textarea"}function O(t,e){const r=t[u];return r?.label?r.label:typeof t.label=="string"?t.label:e}function j(t){const e=t.indexOf(" (");return(e>0?t.slice(0,e):t).trim()}function B(t,e){if(!t)return[];const r=[];for(const[n,s]of Object.entries(t)){if(!s||typeof s!="object"||A(n))continue;const o=s,c=[];l(o,e?.[n],c),!c.length&&o[R]!==void 0&&l(o,o[R],c);const i=O(o,n),a=new Set;for(const{row:d,text:f}of g(o,e?.[n])){const m=E(f);m.length<_||(a.add(m),r.push({key:n,label:i,texts:[m],row:d}))}const p=new Set(a),x=[];for(const d of c){const f=E(d);f.length<_||p.has(f)||(p.add(f),x.push(f))}x.length&&r.push({key:n,label:i,texts:x})}return r}const F=P;function w(t,e){if(!e||typeof e!="object"||A(t))return null;const r=e[u];return!r||!F.includes(r.control)?null:r}function Y(t,e){const r=w(t,e);return r!==null&&r.identifier!==!0}function q(t,e){if(!t)return[];const r=[];for(const[n,s]of Object.entries(t)){const o=w(n,s);if(!o)continue;const{text:c,hidden:i}=b(e?.[n]),a=c||o.placeholder||"",p=y(a).replace(/\s+/gu," ").trim();r.push({key:n,label:j(O(s,n)),preview:p,offPage:o.offPage===!0,identifier:o.identifier===!0,hidden:!!i})}return r}export{C as PART_ATTR,z as PART_KIND_ATTR,k as PART_SELECTED_ATTR,L as decodePartId,X as encodePartId,w as entryPartMetaOf,Y as fieldDrawsEntryRow,H as holdsAuthoredText,E as normalizePartText,j as partHeadLabel,O as partLabel,B as partNeedles,$ as partOpenOn,q as sectionTextParts};
1
+ import{decodePartId as T,encodePartId as L}from"./part-id.js";import{stripMarkup as y}from"../markup.js";import{LINK_FIELD_MARKER as h,SCAFFOLD_FIELD_MARKER as b,TEXT_FIELD_MARKER as u}from"./field-markers.js";import{readHidable as R}from"./hidable.js";import{SCALAR_TEXT_CONTROLS as g}from"./text-control-kinds.js";import{isStructuralTextProp as A}from"./neutralization-contracts.js";import{partRowTexts as P}from"./part-rows.js";const k="data-sf-part",z="data-sf-part-selected",X="data-sf-part-kind",_=2;function H(t,e){if(!t)return!1;const r=T(t);return!!r?.propKey&&r.nodeId===e}function E(t){return y(t).replace(/\s+/gu,"")}function x(t,e,r){const n=t[u];if(n){if(n.control==="textList"){const o=n.itemKey??"text";if(!Array.isArray(e))return;for(const c of e){const i=c?.[o];typeof i=="string"&&r.push(i)}return}const{text:s}=R(e);s?r.push(s):n.placeholder&&r.push(n.placeholder);return}if(!t[h]){if(t.type==="text"||t.type==="textarea"){typeof e=="string"&&e&&r.push(e);return}if(t.arrayFields&&Array.isArray(e)){for(const s of e)for(const[o,c]of Object.entries(t.arrayFields))x(c,s?.[o],r);return}if(t.objectFields&&e&&typeof e=="object")for(const[s,o]of Object.entries(t.objectFields))x(o,e[s],r)}}function B(t){if(!t||typeof t!="object")return!1;const e=t;return e[h]?!1:!!e[u]||e.type==="text"||e.type==="textarea"}function w(t,e){const r=t[u];return r?.label?r.label:typeof t.label=="string"?t.label:e}function j(t){const e=t.indexOf(" (");return(e>0?t.slice(0,e):t).trim()}function Y(t,e){if(!t)return[];const r=[];for(const[n,s]of Object.entries(t)){if(!s||typeof s!="object"||A(n))continue;const o=s,c=[];x(o,e?.[n],c),!c.length&&o[b]!==void 0&&x(o,o[b],c);const i=w(o,n),a=new Set;for(const{row:d,text:f}of P(o,e?.[n])){const m=E(f);m.length<_||(a.add(m),r.push({key:n,label:i,texts:[m],row:d}))}const p=new Set(a),l=[];for(const d of c){const f=E(d);f.length<_||p.has(f)||(p.add(f),l.push(f))}l.length&&r.push({key:n,label:i,texts:l})}return r}const F=g;function O(t,e){if(!e||typeof e!="object"||A(t))return null;const r=e[u];return!r||!F.includes(r.control)?null:r}function q(t,e){const r=O(t,e);return r!==null&&r.identifier!==!0}function G(t,e){if(!t)return[];const r=[];for(const[n,s]of Object.entries(t)){const o=O(n,s);if(!o)continue;const{text:c,hidden:i}=R(e?.[n]),a=c||o.placeholder||"",p=y(a).replace(/\s+/gu," ").trim();r.push({key:n,label:j(w(s,n)),preview:p,offPage:o.offPage===!0,identifier:o.identifier===!0,hidden:!!i})}return r}export{k as PART_ATTR,X as PART_KIND_ATTR,z as PART_SELECTED_ATTR,T as decodePartId,L as encodePartId,O as entryPartMetaOf,q as fieldDrawsEntryRow,B as holdsAuthoredText,E as normalizePartText,j as partHeadLabel,w as partLabel,Y as partNeedles,H as partOpenOn,G as sectionTextParts};
@@ -1,11 +1 @@
1
- type Selector = {
2
- index: number;
3
- zone?: string;
4
- } | null;
5
- export declare function sameSelector(a: Selector, b: Selector, rootZone: string): boolean;
6
- export declare function revealSelectedTreeRow(doc?: Document | undefined): boolean;
7
- export declare function markedTreeNodeId(mode: {
8
- kind: string;
9
- nodeId?: string;
10
- }, selectedId: string | null | undefined): string | null;
11
- export {};
1
+ export { markedTreeNodeId, revealSelectedTreeRow, sameSelector } from "efficient-easel/screen/lib/puck/tree-reveal";
@@ -1 +1 @@
1
- const o=".es-tree",i=".es-tree-row.is-selected";function l(e,n,t){return!e||!n?!1:e.index===n.index&&(e.zone??t)===(n.zone??t)}function u(e,n){if(typeof e.getBoundingClientRect!="function"||typeof n.getBoundingClientRect!="function")return!1;const t=e.getBoundingClientRect(),r=n.getBoundingClientRect();return!t.height||!r.height?!1:r.top>=t.top&&r.bottom<=t.bottom}function c(e=typeof document>"u"?void 0:document){const n=e?.querySelector(o),t=n?.querySelector(i);return t?(u(n,t)||t.scrollIntoView({block:"center",inline:"nearest"}),!0):!1}function f(e,n){return e.kind==="part"?e.nodeId??null:e.kind!=="puck"||n==null?null:String(n)}export{f as markedTreeNodeId,c as revealSelectedTreeRow,l as sameSelector};
1
+ import{markedTreeNodeId as o,revealSelectedTreeRow as d,sameSelector as a}from"efficient-easel/screen/lib/puck/tree-reveal";export{o as markedTreeNodeId,d as revealSelectedTreeRow,a as sameSelector};
@@ -1,25 +1,2 @@
1
- import type { Data } from "efficient-easel/blocks";
2
- import { type TreeItem } from "./content-tree.js";
3
- export type NodeSelector = {
4
- index: number;
5
- zone?: string;
6
- } | null;
7
- type ComponentData = TreeItem;
8
- export type SectionOps = {
9
- insertComponent: (zone: string, type: string, index: number) => string;
10
- insertSection: (type: string, index: number) => string;
11
- replaceComponent: (zone: string, index: number, data: ComponentData) => void;
12
- seedChild: (zone: string, index: number, childType: string, props: Record<string, unknown>) => void;
13
- insertNode: (zone: string, index: number, node: TreeItem) => string;
14
- removeComponent: (zone: string, index: number) => void;
15
- reorderComponent: (zone: string, sourceIndex: number, destinationIndex: number) => void;
16
- duplicateComponent: (zone: string, index: number) => void;
17
- select: (selector: NodeSelector) => void;
18
- setDocument: (data: Data) => void;
19
- commitDocument: (data: Data) => void;
20
- };
21
- export type SectionOpsOptions = {
22
- readOnly?: boolean;
23
- };
24
- export declare function useSectionOps(options?: SectionOpsOptions): SectionOps;
25
- export {};
1
+ export { useSectionOps } from "efficient-easel/screen/lib/puck/use-section-ops";
2
+ export type { NodeSelector, SectionOps, SectionOpsOptions } from "efficient-easel/screen/lib/puck/use-section-ops";
@@ -1 +1 @@
1
- "use client";import{useMemo as d}from"react";import{useEasel as m}from"efficient-easel/easel";import{ROOT_ZONE as a,newNodeId as y}from"./content-tree.js";function O(u={}){const{dispatch:n}=m(s=>s),o=u.readOnly===!0;return d(()=>{const s=(e,t,r)=>{const i=y(t);return o||n({type:"insert",componentType:t,destinationZone:e,destinationIndex:r,id:i}),i},p=(e,t,r)=>{o||n({type:"replace",destinationZone:e,destinationIndex:t,data:r})};return{insertComponent:s,insertSection:(e,t)=>s(a,e,t),replaceComponent:p,seedChild:(e,t,r,i)=>{const c=s(e,r,t);p(e,t,{type:r,props:{id:c,...i}})},insertNode:(e,t,r)=>{const i=s(e,r.type,t);return p(e,t,{type:r.type,props:{...r.props,id:i}}),i},removeComponent:(e,t)=>{o||n({type:"remove",index:t,zone:e})},reorderComponent:(e,t,r)=>{o||n({type:"reorder",sourceIndex:t,destinationIndex:r,destinationZone:e})},duplicateComponent:(e,t)=>{o||n({type:"duplicate",sourceIndex:t,sourceZone:e})},select:e=>n({type:"setUi",ui:{itemSelector:e}}),setDocument:e=>{o||n({type:"setData",data:e})},commitDocument:e=>{o||n({type:"setData",data:()=>e,recordHistory:!0})}}},[n,o])}export{O as useSectionOps};
1
+ "use client";import{useSectionOps as s}from"efficient-easel/screen/lib/puck/use-section-ops";export{s as useSectionOps};
@@ -1,7 +1,7 @@
1
1
  import { type HeroGroupPlacement } from "./hero-group-placement.js";
2
2
  export declare const REEL_PLACEMENTS: readonly ["top-left", "top-center", "top-right", "left", "center", "right", "bottom-left", "bottom-center", "bottom-right"];
3
3
  export type ReelPlacement = HeroGroupPlacement;
4
- export declare const REEL_PLACEMENT_LABELS: Readonly<Record<"left" | "right" | "top-left" | "top-center" | "top-right" | "center" | "bottom-left" | "bottom-center" | "bottom-right", string>>;
4
+ export declare const REEL_PLACEMENT_LABELS: Readonly<Record<"left" | "center" | "top-left" | "top-center" | "top-right" | "right" | "bottom-left" | "bottom-center" | "bottom-right", string>>;
5
5
  export declare const REEL_PLACEMENT_DEFAULT: ReelPlacement;
6
6
  export declare const REEL_SHAPES: readonly ["cards", "dots", "numbers", "bar", "tabs"];
7
7
  export type ReelShape = (typeof REEL_SHAPES)[number];
@@ -1,21 +1 @@
1
- import type { TreeItem } from "./puck/content-tree.js";
2
- export interface SectionTemplateRecord {
3
- id: string;
4
- name: string;
5
- component_type: string;
6
- node: TreeItem | null;
7
- created_at: string;
8
- updated_at: string;
9
- }
10
- export interface SectionTemplate extends SectionTemplateRecord {
11
- node: TreeItem;
12
- }
13
- export interface SectionTemplateStore {
14
- list(): Promise<SectionTemplate[]>;
15
- get(id: string): Promise<SectionTemplateRecord | null>;
16
- ensure(id: string, name: string, node: TreeItem): Promise<SectionTemplate>;
17
- save(name: string, node: TreeItem): Promise<SectionTemplate>;
18
- update(id: string, node: TreeItem): Promise<SectionTemplate>;
19
- rename(id: string, name: string): Promise<SectionTemplateRecord>;
20
- remove(id: string): Promise<void>;
21
- }
1
+ export type { SectionTemplate, SectionTemplateRecord, SectionTemplateStore } from "efficient-easel/screen/lib/section-template-types";
@@ -0,0 +1,2 @@
1
+ import type { ScreenNewNode, ScreenNodeMaker } from "../editor-screen.js";
2
+ export declare function newSectionNode(type: string, { nodeDefaults, newNodeId, sectionLabel, blockIsOffered }: ScreenNodeMaker): ScreenNewNode | null;
@@ -0,0 +1 @@
1
+ import{BOX_TYPE as m}from"../puck/container-blocks.js";import{SLOT_SECTION_SPECS as h}from"../puck/slot-specs.js";import{eyebrowLookFor as a}from"./section-eyebrow-look.js";import{SECTION_HEAD_TYPE as n,SPLITTABLE_SECTIONS as r,canSplitHead as p}from"./section-head-placement.js";import{presetFor as s}from"./section-presets.js";function f(o,{nodeDefaults:i,newNodeId:S,sectionLabel:e,blockIsOffered:c}){const t=s(o);return t?t(i,S):p(o)&&c(m)&&c(n)?{type:m,props:{...i(m),id:S(m),direction:"column",name:`${e(o)} section`,content:[{type:n,props:{...i(n),id:S(n),...r[o]?.headStyle?{headStyle:r[o].headStyle}:{},...r[o]?.headRowGap?{rowGap:r[o].headRowGap}:{},...r[o]?.headDescriptionSize?{descriptionSize:r[o].headDescriptionSize}:{},...a(o)!==a(n)?{eyebrowLook:a(o)}:{}}},{type:o,props:{...i(o),id:S(o),headPlacement:"own-box"}}]}}:h[o]?.fixed===!0?{type:o,props:{...i(o),id:S(o),layout:"boxes"}}:null}export{f as newSectionNode};
@@ -0,0 +1,20 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export declare const ACCENT_TOKEN_EXTENSIONS: readonly string[];
3
+ export type KeptName = {
4
+ readonly path: string;
5
+ readonly pattern: RegExp;
6
+ readonly why: string;
7
+ };
8
+ export type AccentTokenNameOptions = {
9
+ readonly minFiles: number;
10
+ readonly exempt?: Readonly<Record<string, string>>;
11
+ readonly keptNames?: readonly KeptName[];
12
+ };
13
+ export type AccentTokenViolation = {
14
+ readonly file: string;
15
+ readonly line: number;
16
+ readonly text: string;
17
+ readonly kind: "custom-property" | "utility-class";
18
+ };
19
+ export declare function goldTokenUses(file: SourceFile): AccentTokenViolation[];
20
+ export declare function accentTokenName(files: readonly SourceFile[], options: AccentTokenNameOptions): RuleReport;
@@ -0,0 +1,4 @@
1
+ import{ruleChecks as x}from"./rule.js";import{rootsLongestFirst as w}from"./support/accent-rename-bytes.js";const f=[".ts",".tsx",".css",".mjs",".js"],$=/--[a-z0-9-]*gold[a-z0-9-]*/gi,b=new RegExp(String.raw`(?<![a-z0-9-])`+String.raw`(?:[a-z0-9@_.\-\[\]\/]+:)*`+`(?:${w().join("|")})`+String.raw`-gold(?:-2)?`+String.raw`(?:\/(?:\d{1,3}|\[[^\]\s]*\]))?`+String.raw`(?![a-z0-9-])`,"gi");function p(n){const s=[];return n.text.split(`
2
+ `).forEach((o,h)=>{for(const[c,l]of[[$,"custom-property"],[b,"utility-class"]]){c.lastIndex=0;let i;for(;(i=c.exec(o))!==null;)s.push({file:n.path,line:h+1,text:i[0].trim(),kind:l})}}),s}const u=n=>p({path:"fixture.tsx",text:n});function k(n,s){const{check:o,report:h}=x("accent token name"),c=s.exempt??{};{const e=[["bare token"," --gold: var(--gold-base);"],["second stop"," --gold-2: var(--theme-accent-2);"],["dim variant"," --gold-dim: #9a8050;"],["theme bridge"," --color-gold: var(--gold);"],["var() reference"," outline: 2px solid var(--gold);"],["var() with a fallback",'className="text-[var(--gold,#c8a24c)]"'],["text utility",'<span className="text-gold">'],["bg utility",'<div className="bg-gold px-4">'],["directional border",'className="border-t-2 border-t-gold"'],["opacity modifier",'className="bg-gold/10"'],["variant prefix",'className="hover:text-gold"'],["second-stop utility",'className="bg-gold-2"'],["arbitrary opacity",'className="bg-gold/[0.08]"'],["variant + arbitrary opacity",'className="hover:bg-gold/[0.04]"'],["named group variant",'className="group-hover/kf:text-gold"'],["inside a regex literal","assert.match(html, /whitespace-nowrap text-gold/)"]],t=e.filter(([,r])=>u(r).length===0);o(`the scanner flags every shape the rename must remove (${e.length} shapes)`,t.length===0,`it walked past: ${t.map(([r,m])=>`${r} (${m})`).join("; ")}`);const a=[["the new token"," --accent: var(--accent-base);"],["the new utility",'<span className="text-accent">'],["PayPal's own API value",'style: { color: "gold", label: "paypal" }'],["a product finish name",'{ label: "Gloss Gold", value: "gloss-gold" }'],["the phrase renderer",'import { renderGold } from "./goldPhrases";'],["prose in a docblock"," * a gold-italic accent word, dark-on-gold"],["a longer word that starts the same",'className="text-golden-ratio"'],["a hyphen-joined word ending in gold","// see the dark-on-gold treatment"]],g=a.filter(([,r])=>u(r).length>0);o(`the scanner stays quiet on things that merely share the word (${a.length} shapes)`,g.length===0,`it flagged: ${g.map(([r,m])=>`${r} (${m})`).join("; ")}`)}const l=n.filter(e=>f.some(t=>e.path.endsWith(t)));o(`read the source (${l.length} ${f.join("/")} files, at least ${s.minFiles})`,l.length>=s.minFiles,"fewer means the walk lost a folder, and an absence over a missing folder is free");const i=l.filter(e=>!(e.path in c)).flatMap(e=>p(e)),d=new Map;for(const e of i)d.set(e.file,[...d.get(e.file)??[],e]);o(`no file names the colour gold \u2014 the colour is called "accent" (${l.length} files)`,i.length===0,`${i.length} reference(s) to the old colour name across ${d.size} file(s):
3
+ `+[...d.entries()].sort((e,t)=>t[1].length-e[1].length).map(([e,t])=>` ${e} (${t.length}): ${[...new Set(t.map(a=>a.text))].slice(0,6).join(", ")}`).join(`
4
+ `));for(const[e,t]of Object.entries(c)){const a=l.find(g=>g.path===e);o(`the exemption is still real: ${e} (${t})`,!!a&&p(a).length>0,"the file is gone or no longer names the old colour \u2014 remove the stale entry")}for(const e of s.keptNames??[]){const t=n.find(a=>a.path===e.path);o(`a deliberate exclusion is still here: ${e.path} keeps ${e.pattern}`,!!t&&t.text.search(e.pattern)!==-1,t?e.why:`${e.path} was not among the files read \u2014 ${e.why}`)}return h()}export{f as ACCENT_TOKEN_EXTENSIONS,k as accentTokenName,p as goldTokenUses};
@@ -0,0 +1,13 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ type Tokens = Readonly<Record<string, string>>;
3
+ export type ButtonCornersOptions = {
4
+ readonly stylesheet?: string;
5
+ readonly button?: string;
6
+ readonly rendered?: readonly (readonly [string, string])[];
7
+ readonly cornerRadiusTokens?: (v: number | null) => Tokens;
8
+ readonly radiusRange?: readonly [number, number];
9
+ readonly callers: readonly string[];
10
+ readonly callerFloor: number;
11
+ };
12
+ export declare function buttonCorners(files: readonly SourceFile[], options: ButtonCornersOptions): RuleReport;
13
+ export {};
@@ -0,0 +1,2 @@
1
+ import{ruleChecks as m}from"./rule.js";const l="--sf-button-radius",f="sf-button",p=i=>i.replace(/\/\*[\s\S]*?\*\//g,e=>" ".repeat(e.length)),b=i=>/^import\s+Btn\s+from\s+["'](?:@\/components\/Btn|\.{1,2}\/(?:[\w-]+\/)*Btn)["'];?$/m.test(i);function g(i){const e=[],n=/<Btn\b[^>]*className=(?:"([^"]*)"|\{`([^`]*)`\})/g;let u;for(;(u=n.exec(i.text))!==null;){const h=u[1]??u[2]??"";/\brounded-/.test(h)&&e.push(`${i.path}: ${h}`)}return e}function y(i,e){const{check:n,report:u}=m("button corners"),h=t=>i.find(s=>s.path===t);{const t={path:"fixture.tsx",text:['import Btn from "@/components/Btn";','<Btn href="/x" className="mt-4 rounded-lg">Go</Btn>',"<Btn className={`mt-2`}>Go</Btn>"].join(`
2
+ `)};n("\xA76/\xA77 the readers find a Btn import and a rounded-* className handed to it",b(t.text)&&g(t).length===1)}if(e.stylesheet!==void 0){const t=h(e.stylesheet);if(n(`\xA71 read the stylesheet ${e.stylesheet}`,!!t)){const s=/\.sf-button\s*\{([^}]*)\}/.exec(p(t.text));n(`\xA71 ${e.stylesheet} carries a \`.sf-button\` rule`,s!==null,"without it the class is decoration and the setting reaches no button"),s&&(n(`\xA71 the .sf-button rule reads \`var(${l}, 0)\``,/border-radius:\s*var\(--sf-button-radius,\s*0\)/.test(s[1]),`the property for the setting, and the \`0\` for every site that never chose one \u2014 got: ${s[1].trim()}`),n("\xA71 the .sf-button rule does not clip",!/overflow/.test(s[1]),`the trailing arrow translates 3px right on hover and would be shaved \u2014 got: ${s[1].trim()}`))}}if(e.button!==void 0){const t=h(e.button);n(`\xA72 read the shared button ${e.button}`,!!t)&&n(`\xA72 ${e.button} writes no \`rounded-*\` utility`,!/\brounded-(?!full\b)[a-z0-9[]/.test(t.text.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm,"")),"every one carries a built-in value, so an untouched site would jump from square to a radius the day it shipped");const s=e.rendered??[];n(`\xA72 the button was rendered (${s.length} shapes, at least 4)`,s.length>=4,"both variants as a link and as a button");for(const[r,a]of s)n(`\xA72 ${r}: the rendered button carries \`${f}\``,new RegExp(`class="${f}\\b`).test(a),`it is the element's only ear for Corners \u2014 got: ${a.slice(0,140)}`)}const o=e.cornerRadiusTokens;if(o){n("\xA73 a site that never chose a corner publishes NO button radius",!(l in o(null)),`that absence is what leaves it square, byte for byte as before \u2014 got: ${JSON.stringify(o(null))}`),n("\xA73 `0` is an ANSWER, not an absence: it publishes a flattened radius",o(0)[l]==="0",JSON.stringify(o(0)));for(const[d,c]of[[0,"0"],[9,"0.28125rem"],[16,"0.5rem"],[40,"1.25rem"]])n(`\xA73 ${d}px publishes ${l}: ${c}`,o(d)[l]===c,`a button is half the card, which is what inputs and panels beside it read \u2014 got: ${String(o(d)[l])}`);const[t,s]=e.radiusRange??[Number.NaN,Number.NaN];n(`\xA73 both ends of the track (${t}, ${s}) publish a button radius`,e.radiusRange!==void 0&&o(t)[l]!==void 0&&o(s)[l]!==void 0);const r=[null,0,9,40].map(d=>o(d)[l]??"0");n("\xA74 not-set and square both DRAW a square button",r[0]==="0"&&r[1]==="0",`they differ in what they publish, not in today's picture \u2014 got: ${JSON.stringify(r)}`),n("\xA74 9 and 40 draw two different NON-square buttons",r[2]!=="0"&&r[3]!=="0"&&r[2]!==r[3],`a setting that names buttons and moves none of them is this card's defect restored \u2014 got: ${JSON.stringify(r)}`);const a="--radius-full";for(const d of[0,9,16,40])n(`\xA75 ${d}px does not publish the fully-round token`,!(a in o(d)),"swatches, toggle knobs and status dots are a SHAPE, not a corner")}{const t=[];for(const s of e.callers){const r=h(s);r&&t.push(...g(r))}n(`\xA76 no caller hands \`Btn\` a \`rounded-*\` class (${e.callers.length} callers)`,t.length===0,`\`.sf-button\` is unlayered and would silently beat it; give the caller its own property instead \u2014 ${t.join(" | ")}`)}{const t=i.filter(a=>a.path.endsWith(".tsx")&&b(a.text)).map(a=>a.path).sort();n(`\xA77 the tree sweep found ${t.length} file(s) importing Btn (at least ${e.callerFloor})`,t.length>=e.callerFloor&&t.length>0,`too few to be real, so \xA77 is measuring nothing \u2014 ${JSON.stringify(t)}`);const s=t.filter(a=>!e.callers.includes(a));n("\xA77 the callers list names every file that imports `Btn`",s.length===0,`${JSON.stringify(s)} import(s) Btn and ${s.length===1?"is":"are"} not listed \u2014 a caller missing from it is a caller \xA76 never looks at. Add the path \u2014 the list is kept by hand on purpose, so a new caller is a visible line in a diff.`);const r=e.callers.filter(a=>!t.includes(a));n("\xA77 the callers list names no file that stopped importing `Btn`",r.length===0,`${JSON.stringify(r)} ${r.length===1?"is":"are"} listed and no longer import Btn \u2014 a dead row is a sweep over nothing. Drop the path, or say in a comment why the file is still worth sweeping.`)}return u()}export{y as buttonCorners};
@@ -0,0 +1,16 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export declare const CHROME_NOTE_CLASS = "sf-note";
3
+ export type EditorChromeNoteOptions = {
4
+ readonly stylesheet?: string;
5
+ readonly minFiles: number;
6
+ };
7
+ export interface CssRule {
8
+ readonly selector: string;
9
+ readonly body: string;
10
+ readonly line: number;
11
+ }
12
+ export declare function leafRules(css: string): CssRule[];
13
+ export declare function declaration(body: string, prop: string): string | null;
14
+ export declare function noteClasses(text: string): Set<string>;
15
+ export declare function withoutComments(source: string): string;
16
+ export declare function editorChromeNote(files: readonly SourceFile[], options: EditorChromeNoteOptions): RuleReport;
@@ -0,0 +1,13 @@
1
+ import{ruleChecks as z}from"./rule.js";const g="sf-note";function y(o){const i=[],t=[];let c="",r=1,n=0;for(;n<o.length;){const a=o[n];if(a==="/"&&o[n+1]==="*"){const e=o.indexOf("*/",n+2),s=e<0?o.length:e+2;for(const f of o.slice(n,s))f===`
2
+ `&&r++;n=s,c+=" ";continue}if(a==='"'||a==="'"){const e=a;for(n++;n<o.length&&o[n]!==e;)o[n]==="\\"?n++:o[n]===`
3
+ `&&r++,n++;n++;continue}if(a===`
4
+ `&&r++,a==="{"){t.length>0&&t[t.length-1].children++,t.push({selector:c.trim().replace(/\s+/g," "),line:r,bodyStart:n+1,children:0}),c="",n++;continue}if(a==="}"){const e=t.pop();e&&e.children===0&&i.push({selector:e.selector,body:o.slice(e.bodyStart,n),line:e.line}),c="",n++;continue}if(a===";"){c="",n++;continue}c+=a,n++}return i}function m(o,i){const t=o.match(new RegExp(`(?:^|[;{])\\s*${i}\\s*:([^;]*)`));return t?t[1].trim():null}function u(o){return new Set(Array.from(o.matchAll(/\bsf-[a-z0-9-]*?note\b/g),i=>i[0]))}function b(o){return o.replace(/\/\*[\s\S]*?\*\//g," ").replace(/(^|[^:])\/\/.*$/gm,"$1")}const p=(o,i)=>JSON.stringify(o)===JSON.stringify(i);function S(o,i){const{check:t,report:c}=z("editor chrome note");{const e=h=>["sf",h,"note"].join("-"),s=y(`.sf-note {
5
+ font-size: 13px;
6
+ color: red;
7
+ }`);t("the walker reads a rule's own declarations",s.length===1&&s[0].selector===".sf-note"&&s[0].line===1&&m(s[0].body,"font-size")==="13px"&&m(s[0].body,"padding")===null,JSON.stringify(s)),t("font-size is not confused with a longhand that ends in it",m(` font-size-adjust: 0.5;
8
+ font-size: 12px;
9
+ `,"font-size")==="12px"&&m(` font-size-adjust: 0.5;
10
+ `,"font-size")===null),t("a grouping block reports its children, not itself",p(y("@keyframes sf-x { from { opacity: 0; } to { opacity: 1; } }").map(h=>h.selector),["from","to"])),t("braces in comments and strings do not shift nesting",p(y('/* .a { x } */ .b { content: "}"; } .c { color: red; }').map(h=>h.selector),[".b",".c"])),t("modifiers and descendants report the base note class",p([...u(".sf-note--error")],["sf-note"])&&p([...u(".sf-note p + p")],["sf-note"])&&p([...u(`.${e("pages")}, .${e("sm")}`)],[e("pages"),e("sm")])&&u(".sf-notebook").size===0);const f=[`/* Two homes were rejected: \`.${e("pages")}\` belongs to the chrome. */`,`// \u2026and .${e("sm")} is the sitemap's own.`,'const url = "https://x/y";','export const Note = () => <div className="sf-note">hi</div>;'].join(`
11
+ `);t("a class a file only TALKS about is not a class it renders, and a `//` in a URL is not a comment",p([...u(b(f))],["sf-note"])&&/https:\/\/x\/y/.test(b(f)))}let r=new Set([g]);if(i.stylesheet){const e=o.find(s=>s.path===i.stylesheet);if(t(`read the stylesheet ${i.stylesheet}`,!!e)&&e){const s=y(e.text);r=new Set;for(const l of s)for(const d of u(l.selector))r.add(d);t(`the stylesheet parses (${s.length} rules, more than 400)`,s.length>400,"the walker looks broken"),t(`the scan is not vacuous: it finds a note class (${r.size})`,r.size>0,"the selector scan looks broken"),t(`the chrome declares exactly ONE note class, .${g}`,p([...r].sort(),[g]),`it declares ${[...r].sort().map(l=>`.${l}`).join(", ")}. One quiet sentence, one class: a note that needs a different INSET takes it from the block it sits in (\`.sf-pages-list > .sf-note\`), never from a new class`);const f=new Map;for(const l of r){const d=s.find(x=>x.selector===`.${l}`),w=d?m(d.body,"font-size"):null;t(`.${l} has a type rule of its own`,!!d,"it is a modifier without a base"),t(`.${l} states its own font-size`,w!==null,"it inherits one \u2014 that is a size nobody chose"),f.set(l,w)}const h=new Set(f.values());t("every note class states the same size",h.size<=1,`the chrome's notes are set at ${h.size} sizes: ${[...f].map(([l,d])=>`.${l} ${d}`).join(", ")}`);const $=s.find(l=>l.selector===`.${g}`);t(`the note still reads as a note: .${g} is var(--sf-muted)`,!!$&&m($.body,"color")==="var(--sf-muted)",$?`color: ${m($.body,"color")}`:"the chrome no longer declares it")}}const n=o.filter(e=>/\.(tsx?|jsx?|mjs)$/.test(e.path));t(`read the source (${n.length} JS/TS files, at least ${i.minFiles})`,n.length>=i.minFiles,"fewer means the walk broke, and the check below is vacuous");const a=[];for(const e of n)for(const s of u(b(e.text)))r.has(s)||a.push(`${e.path} ${s}`);return t(`no source file names a note class the chrome does not declare (${[...r].map(e=>`.${e}`).join(", ")})`,a.length===0,`a \`.sf-*\` class is scoped under \`.sf-editor\` in the PARENT document, so a missing rule is not a compile error \u2014 the text simply renders at whatever it inherits:
12
+ ${a.join(`
13
+ `)}`),c()}export{g as CHROME_NOTE_CLASS,m as declaration,S as editorChromeNote,y as leafRules,u as noteClasses,b as withoutComments};
@@ -0,0 +1,5 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type EditorContextMenuOptions = {
3
+ readonly minEntries: number;
4
+ };
5
+ export declare function editorContextMenu(files: readonly SourceFile[], options: EditorContextMenuOptions): RuleReport;
@@ -0,0 +1,4 @@
1
+ import{ruleChecks as c}from"./rule.js";const r="efficient-easel",i=`${r}/panel.css`,s=`${r}/menu.css`;function a(h){const n=h.filter(t=>t.path.endsWith(".tsx")&&t.text.includes(i));return{withPanel:n,missing:n.filter(t=>!t.text.includes(s)).map(t=>t.path)}}function p(h,n){const{check:t,report:l}=c("editor context menu");{const e=a([{path:"a.tsx",text:`import "${i}";
2
+ import "${s}";`},{path:"b.tsx",text:`import "${i}";`},{path:"c.tsx",text:`import "${s}";`}]);t("\xA78's reader finds the entries that import the panel sheet, and the one lacking the menu sheet",e.withPanel.length===2&&e.missing.join()==="b.tsx")}{const{withPanel:e,missing:o}=a(h);t(`(l) \xA78 every entry that imports the panel sheet imports the menu sheet too (${e.length} entries, at least ${n.minEntries})`,e.length>=n.minEntries&&e.length>0&&o.length===0,o.length?`these mount the editor chrome but not \`${s}\`, so the menu draws as an unstyled full-width div there:
3
+ `+o.join(`
4
+ `):`the walk found too few entries importing ${i} \u2014 it is not sweeping the editor at all, so this check is asking nothing.`)}return l()}export{p as editorContextMenu};
@@ -0,0 +1,8 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type EditorPuckDragOffOptions = {
3
+ readonly documentPermissions: (canEdit: boolean) => Readonly<Record<string, unknown>>;
4
+ readonly mustFind?: readonly string[];
5
+ readonly harnesses?: Readonly<Record<string, string>>;
6
+ readonly minFiles: number;
7
+ };
8
+ export declare function editorPuckDragOff(files: readonly SourceFile[], options: EditorPuckDragOffOptions): RuleReport;
@@ -0,0 +1,4 @@
1
+ import{ruleChecks as g}from"./rule.js";const $=/<(?:Puck|SiteEditorProvider|EditorStoreProvider|ScreenStoreProvider)\b[^<>]*\bdata=\{[^<>]*>/g,S=/\bpermissions=\{\s*(?:documentPermissions\(|permissions\s*\})/,P=/permissions=\{\s*permissions\s*\}/,k=["delete","drag","duplicate","edit","insert"],d=o=>[...o.text.matchAll($)].map(n=>({file:o.path,tag:n[0]}));function u(o,n){const s=[];return S.test(o.tag)?P.test(o.tag)&&!/documentPermissions\(/.test(n)&&s.push("passes a `permissions` it does not build from documentPermissions"):s.push("mounts the editor without the edit lock's permissions"),s}function w(o,n){const{check:s,report:f}=g("editor puck drag off"),m=n.harnesses??{};for(const e of[!0,!1]){const t=n.documentPermissions(e);s(`documentPermissions(${e}).drag is false`,t.drag===!1,`got ${String(t.drag)}`),s(`documentPermissions(${e}) names all five keys \u2014 Puck MERGES this prop, so a missing key keeps its old value`,Object.keys(t).sort().join(",")===k.join(","),`got ${Object.keys(t).sort().join(",")}`),s(`documentPermissions(${e}).edit is ${e}`,t.edit===e)}{const e="Puck",t="SiteEditorProvider",i=a=>d({path:"fixture.tsx",text:a}).map(p=>u(p,a).length);s("the scanner flags a mount with no permissions",JSON.stringify(i(`<${e} config={c} data={d} />`))==="[1]"),s("the scanner passes a mount that calls the helper",JSON.stringify(i(`<${t} data={d} permissions={documentPermissions(!readOnly)}>`))==="[0]"),s("a local `permissions` passes only when the file builds it from the helper",JSON.stringify(i(`<${e} data={d} permissions={permissions} />`))==="[1]"&&JSON.stringify(i(`const permissions = documentPermissions(true);
2
+ <${e} data={d} permissions={permissions} />`))==="[0]"),s("a tag with no `data=` is not a mount",i(`<${e}.Preview />`).length===0),s("the screen's store provider is a mount under either of its names",["Editor","Screen"].every(a=>JSON.stringify(i(`<${a}StoreProvider config={c} data={d} />`))==="[1]"))}const c=o.filter(e=>/\.tsx?$/.test(e.path));s(`read the source (${c.length} TS/TSX files, at least ${n.minFiles})`,c.length>=n.minFiles,"fewer means the walk broke, and every check below is vacuous");const r=[];for(const e of c)e.path in m||r.push(...d(e));for(const e of n.mustFind??[])s(`found the editor mount (data=\u2026) in ${e}`,r.some(t=>t.file===e));for(const[e,t]of Object.entries(m)){const i=c.find(a=>a.path===e);s(`the harness exemption is still real: ${e} (${t})`,!!i&&d(i).length>0,"remove the stale entry")}const h=new Map(c.map(e=>[e.path,e.text])),l=r.flatMap(e=>u(e,h.get(e.file)??"").map(t=>`${e.file} ${t}`));return s(`every editor mount turns Puck's own canvas drag off (${r.length} mount(s)${r.length?`: ${[...new Set(r.map(e=>e.file))].join(", ")}`:""})`,l.length===0,`without the edit lock's permissions a read-only viewer's canvas would take edits the save then refuses:
3
+ ${l.join(`
4
+ `)}`),f()}export{w as editorPuckDragOff};
@@ -0,0 +1,9 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type EfficientEaselVersionOptions = {
3
+ readonly packageJson: string;
4
+ readonly resolve: (specifier: string) => string;
5
+ readonly readText: (path: string) => string | undefined;
6
+ };
7
+ export declare function satisfiesCaret(version: string, range: string): boolean;
8
+ export declare function importedSubpaths(files: readonly SourceFile[]): string[];
9
+ export declare function efficientEaselVersion(files: readonly SourceFile[], options: EfficientEaselVersionOptions): RuleReport;
@@ -0,0 +1,2 @@
1
+ import{ruleChecks as b}from"./rule.js";const p=n=>JSON.stringify(n),t="efficient-easel",u=`${t}/panel.css`;function m(n,r){const s=/^\^(\d+)\.(\d+)\.(\d+)$/.exec(r.trim());if(!s)throw new Error(`only \`^x.y.z\` pins are understood here, got "${r}"`);const a=/^(\d+)\.(\d+)\.(\d+)/.exec(n.trim());if(!a)throw new Error(`"${n}" is not a version this can read`);const[i,c,h]=s.slice(1).map(Number),[e,o,l]=a.slice(1).map(Number);return!(e!==i||i===0&&o!==c||o<c||o===c&&l<h)}const $=n=>{const r=Math.max(n.lastIndexOf("/"),n.lastIndexOf("\\"));return r<=0?n.slice(0,r+1)||n:n.slice(0,r)};function g(n,r){const s=n(u);let a=$(s);for(let i=0;i<12;i++){const c=r(`${a}/package.json`);if(c!==void 0)try{const e=JSON.parse(c);if(e.name===t){if(!e.version)throw new Error(`${t} resolved but its package.json carries no version`);return e.version}}catch(e){if(!(e instanceof SyntaxError))throw e}const h=$(a);if(h===a)break;a=h}throw new Error(`resolved ${u} to ${s} but found no ${t} package.json above it`)}function v(n){const r=new Set;for(const s of n)if(/\.(ts|tsx|mts|mjs|js|jsx)$/.test(s.path))for(const a of s.text.matchAll(/(?:\bfrom\s*|\bimport\s*\(?\s*|\brequire\s*\(\s*)["'](efficient-easel\/[A-Za-z0-9._/-]+)["']/g))r.add(a[1]);return[...r].sort()}const k=n=>n.code??"threw";function x(n,r){const{check:s,report:a}=b(`${t} version`);{const e=(f,w)=>{try{return m(f,w)}catch{return"threw"}};s("the caret reading is right about 0.x, where the minor is part of the lock",e("0.10.0","^0.10.1")===!1&&e("0.10.1","^0.10.1")===!0&&e("0.11.0","^0.11.0")===!0&&e("0.11.0","^0.10.1")===!1&&e("0.10.1","^0.11.0")===!1&&e("0.11.4","^0.11.0")===!0&&e("0.11.4","~0.11.0")==="threw","a stale patch is caught; 0.x carets cross no minor and go no backwards; patches above the pin pass; only ^x.y.z is read");const o={"/s/node_modules/.pnpm/efficient-easel@1.2.3/node_modules/efficient-easel/package.json":JSON.stringify({name:t,version:"1.2.3"}),"/s/node_modules/.pnpm/efficient-easel@1.2.3/node_modules/efficient-easel/dist/package.json":"{ not json"};let l="";try{l=g(()=>"/s/node_modules/.pnpm/efficient-easel@1.2.3/node_modules/efficient-easel/dist/css/panel.css",f=>o[f])}catch(f){l=f.message}s("the manifest walk climbs past an unreadable manifest to the package's own",l==="1.2.3",l);const d=v([{path:"fixture.ts",text:[`import { A } from ${p(t+"/menu")};`,`// see ${t}/edit-lock`,`const m = await import(${p(t+"/icons")});`,`const s = ${p(t+"/not-an-import")};`].join(`
2
+ `)}]);s("only import syntax counts as an import, never prose or a string",d.join()===`${t}/icons,${t}/menu`,JSON.stringify(d))}let i;try{const e=JSON.parse(r.packageJson);i=e.dependencies?.[t]??e.devDependencies?.[t]}catch(e){s("package.json reads as JSON",!1,e.message)}if(s(`package.json declares a dependency on ${t}`,i!==void 0)){let e;try{e=g(r.resolve,r.readText)}catch(o){s(`${t} resolves from the install`,!1,o.message)}if(e!==void 0){let o,l;try{o=m(e,i)}catch(d){o=!1,l=d.message}s(`the resolved ${t} satisfies the declared pin (declared ${i}, resolved ${e})`,o,l??`this tree resolves ${t} ${e}, which does not satisfy the declared ${i}. The install is stale \u2014 what you are looking at is not what this repository says it is. Refresh it with \`pnpm install\`, and inside Docker remember that a node_modules VOLUME outlives the container: it keeps whatever was installed into it until something installs again.`)}}const c=v(n);if(s(`found the ${t} imports (${c.length} subpaths)`,c.length>0,"the search is broken, not the code")){const e=[];for(const o of c)try{r.resolve(o)}catch(l){e.push(`${o} (${k(l)})`)}s(`every ${t} subpath this tree imports resolves from the install \u2014 ${c.join(", ")}`,e.length===0,`the installed package does not export: ${e.join(", ")}. The pin resolves and the lockfile is valid \u2014 the SUBPATH is missing, which means the pin points at a release published before it existed. Move the pin to the release that carries it.`)}let h=!1;try{r.resolve(`${t}/a-subpath-that-was-never-exported`)}catch{h=!0}return s(`the resolver refuses a ${t} subpath that does not exist`,h,"it resolved one \u2014 this check cannot fail"),a()}export{x as efficientEaselVersion,v as importedSubpaths,m as satisfiesCaret};
@@ -0,0 +1,12 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type EveryButtonOptions = {
3
+ readonly buttonFiles: readonly (readonly [string, number, number])[];
4
+ readonly exclude?: readonly string[];
5
+ readonly accentFillsThatStay: number;
6
+ readonly buttonFills: number;
7
+ readonly buttonSites: number;
8
+ readonly hoverFgKeepers: number;
9
+ readonly chips?: readonly string[];
10
+ readonly runtimeFallbacks?: string;
11
+ };
12
+ export declare function everyButtonFollowsTheButtonColour(files: readonly SourceFile[], options: EveryButtonOptions): RuleReport;
@@ -0,0 +1,7 @@
1
+ import{ruleChecks as F}from"./rule.js";const h=/[-a-zA-Z0-9:/.[\]]*bg-accent[-a-zA-Z0-9:/.[\]]*/g,u=/[-a-zA-Z0-9:/.[\]]*bg-btn[-a-zA-Z0-9:/.[\]]*/g;function N(o){const t=o.trim();return t.startsWith("//")||t.startsWith("*")||t.startsWith("/*")}function f(o,t){const n=[];for(const r of o)r.text.split(`
2
+ `).forEach((l,i)=>{t.lastIndex=0,!(!t.test(l)||N(l))&&n.push({file:r.path,line:i+1,text:l})});return n}function g(o,t){const n=new Map;for(const r of o){const l=r.text.match(t)?.length??0;l&&n.set(r.file,(n.get(r.file)??0)+l)}return n}const d=o=>[...o.values()].reduce((t,n)=>t+n,0);function $(o){const t=/(?<![-:\w])text-bg\b/.test(o),n=/\btext-black\b/.test(o),r=/\bhover:text-bg\b/.test(o)&&/\bhover:bg-btn\b/.test(o);return t||n||r}const w=o=>/\bhover:bg-fg\b/.test(o)&&/\bhover:text-bg\b/.test(o);function B(o,t){const{check:n,report:r}=F("every button follows the button colour");{const e={path:"fixture.tsx",text:['<button className="bg-btn text-btn-ink hover:bg-btn/90" />','// <button className="bg-accent" /> \u2014 a comment is not paint','<span className={on ? "bg-accent text-bg" : "bg-fg/10"} />','<button className="bg-btn text-bg" />','<button className="bg-btn hover:bg-fg hover:text-bg" />'].join(`
3
+ `)},a=f([e],u);n("the readers count fills per occurrence and drop comment lines",d(g(a,u))===4&&d(g(f([e],h),h))===1),n("\xA74's reader flags page ink on a button and keeps the --fg hover's ink",a.filter(s=>$(s.text)).map(s=>s.line).join()==="4"&&a.filter(s=>w(s.text)).map(s=>s.line).join()==="5")}const l=o.filter(e=>/\.(tsx?|css)$/.test(e.path)),i=new Set(t.exclude??[]);for(const e of i)n(`the excluded file is still real: ${e}`,l.some(a=>a.path===e),"remove the stale entry");const b=l.filter(e=>!i.has(e.path));n(`read the .ts, .tsx and .css files (${b.length})`,b.length>0,"the population is empty \u2014 this rule is measuring nothing");const y=f(b,h),c=f(b,u),m=g(y,h),p=g(c,u);{const e=[];for(const[a,s,k]of t.buttonFiles){const v=p.get(a)??0,x=m.get(a)??0;v!==s&&e.push(`${a}: ${v} bg-btn, expected ${s}`),x!==k&&e.push(`${a}: ${x} bg-accent left, expected ${k}`)}n(`\xA71 every ruled button paints the button token, and its file's markers do not (${t.buttonFiles.length} files)`,e.length===0,`a ruled button still paints --accent, or a marker was swept along with it:
4
+ `+e.join(`
5
+ `))}{const e=d(m);n(`\xA72 ${t.accentFillsThatStay} bg-accent fills stay (found ${e})`,e===t.accentFillsThatStay,"either a marker was swept (too few) or new accent paint arrived (too many) \u2014 this count is derived from the tree, so a new call site has to come past it.");const a=d(p);n(`\xA72 ${t.buttonFills} bg-btn fills (found ${a})`,a===t.buttonFills)}for(const e of t.chips??[])n(`\xA73 ${e} still paints bg-accent`,(m.get(e)??0)>0,"a filter chip is not a button, and a sweep that repaints it has swept too far");{n(`\xA74 ${t.buttonSites} sites paint bg-btn (found ${c.length})`,c.length===t.buttonSites,"\xA74 cannot say anything about ink until the fills have actually moved");const e=c.filter(s=>$(s.text)).map(s=>`${s.file}:${s.line}`);n("\xA74 no button inks a bg-btn state with the page background or a literal",e.length===0,`a merchant's button colour moves the fill and leaves the ink behind:
6
+ `+e.join(`
7
+ `));const a=c.filter(s=>w(s.text)).length;n(`\xA74 ${t.hoverFgKeepers} buttons still pair hover:bg-fg with hover:text-bg (found ${a})`,a===t.hoverFgKeepers,"a sweep that moved those to --btn-ink has put the button's ink on a foreground-coloured fill")}if(t.runtimeFallbacks!==void 0){const e=o.find(a=>a.path===t.runtimeFallbacks);if(n(`\xA75 read ${t.runtimeFallbacks}`,!!e)){const a=[...e.text.matchAll(/const (btnBg|btnFg) = \w+ \|\| "([^"]+)";/g)].map(s=>[s[1],s[2]]);n(`\xA75 found the 2 button-colour fallbacks (found ${a.length})`,a.length===2,"if the shape changed, this rule stopped reading the thing it names"),n("\xA75 the UNSET button falls back to --btn and --btn-ink",JSON.stringify(Object.fromEntries(a))===JSON.stringify({btnBg:"var(--btn)",btnFg:"var(--btn-ink)"}),"it falls back to the accent or to a literal again. Unset is the majority case, and on hartes-metal that painted #C0392B under a hard-coded black \u2014 card 66309's defect on the most prominent button of the page. Only the fallback may move; a merchant's own colour short-circuits it.")}}return r()}export{B as everyButtonFollowsTheButtonColour};
@@ -0,0 +1,10 @@
1
+ import type * as Theme from "../theme.js";
2
+ import { type RuleReport, type SourceFile } from "./rule.js";
3
+ export type FlatThemeModule = Pick<typeof Theme, "ACCENT_DARKEN_STEPS" | "contrastRatio" | "darkerAccents" | "DEFAULT_THEMES" | "gradeContrast" | "normalizeThemeColors" | "READABILITY_AA" | "READABILITY_AA_LARGE" | "themeAccentStyle" | "themeColorStyle">;
4
+ export type FlatThemePaletteOptions = {
5
+ readonly minFiles: number;
6
+ readonly editorAccentCarriers: number;
7
+ readonly liveAccentCarrier?: string;
8
+ readonly theme?: FlatThemeModule;
9
+ };
10
+ export declare function flatThemePalette(files: readonly SourceFile[], options: FlatThemePaletteOptions): RuleReport;
@@ -0,0 +1,8 @@
1
+ import{isRuleSource as y,ruleChecks as S}from"./rule.js";import{registryFiles as T,registryText as C}from"./support/registry-source.js";const k=/\.(ts|tsx|js|jsx|css|json)$/,x=["accentLight","backgroundLight","surfaceLight","lineLight","mutedLight","textLight"],_=["lightSurfaces"],R=["usesLightSurfaces","setLightSurfaces","lightPageBackground"],b=["accentForMode","isAccentLinked","linkAccent","unlinkAccent"],N=["--theme-fg-light","--theme-bg-light","--theme-surface-light","--theme-surface-2-light","--theme-line-light","--theme-muted-light","--theme-accent-light","--theme-accent-light-2"],v=[...x,"lightSurfaces"],I=["text","accent","background","surface","line","muted"];function u(c,a){const t=[];for(const d of c)d.text.split(`
2
+ `).forEach((l,f)=>{a.test(l)&&t.push(`${d.path}:${f+1} ${l.trim().slice(0,100)}`)});return t}const A=c=>new RegExp(`\\b${c}\\b`),$=c=>new RegExp(`${c}(?![\\w-])`);function F(c,a){const{check:t,report:d}=S("a theme is one flat palette");{const e=[{path:"fixture.ts",text:`const a = colors.textLight;
3
+ const b = contextLighting;
4
+ `},{path:"fixture.css",text:`.x { color: var(--theme-accent-light-2); }
5
+ .y { color: var(--theme-accent-light); }
6
+ `}],r=u(e,A("textLight"));t("the name reader flags `textLight`, and not `contextLighting`",r.length===1&&r[0].startsWith("fixture.ts:1"),JSON.stringify(r));const n=u(e,$("--theme-accent-light"));t("the token reader flags `--theme-accent-light`, and not `--theme-accent-light-2`",n.length===1&&n[0].startsWith("fixture.css:2"),JSON.stringify(n))}const l=c.filter(e=>k.test(e.path)&&!y(e.path));t(`read the shipped source (${l.length} files, at least ${a.minFiles})`,l.length>=a.minFiles,"every check below asserts an absence, and a read of nothing satisfies an absence");const f=(e,r,n)=>{const s=u(l,r);t(`${e} is gone`,s.length===0,`\`${e}\` still lives in ${s.length} place(s) \u2014 ${n} (66238):
7
+ ${s.join(`
8
+ `)}`)};for(const e of x)f(e,A(e),"a theme is ONE flat palette");for(const e of[..._,...R,...b])f(e,A(e),"a theme is ONE flat palette");for(const e of N)f(e,$(e),"the keys and the tokens are ONE mechanism");if(a.theme){const e=a.theme;e.DEFAULT_THEMES.forEach((n,s)=>{const g=n,o=I.filter(h=>typeof g[h]!="string");t(`preset ${s} still states all six base colours`,o.length===0,`lost ${o.join(", ")} \u2014 66238 removes a duplicated HALF, never a colour`);const i=v.filter(h=>h in g);t(`preset ${s} carries no key 66238 deleted`,i.length===0,`still carries ${i.join(", ")}`)}),t(`read the presets (${e.DEFAULT_THEMES.length})`,e.DEFAULT_THEMES.length>0);const r={selected:0,themes:[e.DEFAULT_THEMES[0]]};for(const[n,s]of[["themeAccentStyle",e.themeAccentStyle(r)],["themeColorStyle",e.themeColorStyle(r)]])t(`${n} does not set --accent or --accent-2 directly`,!("--accent"in s)&&!("--accent-2"in s));t("the grading bands sit exactly on the WCAG thresholds",e.gradeContrast(e.READABILITY_AA)==="good"&&e.gradeContrast(e.READABILITY_AA+.5)==="good"&&e.gradeContrast(e.READABILITY_AA-.01)==="large-only"&&e.gradeContrast(e.READABILITY_AA_LARGE)==="large-only"&&e.gradeContrast(e.READABILITY_AA_LARGE-.01)==="hard"&&e.gradeContrast(1)==="hard"),t("the contrast ratio is the real WCAG figure, and symmetric",e.contrastRatio("#000000","#ffffff")===21&&e.contrastRatio("#ffffff","#000000")===21&&e.contrastRatio("not a colour","#ffffff")===null);{const n="#c9a461",s=e.normalizeThemeColors({text:"#0a0a0a",accent:n,background:"#f5f1e8"}).background,g=e.darkerAccents(n),o=[];g.length!==e.ACCENT_DARKEN_STEPS.length&&o.push(`${g.length} suggestions for ${e.ACCENT_DARKEN_STEPS.length} steps`);let i=e.contrastRatio(n,s)??0;for(const h of g){/^#[0-9a-f]{6}$/.test(h)||o.push(`${h} is not a plain hex`);const m=e.contrastRatio(h,s)??0;m>i||o.push(`${h} (${m}) does not beat the previous ${i}`),i=m}e.gradeContrast(i)!=="good"&&o.push(`the deepest reads ${e.gradeContrast(i)}, not good`),t("the darker suggestions are the same hue, and actually read better",o.length===0,o.join("; "))}t("a non-hex suggestion list is empty rather than three swatches of black",e.darkerAccents("var(--brand)").length===0)}if(a.liveAccentCarrier){const e=c.find(r=>r.path===a.liveAccentCarrier);t(`read the live site's accent carrier, ${a.liveAccentCarrier}`,!!e),t("the live site's accent carrier wears sf-theme-accent",!!e&&/className="contents sf-theme-accent"/.test(e.text))}const E=T(c),p=C(c);t(`read this repository's share of the block registry (${E.length} files)`,E.length>0);const L=[...p.matchAll(/className="[^"]*sf-theme-accent/g)].length;return t(`the registry's editor accent carriers wear sf-theme-accent (found ${L}, expected exactly ${a.editorAccentCarriers})`,L===a.editorAccentCarriers),t("the editor preview emits --theme-accent via themeAccentStyle, not a hand-rolled --accent",!/\["--accent"\]:/.test(p)),d()}export{F as flatThemePalette};
@@ -0,0 +1,8 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type FullWidthMeansFullOptions = {
3
+ readonly converted: readonly string[];
4
+ readonly carrierFloor: number;
5
+ readonly widthHelper?: string;
6
+ };
7
+ export declare const SITE_CAP = "max-w-[var(--sf-site-width,1600px)]";
8
+ export declare function fullWidthMeansFull(files: readonly SourceFile[], options: FullWidthMeansFullOptions): RuleReport;
@@ -0,0 +1,2 @@
1
+ import{ruleChecks as f}from"./rule.js";const o="max-w-[var(--sf-site-width,1600px)]",p=t=>(t.split("/").pop()??t).replace(/\.tsx$/,"");function a(t){return{asks:t.includes("sectionRunsFullWidth(widthProps)"),column:t.includes(`: "mx-auto ${o}`)||t.includes(`: "relative z-[2] mx-auto ${o}`)||t.includes(`: "mx-auto grid ${o}`)}}function w(t,l){const{check:s,report:c}=f("full width means full");{const e=a(`const full = sectionRunsFullWidth(widthProps);
2
+ <div className={full ? "w-full" : "mx-auto ${o} px-6"} />`),n=a(`<div className="mx-auto ${o} px-6" />`);s("the reader passes a conditional cap",e.asks&&e.column),s("\u2026and fails an unconditional one",!n.asks&&!n.column)}const r=t.filter(e=>e.path.endsWith(".tsx")),i=r.filter(e=>e.text.includes(o));s(`\u2026and it finds the files that carry one (${i.length} of ${r.length} .tsx files, at least ${l.carrierFloor})`,i.length>=l.carrierFloor&&i.length>0,`only ${i.length} files carry ${o}`);for(const e of l.converted){const n=t.find(u=>u.path===e);if(!s(`read ${e}`,!!n,"the converted section is gone \u2014 remove it from the list, or find where it went"))continue;const{asks:d,column:h}=a(n.text);s(`${p(e)} asks whether it runs full width`,d,"its content column is unconditional again \u2014 a merchant choosing Full width would get a capped section, which is the defect this card removed"),s("\u2026and still draws the column when it does not",h,"the other arm lost the cap \u2014 every published page of this type would go edge to edge, which is NOT what this card does")}if(l.widthHelper!==void 0){const e=t.find(n=>n.path===l.widthHelper);s(`read the width helper ${l.widthHelper}`,!!e)&&s("sectionRunsFullWidth resolves absence to the content column, never to full",/resolveWidth\(props as WidthProps, "page"\)/.test(e.text),"absence resolving to `full` sends every published section of these types edge to edge on the day this ships")}return c()}export{o as SITE_CAP,w as fullWidthMeansFull};
@@ -0,0 +1,11 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type HeadingCeilingOptions = {
3
+ readonly notSwept?: Readonly<Record<string, string>>;
4
+ readonly reference?: string;
5
+ readonly minSites: number;
6
+ readonly minSwept: number;
7
+ readonly minCapped: number;
8
+ };
9
+ export declare function enclosingLiteral(src: string, at: number): string | null;
10
+ export declare function headingCeilingNotes(files: readonly SourceFile[], options: HeadingCeilingOptions): string[];
11
+ export declare function headingCeilingSweep(files: readonly SourceFile[], options: HeadingCeilingOptions): RuleReport;
@@ -0,0 +1,8 @@
1
+ import{HEADING_FACE_UTILITIES as _}from"../fonts.js";import{isRuleSource as k,ruleChecks as T}from"./rule.js";import{evalLength as S}from"./support/css-length.js";const j=["app/","components/","lib/"],v="40px",E=["1","1.4"],b=/(?:^|\s)((?:[a-z0-9@_.-]*\[[^\]]*\]:|[a-z0-9@_.-]+:)*)text-\[([^\]]*)\]/g;function L(t){const e=/max-\[(\d+)px\]:/.exec(t);return e?Number(e[1]):1600}function O(t,e){let s=-1,o="";for(let i=e;i>0;i--){const l=t[i-1];if((l==="`"||l==="'"||l==='"')&&t[i-2]!=="\\"){o=l,s=i;break}}if(s===-1)return null;for(let i=e;i<t.length;i++)if(t[i]===o&&t[i-1]!=="\\")return t.slice(s,i);return null}const A=/^(#|rgb|hsl|oklch|color|currentColor|inherit|transparent)/;function y(t){const e=[];for(const s of t){const o=s.text,i=new RegExp(_.join("|"),"g");let l;for(;l=i.exec(o);){const a=O(o,l.index);if(!a)continue;const h=[];b.lastIndex=0;let c;for(;c=b.exec(a);){const p=c[2].replace(/^length:/,"");A.test(p)||h.push({token:c[0].trim(),expr:p,vw:L(c[1])})}h.length!==0&&e.push({file:s.path,line:o.slice(0,l.index).split(`
2
+ `).length,className:a,steps:h})}}return e}function u(t,e){return S(t.expr,t.vw,e)}const C=t=>u(t,{"--scale-heading":"1"})>40;function m(t,e){return t[`${e.file}:${e.line}`]??Object.entries(t).find(([s])=>{const o=s.indexOf("#");return o>0&&s.slice(0,o)===e.file&&e.className.includes(s.slice(o+1))})?.[1]??t[e.file]}const I=t=>t.filter(e=>/\.tsx?$/.test(e.path)&&!k(e.path)&&j.some(s=>e.path.startsWith(s)));function D(t,e){const s=e.notSwept??{},o=y(I(t)).filter(l=>!m(s,l)).flatMap(l=>l.steps.filter(a=>!C(a)&&u(a,{"--sf-heading-max":v,"--scale-heading":"1.4"})>40).map(a=>`${l.file}:${l.line} \xB7 ${a.token}`)),i=[];if(o.length){i.push(` note \u2014 ${o.length} step(s) clear 40px at the default heading scale but not at 1.4; outside this card's census, listed so they are not a silent gap:`);for(const l of o)i.push(` ${l}`)}for(const[l,a]of Object.entries(s))i.push(` note \u2014 not swept: ${l} \u2014 ${a}`);return i}function H(t,e){const{check:s,report:o}=T("heading ceiling sweep"),i=e.notSwept??{},l=I(t),a=y(l);s(`read the source (${l.length} .ts/.tsx files)`,l.length>0,"found no source to sweep \u2014 this rule is not measuring anything"),s(`the scanner finds display headings at all (${a.length}, at least ${e.minSites})`,a.length>=e.minSites,`found ${a.length} heading-face class lists with a text-[\u2026] size`);const h=a.filter(n=>!m(i,n)&&n.steps.some(C));for(const n of Object.keys(i)){const r=n.split(/[:#]/)[0];s(`notSwept["${n}"] still matches a real site`,a.some(f=>f.file===r&&m(i,f)===i[n]))}s(`the census finds the headings a 40px ceiling has to bring down (${h.length}, at least ${e.minSwept})`,h.length>=e.minSwept,`only ${h.length} class lists declare a size above 40px`);const c=[];for(const n of h)for(const r of n.steps)for(const f of E){const d=u(r,{"--sf-heading-max":v,"--scale-heading":f});d>40&&c.push(`${n.file}:${n.line} @scale ${f} \u2192 ${d}px \xB7 ${r.token}`)}s("no swept display heading renders above the site's 40px ceiling",c.length===0,`${c.length} step(s) ignore it:
3
+ ${c.join(`
4
+ `)}`);const p=a.flatMap(n=>n.steps.filter(r=>r.expr.includes("--sf-heading-max")).map(r=>({site:n,step:r})));s(`the sweep actually capped headings (${p.length} steps read --sf-heading-max, at least ${e.minCapped})`,p.length>=e.minCapped);const g=[];for(const{site:n,step:r}of p){const f=/^min\((.*),\s*var\(--sf-heading-max,[^)]*\)\)$/.exec(r.expr)?.[1];if(f!==void 0)for(const d of E){const $=S(f,r.vw,{"--scale-heading":d}),w=u(r,{"--scale-heading":d});$!==w&&g.push(`${n.file}:${n.line} @scale ${d}: ${$}px \u2192 ${w}px`)}}if(s("a tenant that sets no ceiling renders exactly what it renders today",g.length===0,`${g.length} step(s) moved:
5
+ ${g.join(`
6
+ `)}`),e.reference!==void 0){const n=t.find(r=>r.path===e.reference);s(`the reference component still spells the ceiling the one way (${e.reference})`,!!n&&/var\(--sf-heading-max,\s*9999px\)/.test(n.text),`${e.reference} no longer reads var(--sf-heading-max,9999px)`)}const N=/^var\(--sf-heading-max,/,x=p.filter(({step:n})=>!N.test(n.expr)).filter(({step:n})=>!/^min\(.*,var\(--sf-heading-max,9999px\)\)$/.test(n.expr)).map(({site:n,step:r})=>`${n.file}:${n.line} \xB7 ${r.token}`);return s("every capped step is spelt the reference's way",x.length===0,`${x.length} second spelling(s):
7
+ ${x.join(`
8
+ `)}`),o()}export{O as enclosingLiteral,D as headingCeilingNotes,H as headingCeilingSweep};
@@ -0,0 +1,7 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type LabelHintsOptions = {
3
+ readonly minLabels: number;
4
+ readonly minJsxLabels: number;
5
+ };
6
+ export declare function hintsIn(label: string): string[];
7
+ export declare function labelHints(files: readonly SourceFile[], options: LabelHintsOptions): RuleReport;
@@ -0,0 +1,5 @@
1
+ import{ruleChecks as m}from"./rule.js";import{readJsxLabels as $,readLabels as k}from"./support/label-extraction.js";function W(t){const e=[];return t.includes("==")?e.push("shows raw `==` to the merchant"):t.includes("=")&&e.push("carries an `x = y` legend"),/[()]/.test(t)&&e.push("carries a bracket hint"),t.includes(" \u2014 ")&&e.push("carries a ` \u2014 ` hint tail"),e}function j(t){return t.includes("=")?t.includes("==")?["shows raw `==` to the reader"]:["carries an `x = y` legend"]:[]}function x(t){const e=k(t.text),a=[];for(const r of e){const l=W(r.label);l.length&&a.push({where:`${t.path}:${r.line}`,label:r.label,why:l})}return{read:e.length,offenders:a}}function g(t){const e=$(t.text),a=[];for(const{line:r,label:l}of e){const i=j(l);i.length&&a.push({where:`${t.path}:${r}`,label:l,why:i})}return{read:e.length,offenders:a}}const w=t=>`${t.length} label(s) are hints, not names \u2014 the manual does the teaching:
2
+ `+t.map(e=>` ${e.where} ${JSON.stringify(e.label)} \u2014 ${e.why.join(", ")}`).join(`
3
+ `);function L(t,e){const{check:a,report:r}=m("label hints");{const n=x({path:"lib/puck/fixture.tsx",text:['heading: plainText("Heading (new line = break)"),','accent: plainText("Accent ==word=="),','legend: text("Size = px"),','tail: textarea("Body \u2014 one paragraph"),','body: plainText("Body", { placeholder: "A ==word== in the theme colour" }),','layout: { type: "select", label: "Layout",',' options: [{ label: "Split (editorial aside)", value: "split" }] },','defaultProps: { heading: "Our ==new== range" },'].join(`
4
+ `)}),s=y=>n.offenders.filter(h=>h.where.endsWith(`:${y}`)).flatMap(h=>h.why);a("W1 flags a bracket hint (and the `x = y` inside it)",s(1).includes("carries a bracket hint")&&s(1).includes("carries an `x = y` legend")),a("W1 flags raw `==` in a label",s(2).includes("shows raw `==` to the merchant")),a("W1 flags an `x = y` legend",s(3).includes("carries an `x = y` legend")),a("W1 flags a ` \u2014 ` hint tail",s(4).includes("carries a ` \u2014 ` hint tail")),a("W1 passes a placeholder, a select option and a default prop, and reads the labels it should",n.offenders.length===4&&n.read===6,JSON.stringify(n));const o=g({path:"components/Fixture.tsx",text:['<Field label="Accent (==word==)" />','<Field label="Size = px" />','<Field label="Name (optional)" />','<button aria-label="a = b" />'].join(`
5
+ `)});a("W2 flags `==` and `x = y` in a JSX label, leaves a bracket alone, and reads no aria-label",o.offenders.length===2&&o.read===3&&o.offenders[0].why[0]==="shows raw `==` to the reader",JSON.stringify(o))}const l=t.filter(n=>n.path.startsWith("lib/puck/")&&/\.(ts|tsx)$/.test(n.path)),i=t.filter(n=>n.path.startsWith("components/")&&n.path.endsWith(".tsx")),f=l.map(x),u=i.map(g),d=f.reduce((n,s)=>n+s.read,0),c=u.reduce((n,s)=>n+s.read,0),p=f.flatMap(n=>n.offenders),b=u.flatMap(n=>n.offenders);return a(`read >= ${e.minLabels} field labels across ${l.length} files under lib/puck (found ${d})`,d>=e.minLabels,"the reader is broken, not the panel"),a(`read >= ${e.minJsxLabels} JSX label attributes across ${i.length} files under components (found ${c})`,c>=e.minJsxLabels,"the reader is broken, not the panel"),a(`W1 every field label under lib/puck is a plain name (${d} read)`,p.length===0,w(p)),a(`W2 no JSX label under components shows \`=\` (${c} read)`,b.length===0,w(b)),r()}export{W as hintsIn,L as labelHints};
@@ -0,0 +1,14 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export declare const NEVER_WORDS: readonly string[];
3
+ export type ParkedLabel = {
4
+ readonly file: string;
5
+ readonly label: string;
6
+ readonly why: string;
7
+ };
8
+ export type LabelVocabularyOptions = {
9
+ readonly floors: Readonly<Record<string, number>>;
10
+ readonly minRegistryLabels: number;
11
+ readonly parked: readonly ParkedLabel[];
12
+ };
13
+ export declare function vocabularyBreaches(label: string, item: boolean): string[];
14
+ export declare function labelVocabulary(files: readonly SourceFile[], options: LabelVocabularyOptions): RuleReport;