Theme selection across light and dark modes is now persisted by name
instead of numeric ID. A shared registry (constants/themes.ts) owns the
single source of truth for all predefined theme definitions, and
utils/themeManager.ts provides a central applyCurrentTheme() /
applyThemeForMode() API that replaces scattered inline resolution logic
in App.vue and PredefinedThemes.vue. Custom themes continue to store
their hex color as the source of truth, marked with the
CUSTOM_THEME_NAME sentinel.
The theme drawer is redesigned: a segmented Light/Dark toggle replaces
the tabs, each theme is a single clickable row (no per-row Apply
button), the applied theme is highlighted with a primary ring plus an
Applied badge, long names truncate instead of wrapping, and the custom
color row opens the picker with an Apply/Cancel dialog.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Summary
- **Service Catalog**: Add progress bars to latency and error rate
columns (P50, P95, P99, Avg Duration, Max Duration, Error Rate), scaled
relative to the column maximum across all filtered services. Raw count
columns (Requests, Errors) kept as plain text to avoid misleading
comparisons.
- **Service Graph Side Panel**: Extend the same progress bar treatment
to the Operations tab and K8s/resource tabs. Remove the outer border box
from RED charts using `:frame="false"` on `RenderDashboardCharts`.
- **Alert History Timeline**: Add a colored strip visualization above
the history table showing firing (red), ok (green), and flapping
(purple) zones across the selected time range. Flapping zones are
detected automatically based on rapid state alternation and shown as a
single collapsed row in the table with transition count and duration.
Status labels normalized — `completed` → Firing,
`condition_not_satisfied` → Ok.
## Changes
### New files
- `web/src/plugins/traces/components/ServiceCatalogBarCell.vue` —
reusable bar cell (label + OProgressBar, scaled to column max)
- `web/src/components/alerts/AlertHistoryTimeline.vue` — state strip
with flapping zone detection, hover tooltips, transition timestamps
### Modified files
- `web/src/plugins/traces/ServicesCatalog.vue` — column maxes computed,
bar cells wired up, column order adjusted
- `web/src/plugins/traces/ServiceGraphNodeSidePanel.vue` — progress bars
on ops/resource tabs, `:frame="false"` on RED charts
- `web/src/components/alerts/AlertHistoryDrawer.vue` — timeline
integrated, flapping row grouping, status label normalization
## Test plan
- [ ] Service Catalog: verify progress bars scale correctly when
filtering services, bars absent on Requests/Errors columns
- [ ] Service Graph side panel: progress bars visible on Operations tab
P95/P99/P75; RED charts have no outer border box
- [ ] Alert History: open an alert with mixed firing/ok history —
timeline strip appears above table
- [ ] Alert History: open an alert with rapid firing↔ok alternation —
purple Flapping zone visible in strip, flapping rows collapsed in table
with expand chevron
- [ ] Alert History: status badges show "Firing" for
completed/error/anomaly, "Ok" for condition_not_satisfied
- [ ] Dark mode: all components render correctly with theme tokens
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: ktx-vaidehi <vaidehi.akhani@kiara.tech>
1. The name is missing on score config list table
datetime error when user is clicking on arrow in quality page / any one
of the score config overview
replace hardcoded badges to obadge in all list page
when user goes to llm sessions it is taking user to traces submenu
## What changed
Adds a streaming progress bar (the same `LoadingProgress.vue` used by
dashboard panels) to the logs results table and histogram chart. The
skeleton loader in `TenstackTable.vue` now hides once any rows exist,
keeping data visible while streaming updates replace values in-place.
## Why
During `streaming_aggs`, hits arrive and replace chunk-by-chunk while
`loading` stays `true` until the stream `end` signal. The full skeleton
covered the table on every refresh, so users couldn't watch the
incremental updates. The progress bar signals an in-progress refresh
without hiding existing data.
## What changed
- **FieldValuesPanel.vue** — Added `tw:relative` to the loading wrapper
so the `OInnerLoading` overlay (which is `absolute inset-0`) is
contained within the expanded section instead of spilling over the
entire field row.
- **IndexList.vue** — Reworked `cancelTraceId` to use
`cancelStreamQueryBasedOnRequestId` (HTTP stream abort) instead of the
now-removed `cancelSearchQueryBasedOnRequestId`. Updated
`cancelFilterCreator` to call `cancelTraceId`, clear `isLoading`, and
empty `traceIdMapper` entries.
- **FieldValuesPanel.spec.ts** — Regression test asserting the loading
wrapper carries `tw:relative`.
- **IndexList.spec.ts** — Un-skipped two previously skipped cancel tests
and updated them to assert on `mockCancelStreamQueryBasedOnRequestId`.
Added new regression tests for the `cancelFilterCreator` →
`cancelTraceId` flow on collapse.
## Why
The loading overlay (`OInnerLoading`) uses `absolute inset-0`
positioning. Without a positioned ancestor, it escaped to the nearest
parent (`.o-field-list__row`), covering the field row and the collapse
toggle. This made it impossible for users to collapse a slow-loading
field to cancel the request. The cancel logic itself was also broken —
it referenced a WebSocket-based cancel function that no longer matched
the actual HTTP-stream transport used for field values.
## What changed
- Created `useWidenRange` composable with extracted `periodToLabel` /
`nextWiderPeriod` pure functions to eliminate duplicate widen-range
logic across `ServiceGraphNoDataState`, `ServicesCatalogNoDataState`,
`TracesNoEventsState`, and `LogsNoEventsState`.
- Added `getEffectiveTimeRange` to `utils/date.ts` to deduplicate inline
datetime-parsing if/else blocks in `ServiceGraph.vue` and
`ServicesCatalog.vue`.
- Created two new animated SVG illustrations (`EmptyServiceGraph.vue` —
horizontal tree topology with one root node calling two children;
`EmptyServicesCatalog.vue` — table card with three service rows and
pulsing status dots), registered them in the OEmptyState preset system,
and added i18n keys across all 12 languages.
- Added dedicated empty-state components `ServiceGraphNoDataState.vue`
and `ServicesCatalogNoDataState.vue` with an "Expand time range" action
card (matching the `TracesNoEventsState.vue` pattern), wired via emit
chain through `ServiceGraph.vue` / `ServicesCatalog.vue` up to
`Index.vue`'s `onWidenTracesRange`.
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
## What changed
- Passes `matchedSetId` and `chipDimensions` as new computed props from
`IncidentDetailDrawer.vue` to the embedded
`TelemetryCorrelationDashboard`, enabling the "View by" subject tabs in
the incident view
- Adds an `OBadge` showing the selected subject as `label = value` after
the subject toggle tabs in `CorrelationEventHeader.vue`
- Fixes the responsive overflow logic so the first context chip can
overflow when space runs out, and the overflow indicator shows "N
Fields" when all chips are hidden instead of a bare "+N"
- Renders hidden overflow chips as stacked `OBadge` components in
`OTooltip`'s `#content` slot instead of a `\n`-joined string
## Why
Incidents opened without a source trace or log event had no
`matchedSetId` / `chipDimensions` passed to the correlation dashboard,
so the "View by" subject toggle (Pod, Node, etc.) never rendered. The
badge was also missing to visually indicate which subject is active.
#12703
## What changed
Removed the `showErrorOnly` key from `searchObj.meta` state and replaced
it with a `computed` property that derives from
`activeIncludeFilterValues` — specifically checking whether
`span_status` includes `"ERROR"`. The `SearchResult.vue` component now
receives `showErrorOnly` as a prop from `Index.vue` instead of reading
it from the shared composable state. `TracesMetricsDashboard.vue` had
its own writable `showErrorOnly` computed removed since it no longer
needs to sync a top-level state key.
## Why
The user identified that `showErrorOnly` was redundant state — the
information is already encoded in
`activeIncludeFilterValues["span_status"]`. Keeping both in sync was
unnecessary maintenance burden. Deriving it from the existing filter
state is the single source of truth and eliminates the risk of the flag
and the actual query filters diverging.
## What
In the traces **spans-mode** list view, the query was built as:
```sql
SELECT * FROM "<stream>" [WHERE ...] ORDER BY start_time <dir>
```
`start_time` is a regular column, so the engine has to perform a full
re-sort of the result set. This change makes the query order by the
configured timestamp column (`_timestamp`) whenever the active sort
column is the default `start_time`:
```sql
SELECT * FROM "<stream>" [WHERE ...] ORDER BY _timestamp <dir>
```
## Why
- Span data is physically stored sorted by the timestamp column, and the
backend has a dedicated optimizer for timestamp-sorted segments.
- A span's `_timestamp` is monotonically equivalent to its `start_time`,
so the **visible row order is unchanged**.
- This avoids the costly full re-sort that `ORDER BY start_time` forces,
improving query latency for the trace/spans view.
## Scope / Notes
- Change is contained to `web/src/plugins/traces/Index.vue`
(spans-mode query builder in `getQueryData`).
- Only the default `start_time` sort is remapped. Any explicit
non-`start_time` sort column the user picks is passed through
unchanged.
- The `sortBy` UI state still reports `start_time`, so the sort
indicator and reset logic are unaffected.
- Not in scope: the single-trace span fetch in `TraceDetails.vue` still
uses `ORDER BY start_time` (can be addressed separately).
## Testing
- Verified the generated spans SQL now uses the timestamp column for the
default sort and produces the same visible ordering.
Co-authored-by: Hengfei Yang <hengfei.yang@gmail.com>
## What changed
Adds stream-change confirmation dialogs across Traces tabs. When a user
changes the stream in **Service Graph** or **Services Catalog**, the
change is no longer applied silently — instead an `ODialog` prompts the
user to confirm, since changing the stream clears the existing query in
the Traces/Spans tabs.
## Why
Previously, stream selection was only synced one-way: changing the
stream in Traces/Spans propagated to all other tabs. Changing the stream
in Service Graph or Services Catalog did not sync back because those
tabs don't show a query — so doing so could erase an active query in
Traces/Spans without user awareness. This change ensures the user is
explicitly asked before clearing their query.
## Why
The custom spinner-based loading states were inconsistent across traces
views anddid not align with the logs TenstackTable skeleton pattern
already established inthe codebase. The new skeleton provides a unified,
low-friction loading experience — shimmer pills at column-aligned
positions that mirror real cell widths — and removes the need for each
consumer to write its own loading template.
## What changed
Fixed three interrelated issues in the Traces:
(1) duplicated search calls caused by redundant watchers and
programmatic triggers, and (3) `isDatetimeChanged` returning `false`
when switching from absolute to relative mode.
## Why
Separately, loading Traces in any mode triggered 2 redundant search API
calls — watchers on `searchObj.data.query` duplicated explicit trigger
calls at every query writer, mount-time filter restoration also called
`searchData()` despite `loadPageData()` already owning the initial
search, and the DateTime picker's mount replay fired a false positive
"change" that triggered an auto-run.
## Approach
- Added a `markProgrammaticDateChange()` / `userChangedValue` flag to
`DateTime.vue` so the SearchBar can distinguish user-driven date changes
from programmatic ones (redirect, metrics brush, mount replay), using
`nextTick` to reset the flag after the flush.
- Removed the `watch` on `searchObj.data.query` and the debounced
auto-run watchers on query/datetime — every writer of `data.query` in
the codebase already calls a search explicitly, so a watcher doubled
every trigger.
- Removed the `searchData()` call inside the `restoreFilters` block on
mount; the single mount search is owned by `loadPageData()`.
- In `SearchBar.vue`, gated the live-mode auto-run on `userChangedValue
=== true` and `datetimeChanged`, and removed the `searchObj.data.query =
newValue` writes in `applyFilters`/`removeFilter` (filter operations
already trigger search via emit).
- Fixed `isDatetimeChanged` to return `true` when the previous type was
`absolute` and the value is now `relative`, so switching mode always
registers as a change.
## What changed
Fixed three issues in the Trace Graph:
- **Multi-root traces no longer crash** — traces where root spans
belonged to distinct services, the same service, or had cyclic service
relationships now render correctly instead of showing an empty graph.
- **Overlapping node labels are truncated** — long service names in
chart nodes are capped at 24 characters with an ellipsis, preventing
overlap with the next tree level.
- **Tooltip now shows the service name** — the pattern node tooltip
header was missing the service name, making it impossible to seethe full
name of a truncated node on hover.
Also added `truncateText` utility to `zincutils.ts` and comprehensive
test fixtures covering all multi-root scenarios.
## What changed
Added a stream-selected guard before setting `searchObj.loading = true`
in the auto-query trigger in `SearchBar.vue`, and added an explicit
`searchObj.loading = false` reset in `Index.vue`'s `runQueryFn` when no
stream is selected.
## Why
When no stream is selected, auto-query is enabled, and the user switches
the date-time mode (relative → absolute → relative), `searchObj.loading`
was set to `true` by SearchBar but never reset to `false`. The
`runQueryFn` in Index.vue would early-return due to an empty
`selectedStream`, but did not clear the loading state, leaving the UI
stuck in an infinite loading spinner.
## Approach
- **SearchBar.vue** (line 2719): Added
`searchObj.data.stream.selectedStream.length > 0` to the condition that
triggers an auto-query on date-time change, preventing
`searchObj.loading = true` when there is no stream to query.
- **Index.vue** (lines 978–981): Inside `runQueryFn`, when
`selectedStream.length` is 0, explicitly set `searchObj.loading = false`
and `searchObj.runQuery = false` before returning, as a defense-in-depth
reset.
## What changed
Enriches the ServiceGraph Node Detail Panel for **inferred services**
(uninstrumented dependencies discovered from span attributes —
databases, message queues, RPC backends, external APIs). Instead of
showing only a single operation for these services, the panel now
displays type-specific dynamic tabs (Hosts, Databases, Queries,
Destinations, URLs, RPC Services) using field fallback chains with
COALESCE expressions, filtered against the actual stream schema.
## Why
Inferred services (detected via `infer_service_name`) previously showed
only one operation in the panel because they're derived from
CLIENT/PRODUCER spans. There was no way to see the different servers,
databases, or URLs they connect to. This PR adds structured context by
inspecting peer attributes (`server_address`, `net_peer_name`,
`db_namespace`, `messaging_destination_name`, etc.) and grouping data
into meaningful tabs per service type.
## Approach
- **INFERRED_SERVICE_TABS registry** — maps each `service_type`
(database/queue/rpc/external) to its relevant tabs with fallback field
chains (e.g., `["server_address", "net_peer_name", "net_peer_ip",
"network_peer_address"]`)
- **SQL helper functions** — `buildCoalesceExpr` and `buildNullCheck`
generate COALESCE expressions for GROUP BY/SELECT and OR clauses for
WHERE filters
- **Schema-aware field filtering** — `resolveStreamSchema` fetches the
trace stream schema; each tab's fallback fields are filtered against it,
dropping tabs with zero surviving fields
- **Caller-aware operations** — Operations table gains a "Caller" column
for inferred services, showing which upstream service invokes each
operation
- **Filter construction** — Row clicks on inferred service tabs generate
`(field1 = 'val' OR field2 = 'val')` filters instead of single-field
equality
- **Metrics tab hidden** for inferred services — no meaningful metric
correlation for uninstrumented backends
- Non-breaking — existing single-field `resourceFilter` paths left
unchanged; `handleServicesCatalogViewTraces` normalizes legacy string
payloads
## What changed
- Extracted parser-limitation guards into `isParserLimitation()` and
refactored `useSearchQuery.ts` to **not block the search** when the PEG
parser flags a false-positive error — the squiggle still shows but the
query proceeds to the backend
- Added a 4th parser-limitation guard for `found="("` inside window
function clauses (covers `SUM(COUNT(*)) OVER (...)`, `NTILE(N) OVER
(PARTITION BY COALESCE(...)...)`)
- Unified all four `fetchRumEventsForTrace` queries to a consistent
±60,000,000 µs buffer around the trace window
- Added `allowedTracingUrls` config + `propagatorTypes` to the RUM SDK
config snippet in `FrontendRumConfig.vue`
- Added an "Explain" query dropdown menu item in the logs SearchBar
- Fixed various traces/RUM UI padding and border issues
(PlayerEventsSidebar, TraceDetails, TracesMetricsDashboard,
SessionViewer)
- Updated i18n keys across all 12 language files
## Why
Two valid SQL queries produced false-positive validation errors
(`SUM(COUNT(*)) OVER (...)`, `NTILE(...) OVER (PARTITION BY
COALESCE(...)...)`), and even after suppressing those, the query was
still **blocked from executing** by `useSearchQuery.ts`. The PEG parser
has known gaps that the DataFusion backend handles correctly — there was
no reason to block valid queries. The RUM event queries also had
inconsistent time buffers across different calls (some used ±10s, others
±60s), causing missed RUM events in trace correlation.
## Approach
- Extracted the 4 parser-limitation guards from `validateSql()` into a
pure sync `isParserLimitation()` function exported from
`sqlDiagnostics.ts`, then reused it in `useSearchQuery.ts` to let query
execution continue when only parser limitations are detected
- Replaced inline constants with a shared `RUM_TIME_BUFFER_US =
60_000_000` in `useRumSpanBuilder.ts` so all four
`fetchRumEventsForTrace` queries use the same ±60s window
- Added `{ timeout: 30000 }` to the aggregate mutation test that was
timing out on ~5,500 broken queries
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
## What changed
- Added a wrap/unwrap button (toggle) in the CorrelatedLogsTable header
toolbar, matching the pattern from `logs/SearchResult.vue` (`wrap-text`
icon, `OTooltip` with `search.messageWrapContent` i18n key)
- Replaced the hard-coded 800px column `maxSize` with a dynamic cap
computed from the table container's actual width, tracked via
`ResizeObserver`
- Last-column fill: if total column widths are less than the container
and the last column is a `longTextFields` type, the remaining space is
added to it