diff --git a/frontend/crash-recorded-standalone.html b/frontend/crash-recorded-standalone.html
index 078eca7..3f7fba2 100644
--- a/frontend/crash-recorded-standalone.html
+++ b/frontend/crash-recorded-standalone.html
@@ -49,7 +49,13 @@
What you can do
- View Stack Trace
+
+ View Stack Trace
+
+
%STACKTRACE%
diff --git a/frontend/crash-recorded.css b/frontend/crash-recorded.css
index 05e3b30..49f8152 100644
--- a/frontend/crash-recorded.css
+++ b/frontend/crash-recorded.css
@@ -230,6 +230,27 @@ summary:hover {
font-size: 0.9375rem;
}
}
+.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;
}
diff --git a/frontend/crash-recorded.html b/frontend/crash-recorded.html
index 9536d1e..7ba7ba7 100644
--- a/frontend/crash-recorded.html
+++ b/frontend/crash-recorded.html
@@ -58,7 +58,13 @@ Help us fix it
- View Stack Trace
+
+ View Stack Trace
+
+
%STACKTRACE%
diff --git a/frontend/frontend.ts b/frontend/frontend.ts
index 3106a6a..8872b94 100644
--- a/frontend/frontend.ts
+++ b/frontend/frontend.ts
@@ -1,5 +1,5 @@
import type { Parse, RemapAPIResponse } from "../lib/parser";
-import { os_names } from "../lib/format";
+import { os_names, addrsToPlainText } from "../lib/format";
import { parse } from "../lib/parser";
import { parseCacheKey, debounce, escapeHTML as eschtml } from "../lib/util";
@@ -191,11 +191,31 @@ const screens: Record void> = {
out.innerHTML = /* html */ `
${cardHead()}
-
+
${cardFooter()}
${issue}
`;
+
+ const copyBtn = out.querySelector(".copy-btn") as HTMLButtonElement;
+ if (copyBtn) {
+ copyBtn.addEventListener("click", () => {
+ const lines = addrsToPlainText(fetched!.commit.oid, fetched!.addresses);
+ const text = `${parsed!.message}\n\n${lines.join("\n")}`;
+ navigator.clipboard.writeText(text).then(() => {
+ copyBtn.querySelector("span")!.textContent = "Copied!";
+ setTimeout(() => {
+ copyBtn.querySelector("span")!.textContent = "Copy";
+ }, 2000);
+ });
+ });
+ }
},
// When fetch fails, fallback to this screen.
diff --git a/frontend/style.css b/frontend/style.css
index 5e141be..56956ac 100644
--- a/frontend/style.css
+++ b/frontend/style.css
@@ -286,6 +286,40 @@ button:hover,
}
}
+.table-wrapper {
+ position: relative;
+}
+
+.copy-btn {
+ position: absolute;
+ top: 0;
+ right: 0;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.35rem;
+ padding: 0.3rem 0.6rem;
+ font-size: 0.8rem;
+ background-color: var(--fn-bg);
+ border: 1px solid var(--fn-border);
+ border-radius: 6px;
+ color: var(--fg);
+ cursor: pointer;
+ opacity: 0.7;
+ transition: opacity 0.15s;
+}
+
+.copy-btn:hover {
+ opacity: 1;
+ background-color: var(--pink);
+ border-color: rgb(247, 97, 174);
+ color: black;
+}
+
+.copy-btn svg {
+ width: 14px;
+ height: 14px;
+}
+
@media (max-width: 480px) {
.card {
margin-left: -1em;