fix(ai): lisibilite des modeles + contexte @ en mode General (BUG-010, BUG-011)
BUG-011: popover des modeles aligne a droite (right:0), largeur 340px bornee, noms sur plusieurs lignes + title. BUG-010: la selection @ capture le vault renvoye par tree-search et _contextVault() le propage aux requetes /context et /chat, corrigeant le contexte ad-hoc en mode General.
This commit is contained in:
+5
-4
@@ -187,9 +187,9 @@ async function _buildPickerUI() {
|
||||
modelPop.className = 'ai-picker-model-pop hidden';
|
||||
// Critical layout is applied inline too: if a stale stylesheet is served, the
|
||||
// popover must still stack vertically instead of flowing as a block of text.
|
||||
modelPop.style.cssText = 'position:absolute;top:calc(100% + 4px);left:0;z-index:60;'
|
||||
+ 'min-width:220px;max-width:320px;background:var(--bg-primary);border:1px solid var(--border);'
|
||||
+ 'border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,0.35);padding:6px;';
|
||||
modelPop.style.cssText = 'position:absolute;top:calc(100% + 4px);right:0;z-index:60;'
|
||||
+ 'min-width:220px;width:340px;max-width:calc(100vw - 24px);background:var(--bg-primary);'
|
||||
+ 'border:1px solid var(--border);border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,0.35);padding:6px;';
|
||||
|
||||
const modelSearch = document.createElement('input');
|
||||
modelSearch.type = 'text';
|
||||
@@ -296,7 +296,8 @@ async function _buildPickerUI() {
|
||||
option.className = 'ai-picker-model-option';
|
||||
option.dataset.value = model;
|
||||
option.textContent = model || t('ai.model_default');
|
||||
option.style.cssText = 'display:block;width:100%;text-align:left;';
|
||||
option.title = model;
|
||||
option.style.cssText = 'display:block;width:100%;text-align:left;white-space:normal;overflow-wrap:anywhere;';
|
||||
if (model === modelSelect.value) option.classList.add('active');
|
||||
option.addEventListener('mousedown', (e) => { e.preventDefault(); _pick(model); });
|
||||
modelList.appendChild(option);
|
||||
|
||||
+26
-13
@@ -321,6 +321,18 @@ class BooksLM {
|
||||
return state.currentVault || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vault used for context/chat requests. Falls back to the vault of the first
|
||||
* ad-hoc `@` entry: in General mode there is no base vault, but the selected
|
||||
* files/directories still belong to a vault that must be sent to the server.
|
||||
*/
|
||||
_contextVault() {
|
||||
if (this._vault) return this._vault;
|
||||
const entry = this._adhocFiles[0] || this._adhocDirs[0] || this._images[0];
|
||||
if (entry && entry.vault) return entry.vault;
|
||||
return this._resolveVault();
|
||||
}
|
||||
|
||||
/** Open a file mentioned in an assistant answer in the main viewer. */
|
||||
async _openFileLink(path) {
|
||||
const vault = this._resolveVault();
|
||||
@@ -378,7 +390,7 @@ class BooksLM {
|
||||
|
||||
const payload = {
|
||||
mode: this._mode,
|
||||
vault: this._vault || this._resolveVault(),
|
||||
vault: this._contextVault(),
|
||||
directory: this._directory,
|
||||
context_files: this._mode === MODE.DOCUMENTS ? this._documents.map((d) => d.path) : [],
|
||||
extra_files: this._adhocFiles.map((f) => f.path),
|
||||
@@ -667,17 +679,17 @@ class BooksLM {
|
||||
return chip;
|
||||
}
|
||||
|
||||
_addAdhocFile(path) {
|
||||
_addAdhocFile(path, vault) {
|
||||
if (!path || this._adhocFiles.some((f) => f.path === path)) return;
|
||||
this._adhocFiles.push({ path });
|
||||
this._adhocFiles.push({ path, vault: vault || null });
|
||||
this._renderAttachments();
|
||||
this._loadContext();
|
||||
}
|
||||
|
||||
_addAdhocDir(path) {
|
||||
_addAdhocDir(path, vault) {
|
||||
const clean = path.replace(/\/+$/, '');
|
||||
if (!clean || this._adhocDirs.some((d) => d.path === clean)) return;
|
||||
this._adhocDirs.push({ path: clean });
|
||||
this._adhocDirs.push({ path: clean, vault: vault || null });
|
||||
this._renderAttachments();
|
||||
this._loadContext();
|
||||
}
|
||||
@@ -808,7 +820,7 @@ class BooksLM {
|
||||
async _showMentionMenu(query) {
|
||||
if (!this._panel) return;
|
||||
const seq = ++this._menuSeq;
|
||||
const vault = this._resolveVault();
|
||||
const vault = this._contextVault();
|
||||
if (!vault && !query) {
|
||||
// Nothing to list without a vault; a typed query can still search all.
|
||||
this._hideMenus();
|
||||
@@ -833,7 +845,7 @@ class BooksLM {
|
||||
const path = r.path || r;
|
||||
const rawType = r.type || (this._classifyPath(path) === 'dir' ? 'dir' : 'file');
|
||||
const type = rawType === 'directory' ? 'dir' : rawType;
|
||||
return { id: path, label: path, type };
|
||||
return { id: path, label: path, type, vault: r.vault || null };
|
||||
});
|
||||
this._renderMenu('mention', items, (item) => this._selectMention(item));
|
||||
} catch (e) {
|
||||
@@ -851,18 +863,19 @@ class BooksLM {
|
||||
textarea.focus();
|
||||
}
|
||||
const path = item.id;
|
||||
const vault = item.vault || this._resolveVault();
|
||||
if (IMAGE_EXT_RE.test(path)) {
|
||||
this._addImagePath(path);
|
||||
this._addImagePath(path, vault);
|
||||
} else if (item.type === 'dir' || this._classifyPath(path) === 'dir') {
|
||||
this._addAdhocDir(path);
|
||||
this._addAdhocDir(path, vault);
|
||||
} else {
|
||||
this._addAdhocFile(path);
|
||||
this._addAdhocFile(path, vault);
|
||||
}
|
||||
}
|
||||
|
||||
_addImagePath(path) {
|
||||
_addImagePath(path, vault) {
|
||||
if (!path || this._images.some((img) => img.path === path)) return;
|
||||
this._images.push({ path, name: path.split('/').pop() });
|
||||
this._images.push({ path, name: path.split('/').pop(), vault: vault || null });
|
||||
this._renderAttachments();
|
||||
}
|
||||
|
||||
@@ -1593,7 +1606,7 @@ class BooksLM {
|
||||
|
||||
const payload = {
|
||||
mode: this._mode,
|
||||
vault: this._vault || this._resolveVault(),
|
||||
vault: this._contextVault(),
|
||||
directory: this._directory,
|
||||
context_files: this._mode === MODE.DOCUMENTS ? this._documents.map((d) => d.path) : [],
|
||||
extra_files: this._adhocFiles.map((f) => f.path),
|
||||
|
||||
+6
-6
@@ -9376,18 +9376,18 @@ body.bookslm-resizing { cursor: ew-resize; user-select: none; }
|
||||
.ai-picker-model-trigger:hover { border-color: var(--accent); }
|
||||
.ai-picker-model-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.ai-picker-model-chevron { opacity: 0.6; font-size: 0.6rem; }
|
||||
.ai-picker-model-pop { position: absolute; top: calc(100% + 4px); left: 0; z-index: 60;
|
||||
min-width: 220px; max-width: 320px; background: var(--bg-primary);
|
||||
.ai-picker-model-pop { position: absolute; top: calc(100% + 4px); right: 0; z-index: 60;
|
||||
min-width: 220px; width: 340px; max-width: calc(100vw - 24px); background: var(--bg-primary);
|
||||
border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.35);
|
||||
padding: 6px; }
|
||||
.ai-picker-model-pop.hidden { display: none; }
|
||||
.ai-picker-model-search { width: 100%; box-sizing: border-box; font-size: 0.72rem;
|
||||
background: var(--bg-secondary); color: var(--text-primary); border: 1px solid var(--border);
|
||||
border-radius: 6px; padding: 5px 8px; margin-bottom: 4px; }
|
||||
.ai-picker-model-list { max-height: 200px; overflow-y: auto; display: flex; flex-direction: column; }
|
||||
.ai-picker-model-option { display: block; width: 100%; text-align: left; font-size: 0.72rem;
|
||||
padding: 5px 8px; border-radius: 5px; background: none; border: none; color: var(--text-primary);
|
||||
cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.ai-picker-model-list { max-height: 240px; overflow-y: auto; display: flex; flex-direction: column; }
|
||||
.ai-picker-model-option { display: block; width: 100%; text-align: left; font-size: 0.78rem;
|
||||
line-height: 1.3; padding: 5px 8px; border-radius: 5px; background: none; border: none;
|
||||
color: var(--text-primary); cursor: pointer; white-space: normal; overflow-wrap: anywhere; }
|
||||
.ai-picker-model-option:hover, .ai-picker-model-option.active { background: var(--surface2); }
|
||||
.ai-picker-model-empty { font-size: 0.7rem; color: var(--text-secondary); padding: 6px 8px; text-align: center; }
|
||||
.ai-picker-model-more { font-size: 0.7rem; color: var(--text-secondary); padding: 6px 8px; text-align: center; }
|
||||
|
||||
Reference in New Issue
Block a user