fix(cli): show renamed/added/deprecated settings separately in config migrate
- commit
- e1942406339e65804237e96959bc1e846d345af5
- author
- brice · · 2025-12-28 07:46 UTC
- parent
- 5fe539f
- signature
- unsigned
2 files · +132 -23
M internal/cli/cmd/config.go +24 -23
| @@ -117,22 +117,23 @@ func runConfigMigrate(_ *cobra.Command, _ []string) error { | |||
| 117 | 117 | return nil | |
| 118 | 118 | } | |
| 119 | 119 | ||
| 120 | - | // Check for migration | |
| 120 | + | // Detect changes (includes renames, additions, and removals) | |
| 121 | 121 | ctx := context.Background() | |
| 122 | - | result, err := uc.Check(ctx, usecase.CheckConfigMigrationInput{}) | |
| 122 | + | detectResult, err := uc.DetectChanges(ctx, usecase.DetectChangesInput{}) | |
| 123 | 123 | if err != nil { | |
| 124 | 124 | fmt.Println(renderer.RenderError(err)) | |
| 125 | 125 | return nil | |
| 126 | 126 | } | |
| 127 | 127 | ||
| 128 | - | if !result.NeedsMigration { | |
| 128 | + | if !detectResult.HasChanges { | |
| 129 | 129 | fmt.Println(renderer.RenderUpToDate(configFile)) | |
| 130 | 130 | return nil | |
| 131 | 131 | } | |
| 132 | 132 | ||
| 133 | - | // Show config info and missing keys | |
| 134 | - | fmt.Println(renderer.RenderConfigInfo(configFile, len(result.MissingKeys))) | |
| 135 | - | fmt.Println(renderer.RenderMissingKeys(result.MissingKeys)) | |
| 133 | + | // Show config info with change summary | |
| 134 | + | fmt.Println(renderer.RenderConfigInfo(configFile, len(detectResult.Changes))) | |
| 135 | + | fmt.Println(renderer.RenderChangesSummary(detectResult.Changes)) | |
| 136 | + | fmt.Println(renderer.RenderChanges(detectResult.Changes)) | |
| 136 | 137 | ||
| 137 | 138 | // If --yes flag, proceed without confirmation | |
| 138 | 139 | if configYes { | |
| @@ -140,7 +141,7 @@ func runConfigMigrate(_ *cobra.Command, _ []string) error { | |||
| 140 | 141 | } | |
| 141 | 142 | ||
| 142 | 143 | // Show confirmation dialog | |
| 143 | - | return runMigrateWithConfirmation(ctx, uc, renderer, app.Theme, result.MissingKeys) | |
| 144 | + | return runMigrateWithConfirmation(ctx, uc, renderer, app.Theme, detectResult.Changes) | |
| 144 | 145 | } | |
| 145 | 146 | ||
| 146 | 147 | // executeMigration performs the actual migration. | |
| @@ -168,12 +169,12 @@ const ( | |||
| 168 | 169 | ||
| 169 | 170 | // migrateModel is the bubbletea model for the migrate confirmation. | |
| 170 | 171 | type migrateModel struct { | |
| 171 | - | spinner spinner.Model | |
| 172 | - | renderer *styles.ConfigRenderer | |
| 173 | - | confirm styles.ConfirmModel | |
| 174 | - | state migrateState | |
| 175 | - | uc *usecase.MigrateConfigUseCase | |
| 176 | - | missingKeys []port.KeyInfo | |
| 172 | + | spinner spinner.Model | |
| 173 | + | renderer *styles.ConfigRenderer | |
| 174 | + | confirm styles.ConfirmModel | |
| 175 | + | state migrateState | |
| 176 | + | uc *usecase.MigrateConfigUseCase | |
| 177 | + | changes []port.KeyChange | |
| 177 | 178 | ||
| 178 | 179 | result string | |
| 179 | 180 | err error | |
| @@ -190,21 +191,21 @@ func newMigrateModel( | |||
| 190 | 191 | renderer *styles.ConfigRenderer, | |
| 191 | 192 | theme *styles.Theme, | |
| 192 | 193 | uc *usecase.MigrateConfigUseCase, | |
| 193 | - | missingKeys []port.KeyInfo, | |
| 194 | + | changes []port.KeyChange, | |
| 194 | 195 | ) migrateModel { | |
| 195 | 196 | s := spinner.New() | |
| 196 | 197 | s.Spinner = spinner.Dot | |
| 197 | 198 | s.Style = lipgloss.NewStyle().Foreground(theme.Accent) | |
| 198 | 199 | ||
| 199 | - | confirm := styles.NewConfirm(theme, "Add these settings with default values?") | |
| 200 | + | confirm := styles.NewConfirm(theme, "Apply these changes?") | |
| 200 | 201 | ||
| 201 | 202 | return migrateModel{ | |
| 202 | - | spinner: s, | |
| 203 | - | renderer: renderer, | |
| 204 | - | confirm: confirm, | |
| 205 | - | state: migrateStateConfirm, | |
| 206 | - | uc: uc, | |
| 207 | - | missingKeys: missingKeys, | |
| 203 | + | spinner: s, | |
| 204 | + | renderer: renderer, | |
| 205 | + | confirm: confirm, | |
| 206 | + | state: migrateStateConfirm, | |
| 207 | + | uc: uc, | |
| 208 | + | changes: changes, | |
| 208 | 209 | } | |
| 209 | 210 | } | |
| 210 | 211 | ||
| @@ -291,12 +292,12 @@ func runMigrateWithConfirmation( | |||
| 291 | 292 | uc *usecase.MigrateConfigUseCase, | |
| 292 | 293 | renderer *styles.ConfigRenderer, | |
| 293 | 294 | theme *styles.Theme, | |
| 294 | - | missingKeys []port.KeyInfo, | |
| 295 | + | changes []port.KeyChange, | |
| 295 | 296 | ) error { | |
| 296 | 297 | // Suppress unused ctx warning - kept for consistency with other run functions | |
| 297 | 298 | _ = ctx | |
| 298 | 299 | ||
| 299 | - | m := newMigrateModel(renderer, theme, uc, missingKeys) | |
| 300 | + | m := newMigrateModel(renderer, theme, uc, changes) | |
| 300 | 301 | p := tea.NewProgram(m) | |
| 301 | 302 | ||
| 302 | 303 | _, err := p.Run() | |
| @@ -117,22 +117,23 @@ func runConfigMigrate(_ *cobra.Command, _ []string) error { | |||||
| 117 | return nil | 117 | return nil | ||
| 118 | } | 118 | } | ||
| 119 | 119 | ||||
| 120 | - | // Check for migration | 120 | + | // Detect changes (includes renames, additions, and removals) |
| 121 | ctx := context.Background() | 121 | ctx := context.Background() | ||
| 122 | - | result, err := uc.Check(ctx, usecase.CheckConfigMigrationInput{}) | 122 | + | detectResult, err := uc.DetectChanges(ctx, usecase.DetectChangesInput{}) |
| 123 | if err != nil { | 123 | if err != nil { | ||
| 124 | fmt.Println(renderer.RenderError(err)) | 124 | fmt.Println(renderer.RenderError(err)) | ||
| 125 | return nil | 125 | return nil | ||
| 126 | } | 126 | } | ||
| 127 | 127 | ||||
| 128 | - | if !result.NeedsMigration { | 128 | + | if !detectResult.HasChanges { |
| 129 | fmt.Println(renderer.RenderUpToDate(configFile)) | 129 | fmt.Println(renderer.RenderUpToDate(configFile)) | ||
| 130 | return nil | 130 | return nil | ||
| 131 | } | 131 | } | ||
| 132 | 132 | ||||
| 133 | - | // Show config info and missing keys | 133 | + | // Show config info with change summary |
| 134 | - | fmt.Println(renderer.RenderConfigInfo(configFile, len(result.MissingKeys))) | 134 | + | fmt.Println(renderer.RenderConfigInfo(configFile, len(detectResult.Changes))) |
| 135 | - | fmt.Println(renderer.RenderMissingKeys(result.MissingKeys)) | 135 | + | fmt.Println(renderer.RenderChangesSummary(detectResult.Changes)) |
| 136 | + | fmt.Println(renderer.RenderChanges(detectResult.Changes)) | |||
| 136 | 137 | ||||
| 137 | // If --yes flag, proceed without confirmation | 138 | // If --yes flag, proceed without confirmation | ||
| 138 | if configYes { | 139 | if configYes { | ||
| @@ -140,7 +141,7 @@ func runConfigMigrate(_ *cobra.Command, _ []string) error { | |||||
| 140 | } | 141 | } | ||
| 141 | 142 | ||||
| 142 | // Show confirmation dialog | 143 | // Show confirmation dialog | ||
| 143 | - | return runMigrateWithConfirmation(ctx, uc, renderer, app.Theme, result.MissingKeys) | 144 | + | return runMigrateWithConfirmation(ctx, uc, renderer, app.Theme, detectResult.Changes) |
| 144 | } | 145 | } | ||
| 145 | 146 | ||||
| 146 | // executeMigration performs the actual migration. | 147 | // executeMigration performs the actual migration. | ||
| @@ -168,12 +169,12 @@ const ( | |||||
| 168 | 169 | ||||
| 169 | // migrateModel is the bubbletea model for the migrate confirmation. | 170 | // migrateModel is the bubbletea model for the migrate confirmation. | ||
| 170 | type migrateModel struct { | 171 | type migrateModel struct { | ||
| 171 | - | spinner spinner.Model | 172 | + | spinner spinner.Model |
| 172 | - | renderer *styles.ConfigRenderer | 173 | + | renderer *styles.ConfigRenderer |
| 173 | - | confirm styles.ConfirmModel | 174 | + | confirm styles.ConfirmModel |
| 174 | - | state migrateState | 175 | + | state migrateState |
| 175 | - | uc *usecase.MigrateConfigUseCase | 176 | + | uc *usecase.MigrateConfigUseCase |
| 176 | - | missingKeys []port.KeyInfo | 177 | + | changes []port.KeyChange |
| 177 | 178 | ||||
| 178 | result string | 179 | result string | ||
| 179 | err error | 180 | err error | ||
| @@ -190,21 +191,21 @@ func newMigrateModel( | |||||
| 190 | renderer *styles.ConfigRenderer, | 191 | renderer *styles.ConfigRenderer, | ||
| 191 | theme *styles.Theme, | 192 | theme *styles.Theme, | ||
| 192 | uc *usecase.MigrateConfigUseCase, | 193 | uc *usecase.MigrateConfigUseCase, | ||
| 193 | - | missingKeys []port.KeyInfo, | 194 | + | changes []port.KeyChange, |
| 194 | ) migrateModel { | 195 | ) migrateModel { | ||
| 195 | s := spinner.New() | 196 | s := spinner.New() | ||
| 196 | s.Spinner = spinner.Dot | 197 | s.Spinner = spinner.Dot | ||
| 197 | s.Style = lipgloss.NewStyle().Foreground(theme.Accent) | 198 | s.Style = lipgloss.NewStyle().Foreground(theme.Accent) | ||
| 198 | 199 | ||||
| 199 | - | confirm := styles.NewConfirm(theme, "Add these settings with default values?") | 200 | + | confirm := styles.NewConfirm(theme, "Apply these changes?") |
| 200 | 201 | ||||
| 201 | return migrateModel{ | 202 | return migrateModel{ | ||
| 202 | - | spinner: s, | 203 | + | spinner: s, |
| 203 | - | renderer: renderer, | 204 | + | renderer: renderer, |
| 204 | - | confirm: confirm, | 205 | + | confirm: confirm, |
| 205 | - | state: migrateStateConfirm, | 206 | + | state: migrateStateConfirm, |
| 206 | - | uc: uc, | 207 | + | uc: uc, |
| 207 | - | missingKeys: missingKeys, | 208 | + | changes: changes, |
| 208 | } | 209 | } | ||
| 209 | } | 210 | } | ||
| 210 | 211 | ||||
| @@ -291,12 +292,12 @@ func runMigrateWithConfirmation( | |||||
| 291 | uc *usecase.MigrateConfigUseCase, | 292 | uc *usecase.MigrateConfigUseCase, | ||
| 292 | renderer *styles.ConfigRenderer, | 293 | renderer *styles.ConfigRenderer, | ||
| 293 | theme *styles.Theme, | 294 | theme *styles.Theme, | ||
| 294 | - | missingKeys []port.KeyInfo, | 295 | + | changes []port.KeyChange, |
| 295 | ) error { | 296 | ) error { | ||
| 296 | // Suppress unused ctx warning - kept for consistency with other run functions | 297 | // Suppress unused ctx warning - kept for consistency with other run functions | ||
| 297 | _ = ctx | 298 | _ = ctx | ||
| 298 | 299 | ||||
| 299 | - | m := newMigrateModel(renderer, theme, uc, missingKeys) | 300 | + | m := newMigrateModel(renderer, theme, uc, changes) |
| 300 | p := tea.NewProgram(m) | 301 | p := tea.NewProgram(m) | ||
| 301 | 302 | ||||
| 302 | _, err := p.Run() | 303 | _, err := p.Run() | ||
M internal/cli/styles/config.go +108 -0
| @@ -69,6 +69,114 @@ func (r *ConfigRenderer) RenderMissingKeys(keys []port.KeyInfo) string { | |||
| 69 | 69 | return sb.String() | |
| 70 | 70 | } | |
| 71 | 71 | ||
| 72 | + | // RenderChanges renders the list of detected changes (added, renamed, removed). | |
| 73 | + | func (r *ConfigRenderer) RenderChanges(changes []port.KeyChange) string { | |
| 74 | + | if len(changes) == 0 { | |
| 75 | + | return "" | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | addStyle := lipgloss.NewStyle().Foreground(r.theme.Success) | |
| 79 | + | renameStyle := lipgloss.NewStyle().Foreground(r.theme.Warning) | |
| 80 | + | removeStyle := lipgloss.NewStyle().Foreground(r.theme.Error) | |
| 81 | + | keyStyle := r.theme.Highlight | |
| 82 | + | valueStyle := r.theme.Subtle | |
| 83 | + | ||
| 84 | + | // Group changes by type | |
| 85 | + | var added, renamed, removed []port.KeyChange | |
| 86 | + | for _, c := range changes { | |
| 87 | + | switch c.Type { | |
| 88 | + | case port.KeyChangeAdded: | |
| 89 | + | added = append(added, c) | |
| 90 | + | case port.KeyChangeRenamed: | |
| 91 | + | renamed = append(renamed, c) | |
| 92 | + | case port.KeyChangeRemoved: | |
| 93 | + | removed = append(removed, c) | |
| 94 | + | } | |
| 95 | + | } | |
| 96 | + | ||
| 97 | + | var sb strings.Builder | |
| 98 | + | ||
| 99 | + | // Show renamed keys first (most important for user to understand) | |
| 100 | + | if len(renamed) > 0 { | |
| 101 | + | sb.WriteString(fmt.Sprintf("\n Renamed settings (%d):\n", len(renamed))) | |
| 102 | + | for _, c := range renamed { | |
| 103 | + | sb.WriteString(fmt.Sprintf( | |
| 104 | + | " %s %s %s %s\n Value: %s\n", | |
| 105 | + | renameStyle.Render("~"), | |
| 106 | + | valueStyle.Render(c.OldKey), | |
| 107 | + | renameStyle.Render("→"), | |
| 108 | + | keyStyle.Render(c.NewKey), | |
| 109 | + | valueStyle.Render(c.OldValue), | |
| 110 | + | )) | |
| 111 | + | } | |
| 112 | + | } | |
| 113 | + | ||
| 114 | + | // Show new settings | |
| 115 | + | if len(added) > 0 { | |
| 116 | + | sb.WriteString(fmt.Sprintf("\n New settings (%d):\n", len(added))) | |
| 117 | + | for _, c := range added { | |
| 118 | + | sb.WriteString(fmt.Sprintf( | |
| 119 | + | " %s %s\n Default: %s\n", | |
| 120 | + | addStyle.Render("+"), | |
| 121 | + | keyStyle.Render(c.NewKey), | |
| 122 | + | valueStyle.Render(c.NewValue), | |
| 123 | + | )) | |
| 124 | + | } | |
| 125 | + | } | |
| 126 | + | ||
| 127 | + | // Show deprecated settings (will be removed) | |
| 128 | + | if len(removed) > 0 { | |
| 129 | + | sb.WriteString(fmt.Sprintf("\n Deprecated settings (%d):\n", len(removed))) | |
| 130 | + | for _, c := range removed { | |
| 131 | + | sb.WriteString(fmt.Sprintf( | |
| 132 | + | " %s %s %s\n", | |
| 133 | + | removeStyle.Render("-"), | |
| 134 | + | valueStyle.Render(c.OldKey), | |
| 135 | + | removeStyle.Render("(will be ignored)"), | |
| 136 | + | )) | |
| 137 | + | } | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | return sb.String() | |
| 141 | + | } | |
| 142 | + | ||
| 143 | + | // RenderChangesSummary renders a summary of changes for the config info header. | |
| 144 | + | func (r *ConfigRenderer) RenderChangesSummary(changes []port.KeyChange) string { | |
| 145 | + | var added, renamed, removed int | |
| 146 | + | for _, c := range changes { | |
| 147 | + | switch c.Type { | |
| 148 | + | case port.KeyChangeAdded: | |
| 149 | + | added++ | |
| 150 | + | case port.KeyChangeRenamed: | |
| 151 | + | renamed++ | |
| 152 | + | case port.KeyChangeRemoved: | |
| 153 | + | removed++ | |
| 154 | + | } | |
| 155 | + | } | |
| 156 | + | ||
| 157 | + | iconStyle := lipgloss.NewStyle().Foreground(r.theme.Accent) | |
| 158 | + | addStyle := lipgloss.NewStyle().Foreground(r.theme.Success) | |
| 159 | + | renameStyle := lipgloss.NewStyle().Foreground(r.theme.Warning) | |
| 160 | + | removeStyle := lipgloss.NewStyle().Foreground(r.theme.Error) | |
| 161 | + | ||
| 162 | + | var parts []string | |
| 163 | + | if renamed > 0 { | |
| 164 | + | parts = append(parts, renameStyle.Render(fmt.Sprintf("%d renamed", renamed))) | |
| 165 | + | } | |
| 166 | + | if added > 0 { | |
| 167 | + | parts = append(parts, addStyle.Render(fmt.Sprintf("%d new", added))) | |
| 168 | + | } | |
| 169 | + | if removed > 0 { | |
| 170 | + | parts = append(parts, removeStyle.Render(fmt.Sprintf("%d deprecated", removed))) | |
| 171 | + | } | |
| 172 | + | ||
| 173 | + | if len(parts) == 0 { | |
| 174 | + | return "" | |
| 175 | + | } | |
| 176 | + | ||
| 177 | + | return fmt.Sprintf("\n %s %s", iconStyle.Render(IconInfo), strings.Join(parts, ", ")) | |
| 178 | + | } | |
| 179 | + | ||
| 72 | 180 | // RenderMigrationSuccess renders the success message after migration. | |
| 73 | 181 | func (r *ConfigRenderer) RenderMigrationSuccess(count int, path string) string { | |
| 74 | 182 | iconStyle := lipgloss.NewStyle().Foreground(r.theme.Success) | |
| @@ -69,6 +69,114 @@ func (r *ConfigRenderer) RenderMissingKeys(keys []port.KeyInfo) string { | |||||
| 69 | return sb.String() | 69 | return sb.String() | ||
| 70 | } | 70 | } | ||
| 71 | 71 | ||||
| 72 | + | // RenderChanges renders the list of detected changes (added, renamed, removed). | |||
| 73 | + | func (r *ConfigRenderer) RenderChanges(changes []port.KeyChange) string { | |||
| 74 | + | if len(changes) == 0 { | |||
| 75 | + | return "" | |||
| 76 | + | } | |||
| 77 | + | ||||
| 78 | + | addStyle := lipgloss.NewStyle().Foreground(r.theme.Success) | |||
| 79 | + | renameStyle := lipgloss.NewStyle().Foreground(r.theme.Warning) | |||
| 80 | + | removeStyle := lipgloss.NewStyle().Foreground(r.theme.Error) | |||
| 81 | + | keyStyle := r.theme.Highlight | |||
| 82 | + | valueStyle := r.theme.Subtle | |||
| 83 | + | ||||
| 84 | + | // Group changes by type | |||
| 85 | + | var added, renamed, removed []port.KeyChange | |||
| 86 | + | for _, c := range changes { | |||
| 87 | + | switch c.Type { | |||
| 88 | + | case port.KeyChangeAdded: | |||
| 89 | + | added = append(added, c) | |||
| 90 | + | case port.KeyChangeRenamed: | |||
| 91 | + | renamed = append(renamed, c) | |||
| 92 | + | case port.KeyChangeRemoved: | |||
| 93 | + | removed = append(removed, c) | |||
| 94 | + | } | |||
| 95 | + | } | |||
| 96 | + | ||||
| 97 | + | var sb strings.Builder | |||
| 98 | + | ||||
| 99 | + | // Show renamed keys first (most important for user to understand) | |||
| 100 | + | if len(renamed) > 0 { | |||
| 101 | + | sb.WriteString(fmt.Sprintf("\n Renamed settings (%d):\n", len(renamed))) | |||
| 102 | + | for _, c := range renamed { | |||
| 103 | + | sb.WriteString(fmt.Sprintf( | |||
| 104 | + | " %s %s %s %s\n Value: %s\n", | |||
| 105 | + | renameStyle.Render("~"), | |||
| 106 | + | valueStyle.Render(c.OldKey), | |||
| 107 | + | renameStyle.Render("→"), | |||
| 108 | + | keyStyle.Render(c.NewKey), | |||
| 109 | + | valueStyle.Render(c.OldValue), | |||
| 110 | + | )) | |||
| 111 | + | } | |||
| 112 | + | } | |||
| 113 | + | ||||
| 114 | + | // Show new settings | |||
| 115 | + | if len(added) > 0 { | |||
| 116 | + | sb.WriteString(fmt.Sprintf("\n New settings (%d):\n", len(added))) | |||
| 117 | + | for _, c := range added { | |||
| 118 | + | sb.WriteString(fmt.Sprintf( | |||
| 119 | + | " %s %s\n Default: %s\n", | |||
| 120 | + | addStyle.Render("+"), | |||
| 121 | + | keyStyle.Render(c.NewKey), | |||
| 122 | + | valueStyle.Render(c.NewValue), | |||
| 123 | + | )) | |||
| 124 | + | } | |||
| 125 | + | } | |||
| 126 | + | ||||
| 127 | + | // Show deprecated settings (will be removed) | |||
| 128 | + | if len(removed) > 0 { | |||
| 129 | + | sb.WriteString(fmt.Sprintf("\n Deprecated settings (%d):\n", len(removed))) | |||
| 130 | + | for _, c := range removed { | |||
| 131 | + | sb.WriteString(fmt.Sprintf( | |||
| 132 | + | " %s %s %s\n", | |||
| 133 | + | removeStyle.Render("-"), | |||
| 134 | + | valueStyle.Render(c.OldKey), | |||
| 135 | + | removeStyle.Render("(will be ignored)"), | |||
| 136 | + | )) | |||
| 137 | + | } | |||
| 138 | + | } | |||
| 139 | + | ||||
| 140 | + | return sb.String() | |||
| 141 | + | } | |||
| 142 | + | ||||
| 143 | + | // RenderChangesSummary renders a summary of changes for the config info header. | |||
| 144 | + | func (r *ConfigRenderer) RenderChangesSummary(changes []port.KeyChange) string { | |||
| 145 | + | var added, renamed, removed int | |||
| 146 | + | for _, c := range changes { | |||
| 147 | + | switch c.Type { | |||
| 148 | + | case port.KeyChangeAdded: | |||
| 149 | + | added++ | |||
| 150 | + | case port.KeyChangeRenamed: | |||
| 151 | + | renamed++ | |||
| 152 | + | case port.KeyChangeRemoved: | |||
| 153 | + | removed++ | |||
| 154 | + | } | |||
| 155 | + | } | |||
| 156 | + | ||||
| 157 | + | iconStyle := lipgloss.NewStyle().Foreground(r.theme.Accent) | |||
| 158 | + | addStyle := lipgloss.NewStyle().Foreground(r.theme.Success) | |||
| 159 | + | renameStyle := lipgloss.NewStyle().Foreground(r.theme.Warning) | |||
| 160 | + | removeStyle := lipgloss.NewStyle().Foreground(r.theme.Error) | |||
| 161 | + | ||||
| 162 | + | var parts []string | |||
| 163 | + | if renamed > 0 { | |||
| 164 | + | parts = append(parts, renameStyle.Render(fmt.Sprintf("%d renamed", renamed))) | |||
| 165 | + | } | |||
| 166 | + | if added > 0 { | |||
| 167 | + | parts = append(parts, addStyle.Render(fmt.Sprintf("%d new", added))) | |||
| 168 | + | } | |||
| 169 | + | if removed > 0 { | |||
| 170 | + | parts = append(parts, removeStyle.Render(fmt.Sprintf("%d deprecated", removed))) | |||
| 171 | + | } | |||
| 172 | + | ||||
| 173 | + | if len(parts) == 0 { | |||
| 174 | + | return "" | |||
| 175 | + | } | |||
| 176 | + | ||||
| 177 | + | return fmt.Sprintf("\n %s %s", iconStyle.Render(IconInfo), strings.Join(parts, ", ")) | |||
| 178 | + | } | |||
| 179 | + | ||||
| 72 | // RenderMigrationSuccess renders the success message after migration. | 180 | // RenderMigrationSuccess renders the success message after migration. | ||
| 73 | func (r *ConfigRenderer) RenderMigrationSuccess(count int, path string) string { | 181 | func (r *ConfigRenderer) RenderMigrationSuccess(count int, path string) string { | ||
| 74 | iconStyle := lipgloss.NewStyle().Foreground(r.theme.Success) | 182 | iconStyle := lipgloss.NewStyle().Foreground(r.theme.Success) | ||