Give every code block its own copy and insert, and number the long ones

This commit is contained in:
Gmer4Lfe
2026-08-10 21:12:15 -04:00
parent eb24e60b8c
commit 9adf760c8e
2 changed files with 108 additions and 42 deletions
+11 -40
View File
@@ -2437,14 +2437,13 @@ if (document.getElementById('vv-sched-ai-chat')) {
vvAiLastQ = q;
return false;
},
onTurn: (answer) => { vvAiFixArm(); vvAiCodeArm(answer); requestAnimationFrame(vvFitRight); },
onTurn: () => { vvAiFixArm(); requestAnimationFrame(vvFitRight); },
// Expanding changes how much of the panel is left for the views above it. Without this the
// chat grows downward past the end of the panel instead of upward into it.
onResize: () => requestAnimationFrame(vvFitRight),
onOffer: (kind, yes) => {
if (kind === 'fix') vvAiFixAnswer(yes);
if (kind === 'code') vvAiCodeAnswer(yes);
},
onOffer: (kind, yes) => { if (kind === 'fix') vvAiFixAnswer(yes); },
// Per-block Insert buttons render only where a page can receive them.
onInsertCode: vvAiInsertCode,
});
}
@@ -2511,52 +2510,24 @@ let vvAiFixAsk = null; // { scope, symptom } while the offer is open and awa
// It is an offer, never automatic: the editor usually holds work in progress, and a reply that
// silently rewrote it would be far worse than copy/paste.
let vvAiCodePending = null;
// Largest fenced block wins when an answer carries several — an explanation typically quotes a
// line or two before giving the whole thing, and the whole thing is what was asked for. A shebang
// beats size outright, since that is unambiguously a script rather than an excerpt.
function vvAiCodeExtract(text) {
if (!text) return '';
const blocks = [];
const re = /```[a-zA-Z0-9_+-]*\n([\s\S]*?)```/g;
let m;
while ((m = re.exec(text)) !== null) blocks.push(m[1].replace(/\s+$/, ''));
if (!blocks.length) return '';
const shebang = blocks.find(b => b.startsWith('#!'));
return shebang || blocks.sort((a, b) => b.length - a.length)[0];
}
// display:'' vs 'none' is how this panel switches views — see vvShowEditor and friends.
function vvAiEditorOpen() {
const ed = document.getElementById('vv-editor');
return !!ed && ed.style.display !== 'none';
}
function vvAiCodeArm(answer) {
vvAiCodePending = null;
if (!vvSchedChat || !vvAiEditorOpen()) return;
const code = vvAiCodeExtract(answer);
// A single short line is a mention, not a script. Offering on those turns every answer into a
// question and trains the operator to ignore the buttons.
if (!code || (code.length < 40 && code.indexOf('\n') === -1)) return;
vvAiCodePending = code;
const lines = code.split('\n').length;
vvSchedChat.offer('code', 'Insert that ' + lines + '-line block into the editor at your cursor?');
}
function vvAiCodeAnswer(yes) {
const code = vvAiCodePending;
vvAiCodePending = null;
if (!yes || !code) return;
// Called by the chat component's per-block Insert button, with that block's exact text. This
// replaced a whole-answer offer: the offer had to guess which block was meant when an answer
// carried several, and it asked after every code answer whether or not anything was wanted.
// A button on each block needs no guess and no question.
function vvAiInsertCode(code) {
if (!code) return;
const ta = document.getElementById('vv-editor-body');
// They may have switched views between the answer and the click. Inserting into a hidden
// textarea would look like nothing happened and be discovered much later.
if (!ta || !vvAiEditorOpen()) {
vvSchedChat.note('The editor is no longer open — nothing was inserted.');
vvSchedChat.note('The editor is not open — nothing was inserted.');
return;
}