From 9a5e828310efac7cc6646d4725d38cec1ea61f21 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sun, 4 Oct 2026 07:20:14 +0000 Subject: [PATCH] feat(ui): inline Lens settings tab and investigation editor (#44479) * feat(ui): move worker status into the Lens notch and New investigation into the list toolbar Co-Authored-By: Claude Fable 5.1 * feat(ui): make the Lens notch entry a settings gear that houses the worker section Co-Authored-By: Claude Fable 5.1 * feat(ui): replace the Lens worker modal with an inline Settings tab Co-Authored-By: Claude Fable 5.1 * feat(ui): section the Lens settings tab with tracing status and worker cards Co-Authored-By: Claude Fable 5.1 * fix(ui): let Lens settings sections span the full card width Co-Authored-By: Claude Fable 5.1 * feat(ui): replace the investigation setup modal with an inline side-by-side editor New, edit, and duplicate now take over the Investigations tab body: matching activity on the left, every setting on the right, with no wizard steps or modal Co-Authored-By: Claude Fable 5.1 * feat(ui): step the inline investigation setup vertically with traces alongside Setup now sits on the left as three progressive steps (activity, criteria, run) that collapse to a summary once done and reopen on click. Matching activity stays on the right for every step. The editor gets a back control and the Investigations notch shows a New, Editing, or Duplicate badge while the editor is open Co-Authored-By: Claude Fable 5.1 * feat(ui): page the matching activity preview with useInfiniteQuery as it scrolls Replace the Previous/Next offset buttons with the same infinite query and near-tail prefetch the traces list uses, so the preview keeps loaded runs and its title while the next page arrives Co-Authored-By: Claude Fable 5.1 * refactor(ui): make the investigation step field map exhaustive over the form schema Co-Authored-By: Claude Fable 5.1 * fix(ui): always show the Settings tab label in the Lens mode switch Co-Authored-By: Claude Fable 5.1 * refactor(ui): collapse Lens worker cards into compact status rows Co-Authored-By: Claude Fable 5.1 * fix(ui): keep the Lens Settings tab icon-only in every state Co-Authored-By: Claude Fable 5.1 * wip * fix(ui): tick the Lens worker health dot so an expired heartbeat goes stale Co-Authored-By: Claude Fable 5.1 * refactor(ui): regroup Lens settings, model and api layers Co-Authored-By: Claude Fable 5.1 * refactor(ui): dedupe Lens formatting helpers Co-Authored-By: Claude Fable 5.1 * fix(ui): poll Lens once, drive the interval from data, settle mutations before invalidating Co-Authored-By: Claude Fable 5.1 * refactor(ui): let Lens leaves fetch their own data Co-Authored-By: Claude Fable 5.1 * refactor(ui): bind drawer and trace shortcuts through react-hotkeys-hook One useShortcut hook replaces the three hand-rolled keydown listeners in SidePanel, the trace step tree and the log drawer, with a layer option deciding which keys a pane claims from the panel around it. The span tree footer now renders ShortcutHints from what is actually bound instead of hand-typed kbd text. * refactor(ui): extract Inspector from SidePanel Inspector.Root owns the open item, J/K stepping, Escape and full screen; Inspector.Row marks a list entry with aria-selected and data-state and toggles it on click or Enter/Space; Inspector.Panel is the resizable side panel with the exit animation and click-outside rules. The runs table and section compose these parts directly, so RunDrawer and the SidePanel prop bag go away. * refactor(ui): model the Lens worker screen as a tagged union and slot in its ready action workerScreen() decides between list, form and install from the worker rows, the registration result and the edit target, so WorkerSettings switches on one value and each card owns its own copy. The post-install CTA is now a ReactNode slot that LensWorkspace fills instead of an onReady callback threaded through LensSettings and WorkerSettings. Clipboard copy state lives in WorkerInstall as mutations, and the styled settings leaves export Props types, set data-slot and accept native element props. Co-Authored-By: Claude Fable 5.1 * refactor(ui): compose the Lens setup stepper from SetupStep children Each step's heading, summary and fields now live together in one SetupStep instead of four parallel structures keyed by index, and the last-step spacing comes from CSS rather than a passed index. The mode prop is now required since InvestigationsView always passes it. Co-Authored-By: Claude Fable 5.1 * fix(ui): keep the Lens Settings panel mounted so a pending worker install survives tab switches The settings TabsContent unmounted WorkerSettings whenever another tab was active, dropping the one-time worker token shown during install. The panel now uses keepMounted, and the workspace test registers a worker, switches tabs and back, then follows the connected worker into the first investigation through the slotted CTA. Co-Authored-By: Claude Fable 5.1 * test(ui): cover the Lens worker install waiting-to-connected transition Co-Authored-By: Claude Fable 5.1 * refactor(ui): give the Lens activity preview a grouped contract and a structural debounce useMatchingActivity now owns its return types (scope options, preview status, page and optional manual selection) instead of borrowing them from the components it feeds, and the preview takes those groups plus the section attributes. The clear-selection action moves into the preview footer, RunList becomes a RunRow leaf, and ScopeFields drops the unused nameField and id props now that MetadataFilters calls useId itself. The preview scope settles through a hashKey-based useDebouncedValue instead of JSON round-tripping into state, and the loading title follows isPlaceholderData since the query keeps previous data. RunFields and AnalysisModelField take the analysis models and the model gate as two objects instead of seven flat props. Co-Authored-By: Claude Fable 5.1 * refactor(ui): select the Lens investigations screen with a pure tagged union investigationScreen maps the list query and the route to one of loading, failed, welcome, list, detail, setup or missing, so the view can switch instead of juggling mutually exclusive booleans. The status model gains activeJob, carries connected inside Readiness and folds the activity probe into one ActivityCheck value for the welcome page Co-Authored-By: Claude Fable 5.1 * test(ui): cover the Lens preview footer clear action and the preview debounce Co-Authored-By: Claude Fable 5.1 * refactor(ui): let Lens investigation leaves own their URL slice and express intent InvestigationsView renders the screen union and owns every write through useInvestigationActions, so leaves receive on* handlers instead of the API writer. useInvestigationResults becomes useRunSnapshot; FindingsTab, HistoryTab, RunPicker and RequestEvidenceSheet read their own nuqs slice and run their own queries. The finding sheet becomes an Inspector side panel (FindingDetails) keyed per finding, with the trace and request evidence sheets grouped in EvidenceSheets. WatchAllBanner owns its mutation, the welcome page takes the readiness and activity values, the progress sampler records on the wall clock outside render, and run history invalidates when the list reports a scheduler-started job Co-Authored-By: Claude Fable 5.1 * test(ui): cover Lens investigation intents, pause, cancel, history refresh and the finding panel Co-Authored-By: Claude Fable 5.1 * fix(ui): keep the inspector open behind sheet overlays and mount HotkeysProvider from a client wrapper * feat(ui): open Lens runs and evidence in the inspector panel A quote's original trace step or logged request now stacks inside the finding panel behind a back link, keeping the finding and its feedback draft mounted. The detail Runs tab and the setup activity preview open runs in the same panel with J/K stepping, so the TraceSheet and RequestEvidenceSheet modals are gone. Picking a different finding or run clears any stacked evidence from the URL. * feat(ui): open Lens investigations in the inspector panel beside the list The investigations list stays on screen and a row opens its investigation in the side panel, so J/K walk investigations and their open findings in display order and the selected row carries the same highlight as runs. The panel body is the former detail page; findings and runs opened inside it nest their own inspector, which claims the keys from the one around it while open. Opening an investigation and peeking at a finding now replace each other in the URL. * fix(ui): run the Lens notch border along the tab pill and flag only a disconnected worker Co-Authored-By: Claude Fable 5.1 * fix(ui): show the shortcut hints in every inspector panel Co-Authored-By: Claude Fable 5.1 * refactor(ui): extract the Lens dot field into composable DotFieldRoot and DotFieldCanvas Move the dot grid model and canvas painter out of TracesTimeline into components/lens/dotField so other Lens surfaces can reuse it. The root owns layout and context; overlays compose as children. agoLabel moves to lens/model/format and the unused columnTop helper is dropped. Co-Authored-By: Claude Fable 5.1 * refactor(ui): drop the manual refresh button from the runs time controls Live polls and range changes refetch, so the button only cleared the zoom, which Escape already does Co-Authored-By: Claude Opus 5.5 * refactor(ui): extract the Lens run search into a composable SearchBox primitive Move the query parser, glob matcher and autocomplete out of runSearch into components/lens/search, generic over a QueryLanguage (field specs plus what free text searches). SearchBox.Root owns the ProseMirror state, menu and keyboard; SearchBox.Input and SearchBox.Suggestions compose under it. Clause highlighting becomes a ProseMirror plugin built from the language. RunSearch now only declares the run fields and composes the parts, so the investigations tab can define its own language and reuse the same box. Co-Authored-By: Claude Fable 5.1 * fix(ui): label the runs range by preset while Live and pin it once paused Co-Authored-By: Claude Opus 5.5 * refactor(ui): split the Lens search language from where its data lives QueryLanguage is now pure vocabulary (keys, groups, icons). Reading fields off loaded items moves to a ClientIndex consumed by a separate evaluator, and value suggestions come from an injectable ValueSource, so a server-backed runs list can plug in a facet lookup while the investigations tab keeps filtering in memory. The parsed query serializes to a typed SearchQuery (text terms plus eq/neq/glob/nglob filters) that the client evaluator consumes today and a server can consume later. The suggestion menu shows a loading row while a source is still answering. Co-Authored-By: Claude Fable 5.1 * style(ui): format the Lens SearchBox and its test with the project prettier config Co-Authored-By: Claude Fable 5.1 * feat(ui): mirror the Lens run filters as trace SQL with a copyable curl in the search footer The suggestions footer gains a slot, and SearchBox.ApiHint fills it with the API equivalent of the typed query: a dialect chip, a one-line preview and a Copy as curl button. The runs box translates each filter to a predicate over the agent_traces_by_key rollup, bounded to the range the list shows, and copies a POST to /v1/traces/query. Any other list can plug its own translate into the same part. Co-Authored-By: Claude Fable 5.1 * feat(ui): show the Lens introduction as a first-visit dialog with a typed don't-show-again The guided setup no longer replaces the Lens tabs. It opens in a dialog on the first visit of a session or from ?setup=lens, with a close and a "Don't show this again" checkbox in its top-right corner. The header "Set up Lens" button is gone. Dismissal state lives in a new schema-validated web storage helper (src/lib/storage.ts) that reads through useSyncExternalStore, so server renders see the fallback and other tabs stay in sync; only Lens uses it for now. Co-Authored-By: Claude Fable 5.1 * refactor(ui): keep only Copy as curl in the Lens run search footer Drop the SQL chip and predicate preview; SearchBox.ApiHint becomes SearchBox.CopyCommand, which takes the command for the current query. Co-Authored-By: Claude Fable 5.1 * feat(ui): align the Lens investigations list with the traces list Use the shared query SearchBox with investigation fields (name, agent, status, schedule), match the traces toolbar, and drop the count footer and inner padding. Co-Authored-By: Claude Opus 5.5 * feat(ui): let Lens settings bring back the introduction after don't show again Co-Authored-By: Claude Opus 5.5 * refactor(ui): share one InspectorTable between the traces list and the Lens investigations tree Compose TanStack Table, react-virtual and the shadcn table cells into InspectorTable parts (Root, Grid, Header, Body, Row, Indent). Investigations get findings as real sub-rows with TanStack expansion instead of a hand-rolled flattener. Co-Authored-By: Claude Opus 5.5 * refactor(ui): break Lens import cycles and move shared pieces out of lens Search and the dot field go to components/shared, run search and the preview button go to view_logs where they are consumed. Lens api, services and demo live under data/, all URL state in route.ts, storage keys in storage.ts, and the session frame styles become cva variants. Co-Authored-By: Claude Opus 5.5 * refactor(ui): one Lens readiness source and one onboarding flow Readiness is computed once in model/readiness and read through useLensReadiness, replacing useLensSetup, status.readiness and the welcome screen's own checks. The Investigations welcome now renders the same onboarding steps as the introduction dialog, with permissions and actions coming from an OnboardingProvider instead of props passed down four levels. StepIndicator and StateMessage are shared lens components, and the step panels are labelled accordion regions. Co-Authored-By: Claude Opus 5.5 * refactor(ui): read the Lens token from services and use semantic status colors Lens services carry the access token they were built for, so trace evidence, readiness and onboarding read it from context instead of a prop threaded through six components. List and history invalidation lives in one data hook. Status colors use the success, warning and destructive tokens, and template-literal class names go through cn. Co-Authored-By: Claude Opus 5.5 * refactor(ui): split Lens demo fixtures from the fake demo APIs Co-Authored-By: Claude Opus 5.5 * feat(ui): show Lens check history as a dot timeline Co-Authored-By: Claude Opus 5.5 * chores * fix(ui): clear stale Lens evidence on run change and keep read-only users off Settings Also names inline option objects to bring local/no-large-inline-object-arg back under budget. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Yujong Lee Co-authored-by: Claude Fable 5.1 --- ui/litellm-dashboard/package-lock.json | 14 + ui/litellm-dashboard/package.json | 1 + ui/litellm-dashboard/src/app/layout.tsx | 5 +- .../src/components/lens/HeaderActions.tsx | 13 - .../src/components/lens/LensModeSwitch.tsx | 101 ++++ ...test.tsx => LensPage.integration.test.tsx} | 4 + .../components/lens/LensServicesProvider.tsx | 13 - .../lens/LensSetup.integration.test.tsx | 266 +++++---- .../lens/LensWorkspace.integration.test.tsx | 267 ++++++++- .../src/components/lens/LensWorkspace.tsx | 388 ++++++-------- .../src/components/lens/api/mutations.ts | 93 ---- .../src/components/lens/data/LensServices.tsx | 40 ++ .../{ => data}/demo/createLensDemo.test.ts | 5 +- .../lens/data/demo/createLensDemo.ts | 76 +++ .../demo/fixtures.ts} | 78 +-- .../lens/{ => data}/demo/lensDemoLongTrace.ts | 0 .../lens/{ => data}/demo/scenarios.ts | 0 .../src/components/lens/data/mutations.ts | 56 ++ .../components/lens/{api => data}/queries.ts | 63 ++- .../components/lens/{api => data}/service.ts | 14 +- .../components/lens/hooks/useLensReadiness.ts | 58 ++ .../lens/hooks/useWorkerConnected.ts | 12 + .../lens/investigations/Evidence.tsx | 131 +++++ .../FindingDetails.integration.test.tsx | 135 +++++ .../lens/investigations/FindingDetails.tsx | 200 +++++++ .../FindingSheet.integration.test.tsx | 51 -- .../lens/investigations/FindingSheet.tsx | 164 ------ .../lens/investigations/InvestigationList.tsx | 491 +++++++++-------- .../InvestigationNavigation.tsx | 54 -- .../investigations/InvestigationProgress.tsx | 29 +- ...tionStatus.tsx => InvestigationStates.tsx} | 51 +- .../InvestigationsView.integration.test.tsx | 369 ++++++++++--- .../investigations/InvestigationsView.tsx | 505 +++++++----------- .../investigations/InvestigationsWelcome.tsx | 168 ------ .../lens/investigations/IssueBrief.tsx | 2 +- .../investigations/RequestEvidenceSheet.tsx | 69 --- .../lens/investigations/TraceSheet.test.tsx | 20 - .../lens/investigations/TraceSheet.tsx | 64 --- .../lens/investigations/WatchAllBanner.tsx | 31 +- .../investigations/detail/CriteriaTab.tsx | 26 +- .../investigations/detail/FindingsTab.tsx | 189 ++++--- .../lens/investigations/detail/HistoryTab.tsx | 58 +- .../detail/HistoryTimeline.test.ts | 98 ++++ .../investigations/detail/HistoryTimeline.tsx | 92 ++++ .../detail/InvestigationActions.test.tsx | 101 ++++ .../detail/InvestigationActions.tsx | 51 +- .../detail/InvestigationDetail.tsx | 172 ++---- .../detail/InvestigationSummary.tsx | 2 +- .../lens/investigations/detail/JobMeta.tsx | 23 + .../investigations/detail/RunNowDialog.tsx | 49 +- .../lens/investigations/detail/RunPicker.tsx | 41 +- .../lens/investigations/detail/RunsTab.tsx | 198 ++++--- .../investigations/detail/useRunHistory.ts | 20 + .../investigations/investigationQuery.test.ts | 76 +++ .../lens/investigations/investigationQuery.ts | 30 ++ .../investigationScreen.test.ts | 103 ++++ .../investigations/investigationScreen.ts | 51 ++ .../investigations/useInvestigationActions.ts | 26 + .../investigations/useInvestigationResults.ts | 107 ---- .../lens/investigations/useProgressSamples.ts | 32 ++ .../lens/investigations/useRunSnapshot.ts | 38 ++ .../src/components/lens/model/findings.ts | 9 +- .../src/components/lens/model/format.test.ts | 18 +- .../src/components/lens/model/format.ts | 12 +- .../src/components/lens/model/inbox.ts | 7 +- .../src/components/lens/model/progress.ts | 2 +- .../components/lens/model/readiness.test.ts | 53 ++ .../src/components/lens/model/readiness.ts | 47 ++ .../runRequest.test.ts} | 2 +- .../src/components/lens/model/runRequest.ts | 30 ++ .../src/components/lens/model/status.test.ts | 48 +- .../src/components/lens/model/status.ts | 30 +- .../src/components/lens/model/types.ts | 14 + .../lens/{setup => model}/watches.ts | 2 +- .../{setup => onboarding}/GatewayFlow.tsx | 5 +- .../lens/onboarding/LensGettingStarted.tsx | 81 +++ .../lens/onboarding/LensIntroDialog.tsx | 80 +++ .../LensIntroduction.module.css | 0 .../LensIntroduction.tsx | 25 +- .../lens/onboarding/OnboardingContext.tsx | 24 + .../lens/onboarding/OnboardingSetup.tsx | 52 ++ .../lens/onboarding/OnboardingSteps.tsx | 251 +++++++++ .../components/lens/{route.tsx => route.ts} | 88 ++- .../lens/runSearch/RunSearch.test.tsx | 140 ----- .../components/lens/runSearch/RunSearch.tsx | 257 --------- .../lens/runSearch/runQuery.test.ts | 96 ---- .../src/components/lens/runSearch/runQuery.ts | 111 ---- .../lens/runSearch/suggestions.test.ts | 75 --- .../components/lens/runSearch/suggestions.ts | 91 ---- .../src/components/lens/services.ts | 20 - .../components/lens/settings/LensSettings.tsx | 84 +++ .../lens/settings/SettingsSection.tsx | 33 ++ .../worker/AnalysisAccessFields.tsx | 8 +- .../worker/AnalysisKeyDetails.tsx | 57 +- .../AnalysisKeyPicker.integration.test.tsx | 7 +- .../worker/AnalysisKeyPicker.tsx | 10 +- .../{setup => settings}/worker/WorkerForm.tsx | 8 +- .../lens/settings/worker/WorkerInstall.tsx | 104 ++++ .../lens/settings/worker/WorkerList.tsx | 65 +++ .../WorkerSettings.integration.test.tsx} | 81 ++- .../lens/settings/worker/WorkerSettings.tsx | 168 ++++++ .../settings/worker/useAnalysisKeyInfo.ts | 10 + .../lens/settings/worker/usePrepareWorker.ts | 66 +++ .../worker/workerCommand.ts | 0 .../worker/workerSchema.test.ts | 0 .../worker/workerSchema.ts | 0 .../lens/settings/worker/workerScreen.test.ts | 45 ++ .../lens/settings/worker/workerScreen.ts | 21 + ...> InvestigationSetup.integration.test.tsx} | 370 +++++++------ .../lens/setup/InvestigationSetup.tsx | 235 ++++++++ .../lens/setup/InvestigationSetupDialog.tsx | 205 ------- .../lens/setup/LensGettingStarted.tsx | 227 -------- .../components/lens/setup/LensSetupSteps.tsx | 229 -------- .../lens/setup/MatchingActivity.tsx | 166 ------ .../lens/setup/MatchingActivityPreview.tsx | 191 ++++--- .../lens/setup/MonitoringDialog.tsx | 2 +- .../components/lens/setup/SetupSteps.test.tsx | 55 ++ .../src/components/lens/setup/SetupSteps.tsx | 79 +++ .../src/components/lens/setup/WatchPicker.tsx | 2 +- .../lens/setup/fields/AnalysisModelField.tsx | 38 +- .../ExpectationsFields.tsx} | 2 +- .../lens/setup/fields/MetadataFilters.tsx | 21 +- .../RunStep.tsx => fields/RunFields.tsx} | 37 +- .../lens/setup/fields/ScopeFields.tsx | 29 +- .../lens/setup/fields/analysisModels.ts | 22 +- .../lens/setup/fields/useAnalysisModels.ts | 33 ++ .../lens/setup/investigationSchema.test-d.ts | 23 + .../lens/setup/investigationSchema.test.ts | 13 +- .../lens/setup/investigationSchema.ts | 53 +- .../components/lens/setup/steps/ScopeStep.tsx | 38 -- .../lens/setup/useDebouncedValue.test.tsx | 38 ++ .../lens/setup/useDebouncedValue.ts | 36 ++ .../src/components/lens/setup/useLensSetup.ts | 59 -- .../lens/setup/useMatchingActivity.ts | 192 +++++++ .../lens/setup/worker/WorkerDialog.tsx | 196 ------- .../lens/setup/worker/WorkerInstall.tsx | 104 ---- .../lens/setup/worker/WorkerList.tsx | 62 --- .../src/components/lens/storage.ts | 6 + .../src/components/lens/ui/StateMessage.tsx | 39 ++ .../src/components/lens/ui/StepIndicator.tsx | 37 ++ .../src/components/lens/ui/frame.ts | 30 ++ .../src/components/shared/Inspector.test.tsx | 321 +++++++++++ .../src/components/shared/Inspector.tsx | 349 ++++++++++++ .../components/shared/InspectorTable.test.tsx | 106 ++++ .../src/components/shared/InspectorTable.tsx | 179 +++++++ .../components/shared/ShortcutHints.test.tsx | 46 ++ .../src/components/shared/ShortcutHints.tsx | 94 ++++ .../src/components/shared/SidePanel.tsx | 239 --------- .../src/components/shared/StatusDot.tsx | 1 + .../shared/__fixtures__/BoundShortcut.tsx | 19 + .../components/shared/dotField/DotField.tsx | 152 ++++++ .../components/shared/dotField/dots.test.ts | 68 +++ .../src/components/shared/dotField/dots.ts | 57 ++ .../shared/search/SearchBox.test.tsx | 250 +++++++++ .../components/shared/search/SearchBox.tsx | 389 ++++++++++++++ .../shared/search/__fixtures__/notes.ts | 47 ++ .../components/shared/search/evaluate.test.ts | 72 +++ .../src/components/shared/search/evaluate.ts | 42 ++ .../components/shared/search/language.test.ts | 41 ++ .../src/components/shared/search/language.ts | 89 +++ .../shared/search/searchQuery.test.ts | 30 ++ .../components/shared/search/searchQuery.ts | 35 ++ .../shared/search/suggestions.test.ts | 106 ++++ .../components/shared/search/suggestions.ts | 132 +++++ .../components/shared/search/valueSource.ts | 21 + .../components/shared/useShortcut.test.tsx | 76 +++ .../src/components/shared/useShortcut.ts | 51 ++ .../view_logs/LogDetailsDrawer/constants.ts | 7 - .../useKeyboardNavigation.test.tsx | 8 +- .../LogDetailsDrawer/useKeyboardNavigation.ts | 70 +-- .../view_logs/TraceView/AgentTracesPage.tsx | 15 +- .../AgentTracesSection.integration.test.tsx | 83 +-- .../TraceView/AgentTracesSection.tsx | 110 ++-- .../TraceView/AgentTracesTable.test.tsx | 55 +- .../view_logs/TraceView/AgentTracesTable.tsx | 275 +++++----- .../TraceView}/LensPreviewButton.tsx | 0 .../view_logs/TraceView/RunDrawer.test.tsx | 284 ---------- .../view_logs/TraceView/RunDrawer.tsx | 66 --- .../view_logs/TraceView/SpanTree.tsx | 33 -- .../view_logs/TraceView/TimeRangeControls.tsx | 101 ++-- .../view_logs/TraceView/TraceDrawer.test.tsx | 14 +- .../view_logs/TraceView/TraceDrawer.tsx | 65 +-- .../TraceView/TracesTimeline.test.ts | 12 +- .../view_logs/TraceView/TracesTimeline.tsx | 145 +---- .../TracingSetupCard.integration.test.tsx | 2 +- .../view_logs/TraceView/TracingSetupCard.tsx | 2 +- .../view_logs/TraceView/lensField.test.ts | 103 ---- .../view_logs/TraceView/lensField.ts | 61 --- .../TraceView/runSearch/RunSearch.test.tsx | 47 ++ .../TraceView/runSearch/RunSearch.tsx | 41 ++ .../TraceView}/runSearch/RunsToolbar.tsx | 11 +- .../TraceView}/runSearch/__fixtures__/runs.ts | 2 +- .../TraceView/runSearch/runQuery.test.ts | 47 ++ .../view_logs/TraceView/runSearch/runQuery.ts | 36 ++ .../TraceView/runSearch/runSql.test.ts | 92 ++++ .../view_logs/TraceView/runSearch/runSql.ts | 81 +++ .../components/view_logs/letterShortcut.ts | 9 - .../src/contexts/HotkeysProvider.tsx | 7 + ui/litellm-dashboard/src/lib/storage.test.tsx | 59 ++ ui/litellm-dashboard/src/lib/storage.ts | 95 ++++ .../tests/lens-test-utils.tsx | 39 ++ ui/litellm-dashboard/tests/setupTests.ts | 9 +- ui/litellm-dashboard/tests/test-utils.tsx | 5 +- 203 files changed, 9750 insertions(+), 6343 deletions(-) delete mode 100644 ui/litellm-dashboard/src/components/lens/HeaderActions.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/LensModeSwitch.tsx rename ui/litellm-dashboard/src/components/lens/{LensNavigation.integration.test.tsx => LensPage.integration.test.tsx} (96%) delete mode 100644 ui/litellm-dashboard/src/components/lens/LensServicesProvider.tsx delete mode 100644 ui/litellm-dashboard/src/components/lens/api/mutations.ts create mode 100644 ui/litellm-dashboard/src/components/lens/data/LensServices.tsx rename ui/litellm-dashboard/src/components/lens/{ => data}/demo/createLensDemo.test.ts (95%) create mode 100644 ui/litellm-dashboard/src/components/lens/data/demo/createLensDemo.ts rename ui/litellm-dashboard/src/components/lens/{demo/createLensDemo.ts => data/demo/fixtures.ts} (77%) rename ui/litellm-dashboard/src/components/lens/{ => data}/demo/lensDemoLongTrace.ts (100%) rename ui/litellm-dashboard/src/components/lens/{ => data}/demo/scenarios.ts (100%) create mode 100644 ui/litellm-dashboard/src/components/lens/data/mutations.ts rename ui/litellm-dashboard/src/components/lens/{api => data}/queries.ts (70%) rename ui/litellm-dashboard/src/components/lens/{api => data}/service.ts (96%) create mode 100644 ui/litellm-dashboard/src/components/lens/hooks/useLensReadiness.ts create mode 100644 ui/litellm-dashboard/src/components/lens/hooks/useWorkerConnected.ts create mode 100644 ui/litellm-dashboard/src/components/lens/investigations/Evidence.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/investigations/FindingDetails.integration.test.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/investigations/FindingDetails.tsx delete mode 100644 ui/litellm-dashboard/src/components/lens/investigations/FindingSheet.integration.test.tsx delete mode 100644 ui/litellm-dashboard/src/components/lens/investigations/FindingSheet.tsx delete mode 100644 ui/litellm-dashboard/src/components/lens/investigations/InvestigationNavigation.tsx rename ui/litellm-dashboard/src/components/lens/investigations/{InvestigationStatus.tsx => InvestigationStates.tsx} (69%) delete mode 100644 ui/litellm-dashboard/src/components/lens/investigations/InvestigationsWelcome.tsx delete mode 100644 ui/litellm-dashboard/src/components/lens/investigations/RequestEvidenceSheet.tsx delete mode 100644 ui/litellm-dashboard/src/components/lens/investigations/TraceSheet.test.tsx delete mode 100644 ui/litellm-dashboard/src/components/lens/investigations/TraceSheet.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/investigations/detail/HistoryTimeline.test.ts create mode 100644 ui/litellm-dashboard/src/components/lens/investigations/detail/HistoryTimeline.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/investigations/detail/InvestigationActions.test.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/investigations/detail/JobMeta.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/investigations/detail/useRunHistory.ts create mode 100644 ui/litellm-dashboard/src/components/lens/investigations/investigationQuery.test.ts create mode 100644 ui/litellm-dashboard/src/components/lens/investigations/investigationQuery.ts create mode 100644 ui/litellm-dashboard/src/components/lens/investigations/investigationScreen.test.ts create mode 100644 ui/litellm-dashboard/src/components/lens/investigations/investigationScreen.ts create mode 100644 ui/litellm-dashboard/src/components/lens/investigations/useInvestigationActions.ts delete mode 100644 ui/litellm-dashboard/src/components/lens/investigations/useInvestigationResults.ts create mode 100644 ui/litellm-dashboard/src/components/lens/investigations/useProgressSamples.ts create mode 100644 ui/litellm-dashboard/src/components/lens/investigations/useRunSnapshot.ts create mode 100644 ui/litellm-dashboard/src/components/lens/model/readiness.test.ts create mode 100644 ui/litellm-dashboard/src/components/lens/model/readiness.ts rename ui/litellm-dashboard/src/components/lens/{investigations/detail/RunNowDialog.test.ts => model/runRequest.test.ts} (97%) create mode 100644 ui/litellm-dashboard/src/components/lens/model/runRequest.ts rename ui/litellm-dashboard/src/components/lens/{setup => model}/watches.ts (98%) rename ui/litellm-dashboard/src/components/lens/{setup => onboarding}/GatewayFlow.tsx (96%) create mode 100644 ui/litellm-dashboard/src/components/lens/onboarding/LensGettingStarted.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/onboarding/LensIntroDialog.tsx rename ui/litellm-dashboard/src/components/lens/{setup => onboarding}/LensIntroduction.module.css (100%) rename ui/litellm-dashboard/src/components/lens/{setup => onboarding}/LensIntroduction.tsx (87%) create mode 100644 ui/litellm-dashboard/src/components/lens/onboarding/OnboardingContext.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/onboarding/OnboardingSetup.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/onboarding/OnboardingSteps.tsx rename ui/litellm-dashboard/src/components/lens/{route.tsx => route.ts} (53%) delete mode 100644 ui/litellm-dashboard/src/components/lens/runSearch/RunSearch.test.tsx delete mode 100644 ui/litellm-dashboard/src/components/lens/runSearch/RunSearch.tsx delete mode 100644 ui/litellm-dashboard/src/components/lens/runSearch/runQuery.test.ts delete mode 100644 ui/litellm-dashboard/src/components/lens/runSearch/runQuery.ts delete mode 100644 ui/litellm-dashboard/src/components/lens/runSearch/suggestions.test.ts delete mode 100644 ui/litellm-dashboard/src/components/lens/runSearch/suggestions.ts delete mode 100644 ui/litellm-dashboard/src/components/lens/services.ts create mode 100644 ui/litellm-dashboard/src/components/lens/settings/LensSettings.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/settings/SettingsSection.tsx rename ui/litellm-dashboard/src/components/lens/{setup => settings}/worker/AnalysisAccessFields.tsx (89%) rename ui/litellm-dashboard/src/components/lens/{setup => settings}/worker/AnalysisKeyDetails.tsx (61%) rename ui/litellm-dashboard/src/components/lens/{setup => settings}/worker/AnalysisKeyPicker.integration.test.tsx (91%) rename ui/litellm-dashboard/src/components/lens/{setup => settings}/worker/AnalysisKeyPicker.tsx (91%) rename ui/litellm-dashboard/src/components/lens/{setup => settings}/worker/WorkerForm.tsx (86%) create mode 100644 ui/litellm-dashboard/src/components/lens/settings/worker/WorkerInstall.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/settings/worker/WorkerList.tsx rename ui/litellm-dashboard/src/components/lens/{setup/worker/WorkerDialog.integration.test.tsx => settings/worker/WorkerSettings.integration.test.tsx} (65%) create mode 100644 ui/litellm-dashboard/src/components/lens/settings/worker/WorkerSettings.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/settings/worker/useAnalysisKeyInfo.ts create mode 100644 ui/litellm-dashboard/src/components/lens/settings/worker/usePrepareWorker.ts rename ui/litellm-dashboard/src/components/lens/{setup => settings}/worker/workerCommand.ts (100%) rename ui/litellm-dashboard/src/components/lens/{setup => settings}/worker/workerSchema.test.ts (100%) rename ui/litellm-dashboard/src/components/lens/{setup => settings}/worker/workerSchema.ts (100%) create mode 100644 ui/litellm-dashboard/src/components/lens/settings/worker/workerScreen.test.ts create mode 100644 ui/litellm-dashboard/src/components/lens/settings/worker/workerScreen.ts rename ui/litellm-dashboard/src/components/lens/setup/{InvestigationSetupDialog.integration.test.tsx => InvestigationSetup.integration.test.tsx} (64%) create mode 100644 ui/litellm-dashboard/src/components/lens/setup/InvestigationSetup.tsx delete mode 100644 ui/litellm-dashboard/src/components/lens/setup/InvestigationSetupDialog.tsx delete mode 100644 ui/litellm-dashboard/src/components/lens/setup/LensGettingStarted.tsx delete mode 100644 ui/litellm-dashboard/src/components/lens/setup/LensSetupSteps.tsx delete mode 100644 ui/litellm-dashboard/src/components/lens/setup/MatchingActivity.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/setup/SetupSteps.test.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/setup/SetupSteps.tsx rename ui/litellm-dashboard/src/components/lens/setup/{steps/ExpectationsStep.tsx => fields/ExpectationsFields.tsx} (98%) rename ui/litellm-dashboard/src/components/lens/setup/{steps/RunStep.tsx => fields/RunFields.tsx} (80%) create mode 100644 ui/litellm-dashboard/src/components/lens/setup/fields/useAnalysisModels.ts create mode 100644 ui/litellm-dashboard/src/components/lens/setup/investigationSchema.test-d.ts delete mode 100644 ui/litellm-dashboard/src/components/lens/setup/steps/ScopeStep.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/setup/useDebouncedValue.test.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/setup/useDebouncedValue.ts delete mode 100644 ui/litellm-dashboard/src/components/lens/setup/useLensSetup.ts create mode 100644 ui/litellm-dashboard/src/components/lens/setup/useMatchingActivity.ts delete mode 100644 ui/litellm-dashboard/src/components/lens/setup/worker/WorkerDialog.tsx delete mode 100644 ui/litellm-dashboard/src/components/lens/setup/worker/WorkerInstall.tsx delete mode 100644 ui/litellm-dashboard/src/components/lens/setup/worker/WorkerList.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/storage.ts create mode 100644 ui/litellm-dashboard/src/components/lens/ui/StateMessage.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/ui/StepIndicator.tsx create mode 100644 ui/litellm-dashboard/src/components/lens/ui/frame.ts create mode 100644 ui/litellm-dashboard/src/components/shared/Inspector.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/Inspector.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/InspectorTable.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/InspectorTable.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/ShortcutHints.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/ShortcutHints.tsx delete mode 100644 ui/litellm-dashboard/src/components/shared/SidePanel.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/__fixtures__/BoundShortcut.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/dotField/DotField.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/dotField/dots.test.ts create mode 100644 ui/litellm-dashboard/src/components/shared/dotField/dots.ts create mode 100644 ui/litellm-dashboard/src/components/shared/search/SearchBox.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/search/SearchBox.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/search/__fixtures__/notes.ts create mode 100644 ui/litellm-dashboard/src/components/shared/search/evaluate.test.ts create mode 100644 ui/litellm-dashboard/src/components/shared/search/evaluate.ts create mode 100644 ui/litellm-dashboard/src/components/shared/search/language.test.ts create mode 100644 ui/litellm-dashboard/src/components/shared/search/language.ts create mode 100644 ui/litellm-dashboard/src/components/shared/search/searchQuery.test.ts create mode 100644 ui/litellm-dashboard/src/components/shared/search/searchQuery.ts create mode 100644 ui/litellm-dashboard/src/components/shared/search/suggestions.test.ts create mode 100644 ui/litellm-dashboard/src/components/shared/search/suggestions.ts create mode 100644 ui/litellm-dashboard/src/components/shared/search/valueSource.ts create mode 100644 ui/litellm-dashboard/src/components/shared/useShortcut.test.tsx create mode 100644 ui/litellm-dashboard/src/components/shared/useShortcut.ts rename ui/litellm-dashboard/src/components/{lens => view_logs/TraceView}/LensPreviewButton.tsx (100%) delete mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/RunDrawer.test.tsx delete mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/RunDrawer.tsx delete mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/lensField.test.ts delete mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/lensField.ts create mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/runSearch/RunSearch.test.tsx create mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/runSearch/RunSearch.tsx rename ui/litellm-dashboard/src/components/{lens => view_logs/TraceView}/runSearch/RunsToolbar.tsx (60%) rename ui/litellm-dashboard/src/components/{lens => view_logs/TraceView}/runSearch/__fixtures__/runs.ts (92%) create mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/runSearch/runQuery.test.ts create mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/runSearch/runQuery.ts create mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/runSearch/runSql.test.ts create mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/runSearch/runSql.ts delete mode 100644 ui/litellm-dashboard/src/components/view_logs/letterShortcut.ts create mode 100644 ui/litellm-dashboard/src/contexts/HotkeysProvider.tsx create mode 100644 ui/litellm-dashboard/src/lib/storage.test.tsx create mode 100644 ui/litellm-dashboard/src/lib/storage.ts create mode 100644 ui/litellm-dashboard/tests/lens-test-utils.tsx diff --git a/ui/litellm-dashboard/package-lock.json b/ui/litellm-dashboard/package-lock.json index b4c886752df..37027f215fe 100644 --- a/ui/litellm-dashboard/package-lock.json +++ b/ui/litellm-dashboard/package-lock.json @@ -42,6 +42,7 @@ "react-dom": "19.2.8", "react-error-boundary": "6.1.6", "react-hook-form": "7.82.0", + "react-hotkeys-hook": "5.3.3", "react-intersection-observer": "11.0.1", "react-json-view-lite": "2.5.0", "react-markdown": "9.1.0", @@ -10537,6 +10538,19 @@ "react": "^16.8.0 || ^17 || ^18 || ^19" } }, + "node_modules/react-hotkeys-hook": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-5.3.3.tgz", + "integrity": "sha512-aswgyWUnE25hmhzHTfKDmKzsaSE5DJ4LKaU/o6rQSXkDd/1Bh9TfAFQbHkf6fLy11HvlYkp+cDDarGdhmCDhoQ==", + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/react-intersection-observer": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-11.0.1.tgz", diff --git a/ui/litellm-dashboard/package.json b/ui/litellm-dashboard/package.json index bba51e9aa2b..388b6c7eab9 100644 --- a/ui/litellm-dashboard/package.json +++ b/ui/litellm-dashboard/package.json @@ -59,6 +59,7 @@ "react-dom": "19.2.8", "react-error-boundary": "6.1.6", "react-hook-form": "7.82.0", + "react-hotkeys-hook": "5.3.3", "react-intersection-observer": "11.0.1", "react-json-view-lite": "2.5.0", "react-markdown": "9.1.0", diff --git a/ui/litellm-dashboard/src/app/layout.tsx b/ui/litellm-dashboard/src/app/layout.tsx index 60db7104a03..7bb05ef11a7 100644 --- a/ui/litellm-dashboard/src/app/layout.tsx +++ b/ui/litellm-dashboard/src/app/layout.tsx @@ -7,6 +7,7 @@ import { ThemeProvider } from "next-themes"; import { AuthProvider } from "@/contexts/AuthContext"; import ReactQueryProvider from "@/contexts/ReactQueryProvider"; +import HotkeysProvider from "@/contexts/HotkeysProvider"; import { Toaster } from "@/components/ui/sonner"; const inter = Inter({ subsets: ["latin"] }); @@ -30,7 +31,9 @@ export default function RootLayout({ - {children} + + {children} + diff --git a/ui/litellm-dashboard/src/components/lens/HeaderActions.tsx b/ui/litellm-dashboard/src/components/lens/HeaderActions.tsx deleted file mode 100644 index 8df9307c990..00000000000 --- a/ui/litellm-dashboard/src/components/lens/HeaderActions.tsx +++ /dev/null @@ -1,13 +0,0 @@ -"use client"; - -import { useContext, type ReactNode } from "react"; -import { createPortal } from "react-dom"; - -import { LensPreviewContext } from "./LensPreviewButton"; - -export function HeaderActions({ children }: { children: ReactNode }) { - const preview = useContext(LensPreviewContext); - const actions =
{children}
; - if (preview === undefined) return actions; - return preview.target ? createPortal(actions, preview.target) : null; -} diff --git a/ui/litellm-dashboard/src/components/lens/LensModeSwitch.tsx b/ui/litellm-dashboard/src/components/lens/LensModeSwitch.tsx new file mode 100644 index 00000000000..11d02567799 --- /dev/null +++ b/ui/litellm-dashboard/src/components/lens/LensModeSwitch.tsx @@ -0,0 +1,101 @@ +"use client"; + +import { Tabs as TabsPrimitive } from "@base-ui/react/tabs"; +import { Activity, ScanSearch, Settings } from "lucide-react"; +import { StatusDot } from "@/components/shared/StatusDot"; +import { cn } from "@/lib/cva.config"; +import type { InvestigationActivity } from "./model/status"; +import { useWorkerConnected } from "./hooks/useWorkerConnected"; +import type { LensList } from "./model/types"; +import { LENS_TABS, type LensTab } from "./route"; +import { frameCorner, frameTab } from "./ui/frame"; + +const MODE_ICONS = { traces: Activity, investigations: ScanSearch, settings: Settings } as const; + +const ACTIVITY_DOT: Record, { className: string; label: string }> = { + running: { className: "bg-info motion-safe:animate-pulse", label: "An investigation is running" }, + queued: { className: "bg-muted-foreground/60", label: "An investigation is queued" }, +}; + +function ActivityDot({ activity }: { activity: InvestigationActivity }) { + if (activity === "idle") return null; + return ( +