@@ -196,6 +196,11 @@ type UI struct {
196196
197197 isTransparent bool
198198
199+ // themeKey identifies the currently applied theme so applyTheme can
200+ // skip the expensive style rebuild when switching to a provider that
201+ // resolves to the same theme.
202+ themeKey string
203+
199204 focus uiFocusState
200205 state uiState
201206
@@ -380,6 +385,12 @@ func New(com *common.Common, initialSessionID string, continueLast bool) *UI {
380385
381386 status := NewStatus(com, ui)
382387
388+ // Seed the active theme key from the large model provider so the
389+ // first model selection can correctly skip a redundant theme swap.
390+ if cfg := com.Config(); cfg != nil {
391+ ui.themeKey = styles.ThemeKeyForProvider(cfg.Models[config.SelectedModelTypeLarge].Provider)
392+ }
393+
383394 ui.setEditorPrompt(com.Workspace.PermissionSkipRequests())
384395 ui.randomizePlaceholders()
385396 ui.textarea.Placeholder = ui.readyPlaceholder
@@ -1842,8 +1853,10 @@ func (m *UI) handleSelectModel(msg dialog.ActionSelectModel) tea.Cmd {
18421853 } else {
18431854 if msg.ModelType == config.SelectedModelTypeLarge {
18441855 // Swap the theme live based on the newly selected large
1845− // model's provider.
1846− m.applyTheme(styles.ThemeForProvider(providerID))
1856+ // model's provider. Skipped when the provider resolves to
1857+ // the already-active theme, which avoids a full markdown
1858+ // re-render of the transcript on every selection.
1859+ m.applyThemeForProvider(providerID)
18471860 }
18481861 if _, ok := cfg.Models[config.SelectedModelTypeSmall]; !ok {
18491862 // Ensure small model is set is unset.
@@ -3371,6 +3384,21 @@ func (m *UI) cacheSidebarLogo(width int) {
33713384 m.sidebarLogo = renderLogo(m.com.Styles, true, m.com.IsHyper(), width)
33723385}
33733386
3387+// applyThemeForProvider swaps the active theme to the one associated with
3388+// the given provider, but only when that theme differs from the one
3389+// already applied. Most providers share a single theme, so re-selecting a
3390+// model from the same theme family would otherwise pay the full cost of
3391+// invalidating the markdown renderer cache and re-rendering the entire
3392+// transcript for no visible change.
3393+func (m *UI) applyThemeForProvider(providerID string) {
3394+ key := styles.ThemeKeyForProvider(providerID)
3395+ if key == m.themeKey {
3396+ return
3397+ }
3398+ m.themeKey = key
3399+ m.applyTheme(styles.ThemeForProvider(providerID))
3400+}
3401+
33743402// applyTheme replaces the active styles with the given theme, drops the
33753403// shared markdown renderer cache, and refreshes every component that
33763404// caches style data.