efficient-web-builder 1.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (406) hide show
  1. package/CHANGELOG.md +47 -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/Breadcrumb.js +1 -1
  12. package/dist/components/Btn.js +1 -1
  13. package/dist/components/CardShadowField.js +1 -1
  14. package/dist/components/CategoryBar.js +1 -1
  15. package/dist/components/ChartSection.js +1 -1
  16. package/dist/components/ChatSettingsPanel.js +1 -1
  17. package/dist/components/ClearColoursDialog.js +1 -1
  18. package/dist/components/ContactLocations.js +5 -5
  19. package/dist/components/CornersField.d.ts +0 -1
  20. package/dist/components/CornersField.js +1 -1
  21. package/dist/components/DocBody.js +1 -1
  22. package/dist/components/DuplicateToPageMenu.js +1 -1
  23. package/dist/components/EditorScreenMount.d.ts +1 -0
  24. package/dist/components/EditorScreenMount.js +1 -0
  25. package/dist/components/EditorStoreProvider.d.ts +1 -0
  26. package/dist/components/EditorStoreProvider.js +1 -0
  27. package/dist/components/FeaturedCover.js +1 -1
  28. package/dist/components/FieldGlyph.js +1 -1
  29. package/dist/components/Footprint.js +1 -1
  30. package/dist/components/HeadingAudit.js +1 -1
  31. package/dist/components/HeroImageField.js +1 -1
  32. package/dist/components/HeroSection.d.ts +2 -6
  33. package/dist/components/HeroSection.js +4 -4
  34. package/dist/components/HoverEffectField.js +1 -1
  35. package/dist/components/ImageBlock.js +1 -1
  36. package/dist/components/ImageFocusRow.js +1 -1
  37. package/dist/components/KeepHint.d.ts +5 -0
  38. package/dist/components/KeepHint.js +1 -0
  39. package/dist/components/LocationsGroupedField.js +1 -1
  40. package/dist/components/LongRead.js +1 -1
  41. package/dist/components/MediaSidePanel.d.ts +1 -6
  42. package/dist/components/MediaSidePanel.js +1 -1
  43. package/dist/components/MediaUploadField.js +1 -1
  44. package/dist/components/MockupSection.js +1 -1
  45. package/dist/components/MultiSelectField.d.ts +4 -1
  46. package/dist/components/MultiSelectField.js +1 -1
  47. package/dist/components/NodeContextMenu.d.ts +2 -96
  48. package/dist/components/NodeContextMenu.js +1 -1
  49. package/dist/components/PageEditGuardNotices.d.ts +1 -1
  50. package/dist/components/PageEditGuardNotices.js +1 -1
  51. package/dist/components/PagesPanel.js +1 -1
  52. package/dist/components/PanelToggleIcon.d.ts +1 -3
  53. package/dist/components/PanelToggleIcon.js +1 -1
  54. package/dist/components/PartResetDot.js +1 -1
  55. package/dist/components/PartSettingsPanel.d.ts +5 -0
  56. package/dist/components/PartSettingsPanel.js +1 -1
  57. package/dist/components/PhotographyField.js +1 -1
  58. package/dist/components/PostCard.js +1 -1
  59. package/dist/components/PreviewDeviceContext.d.ts +2 -4
  60. package/dist/components/PreviewDeviceContext.js +1 -1
  61. package/dist/components/PreviewPartsContext.d.ts +1 -3
  62. package/dist/components/PreviewPartsContext.js +1 -1
  63. package/dist/components/ProcessSteps.js +1 -1
  64. package/dist/components/RMark.js +1 -1
  65. package/dist/components/Reel.js +1 -1
  66. package/dist/components/SearchSelectField.d.ts +3 -1
  67. package/dist/components/SearchSelectField.js +1 -1
  68. package/dist/components/SectionEditor.d.ts +2 -5
  69. package/dist/components/SectionEditor.js +1 -1
  70. package/dist/components/SectionEditorShell.d.ts +1 -1
  71. package/dist/components/SectionEditorShell.js +1 -1
  72. package/dist/components/SectionHeader.js +1 -1
  73. package/dist/components/SectionThemePicker.d.ts +3 -1
  74. package/dist/components/SectionThemePicker.js +1 -1
  75. package/dist/components/SectionThumbnail.js +1 -1
  76. package/dist/components/ShadowColourField.js +1 -1
  77. package/dist/components/SidePanelHead.d.ts +1 -23
  78. package/dist/components/SidePanelHead.js +1 -1
  79. package/dist/components/SideSpaceField.d.ts +0 -1
  80. package/dist/components/SideSpaceField.js +1 -1
  81. package/dist/components/SiteEditorProvider.d.ts +3 -10
  82. package/dist/components/SiteEditorProvider.js +1 -1
  83. package/dist/components/SiteWidthField.d.ts +0 -1
  84. package/dist/components/SiteWidthField.js +1 -1
  85. package/dist/components/SlotChildActionsContext.d.ts +2 -13
  86. package/dist/components/SlotChildActionsContext.js +1 -1
  87. package/dist/components/SlotChildRows.js +1 -1
  88. package/dist/components/SocialLinksPanel.js +1 -1
  89. package/dist/components/SourceStatus.js +1 -1
  90. package/dist/components/Split.js +1 -1
  91. package/dist/components/StandaloneSectionEditor.d.ts +1 -0
  92. package/dist/components/StandaloneSectionEditor.js +1 -1
  93. package/dist/components/StorefrontDocumentContext.d.ts +2 -67
  94. package/dist/components/StorefrontDocumentContext.js +1 -1
  95. package/dist/components/StorefrontEditor.d.ts +1 -0
  96. package/dist/components/StorefrontEditor.js +1 -1
  97. package/dist/components/StorefrontEditorChrome.d.ts +1 -1
  98. package/dist/components/StorefrontEditorChrome.js +1 -1
  99. package/dist/components/StorefrontLinkField.d.ts +9 -0
  100. package/dist/components/StorefrontLinkField.js +1 -1
  101. package/dist/components/StylesPanel.js +1 -1
  102. package/dist/components/Testimonials.js +1 -1
  103. package/dist/components/TextStylesField.js +1 -1
  104. package/dist/components/ThemeCard.js +1 -1
  105. package/dist/components/ThemeColourField.js +1 -1
  106. package/dist/components/ThemeField.js +1 -1
  107. package/dist/components/TickerSettingsPanel.js +1 -1
  108. package/dist/components/Timeline.js +1 -1
  109. package/dist/components/TimelineTrack.js +1 -1
  110. package/dist/components/TitleWithButtons.js +1 -1
  111. package/dist/components/UiIcon.d.ts +2 -10
  112. package/dist/components/UiIcon.js +1 -1
  113. package/dist/components/WebsiteScreenParts.d.ts +2 -0
  114. package/dist/components/WebsiteScreenParts.js +1 -0
  115. package/dist/components/WebsiteSettingsPanel.js +1 -1
  116. package/dist/components/WidthFollowField.js +1 -1
  117. package/dist/components/goldPhrases.js +1 -1
  118. package/dist/components/panel-widths.d.ts +2 -12
  119. package/dist/components/panel-widths.js +1 -1
  120. package/dist/components/storefront-editor.css +147 -1195
  121. package/dist/components/useFooterFocus.d.ts +2 -0
  122. package/dist/components/useFooterFocus.js +1 -0
  123. package/dist/components/useLocationsTree.d.ts +6 -0
  124. package/dist/components/useLocationsTree.js +1 -0
  125. package/dist/components/usePartBridge.d.ts +1 -9
  126. package/dist/components/usePartBridge.js +1 -1
  127. package/dist/components/usePinnedChromeSeed.d.ts +2 -0
  128. package/dist/components/usePinnedChromeSeed.js +1 -0
  129. package/dist/components/useSocialSettings.js +1 -1
  130. package/dist/components/useStorefrontBlogPosts.js +1 -1
  131. package/dist/lib/client-only.d.ts +1 -0
  132. package/dist/lib/client-only.js +1 -0
  133. package/dist/lib/content/contact.js +3 -3
  134. package/dist/lib/content/subscribe.js +1 -1
  135. package/dist/lib/content/view-tiers.d.ts +2 -6
  136. package/dist/lib/content/view-tiers.js +1 -1
  137. package/dist/lib/editor-admin-bridge.d.ts +2 -29
  138. package/dist/lib/editor-admin-bridge.js +1 -1
  139. package/dist/lib/editor-backend-error.d.ts +2 -0
  140. package/dist/lib/editor-backend-error.js +1 -0
  141. package/dist/lib/editor-blocks.d.ts +2 -2
  142. package/dist/lib/editor-chrome.d.ts +2 -52
  143. package/dist/lib/editor-chrome.js +1 -1
  144. package/dist/lib/editor-document-adapter.d.ts +3 -51
  145. package/dist/lib/editor-document-adapter.js +1 -1
  146. package/dist/lib/editor-document-addressing.d.ts +2 -0
  147. package/dist/lib/editor-document-addressing.js +1 -0
  148. package/dist/lib/editor-document-contract.d.ts +1 -0
  149. package/dist/lib/editor-document-contract.js +0 -0
  150. package/dist/lib/editor-document-hooks.d.ts +1 -15
  151. package/dist/lib/editor-document-switch.d.ts +2 -0
  152. package/dist/lib/editor-document-switch.js +1 -0
  153. package/dist/lib/editor-failure.d.ts +1 -4
  154. package/dist/lib/editor-failure.js +1 -1
  155. package/dist/lib/editor-host-inbound.d.ts +1 -1
  156. package/dist/lib/editor-host-inbound.js +1 -1
  157. package/dist/lib/editor-host.d.ts +2 -135
  158. package/dist/lib/editor-host.js +1 -1
  159. package/dist/lib/editor-key-scope.d.ts +1 -3
  160. package/dist/lib/editor-key-scope.js +1 -1
  161. package/dist/lib/editor-load-error.d.ts +1 -4
  162. package/dist/lib/editor-load-error.js +1 -1
  163. package/dist/lib/editor-media-panel.d.ts +2 -23
  164. package/dist/lib/editor-media-panel.js +1 -1
  165. package/dist/lib/editor-media-picker.d.ts +2 -12
  166. package/dist/lib/editor-media-picker.js +1 -1
  167. package/dist/lib/editor-page-lock.d.ts +1 -1
  168. package/dist/lib/editor-page-lock.js +1 -1
  169. package/dist/lib/editor-screen.d.ts +2 -0
  170. package/dist/lib/editor-screen.js +1 -0
  171. package/dist/lib/editor-section-templates.d.ts +1 -2
  172. package/dist/lib/editor-section-templates.js +1 -1
  173. package/dist/lib/editor-session-context.d.ts +1 -7
  174. package/dist/lib/editor-session-context.js +1 -1
  175. package/dist/lib/editor-session.d.ts +2 -45
  176. package/dist/lib/editor-session.js +1 -1
  177. package/dist/lib/editor-site-slug.js +1 -1
  178. package/dist/lib/editor-theme.d.ts +1 -1
  179. package/dist/lib/editor-theme.js +1 -1
  180. package/dist/lib/hero-arrows.d.ts +1 -1
  181. package/dist/lib/links/link-filters.d.ts +1 -0
  182. package/dist/lib/links/link-filters.js +0 -0
  183. package/dist/lib/links/link-picker-setup.d.ts +29 -0
  184. package/dist/lib/links/link-picker-setup.js +1 -0
  185. package/dist/lib/puck/ColourControl.d.ts +4 -1
  186. package/dist/lib/puck/ColourControl.js +1 -1
  187. package/dist/lib/puck/MarkupToolbar.js +1 -1
  188. package/dist/lib/puck/add-palette.d.ts +3 -9
  189. package/dist/lib/puck/add-palette.js +1 -1
  190. package/dist/lib/puck/add-target-types.d.ts +1 -0
  191. package/dist/lib/puck/add-target-types.js +0 -0
  192. package/dist/lib/puck/add-target.d.ts +2 -9
  193. package/dist/lib/puck/block-choice.d.ts +2 -0
  194. package/dist/lib/puck/block-choice.js +1 -0
  195. package/dist/lib/puck/block-offer.js +1 -1
  196. package/dist/lib/puck/block-types-in.d.ts +1 -1
  197. package/dist/lib/puck/block-types-in.js +1 -1
  198. package/dist/lib/puck/blocks/general/CategoryBar/fields.js +1 -1
  199. package/dist/lib/puck/blocks/general/Chart/fields.js +1 -1
  200. package/dist/lib/puck/blocks/general/FAQ/render.js +1 -1
  201. package/dist/lib/puck/blocks/general/FeaturedCover/render.js +1 -1
  202. package/dist/lib/puck/blocks/general/Footprint/fields.js +1 -1
  203. package/dist/lib/puck/blocks/general/IntroBleed/render.js +1 -1
  204. package/dist/lib/puck/blocks/general/LogoBlock/fields.js +1 -1
  205. package/dist/lib/puck/blocks/general/LongRead/render.js +1 -1
  206. package/dist/lib/puck/blocks/general/Masthead/render.js +1 -1
  207. package/dist/lib/puck/blocks/general/Mockup/fields.js +1 -1
  208. package/dist/lib/puck/blocks/general/OrderSummary/render.js +1 -1
  209. package/dist/lib/puck/blocks/general/ParagraphsA/render.js +1 -1
  210. package/dist/lib/puck/blocks/general/PostCard/render.js +1 -1
  211. package/dist/lib/puck/blocks/general/ProcessSteps/render.js +1 -1
  212. package/dist/lib/puck/blocks/general/Series/render.js +1 -1
  213. package/dist/lib/puck/blocks/general/Split/render.js +1 -1
  214. package/dist/lib/puck/blocks/general/Testimonials/fields.js +1 -1
  215. package/dist/lib/puck/blocks/general/Testimonials/render.js +1 -1
  216. package/dist/lib/puck/blocks/general/TimelineTrack/render.js +1 -1
  217. package/dist/lib/puck/blocks/general/index.d.ts +2 -2
  218. package/dist/lib/puck/blocks/placeholders.js +1 -1
  219. package/dist/lib/puck/content-tree.d.ts +2 -43
  220. package/dist/lib/puck/content-tree.js +1 -1
  221. package/dist/lib/puck/controls.d.ts +11 -22
  222. package/dist/lib/puck/controls.js +1 -1
  223. package/dist/lib/puck/device-fields.d.ts +2 -24
  224. package/dist/lib/puck/device-fields.js +1 -1
  225. package/dist/lib/puck/field-buckets.d.ts +2 -0
  226. package/dist/lib/puck/field-buckets.js +1 -0
  227. package/dist/lib/puck/field-groups.d.ts +3 -3
  228. package/dist/lib/puck/field-groups.js +1 -1
  229. package/dist/lib/puck/field-markers.d.ts +2 -16
  230. package/dist/lib/puck/field-markers.js +1 -1
  231. package/dist/lib/puck/footer-block-row-name.d.ts +4 -0
  232. package/dist/lib/puck/footer-block-row-name.js +1 -0
  233. package/dist/lib/puck/footer-blocks.d.ts +2 -10
  234. package/dist/lib/puck/footer-blocks.js +1 -1
  235. package/dist/lib/puck/geocode-resolve.js +1 -1
  236. package/dist/lib/puck/hidable.d.ts +2 -9
  237. package/dist/lib/puck/hidable.js +1 -1
  238. package/dist/lib/puck/locations-tree.js +2 -2
  239. package/dist/lib/puck/neutralization-contracts.js +1 -1
  240. package/dist/lib/puck/palette-sections.d.ts +2 -0
  241. package/dist/lib/puck/palette-sections.js +1 -0
  242. package/dist/lib/puck/panel-fields.d.ts +2 -12
  243. package/dist/lib/puck/panel-fields.js +1 -1
  244. package/dist/lib/puck/panel-no-control-mark.d.ts +2 -7
  245. package/dist/lib/puck/panel-no-control-mark.js +1 -1
  246. package/dist/lib/puck/part-id.d.ts +1 -0
  247. package/dist/lib/puck/part-id.js +1 -0
  248. package/dist/lib/puck/part-rows.js +1 -1
  249. package/dist/lib/puck/pinned-seed-gate.d.ts +2 -12
  250. package/dist/lib/puck/pinned-seed-gate.js +1 -1
  251. package/dist/lib/puck/preview-click.d.ts +1 -2
  252. package/dist/lib/puck/preview-click.js +1 -1
  253. package/dist/lib/puck/preview-dom.d.ts +1 -6
  254. package/dist/lib/puck/preview-dom.js +1 -1
  255. package/dist/lib/puck/preview-events.d.ts +2 -0
  256. package/dist/lib/puck/preview-events.js +1 -0
  257. package/dist/lib/puck/preview-selection-context.d.ts +2 -19
  258. package/dist/lib/puck/preview-selection-context.js +1 -1
  259. package/dist/lib/puck/section-catalog.js +1 -1
  260. package/dist/lib/puck/section-descriptions.js +1 -1
  261. package/dist/lib/puck/section-doc.d.ts +1 -5
  262. package/dist/lib/puck/section-doc.js +1 -1
  263. package/dist/lib/puck/section-groups.d.ts +1 -1
  264. package/dist/lib/puck/slot-child-name.d.ts +2 -8
  265. package/dist/lib/puck/slot-child-name.js +1 -1
  266. package/dist/lib/puck/slot-heal.d.ts +1 -2
  267. package/dist/lib/puck/slot-heal.js +1 -1
  268. package/dist/lib/puck/slot-section-spec.d.ts +1 -0
  269. package/dist/lib/puck/slot-section-spec.js +0 -0
  270. package/dist/lib/puck/slot-specs.d.ts +2 -13
  271. package/dist/lib/puck/style-clipboard.d.ts +1 -19
  272. package/dist/lib/puck/style-clipboard.js +1 -1
  273. package/dist/lib/puck/text-control-kinds.d.ts +2 -13
  274. package/dist/lib/puck/text-control-kinds.js +1 -1
  275. package/dist/lib/puck/text-parts.d.ts +2 -5
  276. package/dist/lib/puck/text-parts.js +1 -1
  277. package/dist/lib/puck/tree-reveal.d.ts +1 -11
  278. package/dist/lib/puck/tree-reveal.js +1 -1
  279. package/dist/lib/puck/use-section-ops.d.ts +2 -25
  280. package/dist/lib/puck/use-section-ops.js +1 -1
  281. package/dist/lib/reel.d.ts +1 -1
  282. package/dist/lib/section-template-types.d.ts +1 -21
  283. package/dist/lib/sections/divider-style.js +1 -1
  284. package/dist/lib/sections/new-section-node.d.ts +2 -0
  285. package/dist/lib/sections/new-section-node.js +1 -0
  286. package/dist/lib/sections/product-card-style.js +1 -1
  287. package/dist/lib/sections/ticker-style.js +1 -1
  288. package/dist/lib/source-rules/accent-token-name.d.ts +20 -0
  289. package/dist/lib/source-rules/accent-token-name.js +4 -0
  290. package/dist/lib/source-rules/button-corners.d.ts +13 -0
  291. package/dist/lib/source-rules/button-corners.js +2 -0
  292. package/dist/lib/source-rules/editor-chrome-note.d.ts +16 -0
  293. package/dist/lib/source-rules/editor-chrome-note.js +13 -0
  294. package/dist/lib/source-rules/editor-context-menu.d.ts +5 -0
  295. package/dist/lib/source-rules/editor-context-menu.js +4 -0
  296. package/dist/lib/source-rules/editor-puck-drag-off.d.ts +8 -0
  297. package/dist/lib/source-rules/editor-puck-drag-off.js +4 -0
  298. package/dist/lib/source-rules/efficient-easel-version.d.ts +9 -0
  299. package/dist/lib/source-rules/efficient-easel-version.js +2 -0
  300. package/dist/lib/source-rules/every-button-follows-the-button-colour.d.ts +12 -0
  301. package/dist/lib/source-rules/every-button-follows-the-button-colour.js +7 -0
  302. package/dist/lib/source-rules/flat-theme-palette.d.ts +10 -0
  303. package/dist/lib/source-rules/flat-theme-palette.js +8 -0
  304. package/dist/lib/source-rules/full-width-means-full.d.ts +8 -0
  305. package/dist/lib/source-rules/full-width-means-full.js +2 -0
  306. package/dist/lib/source-rules/heading-ceiling-sweep.d.ts +11 -0
  307. package/dist/lib/source-rules/heading-ceiling-sweep.js +8 -0
  308. package/dist/lib/source-rules/label-hints.d.ts +7 -0
  309. package/dist/lib/source-rules/label-hints.js +5 -0
  310. package/dist/lib/source-rules/label-vocabulary.d.ts +14 -0
  311. package/dist/lib/source-rules/label-vocabulary.js +5 -0
  312. package/dist/lib/source-rules/mirrored-rows-agree.d.ts +19 -0
  313. package/dist/lib/source-rules/mirrored-rows-agree.js +10 -0
  314. package/dist/lib/source-rules/no-comment-in-jsx.d.ts +9 -0
  315. package/dist/lib/source-rules/no-comment-in-jsx.js +7 -0
  316. package/dist/lib/source-rules/no-glyph-rows.d.ts +15 -0
  317. package/dist/lib/source-rules/no-glyph-rows.js +4 -0
  318. package/dist/lib/source-rules/one-colour-control.d.ts +7 -0
  319. package/dist/lib/source-rules/one-colour-control.js +2 -0
  320. package/dist/lib/source-rules/one-dropdown.d.ts +10 -0
  321. package/dist/lib/source-rules/one-dropdown.js +4 -0
  322. package/dist/lib/source-rules/one-editable-list.d.ts +16 -0
  323. package/dist/lib/source-rules/one-editable-list.js +6 -0
  324. package/dist/lib/source-rules/one-editor-list.d.ts +7 -0
  325. package/dist/lib/source-rules/one-editor-list.js +3 -0
  326. package/dist/lib/source-rules/one-right-click.d.ts +10 -0
  327. package/dist/lib/source-rules/one-right-click.js +2 -0
  328. package/dist/lib/source-rules/one-switch.d.ts +9 -0
  329. package/dist/lib/source-rules/one-switch.js +7 -0
  330. package/dist/lib/source-rules/one-up-level-link.d.ts +16 -0
  331. package/dist/lib/source-rules/one-up-level-link.js +18 -0
  332. package/dist/lib/source-rules/panel-colour-tokens.d.ts +48 -0
  333. package/dist/lib/source-rules/panel-colour-tokens.js +20 -0
  334. package/dist/lib/source-rules/panel-no-divider-lines.d.ts +14 -0
  335. package/dist/lib/source-rules/panel-no-divider-lines.js +10 -0
  336. package/dist/lib/source-rules/panel-one-arrow.d.ts +21 -0
  337. package/dist/lib/source-rules/panel-one-arrow.js +1 -0
  338. package/dist/lib/source-rules/panel-one-hover.d.ts +8 -0
  339. package/dist/lib/source-rules/panel-one-hover.js +2 -0
  340. package/dist/lib/source-rules/panel-rows-use-the-shared-row.d.ts +23 -0
  341. package/dist/lib/source-rules/panel-rows-use-the-shared-row.js +22 -0
  342. package/dist/lib/source-rules/part-slider-fills-row.d.ts +11 -0
  343. package/dist/lib/source-rules/part-slider-fills-row.js +29 -0
  344. package/dist/lib/source-rules/part-style-naming.d.ts +10 -0
  345. package/dist/lib/source-rules/part-style-naming.js +2 -0
  346. package/dist/lib/source-rules/puck-document-surfaces.d.ts +16 -0
  347. package/dist/lib/source-rules/puck-document-surfaces.js +8 -0
  348. package/dist/lib/source-rules/reduced-motion.d.ts +26 -0
  349. package/dist/lib/source-rules/reduced-motion.js +9 -0
  350. package/dist/lib/source-rules/route-content-width.d.ts +22 -0
  351. package/dist/lib/source-rules/route-content-width.js +5 -0
  352. package/dist/lib/source-rules/rule.d.ts +21 -0
  353. package/dist/lib/source-rules/rule.js +1 -0
  354. package/dist/lib/source-rules/section-theme-preview.d.ts +9 -0
  355. package/dist/lib/source-rules/section-theme-preview.js +8 -0
  356. package/dist/lib/source-rules/site-gutter.d.ts +6 -0
  357. package/dist/lib/source-rules/site-gutter.js +4 -0
  358. package/dist/lib/source-rules/site-square-by-default.d.ts +17 -0
  359. package/dist/lib/source-rules/site-square-by-default.js +3 -0
  360. package/dist/lib/source-rules/site-width.d.ts +11 -0
  361. package/dist/lib/source-rules/site-width.js +7 -0
  362. package/dist/lib/source-rules/size-token-paints.d.ts +8 -0
  363. package/dist/lib/source-rules/size-token-paints.js +11 -0
  364. package/dist/lib/source-rules/social-icons-import.d.ts +6 -0
  365. package/dist/lib/source-rules/social-icons-import.js +6 -0
  366. package/dist/lib/source-rules/source-status-is-shared.d.ts +7 -0
  367. package/dist/lib/source-rules/source-status-is-shared.js +1 -0
  368. package/dist/lib/source-rules/style-face-matches-size.d.ts +10 -0
  369. package/dist/lib/source-rules/style-face-matches-size.js +4 -0
  370. package/dist/lib/source-rules/support/accent-rename-bytes.d.ts +5 -0
  371. package/dist/lib/source-rules/support/accent-rename-bytes.js +1 -0
  372. package/dist/lib/source-rules/support/css-box-model.d.ts +5 -0
  373. package/dist/lib/source-rules/support/css-box-model.js +1 -0
  374. package/dist/lib/source-rules/support/css-length.d.ts +2 -0
  375. package/dist/lib/source-rules/support/css-length.js +1 -0
  376. package/dist/lib/source-rules/support/css-rules.d.ts +12 -0
  377. package/dist/lib/source-rules/support/css-rules.js +1 -0
  378. package/dist/lib/source-rules/support/css-source.d.ts +2 -0
  379. package/dist/lib/source-rules/support/css-source.js +3 -0
  380. package/dist/lib/source-rules/support/label-extraction.d.ts +14 -0
  381. package/dist/lib/source-rules/support/label-extraction.js +5 -0
  382. package/dist/lib/source-rules/support/panel-select-cascade.d.ts +72 -0
  383. package/dist/lib/source-rules/support/panel-select-cascade.js +2 -0
  384. package/dist/lib/source-rules/support/part-slider-call-sites.d.ts +9 -0
  385. package/dist/lib/source-rules/support/part-slider-call-sites.js +17 -0
  386. package/dist/lib/source-rules/support/registry-source.d.ts +14 -0
  387. package/dist/lib/source-rules/support/registry-source.js +3 -0
  388. package/dist/lib/source-rules/support/ts-source.d.ts +2 -0
  389. package/dist/lib/source-rules/support/ts-source.js +3 -0
  390. package/dist/lib/source-rules/theme-mode-controls-gone.d.ts +10 -0
  391. package/dist/lib/source-rules/theme-mode-controls-gone.js +5 -0
  392. package/dist/lib/source-rules/upload-intake-guard.d.ts +11 -0
  393. package/dist/lib/source-rules/upload-intake-guard.js +6 -0
  394. package/dist/lib/source-rules/word-styles.d.ts +8 -0
  395. package/dist/lib/source-rules/word-styles.js +4 -0
  396. package/dist/lib/theme-row-persist.js +1 -1
  397. package/dist/lib/theme-save-message.js +1 -1
  398. package/dist/lib/web-backend.d.ts +4 -59
  399. package/dist/lib/web-backend.js +1 -1
  400. package/dist/lib/website-document-addressing.d.ts +2 -0
  401. package/dist/lib/website-document-addressing.js +1 -0
  402. package/dist/lib/website-host-inbound.d.ts +1 -0
  403. package/dist/lib/website-host-inbound.js +1 -0
  404. package/dist/lib/website-session.d.ts +5 -0
  405. package/dist/lib/website-session.js +1 -0
  406. package/package.json +18 -8
@@ -0,0 +1,9 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type SectionThemePreviewOptions = {
3
+ readonly home?: string;
4
+ readonly panels?: readonly string[];
5
+ readonly triggerPanels?: readonly string[];
6
+ readonly menuSheet?: string;
7
+ readonly minFiles: number;
8
+ };
9
+ export declare function sectionThemePreview(files: readonly SourceFile[], options: SectionThemePreviewOptions): RuleReport;
@@ -0,0 +1,8 @@
1
+ import{ruleChecks as u}from"./rule.js";const o="sf-theme-page",d=new RegExp(`["\`\\s]${o}`),w=/from\s+"@\/components\/ThemeCard"/,m=/ThemeColourRow\b(?!s)/;function f(r,n){const t=r.indexOf(`
2
+ ${n} {`);if(t<0)return"";const i=r.indexOf("{",t);return r.slice(i+1,r.indexOf(`
3
+ }`,i)).replace(/\/\*[\s\S]*?\*\//g,"")}function b(r,n){const{check:t,report:i}=u("section theme preview"),h=r.filter(e=>e.path.endsWith(".tsx")),c=e=>r.find(s=>s.path===e)?.text;t("the class reader finds the preview class written into markup",d.test(`<div className="${o} x" />`)),t("…and not a longer class that only starts the same",!d.test(`<div className="x-${o}" />`)),t("a single colour row is told apart from the shared run",m.test("<ThemeColourRow label />")&&!m.test("<ThemeColourRows colors={c} />")),t("the rule-body reader cuts the comment and keeps the declaration",f(`
4
+ .m {
5
+ /* not #eff6ff */
6
+ background: var(--sf-accent-tint);
7
+ }
8
+ `,".m").trim()==="background: var(--sf-accent-tint);"),t(`read the .tsx files (${h.length}, at least ${n.minFiles})`,h.length>=n.minFiles,"fewer means the walk broke");const a=h.filter(e=>d.test(e.text)).map(e=>e.path);t(n.home?`\`.${o}\` is written by exactly one component, and it is ${n.home}`:`no file here writes \`.${o}\``,n.home?a.length===1&&a[0]===n.home:a.length===0,`it is written by ${a.length?a.join(", "):"nothing"}. Two files drawing one preview is the defect card 66503 closes, one level along: they match on the day they are written and drift on every day after — draw it with the builder's components/ThemeCard.tsx`);for(const e of n.panels??[]){const s=c(e)??"";t(`${e} reaches the drawing by import`,w.test(s),"it imports no shared drawing, so whatever it puts on screen is its own"),t(`${e} draws no colour row of its own`,s.length>0&&!m.test(s),"it reaches a row directly instead of drawing the shared run, which is how the two lists drifted apart the first time")}for(const e of n.triggerPanels??[])t(`${e} reaches the closed drawing by import too`,/ThemeCardTrigger/.test(c(e)??""),"a trigger assembled in the panel is a second file drawing a theme");if(n.menuSheet){const e=c(n.menuSheet)??"",s=f(e,".sf-secttheme-menu"),l=f(e,".sf-multi-menu");t(`the instrument found both rules in ${n.menuSheet}, so the absences below mean something`,/background/.test(s)&&/border/.test(l),`.sf-secttheme-menu read ${s.length} chars and .sf-multi-menu ${l.length}`),t("the theme list fills itself with the accent tint",/background:\s*var\(--sf-accent-tint\)/.test(s)),t("…as the FILL, leaving the shared grey frame alone",!/border(-color)?:/.test(s)),t("…and NO literal colour is written there",!/#[0-9a-fA-F]{3,8}\b/.test(s)&&!/\brgba?\(/.test(s),"it must be `--sf-accent-tint`"),t("the SHARED dropdown container is still untinted",!/--sf-accent-tint/.test(l),"`.sf-multi-menu` is worn by MultiSelectField too; tinting every dropdown was offered and declined")}return i()}export{b as sectionThemePreview};
@@ -0,0 +1,6 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type SiteGutterOptions = {
3
+ readonly minFiles: number;
4
+ readonly gutterFloor: number;
5
+ };
6
+ export declare function siteGutter(files: readonly SourceFile[], options: SiteGutterOptions): RuleReport;
@@ -0,0 +1,4 @@
1
+ import{SITE_GUTTER as f}from"../content/page-width.js";import{ruleChecks as h}from"./rule.js";const g=/px-\[(?:min\([^)]*,)?var\(--sf-site-gutter,(\d+)px\)/g;function u(s){return[...s.matchAll(g)].map(e=>({match:e[0],fallback:Number(e[1])}))}function p(s,e){const{check:a,report:c}=h("site gutter");{const t=i=>`var(--sf-site-gutter,${i}px)`,r=u([`className="px-[${t(40)}]"`,`"max-[720px]:px-[min(20px,${t(60)})]"`,'"px-10"',`"py-[${t(40)}]"`].join(`
2
+ `));a("the reader finds a whole gutter and a narrow step, with their fallbacks",r.length===2&&r[0].fallback===40&&r[1].fallback===60,JSON.stringify(r))}const l=s.filter(t=>/\.tsx?$/.test(t.path));a(`read the source (${l.length} TS/TSX files, at least ${e.minFiles})`,l.length>=e.minFiles,"fewer means the walk broke, and the check below is vacuous");let o=0;const n=[];for(const t of l)for(const r of u(t.text))o+=1,r.fallback!==f.default&&n.push(`${t.path}: ${r.match}`);return a(`found >= ${e.gutterFloor} literal gutters (found ${o})`,o>=e.gutterFloor,"fewer means the gutters went back to a hand-written number, or the reader broke"),a(`every --sf-site-gutter fallback is SITE_GUTTER.default (${f.default}px)`,n.length===0,`${n.length} gutter(s) name a second "standard gutter":
3
+ ${n.join(`
4
+ `)}`),c()}export{p as siteGutter};
@@ -0,0 +1,17 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ type Tokens = Readonly<Record<string, string>>;
3
+ export type CornerPublishers = {
4
+ readonly siteCornerRadiusTokens: (v?: number | string | null) => Tokens;
5
+ readonly cornerRadiusTokens: (v?: number | string | null) => Tokens;
6
+ readonly cornerStyleToPx: Readonly<Record<string, number | null>>;
7
+ readonly normalizeCornerStyle: (v?: string | null) => string;
8
+ readonly sectionCornerStyle: (value: unknown) => unknown;
9
+ };
10
+ export type SiteSquareByDefaultOptions = {
11
+ readonly corners?: CornerPublishers;
12
+ readonly livePage?: string;
13
+ readonly canvas?: string;
14
+ readonly minFiles: number;
15
+ };
16
+ export declare function siteSquareByDefault(files: readonly SourceFile[], options: SiteSquareByDefaultOptions): RuleReport;
17
+ export {};
@@ -0,0 +1,3 @@
1
+ import{ruleChecks as f}from"./rule.js";const c=["--radius","--radius-xs","--radius-sm","--radius-md","--radius-lg","--radius-xl","--radius-2xl","--radius-3xl","--radius-4xl"],i=(a,r)=>JSON.stringify(a)===JSON.stringify(r);function h(a){const r=[];return a.text.split(`
2
+ `).forEach((s,u)=>{for(const o of s.matchAll(/["'`]([^"'`]*\brounded-full\b[^"'`]*)["'`]/g))/(^|\s)px-(?!0\b|0\.5\b|1\b|px\b)[\d[]/.test(o[1])&&r.push(`${a.path}:${u+1}`)}),r}function g(a,r){const{check:s,report:u}=f("site square by default"),o=e=>a.find(n=>n.path===e),t=r.corners;if(t){const e=t.siteCornerRadiusTokens(null);s("a website that has chosen no corners publishes every step of the radius scale a `rounded-*` utility reads",c.every(n=>n in e),`missing: ${c.filter(n=>!(n in e)).join(", ")}`),s("…and the card's and the button's own properties","--sf-card-radius"in e&&"--sf-button-radius"in e),s("…every one of them 0 — square",Object.values(e).every(n=>n==="0"),JSON.stringify(e)),s("…exactly what a site that chose 0 publishes",i(e,t.cornerRadiusTokens(0))),s("an absent value and a blank one are the same statement",i(t.siteCornerRadiusTokens(void 0),e)&&i(t.siteCornerRadiusTokens(""),e)&&i(t.siteCornerRadiusTokens(" "),e)),s("a website that has chosen keeps its number",i(t.siteCornerRadiusTokens(16),t.cornerRadiusTokens(16))&&t.siteCornerRadiusTokens(16)["--radius-lg"]==="1rem"),s('the retired `large` word is still 16, and `small` is still "not set"',t.cornerStyleToPx[t.normalizeCornerStyle("large")]===16&&t.cornerStyleToPx[t.normalizeCornerStyle("small")]===null),s('`cornerRadiusTokens(null)` publishes NOTHING — not set there means "follow the site"',i(t.cornerRadiusTokens(null),{})),s("…so an untouched section declares no radius of its own",t.sectionCornerStyle(null)===void 0,JSON.stringify(t.sectionCornerStyle(null)))}if(r.livePage!==void 0){const e=o(r.livePage);if(s(`read the live page's layout ${r.livePage}`,!!e)){const n=e.text;s("the live page publishes the site's corners through siteCornerRadiusTokens",/const siteCornerTokens = siteCornerRadiusTokens\(/.test(n)&&/\.\.\.siteCornerTokens,/.test(n)),s("…and never through the two that publish nothing for an untouched site",!/\bcornerRadiusTokens\(|\bcornerStyleTokens\(/.test(n))}}if(r.canvas!==void 0){const e=o(r.canvas);if(s(`read the editor canvas ${r.canvas}`,!!e)){const n=e.text;s("the editor canvas publishes them through the same function",/\.\.\.siteCornerRadiusTokens\(cornerRadiusPx\),/.test(n)&&/siteCardRadiusTokens\(siteCornerRadiusTokens\(cornerRadiusPx\)\)/.test(n)),s("…and nowhere through the bare one",!/(?<![A-Za-z])cornerRadiusTokens\(/.test(n))}}{const e=h({path:"fixture.tsx",text:['<button className="rounded-full px-4 py-2">Sign in</button>','<span className="rounded-full px-1 text-xs">3</span>','<span className="h-2 w-2 rounded-full bg-fg" />',"<i className={`rounded-full px-[10px]`} />"].join(`
3
+ `)});s("the pill sweep flags a padded circle, and neither a count badge nor a dot",i(e,["fixture.tsx:1","fixture.tsx:4"]),JSON.stringify(e))}const l=a.filter(e=>e.path.endsWith(".tsx"));s(`the pill sweep read the .tsx files (${l.length}, at least ${r.minFiles})`,l.length>=r.minFiles&&l.length>0);const d=l.flatMap(h);return s("no control with horizontal padding wears the circle utility",d.length===0,d.join(", ")),u()}export{g as siteSquareByDefault};
@@ -0,0 +1,11 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type SiteWidthOptions = {
3
+ readonly minFiles: number;
4
+ readonly capFloor: number;
5
+ readonly sections?: readonly string[];
6
+ readonly tokenSheets?: readonly string[];
7
+ readonly chromeStyled?: readonly string[];
8
+ readonly chromeClassed?: readonly string[];
9
+ readonly carrier?: string;
10
+ };
11
+ export declare function siteWidth(files: readonly SourceFile[], options: SiteWidthOptions): RuleReport;
@@ -0,0 +1,7 @@
1
+ import{CHROME_WIDTH_CAP_CLASS as S,PAGE_WIDTH as f,PRE_CHROME_WIDTH_CAP_CLASS as b,PRE_SITE_WIDTH_CAP_CLASS as _,SITE_WIDTH_CAP_CLASS as d,SITE_WIDTH_VAR as g,siteWidthTokens as p}from"../content/page-width.js";import{ruleChecks as C}from"./rule.js";const y=a=>a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),T=a=>/^\s*(\/\/|\*|\/\*)/.test(a);function m(a,i){const t=[];for(const o of a)o.text.split(`
2
+ `).forEach((r,h)=>{i.test(r)&&!T(r)&&t.push({file:o.path,line:h+1,text:r})});return t}function u(a,i){const t=/max-w-\[([^\]]+)\]/.exec(a);if(!t)return null;const o=/^var\((--[\w-]+),\s*([^)]+)\)$/.exec(t[1].trim());return o?i[o[1]]??o[2]:t[1].trim()}function E(a,i){const{check:t,report:o}=C("site width"),r=e=>a.find(n=>n.path===e);{const e=["max-w-[",f.default,"px]"].join(""),n={path:"fixture.tsx",text:[`const a = "mx-auto ${e}";`,`// a note naming ${e} is not a use`,`const b = "${d} px-10";`,`const c = "max-w-[var(${g},1400px)]";`].join(`
3
+ `)},s=m([n],new RegExp(y(_)));t("R1 flags a bare 1600 cap, and not a comment naming one",s.length===1&&s[0].line===1,JSON.stringify(s));const l=m([n],new RegExp(`max-w-\\[var\\(${g}`));t("R2 finds every cap that reads the token, and only the drifted one differs",l.length===2&&l.filter(c=>!c.text.includes(d)).map(c=>c.line).join()==="4",JSON.stringify(l)),t("the cascade reader takes the fallback unset and the published width when set",u(d,p(void 0))===`${f.default}px`&&u(d,p(1400))==="1400px")}const h=a.filter(e=>/\.tsx?$/.test(e.path));t(`read the source (${h.length} TS/TSX files, at least ${i.minFiles})`,h.length>=i.minFiles,"fewer means the walk broke, and every absence below is vacuous");const w=m(h,new RegExp(y(_)));t("R1 no page-width cap is a hand-written 1600 literal any more",w.length===0,`page width is still hard-coded in ${w.length} place(s):
4
+ ${w.map(e=>`${e.file}:${e.line}`).join(`
5
+ `)}`);const $=m(h,new RegExp(`max-w-\\[var\\(${g}`));t(`found >= ${i.capFloor} caps that read the site width token (found ${$.length})`,$.length>=i.capFloor,"fewer means caps went back to a number, or the reader broke");const x=$.filter(e=>/max-w-\[var\(--sf-site-width[^\]]*\]/.exec(e.text)?.[0]!==d).map(e=>`${e.file}:${e.line} — ${e.text.trim()}`);t("R2 every page-width cap reads the site token, with the built-in as its fallback",x.length===0,`${x.length} cap(s) do not match SITE_WIDTH_CAP_CLASS (${d}):
6
+ ${x.join(`
7
+ `)}`);for(const e of i.sections??[]){const n=r(e),s=[...(n?.text??"").matchAll(/"([^"]*max-w-\[[^\]]+\][^"]*)"/g)].map(c=>c[1]);if(!t(`${e} carries a page-width cap`,s.length>0,n?"it no longer carries one":"it is not among the files handed in"))continue;const l=s.filter(c=>u(c,p(void 0))!==`${f.default}px`||u(c,p(1400))!=="1400px");t(`${e} renders at ${f.default} unset, and at the site's width when set`,l.length===0,l.join(" · "))}for(const e of i.tokenSheets??[]){const n=/--sf-site-width:\s*([^;]+);/.exec(r(e)?.text??"");t(`${e} defines the token at the built-in width`,n?.[1].trim()===`${f.default}px`,n?`it defines ${n[1].trim()}`:`${e} defines no --sf-site-width (or was not read)`)}for(const e of i.chromeStyled??[]){const n=r(e)?.text??"";t(`${e} caps at the site's width (chromeWidthStyle)`,/chromeWidthStyle\(/.test(n),`${e} does not cap at the site's width`),t(`${e} does not cap at the built-in number (pageWidthStyle)`,n.length>0&&!/^(?!.*\/\/).*\bpageWidthStyle\(/m.test(n),`${e} still caps at the built-in number, so the chrome ignores the site's width`)}for(const e of i.chromeClassed??[]){const n=r(e)?.text??"";t(`${e} carries ${S}`,n.includes(S),"its tabs sit one gutter wide of the page"),t(`${e} no longer carries the old ${b}`,n.length>0&&!n.includes(b))}if(i.carrier){const e=r(i.carrier)?.text??"",n=/const carrierStyle = \{([\s\S]*?)\} as CSSProperties;/.exec(e),s=/const contentThemeStyle = \{([\s\S]*?)\} as CSSProperties;/.exec(e);t(`${i.carrier} builds a carrierStyle`,!!n)&&n&&t("the site width is published on the accent carrier, so the chrome can read it",/siteWidthTokens\(/.test(n[1]),"the site width is not published on the accent carrier, so the chrome cannot read it"),t(`${i.carrier} builds a contentThemeStyle`,!!s)&&s&&t("…and not on <main> as well — one emitter, not two",!/siteWidthTokens\(/.test(s[1]),"the site width is still published on <main> as well")}return o()}export{E as siteWidth};
@@ -0,0 +1,8 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type SizeTokenPaintsOptions = {
3
+ readonly baseline?: Readonly<Record<string, number>>;
4
+ readonly minFiles: number;
5
+ };
6
+ export declare function stripComments(src: string): string;
7
+ export declare function brokenCount(src: string): number;
8
+ export declare function sizeTokenPaints(files: readonly SourceFile[], options: SizeTokenPaintsOptions): RuleReport;
@@ -0,0 +1,11 @@
1
+ import{ruleChecks as d}from"./rule.js";const b=/text-\[(?!length:)[^\]]*--size-[a-zA-Z]+[^\]]*\]/g;function w(t){let i="",e=0;const l=t.length;for(;e<l;){const a=t[e],h=t[e+1];if(a==="/"&&h==="/"){for(;e<l&&t[e]!==`
2
+ `;)i+=" ",e++;continue}if(a==="/"&&h==="*"){for(;e<l&&!(t[e]==="*"&&t[e+1]==="/");)i+=t[e]===`
3
+ `?`
4
+ `:" ",e++;i+=" ",e+=2;continue}if(a==="`"){for(i+=a,e++;e<l;){if(t[e]==="\\"){i+=t[e]+(t[e+1]??""),e+=2;continue}if(t[e]==="`"){i+="`",e++;break}i+=t[e],e++}continue}if(a==='"'||a==="'"){let o=e+1,r=!1;for(;o<l&&t[o]!==`
5
+ `;){if(t[o]==="\\"){o+=2;continue}if(t[o]===a){r=!0;break}o++}if(!r){i+=a,e++;continue}i+=t.slice(e,o+1),e=o+1;continue}i+=a,e++}return i}function f(t){return(w(t).match(b)??[]).length}function x(){const t="text-[";return[{name:"empty source",src:"",want:0},{name:"no size token at all",src:`<p className="${t}13px] font-mono">hi</p>`,want:0},{name:"the broken form",src:`<p className="${t}var(--size-label,13px)]">hi</p>`,want:1},{name:"the working form",src:`<p className="${t}length:var(--size-label,13px)]">hi</p>`,want:0},{name:"two on one line, desktop and phone",src:`<h2 className="${t}var(--size-headingMedium,44px)] max-[800px]:${t}var(--size-headingMedium,32px)]">t</h2>`,want:2},{name:"wrapped in min(), still broken",src:`<h1 className="${t}min(var(--size-headingLarge,64px),var(--sf-heading-max,9999px))]">t</h1>`,want:1},{name:"a line comment is not code",src:`// ${t}var(--size-label,13px)] is the broken form`,want:0},{name:"a block comment is not code",src:`/* ${t}var(--size-label,13px)] */`,want:0},{name:"an apostrophe does not stop a later comment being stripped",src:`<p>it doesn't paint</p>
6
+ // ${t}var(--size-label,13px)]
7
+ <p>that's why</p>`,want:0}]}function $(t,i){const{check:e,report:l}=d("size token paints"),a=i.baseline??{};let h=!0;for(const n of x()){const s=f(n.src);h=e(`fixture "${n.name}": ${n.want}`,s===n.want,`got ${s}`)&&h}if(!h)return e("the extractor is right, so its verdict on the tree means something",!1,"a fixture failed above"),l();const o=t.filter(n=>/\.tsx?$/.test(n.path));e(`read the source (${o.length} TS/TSX files, at least ${i.minFiles})`,o.length>=i.minFiles,"fewer means the walk broke, and the check below is vacuous");const r=[],p=[];let m=0;for(const n of o){const s=f(n.text);m+=s;const c=a[n.path]??0;s>c?r.push(`${n.path} — ${s} broken, baseline allows ${c}`):s<c&&p.push(`${n.path} — ${s} broken, baseline says ${c}`)}const u=Object.values(a).reduce((n,s)=>n+s,0);e(`no text size is written in the form that compiles to \`color:\` (${m} broken place(s) found, the baseline allows ${u})`,r.length===0,`${r.length} file(s) over budget:
8
+ ${r.join(`
9
+ `)}
10
+ Write it as \`text-[length:var(--size-<role>,<built-in>)]\`. Without \`length:\` Tailwind reads the var() as a
11
+ colour, the declaration is dropped as invalid, and the site's text style setting reaches nothing.`);for(const[n]of Object.entries(a))e(`the baseline entry is still a file: ${n}`,o.some(s=>s.path===n),"delete the stale entry");for(const n of p)e(`improved — lower its baseline entry: ${n}`,!0);return l()}export{f as brokenCount,$ as sizeTokenPaints,w as stripComments};
@@ -0,0 +1,6 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type SocialIconsImportOptions = {
3
+ readonly helper?: string;
4
+ readonly minFiles: number;
5
+ };
6
+ export declare function socialIconsImport(files: readonly SourceFile[], options: SocialIconsImportOptions): RuleReport;
@@ -0,0 +1,6 @@
1
+ import{ruleChecks as d}from"./rule.js";const a=n=>JSON.stringify(n),g="the website builder's components/socialIcons.tsx (`<SocialMark>`)",$=/\.(ts|tsx|js|jsx|mjs|cjs)$/,u=/(?:from\s*|import\s*\(\s*|require\s*\(\s*|import\s+)["'](@tabler\/[^"']+)["']/g,f=/(?:import\s*\*\s*as\s+\w+\s*from|export\s*\*\s*(?:as\s+\w+\s*)?from)\s*["']@tabler\/[^"']+["']/,x=["Instagram","Facebook","Tiktok","Youtube","X","Linkedin","Pinterest","Whatsapp"],c=n=>[...n.matchAll(u)].map(t=>t[1]);function k(n,t){const{check:r,report:b}=d("social icons import");{const e=["@tabler","icons-react"].join("/"),s=[["a named import",`import { IconBrandX } from ${a(e)};`,!1],["a dynamic import()",`const m = await import(${a(e)});`,!1],["a require()",`const t = require(${a(e)});`,!1],["a side-effect import",`import ${a(e)};`,!1],["a namespace import",`import * as Tabler from ${a(e)};`,!0],["a star re-export",`export * from ${a(e)};`,!0],["a named star re-export",`export * as T from ${a(e)};`,!0]];for(const[l,o,p]of s)r(`the reader finds ${l}`,c(o).length===1),r(`…and ${p?"flags":"does not flag"} it as the whole set`,f.test(o)===p);r("a package that is not Tabler is not Tabler",c('import x from "@tablerish/x";').length===0)}const i=n.filter(e=>$.test(e.path));r(`read the source (${i.length} JS/TS files, at least ${t.minFiles})`,i.length>=t.minFiles,"fewer means the walk broke, and every check below is vacuous");const m=i.filter(e=>c(e.text).length>0&&e.path!==t.helper).map(e=>e.path);r(t.helper?`only ${t.helper} imports Tabler`:"no file here imports Tabler",m.length===0,`Tabler is imported outside its one home:
2
+ ${m.join(`
3
+ `)}
4
+ Draw social marks through ${g} instead.`);const h=i.filter(e=>f.test(e.text)).map(e=>e.path);if(r("nothing imports the whole Tabler set",h.length===0,`a namespace or star import from Tabler ships all ~6,000 icons (552 KB gzip):
5
+ ${h.join(`
6
+ `)}`),t.helper){const e=i.find(s=>s.path===t.helper);if(r(`read the helper ${t.helper}`,!!e,"the social marks have lost their one home")&&e){const s=[...new Set(c(e.text))];r("the helper imports from @tabler/icons-react only (the React build, per-icon rewritable)",s.length===1&&s[0]==="@tabler/icons-react",JSON.stringify(s));const l=x.flatMap(o=>[`IconBrand${o}`,`IconBrand${o}Filled`]).filter(o=>!new RegExp(`\\b${o},`).test(e.text));r("the helper imports the sixteen marks by name",l.length===0,`no longer imports ${l.join(", ")}`)}}return b()}export{k as socialIconsImport};
@@ -0,0 +1,7 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type SourceStatusIsSharedOptions = {
3
+ readonly home?: string;
4
+ readonly callers: readonly string[];
5
+ readonly minFiles: number;
6
+ };
7
+ export declare function sourceStatusIsShared(files: readonly SourceFile[], options: SourceStatusIsSharedOptions): RuleReport;
@@ -0,0 +1 @@
1
+ import{ruleChecks as d}from"./rule.js";const n=/sf-blogsource-status/,r=/from "@\/components\/SourceStatus"/;function f(m,t){const{check:s,report:i}=d("source status is shared");s("the import reader finds the shared block's import",r.test('import SourceStatus from "@/components/SourceStatus";')),s("…and not a module whose name only starts the same",!r.test('import X from "@/components/SourceStatusLegacy";')),s("the class reader finds a hand-drawn status line",n.test('<p className="sf-blogsource-status" data-state={s}>'));const a=m.filter(e=>e.path.endsWith(".tsx"));s(`read the .tsx files (${a.length}, at least ${t.minFiles})`,a.length>=t.minFiles,"fewer means the walk broke, and every absence below is vacuous");const h=a.filter(e=>e.path!==t.home&&n.test(e.text)).map(e=>e.path);if(s(t.home?`only ${t.home} draws the status line`:"no file here draws the status line",h.length===0,`${h.join(" · ")} — draw it with the builder's components/SourceStatus.tsx`),t.home){const e=a.find(l=>l.path===t.home);s(`…and ${t.home} really does draw it`,!!e&&n.test(e?.text??""))}const o=t.callers.filter(e=>!r.test(a.find(l=>l.path===e)?.text??""));s(`every named caller imports the shared block (${t.callers.length})`,t.callers.length>0&&o.length===0,t.callers.length===0?"no caller is named — this half measures nothing":o.join(" · "));const c=a.filter(e=>e.path!==t.home&&!t.callers.includes(e.path)&&r.test(e.text)).map(e=>e.path);return s("…and no caller is missing from the list",c.length===0,c.join(" · ")),i()}export{f as sourceStatusIsShared};
@@ -0,0 +1,10 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type StyleFaceMatchesSizeOptions = {
3
+ readonly drawRoots: readonly string[];
4
+ readonly themeSheet?: string;
5
+ readonly accentMarkup?: readonly string[];
6
+ readonly fontStore?: string;
7
+ readonly minFiles: number;
8
+ readonly pairFloor: number;
9
+ };
10
+ export declare function styleFaceMatchesSize(files: readonly SourceFile[], options: StyleFaceMatchesSizeOptions): RuleReport;
@@ -0,0 +1,4 @@
1
+ import{ruleChecks as m}from"./rule.js";const u={"font-display":"headingLarge","font-heading2":"headingMedium","font-heading3":"headingSmall","font-serif":"accent","font-body":"body","font-mono":"label"};function p(i){const n=/(?<![-\w:])((?:[a-z0-9-]+(?:-\[[^\]]*\])?:)*)text-\[length:[^"'`]*?--size-(headingLarge|headingMedium|headingSmall|accent|body|label)\b/g,t=new Set;for(const o of i.matchAll(n))o[1]===""&&t.add(o[2]);return t.size===1?[...t][0]:null}function $(i){const n=/(?<![-\w])((?:[a-z0-9-]+(?:-\[[^\]]*\])?:)*)(font-display|font-heading2|font-heading3|font-serif|font-body|font-mono)(?![-\w])/g,t=new Set;for(const o of i.matchAll(n))o[1].includes("&")||t.add(o[2]);return[...t]}function f(i){const n=p(i);if(!n)return null;const t=$(i);return t.length===1?{step:n,face:t[0]}:null}const w=i=>i.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,"");function S(i,n){const{check:t,report:o}=m("style face matches size"),d=e=>i.find(s=>s.path===e)?.text;{const e=r=>`text-[length:var(--size-${r},44px)]`,s=r=>JSON.stringify(f(r));t("a face and a base size are read as a pair",s(`"font-display ${e("headingMedium")}"`)==='{"step":"headingMedium","face":"font-display"}'),t("…a size inside a variant is not the base",f(`"font-heading2 max-[800px]:${e("headingSmall")}"`)===null),t("…the base wins over a variant on the same line",s(`"font-heading3 ${e("headingSmall")} md:${e("headingMedium")}"`)==='{"step":"headingSmall","face":"font-heading3"}'),t("…a face behind a variant naming `&` is a descendant's, not this element's",f(`"group-[&_em]:font-serif ${e("headingLarge")}"`)===null),t("…while a bare `[&_em]:` face is still read as this element's (the reader as it shipped)",s(`"[&_em]:font-serif ${e("accent")}"`)==='{"step":"accent","face":"font-serif"}'),t("…and two faces on one line are left to a reader",f(`"font-display font-mono ${e("label")}"`)===null)}const l=i.filter(e=>/\.tsx?$/.test(e.path)&&n.drawRoots.some(s=>e.path.startsWith(s)));t(`read the drawing roots ${n.drawRoots.join(", ")} (${l.length} TS/TSX files, at least ${n.minFiles})`,l.length>=n.minFiles,"fewer means the walk broke, and the sweep below is vacuous");let h=0;const g=[];for(const e of l)e.text.split(`
2
+ `).forEach((s,r)=>{const a=f(s);if(!a||(h+=1,a.step==="body"))return;const c=u[a.face];c!==a.step&&g.push(`${e.path}:${r+1} — size says ${a.step}, face says ${a.face} (${c})`)});if(t(`found >= ${n.pairFloor} lines naming one face and one size (found ${h})`,h>=n.pairFloor,"fewer means the reader broke"),t("§3 no text takes its face from one style row and its size from another",g.length===0,`these lines name one style for their size and another for their face, so the merchant cannot fix the text from one row:
3
+ `+g.join(`
4
+ `)),n.themeSheet){const e=d(n.themeSheet)??"";if(t(`read the site stylesheet ${n.themeSheet}`,e.length>0)){for(const a of["2","3"])t(`§1 --font-heading${a} exists and resolves --font-role-heading${a}`,new RegExp(`--font-heading${a}:\\s*var\\(--font-role-heading${a}\\)`).test(e),`without it a section cannot DRAW the face Site styles ▸ Heading ${a} sets, and that Font control moves zero pixels again`);const s=e.slice(e.indexOf("@theme inline"));for(const a of["2","3"]){const c=s.match(new RegExp(`--font-heading${a}:[^;]*;`))?.[0]??"";t(`§2 …and in @theme inline --font-heading${a} points at its OWN role, never back at Heading 1`,c.includes(`--font-role-heading${a}`),`\`--font-heading${a}\` resolves ${c.trim()||"nothing"}`)}t("§4 the Accent size is read, in CSS (`em.text-accent` sets var(--size-accent, inherit))",/em\.text-accent\s*\{[^}]*font-size:\s*var\(--size-accent,\s*inherit\)/.test(e.replace(/\/\*[\s\S]*?\*\//g,"")),"the `em.text-accent` rule is gone, so Site styles ▸ Accent ▸ Size is a dead control again"),t("§4 …and it falls back to `inherit`, never a number",!/--size-accent,\s*\d/.test(e));const r=e.slice(e.indexOf(":root"),e.indexOf("@theme inline"));for(const a of["2","3"])t(`§5 :root defaults --font-role-heading${a} to Heading 1's face, so the swap is invisible until a face is chosen`,new RegExp(`--font-role-heading${a}:\\s*var\\(--font-role-heading\\)`).test(r),"every site that has never opened that row would change typeface")}}for(const e of n.accentMarkup??[]){const s=d(e);t(`§4 ${e} does not set the accent size in the MARKUP`,s!==void 0&&!w(s).includes("--size-accent"),s===void 0?"it is not among the files handed in":"that moves 900 published strings for a size `em.text-accent` already delivers")}if(n.fontStore){const e=d(n.fontStore)??"",s=e.slice(e.indexOf("const pick ="),e.indexOf("// DERIVED from FONT_ROLES"));t(`§5 ${n.fontStore}'s normalizeFonts falls Heading 2 and Heading 3 back to Heading 1`,/role === "heading2" \|\| role === "heading3"/.test(s),"a site that chose Oswald for its headings would get two Space Grotesk ones")}return o()}export{S as styleFaceMatchesSize};
@@ -0,0 +1,5 @@
1
+ export declare const UTILITY_ROOTS: readonly string[];
2
+ export declare const rootsLongestFirst: () => string[];
3
+ export declare function asPreAccentRename(html: string): string;
4
+ export declare function asPreButtonToken(html: string): string;
5
+ export declare function asPreTokenRenames(html: string): string;
@@ -0,0 +1 @@
1
+ const o=["bg","text","border","border-t","border-r","border-b","border-l","border-x","border-y","outline","ring","ring-offset","fill","stroke","from","via","to","accent","caret","decoration","divide","shadow","placeholder"],n=()=>[...o].sort((t,r)=>r.length-t.length),e=new RegExp(String.raw`(?<![a-z0-9-])`+String.raw`((?:[a-z0-9@_.\-\[\]\/]+:)*(?:${n().join("|")}))`+String.raw`-accent(-2)?`+String.raw`((?:\/(?:\d{1,3}|\[[^\]\s]*\]))?)`+String.raw`(?![a-z0-9-])`,"g");function c(t){return t.split("--color-accent").join("--color-gold").split("--accent").join("--gold").replace(e,"$1-gold$2$3")}const i=new RegExp(String.raw`(?<![a-z0-9-])`+String.raw`((?:[a-z0-9@_.\-\[\]\/]+:)*(?:${n().join("|")}))`+String.raw`-btn(-2)?`+String.raw`((?:\/(?:\d{1,3}|\[[^\]\s]*\]))?)`+String.raw`(?![a-z0-9-])`,"g"),a=new RegExp(String.raw`(?<![a-z0-9-])((?:[a-z0-9@_.\-\[\]\/]+:)*)text-btn-ink(?![a-z0-9-])`,"g");function l(t){return t.split("--color-btn-ink").join("--color-accent-ink").split("--color-btn-2").join("--color-accent-2").split("--color-btn").join("--color-accent").split("--btn-ink").join("--accent-ink").split("--btn-2").join("--accent-2").split("--btn").join("--accent").replace(a,"$1text-bg").replace(i,"$1-accent$2$3")}function g(t){return c(l(t))}export{o as UTILITY_ROOTS,c as asPreAccentRename,l as asPreButtonToken,g as asPreTokenRenames,n as rootsLongestFirst};
@@ -0,0 +1,5 @@
1
+ export interface StyleRule {
2
+ selector: string;
3
+ declarations: Map<string, string>;
4
+ }
5
+ export declare function parseRules(css: string): StyleRule[];
@@ -0,0 +1 @@
1
+ import{stripCommentsAndStrings as f}from"./css-source.js";const l=new Map;function u(t){const o=l.get(t);if(o)return o;const p=f(t),e=[],a=/([^{}]+)\{([^{}]*)\}/g;let n;for(;n=a.exec(p);){const s=n[1].trim().replace(/\s+/g," ");if(!s||s.startsWith("@"))continue;const c=new Map;for(const r of n[2].split(";")){const i=r.indexOf(":");i<0||c.set(r.slice(0,i).trim(),r.slice(i+1).trim())}e.push({selector:s,declarations:c})}return l.set(t,e),e}export{u as parseRules};
@@ -0,0 +1,2 @@
1
+ export declare function resolveVars(expr: string, vars: Record<string, string>): string;
2
+ export declare function evalLength(expr: string, vw: number, vars?: Record<string, string>): number;
@@ -0,0 +1 @@
1
+ function d(r,u){let e=r;for(let o=0;o<32;o++){const i=e.lastIndexOf("var(");if(i===-1)return e;let l=0,a=-1;for(let c=i+3;c<e.length;c++)if(e[c]==="(")l++;else if(e[c]===")"&&(l--,l===0)){a=c;break}if(a===-1)throw new Error(`unbalanced var() in: ${r}`);const n=e.slice(i+4,a),t=n.indexOf(","),s=(t===-1?n:n.slice(0,t)).trim(),m=t===-1?"":n.slice(t+1).trim(),f=u[s]??m;if(!f)throw new Error(`var ${s} unset with no fallback in: ${r}`);e=e.slice(0,i)+f+e.slice(a+1)}throw new Error(`var() resolution did not converge: ${r}`)}function h(r,u,e={}){const o=d(r,e).replace(/([\d.]+)rem/g,(n,t)=>`(${Number(t)*16})`).replace(/([\d.]+)px/g,"($1)").replace(/([\d.]+)vw/g,`($1*${u}/100)`);if(!/^[\d\s+*/().,a-z-]+$/i.test(o))throw new Error(`unexpected chars: ${o}`);const a=new Function("calc","min","max","clamp",`return (${o});`)(n=>n,Math.min,Math.max,(n,t,s)=>Math.max(n,Math.min(t,s)));if(!Number.isFinite(a))throw new Error(`did not evaluate to a number: ${r}`);return Math.round(a*100)/100}export{h as evalLength,d as resolveVars};
@@ -0,0 +1,12 @@
1
+ export type Rule = {
2
+ media: string | null;
3
+ selector: string;
4
+ decls: string;
5
+ };
6
+ export declare function blockEnd(src: string, open: number): number;
7
+ export declare function readRules(src: string, media?: string | null, out?: Rule[], selector?: string | null): Rule[];
8
+ export declare function andMedia(outer: string | null, inner: string): string;
9
+ export declare function ownDeclarations(body: string): string;
10
+ export declare function mediaApplies(media: string | null, width: number): boolean;
11
+ export declare function declFor(rules: Rule[], selector: string, prop: string, width: number): string | null;
12
+ export declare function declForClasses(rules: Rule[], classes: readonly string[], prop: string, width: number): string | null;
@@ -0,0 +1 @@
1
+ function m(e,n){let t=0;for(let i=n;i<e.length;i+=1)if(e[i]==="{")t+=1;else if(e[i]==="}"&&(t-=1,t===0))return i;return e.length}function p(e,n=null,t=[],i=null){if(i!==null){const s=g(e);s.trim()&&t.push({media:n,selector:i,decls:s})}let r=0;for(;r<e.length;){const s=e.indexOf("{",r);if(s===-1)break;const o=m(e,s);let c=r;for(let u=s;u>r;u-=1)if(";}".includes(e[u-1])){c=u;break}const l=e.slice(c,s).trim(),f=e.slice(s+1,o);l.startsWith("@media")?p(f,h(n,l),t,i):l.startsWith("@")?p(f,n,t,i):l&&p(f,n,t,l),r=o+1}return t}function h(e,n){const t=n.replace(/^@media\s*/,"").trim();return e===null?`@media ${t}`:`${e} and ${t}`}function g(e){let n="",t=0;for(;t<e.length;){const i=e.indexOf("{",t);if(i===-1){n+=e.slice(t);break}let r=i;for(;r>t&&!";}".includes(e[r-1]);)r-=1;n+=e.slice(t,r),t=m(e,i)+1}return n}function d(e,n){if(!e)return!0;const i=e.replace(/^@media\s*/,"").trim().split(/\s+and\s+/).map(r=>r.trim());for(const r of i){const s=/^\(\s*max-width:\s*([\d.]+)(px|rem)\s*\)$/.exec(r),o=/^\(\s*min-width:\s*([\d.]+)(px|rem)\s*\)$/.exec(r),c=/^\(\s*width\s*(<=|>=|<|>)\s*([\d.]+)(px|rem)\s*\)$/.exec(r),l=/^\(\s*([\d.]+)(px|rem)\s*(<=|>=|<|>)\s*width\s*\)$/.exec(r);if(s){if(n>a(s[1],s[2]))return!1}else if(o){if(n<a(o[1],o[2]))return!1}else if(c){if(!x(n,c[1],a(c[2],c[3])))return!1}else if(l){const f={"<":">",">":"<","<=":">=",">=":"<="}[l[3]];if(!x(n,f,a(l[1],l[2])))return!1}else return!1}return!0}const $=16;function a(e,n){return n==="rem"?Number(e)*$:Number(e)}function x(e,n,t){return n==="<"?e<t:n==="<="?e<=t:n===">"?e>t:e>=t}function w(e,n,t,i){let r=null;for(const s of e){if(!d(s.media,i)||!s.selector.split(",").map(f=>f.trim()).includes(n))continue;const c=new RegExp(`(?:^|;)\\s*${t}\\s*:\\s*([^;]+)`,"g");let l;for(;(l=c.exec(s.decls))!==null;)r=l[1].trim()}return r}function O(e,n,t,i){const r=new Set(n.map(o=>`.${o}`));let s=null;for(const o of e){if(!d(o.media,i)||!o.selector.split(/(?<!\\),/).map(u=>u.trim().replace(/\\/g,"")).some(u=>r.has(u)))continue;const l=new RegExp(`(?:^|;)\\s*${t}\\s*:\\s*([^;]+)`,"g");let f;for(;(f=l.exec(o.decls))!==null;)s=f[1].trim()}return s}export{h as andMedia,m as blockEnd,w as declFor,O as declForClasses,d as mediaApplies,g as ownDeclarations,p as readRules};
@@ -0,0 +1,2 @@
1
+ export declare function stripCommentsAndStrings(css: string): string;
2
+ export declare function stripComments(css: string): string;
@@ -0,0 +1,3 @@
1
+ function l(t,u){let e="",n=0;for(;n<t.length;){const i=t[n];if(i==="/"&&t[n+1]==="*"){const o=t.indexOf("*/",n+2),r=o<0?t.length:o+2;e+=t.slice(n,r).replace(/[^\n]/g," "),n=r;continue}if((i==='"'||i==="'")&&!u){const o=i;for(e+=" ",n++;n<t.length&&t[n]!==o;)e+=t[n]===`
2
+ `?`
3
+ `:" ",t[n]==="\\"&&(n++,n<t.length&&(e+=" ")),n++;e+=" ",n++;continue}e+=i,n++}return e}function f(t){return l(t,!1)}function h(t){return l(t,!0)}export{h as stripComments,f as stripCommentsAndStrings};
@@ -0,0 +1,14 @@
1
+ import type { SourceFile } from "../rule.js";
2
+ export declare const BUILDERS: string[];
3
+ export type FieldLabel = {
4
+ line: number;
5
+ label: string;
6
+ item: boolean;
7
+ builder?: string;
8
+ };
9
+ export declare function readLabels(src: string): FieldLabel[];
10
+ export declare function readRowLabels(src: string): FieldLabel[];
11
+ export declare const JSX_LABEL: RegExp;
12
+ export declare function readJsxLabels(src: string): FieldLabel[];
13
+ export declare function isPanelFile(file: SourceFile): boolean;
14
+ export declare function readPanelLabels(src: string): FieldLabel[];
@@ -0,0 +1,5 @@
1
+ const m=["text","textarea","plainText","copyText","plainTextarea","linkField","textList","upload","toggle","checkbox","select","searchSelect","segmented","slider","colorInput","focalPicker","sizeField","backgroundField","multiSelect"];function A(e){const t=e.split(`
2
+ `),n=new RegExp(`(?<![A-Za-z0-9_.$])(${m.join("|")})\\("((?:[^"\\\\]|\\\\.)*)"`,"g"),l=/label:\s*"((?:[^"\\]|\\.)*)"/g,i=new RegExp(`(?<![A-Za-z0-9_.$])(${m.join("|")})\\(\\s*$`),o=/^\s*"((?:[^"\\]|\\.)*)"\s*,/,r=[];let c=0,h=0;const f=[];return t.forEach((a,u)=>{const d=u+1,b=h>0;for(const s of a.matchAll(n))r.push({line:d,label:s[2],item:b,builder:s[1]});const g=i.exec(a);if(g){let s=u+1;for(;s<t.length&&t[s].trim()==="";)s+=1;const p=s<t.length?o.exec(t[s]):null;p&&r.push({line:s+1,label:p[1],item:b,builder:g[1]})}for(const s of a.matchAll(l)){const p=a.slice(0,s.index??0),x=u>0?t[u-1].trim():"";(/type:\s*"/.test(p)||/type:\s*"[a-z]+"\s*,?$/.test(x))&&r.push({line:d,label:s[1],item:b})}for(let s=0;s<a.length;s+=1)a[s]==="{"?(/arrayFields:\s*$/.test(a.slice(0,s))&&(f.push(c),h+=1),c+=1):a[s]==="}"&&(c-=1,f.length&&c===f[f.length-1]&&(f.pop(),h-=1))}),r}function L(e,t){let n=0,l=-1;for(let o=t-1;o>=0;o-=1){const r=e[o];if(r==="}")n+=1;else if(r==="{"){if(n===0){l=o;break}n-=1}}if(l<0)return"";let i=0;for(let o=l;o<e.length;o+=1)if(e[o]==="{")i+=1;else if(e[o]==="}"&&(i-=1,i===0))return e.slice(l,o+1);return e.slice(l)}function $(e){let t=0,n="";for(const l of e){if(l==="{"||l==="["){t+=1;continue}if(l==="}"||l==="]"){t-=1;continue}t===1&&(n+=l)}return n}function w(e,t){const n=$(L(e,t));return/(^|[,;])\s*(value|fields|render|resolveFields|panelId|href|amount|meta)\s*:/.test(n)}function y(e){const t=e.split(`
3
+ `),n=[];for(let o=0,r=0;o<t.length;o+=1)n.push(r),r+=t[o].length+1;const l=/label:\s*"((?:[^"\\]|\\.)*)"/g,i=[];return t.forEach((o,r)=>{for(const c of o.matchAll(l))w(e,n[r]+(c.index??0))||i.push({line:r+1,label:c[1],item:!1})}),i}const F=/(?<![-A-Za-z])label="((?:[^"\\]|\\.)*)"/g;function E(e){const t=[];return e.split(`
4
+ `).forEach((n,l)=>{for(const i of n.matchAll(F))t.push({line:l+1,label:i[1],item:!1})}),t}const j=/className="sf-field"|from\s*["']efficient-easel/;function S(e){return e.path.startsWith("components/")&&e.path.endsWith(".tsx")&&j.test(e.text)}const k=6;function O(e,t){for(let n=t;n<Math.min(e.length,t+k);n+=1){if(/^\s*\}[,)]?\s*$/.test(e[n])&&n>t)return!1;if(/\bonClick:/.test(e[n]))return!0}return!1}function N(e){const t=new Set,n=e.split(`
5
+ `);return[...A(e),...y(e),...E(e)].filter(l=>{const i=`${l.line}:${l.label}`;return t.has(i)?!1:(t.add(i),!O(n,l.line-1))})}export{m as BUILDERS,F as JSX_LABEL,S as isPanelFile,E as readJsxLabels,A as readLabels,N as readPanelLabels,y as readRowLabels};
@@ -0,0 +1,72 @@
1
+ import type * as TS from "typescript";
2
+ import type { SourceFile } from "../rule.js";
3
+ import type { Rule } from "./css-rules.js";
4
+ export declare function importsOf(ts: typeof TS, file: SourceFile): string[];
5
+ export type PanelSelect = {
6
+ file: string;
7
+ line: number;
8
+ kind: string;
9
+ engineDrawn: boolean;
10
+ classes: string[];
11
+ };
12
+ export type SelectHelper = {
13
+ readonly file: string;
14
+ readonly modules?: readonly string[];
15
+ };
16
+ export declare function census(ts: typeof TS, files: readonly SourceFile[], helper: SelectHelper): {
17
+ population: PanelSelect[];
18
+ helperDeclared: boolean;
19
+ };
20
+ export type Shape = {
21
+ name: string;
22
+ own: string[];
23
+ ancestors: string[];
24
+ rows: number;
25
+ };
26
+ export type Spec = [number, number, number];
27
+ export declare const specCmp: (a: Spec, b: Spec) => number;
28
+ export declare const specStr: (s: Spec) => string;
29
+ export type State = "rest" | "hover";
30
+ export declare function splitDecls(body: string): {
31
+ prop: string;
32
+ value: string;
33
+ }[];
34
+ export declare function splitCombinators(sel: string): string[];
35
+ export type Compound = {
36
+ type: string | null;
37
+ classes: string[];
38
+ needsState: string[];
39
+ };
40
+ export declare function compound(src: string): Compound | null;
41
+ export declare const specOf: (parts: Compound[]) => Spec;
42
+ export declare function mediaHolds(cond: string | null): boolean | null;
43
+ export declare const mediaLabel: (cond: string | null) => string;
44
+ export type Applied = {
45
+ spec: Spec;
46
+ order: number;
47
+ decls: string;
48
+ selector: string;
49
+ sheet: string;
50
+ };
51
+ export type Undecided = {
52
+ sheet: string;
53
+ selector: string;
54
+ media: string;
55
+ decls: string;
56
+ };
57
+ export declare function applicable(rules: Rule[], shape: Shape, sheet: string, base: number, state?: State): {
58
+ applied: Applied[];
59
+ unreadable: string[];
60
+ undecided: Undecided[];
61
+ };
62
+ export declare const INITIAL: Record<string, string>;
63
+ export declare function colorIn(value: string): string | null;
64
+ export declare function readVars(css: string): Map<string, string>;
65
+ export declare function derefVars(value: string, vars: Map<string, string>, depth?: number): string;
66
+ export declare function resolve(applied: Applied[], prop: string, vars?: Map<string, string>): {
67
+ value: string;
68
+ from: string;
69
+ };
70
+ export declare const ANCESTORS: string[];
71
+ export declare function deriveShapes(population: PanelSelect[], panelCss: string): Shape[];
72
+ export declare function chainInMarkup(ts: typeof TS, files: readonly SourceFile[], chain: string[]): Map<string, string[]>;
@@ -0,0 +1,2 @@
1
+ function N(t,i){const r=t.createSourceFile(i.path,i.text,t.ScriptTarget.Latest,!0),s=[],n=o=>{(t.isImportDeclaration(o)||t.isExportDeclaration(o))&&o.moduleSpecifier&&t.isStringLiteral(o.moduleSpecifier)&&s.push(o.moduleSpecifier.text),t.isCallExpression(o)&&(o.expression.kind===t.SyntaxKind.ImportKeyword||t.isIdentifier(o.expression)&&o.expression.text==="require")&&o.arguments.length>0&&t.isStringLiteral(o.arguments[0])&&s.push(o.arguments[0].text),o.forEachChild(n)};return n(r),s}const k=t=>/\.(tsx?|mts)$/.test(t)&&!t.endsWith(".d.ts");function L(t,i){const r=new Map,s=new Set(["/"]);for(const e of i){if(!k(e.path))continue;r.set(`/${e.path}`,e.text);const c=e.path.split("/");for(let u=1;u<c.length;u++)s.add(`/${c.slice(0,u).join("/")}`)}const n={noLib:!0,types:[],noEmit:!0,jsx:t.JsxEmit.Preserve,target:t.ScriptTarget.ESNext,module:t.ModuleKind.ESNext,moduleResolution:t.ModuleResolutionKind.Bundler,baseUrl:"/",paths:{"@/*":["./*"]}},o={fileExists:e=>r.has(e),readFile:e=>r.get(e),directoryExists:e=>s.has(e.replace(/\/+$/,"")||"/"),getDirectories:()=>[],realpath:e=>e,getSourceFile:(e,c)=>{const u=r.get(e);return u===void 0?void 0:t.createSourceFile(e,u,c,!0)},getDefaultLibFileName:()=>"/lib.d.ts",writeFile:()=>{},getCurrentDirectory:()=>"/",getCanonicalFileName:e=>e,useCaseSensitiveFileNames:()=>!0,getNewLine:()=>`
2
+ `};return t.createProgram({rootNames:[...r.keys()],options:n,host:o})}function M(t,i,r){const s=L(t,i),n=s.getTypeChecker();let o;s.getSourceFile(`/${r.file}`)?.forEachChild(a=>{t.isFunctionDeclaration(a)&&a.name?.text==="select"&&(o=a)});const e=new Set(r.modules??[]),c=a=>{if(!t.isImportSpecifier(a))return!1;const m=a.parent.parent.parent;return(a.propertyName??a.name).text==="select"&&t.isStringLiteral(m.moduleSpecifier)&&e.has(m.moduleSpecifier.text)},u=[];for(const a of s.getSourceFiles()){if(a.isDeclarationFile)continue;const m=a.fileName.slice(1),g=l=>a.getLineAndCharacterOfPosition(l.getStart()).line+1;let S=!1;const d=l=>{t.isStringLiteral(l)&&/(^|\s)sf-[a-z0-9-]/.test(l.text)&&(S=!0),t.isImportDeclaration(l)&&t.isStringLiteral(l.moduleSpecifier)&&/^(editor-shell|efficient-easel)(\/|$)/.test(l.moduleSpecifier.text)&&(S=!0),l.forEachChild(d)};d(a);const h=l=>{if(t.isPropertyAssignment(l)&&t.isIdentifier(l.name)&&l.name.text==="type"&&t.isStringLiteral(l.initializer)&&l.initializer.text==="select"&&u.push({file:m,line:g(l),kind:'field: type "select"',engineDrawn:!0,classes:[]}),t.isCallExpression(l)&&t.isIdentifier(l.expression)){const p=n.getSymbolAtLocation(l.expression),x=p&&p.flags&t.SymbolFlags.Alias?n.getAliasedSymbol(p):p;[...p?.declarations??[],...x?.declarations??[]].some(f=>o!==void 0&&f===o||c(f))&&u.push({file:m,line:g(l),kind:"field: select() helper",engineDrawn:!0,classes:[]})}if(t.isJsxOpeningLikeElement(l)&&l.tagName.getText()==="select"&&S&&m!==r.file){const p=l.attributes.properties.find(b=>t.isJsxAttribute(b)&&t.isIdentifier(b.name)&&b.name.text==="className"),x=p&&p.initializer&&t.isStringLiteral(p.initializer)?p.initializer.text:"";u.push({file:m,line:g(l),kind:"element: <select> in panel markup",engineDrawn:!1,classes:x.split(/\s+/).filter(Boolean)})}l.forEachChild(h)};h(a)}return{population:u,helperDeclared:o!==void 0}}const v=(t,i)=>t[0]-i[0]||t[1]-i[1]||t[2]-i[2],O=t=>`(${t[0]},${t[1]},${t[2]})`;function $(t){const i=[];let r=0,s=null,n="";const o=()=>{const e=n.indexOf(":");e>0&&i.push({prop:n.slice(0,e).trim(),value:n.slice(e+1).trim()}),n=""};for(const e of t){if(s){n+=e,e===s&&(s=null);continue}if(e==='"'||e==="'")s=e;else if(e==="(")r+=1;else if(e===")")r=Math.max(0,r-1);else if(e===";"&&r===0){o();continue}n+=e}return o(),i}function A(t){const i=[];let r=0,s="";for(const n of t){if(n==="("&&(r+=1),n===")"&&(r-=1),r===0&&/[\s>+~]/.test(n)){s.trim()&&i.push(s.trim()),s="";continue}s+=n}return s.trim()&&i.push(s.trim()),i}function C(t){let i=t.trim();const r=[],s=[];let n=null;const o=/^[a-zA-Z][a-zA-Z0-9-]*/.exec(i);for(o&&(n=o[0],i=i.slice(o[0].length));i.length;){let e;if(e=/^\.([A-Za-z0-9_-]+)/.exec(i))r.push(e[1]);else if(e=/^:(focus-visible|focus-within|hover|focus|active|checked|disabled)\b/.exec(i))s.push(e[1]);else if(e=/^:dir\([^)]*\)/.exec(i))s.push("dir");else if(e=/^:not\(\[([A-Za-z-]+)\]\)/.exec(i))r.push(`@not-${e[1]}`);else if(e=/^\[([A-Za-z-]+)(?:[~^$*|]?=[^\]]*)?\]/.exec(i))s.push(`[${e[1]}]`);else if(e=/^:has\(([^)]*)\)/.exec(i)){if(!/^\s*>?\s*[a-z]+\s*$/.test(e[1]))return null;r.push("@has-type")}else return null;i=i.slice(e[0].length)}return{type:n,classes:r,needsState:s}}const D=t=>t.reduce((i,r)=>[i[0],i[1]+r.classes.filter(s=>s!=="@has-type").length+r.needsState.length,i[2]+(r.type?1:0)+(r.classes.includes("@has-type")?1:0)],[0,0,0]);function F(t){if(t===null)return!0;let i=!0;for(const r of t.replace(/^@media\s*/i,"").split(/\s+and\s+/i)){const s=r.trim().replace(/^\(|\)$/g,"").trim(),n=/^([a-z-]+)\s*:\s*(.+)$/.exec(s);if(!n)return null;const o=n[1],e=n[2].trim();if(o==="hover"||o==="any-hover"){if(e==="hover")continue;return e==="none"?!1:null}if(o==="pointer"||o==="any-pointer"){if(e==="fine")continue;return e==="coarse"||e==="none"?!1:null}i=null}return i}const T=t=>t??"(no media query)";function Z(t,i,r,s,n="rest"){const o=[],e=[],c=[],u=n==="hover"?new Set(["hover"]):new Set;return t.forEach((a,m)=>{const g=n==="rest"?a.media===null:F(a.media);for(const S of a.selector.split(",")){const d=S.trim();if(!d||!/(^|[\s,>+~])select\b/.test(d)&&!i.own.some(f=>d.includes(`.${f}`)))continue;const h=A(d).map(C),l=h[h.length-1];if(h.some(f=>f===null)||!l){e.push(`${r}: ${d}`);continue}const p=h;if(l.type&&l.type!=="select")continue;const x=l.classes.filter(f=>!f.startsWith("@"));if(!(!x.every(f=>i.own.includes(f))||!l.type&&x.length===0||!l.needsState.every(f=>u.has(f))||!p.slice(0,-1).every(f=>(!f.type||f.type==="div")&&f.classes.filter(y=>!y.startsWith("@")).every(y=>i.ancestors.includes(y))&&f.needsState.every(y=>u.has(y))))&&g!==!1){if(g===null){c.push({sheet:r,selector:d,media:a.media??"",decls:a.decls});continue}o.push({spec:D(p),order:s+m,decls:a.decls,selector:d,sheet:r})}}}),{applied:o,unreadable:e,undecided:c}}const w={appearance:"auto","background-image":"none","background-size":"auto","background-repeat":"repeat","background-position":"0% 0%","background-color":"transparent","border-color":"currentcolor"};function z(t){const i=t.replace(/url\([\s\S]*?\)/g," "),r=/var\(--[A-Za-z0-9-]+(?:\s*,[^()]*)?\)|#[0-9a-fA-F]{3,8}\b|(?:rgba?|hsla?|color-mix|color)\([^()]*(?:\([^()]*\)[^()]*)*\)|\b(?:transparent|currentcolor|white|black)\b/i.exec(i);return r?r[0]:null}function W(t){const i=new Map;for(const r of $(t.replace(/[^{}]*\{|\}/g,";")))r.prop.startsWith("--")&&i.set(r.prop,r.value);return i}function E(t,i,r=0){if(r>8)return t;let s=!1;const n=t.replace(/var\(\s*(--[A-Za-z0-9-]+)\s*(?:,([^()]*(?:\([^()]*\)[^()]*)*))?\)/g,(o,e)=>{const c=i.get(e);return c===void 0?o:(s=!0,c)});return s?E(n,i,r+1):n}function j(t,i,r=new Map){let s=null;const n=(e,c)=>{(!s||v(e.spec,s.spec)>0||v(e.spec,s.spec)===0&&e.order>=s.order)&&(s={value:c.trim(),from:`${e.sheet} { ${e.selector} }`,spec:e.spec,order:e.order})};for(const e of t)for(const c of $(e.decls)){if(c.prop===i||i==="appearance"&&c.prop==="-webkit-appearance"){n(e,c.value);continue}if(c.prop==="border"&&i==="border-color"){n(e,z(c.value)??w[i]);continue}if(c.prop!=="background"||!i.startsWith("background-"))continue;const u=c.value;n(e,i==="background-image"?/url\([\s\S]*?\)|linear-gradient\([\s\S]*?\)|image-set\([\s\S]*?\)/.exec(u)?.[0]??"none":i==="background-repeat"?/\b(no-repeat|repeat-x|repeat-y|round|space|repeat)\b/.exec(u)?.[0]??"repeat":i==="background-color"?z(u)??w[i]:w[i])}const o=s??{value:w[i],from:"(nothing declares one)"};return{value:E(o.value,r),from:o.from}}const I=["sf-editor","sf-body","sf-panel","sf-insp-body","sf-styles","sf-field","es-field","sf-copybox"];function K(t,i){const r=t.filter(e=>!e.engineDrawn),s=e=>new RegExp(`\\.${e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\b`).test(i),n=e=>[...e.classes].filter(s).sort().join(" "),o=new Set([""]);for(const e of r)o.add(n(e));return[...o].sort().map(e=>({name:e?`house .${e.split(" ").join(".")}`:"house (bare)",own:e?e.split(" "):[],ancestors:[...I],rows:r.filter(c=>n(c)===e).length}))}function P(t,i,r){const s=new Map;for(const n of r)s.set(n,[]);for(const n of i){if(!k(n.path))continue;const o=t.createSourceFile(n.path,n.text,t.ScriptTarget.Latest,!0),e=c=>{if(t.isJsxAttribute(c)&&t.isIdentifier(c.name)&&c.name.text==="className"&&c.initializer&&t.isStringLiteral(c.initializer)){const u=c.initializer.text.split(/\s+/).filter(Boolean);for(const a of r)u.includes(a)&&s.get(a)?.push(n.path)}c.forEachChild(e)};e(o)}return s}export{I as ANCESTORS,w as INITIAL,Z as applicable,M as census,P as chainInMarkup,z as colorIn,C as compound,E as derefVars,K as deriveShapes,N as importsOf,F as mediaHolds,T as mediaLabel,W as readVars,j as resolve,v as specCmp,D as specOf,O as specStr,A as splitCombinators,$ as splitDecls};
@@ -0,0 +1,9 @@
1
+ export declare const PART_PANEL = "components/PartSettingsPanel.tsx";
2
+ export interface SliderCallSite {
3
+ fields: string[];
4
+ line: number;
5
+ readoutOff: boolean;
6
+ labelFor(field: string): string;
7
+ }
8
+ export declare function readSliderCallSites(source: string): SliderCallSite[];
9
+ export declare const sliderFields: (sites: readonly SliderCallSite[]) => string[];
@@ -0,0 +1,17 @@
1
+ const a="components/PartSettingsPanel.tsx";function w(t){let n="",e=0,o=null;for(;e<t.length;){const s=t[e],i=t[e+1];if(o){if(n+=s,s==="\\"){n+=i??"",e+=2;continue}s===o&&(o=null),e+=1;continue}if(s==='"'||s==="'"||s==="`"){o=s,n+=s,e+=1;continue}if(s==="/"&&i==="/"){for(;e<t.length&&t[e]!==`
2
+ `;)e+=1;continue}if(s==="/"&&i==="*"){for(e+=2;e<t.length&&!(t[e]==="*"&&t[e+1]==="/");)t[e]===`
3
+ `&&(n+=`
4
+ `),e+=1;e+=2;continue}n+=s,e+=1}return n}function l(t){throw new Error(t)}function S(t){const n=/const PART_FIELDS\s*=\s*\[([\s\S]*?)\]\s*as const/.exec(t);n||l(`${a}: no \`const PART_FIELDS = [ … ] as const\` — this reader takes the panel's row labels from that one table, and cannot find it. Has it been renamed?`);const e=new Map;for(const o of n[1].matchAll(/\{\s*key:\s*"([^"]+)"\s*,\s*label:\s*"([^"]+)"/g))e.set(o[1],o[2]);return e.size===0&&l(`${a}: PART_FIELDS holds no \`key\`/\`label\` pairs.`),e}function m(t){const n=w(t),e=S(n),o=[];for(const s of n.matchAll(/<Slider[\s/>]/g)){const i=s.index??0,d=n.indexOf("/>",i);d===-1&&l(`${a}: a <Slider at offset ${i} is not self-closing. This reader reads a call site's props up to \`/>\`; a Slider with children needs a different reader.`);const u=n.slice(i,d),c=n.slice(0,i),h=/((?:\s*case\s+"[^"]+"\s*:)+)\s*return\s*\(\s*$/.exec(c),f=h?[...h[1].matchAll(/case\s+"([^"]+)"\s*:/g)].map(r=>r[1]):[];f.length===0&&l(`${a}: the <Slider at line ${c.split(`
5
+ `).length} is not returned DIRECTLY by a
6
+ \`case "…": return (\` branch, so this reader cannot say which field it draws.
7
+
8
+ IF SOMETHING NOW WRAPS THE SLIDER, THAT ROW IS BACK TO 129px AND NOTHING ELSE WOULD SAY SO.
9
+ The rule that gives it the row is \`.es-field-ctl > .sf-slider\` — a DIRECT child, because the
10
+ slot is the flex container and only its own children are flex items. A wrapper becomes the
11
+ item, sizes to the track it holds, and the track fills the wrapper instead of the row. A
12
+ descendant selector would NOT save it: it would paint \`flex: 1\` on an element whose parent
13
+ is the wrapper, not the slot, and the row would look fixed while measuring the same 129px.
14
+
15
+ So a wrap needs a decision, not a wider selector — either unwrap it, or give the wrapper the
16
+ row and say why in \`components/storefront-editor.css\`. Card 66247.`),o.push({fields:f,line:c.split(`
17
+ `).length,readoutOff:/readout=\{false\}/.test(u),labelFor(r){const p=e.get(r);return p||l(`${a}: field \`${r}\` draws a slider but is not in PART_FIELDS, so it has no row label. The two declarations have drifted apart.`),p}})}return o}const b=t=>t.flatMap(n=>n.fields);export{a as PART_PANEL,m as readSliderCallSites,b as sliderFields};
@@ -0,0 +1,14 @@
1
+ import type * as TS from "typescript";
2
+ import type { SourceFile } from "../rule.js";
3
+ export declare const CONFIG_FILE = "lib/puck/config.tsx";
4
+ export declare const REGISTRY_DIRS: readonly ["lib/puck/blocks", "lib/puck/passes"];
5
+ export declare const REGISTRY_MODULES: readonly ["lib/puck/page-root.tsx", "lib/puck/page-root-fields.tsx", "lib/puck/registry.ts"];
6
+ export declare function isRegistryPath(p: string): boolean;
7
+ export declare function registryFiles(files: readonly SourceFile[]): SourceFile[];
8
+ export declare function registryText(files: readonly SourceFile[]): string;
9
+ export declare function splitBlockTypes(files: readonly SourceFile[]): string[];
10
+ export declare function blockFiles(files: readonly SourceFile[], type: string): string[];
11
+ export declare function registeredBlockKeys(ts: typeof TS, files: readonly SourceFile[]): string[];
12
+ export declare function blocksWrittenInConfig(ts: typeof TS, files: readonly SourceFile[]): string[];
13
+ export declare function registeredAs(ts: typeof TS, files: readonly SourceFile[], key: string): string | undefined;
14
+ export declare function blockSource(ts: typeof TS, files: readonly SourceFile[], type: string): string;
@@ -0,0 +1,3 @@
1
+ const o="lib/puck/config.tsx",x=["lib/puck/blocks","lib/puck/passes"],g=["lib/puck/page-root.tsx","lib/puck/page-root-fields.tsx","lib/puck/registry.ts"],d=t=>/\.tsx?$/.test(t)&&!t.endsWith(".d.ts");function m(t){return t===o||g.includes(t)?!0:d(t)&&x.some(e=>t.startsWith(`${e}/`))}function k(t){const e=new Map(t.map(n=>[n.path,n])),r=[o,...g].map(n=>e.get(n)).filter(n=>!!n),i=x.flatMap(n=>t.filter(s=>d(s.path)&&s.path.startsWith(`${n}/`)).sort((s,c)=>s.path<c.path?-1:1));return[...r,...i]}function y(t){return k(t).map(e=>e.text).join(`
2
+ `)}function h(t,e){const r=t.find(i=>new RegExp(`^lib/puck/blocks/[^/]+/${e}/render\\.tsx$`).test(i.path));return r?r.path.slice(0,-11):null}function E(t){return t.map(e=>/^lib\/puck\/blocks\/[^/]+\/([^/]+)\/render\.tsx$/.exec(e.path)?.[1]).filter(e=>!!e).sort()}function b(t,e){const r=h(t,e);return r?[`${r}/render.tsx`,`${r}/fields.tsx`].filter(i=>t.some(n=>n.path===i)):t.some(i=>i.path===o)?[o]:[]}function p(t,e){const r=e.find(l=>l.path===o);if(!r)return null;const i=r.text,n=t.createSourceFile(o,i,t.ScriptTarget.Latest,!0,t.ScriptKind.TSX),s={keys:[],literals:new Map,registeredAs:new Map};let c=!1;const f=l=>{if(!c){if(t.isPropertyAssignment(l)&&l.name.getText(n)==="components"&&t.isObjectLiteralExpression(l.initializer)){c=!0;for(const a of l.initializer.properties){if(!t.isPropertyAssignment(a))continue;const u=a.name.getText(n);s.keys.push(u),t.isObjectLiteralExpression(a.initializer)?s.literals.set(u,i.slice(a.getFullStart(),a.end)):s.registeredAs.set(u,a.initializer.getText(n))}return}t.forEachChild(l,f)}};if(f(n),!c)throw new Error(`registry-source: no \`components\` object in ${o}`);return s}function S(t,e){return[...p(t,e)?.keys??[]]}function $(t,e){return[...p(t,e)?.literals.keys()??[]]}function T(t,e,r){return p(t,e)?.registeredAs.get(r)}function w(t,e,r){if(h(e,r))return b(e,r).map(s=>e.find(c=>c.path===s).text).join(`
3
+ `);const n=p(t,e)?.literals.get(r);if(n===void 0)throw new Error(`registry-source: no block "${r}" among the files handed in — no folder under lib/puck/blocks/ and no literal in ${o}'s \`components\``);return n}export{o as CONFIG_FILE,x as REGISTRY_DIRS,g as REGISTRY_MODULES,b as blockFiles,w as blockSource,$ as blocksWrittenInConfig,m as isRegistryPath,T as registeredAs,S as registeredBlockKeys,k as registryFiles,y as registryText,E as splitBlockTypes};
@@ -0,0 +1,2 @@
1
+ export declare function stripComments(source: string): string;
2
+ export declare function balancedBlockAfter(source: string, marker: string): string;
@@ -0,0 +1,3 @@
1
+ const f={single:"'",double:'"',template:"`"};function s(c){let t="",n="code",o=0;for(;o<c.length;){const e=c[o],i=c[o+1];if(n==="code"){if(e==="/"&&i==="/"){n="line",t+=" ",o+=2;continue}if(e==="/"&&i==="*"){n="block",t+=" ",o+=2;continue}e==="'"?n="single":e==='"'?n="double":e==="`"&&(n="template"),t+=e,o+=1;continue}if(n==="line"){e===`
2
+ `?(n="code",t+=e):t+=" ",o+=1;continue}if(n==="block"){if(e==="*"&&i==="/"){n="code",t+=" ",o+=2;continue}t+=e===`
3
+ `?e:" ",o+=1;continue}if(e==="\\"){t+=e+(i??""),o+=2;continue}e===f[n]&&(n="code"),t+=e,o+=1}return t}function r(c,t){const n=s(c),o=n.indexOf(t);if(o<0)throw new Error(`ts-source: marker not found: ${t}`);const e=n.indexOf("{",o+t.length-1);if(e<0)throw new Error(`ts-source: no block opens after: ${t}`);let i=0;for(let l=e;l<n.length;l+=1)if(n[l]==="{")i+=1;else if(n[l]==="}"&&(i-=1,i===0))return c.slice(e,l+1);throw new Error(`ts-source: unbalanced block after: ${t}`)}export{r as balancedBlockAfter,s as stripComments};
@@ -0,0 +1,10 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type ThemeModeControlsGoneOptions = {
3
+ readonly mustRead?: readonly string[];
4
+ readonly liveWiring?: {
5
+ readonly layout: string;
6
+ readonly chrome: string;
7
+ };
8
+ readonly minFiles: number;
9
+ };
10
+ export declare function themeModeControlsGone(files: readonly SourceFile[], options: ThemeModeControlsGoneOptions): RuleReport;
@@ -0,0 +1,5 @@
1
+ import{ruleChecks as $}from"./rule.js";const n=["Default","mode"].join(" "),r=["Show Light","Dark switch"].join(" / "),p=[["Theme","Switch","Field"].join(""),["Theme","Mode","Root","Field"].join("")],w=`components/${p[0]}.tsx`,m=c=>c.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,"");function S(c,h){const{check:t,report:u}=$("theme mode controls gone"),i=c.filter(e=>/\.tsx?$/.test(e.path)),l=e=>i.find(o=>o.path===e)?.text;{const e=m(`export const Row = () => <label>${n}</label>; // ${r}`);t(`the matcher sees "${n}" in code that plainly says it`,e.includes(n));const o=m(`// card 66312 removed the ${n} row
2
+ /* and the ${r} one */
3
+ export const x = 1;`);t("…and a note that names a row, on its own line or in a block, is prose, not code",!o.includes(n)&&!o.includes(r))}t(`§1a the component that drew both rows is gone (${w})`,!i.some(e=>e.path===w),`${w} still exists — it drew both rows and has no other job`),t(`§1b the sweep is real (${i.length} TS/TSX files, at least ${h.minFiles})`,i.length>=h.minFiles,"too few to be this repository, so the absence checks below are passing over nothing");for(const e of h.mustRead??[])t(`§1b the sweep reaches ${e}, a file the rows lived in`,l(e)!==void 0);const a=[],d=[];for(const e of i){const o=m(e.text);for(const s of[n,r])o.includes(s)&&a.push(`${e.path} still names the "${s}" row`);for(const s of p)o.includes(s)&&d.push(`${e.path} still references ${s}`)}if(t("§1b no file names either row",a.length===0,a.join(`
4
+ `)),t("§1c no file wires the retired field or its root",d.length===0,d.join(`
5
+ `)),h.liveWiring){const{layout:e,chrome:o}=h.liveWiring,s=l(e)??"";t(`§3d ${e} builds the boot script from the site's STORED defaultMode / showThemeSwitch`,/buildThemeBootScript\(\{\s*mode:\s*theme\.defaultMode,\s*switchShown:\s*theme\.showThemeSwitch,?\s*\}\)/.test(s),"every site would paint whatever replaced them");for(const f of["loaded.defaultMode","loaded.showThemeSwitch"])t(`§3d …and still reads ${f} from the backend theme row`,s.includes(f));t("§3d …and threads showThemeSwitch to the chrome",/showThemeSwitch=\{theme\.showThemeSwitch\}/.test(s)),t(`§3d ${o} threads the site's switch setting to the Header`,/showThemeToggle=\{showThemeSwitch\}/.test(l(o)??""))}return u()}export{S as themeModeControlsGone};
@@ -0,0 +1,11 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type UploadIntakeGuardOptions = {
3
+ readonly allowlist?: Readonly<Record<string, {
4
+ readonly max: number;
5
+ readonly why: string;
6
+ }>>;
7
+ readonly migrated?: readonly string[];
8
+ readonly minFiles: number;
9
+ };
10
+ export declare function intakeMarkerCount(text: string): number;
11
+ export declare function uploadIntakeGuard(files: readonly SourceFile[], options: UploadIntakeGuardOptions): RuleReport;
@@ -0,0 +1,6 @@
1
+ import{ruleChecks as w}from"./rule.js";const j=/\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/,b=[/type=["']file["']/g,/dataTransfer\??\.files/g,/clipboardData\??\.(files|items)/g],c=/from\s+["']efficient-shell["']/;function p(r){return b.reduce((s,n)=>s+(r.match(n)?.length??0),0)}function I(r,s){const{check:n,report:$}=w("upload intake guard"),f=s.allowlist??{};{const e=o=>["type=",o,"file",o].join(""),t=["dataTransfer","files"],h=["clipboardData","items"],k=["clipboardData","files"],y=[["a double-quoted file input",`<input ${e('"')} accept="image/*" />`],["a single-quoted file input",`<input ${e("'")} />`],["a drop zone reading files",`openPicker(e.${t.join(".")}?.[0] ?? null)`],["…through optional chaining",`const f = e.${t.join("?.")};`],["a paste handler reading items",`Array.from(e.${h.join(".")})`],["a paste handler reading files",`e.${k.join("?.")}`]];for(const[o,l]of y)n(`the scan flags ${o}`,p(l)===1,l);const x=[["a text input",'<input type="text" />'],["a drop zone that only reads the types","e.dataTransfer.types.includes('Files')"],["a paste handler that only reads text","e.clipboardData.getData('text/plain')"],["the shell's own input spread","<input {...intake.inputProps} hidden />"]];for(const[o,l]of x)n(`the scan stays quiet on ${o}`,p(l)===0,l);const g=["efficient","shell"].join("-");n("the root-entry reader finds an import of the shell's root",c.test(`import { x } from "${g}";`)),n("…and not one of a subpath",!c.test(`import { useFileIntake } from "${g}/file-intake";`))}const a=r.filter(e=>j.test(e.path));n(`read the source (${a.length} JS/TS files, at least ${s.minFiles})`,a.length>=s.minFiles,"the walk found too few source files — it is looking in the wrong place, and every check below is vacuous");const i=new Map;for(const e of a){const t=p(e.text);t>0&&i.set(e.path,t)}const d=[...i].filter(([e])=>!(e in f)).map(([e,t])=>`${e} (${t})`);n("no file outside the allowlist hand-rolls a file input, drop zone or paste handler",d.length===0,`take files through useFileIntake from efficient-shell/file-intake (harness UI-15 / PAT-10):
2
+ ${d.join(`
3
+ `)}`);const u=Object.entries(f).filter(([e,t])=>(i.get(e)??0)>t.max).map(([e,t])=>`${e}: ${i.get(e)} > ${t.max}`);n("no allowlisted file grows its hand-rolled intake",u.length===0,u.join(`
4
+ `));for(const[e,t]of Object.entries(f))n(`the allowlist entry is still real: ${e} (${t.why})`,i.has(e),"remove the entry — the ratchet only tightens");for(const e of s.migrated??[]){const t=a.find(h=>h.path===e);!n(`read the migrated surface ${e}`,!!t,"it is not among the files handed in")||!t||(n(`${e} imports the shell's intake`,/from "efficient-shell\/file-intake"/.test(t.text)),n(`${e} drives its zone with useFileIntake`,/useFileIntake\(/.test(t.text)),n(`${e} spreads the intake's drop handlers on its zone`,/\.\.\.intake\.zoneProps/.test(t.text)))}const m=a.filter(e=>c.test(e.text)).map(e=>e.path);return n("nothing imports the shell's root entry",m.length===0,`the root pulls the shell's toasts, stylesheet and window manager; take a React-only subpath:
5
+ ${m.join(`
6
+ `)}`),$()}export{p as intakeMarkerCount,I as uploadIntakeGuard};
@@ -0,0 +1,8 @@
1
+ import { type RuleReport, type SourceFile } from "./rule.js";
2
+ export type WordStylesOptions = {
3
+ readonly exempt?: Readonly<Record<string, string>>;
4
+ readonly minFiles: number;
5
+ readonly labelFloor: number;
6
+ };
7
+ export declare function labelsIn(text: string): string[];
8
+ export declare function wordStyles(files: readonly SourceFile[], options: WordStylesOptions): RuleReport;
@@ -0,0 +1,4 @@
1
+ import{ruleChecks as p}from"./rule.js";const m=/(?:\baria-label|\blabel|\btitle|\bplaceholder)\s*[:=]\s*\{?\s*(["'])([^"']*)\1/g,u=/^(Emphasis|Strong)\b/;function i(s){return[...s.matchAll(m)].map(e=>e[2])}function h(s){return i(s.text).filter(e=>u.test(e)).map(e=>`${s.path}: "${e}"`)}function y(s,e){const{check:l,report:f}=p("word styles"),g=e.exempt??{};{const t={path:"fixture.tsx",text:['const a = { style: "wordHighlight", title: "Emphasis word style" };','const b = <button aria-label="Strong word style" />;','const c = <input placeholder={"Emphasis"} />;',"const d = { label: 'Strong' };","// the Emphasis button and the Strong one are upstream's",'toggle("Strong", false);','const e = { title: "Highlight word style", label: "Bold" };','const f = { title: "A strong claim", label: "Strongest" };'].join(`
2
+ `)},r=i(t.text),a=["Emphasis word style","Strong word style","Emphasis","Strong","Highlight word style","Bold","A strong claim","Strongest"];l("the reader reads a label, title, aria-label and placeholder, and neither prose nor a positional argument",JSON.stringify(r)===JSON.stringify(a),JSON.stringify(r));const o=h(t);l("the rule flags a label that begins Emphasis or Strong, and passes Highlight, Bold, `A strong …` and `Strongest`",JSON.stringify(o)===JSON.stringify(a.slice(0,4).map(b=>`fixture.tsx: "${b}"`)),JSON.stringify(o))}const n=s.filter(t=>/\.tsx?$/.test(t.path)),d=n.reduce((t,r)=>t+i(r.text).length,0);l(`read the source (${n.length} .ts/.tsx files, at least ${e.minFiles})`,n.length>=e.minFiles),l(`read the labels (${d}, at least ${e.labelFloor})`,d>=e.labelFloor,"fewer means the label reader broke, and an absence over a reader that matches nothing is free");const c=n.filter(t=>!(t.path in g)).flatMap(h);l("no merchant-visible label calls our word styles Emphasis or Strong",c.length===0,`our word styles are called Highlight and Bold (2026-10-04) — Emphasis and Strong belong to the upstream ◆ and bold buttons, whose descriptors live in efficient-easel and are not written here:
3
+ ${c.join(`
4
+ `)}`);for(const[t,r]of Object.entries(g)){const a=n.find(o=>o.path===t);l(`the exemption is still real: ${t} (${r})`,!!a&&h(a).length>0,"the file is gone or no longer writes the retired words — remove the stale entry")}return f()}export{i as labelsIn,y as wordStyles};
@@ -1 +1 @@
1
- import{useCallback as m,useEffect as y,useRef as l}from"react";import{backendOf as b,currentEditorSession as v}from"./editor-session.js";import{useEditorSession as x}from"./editor-session-context.js";import{WebsiteBackendError as E}from"./web-backend.js";function h(e=v()){return(t,i,n)=>{const r=b(e).theme;return r?r.update(t,n?{siteId:n}:void 0):Promise.reject(new E("invalid","This backend stores no site theme."))}}function w(e){return e.themeFromDocument?{status:"skipped",reason:"template"}:e.token?null:{status:"skipped",reason:"no-token"}}async function S(e,t=h()){const{themeFromDocument:i,token:n,siteId:r,patch:s}=e,a=w({themeFromDocument:i,token:n});if(a)return a;if(Object.keys(s).length===0)return{status:"skipped",reason:"empty"};try{return await t(s,n,r??void 0),{status:"saved"}}catch(f){return{status:"failed",error:f}}}function R({themeFromDocument:e,tokenRef:t,siteId:i,onResult:n}){const r=l(null),s=l({}),a=x(),f=l(n);f.current=n;const d=l(null),p=m(u=>{if(u.status==="skipped"){const o=`skipped:${u.reason}`;if(d.current===o)return;d.current=o}else d.current=null;f.current?.(u)},[]),c=m(async()=>{r.current&&(clearTimeout(r.current),r.current=null);const u=s.current;s.current={};const o=await S({themeFromDocument:e,token:t?.current,siteId:i,patch:u},h(a));return p(o),o},[e,t,i,p,a]),T=m((u,o=!1)=>{const k=w({themeFromDocument:e,token:t?.current});if(k){p(k);return}s.current={...s.current,...u},r.current&&clearTimeout(r.current),o?c():r.current=setTimeout(()=>{c()},500)},[e,t,c,p]);return y(()=>()=>{c()},[c]),{persist:T,flush:c}}export{w as refusalToQueue,h as themeRowSender,R as useThemeRowPersist,S as writeThemeRow};
1
+ import{useCallback as m,useEffect as y,useRef as l}from"react";import{currentEditorSession as b}from"./editor-session.js";import{backendOf as v}from"./website-session.js";import{useEditorSession as x}from"./editor-session-context.js";import{WebsiteBackendError as E}from"./web-backend.js";function h(e=b()){return(t,i,n)=>{const r=v(e).theme;return r?r.update(t,n?{siteId:n}:void 0):Promise.reject(new E("invalid","This backend stores no site theme."))}}function w(e){return e.themeFromDocument?{status:"skipped",reason:"template"}:e.token?null:{status:"skipped",reason:"no-token"}}async function S(e,t=h()){const{themeFromDocument:i,token:n,siteId:r,patch:s}=e,a=w({themeFromDocument:i,token:n});if(a)return a;if(Object.keys(s).length===0)return{status:"skipped",reason:"empty"};try{return await t(s,n,r??void 0),{status:"saved"}}catch(f){return{status:"failed",error:f}}}function B({themeFromDocument:e,tokenRef:t,siteId:i,onResult:n}){const r=l(null),s=l({}),a=x(),f=l(n);f.current=n;const d=l(null),p=m(u=>{if(u.status==="skipped"){const o=`skipped:${u.reason}`;if(d.current===o)return;d.current=o}else d.current=null;f.current?.(u)},[]),c=m(async()=>{r.current&&(clearTimeout(r.current),r.current=null);const u=s.current;s.current={};const o=await S({themeFromDocument:e,token:t?.current,siteId:i,patch:u},h(a));return p(o),o},[e,t,i,p,a]),T=m((u,o=!1)=>{const k=w({themeFromDocument:e,token:t?.current});if(k){p(k);return}s.current={...s.current,...u},r.current&&clearTimeout(r.current),o?c():r.current=setTimeout(()=>{c()},500)},[e,t,c,p]);return y(()=>()=>{c()},[c]),{persist:T,flush:c}}export{w as refusalToQueue,h as themeRowSender,B as useThemeRowPersist,S as writeThemeRow};
@@ -1 +1 @@
1
- function t(e){switch(e.status){case"saved":return null;case"failed":return{msg:"Not saved. Try again.",tone:"error"};case"skipped":switch(e.reason){case"empty":return null;case"no-token":return{msg:"Not saved. Reload the page.",tone:"error"};case"template":return{msg:"Templates keep their look. Press Save.",tone:"success"}}}}export{t as themeSaveNotice};
1
+ function t(e){switch(e.status){case"saved":return null;case"failed":return{msg:"Not saved. Try again.",tone:"error"};case"skipped":switch(e.reason){case"empty":return null;case"no-token":return{msg:"Not saved. Reload the page.",tone:"error"};case"template":return{msg:"Templates keep their look. Press Done.",tone:"success"}}}}export{t as themeSaveNotice};