fix(ai+editeur): BUG-046 erreur lisible et vault réel pour l'ajout IA au document; BUG-045 fin de la double scrollbar
CI / lint (push) Successful in 1m26s
CI / security (push) Successful in 57s
CI / test (push) Successful in 2m51s
CI / build (push) Successful in 54s
CI / e2e (push) Successful in 11m2s

BUG-046: 'Ajouter du texte au document courant' échouait avec 'Échec de
l'action: [object Object]'. Trois causes corrigées:
- continuation de confirmation sans payload -> second Appliquer POSTait
  sans 'message' (422); le payload d'origine est désormais transmis
- detail 422 FastAPI (tableau d'objets) stringifié tel quel -> nouvelle
  _responseError() qui aplatit en message lisible
- prompt documents/dossier muet sur le vault -> le modèle inventait
  'vault: test' et append_to_file échouait en silence; build_system_prompt
  nomme le vault et enjoint les outils d'écriture de l'utiliser exactement

BUG-045: double barre de défilement dans l'éditeur Editer (fichiers
longs): #editor-body overflow:auto + cm-editor height:100% sous la barre
IA -> corps et scroller CodeMirror défilant tous les deux. Flex column,
seul le scroller défile; override global .cm-scroller retiré. SW v20.

Vérifié: pytest 1038, ruff backend 0, tests frontend AI 84/84,
editor-inline 25/25, validate-imports 38 modules; Playwright sur
l'instance de test (un seul conteneur scrollable) et flux agent complet
append_to_file ok=true sur fichier réel
This commit is contained in:
2026-09-16 09:10:58 -04:00
parent e3df4bbf00
commit 3261917d62
10 changed files with 143 additions and 15 deletions
+28
View File
@@ -191,6 +191,34 @@ async function main() {
panel.remove();
});
// ── 4a-bis. BUG-046: readable errors + payload carry-over on confirmation resume ──
await test("_responseError flattens FastAPI 422 array details (no [object Object])", async () => {
const b = new BooksLM();
const resp422 = {
status: 422,
json: async () => ({ detail: [{ type: "missing", loc: ["body", "message"], msg: "Field required", input: {} }] }),
};
const err422 = await b._responseError(resp422);
assert.equal(err422.message, "Field required", `got "${err422.message}"`);
assert.ok(!err422.message.includes("[object Object]"), "must never stringify to [object Object]");
const respStr = { status: 400, json: async () => ({ detail: "vault invalide" }) };
assert.equal((await b._responseError(respStr)).message, "vault invalide");
const respEmpty = { status: 500, json: async () => { throw new Error("no body"); } };
assert.equal((await b._responseError(respEmpty)).message, "HTTP 500");
});
await test("confirmation resume keeps the payload and formats errors (BUG-046)", async () => {
const { readFileSync } = await import("node:fs");
const src = readFileSync(path.join(JS_DIR, "bookslm.js"), "utf-8");
const apply = src.slice(src.indexOf("async _applyConfirmation"));
assert.match(apply, /payload:\s*msg\.payload\s*\?\s*\{[^\n]*\.\.\.msg\.payload[^\n]*\}\s*:\s*null/,
"continuation must inherit the original request payload (no null payload)");
assert.match(apply, /await this\._responseError\(resp\)/,
"resume path must format non-OK responses via _responseError");
});
// ── 4b. Notion-style thread: top-anchored latest post, provider/model tag, action bar ──
await test("thread renders chronologically (user post above its answer)", () => {
const b = new BooksLM();
+16
View File
@@ -282,6 +282,22 @@ test("style.css lets the inline editor fill the content area", () => {
assert.match(fill[1], /max-width: none;/);
});
// ── BUG-045: one scroll surface only in the CodeMirror editor ──
test("style.css: #editor-body scrolls through the CodeMirror scroller only", () => {
const body = cssSrc.match(/\.editor-body\.editor-body-cm \{([^}]*)\}/);
assert.ok(body, "combined .editor-body.editor-body-cm rule not found");
assert.match(body[1], /overflow:\s*hidden;/, "editor body must not scroll itself");
assert.match(body[1], /display:\s*flex;/);
assert.match(body[1], /flex-direction:\s*column;/);
const cm = cssSrc.match(/\.editor-body\.editor-body-cm \.cm-editor \{([^}]*)\}/);
assert.ok(cm, ".cm-editor fill rule not found");
assert.match(cm[1], /flex:\s*1 1 auto;/);
assert.match(cm[1], /height:\s*auto;/, "100% height below the AI toolbar row caused the double scrollbar");
// The global legacy override must be gone: scroller auto-height + forced overflow-y.
assert.ok(!/\.cm-scroller \{[^}]*min-height:\s*100%;/.test(cssSrc),
"global .cm-scroller min-height override reintroduces the double scrollbar");
});
console.log(`\n${testCount - failCount}/${testCount} tests passed`);
if (failCount > 0) {
console.error(`${failCount} test(s) failed`);
+17
View File
@@ -271,6 +271,23 @@ class TestBuildSystemPrompt:
assert "⚠️" in prompt or "volumineux" in prompt
def test_vault_name_guidance(self):
"""BUG-046: naming the vault steers write tools away from invented names."""
from backend.bookslm import build_system_prompt
context = {
"files": [{"path": "note.md", "title": "N", "content": "x", "type": "markdown"}],
"total_chars": 1,
"file_count": 1,
"directory_tree": "",
}
prompt = build_system_prompt(context, scope="documents", vault_name="TestVault")
assert "TestVault" in prompt
assert "append_to_file" in prompt
assert "jamais un nom inventé" in prompt
# Without a vault name the block stays out (general fallback prompts).
assert "jamais un nom inventé" not in build_system_prompt(context)
# ── Unit tests: caching ────────────────────────────────────────────────