@cryptiklemur/lattice 1.13.0 → 1.14.0
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 +1 -0
- 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
|
@@ -8,13 +8,12 @@ import {
|
|
|
8
8
|
ResponsiveContainer,
|
|
9
9
|
Legend,
|
|
10
10
|
} from "recharts";
|
|
11
|
+
import { getChartColors } from "../chartTokens";
|
|
11
12
|
|
|
12
13
|
interface ProjectRadarProps {
|
|
13
14
|
data: Array<{ project: string; cost: number; sessions: number; avgDuration: number; toolDiversity: number; tokensPerSession: number }>;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
var PROJECT_COLORS = ["#a855f7", "#22c55e", "#f59e0b", "#ef4444", "oklch(55% 0.25 280)"];
|
|
17
|
-
|
|
18
17
|
var AXIS_KEYS = ["cost", "sessions", "avgDuration", "toolDiversity", "tokensPerSession"] as const;
|
|
19
18
|
var AXIS_LABELS: Record<string, string> = {
|
|
20
19
|
cost: "Cost",
|
|
@@ -50,6 +49,7 @@ function CustomTooltip({ active, payload, label }: { active?: boolean; payload?:
|
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
export function ProjectRadar({ data }: ProjectRadarProps) {
|
|
52
|
+
var colors = getChartColors();
|
|
53
53
|
if (!data || data.length === 0) {
|
|
54
54
|
return (
|
|
55
55
|
<div className="flex items-center justify-center h-[250px] text-base-content/25 font-mono text-[11px]">
|
|
@@ -87,10 +87,10 @@ export function ProjectRadar({ data }: ProjectRadarProps) {
|
|
|
87
87
|
return (
|
|
88
88
|
<ResponsiveContainer width="100%" height={280}>
|
|
89
89
|
<RadarChart data={radarData} margin={{ top: 10, right: 30, bottom: 10, left: 30 }}>
|
|
90
|
-
<PolarGrid stroke=
|
|
90
|
+
<PolarGrid stroke={colors.gridStroke} />
|
|
91
91
|
<PolarAngleAxis
|
|
92
92
|
dataKey="axis"
|
|
93
|
-
tick={{ fontSize: 9, fontFamily: "var(--font-mono)", fill:
|
|
93
|
+
tick={{ fontSize: 9, fontFamily: "var(--font-mono)", fill: colors.tickFill }}
|
|
94
94
|
/>
|
|
95
95
|
<PolarRadiusAxis
|
|
96
96
|
angle={90}
|
|
@@ -104,8 +104,8 @@ export function ProjectRadar({ data }: ProjectRadarProps) {
|
|
|
104
104
|
key={project.project}
|
|
105
105
|
name={project.project}
|
|
106
106
|
dataKey={project.project}
|
|
107
|
-
stroke={
|
|
108
|
-
fill={
|
|
107
|
+
stroke={colors.palette[index % colors.palette.length]}
|
|
108
|
+
fill={colors.palette[index % colors.palette.length]}
|
|
109
109
|
fillOpacity={0.1}
|
|
110
110
|
strokeWidth={1.5}
|
|
111
111
|
/>
|
|
@@ -8,21 +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 MODEL_COLORS: Record<string, string> = {
|
|
21
|
-
opus: "#a855f7",
|
|
22
|
-
sonnet: "oklch(55% 0.25 280)",
|
|
23
|
-
haiku: "#22c55e",
|
|
24
|
-
other: "#f59e0b",
|
|
25
|
-
};
|
|
11
|
+
import { getChartColors, getTickStyle, getModelColor } from "../chartTokens";
|
|
26
12
|
|
|
27
13
|
interface ResponseTimeDatum {
|
|
28
14
|
tokens: number;
|
|
@@ -51,12 +37,13 @@ function CustomTooltip({ active, payload }: { active?: boolean; payload?: Array<
|
|
|
51
37
|
|
|
52
38
|
export function ResponseTimeScatter({ data }: ResponseTimeScatterProps) {
|
|
53
39
|
var fullscreenHeight = useChartFullscreen();
|
|
40
|
+
var colors = getChartColors();
|
|
54
41
|
var models = Array.from(new Set(data.map(function (d) { return d.model; })));
|
|
55
42
|
|
|
56
43
|
var byModel = models.map(function (model) {
|
|
57
44
|
return {
|
|
58
45
|
model: model,
|
|
59
|
-
color:
|
|
46
|
+
color: getModelColor(model),
|
|
60
47
|
points: data
|
|
61
48
|
.filter(function (d) { return d.model === model; })
|
|
62
49
|
.map(function (d) { return { tokens: d.tokens, durationSec: d.duration / 1000, model: d.model }; }),
|
|
@@ -66,11 +53,11 @@ export function ResponseTimeScatter({ data }: ResponseTimeScatterProps) {
|
|
|
66
53
|
return (
|
|
67
54
|
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
68
55
|
<ScatterChart margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
69
|
-
<CartesianGrid strokeDasharray="3 3" stroke={
|
|
56
|
+
<CartesianGrid strokeDasharray="3 3" stroke={colors.gridStroke} />
|
|
70
57
|
<XAxis
|
|
71
58
|
dataKey="tokens"
|
|
72
59
|
type="number"
|
|
73
|
-
tick={
|
|
60
|
+
tick={getTickStyle()}
|
|
74
61
|
axisLine={false}
|
|
75
62
|
tickLine={false}
|
|
76
63
|
tickFormatter={function (v) { return v >= 1000 ? (v / 1000).toFixed(0) + "k" : String(v); }}
|
|
@@ -79,13 +66,13 @@ export function ResponseTimeScatter({ data }: ResponseTimeScatterProps) {
|
|
|
79
66
|
<YAxis
|
|
80
67
|
dataKey="durationSec"
|
|
81
68
|
type="number"
|
|
82
|
-
tick={
|
|
69
|
+
tick={getTickStyle()}
|
|
83
70
|
axisLine={false}
|
|
84
71
|
tickLine={false}
|
|
85
72
|
tickFormatter={function (v) { return v.toFixed(0) + "s"; }}
|
|
86
73
|
name="duration"
|
|
87
74
|
/>
|
|
88
|
-
<Tooltip content={<CustomTooltip />} cursor={{ strokeDasharray: "3 3", stroke:
|
|
75
|
+
<Tooltip content={<CustomTooltip />} cursor={{ strokeDasharray: "3 3", stroke: colors.gridStroke }} />
|
|
89
76
|
{byModel.map(function (group) {
|
|
90
77
|
return (
|
|
91
78
|
<Scatter
|
|
@@ -9,25 +9,7 @@ import {
|
|
|
9
9
|
ZAxis,
|
|
10
10
|
} from "recharts";
|
|
11
11
|
import { useChartFullscreen } from "../ChartCard";
|
|
12
|
-
|
|
13
|
-
var TICK_STYLE = {
|
|
14
|
-
fontSize: 10,
|
|
15
|
-
fontFamily: "var(--font-mono)",
|
|
16
|
-
fill: "oklch(0.9 0.02 280 / 0.3)",
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
var GRID_COLOR = "oklch(0.9 0.02 280 / 0.06)";
|
|
20
|
-
|
|
21
|
-
var PROJECT_PALETTE = [
|
|
22
|
-
"oklch(55% 0.25 280)",
|
|
23
|
-
"#a855f7",
|
|
24
|
-
"#22c55e",
|
|
25
|
-
"#f59e0b",
|
|
26
|
-
"oklch(65% 0.2 240)",
|
|
27
|
-
"oklch(65% 0.25 25)",
|
|
28
|
-
"oklch(65% 0.25 150)",
|
|
29
|
-
"oklch(70% 0.2 60)",
|
|
30
|
-
];
|
|
12
|
+
import { getChartColors, getTickStyle } from "../chartTokens";
|
|
31
13
|
|
|
32
14
|
interface SessionBubbleDatum {
|
|
33
15
|
id: string;
|
|
@@ -64,11 +46,12 @@ function CustomTooltip({ active, payload }: { active?: boolean; payload?: Array<
|
|
|
64
46
|
|
|
65
47
|
export function SessionBubbleChart({ data }: SessionBubbleChartProps) {
|
|
66
48
|
var fullscreenHeight = useChartFullscreen();
|
|
49
|
+
var colors = getChartColors();
|
|
67
50
|
var projects = Array.from(new Set(data.map(function (d) { return d.project; })));
|
|
68
51
|
|
|
69
52
|
function getColor(project: string): string {
|
|
70
53
|
var idx = projects.indexOf(project);
|
|
71
|
-
return
|
|
54
|
+
return colors.palette[idx % colors.palette.length];
|
|
72
55
|
}
|
|
73
56
|
|
|
74
57
|
var byProject = projects.map(function (project) {
|
|
@@ -87,12 +70,12 @@ export function SessionBubbleChart({ data }: SessionBubbleChartProps) {
|
|
|
87
70
|
return (
|
|
88
71
|
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
89
72
|
<ScatterChart margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
90
|
-
<CartesianGrid strokeDasharray="3 3" stroke={
|
|
73
|
+
<CartesianGrid strokeDasharray="3 3" stroke={colors.gridStroke} />
|
|
91
74
|
<XAxis
|
|
92
75
|
dataKey="x"
|
|
93
76
|
type="number"
|
|
94
77
|
domain={[minTs, maxTs]}
|
|
95
|
-
tick={
|
|
78
|
+
tick={getTickStyle()}
|
|
96
79
|
axisLine={false}
|
|
97
80
|
tickLine={false}
|
|
98
81
|
tickFormatter={function (v) { return formatDate(v); }}
|
|
@@ -100,13 +83,13 @@ export function SessionBubbleChart({ data }: SessionBubbleChartProps) {
|
|
|
100
83
|
<YAxis
|
|
101
84
|
dataKey="y"
|
|
102
85
|
type="number"
|
|
103
|
-
tick={
|
|
86
|
+
tick={getTickStyle()}
|
|
104
87
|
axisLine={false}
|
|
105
88
|
tickLine={false}
|
|
106
89
|
tickFormatter={function (v) { return v >= 1000 ? (v / 1000).toFixed(0) + "k" : String(v); }}
|
|
107
90
|
/>
|
|
108
91
|
<ZAxis dataKey="z" range={[20, 300]} />
|
|
109
|
-
<Tooltip content={<CustomTooltip />} cursor={{ strokeDasharray: "3 3", stroke:
|
|
92
|
+
<Tooltip content={<CustomTooltip />} cursor={{ strokeDasharray: "3 3", stroke: colors.gridStroke }} />
|
|
110
93
|
{byProject.map(function (group) {
|
|
111
94
|
return (
|
|
112
95
|
<Scatter
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
+
import { getScoreColor } from "../chartTokens";
|
|
2
|
+
|
|
1
3
|
interface SessionComplexityListProps {
|
|
2
4
|
data: Array<{ id: string; title: string; score: number; messages: number; tools: number; contextPercent: number }>;
|
|
3
5
|
}
|
|
4
6
|
|
|
5
|
-
function getScoreColor(score: number, maxScore: number): string {
|
|
6
|
-
var intensity = maxScore > 0 ? Math.min(score / maxScore, 1) : 0;
|
|
7
|
-
var lightness = 0.45 - intensity * 0.1;
|
|
8
|
-
var chroma = 0.1 + intensity * 0.18;
|
|
9
|
-
return "oklch(" + lightness + " " + chroma + " 280)";
|
|
10
|
-
}
|
|
11
|
-
|
|
12
7
|
export function SessionComplexityList({ data }: SessionComplexityListProps) {
|
|
13
8
|
if (!data || data.length === 0) {
|
|
14
9
|
return (
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
|
+
import { getChartColors } from "../chartTokens";
|
|
2
3
|
|
|
3
4
|
interface TimelineDatum {
|
|
4
5
|
id: string;
|
|
@@ -19,16 +20,6 @@ var ROW_STEP = ROW_HEIGHT + ROW_GAP;
|
|
|
19
20
|
var LEFT_MARGIN = 8;
|
|
20
21
|
var RIGHT_MARGIN = 8;
|
|
21
22
|
|
|
22
|
-
var PROJECT_PALETTE = [
|
|
23
|
-
"oklch(55% 0.25 280)",
|
|
24
|
-
"#a855f7",
|
|
25
|
-
"#22c55e",
|
|
26
|
-
"#f59e0b",
|
|
27
|
-
"oklch(65% 0.2 240)",
|
|
28
|
-
"oklch(65% 0.25 25)",
|
|
29
|
-
"oklch(65% 0.25 150)",
|
|
30
|
-
"oklch(70% 0.2 60)",
|
|
31
|
-
];
|
|
32
23
|
|
|
33
24
|
function formatTime(ts: number): string {
|
|
34
25
|
var d = new Date(ts);
|
|
@@ -37,6 +28,7 @@ function formatTime(ts: number): string {
|
|
|
37
28
|
|
|
38
29
|
export function SessionTimeline({ data }: SessionTimelineProps) {
|
|
39
30
|
var [hover, setHover] = useState<{ x: number; y: number; datum: TimelineDatum } | null>(null);
|
|
31
|
+
var colors = getChartColors();
|
|
40
32
|
|
|
41
33
|
if (!data || data.length === 0) {
|
|
42
34
|
return (
|
|
@@ -49,7 +41,7 @@ export function SessionTimeline({ data }: SessionTimelineProps) {
|
|
|
49
41
|
var projects = Array.from(new Set(data.map(function (d) { return d.project; })));
|
|
50
42
|
function getColor(project: string): string {
|
|
51
43
|
var idx = projects.indexOf(project);
|
|
52
|
-
return
|
|
44
|
+
return colors.palette[idx % colors.palette.length];
|
|
53
45
|
}
|
|
54
46
|
|
|
55
47
|
var minTime = Infinity;
|
|
@@ -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 TokenFlowDatum {
|
|
21
14
|
date: string;
|
|
@@ -54,30 +47,31 @@ function formatTokens(v: number): string {
|
|
|
54
47
|
|
|
55
48
|
export function TokenFlowChart({ data }: TokenFlowChartProps) {
|
|
56
49
|
var fullscreenHeight = useChartFullscreen();
|
|
50
|
+
var colors = getChartColors();
|
|
57
51
|
return (
|
|
58
52
|
<ResponsiveContainer width="100%" height={fullscreenHeight || 200}>
|
|
59
53
|
<AreaChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
|
|
60
54
|
<defs>
|
|
61
55
|
<linearGradient id="inputGrad" 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="outputGrad" 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="cacheReadGrad" 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={formatTokens} />
|
|
77
71
|
<Tooltip content={<CustomTooltip />} />
|
|
78
|
-
<Area type="monotone" dataKey="input" stackId="1" stroke=
|
|
79
|
-
<Area type="monotone" dataKey="output" stackId="1" stroke=
|
|
80
|
-
<Area type="monotone" dataKey="cacheRead" stackId="1" stroke=
|
|
72
|
+
<Area type="monotone" dataKey="input" stackId="1" stroke={colors.primary} fill="url(#inputGrad)" strokeWidth={1.5} />
|
|
73
|
+
<Area type="monotone" dataKey="output" stackId="1" stroke={colors.success} fill="url(#outputGrad)" strokeWidth={1.5} />
|
|
74
|
+
<Area type="monotone" dataKey="cacheRead" stackId="1" stroke={colors.warning} fill="url(#cacheReadGrad)" strokeWidth={1.5} />
|
|
81
75
|
</AreaChart>
|
|
82
76
|
</ResponsiveContainer>
|
|
83
77
|
);
|
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
import { Sankey, Tooltip, ResponsiveContainer } from "recharts";
|
|
2
2
|
import { useChartFullscreen } from "../ChartCard";
|
|
3
|
-
|
|
4
|
-
var NODE_COLORS: Record<string, string> = {
|
|
5
|
-
"Input Tokens": "oklch(55% 0.25 280)",
|
|
6
|
-
"Cache Read": "#f59e0b",
|
|
7
|
-
"Cache Creation": "oklch(65% 0.2 240)",
|
|
8
|
-
"Opus": "#a855f7",
|
|
9
|
-
"Sonnet": "oklch(55% 0.25 280)",
|
|
10
|
-
"Haiku": "#22c55e",
|
|
11
|
-
"Other": "#f59e0b",
|
|
12
|
-
"Output Tokens": "#22c55e",
|
|
13
|
-
};
|
|
3
|
+
import { getChartColors } from "../chartTokens";
|
|
14
4
|
|
|
15
5
|
interface SankeyData {
|
|
16
6
|
nodes: Array<{ name: string }>;
|
|
@@ -44,7 +34,18 @@ function CustomTooltip({ active, payload }: { active?: boolean; payload?: Array<
|
|
|
44
34
|
}
|
|
45
35
|
|
|
46
36
|
function SankeyNode({ x, y, width, height, index, payload }: { x: number; y: number; width: number; height: number; index: number; payload: { name: string } }) {
|
|
47
|
-
var
|
|
37
|
+
var colors = getChartColors();
|
|
38
|
+
var NODE_COLORS: Record<string, string> = {
|
|
39
|
+
"Input Tokens": colors.primary,
|
|
40
|
+
"Cache Read": colors.warning,
|
|
41
|
+
"Cache Creation": colors.accent,
|
|
42
|
+
"Opus": colors.secondary,
|
|
43
|
+
"Sonnet": colors.primary,
|
|
44
|
+
"Haiku": colors.success,
|
|
45
|
+
"Other": colors.warning,
|
|
46
|
+
"Output Tokens": colors.success,
|
|
47
|
+
};
|
|
48
|
+
var color = NODE_COLORS[payload.name] || colors.primary;
|
|
48
49
|
return (
|
|
49
50
|
<g>
|
|
50
51
|
<rect x={x} y={y} width={width} height={height} fill={color} fillOpacity={0.85} rx={2} />
|
|
@@ -53,7 +54,7 @@ function SankeyNode({ x, y, width, height, index, payload }: { x: number; y: num
|
|
|
53
54
|
x={x + width + 6}
|
|
54
55
|
y={y + height / 2}
|
|
55
56
|
dy={4}
|
|
56
|
-
fill=
|
|
57
|
+
fill={colors.tickFill}
|
|
57
58
|
fontSize={9}
|
|
58
59
|
fontFamily="var(--font-mono)"
|
|
59
60
|
>
|
|
@@ -66,6 +67,7 @@ function SankeyNode({ x, y, width, height, index, payload }: { x: number; y: num
|
|
|
66
67
|
|
|
67
68
|
export function TokenSankeyChart({ data }: TokenSankeyChartProps) {
|
|
68
69
|
var fullscreenHeight = useChartFullscreen();
|
|
70
|
+
var colors = getChartColors();
|
|
69
71
|
if (!data.links || data.links.length === 0) {
|
|
70
72
|
return (
|
|
71
73
|
<div className="flex items-center justify-center h-[250px] text-base-content/30 font-mono text-[12px]">
|
|
@@ -79,7 +81,7 @@ export function TokenSankeyChart({ data }: TokenSankeyChartProps) {
|
|
|
79
81
|
<Sankey
|
|
80
82
|
data={data}
|
|
81
83
|
node={<SankeyNode x={0} y={0} width={0} height={0} index={0} payload={{ name: "" }} />}
|
|
82
|
-
link={{ stroke:
|
|
84
|
+
link={{ stroke: colors.gridStroke }}
|
|
83
85
|
margin={{ top: 10, right: 100, left: 10, bottom: 10 }}
|
|
84
86
|
nodeWidth={12}
|
|
85
87
|
nodePadding={14}
|
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer } from "recharts";
|
|
2
|
+
import { getChartColors } from "../chartTokens";
|
|
2
3
|
|
|
3
4
|
interface ToolSunburstProps {
|
|
4
5
|
data: Array<{ name: string; category: string; count: number }>;
|
|
5
6
|
}
|
|
6
7
|
|
|
7
|
-
var CATEGORY_COLORS: Record<string, string> = {
|
|
8
|
-
Read: "#22c55e",
|
|
9
|
-
Write: "#f59e0b",
|
|
10
|
-
Execute: "#ef4444",
|
|
11
|
-
AI: "#a855f7",
|
|
12
|
-
Other: "oklch(55% 0.15 280)",
|
|
13
|
-
};
|
|
14
|
-
|
|
15
8
|
function getCategoryColor(category: string): string {
|
|
16
|
-
|
|
9
|
+
var colors = getChartColors();
|
|
10
|
+
return colors.category[category] || colors.category.Other;
|
|
17
11
|
}
|
|
18
12
|
|
|
19
13
|
function getToolColor(category: string, index: number): string {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Treemap, ResponsiveContainer, Tooltip } from "recharts";
|
|
2
2
|
import { useChartFullscreen } from "../ChartCard";
|
|
3
|
+
import { getChartColors, getIntensityColor } from "../chartTokens";
|
|
3
4
|
|
|
4
5
|
interface ToolTreemapProps {
|
|
5
6
|
data: Array<{ name: string; count: number; avgCost: number }>;
|
|
@@ -9,9 +10,7 @@ var MAX_COST_INTENSITY = 0.5;
|
|
|
9
10
|
|
|
10
11
|
function getColor(avgCost: number, maxCost: number): string {
|
|
11
12
|
var intensity = maxCost > 0 ? Math.min(avgCost / maxCost, 1) : 0.3;
|
|
12
|
-
|
|
13
|
-
var chroma = 0.15 + intensity * 0.12;
|
|
14
|
-
return "oklch(" + lightness + " " + chroma + " 280)";
|
|
13
|
+
return getIntensityColor(intensity);
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
function CustomContent(props: {
|
|
@@ -21,6 +20,7 @@ function CustomContent(props: {
|
|
|
21
20
|
}) {
|
|
22
21
|
var { x = 0, y = 0, width = 0, height = 0, name = "", count = 0, avgCost = 0, maxCost = MAX_COST_INTENSITY } = props;
|
|
23
22
|
if (width < 4 || height < 4) return null;
|
|
23
|
+
var colors = getChartColors();
|
|
24
24
|
var showLabel = width > 40 && height > 20;
|
|
25
25
|
var showCount = width > 50 && height > 34;
|
|
26
26
|
return (
|
|
@@ -33,7 +33,7 @@ function CustomContent(props: {
|
|
|
33
33
|
rx={3}
|
|
34
34
|
fill={getColor(avgCost, maxCost)}
|
|
35
35
|
fillOpacity={0.85}
|
|
36
|
-
stroke=
|
|
36
|
+
stroke={colors.gridStroke}
|
|
37
37
|
strokeWidth={1}
|
|
38
38
|
/>
|
|
39
39
|
{showLabel && (
|
|
@@ -42,7 +42,7 @@ function CustomContent(props: {
|
|
|
42
42
|
y={y + height / 2 + (showCount ? -5 : 0)}
|
|
43
43
|
textAnchor="middle"
|
|
44
44
|
dominantBaseline="middle"
|
|
45
|
-
style={{ fontSize: 10, fontFamily: "var(--font-mono)", fill:
|
|
45
|
+
style={{ fontSize: 10, fontFamily: "var(--font-mono)", fill: colors.tickFill }}
|
|
46
46
|
>
|
|
47
47
|
{name}
|
|
48
48
|
</text>
|
|
@@ -53,7 +53,7 @@ function CustomContent(props: {
|
|
|
53
53
|
y={y + height / 2 + 9}
|
|
54
54
|
textAnchor="middle"
|
|
55
55
|
dominantBaseline="middle"
|
|
56
|
-
style={{ fontSize: 9, fontFamily: "var(--font-mono)", fill:
|
|
56
|
+
style={{ fontSize: 9, fontFamily: "var(--font-mono)", fill: colors.tickFill }}
|
|
57
57
|
>
|
|
58
58
|
{count}
|
|
59
59
|
</text>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useRef, useState, useEffect, useMemo } from "react";
|
|
2
|
+
import { useStore } from "@tanstack/react-store";
|
|
2
3
|
import { SendHorizontal, Settings, Paperclip } from "lucide-react";
|
|
3
4
|
import { useSkills } from "../../hooks/useSkills";
|
|
4
5
|
import { CommandPalette, getFilteredItems } from "./CommandPalette";
|
|
@@ -17,6 +18,7 @@ interface ChatInputProps {
|
|
|
17
18
|
onFailedInputConsumed?: () => void;
|
|
18
19
|
prefillText?: string | null;
|
|
19
20
|
onPrefillConsumed?: () => void;
|
|
21
|
+
sessionId?: string | null;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
function getModKey(): string {
|
|
@@ -26,9 +28,37 @@ function getModKey(): string {
|
|
|
26
28
|
return "Ctrl";
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
var
|
|
31
|
+
var historyBySession = new Map<string, string[]>();
|
|
30
32
|
var MAX_HISTORY = 100;
|
|
31
33
|
|
|
34
|
+
function extractTypedInput(text: string): string | null {
|
|
35
|
+
var firstNewline = text.search(/\r?\n/);
|
|
36
|
+
var firstLine = firstNewline !== -1 ? text.slice(0, firstNewline).trim() : text;
|
|
37
|
+
if (firstLine.indexOf(":") !== -1 && /\n---[\r\n]/.test(text)) {
|
|
38
|
+
return "/" + firstLine;
|
|
39
|
+
}
|
|
40
|
+
if (text.startsWith("<skill-name>")) {
|
|
41
|
+
var endTag = text.indexOf("</skill-name>");
|
|
42
|
+
if (endTag !== -1) {
|
|
43
|
+
return "/" + text.slice(12, endTag);
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
if (text.startsWith("<skill-content>")) return null;
|
|
48
|
+
if (text.length > 500) return null;
|
|
49
|
+
return text;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getHistory(sessionId: string | null | undefined): string[] {
|
|
53
|
+
if (!sessionId) return [];
|
|
54
|
+
var hist = historyBySession.get(sessionId);
|
|
55
|
+
if (!hist) {
|
|
56
|
+
hist = [];
|
|
57
|
+
historyBySession.set(sessionId, hist);
|
|
58
|
+
}
|
|
59
|
+
return hist;
|
|
60
|
+
}
|
|
61
|
+
|
|
32
62
|
export function ChatInput(props: ChatInputProps) {
|
|
33
63
|
var textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
34
64
|
var popupRef = useRef<HTMLDivElement>(null);
|
|
@@ -41,26 +71,29 @@ export function ChatInput(props: ChatInputProps) {
|
|
|
41
71
|
var modKey = useMemo(getModKey, []);
|
|
42
72
|
var [historyIndex, setHistoryIndex] = useState(-1);
|
|
43
73
|
var savedCurrentRef = useRef("");
|
|
74
|
+
var inputHistory = getHistory(props.sessionId);
|
|
75
|
+
var historyLoading = useStore(getSessionStore(), function (s) { return s.historyLoading; });
|
|
76
|
+
var messages = useStore(getSessionStore(), function (s) { return s.messages; });
|
|
77
|
+
|
|
78
|
+
useEffect(function () {
|
|
79
|
+
setHistoryIndex(-1);
|
|
80
|
+
savedCurrentRef.current = "";
|
|
81
|
+
}, [props.sessionId]);
|
|
44
82
|
|
|
45
83
|
useEffect(function () {
|
|
46
|
-
|
|
47
|
-
var messages = store.state.messages;
|
|
48
|
-
var seen = new Set<string>();
|
|
84
|
+
if (!props.sessionId || historyLoading) return;
|
|
49
85
|
for (var i = 0; i < messages.length; i++) {
|
|
50
86
|
if (messages[i].type === "user" && messages[i].text) {
|
|
51
|
-
var
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
if (inputHistory.indexOf(text) === -1) {
|
|
55
|
-
inputHistory.push(text);
|
|
56
|
-
}
|
|
87
|
+
var typed = extractTypedInput(messages[i].text!.trim());
|
|
88
|
+
if (typed && inputHistory.indexOf(typed) === -1) {
|
|
89
|
+
inputHistory.push(typed);
|
|
57
90
|
}
|
|
58
91
|
}
|
|
59
92
|
}
|
|
60
93
|
if (inputHistory.length > MAX_HISTORY) {
|
|
61
94
|
inputHistory.splice(0, inputHistory.length - MAX_HISTORY);
|
|
62
95
|
}
|
|
63
|
-
}, []);
|
|
96
|
+
}, [props.sessionId, historyLoading]);
|
|
64
97
|
|
|
65
98
|
var attachmentsHook = useAttachments();
|
|
66
99
|
var voice = useVoiceRecorder();
|