@cryptiklemur/lattice 1.23.1 → 1.23.3
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 +3 -3
- package/client/src/components/analytics/ChartCard.tsx +4 -4
- package/client/src/components/analytics/PeriodSelector.tsx +3 -3
- package/client/src/components/analytics/QuickStats.tsx +8 -8
- package/client/src/components/analytics/charts/CacheEfficiencyChart.tsx +4 -4
- package/client/src/components/analytics/charts/CostAreaChart.tsx +3 -3
- package/client/src/components/analytics/charts/CostDistributionChart.tsx +3 -3
- package/client/src/components/analytics/charts/CumulativeCostChart.tsx +3 -3
- package/client/src/components/analytics/charts/DailySummaryCards.tsx +10 -10
- package/client/src/components/analytics/charts/SessionComplexityList.tsx +5 -3
- package/client/src/components/analytics/charts/TokenFlowChart.tsx +3 -3
- package/package.json +1 -1
|
@@ -33,7 +33,7 @@ function SectionHeader(props: { label: string }) {
|
|
|
33
33
|
return (
|
|
34
34
|
<div className="flex items-center gap-3 pt-4 pb-1">
|
|
35
35
|
<div className="h-px flex-1 bg-base-content/10" />
|
|
36
|
-
<span className="text-[
|
|
36
|
+
<span className="text-[10px] font-mono font-bold uppercase tracking-[0.15em] text-base-content/40">{props.label}</span>
|
|
37
37
|
<div className="h-px flex-1 bg-base-content/10" />
|
|
38
38
|
</div>
|
|
39
39
|
);
|
|
@@ -69,7 +69,7 @@ export function AnalyticsView() {
|
|
|
69
69
|
<div className="flex flex-col h-full overflow-hidden bg-base-100 bg-lattice-grid">
|
|
70
70
|
<div className="flex items-center justify-between px-2 sm:px-4 min-h-10 sm:min-h-12 border-b border-base-300 flex-shrink-0">
|
|
71
71
|
<div className="flex items-center gap-2.5">
|
|
72
|
-
<h1 className="text-
|
|
72
|
+
<h1 className="text-[17px] font-mono font-bold text-base-content">Analytics</h1>
|
|
73
73
|
{analytics.loading && analytics.data && (
|
|
74
74
|
<span className="w-3.5 h-3.5 border-2 border-base-content/15 border-t-primary/60 rounded-full animate-spin" />
|
|
75
75
|
)}
|
|
@@ -77,7 +77,7 @@ export function AnalyticsView() {
|
|
|
77
77
|
<PeriodSelector value={analytics.period} onChange={analytics.setPeriod} />
|
|
78
78
|
</div>
|
|
79
79
|
|
|
80
|
-
<div className={"flex-1 overflow-y-auto px-6 py-4 transition-opacity duration-200 " + (analytics.loading && analytics.data ? "opacity-50" : "opacity-100")}>
|
|
80
|
+
<div className={"flex-1 overflow-y-auto px-3 py-3 sm:px-6 sm:py-4 transition-opacity duration-200 " + (analytics.loading && analytics.data ? "opacity-50" : "opacity-100")}>
|
|
81
81
|
{analytics.loading && !analytics.data && (
|
|
82
82
|
<div className="flex flex-col gap-4 max-w-[1200px] mx-auto">
|
|
83
83
|
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
|
@@ -69,8 +69,8 @@ export function ChartCard(props: ChartCardProps) {
|
|
|
69
69
|
|
|
70
70
|
const cardContent = (
|
|
71
71
|
<>
|
|
72
|
-
<div className="flex items-center justify-between mb-4">
|
|
73
|
-
<span className="text-[
|
|
72
|
+
<div className="flex items-center justify-between mb-3 sm:mb-4">
|
|
73
|
+
<span className="text-[11px] font-mono font-bold uppercase tracking-wider text-base-content/40">
|
|
74
74
|
{props.title}
|
|
75
75
|
</span>
|
|
76
76
|
<div className="flex items-center gap-2">
|
|
@@ -121,7 +121,7 @@ export function ChartCard(props: ChartCardProps) {
|
|
|
121
121
|
<div
|
|
122
122
|
ref={cardRef}
|
|
123
123
|
aria-label={props.title}
|
|
124
|
-
className={"rounded-xl border border-base-content/8 bg-base-300/50 p-4 invisible " + (props.className || "")}
|
|
124
|
+
className={"rounded-xl border border-base-content/8 bg-base-300/50 p-3 sm:p-4 invisible " + (props.className || "")}
|
|
125
125
|
>
|
|
126
126
|
{cardContent}
|
|
127
127
|
</div>
|
|
@@ -180,7 +180,7 @@ export function ChartCard(props: ChartCardProps) {
|
|
|
180
180
|
<div
|
|
181
181
|
ref={cardRef}
|
|
182
182
|
aria-label={props.title}
|
|
183
|
-
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 || "")}
|
|
183
|
+
className={"group rounded-xl border border-base-content/8 bg-base-300/50 p-3 sm:p-4 cursor-pointer hover:border-base-content/12 transition-all duration-200 " + (props.className || "")}
|
|
184
184
|
>
|
|
185
185
|
{cardContent}
|
|
186
186
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
type Period = "24h" | "7d" | "30d" | "90d" | "all";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const PERIODS: Array<{ value: Period; label: string }> = [
|
|
4
4
|
{ value: "24h", label: "24h" },
|
|
5
5
|
{ value: "7d", label: "7d" },
|
|
6
6
|
{ value: "30d", label: "30d" },
|
|
@@ -17,7 +17,7 @@ export function PeriodSelector({ value, onChange }: PeriodSelectorProps) {
|
|
|
17
17
|
return (
|
|
18
18
|
<div role="radiogroup" aria-label="Time period" className="flex items-center gap-1">
|
|
19
19
|
{PERIODS.map(function (period) {
|
|
20
|
-
|
|
20
|
+
const isActive = period.value === value;
|
|
21
21
|
return (
|
|
22
22
|
<button
|
|
23
23
|
key={period.value}
|
|
@@ -25,7 +25,7 @@ export function PeriodSelector({ value, onChange }: PeriodSelectorProps) {
|
|
|
25
25
|
aria-checked={isActive}
|
|
26
26
|
onClick={function () { onChange(period.value); }}
|
|
27
27
|
className={[
|
|
28
|
-
"px-2.5 py-1 rounded-md border text-[10px] font-mono font-bold uppercase tracking-widest transition-colors",
|
|
28
|
+
"min-h-[44px] min-w-[36px] px-3 py-2.5 sm:px-2.5 sm:py-1 rounded-md border text-[10px] font-mono font-bold uppercase tracking-widest transition-colors",
|
|
29
29
|
isActive
|
|
30
30
|
? "bg-primary/15 text-primary border-primary/30"
|
|
31
31
|
: "text-base-content/35 border-base-content/8 hover:text-base-content/60 hover:border-base-content/20",
|
|
@@ -62,45 +62,45 @@ export function QuickStats() {
|
|
|
62
62
|
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
|
63
63
|
<div className="bg-base-content/[0.03] border border-base-content/8 rounded-xl p-3.5">
|
|
64
64
|
<div className="flex items-center justify-between mb-1">
|
|
65
|
-
<span className="text-[10px] font-mono font-
|
|
65
|
+
<span className="text-[10px] font-mono font-bold uppercase tracking-wider text-base-content/40">Cost</span>
|
|
66
66
|
{costSparkData.length > 1 && (
|
|
67
67
|
<div role="img" aria-label={"Cost trend: " + (costSparkData[costSparkData.length - 1].v > costSparkData[0].v ? "increasing" : costSparkData[costSparkData.length - 1].v < costSparkData[0].v ? "decreasing" : "stable")}>
|
|
68
68
|
<Sparkline data={costSparkData} stroke={colors.primary} />
|
|
69
69
|
</div>
|
|
70
70
|
)}
|
|
71
71
|
</div>
|
|
72
|
-
<div className="text-[
|
|
72
|
+
<div className="text-[20px] font-mono text-base-content">${d.totalCost.toFixed(2)}</div>
|
|
73
73
|
</div>
|
|
74
74
|
|
|
75
75
|
<div className="bg-base-content/[0.03] border border-base-content/8 rounded-xl p-3.5">
|
|
76
76
|
<div className="flex items-center justify-between mb-1">
|
|
77
|
-
<span className="text-[10px] font-mono font-
|
|
77
|
+
<span className="text-[10px] font-mono font-bold uppercase tracking-wider text-base-content/40">Sessions</span>
|
|
78
78
|
{sessionsSparkData.length > 1 && (
|
|
79
79
|
<div role="img" aria-label={"Sessions trend: " + (sessionsSparkData[sessionsSparkData.length - 1].v > sessionsSparkData[0].v ? "increasing" : sessionsSparkData[sessionsSparkData.length - 1].v < sessionsSparkData[0].v ? "decreasing" : "stable")}>
|
|
80
80
|
<Sparkline data={sessionsSparkData} stroke={colors.success} />
|
|
81
81
|
</div>
|
|
82
82
|
)}
|
|
83
83
|
</div>
|
|
84
|
-
<div className="text-[
|
|
84
|
+
<div className="text-[20px] font-mono text-base-content">{d.totalSessions}</div>
|
|
85
85
|
</div>
|
|
86
86
|
|
|
87
87
|
<div className="bg-base-content/[0.03] border border-base-content/8 rounded-xl p-3.5">
|
|
88
88
|
<div className="flex items-center justify-between mb-1">
|
|
89
|
-
<span className="text-[10px] font-mono font-
|
|
89
|
+
<span className="text-[10px] font-mono font-bold uppercase tracking-wider text-base-content/40">Tokens</span>
|
|
90
90
|
{tokensSparkData.length > 1 && (
|
|
91
91
|
<div role="img" aria-label={"Tokens trend: " + (tokensSparkData[tokensSparkData.length - 1].v > tokensSparkData[0].v ? "increasing" : tokensSparkData[tokensSparkData.length - 1].v < tokensSparkData[0].v ? "decreasing" : "stable")}>
|
|
92
92
|
<Sparkline data={tokensSparkData} stroke={colors.warning} />
|
|
93
93
|
</div>
|
|
94
94
|
)}
|
|
95
95
|
</div>
|
|
96
|
-
<div className="text-[
|
|
96
|
+
<div className="text-[20px] font-mono text-base-content">{formatTokens(totalTokens)}</div>
|
|
97
97
|
</div>
|
|
98
98
|
|
|
99
99
|
<div className="bg-base-content/[0.03] border border-base-content/8 rounded-xl p-3.5">
|
|
100
100
|
<div className="mb-1">
|
|
101
|
-
<span className="text-[10px] font-mono font-
|
|
101
|
+
<span className="text-[10px] font-mono font-bold uppercase tracking-wider text-base-content/40">Cache Hit</span>
|
|
102
102
|
</div>
|
|
103
|
-
<div className="text-[
|
|
103
|
+
<div className="text-[20px] font-mono text-base-content mb-2">{cacheHitPct}%</div>
|
|
104
104
|
<div
|
|
105
105
|
className="w-full h-1 rounded-full bg-base-content/10 overflow-hidden"
|
|
106
106
|
role="progressbar"
|
|
@@ -30,15 +30,15 @@ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?:
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export function CacheEfficiencyChart({ data }: CacheEfficiencyChartProps) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
const fullscreenHeight = useChartFullscreen();
|
|
34
|
+
const colors = getChartColors();
|
|
35
|
+
const displayData = data.map(function (d) {
|
|
36
36
|
return { date: d.date, rate: d.rate * 100 };
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
return (
|
|
40
40
|
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
41
|
-
<AreaChart data={displayData} margin={{ top: 4, right: 4, left: -
|
|
41
|
+
<AreaChart data={displayData} margin={{ top: 4, right: 4, left: -15, bottom: 0 }}>
|
|
42
42
|
<defs>
|
|
43
43
|
<linearGradient id="cacheEffGrad" x1="0" y1="0" x2="0" y2="1">
|
|
44
44
|
<stop offset="5%" stopColor={colors.success} stopOpacity={0.8} />
|
|
@@ -42,11 +42,11 @@ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?:
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export function CostAreaChart({ data }: CostAreaChartProps) {
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
const fullscreenHeight = useChartFullscreen();
|
|
46
|
+
const colors = getChartColors();
|
|
47
47
|
return (
|
|
48
48
|
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
49
|
-
<AreaChart data={data} margin={{ top: 4, right: 4, left: -
|
|
49
|
+
<AreaChart data={data} margin={{ top: 4, right: 4, left: -15, bottom: 0 }}>
|
|
50
50
|
<defs>
|
|
51
51
|
<linearGradient id="opusGrad" x1="0" y1="0" x2="0" y2="1">
|
|
52
52
|
<stop offset="5%" stopColor={colors.secondary} stopOpacity={0.8} />
|
|
@@ -30,11 +30,11 @@ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?:
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export function CostDistributionChart({ data }: CostDistributionChartProps) {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const fullscreenHeight = useChartFullscreen();
|
|
34
|
+
const colors = getChartColors();
|
|
35
35
|
return (
|
|
36
36
|
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
37
|
-
<AreaChart data={data} margin={{ top: 4, right: 4, left: -
|
|
37
|
+
<AreaChart data={data} margin={{ top: 4, right: 4, left: -15, bottom: 0 }}>
|
|
38
38
|
<defs>
|
|
39
39
|
<linearGradient id="distGrad" x1="0" y1="0" x2="0" y2="1">
|
|
40
40
|
<stop offset="5%" stopColor={colors.primary} stopOpacity={0.8} />
|
|
@@ -30,11 +30,11 @@ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?:
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export function CumulativeCostChart({ data }: CumulativeCostChartProps) {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const fullscreenHeight = useChartFullscreen();
|
|
34
|
+
const colors = getChartColors();
|
|
35
35
|
return (
|
|
36
36
|
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
37
|
-
<AreaChart data={data} margin={{ top: 4, right: 4, left: -
|
|
37
|
+
<AreaChart data={data} margin={{ top: 4, right: 4, left: -15, bottom: 0 }}>
|
|
38
38
|
<defs>
|
|
39
39
|
<linearGradient id="cumulativeGrad" x1="0" y1="0" x2="0" y2="1">
|
|
40
40
|
<stop offset="5%" stopColor={colors.primary} stopOpacity={0.7} />
|
|
@@ -14,14 +14,14 @@ interface DailySummaryCardsProps {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function formatCardDate(dateStr: string): string {
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const d = new Date(dateStr + "T00:00:00");
|
|
18
|
+
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
19
19
|
return days[d.getDay()] + " " + (d.getMonth() + 1) + "/" + d.getDate();
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export function DailySummaryCards({ data }: DailySummaryCardsProps) {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const colors = getChartColors();
|
|
24
|
+
const MODEL_COLORS: Record<string, string> = {
|
|
25
25
|
opus: colors.model.opus,
|
|
26
26
|
sonnet: colors.model.sonnet,
|
|
27
27
|
haiku: colors.model.haiku,
|
|
@@ -35,23 +35,23 @@ export function DailySummaryCards({ data }: DailySummaryCardsProps) {
|
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
const reversed = data.slice().reverse();
|
|
39
39
|
|
|
40
40
|
return (
|
|
41
41
|
<div className="flex gap-3 overflow-x-auto snap-x snap-mandatory pb-2 scrollbar-thin">
|
|
42
42
|
{reversed.map(function (d) {
|
|
43
|
-
|
|
43
|
+
const mixEntries = Object.entries(d.modelMix);
|
|
44
44
|
|
|
45
45
|
return (
|
|
46
46
|
<div
|
|
47
47
|
key={d.date}
|
|
48
48
|
className="flex-shrink-0 snap-start rounded-lg bg-base-300 border border-base-content/5 px-3 py-2.5 w-[140px]"
|
|
49
49
|
>
|
|
50
|
-
<p className="text-[
|
|
50
|
+
<p className="text-[10px] font-mono font-bold uppercase tracking-wider text-base-content/40 mb-1.5">
|
|
51
51
|
{formatCardDate(d.date)}
|
|
52
52
|
</p>
|
|
53
53
|
|
|
54
|
-
<div className="text-[
|
|
54
|
+
<div className="text-[11px] font-mono text-base-content space-y-0.5">
|
|
55
55
|
<p><span className="text-base-content/30">sessions </span>{d.sessions}</p>
|
|
56
56
|
<p><span className="text-base-content/30">cost </span>${d.cost.toFixed(2)}</p>
|
|
57
57
|
{d.topTool && (
|
|
@@ -62,8 +62,8 @@ export function DailySummaryCards({ data }: DailySummaryCardsProps) {
|
|
|
62
62
|
{mixEntries.length > 0 && (
|
|
63
63
|
<div className="mt-2 h-[4px] rounded-full overflow-hidden flex bg-base-content/5">
|
|
64
64
|
{mixEntries.map(function (entry) {
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
const model = entry[0];
|
|
66
|
+
const pct = entry[1];
|
|
67
67
|
if (pct <= 0) return null;
|
|
68
68
|
return (
|
|
69
69
|
<div
|
|
@@ -13,11 +13,12 @@ export function SessionComplexityList({ data }: SessionComplexityListProps) {
|
|
|
13
13
|
);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
const maxScore = data.length > 0 ? data[0].score : 1;
|
|
17
17
|
|
|
18
18
|
return (
|
|
19
|
-
<div className="
|
|
20
|
-
|
|
19
|
+
<div className="overflow-x-auto">
|
|
20
|
+
<div className="flex flex-col gap-1 max-h-[400px] overflow-y-auto min-w-[370px]">
|
|
21
|
+
<div className="grid grid-cols-[2.5rem_1fr_4rem_3rem_3rem_3.5rem] gap-2 px-2 py-1 text-[10px] font-mono text-base-content/30 uppercase tracking-wider">
|
|
21
22
|
<span>Rank</span>
|
|
22
23
|
<span>Session</span>
|
|
23
24
|
<span className="text-right">Score</span>
|
|
@@ -58,5 +59,6 @@ export function SessionComplexityList({ data }: SessionComplexityListProps) {
|
|
|
58
59
|
);
|
|
59
60
|
})}
|
|
60
61
|
</div>
|
|
62
|
+
</div>
|
|
61
63
|
);
|
|
62
64
|
}
|
|
@@ -46,11 +46,11 @@ function formatTokens(v: number): string {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export function TokenFlowChart({ data }: TokenFlowChartProps) {
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const fullscreenHeight = useChartFullscreen();
|
|
50
|
+
const colors = getChartColors();
|
|
51
51
|
return (
|
|
52
52
|
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
53
|
-
<AreaChart data={data} margin={{ top: 4, right: 4, left: -
|
|
53
|
+
<AreaChart data={data} margin={{ top: 4, right: 4, left: -15, bottom: 0 }}>
|
|
54
54
|
<defs>
|
|
55
55
|
<linearGradient id="inputGrad" x1="0" y1="0" x2="0" y2="1">
|
|
56
56
|
<stop offset="5%" stopColor={colors.primary} stopOpacity={0.8} />
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cryptiklemur/lattice",
|
|
3
|
-
"version": "1.23.
|
|
3
|
+
"version": "1.23.3",
|
|
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>",
|