mosaic-headless 1.15.1 → 1.16.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 CHANGED
@@ -1,5 +1,7 @@
1
1
  # mosaic-headless
2
2
 
3
+ [![npm downloads](https://img.shields.io/npm/dt/mosaic-headless?label=npm%20downloads&color=cb3837)](https://www.npmjs.com/package/mosaic-headless)
4
+
3
5
  [Mosaic Pro](https://mosaicbuilder.com)(Nextend)のサイトを、データモデルを直接書いて
4
6
  構築・変更する — ビジュアルエディタも DOM も使わない。
5
7
 
@@ -21,9 +23,12 @@ fractional-index の文字列。エディタはこのモデルの一クライア
21
23
  `data/` を引くこと。**
22
24
 
23
25
  しかも grep ではなく `mo.py` で引く。grep は打った質問には答えるが、本当に抱えている
24
- 質問には答えない。`accordion-content` を grep すればその型が存在することは分かるが、
25
- それを置くと commit は綺麗に通り、そのあと公開ページ全体が 54 バイトのエラー文字列に
26
- なることは教えてくれない。
26
+ 質問には答えない。`accordion-content` を grep すればその型が存在することは分かる。
27
+ スイープ表は BROKE_PAGE だと言う — 置けば公開ページ全体が 54 バイトのエラー文字列に
28
+ なる。どちらも本当で、どちらも間違った答えだ。行の横の注記は、その文字列が「親がない」
29
+ ことを名指ししていること、`accordion > accordion-item` の下に入れ子にすれば commit も
30
+ 描画も通り、キーボードで操作できる開閉 UI までついてくることを言っている。
31
+ `mo.py type` はその三つを一度に見せる。
27
32
 
28
33
  ```bash
29
34
  python tools/mo.py type accordion-content # 1 つの型を、全ライブスイープに接続して表示
@@ -60,14 +65,16 @@ URL に対応するテンプレなし HTTP 406、未ログインには空の b
60
65
  | **ノードタイプ** | 122 / 122。1 ドキュメントにつき 1 タイプで commit → 描画 → 断言 → 削除。70 RENDERED、30 COMMITTED、15 COMMIT_5xx、7 BROKE_PAGE |
61
66
  | **スタイルプロパティ** | 98 / 98 を実ページに書き、コンパイル済み CSS と照合:58 COMPILED、18 ABSENT、21 SKIPPED |
62
67
  | **ノードプロパティ** | 181 / 181 を、各プロパティ自身の validator chain から導いた値で再測定:35 APPLIED、42 NO_EFFECT、55 NO_HOST、47 SKIPPED |
63
- | **レスポンシブ** | 2 サイト計 688 件の `_t`/`_m` 宣言を、サイトが実際に配信したスタイルシートに対して 1 件ずつ断言 — 全件検証済み |
68
+ | **レスポンシブ** | 2 サイト計 731 件の `_t`/`_m` 宣言を、サイトが実際に配信したスタイルシートに対して 1 件ずつ断言 — 全件検証済み |
64
69
  | **コンポーネント** | コンポーネント機構を端から端まで実行し **8 / 8**:カテゴリ配下に作成、ドキュメントを heal、書き込み可能な instance 経由でツリーを投入、読み取り専用の方は同じ書き込みを拒否(ネガティブコントロール)、最後に 2 つのインスタンスが 1 つの定義から描画 |
65
70
  | **スタイル状態** | 53 状態のうち 52 を実ページに書き、テーブルが約束するセレクタと照合:**36 件が完全一致**、12 件 NO_HOST、3 件 SKIPPED、1 件 BROKE_PAGE。どの要素にも使えるグローバル 7 状態はすべて検証済み |
66
71
  | **インタラクション** | JS アニメーション経路を、ネガティブコントロール付きで保存行を読み戻しながら検証:`propertyMetas` は**受理され保存される**。プロパティ値は依然としてバインドされないが、その境界は厳密になった |
67
- | **入場アニメーション** | ページ読み込み後の 10 時点をサンプリングし 7 項目を断言 — 再生されること、アニメーションする `@property` カウンタが 100 に達すること、ベールがヒットテストから外れること、ビューポート内に opacity 0 のまま取り残された要素がないこと、実際のクリックが文書に届くこと、そして `prefers-reduced-motion` ではベールがそもそも存在しないこと |
68
- | **ブラウザ** | 配信されたページを Chromium の 3 つのビューポートで計算スタイル 3,841 件読み取り:2,929 件が一致、912 件は比較不能として明示、**上書き 0 件** |
69
- | **デザイン監査** | コントラスト、フォントフォールバック、CJK の字送り、横溢れ、テキストの切れ、1 行の文字数 — ブラウザ上で実行、**指摘 0 件** |
70
- | **テーマの書き出し/読み込み** | 往復検証済み:テーマ全体を書き出し、コピーとして読み込み、そのコピーがバイト単位で同一のページを配信 |
72
+ | **入場アニメーション** | 単調時計でページ読み込み後の 15 時点をサンプリングし 8 項目を断言 — 再生されること、アニメーションする `@property` カウンタが 100 に達すること、ベールがヒットテストから外れること、ビューポート内に opacity 0 のまま取り残された要素がないこと、実際のクリックが文書に届くこと、`prefers-reduced-motion` ではベールがそもそも存在しないこと、そして全てが落ち着いた後もなお動いているものがあること。文書の初回レイアウトを待ってから始まるため、アニメーションの全くないページに対して遅れフレームは 1 つだけ |
73
+ | **常時アニメーション** | 隅で刷り続け、タップすると拡大する版 — **28 項目**:一時停止したタイムラインをスクラブして周期性を証明、5 つの幅 × 25 のスクロール位置でテキスト*と*操作要素の遮蔽を「決して読めない」を失敗条件として計測、ポインタでも Enter でも開くこと、reduced motion では静止すること。Mosaic 自身のアコーディオンの上に構築 |
74
+ | **アコーディオン** | `accordion-item` と `accordion-content` はスイープ表では BROKE_PAGE。ファクトリが要求する通りに入れ子にすれば commit も描画も通る、**7 件中 7 件**。注記はその行の横にある |
75
+ | **ブラウザ** | 配信された 2 ページを Chromium の 3 つのビューポートで計算スタイル 3,988 件読み取り:2,929 件が一致、912 件は比較不能として明示、**上書き 0 件** |
76
+ | **デザイン監査** | コントラスト、フォントフォールバック、CJK の字送り、横溢れ、テキストの切れ、1 行の文字数 — ブラウザ上で実行、**指摘 26 件、すべて理由付きで裁定済み** — 理由のない容認はリリースゲートが拒否する |
77
+ | **テーマの書き出し/読み込み** | 経路は二つ、どちらも往復検証済み。`theme_export.php` は WP-CLI で行を JSON として移し、id は不変、コピーはバイト単位で同一のページを配信。`theme_zip.py` は Mosaic **自身**の ZIP 書き出し/読み込みをそのマイルストーン・プロトコルで駆動する — 読み込みは `--activate` を付けない限りテストモードに入る、既定がライブサイトの切り替えだからだ — そして **22 項目**でコピーを元とツリー単位で突き合わせる:全テーブル一致、68,337 のノード id を保持、override ノードは再採番、足りない 1 行はツリー走査が運ぶべきでない孤児 |
71
78
  | **実測** | REST ルート 114、element class 151、条件サブジェクト 59、23 テーブル / 206 カラム |
72
79
 
73
80
  `SKIPPED`、`NO_HOST`、`INCONCLUSIVE` を合格率に混ぜることは決してしない。
package/README.ko.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # mosaic-headless
2
2
 
3
+ [![npm downloads](https://img.shields.io/npm/dt/mosaic-headless?label=npm%20downloads&color=cb3837)](https://www.npmjs.com/package/mosaic-headless)
4
+
3
5
  [Mosaic Pro](https://mosaicbuilder.com)(Nextend) 사이트를 데이터 모델에 직접 써서
4
6
  구축하고 수정한다 — 비주얼 에디터도, DOM도 쓰지 않는다.
5
7
 
@@ -22,8 +24,11 @@ fractional-index 문자열이다. 에디터는 이 모델의 한 클라이언트
22
24
 
23
25
  그리고 grep이 아니라 `mo.py`로 찾을 것. grep은 입력한 질문에 답할 뿐, 실제로 품고 있는
24
26
  질문에는 답하지 않는다. `accordion-content`를 grep하면 그 타입이 존재한다는 것은 알 수
25
- 있지만, 그것을 배치하면 commit은 깨끗하게 통과하고 그다음 공개 페이지 전체가 54바이트
26
- 에러 문자열이 된다는 사실은 알려주지 않는다.
27
+ 있다. 스윕 표는 BROKE_PAGE라고 말한다 — 하나 배치하면 공개 페이지 전체가 54바이트 에러
28
+ 문자열이 된다. 둘 다 사실이고, 둘 다 틀린 답이다. 행 옆의 메모는 그 문자열이 "부모가
29
+ 없다"를 지목한다는 것, `accordion > accordion-item` 아래에 중첩하면 commit도 렌더링도
30
+ 되고 키보드로 조작 가능한 펼침 UI까지 따라온다는 것을 말한다. `mo.py type`은 그 셋을
31
+ 한 번에 보여준다.
27
32
 
28
33
  ```bash
29
34
  python tools/mo.py type accordion-content # 타입 하나를 모든 라이브 스윕과 연결해 출력
@@ -60,14 +65,16 @@ URL에 맞는 템플릿 없음 HTTP 406, 로그인하지 않은 사용자
60
65
  | **노드 타입** | 122 / 122. 문서 하나당 타입 하나로 commit → 렌더 → 단언 → 삭제. 70 RENDERED, 30 COMMITTED, 15 COMMIT_5xx, 7 BROKE_PAGE |
61
66
  | **스타일 속성** | 98 / 98을 실제 페이지에 쓰고 컴파일된 CSS와 대조: 58 COMPILED, 18 ABSENT, 21 SKIPPED |
62
67
  | **노드 속성** | 181 / 181을 각 속성 자신의 validator chain에서 도출한 값으로 재측정: 35 APPLIED, 42 NO_EFFECT, 55 NO_HOST, 47 SKIPPED |
63
- | **반응형** | 두 사이트 합쳐 688개의 `_t`/`_m` 선언을, 사이트가 실제로 내보낸 스타일시트에 대해 하나씩 단언 — 전부 검증됨 |
68
+ | **반응형** | 두 사이트 합쳐 731개의 `_t`/`_m` 선언을, 사이트가 실제로 내보낸 스타일시트에 대해 하나씩 단언 — 전부 검증됨 |
64
69
  | **컴포넌트** | 컴포넌트 체계를 끝까지 구동해 **8 / 8**: 카테고리 아래 생성, 문서 heal, 쓰기 가능한 instance로 트리 주입, 읽기 전용 쪽은 같은 쓰기를 거부(음성 대조군), 마지막으로 인스턴스 2개가 정의 하나로 렌더링 |
65
70
  | **스타일 상태** | 53개 상태 중 52개를 실제 페이지에 쓰고 표가 약속한 셀렉터와 대조: **36개가 정확히 일치**, 12개 NO_HOST, 3개 SKIPPED, 1개 BROKE_PAGE. 어떤 요소에나 쓸 수 있는 전역 7개 상태는 전부 검증됨 |
66
71
  | **인터랙션** | JS 애니메이션 경로를 음성 대조군과 함께, 저장된 행을 되읽으며 검증: `propertyMetas`는 **수용되고 저장된다**. 속성 값은 여전히 바인딩되지 않지만 그 경계는 이제 정확하다 |
67
- | **인트로 애니메이션** | 페이지 로드 후 10개 시점을 샘플링해 7가지를 단언 — 재생될 것, 애니메이션되는 `@property` 카운터가 100에 도달할 것, 베일이 히트 테스트에서 빠질 것, 뷰포트 안에 opacity 0으로 갇힌 요소가 없을 것, 실제 클릭이 문서에 닿을 것, 그리고 `prefers-reduced-motion`에서는 베일이 아예 존재하지 않을 것 |
68
- | **브라우저** | 전달된 페이지를 Chromium의 세 가지 뷰포트에서 계산 스타일 3,841건 판독: 2,929건 일치, 912건은 비교 불가로 명시, **덮어쓰기 0건** |
69
- | **디자인 감사** | 명암비, 폰트 폴백, CJK 자간, 가로 넘침, 텍스트 잘림, 한 줄 글자 수 — 브라우저에서 실행, **지적 0건** |
70
- | **테마 내보내기/가져오기** | 왕복 검증: 테마 전체를 내보내 사본으로 가져왔고, 사본이 바이트 단위로 동일한 페이지를 제공 |
72
+ | **인트로 애니메이션** | 단조 시계로 페이지 로드 후 15개 시점을 샘플링해 8가지를 단언 — 재생될 것, 애니메이션되는 `@property` 카운터가 100에 도달할 것, 베일이 히트 테스트에서 빠질 것, 뷰포트 안에 opacity 0으로 갇힌 요소가 없을 것, 실제 클릭이 문서에 닿을 것, `prefers-reduced-motion`에서는 베일이 아예 존재하지 않을 것, 그리고 모든 것이 가라앉은 뒤에도 여전히 움직이는 것이 있을 것. 문서의 첫 레이아웃을 기다린 뒤 시작하므로, 애니메이션이 전혀 없는 페이지 대비 늦은 프레임은 하나뿐 |
73
+ | **상시 애니메이션** | 구석에서 계속 자기를 찍어내고 탭하면 확대되는 판 — **28개 검사**: 일시정지한 타임라인을 스크럽해 주기성 증명, 다섯 폭 × 스물다섯 스크롤 지점에서 텍스트*와* 컨트롤의 가림을 "결코 읽을 수 없음"을 실패 조건으로 측정, 포인터와 Enter 양쪽으로 열림, reduced motion에서는 정지. Mosaic 자체 아코디언 위에 구축 |
74
+ | **아코디언** | `accordion-item`과 `accordion-content`는 스윕 표에서 BROKE_PAGE. 팩토리가 요구하는 대로 중첩하면 commit과 렌더링이 되며, **7건 중 7건**. 메모는 이제 그 행 옆에 있다 |
75
+ | **브라우저** | 전달된 두 페이지를 Chromium의 세 가지 뷰포트에서 계산 스타일 3,988건 판독: 2,929건 일치, 912건은 비교 불가로 명시, **덮어쓰기 0건** |
76
+ | **디자인 감사** | 명암비, 폰트 폴백, CJK 자간, 가로 넘침, 텍스트 잘림, 한 줄 글자 수 — 브라우저에서 실행, **지적 26건, 모두 서면으로 판정** — 이유 없는 승인은 릴리스 게이트가 거부한다 |
77
+ | **테마 내보내기/가져오기** | 두 경로, 모두 왕복 검증. `theme_export.php`는 WP-CLI로 행을 JSON으로 옮기며 id는 그대로, 사본은 바이트 단위로 동일한 페이지를 제공. `theme_zip.py`는 Mosaic **자체**의 ZIP 내보내기/가져오기를 그 마일스톤 프로토콜로 구동한다 — 가져오기는 `--activate`가 없으면 테스트 모드에 놓이는데, 기본값이 라이브 사이트 전환이기 때문이다 — 그리고 **22개 검사**로 사본을 원본과 트리 단위로 대조한다: 모든 테이블 일치, 68,337개 노드 id 유지, override 노드는 재발급, 빠진 한 행은 트리 순회가 가져가면 안 되는 고아 |
71
78
  | **실측** | REST 라우트 114개, element class 151개, 조건 주체 59개, 23개 테이블 / 206개 컬럼 |
72
79
 
73
80
  `SKIPPED`, `NO_HOST`, `INCONCLUSIVE`를 합격률에 섞는 일은 결코 없다.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # mosaic-headless
2
2
 
3
+ [![npm downloads](https://img.shields.io/npm/dt/mosaic-headless?label=npm%20downloads&color=cb3837)](https://www.npmjs.com/package/mosaic-headless)
4
+
3
5
  Build and modify [Mosaic Pro](https://mosaicbuilder.com) (Nextend) sites by writing
4
6
  the data model directly — no visual editor, no DOM.
5
7
 
@@ -22,8 +24,11 @@ from memory. Look it up in `data/`.**
22
24
 
23
25
  And look it up with `mo.py`, not grep. Grep answers the question you typed; it does
24
26
  not answer the question you have. Ask grep about `accordion-content` and it confirms
25
- the type exists. It does not mention that placing one commits cleanly and then
26
- reduces the whole public page to a 54-byte error string.
27
+ the type exists. The sweep says BROKE_PAGE: place one and the whole public page
28
+ becomes a 54-byte error string. Both true, both the wrong answer — the note beside
29
+ the row says that string names a missing parent, and that nested under
30
+ `accordion > accordion-item` the type commits, renders, and hands you a
31
+ keyboard-operable disclosure. `mo.py type` shows all three at once.
27
32
 
28
33
  ```bash
29
34
  python tools/mo.py type accordion-content # one type, joined to every live sweep
@@ -61,14 +66,16 @@ factories, so Pro types register and render regardless.
61
66
  | **node types** | 122 / 122 swept one per document, committed → rendered → asserted → deleted: 70 RENDERED, 30 COMMITTED, 15 COMMIT_5xx, 7 BROKE_PAGE |
62
67
  | **style properties** | 98 / 98 written to a live page and checked against the compiled CSS: 58 COMPILED, 18 ABSENT, 21 SKIPPED |
63
68
  | **node properties** | 181 / 181 re-probed with a value shaped by each property's own validator chain: 35 APPLIED, 42 NO_EFFECT, 55 NO_HOST, 47 SKIPPED |
64
- | **responsive** | 688 `_t`/`_m` declarations across two sites asserted against the stylesheet the site actually served — all verified |
69
+ | **responsive** | 731 `_t`/`_m` declarations across two sites asserted against the stylesheet the site actually served — all verified |
65
70
  | **components** | the component system driven end to end, **8 of 8**: created under a category, document healed, tree filled through the writable instance, the read-only one refused the same write as a negative control, and two instances on a page rendering one definition twice |
66
71
  | **style states** | 52 of the 53 states written to a live page and matched against the selector the table promises: **36 compiled exactly**, 12 NO_HOST, 3 SKIPPED, 1 BROKE_PAGE. All seven globally usable states verified |
67
72
  | **interactions** | the JS animation path probed with negative controls and the row read back: `propertyMetas` **is** accepted and stored; the property values still do not bind, and the boundary is now exact |
68
- | **entrance animation** | the page-load sequence sampled at ten timestamps and asserted on seven counts — it plays, its animated `@property` counter reaches 100, the veil leaves hit-testing, nothing in the viewport is stranded at opacity 0, a real click reaches the document, and under `prefers-reduced-motion` the veil never exists at all |
69
- | **browser** | 3,841 computed-style readings on the delivered page in Chromium at three viewports: 2,929 compared and agreed, 912 not-comparable and labelled, **0 overridden** |
70
- | **design audit** | contrast, font fallback, CJK tracking, overflow, clipped text, line measure — run in the browser, **0 findings** |
71
- | **theme export/import** | round-tripped: a full theme exported, re-imported as a copy, and the copy served byte-identical pages |
73
+ | **entrance animation** | the page-load sequence sampled at fifteen timestamps on a monotonic clock and asserted on eight counts — it plays, its animated `@property` counter reaches 100, the veil leaves hit-testing, nothing in the viewport is stranded at opacity 0, a real click reaches the document, under `prefers-reduced-motion` the veil never exists, and something is still moving once everything has settled. Costs one late frame over a page with no animation at all, because it waits for the document's first layout |
74
+ | **perpetual animation** | a corner plate that keeps printing itself and opens to full size when tapped, **28 checks**: periodicity proved by scrubbing a paused timeline, occlusion of text *and* controls at five widths and twenty-five scroll stops with "never readable" as the failing condition, opened by pointer and by Enter, still under reduced motion. Built on Mosaic's own accordion |
75
+ | **accordion** | `accordion-item` and `accordion-content` sit in the sweep table as BROKE_PAGE; nested as their factory requires they commit and render, **7 of 7**. The note now lives beside the row |
76
+ | **browser** | 3,988 computed-style readings on two delivered pages in Chromium at three viewports: 2,929 compared and agreed, 912 not-comparable and labelled, **0 overridden** |
77
+ | **design audit** | contrast, font fallback, CJK tracking, overflow, clipped text, line measure — run in the browser, **26 findings, every one ruled on in writing** — an acknowledgement without a reason is refused by the release gate |
78
+ | **theme export/import** | two paths, both round-tripped. `theme_export.php` moves rows as JSON over WP-CLI, ids intact, and the copy served byte-identical pages. `theme_zip.py` drives Mosaic's **own** ZIP export/import over its milestone protocol — import lands in test mode unless told `--activate`, because the default is to switch the live site — and **22 checks** hold the copy against the source tree for tree: every table equal, 68,337 node ids kept, override nodes re-keyed, the one missing row an orphan no tree-walk should carry |
72
79
  | **measured live** | 114 REST routes, 151 element classes, 59 condition subjects, 23 tables / 206 columns |
73
80
 
74
81
  `SKIPPED`, `NO_HOST` and `INCONCLUSIVE` are never folded into a pass rate. A sweep
package/README.zh-TW.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # mosaic-headless
2
2
 
3
+ [![npm downloads](https://img.shields.io/npm/dt/mosaic-headless?label=npm%20downloads&color=cb3837)](https://www.npmjs.com/package/mosaic-headless)
4
+
3
5
  直接寫入資料模型來建置與修改 [Mosaic Pro](https://mosaicbuilder.com)(Nextend)網站——
4
6
  不開視覺編輯器,不碰 DOM。
5
7
 
@@ -18,8 +20,10 @@ Mosaic 把一個頁面放在 **23 張自訂資料表**裡,不在 `post_content
18
20
  **絕不憑印象寫節點型別、屬性名稱、列舉值、樣式鍵或 Free/Pro 的判斷。到 `data/` 裡查。**
19
21
 
20
22
  而且要用 `mo.py` 查,不要用 grep。grep 回答你打出來的問題,不回答你真正的問題:
21
- 問它 `accordion-content`,它確認這個型別存在,但不會提到放一個下去會乾淨地寫入、
22
- 然後把整個公開頁面變成一段 54 bytes 的錯誤字串。
23
+ 問它 `accordion-content`,它確認這個型別存在;掃描表則說 BROKE_PAGE——放一個下去,
24
+ 整個公開頁面會變成一段 54 bytes 的錯誤字串。兩個都是真的,也都是錯的答案:列旁邊的
25
+ 註記說那段字串指名的是「缺少父層」,而照 `accordion > accordion-item` 嵌套後它能寫入、
26
+ 能渲染,還免費送你一個鍵盤可操作的展開元件。`mo.py type` 一次把三者都給你。
23
27
 
24
28
  ```bash
25
29
  python tools/mo.py type accordion-content # 一個型別,接上所有線上掃描結果
@@ -53,14 +57,16 @@ commit 時 PHP fatal HTTP 500 (122 種型別裡有 15 種,光放在 div
53
57
  | **節點型別** | 122 / 122,一份文件一種型別:寫入 → 渲染 → 斷言 → 刪除。70 RENDERED、30 COMMITTED、15 COMMIT_5xx、7 BROKE_PAGE |
54
58
  | **樣式屬性** | 98 / 98 寫進真實頁面並對照編譯後的 CSS:58 COMPILED、18 ABSENT、21 SKIPPED |
55
59
  | **節點屬性** | 181 / 181,用每個屬性自己的 validator chain 推導出的值重測:35 APPLIED、42 NO_EFFECT、55 NO_HOST、47 SKIPPED |
56
- | **響應式** | 兩個站共 688 條 `_t`/`_m` 宣告,對照網站實際送出的樣式表逐條斷言——全數通過 |
60
+ | **響應式** | 兩個站共 731 條 `_t`/`_m` 宣告,對照網站實際送出的樣式表逐條斷言——全數通過 |
57
61
  | **元件系統** | 完整驅動過一遍,**8 / 8**:在分類下建立、文件 heal、透過可寫的 instance 填入內容、唯讀的那個作為負對照組確實拒絕同一個寫入,最後兩個實例在頁面上由同一份定義渲染兩次 |
58
62
  | **樣式狀態** | 53 個狀態中的 52 個寫進真實頁面,對照表格承諾的選擇器逐一比對:**36 個完全吻合**、12 個 NO_HOST、3 個 SKIPPED、1 個 BROKE_PAGE。七個可用於任何元素的狀態全數驗證 |
59
63
  | **互動動畫** | 帶負對照組並讀回儲存列來探測 JS 動畫路徑:`propertyMetas` **確實**會被接受並儲存;屬性值仍然無法綁定,但界線現在很精確 |
60
- | **入口動畫** | 在載入後十個時間點取樣並做七項斷言——它有播、被動畫的 `@property` 計數器跑到 100、遮罩退出點擊判定、視窗內沒有任何內容卡在 opacity 0、真實點擊落在文件上,而在 `prefers-reduced-motion` 下遮罩根本不存在 |
61
- | **瀏覽器** | 在 Chromium 三個視窗寬度上對交付頁面做 3,841 次計算樣式讀取:2,929 條比對相符、912 條標為無法比對、**0 條被覆蓋** |
62
- | **設計稽核** | 對比度、字體回退、CJK 字距、水平溢出、文字裁切、每行字數——在瀏覽器裡跑,**0 項發現** |
63
- | **主題匯出/匯入** | 完整來回驗證:匯出整個主題、以副本身分匯入,副本送出的頁面逐位元組相同 |
64
+ | **入口動畫** | 以單調時鐘在載入後十五個時間點取樣、做八項斷言——它有播、被動畫的 `@property` 計數器跑到 100、遮罩退出點擊判定、視窗內沒有任何內容卡在 opacity 0、真實點擊落在文件上、`prefers-reduced-motion` 下遮罩根本不存在、而一切靜止後仍有東西在動。相較於完全沒有動畫的同一頁只多掉一幀,因為它會等文件第一次排版做完才開始 |
65
+ | **永續動畫** | 右下角一塊不斷自我印刷、點了會放大的版子,**28 項檢查**:暫停時間軸逐格比對證明週期性、在五個寬度、二十五個捲動停點量文字*與*控制項的遮擋、以「永遠讀不到」為失敗條件、用指標和 Enter 都能打開、reduced motion 下靜止。建在 Mosaic 自己的 accordion 上 |
66
+ | **accordion** | `accordion-item` 與 `accordion-content` 在掃描表裡是 BROKE_PAGE;照工廠要求的方式嵌套後能寫入、能渲染,**7 之 7**。註記現在就在那一列旁邊 |
67
+ | **瀏覽器** | 在 Chromium 三個視窗寬度上對兩個交付頁面做 3,988 次計算樣式讀取:2,929 條比對相符、912 條標為無法比對、**0 條被覆蓋** |
68
+ | **設計稽核** | 對比度、字體回退、CJK 字距、水平溢出、文字裁切、每行字數——在瀏覽器裡跑,**26 項發現,每一項都有書面裁定**——沒寫理由的 acknowledge 會被發布閘門拒絕 |
69
+ | **主題匯出/匯入** | 兩條路徑,都來回驗證過。`theme_export.php` 用 WP-CLI 把資料列搬成 JSON、ID 原封不動,副本送出的頁面逐位元組相同。`theme_zip.py` 走 Mosaic **自己**的 milestone 協定驅動 ZIP 匯出匯入——匯入預設進 test mode,要 `--activate` 才上線,因為它的預設是直接把 live 站切過去——並以 **22 項檢查**逐表、逐樹比對副本與來源:每張表相等、68,337 個節點 ID 保留、override 節點重新發 ID,唯一少的一列是走樹本來就不該帶的孤兒 |
64
70
  | **線上量測** | 114 條 REST 路由、151 個 element class、59 個條件主體、23 張表 / 206 個欄位 |
65
71
 
66
72
  `SKIPPED`、`NO_HOST`、`INCONCLUSIVE` 永遠不併進通過率。
package/SKILL.md CHANGED
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: "mosaic-headless"
3
3
  description: |
4
- Build and modify Mosaic Pro (Nextend) sites by writing the underlying data model directly - no visual editor, no DOM. Query the real surface with `mo.py`, which joins every source table to the live sweeps so a lookup leads with the measured verdict rather than the declaration (122 node types, 181 properties, 98 style properties with 20 structured value shapes pinned down, 53 style states, 151 element classes, 74 dynamic variables, 12 interaction triggers, 114 REST routes, 23 tables) instead of guessing, with every node type placed on a live site one at a time and asserted against the delivered HTML, the design-token and element-class layers verified against compiled CSS, the @VAR() dynamic language verified against rendered output, nine designed pages built through the tables themselves, and the delivered page re-read in Chromium at three viewports so a rule that is present, correct and still wrong cannot pass.
4
+ Build and modify Mosaic Pro (Nextend) sites by writing the underlying data model directly - no visual editor, no DOM. Query the real surface with `mo.py`, which joins every source table to the live sweeps so a lookup leads with the measured verdict rather than the declaration (122 node types, 181 properties, 98 style properties with 20 structured value shapes pinned down, 53 style states, 151 element classes, 74 dynamic variables, 12 interaction triggers, 114 REST routes, 23 tables) instead of guessing, with every node type placed on a live site one at a time and asserted against the delivered HTML, the design-token and element-class layers verified against compiled CSS, the @VAR() dynamic language verified against rendered output, nine designed pages built through the tables themselves, and the delivered pages re-read in Chromium at three viewports so a rule that is present, correct and still wrong cannot pass. Drives Mosaic's own theme export/import from outside the editor and holds the copy against the source tree for tree.
5
5
  license: "MIT"
6
6
  author: "moksa (https://moksaweb.com)"
7
- version: "1.15.1"
7
+ version: "1.16.0"
8
8
  ---
9
9
 
10
10
  # Headless Mosaic
@@ -23,9 +23,14 @@ from memory. Look it up in `data/`.**
23
23
 
24
24
  And look it up with `mo.py`, not with grep. Grep answers the question you typed;
25
25
  it does not answer the question you have. Ask grep about `accordion-content` and it
26
- confirms the type exists. It does not mention that placing one commits cleanly and
27
- then reduces the entire public page to a 54-byte error string - which is the only
28
- thing about that type worth knowing.
26
+ confirms the type exists. The sweep table says BROKE_PAGE: place one and the entire
27
+ public page becomes a 54-byte error string. Both are true, and both are the wrong
28
+ answer - the note beside the row says the string is `AccordionItemElementMResource
29
+ instance required`, that this is a type committed without the parent its factory
30
+ needs, and that nested under `accordion > accordion-item` it commits, renders and
31
+ gives you a keyboard-operable disclosure for free. `mo.py type` shows all three in
32
+ one answer. Grep shows one, the CSV shows two, and the skill was itself wrong about
33
+ this type for a week.
29
34
 
30
35
  ```bash
31
36
  python tools/mo.py stats # the surface, and what is unsafe
@@ -123,6 +128,12 @@ NODE SWEEP 122 of 122 node types, ONE PER DOCUMENT, committed then rendered th
123
128
  COMMIT_5xx 15 PHP fatal on commit
124
129
  BROKE_PAGE 7 committed, then the whole page died
125
130
  Free 74: 43/22/6/3. Pro 48: 27/8/9/4. data/node-verification.csv
131
+ Three of the non-rendering outcomes are artefacts of the sweep's own
132
+ method - one type per document, under a plain div - and not of the
133
+ type: `component-instance` (needs `component-instance/<id>`),
134
+ `accordion-item` and `accordion-content` (need their parent). Each is
135
+ proven to work as built, and `data/node-type-notes.csv` says so
136
+ beside the row. The sweep verdict stands; the note is what to read.
126
137
 
127
138
  STYLE the states[state][breakpoint][property] shape confirmed by writing it
128
139
  and reading back the compiled CSS; 20 of 22 structured value shapes
@@ -170,29 +181,97 @@ INTERACTION the JS animation path, probed with negative controls and the row re
170
181
  written straight into the table with caches flushed.
171
182
  data/interaction-verification.csv
172
183
 
173
- INTRO the page-load sequence - an ukiyo-e sheet printing itself one carved
174
- block at a time, each impression landing out of register and snapping
175
- true against the kento marks - sampled at thirteen timestamps across
176
- its life
177
- and
178
- asserted on seven counts - it plays, the animated `@property` counter
179
- reaches 100, the veil leaves hit-testing, no in-viewport content is
180
- stranded at opacity 0, a real click reaches the document, and under
181
- `prefers-reduced-motion` the veil never exists at all - plus AMBIENT,
182
- which takes the last two readings and counts how many elements still
183
- differ with no input at all. Every other motion check here fires on an
184
- EVENT; a page can pass all of them and be completely static the moment
185
- you stop scrolling. All pass. data/intro-verification.csv
186
-
187
- BROWSER 3,841 computed-style readings on the delivered page in Chromium, at
184
+ INTRO the page-load sequence - an ukiyo-e sheet printing itself eleven
185
+ carved blocks at a time, each impression landing out of register and
186
+ easing true against the kento marks - sampled at fifteen timestamps
187
+ on a monotonic clock (the first version scheduled against the previous
188
+ sample and drifted fourteen seconds) and asserted on eight counts: it
189
+ plays, the animated `@property` counter reaches 100, the veil leaves
190
+ hit-testing, no in-viewport content is stranded at opacity 0 (elements
191
+ waiting on a scroll timeline are exempt, and a `position:fixed` one
192
+ was the case that showed the exemption had to be by timeline and not
193
+ by position), a real click reaches the document, under
194
+ `prefers-reduced-motion` the veil never exists and nothing is
195
+ invisible at 120ms, plus AMBIENT, which counts how many elements still
196
+ move with no input once everything has settled. Every other motion
197
+ check here fires on an EVENT; a page can pass all of them and be
198
+ completely static the moment you stop scrolling. Also measured, with
199
+ CPU-time counters rather than frame timing, which proved unusable:
200
+ the sequence costs one late frame over a page with no animation in it,
201
+ because it no longer starts until the document's own first layout is
202
+ done. All pass. data/intro-verification.csv
203
+
204
+ LOOP 28 assertions about the one animation nothing else here can see: the
205
+ ukiyo-e plate that keeps printing itself in the corner of the page,
206
+ forever, with no input, and that opens to full size when tapped. Its
207
+ eleven moving parts are SVG groups inside a raw-HTML `code` node, so no
208
+ checker that walks the spec tree knows they exist. RUNS reads them on the clock across a full period; each part is
209
+ then proved periodic by PAUSING its animation and scrubbing
210
+ `currentTime` to T and T + one period (14s), past the stagger, which
211
+ is the only way to compare a compositor-driven transform exactly.
212
+ CLEAR is the one that found a real defect: a fixed decoration is
213
+ opaque, and on a full-bleed page no rectangle anywhere along the right
214
+ edge misses text at every scroll offset - so the test is not "never
215
+ overlaps" but "never makes anything unreadable", per element, at five
216
+ widths and twenty-five scroll stops.
217
+ It caught three footer links buried at the position a reader cannot
218
+ scroll past. Once the plate became a control, REACHABLE holds links and
219
+ buttons to the same rule - swallowing a click is worse than covering a
220
+ word - and OPENS/CLOSES/KEYBOARD check the enlarged view on the
221
+ delivered page, including that Enter works on it. Five widths from 390
222
+ up. data/loop-verification.csv
223
+
224
+ CONVERSION an Elementor page turned into a Mosaic spec and built, then checked
225
+ against its own source: every text string, every image, every link
226
+ target, every h1-h6 level. 5 of 5 on a page with 16 images and 21
227
+ links. Scope was decided by counting a real site rather than by
228
+ taste - across 19 Elementor pages and 3,292 element instances,
229
+ container/heading/text-editor/button/html/icon-list/divider/image
230
+ are 99.6% of everything present, and the long tail (loop-grid, form,
231
+ posts, countdown, third-party addons) is dynamic: a query and a
232
+ server-side action have no node to become. Those are reported per
233
+ element with a reason, never dropped, and `--strict` refuses to
234
+ write a spec while any remain. The verifier earned its place twice
235
+ over: it caught the converter writing `url` onto `text` and `image`
236
+ nodes, which only `button`, `menu-link`, `wysiwyg-link` and
237
+ `dropdown-toggle` declare - 19 of 21 link targets accepted, stored
238
+ and silently emitting no anchor, this platform's signature failure -
239
+ and it caught two bugs in ITSELF that had blamed the converter.
240
+ Then the converted page went through the skill's own suites like any
241
+ other: 96 responsive declarations verified, 5,595 computed values
242
+ with 0 overridden, and a design audit of 49 CONTRAST findings -
243
+ every one traced back to a colour the Elementor source declared,
244
+ none introduced by the conversion. That split is a check of its own,
245
+ FIDELITY, and the inherited defects are recorded beside it as still
246
+ real: a 3.19:1 label is 3.19:1 whoever wrote it. 8 of 8.
247
+ data/conversion-verification.csv
248
+
249
+ ACCORDION 7 checks that resolve a wrong entry in this skill's own tables.
250
+ `accordion-item` and `accordion-content` are recorded BROKE_PAGE, which
251
+ is true and misleading in exactly the way `component-instance` was:
252
+ the failure is `AccordionItemElementMResource instance required`, i.e.
253
+ committed without the parent the factory needs. Nested properly they
254
+ commit, render, and emit `<dl><div class=AccordionItem><dt tabindex=0
255
+ aria-expanded><dd>` - which is why the worked example's enlargeable
256
+ plate is built on it instead of on a checkbox, and gets keyboard
257
+ operation and a screen-reader state for nothing. The negative control
258
+ asks the placement guard rather than committing, because the first
259
+ version of the probe destroyed the positive result it had just
260
+ produced. data/accordion-verification.csv
261
+
262
+ BROWSER 3,988 computed-style readings on the delivered pages in Chromium, at
188
263
  three viewports: every declared property vs `getComputedStyle` on the
189
264
  node it targets. 2,929 compared and agreed, 912 not-comparable and
190
265
  labelled as such, 0 overridden. Plus a design audit that only a browser
191
266
  can run - font fallback, tracking against script, text contrast,
192
- horizontal overflow, clipped text, line measure - currently 0 findings.
267
+ horizontal overflow, clipped text, line measure - 26 findings, every
268
+ one ruled on in writing. Two pages: the homepage, and a WooCommerce
269
+ My Account page whose UI arrives through one `code` node running a
270
+ shortcode - which is also the measurement that shows the property
271
+ sweep's NO_EFFECT on `processShortcodes` was the sweep, not the flag.
193
272
  data/browser-verification.csv, data/design-audit.csv
194
273
 
195
- RWD 688 responsive declarations across two sites asserted against the
274
+ RWD 731 responsive declarations across two sites asserted against the
196
275
  stylesheet the site actually served - each `_t`/`_m` property matched
197
276
  to its element's generated class inside that breakpoint's own media
198
277
  query. All verified; the checker is itself checked against a poisoned
@@ -332,16 +411,20 @@ so the pattern is in the data, not just in this paragraph.
332
411
  | `data/property-verification.csv` | 170 | **probed live** — per-property effect on markup vs CSS, with unprovable enums marked INCONCLUSIVE |
333
412
  | `data/node-property-verification.csv` | 181 | **swept live** — each property probed with a value shaped by its own validator chain, on a type that declares it |
334
413
  | `data/style-verification.csv` | 98 | **swept live** — every style property written to a page and checked against the compiled CSS, with its group beside the result |
335
- | `data/rwd-verification.csv` | 688 | **checked live** - every `_t`/`_m` declaration vs the served stylesheet, with status per row |
336
- | `data/browser-verification.csv` | 3841 | **computed in Chromium** - declared vs `getComputedStyle` at three viewports, `not-comparable` labelled per row |
337
- | `data/design-audit-acknowledged.csv` | 1 | reviewed findings that will not be fixed, each with a written reason. An acknowledgement without a reason is a suppression wearing a better name, and the release gate refuses one |
338
- | `data/design-audit.csv` | 24 | **computed in Chromium** - contrast, font fallback, CJK tracking, overflow, measure. Empty means it ran and found nothing |
414
+ | `data/rwd-verification.csv` | 731 | **checked live** - every `_t`/`_m` declaration vs the served stylesheet, with status per row |
415
+ | `data/browser-verification.csv` | 3988 | **computed in Chromium** - declared vs `getComputedStyle` at three viewports, `not-comparable` labelled per row |
416
+ | `data/design-audit-acknowledged.csv` | 3 | reviewed findings that will not be fixed, each with a written reason. An acknowledgement without a reason is a suppression wearing a better name, and the release gate refuses one |
417
+ | `data/design-audit.csv` | 26 | **computed in Chromium** - contrast, font fallback, CJK tracking, overflow, measure. Empty means it ran and found nothing |
339
418
  | `data/data-class-hierarchy.csv` | 121 | source - every data class and its parent, so a type's inherited properties can be resolved |
340
419
  | `data/style-state-verification.csv` | 52 | **swept live** - each state written on a host of its own type and matched against its promised selector |
341
420
  | `data/component-verification.csv` | 8 | **driven live** - the component lifecycle, each step asserted against the row or the delivered HTML |
342
- | `data/node-type-notes.csv` | 4 | where a sweep outcome is true but misleading on its own, why. Surfaced by `mo.py type` |
421
+ | `data/loop-verification.csv` | 28 | **measured live** - a perpetual animation: periodicity by scrubbing a paused timeline, per-element occlusion of both text and controls at five widths, and the enlarged view opened by pointer and by keyboard |
422
+ | `data/accordion-verification.csv` | 7 | **driven live** - the accordion family nested the way its factory requires, against the guard that refuses it unparented. Resolves two BROKE_PAGE rows |
423
+ | `data/conversion-verification.csv` | 8 | **converted then checked live** - an Elementor page rebuilt as Mosaic and held against its source (text, images, links, heading levels), then put through rwd, browser and the design audit with every finding classified inherited-or-introduced |
424
+ | `data/theme-zip-verification.csv` | 22 | **round-tripped live** - Mosaic's own ZIP export imported in test mode and compared to its source, table by table and tree by tree |
425
+ | `data/node-type-notes.csv` | 8 | where a sweep outcome is true but misleading on its own, why. Surfaced by `mo.py type` |
343
426
  | `data/interaction-verification.csv` | 7 | **probed live** - interaction animation shapes, with negative controls and the stored row beside the payload |
344
- | `data/intro-verification.csv` | 7 + 10 | **sampled live** - the entrance sequence over ten timestamps, plus the seven assertions about it |
427
+ | `data/intro-verification.csv` | 8 + 15 | **sampled live** - the entrance sequence over fifteen timestamps on a monotonic clock, plus the eight assertions about it |
345
428
  | `data/element-classes.csv` | 151 | **live** — the built-in class metas; their IDs are what an `elementClass` record must use |
346
429
  | `data/dynamic-variables.csv` | 74 | source — every `@VAR('ns/name')` expression, by namespace |
347
430
  | `data/evaluator-functions.csv` | 19 | source — the `@` functions with their arity |
@@ -400,6 +483,33 @@ and rewrite nothing but the theme, which means no id remapping and nothing left
400
483
  dangling. Round-tripped and verified: a full theme exported, re-imported as a copy,
401
484
  and the copy served byte-identical pages.
402
485
 
486
+ Mosaic also has its own: `POST /theme/<id>/export` and `POST /theme/import/upload`
487
+ (or `/import/download` from a URL), plus `/duplicate` on themes, masters, templates,
488
+ components and style guides. The native export is a ZIP with the theme's
489
+ attachments inside, produced by a chunked, lock-protected milestone flow — you POST
490
+ repeatedly with `milestoneID` to step it through collect → compress → download — and
491
+ it is gated on admin capability only, not on a licence. It moves a whole theme, never
492
+ a single page. `tools/theme_zip.py` drives both directions from outside the editor.
493
+ Two things about the import that the route list does not tell you: with no
494
+ `themeActivateMode` it defaults to `live` and switches every visitor to the imported
495
+ theme (the tool sends `test`, Mosaic's admin-only preview, unless `--activate`); and a
496
+ milestone must be called until it answers `isCompleted:true` - stop one call early,
497
+ as the first version of the tool did after its last upload chunk, and that
498
+ milestone's batch bookkeeping is left in the lock for the NEXT milestone to read as
499
+ its own, which is how `extract` came to skip creating its directories and fail on
500
+ the first nested file with "Could not copy file".
501
+
502
+ Round-tripped and measured (`data/theme-zip-verification.csv`, 22 checks): export,
503
+ import in test mode, compare. Every scoped table equal; 68,337 of 68,337 nodes
504
+ outside overrides keep their ids; the 1,024 override nodes are re-keyed, with their
505
+ `originalID` re-pointed at the re-keyed component internals; the live theme
506
+ untouched. The node table came back one row short, and that row is the difference
507
+ between the two tools in one line: an orphan `div` with no parent, left by an
508
+ interrupted build, which a tree-walking import correctly does not carry and a
509
+ row-copying one (`theme_import.php`) would. `theme_export.php` exists because a
510
+ 2 MB JSON of rows is easier to diff, version and reason about than a ZIP, and
511
+ because it needs nothing but WP-CLI; the ZIP is what the editor's buttons speak.
512
+
403
513
  `copy_styles.py` pushes one node's style onto others by attrID or prefix, optionally
404
514
  only certain `state.breakpoint` slices, and shows the diff before writing.
405
515
 
@@ -429,6 +539,29 @@ post — `build_all.py` resets first for that reason.
429
539
  number is not stable. The sibling `M_EL_<Type>` class is.
430
540
  - **`body{opacity:0}`** — the theme reveals itself from JavaScript. Screenshot tooling
431
541
  must let scripts run or it captures a blank page.
542
+ - **Two themes can carry the same name, and only the ACTIVE one is bound to
543
+ anything.** Every write to `theme/<themeID>/...` succeeds against an inactive
544
+ theme - master, nodes, components, tokens, all of it - and then
545
+ `createManualTemplate` answers `Master not found`, because it resolves the theme
546
+ from the request, not from your URL. Read the active theme's id off
547
+ `wp theme list --status=active` (`mosaic-1-1-<themeID>`) before building.
548
+ - **Page caches stack.** The demo host ran Varnish AND Breeze; purging Varnish
549
+ changed nothing because Breeze still had the old document. `build_site.py`
550
+ fetches the plain URL and a cache-busted one at the same moment and reports
551
+ STALE CACHE when they differ by more than a percent - believe it, then find
552
+ every layer. `wp breeze purge --cache=all` was the one that mattered.
553
+ - **A `code` node is a wrapper, not a splice.** `insertLocation:"inPlace"` puts your
554
+ markup INSIDE `<div class="M_EL_Code">`, one level down - so a sibling combinator
555
+ from injected HTML to a Mosaic node never matches, and a control you inject has
556
+ to be styled through its own ancestors or through `:has()`. The native accordion
557
+ avoided the question entirely.
558
+ - **`section` arrives with 80px of top and bottom padding.** Measured on a node
559
+ whose spec set neither; between a masthead and the block below it that is a
560
+ 160px hole. Set `paddingTop`/`paddingBottom` explicitly when a section's rhythm
561
+ is meant to come from its contents.
562
+ - **The front page 301s.** When a post is `page_on_front`, its own permalink
563
+ (`/moksa/`) redirects to `/`; a fetch that does not follow redirects reads 0
564
+ bytes and looks like an outage.
432
565
 
433
566
  ## Rendered-tag facts you would otherwise guess wrong
434
567
 
@@ -438,6 +571,18 @@ post — `build_all.py` resets first for that reason.
438
571
  `<mosaic-tabs>`, `<mosaic-accordion>`, `<mosaic-vimeo>`, `<mosaic-youtube>` and
439
572
  friends. A selector written against `div`/`nav` misses all of them.
440
573
  - **`icon` renders inline `<svg>`.**
574
+ - **Only four types take a `url`**: `button`, `menu-link`, `wysiwyg-link`,
575
+ `dropdown-toggle`. Put one on a `text` or an `image` and it is accepted, stored,
576
+ and emits no anchor whatsoever. To make anything else clickable, WRAP it in a
577
+ `menu-link` - that type takes `children: rule=any` and becomes a real `<a href>`
578
+ as soon as it has a `url` (`target="_blank"` included; both probed).
579
+ - **An image's attachment-protocol path is relative to the UPLOADS directory.**
580
+ `wp-attachment://image/<id>/<size>/2026/09/pic.png` resolves, and carries the
581
+ attachment's `width`/`height` onto the tag. Give it the full
582
+ `wp-content/uploads/2026/09/pic.png` - the obvious guess - and Mosaic prefixes
583
+ the uploads base a second time, serving a broken `src` with no error and no
584
+ dimensions. A plain `https://` URL also works, and is the right fallback for an
585
+ image that is not an attachment on this site.
441
586
 
442
587
  ## Tools
443
588
 
@@ -449,6 +594,10 @@ post — `build_all.py` resets first for that reason.
449
594
  | `verify_rwd.py` | does every `_t`/`_m` declaration reach the served stylesheet? |
450
595
  | `verify_browser.py` | does the **browser** compute what the stylesheet promised - and does the result pass a design audit? |
451
596
  | `verify_intro.py` | does the page-load animation play, and - the part that matters - does it END and hand the page back? |
597
+ | `verify_loop.py` | a decoration that runs forever: does it loop (paused-timeline scrub), does it bury text or controls anywhere a reader can rest, does it open by pointer and by keyboard, is it still under reduced motion? |
598
+ | `probe_accordion.py` | the accordion family nested as its factory requires, plus the guard's refusal of the bare node - the probe that corrected two rows of this skill's own tables |
599
+ | `from_elementor.py` | an Elementor `_elementor_data` tree -> a Mosaic page spec, with every element it cannot convert reported by name and reason (`--strict` to refuse a lossy spec) |
600
+ | `verify_conversion.py` | did the conversion carry the page? every source string, image, link and heading level looked for on the delivered Mosaic page |
452
601
  | `sweep_node_types.py` | commit every node type one per document and assert the delivered HTML |
453
602
  | `sweep_style_properties.py` | write every style property and check the compiled CSS |
454
603
  | `sweep_node_properties.py` | probe every node property with a value from its own validator chain |
@@ -456,6 +605,9 @@ post — `build_all.py` resets first for that reason.
456
605
  | `sweep_components.py` | build a component, instance it twice, and prove one definition served both |
457
606
  | `sweep_interactions.py` | which interaction animation shapes survive to the frontend payload |
458
607
  | `theme_export.php` / `theme_import.php` | move a whole theme across installs, ids intact |
608
+ | `theme_zip.py` | drive Mosaic's OWN export/import - the ZIP the editor makes, attachments included, over the milestone protocol; import lands in test mode unless told `--activate` |
609
+ | `theme_zip_compare.php` | hold an imported copy against its source, tree for tree - every scoped table, the (parentType, type) shape, which ids survive, and orphans named rather than counted |
610
+ | `theme_delete.php` | remove a theme completely through the plugin's own routine; refuses the live one |
459
611
  | `copy_styles.py` | push one node's style onto others, by attrID or prefix |
460
612
  | `bootstrap_probe_theme.php` | a licence-free scratch theme |
461
613
  | `mint_session.php` | a matching cookie + `wp_rest` nonce from WP-CLI |
@@ -487,8 +639,21 @@ python tools/sweep_style_properties.py --config sweep.json --page moksa --csv da
487
639
  python tools/sweep_node_properties.py --config sweep.json --page moksa --csv data/node-property-verification.csv
488
640
  python tools/sweep_style_states.py --config sweep.json --post 20 --slug probe-lab --csv data/style-state-verification.csv
489
641
  python tools/sweep_interactions.py --config sweep.json --post 20 --slug probe-lab --csv data/interaction-verification.csv
642
+ python tools/verify_loop.py --url https://site/ --window mk-loop --toggle mk-plate-title --panel mk-plate-item \
643
+ --parts ukw-sky,ukw-sun,ukw-fuji,ukw-mist,ukw-sea,ukw-crest,ukw-boat,ukw-wave,ukw-claw,ukw-swell,ukw-key,mk-loop-seal \
644
+ --period 14000 --csv data/loop-verification.csv
645
+ python tools/probe_accordion.py --config sweep.json --post 20 --slug probe-lab --csv data/accordion-verification.csv
646
+ python tools/from_elementor.py --data _elementor_data.json --out spec.json --report conv.csv --uploads-base https://site/wp-content/uploads --slug converted --post 208
647
+ python tools/verify_rwd.py --config c.json --site spec.json --csv rwd.csv
648
+ python tools/verify_browser.py --config c.json --site spec.json --csv browser.csv --audit audit.csv
649
+ python tools/verify_conversion.py --data _elementor_data.json --url https://site/converted/ --report conv.csv --prefix p2360 --rwd rwd.csv --browser browser.csv --audit audit.csv --csv data/conversion-verification.csv
490
650
  wp eval-file tools/theme_export.php active > theme.json
491
651
  wp eval-file tools/theme_import.php theme.json "Copy" rebind activate
652
+ python tools/theme_zip.py export --config c.json --out theme.zip
653
+ python tools/theme_zip.py import --config c.json --zip theme.zip # test mode
654
+ python tools/theme_zip.py import --config c.json --zip theme.zip --activate # goes live
655
+ wp eval-file tools/theme_zip_compare.php <source> <copy> theme-zip-verification.csv
656
+ wp eval-file tools/theme_delete.php <copy>
492
657
  python tools/probe.py --config lab.json --cases cases.json # ad-hoc measurement
493
658
  python tools/check_placement_predicts.py
494
659
  ```
@@ -20,7 +20,7 @@
20
20
  "description": "Build and modify Mosaic Pro (Nextend) sites by writing the underlying data model directly - no visual editor, no DOM. Query the real surface (122 node types, 181 node properties, 98 style properties, 53 style states, 151 element classes, 74 dynamic variables, 114 REST routes, 23 tables) instead of guessing, with every node type placed on a live site one at a time and asserted against the delivered HTML, every style and node property swept against the compiled CSS and markup, and 569 responsive declarations checked against the stylesheet the site actually served.",
21
21
  "license": "MIT",
22
22
  "author": "moksa (https://moksaweb.com)",
23
- "version": "1.15.1"
23
+ "version": "1.16.0"
24
24
  },
25
25
  "loaderBehaviour": "Upload via Settings -> Skills -> Upload. Claude.ai parses SKILL.md frontmatter and surfaces the skill in your library. The extraction tool (extract-block-schema.php) needs a live WP-CLI connection and won't run in the sandbox; use it from a local terminal against your own site instead.",
26
26
  "uploadSteps": [
@@ -20,7 +20,7 @@
20
20
  "description": "Build and modify Mosaic Pro (Nextend) sites by writing the underlying data model directly - no visual editor, no DOM. Query the real surface (122 node types, 181 node properties, 98 style properties, 53 style states, 151 element classes, 74 dynamic variables, 114 REST routes, 23 tables) instead of guessing, with every node type placed on a live site one at a time and asserted against the delivered HTML, every style and node property swept against the compiled CSS and markup, and 569 responsive declarations checked against the stylesheet the site actually served.",
21
21
  "license": "MIT",
22
22
  "author": "moksa (https://moksaweb.com)",
23
- "version": "1.15.1"
23
+ "version": "1.16.0"
24
24
  },
25
25
  "loaderBehaviour": "Auto-loads on session start when SKILL.md frontmatter parses successfully.",
26
26
  "verified": true,
@@ -20,7 +20,7 @@
20
20
  "description": "Build and modify Mosaic Pro (Nextend) sites by writing the underlying data model directly - no visual editor, no DOM. Query the real surface (122 node types, 181 node properties, 98 style properties, 53 style states, 151 element classes, 74 dynamic variables, 114 REST routes, 23 tables) instead of guessing, with every node type placed on a live site one at a time and asserted against the delivered HTML, every style and node property swept against the compiled CSS and markup, and 569 responsive declarations checked against the stylesheet the site actually served.",
21
21
  "license": "MIT",
22
22
  "author": "moksa (https://moksaweb.com)",
23
- "version": "1.15.1"
23
+ "version": "1.16.0"
24
24
  },
25
25
  "loaderBehaviour": "Confirmed (2026-07-11): Codex CLI natively supports the SKILL.md spec. Place SKILL.md under .codex/skills/<name>/ (project) or ~/.codex/skills/<name>/ (personal) and Codex loads the name+description at session start, then the full body on demand. A parallel, broader convention .agents/skills/ (searched from cwd up to repo root, then ~/.agents/skills/) also exists across multiple tools - if your Codex CLI version prioritizes that path instead, mirror the same SKILL.md there.",
26
26
  "verified": true,
@@ -20,7 +20,7 @@
20
20
  "description": "Build and modify Mosaic Pro (Nextend) sites by writing the underlying data model directly - no visual editor, no DOM. Query the real surface (122 node types, 181 node properties, 98 style properties, 53 style states, 151 element classes, 74 dynamic variables, 114 REST routes, 23 tables) instead of guessing, with every node type placed on a live site one at a time and asserted against the delivered HTML, every style and node property swept against the compiled CSS and markup, and 569 responsive declarations checked against the stylesheet the site actually served.",
21
21
  "license": "MIT",
22
22
  "author": "moksa (https://moksaweb.com)",
23
- "version": "1.15.1"
23
+ "version": "1.16.0"
24
24
  },
25
25
  "loaderBehaviour": "CHANGED as of 2026-07-11: GitHub Copilot added a proper '.github/skills/' Agent Skills directory (December 2025), alongside the older single-file .github/copilot-instructions.md convention. This config targets the new skills-directory form. If your Copilot version predates this (pre Dec 2025), use the instructions-append fallback instead (see fallback below).",
26
26
  "fallback": {
@@ -20,7 +20,7 @@
20
20
  "description": "Build and modify Mosaic Pro (Nextend) sites by writing the underlying data model directly - no visual editor, no DOM. Query the real surface (122 node types, 181 node properties, 98 style properties, 53 style states, 151 element classes, 74 dynamic variables, 114 REST routes, 23 tables) instead of guessing, with every node type placed on a live site one at a time and asserted against the delivered HTML, every style and node property swept against the compiled CSS and markup, and 569 responsive declarations checked against the stylesheet the site actually served.",
21
21
  "license": "MIT",
22
22
  "author": "moksa (https://moksaweb.com)",
23
- "version": "1.15.1"
23
+ "version": "1.16.0"
24
24
  },
25
25
  "loaderBehaviour": "CHANGED as of 2026-07-11: Gemini CLI now natively supports the same SKILL.md standard as Claude Code and Codex CLI - the same directory-based skill works unmodified. Gemini CLI discovers skills in this precedence order: built-in, extension skills, ~/.gemini/skills/ (personal), .gemini/skills/ (project, shared via version control). At session start Gemini injects each discovered skill's name+description into the system prompt and calls activate_skill when a task matches.",
26
26
  "verified": true,