app.webview - WebView API
Displays web pages or HTML content in a standalone window with JavaScript interaction support.
Permission: Requires
webviewdeclared in the manifest (L2 sensitive)
Methods
app.webview.open(config)
Opens a WebView window.
Parameters:
config(table):url(string, optional) - URL to load (mutually exclusive withhtml)html(string, optional) - HTML content to display (mutually exclusive withurl)title(string, optional) - Window titlewidth(number, optional) - Window width (default 800)height(number, optional) - Window height (default 600)x(number, optional) - Window X positiony(number, optional) - Window Y positionresizable(boolean, optional) - Whether the window is resizable (default true)closable(boolean, optional) - Whether the window is closable (default true)onClose(function, optional) - Window close callbackfunction(id)
Returns: string, error - WebView ID
-- Load a URL
local id, err = app.webview.open({
url = "https://example.com",
title = "Preview",
width = 1024,
height = 768
})
-- Load HTML
local id, err = app.webview.open({
html = "<h1>Hello</h1><p>This is a webview.</p>",
title = "Custom Page",
width = 400,
height = 300
})
-- With close callback
local id, err = app.webview.open({
url = "https://example.com",
onClose = function(id)
app.log.info("WebView closed: " .. id)
end
})
app.webview.close(id)
Closes the specified WebView window.
Parameters:
id(string) - WebView ID
Returns: boolean, error
local ok, err = app.webview.close(webviewId)
app.webview.closeAll()
Closes all WebView windows.
Returns: boolean
app.webview.closeAll()
app.webview.eval(id, js)
Executes JavaScript code in the WebView.
Parameters:
id(string) - WebView IDjs(string) - JavaScript code
Returns: string|nil, error - JavaScript execution result (as a JSON string)
-- Get the page title
local title, err = app.webview.eval(id, "document.title")
-- Get form data
local data, err = app.webview.eval(id, [[
JSON.stringify({
name: document.getElementById('name').value,
email: document.getElementById('email').value
})
]])
if data then
local result = app.json.parse(data)
end
-- Modify page content
app.webview.eval(id, "document.body.style.backgroundColor = '#f0f0f0'")
app.webview.loadURL(id, url)
Loads a new URL in an existing WebView.
Parameters:
id(string) - WebView IDurl(string) - URL to load
Returns: boolean, error
local ok, err = app.webview.loadURL(id, "https://other-page.com")
app.webview.loadHTML(id, html)
Loads new HTML content in an existing WebView.
Parameters:
id(string) - WebView IDhtml(string) - HTML content
Returns: boolean, error
local ok, err = app.webview.loadHTML(id, "<h1>Updated Content</h1>")
Examples
Markdown preview
function MyPlugin:handlePreview(context)
local file = context.selectedFiles[1]
if not file then return end
local content = app.file.read(file)
if not content then return end
local html = string.format([[
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: -apple-system; padding: 20px; max-width: 800px; margin: 0 auto; }
pre { background: #f5f5f5; padding: 12px; border-radius: 6px; overflow-x: auto; }
code { font-family: 'SF Mono', monospace; }
</style>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
</head>
<body>
<div id="content"></div>
<script>
document.getElementById('content').innerHTML = marked.parse(%s);
</script>
</body>
</html>
]], app.json.stringify(content))
app.webview.open({
html = html,
title = app.path.basename(file),
width = 900,
height = 700
})
end
Custom settings UI
function MyPlugin:handleSettings(context)
local settings = app.settings.getAll()
local html = string.format([[
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: -apple-system; padding: 20px; }
.field { margin: 12px 0; }
label { display: block; margin-bottom: 4px; font-weight: 600; }
input, select { width: 100%%; padding: 6px; border: 1px solid #ccc; border-radius: 4px; }
button { padding: 8px 16px; background: #007AFF; color: white; border: none; border-radius: 6px; cursor: pointer; }
</style>
</head>
<body>
<h2>Plugin Settings</h2>
<div class="field">
<label>API Key</label>
<input id="apiKey" value="%s">
</div>
<div class="field">
<label>Output Format</label>
<select id="format">
<option value="json">JSON</option>
<option value="csv">CSV</option>
</select>
</div>
<button onclick="save()">Save</button>
<script>
function save() {
window.__result = JSON.stringify({
apiKey: document.getElementById('apiKey').value,
format: document.getElementById('format').value
});
}
</script>
</body>
</html>
]], settings.apiKey or "")
local id, err = app.webview.open({
html = html,
title = "Settings",
width = 400,
height = 300,
resizable = false,
onClose = function(wvId)
local result = app.webview.eval(wvId, "window.__result || null")
if result and result ~= "null" then
local data = app.json.parse(result)
if data then
app.settings.set("apiKey", data.apiKey)
app.settings.set("format", data.format)
end
end
end
})
end
Data visualization
function MyPlugin:handleVisualize(context)
local files = context.selectedFiles
local sizes = {}
for _, file in ipairs(files) do
table.insert(sizes, {
name = app.path.basename(file),
size = app.file.size(file) or 0
})
end
local html = string.format([[
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>body { padding: 20px; }</style>
</head>
<body>
<canvas id="chart"></canvas>
<script>
var data = %s;
new Chart(document.getElementById('chart'), {
type: 'bar',
data: {
labels: data.map(d => d.name),
datasets: [{
label: 'File Size (bytes)',
data: data.map(d => d.size),
backgroundColor: '#007AFF'
}]
}
});
</script>
</body>
</html>
]], app.json.stringify(sizes))
app.webview.open({
html = html,
title = "File Size Analysis",
width = 800,
height = 500
})
end
Notes
- Window limit: Each plugin can have up to 5 WebView windows
- JavaScript results:
eval()returns results as JSON strings - Close callback:
onCloseis automatically triggered when the window is closed - Resource cleanup: All WebView windows are automatically closed when the plugin is unloaded
- Network access: WebView can load external URLs, subject to App Transport Security restrictions