diff --git a/src/components/ArticleV3/ArticleCellExplainCodeButton.css b/src/components/ArticleV3/ArticleCellExplainCodeButton.css index b8d385c8..68f834dc 100644 --- a/src/components/ArticleV3/ArticleCellExplainCodeButton.css +++ b/src/components/ArticleV3/ArticleCellExplainCodeButton.css @@ -1,6 +1,7 @@ .ArticleCellExplainCodeButton { --bs-btn-disabled-opacity: 0.8; - --status-error: red; + --status-error: tomato; + --status-error-alpha: rgba(255, 99, 71, 0.2); --status-success: green; color: var(--white); } @@ -45,10 +46,16 @@ display: block; } -/* .ArticleCellExplainCodeButton.executing .ArticleCellExplainCodeButton__iconWrapper::after { - border: 2px solid var(--white); - animation: ArticleCellExplainCodeButton__rotate 2s linear infinite; -} */ +.ArticleCellExplainCodeButton.error button { + color: var(--status-error); + border-color: var(--status-error) !important; +} +.ArticleCellExplainCodeButton.error button:hover { + + border-color: var(--status-error) !important; + background-color: var(--status-error-alpha); +} + .ArticleCellExplainCodeButton.success .ArticleCellExplainCodeButton__iconWrapper::after { border: 2px solid var(--primary); } @@ -57,6 +64,15 @@ color: var(--primary); } +.ArticleCellExplainCodeButton.error .ArticleCellExplainCodeButton__iconWrapper { + color: var(--status-error); +} + +.ArticleCellExplainCodeButton.error .ArticleCellExplainCodeButton__iconWrapper::after { + border: 2px solid var(--status-error); +} + + @keyframes ArticleCellExplainCodeButton__rotate { 0% { transform: translate(-50%, -50%) rotate(0deg); diff --git a/src/components/ArticleV3/ArticleCellExplainCodeButton.js b/src/components/ArticleV3/ArticleCellExplainCodeButton.js index 860bc2c6..cce6a56d 100644 --- a/src/components/ArticleV3/ArticleCellExplainCodeButton.js +++ b/src/components/ArticleV3/ArticleCellExplainCodeButton.js @@ -1,4 +1,4 @@ -import { BrainWarning, ElectronicsChip, MagicWand, PauseSolid } from 'iconoir-react' +import { BrainWarning, ElectronicsChip, PauseSolid } from 'iconoir-react' import React from 'react' import './ArticleCellExplainCodeButton.css' import CircularLoading from '../CircularLoading' @@ -17,7 +17,7 @@ export const StatusBeforeExecuting = 'beforeExecuting' export const AvailableStatuses = [StatusIdle, StatusExecuting, StatusSuccess, StatusError] const StatusIcons = { - [StatusIdle]: MagicWand, + [StatusIdle]: ElectronicsChip, [StatusExecuting]: PauseSolid, [StatusSuccess]: ElectronicsChip, [StatusError]: BrainWarning, @@ -42,7 +42,7 @@ const ArticleCellExplainCodeButton = ({
diff --git a/src/components/ArticleV3/ArticleCellExplainer.css b/src/components/ArticleV3/ArticleCellExplainer.css index cb2908da..e7353bba 100644 --- a/src/components/ArticleV3/ArticleCellExplainer.css +++ b/src/components/ArticleV3/ArticleCellExplainer.css @@ -2,3 +2,15 @@ overflow: hidden; height: 0; } + +.ArticleCellExplainer__messages { + margin-top: var(--spacer-2); + padding: var(--spacer-1) var(--spacer-2); + border-radius: 5px; + max-height: 600px; +} +.ArticleCellExplainer__messages.error { + background: #ff634755; + color: tomato; + position: relative; +} diff --git a/src/components/ArticleV3/ArticleCellExplainer.js b/src/components/ArticleV3/ArticleCellExplainer.js index 47811f55..6252c215 100644 --- a/src/components/ArticleV3/ArticleCellExplainer.js +++ b/src/components/ArticleV3/ArticleCellExplainer.js @@ -11,37 +11,51 @@ import './ArticleCellExplainer.css' import { useArticleCellExplainerStore } from '../../store' import { useMutation } from '@tanstack/react-query' +import { useTranslation } from 'react-i18next' + +console.info( + '%cEnable Code Explainer', + 'font-weight: bold', + process.env.REACT_APP_ENABLE_CODE_EXPLAINER === 'true', +) const ArticleCellExplainer = ({ source = '', cellIdx = '', className = '' }) => { + const { t } = useTranslation() const messagesRef = useRef(null) const resultRef = useRef(null) const [styles, api] = useSpring(() => ({ height: 0, + opacity: 0, })) const [free, lock] = useArticleCellExplainerStore((state) => [state.free, state.lock]) const currentCellIdx = useArticleCellExplainerStore((state) => state.cellIdx) - const { status, mutate, data, error } = useMutation({ + const { status, mutate } = useMutation({ mutationFn: (code) => { - console.info('[ArticleCellExplainer] mutationFn', code) - return axios - .post('/api/explain', { - code, - }) - .catch((error) => { - console.error('[ArticleCellExplainer] mutationFn error', error) - api.start({ - height: messagesRef.current.scrollHeight, - }) - free() - throw error - }) + console.info('[ArticleCellExplainer] mutationFn n.chars:', code.length) + return axios.post('/api/explain', { + code, + }) }, onSuccess: (res) => { console.info('[ArticleCellExplainer] mutation success', res) - resultRef.current.innerHTML = JSON.stringify(res.data.result, null, 2) + const messages = res.data.result.choices.map((choice) => choice.content).join('
') + resultRef.current.innerHTML = messages api.start({ height: messagesRef.current.scrollHeight, + opacity: 1, + }) + free() + return res + }, + onError: (error) => { + console.error('[ArticleCellExplainer] mutation error', error) + resultRef.current.innerHTML = t('errors.explainCodeNotAvailable', { + error: error.message, + }) + api.start({ + height: messagesRef.current.scrollHeight, + opacity: 1, }) free() }, @@ -52,10 +66,12 @@ const ArticleCellExplainer = ({ source = '', cellIdx = '', className = '' }) => lock(cellIdx) let codeToExplain = Array.isArray(source) ? source.join('\n') : source // reduce the code To Explain Length to max 500 chars + codeToExplain = codeToExplain.trim().replace(/\n/g, ' ').replace(/\s+/g, ' ') if (codeToExplain.length > 1000) { codeToExplain = codeToExplain.slice(0, 1000) console.debug('[ArticleCellExplainer] codeToExplain shortened to 1000 chars') } + mutate(codeToExplain) } @@ -63,19 +79,20 @@ const ArticleCellExplainer = ({ source = '', cellIdx = '', className = '' }) => free() api.start({ height: 0, + opacity: 0, }) } // display Status let displayStatus = StatusIdle if (status === 'success') { displayStatus = StatusSuccess - } else if (status === 'loading') { + } else if (status === 'pending') { displayStatus = StatusExecuting } else if (status === 'error') { displayStatus = StatusError } const disabled = currentCellIdx !== null - const cellSource = Array.isArray(source) ? source.join('\n') : source + // const cellSource = Array.isArray(source) ? source.join('\n') : source return (
@@ -92,20 +109,13 @@ const ArticleCellExplainer = ({ source = '', cellIdx = '', className = '' }) => (close)
- -
- axios: {status} -
{JSON.stringify({ data, error })}
-

Explainer

- {cellSource.length} characters -
{cellSource}
-

- This code cell is an explainer cell. It is used to provide context and explanation for the - code cell above it. -

-

- You can edit the content of this cell by clicking the pencil icon in the top right corner. -

+ +
)