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.
WIP Reference
Worldspace Interface Protocolの仕様文書と、その文書を実際のWIPで読むサイトです。
構成
contents/— 仕様の正本。番号付きMarkdownを章順に読みます。contents/draft/— 検討中の草案。公開サイトとWIPの生成データには含めません。site/— Astroの静的サイトと、ブラウザ内で動くRust Client / Host。
第1章:コンセプト / Core Model / Protocol / Client / WIP over HTTP
ブラウザ内のWIP
ツリー・本文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は自動実行しません。
- Protocolを開くと、Rust
wip-text-viewが生成した選択済みObjectのText Viewを表示します。ツリーで別Objectを選ぶと、そのText Viewに切り替わります。同じObjectの再選択でもObserveします。 - 未取得のInterfaceは
? /::documentの形式で表示します。クリックするとDescriptorとInterface Text Viewを取得してv /::documentとなり、再クリックで> /::documentに折りたたみます。取得済みの再展開はClientのfresh cacheを利用します。 - 各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(@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します。
# この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管理しません。
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だけでインターネットへ自動デプロイする設定ではありません。
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-siteartifactを作ります。Rust revisionの更新時は両repositoryのテストを実施し、参照commitを取得可能にしてからCIを動かしてください。