groove-dev 0.24.2 → 0.24.4
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/node_modules/@groove-dev/cli/bin/groove.js +4 -1
- package/node_modules/@groove-dev/gui/dist/assets/{index-z5gtKpHQ.js → index-D4juRNU_.js} +39 -39
- package/node_modules/@groove-dev/gui/dist/index.html +1 -1
- package/node_modules/@groove-dev/gui/src/components/dashboard/token-chart.jsx +108 -91
- package/package.json +1 -1
- package/packages/cli/bin/groove.js +4 -1
- package/packages/gui/dist/assets/{index-z5gtKpHQ.js → index-D4juRNU_.js} +39 -39
- package/packages/gui/dist/index.html +1 -1
- package/packages/gui/src/components/dashboard/token-chart.jsx +108 -91
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
7
7
|
<title>Groove GUI</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-D4juRNU_.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/vendor-C0HXlhrU.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/reactflow-BQPfi37R.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/codemirror-BBL3i_JW.js">
|
|
@@ -4,8 +4,8 @@ import { HEX, hexAlpha } from '../../lib/theme-hex';
|
|
|
4
4
|
import { fmtNum, fmtDollar, fmtPct } from '../../lib/format';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Modern token flow chart — edge-to-edge gradient fill, floating labels,
|
|
8
|
+
* hover tooltip for detail. Self-sizing via internal ResizeObserver.
|
|
9
9
|
*/
|
|
10
10
|
const TokenChart = memo(function TokenChart({ data }) {
|
|
11
11
|
const containerRef = useRef(null);
|
|
@@ -14,11 +14,12 @@ const TokenChart = memo(function TokenChart({ data }) {
|
|
|
14
14
|
const [hover, setHover] = useState(null);
|
|
15
15
|
|
|
16
16
|
const { width, height } = size;
|
|
17
|
-
|
|
17
|
+
// Minimal padding — just enough for floating labels to breathe
|
|
18
|
+
const pad = { top: 28, right: 12, bottom: 8, left: 12 };
|
|
18
19
|
const w = Math.max(width - pad.left - pad.right, 0);
|
|
19
20
|
const h = Math.max(height - pad.top - pad.bottom, 0);
|
|
20
21
|
|
|
21
|
-
// Self-size
|
|
22
|
+
// Self-size
|
|
22
23
|
useEffect(() => {
|
|
23
24
|
const el = containerRef.current;
|
|
24
25
|
if (!el) return;
|
|
@@ -45,7 +46,7 @@ const TokenChart = memo(function TokenChart({ data }) {
|
|
|
45
46
|
// Draw
|
|
46
47
|
useEffect(() => {
|
|
47
48
|
const canvas = canvasRef.current;
|
|
48
|
-
if (!canvas || !data?.length || width <= 0 || height <= 0) return;
|
|
49
|
+
if (!canvas || !data?.length || width <= 0 || height <= 0 || w <= 0 || h <= 0) return;
|
|
49
50
|
const ctx = canvas.getContext('2d');
|
|
50
51
|
const dpr = window.devicePixelRatio || 1;
|
|
51
52
|
|
|
@@ -54,8 +55,6 @@ const TokenChart = memo(function TokenChart({ data }) {
|
|
|
54
55
|
ctx.scale(dpr, dpr);
|
|
55
56
|
ctx.clearRect(0, 0, width, height);
|
|
56
57
|
|
|
57
|
-
if (w <= 0 || h <= 0) return;
|
|
58
|
-
|
|
59
58
|
const tokens = data.map((d) => d.tokens || 0);
|
|
60
59
|
const costs = data.map((d) => d.costUsd || 0);
|
|
61
60
|
const caches = data.map((d) => d.cacheHitRate ?? null);
|
|
@@ -63,23 +62,34 @@ const TokenChart = memo(function TokenChart({ data }) {
|
|
|
63
62
|
const maxC = Math.max(...costs, 0.01);
|
|
64
63
|
const hasCacheData = caches.some((c) => c !== null && c > 0);
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
const xAt = (i) => pad.left + (i / Math.max(data.length - 1, 1)) * w;
|
|
66
|
+
const yToken = (v) => pad.top + h - (v / maxT) * h;
|
|
67
|
+
const yCost = (v) => pad.top + h - (v / maxC) * h;
|
|
68
|
+
const yCache = (v) => pad.top + h - (v * h);
|
|
69
|
+
|
|
70
|
+
// ── Subtle horizontal guidelines (3 lines, dotted) ──────
|
|
71
|
+
ctx.setLineDash([2, 4]);
|
|
72
|
+
ctx.strokeStyle = hexAlpha(HEX.text4, 0.25);
|
|
73
|
+
ctx.lineWidth = 1;
|
|
74
|
+
for (let i = 1; i <= 3; i++) {
|
|
70
75
|
const y = pad.top + (h / 4) * i;
|
|
71
76
|
ctx.beginPath();
|
|
72
77
|
ctx.moveTo(pad.left, y);
|
|
73
78
|
ctx.lineTo(pad.left + w, y);
|
|
74
79
|
ctx.stroke();
|
|
75
80
|
}
|
|
81
|
+
ctx.setLineDash([]);
|
|
76
82
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
// ── Floating Y-axis labels (inside chart, top-left) ─────
|
|
84
|
+
ctx.font = "9px 'JetBrains Mono Variable', monospace";
|
|
85
|
+
ctx.textAlign = 'left';
|
|
86
|
+
ctx.fillStyle = hexAlpha(HEX.text3, 0.6);
|
|
87
|
+
// Top label (max)
|
|
88
|
+
ctx.fillText(fmtNum(maxT), pad.left + 4, pad.top + 10);
|
|
89
|
+
// Mid label
|
|
90
|
+
ctx.fillText(fmtNum(maxT / 2), pad.left + 4, pad.top + h / 2 + 4);
|
|
81
91
|
|
|
82
|
-
// Token area fill
|
|
92
|
+
// ── Token area fill (edge-to-edge gradient) ─────────────
|
|
83
93
|
ctx.beginPath();
|
|
84
94
|
ctx.moveTo(pad.left, pad.top + h);
|
|
85
95
|
for (let i = 0; i < data.length; i++) {
|
|
@@ -88,15 +98,17 @@ const TokenChart = memo(function TokenChart({ data }) {
|
|
|
88
98
|
ctx.lineTo(xAt(data.length - 1), pad.top + h);
|
|
89
99
|
ctx.closePath();
|
|
90
100
|
const grad = ctx.createLinearGradient(0, pad.top, 0, pad.top + h);
|
|
91
|
-
grad.addColorStop(0, hexAlpha(HEX.accent, 0.
|
|
92
|
-
grad.addColorStop(
|
|
101
|
+
grad.addColorStop(0, hexAlpha(HEX.accent, 0.18));
|
|
102
|
+
grad.addColorStop(0.7, hexAlpha(HEX.accent, 0.04));
|
|
103
|
+
grad.addColorStop(1, hexAlpha(HEX.accent, 0));
|
|
93
104
|
ctx.fillStyle = grad;
|
|
94
105
|
ctx.fill();
|
|
95
106
|
|
|
96
|
-
// Token line
|
|
107
|
+
// ── Token line ──────────────────────────────────────────
|
|
97
108
|
ctx.beginPath();
|
|
98
109
|
ctx.strokeStyle = HEX.accent;
|
|
99
110
|
ctx.lineWidth = 1.5;
|
|
111
|
+
ctx.lineJoin = 'round';
|
|
100
112
|
for (let i = 0; i < data.length; i++) {
|
|
101
113
|
const x = xAt(i);
|
|
102
114
|
const y = yToken(tokens[i]);
|
|
@@ -104,11 +116,12 @@ const TokenChart = memo(function TokenChart({ data }) {
|
|
|
104
116
|
}
|
|
105
117
|
ctx.stroke();
|
|
106
118
|
|
|
107
|
-
// Cost line (dashed)
|
|
119
|
+
// ── Cost line (dashed, subtle) ──────────────────────────
|
|
108
120
|
ctx.beginPath();
|
|
109
|
-
ctx.strokeStyle = HEX.warning;
|
|
121
|
+
ctx.strokeStyle = hexAlpha(HEX.warning, 0.6);
|
|
110
122
|
ctx.lineWidth = 1;
|
|
111
|
-
ctx.
|
|
123
|
+
ctx.lineJoin = 'round';
|
|
124
|
+
ctx.setLineDash([5, 4]);
|
|
112
125
|
for (let i = 0; i < data.length; i++) {
|
|
113
126
|
const x = xAt(i);
|
|
114
127
|
const y = yCost(costs[i]);
|
|
@@ -117,11 +130,12 @@ const TokenChart = memo(function TokenChart({ data }) {
|
|
|
117
130
|
ctx.stroke();
|
|
118
131
|
ctx.setLineDash([]);
|
|
119
132
|
|
|
120
|
-
// Cache hit rate line (dotted)
|
|
133
|
+
// ── Cache hit rate line (dotted, subtle) ────────────────
|
|
121
134
|
if (hasCacheData) {
|
|
122
135
|
ctx.beginPath();
|
|
123
|
-
ctx.strokeStyle = hexAlpha(HEX.info, 0.
|
|
136
|
+
ctx.strokeStyle = hexAlpha(HEX.info, 0.45);
|
|
124
137
|
ctx.lineWidth = 1;
|
|
138
|
+
ctx.lineJoin = 'round';
|
|
125
139
|
ctx.setLineDash([2, 3]);
|
|
126
140
|
let started = false;
|
|
127
141
|
for (let i = 0; i < data.length; i++) {
|
|
@@ -136,101 +150,104 @@ const TokenChart = memo(function TokenChart({ data }) {
|
|
|
136
150
|
ctx.setLineDash([]);
|
|
137
151
|
}
|
|
138
152
|
|
|
139
|
-
//
|
|
140
|
-
ctx.
|
|
141
|
-
ctx.font = "10px 'JetBrains Mono Variable', monospace";
|
|
153
|
+
// ── Inline legend (top-right, pill-style) ───────────────
|
|
154
|
+
ctx.font = "9px 'Inter Variable', sans-serif";
|
|
142
155
|
ctx.textAlign = 'right';
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
ctx.fillText(fmtNum(val), pad.left - 6, pad.top + (h / 4) * i + 4);
|
|
146
|
-
}
|
|
156
|
+
let rx = width - pad.right - 4;
|
|
157
|
+
const ly = 14;
|
|
147
158
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
ctx.
|
|
159
|
+
if (hasCacheData) {
|
|
160
|
+
ctx.fillStyle = hexAlpha(HEX.info, 0.5);
|
|
161
|
+
ctx.fillText('Cache', rx, ly);
|
|
162
|
+
rx -= ctx.measureText('Cache').width + 4;
|
|
163
|
+
ctx.beginPath();
|
|
164
|
+
ctx.arc(rx, ly - 3, 2.5, 0, Math.PI * 2);
|
|
165
|
+
ctx.fillStyle = hexAlpha(HEX.info, 0.5);
|
|
166
|
+
ctx.fill();
|
|
167
|
+
rx -= 12;
|
|
154
168
|
}
|
|
155
169
|
|
|
156
|
-
|
|
157
|
-
ctx.
|
|
158
|
-
ctx.
|
|
159
|
-
|
|
160
|
-
|
|
170
|
+
ctx.fillStyle = hexAlpha(HEX.warning, 0.7);
|
|
171
|
+
ctx.fillText('Cost', rx, ly);
|
|
172
|
+
rx -= ctx.measureText('Cost').width + 4;
|
|
173
|
+
ctx.beginPath();
|
|
174
|
+
ctx.arc(rx, ly - 3, 2.5, 0, Math.PI * 2);
|
|
175
|
+
ctx.fillStyle = hexAlpha(HEX.warning, 0.7);
|
|
176
|
+
ctx.fill();
|
|
177
|
+
rx -= 12;
|
|
161
178
|
|
|
162
179
|
ctx.fillStyle = HEX.accent;
|
|
163
|
-
ctx.
|
|
164
|
-
|
|
165
|
-
ctx.fillStyle = HEX.text2;
|
|
166
|
-
ctx.fillText('Tokens', lx, legendY);
|
|
167
|
-
lx += 44;
|
|
168
|
-
|
|
169
|
-
ctx.fillStyle = HEX.warning;
|
|
170
|
-
ctx.setLineDash([3, 2]);
|
|
180
|
+
ctx.fillText('Tokens', rx, ly);
|
|
181
|
+
rx -= ctx.measureText('Tokens').width + 4;
|
|
171
182
|
ctx.beginPath();
|
|
172
|
-
ctx.
|
|
173
|
-
ctx.
|
|
174
|
-
ctx.
|
|
175
|
-
ctx.lineWidth = 1;
|
|
176
|
-
ctx.stroke();
|
|
177
|
-
ctx.setLineDash([]);
|
|
178
|
-
lx += 11;
|
|
179
|
-
ctx.fillStyle = HEX.text2;
|
|
180
|
-
ctx.fillText('Cost', lx, legendY);
|
|
181
|
-
|
|
182
|
-
if (hasCacheData) {
|
|
183
|
-
lx += 36;
|
|
184
|
-
ctx.setLineDash([2, 2]);
|
|
185
|
-
ctx.beginPath();
|
|
186
|
-
ctx.moveTo(lx, legendY - 2);
|
|
187
|
-
ctx.lineTo(lx + 8, legendY - 2);
|
|
188
|
-
ctx.strokeStyle = hexAlpha(HEX.info, 0.5);
|
|
189
|
-
ctx.lineWidth = 1;
|
|
190
|
-
ctx.stroke();
|
|
191
|
-
ctx.setLineDash([]);
|
|
192
|
-
lx += 11;
|
|
193
|
-
ctx.fillStyle = HEX.text2;
|
|
194
|
-
ctx.fillText('Cache', lx, legendY);
|
|
195
|
-
}
|
|
183
|
+
ctx.arc(rx, ly - 3, 2.5, 0, Math.PI * 2);
|
|
184
|
+
ctx.fillStyle = HEX.accent;
|
|
185
|
+
ctx.fill();
|
|
196
186
|
|
|
197
|
-
// Hover crosshair
|
|
187
|
+
// ── Hover crosshair + tooltip ───────────────────────────
|
|
198
188
|
if (hover && hover.index >= 0 && hover.index < data.length) {
|
|
199
189
|
const hx = hover.x;
|
|
190
|
+
|
|
191
|
+
// Vertical line
|
|
200
192
|
ctx.beginPath();
|
|
201
193
|
ctx.moveTo(hx, pad.top);
|
|
202
194
|
ctx.lineTo(hx, pad.top + h);
|
|
203
|
-
ctx.strokeStyle = hexAlpha(HEX.
|
|
195
|
+
ctx.strokeStyle = hexAlpha(HEX.text1, 0.15);
|
|
204
196
|
ctx.lineWidth = 1;
|
|
205
197
|
ctx.setLineDash([]);
|
|
206
198
|
ctx.stroke();
|
|
207
199
|
|
|
200
|
+
// Dot on token line
|
|
208
201
|
const d = data[hover.index];
|
|
202
|
+
const dotY = yToken(d.tokens || 0);
|
|
203
|
+
ctx.beginPath();
|
|
204
|
+
ctx.arc(hx, dotY, 3, 0, Math.PI * 2);
|
|
205
|
+
ctx.fillStyle = HEX.accent;
|
|
206
|
+
ctx.fill();
|
|
207
|
+
|
|
208
|
+
// Tooltip
|
|
209
209
|
const lines = [
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
{ label: 'Tokens', value: fmtNum(d.tokens || 0), color: HEX.accent },
|
|
211
|
+
{ label: 'Cost', value: fmtDollar(d.costUsd || 0), color: HEX.warning },
|
|
212
212
|
];
|
|
213
|
-
if (d.cacheHitRate != null)
|
|
213
|
+
if (d.cacheHitRate != null) {
|
|
214
|
+
lines.push({ label: 'Cache', value: fmtPct(d.cacheHitRate * 100), color: HEX.info });
|
|
215
|
+
}
|
|
214
216
|
|
|
215
|
-
const tooltipW =
|
|
216
|
-
const tooltipH = lines.length *
|
|
217
|
-
let tx = hx +
|
|
218
|
-
if (tx + tooltipW > width -
|
|
219
|
-
const ty = pad.top
|
|
217
|
+
const tooltipW = 100;
|
|
218
|
+
const tooltipH = lines.length * 16 + 12;
|
|
219
|
+
let tx = hx + 12;
|
|
220
|
+
if (tx + tooltipW > width - 8) tx = hx - tooltipW - 12;
|
|
221
|
+
const ty = Math.max(pad.top, dotY - tooltipH / 2);
|
|
220
222
|
|
|
221
|
-
|
|
222
|
-
ctx.
|
|
223
|
-
ctx.lineWidth = 1;
|
|
223
|
+
// Background
|
|
224
|
+
ctx.fillStyle = hexAlpha(HEX.surface0, 0.92);
|
|
224
225
|
ctx.beginPath();
|
|
225
|
-
ctx.roundRect(tx, ty, tooltipW, tooltipH,
|
|
226
|
+
ctx.roundRect(tx, ty, tooltipW, tooltipH, 4);
|
|
226
227
|
ctx.fill();
|
|
228
|
+
ctx.strokeStyle = hexAlpha(HEX.text4, 0.2);
|
|
229
|
+
ctx.lineWidth = 1;
|
|
227
230
|
ctx.stroke();
|
|
228
231
|
|
|
229
|
-
|
|
230
|
-
ctx.fillStyle = HEX.text1;
|
|
232
|
+
// Rows
|
|
231
233
|
ctx.textAlign = 'left';
|
|
232
234
|
lines.forEach((line, i) => {
|
|
233
|
-
|
|
235
|
+
const rowY = ty + 14 + i * 16;
|
|
236
|
+
// Dot
|
|
237
|
+
ctx.beginPath();
|
|
238
|
+
ctx.arc(tx + 8, rowY - 3, 2, 0, Math.PI * 2);
|
|
239
|
+
ctx.fillStyle = line.color;
|
|
240
|
+
ctx.fill();
|
|
241
|
+
// Label
|
|
242
|
+
ctx.font = "8px 'Inter Variable', sans-serif";
|
|
243
|
+
ctx.fillStyle = HEX.text3;
|
|
244
|
+
ctx.fillText(line.label, tx + 14, rowY);
|
|
245
|
+
// Value
|
|
246
|
+
ctx.font = "9px 'JetBrains Mono Variable', monospace";
|
|
247
|
+
ctx.fillStyle = HEX.text0;
|
|
248
|
+
ctx.textAlign = 'right';
|
|
249
|
+
ctx.fillText(line.value, tx + tooltipW - 8, rowY);
|
|
250
|
+
ctx.textAlign = 'left';
|
|
234
251
|
});
|
|
235
252
|
}
|
|
236
253
|
}, [data, width, height, hover, w, h, pad]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.4",
|
|
4
4
|
"description": "Open-source agent orchestration layer — the AI company OS. MCP integrations (Slack, Gmail, Stripe, 15+), agent scheduling (cron), business roles (CMO, CFO, EA). GUI dashboard, multi-agent coordination, zero cold-start, infinite sessions. Works with Claude Code, Codex, Gemini CLI, Ollama.",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"author": "Groove Dev <hello@groovedev.ai> (https://groovedev.ai)",
|
|
@@ -20,11 +20,14 @@ import { connect } from '../src/commands/connect.js';
|
|
|
20
20
|
import { disconnect } from '../src/commands/disconnect.js';
|
|
21
21
|
import { audit } from '../src/commands/audit.js';
|
|
22
22
|
import { federationPair, federationUnpair, federationList, federationStatus } from '../src/commands/federation.js';
|
|
23
|
+
import { createRequire } from 'node:module';
|
|
24
|
+
const require = createRequire(import.meta.url);
|
|
25
|
+
const { version } = require('../../../package.json');
|
|
23
26
|
|
|
24
27
|
program
|
|
25
28
|
.name('groove')
|
|
26
29
|
.description('Agent orchestration layer for AI coding tools')
|
|
27
|
-
.version(
|
|
30
|
+
.version(version);
|
|
28
31
|
|
|
29
32
|
program
|
|
30
33
|
.command('start')
|