refactor: better logs

This commit is contained in:
zhom committed 2026-10-10 01:11:04 +04:00
1 parent 2604994507
commit bf625fac2c
91 files changed
+4353 -3239

No files matched your search

+31 -24
View File
@@ -14,7 +14,10 @@ import {
NoteAction,
useTimeFormat,
} from "@/components/agent-console-parts";
import { DATA_TABLE_CLASSES } from "@/components/table-controls";
import {
DATA_TABLE_CLASSES,
TableCheckboxSlot,
} from "@/components/table-controls";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
@@ -190,17 +193,19 @@ export function AgentProfiles({
<TableHeader className={DATA_TABLE_CLASSES.header}>
<TableRow className={DATA_TABLE_CLASSES.headerRow}>
<TableHead className={cn(DATA_TABLE_CLASSES.head, "w-8")}>
<Checkbox
checked={allChecked}
aria-label={t("common.aria.selectAll")}
onCheckedChange={(checked) => {
setSelected(
checked === true
? new Set(rows.map((row) => row.profile_id))
: new Set(),
);
}}
/>
<TableCheckboxSlot>
<Checkbox
checked={allChecked}
aria-label={t("common.aria.selectAll")}
onCheckedChange={(checked) => {
setSelected(
checked === true
? new Set(rows.map((row) => row.profile_id))
: new Set(),
);
}}
/>
</TableCheckboxSlot>
</TableHead>
<TableHead className={DATA_TABLE_CLASSES.head}>
{t("agent.profiles.columns.profile")}
@@ -259,18 +264,20 @@ export function AgentProfiles({
}
>
<TableCell>
<Checkbox
checked={selected.has(row.profile_id)}
aria-label={t("agent.profiles.selectRow", { name })}
onCheckedChange={(checked) => {
setSelected((previous) => {
const next = new Set(previous);
if (checked === true) next.add(row.profile_id);
else next.delete(row.profile_id);
return next;
});
}}
/>
<TableCheckboxSlot>
<Checkbox
checked={selected.has(row.profile_id)}
aria-label={t("agent.profiles.selectRow", { name })}
onCheckedChange={(checked) => {
setSelected((previous) => {
const next = new Set(previous);
if (checked === true) next.add(row.profile_id);
else next.delete(row.profile_id);
return next;
});
}}
/>
</TableCheckboxSlot>
</TableCell>
<TableCell className="max-w-56">
<span className="block truncate text-sm font-medium">
+1 -1
View File
@@ -2565,7 +2565,7 @@ export function ProfilesDataTable({
sideOffset={4}
horizontalOffset={8}
>
<span className="inline-flex items-center">
<span className="block">
<TableRowCheckbox
table={table}
row={row}
+68 -58
View File
@@ -549,6 +549,12 @@ export function SortableColumnHeader<T extends RowData>({
const selectionAnchors = new WeakMap<object, string>();
/** Centers a selection checkbox in its cell, at any column width. Inline, it
* would sit on the text baseline and hug the cell's start. */
export function TableCheckboxSlot({ children }: { children: React.ReactNode }) {
return <span className="flex items-center justify-center">{children}</span>;
}
export function TableSelectHeader<T extends RowData>({
table,
}: {
@@ -560,26 +566,28 @@ export function TableSelectHeader<T extends RowData>({
.rows.filter((row) => row.getCanSelect());
const selected = selectable.filter((row) => row.getIsSelected()).length;
return (
<Checkbox
aria-label={t("common.aria.selectAll")}
className="relative after:absolute after:-inset-2 after:content-['']"
checked={
selected > 0 && selected === selectable.length
? true
: selected > 0
? "indeterminate"
: false
}
disabled={!selectable.length}
onCheckedChange={(checked) => {
selectionAnchors.delete(table);
table.setRowSelection(
checked
? Object.fromEntries(selectable.map((row) => [row.id, true]))
: {},
);
}}
/>
<TableCheckboxSlot>
<Checkbox
aria-label={t("common.aria.selectAll")}
className="relative after:absolute after:-inset-2 after:content-['']"
checked={
selected > 0 && selected === selectable.length
? true
: selected > 0
? "indeterminate"
: false
}
disabled={!selectable.length}
onCheckedChange={(checked) => {
selectionAnchors.delete(table);
table.setRowSelection(
checked
? Object.fromEntries(selectable.map((row) => [row.id, true]))
: {},
);
}}
/>
</TableCheckboxSlot>
);
}
@@ -595,45 +603,47 @@ export function TableRowCheckbox<T extends RowData>({
const { t } = useTranslation();
const hasSelection = table.getSelectedRowModel().rows.length > 0;
return (
<span className="group relative inline-flex size-4 items-center justify-center">
<Checkbox
aria-label={t("common.aria.selectRow")}
checked={row.getIsSelected()}
disabled={!row.getCanSelect()}
className={cn(
"relative after:absolute after:-inset-2 after:content-['']",
icon &&
!hasSelection &&
"opacity-0 group-hover:opacity-100 focus-visible:opacity-100",
<TableCheckboxSlot>
<span className="group relative flex size-4 shrink-0 items-center justify-center">
<Checkbox
aria-label={t("common.aria.selectRow")}
checked={row.getIsSelected()}
disabled={!row.getCanSelect()}
className={cn(
"relative after:absolute after:-inset-2 after:content-['']",
icon &&
!hasSelection &&
"opacity-0 group-hover:opacity-100 focus-visible:opacity-100",
)}
onClick={(event) => {
if (!row.getCanSelect()) return;
const ids = table
.getRowModel()
.rows.filter((item) => item.getCanSelect())
.map((item) => item.id);
table.setRowSelection((prev) =>
selectTableRange(
ids,
prev,
selectionAnchors.get(table) ?? null,
row.id,
!row.getIsSelected(),
event.shiftKey,
),
);
if (!event.shiftKey) selectionAnchors.set(table, row.id);
}}
/>
{icon && !hasSelection && (
<span
aria-hidden="true"
className="pointer-events-none absolute inset-0 group-hover:opacity-0 group-focus-within:opacity-0"
>
{icon}
</span>
)}
onClick={(event) => {
if (!row.getCanSelect()) return;
const ids = table
.getRowModel()
.rows.filter((item) => item.getCanSelect())
.map((item) => item.id);
table.setRowSelection((prev) =>
selectTableRange(
ids,
prev,
selectionAnchors.get(table) ?? null,
row.id,
!row.getIsSelected(),
event.shiftKey,
),
);
if (!event.shiftKey) selectionAnchors.set(table, row.id);
}}
/>
{icon && !hasSelection && (
<span
aria-hidden="true"
className="pointer-events-none absolute inset-0 group-hover:opacity-0 group-focus-within:opacity-0"
>
{icon}
</span>
)}
</span>
</span>
</TableCheckboxSlot>
);
}
+21 -1
View File
@@ -1,12 +1,32 @@
import { attachConsole } from "@tauri-apps/plugin-log";
import { attachConsole, error as logError } from "@tauri-apps/plugin-log";
let consoleAttached = false;
let errorsForwarded = false;
function describe(reason: unknown): string {
if (reason instanceof Error) return reason.stack ?? reason.message;
return typeof reason === "string" ? reason : JSON.stringify(reason);
}
export async function setupLogging() {
if (consoleAttached) {
return;
}
// Only uncaught failures go to the log file. Forwarding console.error too
// would loop: attachConsole writes every backend line back to the console.
if (!errorsForwarded) {
errorsForwarded = true;
window.addEventListener("error", (event) => {
void logError(
`Uncaught error err="${describe(event.error ?? event.message)}" source=${event.filename}:${event.lineno}:${event.colno}`,
);
});
window.addEventListener("unhandledrejection", (event) => {
void logError(`Unhandled rejection err="${describe(event.reason)}"`);
});
}
try {
await attachConsole();
consoleAttached = true;