From a2cfa5d010b49b923a927fe1947a9833f52a00a8 Mon Sep 17 00:00:00 2001 From: Junyi Hou Date: Thu, 19 Feb 2026 01:24:45 +0800 Subject: [PATCH] refactor: improve state management and accessibility in components - Updated `LoadingIndicator` to use `useReducer` for better state handling. - Refactored `OnboardingGuide` to utilize `useReducer` for image loading state. - Enhanced accessibility by adding `role` and `tabIndex` attributes to various interactive elements across components. - Simplified state updates in `ChatHistoryModal` and `LoginWithOverleaf` by consolidating state management. - Improved performance in `CodeBlock` by using `useMemo` for highlighted code. - General code cleanup and consistency improvements across multiple components. --- webapp/_webapp/src/components/code-block.tsx | 8 +-- .../src/components/loading-indicator.tsx | 42 ++++++++++--- .../message-entry-container/assistant.tsx | 6 +- .../message-entry-container/tools/general.tsx | 2 +- .../paper-score-comment/filter-controls.tsx | 3 + .../message-entry-container/tools/tools.tsx | 2 +- .../tools/xtramcp/generate-citations.tsx | 6 ++ .../tools/xtramcp/online-search-papers.tsx | 6 ++ .../tools/xtramcp/review-paper.tsx | 16 +++-- .../tools/xtramcp/search-relevant-papers.tsx | 6 ++ .../tools/xtramcp/verify-citations.tsx | 6 ++ .../tools/xtramcp/xtramcp-generic-card.tsx | 9 +++ .../src/components/onboarding-guide.tsx | 61 +++++++++++++------ webapp/_webapp/src/components/tabs.tsx | 2 +- .../_webapp/src/components/text-patches.tsx | 7 ++- webapp/_webapp/src/main.tsx | 26 ++------ webapp/_webapp/src/stores/selection-store.ts | 4 ++ webapp/_webapp/src/views/chat/body/index.tsx | 21 +++++++ .../views/chat/footer/toolbar/selection.tsx | 48 +++++++++++---- .../src/views/chat/header/chat-button.tsx | 9 +++ .../views/chat/header/chat-history-modal.tsx | 33 +++++----- webapp/_webapp/src/views/devtools/index.tsx | 3 +- webapp/_webapp/src/views/embed-sidebar.tsx | 2 +- .../src/views/login/advanced-settings.tsx | 2 +- webapp/_webapp/src/views/login/index.tsx | 3 + .../src/views/login/login-with-overleaf.tsx | 6 +- webapp/_webapp/src/views/office/app.tsx | 17 +++--- .../views/prompts/project-instructions.tsx | 2 +- .../src/views/prompts/user-instructions.tsx | 2 +- .../src/views/settings/sections/footer.tsx | 21 +++++++ .../views/settings/sections/ui-settings.tsx | 14 ++++- .../sections/user-developer-tools.tsx | 2 +- .../src/views/settings/setting-text-input.tsx | 2 - 33 files changed, 286 insertions(+), 113 deletions(-) diff --git a/webapp/_webapp/src/components/code-block.tsx b/webapp/_webapp/src/components/code-block.tsx index 16c71ec5..f237456d 100644 --- a/webapp/_webapp/src/components/code-block.tsx +++ b/webapp/_webapp/src/components/code-block.tsx @@ -3,7 +3,7 @@ import "highlight.js/styles/default.min.css"; import latex from "highlight.js/lib/languages/latex"; hljs.registerLanguage("latex", latex); -import { useState, useEffect } from "react"; +import { useMemo } from "react"; type CodeBlockProps = { code: string; @@ -11,11 +11,7 @@ type CodeBlockProps = { }; export const CodeBlock = ({ code, className }: CodeBlockProps) => { - const [highlightedCode, setHighlightedCode] = useState(code); - - useEffect(() => { - setHighlightedCode(hljs.highlight(code, { language: "latex" }).value); - }, [code]); + const highlightedCode = useMemo(() => hljs.highlight(code, { language: "latex" }).value, [code]); return (
 {
   // State
-  const [progress, setProgress] = useState(0);
-  const [phase, setPhase] = useState("green");
-  const [isTimeout, setIsTimeout] = useState(false);
+  const [{ progress, phase, isTimeout }, dispatch] = useReducer(indicatorReducer, {
+    progress: 0,
+    phase: "green",
+    isTimeout: false,
+  });
 
   // Handle progress animation
   useEffect(() => {
@@ -103,18 +129,18 @@ export const LoadingIndicator = ({ text = "Thinking", estimatedSeconds = 0, erro
         // we spend 100% of estimatedDuration in green,
         // 50% in orange, and 50% in red before warning.
         if (phase === "green") {
-          setPhase("orange");
+          dispatch({ type: "ADVANCE_PHASE", nextPhase: "orange" });
           currentProgress = 0;
         } else if (phase === "orange") {
-          setPhase("red");
+          dispatch({ type: "ADVANCE_PHASE", nextPhase: "red" });
           currentProgress = 0;
         } else if (phase === "red") {
-          setIsTimeout(true);
+          dispatch({ type: "SET_TIMEOUT" });
           return;
         }
       }
 
-      setProgress(currentProgress);
+      dispatch({ type: "SET_PROGRESS", progress: currentProgress });
 
       if (!isTimeout) {
         animationFrameId = requestAnimationFrame(updateProgress);
diff --git a/webapp/_webapp/src/components/message-entry-container/assistant.tsx b/webapp/_webapp/src/components/message-entry-container/assistant.tsx
index 47743da3..ca692d44 100644
--- a/webapp/_webapp/src/components/message-entry-container/assistant.tsx
+++ b/webapp/_webapp/src/components/message-entry-container/assistant.tsx
@@ -132,8 +132,8 @@ export const AssistantMessageContainer = ({
             )}
 
             {/* PaperDebugger blocks */}
-            {parsedMessage.paperDebuggerContent.map((content, index) => (
-              
+            {parsedMessage.paperDebuggerContent.map((content) => (
+              
                 {content}
               
             ))}
@@ -147,7 +147,7 @@ export const AssistantMessageContainer = ({
           {((parsedMessage.regularContent?.length || 0) > 0 || parsedMessage.paperDebuggerContent.length > 0) && (
             
- + { if (e.key === 'Enter' || e.key === ' ') { handleCopy(); } }} tabIndex={0} role="button" aria-label="Copy message"> diff --git a/webapp/_webapp/src/components/message-entry-container/tools/general.tsx b/webapp/_webapp/src/components/message-entry-container/tools/general.tsx index f07a1c3a..c381ff03 100644 --- a/webapp/_webapp/src/components/message-entry-container/tools/general.tsx +++ b/webapp/_webapp/src/components/message-entry-container/tools/general.tsx @@ -95,7 +95,7 @@ export const GeneralToolCard = ({ // When there is a message, show the compact card with collapsible content return (
-
+
{ if (e.key === 'Enter' || e.key === ' ') { toggleCollapse(); } }}>