| @@ -1,62 +1,58 @@ | ||
| 1 | -import { AnimateChunks } from '@agent/components/messages/AnimateChunks'; | |
| 2 | 1 | import { useStatusStore } from '@agent/state/status'; |
| 3 | 2 | import { useEffect, useMemo, useState } from '@wordpress/element'; |
| 4 | 3 | import { decodeEntities } from '@wordpress/html-entities'; |
| 5 | 4 | import { __ } from '@wordpress/i18n'; |
| 6 | -import classNames from 'classnames'; | |
| 7 | 5 | |
| 8 | -const canAnimateTypes = ['calling-agent', 'agent-working', 'tool-started']; | |
| 6 | +const linesFor = (label) => ({ | |
| 7 | + 'calling-agent': __('Thinking...', 'extendify-local'), | |
| 8 | + 'agent-working': [ | |
| 9 | + __('Working on it...', 'extendify-local'), | |
| 10 | + __('Interpreting message...', 'extendify-local'), | |
| 11 | + __('Formulating a response...', 'extendify-local'), | |
| 12 | + __('Reviewing logic...', 'extendify-local'), | |
| 13 | + ], | |
| 14 | + 'workflow-tool-processing': __('Processing...', 'extendify-local'), | |
| 15 | + 'tool-started': label || __('Gathering data...', 'extendify-local'), | |
| 16 | + 'credits-exhausted': __('Usage limit reached', 'extendify-local'), | |
| 17 | + 'credits-restored': __('Usage limit restored', 'extendify-local'), | |
| 18 | +}); | |
| 9 | 19 | |
| 10 | -export const StatusIndicator = () => { | |
| 11 | - const status = useStatusStore((s) => s.statuses.at(-1)); | |
| 12 | - const { type, label } = status ?? {}; | |
| 20 | +// Randomized 3-5s beat so a long wait never looks stuck or syncs across sites. | |
| 21 | +const useCurrentLine = (lines) => { | |
| 13 | 22 | const [index, setIndex] = useState(0); |
| 14 | - const statusContent = useMemo( | |
| 15 | - () => ({ | |
| 16 | - 'calling-agent': __('Thinking...', 'extendify-local'), | |
| 17 | - 'agent-working': [ | |
| 18 | - __('Working on it...', 'extendify-local'), | |
| 19 | - __('Interpreting message...', 'extendify-local'), | |
| 20 | - __('Formulating a response...', 'extendify-local'), | |
| 21 | - __('Reviewing logic...', 'extendify-local'), | |
| 22 | - ], | |
| 23 | - 'workflow-tool-processing': __('Processing...', 'extendify-local'), | |
| 24 | - 'tool-started': label || __('Gathering data...', 'extendify-local'), | |
| 25 | - 'tool-completed': label || __('Analyzing...', 'extendify-local'), | |
| 26 | - 'credits-exhausted': __('Usage limit reached', 'extendify-local'), | |
| 27 | - 'credits-restored': __('Usage limit restored', 'extendify-local'), | |
| 28 | - }), | |
| 29 | - [label], | |
| 30 | - ); | |
| 31 | - const content = statusContent[type]; | |
| 32 | - const isList = Array.isArray(content); | |
| 23 | + const isList = Array.isArray(lines); | |
| 33 | 24 | |
| 34 | - useEffect(() => setIndex(0), [type]); | |
| 25 | + useEffect(() => setIndex(0), [lines]); | |
| 35 | 26 | |
| 36 | 27 | useEffect(() => { |
| 37 | 28 | if (!isList) return; |
| 38 | - // Cycle verbs on a randomized 3-5s beat so it never looks stuck. | |
| 39 | 29 | const timer = setTimeout( |
| 40 | - () => setIndex((i) => (i + 1) % content.length), | |
| 30 | + () => setIndex((i) => (i + 1) % lines.length), | |
| 41 | 31 | 3000 + Math.random() * 2000, |
| 42 | 32 | ); |
| 43 | 33 | return () => clearTimeout(timer); |
| 44 | - }, [isList, content, index]); | |
| 34 | + }, [isList, lines, index]); | |
| 45 | 35 | |
| 46 | - if (!type || !content) return null; | |
| 47 | - const text = isList ? content[index] : content; | |
| 36 | + return isList ? lines[index] : lines; | |
| 37 | +}; | |
| 48 | 38 | |
| 39 | +export const StatusIndicator = () => { | |
| 40 | + const leavingPage = useStatusStore((s) => s.leavingPage); | |
| 41 | + const { type, label } = useStatusStore((s) => s.statuses.at(-1)) ?? {}; | |
| 42 | + const lines = useMemo(() => linesFor(label)[type], [label, type]); | |
| 43 | + const currentLine = useCurrentLine(lines); | |
| 44 | + const text = leavingPage | |
| 45 | + ? __('Refreshing the page to show your changes…', 'extendify-local') | |
| 46 | + : currentLine; | |
| 47 | + | |
| 48 | + if (!text) return null; | |
| 49 | + | |
| 49 | 50 | return ( |
| 50 | - <div | |
| 51 | - className={classNames('p-2 text-center text-xs italic text-gray-700', { | |
| 52 | - 'status-animation': canAnimateTypes.includes(type), | |
| 53 | - })} | |
| 54 | - > | |
| 55 | - <AnimateChunks | |
| 56 | - key={`${type}-${index}`} | |
| 57 | - words={decodeEntities(text).split('')} | |
| 58 | - delay={0.02} | |
| 59 | - /> | |
| 51 | + <div className="p-2 text-center text-xs italic text-gray-700"> | |
| 52 | + {/* Reusing the node drops the new line mid-sweep. */} | |
| 53 | + <span key={text} className="status-animation"> | |
| 54 | + {decodeEntities(text)} | |
| 55 | + </span> | |
| 60 | 56 | </div> |
| 61 | 57 | ); |
| 62 | 58 | }; |