feat(voyage): implement screenshots-spor convention (window.__hooks + docs/screenshots/)
This commit is contained in:
parent
b70b480d0d
commit
e839ba2a7a
3 changed files with 179 additions and 0 deletions
89
plugins/voyage/docs/screenshots/README.md
Normal file
89
plugins/voyage/docs/screenshots/README.md
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# Voyage playground screenshot convention (v4.3)
|
||||
|
||||
This directory holds reference screenshots for the Voyage playground
|
||||
(`playground/voyage-playground.html`). They serve as a visual baseline for
|
||||
manual review of design changes and as fixtures for the Wave 7 axe-core
|
||||
Playwright spec (`tests/e2e/voyage-playground-a11y.spec.mjs`).
|
||||
|
||||
Screenshots are NOT auto-committed. The `tests/e2e/snapshots/` directory
|
||||
(produced by Playwright in Wave 7) is the test-baseline; this directory
|
||||
holds curated illustrative captures for documentation.
|
||||
|
||||
## Mappestruktur
|
||||
|
||||
```
|
||||
docs/screenshots/
|
||||
README.md ← this file
|
||||
dashboard/ ← project-dashboard fleet-grid views
|
||||
artifact-detail/ ← drill-down render of brief/plan/review
|
||||
annotation/ ← gutter-badge + sidebar + active highlight states
|
||||
dark-mode/ ← screenshots taken with html[data-theme="dark"]
|
||||
light-mode/ ← screenshots taken with html[data-theme="light"]
|
||||
```
|
||||
|
||||
Each subfolder contains PNGs named by feature + variant, e.g.
|
||||
`dashboard/fleet-grid-with-progress-tile.png`,
|
||||
`annotation/active-anchor-yellow-tint.png`.
|
||||
|
||||
## Hooks (window.__voyage)
|
||||
|
||||
The playground exposes three automation hooks for headless screenshot
|
||||
scripts. They are namespaced under `window.__voyage` to avoid global
|
||||
pollution:
|
||||
|
||||
| Method | Purpose |
|
||||
|--------|---------|
|
||||
| `window.__voyage.navigate(surface)` | Switch surface. `surface ∈ ['dashboard', 'detail', 'render', 'a11y']` |
|
||||
| `window.__voyage.scheduleRender(state)` | Pre-populate state. `state.markdown` triggers `mountRender`; `state.artifacts` triggers `renderDashboard` |
|
||||
| `window.__voyage.getProjectArtifacts()` | Returns the last-loaded `ProjectArtifacts` object (or `null`) |
|
||||
|
||||
The hook surface mirrors the pattern used in
|
||||
`plugins/llm-security/playground/llm-security-playground.html`
|
||||
(`window.__navigate`, `window.__scheduleRender`).
|
||||
|
||||
## Producing screenshots — Playwright headless (Wave 7+)
|
||||
|
||||
Wave 7 ships a Playwright spec
|
||||
(`tests/e2e/voyage-playground-a11y.spec.mjs`). Reuse its setup for
|
||||
screenshots:
|
||||
|
||||
```js
|
||||
// tests/e2e/snapshot-helper.mjs (NOT committed in v4.3 — illustrative)
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
test('dashboard with all artifacts', async ({ page }) => {
|
||||
await page.goto('file://' + process.cwd() + '/playground/voyage-playground.html');
|
||||
await page.evaluate(() => {
|
||||
window.__voyage.scheduleRender({
|
||||
artifacts: {
|
||||
basePath: 'demo',
|
||||
brief: { path: 'brief.md', content: '...' },
|
||||
plan: { path: 'plan.md', content: '...' },
|
||||
review: null,
|
||||
research: [],
|
||||
architecture: { overview: null, gaps: null, looseFiles: [] },
|
||||
progress: null,
|
||||
looseFiles: [],
|
||||
}
|
||||
});
|
||||
});
|
||||
await page.screenshot({ path: 'docs/screenshots/dashboard/fleet-grid-demo.png' });
|
||||
});
|
||||
```
|
||||
|
||||
## Producing screenshots — manuelt
|
||||
|
||||
1. Åpne `playground/voyage-playground.html` i Chrome/Firefox.
|
||||
2. Hvis du trenger et bestemt UI-state, paste relevant artifact-innhold i
|
||||
textarea og trykk Render.
|
||||
3. macOS: `Cmd+Shift+4`, deretter `Space` for vindu-skjermbilde.
|
||||
4. Lagre med beskrivende filnavn under riktig undermappe ovenfor.
|
||||
|
||||
## Konvensjon for nye screenshots
|
||||
|
||||
- Bruk PNG, ikke JPG (lossless for tekst-tunge UIer).
|
||||
- Kjør i konsistent viewport (1280×800 anbefales for desktop-baseline).
|
||||
- Ta dark- og light-mode-varianter parvis når komponenten har visuelle
|
||||
forskjeller mellom temaene.
|
||||
- Ikke commit screenshots med personidentifiserende info eller hemmelig
|
||||
artifact-innhold.
|
||||
|
|
@ -2840,6 +2840,65 @@ playground first-run shows a complete round-trip-able artifact.
|
|||
});
|
||||
}
|
||||
|
||||
// ---- v4.3 Step 23 — screenshots-spor convention -------------------
|
||||
// Expose a minimal automation surface for headless screenshot scripts
|
||||
// (Wave 7 axe-spec + manual screenshot tooling). Mirrors the
|
||||
// llm-security-playground.html `window.__navigate` /
|
||||
// `window.__scheduleRender` pattern but namespaced under
|
||||
// `window.__voyage` to avoid global pollution. All three methods are
|
||||
// intentionally read-only from the outside — they wrap existing
|
||||
// functions; we never expose state-mutating internals directly.
|
||||
//
|
||||
// Methods:
|
||||
// navigate(surface) — surface ∈ ['dashboard', 'detail', 'render', 'a11y']
|
||||
// scheduleRender(state) — state.markdown → mountRender; state.artifacts → renderDashboard
|
||||
// getProjectArtifacts() — returns the last-loaded ProjectArtifacts object (or null)
|
||||
//
|
||||
// See docs/screenshots/README.md for the screenshot mappestruktur.
|
||||
window.__voyage = {
|
||||
navigate: function (surface) {
|
||||
var s = String(surface || '').toLowerCase();
|
||||
if (s === 'dashboard') {
|
||||
if (__voyageCurrentArtifacts && typeof renderDashboard === 'function') {
|
||||
renderDashboard(__voyageCurrentArtifacts);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
if (s === 'detail') {
|
||||
// No-op without an artifact-key; renderArtifactDetail requires
|
||||
// a key (brief|plan|review|progress|research:N|architecture:overview).
|
||||
return s;
|
||||
}
|
||||
if (s === 'render') {
|
||||
var ta = document.getElementById('voyage-paste-input');
|
||||
if (ta && typeof mountRender === 'function') mountRender(ta.value || '');
|
||||
return s;
|
||||
}
|
||||
if (s === 'a11y') {
|
||||
var panel = document.getElementById('voyage-a11y-panel');
|
||||
if (panel) panel.hidden = false;
|
||||
return s;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
scheduleRender: function (state) {
|
||||
if (!state || typeof state !== 'object') return false;
|
||||
if (typeof state.markdown === 'string' && typeof mountRender === 'function') {
|
||||
var ta = document.getElementById('voyage-paste-input');
|
||||
if (ta) ta.value = state.markdown;
|
||||
mountRender(state.markdown);
|
||||
}
|
||||
if (state.artifacts && typeof renderDashboard === 'function') {
|
||||
__voyageCurrentArtifacts = state.artifacts;
|
||||
renderDashboard(state.artifacts);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
getProjectArtifacts: function () {
|
||||
return __voyageCurrentArtifacts || null;
|
||||
},
|
||||
};
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -382,3 +382,34 @@ test('voyage-playground.html declares wireA11yToggle JS function (v4.3 Step 22)'
|
|||
assert.match(text, /panel\.hidden\s*=\s*!willOpen/, 'panel.hidden toggle required');
|
||||
assert.match(text, /setAttribute\('aria-expanded'/, 'aria-expanded update required');
|
||||
});
|
||||
|
||||
// v4.3 Step 23 — screenshots-spor convention (window.__hooks + docs/screenshots/)
|
||||
test('voyage-playground.html exposes window.__voyage automation hooks (v4.3 Step 23)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
// window.__voyage must be assigned (object literal or assignment expression)
|
||||
assert.match(text, /window\.__voyage\s*=\s*\{/, 'window.__voyage = { ... } assignment required');
|
||||
});
|
||||
|
||||
test('voyage-playground.html window.__voyage exposes navigate/scheduleRender/getProjectArtifacts (v4.3 Step 23)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
// Each method must appear as a property of the exposed object.
|
||||
assert.match(text, /navigate:\s*function/, 'navigate method required');
|
||||
assert.match(text, /scheduleRender:\s*function/, 'scheduleRender method required');
|
||||
assert.match(text, /getProjectArtifacts:\s*function/, 'getProjectArtifacts method required');
|
||||
});
|
||||
|
||||
test('docs/screenshots/README.md documents mappestruktur + hooks (v4.3 Step 23)', () => {
|
||||
const path = join(ROOT, 'docs', 'screenshots', 'README.md');
|
||||
const text = readFileSync(path, 'utf-8');
|
||||
assert.match(text, /Mappestruktur/, 'Mappestruktur heading required');
|
||||
// Must list each documented subfolder
|
||||
assert.match(text, /dashboard\//, 'dashboard/ subfolder documented');
|
||||
assert.match(text, /artifact-detail\//, 'artifact-detail/ subfolder documented');
|
||||
assert.match(text, /annotation\//, 'annotation/ subfolder documented');
|
||||
assert.match(text, /dark-mode\//, 'dark-mode/ subfolder documented');
|
||||
assert.match(text, /light-mode\//, 'light-mode/ subfolder documented');
|
||||
// Hooks documentation must reference all three methods
|
||||
assert.match(text, /window\.__voyage\.navigate/, 'navigate hook documented');
|
||||
assert.match(text, /window\.__voyage\.scheduleRender/, 'scheduleRender hook documented');
|
||||
assert.match(text, /window\.__voyage\.getProjectArtifacts/, 'getProjectArtifacts hook documented');
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue