feat(ai): ajouter une section de la reponse et supporter l'editeur Forge (BUG-057, #102)
L'assistant IA peut desormais inserer sa reponse dans l'editeur Forge (postMessage parent-insert) en plus d'Editer, et chaque bloc de code propose un bouton « Ajouter la section » pour n'inserer que ce bloc.
This commit is contained in:
@@ -404,6 +404,73 @@ async function main() {
|
||||
panel.remove();
|
||||
});
|
||||
|
||||
await test("add action delegates to the Forge iframe when no CodeMirror is open (BUG-057)", () => {
|
||||
const b = new BooksLM();
|
||||
const panel = b._render();
|
||||
b._panel = panel;
|
||||
document.body.appendChild(panel);
|
||||
b._messages = [{ role: "assistant", content: "Réponse Forge" }];
|
||||
b._renderMessages();
|
||||
state.editorView = null;
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.id = "forge-iframe";
|
||||
document.body.appendChild(iframe);
|
||||
const posted = [];
|
||||
iframe.contentWindow.postMessage = (msg) => posted.push(msg);
|
||||
panel.querySelector(".bookslm-msg.assistant .bookslm-msg-action-insert").click();
|
||||
assert.equal(posted.length, 1, "one postMessage to the Forge iframe");
|
||||
assert.equal(posted[0].type, "parent-insert");
|
||||
assert.ok(posted[0].text.includes("Réponse Forge"), "answer forwarded");
|
||||
iframe.remove();
|
||||
panel.remove();
|
||||
});
|
||||
|
||||
await test("add action falls back to the plain textarea editor", () => {
|
||||
const b = new BooksLM();
|
||||
const panel = b._render();
|
||||
b._panel = panel;
|
||||
document.body.appendChild(panel);
|
||||
b._messages = [{ role: "assistant", content: "Réponse textarea" }];
|
||||
b._renderMessages();
|
||||
state.editorView = null;
|
||||
const ta = document.createElement("textarea");
|
||||
ta.value = "Ligne existante";
|
||||
document.body.appendChild(ta);
|
||||
ta.setSelectionRange(ta.value.length, ta.value.length);
|
||||
state.fallbackEditorEl = ta;
|
||||
panel.querySelector(".bookslm-msg.assistant .bookslm-msg-action-insert").click();
|
||||
assert.ok(ta.value.includes("Réponse textarea"), "answer appended to the textarea");
|
||||
state.fallbackEditorEl = null;
|
||||
ta.remove();
|
||||
panel.remove();
|
||||
});
|
||||
|
||||
await test("code block exposes an add-section button inserting only the block (#102)", () => {
|
||||
const b = new BooksLM();
|
||||
const panel = b._render();
|
||||
b._panel = panel;
|
||||
document.body.appendChild(panel);
|
||||
b._messages = [{
|
||||
role: "assistant",
|
||||
content: "Voici la section :\n\n```markdown\n## Titre\n\nContenu\n```\n\nFin.",
|
||||
}];
|
||||
b._renderMessages();
|
||||
const btn = panel.querySelector(".bookslm-msg.assistant .bookslm-code-insert");
|
||||
assert.ok(btn, "per-block add button rendered");
|
||||
const dispatched = [];
|
||||
state.editorView = {
|
||||
state: { selection: { main: { to: 0 } } },
|
||||
dispatch: (spec) => dispatched.push(spec),
|
||||
focus: () => {},
|
||||
};
|
||||
btn.click();
|
||||
assert.equal(dispatched.length, 1, "editor dispatch called");
|
||||
assert.ok(dispatched[0].changes.insert.includes("## Titre"), "block content inserted");
|
||||
assert.ok(!dispatched[0].changes.insert.includes("```"), "code fences stripped");
|
||||
state.editorView = null;
|
||||
panel.remove();
|
||||
});
|
||||
|
||||
await test("manual wheel scroll releases the top-pinning", async () => {
|
||||
const b = new BooksLM();
|
||||
const panel = b._render();
|
||||
|
||||
@@ -347,6 +347,13 @@ test("editor-poc.html reloads Forge buffer on parent-reload", () => {
|
||||
assert.match(handler[1], /loadFile\(\);/);
|
||||
});
|
||||
|
||||
test("editor-poc.html inserts assistant text on parent-insert (BUG-057)", () => {
|
||||
assert.match(forgeSrc, /if \(e\.data\.type === 'parent-insert' && typeof e\.data\.text === 'string'\) \{/);
|
||||
const handler = forgeSrc.match(/if \(e\.data\.type === 'parent-insert' && typeof e\.data\.text === 'string'\) \{([\s\S]*?)\n \}/);
|
||||
assert.ok(handler, "parent-insert handler not found");
|
||||
assert.match(handler[1], /insertAtCursor\(/);
|
||||
});
|
||||
|
||||
test("style.css lets the inline editor fill the content area", () => {
|
||||
assert.match(cssSrc, /\.editor-modal\.editor-inline-mode \{/);
|
||||
assert.match(cssSrc, /\.editor-modal\.editor-inline-mode \{[\s\S]*?pointer-events: none;/);
|
||||
|
||||
Reference in New Issue
Block a user