@tonyclaw/agent-inspector 3.0.14 → 3.0.16
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/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-DDfwHQUi.js → CompareDrawer-BJmxAiM0.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-C40Nig73.js +106 -0
- package/.output/public/assets/{ReplayDialog-D-26DMBt.js → ReplayDialog-CkLdOMJr.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-B6zNFqNX.js → RequestAnatomy-DemOFfSc.js} +1 -1
- package/.output/public/assets/{ResponseView-Ccy95rWI.js → ResponseView-9LhpuDo7.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-yIiscGuE.js → StreamingChunkSequence-RRWYBOdz.js} +1 -1
- package/.output/public/assets/{_sessionId-BnMB0zab.js → _sessionId-CWEh2Jsn.js} +1 -1
- package/.output/public/assets/index-HRfrFVYw.css +1 -0
- package/.output/public/assets/{index-BbtAIxbg.js → index-Lwir70Rs.js} +1 -1
- package/.output/public/assets/index-hjNWJZGE.js +1 -0
- package/.output/public/assets/{json-viewer-C838cVmD.js → json-viewer-D8gvbDlB.js} +1 -1
- package/.output/public/assets/{main-BfN4culo.js → main-ovrz0tyD.js} +7 -7
- package/.output/server/_libs/lucide-react.mjs +215 -201
- package/.output/server/{_sessionId-DoprmwZV.mjs → _sessionId-zI_GFX_7.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-YMLJNxXR.mjs → CompareDrawer-Ckcx1RVu.mjs} +3 -3
- package/.output/server/_ssr/{ProxyViewerContainer-C62eZnva.mjs → ProxyViewerContainer-BgXewlIr.mjs} +729 -378
- package/.output/server/_ssr/{ReplayDialog-NmN7Ukjo.mjs → ReplayDialog-wx3nDtZn.mjs} +4 -4
- package/.output/server/_ssr/{RequestAnatomy-Dawm2qxe.mjs → RequestAnatomy-DN80zt0G.mjs} +3 -3
- package/.output/server/_ssr/{ResponseView-BHyAY83t.mjs → ResponseView-DD7rTu_l.mjs} +3 -3
- package/.output/server/_ssr/{StreamingChunkSequence-C4qgcJbg.mjs → StreamingChunkSequence-DAgz6Vvw.mjs} +3 -3
- package/.output/server/_ssr/{index-B2Pf6W_T.mjs → index-dcTjp8du.mjs} +14 -5
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-u67BASf8.mjs → json-viewer-N2cuzGWR.mjs} +3 -3
- package/.output/server/_ssr/{router-CZhFVMGN.mjs → router-BfQ0QsD5.mjs} +132 -17
- package/.output/server/{_tanstack-start-manifest_v-Dk2HfOoe.mjs → _tanstack-start-manifest_v-BXnYrbcJ.mjs} +1 -1
- package/.output/server/index.mjs +75 -75
- package/package.json +1 -1
- package/src/components/ProxyViewer.tsx +475 -197
- package/src/components/ProxyViewerContainer.tsx +145 -17
- package/src/components/proxy-viewer/ToolTraceEvents.tsx +36 -10
- package/src/components/proxy-viewer/TurnGroup.tsx +4 -1
- package/src/lib/chunkLoadRecovery.ts +83 -0
- package/src/proxy/formats/openai/codexMetadata.ts +61 -0
- package/src/proxy/formats/openai/handler.ts +8 -2
- package/src/routes/__root.tsx +6 -3
- package/src/routes/index.tsx +25 -1
- package/src/routes/session/$sessionId.tsx +1 -0
- package/styles/globals.css +54 -0
- package/.output/public/assets/ProxyViewerContainer-CbNsaoQw.js +0 -106
- package/.output/public/assets/index-49RC4dlD.js +0 -1
- package/.output/public/assets/index-DXKupW0r.css +0 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ChangeEvent,
|
|
3
|
+
type CSSProperties,
|
|
3
4
|
type JSX,
|
|
5
|
+
type PointerEvent as ReactPointerEvent,
|
|
4
6
|
type ReactNode,
|
|
5
7
|
useCallback,
|
|
6
8
|
useEffect,
|
|
@@ -26,6 +28,8 @@ import {
|
|
|
26
28
|
FileJson,
|
|
27
29
|
Gauge,
|
|
28
30
|
Flag,
|
|
31
|
+
PanelRightClose,
|
|
32
|
+
PanelRightOpen,
|
|
29
33
|
Plus,
|
|
30
34
|
RefreshCw,
|
|
31
35
|
Search,
|
|
@@ -1381,6 +1385,291 @@ function BrandHeader({
|
|
|
1381
1385
|
);
|
|
1382
1386
|
}
|
|
1383
1387
|
|
|
1388
|
+
type FloatingLogoPosition = {
|
|
1389
|
+
x: number;
|
|
1390
|
+
y: number;
|
|
1391
|
+
};
|
|
1392
|
+
|
|
1393
|
+
type FloatingLogoDragState = {
|
|
1394
|
+
pointerId: number;
|
|
1395
|
+
offsetX: number;
|
|
1396
|
+
offsetY: number;
|
|
1397
|
+
};
|
|
1398
|
+
|
|
1399
|
+
type FloatingLogoEdge = "left" | "right" | "top" | "bottom";
|
|
1400
|
+
|
|
1401
|
+
const FLOATING_LOGO_SIZE = 56;
|
|
1402
|
+
const FLOATING_LOGO_MARGIN = 14;
|
|
1403
|
+
const FLOATING_LOGO_MIN_DRIFT_MS = 5200;
|
|
1404
|
+
const FLOATING_LOGO_DRIFT_WINDOW_MS = 2400;
|
|
1405
|
+
const FLOATING_LOGO_MIN_STEP = 72;
|
|
1406
|
+
const FLOATING_LOGO_MAX_STEP = 156;
|
|
1407
|
+
const DETAILS_PANEL_COLLAPSED_STORAGE_KEY = "agent-inspector.detailsPanelCollapsed";
|
|
1408
|
+
|
|
1409
|
+
function readStoredDetailsPanelCollapsed(): boolean {
|
|
1410
|
+
if (typeof window === "undefined") return true;
|
|
1411
|
+
const stored = window.localStorage.getItem(DETAILS_PANEL_COLLAPSED_STORAGE_KEY);
|
|
1412
|
+
return stored === null || stored === "1";
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
function clampFloatingLogoPosition(position: FloatingLogoPosition): FloatingLogoPosition {
|
|
1416
|
+
const maxX = Math.max(
|
|
1417
|
+
FLOATING_LOGO_MARGIN,
|
|
1418
|
+
window.innerWidth - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN,
|
|
1419
|
+
);
|
|
1420
|
+
const maxY = Math.max(
|
|
1421
|
+
FLOATING_LOGO_MARGIN,
|
|
1422
|
+
window.innerHeight - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN,
|
|
1423
|
+
);
|
|
1424
|
+
return {
|
|
1425
|
+
x: Math.min(Math.max(position.x, FLOATING_LOGO_MARGIN), maxX),
|
|
1426
|
+
y: Math.min(Math.max(position.y, FLOATING_LOGO_MARGIN), maxY),
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
function snapFloatingLogoToEdge(position: FloatingLogoPosition): FloatingLogoPosition {
|
|
1431
|
+
const clamped = clampFloatingLogoPosition(position);
|
|
1432
|
+
const maxX = Math.max(
|
|
1433
|
+
FLOATING_LOGO_MARGIN,
|
|
1434
|
+
window.innerWidth - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN,
|
|
1435
|
+
);
|
|
1436
|
+
const maxY = Math.max(
|
|
1437
|
+
FLOATING_LOGO_MARGIN,
|
|
1438
|
+
window.innerHeight - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN,
|
|
1439
|
+
);
|
|
1440
|
+
const leftDistance = clamped.x - FLOATING_LOGO_MARGIN;
|
|
1441
|
+
const rightDistance = maxX - clamped.x;
|
|
1442
|
+
const topDistance = clamped.y - FLOATING_LOGO_MARGIN;
|
|
1443
|
+
const bottomDistance = maxY - clamped.y;
|
|
1444
|
+
const nearestDistance = Math.min(leftDistance, rightDistance, topDistance, bottomDistance);
|
|
1445
|
+
|
|
1446
|
+
if (nearestDistance === leftDistance) return { x: FLOATING_LOGO_MARGIN, y: clamped.y };
|
|
1447
|
+
if (nearestDistance === rightDistance) return { x: maxX, y: clamped.y };
|
|
1448
|
+
if (nearestDistance === topDistance) return { x: clamped.x, y: FLOATING_LOGO_MARGIN };
|
|
1449
|
+
return { x: clamped.x, y: maxY };
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
function randomFloatingLogoEdgePosition(): FloatingLogoPosition {
|
|
1453
|
+
const maxX = Math.max(
|
|
1454
|
+
FLOATING_LOGO_MARGIN,
|
|
1455
|
+
window.innerWidth - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN,
|
|
1456
|
+
);
|
|
1457
|
+
const maxY = Math.max(
|
|
1458
|
+
FLOATING_LOGO_MARGIN,
|
|
1459
|
+
window.innerHeight - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN,
|
|
1460
|
+
);
|
|
1461
|
+
const randomX = FLOATING_LOGO_MARGIN + Math.random() * (maxX - FLOATING_LOGO_MARGIN);
|
|
1462
|
+
const randomY = FLOATING_LOGO_MARGIN + Math.random() * (maxY - FLOATING_LOGO_MARGIN);
|
|
1463
|
+
const edgeIndex = Math.floor(Math.random() * 4);
|
|
1464
|
+
|
|
1465
|
+
switch (edgeIndex) {
|
|
1466
|
+
case 0:
|
|
1467
|
+
return { x: FLOATING_LOGO_MARGIN, y: randomY };
|
|
1468
|
+
case 1:
|
|
1469
|
+
return { x: maxX, y: randomY };
|
|
1470
|
+
case 2:
|
|
1471
|
+
return { x: randomX, y: FLOATING_LOGO_MARGIN };
|
|
1472
|
+
case 3:
|
|
1473
|
+
return { x: randomX, y: maxY };
|
|
1474
|
+
default:
|
|
1475
|
+
return { x: maxX, y: FLOATING_LOGO_MARGIN };
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
function nearestFloatingLogoEdge(position: FloatingLogoPosition): FloatingLogoEdge {
|
|
1480
|
+
const clamped = clampFloatingLogoPosition(position);
|
|
1481
|
+
const maxX = Math.max(
|
|
1482
|
+
FLOATING_LOGO_MARGIN,
|
|
1483
|
+
window.innerWidth - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN,
|
|
1484
|
+
);
|
|
1485
|
+
const maxY = Math.max(
|
|
1486
|
+
FLOATING_LOGO_MARGIN,
|
|
1487
|
+
window.innerHeight - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN,
|
|
1488
|
+
);
|
|
1489
|
+
const distances: { edge: FloatingLogoEdge; distance: number }[] = [
|
|
1490
|
+
{ edge: "left", distance: clamped.x - FLOATING_LOGO_MARGIN },
|
|
1491
|
+
{ edge: "right", distance: maxX - clamped.x },
|
|
1492
|
+
{ edge: "top", distance: clamped.y - FLOATING_LOGO_MARGIN },
|
|
1493
|
+
{ edge: "bottom", distance: maxY - clamped.y },
|
|
1494
|
+
];
|
|
1495
|
+
return distances.reduce((best, current) => (current.distance < best.distance ? current : best))
|
|
1496
|
+
.edge;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
function nextFloatingLogoWalkingPosition(position: FloatingLogoPosition): FloatingLogoPosition {
|
|
1500
|
+
const clamped = snapFloatingLogoToEdge(position);
|
|
1501
|
+
const maxX = Math.max(
|
|
1502
|
+
FLOATING_LOGO_MARGIN,
|
|
1503
|
+
window.innerWidth - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN,
|
|
1504
|
+
);
|
|
1505
|
+
const maxY = Math.max(
|
|
1506
|
+
FLOATING_LOGO_MARGIN,
|
|
1507
|
+
window.innerHeight - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN,
|
|
1508
|
+
);
|
|
1509
|
+
const step =
|
|
1510
|
+
FLOATING_LOGO_MIN_STEP + Math.random() * (FLOATING_LOGO_MAX_STEP - FLOATING_LOGO_MIN_STEP);
|
|
1511
|
+
const direction = Math.random() > 0.5 ? 1 : -1;
|
|
1512
|
+
const edge = nearestFloatingLogoEdge(clamped);
|
|
1513
|
+
|
|
1514
|
+
switch (edge) {
|
|
1515
|
+
case "left": {
|
|
1516
|
+
const nextY = clamped.y + step * direction;
|
|
1517
|
+
if (nextY <= FLOATING_LOGO_MARGIN)
|
|
1518
|
+
return { x: FLOATING_LOGO_MARGIN, y: FLOATING_LOGO_MARGIN };
|
|
1519
|
+
if (nextY >= maxY) return { x: FLOATING_LOGO_MARGIN, y: maxY };
|
|
1520
|
+
return { x: FLOATING_LOGO_MARGIN, y: nextY };
|
|
1521
|
+
}
|
|
1522
|
+
case "right": {
|
|
1523
|
+
const nextY = clamped.y + step * direction;
|
|
1524
|
+
if (nextY <= FLOATING_LOGO_MARGIN) return { x: maxX, y: FLOATING_LOGO_MARGIN };
|
|
1525
|
+
if (nextY >= maxY) return { x: maxX, y: maxY };
|
|
1526
|
+
return { x: maxX, y: nextY };
|
|
1527
|
+
}
|
|
1528
|
+
case "top": {
|
|
1529
|
+
const nextX = clamped.x + step * direction;
|
|
1530
|
+
if (nextX <= FLOATING_LOGO_MARGIN)
|
|
1531
|
+
return { x: FLOATING_LOGO_MARGIN, y: FLOATING_LOGO_MARGIN };
|
|
1532
|
+
if (nextX >= maxX) return { x: maxX, y: FLOATING_LOGO_MARGIN };
|
|
1533
|
+
return { x: nextX, y: FLOATING_LOGO_MARGIN };
|
|
1534
|
+
}
|
|
1535
|
+
case "bottom": {
|
|
1536
|
+
const nextX = clamped.x + step * direction;
|
|
1537
|
+
if (nextX <= FLOATING_LOGO_MARGIN) return { x: FLOATING_LOGO_MARGIN, y: maxY };
|
|
1538
|
+
if (nextX >= maxX) return { x: maxX, y: maxY };
|
|
1539
|
+
return { x: nextX, y: maxY };
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
function FloatingInspectorLogo(): JSX.Element {
|
|
1545
|
+
const [position, setPosition] = useState<FloatingLogoPosition>({
|
|
1546
|
+
x: FLOATING_LOGO_MARGIN,
|
|
1547
|
+
y: FLOATING_LOGO_MARGIN,
|
|
1548
|
+
});
|
|
1549
|
+
const [motionTick, setMotionTick] = useState(0);
|
|
1550
|
+
const [dragging, setDragging] = useState(false);
|
|
1551
|
+
const dragStateRef = useRef<FloatingLogoDragState | null>(null);
|
|
1552
|
+
|
|
1553
|
+
useEffect(() => {
|
|
1554
|
+
setPosition(randomFloatingLogoEdgePosition());
|
|
1555
|
+
}, []);
|
|
1556
|
+
|
|
1557
|
+
useEffect(() => {
|
|
1558
|
+
if (dragging) return undefined;
|
|
1559
|
+
let timeoutId: number | null = null;
|
|
1560
|
+
|
|
1561
|
+
const scheduleDrift = () => {
|
|
1562
|
+
const delay = FLOATING_LOGO_MIN_DRIFT_MS + Math.random() * FLOATING_LOGO_DRIFT_WINDOW_MS;
|
|
1563
|
+
timeoutId = window.setTimeout(() => {
|
|
1564
|
+
setMotionTick((current) => current + 1);
|
|
1565
|
+
setPosition((current) => nextFloatingLogoWalkingPosition(current));
|
|
1566
|
+
scheduleDrift();
|
|
1567
|
+
}, delay);
|
|
1568
|
+
};
|
|
1569
|
+
|
|
1570
|
+
scheduleDrift();
|
|
1571
|
+
return () => {
|
|
1572
|
+
if (timeoutId !== null) {
|
|
1573
|
+
window.clearTimeout(timeoutId);
|
|
1574
|
+
}
|
|
1575
|
+
};
|
|
1576
|
+
}, [dragging]);
|
|
1577
|
+
|
|
1578
|
+
useEffect(() => {
|
|
1579
|
+
const handleResize = () => {
|
|
1580
|
+
setPosition((current) => snapFloatingLogoToEdge(current));
|
|
1581
|
+
};
|
|
1582
|
+
window.addEventListener("resize", handleResize);
|
|
1583
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
1584
|
+
}, []);
|
|
1585
|
+
|
|
1586
|
+
const handlePointerDown = useCallback((event: ReactPointerEvent<HTMLButtonElement>) => {
|
|
1587
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
1588
|
+
dragStateRef.current = {
|
|
1589
|
+
pointerId: event.pointerId,
|
|
1590
|
+
offsetX: event.clientX - rect.left,
|
|
1591
|
+
offsetY: event.clientY - rect.top,
|
|
1592
|
+
};
|
|
1593
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
1594
|
+
setDragging(true);
|
|
1595
|
+
}, []);
|
|
1596
|
+
|
|
1597
|
+
const handlePointerMove = useCallback((event: ReactPointerEvent<HTMLButtonElement>) => {
|
|
1598
|
+
const dragState = dragStateRef.current;
|
|
1599
|
+
if (dragState === null || dragState.pointerId !== event.pointerId) return;
|
|
1600
|
+
setPosition(
|
|
1601
|
+
clampFloatingLogoPosition({
|
|
1602
|
+
x: event.clientX - dragState.offsetX,
|
|
1603
|
+
y: event.clientY - dragState.offsetY,
|
|
1604
|
+
}),
|
|
1605
|
+
);
|
|
1606
|
+
}, []);
|
|
1607
|
+
|
|
1608
|
+
const handlePointerUp = useCallback((event: ReactPointerEvent<HTMLButtonElement>) => {
|
|
1609
|
+
const dragState = dragStateRef.current;
|
|
1610
|
+
if (dragState === null || dragState.pointerId !== event.pointerId) return;
|
|
1611
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) {
|
|
1612
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
1613
|
+
}
|
|
1614
|
+
dragStateRef.current = null;
|
|
1615
|
+
setDragging(false);
|
|
1616
|
+
setPosition((current) => snapFloatingLogoToEdge(current));
|
|
1617
|
+
}, []);
|
|
1618
|
+
|
|
1619
|
+
const style = useMemo<CSSProperties>(
|
|
1620
|
+
() => ({
|
|
1621
|
+
transform: `translate3d(${position.x}px, ${position.y}px, 0) rotate(${
|
|
1622
|
+
motionTick % 2 === 0 ? "-4deg" : "5deg"
|
|
1623
|
+
}) scale(${dragging ? "1.08" : "1"})`,
|
|
1624
|
+
transition: dragging ? "none" : "transform 4800ms cubic-bezier(0.33, 1, 0.68, 1)",
|
|
1625
|
+
touchAction: "none",
|
|
1626
|
+
}),
|
|
1627
|
+
[dragging, motionTick, position.x, position.y],
|
|
1628
|
+
);
|
|
1629
|
+
|
|
1630
|
+
return (
|
|
1631
|
+
<button
|
|
1632
|
+
type="button"
|
|
1633
|
+
className={cn(
|
|
1634
|
+
"fixed left-0 top-0 z-40 flex size-14 items-center justify-center rounded-full bg-transparent text-foreground outline-none transition-[filter,opacity] duration-200 hover:opacity-95 hover:drop-shadow-[0_0_24px_rgba(245,158,11,0.36)] focus-visible:ring-1 focus-visible:ring-amber-300/55",
|
|
1635
|
+
dragging ? "cursor-grabbing" : "cursor-grab",
|
|
1636
|
+
)}
|
|
1637
|
+
style={style}
|
|
1638
|
+
onPointerDown={handlePointerDown}
|
|
1639
|
+
onPointerMove={handlePointerMove}
|
|
1640
|
+
onPointerUp={handlePointerUp}
|
|
1641
|
+
onPointerCancel={handlePointerUp}
|
|
1642
|
+
aria-label="Floating Inspector logo"
|
|
1643
|
+
title="Drag Inspector logo"
|
|
1644
|
+
>
|
|
1645
|
+
<span
|
|
1646
|
+
className={cn(
|
|
1647
|
+
"relative flex size-12 items-center justify-center",
|
|
1648
|
+
!dragging && "floating-crab-body",
|
|
1649
|
+
)}
|
|
1650
|
+
aria-hidden="true"
|
|
1651
|
+
>
|
|
1652
|
+
<CrabLogo className="relative z-10 size-11 text-amber-500 drop-shadow-[0_0_15px_rgba(245,158,11,0.30)]" />
|
|
1653
|
+
<span className="pointer-events-none absolute inset-x-0 bottom-0 z-0 flex items-end justify-center gap-1 text-amber-400/80">
|
|
1654
|
+
{[0, 1, 2, 3, 4, 5].map((leg) => (
|
|
1655
|
+
<span
|
|
1656
|
+
key={leg}
|
|
1657
|
+
className={cn(
|
|
1658
|
+
"floating-crab-leg h-2 w-0.5 rounded-full bg-current shadow-[0_0_7px_rgba(245,158,11,0.24)]",
|
|
1659
|
+
leg < 3 ? "floating-crab-leg-left" : "floating-crab-leg-right",
|
|
1660
|
+
dragging && "animate-none",
|
|
1661
|
+
)}
|
|
1662
|
+
style={{
|
|
1663
|
+
animationDelay: `${leg % 2 === 0 ? 0 : 180}ms`,
|
|
1664
|
+
}}
|
|
1665
|
+
/>
|
|
1666
|
+
))}
|
|
1667
|
+
</span>
|
|
1668
|
+
</span>
|
|
1669
|
+
</button>
|
|
1670
|
+
);
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1384
1673
|
function SessionContextLogButton({ label, logId }: { label: string; logId: number }): JSX.Element {
|
|
1385
1674
|
return (
|
|
1386
1675
|
<button
|
|
@@ -1697,8 +1986,8 @@ export type ProxyViewerProps = {
|
|
|
1697
1986
|
onSessionChange: (session: string) => void;
|
|
1698
1987
|
onModelChange: (model: string) => void;
|
|
1699
1988
|
onClearAll: () => void;
|
|
1700
|
-
/** Delete
|
|
1701
|
-
onDeleteCurrentView?: (
|
|
1989
|
+
/** Delete persisted storage for the selected session, or all storage from the all-sessions view. */
|
|
1990
|
+
onDeleteCurrentView?: () => Promise<void>;
|
|
1702
1991
|
/** Reload the selected/pinned session from stored logs without requiring a page refresh. */
|
|
1703
1992
|
onReloadSession?: () => void;
|
|
1704
1993
|
/** Clear only the logs whose ids are passed. Called by the per-group
|
|
@@ -1765,8 +2054,9 @@ export function ProxyViewer({
|
|
|
1765
2054
|
const [importStatus, setImportStatus] = useState<string | null>(null);
|
|
1766
2055
|
const [importError, setImportError] = useState<string | null>(null);
|
|
1767
2056
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
1768
|
-
const [deleteStoredData, setDeleteStoredData] = useState(false);
|
|
1769
2057
|
const [deletingCurrentView, setDeletingCurrentView] = useState(false);
|
|
2058
|
+
const [detailsPanelCollapsed, setDetailsPanelCollapsed] = useState(true);
|
|
2059
|
+
const [detailsPanelStorageLoaded, setDetailsPanelStorageLoaded] = useState(false);
|
|
1770
2060
|
const [comparePair, setComparePair] = useState<[CapturedLog, CapturedLog] | null>(null);
|
|
1771
2061
|
const [crabEntrancePhase, setCrabEntrancePhase] = useState<"hidden" | "playing" | "done">(
|
|
1772
2062
|
"hidden",
|
|
@@ -1807,6 +2097,19 @@ export function ProxyViewer({
|
|
|
1807
2097
|
};
|
|
1808
2098
|
}, []);
|
|
1809
2099
|
|
|
2100
|
+
useEffect(() => {
|
|
2101
|
+
setDetailsPanelCollapsed(readStoredDetailsPanelCollapsed());
|
|
2102
|
+
setDetailsPanelStorageLoaded(true);
|
|
2103
|
+
}, []);
|
|
2104
|
+
|
|
2105
|
+
useEffect(() => {
|
|
2106
|
+
if (!detailsPanelStorageLoaded) return;
|
|
2107
|
+
window.localStorage.setItem(
|
|
2108
|
+
DETAILS_PANEL_COLLAPSED_STORAGE_KEY,
|
|
2109
|
+
detailsPanelCollapsed ? "1" : "0",
|
|
2110
|
+
);
|
|
2111
|
+
}, [detailsPanelCollapsed, detailsPanelStorageLoaded]);
|
|
2112
|
+
|
|
1810
2113
|
useEffect(() => {
|
|
1811
2114
|
if (pinnedSessionId === undefined) {
|
|
1812
2115
|
document.title = "Agent Inspector";
|
|
@@ -1898,7 +2201,6 @@ export function ProxyViewer({
|
|
|
1898
2201
|
}, [cancelTransientTasks, onClearAll]);
|
|
1899
2202
|
|
|
1900
2203
|
const handleRequestDeleteCurrentView = useCallback(() => {
|
|
1901
|
-
setDeleteStoredData(false);
|
|
1902
2204
|
setDeleteDialogOpen(true);
|
|
1903
2205
|
}, []);
|
|
1904
2206
|
|
|
@@ -1908,14 +2210,10 @@ export function ProxyViewer({
|
|
|
1908
2210
|
setImportError(null);
|
|
1909
2211
|
setComparePair(null);
|
|
1910
2212
|
|
|
1911
|
-
if (
|
|
1912
|
-
setDeleteDialogOpen(false);
|
|
1913
|
-
onClearAll();
|
|
1914
|
-
return;
|
|
1915
|
-
}
|
|
2213
|
+
if (onDeleteCurrentView === undefined) return;
|
|
1916
2214
|
|
|
1917
2215
|
setDeletingCurrentView(true);
|
|
1918
|
-
void onDeleteCurrentView(
|
|
2216
|
+
void onDeleteCurrentView()
|
|
1919
2217
|
.then(() => {
|
|
1920
2218
|
setDeleteDialogOpen(false);
|
|
1921
2219
|
})
|
|
@@ -1930,7 +2228,7 @@ export function ProxyViewer({
|
|
|
1930
2228
|
.finally(() => {
|
|
1931
2229
|
setDeletingCurrentView(false);
|
|
1932
2230
|
});
|
|
1933
|
-
}, [cancelTransientTasks,
|
|
2231
|
+
}, [cancelTransientTasks, onDeleteCurrentView, onNotify]);
|
|
1934
2232
|
|
|
1935
2233
|
const handleReloadCurrentSession = useCallback(() => {
|
|
1936
2234
|
if (onReloadSession === undefined) return;
|
|
@@ -1945,6 +2243,10 @@ export function ProxyViewer({
|
|
|
1945
2243
|
setComparePair(null);
|
|
1946
2244
|
}, []);
|
|
1947
2245
|
|
|
2246
|
+
const toggleDetailsPanelCollapsed = useCallback(() => {
|
|
2247
|
+
setDetailsPanelCollapsed((value) => !value);
|
|
2248
|
+
}, []);
|
|
2249
|
+
|
|
1948
2250
|
const groups = useMemo(() => groupLogsByConversation(logs), [logs]);
|
|
1949
2251
|
const displayNumberByLogId = useMemo(() => buildDisplayNumberByLogId(groups), [groups]);
|
|
1950
2252
|
const sessionContextScope = useMemo(
|
|
@@ -1966,6 +2268,13 @@ export function ProxyViewer({
|
|
|
1966
2268
|
const exportActionVisibility = getExportActionVisibility(captureMode, logs.length);
|
|
1967
2269
|
const isPinnedSessionPage = pinnedSessionId !== undefined;
|
|
1968
2270
|
const canClearCurrentView = logs.length > 0;
|
|
2271
|
+
const canDeleteCurrentView =
|
|
2272
|
+
onDeleteCurrentView !== undefined && (logs.length > 0 || selectedSession !== "__all__");
|
|
2273
|
+
const deleteTargetLabel = selectedSession === "__all__" ? "all sessions" : "this session";
|
|
2274
|
+
const deleteTargetDescription =
|
|
2275
|
+
selectedSession === "__all__"
|
|
2276
|
+
? "Deletes every stored log, streaming chunk, and session archive entry across all sessions."
|
|
2277
|
+
: "Deletes every stored log, streaming chunk, and session archive entry for this session, including history that is not currently loaded.";
|
|
1969
2278
|
const reloadSessionLabel = logs.length === 0 ? "Load" : "Reload";
|
|
1970
2279
|
const sessionInputValue = selectedSession === "__all__" ? "" : selectedSession;
|
|
1971
2280
|
const sessionSelectValue =
|
|
@@ -2019,173 +2328,159 @@ export function ProxyViewer({
|
|
|
2019
2328
|
|
|
2020
2329
|
return (
|
|
2021
2330
|
<div className="bg-background min-h-screen w-full text-foreground">
|
|
2022
|
-
<
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2331
|
+
<button
|
|
2332
|
+
type="button"
|
|
2333
|
+
onClick={toggleDetailsPanelCollapsed}
|
|
2334
|
+
className={cn(
|
|
2335
|
+
"group fixed top-1/2 right-3 z-40 inline-flex h-14 w-7 -translate-y-1/2 items-center justify-center rounded-l-md border border-white/[0.08] bg-background/90 text-muted-foreground shadow-[0_12px_34px_rgba(0,0,0,0.26),inset_0_1px_0_rgba(255,255,255,0.06)] backdrop-blur transition-[right,background-color,color,border-color] duration-200 hover:border-white/[0.16] hover:bg-white/[0.07] hover:text-foreground focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none",
|
|
2336
|
+
detailsPanelCollapsed ? "right-3" : "lg:right-[326px] xl:right-[346px]",
|
|
2337
|
+
)}
|
|
2338
|
+
aria-label={detailsPanelCollapsed ? "Show details panel" : "Hide details panel"}
|
|
2339
|
+
title={detailsPanelCollapsed ? "Show details panel" : "Hide details panel"}
|
|
2340
|
+
>
|
|
2341
|
+
<span className="absolute left-1 top-2 bottom-2 w-px rounded-full bg-white/[0.16] transition-colors group-hover:bg-white/[0.28]" />
|
|
2342
|
+
{detailsPanelCollapsed ? (
|
|
2343
|
+
<PanelRightOpen className="size-3.5" />
|
|
2344
|
+
) : (
|
|
2345
|
+
<PanelRightClose className="size-3.5" />
|
|
2346
|
+
)}
|
|
2347
|
+
</button>
|
|
2348
|
+
{detailsPanelCollapsed && <FloatingInspectorLogo />}
|
|
2349
|
+
<div
|
|
2350
|
+
className={cn(
|
|
2351
|
+
"grid min-h-screen w-full grid-cols-1",
|
|
2352
|
+
detailsPanelCollapsed
|
|
2353
|
+
? "lg:grid-cols-[minmax(0,1fr)]"
|
|
2354
|
+
: "lg:grid-cols-[minmax(0,1fr)_340px] xl:grid-cols-[minmax(0,1fr)_360px]",
|
|
2355
|
+
)}
|
|
2356
|
+
>
|
|
2357
|
+
{!detailsPanelCollapsed && (
|
|
2358
|
+
<aside className="bg-background/95 order-1 border-b border-white/[0.045] lg:sticky lg:top-0 lg:order-2 lg:h-screen lg:overflow-y-auto lg:border-b-0 lg:border-l lg:border-white/[0.055]">
|
|
2359
|
+
<div className="space-y-4 p-4">
|
|
2360
|
+
<BrandHeader
|
|
2361
|
+
compact={true}
|
|
2362
|
+
crabEntrancePhase={crabEntrancePhase}
|
|
2363
|
+
currentSessionId={labSessionId}
|
|
2364
|
+
logCount={logs.length}
|
|
2365
|
+
/>
|
|
2031
2366
|
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
onClick={handleReloadCurrentSession}
|
|
2050
|
-
disabled={isLoading}
|
|
2051
|
-
className="bg-white/[0.035] hover:bg-white/[0.065] inline-flex h-8 min-w-0 items-center justify-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
|
|
2052
|
-
title="Load stored logs for this session"
|
|
2053
|
-
>
|
|
2054
|
-
<RefreshCw className={cn("size-3.5", isLoading && "animate-spin")} />
|
|
2055
|
-
<span>{reloadSessionLabel}</span>
|
|
2056
|
-
</button>
|
|
2367
|
+
<SidebarPanel label="Session">
|
|
2368
|
+
<div className="grid grid-cols-2 gap-2">
|
|
2369
|
+
<SidebarMetric label="Requests" value={logs.length} />
|
|
2370
|
+
<SidebarMetric label="Sessions" value={sessions.length} />
|
|
2371
|
+
<SidebarMetric
|
|
2372
|
+
label="Input"
|
|
2373
|
+
value={formatTokens(hasSessionContext ? sessionContextTotalIn : totalIn)}
|
|
2374
|
+
/>
|
|
2375
|
+
<SidebarMetric
|
|
2376
|
+
label="Output"
|
|
2377
|
+
value={formatTokens(hasSessionContext ? sessionContextTotalOut : totalOut)}
|
|
2378
|
+
/>
|
|
2379
|
+
</div>
|
|
2380
|
+
{sessionLoadProgress !== undefined && (
|
|
2381
|
+
<div className="mt-2 rounded-md bg-black/20 px-2 py-2 shadow-[inset_0_0_0_1px_rgba(255,255,255,0.045)]">
|
|
2382
|
+
<SessionLoadProgressBar progress={sessionLoadProgress} />
|
|
2383
|
+
</div>
|
|
2057
2384
|
)}
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
"
|
|
2064
|
-
|
|
2065
|
-
onDeleteCurrentView === undefined &&
|
|
2066
|
-
"col-span-2",
|
|
2067
|
-
)}
|
|
2068
|
-
title="Clear the current view. Stored logs remain reloadable."
|
|
2069
|
-
>
|
|
2070
|
-
<Trash2 className="size-3.5" />
|
|
2071
|
-
<span>Clear</span>
|
|
2072
|
-
</button>
|
|
2073
|
-
{onDeleteCurrentView !== undefined && (
|
|
2074
|
-
<button
|
|
2075
|
-
type="button"
|
|
2076
|
-
onClick={handleRequestDeleteCurrentView}
|
|
2077
|
-
disabled={!canClearCurrentView || deletingCurrentView}
|
|
2078
|
-
className="bg-red-500/[0.07] hover:bg-red-500/[0.12] inline-flex h-8 min-w-0 items-center justify-center gap-1.5 rounded-md px-2 text-xs text-red-200 shadow-[inset_0_0_0_1px_rgba(248,113,113,0.14)] transition-colors hover:text-red-100 disabled:cursor-not-allowed disabled:opacity-50"
|
|
2079
|
-
title="Choose whether to delete stored data for the current view."
|
|
2385
|
+
</SidebarPanel>
|
|
2386
|
+
|
|
2387
|
+
<SidebarPanel label="Workspace">
|
|
2388
|
+
<div className="bg-black/20 rounded-md px-3 py-2 shadow-[inset_0_1px_0_rgba(255,255,255,0.035)]">
|
|
2389
|
+
<div
|
|
2390
|
+
className="truncate font-mono text-xs font-semibold text-cyan-100"
|
|
2391
|
+
title={activeScopeLabel}
|
|
2080
2392
|
>
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
{sessionLoadProgress !== undefined && (
|
|
2087
|
-
<div className="mt-2 rounded-md bg-black/20 px-2 py-2 shadow-[inset_0_0_0_1px_rgba(255,255,255,0.045)]">
|
|
2088
|
-
<SessionLoadProgressBar progress={sessionLoadProgress} />
|
|
2393
|
+
{activeScopeLabel}
|
|
2394
|
+
</div>
|
|
2395
|
+
<div className="mt-1 text-[11px] text-muted-foreground">
|
|
2396
|
+
{isPinnedSessionPage ? "Session workspace" : "Live workspace"}
|
|
2397
|
+
</div>
|
|
2089
2398
|
</div>
|
|
2399
|
+
</SidebarPanel>
|
|
2400
|
+
|
|
2401
|
+
{hasSessionContext && sessionContextScope !== null && (
|
|
2402
|
+
<SessionContextBar
|
|
2403
|
+
label={sessionContextScope.label}
|
|
2404
|
+
logs={sessionContextLogs}
|
|
2405
|
+
totalIn={sessionContextTotalIn}
|
|
2406
|
+
totalOut={sessionContextTotalOut}
|
|
2407
|
+
timeDisplayFormat={timeDisplayFormat}
|
|
2408
|
+
contextSummary={sessionContextSummary}
|
|
2409
|
+
showBackLink={sessionContextScope.showBackLink}
|
|
2410
|
+
/>
|
|
2090
2411
|
)}
|
|
2091
|
-
</SidebarPanel>
|
|
2092
|
-
|
|
2093
|
-
<SidebarPanel label="Workspace">
|
|
2094
|
-
<div className="bg-black/20 rounded-md px-3 py-2 shadow-[inset_0_1px_0_rgba(255,255,255,0.035)]">
|
|
2095
|
-
<div
|
|
2096
|
-
className="truncate font-mono text-xs font-semibold text-cyan-100"
|
|
2097
|
-
title={activeScopeLabel}
|
|
2098
|
-
>
|
|
2099
|
-
{activeScopeLabel}
|
|
2100
|
-
</div>
|
|
2101
|
-
<div className="mt-1 text-[11px] text-muted-foreground">
|
|
2102
|
-
{isPinnedSessionPage ? "Session workspace" : "Live workspace"}
|
|
2103
|
-
</div>
|
|
2104
|
-
</div>
|
|
2105
|
-
</SidebarPanel>
|
|
2106
|
-
|
|
2107
|
-
{hasSessionContext && sessionContextScope !== null && (
|
|
2108
|
-
<SessionContextBar
|
|
2109
|
-
label={sessionContextScope.label}
|
|
2110
|
-
logs={sessionContextLogs}
|
|
2111
|
-
totalIn={sessionContextTotalIn}
|
|
2112
|
-
totalOut={sessionContextTotalOut}
|
|
2113
|
-
timeDisplayFormat={timeDisplayFormat}
|
|
2114
|
-
contextSummary={sessionContextSummary}
|
|
2115
|
-
showBackLink={sessionContextScope.showBackLink}
|
|
2116
|
-
/>
|
|
2117
|
-
)}
|
|
2118
2412
|
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2413
|
+
{hasSessionContext &&
|
|
2414
|
+
pinnedSessionId !== undefined &&
|
|
2415
|
+
sessionMemberships.length > 0 && (
|
|
2416
|
+
<SessionMembershipPanel memberships={sessionMemberships} />
|
|
2417
|
+
)}
|
|
2124
2418
|
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
)}
|
|
2151
|
-
</div>
|
|
2152
|
-
<Select value={sessionSelectValue} onValueChange={handleSessionSelectChange}>
|
|
2153
|
-
<SelectTrigger className="bg-black/25 hover:bg-white/[0.045] h-9 w-full border-0 text-xs text-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)]">
|
|
2154
|
-
<SelectValue placeholder="All sessions" />
|
|
2155
|
-
</SelectTrigger>
|
|
2156
|
-
<SelectContent>
|
|
2157
|
-
<SelectItem value="__all__">All sessions</SelectItem>
|
|
2158
|
-
{sessionSelectValue === "__typed__" && (
|
|
2159
|
-
<SelectItem value="__typed__">
|
|
2160
|
-
Typed: {truncateSessionId(selectedSession)}
|
|
2161
|
-
</SelectItem>
|
|
2419
|
+
<SidebarPanel label="Filters">
|
|
2420
|
+
<div className="space-y-2">
|
|
2421
|
+
{!hideSessionFilter && (
|
|
2422
|
+
<>
|
|
2423
|
+
<div className="relative">
|
|
2424
|
+
<Search className="pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" />
|
|
2425
|
+
<input
|
|
2426
|
+
type="search"
|
|
2427
|
+
value={sessionInputValue}
|
|
2428
|
+
onChange={handleSessionInputChange}
|
|
2429
|
+
placeholder="Paste session ID"
|
|
2430
|
+
className="bg-black/25 hover:bg-white/[0.045] focus-visible:ring-ring h-9 w-full rounded-md px-8 font-mono text-xs text-foreground outline-none shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors placeholder:font-sans placeholder:text-muted-foreground focus-visible:ring-1"
|
|
2431
|
+
aria-label="Filter by session ID"
|
|
2432
|
+
title="Filter by exact session ID"
|
|
2433
|
+
/>
|
|
2434
|
+
{sessionInputValue !== "" && (
|
|
2435
|
+
<button
|
|
2436
|
+
type="button"
|
|
2437
|
+
onClick={() => onSessionChange("__all__")}
|
|
2438
|
+
className="absolute right-1.5 top-1/2 inline-flex size-6 -translate-y-1/2 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-white/[0.06] hover:text-foreground"
|
|
2439
|
+
aria-label="Clear session filter"
|
|
2440
|
+
title="Clear session filter"
|
|
2441
|
+
>
|
|
2442
|
+
<X className="size-3.5" />
|
|
2443
|
+
</button>
|
|
2162
2444
|
)}
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
</
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2445
|
+
</div>
|
|
2446
|
+
<Select value={sessionSelectValue} onValueChange={handleSessionSelectChange}>
|
|
2447
|
+
<SelectTrigger className="bg-black/25 hover:bg-white/[0.045] h-9 w-full border-0 text-xs text-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)]">
|
|
2448
|
+
<SelectValue placeholder="All sessions" />
|
|
2449
|
+
</SelectTrigger>
|
|
2450
|
+
<SelectContent>
|
|
2451
|
+
<SelectItem value="__all__">All sessions</SelectItem>
|
|
2452
|
+
{sessionSelectValue === "__typed__" && (
|
|
2453
|
+
<SelectItem value="__typed__">
|
|
2454
|
+
Typed: {truncateSessionId(selectedSession)}
|
|
2455
|
+
</SelectItem>
|
|
2456
|
+
)}
|
|
2457
|
+
{sessions.map((s) => (
|
|
2458
|
+
<SelectItem key={s} value={s}>
|
|
2459
|
+
{truncateSessionId(s)}
|
|
2460
|
+
</SelectItem>
|
|
2461
|
+
))}
|
|
2462
|
+
</SelectContent>
|
|
2463
|
+
</Select>
|
|
2464
|
+
</>
|
|
2465
|
+
)}
|
|
2466
|
+
<Select value={selectedModel} onValueChange={onModelChange}>
|
|
2467
|
+
<SelectTrigger className="bg-black/25 hover:bg-white/[0.045] h-9 w-full border-0 text-xs text-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)]">
|
|
2468
|
+
<SelectValue placeholder="All models" />
|
|
2469
|
+
</SelectTrigger>
|
|
2470
|
+
<SelectContent>
|
|
2471
|
+
<SelectItem value="__all__">All models</SelectItem>
|
|
2472
|
+
{models.map((m) => (
|
|
2473
|
+
<SelectItem key={m} value={m}>
|
|
2474
|
+
{m}
|
|
2475
|
+
</SelectItem>
|
|
2476
|
+
))}
|
|
2477
|
+
</SelectContent>
|
|
2478
|
+
</Select>
|
|
2479
|
+
</div>
|
|
2480
|
+
</SidebarPanel>
|
|
2481
|
+
</div>
|
|
2482
|
+
</aside>
|
|
2483
|
+
)}
|
|
2189
2484
|
|
|
2190
2485
|
<main className="order-2 min-w-0 lg:order-1">
|
|
2191
2486
|
<div className="bg-background/95 shadow-[0_12px_36px_rgba(0,0,0,0.18),inset_0_-1px_0_rgba(255,255,255,0.045)] sticky top-0 z-30 px-4 py-3 lg:px-5">
|
|
@@ -2289,9 +2584,9 @@ export function ProxyViewer({
|
|
|
2289
2584
|
<button
|
|
2290
2585
|
type="button"
|
|
2291
2586
|
onClick={handleRequestDeleteCurrentView}
|
|
2292
|
-
disabled={!
|
|
2587
|
+
disabled={!canDeleteCurrentView || deletingCurrentView}
|
|
2293
2588
|
className="bg-red-500/[0.07] hover:bg-red-500/[0.12] inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-red-200 shadow-[inset_0_0_0_1px_rgba(248,113,113,0.14)] transition-colors hover:text-red-100 disabled:cursor-not-allowed disabled:opacity-50"
|
|
2294
|
-
title="
|
|
2589
|
+
title="Delete persisted history for the selected session."
|
|
2295
2590
|
>
|
|
2296
2591
|
<Trash2 className="size-3.5" />
|
|
2297
2592
|
<span>Delete</span>
|
|
@@ -2431,28 +2726,15 @@ export function ProxyViewer({
|
|
|
2431
2726
|
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
|
2432
2727
|
<DialogContent className="border-red-500/20 bg-zinc-950 text-zinc-100 sm:max-w-md">
|
|
2433
2728
|
<DialogHeader>
|
|
2434
|
-
<DialogTitle>Delete
|
|
2729
|
+
<DialogTitle>Delete {deleteTargetLabel} history?</DialogTitle>
|
|
2435
2730
|
<DialogDescription>
|
|
2436
|
-
|
|
2437
|
-
current view.
|
|
2731
|
+
Clear only hides logs from the UI. Delete removes stored history from disk.
|
|
2438
2732
|
</DialogDescription>
|
|
2439
2733
|
</DialogHeader>
|
|
2440
|
-
<
|
|
2441
|
-
<
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
checked={deleteStoredData}
|
|
2445
|
-
disabled={deletingCurrentView}
|
|
2446
|
-
onChange={(event) => setDeleteStoredData(event.currentTarget.checked)}
|
|
2447
|
-
/>
|
|
2448
|
-
<span className="min-w-0">
|
|
2449
|
-
<span className="block font-medium">Also delete stored data</span>
|
|
2450
|
-
<span className="mt-1 block text-xs text-red-200/80">
|
|
2451
|
-
Removes persisted log rows, streaming chunks, and session archive entries for these{" "}
|
|
2452
|
-
{logs.length.toString()} visible request{logs.length === 1 ? "" : "s"}.
|
|
2453
|
-
</span>
|
|
2454
|
-
</span>
|
|
2455
|
-
</label>
|
|
2734
|
+
<div className="rounded-md bg-red-500/[0.08] px-3 py-3 text-sm text-red-100 shadow-[inset_0_0_0_1px_rgba(248,113,113,0.16)]">
|
|
2735
|
+
<span className="block font-medium">This is a full history delete.</span>
|
|
2736
|
+
<span className="mt-1 block text-xs text-red-200/80">{deleteTargetDescription}</span>
|
|
2737
|
+
</div>
|
|
2456
2738
|
<div className="flex justify-end gap-2">
|
|
2457
2739
|
<Button
|
|
2458
2740
|
type="button"
|
|
@@ -2465,16 +2747,12 @@ export function ProxyViewer({
|
|
|
2465
2747
|
</Button>
|
|
2466
2748
|
<Button
|
|
2467
2749
|
type="button"
|
|
2468
|
-
variant=
|
|
2750
|
+
variant="destructive"
|
|
2469
2751
|
size="sm"
|
|
2470
2752
|
disabled={deletingCurrentView}
|
|
2471
2753
|
onClick={handleConfirmDeleteCurrentView}
|
|
2472
2754
|
>
|
|
2473
|
-
{deletingCurrentView
|
|
2474
|
-
? "Deleting..."
|
|
2475
|
-
: deleteStoredData
|
|
2476
|
-
? "Delete data"
|
|
2477
|
-
: "Clear only"}
|
|
2755
|
+
{deletingCurrentView ? "Deleting..." : "Delete history"}
|
|
2478
2756
|
</Button>
|
|
2479
2757
|
</div>
|
|
2480
2758
|
</DialogContent>
|