fix(ai): la liste des fournisseurs de l'assistant suit la configuration (BUG-043)
Le picker lit /api/ai/status une seule fois, à sa construction, et le panneau de l'assistant est un singleton monté pour toute la session : ajouter ou supprimer une clé API dans la configuration du projet laissait la liste des fournisseurs figée jusqu'à un rechargement de page. - ai.js : nouveau refreshAIPickers() qui reconstruit chaque picker monté dans son emplacement .ai-picker-slot (constante PICKER_SLOT_CLASS) ; le slot est conservé même sans fournisseur configuré, donc le premier fournisseur ajouté s'y monte aussi ; une sélection persistée dont le fournisseur n'est plus configuré est purgée de obsigate_ai_picker (retour au défaut, plus de modèle fantôme dans le déclencheur). - bookslm.js : l'emplacement .bookslm-picker-host porte la classe ai-picker-slot et reste dans la barre (replaceChildren au lieu de replaceWith). - config.js : refreshAIPickers() après saveAIKeys() et deleteAIKey(). - tests : +4 tests JSDOM (ajout/retrait dans la barre, premier montage dans un slot vide, purge de la sélection orpheline, câblage save/delete). Vérifié : tests frontend 66/66 (IA) + 9 suites JSDOM, validate-imports 36 modules, pytest 963 passed / 6 skipped, ruff 0, et contrôle navigateur (Playwright) sur l'instance de test — ajout de nvidia visible sans rechargement, retrait effectif + sélection réinitialisée.
This commit is contained in:
+46
-2
@@ -434,8 +434,16 @@ async function _buildPickerUI() {
|
||||
_updateCaps();
|
||||
});
|
||||
|
||||
// Load models for the initial provider selection
|
||||
if (pickerState.provider && providerNames.includes(pickerState.provider)) {
|
||||
// Load models for the initial provider selection. A provider that is no
|
||||
// longer configured (its API key was removed) is dropped from the stored
|
||||
// selection so the toolbar never advertises a stale provider/model.
|
||||
if (pickerState.provider && !providerNames.includes(pickerState.provider)) {
|
||||
pickerState.provider = null;
|
||||
pickerState.model = null;
|
||||
delete pickerState.capabilities;
|
||||
_writePicker(pickerState);
|
||||
}
|
||||
if (pickerState.provider) {
|
||||
_loadModels(pickerState.provider);
|
||||
} else {
|
||||
_syncTrigger();
|
||||
@@ -450,6 +458,40 @@ async function _buildPickerUI() {
|
||||
return wrap;
|
||||
}
|
||||
|
||||
// Mount point that survives even when no picker can be built (no configured
|
||||
// provider). Keeping it lets a later refresh mount the picker at the same spot.
|
||||
const PICKER_SLOT_CLASS = 'ai-picker-slot';
|
||||
|
||||
/**
|
||||
* Rebuild every mounted provider/model picker so the provider list matches the
|
||||
* current AI configuration of the project.
|
||||
*
|
||||
* A picker reads `/api/ai/status` once, when it is built — and the assistant
|
||||
* panel is a singleton mounted for the whole session. Without this refresh,
|
||||
* adding or removing a provider key in the project config left a stale
|
||||
* provider list in the assistant sidebar until a full page reload.
|
||||
*/
|
||||
async function refreshAIPickers() {
|
||||
const mounts = Array.from(document.querySelectorAll(`.${PICKER_SLOT_CLASS}, .ai-picker`));
|
||||
let mounted = 0;
|
||||
for (const mount of mounts) {
|
||||
// A picker living inside a slot is detached once the slot is refilled.
|
||||
if (!mount.isConnected) continue;
|
||||
const fresh = await _buildPickerUI();
|
||||
if (fresh) {
|
||||
if (mount.classList.contains(PICKER_SLOT_CLASS)) mount.replaceChildren(fresh);
|
||||
else mount.replaceWith(fresh);
|
||||
mounted += 1;
|
||||
} else if (!mount.classList.contains(PICKER_SLOT_CLASS)) {
|
||||
// Every provider was removed: keep an inert mount point in place.
|
||||
const slot = document.createElement('span');
|
||||
slot.className = PICKER_SLOT_CLASS;
|
||||
mount.replaceWith(slot);
|
||||
}
|
||||
}
|
||||
return mounted;
|
||||
}
|
||||
|
||||
// ── Get selected text from CodeMirror ──
|
||||
function getSelection(editorView) {
|
||||
if (!editorView) return '';
|
||||
@@ -978,5 +1020,7 @@ export {
|
||||
getModelCapabilities,
|
||||
renderCapabilityList,
|
||||
_buildPickerUI as buildAIPickerUI,
|
||||
refreshAIPickers,
|
||||
PICKER_SLOT_CLASS,
|
||||
_promptRewriteInstruction,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user