@cryptiklemur/lattice 1.23.0 → 1.23.2

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.
@@ -3,19 +3,25 @@ import { BarChart3 } from "lucide-react";
3
3
  import { useAnalytics } from "../../hooks/useAnalytics";
4
4
  import { useMesh } from "../../hooks/useMesh";
5
5
 
6
- class ChartErrorBoundary extends Component<{ children: React.ReactNode; name: string }, { error: Error | null }> {
6
+ class ChartErrorBoundary extends Component<{ children: React.ReactNode; name: string }, { hasError: boolean }> {
7
7
  constructor(props: { children: React.ReactNode; name: string }) {
8
8
  super(props);
9
- this.state = { error: null };
9
+ this.state = { hasError: false };
10
10
  }
11
- static getDerivedStateFromError(error: Error) {
12
- return { error: error };
11
+ static getDerivedStateFromError() {
12
+ return { hasError: true };
13
13
  }
14
14
  render() {
15
- if (this.state.error) {
15
+ if (this.state.hasError) {
16
16
  return (
17
- <div className="flex items-center justify-center h-[200px] text-base-content/25 font-mono text-[11px]">
18
- Chart error: {this.props.name}
17
+ <div className="flex flex-col items-center justify-center h-[200px] gap-3">
18
+ <span className="text-base-content/30 font-mono text-[12px]">Unable to load chart</span>
19
+ <button
20
+ onClick={() => this.setState({ hasError: false })}
21
+ className="text-[11px] font-mono text-primary/60 hover:text-primary transition-colors cursor-pointer px-3 py-1 rounded-md border border-primary/20 hover:border-primary/40"
22
+ >
23
+ Retry
24
+ </button>
19
25
  </div>
20
26
  );
21
27
  }
@@ -26,9 +32,9 @@ class ChartErrorBoundary extends Component<{ children: React.ReactNode; name: st
26
32
  function SectionHeader(props: { label: string }) {
27
33
  return (
28
34
  <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" />
35
+ <div className="h-px flex-1 bg-base-content/10" />
36
+ <span className="text-[10px] font-mono font-bold uppercase tracking-[0.15em] text-base-content/40">{props.label}</span>
37
+ <div className="h-px flex-1 bg-base-content/10" />
32
38
  </div>
33
39
  );
34
40
  }
@@ -55,15 +61,15 @@ import { SessionComplexityList } from "./charts/SessionComplexityList";
55
61
  import { NodeFleetOverview } from "./charts/NodeFleetOverview";
56
62
 
57
63
  export function AnalyticsView() {
58
- var analytics = useAnalytics();
59
- var mesh = useMesh();
60
- var nodes = mesh.nodes;
64
+ const analytics = useAnalytics();
65
+ const mesh = useMesh();
66
+ const nodes = mesh.nodes;
61
67
 
62
68
  return (
63
69
  <div className="flex flex-col h-full overflow-hidden bg-base-100 bg-lattice-grid">
64
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">
65
71
  <div className="flex items-center gap-2.5">
66
- <h1 className="text-sm font-mono font-semibold text-base-content">Analytics</h1>
72
+ <h1 className="text-[17px] font-mono font-bold text-base-content">Analytics</h1>
67
73
  {analytics.loading && analytics.data && (
68
74
  <span className="w-3.5 h-3.5 border-2 border-base-content/15 border-t-primary/60 rounded-full animate-spin" />
69
75
  )}
@@ -71,7 +77,7 @@ export function AnalyticsView() {
71
77
  <PeriodSelector value={analytics.period} onChange={analytics.setPeriod} />
72
78
  </div>
73
79
 
74
- <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")}>
75
81
  {analytics.loading && !analytics.data && (
76
82
  <div className="flex flex-col gap-4 max-w-[1200px] mx-auto">
77
83
  <div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
@@ -98,24 +104,34 @@ export function AnalyticsView() {
98
104
  <SectionHeader label="Cost" />
99
105
 
100
106
  <ChartCard title="Cost Over Time">
101
- <CostAreaChart data={analytics.data.costOverTime} />
107
+ <ChartErrorBoundary name="CostArea">
108
+ <CostAreaChart data={analytics.data.costOverTime} />
109
+ </ChartErrorBoundary>
102
110
  </ChartCard>
103
111
 
104
112
  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
105
113
  <ChartCard title="Cost Breakdown">
106
- <CostDonutChart modelUsage={analytics.data.modelUsage} totalCost={analytics.data.totalCost} />
114
+ <ChartErrorBoundary name="CostDonut">
115
+ <CostDonutChart modelUsage={analytics.data.modelUsage} totalCost={analytics.data.totalCost} />
116
+ </ChartErrorBoundary>
107
117
  </ChartCard>
108
118
  <ChartCard title="Cumulative Cost">
109
- <CumulativeCostChart data={analytics.data.cumulativeCost} />
119
+ <ChartErrorBoundary name="CumulativeCost">
120
+ <CumulativeCostChart data={analytics.data.cumulativeCost} />
121
+ </ChartErrorBoundary>
110
122
  </ChartCard>
111
123
  </div>
112
124
 
113
125
  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
114
126
  <ChartCard title="Cost Distribution">
115
- <CostDistributionChart data={analytics.data.costDistribution} />
127
+ <ChartErrorBoundary name="CostDistribution">
128
+ <CostDistributionChart data={analytics.data.costDistribution} />
129
+ </ChartErrorBoundary>
116
130
  </ChartCard>
117
131
  <ChartCard title="Session Costs">
118
- <SessionBubbleChart data={analytics.data.sessionBubbles} />
132
+ <ChartErrorBoundary name="SessionBubble">
133
+ <SessionBubbleChart data={analytics.data.sessionBubbles} />
134
+ </ChartErrorBoundary>
119
135
  </ChartCard>
120
136
  </div>
121
137
 
@@ -182,13 +198,11 @@ export function AnalyticsView() {
182
198
 
183
199
  <SectionHeader label="Projects" />
184
200
 
185
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
186
- <ChartCard title="Project Comparison">
187
- <ChartErrorBoundary name="Radar">
188
- <ProjectRadar data={analytics.data.projectRadar} />
189
- </ChartErrorBoundary>
190
- </ChartCard>
191
- </div>
201
+ <ChartCard title="Project Comparison">
202
+ <ChartErrorBoundary name="Radar">
203
+ <ProjectRadar data={analytics.data.projectRadar} />
204
+ </ChartErrorBoundary>
205
+ </ChartCard>
192
206
 
193
207
  <ChartCard title="Session Complexity">
194
208
  <ChartErrorBoundary name="Complexity">
@@ -3,14 +3,14 @@ import { useFocusTrap } from "../../hooks/useFocusTrap";
3
3
  import { Maximize2, Minimize2 } from "lucide-react";
4
4
  import type { ReactNode } from "react";
5
5
 
6
- var ChartFullscreenContext = createContext<number | false>(false);
6
+ const ChartFullscreenContext = createContext<number | false>(false);
7
7
 
8
8
  export function useChartFullscreen(): number | false {
9
9
  return useContext(ChartFullscreenContext);
10
10
  }
11
11
 
12
12
  function useViewportChartHeight(): number {
13
- var [h, setH] = useState(Math.round(window.innerHeight * 0.5));
13
+ const [h, setH] = useState(Math.round(window.innerHeight * 0.5));
14
14
  useEffect(function () {
15
15
  function onResize() { setH(Math.round(window.innerHeight * 0.5)); }
16
16
  window.addEventListener("resize", onResize);
@@ -27,13 +27,13 @@ interface ChartCardProps {
27
27
  }
28
28
 
29
29
  export function ChartCard(props: ChartCardProps) {
30
- var [isFullscreen, setIsFullscreen] = useState(false);
31
- var chartHeight = useViewportChartHeight();
32
- var cardRef = useRef<HTMLDivElement>(null);
33
- var [originRect, setOriginRect] = useState<DOMRect | null>(null);
34
- var [animating, setAnimating] = useState(false);
35
- var fullscreenModalRef = useRef<HTMLDivElement>(null);
36
- var closeFullscreenCb = useCallback(function () { closeFullscreen(); }, []);
30
+ const [isFullscreen, setIsFullscreen] = useState(false);
31
+ const chartHeight = useViewportChartHeight();
32
+ const cardRef = useRef<HTMLDivElement>(null);
33
+ const [originRect, setOriginRect] = useState<DOMRect | null>(null);
34
+ const [animating, setAnimating] = useState(false);
35
+ const fullscreenModalRef = useRef<HTMLDivElement>(null);
36
+ const closeFullscreenCb = useCallback(function () { closeFullscreen(); }, []);
37
37
  useFocusTrap(fullscreenModalRef, closeFullscreenCb, isFullscreen);
38
38
 
39
39
  function openFullscreen() {
@@ -67,10 +67,10 @@ export function ChartCard(props: ChartCardProps) {
67
67
  return function () { document.body.style.overflow = ""; };
68
68
  }, [isFullscreen]);
69
69
 
70
- var cardContent = (
70
+ const cardContent = (
71
71
  <>
72
- <div className="flex items-center justify-between mb-4">
73
- <span className="text-[10px] font-mono font-bold uppercase tracking-widest text-base-content/35">
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">
@@ -91,17 +91,19 @@ export function ChartCard(props: ChartCardProps) {
91
91
  </button>
92
92
  </div>
93
93
  </div>
94
- {props.children}
94
+ <div role="img" aria-label={props.title}>
95
+ {props.children}
96
+ </div>
95
97
  </>
96
98
  );
97
99
 
98
100
  if (isFullscreen) {
99
- var overlayStyle: React.CSSProperties = {
101
+ const overlayStyle: React.CSSProperties = {
100
102
  transition: "opacity 250ms cubic-bezier(0.4, 0, 0.2, 1)",
101
103
  opacity: animating ? 0 : 1,
102
104
  };
103
105
 
104
- var modalStyle: React.CSSProperties = {
106
+ const modalStyle: React.CSSProperties = {
105
107
  transition: "all 250ms cubic-bezier(0.4, 0, 0.2, 1)",
106
108
  };
107
109
 
@@ -118,7 +120,8 @@ export function ChartCard(props: ChartCardProps) {
118
120
  <>
119
121
  <div
120
122
  ref={cardRef}
121
- className={"rounded-xl border border-base-content/8 bg-base-300/50 p-4 invisible " + (props.className || "")}
123
+ aria-label={props.title}
124
+ className={"rounded-xl border border-base-content/8 bg-base-300/50 p-3 sm:p-4 invisible " + (props.className || "")}
122
125
  >
123
126
  {cardContent}
124
127
  </div>
@@ -176,7 +179,8 @@ export function ChartCard(props: ChartCardProps) {
176
179
  return (
177
180
  <div
178
181
  ref={cardRef}
179
- 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 || "")}
182
+ aria-label={props.title}
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 || "")}
180
184
  >
181
185
  {cardContent}
182
186
  </div>
@@ -1,6 +1,6 @@
1
1
  type Period = "24h" | "7d" | "30d" | "90d" | "all";
2
2
 
3
- var PERIODS: Array<{ value: Period; label: string }> = [
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
- var isActive = period.value === value;
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",
@@ -31,7 +31,7 @@ function Sparkline({ data, stroke }: SparklineProps) {
31
31
  }
32
32
 
33
33
  export function QuickStats() {
34
- var analytics = useAnalytics();
34
+ const analytics = useAnalytics();
35
35
 
36
36
  if (!analytics.data) {
37
37
  return (
@@ -48,48 +48,67 @@ export function QuickStats() {
48
48
  );
49
49
  }
50
50
 
51
- var d = analytics.data;
52
- var colors = getChartColors();
51
+ const d = analytics.data;
52
+ const colors = getChartColors();
53
53
 
54
- var costSparkData = d.costOverTime.slice(-7).map(function (e: typeof d.costOverTime[number]) { return { v: e.total }; });
55
- var sessionsSparkData = d.sessionsOverTime.slice(-7).map(function (e: typeof d.sessionsOverTime[number]) { return { v: e.count }; });
56
- var tokensSparkData = d.tokensOverTime.slice(-7).map(function (e: typeof d.tokensOverTime[number]) { return { v: e.input + e.output }; });
54
+ const costSparkData = d.costOverTime.slice(-7).map(function (e: typeof d.costOverTime[number]) { return { v: e.total }; });
55
+ const sessionsSparkData = d.sessionsOverTime.slice(-7).map(function (e: typeof d.sessionsOverTime[number]) { return { v: e.count }; });
56
+ const tokensSparkData = d.tokensOverTime.slice(-7).map(function (e: typeof d.tokensOverTime[number]) { return { v: e.input + e.output }; });
57
57
 
58
- var totalTokens = d.totalTokens.input + d.totalTokens.output;
59
- var cacheHitPct = Math.round(d.cacheHitRate * 100);
58
+ const totalTokens = d.totalTokens.input + d.totalTokens.output;
59
+ const cacheHitPct = Math.round(d.cacheHitRate * 100);
60
60
 
61
61
  return (
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-semibold uppercase tracking-wider text-base-content/35">Cost</span>
66
- {costSparkData.length > 1 && <Sparkline data={costSparkData} stroke={colors.primary} />}
65
+ <span className="text-[10px] font-mono font-bold uppercase tracking-wider text-base-content/40">Cost</span>
66
+ {costSparkData.length > 1 && (
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
+ <Sparkline data={costSparkData} stroke={colors.primary} />
69
+ </div>
70
+ )}
67
71
  </div>
68
- <div className="text-[22px] font-mono text-base-content/85">${d.totalCost.toFixed(2)}</div>
72
+ <div className="text-[20px] font-mono text-base-content">${d.totalCost.toFixed(2)}</div>
69
73
  </div>
70
74
 
71
75
  <div className="bg-base-content/[0.03] border border-base-content/8 rounded-xl p-3.5">
72
76
  <div className="flex items-center justify-between mb-1">
73
- <span className="text-[10px] font-mono font-semibold uppercase tracking-wider text-base-content/35">Sessions</span>
74
- {sessionsSparkData.length > 1 && <Sparkline data={sessionsSparkData} stroke={colors.success} />}
77
+ <span className="text-[10px] font-mono font-bold uppercase tracking-wider text-base-content/40">Sessions</span>
78
+ {sessionsSparkData.length > 1 && (
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
+ <Sparkline data={sessionsSparkData} stroke={colors.success} />
81
+ </div>
82
+ )}
75
83
  </div>
76
- <div className="text-[22px] font-mono text-base-content/85">{d.totalSessions}</div>
84
+ <div className="text-[20px] font-mono text-base-content">{d.totalSessions}</div>
77
85
  </div>
78
86
 
79
87
  <div className="bg-base-content/[0.03] border border-base-content/8 rounded-xl p-3.5">
80
88
  <div className="flex items-center justify-between mb-1">
81
- <span className="text-[10px] font-mono font-semibold uppercase tracking-wider text-base-content/35">Tokens</span>
82
- {tokensSparkData.length > 1 && <Sparkline data={tokensSparkData} stroke={colors.warning} />}
89
+ <span className="text-[10px] font-mono font-bold uppercase tracking-wider text-base-content/40">Tokens</span>
90
+ {tokensSparkData.length > 1 && (
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
+ <Sparkline data={tokensSparkData} stroke={colors.warning} />
93
+ </div>
94
+ )}
83
95
  </div>
84
- <div className="text-[22px] font-mono text-base-content/85">{formatTokens(totalTokens)}</div>
96
+ <div className="text-[20px] font-mono text-base-content">{formatTokens(totalTokens)}</div>
85
97
  </div>
86
98
 
87
99
  <div className="bg-base-content/[0.03] border border-base-content/8 rounded-xl p-3.5">
88
100
  <div className="mb-1">
89
- <span className="text-[10px] font-mono font-semibold uppercase tracking-wider text-base-content/35">Cache Hit</span>
101
+ <span className="text-[10px] font-mono font-bold uppercase tracking-wider text-base-content/40">Cache Hit</span>
90
102
  </div>
91
- <div className="text-[22px] font-mono text-base-content/85 mb-2">{cacheHitPct}%</div>
92
- <div className="w-full h-1 rounded-full bg-base-content/10 overflow-hidden">
103
+ <div className="text-[20px] font-mono text-base-content mb-2">{cacheHitPct}%</div>
104
+ <div
105
+ className="w-full h-1 rounded-full bg-base-content/10 overflow-hidden"
106
+ role="progressbar"
107
+ aria-valuenow={cacheHitPct}
108
+ aria-valuemin={0}
109
+ aria-valuemax={100}
110
+ aria-label="Cache hit rate"
111
+ >
93
112
  <div
94
113
  className="h-full rounded-full bg-primary transition-all duration-300"
95
114
  style={{ width: cacheHitPct + "%" }}
@@ -59,7 +59,7 @@ export function ActivityCalendar({ data }: ActivityCalendarProps) {
59
59
  if (!data || data.length === 0) {
60
60
  return (
61
61
  <div className="flex items-center justify-center h-[120px] text-base-content/25 font-mono text-[11px]">
62
- No data
62
+ No data for this period
63
63
  </div>
64
64
  );
65
65
  }
@@ -30,15 +30,15 @@ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?:
30
30
  }
31
31
 
32
32
  export function CacheEfficiencyChart({ data }: CacheEfficiencyChartProps) {
33
- var fullscreenHeight = useChartFullscreen();
34
- var colors = getChartColors();
35
- var displayData = data.map(function (d) {
33
+ constfullscreenHeight = useChartFullscreen();
34
+ constcolors = getChartColors();
35
+ constdisplayData = 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: -20, bottom: 0 }}>
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
- var fullscreenHeight = useChartFullscreen();
46
- var colors = getChartColors();
45
+ constfullscreenHeight = useChartFullscreen();
46
+ constcolors = getChartColors();
47
47
  return (
48
48
  <ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
49
- <AreaChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
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
- var fullscreenHeight = useChartFullscreen();
34
- var colors = getChartColors();
33
+ constfullscreenHeight = useChartFullscreen();
34
+ constcolors = getChartColors();
35
35
  return (
36
36
  <ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
37
- <AreaChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
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
- var fullscreenHeight = useChartFullscreen();
34
- var colors = getChartColors();
33
+ constfullscreenHeight = useChartFullscreen();
34
+ constcolors = getChartColors();
35
35
  return (
36
36
  <ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
37
- <AreaChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
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
- var d = new Date(dateStr + "T00:00:00");
18
- var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
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
- var colors = getChartColors();
24
- var MODEL_COLORS: Record<string, string> = {
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,
@@ -30,28 +30,28 @@ export function DailySummaryCards({ data }: DailySummaryCardsProps) {
30
30
  if (!data || data.length === 0) {
31
31
  return (
32
32
  <div className="flex items-center justify-center h-[100px] text-base-content/25 font-mono text-[11px]">
33
- No data
33
+ No data for this period
34
34
  </div>
35
35
  );
36
36
  }
37
37
 
38
- var reversed = data.slice().reverse();
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
- var mixEntries = Object.entries(d.modelMix);
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-[11px] font-mono font-bold text-base-content/60 mb-1.5">
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-[10px] font-mono text-base-content/50 space-y-0.5">
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
- var model = entry[0];
66
- var pct = entry[1];
65
+ const model = entry[0];
66
+ const pct = entry[1];
67
67
  if (pct <= 0) return null;
68
68
  return (
69
69
  <div
@@ -29,7 +29,7 @@ export function HourlyHeatmap({ data }: HourlyHeatmapProps) {
29
29
  if (!data || data.length === 0) {
30
30
  return (
31
31
  <div className="flex items-center justify-center h-[200px] text-base-content/25 font-mono text-[11px]">
32
- No data
32
+ No data for this period
33
33
  </div>
34
34
  );
35
35
  }
@@ -13,11 +13,12 @@ export function SessionComplexityList({ data }: SessionComplexityListProps) {
13
13
  );
14
14
  }
15
15
 
16
- var maxScore = data.length > 0 ? data[0].score : 1;
16
+ const maxScore = data.length > 0 ? data[0].score : 1;
17
17
 
18
18
  return (
19
- <div className="flex flex-col gap-1 max-h-[400px] overflow-y-auto">
20
- <div className="grid grid-cols-[2.5rem_1fr_4rem_3rem_3rem_3.5rem] gap-2 px-2 py-1 text-[9px] font-mono text-base-content/30 uppercase tracking-wider">
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
  }
@@ -33,7 +33,7 @@ export function SessionTimeline({ data }: SessionTimelineProps) {
33
33
  if (!data || data.length === 0) {
34
34
  return (
35
35
  <div className="flex items-center justify-center h-[200px] text-base-content/25 font-mono text-[11px]">
36
- No data
36
+ No data for this period
37
37
  </div>
38
38
  );
39
39
  }
@@ -46,11 +46,11 @@ function formatTokens(v: number): string {
46
46
  }
47
47
 
48
48
  export function TokenFlowChart({ data }: TokenFlowChartProps) {
49
- var fullscreenHeight = useChartFullscreen();
50
- var colors = getChartColors();
49
+ constfullscreenHeight = useChartFullscreen();
50
+ constcolors = getChartColors();
51
51
  return (
52
52
  <ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
53
- <AreaChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
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.0",
3
+ "version": "1.23.2",
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>",