feat(ms-ai-architect): v1.15.0 — playground v3 project-view integration
Erstatter v2 project-surface (screen-tabs + category-tabs + per-command paste-cards)
med v3 renderProjectView (sidebar med 17 artifacts + main-area + import-modal overlay).
renderActive() ruter project-surface til renderProjectSurfaceV3() som wrapper
renderProjectView + topbar + app-shell.
V2-surface helt fjernet:
- renderProjectSurface (152 linjer)
- renderCommandSubCard (87 linjer)
- rehydratePasteImports (15 linjer)
- ACTIONS['project-screen'], currentProjectScreen
- 5 v2-CSS-klasser: .project-tabs, .project-tab*, .sub-zone, .paste-import-row, .project-header__*, .command-cards
Zombie-handlers beholdt for test-back-compat:
currentProjectTab, ACTIONS['project-tab'], ACTIONS['parse'],
handlePasteImport, window.__handlePasteImport. Unreachable fra v3 DOM
men nødvendige for test-playground-v3.sh + test-playground-parsers.sh.
2 fingerprint-gap lukket:
- requirements.headers: utvidet med "EU AI Act — Krav" pattern
- license.headers: utvidet med "Lisens-kapabilitetsmatrise" pattern
- KNOWN_GAP_FIXTURES = {} i test-playground-fingerprints.sh
migrateDataVersion utvidet med parserFor (3. arg):
- Demo-state med kun raw_markdown auto-parses til project.artifacts[cid]
- defaultParserFor(cmdId) resolverer PARSERS[archetypeFor(cmdId)]
- 3 bootstrap-callsites oppdatert (cold-load, import, load-demo)
Ship-QA bugfixes funnet via browser-dogfood:
- components-tier4-project-view.css lagt til i <link>-kjeden (var ikke loaded
-> modal-overlay og two-column layout virket ikke)
- renderImportModal setter data-open="true" (DS-kontrakt for display: flex)
Bundler også sesjon 2-4 deliverables som ikke ble committed tidligere:
- shared/playground-design-system v0.6.0 (Tier 4 project-view CSS + 6 tokens)
- ms-ai-architect/playground/vendor/ re-sync til DS v0.6.0
- tests/test-playground-fingerprints.sh (sesjon 4 NY - 32 PASS)
- tests/test-playground-projectview.sh (sesjon 4 NY - 30 PASS)
- tests/test-playground-actions.sh (sesjon 4 NY - 19 PASS)
- tests/test-playground-migrations.sh utvidet (7 -> 16 PASS)
- tests/run-e2e.sh wirer alle 6 playground-suiter
Stats:
- bash tests/run-e2e.sh --playground: 386 PASS, 0 FAIL, 2 WARN (pre-eks)
- bash tests/run-e2e.sh (full): All E2E suites passed
- bash tests/validate-plugin.sh: 219 PASS
Screenshots regenerert til playground/screenshots/v1.15.0/ (24 PNG-er, 12
surfaces x 2 tema). Nye v3-surfaces: project-overview, project-artifact-*,
project-import-modal (viewport-only), project-search.
Docs oppdatert (3 nivåer): README.md (badge + version history),
CHANGELOG.md, CLAUDE.md (playground-seksjon + valideringstabell),
rot-README.md + rot-CLAUDE.md (marketplace-landingen + plugin-index).
.gitignore: ny pattern *.local.html + *.local.json for sesjon-state-filer.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
9affdca23e
commit
d8882f5220
47 changed files with 3722 additions and 409 deletions
|
|
@ -1,14 +1,12 @@
|
|||
#!/usr/bin/env node
|
||||
// Capture playground screenshots for v1.14.0 documentation.
|
||||
// Capture playground screenshots for v1.15.0 documentation.
|
||||
//
|
||||
// Opens the single-file playground HTML via file://, drives it through:
|
||||
// - Initial onboarding (empty state)
|
||||
// - "Last inn demo-data" → project surface with all 17 reports rehydrated
|
||||
// - All 4 project screen-tabs (oversikt / rapporter / kontekst / eksport)
|
||||
// - Each rapport-tab category (regulatory / security / economy / docs / tool)
|
||||
// - Both themes (dark + light)
|
||||
// v1.15.0: v2 project-surface (renderProjectSurface med screen-tabs +
|
||||
// category-tabs) erstattet av renderProjectView (sidebar med 17 artifacts +
|
||||
// main-area med per-artifact view + import-modal). Skjermbilder oppdatert
|
||||
// til å fange v3-surfaces.
|
||||
//
|
||||
// Output: playground/screenshots/v1.14.0/<surface>-<theme>.png
|
||||
// Output: playground/screenshots/v1.15.0/<surface>-<theme>.png
|
||||
//
|
||||
// Usage:
|
||||
// cd tests/screenshot
|
||||
|
|
@ -25,7 +23,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|||
const __dirname = dirname(__filename);
|
||||
const PLUGIN_ROOT = resolve(__dirname, '..', '..');
|
||||
const HTML_PATH = join(PLUGIN_ROOT, 'playground', 'ms-ai-architect-playground.html');
|
||||
const OUT_DIR = join(PLUGIN_ROOT, 'playground', 'screenshots', 'v1.14.0');
|
||||
const OUT_DIR = join(PLUGIN_ROOT, 'playground', 'screenshots', 'v1.15.0');
|
||||
const HTML_URL = 'file://' + HTML_PATH;
|
||||
|
||||
const VIEWPORT = { width: 1440, height: 900 };
|
||||
|
|
@ -49,7 +47,6 @@ async function clearState(page) {
|
|||
await page.evaluate(() => {
|
||||
try { localStorage.clear(); } catch (e) {}
|
||||
try {
|
||||
// Best-effort: clear IndexedDB databases.
|
||||
const dbs = ['ms-ai-architect-state-v1', 'ms-ai-architect-playground'];
|
||||
dbs.forEach((n) => indexedDB.deleteDatabase(n));
|
||||
} catch (e) {}
|
||||
|
|
@ -61,9 +58,9 @@ async function loadDemo(page) {
|
|||
const action = document.querySelector('[data-action="load-demo"]');
|
||||
if (action) action.click();
|
||||
});
|
||||
// Wait for project surface to render + rehydrate paste-imports.
|
||||
await page.waitForSelector('[data-surface="project"]:not([hidden])', { timeout: 5000 });
|
||||
await page.waitForTimeout(800); // settle rehydrate microtasks
|
||||
// Settle migrasjon (v2→v3 auto-parse) + render.
|
||||
await page.waitForTimeout(1200);
|
||||
}
|
||||
|
||||
async function clickAction(page, action) {
|
||||
|
|
@ -71,28 +68,46 @@ async function clickAction(page, action) {
|
|||
const el = document.querySelector('[data-action="' + a + '"]');
|
||||
if (el) el.click();
|
||||
}, action);
|
||||
await page.waitForTimeout(300);
|
||||
}
|
||||
|
||||
async function clickProjectTab(page, tabId) {
|
||||
await page.evaluate((t) => {
|
||||
const el = document.querySelector('[data-action="project-tab"][data-tab="' + t + '"]');
|
||||
if (el) el.click();
|
||||
}, tabId);
|
||||
await page.waitForTimeout(400);
|
||||
}
|
||||
|
||||
async function clickProjectScreen(page, screenId) {
|
||||
await page.evaluate((s) => {
|
||||
const el = document.querySelector('[data-action="project-screen"][data-screen="' + s + '"]');
|
||||
async function selectArtifact(page, artifactId) {
|
||||
await page.evaluate((id) => {
|
||||
const el = document.querySelector('[data-action="project-select-artifact"][data-artifact-id="' + id + '"]');
|
||||
if (el) el.click();
|
||||
}, screenId);
|
||||
}, artifactId);
|
||||
await page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
async function openImportModal(page, prefillCmd) {
|
||||
await page.evaluate((cid) => {
|
||||
// Foretrukket: artifact-reimport-knappen (har eksisterende markdown).
|
||||
if (cid) {
|
||||
const el = document.querySelector('[data-action="artifact-reimport"][data-command="' + cid + '"]');
|
||||
if (el) { el.click(); return; }
|
||||
}
|
||||
// Fallback: generisk "Importer rapport"-knapp.
|
||||
const open = document.querySelector('[data-action="import-open"]');
|
||||
if (open) open.click();
|
||||
}, prefillCmd);
|
||||
await page.waitForSelector('[data-import-modal]', { timeout: 3000 });
|
||||
await page.waitForTimeout(400);
|
||||
}
|
||||
|
||||
async function shoot(page, name) {
|
||||
async function setSearchQuery(page, query) {
|
||||
await page.evaluate((q) => {
|
||||
const input = document.querySelector('[data-project-search]');
|
||||
if (!input) return;
|
||||
input.value = q;
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
}, query);
|
||||
await page.waitForTimeout(400);
|
||||
}
|
||||
|
||||
async function shoot(page, name, opts) {
|
||||
const path = join(OUT_DIR, name + '.png');
|
||||
await page.screenshot({ path, fullPage: FULL_PAGE });
|
||||
const useFullPage = (opts && opts.fullPage != null) ? opts.fullPage : FULL_PAGE;
|
||||
await page.screenshot({ path, fullPage: useFullPage });
|
||||
console.log(' → ' + name + '.png');
|
||||
}
|
||||
|
||||
|
|
@ -106,52 +121,58 @@ async function captureAllSurfaces(page, theme) {
|
|||
await setTheme(page, theme);
|
||||
await shoot(page, '01-onboarding-empty-' + theme);
|
||||
|
||||
// 2. Load demo → project surface (rapporter screen, regulatory tab default)
|
||||
// 2. Load demo → project-view overview (default — ingen artifact valgt)
|
||||
await loadDemo(page);
|
||||
await setTheme(page, theme);
|
||||
await shoot(page, '02-project-rapporter-regulatory-' + theme);
|
||||
await shoot(page, '02-project-overview-' + theme);
|
||||
|
||||
// 3. Project tab cycle (5 categories)
|
||||
const TABS = [
|
||||
{ id: 'security', label: 'security' },
|
||||
{ id: 'economy', label: 'economy' },
|
||||
{ id: 'documentation', label: 'documentation' },
|
||||
{ id: 'tool', label: 'tool' }
|
||||
// 3-7. 5 sample artifacts som dekker arketype-bredden
|
||||
const SAMPLE_ARTIFACTS = [
|
||||
{ id: 'classify', label: 'classify' }, // AI Act-pyramide
|
||||
{ id: 'security', label: 'security' }, // 6×5 sikkerhets-matrise
|
||||
{ id: 'ros', label: 'ros' }, // ROS matrise + radar
|
||||
{ id: 'cost', label: 'cost' }, // P10/P50/P90 distribusjon
|
||||
{ id: 'summary', label: 'summary' } // Beslutningsnotat
|
||||
];
|
||||
for (const tab of TABS) {
|
||||
await clickProjectTab(page, tab.id);
|
||||
await page.waitForTimeout(500);
|
||||
await shoot(page, '03-project-rapporter-' + tab.label + '-' + theme);
|
||||
for (let i = 0; i < SAMPLE_ARTIFACTS.length; i++) {
|
||||
const a = SAMPLE_ARTIFACTS[i];
|
||||
await selectArtifact(page, a.id);
|
||||
const num = String(3 + i).padStart(2, '0');
|
||||
await shoot(page, num + '-project-artifact-' + a.label + '-' + theme);
|
||||
}
|
||||
|
||||
// 4. Project screen-tabs (oversikt / kontekst / eksport)
|
||||
await clickProjectScreen(page, 'oversikt');
|
||||
await shoot(page, '04-project-oversikt-' + theme);
|
||||
await clickProjectScreen(page, 'kontekst');
|
||||
await shoot(page, '05-project-kontekst-' + theme);
|
||||
await clickProjectScreen(page, 'eksport');
|
||||
await shoot(page, '06-project-eksport-' + theme);
|
||||
// 8. Import-modal åpen (med prefill fra eksisterende ros-artifact)
|
||||
// Viewport-only (ikke fullPage) — modal er position:fixed; fullPage
|
||||
// skroller forbi overlay-en og kaster bort kontekst.
|
||||
await openImportModal(page, 'ros');
|
||||
await page.evaluate(() => window.scrollTo(0, 0));
|
||||
await page.waitForTimeout(200);
|
||||
await shoot(page, '08-project-import-modal-' + theme, { fullPage: false });
|
||||
await clickAction(page, 'import-close');
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
// Back to rapporter for nav screenshots
|
||||
await clickProjectScreen(page, 'rapporter');
|
||||
// 9. Sidebar-søk aktivt (filtrer på "ros")
|
||||
await setSearchQuery(page, 'ros');
|
||||
await shoot(page, '09-project-search-' + theme);
|
||||
await setSearchQuery(page, ''); // reset
|
||||
|
||||
// 5. Home surface
|
||||
// 10. Home surface
|
||||
await clickAction(page, 'goto-home');
|
||||
await page.waitForSelector('[data-surface="home"]:not([hidden])');
|
||||
await page.waitForTimeout(300);
|
||||
await shoot(page, '07-home-' + theme);
|
||||
await shoot(page, '10-home-' + theme);
|
||||
|
||||
// 6. Catalog surface
|
||||
// 11. Catalog surface
|
||||
await clickAction(page, 'goto-catalog');
|
||||
await page.waitForSelector('[data-surface="catalog"]:not([hidden])');
|
||||
await page.waitForTimeout(300);
|
||||
await shoot(page, '08-catalog-' + theme);
|
||||
await shoot(page, '11-catalog-' + theme);
|
||||
|
||||
// 7. Onboarding (with prefilled state from demo)
|
||||
// 12. Onboarding prefilled (post-demo med org-felter fylt)
|
||||
await clickAction(page, 'goto-onboarding');
|
||||
await page.waitForSelector('[data-surface="onboarding"]:not([hidden])');
|
||||
await page.waitForTimeout(300);
|
||||
await shoot(page, '09-onboarding-prefilled-' + theme);
|
||||
await shoot(page, '12-onboarding-prefilled-' + theme);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
|
@ -160,7 +181,7 @@ async function main() {
|
|||
const browser = await chromium.launch();
|
||||
const context = await browser.newContext({
|
||||
viewport: VIEWPORT,
|
||||
deviceScaleFactor: 2 // crisper screenshots for retina
|
||||
deviceScaleFactor: 2
|
||||
});
|
||||
const page = await context.newPage();
|
||||
page.on('console', (msg) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue