Files
Hare db4c8ba97f
Reference site / build (push) Canceled after 0s
feat: add highlighted protocol views and inline operation forms
Expose Rust Text View tokens and safely highlight signatures and JSON results. Add animated Interface and inline Run disclosures with a shared 10ms line cadence, typed argument inputs, and explicit execution.

Update documentation and browser regressions for the new controls and explicit counter-value responses.
2026-10-26 10:12:22 +09:00

149 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WIP Reference
Worldspace Interface Protocolの仕様文書と、その文書を実際のWIPで読むサイトです。
## 構成
- [`contents/`](contents/) — 仕様の正本。番号付きMarkdownを章順に読みます。
- [`contents/draft/`](contents/draft/) — 検討中の草案。公開サイトとWIPの生成データには含めません。
- [`site/`](site/) — Astroの静的サイトと、ブラウザ内で動くRust Client / Host。
[第1章:コンセプト](contents/1-concept.md) / [Core Model](contents/2-core-model.md) / [Protocol](contents/3-protocol.md) / [Client](contents/4-client.md) / [WIP over HTTP](contents/5.1-wip-over-http.md)
## ブラウザ内のWIP
```text
ツリー・本文UI (TypeScript)
→ BrowserSession (WASM公開API)
→ wip-client::direct::Client
→ 型付き要求・応答の直接呼び出し
→ サイト用Worldspace / wip-host
```
ClientとHostは同じWASM内にあります。三つのinteraction(observe、fetch_interface、call_operation)にHTTP binding、JSON wire envelope、socketは使いません。WASMと`worldspace.json`の取得、Astroによる静的ページ遷移は通常のasset/page配信です。
公開文書と既存のhome/indexコンポーネントから`worldspace.json`をビルド時に生成します。本文の別コピーを編集する必要はありません。Rust Providerはそれを変更不可の文書Objectとして公開し、本文はroot scopeの`document.read()`で取得します。章Objectは本文と子の節を同時に持ちます。
Worldspace path、表示用の既存識別子、Web URLは別です。たとえば:
- Worldspace: `/book/1-concept.md/1.1-interaction-abstraction.md`
- 表示識別子: `/1.1-interaction-abstraction.md`
- Web URL: `/book/1.1-interaction-abstraction/`
ツリーはClientの観測とOperation結果を使い、URL・履歴・折りたたみ・読書位置はUIが管理します。WASM sessionはAstroのページ切替をまたいで存続します。JavaScriptなし、WASM未対応、asset読み込み失敗時も、静的HTMLと通常リンクで閲覧できます。`data-wip-state="ready"`だけが実WIP経由の反映成功を表し、失敗時は`fallback`です。
主なファイル:
- `site/worldspace/` — サイト専用Rust Provider、入力検証、再帰observe、document Interface。
- `site/browser/` — Rust ClientとHostを所有するWASM session。
- `site/src/pages/worldspace.json.ts` — 公開文書・home/indexのデータ生成。
- `site/src/scripts/browser-wip.ts` — WASM初期化、session寿命、結果検証と本文反映。
- `site/src/scripts/landing.ts` — 共通ツリー、導入演出、ページ切替と読書位置。
### Protocolモード
右上の`Read` / `Protocol`で切り替えます。Protocolでは通常モードとは独立したClient session・WASM instanceを使い、`?mode=protocol`をツリー遷移と再読込で保持します。Protocol開始時とツリーでのObject選択時には、選択済みObjectをDepth=1で自動Observeします。直リンク・再読込・履歴移動でも現在のObjectを対象とし、Interface取得と本文Operationは自動実行しません。
1. Protocolを開くと、Rust `wip-text-view`が生成した選択済みObjectのText Viewを表示します。ツリーで別Objectを選ぶと、そのText Viewに切り替わります。同じObjectの再選択でもObserveします。
2. 未取得のInterfaceは`? /::document`の形式で表示します。クリックするとDescriptorとInterface Text Viewを取得して`v /::document`となり、再クリックで`> /::document`に折りたたみます。取得済みの再展開はClientのfresh cacheを利用します。
3. 各Operationのコメントと宣言の間にある`▶ Run`を押すと、その場に`create(name: "…")`のような引数入力フォームが展開されます。`Execute`または単一行入力でのEnterで実行します。引数なしの`document.read()`もフォームを開くだけでは呼び出さず、明示実行が必要です。フォームは再クリックまたはEscapeで閉じられます。
文字列は引用符内の欄にそのまま入力し、整数は10進表記で入力します。UIがパラメーターごとにtagged Valueへ変換し、WASM APIには従来と同じJSON record(例:`{"name":{"kind":"string","value":"example"}}`)を渡します。`integer`はJSのNumberを経由せず文字列として渡し、Protocol Integerの範囲(±9,007,199,254,740,991)などはClientで検証します。省略可能な引数はincludeで送信を選択でき、複合型・名前付き型には個別のtagged Value JSON欄を使います。`bytes`は0〜255の配列、`record`と`list`は再帰的なtagged Value、`unit`は`{"kind":"unit"}`です。API入力上限は64KiB・32階層・4096値、path/scope/name/operationは各4096 UTF-8 bytesです。
Object/Interfaceのハイライトには`wip-text-view`の`tokenize_object` / `tokenize_interface`を使います。WASMは同じtoken列から`textView`と`textViewTokens`(kind/text)を返し、UIは文字列を再解析せず、安全なテキストノードで種別を着色します。ライト/ダークのパレットは`site/src/styles/text-view.css`に定義しています。空白・改行・引用は保持し、未知の種別は無装飾、不整合やtokenなしの応答は元のplain textで表示します。
InterfaceとRunフォームの開閉は同じ行アニメーションを使います。展開時は上から、折りたたみ時は下から1行10msずつフェードし、高さも行数×10msで変化します。reduced-motion設定と印刷では省略します。
Operation結果のハイライトには[Kirari](https://github.com/vortexkit/kirari)(`@vortexlabs/kirari`)のcoreとJSON grammarだけを使い、token配列を安全なspan/text nodeへ変換します。キー・文字列・数値・真偽値・null・記号をライト/ダーク対応で色分けし、整形済みJSONの文字列は変更しません。ハイライトに失敗した場合もplain textで結果を保持します。
Object/InterfaceのText ViewとOperation結果は別領域です。結果はpath・Interface・Operation付きのtagged JSONで、本文HTMLも文字列として表示し、markupとして実行しません。対象・Interface・Operationの変更で以前の結果表示を消します。ObserveとInterface取得はClientのfresh cacheを使えますが、Operationは明示実行ごとに1回呼び出し、自動再試行しません。ページ遷移・モード往復ではsessionを保持し、再読込で新sessionになります。
初期化失敗時は利用不可、trap後はその手動instanceを再利用・解放せず再読込が必要と表示します。`Read`では静的本文を閲覧できます。JavaScriptなしではProtocol URLでも通常の静的閲覧を維持し、未実行を実行成功として表示しません。
### Sources / Packages
`/sources/`と`/packages/`は`/book`と並ぶルート直下の葉Objectです。リンク先リポジトリや各crateを子Objectにはしません。通常ページと`document.read()`は同じrendererから本文を生成します。
- Sources: `wip-reference`と`wip-rs`の2リポジトリ。
- Packages: Rust workspaceのREADMEとCargo manifestに基づく`wip-protocol`、`wip-interface-macros`、`wip-interface`、`wip-host`、`wip-http`、`wip-client`、`wip-text-view`。各crateのsourceとworkspace READMEへリンクします。レジストリでの公開状況や公開バージョンは表しません。
- 一覧の正本は`site/src/lib/catalog.ts`です。内部crate、examples、draftは公開一覧に含めません。
### Playground
`/playground/?mode=protocol`で、メモリ内のObjectを操作できます。親は`/::document`と`/::collection`、子カウンターは`/::counter`を公開します。
- `collection.create(name)` / `remove(name)`:子Objectを作成・削除。名前は`[A-Za-z0-9_-]{1,32}`、最大32個。新規値は0です。
- `counter.get()` / `increment(amount)` / `reset()`:現在値の取得・増減・0へのリセット。Protocol Integerの範囲外への変更は失敗し、状態を変更しません。
- 初期状態は`apple = 3`、`orange = 0`。ツリーはObject名だけを表示します。`description`は`An in-memory counter.`という説明文で、現在値や機械可読データとして解釈しません。値は明示的な`counter.get()` / `increment()` / `reset()`のOperationレスポンスに表示します。
- 操作成功後に親と選択中Objectを再Observeします。変更可能なPlaygroundのObserveはClientの`refresh_observed`で実取得し、作成・削除による子一覧の変更をツリーへ反映します。Observeからカウンター値を読み取ったり、値の取得Operationを自動実行したりしません。再観測に失敗しても成功済みOperationを再実行しません。
- 子の選択は`/playground/?mode=protocol&object=%2Fplayground%2Fapple`のようなURLで保持します。ページ・履歴・Read/Protocol往復ではProtocol sessionが残り、ブラウザの再読み込みで初期化します。削除済みObjectへの履歴移動はエラーを表示し、復活させません。
- 通常Readは静的な操作説明を表示します。状態の永続化・外部送信・文書変更はありません。
例:createの`name`欄に`banana`、incrementの`amount`欄に`2`を入力すると、それぞれ`create(name: "banana")`、`increment(amount: 2)`の呼び出しになります。
### 実行上の境界
このサイトの操作は同期・メモリ内です。文書は読み取り専用で、Playgroundだけがsession内の変更を許可します。入力JSONは16MiB、Object数4096、階層32を上限とし、Clientにもstate/history上限を設けています。JS APIにdeadlineや並行実行中のcancellation設定はなく、HostにはdeadlineなしのExecutionContextを渡します。ブラウザ非対応の`std::time::Instant::now()`を呼ぶdeadline経路は利用しません。将来deadlineを公開する場合はブラウザ対応時計と実行テストが必要です。
release WASMは`panic=abort`です。Rustのpanic/trapは`catch_unwind`でProtocol failureへ回復できるとは扱いません。通常の入力・ProtocolエラーはJS例外として返り、サイトは静的表示へfallbackします。trapしたinstanceは以後呼び出し・解放せず、そのブラウザdocumentの残りの寿命は静的表示へfallbackします。ページ切替でOperationを再実行せず、復帰には再読み込みによる新instanceを使います。
JS境界のJSONはサイト専用のtree/document DTOであり、汎用WIP wire形式ではありません。本文はビルド時に既存Markdown rendererが生成した公開HTMLです。任意の外部Hostが返すHTMLを無条件に挿入する汎用クライアントではありません。
## 開発・検証
Node.js 22.12以上(CIは24)、npm、Rust 1.97.1、`wasm32-unknown-unknown`標準ライブラリ、`wasm-bindgen-cli` 0.2.121を使います。Rust sourceはこのrepoの隣の`wip-rs/`に置き、`site/rust-source.json`で指定するrevisionをcheckoutします。
```sh
# このrepositoryのrootで実行
# 既存の隣接checkoutがある場合はcloneせず、その作業を保全してください。
git clone https://gitea.hareworks.net/Hare/wip-rs ../wip-rs
git -C ../wip-rs checkout "$(node -p 'require("./site/rust-source.json").revision')"
rustup toolchain install 1.97.1 --component rustfmt --component clippy --target wasm32-unknown-unknown
cargo +1.97.1 install wasm-bindgen-cli --version 0.2.121 --locked
cd site
npm ci
npm run dev
```
`npm run dev`と`npm run build`は先にWASMをsourceから生成します。Rustとwasm-bindgenがPATHにないNix環境では、隣接`wip-rs/flake.lock`を使って開発環境・LLD・wasm-bindgenを起動します。Cargo.lockとwasm-bindgenのバージョンは固定し、rustup環境では`site/rust-toolchain.toml`を使います。通常のWASMビルドはRust sourceのrevisionとclean状態を確認します。Rust側を同時編集するローカル開発に限り`WIP_ALLOW_DIRTY_SOURCE=1`で明示的に解除できますが、release検証では解除しません。生成物`site/public/wip-wasm/`と`site/target/`はGit管理しません。
```sh
npm run verify # unit tests → Astro check → WASM/build → link checks
WIP_TEST_GENERATED=1 node --test tests/worldspace-data.test.mjs
cargo test --workspace --locked -- --include-ignored # build後の実文書テストも実施
cargo clippy --workspace --all-targets --locked -- -D warnings
npx playwright install chromium
npm run test:browser
npm run test:dev # dev serverでも実WASMの読み込み・本文反映とフォント配信を確認
# インストール済みChromeを使う場合:CHROME_PATH=/path/to/chrome npm run test:browser
npm run preview
```
Nixでnativeテストを行う場合は`nix develop ../../wip-rs --command cargo test --workspace --locked -- --include-ignored`のように実行します。テスト用previewは`127.0.0.1:4322`、`test:dev`は別のdev serverを4323で起動し、通常のdev server(4321)を再利用・停止しません。`SITE_BASE=/reference npm run test:dev`でサブパスのWASM・全6書体の読み込みも確認できます。フォント定義は`public/fonts/fonts.css`をbase付きURLで参照し、各WOFF2はCSSからの相対URLで解決します。
`npm run test:browser`は先に`test-probes` feature付きの別WASMを`site/test-assets/`へ生成します。`wasm-limits.spec.ts`は実WASMの入力上限と意図的なRust panicを検証し、trapがProtocol failureへ誤変換されず、サイトがsessionを再利用しないことを確認します。このprobeはテストrouteでのみ配信され、通常の`public/`・`dist/`には入りません。
ブラウザテストはPC/mobile、no-JS、実WASMの独立session・三つのinteraction、WIP結果によるUI更新、失敗fallback、履歴、初回演出、ライト/ダークを検証します。`wasm.spec.ts`は静的asset取得後にfetchを禁止して直接呼び出しを確認し、`wip.spec.ts`は壊した初期HTMLを実WIP結果が修復することまで確認します。
## 表示と文書編集
`/`が独立したホーム、`/book/`が目次です。各章の既存Web URLを維持し、共通ツリーでは章の下に節を表示します。初回ホームの導入演出は入力で開始し、EscapeやSkipで完了できます。`prefers-reduced-motion`では演出を省略します。配色はOSの`prefers-color-scheme`へ追従し、JavaScriptなしでも有効です。
文書は`contents/`だけを編集します。先頭の`#`見出しをタイトルに使うためfrontmatterは不要です。Markdownの相対`.md`リンクはビルド時にBook URLへ変換し、query/fragmentを維持します。draftへのリンクは公開リンクにしません。`npm run check:links`で生成ページ・内部リンク・見出しanchorを検証します。
## サイト公開
静的出力の`site/dist/`を公開します。常駐WIPサーバー、Nodeサーバー、データベースは不要です。このrepositoryへのpushだけでインターネットへ自動デプロイする設定ではありません。
```sh
SITE_URL=https://your-domain.example npm run verify
# サブパス配信の検証
SITE_URL=https://your-domain.example SITE_BASE=/wip-reference npm run verify
SITE_URL=https://your-domain.example SITE_BASE=/wip-reference npm run test:browser
```
- `SITE_URL`はorigin、`SITE_BASE`はpath prefixです。既定のbaseは`/`です。
- Static hostはディレクトリの`index.html`を配信し、未知URLに`404.html`を返します。SPA fallbackは不要です。
- `.wasm`は`application/wasm`で配信してください。モジュール、WASM、manifest、HTMLを同じbuildの成果物として公開します。
- Gitea Actionsは固定revisionのRust sourceとサイトを組み合わせ、通常/サブパスの検証後に`wip-reference-site` artifactを作ります。Rust revisionの更新時は両repositoryのテストを実施し、参照commitを取得可能にしてからCIを動かしてください。