From 7163961738652b4cf17e0ecff9a36ccb0610b621 Mon Sep 17 00:00:00 2001 From: robertsLando Date: Thu, 5 Mar 2026 18:10:22 +0100 Subject: [PATCH 1/3] feat: add Playwright screenshot tooling for Claude Code Add browser screenshot automation so Claude Code can visually verify UI changes, attach screenshots to PRs, and update documentation images. - Add Playwright MCP server config (.mcp.json) for interactive browser control - Add screenshot skill with reusable template script - Supports dark mode, auth handling, configurable viewport - Includes route-to-screenshot mapping for documentation updates Co-Authored-By: Claude Opus 4.6 --- .../skills/screenshot/screenshot-template.js | 109 ++++++++++++++++++ .claude/skills/screenshot/skill.md | 82 +++++++++++++ .mcp.json | 13 +++ 3 files changed, 204 insertions(+) create mode 100644 .claude/skills/screenshot/screenshot-template.js create mode 100644 .claude/skills/screenshot/skill.md create mode 100644 .mcp.json diff --git a/.claude/skills/screenshot/screenshot-template.js b/.claude/skills/screenshot/screenshot-template.js new file mode 100644 index 0000000000..4e9f418497 --- /dev/null +++ b/.claude/skills/screenshot/screenshot-template.js @@ -0,0 +1,109 @@ +#!/usr/bin/env node + +/** + * Screenshot template for zwave-js-ui + * + * Usage: node screenshot-template.js [width] [height] + * + * Examples: + * node screenshot-template.js "http://localhost:8092/#/settings" /tmp/screenshots/settings.png + * node screenshot-template.js "http://localhost:8092/#/mesh" /tmp/screenshots/mesh.png 1920 1080 + */ + +const { chromium } = require('playwright') +const path = require('path') +const fs = require('fs') + +const url = process.argv[2] +const outputPath = process.argv[3] +const viewportWidth = parseInt(process.argv[4]) || 1280 +const viewportHeight = parseInt(process.argv[5]) || 720 + +if (!url || !outputPath) { + console.error( + 'Usage: node screenshot-template.js [width] [height]', + ) + process.exit(1) +} + +// Check if auth is enabled by reading store/settings.json +function isAuthEnabled() { + const cwdSettingsPath = path.resolve(process.cwd(), 'store/settings.json') + const scriptSettingsPath = path.resolve( + __dirname, + '../../../store/settings.json', + ) + + for (const p of [cwdSettingsPath, scriptSettingsPath]) { + try { + const settings = JSON.parse(fs.readFileSync(p, 'utf8')) + return settings?.gateway?.authEnabled === true + } catch { + // continue to next path + } + } + return false +} + +async function takeScreenshot() { + const authEnabled = isAuthEnabled() + const outputDir = path.dirname(outputPath) + fs.mkdirSync(outputDir, { recursive: true }) + + const browser = await chromium.launch({ headless: true }) + const context = await browser.newContext({ + viewport: { width: viewportWidth, height: viewportHeight }, + }) + + // Set dark mode in localStorage before navigation + await context.addInitScript(() => { + localStorage.setItem('dark', 'true') + }) + + const page = await context.newPage() + + if (authEnabled) { + // Navigate to the app root first — router will redirect to login + await page.goto('http://localhost:8092/', { + waitUntil: 'networkidle', + }) + await page.waitForTimeout(1000) + + // Fill login form + try { + await page.fill( + 'input[type="text"], input[name="username"]', + 'admin', + { timeout: 5000 }, + ) + await page.fill('input[type="password"]', 'zwave', { + timeout: 5000, + }) + await page.click('button[type="submit"]', { timeout: 5000 }) + await page.waitForTimeout(2000) + } catch (e) { + console.warn( + 'Login form not found or login failed, continuing...', + e.message, + ) + } + } + + // Navigate to the target URL + await page.goto(url, { waitUntil: 'networkidle' }) + + // Wait for Vuetify rendering + await page.waitForTimeout(2000) + + await page.screenshot({ path: outputPath, fullPage: false }) + console.log( + `Screenshot saved to ${outputPath} (${viewportWidth}x${viewportHeight})`, + ) + + await browser.close() +} + +takeScreenshot().catch((err) => { + console.error('Screenshot failed:', err) + process.exit(1) +}) diff --git a/.claude/skills/screenshot/skill.md b/.claude/skills/screenshot/skill.md new file mode 100644 index 0000000000..59bfa0ef24 --- /dev/null +++ b/.claude/skills/screenshot/skill.md @@ -0,0 +1,82 @@ +# Screenshot Skill + +Take browser screenshots of the running zwave-js-ui dev server using Playwright. + +## Invocation + +User says: `/screenshot` + +## Prerequisites + +Dev servers must be running: +- Frontend: `npm run dev` (port 8092) +- Backend: `npm run dev:server` (port 8091) +- Fake stick: `npm run fake-stick` (port 5555) + +## How to Take Screenshots + +Run the screenshot template script: + +```bash +node .claude/skills/screenshot/screenshot-template.js [width] [height] +``` + +Examples: +```bash +# Single page screenshot +node .claude/skills/screenshot/screenshot-template.js "http://localhost:8092/#/settings" /tmp/screenshots/settings.png + +# Custom viewport +node .claude/skills/screenshot/screenshot-template.js "http://localhost:8092/#/mesh" /tmp/screenshots/mesh.png 1920 1080 +``` + +## Configuration + +- **Default viewport:** 1280x720 +- **Dark mode:** Enabled by default via localStorage +- **Wait strategy:** `networkidle` + 2s delay for Vuetify rendering +- **Hash routing:** All routes use `http://localhost:8092/#/` + +## Auth Handling + +The script checks `store/settings.json` for `gateway.authEnabled`: +- If `true`: automatically logs in with `admin` / `zwave` credentials +- If `false` or unset: auth is bypassed, navigates directly + +## Output Locations + +- **Ad-hoc screenshots:** `/tmp/screenshots/` +- **Documentation updates:** `docs/_images/` in the project root + +## Route-to-Screenshot Mapping (Documentation) + +| File | Route | Notes | +|------|-------|-------| +| `control_panel_dark.png` | `/#/control-panel` | Dark mode | +| `settings.png` | `/#/settings` | Light mode override | +| `settings_dark.png` | `/#/settings` | Dark mode | +| `debug.png` | `/#/debug` | Needs backend | +| `mesh_diagram.png` | `/#/mesh` | Needs backend + fake-stick | +| `mesh-selected.png` | `/#/mesh` | With a node selected | +| `scenes.png` | `/#/scenes` | | +| `nodes_manager.png` | `/#/control-panel` | Nodes table view | +| `unknown-device.png` | `/#/control-panel` | Specific unknown node | +| `hass_devices.png` | `/#/settings` | Home Assistant tab | +| `gateway_values_table.png` | `/#/settings` | Gateway values section | + +## Workflows + +### Ad-hoc PR Screenshots +1. Take screenshot to `/tmp/screenshots/` +2. Reference in PR comment or attach via GitHub + +### Documentation Screenshot Updates +1. Take screenshots for each route in the mapping table +2. Save directly to `docs/_images/` overwriting existing files +3. Stage and commit: `git add docs/_images/ && git commit -m "docs: update UI screenshots"` + +## Troubleshooting + +- If pages show login screen, check `store/settings.json` auth config +- If pages are blank, ensure all three dev servers are running +- If Vuetify components aren't rendered, increase the wait delay diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000000..899cfa04e6 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,13 @@ +{ + "mcpServers": { + "playwright": { + "command": "npx", + "args": [ + "@playwright/mcp@latest", + "--headless", + "--viewport-size", + "1280,720" + ] + } + } +} From 7c37b84dbca8d1c0dcc54c508a69440f8bce7b35 Mon Sep 17 00:00:00 2001 From: robertsLando Date: Thu, 5 Mar 2026 18:12:17 +0100 Subject: [PATCH 2/3] refactor: rename screenshot skill to ui-browser, expand scope Rename .claude/skills/screenshot/ to .claude/skills/ui-browser/ and rewrite skill.md as a comprehensive UI browsing guide. Now covers: - Interactive browsing via Playwright MCP (navigation, inspection, interaction) - Full route reference with descriptions - App layout structure (nav drawer, app bar, content area) - Auth flow details with exact selectors - Common verification patterns (component edits, layout changes, dialogs) - Batch screenshot template (kept as sub-tool) - Troubleshooting guide Co-Authored-By: Claude Opus 4.6 --- .claude/skills/screenshot/skill.md | 82 ---------- .../screenshot-template.js | 0 .claude/skills/ui-browser/skill.md | 143 ++++++++++++++++++ 3 files changed, 143 insertions(+), 82 deletions(-) delete mode 100644 .claude/skills/screenshot/skill.md rename .claude/skills/{screenshot => ui-browser}/screenshot-template.js (100%) create mode 100644 .claude/skills/ui-browser/skill.md diff --git a/.claude/skills/screenshot/skill.md b/.claude/skills/screenshot/skill.md deleted file mode 100644 index 59bfa0ef24..0000000000 --- a/.claude/skills/screenshot/skill.md +++ /dev/null @@ -1,82 +0,0 @@ -# Screenshot Skill - -Take browser screenshots of the running zwave-js-ui dev server using Playwright. - -## Invocation - -User says: `/screenshot` - -## Prerequisites - -Dev servers must be running: -- Frontend: `npm run dev` (port 8092) -- Backend: `npm run dev:server` (port 8091) -- Fake stick: `npm run fake-stick` (port 5555) - -## How to Take Screenshots - -Run the screenshot template script: - -```bash -node .claude/skills/screenshot/screenshot-template.js [width] [height] -``` - -Examples: -```bash -# Single page screenshot -node .claude/skills/screenshot/screenshot-template.js "http://localhost:8092/#/settings" /tmp/screenshots/settings.png - -# Custom viewport -node .claude/skills/screenshot/screenshot-template.js "http://localhost:8092/#/mesh" /tmp/screenshots/mesh.png 1920 1080 -``` - -## Configuration - -- **Default viewport:** 1280x720 -- **Dark mode:** Enabled by default via localStorage -- **Wait strategy:** `networkidle` + 2s delay for Vuetify rendering -- **Hash routing:** All routes use `http://localhost:8092/#/` - -## Auth Handling - -The script checks `store/settings.json` for `gateway.authEnabled`: -- If `true`: automatically logs in with `admin` / `zwave` credentials -- If `false` or unset: auth is bypassed, navigates directly - -## Output Locations - -- **Ad-hoc screenshots:** `/tmp/screenshots/` -- **Documentation updates:** `docs/_images/` in the project root - -## Route-to-Screenshot Mapping (Documentation) - -| File | Route | Notes | -|------|-------|-------| -| `control_panel_dark.png` | `/#/control-panel` | Dark mode | -| `settings.png` | `/#/settings` | Light mode override | -| `settings_dark.png` | `/#/settings` | Dark mode | -| `debug.png` | `/#/debug` | Needs backend | -| `mesh_diagram.png` | `/#/mesh` | Needs backend + fake-stick | -| `mesh-selected.png` | `/#/mesh` | With a node selected | -| `scenes.png` | `/#/scenes` | | -| `nodes_manager.png` | `/#/control-panel` | Nodes table view | -| `unknown-device.png` | `/#/control-panel` | Specific unknown node | -| `hass_devices.png` | `/#/settings` | Home Assistant tab | -| `gateway_values_table.png` | `/#/settings` | Gateway values section | - -## Workflows - -### Ad-hoc PR Screenshots -1. Take screenshot to `/tmp/screenshots/` -2. Reference in PR comment or attach via GitHub - -### Documentation Screenshot Updates -1. Take screenshots for each route in the mapping table -2. Save directly to `docs/_images/` overwriting existing files -3. Stage and commit: `git add docs/_images/ && git commit -m "docs: update UI screenshots"` - -## Troubleshooting - -- If pages show login screen, check `store/settings.json` auth config -- If pages are blank, ensure all three dev servers are running -- If Vuetify components aren't rendered, increase the wait delay diff --git a/.claude/skills/screenshot/screenshot-template.js b/.claude/skills/ui-browser/screenshot-template.js similarity index 100% rename from .claude/skills/screenshot/screenshot-template.js rename to .claude/skills/ui-browser/screenshot-template.js diff --git a/.claude/skills/ui-browser/skill.md b/.claude/skills/ui-browser/skill.md new file mode 100644 index 0000000000..93d25aa513 --- /dev/null +++ b/.claude/skills/ui-browser/skill.md @@ -0,0 +1,143 @@ +# UI Browser Skill + +Browse, inspect, and screenshot the running zwave-js-ui dev server using Playwright MCP tools. + +Use this skill after making UI changes to visually verify they look correct, debug layout issues, inspect component state, or capture screenshots for PRs and documentation. + +## Prerequisites + +Dev servers must be running: +- Frontend: `npm run dev` (port 8092) +- Backend: `npm run dev:server` (port 8091) +- Fake stick: `npm run fake-stick` (port 5555) + +The Playwright MCP server must be configured (see `.mcp.json` in project root). + +## App Structure + +### Routing + +The app uses **Vue Router with hash history**. All URLs follow this pattern: +``` +http://localhost:8092/#/ +``` + +### Available Routes + +| Route | Page | Description | +|-------|------|-------------| +| `/` | Login | Auth page (only shown when auth is enabled) | +| `/control-panel` | Control Panel | Main page — node list + node details | +| `/settings` | Settings | App configuration, gateway values, Home Assistant | +| `/scenes` | Scenes | Scene management | +| `/debug` | Debug | Log viewer and debug console | +| `/store` | Store | MQTT/gateway store explorer | +| `/mesh` | Network Graph | Z-Wave mesh network visualization | +| `/smart-start` | Smart Start | Smart Start provisioning list | +| `/controller-chart` | Controller Chart | Controller statistics | +| `/zniffer` | Zniffer | Z-Wave traffic sniffer | +| `/configuration-templates` | Config Templates | Shared device configuration templates | + +### Layout + +- **Navigation drawer** (left sidebar): `v-navigation-drawer` — toggled via hamburger icon in app bar +- **App bar** (top): `v-app-bar` — contains nav toggle, page title, action buttons +- **Main content**: below app bar, fills remaining space + +### Auth Handling + +Auth is controlled by `store/settings.json` → `gateway.authEnabled`: +- **When `false` or unset**: No login needed. Navigating to `/` redirects to `/control-panel`. +- **When `true`**: All routes except `/` require login. The login form has: + - Username field: `input[name="username"]` + - Password field: `input[name="password"]` + - Submit button: `button[type="submit"]` (inside `#login-form`) + - Default credentials: `admin` / `zwave` + +## Using Playwright MCP for Interactive Browsing + +The Playwright MCP server gives you these capabilities: + +### Navigation +1. **Navigate** to any route: use the browser navigate tool with `http://localhost:8092/#/` +2. **Wait** for content: Vuetify components need ~2s after `networkidle` to fully render +3. **Go back/forward**: use browser navigation tools + +### Inspection +1. **Take a snapshot** (accessibility tree): see the page structure, text content, and interactive elements without a screenshot +2. **Take a screenshot**: capture visual state for verification or PR attachment +3. **Get element text**: read content from specific elements + +### Interaction +1. **Click** elements: buttons, tabs, list items, navigation links +2. **Fill** form fields: text inputs, selectors +3. **Select** dropdown options +4. **Hover** for tooltips or dropdown menus + +### Common Verification Patterns + +**After editing a Vue component:** +1. Navigate to the page containing that component +2. Take a screenshot to verify the visual result +3. If something looks off, take a snapshot to inspect the DOM structure + +**After changing styles/layout:** +1. Navigate to the affected page +2. Screenshot at default viewport (1280x720) +3. Optionally test responsive: resize viewport and screenshot again + +**After modifying a dialog/modal:** +1. Navigate to the page +2. Click the button/action that opens the dialog +3. Screenshot the dialog in its open state + +**Verifying dark mode:** +1. The app respects `localStorage.dark` — set it via the browser console or use the theme toggle in the UI + +## Batch Screenshots + +For capturing multiple pages at once (e.g., updating documentation), use the template script: + +```bash +node .claude/skills/ui-browser/screenshot-template.js [width] [height] +``` + +Examples: +```bash +# Default 1280x720, dark mode +node .claude/skills/ui-browser/screenshot-template.js "http://localhost:8092/#/settings" /tmp/screenshots/settings.png + +# Custom viewport +node .claude/skills/ui-browser/screenshot-template.js "http://localhost:8092/#/mesh" /tmp/screenshots/mesh.png 1920 1080 +``` + +The script automatically handles auth and sets dark mode via localStorage. + +### Output Locations + +- **Ad-hoc screenshots:** `/tmp/screenshots/` +- **Documentation updates:** `docs/_images/` in the project root + +### Route-to-Documentation Mapping + +| File | Route | Notes | +|------|-------|-------| +| `control_panel_dark.png` | `/#/control-panel` | Dark mode | +| `settings.png` | `/#/settings` | Light mode override | +| `settings_dark.png` | `/#/settings` | Dark mode | +| `debug.png` | `/#/debug` | Needs backend | +| `mesh_diagram.png` | `/#/mesh` | Needs backend + fake-stick | +| `mesh-selected.png` | `/#/mesh` | With a node selected | +| `scenes.png` | `/#/scenes` | | +| `nodes_manager.png` | `/#/control-panel` | Nodes table view | +| `unknown-device.png` | `/#/control-panel` | Specific unknown node | +| `hass_devices.png` | `/#/settings` | Home Assistant tab | +| `gateway_values_table.png` | `/#/settings` | Gateway values section | + +## Troubleshooting + +- **Login screen keeps appearing**: Check `store/settings.json` for `gateway.authEnabled`. If true, log in first. +- **Blank page**: Ensure all three dev servers are running. Check browser console for errors via snapshot. +- **Components not rendered**: Vuetify needs time to mount. Wait 2-3s after navigation before taking screenshots. +- **Stale content**: The app uses Socket.IO for real-time updates. If data looks stale, the backend may not be connected to the fake stick. +- **Navigation drawer missing**: It may be collapsed. Click the hamburger icon (`v-app-bar-nav-icon`) in the top-left to toggle it. From 8f6ff97a5ac95cd4ab0d2197601d3d9e8719190f Mon Sep 17 00:00:00 2001 From: robertsLando Date: Thu, 5 Mar 2026 18:17:22 +0100 Subject: [PATCH 3/3] fix: align skill with official Claude Code skill format - Rename skill.md to SKILL.md (required uppercase convention) - Add YAML frontmatter with name, description (third-person trigger phrases), and version fields - Rewrite body in imperative form instead of second person - Move route-to-doc mapping and troubleshooting to references/screenshots.md for progressive disclosure (keep SKILL.md lean, load details as needed) Co-Authored-By: Claude Opus 4.6 --- .claude/skills/ui-browser/SKILL.md | 119 +++++++++++++++ .../ui-browser/references/screenshots.md | 38 +++++ .claude/skills/ui-browser/skill.md | 143 ------------------ 3 files changed, 157 insertions(+), 143 deletions(-) create mode 100644 .claude/skills/ui-browser/SKILL.md create mode 100644 .claude/skills/ui-browser/references/screenshots.md delete mode 100644 .claude/skills/ui-browser/skill.md diff --git a/.claude/skills/ui-browser/SKILL.md b/.claude/skills/ui-browser/SKILL.md new file mode 100644 index 0000000000..95d9f99695 --- /dev/null +++ b/.claude/skills/ui-browser/SKILL.md @@ -0,0 +1,119 @@ +--- +name: UI Browser +description: > + This skill should be used when the user asks to "verify UI changes", + "check the UI", "take a screenshot", "browse the app", "inspect a page", + "test how it looks", "visually verify", "screenshot for PR", or + "update documentation screenshots". Also use after editing any Vue component, + Vuetify template, CSS, or layout code to confirm the result visually. +version: 0.1.0 +--- + +# UI Browser + +Browse, inspect, and screenshot the running zwave-js-ui dev server using Playwright MCP tools. Verify UI changes visually after editing components, debug layout issues interactively, and capture screenshots for PRs and documentation. + +## Prerequisites + +Ensure the dev servers are running before browsing: +- Frontend: `npm run dev` (port 8092) +- Backend: `npm run dev:server` (port 8091) +- Fake stick: `npm run fake-stick` (port 5555) + +The Playwright MCP server is configured in `.mcp.json` at the project root. + +## App Navigation + +The app uses Vue Router with hash history. All URLs follow this pattern: +``` +http://localhost:8092/#/ +``` + +### Route Quick Reference + +| Route | Page | +|-------|------| +| `/control-panel` | Control Panel (main page, node list + details) | +| `/settings` | Settings (config, gateway values, Home Assistant) | +| `/scenes` | Scenes | +| `/debug` | Debug (log viewer) | +| `/store` | Store (MQTT/gateway explorer) | +| `/mesh` | Network Graph (mesh visualization) | +| `/smart-start` | Smart Start | +| `/controller-chart` | Controller Chart | +| `/zniffer` | Zniffer (traffic sniffer) | +| `/configuration-templates` | Configuration Templates | + +### Layout Structure + +- **Navigation drawer** (left sidebar): `v-navigation-drawer` — toggle via hamburger icon +- **App bar** (top): `v-app-bar` — nav toggle, page title, action buttons +- **Main content**: below app bar, fills remaining viewport + +### Auth Handling + +Auth is controlled by `store/settings.json` → `gateway.authEnabled`: +- **When `false` or unset**: no login needed, `/` redirects to `/control-panel` +- **When `true`**: login required. Fill `input[name="username"]` with `admin`, `input[name="password"]` with `zwave`, click `button[type="submit"]` + +## Interactive Browsing with Playwright MCP + +### Navigate to a Page + +Use the browser navigate tool with `http://localhost:8092/#/`. Wait 2-3 seconds after navigation for Vuetify components to fully render. + +### Inspect Page Content + +Take a **snapshot** (accessibility tree) to see page structure, text, and interactive elements without a visual screenshot. Use this to find selectors, verify text content, or debug missing elements. + +### Capture Screenshots + +Take a **screenshot** to capture the visual state. Save to `/tmp/screenshots/` for ad-hoc use or `docs/_images/` for documentation updates. + +### Interact with Elements + +- **Click** buttons, tabs, list items, navigation links +- **Fill** text inputs and form fields +- **Select** dropdown options +- **Hover** to trigger tooltips or dropdown menus + +## Verification Workflows + +### After Editing a Vue Component + +1. Navigate to the page containing the component +2. Take a screenshot to verify the visual result +3. If something looks off, take a snapshot to inspect the DOM structure + +### After Changing Styles or Layout + +1. Navigate to the affected page +2. Screenshot at default viewport (1280x720) +3. Optionally resize the viewport and screenshot again to test responsive behavior + +### After Modifying a Dialog or Modal + +1. Navigate to the page +2. Click the button or action that opens the dialog +3. Screenshot the dialog in its open state + +### Verifying Dark Mode + +The app reads `localStorage.dark`. Set it via the browser console or toggle the theme switch in the UI. The batch screenshot script sets dark mode by default. + +## Batch Screenshots + +For capturing multiple pages at once, run the template script: + +```bash +node .claude/skills/ui-browser/screenshot-template.js [width] [height] +``` + +Default viewport is 1280x720. The script auto-handles auth and sets dark mode. + +```bash +node .claude/skills/ui-browser/screenshot-template.js "http://localhost:8092/#/settings" /tmp/screenshots/settings.png +node .claude/skills/ui-browser/screenshot-template.js "http://localhost:8092/#/mesh" /tmp/screenshots/mesh.png 1920 1080 +``` + +For the full route-to-documentation image mapping and troubleshooting tips, see `references/screenshots.md`. diff --git a/.claude/skills/ui-browser/references/screenshots.md b/.claude/skills/ui-browser/references/screenshots.md new file mode 100644 index 0000000000..cedaa46b84 --- /dev/null +++ b/.claude/skills/ui-browser/references/screenshots.md @@ -0,0 +1,38 @@ +# Screenshot Reference + +## Output Locations + +- **Ad-hoc screenshots:** `/tmp/screenshots/` +- **Documentation updates:** `docs/_images/` in the project root + +## Route-to-Documentation Mapping + +These files in `docs/_images/` correspond to specific routes and states: + +| File | Route | Notes | +|------|-------|-------| +| `control_panel_dark.png` | `/#/control-panel` | Dark mode | +| `settings.png` | `/#/settings` | Light mode override | +| `settings_dark.png` | `/#/settings` | Dark mode | +| `debug.png` | `/#/debug` | Needs backend | +| `mesh_diagram.png` | `/#/mesh` | Needs backend + fake-stick | +| `mesh-selected.png` | `/#/mesh` | With a node selected | +| `scenes.png` | `/#/scenes` | | +| `nodes_manager.png` | `/#/control-panel` | Nodes table view | +| `unknown-device.png` | `/#/control-panel` | Specific unknown node | +| `hass_devices.png` | `/#/settings` | Home Assistant tab | +| `gateway_values_table.png` | `/#/settings` | Gateway values section | + +## Documentation Update Workflow + +1. Take screenshots for each route in the mapping table above +2. Save directly to `docs/_images/` overwriting existing files +3. Stage and commit: `git add docs/_images/ && git commit -m "docs: update UI screenshots"` + +## Troubleshooting + +- **Login screen keeps appearing**: Check `store/settings.json` for `gateway.authEnabled`. If true, handle login first via Playwright MCP or let the batch script handle it automatically. +- **Blank page**: Ensure all three dev servers are running (frontend, backend, fake-stick). Take a snapshot to check for error messages in the DOM. +- **Components not rendered**: Vuetify needs 2-3s after `networkidle` to mount. Increase wait time before screenshotting. +- **Stale content**: The app uses Socket.IO for real-time updates. If data looks stale, the backend may not be connected to the fake stick. +- **Navigation drawer missing**: It may be collapsed. Click the hamburger icon (`v-app-bar-nav-icon`) in the top-left to toggle it open. diff --git a/.claude/skills/ui-browser/skill.md b/.claude/skills/ui-browser/skill.md deleted file mode 100644 index 93d25aa513..0000000000 --- a/.claude/skills/ui-browser/skill.md +++ /dev/null @@ -1,143 +0,0 @@ -# UI Browser Skill - -Browse, inspect, and screenshot the running zwave-js-ui dev server using Playwright MCP tools. - -Use this skill after making UI changes to visually verify they look correct, debug layout issues, inspect component state, or capture screenshots for PRs and documentation. - -## Prerequisites - -Dev servers must be running: -- Frontend: `npm run dev` (port 8092) -- Backend: `npm run dev:server` (port 8091) -- Fake stick: `npm run fake-stick` (port 5555) - -The Playwright MCP server must be configured (see `.mcp.json` in project root). - -## App Structure - -### Routing - -The app uses **Vue Router with hash history**. All URLs follow this pattern: -``` -http://localhost:8092/#/ -``` - -### Available Routes - -| Route | Page | Description | -|-------|------|-------------| -| `/` | Login | Auth page (only shown when auth is enabled) | -| `/control-panel` | Control Panel | Main page — node list + node details | -| `/settings` | Settings | App configuration, gateway values, Home Assistant | -| `/scenes` | Scenes | Scene management | -| `/debug` | Debug | Log viewer and debug console | -| `/store` | Store | MQTT/gateway store explorer | -| `/mesh` | Network Graph | Z-Wave mesh network visualization | -| `/smart-start` | Smart Start | Smart Start provisioning list | -| `/controller-chart` | Controller Chart | Controller statistics | -| `/zniffer` | Zniffer | Z-Wave traffic sniffer | -| `/configuration-templates` | Config Templates | Shared device configuration templates | - -### Layout - -- **Navigation drawer** (left sidebar): `v-navigation-drawer` — toggled via hamburger icon in app bar -- **App bar** (top): `v-app-bar` — contains nav toggle, page title, action buttons -- **Main content**: below app bar, fills remaining space - -### Auth Handling - -Auth is controlled by `store/settings.json` → `gateway.authEnabled`: -- **When `false` or unset**: No login needed. Navigating to `/` redirects to `/control-panel`. -- **When `true`**: All routes except `/` require login. The login form has: - - Username field: `input[name="username"]` - - Password field: `input[name="password"]` - - Submit button: `button[type="submit"]` (inside `#login-form`) - - Default credentials: `admin` / `zwave` - -## Using Playwright MCP for Interactive Browsing - -The Playwright MCP server gives you these capabilities: - -### Navigation -1. **Navigate** to any route: use the browser navigate tool with `http://localhost:8092/#/` -2. **Wait** for content: Vuetify components need ~2s after `networkidle` to fully render -3. **Go back/forward**: use browser navigation tools - -### Inspection -1. **Take a snapshot** (accessibility tree): see the page structure, text content, and interactive elements without a screenshot -2. **Take a screenshot**: capture visual state for verification or PR attachment -3. **Get element text**: read content from specific elements - -### Interaction -1. **Click** elements: buttons, tabs, list items, navigation links -2. **Fill** form fields: text inputs, selectors -3. **Select** dropdown options -4. **Hover** for tooltips or dropdown menus - -### Common Verification Patterns - -**After editing a Vue component:** -1. Navigate to the page containing that component -2. Take a screenshot to verify the visual result -3. If something looks off, take a snapshot to inspect the DOM structure - -**After changing styles/layout:** -1. Navigate to the affected page -2. Screenshot at default viewport (1280x720) -3. Optionally test responsive: resize viewport and screenshot again - -**After modifying a dialog/modal:** -1. Navigate to the page -2. Click the button/action that opens the dialog -3. Screenshot the dialog in its open state - -**Verifying dark mode:** -1. The app respects `localStorage.dark` — set it via the browser console or use the theme toggle in the UI - -## Batch Screenshots - -For capturing multiple pages at once (e.g., updating documentation), use the template script: - -```bash -node .claude/skills/ui-browser/screenshot-template.js [width] [height] -``` - -Examples: -```bash -# Default 1280x720, dark mode -node .claude/skills/ui-browser/screenshot-template.js "http://localhost:8092/#/settings" /tmp/screenshots/settings.png - -# Custom viewport -node .claude/skills/ui-browser/screenshot-template.js "http://localhost:8092/#/mesh" /tmp/screenshots/mesh.png 1920 1080 -``` - -The script automatically handles auth and sets dark mode via localStorage. - -### Output Locations - -- **Ad-hoc screenshots:** `/tmp/screenshots/` -- **Documentation updates:** `docs/_images/` in the project root - -### Route-to-Documentation Mapping - -| File | Route | Notes | -|------|-------|-------| -| `control_panel_dark.png` | `/#/control-panel` | Dark mode | -| `settings.png` | `/#/settings` | Light mode override | -| `settings_dark.png` | `/#/settings` | Dark mode | -| `debug.png` | `/#/debug` | Needs backend | -| `mesh_diagram.png` | `/#/mesh` | Needs backend + fake-stick | -| `mesh-selected.png` | `/#/mesh` | With a node selected | -| `scenes.png` | `/#/scenes` | | -| `nodes_manager.png` | `/#/control-panel` | Nodes table view | -| `unknown-device.png` | `/#/control-panel` | Specific unknown node | -| `hass_devices.png` | `/#/settings` | Home Assistant tab | -| `gateway_values_table.png` | `/#/settings` | Gateway values section | - -## Troubleshooting - -- **Login screen keeps appearing**: Check `store/settings.json` for `gateway.authEnabled`. If true, log in first. -- **Blank page**: Ensure all three dev servers are running. Check browser console for errors via snapshot. -- **Components not rendered**: Vuetify needs time to mount. Wait 2-3s after navigation before taking screenshots. -- **Stale content**: The app uses Socket.IO for real-time updates. If data looks stale, the backend may not be connected to the fake stick. -- **Navigation drawer missing**: It may be collapsed. Click the hamburger icon (`v-app-bar-nav-icon`) in the top-left to toggle it.