claude-session-dashboard 0.1.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -14
- package/dist/client/assets/_dashboard-I7m6D7BE.js +1 -0
- package/dist/client/assets/_sessionId-DEliIff6.js +12 -0
- package/dist/client/assets/app-D7yorIIh.css +1 -0
- package/dist/client/assets/{createServerFn-Le0d8Pjz.js → createServerFn-Bn6_ISOt.js} +1 -1
- package/dist/client/assets/format-Bsprb3az.js +1 -0
- package/dist/client/assets/index-BkqRvnEf.js +1 -0
- package/dist/client/assets/{main-CzD8HjLq.js → main-CfJIADCp.js} +7 -7
- package/dist/client/assets/sessions.queries-CrJg4dYU.js +1 -0
- package/dist/client/assets/settings-C4_lsEzl.js +1 -0
- package/dist/client/assets/{settings.types-B4841OLF.js → settings.types-9Qf5WcRY.js} +1 -1
- package/dist/client/assets/stats-_r1gmaTe.js +4 -0
- package/dist/client/assets/useSessionCost-DPZ-ubM1.js +65 -0
- package/dist/client/favicon.svg +3 -0
- package/dist/server/assets/_dashboard-TUzgwLqB.js +112 -0
- package/dist/server/assets/{_sessionId-BwZK4Ezz.js → _sessionId-C-XZIPqn.js} +57 -35
- package/dist/server/assets/_tanstack-start-manifest_v-B51mSkGz.js +4 -0
- package/dist/server/assets/{claude-path-CkuljM34.js → claude-path-BdwflgZ1.js} +9 -3
- package/dist/server/assets/{format-CGmJnuhZ.js → format-DIZHV7IJ.js} +3 -3
- package/dist/server/assets/{index-D4VWrt2z.js → index-CKfH7HpA.js} +28 -60
- package/dist/server/assets/project-analytics.server-BkWSd6a8.js +61 -0
- package/dist/server/assets/{router-xTSe9UH_.js → router-Cb_hBXHI.js} +62 -31
- package/dist/server/assets/{session-detail.server-azkRfON2.js → session-detail.server-DLXl-Pn-.js} +1 -1
- package/dist/server/assets/session-scanner-CLfls9u-.js +93 -0
- package/dist/server/assets/sessions.queries-B5ZBiVJy.js +42 -0
- package/dist/server/assets/{sessions.server-B8zbmvSM.js → sessions.server-CUhasKW2.js} +5 -89
- package/dist/server/assets/{settings-ko61yfVs.js → settings-C0_KyVQQ.js} +66 -20
- package/dist/server/assets/stats-BtgVene-.js +886 -0
- package/dist/server/assets/{stats.server-BZWxV-mC.js → stats.server-qTOvID9-.js} +62 -3
- package/dist/server/assets/useSessionCost-CYs5UOX-.js +209 -0
- package/dist/server/server.js +13 -10
- package/package.json +11 -1
- package/dist/client/assets/_dashboard-CYwTENkn.js +0 -1
- package/dist/client/assets/_sessionId-Bwfhm_El.js +0 -12
- package/dist/client/assets/app-DhZyFob1.css +0 -1
- package/dist/client/assets/format-Bf-cSf6L.js +0 -1
- package/dist/client/assets/index-DXhX1hdS.js +0 -1
- package/dist/client/assets/settings-BSPc79zZ.js +0 -1
- package/dist/client/assets/stats-CDIvpOt9.js +0 -4
- package/dist/client/assets/useSessionCost-9NP6uhla.js +0 -61
- package/dist/server/assets/_dashboard--ukhquwO.js +0 -97
- package/dist/server/assets/_tanstack-start-manifest_v-gtQY7f-T.js +0 -4
- package/dist/server/assets/stats-DItsFPp5.js +0 -266
- package/dist/server/assets/useSessionCost-EB0VxklP.js +0 -76
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMatches, Link, Outlet } from "@tanstack/react-router";
|
|
3
|
+
import { useQuery } from "@tanstack/react-query";
|
|
4
|
+
import { a as activeSessionsQuery } from "./sessions.queries-B5ZBiVJy.js";
|
|
5
|
+
import "./createSsrRpc-CVg2UDl0.js";
|
|
6
|
+
import "../server.js";
|
|
7
|
+
import "@tanstack/history";
|
|
8
|
+
import "@tanstack/router-core/ssr/client";
|
|
9
|
+
import "@tanstack/router-core";
|
|
10
|
+
import "node:async_hooks";
|
|
11
|
+
import "@tanstack/router-core/ssr/server";
|
|
12
|
+
import "h3-v2";
|
|
13
|
+
import "tiny-invariant";
|
|
14
|
+
import "seroval";
|
|
15
|
+
import "@tanstack/react-router/ssr/server";
|
|
16
|
+
import "zod";
|
|
17
|
+
function ActiveSessionsBadge() {
|
|
18
|
+
const { data: activeSessions } = useQuery(activeSessionsQuery);
|
|
19
|
+
const count = activeSessions?.length ?? 0;
|
|
20
|
+
if (count === 0) return null;
|
|
21
|
+
return /* @__PURE__ */ jsx("span", { className: "ml-auto rounded-full bg-emerald-500/20 px-1.5 py-0.5 text-[10px] font-medium text-emerald-400", children: count });
|
|
22
|
+
}
|
|
23
|
+
const NAV_ITEMS = [
|
|
24
|
+
{
|
|
25
|
+
to: "/sessions",
|
|
26
|
+
label: "Sessions",
|
|
27
|
+
icon: /* @__PURE__ */ jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
|
|
28
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "4", x2: "14", y2: "4" }),
|
|
29
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "8", x2: "14", y2: "8" }),
|
|
30
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "12", x2: "14", y2: "12" })
|
|
31
|
+
] })
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
to: "/stats",
|
|
35
|
+
label: "Stats",
|
|
36
|
+
icon: /* @__PURE__ */ jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 16 16", fill: "currentColor", children: [
|
|
37
|
+
/* @__PURE__ */ jsx("rect", { x: "1", y: "8", width: "3", height: "7", rx: "0.5" }),
|
|
38
|
+
/* @__PURE__ */ jsx("rect", { x: "6.5", y: "4", width: "3", height: "11", rx: "0.5" }),
|
|
39
|
+
/* @__PURE__ */ jsx("rect", { x: "12", y: "1", width: "3", height: "14", rx: "0.5" })
|
|
40
|
+
] })
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
to: "/settings",
|
|
44
|
+
label: "Settings",
|
|
45
|
+
icon: /* @__PURE__ */ jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 16 16", fill: "currentColor", children: [
|
|
46
|
+
/* @__PURE__ */ jsx("path", { d: "M8 10a2 2 0 100-4 2 2 0 000 4z" }),
|
|
47
|
+
/* @__PURE__ */ jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M6.5.8a1 1 0 011-.8h1a1 1 0 011 .8l.15.9a5.5 5.5 0 011.1.64l.86-.36a1 1 0 011.17.36l.5.86a1 1 0 01-.18 1.17l-.7.55a5.5 5.5 0 010 1.27l.7.55a1 1 0 01.18 1.17l-.5.86a1 1 0 01-1.17.36l-.86-.36a5.5 5.5 0 01-1.1.64l-.14.9a1 1 0 01-1 .8h-1a1 1 0 01-1-.8l-.14-.9a5.5 5.5 0 01-1.1-.64l-.87.36a1 1 0 01-1.17-.36l-.5-.86a1 1 0 01.18-1.17l.7-.55a5.5 5.5 0 010-1.27l-.7-.55a1 1 0 01-.18-1.17l.5-.86a1 1 0 011.17-.36l.87.36a5.5 5.5 0 011.1-.64L6.5.8zM8 11a3 3 0 100-6 3 3 0 000 6z" })
|
|
48
|
+
] })
|
|
49
|
+
}
|
|
50
|
+
];
|
|
51
|
+
function AppShell({ children }) {
|
|
52
|
+
const matches = useMatches();
|
|
53
|
+
const currentPath = matches[matches.length - 1]?.pathname ?? "";
|
|
54
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-h-screen", children: [
|
|
55
|
+
/* @__PURE__ */ jsxs("aside", { className: "flex w-56 shrink-0 flex-col border-r border-gray-800 bg-gray-950", children: [
|
|
56
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-14 items-center border-b border-gray-800 px-4", children: /* @__PURE__ */ jsxs(Link, { to: "/sessions", className: "text-sm font-bold text-white", children: [
|
|
57
|
+
/* @__PURE__ */ jsx("span", { className: "text-brand-500", children: "Claude" }),
|
|
58
|
+
" Dashboard"
|
|
59
|
+
] }) }),
|
|
60
|
+
/* @__PURE__ */ jsx("nav", { className: "flex-1 p-3", children: NAV_ITEMS.map((item) => {
|
|
61
|
+
const isActive = currentPath.startsWith(item.to);
|
|
62
|
+
return /* @__PURE__ */ jsxs(
|
|
63
|
+
Link,
|
|
64
|
+
{
|
|
65
|
+
to: item.to,
|
|
66
|
+
className: `flex items-center gap-2.5 rounded-lg px-3 py-2 text-sm transition-colors ${isActive ? "bg-gray-800 text-white" : "text-gray-400 hover:bg-gray-800/50 hover:text-gray-200"}`,
|
|
67
|
+
children: [
|
|
68
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-500", children: item.icon }),
|
|
69
|
+
item.label,
|
|
70
|
+
item.to === "/sessions" && /* @__PURE__ */ jsx(ActiveSessionsBadge, {})
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
item.to
|
|
74
|
+
);
|
|
75
|
+
}) }),
|
|
76
|
+
/* @__PURE__ */ jsx("div", { className: "border-t border-gray-800 p-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
77
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
78
|
+
/* @__PURE__ */ jsx(
|
|
79
|
+
"a",
|
|
80
|
+
{
|
|
81
|
+
href: "https://github.com/dlupiak/claude-session-dashboard",
|
|
82
|
+
target: "_blank",
|
|
83
|
+
rel: "noopener noreferrer",
|
|
84
|
+
className: "text-gray-500 hover:text-gray-300 transition-colors",
|
|
85
|
+
title: "GitHub Repository",
|
|
86
|
+
children: /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" }) })
|
|
87
|
+
}
|
|
88
|
+
),
|
|
89
|
+
/* @__PURE__ */ jsx(
|
|
90
|
+
"a",
|
|
91
|
+
{
|
|
92
|
+
href: "https://www.npmjs.com/package/claude-session-dashboard",
|
|
93
|
+
target: "_blank",
|
|
94
|
+
rel: "noopener noreferrer",
|
|
95
|
+
className: "text-gray-500 hover:text-gray-300 transition-colors",
|
|
96
|
+
title: "npm Package",
|
|
97
|
+
children: /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M0 0v16h16V0H0zm13 13H8V5H5v8H3V3h10v10z" }) })
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
] }),
|
|
101
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-600", children: "Read-only" })
|
|
102
|
+
] }) })
|
|
103
|
+
] }),
|
|
104
|
+
/* @__PURE__ */ jsx("main", { className: "flex-1 overflow-auto", children: /* @__PURE__ */ jsx("div", { className: "mx-auto max-w-5xl px-6 py-6", children }) })
|
|
105
|
+
] });
|
|
106
|
+
}
|
|
107
|
+
function DashboardLayout() {
|
|
108
|
+
return /* @__PURE__ */ jsx(AppShell, { children: /* @__PURE__ */ jsx(Outlet, {}) });
|
|
109
|
+
}
|
|
110
|
+
export {
|
|
111
|
+
DashboardLayout as component
|
|
112
|
+
};
|
|
@@ -5,11 +5,12 @@ import { c as createSsrRpc } from "./createSsrRpc-CVg2UDl0.js";
|
|
|
5
5
|
import { c as createServerFn } from "../server.js";
|
|
6
6
|
import { useRef, useCallback, useState, useEffect, useMemo } from "react";
|
|
7
7
|
import { format } from "date-fns";
|
|
8
|
-
import {
|
|
8
|
+
import { a as formatDuration, f as formatTokenCount, e as formatDateTime, c as formatUSD } from "./format-DIZHV7IJ.js";
|
|
9
9
|
import { ResponsiveContainer, AreaChart, YAxis, Tooltip, ReferenceLine, Area } from "recharts";
|
|
10
10
|
import { s as settingsQuery } from "./settings.queries-DSQd324O.js";
|
|
11
|
-
import { g as getMergedPricing, c as calculateSessionCost, u as useSessionCost } from "./useSessionCost-
|
|
12
|
-
import { a as
|
|
11
|
+
import { g as getMergedPricing, c as calculateSessionCost, u as useSessionCost, E as ExportDropdown, d as downloadFile, e as sessionToJSON } from "./useSessionCost-CYs5UOX-.js";
|
|
12
|
+
import { a as activeSessionsQuery } from "./sessions.queries-B5ZBiVJy.js";
|
|
13
|
+
import { b as Route, u as usePrivacy } from "./router-Cb_hBXHI.js";
|
|
13
14
|
import "@tanstack/history";
|
|
14
15
|
import "@tanstack/router-core/ssr/client";
|
|
15
16
|
import "@tanstack/router-core";
|
|
@@ -24,11 +25,12 @@ import "zod";
|
|
|
24
25
|
const getSessionDetail = createServerFn({
|
|
25
26
|
method: "GET"
|
|
26
27
|
}).inputValidator((input) => input).handler(createSsrRpc("ff8a3161afdfa175e9c519e4146a56ab5bce6e80745e99cfc2191ebbb7a859bb"));
|
|
27
|
-
function sessionDetailQuery(sessionId, projectPath) {
|
|
28
|
+
function sessionDetailQuery(sessionId, projectPath, isActive) {
|
|
28
29
|
return queryOptions({
|
|
29
30
|
queryKey: ["session", "detail", sessionId],
|
|
30
31
|
queryFn: () => getSessionDetail({ data: { sessionId, projectPath } }),
|
|
31
|
-
staleTime: 3e4
|
|
32
|
+
staleTime: isActive ? 2e3 : 3e4,
|
|
33
|
+
refetchInterval: isActive ? 5e3 : void 0
|
|
32
34
|
});
|
|
33
35
|
}
|
|
34
36
|
function buildTimelineChartData(turns, agents, skills, errors) {
|
|
@@ -159,10 +161,10 @@ function shortenToolName(name) {
|
|
|
159
161
|
}
|
|
160
162
|
const TOOL_COLORS = {
|
|
161
163
|
// File reading
|
|
162
|
-
Read: "#
|
|
163
|
-
//
|
|
164
|
-
Grep: "#
|
|
165
|
-
Glob: "#
|
|
164
|
+
Read: "#e09070",
|
|
165
|
+
// brand-400
|
|
166
|
+
Grep: "#e09070",
|
|
167
|
+
Glob: "#e09070",
|
|
166
168
|
// File writing
|
|
167
169
|
Write: "#34d399",
|
|
168
170
|
// emerald-400
|
|
@@ -380,7 +382,6 @@ function TimelineChart({ data, width, onHover }) {
|
|
|
380
382
|
y: agentLaneYs[i],
|
|
381
383
|
toX,
|
|
382
384
|
laneHeight: LANE_HEIGHT,
|
|
383
|
-
leftMargin: LEFT_MARGIN,
|
|
384
385
|
onHover,
|
|
385
386
|
getPosition
|
|
386
387
|
},
|
|
@@ -524,7 +525,6 @@ function AgentLaneSVG({
|
|
|
524
525
|
y,
|
|
525
526
|
toX,
|
|
526
527
|
laneHeight,
|
|
527
|
-
leftMargin,
|
|
528
528
|
onHover,
|
|
529
529
|
getPosition
|
|
530
530
|
}) {
|
|
@@ -825,7 +825,7 @@ function StatBadge({
|
|
|
825
825
|
color
|
|
826
826
|
}) {
|
|
827
827
|
const colorMap = {
|
|
828
|
-
blue: "bg-
|
|
828
|
+
blue: "bg-brand-500/15 text-brand-400",
|
|
829
829
|
purple: "bg-purple-500/15 text-purple-400",
|
|
830
830
|
gray: "bg-gray-800 text-gray-300",
|
|
831
831
|
indigo: "bg-indigo-500/15 text-indigo-400",
|
|
@@ -898,7 +898,7 @@ function ContextWindowPanel({ contextWindow, tokens }) {
|
|
|
898
898
|
/* @__PURE__ */ jsx(
|
|
899
899
|
"div",
|
|
900
900
|
{
|
|
901
|
-
className: "bg-
|
|
901
|
+
className: "bg-brand-500",
|
|
902
902
|
style: { width: `${messagesPct}%` },
|
|
903
903
|
title: `Messages: ~${formatTokenCount(messagesEstimate)}`
|
|
904
904
|
}
|
|
@@ -932,7 +932,7 @@ function ContextWindowPanel({ contextWindow, tokens }) {
|
|
|
932
932
|
"system"
|
|
933
933
|
] }),
|
|
934
934
|
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
|
|
935
|
-
/* @__PURE__ */ jsx("span", { className: "inline-block h-2 w-2 rounded-sm bg-
|
|
935
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block h-2 w-2 rounded-sm bg-brand-500" }),
|
|
936
936
|
"messages"
|
|
937
937
|
] }),
|
|
938
938
|
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
|
|
@@ -957,7 +957,7 @@ function ContextWindowPanel({ contextWindow, tokens }) {
|
|
|
957
957
|
label: "Messages",
|
|
958
958
|
value: messagesEstimate,
|
|
959
959
|
total: contextLimit,
|
|
960
|
-
color: "bg-
|
|
960
|
+
color: "bg-brand-500",
|
|
961
961
|
prefix: "~"
|
|
962
962
|
}
|
|
963
963
|
),
|
|
@@ -984,8 +984,8 @@ function ContextWindowPanel({ contextWindow, tokens }) {
|
|
|
984
984
|
/* @__PURE__ */ jsx("p", { className: "mb-1 text-[10px] text-gray-500", children: "Context growth" }),
|
|
985
985
|
/* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: 96, children: /* @__PURE__ */ jsxs(AreaChart, { data: chartData, children: [
|
|
986
986
|
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: "contextGrad", x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
987
|
-
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "#
|
|
988
|
-
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "#
|
|
987
|
+
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "#d97757", stopOpacity: 0.3 }),
|
|
988
|
+
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "#d97757", stopOpacity: 0.05 })
|
|
989
989
|
] }) }),
|
|
990
990
|
/* @__PURE__ */ jsx(
|
|
991
991
|
YAxis,
|
|
@@ -998,8 +998,8 @@ function ContextWindowPanel({ contextWindow, tokens }) {
|
|
|
998
998
|
Tooltip,
|
|
999
999
|
{
|
|
1000
1000
|
contentStyle: {
|
|
1001
|
-
background: "#
|
|
1002
|
-
border: "1px solid #
|
|
1001
|
+
background: "#1c1c1a",
|
|
1002
|
+
border: "1px solid #3d3b36",
|
|
1003
1003
|
borderRadius: "8px",
|
|
1004
1004
|
fontSize: "11px"
|
|
1005
1005
|
},
|
|
@@ -1030,7 +1030,7 @@ function ContextWindowPanel({ contextWindow, tokens }) {
|
|
|
1030
1030
|
{
|
|
1031
1031
|
type: "stepAfter",
|
|
1032
1032
|
dataKey: "context",
|
|
1033
|
-
stroke: "#
|
|
1033
|
+
stroke: "#d97757",
|
|
1034
1034
|
fill: "url(#contextGrad)",
|
|
1035
1035
|
strokeWidth: 1.5,
|
|
1036
1036
|
dot: false
|
|
@@ -1090,7 +1090,7 @@ function CategoryRow({
|
|
|
1090
1090
|
function TokenBreakdown({ tokens }) {
|
|
1091
1091
|
const allTotal = tokens.inputTokens + tokens.outputTokens + tokens.cacheReadInputTokens + tokens.cacheCreationInputTokens;
|
|
1092
1092
|
const items = [
|
|
1093
|
-
{ label: "Input", value: tokens.inputTokens, color: "bg-
|
|
1093
|
+
{ label: "Input", value: tokens.inputTokens, color: "bg-brand-400" },
|
|
1094
1094
|
{ label: "Output", value: tokens.outputTokens, color: "bg-emerald-400" },
|
|
1095
1095
|
{ label: "Cache Read", value: tokens.cacheReadInputTokens, color: "bg-amber-400" },
|
|
1096
1096
|
{ label: "Cache Create", value: tokens.cacheCreationInputTokens, color: "bg-purple-400" }
|
|
@@ -1114,7 +1114,7 @@ function TokenFallback({ tokens }) {
|
|
|
1114
1114
|
const activeTotal = tokens.inputTokens + tokens.outputTokens;
|
|
1115
1115
|
const allTotal = activeTotal + tokens.cacheReadInputTokens + tokens.cacheCreationInputTokens;
|
|
1116
1116
|
const items = [
|
|
1117
|
-
{ label: "Input", value: tokens.inputTokens, color: "text-
|
|
1117
|
+
{ label: "Input", value: tokens.inputTokens, color: "text-brand-400" },
|
|
1118
1118
|
{ label: "Output", value: tokens.outputTokens, color: "text-emerald-400" },
|
|
1119
1119
|
{ label: "Cache Read", value: tokens.cacheReadInputTokens, color: "text-amber-400" },
|
|
1120
1120
|
{ label: "Cache Create", value: tokens.cacheCreationInputTokens, color: "text-purple-400" }
|
|
@@ -1165,9 +1165,9 @@ function ToolUsagePanel({
|
|
|
1165
1165
|
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
|
|
1166
1166
|
"div",
|
|
1167
1167
|
{
|
|
1168
|
-
className: "h-4 rounded bg-
|
|
1168
|
+
className: "h-4 rounded bg-brand-500/20",
|
|
1169
1169
|
style: { width: `${count / maxCount * 100}%` },
|
|
1170
|
-
children: /* @__PURE__ */ jsx("span", { className: "px-1.5 text-xs text-
|
|
1170
|
+
children: /* @__PURE__ */ jsx("span", { className: "px-1.5 text-xs text-brand-300", children: count })
|
|
1171
1171
|
}
|
|
1172
1172
|
) })
|
|
1173
1173
|
] }, tool)) })
|
|
@@ -1181,7 +1181,7 @@ function ErrorPanel({ errors }) {
|
|
|
1181
1181
|
errors.length,
|
|
1182
1182
|
")"
|
|
1183
1183
|
] }),
|
|
1184
|
-
/* @__PURE__ */ jsx("div", { className: "mt-3 space-y-2", children: errors.map((error, i) => /* @__PURE__ */ jsxs("div", { className: "rounded-lg bg-red-950/30 p-2.5", children: [
|
|
1184
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3 max-h-64 space-y-2 overflow-y-auto", children: errors.map((error, i) => /* @__PURE__ */ jsxs("div", { className: "rounded-lg bg-red-950/30 p-2.5", children: [
|
|
1185
1185
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1186
1186
|
/* @__PURE__ */ jsx("span", { className: "text-xs font-mono text-red-300", children: error.type }),
|
|
1187
1187
|
error.timestamp && /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-500", children: formatDateTime(error.timestamp) })
|
|
@@ -1342,8 +1342,8 @@ const statusConfig = {
|
|
|
1342
1342
|
pending: { label: "Pending", bg: "bg-gray-500/20", text: "text-gray-400" },
|
|
1343
1343
|
in_progress: {
|
|
1344
1344
|
label: "In Progress",
|
|
1345
|
-
bg: "bg-
|
|
1346
|
-
text: "text-
|
|
1345
|
+
bg: "bg-brand-500/20",
|
|
1346
|
+
text: "text-brand-400"
|
|
1347
1347
|
},
|
|
1348
1348
|
completed: {
|
|
1349
1349
|
label: "Done",
|
|
@@ -1437,7 +1437,7 @@ function CostEstimationPanel({ tokensByModel }) {
|
|
|
1437
1437
|
}
|
|
1438
1438
|
function CategoryBreakdown({ cost }) {
|
|
1439
1439
|
const categories = [
|
|
1440
|
-
{ label: "Input tokens", value: cost.byCategory.input, color: "text-
|
|
1440
|
+
{ label: "Input tokens", value: cost.byCategory.input, color: "text-brand-400" },
|
|
1441
1441
|
{ label: "Output tokens", value: cost.byCategory.output, color: "text-emerald-400" },
|
|
1442
1442
|
{ label: "Cache read", value: cost.byCategory.cacheRead, color: "text-amber-400" },
|
|
1443
1443
|
{ label: "Cache write", value: cost.byCategory.cacheWrite, color: "text-purple-400" }
|
|
@@ -1450,7 +1450,7 @@ function CategoryBreakdown({ cost }) {
|
|
|
1450
1450
|
function CostBar({ cost }) {
|
|
1451
1451
|
if (cost.totalUSD === 0) return null;
|
|
1452
1452
|
const segments = [
|
|
1453
|
-
{ key: "input", value: cost.byCategory.input, color: "bg-
|
|
1453
|
+
{ key: "input", value: cost.byCategory.input, color: "bg-brand-400" },
|
|
1454
1454
|
{ key: "output", value: cost.byCategory.output, color: "bg-emerald-400" },
|
|
1455
1455
|
{ key: "cacheRead", value: cost.byCategory.cacheRead, color: "bg-amber-400" },
|
|
1456
1456
|
{ key: "cacheWrite", value: cost.byCategory.cacheWrite, color: "bg-purple-400" }
|
|
@@ -1510,6 +1510,19 @@ function CostSummaryLine({ tokensByModel }) {
|
|
|
1510
1510
|
}
|
|
1511
1511
|
);
|
|
1512
1512
|
}
|
|
1513
|
+
function ActiveSessionBanner() {
|
|
1514
|
+
return /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center gap-2 rounded-lg border border-emerald-800 bg-emerald-900/30 px-4 py-2 text-sm text-emerald-300", children: [
|
|
1515
|
+
/* @__PURE__ */ jsxs("span", { className: "relative flex h-2 w-2", children: [
|
|
1516
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-75" }),
|
|
1517
|
+
/* @__PURE__ */ jsx("span", { className: "relative inline-flex h-2 w-2 rounded-full bg-emerald-500" })
|
|
1518
|
+
] }),
|
|
1519
|
+
"This session is currently active. Data refreshes automatically."
|
|
1520
|
+
] });
|
|
1521
|
+
}
|
|
1522
|
+
function useIsSessionActive(sessionId) {
|
|
1523
|
+
const { data: activeSessions } = useQuery(activeSessionsQuery);
|
|
1524
|
+
return activeSessions?.some((s) => s.sessionId === sessionId) ?? false;
|
|
1525
|
+
}
|
|
1513
1526
|
function SessionDetailPage() {
|
|
1514
1527
|
const {
|
|
1515
1528
|
sessionId
|
|
@@ -1519,13 +1532,15 @@ function SessionDetailPage() {
|
|
|
1519
1532
|
} = Route.useSearch();
|
|
1520
1533
|
const {
|
|
1521
1534
|
privacyMode,
|
|
1522
|
-
anonymizeProjectName
|
|
1535
|
+
anonymizeProjectName,
|
|
1536
|
+
anonymizeBranch
|
|
1523
1537
|
} = usePrivacy();
|
|
1538
|
+
const isActive = useIsSessionActive(sessionId);
|
|
1524
1539
|
const {
|
|
1525
1540
|
data: detail,
|
|
1526
1541
|
isLoading,
|
|
1527
1542
|
error
|
|
1528
|
-
} = useQuery(sessionDetailQuery(sessionId, project));
|
|
1543
|
+
} = useQuery(sessionDetailQuery(sessionId, project, isActive));
|
|
1529
1544
|
if (isLoading) {
|
|
1530
1545
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1531
1546
|
/* @__PURE__ */ jsx("div", { className: "h-8 w-48 animate-pulse rounded bg-gray-800" }),
|
|
@@ -1538,19 +1553,20 @@ function SessionDetailPage() {
|
|
|
1538
1553
|
"Failed to load session: ",
|
|
1539
1554
|
error?.message ?? "Not found"
|
|
1540
1555
|
] }),
|
|
1541
|
-
/* @__PURE__ */ jsx(Link, { to: "/sessions", className: "mt-2 inline-block text-sm text-
|
|
1556
|
+
/* @__PURE__ */ jsx(Link, { to: "/sessions", className: "mt-2 inline-block text-sm text-brand-300 hover:underline", children: "Back to sessions" })
|
|
1542
1557
|
] });
|
|
1543
1558
|
}
|
|
1544
1559
|
const startedAt = detail.turns[0]?.timestamp;
|
|
1545
1560
|
const endedAt = detail.turns[detail.turns.length - 1]?.timestamp;
|
|
1546
1561
|
const durationMs = startedAt && endedAt ? new Date(endedAt).getTime() - new Date(startedAt).getTime() : 0;
|
|
1547
1562
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
1563
|
+
isActive && /* @__PURE__ */ jsx(ActiveSessionBanner, {}),
|
|
1548
1564
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
|
|
1549
1565
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1550
1566
|
/* @__PURE__ */ jsx(Link, { to: "/sessions", className: "text-xs text-gray-500 hover:text-gray-300", children: "← Sessions" }),
|
|
1551
1567
|
/* @__PURE__ */ jsx("h1", { className: "mt-1 text-xl font-bold text-white", children: privacyMode ? anonymizeProjectName(detail.projectName) : detail.projectName }),
|
|
1552
1568
|
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-3 text-xs text-gray-400", children: [
|
|
1553
|
-
detail.branch && /* @__PURE__ */ jsx("span", { className: "font-mono", children: detail.branch }),
|
|
1569
|
+
detail.branch && /* @__PURE__ */ jsx("span", { className: "font-mono", children: anonymizeBranch(detail.branch) }),
|
|
1554
1570
|
startedAt && /* @__PURE__ */ jsx("span", { children: formatDateTime(startedAt) }),
|
|
1555
1571
|
/* @__PURE__ */ jsx("span", { children: formatDuration(durationMs) }),
|
|
1556
1572
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
@@ -1561,7 +1577,13 @@ function SessionDetailPage() {
|
|
|
1561
1577
|
] }),
|
|
1562
1578
|
detail.models.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-1 flex gap-1", children: detail.models.map((m) => /* @__PURE__ */ jsx("span", { className: "rounded bg-gray-800 px-1.5 py-0.5 text-[10px] font-mono text-gray-400", children: m.replace(/^claude-/, "").split("-202")[0] }, m)) })
|
|
1563
1579
|
] }),
|
|
1564
|
-
/* @__PURE__ */
|
|
1580
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1581
|
+
/* @__PURE__ */ jsx(ExportDropdown, { options: [{
|
|
1582
|
+
label: "Export Session (JSON)",
|
|
1583
|
+
onClick: () => downloadFile(sessionToJSON(detail), `session-${sessionId.slice(0, 8)}.json`, "application/json")
|
|
1584
|
+
}] }),
|
|
1585
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-gray-600", children: sessionId.slice(0, 8) })
|
|
1586
|
+
] })
|
|
1565
1587
|
] }),
|
|
1566
1588
|
/* @__PURE__ */ jsxs("div", { className: "mt-6 grid grid-cols-1 gap-4 md:grid-cols-2", children: [
|
|
1567
1589
|
/* @__PURE__ */ jsx(ContextWindowPanel, { contextWindow: detail.contextWindow, tokens: detail.totalTokens }),
|
|
@@ -1570,7 +1592,7 @@ function SessionDetailPage() {
|
|
|
1570
1592
|
/* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(CostEstimationPanel, { tokensByModel: detail.tokensByModel }) }),
|
|
1571
1593
|
(detail.agents.length > 0 || detail.skills.length > 0) && /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(AgentsSkillsPanel, { agents: detail.agents, skills: detail.skills }) }),
|
|
1572
1594
|
detail.tasks.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(TasksPanel, { tasks: detail.tasks }) }),
|
|
1573
|
-
/* @__PURE__ */ jsx(ErrorPanel, { errors: detail.errors }),
|
|
1595
|
+
/* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(ErrorPanel, { errors: detail.errors }) }),
|
|
1574
1596
|
/* @__PURE__ */ jsxs("div", { className: "mt-6", children: [
|
|
1575
1597
|
/* @__PURE__ */ jsx("h2", { className: "mb-3 text-sm font-semibold text-gray-300", children: "Timeline" }),
|
|
1576
1598
|
/* @__PURE__ */ jsx(TimelineEventsChart, { turns: detail.turns, agents: detail.agents, skills: detail.skills, errors: detail.errors })
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
const tsrStartManifest = () => ({ "routes": { "__root__": { "filePath": "/home/runner/work/claude-session-dashboard/claude-session-dashboard/apps/web/src/routes/__root.tsx", "children": ["/", "/_dashboard"], "preloads": ["/assets/main-CfJIADCp.js"], "assets": [] }, "/": { "filePath": "/home/runner/work/claude-session-dashboard/claude-session-dashboard/apps/web/src/routes/index.tsx" }, "/_dashboard": { "filePath": "/home/runner/work/claude-session-dashboard/claude-session-dashboard/apps/web/src/routes/_dashboard.tsx", "children": ["/_dashboard/settings", "/_dashboard/stats", "/_dashboard/sessions/$sessionId", "/_dashboard/sessions/"], "assets": [], "preloads": ["/assets/_dashboard-I7m6D7BE.js", "/assets/createServerFn-Bn6_ISOt.js", "/assets/sessions.queries-CrJg4dYU.js"] }, "/_dashboard/settings": { "filePath": "/home/runner/work/claude-session-dashboard/claude-session-dashboard/apps/web/src/routes/_dashboard/settings.tsx", "assets": [], "preloads": ["/assets/settings-C4_lsEzl.js", "/assets/settings.types-9Qf5WcRY.js"] }, "/_dashboard/stats": { "filePath": "/home/runner/work/claude-session-dashboard/claude-session-dashboard/apps/web/src/routes/_dashboard/stats.tsx", "assets": [], "preloads": ["/assets/stats-_r1gmaTe.js", "/assets/format-Bsprb3az.js", "/assets/useSessionCost-DPZ-ubM1.js", "/assets/settings.types-9Qf5WcRY.js"] }, "/_dashboard/sessions/$sessionId": { "filePath": "/home/runner/work/claude-session-dashboard/claude-session-dashboard/apps/web/src/routes/_dashboard/sessions/$sessionId.tsx", "assets": [], "preloads": ["/assets/_sessionId-DEliIff6.js", "/assets/format-Bsprb3az.js", "/assets/useSessionCost-DPZ-ubM1.js", "/assets/settings.types-9Qf5WcRY.js"] }, "/_dashboard/sessions/": { "filePath": "/home/runner/work/claude-session-dashboard/claude-session-dashboard/apps/web/src/routes/_dashboard/sessions/index.tsx", "assets": [], "preloads": ["/assets/index-BkqRvnEf.js", "/assets/format-Bsprb3az.js"] } }, "clientEntry": "/assets/main-CfJIADCp.js" });
|
|
2
|
+
export {
|
|
3
|
+
tsrStartManifest
|
|
4
|
+
};
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import * as os from "node:os";
|
|
3
|
-
|
|
3
|
+
function resolveClaudeDir() {
|
|
4
|
+
if (process.env.CLAUDE_HOME) {
|
|
5
|
+
return path.resolve(process.env.CLAUDE_HOME);
|
|
6
|
+
}
|
|
7
|
+
return path.join(os.homedir(), ".claude");
|
|
8
|
+
}
|
|
9
|
+
const CLAUDE_DIR = resolveClaudeDir();
|
|
4
10
|
function getProjectsDir() {
|
|
5
11
|
return path.join(CLAUDE_DIR, "projects");
|
|
6
12
|
}
|
|
@@ -17,9 +23,9 @@ function extractSessionId(filename) {
|
|
|
17
23
|
return filename.replace(/\.jsonl$/, "");
|
|
18
24
|
}
|
|
19
25
|
export {
|
|
20
|
-
|
|
26
|
+
getProjectsDir as a,
|
|
21
27
|
extractSessionId as b,
|
|
22
28
|
decodeProjectDirName as d,
|
|
23
29
|
extractProjectName as e,
|
|
24
|
-
|
|
30
|
+
getStatsPath as g
|
|
25
31
|
};
|
|
@@ -48,9 +48,9 @@ function formatUSD(amount) {
|
|
|
48
48
|
return `$${amount.toFixed(2)}`;
|
|
49
49
|
}
|
|
50
50
|
export {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
formatDuration as a,
|
|
52
|
+
formatRelativeTime as b,
|
|
53
|
+
formatUSD as c,
|
|
54
54
|
formatBytes as d,
|
|
55
55
|
formatDateTime as e,
|
|
56
56
|
formatTokenCount as f
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect, useRef, useCallback, useMemo } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { useQuery } from "@tanstack/react-query";
|
|
4
4
|
import { Link, useNavigate } from "@tanstack/react-router";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import
|
|
5
|
+
import { p as paginatedSessionListQuery, a as activeSessionsQuery } from "./sessions.queries-B5ZBiVJy.js";
|
|
6
|
+
import { a as formatDuration, b as formatRelativeTime, d as formatBytes } from "./format-DIZHV7IJ.js";
|
|
7
|
+
import { u as usePrivacy, a as Route } from "./router-Cb_hBXHI.js";
|
|
8
|
+
import "./createSsrRpc-CVg2UDl0.js";
|
|
9
|
+
import "../server.js";
|
|
10
10
|
import "@tanstack/history";
|
|
11
11
|
import "@tanstack/router-core/ssr/client";
|
|
12
12
|
import "@tanstack/router-core";
|
|
@@ -16,41 +16,8 @@ import "h3-v2";
|
|
|
16
16
|
import "tiny-invariant";
|
|
17
17
|
import "seroval";
|
|
18
18
|
import "@tanstack/react-router/ssr/server";
|
|
19
|
+
import "zod";
|
|
19
20
|
import "date-fns";
|
|
20
|
-
const getSessionList = createServerFn({
|
|
21
|
-
method: "GET"
|
|
22
|
-
}).handler(createSsrRpc("bf8e4a7901f1843bdc9c46be1ad5ad59c615b8bbe611b73eb3ff28f20e43ee0d"));
|
|
23
|
-
const getActiveSessionList = createServerFn({
|
|
24
|
-
method: "GET"
|
|
25
|
-
}).handler(createSsrRpc("839d29fe93dfa2a6d506af7b48ca25197190a5ff4c796e970ddfdc6e8c98827f"));
|
|
26
|
-
const paginatedSessionsInputSchema = z.object({
|
|
27
|
-
page: z.number().int().min(1),
|
|
28
|
-
pageSize: z.number().int().min(5).max(100),
|
|
29
|
-
search: z.string(),
|
|
30
|
-
status: z.enum(["all", "active", "completed"]),
|
|
31
|
-
project: z.string()
|
|
32
|
-
});
|
|
33
|
-
const getPaginatedSessions = createServerFn({
|
|
34
|
-
method: "GET"
|
|
35
|
-
}).inputValidator((input) => paginatedSessionsInputSchema.parse(input)).handler(createSsrRpc("a3f42f9012fd83586787da8f7cb90649da739dd947d867eb67572f68735ff495"));
|
|
36
|
-
queryOptions({
|
|
37
|
-
queryKey: ["sessions", "list"],
|
|
38
|
-
queryFn: () => getSessionList(),
|
|
39
|
-
refetchInterval: 3e4
|
|
40
|
-
});
|
|
41
|
-
const activeSessionsQuery = queryOptions({
|
|
42
|
-
queryKey: ["sessions", "active"],
|
|
43
|
-
queryFn: () => getActiveSessionList(),
|
|
44
|
-
refetchInterval: 3e3
|
|
45
|
-
});
|
|
46
|
-
function paginatedSessionListQuery(params) {
|
|
47
|
-
return queryOptions({
|
|
48
|
-
queryKey: ["sessions", "paginated", params],
|
|
49
|
-
queryFn: () => getPaginatedSessions({ data: params }),
|
|
50
|
-
placeholderData: keepPreviousData,
|
|
51
|
-
refetchInterval: 3e4
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
21
|
function StatusBadge({ isActive }) {
|
|
55
22
|
if (isActive) {
|
|
56
23
|
return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-full bg-emerald-500/15 px-2.5 py-0.5 text-xs font-medium text-emerald-400", children: [
|
|
@@ -79,9 +46,10 @@ function RunningTimer({ startedAt }) {
|
|
|
79
46
|
return /* @__PURE__ */ jsx("span", { className: "text-emerald-400", children: formatDuration(elapsed) });
|
|
80
47
|
}
|
|
81
48
|
function SessionCard({ session }) {
|
|
82
|
-
const { privacyMode, anonymizePath, anonymizeProjectName } = usePrivacy();
|
|
49
|
+
const { privacyMode, anonymizePath, anonymizeProjectName, anonymizeBranch } = usePrivacy();
|
|
83
50
|
const displayName = privacyMode ? anonymizeProjectName(session.projectName) : session.projectName;
|
|
84
|
-
const displayCwd = session.cwd ?
|
|
51
|
+
const displayCwd = session.cwd ? anonymizePath(session.cwd, session.projectName) : null;
|
|
52
|
+
const displayBranch = session.branch ? anonymizeBranch(session.branch) : null;
|
|
85
53
|
return /* @__PURE__ */ jsxs(
|
|
86
54
|
Link,
|
|
87
55
|
{
|
|
@@ -96,7 +64,7 @@ function SessionCard({ session }) {
|
|
|
96
64
|
/* @__PURE__ */ jsx("h3", { className: "truncate text-sm font-semibold text-white", children: displayName }),
|
|
97
65
|
/* @__PURE__ */ jsx(StatusBadge, { isActive: session.isActive })
|
|
98
66
|
] }),
|
|
99
|
-
|
|
67
|
+
displayBranch && /* @__PURE__ */ jsx("p", { className: "mt-1 truncate text-xs text-gray-500", children: /* @__PURE__ */ jsx("span", { className: "font-mono", children: displayBranch }) })
|
|
100
68
|
] }),
|
|
101
69
|
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-gray-500", children: formatRelativeTime(session.lastActiveAt) })
|
|
102
70
|
] }),
|
|
@@ -162,7 +130,7 @@ function SessionFilters({ projects, activeCount }) {
|
|
|
162
130
|
placeholder: "Search sessions...",
|
|
163
131
|
value: localSearch,
|
|
164
132
|
onChange: (e) => handleSearchChange(e.target.value),
|
|
165
|
-
className: "rounded-lg border border-gray-700 bg-gray-800/50 px-3 py-1.5 text-sm text-gray-200 placeholder-gray-500 outline-none focus:border-
|
|
133
|
+
className: "rounded-lg border border-gray-700 bg-gray-800/50 px-3 py-1.5 text-sm text-gray-200 placeholder-gray-500 outline-none focus:border-brand-500 focus:ring-1 focus:ring-brand-500"
|
|
166
134
|
}
|
|
167
135
|
),
|
|
168
136
|
/* @__PURE__ */ jsx("div", { className: "flex rounded-lg border border-gray-700 text-xs", children: ["all", "active", "completed"].map((s) => /* @__PURE__ */ jsxs(
|
|
@@ -186,7 +154,7 @@ function SessionFilters({ projects, activeCount }) {
|
|
|
186
154
|
{
|
|
187
155
|
value: project,
|
|
188
156
|
onChange: (e) => handleProjectChange(e.target.value),
|
|
189
|
-
className: "rounded-lg border border-gray-700 bg-gray-800/50 px-3 py-1.5 text-sm text-gray-200 outline-none focus:border-
|
|
157
|
+
className: "rounded-lg border border-gray-700 bg-gray-800/50 px-3 py-1.5 text-sm text-gray-200 outline-none focus:border-brand-500",
|
|
190
158
|
children: [
|
|
191
159
|
/* @__PURE__ */ jsx("option", { value: "", children: "All projects" }),
|
|
192
160
|
projects.map((p) => /* @__PURE__ */ jsx("option", { value: p, children: privacyMode ? anonymizeProjectName(p) : p }, p))
|
|
@@ -200,20 +168,20 @@ const VALID_SIZES = [5, 10, 25, 50];
|
|
|
200
168
|
function isValidSize(value) {
|
|
201
169
|
return VALID_SIZES.includes(value);
|
|
202
170
|
}
|
|
203
|
-
function
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (isValidSize(parsed)) {
|
|
211
|
-
setStoredPageSize(parsed);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
} catch {
|
|
171
|
+
function readStoredPageSize() {
|
|
172
|
+
if (typeof window === "undefined") return null;
|
|
173
|
+
try {
|
|
174
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
175
|
+
if (raw !== null) {
|
|
176
|
+
const parsed = Number(raw);
|
|
177
|
+
if (isValidSize(parsed)) return parsed;
|
|
215
178
|
}
|
|
216
|
-
}
|
|
179
|
+
} catch {
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
function usePageSizePreference() {
|
|
184
|
+
const [storedPageSize, setStoredPageSize] = useState(readStoredPageSize);
|
|
217
185
|
const setPageSize = useCallback((size) => {
|
|
218
186
|
if (!isValidSize(size)) return;
|
|
219
187
|
try {
|
|
@@ -244,7 +212,7 @@ function PaginationControls({
|
|
|
244
212
|
{
|
|
245
213
|
value: pageSize,
|
|
246
214
|
onChange: (e) => onPageSizeChange(Number(e.target.value)),
|
|
247
|
-
className: "rounded-lg border border-gray-700 bg-gray-800/50 px-2 py-1 text-xs text-gray-200 outline-none focus:border-
|
|
215
|
+
className: "rounded-lg border border-gray-700 bg-gray-800/50 px-2 py-1 text-xs text-gray-200 outline-none focus:border-brand-500",
|
|
248
216
|
children: VALID_SIZES.map((size) => /* @__PURE__ */ jsxs("option", { value: size, children: [
|
|
249
217
|
size,
|
|
250
218
|
" / page"
|
|
@@ -288,7 +256,7 @@ function PaginationControls({
|
|
|
288
256
|
"button",
|
|
289
257
|
{
|
|
290
258
|
onClick: () => onPageChange(item),
|
|
291
|
-
className: `min-w-[2rem] rounded-lg px-2 py-1.5 text-xs font-mono transition-colors ${item === page ? "bg-
|
|
259
|
+
className: `min-w-[2rem] rounded-lg px-2 py-1.5 text-xs font-mono transition-colors ${item === page ? "bg-brand-600 text-white" : "border border-gray-700 bg-gray-800 text-gray-400 hover:bg-gray-700 hover:text-gray-200"}`,
|
|
292
260
|
children: item
|
|
293
261
|
},
|
|
294
262
|
item
|