Mercurial
diff infinite_canvas/web_surface_web.c @ 280:49e9e591c9bb
Add persistent dictation, prewarmed WebRTC speech input, Copilot SDK routing, animated conversation lifecycle controls, parking, and architecture coverage.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Tue, 18 Aug 2026 19:14:53 -0700 |
| parents | 8d560f50ed4c |
| children |
line wrap: on
line diff
--- a/infinite_canvas/web_surface_web.c Mon Aug 17 22:22:36 2026 -0700 +++ b/infinite_canvas/web_surface_web.c Tue Aug 18 19:14:53 2026 -0700 @@ -9,6 +9,69 @@ }); }); +EM_JS(void, Canvas_Web_Dictation_Init, (), { + if (Module.canvasDictationInitialized) return; + Module.canvasDictationInitialized = true; + Module.canvasDictationQueue = []; + window.addEventListener("message", event => { + if (event.data?.type !== "zenbu.dictation.event") return; + const frame = document.getElementById("canvas-dictation-transport"); + if (!frame || frame.contentWindow !== event.source) return; + Module.canvasDictationQueue.push({ + kind: String(event.data.kind || ""), + text: String(event.data.text || ""), + }); + }); +}); + +EM_JS(void, Canvas_Web_Dictation_Set_Active, (int active), { + let frame = document.getElementById("canvas-dictation-transport"); + if (!frame) { + frame = document.createElement("iframe"); + frame.id = "canvas-dictation-transport"; + frame.title = "Canvas dictation transport"; + frame.allow = "microphone"; + frame.sandbox = + "allow-same-origin allow-scripts"; + frame.style.position = "fixed"; + frame.style.left = "-2px"; + frame.style.top = "-2px"; + frame.style.width = "1px"; + frame.style.height = "1px"; + frame.style.opacity = "0"; + frame.style.pointerEvents = "none"; + frame.src = "http://127.0.0.1:8090/?canvas=1"; + document.body.appendChild(frame); + } + const send = () => frame.contentWindow.postMessage( + { + type: "zenbu.dictation.set-active", + active: active !== 0, + }, + "*"); + frame.addEventListener("load", send, {once: true}); + send(); +}); + +EM_JS(void, Canvas_Web_Dictation_Commit, (), { + const frame = document.getElementById("canvas-dictation-transport"); + frame?.contentWindow?.postMessage( + {type: "zenbu.dictation.commit"}, + "*"); +}); + +EM_JS(int, Canvas_Web_Dictation_Consume, ( + char *p_text, + int text_capacity), { + const event = Module.canvasDictationQueue?.shift(); + if (!event) return 0; + stringToUTF8(event.text, p_text, text_capacity); + if (event.kind === "status") return 1; + if (event.kind === "partial") return 2; + if (event.kind === "final") return 3; + return 0; +}); + EM_JS(void, Canvas_Web_Create_Or_Update, ( uint32 entity_id, const char *p_url, @@ -170,6 +233,7 @@ } surface.remove(); }); + document.getElementById("canvas-dictation-transport")?.remove(); }); static Rectangle Canvas_Web_Content_Bounds( @@ -236,6 +300,7 @@ p_surface->p_arena = p_arena; p_surface->dark_mode = dark_mode; p_surface->initialized = TRUE; + Canvas_Web_Dictation_Init(); return TRUE; } @@ -369,6 +434,32 @@ Canvas_Web_History_Go(entity_id, 1); } +void Canvas_Web_Surface_Set_Dictation_Active( + Canvas_Web_Surface *p_surface, + boolean active) +{ + (void)p_surface; + Canvas_Web_Dictation_Set_Active(active); +} + +void Canvas_Web_Surface_Commit_Dictation( + Canvas_Web_Surface *p_surface) +{ + (void)p_surface; + Canvas_Web_Dictation_Commit(); +} + +Canvas_Dictation_Event Canvas_Web_Surface_Consume_Dictation( + Canvas_Web_Surface *p_surface, + char *p_text, + size_t text_capacity) +{ + (void)p_surface; + return (Canvas_Dictation_Event)Canvas_Web_Dictation_Consume( + p_text, + (int)text_capacity); +} + boolean Canvas_Web_Surface_Is_Focused(const Canvas_Web_Surface *p_surface) { (void)p_surface;