Bindings

examples/webview/bindings.ts — typed bindings: pass a plain object of functions to the constructor and every key becomes a typed window.* function in the page. No hand-written JSON — the page's arguments decode into each callback's declared parameter types and the return value JSON-encodes automatically, async included. The object's keys are the entire exposed surface (the allowlist): nothing reaches window unless it's here.

The bindings value can be an inline object literal or, as here, a variable — handy when the API grows or is assembled elsewhere. `w.bind(name, cb)` stays the raw string-JSON escape hatch and `w.bindTyped(name, fn)` the imperative typed form; add --emit-window-dts to generate the page-side Window typing.

Build: klainmain examples/webview/bindings.ts && ./examples/webview/bindings Windows: klainmain examples/webview/bindings.ts && examples/webview/bindings.exe (WebView2; see README) (macOS: zero extra deps; Linux: WebKitGTK dev packages — see README.)

import { Webview } from 'klain:webview'
import { readdirSync } from 'fs'

interface Stats { calls: number; last: string }

let calls = 0
let last = ""

const api = {
  // scalar in, scalar out — page: await window.add(40, 2)
  add: (a: number, b: number): number => a + b,
  // string in, string out
  greet: (name: string): string => {
    calls = calls + 1
    last = name
    return `Hello from native code, ${name}!`
  },
  // object return — JSON-encoded for the page
  stats: (): Stats => ({ calls: calls, last: last }),
  // real native work: list a directory with fs
  listDir: (path: string): string[] => readdirSync(path),
  // async — the page's promise settles with the encoded value
  slowDouble: async (n: number): Promise<number> => {
    await new Promise<number>((resolve) => { setTimeout(() => resolve(0), 300) })
    return n * 2
  },
}

const w = new Webview({ title: "Typed Bindings", width: 700, height: 520, debug: true, bindings: api })

w.html(`<!doctype html>
<html>
<head><meta charset="utf-8"><title>Typed Bindings</title>
<style>
  body { font-family: -apple-system, system-ui, sans-serif; margin: 2rem; }
  button { font-size: 1rem; padding: .5rem 1rem; margin: .25rem .5rem .25rem 0; }
  pre { background: #f4f4f4; padding: .75rem; min-height: 6rem; }
</style></head>
<body>
  <h1>Typed window.* bindings</h1>
  <button onclick="run('add', window.add(40, 2))">add(40, 2)</button>
  <button onclick="run('greet', window.greet('Thessaloniki'))">greet('Thessaloniki')</button>
  <button onclick="run('stats', window.stats())">stats()</button>
  <button onclick="run('listDir', window.listDir('.'))">listDir('.')</button>
  <button onclick="run('slowDouble', window.slowDouble(21))">await slowDouble(21)</button>
  <pre id="out"></pre>
  <script>
    async function run(label, promise) {
      const value = await promise
      document.getElementById('out').textContent = label + ' -> ' + JSON.stringify(value, null, 2)
    }
  </script>
</body>
</html>`)

w.run()
console.log("window closed")
View source on GitHubexamples/webview/bindings.ts