88 lines
2.5 KiB
TypeScript
88 lines
2.5 KiB
TypeScript
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 = `<b>Переменная ${varName}</b>`;
|
|
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 };
|
|
}
|