Skip to content

feat(vscode): improve model selector — Recommended group, standard styles, free badge, auto-scroll#7137

Merged
kirillk merged 18 commits intomainfrom
kirillk/model-picker
Mar 17, 2026
Merged

feat(vscode): improve model selector — Recommended group, standard styles, free badge, auto-scroll#7137
kirillk merged 18 commits intomainfrom
kirillk/model-picker

Conversation

@kirillk
Copy link
Contributor

@kirillk kirillk commented Mar 16, 2026

Context

fixes #7123

Improves the model selector in the VS Code extension webview across several dimensions: surfaces recommended models, adopts design-system tokens throughout, upgrades the free badge to a proper kilo-ui component, and auto-scrolls to the active model when the picker opens.

Implementation

Recommended group

  • Adds a "Recommended" group at the top of the model list. Models with a recommendedIndex are collected and sorted by that index, then rendered as the first group before the per-provider groups.
  • Adds the model.group.recommended i18n key to all locale files.

Standard styles (kilo-ui design system)

  • model-selector-group-label now uses CSS variable tokens (--font-size-x-small, --font-weight-medium, --line-height-large, --letter-spacing-normal) instead of hard-coded values, matching the kilo-ui design system.
  • .model-selector-item.active now applies --surface-interactive-active background and --text-on-interactive-base foreground (with VSCode fallbacks) so the selected row is visually distinct. Bold weight is moved to the inner name span only.
  • Group dividers use --border-weak-base / --vscode-panel-border fallback.
  • The hand-rolled .model-selector-tag CSS block is removed entirely — replaced by the <Tag> component from kilo-ui (see below).

Free badge via kilo-ui Tag

  • isFree logic is updated to prefer the explicit model.isFree boolean attribute from the backend, with a fallback to cost.input === 0 && cost.output === 0 for the transition period (TODO comment marks it for cleanup once backend ships the attribute).
  • The inline <span class="model-selector-tag"> is replaced with <Tag data-variant="member"> from @kilocode/kilo-ui/tag, which carries the correct design-system styling.

Auto-scroll to selected model

  • When the popover opens, after focusing the search input, scrollIntoView({ block: "center" }) is called on the .model-selector-item.active element, so the currently active model is visible without manual scrolling.

Search scope

  • Model filtering is narrowed to model.name only (previously also matched providerName and id), reducing noise in search results.

Alphabetical sorting within groups

  • Models within each provider group are sorted alphabetically by name, clustering same-prefix variants together for easier scanning.

Screenshots

Before

before-light before-dark

After

after-light after-dark

How to Test

  1. Open the extension and click the model selector.
  2. Verify the picker opens with a Recommended group at the top.
  3. Confirm the active model is scrolled into center view automatically.
  4. Check that free-tier models show the Free badge using the kilo-ui Tag styling.
  5. Confirm group label and active-item styling matches the design system (background highlight, not just bold text).
  6. Type in the search box — results should filter by model name only.

Get in Touch

kirillk added 6 commits March 16, 2026 14:14
Models with a recommendedIndex are grouped into a dedicated
'Recommended' section at the top of the model picker, sorted
by index. Adds model.group.recommended i18n key for all 16
supported languages.
@kilo-code-bot
Copy link
Contributor

kilo-code-bot bot commented Mar 16, 2026

Code Review Summary

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
packages/kilo-vscode/webview-ui/src/components/shared/ModelSelector.tsx 76 Search now only matches model.name, so provider-name queries still fail for non-Kilo models where providerName is rendered separately from the row label.
Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

None.

Files Reviewed (8 files)
  • packages/kilo-gateway/src/api/models.ts - 0 issues
  • packages/kilo-vscode/tests/unit/model-selector-utils.test.ts - 0 issues
  • packages/kilo-vscode/webview-ui/src/components/shared/ModelSelector.tsx - 1 issue
  • packages/kilo-vscode/webview-ui/src/components/shared/model-selector-utils.ts - 0 issues
  • packages/kilo-vscode/webview-ui/src/styles/chat.css - 0 issues
  • packages/kilo-vscode/webview-ui/src/types/messages.ts - 0 issues
  • packages/opencode/src/provider/models.ts - 0 issues
  • packages/opencode/src/provider/provider.ts - 0 issues

@kirillk kirillk marked this pull request as draft March 16, 2026 20:11
@kirillk kirillk marked this pull request as ready for review March 16, 2026 20:20
@lambertjosh
Copy link
Contributor

Looks pretty good to me!

Model filtering is narrowed to model.name only (previously also matched providerName and id), reducing noise in search results.

Hrm, do folks sometimes search on these? I think I've probably searched by company/provider before than model name to get the list. For example I think I once searched for nvidia because I couldn't remember the model names themselves.

@kirillk
Copy link
Contributor Author

kirillk commented Mar 16, 2026

Looks pretty good to me!

Model filtering is narrowed to model.name only (previously also matched providerName and id), reducing noise in search results.

Hrm, do folks sometimes search on these? I think I've probably searched by company/provider before than model name to get the list. For example I think I once searched for nvidia because I couldn't remember the model names themselves.

nvidia will work since cloud gives us names prefixed with provider name, so we use it in search
Screen Shot 2026-03-16 at 5 14 00 PM

also I sort models by sing provider names too, so Open AI et al are all together now
Screen Shot 2026-03-16 at 5 15 32 PM

@kirillk
Copy link
Contributor Author

kirillk commented Mar 16, 2026

Looks pretty good to me!

Model filtering is narrowed to model.name only (previously also matched providerName and id), reducing noise in search results.

Hrm, do folks sometimes search on these? I think I've probably searched by company/provider before than model name to get the list. For example I think I once searched for nvidia because I couldn't remember the model names themselves.

What changed is that we know filter on what people actually see on the list. Provider name + model name exists so this filter will work.

They only thing that won't work if you search by model ID. The reason for that is if you search kilo it will have all kilo gateway models, which doesn't make much sense. I can handle a special case if you paste a model ID it will find it just not sure if it solves a problem that exists or not. My gut feeling is that there's no real need for that but we can wait for other folks to respond.

@lambertjosh
Copy link
Contributor

What changed is that we know filter on what people actually see on the list. Provider name + model name exists so this filter will work.

OK, I read that as we removed provide name matching but as long as we keep that I'm good with this 👍

export function isFree(model: Pick<EnrichedModel, "isFree" | "cost">): boolean {
if (model.isFree !== undefined) return model.isFree
// TODO: fallback to cost check — delete once backend ships isFree attribute
return model.cost?.input === 0 && model.cost?.output === 0
Copy link
Contributor

Choose a reason for hiding this comment

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

WARNING: Zero-cost fallback will mislabel unknown-price models as free

The provider payload normalizes missing pricing to cost: { input: 0, output: 0 }, so this check now returns true for any custom or BYOK model that simply does not publish cost metadata. The TUI/app still gate the free badge to Kilo/opencode models, so this change starts showing Free on models whose price is actually unknown rather than free.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

solved with a temporary fallback

@kirillk kirillk enabled auto-merge March 17, 2026 13:15
@kirillk kirillk marked this pull request as draft March 17, 2026 13:52
auto-merge was automatically disabled March 17, 2026 13:52

Pull request was converted to draft

kirillk added 3 commits March 17, 2026 11:59
Add isFree field to the full model data pipeline: gateway schema,
ModelsDev.Model, Provider.Model, OpenAPI spec, and SDK types.
Remove cost-based isFree fallback in the extension model selector —
rely solely on the backend-provided value.
@kirillk kirillk marked this pull request as ready for review March 17, 2026 17:40
@kirillk kirillk merged commit b9ddda1 into main Mar 17, 2026
16 checks passed
@kirillk kirillk deleted the kirillk/model-picker branch March 17, 2026 21:23
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.

Model Picker: Add Recommended Models Section and Free Model Badge

4 participants