oh-my-opencode-dashboard 0.0.5 → 0.1.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/README.md +32 -4
- package/dist/assets/index-B1tQFDjw.js +40 -0
- package/dist/assets/index-BFRahC0d.css +1 -0
- package/dist/index.html +3 -3
- package/package.json +2 -2
- package/src/App.tsx +486 -214
- package/src/app-payload.test.ts +108 -1
- package/src/server/api.test.ts +28 -7
- package/src/server/api.ts +1 -1
- package/src/server/dashboard.test.ts +41 -0
- package/src/server/dashboard.ts +79 -0
- package/src/server/dev.ts +1 -1
- package/src/server/start.ts +1 -1
- package/src/styles.css +58 -0
- package/src/time-series-ui.test.tsx +111 -0
- package/src/timeseries-stacked.test.ts +36 -24
- package/src/timeseries-stacked.ts +21 -5
- package/dist/assets/index--GqzhA4-.css +0 -1
- package/dist/assets/index-CiC6k4Yg.js +0 -40
|
@@ -4,21 +4,21 @@ import { computeStackedSegments, AgentCounts, StackedSegment } from "./timeserie
|
|
|
4
4
|
describe("computeStackedSegments", () => {
|
|
5
5
|
describe("Edge cases", () => {
|
|
6
6
|
it("should return empty array when chartHeight <= 0", () => {
|
|
7
|
-
const counts: AgentCounts = { sisyphus: 10, prometheus: 5, atlas: 3 };
|
|
7
|
+
const counts: AgentCounts = { sisyphus: 10, prometheus: 5, atlas: 3, other: 0 };
|
|
8
8
|
|
|
9
9
|
expect(computeStackedSegments(counts, 20, 0)).toEqual([]);
|
|
10
10
|
expect(computeStackedSegments(counts, 20, -5)).toEqual([]);
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
it("should return empty array when scaleMax <= 0", () => {
|
|
14
|
-
const counts: AgentCounts = { sisyphus: 10, prometheus: 5, atlas: 3 };
|
|
14
|
+
const counts: AgentCounts = { sisyphus: 10, prometheus: 5, atlas: 3, other: 0 };
|
|
15
15
|
|
|
16
16
|
expect(computeStackedSegments(counts, 0, 100)).toEqual([]);
|
|
17
17
|
expect(computeStackedSegments(counts, -10, 100)).toEqual([]);
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
it("should return empty array when all counts are zero", () => {
|
|
21
|
-
const counts: AgentCounts = { sisyphus: 0, prometheus: 0, atlas: 0 };
|
|
21
|
+
const counts: AgentCounts = { sisyphus: 0, prometheus: 0, atlas: 0, other: 0 };
|
|
22
22
|
|
|
23
23
|
const result = computeStackedSegments(counts, 20, 100);
|
|
24
24
|
expect(result).toEqual([]);
|
|
@@ -29,6 +29,7 @@ describe("computeStackedSegments", () => {
|
|
|
29
29
|
sisyphus: NaN,
|
|
30
30
|
prometheus: Infinity,
|
|
31
31
|
atlas: -5,
|
|
32
|
+
other: NaN,
|
|
32
33
|
} as unknown as AgentCounts;
|
|
33
34
|
|
|
34
35
|
const result = computeStackedSegments(invalidCounts, 20, 100);
|
|
@@ -40,6 +41,7 @@ describe("computeStackedSegments", () => {
|
|
|
40
41
|
sisyphus: 10,
|
|
41
42
|
prometheus: NaN,
|
|
42
43
|
atlas: -3,
|
|
44
|
+
other: Infinity,
|
|
43
45
|
} as unknown as AgentCounts;
|
|
44
46
|
|
|
45
47
|
const result = computeStackedSegments(mixedCounts, 20, 100);
|
|
@@ -54,7 +56,7 @@ describe("computeStackedSegments", () => {
|
|
|
54
56
|
|
|
55
57
|
describe("Single agent scenarios", () => {
|
|
56
58
|
it("should return one segment when only sisyphus is non-zero", () => {
|
|
57
|
-
const counts: AgentCounts = { sisyphus: 10, prometheus: 0, atlas: 0 };
|
|
59
|
+
const counts: AgentCounts = { sisyphus: 10, prometheus: 0, atlas: 0, other: 0 };
|
|
58
60
|
|
|
59
61
|
const result = computeStackedSegments(counts, 20, 100);
|
|
60
62
|
expect(result).toHaveLength(1);
|
|
@@ -66,7 +68,7 @@ describe("computeStackedSegments", () => {
|
|
|
66
68
|
});
|
|
67
69
|
|
|
68
70
|
it("should return one segment when only prometheus is non-zero", () => {
|
|
69
|
-
const counts: AgentCounts = { sisyphus: 0, prometheus: 15, atlas: 0 };
|
|
71
|
+
const counts: AgentCounts = { sisyphus: 0, prometheus: 15, atlas: 0, other: 0 };
|
|
70
72
|
|
|
71
73
|
const result = computeStackedSegments(counts, 30, 120);
|
|
72
74
|
expect(result).toHaveLength(1);
|
|
@@ -78,7 +80,7 @@ describe("computeStackedSegments", () => {
|
|
|
78
80
|
});
|
|
79
81
|
|
|
80
82
|
it("should return one segment when only atlas is non-zero", () => {
|
|
81
|
-
const counts: AgentCounts = { sisyphus: 0, prometheus: 0, atlas: 8 };
|
|
83
|
+
const counts: AgentCounts = { sisyphus: 0, prometheus: 0, atlas: 8, other: 0 };
|
|
82
84
|
|
|
83
85
|
const result = computeStackedSegments(counts, 16, 80);
|
|
84
86
|
expect(result).toHaveLength(1);
|
|
@@ -90,7 +92,7 @@ describe("computeStackedSegments", () => {
|
|
|
90
92
|
});
|
|
91
93
|
|
|
92
94
|
it("should round to at least 1px for non-zero values", () => {
|
|
93
|
-
const counts: AgentCounts = { sisyphus: 1, prometheus: 0, atlas: 0 };
|
|
95
|
+
const counts: AgentCounts = { sisyphus: 1, prometheus: 0, atlas: 0, other: 0 };
|
|
94
96
|
|
|
95
97
|
const result = computeStackedSegments(counts, 1000, 100);
|
|
96
98
|
expect(result).toHaveLength(1);
|
|
@@ -100,7 +102,7 @@ describe("computeStackedSegments", () => {
|
|
|
100
102
|
|
|
101
103
|
describe("Multiple agent scenarios", () => {
|
|
102
104
|
it("should return multiple segments in correct order (bottom to top)", () => {
|
|
103
|
-
const counts: AgentCounts = { sisyphus: 10, prometheus: 20, atlas: 15 };
|
|
105
|
+
const counts: AgentCounts = { sisyphus: 10, prometheus: 20, atlas: 15, other: 0 };
|
|
104
106
|
|
|
105
107
|
const result = computeStackedSegments(counts, 50, 100);
|
|
106
108
|
expect(result).toHaveLength(3);
|
|
@@ -116,7 +118,7 @@ describe("computeStackedSegments", () => {
|
|
|
116
118
|
});
|
|
117
119
|
|
|
118
120
|
it("should correctly calculate heights for all agents", () => {
|
|
119
|
-
const counts: AgentCounts = { sisyphus: 10, prometheus: 20, atlas: 15 };
|
|
121
|
+
const counts: AgentCounts = { sisyphus: 10, prometheus: 20, atlas: 15, other: 0 };
|
|
120
122
|
|
|
121
123
|
const result = computeStackedSegments(counts, 50, 100);
|
|
122
124
|
const totalHeight = result.reduce((sum, seg) => sum + seg.height, 0);
|
|
@@ -130,7 +132,7 @@ describe("computeStackedSegments", () => {
|
|
|
130
132
|
});
|
|
131
133
|
|
|
132
134
|
it("should handle zero values mixed with non-zero values", () => {
|
|
133
|
-
const counts: AgentCounts = { sisyphus: 10, prometheus: 0, atlas: 15 };
|
|
135
|
+
const counts: AgentCounts = { sisyphus: 10, prometheus: 0, atlas: 15, other: 0 };
|
|
134
136
|
|
|
135
137
|
const result = computeStackedSegments(counts, 30, 90);
|
|
136
138
|
expect(result).toHaveLength(2);
|
|
@@ -142,11 +144,21 @@ describe("computeStackedSegments", () => {
|
|
|
142
144
|
// Check positioning
|
|
143
145
|
expect(result[0].y).toBeGreaterThan(result[1].y);
|
|
144
146
|
});
|
|
147
|
+
it("should include sand segment when other is non-zero", () => {
|
|
148
|
+
const counts: AgentCounts = { sisyphus: 10, prometheus: 20, atlas: 15, other: 5 };
|
|
149
|
+
const result = computeStackedSegments(counts, 50, 100);
|
|
150
|
+
expect(result).toHaveLength(4);
|
|
151
|
+
|
|
152
|
+
expect(result[0].tone).toBe("teal");
|
|
153
|
+
expect(result[1].tone).toBe("red");
|
|
154
|
+
expect(result[2].tone).toBe("green");
|
|
155
|
+
expect(result[3].tone).toBe("sand");
|
|
156
|
+
});
|
|
145
157
|
});
|
|
146
158
|
|
|
147
159
|
describe("Clamping and overflow behavior", () => {
|
|
148
160
|
it("should ensure sum of heights never exceeds chartHeight", () => {
|
|
149
|
-
const counts: AgentCounts = { sisyphus: 100, prometheus: 100, atlas: 100 };
|
|
161
|
+
const counts: AgentCounts = { sisyphus: 100, prometheus: 100, atlas: 100, other: 100 };
|
|
150
162
|
|
|
151
163
|
const result = computeStackedSegments(counts, 100, 50); // Should overflow
|
|
152
164
|
const totalHeight = result.reduce((sum, seg) => sum + seg.height, 0);
|
|
@@ -155,17 +167,17 @@ describe("computeStackedSegments", () => {
|
|
|
155
167
|
});
|
|
156
168
|
|
|
157
169
|
it("should preserve at least 1px for non-zero agents when possible", () => {
|
|
158
|
-
const counts: AgentCounts = { sisyphus: 1, prometheus: 1, atlas: 1 };
|
|
170
|
+
const counts: AgentCounts = { sisyphus: 1, prometheus: 1, atlas: 1, other: 1 };
|
|
159
171
|
|
|
160
172
|
const result = computeStackedSegments(counts, 100, 10);
|
|
161
173
|
|
|
162
174
|
// All agents should be visible with at least 1px each
|
|
163
|
-
expect(result).toHaveLength(
|
|
175
|
+
expect(result).toHaveLength(4);
|
|
164
176
|
expect(result.every(seg => seg.height >= 1)).toBe(true);
|
|
165
177
|
});
|
|
166
178
|
|
|
167
179
|
it("should distribute overflow reduction fairly", () => {
|
|
168
|
-
const counts: AgentCounts = { sisyphus: 40, prometheus: 35, atlas: 25 };
|
|
180
|
+
const counts: AgentCounts = { sisyphus: 40, prometheus: 35, atlas: 25, other: 10 };
|
|
169
181
|
|
|
170
182
|
const result = computeStackedSegments(counts, 100, 80);
|
|
171
183
|
const totalHeight = result.reduce((sum, seg) => sum + seg.height, 0);
|
|
@@ -178,7 +190,7 @@ describe("computeStackedSegments", () => {
|
|
|
178
190
|
});
|
|
179
191
|
|
|
180
192
|
it("should handle extreme overflow gracefully", () => {
|
|
181
|
-
const counts: AgentCounts = { sisyphus: 1000, prometheus: 1000, atlas: 1000 };
|
|
193
|
+
const counts: AgentCounts = { sisyphus: 1000, prometheus: 1000, atlas: 1000, other: 1000 };
|
|
182
194
|
|
|
183
195
|
const result = computeStackedSegments(counts, 100, 5);
|
|
184
196
|
const totalHeight = result.reduce((sum, seg) => sum + seg.height, 0);
|
|
@@ -191,7 +203,7 @@ describe("computeStackedSegments", () => {
|
|
|
191
203
|
|
|
192
204
|
describe("Deterministic behavior", () => {
|
|
193
205
|
it("should produce identical results for identical inputs", () => {
|
|
194
|
-
const counts: AgentCounts = { sisyphus: 15, prometheus: 25, atlas: 10 };
|
|
206
|
+
const counts: AgentCounts = { sisyphus: 15, prometheus: 25, atlas: 10, other: 0 };
|
|
195
207
|
|
|
196
208
|
const result1 = computeStackedSegments(counts, 60, 100);
|
|
197
209
|
const result2 = computeStackedSegments(counts, 60, 100);
|
|
@@ -201,10 +213,10 @@ describe("computeStackedSegments", () => {
|
|
|
201
213
|
|
|
202
214
|
it("should maintain consistent segment order regardless of input magnitudes", () => {
|
|
203
215
|
const testCases = [
|
|
204
|
-
{ sisyphus: 100, prometheus: 1, atlas: 1 },
|
|
205
|
-
{ sisyphus: 1, prometheus: 100, atlas: 1 },
|
|
206
|
-
{ sisyphus: 1, prometheus: 1, atlas: 100 },
|
|
207
|
-
{ sisyphus: 50, prometheus: 25, atlas: 75 },
|
|
216
|
+
{ sisyphus: 100, prometheus: 1, atlas: 1, other: 0 },
|
|
217
|
+
{ sisyphus: 1, prometheus: 100, atlas: 1, other: 0 },
|
|
218
|
+
{ sisyphus: 1, prometheus: 1, atlas: 100, other: 0 },
|
|
219
|
+
{ sisyphus: 50, prometheus: 25, atlas: 75, other: 0 },
|
|
208
220
|
] as AgentCounts[];
|
|
209
221
|
|
|
210
222
|
testCases.forEach(counts => {
|
|
@@ -230,7 +242,7 @@ describe("computeStackedSegments", () => {
|
|
|
230
242
|
|
|
231
243
|
describe("Boundary conditions", () => {
|
|
232
244
|
it("should handle very small chartHeight", () => {
|
|
233
|
-
const counts: AgentCounts = { sisyphus: 10, prometheus: 5, atlas: 3 };
|
|
245
|
+
const counts: AgentCounts = { sisyphus: 10, prometheus: 5, atlas: 3, other: 0 };
|
|
234
246
|
|
|
235
247
|
const result = computeStackedSegments(counts, 20, 1);
|
|
236
248
|
const totalHeight = result.reduce((sum, seg) => sum + seg.height, 0);
|
|
@@ -239,7 +251,7 @@ describe("computeStackedSegments", () => {
|
|
|
239
251
|
});
|
|
240
252
|
|
|
241
253
|
it("should handle very large scaleMax", () => {
|
|
242
|
-
const counts: AgentCounts = { sisyphus: 10, prometheus: 5, atlas: 3 };
|
|
254
|
+
const counts: AgentCounts = { sisyphus: 10, prometheus: 5, atlas: 3, other: 0 };
|
|
243
255
|
|
|
244
256
|
const result = computeStackedSegments(counts, 1000000, 100);
|
|
245
257
|
|
|
@@ -249,7 +261,7 @@ describe("computeStackedSegments", () => {
|
|
|
249
261
|
});
|
|
250
262
|
|
|
251
263
|
it("should handle fractional results correctly", () => {
|
|
252
|
-
const counts: AgentCounts = { sisyphus: 1, prometheus: 1, atlas: 1 };
|
|
264
|
+
const counts: AgentCounts = { sisyphus: 1, prometheus: 1, atlas: 1, other: 1 };
|
|
253
265
|
|
|
254
266
|
const result = computeStackedSegments(counts, 3, 10);
|
|
255
267
|
|
|
@@ -258,4 +270,4 @@ describe("computeStackedSegments", () => {
|
|
|
258
270
|
expect(result.every(seg => Number.isInteger(seg.y))).toBe(true);
|
|
259
271
|
});
|
|
260
272
|
});
|
|
261
|
-
});
|
|
273
|
+
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Independent of React/DOM for testability.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export type AgentTone = "teal" | "red" | "green";
|
|
6
|
+
export type AgentTone = "teal" | "red" | "green" | "sand";
|
|
7
7
|
|
|
8
8
|
export interface StackedSegment {
|
|
9
9
|
tone: AgentTone;
|
|
@@ -15,6 +15,7 @@ export interface AgentCounts {
|
|
|
15
15
|
sisyphus: number;
|
|
16
16
|
prometheus: number;
|
|
17
17
|
atlas: number;
|
|
18
|
+
other: number;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -24,6 +25,7 @@ export interface AgentCounts {
|
|
|
24
25
|
* 1. Sisyphus (teal) - bottom
|
|
25
26
|
* 2. Prometheus (red) - middle
|
|
26
27
|
* 3. Atlas (green) - top
|
|
28
|
+
* 4. Other main agents (sand) - top
|
|
27
29
|
*
|
|
28
30
|
* @param counts - Agent counts for this bucket
|
|
29
31
|
* @param scaleMax - Maximum value for scaling (must be > 0)
|
|
@@ -45,9 +47,10 @@ export function computeStackedSegments(
|
|
|
45
47
|
sisyphus: Math.max(0, Number.isFinite(counts.sisyphus) ? counts.sisyphus : 0),
|
|
46
48
|
prometheus: Math.max(0, Number.isFinite(counts.prometheus) ? counts.prometheus : 0),
|
|
47
49
|
atlas: Math.max(0, Number.isFinite(counts.atlas) ? counts.atlas : 0),
|
|
50
|
+
other: Math.max(0, Number.isFinite(counts.other) ? counts.other : 0),
|
|
48
51
|
};
|
|
49
52
|
|
|
50
|
-
const total = sanitized.sisyphus + sanitized.prometheus + sanitized.atlas;
|
|
53
|
+
const total = sanitized.sisyphus + sanitized.prometheus + sanitized.atlas + sanitized.other;
|
|
51
54
|
if (total === 0) {
|
|
52
55
|
return [];
|
|
53
56
|
}
|
|
@@ -57,6 +60,7 @@ export function computeStackedSegments(
|
|
|
57
60
|
sisyphus: (sanitized.sisyphus / scaleMax) * chartHeight,
|
|
58
61
|
prometheus: (sanitized.prometheus / scaleMax) * chartHeight,
|
|
59
62
|
atlas: (sanitized.atlas / scaleMax) * chartHeight,
|
|
63
|
+
other: (sanitized.other / scaleMax) * chartHeight,
|
|
60
64
|
};
|
|
61
65
|
|
|
62
66
|
// Round to pixels, ensuring sum never exceeds chartHeight
|
|
@@ -64,10 +68,11 @@ export function computeStackedSegments(
|
|
|
64
68
|
sisyphus: Math.max(1, Math.round(rawHeights.sisyphus)) * (sanitized.sisyphus > 0 ? 1 : 0),
|
|
65
69
|
prometheus: Math.max(1, Math.round(rawHeights.prometheus)) * (sanitized.prometheus > 0 ? 1 : 0),
|
|
66
70
|
atlas: Math.max(1, Math.round(rawHeights.atlas)) * (sanitized.atlas > 0 ? 1 : 0),
|
|
71
|
+
other: Math.max(1, Math.round(rawHeights.other)) * (sanitized.other > 0 ? 1 : 0),
|
|
67
72
|
};
|
|
68
73
|
|
|
69
74
|
// Ensure non-zero agents remain visible when possible
|
|
70
|
-
let totalRounded = roundedHeights.sisyphus + roundedHeights.prometheus + roundedHeights.atlas;
|
|
75
|
+
let totalRounded = roundedHeights.sisyphus + roundedHeights.prometheus + roundedHeights.atlas + roundedHeights.other;
|
|
71
76
|
|
|
72
77
|
// Distribute any overflow reduction fairly
|
|
73
78
|
if (totalRounded > chartHeight) {
|
|
@@ -76,6 +81,7 @@ export function computeStackedSegments(
|
|
|
76
81
|
{ key: 'sisyphus' as keyof typeof roundedHeights, height: roundedHeights.sisyphus },
|
|
77
82
|
{ key: 'prometheus' as keyof typeof roundedHeights, height: roundedHeights.prometheus },
|
|
78
83
|
{ key: 'atlas' as keyof typeof roundedHeights, height: roundedHeights.atlas },
|
|
84
|
+
{ key: 'other' as keyof typeof roundedHeights, height: roundedHeights.other },
|
|
79
85
|
].filter(w => w.height > 0);
|
|
80
86
|
|
|
81
87
|
if (weights.length > 0) {
|
|
@@ -93,7 +99,7 @@ export function computeStackedSegments(
|
|
|
93
99
|
}
|
|
94
100
|
|
|
95
101
|
// If still over, trim from largest segments
|
|
96
|
-
totalRounded = roundedHeights.sisyphus + roundedHeights.prometheus + roundedHeights.atlas;
|
|
102
|
+
totalRounded = roundedHeights.sisyphus + roundedHeights.prometheus + roundedHeights.atlas + roundedHeights.other;
|
|
97
103
|
while (totalRounded > chartHeight) {
|
|
98
104
|
const sortedWeights = weights
|
|
99
105
|
.map(w => ({ ...w, height: roundedHeights[w.key] }))
|
|
@@ -141,5 +147,15 @@ export function computeStackedSegments(
|
|
|
141
147
|
});
|
|
142
148
|
}
|
|
143
149
|
|
|
150
|
+
// Other main agents (sand) - top
|
|
151
|
+
if (roundedHeights.other > 0) {
|
|
152
|
+
currentY -= roundedHeights.other;
|
|
153
|
+
segments.push({
|
|
154
|
+
tone: "sand",
|
|
155
|
+
y: currentY,
|
|
156
|
+
height: roundedHeights.other,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
144
160
|
return segments;
|
|
145
|
-
}
|
|
161
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{--bg-cream: #f5efe2;--bg-cream-2: #efe6d2;--paper: rgba(255, 255, 255, .52);--paper-strong: rgba(255, 255, 255, .72);--ink: #1f2426;--muted: rgba(31, 36, 38, .62);--line: rgba(31, 36, 38, .1);--shadow: 0 18px 40px rgba(29, 32, 33, .09);--teal-ink: #0f3f3a;--teal: #0f5a51;--teal-soft: rgba(15, 90, 81, .13);--green: #1f6b3d;--green-soft: rgba(31, 107, 61, .14);--sand: #6a5b3d;--sand-soft: rgba(135, 114, 71, .14);--red: #7a2b2b;--red-soft: rgba(122, 43, 43, .12);--radius: 18px}*{box-sizing:border-box}html,body{height:100%}body{margin:0;color:var(--ink);background:radial-gradient(1200px 500px at 16% 0%,rgba(15,90,81,.1),transparent 60%),radial-gradient(900px 420px at 80% 12%,rgba(135,114,71,.12),transparent 62%),linear-gradient(180deg,var(--bg-cream),var(--bg-cream-2));font-family:ui-sans-serif,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;text-rendering:geometricPrecision}.page{min-height:100%;position:relative}.page:before{content:"";position:fixed;top:0;right:0;bottom:0;left:0;pointer-events:none;background:repeating-linear-gradient(90deg,rgba(0,0,0,.012),rgba(0,0,0,.012) 1px,transparent 1px,transparent 6px),repeating-linear-gradient(0deg,rgba(0,0,0,.01),rgba(0,0,0,.01) 1px,transparent 1px,transparent 10px);mix-blend-mode:multiply;opacity:.16}.container{width:min(1120px,calc(100% - 44px));margin:0 auto;padding:28px 0 36px}.topbar{display:flex;align-items:flex-start;justify-content:space-between;gap:18px}.brand{display:flex;align-items:flex-start;gap:14px}.brandMark{width:44px;height:44px;border-radius:14px;background:radial-gradient(14px 14px at 28% 28%,#ffffff9e,#fff0 70%),linear-gradient(135deg,#0f5a51,#0d3f3a);box-shadow:0 10px 22px #0f3c372e;position:relative}.brandMark:after{content:"";position:absolute;top:7px;right:7px;bottom:7px;left:7px;border-radius:12px;border:1px solid rgba(255,255,255,.22)}.brandText h1{margin:0;font-size:34px;line-height:1.05;font-weight:650;letter-spacing:-.02em;font-family:Iowan Old Style,Palatino Linotype,Palatino,Georgia,serif}.brandText p{margin:6px 0 0;font-size:13px;color:var(--muted)}.hint{color:#7a2b2bb8}.topbarActions{display:flex;align-items:center;gap:10px;padding-top:6px}.button{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid rgba(31,36,38,.14);background:#ffffffa3;padding:9px 12px;border-radius:999px;color:var(--ink);font-size:12px;line-height:1;cursor:pointer;box-shadow:0 6px 16px #1d20210f;transition:transform .12s ease,background .12s ease,border-color .12s ease}.button:hover{transform:translateY(-1px);background:#ffffffc7;border-color:#1f24262e}.button:active{transform:translateY(0)}.pill{display:inline-flex;align-items:center;gap:8px;padding:7px 12px;border-radius:999px;border:1px solid rgba(31,36,38,.12);background:var(--paper);color:var(--ink);font-size:12px;line-height:1;white-space:nowrap}.pillDot{width:8px;height:8px;border-radius:99px;background:currentColor;opacity:.82}.pill-teal{background:var(--teal-soft);border-color:#0f5a5138;color:var(--teal-ink)}.pill-sand{background:var(--sand-soft);border-color:#87724742;color:var(--sand)}.pill-red{background:var(--red-soft);border-color:#7a2b2b42;color:var(--red)}.stack{display:flex;flex-direction:column;gap:18px;margin-top:18px}.grid2{display:grid;grid-template-columns:1fr 1fr;gap:18px}.card{border-radius:var(--radius);border:1px solid rgba(31,36,38,.12);background:#ffffff8f;box-shadow:var(--shadow);padding:18px;-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px)}.cardHeader{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}.cardHeader h2{margin:0;font-size:16px;font-weight:680;font-family:Iowan Old Style,Palatino Linotype,Palatino,Georgia,serif}.kv{display:grid;gap:10px}.kvRow{display:grid;grid-template-columns:150px 1fr;align-items:baseline;gap:12px}.kvKey{font-size:11px;letter-spacing:.08em;color:#1f242694}.kvVal{font-size:14px;color:var(--ink)}.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace}.muted{color:var(--muted)}.divider{height:1px;background:var(--line);margin:14px 0}.progressWrap{margin-top:12px}.progressTrack{height:12px;border-radius:999px;border:1px solid rgba(31,36,38,.12);background:#ffffff6b;overflow:hidden}.progressFill{height:100%;width:0;background:linear-gradient(90deg,#0f5a51,#0d3f3a);border-radius:999px;box-shadow:inset 0 0 0 1px #ffffff2e;transition:width .42s ease}.path{margin-top:10px;color:#1f2426b3;font-size:12px}.badge{width:28px;height:28px;border-radius:999px;display:grid;place-items:center;background:#ffffffa8;border:1px solid rgba(31,36,38,.14);font-size:12px}.tableWrap{overflow:auto}.table{width:100%;border-collapse:collapse}.table th{text-align:left;font-size:11px;letter-spacing:.08em;color:#1f242694;padding:12px 10px;border-bottom:1px solid var(--line);white-space:nowrap}.table td{padding:14px 10px;border-bottom:1px solid rgba(31,36,38,.07);vertical-align:top;font-size:13px}.taskTitle{font-weight:630}.taskSub{margin-top:4px;font-size:12px;color:#1f24269e}.bgTaskRowTitleWrap{display:flex;align-items:flex-start;gap:10px}.bgTaskRowTitleText{min-width:0}.bgTaskToggle{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:22px;height:22px;border-radius:10px;border:1px solid rgba(31,36,38,.16);background:#ffffff9e;box-shadow:0 6px 14px #1d20210f;color:#1f2426b8;display:inline-flex;align-items:center;justify-content:center;padding:0;cursor:pointer;flex:0 0 auto;margin-top:2px;transition:transform .12s ease,background .12s ease,border-color .12s ease}.bgTaskToggle:before{content:"▸";font-size:12px;line-height:1;transform:translate(.5px)}.bgTaskToggle:hover{transform:translateY(-1px);background:#ffffffc7;border-color:#1f242633}.bgTaskToggle:active{transform:translateY(0)}.bgTaskToggle:focus-visible{outline:2px solid rgba(15,90,81,.3);outline-offset:2px}.bgTaskToggle[aria-expanded=true]{background:#0f5a511a;border-color:#0f5a5138;color:var(--teal-ink)}.bgTaskToggle[aria-expanded=true]:before{content:"▾";transform:translateY(-.5px)}.table td.bgTaskDetailCell{padding:0 10px 14px}.bgTaskDetail{padding:12px 12px 10px;border-radius:14px;border:1px solid rgba(31,36,38,.08);background:#ffffff8a;box-shadow:inset 0 1px #ffffff47,0 10px 22px #1d20210d;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.bgTaskDetailHeader{font-size:12px;margin-bottom:10px}.bgTaskDetailEmpty{font-size:13px}.bgTaskToolCallsGrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:10px}.bgTaskToolCall{border-radius:14px;border:1px solid rgba(31,36,38,.1);background:radial-gradient(120px 50px at 12% 18%,rgba(15,90,81,.06),transparent 70%),#ffffffa3;padding:10px 11px}.bgTaskToolCallRow{display:grid;grid-template-columns:minmax(140px,1fr) max-content;gap:10px;align-items:baseline}.bgTaskToolCallTool,.bgTaskToolCallStatus,.bgTaskToolCallId{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bgTaskToolCallTime{margin-top:8px;font-size:12px}.bgTaskToolCallId{margin-top:6px;font-size:12px;opacity:.78}.details{border-radius:var(--radius);border:1px solid rgba(31,36,38,.12);background:#ffffff85;box-shadow:0 18px 40px #1d202112;overflow:hidden}.detailsSummary{list-style:none;cursor:pointer;padding:16px 18px;display:flex;align-items:center;justify-content:space-between;gap:10px}.detailsSummary::-webkit-details-marker{display:none}.detailsTitle{font-size:16px;font-weight:680;font-family:Iowan Old Style,Palatino Linotype,Palatino,Georgia,serif}.chev{width:10px;height:10px;border-right:2px solid rgba(31,36,38,.42);border-bottom:2px solid rgba(31,36,38,.42);transform:rotate(45deg);transition:transform .15s ease}.details[open] .chev{transform:rotate(-135deg)}.detailsBody{border-top:1px solid var(--line);padding:16px 18px 18px}.code{margin:0;padding:14px;border-radius:14px;border:1px solid rgba(31,36,38,.1);background:#ffffffb3;overflow:auto;font-size:12px;line-height:1.5}.footer{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:18px;padding:10px 2px 0;color:#1f242699;font-size:12px}.timeSeries{margin-top:18px;padding:16px 18px 12px;border-radius:var(--radius);background:var(--paper);box-shadow:var(--shadow);border:1px solid rgba(31,36,38,.06);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.timeSeriesHeader{display:flex;align-items:baseline;gap:18px;padding-bottom:8px;border-bottom:1px solid rgba(31,36,38,.06)}.timeSeriesTitle{margin:0;font-size:20px;font-weight:680;letter-spacing:-.02em;font-family:Iowan Old Style,Palatino Linotype,Palatino,Georgia,serif}.timeSeriesSub{margin:0;font-size:14px;color:var(--muted)}.timeSeriesAxisTop{display:grid;grid-template-columns:160px 1fr;gap:18px;margin-top:8px}.timeSeriesAxisTopLabels{display:flex;align-items:flex-end;justify-content:space-between;gap:14px;font-size:12px;letter-spacing:.03em;color:#1f24269e;font-variant-numeric:tabular-nums}.timeSeriesAxisTopLabel{white-space:nowrap}.timeSeriesRows{margin-top:8px;display:grid;gap:6px}.timeSeriesRow{display:grid;grid-template-columns:160px 1fr;gap:18px;align-items:center;padding:4px 0}.timeSeriesRow+.timeSeriesRow{border-top:1px solid rgba(31,36,38,.06);padding-top:10px}.timeSeriesRowLabel{font-size:20px;line-height:1.05;font-weight:650;letter-spacing:-.01em;font-family:Iowan Old Style,Palatino Linotype,Palatino,Georgia,serif}.timeSeriesSvgWrap{position:relative;height:24px;border-radius:10px;background:radial-gradient(140px 36px at 8% 60%,rgba(255,255,255,.16),transparent 70%),linear-gradient(180deg,#ffffff1f,#ffffff0d);box-shadow:inset 0 0 0 1px #1f24260a,inset 0 -1px #ffffff1a}.timeSeriesSvg{width:100%;height:100%;display:block}.timeSeriesGridline{stroke:#1f242614;shape-rendering:crispEdges}.timeSeriesBar{shape-rendering:crispEdges}.timeSeriesBarBaseline{fill:#1f242633}.timeSeriesBar--teal,.timeSeriesRow[data-tone=teal] .timeSeriesBar{fill:var(--teal)}.timeSeriesBar--sand,.timeSeriesRow[data-tone=sand] .timeSeriesBar{fill:var(--sand)}.timeSeriesBar--red,.timeSeriesRow[data-tone=red] .timeSeriesBar{fill:var(--red)}.timeSeriesBar--green,.timeSeriesRow[data-tone=green] .timeSeriesBar{fill:var(--green)}.timeSeriesBar--muted,.timeSeriesRow[data-tone=muted] .timeSeriesBar{fill:#1f24265c}.timeSeriesAxisBottom{display:grid;grid-template-columns:160px 1fr;gap:18px;margin-top:10px;padding-top:8px;border-top:1px solid rgba(31,36,38,.06)}.timeSeriesAxisBottomLabels{display:flex;justify-content:space-between;gap:12px;font-size:12px;letter-spacing:.02em;color:#1f24269e;font-variant-numeric:tabular-nums}.timeSeriesAxisBottomLabel{white-space:nowrap}@media (max-width: 920px){.grid2{grid-template-columns:1fr}.timeSeriesAxisTop,.timeSeriesRow,.timeSeriesAxisBottom{grid-template-columns:140px 1fr}.timeSeriesRowLabel{font-size:18px}.kvRow{grid-template-columns:140px 1fr}.table th:nth-child(6),.table td:nth-child(6){display:none}}@media (max-width: 520px){.container{width:min(1120px,calc(100% - 28px));padding-top:22px}.timeSeries{padding:14px 14px 10px}.timeSeriesAxisTop{display:none}.timeSeriesRow,.timeSeriesAxisBottom{grid-template-columns:1fr;gap:6px}.timeSeriesRow+.timeSeriesRow{padding-top:8px}.timeSeriesRowLabel{font-size:16px}.timeSeriesAxisBottomLabels{font-size:11px}.topbar{flex-direction:column;align-items:stretch}.topbarActions{justify-content:flex-start;flex-wrap:wrap}.kvRow{grid-template-columns:1fr;gap:6px}.footer{flex-direction:column;align-items:flex-start}.bgTaskToolCallsGrid{grid-template-columns:1fr}}
|