@cryptiklemur/lattice 1.23.3 → 1.24.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.
@@ -31,7 +31,7 @@ class ChartErrorBoundary extends Component<{ children: React.ReactNode; name: st
31
31
 
32
32
  function SectionHeader(props: { label: string }) {
33
33
  return (
34
- <div className="flex items-center gap-3 pt-4 pb-1">
34
+ <div className="flex items-center gap-3 mb-1">
35
35
  <div className="h-px flex-1 bg-base-content/10" />
36
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" />
@@ -67,9 +67,9 @@ export function AnalyticsView() {
67
67
 
68
68
  return (
69
69
  <div className="flex flex-col h-full overflow-hidden bg-base-100 bg-lattice-grid">
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">
70
+ <div className="flex items-center justify-between px-4 py-3 border-b border-base-300 flex-shrink-0">
71
71
  <div className="flex items-center gap-2.5">
72
- <h1 className="text-[17px] font-mono font-bold text-base-content">Analytics</h1>
72
+ <h1 className="text-[13px] font-mono font-bold text-base-content/90">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
  )}
@@ -98,117 +98,116 @@ export function AnalyticsView() {
98
98
  )}
99
99
 
100
100
  {analytics.data && (
101
- <div className="flex flex-col gap-4 max-w-[1200px] mx-auto pb-8">
101
+ <div className="flex flex-col max-w-[1200px] mx-auto pb-12">
102
102
  <QuickStats />
103
103
 
104
- <SectionHeader label="Cost" />
105
-
106
- <ChartCard title="Cost Over Time">
107
- <ChartErrorBoundary name="CostArea">
108
- <CostAreaChart data={analytics.data.costOverTime} />
109
- </ChartErrorBoundary>
110
- </ChartCard>
111
-
112
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
113
- <ChartCard title="Cost Breakdown">
114
- <ChartErrorBoundary name="CostDonut">
115
- <CostDonutChart modelUsage={analytics.data.modelUsage} totalCost={analytics.data.totalCost} />
104
+ <section className="flex flex-col gap-3 mt-8">
105
+ <SectionHeader label="Spending" />
106
+ <ChartCard title="Spending Over Time">
107
+ <ChartErrorBoundary name="CostArea">
108
+ <CostAreaChart data={analytics.data.costOverTime} />
116
109
  </ChartErrorBoundary>
117
110
  </ChartCard>
118
- <ChartCard title="Cumulative Cost">
119
- <ChartErrorBoundary name="CumulativeCost">
120
- <CumulativeCostChart data={analytics.data.cumulativeCost} />
111
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
112
+ <ChartCard title="Spend by Model">
113
+ <ChartErrorBoundary name="CostDonut">
114
+ <CostDonutChart modelUsage={analytics.data.modelUsage} totalCost={analytics.data.totalCost} />
115
+ </ChartErrorBoundary>
116
+ </ChartCard>
117
+ <ChartCard title="Running Total">
118
+ <ChartErrorBoundary name="CumulativeCost">
119
+ <CumulativeCostChart data={analytics.data.cumulativeCost} />
120
+ </ChartErrorBoundary>
121
+ </ChartCard>
122
+ </div>
123
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
124
+ <ChartCard title="Session Cost Ranges">
125
+ <ChartErrorBoundary name="CostDistribution">
126
+ <CostDistributionChart data={analytics.data.costDistribution} />
127
+ </ChartErrorBoundary>
128
+ </ChartCard>
129
+ <ChartCard title="Cost per Session">
130
+ <ChartErrorBoundary name="SessionBubble">
131
+ <SessionBubbleChart data={analytics.data.sessionBubbles} />
132
+ </ChartErrorBoundary>
133
+ </ChartCard>
134
+ </div>
135
+ </section>
136
+
137
+ <section className="flex flex-col gap-3 mt-10">
138
+ <SectionHeader label="Usage & Speed" />
139
+ <ChartCard title="Token Usage Over Time">
140
+ <ChartErrorBoundary name="TokenFlow">
141
+ <TokenFlowChart data={analytics.data.tokensOverTime} />
121
142
  </ChartErrorBoundary>
122
143
  </ChartCard>
123
- </div>
124
-
125
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
126
- <ChartCard title="Cost Distribution">
127
- <ChartErrorBoundary name="CostDistribution">
128
- <CostDistributionChart data={analytics.data.costDistribution} />
144
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
145
+ <ChartCard title="Cache Hit Rate">
146
+ <ChartErrorBoundary name="CacheEfficiency">
147
+ <CacheEfficiencyChart data={analytics.data.cacheHitRateOverTime} />
148
+ </ChartErrorBoundary>
149
+ </ChartCard>
150
+ <ChartCard title="Response Speed">
151
+ <ChartErrorBoundary name="ResponseTime">
152
+ <ResponseTimeScatter data={analytics.data.responseTimeData} />
153
+ </ChartErrorBoundary>
154
+ </ChartCard>
155
+ </div>
156
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
157
+ <ChartCard title="Context Utilization">
158
+ <ChartErrorBoundary name="ContextUtilization">
159
+ <ContextUtilizationChart data={analytics.data.contextUtilization} />
160
+ </ChartErrorBoundary>
161
+ </ChartCard>
162
+ <ChartCard title="Token Breakdown">
163
+ <ChartErrorBoundary name="Sankey">
164
+ <TokenSankeyChart data={analytics.data.tokenFlowSankey} />
165
+ </ChartErrorBoundary>
166
+ </ChartCard>
167
+ </div>
168
+ </section>
169
+
170
+ <section className="flex flex-col gap-3 mt-10">
171
+ <SectionHeader label="Activity" />
172
+ <ChartCard title="Activity Calendar">
173
+ <ChartErrorBoundary name="Calendar">
174
+ <ActivityCalendar data={analytics.data.activityCalendar} />
129
175
  </ChartErrorBoundary>
130
176
  </ChartCard>
131
- <ChartCard title="Session Costs">
132
- <ChartErrorBoundary name="SessionBubble">
133
- <SessionBubbleChart data={analytics.data.sessionBubbles} />
177
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
178
+ <ChartCard title="Peak Hours">
179
+ <ChartErrorBoundary name="HourlyHeatmap">
180
+ <HourlyHeatmap data={analytics.data.hourlyHeatmap} />
181
+ </ChartErrorBoundary>
182
+ </ChartCard>
183
+ <ChartCard title="Session Timeline">
184
+ <ChartErrorBoundary name="Timeline">
185
+ <SessionTimeline data={analytics.data.sessionTimeline} />
186
+ </ChartErrorBoundary>
187
+ </ChartCard>
188
+ </div>
189
+ <ChartCard title="Daily Summary">
190
+ <ChartErrorBoundary name="DailySummary">
191
+ <DailySummaryCards data={analytics.data.dailySummaries} />
134
192
  </ChartErrorBoundary>
135
193
  </ChartCard>
136
- </div>
137
-
138
- <SectionHeader label="Tokens & Performance" />
139
-
140
- <ChartCard title="Token Flow">
141
- <ChartErrorBoundary name="TokenFlow">
142
- <TokenFlowChart data={analytics.data.tokensOverTime} />
143
- </ChartErrorBoundary>
144
- </ChartCard>
145
-
146
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
147
- <ChartCard title="Cache Efficiency">
148
- <ChartErrorBoundary name="CacheEfficiency">
149
- <CacheEfficiencyChart data={analytics.data.cacheHitRateOverTime} />
150
- </ChartErrorBoundary>
151
- </ChartCard>
152
- <ChartCard title="Response Time vs Tokens">
153
- <ChartErrorBoundary name="ResponseTime">
154
- <ResponseTimeScatter data={analytics.data.responseTimeData} />
155
- </ChartErrorBoundary>
156
- </ChartCard>
157
- </div>
158
-
159
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
160
- <ChartCard title="Context Window Usage">
161
- <ChartErrorBoundary name="ContextUtilization">
162
- <ContextUtilizationChart data={analytics.data.contextUtilization} />
163
- </ChartErrorBoundary>
164
- </ChartCard>
165
- <ChartCard title="Token Flow (Sankey)">
166
- <ChartErrorBoundary name="Sankey">
167
- <TokenSankeyChart data={analytics.data.tokenFlowSankey} />
168
- </ChartErrorBoundary>
169
- </ChartCard>
170
- </div>
171
-
172
- <SectionHeader label="Activity" />
173
-
174
- <ChartCard title="Activity Calendar">
175
- <ChartErrorBoundary name="Calendar">
176
- <ActivityCalendar data={analytics.data.activityCalendar} />
177
- </ChartErrorBoundary>
178
- </ChartCard>
179
-
180
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
181
- <ChartCard title="Hourly Activity">
182
- <ChartErrorBoundary name="HourlyHeatmap">
183
- <HourlyHeatmap data={analytics.data.hourlyHeatmap} />
184
- </ChartErrorBoundary>
185
- </ChartCard>
186
- <ChartCard title="Session Timeline">
187
- <ChartErrorBoundary name="Timeline">
188
- <SessionTimeline data={analytics.data.sessionTimeline} />
189
- </ChartErrorBoundary>
190
- </ChartCard>
191
- </div>
192
-
193
- <ChartCard title="Daily Summary">
194
- <ChartErrorBoundary name="DailySummary">
195
- <DailySummaryCards data={analytics.data.dailySummaries} />
196
- </ChartErrorBoundary>
197
- </ChartCard>
198
-
199
- <SectionHeader label="Projects" />
200
-
201
- <ChartCard title="Project Comparison">
202
- <ChartErrorBoundary name="Radar">
203
- <ProjectRadar data={analytics.data.projectRadar} />
204
- </ChartErrorBoundary>
205
- </ChartCard>
206
-
207
- <ChartCard title="Session Complexity">
208
- <ChartErrorBoundary name="Complexity">
209
- <SessionComplexityList data={analytics.data.sessionComplexity} />
210
- </ChartErrorBoundary>
211
- </ChartCard>
194
+ </section>
195
+
196
+ <section className="flex flex-col gap-3 mt-10">
197
+ <SectionHeader label="Projects" />
198
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
199
+ <ChartCard title="Project Comparison">
200
+ <ChartErrorBoundary name="Radar">
201
+ <ProjectRadar data={analytics.data.projectRadar} />
202
+ </ChartErrorBoundary>
203
+ </ChartCard>
204
+ <ChartCard title="Session Complexity">
205
+ <ChartErrorBoundary name="Complexity">
206
+ <SessionComplexityList data={analytics.data.sessionComplexity} />
207
+ </ChartErrorBoundary>
208
+ </ChartCard>
209
+ </div>
210
+ </section>
212
211
 
213
212
  <SectionHeader label="Fleet" />
214
213
 
@@ -221,11 +220,29 @@ export function AnalyticsView() {
221
220
  )}
222
221
 
223
222
  {!analytics.loading && !analytics.error && !analytics.data && (
224
- <div className="flex flex-col items-center justify-center py-24 gap-4">
225
- <BarChart3 size={40} className="text-base-content/10" />
223
+ <div className="flex flex-col items-center justify-center py-20 gap-6 max-w-[400px] mx-auto">
224
+ <div className="w-16 h-16 rounded-2xl bg-primary/8 flex items-center justify-center">
225
+ <BarChart3 size={28} className="text-primary/40" />
226
+ </div>
226
227
  <div className="text-center">
227
- <p className="text-[14px] font-mono text-base-content/40">No analytics data yet</p>
228
- <p className="text-[12px] text-base-content/25 mt-1">Start a Claude session to begin tracking usage</p>
228
+ <p className="text-[15px] font-mono font-semibold text-base-content/60 mb-2">No analytics data yet</p>
229
+ <p className="text-[13px] text-base-content/30 leading-relaxed">
230
+ Analytics tracks your spending, token usage, cache efficiency, and session patterns across all projects. Start a Claude session to see your first data here.
231
+ </p>
232
+ </div>
233
+ <div className="flex flex-col gap-2 text-[11px] text-base-content/25 font-mono">
234
+ <div className="flex items-center gap-2">
235
+ <div className="w-1 h-1 rounded-full bg-primary/40" />
236
+ <span>Spending trends and cost breakdowns</span>
237
+ </div>
238
+ <div className="flex items-center gap-2">
239
+ <div className="w-1 h-1 rounded-full bg-primary/40" />
240
+ <span>Cache hit rates and response speed</span>
241
+ </div>
242
+ <div className="flex items-center gap-2">
243
+ <div className="w-1 h-1 rounded-full bg-primary/40" />
244
+ <span>Activity patterns and session complexity</span>
245
+ </div>
229
246
  </div>
230
247
  </div>
231
248
  )}
@@ -62,7 +62,7 @@ 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-bold uppercase tracking-wider text-base-content/40">Cost</span>
65
+ <span className="text-[10px] font-mono font-bold uppercase tracking-wider text-base-content/40">Total Spend</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} />
@@ -86,7 +86,7 @@ export function QuickStats() {
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-bold uppercase tracking-wider text-base-content/40">Tokens</span>
89
+ <span className="text-[10px] font-mono font-bold uppercase tracking-wider text-base-content/40">Total 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} />
@@ -98,7 +98,7 @@ export function QuickStats() {
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-bold uppercase tracking-wider text-base-content/40">Cache Hit</span>
101
+ <span className="text-[10px] font-mono font-bold uppercase tracking-wider text-base-content/40">Cache Rate</span>
102
102
  </div>
103
103
  <div className="text-[20px] font-mono text-base-content mb-2">{cacheHitPct}%</div>
104
104
  <div
@@ -95,12 +95,12 @@ export function getChartColors(): ChartColors {
95
95
  var contentRaw = css("--color-base-content");
96
96
 
97
97
  var tickFill = contentRaw
98
- ? "oklch(" + contentRaw + " / 0.3)"
99
- : "oklch(0.9 0.02 280 / 0.3)";
98
+ ? "oklch(" + contentRaw + " / 0.5)"
99
+ : "oklch(0.9 0.02 280 / 0.5)";
100
100
 
101
101
  var gridStroke = contentRaw
102
- ? "oklch(" + contentRaw + " / 0.06)"
103
- : "oklch(0.9 0.02 280 / 0.06)";
102
+ ? "oklch(" + contentRaw + " / 0.12)"
103
+ : "oklch(0.9 0.02 280 / 0.12)";
104
104
 
105
105
  _cache = {
106
106
  primary,
@@ -58,8 +58,9 @@ export function ActivityCalendar({ data }: ActivityCalendarProps) {
58
58
 
59
59
  if (!data || data.length === 0) {
60
60
  return (
61
- <div className="flex items-center justify-center h-[120px] text-base-content/25 font-mono text-[11px]">
62
- No data for this period
61
+ <div className="flex flex-col items-center justify-center h-[120px] gap-1">
62
+ <span className="text-base-content/25 font-mono text-[11px]">No activity recorded</span>
63
+ <span className="text-base-content/15 text-[10px]">Sessions will appear as colored cells by day</span>
63
64
  </div>
64
65
  );
65
66
  }
@@ -29,8 +29,9 @@ export function DailySummaryCards({ data }: DailySummaryCardsProps) {
29
29
  };
30
30
  if (!data || data.length === 0) {
31
31
  return (
32
- <div className="flex items-center justify-center h-[100px] text-base-content/25 font-mono text-[11px]">
33
- No data for this period
32
+ <div className="flex flex-col items-center justify-center h-[100px] gap-1">
33
+ <span className="text-base-content/25 font-mono text-[11px]">No daily data</span>
34
+ <span className="text-base-content/15 text-[10px]">Day-by-day cost, session, and model breakdown</span>
34
35
  </div>
35
36
  );
36
37
  }
@@ -28,8 +28,9 @@ export function HourlyHeatmap({ data }: HourlyHeatmapProps) {
28
28
 
29
29
  if (!data || data.length === 0) {
30
30
  return (
31
- <div className="flex items-center justify-center h-[200px] text-base-content/25 font-mono text-[11px]">
32
- No data for this period
31
+ <div className="flex flex-col items-center justify-center h-[200px] gap-1">
32
+ <span className="text-base-content/25 font-mono text-[11px]">No activity in this period</span>
33
+ <span className="text-base-content/15 text-[10px]">Shows your busiest hours and days of the week</span>
33
34
  </div>
34
35
  );
35
36
  }
@@ -10,8 +10,9 @@ export function NodeFleetOverview(props: NodeFleetOverviewProps) {
10
10
 
11
11
  if (nodes.length === 0) {
12
12
  return (
13
- <div className="flex items-center justify-center h-[200px] text-base-content/30 font-mono text-[12px]">
14
- No nodes connected
13
+ <div className="flex flex-col items-center justify-center h-[200px] gap-1">
14
+ <span className="text-base-content/30 font-mono text-[12px]">No nodes connected</span>
15
+ <span className="text-base-content/20 text-[11px]">Pair a node in Settings to see fleet data</span>
15
16
  </div>
16
17
  );
17
18
  }
@@ -7,8 +7,9 @@ interface SessionComplexityListProps {
7
7
  export function SessionComplexityList({ data }: SessionComplexityListProps) {
8
8
  if (!data || data.length === 0) {
9
9
  return (
10
- <div className="flex items-center justify-center h-[200px] text-base-content/25 font-mono text-[11px]">
11
- No session data
10
+ <div className="flex flex-col items-center justify-center h-[200px] gap-1">
11
+ <span className="text-base-content/25 font-mono text-[11px]">No sessions to rank</span>
12
+ <span className="text-base-content/15 text-[10px]">Sessions ranked by messages, tools, and context usage</span>
12
13
  </div>
13
14
  );
14
15
  }
@@ -32,8 +32,9 @@ export function SessionTimeline({ data }: SessionTimelineProps) {
32
32
 
33
33
  if (!data || data.length === 0) {
34
34
  return (
35
- <div className="flex items-center justify-center h-[200px] text-base-content/25 font-mono text-[11px]">
36
- No data for this period
35
+ <div className="flex flex-col items-center justify-center h-[200px] gap-1">
36
+ <span className="text-base-content/25 font-mono text-[11px]">No sessions in this period</span>
37
+ <span className="text-base-content/15 text-[10px]">Session durations will appear as bars on a timeline</span>
37
38
  </div>
38
39
  );
39
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cryptiklemur/lattice",
3
- "version": "1.23.3",
3
+ "version": "1.24.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>",