feat: emphasize update changelog sections
Render release-note headings, categories, items, and body lines as safe structured text with larger typography for section labels and compact spacing for details.
This commit is contained in:
+31
-19
@@ -7445,26 +7445,40 @@ function setupListeners() {
|
||||
}
|
||||
|
||||
// --- Update UI ---
|
||||
function _formatUpdateReleaseNotes(value) {
|
||||
const output = [];
|
||||
let pendingBlank = false;
|
||||
for (const rawLine of String(value || '').replace(/\r\n?/g, '\n').split('\n')) {
|
||||
let line = rawLine.trim();
|
||||
if (!line) {
|
||||
if (output.length > 0) pendingBlank = true;
|
||||
continue;
|
||||
}
|
||||
line = line
|
||||
.replace(/^#{1,6}\s+/, '')
|
||||
.replace(/^[-*+]\s+/, '• ')
|
||||
function _cleanUpdateReleaseText(value) {
|
||||
return String(value || '')
|
||||
.replace(/\*\*([^*]+)\*\*/g, '$1')
|
||||
.replace(/`([^`]+)`/g, '$1')
|
||||
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1');
|
||||
if (pendingBlank && output.at(-1) !== '') output.push('');
|
||||
output.push(line);
|
||||
pendingBlank = false;
|
||||
}
|
||||
|
||||
function _renderUpdateReleaseNotes(container, value) {
|
||||
const fragment = document.createDocumentFragment();
|
||||
let consumed = 0;
|
||||
for (const rawLine of String(value || '').replace(/\r\n?/g, '\n').split('\n')) {
|
||||
const line = rawLine.trim();
|
||||
if (!line) continue;
|
||||
const heading = line.match(/^(#{1,6})\s+(.+)$/);
|
||||
const bullet = line.match(/^[-*+]\s+(.+)$/);
|
||||
const className = heading
|
||||
? (heading[1].length <= 2 ? 'update-release-heading' : 'update-release-category')
|
||||
: bullet
|
||||
? 'update-release-item'
|
||||
: 'update-release-line';
|
||||
const source = heading ? heading[2] : bullet ? `• ${bullet[1]}` : line;
|
||||
const cleaned = _cleanUpdateReleaseText(source);
|
||||
const available = 2400 - consumed;
|
||||
if (available <= 0) break;
|
||||
const text = cleaned.length > available ? `${cleaned.slice(0, Math.max(0, available - 1))}…` : cleaned;
|
||||
const element = document.createElement('div');
|
||||
element.className = className;
|
||||
element.textContent = text;
|
||||
fragment.appendChild(element);
|
||||
consumed += text.length;
|
||||
if (text.endsWith('…')) break;
|
||||
}
|
||||
return output.join('\n');
|
||||
container.replaceChildren(fragment);
|
||||
return container.childElementCount > 0;
|
||||
}
|
||||
|
||||
function showUpdateBanner(info) {
|
||||
@@ -7488,9 +7502,7 @@ function showUpdateBanner(info) {
|
||||
message.hidden = false;
|
||||
}
|
||||
if (notes && notesBody) {
|
||||
const releaseNotes = _formatUpdateReleaseNotes(info.releaseNotes);
|
||||
notesBody.textContent = releaseNotes.length > 2400 ? `${releaseNotes.slice(0, 2399)}…` : releaseNotes;
|
||||
notes.hidden = !releaseNotes;
|
||||
notes.hidden = !_renderUpdateReleaseNotes(notesBody, info.releaseNotes);
|
||||
}
|
||||
if (installButton) {
|
||||
installButton.disabled = false;
|
||||
|
||||
+31
-1
@@ -2491,7 +2491,37 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
|
||||
}
|
||||
|
||||
.update-release-notes-body {
|
||||
white-space: pre-wrap;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.update-release-heading {
|
||||
margin: 2px 0 11px;
|
||||
color: var(--text);
|
||||
font-size: 14px;
|
||||
font-weight: 750;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.update-release-category {
|
||||
margin: 15px 0 7px;
|
||||
color: var(--text);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.update-release-heading + .update-release-category {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.update-release-item,
|
||||
.update-release-line {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.update-release-item {
|
||||
padding-left: 11px;
|
||||
text-indent: -9px;
|
||||
}
|
||||
|
||||
.update-progress-track {
|
||||
|
||||
@@ -179,5 +179,7 @@ test('header occupies its final geometry before asynchronous initialization', ()
|
||||
assert.match(html, /class="update-progress-footer"[\s\S]*id="updateProgressDetails"[\s\S]*id="updateProgressSize"[\s\S]*id="updateProgressSpeed"[\s\S]*id="updateProgressEta"[\s\S]*id="updateProgressText"/u);
|
||||
assert.match(css, /#updateProgressDetails\s*\{[^}]*display:\s*grid;[^}]*grid-template-columns:\s*19ch 2ch 11ch 2ch 10ch;[^}]*column-gap:\s*0;[^}]*font-variant-numeric:\s*tabular-nums;/su);
|
||||
assert.match(css, /\.update-progress-separator\s*\{[^}]*display:\s*grid;[^}]*place-items:\s*center;/su);
|
||||
assert.match(css, /\.update-release-heading\s*\{[^}]*font-size:\s*14px;[^}]*font-weight:\s*750;/su);
|
||||
assert.match(css, /\.update-release-category\s*\{[^}]*font-size:\s*13px;[^}]*font-weight:\s*700;/su);
|
||||
assert.match(css, /#updateProgressDetails\[hidden\]\s*\{[^}]*display:\s*none;/su);
|
||||
});
|
||||
|
||||
+2
-2
@@ -2447,8 +2447,8 @@ setTimeout(async () => {
|
||||
check('Update dialog offers cancel and install actions', updateDialogActions === 'Abbrechen|Jetzt installieren');
|
||||
const updateCloseColor = await wc.executeJavaScript('getComputedStyle(document.getElementById("updateCloseBtn")).color');
|
||||
check('Update dialog exposes a red close action', updateCloseColor === 'rgb(255, 133, 140)');
|
||||
const updateDialogChangelog = await wc.executeJavaScript('(() => { const title = document.querySelector(".update-release-notes-title"); const body = document.getElementById("updateReleaseNotesBody"); const titleRect = title?.getBoundingClientRect(); const bodyRect = body?.getBoundingClientRect(); return { hidden: document.getElementById("updateReleaseNotes")?.hidden, title: title?.textContent?.trim(), body: body?.textContent, gap: bodyRect && titleRect ? bodyRect.top - titleRect.bottom : null }; })()');
|
||||
check('Update dialog renders a compact normalized changelog', updateDialogChangelog.hidden === false && updateDialogChangelog.title === 'Changelog' && updateDialogChangelog.body === 'New in this version\\n\\nMenus and navigation\\n\\n• Added live language switching.\\n• Improved settings layout.' && updateDialogChangelog.gap <= 10);
|
||||
const updateDialogChangelog = await wc.executeJavaScript('(() => { const title = document.querySelector(".update-release-notes-title"); const body = document.getElementById("updateReleaseNotesBody"); const heading = body?.querySelector(".update-release-heading"); const category = body?.querySelector(".update-release-category"); const items = [...(body?.querySelectorAll(".update-release-item") || [])]; const titleRect = title?.getBoundingClientRect(); const bodyRect = body?.getBoundingClientRect(); return { hidden: document.getElementById("updateReleaseNotes")?.hidden, title: title?.textContent?.trim(), heading: heading?.textContent, headingSize: getComputedStyle(heading).fontSize, category: category?.textContent, categorySize: getComputedStyle(category).fontSize, items: items.map(item => item.textContent).join("|"), gap: bodyRect && titleRect ? bodyRect.top - titleRect.bottom : null }; })()');
|
||||
check('Update dialog renders a compact changelog with larger headings and categories', updateDialogChangelog.hidden === false && updateDialogChangelog.title === 'Changelog' && updateDialogChangelog.heading === 'New in this version' && updateDialogChangelog.headingSize === '14px' && updateDialogChangelog.category === 'Menus and navigation' && updateDialogChangelog.categorySize === '13px' && updateDialogChangelog.items === '• Added live language switching.|• Improved settings layout.' && updateDialogChangelog.gap <= 10);
|
||||
const updateDialogSize = await wc.executeJavaScript('(() => ({ width: document.querySelector(".update-dialog")?.getBoundingClientRect().width, notesHeight: document.getElementById("updateReleaseNotes")?.getBoundingClientRect().height }))()');
|
||||
check('Update dialog and changelog provide more horizontal and vertical room', updateDialogSize.width === 576 && updateDialogSize.notesHeight === 264);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user