diff --git a/README.md b/README.md index 270f0b7..7264f91 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,9 @@ Open `http://127.0.0.1:8891/`. The page can spawn: - `Sai VN Surface`, a fixture for visual-novel scenes exported as `gamecult.eve.surface.v1`, including embedded Norn and TeX surfaces placed diegetically inside the scene. +- `Huginn .cc`, a read-only `.eve` fixture emitted by CultCache/Huginn for + `.cc` files: source witness, schema catalog, persisted records, payload byte + counts, and decode status. Huginn emits Eve DSL; Eve compiles and renders it. - `Reactive DSL`, a browser-reference fixture that compiles a small `.eve` composition language into the same surface contract and binds UI controls to live CultMesh-shaped vars, collections, and derived fields. diff --git a/web/fixtures/huginn-cc-surface.eve b/web/fixtures/huginn-cc-surface.eve new file mode 100644 index 0000000..6deaeab --- /dev/null +++ b/web/fixtures/huginn-cc-surface.eve @@ -0,0 +1,30 @@ +# Huginn emits Eve DSL. Renderers compile and lower it. +surface cultcache.huginn.inspector "Huginn .cc Inspector" +version 1 + +collection huginn.catalog.entries +item huginn.catalog.entries "gamecult.persona_state | gamecult.persona_state.v0 | sha256:persona-state | 12 members" +item huginn.catalog.entries "voidbot.moderation_cursor | voidbot.moderation_cursor.v1 | sha256:moderation-cursor | 4 members" +item huginn.catalog.entries "voidbot.face_affect | voidbot.face_affect.v1 | sha256:face-affect | 7 members" + +collection huginn.records.entries +item huginn.records.entries "persona:huginn | gamecult.persona_state | 2026-05-31T22:14:12.000Z | 2048 bytes | decoded" +item huginn.records.entries "cursor:moderation | voidbot.moderation_cursor | 2026-05-31T22:12:03.000Z | 640 bytes | decoded" +item huginn.records.entries "affect:face | voidbot.face_affect | 2026-05-31T22:13:44.000Z | 768 bytes | decoded" + +card source "Source Witness" +text "file: E:\\Projects\\CultCacheTS\\.voidbot\\state\\huginn.cc" +text "format: cultcache.store.v1" +text "bytes: 3456" +text ".cc bytes are authority; Huginn only emits Eve DSL." +end + +card catalog "Schema Catalog" +text "schemas: 3" +list "Catalog Entries" bind huginn.catalog.entries +end + +card records "Persisted Records" +text "records: 3" +list "Record Entries" bind huginn.records.entries +end diff --git a/web/index.html b/web/index.html index b1490c7..2a3a48e 100644 --- a/web/index.html +++ b/web/index.html @@ -23,6 +23,7 @@ + diff --git a/web/surface.js b/web/surface.js index f3f57a3..8de04c0 100644 --- a/web/surface.js +++ b/web/surface.js @@ -7,6 +7,7 @@ const surfaceVersion = document.querySelector("#surface-version"); const voidBotTab = document.querySelector("#voidbot-tab"); const fensalirTab = document.querySelector("#fensalir-tab"); const saiTab = document.querySelector("#sai-tab"); +const huginnTab = document.querySelector("#huginn-tab"); const dslTab = document.querySelector("#dsl-tab"); let socket; @@ -30,6 +31,13 @@ saiTab.addEventListener("click", async () => { renderSurface(await response.json(), "fixture"); }); +huginnTab.addEventListener("click", async () => { + setActiveTab(huginnTab); + closeSocket(); + const response = await fetch("./fixtures/huginn-cc-surface.eve"); + renderSurface(compileEveDsl(await response.text()), "dsl"); +}); + dslTab.addEventListener("click", async () => { setActiveTab(dslTab); closeSocket(); @@ -40,7 +48,7 @@ dslTab.addEventListener("click", async () => { openVoidBot(); function setActiveTab(tab) { - for (const button of [voidBotTab, fensalirTab, saiTab, dslTab]) { + for (const button of [voidBotTab, fensalirTab, saiTab, huginnTab, dslTab]) { button.classList.toggle("active", button === tab); } } @@ -52,20 +60,25 @@ function openVoidBot() { const url = `${scheme}//${host}:8795/eve/deck`; statusEl.textContent = `connecting ${url}`; socket = new WebSocket(url); + const activeSocket = socket; socket.addEventListener("open", () => { + if (socket !== activeSocket) return; statusEl.textContent = "connected to Mimir Eve broker"; - socket.send(JSON.stringify({ type: "open-provider", providerId: "voidbot.swarm" })); + activeSocket.send(JSON.stringify({ type: "open-provider", providerId: "voidbot.swarm" })); }); socket.addEventListener("message", event => { + if (socket !== activeSocket) return; const state = JSON.parse(event.data); if (state.providerId === "voidbot.swarm") { renderSurface(state, "live"); } }); socket.addEventListener("close", () => { + if (socket !== activeSocket) return; statusEl.textContent = "Mimir broker disconnected"; }); socket.addEventListener("error", () => { + if (socket !== activeSocket) return; statusEl.textContent = "Mimir broker error; is port 8795 running?"; }); }