@cryptiklemur/lattice 1.13.0 → 1.14.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/ChartCard.tsx +3 -0
- package/client/src/components/analytics/QuickStats.tsx +5 -3
- package/client/src/components/analytics/chartTokens.ts +182 -0
- package/client/src/components/analytics/charts/ActivityCalendar.tsx +3 -1
- package/client/src/components/analytics/charts/CacheEfficiencyChart.tsx +8 -14
- package/client/src/components/analytics/charts/ContextUtilizationChart.tsx +6 -20
- package/client/src/components/analytics/charts/CostAreaChart.tsx +17 -23
- package/client/src/components/analytics/charts/CostDistributionChart.tsx +8 -14
- package/client/src/components/analytics/charts/CostDonutChart.tsx +5 -17
- package/client/src/components/analytics/charts/CumulativeCostChart.tsx +8 -14
- package/client/src/components/analytics/charts/DailySummaryCards.tsx +9 -7
- package/client/src/components/analytics/charts/HourlyHeatmap.tsx +3 -2
- package/client/src/components/analytics/charts/PermissionBreakdown.tsx +5 -8
- package/client/src/components/analytics/charts/ProjectRadar.tsx +6 -6
- package/client/src/components/analytics/charts/ResponseTimeScatter.tsx +7 -20
- package/client/src/components/analytics/charts/SessionBubbleChart.tsx +7 -24
- package/client/src/components/analytics/charts/SessionComplexityList.tsx +2 -7
- package/client/src/components/analytics/charts/SessionTimeline.tsx +3 -11
- package/client/src/components/analytics/charts/TokenFlowChart.tsx +14 -20
- package/client/src/components/analytics/charts/TokenSankeyChart.tsx +16 -14
- package/client/src/components/analytics/charts/ToolSunburst.tsx +3 -9
- package/client/src/components/analytics/charts/ToolTreemap.tsx +6 -6
- package/client/src/components/chat/ChatInput.tsx +44 -11
- package/client/src/components/chat/ChatView.tsx +58 -37
- package/client/src/components/chat/Message.tsx +170 -17
- package/client/src/components/chat/ToolGroup.tsx +1 -1
- package/client/src/components/dashboard/DashboardView.tsx +30 -6
- package/client/src/components/project-settings/ProjectMemory.tsx +18 -2
- package/client/src/components/project-settings/ProjectSettingsView.tsx +2 -2
- package/client/src/components/settings/SettingsView.tsx +2 -2
- package/client/src/components/settings/skill-shared.tsx +10 -2
- package/client/src/components/sidebar/AddProjectModal.tsx +10 -1
- package/client/src/components/sidebar/NodeSettingsModal.tsx +10 -1
- package/client/src/components/sidebar/ProjectRail.tsx +9 -1
- package/client/src/components/sidebar/SessionList.tsx +205 -20
- package/client/src/components/sidebar/Sidebar.tsx +2 -2
- package/client/src/components/sidebar/UserMenu.tsx +5 -1
- package/client/src/components/ui/IconPicker.tsx +2 -2
- package/client/src/components/ui/PopupMenu.tsx +25 -5
- package/client/src/components/ui/Toast.tsx +1 -1
- package/client/src/components/workspace/TaskEditModal.tsx +16 -6
- package/client/src/hooks/useSession.ts +11 -2
- package/client/src/hooks/useSwipeDrawer.ts +28 -4
- package/client/src/hooks/useWebSocket.ts +3 -0
- package/client/src/stores/session.ts +10 -0
- package/client/src/styles/global.css +62 -2
- package/client/src/utils/formatSessionTitle.ts +17 -0
- package/package.json +1 -1
- package/server/src/handlers/session.ts +19 -1
- package/server/src/project/session.ts +83 -1
- package/shared/src/messages.ts +21 -2
- package/shared/src/models.ts +9 -0
|
@@ -136,6 +136,9 @@ export function ChartCard(props: ChartCardProps) {
|
|
|
136
136
|
<div
|
|
137
137
|
className="fixed inset-0 z-[9999] flex items-center justify-center p-8"
|
|
138
138
|
style={{ pointerEvents: "none" }}
|
|
139
|
+
role="dialog"
|
|
140
|
+
aria-modal="true"
|
|
141
|
+
aria-label={props.title + " (fullscreen)"}
|
|
139
142
|
>
|
|
140
143
|
<div
|
|
141
144
|
className="w-full max-w-[1100px] rounded-2xl border border-base-content/10 bg-base-200 shadow-2xl overflow-hidden flex flex-col"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { LineChart, Line, ResponsiveContainer } from "recharts";
|
|
2
2
|
import { useAnalytics } from "../../hooks/useAnalytics";
|
|
3
|
+
import { getChartColors } from "./chartTokens";
|
|
3
4
|
|
|
4
5
|
function formatTokens(n: number): string {
|
|
5
6
|
if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + "M";
|
|
@@ -48,6 +49,7 @@ export function QuickStats() {
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
var d = analytics.data;
|
|
52
|
+
var colors = getChartColors();
|
|
51
53
|
|
|
52
54
|
var costSparkData = d.costOverTime.slice(-7).map(function (e) { return { v: e.total }; });
|
|
53
55
|
var sessionsSparkData = d.sessionsOverTime.slice(-7).map(function (e) { return { v: e.count }; });
|
|
@@ -61,7 +63,7 @@ export function QuickStats() {
|
|
|
61
63
|
<div className="bg-base-content/[0.03] border border-base-content/8 rounded-xl p-3.5">
|
|
62
64
|
<div className="flex items-center justify-between mb-1">
|
|
63
65
|
<span className="text-[10px] font-mono font-semibold uppercase tracking-wider text-base-content/35">Cost</span>
|
|
64
|
-
{costSparkData.length > 1 && <Sparkline data={costSparkData} stroke=
|
|
66
|
+
{costSparkData.length > 1 && <Sparkline data={costSparkData} stroke={colors.primary} />}
|
|
65
67
|
</div>
|
|
66
68
|
<div className="text-[22px] font-mono text-base-content/85">${d.totalCost.toFixed(2)}</div>
|
|
67
69
|
</div>
|
|
@@ -69,7 +71,7 @@ export function QuickStats() {
|
|
|
69
71
|
<div className="bg-base-content/[0.03] border border-base-content/8 rounded-xl p-3.5">
|
|
70
72
|
<div className="flex items-center justify-between mb-1">
|
|
71
73
|
<span className="text-[10px] font-mono font-semibold uppercase tracking-wider text-base-content/35">Sessions</span>
|
|
72
|
-
{sessionsSparkData.length > 1 && <Sparkline data={sessionsSparkData} stroke=
|
|
74
|
+
{sessionsSparkData.length > 1 && <Sparkline data={sessionsSparkData} stroke={colors.success} />}
|
|
73
75
|
</div>
|
|
74
76
|
<div className="text-[22px] font-mono text-base-content/85">{d.totalSessions}</div>
|
|
75
77
|
</div>
|
|
@@ -77,7 +79,7 @@ export function QuickStats() {
|
|
|
77
79
|
<div className="bg-base-content/[0.03] border border-base-content/8 rounded-xl p-3.5">
|
|
78
80
|
<div className="flex items-center justify-between mb-1">
|
|
79
81
|
<span className="text-[10px] font-mono font-semibold uppercase tracking-wider text-base-content/35">Tokens</span>
|
|
80
|
-
{tokensSparkData.length > 1 && <Sparkline data={tokensSparkData} stroke=
|
|
82
|
+
{tokensSparkData.length > 1 && <Sparkline data={tokensSparkData} stroke={colors.warning} />}
|
|
81
83
|
</div>
|
|
82
84
|
<div className="text-[22px] font-mono text-base-content/85">{formatTokens(totalTokens)}</div>
|
|
83
85
|
</div>
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme-aware chart color tokens.
|
|
3
|
+
*
|
|
4
|
+
* Reads CSS custom properties set by useTheme so every chart
|
|
5
|
+
* automatically adapts when the user switches Base16 themes.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { getChartColors } from "../chartTokens";
|
|
9
|
+
* // inside component render:
|
|
10
|
+
* var colors = getChartColors();
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
function css(prop: string): string {
|
|
14
|
+
return getComputedStyle(document.documentElement).getPropertyValue(prop).trim();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function oklch(raw: string): string {
|
|
18
|
+
return raw ? "oklch(" + raw + ")" : "";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ChartColors {
|
|
22
|
+
/** Primary brand color (base0D — blue) */
|
|
23
|
+
primary: string;
|
|
24
|
+
/** Secondary color (base0E — purple) */
|
|
25
|
+
secondary: string;
|
|
26
|
+
/** Accent / info color (base0C — cyan) */
|
|
27
|
+
accent: string;
|
|
28
|
+
/** Success / green (base0B) */
|
|
29
|
+
success: string;
|
|
30
|
+
/** Warning / yellow (base0A) */
|
|
31
|
+
warning: string;
|
|
32
|
+
/** Error / red (base08) */
|
|
33
|
+
error: string;
|
|
34
|
+
/** Orange (base09) */
|
|
35
|
+
orange: string;
|
|
36
|
+
/** Magenta (base0F) */
|
|
37
|
+
magenta: string;
|
|
38
|
+
|
|
39
|
+
/** Tick label fill — content color at 30% opacity */
|
|
40
|
+
tickFill: string;
|
|
41
|
+
/** Grid stroke — content color at 6% opacity */
|
|
42
|
+
gridStroke: string;
|
|
43
|
+
|
|
44
|
+
/** Model palette keyed by model family */
|
|
45
|
+
model: {
|
|
46
|
+
opus: string;
|
|
47
|
+
sonnet: string;
|
|
48
|
+
haiku: string;
|
|
49
|
+
other: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** Ordered palette for multi-series charts (8 colors) */
|
|
53
|
+
palette: string[];
|
|
54
|
+
|
|
55
|
+
/** Category colors for tool classification */
|
|
56
|
+
category: Record<string, string>;
|
|
57
|
+
|
|
58
|
+
/** Permission colors */
|
|
59
|
+
permission: {
|
|
60
|
+
allowed: string;
|
|
61
|
+
denied: string;
|
|
62
|
+
alwaysAllowed: string;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
var _cache: ChartColors | null = null;
|
|
67
|
+
var _cacheKey: string = "";
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Returns chart colors derived from the current CSS theme.
|
|
71
|
+
* Results are cached per theme so repeated calls within
|
|
72
|
+
* the same render cycle are cheap.
|
|
73
|
+
*/
|
|
74
|
+
export function getChartColors(): ChartColors {
|
|
75
|
+
var key = css("--color-primary") + css("--color-base-content");
|
|
76
|
+
if (_cache && _cacheKey === key) return _cache;
|
|
77
|
+
|
|
78
|
+
var primary = oklch(css("--color-primary"));
|
|
79
|
+
var secondary = oklch(css("--color-secondary"));
|
|
80
|
+
var accent = oklch(css("--color-accent"));
|
|
81
|
+
var success = oklch(css("--color-success"));
|
|
82
|
+
var warning = oklch(css("--color-warning"));
|
|
83
|
+
var error = oklch(css("--color-error"));
|
|
84
|
+
var orange = css("--base09") ? "#" + css("--base09") : warning;
|
|
85
|
+
var magenta = css("--base0F") ? "#" + css("--base0F") : secondary;
|
|
86
|
+
var contentRaw = css("--color-base-content");
|
|
87
|
+
|
|
88
|
+
var tickFill = contentRaw
|
|
89
|
+
? "oklch(" + contentRaw + " / 0.3)"
|
|
90
|
+
: "oklch(0.9 0.02 280 / 0.3)";
|
|
91
|
+
|
|
92
|
+
var gridStroke = contentRaw
|
|
93
|
+
? "oklch(" + contentRaw + " / 0.06)"
|
|
94
|
+
: "oklch(0.9 0.02 280 / 0.06)";
|
|
95
|
+
|
|
96
|
+
_cache = {
|
|
97
|
+
primary,
|
|
98
|
+
secondary,
|
|
99
|
+
accent,
|
|
100
|
+
success,
|
|
101
|
+
warning,
|
|
102
|
+
error,
|
|
103
|
+
orange,
|
|
104
|
+
magenta,
|
|
105
|
+
|
|
106
|
+
tickFill,
|
|
107
|
+
gridStroke,
|
|
108
|
+
|
|
109
|
+
model: {
|
|
110
|
+
opus: secondary,
|
|
111
|
+
sonnet: primary,
|
|
112
|
+
haiku: success,
|
|
113
|
+
other: warning,
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
palette: [primary, secondary, success, warning, accent, orange, error, magenta],
|
|
117
|
+
|
|
118
|
+
category: {
|
|
119
|
+
Read: success,
|
|
120
|
+
Write: warning,
|
|
121
|
+
Execute: error,
|
|
122
|
+
AI: secondary,
|
|
123
|
+
Other: primary,
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
permission: {
|
|
127
|
+
allowed: success,
|
|
128
|
+
denied: error,
|
|
129
|
+
alwaysAllowed: primary,
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
_cacheKey = key;
|
|
133
|
+
return _cache;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Tick style object ready to spread onto Recharts axis components. */
|
|
137
|
+
export function getTickStyle() {
|
|
138
|
+
var c = getChartColors();
|
|
139
|
+
return {
|
|
140
|
+
fontSize: 10,
|
|
141
|
+
fontFamily: "var(--font-mono)",
|
|
142
|
+
fill: c.tickFill,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Returns a color for a model name by matching against known families.
|
|
148
|
+
*/
|
|
149
|
+
export function getModelColor(model: string): string {
|
|
150
|
+
var c = getChartColors();
|
|
151
|
+
var key = model.toLowerCase();
|
|
152
|
+
if (key.includes("opus")) return c.model.opus;
|
|
153
|
+
if (key.includes("sonnet")) return c.model.sonnet;
|
|
154
|
+
if (key.includes("haiku")) return c.model.haiku;
|
|
155
|
+
return c.model.other;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Generate a dynamic OKLCH color based on the theme's primary hue.
|
|
160
|
+
* Useful for heatmaps and treemaps that derive intensity from data.
|
|
161
|
+
*/
|
|
162
|
+
export function getIntensityColor(intensity: number, hueOverride?: number): string {
|
|
163
|
+
var raw = getComputedStyle(document.documentElement).getPropertyValue("--color-primary").trim();
|
|
164
|
+
var hue = hueOverride ?? 280;
|
|
165
|
+
if (raw) {
|
|
166
|
+
var parts = raw.split(/\s+/);
|
|
167
|
+
if (parts.length >= 3) {
|
|
168
|
+
hue = parseFloat(parts[2]) || hue;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
var lightness = 0.45 - intensity * 0.15;
|
|
172
|
+
var chroma = 0.15 + intensity * 0.12;
|
|
173
|
+
return "oklch(" + lightness + " " + chroma + " " + hue + ")";
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Generate a score-based color using theme hue.
|
|
178
|
+
*/
|
|
179
|
+
export function getScoreColor(score: number, maxScore: number): string {
|
|
180
|
+
var intensity = maxScore > 0 ? Math.min(score / maxScore, 1) : 0;
|
|
181
|
+
return getIntensityColor(intensity);
|
|
182
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
|
+
import { getChartColors } from "../chartTokens";
|
|
2
3
|
|
|
3
4
|
interface CalendarDatum {
|
|
4
5
|
date: string;
|
|
@@ -18,7 +19,6 @@ var DAY_LABEL_WIDTH = 28;
|
|
|
18
19
|
var MONTH_LABEL_HEIGHT = 14;
|
|
19
20
|
|
|
20
21
|
var INTENSITY_OPACITIES = [0.05, 0.15, 0.3, 0.5, 0.8];
|
|
21
|
-
var PRIMARY_COLOR = "oklch(55% 0.25 280)";
|
|
22
22
|
|
|
23
23
|
var MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
24
24
|
var DAY_LABELS = [
|
|
@@ -43,6 +43,8 @@ function parseDateParts(dateStr: string): { year: number; month: number; day: nu
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export function ActivityCalendar({ data }: ActivityCalendarProps) {
|
|
46
|
+
var colors = getChartColors();
|
|
47
|
+
var PRIMARY_COLOR = colors.primary;
|
|
46
48
|
var [hover, setHover] = useState<{ x: number; y: number; datum: CalendarDatum } | null>(null);
|
|
47
49
|
|
|
48
50
|
if (!data || data.length === 0) {
|
|
@@ -8,14 +8,7 @@ import {
|
|
|
8
8
|
ResponsiveContainer,
|
|
9
9
|
} from "recharts";
|
|
10
10
|
import { useChartFullscreen } from "../ChartCard";
|
|
11
|
-
|
|
12
|
-
var TICK_STYLE = {
|
|
13
|
-
fontSize: 10,
|
|
14
|
-
fontFamily: "var(--font-mono)",
|
|
15
|
-
fill: "oklch(0.9 0.02 280 / 0.3)",
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
var GRID_COLOR = "oklch(0.9 0.02 280 / 0.06)";
|
|
11
|
+
import { getChartColors, getTickStyle } from "../chartTokens";
|
|
19
12
|
|
|
20
13
|
interface CacheEfficiencyDatum {
|
|
21
14
|
date: string;
|
|
@@ -38,6 +31,7 @@ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?:
|
|
|
38
31
|
|
|
39
32
|
export function CacheEfficiencyChart({ data }: CacheEfficiencyChartProps) {
|
|
40
33
|
var fullscreenHeight = useChartFullscreen();
|
|
34
|
+
var colors = getChartColors();
|
|
41
35
|
var displayData = data.map(function (d) {
|
|
42
36
|
return { date: d.date, rate: d.rate * 100 };
|
|
43
37
|
});
|
|
@@ -47,15 +41,15 @@ export function CacheEfficiencyChart({ data }: CacheEfficiencyChartProps) {
|
|
|
47
41
|
<AreaChart data={displayData} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
48
42
|
<defs>
|
|
49
43
|
<linearGradient id="cacheEffGrad" x1="0" y1="0" x2="0" y2="1">
|
|
50
|
-
<stop offset="5%" stopColor=
|
|
51
|
-
<stop offset="95%" stopColor=
|
|
44
|
+
<stop offset="5%" stopColor={colors.success} stopOpacity={0.4} />
|
|
45
|
+
<stop offset="95%" stopColor={colors.success} stopOpacity={0.02} />
|
|
52
46
|
</linearGradient>
|
|
53
47
|
</defs>
|
|
54
|
-
<CartesianGrid strokeDasharray="3 3" stroke={
|
|
55
|
-
<XAxis dataKey="date" tick={
|
|
56
|
-
<YAxis domain={[0, 100]} tick={
|
|
48
|
+
<CartesianGrid strokeDasharray="3 3" stroke={colors.gridStroke} vertical={false} />
|
|
49
|
+
<XAxis dataKey="date" tick={getTickStyle()} axisLine={false} tickLine={false} />
|
|
50
|
+
<YAxis domain={[0, 100]} tick={getTickStyle()} axisLine={false} tickLine={false} tickFormatter={function (v) { return v + "%"; }} />
|
|
57
51
|
<Tooltip content={<CustomTooltip />} />
|
|
58
|
-
<Area type="monotone" dataKey="rate" stroke=
|
|
52
|
+
<Area type="monotone" dataKey="rate" stroke={colors.success} fill="url(#cacheEffGrad)" strokeWidth={1.5} />
|
|
59
53
|
</AreaChart>
|
|
60
54
|
</ResponsiveContainer>
|
|
61
55
|
);
|
|
@@ -8,22 +8,7 @@ import {
|
|
|
8
8
|
ResponsiveContainer,
|
|
9
9
|
} from "recharts";
|
|
10
10
|
import { useChartFullscreen } from "../ChartCard";
|
|
11
|
-
|
|
12
|
-
var TICK_STYLE = {
|
|
13
|
-
fontSize: 10,
|
|
14
|
-
fontFamily: "var(--font-mono)",
|
|
15
|
-
fill: "oklch(0.9 0.02 280 / 0.3)",
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
var GRID_COLOR = "oklch(0.9 0.02 280 / 0.06)";
|
|
19
|
-
|
|
20
|
-
var LINE_COLORS = [
|
|
21
|
-
"oklch(55% 0.25 280)",
|
|
22
|
-
"#a855f7",
|
|
23
|
-
"#22c55e",
|
|
24
|
-
"#f59e0b",
|
|
25
|
-
"oklch(65% 0.2 240)",
|
|
26
|
-
];
|
|
11
|
+
import { getChartColors, getTickStyle } from "../chartTokens";
|
|
27
12
|
|
|
28
13
|
interface ContextUtilDatum {
|
|
29
14
|
messageIndex: number;
|
|
@@ -55,6 +40,7 @@ function CustomTooltip({ active, payload }: { active?: boolean; payload?: Array<
|
|
|
55
40
|
|
|
56
41
|
export function ContextUtilizationChart({ data }: ContextUtilizationChartProps) {
|
|
57
42
|
var fullscreenHeight = useChartFullscreen();
|
|
43
|
+
var colors = getChartColors();
|
|
58
44
|
var sessionMap = new Map<string, { title: string; points: Array<{ messageIndex: number; contextPercent: number }> }>();
|
|
59
45
|
for (var i = 0; i < data.length; i++) {
|
|
60
46
|
var d = data[i];
|
|
@@ -88,9 +74,9 @@ export function ContextUtilizationChart({ data }: ContextUtilizationChartProps)
|
|
|
88
74
|
return (
|
|
89
75
|
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
90
76
|
<LineChart data={merged} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
91
|
-
<CartesianGrid strokeDasharray="3 3" stroke={
|
|
92
|
-
<XAxis dataKey="messageIndex" tick={
|
|
93
|
-
<YAxis domain={[0, 100]} tick={
|
|
77
|
+
<CartesianGrid strokeDasharray="3 3" stroke={colors.gridStroke} vertical={false} />
|
|
78
|
+
<XAxis dataKey="messageIndex" tick={getTickStyle()} axisLine={false} tickLine={false} />
|
|
79
|
+
<YAxis domain={[0, 100]} tick={getTickStyle()} axisLine={false} tickLine={false} tickFormatter={function (v) { return v + "%"; }} />
|
|
94
80
|
<Tooltip content={<CustomTooltip />} />
|
|
95
81
|
{sessions.map(function (s, idx) {
|
|
96
82
|
return (
|
|
@@ -99,7 +85,7 @@ export function ContextUtilizationChart({ data }: ContextUtilizationChartProps)
|
|
|
99
85
|
type="monotone"
|
|
100
86
|
dataKey={s[0]}
|
|
101
87
|
name={s[1].title.slice(0, 30)}
|
|
102
|
-
stroke={
|
|
88
|
+
stroke={colors.palette[idx % colors.palette.length]}
|
|
103
89
|
strokeWidth={1.5}
|
|
104
90
|
dot={false}
|
|
105
91
|
connectNulls
|
|
@@ -8,14 +8,7 @@ import {
|
|
|
8
8
|
ResponsiveContainer,
|
|
9
9
|
} from "recharts";
|
|
10
10
|
import { useChartFullscreen } from "../ChartCard";
|
|
11
|
-
|
|
12
|
-
var TICK_STYLE = {
|
|
13
|
-
fontSize: 10,
|
|
14
|
-
fontFamily: "var(--font-mono)",
|
|
15
|
-
fill: "oklch(0.9 0.02 280 / 0.3)",
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
var GRID_COLOR = "oklch(0.9 0.02 280 / 0.06)";
|
|
11
|
+
import { getChartColors, getTickStyle } from "../chartTokens";
|
|
19
12
|
|
|
20
13
|
interface CostAreaDatum {
|
|
21
14
|
date: string;
|
|
@@ -50,35 +43,36 @@ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?:
|
|
|
50
43
|
|
|
51
44
|
export function CostAreaChart({ data }: CostAreaChartProps) {
|
|
52
45
|
var fullscreenHeight = useChartFullscreen();
|
|
46
|
+
var colors = getChartColors();
|
|
53
47
|
return (
|
|
54
48
|
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
55
49
|
<AreaChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
56
50
|
<defs>
|
|
57
51
|
<linearGradient id="opusGrad" x1="0" y1="0" x2="0" y2="1">
|
|
58
|
-
<stop offset="5%" stopColor=
|
|
59
|
-
<stop offset="95%" stopColor=
|
|
52
|
+
<stop offset="5%" stopColor={colors.secondary} stopOpacity={0.4} />
|
|
53
|
+
<stop offset="95%" stopColor={colors.secondary} stopOpacity={0.05} />
|
|
60
54
|
</linearGradient>
|
|
61
55
|
<linearGradient id="sonnetGrad" x1="0" y1="0" x2="0" y2="1">
|
|
62
|
-
<stop offset="5%" stopColor=
|
|
63
|
-
<stop offset="95%" stopColor=
|
|
56
|
+
<stop offset="5%" stopColor={colors.primary} stopOpacity={0.4} />
|
|
57
|
+
<stop offset="95%" stopColor={colors.primary} stopOpacity={0.05} />
|
|
64
58
|
</linearGradient>
|
|
65
59
|
<linearGradient id="haikuGrad" x1="0" y1="0" x2="0" y2="1">
|
|
66
|
-
<stop offset="5%" stopColor=
|
|
67
|
-
<stop offset="95%" stopColor=
|
|
60
|
+
<stop offset="5%" stopColor={colors.success} stopOpacity={0.4} />
|
|
61
|
+
<stop offset="95%" stopColor={colors.success} stopOpacity={0.05} />
|
|
68
62
|
</linearGradient>
|
|
69
63
|
<linearGradient id="otherGrad" x1="0" y1="0" x2="0" y2="1">
|
|
70
|
-
<stop offset="5%" stopColor=
|
|
71
|
-
<stop offset="95%" stopColor=
|
|
64
|
+
<stop offset="5%" stopColor={colors.warning} stopOpacity={0.4} />
|
|
65
|
+
<stop offset="95%" stopColor={colors.warning} stopOpacity={0.05} />
|
|
72
66
|
</linearGradient>
|
|
73
67
|
</defs>
|
|
74
|
-
<CartesianGrid strokeDasharray="3 3" stroke={
|
|
75
|
-
<XAxis dataKey="date" tick={
|
|
76
|
-
<YAxis tick={
|
|
68
|
+
<CartesianGrid strokeDasharray="3 3" stroke={colors.gridStroke} vertical={false} />
|
|
69
|
+
<XAxis dataKey="date" tick={getTickStyle()} axisLine={false} tickLine={false} />
|
|
70
|
+
<YAxis tick={getTickStyle()} axisLine={false} tickLine={false} tickFormatter={function (v) { return "$" + v.toFixed(2); }} />
|
|
77
71
|
<Tooltip content={<CustomTooltip />} />
|
|
78
|
-
<Area type="monotone" dataKey="opus" stackId="1" stroke=
|
|
79
|
-
<Area type="monotone" dataKey="sonnet" stackId="1" stroke=
|
|
80
|
-
<Area type="monotone" dataKey="haiku" stackId="1" stroke=
|
|
81
|
-
<Area type="monotone" dataKey="other" stackId="1" stroke=
|
|
72
|
+
<Area type="monotone" dataKey="opus" stackId="1" stroke={colors.secondary} fill="url(#opusGrad)" strokeWidth={1.5} />
|
|
73
|
+
<Area type="monotone" dataKey="sonnet" stackId="1" stroke={colors.primary} fill="url(#sonnetGrad)" strokeWidth={1.5} />
|
|
74
|
+
<Area type="monotone" dataKey="haiku" stackId="1" stroke={colors.success} fill="url(#haikuGrad)" strokeWidth={1.5} />
|
|
75
|
+
<Area type="monotone" dataKey="other" stackId="1" stroke={colors.warning} fill="url(#otherGrad)" strokeWidth={1.5} />
|
|
82
76
|
</AreaChart>
|
|
83
77
|
</ResponsiveContainer>
|
|
84
78
|
);
|
|
@@ -8,14 +8,7 @@ import {
|
|
|
8
8
|
ResponsiveContainer,
|
|
9
9
|
} from "recharts";
|
|
10
10
|
import { useChartFullscreen } from "../ChartCard";
|
|
11
|
-
|
|
12
|
-
var TICK_STYLE = {
|
|
13
|
-
fontSize: 10,
|
|
14
|
-
fontFamily: "var(--font-mono)",
|
|
15
|
-
fill: "oklch(0.9 0.02 280 / 0.3)",
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
var GRID_COLOR = "oklch(0.9 0.02 280 / 0.06)";
|
|
11
|
+
import { getChartColors, getTickStyle } from "../chartTokens";
|
|
19
12
|
|
|
20
13
|
interface DistributionDatum {
|
|
21
14
|
bucket: string;
|
|
@@ -38,23 +31,24 @@ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?:
|
|
|
38
31
|
|
|
39
32
|
export function CostDistributionChart({ data }: CostDistributionChartProps) {
|
|
40
33
|
var fullscreenHeight = useChartFullscreen();
|
|
34
|
+
var colors = getChartColors();
|
|
41
35
|
return (
|
|
42
36
|
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
43
37
|
<AreaChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
44
38
|
<defs>
|
|
45
39
|
<linearGradient id="distGrad" x1="0" y1="0" x2="0" y2="1">
|
|
46
|
-
<stop offset="5%" stopColor=
|
|
47
|
-
<stop offset="95%" stopColor=
|
|
40
|
+
<stop offset="5%" stopColor={colors.primary} stopOpacity={0.35} />
|
|
41
|
+
<stop offset="95%" stopColor={colors.primary} stopOpacity={0.02} />
|
|
48
42
|
</linearGradient>
|
|
49
43
|
</defs>
|
|
50
|
-
<CartesianGrid strokeDasharray="3 3" stroke={
|
|
51
|
-
<XAxis dataKey="bucket" tick={
|
|
52
|
-
<YAxis tick={
|
|
44
|
+
<CartesianGrid strokeDasharray="3 3" stroke={colors.gridStroke} vertical={false} />
|
|
45
|
+
<XAxis dataKey="bucket" tick={getTickStyle()} axisLine={false} tickLine={false} />
|
|
46
|
+
<YAxis tick={getTickStyle()} axisLine={false} tickLine={false} allowDecimals={false} />
|
|
53
47
|
<Tooltip content={<CustomTooltip />} />
|
|
54
48
|
<Area
|
|
55
49
|
type="monotone"
|
|
56
50
|
dataKey="count"
|
|
57
|
-
stroke=
|
|
51
|
+
stroke={colors.primary}
|
|
58
52
|
fill="url(#distGrad)"
|
|
59
53
|
strokeWidth={2}
|
|
60
54
|
/>
|
|
@@ -1,20 +1,6 @@
|
|
|
1
1
|
import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer } from "recharts";
|
|
2
2
|
import { useChartFullscreen } from "../ChartCard";
|
|
3
|
-
|
|
4
|
-
var MODEL_COLORS: Record<string, string> = {
|
|
5
|
-
opus: "#a855f7",
|
|
6
|
-
sonnet: "oklch(55% 0.25 280)",
|
|
7
|
-
haiku: "#22c55e",
|
|
8
|
-
other: "#f59e0b",
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
function getModelColor(model: string): string {
|
|
12
|
-
var key = model.toLowerCase();
|
|
13
|
-
if (key.includes("opus")) return MODEL_COLORS.opus;
|
|
14
|
-
if (key.includes("sonnet")) return MODEL_COLORS.sonnet;
|
|
15
|
-
if (key.includes("haiku")) return MODEL_COLORS.haiku;
|
|
16
|
-
return MODEL_COLORS.other;
|
|
17
|
-
}
|
|
3
|
+
import { getChartColors, getModelColor } from "../chartTokens";
|
|
18
4
|
|
|
19
5
|
interface ModelUsage {
|
|
20
6
|
model: string;
|
|
@@ -40,12 +26,13 @@ function CustomTooltip({ active, payload }: { active?: boolean; payload?: Array<
|
|
|
40
26
|
}
|
|
41
27
|
|
|
42
28
|
function CenterLabel({ totalCost }: { totalCost: number }) {
|
|
29
|
+
var colors = getChartColors();
|
|
43
30
|
return (
|
|
44
31
|
<text x="50%" y="50%" textAnchor="middle" dominantBaseline="middle">
|
|
45
|
-
<tspan x="50%" dy="-0.4em" style={{ fontSize: 11, fontFamily: "var(--font-mono)", fill:
|
|
32
|
+
<tspan x="50%" dy="-0.4em" style={{ fontSize: 11, fontFamily: "var(--font-mono)", fill: colors.tickFill }}>
|
|
46
33
|
TOTAL
|
|
47
34
|
</tspan>
|
|
48
|
-
<tspan x="50%" dy="1.4em" style={{ fontSize: 14, fontFamily: "var(--font-mono)", fill:
|
|
35
|
+
<tspan x="50%" dy="1.4em" style={{ fontSize: 14, fontFamily: "var(--font-mono)", fill: colors.tickFill, fontWeight: 700 }}>
|
|
49
36
|
${totalCost.toFixed(2)}
|
|
50
37
|
</tspan>
|
|
51
38
|
</text>
|
|
@@ -54,6 +41,7 @@ function CenterLabel({ totalCost }: { totalCost: number }) {
|
|
|
54
41
|
|
|
55
42
|
export function CostDonutChart({ modelUsage, totalCost }: CostDonutChartProps) {
|
|
56
43
|
var fullscreenHeight = useChartFullscreen();
|
|
44
|
+
var colors = getChartColors();
|
|
57
45
|
return (
|
|
58
46
|
<div>
|
|
59
47
|
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
@@ -8,14 +8,7 @@ import {
|
|
|
8
8
|
ResponsiveContainer,
|
|
9
9
|
} from "recharts";
|
|
10
10
|
import { useChartFullscreen } from "../ChartCard";
|
|
11
|
-
|
|
12
|
-
var TICK_STYLE = {
|
|
13
|
-
fontSize: 10,
|
|
14
|
-
fontFamily: "var(--font-mono)",
|
|
15
|
-
fill: "oklch(0.9 0.02 280 / 0.3)",
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
var GRID_COLOR = "oklch(0.9 0.02 280 / 0.06)";
|
|
11
|
+
import { getChartColors, getTickStyle } from "../chartTokens";
|
|
19
12
|
|
|
20
13
|
interface CumulativeDatum {
|
|
21
14
|
date: string;
|
|
@@ -38,23 +31,24 @@ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?:
|
|
|
38
31
|
|
|
39
32
|
export function CumulativeCostChart({ data }: CumulativeCostChartProps) {
|
|
40
33
|
var fullscreenHeight = useChartFullscreen();
|
|
34
|
+
var colors = getChartColors();
|
|
41
35
|
return (
|
|
42
36
|
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
43
37
|
<AreaChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
44
38
|
<defs>
|
|
45
39
|
<linearGradient id="cumulativeGrad" x1="0" y1="0" x2="0" y2="1">
|
|
46
|
-
<stop offset="5%" stopColor=
|
|
47
|
-
<stop offset="95%" stopColor=
|
|
40
|
+
<stop offset="5%" stopColor={colors.primary} stopOpacity={0.3} />
|
|
41
|
+
<stop offset="95%" stopColor={colors.primary} stopOpacity={0} />
|
|
48
42
|
</linearGradient>
|
|
49
43
|
</defs>
|
|
50
|
-
<CartesianGrid strokeDasharray="3 3" stroke={
|
|
51
|
-
<XAxis dataKey="date" tick={
|
|
52
|
-
<YAxis tick={
|
|
44
|
+
<CartesianGrid strokeDasharray="3 3" stroke={colors.gridStroke} vertical={false} />
|
|
45
|
+
<XAxis dataKey="date" tick={getTickStyle()} axisLine={false} tickLine={false} />
|
|
46
|
+
<YAxis tick={getTickStyle()} axisLine={false} tickLine={false} tickFormatter={function (v) { return "$" + v.toFixed(2); }} />
|
|
53
47
|
<Tooltip content={<CustomTooltip />} />
|
|
54
48
|
<Area
|
|
55
49
|
type="monotone"
|
|
56
50
|
dataKey="total"
|
|
57
|
-
stroke=
|
|
51
|
+
stroke={colors.primary}
|
|
58
52
|
fill="url(#cumulativeGrad)"
|
|
59
53
|
strokeWidth={2}
|
|
60
54
|
/>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getChartColors } from "../chartTokens";
|
|
2
|
+
|
|
1
3
|
interface DailySummaryDatum {
|
|
2
4
|
date: string;
|
|
3
5
|
sessions: number;
|
|
@@ -11,13 +13,6 @@ interface DailySummaryCardsProps {
|
|
|
11
13
|
data: DailySummaryDatum[];
|
|
12
14
|
}
|
|
13
15
|
|
|
14
|
-
var MODEL_COLORS: Record<string, string> = {
|
|
15
|
-
opus: "oklch(55% 0.25 280)",
|
|
16
|
-
sonnet: "#a855f7",
|
|
17
|
-
haiku: "#22c55e",
|
|
18
|
-
other: "#f59e0b",
|
|
19
|
-
};
|
|
20
|
-
|
|
21
16
|
function formatCardDate(dateStr: string): string {
|
|
22
17
|
var d = new Date(dateStr + "T00:00:00");
|
|
23
18
|
var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
@@ -25,6 +20,13 @@ function formatCardDate(dateStr: string): string {
|
|
|
25
20
|
}
|
|
26
21
|
|
|
27
22
|
export function DailySummaryCards({ data }: DailySummaryCardsProps) {
|
|
23
|
+
var colors = getChartColors();
|
|
24
|
+
var MODEL_COLORS: Record<string, string> = {
|
|
25
|
+
opus: colors.model.opus,
|
|
26
|
+
sonnet: colors.model.sonnet,
|
|
27
|
+
haiku: colors.model.haiku,
|
|
28
|
+
other: colors.model.other,
|
|
29
|
+
};
|
|
28
30
|
if (!data || data.length === 0) {
|
|
29
31
|
return (
|
|
30
32
|
<div className="flex items-center justify-center h-[100px] text-base-content/25 font-mono text-[11px]">
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
|
+
import { getChartColors } from "../chartTokens";
|
|
2
3
|
|
|
3
4
|
interface HeatmapDatum {
|
|
4
5
|
day: number;
|
|
@@ -20,9 +21,9 @@ var DAY_ORDER = [1, 2, 3, 4, 5, 6, 0];
|
|
|
20
21
|
var DAY_NAMES = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
21
22
|
var HOUR_LABELS = [0, 3, 6, 9, 12, 15, 18, 21];
|
|
22
23
|
|
|
23
|
-
var PRIMARY_COLOR = "oklch(55% 0.25 280)";
|
|
24
|
-
|
|
25
24
|
export function HourlyHeatmap({ data }: HourlyHeatmapProps) {
|
|
25
|
+
var colors = getChartColors();
|
|
26
|
+
var PRIMARY_COLOR = colors.primary;
|
|
26
27
|
var [hover, setHover] = useState<{ x: number; y: number; day: string; hour: number; count: number } | null>(null);
|
|
27
28
|
|
|
28
29
|
if (!data || data.length === 0) {
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer } from "recharts";
|
|
2
|
+
import { getChartColors } from "../chartTokens";
|
|
2
3
|
|
|
3
4
|
interface PermissionBreakdownProps {
|
|
4
5
|
data: { allowed: number; denied: number; alwaysAllowed: number };
|
|
5
6
|
}
|
|
6
7
|
|
|
7
|
-
var COLORS = {
|
|
8
|
-
allowed: "#22c55e",
|
|
9
|
-
denied: "#ef4444",
|
|
10
|
-
alwaysAllowed: "oklch(55% 0.25 280)",
|
|
11
|
-
};
|
|
12
|
-
|
|
13
8
|
function CustomTooltip({ active, payload }: { active?: boolean; payload?: Array<{ name: string; value: number }> }) {
|
|
14
9
|
if (!active || !payload || payload.length === 0) return null;
|
|
15
10
|
var entry = payload[0];
|
|
@@ -22,6 +17,8 @@ function CustomTooltip({ active, payload }: { active?: boolean; payload?: Array<
|
|
|
22
17
|
}
|
|
23
18
|
|
|
24
19
|
export function PermissionBreakdown({ data }: PermissionBreakdownProps) {
|
|
20
|
+
var chartColors = getChartColors();
|
|
21
|
+
var COLORS = chartColors.permission;
|
|
25
22
|
var total = data.allowed + data.denied + data.alwaysAllowed;
|
|
26
23
|
|
|
27
24
|
if (total === 0) {
|
|
@@ -73,10 +70,10 @@ export function PermissionBreakdown({ data }: PermissionBreakdownProps) {
|
|
|
73
70
|
</Pie>
|
|
74
71
|
<Tooltip content={<CustomTooltip />} />
|
|
75
72
|
<text x="50%" y="50%" textAnchor="middle" dominantBaseline="middle">
|
|
76
|
-
<tspan x="50%" dy="-0.3em" style={{ fontSize: 14, fontFamily: "var(--font-mono)", fill:
|
|
73
|
+
<tspan x="50%" dy="-0.3em" style={{ fontSize: 14, fontFamily: "var(--font-mono)", fill: chartColors.tickFill, fontWeight: 700 }}>
|
|
77
74
|
{total.toLocaleString()}
|
|
78
75
|
</tspan>
|
|
79
|
-
<tspan x="50%" dy="1.3em" style={{ fontSize: 9, fontFamily: "var(--font-mono)", fill:
|
|
76
|
+
<tspan x="50%" dy="1.3em" style={{ fontSize: 9, fontFamily: "var(--font-mono)", fill: chartColors.tickFill }}>
|
|
80
77
|
total
|
|
81
78
|
</tspan>
|
|
82
79
|
</text>
|