Skip to content

Commit d157793

Browse files
committed
chore(release): 发布 v1.14.1
1 parent 188cca2 commit d157793

11 files changed

Lines changed: 157 additions & 36 deletions

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.14.1] - 2026-07-11
11+
### Fixed
12+
- **Changelog popup**: Toggling the setting applies without reload; when the popup is off, the last-seen version is still tracked so enabling later does not replay stale update notices.
13+
- **Custom provider errors**: Missing Base URL / Model shows a localized message with an **Open Settings** action instead of a generic failure.
14+
- **Legacy override migration**: Partial `providerOverrides` entries now merge missing fields from legacy per-provider keys instead of skipping the whole provider.
15+
- **Auto Push misconfig**: Warn on activate (not only when the setting changes) if Auto Push is on while Auto Commit is off.
16+
17+
### Changed
18+
- Generate / Set API Key wait for settings migration to finish so upgrades do not briefly use pre-migration defaults.
19+
1020
## [1.14.0] - 2026-07-10
1121
### Changed
1222
- **Settings reorganization**: Split into **WTF Commit** (basics) and **WTF Commit › Advanced** so everyday options (provider, languages, auto commit/push, confirms) are not buried under prompt/diff/endpoint knobs.

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,11 @@ Other triggers: ✨ on the Source Control title bar, or Command Palette → **`W
6464

6565
> Prefer review-only? Turn **Auto Commit** off — the message stays in Source Control for you to edit and commit manually.
6666
67-
## 🆕 Latest (v1.14.0)
67+
## 🆕 Latest (v1.14.1)
6868

69-
- **Simpler settings**: Everyday options live under **WTF Commit**; prompt, endpoints, and diff limits moved to **WTF Commit › Advanced**.
70-
- **Clearer language names**: **Commit Message Language** (vs UI Language); old `language` / `customLanguage` keys migrate automatically.
71-
- **One override path**: Built-in provider endpoints use **Provider Overrides** only (legacy per-provider keys migrate on activate).
69+
- **Smoother settings UX**: Changelog popup reacts without reload; Auto Push misconfig warns on startup.
70+
- **Clearer Custom setup errors**: Missing Base URL / Model opens Settings instead of a generic failure.
71+
- **Safer upgrades**: Partial provider overrides merge legacy endpoint fields; generate waits for settings migration.
7272

7373
> See [CHANGELOG](CHANGELOG.md) for earlier releases.
7474

README_zh.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,11 @@
6464

6565
> 只想生成信息、自己点提交?把 **Auto Commit** 关掉即可。
6666
67-
## 🆕 最新更新(v1.14.0
67+
## 🆕 最新更新(v1.14.1
6868

69-
- **设置更清爽**日常选项在 **WTF Commit**;Prompt、端点、diff 限制移到 **WTF Commit › Advanced**
70-
- **语言命名更清晰****Commit Message Language**(与 UI Language 区分);旧的 `language` / `customLanguage` 会自动迁移
71-
- **端点覆盖统一**内置服务商只走 **Provider Overrides**(遗留的 per-provider key 启动时迁移)
69+
- **设置体验更顺**更新日志弹窗无需重载即可生效;Auto Push 配置不当会在启动时提醒
70+
- **Custom 报错更清楚**缺少 Base URL / Model 时直接引导打开设置,而不是笼统失败
71+
- **升级更稳**部分 Provider Overrides 会补齐遗留端点字段;生成前等待设置迁移完成
7272

7373
> 更早版本的更新说明请查看 [CHANGELOG](CHANGELOG.md)
7474

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "wtf-commit",
33
"displayName": "WTF Commit",
44
"description": "AI commit messages from your git diff. Conventional Commits, your own API key — DeepSeek, Gemini, OpenAI, and more.",
5-
"version": "1.14.0",
5+
"version": "1.14.1",
66
"license": "MIT",
77
"publisher": "codertesla",
88
"icon": "icon.png",
@@ -168,7 +168,7 @@
168168
"wtfCommit.changelogPopup": {
169169
"type": "boolean",
170170
"default": false,
171-
"description": "Show a notification with a View Changelog action after the extension is updated.",
171+
"description": "Show a notification with a View Changelog action after the extension is updated. Takes effect immediately when toggled.",
172172
"order": 1
173173
},
174174
"wtfCommit.warnOnTruncatedDiff": {

src/commands/generate.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import { readExtensionConfig, getSecretKeyName } from '../config';
1111
import { resolveRepository, collectStageablePaths } from '../git';
1212
import { DiffPreparationError, getOptimizedDiff } from '../diff';
13+
import { IncompleteProviderConfigError } from '../provider-config';
1314
import { buildProviderEndpoint, callLLM } from '../llm/provider';
1415
import { classifyPushFailure, formatPushFailureMessage } from '../push-failure';
1516
import { createStreamingSink, restoreIntent, restoreIntentOnAbort } from '../ui';
@@ -406,6 +407,19 @@ export async function runGenerate(context: vscode.ExtensionContext): Promise<voi
406407
return;
407408
}
408409

410+
if (error instanceof IncompleteProviderConfigError) {
411+
const messageKey =
412+
error.field === 'baseUrl' ? 'providerBaseUrlMissing' : 'providerModelMissing';
413+
const action = await vscode.window.showErrorMessage(
414+
t(messageKey, { provider: error.provider }),
415+
t('openSettings')
416+
);
417+
if (action === t('openSettings')) {
418+
void vscode.commands.executeCommand('workbench.action.openSettings', 'wtfCommit');
419+
}
420+
return;
421+
}
422+
409423
if (error instanceof RequestFailure) {
410424
if (error.code === 'cancelled') {
411425
restoreIntentOnAbort(repositoryForRestore?.inputBox, intentForRestore);

src/extension.ts

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export function activate(context: vscode.ExtensionContext) {
1414

1515
logInfo('Extension activated');
1616

17-
migrateLegacySettings().catch((error) => {
17+
const settingsReady = migrateLegacySettings().catch((error) => {
1818
logError('Failed to migrate legacy settings', error);
1919
});
2020

@@ -52,23 +52,29 @@ export function activate(context: vscode.ExtensionContext) {
5252
if (event.affectsConfiguration('wtfCommit.uiLanguage')) {
5353
syncUiLanguage();
5454
}
55-
if (event.affectsConfiguration('wtfCommit.autoPush')) {
55+
if (event.affectsConfiguration('wtfCommit.autoPush') || event.affectsConfiguration('wtfCommit.autoCommit')) {
5656
void warnIfAutoPushWithoutAutoCommit();
5757
}
58+
if (event.affectsConfiguration('wtfCommit.changelogPopup')) {
59+
void syncChangelogTracking(context);
60+
}
5861
})
5962
);
6063

61-
maybeShowChangelog(context);
64+
void syncChangelogTracking(context);
65+
void warnIfAutoPushWithoutAutoCommit();
6266

6367
checkFirstUseGuidance(context).catch((error) => {
6468
logError('Failed to check first-use guidance', error);
6569
});
6670

6771
context.subscriptions.push(
6872
vscode.commands.registerCommand('wtf-commit.setApiKey', async () => {
73+
await settingsReady;
6974
await runSetApiKey(context);
7075
}),
7176
vscode.commands.registerCommand('wtf-commit.generate', async () => {
77+
await settingsReady;
7278
await runGenerate(context);
7379
}),
7480
vscode.commands.registerCommand('wtf-commit.showOutput', () => {
@@ -107,13 +113,29 @@ async function warnIfAutoPushWithoutAutoCommit(): Promise<void> {
107113

108114
export function deactivate() {}
109115

110-
function maybeShowChangelog(context: vscode.ExtensionContext): void {
111-
if (!vscode.workspace.getConfiguration('wtfCommit').get<boolean>('changelogPopup', false)) {
116+
/**
117+
* Track extension version for changelog notices.
118+
* When the popup is off, still advance lastVersion so enabling later does not
119+
* replay stale update toasts for versions the user already ran.
120+
*/
121+
async function syncChangelogTracking(context: vscode.ExtensionContext): Promise<void> {
122+
const popupEnabled = vscode.workspace
123+
.getConfiguration('wtfCommit')
124+
.get<boolean>('changelogPopup', false);
125+
if (popupEnabled) {
126+
await checkChangelog(context);
112127
return;
113128
}
114-
checkChangelog(context).catch((error) => {
115-
logError('Failed to check changelog', error);
116-
});
129+
await advanceLastVersionQuietly(context);
130+
}
131+
132+
async function advanceLastVersionQuietly(context: vscode.ExtensionContext): Promise<void> {
133+
const currentVersion = String(context.extension.packageJSON.version);
134+
const lastVersion = context.globalState.get<string>('wtfCommit.lastVersion');
135+
if (currentVersion === lastVersion) {
136+
return;
137+
}
138+
await context.globalState.update('wtfCommit.lastVersion', currentVersion);
117139
}
118140

119141
async function checkChangelog(context: vscode.ExtensionContext): Promise<void> {

src/i18n.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ export type MessageKey =
5858
| 'dontRemindMe'
5959
| 'openSourceControl'
6060
| 'openSettings'
61+
| 'providerBaseUrlMissing'
62+
| 'providerModelMissing'
6163
| 'cancel'
6264
| 'readyToCommit'
6365
| 'commit'
@@ -145,6 +147,10 @@ const en: Dictionary = {
145147
dontRemindMe: "Don't Remind Me",
146148
openSourceControl: 'Open Source Control',
147149
openSettings: 'Open Settings',
150+
providerBaseUrlMissing:
151+
'Base URL is missing for {provider}. Set it under Settings → WTF Commit › Advanced (Custom Base URL, or Provider Overrides for built-ins).',
152+
providerModelMissing:
153+
'Model is missing for {provider}. Set it under Settings → WTF Commit › Advanced (Custom Model, or Provider Overrides for built-ins).',
148154
cancel: 'Cancel',
149155
readyToCommit: 'Ready to commit',
150156
commit: 'Commit',
@@ -225,6 +231,10 @@ const zh: Dictionary = {
225231
dontRemindMe: '不再提醒',
226232
openSourceControl: '打开源代码管理',
227233
openSettings: '打开设置',
234+
providerBaseUrlMissing:
235+
'{provider} 缺少 Base URL。请在设置 → WTF Commit › Advanced 中配置(Custom 用 Base URL;内置服务商用 Provider Overrides)。',
236+
providerModelMissing:
237+
'{provider} 缺少 Model。请在设置 → WTF Commit › Advanced 中配置(Custom 用 Model;内置服务商用 Provider Overrides)。',
228238
cancel: '取消',
229239
readyToCommit: '准备提交',
230240
commit: '提交',

src/provider-config.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,22 @@ export interface ProviderConfigInput {
88
customModel?: string;
99
}
1010

11+
export type IncompleteProviderField = 'baseUrl' | 'model';
12+
13+
export class IncompleteProviderConfigError extends Error {
14+
constructor(
15+
public readonly provider: ProviderName,
16+
public readonly field: IncompleteProviderField
17+
) {
18+
super(
19+
field === 'baseUrl'
20+
? `Base URL is missing for ${provider}.`
21+
: `Model is missing for ${provider}.`
22+
);
23+
this.name = 'IncompleteProviderConfigError';
24+
}
25+
}
26+
1127
export function resolveProviderConfig(input: ProviderConfigInput): { baseUrl: string; model: string } {
1228
const baseUrl = input.providerBaseUrl?.trim()
1329
|| (input.provider === 'Custom' ? input.customBaseUrl?.trim() : undefined)
@@ -17,14 +33,10 @@ export function resolveProviderConfig(input: ProviderConfigInput): { baseUrl: st
1733
|| (input.provider !== 'Custom' ? PROVIDERS[input.provider].model : '');
1834

1935
if (!baseUrl) {
20-
throw new Error(
21-
`Base URL is missing for ${input.provider}. For Custom, set Base URL under Settings → WTF Commit › Advanced; for built-ins, use Provider Overrides.`
22-
);
36+
throw new IncompleteProviderConfigError(input.provider, 'baseUrl');
2337
}
2438
if (!model) {
25-
throw new Error(
26-
`Model is missing for ${input.provider}. For Custom, set Model under Settings → WTF Commit › Advanced; for built-ins, use Provider Overrides.`
27-
);
39+
throw new IncompleteProviderConfigError(input.provider, 'model');
2840
}
2941

3042
return { baseUrl, model };

src/settings-migrate.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as vscode from 'vscode';
22
import { PROVIDER_NAMES } from './types';
33
import { logInfo } from './log';
4+
import { mergeLegacyProviderOverride } from './settings-resolve';
45

56
type ProviderOverride = { baseUrl?: string; model?: string };
67
type ConfigTarget = vscode.ConfigurationTarget.Global | vscode.ConfigurationTarget.Workspace | vscode.ConfigurationTarget.WorkspaceFolder;
@@ -62,7 +63,10 @@ async function migrateLegacyProviderKeys(config: vscode.WorkspaceConfiguration):
6263
if (Object.keys(merged.additions).length === 0) {
6364
continue;
6465
}
65-
const next = { ...merged.existing, ...merged.additions };
66+
const next: Record<string, ProviderOverride> = { ...merged.existing };
67+
for (const [provider, entry] of Object.entries(merged.additions)) {
68+
next[provider] = entry;
69+
}
6670
await config.update('providerOverrides', next, target);
6771
logInfo(`Migrated legacy per-provider endpoint settings into providerOverrides (${targetLabel(target)})`);
6872
}
@@ -77,18 +81,12 @@ function collectLegacyOverridesForTarget(
7781
const additions: Record<string, ProviderOverride> = {};
7882

7983
for (const provider of PROVIDER_NAMES) {
80-
if (existing[provider]?.baseUrl || existing[provider]?.model) {
81-
continue;
82-
}
8384
const baseUrl = readScopedString(config, `${provider}.baseUrl`, target);
8485
const model = readScopedString(config, `${provider}.model`, target);
85-
if (!baseUrl && !model) {
86-
continue;
86+
const merged = mergeLegacyProviderOverride(existing[provider], { baseUrl, model });
87+
if (merged) {
88+
additions[provider] = merged;
8789
}
88-
additions[provider] = {
89-
...(baseUrl ? { baseUrl } : {}),
90-
...(model ? { model } : {}),
91-
};
9290
}
9391

9492
return { existing, additions };

src/settings-resolve.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,31 @@ export function readProviderOverride(
3333
model: entry?.model?.trim() || undefined,
3434
};
3535
}
36+
37+
/**
38+
* Merge legacy per-provider fields into an existing override entry.
39+
* Only fills missing fields — never overwrites an explicit override value.
40+
* Returns undefined when nothing new would be added.
41+
*/
42+
export function mergeLegacyProviderOverride(
43+
existing: ProviderOverride | undefined,
44+
legacy: { baseUrl?: string; model?: string }
45+
): ProviderOverride | undefined {
46+
const baseUrl = legacy.baseUrl?.trim() || undefined;
47+
const model = legacy.model?.trim() || undefined;
48+
if (!baseUrl && !model) {
49+
return undefined;
50+
}
51+
52+
const next: ProviderOverride = { ...(existing || {}) };
53+
let changed = false;
54+
if (!next.baseUrl && baseUrl) {
55+
next.baseUrl = baseUrl;
56+
changed = true;
57+
}
58+
if (!next.model && model) {
59+
next.model = model;
60+
changed = true;
61+
}
62+
return changed ? next : undefined;
63+
}

0 commit comments

Comments
 (0)