@tonyclaw/agent-inspector 4.0.1 → 4.0.3
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/backend/nitro.json +1 -1
- package/.output/cli.js +193 -110
- package/.output/server/_ssr/index.mjs +1 -1
- package/.output/server/_ssr/{router-CTHqmroT.mjs → router-ChILzhmz.mjs} +92 -4
- package/.output/server/index.mjs +1 -1
- package/.output/ui/assets/{CompareDrawer-L3n4J-Or.js → CompareDrawer-Co3cUF_I.js} +1 -1
- package/.output/ui/assets/{InspectorPet-D5o_3OZS.js → InspectorPet-jFcrL37X.js} +1 -1
- package/.output/ui/assets/{ProxyViewerContainer-Ocagx75S.js → ProxyViewerContainer-BPzt_VpM.js} +4 -4
- package/.output/ui/assets/{ReplayDialog-Bk7x1h-y.js → ReplayDialog-C6O_zuwI.js} +1 -1
- package/.output/ui/assets/{RequestAnatomy-U4lzL7Sc.js → RequestAnatomy-Ca6NvB7L.js} +1 -1
- package/.output/ui/assets/{ResponseView-LOWfvBLK.js → ResponseView-BzqKcveJ.js} +1 -1
- package/.output/ui/assets/{StreamingChunkSequence-DPfsrrXQ.js → StreamingChunkSequence-9pCZIsS_.js} +1 -1
- package/.output/ui/assets/{_sessionId-0MlWS-CO.js → _sessionId-BMP3KVGR.js} +1 -1
- package/.output/ui/assets/{_sessionId-OdX2Jvt1.js → _sessionId-y4eRoT1H.js} +1 -1
- package/.output/ui/assets/{index-Ea-c8hPg.js → index-CH5w-haG.js} +1 -1
- package/.output/ui/assets/{index-zVVS53Hd.js → index-CmocEhyU.js} +1 -1
- package/.output/ui/assets/{index-Du4qNOOt.js → index-DT8sMJ_n.js} +2 -2
- package/.output/ui/assets/{index-gvFj_9K6.js → index-bfsbIRZa.js} +1 -1
- package/.output/ui/assets/{json-viewer-C1WAMzg3.js → json-viewer-BfFOSDG5.js} +1 -1
- package/.output/ui/assets/{jszip.min-B8AKu2zm.js → jszip.min-BkbVkOsp.js} +1 -1
- package/.output/ui/index.html +1 -1
- package/package.json +5 -10
- package/src/assets/IDE/claudecode-color.svg +0 -1
- package/src/assets/IDE/codex-color.svg +0 -1
- package/src/assets/IDE/opencode.svg +0 -1
- package/src/assets/IDE/xiaomimimo.svg +0 -1
- package/src/assets/favicon.svg +0 -21
- package/src/assets/logos/alibaba.svg +0 -1
- package/src/assets/logos/anthropic.svg +0 -1
- package/src/assets/logos/claude-code.svg +0 -4
- package/src/assets/logos/deepseek.svg +0 -1
- package/src/assets/logos/minimax.jpeg +0 -0
- package/src/assets/logos/openai.svg +0 -1
- package/src/assets/logos/opencode.svg +0 -4
- package/src/assets/logos/qwen.webp +0 -0
- package/src/assets/logos/zhipuai.svg +0 -219
- package/src/backend/router.tsx +0 -36
- package/src/backend/routes/$.ts +0 -26
- package/src/backend/routes/__root.ts +0 -4
- package/src/backend/routes/api/-instances.ts +0 -7
- package/src/backend/routes/api/alerts.summary.ts +0 -28
- package/src/backend/routes/api/alerts.ts +0 -70
- package/src/backend/routes/api/config.paths.ts +0 -14
- package/src/backend/routes/api/config.ts +0 -73
- package/src/backend/routes/api/ecosystem.packages.$packageId.help.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.$packageId.install.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.$packageId.runner-presets.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.$packageId.upgrade.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.ts +0 -21
- package/src/backend/routes/api/ecosystem.recipes.$recipeId.run.ts +0 -11
- package/src/backend/routes/api/ecosystem.tasks.$taskId.ts +0 -16
- package/src/backend/routes/api/ecosystem.tasks.ts +0 -12
- package/src/backend/routes/api/groups.$groupId.evidence.ts +0 -55
- package/src/backend/routes/api/groups.$groupId.sessions.ts +0 -50
- package/src/backend/routes/api/groups.$groupId.ts +0 -93
- package/src/backend/routes/api/groups.ts +0 -79
- package/src/backend/routes/api/health.ts +0 -22
- package/src/backend/routes/api/instances.$name.connection.ts +0 -12
- package/src/backend/routes/api/instances.$name.restart.ts +0 -23
- package/src/backend/routes/api/instances.$name.start.ts +0 -18
- package/src/backend/routes/api/instances.$name.stop.ts +0 -23
- package/src/backend/routes/api/instances.$name.ts +0 -12
- package/src/backend/routes/api/instances.ts +0 -16
- package/src/backend/routes/api/knowledge.candidates.$candidateId.promote.ts +0 -32
- package/src/backend/routes/api/knowledge.candidates.$candidateId.ts +0 -50
- package/src/backend/routes/api/knowledge.candidates.ts +0 -10
- package/src/backend/routes/api/knowledge.project-context.ts +0 -29
- package/src/backend/routes/api/knowledge.search.ts +0 -62
- package/src/backend/routes/api/knowledge.sessions.$sessionId.candidates.ts +0 -26
- package/src/backend/routes/api/logs.$id.body.ts +0 -58
- package/src/backend/routes/api/logs.$id.chunks.ts +0 -37
- package/src/backend/routes/api/logs.$id.replay.ts +0 -297
- package/src/backend/routes/api/logs.$id.ts +0 -23
- package/src/backend/routes/api/logs.import.ts +0 -39
- package/src/backend/routes/api/logs.stream.ts +0 -174
- package/src/backend/routes/api/logs.ts +0 -214
- package/src/backend/routes/api/mcp.ts +0 -25
- package/src/backend/routes/api/models.ts +0 -10
- package/src/backend/routes/api/pi-agent.ts +0 -29
- package/src/backend/routes/api/providers.$providerId.model-metadata.ts +0 -173
- package/src/backend/routes/api/providers.$providerId.test.log.ts +0 -342
- package/src/backend/routes/api/providers.$providerId.ts +0 -111
- package/src/backend/routes/api/providers.export.ts +0 -44
- package/src/backend/routes/api/providers.import.ts +0 -81
- package/src/backend/routes/api/providers.scan.ts +0 -28
- package/src/backend/routes/api/providers.ts +0 -102
- package/src/backend/routes/api/runs.$runId.evidence.ts +0 -55
- package/src/backend/routes/api/runs.$runId.ts +0 -50
- package/src/backend/routes/api/runs.ts +0 -62
- package/src/backend/routes/api/runtime.ts +0 -46
- package/src/backend/routes/api/sessions.ts +0 -51
- package/src/backend/routes/api/storage.ts +0 -56
- package/src/backend/routes/livez.ts +0 -13
- package/src/backend/routes/metrics.ts +0 -14
- package/src/backend/routes/proxy/$.ts +0 -15
- package/src/backend/routes/readyz.ts +0 -18
- package/src/backend/start.ts +0 -10
- package/src/cli/alias.ts +0 -431
- package/src/cli/detect-tools.ts +0 -123
- package/src/cli/doctor.ts +0 -836
- package/src/cli/instance.ts +0 -1071
- package/src/cli/instanceApi.ts +0 -213
- package/src/cli/instanceArgs.ts +0 -301
- package/src/cli/instanceControl.ts +0 -191
- package/src/cli/instanceModel.ts +0 -276
- package/src/cli/instanceRegistry.ts +0 -731
- package/src/cli/networkHints.ts +0 -170
- package/src/cli/onboard.ts +0 -1668
- package/src/cli/onboardTarget.ts +0 -228
- package/src/cli/rustBackendPackage.ts +0 -458
- package/src/cli/startupOutput.ts +0 -16
- package/src/cli/templates/codex-skill-onboard.ts +0 -320
- package/src/cli/templates/command-onboard.ts +0 -25
- package/src/cli/templates/skill-onboard.ts +0 -625
- package/src/cli.ts +0 -1433
- package/src/components/OnboardingBanner.tsx +0 -115
- package/src/components/ProxyViewer.tsx +0 -2259
- package/src/components/ProxyViewerContainer.tsx +0 -1076
- package/src/components/alerts/AlertsDialog.tsx +0 -615
- package/src/components/clients/ClientLogo.tsx +0 -175
- package/src/components/ecosystem/AgentLabDialog.tsx +0 -947
- package/src/components/errors/SafeErrorBoundary.tsx +0 -201
- package/src/components/groups/GroupsDialog.tsx +0 -1304
- package/src/components/inspector-pet/InspectorPet.tsx +0 -398
- package/src/components/inspector-pet/InspectorPetCrab3D.tsx +0 -455
- package/src/components/inspector-pet/inspectorPetLogic.ts +0 -272
- package/src/components/pi-agent/PiAgentPanel.tsx +0 -446
- package/src/components/pi-agent/piAgentChatLogic.ts +0 -273
- package/src/components/providers/ImportWizardDialog.tsx +0 -281
- package/src/components/providers/ProviderCard.tsx +0 -347
- package/src/components/providers/ProviderForm.tsx +0 -1183
- package/src/components/providers/ProviderLogo.tsx +0 -122
- package/src/components/providers/ProviderTestResultsView.tsx +0 -734
- package/src/components/providers/ProvidersPanel.tsx +0 -714
- package/src/components/providers/SettingsDialog.tsx +0 -1042
- package/src/components/providers/providerFormCapabilities.ts +0 -36
- package/src/components/proxy-viewer/AgentTraceSummary.tsx +0 -902
- package/src/components/proxy-viewer/AnswerMarkdown.tsx +0 -51
- package/src/components/proxy-viewer/ApplicationBar.tsx +0 -123
- package/src/components/proxy-viewer/CompareDrawer.tsx +0 -984
- package/src/components/proxy-viewer/ConversationGroup.tsx +0 -208
- package/src/components/proxy-viewer/ConversationGroupList.tsx +0 -652
- package/src/components/proxy-viewer/ConversationHeader.tsx +0 -429
- package/src/components/proxy-viewer/LiveConnectionStatus.tsx +0 -186
- package/src/components/proxy-viewer/LogEntry.tsx +0 -1419
- package/src/components/proxy-viewer/LogEntryHeader.tsx +0 -596
- package/src/components/proxy-viewer/ProviderLogoStack.tsx +0 -57
- package/src/components/proxy-viewer/RawExportWarning.tsx +0 -77
- package/src/components/proxy-viewer/ReplayDialog.tsx +0 -636
- package/src/components/proxy-viewer/RequestToolsPanel.tsx +0 -299
- package/src/components/proxy-viewer/ResponseView.tsx +0 -202
- package/src/components/proxy-viewer/SessionLoadProgressBar.tsx +0 -61
- package/src/components/proxy-viewer/StreamingChunkSequence.tsx +0 -265
- package/src/components/proxy-viewer/ThreadConnector.tsx +0 -165
- package/src/components/proxy-viewer/ToolTraceEvents.tsx +0 -64
- package/src/components/proxy-viewer/TurnGroup.tsx +0 -383
- package/src/components/proxy-viewer/TurnGroupList.tsx +0 -156
- package/src/components/proxy-viewer/accessibility.ts +0 -8
- package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +0 -887
- package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +0 -222
- package/src/components/proxy-viewer/anatomy/contextIntelligence.ts +0 -737
- package/src/components/proxy-viewer/anatomy/sessionContextSummary.ts +0 -268
- package/src/components/proxy-viewer/anatomy/tokenEstimate.ts +0 -53
- package/src/components/proxy-viewer/anatomy/types.ts +0 -41
- package/src/components/proxy-viewer/anatomy/useAnatomyJump.ts +0 -114
- package/src/components/proxy-viewer/bodyHydration.ts +0 -123
- package/src/components/proxy-viewer/cacheTrend.ts +0 -50
- package/src/components/proxy-viewer/diff/DiffView.tsx +0 -321
- package/src/components/proxy-viewer/diff/computeDiff.ts +0 -178
- package/src/components/proxy-viewer/diff/index.ts +0 -3
- package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +0 -194
- package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +0 -81
- package/src/components/proxy-viewer/formats/anthropic/thinkingExtract.ts +0 -21
- package/src/components/proxy-viewer/formats/index.tsx +0 -47
- package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +0 -442
- package/src/components/proxy-viewer/index.ts +0 -9
- package/src/components/proxy-viewer/lazy.ts +0 -41
- package/src/components/proxy-viewer/liveConnectionState.ts +0 -158
- package/src/components/proxy-viewer/log-formats/anthropic.ts +0 -191
- package/src/components/proxy-viewer/log-formats/index.ts +0 -23
- package/src/components/proxy-viewer/log-formats/openai.ts +0 -256
- package/src/components/proxy-viewer/log-formats/types.ts +0 -40
- package/src/components/proxy-viewer/log-formats/unknown.ts +0 -18
- package/src/components/proxy-viewer/logEntryVisibility.ts +0 -73
- package/src/components/proxy-viewer/logFocus.ts +0 -54
- package/src/components/proxy-viewer/proxyViewerContainerLogic.ts +0 -124
- package/src/components/proxy-viewer/proxyViewerLogic.ts +0 -488
- package/src/components/proxy-viewer/replayComparison.ts +0 -131
- package/src/components/proxy-viewer/requestDiff.ts +0 -277
- package/src/components/proxy-viewer/requestTools.ts +0 -220
- package/src/components/proxy-viewer/useCopyFeedback.ts +0 -38
- package/src/components/proxy-viewer/useKeyboardNavigation.ts +0 -232
- package/src/components/proxy-viewer/viewerState.ts +0 -176
- package/src/components/ui/badge.tsx +0 -47
- package/src/components/ui/button.tsx +0 -47
- package/src/components/ui/collapsible.tsx +0 -21
- package/src/components/ui/confirm-dialog.tsx +0 -51
- package/src/components/ui/crab-logo.tsx +0 -45
- package/src/components/ui/crab-variants.tsx +0 -467
- package/src/components/ui/dialog.tsx +0 -130
- package/src/components/ui/icon-trigger.ts +0 -2
- package/src/components/ui/json-viewer-bulk.ts +0 -132
- package/src/components/ui/json-viewer.tsx +0 -508
- package/src/components/ui/scroll-area.tsx +0 -54
- package/src/components/ui/select.tsx +0 -178
- package/src/components/ui/separator.tsx +0 -28
- package/src/components/ui/tabs.tsx +0 -88
- package/src/components/ui/tooltip.tsx +0 -51
- package/src/components/ui/transient-toast.tsx +0 -105
- package/src/contracts/anthropic.ts +0 -230
- package/src/contracts/index.ts +0 -59
- package/src/contracts/json.ts +0 -37
- package/src/contracts/log.ts +0 -139
- package/src/contracts/openai.ts +0 -267
- package/src/index.css +0 -11
- package/src/knowledge/candidateStore.ts +0 -94
- package/src/knowledge/distiller.ts +0 -98
- package/src/knowledge/openclawClient.ts +0 -161
- package/src/knowledge/openclawGatewayClient.ts +0 -249
- package/src/knowledge/openclawMarkdown.ts +0 -146
- package/src/knowledge/redactor.ts +0 -80
- package/src/knowledge/types.ts +0 -83
- package/src/lib/alertContract.ts +0 -76
- package/src/lib/apiClient.ts +0 -166
- package/src/lib/apiFormat.ts +0 -29
- package/src/lib/backendImplementation.ts +0 -16
- package/src/lib/basePath.ts +0 -107
- package/src/lib/browserRuntimeContract.ts +0 -124
- package/src/lib/browserRuntimeResolver.ts +0 -333
- package/src/lib/chunkLoadRecovery.ts +0 -83
- package/src/lib/claudeCodeStrip.ts +0 -98
- package/src/lib/clipboard.ts +0 -67
- package/src/lib/codingAgentEndpoints.ts +0 -54
- package/src/lib/ecosystemContract.ts +0 -130
- package/src/lib/export-logs.ts +0 -287
- package/src/lib/groupContract.ts +0 -166
- package/src/lib/instanceContract.ts +0 -239
- package/src/lib/jsonFile.ts +0 -46
- package/src/lib/logImportContract.ts +0 -12
- package/src/lib/managedInstance.ts +0 -112
- package/src/lib/mask.ts +0 -4
- package/src/lib/objectUtils.ts +0 -22
- package/src/lib/piAgentContract.ts +0 -102
- package/src/lib/providerContract.ts +0 -125
- package/src/lib/providerImportContract.ts +0 -27
- package/src/lib/providerModelMetadata.ts +0 -458
- package/src/lib/providerTestContract.ts +0 -170
- package/src/lib/providerTestPrompt.ts +0 -100
- package/src/lib/publicBasePathContext.ts +0 -137
- package/src/lib/resourceLimits.ts +0 -345
- package/src/lib/runContract.ts +0 -164
- package/src/lib/runtimeConfig.ts +0 -43
- package/src/lib/safeDiagnostic.ts +0 -38
- package/src/lib/safeFetch.ts +0 -508
- package/src/lib/serverPort.ts +0 -47
- package/src/lib/sessionInfoContract.ts +0 -78
- package/src/lib/sessionUrl.ts +0 -46
- package/src/lib/ssrfGuard.ts +0 -133
- package/src/lib/stopReason.ts +0 -130
- package/src/lib/themeMode.ts +0 -41
- package/src/lib/timeDisplay.ts +0 -22
- package/src/lib/toolTrace.ts +0 -290
- package/src/lib/uiScale.ts +0 -117
- package/src/lib/unknown.ts +0 -27
- package/src/lib/upstreamUrl.ts +0 -150
- package/src/lib/useAlerts.ts +0 -82
- package/src/lib/useGroupEvidence.ts +0 -37
- package/src/lib/useGroups.ts +0 -32
- package/src/lib/useOnboarding.ts +0 -93
- package/src/lib/useProviders.ts +0 -30
- package/src/lib/useStripConfig.ts +0 -154
- package/src/lib/useThemeMode.ts +0 -40
- package/src/lib/useUiScale.ts +0 -52
- package/src/lib/utils.ts +0 -78
- package/src/mcp/currentContext.ts +0 -65
- package/src/mcp/instanceHandlers.ts +0 -151
- package/src/mcp/loopback.ts +0 -88
- package/src/mcp/mode.ts +0 -75
- package/src/mcp/previewExtractor.ts +0 -233
- package/src/mcp/server.ts +0 -2274
- package/src/mcp/toolHandlers.ts +0 -1533
- package/src/proxy/alerts.ts +0 -664
- package/src/proxy/chunkStorage.ts +0 -123
- package/src/proxy/config.ts +0 -218
- package/src/proxy/constants.ts +0 -66
- package/src/proxy/dataDir.ts +0 -147
- package/src/proxy/ecosystemExecutionConfirmation.ts +0 -114
- package/src/proxy/ecosystemExecutionRoute.ts +0 -116
- package/src/proxy/ecosystemRegistry.ts +0 -296
- package/src/proxy/ecosystemTasks.ts +0 -415
- package/src/proxy/evidenceAnalysis.ts +0 -526
- package/src/proxy/evidenceExporter.ts +0 -235
- package/src/proxy/formats/anthropic/anthropicProvider.ts +0 -75
- package/src/proxy/formats/anthropic/handler.ts +0 -75
- package/src/proxy/formats/anthropic/index.ts +0 -13
- package/src/proxy/formats/anthropic/register.ts +0 -4
- package/src/proxy/formats/anthropic/schemas.ts +0 -6
- package/src/proxy/formats/anthropic/stream.ts +0 -205
- package/src/proxy/formats/handler.ts +0 -46
- package/src/proxy/formats/index.ts +0 -13
- package/src/proxy/formats/jsonSchema.ts +0 -2
- package/src/proxy/formats/openai/alibabaProvider.ts +0 -34
- package/src/proxy/formats/openai/codexMetadata.ts +0 -61
- package/src/proxy/formats/openai/handler.ts +0 -133
- package/src/proxy/formats/openai/index.ts +0 -33
- package/src/proxy/formats/openai/provider.ts +0 -50
- package/src/proxy/formats/openai/register.ts +0 -4
- package/src/proxy/formats/openai/schemas.ts +0 -25
- package/src/proxy/formats/openai/stream.ts +0 -600
- package/src/proxy/formats/openai/zhipuProvider.ts +0 -31
- package/src/proxy/formats/providerAdapter.ts +0 -50
- package/src/proxy/formats/providerRegistry.ts +0 -51
- package/src/proxy/formats/providers/index.ts +0 -3
- package/src/proxy/formats/registry.ts +0 -65
- package/src/proxy/groupEvidenceExporter.ts +0 -371
- package/src/proxy/groupStore.ts +0 -336
- package/src/proxy/handler.ts +0 -729
- package/src/proxy/identityProxy.ts +0 -1842
- package/src/proxy/jsonlRecovery.ts +0 -554
- package/src/proxy/logBodyChunks.ts +0 -91
- package/src/proxy/logFinalizer.ts +0 -777
- package/src/proxy/logFinalizer.worker.ts +0 -24
- package/src/proxy/logImportUpload.ts +0 -128
- package/src/proxy/logImporter.ts +0 -813
- package/src/proxy/logIndex.ts +0 -663
- package/src/proxy/logSearch.ts +0 -122
- package/src/proxy/logger.ts +0 -537
- package/src/proxy/openaiOrphanToolStrip.ts +0 -142
- package/src/proxy/platformCommands.ts +0 -258
- package/src/proxy/privateDataPath.ts +0 -419
- package/src/proxy/providerImporters.ts +0 -578
- package/src/proxy/providerScanStore.ts +0 -87
- package/src/proxy/providerSecretStore.ts +0 -216
- package/src/proxy/providers.ts +0 -998
- package/src/proxy/rawStreamCapture.ts +0 -342
- package/src/proxy/runFailures.ts +0 -94
- package/src/proxy/runStore.ts +0 -214
- package/src/proxy/runtimeAdmission.ts +0 -55
- package/src/proxy/runtimeHealth.ts +0 -221
- package/src/proxy/runtimeMetrics.ts +0 -149
- package/src/proxy/runtimeShutdown.ts +0 -157
- package/src/proxy/schemas.ts +0 -112
- package/src/proxy/sessionArchive.ts +0 -479
- package/src/proxy/sessionInfo.ts +0 -266
- package/src/proxy/sessionProcess.ts +0 -194
- package/src/proxy/sessionRuntime.ts +0 -121
- package/src/proxy/sessionSupervisor.ts +0 -305
- package/src/proxy/sessionWorkerEntry.ts +0 -26
- package/src/proxy/shutdownCoordinator.ts +0 -90
- package/src/proxy/socketTracker.ts +0 -639
- package/src/proxy/sqliteLogIndex.ts +0 -733
- package/src/proxy/storageLifecycle.ts +0 -432
- package/src/proxy/store.ts +0 -1773
- package/src/proxy/streamTiming.ts +0 -31
- package/src/proxy/toolSchemaWarnings.ts +0 -529
- package/src/proxy/uiAssetServer.ts +0 -106
- package/src/proxy/upstream.ts +0 -127
- package/src/services/alerts.ts +0 -12
- package/src/services/groups.ts +0 -119
- package/src/services/piAgent.ts +0 -421
- package/src/services/piAgentRetrieval.ts +0 -1180
- package/src/services/result.ts +0 -17
- package/src/services/runs.ts +0 -76
- package/src/ui/bootstrap.ts +0 -167
- package/src/ui/index.html +0 -38
- package/src/ui/logsStreamClient.ts +0 -20
- package/src/ui/main.tsx +0 -13
- package/src/ui/public/runtime-config.js +0 -2
- package/src/ui/router.tsx +0 -13
- package/src/ui/routes/__root.tsx +0 -69
- package/src/ui/routes/index.tsx +0 -35
- package/src/ui/routes/session/$sessionId.tsx +0 -50
- package/src/ui/runtimeDiscovery.ts +0 -50
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { useCallback, useMemo, useState, useTransition } from "react";
|
|
2
|
-
|
|
3
|
-
type JsonPrimitive = string | number | boolean | null;
|
|
4
|
-
type JsonObject = Readonly<{ [key: string]: JsonValue }>;
|
|
5
|
-
export type JsonValue = JsonPrimitive | ReadonlyArray<JsonValue> | JsonObject;
|
|
6
|
-
|
|
7
|
-
export type JsonExpansionPolicy = {
|
|
8
|
-
depth: number;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const FULL_EXPAND_NODE_LIMIT = 400;
|
|
12
|
-
const STRUCTURE_EXPAND_NODE_LIMIT = 2_000;
|
|
13
|
-
|
|
14
|
-
function isJsonArray(value: JsonValue): value is ReadonlyArray<JsonValue> {
|
|
15
|
-
return Array.isArray(value);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function countJsonNodesUpTo(value: JsonValue, limit: number): number {
|
|
19
|
-
const stack: JsonValue[] = [value];
|
|
20
|
-
let count = 0;
|
|
21
|
-
|
|
22
|
-
while (stack.length > 0) {
|
|
23
|
-
const current = stack.pop();
|
|
24
|
-
if (current === undefined) continue;
|
|
25
|
-
count += 1;
|
|
26
|
-
if (count > limit) return count;
|
|
27
|
-
|
|
28
|
-
if (current === null || typeof current !== "object") continue;
|
|
29
|
-
if (isJsonArray(current)) {
|
|
30
|
-
for (const child of current) {
|
|
31
|
-
stack.push(child);
|
|
32
|
-
}
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
for (const key of Object.keys(current)) {
|
|
37
|
-
const child = current[key];
|
|
38
|
-
if (child !== undefined) stack.push(child);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return count;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function getJsonExpansionPolicy(value: JsonValue): JsonExpansionPolicy {
|
|
46
|
-
const nodeCount = countJsonNodesUpTo(value, STRUCTURE_EXPAND_NODE_LIMIT + 1);
|
|
47
|
-
if (nodeCount <= FULL_EXPAND_NODE_LIMIT) return { depth: Number.POSITIVE_INFINITY };
|
|
48
|
-
if (nodeCount <= STRUCTURE_EXPAND_NODE_LIMIT) return { depth: 3 };
|
|
49
|
-
return { depth: 2 };
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export type JsonBulkExpansion = {
|
|
53
|
-
parsedData: JsonValue | null;
|
|
54
|
-
policy: JsonExpansionPolicy | null;
|
|
55
|
-
isExpanded: boolean;
|
|
56
|
-
toggle: () => void;
|
|
57
|
-
isPending: boolean;
|
|
58
|
-
bulkDepth: number;
|
|
59
|
-
bulkRevision: number;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
type ParsedJsonText =
|
|
63
|
-
| {
|
|
64
|
-
kind: "json";
|
|
65
|
-
data: JsonValue;
|
|
66
|
-
}
|
|
67
|
-
| {
|
|
68
|
-
kind: "text";
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
export function parseJsonText(text: string): ParsedJsonText {
|
|
72
|
-
try {
|
|
73
|
-
const parsed: unknown = JSON.parse(text);
|
|
74
|
-
return { kind: "json", data: safeJsonValue(parsed) };
|
|
75
|
-
} catch {
|
|
76
|
-
return { kind: "text" };
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function useJsonBulkExpansion(text: string | null): JsonBulkExpansion {
|
|
81
|
-
const parsed = useMemo(() => (text === null ? null : parseJsonText(text)), [text]);
|
|
82
|
-
const parsedData = parsed?.kind === "json" ? parsed.data : null;
|
|
83
|
-
const policy = useMemo(
|
|
84
|
-
() => (parsedData === null ? null : getJsonExpansionPolicy(parsedData)),
|
|
85
|
-
[parsedData],
|
|
86
|
-
);
|
|
87
|
-
const [isExpanded, setIsExpanded] = useState(false);
|
|
88
|
-
const [bulkDepth, setBulkDepth] = useState(0);
|
|
89
|
-
const [bulkRevision, setBulkRevision] = useState(0);
|
|
90
|
-
const [isPending, startTransition] = useTransition();
|
|
91
|
-
|
|
92
|
-
const toggle = useCallback(() => {
|
|
93
|
-
const nextExpanded = !isExpanded;
|
|
94
|
-
const targetDepth = nextExpanded && policy !== null ? policy.depth : 0;
|
|
95
|
-
startTransition(() => {
|
|
96
|
-
setIsExpanded(nextExpanded);
|
|
97
|
-
setBulkDepth(targetDepth);
|
|
98
|
-
setBulkRevision((current) => current + 1);
|
|
99
|
-
});
|
|
100
|
-
}, [isExpanded, policy]);
|
|
101
|
-
|
|
102
|
-
return { parsedData, policy, isExpanded, toggle, isPending, bulkDepth, bulkRevision };
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export function safeJsonValue(value: unknown): JsonValue {
|
|
106
|
-
if (value === null || value === undefined) return null;
|
|
107
|
-
switch (typeof value) {
|
|
108
|
-
case "string":
|
|
109
|
-
return value;
|
|
110
|
-
case "number":
|
|
111
|
-
return value;
|
|
112
|
-
case "boolean":
|
|
113
|
-
return value;
|
|
114
|
-
case "object": {
|
|
115
|
-
if (Array.isArray(value)) {
|
|
116
|
-
return value.map((item: unknown) => safeJsonValue(item));
|
|
117
|
-
}
|
|
118
|
-
const result: Record<string, JsonValue> = {};
|
|
119
|
-
for (const key of Object.keys(value)) {
|
|
120
|
-
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
121
|
-
result[key] = safeJsonValue(descriptor?.value);
|
|
122
|
-
}
|
|
123
|
-
return result;
|
|
124
|
-
}
|
|
125
|
-
case "bigint":
|
|
126
|
-
case "symbol":
|
|
127
|
-
case "function":
|
|
128
|
-
case "undefined":
|
|
129
|
-
return String(value);
|
|
130
|
-
}
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
@@ -1,508 +0,0 @@
|
|
|
1
|
-
import { Check, ChevronDown, ChevronRight, ChevronsDown, Copy } from "lucide-react";
|
|
2
|
-
import { type JSX, memo, useEffect, useMemo, useState } from "react";
|
|
3
|
-
import { copyTextToClipboard } from "../../lib/clipboard";
|
|
4
|
-
import { cn } from "../../lib/utils";
|
|
5
|
-
import { Button } from "./button";
|
|
6
|
-
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip";
|
|
7
|
-
import type { JsonValue } from "./json-viewer-bulk";
|
|
8
|
-
import { parseJsonText, safeJsonValue } from "./json-viewer-bulk";
|
|
9
|
-
|
|
10
|
-
type DataType = "string" | "number" | "boolean" | "null" | "array" | "object";
|
|
11
|
-
|
|
12
|
-
function classifyValue(value: JsonValue): DataType {
|
|
13
|
-
if (value === null) return "null";
|
|
14
|
-
if (Array.isArray(value)) return "array";
|
|
15
|
-
switch (typeof value) {
|
|
16
|
-
case "string":
|
|
17
|
-
return "string";
|
|
18
|
-
case "number":
|
|
19
|
-
return "number";
|
|
20
|
-
case "boolean":
|
|
21
|
-
return "boolean";
|
|
22
|
-
case "object":
|
|
23
|
-
return "object";
|
|
24
|
-
case "bigint":
|
|
25
|
-
case "symbol":
|
|
26
|
-
case "undefined":
|
|
27
|
-
case "function":
|
|
28
|
-
return "object";
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function isExpandable(value: JsonValue): boolean {
|
|
33
|
-
return value !== null && (Array.isArray(value) || typeof value === "object");
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function getEntries(value: JsonValue): ReadonlyArray<readonly [string, JsonValue]> {
|
|
37
|
-
if (Array.isArray(value)) {
|
|
38
|
-
return value.map((item, index): readonly [string, JsonValue] => [String(index), item]);
|
|
39
|
-
}
|
|
40
|
-
if (typeof value === "object" && value !== null) {
|
|
41
|
-
return Object.entries(value);
|
|
42
|
-
}
|
|
43
|
-
return [];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const STRING_TRUNCATE_LIMIT = 120;
|
|
47
|
-
|
|
48
|
-
function JsonStringText({ text }: { text: string }): JSX.Element {
|
|
49
|
-
return <span className="whitespace-pre-wrap break-words">{text}</span>;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function StringValue({ text }: { text: string }): JSX.Element {
|
|
53
|
-
const [expanded, setExpanded] = useState(false);
|
|
54
|
-
const isLong = text.length > STRING_TRUNCATE_LIMIT;
|
|
55
|
-
|
|
56
|
-
if (!isLong) {
|
|
57
|
-
return (
|
|
58
|
-
<span className="text-emerald-400 break-all">
|
|
59
|
-
"
|
|
60
|
-
<JsonStringText text={text} />
|
|
61
|
-
"
|
|
62
|
-
</span>
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return (
|
|
67
|
-
<span className="text-emerald-400 break-all">
|
|
68
|
-
"
|
|
69
|
-
{expanded ? (
|
|
70
|
-
<span
|
|
71
|
-
className="cursor-pointer"
|
|
72
|
-
onClick={(e) => {
|
|
73
|
-
e.stopPropagation();
|
|
74
|
-
setExpanded(false);
|
|
75
|
-
}}
|
|
76
|
-
onKeyDown={(e) => {
|
|
77
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
78
|
-
e.stopPropagation();
|
|
79
|
-
setExpanded(false);
|
|
80
|
-
}
|
|
81
|
-
}}
|
|
82
|
-
role="button"
|
|
83
|
-
tabIndex={0}
|
|
84
|
-
>
|
|
85
|
-
<JsonStringText text={text} />
|
|
86
|
-
</span>
|
|
87
|
-
) : (
|
|
88
|
-
<Tooltip delayDuration={300}>
|
|
89
|
-
<TooltipTrigger
|
|
90
|
-
onClick={(e) => {
|
|
91
|
-
e.stopPropagation();
|
|
92
|
-
setExpanded(true);
|
|
93
|
-
}}
|
|
94
|
-
className="text-left cursor-pointer"
|
|
95
|
-
>
|
|
96
|
-
<span>{text.slice(0, STRING_TRUNCATE_LIMIT)}</span>
|
|
97
|
-
<span className="text-emerald-400/50">...</span>
|
|
98
|
-
</TooltipTrigger>
|
|
99
|
-
<TooltipContent
|
|
100
|
-
side="bottom"
|
|
101
|
-
className="max-w-md text-xs p-2 break-words whitespace-pre-wrap"
|
|
102
|
-
>
|
|
103
|
-
{text.slice(0, 500)}
|
|
104
|
-
{text.length > 500 ? "..." : ""}
|
|
105
|
-
</TooltipContent>
|
|
106
|
-
</Tooltip>
|
|
107
|
-
)}
|
|
108
|
-
"
|
|
109
|
-
</span>
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function PrimitiveValue({ value }: { value: JsonValue }): JSX.Element {
|
|
114
|
-
if (value === null) {
|
|
115
|
-
return <span className="text-rose-400 italic">null</span>;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
switch (typeof value) {
|
|
119
|
-
case "string":
|
|
120
|
-
return <StringValue text={value} />;
|
|
121
|
-
case "number":
|
|
122
|
-
return <span className="text-amber-400">{value}</span>;
|
|
123
|
-
case "boolean":
|
|
124
|
-
return <span className="text-blue-400">{value ? "true" : "false"}</span>;
|
|
125
|
-
case "object":
|
|
126
|
-
case "bigint":
|
|
127
|
-
case "symbol":
|
|
128
|
-
case "undefined":
|
|
129
|
-
case "function":
|
|
130
|
-
return <span className="text-muted-foreground">{JSON.stringify(value)}</span>;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function CopyValueButton({ value }: { value: JsonValue }): JSX.Element {
|
|
135
|
-
const [copied, setCopied] = useState(false);
|
|
136
|
-
|
|
137
|
-
function handleCopy(e: React.MouseEvent): void {
|
|
138
|
-
e.stopPropagation();
|
|
139
|
-
void copyTextToClipboard(JSON.stringify(value, null, 2)).then((success) => {
|
|
140
|
-
if (!success) return;
|
|
141
|
-
setCopied(true);
|
|
142
|
-
setTimeout(() => {
|
|
143
|
-
setCopied(false);
|
|
144
|
-
}, 2000);
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return (
|
|
149
|
-
<button
|
|
150
|
-
type="button"
|
|
151
|
-
onClick={handleCopy}
|
|
152
|
-
className="inline-flex items-center gap-1 text-[10px] text-muted-foreground hover:text-foreground transition-colors"
|
|
153
|
-
aria-label={copied ? "Copied JSON" : "Copy JSON"}
|
|
154
|
-
title="Copy JSON"
|
|
155
|
-
>
|
|
156
|
-
{copied ? <Check className="size-3 text-green-500" /> : <Copy className="size-3" />}
|
|
157
|
-
</button>
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function CopyButton({ value }: { value: JsonValue }): JSX.Element {
|
|
162
|
-
const [copied, setCopied] = useState(false);
|
|
163
|
-
|
|
164
|
-
function handleCopy(e: React.MouseEvent): void {
|
|
165
|
-
e.stopPropagation();
|
|
166
|
-
void copyTextToClipboard(JSON.stringify(value, null, 2)).then((success) => {
|
|
167
|
-
if (!success) return;
|
|
168
|
-
setCopied(true);
|
|
169
|
-
setTimeout(() => {
|
|
170
|
-
setCopied(false);
|
|
171
|
-
}, 2000);
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return (
|
|
176
|
-
<button
|
|
177
|
-
type="button"
|
|
178
|
-
onClick={handleCopy}
|
|
179
|
-
className="opacity-0 group-hover/row:opacity-100 hover:bg-muted p-0.5 rounded transition-opacity shrink-0"
|
|
180
|
-
aria-label={copied ? "Copied JSON value" : "Copy JSON value"}
|
|
181
|
-
title="Copy to clipboard"
|
|
182
|
-
>
|
|
183
|
-
{copied ? (
|
|
184
|
-
<Check className="size-3 text-green-500" />
|
|
185
|
-
) : (
|
|
186
|
-
<Copy className="size-3 text-muted-foreground" />
|
|
187
|
-
)}
|
|
188
|
-
</button>
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
type JsonNodeProps = {
|
|
193
|
-
name: string;
|
|
194
|
-
value: JsonValue;
|
|
195
|
-
level: number;
|
|
196
|
-
defaultExpandDepth: number;
|
|
197
|
-
isArrayItem: boolean;
|
|
198
|
-
/** Full JSON-pointer-style path to this node (e.g. "/messages/3"). */
|
|
199
|
-
path: string;
|
|
200
|
-
/** When set, ancestors of this path auto-expand for one render. */
|
|
201
|
-
expandTargetPath: string | null;
|
|
202
|
-
/** Set of paths that should expose `data-anatomy-path` on their row. */
|
|
203
|
-
anatomyPaths: Set<string> | null;
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
function ExpandCollapseButton({
|
|
207
|
-
onClick,
|
|
208
|
-
}: {
|
|
209
|
-
onClick: (e: React.MouseEvent) => void;
|
|
210
|
-
}): JSX.Element {
|
|
211
|
-
return (
|
|
212
|
-
<button
|
|
213
|
-
type="button"
|
|
214
|
-
onClick={onClick}
|
|
215
|
-
className="opacity-0 group-hover/row:opacity-100 hover:bg-muted p-0.5 rounded transition-opacity shrink-0"
|
|
216
|
-
title="Expand all descendants"
|
|
217
|
-
>
|
|
218
|
-
<ChevronsDown className="size-3.5 text-muted-foreground" />
|
|
219
|
-
</button>
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow -- named function gives React DevTools a readable displayName
|
|
224
|
-
const JsonNode = memo(function JsonNode({
|
|
225
|
-
name,
|
|
226
|
-
value,
|
|
227
|
-
level,
|
|
228
|
-
defaultExpandDepth,
|
|
229
|
-
isArrayItem,
|
|
230
|
-
path,
|
|
231
|
-
expandTargetPath,
|
|
232
|
-
anatomyPaths,
|
|
233
|
-
}: JsonNodeProps): JSX.Element {
|
|
234
|
-
// A node is an ancestor of the target if the target starts with this
|
|
235
|
-
// node's path followed by a path separator. The root path is "" and
|
|
236
|
-
// is an ancestor of every non-empty path.
|
|
237
|
-
const isAncestorOfTarget = useMemo(() => {
|
|
238
|
-
if (expandTargetPath === null) return false;
|
|
239
|
-
if (path === "") return expandTargetPath.length > 0;
|
|
240
|
-
return expandTargetPath === path || expandTargetPath.startsWith(`${path}/`);
|
|
241
|
-
}, [expandTargetPath, path]);
|
|
242
|
-
|
|
243
|
-
const initialExpanded = level < defaultExpandDepth || (isAncestorOfTarget && isExpandable(value));
|
|
244
|
-
|
|
245
|
-
const [expanded, setExpanded] = useState(initialExpanded);
|
|
246
|
-
// Re-evaluate on every render so a freshly-changed expandTargetPath
|
|
247
|
-
// immediately auto-expands a previously-collapsed ancestor.
|
|
248
|
-
useEffect(() => {
|
|
249
|
-
if (isAncestorOfTarget && isExpandable(value) && !expanded) {
|
|
250
|
-
setExpanded(true);
|
|
251
|
-
}
|
|
252
|
-
// We deliberately only react to expandTargetPath changes here.
|
|
253
|
-
}, [expandTargetPath]);
|
|
254
|
-
|
|
255
|
-
const [childResetKey, setChildResetKey] = useState(0);
|
|
256
|
-
const [childDepthOverride, setChildDepthOverride] = useState<number | null>(null);
|
|
257
|
-
const expandable = isExpandable(value);
|
|
258
|
-
const fullyExpanded = childDepthOverride === Number.POSITIVE_INFINITY;
|
|
259
|
-
const entries = useMemo(() => getEntries(value), [value]);
|
|
260
|
-
const hasExpandableChild = useMemo(
|
|
261
|
-
() => entries.some(([, child]) => isExpandable(child)),
|
|
262
|
-
[entries],
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
const dataType = classifyValue(value);
|
|
266
|
-
const openBracket = dataType === "array" ? "[" : "{";
|
|
267
|
-
const closeBracket = dataType === "array" ? "]" : "}";
|
|
268
|
-
|
|
269
|
-
const hasAnatomyPath = anatomyPaths !== null && anatomyPaths.has(path);
|
|
270
|
-
|
|
271
|
-
function expandAllDeep(): void {
|
|
272
|
-
setExpanded(true);
|
|
273
|
-
setChildDepthOverride(Number.POSITIVE_INFINITY);
|
|
274
|
-
setChildResetKey((k) => k + 1);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function collapseToDefault(): void {
|
|
278
|
-
setExpanded(false);
|
|
279
|
-
setChildDepthOverride(0);
|
|
280
|
-
setChildResetKey((k) => k + 1);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function handleRowToggle(): void {
|
|
284
|
-
if (fullyExpanded) {
|
|
285
|
-
collapseToDefault();
|
|
286
|
-
} else {
|
|
287
|
-
setExpanded(!expanded);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function handleExpandAll(e: React.MouseEvent): void {
|
|
292
|
-
e.stopPropagation();
|
|
293
|
-
expandAllDeep();
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const effectiveChildDepth = childDepthOverride ?? defaultExpandDepth;
|
|
297
|
-
|
|
298
|
-
return (
|
|
299
|
-
<div className={cn(level > 0 && "border-l border-border/50 ml-2")}>
|
|
300
|
-
<div
|
|
301
|
-
className={cn(
|
|
302
|
-
"flex items-start gap-1 py-0.5 px-1 -ml-1 rounded-sm group/row",
|
|
303
|
-
expandable && "cursor-pointer hover:bg-muted/50",
|
|
304
|
-
)}
|
|
305
|
-
data-anatomy-path={hasAnatomyPath ? path : undefined}
|
|
306
|
-
onClick={expandable ? handleRowToggle : undefined}
|
|
307
|
-
onKeyDown={
|
|
308
|
-
expandable
|
|
309
|
-
? (e) => {
|
|
310
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
311
|
-
e.preventDefault();
|
|
312
|
-
handleRowToggle();
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
: undefined
|
|
316
|
-
}
|
|
317
|
-
onDoubleClick={expandable && hasExpandableChild ? () => expandAllDeep() : undefined}
|
|
318
|
-
role={expandable ? "button" : undefined}
|
|
319
|
-
tabIndex={expandable ? 0 : undefined}
|
|
320
|
-
>
|
|
321
|
-
{expandable ? (
|
|
322
|
-
<span className="w-4 h-5 flex items-center justify-center shrink-0">
|
|
323
|
-
{expanded ? (
|
|
324
|
-
<ChevronDown className="size-3 text-muted-foreground" />
|
|
325
|
-
) : (
|
|
326
|
-
<ChevronRight className="size-3 text-muted-foreground" />
|
|
327
|
-
)}
|
|
328
|
-
</span>
|
|
329
|
-
) : (
|
|
330
|
-
<span className="w-4 shrink-0" />
|
|
331
|
-
)}
|
|
332
|
-
|
|
333
|
-
<span className={cn("shrink-0", isArrayItem ? "text-muted-foreground" : "text-cyan-400")}>
|
|
334
|
-
{isArrayItem ? name : `"${name}"`}
|
|
335
|
-
</span>
|
|
336
|
-
|
|
337
|
-
<span className="text-muted-foreground shrink-0">{expandable ? "" : ":"}</span>
|
|
338
|
-
|
|
339
|
-
{expandable ? (
|
|
340
|
-
<span className="text-muted-foreground">
|
|
341
|
-
{openBracket}
|
|
342
|
-
<span className="text-muted-foreground/60 text-xs">
|
|
343
|
-
{" "}
|
|
344
|
-
{entries.length} {entries.length === 1 ? "item" : "items"} {closeBracket}
|
|
345
|
-
</span>
|
|
346
|
-
</span>
|
|
347
|
-
) : (
|
|
348
|
-
<span className="min-w-0">
|
|
349
|
-
<PrimitiveValue value={value} />
|
|
350
|
-
</span>
|
|
351
|
-
)}
|
|
352
|
-
|
|
353
|
-
{expandable && hasExpandableChild && !fullyExpanded && (
|
|
354
|
-
<ExpandCollapseButton onClick={handleExpandAll} />
|
|
355
|
-
)}
|
|
356
|
-
<CopyButton value={value} />
|
|
357
|
-
</div>
|
|
358
|
-
|
|
359
|
-
{expandable && expanded && (
|
|
360
|
-
<div className="pl-4" key={childResetKey}>
|
|
361
|
-
{entries.map(([key, childValue]) => (
|
|
362
|
-
<JsonNode
|
|
363
|
-
key={key}
|
|
364
|
-
name={key}
|
|
365
|
-
value={childValue}
|
|
366
|
-
level={level + 1}
|
|
367
|
-
defaultExpandDepth={effectiveChildDepth}
|
|
368
|
-
isArrayItem={dataType === "array"}
|
|
369
|
-
path={path === "" ? `/${key}` : `${path}/${key}`}
|
|
370
|
-
expandTargetPath={expandTargetPath}
|
|
371
|
-
anatomyPaths={anatomyPaths}
|
|
372
|
-
/>
|
|
373
|
-
))}
|
|
374
|
-
<div className="text-muted-foreground py-0.5 px-1">{closeBracket}</div>
|
|
375
|
-
</div>
|
|
376
|
-
)}
|
|
377
|
-
</div>
|
|
378
|
-
);
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
export type JsonViewerProps = {
|
|
382
|
-
data: JsonValue;
|
|
383
|
-
defaultExpandDepth?: number;
|
|
384
|
-
className?: string;
|
|
385
|
-
showCopy?: boolean;
|
|
386
|
-
bulkDepth?: number;
|
|
387
|
-
bulkRevision?: number;
|
|
388
|
-
/**
|
|
389
|
-
* Set of JSON-pointer-style paths whose row should expose a
|
|
390
|
-
* `data-anatomy-path` attribute. Used by the Context view to
|
|
391
|
-
* locate rows for click-to-jump. When `null` or `undefined`,
|
|
392
|
-
* no `data-anatomy-path` attribute is emitted (zero cost).
|
|
393
|
-
*/
|
|
394
|
-
anatomyPaths?: Set<string> | null;
|
|
395
|
-
/**
|
|
396
|
-
* When set, every ancestor of this path in the tree auto-expands
|
|
397
|
-
* for one render so the target row becomes visible. The hook
|
|
398
|
-
* flips this back to `null` after the scroll lands.
|
|
399
|
-
*/
|
|
400
|
-
expandToPath?: string | null;
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
export function JsonViewer({
|
|
404
|
-
data,
|
|
405
|
-
defaultExpandDepth = 0,
|
|
406
|
-
className,
|
|
407
|
-
showCopy = false,
|
|
408
|
-
bulkDepth: controlledBulkDepth,
|
|
409
|
-
bulkRevision: controlledBulkRevision,
|
|
410
|
-
anatomyPaths = null,
|
|
411
|
-
expandToPath = null,
|
|
412
|
-
}: JsonViewerProps): JSX.Element {
|
|
413
|
-
const expandable = isExpandable(data);
|
|
414
|
-
const entries = useMemo(() => getEntries(data), [data]);
|
|
415
|
-
|
|
416
|
-
const bulkDepth = controlledBulkDepth ?? defaultExpandDepth;
|
|
417
|
-
const bulkRevision = controlledBulkRevision ?? 0;
|
|
418
|
-
|
|
419
|
-
if (!expandable) {
|
|
420
|
-
return (
|
|
421
|
-
<TooltipProvider>
|
|
422
|
-
<div className={cn("font-mono text-xs leading-relaxed", className)}>
|
|
423
|
-
<div className="flex items-center gap-1">
|
|
424
|
-
<PrimitiveValue value={data} />
|
|
425
|
-
{showCopy && <CopyValueButton value={data} />}
|
|
426
|
-
</div>
|
|
427
|
-
</div>
|
|
428
|
-
</TooltipProvider>
|
|
429
|
-
);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
const dataType = classifyValue(data);
|
|
433
|
-
const isArray = dataType === "array";
|
|
434
|
-
|
|
435
|
-
return (
|
|
436
|
-
<TooltipProvider>
|
|
437
|
-
<div className={cn("font-mono text-xs leading-relaxed", className)}>
|
|
438
|
-
{showCopy && (
|
|
439
|
-
<div className="mb-2 flex items-center justify-end gap-2">
|
|
440
|
-
<CopyValueButton value={data} />
|
|
441
|
-
</div>
|
|
442
|
-
)}
|
|
443
|
-
{entries.length === 0 && (
|
|
444
|
-
<div className="rounded-md border border-white/10 bg-black/20 px-3 py-2 text-muted-foreground">
|
|
445
|
-
<span className="text-cyan-300">{isArray ? "[]" : "{}"}</span>
|
|
446
|
-
<span className="ml-2 text-[10px] uppercase">0 items</span>
|
|
447
|
-
</div>
|
|
448
|
-
)}
|
|
449
|
-
<div key={bulkRevision}>
|
|
450
|
-
{entries.map(([key, childValue]) => (
|
|
451
|
-
<JsonNode
|
|
452
|
-
key={key}
|
|
453
|
-
name={key}
|
|
454
|
-
value={childValue}
|
|
455
|
-
level={0}
|
|
456
|
-
defaultExpandDepth={bulkDepth}
|
|
457
|
-
isArrayItem={isArray}
|
|
458
|
-
path={`/${key}`}
|
|
459
|
-
expandTargetPath={expandToPath}
|
|
460
|
-
anatomyPaths={anatomyPaths}
|
|
461
|
-
/>
|
|
462
|
-
))}
|
|
463
|
-
</div>
|
|
464
|
-
</div>
|
|
465
|
-
</TooltipProvider>
|
|
466
|
-
);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
export type JsonViewerFromStringProps = {
|
|
470
|
-
text: string;
|
|
471
|
-
defaultExpandDepth?: number;
|
|
472
|
-
className?: string;
|
|
473
|
-
/**
|
|
474
|
-
* Forwarded to the inner `JsonViewer` so that a parent bulk-expansion
|
|
475
|
-
* controller (e.g. the header's Expand-all button) can expand/collapse
|
|
476
|
-
* all nodes. `JsonViewer` falls back to `defaultExpandDepth` when both
|
|
477
|
-
* are unset.
|
|
478
|
-
*/
|
|
479
|
-
bulkDepth?: number;
|
|
480
|
-
bulkRevision?: number;
|
|
481
|
-
};
|
|
482
|
-
|
|
483
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow -- named function gives React DevTools a readable displayName
|
|
484
|
-
export const JsonViewerFromString = memo(function JsonViewerFromString({
|
|
485
|
-
text,
|
|
486
|
-
defaultExpandDepth = 0,
|
|
487
|
-
className,
|
|
488
|
-
bulkDepth,
|
|
489
|
-
bulkRevision,
|
|
490
|
-
}: JsonViewerFromStringProps): JSX.Element {
|
|
491
|
-
const parsed = useMemo(() => parseJsonText(text), [text]);
|
|
492
|
-
|
|
493
|
-
if (parsed.kind === "json") {
|
|
494
|
-
return (
|
|
495
|
-
<JsonViewer
|
|
496
|
-
data={parsed.data}
|
|
497
|
-
defaultExpandDepth={defaultExpandDepth}
|
|
498
|
-
className={className}
|
|
499
|
-
bulkDepth={bulkDepth}
|
|
500
|
-
bulkRevision={bulkRevision}
|
|
501
|
-
/>
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
return (
|
|
506
|
-
<pre className={cn("font-mono text-xs whitespace-pre-wrap break-words", className)}>{text}</pre>
|
|
507
|
-
);
|
|
508
|
-
});
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { ScrollArea as ScrollAreaPrimitive } from "radix-ui";
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
|
|
4
|
-
import { cn } from "../../lib/utils";
|
|
5
|
-
|
|
6
|
-
function ScrollArea({
|
|
7
|
-
className,
|
|
8
|
-
children,
|
|
9
|
-
...props
|
|
10
|
-
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>): React.JSX.Element {
|
|
11
|
-
return (
|
|
12
|
-
<ScrollAreaPrimitive.Root
|
|
13
|
-
data-slot="scroll-area"
|
|
14
|
-
className={cn("relative overflow-hidden", className)}
|
|
15
|
-
{...props}
|
|
16
|
-
>
|
|
17
|
-
<ScrollAreaPrimitive.Viewport
|
|
18
|
-
data-slot="scroll-area-viewport"
|
|
19
|
-
className="focus-visible:ring-ring/50 size-full max-h-[inherit] rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
|
|
20
|
-
>
|
|
21
|
-
{children}
|
|
22
|
-
</ScrollAreaPrimitive.Viewport>
|
|
23
|
-
<ScrollBar />
|
|
24
|
-
<ScrollAreaPrimitive.Corner />
|
|
25
|
-
</ScrollAreaPrimitive.Root>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function ScrollBar({
|
|
30
|
-
className,
|
|
31
|
-
orientation = "vertical",
|
|
32
|
-
...props
|
|
33
|
-
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>): React.JSX.Element {
|
|
34
|
-
return (
|
|
35
|
-
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
|
36
|
-
data-slot="scroll-area-scrollbar"
|
|
37
|
-
orientation={orientation}
|
|
38
|
-
className={cn(
|
|
39
|
-
"flex touch-none p-px transition-colors select-none",
|
|
40
|
-
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent",
|
|
41
|
-
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent",
|
|
42
|
-
className,
|
|
43
|
-
)}
|
|
44
|
-
{...props}
|
|
45
|
-
>
|
|
46
|
-
<ScrollAreaPrimitive.ScrollAreaThumb
|
|
47
|
-
data-slot="scroll-area-thumb"
|
|
48
|
-
className="bg-border relative flex-1 rounded-full"
|
|
49
|
-
/>
|
|
50
|
-
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export { ScrollArea, ScrollBar };
|