refactor(ms-ai-architect): playground v1.14.0 sesjon 3 — risk-rapporter til DS-konvensjon
- renderDpia: matrix wrappet i .card med h2 - renderSecurity: ros-layout (matrix+radar), small-multiples-section, top-risks som <ol> i .card - renderRos: speil renderSecurity (5x5) + summary-grid for top-risks+recommendation - renderFindingsBlock: fjern .report-meta-band-aid, bruk findings-section + findings__items--standalone - Legg til .ros-layout, .summary-grid, .findings-section, .small-multiples-section i lokal CSS - Fjern .top-risks fra defensive layout-block - test-playground-v3.sh: bytt .findings__list → .findings__items i DS-klasse-asserts - Style-blokk: 182 → 188 linjer (mål ≤195 nådd) Refs V1.14.0-AUDIT.local.md sub-batch B + helper-section.
This commit is contained in:
parent
76a64bde48
commit
d117bea219
2 changed files with 109 additions and 63 deletions
|
|
@ -185,6 +185,15 @@
|
|||
ikke skubber innhold utenfor viewport. */
|
||||
.recommendation-card__body { overflow-wrap: anywhere; word-break: break-word; }
|
||||
|
||||
/* v1.14.0 sesjon 3: layout-utilities for risk-rapporter (renderDpia, renderSecurity, renderRos).
|
||||
Speiler Anthropic-ref ros-lier-scenario. Hoist til DS i v1.15.0 hvis andre plugins trenger samme. */
|
||||
.ros-layout { display: grid; grid-template-columns: 1fr 320px; gap: var(--space-6); align-items: start; margin-block: var(--space-5); }
|
||||
.summary-grid { display: grid; grid-template-columns: 1.4fr 1fr; gap: var(--space-5); align-items: start; margin-block: var(--space-5); }
|
||||
@media (max-width: 980px) { .ros-layout, .summary-grid { grid-template-columns: 1fr; } }
|
||||
.findings-section, .small-multiples-section { margin-block: var(--space-5); }
|
||||
.findings-section > h3, .small-multiples-section > h3 { margin: 0 0 var(--space-3); font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); }
|
||||
.findings__items--standalone { list-style: none; margin: 0; padding: 0; border: 1px solid var(--color-border-subtle); border-radius: var(--radius-lg); background: var(--color-surface); overflow: hidden; }
|
||||
|
||||
/* v1.13.1 fix (B7): .report-meta er ikke definert i vendored DS — vi bruker
|
||||
<section class="report-meta"> som outer-wrapper i flere rendrere. Browser-
|
||||
defaults for <dl><dd><ul> gir uforutsigbare indents (Forpliktelser-bullet-
|
||||
|
|
@ -209,12 +218,11 @@
|
|||
/* v1.13.1 fix (B12, B13, B15): defensive block-layout for sibling-rapport-
|
||||
seksjoner som kan ha overflow-issues mot foregående grid-elementer
|
||||
(.small-multiples, .kanban-board, .mat-ladder). Sikrer eksplisitt
|
||||
block-flow + clear så de ikke leker grid-celler eller flyter. */
|
||||
.top-risks,
|
||||
block-flow + clear så de ikke leker grid-celler eller flyter.
|
||||
v1.14.0 sesjon 3: .top-risks fjernet (ligger nå i .card med riktig DS block-flow). */
|
||||
.suppressed-panel,
|
||||
.phase-detail,
|
||||
.aiact-timeline,
|
||||
.small-multiples + .top-risks,
|
||||
.kanban-board + .report-meta,
|
||||
.mat-ladder + .phase-detail,
|
||||
.phase-detail + .phase-detail { display: block; clear: both; width: 100%; }
|
||||
|
|
@ -3327,9 +3335,11 @@
|
|||
}
|
||||
|
||||
function renderFindingsBlock(findings, label) {
|
||||
// v1.13.0 fix (B1): DS' .findings er grid 360px+1fr (list+detail-panel).
|
||||
// Vi har bare list-kolonnen; bruk <section class="report-meta"> som outer
|
||||
// for å beholde findings__list-strukturen uten å bli klemt til 360px.
|
||||
// v1.14.0 sesjon 3: refaktorert fra .report-meta-band-aid til standalone
|
||||
// findings-section. DS' .findings er grid 360px+1fr (list+detail-panel) —
|
||||
// siden vi ikke har detail-panel, bruker vi en standalone container med
|
||||
// .findings__items--standalone-modifier som styles lokalt.
|
||||
if (!findings || !findings.length) return '';
|
||||
const items = findings.map(function (f) {
|
||||
return '<li class="findings__item">' +
|
||||
'<span class="findings__item-severity-dot" data-severity="' + escapeAttr(f.severity || 'info') + '"></span>' +
|
||||
|
|
@ -3338,14 +3348,9 @@
|
|||
'<span class="findings__item-meta">Lokasjon: ' + escapeHtml(f.location || '—') + ' · Severity: ' + escapeHtml(f.severity || '—') + '</span>' +
|
||||
'</li>';
|
||||
}).join('');
|
||||
return '<section class="report-meta">' +
|
||||
'<h4>' + escapeHtml(label) + '</h4>' +
|
||||
'<div class="findings__list">' +
|
||||
'<div class="findings__group">' +
|
||||
'<div class="findings__group-header"><span>' + escapeHtml(label) + '</span><span>' + findings.length + '</span></div>' +
|
||||
'<ul class="findings__items">' + items + '</ul>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
return '<section class="findings-section">' +
|
||||
'<h3>' + escapeHtml(label) + '</h3>' +
|
||||
'<ul class="findings__items findings__items--standalone">' + items + '</ul>' +
|
||||
'</section>';
|
||||
}
|
||||
|
||||
|
|
@ -3671,7 +3676,12 @@
|
|||
}
|
||||
|
||||
function renderDpia(data, slot) {
|
||||
const matrixHtml = renderMatrixHtml(data, 5);
|
||||
// v1.14.0 sesjon 3: matrix wrappet i .card med h2 for visuell separasjon
|
||||
// fra residual-pair og threats-tabell (per Anthropic-ref ros-lier-pattern).
|
||||
const matrixHtml = '<div class="card" style="padding: var(--space-6)">' +
|
||||
'<h2>5×5 Risikomatrise</h2>' +
|
||||
renderMatrixHtml(data, 5) +
|
||||
'</div>';
|
||||
const threatsHtml = renderThreatsTable(data.threats);
|
||||
const rp = data.residualPair;
|
||||
let residualHtml = '';
|
||||
|
|
@ -3735,34 +3745,51 @@
|
|||
if (n >= 4) return 'medium';
|
||||
return 'low';
|
||||
};
|
||||
const matrixHtml = renderMatrixHtml(data, 6);
|
||||
const radarHtml = renderRadarSvg(data.dimensions || []);
|
||||
// C7 small-multiples per OWASP-kategori (driver: categoryGrades).
|
||||
// v1.14.0 sesjon 3: matrix + radar i .ros-layout (1fr 320px) per Anthropic-ref
|
||||
// ros-lier-scenario. Matrix står i .card, radar i <aside class="card">.
|
||||
const layoutHtml = '<div class="ros-layout">' +
|
||||
'<div class="card" style="padding: var(--space-6)">' +
|
||||
'<h2>6×5 Sikkerhetsmatrise</h2>' +
|
||||
renderMatrixHtml(data, 6) +
|
||||
'</div>' +
|
||||
'<aside class="card">' +
|
||||
'<h3>Dimensjons-radar</h3>' +
|
||||
renderRadarSvg(data.dimensions || []) +
|
||||
'</aside>' +
|
||||
'</div>';
|
||||
// C7 small-multiples per OWASP-kategori (driver: categoryGrades) — egen seksjon
|
||||
// i full bredde under layout.
|
||||
const cats = data.categoryGrades || [];
|
||||
const smallMultiplesHtml = cats.length ? '<div class="small-multiples">' + cats.map(function (c) {
|
||||
const grade = c.grade || '';
|
||||
const fillPct = Math.max(0, Math.min(100, ((Number(c.score) || 0) / 5) * 100));
|
||||
return '<div class="sm-card">' +
|
||||
'<div class="sm-card__header">' +
|
||||
'<span class="sm-card__name">' + escapeHtml(c.name || '') + '</span>' +
|
||||
'<span class="sm-card__grade" data-grade="' + escapeAttr(grade) + '">' + escapeHtml(grade) + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="sm-card__bar"><div class="sm-card__bar-fill" style="width: ' + fillPct.toFixed(0) + '%"></div></div>' +
|
||||
'<span class="sm-card__status">Score ' + escapeHtml(String(c.score || 0)) + ' / 5</span>' +
|
||||
'</div>';
|
||||
}).join('') + '</div>' : '';
|
||||
// C4 top-risks-list (max 5).
|
||||
const topRisks = (data.topRisks || []).slice(0, 5);
|
||||
const topRisksHtml = topRisks.length ? '<section class="top-risks">' +
|
||||
'<h4 class="top-risks__heading">Top-risikoer</h4>' +
|
||||
topRisks.map(function (r, i) {
|
||||
const sev = r.severity || sevForScore(r.score);
|
||||
return '<div class="top-risk" data-severity="' + escapeAttr(sev) + '">' +
|
||||
'<span class="top-risk__rank">' + (i + 1) + '</span>' +
|
||||
'<span class="top-risk__desc">' + escapeHtml(r.description || '') + '</span>' +
|
||||
'<span class="top-risk__score">' + escapeHtml(String(r.score || 0)) + '</span>' +
|
||||
const smallMultiplesHtml = cats.length ? '<section class="small-multiples-section">' +
|
||||
'<h3>Posture per OWASP-kategori</h3>' +
|
||||
'<div class="small-multiples">' + cats.map(function (c) {
|
||||
const grade = c.grade || '';
|
||||
const fillPct = Math.max(0, Math.min(100, ((Number(c.score) || 0) / 5) * 100));
|
||||
return '<div class="sm-card">' +
|
||||
'<div class="sm-card__header">' +
|
||||
'<span class="sm-card__name">' + escapeHtml(c.name || '') + '</span>' +
|
||||
'<span class="sm-card__grade" data-grade="' + escapeAttr(grade) + '">' + escapeHtml(grade) + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="sm-card__bar"><div class="sm-card__bar-fill" style="width: ' + fillPct.toFixed(0) + '%"></div></div>' +
|
||||
'<span class="sm-card__status">Score ' + escapeHtml(String(c.score || 0)) + ' / 5</span>' +
|
||||
'</div>';
|
||||
}).join('') + '</section>' : '';
|
||||
}).join('') + '</div>' +
|
||||
'</section>' : '';
|
||||
// C4 top-risks-list (max 5) — som <ol class="top-risks"> inne i .card.
|
||||
const topRisks = (data.topRisks || []).slice(0, 5);
|
||||
const topRisksHtml = topRisks.length ? '<div class="card">' +
|
||||
'<h2>Topp-risikoer</h2>' +
|
||||
'<ol class="top-risks">' +
|
||||
topRisks.map(function (r, i) {
|
||||
const sev = r.severity || sevForScore(r.score);
|
||||
return '<li class="top-risk" data-severity="' + escapeAttr(sev) + '">' +
|
||||
'<span class="top-risk__rank">' + (i + 1) + '</span>' +
|
||||
'<span class="top-risk__desc">' + escapeHtml(r.description || '') + '</span>' +
|
||||
'<span class="top-risk__score">' + escapeHtml(String(r.score || 0)) + '</span>' +
|
||||
'</li>';
|
||||
}).join('') +
|
||||
'</ol>' +
|
||||
'</div>' : '';
|
||||
// B6 residual-pair (når data.residualPair finnes).
|
||||
const rp = data.residualPair;
|
||||
let residualHtml = '';
|
||||
|
|
@ -3790,7 +3817,7 @@
|
|||
'</div>';
|
||||
}
|
||||
const findingsHtml = renderFindingsBlock(data.findings || [], 'Sikkerhetsfunn');
|
||||
const body = matrixHtml + radarHtml + smallMultiplesHtml + topRisksHtml + residualHtml + findingsHtml;
|
||||
const body = layoutHtml + smallMultiplesHtml + topRisksHtml + residualHtml + findingsHtml;
|
||||
// Utvid matrix-risk-6x5-keyStats med RESTRISIKO når residualPair finnes.
|
||||
const baseStats = inferKeyStats(data, 'matrix-risk-6x5');
|
||||
const stats = (data.keyStats || (rp && rp.after
|
||||
|
|
@ -3818,21 +3845,34 @@
|
|||
if (n >= 4) return 'medium';
|
||||
return 'low';
|
||||
};
|
||||
const matrixHtml = renderMatrixHtml(data, 5);
|
||||
const radarHtml = renderRadarSvg(data.radar_axes || []);
|
||||
// C4 top-risks-list (max 5).
|
||||
// v1.14.0 sesjon 3: speil renderSecurity-pattern. Matrix + radar i .ros-layout
|
||||
// (1fr 320px), top-risks + recommendation i .summary-grid (1.4fr 1fr).
|
||||
const layoutHtml = '<div class="ros-layout">' +
|
||||
'<div class="card" style="padding: var(--space-6)">' +
|
||||
'<h2>5×5 Risikomatrise</h2>' +
|
||||
renderMatrixHtml(data, 5) +
|
||||
'</div>' +
|
||||
'<aside class="card">' +
|
||||
'<h3>Risiko-radar</h3>' +
|
||||
renderRadarSvg(data.radar_axes || []) +
|
||||
'</aside>' +
|
||||
'</div>';
|
||||
// C4 top-risks-list (max 5) — som <ol class="top-risks"> inne i .card.
|
||||
const topRisks = (data.topRisks || []).slice(0, 5);
|
||||
const topRisksHtml = topRisks.length ? '<section class="top-risks">' +
|
||||
'<h4 class="top-risks__heading">Top-risikoer</h4>' +
|
||||
topRisks.map(function (r, i) {
|
||||
const sev = r.severity || sevForScore(r.score);
|
||||
const scoreLabel = r.score ? String(r.score) : (r.severity || '—').toUpperCase();
|
||||
return '<div class="top-risk" data-severity="' + escapeAttr(sev) + '">' +
|
||||
'<span class="top-risk__rank">' + (i + 1) + '</span>' +
|
||||
'<span class="top-risk__desc">' + escapeHtml(r.description || '') + '</span>' +
|
||||
'<span class="top-risk__score">' + escapeHtml(scoreLabel) + '</span>' +
|
||||
'</div>';
|
||||
}).join('') + '</section>' : '';
|
||||
const topRisksCardHtml = topRisks.length ? '<div class="card">' +
|
||||
'<h2>Topp-risikoer</h2>' +
|
||||
'<ol class="top-risks">' +
|
||||
topRisks.map(function (r, i) {
|
||||
const sev = r.severity || sevForScore(r.score);
|
||||
const scoreLabel = r.score ? String(r.score) : (r.severity || '—').toUpperCase();
|
||||
return '<li class="top-risk" data-severity="' + escapeAttr(sev) + '">' +
|
||||
'<span class="top-risk__rank">' + (i + 1) + '</span>' +
|
||||
'<span class="top-risk__desc">' + escapeHtml(r.description || '') + '</span>' +
|
||||
'<span class="top-risk__score">' + escapeHtml(scoreLabel) + '</span>' +
|
||||
'</li>';
|
||||
}).join('') +
|
||||
'</ol>' +
|
||||
'</div>' : '';
|
||||
// B6 residual-pair (gjenbruker mønster fra Dpia / Security).
|
||||
const rp = data.residualPair;
|
||||
let residualHtml = '';
|
||||
|
|
@ -3861,12 +3901,18 @@
|
|||
}
|
||||
// D5 recommendation-card.
|
||||
const rec = data.recommendation || '';
|
||||
const recommendationHtml = rec ? '<aside class="recommendation-card">' +
|
||||
const recommendationCardHtml = rec ? '<aside class="recommendation-card">' +
|
||||
'<span class="recommendation-card__label">Anbefaling</span>' +
|
||||
'<p class="recommendation-card__body">' + escapeHtml(rec).replace(/\n/g, '<br>') + '</p>' +
|
||||
'</aside>' : '';
|
||||
// Top-risks + recommendation i .summary-grid (1.4fr 1fr) per Anthropic-ref ros-lier.
|
||||
// Hvis bare en av delene finnes, fyll andre kolonne med tom div for å bevare grid.
|
||||
const summaryGridHtml = (topRisksCardHtml || recommendationCardHtml) ? '<div class="summary-grid">' +
|
||||
(topRisksCardHtml || '<div></div>') +
|
||||
(recommendationCardHtml || '<div></div>') +
|
||||
'</div>' : '';
|
||||
const threatsHtml = renderThreatsTable(data.threats);
|
||||
const body = matrixHtml + radarHtml + topRisksHtml + residualHtml + threatsHtml + recommendationHtml;
|
||||
const body = layoutHtml + summaryGridHtml + residualHtml + threatsHtml;
|
||||
// Utvid matrix-risk-keyStats med RESTRISIKO når residualPair finnes
|
||||
// (samme mønster som renderDpia).
|
||||
const baseStats = inferKeyStats(data, 'matrix-risk');
|
||||
|
|
|
|||
|
|
@ -198,11 +198,11 @@ fi
|
|||
# -------------------------------------------------------
|
||||
# 12. Design-system CSS-klasse-bruk (Tier 1+2+3)
|
||||
# -------------------------------------------------------
|
||||
# v1.13.0 fix (B1): .findings outer-wrapper bevisst fjernet — DS' .findings er
|
||||
# grid 360px+1fr (list+detail-panel), men playground bruker bare list-kolonnen.
|
||||
# Asserterer .findings__list (BEM-list) i stedet for å bekrefte at findings-
|
||||
# strukturen fortsatt er i bruk uten at vi misbruker grid-containeren.
|
||||
DS_CLASSES=".pyramide .matrix .radar .findings__list .distribution .critique-card .capability-matrix .aiact-timeline .tracks .error-summary .guide-panel .expansion .form-progress"
|
||||
# v1.14.0 sesjon 3: .findings__list-wrapper fjernet sammen med .report-meta-band-aid.
|
||||
# renderFindingsBlock bruker nå <section class="findings-section"> + <ul class="findings__items
|
||||
# findings__items--standalone">. Asserterer .findings__items (BEM-list-items) i stedet —
|
||||
# bekrefter at findings-strukturen fortsatt er i bruk uten at vi misbruker grid-containeren.
|
||||
DS_CLASSES=".pyramide .matrix .radar .findings__items .distribution .critique-card .capability-matrix .aiact-timeline .tracks .error-summary .guide-panel .expansion .form-progress"
|
||||
for cls in $DS_CLASSES; do
|
||||
# Match som klassen i class="..." eller selektor — søk på klassenavnet uten leading dot
|
||||
bare="${cls#.}"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue