mosaic-headless 1.2.0 → 1.2.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.
Files changed (41) hide show
  1. package/README.ja.md +3 -3
  2. package/README.ko.md +96 -0
  3. package/README.md +13 -4
  4. package/README.zh-TW.md +3 -3
  5. package/SKILL.md +3 -3
  6. package/assets/templates/platforms/claude-ai.json +1 -1
  7. package/assets/templates/platforms/claude-code.json +1 -1
  8. package/assets/templates/platforms/codex-cli.json +1 -1
  9. package/assets/templates/platforms/copilot.json +1 -1
  10. package/assets/templates/platforms/gemini-cli.json +1 -1
  11. package/bin/check-release.mjs +9 -2
  12. package/data/animatable-properties.csv +23 -23
  13. package/data/condition-comparators.csv +13 -13
  14. package/data/condition-subjects.csv +60 -60
  15. package/data/db-columns.csv +207 -207
  16. package/data/default-children.csv +11 -11
  17. package/data/dynamic-variables.csv +75 -75
  18. package/data/element-classes.csv +152 -152
  19. package/data/evaluator-functions.csv +20 -20
  20. package/data/interaction-types.csv +13 -13
  21. package/data/node-properties.csv +182 -182
  22. package/data/node-property-verification.csv +182 -182
  23. package/data/node-types.csv +123 -123
  24. package/data/node-verification.csv +123 -123
  25. package/data/placement-rules.csv +123 -123
  26. package/data/pluggables.csv +208 -208
  27. package/data/property-verification.csv +171 -171
  28. package/data/rest-routes.csv +115 -115
  29. package/data/rwd-verification.csv +577 -570
  30. package/data/style-properties.csv +99 -99
  31. package/data/style-states.csv +54 -54
  32. package/data/style-value-shapes.csv +23 -23
  33. package/data/style-verification.csv +99 -99
  34. package/package.json +59 -59
  35. package/references/failure-modes.md +22 -0
  36. package/sites/_moksa.py +72 -26
  37. package/sites/moksa.json +9071 -8684
  38. package/tools/verify_rwd.py +14 -1
  39. package/README.zh-CN.md +0 -87
  40. package/tools/build_all.py +0 -55
  41. package/tools/build_report.py +0 -221
package/package.json CHANGED
@@ -1,59 +1,59 @@
1
- {
2
- "name": "mosaic-headless",
3
- "version": "1.2.0",
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
- "keywords": [
6
- "wordpress",
7
- "mosaic",
8
- "mosaic-pro",
9
- "nextend",
10
- "page-builder",
11
- "claude-code",
12
- "agent-skill",
13
- "ai-agent",
14
- "headless",
15
- "woocommerce",
16
- "wp-cli"
17
- ],
18
- "homepage": "https://github.com/Moksa1123/mosaic-headless#readme",
19
- "repository": {
20
- "type": "git",
21
- "url": "git+https://github.com/Moksa1123/mosaic-headless.git"
22
- },
23
- "bugs": {
24
- "url": "https://github.com/Moksa1123/mosaic-headless/issues"
25
- },
26
- "license": "MIT",
27
- "author": "moksa (https://moksaweb.com)",
28
- "type": "module",
29
- "bin": {
30
- "mosaic-headless": "bin/install.mjs"
31
- },
32
- "files": [
33
- "bin/",
34
- "assets/",
35
- "data/*.csv",
36
- "references/",
37
- "sites/_moksa.py",
38
- "sites/moksa.json",
39
- "tools/*.py",
40
- "tools/*.php",
41
- "SKILL.md",
42
- "README.md",
43
- "README.zh-TW.md",
44
- "README.zh-CN.md",
45
- "README.ja.md",
46
- "LICENSE"
47
- ],
48
- "engines": {
49
- "node": ">=18"
50
- },
51
- "scripts": {
52
- "install-skill": "node bin/install.mjs",
53
- "check": "node bin/check-release.mjs",
54
- "preversion": "npm run check",
55
- "version": "node bin/sync-version.mjs && git add SKILL.md assets/templates/platforms",
56
- "postversion": "git push && git push --tags",
57
- "prepublishOnly": "npm run check"
58
- }
59
- }
1
+ {
2
+ "name": "mosaic-headless",
3
+ "version": "1.2.1",
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
+ "keywords": [
6
+ "wordpress",
7
+ "mosaic",
8
+ "mosaic-pro",
9
+ "nextend",
10
+ "page-builder",
11
+ "claude-code",
12
+ "agent-skill",
13
+ "ai-agent",
14
+ "headless",
15
+ "woocommerce",
16
+ "wp-cli"
17
+ ],
18
+ "homepage": "https://github.com/Moksa1123/mosaic-headless#readme",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/Moksa1123/mosaic-headless.git"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/Moksa1123/mosaic-headless/issues"
25
+ },
26
+ "license": "MIT",
27
+ "author": "moksa (https://moksaweb.com)",
28
+ "type": "module",
29
+ "bin": {
30
+ "mosaic-headless": "bin/install.mjs"
31
+ },
32
+ "files": [
33
+ "bin/",
34
+ "assets/",
35
+ "data/*.csv",
36
+ "references/",
37
+ "sites/_moksa.py",
38
+ "sites/moksa.json",
39
+ "tools/*.py",
40
+ "tools/*.php",
41
+ "SKILL.md",
42
+ "README.md",
43
+ "README.zh-TW.md",
44
+ "README.ja.md",
45
+ "README.ko.md",
46
+ "LICENSE"
47
+ ],
48
+ "engines": {
49
+ "node": ">=18"
50
+ },
51
+ "scripts": {
52
+ "install-skill": "node bin/install.mjs",
53
+ "check": "node bin/check-release.mjs",
54
+ "preversion": "npm run check",
55
+ "version": "node bin/sync-version.mjs && git add SKILL.md assets/templates/platforms",
56
+ "postversion": "git push && git push --tags",
57
+ "prepublishOnly": "npm run check"
58
+ }
59
+ }
@@ -171,6 +171,28 @@ required children on commit.
171
171
  A record committed with `"modified_gmt": "2026-09-05 17:00:00"` came back stored as
172
172
  `16:59:16`. The server overwrites it. Do not use a value you sent as a local cache key.
173
173
 
174
+ ## A page cache turns "it worked" into "the node does not exist"
175
+
176
+ Not a Mosaic failure - a failure of *checking* Mosaic, and it produces the exact
177
+ result this skill exists to prevent: a confident negative about something that is
178
+ actually there.
179
+
180
+ Commit succeeds. `build_site.py` reports the byte size the renderer produced. Then
181
+ the verifier fetches the URL, a full-page cache in front of WordPress (Varnish on
182
+ Cloudways, or any CDN) answers with the pre-commit HTML, and every declaration on a
183
+ node you have just added comes back `no-element`. Measured on this install: the
184
+ builder saw 138878 bytes, `curl` on the same URL a second later got 136021, and
185
+ seven declarations on two brand-new grid wrappers were reported missing while the
186
+ wrappers were sitting correctly in the database and in the committed JSON.
187
+
188
+ The tell is the byte count: if the size the builder reports and the size you fetch
189
+ disagree, you are not looking at what you wrote.
190
+
191
+ `tools/verify_rwd.py` now appends a unique `_v=<ms>` query string and sends
192
+ `Cache-Control: no-cache` on every fetch, so the assertion is always made against the
193
+ document that was actually committed. Do the same in anything else that checks a page
194
+ - including a browser: hard-reload is not always enough, a query string always is.
195
+
174
196
  ## Reproducing all of this
175
197
 
176
198
  ```bash
package/sites/_moksa.py CHANGED
@@ -131,7 +131,7 @@ def mono(text, size="11px", color="--mk-muted", track="0.14em", **st):
131
131
  fontSize=size, letterSpacing=track, **st)
132
132
 
133
133
 
134
- def figure(attr, num, size="40px", t="34px", m="27px"):
134
+ def figure(attr, num, size="56px", t="44px", m="34px"):
135
135
  """A number set as one masked box per digit.
136
136
 
137
137
  A count-up needs JavaScript; a digit rolling out from behind its own edge does
@@ -149,8 +149,8 @@ def figure(attr, num, size="40px", t="34px", m="27px"):
149
149
  "overflow:hidden;display:inline-block;"
150
150
  "--d:%dms;" % (j * 70)}}},
151
151
  "children": [T("h3", ch, color={"token": "--mk-ink"}, fontSize=size,
152
- fontWeight="500", letterSpacing="-0.04em",
153
- lineHeight="1.05", fontFamily=MONO,
152
+ fontWeight="600", letterSpacing="-0.03em",
153
+ lineHeight="1.02", fontFamily=DISPLAY,
154
154
  _t={"fontSize": t}, _m={"fontSize": m})]}
155
155
  for j, ch in enumerate(num)
156
156
  ]}
@@ -172,7 +172,7 @@ def clause(num, en, zh, attr):
172
172
  box(attr + "-t", {}, [
173
173
  mono(en, size="11px", color="--mk-muted"),
174
174
  T("h2", zh, color={"token": "--mk-ink"}, fontSize="34px",
175
- fontWeight="600", letterSpacing="-0.025em", lineHeight="1.3",
175
+ fontWeight="600", letterSpacing="0.01em", lineHeight="1.35",
176
176
  fontFamily=DISPLAY, marginTop="10px",
177
177
  _t={"fontSize": "29px"}, _m={"fontSize": "23px"}),
178
178
  ]),
@@ -234,6 +234,16 @@ SPEC = [
234
234
  ("SUPPORT", "876", "技術支援與服務"),
235
235
  ]
236
236
 
237
+ # The masthead's right-hand column. A profile document states its terms in its own
238
+ # header, and it is the content that fills the frame the headline leaves empty.
239
+ SIDE = [
240
+ ("DISCIPLINE", "Web / AI / Automation"),
241
+ ("STACK", "WordPress · n8n · Claude"),
242
+ ("ENGAGEMENT", "專案制 / 長期維運"),
243
+ ("LEAD TIME", "3–8 週"),
244
+ ("RESPONSE", "1 個工作天"),
245
+ ]
246
+
237
247
  SERVICES = [
238
248
  ("01", "WEB & E-COMMERCE", "網站與電商",
239
249
  "品牌官網、電商平台與 WordPress 開發,乾淨的結構與好管理的後台。"),
@@ -659,7 +669,7 @@ FOOTER = box("mk-footer",
659
669
  mono("MAKE WEB MEANINGFUL", size="11px", color="--mk-accent",
660
670
  track="0.2em"),
661
671
  T("h2", "打造有價值的網站體驗。", color={"token": "--mk-paper"},
662
- fontSize="30px", fontWeight="500", letterSpacing="-0.01em",
672
+ fontSize="30px", fontWeight="500", letterSpacing="0.02em",
663
673
  lineHeight="1.5", fontFamily=CJK, marginTop="16px",
664
674
  _m={"fontSize": "22px"}),
665
675
  box("mk-f-contact",
@@ -712,8 +722,8 @@ FOOTER = box("mk-footer",
712
722
 
713
723
  # ── the page ──────────────────────────────────────────────────────────────────
714
724
  HERO_LINE = dict(color={"token": "--mk-ink"}, fontSize="62px", fontWeight="600",
715
- lineHeight="1.12", letterSpacing="-0.035em", fontFamily=DISPLAY,
716
- _t={"fontSize": "46px"}, _m={"fontSize": "31px"})
725
+ lineHeight="1.14", letterSpacing="0.005em", fontFamily=DISPLAY,
726
+ _t={"fontSize": "48px"}, _m={"fontSize": "32px"})
717
727
 
718
728
  MASTHEAD = section("mk-mast", [wrap("mk-mast-in", [
719
729
  # the document's own header block: what this is, where it is from, which revision
@@ -733,11 +743,19 @@ MASTHEAD = section("mk-mast", [wrap("mk-mast-in", [
733
743
  {"display": "flex", "alignItems": "center", "marginTop": "10px"},
734
744
  [box("mk-caret", {}, [])]),
735
745
  ]),
746
+ box("mk-mast-body",
747
+ {"display": "grid", "gridCols": "1.35fr .65fr", "columnGap": "56px",
748
+ "rowGap": "32px", "alignItems": "start", "marginTop": "26px"},
749
+ _t={"gridCols": "1.2fr .8fr", "columnGap": "36px"},
750
+ _m={"gridCols": "repeat(1, 1fr)", "columnGap": "0px", "rowGap": "26px",
751
+ "marginTop": "20px"},
752
+ children=[
753
+ box("mk-mast-left", {}, [
736
754
  {"type": "text", "data": {"tagName": "p", "attrID": "mk-mast-lede"},
737
755
  "style": bp({"color": {"token": "--mk-muted"}, "fontSize": "16px",
738
- "lineHeight": "2", "marginTop": "24px", "maxWidth": "34em"},
756
+ "lineHeight": "2", "marginTop": "0px", "maxWidth": "30em"},
739
757
  None,
740
- {"fontSize": "14px", "lineHeight": "1.95", "marginTop": "20px"}),
758
+ {"fontSize": "14px", "lineHeight": "1.95", "marginTop": "0px"}),
741
759
  "text": "我打造網站、開發軟體、導入 AI、串起自動化流程,"
742
760
  "讓技術不只是工具,而是幫你省下時間、長出業績的數位夥伴。"},
743
761
  {"type": "div", "data": {"attrID": "mk-mast-cta"},
@@ -747,19 +765,46 @@ MASTHEAD = section("mk-mast", [wrap("mk-mast-in", [
747
765
  "children": [
748
766
  # underlined links, not buttons: a document points, it does not sell
749
767
  {"type": "button", "data": {"attrID": "mk-cta-%d" % n, "url": href},
750
- "style": {"&": {"_": {"backgroundColor": "rgba(0,0,0,0)",
751
- "color": {"token": "--mk-ink"}, "fontSize": "14px",
752
- "fontFamily": MONO, "letterSpacing": "0.06em",
753
- "paddingTop": "8px", "paddingBottom": "8px",
754
- "radius": "0px", "cursor": "pointer",
755
- "customStyles":
756
- "border-bottom:1px solid rgb(22,24,28);",
757
- "transitionAll": "180ms ease"}},
758
- "hover": {"_": {"color": {"token": "--mk-accent"}}}},
768
+ "style": {"&": {"_": dict(
769
+ {"backgroundColor": {"token": "--mk-accent"},
770
+ "color": {"token": "--mk-paper"},
771
+ "paddingLeft": "22px", "paddingRight": "22px",
772
+ "paddingTop": "13px", "paddingBottom": "13px",
773
+ "customStyles": "border:1px solid rgb(255,90,54);"}
774
+ if n == 1 else
775
+ {"backgroundColor": "rgba(0,0,0,0)", "color": {"token": "--mk-ink"},
776
+ "paddingLeft": "0px", "paddingRight": "0px",
777
+ "paddingTop": "13px", "paddingBottom": "13px",
778
+ "customStyles": "border:1px solid rgba(0,0,0,0);"
779
+ "border-bottom-color:rgb(22,24,28);"},
780
+ fontSize="13px", fontFamily=MONO, letterSpacing="0.1em",
781
+ radius="0px", cursor="pointer", transitionAll="180ms ease")},
782
+ "hover": {"_": ({"backgroundColor": {"token": "--mk-ink"},
783
+ "customStyles": "border:1px solid rgb(22,24,28);"}
784
+ if n == 1
785
+ else {"color": {"token": "--mk-accent"}})}},
759
786
  "text": text}
760
- for n, (text, href) in enumerate([("→ 服務項目", "#services"),
761
- ("→ 精選作品", "#works")], start=1)
787
+ for n, (text, href) in enumerate([("服務項目 →", "#services"),
788
+ ("精選作品 →", "#works")], start=1)
762
789
  ]},
790
+ ]),
791
+ # the right-hand column: what a spec sheet puts in its header block
792
+ box("mk-mast-side",
793
+ {"display": "grid", "rowGap": "0px",
794
+ "customStyles": "border-top:1px solid " + RULE_INK + ";"},
795
+ _m={"customStyles": "border-top:1px solid " + RULE + ";"},
796
+ children=[
797
+ box("mk-mast-side-%d" % i,
798
+ {"display": "grid", "gridCols": "1fr auto",
799
+ "columnGap": "14px", "alignItems": "baseline",
800
+ "paddingTop": "11px", "paddingBottom": "11px",
801
+ "customStyles": ("" if i == 0
802
+ else "border-top:1px solid " + RULE + ";")},
803
+ [mono(k, size="10px", color="--mk-faint", track="0.18em"),
804
+ mono(v, size="12px", color="--mk-ink", track="0.02em")])
805
+ for i, (k, v) in enumerate(SIDE)
806
+ ]),
807
+ ]),
763
808
  box("mk-spec-rule", {"marginTop": "56px"}, [], _m={"marginTop": "40px"}),
764
809
  # the figures as a datasheet, not as four big numbers looking for attention
765
810
  grid("mk-spec", 4, "0px", tcols=2, mcols=2,
@@ -787,7 +832,8 @@ MASTHEAD = section("mk-mast", [wrap("mk-mast-in", [
787
832
  "columnGap": "3px", "marginTop": "12px"},
788
833
  [figure("mk-fig-%d" % i, num),
789
834
  T("p", "+", color={"token": "--mk-accent"},
790
- fontSize="17px", fontFamily=MONO, fontWeight="500")]),
835
+ fontSize="22px", fontFamily=DISPLAY, fontWeight="600",
836
+ _m={"fontSize": "16px"})]),
791
837
  T("p", zh, color={"token": "--mk-muted"}, fontSize="12px",
792
838
  marginTop="10px", lineHeight="1.7", _m={"fontSize": "11px"}),
793
839
  ])
@@ -970,8 +1016,8 @@ PRODUCT_SEC = section("products", [wrap("mk-prod-in", [
970
1016
  mono("OUR PRODUCTS", size="11px", color="rgb(140,142,150)"),
971
1017
  T("h2", "不只接案,也開發自己的產品",
972
1018
  color={"token": "--mk-paper"}, fontSize="34px",
973
- fontWeight="600", letterSpacing="-0.025em",
974
- lineHeight="1.3", fontFamily=DISPLAY, marginTop="10px",
1019
+ fontWeight="600", letterSpacing="0.01em",
1020
+ lineHeight="1.35", fontFamily=DISPLAY, marginTop="10px",
975
1021
  _t={"fontSize": "29px"}, _m={"fontSize": "23px"}),
976
1022
  ]),
977
1023
  ]),
@@ -995,7 +1041,7 @@ PRODUCT_SEC = section("products", [wrap("mk-prod-in", [
995
1041
  T("h3", name, color={"token": "--mk-paper"},
996
1042
  fontSize="24px", fontWeight="600",
997
1043
  fontFamily=DISPLAY, marginTop="9px",
998
- letterSpacing="-0.02em", _m={"fontSize": "21px"}),
1044
+ letterSpacing="0.01em", _m={"fontSize": "21px"}),
999
1045
  # a property of the product, not a column of the
1000
1046
  # table: as a fourth grid child it landed alone in
1001
1047
  # the 72px number gutter on row two at tablet
@@ -1063,8 +1109,8 @@ CONTACT = section("contact", [wrap("mk-contact-in", [
1063
1109
  mono("START A PROJECT", size="11px", color="--mk-muted"),
1064
1110
  ml("h2", "準備好升級\n你的數位競爭力了嗎?",
1065
1111
  color={"token": "--mk-ink"}, fontSize="44px",
1066
- fontWeight="600", letterSpacing="-0.03em",
1067
- lineHeight="1.24", fontFamily=DISPLAY, marginTop="12px",
1112
+ fontWeight="600", letterSpacing="0.01em",
1113
+ lineHeight="1.3", fontFamily=DISPLAY, marginTop="12px",
1068
1114
  _t={"fontSize": "36px"}, _m={"fontSize": "26px"}),
1069
1115
  T("p", "先看方案抓預算,或直接告訴我們你想解決的問題,"
1070
1116
  "一個工作天內回覆。",