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
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue