Show visualization brush filters in toolbar and chat#58
Draft
Dev-Lan wants to merge 8 commits into
Draft
Conversation
Let IntervalFilterComponent and PointFilterComponent write their edits through an injected callback instead of always targeting dataFiltersStore.setDataSelection. Defaults to the existing LLM-filter path, so current callers are unaffected. This lets the same widgets drive a visualization brush (which lives in selectionsStore). Export both components from the tool-calls barrel for reuse. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Brush/click selections created by interacting with a visualization live in selectionsStore, keyed by the source viz's uuid. This hook derives them as first-class filters, gated to currently-active vizzes (so a closed viz's stale selection never shows) and to non-empty selections. The gating logic is extracted into a pure, exported selectBrushFilters function and unit-tested. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
FilterToolbar now renders a chip for each active brush filter alongside LLM-originated filters. Clearing a brush chip clears the authoritative selectionsStore entry; DashboardCard observes its own brush being cleared and remounts the source UDIVis (via a key bump on the true->false transition) so the rendered brush rectangle clears too — UDIVis offers no programmatic way to drop it otherwise. Fixes the toolbar half of #29. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add BrushFilterWidgets, rendered at the bottom of the message list, which shows an adjustment widget for each active visualization brush filter — mirroring how LLM-originated FilterData filters render. Edits write back to selectionsStore (not the conversation), so brush filters drive the filter pipeline both ways without leaking into the LLM message history. Emptying a widget fully clears the brush, matching the toolbar. Fixes the chat-widget half of #29. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Brush filter adjustment widgets now appear in the same muted message bubble as LLM-originated FilterData filters, instead of a separate "Visualization filters" section at the bottom. Addresses feedback that they should read as chat messages like agent-generated filters. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DashboardCard previously stripped this viz's own uuid from the selections passed to UDIVis, so an edit made in the chat adjustment widget never reached the source chart. As a result the brush rectangle didn't move on edit (issue 3), and entity counts only updated when a second viz happened to bind the selection into the shared DataSourcesStore — i.e. two-way filtering appeared to work only with more than one visualization (issue 2). Pass the full selection set through instead; UDIVis no-ops an external selection equal to its current one, so live brushing doesn't loop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Unchecking every value in a point/categorical brush widget used to null out the selection, which removed the widget entirely. Now the emptied selection is kept (present but valueless), so the chat adjustment widget persists for re-selection. useBrushFilters gates on a present (non-null) selection; the toolbar chip is suppressed for valueless brushes via the new brushHasValue helper. The brush is fully removed only via the toolbar chip's clear action. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A brush originating from a visualization can report a range slightly outside the data extent, because some charts pad their axis with a visual buffer. The slider thumbs were bound to that raw value and could sit past the ends of the track. Clamp only the thumb positions to [min, max]; the stored range value is unchanged, so the filter still reflects the brush. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
selectionsStore(keyed by the source viz'suuid). Nothing is mirrored intodataFiltersStore, and no synthetic messages are added to the conversation — so brush filters never leak into the LLM message history and there's no cross-store sync to drift or loop.FilterDatapath is untouched (the new behavior is additive), so non-visualization filters work exactly as before.Fixes hms-dbmi/udi-yac#24.
Changes
src/features/dashboard/hooks/useBrushFilters.ts(new) — derives brush filters fromselectionsStore+ active visualizations, gated to currently-active vizzes (no stale chips from closed vizzes) and non-empty selections. PureselectBrushFiltersextracted and unit-tested.src/features/dashboard/components/FilterToolbar.tsx— renders a chip per brush filter; clearing a brush chip clears itsselectionsStoreentry.src/features/dashboard/components/DashboardCard.tsx— when a viz's own brush is cleared externally, remountsUDIVis(key bump on the true→false transition only) so the rendered brush rectangle clears too.src/features/tool-calls/components/{Interval,Point}FilterComponent.tsx— add an optionalonCommitoverride (defaults to the existingdataFiltersStorepath) so the same widgets can write toselectionsStore. Exported from the tool-calls barrel.src/features/chat/components/BrushFilterWidgets.tsx(new) +MessageList.tsx— render an adjustment widget per brush filter at the bottom of the chat; edits write back toselectionsStore, and emptying a widget fully clears the brush.Spec
.claude/todo/done/show-viz-filters-in-toolbar-and-chat.md(issue hms-dbmi/udi-yac#24).Test plan
Automated:
pnpm test— 151 tests pass (5 new forselectBrushFiltersgating).pnpm typecheck— clean.pnpm lint— clean (one pre-existingDataCountswarning, unrelated).pnpm build— succeeds.Manual (needs a running app):
FilterDatafilter → still works as before (no regression).Notes / known limitation
selectionsStore, but the source chart excludes its own selection by design and UDIVis has no API to reposition an existing brush, so the source chart's own brush rectangle does not visually move on a widget edit (it does clear correctly on removal). This is a UDIVis constraint, not a data-correctness issue.🤖 Generated with Claude Code