mosaic-headless 1.30.0 → 1.30.1

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.
@@ -0,0 +1,148 @@
1
+ breakpoint,attrID,property,declared,computed,status
2
+ _,mk-acct-body,backgroundColor,"rgb(250,250,247)","rgb(250, 250, 247)",ok
3
+ _,mk-acct-body,paddingLeft,40px,40px,ok
4
+ _,mk-acct-body,paddingRight,40px,40px,ok
5
+ _,mk-acct-body,fontFamily,"'Noto Sans TC','PingFang TC','Hiragino Sans TC','Microsoft JhengHei',sans-serif","""Noto Sans TC"", ""PingFang TC"", ""Hiragino Sans TC"", ""Microsoft JhengHei"", sans-serif",ok
6
+ _,mk-acct-body-in,width,100%,viewport- or parent-relative,not-comparable
7
+ _,mk-acct-body-in,maxWidth,1240px,1240px,ok
8
+ _,mk-acct-body-in,marginLeft,auto,auto resolves to a used value (60px),not-comparable
9
+ _,mk-acct-body-in,marginRight,auto,auto resolves to a used value (60px),not-comparable
10
+ _,mk-acct-head,display,grid,grid,ok
11
+ _,mk-acct-head,gridCols,84px 1fr,84px 1156px,ok
12
+ _,mk-acct-head,columnGap,0px,0px,ok
13
+ _,mk-acct-head,alignItems,start,start,ok
14
+ _,mk-acct-head,paddingTop,22px,22px,ok
15
+ _,mk-acct-head,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
16
+ _,mk-acct-mast,backgroundColor,"rgb(250,250,247)","rgb(250, 250, 247)",ok
17
+ _,mk-acct-mast,paddingLeft,40px,40px,ok
18
+ _,mk-acct-mast,paddingRight,40px,40px,ok
19
+ _,mk-acct-mast,fontFamily,"'Noto Sans TC','PingFang TC','Hiragino Sans TC','Microsoft JhengHei',sans-serif","""Noto Sans TC"", ""PingFang TC"", ""Hiragino Sans TC"", ""Microsoft JhengHei"", sans-serif",ok
20
+ _,mk-acct-mast-in,width,100%,viewport- or parent-relative,not-comparable
21
+ _,mk-acct-mast-in,maxWidth,1240px,1240px,ok
22
+ _,mk-acct-mast-in,marginLeft,auto,auto resolves to a used value (60px),not-comparable
23
+ _,mk-acct-mast-in,marginRight,auto,auto resolves to a used value (60px),not-comparable
24
+ _,mk-acct-pad,paddingTop,70px,70px,ok
25
+ _,mk-acct-pad,paddingBottom,48px,48px,ok
26
+ _,mk-acct-r-0,display,grid,grid,ok
27
+ _,mk-acct-r-0,gridCols,150px 1fr,150px 986px,ok
28
+ _,mk-acct-r-0,columnGap,20px,20px,ok
29
+ _,mk-acct-r-0,alignItems,baseline,baseline,ok
30
+ _,mk-acct-r-0,paddingTop,13px,13px,ok
31
+ _,mk-acct-r-0,paddingBottom,13px,13px,ok
32
+ _,mk-acct-r-0,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
33
+ _,mk-acct-r-1,display,grid,grid,ok
34
+ _,mk-acct-r-1,gridCols,150px 1fr,150px 986px,ok
35
+ _,mk-acct-r-1,columnGap,20px,20px,ok
36
+ _,mk-acct-r-1,alignItems,baseline,baseline,ok
37
+ _,mk-acct-r-1,paddingTop,13px,13px,ok
38
+ _,mk-acct-r-1,paddingBottom,13px,13px,ok
39
+ _,mk-acct-r-1,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
40
+ _,mk-acct-r-2,display,grid,grid,ok
41
+ _,mk-acct-r-2,gridCols,150px 1fr,150px 986px,ok
42
+ _,mk-acct-r-2,columnGap,20px,20px,ok
43
+ _,mk-acct-r-2,alignItems,baseline,baseline,ok
44
+ _,mk-acct-r-2,paddingTop,13px,13px,ok
45
+ _,mk-acct-r-2,paddingBottom,13px,13px,ok
46
+ _,mk-acct-r-2,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
47
+ _,mk-acct-rows,marginTop,30px,30px,ok
48
+ _,mk-acct-rows,display,grid,grid,ok
49
+ _,mk-acct-rows,rowGap,0px,0px,ok
50
+ _,mk-acct-sheet,paddingBottom,120px,120px,ok
51
+ _t,mk-acct-body,backgroundColor,"rgb(250,250,247)","rgb(250, 250, 247)",ok
52
+ _t,mk-acct-body,paddingLeft,28px,28px,ok
53
+ _t,mk-acct-body,paddingRight,28px,28px,ok
54
+ _t,mk-acct-body,fontFamily,"'Noto Sans TC','PingFang TC','Hiragino Sans TC','Microsoft JhengHei',sans-serif","""Noto Sans TC"", ""PingFang TC"", ""Hiragino Sans TC"", ""Microsoft JhengHei"", sans-serif",ok
55
+ _t,mk-acct-body-in,width,100%,viewport- or parent-relative,not-comparable
56
+ _t,mk-acct-body-in,maxWidth,1240px,1240px,ok
57
+ _t,mk-acct-body-in,marginLeft,auto,auto resolves to a used value (0px),not-comparable
58
+ _t,mk-acct-body-in,marginRight,auto,auto resolves to a used value (0px),not-comparable
59
+ _t,mk-acct-head,display,grid,grid,ok
60
+ _t,mk-acct-head,gridCols,84px 1fr,84px 760px,ok
61
+ _t,mk-acct-head,columnGap,0px,0px,ok
62
+ _t,mk-acct-head,alignItems,start,start,ok
63
+ _t,mk-acct-head,paddingTop,22px,22px,ok
64
+ _t,mk-acct-head,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
65
+ _t,mk-acct-mast,backgroundColor,"rgb(250,250,247)","rgb(250, 250, 247)",ok
66
+ _t,mk-acct-mast,paddingLeft,28px,28px,ok
67
+ _t,mk-acct-mast,paddingRight,28px,28px,ok
68
+ _t,mk-acct-mast,fontFamily,"'Noto Sans TC','PingFang TC','Hiragino Sans TC','Microsoft JhengHei',sans-serif","""Noto Sans TC"", ""PingFang TC"", ""Hiragino Sans TC"", ""Microsoft JhengHei"", sans-serif",ok
69
+ _t,mk-acct-mast-in,width,100%,viewport- or parent-relative,not-comparable
70
+ _t,mk-acct-mast-in,maxWidth,1240px,1240px,ok
71
+ _t,mk-acct-mast-in,marginLeft,auto,auto resolves to a used value (0px),not-comparable
72
+ _t,mk-acct-mast-in,marginRight,auto,auto resolves to a used value (0px),not-comparable
73
+ _t,mk-acct-pad,paddingTop,50px,50px,ok
74
+ _t,mk-acct-pad,paddingBottom,48px,48px,ok
75
+ _t,mk-acct-r-0,display,grid,grid,ok
76
+ _t,mk-acct-r-0,gridCols,150px 1fr,150px 590px,ok
77
+ _t,mk-acct-r-0,columnGap,20px,20px,ok
78
+ _t,mk-acct-r-0,alignItems,baseline,baseline,ok
79
+ _t,mk-acct-r-0,paddingTop,13px,13px,ok
80
+ _t,mk-acct-r-0,paddingBottom,13px,13px,ok
81
+ _t,mk-acct-r-0,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
82
+ _t,mk-acct-r-1,display,grid,grid,ok
83
+ _t,mk-acct-r-1,gridCols,150px 1fr,150px 590px,ok
84
+ _t,mk-acct-r-1,columnGap,20px,20px,ok
85
+ _t,mk-acct-r-1,alignItems,baseline,baseline,ok
86
+ _t,mk-acct-r-1,paddingTop,13px,13px,ok
87
+ _t,mk-acct-r-1,paddingBottom,13px,13px,ok
88
+ _t,mk-acct-r-1,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
89
+ _t,mk-acct-r-2,display,grid,grid,ok
90
+ _t,mk-acct-r-2,gridCols,150px 1fr,150px 590px,ok
91
+ _t,mk-acct-r-2,columnGap,20px,20px,ok
92
+ _t,mk-acct-r-2,alignItems,baseline,baseline,ok
93
+ _t,mk-acct-r-2,paddingTop,13px,13px,ok
94
+ _t,mk-acct-r-2,paddingBottom,13px,13px,ok
95
+ _t,mk-acct-r-2,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
96
+ _t,mk-acct-rows,marginTop,30px,30px,ok
97
+ _t,mk-acct-rows,display,grid,grid,ok
98
+ _t,mk-acct-rows,rowGap,0px,0px,ok
99
+ _t,mk-acct-sheet,paddingBottom,120px,120px,ok
100
+ _m,mk-acct-body,backgroundColor,"rgb(250,250,247)","rgb(250, 250, 247)",ok
101
+ _m,mk-acct-body,paddingLeft,18px,18px,ok
102
+ _m,mk-acct-body,paddingRight,18px,18px,ok
103
+ _m,mk-acct-body,fontFamily,"'Noto Sans TC','PingFang TC','Hiragino Sans TC','Microsoft JhengHei',sans-serif","""Noto Sans TC"", ""PingFang TC"", ""Hiragino Sans TC"", ""Microsoft JhengHei"", sans-serif",ok
104
+ _m,mk-acct-body-in,width,100%,viewport- or parent-relative,not-comparable
105
+ _m,mk-acct-body-in,maxWidth,1240px,1240px,ok
106
+ _m,mk-acct-body-in,marginLeft,auto,auto resolves to a used value (0px),not-comparable
107
+ _m,mk-acct-body-in,marginRight,auto,auto resolves to a used value (0px),not-comparable
108
+ _m,mk-acct-head,display,grid,grid,ok
109
+ _m,mk-acct-head,gridCols,48px 1fr,48px 306px,ok
110
+ _m,mk-acct-head,columnGap,0px,0px,ok
111
+ _m,mk-acct-head,alignItems,start,start,ok
112
+ _m,mk-acct-head,paddingTop,22px,22px,ok
113
+ _m,mk-acct-head,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
114
+ _m,mk-acct-mast,backgroundColor,"rgb(250,250,247)","rgb(250, 250, 247)",ok
115
+ _m,mk-acct-mast,paddingLeft,18px,18px,ok
116
+ _m,mk-acct-mast,paddingRight,18px,18px,ok
117
+ _m,mk-acct-mast,fontFamily,"'Noto Sans TC','PingFang TC','Hiragino Sans TC','Microsoft JhengHei',sans-serif","""Noto Sans TC"", ""PingFang TC"", ""Hiragino Sans TC"", ""Microsoft JhengHei"", sans-serif",ok
118
+ _m,mk-acct-mast-in,width,100%,viewport- or parent-relative,not-comparable
119
+ _m,mk-acct-mast-in,maxWidth,1240px,1240px,ok
120
+ _m,mk-acct-mast-in,marginLeft,auto,auto resolves to a used value (0px),not-comparable
121
+ _m,mk-acct-mast-in,marginRight,auto,auto resolves to a used value (0px),not-comparable
122
+ _m,mk-acct-pad,paddingTop,24px,24px,ok
123
+ _m,mk-acct-pad,paddingBottom,34px,34px,ok
124
+ _m,mk-acct-r-0,display,grid,grid,ok
125
+ _m,mk-acct-r-0,gridCols,84px 1fr,84px 210px,ok
126
+ _m,mk-acct-r-0,columnGap,12px,12px,ok
127
+ _m,mk-acct-r-0,alignItems,baseline,baseline,ok
128
+ _m,mk-acct-r-0,paddingTop,13px,13px,ok
129
+ _m,mk-acct-r-0,paddingBottom,13px,13px,ok
130
+ _m,mk-acct-r-0,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
131
+ _m,mk-acct-r-1,display,grid,grid,ok
132
+ _m,mk-acct-r-1,gridCols,84px 1fr,84px 210px,ok
133
+ _m,mk-acct-r-1,columnGap,12px,12px,ok
134
+ _m,mk-acct-r-1,alignItems,baseline,baseline,ok
135
+ _m,mk-acct-r-1,paddingTop,13px,13px,ok
136
+ _m,mk-acct-r-1,paddingBottom,13px,13px,ok
137
+ _m,mk-acct-r-1,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
138
+ _m,mk-acct-r-2,display,grid,grid,ok
139
+ _m,mk-acct-r-2,gridCols,84px 1fr,84px 210px,ok
140
+ _m,mk-acct-r-2,columnGap,12px,12px,ok
141
+ _m,mk-acct-r-2,alignItems,baseline,baseline,ok
142
+ _m,mk-acct-r-2,paddingTop,13px,13px,ok
143
+ _m,mk-acct-r-2,paddingBottom,13px,13px,ok
144
+ _m,mk-acct-r-2,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
145
+ _m,mk-acct-rows,marginTop,30px,30px,ok
146
+ _m,mk-acct-rows,display,grid,grid,ok
147
+ _m,mk-acct-rows,rowGap,0px,0px,ok
148
+ _m,mk-acct-sheet,paddingBottom,72px,72px,ok
@@ -60,7 +60,7 @@ WPMenuElementMResourceData,ElementMResourceDataAbstract,free,createDataSimple,me
60
60
  NavbarElementMResourceData,ElementMResourceDataAbstract,free,createDataSimple,buttonBreakpoint,ValidateAllowUndefined|ValidatorAcceptedValues|ValidatorBreakpoint|Validators,none,true,,data-animation|data-toggle-breakpoint-id,Mosaic/NodeTypes/Navbar/NavbarElementMResourceData.php
61
61
  NavbarElementMResourceData,ElementMResourceDataAbstract,free,createDataSimple,animation,ValidateAllowUndefined|ValidatorAcceptedValues|Validators,none|fade|fadeDown|fadeUp|scaleDown|flipDown,true,,data-animation|data-toggle-breakpoint-id,Mosaic/NodeTypes/Navbar/NavbarElementMResourceData.php
62
62
  NavbarElementMResourceData,ElementMResourceDataAbstract,free,createDataSimple,disableScroll,ValidateAllowUndefined|ValidatorAcceptedValues|Validators,0|1,true,,data-animation|data-toggle-breakpoint-id,Mosaic/NodeTypes/Navbar/NavbarElementMResourceData.php
63
- NodeMResourceDataAbstract,MResourceDataAbstract,free,createDataSimple,currentStatus,ValidatorAcceptedValues,,,,,Mosaic/NodeTypes/NodeAbstract/NodeMResourceDataAbstract.php
63
+ NodeMResourceDataAbstract,MResourceDataAbstract,free,createDataSimple,currentStatus,ValidatorAcceptedValues,PUBLISH|DELETE,,,,Mosaic/NodeTypes/NodeAbstract/NodeMResourceDataAbstract.php
64
64
  NodeMResourceDataAbstract,MResourceDataAbstract,free,createDataGroup,override,,,true,,,Mosaic/NodeTypes/NodeAbstract/NodeMResourceDataAbstract.php
65
65
  OpenStreetMapElementMResourceData,ElementMResourceDataAbstract,free,createDataSimple,latitude,ValidateAllowUndefined|ValidatorDynamicCodeObject|ValidatorFloat|ValidatorFloatAsString|Validators,,true,,,Mosaic/NodeTypes/OpenStreetMap/OpenStreetMapElementMResourceData.php
66
66
  OpenStreetMapElementMResourceData,ElementMResourceDataAbstract,free,createDataSimple,longitude,ValidateAllowUndefined|ValidatorDynamicCodeObject|ValidatorFloat|ValidatorFloatAsString|Validators,,true,,,Mosaic/NodeTypes/OpenStreetMap/OpenStreetMapElementMResourceData.php
@@ -134,7 +134,7 @@ SliderElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,aut
134
134
  SliderElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,isCarousel,ValidateAllowUndefined|ValidatorAcceptedValues|Validators,0|1,true,,data-animation-type|data-duration|data-easing|data-autoplay|data-autoplay-delay|data-autoplay-limit|data-carousel|data-default-slide-index|data-slide-label|role|aria-roledescription,Mosaic/NodeTypes/Pro/Slider/SliderElementMResourceData.php
135
135
  SliderElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,defaultSlideIndex,ValidateAllowUndefined|ValidatorInteger|ValidatorIntegerAsString|Validators,,true,,data-animation-type|data-duration|data-easing|data-autoplay|data-autoplay-delay|data-autoplay-limit|data-carousel|data-default-slide-index|data-slide-label|role|aria-roledescription,Mosaic/NodeTypes/Pro/Slider/SliderElementMResourceData.php
136
136
  SliderElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,ariaLabel,ValidateAllowUndefined|ValidatorString|Validators,,true,,data-animation-type|data-duration|data-easing|data-autoplay|data-autoplay-delay|data-autoplay-limit|data-carousel|data-default-slide-index|data-slide-label|role|aria-roledescription,Mosaic/NodeTypes/Pro/Slider/SliderElementMResourceData.php
137
- SliderSlideElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,image,ValidateAllowUndefined|ValidatorAcceptedValues|ValidatorDynamicCodeObject|ValidatorImageAttachment|ValidatorURL|Validators,,true,,,Mosaic/NodeTypes/Pro/Slider/SliderSlides/SliderSlide/SliderSlideElementMResourceData.php
137
+ SliderSlideElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,image,ValidateAllowUndefined|ValidatorAcceptedValues|ValidatorDynamicCodeObject|ValidatorImageAttachment|ValidatorURL|Validators,MosaicDefaultURL,true,,,Mosaic/NodeTypes/Pro/Slider/SliderSlides/SliderSlide/SliderSlideElementMResourceData.php
138
138
  SliderSlideElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,positionX,ValidateAllowUndefined|ValidatorStyleBackground|ValidatorStyleBackgroundPosition|Validators,,true,,,Mosaic/NodeTypes/Pro/Slider/SliderSlides/SliderSlide/SliderSlideElementMResourceData.php
139
139
  SliderSlideElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,positionY,ValidateAllowUndefined|ValidatorStyleBackground|ValidatorStyleBackgroundPosition|Validators,,true,,,Mosaic/NodeTypes/Pro/Slider/SliderSlides/SliderSlide/SliderSlideElementMResourceData.php
140
140
  SliderSlideElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,alt,ValidateAllowUndefined|ValidatorDynamicCodeObject|ValidatorString|Validators,,true,,,Mosaic/NodeTypes/Pro/Slider/SliderSlides/SliderSlide/SliderSlideElementMResourceData.php
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mosaic-headless",
3
- "version": "1.30.0",
3
+ "version": "1.30.1",
4
4
  "description": "AI-agent skill: build Mosaic Pro (Nextend) WordPress sites by writing the underlying data model directly - 23 custom tables, no visual editor, no DOM. Every node type, style property and node property swept against a live install and asserted on the delivered HTML and compiled CSS. Installs into Claude Code, Cursor, Codex CLI, Gemini CLI, Copilot, Continue, Windsurf and Claude.ai.",
5
5
  "keywords": [
6
6
  "wordpress",
@@ -33,6 +33,7 @@
33
33
  "scripts/",
34
34
  "assets/",
35
35
  "data/*.csv",
36
+ "data/browser-verification/*.csv",
36
37
  "references/",
37
38
  "sites/_moksa.py",
38
39
  "sites/_components.py",
@@ -26,9 +26,37 @@ import { fileURLToPath } from "node:url";
26
26
 
27
27
  const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
28
28
  const read = (p) => fs.readFileSync(path.join(ROOT, p), "utf8");
29
+ // A table split one file per page (data/browser-verification/) is still one table:
30
+ // its CSVs, in name order, each with its own header.
31
+ const tableFiles = (p) => fs.statSync(path.join(ROOT, p)).isDirectory()
32
+ ? fs.readdirSync(path.join(ROOT, p)).filter((f) => f.endsWith(".csv")).sort()
33
+ .map((f) => `${p}/${f}`)
34
+ : [p];
29
35
  const problems = [];
30
36
  const fail = (m) => problems.push(m);
31
37
 
38
+ // ---------- the plugin directory's limits -----------------------------------
39
+ //
40
+ // The Claude plugin directory reads the repository root, every tracked file of it,
41
+ // and holds a submission for a reviewer when any file that is not an image or font
42
+ // is over 256 KiB, or when there are more than 512 files. A 400 KB CSV and a 2 MB
43
+ // generated site file did exactly that. Checked here, so it fails on this machine
44
+ // rather than in a review queue a week later.
45
+ try {
46
+ const tracked = execSync("git ls-files -z", { cwd: ROOT }).toString().split("\0").filter(Boolean);
47
+ if (tracked.length > 512) fail(`${tracked.length} tracked files - the plugin directory allows 512`);
48
+ for (const f of tracked) {
49
+ if (/\.(png|jpe?g|gif|webp|svg|ico|woff2?|ttf|otf)$/i.test(f)) continue;
50
+ const full = path.join(ROOT, f);
51
+ if (!fs.existsSync(full)) continue;
52
+ const size = fs.statSync(full).size;
53
+ if (size > 256 * 1024)
54
+ fail(`${f} is ${(size / 1024).toFixed(0)} KiB - the plugin directory holds any non-image file over 256 KiB`);
55
+ }
56
+ } catch {
57
+ // not a git checkout (an unpacked tarball): nothing to measure against
58
+ }
59
+
32
60
  // ---------- versions agree --------------------------------------------------
33
61
 
34
62
  const pkg = JSON.parse(read("package.json"));
@@ -91,13 +119,14 @@ if (!fs.existsSync(path.join(ROOT, pkg.bin["mosaic-headless"])))
91
119
 
92
120
  // ---------- the numbers in the docs are counted, not trusted ----------------
93
121
 
94
- const rows = (p) => read(p).trim().split("\n").length - 1; // minus the header
122
+ const rows = (p) => tableFiles(p)
123
+ .reduce((n, f) => n + read(f).trim().split("\n").length - 1, 0); // minus each header
95
124
  const counts = {
96
125
  "data/node-verification.csv": 126,
97
126
  "data/style-verification.csv": 98,
98
127
  "data/node-property-verification.csv": 191,
99
128
  "data/rwd-verification.csv": 733,
100
- "data/browser-verification.csv": 4132,
129
+ "data/browser-verification": 4132,
101
130
  "data/style-state-verification.csv": 52,
102
131
  "data/interaction-verification.csv": 7,
103
132
  "data/data-class-hierarchy.csv": 125,
@@ -131,7 +160,7 @@ if (!style.includes("SKIPPED")) fail("style-verification.csv has no SKIPPED rows
131
160
 
132
161
  // The browser pass has the same obligation, under a different word: a declaration it
133
162
  // cannot soundly compare must say so rather than be counted as agreement.
134
- const browser = read("data/browser-verification.csv");
163
+ const browser = tableFiles("data/browser-verification").map(read).join("\n");
135
164
  if (!browser.includes("not-comparable"))
136
165
  fail("browser-verification.csv has no not-comparable rows - a computed-value check "
137
166
  + "that claims to compare everything is comparing things it cannot");
@@ -190,13 +219,13 @@ if (/,FAIL,/.test(zip)) fail("data/theme-zip-verification.csv carries a failed c
190
219
  // rwd and browser counts, and must not carry the previous ones.
191
220
  {
192
221
  const fmt = (n) => n.toLocaleString("en-US");
193
- const want = [rows("data/rwd-verification.csv"), rows("data/browser-verification.csv")];
222
+ const want = [rows("data/rwd-verification.csv"), rows("data/browser-verification")];
194
223
  for (const readme of ["README.md", "README.zh-TW.md", "README.ja.md", "README.ko.md"]) {
195
224
  const text = read(readme);
196
225
  for (const n of want)
197
226
  if (!text.includes(fmt(n)) && !text.includes(String(n)))
198
227
  fail(`${readme} does not quote ${fmt(n)} - it is quoting a stale count`);
199
- if (/0 findings|指摘 0 件|지적 0건|0 項發現/.test(text))
228
+ if (/\b0 findings\b|指摘 0 件|지적 0건|0 項發現/.test(text))
200
229
  fail(`${readme} still claims a design audit with 0 findings`);
201
230
  }
202
231
  }
@@ -100,7 +100,7 @@ def extract(plugin_root, out_dir):
100
100
  # ---- and the ones a type heals into itself
101
101
  stem_by_class = {}
102
102
  for path, src in files:
103
- m = re.search(r"class\s+(\w+)ElementTypeFactory", src)
103
+ m = re.search(r"class\s+(\w+)ElementTypeFactory\b", src)
104
104
  if m:
105
105
  stem_by_class[m.group(1)] = stem_to_slug.get(m.group(1), "")
106
106
 
@@ -154,7 +154,7 @@ def extract(plugin_root, out_dir):
154
154
  accepted = []
155
155
  if acc:
156
156
  accepted = re.findall(r"'([^']+)'", acc.group(1))
157
- accepted += [c for c in re.findall(r"\w+::(\w+)", acc.group(1))]
157
+ accepted += [c for c in re.findall(r"\b\w+::(\w+)\b", acc.group(1))]
158
158
  props.append(
159
159
  {
160
160
  "owner_class": owner,
@@ -213,7 +213,7 @@ def tidy(text):
213
213
  report every hairline on the page as overridden."""
214
214
  t = re.sub(r"\s*,\s*", ",", (text or "").strip().lower())
215
215
  t = re.sub(r"\s+", " ", t)
216
- return re.sub(r"(?<![\d.])\.(\d)", r"0.", t)
216
+ return re.sub(r"(?<![\d.])\.(\d)", r"0.\1", t)
217
217
 
218
218
 
219
219
  # The comparison knows properties by their Mosaic key, but customDeclarations hands it raw
@@ -609,7 +609,8 @@ def main():
609
609
  ap.add_argument("--config",
610
610
  help="JSON with base/cookie/nonce; omit it and the MOSAIC_SITE_URL / MOSAIC_REST_COOKIE / MOSAIC_REST_NONCE environment variables supply them instead")
611
611
  ap.add_argument("--site", required=True)
612
- ap.add_argument("--csv", help="the computed-value table")
612
+ ap.add_argument("--csv", help="a DIRECTORY: the computed-value table is "
613
+ "written one file per page, <dir>/<slug>.csv")
613
614
  ap.add_argument("--audit", help="the design-audit findings")
614
615
  ap.add_argument("--ack", default=os.path.join(
615
616
  os.path.dirname(os.path.abspath(__file__)), "..", "data",
@@ -754,13 +755,24 @@ def main():
754
755
  # count in the release gate - should not have to find the blank line between them.
755
756
  # The audit file is written even when it is empty: a header with no rows says
756
757
  # "this ran and found nothing", where a missing file says nothing at all.
758
+ #
759
+ # The computed-value table is one file per page, named by its slug, with no url
760
+ # column: a single file carried the site's address 4,000 times over and passed
761
+ # the plugin directory's 256 KiB per-file limit. The slug is the filename, and
762
+ # the address is whatever --config pointed at.
757
763
  if a.csv:
758
- with open(a.csv, "w", newline="", encoding="utf-8") as fh:
759
- w = csv.writer(fh)
760
- w.writerow(["url", "breakpoint", "attrID", "property", "declared",
761
- "computed", "status"])
762
- w.writerows(rows)
763
- print("\nwrote %s (%d rows)" % (a.csv, len(rows)))
764
+ os.makedirs(a.csv, exist_ok=True)
765
+ per_page = {}
766
+ for r in rows:
767
+ per_page.setdefault(r[0].rstrip("/").rsplit("/", 1)[-1], []).append(r[1:])
768
+ for slug, page_rows in per_page.items():
769
+ out = os.path.join(a.csv, slug + ".csv")
770
+ with open(out, "w", newline="", encoding="utf-8") as fh:
771
+ w = csv.writer(fh)
772
+ w.writerow(["breakpoint", "attrID", "property", "declared",
773
+ "computed", "status"])
774
+ w.writerows(page_rows)
775
+ print("\nwrote %s (%d rows)" % (out, len(page_rows)))
764
776
  if a.audit:
765
777
  with open(a.audit, "w", newline="", encoding="utf-8") as fh:
766
778
  w = csv.writer(fh)
@@ -29,6 +29,7 @@ import argparse
29
29
  import csv
30
30
  import html as htmllib
31
31
  import json
32
+ import os
32
33
  import re
33
34
  import sys
34
35
  import time
@@ -129,7 +130,7 @@ def main():
129
130
  ap.add_argument("--report", help="the CSV from_elementor.py wrote")
130
131
  ap.add_argument("--csv")
131
132
  ap.add_argument("--width", type=int, default=1440)
132
- ap.add_argument("--browser", help="browser-verification CSV from verify_browser.py")
133
+ ap.add_argument("--browser", help="browser-verification CSV, or the per-page directory, from verify_browser.py")
133
134
  ap.add_argument("--audit", help="design-audit CSV from verify_browser.py")
134
135
  ap.add_argument("--rwd", help="rwd-verification CSV from verify_rwd.py")
135
136
  ap.add_argument("--prefix", default="el",
@@ -230,7 +231,10 @@ def main():
230
231
  "own media query" % len(rr) if not bad else
231
232
  "%d of %d not verified" % (len(bad), len(rr)))
232
233
  if a.browser:
233
- br = list(csv.DictReader(open(a.browser, encoding="utf-8")))
234
+ # a file, or the per-page directory verify_browser.py writes
235
+ files = ([os.path.join(a.browser, f) for f in sorted(os.listdir(a.browser))
236
+ if f.endswith(".csv")] if os.path.isdir(a.browser) else [a.browser])
237
+ br = [r for f in files for r in csv.DictReader(open(f, encoding="utf-8"))]
234
238
  ok = sum(1 for r in br if r.get("status") == "ok")
235
239
  over = [r for r in br if r.get("status") == "OVERRIDDEN"]
236
240
  check("COMPUTED", bool(br) and not over,