@wcstack/state 4.0.0-rc.4 → 4.0.0-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.ja.md +68 -9
  2. package/README.md +69 -10
  3. package/dist/auto.min.js +1 -1
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.esm.js +1 -1
  6. package/dist/manifest.d.ts +2 -0
  7. package/dist/manifest.esm.js +15 -2
  8. package/dist/parser.esm.js +3 -1
  9. package/dist/split/auto.js +1 -1
  10. package/dist/split/chunks/{chunk-SEPY6OHJ.js → chunk-5MLEMVL3.js} +1 -1
  11. package/dist/split/chunks/{chunk-5LIOXC7W.js → chunk-ACYOJ3NW.js} +1 -1
  12. package/dist/split/chunks/{chunk-HSOCDTWX.js → chunk-C3UYMJRT.js} +1 -1
  13. package/dist/split/chunks/{chunk-372Y2FIC.js → chunk-COJU24UI.js} +1 -1
  14. package/dist/split/chunks/{chunk-7644KQ3T.js → chunk-FVHQWDF4.js} +1 -1
  15. package/dist/split/chunks/{chunk-ROX5KEJX.js → chunk-FZJY4CF3.js} +1 -1
  16. package/dist/split/chunks/chunk-HSMALJSW.js +1 -0
  17. package/dist/split/chunks/{chunk-6GES2BK3.js → chunk-L2BZD5DU.js} +1 -1
  18. package/dist/split/chunks/chunk-VY2XLPFM.js +1 -0
  19. package/dist/split/core.js +1 -1
  20. package/dist/split/features/devtools.js +1 -1
  21. package/dist/split/features/diagnostics.js +1 -1
  22. package/dist/split/features/formats.js +1 -1
  23. package/dist/split/features/list-keys.js +1 -1
  24. package/dist/split/features/native-commands.d.ts +31 -0
  25. package/dist/split/features/native-commands.js +1 -0
  26. package/dist/split/features/recursion.js +1 -1
  27. package/dist/split/features/scopes.js +1 -1
  28. package/dist/split/features/ssr.js +1 -1
  29. package/dist/split/features/temporal.js +1 -1
  30. package/dist/wcs-manifest.json +56 -2
  31. package/package.json +1 -1
  32. package/dist/split/chunks/chunk-Z7B3ZZE4.js +0 -1
  33. package/dist/split/chunks/chunk-ZOINIOIM.js +0 -1
  34. /package/dist/split/chunks/{chunk-G2ZO6SXV.js → chunk-SA2LYPN7.js} +0 -0
package/README.ja.md CHANGED
@@ -191,9 +191,9 @@
191
191
  <script type="importmap">
192
192
  {
193
193
  "imports": {
194
- "@wcstack/state/core": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.4/dist/split/core.js",
195
- "@wcstack/state/features/temporal": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.4/dist/split/features/temporal.js",
196
- "@wcstack/state/features/scopes": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.4/dist/split/features/scopes.js"
194
+ "@wcstack/state/core": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.5/dist/split/core.js",
195
+ "@wcstack/state/features/temporal": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.5/dist/split/features/temporal.js",
196
+ "@wcstack/state/features/scopes": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.5/dist/split/features/scopes.js"
197
197
  }
198
198
  }
199
199
  </script>
@@ -229,6 +229,7 @@
229
229
  | `@wcstack/state/core` | バインディングの本体: `data-wcs`・`{{ }}` とコメントバインディング・`for` / `if`・パス getter・イベント・`$command` / `$on`・条件の 10 個のフィルタ(`eq` `ne` `not` `lt` `le` `gt` `ge` `truthy` `falsy` `boolean`)・`bootstrapState`・`installFeatures` |
230
230
  | `@wcstack/state/features/temporal` | `$watch`・`$stream` |
231
231
  | `@wcstack/state/features/list-keys` | `$listKeys` |
232
+ | `@wcstack/state/features/native-commands` | ネイティブ要素の `command.<method>:`(`<dialog>` の `showModal()`・`<input>` の `focus()`・`<video>` の `play()` など)—— [ネイティブ要素のコマンド](#ネイティブ要素のコマンド) |
232
233
  | `@wcstack/state/features/scopes` | `bind-component`・`mount=` のボリューム・オーバーレイの公開 getter・DCC(`data-wc-definition`) |
233
234
  | `@wcstack/state/features/recursion` | `$recursion` と `**` パス |
234
235
  | `@wcstack/state/features/ssr` | `enable-ssr`: サーバー描画とハイドレーション |
@@ -252,7 +253,7 @@
252
253
  ページが名指したアドオンを、同じビルドから読み込みます。
253
254
 
254
255
  ```html
255
- <script type="module" src="https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.4/dist/split/auto.js"></script>
256
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.5/dist/split/auto.js"></script>
256
257
  <wcs-state features="scopes diagnostics">
257
258
  <script type="module">
258
259
  export default {
@@ -274,7 +275,7 @@
274
275
  | 何のためか | どの `<wcs-state>` が動き出すよりも前に揃っているべきもの — `mount=` / `bind-component` / DCC の要素が何であるかを決める `scopes` — と、ページの開発補助(`diagnostics`・`devtools`) | その state が必要とするアドオン(`$watch` のための `temporal`、フィルタのための `formats`) |
275
276
  | 誰が読むか | 分割版の auto エントリだけ | すべてのエントリ: 分割版の auto エントリは足りないアドオンを読み込み、それ以外のエントリは入っているかを確かめ、入っていなければその state を `[wcs/feature-not-installed]` で失敗させる |
276
277
 
277
- 名前は `formats`・`diagnostics`・`temporal`・`list-keys`・`scopes`・`recursion`・`ssr`・`devtools` です。
278
+ 名前は `formats`・`diagnostics`・`temporal`・`list-keys`・`scopes`・`recursion`・`ssr`・`devtools`・`native-commands` です。
278
279
  `features=` にそれ以外の名前があると、分割版の auto エントリは `<wcs-state>` を定義する前に
279
280
  `[wcs/feature-unknown]` で失敗します。`$features` にあれば、その state が失敗します(分割版の auto
280
281
  エントリでは `[wcs/feature-unknown]`、それ以外では `[wcs/feature-not-installed]`)。配列でない
@@ -2104,7 +2105,7 @@ export default {
2104
2105
  | 部位 | 説明 |
2105
2106
  |---|---|
2106
2107
  | `command.` | 固定の prefix |
2107
- | `<methodName>` | 起動する要素のメソッド。名前は `static wcBindable.commands` に `{ name: "<methodName>" }` として現れること |
2108
+ | `<methodName>` | 起動する要素のメソッド。名前は `static wcBindable.commands` に `{ name: "<methodName>" }` として現れること —— ネイティブ要素では [ネイティブ要素のコマンド](#ネイティブ要素のコマンド) の表にあること |
2108
2109
  | `$command.<tokenName>` | `CommandToken` に解決される明示的な名前空間パス。`<tokenName>` は `$commandTokens` で宣言された名前であること |
2109
2110
 
2110
2111
  右辺は `$command.<tokenName>` と書く必要があります —— ベア名の省略形(`fetchUsers`)は非対応です。`$command.` 名前空間を経由することでバインディングの意図が HTML 上で明示され、トップレベルの state 名前空間を token 名で汚さずに済みます。
@@ -2130,11 +2131,11 @@ class MyFetcher extends HTMLElement {
2130
2131
 
2131
2132
  検証ルール(バインディング時に強制):
2132
2133
 
2133
- - 要素は `protocol: "wc-bindable"` かつ整数 `version` が `1` 以上(現行プロトコルは `1`。1 以上のすべてのバージョンが core 互換)の `static wcBindable` を公開するカスタム要素であること
2134
- - `methodName` は `wcBindable.commands` に(`name` で)現れること
2134
+ - 要素は `protocol: "wc-bindable"` かつ整数 `version` が `1` 以上(現行プロトコルは `1`。1 以上のすべてのバージョンが core 互換)の `static wcBindable` を公開するカスタム要素であること —— または、`native-commands` アドオンがあればネイティブ要素([ネイティブ要素のコマンド](#ネイティブ要素のコマンド))
2135
+ - `methodName` は `wcBindable.commands` に(`name` で)現れること(ネイティブ要素では [ネイティブ要素のコマンド](#ネイティブ要素のコマンド) の表にあること)
2135
2136
  - `<tokenName>` は `$commandTokens` で宣言されていること(`$command.typo` は `[wcs/token-undeclared]` を throw する)
2136
2137
 
2137
- カスタム要素はクラスが定義された時点で検査され、ネイティブ要素はその場で拒否されます(`[wcs/token-misconfigured]`)。ページのバインド中に見つかった違反は `<wcs-state>` の初期化を失敗させます。`for:` / `if:` の行の中で見つかった違反や、ページのバインド後にクラスが定義されて見つかった違反は、そのバインディング 1 本の失敗として `$errorCallback` に届き、行はそのまま構築されます([初期化の失敗](#初期化の失敗))。
2138
+ カスタム要素はクラスが定義された時点で検査され、ネイティブ要素はその場で検査されます —— アドオンが入っていれば(`@wcstack/state` と `/auto` には含まれています)[native-commands](#ネイティブ要素のコマンド) の表と照合し、入っていなければ拒否します(`[wcs/token-misconfigured]` `#1202`)。ページのバインド中に見つかった違反は `<wcs-state>` の初期化を失敗させます。`for:` / `if:` の行の中で見つかった違反や、ページのバインド後にクラスが定義されて見つかった違反は、そのバインディング 1 本の失敗として `$errorCallback` に届き、行はそのまま構築されます([初期化の失敗](#初期化の失敗))。
2138
2139
 
2139
2140
  ### Token API
2140
2141
 
@@ -2192,6 +2193,64 @@ command token は state コードから emit する必要はありません。DO
2192
2193
  <my-list data-wcs="command.reset: $command.reset"></my-list>
2193
2194
  ```
2194
2195
 
2196
+ ### ネイティブ要素のコマンド
2197
+
2198
+ `native-commands` アドオン(`@wcstack/state` と `/auto` には含まれています。`/core` のページは `@wcstack/state/features/native-commands` を入れます)があると、`command.<method>:` は**ネイティブ**要素のメソッドも購読します。ダイアログを開く・入力欄にフォーカスする・動画を再生するといった操作が「state が emit する token」になり、state のコードは要素に手を伸ばしません:
2199
+
2200
+ ```html
2201
+ <button data-wcs="onclick: $command.openEditor">編集</button>
2202
+ <dialog data-wcs="command.showModal: $command.openEditor; command.close: $command.closeEditor">
2203
+ <input data-wcs="value: title">
2204
+ <button data-wcs="onclick: save">保存</button>
2205
+ </dialog>
2206
+ ```
2207
+
2208
+ ```javascript
2209
+ export default {
2210
+ title: "",
2211
+ $commandTokens: ["openEditor", "closeEditor"],
2212
+ async save() {
2213
+ await saveTitle(this.title);
2214
+ this.$command.closeEditor.emit("saved"); // → dialog.close("saved")
2215
+ },
2216
+ };
2217
+ ```
2218
+
2219
+ ネイティブ要素が呼べるのは次の表のメソッドだけです。それ以外のメソッドは、カスタム要素の宣言に無いコマンドと同じく、バインディングの取り付け時に `[wcs/token-misconfigured]`(`#1205`。その要素が呼べるメソッドを列挙する)を throw します:
2220
+
2221
+ | 要素 | メソッド |
2222
+ |---|---|
2223
+ | すべての要素 | `focus` `blur` `click` `scrollIntoView` `showPopover` `hidePopover` `togglePopover` |
2224
+ | `<dialog>` | `show` `showModal` `close` `requestClose` |
2225
+ | `<form>` | `requestSubmit` `checkValidity` `reportValidity` |
2226
+ | `<input>` | `select` `setSelectionRange` `showPicker` `setCustomValidity` `checkValidity` `reportValidity` |
2227
+ | `<textarea>` | `select` `setSelectionRange` `setCustomValidity` `checkValidity` `reportValidity` |
2228
+ | `<select>` | `showPicker` `setCustomValidity` `checkValidity` `reportValidity` |
2229
+ | `<audio>` `<video>` | `play` `pause` `load` |
2230
+
2231
+ 意図して入れていないもの: HTML・属性・木を書き換えるメソッド(`insertAdjacentHTML`・`setAttribute`・`remove` など —— `html:` / `attr.` で束縛してください)、`input` イベント無しでコントロールの値を変えるため state が変化を知らないもの(`form.reset()`・`stepUp()`・`stepDown()`・`setRangeText()` —— state を書き換えてください)、入力検証と `submit` イベントを飛ばす `form.submit()`(`requestSubmit` はどちらも飛ばしません)、そして状態の出力も持つ `@wcstack/fullscreen`・`@wcstack/pointer-lock`・`@wcstack/picture-in-picture` が扱うフルスクリーン / ポインタロック / ピクチャーインピクチャーの要求です。
2232
+
2233
+ - **引数。** ネイティブのメソッドは `emit` の引数を受け取ります。ただし第 1 引数が `Event` なら引数なしで呼びます。`on…: $command.x` のバインディングは `(event, ...listIndexes)` を emit しますが、これを受け取るネイティブメソッドはありません —— `close(event)` は dialog の `returnValue` を `"[object PointerEvent]"` にし、`requestSubmit(event)` は throw します。state からの `emit("saved")`・`emit({ preventScroll: true })`・`emit({ block: "center" })` はそのまま渡ります。カスタム要素のメソッドは従来どおりすべてを受け取ります。
2234
+ - **決めるのは表で、ブラウザではありません。** 表にあるメソッドはどのブラウザでも(サーバーでも)束縛できます。そのメソッドを持たないブラウザ(古いブラウザの `requestClose` など)では、token が emit されたときに呼び出しが失敗し、throw した購読者と同じく報告されます。
2235
+ - **戻り値と例外**はカスタム要素と同じです: `emit` は `checkValidity()` の真偽値や `play()` の `Promise` を返します。throw したメソッド(非モーダルで開いている dialog への `showModal()`、`popover` の無い要素への `showPopover()`)は `console.error` で報告され、ほかの購読者には届きます。reject した `Promise` は捕捉しません: 自動再生の拒否を扱うには、state から `await Promise.all(this.$command.play.emit())` してください。
2236
+ - **描画は後から走ります。** 書き込みはマイクロタスクで描画されるので、`this.editing = true; this.$command.focusTitle.emit();` の emit は、まだ表示されていない入力欄に —— `if:` の中なら、まだ作られていない入力欄に —— 届きます。描かれてから `$renderedCallback` で emit してください:
2237
+
2238
+ ```html
2239
+ <template data-wcs="if: editing">
2240
+ <input data-wcs="value: title; command.focus: $command.focusTitle">
2241
+ </template>
2242
+ ```
2243
+
2244
+ ```javascript
2245
+ $renderedCallback(paths) {
2246
+ if (paths.includes("editing") && this.editing) this.$command.focusTitle.emit();
2247
+ },
2248
+ ```
2249
+
2250
+ - **ユーザー操作。** `showPicker()` と音声つきの `play()` は直前のユーザー操作を必要とします。`on…: $command.x` はイベントの処理の中でメソッドを呼びますが、`await` の後の `emit` では間に合わないことがあります。
2251
+ - **カスタム要素は変わりません**: コマンドは `wcBindable` で宣言し、宣言の無いものは従来どおり `#1202` を throw します。カスタマイズ済み組み込み要素(`<button is="…">`)はネイティブ要素です。
2252
+ - **ボタンだけで開閉が済むなら**、プラットフォームの invoker commands は state を必要としません: `<button commandfor="dlg" command="show-modal">`。state が決めるとき(fetch の後、検証の後)や、invoker commands に無い操作(`focus`・`play`・`scrollIntoView`)に `command.` を使ってください。
2253
+
2195
2254
  ## Event Token(イベントバインディング)
2196
2255
 
2197
2256
  command token はコンポーネントへ *押し込み* ます(state がメソッドを起動)。**event token** はその正確な双対 —— コンポーネントから *引き出し* ます(要素がイベントを dispatch し、state が受信)。両者で要素 ↔ state 境界の双方向をカバーし、どちらの側も相手への参照を一切持ちません。共有されるのは token のみです。
package/README.md CHANGED
@@ -192,9 +192,9 @@ leaves features out, it can compose them instead:
192
192
  <script type="importmap">
193
193
  {
194
194
  "imports": {
195
- "@wcstack/state/core": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.4/dist/split/core.js",
196
- "@wcstack/state/features/temporal": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.4/dist/split/features/temporal.js",
197
- "@wcstack/state/features/scopes": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.4/dist/split/features/scopes.js"
195
+ "@wcstack/state/core": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.5/dist/split/core.js",
196
+ "@wcstack/state/features/temporal": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.5/dist/split/features/temporal.js",
197
+ "@wcstack/state/features/scopes": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.5/dist/split/features/scopes.js"
198
198
  }
199
199
  }
200
200
  </script>
@@ -230,6 +230,7 @@ The file names under `dist/split/chunks/` carry a content hash: if you list chun
230
230
  | `@wcstack/state/core` | The binding engine: `data-wcs`, `{{ }}` and comment bindings, `for` / `if`, path getters, events, `$command` / `$on`, the 10 condition filters (`eq` `ne` `not` `lt` `le` `gt` `ge` `truthy` `falsy` `boolean`), `bootstrapState`, `installFeatures` |
231
231
  | `@wcstack/state/features/temporal` | `$watch`, `$stream` |
232
232
  | `@wcstack/state/features/list-keys` | `$listKeys` |
233
+ | `@wcstack/state/features/native-commands` | `command.<method>:` on native elements (`<dialog>`.`showModal()`, `<input>`.`focus()`, `<video>`.`play()`, …) — [Native Element Commands](#native-element-commands) |
233
234
  | `@wcstack/state/features/scopes` | `bind-component`, `mount=` volumes, overlay exports, DCC (`data-wc-definition`) |
234
235
  | `@wcstack/state/features/recursion` | `$recursion` and `**` paths |
235
236
  | `@wcstack/state/features/ssr` | `enable-ssr`: server rendering and hydration |
@@ -253,7 +254,7 @@ receptacle the owning add-on fills on install).
253
254
  core, and the add-ons the page names from the same build.
254
255
 
255
256
  ```html
256
- <script type="module" src="https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.4/dist/split/auto.js"></script>
257
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@wcstack/state@4.0.0-rc.5/dist/split/auto.js"></script>
257
258
  <wcs-state features="scopes diagnostics">
258
259
  <script type="module">
259
260
  export default {
@@ -275,8 +276,8 @@ The add-ons are named in two places, for two different reasons:
275
276
  | What it is for | What must be there before any `<wcs-state>` starts — `scopes`, which decides what a `mount=` / `bind-component` / DCC element is — and the page's development aids (`diagnostics`, `devtools`) | The add-ons that state needs (`temporal` for its `$watch`, `formats` for its filters) |
276
277
  | Who reads it | Only the split auto entry | Every entry: the split auto entry loads the missing add-ons; the other entries check that they are installed and fail the state with `[wcs/feature-not-installed]` otherwise |
277
278
 
278
- The names are `formats`, `diagnostics`, `temporal`, `list-keys`, `scopes`, `recursion`, `ssr` and
279
- `devtools`. Any other name in `features=` makes the split auto entry fail with `[wcs/feature-unknown]`
279
+ The names are `formats`, `diagnostics`, `temporal`, `list-keys`, `scopes`, `recursion`, `ssr`,
280
+ `devtools` and `native-commands`. Any other name in `features=` makes the split auto entry fail with `[wcs/feature-unknown]`
280
281
  before it defines `<wcs-state>`; in `$features` it fails that state (`[wcs/feature-unknown]` on the
281
282
  split auto entry, `[wcs/feature-not-installed]` elsewhere). A `$features` that is not an array throws
282
283
  `#46`, and a volume may not declare one.
@@ -2114,7 +2115,7 @@ export default {
2114
2115
  | Part | Description |
2115
2116
  |---|---|
2116
2117
  | `command.` | Fixed prefix |
2117
- | `<methodName>` | The element's method to invoke. The name must appear as `{ name: "<methodName>" }` in `static wcBindable.commands` |
2118
+ | `<methodName>` | The element's method to invoke. The name must appear as `{ name: "<methodName>" }` in `static wcBindable.commands` — on a native element, in the table of [Native Element Commands](#native-element-commands) |
2118
2119
  | `$command.<tokenName>` | Explicit namespace path that resolves to a `CommandToken`. `<tokenName>` must be a name declared in `$commandTokens` |
2119
2120
 
2120
2121
  The right-hand side must be written as `$command.<tokenName>` — the bare-name shorthand (`fetchUsers`) is not supported. Going through the `$command.` namespace makes the binding's intent explicit in the HTML and keeps the top-level state namespace free of token names.
@@ -2140,11 +2141,11 @@ class MyFetcher extends HTMLElement {
2140
2141
 
2141
2142
  Validation rules (enforced at binding time):
2142
2143
 
2143
- - The element must be a custom element exposing `static wcBindable` with `protocol: "wc-bindable"` and an integer `version` of `1` or later (the current protocol version is `1`; all versions ≥ 1 are core-compatible)
2144
- - `methodName` must appear (by `name`) in `wcBindable.commands`
2144
+ - The element must be a custom element exposing `static wcBindable` with `protocol: "wc-bindable"` and an integer `version` of `1` or later (the current protocol version is `1`; all versions ≥ 1 are core-compatible) — or a native element, with the `native-commands` add-on ([Native Element Commands](#native-element-commands))
2145
+ - `methodName` must appear (by `name`) in `wcBindable.commands` (on a native element: in the table of [Native Element Commands](#native-element-commands))
2145
2146
  - `<tokenName>` must be declared in `$commandTokens` (`$command.typo` throws `[wcs/token-undeclared]`)
2146
2147
 
2147
- A custom element is checked once its class is defined; a native element is refused at once (`[wcs/token-misconfigured]`). A violation found while the page is bound fails the `<wcs-state>`'s initialization; one found inside a `for:` / `if:` row, or when a class is defined after the page was bound, fails that one binding, which goes to `$errorCallback`, and the row is still built ([Initialization failures](#initialization-failures)).
2148
+ A custom element is checked once its class is defined; a native element at once — against the [native-commands](#native-element-commands) table where the add-on is installed (`@wcstack/state` and `/auto` include it), and refused otherwise (`[wcs/token-misconfigured]` `#1202`). A violation found while the page is bound fails the `<wcs-state>`'s initialization; one found inside a `for:` / `if:` row, or when a class is defined after the page was bound, fails that one binding, which goes to `$errorCallback`, and the row is still built ([Initialization failures](#initialization-failures)).
2148
2149
 
2149
2150
  ### Token API
2150
2151
 
@@ -2202,6 +2203,64 @@ This is pure wiring: the event endpoint is connected to a command-token endpoint
2202
2203
  <my-list data-wcs="command.reset: $command.reset"></my-list>
2203
2204
  ```
2204
2205
 
2206
+ ### Native Element Commands
2207
+
2208
+ With the `native-commands` add-on (`@wcstack/state` and `/auto` include it; a `/core` page installs `@wcstack/state/features/native-commands`), `command.<method>:` also subscribes a **native** element's method. Opening a dialog, focusing an input or playing a video becomes a token the state emits — the state code never reaches for the element:
2209
+
2210
+ ```html
2211
+ <button data-wcs="onclick: $command.openEditor">Edit</button>
2212
+ <dialog data-wcs="command.showModal: $command.openEditor; command.close: $command.closeEditor">
2213
+ <input data-wcs="value: title">
2214
+ <button data-wcs="onclick: save">Save</button>
2215
+ </dialog>
2216
+ ```
2217
+
2218
+ ```javascript
2219
+ export default {
2220
+ title: "",
2221
+ $commandTokens: ["openEditor", "closeEditor"],
2222
+ async save() {
2223
+ await saveTitle(this.title);
2224
+ this.$command.closeEditor.emit("saved"); // → dialog.close("saved")
2225
+ },
2226
+ };
2227
+ ```
2228
+
2229
+ A native element may run only the methods of this table. Any other method throws `[wcs/token-misconfigured]` (`#1205`, naming the methods that element may run) when the binding is attached — like an undeclared command of a custom element:
2230
+
2231
+ | Element | Methods |
2232
+ |---|---|
2233
+ | any element | `focus` `blur` `click` `scrollIntoView` `showPopover` `hidePopover` `togglePopover` |
2234
+ | `<dialog>` | `show` `showModal` `close` `requestClose` |
2235
+ | `<form>` | `requestSubmit` `checkValidity` `reportValidity` |
2236
+ | `<input>` | `select` `setSelectionRange` `showPicker` `setCustomValidity` `checkValidity` `reportValidity` |
2237
+ | `<textarea>` | `select` `setSelectionRange` `setCustomValidity` `checkValidity` `reportValidity` |
2238
+ | `<select>` | `showPicker` `setCustomValidity` `checkValidity` `reportValidity` |
2239
+ | `<audio>` `<video>` | `play` `pause` `load` |
2240
+
2241
+ Left out on purpose: the methods that write HTML, an attribute or the tree (`insertAdjacentHTML`, `setAttribute`, `remove`, … — bind `html:` / `attr.` instead); those that change a control's value without an `input` event, so the state would not see it (`form.reset()`, `stepUp()`, `stepDown()`, `setRangeText()` — write the state instead); `form.submit()`, which skips validation and the `submit` event (`requestSubmit` does neither); and the fullscreen / pointer-lock / picture-in-picture requests, which `@wcstack/fullscreen`, `@wcstack/pointer-lock` and `@wcstack/picture-in-picture` cover along with their state.
2242
+
2243
+ - **Arguments.** A native method receives the `emit` arguments unless the first one is an `Event`. An `on…: $command.x` binding emits `(event, ...listIndexes)`, which no native method takes — `close(event)` would set the dialog's `returnValue` to `"[object PointerEvent]"`, `requestSubmit(event)` would throw — so it is called with none. From the state, `emit("saved")`, `emit({ preventScroll: true })` or `emit({ block: "center" })` pass through. A custom element's method still receives everything.
2244
+ - **The table decides, not the browser.** A method in the table binds in every browser (and on the server). Where a browser lacks it (`requestClose` in an older one), the call fails when the token is emitted and is reported like any subscriber that throws.
2245
+ - **Return values and errors** follow the custom elements': `emit` returns `checkValidity()`'s boolean or `play()`'s `Promise`. A method that throws (`showModal()` on a dialog already open non-modally, `showPopover()` on an element without `popover`) is reported with `console.error`, and the other subscribers still run. A rejected `Promise` is not caught: `await Promise.all(this.$command.play.emit())` from the state to handle an autoplay refusal.
2246
+ - **Rendering comes later.** Writes render in a microtask, so `this.editing = true; this.$command.focusTitle.emit();` reaches an input that is still hidden — or, inside `if:`, not built yet. Emit once it is drawn, from `$renderedCallback`:
2247
+
2248
+ ```html
2249
+ <template data-wcs="if: editing">
2250
+ <input data-wcs="value: title; command.focus: $command.focusTitle">
2251
+ </template>
2252
+ ```
2253
+
2254
+ ```javascript
2255
+ $renderedCallback(paths) {
2256
+ if (paths.includes("editing") && this.editing) this.$command.focusTitle.emit();
2257
+ },
2258
+ ```
2259
+
2260
+ - **User activation.** `showPicker()` and `play()` with sound need a recent user gesture. `on…: $command.x` calls the method inside the event; an `emit` after an `await` may come too late.
2261
+ - **Custom elements are unchanged**: they declare their commands in `wcBindable`, and one without a declaration still throws `#1202`. A customized built-in (`<button is="…">`) is a native element.
2262
+ - **When a button alone opens or closes it**, the platform's invoker commands need no state at all: `<button commandfor="dlg" command="show-modal">`. Use `command.` when the state decides (after a fetch, after validation), or for what invoker commands do not cover (`focus`, `play`, `scrollIntoView`).
2263
+
2205
2264
  ## Event Token (Event Binding)
2206
2265
 
2207
2266
  Command tokens push *into* a component (state invokes a method). **Event tokens** are the exact dual — they pull *out* of a component (an element dispatches an event, state receives it). Together they cover both directions of the element ↔ state boundary, and neither side ever holds a reference to the other — the token is the only shared object.