import { syntaxTree } from '@codemirror/language'; import { type EditorState, type Extension } from '@codemirror/state'; import { hoverTooltip, type TooltipView } from '@codemirror/view'; import clsx from 'clsx'; import { findEnvelopingNodes } from '@/utils/codemirror'; import { describePromptVariable } from '../../labels'; import { type PromptVariableType } from '../../models/prompting'; import { Variable } from './parse/parser.terms'; /** * Retrieves variable from position in Editor. */ function findVariableAt(pos: number, state: EditorState) { const nodes = findEnvelopingNodes(pos, pos, syntaxTree(state), [Variable]); if (nodes.length !== 1) { return undefined; } const start = nodes[0].from; const end = nodes[0].to; const text = state.doc.sliceString(start, end); const match = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/.exec(text); const varName = match?.[1]; if (!varName) { return undefined; } return { varName: varName, start: start, end: end }; } const tooltipProducer = (available: string[]) => { return hoverTooltip((view, pos) => { const parse = findVariableAt(pos, view.state); if (!parse) { return null; } const isAvailable = available.includes(parse.varName); return { pos: parse.start, end: parse.end, above: false, create: () => domTooltipVariable(parse.varName, isAvailable) }; }); }; export function variableHoverTooltip(available: string[]): Extension { return [tooltipProducer(available)]; } /** * Create DOM tooltip for {@link PromptVariableType}. */ function domTooltipVariable(varName: string, isAvailable: boolean): TooltipView { const dom = document.createElement('div'); dom.className = clsx( 'max-h-100 max-w-100 min-w-40', 'dense', 'p-2 flex flex-col', 'rounded-md shadow-md', 'cc-scroll-y', 'text-sm bg-card', 'select-none cursor-auto' ); const header = document.createElement('p'); header.innerHTML = `Переменная ${varName}`; dom.appendChild(header); const status = document.createElement('p'); status.className = isAvailable ? 'text-green-700' : 'text-red-700'; status.innerText = isAvailable ? 'Доступна для использования' : 'Недоступна для использования'; dom.appendChild(status); const desc = document.createElement('p'); desc.className = ''; desc.innerText = `Описание: ${describePromptVariable(varName as PromptVariableType)}`; dom.appendChild(desc); return { dom: dom }; }