From 9cabba671d99ca63d6d28e8aa6a9f270b51038b4 Mon Sep 17 00:00:00 2001 From: rawan10101 Date: Thu, 16 Jul 2026 21:17:54 +0300 Subject: [PATCH] feat(wasm-emcc): add browser support for Neovim --- src/wasm/app.js | 60 +++++++++++++++++++++++++++++++++++++++++ src/wasm/index.html | 25 +++++++++++++++++ src/wasm/msgpack.js | 0 src/wasm/nvim-worker.js | 0 src/wasm/protocol.js | 0 src/wasm/rpc.js | 0 src/wasm/serve.py | 16 +++++++++++ src/wasm/style.css | 5 ++++ 8 files changed, 106 insertions(+) create mode 100644 src/wasm/app.js create mode 100644 src/wasm/index.html create mode 100644 src/wasm/msgpack.js create mode 100644 src/wasm/nvim-worker.js create mode 100644 src/wasm/protocol.js create mode 100644 src/wasm/rpc.js create mode 100644 src/wasm/serve.py create mode 100644 src/wasm/style.css diff --git a/src/wasm/app.js b/src/wasm/app.js new file mode 100644 index 0000000000..48b4f61e25 --- /dev/null +++ b/src/wasm/app.js @@ -0,0 +1,60 @@ +const statusEl = document.getElementById('status'); +const logEl = document.getElementById('log'); +const setStatus = s => statusEl.textContent = s; +const log = (...args) => { + logEl.textContent += args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ') + '\n'; + console.log(...args); +}; + +if (!crossOriginIsolated) { + setStatus('NOT cross-origin isolated -- SharedArrayBuffer unavailable. Serve with COOP/COEP headers.'); +} else { + main(); +} + +function main() { + const transport = new WorkerTransport('nvim-worker.js', { cols: 80, rows: 24 }); + transport.onStatus(text => { setStatus(text); log('[status]', text); }); + + const nvim = new RpcClient(transport); + setTimeout(async () => { + console.log("Auto Request"); + + try { + const r = await nvim.request("nvim_get_api_info", []); + console.log(r); + } catch (e) { + console.error(e); + } +}, 10000); + + nvim.on('redraw', params => log('Notification redraw', params)); + + document.getElementById('apiInfoBtn').addEventListener('click', async () => { + setStatus('sending nvim_get_api_info...'); + try { + const result = await nvim.request('nvim_get_api_info', []); + log('RESPONSE nvim_get_api_info ->', result); + setStatus('got nvim_get_api_info response'); + } catch (e) { + log('ERROR nvim_get_api_info ->', e); + setStatus('nvim_get_api_info failed, see log'); + } + }); + + document.getElementById('attachBtn').addEventListener('click', async () => { + setStatus('sending nvim_ui_attach...'); + try { + const result = await nvim.request('nvim_ui_attach', [80, 24, { rgb: true, ext_linegrid: true }]); + log('RESPONSE nvim_ui_attach ->', result); + setStatus('UI attached finally. Watching for redraw notifications...'); + } catch (e) { + log('ERROR nvim_ui_attach ->', e); + setStatus('nvim_ui_attach failed, see log'); + } + }); + + document.getElementById('persistBtn').addEventListener('click', () => { + transport.persist(); + }); +} diff --git a/src/wasm/index.html b/src/wasm/index.html new file mode 100644 index 0000000000..44b38347a4 --- /dev/null +++ b/src/wasm/index.html @@ -0,0 +1,25 @@ + + + + +nvim.wasm RPC client + + + +
+ + + + booting… +
+
+ + + + + + + + + + diff --git a/src/wasm/msgpack.js b/src/wasm/msgpack.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/wasm/nvim-worker.js b/src/wasm/nvim-worker.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/wasm/protocol.js b/src/wasm/protocol.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/wasm/rpc.js b/src/wasm/rpc.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/wasm/serve.py b/src/wasm/serve.py new file mode 100644 index 0000000000..753935f9e7 --- /dev/null +++ b/src/wasm/serve.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +import http.server +import socketserver + +PORT = 8001 + +class Handler(http.server.SimpleHTTPRequestHandler): + def end_headers(self): + self.send_header("Cross-Origin-Opener-Policy", "same-origin") + self.send_header("Cross-Origin-Embedder-Policy", "require-corp") + super().end_headers() + +if __name__ == "__main__": + with socketserver.TCPServer(("", PORT), Handler) as httpd: + print(f"Serving with COOP/COEP on http://localhost:{PORT}") + httpd.serve_forever() diff --git a/src/wasm/style.css b/src/wasm/style.css new file mode 100644 index 0000000000..d27d30a75a --- /dev/null +++ b/src/wasm/style.css @@ -0,0 +1,5 @@ +body { font-family: monospace; background: #1e1e1e; color: #ddd; padding: 10px; margin: 0; } +#bar { margin-bottom: 10px; } +#bar button { margin-right: 8px; } +#status { color: #9c9; } +#log { white-space: pre-wrap; background: #111; padding: 10px; border-radius: 4px; }