fix(voyage): inline screenshot gallery loads docs/screenshots/ PNGs (31d28f65)
This commit is contained in:
parent
9909ce1066
commit
412b4561f5
5 changed files with 137 additions and 8 deletions
|
|
@ -14,7 +14,7 @@
|
|||
| 3 | Theme bootstrap IIFE | PASS | Group A test SC1.3; `data-theme` + `prefers-color-scheme` IIFE i HTML head |
|
||||
| 4 | Onboarding-grid (redefinert) | PASS-redef | Group A test SC1.4; voyage bruker `fleet-grid` + `fleet-tile` istedenfor onboarding-grid (scope-guardian SC-GAP-1, Assumption #21) |
|
||||
| 5 | A11Y-panel | PASS | Group A test SC1.5; `guide-panel--info` + `key-stats` + `findings__items` (Wave 5 Step 22) |
|
||||
| 6 | Screenshots-spor (redefinert) | PASS-redef | Group A test SC1.6; `window.__voyage` hooks + `docs/screenshots/README.md` istedenfor inline gallery (scope-guardian SC-GAP-2, Assumption #22) |
|
||||
| 6 | Screenshots-spor | PASS | Group D test SC1.6; `loadProjectDirectory` leser `docs/screenshots/**/*.png` via `readAsDataURL` (2 MB cap) og `renderScreenshotGallery` mounter en `<figure>`-grid i dashboardet. Erstatter scope-guardian SC-GAP-2 PASS-redef med faktisk inline gallery (v4.3 Step 8, finding 31d28f65). |
|
||||
| 7 | Body typografi | PASS | Group A test SC1.7; `var(--font-size-*)` + `var(--font-family-mono)` brukt gjennomgående |
|
||||
| 8 | Spacing rhythm | PASS | Group A test SC1.8; ≥5 distinkte `var(--space-N)` referanser |
|
||||
| 9 | Color-token fidelity | PASS | Group A test SC1.9; `badge--scope-voyage` + `--color-scope-voyage` brukt |
|
||||
|
|
@ -57,13 +57,12 @@
|
|||
- **Test:** `SC1.5 A11Y panel — guide-panel--info + key-stats + findings`
|
||||
- **Status:** PASS
|
||||
|
||||
### Element 6 — Screenshots-spor (REDEFINERT)
|
||||
### Element 6 — Screenshots-spor
|
||||
- **Bokstavelig krav:** llm-security har inline gallery med thumbnail-grid for case-studies
|
||||
- **Voyage-tolkning:** Inline gallery deferret. Wave 5 Step 23 leverer `window.__voyage` hooks-spor (`navigate`, `scheduleRender`, `getProjectArtifacts`) + `docs/screenshots/README.md` mappe-konvensjon
|
||||
- **Implementering:** Hooks brukes av Playwright-spec (Step 30); manuell screenshot-prosedyre dokumentert i `docs/screenshots/README.md`
|
||||
- **Test:** `SC1.6 screenshots-spor — window.__voyage hooks + docs convention`
|
||||
- **Status:** PASS-redef (scope-guardian SC-GAP-2, Assumption #22 — operatør-sign-off ved sesjon-start)
|
||||
- **Risk:** Samme som Element 4 — `/trekreview` kan kreve faktisk gallery-komponent.
|
||||
- **Voyage-implementering (v4.3 Step 8, finding 31d28f65):** `loadProjectDirectory` detekterer `docs/screenshots/**/*.png` via path-prefix-match, leser dem via `FileReader.readAsDataURL()` med 2 MB per-bilde cap (overskridelse annonseres via aria-live). `renderScreenshotGallery(screenshots)` bygger en `<figure>`-grid med `<img src="<dataUrl>" alt="<filename>">` som mountes under `fleet-grid` i `renderDashboard`. Erstatter den tidligere PASS-redef-tolkningen — voyage har nå et faktisk inline gallery.
|
||||
- **Beholder:** `window.__voyage` hooks fra Wave 5 Step 23 (`navigate`, `scheduleRender`, `getProjectArtifacts`) + `docs/screenshots/README.md` mappe-konvensjon — fortsatt brukt av Playwright-spec og av operatør for manuell screenshot-prosedyre.
|
||||
- **Test:** Group D Playwright-test `SC1.6 inline gallery — data:image PNGs rendered (31d28f65)` injiserer en fixture-artifact via `scheduleRender` og asserter `#voyage-dashboard img[src^="data:image/png"]` count > 0.
|
||||
- **Status:** PASS (inline gallery implementert; scope-guardian SC-GAP-2 lukket).
|
||||
|
||||
### Element 7 — Body typografi
|
||||
- **Bokstavelig krav:** typografi-skala bruker DS-tokens, ikke literal pixel-verdier
|
||||
|
|
|
|||
|
|
@ -844,6 +844,42 @@
|
|||
}
|
||||
.voyage-back-btn:hover { background: var(--color-bg-soft); }
|
||||
|
||||
/* v4.3 Step 8 — inline screenshot gallery (finding 31d28f65).
|
||||
Renders below the fleet-grid in the dashboard. <figure> grid with
|
||||
responsive auto-fit columns at 240px min; <figcaption> shows the
|
||||
relative path so operators can correlate to docs/screenshots/. */
|
||||
.voyage-screenshot-gallery { margin-top: var(--space-4); }
|
||||
.voyage-screenshot-gallery h3 {
|
||||
margin: 0 0 var(--space-3) 0;
|
||||
font-size: var(--font-size-lg);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
.voyage-screenshot-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: var(--space-3);
|
||||
}
|
||||
.voyage-screenshot {
|
||||
margin: 0;
|
||||
border: 1px solid var(--color-border-subtle);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--color-surface);
|
||||
overflow: hidden;
|
||||
}
|
||||
.voyage-screenshot img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.voyage-screenshot figcaption {
|
||||
padding: var(--space-2) var(--space-3);
|
||||
font-family: var(--font-family-mono);
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-text-secondary);
|
||||
border-top: 1px solid var(--color-border-subtle);
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* v4.3 remediation: color-contrast fix for finding 09132940.
|
||||
The vendored DS sets `.key-stat__label` to var(--color-text-tertiary)
|
||||
which is #6E7781 in light theme — borderline 4.5:1 WCAG-AA contrast
|
||||
|
|
@ -1455,6 +1491,10 @@ playground first-run shows a complete round-trip-able artifact.
|
|||
progress: null,
|
||||
research: [],
|
||||
architecture: { overview: null, gaps: null, looseFiles: [] },
|
||||
// v4.3 Step 8 — inline screenshot gallery (finding 31d28f65).
|
||||
// `screenshots` is populated below from docs/screenshots/**/*.png
|
||||
// entries. Each item: { path, dataUrl }.
|
||||
screenshots: [],
|
||||
looseFiles: []
|
||||
};
|
||||
|
||||
|
|
@ -1480,13 +1520,38 @@ playground first-run shows a complete round-trip-able artifact.
|
|||
else if (parts.length === 2 && parts[0] === 'architecture' && name === 'overview.md') bucket = 'arch_overview';
|
||||
else if (parts.length === 2 && parts[0] === 'architecture' && name === 'gaps.md') bucket = 'arch_gaps';
|
||||
else if (parts.length === 2 && parts[0] === 'architecture' && /\.md$/.test(name)) bucket = 'arch_loose';
|
||||
// v4.3 Step 8 — docs/screenshots/**/*.png → inline gallery
|
||||
else if (parts[0] === 'docs' && parts[1] === 'screenshots' && /\.png$/i.test(name)) bucket = 'screenshot';
|
||||
else bucket = 'loose';
|
||||
classified.push({ file: f, rel: inside, bucket: bucket });
|
||||
}
|
||||
|
||||
// Phase 2 — read content + parse frontmatter (async).
|
||||
// v4.3 Step 8 — `screenshot` bucket uses readAsDataURL with a 2 MB
|
||||
// per-image cap (finding 31d28f65); oversized PNGs are skipped
|
||||
// with an aria-live announce so AT users know what was suppressed.
|
||||
var SCREENSHOT_MAX_BYTES = 2 * 1024 * 1024;
|
||||
for (var j = 0; j < classified.length; j++) {
|
||||
var c = classified[j];
|
||||
if (c.bucket === 'screenshot') {
|
||||
if (c.file && typeof c.file.size === 'number' && c.file.size > SCREENSHOT_MAX_BYTES) {
|
||||
announce('Hopper over for stort screenshot: ' + c.rel);
|
||||
continue;
|
||||
}
|
||||
var dataUrl = '';
|
||||
try {
|
||||
dataUrl = await new Promise(function (resolve, reject) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function () { resolve(String(reader.result || '')); };
|
||||
reader.onerror = function () { reject(reader.error); };
|
||||
reader.readAsDataURL(c.file);
|
||||
});
|
||||
} catch (_) { dataUrl = ''; }
|
||||
if (dataUrl) {
|
||||
artifacts.screenshots.push({ path: c.rel, dataUrl: dataUrl });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
var text = '';
|
||||
try { text = await c.file.text(); } catch (_) { text = ''; }
|
||||
var fm = quickParseFrontmatter(text);
|
||||
|
|
@ -1504,6 +1569,7 @@ playground first-run shows a complete round-trip-able artifact.
|
|||
}
|
||||
}
|
||||
artifacts.research.sort(function (a, b) { return a.path.localeCompare(b.path); });
|
||||
artifacts.screenshots.sort(function (a, b) { return a.path.localeCompare(b.path); });
|
||||
|
||||
// Phase 3 — deterministic storage-key from basePath (NOT URL params)
|
||||
// so draft-annotation state is isolated per project.
|
||||
|
|
@ -1807,6 +1873,26 @@ playground first-run shows a complete round-trip-able artifact.
|
|||
return parts[parts.length - 1] || p;
|
||||
}
|
||||
|
||||
// v4.3 Step 8 — inline screenshot gallery (finding 31d28f65).
|
||||
// Builds a <figure> grid from the screenshots[] array populated by
|
||||
// loadProjectDirectory. Each item renders as a data:image/png <img>
|
||||
// wrapped in a <figure>/<figcaption>. Returns an empty string when
|
||||
// there are no screenshots so the dashboard layout stays clean.
|
||||
function renderScreenshotGallery(screenshots) {
|
||||
if (!screenshots || !screenshots.length) return '';
|
||||
var items = screenshots.map(function (s) {
|
||||
var name = (s.path || '').split('/').pop() || s.path || '';
|
||||
return '<figure class="voyage-screenshot">' +
|
||||
'<img src="' + s.dataUrl + '" alt="' + escapeHtml(name) + '" loading="lazy">' +
|
||||
'<figcaption>' + escapeHtml(s.path) + '</figcaption>' +
|
||||
'</figure>';
|
||||
}).join('');
|
||||
return '<section class="voyage-screenshot-gallery" aria-label="Screenshots">' +
|
||||
'<h3>Screenshots</h3>' +
|
||||
'<div class="voyage-screenshot-grid">' + items + '</div>' +
|
||||
'</section>';
|
||||
}
|
||||
|
||||
function renderDashboard(projectArtifacts, slot) {
|
||||
var host = slot || $('voyage-dashboard');
|
||||
if (!host) return;
|
||||
|
|
@ -1828,7 +1914,8 @@ playground first-run shows a complete round-trip-able artifact.
|
|||
}).join('');
|
||||
|
||||
var projectName = shortenBasePath(projectArtifacts.basePath);
|
||||
var bodyHtml = '<div class="fleet-grid">' + tilesHtml + '</div>';
|
||||
var galleryHtml = renderScreenshotGallery(projectArtifacts.screenshots || []);
|
||||
var bodyHtml = '<div class="fleet-grid">' + tilesHtml + '</div>' + galleryHtml;
|
||||
host.innerHTML = renderPageShell({
|
||||
eyebrow: 'Project dashboard',
|
||||
title: projectName,
|
||||
|
|
|
|||
|
|
@ -145,6 +145,38 @@ test.describe('voyage-playground a11y (axe-core)', () => {
|
|||
expect(nodes, `color-contrast violations remain: ${JSON.stringify(nodes, null, 2)}`).toEqual([]);
|
||||
});
|
||||
|
||||
// v4.3 Step 8 — inline screenshot gallery (finding 31d28f65).
|
||||
// Injects a pre-built artifacts object with screenshots[] via the
|
||||
// window.__voyage.scheduleRender hook (avoids webkitdirectory which
|
||||
// is not programmatically triggerable). Asserts the dashboard renders
|
||||
// at least one data:image PNG <img> tag.
|
||||
test('SC1.6 inline gallery — data:image PNGs rendered (31d28f65)', async ({ page }) => {
|
||||
await page.goto('voyage-playground.html');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
// 1×1 transparent PNG (same base64 as the fixture file)
|
||||
const SAMPLE_DATA_URL =
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==';
|
||||
await page.evaluate((dataUrl) => {
|
||||
window.__voyage.scheduleRender({
|
||||
artifacts: {
|
||||
basePath: 'fixture-project',
|
||||
storageKey: 'voyage_proj_fixture',
|
||||
brief: { path: 'brief.md', content: '# Fixture', frontmatter: {} },
|
||||
plan: null,
|
||||
review: null,
|
||||
progress: null,
|
||||
research: [],
|
||||
architecture: { overview: null, gaps: null, looseFiles: [] },
|
||||
screenshots: [{ path: 'docs/screenshots/dashboard/sample.png', dataUrl: dataUrl }],
|
||||
looseFiles: [],
|
||||
},
|
||||
});
|
||||
}, SAMPLE_DATA_URL);
|
||||
// The gallery is rendered inside #voyage-dashboard
|
||||
const imgCount = await page.locator('#voyage-dashboard img[src^="data:image/png"]').count();
|
||||
expect(imgCount, 'expected at least one data:image/png <img> in the gallery').toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('pixel-diff smoke 1280×900 — light + dark within 2% threshold (SC1 backup)', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1280, height: 900 });
|
||||
// Light theme baseline
|
||||
|
|
|
|||
11
plugins/voyage/tests/fixtures/screenshot-project/brief.md
vendored
Normal file
11
plugins/voyage/tests/fixtures/screenshot-project/brief.md
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
task: Screenshot gallery fixture for Group D test
|
||||
slug: screenshot-project
|
||||
project_dir: tests/fixtures/screenshot-project
|
||||
---
|
||||
|
||||
# Screenshot fixture brief
|
||||
|
||||
Minimal brief.md so `loadProjectDirectory` reaches its render phase
|
||||
without emitting the "brief.md mangler" warning. Real verification
|
||||
is the data:image PNG count assertion in the Group D test.
|
||||
BIN
plugins/voyage/tests/fixtures/screenshot-project/docs/screenshots/dashboard/sample.png
vendored
Normal file
BIN
plugins/voyage/tests/fixtures/screenshot-project/docs/screenshots/dashboard/sample.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 B |
Loading…
Add table
Add a link
Reference in a new issue