mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-08-16 13:50:36 +03:00
F: Implement prompt editor
This commit is contained in:
parent
a0a4317d71
commit
f473f319da
|
@ -4,7 +4,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"generate": "lezer-generator src/features/rsform/components/rs-input/rslang/rslang-fast.grammar -o src/features/rsform/components/rs-input/rslang/parser.ts && lezer-generator src/features/rsform/components/rs-input/rslang/rslang-ast.grammar -o src/features/rsform/components/rs-input/rslang/parser-ast.ts && lezer-generator src/features/rsform/components/refs-input/parse/refs-text.grammar -o src/features/rsform/components/refs-input/parse/parser.ts",
|
"generate": "lezer-generator src/features/rsform/components/rs-input/rslang/rslang-fast.grammar -o src/features/rsform/components/rs-input/rslang/parser.ts && lezer-generator src/features/rsform/components/rs-input/rslang/rslang-ast.grammar -o src/features/rsform/components/rs-input/rslang/parser-ast.ts && lezer-generator src/features/rsform/components/refs-input/parse/refs-text.grammar -o src/features/rsform/components/refs-input/parse/parser.ts && lezer-generator src/features/ai/components/prompt-input/parse/prompt-text.grammar -o src/features/ai/components/prompt-input/parse/parser.ts",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:e2e": "playwright test",
|
"test:e2e": "playwright test",
|
||||||
"dev": "vite --host",
|
"dev": "vite --host",
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { type CompletionContext } from '@codemirror/autocomplete';
|
||||||
|
|
||||||
|
import { describePromptVariable } from '../../labels';
|
||||||
|
import { type PromptVariableType } from '../../models/prompting';
|
||||||
|
|
||||||
|
export function variableCompletions(variables: string[]) {
|
||||||
|
return (context: CompletionContext) => {
|
||||||
|
let word = context.matchBefore(/\{\{[a-zA-Z.-]*/);
|
||||||
|
if (!word && context.explicit) {
|
||||||
|
word = { from: context.pos, to: context.pos, text: '' };
|
||||||
|
}
|
||||||
|
if (!word || (word.from == word.to && !context.explicit)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
from: word.from,
|
||||||
|
to: word.to,
|
||||||
|
options: variables.map(name => ({
|
||||||
|
label: `{{${name}}}`,
|
||||||
|
info: describePromptVariable(name as PromptVariableType)
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export { PromptInput } from './prompt-input';
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { syntaxTree } from '@codemirror/language';
|
||||||
|
import { RangeSetBuilder } from '@codemirror/state';
|
||||||
|
import { Decoration, type DecorationSet, type EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';
|
||||||
|
|
||||||
|
const invalidVarMark = Decoration.mark({
|
||||||
|
class: 'text-destructive'
|
||||||
|
});
|
||||||
|
|
||||||
|
const validMark = Decoration.mark({
|
||||||
|
class: 'text-(--acc-fg-purple)'
|
||||||
|
});
|
||||||
|
|
||||||
|
class MarkVariablesPlugin {
|
||||||
|
decorations: DecorationSet;
|
||||||
|
allowed: string[];
|
||||||
|
|
||||||
|
constructor(view: EditorView, allowed: string[]) {
|
||||||
|
this.allowed = allowed;
|
||||||
|
this.decorations = this.buildDecorations(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(update: ViewUpdate) {
|
||||||
|
if (update.docChanged || update.viewportChanged) {
|
||||||
|
this.decorations = this.buildDecorations(update.view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildDecorations(view: EditorView): DecorationSet {
|
||||||
|
const builder = new RangeSetBuilder<Decoration>();
|
||||||
|
const tree = syntaxTree(view.state);
|
||||||
|
const doc = view.state.doc;
|
||||||
|
|
||||||
|
tree.iterate({
|
||||||
|
enter: node => {
|
||||||
|
if (node.name === 'Variable') {
|
||||||
|
// Extract inner text from the Variable node ({{my_var}})
|
||||||
|
const text = doc.sliceString(node.from, node.to);
|
||||||
|
const match = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/.exec(text);
|
||||||
|
const varName = match?.[1];
|
||||||
|
|
||||||
|
if (!varName || !this.allowed.includes(varName)) {
|
||||||
|
builder.add(node.from, node.to, invalidVarMark);
|
||||||
|
} else {
|
||||||
|
builder.add(node.from, node.to, validMark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return builder.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a ViewPlugin that marks invalid variables in the editor. */
|
||||||
|
export function markVariables(allowed: string[]) {
|
||||||
|
return ViewPlugin.fromClass(
|
||||||
|
class extends MarkVariablesPlugin {
|
||||||
|
constructor(view: EditorView) {
|
||||||
|
super(view, allowed);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decorations: plugin => plugin.decorations
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { syntaxTree } from '@codemirror/language';
|
||||||
|
import { RangeSetBuilder } from '@codemirror/state';
|
||||||
|
import { Decoration, type DecorationSet, type EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';
|
||||||
|
|
||||||
|
const noSpellcheckMark = Decoration.mark({
|
||||||
|
attributes: { spellcheck: 'false' }
|
||||||
|
});
|
||||||
|
|
||||||
|
class NoSpellcheckPlugin {
|
||||||
|
decorations: DecorationSet;
|
||||||
|
|
||||||
|
constructor(view: EditorView) {
|
||||||
|
this.decorations = this.buildDecorations(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(update: ViewUpdate) {
|
||||||
|
if (update.docChanged || update.viewportChanged) {
|
||||||
|
this.decorations = this.buildDecorations(update.view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildDecorations(view: EditorView): DecorationSet {
|
||||||
|
const builder = new RangeSetBuilder<Decoration>();
|
||||||
|
|
||||||
|
const tree = syntaxTree(view.state);
|
||||||
|
for (const { from, to } of view.visibleRanges) {
|
||||||
|
tree.iterate({
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
enter: node => {
|
||||||
|
if (node.name === 'Variable') {
|
||||||
|
builder.add(node.from, node.to, noSpellcheckMark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Plugin that adds a no-spellcheck attribute to all variables in the editor. */
|
||||||
|
export const noSpellcheckForVariables = ViewPlugin.fromClass(NoSpellcheckPlugin, {
|
||||||
|
decorations: (plugin: NoSpellcheckPlugin) => plugin.decorations
|
||||||
|
});
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { styleTags, tags } from '@lezer/highlight';
|
||||||
|
|
||||||
|
export const highlighting = styleTags({
|
||||||
|
Variable: tags.name,
|
||||||
|
Error: tags.comment
|
||||||
|
});
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { LRLanguage } from '@codemirror/language';
|
||||||
|
|
||||||
|
import { parser } from './parser';
|
||||||
|
|
||||||
|
export const PromptLanguage = LRLanguage.define({
|
||||||
|
parser: parser,
|
||||||
|
languageData: {}
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
|
export const Text = 1,
|
||||||
|
Variable = 2,
|
||||||
|
Error = 3,
|
||||||
|
Filler = 4;
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { printTree } from '@/utils/codemirror';
|
||||||
|
|
||||||
|
import { parser } from './parser';
|
||||||
|
|
||||||
|
const testData = [
|
||||||
|
['', '[Text]'],
|
||||||
|
['тест русский', '[Text[Filler]]'],
|
||||||
|
['test english', '[Text[Filler]]'],
|
||||||
|
['test greek σσσ', '[Text[Filler]]'],
|
||||||
|
['X1 раз два X2', '[Text[Filler]]'],
|
||||||
|
['{{variable}}', '[Text[Variable]]'],
|
||||||
|
['{{var_1}}', '[Text[Variable]]'],
|
||||||
|
['{{user.name}}', '[Text[Variable]]'],
|
||||||
|
['!error!', '[Text[Error]]'],
|
||||||
|
['word !error! word', '[Text[Filler][Error][Filler]]'],
|
||||||
|
['{{variable}} !error! word', '[Text[Variable][Error][Filler]]'],
|
||||||
|
['word {{variable}}', '[Text[Filler][Variable]]'],
|
||||||
|
['word {{variable}} !error!', '[Text[Filler][Variable][Error]]'],
|
||||||
|
['{{variable}} word', '[Text[Variable][Filler]]'],
|
||||||
|
['!err! {{variable}}', '[Text[Error][Variable]]'],
|
||||||
|
['!err! {{variable}} word', '[Text[Error][Variable][Filler]]']
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
/** Test prompt grammar parser with various prompt inputs */
|
||||||
|
describe('Prompt grammar parser', () => {
|
||||||
|
it.each(testData)('Parse %p', (input: string, expectedTree: string) => {
|
||||||
|
const tree = parser.parse(input);
|
||||||
|
expect(printTree(tree)).toBe(expectedTree);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,20 @@
|
||||||
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
|
import { LRParser } from '@lezer/lr';
|
||||||
|
import { highlighting } from './highlight';
|
||||||
|
export const parser = LRParser.deserialize({
|
||||||
|
version: 14,
|
||||||
|
states:
|
||||||
|
"!vQVQPOOObQQO'#C^OgQPO'#CjO]QPO'#C_OOQO'#C`'#C`OOQO'#Ce'#CeOOQO'#Ca'#CaQVQPOOOuQPO,58xOOQO,59U,59UOzQPO,58yOOQO-E6_-E6_OOQO1G.d1G.dOOQO1G.e1G.e",
|
||||||
|
stateData: '!S~OWOS~OZPO]RO_QO~O[WO~O_QOU^XZ^X]^X~OY[O~O]]O~O_W~',
|
||||||
|
goto: 'x_PP```dPPPjPPPPnTTOVQVORZVTUOVSSOVQXQRYR',
|
||||||
|
nodeNames: '⚠ Text Variable Error Filler',
|
||||||
|
maxTerm: 15,
|
||||||
|
propSources: [highlighting],
|
||||||
|
skippedNodes: [0],
|
||||||
|
repeatNodeCount: 1,
|
||||||
|
tokenData:
|
||||||
|
"(^~RqOX#YXZ#zZ^$o^p#Ypq#zqr&hr!b#Y!c!}&m!}#R#Y#R#S&m#S#T#Y#T#o&m#o#p'v#q#r(R#r#y#Y#y#z$o#z$f#Y$f$g$o$g#BY#Y#BY#BZ$o#BZ$IS#Y$IS$I_$o$I_$I|#Y$I|$JO$o$JO$JT#Y$JT$JU$o$JU$KV#Y$KV$KW$o$KW&FU#Y&FU&FV$o&FV;'S#Y;'S;=`#t<%lO#YP#_V_POX#YZp#Yr!b#Y!c#o#Y#r;'S#Y;'S;=`#t<%lO#YP#wP;=`<%l#Y~$PYW~X^#zpq#z#y#z#z$f$g#z#BY#BZ#z$IS$I_#z$I|$JO#z$JT$JU#z$KV$KW#z&FU&FV#z~$vj_PW~OX#YXZ#zZ^$o^p#Ypq#zr!b#Y!c#o#Y#r#y#Y#y#z$o#z$f#Y$f$g$o$g#BY#Y#BY#BZ$o#BZ$IS#Y$IS$I_$o$I_$I|#Y$I|$JO$o$JO$JT#Y$JT$JU$o$JU$KV#Y$KV$KW$o$KW&FU#Y&FU&FV$o&FV;'S#Y;'S;=`#t<%lO#Y~&mO]~R&t`[Q_POX#YZp#Yr}#Y}!O&m!O!P&m!P!Q#Y!Q![&m![!b#Y!c!}&m!}#R#Y#R#S&m#S#T#Y#T#o&m#r;'S#Y;'S;=`#t<%lO#Y~'yP#o#p'|~(ROZ~~(UP#q#r(X~(^OY~",
|
||||||
|
tokenizers: [0, 1],
|
||||||
|
topRules: { Text: [0, 1] },
|
||||||
|
tokenPrec: 47
|
||||||
|
});
|
|
@ -0,0 +1,39 @@
|
||||||
|
@precedence {
|
||||||
|
text @right
|
||||||
|
p1
|
||||||
|
p2
|
||||||
|
p3
|
||||||
|
}
|
||||||
|
|
||||||
|
@top Text { textItem* }
|
||||||
|
|
||||||
|
@skip { space }
|
||||||
|
|
||||||
|
@tokens {
|
||||||
|
space { @whitespace+ }
|
||||||
|
variable { $[a-zA-Z_]$[a-zA-Z0-9_.-]* }
|
||||||
|
word { ![@{|}! \t\n]+ }
|
||||||
|
|
||||||
|
@precedence { word, space }
|
||||||
|
}
|
||||||
|
|
||||||
|
textItem {
|
||||||
|
!p1 Variable |
|
||||||
|
!p2 Error |
|
||||||
|
!p3 Filler
|
||||||
|
}
|
||||||
|
|
||||||
|
Filler { word_enum }
|
||||||
|
Error { "!" word_enum "!" }
|
||||||
|
word_enum {
|
||||||
|
word |
|
||||||
|
word !text word_enum
|
||||||
|
}
|
||||||
|
|
||||||
|
Variable {
|
||||||
|
"{{" variable "}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
@detectDelim
|
||||||
|
|
||||||
|
@external propSource highlighting from "./highlight"
|
|
@ -0,0 +1,144 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { forwardRef, useRef } from 'react';
|
||||||
|
import { autocompletion } from '@codemirror/autocomplete';
|
||||||
|
import { type Extension } from '@codemirror/state';
|
||||||
|
import { tags } from '@lezer/highlight';
|
||||||
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
|
import CodeMirror, {
|
||||||
|
type BasicSetupOptions,
|
||||||
|
type ReactCodeMirrorProps,
|
||||||
|
type ReactCodeMirrorRef
|
||||||
|
} from '@uiw/react-codemirror';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import { EditorView } from 'codemirror';
|
||||||
|
|
||||||
|
import { Label } from '@/components/input';
|
||||||
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
|
import { APP_COLORS } from '@/styling/colors';
|
||||||
|
|
||||||
|
import { PromptVariableType } from '../../models/prompting';
|
||||||
|
|
||||||
|
import { variableCompletions } from './completion';
|
||||||
|
import { markVariables } from './mark-variables';
|
||||||
|
import { noSpellcheckForVariables } from './no-spellcheck';
|
||||||
|
import { PromptLanguage } from './parse';
|
||||||
|
import { variableHoverTooltip } from './tooltip';
|
||||||
|
|
||||||
|
const EDITOR_OPTIONS: BasicSetupOptions = {
|
||||||
|
highlightSpecialChars: false,
|
||||||
|
history: true,
|
||||||
|
drawSelection: false,
|
||||||
|
syntaxHighlighting: false,
|
||||||
|
defaultKeymap: true,
|
||||||
|
historyKeymap: true,
|
||||||
|
|
||||||
|
lineNumbers: false,
|
||||||
|
highlightActiveLineGutter: false,
|
||||||
|
foldGutter: false,
|
||||||
|
dropCursor: true,
|
||||||
|
allowMultipleSelections: false,
|
||||||
|
indentOnInput: false,
|
||||||
|
bracketMatching: false,
|
||||||
|
closeBrackets: false,
|
||||||
|
autocompletion: false,
|
||||||
|
rectangularSelection: false,
|
||||||
|
crosshairCursor: false,
|
||||||
|
highlightActiveLine: false,
|
||||||
|
highlightSelectionMatches: false,
|
||||||
|
closeBracketsKeymap: false,
|
||||||
|
searchKeymap: false,
|
||||||
|
foldKeymap: false,
|
||||||
|
completionKeymap: false,
|
||||||
|
lintKeymap: false
|
||||||
|
};
|
||||||
|
|
||||||
|
interface PromptInputProps
|
||||||
|
extends Pick<
|
||||||
|
ReactCodeMirrorProps,
|
||||||
|
| 'id' //
|
||||||
|
| 'height'
|
||||||
|
| 'minHeight'
|
||||||
|
| 'maxHeight'
|
||||||
|
| 'value'
|
||||||
|
| 'onFocus'
|
||||||
|
| 'onBlur'
|
||||||
|
| 'placeholder'
|
||||||
|
| 'style'
|
||||||
|
| 'className'
|
||||||
|
> {
|
||||||
|
value: string;
|
||||||
|
onChange: (newValue: string) => void;
|
||||||
|
availableVariables?: string[];
|
||||||
|
|
||||||
|
label?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
initialValue?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PromptInput = forwardRef<ReactCodeMirrorRef, PromptInputProps>(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
id, //
|
||||||
|
label,
|
||||||
|
disabled,
|
||||||
|
onChange,
|
||||||
|
...restProps
|
||||||
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
const darkMode = usePreferencesStore(state => state.darkMode);
|
||||||
|
|
||||||
|
const internalRef = useRef<ReactCodeMirrorRef>(null);
|
||||||
|
const thisRef = !ref || typeof ref === 'function' ? internalRef : ref;
|
||||||
|
|
||||||
|
const cursor = !disabled ? 'cursor-text' : 'cursor-default';
|
||||||
|
const customTheme: Extension = createTheme({
|
||||||
|
theme: darkMode ? 'dark' : 'light',
|
||||||
|
settings: {
|
||||||
|
fontFamily: 'inherit',
|
||||||
|
background: !disabled ? APP_COLORS.bgInput : APP_COLORS.bgDefault,
|
||||||
|
foreground: APP_COLORS.fgDefault,
|
||||||
|
caret: APP_COLORS.fgDefault
|
||||||
|
},
|
||||||
|
styles: [
|
||||||
|
{ tag: tags.name, cursor: 'default' }, // Variable
|
||||||
|
{ tag: tags.comment, color: APP_COLORS.fgRed } // Error
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const variables = restProps.availableVariables ?? Object.values(PromptVariableType);
|
||||||
|
const autoCompleter = autocompletion({
|
||||||
|
override: [variableCompletions(variables)],
|
||||||
|
activateOnTyping: true,
|
||||||
|
icons: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const editorExtensions = [
|
||||||
|
EditorView.lineWrapping,
|
||||||
|
EditorView.contentAttributes.of({ spellcheck: 'true' }),
|
||||||
|
PromptLanguage,
|
||||||
|
variableHoverTooltip(variables),
|
||||||
|
autoCompleter,
|
||||||
|
noSpellcheckForVariables,
|
||||||
|
markVariables(variables)
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={clsx('flex flex-col gap-2', cursor)}>
|
||||||
|
<Label text={label} />
|
||||||
|
<CodeMirror
|
||||||
|
id={id}
|
||||||
|
ref={thisRef}
|
||||||
|
basicSetup={EDITOR_OPTIONS}
|
||||||
|
theme={customTheme}
|
||||||
|
extensions={editorExtensions}
|
||||||
|
indentWithTab={false}
|
||||||
|
onChange={onChange}
|
||||||
|
editable={!disabled}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
|
@ -0,0 +1,87 @@
|
||||||
|
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',
|
||||||
|
'px-2 py-1 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 };
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
import { TextArea } from '@/components/input';
|
import { TextArea } from '@/components/input';
|
||||||
|
|
||||||
|
import { PromptInput } from '../../components/prompt-input';
|
||||||
|
import { useAvailableVariables } from '../../stores/use-available-variables';
|
||||||
|
|
||||||
interface TabPromptEditProps {
|
interface TabPromptEditProps {
|
||||||
label: string;
|
label: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
@ -8,6 +11,7 @@ interface TabPromptEditProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TabPromptEdit({ label, description, text, setText }: TabPromptEditProps) {
|
export function TabPromptEdit({ label, description, text, setText }: TabPromptEditProps) {
|
||||||
|
const availableVariables = useAvailableVariables();
|
||||||
return (
|
return (
|
||||||
<div className='cc-column'>
|
<div className='cc-column'>
|
||||||
<div className='flex flex-col gap-2'>
|
<div className='flex flex-col gap-2'>
|
||||||
|
@ -20,13 +24,13 @@ export function TabPromptEdit({ label, description, text, setText }: TabPromptEd
|
||||||
rows={1}
|
rows={1}
|
||||||
/>
|
/>
|
||||||
<TextArea id='prompt-description' label='Описание' value={description} disabled noResize rows={3} />
|
<TextArea id='prompt-description' label='Описание' value={description} disabled noResize rows={3} />
|
||||||
<TextArea
|
<PromptInput
|
||||||
id='prompt-text' //
|
id='prompt-text' //
|
||||||
label='Текст шаблона'
|
label='Текст шаблона'
|
||||||
value={text}
|
value={text}
|
||||||
onChange={event => setText(event.target.value)}
|
onChange={setText}
|
||||||
noResize
|
maxHeight='10rem'
|
||||||
rows={8}
|
availableVariables={availableVariables}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
/** Represents prompt variable type. */
|
/** Represents prompt variable type. */
|
||||||
export const PromptVariableType = {
|
export const PromptVariableType = {
|
||||||
BLOCK: 'block',
|
|
||||||
|
|
||||||
OSS: 'oss',
|
|
||||||
|
|
||||||
SCHEMA: 'schema',
|
SCHEMA: 'schema',
|
||||||
SCHEMA_THESAURUS: 'schema.thesaurus',
|
SCHEMA_THESAURUS: 'schema.thesaurus',
|
||||||
// SCHEMA_GRAPH: 'schema.graph',
|
// SCHEMA_GRAPH: 'schema.graph',
|
||||||
// SCHEMA_TYPE_GRAPH: 'schema.type-graph',
|
// SCHEMA_TYPE_GRAPH: 'schema.type-graph',
|
||||||
|
|
||||||
CONSTITUENTA: 'constituenta',
|
CONSTITUENTA: 'constituenta',
|
||||||
CONSTITUENTA_SYNTAX_TREE: 'constituent.ast'
|
CONSTITUENTA_SYNTAX_TREE: 'constituent.ast',
|
||||||
|
|
||||||
|
OSS: 'oss',
|
||||||
|
|
||||||
|
BLOCK: 'block'
|
||||||
} as const;
|
} as const;
|
||||||
export type PromptVariableType = (typeof PromptVariableType)[keyof typeof PromptVariableType];
|
export type PromptVariableType = (typeof PromptVariableType)[keyof typeof PromptVariableType];
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useDebounce } from 'use-debounce';
|
import { useDebounce } from 'use-debounce';
|
||||||
|
|
||||||
|
import { PromptInput } from '@/features/ai/components/prompt-input';
|
||||||
import { useAuthSuspense } from '@/features/auth';
|
import { useAuthSuspense } from '@/features/auth';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/control';
|
import { MiniButton } from '@/components/control';
|
||||||
|
@ -88,6 +89,11 @@ export function FormPromptTemplate({ promptTemplate, className, isMutable, toggl
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleChangeText(newValue: string, onChange: (newValue: string) => void) {
|
||||||
|
setSampleResult(null);
|
||||||
|
onChange(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
id={globalIDs.prompt_editor}
|
id={globalIDs.prompt_editor}
|
||||||
|
@ -108,14 +114,21 @@ export function FormPromptTemplate({ promptTemplate, className, isMutable, toggl
|
||||||
error={errors.description}
|
error={errors.description}
|
||||||
disabled={isProcessing || !isMutable}
|
disabled={isProcessing || !isMutable}
|
||||||
/>
|
/>
|
||||||
<TextArea
|
|
||||||
id='prompt_text'
|
<Controller
|
||||||
label='Содержание' //
|
control={control}
|
||||||
fitContent
|
name='text'
|
||||||
className='disabled:min-h-9 max-h-64'
|
render={({ field }) => (
|
||||||
{...register('text')}
|
<PromptInput
|
||||||
error={errors.text}
|
id='prompt_text'
|
||||||
disabled={isProcessing || !isMutable}
|
label='Содержание'
|
||||||
|
placeholder='Пример: Предложи дополнение для КС {{schema}}'
|
||||||
|
className='disabled:min-h-9 max-h-64'
|
||||||
|
value={field.value}
|
||||||
|
onChange={newValue => handleChangeText(newValue, field.onChange)}
|
||||||
|
disabled={isProcessing || !isMutable}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
<div className='flex justify-between'>
|
<div className='flex justify-between'>
|
||||||
<Controller
|
<Controller
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
|
import { useFitHeight } from '@/stores/app-layout';
|
||||||
|
|
||||||
import { describePromptVariable } from '../../labels';
|
import { describePromptVariable } from '../../labels';
|
||||||
import { PromptVariableType } from '../../models/prompting';
|
import { PromptVariableType } from '../../models/prompting';
|
||||||
|
|
||||||
/** Displays all prompt variable types with their descriptions. */
|
/** Displays all prompt variable types with their descriptions. */
|
||||||
export function TabViewVariables() {
|
export function TabViewVariables() {
|
||||||
|
const panelHeight = useFitHeight('3rem');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='pt-8'>
|
<div className='mt-10 cc-scroll-y min-h-40' style={{ maxHeight: panelHeight }}>
|
||||||
<ul className='space-y-1'>
|
<ul className='space-y-1'>
|
||||||
{Object.values(PromptVariableType).map(variableType => (
|
{Object.values(PromptVariableType).map(variableType => (
|
||||||
<li key={variableType} className='flex flex-col'>
|
<li key={variableType} className='flex flex-col'>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
export const
|
export const Text = 1,
|
||||||
Text = 1,
|
|
||||||
RefEntity = 2,
|
RefEntity = 2,
|
||||||
Global = 3,
|
Global = 3,
|
||||||
Grams = 4,
|
Grams = 4,
|
||||||
|
@ -8,4 +7,4 @@ export const
|
||||||
Offset = 6,
|
Offset = 6,
|
||||||
Nominal = 7,
|
Nominal = 7,
|
||||||
Error = 8,
|
Error = 8,
|
||||||
Filler = 9
|
Filler = 9;
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
import {LRParser} from "@lezer/lr"
|
import { LRParser } from '@lezer/lr';
|
||||||
import {highlighting} from "./highlight"
|
import { highlighting } from './highlight';
|
||||||
export const parser = LRParser.deserialize({
|
export const parser = LRParser.deserialize({
|
||||||
version: 14,
|
version: 14,
|
||||||
states: "$nQVQPOOObQQO'#C^OOQO'#Cl'#ClOjQPO'#CuOxQPO'#CdOOQO'#Ce'#CeOOQO'#Ck'#CkOOQO'#Cf'#CfQVQPOOO}QPO,58xO!SQPO,58{OOQO,59a,59aO!XQPO,59OOOQO-E6d-E6dO!^QSO1G.dO!cQPO1G.gOOQO1G.j1G.jO!hQQO'#CpOOQO'#C`'#C`O!pQPO7+$OOOQO'#Cg'#CgO!uQPO'#CcO!}QPO7+$RO!^QSO,59[OOQO<<Gj<<GjOOQO-E6e-E6eOOQO<<Gm<<GmOOQO1G.v1G.v",
|
states:
|
||||||
stateData: "#V~O^OS~ObPOgROhSO~ORXOUYO~OgRO[iXbiXhiX~OgRO~Oc^O~Oc_O~Oh`O~OeaO~OgdO~OfgOadX~OahO~OgdOaVX~OajO~Og^~",
|
"$nQVQPOOObQQO'#C^OOQO'#Cl'#ClOjQPO'#CuOxQPO'#CdOOQO'#Ce'#CeOOQO'#Ck'#CkOOQO'#Cf'#CfQVQPOOO}QPO,58xO!SQPO,58{OOQO,59a,59aO!XQPO,59OOOQO-E6d-E6dO!^QSO1G.dO!cQPO1G.gOOQO1G.j1G.jO!hQQO'#CpOOQO'#C`'#C`O!pQPO7+$OOOQO'#Cg'#CgO!uQPO'#CcO!}QPO7+$RO!^QSO,59[OOQO<<Gj<<GjOOQO-E6e-E6eOOQO<<Gm<<GmOOQO1G.v1G.v",
|
||||||
goto: "!kjPPkPokPruuy!PPPP!VuPPP!ZPPPP!aTQOWRc^Rf_TUOWQWOR]WQe_RieTVOWQb^RkgSTOWQZRR[S",
|
stateData: '#V~O^OS~ObPOgROhSO~ORXOUYO~OgRO[iXbiXhiX~OgRO~Oc^O~Oc_O~Oh`O~OeaO~OgdO~OfgOadX~OahO~OgdOaVX~OajO~Og^~',
|
||||||
nodeNames: "⚠ Text RefEntity Global Grams RefSyntactic Offset Nominal Error Filler",
|
goto: '!kjPPkPokPruuy!PPPP!VuPPP!ZPPPP!aTQOWRc^Rf_TUOWQWOR]WQe_RieTVOWQb^RkgSTOWQZRR[S',
|
||||||
|
nodeNames: '⚠ Text RefEntity Global Grams RefSyntactic Offset Nominal Error Filler',
|
||||||
maxTerm: 25,
|
maxTerm: 25,
|
||||||
propSources: [highlighting],
|
propSources: [highlighting],
|
||||||
skippedNodes: [0],
|
skippedNodes: [0],
|
||||||
repeatNodeCount: 2,
|
repeatNodeCount: 2,
|
||||||
tokenData: ".V~R!UOX$eXZ%VZ^%z^p$epq%Vqr'sr|$e|}'x}!O(f!O!Q$e!Q!R)i!R![*i![!b$e!b!c+k!c!d+v!d!e)i!e!f+v!f!g+v!g!h)i!h!i+v!i!r)i!r!s+v!s!t)i!t!u+v!u!v+v!v!w+v!w!z)i!z!{+v!{!})i!}#T$e#T#o)i#p#q-{#q#r.Q#r#y$e#y#z%z#z$f$e$f$g%z$g#BY$e#BY#BZ%z#BZ$IS$e$IS$I_%z$I_$I|$e$I|$JO%z$JO$JT$e$JT$JU%z$JU$KV$e$KV$KW%z$KW&FU$e&FU&FV%z&FV;'S$e;'S;=`%P<%lO$eP$jVgPOX$eZp$er!b$e!c#o$e#r;'S$e;'S;=`%P<%lO$eP%SP;=`<%l$e~%[Y^~X^%Vpq%V#y#z%V$f$g%V#BY#BZ%V$IS$I_%V$I|$JO%V$JT$JU%V$KV$KW%V&FU&FV%V~&RjgP^~OX$eXZ%VZ^%z^p$epq%Vr!b$e!c#o$e#r#y$e#y#z%z#z$f$e$f$g%z$g#BY$e#BY#BZ%z#BZ$IS$e$IS$I_%z$I_$I|$e$I|$JO%z$JO$JT$e$JT$JU%z$JU$KV$e$KV$KW%z$KW&FU$e&FU&FV%z&FV;'S$e;'S;=`%P<%lO$e~'xOh~R(PVfQgPOX$eZp$er!b$e!c#o$e#r;'S$e;'S;=`%P<%lO$eV(m^eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q!R)i!R![*i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eT)p]eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![)i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eV*r]UQeSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![*i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$e~+nP#o#p+q~+vOb~V+}^eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q!R)i!R![,y![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eV-S]RQeSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![,y![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$e~.QOc~~.VOa~",
|
tokenData:
|
||||||
|
".V~R!UOX$eXZ%VZ^%z^p$epq%Vqr'sr|$e|}'x}!O(f!O!Q$e!Q!R)i!R![*i![!b$e!b!c+k!c!d+v!d!e)i!e!f+v!f!g+v!g!h)i!h!i+v!i!r)i!r!s+v!s!t)i!t!u+v!u!v+v!v!w+v!w!z)i!z!{+v!{!})i!}#T$e#T#o)i#p#q-{#q#r.Q#r#y$e#y#z%z#z$f$e$f$g%z$g#BY$e#BY#BZ%z#BZ$IS$e$IS$I_%z$I_$I|$e$I|$JO%z$JO$JT$e$JT$JU%z$JU$KV$e$KV$KW%z$KW&FU$e&FU&FV%z&FV;'S$e;'S;=`%P<%lO$eP$jVgPOX$eZp$er!b$e!c#o$e#r;'S$e;'S;=`%P<%lO$eP%SP;=`<%l$e~%[Y^~X^%Vpq%V#y#z%V$f$g%V#BY#BZ%V$IS$I_%V$I|$JO%V$JT$JU%V$KV$KW%V&FU&FV%V~&RjgP^~OX$eXZ%VZ^%z^p$epq%Vr!b$e!c#o$e#r#y$e#y#z%z#z$f$e$f$g%z$g#BY$e#BY#BZ%z#BZ$IS$e$IS$I_%z$I_$I|$e$I|$JO%z$JO$JT$e$JT$JU%z$JU$KV$e$KV$KW%z$KW&FU$e&FU&FV%z&FV;'S$e;'S;=`%P<%lO$e~'xOh~R(PVfQgPOX$eZp$er!b$e!c#o$e#r;'S$e;'S;=`%P<%lO$eV(m^eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q!R)i!R![*i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eT)p]eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![)i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eV*r]UQeSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![*i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$e~+nP#o#p+q~+vOb~V+}^eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q!R)i!R![,y![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eV-S]RQeSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![,y![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$e~.QOc~~.VOa~",
|
||||||
tokenizers: [0, 1, 2],
|
tokenizers: [0, 1, 2],
|
||||||
topRules: {"Text":[0,1]},
|
topRules: { Text: [0, 1] },
|
||||||
tokenPrec: 96
|
tokenPrec: 96
|
||||||
})
|
});
|
||||||
|
|
|
@ -30,6 +30,34 @@ import { refsNavigation } from './click-navigation';
|
||||||
import { NaturalLanguage, ReferenceTokens } from './parse';
|
import { NaturalLanguage, ReferenceTokens } from './parse';
|
||||||
import { refsHoverTooltip } from './tooltip';
|
import { refsHoverTooltip } from './tooltip';
|
||||||
|
|
||||||
|
const editorSetup: BasicSetupOptions = {
|
||||||
|
highlightSpecialChars: false,
|
||||||
|
history: true,
|
||||||
|
drawSelection: false,
|
||||||
|
syntaxHighlighting: false,
|
||||||
|
defaultKeymap: true,
|
||||||
|
historyKeymap: true,
|
||||||
|
|
||||||
|
lineNumbers: false,
|
||||||
|
highlightActiveLineGutter: false,
|
||||||
|
foldGutter: false,
|
||||||
|
dropCursor: true,
|
||||||
|
allowMultipleSelections: false,
|
||||||
|
indentOnInput: false,
|
||||||
|
bracketMatching: false,
|
||||||
|
closeBrackets: false,
|
||||||
|
autocompletion: false,
|
||||||
|
rectangularSelection: false,
|
||||||
|
crosshairCursor: false,
|
||||||
|
highlightActiveLine: false,
|
||||||
|
highlightSelectionMatches: false,
|
||||||
|
closeBracketsKeymap: false,
|
||||||
|
searchKeymap: false,
|
||||||
|
foldKeymap: false,
|
||||||
|
completionKeymap: false,
|
||||||
|
lintKeymap: false
|
||||||
|
};
|
||||||
|
|
||||||
interface RefsInputInputProps
|
interface RefsInputInputProps
|
||||||
extends Pick<
|
extends Pick<
|
||||||
ReactCodeMirrorProps,
|
ReactCodeMirrorProps,
|
||||||
|
@ -199,32 +227,3 @@ export const RefsInput = forwardRef<ReactCodeMirrorRef, RefsInputInputProps>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// ======= Internal ==========
|
|
||||||
const editorSetup: BasicSetupOptions = {
|
|
||||||
highlightSpecialChars: false,
|
|
||||||
history: true,
|
|
||||||
drawSelection: false,
|
|
||||||
syntaxHighlighting: false,
|
|
||||||
defaultKeymap: true,
|
|
||||||
historyKeymap: true,
|
|
||||||
|
|
||||||
lineNumbers: false,
|
|
||||||
highlightActiveLineGutter: false,
|
|
||||||
foldGutter: false,
|
|
||||||
dropCursor: true,
|
|
||||||
allowMultipleSelections: false,
|
|
||||||
indentOnInput: false,
|
|
||||||
bracketMatching: false,
|
|
||||||
closeBrackets: false,
|
|
||||||
autocompletion: false,
|
|
||||||
rectangularSelection: false,
|
|
||||||
crosshairCursor: false,
|
|
||||||
highlightActiveLine: false,
|
|
||||||
highlightSelectionMatches: false,
|
|
||||||
closeBracketsKeymap: false,
|
|
||||||
searchKeymap: false,
|
|
||||||
foldKeymap: false,
|
|
||||||
completionKeymap: false,
|
|
||||||
lintKeymap: false
|
|
||||||
};
|
|
||||||
|
|
|
@ -26,6 +26,34 @@ import { RSLanguage } from './rslang';
|
||||||
import { getSymbolSubstitute, RSTextWrapper } from './text-editing';
|
import { getSymbolSubstitute, RSTextWrapper } from './text-editing';
|
||||||
import { rsHoverTooltip } from './tooltip';
|
import { rsHoverTooltip } from './tooltip';
|
||||||
|
|
||||||
|
const editorSetup: BasicSetupOptions = {
|
||||||
|
highlightSpecialChars: false,
|
||||||
|
history: true,
|
||||||
|
drawSelection: false,
|
||||||
|
syntaxHighlighting: false,
|
||||||
|
defaultKeymap: true,
|
||||||
|
historyKeymap: true,
|
||||||
|
|
||||||
|
lineNumbers: false,
|
||||||
|
highlightActiveLineGutter: false,
|
||||||
|
foldGutter: false,
|
||||||
|
dropCursor: true,
|
||||||
|
allowMultipleSelections: false,
|
||||||
|
indentOnInput: false,
|
||||||
|
bracketMatching: false,
|
||||||
|
closeBrackets: false,
|
||||||
|
autocompletion: false,
|
||||||
|
rectangularSelection: false,
|
||||||
|
crosshairCursor: false,
|
||||||
|
highlightActiveLine: false,
|
||||||
|
highlightSelectionMatches: false,
|
||||||
|
closeBracketsKeymap: false,
|
||||||
|
searchKeymap: false,
|
||||||
|
foldKeymap: false,
|
||||||
|
completionKeymap: false,
|
||||||
|
lintKeymap: false
|
||||||
|
};
|
||||||
|
|
||||||
interface RSInputProps
|
interface RSInputProps
|
||||||
extends Pick<
|
extends Pick<
|
||||||
ReactCodeMirrorProps,
|
ReactCodeMirrorProps,
|
||||||
|
@ -63,7 +91,6 @@ export const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
||||||
className,
|
className,
|
||||||
style,
|
style,
|
||||||
|
|
||||||
onChange,
|
|
||||||
onAnalyze,
|
onAnalyze,
|
||||||
...restProps
|
...restProps
|
||||||
},
|
},
|
||||||
|
@ -169,7 +196,6 @@ export const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
||||||
theme={customTheme}
|
theme={customTheme}
|
||||||
extensions={editorExtensions}
|
extensions={editorExtensions}
|
||||||
indentWithTab={false}
|
indentWithTab={false}
|
||||||
onChange={onChange}
|
|
||||||
editable={!disabled}
|
editable={!disabled}
|
||||||
onKeyDown={handleInput}
|
onKeyDown={handleInput}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
@ -178,32 +204,3 @@ export const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// ======= Internal ==========
|
|
||||||
const editorSetup: BasicSetupOptions = {
|
|
||||||
highlightSpecialChars: false,
|
|
||||||
history: true,
|
|
||||||
drawSelection: false,
|
|
||||||
syntaxHighlighting: false,
|
|
||||||
defaultKeymap: true,
|
|
||||||
historyKeymap: true,
|
|
||||||
|
|
||||||
lineNumbers: false,
|
|
||||||
highlightActiveLineGutter: false,
|
|
||||||
foldGutter: false,
|
|
||||||
dropCursor: true,
|
|
||||||
allowMultipleSelections: false,
|
|
||||||
indentOnInput: false,
|
|
||||||
bracketMatching: false,
|
|
||||||
closeBrackets: false,
|
|
||||||
autocompletion: false,
|
|
||||||
rectangularSelection: false,
|
|
||||||
crosshairCursor: false,
|
|
||||||
highlightActiveLine: false,
|
|
||||||
highlightSelectionMatches: false,
|
|
||||||
closeBracketsKeymap: false,
|
|
||||||
searchKeymap: false,
|
|
||||||
foldKeymap: false,
|
|
||||||
completionKeymap: false,
|
|
||||||
lintKeymap: false
|
|
||||||
};
|
|
||||||
|
|
|
@ -64,6 +64,16 @@
|
||||||
padding: 0.15rem 0.375rem;
|
padding: 0.15rem 0.375rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cm-completionInfo {
|
||||||
|
user-select: none;
|
||||||
|
cursor: default;
|
||||||
|
background-color: var(--color-input) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-tooltip-autocomplete ul li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
.cm-editor.cm-focused {
|
.cm-editor.cm-focused {
|
||||||
border-color: var(--color-primary);
|
border-color: var(--color-primary);
|
||||||
outline: 2px solid var(--color-primary);
|
outline: 2px solid var(--color-primary);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user