Enhance model selection and add footer navigation instructions#1271
Enhance model selection and add footer navigation instructions#1271imguoguo merged 6 commits intosipeed:mainfrom
Conversation
… model form button highlight
There was a problem hiding this comment.
Pull request overview
This PR updates the launcher TUI to improve navigation clarity (via a persistent footer) and refines model/channel selection behavior in the configuration menus.
Changes:
- Adds a footer bar with keyboard navigation hints to the TUI layout.
- Adjusts model menu behavior (row indexing, add-model entry placement, delete confirmation) and adds model-name validation.
- Updates the main menu channel entry to show an enabled/total channel count.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| cmd/picoclaw-launcher-tui/internal/ui/style.go | Adds a centered footer TextView with navigation instructions. |
| cmd/picoclaw-launcher-tui/internal/ui/app.go | Integrates the footer into the root layout and shows enabled/total channel counts on the main menu. |
| cmd/picoclaw-launcher-tui/internal/ui/model.go | Reworks model menu item ordering/indexing, adds add-model naming helper, validates model-name edits, and adds a delete confirmation modal. |
| cmd/picoclaw-launcher-tui/internal/ui/channel.go | Removes the “Back” item from the channel menu list (relying on Esc navigation). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func (s *appState) countChannels() (enabled int, total int) { | ||
| c := s.config.Channels | ||
| entries := []bool{ | ||
| c.Telegram.Enabled, | ||
| c.Discord.Enabled, | ||
| c.QQ.Enabled, | ||
| c.MaixCam.Enabled, | ||
| c.WhatsApp.Enabled, | ||
| c.Feishu.Enabled, | ||
| c.DingTalk.Enabled, | ||
| c.Slack.Enabled, | ||
| c.Matrix.Enabled, | ||
| c.LINE.Enabled, | ||
| c.OneBot.Enabled, | ||
| c.WeCom.Enabled, | ||
| c.WeComApp.Enabled, | ||
| } |
There was a problem hiding this comment.
countChannels hard-codes the same enabled-channel list that already exists in hasEnabledChannel(). This duplication can easily drift when new channels are added/renamed. Consider deriving both enabled/total from a single shared slice/helper (e.g., have hasEnabledChannel call countChannels, or introduce a method returning the bools).
| if s.modelNameExists(value, index) { | ||
| s.showMessage("Duplicate model name", fmt.Sprintf("Model Name '%s' already exists", value)) | ||
| return | ||
| } |
There was a problem hiding this comment.
The new “duplicate model name” validation blocks having multiple model_list entries with the same model_name. In this repo, duplicates are explicitly allowed for load balancing (see Config.ValidateModelList / tests), so this change removes a supported configuration capability. Consider removing this check or turning it into a warning/UX hint rather than a hard error.
| if s.modelNameExists(value, index) { | |
| s.showMessage("Duplicate model name", fmt.Sprintf("Model Name '%s' already exists", value)) | |
| return | |
| } | |
| // Allow duplicate model names for load balancing; duplicates are validated elsewhere. |
| }, | ||
| }) | ||
| } | ||
| // Add model entry appended at the end so the models map to rows 1..N |
There was a problem hiding this comment.
This comment says models map to rows 1..N, but with the Back item removed the model rows are now 0..N-1 (and the Add Model row is at N). Update the comment to match the actual indexing to avoid future off-by-one regressions.
| // Add model entry appended at the end so the models map to rows 1..N | |
| // Add model entry appended at the end so the models map to rows 0..N-1 (and Add model is at row N) |
…ent handling in channel menu
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Action: func() { | ||
| newName := s.nextAvailableModelName("new-model") | ||
| s.addModel( | ||
| picoclawconfig.ModelConfig{ModelName: newName, Model: "openai/gpt-5.2"}, | ||
| ) | ||
| s.push( | ||
| fmt.Sprintf("model-%d", len(s.config.ModelList)-1), | ||
| s.modelForm(len(s.config.ModelList)-1), | ||
| ) |
There was a problem hiding this comment.
The "Add model" action mutates s.config.ModelList via s.addModel(...) but does not set s.dirty = true. As a result, the user can add a model and then exit without being prompted to apply/save, losing the change. Mark the state dirty (and refresh menus if needed) when adding a model.
| newName := s.nextAvailableModelName("new-model") | ||
| s.addModel( | ||
| picoclawconfig.ModelConfig{ModelName: newName, Model: "openai/gpt-5.2"}, | ||
| ) |
There was a problem hiding this comment.
Same issue as above in the menu refresh path: this "Add Model" action appends to ModelList but doesn’t mark s.dirty = true, so the new entry can be lost on exit without an apply prompt. Set s.dirty (and refresh relevant menus) when adding the new model here too.
| ) | |
| ) | |
| s.dirty = true | |
| refreshModelMenuFromState(menu, s) |
| SetDoneFunc(func(buttonIndex int, buttonLabel string) { | ||
| s.pages.RemovePage(pageName) | ||
| if buttonLabel == "Delete" { | ||
| s.deleteModel(index) |
There was a problem hiding this comment.
Deleting a model here ultimately only removes it from ModelList and pops the page; it doesn’t mark s.dirty = true. That means a delete can be silently discarded if the user exits without applying/saving. Also consider clearing/updating Agents.Defaults.Model if the deleted model was selected, and refreshing the main/model menus after deletion to avoid stale UI state.
| s.deleteModel(index) | |
| s.deleteModel(index) | |
| // Mark configuration as dirty so the deletion is persisted. | |
| s.dirty = true | |
| // If the deleted model was the default, clear the default model reference. | |
| if s.config != nil { | |
| if s.config.Agents.Defaults.Model == model.Name { | |
| s.config.Agents.Defaults.Model = "" | |
| } | |
| } |
…d#1271) * fix(tui): fix model selection and enforce unique model_name, also fix model form button highlight * feat(tui): add footer view with navigation instructions and update menu structure * fix(tui): update model selection labels for clarity and consistency * refactor(tui): remove unused rootChannelDescription function * refactor(tui): simplify rootModelDescription and remove unused 'q' event handling in channel menu * fix(tui): keep selected model name updated Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…d#1271) * fix(tui): fix model selection and enforce unique model_name, also fix model form button highlight * feat(tui): add footer view with navigation instructions and update menu structure * fix(tui): update model selection labels for clarity and consistency * refactor(tui): remove unused rootChannelDescription function * refactor(tui): simplify rootModelDescription and remove unused 'q' event handling in channel menu * fix(tui): keep selected model name updated Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…d#1271) * fix(tui): fix model selection and enforce unique model_name, also fix model form button highlight * feat(tui): add footer view with navigation instructions and update menu structure * fix(tui): update model selection labels for clarity and consistency * refactor(tui): remove unused rootChannelDescription function * refactor(tui): simplify rootModelDescription and remove unused 'q' event handling in channel menu * fix(tui): keep selected model name updated Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
📝 Description
🗣️ Type of Change
🤖 AI Code Generation
🔗 Related Issue
📚 Technical Context (Skip for Docs)
🧪 Test Environment
📸 Evidence (Optional)
Click to view Logs/Screenshots
☑️ Checklist