From 0357a5e33000045b05514601626a90ee8ac0e160 Mon Sep 17 00:00:00 2001 From: Rohit Kushwaha Date: Sun, 26 Jul 2026 19:20:05 +0530 Subject: [PATCH 1/2] feat: webview api docs --- .vitepress/config.mts | 4 + docs/advanced-apis/webview.md | 169 ++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 docs/advanced-apis/webview.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 09933ea..a179d2a 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -328,6 +328,10 @@ export default defineConfig({ text: "Intent", link: "/docs/advanced-apis/intent", }, + { + text: "WebView", + link: "/docs/advanced-apis/webview", + }, ], }, ], diff --git a/docs/advanced-apis/webview.md b/docs/advanced-apis/webview.md new file mode 100644 index 0000000..873edc8 --- /dev/null +++ b/docs/advanced-apis/webview.md @@ -0,0 +1,169 @@ +# WebView + +The `webview` module exposes Acode's native WebView API. Require it with `acode.require('webview')` to display web content in a fullscreen view or run a headless (hidden) WebView in the background, and communicate with the loaded page over a two-way messaging bridge. + +## Import + +```js +const webview = acode.require('webview'); +``` + +## API Overview + +The module exposes a single method: + +- `create(options)`: Creates a new WebView instance. Resolves to a WebView instance object. + +Each instance exposes these methods: + +- `loadURL(url)`: Loads a URL. Only `http://` and `https://` URLs are allowed; input without a scheme (e.g. `example.com`) is loaded over `https`. +- `loadHTML(html)`: Loads an HTML string directly. +- `evaluate(js)`: Evaluates JavaScript in the page and resolves with the result. +- `postMessage(message)`: Sends a message to the page. Non-string values are JSON-stringified. +- `onMessage(callback)`: Registers a callback for messages sent from the page. +- `offMessage(callback)`: Removes a previously registered message callback. +- `on(event, callback)`: Registers a listener for lifecycle events (see Events). +- `off(event, callback)`: Removes a previously registered event listener. +- `show()`: Shows a fullscreen WebView (or brings it back to the front after `hide()`). +- `hide()`: Moves a fullscreen WebView to the background, keeping its page state intact. +- `reload()`: Reloads the current page. +- `destroy()`: Destroys the instance and releases its resources. **You must call this when the WebView is no longer needed.** + +## Create + +```js +// Fullscreen WebView shown immediately +const view = await webview.create({ + mode: 'fullscreen', + title: 'My Page', +}); +await view.loadURL('https://example.com'); + +// Hidden (headless) WebView running in the background +const headless = await webview.create({ mode: 'hidden' }); +await headless.loadURL('https://example.com'); +const title = await headless.evaluate('document.title'); + +// Fullscreen, but only shown when you call show() +const deferred = await webview.create({ mode: 'fullscreen', visible: false }); +await deferred.loadURL('https://example.com'); +await deferred.show(); +``` + +### WebViewOptions + +Options accepted by `create()`: + +- `mode`: `'fullscreen'` or `'hidden'` (default `'hidden'`). Fullscreen opens the WebView in its own activity; hidden creates a headless WebView that is never displayed. +- `title`: Title shown in the fullscreen activity. +- `allowNavigation`: Boolean, whether the page is allowed to navigate (default `true`). When `false`, all in-page navigation is blocked. +- `allowDownloads`: Boolean, enables downloads (default `false`). Downloads are confirmed with a dialog and saved via the system DownloadManager into the public Downloads directory. +- `visible`: Boolean, whether a fullscreen WebView is shown immediately (default `true`). When `false`, the launch is deferred until `show()` is called. + +### Return Value + +`create()` resolves to an instance object with: + +- `id`: Unique id (e.g. `wv_1a2b3c4d5e6f`). +- Plus the instance methods listed in API Overview. + +## Manage + +```js +// Load content +await view.loadURL('https://example.com'); +await view.loadHTML('

Hello from my plugin

'); + +// Run JavaScript in the page and get the result +const title = await view.evaluate('document.title'); + +// Reload and destroy +await view.reload(); +await view.destroy(); +``` + +> [!Warning] +> Always call `destroy()` once the WebView is no longer required. Every instance holds a native WebView, so leaving instances alive leaks memory and keeps pages (and their scripts/timers/network activity) running in the background. + +> [!Note] +> After an instance is destroyed (via `destroy()` or by the user closing a fullscreen WebView), calling any method on it throws `WebView has been destroyed`. + +## Messaging + +The plugin and the page can exchange messages over a two-way bridge. A `window.webview` object is injected into every loaded page: + +```js +// Inside the page (your HTML or website scripts) +window.webview.onMessage((msg) => { + console.log('from plugin:', msg); +}); +window.webview.postMessage({ hello: 'world' }); +``` + +```js +// In your plugin +view.onMessage((msg) => { + console.log('from page:', msg); // { hello: 'world' } +}); +await view.postMessage({ fromPlugin: true }); +``` + +- Messages can be strings or any JSON-serializable value. JSON payloads are parsed automatically on both sides; anything else arrives as a raw string. +- The bridge is re-injected after every navigation, so `window.webview` is always available to the current page. +- Use `offMessage(callback)` on either side to unsubscribe. + +> [!Note] +> Fullscreen instances create the native WebView lazily, so `postMessage()`, `evaluate()` and `reload()` reject with `WebView is not ready` until a page exists. `loadURL()` and `loadHTML()` called early are queued and applied once the WebView is created. + +## Events + +Listen for lifecycle events with `on(event, callback)`. The callback receives `(event, data)`: + +```js +view.on('pageFinished', (event, data) => { + console.log('Loaded:', data.url, data.title); +}); + +view.on('titleChanged', (event, data) => { + console.log('New title:', data.title); +}); + +view.on('closed', () => { + console.log('WebView was closed'); +}); +``` + +- `pageFinished`: A page finished loading. Data: `{ url, title }`. +- `titleChanged`: The page title changed. Data: `{ title }`. +- `closed`: A fullscreen WebView was closed by the user (back button/task removal). The instance is marked destroyed and further calls on it fail fast. + +Use `off(event, callback)` to remove a listener. + +## Behavior & Lifecycle + +- Modes: `fullscreen` hosts the WebView in its own activity; `hidden` is headless and never displayed, useful for background automation or scraping. +- Back button: In fullscreen mode it navigates back through page history first; when nothing is left, the WebView closes and the `closed` event fires. +- Hide/Show: `hide()` backgrounds the fullscreen activity without destroying it, so `show()` restores it with the page state intact. +- Cleanup: Instances are not tied to your plugin's lifecycle. Destroy every instance you create — ideally in your plugin's `destroy()` function — so hidden WebViews don't outlive the plugin. +- Security: Hosted content is isolated. File and content scheme access is disabled, only `http(s)` URLs can load, and non-http(s) navigation (`file:`, `intent:`, `javascript:`, `tel:`, ...) is always blocked. When `allowNavigation` is `false`, all navigation is blocked. + +## Example: Headless Title Fetcher + +```js +const webview = acode.require('webview'); + +const view = await webview.create({ mode: 'hidden' }); + +view.onMessage((msg) => { + if (msg.type === 'title') { + console.log('Page title:', msg.title); + view.destroy(); + } +}); + +await view.loadHTML(` + +`); +``` From a68f5c34150891ee62543bdb6ea51d54b5808653 Mon Sep 17 00:00:00 2001 From: Rohit Kushvaha Date: Mon, 27 Jul 2026 08:49:49 +0530 Subject: [PATCH 2/2] Update webview.md --- docs/advanced-apis/webview.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/advanced-apis/webview.md b/docs/advanced-apis/webview.md index 873edc8..feba94c 100644 --- a/docs/advanced-apis/webview.md +++ b/docs/advanced-apis/webview.md @@ -29,6 +29,12 @@ Each instance exposes these methods: - `reload()`: Reloads the current page. - `destroy()`: Destroys the instance and releases its resources. **You must call this when the WebView is no longer needed.** +> [!Warning] +> Always call `destroy()` once the WebView is no longer required. Every instance holds a native WebView, so leaving instances alive leaks memory and keeps pages (and their scripts/timers/network activity) running in the background. + +> [!Note] +> After an instance is destroyed (via `destroy()` or by the user closing a fullscreen WebView), calling any method on it throws `WebView has been destroyed`. + ## Create ```js @@ -82,12 +88,6 @@ await view.reload(); await view.destroy(); ``` -> [!Warning] -> Always call `destroy()` once the WebView is no longer required. Every instance holds a native WebView, so leaving instances alive leaks memory and keeps pages (and their scripts/timers/network activity) running in the background. - -> [!Note] -> After an instance is destroyed (via `destroy()` or by the user closing a fullscreen WebView), calling any method on it throws `WebView has been destroyed`. - ## Messaging The plugin and the page can exchange messages over a two-way bridge. A `window.webview` object is injected into every loaded page: