Add error ID display and copy shortcuts to prod-errors TUI

Show error ID after status label in list view. Add 'c' shortcut to copy error ID from list and full stacktrace from detail view. Replace Enter-to-copy-line with 'c' copy stacktrace in detail. Show error reason in detail header instead of kind.
This commit is contained in:
Claudio Ortolina
2026-05-08 22:43:02 +01:00
parent b7b2324d99
commit 5162b992cd
+41 -10
View File
@@ -760,6 +760,16 @@ class ErrorBrowser {
// ── List mode keys ──────────────────────────────────────────────── // ── List mode keys ────────────────────────────────────────────────
if (this.mode === "list" || this.mode === "loading") { if (this.mode === "list" || this.mode === "loading") {
if (matchesKey(data, "c")) {
if (this.errors.length > 0 && this.cursorIndex >= 0) {
const error = this.errors[this.cursorIndex];
if (error) {
this.onCopy?.(String(error.id));
}
}
return;
}
if (matchesKey(data, "r")) { if (matchesKey(data, "r")) {
this.toggleResolved(); this.toggleResolved();
return; return;
@@ -868,13 +878,11 @@ class ErrorBrowser {
return; return;
} }
if (matchesKey(data, Key.enter)) { if (matchesKey(data, "c")) {
// Copy current line to editor (prod-logs pattern)
if (this.selectedError) { if (this.selectedError) {
const detailLines = this.buildDetailLines(); const text = this.buildStacktraceText();
const idx = this.detailScrollOffset; if (text) {
if (idx < detailLines.length) { this.onCopy?.(text);
this.onCopy?.(detailLines[idx]!);
} }
} }
return; return;
@@ -1035,7 +1043,7 @@ class ErrorBrowser {
error.reason, error.reason,
Math.max(30, width - 25), Math.max(30, width - 25),
); );
const line1 = `${cursor} ${statusBadge}${mutedLabel} ${error.kind}: ${reasonText}`; const line1 = `${cursor} ${statusBadge} #${error.id}${mutedLabel} ${error.kind}: ${reasonText}`;
result.push( result.push(
truncateToWidth( truncateToWidth(
isCursor ? theme.fg("accent", line1) : line1, isCursor ? theme.fg("accent", line1) : line1,
@@ -1075,7 +1083,7 @@ class ErrorBrowser {
truncateToWidth( truncateToWidth(
theme.fg( theme.fg(
"dim", "dim",
` ↑↓/jk navigate ↵ details r ${resolvedLabel} resolved m ${mutedLabel} muted M toggle mute R toggle resolve`, ` ↑↓/jk navigate c copy id ↵ details r ${resolvedLabel} resolved m ${mutedLabel} muted M toggle mute R toggle resolve`,
), ),
width, width,
"", "",
@@ -1104,6 +1112,29 @@ class ErrorBrowser {
return formatDetailLines(error); return formatDetailLines(error);
} }
private buildStacktraceText(): string {
const error = this.selectedError;
if (!error) return "";
const lines: string[] = [];
const occurrences = error.occurrences ?? [];
for (const occ of occurrences) {
if (occ.stacktrace?.lines?.length) {
lines.push(`# Occurrence ${occ.inserted_at}`);
for (const sl of occ.stacktrace.lines) {
const app = sl.application || "\u2014";
const loc = sl.file
? `${sl.file}${sl.line != null ? `:${sl.line}` : ""}`
: "(nofile)";
lines.push(
` ${app} / ${sl.module}.${sl.function}/${sl.arity} ${loc}`,
);
}
lines.push("");
}
}
return lines.join("\n").trimEnd();
}
private renderDetail(width: number, theme: Theme): string[] { private renderDetail(width: number, theme: Theme): string[] {
const border = theme.fg("accent", "─".repeat(width)); const border = theme.fg("accent", "─".repeat(width));
const result: string[] = []; const result: string[] = [];
@@ -1114,7 +1145,7 @@ class ErrorBrowser {
// Header // Header
const error = this.selectedError; const error = this.selectedError;
const headerText = error const headerText = error
? ` Error #${error.id}: ${error.kind}` ? ` Error #${error.id}: ${error.reason}`
: " Error Detail"; : " Error Detail";
result.push( result.push(
truncateToWidth(theme.fg("accent", theme.bold(headerText)), width, ""), truncateToWidth(theme.fg("accent", theme.bold(headerText)), width, ""),
@@ -1154,7 +1185,7 @@ class ErrorBrowser {
truncateToWidth( truncateToWidth(
theme.fg( theme.fg(
"dim", "dim",
` Lines ${fromLine}-${toLine} of ${detailLines.length} (${pct}%) ↑↓/jk scroll Enter copy M mute R resolve Escape back`, ` Lines ${fromLine}-${toLine} of ${detailLines.length} (${pct}%) ↑↓/jk scroll c copy stacktrace M mute R resolve Escape back`,
), ),
width, width,
"", "",