Conversation
Adds a copy button to the stack trace display in three places: 1. Main remapper UI - copies the panic message + plain text stack trace using the existing addrsToPlainText formatter 2. Crash-recorded page - copies the rendered markdown stack trace text 3. Crash-recorded standalone page - same as above The button shows "Copied!" feedback for 2 seconds after clicking. Closes oven-sh/bun#26888 Co-Authored-By: Claude <noreply@anthropic.com>
WalkthroughThe PR implements copy-to-clipboard functionality for stack traces across multiple frontend components. Users can now copy stack trace content via new copy buttons with visual feedback indicating successful copy action. Changes
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In `@frontend/crash-recorded-standalone.html`:
- Line 54: The inline onclick handler for the copy button uses
navigator.clipboard.writeText(...) without a .catch, so failures are silent;
update the handler attached to elements with class "copy-trace-btn" (the current
inline onclick) to add a .catch handler on the promise returned by
navigator.clipboard.writeText that logs the error (console.error) and provides
UI feedback (e.g., change the button's inner span text to "Copy failed" or
similar, then revert after a timeout), and ensure the success .then still sets
"Copied!" and reverts after 2s; keep using event.stopPropagation() and the
existing element lookup (document.querySelector('.stacktrace')) but handle
rejection paths explicitly.
In `@frontend/crash-recorded.css`:
- Around line 233-253: The CSS blocks for the selectors .copy-trace-btn,
.copy-trace-btn:hover, and .copy-trace-btn svg are missing a blank line before
each rule; update the formatting so there is an empty line preceding each of
these selectors to match the file's established spacing and satisfy Stylelint
(i.e., insert a single blank line immediately above the .copy-trace-btn rule,
above .copy-trace-btn:hover, and above .copy-trace-btn svg).
In `@frontend/crash-recorded.html`:
- Line 63: The inline onclick handler on the button (class "copy-trace-btn")
calls navigator.clipboard.writeText(...) but lacks error handling; update the
handler for navigator.clipboard.writeText (or the function it calls) to add a
.catch() (or try/catch if converted to async) so clipboard errors are handled
and user feedback is given (e.g., change the button's inner span text to
"Failed" or show an error tooltip) and optionally log the error to console;
ensure you reference the same DOM access used now
(document.querySelector('.stacktrace').textContent and
this.querySelector('span')) so success still sets "Copied!" and failure sets a
clear failure state.
In `@frontend/frontend.ts`:
- Around line 211-216: The clipboard write call using
navigator.clipboard.writeText(text) in the click handler should handle failures;
add a .catch(err => { ... }) after the .then to handle rejection, e.g. set
copyBtn.querySelector("span")!.textContent to an error/fallback message (or
revert to "Copy"), and log the error (console.error or a logger) so failures are
visible; ensure you still restore the button text in a timeout on both success
and failure paths and reference navigator.clipboard.writeText, copyBtn, and the
span querySelector in your changes.
In `@frontend/style.css`:
- Line 314: Replace the legacy comma-separated rgb() value used on the
border-color declaration with the modern space-separated color-function syntax;
locate the border-color: rgb(247, 97, 174) statement and change it to use
rgb(247 97 174) (no commas) so it satisfies Stylelint's modern color-function
rule.
| <summary>View Stack Trace</summary> | ||
| <summary> | ||
| View Stack Trace | ||
| <button class="copy-trace-btn" onclick="event.stopPropagation();var t=document.querySelector('.stacktrace').textContent;navigator.clipboard.writeText(t).then(function(){this.querySelector('span').textContent='Copied!';var b=this;setTimeout(function(){b.querySelector('span').textContent='Copy'},2e3)}.bind(this))" title="Copy stack trace"> |
There was a problem hiding this comment.
Add error handling for clipboard API failure.
The navigator.clipboard.writeText() call has no .catch() handler. If clipboard access is denied (e.g., in non-secure contexts or when permissions are blocked), the promise rejects silently and the "Copied!" feedback never appears, leaving users confused.
🛡️ Proposed fix with error handling
- <button class="copy-trace-btn" onclick="event.stopPropagation();var t=document.querySelector('.stacktrace').textContent;navigator.clipboard.writeText(t).then(function(){this.querySelector('span').textContent='Copied!';var b=this;setTimeout(function(){b.querySelector('span').textContent='Copy'},2e3)}.bind(this))" title="Copy stack trace">
+ <button class="copy-trace-btn" onclick="event.stopPropagation();var t=document.querySelector('.stacktrace').textContent;navigator.clipboard.writeText(t).then(function(){this.querySelector('span').textContent='Copied!';var b=this;setTimeout(function(){b.querySelector('span').textContent='Copy'},2000)}.bind(this)).catch(function(){this.querySelector('span').textContent='Failed';var b=this;setTimeout(function(){b.querySelector('span').textContent='Copy'},2000)}.bind(this))" title="Copy stack trace">📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <button class="copy-trace-btn" onclick="event.stopPropagation();var t=document.querySelector('.stacktrace').textContent;navigator.clipboard.writeText(t).then(function(){this.querySelector('span').textContent='Copied!';var b=this;setTimeout(function(){b.querySelector('span').textContent='Copy'},2e3)}.bind(this))" title="Copy stack trace"> | |
| <button class="copy-trace-btn" onclick="event.stopPropagation();var t=document.querySelector('.stacktrace').textContent;navigator.clipboard.writeText(t).then(function(){this.querySelector('span').textContent='Copied!';var b=this;setTimeout(function(){b.querySelector('span').textContent='Copy'},2000)}.bind(this)).catch(function(){this.querySelector('span').textContent='Failed';var b=this;setTimeout(function(){b.querySelector('span').textContent='Copy'},2000)}.bind(this))" title="Copy stack trace"> |
🤖 Prompt for AI Agents
In `@frontend/crash-recorded-standalone.html` at line 54, The inline onclick
handler for the copy button uses navigator.clipboard.writeText(...) without a
.catch, so failures are silent; update the handler attached to elements with
class "copy-trace-btn" (the current inline onclick) to add a .catch handler on
the promise returned by navigator.clipboard.writeText that logs the error
(console.error) and provides UI feedback (e.g., change the button's inner span
text to "Copy failed" or similar, then revert after a timeout), and ensure the
success .then still sets "Copied!" and reverts after 2s; keep using
event.stopPropagation() and the existing element lookup
(document.querySelector('.stacktrace')) but handle rejection paths explicitly.
| .copy-trace-btn { | ||
| margin-left: auto; | ||
| display: inline-flex; | ||
| align-items: center; | ||
| gap: 0.35rem; | ||
| padding: 0.25rem 0.5rem; | ||
| font-size: 0.75rem; | ||
| background: #262626; | ||
| border: 1px solid #383838; | ||
| border-radius: 4px; | ||
| color: #a3a3a3; | ||
| cursor: pointer; | ||
| transition: color 0.15s, background 0.15s; | ||
| } | ||
| .copy-trace-btn:hover { | ||
| background: #333; | ||
| color: #f472b6; | ||
| } | ||
| .copy-trace-btn svg { | ||
| flex-shrink: 0; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Add empty lines before CSS rules for consistency.
Stylelint flags missing empty lines before .copy-trace-btn, .copy-trace-btn:hover, and .copy-trace-btn svg rules. Adding empty lines improves readability and matches the formatting pattern used elsewhere in this file.
🧹 Proposed formatting fix
.hidden {
display: none;
}
+
.copy-trace-btn {
margin-left: auto;
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
background: `#262626`;
border: 1px solid `#383838`;
border-radius: 4px;
color: `#a3a3a3`;
cursor: pointer;
transition: color 0.15s, background 0.15s;
}
+
.copy-trace-btn:hover {
background: `#333`;
color: `#f472b6`;
}
+
.copy-trace-btn svg {
flex-shrink: 0;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| .copy-trace-btn { | |
| margin-left: auto; | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.35rem; | |
| padding: 0.25rem 0.5rem; | |
| font-size: 0.75rem; | |
| background: #262626; | |
| border: 1px solid #383838; | |
| border-radius: 4px; | |
| color: #a3a3a3; | |
| cursor: pointer; | |
| transition: color 0.15s, background 0.15s; | |
| } | |
| .copy-trace-btn:hover { | |
| background: #333; | |
| color: #f472b6; | |
| } | |
| .copy-trace-btn svg { | |
| flex-shrink: 0; | |
| } | |
| .hidden { | |
| display: none; | |
| } | |
| .copy-trace-btn { | |
| margin-left: auto; | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.35rem; | |
| padding: 0.25rem 0.5rem; | |
| font-size: 0.75rem; | |
| background: `#262626`; | |
| border: 1px solid `#383838`; | |
| border-radius: 4px; | |
| color: `#a3a3a3`; | |
| cursor: pointer; | |
| transition: color 0.15s, background 0.15s; | |
| } | |
| .copy-trace-btn:hover { | |
| background: `#333`; | |
| color: `#f472b6`; | |
| } | |
| .copy-trace-btn svg { | |
| flex-shrink: 0; | |
| } |
🧰 Tools
🪛 Stylelint (17.2.0)
[error] 233-246: Expected empty line before rule (rule-empty-line-before)
(rule-empty-line-before)
[error] 247-250: Expected empty line before rule (rule-empty-line-before)
(rule-empty-line-before)
[error] 251-253: Expected empty line before rule (rule-empty-line-before)
(rule-empty-line-before)
🤖 Prompt for AI Agents
In `@frontend/crash-recorded.css` around lines 233 - 253, The CSS blocks for the
selectors .copy-trace-btn, .copy-trace-btn:hover, and .copy-trace-btn svg are
missing a blank line before each rule; update the formatting so there is an
empty line preceding each of these selectors to match the file's established
spacing and satisfy Stylelint (i.e., insert a single blank line immediately
above the .copy-trace-btn rule, above .copy-trace-btn:hover, and above
.copy-trace-btn svg).
| <summary>View Stack Trace</summary> | ||
| <summary> | ||
| View Stack Trace | ||
| <button class="copy-trace-btn" onclick="event.stopPropagation();var t=document.querySelector('.stacktrace').textContent;navigator.clipboard.writeText(t).then(function(){this.querySelector('span').textContent='Copied!';var b=this;setTimeout(function(){b.querySelector('span').textContent='Copy'},2e3)}.bind(this))" title="Copy stack trace"> |
There was a problem hiding this comment.
Add error handling for clipboard API failure.
Same issue as the standalone version—no .catch() handler for navigator.clipboard.writeText(). If clipboard access fails, users get no feedback.
🛡️ Proposed fix with error handling
- <button class="copy-trace-btn" onclick="event.stopPropagation();var t=document.querySelector('.stacktrace').textContent;navigator.clipboard.writeText(t).then(function(){this.querySelector('span').textContent='Copied!';var b=this;setTimeout(function(){b.querySelector('span').textContent='Copy'},2e3)}.bind(this))" title="Copy stack trace">
+ <button class="copy-trace-btn" onclick="event.stopPropagation();var t=document.querySelector('.stacktrace').textContent;navigator.clipboard.writeText(t).then(function(){this.querySelector('span').textContent='Copied!';var b=this;setTimeout(function(){b.querySelector('span').textContent='Copy'},2000)}.bind(this)).catch(function(){this.querySelector('span').textContent='Failed';var b=this;setTimeout(function(){b.querySelector('span').textContent='Copy'},2000)}.bind(this))" title="Copy stack trace">📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <button class="copy-trace-btn" onclick="event.stopPropagation();var t=document.querySelector('.stacktrace').textContent;navigator.clipboard.writeText(t).then(function(){this.querySelector('span').textContent='Copied!';var b=this;setTimeout(function(){b.querySelector('span').textContent='Copy'},2e3)}.bind(this))" title="Copy stack trace"> | |
| <button class="copy-trace-btn" onclick="event.stopPropagation();var t=document.querySelector('.stacktrace').textContent;navigator.clipboard.writeText(t).then(function(){this.querySelector('span').textContent='Copied!';var b=this;setTimeout(function(){b.querySelector('span').textContent='Copy'},2000)}.bind(this)).catch(function(){this.querySelector('span').textContent='Failed';var b=this;setTimeout(function(){b.querySelector('span').textContent='Copy'},2000)}.bind(this))" title="Copy stack trace"> |
🤖 Prompt for AI Agents
In `@frontend/crash-recorded.html` at line 63, The inline onclick handler on the
button (class "copy-trace-btn") calls navigator.clipboard.writeText(...) but
lacks error handling; update the handler for navigator.clipboard.writeText (or
the function it calls) to add a .catch() (or try/catch if converted to async) so
clipboard errors are handled and user feedback is given (e.g., change the
button's inner span text to "Failed" or show an error tooltip) and optionally
log the error to console; ensure you reference the same DOM access used now
(document.querySelector('.stacktrace').textContent and
this.querySelector('span')) so success still sets "Copied!" and failure sets a
clear failure state.
| navigator.clipboard.writeText(text).then(() => { | ||
| copyBtn.querySelector("span")!.textContent = "Copied!"; | ||
| setTimeout(() => { | ||
| copyBtn.querySelector("span")!.textContent = "Copy"; | ||
| }, 2000); | ||
| }); |
There was a problem hiding this comment.
Add error handling for clipboard API failure.
The navigator.clipboard.writeText() call lacks a .catch() handler. Unlike the HTML templates, TypeScript makes it easy to handle this properly.
🛡️ Proposed fix with error handling
navigator.clipboard.writeText(text).then(() => {
copyBtn.querySelector("span")!.textContent = "Copied!";
setTimeout(() => {
copyBtn.querySelector("span")!.textContent = "Copy";
}, 2000);
+ }).catch(() => {
+ copyBtn.querySelector("span")!.textContent = "Failed";
+ setTimeout(() => {
+ copyBtn.querySelector("span")!.textContent = "Copy";
+ }, 2000);
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| navigator.clipboard.writeText(text).then(() => { | |
| copyBtn.querySelector("span")!.textContent = "Copied!"; | |
| setTimeout(() => { | |
| copyBtn.querySelector("span")!.textContent = "Copy"; | |
| }, 2000); | |
| }); | |
| navigator.clipboard.writeText(text).then(() => { | |
| copyBtn.querySelector("span")!.textContent = "Copied!"; | |
| setTimeout(() => { | |
| copyBtn.querySelector("span")!.textContent = "Copy"; | |
| }, 2000); | |
| }).catch(() => { | |
| copyBtn.querySelector("span")!.textContent = "Failed"; | |
| setTimeout(() => { | |
| copyBtn.querySelector("span")!.textContent = "Copy"; | |
| }, 2000); | |
| }); |
🤖 Prompt for AI Agents
In `@frontend/frontend.ts` around lines 211 - 216, The clipboard write call using
navigator.clipboard.writeText(text) in the click handler should handle failures;
add a .catch(err => { ... }) after the .then to handle rejection, e.g. set
copyBtn.querySelector("span")!.textContent to an error/fallback message (or
revert to "Copy"), and log the error (console.error or a logger) so failures are
visible; ensure you still restore the button text in a timeout on both success
and failure paths and reference navigator.clipboard.writeText, copyBtn, and the
span querySelector in your changes.
| .copy-btn:hover { | ||
| opacity: 1; | ||
| background-color: var(--pink); | ||
| border-color: rgb(247, 97, 174); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Use modern color-function notation.
Stylelint flags rgb(247, 97, 174) as using legacy syntax. Modern CSS uses space-separated values without commas.
🧹 Proposed fix
.copy-btn:hover {
opacity: 1;
background-color: var(--pink);
- border-color: rgb(247, 97, 174);
+ border-color: rgb(247 97 174);
color: black;
}🧰 Tools
🪛 Stylelint (17.2.0)
[error] 314-314: Expected modern color-function notation (color-function-notation)
(color-function-notation)
🤖 Prompt for AI Agents
In `@frontend/style.css` at line 314, Replace the legacy comma-separated rgb()
value used on the border-color declaration with the modern space-separated
color-function syntax; locate the border-color: rgb(247, 97, 174) statement and
change it to use rgb(247 97 174) (no commas) so it satisfies Stylelint's modern
color-function rule.
d768a24 to
e3f577f
Compare
Summary
addrsToPlainTextformatter)<details>sectionCloses oven-sh/bun#26888
Test plan
🤖 Generated with Claude Code