refactor: remove redundant 'use client' directives from numerous components.

This commit is contained in:
JOYCEQL
2026-02-26 01:48:53 +08:00
parent c0b9dff50f
commit 2876da4072
80 changed files with 99 additions and 101 deletions
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { useState, useEffect } from "react";
import { useTranslations } from "@/i18n/compat/client";
import { ExternalLink } from "lucide-react";
-1
View File
@@ -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";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import React, { useEffect } from "react";
import { useTranslations, useLocale } from "@/i18n/compat/client";
import { useRouter } from "@/lib/navigation";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { useState, useEffect } from "react";
import { Folder, Trash2 } from "lucide-react";
import { useTranslations } from "@/i18n/compat/client";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { useState, useRef } from "react";
import { useTranslations } from "@/i18n/compat/client";
import { motion } from "framer-motion";
+4 -2
View File
@@ -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"
>
<div className="h-full overflow-y-auto">
<div
className="h-full overflow-y-auto"
data-preview-scroll-container="true"
>
<PreviewPanel
sidePanelCollapsed={sidePanelCollapsed}
editPanelCollapsed={editPanelCollapsed}
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { ThemeProvider } from "next-themes";
import { HeroUIProvider } from "@heroui/react";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import React from "react";
import { Pencil } from "lucide-react";
import { motion } from "framer-motion";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { useTranslations } from "@/i18n/compat/client";
import { AlertCircle } from "lucide-react";
import { motion } from "framer-motion";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { useState, useEffect, useMemo } from "react";
import { motion } from "framer-motion";
import { CalendarIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { useMemo } from "react";
import { motion } from "framer-motion";
import { Layout, Type, SpaceIcon, Palette, Zap } from "lucide-react";
@@ -1,4 +1,3 @@
"use client";
import React, { useState } from "react";
import { PlusCircle, GripVertical, Trash2, Eye, EyeOff } from "lucide-react";
import { Reorder, AnimatePresence, motion } from "framer-motion";
@@ -1,4 +1,3 @@
"use client";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { useResumeStore } from "@/store/useResumeStore";
@@ -1,4 +1,3 @@
"use client";
import { cn } from "@/lib/utils";
import { useResumeStore } from "@/store/useResumeStore";
import { Reorder } from "framer-motion";
@@ -1,4 +1,3 @@
"use client";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import {
@@ -1,4 +1,3 @@
"use client";
import { cn } from "@/lib/utils";
import { Reorder } from "framer-motion";
import { PlusCircle } from "lucide-react";
@@ -1,4 +1,3 @@
"use client";
import { useGrammarCheck } from "@/hooks/useGrammarCheck";
import { useResumeStore } from "@/store/useResumeStore";
@@ -1,4 +1,3 @@
"use client";
import { motion, Reorder, useDragControls } from "framer-motion";
import { Eye, EyeOff, GripVertical, Trash2 } from "lucide-react";
@@ -1,4 +1,3 @@
"use client";
import { Reorder } from "framer-motion";
import { MenuSection } from "@/types/resume";
import LayoutItem from "./LayoutItem";
@@ -1,4 +1,3 @@
"use client";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { useResumeStore } from "@/store/useResumeStore";
@@ -1,4 +1,3 @@
"use client";
import { useResumeStore } from "@/store/useResumeStore";
import { cn } from "@/lib/utils";
import Field from "../Field";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import React from "react";
import { useTranslations } from "@/i18n/compat/client";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { useTranslations } from "@/i18n/compat/client";
import Image from "@/lib/image";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { useNavigate } from "@tanstack/react-router";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { useState } from "react";
import { usePathname } from "@/lib/navigation";
@@ -1,4 +1,3 @@
"use client";
import { motion } from "framer-motion";
interface AnimatedFeatureProps {
@@ -1,4 +1,3 @@
"use client";
import { useState } from "react";
import { X, Menu } from "lucide-react";
@@ -1,4 +1,3 @@
"use client";
import { motion } from "framer-motion";
import { useTranslations, useLocale } from "@/i18n/compat/client";
@@ -1,4 +1,3 @@
"use client";
import { motion, useScroll } from "framer-motion";
export default function ScrollBackground() {
@@ -1,4 +1,3 @@
"use client";
import { useState, useEffect } from "react";
import { useScroll } from "framer-motion";
+1 -1
View File
@@ -1,4 +1,3 @@
"use client";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
@@ -124,6 +123,7 @@ export function MobileWorkbench() {
exit={{ opacity: 0, scale: 1.05 }}
transition={{ duration: 0.2 }}
className="h-full overflow-y-auto bg-gray-100"
data-preview-scroll-container="true"
>
<PreviewPanel
sidePanelCollapsed={true}
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { AnimatePresence, motion } from "framer-motion";
import SectionTitle from "./SectionTitle";
import { GlobalSettings, CustomItem } from "@/types/resume";
@@ -1,4 +1,3 @@
"use client";
import { AnimatePresence, motion } from "framer-motion";
import { Education, GlobalSettings } from "@/types/resume";
import SectionTitle from "./SectionTitle";
@@ -1,4 +1,3 @@
"use client";
import React from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Experience, GlobalSettings } from "@/types/resume";
@@ -1,4 +1,3 @@
"use client";
import React, { useMemo } from "react";
import { useParams } from "@tanstack/react-router";
+13 -4
View File
@@ -1,4 +1,3 @@
"use client";
import React, { useCallback, useState } from "react";
import {
Edit2,
@@ -392,14 +391,24 @@ const PreviewDock = ({
// ... (keep other hooks)
const handleGrammarCheck = useCallback(async () => {
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"));
@@ -1,4 +1,3 @@
"use client";
import React from "react";
import { motion, AnimatePresence } from "framer-motion";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { useMemo } from "react";
import { GlobalSettings } from "@/types/resume";
import { useResumeStore } from "@/store/useResumeStore";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { motion } from "framer-motion";
import SectionTitle from "./SectionTitle";
import { GlobalSettings } from "@/types/resume";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import React, { useEffect, useMemo, useState, useRef } from "react";
import throttle from "lodash/throttle";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { useEffect, useState } from "react";
import { Star } from "lucide-react";
import { motion } from "framer-motion";
@@ -1,4 +1,3 @@
"use client";
import React, { useEffect, useState } from "react";
import { cn } from "@/lib/utils";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { useLocale } from "@/i18n/compat/client";
import { useLocation, useNavigate } from "@tanstack/react-router";
import { Languages } from "lucide-react";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import React from "react";
import Image from "@/lib/image";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import React, { useState, useRef } from "react";
import { useTranslations } from "@/i18n/compat/client";
import {
@@ -1,4 +1,3 @@
"use client";
import React, { useState, useRef, useEffect } from "react";
import { motion } from "framer-motion";
import { Upload, X } from "lucide-react";
-1
View File
@@ -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";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import React, { useState, useEffect, useRef } from "react";
import { ArrowUp } from "lucide-react";
import { Button } from "@/components/ui/button";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { useEffect, useRef, useState } from "react";
import { Layout, PanelsLeftBottom } from "lucide-react";
import { motion } from "framer-motion";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { useState } from "react";
import { motion } from "framer-motion";
-1
View File
@@ -1,4 +1,3 @@
"use client";
import * as React from "react";
import { Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";
@@ -1,4 +1,3 @@
"use client";
import { useEffect, useState, useRef } from "react";
import { Loader2, Sparkles } from "lucide-react";
import { toast } from "sonner";
@@ -1,4 +1,3 @@
"use client";
import React, { useEffect } from "react";
import { useEditor, EditorContent } from "@tiptap/react";
import { useTranslations } from "@/i18n/compat/client";
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import * as AccordionPrimitive from "@radix-ui/react-accordion"
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import { ChevronLeft, ChevronRight } from "lucide-react"
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { forwardRef, useMemo, useState } from "react";
import { HexColorPicker } from "react-colorful";
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import { type DialogProps } from "@radix-ui/react-dialog"
-1
View File
@@ -1,4 +1,3 @@
"use client";
import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog";
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import { Drawer as DrawerPrimitive } from "vaul"
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"
-1
View File
@@ -1,4 +1,3 @@
"use client"
import { GripVertical } from "lucide-react"
import * as ResizablePrimitive from "react-resizable-panels"
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import * as SeparatorPrimitive from "@radix-ui/react-separator"
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import * as SheetPrimitive from "@radix-ui/react-dialog"
-1
View File
@@ -1,4 +1,3 @@
"use client";
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import * as SliderPrimitive from "@radix-ui/react-slider"
-1
View File
@@ -1,4 +1,3 @@
"use client"
import { useTheme } from "next-themes"
import { Toaster as Sonner } from "sonner"
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import * as SwitchPrimitives from "@radix-ui/react-switch"
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import * as TabsPrimitive from "@radix-ui/react-tabs"
-1
View File
@@ -1,4 +1,3 @@
"use client"
import * as React from "react"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
-1
View File
@@ -1,4 +1,3 @@
"use client";
import { DateInput } from "@heroui/date-input";
import { HeroUIProvider } from "@heroui/react";
@@ -1,4 +1,3 @@
"use client";
import { DateInput } from "@heroui/date-input";
import { HeroUIProvider } from "@heroui/react";
+1
View File
@@ -5,6 +5,7 @@ export const Route = createFileRoute("/app/dashboard")({
head: () => ({
meta: [{ name: "robots", content: "noindex,nofollow" }]
}),
ssr: false,
component: DashboardRouteLayout
});
+1
View File
@@ -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,
})
+79 -20
View File
@@ -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<HTMLElement>('[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<GrammarStore>((set, get) => ({
isChecking: false,
errors: [],
@@ -104,9 +153,8 @@ export const useGrammarStore = create<GrammarStore>((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<GrammarStore>((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<GrammarStore>((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
});
});
}