morphit/apps/web/scripts/identity-label-truncation-smoke.ts
2026-08-07 14:23:23 -07:00

158 lines
7.1 KiB
TypeScript

#!/usr/bin/env tsx
/**
* Morphit — IdentityLabel truncation smoke (v1.7.7, t.txt #9).
*
* Ken photographed a chat card where "Super loong display name" ran past the
* card edge into the Restore button, while the RE: line directly beneath it
* truncated at "RE: I'm bu…". Two lines in the same card, disagreeing about how
* much room they had.
*
* THE CAUSE, and why it is worth a guard rather than a one-line fix and a shrug:
* `IdentityLabel`'s root was `inline-flex` with no `min-w-0` / `max-w-full`.
* That is TWO independent reasons to refuse to shrink — an inline-flex box sizes
* to its content, AND a flex item defaults to `min-width: auto` ("never smaller
* than my content"). So the root ignored the width its parent handed it, and
* every `min-w-0 truncate` INSIDE the component never saw a constraint to
* truncate against. The truncation wasn't missing. It was INERT.
*
* That is the whole trap: the fix looks present in the markup. You can read
* `truncate` on the name span, believe the component truncates, and be wrong —
* because correctness lives in the CHAIN, not in any single element. One
* `min-width: auto` anywhere above pins everything below it.
*
* IdentityLabel renders in 18 files. A regression here is silent (text simply
* overflows on a narrow screen; nothing errors, no test fails), and only ever
* shows up in a screenshot from a phone.
*/
import { readFileSync } from 'node:fs';
import { resolve, dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..', '..');
const read = (p: string): string => readFileSync(join(ROOT, p), 'utf8');
let pass = 0;
let fail = 0;
const check = (name: string, ok: boolean, detail = ''): void => {
if (ok) { pass++; console.log(` \u2713 ${name}`); }
else { fail++; console.log(` \u2717 ${name}${detail ? `\n ${detail}` : ''}`); }
};
const id = read('apps/web/src/lib/components/IdentityLabel.svelte');
const page = read('apps/web/src/routes/[lang]/chat/+page.svelte');
// ── the shrink chain, top to bottom ─────────────────────────────────
check(
'1 the inbox card gives IdentityLabel a bounded column',
/<div class="flex min-w-0 flex-1 flex-col gap-0\.5">/.test(page),
'without min-w-0 here the column itself refuses to shrink and nothing below can truncate'
);
check(
'2 IdentityLabel root can shrink (min-w-0 + max-w-full on the inline-flex)',
/<span class="group inline-flex min-w-0 max-w-full items-center/.test(id),
'inline-flex sizes to content AND flex items default to min-width:auto — both must be overridden'
);
check(
'3 the name anchor can shrink',
/<a[\s\S]{0,80}?class="inline-flex min-w-0 max-w-full items-baseline/.test(id)
);
check(
'4 the no-href name span can shrink',
/<span class="inline-flex min-w-0 max-w-full items-baseline">/.test(id)
);
check(
'5 the name itself still truncates',
/<span class="min-w-0 truncate">\{@render nameText\(\)\}<\/span>/.test(id),
'this line looked correct the whole time it was doing nothing'
);
check(
'6 the key/handle branch also truncates',
/<span class="inline-flex min-w-0 flex-col leading-tight">[\s\S]{0,120}?<span class="truncate">/.test(id)
);
// ── the RE: line must agree with the name line ──────────────────────
check(
'7 the RE: row can shrink',
/<div class="flex min-w-0 items-baseline gap-1 text-xs/.test(page)
);
check(
'8 the order title truncates rather than overflowing',
/<span class="min-w-0 truncate">\{orderTitle\}<\/span>/.test(page)
);
check(
'9 the status badge never shrinks (it is short and must stay legible)',
/<span class="flex-none">\(\{orderStatusLabel\(convo\.order\)\}\)<\/span>/.test(page)
);
// ── t.txt #9: the feedback row ──────────────────────────────────────
check(
'10 the feedback row does NOT show the "I rated @x:" label',
!/<span class="font-medium"\s*>\{\$_\('profile\.given_rated'/.test(page),
'it wrapped to a second line, pushed the stars down, and starved the comment of width'
);
check(
'11 …but a screen reader still gets the sentence',
/class="sr-only"[\s\S]{0,120}?profile\.given_rated/.test(page),
'removing a VISUAL label must not remove the meaning'
);
check(
'12 the stars never shrink',
/<span class="flex-none text-morphit-emerald" aria-hidden="true"/.test(page),
'flex-none is what hands the leftover width to the comment'
);
check(
'13 the feedback row does not wrap (it truncates at the edge instead)',
/<div class="flex items-center gap-2 text-xs text-ink-500 dark:text-ink-400">/.test(page) &&
!/flex flex-wrap items-baseline gap-1 text-xs text-ink-500/.test(page),
'flex-wrap let the row grow a second line instead of the comment truncating honestly'
);
check(
'14 the comment still truncates',
/<span[^>]*\bclass="min-w-0 truncate text-ink-500 dark:text-ink-400"[^>]*>\{fb\.record\.comment\}<\/span/.test(page)
);
// ── t.txt #6: review cards ────────────────────────────────────────
// [KEN]: "no need to show the (@username) in parenthesis, and be sure to
// truncate the display name line since it is too wide for mobile. the layout of
// those feedback/review cards on mobile is attrocious."
const profile = read('apps/web/src/routes/[lang]/[x+40][account=account]/+page.svelte');
check(
'15 review cards do NOT repeat the handle in parentheses',
!/showHandleAfterName/.test(profile),
'it cost half a phone-width to repeat what the key underneath already answers better'
);
check(
'16 …and the prop that drew it is gone entirely, not just unused',
!/showHandleAfterName/.test(id.replace(/<!--[\s\S]*?-->/g, '')),
'a prop with no callers is cruft the compiler cannot see'
);
check(
'17 the reviewer is still identified — by KEY, which is the real identity',
/publicKeyString=\{reviewerProfileMap\[fb\.reviewer\]\?\.posting_pubkey \?\? undefined\}/.test(
profile
),
'a display name is user-chosen and not unique; removing the handle is only safe because the key stays'
);
check(
'18 …and the label still links to the account',
/href=\{lp\(`\/@\$\{fb\.reviewer\}`\)\}/.test(profile),
'the handle is one tap away, which is where it belongs on a narrow screen'
);
check(
'19 the key-bearing branch stacks name over key and truncates the name',
/<span class="inline-flex min-w-0 flex-col leading-tight">\s*<span class="truncate">\{@render nameText\(\)\}<\/span>/.test(
id
),
'this is the branch review cards take (publicKeyString is set); min-w-0 on the COLUMN is what lets the name shrink'
);
check(
'20 the key itself is truncated head…tail, so it never drives the width',
/truncatePublicKey/.test(id),
'an untruncated BLT key is wider than most display names and would keep the column wide on its own'
);
console.log('');
if (fail === 0) console.log(`\u2713 all ${pass} identity-label-truncation checks passed`);
else { console.error(`\u2717 ${fail} of ${pass + fail} identity-label-truncation checks FAILED`); process.exit(1); }