Fix SDLC delivery rendering and regenerate package
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 18mm 16mm 28mm;
|
||||
margin: 18mm 16mm 20mm;
|
||||
}
|
||||
|
||||
:root {
|
||||
@@ -64,7 +64,7 @@ body { margin: 0; }
|
||||
font-size: 15pt;
|
||||
font-weight: 500;
|
||||
flex: 0 1 72mm;
|
||||
overflow-wrap: anywhere;
|
||||
overflow-wrap: break-word;
|
||||
min-width: 52mm;
|
||||
padding: 4mm 6mm;
|
||||
text-align: center;
|
||||
@@ -80,7 +80,7 @@ body { margin: 0; }
|
||||
.document-control th,
|
||||
.document-control td {
|
||||
border: .5pt solid var(--brn-line);
|
||||
padding: 2.2mm 3mm;
|
||||
padding: 2mm 3mm;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
@@ -88,43 +88,75 @@ body { margin: 0; }
|
||||
.document-control th {
|
||||
background: var(--brn-pale-blue);
|
||||
font-weight: 700;
|
||||
width: 26%;
|
||||
white-space: nowrap;
|
||||
width: 1%;
|
||||
}
|
||||
|
||||
h1, h2, h3 { break-after: avoid; color: var(--brn-ink); }
|
||||
h1 { font-size: 18pt; margin: 0 0 5mm; }
|
||||
h2 { border-left: 4pt solid var(--brn-blue); font-size: 14pt; margin: 9mm 0 4mm; padding-left: 3mm; }
|
||||
h3 { color: var(--brn-blue); font-size: 11.5pt; margin: 6mm 0 3mm; }
|
||||
h2 { border-left: 4pt solid var(--brn-blue); font-size: 14pt; margin: 6mm 0 3mm; padding-left: 3mm; }
|
||||
h3 { color: var(--brn-blue); font-size: 11.5pt; margin: 4mm 0 2mm; }
|
||||
|
||||
p { margin: 0 0 3.5mm; }
|
||||
p { margin: 0 0 3mm; }
|
||||
|
||||
ul, ol { margin: 0 0 3.5mm; padding-left: 7mm; }
|
||||
li { margin: 0 0 1mm; }
|
||||
|
||||
code {
|
||||
font-family: "DejaVu Sans Mono", "Courier New", monospace;
|
||||
font-size: .9em;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
table:not(.document-control) {
|
||||
border-collapse: collapse;
|
||||
border: .75pt solid #718096;
|
||||
break-inside: auto;
|
||||
font-size: 9pt;
|
||||
margin: 0 0 5mm;
|
||||
table-layout: fixed;
|
||||
table-layout: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table:not(.document-control) th,
|
||||
table:not(.document-control) td {
|
||||
border: .5pt solid var(--brn-line);
|
||||
overflow-wrap: anywhere;
|
||||
padding: 2mm;
|
||||
break-inside: avoid;
|
||||
overflow-wrap: break-word;
|
||||
padding: 1.6mm 2mm;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
table:not(.document-control) th { background: var(--brn-pale-blue); font-weight: 700; }
|
||||
|
||||
/* Column alignment carried over from the Markdown separator row. */
|
||||
table:not(.document-control) th.a-right,
|
||||
table:not(.document-control) td.a-right { text-align: right; }
|
||||
table:not(.document-control) th.a-center,
|
||||
table:not(.document-control) td.a-center { text-align: center; }
|
||||
|
||||
/* Short columns (IDs, dates, counts) shrink to their content instead of
|
||||
taking an equal share of the table width. */
|
||||
table:not(.document-control) th.tight,
|
||||
table:not(.document-control) td.tight { white-space: nowrap; width: 1%; }
|
||||
|
||||
thead { display: table-header-group; }
|
||||
tr { break-inside: avoid; }
|
||||
|
||||
.approval-block { break-inside: avoid; margin-top: 9mm; }
|
||||
.approval-block__person { margin: 5mm 0; min-height: 21mm; }
|
||||
.approval-block__line { border-bottom: .5pt solid var(--brn-ink); display: inline-block; min-width: 70mm; }
|
||||
.approval-block__person { break-inside: avoid; margin: 0 0 6mm; }
|
||||
.approval-block__person h3 { margin: 0 0 2mm; }
|
||||
|
||||
.approval-block__line {
|
||||
border-bottom: .5pt solid var(--brn-ink);
|
||||
display: inline-block;
|
||||
min-width: 68mm;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
.approval-fields {
|
||||
line-height: 1.62;
|
||||
margin: 0 0 8mm;
|
||||
padding: 2.5mm 0 4mm;
|
||||
line-height: 1.55;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ const escapeHtml = (value) => value
|
||||
|
||||
const renderInline = (value) => escapeHtml(value)
|
||||
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
||||
.replace(/`([^`]+)`/g, '<code>$1</code>');
|
||||
.replace(/`([^`]+)`/g, '<code>$1</code>')
|
||||
.replace(/_{3,}/g, '<span class="approval-block__line"></span>')
|
||||
.replace(/\\([\\*_`~])/g, '$1');
|
||||
|
||||
const applyTemplate = (template, values) => template.replace(/{{(\w+)}}/g, (_, key) => values[key] ?? '');
|
||||
|
||||
@@ -30,11 +32,41 @@ function isSeparator(cells) {
|
||||
return cells.every((cell) => /^:?-{3,}:?$/.test(cell));
|
||||
}
|
||||
|
||||
const TIGHT_COLUMN_MAX = 12;
|
||||
|
||||
function columnAlignment(separatorCells) {
|
||||
return separatorCells.map((cell) => {
|
||||
const left = cell.startsWith(':');
|
||||
const right = cell.endsWith(':');
|
||||
if (left && right) return 'a-center';
|
||||
if (right) return 'a-right';
|
||||
return '';
|
||||
});
|
||||
}
|
||||
|
||||
function renderTable(lines, className = '') {
|
||||
const rows = lines.map(tableCells);
|
||||
const header = rows[0];
|
||||
const data = isSeparator(rows[1] ?? []) ? rows.slice(2) : rows.slice(1);
|
||||
const cells = (tag, row) => row.map((cell) => `<${tag}>${renderInline(cell)}</${tag}>`).join('');
|
||||
const hasSeparator = isSeparator(rows[1] ?? []);
|
||||
const align = hasSeparator ? columnAlignment(rows[1]) : header.map(() => '');
|
||||
const data = hasSeparator ? rows.slice(2) : rows.slice(1);
|
||||
|
||||
// A column whose longest cell is short (IDs, dates, counts, statuses) is
|
||||
// marked "tight" so auto table layout shrinks it to its content instead of
|
||||
// handing every column an equal share of the page width.
|
||||
const widest = header.map((_, column) => Math.max(
|
||||
(header[column] ?? '').length,
|
||||
...data.map((row) => (row[column] ?? '').replace(/[`*]/g, '').length)
|
||||
));
|
||||
const classFor = (column) => [
|
||||
align[column] ?? '',
|
||||
widest[column] <= TIGHT_COLUMN_MAX ? 'tight' : ''
|
||||
].filter(Boolean).join(' ');
|
||||
|
||||
const cells = (tag, row) => row.map((cell, column) => {
|
||||
const attr = classFor(column);
|
||||
return `<${tag}${attr ? ` class="${attr}"` : ''}>${renderInline(cell)}</${tag}>`;
|
||||
}).join('');
|
||||
return `<table${className ? ` class="${className}"` : ''}><thead><tr>${cells('th', header)}</tr></thead><tbody>${data.map((row) => `<tr>${cells('td', row)}</tr>`).join('')}</tbody></table>`;
|
||||
}
|
||||
|
||||
@@ -79,10 +111,23 @@ function markdownToHtml(markdown) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (/^\d+\.\s+/.test(line)) {
|
||||
const items = [];
|
||||
while (index < lines.length && /^\d+\.\s+/.test(lines[index])) items.push(`<li>${renderInline(lines[index++].replace(/^\d+\.\s+/, ''))}</li>`);
|
||||
output.push(`<ol>${items.join('')}</ol>`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const paragraph = [];
|
||||
while (index < lines.length && lines[index].trim() && !isTableLine(lines[index]) && !/^(#{1,3})\s+/.test(lines[index]) && !/^[-*]\s+/.test(lines[index])) paragraph.push(lines[index++]);
|
||||
const approvalClass = paragraph.some((line) => /^(Name|Role|Signature|Date|Position|Company|Project roles|Decision):/.test(line.trimEnd())) ? ' class="approval-fields"' : '';
|
||||
output.push(`<p${approvalClass}>${renderParagraph(paragraph)}</p>`);
|
||||
while (index < lines.length && lines[index].trim() && !isTableLine(lines[index]) && !/^(#{1,3})\s+/.test(lines[index]) && !/^[-*]\s+/.test(lines[index]) && !/^\d+\.\s+/.test(lines[index])) paragraph.push(lines[index++]);
|
||||
const isApproval = paragraph.some((line) => /^(Name|Role|Signature|Date|Position|Company|Project roles|Decision):/.test(line.trimEnd()));
|
||||
const html = `<p${isApproval ? ' class="approval-fields"' : ''}>${renderParagraph(paragraph)}</p>`;
|
||||
// Keep a signatory's heading and fields on one page.
|
||||
if (isApproval && /^<h3>/.test(output[output.length - 1] ?? '')) {
|
||||
output.push(`<div class="approval-block__person">${output.pop()}${html}</div>`);
|
||||
} else {
|
||||
output.push(html);
|
||||
}
|
||||
}
|
||||
|
||||
return output.join('\n');
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user