fix: add copy button to error display and stabilize error re-renders (#37)#38
fix: add copy button to error display and stabilize error re-renders (#37)#38claude[bot] wants to merge 4 commits intomainfrom
Conversation
…37) Add a copy icon button (content-copy) to the error display component so users can copy error text to clipboard. Prevent error text from re-rendering during query polling when the error content hasn't changed, which was causing text selection to be lost. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: c2e1a30 The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Using `:` as the name/message separator in the each-block key could collide if error.name contains a colon. Align with serializeErrors which already uses `\0` to avoid this ambiguity.
There was a problem hiding this comment.
Code Review
One bug was fixed directly (commit 5965f69). Three issues below require attention.
Fixed: #each key used : as delimiter — inconsistent with serializeErrors and collision-prone
queries.svelte (original line 64)
{#each errors as error (`${error.name}:${error.message}`)}Using : as the separator means any error whose .name contains a colon (e.g. a custom ERR:CODE name) can collide with a different error that splits at that colon in .message. serializeErrors already uses \0 precisely to avoid this. Fixed in the follow-up commit to align both usages.
Issue: setTimeout not cancelled on component teardown
src/lib/components/error.svelte, lines 25–27
setTimeout(() => {
showCopySuccess = false
}, 750)The timeout ID is never stored or cleared. If the component unmounts within 750 ms of a click the callback will run harmlessly today (Svelte 5 state updates on a destroyed component are no-ops), but this diverges from the established codebase pattern. refetch-controller.svelte (lines 87–90) shows the correct approach:
let timeoutId: ReturnType<typeof setTimeout> | undefined
const copyErrorToClipboard = async () => {
try {
await globalThis.navigator.clipboard.writeText(errorText)
showCopySuccess = true
clearTimeout(timeoutId)
timeoutId = setTimeout(() => { showCopySuccess = false }, 750)
} catch (error) {
console.error('Failed to copy error to clipboard', error)
}
}
$effect(() => () => clearTimeout(timeoutId))The clearTimeout before re-assigning also prevents stacking multiple timers if the user clicks rapidly.
Issue: No unit tests for the serializeErrors guard in queries.svelte
The new serializeErrors comparison guard (queries.svelte lines 40–42) has zero test coverage. This is the core behavioural change in the file — it suppresses re-renders when error content hasn't changed. Missing edge cases:
- Guard passes (different errors → state updates).
- Guard blocks (same errors → no state update, selection preserved).
- Guard resets correctly when all queries succeed (
errors = []). - Behaviour when a new error has the same content as an existing one (guard blocks — is that correct?).
Please add a queries.spec.ts similar to refetch-controller.spec.ts.
@claude resolve these |
|
Claude finished @DTCurrie's task in 4m 21s —— View job Resolved Review Issues
Changes made (commit 37a3d33)
Added let copySuccessTimeoutId: ReturnType<typeof setTimeout> | undefined
const copyErrorToClipboard = async () => {
try {
await globalThis.navigator.clipboard.writeText(errorText)
showCopySuccess = true
clearTimeout(copySuccessTimeoutId)
copySuccessTimeoutId = setTimeout(() => { showCopySuccess = false }, 750)
} catch (error) {
console.error('Failed to copy error to clipboard', error)
}
}
$effect(() => () => clearTimeout(copySuccessTimeoutId))
|
…tests - Store setTimeout ID in error.svelte and clear it on component teardown via $effect cleanup, matching the refetch-controller.svelte pattern; also clear before re-assigning to prevent stacking on rapid clicks - Add queries.spec.ts covering the serializeErrors guard: guard passes (different content updates state), guard blocks (same content preserves DOM element), guard resets on success, and loading-state preservation Co-authored-by: Devin T. Currie <DTCurrie@users.noreply.github.com>

Summary
content-copyfrom prime-coreIconButton) to the error display component so users can copy error text to their clipboardqueries.svelteby comparing error content (name + message) before updating state, preventing identical errors from causing DOM re-renders during query polling loops#eachblock for error rendering instead of object referencesCloses #37
DRI
@DTCurrie is the responsible engineer for this PR.
Test plan
🤖 Generated with Claude Code