efficient-web-builder 24.0.0 → 25.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,49 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 25.1.0 - 2026-10-20
6
+
7
+ **Chart, Integrations and Mockup no longer crash a published page; the rules over source read a host's imports** (keep editor code off shopper pages, storefront step 2b). A MINOR: two exports are added (below), none changes or goes.
8
+
9
+ ### Fixed
10
+
11
+ - **A published Chart, Integrations or Mockup crashed once a merchant typed into one of its text rows.** A row with an eye (`text()` / `textarea()`) stores `{ text, hidden }` after the first keystroke, and these three renders passed every such row but the heading on raw: `ChartSection` called `(body ?? "").trim()` on the object and threw. Each render now reads every hideable row through the one reader: `hidableRow` (`lib/puck/blocks/scaffold`, beside `phHide`) gives the words, `""` when the eye is shut, and nothing when nothing was stored, so a bare string — a row stored before it had an eye — draws byte for byte as before. **Both stored shapes are safe, so no stored page needs a change.**
12
+ - Since 24.0.0 a host imports the builder by package name and side (`efficient-web-builder/render/components/Btn`), while the builder's own `src/` writes `@/components/Btn`. Five rules knew only the second spelling: `button-corners`, `panel-rows-use-the-shared-row`, `puck-document-surfaces`, `section-theme-preview` and `source-status-is-shared`. On a host each one read none of its imports, so a floor such as "the sweep found at least 9 files importing Btn" failed for a reason that was not the rule's. Each now reads both spellings through one helper, `builderModuleSpec` (`lib/source-rules/support/ts-source`), and the rules with an import reader on a fixture check the host's spelling too.
13
+ - `panel-rows-use-the-shared-row` read a WRAPPED row's arguments wrong: for `withShowWhen(segmented("Shape", <SquareIcon />, …), rule)` it took the wrapper's paren and read `rule` as the glyph, so a row with a glyph was reported bare. `callParen` returns the paren that ends the match. A fixture holds both cases: the glyphed wrapped row passes and a bare one is caught.
14
+
15
+ ### Added
16
+
17
+ - `hidableRow` (`efficient-web-builder/render/lib/puck/blocks/scaffold`): a hideable row as a component that takes a plain string reads it. Built on `readHidable`, the one reader.
18
+ - `lib/puck/hidable-rows-drawn` (`efficient-web-builder/editor/lib/puck/hidable-rows-drawn`): the check that every hideable row draws in both stored shapes and that a shut eye hides it, at a block's defaults and in every variant. One definition: the builder runs it over the general set (`scripts/test-hidable-rows-drawn.tsx`), a host over its own blocks with its own providers (it hands in the drawing). A planted render that passes a row raw fails it.
19
+
20
+ ## 25.0.0 - 2026-10-19
21
+
22
+ **The letter cases are one code, easel's (#159), and the builder runs on efficient-shell 8** (SSOT cleanup, GG 2026-10-17; keep editor code off shopper pages, efficient-shell #553). Every site draws what it drew. A MAJOR for hosts, for the two reasons below.
23
+
24
+ ### Changed for hosts (breaking)
25
+
26
+ - **BREAKING: `efficient-easel` peer and dev range `^13.2.0`** (it was `^13.1.1`). The case code comes from `efficient-easel/draw`, which carries it since 13.2.0 (efficient-easel #296): a published page may take easel only through `render`, `draw` and `draw/client`. 13.2.0 also accepts efficient-shell 8 and names its render group in its `package.json` (`entryGroups.render`).
27
+ - **BREAKING: `PART_CASES` and `PartCase` are gone from `render/lib/puck/part-styles`.** They were second names for easel's `TEXT_STYLE_CASES` and `TextStyleCase`; import those from `efficient-easel/draw` (no host imports them today).
28
+
29
+ ### Changed
30
+
31
+ - The efficient-shell peer range is `^7.75.0 || ^8.0.0`. Shell 8 moves its icon maps to `efficient-shell/icons/sets` and makes every drawing a module of its own; the builder imports drawings by name and no map (24.0.0), so nothing it uses moves. The dev copy is `^8.0.0`, so the build and the guards run on 8.
32
+ - `scripts/entry-sides.mjs` reads the easel entries a render module may import from that field. It no longer writes the 11 entries out, so the list has one home: easel's. The build fails if the installed easel names no render group.
33
+ - `partCaseTransform` / `partCaseOfTransform` read easel's `textStyleCaseCss` / `textStyleCaseOfCss`. Their own `CASE_TRANSFORM` table is gone, and so is `site-text-styles.ts`'s `CASE_CSS`. The CSS each case draws is the same.
34
+
35
+ ### Guards
36
+
37
+ - `scripts/test-one-text-case-code.ts`: the four cases turn into their CSS and back, and no file in `src/` spells a case list, a case-to-CSS table, `PART_CASES` or `PartCase` again.
38
+ - `test-render-closure`: on shell 8 the looks map lives in `efficient-shell/icons/sets`; a shopper file that imports it is a finding, and the byte ceiling now reads the map's own chunk (the one `icons/looks/map.js` re-exports), not `icons/efficient-looks`, which only re-exports since 8.0.0.
39
+ - `test-clear-formatting`: the shell toolbar's buttons keep the field's focus through one named handler in shell 8's build (`onMouseDown:i`, `i=e=>e.preventDefault()`); the check reads both forms, and a handler that does not prevent the default still fails.
40
+ - `test-image-copies-sized`: shell 8.1.0's contract names the live copies host, so "no host" is now `host: null` set on purpose, and a new §2b checks that a page asks for copies by default.
41
+ - `test-corners-and-grid-from-the-shell` no longer checks the shell range a second time (`test-shell-peer-range` holds it).
42
+
43
+ ### For hosts
44
+
45
+ - A host may move to efficient-shell 8 with this release, and moves its efficient-easel to 13.2.0. A host on shell 7.75 or later needs no other change.
46
+ - ⚠️ **efficient-shell 8.1.0 turns image copies on**: every image the builder draws from an upload asks for a resized copy from the live copies host. Before a host takes shell 8.1.0, its pages must allow that host in `img-src` (efficient-shell 8.1.0's CHANGELOG). Shell 8.0.x still hands the original back.
47
+
5
48
  ## 24.0.0 - 2026-10-19
6
49
 
7
50
  **Three sides: render, format and editor, and the package exports them and nothing else** (keep editor code off shopper pages, PR 1c; plan approved by JJ, `doc/adr/0005`). A published page draws from the `render` side, which cannot reach the editor. A MAJOR: every import path a host writes moves.
@@ -1,7 +1,8 @@
1
- import type { PartCase, PartWeight } from "../../render/lib/puck/part-styles.js";
1
+ import type { PartWeight } from "../../render/lib/puck/part-styles.js";
2
+ import type { TextStyleCase } from "efficient-easel/draw";
2
3
  export declare function WeightGlyph({ weight }: {
3
4
  weight?: PartWeight;
4
5
  }): import("react").JSX.Element;
5
6
  export declare function CaseGlyph({ letterCase }: {
6
- letterCase?: PartCase;
7
+ letterCase?: TextStyleCase;
7
8
  }): import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ export type HidableCheckField = {
2
+ type?: string;
3
+ arrayFields?: Record<string, unknown>;
4
+ options?: {
5
+ value?: unknown;
6
+ }[];
7
+ } & Record<string, unknown>;
8
+ export type HidableCheckBlock = {
9
+ fields?: Record<string, HidableCheckField | undefined>;
10
+ defaultProps?: Record<string, unknown>;
11
+ };
12
+ export type HidableCheckDraw = (type: string, props: Record<string, unknown>) => {
13
+ html?: string;
14
+ error?: string;
15
+ };
16
+ export declare const isHideableRow: (field: unknown) => boolean;
17
+ export declare function hidableRowProblems(blocks: Record<string, HidableCheckBlock>, types: readonly string[], draw: HidableCheckDraw): {
18
+ list: string[];
19
+ rows: number;
20
+ drawings: number;
21
+ };
@@ -0,0 +1 @@
1
+ import{textControlKind as m}from"../../../render/lib/puck/part-text.js";const v=new Set(["text","textarea"]),g=s=>v.has(m(s)??""),d=s=>`Zq${s.replace(/[^A-Za-z0-9]/g,"")}Zq`,j=(s,n)=>n==="bare"?d(s):{text:d(s),hidden:n==="shut"};function $(s,n){const t={...s.defaultProps??{},id:"t-1",puck:{isEditing:!1}},r=[];for(const[e,o]of Object.entries(s.fields??{}))if(o){if(o.type==="slot")t[e]=()=>null;else if(g(o))t[e]=j(e,n),r.push(d(e));else if(o.type==="array"&&o.arrayFields){const i=Object.entries(o.arrayFields).filter(([,a])=>g(a));if(!i.length)continue;const c=Array.isArray(t[e])&&t[e].length?t[e]:[{},{}];t[e]=c.map((a,f)=>{const u={...a??{}};for(const[l]of i)u[l]=j(`${e}${f}${l}`,n),r.push(d(`${e}${f}${l}`));return u})}}return{props:t,needles:r}}function A(s){const n=[{label:"",props:{}}];for(const[t,r]of Object.entries(s.fields??{})){const e=r?.options;if(!(!Array.isArray(e)||e.length>16))for(const o of e)o&&typeof o.value!="object"&&n.push({label:` (${t}=${String(o.value)})`,props:{[t]:o.value}})}return n}function k(s,n,t){const r=[];let e=0,o=0;for(const i of n){const c=s[i];if(!c)continue;const a=$(c,"open");if(e+=a.needles.length,!a.needles.length)continue;const f=$(c,"shut"),u=$(c,"bare");for(const l of A(c)){o+=1;const p=`${i}${l.label}`,h=t(i,{...a.props,...l.props});h.error?r.push(`${p}: a typed row ({ text, hidden: false }) throws — ${h.error}`):h.html?.includes("[object Object]")&&r.push(`${p}: a typed row reaches the page as [object Object]`);const b=t(i,{...f.props,...l.props});if(b.error)r.push(`${p}: a hidden row ({ text, hidden: true }) throws — ${b.error}`);else{const y=f.needles.filter(x=>b.html?.includes(x));y.length&&r.push(`${p}: a hidden row still draws its words (${y.join(", ")})`)}const w=t(i,{...u.props,...l.props});w.error&&r.push(`${p}: a row stored as a bare string throws — ${w.error}`)}}return{list:r,rows:e,drawings:o}}export{k as hidableRowProblems,g as isHideableRow};
@@ -1,2 +1,2 @@
1
- import{FONT_OPTIONS as D,normalizeFonts as F}from"../../format/lib/fonts.js";import{PART_PHONE_MAX_CSS as J,partSpacingOf as B}from"../../render/lib/puck/part-styles.js";import{compressionMetrics as G,headingTypographyFromRow as U,normalizeHeadingCompression as C,normalizeSizes as I}from"../../format/lib/theme.js";const H=e=>e==="sentence"?"none":void 0,h=e=>typeof e=="number"&&Number.isFinite(e)?e:void 0,v=e=>typeof e=="string"&&e?e:void 0,E=["font","color","size","sizePhone","weight","italic","lineHeight","letterSpacing","paragraphSpacing","letterCase","largestSize"];function p(e){const i={};for(const n of E)e[n]!==void 0&&(i[n]=e[n]);return i}function w(e,i){const{auto:n,...t}=e,a=new Set(i??[]),s=E.filter(o=>a.has(o)&&e.values[o]===void 0);return s.length?{...t,auto:s}:t}function O(e){const i=e.fonts,n=e.sizes,t=e.typography,a=G(e.compression),s=(l,r)=>({id:`heading-${l}`,kind:"heading",name:`Heading ${l}`,values:p(r)}),o=(l,r={})=>({id:`body-${l}`,kind:"body",name:`Body ${l}`,values:p(r)});return[s(1,{font:v(i.heading),size:h(n.headingLarge),weight:h(t.weight),lineHeight:a.lineHeight,letterSpacing:a.letterSpacing,letterCase:H(t.case),largestSize:h(t.maxPx)}),s(2,{font:v(i.heading2),size:h(n.headingMedium),weight:h(t.weight2),lineHeight:a.lineHeight,letterSpacing:a.letterSpacing,letterCase:H(t.case2)}),s(3,{font:v(i.heading3),size:h(n.headingSmall),weight:h(t.weight3),lineHeight:a.lineHeight,letterSpacing:a.letterSpacing,letterCase:H(t.case3)}),o(1,{font:v(i.body),size:h(n.body)}),o(2),o(3),o(4),o(5,{font:v(i.label),size:h(n.label)})]}function _(e){return Array.isArray(e)&&e.every(n=>n&&typeof n=="object"&&typeof n.id=="string"&&(n.kind==="heading"||n.kind==="body")&&typeof n.name=="string")?e.map(n=>w({...n,values:p(n.values??{})},Array.isArray(n.auto)?n.auto:void 0)):null}function ie(e){return _(e)??[]}function T(e,i){const n=O(i),t=_(e);return t?t.map(a=>{const s={...P(n,a.id)};for(const o of a.auto??[])delete s[o];return{...a,values:p({...s,...a.values})}}):n}const P=(e,i)=>e.find(n=>n.id===i)?.values??{},M=["heading-1","heading-2","heading-3"];function ae({saved:e,old:i,cur:n,next:t,sizeOf:a}){const s=O(i),o=_(e)??s.map(d=>({...d,values:{}})),l=T(o,i),r=[],g=t.map(d=>{const f=n.find(u=>u.id===d.id),S=o.find(u=>u.id===d.id);if(!f||!S)return W([d],a)[0];const c={...S.values},x=new Set(S.auto??[]),z=d.values,A=f.values,N=[...new Set([...Object.keys(z),...Object.keys(A)])].filter(u=>JSON.stringify(z[u])!==JSON.stringify(A[u]));N.sort((u,m)=>+(u==="letterSpacing")-+(m==="letterSpacing"));for(const u of N){const m=u==="lineSpacing"?"lineHeight":u,k=z[u];if(k===void 0){delete c[m],P(s,d.id)[m]!==void 0&&(r.push([d.id,m]),x.add(m));continue}if(x.delete(m),u==="lineSpacing")c.lineHeight=b(k/100,3);else if(u==="letterSpacing"){const R=l.find(V=>V.id===d.id),j=a({...d,values:p({...R?.values??{},...c})});c.letterSpacing=j?b(k/j,4):0}else c[m]=k}return w({id:d.id,kind:d.kind,name:d.name,values:p(c)},x)});let y=i.compression;if(r.some(([d,f])=>(f==="lineHeight"||f==="letterSpacing")&&M.includes(d))&&C(y)!=="normal"){for(const d of M){const f=g.find(c=>c.id===d);if(!f)continue;const S={...f.values};for(const c of["lineHeight","letterSpacing"])r.some(([x,z])=>x===d&&z===c)||S[c]===void 0&&!(f.auto??[]).includes(c)&&(S[c]=P(s,d)[c]);f.values=p(S)}y="normal"}return{saved:g,old:{...i,compression:y}}}function oe(e,i){const n={text_styles:e.saved};return e.old.compression!==i.compression&&(n.heading_compression=C(e.old.compression)),n}const b=(e,i)=>Number(e.toFixed(i));function se(e,i){return e.map(n=>{const{lineHeight:t,letterSpacing:a,...s}=n.values,o={...s};return t!==void 0&&(o.lineSpacing=b(t*100,1)),a!==void 0&&(o.letterSpacing=b(a*i(n),2)),{id:n.id,kind:n.kind,name:n.name,values:o}})}function W(e,i){return e.map(n=>{const{lineSpacing:t,letterSpacing:a,...s}=n.values,o={id:n.id,kind:n.kind,name:n.name,values:p({...s})},l={...o.values};if(t!==void 0&&(l.lineHeight=b(t/100,3)),a!==void 0){const r=i({...o,values:{...o.values}});l.letterSpacing=r?b(a/r,4):0}return{...o,values:p(l)}})}function re(e){const i={fonts:F({heading:e.heading_font??void 0,heading2:e.heading_2_font??void 0,heading3:e.heading_3_font??void 0,body:e.body_font??void 0,accent:e.accent_font??void 0,label:e.label_font??void 0}),sizes:I({headingLarge:e.heading_size_large_px,headingMedium:e.heading_size_medium_px,headingSmall:e.heading_size_small_px,accent:e.accent_size_px,body:e.body_size_px,label:e.label_size_px}),compression:C(e.heading_compression),typography:U(e)},n=e.text_styles??null,t=Array.isArray(n)?L(T(n,i),i):i;return{fonts:F(t.fonts),sizes:I(t.sizes),compression:C(t.compression),typography:t.typography,textStyles:n}}function L(e,i){const n=d=>e.find(f=>f.id===d)?.values??{},t=n("heading-1"),a=n("heading-2"),s=n("heading-3"),o=n("body-1"),l=n("body-5"),r=i.fonts,g=i.sizes,y=i.typography,$=d=>d==="none"?"sentence":"authored";return{fonts:{...r,heading:t.font??r.heading,heading2:a.font??t.font??r.heading2,heading3:s.font??t.font??r.heading3,body:o.font??r.body,label:l.font??r.label},sizes:{...g,headingLarge:t.size??null,headingMedium:a.size??null,headingSmall:s.size??null,body:o.size??null,label:l.size??null},compression:i.compression,typography:{...y,case:$(t.letterCase),weight:t.weight??null,maxPx:t.largestSize??null,weight2:a.weight??null,weight3:s.weight??null,case2:$(a.letterCase),case3:$(s.letterCase)}}}function de(e){const i=_(e.textStyles);if(!i||i.every(a=>Object.keys(a.values).length===0&&!a.auto))return e;const n={fonts:e.fonts??{},sizes:e.sizes??{},compression:String(e.headingCompression??"normal"),typography:e.headingTypography??{}},t=L(T(i,n),n);return{...e,fonts:t.fonts,sizes:t.sizes,headingCompression:t.compression,headingTypography:t.typography}}function X(e){switch(e.id){case"heading-1":return"font-display";case"heading-2":return"font-heading2";case"heading-3":return"font-heading3";case"body-1":return"font-body";case"body-5":return"font-mono";default:return`font-${e.id}`}}const Z={none:"none",upper:"uppercase",capitalize:"capitalize",lower:"lowercase"};function q(e){return e==="accent"?"var(--accent)":e==="text"?"var(--fg)":e==="labels"?"var(--fg-dim)":/^#[0-9a-f]{6}$/i.test(e)?e:null}function K(e){const i=":is([data-sf-bullet], [data-sf-number])";return[`${e} br:not(br + br) { display: block; content: ""; margin-top: var(--sf-part-para) }`,`${e} ${i}:not(:first-child) { margin-top: var(--sf-part-para) }`,`${e} ${i}:not(:last-child) { margin-bottom: var(--sf-part-para) }`]}function le(e){const i=[];for(const n of e){const t=n.values,a=`.${X(n)}`,s=[],o=t.font!==void 0?D.find(g=>g.key===t.font)?.cssVar:void 0;if(o&&s.push(`font-family: var(${o})`),t.size!==void 0){const g=t.largestSize!==void 0?Math.min(t.size,t.largestSize):t.size;s.push(`font-size: ${g}px`)}const l=t.color!==void 0?q(t.color):null;l&&s.push(`color: ${l}`),t.italic===!0&&s.push("font-style: italic"),t.weight!==void 0&&s.push(`font-weight: ${t.weight}`);const r=[];if(t.lineHeight!==void 0&&r.push(`line-height: ${t.lineHeight}`),t.letterSpacing!==void 0&&r.push(`letter-spacing: ${t.letterSpacing}em`),n.kind==="body"&&s.push(...r),t.letterCase!==void 0&&s.push(`text-transform: ${Z[t.letterCase]}`),t.paragraphSpacing&&s.push(`--sf-part-para: ${t.paragraphSpacing}px`),s.length&&i.push(`${a} { ${s.join("; ")} }`),n.kind==="heading"&&r.length&&i.push(`${a}, ${a}:is(h1, h2, h3, h4) { ${r.join("; ")} }`),t.paragraphSpacing&&i.push(...K(a)),t.sizePhone!==void 0){const g=Q[n.id],y=[`${a} { font-size: ${t.sizePhone}px }`];g&&y.push(`.sf-themed-content { --size-${g}: ${t.sizePhone}px !important }`),i.push(`@media ${J} { ${y.join(" ")} }`)}}return i.join(`
2
- `)}const Q={"heading-1":"headingLarge","heading-2":"headingMedium","heading-3":"headingSmall","body-1":"body","body-5":"label"};function ce(e){return e!==!0&&e!==!1?"":`em.text-accent { font-style: ${e?"italic":"normal"} }`}function ue(e){switch(e){case"heading":return"heading-1";case"heading2":return"heading-2";case"heading3":return"heading-3";case"body":return"body-1";case"label":return"body-5";default:return}}function Y(e){const i=B(e);return p({font:e.font,color:e.color,size:e.size,weight:e.weight,italic:e.italic,lineHeight:i.lineHeight,letterSpacing:i.letterSpacingEm,paragraphSpacing:e.paragraphSpacing,letterCase:e.case})}function fe(e){return Object.keys(Y(e)).length>0}function ge({saved:e,old:i,styleId:n,values:t}){return{saved:(_(e)??O(i).map(o=>({...o,values:{}}))).map(o=>o.id===n?w({...o,values:p({...o.values,...t})},o.auto):o),old:i}}function pe(e){const i=e.filter(n=>n.kind==="body").map(n=>Number(/^body-(\d+)$/.exec(n.id)?.[1])).filter(n=>Number.isInteger(n)&&n>0);return i.length?`body-${Math.max(...i)}`:"body-1"}export{ce as accentItalicCss,ae as applyListChange,pe as eyebrowStyleId,X as faceClassOf,W as fromPanelStyles,fe as hasStyleChange,oe as listChangePatch,Y as partStyleToTextStyleValues,ge as pushIntoStyle,T as resolveTextStyles,re as siteTextFromRow,ie as storedTextStyles,ue as styleIdOfRole,le as textStylesCss,O as textStylesFromOld,L as textStylesToOld,de as themeWithTextStyles,se as toPanelStyles};
1
+ import{textStyleCaseCss as D}from"efficient-easel/draw";import{FONT_OPTIONS as J,normalizeFonts as F}from"../../format/lib/fonts.js";import{PART_PHONE_MAX_CSS as B,partSpacingOf as G}from"../../render/lib/puck/part-styles.js";import{compressionMetrics as U,headingTypographyFromRow as W,normalizeHeadingCompression as C,normalizeSizes as I}from"../../format/lib/theme.js";const H=e=>e==="sentence"?"none":void 0,h=e=>typeof e=="number"&&Number.isFinite(e)?e:void 0,_=e=>typeof e=="string"&&e?e:void 0,M=["font","color","size","sizePhone","weight","italic","lineHeight","letterSpacing","paragraphSpacing","letterCase","largestSize"];function p(e){const i={};for(const n of M)e[n]!==void 0&&(i[n]=e[n]);return i}function O(e,i){const{auto:n,...t}=e,a=new Set(i??[]),s=M.filter(o=>a.has(o)&&e.values[o]===void 0);return s.length?{...t,auto:s}:t}function w(e){const i=e.fonts,n=e.sizes,t=e.typography,a=U(e.compression),s=(l,r)=>({id:`heading-${l}`,kind:"heading",name:`Heading ${l}`,values:p(r)}),o=(l,r={})=>({id:`body-${l}`,kind:"body",name:`Body ${l}`,values:p(r)});return[s(1,{font:_(i.heading),size:h(n.headingLarge),weight:h(t.weight),lineHeight:a.lineHeight,letterSpacing:a.letterSpacing,letterCase:H(t.case),largestSize:h(t.maxPx)}),s(2,{font:_(i.heading2),size:h(n.headingMedium),weight:h(t.weight2),lineHeight:a.lineHeight,letterSpacing:a.letterSpacing,letterCase:H(t.case2)}),s(3,{font:_(i.heading3),size:h(n.headingSmall),weight:h(t.weight3),lineHeight:a.lineHeight,letterSpacing:a.letterSpacing,letterCase:H(t.case3)}),o(1,{font:_(i.body),size:h(n.body)}),o(2),o(3),o(4),o(5,{font:_(i.label),size:h(n.label)})]}function $(e){return Array.isArray(e)&&e.every(n=>n&&typeof n=="object"&&typeof n.id=="string"&&(n.kind==="heading"||n.kind==="body")&&typeof n.name=="string")?e.map(n=>O({...n,values:p(n.values??{})},Array.isArray(n.auto)?n.auto:void 0)):null}function ae(e){return $(e)??[]}function T(e,i){const n=w(i),t=$(e);return t?t.map(a=>{const s={...P(n,a.id)};for(const o of a.auto??[])delete s[o];return{...a,values:p({...s,...a.values})}}):n}const P=(e,i)=>e.find(n=>n.id===i)?.values??{},E=["heading-1","heading-2","heading-3"];function oe({saved:e,old:i,cur:n,next:t,sizeOf:a}){const s=w(i),o=$(e)??s.map(d=>({...d,values:{}})),l=T(o,i),r=[],y=t.map(d=>{const g=n.find(u=>u.id===d.id),S=o.find(u=>u.id===d.id);if(!g||!S)return X([d],a)[0];const c={...S.values},z=new Set(S.auto??[]),v=d.values,N=g.values,A=[...new Set([...Object.keys(v),...Object.keys(N)])].filter(u=>JSON.stringify(v[u])!==JSON.stringify(N[u]));A.sort((u,m)=>+(u==="letterSpacing")-+(m==="letterSpacing"));for(const u of A){const m=u==="lineSpacing"?"lineHeight":u,k=v[u];if(k===void 0){delete c[m],P(s,d.id)[m]!==void 0&&(r.push([d.id,m]),z.add(m));continue}if(z.delete(m),u==="lineSpacing")c.lineHeight=x(k/100,3);else if(u==="letterSpacing"){const R=l.find(V=>V.id===d.id),j=a({...d,values:p({...R?.values??{},...c})});c.letterSpacing=j?x(k/j,4):0}else c[m]=k}return O({id:d.id,kind:d.kind,name:d.name,values:p(c)},z)});let f=i.compression;if(r.some(([d,g])=>(g==="lineHeight"||g==="letterSpacing")&&E.includes(d))&&C(f)!=="normal"){for(const d of E){const g=y.find(c=>c.id===d);if(!g)continue;const S={...g.values};for(const c of["lineHeight","letterSpacing"])r.some(([z,v])=>z===d&&v===c)||S[c]===void 0&&!(g.auto??[]).includes(c)&&(S[c]=P(s,d)[c]);g.values=p(S)}f="normal"}return{saved:y,old:{...i,compression:f}}}function se(e,i){const n={text_styles:e.saved};return e.old.compression!==i.compression&&(n.heading_compression=C(e.old.compression)),n}const x=(e,i)=>Number(e.toFixed(i));function re(e,i){return e.map(n=>{const{lineHeight:t,letterSpacing:a,...s}=n.values,o={...s};return t!==void 0&&(o.lineSpacing=x(t*100,1)),a!==void 0&&(o.letterSpacing=x(a*i(n),2)),{id:n.id,kind:n.kind,name:n.name,values:o}})}function X(e,i){return e.map(n=>{const{lineSpacing:t,letterSpacing:a,...s}=n.values,o={id:n.id,kind:n.kind,name:n.name,values:p({...s})},l={...o.values};if(t!==void 0&&(l.lineHeight=x(t/100,3)),a!==void 0){const r=i({...o,values:{...o.values}});l.letterSpacing=r?x(a/r,4):0}return{...o,values:p(l)}})}function de(e){const i={fonts:F({heading:e.heading_font??void 0,heading2:e.heading_2_font??void 0,heading3:e.heading_3_font??void 0,body:e.body_font??void 0,accent:e.accent_font??void 0,label:e.label_font??void 0}),sizes:I({headingLarge:e.heading_size_large_px,headingMedium:e.heading_size_medium_px,headingSmall:e.heading_size_small_px,accent:e.accent_size_px,body:e.body_size_px,label:e.label_size_px}),compression:C(e.heading_compression),typography:W(e)},n=e.text_styles??null,t=Array.isArray(n)?L(T(n,i),i):i;return{fonts:F(t.fonts),sizes:I(t.sizes),compression:C(t.compression),typography:t.typography,textStyles:n}}function L(e,i){const n=d=>e.find(g=>g.id===d)?.values??{},t=n("heading-1"),a=n("heading-2"),s=n("heading-3"),o=n("body-1"),l=n("body-5"),r=i.fonts,y=i.sizes,f=i.typography,b=d=>d==="none"?"sentence":"authored";return{fonts:{...r,heading:t.font??r.heading,heading2:a.font??t.font??r.heading2,heading3:s.font??t.font??r.heading3,body:o.font??r.body,label:l.font??r.label},sizes:{...y,headingLarge:t.size??null,headingMedium:a.size??null,headingSmall:s.size??null,body:o.size??null,label:l.size??null},compression:i.compression,typography:{...f,case:b(t.letterCase),weight:t.weight??null,maxPx:t.largestSize??null,weight2:a.weight??null,weight3:s.weight??null,case2:b(a.letterCase),case3:b(s.letterCase)}}}function le(e){const i=$(e.textStyles);if(!i||i.every(a=>Object.keys(a.values).length===0&&!a.auto))return e;const n={fonts:e.fonts??{},sizes:e.sizes??{},compression:String(e.headingCompression??"normal"),typography:e.headingTypography??{}},t=L(T(i,n),n);return{...e,fonts:t.fonts,sizes:t.sizes,headingCompression:t.compression,headingTypography:t.typography}}function Z(e){switch(e.id){case"heading-1":return"font-display";case"heading-2":return"font-heading2";case"heading-3":return"font-heading3";case"body-1":return"font-body";case"body-5":return"font-mono";default:return`font-${e.id}`}}function q(e){return e==="accent"?"var(--accent)":e==="text"?"var(--fg)":e==="labels"?"var(--fg-dim)":/^#[0-9a-f]{6}$/i.test(e)?e:null}function K(e){const i=":is([data-sf-bullet], [data-sf-number])";return[`${e} br:not(br + br) { display: block; content: ""; margin-top: var(--sf-part-para) }`,`${e} ${i}:not(:first-child) { margin-top: var(--sf-part-para) }`,`${e} ${i}:not(:last-child) { margin-bottom: var(--sf-part-para) }`]}function ce(e){const i=[];for(const n of e){const t=n.values,a=`.${Z(n)}`,s=[],o=t.font!==void 0?J.find(f=>f.key===t.font)?.cssVar:void 0;if(o&&s.push(`font-family: var(${o})`),t.size!==void 0){const f=t.largestSize!==void 0?Math.min(t.size,t.largestSize):t.size;s.push(`font-size: ${f}px`)}const l=t.color!==void 0?q(t.color):null;l&&s.push(`color: ${l}`),t.italic===!0&&s.push("font-style: italic"),t.weight!==void 0&&s.push(`font-weight: ${t.weight}`);const r=[];t.lineHeight!==void 0&&r.push(`line-height: ${t.lineHeight}`),t.letterSpacing!==void 0&&r.push(`letter-spacing: ${t.letterSpacing}em`),n.kind==="body"&&s.push(...r);const y=D(t.letterCase);if(y&&s.push(`text-transform: ${y}`),t.paragraphSpacing&&s.push(`--sf-part-para: ${t.paragraphSpacing}px`),s.length&&i.push(`${a} { ${s.join("; ")} }`),n.kind==="heading"&&r.length&&i.push(`${a}, ${a}:is(h1, h2, h3, h4) { ${r.join("; ")} }`),t.paragraphSpacing&&i.push(...K(a)),t.sizePhone!==void 0){const f=Q[n.id],b=[`${a} { font-size: ${t.sizePhone}px }`];f&&b.push(`.sf-themed-content { --size-${f}: ${t.sizePhone}px !important }`),i.push(`@media ${B} { ${b.join(" ")} }`)}}return i.join(`
2
+ `)}const Q={"heading-1":"headingLarge","heading-2":"headingMedium","heading-3":"headingSmall","body-1":"body","body-5":"label"};function ue(e){return e!==!0&&e!==!1?"":`em.text-accent { font-style: ${e?"italic":"normal"} }`}function fe(e){switch(e){case"heading":return"heading-1";case"heading2":return"heading-2";case"heading3":return"heading-3";case"body":return"body-1";case"label":return"body-5";default:return}}function Y(e){const i=G(e);return p({font:e.font,color:e.color,size:e.size,weight:e.weight,italic:e.italic,lineHeight:i.lineHeight,letterSpacing:i.letterSpacingEm,paragraphSpacing:e.paragraphSpacing,letterCase:e.case})}function ge(e){return Object.keys(Y(e)).length>0}function pe({saved:e,old:i,styleId:n,values:t}){return{saved:($(e)??w(i).map(o=>({...o,values:{}}))).map(o=>o.id===n?O({...o,values:p({...o.values,...t})},o.auto):o),old:i}}function he(e){const i=e.filter(n=>n.kind==="body").map(n=>Number(/^body-(\d+)$/.exec(n.id)?.[1])).filter(n=>Number.isInteger(n)&&n>0);return i.length?`body-${Math.max(...i)}`:"body-1"}export{ue as accentItalicCss,oe as applyListChange,he as eyebrowStyleId,Z as faceClassOf,X as fromPanelStyles,ge as hasStyleChange,se as listChangePatch,Y as partStyleToTextStyleValues,pe as pushIntoStyle,T as resolveTextStyles,de as siteTextFromRow,ae as storedTextStyles,fe as styleIdOfRole,ce as textStylesCss,w as textStylesFromOld,L as textStylesToOld,le as themeWithTextStyles,re as toPanelStyles};
@@ -1,2 +1,2 @@
1
- import{ruleChecks as m}from"./rule.js";const l="--sf-button-radius",f="sf-button",p=i=>i.replace(/\/\*[\s\S]*?\*\//g,e=>" ".repeat(e.length)),b=i=>/^import\s+Btn\s+from\s+["'](?:@\/components\/Btn|\.{1,2}\/(?:[\w-]+\/)*Btn)["'];?$/m.test(i);function g(i){const e=[],n=/<Btn\b[^>]*className=(?:"([^"]*)"|\{`([^`]*)`\})/g;let u;for(;(u=n.exec(i.text))!==null;){const h=u[1]??u[2]??"";/\brounded-/.test(h)&&e.push(`${i.path}: ${h}`)}return e}function y(i,e){const{check:n,report:u}=m("button corners"),h=t=>i.find(s=>s.path===t);{const t={path:"fixture.tsx",text:['import Btn from "@/components/Btn";','<Btn href="/x" className="mt-4 rounded-lg">Go</Btn>',"<Btn className={`mt-2`}>Go</Btn>"].join(`
2
- `)};n("§6/§7 the readers find a Btn import and a rounded-* className handed to it",b(t.text)&&g(t).length===1)}if(e.stylesheet!==void 0){const t=h(e.stylesheet);if(n(`§1 read the stylesheet ${e.stylesheet}`,!!t)){const s=/\.sf-button\s*\{([^}]*)\}/.exec(p(t.text));n(`§1 ${e.stylesheet} carries a \`.sf-button\` rule`,s!==null,"without it the class is decoration and the setting reaches no button"),s&&(n(`§1 the .sf-button rule reads \`var(${l}, 0)\``,/border-radius:\s*var\(--sf-button-radius,\s*0\)/.test(s[1]),`the property for the setting, and the \`0\` for every site that never chose one — got: ${s[1].trim()}`),n("§1 the .sf-button rule does not clip",!/overflow/.test(s[1]),`the trailing arrow translates 3px right on hover and would be shaved — got: ${s[1].trim()}`))}}if(e.button!==void 0){const t=h(e.button);n(`§2 read the shared button ${e.button}`,!!t)&&n(`§2 ${e.button} writes no \`rounded-*\` utility`,!/\brounded-(?!full\b)[a-z0-9[]/.test(t.text.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm,"")),"every one carries a built-in value, so an untouched site would jump from square to a radius the day it shipped");const s=e.rendered??[];n(`§2 the button was rendered (${s.length} shapes, at least 4)`,s.length>=4,"both variants as a link and as a button");for(const[r,a]of s)n(`§2 ${r}: the rendered button carries \`${f}\``,new RegExp(`class="${f}\\b`).test(a),`it is the element's only ear for Corners — got: ${a.slice(0,140)}`)}const o=e.cornerRadiusTokens;if(o){n("§3 a site that never chose a corner publishes NO button radius",!(l in o(null)),`that absence is what leaves it square, byte for byte as before — got: ${JSON.stringify(o(null))}`),n("§3 `0` is an ANSWER, not an absence: it publishes a flattened radius",o(0)[l]==="0",JSON.stringify(o(0)));for(const[d,c]of[[0,"0"],[9,"0.28125rem"],[16,"0.5rem"],[40,"1.25rem"]])n(`§3 ${d}px publishes ${l}: ${c}`,o(d)[l]===c,`a button is half the card, which is what inputs and panels beside it read — got: ${String(o(d)[l])}`);const[t,s]=e.radiusRange??[Number.NaN,Number.NaN];n(`§3 both ends of the track (${t}, ${s}) publish a button radius`,e.radiusRange!==void 0&&o(t)[l]!==void 0&&o(s)[l]!==void 0);const r=[null,0,9,40].map(d=>o(d)[l]??"0");n("§4 not-set and square both DRAW a square button",r[0]==="0"&&r[1]==="0",`they differ in what they publish, not in today's picture — got: ${JSON.stringify(r)}`),n("§4 9 and 40 draw two different NON-square buttons",r[2]!=="0"&&r[3]!=="0"&&r[2]!==r[3],`a setting that names buttons and moves none of them is this card's defect restored — got: ${JSON.stringify(r)}`);const a="--radius-full";for(const d of[0,9,16,40])n(`§5 ${d}px does not publish the fully-round token`,!(a in o(d)),"swatches, toggle knobs and status dots are a SHAPE, not a corner")}{const t=[];for(const s of e.callers){const r=h(s);r&&t.push(...g(r))}n(`§6 no caller hands \`Btn\` a \`rounded-*\` class (${e.callers.length} callers)`,t.length===0,`\`.sf-button\` is unlayered and would silently beat it; give the caller its own property instead — ${t.join(" | ")}`)}{const t=i.filter(a=>a.path.endsWith(".tsx")&&b(a.text)).map(a=>a.path).sort();n(`§7 the tree sweep found ${t.length} file(s) importing Btn (at least ${e.callerFloor})`,t.length>=e.callerFloor&&t.length>0,`too few to be real, so §7 is measuring nothing — ${JSON.stringify(t)}`);const s=t.filter(a=>!e.callers.includes(a));n("§7 the callers list names every file that imports `Btn`",s.length===0,`${JSON.stringify(s)} import(s) Btn and ${s.length===1?"is":"are"} not listed — a caller missing from it is a caller §6 never looks at. Add the path — the list is kept by hand on purpose, so a new caller is a visible line in a diff.`);const r=e.callers.filter(a=>!t.includes(a));n("§7 the callers list names no file that stopped importing `Btn`",r.length===0,`${JSON.stringify(r)} ${r.length===1?"is":"are"} listed and no longer import Btn — a dead row is a sweep over nothing. Drop the path, or say in a comment why the file is still worth sweeping.`)}return u()}export{y as buttonCorners};
1
+ import{ruleChecks as p}from"./rule.js";import{builderModuleSpec as g}from"./support/ts-source.js";const l="--sf-button-radius",b="sf-button",$=i=>i.replace(/\/\*[\s\S]*?\*\//g,t=>" ".repeat(t.length)),w=new RegExp(`^import\\s+Btn\\s+from\\s+["'](?:${g("components/Btn")}|\\.{1,2}\\/(?:[\\w-]+\\/)*Btn)["'];?$`,"m"),c=i=>w.test(i);function m(i){const t=[],n=/<Btn\b[^>]*className=(?:"([^"]*)"|\{`([^`]*)`\})/g;let h;for(;(h=n.exec(i.text))!==null;){const u=h[1]??h[2]??"";/\brounded-/.test(u)&&t.push(`${i.path}: ${u}`)}return t}function x(i,t){const{check:n,report:h}=p("button corners"),u=e=>i.find(s=>s.path===e);{const e={path:"fixture.tsx",text:['import Btn from "@/components/Btn";','<Btn href="/x" className="mt-4 rounded-lg">Go</Btn>',"<Btn className={`mt-2`}>Go</Btn>"].join(`
2
+ `)};n("§6/§7 the readers find a Btn import and a rounded-* className handed to it",c(e.text)&&m(e).length===1),n("§7 …and a host's import of it by package name and side (efficient-web-builder 24)",c('import Btn from "efficient-web-builder/render/components/Btn";')&&!c('import Btn from "efficient-web-builder/render/components/BtnGroup";'))}if(t.stylesheet!==void 0){const e=u(t.stylesheet);if(n(`§1 read the stylesheet ${t.stylesheet}`,!!e)){const s=/\.sf-button\s*\{([^}]*)\}/.exec($(e.text));n(`§1 ${t.stylesheet} carries a \`.sf-button\` rule`,s!==null,"without it the class is decoration and the setting reaches no button"),s&&(n(`§1 the .sf-button rule reads \`var(${l}, 0)\``,/border-radius:\s*var\(--sf-button-radius,\s*0\)/.test(s[1]),`the property for the setting, and the \`0\` for every site that never chose one — got: ${s[1].trim()}`),n("§1 the .sf-button rule does not clip",!/overflow/.test(s[1]),`the trailing arrow translates 3px right on hover and would be shaved — got: ${s[1].trim()}`))}}if(t.button!==void 0){const e=u(t.button);n(`§2 read the shared button ${t.button}`,!!e)&&n(`§2 ${t.button} writes no \`rounded-*\` utility`,!/\brounded-(?!full\b)[a-z0-9[]/.test(e.text.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm,"")),"every one carries a built-in value, so an untouched site would jump from square to a radius the day it shipped");const s=t.rendered??[];n(`§2 the button was rendered (${s.length} shapes, at least 4)`,s.length>=4,"both variants as a link and as a button");for(const[r,a]of s)n(`§2 ${r}: the rendered button carries \`${b}\``,new RegExp(`class="${b}\\b`).test(a),`it is the element's only ear for Corners — got: ${a.slice(0,140)}`)}const o=t.cornerRadiusTokens;if(o){n("§3 a site that never chose a corner publishes NO button radius",!(l in o(null)),`that absence is what leaves it square, byte for byte as before — got: ${JSON.stringify(o(null))}`),n("§3 `0` is an ANSWER, not an absence: it publishes a flattened radius",o(0)[l]==="0",JSON.stringify(o(0)));for(const[d,f]of[[0,"0"],[9,"0.28125rem"],[16,"0.5rem"],[40,"1.25rem"]])n(`§3 ${d}px publishes ${l}: ${f}`,o(d)[l]===f,`a button is half the card, which is what inputs and panels beside it read — got: ${String(o(d)[l])}`);const[e,s]=t.radiusRange??[Number.NaN,Number.NaN];n(`§3 both ends of the track (${e}, ${s}) publish a button radius`,t.radiusRange!==void 0&&o(e)[l]!==void 0&&o(s)[l]!==void 0);const r=[null,0,9,40].map(d=>o(d)[l]??"0");n("§4 not-set and square both DRAW a square button",r[0]==="0"&&r[1]==="0",`they differ in what they publish, not in today's picture — got: ${JSON.stringify(r)}`),n("§4 9 and 40 draw two different NON-square buttons",r[2]!=="0"&&r[3]!=="0"&&r[2]!==r[3],`a setting that names buttons and moves none of them is this card's defect restored — got: ${JSON.stringify(r)}`);const a="--radius-full";for(const d of[0,9,16,40])n(`§5 ${d}px does not publish the fully-round token`,!(a in o(d)),"swatches, toggle knobs and status dots are a SHAPE, not a corner")}{const e=[];for(const s of t.callers){const r=u(s);r&&e.push(...m(r))}n(`§6 no caller hands \`Btn\` a \`rounded-*\` class (${t.callers.length} callers)`,e.length===0,`\`.sf-button\` is unlayered and would silently beat it; give the caller its own property instead — ${e.join(" | ")}`)}{const e=i.filter(a=>a.path.endsWith(".tsx")&&c(a.text)).map(a=>a.path).sort();n(`§7 the tree sweep found ${e.length} file(s) importing Btn (at least ${t.callerFloor})`,e.length>=t.callerFloor&&e.length>0,`too few to be real, so §7 is measuring nothing — ${JSON.stringify(e)}`);const s=e.filter(a=>!t.callers.includes(a));n("§7 the callers list names every file that imports `Btn`",s.length===0,`${JSON.stringify(s)} import(s) Btn and ${s.length===1?"is":"are"} not listed — a caller missing from it is a caller §6 never looks at. Add the path — the list is kept by hand on purpose, so a new caller is a visible line in a diff.`);const r=t.callers.filter(a=>!e.includes(a));n("§7 the callers list names no file that stopped importing `Btn`",r.length===0,`${JSON.stringify(r)} ${r.length===1?"is":"are"} listed and no longer import Btn — a dead row is a sweep over nothing. Drop the path, or say in a comment why the file is still worth sweeping.`)}return h()}export{x as buttonCorners};
@@ -1,22 +1,23 @@
1
- import{ruleChecks as v}from"./rule.js";import{isRegistryPath as F,registryFiles as D}from"./support/registry-source.js";const L="lib/puck/controls.tsx",I={},S={},E=["boxShareField","edgePad","placementField","CoordsField","textList","segmented","select","slider"],P=/sf-field-label|<Label>/,N=/<[A-Z][A-Za-z0-9]*Icon\s*\/>/,B=s=>s.startsWith("//")||s.startsWith("*")||s.startsWith("/*");function A(s,o){const e=s.indexOf("(",o);if(e===-1)return"";let g=0;const a=[];let f=e+1;for(let d=e;d<s.length;d+=1){const r=s[d];if(r==="("||r==="["||r==="{")g+=1;else if(r===")"||r==="]"||r==="}"){if(g-=1,g===0){a.push(s.slice(f,d));break}}else if(r===","&&g===1&&(a.push(s.slice(f,d)),f=d+1,a.length===2))break}return a[1]??""}function W(s,o){const e=new RegExp(`(?:^|[^.\\w])${o}\\s*(?:<[^()]*?>)?\\s*\\(|\\bcontrols\\.${o}\\s*(?:<[^()]*?>)?\\s*\\(`).exec(s);return e?s.indexOf("(",e.index):s.indexOf("(")}function _(s,o){const e=s.slice(0,o);return e.includes("//")?!1:(e.match(/"/g)??[]).length%2===0&&(e.match(/`/g)??[]).length%2===0&&(e.match(/'/g)??[]).length%2===0}const T=(s,o)=>new RegExp(`import \\{[^}]*\\b${o}\\b[^}]*\\} from "@/lib/puck/controls"`).test(s)||/import \* as controls from "@\/lib\/puck\/controls"/.test(s);function G(s,o,e){const g=new RegExp(`(^|[^.\\w])${o}\\s*(<[^()]*?>)?\\s*\\(|\\bcontrols\\.${o}\\s*(<[^()]*?>)?\\s*\\(`,"g"),a=new RegExp(`^(export )?function ${o}(<[^>]*>)?\\(`),f=[];let d=0;for(const r of s){const p=r.text.split(`
2
- `),x=p.join(`
3
- `);p.forEach((b,h)=>{const m=b.trim();if(B(m)||a.test(m))return;g.lastIndex=0;const $=g.exec(b);if(!$||!_(b,$.index))return;d+=1;const t=A(x,p.slice(0,h).join(`
4
- `).length+W(b,o));N.test(t)||/<FieldGlyph\b/.test(t)||e.some(n=>n.test(t))||f.push(`${r.path}:${h+1} ${m.slice(0,80)}`)})}return{bare:f,total:d}}function U(s){const o={};for(const e of s){const g=e.text.split(`
5
- `);g.forEach((a,f)=>{if(!/className="sf-field"/.test(a))return;const d=g.slice(f,f+9).join(`
6
- `);if(!P.test(d))return;const r=d.match(/sf-field-label[^>]*>([^<{][^<]*)</),p=d.match(/sf-field-label[^>]*>\{([^}]*)\}/),x=d.match(/<Label>([^<{][^<]*)</),b=d.match(/<Label>\{([^}]*)\}/),h=$=>$.trim()==="label"?"{label}":"{...}",m=r?r[1].trim():p?h(p[1]):x?x[1].trim():b?h(b[1]):"{...}";(o[e.path]??=[]).push(m)})}return o}function M(s){const o=[];let e=0;for(const g of s){const a=g.text.split(`
7
- `);a.forEach((f,d)=>{if(!/<SettingsField\b/.test(f))return;const r=[];let p=!1;for(let b=d;b<Math.min(d+40,a.length)&&!p;b+=1){let h=a[b];if(h=h.replace(/\/\*[\s\S]*?\*\//g," "),/\/\*/.test(h)&&(h=h.slice(0,h.indexOf("/*"))),/^\s*\*/.test(a[b]))continue;const m=h.indexOf("//");m!==-1&&(h=h.slice(0,m)),r.push(h),h.includes(">")&&(p=!0)}const x=r.join(`
8
- `);/\blabel=/.test(x)&&(e+=1,!/\bicon=/.test(x)&&o.push(`${g.path}:${d+1} ${f.trim().slice(0,80)}`))})}return{seen:e,bare:o}}function K(s,o){const{check:e,report:g}=v("panel rows use the shared row"),a=o.exceptions??[],{floors:f}=o;{const t={path:"fixture.tsx",text:['import { slider, textList } from "@/lib/puck/controls";','const a = { textList: () => controls.textList("Items", <ListIcon />, x) };','const b = slider("Thickness", 0, 10);','const c = slider("Gap", <GapIcon />, 0, 10);','// slider("In a comment", 0, 10) is not a row'].join(`
9
- `)},n=G([t],"textList",[]),c=G([t],"slider",[]);e("the reader reads a namespaced call's own arguments, not the arrow's",n.total===1&&n.bare.length===0,JSON.stringify(n)),e("the reader flags a bare row whose neighbour has a glyph, and skips a comment",c.total===2&&c.bare.length===1&&c.bare[0].startsWith("fixture.tsx:3"),JSON.stringify(c))}const r=new Map(s.map(t=>[t.path,t])).get(L);e(o.writesBuilders?`read ${L}, where the builders are declared`:`${L} is not among this repository's files (it calls the builders, it does not declare them)`,!!r===o.writesBuilders);const p=D(s);e(`read this repository's share of the block registry (${p.length} files)`,p.length>0);const x=s.filter(t=>/^components\/[^/]+\.tsx$/.test(t.path)),b=s.filter(t=>/^scripts\/[^/]+\.tsx$/.test(t.path)),h=[...p,...r?[r]:[],...x],m=t=>t.path===L;{const t=h.filter(l=>m(l)||/import \{[^}]*\btoggle\b[^}]*\} from "@\/lib\/puck\/controls"/.test(l.text)),n=t.filter(l=>m(l)||F(l.path));e("only the known files use the toggle builder",t.length===n.length&&(!o.writesBuilders||n.some(m))&&n.some(l=>F(l.path)),t.map(l=>l.path).join(" · "));const c=[];let u=0;for(const l of n)l.text.split(`
10
- `).forEach((w,i)=>{const y=w.trim();B(y)||/classList\.toggle/.test(w)||/^export function toggle\(/.test(y)||/(^|[^.\w])toggle\(/.test(w)&&(u+=1,!(N.test(w)||/<FieldGlyph\b/.test(w))&&c.push(`${l.path}:${i+1} ${y.slice(0,80)}`))});e("every toggle() row carries a glyph",c.length===0,`
11
- ${c.join(`
12
- `)}`),e(`…and there are rows to check (${u} toggle() call sites, at least ${f.toggle})`,u>=f.toggle,"the scan is reading nothing")}const $=[{builder:"textList",inControls:!1,accept:[/TICKER_ITEMS_ROW\.icon/],name:"every textList() row carries a glyph"},{builder:"segmented",inControls:!0,accept:[/\bnoGlyph\(/],name:"every segmented() row carries a glyph (or a ruled noGlyph)"},{builder:"select",inControls:!0,accept:[/\btextKindGlyph\(/,/\bnoGlyph\(/],name:"every select() row carries a glyph (or a ruled noGlyph)"},{builder:"slider",inControls:!0,accept:[/\bnoGlyph\(/],name:"every slider() row carries a glyph"}];for(const t of $){const n=[...p,...t.inControls&&r?[r]:[],...b].filter(l=>t.inControls&&m(l)||F(l.path)||T(l.text,t.builder)),{bare:c,total:u}=G(n,t.builder,t.accept);e(t.name,c.length===0,`
13
- ${c.join(`
14
- `)}`),e(`…and the ${t.builder} scan actually read rows (${u} call sites in ${n.length} files, at least ${f[t.builder]})`,u>=f[t.builder])}{const t=U(h);for(const i of a)e(`the exception still names a real row — ${i.label}`,(t[i.file]??[]).includes(i.label),`${i.file} no longer draws it`);const n={};for(const[i,y]of Object.entries(t)){const k=y.filter(O=>!a.some(j=>j.file===i&&j.label===O));k.length&&(n[i]=k)}const c=i=>i.slice().sort(),u=[];for(const[i,y]of Object.entries(n)){const k=I[i];if(!k){u.push(`${i} — hand-rolls ${y.length} row(s) and is not in PENDING`);continue}const O=c(y),j=c(k);(O.length!==j.length||O.some((C,R)=>C!==j[R]))&&u.push(`${i} — found [${O.join(" · ")}] but PENDING says [${j.join(" · ")}]`)}e("no NEW hand-rolled settings row",u.length===0,`
15
- ${u.join(`
16
- `)}`);const l=Object.keys(I).filter(i=>!n[i]);e("no PENDING entry has outlived its subject",l.length===0,l.join(" · "));const w=Object.values(n).reduce((i,y)=>i+y.length,0);e("the hand-rolled rows are GONE — the count is zero",w===0,`${w}`),e("…and no file is left hand-rolling one",Object.keys(I).length===0,Object.keys(I).join(" · ")),e(`…and the rulings are exactly ${o.rulings} (a new one is argued in review, not added)`,a.length===o.rulings&&a.every(i=>i.why.trim().length>40),`${a.length}`),e("…and BlogSource is not among them, because its row is gone",!a.some(i=>i.file.endsWith("BlogSource.tsx")))}{const t=Object.values(S).reduce((n,c)=>n+c,0);if(e("every builder is finished — nothing left to count",t===0,`${t}`),e("…and the emptiness is because FOUR builders are done, not because the list was cleared",E.length===8&&["textList","segmented","select","slider"].every(n=>E.includes(n)),E.join(" · ")),e("…and a finished builder has left the list",E.every(n=>!(n in S)),E.join(" · ")),r){const n=Object.keys(S).filter(c=>!new RegExp(`(export )?function ${c}\\(`).test(r.text));e(`…and every builder it names is really declared in ${L}`,n.length===0,n.join(" · "))}}if(r){const t=r.text,n=t.indexOf("export function backgroundField("),c=n===-1?"":t.slice(n),u=c.slice(0,c.indexOf(`
1
+ import{ruleChecks as P}from"./rule.js";import{builderModuleSpec as W}from"./support/ts-source.js";import{isRegistryPath as F,registryFiles as A}from"./support/registry-source.js";const L="lib/puck/controls.tsx",k={},N={},E=["boxShareField","edgePad","placementField","CoordsField","textList","segmented","select","slider"],T=/sf-field-label|<Label>/,R=/<[A-Z][A-Za-z0-9]*Icon\s*\/>/,C=s=>s.startsWith("//")||s.startsWith("*")||s.startsWith("/*");function _(s,o){const e=s.indexOf("(",o);if(e===-1)return"";let u=0;const c=[];let g=e+1;for(let d=e;d<s.length;d+=1){const i=s[d];if(i==="("||i==="["||i==="{")u+=1;else if(i===")"||i==="]"||i==="}"){if(u-=1,u===0){c.push(s.slice(g,d));break}}else if(i===","&&u===1&&(c.push(s.slice(g,d)),g=d+1,c.length===2))break}return c[1]??""}function z(s,o){const e=new RegExp(`(?:^|[^.\\w])${o}\\s*(?:<[^()]*?>)?\\s*\\(|\\bcontrols\\.${o}\\s*(?:<[^()]*?>)?\\s*\\(`).exec(s);return e?e.index+e[0].length-1:s.indexOf("(")}function M(s,o){const e=s.slice(0,o);return e.includes("//")?!1:(e.match(/"/g)??[]).length%2===0&&(e.match(/`/g)??[]).length%2===0&&(e.match(/'/g)??[]).length%2===0}const G=W("lib/puck/controls"),B=(s,o)=>new RegExp(`import \\{[^}]*\\b${o}\\b[^}]*\\} from "${G}"`).test(s)||new RegExp(`import \\* as controls from "${G}"`).test(s);function I(s,o,e){const u=new RegExp(`(^|[^.\\w])${o}\\s*(<[^()]*?>)?\\s*\\(|\\bcontrols\\.${o}\\s*(<[^()]*?>)?\\s*\\(`,"g"),c=new RegExp(`^(export )?function ${o}(<[^>]*>)?\\(`),g=[];let d=0;for(const i of s){const p=i.text.split(`
2
+ `),w=p.join(`
3
+ `);p.forEach((b,h)=>{const m=b.trim();if(C(m)||c.test(m))return;u.lastIndex=0;const $=u.exec(b);if(!$||!M(b,$.index))return;d+=1;const t=_(w,p.slice(0,h).join(`
4
+ `).length+z(b,o));R.test(t)||/<FieldGlyph\b/.test(t)||e.some(n=>n.test(t))||g.push(`${i.path}:${h+1} ${m.slice(0,80)}`)})}return{bare:g,total:d}}function U(s){const o={};for(const e of s){const u=e.text.split(`
5
+ `);u.forEach((c,g)=>{if(!/className="sf-field"/.test(c))return;const d=u.slice(g,g+9).join(`
6
+ `);if(!T.test(d))return;const i=d.match(/sf-field-label[^>]*>([^<{][^<]*)</),p=d.match(/sf-field-label[^>]*>\{([^}]*)\}/),w=d.match(/<Label>([^<{][^<]*)</),b=d.match(/<Label>\{([^}]*)\}/),h=$=>$.trim()==="label"?"{label}":"{...}",m=i?i[1].trim():p?h(p[1]):w?w[1].trim():b?h(b[1]):"{...}";(o[e.path]??=[]).push(m)})}return o}function J(s){const o=[];let e=0;for(const u of s){const c=u.text.split(`
7
+ `);c.forEach((g,d)=>{if(!/<SettingsField\b/.test(g))return;const i=[];let p=!1;for(let b=d;b<Math.min(d+40,c.length)&&!p;b+=1){let h=c[b];if(h=h.replace(/\/\*[\s\S]*?\*\//g," "),/\/\*/.test(h)&&(h=h.slice(0,h.indexOf("/*"))),/^\s*\*/.test(c[b]))continue;const m=h.indexOf("//");m!==-1&&(h=h.slice(0,m)),i.push(h),h.includes(">")&&(p=!0)}const w=i.join(`
8
+ `);/\blabel=/.test(w)&&(e+=1,!/\bicon=/.test(w)&&o.push(`${u.path}:${d+1} ${g.trim().slice(0,80)}`))})}return{seen:e,bare:o}}function H(s,o){const{check:e,report:u}=P("panel rows use the shared row"),c=o.exceptions??[],{floors:g}=o;{const t={path:"fixture.tsx",text:['import { slider, textList } from "@/lib/puck/controls";','const a = { textList: () => controls.textList("Items", <ListIcon />, x) };','const b = slider("Thickness", 0, 10);','const c = slider("Gap", <GapIcon />, 0, 10);','// slider("In a comment", 0, 10) is not a row'].join(`
9
+ `)},n=I([t],"textList",[]),a=I([t],"slider",[]);e("the reader reads a namespaced call's own arguments, not the arrow's",n.total===1&&n.bare.length===0,JSON.stringify(n)),e("the reader flags a bare row whose neighbour has a glyph, and skips a comment",a.total===2&&a.bare.length===1&&a.bare[0].startsWith("fixture.tsx:3"),JSON.stringify(a));const f={path:"wrapped.tsx",text:['const a = { shape: withShowWhen(segmented("Shape", <SquareIcon />, opts), rule) };','const b = { size: withShowWhen(segmented("Size", opts), rule) };'].join(`
10
+ `)},r=I([f],"segmented",[]);e("the reader reads a wrapped call's own arguments: the glyphed row passes, the bare one is caught",r.total===2&&r.bare.length===1&&r.bare[0].startsWith("wrapped.tsx:2"),JSON.stringify(r))}const i=new Map(s.map(t=>[t.path,t])).get(L);e(o.writesBuilders?`read ${L}, where the builders are declared`:`${L} is not among this repository's files (it calls the builders, it does not declare them)`,!!i===o.writesBuilders);const p=A(s);e(`read this repository's share of the block registry (${p.length} files)`,p.length>0);const w=s.filter(t=>/^components\/[^/]+\.tsx$/.test(t.path)),b=s.filter(t=>/^scripts\/[^/]+\.tsx$/.test(t.path)),h=[...p,...i?[i]:[],...w],m=t=>t.path===L;{const t=h.filter(r=>m(r)||B(r.text,"toggle")),n=t.filter(r=>m(r)||F(r.path));e("only the known files use the toggle builder",t.length===n.length&&(!o.writesBuilders||n.some(m))&&n.some(r=>F(r.path)),t.map(r=>r.path).join(" · "));const a=[];let f=0;for(const r of n)r.text.split(`
11
+ `).forEach((x,l)=>{const y=x.trim();C(y)||/classList\.toggle/.test(x)||/^export function toggle\(/.test(y)||/(^|[^.\w])toggle\(/.test(x)&&(f+=1,!(R.test(x)||/<FieldGlyph\b/.test(x))&&a.push(`${r.path}:${l+1} ${y.slice(0,80)}`))});e("every toggle() row carries a glyph",a.length===0,`
12
+ ${a.join(`
13
+ `)}`),e(`…and there are rows to check (${f} toggle() call sites, at least ${g.toggle})`,f>=g.toggle,"the scan is reading nothing")}const $=[{builder:"textList",inControls:!1,accept:[/TICKER_ITEMS_ROW\.icon/],name:"every textList() row carries a glyph"},{builder:"segmented",inControls:!0,accept:[/\bnoGlyph\(/],name:"every segmented() row carries a glyph (or a ruled noGlyph)"},{builder:"select",inControls:!0,accept:[/\btextKindGlyph\(/,/\bnoGlyph\(/],name:"every select() row carries a glyph (or a ruled noGlyph)"},{builder:"slider",inControls:!0,accept:[/\bnoGlyph\(/],name:"every slider() row carries a glyph"}];for(const t of $){const n=[...p,...t.inControls&&i?[i]:[],...b].filter(r=>t.inControls&&m(r)||F(r.path)||B(r.text,t.builder)),{bare:a,total:f}=I(n,t.builder,t.accept);e(t.name,a.length===0,`
14
+ ${a.join(`
15
+ `)}`),e(`…and the ${t.builder} scan actually read rows (${f} call sites in ${n.length} files, at least ${g[t.builder]})`,f>=g[t.builder])}{const t=U(h);for(const l of c)e(`the exception still names a real row — ${l.label}`,(t[l.file]??[]).includes(l.label),`${l.file} no longer draws it`);const n={};for(const[l,y]of Object.entries(t)){const O=y.filter(S=>!c.some(j=>j.file===l&&j.label===S));O.length&&(n[l]=O)}const a=l=>l.slice().sort(),f=[];for(const[l,y]of Object.entries(n)){const O=k[l];if(!O){f.push(`${l} — hand-rolls ${y.length} row(s) and is not in PENDING`);continue}const S=a(y),j=a(O);(S.length!==j.length||S.some((v,D)=>v!==j[D]))&&f.push(`${l} — found [${S.join(" · ")}] but PENDING says [${j.join(" · ")}]`)}e("no NEW hand-rolled settings row",f.length===0,`
16
+ ${f.join(`
17
+ `)}`);const r=Object.keys(k).filter(l=>!n[l]);e("no PENDING entry has outlived its subject",r.length===0,r.join(" · "));const x=Object.values(n).reduce((l,y)=>l+y.length,0);e("the hand-rolled rows are GONE — the count is zero",x===0,`${x}`),e("…and no file is left hand-rolling one",Object.keys(k).length===0,Object.keys(k).join(" · ")),e(`…and the rulings are exactly ${o.rulings} (a new one is argued in review, not added)`,c.length===o.rulings&&c.every(l=>l.why.trim().length>40),`${c.length}`),e("…and BlogSource is not among them, because its row is gone",!c.some(l=>l.file.endsWith("BlogSource.tsx")))}{const t=Object.values(N).reduce((n,a)=>n+a,0);if(e("every builder is finished — nothing left to count",t===0,`${t}`),e("…and the emptiness is because FOUR builders are done, not because the list was cleared",E.length===8&&["textList","segmented","select","slider"].every(n=>E.includes(n)),E.join(" · ")),e("…and a finished builder has left the list",E.every(n=>!(n in N)),E.join(" · ")),i){const n=Object.keys(N).filter(a=>!new RegExp(`(export )?function ${a}\\(`).test(i.text));e(`…and every builder it names is really declared in ${L}`,n.length===0,n.join(" · "))}}if(i){const t=i.text,n=t.indexOf("export function backgroundField("),a=n===-1?"":t.slice(n),f=a.slice(0,a.indexOf(`
17
18
  }
18
- `));e("backgroundField is declared",n!==-1),e("backgroundField renders SettingsField",/<SettingsField/.test(u)),e("…and hand-rolls no row of its own",u.length>0&&!/className="sf-field"/.test(u)),e("…and passes `BackgroundIcon`, not merely some glyph",/icon=\{<BackgroundIcon \/>\}/.test(u));const l=t.indexOf("export function cardImageBackgroundField("),w=l===-1?"":t.slice(l);e("…and cardImageBackgroundField still passes `GroundIcon`",/icon=\{<GroundIcon \/>\}/.test(w.slice(0,w.indexOf(`
19
+ `));e("backgroundField is declared",n!==-1),e("backgroundField renders SettingsField",/<SettingsField/.test(f)),e("…and hand-rolls no row of its own",f.length>0&&!/className="sf-field"/.test(f)),e("…and passes `BackgroundIcon`, not merely some glyph",/icon=\{<BackgroundIcon \/>\}/.test(f));const r=t.indexOf("export function cardImageBackgroundField("),x=r===-1?"":t.slice(r);e("…and cardImageBackgroundField still passes `GroundIcon`",/icon=\{<GroundIcon \/>\}/.test(x.slice(0,x.indexOf(`
19
20
  }
20
- `))))}{const{seen:t,bare:n}=M(x);e(`the hand-rolled row scan read something (${t} hand-drawn SettingsField rows across ${x.length} files, at least ${f.handDrawn})`,t>=f.handDrawn),e("no hand-rolled SettingsField row goes without a glyph",n.length===0,`
21
+ `))))}{const{seen:t,bare:n}=J(w);e(`the hand-rolled row scan read something (${t} hand-drawn SettingsField rows across ${w.length} files, at least ${g.handDrawn})`,t>=g.handDrawn),e("no hand-rolled SettingsField row goes without a glyph",n.length===0,`
21
22
  ${n.join(`
22
- `)}`)}return g()}export{L as CONTROLS_FILE,W as callParen,K as panelRowsUseTheSharedRow,A as secondArg};
23
+ `)}`)}return u()}export{L as CONTROLS_FILE,z as callParen,H as panelRowsUseTheSharedRow,_ as secondArg};
@@ -1,8 +1,8 @@
1
- import{ruleChecks as P}from"./rule.js";const o=/<(Render|Puck|SiteEditorProvider|EditorStoreProvider|ScreenStoreProvider)\b[^<>]*\bdata=\{/,m=/migrateRenamedProps\s*\(/,g=/export function migrateRenamedProps\b/;function S(l,r){const{check:t,report:p}=P("puck document surfaces"),a=e=>l.find(s=>s.path===e)?.text;{const e=n=>`<${n}`;t("a mount is found",o.test(`${e("Render")} config={c} data={doc} />`)),t("…across lines",o.test(`${e("SiteEditorProvider")}
1
+ import{ruleChecks as R}from"./rule.js";import{builderModuleSpec as S}from"./support/ts-source.js";const o=/<(Render|Puck|SiteEditorProvider|EditorStoreProvider|ScreenStoreProvider)\b[^<>]*\bdata=\{/,m=/migrateRenamedProps\s*\(/,p=/export function migrateRenamedProps\b/;function v(l,r){const{check:t,report:g}=R("puck document surfaces"),a=e=>l.find(s=>s.path===e)?.text;{const e=n=>`<${n}`;t("a mount is found",o.test(`${e("Render")} config={c} data={doc} />`)),t("…across lines",o.test(`${e("SiteEditorProvider")}
2
2
  config={c}
3
3
  data={initial}
4
- >`)),t("…and the screen's store provider is one under either of its names",["Editor","Screen"].every(n=>o.test(`${e(`${n}StoreProvider`)} config={c} data={doc}>`))),t("…and a tag named in prose is not one",!o.test(`the ${e("Puck")}> mounts, then its data={x} is read`)),t("…nor the composition API",!o.test(`${e("Puck.Preview")} />`));const s="migrateRenamedProps";t("a call is found",m.test(`const d = ${s}(stored);`)),t("…and a mention is not one",!m.test(`// runs ${s} first`)),t("the declaration is found, generic as it is",g.test(`export function ${s}<T>(data: T): T {`))}const d=l.filter(e=>/\.tsx?$/.test(e.path));t(`the source scan reached the app (${d.length} TS/TSX files, at least ${r.minFiles})`,d.length>=r.minFiles);const c=d.filter(e=>o.test(e.text)).map(e=>e.path).sort(),$=Object.keys(r.mounts).sort(),u=c.filter(e=>!(e in r.mounts)),f=$.filter(e=>!c.includes(e));t(`every mount is declared (${c.length} found) — a NEW one must say whether its document comes from stored props (migrate it) or is a mock (say so)`,u.length===0,u.map(e=>`UNDECLARED: ${e}`).join(`
4
+ >`)),t("…and the screen's store provider is one under either of its names",["Editor","Screen"].every(n=>o.test(`${e(`${n}StoreProvider`)} config={c} data={doc}>`))),t("…and a tag named in prose is not one",!o.test(`the ${e("Puck")}> mounts, then its data={x} is read`)),t("…nor the composition API",!o.test(`${e("Puck.Preview")} />`));const s="migrateRenamedProps";t("a call is found",m.test(`const d = ${s}(stored);`)),t("…and a mention is not one",!m.test(`// runs ${s} first`)),t("the declaration is found, generic as it is",p.test(`export function ${s}<T>(data: T): T {`))}const d=l.filter(e=>/\.tsx?$/.test(e.path));t(`the source scan reached the app (${d.length} TS/TSX files, at least ${r.minFiles})`,d.length>=r.minFiles);const c=d.filter(e=>o.test(e.text)).map(e=>e.path).sort(),$=Object.keys(r.mounts).sort(),u=c.filter(e=>!(e in r.mounts)),f=$.filter(e=>!c.includes(e));t(`every mount is declared (${c.length} found) — a NEW one must say whether its document comes from stored props (migrate it) or is a mock (say so)`,u.length===0,u.map(e=>`UNDECLARED: ${e}`).join(`
5
5
  `)),t("every declared mount is a real mount — a stale row would shelter a surface that later stops migrating",f.length===0,f.map(e=>`NOT FOUND: ${e}`).join(`
6
6
  `));const i=Object.entries(r.mounts).filter(([,e])=>e.migratedBy!==null),h=i.filter(([,e])=>!m.test(a(e.migratedBy)??""));if(t(`SURFACES=${i.length} — every mount fed from STORED props runs migrateRenamedProps, so a renamed prop key cannot silently reset a live section`,h.length===0,h.map(([e,s])=>`${e}
7
7
  → ${s.migratedBy} never calls migrateRenamedProps(`).join(`
8
- `)),r.handedMigrator){const{rules:e,entry:s}=r.handedMigrator,n=a(e)??"";t(`${e} carries the REAL migrateRenamedProps, not a stand-in`,/import \{ migrateRenamedProps \} from "@\/lib\/puck\/renamed-props";/.test(n)&&/^\s*migrateRenamedProps,$/m.test(n)),t(`…${s} passes those rules to the screen's mount`,/<(?:EditorScreenMount|ScreenMount)\b[^>]*addressing=\{WEBSITE_ADDRESSING\}/.test(a(s)??""))}return t(`at least ${r.storedFloor} mount(s) fed from stored props (found ${i.length})`,i.length>=r.storedFloor,"if this drops, a surface was reclassified as a mock rather than fixed"),r.migrator&&t(`${r.migrator} still exports migrateRenamedProps — the function §2 hunts calls of is live`,g.test(a(r.migrator)??"")),p()}export{S as puckDocumentSurfaces};
8
+ `)),r.handedMigrator){const{rules:e,entry:s}=r.handedMigrator,n=a(e)??"";t(`${e} carries the REAL migrateRenamedProps, not a stand-in`,new RegExp(`import \\{ migrateRenamedProps \\} from "${S("lib/puck/renamed-props")}";`).test(n)&&/^\s*migrateRenamedProps,$/m.test(n)),t(`…${s} passes those rules to the screen's mount`,/<(?:EditorScreenMount|ScreenMount)\b[^>]*addressing=\{WEBSITE_ADDRESSING\}/.test(a(s)??""))}return t(`at least ${r.storedFloor} mount(s) fed from stored props (found ${i.length})`,i.length>=r.storedFloor,"if this drops, a surface was reclassified as a mock rather than fixed"),r.migrator&&t(`${r.migrator} still exports migrateRenamedProps — the function §2 hunts calls of is live`,p.test(a(r.migrator)??"")),g()}export{v as puckDocumentSurfaces};
@@ -1,8 +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(`
1
+ import{ruleChecks as u}from"./rule.js";import{builderModuleSpec as w}from"./support/ts-source.js";const o="sf-theme-page",d=new RegExp(`["\`\\s]${o}`),g=new RegExp(`from\\s+"${w("components/ThemeCard")}"`),m=/ThemeColourRow\b(?!s)/;function f(r,n){const t=r.indexOf(`
2
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(`
3
+ }`,i)).replace(/\/\*[\s\S]*?\*\//g,"")}function $(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
4
  .m {
5
5
  /* not #eff6ff */
6
6
  background: var(--sf-accent-tint);
7
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};
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`,g.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{$ as sectionThemePreview};
@@ -1 +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};
1
+ import{ruleChecks as d}from"./rule.js";import{builderModuleSpec as u}from"./support/ts-source.js";const l=/sf-blogsource-status/,r=new RegExp(`from "${u("components/SourceStatus")}"`);function g(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 a host's, by package name and side (efficient-web-builder 24)",r.test('import SourceStatus from "efficient-web-builder/render/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",l.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 o=a.filter(e=>e.path!==t.home&&l.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",o.length===0,`${o.join(" · ")} — draw it with the builder's components/SourceStatus.tsx`),t.home){const e=a.find(n=>n.path===t.home);s(`…and ${t.home} really does draw it`,!!e&&l.test(e?.text??""))}const h=t.callers.filter(e=>!r.test(a.find(n=>n.path===e)?.text??""));s(`every named caller imports the shared block (${t.callers.length})`,t.callers.length>0&&h.length===0,t.callers.length===0?"no caller is named — this half measures nothing":h.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{g as sourceStatusIsShared};
@@ -1,2 +1,3 @@
1
1
  export declare function stripComments(source: string): string;
2
2
  export declare function balancedBlockAfter(source: string, marker: string): string;
3
+ export declare function builderModuleSpec(path: string): string;
@@ -1,3 +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};
1
+ const f={single:"'",double:'"',template:"`"};function r(i){let t="",n="code",o=0;for(;o<i.length;){const e=i[o],c=i[o+1];if(n==="code"){if(e==="/"&&c==="/"){n="line",t+=" ",o+=2;continue}if(e==="/"&&c==="*"){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==="*"&&c==="/"){n="code",t+=" ",o+=2;continue}t+=e===`
3
+ `?e:" ",o+=1;continue}if(e==="\\"){t+=e+(c??""),o+=2;continue}e===f[n]&&(n="code"),t+=e,o+=1}return t}function d(i,t){const n=r(i),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 c=0;for(let l=e;l<n.length;l+=1)if(n[l]==="{")c+=1;else if(n[l]==="}"&&(c-=1,c===0))return i.slice(e,l+1);throw new Error(`ts-source: unbalanced block after: ${t}`)}function s(i){return`(?:@\\/|efficient-web-builder\\/(?:render|format|editor)\\/)${i.replace(/[.*+?^${}()|[\]\\/]/g,"\\$&")}`}export{d as balancedBlockAfter,s as builderModuleSpec,r as stripComments};
@@ -1,2 +1,2 @@
1
- import{jsx as h}from"react/jsx-runtime";import m from"../../../../../components/ChartSection.js";import{phRows as d,phHide as r}from"../../scaffold.js";import{say as t}from"../../../../../../format/lib/editor-words.js";const o={eyebrow:"By the numbers",title:`A picture worth
2
- *a thousand rows.*`},y=()=>[{label:t("Before"),value:"9"},{label:t("After"),value:"3"},{label:t("With us"),value:"1"}],x={label:"Chart",defaultProps:{eyebrow:"",title:"",body:"",items:[],caption:"",chartStyle:"bars",valueSuffix:""},render:({eyebrow:a,title:i,body:l,items:s,caption:f,chartStyle:n,valueSuffix:b,puck:e})=>h(m,{eyebrow:r(a,o.eyebrow,e),title:r(i,o.title,e),body:l,items:d(s,y(),e),chartStyle:n,valueSuffix:b,caption:f,editing:e?.isEditing})};export{o as CHART_PH,x as ChartRender};
1
+ import{jsx as m}from"react/jsx-runtime";import d from"../../../../../components/ChartSection.js";import{phRows as y,phHide as o,hidableRow as t}from"../../scaffold.js";import{say as r}from"../../../../../../format/lib/editor-words.js";const a={eyebrow:"By the numbers",title:`A picture worth
2
+ *a thousand rows.*`},p=()=>[{label:r("Before"),value:"9"},{label:r("After"),value:"3"},{label:r("With us"),value:"1"}],S={label:"Chart",defaultProps:{eyebrow:"",title:"",body:"",items:[],caption:"",chartStyle:"bars",valueSuffix:""},render:({eyebrow:i,title:l,body:s,items:b,caption:f,chartStyle:h,valueSuffix:n,puck:e})=>m(d,{eyebrow:o(i,a.eyebrow,e),title:o(l,a.title,e),body:t(s),items:y(b,p(),e),chartStyle:h,valueSuffix:t(n),caption:t(f),editing:e?.isEditing})};export{a as CHART_PH,S as ChartRender};
@@ -1,2 +1,2 @@
1
- import{jsx as p}from"react/jsx-runtime";import d from"../../../../../components/IntegrationsSection.js";import{phHide as e}from"../../scaffold.js";const y={label:"Integrations Grid",defaultProps:{eyebrow:"",title:"",body:"",apps:[],statValue:"",statLabel:"",note:""},render:({eyebrow:o,title:a,body:r,apps:i,statValue:s,statLabel:n,note:l,puck:t})=>p(d,{eyebrow:e(o,"Integrations",t),title:e(a,`Plays well with
2
- *your favourite apps.*`,t),body:r,apps:i,statValue:s,statLabel:n,note:l,editing:t?.isEditing})};export{y as IntegrationsRender};
1
+ import{jsx as p}from"react/jsx-runtime";import b from"../../../../../components/IntegrationsSection.js";import{phHide as o,hidableRow as t}from"../../scaffold.js";const f={label:"Integrations Grid",defaultProps:{eyebrow:"",title:"",body:"",apps:[],statValue:"",statLabel:"",note:""},render:({eyebrow:a,title:i,body:r,apps:s,statValue:n,statLabel:l,note:d,puck:e})=>p(b,{eyebrow:o(a,"Integrations",e),title:o(i,`Plays well with
2
+ *your favourite apps.*`,e),body:t(r),apps:s,statValue:t(n),statLabel:t(l),note:t(d),editing:e?.isEditing})};export{f as IntegrationsRender};
@@ -1,2 +1,2 @@
1
- import{jsx as E}from"react/jsx-runtime";import I from"../../../../../components/MockupSection.js";import{phHide as t}from"../../scaffold.js";const q={label:"Mockup Card",defaultProps:{eyebrow:"",title:"",body:"",variant:"spec-card",cardHeader:"",badge:"",cardTitle:"",cardSubtitle:"",pills:[],tiles:[],rows:[],button:"",footnote:"",col1Title:"",col1Cards:[],col2Title:"",col2Cards:[],col3Title:"",col3Cards:[],col4Title:"",col4Cards:[],barsHeading:"",bars:[],termLines:[],urlText:"",heroTitle:"",heroButton:"",products:[],align:"right"},render:({eyebrow:o,title:r,body:l,variant:i,cardHeader:a,badge:d,cardTitle:c,cardSubtitle:s,pills:n,tiles:b,rows:u,button:T,footnote:p,col1Title:g,col1Cards:m,col2Title:C,col2Cards:h,col3Title:f,col3Cards:x,col4Title:H,col4Cards:w,barsHeading:y,bars:S,termLines:M,urlText:j,heroTitle:v,heroButton:B,products:L,align:k,puck:e})=>E(I,{eyebrow:t(o,"In the product",e),title:t(r,`Seen from
2
- *the counter.*`,e),body:l,variant:i,cardHeader:a,badge:d,cardTitle:c,cardSubtitle:s,pills:n,tiles:b,rows:u,button:T,footnote:p,col1Title:g,col1Cards:m,col2Title:C,col2Cards:h,col3Title:f,col3Cards:x,col4Title:H,col4Cards:w,barsHeading:y,bars:S,termLines:M,urlText:j,heroTitle:v,heroButton:B,products:L,align:k,editing:e?.isEditing})};export{q as MockupRender};
1
+ import{jsx as E}from"react/jsx-runtime";import I from"../../../../../components/MockupSection.js";import{phHide as o,hidableRow as e}from"../../scaffold.js";const z={label:"Mockup Card",defaultProps:{eyebrow:"",title:"",body:"",variant:"spec-card",cardHeader:"",badge:"",cardTitle:"",cardSubtitle:"",pills:[],tiles:[],rows:[],button:"",footnote:"",col1Title:"",col1Cards:[],col2Title:"",col2Cards:[],col3Title:"",col3Cards:[],col4Title:"",col4Cards:[],barsHeading:"",bars:[],termLines:[],urlText:"",heroTitle:"",heroButton:"",products:[],align:"right"},render:({eyebrow:r,title:l,body:i,variant:a,cardHeader:d,badge:c,cardTitle:s,cardSubtitle:n,pills:b,tiles:u,rows:T,button:p,footnote:g,col1Title:h,col1Cards:m,col2Title:C,col2Cards:f,col3Title:w,col3Cards:x,col4Title:H,col4Cards:y,barsHeading:S,bars:M,termLines:j,urlText:v,heroTitle:B,heroButton:L,products:R,align:k,puck:t})=>E(I,{eyebrow:o(r,"In the product",t),title:o(l,`Seen from
2
+ *the counter.*`,t),body:e(i),variant:a,cardHeader:e(d),badge:e(c),cardTitle:e(s),cardSubtitle:e(n),pills:b,tiles:u,rows:T,button:e(p),footnote:e(g),col1Title:e(h),col1Cards:m,col2Title:e(C),col2Cards:f,col3Title:e(w),col3Cards:x,col4Title:e(H),col4Cards:y,barsHeading:e(S),bars:M,termLines:j,urlText:e(v),heroTitle:e(B),heroButton:e(L),products:R,align:k,editing:t?.isEditing})};export{z as MockupRender};
@@ -11,6 +11,7 @@ export type HidableProp = {
11
11
  export declare function phHide(value: HidableProp, placeholder: string, puck?: {
12
12
  isEditing?: boolean;
13
13
  }): string | undefined;
14
+ export declare function hidableRow(value: HidableProp): string | undefined;
14
15
  export declare function phHideDefault(value: HidableProp, placeholder: string, puck?: {
15
16
  isEditing?: boolean;
16
17
  }): string | undefined;
@@ -1 +1 @@
1
- import{say as d}from"../../../../format/lib/editor-words.js";import{readHidable as i}from"efficient-easel/draw";import{CTA_PH as o}from"../../../../format/lib/puck/blocks/placeholders.js";function u(t,r,e){return t||(e?.isEditing?d(r):void 0)}function x(t,r,e){const n=Array.isArray(t)?t:[];return n.length?n:e?.isEditing?r:[]}function f(t,r,e){const n=i(t);if(!n.hidden)return u(n.text,r,e)}function s(t,r,e){return i(t).hidden?"":f(t,r,e)}function a(t,r,e){return{text:f(t,o.text,e),href:r||o.href}}export{a as ctaHide,f as phHide,s as phHideDefault,u as phPrev,x as phRows};
1
+ import{say as f}from"../../../../format/lib/editor-words.js";import{readHidable as i}from"efficient-easel/draw";import{CTA_PH as o}from"../../../../format/lib/puck/blocks/placeholders.js";function u(t,n,e){return t||(e?.isEditing?f(n):void 0)}function x(t,n,e){const r=Array.isArray(t)?t:[];return r.length?r:e?.isEditing?n:[]}function d(t,n,e){const r=i(t);if(!r.hidden)return u(r.text,n,e)}function s(t){if(t==null)return;const n=i(t);return n.hidden?"":n.text}function m(t,n,e){return i(t).hidden?"":d(t,n,e)}function a(t,n,e){return{text:d(t,o.text,e),href:n||o.href}}export{a as ctaHide,s as hidableRow,d as phHide,m as phHideDefault,u as phPrev,x as phRows};
@@ -1,10 +1,9 @@
1
+ import { type TextStyleCase } from "efficient-easel/draw";
1
2
  import { type FontRole } from "../../../format/lib/fonts.js";
2
3
  import type { PartEl } from "../../../editor/lib/puck/part-anchors.js";
3
4
  import { type PartNeedle } from "./text-parts.js";
4
5
  export declare const PART_STYLES_PROP = "_partStyles";
5
6
  export declare const PART_STYLED_ATTR = "data-sf-part-styled";
6
- export declare const PART_CASES: readonly ["none", "upper", "capitalize", "lower"];
7
- export type PartCase = (typeof PART_CASES)[number];
8
7
  export declare const PART_WEIGHTS: readonly [400, 500, 600, 700];
9
8
  export type PartWeight = (typeof PART_WEIGHTS)[number];
10
9
  export declare const PART_ALIGNS: readonly ["left", "center", "right", "justify"];
@@ -62,7 +61,7 @@ export type PartStyle = {
62
61
  lineHeight?: number;
63
62
  letterSpacing?: number;
64
63
  paragraphSpacing?: number;
65
- case?: PartCase;
64
+ case?: TextStyleCase;
66
65
  align?: PartAlign;
67
66
  padding?: number;
68
67
  width?: number;
@@ -77,7 +76,7 @@ export type PartStyleBase = {
77
76
  lineRatio: number;
78
77
  letterEm: number;
79
78
  align: PartAlign;
80
- case: PartCase;
79
+ case: TextStyleCase;
81
80
  paddingPx: number;
82
81
  widthPx: number;
83
82
  };
@@ -95,8 +94,8 @@ export declare function readPartStyle(props: Record<string, unknown> | undefined
95
94
  export declare function partStyleCount(props: Record<string, unknown> | undefined, partKey: string): number;
96
95
  export declare function propsWithPartStyleValue<K extends keyof PartStyle>(props: Record<string, unknown>, partKey: string, prop: K, value: PartStyle[K] | null): Record<string, unknown>;
97
96
  export declare function propsWithoutPartStyle(props: Record<string, unknown>, partKey: string): Record<string, unknown>;
98
- export declare function partCaseOfTransform(transform: string | null | undefined): PartCase;
99
- export declare function partCaseTransform(value: PartCase | undefined): string;
97
+ export declare function partCaseOfTransform(transform: string | null | undefined): TextStyleCase;
98
+ export declare function partCaseTransform(value: TextStyleCase | undefined): string;
100
99
  export declare function partSpacingOf(style: PartStyle): {
101
100
  lineHeight?: number;
102
101
  letterSpacingEm?: number;
@@ -1,2 +1,2 @@
1
- import{FONT_OPTIONS as V,FONT_ROLES as M,fontOption as Y,SIZE_STEP_OF_ROLE as Z}from"../../../format/lib/fonts.js";import{encodePartKey as q}from"../../../format/lib/puck/part-key.js";import{decodePartId as $,normalizePartText as b}from"./text-parts.js";import{COLOUR_TOKEN as H}from"../../../format/lib/word-styles.js";const E="_partStyles",v="data-sf-part-styled",J=["none","upper","capitalize","lower"],Q=[400,500,600,700],z=["left","center","right","justify"],F=5,L=1e3,tt=-5,et=10,nt=0,rt=64,ot=2,it=120,T=1280,st=10,j={size:{min:F,max:L,step:1},sizePhone:{min:F,max:L,step:1},padding:{min:nt,max:rt,step:ot},width:{min:it,max:T,step:st}},at=.05,ct=.01,lt=["line","letter"],ft=.5,ut=2.5,pt=-1,dt=1,ht=0,xt=400,O={none:"none",upper:"uppercase",capitalize:"capitalize",lower:"lowercase"};function m(t){return!t||typeof t!="object"||Array.isArray(t)?null:t}function k(t){return m(t?.[E])??{}}function W(t,e){const n=k(t);return Object.hasOwn(n,e)?m(n[e])??{}:{}}function Zt(t,e){return Object.values(W(t,e)).filter(n=>n!==void 0).length}function D(t,e,n){const r={...k(t)};if(Pt(n).length?r[e]=n:delete r[e],!Object.keys(r).length){const{[E]:o,...s}=t;return s}return{...t,[E]:r}}function Pt(t){return Object.entries(t).filter(([,e])=>e!==void 0)}function gt(t){return lt.includes(t)}const _=(t,e,n)=>Math.min(n,Math.max(e,t));function mt(t,e){if(gt(t)){const n=Math.round(Number(e));return!Number.isFinite(n)||n===0?null:_(n,tt,et)}if(t==="lineHeight"){const n=Number(e);return Number.isFinite(n)?P(_(n,ft,ut),3):null}if(t==="letterSpacing"){const n=Number(e);return Number.isFinite(n)?P(_(n,pt,dt),4):null}if(t==="paragraphSpacing"){const n=Math.round(Number(e));return!Number.isFinite(n)||n<=0?null:_(n,ht,xt)}if(Object.hasOwn(j,t)){const{min:n,max:r}=j[t],o=Math.round(Number(e));return Number.isFinite(o)?_(o,n,r):null}return t==="italic"?typeof e=="boolean"?e:null:t==="role"?M.includes(String(e))?e:null:e}const _t=["weight","size","sizePhone","line","letter","lineHeight","letterSpacing","paragraphSpacing","padding","width"];function C(t){let e=null;const n=r=>{e??={...t},delete e[r]};for(const r of _t){const o=t[r];o!==void 0&&!(typeof o=="number"&&Number.isFinite(o))&&n(r)}return t.align!==void 0&&!z.includes(t.align)&&n("align"),e??t}const St=[["font","role"],["line","lineHeight"],["letter","letterSpacing"]];function At(t){const e=[];for(const[n,r]of St)n===t?e.push(r):r===t&&e.push(n);return e}function qt(t,e,n,r){const o={...W(t,e)},s=r==null?null:mt(n,r);if(s===null)delete o[n];else{o[n]=s;for(const i of At(n))delete o[i]}return D(t,e,o)}function vt(t,e){return D(t,e,{})}function Tt(t){for(const e of J)if(O[e]===t)return e;return"none"}function Rt(t){return!t||!Object.hasOwn(O,t)?"":O[t]}const bt={heading:"--font-role-heading",heading2:"--font-role-heading2",heading3:"--font-role-heading3",body:"--font-role-body",accent:"--font-role-accent",label:"--font-role-label"};function Et(t){const e=t.role;return e&&M.includes(e)?e:null}const Ot={headingLarge:"min(clamp(64px,9vw,144px), var(--sf-heading-max,9999px))",headingMedium:"min(32px, var(--sf-heading-max,9999px))",headingSmall:"min(24px, var(--sf-heading-max,9999px))",accent:"32px",body:"14px",label:"12px"};function Nt(t){const e=Z[t],n=Ot[e];return n?`var(--size-${e}, ${n})`:`var(--size-${e})`}function wt(t){const e={};return typeof t.lineHeight=="number"?e.lineHeight=t.lineHeight:t.line&&(e.lineHeight=P(It+t.line*at,3)),typeof t.letterSpacing=="number"?e.letterSpacingEm=t.letterSpacing:t.letter&&(e.letterSpacingEm=P(t.letter*ct,4)),e}const It=1,S="--sf-part-para",yt="(max-width: 719.98px)";function K(t){return t=C(t),typeof t.sizePhone=="number"&&t.sizePhone>0?{fontSize:`${t.sizePhone}px`}:{}}function Mt(t,e){return e&&typeof t.sizePhone=="number"&&t.sizePhone>0?{...t,size:t.sizePhone}:t}function R(t,e){t=C(t);const n=Et(t),r=wt(t),o=t.size?`${t.size}px`:n?Nt(n):"";return{fontFamily:t.font?Ft(t.font):n?`var(${bt[n]})`:"",fontWeight:t.weight?String(t.weight):"",fontSize:o,minWidth:o?"0":"",overflowWrap:o?"break-word":"",lineHeight:r.lineHeight!==void 0?String(r.lineHeight):"",letterSpacing:r.letterSpacingEm!==void 0?`${r.letterSpacingEm}em`:"",[S]:t.paragraphSpacing?`${t.paragraphSpacing}px`:"",textTransform:Rt(t.case),textAlign:t.align??"",paddingTop:N(t.padding),paddingBottom:N(t.padding),maxWidth:N(t.width),color:$t(t.color),fontStyle:t.italic===!0?"italic":t.italic===!1?"normal":""}}const $t=t=>t===void 0||t===""?"":Object.hasOwn(H,t)?H[t]:G.test(t)?t:"",G=/^#[0-9a-fA-F]{6}$/;function Jt(t){return G.test(t)}const N=t=>t===void 0?"":`${t}px`,P=(t,e)=>Number(t.toFixed(e));function Qt(t,e){const n=Number.parseFloat(t.fontSize)||16,r=Number.parseFloat(t.lineHeight),o=Number.parseFloat(t.letterSpacing),s=Number.parseFloat(t.paddingTop),i=Number.parseFloat(t.maxWidth);return{fontKey:e,sizePx:n,weight:Ht(Number.parseInt(t.fontWeight,10)),lineRatio:Number.isFinite(r)?P(r/n,3):1.2,letterEm:Number.isFinite(o)?P(o/n,4):0,align:zt(t.textAlign),case:Tt(t.textTransform),paddingPx:Number.isFinite(s)?Math.round(s):0,widthPx:Number.isFinite(i)&&i<T?Math.round(i):T}}function Ht(t){return Number.isFinite(t)?Q.reduce((e,n)=>Math.abs(n-t)<Math.abs(e-t)?n:e):400}function zt(t){for(const e of z)if(e===t)return e;return t==="end"?"right":"left"}function te(t,e){const n=X(t);if(!n)return"";for(const r of V)if(X(e(r.cssVar))===n)return r.key;return""}const X=t=>(t.split(",")[0]??"").replace(/["']/g,"").replace(/\s+/g," ").trim().toLowerCase();function Ft(t){const e=Y(t);return e?`var(${e.cssVar})`:""}function ee(t,e,n,r){return!e||$(e)?.nodeId!==t?null:n.find(s=>r(s.getAttribute("class")))??n[0]??null}function ne(t,e,n=()=>A,r=i=>i,o=kt,s=!1){let i=0;for(const{element:a,partId:f}of t){const c=$(f)?.propKey;if(!c)continue;const l=r(c),h=Object.hasOwn(e,l)?m(e[l])??{}:{},p=R(Mt(h,s),A),u=I.some(x=>a.style[x]);let d=!1;for(const[x,g]of Object.entries(p))Wt(a,x,g),g&&(d=!0);if(u||I.some(x=>p[x]))for(const x of Lt(a,o))for(const g of I)x.style[g]=p[g]??"";d&&(i+=1)}return i}function Lt(t,e){const n=b(t.textContent??""),r=[];let o=t;for(let s=0;s<jt&&e(o);s+=1){const i=o.parentElement;if(!i||b(i.textContent??"")!==n)break;r.push(i),o=i}return r}const jt=8;function kt(t){const e=t;return typeof window>"u"||!e?.ownerDocument?!1:window.getComputedStyle(e).display==="inline"}const A={fontKey:"",sizePx:16,weight:400,lineRatio:1,letterEm:0,align:"left",case:"none",paddingPx:0,widthPx:T};function Wt(t,e,n){if(!e.startsWith("--")){t.style[e]=n;return}const r=t.style;typeof r.setProperty=="function"?n?r.setProperty(e,n):r.removeProperty?.(e):r[e]=n;const o=t;e===S&&(n?o.setAttribute?.(U,""):o.removeAttribute?.(U))}const U="data-sf-part-para",Dt=Object.keys(R({},A)),w=new Set(["color","overflowWrap",S,"fontStyle"]),I=Dt.filter(t=>!w.has(t));function re(t,e,n,r=o=>o){const o=new Map;for(const a of t){const f=q(a.key,a.row);for(const c of a.texts){const l=o.get(c);l?(l.keys.add(a.key),l.partKeys.add(f)):o.set(c,{keys:new Set([a.key]),partKeys:new Set([f])})}}const s=new Map;for(const a of Object.keys(e)){if(!Ct(a))continue;const f=m(e[a])??{},c=R(f,A),l=K(f);!Object.values(c).some(Boolean)&&!Object.keys(l).length||s.set(a,{stamp:B(n,a),declarations:c,...Object.keys(l).length?{phone:l}:{}})}const i=new Map;for(const[a,{keys:f,partKeys:c}]of o){const l=c.size===1?[...c][0]:f.size===1?[...f][0]:null;if(!l)continue;const h=r(l);if(!Object.hasOwn(e,h))continue;const p=m(e[h])??{},u=R(p,A),d=K(p);!Object.values(u).some(Boolean)&&!Object.keys(d).length||i.set(a,{stamp:B(n,l),declarations:u,...Object.keys(d).length?{phone:d}:{}})}return i.size||s.size?{rules:i,kindRules:s,drawn:new Set(o.keys())}:null}function Ct(t){return t.startsWith("@")}function oe(t,e){return t?.kindRules.get(e)??null}function B(t,e){const n=r=>r.replace(/[^A-Za-z0-9_-]+/g,"-");return`${n(t)}~${n(e)}`}function ie(t){const e=[],n=new Set,r=o=>{const{stamp:s,declarations:i}=o;if(n.has(s))return;n.add(s);const a=Object.entries(i).filter(([,u])=>u!==""),f=u=>u.map(([d,y])=>`${Gt(d)}: ${y}`).join("; "),c=`[${v}="${s}"]`,l=a.filter(([u])=>!w.has(u)),h=a.filter(([u])=>w.has(u));l.length&&e.push(`${c}, *:has(> ${c}) { ${f(l)} }`),h.length&&e.push(`${c} { ${f(h)} }`),i[S]&&e.push(...Kt(c));const p=Object.entries(o.phone??{}).filter(([,u])=>u!=="");p.length&&e.push(`@media ${yt} { ${c}, *:has(> ${c}) { ${f(p)} } }`)};for(const o of t.rules.values())r(o);for(const o of t.kindRules.values())r(o);return e.join(`
2
- `)}function Kt(t){const e=`var(${S})`,n=":is([data-sf-bullet], [data-sf-number])";return[`${t} br:not(br + br) { display: block; content: ""; margin-top: ${e} }`,`${t} ${n}:not(:first-child) { margin-top: ${e} }`,`${t} ${n}:not(:last-child) { margin-bottom: ${e} }`]}const Gt=t=>t.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`);function se(t,e){if(!t)return null;const n=b(e),r=t.rules.get(n);if(r)return r;if(n.length<Xt||t.drawn.has(n))return null;let o=null;for(const[s,i]of t.rules)if(s.includes(n)){if(o&&o.stamp!==i.stamp)return null;o=i}return o}const Xt=8;export{z as PART_ALIGNS,J as PART_CASES,dt as PART_LETTER_EM_MAX,pt as PART_LETTER_EM_MIN,ct as PART_LETTER_STEP,ut as PART_LINE_HEIGHT_MAX,ft as PART_LINE_HEIGHT_MIN,at as PART_LINE_STEP,j as PART_MEASURED_TRACKS,et as PART_NUDGE_MAX,tt as PART_NUDGE_MIN,rt as PART_PADDING_MAX,nt as PART_PADDING_MIN,ot as PART_PADDING_STEP,U as PART_PARAGRAPH_ATTR,xt as PART_PARAGRAPH_MAX,ht as PART_PARAGRAPH_MIN,S as PART_PARAGRAPH_VAR,yt as PART_PHONE_MAX_CSS,lt as PART_RELATIVE_PROPS,L as PART_SIZE_MAX,F as PART_SIZE_MIN,v as PART_STYLED_ATTR,E as PART_STYLES_PROP,Dt as PART_STYLE_PROPERTIES,Q as PART_WEIGHTS,T as PART_WIDTH_MAX,it as PART_WIDTH_MIN,st as PART_WIDTH_STEP,I as SHARED_PART_PROPERTIES,ne as applyPartStyles,Jt as isHexColour,Ct as isPartKindKey,Kt as paragraphRules,Tt as partCaseOfTransform,Rt as partCaseTransform,te as partFontKeyOf,K as partPhoneDeclarations,oe as partRuleForKind,se as partRuleForText,wt as partSpacingOf,Qt as partStyleBase,Zt as partStyleCount,ie as partStyleCss,R as partStyleDeclarations,Mt as partStyleForScreen,ee as partStyleSource,qt as propsWithPartStyleValue,vt as propsWithoutPartStyle,re as publishedPartStyles,W as readPartStyle,k as readPartStyles};
1
+ import{textStyleCaseCss as B,textStyleCaseOfCss as V}from"efficient-easel/draw";import{FONT_OPTIONS as Y,FONT_ROLES as I,fontOption as Z,SIZE_STEP_OF_ROLE as v}from"../../../format/lib/fonts.js";import{encodePartKey as q}from"../../../format/lib/puck/part-key.js";import{decodePartId as $,normalizePartText as b}from"./text-parts.js";import{COLOUR_TOKEN as M}from"../../../format/lib/word-styles.js";const E="_partStyles",J="data-sf-part-styled",Q=[400,500,600,700],H=["left","center","right","justify"],F=5,z=1e3,tt=-5,et=10,nt=0,rt=64,ot=2,it=120,T=1280,st=10,L={size:{min:F,max:z,step:1},sizePhone:{min:F,max:z,step:1},padding:{min:nt,max:rt,step:ot},width:{min:it,max:T,step:st}},at=.05,ct=.01,lt=["line","letter"],ft=.5,ut=2.5,pt=-1,dt=1,xt=0,ht=400;function g(t){return!t||typeof t!="object"||Array.isArray(t)?null:t}function j(t){return g(t?.[E])??{}}function k(t,e){const n=j(t);return Object.hasOwn(n,e)?g(n[e])??{}:{}}function vt(t,e){return Object.values(k(t,e)).filter(n=>n!==void 0).length}function C(t,e,n){const r={...j(t)};if(mt(n).length?r[e]=n:delete r[e],!Object.keys(r).length){const{[E]:o,...s}=t;return s}return{...t,[E]:r}}function mt(t){return Object.entries(t).filter(([,e])=>e!==void 0)}function Pt(t){return lt.includes(t)}const _=(t,e,n)=>Math.min(n,Math.max(e,t));function gt(t,e){if(Pt(t)){const n=Math.round(Number(e));return!Number.isFinite(n)||n===0?null:_(n,tt,et)}if(t==="lineHeight"){const n=Number(e);return Number.isFinite(n)?m(_(n,ft,ut),3):null}if(t==="letterSpacing"){const n=Number(e);return Number.isFinite(n)?m(_(n,pt,dt),4):null}if(t==="paragraphSpacing"){const n=Math.round(Number(e));return!Number.isFinite(n)||n<=0?null:_(n,xt,ht)}if(Object.hasOwn(L,t)){const{min:n,max:r}=L[t],o=Math.round(Number(e));return Number.isFinite(o)?_(o,n,r):null}return t==="italic"?typeof e=="boolean"?e:null:t==="role"?I.includes(String(e))?e:null:e}const _t=["weight","size","sizePhone","line","letter","lineHeight","letterSpacing","paragraphSpacing","padding","width"];function W(t){let e=null;const n=r=>{e??={...t},delete e[r]};for(const r of _t){const o=t[r];o!==void 0&&!(typeof o=="number"&&Number.isFinite(o))&&n(r)}return t.align!==void 0&&!H.includes(t.align)&&n("align"),e??t}const St=[["font","role"],["line","lineHeight"],["letter","letterSpacing"]];function At(t){const e=[];for(const[n,r]of St)n===t?e.push(r):r===t&&e.push(n);return e}function qt(t,e,n,r){const o={...k(t,e)},s=r==null?null:gt(n,r);if(s===null)delete o[n];else{o[n]=s;for(const i of At(n))delete o[i]}return C(t,e,o)}function Jt(t,e){return C(t,e,{})}function Tt(t){return V(t)??"none"}function Rt(t){return B(t)??""}const bt={heading:"--font-role-heading",heading2:"--font-role-heading2",heading3:"--font-role-heading3",body:"--font-role-body",accent:"--font-role-accent",label:"--font-role-label"};function Et(t){const e=t.role;return e&&I.includes(e)?e:null}const Nt={headingLarge:"min(clamp(64px,9vw,144px), var(--sf-heading-max,9999px))",headingMedium:"min(32px, var(--sf-heading-max,9999px))",headingSmall:"min(24px, var(--sf-heading-max,9999px))",accent:"32px",body:"14px",label:"12px"};function Ot(t){const e=v[t],n=Nt[e];return n?`var(--size-${e}, ${n})`:`var(--size-${e})`}function wt(t){const e={};return typeof t.lineHeight=="number"?e.lineHeight=t.lineHeight:t.line&&(e.lineHeight=m(yt+t.line*at,3)),typeof t.letterSpacing=="number"?e.letterSpacingEm=t.letterSpacing:t.letter&&(e.letterSpacingEm=m(t.letter*ct,4)),e}const yt=1,S="--sf-part-para",It="(max-width: 719.98px)";function D(t){return t=W(t),typeof t.sizePhone=="number"&&t.sizePhone>0?{fontSize:`${t.sizePhone}px`}:{}}function $t(t,e){return e&&typeof t.sizePhone=="number"&&t.sizePhone>0?{...t,size:t.sizePhone}:t}function R(t,e){t=W(t);const n=Et(t),r=wt(t),o=t.size?`${t.size}px`:n?Ot(n):"";return{fontFamily:t.font?zt(t.font):n?`var(${bt[n]})`:"",fontWeight:t.weight?String(t.weight):"",fontSize:o,minWidth:o?"0":"",overflowWrap:o?"break-word":"",lineHeight:r.lineHeight!==void 0?String(r.lineHeight):"",letterSpacing:r.letterSpacingEm!==void 0?`${r.letterSpacingEm}em`:"",[S]:t.paragraphSpacing?`${t.paragraphSpacing}px`:"",textTransform:Rt(t.case),textAlign:t.align??"",paddingTop:N(t.padding),paddingBottom:N(t.padding),maxWidth:N(t.width),color:Mt(t.color),fontStyle:t.italic===!0?"italic":t.italic===!1?"normal":""}}const Mt=t=>t===void 0||t===""?"":Object.hasOwn(M,t)?M[t]:K.test(t)?t:"",K=/^#[0-9a-fA-F]{6}$/;function Qt(t){return K.test(t)}const N=t=>t===void 0?"":`${t}px`,m=(t,e)=>Number(t.toFixed(e));function te(t,e){const n=Number.parseFloat(t.fontSize)||16,r=Number.parseFloat(t.lineHeight),o=Number.parseFloat(t.letterSpacing),s=Number.parseFloat(t.paddingTop),i=Number.parseFloat(t.maxWidth);return{fontKey:e,sizePx:n,weight:Ht(Number.parseInt(t.fontWeight,10)),lineRatio:Number.isFinite(r)?m(r/n,3):1.2,letterEm:Number.isFinite(o)?m(o/n,4):0,align:Ft(t.textAlign),case:Tt(t.textTransform),paddingPx:Number.isFinite(s)?Math.round(s):0,widthPx:Number.isFinite(i)&&i<T?Math.round(i):T}}function Ht(t){return Number.isFinite(t)?Q.reduce((e,n)=>Math.abs(n-t)<Math.abs(e-t)?n:e):400}function Ft(t){for(const e of H)if(e===t)return e;return t==="end"?"right":"left"}function ee(t,e){const n=G(t);if(!n)return"";for(const r of Y)if(G(e(r.cssVar))===n)return r.key;return""}const G=t=>(t.split(",")[0]??"").replace(/["']/g,"").replace(/\s+/g," ").trim().toLowerCase();function zt(t){const e=Z(t);return e?`var(${e.cssVar})`:""}function ne(t,e,n,r){return!e||$(e)?.nodeId!==t?null:n.find(s=>r(s.getAttribute("class")))??n[0]??null}function re(t,e,n=()=>A,r=i=>i,o=kt,s=!1){let i=0;for(const{element:a,partId:f}of t){const c=$(f)?.propKey;if(!c)continue;const l=r(c),x=Object.hasOwn(e,l)?g(e[l])??{}:{},p=R($t(x,s),A),u=w.some(h=>a.style[h]);let d=!1;for(const[h,P]of Object.entries(p))Ct(a,h,P),P&&(d=!0);if(u||w.some(h=>p[h]))for(const h of Lt(a,o))for(const P of w)h.style[P]=p[P]??"";d&&(i+=1)}return i}function Lt(t,e){const n=b(t.textContent??""),r=[];let o=t;for(let s=0;s<jt&&e(o);s+=1){const i=o.parentElement;if(!i||b(i.textContent??"")!==n)break;r.push(i),o=i}return r}const jt=8;function kt(t){const e=t;return typeof window>"u"||!e?.ownerDocument?!1:window.getComputedStyle(e).display==="inline"}const A={fontKey:"",sizePx:16,weight:400,lineRatio:1,letterEm:0,align:"left",case:"none",paddingPx:0,widthPx:T};function Ct(t,e,n){if(!e.startsWith("--")){t.style[e]=n;return}const r=t.style;typeof r.setProperty=="function"?n?r.setProperty(e,n):r.removeProperty?.(e):r[e]=n;const o=t;e===S&&(n?o.setAttribute?.(X,""):o.removeAttribute?.(X))}const X="data-sf-part-para",Wt=Object.keys(R({},A)),O=new Set(["color","overflowWrap",S,"fontStyle"]),w=Wt.filter(t=>!O.has(t));function oe(t,e,n,r=o=>o){const o=new Map;for(const a of t){const f=q(a.key,a.row);for(const c of a.texts){const l=o.get(c);l?(l.keys.add(a.key),l.partKeys.add(f)):o.set(c,{keys:new Set([a.key]),partKeys:new Set([f])})}}const s=new Map;for(const a of Object.keys(e)){if(!Dt(a))continue;const f=g(e[a])??{},c=R(f,A),l=D(f);!Object.values(c).some(Boolean)&&!Object.keys(l).length||s.set(a,{stamp:U(n,a),declarations:c,...Object.keys(l).length?{phone:l}:{}})}const i=new Map;for(const[a,{keys:f,partKeys:c}]of o){const l=c.size===1?[...c][0]:f.size===1?[...f][0]:null;if(!l)continue;const x=r(l);if(!Object.hasOwn(e,x))continue;const p=g(e[x])??{},u=R(p,A),d=D(p);!Object.values(u).some(Boolean)&&!Object.keys(d).length||i.set(a,{stamp:U(n,l),declarations:u,...Object.keys(d).length?{phone:d}:{}})}return i.size||s.size?{rules:i,kindRules:s,drawn:new Set(o.keys())}:null}function Dt(t){return t.startsWith("@")}function ie(t,e){return t?.kindRules.get(e)??null}function U(t,e){const n=r=>r.replace(/[^A-Za-z0-9_-]+/g,"-");return`${n(t)}~${n(e)}`}function se(t){const e=[],n=new Set,r=o=>{const{stamp:s,declarations:i}=o;if(n.has(s))return;n.add(s);const a=Object.entries(i).filter(([,u])=>u!==""),f=u=>u.map(([d,y])=>`${Gt(d)}: ${y}`).join("; "),c=`[${J}="${s}"]`,l=a.filter(([u])=>!O.has(u)),x=a.filter(([u])=>O.has(u));l.length&&e.push(`${c}, *:has(> ${c}) { ${f(l)} }`),x.length&&e.push(`${c} { ${f(x)} }`),i[S]&&e.push(...Kt(c));const p=Object.entries(o.phone??{}).filter(([,u])=>u!=="");p.length&&e.push(`@media ${It} { ${c}, *:has(> ${c}) { ${f(p)} } }`)};for(const o of t.rules.values())r(o);for(const o of t.kindRules.values())r(o);return e.join(`
2
+ `)}function Kt(t){const e=`var(${S})`,n=":is([data-sf-bullet], [data-sf-number])";return[`${t} br:not(br + br) { display: block; content: ""; margin-top: ${e} }`,`${t} ${n}:not(:first-child) { margin-top: ${e} }`,`${t} ${n}:not(:last-child) { margin-bottom: ${e} }`]}const Gt=t=>t.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`);function ae(t,e){if(!t)return null;const n=b(e),r=t.rules.get(n);if(r)return r;if(n.length<Xt||t.drawn.has(n))return null;let o=null;for(const[s,i]of t.rules)if(s.includes(n)){if(o&&o.stamp!==i.stamp)return null;o=i}return o}const Xt=8;export{H as PART_ALIGNS,dt as PART_LETTER_EM_MAX,pt as PART_LETTER_EM_MIN,ct as PART_LETTER_STEP,ut as PART_LINE_HEIGHT_MAX,ft as PART_LINE_HEIGHT_MIN,at as PART_LINE_STEP,L as PART_MEASURED_TRACKS,et as PART_NUDGE_MAX,tt as PART_NUDGE_MIN,rt as PART_PADDING_MAX,nt as PART_PADDING_MIN,ot as PART_PADDING_STEP,X as PART_PARAGRAPH_ATTR,ht as PART_PARAGRAPH_MAX,xt as PART_PARAGRAPH_MIN,S as PART_PARAGRAPH_VAR,It as PART_PHONE_MAX_CSS,lt as PART_RELATIVE_PROPS,z as PART_SIZE_MAX,F as PART_SIZE_MIN,J as PART_STYLED_ATTR,E as PART_STYLES_PROP,Wt as PART_STYLE_PROPERTIES,Q as PART_WEIGHTS,T as PART_WIDTH_MAX,it as PART_WIDTH_MIN,st as PART_WIDTH_STEP,w as SHARED_PART_PROPERTIES,re as applyPartStyles,Qt as isHexColour,Dt as isPartKindKey,Kt as paragraphRules,Tt as partCaseOfTransform,Rt as partCaseTransform,ee as partFontKeyOf,D as partPhoneDeclarations,ie as partRuleForKind,ae as partRuleForText,wt as partSpacingOf,te as partStyleBase,vt as partStyleCount,se as partStyleCss,R as partStyleDeclarations,$t as partStyleForScreen,ne as partStyleSource,qt as propsWithPartStyleValue,Jt as propsWithoutPartStyle,oe as publishedPartStyles,k as readPartStyle,j as readPartStyles};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "efficient-web-builder",
3
- "version": "24.0.0",
3
+ "version": "25.1.0",
4
4
  "description": "The EFFICIENT website builder: the website editor, the general sections and the renderer that draws them, compiled. Hostable by any EFFICIENT product; nothing here imports a host.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "Efficient Pty Ltd",
@@ -52,8 +52,8 @@
52
52
  },
53
53
  "peerDependencies": {
54
54
  "@tabler/icons-react": "^3.46.0",
55
- "efficient-easel": "^13.1.1",
56
- "efficient-shell": "^7.75.0",
55
+ "efficient-easel": "^13.2.0",
56
+ "efficient-shell": "^7.75.0 || ^8.0.0",
57
57
  "isomorphic-dompurify": "^3.18.0",
58
58
  "next": ">=16",
59
59
  "react": ">=19",
@@ -72,8 +72,8 @@
72
72
  "@types/node": "^20",
73
73
  "@types/react": "^19",
74
74
  "@types/react-dom": "^19",
75
- "efficient-easel": "^13.1.1",
76
- "efficient-shell": "^7.75.0",
75
+ "efficient-easel": "^13.2.0",
76
+ "efficient-shell": "^8.0.0",
77
77
  "esbuild": "^0.28.0",
78
78
  "happy-dom": "^20.14.5",
79
79
  "isomorphic-dompurify": "^3.18.0",