feat(diff): copy to clipboard markdown (#187) #195
No reviewers
Labels
No labels
bug
documentation
duplicate
e-copy
e-features
e-mobile
enhancement
f-coverage
f-forensic
f-perf
f-privacy
forensic
good first issue
help wanted
infra
invalid
phase-a
phase-b
phase-c
phase-d
phase-e
phase-f
phase-g
phase-h
priority-1
priority-2
priority-3
privacy
question
v5
v6
video-hardening
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: forgejo_admin/exifcleaner-web#195
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/issue-187-copy-diff-clipboard"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Adds a Copy button to the metadata diff expansion (and every cleaned-leaf diff inside a ZIP) that writes a markdown-formatted before/after report to the clipboard. Reuses the existing
onCopyToastpipeline that previously only fired forErrorExpansion.Closes #187
Format
makePaneGroupSummary)\n/\r)outer.zip > inner/photo.jpgfor nested archives)Screenshots
Desktop (1280×800)
Empty state

After processing — row collapsed

Row expanded — Copy button top-right

After Copy click — toast visible

ZIP archive expanded — cleaned-leaf diff with its own Copy button

Android (390×844,
isNativeAndroidmocked)These screenshots show the Copy button alongside PR #189's Save (download arrow) + Share icons in the row's result column. No DOM or CSS collision — the Copy button lives inside the diff expansion, the Save/Share icons live in the row's result cell.
Empty state

After processing — row shows Save + Share icons in result column

Row expanded — Copy button (icon-only at 480px breakpoint) alongside Save/Share

Code changes
src/web/utils/format_diff_clipboard.ts— pure markdown formatter, 12 unit testssrc/web/utils/diff_rows.ts— movedDiffRow/computeDiffRows/groupRowsBySource/makeKeyout ofMetadataDiffTableso the formatter and the renderer share one source of truthsrc/web/components/icons/ClipboardIcon.tsxMetadataDiffTable.tsxaccepts optionalonCopy?: () => voidand renders the floating Copy button (absolute top-right; top-left in RTL; icon-only below 480px)MetadataDiffExpansion.tsxbuildshandleCopyfromnavigator.clipboard.writeText(formatDiffForClipboard(...))ZipExpansion.tsxthreadsonCopyToastthrough to every cleaned-leaf diff (recursive for nested archives) with the leaf's archive-relative path as the markdown header filenameFileRow.tsxpipesfilename={file.name}+onCopyToastto both expansionsFileTable.tsxlifts the hardcoded"Copied to clipboard"toast string tot("copyToast").resources/strings.json— 6 new keys in en / es / ar (matches existing newer-keys pattern; locale fallback handles other languages)Test plan
yarn typecheck— cleanyarn lint— clean (prettier)yarn test— 583/583 (was 571 + 12 new formatter tests + 3 new diff_rows tests, minus 0)yarn check:deps— no circular depsyarn build:web+yarn build:web:standalone— both build cleanyarn test:e2e:web:desktop -- copy_diff— e2e passes; drops sample.jpg → expands → clicks Copy → asserts toast text + clipboard payload vianavigator.clipboard.readText()WebKit + standalone-mobile projects skip the e2e because Playwright's WebKit driver doesn't grant
clipboard-readwithout a user prompt andfile://origins can't receive clipboard permission grants under Playwright. The clipboard write itself works in all environments (component test covers it).Builds a handleCopy callback that writes the formatted markdown via navigator.clipboard.writeText and fires onCopyToast on success. Clipboard failures are silent (matches ErrorExpansion precedent). FileRow now passes filename={file.name} and onCopyToast through to MetadataDiffExpansion so the diff knows what filename to put in the markdown header. Issue #187. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>Adds an optional onCopyToast prop to ZipExpansion (passed through to nested archives recursively). When a cleaned-leaf diff renders, the component builds a per-leaf copy handler with the leaf's archive-relative path as the markdown header filename ('outer.zip > inner/photo.jpg' for nested archives). Issue #187. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>FileRow passes onCopyToast to ZipExpansion so cleaned-leaf diffs inside archives get their own Copy button. The hardcoded 'Copied to clipboard' string in FileTable now reads from t('copyToast'), respecting the user's locale. Issue #187. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>