Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add status message below prompt editor #284

Merged
merged 13 commits into from
Jun 11, 2024
Merged
4 changes: 3 additions & 1 deletion web-src/src/components/GenerateButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { log } from '../helpers/MetricsHelper.js';
import { contentFragmentState } from '../state/ContentFragmentState.js';
import { RUN_MODE_CF } from '../state/RunMode.js';
import { FIREFALL_ACTION_TYPES } from '../services/FirefallService.js';
import { promptSyntaxErrorState } from '../state/PromptSyntaxErrorState.js';

const createWaitMessagesController = (intlFn) => {
const displayToast = (msg, timeout = 1500) => {
Expand Down Expand Up @@ -92,6 +93,7 @@ export function GenerateButton() {
const parameters = useRecoilValue(parametersState);
const contentFragment = useRecoilValue(contentFragmentState);
const temperature = useRecoilValue(temperatureState);
const syntaxError = useRecoilValue(promptSyntaxErrorState);

const setResults = useSetRecoilState(resultsState);
const setIsOpenPromptEditor = useSetRecoilState(promptEditorState);
Expand Down Expand Up @@ -154,7 +156,7 @@ export function GenerateButton() {
variant="cta"
style="fill"
onPress={handleGenerate}
isDisabled={generationInProgress}>
isDisabled={generationInProgress || syntaxError}>
slitviachenko marked this conversation as resolved.
Show resolved Hide resolved
{generationInProgress ? <ProgressCircle size="S" aria-label="Generate" isIndeterminate right="8px" /> : <GenAIIcon marginEnd={'8px'} color={'white'}/>}
{formatMessage(intlMessages.promptSessionSideView.generateButtonLabel)}
</Button>
Expand Down
53 changes: 51 additions & 2 deletions web-src/src/components/PromptEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ import { useRecoilState, useRecoilValue } from 'recoil';
import { css, injectGlobal } from '@emotion/css';
import { Global } from '@emotion/react';
import { motion, AnimatePresence } from 'framer-motion';
import CheckmarkCircle from '@spectrum-icons/workflow/CheckmarkCircle';
import Close from '@spectrum-icons/workflow/Close';
import Alert from '@spectrum-icons/workflow/Alert';
import { useIntl } from 'react-intl';

import { intlMessages } from './PromptSessionSideView.l10n.js';

import { parametersState } from '../state/ParametersState.js';
import { promptState } from '../state/PromptState.js';
import { promptSyntaxErrorState } from '../state/PromptSyntaxErrorState.js';
import { NO_VALUE_STRING, renderPrompt } from '../helpers/PromptRenderer.js';
import { log } from '../helpers/MetricsHelper.js';

Expand Down Expand Up @@ -75,6 +78,15 @@ const style = {
border-radius: 4px;
padding: 12px;
`,
containerError: css`
width: 100%;
height: 100%;
position: relative;
overflow: auto;
border: 1px solid var(--spectrum-red-900);
border-radius: 4px;
padding: 12px;
`,
editor: css`
font-family: Monospaced, monospace;
font-size: 12px;
Expand All @@ -86,24 +98,45 @@ const style = {
textarea: css`
outline: none;
`,
errorHelpText: css`
margin-top: 15px;
color: var(--spectrum-red-900);
`,
validHelpText: css`
margin-top: 15px;
color: var(--spectrum-green-700);
`,
hidden: css`
display: none;
`,
};

function PromptEditor({ isOpen, onClose, ...props }) {
const [prompt, setPrompt] = useRecoilState(promptState);
const [promptText, setPromptText] = useState(prompt);
const [viewSource, setViewSource] = useState(false);
const [editorSyntaxError, setEditorSyntaxError] = useRecoilState(promptSyntaxErrorState);

const parameters = useRecoilValue(parametersState);
const contentFragment = useRecoilValue(contentFragmentState);

const { formatMessage } = useIntl();

useEffect(() => {
setPrompt(promptText);
const promptEditorTextArea = document.getElementById('promptEditorTextArea');
if (promptEditorTextArea) {
promptEditorTextArea.setAttribute('title', 'Prompt Editor');
}

// detect unsupported characters in editor
const regex = /=".*[{}"]+.*"/g;
const matches = [...promptText.matchAll(regex)];

if (matches.length > 0) {
setEditorSyntaxError(true);
} else {
setEditorSyntaxError(false);
}
}, [promptText, setPrompt]);

useEffect(() => {
Expand Down Expand Up @@ -181,7 +214,7 @@ function PromptEditor({ isOpen, onClose, ...props }) {
</Flex>
</Flex>

<div className={style.container}>
<div className={editorSyntaxError ? style.containerError : style.container}>
<SimpleEditor
className={style.editor}
textareaClassName={style.textarea}
Expand All @@ -196,6 +229,22 @@ function PromptEditor({ isOpen, onClose, ...props }) {
readOnly={!viewSource}
/>
</div>

<Flex gap="size-100" UNSAFE_className={editorSyntaxError ? style.hidden : style.validHelpText }>
<CheckmarkCircle aria-label="Positive Alert" color="positive" />
<Text>
Prompt is valid
</Text>
</Flex>

<Flex gap="size-100" UNSAFE_className={editorSyntaxError ? style.errorHelpText : style.hidden}>
<Alert aria-label="Negative Alert" color="negative" />
<Text>
The characters <span style={{ fontWeight: '600' }}>&#123;</span>, <span style={{ fontWeight: '600' }}>&#125;</span>,
and <span style={{ fontWeight: '600' }}>&quot;</span> are reserved and can&apos;t be used within quoted text values.
Please remove or replace these characters and try again.
</Text>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Localizable text. It's a good opportunity to localize a very complex text string.
Please refer to this example:

{formatMessage(intlMessages.app.consentDialogContent, {

</Flex>
</motion.div>
)}
</AnimatePresence>
Expand Down
18 changes: 18 additions & 0 deletions web-src/src/state/PromptSyntaxErrorState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright 2023 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import { atom } from 'recoil';

export const promptSyntaxErrorState = atom({
key: 'promptSyntaxErrorState',
default: false,
});
Loading