mosaic-headless 1.2.1 → 1.3.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/README.ja.md +16 -1
- package/README.ko.md +16 -1
- package/README.md +17 -1
- package/README.zh-TW.md +14 -1
- package/SKILL.md +77 -5
- package/assets/templates/platforms/claude-ai.json +1 -1
- package/assets/templates/platforms/claude-code.json +1 -1
- package/assets/templates/platforms/codex-cli.json +1 -1
- package/assets/templates/platforms/copilot.json +1 -1
- package/assets/templates/platforms/gemini-cli.json +1 -1
- package/bin/check-release.mjs +20 -0
- package/data/browser-verification.csv +2238 -0
- package/data/data-class-hierarchy.csv +122 -0
- package/data/design-audit.csv +1 -0
- package/data/node-property-verification.csv +2 -2
- package/data/node-types.csv +123 -123
- package/package.json +1 -1
- package/references/failure-modes.md +52 -0
- package/sites/_moksa.py +35 -19
- package/sites/moksa.json +55 -51
- package/tools/build_page.py +34 -1
- package/tools/extract_node_types.py +26 -1
- package/tools/mo.py +686 -0
- package/tools/sweep_node_properties.py +41 -2
- package/tools/verify_browser.py +589 -0
package/tools/build_page.py
CHANGED
|
@@ -140,6 +140,10 @@ class Surface:
|
|
|
140
140
|
# string to them is accepted and then silently produces `transform:none`, `box-shadow:none`
|
|
141
141
|
# or no rule at all - so these shorthands exist to make the correct shape unavoidable.
|
|
142
142
|
# Each was confirmed against the compiled CSS; see references/styling.md.
|
|
143
|
+
|
|
144
|
+
# A fixed namespace, so a design token's row ID is a function of its name alone and
|
|
145
|
+
# a rebuild rebinds the same row instead of adding a second one.
|
|
146
|
+
VAR_NAMESPACE = uuid.UUID("6d6f7361-6963-4865-6164-6c657373ff01")
|
|
143
147
|
VAR_IDS = {}
|
|
144
148
|
|
|
145
149
|
|
|
@@ -245,8 +249,27 @@ def theme_records(spec, doc, surface):
|
|
|
245
249
|
if not (collection and mode and skin):
|
|
246
250
|
sys.exit("theme.variables needs a healed collection/mode/skin")
|
|
247
251
|
recs = []
|
|
252
|
+
# The ID is DERIVED from the token name, not minted.
|
|
253
|
+
#
|
|
254
|
+
# It used to be a fresh `uuid4()` per run, and `_varIDs` starts empty every
|
|
255
|
+
# time, so each build wrote a NEW collectionVariable row and left the old one
|
|
256
|
+
# in place. The rows are parented to the theme's collection, which survives
|
|
257
|
+
# `build_site.py` making a fresh master, so they accumulate - and both end up
|
|
258
|
+
# in `:root`, where the later one wins. Measured: after changing `--mk-faint`
|
|
259
|
+
# from rgb(160,162,168) to rgb(107,109,113), the delivered stylesheet carried
|
|
260
|
+
#
|
|
261
|
+
# --mk-faint: rgb(107, 109, 113)
|
|
262
|
+
# --mk-faint: rgb(160, 162, 168)
|
|
263
|
+
#
|
|
264
|
+
# and the page kept rendering the old grey. Every server-side check passed:
|
|
265
|
+
# the commit succeeded, the new variable existed, its value was correct. Only
|
|
266
|
+
# `verify_browser.py`, reading the computed colour off the element, saw it.
|
|
267
|
+
#
|
|
268
|
+
# A UUIDv5 over the custom property is stable across runs and across machines
|
|
269
|
+
# with no state to carry, so a rebuild REBINDS the row instead of adding one.
|
|
248
270
|
for custom_property, value in variables.items():
|
|
249
|
-
vid =
|
|
271
|
+
vid = str(uuid.uuid5(VAR_NAMESPACE, custom_property))
|
|
272
|
+
spec.setdefault("_varIDs", {})[custom_property] = vid
|
|
250
273
|
recs.append({"newRevisionRecord": {
|
|
251
274
|
"ID": vid, "parentType": "collection", "parentID": collection,
|
|
252
275
|
"ordering": "a0", "status": "publish", "revision": "", "version": "",
|
|
@@ -255,6 +278,16 @@ def theme_records(spec, doc, surface):
|
|
|
255
278
|
"customProperty": custom_property,
|
|
256
279
|
"skinsData": {skin: {mode: {"value": value["value"]}}}}},
|
|
257
280
|
"originalRevisionRecord": None})
|
|
281
|
+
# Reap strays from before the ID became derivable. Anything on this
|
|
282
|
+
# collection that claims a custom property we manage, under an ID we did not
|
|
283
|
+
# derive, is a leftover that can still win the cascade.
|
|
284
|
+
managed = {v["ID"] for v in (r["newRevisionRecord"] for r in recs)}
|
|
285
|
+
for row in (doc.get("collectionVariable") or []):
|
|
286
|
+
cp = (row.get("data") or {}).get("customProperty")
|
|
287
|
+
if cp in variables and row["ID"] not in managed:
|
|
288
|
+
print(" reaping stale variable %s (%s)" % (cp, row["ID"][:8]))
|
|
289
|
+
recs.append({"newRevisionRecord": dict(row, status="delete"),
|
|
290
|
+
"originalRevisionRecord": row})
|
|
258
291
|
out["collectionVariable"] = recs
|
|
259
292
|
# register the IDs before element-class styles are expanded - those cite
|
|
260
293
|
# tokens too, and expand_shorthands resolves {"token": ...} from this map
|
|
@@ -78,10 +78,21 @@ def extract(plugin_root, out_dir):
|
|
|
78
78
|
relpath = rel(path, plugin_root)
|
|
79
79
|
# the data class declaring this type's own properties sits beside the factory;
|
|
80
80
|
# node-properties.csv is keyed by that class name, so record it to make the join
|
|
81
|
-
|
|
81
|
+
#
|
|
82
|
+
# Its OWN parent has to be recorded here too. A data class that declares no
|
|
83
|
+
# properties of its own writes no row into node-properties.csv, so the
|
|
84
|
+
# `extends` column there cannot tell you what it inherits - and 64 of the 122
|
|
85
|
+
# types are exactly that case. Without this column a lookup for
|
|
86
|
+
# `accordion-content` reports zero properties, when in fact it has the nine
|
|
87
|
+
# every element has. An empty answer that looks like a real one is the
|
|
88
|
+
# failure this whole skill argues against, so read the parent off the class.
|
|
89
|
+
data_class, data_extends = "", ""
|
|
82
90
|
for sibling in sorted(os.listdir(os.path.dirname(path))):
|
|
83
91
|
if sibling.endswith("MResourceData.php"):
|
|
84
92
|
data_class = sibling[:-4]
|
|
93
|
+
dcls = RE_CLASS.search(read(os.path.join(os.path.dirname(path),
|
|
94
|
+
sibling)))
|
|
95
|
+
data_extends = dcls.group(3) if dcls else ""
|
|
85
96
|
break
|
|
86
97
|
bools = dict(RE_METHOD_BOOL.findall(src))
|
|
87
98
|
alias = RE_ALIAS.search(src)
|
|
@@ -100,15 +111,26 @@ def extract(plugin_root, out_dir):
|
|
|
100
111
|
"can_be_parent": bools.get("canBeParentFor", ""),
|
|
101
112
|
"default_element_class": (dflt.group(1).strip() if dflt else ""),
|
|
102
113
|
"data_class": data_class,
|
|
114
|
+
"data_extends": data_extends,
|
|
103
115
|
"file": relpath,
|
|
104
116
|
}
|
|
105
117
|
)
|
|
106
118
|
|
|
119
|
+
# Every data class, whether or not it declares a property of its own. An abstract
|
|
120
|
+
# that adds nothing still sits in the chain - `ElementMResourceLoopDataAbstract`
|
|
121
|
+
# is one, and five node types inherit through it - so a hierarchy built only from
|
|
122
|
+
# classes that happen to own properties has holes exactly where the plain ones
|
|
123
|
+
# are. This file makes the walk total.
|
|
124
|
+
hierarchy = []
|
|
107
125
|
props = []
|
|
108
126
|
for path in sorted(walk(node_root, ("MResourceData.php", "MResourceDataAbstract.php"))):
|
|
109
127
|
src = read(path)
|
|
110
128
|
cls = RE_CLASS.search(src)
|
|
111
129
|
owner = cls.group(2) if cls else os.path.basename(path)[:-4]
|
|
130
|
+
hierarchy.append({"class": owner,
|
|
131
|
+
"extends": cls.group(3) if cls else "",
|
|
132
|
+
"abstract": "yes" if (cls and cls.group(1)) else "no",
|
|
133
|
+
"file": rel(path, plugin_root)})
|
|
112
134
|
relpath = rel(path, plugin_root)
|
|
113
135
|
reserved = RE_RESERVED.search(src)
|
|
114
136
|
reserved_attrs = re.findall(r"'([^']+)'", reserved.group(1)) if reserved else []
|
|
@@ -146,6 +168,9 @@ def extract(plugin_root, out_dir):
|
|
|
146
168
|
os.makedirs(out_dir, exist_ok=True)
|
|
147
169
|
write_csv(os.path.join(out_dir, "node-types.csv"), types)
|
|
148
170
|
write_csv(os.path.join(out_dir, "node-properties.csv"), props)
|
|
171
|
+
write_csv(os.path.join(out_dir, "data-class-hierarchy.csv"),
|
|
172
|
+
sorted(hierarchy, key=lambda r: r["class"]))
|
|
173
|
+
print("data classes: %d" % len(hierarchy))
|
|
149
174
|
print("node types: %d (%d pro)" % (len(types), sum(t["edition"] == "pro" for t in types)))
|
|
150
175
|
print("node properties: %d across %d classes" % (len(props), len({p["owner_class"] for p in props})))
|
|
151
176
|
|