webview-bun icon indicating copy to clipboard operation
webview-bun copied to clipboard

can't open the local url

Open ilyaChuk opened this issue 5 months ago • 4 comments

import { Webview } from 'webview-bun';
import index from "./index.html";
import { serve } from "bun";

const server = serve({
  routes: {
    "/*": index,
  },
});
setTimeout(() => {
  let w = new Webview();
  w.title = 'Hello World';
  w.navigate(server.url.toString()); // http://localhost:3000/
  w.run();
}, 500);

opens about:bank. but, opening, for example, https://example.com - It's working

ilyaChuk avatar Jul 01 '25 13:07 ilyaChuk

ok, I figured it out, it's because the webview is blocking the stream so much that localhost:3000 cannot be created. It helps to bring logic into the webworker. But.. it doesn't work when compiling #37

ilyaChuk avatar Jul 01 '25 14:07 ilyaChuk

Webview doesn't need a server, it's a browser - a client. Due to the event loop, and bun not having threads, you can't serve AND run webview in the same process. You can use a child process OR you can treat your webview-bun app as just a client-side app, build a SPA, and/or bind functions to call using webview.bind. Hope this helps.

gabereiser avatar Aug 06 '25 23:08 gabereiser

There are so many limitations, for example, need to compile the SPA into a true single file with embedded CSS and JS, Since webview only accepts a single HTML string instead of Bun.HTMLBundle. What about images, font, and other resources?

deckyfx avatar Oct 22 '25 00:10 deckyfx

If it’s defined in the css, it should be bundled. If it’s not, you can use the internet url of it somewhere (github pages perhaps). You can serve it up as a base64 string from a IPC function call. Or you can attempt the dreaded file:// path…

gabereiser avatar Oct 25 '25 00:10 gabereiser

Good evening everyone, I'm Brazilian. I don't speak English very well, but I found a solution. I managed to create a binary like this and also get the server working alongside it. On src/index.ts make:

` import WebView from "@rcompat/webview"; import platform from "@rcompat/webview/windows-x64";

async function runServerMode() { const { default: ServerModule } = await import("./server/server.module"); new ServerModule(); await new Promise(() => {}); }

function isScriptRunMode(): boolean { return !!process.argv[1] && /.(js|ts|mjs|cjs)$/.test(process.argv[1]); }

function determineSpawnCmd(): string[] { if (isScriptRunMode()) { return ["bun", process.argv[1]!, "--server"]; } const exePath = process.execPath || process.argv[0]; return [exePath!, "--server"]; }

async function streamChildOutput(stream: ReadableStream<Uint8Array> | null, logFn: (s: string) => void) { if (!stream) return; const reader = (stream as ReadableStream<Uint8Array>).getReader(); const decoder = new TextDecoder(); try { while (true) { const { value, done } = await reader.read(); if (done) break; if (value) logFn(decoder.decode(value)); } } catch (decoder_error) { console.error({decoder_error}) // ignore read errors on shutdown } finally { try { await reader.cancel(); } catch {} } }

async function waitForServer(url: string, timeoutMs = 5000, intervalMs = 150) { const start = Date.now(); while (Date.now() - start < timeoutMs) { try { const res = await fetch(url, { method: "GET" }); if (res.ok) return true; } catch (start_error) { console.error({start_error}) // ingnorar } await new Promise((r) => setTimeout(r, intervalMs)); } return false; }

async function runMainMode() { const cmd = determineSpawnCmd();

const child = Bun.spawn({ cmd, stdout: "pipe", stderr: "pipe", env: process.env, cwd: process.cwd(), });

streamChildOutput(child.stdout as unknown as ReadableStream<Uint8Array> | null, (s) => console.log("[SERVER]", s.trim()) ); streamChildOutput(child.stderr as unknown as ReadableStream<Uint8Array> | null, (s) => console.error("[SERVER-ERR]", s.trim()) );

// aguarda servidor subir (healthcheck) const url = "http://localhost:3000/"; const ok = await waitForServer(url, 8000, 200); if (!ok) { console.error("Servidor não respondeu em http://localhost:3000/ — abortando."); // fecha child e sai try { child.kill(); } catch {} process.exit(1); }

// cria e abre o WebView const view = new WebView({ debug: true, platform }); view.navigate(url); view.run();

// quando WebView fechar, finaliza o child try { child.kill(); } catch (run_main_mode_error) { console.error({run_main_mode_error}) // ingorar } }

// ENTRYPOINT if (process.argv.includes("--server")) { runServerMode().catch((e) => { console.error("Server failed:", e); process.exit(1); }); } else { runMainMode().catch((e) => { console.error("Main failed:", e); process.exit(1); }); }

`

Wesley-Fernandes avatar Nov 24 '25 07:11 Wesley-Fernandes