fix: short stacked labels, declared lookup hash

This commit is contained in:
Abdessamad Derraz committed 2026-10-24 00:47:51 +02:00
1 parent 8667956a0b
commit b967cb8d04
2 files changed
+73 -6

No files matched your search

+11 -2
View File
@@ -16,6 +16,10 @@
// so the row is wider than a phone at any length (NetherSX2's File,
// Required, Hash summary measured 604 px in a 358 px column).
//
// A stacked cell is labelled with its column name, or with the header's
// `title` when the table's writer gives one: "Name as declared" wrapped to two
// lines in the label column, its title "Declared as" does not.
//
// A table of RECORD_ROWS rows or more gets its filter box here, above it.
// Written by site.ts once the page had painted, the box pushed the table
// down (CLS 0.066 on gaps-files-to-source). site.ts only wires it.
@@ -101,6 +105,9 @@ const headingBefore = (ctx, table, title) => {
return title || 'this table';
};
// The label a stacked cell shows: the header's title, else its text.
export const stackedLabel = (title, text) => String(title ?? '').trim() || text;
const filterBox = ({ id, heading, labels, rows }) => ({
type: 'element',
tagName: 'div',
@@ -136,7 +143,9 @@ export default function stackTables() {
const head = elements(table, 'thead')[0];
const body = elements(table, 'tbody')[0];
if (!head || !body) return;
const labels = elements(head, 'th').map((cell) => ctx.textContent(cell).trim());
const headers = elements(head, 'th');
const labels = headers.map((cell) => ctx.textContent(cell).trim());
const stacked = headers.map((cell, column) => stackedLabel(cell.properties?.title, labels[column]));
const rows = elements(body, 'tr');
const frontmatter = ctx.data.astro?.frontmatter ?? {};
if (rows.length >= RECORD_ROWS) {
@@ -153,7 +162,7 @@ export default function stackTables() {
if (kind === 'kv' || rows.length >= RECORD_ROWS) return;
for (const row of rows) {
elements(row, 'td').forEach((cell, column) => {
if (labels[column] && cell.properties?.dataLabel === undefined) ctx.setProperty(cell, 'dataLabel', labels[column]);
if (stacked[column] && cell.properties?.dataLabel === undefined) ctx.setProperty(cell, 'dataLabel', stacked[column]);
});
}
},
+62 -4
View File
@@ -8,8 +8,11 @@
const HASH_LENGTHS: Record<number, string> = { 8: 'CRC32', 32: 'MD5', 40: 'SHA1', 64: 'SHA256' };
// One line of api/v1/lookup/. `system_name` is read when a record carries it.
// `algorithm` is the hash the value was found under, `declared` the hash
// type the setup or profile wrote for that entry.
type Item = {
algorithm: string;
declared?: string;
layer: string;
name?: string;
value: string;
@@ -111,10 +114,12 @@ const filterTable = (table: HTMLTableElement) => {
// A stacked table of many rows leaves its column names out of the HTML
// (rehype-stack-tables.mjs): each cell gets its own here, as the table nears
// the screen. The name sits in the left room every stacked cell keeps, so
// writing it moves nothing.
// writing it moves nothing. A header's `title` is its short stacked name.
const stackedLabel = (cell: Element) => cell.getAttribute('title')?.trim() || (cell.textContent?.trim() ?? '');
const labelCells = (table: HTMLTableElement) => {
if (table.dataset.rbStack !== 'true') return;
const labels = [...table.querySelectorAll('thead th')].map((cell) => cell.textContent?.trim() ?? '');
const labels = [...table.querySelectorAll('thead th')].map(stackedLabel);
for (const row of table.tBodies[0]?.rows ?? []) {
[...row.cells].forEach((cell, column) => {
if (labels[column] && !cell.dataset.label) cell.dataset.label = labels[column];
@@ -173,6 +178,49 @@ const enhanceTables = () => {
});
};
// Paths
// A long path breaks at its separators, never inside a word. The build
// writes the `wbr` of a page's first BREAK_BUDGET separators
// (rehype-short-code.mjs), since each is an element of the page. The others
// are written here, as their code nears the screen. Same rule: inline code
// over 30 characters, a break after each `/` and `\` but the last character.
const LONG_CODE = 30;
const SEPARATORS = /([/\\])(?!$)/g;
const SEPARATOR = /[/\\](?!$)/;
const unbrokenPath = (code: Element) =>
!code.closest('pre') &&
!code.querySelector('wbr') &&
[...code.childNodes].every((child) => child.nodeType === Node.TEXT_NODE) &&
(code.textContent ?? '').length > LONG_CODE &&
SEPARATOR.test(code.textContent ?? '');
const breakPath = (code: Element) => {
if (!unbrokenPath(code)) return;
const parts = (code.textContent ?? '').split(SEPARATORS).flatMap((piece, index) => [
...(piece ? [document.createTextNode(piece)] : []),
...(index % 2 ? [document.createElement('wbr')] : []),
]);
code.replaceChildren(...parts);
};
const breakPaths = () => {
const observer = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
breakPath(entry.target);
observer.unobserve(entry.target);
}
},
{ rootMargin: '400px 0px' },
);
const idle = window.requestIdleCallback ?? ((callback: () => void) => setTimeout(callback, 1));
idle(() => {
for (const code of document.querySelectorAll('.sl-markdown-content code')) {
if (unbrokenPath(code)) observer.observe(code);
}
});
};
// Hashing a file on this device: SHA1 and SHA256 by Web Crypto, MD5 and
// CRC32 here, since Web Crypto has neither. The bytes never leave the page.
@@ -381,6 +429,14 @@ const agreed = (values: (string | undefined)[]): string | undefined => {
// "system:md5", and any file that has it passes.
const anyName = (name: string) => !name || name.includes(':');
// The column name a stacked row shows where the full one wraps on a phone,
// as rehype-stack-tables.mjs reads a header's `title`.
const STACKED_LABELS: Record<string, string> = { 'Name as declared': 'Declared as' };
// "Hash matched" is the hash the setup's list writes, whatever was pasted:
// Batocera declares an MD5, so its row says MD5 when a CRC32 found it.
const declaredHash = (item: Item) => (item.declared || item.algorithm).toUpperCase();
const setupTable = (rows: { item: Item; entry?: Declared; setup: string }[]) => {
const table = document.createElement('table');
const head = table.createTHead().insertRow();
@@ -388,6 +444,7 @@ const setupTable = (rows: { item: Item; entry?: Declared; setup: string }[]) =>
const cell = document.createElement('th');
cell.scope = 'col';
cell.textContent = label;
if (STACKED_LABELS[label]) cell.title = STACKED_LABELS[label];
head.append(cell);
}
const body = table.createTBody();
@@ -399,12 +456,12 @@ const setupTable = (rows: { item: Item; entry?: Declared; setup: string }[]) =>
link(item.url, setup),
anyName(name) ? 'Any name' : code(name),
destination ? code(destination) : '-',
item.algorithm.toUpperCase(),
declaredHash(item),
entry?.required === undefined ? '-' : entry.required ? 'Yes' : 'No',
];
cells.forEach((content, column) => {
const cell = row.insertCell();
cell.dataset.label = head.cells[column].textContent!;
cell.dataset.label = stackedLabel(head.cells[column]);
cell.append(content);
});
}
@@ -892,6 +949,7 @@ const notFound = () => {
resolveFileAddress();
followMovedCard();
enhanceTables();
breakPaths();
hashSearch();
glossaryTerms();
osTabs();