fix(frontend): improve PDF zoom and diff readability
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
- PDF viewer: append #zoom=page-width to iframe src so A4 letters fill the panel width instead of leaving large grey gutters - Diff view: trim unchanged context to 4 words either side of each change, replacing long runs with '…' so edits are easy to spot Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -112,6 +112,44 @@ function personLabel(p: { firstName: string; lastName: string }): string {
|
||||
return `${p.firstName} ${p.lastName}`.trim();
|
||||
}
|
||||
|
||||
const DIFF_CONTEXT_WORDS = 4;
|
||||
|
||||
type DiffPart = { value: string; added?: boolean; removed?: boolean };
|
||||
|
||||
function trimContextParts(parts: DiffPart[]): DiffPart[] {
|
||||
return parts.flatMap((part, i) => {
|
||||
if (part.added || part.removed) return [part];
|
||||
const tokens = part.value.split(/(\s+)/).filter(Boolean);
|
||||
const wordCount = tokens.filter((t) => /\S/.test(t)).length;
|
||||
if (wordCount <= DIFF_CONTEXT_WORDS * 2) return [part];
|
||||
|
||||
function keepFirst(n: number): string {
|
||||
let count = 0;
|
||||
const out: string[] = [];
|
||||
for (const t of tokens) {
|
||||
out.push(t);
|
||||
if (/\S/.test(t) && ++count >= n) break;
|
||||
}
|
||||
return out.join('');
|
||||
}
|
||||
function keepLast(n: number): string {
|
||||
let count = 0;
|
||||
const out: string[] = [];
|
||||
for (const t of [...tokens].reverse()) {
|
||||
out.unshift(t);
|
||||
if (/\S/.test(t) && ++count >= n) break;
|
||||
}
|
||||
return out.join('');
|
||||
}
|
||||
|
||||
const isFirst = i === 0;
|
||||
const isLast = i === parts.length - 1;
|
||||
if (isFirst) return [{ value: '… ' + keepLast(DIFF_CONTEXT_WORDS) }];
|
||||
if (isLast) return [{ value: keepFirst(DIFF_CONTEXT_WORDS) + ' …' }];
|
||||
return [{ value: keepFirst(DIFF_CONTEXT_WORDS) + ' … ' + keepLast(DIFF_CONTEXT_WORDS) }];
|
||||
});
|
||||
}
|
||||
|
||||
function buildDiff(older: SnapshotDoc | null, newer: SnapshotDoc): DiffEntry[] {
|
||||
const entries: DiffEntry[] = [];
|
||||
|
||||
@@ -119,7 +157,7 @@ function buildDiff(older: SnapshotDoc | null, newer: SnapshotDoc): DiffEntry[] {
|
||||
const a = older?.[field] ?? '';
|
||||
const b = newer[field] ?? '';
|
||||
if (a === b) continue;
|
||||
const parts = diffWords(a, b);
|
||||
const parts = trimContextParts(diffWords(a, b));
|
||||
entries.push({ kind: 'text', field, label: fieldLabels[field](), parts });
|
||||
}
|
||||
|
||||
@@ -844,14 +882,14 @@ function versionLabel(v: VersionSummary, index: number): string {
|
||||
</div>
|
||||
{:else if fileUrl && doc.originalFilename.toLowerCase().endsWith('.pdf')}
|
||||
<iframe
|
||||
src={fileUrl}
|
||||
src="{fileUrl}#zoom=page-width"
|
||||
title={m.doc_preview_iframe_title()}
|
||||
class="h-full w-full border-none bg-white"
|
||||
></iframe>
|
||||
{:else if fileUrl}
|
||||
<div class="flex h-full w-full items-center justify-center overflow-auto p-8">
|
||||
<img
|
||||
src={fileUrl}
|
||||
src="{fileUrl}#zoom=page-width"
|
||||
alt={m.doc_image_alt()}
|
||||
class="max-h-full max-w-full object-contain shadow-2xl"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user