F: Implementing PromptEdit pt3
This commit is contained in:
parent
56652095af
commit
56f1bcacad
|
@ -48,6 +48,7 @@ export { LuFolderOpen as IconFolderOpened } from 'react-icons/lu';
|
||||||
export { LuFolderClosed as IconFolderClosed } from 'react-icons/lu';
|
export { LuFolderClosed as IconFolderClosed } from 'react-icons/lu';
|
||||||
export { LuFolderDot as IconFolderEmpty } from 'react-icons/lu';
|
export { LuFolderDot as IconFolderEmpty } from 'react-icons/lu';
|
||||||
export { TbHelpOctagon as IconHelp } from 'react-icons/tb';
|
export { TbHelpOctagon as IconHelp } from 'react-icons/tb';
|
||||||
|
export { LuPresentation as IconSample } from 'react-icons/lu';
|
||||||
export { LuLightbulbOff as IconHelpOff } from 'react-icons/lu';
|
export { LuLightbulbOff as IconHelpOff } from 'react-icons/lu';
|
||||||
export { RiPushpinFill as IconPin } from 'react-icons/ri';
|
export { RiPushpinFill as IconPin } from 'react-icons/ri';
|
||||||
export { RiUnpinLine as IconUnpin } from 'react-icons/ri';
|
export { RiUnpinLine as IconUnpin } from 'react-icons/ri';
|
||||||
|
|
|
@ -3,11 +3,26 @@ import { PromptVariableType } from './models/prompting';
|
||||||
const describePromptVariableRecord: Record<PromptVariableType, string> = {
|
const describePromptVariableRecord: Record<PromptVariableType, string> = {
|
||||||
[PromptVariableType.BLOCK]: 'Текущий блок операционной схемы',
|
[PromptVariableType.BLOCK]: 'Текущий блок операционной схемы',
|
||||||
[PromptVariableType.OSS]: 'Текущая операционная схема',
|
[PromptVariableType.OSS]: 'Текущая операционная схема',
|
||||||
[PromptVariableType.SCHEMA]: 'Текущая концептуальный схема',
|
[PromptVariableType.SCHEMA]: 'Текущая концептуальная схема',
|
||||||
[PromptVariableType.CONSTITUENTA]: 'Текущая конституента'
|
[PromptVariableType.CONSTITUENTA]: 'Текущая конституента'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockPromptVariableRecord: Record<PromptVariableType, string> = {
|
||||||
|
[PromptVariableType.BLOCK]: 'Пример: Текущий блок операционной схемы',
|
||||||
|
[PromptVariableType.OSS]: 'Пример: Текущая операционная схема',
|
||||||
|
[PromptVariableType.SCHEMA]: 'Пример: Текущая концептуальная схема',
|
||||||
|
[PromptVariableType.CONSTITUENTA]: 'Пример: Текущая конституента'
|
||||||
|
};
|
||||||
|
|
||||||
/** Retrieves description for {@link PromptVariableType}. */
|
/** Retrieves description for {@link PromptVariableType}. */
|
||||||
export function describePromptVariable(itemType: PromptVariableType): string {
|
export function describePromptVariable(itemType: PromptVariableType): string {
|
||||||
return describePromptVariableRecord[itemType] ?? `UNKNOWN VARIABLE TYPE: ${itemType}`;
|
return describePromptVariableRecord[itemType] ?? `UNKNOWN VARIABLE TYPE: ${itemType}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Retrieves mock text for {@link PromptVariableType}. */
|
||||||
|
export function mockPromptVariable(variable: string): string {
|
||||||
|
if (!Object.values(PromptVariableType).includes(variable as PromptVariableType)) {
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
return mockPromptVariableRecord[variable as PromptVariableType] ?? `UNKNOWN VARIABLE: ${variable}`;
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { mockPromptVariable } from '../labels';
|
||||||
|
|
||||||
/** Extracts a list of variables (as string[]) from a target string.
|
/** Extracts a list of variables (as string[]) from a target string.
|
||||||
* Note: Variables are wrapped in {{...}} and can include a-zA-Z, hyphen, and dot inside curly braces.
|
* Note: Variables are wrapped in {{...}} and can include a-zA-Z, hyphen, and dot inside curly braces.
|
||||||
* */
|
* */
|
||||||
|
@ -10,3 +12,18 @@ export function extractPromptVariables(target: string): string[] {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Generates a sample text from a target templates. */
|
||||||
|
export function generateSample(target: string): string {
|
||||||
|
const variables = extractPromptVariables(target);
|
||||||
|
if (variables.length === 0) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
let result = target;
|
||||||
|
for (const variable of variables) {
|
||||||
|
const mockText = mockPromptVariable(variable);
|
||||||
|
const escapedVar = variable.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
result = result.replace(new RegExp(`\\{\\{${escapedVar}\\}\\}`, 'g'), mockText);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -1,18 +1,23 @@
|
||||||
'use no memo'; // TODO: remove when react hook forms are compliant with react compiler
|
'use no memo'; // TODO: remove when react hook forms are compliant with react compiler
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useRef } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm, useWatch } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import { useDebounce } from 'use-debounce';
|
||||||
|
|
||||||
import { useMutatingPrompts } from '@/features/ai/backend/use-mutating-prompts';
|
import { useMutatingPrompts } from '@/features/ai/backend/use-mutating-prompts';
|
||||||
import { useUpdatePromptTemplate } from '@/features/ai/backend/use-update-prompt-template';
|
import { useUpdatePromptTemplate } from '@/features/ai/backend/use-update-prompt-template';
|
||||||
|
import { generateSample } from '@/features/ai/models/prompting-api';
|
||||||
import { useAuthSuspense } from '@/features/auth';
|
import { useAuthSuspense } from '@/features/auth';
|
||||||
|
|
||||||
|
import { MiniButton } from '@/components/control';
|
||||||
|
import { IconSample } from '@/components/icons';
|
||||||
import { Checkbox, TextArea, TextInput } from '@/components/input';
|
import { Checkbox, TextArea, TextInput } from '@/components/input';
|
||||||
import { cn } from '@/components/utils';
|
import { cn } from '@/components/utils';
|
||||||
import { useModificationStore } from '@/stores/modification';
|
import { useModificationStore } from '@/stores/modification';
|
||||||
import { globalIDs } from '@/utils/constants';
|
import { globalIDs, PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type IPromptTemplate,
|
type IPromptTemplate,
|
||||||
|
@ -33,6 +38,8 @@ export function FormPromptTemplate({ promptTemplate, className, isMutable, toggl
|
||||||
const isProcessing = useMutatingPrompts();
|
const isProcessing = useMutatingPrompts();
|
||||||
const setIsModified = useModificationStore(state => state.setIsModified);
|
const setIsModified = useModificationStore(state => state.setIsModified);
|
||||||
const { updatePromptTemplate } = useUpdatePromptTemplate();
|
const { updatePromptTemplate } = useUpdatePromptTemplate();
|
||||||
|
const [sampleResult, setSampleResult] = useState<string | null>(null);
|
||||||
|
const [debouncedResult] = useDebounce(sampleResult, PARAMETER.moveDuration);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
|
@ -50,6 +57,7 @@ export function FormPromptTemplate({ promptTemplate, className, isMutable, toggl
|
||||||
is_shared: promptTemplate.is_shared
|
is_shared: promptTemplate.is_shared
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const text = useWatch({ control, name: 'text' });
|
||||||
|
|
||||||
const prevReset = useRef(toggleReset);
|
const prevReset = useRef(toggleReset);
|
||||||
const prevTemplate = useRef(promptTemplate);
|
const prevTemplate = useRef(promptTemplate);
|
||||||
|
@ -63,6 +71,7 @@ export function FormPromptTemplate({ promptTemplate, className, isMutable, toggl
|
||||||
text: promptTemplate.text,
|
text: promptTemplate.text,
|
||||||
is_shared: promptTemplate.is_shared
|
is_shared: promptTemplate.is_shared
|
||||||
});
|
});
|
||||||
|
setSampleResult(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
const prevDirty = useRef(isDirty);
|
const prevDirty = useRef(isDirty);
|
||||||
|
@ -81,7 +90,7 @@ export function FormPromptTemplate({ promptTemplate, className, isMutable, toggl
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
id={globalIDs.prompt_editor}
|
id={globalIDs.prompt_editor}
|
||||||
className={cn('flex flex-col gap-3 px-6', className)}
|
className={cn('flex flex-col gap-3 px-6 py-2', className)}
|
||||||
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
||||||
>
|
>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
@ -101,11 +110,13 @@ export function FormPromptTemplate({ promptTemplate, className, isMutable, toggl
|
||||||
<TextArea
|
<TextArea
|
||||||
id='prompt_text'
|
id='prompt_text'
|
||||||
label='Содержание' //
|
label='Содержание' //
|
||||||
|
fitContent
|
||||||
|
className='disabled:min-h-9 max-h-64'
|
||||||
{...register('text')}
|
{...register('text')}
|
||||||
error={errors.text}
|
error={errors.text}
|
||||||
disabled={isProcessing || !isMutable}
|
disabled={isProcessing || !isMutable}
|
||||||
/>
|
/>
|
||||||
|
<div className='flex justify-between'>
|
||||||
<Controller
|
<Controller
|
||||||
name='is_shared'
|
name='is_shared'
|
||||||
control={control}
|
control={control}
|
||||||
|
@ -121,6 +132,22 @@ export function FormPromptTemplate({ promptTemplate, className, isMutable, toggl
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<MiniButton
|
||||||
|
title='Сгенерировать пример запроса'
|
||||||
|
icon={<IconSample size='1.25rem' className='icon-primary' />}
|
||||||
|
onClick={() => setSampleResult(!!sampleResult ? null : generateSample(text))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={clsx('cc-prompt-result overflow-y-hidden', sampleResult !== null && 'open')}>
|
||||||
|
<TextArea
|
||||||
|
fitContent
|
||||||
|
className='mt-3 max-h-64 min-h-12'
|
||||||
|
label='Пример запроса'
|
||||||
|
value={sampleResult ?? debouncedResult ?? ''}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ export function TabEditTemplate({ activeID }: TabEditTemplateProps) {
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<FormPromptTemplate
|
<FormPromptTemplate
|
||||||
className='mt-12 xs:mt-4 w-100 md:w-180 min-w-70'
|
className='mt-8 xs:mt-0 w-100 md:w-180 min-w-70'
|
||||||
isMutable={isMutable}
|
isMutable={isMutable}
|
||||||
promptTemplate={promptTemplate}
|
promptTemplate={promptTemplate}
|
||||||
toggleReset={toggleReset}
|
toggleReset={toggleReset}
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
import { describePromptVariable } from '../../labels';
|
||||||
|
import { PromptVariableType } from '../../models/prompting';
|
||||||
|
|
||||||
|
/** Displays all prompt variable types with their descriptions. */
|
||||||
export function TabViewVariables() {
|
export function TabViewVariables() {
|
||||||
return <div className='pt-8 border rounded'>View all variables</div>;
|
return (
|
||||||
|
<div className='pt-8'>
|
||||||
|
<ul className='space-y-1'>
|
||||||
|
{Object.values(PromptVariableType).map(variableType => (
|
||||||
|
<li key={variableType} className='flex flex-col'>
|
||||||
|
<span className='font-math text-primary'>{`{{${variableType}}}`}</span>
|
||||||
|
<span className='font-main text-muted-foreground'>{describePromptVariable(variableType)}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ export function SidePanel({ isMounted, className }: SidePanelProps) {
|
||||||
selectedItems.length === 1 && selectedItems[0].nodeType === NodeType.BLOCK ? selectedItems[0] : null;
|
selectedItems.length === 1 && selectedItems[0].nodeType === NodeType.BLOCK ? selectedItems[0] : null;
|
||||||
const selectedSchema = selectedOperation?.result ?? null;
|
const selectedSchema = selectedOperation?.result ?? null;
|
||||||
|
|
||||||
const debouncedMounted = useDebounce(isMounted, PARAMETER.moveDuration);
|
const [debouncedMounted] = useDebounce(isMounted, PARAMETER.moveDuration);
|
||||||
const closePanel = usePreferencesStore(state => state.toggleShowOssSidePanel);
|
const closePanel = usePreferencesStore(state => state.toggleShowOssSidePanel);
|
||||||
const sidePanelHeight = useMainHeight();
|
const sidePanelHeight = useMainHeight();
|
||||||
|
|
||||||
|
|
|
@ -221,6 +221,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@utility cc-prompt-result {
|
||||||
|
transition-property: clip-path, height;
|
||||||
|
transition-duration: var(--duration-move);
|
||||||
|
transition-timing-function: var(--ease-in-out);
|
||||||
|
|
||||||
|
clip-path: inset(0% 0% 100% 0%);
|
||||||
|
height: 0;
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
clip-path: inset(0% 0% 0% 0%);
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@utility cc-parsing-result {
|
@utility cc-parsing-result {
|
||||||
transition-property: clip-path, padding, margin, border, height;
|
transition-property: clip-path, padding, margin, border, height;
|
||||||
transition-duration: var(--duration-move);
|
transition-duration: var(--duration-move);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user