feat(messages): word diff of the last edit on "(edited)" hover (#144)
CI / Build & Quality Checks (push) Successful in 1m40s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 8s
CI / Trigger Desktop Build (push) Successful in 7s
CI / Playwright smoke (e2e) (push) Canceled after 1m37s
CI / Build & Quality Checks (push) Successful in 1m40s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 8s
CI / Trigger Desktop Build (push) Successful in 7s
CI / Playwright smoke (e2e) (push) Canceled after 1m37s
Hovering or focusing "(edited)" shows a tooltip with only the most recent edit as a word diff — removed words struck, added words bold — plus a +N/−N summary. Clicking still opens the full history viewer. On touch, a long-press on the label shows the same diff as a popout (a plain tap opens the viewer; the message's own long-press action sheet is not triggered). utils/wordDiff.ts is a unit-tested LCS over words that ignores whitespace- only changes and gives up past 400 words. Only plain-text bodies are diffed; formatted edits fall back to the viewer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import { describe, it } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { countChanges, wordDiff, WORD_DIFF_LIMIT } from './wordDiff';
|
||||
|
||||
const render = (tokens: ReturnType<typeof wordDiff>) =>
|
||||
tokens
|
||||
?.map((t) =>
|
||||
t.op === 'same'
|
||||
? t.text
|
||||
: `${t.op === 'add' ? '+' : '-'}[${t.text.trim()}]${t.text.endsWith(' ') ? ' ' : ''}`,
|
||||
)
|
||||
.join('');
|
||||
|
||||
describe('wordDiff', () => {
|
||||
it('returns undefined for identical text', () => {
|
||||
assert.equal(wordDiff('hello world', 'hello world'), undefined);
|
||||
});
|
||||
|
||||
it('marks a replaced word', () => {
|
||||
assert.equal(render(wordDiff('see you at 5', 'see you at 6')), 'see you at -[5]+[6]');
|
||||
});
|
||||
|
||||
it('marks insertions and deletions in place', () => {
|
||||
assert.equal(
|
||||
render(wordDiff('the quick fox', 'the quick brown fox jumps')),
|
||||
'the quick +[brown] fox +[jumps]',
|
||||
);
|
||||
assert.equal(render(wordDiff('a b c d', 'a d')), 'a -[b c] d');
|
||||
});
|
||||
|
||||
it('ignores whitespace-only changes', () => {
|
||||
assert.equal(wordDiff('a b', 'a b'), undefined);
|
||||
assert.equal(wordDiff('a b\n', 'a b'), undefined);
|
||||
});
|
||||
|
||||
it('handles fully different text', () => {
|
||||
assert.deepEqual(wordDiff('one', 'two'), [
|
||||
{ op: 'del', text: 'one' },
|
||||
{ op: 'add', text: 'two' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('gives up past the word limit', () => {
|
||||
const long = Array.from({ length: WORD_DIFF_LIMIT }, (_, i) => `w${i}`).join(' ');
|
||||
assert.equal(wordDiff(long, `${long} extra`), undefined);
|
||||
});
|
||||
|
||||
it('counts changed words', () => {
|
||||
const d = wordDiff('a b c', 'a x y z c');
|
||||
assert.ok(d);
|
||||
assert.deepEqual(countChanges(d), { added: 3, removed: 1 });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user