From 2876da4072e1421a5a6865bd80f8f28dd3a30bd8 Mon Sep 17 00:00:00 2001 From: JOYCEQL <1449239013@qq.com> Date: Thu, 26 Feb 2026 01:48:53 +0800 Subject: [PATCH] refactor: remove redundant 'use client' directives from numerous components. --- src/app/app/dashboard/ai/page.tsx | 1 - src/app/app/dashboard/client.tsx | 1 - src/app/app/dashboard/resumes/page.tsx | 1 - src/app/app/dashboard/settings/page.tsx | 1 - src/app/app/dashboard/templates/page.tsx | 1 - src/app/app/workbench/[id]/page.tsx | 6 +- src/app/providers.tsx | 1 - src/components/editor/EditPanel.tsx | 1 - src/components/editor/EditorHeader.tsx | 1 - src/components/editor/Field.tsx | 1 - src/components/editor/SidePanel.tsx | 1 - src/components/editor/basic/BasicPanel.tsx | 1 - .../editor/education/EducationItem.tsx | 1 - .../editor/education/EducationPanel.tsx | 1 - .../editor/experience/ExperienceItem.tsx | 1 - .../editor/experience/ExperiencePanel.tsx | 1 - .../editor/grammar/GrammarCheckDrawer.tsx | 1 - src/components/editor/layout/LayoutItem.tsx | 1 - .../editor/layout/LayoutSetting.tsx | 1 - src/components/editor/project/ProjectItem.tsx | 1 - src/components/editor/skills/SkillPanel.tsx | 1 - src/components/home/FAQSection.tsx | 1 - src/components/home/FeaturesSection.tsx | 1 - src/components/home/GoDashboard.tsx | 1 - src/components/home/LandingHeader.tsx | 1 - .../home/client/AnimatedFeature.tsx | 1 - src/components/home/client/MenuToggle.tsx | 1 - src/components/home/client/MobileMenu.tsx | 1 - .../home/client/ScrollBackground.tsx | 1 - src/components/home/client/ScrollHeader.tsx | 1 - src/components/mobile/MobileWorkbench.tsx | 2 +- src/components/preview/CustomSection.tsx | 1 - src/components/preview/EducationSection.tsx | 1 - src/components/preview/ExperienceSection.tsx | 1 - .../preview/IframeTemplateViewer.tsx | 1 - src/components/preview/PreviewDock.tsx | 17 +++- src/components/preview/ProjectSection.tsx | 1 - src/components/preview/SectionTitle.tsx | 1 - src/components/preview/SkillPanel.tsx | 1 - src/components/preview/index.tsx | 1 - src/components/shared/GitHubStars.tsx | 1 - src/components/shared/GithubContribution.tsx | 1 - src/components/shared/LanguageSwitch.tsx | 1 - src/components/shared/Logo.tsx | 1 - src/components/shared/PdfExport.tsx | 1 - src/components/shared/PhotoConfigDrawer.tsx | 1 - src/components/shared/PhotoSelector.tsx | 1 - src/components/shared/ScrollToTop.tsx | 1 - src/components/shared/TemplateSheet.tsx | 1 - src/components/shared/ThemeModal.tsx | 1 - src/components/shared/ThemeToggle.tsx | 1 - src/components/shared/ai/AIPolishDialog.tsx | 1 - .../shared/rich-editor/RichEditor.tsx | 1 - src/components/ui/accordion.tsx | 1 - src/components/ui/alert-dialog.tsx | 1 - src/components/ui/calendar.tsx | 1 - src/components/ui/color-picker.tsx | 1 - src/components/ui/command.tsx | 1 - src/components/ui/dialog.tsx | 1 - src/components/ui/drawer.tsx | 1 - src/components/ui/dropdown-menu.tsx | 1 - src/components/ui/hover-card.tsx | 1 - src/components/ui/label.tsx | 1 - src/components/ui/popover.tsx | 1 - src/components/ui/resizable.tsx | 1 - src/components/ui/scroll-area.tsx | 1 - src/components/ui/select.tsx | 1 - src/components/ui/separator.tsx | 1 - src/components/ui/sheet.tsx | 1 - src/components/ui/sidebar.tsx | 1 - src/components/ui/slider.tsx | 1 - src/components/ui/sonner.tsx | 1 - src/components/ui/switch.tsx | 1 - src/components/ui/tabs.tsx | 1 - src/components/ui/tooltip.tsx | 1 - src/components/ui/unified-date-input.tsx | 1 - .../ui/unified-date-range-input.tsx | 1 - src/routes/app/dashboard.tsx | 1 + src/routes/app/preview-template/$id.tsx | 1 + src/store/useGrammarStore.ts | 99 +++++++++++++++---- 80 files changed, 99 insertions(+), 101 deletions(-) diff --git a/src/app/app/dashboard/ai/page.tsx b/src/app/app/dashboard/ai/page.tsx index ad4820f..1e61a55 100644 --- a/src/app/app/dashboard/ai/page.tsx +++ b/src/app/app/dashboard/ai/page.tsx @@ -1,4 +1,3 @@ -"use client"; import { useState, useEffect } from "react"; import { useTranslations } from "@/i18n/compat/client"; import { ExternalLink } from "lucide-react"; diff --git a/src/app/app/dashboard/client.tsx b/src/app/app/dashboard/client.tsx index 3f1b766..2e6d13d 100644 --- a/src/app/app/dashboard/client.tsx +++ b/src/app/app/dashboard/client.tsx @@ -1,4 +1,3 @@ -"use client"; import { useEffect, useState } from "react"; import { IconResumes, IconTemplates, IconSettings, IconAI } from "@/components/shared/icons/SidebarIcons"; import { usePathname, useRouter } from "@/lib/navigation"; diff --git a/src/app/app/dashboard/resumes/page.tsx b/src/app/app/dashboard/resumes/page.tsx index 381b465..4b63351 100644 --- a/src/app/app/dashboard/resumes/page.tsx +++ b/src/app/app/dashboard/resumes/page.tsx @@ -1,4 +1,3 @@ -"use client"; import React, { useEffect } from "react"; import { useTranslations, useLocale } from "@/i18n/compat/client"; import { useRouter } from "@/lib/navigation"; diff --git a/src/app/app/dashboard/settings/page.tsx b/src/app/app/dashboard/settings/page.tsx index c5ef535..c812a00 100644 --- a/src/app/app/dashboard/settings/page.tsx +++ b/src/app/app/dashboard/settings/page.tsx @@ -1,4 +1,3 @@ -"use client"; import { useState, useEffect } from "react"; import { Folder, Trash2 } from "lucide-react"; import { useTranslations } from "@/i18n/compat/client"; diff --git a/src/app/app/dashboard/templates/page.tsx b/src/app/app/dashboard/templates/page.tsx index a2b0901..ed68cef 100644 --- a/src/app/app/dashboard/templates/page.tsx +++ b/src/app/app/dashboard/templates/page.tsx @@ -1,4 +1,3 @@ -"use client"; import { useState, useRef } from "react"; import { useTranslations } from "@/i18n/compat/client"; import { motion } from "framer-motion"; diff --git a/src/app/app/workbench/[id]/page.tsx b/src/app/app/workbench/[id]/page.tsx index b1fee5f..ece3503 100644 --- a/src/app/app/workbench/[id]/page.tsx +++ b/src/app/app/workbench/[id]/page.tsx @@ -1,4 +1,3 @@ -"use client"; import React, { useState, useEffect, memo } from "react"; import { motion, AnimatePresence } from "framer-motion"; @@ -344,7 +343,10 @@ export default function Home() { defaultSize={panelSizes?.[2]} className="bg-gray-100" > -
+
{ - if (!resumeContentRef.current) return; - if (!checkConfiguration()) { return; } try { - const text = resumeContentRef.current.innerText; + const previewContent = + resumeContentRef.current || document.getElementById("resume-preview"); + if (!previewContent) { + toast.error(t("grammarCheck.errorToast")); + return; + } + + const text = previewContent.innerText?.trim(); + if (!text) { + toast.error(t("grammarCheck.errorToast")); + return; + } + await checkGrammar(text); } catch (error) { toast.error(t("grammarCheck.errorToast")); diff --git a/src/components/preview/ProjectSection.tsx b/src/components/preview/ProjectSection.tsx index ffd60b2..fd0beb3 100644 --- a/src/components/preview/ProjectSection.tsx +++ b/src/components/preview/ProjectSection.tsx @@ -1,4 +1,3 @@ -"use client"; import React from "react"; import { motion, AnimatePresence } from "framer-motion"; diff --git a/src/components/preview/SectionTitle.tsx b/src/components/preview/SectionTitle.tsx index fdabc5b..1c09690 100644 --- a/src/components/preview/SectionTitle.tsx +++ b/src/components/preview/SectionTitle.tsx @@ -1,4 +1,3 @@ -"use client"; import { useMemo } from "react"; import { GlobalSettings } from "@/types/resume"; import { useResumeStore } from "@/store/useResumeStore"; diff --git a/src/components/preview/SkillPanel.tsx b/src/components/preview/SkillPanel.tsx index 191ce26..73b2212 100644 --- a/src/components/preview/SkillPanel.tsx +++ b/src/components/preview/SkillPanel.tsx @@ -1,4 +1,3 @@ -"use client"; import { motion } from "framer-motion"; import SectionTitle from "./SectionTitle"; import { GlobalSettings } from "@/types/resume"; diff --git a/src/components/preview/index.tsx b/src/components/preview/index.tsx index 7561792..1672da3 100644 --- a/src/components/preview/index.tsx +++ b/src/components/preview/index.tsx @@ -1,4 +1,3 @@ -"use client"; import React, { useEffect, useMemo, useState, useRef } from "react"; import throttle from "lodash/throttle"; diff --git a/src/components/shared/GitHubStars.tsx b/src/components/shared/GitHubStars.tsx index 961f506..651d56f 100644 --- a/src/components/shared/GitHubStars.tsx +++ b/src/components/shared/GitHubStars.tsx @@ -1,4 +1,3 @@ -"use client"; import { useEffect, useState } from "react"; import { Star } from "lucide-react"; import { motion } from "framer-motion"; diff --git a/src/components/shared/GithubContribution.tsx b/src/components/shared/GithubContribution.tsx index f141bdd..745b8b8 100644 --- a/src/components/shared/GithubContribution.tsx +++ b/src/components/shared/GithubContribution.tsx @@ -1,4 +1,3 @@ -"use client"; import React, { useEffect, useState } from "react"; import { cn } from "@/lib/utils"; diff --git a/src/components/shared/LanguageSwitch.tsx b/src/components/shared/LanguageSwitch.tsx index 4ed49c4..a9f4772 100644 --- a/src/components/shared/LanguageSwitch.tsx +++ b/src/components/shared/LanguageSwitch.tsx @@ -1,4 +1,3 @@ -"use client"; import { useLocale } from "@/i18n/compat/client"; import { useLocation, useNavigate } from "@tanstack/react-router"; import { Languages } from "lucide-react"; diff --git a/src/components/shared/Logo.tsx b/src/components/shared/Logo.tsx index 552217c..e98f18c 100644 --- a/src/components/shared/Logo.tsx +++ b/src/components/shared/Logo.tsx @@ -1,4 +1,3 @@ -"use client"; import React from "react"; import Image from "@/lib/image"; diff --git a/src/components/shared/PdfExport.tsx b/src/components/shared/PdfExport.tsx index e152b1d..d82ed3b 100644 --- a/src/components/shared/PdfExport.tsx +++ b/src/components/shared/PdfExport.tsx @@ -1,4 +1,3 @@ -"use client"; import React, { useState, useRef } from "react"; import { useTranslations } from "@/i18n/compat/client"; import { diff --git a/src/components/shared/PhotoConfigDrawer.tsx b/src/components/shared/PhotoConfigDrawer.tsx index 3806f03..903a5a7 100644 --- a/src/components/shared/PhotoConfigDrawer.tsx +++ b/src/components/shared/PhotoConfigDrawer.tsx @@ -1,4 +1,3 @@ -"use client"; import React, { useState, useRef, useEffect } from "react"; import { motion } from "framer-motion"; import { Upload, X } from "lucide-react"; diff --git a/src/components/shared/PhotoSelector.tsx b/src/components/shared/PhotoSelector.tsx index c644229..82164aa 100644 --- a/src/components/shared/PhotoSelector.tsx +++ b/src/components/shared/PhotoSelector.tsx @@ -1,4 +1,3 @@ -"use client"; import React, { useState } from "react"; import { Settings2, Image, EyeOff, Eye } from "lucide-react"; import { Button } from "@/components/ui/button"; diff --git a/src/components/shared/ScrollToTop.tsx b/src/components/shared/ScrollToTop.tsx index 0039d75..5787325 100644 --- a/src/components/shared/ScrollToTop.tsx +++ b/src/components/shared/ScrollToTop.tsx @@ -1,4 +1,3 @@ -"use client"; import React, { useState, useEffect, useRef } from "react"; import { ArrowUp } from "lucide-react"; import { Button } from "@/components/ui/button"; diff --git a/src/components/shared/TemplateSheet.tsx b/src/components/shared/TemplateSheet.tsx index 1cabea9..9e97482 100644 --- a/src/components/shared/TemplateSheet.tsx +++ b/src/components/shared/TemplateSheet.tsx @@ -1,4 +1,3 @@ -"use client"; import { useEffect, useRef, useState } from "react"; import { Layout, PanelsLeftBottom } from "lucide-react"; import { motion } from "framer-motion"; diff --git a/src/components/shared/ThemeModal.tsx b/src/components/shared/ThemeModal.tsx index bc0d5c3..ac5ed2a 100644 --- a/src/components/shared/ThemeModal.tsx +++ b/src/components/shared/ThemeModal.tsx @@ -1,4 +1,3 @@ -"use client"; import { useState } from "react"; import { motion } from "framer-motion"; diff --git a/src/components/shared/ThemeToggle.tsx b/src/components/shared/ThemeToggle.tsx index bf9c1a0..a9d72c5 100644 --- a/src/components/shared/ThemeToggle.tsx +++ b/src/components/shared/ThemeToggle.tsx @@ -1,4 +1,3 @@ -"use client"; import * as React from "react"; import { Moon, Sun } from "lucide-react"; import { useTheme } from "next-themes"; diff --git a/src/components/shared/ai/AIPolishDialog.tsx b/src/components/shared/ai/AIPolishDialog.tsx index 2afcda9..84aa862 100644 --- a/src/components/shared/ai/AIPolishDialog.tsx +++ b/src/components/shared/ai/AIPolishDialog.tsx @@ -1,4 +1,3 @@ -"use client"; import { useEffect, useState, useRef } from "react"; import { Loader2, Sparkles } from "lucide-react"; import { toast } from "sonner"; diff --git a/src/components/shared/rich-editor/RichEditor.tsx b/src/components/shared/rich-editor/RichEditor.tsx index 03b9d49..d313555 100644 --- a/src/components/shared/rich-editor/RichEditor.tsx +++ b/src/components/shared/rich-editor/RichEditor.tsx @@ -1,4 +1,3 @@ -"use client"; import React, { useEffect } from "react"; import { useEditor, EditorContent } from "@tiptap/react"; import { useTranslations } from "@/i18n/compat/client"; diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx index 2f55a32..a9f78b1 100644 --- a/src/components/ui/accordion.tsx +++ b/src/components/ui/accordion.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import * as AccordionPrimitive from "@radix-ui/react-accordion" diff --git a/src/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx index 25e7b47..f49ac77 100644 --- a/src/components/ui/alert-dialog.tsx +++ b/src/components/ui/alert-dialog.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx index 2f02434..eb1d091 100644 --- a/src/components/ui/calendar.tsx +++ b/src/components/ui/calendar.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import { ChevronLeft, ChevronRight } from "lucide-react" diff --git a/src/components/ui/color-picker.tsx b/src/components/ui/color-picker.tsx index 0c8c497..167ab16 100644 --- a/src/components/ui/color-picker.tsx +++ b/src/components/ui/color-picker.tsx @@ -1,4 +1,3 @@ -"use client"; import { forwardRef, useMemo, useState } from "react"; import { HexColorPicker } from "react-colorful"; diff --git a/src/components/ui/command.tsx b/src/components/ui/command.tsx index ace9b53..ad2e807 100644 --- a/src/components/ui/command.tsx +++ b/src/components/ui/command.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import { type DialogProps } from "@radix-ui/react-dialog" diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx index af8d0a5..01013bd 100644 --- a/src/components/ui/dialog.tsx +++ b/src/components/ui/dialog.tsx @@ -1,4 +1,3 @@ -"use client"; import * as React from "react"; import * as DialogPrimitive from "@radix-ui/react-dialog"; diff --git a/src/components/ui/drawer.tsx b/src/components/ui/drawer.tsx index 6a0ef53..d15a69d 100644 --- a/src/components/ui/drawer.tsx +++ b/src/components/ui/drawer.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import { Drawer as DrawerPrimitive } from "vaul" diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx index 0fc4c0e..6421c83 100644 --- a/src/components/ui/dropdown-menu.tsx +++ b/src/components/ui/dropdown-menu.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" diff --git a/src/components/ui/hover-card.tsx b/src/components/ui/hover-card.tsx index e54d91c..79e061d 100644 --- a/src/components/ui/hover-card.tsx +++ b/src/components/ui/hover-card.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import * as HoverCardPrimitive from "@radix-ui/react-hover-card" diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx index 5341821..a44cd78 100644 --- a/src/components/ui/label.tsx +++ b/src/components/ui/label.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import * as LabelPrimitive from "@radix-ui/react-label" diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx index a0ec48b..0910077 100644 --- a/src/components/ui/popover.tsx +++ b/src/components/ui/popover.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import * as PopoverPrimitive from "@radix-ui/react-popover" diff --git a/src/components/ui/resizable.tsx b/src/components/ui/resizable.tsx index f4bc558..a3b1c2d 100644 --- a/src/components/ui/resizable.tsx +++ b/src/components/ui/resizable.tsx @@ -1,4 +1,3 @@ -"use client" import { GripVertical } from "lucide-react" import * as ResizablePrimitive from "react-resizable-panels" diff --git a/src/components/ui/scroll-area.tsx b/src/components/ui/scroll-area.tsx index 0b4a48d..3f73997 100644 --- a/src/components/ui/scroll-area.tsx +++ b/src/components/ui/scroll-area.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx index cbe5a36..4911962 100644 --- a/src/components/ui/select.tsx +++ b/src/components/ui/select.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import * as SelectPrimitive from "@radix-ui/react-select" diff --git a/src/components/ui/separator.tsx b/src/components/ui/separator.tsx index 12d81c4..658f0cf 100644 --- a/src/components/ui/separator.tsx +++ b/src/components/ui/separator.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import * as SeparatorPrimitive from "@radix-ui/react-separator" diff --git a/src/components/ui/sheet.tsx b/src/components/ui/sheet.tsx index a37f17b..15ad757 100644 --- a/src/components/ui/sheet.tsx +++ b/src/components/ui/sheet.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import * as SheetPrimitive from "@radix-ui/react-dialog" diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx index 23ab98e..4579e9f 100644 --- a/src/components/ui/sidebar.tsx +++ b/src/components/ui/sidebar.tsx @@ -1,4 +1,3 @@ -"use client"; import * as React from "react"; import { Slot } from "@radix-ui/react-slot"; diff --git a/src/components/ui/slider.tsx b/src/components/ui/slider.tsx index c31c2b3..7ebcc1e 100644 --- a/src/components/ui/slider.tsx +++ b/src/components/ui/slider.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import * as SliderPrimitive from "@radix-ui/react-slider" diff --git a/src/components/ui/sonner.tsx b/src/components/ui/sonner.tsx index 452f4d9..6dc449e 100644 --- a/src/components/ui/sonner.tsx +++ b/src/components/ui/sonner.tsx @@ -1,4 +1,3 @@ -"use client" import { useTheme } from "next-themes" import { Toaster as Sonner } from "sonner" diff --git a/src/components/ui/switch.tsx b/src/components/ui/switch.tsx index bc69cf2..ee1f4b8 100644 --- a/src/components/ui/switch.tsx +++ b/src/components/ui/switch.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import * as SwitchPrimitives from "@radix-ui/react-switch" diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx index 26eb109..ffb5beb 100644 --- a/src/components/ui/tabs.tsx +++ b/src/components/ui/tabs.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import * as TabsPrimitive from "@radix-ui/react-tabs" diff --git a/src/components/ui/tooltip.tsx b/src/components/ui/tooltip.tsx index 30fc44d..3de65cd 100644 --- a/src/components/ui/tooltip.tsx +++ b/src/components/ui/tooltip.tsx @@ -1,4 +1,3 @@ -"use client" import * as React from "react" import * as TooltipPrimitive from "@radix-ui/react-tooltip" diff --git a/src/components/ui/unified-date-input.tsx b/src/components/ui/unified-date-input.tsx index aa3aab7..1bc4c3f 100644 --- a/src/components/ui/unified-date-input.tsx +++ b/src/components/ui/unified-date-input.tsx @@ -1,4 +1,3 @@ -"use client"; import { DateInput } from "@heroui/date-input"; import { HeroUIProvider } from "@heroui/react"; diff --git a/src/components/ui/unified-date-range-input.tsx b/src/components/ui/unified-date-range-input.tsx index 2455d42..ff3a1cf 100644 --- a/src/components/ui/unified-date-range-input.tsx +++ b/src/components/ui/unified-date-range-input.tsx @@ -1,4 +1,3 @@ -"use client"; import { DateInput } from "@heroui/date-input"; import { HeroUIProvider } from "@heroui/react"; diff --git a/src/routes/app/dashboard.tsx b/src/routes/app/dashboard.tsx index 6b27459..0fd920f 100644 --- a/src/routes/app/dashboard.tsx +++ b/src/routes/app/dashboard.tsx @@ -5,6 +5,7 @@ export const Route = createFileRoute("/app/dashboard")({ head: () => ({ meta: [{ name: "robots", content: "noindex,nofollow" }] }), + ssr: false, component: DashboardRouteLayout }); diff --git a/src/routes/app/preview-template/$id.tsx b/src/routes/app/preview-template/$id.tsx index 4e626bd..17c6bc9 100644 --- a/src/routes/app/preview-template/$id.tsx +++ b/src/routes/app/preview-template/$id.tsx @@ -2,5 +2,6 @@ import { createFileRoute } from '@tanstack/react-router' import IframeTemplateViewer from '../../../components/preview/IframeTemplateViewer' export const Route = createFileRoute('/app/preview-template/$id')({ + ssr: false, component: IframeTemplateViewer, }) diff --git a/src/store/useGrammarStore.ts b/src/store/useGrammarStore.ts index 5eed195..f9b29a9 100644 --- a/src/store/useGrammarStore.ts +++ b/src/store/useGrammarStore.ts @@ -28,6 +28,55 @@ interface GrammarStore { dismissError: (index: number) => void; } +const markSingleError = ( + marker: Mark, + error: GrammarError, + options?: { selected?: boolean; activeIndex?: number } +) => { + const keyword = (error.text || "").trim(); + if (!keyword) return; + + let hasMarked = false; + marker.mark(keyword, { + separateWordSearch: false, + acrossElements: true, + className: cn( + "grammar-error", + error.type, + options?.selected && options?.activeIndex !== undefined && `active-${options.activeIndex}` + ), + filter: () => { + if (hasMarked) return false; + hasMarked = true; + return true; + } + }); +}; + +const getPreviewScrollContainer = (element: HTMLElement): HTMLElement | null => { + const containers = Array.from( + document.querySelectorAll('[data-preview-scroll-container="true"]') + ); + + for (const container of containers) { + if (container.contains(element)) { + return container; + } + } + + let current: HTMLElement | null = element.parentElement; + while (current) { + const style = window.getComputedStyle(current); + const canScrollY = /(auto|scroll)/.test(style.overflowY); + if (canScrollY && current.scrollHeight > current.clientHeight) { + return current; + } + current = current.parentElement; + } + + return null; +}; + export const useGrammarStore = create((set, get) => ({ isChecking: false, errors: [], @@ -104,9 +153,8 @@ export const useGrammarStore = create((set, get) => ({ const marker = new Mark(preview); marker.unmark(); grammarErrors.errors.forEach((error: GrammarError) => { - marker.mark(error.context || error.text || "", { - className: "bg-yellow-200 dark:bg-yellow-900", - }); + // 仅标注错误片段,避免整句/全局模糊匹配造成误高亮 + markSingleError(marker, error); }); } } catch (parseError) { @@ -143,21 +191,36 @@ export const useGrammarStore = create((set, get) => ({ marker.unmark(); errors.forEach((err, i) => { - marker.mark(err.context || err.text || "", { - className: cn( - "bg-yellow-200 dark:bg-yellow-900", - i === index && "bg-green-200 dark:bg-green-900" - ), + markSingleError(marker, err, { + selected: i === index, + activeIndex: index }); }); const marks = preview.querySelectorAll("mark"); const selectedMark = marks[index]; if (selectedMark) { - selectedMark.scrollIntoView({ - behavior: "smooth", - block: "center", - }); + const scrollContainer = getPreviewScrollContainer(selectedMark as HTMLElement); + + if (scrollContainer) { + const containerRect = scrollContainer.getBoundingClientRect(); + const markRect = selectedMark.getBoundingClientRect(); + const currentTop = scrollContainer.scrollTop; + const nextTop = + currentTop + + (markRect.top - containerRect.top) - + scrollContainer.clientHeight / 2 + + markRect.height / 2; + const maxTop = Math.max( + 0, + scrollContainer.scrollHeight - scrollContainer.clientHeight + ); + + scrollContainer.scrollTo({ + top: Math.max(0, Math.min(nextTop, maxTop)), + behavior: "smooth" + }); + } } }, dismissError: (index: number) => { @@ -171,14 +234,10 @@ export const useGrammarStore = create((set, get) => ({ const marker = new Mark(preview); marker.unmark(); newErrors.forEach((error, i) => { - marker.mark(error.context || error.text || "", { - className: cn( - "bg-yellow-200 dark:bg-yellow-900", - state.selectedErrorIndex === i && "bg-green-200 dark:bg-green-900" - // 注意:selectedErrorIndex 可能因为删除而需要调整,这里简化处理,稍后在选中逻辑中可能需要优化 - // 为了保持一致性,最好是重新选中当前索引或重置选中 - ) - }); + markSingleError(marker, error, { + selected: state.selectedErrorIndex === i, + activeIndex: state.selectedErrorIndex ?? undefined + }); }); }