WebView

Experimental. A widget that renders a useful subset of CSS 3 / HTML 5 (over CSS 2.1, HTML 4.01, CSS Level 1 and HTML 3.2) with the pure-Go modernc.org/webview engine. Read-only by default — a present-only view for "HTML/CSS you control", a richer alternative to Text for things like complex bordered tables, floats, flexbox and positioned layout — with opt-in in-page JavaScript (see Scripting). It is still not a browser: there is no remote networking, and forms are not interactive yet. The API and the rendered output may change.

Live preview

A bordered, zebra-striped table with status pills — the kind of self-contained HTML/CSS layout you'd reach past Text for:

Live preview. In the Player's built-in docs viewer (scriptweaver --help), this renders WebView as a live, theme-aware widget you can interact with.

The engine paints the page into an off-screen RGBA buffer that is blitted into the widget. The view reflows to the widget's real width, scrolls when the content is taller (mouse wheel and drawn scroll bars), supports mouse text selection with Ctrl+C to copy, and reports link clicks to a callback.

Beyond the CSS 1 / HTML 3.2 basics (the box model with margin collapsing, borders and backgrounds; fonts and text alignment; <table> with colspan/rowspan, captions and automatic column widths; floats) it lays out the CSS 2.1 additions — positioned layout (position: relative/absolute/fixed with z-index and overflow clipping), inline-block, min/max sizing, attribute and structural selectors, external <link rel=stylesheet> / @import stylesheets, background-image, richer list markers, and the CSS 2.1 table model (border-collapse, table-layout, border-spacing, empty-cells) — and a useful slice of CSS 3 / HTML 5: @media queries (re-evaluated on resize), flexbox (display: flex with gap and flex-grow/-shrink/-basis), 2-D transforms, custom properties (--name / var()), position: sticky, generated content on ::before/::after, the :hover/:focus pseudo-classes, border-radius, box-sizing and extended color (rgb()/hsl(), hex alpha); the HTML 5 sectioning elements (<header>, <nav>, <main>, <article>, <section>, <footer>, <figure>…), the new form-input types and data: URIs. So moderately rich, self-contained documents lay out correctly. International text is handled too (see Limitations).

Example

const wv = app.WebView({});
wv.pack.configure({ fill: 'both', expand: true });

wv.loadHtml(`
  <!doctype html>
  <html><head><style>
    body { font-family: sans-serif; color: #222; margin: 16px; }
    h1 { color: #1a5276; border-bottom: 2px solid #1a5276; }
    table { border-collapse: collapse; }
    th, td { border: 1px solid #999; padding: 4px 8px; }
    th { background: #1a5276; color: white; }
    a { color: #2e86c1; }
  </style></head>
  <body>
    <h1>Release notes</h1>
    <p>This is <b>bold</b>, <i>italic</i>, and a
       <a href="https://example.com/changelog">link</a>.</p>
    <table>
      <tr><th>Widget</th><th>Status</th></tr>
      <tr><td>Card</td><td>shipped</td></tr>
      <tr><td>WebView</td><td>experimental</td></tr>
    </table>
  </body></html>`);

// The widget never navigates on its own — you decide what a link does.
wv.onLinkClick((href) => sw.sys.openURL(href));

Methods

Method Description
loadHtml(html) Render an HTML string. Relative sub-resources (none, for an inline string) resolve against the process working directory; prefer inline styles and data: URIs here.
loadUrl(url) Render a local document: a file path, a file:// URL, or a //zipfs:/… bundle path. The document's relative images and stylesheets resolve against it. Remote URLs (http, https, …) are refused — this view never reaches the network.
onLinkClick(fn) Register a callback invoked with the link's href (a string) when the user clicks a hyperlink. Pass null to clear it. The widget itself does not follow links.
text() Return the rendered document's visible text in reading order — useful for "copy all", search, or seeding an accessible description.
enableScript() Opt this view into in-page JavaScript (see Scripting). Call it before loadHtml/loadUrl so the first document's <script>s run. Returns this; scripting is off by default.

A pointer drag selects text and Ctrl+C copies the selection to the clipboard.

Options

WebView is a display widget (a themed frame holding the rendered image). It takes the common container options:

Option Type Description
script boolean Opt into in-page JavaScript at construction — equivalent to calling enableScript() before the first load. Default false. See Scripting.
width number Requested width in pixels. Usually you let geometry drive it (pack/grid/place) and the page reflows to fit.
height number Requested height in pixels.
padding string Inner padding, e.g. '8' or '8 4'.
cursor string The mouse cursor over the view.

Scripting (opt-in)

A WebView is a read-only HTML/CSS surface by default. Pass { script: true } at construction, or call enableScript() before the first load, to opt into the engine's JavaScript + DOM subsystem so in-page <script> runs: a mutable DOM with coalesced reflow, events (addEventListener, capture/bubble, preventDefault), timers and microtasks, native Promises and async/await, ES modules, and a read-only getComputedStyle.

Page scripts run on the Player's own vendored QuickJS, in a separate runtime with their own globals — they don't share your app's environment (no app/sw) and can't drive the rest of the UI. Scripting is not remote-capable either: a page's fetch / XMLHttpRequest and every sub-resource load stay local-only (files and the in-process VFS), exactly like loadUrl. With scripting off, <script> elements are ignored.

const wv = app.WebView({ script: true }); // or: app.WebView({}).enableScript()
wv.pack.configure({ fill: 'both', expand: true });
wv.loadHtml(`<button id="b">clicked 0×</button><script>
  let n = 0;
  document.getElementById('b').addEventListener('click', (e) => {
    e.target.textContent = 'clicked ' + ++n + '×';
  });
</script>`);

Limitations

This is an early, deliberately narrow widget. Known constraints:

See also