@tonyclaw/agent-inspector 3.0.28 → 3.0.30
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/cli.js +791 -191
- package/.output/nitro.json +1 -1
- package/.output/public/assets/CompareDrawer-B6cXJohL.js +1 -0
- package/.output/public/assets/InspectorPet-mAzeGmEE.js +4108 -0
- package/.output/public/assets/ProxyViewerContainer-CkFWv_Nm.js +126 -0
- package/.output/public/assets/{ReplayDialog-CZMApaF4.js → ReplayDialog-DodiTL6E.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-DNWccR7O.js → RequestAnatomy-B3IauToI.js} +1 -1
- package/.output/public/assets/ResponseView-Hn-5ordK.js +3 -0
- package/.output/public/assets/StreamingChunkSequence-vLDOVQM_.js +1 -0
- package/.output/public/assets/_sessionId-BFH4TOaI.js +1 -0
- package/.output/public/assets/{_sessionId-DRfvuE2k.js → _sessionId-Cj_HIFZx.js} +1 -1
- package/.output/public/assets/{index-D4HkS7Ct.js → index-Bmj2lcWE.js} +1 -1
- package/.output/public/assets/index-CgJj-HBC.css +1 -0
- package/.output/public/assets/index-Ch7uqnCT.js +1 -0
- package/.output/public/assets/index-DkDdKvLl.js +76 -0
- package/.output/public/assets/{index-B3aAuqWq.js → index-ZlhqCrSg.js} +6 -6
- package/.output/public/assets/{json-viewer-wuJSKaed.js → json-viewer-BdrnVQu-.js} +1 -1
- package/.output/public/assets/jszip.min-CS_nt_o1.js +2 -0
- package/.output/public/assets/qwen-mMn3f5ul.webp +0 -0
- package/.output/server/_libs/jszip.mjs +35 -9
- package/.output/server/_libs/lucide-react.mjs +86 -86
- package/.output/server/_libs/radix-ui__react-dialog.mjs +3 -3
- package/.output/server/_libs/tanstack__react-router.mjs +1 -1
- package/.output/server/{_sessionId-BJTNiP_O.mjs → _sessionId-C5S3pGZd.mjs} +15 -15
- package/.output/server/_sessionId-g6D69lKq.mjs +81 -0
- package/.output/server/_ssr/{CompareDrawer-BMRH3N97.mjs → CompareDrawer-CqUmGHal.mjs} +138 -103
- package/.output/server/_ssr/InspectorPet-DI0UJd01.mjs +925 -0
- package/.output/server/_ssr/{ProxyViewerContainer-AFwcHAIR.mjs → ProxyViewerContainer-CvsUmbJ3.mjs} +1216 -1659
- package/.output/server/_ssr/{ReplayDialog-CbWvRHiN.mjs → ReplayDialog-DS48dpFG.mjs} +18 -18
- package/.output/server/_ssr/{RequestAnatomy-C4kcPyqN.mjs → RequestAnatomy-DxYVQU1C.mjs} +58 -58
- package/.output/server/_ssr/{ResponseView-D8VYt-gZ.mjs → ResponseView-BLXW73eq.mjs} +60 -60
- package/.output/server/_ssr/{StreamingChunkSequence-BUUo6vU0.mjs → StreamingChunkSequence-DD0iFuy0.mjs} +16 -16
- package/.output/server/_ssr/{index-DTwMEEXO.mjs → index-B10LIaK0.mjs} +15 -15
- package/.output/server/_ssr/index-RHVJnU2p.mjs +81 -0
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-BqF1pSQD.mjs → json-viewer-LDu2XBaK.mjs} +58 -58
- package/.output/server/_ssr/{router-C8BOns7y.mjs → router-GKvrwifJ.mjs} +9005 -6488
- package/.output/server/_tanstack-start-manifest_v-Df2n4mbT.mjs +4 -0
- package/.output/server/index.mjs +105 -77
- package/.output/workers/logFinalizer.worker.js +16908 -0
- package/.output/workers/sessionWorkerEntry.js +16904 -0
- package/README.md +21 -5
- package/package.json +6 -2
- package/src/assets/logos/qwen.webp +0 -0
- package/src/cli.ts +85 -10
- package/src/components/OnboardingBanner.tsx +41 -21
- package/src/components/ProxyViewer.tsx +198 -269
- package/src/components/ProxyViewerContainer.tsx +128 -17
- package/src/components/clients/ClientLogo.tsx +9 -6
- package/src/components/ecosystem/AgentLabDialog.tsx +166 -19
- package/src/components/errors/SafeErrorBoundary.tsx +201 -0
- package/src/components/pi-agent/PiAgentPanel.tsx +4 -1
- package/src/components/providers/ImportWizardDialog.tsx +18 -42
- package/src/components/providers/ProviderLogo.tsx +1 -1
- package/src/components/providers/ProvidersPanel.tsx +8 -5
- package/src/components/providers/SettingsDialog.tsx +34 -11
- package/src/components/proxy-viewer/ApplicationBar.tsx +63 -0
- package/src/components/proxy-viewer/CompareDrawer.tsx +92 -49
- package/src/components/proxy-viewer/ConversationGroupList.tsx +49 -34
- package/src/components/proxy-viewer/ConversationHeader.tsx +22 -18
- package/src/components/proxy-viewer/LiveConnectionStatus.tsx +186 -0
- package/src/components/proxy-viewer/LogEntry.tsx +283 -275
- package/src/components/proxy-viewer/RawExportWarning.tsx +77 -0
- package/src/components/proxy-viewer/SessionLoadProgressBar.tsx +61 -0
- package/src/components/proxy-viewer/ThreadConnector.tsx +16 -1
- package/src/components/proxy-viewer/TurnGroup.tsx +13 -2
- package/src/components/proxy-viewer/accessibility.ts +8 -0
- package/src/components/proxy-viewer/lazy.ts +4 -0
- package/src/components/proxy-viewer/liveConnectionState.ts +158 -0
- package/src/components/proxy-viewer/proxyViewerLogic.ts +10 -0
- package/src/components/proxy-viewer/viewerState.ts +2 -6
- package/src/components/ui/select.tsx +1 -1
- package/src/components/ui/tabs.tsx +3 -3
- package/src/components/ui/transient-toast.tsx +1 -1
- package/src/lib/apiClient.ts +17 -2
- package/src/lib/ecosystemContract.ts +34 -0
- package/src/lib/export-logs.ts +1 -1
- package/src/lib/providerContract.ts +70 -4
- package/src/lib/providerImportContract.ts +27 -0
- package/src/lib/providerModelMetadata.ts +16 -7
- package/src/lib/resourceLimits.ts +152 -0
- package/src/lib/safeDiagnostic.ts +38 -0
- package/src/lib/useProviders.ts +4 -4
- package/src/lib/utils.ts +5 -1
- package/src/mcp/server.ts +39 -6
- package/src/mcp/toolHandlers.ts +131 -4
- package/src/proxy/chunkStorage.ts +20 -6
- package/src/proxy/config.ts +20 -6
- package/src/proxy/dataDir.ts +3 -0
- package/src/proxy/ecosystemExecutionConfirmation.ts +114 -0
- package/src/proxy/ecosystemExecutionRoute.ts +116 -0
- package/src/proxy/ecosystemTasks.ts +48 -0
- package/src/proxy/evidenceExporter.ts +23 -9
- package/src/proxy/groupEvidenceExporter.ts +26 -5
- package/src/proxy/groupStore.ts +13 -3
- package/src/proxy/handler.ts +103 -20
- package/src/proxy/identityProxy.ts +333 -14
- package/src/proxy/logFinalizer.ts +73 -6
- package/src/proxy/logImportUpload.ts +128 -0
- package/src/proxy/logImporter.ts +321 -70
- package/src/proxy/logIndex.ts +16 -6
- package/src/proxy/logger.ts +294 -41
- package/src/proxy/privateDataPath.ts +183 -0
- package/src/proxy/providerScanStore.ts +87 -0
- package/src/proxy/providerSecretStore.ts +58 -19
- package/src/proxy/providers.ts +175 -58
- package/src/proxy/rawStreamCapture.ts +66 -5
- package/src/proxy/runStore.ts +13 -3
- package/src/proxy/runtimeAdmission.ts +52 -0
- package/src/proxy/runtimeHealth.ts +206 -0
- package/src/proxy/runtimeShutdown.ts +75 -0
- package/src/proxy/sessionArchive.ts +15 -2
- package/src/proxy/sessionProcess.ts +19 -0
- package/src/proxy/sessionRuntime.ts +7 -0
- package/src/proxy/shutdownCoordinator.ts +90 -0
- package/src/proxy/sqliteLogIndex.ts +18 -2
- package/src/proxy/store.ts +18 -2
- package/src/routes/__root.tsx +18 -1
- package/src/routes/api/alerts.summary.ts +12 -8
- package/src/routes/api/alerts.ts +27 -9
- package/src/routes/api/ecosystem.packages.$packageId.help.ts +3 -8
- package/src/routes/api/ecosystem.packages.$packageId.install.ts +3 -8
- package/src/routes/api/ecosystem.packages.$packageId.runner-presets.ts +3 -11
- package/src/routes/api/ecosystem.packages.$packageId.upgrade.ts +3 -8
- package/src/routes/api/ecosystem.recipes.$recipeId.run.ts +3 -8
- package/src/routes/api/health.ts +9 -2
- package/src/routes/api/knowledge.project-context.ts +11 -0
- package/src/routes/api/knowledge.search.ts +33 -2
- package/src/routes/api/logs.$id.body.ts +16 -2
- package/src/routes/api/logs.import.ts +7 -18
- package/src/routes/api/logs.stream.ts +147 -69
- package/src/routes/api/logs.ts +55 -14
- package/src/routes/api/providers.$providerId.model-metadata.ts +22 -4
- package/src/routes/api/providers.$providerId.ts +37 -6
- package/src/routes/api/providers.export.ts +34 -16
- package/src/routes/api/providers.import.ts +42 -8
- package/src/routes/api/providers.scan.ts +13 -8
- package/src/routes/api/providers.ts +36 -5
- package/src/routes/api/runs.ts +12 -12
- package/src/routes/api/sessions.ts +15 -8
- package/src/routes/index.tsx +6 -0
- package/src/routes/livez.ts +13 -0
- package/src/routes/readyz.ts +18 -0
- package/src/routes/session/$sessionId.tsx +6 -0
- package/styles/globals.css +19 -3
- package/.output/public/assets/CompareDrawer-TghZr5t1.js +0 -1
- package/.output/public/assets/ProxyViewerContainer-fShBAZjj.js +0 -4234
- package/.output/public/assets/ResponseView-d25MdFhY.js +0 -3
- package/.output/public/assets/StreamingChunkSequence-DIFZEnWe.js +0 -1
- package/.output/public/assets/index-BsxAGqMA.css +0 -1
- package/.output/public/assets/index-DzARL2Fi.js +0 -76
- package/.output/public/assets/qwen-CONDcHqt.png +0 -0
- package/.output/server/_tanstack-start-manifest_v-BBf2nRhb.mjs +0 -4
- package/src/assets/logos/mcp.png +0 -0
- package/src/assets/logos/qwen.png +0 -0
- package/src/components/ui/mcp-logo.tsx +0 -20
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Suspense, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import type { JSX } from "react";
|
|
2
|
+
import type { JSX, KeyboardEvent as ReactKeyboardEvent } from "react";
|
|
3
3
|
import {
|
|
4
4
|
Check,
|
|
5
5
|
ChevronRight,
|
|
@@ -46,6 +46,11 @@ type ChangedOp = Extract<DiffOp, { kind: "changed" }>;
|
|
|
46
46
|
|
|
47
47
|
type DiffMode = "unified" | "split";
|
|
48
48
|
|
|
49
|
+
export const COMPARE_RESPONSIVE_HEADER_CLASS_NAME =
|
|
50
|
+
"flex flex-col gap-3 border-b border-border px-3 py-3 sm:flex-row sm:items-start sm:gap-4 sm:px-4";
|
|
51
|
+
export const COMPARE_RESPONSIVE_SPLIT_CLASS_NAME =
|
|
52
|
+
"grid min-w-0 grid-cols-1 gap-x-2 gap-y-0.5 px-3 py-2 text-xs md:grid-cols-[200px_minmax(0,1fr)_minmax(0,1fr)]";
|
|
53
|
+
|
|
49
54
|
/** Walk the JsonNode tree and pretty-print it back to a JSON string for the
|
|
50
55
|
* expanded-equal-subtree view. The node is a plain object structure so
|
|
51
56
|
* `JSON.stringify` produces correct output. */
|
|
@@ -162,23 +167,23 @@ const KIND_VISUAL: Record<
|
|
|
162
167
|
> = {
|
|
163
168
|
added: {
|
|
164
169
|
icon: Plus,
|
|
165
|
-
accent: "text-
|
|
166
|
-
bg: "bg-
|
|
167
|
-
border: "border-l-
|
|
170
|
+
accent: "text-status-success",
|
|
171
|
+
bg: "bg-status-success/5 hover:bg-status-success/10",
|
|
172
|
+
border: "border-l-status-success",
|
|
168
173
|
label: "ADDED",
|
|
169
174
|
},
|
|
170
175
|
removed: {
|
|
171
176
|
icon: Minus,
|
|
172
|
-
accent: "text-
|
|
173
|
-
bg: "bg-
|
|
174
|
-
border: "border-l-
|
|
177
|
+
accent: "text-status-danger",
|
|
178
|
+
bg: "bg-status-danger/5 hover:bg-status-danger/10",
|
|
179
|
+
border: "border-l-status-danger",
|
|
175
180
|
label: "REMOVED",
|
|
176
181
|
},
|
|
177
182
|
changed: {
|
|
178
183
|
icon: Pencil,
|
|
179
|
-
accent: "text-
|
|
180
|
-
bg: "bg-
|
|
181
|
-
border: "border-l-
|
|
184
|
+
accent: "text-status-watch",
|
|
185
|
+
bg: "bg-status-watch/5 hover:bg-status-watch/10",
|
|
186
|
+
border: "border-l-status-watch",
|
|
182
187
|
label: "CHANGED",
|
|
183
188
|
},
|
|
184
189
|
equal: {
|
|
@@ -294,19 +299,19 @@ function UnifiedOpRow({
|
|
|
294
299
|
? [
|
|
295
300
|
{
|
|
296
301
|
text: previewNode(op.left, 400),
|
|
297
|
-
tone: "text-
|
|
302
|
+
tone: "text-status-danger line-through",
|
|
298
303
|
},
|
|
299
|
-
{ text: previewNode(op.right, 400), tone: "text-
|
|
304
|
+
{ text: previewNode(op.right, 400), tone: "text-status-success" },
|
|
300
305
|
]
|
|
301
306
|
: op.kind === "removed"
|
|
302
307
|
? [
|
|
303
308
|
{
|
|
304
309
|
text: previewNode(op.value, 400),
|
|
305
|
-
tone: "text-
|
|
310
|
+
tone: "text-status-danger line-through",
|
|
306
311
|
},
|
|
307
312
|
]
|
|
308
313
|
: op.kind === "added"
|
|
309
|
-
? [{ text: previewNode(op.value, 400), tone: "text-
|
|
314
|
+
? [{ text: previewNode(op.value, 400), tone: "text-status-success" }]
|
|
310
315
|
: [{ text: previewNode(op.value, 400), tone: "text-muted-foreground" }];
|
|
311
316
|
|
|
312
317
|
const justCopied = copiedPath === op.path && op.path !== "";
|
|
@@ -376,7 +381,7 @@ function UnifiedOpRow({
|
|
|
376
381
|
className={cn(
|
|
377
382
|
"shrink-0 p-1 rounded transition-colors cursor-pointer inline-flex items-center justify-center",
|
|
378
383
|
justCopied
|
|
379
|
-
? "text-
|
|
384
|
+
? "text-status-success"
|
|
380
385
|
: "text-muted-foreground/50 hover:text-foreground hover:bg-muted",
|
|
381
386
|
)}
|
|
382
387
|
aria-label={justCopied ? "Copied" : "Copy"}
|
|
@@ -427,12 +432,12 @@ function ExpandedSubtree({ op }: { op: AddedOp | RemovedOp | ChangedOp }): JSX.E
|
|
|
427
432
|
}
|
|
428
433
|
return (
|
|
429
434
|
<div className="pl-5 mt-2 grid grid-cols-1 md:grid-cols-2 gap-2">
|
|
430
|
-
<div className="border border-
|
|
431
|
-
<div className="text-[10px] uppercase tracking-wider text-
|
|
435
|
+
<div className="rounded border border-status-danger/30 bg-status-danger/5 p-2">
|
|
436
|
+
<div className="mb-1 text-[10px] uppercase tracking-wider text-status-danger">Old</div>
|
|
432
437
|
<LazyJsonStringView text={nodeToJsonString(op.left)} defaultExpandDepth={0} />
|
|
433
438
|
</div>
|
|
434
|
-
<div className="border border-
|
|
435
|
-
<div className="text-[10px] uppercase tracking-wider text-
|
|
439
|
+
<div className="rounded border border-status-success/30 bg-status-success/5 p-2">
|
|
440
|
+
<div className="mb-1 text-[10px] uppercase tracking-wider text-status-success">New</div>
|
|
436
441
|
<LazyJsonStringView text={nodeToJsonString(op.right)} defaultExpandDepth={0} />
|
|
437
442
|
</div>
|
|
438
443
|
</div>
|
|
@@ -461,7 +466,7 @@ function SummaryChips({
|
|
|
461
466
|
className={cn(
|
|
462
467
|
"inline-flex items-center gap-1 px-2 py-0.5 rounded-full border cursor-pointer transition-colors",
|
|
463
468
|
counts.removed > 0
|
|
464
|
-
? "border-
|
|
469
|
+
? "border-status-danger/40 bg-status-danger/10 text-status-danger hover:bg-status-danger/20"
|
|
465
470
|
: "border-border text-muted-foreground/40 cursor-not-allowed",
|
|
466
471
|
)}
|
|
467
472
|
title={counts.removed > 0 ? "Jump to first removed" : "No removals"}
|
|
@@ -476,7 +481,7 @@ function SummaryChips({
|
|
|
476
481
|
className={cn(
|
|
477
482
|
"inline-flex items-center gap-1 px-2 py-0.5 rounded-full border cursor-pointer transition-colors",
|
|
478
483
|
counts.added > 0
|
|
479
|
-
? "border-
|
|
484
|
+
? "border-status-success/40 bg-status-success/10 text-status-success hover:bg-status-success/20"
|
|
480
485
|
: "border-border text-muted-foreground/40 cursor-not-allowed",
|
|
481
486
|
)}
|
|
482
487
|
title={counts.added > 0 ? "Jump to first added" : "No additions"}
|
|
@@ -491,7 +496,7 @@ function SummaryChips({
|
|
|
491
496
|
className={cn(
|
|
492
497
|
"inline-flex items-center gap-1 px-2 py-0.5 rounded-full border cursor-pointer transition-colors",
|
|
493
498
|
counts.changed > 0
|
|
494
|
-
? "border-
|
|
499
|
+
? "border-status-watch/40 bg-status-watch/10 text-status-watch hover:bg-status-watch/20"
|
|
495
500
|
: "border-border text-muted-foreground/40 cursor-not-allowed",
|
|
496
501
|
)}
|
|
497
502
|
title={counts.changed > 0 ? "Jump to first changed" : "No changes"}
|
|
@@ -563,27 +568,29 @@ function SideSummary({
|
|
|
563
568
|
className={cn(
|
|
564
569
|
"text-[10px] px-1.5 py-0 h-5 font-mono shrink-0",
|
|
565
570
|
side === "left"
|
|
566
|
-
? "border-
|
|
567
|
-
: "border-
|
|
571
|
+
? "border-status-danger/40 text-status-danger"
|
|
572
|
+
: "border-status-success/40 text-status-success",
|
|
568
573
|
)}
|
|
569
574
|
>
|
|
570
575
|
{side === "left" ? "← Left" : "Right →"}
|
|
571
576
|
</Badge>
|
|
572
|
-
<span className="font-mono text-
|
|
577
|
+
<span className="font-mono text-status-info" title={`Log ID ${String(log.id)}`}>
|
|
573
578
|
#{displayNumber}
|
|
574
579
|
</span>
|
|
575
580
|
{log.model !== null && (
|
|
576
|
-
<span className="font-mono leading-snug text-muted-foreground">
|
|
581
|
+
<span className="min-w-0 truncate font-mono leading-snug text-muted-foreground">
|
|
582
|
+
{log.model}
|
|
583
|
+
</span>
|
|
577
584
|
)}
|
|
578
585
|
</div>
|
|
579
|
-
<div className="flex items-center gap-3
|
|
586
|
+
<div className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1 font-mono text-muted-foreground">
|
|
580
587
|
{log.cacheCreationInputTokens !== null && log.cacheCreationInputTokens > 0 && (
|
|
581
|
-
<span className="text-
|
|
588
|
+
<span className="text-status-success">
|
|
582
589
|
KV Cache +{formatTokens(log.cacheCreationInputTokens)}
|
|
583
590
|
</span>
|
|
584
591
|
)}
|
|
585
592
|
{log.cacheReadInputTokens !== null && log.cacheReadInputTokens > 0 && (
|
|
586
|
-
<span className="text-
|
|
593
|
+
<span className="text-status-accent">
|
|
587
594
|
KV Cache ~{formatTokens(log.cacheReadInputTokens)}
|
|
588
595
|
</span>
|
|
589
596
|
)}
|
|
@@ -605,6 +612,9 @@ export function CompareDrawer({
|
|
|
605
612
|
rightDisplayNumber,
|
|
606
613
|
onClose,
|
|
607
614
|
}: CompareDrawerProps): JSX.Element {
|
|
615
|
+
const panelRef = useRef<HTMLDivElement>(null);
|
|
616
|
+
const closeButtonRef = useRef<HTMLButtonElement>(null);
|
|
617
|
+
const previousActiveElementRef = useRef<HTMLElement | null>(null);
|
|
608
618
|
// Memoize the diff so re-renders (e.g. parent re-renders) don't recompute.
|
|
609
619
|
const ops = useMemo<DiffOp[]>(() => {
|
|
610
620
|
const leftRequest = getLogFormatAdapter(resolveLogFormat(left)).analyzeRequest(
|
|
@@ -716,8 +726,12 @@ export function CompareDrawer({
|
|
|
716
726
|
}
|
|
717
727
|
};
|
|
718
728
|
|
|
719
|
-
// Esc keybinding
|
|
729
|
+
// Esc keybinding, initial focus, focus restoration, and body scroll lock while open.
|
|
720
730
|
useEffect(() => {
|
|
731
|
+
previousActiveElementRef.current =
|
|
732
|
+
document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
733
|
+
closeButtonRef.current?.focus();
|
|
734
|
+
|
|
721
735
|
const onKey = (e: KeyboardEvent) => {
|
|
722
736
|
if (e.key === "Escape") onClose();
|
|
723
737
|
};
|
|
@@ -727,9 +741,36 @@ export function CompareDrawer({
|
|
|
727
741
|
return () => {
|
|
728
742
|
document.removeEventListener("keydown", onKey);
|
|
729
743
|
document.body.style.overflow = prevOverflow;
|
|
744
|
+
previousActiveElementRef.current?.focus();
|
|
730
745
|
};
|
|
731
746
|
}, [onClose]);
|
|
732
747
|
|
|
748
|
+
const handlePanelKeyDown = (event: ReactKeyboardEvent<HTMLDivElement>): void => {
|
|
749
|
+
event.stopPropagation();
|
|
750
|
+
if (event.key !== "Tab") return;
|
|
751
|
+
|
|
752
|
+
const panel = panelRef.current;
|
|
753
|
+
if (panel === null) return;
|
|
754
|
+
const focusable = Array.from(
|
|
755
|
+
panel.querySelectorAll<HTMLElement>(
|
|
756
|
+
'button:not([disabled]), [href], input:not([disabled]), [tabindex]:not([tabindex="-1"])',
|
|
757
|
+
),
|
|
758
|
+
);
|
|
759
|
+
const first = focusable[0];
|
|
760
|
+
const last = focusable[focusable.length - 1];
|
|
761
|
+
if (first === undefined || last === undefined) return;
|
|
762
|
+
|
|
763
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
764
|
+
event.preventDefault();
|
|
765
|
+
last.focus();
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
if (!event.shiftKey && document.activeElement === last) {
|
|
769
|
+
event.preventDefault();
|
|
770
|
+
first.focus();
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
|
|
733
774
|
const sameSession = getConversationId(left) === getConversationId(right);
|
|
734
775
|
const allEqual = ops.length === 1 && ops[0]?.kind === "equal";
|
|
735
776
|
|
|
@@ -751,22 +792,24 @@ export function CompareDrawer({
|
|
|
751
792
|
|
|
752
793
|
{/* Drawer panel */}
|
|
753
794
|
<div
|
|
795
|
+
ref={panelRef}
|
|
754
796
|
className={cn(
|
|
755
|
-
"relative bg-background border-l border-border shadow-xl",
|
|
797
|
+
"relative overflow-x-hidden bg-background border-l border-border shadow-xl",
|
|
756
798
|
"w-full md:w-[70vw] max-w-[1100px] flex flex-col h-full",
|
|
757
799
|
)}
|
|
758
800
|
onClick={(e) => e.stopPropagation()}
|
|
759
|
-
onKeyDown={
|
|
801
|
+
onKeyDown={handlePanelKeyDown}
|
|
760
802
|
>
|
|
761
803
|
{/* Header */}
|
|
762
|
-
<div className=
|
|
763
|
-
<div className="flex-1
|
|
804
|
+
<div className={COMPARE_RESPONSIVE_HEADER_CLASS_NAME}>
|
|
805
|
+
<div className="grid min-w-0 flex-1 grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-4">
|
|
764
806
|
<SideSummary log={left} side="left" displayNumber={leftDisplayNumber} />
|
|
765
807
|
<SideSummary log={right} side="right" displayNumber={rightDisplayNumber} />
|
|
766
808
|
</div>
|
|
767
|
-
<div className="flex items-center gap-2
|
|
809
|
+
<div className="flex w-full shrink-0 items-center justify-between gap-2 sm:w-auto sm:justify-start">
|
|
768
810
|
<ModeToggle mode={mode} onChange={setMode} />
|
|
769
811
|
<button
|
|
812
|
+
ref={closeButtonRef}
|
|
770
813
|
type="button"
|
|
771
814
|
onClick={onClose}
|
|
772
815
|
aria-label="Close"
|
|
@@ -778,7 +821,7 @@ export function CompareDrawer({
|
|
|
778
821
|
</div>
|
|
779
822
|
|
|
780
823
|
{!sameSession && (
|
|
781
|
-
<div className="px-4 py-1.5 text-xs text-
|
|
824
|
+
<div className="border-b border-border bg-warning/10 px-4 py-1.5 text-xs text-foreground">
|
|
782
825
|
Heads up: the two selected logs are from different sessions.
|
|
783
826
|
</div>
|
|
784
827
|
)}
|
|
@@ -835,7 +878,7 @@ export function CompareDrawer({
|
|
|
835
878
|
);
|
|
836
879
|
}
|
|
837
880
|
|
|
838
|
-
/**
|
|
881
|
+
/** Three-column on wide screens and a bounded stacked comparison on narrow screens. */
|
|
839
882
|
function SplitBody({
|
|
840
883
|
grouped,
|
|
841
884
|
left,
|
|
@@ -850,9 +893,9 @@ function SplitBody({
|
|
|
850
893
|
rightDisplayNumber: number;
|
|
851
894
|
}): JSX.Element {
|
|
852
895
|
return (
|
|
853
|
-
<div
|
|
896
|
+
<div data-compare-layout="responsive-split" className={COMPARE_RESPONSIVE_SPLIT_CLASS_NAME}>
|
|
854
897
|
{/* Column headers */}
|
|
855
|
-
<div className="grid grid-cols-
|
|
898
|
+
<div className="col-span-1 mb-2 grid grid-cols-1 gap-x-2 gap-y-1 border-b border-border pb-2 text-[10px] uppercase tracking-wider text-muted-foreground md:col-span-3 md:grid-cols-[200px_minmax(0,1fr)_minmax(0,1fr)]">
|
|
856
899
|
<span>Path</span>
|
|
857
900
|
<span title={`Log ID ${String(left.id)}`}>Left (Log #{leftDisplayNumber})</span>
|
|
858
901
|
<span title={`Log ID ${String(right.id)}`}>Right (Log #{rightDisplayNumber})</span>
|
|
@@ -864,7 +907,7 @@ function SplitBody({
|
|
|
864
907
|
<div
|
|
865
908
|
// biome-ignore lint/suspicious/noArrayIndexKey: positional in the grouped list
|
|
866
909
|
key={i}
|
|
867
|
-
className="col-span-
|
|
910
|
+
className="col-span-1 px-2 py-1 text-xs text-muted-foreground/60 md:col-span-3"
|
|
868
911
|
>
|
|
869
912
|
{g.ops.length} equal siblings collapsed — switch to Unified to expand
|
|
870
913
|
</div>
|
|
@@ -876,7 +919,7 @@ function SplitBody({
|
|
|
876
919
|
<div
|
|
877
920
|
// biome-ignore lint/suspicious/noArrayIndexKey: positional in the grouped list
|
|
878
921
|
key={i}
|
|
879
|
-
className="col-span-
|
|
922
|
+
className="col-span-1 grid min-w-0 grid-cols-1 gap-x-2 gap-y-1 px-2 py-0.5 text-muted-foreground md:col-span-3 md:grid-cols-[200px_minmax(0,1fr)_minmax(0,1fr)]"
|
|
880
923
|
>
|
|
881
924
|
<span className="font-mono text-xs truncate" title={op.path}>
|
|
882
925
|
{op.path}
|
|
@@ -895,10 +938,10 @@ function SplitBody({
|
|
|
895
938
|
<div
|
|
896
939
|
// biome-ignore lint/suspicious/noArrayIndexKey: positional in the grouped list
|
|
897
940
|
key={i}
|
|
898
|
-
className="col-span-
|
|
941
|
+
className="col-span-1 rounded border-l-2 border-l-status-success bg-status-success/5 px-2 py-1 text-xs md:col-span-3"
|
|
899
942
|
>
|
|
900
943
|
<div className="font-mono text-xs text-muted-foreground mb-0.5">{op.path}</div>
|
|
901
|
-
<div className="font-mono break-all text-
|
|
944
|
+
<div className="font-mono break-all text-status-success">
|
|
902
945
|
+ {previewNode(op.value, 400)}
|
|
903
946
|
</div>
|
|
904
947
|
</div>
|
|
@@ -909,10 +952,10 @@ function SplitBody({
|
|
|
909
952
|
<div
|
|
910
953
|
// biome-ignore lint/suspicious/noArrayIndexKey: positional in the grouped list
|
|
911
954
|
key={i}
|
|
912
|
-
className="col-span-
|
|
955
|
+
className="col-span-1 rounded border-l-2 border-l-status-danger bg-status-danger/5 px-2 py-1 text-xs md:col-span-3"
|
|
913
956
|
>
|
|
914
957
|
<div className="font-mono text-xs text-muted-foreground mb-0.5">{op.path}</div>
|
|
915
|
-
<div className="font-mono break-all text-
|
|
958
|
+
<div className="font-mono break-all text-status-danger line-through">
|
|
916
959
|
− {previewNode(op.value, 400)}
|
|
917
960
|
</div>
|
|
918
961
|
</div>
|
|
@@ -922,14 +965,14 @@ function SplitBody({
|
|
|
922
965
|
<div
|
|
923
966
|
// biome-ignore lint/suspicious/noArrayIndexKey: positional in the grouped list
|
|
924
967
|
key={i}
|
|
925
|
-
className="col-span-
|
|
968
|
+
className="col-span-1 rounded border-l-2 border-l-status-watch bg-status-watch/5 px-2 py-1 text-xs md:col-span-3"
|
|
926
969
|
>
|
|
927
970
|
<div className="font-mono text-xs text-muted-foreground mb-1">{op.path}</div>
|
|
928
|
-
<div className="grid grid-cols-
|
|
929
|
-
<div className="font-mono text-
|
|
971
|
+
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
|
972
|
+
<div className="font-mono break-all text-status-danger line-through">
|
|
930
973
|
{previewNode(op.left, 400)}
|
|
931
974
|
</div>
|
|
932
|
-
<div className="font-mono text-
|
|
975
|
+
<div className="font-mono break-all text-status-success">
|
|
933
976
|
{previewNode(op.right, 400)}
|
|
934
977
|
</div>
|
|
935
978
|
</div>
|
|
@@ -284,14 +284,16 @@ function countSessions(processes: readonly ProcessGroupData[]): number {
|
|
|
284
284
|
return sessions;
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
-
function readStoredIdSet(storageKey: string): ReadonlySet<string> {
|
|
288
|
-
if (typeof window === "undefined") return
|
|
287
|
+
function readStoredIdSet(storageKey: string): ReadonlySet<string> | null {
|
|
288
|
+
if (typeof window === "undefined") return null;
|
|
289
289
|
try {
|
|
290
|
-
const
|
|
291
|
-
if (
|
|
290
|
+
const storedValue = window.localStorage.getItem(storageKey);
|
|
291
|
+
if (storedValue === null) return null;
|
|
292
|
+
const parsed: unknown = JSON.parse(storedValue);
|
|
293
|
+
if (!Array.isArray(parsed)) return null;
|
|
292
294
|
return new Set(parsed.filter((value): value is string => typeof value === "string"));
|
|
293
295
|
} catch {
|
|
294
|
-
return
|
|
296
|
+
return null;
|
|
295
297
|
}
|
|
296
298
|
}
|
|
297
299
|
|
|
@@ -312,6 +314,21 @@ function removeIds(values: ReadonlySet<string>, ids: ReadonlySet<string>): Reado
|
|
|
312
314
|
return changed ? next : values;
|
|
313
315
|
}
|
|
314
316
|
|
|
317
|
+
export function buildDefaultCollapsedProcessIds(
|
|
318
|
+
hierarchy: readonly ClientGroupData[],
|
|
319
|
+
): ReadonlySet<string> {
|
|
320
|
+
const collapsedIds = new Set<string>();
|
|
321
|
+
for (const clientGroup of hierarchy) {
|
|
322
|
+
for (let index = 1; index < clientGroup.processes.length; index++) {
|
|
323
|
+
const processGroup = clientGroup.processes[index];
|
|
324
|
+
if (processGroup !== undefined && !hasPendingLogs(processGroup.logs)) {
|
|
325
|
+
collapsedIds.add(processGroup.id);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return collapsedIds;
|
|
330
|
+
}
|
|
331
|
+
|
|
315
332
|
function shortProjectPath(projectFolder: string | null): string | null {
|
|
316
333
|
if (projectFolder === null || projectFolder === "") return null;
|
|
317
334
|
const parts = projectFolder
|
|
@@ -337,12 +354,12 @@ function SummaryChip({
|
|
|
337
354
|
return (
|
|
338
355
|
<span
|
|
339
356
|
className={cn(
|
|
340
|
-
"
|
|
357
|
+
"inline-flex h-7 items-center gap-1 rounded-md bg-[var(--inspector-surface-low)] px-2 text-xs text-muted-foreground",
|
|
341
358
|
className,
|
|
342
359
|
)}
|
|
343
360
|
>
|
|
344
|
-
<span className="font-mono text-[10px] uppercase text-
|
|
345
|
-
<span className="font-mono tabular-nums text-
|
|
361
|
+
<span className="font-mono text-[10px] uppercase text-muted-foreground/70">{label}</span>
|
|
362
|
+
<span className="font-mono tabular-nums text-foreground">{value}</span>
|
|
346
363
|
</span>
|
|
347
364
|
);
|
|
348
365
|
}
|
|
@@ -351,13 +368,13 @@ function AncestorActivityIndicator({ count }: { count: number }): JSX.Element {
|
|
|
351
368
|
const label = `${count} open turn${count === 1 ? "" : "s"}`;
|
|
352
369
|
return (
|
|
353
370
|
<span
|
|
354
|
-
className="relative inline-flex size-6 shrink-0 items-center justify-center rounded-md bg-
|
|
371
|
+
className="relative inline-flex size-6 shrink-0 items-center justify-center rounded-md bg-[var(--inspector-surface-medium)] text-[var(--inspector-status-watch)] ring-1 ring-border/50"
|
|
355
372
|
aria-label={label}
|
|
356
373
|
title={label}
|
|
357
374
|
>
|
|
358
375
|
<Loader2 className="size-3 animate-spin" />
|
|
359
376
|
{count > 1 && (
|
|
360
|
-
<span className="absolute translate-x-2 -translate-y-2 rounded bg-
|
|
377
|
+
<span className="absolute translate-x-2 -translate-y-2 rounded bg-card px-0.5 font-mono text-[8px] tabular-nums text-[var(--inspector-status-watch)]">
|
|
361
378
|
{count}
|
|
362
379
|
</span>
|
|
363
380
|
)}
|
|
@@ -373,14 +390,16 @@ export function ConversationGroupList({
|
|
|
373
390
|
const [collapsedClientIds, setCollapsedClientIds] = useState<ReadonlySet<string>>(
|
|
374
391
|
() => new Set(),
|
|
375
392
|
);
|
|
376
|
-
const [collapsedProcessIds, setCollapsedProcessIds] = useState<ReadonlySet<string>>(
|
|
377
|
-
()
|
|
393
|
+
const [collapsedProcessIds, setCollapsedProcessIds] = useState<ReadonlySet<string>>(() =>
|
|
394
|
+
buildDefaultCollapsedProcessIds(hierarchy),
|
|
378
395
|
);
|
|
379
396
|
const [storedCollapseStateLoaded, setStoredCollapseStateLoaded] = useState(false);
|
|
380
397
|
|
|
381
398
|
useEffect(() => {
|
|
382
|
-
|
|
383
|
-
|
|
399
|
+
const storedClientIds = readStoredIdSet(COLLAPSED_CLIENTS_STORAGE_KEY);
|
|
400
|
+
const storedProcessIds = readStoredIdSet(COLLAPSED_PROCESSES_STORAGE_KEY);
|
|
401
|
+
if (storedClientIds !== null) setCollapsedClientIds(storedClientIds);
|
|
402
|
+
if (storedProcessIds !== null) setCollapsedProcessIds(storedProcessIds);
|
|
384
403
|
setStoredCollapseStateLoaded(true);
|
|
385
404
|
}, []);
|
|
386
405
|
|
|
@@ -470,38 +489,34 @@ function ClientGroupSection({
|
|
|
470
489
|
return (
|
|
471
490
|
<section
|
|
472
491
|
className={cn(
|
|
473
|
-
"
|
|
474
|
-
isWorking && "bg-card/
|
|
492
|
+
"relative isolate overflow-hidden rounded-[8px] bg-card/65",
|
|
493
|
+
isWorking && "bg-card/80",
|
|
475
494
|
)}
|
|
476
495
|
data-ide-group={group.id}
|
|
477
496
|
>
|
|
478
497
|
<button
|
|
479
498
|
type="button"
|
|
480
|
-
className="grid w-full min-w-0 grid-cols-[2.25rem_minmax(0,1fr)_auto] items-center gap-2.5 px-3 py-2.5 text-left transition-colors hover:bg-
|
|
499
|
+
className="grid w-full min-w-0 grid-cols-[2.25rem_minmax(0,1fr)_auto] items-center gap-2.5 px-3 py-2.5 text-left transition-colors hover:bg-[var(--inspector-surface-low)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
481
500
|
onClick={onToggleClient}
|
|
482
501
|
aria-expanded={!collapsed}
|
|
483
502
|
>
|
|
484
503
|
{group.client === "unknown" ? (
|
|
485
|
-
<span className="
|
|
504
|
+
<span className="inline-flex size-9 shrink-0 items-center justify-center rounded-md bg-[var(--inspector-surface-low)] text-muted-foreground">
|
|
486
505
|
<Monitor className="size-4" />
|
|
487
506
|
</span>
|
|
488
507
|
) : (
|
|
489
|
-
<ClientLogo
|
|
490
|
-
client={group.client}
|
|
491
|
-
className="size-9 rounded-md text-cyan-100"
|
|
492
|
-
decorative={true}
|
|
493
|
-
/>
|
|
508
|
+
<ClientLogo client={group.client} className="size-9 rounded-md" decorative={true} />
|
|
494
509
|
)}
|
|
495
510
|
|
|
496
511
|
<span className="min-w-0">
|
|
497
512
|
<span className="flex min-w-0 items-center gap-2">
|
|
498
513
|
<span
|
|
499
|
-
className="truncate text-sm font-semibold text-
|
|
514
|
+
className="truncate text-sm font-semibold text-foreground"
|
|
500
515
|
title={group.userAgent ?? group.label}
|
|
501
516
|
>
|
|
502
517
|
{group.label}
|
|
503
518
|
</span>
|
|
504
|
-
<span className="rounded bg-
|
|
519
|
+
<span className="rounded bg-[var(--inspector-surface-medium)] px-1.5 py-0.5 font-mono text-[9px] uppercase text-muted-foreground">
|
|
505
520
|
{clientAppTypeLabel(group.client)}
|
|
506
521
|
</span>
|
|
507
522
|
{isWorking && <AncestorActivityIndicator count={pendingTurnCount} />}
|
|
@@ -524,14 +539,14 @@ function ClientGroupSection({
|
|
|
524
539
|
<SummaryChip label="OUT" value={formatTokenCount(totals.outputTokens)} />
|
|
525
540
|
</span>
|
|
526
541
|
)}
|
|
527
|
-
<span className="
|
|
542
|
+
<span className="inline-flex size-8 shrink-0 items-center justify-center rounded-md bg-[var(--inspector-surface-low)] text-muted-foreground transition-colors group-hover:bg-[var(--inspector-surface-medium)]">
|
|
528
543
|
{collapsed ? <ChevronRight className="size-4" /> : <ChevronDown className="size-4" />}
|
|
529
544
|
</span>
|
|
530
545
|
</span>
|
|
531
546
|
</button>
|
|
532
547
|
|
|
533
548
|
{!collapsed && (
|
|
534
|
-
<div className="space-y-
|
|
549
|
+
<div className="space-y-0.5 px-2 pb-2 pt-0.5">
|
|
535
550
|
{group.processes.map((processGroup) => (
|
|
536
551
|
<ProcessGroupSection
|
|
537
552
|
key={processGroup.id}
|
|
@@ -567,23 +582,23 @@ function ProcessGroupSection({
|
|
|
567
582
|
const processLabel =
|
|
568
583
|
group.clientPid !== null && group.clientPid !== undefined
|
|
569
584
|
? `PID ${group.clientPid}`
|
|
570
|
-
: "
|
|
585
|
+
: "Process unavailable";
|
|
571
586
|
|
|
572
587
|
return (
|
|
573
|
-
<section className="relative pl-
|
|
588
|
+
<section className="relative pl-3">
|
|
574
589
|
<button
|
|
575
590
|
type="button"
|
|
576
|
-
className="
|
|
591
|
+
className="grid w-full min-w-0 grid-cols-[2rem_minmax(0,1fr)_auto] items-center gap-2.5 rounded-md bg-[var(--inspector-surface-low)] px-2.5 py-2 text-left transition-colors hover:bg-[var(--inspector-surface-medium)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
577
592
|
onClick={onToggle}
|
|
578
593
|
aria-expanded={!collapsed}
|
|
579
594
|
>
|
|
580
|
-
<span className="
|
|
595
|
+
<span className="inline-flex size-8 shrink-0 items-center justify-center text-[var(--inspector-status-watch)]">
|
|
581
596
|
<Cpu className="size-4" />
|
|
582
597
|
</span>
|
|
583
598
|
|
|
584
599
|
<span className="min-w-0">
|
|
585
600
|
<span className="flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1">
|
|
586
|
-
<span className="font-mono text-xs font-semibold tabular-nums text-
|
|
601
|
+
<span className="font-mono text-xs font-semibold tabular-nums text-foreground">
|
|
587
602
|
{processLabel}
|
|
588
603
|
</span>
|
|
589
604
|
{isWorking && <AncestorActivityIndicator count={pendingTurnCount} />}
|
|
@@ -612,14 +627,14 @@ function ProcessGroupSection({
|
|
|
612
627
|
<SummaryChip label="OUT" value={formatTokenCount(totals.outputTokens)} />
|
|
613
628
|
</span>
|
|
614
629
|
)}
|
|
615
|
-
<span className="
|
|
630
|
+
<span className="inline-flex size-7 shrink-0 items-center justify-center rounded-md bg-[var(--inspector-surface-low)] text-muted-foreground transition-colors hover:bg-[var(--inspector-surface-medium)]">
|
|
616
631
|
{collapsed ? <ChevronRight className="size-4" /> : <ChevronDown className="size-4" />}
|
|
617
632
|
</span>
|
|
618
633
|
</span>
|
|
619
634
|
</button>
|
|
620
635
|
|
|
621
636
|
{!collapsed && (
|
|
622
|
-
<div className="space-y-1
|
|
637
|
+
<div className="space-y-1 pb-1 pl-3 pr-1 pt-1">
|
|
623
638
|
{group.sessions.map((session) => (
|
|
624
639
|
<ConversationGroup
|
|
625
640
|
key={session.id}
|