Fix SDLC delivery rendering and regenerate package
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
@page {
|
@page {
|
||||||
size: A4;
|
size: A4;
|
||||||
margin: 18mm 16mm 28mm;
|
margin: 18mm 16mm 20mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
@@ -64,7 +64,7 @@ body { margin: 0; }
|
|||||||
font-size: 15pt;
|
font-size: 15pt;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
flex: 0 1 72mm;
|
flex: 0 1 72mm;
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: break-word;
|
||||||
min-width: 52mm;
|
min-width: 52mm;
|
||||||
padding: 4mm 6mm;
|
padding: 4mm 6mm;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -80,7 +80,7 @@ body { margin: 0; }
|
|||||||
.document-control th,
|
.document-control th,
|
||||||
.document-control td {
|
.document-control td {
|
||||||
border: .5pt solid var(--brn-line);
|
border: .5pt solid var(--brn-line);
|
||||||
padding: 2.2mm 3mm;
|
padding: 2mm 3mm;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
@@ -88,43 +88,75 @@ body { margin: 0; }
|
|||||||
.document-control th {
|
.document-control th {
|
||||||
background: var(--brn-pale-blue);
|
background: var(--brn-pale-blue);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
width: 26%;
|
white-space: nowrap;
|
||||||
|
width: 1%;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3 { break-after: avoid; color: var(--brn-ink); }
|
h1, h2, h3 { break-after: avoid; color: var(--brn-ink); }
|
||||||
h1 { font-size: 18pt; margin: 0 0 5mm; }
|
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; }
|
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: 6mm 0 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) {
|
table:not(.document-control) {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
border: .75pt solid #718096;
|
border: .75pt solid #718096;
|
||||||
|
break-inside: auto;
|
||||||
font-size: 9pt;
|
font-size: 9pt;
|
||||||
margin: 0 0 5mm;
|
margin: 0 0 5mm;
|
||||||
table-layout: fixed;
|
table-layout: auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
table:not(.document-control) th,
|
table:not(.document-control) th,
|
||||||
table:not(.document-control) td {
|
table:not(.document-control) td {
|
||||||
border: .5pt solid var(--brn-line);
|
border: .5pt solid var(--brn-line);
|
||||||
overflow-wrap: anywhere;
|
break-inside: avoid;
|
||||||
padding: 2mm;
|
overflow-wrap: break-word;
|
||||||
|
padding: 1.6mm 2mm;
|
||||||
|
text-align: left;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
word-break: normal;
|
word-break: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
table:not(.document-control) th { background: var(--brn-pale-blue); font-weight: 700; }
|
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; }
|
thead { display: table-header-group; }
|
||||||
tr { break-inside: avoid; }
|
tr { break-inside: avoid; }
|
||||||
|
|
||||||
.approval-block { break-inside: avoid; margin-top: 9mm; }
|
.approval-block__person { break-inside: avoid; margin: 0 0 6mm; }
|
||||||
.approval-block__person { margin: 5mm 0; min-height: 21mm; }
|
.approval-block__person h3 { margin: 0 0 2mm; }
|
||||||
.approval-block__line { border-bottom: .5pt solid var(--brn-ink); display: inline-block; min-width: 70mm; }
|
|
||||||
|
.approval-block__line {
|
||||||
|
border-bottom: .5pt solid var(--brn-ink);
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 68mm;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
.approval-fields {
|
.approval-fields {
|
||||||
line-height: 1.62;
|
line-height: 1.55;
|
||||||
margin: 0 0 8mm;
|
margin: 0;
|
||||||
padding: 2.5mm 0 4mm;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ const escapeHtml = (value) => value
|
|||||||
|
|
||||||
const renderInline = (value) => escapeHtml(value)
|
const renderInline = (value) => escapeHtml(value)
|
||||||
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
.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] ?? '');
|
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));
|
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 = '') {
|
function renderTable(lines, className = '') {
|
||||||
const rows = lines.map(tableCells);
|
const rows = lines.map(tableCells);
|
||||||
const header = rows[0];
|
const header = rows[0];
|
||||||
const data = isSeparator(rows[1] ?? []) ? rows.slice(2) : rows.slice(1);
|
const hasSeparator = isSeparator(rows[1] ?? []);
|
||||||
const cells = (tag, row) => row.map((cell) => `<${tag}>${renderInline(cell)}</${tag}>`).join('');
|
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>`;
|
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;
|
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 = [];
|
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++]);
|
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 approvalClass = paragraph.some((line) => /^(Name|Role|Signature|Date|Position|Company|Project roles|Decision):/.test(line.trimEnd())) ? ' class="approval-fields"' : '';
|
const isApproval = paragraph.some((line) => /^(Name|Role|Signature|Date|Position|Company|Project roles|Decision):/.test(line.trimEnd()));
|
||||||
output.push(`<p${approvalClass}>${renderParagraph(paragraph)}</p>`);
|
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');
|
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