Use this skill when a task needs to run Lynx bundles with the node-lynx CLI, capture screenshots, open a macOS preview window, use DebugRouter OpenCard, inspect rendered content through CDP, or integrate node-lynx through JavaScript or TypeScript APIs.
70
85%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Use node-lynx as a local Lynx runtime for screenshots, preview windows, smoke tests, DebugRouter OpenCard sessions, and automation that needs to inspect or interact with a rendered Lynx page from Node.js.
Default sample template URL:
https://lynxjs.org/lynx-examples/gallery/dist/GalleryComplete.lynx.bundlePrefer the CLI for one-off screenshots, quick template validation, and simple preview sessions.
Show available options:
node-lynx --helpCapture a headless screenshot and exit:
node-lynx render \
"https://lynxjs.org/lynx-examples/gallery/dist/GalleryComplete.lynx.bundle" \
--width 268 \
--height 469 \
--dpr 2 \
--output ./gallery.png \
--timeout 30000 \
--screenshot-delay 500 \
--no-debug-routerRender a local bundle:
node-lynx render \
--template ./dist/main/template.js \
--width 390 \
--height 844 \
--output ./node-lynx-local.png \
--no-debug-routerOpen a visible preview window on macOS:
node-lynx preview \
"https://lynxjs.org/lynx-examples/gallery/dist/GalleryComplete.lynx.bundle" \
--width 268 \
--height 469 \
--dpr 2 \
--title "Node Lynx Preview"Start a DebugRouter-backed session without an initial template:
node-lynx renderCLI rules:
render for headless screenshots and automation.preview only on macOS; it creates an AppKit window.--template <path> for local bundles and --url <url> for remote
bundles. A positional http:// or https:// input is treated as a remote
bundle URL.--width and --height as CSS pixel viewport values.--dpr or --device-pixel-ratio to control output scale; the PNG pixel
size is width * dpr by height * dpr.--timeout <ms> for bundle download, template load, CDP calls, and first
frame submission timeouts.--screenshot-delay <ms> to wait after the first frame before capture.
This defaults to 100; use a larger delay for image-heavy or network-heavy
pages.--no-debug-router for CI or one-shot screenshot commands that should
exit immediately after writing the PNG.--no-debug-router when you need DebugRouter/OpenCard. Without an
initial template, render waits for DebugRouter OpenCard. preview also
waits for OpenCard without creating an initial window.Prefer the API when the task needs programmatic setup, repeated captures, global props, CDP calls, input simulation, or custom lifecycle handling.
Import from the node-lynx package declared by the target project. For public npm usage this is typically:
import { HeadlessLynxView } from '@lynx-js/node-lynx';Render a remote template to PNG:
import { writeFile } from 'node:fs/promises';
import { HeadlessLynxView } from '@lynx-js/node-lynx';
const templateUrl =
'https://lynxjs.org/lynx-examples/gallery/dist/GalleryComplete.lynx.bundle';
const view = new HeadlessLynxView({
width: 268,
height: 469,
devicePixelRatio: 2,
timeoutMs: 30000,
});
try {
await view.loadTemplateFromUrl(templateUrl, {
initialData: { source: 'node-lynx' },
globalProps: { theme: 'light' },
});
const png = await view.screenshot({ settleMs: 500 });
await writeFile('./gallery.png', png);
} finally {
view.destroy();
}Render a local bundle buffer. Always pass a file URL so relative resources have the right base URL:
import { readFile, writeFile } from 'node:fs/promises';
import { resolve } from 'node:path';
import { pathToFileURL } from 'node:url';
import { HeadlessLynxView } from '@lynx-js/node-lynx';
const templatePath = resolve('./dist/main/template.js');
const view = new HeadlessLynxView({ width: 390, height: 844 });
try {
await view.loadTemplate(await readFile(templatePath), {
url: pathToFileURL(templatePath).href,
});
await writeFile('./node-lynx-local.png', await view.screenshot());
} finally {
view.destroy();
}Inspect the DOM through CDP:
const responseText = await view.invokeCDPFromSDK(
JSON.stringify({
id: 1,
method: 'DOM.getDocument',
params: { depth: -1, pierce: true },
})
);
const response = JSON.parse(responseText);Update runtime data after load:
view.updateData({ selected: true });
view.updateGlobalProps({ locale: 'en-US' });
await view.waitForFrame();Use WindowedLynxView only on macOS when a visible app window is required.
import { WindowedLynxView } from '@lynx-js/node-lynx';
const view = new WindowedLynxView({
width: 268,
height: 469,
devicePixelRatio: 2,
title: 'Node Lynx Preview',
});
try {
await view.loadTemplateFromUrl(
'https://lynxjs.org/lynx-examples/gallery/dist/GalleryComplete.lynx.bundle'
);
await view.waitForFrame();
view.click(20, 70);
view.typeText('hello from node-lynx');
view.pressKey('Enter');
await view.waitUntilClosed();
} finally {
view.destroy();
}Interaction APIs use CSS pixel coordinates. Supported pressKey values are
Backspace, Delete, Enter, ArrowLeft, ArrowRight, ArrowUp, and
ArrowDown.
Use OpenCard managers when the page should be opened by DebugRouter instead of an initial CLI/API template load.
import {
HeadlessOpenCardManager,
LynxEnv,
WindowedOpenCardManager,
} from '@lynx-js/node-lynx';
LynxEnv.init();
LynxEnv.setAppInfo(['App', 'AppVersion'], ['NodeLynxSkill', '1.0.0']);
const Manager =
process.platform === 'darwin' ? WindowedOpenCardManager : HeadlessOpenCardManager;
const manager = new Manager({
view: { width: 268, height: 469, devicePixelRatio: 2, timeoutMs: 30000 },
onCardLoaded(card) {
console.log(`opened ${card.url}`);
},
onCardError(error, card) {
console.error(`failed ${card.url}: ${error.message}`);
},
});
manager.install();
process.once('SIGINT', () => {
manager.dispose();
process.exit(0);
});waitForFrame() as "a frame was submitted", not as proof that every
image or network resource has decoded.screenshot({ settleMs }) or CLI --screenshot-delay for pragmatic
post-frame settling. Prefer a page-level ready signal when the page exposes
one.destroy() in a finally block for each view.c17426b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.