@cryptiklemur/lattice 1.10.3 → 1.11.1
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/client/src/components/analytics/AnalyticsView.tsx +44 -2
- package/client/src/components/analytics/ChartCard.tsx +38 -5
- package/client/src/components/analytics/charts/CacheEfficiencyChart.tsx +3 -1
- package/client/src/components/analytics/charts/ContextUtilizationChart.tsx +3 -1
- package/client/src/components/analytics/charts/CostAreaChart.tsx +3 -1
- package/client/src/components/analytics/charts/CostDistributionChart.tsx +3 -1
- package/client/src/components/analytics/charts/CostDonutChart.tsx +3 -1
- package/client/src/components/analytics/charts/CumulativeCostChart.tsx +3 -1
- package/client/src/components/analytics/charts/ResponseTimeScatter.tsx +3 -1
- package/client/src/components/analytics/charts/SessionBubbleChart.tsx +3 -1
- package/client/src/components/analytics/charts/TokenFlowChart.tsx +3 -1
- package/client/src/components/analytics/charts/TokenSankeyChart.tsx +3 -1
- package/client/src/components/analytics/charts/ToolTreemap.tsx +3 -1
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Component } from "react";
|
|
2
|
+
import { BarChart3 } from "lucide-react";
|
|
2
3
|
import { useAnalytics } from "../../hooks/useAnalytics";
|
|
3
4
|
import { useMesh } from "../../hooks/useMesh";
|
|
4
5
|
|
|
@@ -21,8 +22,20 @@ class ChartErrorBoundary extends Component<{ children: React.ReactNode; name: st
|
|
|
21
22
|
return this.props.children;
|
|
22
23
|
}
|
|
23
24
|
}
|
|
25
|
+
|
|
26
|
+
function SectionHeader(props: { label: string }) {
|
|
27
|
+
return (
|
|
28
|
+
<div className="flex items-center gap-3 pt-4 pb-1">
|
|
29
|
+
<div className="h-px flex-1 bg-base-content/6" />
|
|
30
|
+
<span className="text-[9px] font-mono font-bold uppercase tracking-[0.15em] text-base-content/20">{props.label}</span>
|
|
31
|
+
<div className="h-px flex-1 bg-base-content/6" />
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
import { PeriodSelector } from "./PeriodSelector";
|
|
25
37
|
import { ChartCard } from "./ChartCard";
|
|
38
|
+
import { QuickStats } from "./QuickStats";
|
|
26
39
|
import { CostAreaChart } from "./charts/CostAreaChart";
|
|
27
40
|
import { CumulativeCostChart } from "./charts/CumulativeCostChart";
|
|
28
41
|
import { CostDonutChart } from "./charts/CostDonutChart";
|
|
@@ -58,7 +71,18 @@ export function AnalyticsView() {
|
|
|
58
71
|
|
|
59
72
|
<div className="flex-1 overflow-y-auto px-6 py-4">
|
|
60
73
|
{analytics.loading && !analytics.data && (
|
|
61
|
-
<div className="
|
|
74
|
+
<div className="flex flex-col gap-4 max-w-[1200px] mx-auto">
|
|
75
|
+
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
|
76
|
+
{[0, 1, 2, 3].map(function (i) {
|
|
77
|
+
return <div key={i} className="h-24 rounded-xl bg-base-content/[0.03] animate-pulse" />;
|
|
78
|
+
})}
|
|
79
|
+
</div>
|
|
80
|
+
<div className="h-[240px] rounded-xl bg-base-content/[0.03] animate-pulse" />
|
|
81
|
+
<div className="grid grid-cols-2 gap-4">
|
|
82
|
+
<div className="h-[240px] rounded-xl bg-base-content/[0.03] animate-pulse" />
|
|
83
|
+
<div className="h-[240px] rounded-xl bg-base-content/[0.03] animate-pulse" />
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
62
86
|
)}
|
|
63
87
|
|
|
64
88
|
{analytics.error && (
|
|
@@ -67,6 +91,10 @@ export function AnalyticsView() {
|
|
|
67
91
|
|
|
68
92
|
{analytics.data && (
|
|
69
93
|
<div className="flex flex-col gap-4 max-w-[1200px] mx-auto pb-8">
|
|
94
|
+
<QuickStats />
|
|
95
|
+
|
|
96
|
+
<SectionHeader label="Cost" />
|
|
97
|
+
|
|
70
98
|
<ChartCard title="Cost Over Time">
|
|
71
99
|
<CostAreaChart data={analytics.data.costOverTime} />
|
|
72
100
|
</ChartCard>
|
|
@@ -89,6 +117,8 @@ export function AnalyticsView() {
|
|
|
89
117
|
</ChartCard>
|
|
90
118
|
</div>
|
|
91
119
|
|
|
120
|
+
<SectionHeader label="Tokens & Performance" />
|
|
121
|
+
|
|
92
122
|
<ChartCard title="Token Flow">
|
|
93
123
|
<ChartErrorBoundary name="TokenFlow">
|
|
94
124
|
<TokenFlowChart data={analytics.data.tokensOverTime} />
|
|
@@ -121,6 +151,8 @@ export function AnalyticsView() {
|
|
|
121
151
|
</ChartCard>
|
|
122
152
|
</div>
|
|
123
153
|
|
|
154
|
+
<SectionHeader label="Activity" />
|
|
155
|
+
|
|
124
156
|
<ChartCard title="Activity Calendar">
|
|
125
157
|
<ChartErrorBoundary name="Calendar">
|
|
126
158
|
<ActivityCalendar data={analytics.data.activityCalendar} />
|
|
@@ -146,6 +178,8 @@ export function AnalyticsView() {
|
|
|
146
178
|
</ChartErrorBoundary>
|
|
147
179
|
</ChartCard>
|
|
148
180
|
|
|
181
|
+
<SectionHeader label="Tools & Projects" />
|
|
182
|
+
|
|
149
183
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
150
184
|
<ChartCard title="Tool Usage (Treemap)">
|
|
151
185
|
<ChartErrorBoundary name="Treemap">
|
|
@@ -178,6 +212,8 @@ export function AnalyticsView() {
|
|
|
178
212
|
</ChartErrorBoundary>
|
|
179
213
|
</ChartCard>
|
|
180
214
|
|
|
215
|
+
<SectionHeader label="Fleet" />
|
|
216
|
+
|
|
181
217
|
<ChartCard title="Node Fleet">
|
|
182
218
|
<ChartErrorBoundary name="Fleet">
|
|
183
219
|
<NodeFleetOverview nodes={nodes} />
|
|
@@ -187,7 +223,13 @@ export function AnalyticsView() {
|
|
|
187
223
|
)}
|
|
188
224
|
|
|
189
225
|
{!analytics.loading && !analytics.error && !analytics.data && (
|
|
190
|
-
<div className="
|
|
226
|
+
<div className="flex flex-col items-center justify-center py-24 gap-4">
|
|
227
|
+
<BarChart3 size={40} className="text-base-content/10" />
|
|
228
|
+
<div className="text-center">
|
|
229
|
+
<p className="text-[14px] font-mono text-base-content/40">No analytics data yet</p>
|
|
230
|
+
<p className="text-[12px] text-base-content/25 mt-1">Start a Claude session to begin tracking usage</p>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
191
233
|
)}
|
|
192
234
|
</div>
|
|
193
235
|
</div>
|
|
@@ -1,7 +1,40 @@
|
|
|
1
|
-
import { useState, useEffect, useRef } from "react";
|
|
1
|
+
import { useState, useEffect, useRef, createContext, useContext } from "react";
|
|
2
2
|
import { Maximize2, Minimize2 } from "lucide-react";
|
|
3
3
|
import type { ReactNode } from "react";
|
|
4
4
|
|
|
5
|
+
var ChartFullscreenContext = createContext<number | false>(false);
|
|
6
|
+
|
|
7
|
+
export function useChartFullscreen(): number | false {
|
|
8
|
+
return useContext(ChartFullscreenContext);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function FullscreenChartArea(props: { children: ReactNode }) {
|
|
12
|
+
var containerRef = useRef<HTMLDivElement>(null);
|
|
13
|
+
var [height, setHeight] = useState(400);
|
|
14
|
+
|
|
15
|
+
useEffect(function () {
|
|
16
|
+
if (!containerRef.current) return;
|
|
17
|
+
var observer = new ResizeObserver(function (entries) {
|
|
18
|
+
for (var i = 0; i < entries.length; i++) {
|
|
19
|
+
var h = entries[i].contentRect.height;
|
|
20
|
+
if (h > 0) setHeight(h);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
observer.observe(containerRef.current);
|
|
24
|
+
var h = containerRef.current.clientHeight;
|
|
25
|
+
if (h > 0) setHeight(h);
|
|
26
|
+
return function () { observer.disconnect(); };
|
|
27
|
+
}, []);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div ref={containerRef} className="flex-1 p-6 overflow-auto min-h-0">
|
|
31
|
+
<ChartFullscreenContext.Provider value={height - 48}>
|
|
32
|
+
{props.children}
|
|
33
|
+
</ChartFullscreenContext.Provider>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
5
38
|
interface ChartCardProps {
|
|
6
39
|
title: string;
|
|
7
40
|
children: ReactNode;
|
|
@@ -71,7 +104,7 @@ export function ChartCard(props: ChartCardProps) {
|
|
|
71
104
|
openFullscreen();
|
|
72
105
|
}
|
|
73
106
|
}}
|
|
74
|
-
className="text-base-content/20 hover:text-base-content/50 transition-
|
|
107
|
+
className="opacity-0 group-hover:opacity-100 text-base-content/20 hover:text-base-content/50 transition-all duration-200 cursor-pointer p-0.5 rounded hover:bg-base-content/5"
|
|
75
108
|
aria-label={isFullscreen ? "Exit fullscreen" : "Fullscreen"}
|
|
76
109
|
title={isFullscreen ? "Exit fullscreen (Esc)" : "Fullscreen"}
|
|
77
110
|
>
|
|
@@ -144,9 +177,9 @@ export function ChartCard(props: ChartCardProps) {
|
|
|
144
177
|
</button>
|
|
145
178
|
</div>
|
|
146
179
|
</div>
|
|
147
|
-
<
|
|
180
|
+
<FullscreenChartArea>
|
|
148
181
|
{props.children}
|
|
149
|
-
</
|
|
182
|
+
</FullscreenChartArea>
|
|
150
183
|
</div>
|
|
151
184
|
</div>
|
|
152
185
|
</>
|
|
@@ -156,7 +189,7 @@ export function ChartCard(props: ChartCardProps) {
|
|
|
156
189
|
return (
|
|
157
190
|
<div
|
|
158
191
|
ref={cardRef}
|
|
159
|
-
className={"rounded-xl border border-base-content/8 bg-base-300/50 p-4 " + (props.className || "")}
|
|
192
|
+
className={"group rounded-xl border border-base-content/8 bg-base-300/50 p-4 cursor-pointer hover:border-base-content/12 transition-all duration-200 " + (props.className || "")}
|
|
160
193
|
>
|
|
161
194
|
{cardContent}
|
|
162
195
|
</div>
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
Tooltip,
|
|
8
8
|
ResponsiveContainer,
|
|
9
9
|
} from "recharts";
|
|
10
|
+
import { useChartFullscreen } from "../ChartCard";
|
|
10
11
|
|
|
11
12
|
var TICK_STYLE = {
|
|
12
13
|
fontSize: 10,
|
|
@@ -36,12 +37,13 @@ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?:
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export function CacheEfficiencyChart({ data }: CacheEfficiencyChartProps) {
|
|
40
|
+
var fullscreenHeight = useChartFullscreen();
|
|
39
41
|
var displayData = data.map(function (d) {
|
|
40
42
|
return { date: d.date, rate: d.rate * 100 };
|
|
41
43
|
});
|
|
42
44
|
|
|
43
45
|
return (
|
|
44
|
-
<ResponsiveContainer width="100%" height={200}>
|
|
46
|
+
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
45
47
|
<AreaChart data={displayData} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
46
48
|
<defs>
|
|
47
49
|
<linearGradient id="cacheEffGrad" x1="0" y1="0" x2="0" y2="1">
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
Tooltip,
|
|
8
8
|
ResponsiveContainer,
|
|
9
9
|
} from "recharts";
|
|
10
|
+
import { useChartFullscreen } from "../ChartCard";
|
|
10
11
|
|
|
11
12
|
var TICK_STYLE = {
|
|
12
13
|
fontSize: 10,
|
|
@@ -53,6 +54,7 @@ function CustomTooltip({ active, payload }: { active?: boolean; payload?: Array<
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
export function ContextUtilizationChart({ data }: ContextUtilizationChartProps) {
|
|
57
|
+
var fullscreenHeight = useChartFullscreen();
|
|
56
58
|
var sessionMap = new Map<string, { title: string; points: Array<{ messageIndex: number; contextPercent: number }> }>();
|
|
57
59
|
for (var i = 0; i < data.length; i++) {
|
|
58
60
|
var d = data[i];
|
|
@@ -84,7 +86,7 @@ export function ContextUtilizationChart({ data }: ContextUtilizationChartProps)
|
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
return (
|
|
87
|
-
<ResponsiveContainer width="100%" height={200}>
|
|
89
|
+
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
88
90
|
<LineChart data={merged} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
89
91
|
<CartesianGrid strokeDasharray="3 3" stroke={GRID_COLOR} vertical={false} />
|
|
90
92
|
<XAxis dataKey="messageIndex" tick={TICK_STYLE} axisLine={false} tickLine={false} />
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
Tooltip,
|
|
8
8
|
ResponsiveContainer,
|
|
9
9
|
} from "recharts";
|
|
10
|
+
import { useChartFullscreen } from "../ChartCard";
|
|
10
11
|
|
|
11
12
|
var TICK_STYLE = {
|
|
12
13
|
fontSize: 10,
|
|
@@ -48,8 +49,9 @@ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?:
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
export function CostAreaChart({ data }: CostAreaChartProps) {
|
|
52
|
+
var fullscreenHeight = useChartFullscreen();
|
|
51
53
|
return (
|
|
52
|
-
<ResponsiveContainer width="100%" height={200}>
|
|
54
|
+
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
53
55
|
<AreaChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
54
56
|
<defs>
|
|
55
57
|
<linearGradient id="opusGrad" x1="0" y1="0" x2="0" y2="1">
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
Tooltip,
|
|
8
8
|
ResponsiveContainer,
|
|
9
9
|
} from "recharts";
|
|
10
|
+
import { useChartFullscreen } from "../ChartCard";
|
|
10
11
|
|
|
11
12
|
var TICK_STYLE = {
|
|
12
13
|
fontSize: 10,
|
|
@@ -36,8 +37,9 @@ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?:
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export function CostDistributionChart({ data }: CostDistributionChartProps) {
|
|
40
|
+
var fullscreenHeight = useChartFullscreen();
|
|
39
41
|
return (
|
|
40
|
-
<ResponsiveContainer width="100%" height={200}>
|
|
42
|
+
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
41
43
|
<AreaChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
42
44
|
<defs>
|
|
43
45
|
<linearGradient id="distGrad" x1="0" y1="0" x2="0" y2="1">
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer } from "recharts";
|
|
2
|
+
import { useChartFullscreen } from "../ChartCard";
|
|
2
3
|
|
|
3
4
|
var MODEL_COLORS: Record<string, string> = {
|
|
4
5
|
opus: "#a855f7",
|
|
@@ -52,9 +53,10 @@ function CenterLabel({ totalCost }: { totalCost: number }) {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
export function CostDonutChart({ modelUsage, totalCost }: CostDonutChartProps) {
|
|
56
|
+
var fullscreenHeight = useChartFullscreen();
|
|
55
57
|
return (
|
|
56
58
|
<div>
|
|
57
|
-
<ResponsiveContainer width="100%" height={200}>
|
|
59
|
+
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
58
60
|
<PieChart>
|
|
59
61
|
<Pie
|
|
60
62
|
data={modelUsage}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
Tooltip,
|
|
8
8
|
ResponsiveContainer,
|
|
9
9
|
} from "recharts";
|
|
10
|
+
import { useChartFullscreen } from "../ChartCard";
|
|
10
11
|
|
|
11
12
|
var TICK_STYLE = {
|
|
12
13
|
fontSize: 10,
|
|
@@ -36,8 +37,9 @@ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?:
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export function CumulativeCostChart({ data }: CumulativeCostChartProps) {
|
|
40
|
+
var fullscreenHeight = useChartFullscreen();
|
|
39
41
|
return (
|
|
40
|
-
<ResponsiveContainer width="100%" height={200}>
|
|
42
|
+
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
41
43
|
<AreaChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
42
44
|
<defs>
|
|
43
45
|
<linearGradient id="cumulativeGrad" x1="0" y1="0" x2="0" y2="1">
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
Tooltip,
|
|
8
8
|
ResponsiveContainer,
|
|
9
9
|
} from "recharts";
|
|
10
|
+
import { useChartFullscreen } from "../ChartCard";
|
|
10
11
|
|
|
11
12
|
var TICK_STYLE = {
|
|
12
13
|
fontSize: 10,
|
|
@@ -49,6 +50,7 @@ function CustomTooltip({ active, payload }: { active?: boolean; payload?: Array<
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
export function ResponseTimeScatter({ data }: ResponseTimeScatterProps) {
|
|
53
|
+
var fullscreenHeight = useChartFullscreen();
|
|
52
54
|
var models = Array.from(new Set(data.map(function (d) { return d.model; })));
|
|
53
55
|
|
|
54
56
|
var byModel = models.map(function (model) {
|
|
@@ -62,7 +64,7 @@ export function ResponseTimeScatter({ data }: ResponseTimeScatterProps) {
|
|
|
62
64
|
});
|
|
63
65
|
|
|
64
66
|
return (
|
|
65
|
-
<ResponsiveContainer width="100%" height={200}>
|
|
67
|
+
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
66
68
|
<ScatterChart margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
67
69
|
<CartesianGrid strokeDasharray="3 3" stroke={GRID_COLOR} />
|
|
68
70
|
<XAxis
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
ResponsiveContainer,
|
|
9
9
|
ZAxis,
|
|
10
10
|
} from "recharts";
|
|
11
|
+
import { useChartFullscreen } from "../ChartCard";
|
|
11
12
|
|
|
12
13
|
var TICK_STYLE = {
|
|
13
14
|
fontSize: 10,
|
|
@@ -62,6 +63,7 @@ function CustomTooltip({ active, payload }: { active?: boolean; payload?: Array<
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
export function SessionBubbleChart({ data }: SessionBubbleChartProps) {
|
|
66
|
+
var fullscreenHeight = useChartFullscreen();
|
|
65
67
|
var projects = Array.from(new Set(data.map(function (d) { return d.project; })));
|
|
66
68
|
|
|
67
69
|
function getColor(project: string): string {
|
|
@@ -83,7 +85,7 @@ export function SessionBubbleChart({ data }: SessionBubbleChartProps) {
|
|
|
83
85
|
var maxTs = Math.max(...data.map(function (d) { return d.timestamp; }));
|
|
84
86
|
|
|
85
87
|
return (
|
|
86
|
-
<ResponsiveContainer width="100%" height={200}>
|
|
88
|
+
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
87
89
|
<ScatterChart margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
88
90
|
<CartesianGrid strokeDasharray="3 3" stroke={GRID_COLOR} />
|
|
89
91
|
<XAxis
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
Tooltip,
|
|
8
8
|
ResponsiveContainer,
|
|
9
9
|
} from "recharts";
|
|
10
|
+
import { useChartFullscreen } from "../ChartCard";
|
|
10
11
|
|
|
11
12
|
var TICK_STYLE = {
|
|
12
13
|
fontSize: 10,
|
|
@@ -52,8 +53,9 @@ function formatTokens(v: number): string {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
export function TokenFlowChart({ data }: TokenFlowChartProps) {
|
|
56
|
+
var fullscreenHeight = useChartFullscreen();
|
|
55
57
|
return (
|
|
56
|
-
<ResponsiveContainer width="100%" height={200}>
|
|
58
|
+
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
57
59
|
<AreaChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
58
60
|
<defs>
|
|
59
61
|
<linearGradient id="inputGrad" x1="0" y1="0" x2="0" y2="1">
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Sankey, Tooltip, ResponsiveContainer } from "recharts";
|
|
2
|
+
import { useChartFullscreen } from "../ChartCard";
|
|
2
3
|
|
|
3
4
|
var NODE_COLORS: Record<string, string> = {
|
|
4
5
|
"Input Tokens": "oklch(55% 0.25 280)",
|
|
@@ -64,6 +65,7 @@ function SankeyNode({ x, y, width, height, index, payload }: { x: number; y: num
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
export function TokenSankeyChart({ data }: TokenSankeyChartProps) {
|
|
68
|
+
var fullscreenHeight = useChartFullscreen();
|
|
67
69
|
if (!data.links || data.links.length === 0) {
|
|
68
70
|
return (
|
|
69
71
|
<div className="flex items-center justify-center h-[250px] text-base-content/30 font-mono text-[12px]">
|
|
@@ -73,7 +75,7 @@ export function TokenSankeyChart({ data }: TokenSankeyChartProps) {
|
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
return (
|
|
76
|
-
<ResponsiveContainer width="100%" height={250}>
|
|
78
|
+
<ResponsiveContainer width="100%" height={fullscreenHeight || 250}>
|
|
77
79
|
<Sankey
|
|
78
80
|
data={data}
|
|
79
81
|
node={<SankeyNode x={0} y={0} width={0} height={0} index={0} payload={{ name: "" }} />}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Treemap, ResponsiveContainer, Tooltip } from "recharts";
|
|
2
|
+
import { useChartFullscreen } from "../ChartCard";
|
|
2
3
|
|
|
3
4
|
interface ToolTreemapProps {
|
|
4
5
|
data: Array<{ name: string; count: number; avgCost: number }>;
|
|
@@ -76,6 +77,7 @@ function CustomTooltip({ active, payload }: { active?: boolean; payload?: Array<
|
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
export function ToolTreemap({ data }: ToolTreemapProps) {
|
|
80
|
+
var fullscreenHeight = useChartFullscreen();
|
|
79
81
|
if (!data || data.length === 0) {
|
|
80
82
|
return (
|
|
81
83
|
<div className="flex items-center justify-center h-[250px] text-base-content/25 font-mono text-[11px]">
|
|
@@ -94,7 +96,7 @@ export function ToolTreemap({ data }: ToolTreemapProps) {
|
|
|
94
96
|
});
|
|
95
97
|
|
|
96
98
|
return (
|
|
97
|
-
<ResponsiveContainer width="100%" height={250}>
|
|
99
|
+
<ResponsiveContainer width="100%" height={fullscreenHeight || 250}>
|
|
98
100
|
<Treemap
|
|
99
101
|
data={treemapData}
|
|
100
102
|
dataKey="size"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cryptiklemur/lattice",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.1",
|
|
4
4
|
"description": "Multi-machine agentic dashboard for Claude Code. Monitor sessions, manage MCP servers and skills, orchestrate across mesh-networked nodes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Aaron Scherer <me@aaronscherer.me>",
|