Skip to content

fix(model-combobox): stop filter flash on close - #536

Merged
cooper (czxtm) merged 2 commits into
mainfrom
alex/fix-filter-flash
Jul 17, 2026
Merged

fix(model-combobox): stop filter flash on close#536
cooper (czxtm) merged 2 commits into
mainfrom
alex/fix-filter-flash

Conversation

@alex-sparus

Copy link
Copy Markdown
Contributor

Summary

  • fixes the flash on selecting a model
without.fix.mov
with.fix.mov

Test Plan

  • Manual test

Docs

  • Docs updated (companion PR in darkmatter/nixmac-web: #___)
  • No docs update needed

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor
Warnings
⚠️

No Linear issue ID found in this PR's title, description, or branch name (expected something like ENG-123). Add one so this work is traceable in Linear, or add #no-linear to the PR description to acknowledge it's intentionally untracked.

⚠️

New UI components were added without a Storybook story. Consider adding a sibling *.stories.tsx file:

  • apps/native/src/components/ui/combobox.tsx

📋 PR Overview

Lines changed 842 (+687 / -155)
Files 4 added, 8 modified, 0 deleted
Draft / WIP no
Has Test Plan yes
Linear issue no
No Test Plan Needed no
New UI components yes (1)
New Storybook stories no
New Rust modules no
New TS source files yes (1)
New tests yes (2)
package.json touched yes
Cargo.toml touched no
Infra / CI touched no

🔬 Coverage

Report Lines Statements Functions Branches
apps/native/coverage/coverage-summary.json 35.7% 35.2% 31.2% 30.0%

Generated by 🚫 dangerJS against c45acc8

@darkmatter

darkmatter Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

🎨 Storybook preview

Open Storybook preview

Updated for c45acc8


🧭 Story changes

Compared to main (snapshot diff at story level):

✏️ Changed stories (5)

  • ui.test.tsx › Input default matches snapshot (packages/ui/src/components/ui/ui.test.tsx)
  • ui.test.tsx › Input disabled matches snapshot (packages/ui/src/components/ui/ui.test.tsx)
  • ui.test.tsx › Input types match snapshot (packages/ui/src/components/ui/ui.test.tsx)
  • ui.test.tsx › Textarea disabled matches snapshot (packages/ui/src/components/ui/ui.test.tsx)
  • ui.test.tsx › Textarea matches snapshot (packages/ui/src/components/ui/ui.test.tsx)

💡 Update snapshots ↗ to regenerate baselines and open a PR.


⚠️ Detected UI changes (5)

These stories' HTML snapshots changed. I've added screenshots + links to the changed stories below. Review them carefully then accept the changes to regenerate baselines and include them in this PR:

UI/InputGroup › Inline Addons

UI/InputGroup › Inline Addons

UI/InputGroup › Block Addons

UI/InputGroup › Block Addons

UI/Select › Provider

UI/Select › Provider

Flows/Evolve › Playground

Flows/Evolve › Playground

Flows/Evolve › 1. Begin (idle)

Flows/Evolve › 1. Begin (idle)


Accept UI changes

  • Click here to accept these changes

Alternatively, you can run bun run test:update-snapshots locally to re-generate the baselines and then push the changes to this PR.

What does this do?

The screenshots above show UI changes detected by the Storybook
snapshot tests run on this PR. Each image is the rendered output of
a Storybook story from the code in this PR branch; the snapshot
test compared it against the committed baseline in
__snapshots__/ and flagged the difference.

Checking the box tells the darkmatter[bot] to regenerate the
baselines from this PR's current code and commit them directly to
this branch. The new baselines become the source of truth for
future runs — only accept after confirming the visual changes are
intentional.

Comparison baseline: the committed __snapshots__/ files on this
PR branch (carried forward from develop). Accept updates them in
place on this branch.

@darkmatter darkmatter Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean fix. Decoupling search (popover-internal filter text) from value (external controlled value) is the right call — handleSelect no longer touches the filter state, so the list doesn't re-filter during the close animation.

Verified the key scenarios hold:

  • Re-sync on open: handleOpenChange(true)setSearch(value) ensures the search field starts from the current value each time the popover opens.
  • Trigger display: Reading value directly eliminates the useEffect sync and its one-render delay — strictly better.
  • Custom option / Check icon: Both correctly switched to search; since handleInputChange calls both setSearch and onChange, value === search stays valid during typing.
  • External value change while closed: Trigger updates immediately; search syncs on next open. No stale UI visible.
  • External value change while open: search isn't clobbered mid-typing — better than the old useEffect approach.

No correctness, security, or quality concerns. Ship it.

@darkmatter

darkmatter Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review posted on PR #536.

Verdict: Approved. The fix cleanly decouples the popover-internal search state from the external value, eliminating the filter flash on close without introducing any behavioral regressions. Key scenarios verified: re-sync on open, trigger display, custom-option/Check-icon correctness, and external value changes (both open and closed states). The intent is well-documented in the inline comments. No correctness, security, or quality issues found.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually... this is something I wanted to change but in a different direction, it is tracked here: https://linear.app/darkmatterlabs/issue/ENG-567/empty-list-when-chosing-a-model-with-the-drop-down-a-second-time

In my opinion, the "search" string should not change when you choose a model. the current behavior means that if I want to choose a different model, I need to clear the search string every time, otherwise I only see the currently selected model as option.

The search string should probably only change when the user writes something to it. If anything, when selecting a model, perhaps it should be cleared (but no strong opinion here, an argument could be made to just keep it until the user types something else).

@czxtm

Copy link
Copy Markdown
Member

hmm i dont really have an opinion here and will defer to Juanpe Bolívar (@arximboldi) - in this scenario I usually will go look at shadcn or elements on my iphone since they iterate on this kind of stufff a lot. in this case they do something interesting though, they reset the input on blur unless you had something selected. so the rule is to restore your previous selection, which is empty the first time https://ui.shadcn.com/docs/components/base/combobox

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much better, thank you! I also love that the "default" option is now an explicit item in the list, a missing thing that was bothering me already a little bit...

About the code: perhaps the core component could be extracted into a combobox component that is not "AI model" related. Since the code was already like this, I don't think you are required to merge this and we can have this as a follow up if necessary.

@czxtm

Copy link
Copy Markdown
Member

Juanpe Bolívar (@arximboldi) hah i actually gave the same suggestion via slack

@darkmatter

darkmatter Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

attempt: f9e8c1b #1 proposed — unused import AI_MODEL_PROVIDERS; remove it from ai-models-tab.tsx
attempt: f9e8c1b #2 proposed — same root cause confirmed in build job; remove unused AI_MODEL_PROVIDERS import

Diagnosis

  • Failing checks: build (run 29544978518, job 87775694760, step "Build Tauri app") and TypeScript.
  • Root error: src/components/widget/settings/ai-models-tab.tsx(19,3): error TS6133: 'AI_MODEL_PROVIDERS' is declared but its value is never read.beforeBuildCommand 'bun run build' failed with exit code 2.
  • Cause: this PR's diff added two imports from @/lib/providers/ai-modelsAI_MODEL_PROVIDERS and getAiModelProvider. Only getAiModelProvider is used (twice, as defaultModel={getAiModelProvider(...)...}). AI_MODEL_PROVIDERS appears only on the import line → tsc noUnusedLocals fails the build.
  • Verified against PR head f9e8c1b: AI_MODEL_PROVIDERS has zero references in the file beyond the import.

Proposed fix (no push — dry run)

File: apps/native/src/components/widget/settings/ai-models-tab.tsx

  • Delete the single line AI_MODEL_PROVIDERS, (line 19) from the named import block.
  • Keep getAiModelProvider (used at the two defaultModel=... sites).
  • No other changes needed; bun run build / tsc --noEmit will pass.

If this is attempt #2

The fixer stops retrying this SHA after attempt #2. A human should apply the one-line removal above (or run bunx tsc --noEmit locally to confirm) and push to alex/fix-filter-flash.

— sent by Claude (ci_fixer)

@darkmatter

darkmatter Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

👀 CI is failing here (TypeScript, build) — on it. I'll post a diagnosis with a proposed fix; progress lands in the ci-fixer ledger comment. — sent by Claude (ci_fixer)

Match Base UI combobox semantics: the field itself is the cmdk input
(no search row in the popup), opening shows the unfiltered list with
the selected model checked and highlighted, and typing only filters —
values commit on selection. An explicit "Default: <model>" row commits
the empty string, which means "use the provider default"; consumers
pass the default model name.

- keep the model list across opens (clear only on provider change):
  async item remount mid-click made cmdk re-highlight and re-focus,
  which WebKit answers by collapsing the caret to the start
- freeze the filter query on select so the closing popover does not
  re-filter mid-animation
- control cmdk highlight so opening highlights the current selection
  and Enter cannot commit a different model than the chosen one
- restore native Home/End/arrow editing while the popover is closed;
  ArrowUp/Down open the list
- cover the interaction matrix with component tests
Move the input-anchored combobox (search/query split, controlled
highlight, key handling) to packages/ui as a reusable control;
ModelCombobox keeps only provider model loading and maps its
defaults onto the generic props. Generic interaction tests move to
packages/ui; the unit project now includes packages/ui tests, which
resurrected ui.test.tsx — its snapshots were stale and regenerated.
Comment thread apps/native/src/components/widget/controls/model-combobox.tsx
@czxtm
cooper (czxtm) added this pull request to the merge queue Jul 17, 2026
Merged via the queue into main with commit 13f32f7 Jul 17, 2026
10 checks passed
@czxtm
cooper (czxtm) deleted the alex/fix-filter-flash branch July 17, 2026 08:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants