efficient-web-builder 26.3.1 → 27.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/editor/components/storefront-editor.css +18 -13
- package/dist/format/lib/build/module-specifiers.d.ts +39 -0
- package/dist/format/lib/build/module-specifiers.js +1 -0
- package/dist/format/lib/source-rules/panel-no-divider-lines.js +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 27.0.0 - 2026-10-20
|
|
6
|
+
|
|
7
|
+
**The settings panels read easel's one side inset** (GG option A, 2026-10-20). A MAJOR: the `efficient-easel` peer range rises from `^13.3.0 || ^14.0.0` to `^14.1.0`, and the dev range from `^14.0.0`.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Every panel head and the settings rows share one side inset: 16px, and 12px under a 260px panel.** efficient-easel 14.1.0 declares it once, `--es-insp-pad-x` on the side panel. Before, the heads used 16 and 12 and the rows had their own 8px under a 284px panel, so between 220 and 283px a head and its rows sat up to 8px apart. At a 220px panel the rows now have 12px each side, not 8.
|
|
12
|
+
- `storefront-editor.css` has no inset alias of its own any more (`--sf-insp-pad-x` is gone). The right inset (`--sf-insp-pad-end`) and the full-bleed group rule read `--es-insp-pad-x`.
|
|
13
|
+
- The Appearance rail button draws the shell's palette glyph; its own svg is gone (#208).
|
|
14
|
+
|
|
15
|
+
### Guards
|
|
16
|
+
|
|
17
|
+
- `scripts/panel-body-padding.ts` reads the inset from easel's token rules. `test-panel-padding-narrows.ts` holds 16, and 12 at and under a saved 260px panel. `test-panel-strip-width-stable.ts` re-aims its samples: the inset step at 260 gives the 239px tab line two bands, and the 197px line is at the 220px floor again. `panel-no-divider-lines` checks the full-bleed rule against `--es-insp-pad-x`.
|
|
18
|
+
- `scripts/test-two-ways-to-add.ts` §1 and `scripts/test-publish-main-action.tsx`: the easel peer and dev ranges are `^14.1.0`.
|
|
19
|
+
|
|
5
20
|
## 26.3.1 - 2026-10-20
|
|
6
21
|
|
|
7
22
|
**`format/lib/puck/box-footer` is back** (26.3.0 removed it by mistake). A PATCH: every file 26.2.0 published is in the package again.
|
|
@@ -870,23 +870,22 @@
|
|
|
870
870
|
came to hang 6px OUTSIDE the panel at every width under 280 (measured: gap
|
|
871
871
|
-6.0px at 260 and at 220, against 0.0 at the default 320). One name, read
|
|
872
872
|
by the padding, by the narrow override, and by anything that undoes it. */
|
|
873
|
-
/* THE SIDES ARE EASEL'S
|
|
874
|
-
`es-insp-body
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
--sf-insp-pad-x: var(--es-insp-pad-x, var(--space-16));
|
|
873
|
+
/* THE SIDES ARE EASEL'S (one text panel W1; one home since efficient-easel
|
|
874
|
+
14.1.0): the body wears `es-insp-body`, and the inset is easel's
|
|
875
|
+
`--es-insp-pad-x`, set on the side panel: 16px, 12px under a 260px panel.
|
|
876
|
+
This sheet keeps no alias of its own; the rules below that reach the
|
|
877
|
+
panel's edge read easel's token. */
|
|
879
878
|
padding-block: var(--space-4) var(--space-20);
|
|
880
879
|
/* THE RIGHT INSET COUNTS THE SCROLLBAR'S LANE (2026-10-20). The gutter above
|
|
881
880
|
is held whether or not the scrollbar is drawn, so the rows' right side was
|
|
882
881
|
the inset PLUS the lane: 16 + 11 at a 320px panel, against 16 on the left
|
|
883
882
|
and 16 in the head. Now the lane is part of the inset: the end padding is
|
|
884
883
|
the inset less the lane, so both sides and the head read one number — 16,
|
|
885
|
-
and
|
|
884
|
+
and 12 under a 260px panel. The lane is 8px for that reason: the narrow
|
|
886
885
|
inset must hold all of it (`@supports` below). A browser that draws its
|
|
887
886
|
own scrollbar (Firefox) keeps a 0px lane here and the full inset. */
|
|
888
887
|
--sf-insp-bar-w: 0px;
|
|
889
|
-
--sf-insp-pad-end: calc(var(--
|
|
888
|
+
--sf-insp-pad-end: calc(var(--es-insp-pad-x) - var(--sf-insp-bar-w));
|
|
890
889
|
padding-inline-end: var(--sf-insp-pad-end);
|
|
891
890
|
|
|
892
891
|
/* THE DROPDOWN ARROW, WRITTEN ONCE — card 66399.c.
|
|
@@ -907,7 +906,13 @@
|
|
|
907
906
|
--sf-select-arrow: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='%23c3c3c3'><polygon points='0,0 100,0 50,50'/></svg>");
|
|
908
907
|
}
|
|
909
908
|
|
|
910
|
-
/* 2026-10-
|
|
909
|
+
/* 2026-10-20: THE INSET IS EASEL'S, 16 AND 12 UNDER 260 (efficient-easel
|
|
910
|
+
14.1.0, GG option A). The 8px under a 284px panel that the notes below
|
|
911
|
+
measured is gone: one side inset for every head and body, one home.
|
|
912
|
+
The history stays, because the overflow sweep is still how a width is
|
|
913
|
+
judged.
|
|
914
|
+
|
|
915
|
+
2026-10-13: THE WIDE PADDING IS 16, AND THE LINE 284. 14 was off the
|
|
911
916
|
spacing scale (Lewis 2026-10-12: 2 / 4 / 8 / 12 / 16 …) and a value off it
|
|
912
917
|
goes UP. The content width card 66212 protects stays 241px; at 16 each side
|
|
913
918
|
that needs a 285px panel, so the query below reads `< 284px`. The note that
|
|
@@ -952,8 +957,8 @@
|
|
|
952
957
|
and 10 has nothing to spare, and type is paid vertically as well — card 66206
|
|
953
958
|
has just set the row rhythm to 16px, which shrinking type would hand straight
|
|
954
959
|
back. The padding was the horizontal cost, so the padding is what moved. */
|
|
955
|
-
/* The narrow inset (
|
|
956
|
-
|
|
960
|
+
/* The narrow inset (12px under a 260px panel) is easel's, on the side panel
|
|
961
|
+
(`--es-insp-pad-x`, 14.1.0); every reader here narrows with it. */
|
|
957
962
|
|
|
958
963
|
/* The lane the right inset counts (see `.sf-insp-body`): the body's own
|
|
959
964
|
scrollbar, 8px where this sheet can size it. easel's `.sf-scroll` draws 11px,
|
|
@@ -1776,9 +1781,9 @@
|
|
|
1776
1781
|
⚠️ READ FROM THE TOKEN, never typed. A literal `14px` here hung the line
|
|
1777
1782
|
6px OUTSIDE the panel at every width under 280, where card 66212 narrows
|
|
1778
1783
|
the padding to 8 — measured at 260 and at 220 before this was a `var()`. */
|
|
1779
|
-
margin-left: calc(var(--
|
|
1784
|
+
margin-left: calc(var(--es-insp-pad-x) * -1);
|
|
1780
1785
|
margin-right: calc(var(--sf-insp-pad-end) * -1);
|
|
1781
|
-
padding-left: var(--
|
|
1786
|
+
padding-left: var(--es-insp-pad-x);
|
|
1782
1787
|
padding-right: var(--sf-insp-pad-end);
|
|
1783
1788
|
}
|
|
1784
1789
|
/* The heading's own top margin is the wrapper's job now. */
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface ModuleSpecifier {
|
|
2
|
+
start: number;
|
|
3
|
+
end: number;
|
|
4
|
+
text: string;
|
|
5
|
+
}
|
|
6
|
+
interface SyntaxNode {
|
|
7
|
+
kind: number;
|
|
8
|
+
getStart(source?: unknown): number;
|
|
9
|
+
getEnd(): number;
|
|
10
|
+
}
|
|
11
|
+
export interface TypeScriptApi {
|
|
12
|
+
createSourceFile(fileName: string, text: string, target: number, setParentNodes: boolean, kind: number): SyntaxNode;
|
|
13
|
+
ScriptTarget: {
|
|
14
|
+
Latest: number;
|
|
15
|
+
};
|
|
16
|
+
ScriptKind: {
|
|
17
|
+
JS: number;
|
|
18
|
+
TS: number;
|
|
19
|
+
};
|
|
20
|
+
SyntaxKind: {
|
|
21
|
+
ImportKeyword: number;
|
|
22
|
+
};
|
|
23
|
+
forEachChild(node: SyntaxNode, visit: (child: SyntaxNode) => void): unknown;
|
|
24
|
+
isImportDeclaration(node: SyntaxNode): boolean;
|
|
25
|
+
isExportDeclaration(node: SyntaxNode): boolean;
|
|
26
|
+
isCallExpression(node: SyntaxNode): boolean;
|
|
27
|
+
isIdentifier(node: SyntaxNode): boolean;
|
|
28
|
+
isImportTypeNode(node: SyntaxNode): boolean;
|
|
29
|
+
isLiteralTypeNode(node: SyntaxNode): boolean;
|
|
30
|
+
isModuleDeclaration(node: SyntaxNode): boolean;
|
|
31
|
+
isStringLiteral(node: unknown): boolean;
|
|
32
|
+
}
|
|
33
|
+
export declare function moduleSpecifiers(ts: TypeScriptApi, fileName: string, text: string): ModuleSpecifier[];
|
|
34
|
+
export declare function relinkSpecifiers(ts: TypeScriptApi, fileName: string, text: string, retarget: (spec: string) => string | null): {
|
|
35
|
+
text: string;
|
|
36
|
+
rewritten: number;
|
|
37
|
+
};
|
|
38
|
+
export declare function relativeSpecifier(from: string, to: string): string;
|
|
39
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function u(e,a,s){const c=e.createSourceFile(a,s,e.ScriptTarget.Latest,!1,a.endsWith(".d.ts")?e.ScriptKind.TS:e.ScriptKind.JS),i=[],r=t=>i.push({start:t.getStart(c)+1,end:t.getEnd()-1,text:t.text}),n=t=>{const o=t;if((e.isImportDeclaration(t)||e.isExportDeclaration(t))&&o.moduleSpecifier&&e.isStringLiteral(o.moduleSpecifier))r(o.moduleSpecifier);else if(e.isCallExpression(t)){const l=o.arguments,p=o.expression;l.length>0&&e.isStringLiteral(l[0])&&(p.kind===e.SyntaxKind.ImportKeyword||e.isIdentifier(p)&&p.text==="require")&&r(l[0])}else if(e.isImportTypeNode(t)){const l=o.argument;e.isLiteralTypeNode(l)&&e.isStringLiteral(l.literal)&&r(l.literal)}else e.isModuleDeclaration(t)&&e.isStringLiteral(o.name)&&r(o.name);e.forEachChild(t,n)};return n(c),i.sort((t,o)=>o.start-t.start)}function f(e,a,s,c){let i=s,r=0;for(const n of u(e,a,s)){const t=c(n.text);t===null||t===n.text||(i=i.slice(0,n.start)+t+i.slice(n.end),r+=1)}return{text:i,rewritten:r}}function S(e,a){const s=e.split("/").slice(0,-1),c=a.split("/");let i=0;for(;i<s.length&&i<c.length-1&&s[i]===c[i];)i+=1;const r=s.length-i,n=c.slice(i).join("/");return r===0?`./${n}`:`${"../".repeat(r)}${n}`}export{u as moduleSpecifiers,S as relativeSpecifier,f as relinkSpecifiers};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import{ruleChecks as O}from"./rule.js";function j(r){const s=r.split(`
|
|
2
2
|
`),n=[];return s.forEach((g,a)=>{if(!/border-(top|bottom|left|right)[a-z-]*\s*:[^;]*\bsolid\b/.test(g))return;let $=0,c="?";for(let i=a-1;i>=0;i-=1)if($+=(s[i].match(/\}/g)??[]).length-(s[i].match(/\{/g)??[]).length,$<0){c=s[i].split("{")[0].trim();let h=i-1;for(;h>=0&&s[h].trim().endsWith(",");)c=`${s[h].trim()} ${c}`,h-=1;break}n.push({line:a+1,selector:c,decl:g.trim()})}),n}const y=r=>r.split(/[\s,+>~]/)[0].trim(),k=(r,s)=>s in r||y(s)in r,L=/\bzinc-|--sf-line\b|--sf-divider\b|--sf-input-border\b|--sf-chip-bg\b|--es-line\b|--es-divider\b|--sf-text-secondary\b/,S=[["utility",/(^|["'`\s])border-(t|b|l|r)($|["'`\s])/],["style object",/border(Top|Bottom|Left|Right)\s*:/],["css in a string",/border-(top|bottom|left|right)\s*:/]];function E(r){return/radius/i.test(r)||!L.test(r)?null:S.find(([,s])=>s.test(r))?.[0]??null}function T(r,s){const{check:n,report:g}=O("panel divider lines"),a=s.allowed??{},$=s.pendingElsewhere??{},c=s.allowedTsx??[];{const e=u=>["border",u].join("-"),t=[".a,",".b + .c {",` ${e("top")}: 1px solid red;`,"}",`.d { ${e("radius")}: 4px; }`].join(`
|
|
3
3
|
`),l=j(t);n("the census finds a directional solid border, with its whole multi-line selector",l.length===1&&l[0].selector===".a, .b + .c"&&y(l[0].selector)===".a",JSON.stringify(l));const d=["zinc","200"].join("-"),f=[["a utility",`<div className="${e("t")} border-${d}" />`,"utility"],["a style object",`style={{ ${["border","Top"].join("")}: "1px solid var(--sf-line)" }}`,"style object"],["CSS in a string",`const s = "${e("left")}: 1px solid var(--es-divider)";`,"css in a string"],["a border in the storefront's own ink",`<div className="${e("t")} border-line" />`,null],["a radius",`<div className="${e("t")} border-${d} rounded" style={{ borderRadius: 4 }} />`,null]];for(const[u,o,p]of f)n(`the sweep reads ${u} as ${p??"no hairline"}`,E(o)===p,o)}const i=[];for(const e of s.panelSheets??[]){const t=r.find(o=>o.path===e)?.text??"",l=j(t);n(`${e} was read and parsed`,t.length>1e4&&l.length>0,`${t.length} bytes, ${l.length} directional border(s)`),i.push(...l.map(o=>({...o,sheet:e})));const f=(t.replace(/\/\*[\s\S]*?\*\//g,"").match(/\[class\*="_PuckFields[^"]*"\][^{]*\{/g)??[]).length;n(`${e} reaches for no Puck field wrapper`,f===0,`${f} rule(s) still select \`[class*="_PuckFields…"]\` — the rows are \`.es-insp-row\` now`);const u=t.replace(/,\s*\n\s*/g,", ");for(const o of s.fullBleed??[]){if(!(o in a))continue;const p=u.indexOf(`
|
|
4
|
-
${o} {`);if(!n(`the allowed full-bleed rule is still in ${e}`,p>=0,o))continue;const v=u.slice(p,u.indexOf("}",p));for(const[w,x]of[["left","--
|
|
4
|
+
${o} {`);if(!n(`the allowed full-bleed rule is still in ${e}`,p>=0,o))continue;const v=u.slice(p,u.indexOf("}",p));for(const[w,x]of[["left","--es-insp-pad-x"],["right","--sf-insp-pad-end"]])n(`the full-bleed rule cancels the panel's ${w} padding, so it reaches the edge`,new RegExp(`margin-${w}:\\s*calc\\(var\\(${x}\\)\\s*\\*\\s*-1\\)`).test(v),"inset, this is the line the owner rejected — full bleed is the exemption"),n(`…and puts that ${w} padding back inside, so the words stay on the rows' edge`,new RegExp(`padding-${w}:\\s*var\\(${x}\\)`).test(v))}}for(const e of s.fullBleed??[])n(`the full-bleed selector is an ALLOWED entry: ${e}`,e in a);if(s.panelSheets?.length){const e=i.filter(t=>!k(a,t.selector)&&!k($,t.selector));n(`no unlisted directional border survives in a panel stylesheet (${i.length} censused)`,e.length===0,`${e.length} neither removed, allowed, nor owed to another card — delete it and let the rhythm tokens separate, or add it to the allow-list with a reason if it is a control edge:
|
|
5
5
|
`+e.map(t=>`${t.sheet}:${t.line} ${t.selector} — ${t.decl}`).join(`
|
|
6
6
|
`))}const h=e=>i.some(t=>t.selector===e||y(t.selector)===e);for(const[e,t]of Object.entries(a))n(`ALLOWED entry ${e} still names a real rule (${t.slice(0,48)}…)`,h(e),"the rule is gone: delete this entry with it");for(const[e,t]of Object.entries($))n(`PENDING entry ${e} is still owed — ${t}`,h(e),"that card has landed and the rule is gone: delete this entry");const b=r.filter(e=>/\.tsx?$/.test(e.path));n(`the repository's own TS/TSX files were swept (${b.length}, at least ${s.minFiles})`,b.length>=s.minFiles);const m=[];for(const e of b)e.text.split(`
|
|
7
7
|
`).forEach((t,l)=>{const d=E(t);d&&(c.some(f=>f.file===e.path&&t.includes(f.util))||m.push(`${e.path}:${l+1} (${d}) ${t.trim().slice(0,80)}`))});n("no editor panel file draws an unlisted directional border",m.length===0,`${m.length} found:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "efficient-web-builder",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "27.0.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,7 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"@tabler/icons-react": "^3.46.0",
|
|
55
|
-
"efficient-easel": "^
|
|
55
|
+
"efficient-easel": "^14.1.0",
|
|
56
56
|
"efficient-shell": "^7.75.0 || ^8.0.0",
|
|
57
57
|
"isomorphic-dompurify": "^3.18.0",
|
|
58
58
|
"next": ">=16",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"@types/node": "^20",
|
|
73
73
|
"@types/react": "^19",
|
|
74
74
|
"@types/react-dom": "^19",
|
|
75
|
-
"efficient-easel": "^14.
|
|
75
|
+
"efficient-easel": "^14.1.0",
|
|
76
76
|
"efficient-shell": "^8.0.0",
|
|
77
77
|
"esbuild": "^0.28.0",
|
|
78
78
|
"happy-dom": "^20.14.5",
|