feat(ui): complete Plan 06-01 - fix model scan + move model selector to input
- Fix Rust model scan to only list top-level models (not subdirectories) - Add scan_model_dir_top_level() function in model_files.rs - Create ModelSelectorPill component for chat input area - Remove model-section from Sidebar, move to ChatView - Update ChatView props to receive model-related props - Update tests for new ChatView props
This commit is contained in:
1 parent
3364dba5aa
commit
cb722e79a6
11 files changed
+1695
-254
No files matched your search
@@ -0,0 +1,77 @@
|
||||
---
|
||||
phase: 06-chat-ui-redesign
|
||||
plan: 01
|
||||
status: complete
|
||||
completed_at: "2026-04-02T12:00:00Z"
|
||||
---
|
||||
|
||||
# Plan 06-01 Summary: Backend + ModelSelectorPill
|
||||
|
||||
## Completed Tasks
|
||||
|
||||
### Task 1: Fix Rust model scan to top-level only ✅
|
||||
- **Status**: Already implemented
|
||||
- `scan_model_dir_top_level()` function exists in `model_files.rs`
|
||||
- `scan_models` Tauri command uses top-level scan (not recursive)
|
||||
- All 5 tests pass including `scan_model_dir_returns_only_top_level_gguf`
|
||||
|
||||
### Task 2: Create ModelSelectorPill + wire into ChatView + clean Sidebar ✅
|
||||
- **ModelSelectorPill.tsx**: Created at `tauri-app/src/components/ModelSelectorPill.tsx`
|
||||
- Extracts model deduplication logic from Sidebar
|
||||
- Renders compact pill button with status dot and dropdown
|
||||
- Shows spinner when `isSwapping` is true
|
||||
- Uses `getDisplayName` and `isInChatSelector` from modelAlias
|
||||
|
||||
- **Sidebar.tsx**: Cleaned
|
||||
- Removed `model-section` block (lines 131-170)
|
||||
- Removed model-related props: `models`, `localModels`, `selectedModel`, `onModelChange`, `onModelSwap`, `isSwapping`, `status`
|
||||
- Removed `ModelInfo` and `RuntimeModelFile` imports
|
||||
|
||||
- **ChatView.tsx**: Updated
|
||||
- Added model props to Props interface (lines 26-32)
|
||||
- Imported `ModelSelectorPill` component
|
||||
- Rendered `<ModelSelectorPill>` in hero input (line 627)
|
||||
- Rendered `<ModelSelectorPill>` in regular input (line 851)
|
||||
|
||||
- **App.tsx**: Updated
|
||||
- Removed model props from `<Sidebar>` component
|
||||
- Added model props to `<ChatView>`: `models`, `localModels`, `selectedModel`, `onModelChange`, `onModelSwap`, `isSwapping`, `status`
|
||||
|
||||
- **ChatView.test.tsx**: Fixed
|
||||
- Added `defaultChatViewProps` with required model props
|
||||
- Spread `{...defaultChatViewProps}` to all test renders
|
||||
|
||||
## Verification Results
|
||||
|
||||
### TypeScript Build ✅
|
||||
```
|
||||
> tsc && vite build
|
||||
✓ 6325 modules transformed.
|
||||
✓ built in 5.50s
|
||||
```
|
||||
|
||||
### Rust Tests ✅
|
||||
```
|
||||
running 5 tests
|
||||
test model_files::tests::scan_model_dir_filters_and_sorts_gguf_files ... ok
|
||||
test model_files::tests::scan_model_dir_ignores_directories_named_like_models ... ok
|
||||
test model_files::tests::scan_model_dir_skips_hidden_directories ... ok
|
||||
test model_files::tests::scan_model_dir_recursive_finds_nested_gguf ... ok
|
||||
test model_files::tests::scan_model_dir_returns_only_top_level_gguf ... ok
|
||||
|
||||
test result: ok. 5 passed; 0 failed
|
||||
```
|
||||
|
||||
## Files Modified
|
||||
- `tauri-app/src-tauri/src/model_files.rs` (already had top-level scan)
|
||||
- `tauri-app/src/components/ModelSelectorPill.tsx` (created)
|
||||
- `tauri-app/src/components/Sidebar.tsx` (cleaned)
|
||||
- `tauri-app/src/components/ChatView.tsx` (added model props + ModelSelectorPill)
|
||||
- `tauri-app/src/App.tsx` (routed model props to ChatView)
|
||||
- `tauri-app/src/components/ChatView.test.tsx` (fixed tests)
|
||||
|
||||
## Success Criteria Met
|
||||
- [x] Top-level model scan: `scan_models("./models")` returns only `.gguf` files directly in the folder
|
||||
- [x] Model selector visible in chat input pill (hero screen and regular screen)
|
||||
- [x] No model selector in the sidebar
|
||||
- [x] TypeScript and Rust compile without errors
|
||||
@@ -0,0 +1,41 @@
|
||||
# Task 1 Summary: Fix Rust model scan to top-level only
|
||||
|
||||
**Status:** ✅ Complete
|
||||
**Files Modified:**
|
||||
- `tauri-app/src-tauri/src/model_files.rs`
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. Added `scan_model_dir_top_level` function (lines 46-71)
|
||||
A new function that scans only the immediate children of a directory (depth=1), returning only `.gguf` files without recursing into subdirectories.
|
||||
|
||||
### 2. Updated `scan_model_dir` to use top-level scan (lines 31-44)
|
||||
Changed from calling `scan_model_dir_recursive` to calling `scan_model_dir_top_level` as the default behavior.
|
||||
|
||||
### 3. Kept `scan_model_dir_recursive` for future use (lines 73-97)
|
||||
The recursive function is preserved with `#[allow(dead_code)]` annotation for potential future use cases.
|
||||
|
||||
### 4. Updated tests
|
||||
- Renamed `scan_model_dir_finds_gguf_in_subdirectories` to `scan_model_dir_returns_only_top_level_gguf`
|
||||
- Updated assertion to expect only top-level models
|
||||
- Added new test `scan_model_dir_recursive_finds_nested_gguf` to verify the recursive function still works
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
cd tauri-app/src-tauri && cargo test scan_model
|
||||
```
|
||||
|
||||
All 5 tests pass:
|
||||
- `scan_model_dir_filters_and_sorts_gguf_files` ✅
|
||||
- `scan_model_dir_ignores_directories_named_like_models` ✅
|
||||
- `scan_model_dir_skips_hidden_directories` ✅
|
||||
- `scan_model_dir_recursive_finds_nested_gguf` ✅
|
||||
- `scan_model_dir_returns_only_top_level_gguf` ✅
|
||||
|
||||
## Success Criteria Met
|
||||
|
||||
- ✅ `scan_models` Tauri command returns only `.gguf` files in the immediate directory
|
||||
- ✅ Nested models in subdirectories are excluded from default scan
|
||||
- ✅ Recursive scan function remains available for future use
|
||||
- ✅ All existing tests continue to pass
|
||||
@@ -0,0 +1,70 @@
|
||||
# Task 2 Summary: Create ModelSelectorPill + wire into ChatView + clean Sidebar
|
||||
|
||||
**Status:** ✅ Complete
|
||||
**Files Modified:**
|
||||
- `tauri-app/src/components/ModelSelectorPill.tsx` (new file)
|
||||
- `tauri-app/src/components/ChatView.css` (added model selector styles)
|
||||
- `tauri-app/src/components/Sidebar.tsx` (removed model-section)
|
||||
- `tauri-app/src/App.tsx` (moved model props to ChatView)
|
||||
- `tauri-app/src/components/ChatView.tsx` (added ModelSelectorPill to input areas)
|
||||
- `tauri-app/src/components/ChatView.test.tsx` (updated tests with new props)
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. Created `ModelSelectorPill.tsx`
|
||||
New component that:
|
||||
- Displays a compact pill button with the selected model's display name
|
||||
- Shows a status dot indicating server status
|
||||
- Opens a dropdown listbox on click with available models
|
||||
- Filters models using `isInChatSelector` (Cooper + Iron only)
|
||||
- Supports loading/swapping state with spinner
|
||||
- Props: `models`, `localModels`, `selectedModel`, `onModelChange`, `onModelSwap`, `isSwapping`, `status`
|
||||
|
||||
### 2. Added CSS styles for ModelSelectorPill
|
||||
Added to `ChatView.css`:
|
||||
- `.model-selector-pill-wrapper` - container with relative positioning
|
||||
- `.model-selector-pill` - pill button styling
|
||||
- `.model-status-dot` - status indicator dot
|
||||
- `.model-selector-menu` - dropdown listbox
|
||||
- `.model-selector-item` - dropdown item styling
|
||||
- Responsive styles for input box placement
|
||||
|
||||
### 3. Cleaned Sidebar.tsx
|
||||
- Removed `model-section` div block (lines 131-170)
|
||||
- Removed model-related props from `SidebarProps` interface
|
||||
- Removed unused imports: `ModelInfo`, `ServerStatus`, `RuntimeModelFile`, `ChevronDown`, `Loader2`
|
||||
- Removed unused state: `dropdownOpen`, `dropdownRef`
|
||||
- Removed deduplication logic and `handleSelectModel` handler
|
||||
|
||||
### 4. Updated App.tsx
|
||||
- Removed model props from `<Sidebar>` component
|
||||
- Added model props to `<ChatView>`: `models`, `localModels`, `selectedModel`, `onModelChange`, `onModelSwap`, `isSwapping`, `status`
|
||||
|
||||
### 5. Updated ChatView.tsx
|
||||
- Added new props to `Props` interface
|
||||
- Imported `ModelSelectorPill`, `ModelInfo`, `ServerStatus`, `RuntimeModelFile`
|
||||
- Rendered `<ModelSelectorPill>` in two places:
|
||||
- Hero input block (inside `.input-box--hero`, before `.attachment-actions`)
|
||||
- Regular input block (inside `.input-box`, before `.attachment-actions`)
|
||||
|
||||
### 6. Updated ChatView.test.tsx
|
||||
- Added `defaultChatViewProps` object with required model props
|
||||
- Spread `{...defaultChatViewProps}` in all ChatView renders
|
||||
- Updated test assertion from "Connect an API" to "Runtime offline"
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
cd tauri-app && npm run build
|
||||
cd tauri-app && npm test -- ChatView
|
||||
```
|
||||
|
||||
Both build and tests pass successfully.
|
||||
|
||||
## Success Criteria Met
|
||||
|
||||
- ✅ ModelSelectorPill component created and renders in both input areas
|
||||
- ✅ Model selector removed from Sidebar
|
||||
- ✅ Model props flow from App.tsx → ChatView → ModelSelectorPill
|
||||
- ✅ TypeScript build passes with no errors
|
||||
- ✅ All ChatView tests pass
|
||||
Reference in new issue
Block a user