원클릭으로
ui-deck
Start woodglue server, take UI screenshots with Playwright, and build a self-contained HTML presentation deck
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Start woodglue server, take UI screenshots with Playwright, and build a self-contained HTML presentation deck
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | ui_deck |
| description | Start woodglue server, take UI screenshots with Playwright, and build a self-contained HTML presentation deck |
| argument-hint | <what to highlight -- UI functionality, code paths, RPC calls> |
Build a self-contained HTML slide deck showcasing woodglue's UI by starting the server, driving a headless browser, and assembling screenshots into a navigable presentation.
$ARGUMENTS contains an optional prompt describing what to highlight (UI
functionality, code paths, RPC calls, specific features).
If no argument is provided, run git log --oneline -20 and review recent
changes to identify notable functionality worth demonstrating.
Plan the slide sequence before taking any screenshots. Think about narrative arc: start broad (home view), drill into specifics (namespaces, methods, runs, DAGs), and close with any interesting edge cases.
Determine the output path:
{date} from date +%Y-%m-%d{name} -- up to 25 chars summarizing the prompt, lowercase with dashes or
underscores, no spaces. Default: ui-deckdocs/ai/decks/{date}-{name}.htmlThe user can pass --data <dir> and --port <port> in the arguments. Defaults:
--data ./data, --port 5321.
uv run playwright install chromium 2>/dev/null || (uv add --dev playwright && uv run playwright install chromium)
Ensure the output directory exists:
mkdir -p docs/ai/decks
cd src/woodglue/ui && npm install && npx vite build
Start the server as a background process. Note: --data and --port are
top-level flags on wgl, NOT subcommand flags on start:
uv run wgl --data ./data start &
Capture the PID from the background job or read it from data/wgl.pid.
IMPORTANT: Always use 127.0.0.1, never localhost. The server binds
127.0.0.1 only. Using localhost may resolve to IPv6 ::1 first, causing
connection refused or silent auth failures when curl retries on IPv4.
Wait for the server to be ready:
for i in $(seq 1 30); do
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:5321/ui/ | grep -q 200 && break
sleep 0.5
done
If auth is enabled, extract the token programmatically:
uv run python -c "from woodglue.token_store import get_single_token; print(get_single_token('data/auth.db'))"
If the server fails to start, report the error and stop.
Call RPC to discover what namespaces and capabilities are available. If auth
is enabled, include the Bearer token header. Always use 127.0.0.1:
TOKEN="<token from step 3>"
curl -s http://127.0.0.1:5321/rpc \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $TOKEN" \
-d '{"jsonrpc":"2.0","method":"system.list_namespaces","id":1}'
Based on the response, decide which UI states to capture. Consider what namespaces exist and their capabilities (expose_api, run_engine, has_cache).
Before writing the Playwright script, plan which states to capture. Consider:
Write a disposable Python helper script to a temp file and run it with
uv run python <script>.
The script should use Playwright's sync API:
from playwright.sync_api import sync_playwright
import base64, json, os, sys
port = int(sys.argv[1])
output_dir = sys.argv[2]
auth_token = sys.argv[3] if len(sys.argv) > 3 else None
os.makedirs(output_dir, exist_ok=True)
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context(viewport={"width": 1280, "height": 800})
if auth_token:
context.add_cookies([{
"name": "wgl_token",
"value": auth_token,
"domain": "127.0.0.1",
"path": "/",
}])
page = context.new_page()
# For each planned screenshot:
# 1. Navigate or click
# 2. Wait for content to load (networkidle or specific selectors)
# 3. Take screenshot as PNG
# 4. Save to output_dir with sequential naming (01_home.png, etc.)
browser.close()
All screenshots should be PNG at 1280x800 viewport.
After collecting all screenshots, assemble the HTML deck. Read each screenshot file, base64-encode it, and embed it in the HTML.
The HTML deck must be completely self-contained: all images base64-encoded, all CSS and JS inline, no external dependencies.
Layout:
Styling:
Claude decides per-slide what format is most appropriate:
<img> filling the main area, caption text below<pre> block with inline CSS coloring (no
external libraries)When a code snippet or RPC call is relevant to explain a screenshot, read the relevant source file and include the pertinent lines as a code slide or as part of a combined slide.
Write the final HTML to the planned output path (docs/ai/decks/{date}-{name}.html)
using the Write tool.
After writing the deck, update docs/ai/decks/index.md to list all decks in
the directory. Read the existing index (or create it if missing), then rewrite
it with a table row for every *.html file in docs/ai/decks/, sorted by
date descending. Use the same format:
# UI Decks
Self-contained HTML presentations showcasing woodglue UI functionality.
Generated by the `/ui_deck` skill.
| Date | Deck | Description |
|------|------|-------------|
| YYYY-MM-DD | [Title](./filename.html) | One-line description |
kill $(cat data/wgl.pid) or kill by the captured PIDThe woodglue UI stores the auth token in a wgl_token cookie, but sends it as
an Authorization: Bearer header in JS fetch calls. Setting the cookie in
Playwright is sufficient for the SPA at /ui/ (the JS reads the cookie and
adds the header). However, do NOT navigate directly to non-SPA endpoints
like /docs/llms.txt or /docs/openapi.json -- those require the Bearer
header which Playwright can't set on page navigations. Only screenshot pages
under /ui/.
Before writing the Playwright script, read src/woodglue/ui/src/main.js to
find the actual CSS selectors. Key selectors as of v0.0.4:
.ns-card -- namespace cards on home view.run-row[data-run-id] -- clickable run items in run list.graph-node[data-label] -- clickable DAG nodes in graph SVG.io-load-btn -- "Load I/O" button in node detail panel.detail-close -- close button on detail panel (the X)text=<name> -- sidebar method/namespace links (use Playwright text selector)Always verify selectors against the current main.js before scripting. Do NOT
guess selectors like .run-item, .dag-node, .node-box -- these do not
exist.
Always use http://127.0.0.1:<port>, never http://localhost:<port>. The
server binds 127.0.0.1 and localhost may resolve to IPv6 ::1, causing
connection failures or silent auth mismatches.