@wcstack/devtools 4.0.0-rc.6 → 4.0.0-rc.7
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 -8
- package/README.md +17 -8
- package/dist/auto.min.js +1 -1
- package/dist/auto.min.js.map +1 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.esm.js +110 -13
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
## クイックスタート
|
|
21
21
|
|
|
22
22
|
```html
|
|
23
|
-
<!-- @wcstack/state より前に読む:
|
|
23
|
+
<!-- @wcstack/state より前に読む: 配線台帳が最初からライブになる(遅延アタッチを参照) -->
|
|
24
24
|
<script type="module" src="https://esm.run/@wcstack/devtools/auto"></script>
|
|
25
25
|
<script type="module" src="https://esm.run/@wcstack/state/auto"></script>
|
|
26
26
|
```
|
|
@@ -38,7 +38,7 @@ auto エントリは `<wcs-devtools>` を定義し、ページに無ければ `<
|
|
|
38
38
|
|
|
39
39
|
| ペイン | 内容 |
|
|
40
40
|
|---|---|
|
|
41
|
-
| **State** | 各ルート state ツリー(root node ごとに 1 本・ラベルはルート由来 — v2 に名前次元は無い)のトップレベルキー、配列/オブジェクトの展開、computed getter。値クリックでインライン編集 — 書き込みは通常のリアクティブパイプライン(set トラップ → 更新バッチ → DOM)を通るため、アプリコードが書いたのと同じようにページが反応する。**パス**クリックで束縛ノードをハイライト。ツリーの下には、マウント記録の **Overlays** と、`$eq` / `$eqPath` / `$eqIndex` の購読を path ごとに数えた **Keyed selection**(行・鍵・リスト単位の監視の数と最後の値)が出る。path が getter で、読む getter が全部再評価される形には `tracked` バッジが付く(`@wcstack/state` 3.0
|
|
41
|
+
| **State** | 各ルート state ツリー(root node ごとに 1 本・ラベルはルート由来 — v2 に名前次元は無い)のトップレベルキー、配列/オブジェクトの展開、computed getter。値クリックでインライン編集 — 書き込みは通常のリアクティブパイプライン(set トラップ → 更新バッチ → DOM)を通るため、アプリコードが書いたのと同じようにページが反応する。**パス**クリックで束縛ノードをハイライト。ツリーの下には、マウント記録の **Overlays** と、`$eq` / `$eqPath` / `$eqIndex` の購読を path ごとに数えた **Keyed selection**(行・鍵・リスト単位の監視の数と最後の値)が出る。path が getter で、読む getter が全部再評価される形には `tracked` バッジが付く(`@wcstack/state` 3.0 以降)。パネルを開いている間は、更新バッチのたびにペインを読み直す(多くて 1 フレームに 1 回。タイムラインの一時停止中も読み直す)。インライン編集の入力欄にフォーカスがある間と、ペインの中でポインタを押している間は描き直しを待ち、終わってから追いつく。 |
|
|
42
42
|
| **Wiring** | ライブ binding 台帳: binding ごとの `property ← path` 行と型バッジ(`text` / `prop` / `for` / …)。**⌖ pick** でページ要素をクリックするとその要素の配線だけに絞れる。行クリックで束縛ノードをハイライト。**coverage** の切り替えは、state が宣言したものと、パネルが観測を始めてから起きたことを突き合わせる: `$watch` のキーごと(`fired` ×n / `never`)、command・event トークンごと(`emitted` ×n / `never` / `emitted-unheard` — どの発火も購読者ゼロ)、宣言したバインディングごと(`attached` / `never-attached`)。`@wcstack/state` 3.x では、リストに `for` のバインドも `$listKeys` の宣言も無いワイルドカードの行 watch は、`never` ではなく `prerequisite-missing` と出る(3.x ではリストへの書き込みがその watch に届かない)。4.0 の行 watch はどちらも要らないので、4.0 のページでは出ない。 |
|
|
43
43
|
| **Timeline** | ring buffer(既定 500 件): `write`(旧値が取れた場合は併記)、`batch`(drain ごとの dedup 済み更新アドレス)、`command` / `event` トークン発火(引数要約 + 購読者数 — **購読者ゼロの空撃ちには警告バッジ**。whenDefined 前配線レースの検出に効く)、state 要素の登録/解除。⏸ で一時停止、🗑 でクリア。 |
|
|
44
44
|
|
|
@@ -53,11 +53,19 @@ auto エントリは `<wcs-devtools>` を定義し、ページに無ければ `<
|
|
|
53
53
|
|
|
54
54
|
## 遅延アタッチ
|
|
55
55
|
|
|
56
|
-
バインディング構築**後**に devtools
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
バインディング構築**後**に devtools がロード(または注入)されたときに見えるものは、
|
|
57
|
+
フックのプロトコルで話す相手のランタイムによって違う:
|
|
58
|
+
|
|
59
|
+
- **`@wcstack/state` 4.0** はバインディングを列挙できる。devtools がアタッチすると、
|
|
60
|
+
ランタイムがその時点のバインディングをすべて `state:binding-added` として送るので、
|
|
61
|
+
Wiring ペインには、先に読み込んだときと同じライブ台帳が出る。
|
|
62
|
+
- **`@wcstack/state` 3.x** は列挙できる台帳を持たないので、アタッチ前の
|
|
63
|
+
`binding-added` は復元できない。Wiring ペインは **declared** ビュー
|
|
64
|
+
(`data-wcs` 属性と `wcs-*` コメントの再スキャン)にフォールバックし、リロード
|
|
65
|
+
導線を出す。
|
|
66
|
+
|
|
67
|
+
どちらのランタイムでも、それ以外(state ツリー・編集・アタッチ以降のタイムライン)は
|
|
68
|
+
全機能動く(アタッチ前のイベントは再生しない)。プロトコル §6 参照。
|
|
61
69
|
|
|
62
70
|
## 注意・制限
|
|
63
71
|
|
|
@@ -78,7 +86,7 @@ import { DevtoolsCore, getOrCreateHookRegistry, formatValue, scanDeclaredBinding
|
|
|
78
86
|
|
|
79
87
|
const core = new DevtoolsCore({ timelineCapacity: 200 });
|
|
80
88
|
core.connect();
|
|
81
|
-
core.onChange((kind) => { /* "sources" | "roster" | "wiring" | "timeline" | "coverage" */ });
|
|
89
|
+
core.onChange((kind) => { /* "sources" | "roster" | "wiring" | "timeline" | "coverage" | "values" */ });
|
|
82
90
|
core.getRoster(); // 観測中の <wcs-state> 要素
|
|
83
91
|
core.getCoverageReport(); // 宣言 × 実測(coverage ビュー)
|
|
84
92
|
core.getTimeline(); // ring buffer スナップショット
|
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ command/event-token emissions.
|
|
|
21
21
|
## Quick start
|
|
22
22
|
|
|
23
23
|
```html
|
|
24
|
-
<!-- load BEFORE @wcstack/state so the wiring ledger is
|
|
24
|
+
<!-- load BEFORE @wcstack/state so the wiring ledger is live from the start (see Late attach) -->
|
|
25
25
|
<script type="module" src="https://esm.run/@wcstack/devtools/auto"></script>
|
|
26
26
|
<script type="module" src="https://esm.run/@wcstack/state/auto"></script>
|
|
27
27
|
```
|
|
@@ -40,7 +40,7 @@ yourself:
|
|
|
40
40
|
|
|
41
41
|
| Pane | What it shows |
|
|
42
42
|
|---|---|
|
|
43
|
-
| **State** | Every root state tree (one per root node, labelled by its root — v2 has no name dimension): top-level keys, expandable arrays/objects, computed getters. Click a value to edit it inline — the write goes through the normal reactive pipeline (set trap → update batch → DOM), so the page reacts exactly as if application code had written it. Click a **path** to highlight the DOM nodes bound to it. Below the tree, **Overlays** lists the mount records and **Keyed selection** the `$eq` / `$eqPath` / `$eqIndex` subscriptions per path — rows, keys, list watchers and the last value — with a `tracked` badge when the path is a getter, so every getter reading it re-evaluates (`@wcstack/state` 3.0+). |
|
|
43
|
+
| **State** | Every root state tree (one per root node, labelled by its root — v2 has no name dimension): top-level keys, expandable arrays/objects, computed getters. Click a value to edit it inline — the write goes through the normal reactive pipeline (set trap → update batch → DOM), so the page reacts exactly as if application code had written it. Click a **path** to highlight the DOM nodes bound to it. Below the tree, **Overlays** lists the mount records and **Keyed selection** the `$eq` / `$eqPath` / `$eqIndex` subscriptions per path — rows, keys, list watchers and the last value — with a `tracked` badge when the path is a getter, so every getter reading it re-evaluates (`@wcstack/state` 3.0+). While the panel is open, the pane is read again after every update batch (at most once per frame, and also while the timeline is paused); it holds still while an inline edit has focus or a pointer is pressed in it, then catches up. |
|
|
44
44
|
| **Wiring** | The live binding ledger: `property ← path` rows per binding, with type badges (`text` / `prop` / `for` / …). Use **⌖ pick** to click a page element and see only its bindings. Rows highlight their bound nodes on click. The **coverage** toggle compares what the state declares with what has happened since the panel started observing: each `$watch` key (`fired` ×n / `never`), each command and event token (`emitted` ×n / `never` / `emitted-unheard` — every emit had zero subscribers) and each declared binding (`attached` / `never-attached`). With `@wcstack/state` 3.x, a wildcard row watch whose list has no `for` binding and no `$listKeys` declaration shows `prerequisite-missing` instead of `never` (3.x list writes do not reach such a watch); 4.0 row watches need neither, so a 4.0 page never shows it. |
|
|
45
45
|
| **Timeline** | A ring buffer (default 500) of `write` (with old value when available), `batch` (deduplicated update addresses per drain), `command` / `event` token emissions (with argument summaries and subscriber counts — **zero-subscriber emissions get a warning badge**, catching wired-before-`whenDefined` races), and state element registration. ⏸ pauses, 🗑 clears. |
|
|
46
46
|
|
|
@@ -55,11 +55,20 @@ yourself:
|
|
|
55
55
|
|
|
56
56
|
## Late attach
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
What devtools sees when it loads (or is injected) **after** the bindings were
|
|
59
|
+
built depends on the runtime it talks to through the hook protocol:
|
|
60
|
+
|
|
61
|
+
- **`@wcstack/state` 4.0** can list its bindings: when devtools attaches, the
|
|
62
|
+
runtime sends every current binding as `state:binding-added`, so the Wiring
|
|
63
|
+
pane shows the live ledger, as if devtools had loaded first.
|
|
64
|
+
- **`@wcstack/state` 3.x** keeps no ledger it can list, so the
|
|
65
|
+
`binding-added` events from before the attach are lost. The Wiring pane falls
|
|
66
|
+
back to a **declared** view (the `data-wcs` attributes and `wcs-*` comments,
|
|
67
|
+
scanned again) and offers a reload link.
|
|
68
|
+
|
|
69
|
+
With either runtime, everything else works fully: the state tree, editing, and
|
|
70
|
+
the timeline from that point on (earlier events are not replayed). See
|
|
71
|
+
protocol §6.
|
|
63
72
|
|
|
64
73
|
## Notes & limitations
|
|
65
74
|
|
|
@@ -80,7 +89,7 @@ import { DevtoolsCore, getOrCreateHookRegistry, formatValue, scanDeclaredBinding
|
|
|
80
89
|
|
|
81
90
|
const core = new DevtoolsCore({ timelineCapacity: 200 });
|
|
82
91
|
core.connect();
|
|
83
|
-
core.onChange((kind) => { /* "sources" | "roster" | "wiring" | "timeline" | "coverage" */ });
|
|
92
|
+
core.onChange((kind) => { /* "sources" | "roster" | "wiring" | "timeline" | "coverage" | "values" */ });
|
|
84
93
|
core.getRoster(); // observed <wcs-state> elements
|
|
85
94
|
core.getCoverageReport(); // declared × observed (the coverage view)
|
|
86
95
|
core.getTimeline(); // ring buffer snapshot
|
package/dist/auto.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e="__WCSTACK_DEVTOOLS_HOOK__";function t(){const t=globalThis,n=t[e];if(void 0!==n)return 2!==n.version&&console.warn(`[wcstack/devtools] hook registry version mismatch: found ${n.version}, expected 2. Keeping the existing registry (first-wins).`),n;const i=function(){const e=new Map,t=new Set,n=e=>{if(0===t.size)return void e._setSink(null);const n=e.id;e._setSink(e=>{for(const i of t)i.onEvent?.(n,e)})};return{version:2,sources:e,register(i){if(!e.has(i.id)){e.set(i.id,i),n(i);for(const e of t)e.onSourceRegistered?.(i)}},unregister(n){const i=e.get(n);if(void 0!==i){i._setSink(null),e.delete(n);for(const e of t)e.onSourceUnregistered?.(n)}},addListener(i){t.add(i);for(const t of e.values())n(t),i.onSourceRegistered?.(t);return()=>{if(t.delete(i))for(const t of e.values())n(t)}}}}();return t[e]=i,i}const n=80;function i(e){const t=e.constructor,n=t?.name;return"string"==typeof n&&n.length>0?n:"Object"}function o(e,t=2){switch(typeof e){case"string":return`"${e.length>n?e.slice(0,n)+"…":e}"`;case"number":case"boolean":case"bigint":return String(e);case"symbol":return e.toString();case"undefined":return"undefined";case"function":return"[[Function]]"}if(null===e)return"null";const s=e;if(Array.isArray(s)){if(t<=0)return`[[Array(${s.length})]]`;const e=s.slice(0,3).map(e=>o(e,t-1)),n=s.length>3?`, …(${s.length})`:"";return`[${e.join(", ")}${n}]`}if(function(e){const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t}(s)){if(t<=0)return"[[Object]]";const e=Object.keys(s),n=e.slice(0,3).map(e=>`${e}: ${o(s[e],t-1)}`),i=e.length>3?`, …(${e.length})`:"";return`{${n.join(", ")}${i}}`}return`[[${i(s)}]]`}function s(e){if(null!==e&&"object"==typeof e){const{name:t,message:o}=e;if("string"==typeof o){const s="string"==typeof t&&t.length>0?t:i(e),r=o.length>n?o.slice(0,n)+"…":o;return r.length>0?`${s}: ${r}`:s}}return o(e,1)}function r(e){if(0===e.length)return"";const t=e.slice(0,3).map(e=>{const t=o(e,1);return t.length>n?t.slice(0,n)+"…":t}),i=e.length>3?`, …(${e.length})`:"";return`${t.join(", ")}${i}`}const a=String.fromCharCode(0);function c(e){return e}function l(e,t){return e+a+t}function d(e,t){return e+a+t}function h(e){return"undefined"!=typeof ShadowRoot&&e instanceof ShadowRoot?`<${e.host.tagName.toLowerCase()}>`:9===e.nodeType?"document":e.nodeName.toLowerCase()}function u(e){return!(Number.parseInt(String(e),10)>=4)}function p(e,t,n){return void 0===n?{kind:e,name:t,status:"never",count:0,note:null}:n.zeroSubscriberCount===n.count?{kind:e,name:t,status:"emitted-unheard",count:n.count,note:`all ${n.count} emit(s) had 0 subscribers`}:{kind:e,name:t,status:"emitted",count:n.count,note:n.zeroSubscriberCount>0?`${n.zeroSubscriberCount}/${n.count} emit(s) had 0 subscribers`:null}}class m{_timelineCapacity;_removeListener=null;_sources=new Map;_roster=new Map;_wiringByPathKey=new Map;_wiringEntryByBinding=new WeakMap;_timeline=[];_seq=0;_paused=!1;_changeListeners=new Set;_observingSince=null;_watchFiredStats=new Map;_tokenEmitStats=new Map;_everAttachedKeys=new Set;constructor(e){this._timelineCapacity=e?.timelineCapacity??500}get connected(){return null!==this._removeListener}get paused(){return this._paused}set paused(e){this._paused=e}connect(){if(null!==this._removeListener)return;this._observingSince=performance.now();const e=t();this._removeListener=e.addListener({onSourceRegistered:e=>{this._sources.set(e.id,e),this._refreshRosterOf(e),this._notify("sources")},onSourceUnregistered:e=>{this._sources.delete(e),this._roster.delete(e),this._notify("sources"),this._notify("roster")},onEvent:(e,t)=>{this._ingest(e,t)}})}disconnect(){null!==this._removeListener&&(this._removeListener(),this._removeListener=null,this._sources.clear(),this._roster.clear(),this._wiringByPathKey.clear(),this._wiringEntryByBinding=new WeakMap,this._observingSince=null,this._watchFiredStats.clear(),this._tokenEmitStats.clear(),this._everAttachedKeys.clear(),this._notify("sources"),this._notify("roster"),this._notify("wiring"),this._notify("coverage"))}get observingSince(){return this._observingSince}onChange(e){return this._changeListeners.add(e),()=>{this._changeListeners.delete(e)}}getSources(){return[...this._sources.values()]}getRoster(){const e=[];for(const t of this._roster.values())e.push(...t);return e}refreshRoster(){for(const e of this._sources.values())this._refreshRosterOf(e);this._notify("roster")}getTimeline(){return this._timeline}clearTimeline(){this._timeline=[],this._notify("timeline")}getWiringForPath(e,t){const n=this._wiringByPathKey.get(e);if(void 0===n)return[];const i=this._collectAlive(n);return void 0===t?i:i.filter(e=>null===e.stateElementRef||e.stateElementRef.deref()===t)}getAllWiring(){const e=[];for(const t of this._wiringByPathKey.values())e.push(...this._collectAlive(t));return e}getWiringForNode(e){const t=[];for(const n of this._wiringByPathKey.values())for(const i of this._collectAlive(n)){const n=i.bindingRef.deref();(n.node===e||n.replaceNode===e||e.contains(n.node)||e.contains(n.replaceNode))&&t.push(i)}return t}getCanonicalDeclared(){let e=!1;const t=[],n=new Set;for(const[i,o]of this._sources){if("function"!=typeof o.getDeclaredBindings)continue;e=!0;const s=new Set;for(const e of this._roster.get(i)??[])s.add(e.rootNode);for(const e of s)for(const i of o.getDeclaredBindings(e)){const e=[i.statePathName,i.propName,i.bindingType,i.origin,i.raw].join(a);n.has(e)||(n.add(e),t.push(i))}}return e?t:null}getCoverageReport(e){const t=[];for(const n of this.getRoster()){const i=n.summary;if(void 0!==e&&i.element!==e)continue;const o=u(this._sources.get(n.sourceId).packageVersion);for(const e of i.watchPaths??[]){const n=this._measuredFor(this._watchFiredStats,c(e),i.element)?.count??0;if(n>0){t.push({kind:"watch",name:e,status:"fired",count:n,note:null});continue}if(!o){t.push({kind:"watch",name:e,status:"never",count:0,note:null});continue}const s=void 0!==i.keyedListPaths;let r=null;for(let t=e.indexOf(".*");-1!==t;t=e.indexOf(".*",t+2)){const n=e.slice(0,t);if(!(i.paths.list.has(n)||(i.keyedListPaths?.has(n)??!1))){r=n;break}}null===r?t.push({kind:"watch",name:e,status:"never",count:0,note:null}):t.push({kind:"watch",name:e,status:"prerequisite-missing",count:0,note:s?`list "${r}" has no for binding and no $listKeys declaration — list writes never reach its rows (only explicit-index writes can fire this watch)`:`no for binding observed for list "${r}" (a $listKeys declaration would still let list writes fire it)`})}for(const e of i.commandTokenNames)t.push(p("command",e,this._measuredFor(this._tokenEmitStats,l("command",e),i.element)));for(const e of i.eventTokenNames)t.push(p("eventToken",e,this._measuredFor(this._tokenEmitStats,l("event",e),i.element)))}const n=this.getCanonicalDeclared();if(null!==n){const e=new Set;for(const i of n){if("for"===i.bindingType||"if"===i.bindingType||"elseif"===i.bindingType||"else"===i.bindingType)continue;if(i.propName.startsWith("eventToken."))continue;const n=d(i.statePathName,i.propName);if(e.has(n))continue;e.add(n);const o=this._everAttachedKeys.has(n);t.push({kind:"binding",name:`${i.propName} ← ${i.statePathName}`,status:o?"attached":"never-attached",count:o?1:0,note:"fragment"===i.origin?"template interior (attaches when rows materialize)":null})}}return t}keysOf(e){const t=this._sources.get(e.sourceId);return void 0===t||"function"!=typeof t.keys?[]:t.keys(e.rootNode)}overlaysOf(e){const t=this._sources.get(e.sourceId);return void 0===t||"function"!=typeof t.overlays?null:t.overlays(e.rootNode)}keyedSubscriptionsOf(e){const t=this._sources.get(e.sourceId);return void 0===t||"function"!=typeof t.keyedSubscriptions?null:t.keyedSubscriptions(e.rootNode)}readValue(e,t,n){const i=this._sources.get(e.sourceId);if(void 0!==i)return i.read(e.rootNode,t,n)}writeValue(e,t,n,i){const o=this._sources.get(e.sourceId);void 0!==o&&o.write(e.rootNode,t,n,i)}_notify(e){for(const t of this._changeListeners)t(e)}_refreshRosterOf(e){const t=[],n=new Map;for(const i of e.getStateElements()){const o=h(i.rootNode),s=(n.get(o)??0)+1;n.set(o,s),t.push({sourceId:e.id,label:1===s?o:`${o}#${s}`,rootNode:i.rootNode,summary:i})}this._roster.set(e.id,t)}_statFor(e,t,n){let i=e.get(t);void 0===i&&(i=[],e.set(t,i));const o="object"==typeof n&&null!==n?n:null;for(const e of i)if(null===o?null===e.stateElementRef:e.stateElementRef?.deref()===o)return e;const s={stateElementRef:null===o?null:new WeakRef(o),count:0,zeroSubscriberCount:0};return i.push(s),s}_measuredFor(e,t,n){const i=e.get(t);if(void 0===i)return;let o=0,s=0;for(let e=i.length-1;e>=0;e--){const t=i[e].stateElementRef;null===t||void 0!==t.deref()?null!==t&&t.deref()!==n||(o+=i[e].count,s+=i[e].zeroSubscriberCount):i.splice(e,1)}return 0===i.length&&e.delete(t),0===o?void 0:{count:o,zeroSubscriberCount:s}}_collectAlive(e){const t=[];for(const n of e)void 0!==n.bindingRef.deref()?t.push(n):e.delete(n);return t}_appendTimeline(e){if(this._paused)return;this._timeline.push({...e,seq:this._seq++,time:performance.now()});const t=this._timeline.length-this._timelineCapacity;t>0&&this._timeline.splice(0,t),this._notify("timeline")}_labelOf(e){const t=e.listIndex?.indexes,n=e.absolutePathInfo.pathInfo.path;return void 0!==t?`${n}[${t.join(",")}]`:n}_ingest(e,t){switch(t.type){case"state:element-registered":{const n=this._sources.get(e);return void 0!==n&&(this._refreshRosterOf(n),this._notify("roster")),void this._appendTimeline({sourceId:e,kind:"element-registered",label:h(t.rootNode),detail:"",subscriberCount:null})}case"state:element-unregistered":{const n=this._sources.get(e);return void 0!==n&&(this._refreshRosterOf(n),this._notify("roster")),void this._appendTimeline({sourceId:e,kind:"element-unregistered",label:h(t.rootNode),detail:"",subscriberCount:null})}case"state:write":{const n=t.hasOldValue?`${o(t.value)} (was ${o(t.oldValue)})`:o(t.value);return void this._appendTimeline({sourceId:e,kind:"write",label:this._labelOf(t.absoluteAddress),detail:n,subscriberCount:null})}case"state:update-batch":{const n=[];let i=0;for(const e of t.addresses)i++,n.length<3&&n.push(this._labelOf(e));if(0===i)return;const o=i>n.length?`, …(${i})`:"";return void this._appendTimeline({sourceId:e,kind:"batch",label:`${i} address${1===i?"":"es"}`,detail:`${n.join(", ")}${o}`,subscriberCount:null})}case"state:binding-added":{const n=t.absoluteAddress.absolutePathInfo.pathInfo.path,i=n;let o=this._wiringByPathKey.get(i);void 0===o&&(o=new Set,this._wiringByPathKey.set(i,o));const s=t.absoluteAddress.absolutePathInfo.stateElement,r={sourceId:e,path:n,propName:t.binding.propName,bindingType:t.binding.bindingType,bindingRef:new WeakRef(t.binding),stateElementRef:"object"==typeof s&&null!==s?new WeakRef(s):null};return o.add(r),this._wiringEntryByBinding.set(t.binding,r),this._everAttachedKeys.add(d(n,t.binding.propName)),this._notify("wiring"),void this._notify("coverage")}case"state:binding-removed":{const e=this._wiringEntryByBinding.get(t.binding);if(void 0===e)return;this._wiringEntryByBinding.delete(t.binding);const n=e.path,i=this._wiringByPathKey.get(n);return void 0!==i&&(i.delete(e),0===i.size&&this._wiringByPathKey.delete(n)),void this._notify("wiring")}case"state:binding-cleared":{const e=t.absoluteAddress.absolutePathInfo.pathInfo.path;return void(this._wiringByPathKey.delete(e)&&this._notify("wiring"))}case"state:token-emit":{const n=this._statFor(this._tokenEmitStats,l(t.kind,t.tokenName),t.stateElement);return n.count+=1,0===t.subscriberCount&&(n.zeroSubscriberCount+=1),this._notify("coverage"),void this._appendTimeline({sourceId:e,kind:t.kind,label:t.tokenName,detail:r(t.args),subscriberCount:t.subscriberCount})}case"state:watch-fired":return this._statFor(this._watchFiredStats,t.path,t.stateElement).count+=1,void this._notify("coverage");case"state:watch-error":return void this._appendTimeline({sourceId:e,kind:"watch-error",label:t.path,detail:`${t.phase}: ${s(t.error)}`,subscriberCount:null});case"state:watch-chain-limit":return void this._appendTimeline({sourceId:e,kind:"watch-chain-limit",label:`depth > ${t.maxDepth}`,detail:t.paths.join(", "),subscriberCount:null});case"state:render-chain-limit":return void this._appendTimeline({sourceId:e,kind:"render-chain-limit",label:`depth > ${t.maxDepth}`,detail:t.paths.join(", "),subscriberCount:null});case"state:path-unresolved":return void this._appendTimeline({sourceId:e,kind:"path-unresolved",label:t.path,detail:`${t.source}: "${t.missingSegment}" is not declared`,subscriberCount:null});case"state:binding-apply-error":return void this._appendTimeline({sourceId:e,kind:"binding-apply-error",label:t.path,detail:`${t.bindingType}: ${s(t.error)}`,subscriberCount:null});case"propagation:suppressed":return void this._appendTimeline({sourceId:e,kind:"propagation-suppressed",label:t.member,detail:`${t.reason} (tx ${t.transactionId}, edge ${t.edgeId})`,subscriberCount:null});case"propagation:coalesced":return void this._appendTimeline({sourceId:e,kind:"propagation-coalesced",label:this._labelOf(t.absoluteAddress),detail:`tx ${t.droppedTransactionId} dropped (winner tx ${t.winnerTransactionId})`,subscriberCount:null});case"propagation:hop-limit":return void this._appendTimeline({sourceId:e,kind:"propagation-hop-limit",label:this._labelOf(t.absoluteAddress),detail:`hop ${t.hop} (tx ${t.transactionId})`,subscriberCount:null});case"contract:drift":{const n=void 0!==t.member?`: ${t.member}`:"",i="event-mismatch"===t.reason?` (sidecar ${t.sidecarEvent??"?"} / live ${t.liveEvent??"?"})`:"";return void this._appendTimeline({sourceId:e,kind:"contract-drift",label:t.tag,detail:`${t.reason}${n}${i}`,subscriberCount:null})}case"contract:manifest-read":case"contract:unsupported-extension":return}}}const g=["wcs-text","wcs-for","wcs-if","wcs-elseif","wcs-else"];function f(e,t,n){const i=t.trim();if(0===i.length)return null;const o=i.indexOf(":");if(o<0)return null;const s=i.slice(0,o).trim(),r=i.slice(o+1).trim();if(0===s.length||0===r.length)return null;const[a,...c]=r.split("|").map(e=>e.trim());return{element:e,propName:s,path:a,filters:c.filter(e=>e.length>0),origin:n,raw:i}}class _ extends HTMLElement{static get observedAttributes(){return["open","dock","hotkey"]}_core=null;_removeCoreListener=null;_panel=null;_badge=null;_stateSelect=null;_paneElements={};_wiringView="wiring";_highlightLayer=null;_dirtyPanes=new Set;_renderScheduled=!1;_selectedRosterKey=null;_selectedPath=null;_pickedNode=null;_pickMode=!1;_expanded=new Set;_hotkeyHandler=null;_pickHandler=null;get core(){return this._core}connectedCallback(){if(document.documentElement.hasAttribute("data-wcs-server"))return;null===this.shadowRoot&&this._buildShadow();const e=Number(this.getAttribute("buffer")??"");this._core=new m({timelineCapacity:Number.isFinite(e)&&e>0?e:void 0}),this._removeCoreListener=this._core.onChange(e=>{"roster"===e||"sources"===e?(this._markDirty("state"),this._markDirty("wiring")):"wiring"===e?this._markDirty("wiring"):"coverage"===e?"coverage"===this._wiringView&&this._markDirty("wiring"):this._markDirty("timeline")}),this._core.connect(),this._applyDock(),this._applyOpen(),this._installHotkey(),this._markDirty("state"),this._markDirty("wiring"),this._markDirty("timeline")}disconnectedCallback(){this._removeCoreListener?.(),this._removeCoreListener=null,this._core?.disconnect(),this._core=null,this._uninstallHotkey(),this._exitPickMode()}attributeChangedCallback(e){null!==this.shadowRoot&&("open"===e?this._applyOpen():"dock"===e?this._applyDock():"hotkey"===e&&(this._uninstallHotkey(),this._installHotkey()))}__flushRenderForTest(){this._renderDirty()}_buildShadow(){const e=this.attachShadow({mode:"open"}),t=document.createElement("style");t.textContent='\n:host {\n all: initial;\n position: fixed;\n z-index: 2147483647;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n inset: auto 0 0 auto;\n}\n* { box-sizing: border-box; }\n.badge {\n position: fixed;\n right: 12px;\n bottom: 12px;\n width: 40px;\n height: 40px;\n border-radius: 20px;\n border: 1px solid #2b4f78;\n background: #10263f;\n color: #9fd0ff;\n font-weight: 700;\n cursor: pointer;\n}\n.panel {\n position: fixed;\n display: flex;\n flex-direction: column;\n background: #0d1b2a;\n color: #d7e3f4;\n border: 1px solid #2b4f78;\n box-shadow: 0 0 24px rgba(0,0,0,.5);\n}\n.panel.dock-bottom { left: 0; right: 0; bottom: 0; height: 45vh; }\n.panel.dock-right { top: 0; right: 0; bottom: 0; width: 420px; }\n.panel[hidden] { display: none; }\nheader {\n display: flex;\n gap: 6px;\n align-items: center;\n padding: 6px 8px;\n border-bottom: 1px solid #2b4f78;\n background: #10263f;\n}\nheader .title { font-weight: 700; color: #9fd0ff; margin-right: 4px; }\nheader select, header button {\n font: inherit;\n background: #16324f;\n color: #d7e3f4;\n border: 1px solid #2b4f78;\n border-radius: 4px;\n padding: 2px 8px;\n cursor: pointer;\n}\nheader button[aria-pressed="true"] { background: #2b5d8f; }\nheader .spacer { flex: 1; }\n.panes { display: flex; flex: 1; min-height: 0; }\n.pane { flex: 1; min-width: 0; overflow: auto; padding: 6px 8px; border-right: 1px solid #1d3a5c; }\n.pane:last-child { border-right: none; }\n.pane h3 {\n margin: 0 0 6px;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .08em;\n color: #7ba7d4;\n}\n.tree-row { display: flex; gap: 6px; padding: 1px 0; align-items: baseline; white-space: nowrap; }\n.tree-row .toggle { width: 14px; cursor: pointer; color: #7ba7d4; user-select: none; }\n.tree-row .key { color: #9fd0ff; cursor: pointer; }\n.tree-row .value { color: #ffd9a0; overflow: hidden; text-overflow: ellipsis; }\n.tree-row .value.editable { cursor: pointer; }\n.tree-row input { font: inherit; background: #16324f; color: #ffd9a0; border: 1px solid #2b4f78; }\n.badge-tag {\n display: inline-block;\n padding: 0 4px;\n border-radius: 3px;\n font-size: 10px;\n background: #26456a;\n color: #a8c6e8;\n}\n.badge-tag.warn { background: #6a3326; color: #ffb3a0; }\n.badge-tag.declared { background: #4a4426; color: #efe3a0; }\n.wiring-row, .timeline-row { padding: 1px 0; white-space: nowrap; }\n.wiring-row .prop { color: #b7f0c0; }\n.wiring-row .path { color: #9fd0ff; }\n.timeline-row .t { color: #6f88a3; }\n.timeline-row .label { color: #9fd0ff; }\n.timeline-row .detail { color: #ffd9a0; }\n.timeline-row .kind { display: inline-block; min-width: 52px; text-align: center; }\n.empty { color: #6f88a3; font-style: italic; padding: 4px 0; }\n.notice { color: #efe3a0; padding: 2px 0 6px; }\n.notice button { font: inherit; margin-left: 6px; cursor: pointer; }\n.wiring-row .detail { color: #6f88a3; }\n.overlays-heading { margin-top: 10px; }\n.overlay-row { padding: 1px 0; white-space: nowrap; }\n.overlay-row .prop { color: #b7f0c0; }\n.overlay-row .path { color: #9fd0ff; }\n.overlay-row .detail { color: #6f88a3; }\n.keyed-row { padding: 1px 0; white-space: nowrap; }\n.keyed-row .path { color: #9fd0ff; }\n.keyed-row .detail { color: #6f88a3; }\n.wiring-tabs { padding: 0 0 4px; }\n.wiring-tabs button { font: inherit; cursor: pointer; margin-right: 4px; opacity: 0.6; }\n.wiring-tabs button.active { opacity: 1; font-weight: bold; }\n.hl-box {\n position: fixed;\n pointer-events: none;\n border: 1px solid #58c2ff;\n background: rgba(88,194,255,.18);\n}\n',e.append(t);const n=document.createElement("button");n.className="badge",n.title="wcstack DevTools",n.textContent="WCS",n.addEventListener("click",()=>this.toggle()),e.append(n),this._badge=n;const i=document.createElement("div");i.className="panel dock-bottom",i.hidden=!0;const o=document.createElement("header"),s=document.createElement("span");s.className="title",s.textContent="wcstack devtools",o.append(s);const r=document.createElement("select");r.title="state element",r.addEventListener("change",()=>{this._selectedRosterKey=r.value||null,this._selectedPath=null,this._expanded.clear(),this._markDirty("state"),this._markDirty("wiring")}),o.append(r),this._stateSelect=r;const a=this._headerButton(o,"⌖ pick","pick a page element");a.addEventListener("click",()=>{this._pickMode?this._exitPickMode():this._enterPickMode(),a.setAttribute("aria-pressed",String(this._pickMode))}),a.dataset.role="pick";const c=this._headerButton(o,"⏸","pause timeline");c.addEventListener("click",()=>{const e=this._core;null!==e&&(e.paused=!e.paused,c.setAttribute("aria-pressed",String(e.paused)))}),c.dataset.role="pause";const l=this._headerButton(o,"🗑","clear timeline");l.addEventListener("click",()=>{this._core?.clearTimeline()}),l.dataset.role="clear";const d=document.createElement("span");d.className="spacer",o.append(d);const h=this._headerButton(o,"dock","toggle dock position");h.addEventListener("click",()=>{const e="bottom"===(this.getAttribute("dock")??"bottom")?"right":"bottom";this.setAttribute("dock",e)}),h.dataset.role="dock";const u=this._headerButton(o,"×","close");u.addEventListener("click",()=>this.toggle(!1)),u.dataset.role="close",i.append(o);const p=document.createElement("div");p.className="panes";for(const[e,t]of[["state","State"],["wiring","Wiring"],["timeline","Timeline"]]){const n=document.createElement("section");n.className=`pane pane-${e}`;const i=document.createElement("h3");i.textContent=t;const o=document.createElement("div");o.className="pane-body",n.append(i,o),p.append(n),this._paneElements[e]=o}i.append(p),e.append(i),this._panel=i;const m=document.createElement("div");m.className="hl-layer",e.append(m),this._highlightLayer=m}_headerButton(e,t,n){const i=document.createElement("button");return i.textContent=t,i.title=n,i.setAttribute("aria-pressed","false"),e.append(i),i}get open(){return this.hasAttribute("open")}toggle(e){e??!this.open?this.setAttribute("open",""):this.removeAttribute("open")}_applyOpen(){this._panel.hidden=!this.open,this._badge.hidden=this.open,this.open&&(this._markDirty("state"),this._markDirty("wiring"),this._markDirty("timeline"))}_applyDock(){const e="right"===this.getAttribute("dock")?"right":"bottom";this._panel.classList.toggle("dock-right","right"===e),this._panel.classList.toggle("dock-bottom","bottom"===e)}_installHotkey(){const e=this.getAttribute("hotkey")??"Alt+Shift+D";if("none"===e)return;const t=e.split("+").map(e=>e.trim().toLowerCase()),n=t[t.length-1],i=t.includes("alt"),o=t.includes("shift"),s=t.includes("ctrl"),r=t.includes("meta");this._hotkeyHandler=e=>{e.key.toLowerCase()===n&&e.altKey===i&&e.shiftKey===o&&e.ctrlKey===s&&e.metaKey===r&&(e.preventDefault(),this.toggle())},window.addEventListener("keydown",this._hotkeyHandler)}_uninstallHotkey(){null!==this._hotkeyHandler&&(window.removeEventListener("keydown",this._hotkeyHandler),this._hotkeyHandler=null)}_enterPickMode(){this._pickMode=!0,this._pickHandler=e=>{const t=e.target;t.getRootNode()!==this.shadowRoot&&t!==this&&(e.preventDefault(),e.stopPropagation(),this._pickedNode=t,this._selectedPath=null,this._exitPickMode(),this._markDirty("wiring"))},document.addEventListener("click",this._pickHandler,{capture:!0})}_exitPickMode(){null!==this._pickHandler&&(document.removeEventListener("click",this._pickHandler,{capture:!0}),this._pickHandler=null),this._pickMode=!1;const e=this.shadowRoot?.querySelector('button[data-role="pick"]');e?.setAttribute("aria-pressed","false")}_markDirty(e){this._dirtyPanes.add(e),this._renderScheduled||(this._renderScheduled=!0,requestAnimationFrame(()=>{this._renderDirty()}))}_renderDirty(){if(this._renderScheduled=!1,null===this._core||null===this.shadowRoot)return void this._dirtyPanes.clear();const e=this._dirtyPanes;this._dirtyPanes=new Set,e.has("state")&&this._renderStatePane(),e.has("wiring")&&this._renderWiringPane(),e.has("timeline")&&this._renderTimelinePane()}_rosterKey(e){return`${e.sourceId}:${e.label}`}_selectedRoster(){const e=this._core.getRoster();if(0===e.length)return null;return e.find(e=>this._rosterKey(e)===this._selectedRosterKey)??e[0]}_renderStatePane(){const e=this._core,t=this._paneElements.state,n=this._stateSelect,i=e.getRoster(),o=this._selectedRoster();if(n.replaceChildren(...i.map(e=>{const t=document.createElement("option");return t.value=this._rosterKey(e),t.textContent=`${e.label} (${e.sourceId.slice(0,12)})`,t.selected=null!==o&&this._rosterKey(e)===this._rosterKey(o),t})),t.replaceChildren(),null===o)return void t.append(this._emptyRow("no <wcs-state> elements observed"));this._selectedRosterKey=this._rosterKey(o);const s=e.keysOf(o);if(0===s.length)return t.append(this._emptyRow("no readable keys (runtime without keys() API?)")),this._renderOverlaysSection(t,o),void this._renderKeyedSection(t,o);for(const e of s)this._renderTreeNode(t,o,{path:e,indexes:[]},e,0);this._renderOverlaysSection(t,o),this._renderKeyedSection(t,o)}_renderKeyedSection(e,t){const n=this._core.keyedSubscriptionsOf(t);if(null===n||0===n.length)return;const i=document.createElement("h3");i.className="overlays-heading",i.textContent=`Keyed selection (${n.length} path${1===n.length?"":"s"})`,e.append(i);for(const t of n)e.append(this._keyedRow(t))}_keyedRow(e){const t=document.createElement("div");t.className="keyed-row";const n=document.createElement("span");if(n.className="path",n.textContent=e.path,t.append(n),e.tracked){const e=document.createElement("span");e.className="badge-tag warn",e.textContent="tracked",e.title="the path is a getter or under one, so $eq cannot subscribe by key: every getter that reads it re-evaluates when it changes",t.append(document.createTextNode(" "),e)}const i=[];e.rows>0&&i.push(`rows ${e.rows} · keys ${e.keys}`),e.lists>0&&i.push(`list watchers ${e.lists}`),e.tracked||i.push(`last ${o(e.lastValue,1)}`);const s=document.createElement("span");return s.className="detail",s.textContent=i.length>0?` ${i.join(" · ")}`:"",t.append(s),t}_renderOverlaysSection(e,t){const n=this._core.overlaysOf(t);if(null===n||0===n.length)return;const i=document.createElement("h3");i.className="overlays-heading",i.textContent=`Overlays (${n.length} mount${1===n.length?"":"s"})`,e.append(i);for(const t of n)e.append(this._overlayRow(t))}_overlayRow(e){const t=document.createElement("div");t.className="overlay-row";const n=document.createElement("span");n.className="badge-tag",n.textContent=e.marker;const i=document.createElement("span");i.className="prop",i.textContent=`<${e.componentTag}>`;const o=document.createElement("span");o.className="path",o.textContent=e.stateProp,t.append(n,document.createTextNode(" "),i,document.createTextNode(" "),o);const s=[];for(const t of e.mountTable)s.push(`${""===t.inner?"(root)":t.inner} → ${t.outer}`);e.delta>0&&s.push(`Δ${e.delta}`),e.privateKeys.length>0&&s.push(`private: ${e.privateKeys.join(", ")}`);const r=e.exports??[];if(r.length>0&&s.push(`exports: ${r.join(", ")}`),e.getterKeys.length>0&&s.push(`getters: ${e.getterKeys.join(", ")}`),s.length>0){const e=document.createElement("span");e.className="detail",e.textContent=` ${s.join(" · ")}`,t.append(e)}return t}_renderTreeNode(e,t,n,i,s){const r=this._core;let a,c=!0;try{a=r.readValue(t,n.path,n.indexes)}catch{c=!1}const l=document.createElement("div");l.className="tree-row",l.style.paddingLeft=14*s+"px";const d=c&&function(e){if(Array.isArray(e))return!0;if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t}(a),h=function(e){return`${e.path}#${e.indexes.join(",")}`}(n),u=d&&this._expanded.has(h),p=document.createElement("span");p.className="toggle",p.textContent=d?u?"▾":"▸":"",d&&p.addEventListener("click",()=>{this._expanded.has(h)?this._expanded.delete(h):this._expanded.add(h),this._markDirty("state")}),l.append(p);const m=document.createElement("span");m.className="key",m.textContent=`${i}:`,m.title=n.path,m.addEventListener("click",()=>{this._selectedPath=n.path,this._pickedNode=null,this._markDirty("wiring"),this._highlightPath(t,n.path)}),l.append(m);const g=document.createElement("span");if(g.className="value",c){g.textContent=o(a,1);(null===a||"object"!=typeof a)&&"function"!=typeof a&&(g.classList.add("editable"),g.title="click to edit",g.addEventListener("click",()=>{this._beginEdit(l,g,t,n,a)}))}else g.textContent="(unreadable getter)";if(l.append(g),e.append(l),u)if(Array.isArray(a)){const i=Math.min(a.length,20);for(let o=0;o<i;o++)this._renderTreeNode(e,t,{path:`${n.path}.*`,indexes:[...n.indexes,o]},`[${o}]`,s+1);if(a.length>i){const t=this._emptyRow(`…(${a.length} items)`);t.style.paddingLeft=14*(s+1)+"px",e.append(t)}}else for(const i of Object.keys(a))this._renderTreeNode(e,t,{path:`${n.path}.${i}`,indexes:n.indexes},i,s+1)}_beginEdit(e,t,n,i,o){const s=document.createElement("input");s.value="string"==typeof o?o:String(o);const r=()=>{this._core?.writeValue(n,i.path,function(e){const t=e.trim();try{return JSON.parse(t)}catch{return e}}(s.value),i.indexes),this._markDirty("state")};s.addEventListener("keydown",e=>{"Enter"===e.key?r():"Escape"===e.key&&this._markDirty("state")}),e.replaceChild(s,t),s.focus()}_renderWiringPane(){const e=this._core,t=this._paneElements.wiring;t.replaceChildren();const n=document.createElement("div");n.className="wiring-tabs";for(const[e,t]of[["wiring","wiring"],["coverage","coverage"]]){const i=document.createElement("button");i.textContent=t,this._wiringView===e&&i.classList.add("active"),i.addEventListener("click",()=>{this._wiringView=e,this._markDirty("wiring")}),n.append(i)}if(t.append(n),"coverage"===this._wiringView)return void this._renderCoverageView(t);let i,o;if(null!==this._pickedNode){i=e.getWiringForNode(this._pickedNode);const t=this._pickedNode;o=t instanceof Element?`<${t.tagName.toLowerCase()}>`:t.nodeName}else if(null!==this._selectedPath){const t=this._selectedRoster();i=null!==t?e.getWiringForPath(this._selectedPath,t.summary.element):[],o=this._selectedPath}else i=e.getAllWiring(),o="all";const s=document.createElement("div");if(s.textContent=`context: ${o} — ${i.length} live binding${1===i.length?"":"s"}`,t.append(s),i.length>0){for(const e of i)t.append(this._wiringRow(e));return}const r=e.getCanonicalDeclared(),a=this._selectedRoster(),c=null===r&&null!==a?function(e,t="data-wcs"){const n=[];for(const i of e.querySelectorAll(`[${t}]`)){const e=i.getAttribute(t);for(const t of e.split(";")){const e=f(i,t,"attribute");null!==e&&n.push(e)}}const i=(e.ownerDocument??e).createTreeWalker(e,NodeFilter.SHOW_COMMENT);let o=i.nextNode();for(;null!==o;){const e=o.textContent.trim(),t=g.find(t=>e===t||e.startsWith(t+":"));if(void 0!==t){const i=o.parentNode instanceof Element?o.parentNode:null;if(null!==i){const o=e===t?"":e.slice(t.length+1).trim();if(o.length>0){const e="wcs-text"===t?"textContent":t.slice(4);n.push(f(i,`${e}: ${o}`,"comment"))}}}o=i.nextNode()}return n}(this._scanRootOf(a)):[];if(0===(r?.length??0)&&0===c.length)return void t.append(this._emptyRow("no bindings observed"));const l=document.createElement("div");l.className="notice";const d=document.createElement("span");d.className="badge-tag declared",d.textContent=null!==r?"declared (canonical)":"declared",l.append(d,document.createTextNode(" attached late — reload to capture live bindings "));const h=document.createElement("button");if(h.textContent="reload",h.addEventListener("click",()=>{location.reload()}),l.append(h),t.append(l),null!==r)for(const e of r)t.append(this._canonicalDeclaredRow(e));else for(const e of c)t.append(this._declaredRow(e))}_renderCoverageView(e){const t=this._core,n=t.observingSince,i=document.createElement("div"),o=null===n?null:new Date(Date.now()-(performance.now()-n)).toLocaleTimeString();i.textContent=null===o?"not observing":`observing since ${o} (declared vs measured after this point)`,e.append(i);const s=this._selectedRoster(),r=t.getCoverageReport(s?.summary.element);if(0!==r.length)for(const t of r){const n=document.createElement("div");n.className="wiring-row";const i=document.createElement("span");i.className="badge-tag",i.textContent=t.kind;const o=document.createElement("span");o.className="badge-tag",o.textContent=t.count>1?`${t.status} ×${t.count}`:t.status,"never"===t.status||"never-attached"===t.status||"emitted-unheard"===t.status?o.classList.add("warn"):"prerequisite-missing"===t.status&&o.classList.add("declared");const s=document.createElement("span");if(s.className="path",s.textContent=` ${t.name} `,n.append(i,document.createTextNode(" "),s,o),null!==t.note){const e=document.createElement("span");e.className="detail",e.textContent=` ${t.note}`,n.append(e)}e.append(n)}else e.append(this._emptyRow("nothing declared to cover"))}_canonicalDeclaredRow(e){const t=document.createElement("div");t.className="wiring-row";const n=document.createElement("span");n.className="badge-tag declared",n.textContent=e.origin;const i=document.createElement("span");i.className="prop",i.textContent=e.propName;const o=document.createElement("span");o.className="path";const s=e.outFilters.map(e=>` | ${e.filterName}(${e.args.join(",")})`).join("");if(o.textContent=`${e.statePathName}${s}`,t.append(n,document.createTextNode(" "),i,document.createTextNode(" ← "),o),null!==e.node){const n=e.node;t.addEventListener("click",()=>{this._highlightNodes([n])})}return t}_scanRootOf(e){const t=e.rootNode;return t.nodeType===Node.DOCUMENT_NODE||t.nodeType===Node.DOCUMENT_FRAGMENT_NODE?t:document}_wiringRow(e){const t=document.createElement("div");t.className="wiring-row";const n=document.createElement("span");n.className="prop",n.textContent=e.propName;const i=document.createTextNode(" ← "),o=document.createElement("span");o.className="path",o.textContent=e.path;const s=document.createElement("span");return s.className="badge-tag",s.textContent=e.bindingType,t.append(s,document.createTextNode(" "),n,i,o),t.addEventListener("click",()=>{const t=e.bindingRef.deref();void 0!==t&&this._highlightNodes([t.node,t.replaceNode])}),t}_declaredRow(e){const t=document.createElement("div");t.className="wiring-row";const n=document.createElement("span");n.className="badge-tag declared",n.textContent=e.origin;const i=document.createElement("span");i.className="prop",i.textContent=e.propName;const o=document.createElement("span");return o.className="path",o.textContent=e.path,t.append(n,document.createTextNode(" "),i,document.createTextNode(" ← "),o),t.addEventListener("click",()=>{this._highlightNodes([e.element])}),t}_renderTimelinePane(){const e=this._core,t=this._paneElements.timeline,n=e.getTimeline(),i=Math.max(0,n.length-200),o=[];i>0&&o.push(this._emptyRow(`…(${i} earlier entries)`));for(let e=i;e<n.length;e++)o.push(this._timelineRow(n[e]));0===o.length&&o.push(this._emptyRow("no activity yet")),t.replaceChildren(...o),t.scrollTop=t.scrollHeight}_timelineRow(e){const t=document.createElement("div");t.className="timeline-row";const n=document.createElement("span");n.className="t",n.textContent=`${(e.time/1e3).toFixed(3)}s `;const i=document.createElement("span");i.className="badge-tag kind",i.textContent=e.kind,0===e.subscriberCount&&(i.classList.add("warn"),i.title="emitted with no subscribers"),"watch-error"!==e.kind&&"watch-chain-limit"!==e.kind||(i.classList.add("warn"),i.title="watch-error"===e.kind?"a $watch or $scan failure (read, handler, fold or write — see the phase in the detail); the runtime isolated it (console.error only)":"a $watch write chain hit the depth limit and was cut off"),"render-chain-limit"===e.kind&&(i.classList.add("warn"),i.title="writes made while rendering kept starting new batches; the chain hit the depth limit and this batch was not rendered"),"path-unresolved"!==e.kind&&"binding-apply-error"!==e.kind||(i.classList.add("warn"),i.title="path-unresolved"===e.kind?"a wired path does not resolve on the state; its updates are dropped":"a binding threw while applying; the runtime isolated it (console.error only)"),"propagation-hop-limit"!==e.kind&&"contract-drift"!==e.kind||(i.classList.add("warn"),i.title="propagation-hop-limit"===e.kind?"a two-way propagation chain hit the hop limit and was cut off":"sidecar manifest drifted from the live wcBindable declaration (live wins)");const o=document.createElement("span");o.className="label",o.textContent=` ${e.label} `;const s=document.createElement("span");return s.className="detail",s.textContent=e.detail,t.append(n,i,o,s),t}_emptyRow(e){const t=document.createElement("div");return t.className="empty",t.textContent=e,t}_highlightPath(e,t){const n=this._core,i=[];for(const o of n.getWiringForPath(t,e.summary.element)){const e=o.bindingRef.deref();void 0!==e&&i.push(e.node,e.replaceNode)}this._highlightNodes(i)}_highlightNodes(e){const t=this._highlightLayer;t.replaceChildren();const n=new Set;for(const i of e){const e=i instanceof Element?i:i.parentElement;if(null===e||n.has(e)||!e.isConnected)continue;n.add(e);const o=e.getBoundingClientRect(),s=document.createElement("div");s.className="hl-box",s.style.left=`${o.left}px`,s.style.top=`${o.top}px`,s.style.width=`${o.width}px`,s.style.height=`${o.height}px`,t.append(s)}setTimeout(()=>{t.replaceChildren()},2e3)}}const b="wcs-devtools";function y(){null===document.querySelector(b)&&document.body.appendChild(document.createElement(b))}document.documentElement.hasAttribute("data-wcs-server")||(customElements.get(b)||customElements.define(b,_),null!==document.body?y():document.addEventListener("DOMContentLoaded",y,{once:!0}));
|
|
1
|
+
const e="__WCSTACK_DEVTOOLS_HOOK__";function t(){const t=globalThis,n=t[e];if(void 0!==n)return 2!==n.version&&console.warn(`[wcstack/devtools] hook registry version mismatch: found ${n.version}, expected 2. Keeping the existing registry (first-wins).`),n;const i=function(){const e=new Map,t=new Set,n=e=>{if(0===t.size)return void e._setSink(null);const n=e.id;e._setSink(e=>{for(const i of t)i.onEvent?.(n,e)})};return{version:2,sources:e,register(i){if(!e.has(i.id)){e.set(i.id,i),n(i);for(const e of t)e.onSourceRegistered?.(i)}},unregister(n){const i=e.get(n);if(void 0!==i){i._setSink(null),e.delete(n);for(const e of t)e.onSourceUnregistered?.(n)}},addListener(i){t.add(i);for(const t of e.values())n(t),i.onSourceRegistered?.(t);return()=>{if(t.delete(i))for(const t of e.values())n(t)}}}}();return t[e]=i,i}const n=80;function i(e){const t=e.constructor,n=t?.name;return"string"==typeof n&&n.length>0?n:"Object"}function s(e,t=2){switch(typeof e){case"string":return`"${e.length>n?e.slice(0,n)+"…":e}"`;case"number":case"boolean":case"bigint":return String(e);case"symbol":return e.toString();case"undefined":return"undefined";case"function":return"[[Function]]"}if(null===e)return"null";const o=e;if(Array.isArray(o)){if(t<=0)return`[[Array(${o.length})]]`;const e=o.slice(0,3).map(e=>s(e,t-1)),n=o.length>3?`, …(${o.length})`:"";return`[${e.join(", ")}${n}]`}if(function(e){const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t}(o)){if(t<=0)return"[[Object]]";const e=Object.keys(o),n=e.slice(0,3).map(e=>`${e}: ${s(o[e],t-1)}`),i=e.length>3?`, …(${e.length})`:"";return`{${n.join(", ")}${i}}`}return`[[${i(o)}]]`}function o(e){if(null!==e&&"object"==typeof e){const{name:t,message:s}=e;if("string"==typeof s){const o="string"==typeof t&&t.length>0?t:i(e),r=s.length>n?s.slice(0,n)+"…":s;return r.length>0?`${o}: ${r}`:o}}return s(e,1)}function r(e){if(0===e.length)return"";const t=e.slice(0,3).map(e=>{const t=s(e,1);return t.length>n?t.slice(0,n)+"…":t}),i=e.length>3?`, …(${e.length})`:"";return`${t.join(", ")}${i}`}const a=String.fromCharCode(0);function l(e){return e}function c(e,t){return e+a+t}function d(e,t){return e+a+t}function h(e){return"undefined"!=typeof ShadowRoot&&e instanceof ShadowRoot?`<${e.host.tagName.toLowerCase()}>`:9===e.nodeType?"document":e.nodeName.toLowerCase()}function u(e){return!(Number.parseInt(String(e),10)>=4)}function p(e,t,n){return void 0===n?{kind:e,name:t,status:"never",count:0,note:null}:n.zeroSubscriberCount===n.count?{kind:e,name:t,status:"emitted-unheard",count:n.count,note:`all ${n.count} emit(s) had 0 subscribers`}:{kind:e,name:t,status:"emitted",count:n.count,note:n.zeroSubscriberCount>0?`${n.zeroSubscriberCount}/${n.count} emit(s) had 0 subscribers`:null}}class m{_timelineCapacity;_removeListener=null;_sources=new Map;_roster=new Map;_wiringByPathKey=new Map;_wiringEntryByBinding=new WeakMap;_timeline=[];_seq=0;_paused=!1;_changeListeners=new Set;_observingSince=null;_watchFiredStats=new Map;_tokenEmitStats=new Map;_everAttachedKeys=new Set;constructor(e){this._timelineCapacity=e?.timelineCapacity??500}get connected(){return null!==this._removeListener}get paused(){return this._paused}set paused(e){this._paused=e}connect(){if(null!==this._removeListener)return;this._observingSince=performance.now();const e=t();this._removeListener=e.addListener({onSourceRegistered:e=>{this._sources.set(e.id,e),this._refreshRosterOf(e),this._notify("sources")},onSourceUnregistered:e=>{this._sources.delete(e),this._roster.delete(e),this._notify("sources"),this._notify("roster")},onEvent:(e,t)=>{this._ingest(e,t)}})}disconnect(){null!==this._removeListener&&(this._removeListener(),this._removeListener=null,this._sources.clear(),this._roster.clear(),this._wiringByPathKey.clear(),this._wiringEntryByBinding=new WeakMap,this._observingSince=null,this._watchFiredStats.clear(),this._tokenEmitStats.clear(),this._everAttachedKeys.clear(),this._notify("sources"),this._notify("roster"),this._notify("wiring"),this._notify("coverage"))}get observingSince(){return this._observingSince}onChange(e){return this._changeListeners.add(e),()=>{this._changeListeners.delete(e)}}getSources(){return[...this._sources.values()]}getRoster(){const e=[];for(const t of this._roster.values())e.push(...t);return e}refreshRoster(){for(const e of this._sources.values())this._refreshRosterOf(e);this._notify("roster")}getTimeline(){return this._timeline}clearTimeline(){this._timeline=[],this._notify("timeline")}getWiringForPath(e,t){const n=this._wiringByPathKey.get(e);if(void 0===n)return[];const i=this._collectAlive(n);return void 0===t?i:i.filter(e=>null===e.stateElementRef||e.stateElementRef.deref()===t)}getAllWiring(){const e=[];for(const t of this._wiringByPathKey.values())e.push(...this._collectAlive(t));return e}getWiringForNode(e){const t=[];for(const n of this._wiringByPathKey.values())for(const i of this._collectAlive(n)){const n=i.bindingRef.deref();(n.node===e||n.replaceNode===e||e.contains(n.node)||e.contains(n.replaceNode))&&t.push(i)}return t}getCanonicalDeclared(){let e=!1;const t=[],n=new Set;for(const[i,s]of this._sources){if("function"!=typeof s.getDeclaredBindings)continue;e=!0;const o=new Set;for(const e of this._roster.get(i)??[])o.add(e.rootNode);for(const e of o)for(const i of s.getDeclaredBindings(e)){const e=[i.statePathName,i.propName,i.bindingType,i.origin,i.raw].join(a);n.has(e)||(n.add(e),t.push(i))}}return e?t:null}getCoverageReport(e){const t=[];for(const n of this.getRoster()){const i=n.summary;if(void 0!==e&&i.element!==e)continue;const s=u(this._sources.get(n.sourceId).packageVersion);for(const e of i.watchPaths??[]){const n=this._measuredFor(this._watchFiredStats,l(e),i.element)?.count??0;if(n>0){t.push({kind:"watch",name:e,status:"fired",count:n,note:null});continue}if(!s){t.push({kind:"watch",name:e,status:"never",count:0,note:null});continue}const o=void 0!==i.keyedListPaths;let r=null;for(let t=e.indexOf(".*");-1!==t;t=e.indexOf(".*",t+2)){const n=e.slice(0,t);if(!(i.paths.list.has(n)||(i.keyedListPaths?.has(n)??!1))){r=n;break}}null===r?t.push({kind:"watch",name:e,status:"never",count:0,note:null}):t.push({kind:"watch",name:e,status:"prerequisite-missing",count:0,note:o?`list "${r}" has no for binding and no $listKeys declaration — list writes never reach its rows (only explicit-index writes can fire this watch)`:`no for binding observed for list "${r}" (a $listKeys declaration would still let list writes fire it)`})}for(const e of i.commandTokenNames)t.push(p("command",e,this._measuredFor(this._tokenEmitStats,c("command",e),i.element)));for(const e of i.eventTokenNames)t.push(p("eventToken",e,this._measuredFor(this._tokenEmitStats,c("event",e),i.element)))}const n=this.getCanonicalDeclared();if(null!==n){const e=new Set;for(const i of n){if("for"===i.bindingType||"if"===i.bindingType||"elseif"===i.bindingType||"else"===i.bindingType)continue;if(i.propName.startsWith("eventToken."))continue;const n=d(i.statePathName,i.propName);if(e.has(n))continue;e.add(n);const s=this._everAttachedKeys.has(n);t.push({kind:"binding",name:`${i.propName} ← ${i.statePathName}`,status:s?"attached":"never-attached",count:s?1:0,note:"fragment"===i.origin?"template interior (attaches when rows materialize)":null})}}return t}keysOf(e){const t=this._sources.get(e.sourceId);return void 0===t||"function"!=typeof t.keys?[]:t.keys(e.rootNode)}overlaysOf(e){const t=this._sources.get(e.sourceId);return void 0===t||"function"!=typeof t.overlays?null:t.overlays(e.rootNode)}keyedSubscriptionsOf(e){const t=this._sources.get(e.sourceId);return void 0===t||"function"!=typeof t.keyedSubscriptions?null:t.keyedSubscriptions(e.rootNode)}readValue(e,t,n){const i=this._sources.get(e.sourceId);if(void 0!==i)return i.read(e.rootNode,t,n)}writeValue(e,t,n,i){const s=this._sources.get(e.sourceId);void 0!==s&&s.write(e.rootNode,t,n,i)}_notify(e){for(const t of this._changeListeners)t(e)}_refreshRosterOf(e){const t=[],n=new Map;for(const i of e.getStateElements()){const s=h(i.rootNode),o=(n.get(s)??0)+1;n.set(s,o),t.push({sourceId:e.id,label:1===o?s:`${s}#${o}`,rootNode:i.rootNode,summary:i})}this._roster.set(e.id,t)}_statFor(e,t,n){let i=e.get(t);void 0===i&&(i=[],e.set(t,i));const s="object"==typeof n&&null!==n?n:null;for(const e of i)if(null===s?null===e.stateElementRef:e.stateElementRef?.deref()===s)return e;const o={stateElementRef:null===s?null:new WeakRef(s),count:0,zeroSubscriberCount:0};return i.push(o),o}_measuredFor(e,t,n){const i=e.get(t);if(void 0===i)return;let s=0,o=0;for(let e=i.length-1;e>=0;e--){const t=i[e].stateElementRef;null===t||void 0!==t.deref()?null!==t&&t.deref()!==n||(s+=i[e].count,o+=i[e].zeroSubscriberCount):i.splice(e,1)}return 0===i.length&&e.delete(t),0===s?void 0:{count:s,zeroSubscriberCount:o}}_collectAlive(e){const t=[];for(const n of e)void 0!==n.bindingRef.deref()?t.push(n):e.delete(n);return t}_appendTimeline(e){if(this._paused)return;this._timeline.push({...e,seq:this._seq++,time:performance.now()});const t=this._timeline.length-this._timelineCapacity;t>0&&this._timeline.splice(0,t),this._notify("timeline")}_labelOf(e){const t=e.listIndex?.indexes,n=e.absolutePathInfo.pathInfo.path;return void 0!==t?`${n}[${t.join(",")}]`:n}_ingest(e,t){switch(t.type){case"state:element-registered":{const n=this._sources.get(e);return void 0!==n&&(this._refreshRosterOf(n),this._notify("roster")),void this._appendTimeline({sourceId:e,kind:"element-registered",label:h(t.rootNode),detail:"",subscriberCount:null})}case"state:element-unregistered":{const n=this._sources.get(e);return void 0!==n&&(this._refreshRosterOf(n),this._notify("roster")),void this._appendTimeline({sourceId:e,kind:"element-unregistered",label:h(t.rootNode),detail:"",subscriberCount:null})}case"state:write":{const n=t.hasOldValue?`${s(t.value)} (was ${s(t.oldValue)})`:s(t.value);return void this._appendTimeline({sourceId:e,kind:"write",label:this._labelOf(t.absoluteAddress),detail:n,subscriberCount:null})}case"state:update-batch":{const n=[];let i=0;for(const e of t.addresses)i++,n.length<3&&n.push(this._labelOf(e));if(0===i)return;this._notify("values");const s=i>n.length?`, …(${i})`:"";return void this._appendTimeline({sourceId:e,kind:"batch",label:`${i} address${1===i?"":"es"}`,detail:`${n.join(", ")}${s}`,subscriberCount:null})}case"state:binding-added":{const n=t.absoluteAddress.absolutePathInfo.pathInfo.path,i=n;let s=this._wiringByPathKey.get(i);void 0===s&&(s=new Set,this._wiringByPathKey.set(i,s));const o=t.absoluteAddress.absolutePathInfo.stateElement,r={sourceId:e,path:n,propName:t.binding.propName,bindingType:t.binding.bindingType,bindingRef:new WeakRef(t.binding),stateElementRef:"object"==typeof o&&null!==o?new WeakRef(o):null};return s.add(r),this._wiringEntryByBinding.set(t.binding,r),this._everAttachedKeys.add(d(n,t.binding.propName)),this._notify("wiring"),void this._notify("coverage")}case"state:binding-removed":{const e=this._wiringEntryByBinding.get(t.binding);if(void 0===e)return;this._wiringEntryByBinding.delete(t.binding);const n=e.path,i=this._wiringByPathKey.get(n);return void 0!==i&&(i.delete(e),0===i.size&&this._wiringByPathKey.delete(n)),void this._notify("wiring")}case"state:binding-cleared":{const e=t.absoluteAddress.absolutePathInfo.pathInfo.path;return void(this._wiringByPathKey.delete(e)&&this._notify("wiring"))}case"state:token-emit":{const n=this._statFor(this._tokenEmitStats,c(t.kind,t.tokenName),t.stateElement);return n.count+=1,0===t.subscriberCount&&(n.zeroSubscriberCount+=1),this._notify("coverage"),void this._appendTimeline({sourceId:e,kind:t.kind,label:t.tokenName,detail:r(t.args),subscriberCount:t.subscriberCount})}case"state:watch-fired":return this._statFor(this._watchFiredStats,t.path,t.stateElement).count+=1,void this._notify("coverage");case"state:watch-error":return void this._appendTimeline({sourceId:e,kind:"watch-error",label:t.path,detail:`${t.phase}: ${o(t.error)}`,subscriberCount:null});case"state:watch-chain-limit":return void this._appendTimeline({sourceId:e,kind:"watch-chain-limit",label:`depth > ${t.maxDepth}`,detail:t.paths.join(", "),subscriberCount:null});case"state:render-chain-limit":return void this._appendTimeline({sourceId:e,kind:"render-chain-limit",label:`depth > ${t.maxDepth}`,detail:t.paths.join(", "),subscriberCount:null});case"state:path-unresolved":return void this._appendTimeline({sourceId:e,kind:"path-unresolved",label:t.path,detail:`${t.source}: "${t.missingSegment}" is not declared`,subscriberCount:null});case"state:binding-apply-error":return void this._appendTimeline({sourceId:e,kind:"binding-apply-error",label:t.path,detail:`${t.bindingType}: ${o(t.error)}`,subscriberCount:null});case"propagation:suppressed":return void this._appendTimeline({sourceId:e,kind:"propagation-suppressed",label:t.member,detail:`${t.reason} (tx ${t.transactionId}, edge ${t.edgeId})`,subscriberCount:null});case"propagation:coalesced":return void this._appendTimeline({sourceId:e,kind:"propagation-coalesced",label:this._labelOf(t.absoluteAddress),detail:`tx ${t.droppedTransactionId} dropped (winner tx ${t.winnerTransactionId})`,subscriberCount:null});case"propagation:hop-limit":return void this._appendTimeline({sourceId:e,kind:"propagation-hop-limit",label:this._labelOf(t.absoluteAddress),detail:`hop ${t.hop} (tx ${t.transactionId})`,subscriberCount:null});case"contract:drift":{const n=void 0!==t.member?`: ${t.member}`:"",i="event-mismatch"===t.reason?` (sidecar ${t.sidecarEvent??"?"} / live ${t.liveEvent??"?"})`:"";return void this._appendTimeline({sourceId:e,kind:"contract-drift",label:t.tag,detail:`${t.reason}${n}${i}`,subscriberCount:null})}case"contract:manifest-read":case"contract:unsupported-extension":return}}}const g=["wcs-text","wcs-for","wcs-if","wcs-elseif","wcs-else"];function f(e,t,n){const i=t.trim();if(0===i.length)return null;const s=i.indexOf(":");if(s<0)return null;const o=i.slice(0,s).trim(),r=i.slice(s+1).trim();if(0===o.length||0===r.length)return null;const[a,...l]=r.split("|").map(e=>e.trim());return{element:e,propName:o,path:a,filters:l.filter(e=>e.length>0),origin:n,raw:i}}class _ extends HTMLElement{static get observedAttributes(){return["open","dock","hotkey"]}_core=null;_removeCoreListener=null;_panel=null;_badge=null;_stateSelect=null;_paneElements={};_wiringView="wiring";_highlightLayer=null;_dirtyPanes=new Set;_renderScheduled=!1;_selectedRosterKey=null;_selectedPath=null;_pickedNode=null;_pickMode=!1;_expanded=new Set;_hotkeyHandler=null;_pickHandler=null;_editInput=null;_statePointerDown=!1;get core(){return this._core}connectedCallback(){if(document.documentElement.hasAttribute("data-wcs-server"))return;null===this.shadowRoot&&this._buildShadow();const e=Number(this.getAttribute("buffer")??"");this._core=new m({timelineCapacity:Number.isFinite(e)&&e>0?e:void 0}),this._removeCoreListener=this._core.onChange(e=>{"roster"===e||"sources"===e?(this._markDirty("state"),this._markDirty("wiring")):"wiring"===e?this._markDirty("wiring"):"coverage"===e?"coverage"===this._wiringView&&this._markDirty("wiring"):"values"===e?this.open&&this._markDirty("state"):this._markDirty("timeline")}),this._core.connect(),this._applyDock(),this._applyOpen(),this._installHotkey(),this._markDirty("state"),this._markDirty("wiring"),this._markDirty("timeline")}disconnectedCallback(){this._removeCoreListener?.(),this._removeCoreListener=null,this._core?.disconnect(),this._core=null,this._uninstallHotkey(),this._exitPickMode()}attributeChangedCallback(e){null!==this.shadowRoot&&("open"===e?this._applyOpen():"dock"===e?this._applyDock():"hotkey"===e&&(this._uninstallHotkey(),this._installHotkey()))}__flushRenderForTest(){this._renderDirty()}_buildShadow(){const e=this.attachShadow({mode:"open"}),t=document.createElement("style");t.textContent='\n:host {\n all: initial;\n position: fixed;\n z-index: 2147483647;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n inset: auto 0 0 auto;\n}\n* { box-sizing: border-box; }\n.badge {\n position: fixed;\n right: 12px;\n bottom: 12px;\n width: 40px;\n height: 40px;\n border-radius: 20px;\n border: 1px solid #2b4f78;\n background: #10263f;\n color: #9fd0ff;\n font-weight: 700;\n cursor: pointer;\n}\n.panel {\n position: fixed;\n display: flex;\n flex-direction: column;\n background: #0d1b2a;\n color: #d7e3f4;\n border: 1px solid #2b4f78;\n box-shadow: 0 0 24px rgba(0,0,0,.5);\n}\n.panel.dock-bottom { left: 0; right: 0; bottom: 0; height: 45vh; }\n.panel.dock-right { top: 0; right: 0; bottom: 0; width: 420px; }\n.panel[hidden] { display: none; }\nheader {\n display: flex;\n gap: 6px;\n align-items: center;\n padding: 6px 8px;\n border-bottom: 1px solid #2b4f78;\n background: #10263f;\n}\n/* The header never grows wider than the panel: the title and the middle group shrink (the\n group scrolls sideways when even that is not enough), so dock and × always stay in view —\n the right dock is only 420px wide. */\nheader .title {\n flex: 0 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 700;\n color: #9fd0ff;\n margin-right: 4px;\n}\nheader .tools {\n flex: 1 1 auto;\n min-width: 0;\n display: flex;\n gap: 6px;\n align-items: center;\n overflow-x: auto;\n}\nheader .tools select { flex: 0 1 auto; min-width: 6em; text-overflow: ellipsis; }\nheader .tools button, header > button { flex: none; white-space: nowrap; }\nheader select, header button {\n font: inherit;\n background: #16324f;\n color: #d7e3f4;\n border: 1px solid #2b4f78;\n border-radius: 4px;\n padding: 2px 8px;\n cursor: pointer;\n}\nheader button[aria-pressed="true"] { background: #2b5d8f; }\n.panes { display: flex; flex: 1; min-height: 0; }\n.pane { flex: 1; min-width: 0; overflow: auto; padding: 6px 8px; border-right: 1px solid #1d3a5c; }\n.pane:last-child { border-right: none; }\n.pane h3 {\n margin: 0 0 6px;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .08em;\n color: #7ba7d4;\n}\n.tree-row { display: flex; gap: 6px; padding: 1px 0; align-items: baseline; white-space: nowrap; }\n.tree-row .toggle { width: 14px; cursor: pointer; color: #7ba7d4; user-select: none; }\n.tree-row .key { color: #9fd0ff; cursor: pointer; }\n.tree-row .value { color: #ffd9a0; overflow: hidden; text-overflow: ellipsis; }\n.tree-row .value.editable { cursor: pointer; }\n.tree-row input { font: inherit; background: #16324f; color: #ffd9a0; border: 1px solid #2b4f78; }\n.badge-tag {\n display: inline-block;\n padding: 0 4px;\n border-radius: 3px;\n font-size: 10px;\n background: #26456a;\n color: #a8c6e8;\n}\n.badge-tag.warn { background: #6a3326; color: #ffb3a0; }\n.badge-tag.declared { background: #4a4426; color: #efe3a0; }\n.wiring-row, .timeline-row { padding: 1px 0; white-space: nowrap; }\n.wiring-row .prop { color: #b7f0c0; }\n.wiring-row .path { color: #9fd0ff; }\n.timeline-row .t { color: #6f88a3; }\n.timeline-row .label { color: #9fd0ff; }\n.timeline-row .detail { color: #ffd9a0; }\n.timeline-row .kind { display: inline-block; min-width: 52px; text-align: center; }\n.empty { color: #6f88a3; font-style: italic; padding: 4px 0; }\n.notice { color: #efe3a0; padding: 2px 0 6px; }\n.notice button { font: inherit; margin-left: 6px; cursor: pointer; }\n.wiring-row .detail { color: #6f88a3; }\n.overlays-heading { margin-top: 10px; }\n.overlay-row { padding: 1px 0; white-space: nowrap; }\n.overlay-row .prop { color: #b7f0c0; }\n.overlay-row .path { color: #9fd0ff; }\n.overlay-row .detail { color: #6f88a3; }\n.keyed-row { padding: 1px 0; white-space: nowrap; }\n.keyed-row .path { color: #9fd0ff; }\n.keyed-row .detail { color: #6f88a3; }\n.wiring-tabs { padding: 0 0 4px; }\n.wiring-tabs button { font: inherit; cursor: pointer; margin-right: 4px; opacity: 0.6; }\n.wiring-tabs button.active { opacity: 1; font-weight: bold; }\n.hl-box {\n position: fixed;\n pointer-events: none;\n border: 1px solid #58c2ff;\n background: rgba(88,194,255,.18);\n}\n',e.append(t);const n=document.createElement("button");n.className="badge",n.title="wcstack DevTools",n.textContent="WCS",n.addEventListener("click",()=>this.toggle()),e.append(n),this._badge=n;const i=document.createElement("div");i.className="panel dock-bottom",i.hidden=!0;const s=document.createElement("header"),o=document.createElement("span");o.className="title",o.textContent="wcstack devtools",s.append(o);const r=document.createElement("div");r.className="tools",s.append(r);const a=document.createElement("select");a.title="state element",a.addEventListener("change",()=>{this._selectedRosterKey=a.value||null,this._selectedPath=null,this._expanded.clear(),this._markDirty("state"),this._markDirty("wiring")}),r.append(a),this._stateSelect=a;const l=this._headerButton(r,"⌖ pick","pick a page element");l.addEventListener("click",()=>{this._pickMode?this._exitPickMode():this._enterPickMode(),l.setAttribute("aria-pressed",String(this._pickMode))}),l.dataset.role="pick";const c=this._headerButton(r,"⏸","pause timeline");c.addEventListener("click",()=>{const e=this._core;null!==e&&(e.paused=!e.paused,c.setAttribute("aria-pressed",String(e.paused)))}),c.dataset.role="pause";const d=this._headerButton(r,"🗑","clear timeline");d.addEventListener("click",()=>{this._core?.clearTimeline()}),d.dataset.role="clear";const h=this._headerButton(s,"dock","toggle dock position");h.addEventListener("click",()=>{const e="bottom"===(this.getAttribute("dock")??"bottom")?"right":"bottom";this.setAttribute("dock",e)}),h.dataset.role="dock";const u=this._headerButton(s,"×","close");u.addEventListener("click",()=>this.toggle(!1)),u.dataset.role="close",i.append(s);const p=document.createElement("div");p.className="panes";for(const[e,t]of[["state","State"],["wiring","Wiring"],["timeline","Timeline"]]){const n=document.createElement("section");n.className=`pane pane-${e}`;const i=document.createElement("h3");i.textContent=t;const s=document.createElement("div");s.className="pane-body",n.append(i,s),p.append(n),this._paneElements[e]=s}this._paneElements.state.parentElement.addEventListener("pointerdown",()=>{this._holdStateWhilePressed()}),i.append(p),e.append(i),this._panel=i;const m=document.createElement("div");m.className="hl-layer",e.append(m),this._highlightLayer=m}_headerButton(e,t,n){const i=document.createElement("button");return i.textContent=t,i.title=n,i.setAttribute("aria-pressed","false"),e.append(i),i}get open(){return this.hasAttribute("open")}toggle(e){e??!this.open?this.setAttribute("open",""):this.removeAttribute("open")}_applyOpen(){this._panel.hidden=!this.open,this._badge.hidden=this.open,this.open&&(this._markDirty("state"),this._markDirty("wiring"),this._markDirty("timeline"))}_applyDock(){const e="right"===this.getAttribute("dock")?"right":"bottom";this._panel.classList.toggle("dock-right","right"===e),this._panel.classList.toggle("dock-bottom","bottom"===e)}_installHotkey(){const e=this.getAttribute("hotkey")??"Alt+Shift+D";if("none"===e)return;const t=e.split("+").map(e=>e.trim().toLowerCase()),n=t[t.length-1],i=t.includes("alt"),s=t.includes("shift"),o=t.includes("ctrl"),r=t.includes("meta");this._hotkeyHandler=e=>{e.key.toLowerCase()===n&&e.altKey===i&&e.shiftKey===s&&e.ctrlKey===o&&e.metaKey===r&&(e.preventDefault(),this.toggle())},window.addEventListener("keydown",this._hotkeyHandler)}_uninstallHotkey(){null!==this._hotkeyHandler&&(window.removeEventListener("keydown",this._hotkeyHandler),this._hotkeyHandler=null)}_enterPickMode(){this._pickMode=!0,this._pickHandler=e=>{const t=e.target;t.getRootNode()!==this.shadowRoot&&t!==this&&(e.preventDefault(),e.stopPropagation(),this._pickedNode=t,this._selectedPath=null,this._exitPickMode(),this._markDirty("wiring"))},document.addEventListener("click",this._pickHandler,{capture:!0})}_exitPickMode(){null!==this._pickHandler&&(document.removeEventListener("click",this._pickHandler,{capture:!0}),this._pickHandler=null),this._pickMode=!1;const e=this.shadowRoot?.querySelector('button[data-role="pick"]');e?.setAttribute("aria-pressed","false")}_markDirty(e){this._dirtyPanes.add(e),this._renderScheduled||(this._renderScheduled=!0,requestAnimationFrame(()=>{this._renderDirty()}))}_renderDirty(){if(this._renderScheduled=!1,null===this._core||null===this.shadowRoot)return void this._dirtyPanes.clear();const e=this._dirtyPanes;this._dirtyPanes=new Set,e.has("state")&&(this._stateHeld()?this._dirtyPanes.add("state"):this._renderStatePane()),e.has("wiring")&&this._renderWiringPane(),e.has("timeline")&&this._renderTimelinePane()}_stateHeld(){return!!this._statePointerDown||null!==this._editInput&&this.shadowRoot.activeElement===this._editInput}_releaseState(){this._dirtyPanes.has("state")&&this._markDirty("state")}_holdStateWhilePressed(){this._statePointerDown=!0;const e=()=>{window.removeEventListener("pointerup",e,!0),window.removeEventListener("pointercancel",e,!0),this._statePointerDown=!1,this._releaseState()};window.addEventListener("pointerup",e,!0),window.addEventListener("pointercancel",e,!0)}_rosterKey(e){return`${e.sourceId}:${e.label}`}_selectedRoster(){const e=this._core.getRoster();if(0===e.length)return null;return e.find(e=>this._rosterKey(e)===this._selectedRosterKey)??e[0]}_renderStatePane(){this._editInput=null;const e=this._paneElements.state,t=e.parentElement,n=t.scrollTop;this._renderStateBody(e),t.scrollTop=n}_renderStateBody(e){const t=this._core,n=this._stateSelect,i=t.getRoster(),s=this._selectedRoster();if(n.replaceChildren(...i.map(e=>{const t=document.createElement("option");return t.value=this._rosterKey(e),t.textContent=`${e.label} (${e.sourceId.slice(0,12)})`,t.selected=null!==s&&this._rosterKey(e)===this._rosterKey(s),t})),e.replaceChildren(),null===s)return void e.append(this._emptyRow("no <wcs-state> elements observed"));this._selectedRosterKey=this._rosterKey(s);const o=t.keysOf(s);if(0===o.length)return e.append(this._emptyRow("no readable keys (runtime without keys() API?)")),this._renderOverlaysSection(e,s),void this._renderKeyedSection(e,s);for(const t of o)this._renderTreeNode(e,s,{path:t,indexes:[]},t,0);this._renderOverlaysSection(e,s),this._renderKeyedSection(e,s)}_renderKeyedSection(e,t){const n=this._core.keyedSubscriptionsOf(t);if(null===n||0===n.length)return;const i=document.createElement("h3");i.className="overlays-heading",i.textContent=`Keyed selection (${n.length} path${1===n.length?"":"s"})`,e.append(i);for(const t of n)e.append(this._keyedRow(t))}_keyedRow(e){const t=document.createElement("div");t.className="keyed-row";const n=document.createElement("span");if(n.className="path",n.textContent=e.path,t.append(n),e.tracked){const e=document.createElement("span");e.className="badge-tag warn",e.textContent="tracked",e.title="the path is a getter or under one, so $eq cannot subscribe by key: every getter that reads it re-evaluates when it changes",t.append(document.createTextNode(" "),e)}const i=[];e.rows>0&&i.push(`rows ${e.rows} · keys ${e.keys}`),e.lists>0&&i.push(`list watchers ${e.lists}`),e.tracked||i.push(`last ${s(e.lastValue,1)}`);const o=document.createElement("span");return o.className="detail",o.textContent=i.length>0?` ${i.join(" · ")}`:"",t.append(o),t}_renderOverlaysSection(e,t){const n=this._core.overlaysOf(t);if(null===n||0===n.length)return;const i=document.createElement("h3");i.className="overlays-heading",i.textContent=`Overlays (${n.length} mount${1===n.length?"":"s"})`,e.append(i);for(const t of n)e.append(this._overlayRow(t))}_overlayRow(e){const t=document.createElement("div");t.className="overlay-row";const n=document.createElement("span");n.className="badge-tag",n.textContent=e.marker;const i=document.createElement("span");i.className="prop",i.textContent=`<${e.componentTag}>`;const s=document.createElement("span");s.className="path",s.textContent=e.stateProp,t.append(n,document.createTextNode(" "),i,document.createTextNode(" "),s);const o=[];for(const t of e.mountTable)o.push(`${""===t.inner?"(root)":t.inner} → ${t.outer}`);e.delta>0&&o.push(`Δ${e.delta}`),e.privateKeys.length>0&&o.push(`private: ${e.privateKeys.join(", ")}`);const r=e.exports??[];if(r.length>0&&o.push(`exports: ${r.join(", ")}`),e.getterKeys.length>0&&o.push(`getters: ${e.getterKeys.join(", ")}`),o.length>0){const e=document.createElement("span");e.className="detail",e.textContent=` ${o.join(" · ")}`,t.append(e)}return t}_renderTreeNode(e,t,n,i,o){const r=this._core;let a,l=!0;try{a=r.readValue(t,n.path,n.indexes)}catch{l=!1}const c=document.createElement("div");c.className="tree-row",c.style.paddingLeft=14*o+"px";const d=l&&function(e){if(Array.isArray(e))return!0;if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t}(a),h=function(e){return`${e.path}#${e.indexes.join(",")}`}(n),u=d&&this._expanded.has(h),p=document.createElement("span");p.className="toggle",p.textContent=d?u?"▾":"▸":"",d&&p.addEventListener("click",()=>{this._expanded.has(h)?this._expanded.delete(h):this._expanded.add(h),this._markDirty("state")}),c.append(p);const m=document.createElement("span");m.className="key",m.textContent=`${i}:`,m.title=n.path,m.addEventListener("click",()=>{this._selectedPath=n.path,this._pickedNode=null,this._markDirty("wiring"),this._highlightPath(t,n.path)}),c.append(m);const g=document.createElement("span");if(g.className="value",l){g.textContent=s(a,1);(null===a||"object"!=typeof a)&&"function"!=typeof a&&(g.classList.add("editable"),g.title="click to edit",g.addEventListener("click",()=>{this._beginEdit(c,g,t,n,a)}))}else g.textContent="(unreadable getter)";if(c.append(g),e.append(c),u)if(Array.isArray(a)){const i=Math.min(a.length,20);for(let s=0;s<i;s++)this._renderTreeNode(e,t,{path:`${n.path}.*`,indexes:[...n.indexes,s]},`[${s}]`,o+1);if(a.length>i){const t=this._emptyRow(`…(${a.length} items)`);t.style.paddingLeft=14*(o+1)+"px",e.append(t)}}else for(const i of Object.keys(a))this._renderTreeNode(e,t,{path:`${n.path}.${i}`,indexes:n.indexes},i,o+1)}_beginEdit(e,t,n,i,s){const o=document.createElement("input");o.value="string"==typeof s?s:String(s);const r=()=>{this._editInput=null,this._core?.writeValue(n,i.path,function(e){const t=e.trim();try{return JSON.parse(t)}catch{return e}}(o.value),i.indexes),this._markDirty("state")};o.addEventListener("keydown",e=>{"Enter"===e.key?r():"Escape"===e.key&&(this._editInput=null,this._markDirty("state"))}),o.addEventListener("blur",()=>{this._releaseState()}),e.replaceChild(o,t),this._editInput=o,o.focus()}_renderWiringPane(){const e=this._core,t=this._paneElements.wiring;t.replaceChildren();const n=document.createElement("div");n.className="wiring-tabs";for(const[e,t]of[["wiring","wiring"],["coverage","coverage"]]){const i=document.createElement("button");i.textContent=t,this._wiringView===e&&i.classList.add("active"),i.addEventListener("click",()=>{this._wiringView=e,this._markDirty("wiring")}),n.append(i)}if(t.append(n),"coverage"===this._wiringView)return void this._renderCoverageView(t);let i,s;if(null!==this._pickedNode){i=e.getWiringForNode(this._pickedNode);const t=this._pickedNode;s=t instanceof Element?`<${t.tagName.toLowerCase()}>`:t.nodeName}else if(null!==this._selectedPath){const t=this._selectedRoster();i=null!==t?e.getWiringForPath(this._selectedPath,t.summary.element):[],s=this._selectedPath}else i=e.getAllWiring(),s="all";const o=document.createElement("div");if(o.textContent=`context: ${s} — ${i.length} live binding${1===i.length?"":"s"}`,t.append(o),i.length>0){for(const e of i)t.append(this._wiringRow(e));return}const r=e.getCanonicalDeclared(),a=this._selectedRoster(),l=null===r&&null!==a?function(e,t="data-wcs"){const n=[];for(const i of e.querySelectorAll(`[${t}]`)){const e=i.getAttribute(t);for(const t of e.split(";")){const e=f(i,t,"attribute");null!==e&&n.push(e)}}const i=(e.ownerDocument??e).createTreeWalker(e,NodeFilter.SHOW_COMMENT);let s=i.nextNode();for(;null!==s;){const e=s.textContent.trim(),t=g.find(t=>e===t||e.startsWith(t+":"));if(void 0!==t){const i=s.parentNode instanceof Element?s.parentNode:null;if(null!==i){const s=e===t?"":e.slice(t.length+1).trim();if(s.length>0){const e="wcs-text"===t?"textContent":t.slice(4);n.push(f(i,`${e}: ${s}`,"comment"))}}}s=i.nextNode()}return n}(this._scanRootOf(a)):[];if(0===(r?.length??0)&&0===l.length)return void t.append(this._emptyRow("no bindings observed"));const c=document.createElement("div");c.className="notice";const d=document.createElement("span");d.className="badge-tag declared",d.textContent=null!==r?"declared (canonical)":"declared",c.append(d,document.createTextNode(" attached late — reload to capture live bindings "));const h=document.createElement("button");if(h.textContent="reload",h.addEventListener("click",()=>{location.reload()}),c.append(h),t.append(c),null!==r)for(const e of r)t.append(this._canonicalDeclaredRow(e));else for(const e of l)t.append(this._declaredRow(e))}_renderCoverageView(e){const t=this._core,n=t.observingSince,i=document.createElement("div"),s=null===n?null:new Date(Date.now()-(performance.now()-n)).toLocaleTimeString();i.textContent=null===s?"not observing":`observing since ${s} (declared vs measured after this point)`,e.append(i);const o=this._selectedRoster(),r=t.getCoverageReport(o?.summary.element);if(0!==r.length)for(const t of r){const n=document.createElement("div");n.className="wiring-row";const i=document.createElement("span");i.className="badge-tag",i.textContent=t.kind;const s=document.createElement("span");s.className="badge-tag",s.textContent=t.count>1?`${t.status} ×${t.count}`:t.status,"never"===t.status||"never-attached"===t.status||"emitted-unheard"===t.status?s.classList.add("warn"):"prerequisite-missing"===t.status&&s.classList.add("declared");const o=document.createElement("span");if(o.className="path",o.textContent=` ${t.name} `,n.append(i,document.createTextNode(" "),o,s),null!==t.note){const e=document.createElement("span");e.className="detail",e.textContent=` ${t.note}`,n.append(e)}e.append(n)}else e.append(this._emptyRow("nothing declared to cover"))}_canonicalDeclaredRow(e){const t=document.createElement("div");t.className="wiring-row";const n=document.createElement("span");n.className="badge-tag declared",n.textContent=e.origin;const i=document.createElement("span");i.className="prop",i.textContent=e.propName;const s=document.createElement("span");s.className="path";const o=e.outFilters.map(e=>` | ${e.filterName}(${e.args.join(",")})`).join("");if(s.textContent=`${e.statePathName}${o}`,t.append(n,document.createTextNode(" "),i,document.createTextNode(" ← "),s),null!==e.node){const n=e.node;t.addEventListener("click",()=>{this._highlightNodes([n])})}return t}_scanRootOf(e){const t=e.rootNode;return t.nodeType===Node.DOCUMENT_NODE||t.nodeType===Node.DOCUMENT_FRAGMENT_NODE?t:document}_wiringRow(e){const t=document.createElement("div");t.className="wiring-row";const n=document.createElement("span");n.className="prop",n.textContent=e.propName;const i=document.createTextNode(" ← "),s=document.createElement("span");s.className="path",s.textContent=e.path;const o=document.createElement("span");return o.className="badge-tag",o.textContent=e.bindingType,t.append(o,document.createTextNode(" "),n,i,s),t.addEventListener("click",()=>{const t=e.bindingRef.deref();void 0!==t&&this._highlightNodes([t.node,t.replaceNode])}),t}_declaredRow(e){const t=document.createElement("div");t.className="wiring-row";const n=document.createElement("span");n.className="badge-tag declared",n.textContent=e.origin;const i=document.createElement("span");i.className="prop",i.textContent=e.propName;const s=document.createElement("span");return s.className="path",s.textContent=e.path,t.append(n,document.createTextNode(" "),i,document.createTextNode(" ← "),s),t.addEventListener("click",()=>{this._highlightNodes([e.element])}),t}_renderTimelinePane(){const e=this._core,t=this._paneElements.timeline,n=e.getTimeline(),i=Math.max(0,n.length-200),s=[];i>0&&s.push(this._emptyRow(`…(${i} earlier entries)`));for(let e=i;e<n.length;e++)s.push(this._timelineRow(n[e]));0===s.length&&s.push(this._emptyRow("no activity yet")),t.replaceChildren(...s),t.scrollTop=t.scrollHeight}_timelineRow(e){const t=document.createElement("div");t.className="timeline-row";const n=document.createElement("span");n.className="t",n.textContent=`${(e.time/1e3).toFixed(3)}s `;const i=document.createElement("span");i.className="badge-tag kind",i.textContent=e.kind,0===e.subscriberCount&&(i.classList.add("warn"),i.title="emitted with no subscribers"),"watch-error"!==e.kind&&"watch-chain-limit"!==e.kind||(i.classList.add("warn"),i.title="watch-error"===e.kind?"a $watch or $scan failure (read, handler, fold or write — see the phase in the detail); the runtime isolated it (console.error only)":"a $watch write chain hit the depth limit and was cut off"),"render-chain-limit"===e.kind&&(i.classList.add("warn"),i.title="writes made while rendering kept starting new batches; the chain hit the depth limit and this batch was not rendered"),"path-unresolved"!==e.kind&&"binding-apply-error"!==e.kind||(i.classList.add("warn"),i.title="path-unresolved"===e.kind?"a wired path does not resolve on the state; its updates are dropped":"a binding threw while applying; the runtime isolated it (console.error only)"),"propagation-hop-limit"!==e.kind&&"contract-drift"!==e.kind||(i.classList.add("warn"),i.title="propagation-hop-limit"===e.kind?"a two-way propagation chain hit the hop limit and was cut off":"sidecar manifest drifted from the live wcBindable declaration (live wins)");const s=document.createElement("span");s.className="label",s.textContent=` ${e.label} `;const o=document.createElement("span");return o.className="detail",o.textContent=e.detail,t.append(n,i,s,o),t}_emptyRow(e){const t=document.createElement("div");return t.className="empty",t.textContent=e,t}_highlightPath(e,t){const n=this._core,i=[];for(const s of n.getWiringForPath(t,e.summary.element)){const e=s.bindingRef.deref();void 0!==e&&i.push(e.node,e.replaceNode)}this._highlightNodes(i)}_highlightNodes(e){const t=this._highlightLayer;t.replaceChildren();const n=new Set;for(const i of e){const e=i instanceof Element?i:i.parentElement;if(null===e||n.has(e)||!e.isConnected)continue;n.add(e);const s=e.getBoundingClientRect(),o=document.createElement("div");o.className="hl-box",o.style.left=`${s.left}px`,o.style.top=`${s.top}px`,o.style.width=`${s.width}px`,o.style.height=`${s.height}px`,t.append(o)}setTimeout(()=>{t.replaceChildren()},2e3)}}const w="wcs-devtools";function b(){null===document.querySelector(w)&&document.body.appendChild(document.createElement(w))}document.documentElement.hasAttribute("data-wcs-server")||(customElements.get(w)||customElements.define(w,_),null!==document.body?b():document.addEventListener("DOMContentLoaded",b,{once:!0}));
|
|
2
2
|
//# sourceMappingURL=auto.min.js.map
|