master
345 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
01d4659a3e |
GDGT-3241: SegmentedControl constrained text + icon props (#83336)
* GDGT-3241 Constrain SegmentedControl items to text and icon props Items are now typed data (label, icon, ariaLabel) instead of arbitrary label nodes, and the component owns icon size and spacing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Update Loki Snapshots * set line height on SegmentedControl label * add second click to assert handler doesnt refire on multiple clicks * interface -> type * add test --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
145357fbb1 | Skip unused addons and font loads in Loki screenshot runs (#83068) | ||
|
|
c80438a092 |
Migrate the filter value lists off Emotion (#83384)
* Migrate the filter value lists off Emotion * Update Loki Snapshots * Address review feedback and truncate long filter values * Revert the truncation of long filter values --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
24a52962de |
UXW-3922 Add syntax highlight for AI system prompts (#79675)
* UXW-3922 Add syntax highlight for AI system prompts * Review fixes * Fix CI * Update Loki Snapshots * Update Loki Snapshots * Fix e2e --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
cbc6c3a858 |
Restyle Sidesheet to Figma and add the offset variant (GDGT-2480) (#82747)
* Restyle Sidesheet to Figma and add the offset variant (GDGT-2480)
Brings the common Sidesheet in line with the Figma spec and adds the
inset ("offset") variant the design system defines.
- offset?: 16 applies a 16px top/right/bottom inset and a 16px radius;
typed as the literal 16 so unsupported values can't enter the API
- 464px (29rem) default width, applied only when `size` is omitted so
the named sizes stay exactly as they were
- surface-primary background, xs_outline elevation, 24/24/16 header,
h4 title, and a 32px close control with a 16px icon
- Storybook: added an Offset story, and reduced the MDX page to a
summary. Each story embedded there rendered a real fixed-position
Sidesheet, so the docs page stacked them all on top of each other
and none could be read; the stories remain in the sidebar.
Mantine Modal internals (focus trap, escape handling, overlay, portals,
nested subpages) are unchanged; this is a restyle plus one additive prop.
* Use the design-approved sm width for Sidesheets
* Make Sidesheet offset a boolean option
* Move static Sidesheet styling to Mantine props
* Add Loki references for Sidesheet layouts
* Update Loki Snapshots
* Wait for Sidesheet before capturing Loki references
* GDGT-2480: Address review comments
---------
Co-authored-by: Metabase Automation <github-automation@metabase.com>
|
||
|
|
121fa7febe |
GDGT-3240: SegmentedControl (#82657)
* GDGT-2463 update SegmentedControl to new Figma designs * Update Loki Snapshots * Update Loki Snapshots * Update Loki Snapshots * exclude `c` prop from SegmentedControl * remove disabled attribute from segmented control item * Show SegmentedControl overview story in light and dark themes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Update Loki Snapshots --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
97283f9c02 |
GDGT-3231: 1. Combobox: add Select, MultiSelect and Combobox stories with Loki snapshots (#82756)
* test(ui): add Select, MultiSelect and Combobox Overview stories with Loki * test(ui): address review, drop Combobox story and searchable sections * Update Loki Snapshots --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
e28f66a5ac |
Migrate the visualization skeletons off Emotion (#82992)
* Migrate the visualization skeletons off Emotion * Skeletons: shared svg wrapper, inlined caption, Loki stories |
||
|
|
bc75a88888 |
GDGT-2458: Accordion (#82562)
* test(ui): add Accordion Storybook story and Loki filter * feat(ui): restyle Accordion from Figma * fix(Accordion): keep theme selectors one class deep so call-site overrides still win * refactor: drop Accordion call-site overrides now covered by the theme * test(ui): add Loki reference for Accordion Overview story * fix(Accordion): hide the default chevron from assistive tech * test: update DataComplexityCards snapshot for the Accordion separated default * fix(RemoteSyncDependencyModal): drop overrides the new Accordion theme makes redundant, keep control transparent * fix(RemoteSyncDependencyModal): let the Accordion theme space the rows * fix(Accordion): keep icon grey on disabled hover, honour the radius prop * test(ui): add disabled hover row to the Accordion Overview story * Update Loki Snapshots --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
fa20179ff6 | Restyle Button to the Figma spec and migrate call sites (#82236) | ||
|
|
b084ade539 |
GDGT-3224: migrate TabRow to CSS modules to fix gradient theme issue (#82482)
* GDGT: migrate TabRow to CSS modules to fix gradient theme issue * Update Loki Snapshots * increase scroll gradient size * Update Loki Snapshots * Add dark theme story for narrow dashboard with many tabs Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Add placeholder Loki reference for dark theme narrow tabs story Copy of the light theme snapshot so the loki-update workflow detects a diff and replaces it with the CI-generated image. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Update Loki Snapshots * move left/right styles into existing classes --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
72ea707064 |
Add dataPointsVisible extension to eCharts (#81721)
* useRenderedDataVisibility * static viz, other adjustments * remove dead hook * Split panels, simplification * handling line series with a range very close to chart boundary, ignore goal line * return true on traversal if intersection is found |
||
|
|
236a1cc9bd | Gdgt 2461 tabs (#82174) | ||
|
|
1a8883d604 |
Revert "Apply responsive spacing and typography to dashboard chart cards" (#82346)
* Revert "Apply responsive spacing and typography to dashboard chart cards (#81…"
This reverts commit
|
||
|
|
5b7c89d8ee |
GDGT-2478 Translate PasswordInput toggle label at call time (#82338)
* GDGT-2478 Translate PasswordInput toggle label at call time * Update Loki Snapshots --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
ca17b89463 |
GDGT-2478 PasswordInput (#82117)
* test(ui): add PasswordInput Storybook story * test(ui): add PasswordInput to Loki as baseline for restyle * feat(ui): restyle PasswordInput from Figma and add input-background token * test: cover PasswordInput visibility toggle in unit and e2e tests * fix(PasswordInput): use highlight border color in focused error mode by overriding Input styles * Update Loki Snapshots * test(ui): add Loki reference for PasswordInput Overview story * fix(PasswordInput): address review feedback - keep border width constant and use an inset ring for focus/error to avoid layout shift - tie right padding to the toggle button size so text clears it in lg - restrict size prop to sm | md | lg - order color keys alphabetically - explicit visibility assertion in the e2e spec * Update Loki Snapshots --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
6a035e234c |
Use JetBrains Mono as the code font (#82278)
* Use JetBrains Mono as the code font Ship JetBrains Mono (regular + bold) and point the monospace font family at it, replacing the hardcoded Monaco/Menlo stacks scattered across the codebase. - Add the woff2 files, @font-face declarations, and the type, fallback map, docs and backend test entries that keep the admin font picker in sync with the fonts directory. - Route every monospace call site through --mb-default-monospace-font-family, and define that variable in the embedding SDK's variable set, where it was previously missing. With the variable always defined, the per-call-site `, monospace` fallback is redundant and is dropped. - Inline the variable at the CodeMirror theme, which was the only consumer of monospaceFontFamily, and delete typography.ts. - Set the Code component to 13px/22px per the design spec. * Update Loki Snapshots --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
13e00d55b9 |
Apply responsive spacing and typography to dashboard chart cards (#81906)
* Apply responsive spacing and typography to dashboard chart cards (UXW-5181) * Update Loki Snapshots * Fix preview height regression and update e2e tests for dashcard size tiers * Fix card heading typography and non-cartesian chart bottom padding * Update Loki Snapshots * Apply new styles only on cartesian charts * Move changed styled components to css modules * Update Loki Snapshots --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
c0949b1d38 | Gdgt 2465 popover (#81561) | ||
|
|
979b344073 |
Fix light theme border-highlight token (#82157)
Fix light theme border-highlight: brand[30] -> brand[60] (GDGT-2484) |
||
|
|
c7a5060ca8 |
GDGT-2462: Update Switch component (#81709)
* GDGT-2462: Update Switch component * GDGT-2462: Add Switch Loki references * Update Loki Snapshots * Address PR findings --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
0e7e969925 |
GDGT-2467: Curate Paper radius tokens (#81700)
* GDGT-2467: Restrict Paper padding/radius to curated token options * Update Loki Snapshots * GDGT-2467: Update entity picker width e2e expectation for Paper padding * GDGT-2467: Keep Paper default padding at 0, add no-padding option to stories * GDGT-2467: Keep all padding options in Paper stories, only curate radius * Update Loki Snapshots * GDGT-2467: Type PAPER_RADIUS_OPTIONS against RadiusScaleKey --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
5885d30ad0 |
GDGT-2486 Add elevation, radius, and spacing scale variables (#80481)
* GDGT-2486 Add elevation, radius, and spacing scale variables
* GDGT-2486 Remap scale tokens in files merged from master
* Address review
Review fixes for the GDGT-2486 scale migration:
- Revert double-shifted tokens from the merge-remap pass: it re-applied
the old-to-new mapping to 22 files already remapped by the first pass,
shifting spacing values one extra step (e.g. xl 24px -> xxl 32px).
- Fix codemod escapes where old key names kept their name but changed px:
Modal default padding (lg->xl, 24px), MultiSelect radius (sm->xs, 6px),
MetricSearchInput radius-sm, ThemeEditor PreviewPanel radius-md,
ColumnInput theme.spacing.xs->xxs, ChartTypeSettings Space h props,
TableRowForCacheableItem icon placeholder w (md->lg, 16px),
ResizeHandle/ResizableBoxHandle grip h (xs->xxs, 4px).
- Pin Group/Stack default gap to "lg" (16px): Mantine's default is
"md", which is 12px in the new scale (was 16px in the old scale).
- Allowlist the runtime-defined --mantine-spacing-xxs/-xxl and
--mantine-radius-xxs CSS variables in find-never-defined-css-variables
(fixes fe-lint CI).
- Add theme.unit.spec.ts covering the spacing/radius/shadow scales,
dark-mode shadow set, whitelabel independence, and a guard asserting
every scale-based component default resolves to a defined key.
- Derive Card/Paper story argTypes options from the real scale.
- Augment MantineThemeSizesOverride so the custom scale tokens appear in
editor autocomplete for p/m/gap/radius/shadow props.
- Update the emotion-migrate and metabase-ui-component-from-figma skill
token tables to the new scale (spacing/radius values, shadow tokens,
snap tables).
* Update Loki Snapshots
* Address review: fix h/w style props missed by the scale codemod
The codemod did not remap height/width style props, so every bare h=/w=
scale-key value silently changed px with the new scale. Remap each to the
px-preserving key (xs 4px->xxs, md 16px->lg, lg 24px->xl, xl 32px->xxl):
- PaneHeader breadcrumb row h xl->xxl (32px, restores data studio
pane header height)
- BrowseDatabases logo boxes h/w xl->xxl (32px)
- DatabaseConnectionStringField rows h lg->xl (24px)
- DatabaseEngineList logo Center h/w lg->xl (24px)
- Metabot chat chevron flexes h/w md->lg (16px)
- MetricsFilterPill h lg->xl (24px)
- DatasetNotebook divider h xs->xxs (4px)
- DataReference NodeList icon w md->lg (16px)
- transforms-python ResizableBoxHandle grip h xs->xxs (4px)
Audited: no remaining bare h/w/maw/mah/miw/mih old-key props whose px
changed (verified line-identical against master for every occurrence).
* Address review: fix bdrs and responsive-object size tokens missed by the codemod
Two more escape classes from the scale codemod, found by a full sweep of
every touched-token form (string style props, responsive objects, CSS
vars, theme accessors) with each line compared against master:
- bdrs style prop (border-radius): remapped px-preservingly at 69 sites
in 58 files (xs 4px->xxs, sm 6px->xs, md 8px->sm). The codemod remapped
radius= but not its style-prop alias bdrs.
- Responsive object values (e.g. p={{ base: "md", sm: "xl" }}): remapped
the token VALUES at 9 sites in 6 files (base/sm/md breakpoint keys are
untouched; only their spacing values shifted: md 16->lg, xl 32->xxl,
xs 4->xxs).
Sweep result after fixes: zero remaining line-identical old-key token
references to spacing xs/md/lg/xl, radius xs/sm/md, or shadow xs/md in
any form. Intentional survivors unchanged: radius/shadow/bdrs "lg"
(disclosed broken-key-now-16px class), spacing/radius sm where the
px value did not change.
Verified: tsc, eslint (changed files), metabase/ui jest suite.
* Address review: fix remaining token escape forms (object props, braces props, style objects)
Final codemod escape classes from the exhaustive token sweep, all
remapped px-preservingly:
- Object-literal / default-parameter prop values (15 sites / 8 files):
ItemList picker-item default containerProps pb xs->xxs (4px, the
reported gap), FormFooter pt lg->xl, SlackSetup pt lg->xl,
EmbeddingSecretKeyWidget mb xs->xxs, CreateDashboardForm labelProps
mb xs->xxs x2, databases styles mb lg->xl, NotebookDataPicker
menuDropdownProps mt xl->xxl, Banner story py md->lg
- Braces-wrapped string props: CloudSMTPConnectionCard p lg->xl (24px)
and gap md->lg (16px), SettingsLdapForm mt xs->xxs,
DashboardLeaveConfirmationModal mb lg->xl
- style-object CSS properties: DatasetNotebook borderRadius xs->xxs
- Ternary token values inside responsive objects: SummarizeStep gap
md->lg (16px)
Sweep now returns zero real misses across every checked form: string
style props (incl. h/w/bdrs), braces props, responsive objects with
ternaries, object-literal/default-param props, style-object CSS props,
CSS var() references, theme.* accessors. Remaining grep hits are
untouched scales (lh, fz, size, breakpoints) or px-identical sm values.
* Address review: preserve Mantine spacing defaults exposed by Loki
* Update Loki Snapshots
* Address review: fix remaining scale regressions and visual guards
* Address review: make Loki update capture new references
* Update Loki Snapshots
* Address review: keep only targeted Loki references
* Address review: assign ticket component elevations
* Update Loki Snapshots
* Address review: tighten MonthPicker header spacing
* Address review: refine elevation visual stories
* Update Loki Snapshots
* Address review: keep renamed Menu Loki story selected
* Update Loki Snapshots
* Address review: preserve inherited Mantine defaults
* Address review: allow stock Mantine shadow tokens
* Address review: replace token audit test with ESLint rule
* Address review: drop unrelated whitespace-only dev/src changes
* Address review: use rem() in radius scale and rem docs
* Update Loki Snapshots
* GDGT-2486: Address PR comments
* GDGT-2486: Adjust negative tokens
* GDGT-2486: Address review findings
* Update Loki Snapshots
---------
Co-authored-by: Metabase Automation <github-automation@metabase.com>
|
||
|
|
84223a0d4d |
Restyle Number and Trend dashboard cards (UXW-5128) (#81131)
* Restyle Number and Trend dashboard cards (UXW-5128) * Adjust sizes and tooltips * Adjust sizes, fix icon * fix * Fixes after merge * Update Loki Snapshots * Fix multiple comparisons * Add link to card title, Implement query builder view * Undo scalar dashcard ellipsis menu styling changes * Design review fixes * Fix e2e * Fix e2e * Review fixes * Update Loki Snapshots * fix * Self review fixes * Self review fixes * Add a "Show comparison value" display setting to the Trend visualization (#81148) * Add Show comparison value setting to the Trend visualization * Address review comments * Standardize ellipsis menu styling across all dashboard cards (#81405) * Standardize dashboard card ellipsis menus * Update Loki Snapshots * Update Loki Snapshots * Refine dashcard menu button props --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> * Fix review comments * Update Loki Snapshots * Add more tests * Add keyboard navigation * Fix borders for dark theme * Update Loki Snapshots --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> Co-authored-by: Anderson <anderson@metabase.com> Co-authored-by: Anderson <ands.alves.nunes@gmail.com> |
||
|
|
05300912fb |
Radio component redesign (#80372)
* Redesign Radio * Add Radio stories to loki filter * disallow radio labelPosition * use StoryBoard in radio stories * add new story's snapshots * Radio.Card: border, leftSection, migrate call sites - card border 0.5px border-neutral-strong, none when disabled - children disallowed, leftSection slot for icons - EditUserStrategyModal + DataSegregationStrategyPicker on label/description * update loki snapshots * Icon color prop instead of css module in DataSegregationStrategyPicker * remove extra div * make the indicator in the radio card optional * fixed radio size, no size prop * radio card body via mantine text/stack * forwardRef radio card * radio card border: highlight when selected * update loki snapshots * drop size="xs" from radio call sites * fix card growing on select, interactive card stories, optional checkbox card indicator * skip interactive card stories in loki, update card snapshots |
||
|
|
c433278e06 | Gdgt 2472 loader (#80586) | ||
|
|
11be7cb3e7 |
Restyle NavLink with primary and secondary variants (#80640)
* Add primary/secondary NavLink variants and sibling components (GDGT-2469)
Restyle the design-system NavLink from Figma:
- Add `primary` (brand-fill selected) and `secondary` (subtle gray selected)
variants, with Default/Hover/Pressed/Selected states.
- Add the `icon-{primary,secondary,disabled,brand}-inverse` color tokens.
- Add `NavLinkButton` and `NavLinkBadge` sibling components for the right
section; both invert automatically on a selected `primary` item (no prop),
the button also has its own hover/press and stops click propagation.
- Give NavLink a `leftIcon` prop so it renders and colors the leading icon
itself instead of styling the section slot.
- Add a two-variant state-matrix Storybook showcase (Loki-covered).
* Migrate NavLink call sites to primary/secondary variants (GDGT-2469)
- Admin nav: `admin-nav` -> `secondary`
- Entity picker: current level -> `primary`, other levels -> `secondary`;
search results -> `primary`
- Remove the now-unused `default`/`mb-light`/`admin-nav` variant CSS
* Revert NavLink `leftIcon` prop back to `leftSection` (GDGT-2469)
Drop the polymorphic NavLink wrapper and colour the leading icon via the
`.NavLinkSection[data-position="left"]` CSS overrides again.
* Update Loki Snapshots
* Cleanup
* Update Loki Snapshots
* Add Loki reference for the NavLink Overview story (GDGT-2469)
* Update Loki Snapshots
* Address NavLink review comments (GDGT-2469)
- Migrate the remaining `default` NavLink (embedding SimpleDataPicker) to
`primary`, restoring its selected styling
- Render NavLinkButton as a `<div role="button">` (valid inside a NavLink's
`<a>`) with keyboard activation, instead of a `<button>`
* Update Loki Snapshots
---------
Co-authored-by: Metabase Automation <github-automation@metabase.com>
|
||
|
|
7692d3f479 |
UXW-5117 Fix Timeline event pills displayed on a wrong date when merged (#80567)
* UXW-5117 Implement timeline events stacks * Improve performance * Fix edge cases, improve performance, fix issues * Review fixes * Place chips at real data point * Self review |
||
|
|
27bfc0d005 |
Checkbox component redesign (#80262)
* checkbox redesign * small fix * fix disabled checkbox visual size * disallow checkbox labelPosition * add new loki tests * update loki snapshots * fixup! update loki snapshots * fix e2e specs relying on checkbox Icon-check/Icon-dash classes * checkbox card border add border-highlight token, use it for selected card * disallow checkbox size single 16px size; xs/sm were both 16px and md was unused * disallow checkbox card children * drop redundant role from checkbox icon * checkbox card body via mantine text/stack * forwardRef checkbox card * drop border on disabled checkbox card * drop disabled checkbox border ring alpha border over alpha bg stacked into a visible ring * hide unchecked card indicator icon mantine hides indicator icon via color:transparent; .icon color override defeated it * update colors loki snapshot |
||
|
|
a90f1170ba |
GDGT-2460 Update Toast styles (#80327)
* GDGT-2460 Update Toast styles * GDGT-2460 Port Toast to Mantine Notification * Update Loki Snapshots --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
ce55f0cdc6 |
Fix axis labels for timezone naive strings (#80121)
* fix axis labels for timezone naive strings * Update Loki Snapshots * fix lint after rebase * don't throw in tests --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
97ecbeab61 |
Update auto 'Show Dots' logic (#80071)
* Update auto 'Show Dots' logic * Update Loki Snapshots * PR feedback --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
0c5d3b3a59 |
GDGT-2459: Update Tooltip component (#79994)
* GDGT-2459 Update Tooltip styles * Update Loki Snapshots * Add Tooltip Overview Loki reference * GDGT-2459 Add Tooltip spacing and shadow * GDGT-2459 Use standard small Tooltip shadow * Update Loki Snapshots --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
e406a40c47 |
Reveal column descriptions on hover in the Detail visualization (#78875)
* Show column descriptions in the Detail visualization via an info icon tooltip * Reveal the Detail column description icon on hover instead of always * Reserve the description icon's width so hovering never shifts the Detail table * Address review comments * Remove delay showing info icon |
||
|
|
2f3d32a2ba |
UXW-4983 disable download to pdf for empty dashboard tab (#79490)
* UXW-4983 disable download to pdf for empty dashboard tab * Add tooltip * Update Loki Snapshots * Review fixes - hide button when dashboard is empty * Update Loki Snapshots * Fix e2e * Review fix --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
789d334847 |
Explorations (#73244)
* Fixed failing CI * Add query timestamp, refactor error marker * no-mbql-introspection pass * Order temporal-axis exploration variants by date, not aggregation When the row cap fires on a time series, aggregation-desc ordering keeps the highest-count buckets scattered across the full range: gaps in the x-axis, old spikes beating recent data. Temporal axes now order by the temporal breakout desc (aggregation desc as tiebreak) before the limit, so the most recent contiguous window survives. - time-facet: order by the temporal breakout (second breakout) desc - default: branch on dim type — temporal dims by recency, categorical dims keep aggregation desc - per-value-time-series: gains the same date-desc cap (had none, so a single fine-grained series could blow query-caching-max-kb) - all three also filter the NULL date bucket: it can't render on a time axis, and its position under ORDER BY DESC is warehouse-dependent (Postgres NULLs first, MySQL last), making cap survival non-deterministic otherwise - temporal-pattern-day/hour unchanged (bounded, no cap interaction) * Added debug info to interestingness calculations * even less mbql introspection * oops, delete .scratch * Sidebar UI fixes - error marker, loading shimmer * Interestingness >= 0.7 * Adjust prompts list animation * Fix e2e * Note that the LLM query planner is a POC, not used in production * Fix searching for explorations * Use llm-metabot-provider for everything * Log interestingless calculation as info for now * Log interestingless calculation back as debug * Fix FE lint * fix ai summary showing dirty while generating * Use llm-metabot-provider * collection breadcrumbs * add back null values for temporal variants * order by for temporal pattern variants * fix top-n-other * doh, fix temporal pattern queries * fix name in viz header, don't show be error in tooltip * sort numeric bins in heat map header, fix tests * UXW-4483 Add matching colors for category + breakout charts (#75912) * Fix CI * Fix units, fix loki * UXW-4522 Update last query run timestamp in background * Fix loki * Fix e2e * Improve Explorations viz types (#75958) * UXW-4483 Add matching colors for category + breakout charts * improve viz types first pass * add error boundary * add row chart support for dimension_value_colors * Fix typecheck * fix tests * better handling of segments with row fallback, fix null color bug --------- Co-authored-by: Denis Berezin <denis.berezin@metabase.com> * UXW-4402: Make explorations respect sandboxing/impersonation/db routing (#75572) * Make explorations respect sandboxing/impersonation/db routing * some self-review fixes * more cleanup * test fixes * doh * Fixing CI failures * capitalize 'By' consistently * Fixed CI failure * add loki snapshots * Review fix * Fixes after merge * Rework Explorations UX (#76300) * comment on exploration viz first pass * wip * wip * wip * UX changes first pass finished * code cleanup, fix, add tests * address review comments * Code cleanup --------- Co-authored-by: Nathan Voxland <nathan@metabase.com> * Refine explorations with metabot (#75326) * teach metabot about new exploration ux * refine explorations * address self-cr * pass over FE * CR response * fix test * Add :model/Exploration to api + eid-translation model-imports These modules reference :model/Exploration after merging the latest explorations base; declare the imports to satisfy model boundaries. --------- Co-authored-by: Kevin Kelleher <kevin.kelleher@metabase.com> * Harden explorations runner: - planner crash recovery - Bound timeline scoring - add metrics * UX tweaks * Additional logging of errors on ai summary, plus increased thinking budget * Improved timeline interestingness calculation * Handle sonnet 5 etc better * Make explorations groups a real thing (#76643) * Rename ExplorationThreadGroup model to ExplorationBlock Explorations organize their queries in a hierarchy: an Exploration has Threads (one per run), a Thread has Blocks, and a Block is one metrics-by-dimensions grouping that renders as a single sidebar heading. That persisted grouping entity was named ExplorationThreadGroup, and the word "group" was overloaded across the backend and frontend. Rename the entity to Block — the name the frontend already uses — so the larger data-model changes that follow start from one clear name. No behavior change. Renames :model/ExplorationThreadGroup -> :model/ExplorationBlock and table exploration_thread_group -> exploration_block. - Append a renameTable changeset rather than editing the createTable in place: the original is already applied to running dev DBs, so an in-place edit would fail checksum validation and force a DB rebuild. - Rename the model namespace/file; update all call sites, the require alias (:as block), and insert-blocks!. - Update model registrations (resolution, serdes models, copy, entity-id test) and re-sort the entries. - Refresh the model's docstrings to describe a "block". The planner's :thread-groups ctx key and the read-side group-tree are left untouched here; they belong to later read-side work. * Persist exploration pages with stable ids reconciled across reruns Within a Block, an Exploration's queries are organized into Pages: a Page is the bundle of queries for one (metric, dimension, query_type). Until now a Page existed only as a derived, content-addressed key computed at read time, so anything that wanted to point at a Page had to reference a string that changed whenever the grouping definition changed. This makes a Page a real persisted row whose surrogate id is its identity, so comments (and stars, coming next) can anchor to a Page and survive both a rerun — where queries are deleted and regenerated — and a future re-grouping. Introduce ExplorationPage: the persisted (block, card, dimension, query_type) page within a block. - New exploration_page table + model, FK to exploration_block (cascade) and report_card. Registered in resolution, serdes (excluded), copy, and the entity-id test. - Add exploration_query.page_id (NOT NULL, FK, cascade): the reconciler stamps it on every query, so it's required from the start. group_id stays for now as the block pointer the read-side and deep-link URLs use; dropping it waits for the read-side rework. - Reconcile in insert-plan-rows!, the single insert point for both initial Start and rerun: find-or-create a page per distinct key (stamping page_id), then GC pages left with no queries. reset-thread- for-rerun! is unchanged — pages survive a query delete (cascade is page->query), so find-or-create recovers the same page ids on rerun. - No DB unique index on the key: dimension_id is text (not portably indexable on MySQL) and planning is single-threaded, so uniqueness is enforced by find-or-create and matching is done in Clojure. - Migration changesets appended (the table/group_id changesets are already applied to the running dev db). Tests cover page-id stamping, rerun id-stability, orphan GC, and perms. * Return nested blocks→pages from the read API; drop query.group_id An Exploration Thread's structure is Blocks, each containing its Pages, each Page bundling the ids of its queries. Until now the backend instead returned a hand-rolled flat list of typed nodes (sidebar/singleton/page) linked by parent_group_id and keyed by auto:<...> strings, which the frontend then re-nested. Now that Blocks and Pages are real rows, return the nested shape the tables already encode: GET /:id returns :blocks, each with its :pages, built from ExplorationBlock + ExplorationPage rows and queries grouped by page_id. - New metabase.explorations.blocks (renamed from .groups): blocks-tree builds the nested shape; block headings + page names + score-sorting ported over; block-display-name made public for the LLM planner ctx. - Deep links go to a page by id: page-url → /question/research/<exp>/ page/<page-id>; the AI-summary and document-append builders pass the primary query's page_id. - Drop exploration_query.group_id (and its FK/index); a query reaches its block through its page now — build-row-context (used by the runner to finalize each row's MBQL) resolves the block via the page, which is what the group_id read there used to do. - API schemas: ExplorationQueryGroup → ExplorationBlockNode + ExplorationPageNode. - Migration changesets appended (the table is live on the dev db). - Rewrite the read-model + endpoint tests for the nested shape; every ExplorationQuery test fixture now hangs off a page (page_id NOT NULL). The create payload key stays :groups for now (separate input contract, touches the data-picker flow). FE consumption of :blocks lands next. * Consume nested blocks→pages on the frontend The frontend renders an Exploration Thread as sidebar headings — one per Block — with a navigable item per Page (a Page being the bundle of queries for one metric/dimension/variant). It used to receive a flat typed-node `groups` list and re-nest it itself; this switches it to the nested `blocks` array (block → pages) the read API now returns, and renames the navigable unit from "group" to "page" throughout. Users see the same sidebar and charts — this lines the frontend up with the backend's shape. - exploration.ts: add ExplorationBlockNode / ExplorationPageNode; thread `.groups` → `.blocks`; remove ExplorationQueryGroup / its display-type; the navigable id is now a page node id (kept as an opaque string). - Sidebar utils build the tree directly from blocks→pages (one heading per block, one child per page); score-sorting + empty-block filtering preserved. - The navigable unit is renamed group→page end to end: the route segment becomes /page/<id>, matching the backend's AI-summary deep links. The selection/url/comment-draft id stays a string via String(page.id), so the existing string-keyed plumbing is unchanged. - Visualization toolbar/header/comment-draft props renamed group→page; the mark-as-interesting handler is left a stub for the stars feature. - Specs, stories, and test-utils rewritten to the nested shape. The create-payload request key stays `groups` (separate input contract). * Retain comment-anchored pages across rerun GC; point comment URLs at /page/ A Page bundles the queries for one (metric, dimension, query_type), and a comment can be anchored to a Page — comment.child_target_id holds the page id. On a rerun an Exploration's queries are deleted and regenerated, and any Page left with no queries is garbage-collected. A Page a comment points at must survive that cleanup even when it ends up empty, or the comment would resolve to nothing. This makes the rerun GC comment-aware and moves comment deep-links onto the page route. - gc-orphan-pages! now retains an orphan page when some "exploration"- targeted comment's child_target_id equals the page id (compared as strings). Empty + no comment is still GC'd as before. Page ids are globally-unique PKs, so matching on child_target_id alone is unambiguous. - exploration-comment-url: /group/<child> → /page/<child>, matching the page route and blocks/page-url; stale "group ID" docstring fixed. - explorations imports :model/Comment for the retention lookup (comments doesn't depend on explorations, so no dependency cycle). - No backfill: the dev Postgres has zero exploration comments and zero auto:* child_target_id rows, so the optional migration is skipped. - Test: comment on a page → drop that page's selection → rerun → the page is retained and the comment still resolves; a no-comment empty page in the same rerun is still GC'd. * Generate short and long page names from metric, dimension, and variant A Page — the bundle of queries for one (metric, dimension, query_type) — is shown in the sidebar under its Block's heading, but is also referenced on its own elsewhere (comment deep-links, AI-summary chart titles, search). Page names are derived, not stored, so a Page needs both a short label that omits whatever its Block heading already shows (e.g. under a "Number of Orders" heading: "By Category over time") and a full, self-describing label for context-free places ("Number of Orders by Category over time"). This generates both from the Page's parts — the metric name, the dimension name, and a short per-variant qualifier — rather than by string-slicing a finished name. - variants/variant-qualifier: query_type → a short, param-free localized qualifier ("over time", "(Day of week)", ...) or nil for the default breakdown. Lives next to query-name so all variant naming is in one place. Unit-tested. - blocks: page-long-name and page-short-name generate from parts (metric + dimension + qualifier) using tru templates and the block's anchor; page-node emits :name and :long_name. - API ::ExplorationPageNode and the FE ExplorationPageNode type gain :long_name (FE display wiring left for separate work). - Same-dimension pages that differ only by variant (default vs top-n-other) are now distinctly named instead of colliding; a blocks-tree test that asserted the collision now asserts they differ. * Add ability to star pages, sidebar tabs UX (#76755) * add ability to star, sidebar tabs * - renamed "user interestingness" to "starred" - Let starred just be true|false, no null - Removed DELETE starred API, just PUT now --------- Co-authored-by: Nathan Voxland <nathan@metabase.com> * Fix CI: test compile error, migration boolean type, e2e formatting - documents/search_test.clj: qualify text->prose-mirror-ast with the documents.test-util/ alias in the exploration-thread exclusion test. The unresolved symbol broke backend test compilation, which cascaded to every app-db/driver test job and clj-kondo ("0 tests" everywhere). - 20260428_explorations.yaml: use ${boolean.type} instead of bare boolean for exploration_page.starred (required post-v49 so MySQL uses BIT(1)). - oxfmt formatting for two explorations frontend files (fe-lint). * Update e2e specs for reworked comment URL + explorations sidebar - comment helper: match the node comment link by path only. The `?new=true` marker was removed from comment URLs (base commit "respect query params in all urls"); the CommentsMenu unit test was updated but this e2e helper was missed. Verified the comment flow still works (opening a node's thread shows the new-comment editor). - explorations spec: align with the reworked sidebar — * `by <dim>` leaf label is now `By <dim>` * Scratchpad doc test: only Scratchpad is created synchronously; the AI Summary doc is created asynchronously, so stop asserting it (and the removed "Findings" grouping) at creation time. * AI-summary completion test: the AI Summary doc row now hangs under the thread heading ("Initial investigation"), not a "Findings" group. * remove the two "Add to document" tests — that control was removed. * Fix stylelint rule-empty-line-before in explorations CSS modules Add blank line before nested &:hover rules in ActionToolbar.module.css and ExplorationGroupVisualization.module.css. These surfaced in fe-lint's lint-css step once the oxfmt formatting failure was resolved. * - cleaned up migration file - Improve group renaming in code - preserve starts between re-runs like comments * Fixing CI failures --------- Co-authored-by: Kevin Kelleher <kevin.kelleher@metabase.com> Co-authored-by: Nathan Voxland <nathan@metabase.com> * Don't send empty messages to the LLM * address review comments from 76755 * UXW-4765 Hide charts (#77488) * UXW-4765 Hide charts * Review fixes * Fix the failing explorations unit tests and Loki stories (#77564) * Fix the failing explorations unit tests and Loki stories at the branch tip * Restore the original all-hidden copy and drop thread headings from it * Delete the outdated ExplorationGroupVisualization stories and their Loki references * Rephrase the hide toast to put the chart name first * UXW-4764 Remove interestingness badges on results (#77544) * UXW-4764 Remove interestingness badges on results * Drop e2e assertion for the removed sidebar interestingness markers * Colocate the interesting and error markers with their only consumers * Remove the interestingness marker from the timeline picker too * fix types, lints, formatting * Uxw 4779 update tab menu to match designs (#77543) * UXW-4779 Update sidebar tab menu to match designs * Show a pointer cursor across the whole SegmentedControl * Give the icon tabs img semantics and hide the decorative icon names * Let getExplorationSidebarTabsInfo own the tab icon config * Revert the SegmentedControl root pointer cursor, Mantine already puts it on the labels * Regenerate modules config after master merge The merge left 'explorations' and master's new 'entity-retrieval' out of sorted order, which metabase.core.modules-test asserts on. * Float the action toolbar pinned to the bottom of the chart canvas (#77667) * fix types * revert timelines in documents * Review fixes: stop requiring the QP's internal cache.impl from other modules Explorations code that saves and loads query-result snapshots was requiring metabase.query-processor.middleware.cache.impl directly — an internal namespace of the query-processor module that other modules aren't supposed to reach into. Since the module's :api set is generated from actual usage, those requires had put cache.impl on the module's public surface — something master never exposed. Modules talk to each other through a small public facade (metabase.query-processor.core) instead. The two helpers those callers need — do-with-serialization (write a QP result as a gzipped blob) and with-reducible-deserialized-results (read one back) — are now re-exported from the qp.core facade, and the explorations callers go through it. One consumer can't: queries/cached_result.clj is itself loaded while qp.core is loading, so requiring the facade from there would be a circular-load error at boot. It gets a proper home instead — a new small, documented public namespace, metabase.query-processor.result-serialization, re-exporting the same two helpers. With nothing outside the module requiring cache.impl anymore, the regenerated :api drops it, matching master. * Review fixes: explorations planner and AI-summary polish - Nested cardEmbeds: the AI-summary postprocessor wrapped only top-level cardEmbed nodes in resizeNode containers, while the card materializer walks the whole document — so a cardEmbed the LLM emitted inside e.g. a blockquote got a real card but rendered collapsed to zero height. The wrapper now walks the tree too (skipping already-wrapped nodes). - Awareness-tier sort validation: phase 2 validates that embedded categorical charts carry an explicit sort instruction, but the validation set was built only from top-tier breakouts — an awareness-tier categorical chart escaped validation entirely, and its prompt block (slim-block) never told the model a sort was required. Both tiers are validated now and slim-block emits the same per-chart sort directive as full-block. - Contextual-scoring gate order: safe-score+describe resolved its scoring context (a Card select and a SQL compile) before checking whether the row is scorable at all (chart config present, creator known, non-blank thread prompt) — wasted DB reads and a query compile for every unscorable row. The cheap gates run first now. Its docstring also claimed the LLM gate throws and is swallowed; it actually returns nil from a pre-flight cond. - Discovery-cache TTL: the top-K category discovery cache was a process-lifetime LRU, so a stale top-K result could be served forever on a long-lived instance; entries now also expire after 15 minutes. - Chart-name i18n: filtered-subset chart names built their multi-value summary as (str (count values) " values") — a hardcoded, unpluralized English fragment inside an otherwise-translated name. It uses trun now. - Docstring/comment corrections: effective-cardinality documented (and branched on) a :num-bins bucket that no caller can produce — branch and docs removed; run-one-plan-iteration! said it runs "the LLM planner" when the default is the mechanical one; a variants-test section comment claimed temporal variants add a not-null filter that no variant adds. Also repoints this module's result-blob serialization calls at the qp.core facade (the consumer half of the cache.impl cleanup in this stack's first commit). * Review fixes: explorations API permissions, documents, and search - Creating an exploration accepted arbitrary card ids (in blocks) and timeline ids without checking the caller could read them, so you could attach someone else's private metric or timeline and see its name/events later. Both are read-checked at create time now, and timeline hydration filters out timelines the viewer can't read. A duplicated timeline id used to hit a unique constraint and 500 the request; ids are deduped. - The restart endpoint re-selected the Exploration row after resetting the thread, but the reset never touches that row — the redundant select is dropped. - GET /api/document listed documents that belong to exploration threads, which search, recents, and collection items all deliberately hide. It applies the same filter now — which as a side effect removes an N+1 pattern where each listed doc could trigger several permission queries. - The document validator rejected {:type "doc" :content []} — the exact empty document the backend itself creates for new threads. An empty root doc is valid now. - Search: is_published was deliberately hidden from search responses on master (#66841) and this branch accidentally re-exposed it — hidden again in both search engines. Exploration descriptions are searchable in the legacy engine too, matching the new engine. - Data picker: metrics and dimension groups now use the same interestingness filter (a metric could previously reference dimension ids that appeared in no group), search matches the displayed "<group> - <dimension>" text, and a collection-ids query that ran twice per request runs once. - Consistency: the create event publishes after the transaction commits (observers could previously fire before the data was visible). * Review fixes: metabot / LLM infrastructure - Asking Claude for extended thinking while also forcing a tool call (tool_choice "required") produced a request Anthropic rejects with a 400. The guard that already handled this for structured output now covers the general tools path too. - The e2e guard that stops tests from hitting a real LLM provider threw a raw MalformedURLException on unparseable URLs; it now fails closed with its normal friendly message — and it was moved out of a catch-all that would have rerouted it, so it fails loudly. - The timeline tools handed the LLM broken XML (an attribute quote and a tag never closed) and interpolated user-written names/descriptions unescaped, letting content break the markup. Output is well-formed and escaped. - Hardening/docs: the retryable-error check bounds its exception cause-chain walk at 10 (a cyclic getCause could loop forever); the contextual-interestingness response schema now really forbids extra properties (its docstring claimed it already did); docstrings about thinking model buckets, return shapes, and inputs corrected. - Also repairs three test breaks that already existed at HEAD: an arity mismatch from the OpenRouter merge, a test asserting thinking blocks are dropped (they now stream as reasoning parts), and tests that didn't stub the LLM gate's newer usage/permission checks. * Review fixes: lib-metric, metrics transforms, startup sync, interestingness - group-by-source was re-exported for JVM Clojure but missing from the ClojureScript branch of lib-metric.core; added. - Metric/measure display-info inherited a default :long-display-name even where the method overrode :display-name, so the two could disagree; they're set together now. - A failure in the startup metric-dimension sync aborted server boot entirely; it's wrapped to log an error and continue. - The interestingness "leftovers" backfill loaded every unscored field for the database into memory in one select; it now streams one field at a time, and the namespace docstring mentions this second recompute trigger. - Added a unit test covering the dimensions :out transform (kebab-key renames + type-value keywordization). * Review fixes: enterprise perms/sync/deps, cached card query, test quality - Remote sync: an event payload that simply omitted a key used to satisfy a nil condition (e.g. "exploration_thread_id must be nil") — conditions now require the key to be present with the right value, checked with (find object k), which distinguishes a present-but-nil entry from an absent key. Vector values in removal conditions rendered as broken equality clauses; they render as IN. - Dependency graph: smartLink nodes with nil/placeholder ids added nil to the dependency set (the sibling cardEmbed branch already guarded this); guarded, with regression tests for both branches. - Test quality: the revisions exclusion test asserted the impl against its own private set, so it could never fail; it checks a hardcoded expected list now. Exploration bookmarks get real API coverage (create/list/delete/ordering/archived), and a mislabeled test string was fixed. * Review fixes: v4 instance-analytics views with explorations usage sources The AI-usage analytics views translate machine source names ("exploration", "contextual_interestingness", …) into human-readable labels with a SQL CASE. The explorations stack added its new sources to the v1 SQL files — but the views actually installed on instances are v3, and nothing reinstalled them, so users saw the raw machine names fall through the ELSE branch. Adds v4 of ai_usage_log and metabot_conversations (v3 plus the new CASE branches, with one consistent "Explorations" label — v1 said "Exploration" for the source but "Explorations" for the profile), a migration that reinstalls the views at v4, an updated pointer comment in usage.clj, and a parity test that fails whenever a known source/profile is missing a CASE branch in the latest views — exactly the drift that happened here. * Review fixes: frontend (minimal) and e2e Only the low-risk frontend fixes from the review pass, plus the e2e test fixes; the other frontend findings are deliberately left out to keep this branch's FE diff small. - getDimensionIcon existed byte-for-byte in two files; the duplicate module is deleted and its one importer now uses the copy in common/utils/columns.ts. - e2e: an assertion that searched for text inside another text node (and so always passed) is meaningful now; readSelectedText no longer breaks Cypress's retry behavior by asserting after a .then; and the seemingly pointless GET /api/metric/:id in seedMetrics got a comment explaining the side effect it exists for (it persists metric dimensions). * Remove the LLM query planner Explorations plans queries with the mechanical planner only now. Removed: the LLM planner namespace, its prompt templates (plan.selmer / repair.selmer) and prompt loader, the prompt-markdown renderers in the planner-context namespace, the explorations-query-planner setting (mechanical/llm/auto), and the :skip-no-llm exit for "planner requires an LLM but none is configured". pick-planner! now always returns the mechanical planner — kept as a seam so tests can stub it and a future planner has an obvious dispatch point. The planner-contract protocol and the shared context builder stay (they are planner-agnostic), and the query_plan_transcript column stays (every planner writes a transcript); its remark no longer describes an LLM exchange. * remove graph._dimension_value_colors (per bar colors) * Add row count to explorations API (#77740) Already available, we just need to persist it and thread it through the API. * Format fix * Exploration UX... um explorations. (#76901) * click thru * better comment links * chart avatars * avatars * allow subsubexplorations * disable explore further for Other group in top n charts and dimension anchored blocks * fix explore further for binned numeric columns * comment highlighting, remove avatars * comment label formatting * map and table highlighting * use built in viz click actions popover instead of custom * fix comment badge in dark mode * self review cleanup * fix and simplify new thread toast * fix be lints, add tests * fix tests * add fe, e2e tests * - POST /:id/explore-further now verifies the clicked page belongs to this exploration before copying its block, closing a hole where any page in the instance could be copied into any writable exploration. - key on the stable :explore-filters spec instead of a hash of the per-row-reconstructed :dataset_query (which minted fresh :lib/uuids and so never hit). build-row-context exposes :explore-filters on the row context for the cache to key on. Also reuse metabase.util/capitalize-first-char in the block title and thread name helpers instead of hand-rolled str/upper-case. * Code cleanup * Gate exploration derived metadata by viewer's permissions * fix lints * Code review improvements * Den/uxw 4741 explorations new sidebar (#77674) * Update tabs view, add filter button menu * Fix tests * Add sidebar items vertical lines + icons * Other sidebar changes - viewed state, sticky hierarchy * Show sub-explorations near it's parent item * Fix issues * Self review * Fixes after merge * Self review * Review fixes * Fix empty states * Review fixes --------- Co-authored-by: John Swanson <john.swanson@metabase.com> Co-authored-by: Kevin Kelleher <kevin.kelleher@metabase.com> Co-authored-by: Nathan Voxland <nathan@metabase.com> Co-authored-by: Denis Berezin <denis.berezin@metabase.com> * Explorations self-review cleanup (#77717) * Consolidate the explorations migration in place The 063 explorations migration had grown by accretion: tables were created and then altered by later changesets in the same unreleased file. Since the branch convention is to edit unmerged changesets in place, the final shapes are folded into the createTables: - exploration_query gains params, nullable dataset_query/entity_id, and page_id inline (the page_id FK and index stay separate — exploration_page is created later in the file, so they can't run earlier). - stored_result gains data_access_token inline; its backfill changeset was deleted outright — created and backfilled in the same release, it could only ever run against an empty table. - exploration_query_result gains chart_stats / metric_description / chart_description inline, which also retires the randomly-named changeset id v63.gr324asd. - idx_eqti_query_id deleted: the unique constraint on the same leading column already provides that index. The completed_at and analysis_started_at backfills are deleted outright: the explorations feature merges to master in one piece, so at upgrade time no instance can have pre-existing exploration threads to backfill. (They were also dangerous — they stamped columns the cancel endpoint and the completion handler gate on being NULL, permanently locking out any thread in flight during the migration.) Cosmetics: backfilled default docs are named "Scratchpad" to match runtime (the backfill said "Findings"); stale remarks corrected (chart_stats is EDN not JSON, the real query_type variant list, what collection_id actually defaults to). Existing dev databases will need a migration reset. * Set exploration thread started_at in the INSERT The thread was inserted without started_at and then immediately updated to set it, on the theory that the background worker must not see the thread before its dependent document/block/timeline rows exist. The whole create shares one transaction, so intra-transaction ordering is invisible to the worker (it reads on another connection; the commit is atomic) — the flag can simply be set in the INSERT, saving a write. * Guard exploration restart against in-flight work The restart endpoint could fire while the planner was still working on the thread, deleting its rows mid-plan and wedging the thread in a state no worker would ever pick up again. reset-thread-for-rerun! is now a CAS: it applies only to a terminal thread (completed_at set) with no query still mid-execution, and the endpoint returns 409 ("cancel it before restarting") otherwise. A successful reset leaves the thread exactly in the shape the planning worker's claim predicate picks up. The endpoint also published :event/exploration-update, which provably did nothing: restart changes no Exploration column, and the revision system skips unchanged objects. The publish is removed; the AI-summary doc reset still records a Document revision, which is what /mine's last-touched ordering composes. * Route mid-stream LLM errors through the provider-friendly rethrow LLM responses stream lazily; a connection timeout between SSE chunks escaped as a raw SocketTimeoutException because the provider error handling only wrapped request setup — the reduction happens later, in the agent loop. Every provider adapter's SSE reducible (claude, openai, openrouter, azure, bedrock) is now wrapped so consumption-time failures get the same translation as request-time errors. * Grant the read-only timelines scope under metabot-nlq The explorations metabot profile instructs the model to use the timeline tools, but those tools were granted only under :permission/metabot-other-tools while the profile is gated on NLQ — so for NLQ-only users they were silently filtered out of scope while the prompt kept referring to them. agent:timelines:* (whose only member is the read-only list/details scope) is now granted under both permissions. * Don't retry deterministically-failing fields in the interestingness leftovers pass The leftovers pass selects on dimension_interestingness IS NULL, so a field whose scoring deterministically throws was re-attempted on every sync forever. A per-process attempted-marker now skips fields that already failed in this process; a restart re-enables retry, so transient failures still converge. Process-local by design — no schema change, and no sentinel score leaking into product surfaces that read the column as a number. * Fail closed when a sandboxed field's user attributes are missing sandbox-token-for-table returned nil when the field was sandboxed but the user's sandbox-attribute lookup came up empty — and the downstream compatibility gate reads nil as "unrestricted". That's fail-open. It now returns a user-scoped "indeterminate" sentinel that matches nobody else's token (two indeterminate users don't match each other either): fail-closed. Also adds direct and end-to-end tests for the impersonation and routing token producers, which previously had none. * Require the (card, stored_result) pairing on the cached card-query path POST /api/card/:id/query with a stored_result_id read-checked the card but then served whatever snapshot id the client sent — nothing verified the snapshot actually belonged to that card. The pairing must now exist in stored_result_use (404 when it doesn't, so an unpaired probe can't confirm a snapshot exists). Both materialization paths already insert the pairing rows, and the table ships in the same unreleased release, so no legacy data lacks them. The viewer-lens check also denied a nil dataset_query only by accident (a schema exception swallowed by the catch — and in prod, where mu/defn logs instead of throwing, it would have failed open); it denies explicitly now. Plus: consistent t2/select-one forms in the endpoint, a stored_result_use docstring update, and the generated module config picking up the new StoredResultUse consumption. * Apply the personal-collection default when it resolves after first render The "default new explorations to my personal collection" logic captured personalCollectionId once in a useState initializer — if the selector resolved after the first render, the default never applied. It now reconciles when the id arrives, without overriding an explicit user choice. * Move the explorations migrations to v64 The v63 release branch has been cut, so the explorations feature will ship in v64 and its migrations belong in the 064 directory. Both files move (20260428_explorations.yaml and 20260519_exploration_bookmark.yaml) and every changeset ID is reprefixed v63. -> v64. No changeset content changes. Since we're already editing unreleased changesets in place on this branch, existing dev databases needed a migration reset anyway. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * v_metabot_conversations v4: restore include-deleted semantics lost in merge Master's #77542 (conversation branches) and this branch both created metabot_conversations/v4 in parallel: master's dropped the deleted_at IS NULL filters so soft-deleted (rewound/regenerated) messages are counted; ours added CASE branches for the new explorations usage sources and profile. The 'Merged from master' resolution kept only our copy, silently losing the deleted-message semantics — which is why deleted-messages-included-test and last-message-at-test fail: they assert master's behavior against our view. This drops the three filter lines from each dialect's v4 file so v4 is the union of both changes. Both installing changesets (our v63 and master's v64) are runOnChange: true and checksum the referenced file, so every instance reinstalls the corrected view on upgrade — no new version or migration needed. * Drop the redundant v_metabot_conversations v4 installer Master's #77542 (064/20260713_metabot_analytics_views_include_deleted.yaml) already installs metabot_conversations v4 via a runOnChange changeset against the same SQL files, which now carry both sides' changes: the explorations CASE branches and the include-deleted semantics. A second installer here was redundant — and would have collided with master's changeset IDs (v64.2026-07-13T00:00:00/01) had this file been mechanically renumbered to v64 like the other explorations migrations. The ai_usage_log v4 changeset stays: nothing on master installs that view. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Move the ai_usage_log v4 migration to 064 Same reasoning as the explorations tables: v63 is cut, this ships in v64. The changeset ID takes the next free slot for its date (v64.2026-07-13T00:00:02 — master's include-deleted file owns 00 and 01) rather than a mechanical v63->v64 rewrite that would have collided. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * Show a crossed-eye indicator with a tooltip on hidden pages in the exploration sidebar (#77907) * Explorations no AI Summary (#77878) * Extract AI Summary + document-in-exploration out of explorations Removes the LLM-generated "AI Summary" feature and the ability to display a document within an exploration. The feature-restoring (inverse) commit is kept on branch `explorations-ai-summary`; cherry-pick it to bring the functionality back. Backend: - Delete the explorations.ai-summary.* pipeline, composite, document-perms, prompt templates, and their tests - Drop exploration->Document creation, the /thread/:id/documents endpoints, the :documents hydrate, ai_summary_document_id, and the AI-summary reset - Revert the documents-module coupling: content-visibility gate, the document.exploration_thread_id FK/column, the view-log skip, and the GET /document list filter; delete documents/validate.clj - Drop the collection-items show_exploration_documents option and the exploration-doc exclusions in recents / search / serdes / remote-sync; drop the search exploration-thread-id attribute - Remove the ai_summary_document_id and document.exploration_thread_id migration changesets (edited in place) Frontend: - Delete the ExplorationDocument component and its tests - Remove the AI-summary sidebar node, completion toast, and the document API / urls / types; keep the shared comments sidesheet and page routing * Extract static card-embed in documents out of explorations Removes the static (cached-snapshot) card-embed capability from documents, so it can land on its own branch (`static-embed`) independently of the AI-Summary feature. The feature-restoring (inverse) commit lives on `static-embed`. Keeps the base `stored_result` model, the exploration query-result cache, and the cached-result permission helpers (`assert-can-view-cached-result!`) that the exploration `/query/:id` streaming endpoint uses. Also keeps the general `report_card.document_id` "card in a document" feature (it predates this work). Removed: - The cached-result serve path (`cached-dataset`, `apply-sort`, `deserialize-cached-result`, `allowed-chart-sorts`, `viewer-can-view-cached-result?`) and the `stored_result_id`/`sort` branch of `POST /api/card/:id/query` - `stored_result_use.card_id` (column + model arm; keeps `exploration_id`) - The documents serdes static-node filter (reverted to master's live-only rewrite) and the prose-mirror static docstrings - Frontend: the tiptap `cardEmbed` static rendering (the `stored_result_id` + `sort` + `chart_href` trio) across the CardEmbed node/menu, document hooks, redux, and `card.ts`; deleted the CardEmbedNode static unit spec * Fix pre-existing cljfmt/oxfmt formatting to unblock CI These 4 files (a stray blank line each) are unchanged from the base explorations branch — the format checks were failing on inherited, not extraction, changes. * more documents cleanup * Update Loki Snapshots * change references from entity (which was page or document) to just page --------- Co-authored-by: Kevin Kelleher <kevin.kelleher@metabase.com> Co-authored-by: Metabase Automation <github-automation@metabase.com> * Restore fix * Fix Metabot exploration tools streaming empty results to the client (#77990) Since |
||
|
|
95e367f533 |
Remove color-mix from theme color definitions (#78665)
* GDGT-2851 Remove color-mix from theme color definitions
color-mix can only be resolved by a browser, so any theme color using it
breaks static viz, which renders on the server (see UXW-4690, where the
gauge failed on background_page-tertiary-inverse).
Per design's proposal, introduce a new orion-95 stop and use solid scale
values for the page backgrounds:
- dark: page primary/secondary/tertiary -> orion 95/100/110
- light: page primary/secondary/tertiary inverse -> orion 95/100/110
The remaining mixes are collapsed to their literal equivalents:
shadow-default to a plain rgba, and tooltip-background-focused to the
next stop down from tooltip-background.
The mixes that derive from whitelabel colors (core-brand, core-filter,
core-summarize) are left alone, since their values aren't known ahead of
time. A unit test now guards against reintroducing color-mix anywhere
else in the theme definitions.
* Update Loki Snapshots
* Remove var() references and non-hsla literals from theme definitions
Static viz feeds theme colors straight into Color(), which only parses
literals, so a var() throws there exactly like a color-mix does — even
when it points at a perfectly ordinary color. Collapse the remaining
var() references to the underlying base color, and normalize the few
hex and bare-keyword values to hsla so every theme color is a literal.
The guard test drops the transitive resolver it no longer needs and just
asserts that every non-whitelabel color is an hsla literal.
* Fix regression in pivot table
* Empty commit to trigger CI
* Manually update pivot table loki snapshot
* Update e2e colour assertions for the new background tokens
Dark background_page-primary moved from orion-100 to the new orion-95,
so anything asserting rgb(7, 23, 34) for it now expects rgb(12, 28, 39).
The dark mode body assertion was matching color(srgb 0.0204 0.06792
0.0996) — Chrome's computed form for the old color-mix on
background_page-secondary. That token is a plain hsla now, so Chrome
reports it as rgb(7, 23, 34).
* Fix e2e
* Fix e2e
* Revert changes in assertDarkMode
* Fix oss e2e spec
* Revert SDK theming assertions to the deprecated background token
The SDK theme presets resolve colours through the first entry of
SDK_TO_MAIN_APP_COLORS_MAPPING, and for `background` that is the
deprecated `background-primary`, which still points at orion-100.
`background_page-primary` sits at index 1 and never gets read, so
table-root is unaffected by the move to orion-95.
* Address review feedback on the colour theme changes
Move the gold and upsell-gem literals into base-colors, since each was
repeated across both theme files. Drop the intermediate staticColors
binding in the guard test in favour of a second filter, and drop the
`?? {}` on theme.colors, which is a required field.
---------
Co-authored-by: Metabase Automation <github-automation@metabase.com>
|
||
|
|
a9731f6e65 |
style(sdk-bundle): Alert component redesign (#78260)
* redesign Alert, remove deprecated Alert component * fix styling so far * update alert stories * additional changes * small fix * refactor Alert config * address PR's comments * chores * Update Loki Snapshots * addressing the designing feedback in places * addressing the designing feedback in the Alert itself * add loki tests --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
528ae0a381 | Update table loading skeleton to use patterns (#78361) | ||
|
|
cd9d166077 | Fix trend viz in FE PDFs (#77612) | ||
|
|
8705016252 |
Fix gauge breaking in static-viz (#77724)
Fix gauge color crashing Co-authored-by: Kevin Kelleher <16599994+kevinkelleher12@users.noreply.github.com> |
||
|
|
03a8afdba8 |
Truncate categorical axis labels rather than not showing them (#77131)
* truncate categorical axis labels * Update Loki Snapshots --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
7b3ed42a61 |
GDGT-2585 Replace deprecated Input component (#77559)
* initial migration * fix link input size * remove dead files * Use Input.ClearButton for textinput clear icon Replace hand-rolled Tooltip+ActionIcon+Icon clear pattern w/ Mantine Input.ClearButton across 5 sites; c=text-secondary + rightSectionPointerEvents=all. Comment why LinkViz needs pointer-events on both root+input. * fix unit test * Update loki refs for parameter search Input->TextInput * Use Input.ClearButton in PermissionsEditorContent; rm blank line in SearchBar styled * addressing the PR's comments |
||
|
|
a9e1b0edf7 | GDGT-2454 Badge (#74714) | ||
|
|
07c824b6fb |
fix(sdk): derive dark mode color scheme based on sdk theme colors (#77269)
* add css variables for styled components * commit loki snapshot change * add resolved theme mocks * use real theme color values |
||
|
|
3512caf5ee |
refactor: Migrate feedback, components, misc, upsell, illustration & admin color tokens (#76871)
* Migrate remaining color tokens to new names (GDGT-2535)
Migrate the remaining legacy design-system color tokens to their new
names per the GDGT-2517 migration guide:
- feedback: error/danger -> feedback-negative, success ->
feedback-positive, warning/gold -> feedback-warning
- misc/core: info -> core-info, white -> core-white, shadow ->
shadow-default, overlay -> modal-overlay, focus -> input-focus,
metabase-brand -> core-metabase_brand
- admin: admin-navbar* -> navbar-admin*
- upsell/illustration: per guide
- remove unused bronze/copper/silver
Covers all reference forms: CSS `var(--mb-color-*)`, `color()`/helper
calls, Mantine `c`/`bg`/`color`/`iconColor` props (string and ternary),
object-literal `color:`/`iconColor:` keys, `ColorName` return values,
arrays, and `Record<_, ColorName>` value maps. Tightens `Undo.toastColor`
from `string` to `ColorName` so future token removals fail at compile
time. Maps the `Alert` component's semantic `color` prop to feedback
tokens internally so its call sites stay unchanged.
Also sweeps up brand/filter/summarize consumer stragglers left over from
the earlier (merged) migration. SDK/whitelabel input keys, the
derive-theme alias definitions, and static-viz's local palette are left
on their existing names by design.
* Migrate missed success-secondary props in StepperWithCards
The .module.css was migrated to feedback-positive-selected but the two
inline c="success-secondary" props (Done check icon + label) were missed.
* Update Loki Snapshots
* Fix toastColor type widening in metabot toast-prop helpers
Undo.toastColor tightened to ColorName in this branch; the two get*ToastProps
helpers returned object literals where toastColor widened to string (missing
as const, unlike their iconColor/dismissIconColor siblings).
* Update TransformListPage sync-color spec to migrated feedback token names
utils.ts color map was migrated (success/warning/danger -> feedback-*) but
the spec assertions still expected the old names.
* Update SDK theme + treemap specs for feedback/misc token mirrors
- get-embedding-theme: SDK mapping now fans text-white->core-white and
focus->input-focus, so the expected override gains those keys.
- treemap option: getColor("white") became getColor("core-white"); add
core-white to the test's MOCK_COLORS palette.
* Update Loki Snapshots
* Update toast color-attr assertions to migrated feedback token names
* Cleanup
* Migrate remaining color tokens in Button and Table stories
* Add explainer comment for colors naming convention
---------
Co-authored-by: Metabase Automation <github-automation@metabase.com>
|
||
|
|
6bf214f2c2 |
update Echarts to 6.1.0 (#75139)
* echarts 6.1.0 * adjusting BE test |
||
|
|
3487a7801f |
GDGT-2750: Treemap renders incorrectly in PDF export (#76996)
* fix treemap wrong size in grid driven pdfs * add missing loki screenshot |
||
|
|
132a7e674a |
refactor: Migrate icon tokens (disabled/secondary swap) (#76792)
* GDGT-2534 Migrate icon tokens (disabled/secondary swap + text aliases) Per design, the icon tokens are corrected: `icon-disabled` and `icon-secondary` swap meaning, and all four icon tokens become runtime aliases of their text counterparts. - Swap consumers: `icon-disabled` <-> `icon-secondary` call sites. - Redefine in both themes: - icon-primary -> var(--mb-color-text-primary) (value unchanged) - icon-secondary -> var(--mb-color-text-secondary) - icon-disabled -> var(--mb-color-text-disabled) - icon-brand -> var(--mb-color-text-brand) (brand[40] -> brand[50]) Token names are unchanged, so there's nothing to remove at cleanup. Icon tokens aren't SDK public theme keys and aren't used in static-viz, so the var() aliases are safe everywhere. * Update Loki Snapshots --------- Co-authored-by: Metabase Automation <github-automation@metabase.com> |
||
|
|
8f5eb11fba |
refactor: Migrate text and syntax tokens (#76508)
* GDGT-2533 Migrate text and syntax tokens
Sweep all consumer references from the legacy text/syntax tokens to their
new names (added in the foundation PR):
- text-tertiary -> text-disabled (and -inverse) — value shifts from solid
orion to orion-alpha (intended recolor)
- text-hover -> text-brand-hover (value unchanged)
- syntax-parameters(-active) -> text-syntax-parameter(-active)
- saturated-{blue,green,yellow,red,purple} -> core-{blue,green,yellow,red,
purple}-saturated (value unchanged)
`text-tertiary` and `text-hover` are also SDK public theme keys, so the
SDK_TO_MAIN_APP_COLORS_MAPPING right-hand sides gain the new internal names
as second targets and the public keys are left unchanged.
Per design review, the five saturated colors are renamed to
`core-{color}-saturated` palette primitives rather than `text-syntax-{role}`;
this PR adds those token definitions, removes the superseded text-syntax-*
ones, and keeps the legacy saturated-* definitions emitting until cleanup.
`text-secondary-opaque` is left untouched (open design question).
* Formatting
* Update Loki Snapshots
* Keep public SDK theme key text-tertiary in embedding examples/tests
text-tertiary is an embedder-facing SDK theme key (MetabaseColors), not an
internal token. The blanket sweep wrongly renamed it to the internal
text-disabled in three embedding spots that live outside the type-check
scope (e2e specs + the CLI scaffold snippet). Revert those to the public
text-tertiary; the SDK maps it internally via SDK_TO_MAIN_APP_COLORS_MAPPING.
---------
Co-authored-by: Metabase Automation <github-automation@metabase.com>
|