mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 02:29:33 +01:00
fix(settings): Apply cannot keep a later Save open, refreshes only after a saved PUT (review)
- Split the one-shot keep-open intent from the in-flight guard and consume it before the first await, so a Save clicked while an Apply is in flight closes the modal. - Refresh the dependent groups only when the settings PUT returned ok (_apiPut answers null or a non-ok response instead of throwing), and also when only the webhook save failed. - Find the 'Apply or Save' hint by a data marker, not its English text; add zh-CN strings for the new toast and title. - Tests run the real saveAppSettings() (Apply then Save mid-flight, a failed PUT, a webhook-only failure, a plain Save). - Narrow the changeset and JSDoc to MCP sync and CLI management; touch the tray comment, the architecture note and the wiki.
This commit is contained in:
1 parent
9c63c41283
commit
c75d62ce04
7 files changed
+157
-31
No files matched your search
@@ -718,6 +718,8 @@
|
||||
|
||||
// Dynamic common status / toasts
|
||||
'Settings saved': '设置已保存',
|
||||
'Settings applied': '设置已应用',
|
||||
'Save and keep Settings open': '保存并保持设置打开',
|
||||
'Settings saved locally': '设置已保存到本机',
|
||||
'Tunnel active': '隧道已启用',
|
||||
'Tunnel starting — QR code will appear when ready...': '隧道正在启动,准备好后将显示二维码…',
|
||||
|
||||
@@ -3552,7 +3552,7 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
}
|
||||
|
||||
/* Save + Close are the two ways out of the sheet (save-and-close vs
|
||||
discard-and-close), hit in the same corner with the same thumb, so here —
|
||||
discard-and-close; Apply saves but keeps the sheet open), hit in the same corner with the same thumb, so here —
|
||||
and only here, since Save is header-only below 860px — they share a
|
||||
recessed tray and matching pill geometry instead of reading as a fat
|
||||
accent pill parked beside a stray × glyph. Tray colors come from skin
|
||||
|
||||
@@ -1220,12 +1220,12 @@ Object.assign(CodemanApp.prototype, {
|
||||
/** Preview (apply=false) or run (apply=true) the MCP server sync across enabled CLIs. */
|
||||
async mcpSync(apply) {
|
||||
const out = this.$('mcpSyncResult');
|
||||
const show = (html) => {
|
||||
if (out) { out.style.display = 'block'; out.innerHTML = html; }
|
||||
const show = (html, hint = '') => {
|
||||
if (out) { out.style.display = 'block'; out.innerHTML = html; out.dataset.hint = hint; }
|
||||
};
|
||||
// Switched on in this modal but not saved yet: the routes would only answer "disabled".
|
||||
if (!this._mcpSyncSavedOn) {
|
||||
show('Apply or Save settings to turn MCP sync on first, then preview or sync.');
|
||||
show('Apply or Save settings to turn MCP sync on first, then preview or sync.', 'save-first');
|
||||
return;
|
||||
}
|
||||
if (apply && !confirm('Add missing MCP servers to every installed, enabled CLI\'s config file? Env values and headers on those servers are copied too.')) return;
|
||||
@@ -2507,24 +2507,30 @@ Object.assign(CodemanApp.prototype, {
|
||||
},
|
||||
|
||||
/**
|
||||
* Apply button: the same save as Save, but the modal stays open and the groups that depend on
|
||||
* a saved value (MCP sync, custom model endpoints, CLI management) are refreshed in place, so
|
||||
* a switch that unlocks more settings needs no close-and-reopen. It is a wrapper rather than
|
||||
* an option on saveAppSettings() so that function's signature (which tests locate by text)
|
||||
* stays as it was.
|
||||
* Apply button: the same save as Save, but the modal stays open and the MCP sync group (its
|
||||
* Preview and Sync need the saved flag) and the CLI management writes are refreshed in place, so
|
||||
* turning either on needs no close-and-reopen. It is a wrapper rather than an option on
|
||||
* saveAppSettings() so that function's signature (which tests locate by text) stays as it was.
|
||||
*
|
||||
* `_keepSettingsOpenOnce` is the one-shot intent and `_applyInFlight` the double-click guard:
|
||||
* saveAppSettings() consumes the intent before its first await, so a Save clicked while an Apply
|
||||
* is still in flight is an ordinary Save and closes the modal.
|
||||
*/
|
||||
async applyAppSettings() {
|
||||
if (this._applyingSettings) return;
|
||||
this._applyingSettings = true;
|
||||
if (this._applyInFlight) return;
|
||||
this._applyInFlight = true;
|
||||
this._keepSettingsOpenOnce = true;
|
||||
try {
|
||||
await this.saveAppSettings();
|
||||
} finally {
|
||||
this._applyingSettings = false;
|
||||
this._applyInFlight = false;
|
||||
this._keepSettingsOpenOnce = false;
|
||||
}
|
||||
},
|
||||
|
||||
async saveAppSettings() {
|
||||
const keepOpen = this._applyingSettings === true;
|
||||
const keepOpen = this._keepSettingsOpenOnce === true;
|
||||
this._keepSettingsOpenOnce = false;
|
||||
// Gesture overlay is injected at page render (server-side), so a change to it
|
||||
// only takes effect on reload — remember the prior value to decide below.
|
||||
const _prev = this.loadAppSettingsFromStorage();
|
||||
@@ -2867,6 +2873,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
...serverSettings
|
||||
} = settings;
|
||||
let webhookError = '';
|
||||
let serverSaved = false;
|
||||
try {
|
||||
const res = await this._apiPut('/api/settings', {
|
||||
...serverSettings,
|
||||
@@ -2888,6 +2895,10 @@ Object.assign(CodemanApp.prototype, {
|
||||
return;
|
||||
}
|
||||
|
||||
// `_apiPut` answers null or a non-ok response instead of throwing, so this is the only
|
||||
// evidence the server kept the flags the Apply refresh below reads.
|
||||
serverSaved = !!res?.ok;
|
||||
|
||||
// Save model configuration separately
|
||||
await this.saveModelConfigFromSettings();
|
||||
|
||||
@@ -2911,11 +2922,13 @@ Object.assign(CodemanApp.prototype, {
|
||||
this.showToast('Settings saved locally', 'warning');
|
||||
}
|
||||
|
||||
// Only when the settings PUT landed: after a 400 or a dropped connection the server still has the
|
||||
// old flags, and a webhook-only failure still saved the rest, so this runs ahead of that branch.
|
||||
if (keepOpen && serverSaved) this._refreshSettingsAfterApply(settings);
|
||||
|
||||
if (webhookError) {
|
||||
document.getElementById('webhookGroup')?.scrollIntoView({ block: 'center' });
|
||||
} else if (keepOpen) {
|
||||
this._refreshSettingsAfterApply(settings);
|
||||
} else {
|
||||
} else if (!keepOpen) {
|
||||
this.closeAppSettings();
|
||||
}
|
||||
|
||||
@@ -2945,7 +2958,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
// The MCP routes read the saved flag, so switching it on is only usable from now.
|
||||
this._mcpSyncSavedOn = settings.mcpSyncEnabled === true;
|
||||
const out = this.$('mcpSyncResult');
|
||||
if (this._mcpSyncSavedOn && out && out.textContent.startsWith('Apply or Save settings')) {
|
||||
if (this._mcpSyncSavedOn && out && out.dataset?.hint === 'save-first') {
|
||||
out.style.display = 'none';
|
||||
out.innerHTML = '';
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user