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.
@@ -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-z5gtKpHQ.js"></script>
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
- * Self-sizing token chart — fills its parent via absolute positioning.
8
- * Parent must be `position: relative` (or a grid/flex cell).
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
- const pad = { top: 16, right: 62, bottom: 30, left: 62 };
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: observe wrapper
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
- // Grid lines
67
- ctx.strokeStyle = HEX.surface5;
68
- ctx.lineWidth = 0.5;
69
- for (let i = 0; i <= 4; i++) {
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
- const xAt = (i) => pad.left + (i / (data.length - 1)) * w;
78
- const yToken = (v) => pad.top + h - (v / maxT) * h;
79
- const yCost = (v) => pad.top + h - (v / maxC) * h;
80
- const yCache = (v) => pad.top + h - (v * h);
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.12));
92
- grad.addColorStop(1, hexAlpha(HEX.accent, 0.01));
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.setLineDash([4, 3]);
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.5);
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
- // Left axis labels (tokens)
140
- ctx.fillStyle = HEX.text2;
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
- for (let i = 0; i <= 4; i++) {
144
- const val = (maxT / 4) * (4 - i);
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
- // Right axis labels (cost)
149
- ctx.textAlign = 'left';
150
- ctx.fillStyle = HEX.text3;
151
- for (let i = 0; i <= 4; i++) {
152
- const val = (maxC / 4) * (4 - i);
153
- ctx.fillText(fmtDollar(val), pad.left + w + 6, pad.top + (h / 4) * i + 4);
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
- // Legend
157
- ctx.font = "10px 'Inter Variable', sans-serif";
158
- ctx.textAlign = 'left';
159
- const legendY = height - 8;
160
- let lx = pad.left;
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.fillRect(lx, legendY - 3, 8, 1.5);
164
- lx += 11;
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.moveTo(lx, legendY - 2);
173
- ctx.lineTo(lx + 8, legendY - 2);
174
- ctx.strokeStyle = HEX.warning;
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.text2, 0.3);
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
- `${fmtNum(d.tokens || 0)} tok`,
211
- `${fmtDollar(d.costUsd || 0)}`,
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) lines.push(`${fmtPct(d.cacheHitRate * 100)} cache`);
213
+ if (d.cacheHitRate != null) {
214
+ lines.push({ label: 'Cache', value: fmtPct(d.cacheHitRate * 100), color: HEX.info });
215
+ }
214
216
 
215
- const tooltipW = 80;
216
- const tooltipH = lines.length * 14 + 8;
217
- let tx = hx + 8;
218
- if (tx + tooltipW > width - 4) tx = hx - tooltipW - 8;
219
- const ty = pad.top + 8;
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
- ctx.fillStyle = hexAlpha(HEX.surface1, 0.95);
222
- ctx.strokeStyle = HEX.surface4;
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, 3);
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
- ctx.font = "9px 'JetBrains Mono Variable', monospace";
230
- ctx.fillStyle = HEX.text1;
232
+ // Rows
231
233
  ctx.textAlign = 'left';
232
234
  lines.forEach((line, i) => {
233
- ctx.fillText(line, tx + 6, ty + 14 + i * 14);
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.2",
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('0.22.20');
30
+ .version(version);
28
31
 
29
32
  program
30
33
  .command('start')