v2.14.0: sidebar pagination + named LLM provider profiles
Pagination for the sidebar so large databases stay usable in the TUI, plus named LLM provider profiles so switching between OpenAI / Ollama / a remote inference rig is one keystroke instead of a config edit.
Highlights
Sidebar cursor pagination (#15)
The sidebar previously fetched a fixed 200 rows and stopped, so on large DBs older entries were unreachable from the TUI. Now each tab fetches one page (DEFAULT_PAGE_SIZE = 200) at a time using cursor-based keyset pagination.
- Header shows
conversations · N loaded · more (Ctrl+L)when more pages remain - New binding
Ctrl+L(and/load-moreanalogue via the action) appends the next page in place - Switching mode/search resets the cursor and reloads from page 1
- Search uses cursor mode too
Also fixed a latent bug in db.search_conversations: when FTS5 found no matches AND cursor mode was requested, it was returning a bare list instead of a PaginatedResult. Wiring the sidebar through cursor mode exposed it.
Named LLM provider profiles (#16)
Define multiple endpoints in ~/.ctk/config.json and switch between them at startup or live in the TUI:
{
"providers": {
"default": "muse",
"openai": {"base_url": "https://api.openai.com/v1", "default_model": "gpt-5"},
"muse": {"base_url": "http://muse.lan:8000/v1", "default_model": "qwen3-omni"},
"ollama": {"base_url": "http://localhost:11434/v1", "default_model": "llama3.1:70b"}
}
}- TUI:
/providerlists profiles,/provider <name>rebuilds the active provider live;/modelnow shows profile + base_url too - CLI:
ctk --provider <name>flag at startup - API keys: reads
<PROFILE>_API_KEYfirst (soMUSE_API_KEYworks), falls back toOPENAI_API_KEY, then config file (with warning) - Backward compat: legacy configs with only
providers.openaikeep working
Pilot test harness for modals (#14)
Three releases (2.13.1, 2.13.2, 2.13.3) shipped this month because nothing actually opened the modals headlessly before tagging. New harness in tests/unit/test_textual_tui.py uses Textual's Pilot to push every modal, force a render via pause(), and dismiss via Esc / y / n. Would have caught the _render and _bindings shadowing crashes at unit-test time.
Documentation refresh (#17)
- README rewritten around the 2.14.x surface (citation bumped, dropped references to ~17 removed subcommands and the Views system)
- CLAUDE.md gained the Textual
_render/_bindingsshadowing trap in Gotchas, refreshed test counts, documented the Pilot harness pattern
Stats
- 1700 unit tests pass
- ~1300 net lines added across the four PRs
Install
pip install --upgrade conversation-tk