import { Channel, invoke } from '@tauri-apps/api/core'; import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; import { marked } from 'marked'; import DOMPurify from 'dompurify'; type Audience = { id: string; name: string; instructions: string }; type Settings = { base_url: string; model: string; api_key_env: string; context_bytes: number; max_output_tokens: number; token_parameter: string; stream: boolean; selected_audience: string; audiences: Audience[] }; type Source = { id: string; role: string; path: string; start_line: number; end_line: number; enclosing: string; code: string; truncated: boolean }; type Callee = { is_concept: boolean; is_standard: boolean; id: string; location: { path: string; label: string; range: { start: { line: number; character: number } } } }; type Prepared = { concept_dependencies: boolean; include_standard: boolean; available_callees: Callee[]; selected_callees: string[]; recursion_depth: number; prompt_budget: number; id: number; endpoint: string; model: string; audience: string; prompt_bytes: number; messages: { role: string; content: string }[]; bundle: { selected_path: string; sources: Source[]; usages: unknown[]; references_found: number; callers_found: number; callees_found: number; omissions: string[]; diagnostics: string[] } }; type Options = { workspace: () => { root: string } | null; path: () => string; epoch: () => number; ready: () => Promise; open: (path: string, hit: { path: string; line: number; column: number; end_column: number }, semantic?: boolean) => Promise }; const $ = (id: string) => document.getElementById(id) as T; export function installExplanations(editor: monaco.editor.IStandaloneCodeEditor, options: Options) { const pane = document.createElement('section'); pane.id = 'explanation-pane'; pane.hidden = true; pane.innerHTML = `
Explain code

Select C/C++ code, then prepare context.

Preparation stays local. Sending uses the endpoint you configure.

Inspect context
Exact request messages

`; document.querySelector('main')!.append(pane); const dialog = document.createElement('dialog'); dialog.id = 'llm-settings'; dialog.innerHTML = `

Model settings

Saved for this project. Use an OpenAI-compatible Chat Completions endpoint.

`; document.body.append(dialog); let prepared: Prepared | null = null, text = '', generation = 0, busy = false; let questionRevision = 0, questionPending = false, bodiesDirty = false; let questionTimer: ReturnType | undefined; let settings: Settings | null = null, profile = '', renderTimer: ReturnType | undefined; function show() { pane.dispatchEvent(new Event('cex-show-chat')); pane.hidden = false; document.querySelector('main')!.classList.add('explaining'); editor.layout(); } function setBusy(value: boolean) { busy = value; $('prepare-context').disabled = value; $('send-explanation').disabled = value || !prepared || questionPending || bodiesDirty; $('callee-options').disabled = value; $('apply-callees').disabled = value || questionPending; $('cancel-explanation').hidden = !value; $('model-settings').disabled = value; $('explain-question').disabled = value; } function clearContext() { ++questionRevision; clearTimeout(questionTimer); questionPending = false; bodiesDirty = false; $('callee-options').hidden = true; $('context-size-warning').textContent = ''; prepared = null; $('send-explanation').disabled = true; $('context-summary').replaceChildren(); $('context-sources').replaceChildren(); $('context-messages').textContent = ''; $('explain-destination').textContent = ''; } async function citation(source: Source) { await options.open(source.path, { path: source.path, line: source.start_line, column: 1, end_column: 1 }, true); const current = editor.getModel()?.getLinesContent().slice(source.start_line - 1, source.end_line).join('\n'); $('citation-status').textContent = current === source.code ? `Opened ${source.path}:${source.start_line}.` : 'This file differs from the reviewed snapshot or could not be opened. Inspect context to see the exact evidence sent.'; } function renderAnswer() { const html = marked.parse(text, { async: false }); $('explanation-answer').innerHTML = DOMPurify.sanitize(html, { FORBID_TAGS: ['img', 'svg', 'math', 'iframe', 'object', 'form', 'input', 'button', 'video', 'audio'], FORBID_ATTR: ['style', 'src', 'srcset'] }); // Model-supplied URLs are text only. Only source IDs from our reviewed bundle // become actionable links; citations cannot grant arbitrary file access. $('explanation-answer').querySelectorAll('a').forEach(a => a.replaceWith(document.createTextNode(a.textContent ?? ''))); const walker = document.createTreeWalker($('explanation-answer'), NodeFilter.SHOW_TEXT); const nodes: Text[] = []; while (walker.nextNode()) { const node = walker.currentNode as Text; if (!node.parentElement?.closest('pre,code')) nodes.push(node); } for (const node of nodes) { const matches = [...node.data.matchAll(/\[S[1-9]\d*\]/g)]; if (!matches.length) continue; const fragment = document.createDocumentFragment(); let offset = 0; for (const match of matches) { fragment.append(node.data.slice(offset, match.index)); const id = match[0].slice(1, -1), source = prepared?.bundle.sources.find(s => s.id === id); if (source) { const button = document.createElement('button'); button.className = 'source-citation'; button.textContent = match[0]; button.title = `${source.path}:${source.start_line}-${source.end_line}`; button.onclick = () => void citation(source); fragment.append(button); } else { const unknown = document.createElement('span'); unknown.textContent = `${match[0]} (unknown source)`; unknown.className = 'error'; fragment.append(unknown); } offset = match.index! + match[0].length; } fragment.append(node.data.slice(offset)); node.replaceWith(fragment); } } function renderContext(value: Prepared, controls = true) { const fraction = value.prompt_bytes / value.prompt_budget; $('context-size-warning').textContent = `${(value.prompt_bytes / 1024).toFixed(1)} KiB / ${(value.prompt_budget / 1024).toFixed(0)} KiB prompt budget. ${fraction >= 0.75 || value.prompt_bytes >= 32768 ? 'Large context: sending this much code can increase latency and cost and leave less model capacity for the answer.' : 'More included bodies increase request size and generation cost.'}${value.bundle.sources.some(s => s.truncated) ? ' Some bodies are truncated; inspect the snapshots.' : ''}`; if (controls) { bodiesDirty = false; $('callee-options').hidden = false; $('apply-callees').textContent = value.concept_dependencies ? 'Apply concept selection' : 'Apply body selection'; $('dependency-heading').textContent = value.concept_dependencies ? 'Concept dependencies' : 'Called-function bodies'; $('include-standard-label').hidden = !value.concept_dependencies; $('include-standard').checked = value.include_standard ?? false; const depthOptions = $('callee-depth').options; depthOptions[0].text = '0 — No additional definitions'; depthOptions[1].text = value.concept_dependencies ? '1 — Selected concepts' : '1 — Selected direct callees'; depthOptions[2].text = value.concept_dependencies ? '2 — Selected concepts and their dependencies' : '2 — Selected callees and their callees'; $('callee-depth').value = String(value.recursion_depth); $('callee-list').replaceChildren(); for (const callee of value.available_callees) { const label = document.createElement('label'), input = document.createElement('input'); input.type = 'checkbox'; input.value = callee.id; input.dataset.standard = String(callee.is_standard ?? false); input.disabled = callee.is_standard && !value.include_standard; input.checked = !input.disabled && value.selected_callees.includes(callee.id); input.onchange = bodiesChanged; label.append(input, ` ${callee.location.label || 'Dependency'}${callee.is_standard ? ' [std]' : ''} — ${callee.location.path}:${callee.location.range.start.line + 1}`); $('callee-list').append(label); } if (!value.available_callees.length) $('callee-list').textContent = value.concept_dependencies ? 'No referenced concepts found in this constraint.' : 'No direct callees found for this selection.'; } $('explain-target').textContent = value.bundle.selected_path; $('explain-destination').textContent = `Send to ${value.endpoint} · model: ${value.model || '(not configured)'} · audience: ${value.audience}`; const summary = document.createElement('p'); summary.className = 'hint'; summary.textContent = `${value.bundle.sources.length} snapshots · ${value.bundle.references_found} references · ${value.bundle.callers_found} callers · ${value.concept_dependencies ? `${value.available_callees.length} concept dependencies` : `${value.bundle.callees_found} callees`} · ${(value.prompt_bytes / 1024).toFixed(1)} KiB of prompt messages (bytes, not tokens).`; $('context-summary').replaceChildren(summary); for (const message of [...value.bundle.omissions, ...value.bundle.diagnostics]) { const p = document.createElement('p'); p.className = 'hint'; p.textContent = message; $('context-summary').append(p); } $('context-sources').replaceChildren(); for (const source of value.bundle.sources) { const details = document.createElement('details'), title = document.createElement('summary'), pre = document.createElement('pre'); title.textContent = `[${source.id}] ${source.role}: ${source.path}:${source.start_line}-${source.end_line}${source.enclosing ? ` · ${source.enclosing}` : ''}${source.truncated ? ' (truncated)' : ''}`; pre.textContent = source.code.split('\n').map((line, i) => `${source.start_line + i}: ${line}`).join('\n'); details.append(title, pre); $('context-sources').append(details); } $('context-messages').textContent = JSON.stringify(value.messages, null, 2); } async function prepareContext() { const workspace = options.workspace(), path = options.path(); if (!workspace || !path || editor.getModel()?.getLanguageId() !== 'cpp') { $('explain-status').textContent = 'Open a C/C++ file and select a symbol first.'; return; } show(); const operation = ++generation, epoch = options.epoch(); const selected = editor.getSelection(); const position = selected && !selected.isEmpty() ? selected.getStartPosition() : editor.getPosition()!; const selection = selected && !selected.isEmpty() ? { start: { line: selected.startLineNumber - 1, character: selected.startColumn - 1 }, end: { line: selected.endLineNumber - 1, character: selected.endColumn - 1 } } : null; clearContext(); text = ''; $('explanation-answer').replaceChildren(); $('citation-status').textContent = ''; $('explain-status').textContent = 'Gathering compiler-backed context locally…'; setBusy(true); try { await options.ready(); if (operation !== generation || epoch !== options.epoch()) return; const value = await invoke('prepare_explanation', { root: workspace.root, path, position: { line: position.lineNumber - 1, character: position.column - 1 }, selection, question: $('explain-question').value }); if (operation !== generation || epoch !== options.epoch()) return; prepared = value; renderContext(value); $('context-details').open = true; $('explain-status').textContent = 'Context ready. Review the evidence and destination, then send.'; } catch (error) { if (operation === generation) $('explain-status').textContent = String(error); } finally { if (operation === generation) setBusy(false); } } function bodiesChanged() { bodiesDirty = true; if ($('callee-depth').value === '0' && $('callee-list').querySelector('input:checked')) $('callee-depth').value = '1'; setBusy(busy); $('explain-status').textContent = 'Dependency selection changed. Apply it to update the context and size preview.'; } $('callee-depth').onchange = () => { bodiesDirty = true; setBusy(busy); $('explain-status').textContent = 'Apply body selection to update the preview.'; }; $('include-standard').onchange = () => { const include = $('include-standard').checked; $('callee-list').querySelectorAll('input[data-standard="true"]').forEach(input => { input.disabled = !include; if (!include) input.checked = false; }); bodiesChanged(); }; $('callees-all').onclick = () => { $('callee-list').querySelectorAll('input').forEach(input => input.checked = !input.disabled); bodiesChanged(); }; $('callees-none').onclick = () => { $('callee-list').querySelectorAll('input').forEach(input => input.checked = false); bodiesChanged(); }; $('apply-callees').onclick = async () => { const workspace = options.workspace(); if (!workspace || !prepared || busy || questionPending) return; const operation = ++generation, epoch = options.epoch(); setBusy(true); $('explain-status').textContent = 'Collecting selected dependency definitions locally…'; try { const value = await invoke('expand_explanation', { root: workspace.root, id: prepared.id, selected: [...$('callee-list').querySelectorAll('input:checked')].map(input => input.value), depth: Number($('callee-depth').value), includeStandard: $('include-standard').checked, question: $('explain-question').value }); if (operation !== generation || epoch !== options.epoch()) return; prepared = value; text = ''; renderAnswer(); renderContext(value); $('explain-status').textContent = 'Body selection applied. Review the size and any omissions before sending.'; } catch (error) { if (operation === generation) $('explain-status').textContent = String(error); } finally { if (operation === generation) setBusy(false); } }; $('explain-question').oninput = () => { clearTimeout(questionTimer); const revision = ++questionRevision; if (!prepared) return; questionPending = true; setBusy(busy); $('explain-status').textContent = 'Updating prompt using the existing source snapshots…'; questionTimer = setTimeout(async () => { const workspace = options.workspace(), context = prepared; if (!workspace || !context) return; try { const value = await invoke('update_explanation_question', { root: workspace.root, id: context.id, question: $('explain-question').value }); if (revision !== questionRevision) return; prepared = value; renderContext(value, false); questionPending = false; setBusy(busy); $('explain-status').textContent = 'Prompt updated. Existing source snapshots are ready to send.'; } catch (error) { if (revision === questionRevision) $('explain-status').textContent = String(error); } }, 200); }; dialog.addEventListener('cancel', () => { $('llm-key').value = ''; }); $('prepare-context').onclick = () => void prepareContext(); $('nav-explain').onclick = () => { show(); if (!busy) void prepareContext(); }; editor.addAction({ id: 'cex.explain', label: 'Explain Selected Code…', contextMenuGroupId: 'navigation', contextMenuOrder: 2, run: () => { show(); if (!busy) return prepareContext(); } }); $('send-explanation').onclick = async () => { const workspace = options.workspace(); if (!workspace || !prepared || busy || questionPending || bodiesDirty) return; const operation = ++generation, epoch = options.epoch(), id = prepared.id; setBusy(true); text = ''; $('explanation-answer').replaceChildren(); $('explain-status').textContent = `Requesting explanation from ${prepared.endpoint}…`; const onDelta = new Channel(); onDelta.onmessage = delta => { if (operation !== generation || epoch !== options.epoch()) return; text += delta; if (renderTimer === undefined) renderTimer = setTimeout(() => { renderTimer = undefined; renderAnswer(); }, 80); }; try { const result = await invoke<{ text: string; finish_reason: string }>('send_explanation', { root: workspace.root, id, onDelta }); if (operation !== generation || epoch !== options.epoch()) return; text = result.text; renderAnswer(); $('explain-status').textContent = result.finish_reason === 'length' ? 'Output limit reached; this explanation may be incomplete.' : result.finish_reason === 'content_filter' ? 'The provider filtered this response; it may be incomplete.' : 'Explanation complete. Source citations open the reviewed locations.'; } catch (error) { if (operation === generation) { $('explain-status').textContent = `${String(error)}${text ? ' Text shown is partial.' : ''}`; renderAnswer(); } } finally { if (operation === generation) setBusy(false); } }; $('cancel-explanation').onclick = () => { ++generation; const workspace = options.workspace(); if (workspace) void invoke('cancel_explanation', { root: workspace.root }).catch(error => { $('explain-status').textContent = String(error); }); setBusy(false); renderAnswer(); $('explain-status').textContent = `Cancelled.${text ? ' Text shown is partial.' : ''}`; }; $('close-explanation').onclick = () => { pane.hidden = true; document.querySelector('main')!.classList.remove('explaining'); editor.layout(); }; function audienceInstructions() { if (settings) { const a = settings.audiences.find(a => a.id === profile); if (a) a.instructions = $('llm-instructions').value; } } $('llm-audience').onchange = () => { audienceInstructions(); profile = $('llm-audience').value; $('llm-instructions').value = settings?.audiences.find(a => a.id === profile)?.instructions ?? ''; }; $('model-settings').onclick = async () => { const workspace = options.workspace(); if (!workspace) { $('explain-status').textContent = 'Open a project first.'; return; } const epoch = options.epoch(); try { const result = await invoke<{ settings: Settings; has_session_key: boolean }>('get_llm_settings', { root: workspace.root }); if (epoch !== options.epoch()) return; settings = result.settings; $('llm-url').value = settings.base_url; $('llm-model').value = settings.model; $('llm-key-env').value = settings.api_key_env; $('llm-key').value = ''; $('clear-llm-key').checked = false; $('key-state').textContent = result.has_session_key ? 'A key is set for this session and endpoint.' : 'No session key is set. Local endpoints may not require one.'; $('llm-budget').value = String(settings.context_bytes / 1024); $('llm-output').value = String(settings.max_output_tokens); $('llm-token-parameter').value = settings.token_parameter; $('llm-stream').checked = settings.stream; $('llm-audience').replaceChildren(...settings.audiences.map(a => { const option = document.createElement('option'); option.value = a.id; option.textContent = a.name; return option; })); profile = settings.selected_audience; $('llm-audience').value = profile; $('llm-instructions').value = settings.audiences.find(a => a.id === profile)!.instructions; $('llm-error').textContent = ''; dialog.showModal(); } catch (error) { $('explain-status').textContent = String(error); } }; $('preset-local').onclick = () => { $('llm-url').value = 'http://localhost:1234/v1'; $('llm-key-env').value = ''; $('clear-llm-key').checked = true; }; $('preset-openai').onclick = () => { $('llm-url').value = 'https://api.openai.com/v1'; $('llm-key-env').value = 'OPENAI_API_KEY'; $('clear-llm-key').checked = true; }; $('llm-cancel').onclick = () => { $('llm-key').value = ''; dialog.close(); }; $('llm-form').onsubmit = async event => { event.preventDefault(); const workspace = options.workspace(); if (!workspace || !settings) return; audienceInstructions(); settings = { ...settings, base_url: $('llm-url').value.trim(), model: $('llm-model').value.trim(), api_key_env: $('llm-key-env').value.trim(), context_bytes: Number($('llm-budget').value) * 1024, max_output_tokens: Number($('llm-output').value), token_parameter: $('llm-token-parameter').value, stream: $('llm-stream').checked, selected_audience: profile }; const key = $('llm-key').value; const apiKey = key || ($('clear-llm-key').checked ? '' : null); $('llm-key').value = ''; $('llm-save').disabled = true; try { await invoke('save_llm_settings', { root: workspace.root, settings, apiKey }); clearContext(); text = ''; renderAnswer(); dialog.close(); $('explain-status').textContent = 'Settings saved. Prepare context using the selected audience before sending.'; } catch (error) { $('llm-error').textContent = String(error); } finally { $('llm-save').disabled = false; } }; return { reset() { ++generation; clearTimeout(renderTimer); renderTimer = undefined; clearContext(); text = ''; $('explanation-answer').replaceChildren(); $('citation-status').textContent = ''; $('explain-target').textContent = 'Select C/C++ code, then prepare context.'; $('explain-status').textContent = 'Preparation stays local. Sending uses the endpoint you configure.'; setBusy(false); dialog.close(); $('llm-key').value = ''; }, fileOpened() { $('nav-explain').disabled = editor.getModel()?.getLanguageId() !== 'cpp'; }, }; }