Skip to content

feat: persist filter state across sessions#430

Merged
AlexsJones merged 3 commits intoAlexsJones:mainfrom
mertsatilmaz:feat/remember-filters
Apr 13, 2026
Merged

feat: persist filter state across sessions#430
AlexsJones merged 3 commits intoAlexsJones:mainfrom
mertsatilmaz:feat/remember-filters

Conversation

@mertsatilmaz
Copy link
Copy Markdown
Contributor

Summary

  • Persists all TUI filter selections to ~/.config/llmfit/filters.json so they are restored on next launch
  • Follows the existing theme persistence pattern (Theme::save()/Theme::load())
  • Multi-select filters (providers, use cases, capabilities, quants, run modes, params buckets, licenses, runtimes) are stored by name rather than index position, so changes to the model database between runs won't corrupt saved state
  • All fields are Option for forward/backward compatibility

Test plan

  • Launch llmfit, change several filters (fit, sort, provider popup, etc.), quit
  • Re-launch llmfit — verify filters are restored
  • Delete ~/.config/llmfit/filters.json — verify app starts with defaults
  • Add a corrupt/empty JSON file — verify app falls back to defaults gracefully

Closes #428

Saves all filter selections (fit, availability, TP, sort, installed-first,
search query, and all multi-select popup filters) to ~/.config/llmfit/filters.json
so they are restored on next launch. Follows the existing theme persistence
pattern. Multi-select filters are stored by name rather than index position,
so changes to the model database between runs won't corrupt saved state.

Closes AlexsJones#428
Copy link
Copy Markdown
Owner

@AlexsJones AlexsJones left a comment

Choose a reason for hiding this comment

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

Nice work! Clean structure and good use of Option fields for forward compat. Left a few inline comments, mostly around save timing.

Comment thread llmfit-tui/src/tui_app.rs
if self.filtered_fits.is_empty() {
self.selected_row = 0;
} else if self.selected_row >= self.filtered_fits.len() {
self.selected_row = self.filtered_fits.len() - 1;
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This is the only call site for save_filters(), but most filter changes go through apply_filters() directly (fit, availability, TP, search, popups). That means those changes won't actually persist.

Consider saving on quit instead (in tui_events.rs where should_quit = true). That avoids disk I/O on every keystroke too.

Comment thread llmfit-tui/src/tui_app.rs
sim_cursor_position: 0,
context_limit,
theme: Theme::load(),
backend_hidden_count,
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Small thing: the old code called apply_filters() here, which was sufficient since all_fits is already sorted during construction. re_sort() re-sorts them again unnecessarily. Not a bug, just a wasted pass on startup.

Comment thread llmfit-tui/src/filter_config.rs Outdated
fn config_path() -> Option<PathBuf> {
let home = std::env::var("HOME")
.or_else(|_| std::env::var("USERPROFILE"))
.ok()?;
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Looks like Theme uses the same HOME/USERPROFILE approach so this is consistent. If you ever want to align with platform conventions (~/Library/Application Support on macOS, XDG on Linux), the dirs crate handles that nicely. No blocker though.

Address PR review feedback:
- Move save_filters() from apply_filters() to quit handler to avoid
  disk I/O on every filter change and ensure all paths persist state
- Replace re_sort() with apply_filters() on startup since all_fits is
  already sorted during construction
- Fix rustfmt formatting
@mertsatilmaz
Copy link
Copy Markdown
Contributor Author

Thanks for the review! Pushed a fix:

  • Moved save_filters() to the quit handler so all filter paths persist and we avoid disk I/O on every change
  • Replaced re_sort() with apply_filters() on startup since data is already sorted during construction
  • Fixed rustfmt

@AlexsJones
Copy link
Copy Markdown
Owner

Hey thanks for the quick turnaround on those fixes. Sorry I didn't flag this earlier but the config_path() function should probably use dirs::config_dir() instead of raw $HOME/.config. It's already a dependancy in the project and would handle macOS (~/Library/Application Support) and Windows properly. The Theme code has the same issue tbh but no reason to carry it forward here.

Other than that this looks good to go.

Replace manual HOME/USERPROFILE env var lookup with dirs::config_dir()
which handles macOS ~/Library/Application Support, Linux XDG, and
Windows AppData correctly.
@mertsatilmaz
Copy link
Copy Markdown
Contributor Author

#3dbf3db Added dirs as a dependency to llmfit-tui for the config_dir() switch. Happy to open a follow-up PR to migrate theme.rs (and any other HOME/USERPROFILE call sites) to dirs as well.

@mertsatilmaz
Copy link
Copy Markdown
Contributor Author

Opened #437 to migrate the remaining HOME/USERPROFILE call sites (theme.rs, update.rs, providers.rs) to dirs as well.

@AlexsJones AlexsJones merged commit c3962e4 into AlexsJones:main Apr 13, 2026
6 checks passed
This was referenced Apr 13, 2026
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.

[Feature Request] Remember filters

2 participants