diff --git a/CLAUDE.md b/CLAUDE.md
index 692052d..107760f 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -22,9 +22,7 @@ pnpm run start
pnpm run lint
# 数据库操作
-pnpm prisma generate # 生成 Prisma client 到 src/generated/prisma
-pnpm prisma db push # 推送 schema 变更到数据库
-pnpm prisma studio # 打开 Prisma Studio 查看数据库
+# 不要进行数据库操作,让用户操作数据库
```
## 技术栈
@@ -102,31 +100,6 @@ src/modules/{module}/
**LLM 集成**: 使用智谱 AI API 进行翻译和 IPA 生成。通过环境变量 `ZHIPU_API_KEY` 和 `ZHIPU_MODEL_NAME` 配置。
-### 环境变量
-
-需要在 `.env.local` 中配置:
-
-```env
-# LLM 集成(智谱 AI 用于翻译和 IPA 生成)
-ZHIPU_API_KEY=your-api-key
-ZHIPU_MODEL_NAME=your-model-name
-
-# 阿里云千问 TTS(文本转语音)
-DASHSCORE_API_KEY=your-dashscore-api-key
-
-# 认证
-BETTER_AUTH_SECRET=your-secret
-BETTER_AUTH_URL=http://localhost:3000
-GITHUB_CLIENT_ID=your-client-id
-GITHUB_CLIENT_SECRET=your-client-secret
-
-# 数据库
-DATABASE_URL=postgresql://username:password@localhost:5432/database_name
-```
-
-## 重要配置细节
-
-- **Prisma client 输出**: 自定义目录 `src/generated/prisma`(不是默认的 `node_modules/.prisma`)
- **Standalone 输出**: 为 Docker 部署配置
- **React Compiler**: 在 `next.config.ts` 中启用以自动优化
- **HTTPS 开发**: 开发服务器使用 `--experimental-https` 标志
@@ -147,7 +120,6 @@ DATABASE_URL=postgresql://username:password@localhost:5432/database_name
## 开发注意事项
- 使用 pnpm,而不是 npm 或 yarn
-- schema 变更后,先运行 `pnpm prisma generate` 再运行 `pnpm prisma db push`
- 应用使用 TypeScript 严格模式 - 确保类型安全
- 所有面向用户的文本都需要国际化
- **优先使用 Server Components**,只在需要交互时使用 Client Components
diff --git a/src/app/(features)/alphabet/AlphabetCard.tsx b/src/app/(features)/alphabet/AlphabetCard.tsx
index b303490..0d5435d 100644
--- a/src/app/(features)/alphabet/AlphabetCard.tsx
+++ b/src/app/(features)/alphabet/AlphabetCard.tsx
@@ -3,11 +3,11 @@
import { useState, useEffect, useCallback } from "react";
import { useTranslations } from "next-intl";
import { Letter, SupportedAlphabets } from "@/lib/interfaces";
-import { IconClick, CircleToggleButton, CircleButton, PrimaryButton } from "@/components/ui/buttons";
+import { IconClick, CircleToggleButton, CircleButton, PrimaryButton } from "@/design-system/base/button";
import { IMAGES } from "@/config/images";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { PageLayout } from "@/components/ui/PageLayout";
-import { Card } from "@/components/ui/Card";
+import { Card } from "@/design-system/base/card";
interface AlphabetCardProps {
alphabet: Letter[];
@@ -103,7 +103,7 @@ export function AlphabetCard({ alphabet, alphabetType, onBack }: AlphabetCardPro
{/* 右上角返回按钮 - outside the white card */}
{/* 上一个按钮 */}
-
+
{/* 中间区域:随机按钮 */}
@@ -202,7 +202,7 @@ export function AlphabetCard({ alphabet, alphabetType, onBack }: AlphabetCardPro
{/* 下一个按钮 */}
-
+
diff --git a/src/app/(features)/alphabet/MemoryCard.tsx b/src/app/(features)/alphabet/MemoryCard.tsx
index 8b29e41..faaaf04 100644
--- a/src/app/(features)/alphabet/MemoryCard.tsx
+++ b/src/app/(features)/alphabet/MemoryCard.tsx
@@ -1,5 +1,5 @@
-import { LightButton } from "@/components/ui/buttons";
-import { IconClick } from "@/components/ui/buttons";
+import { LightButton } from "@/design-system/base/button";
+import { IconClick } from "@/design-system/base/button";
import { IMAGES } from "@/config/images";
import { Letter, SupportedAlphabets } from "@/lib/interfaces";
import {
@@ -45,10 +45,10 @@ export function MemoryCard({
className="w-full flex justify-center items-center"
onKeyDown={(e: KeyboardEvent) => e.preventDefault()}
>
-
+
setChosenAlphabet(null)}
@@ -64,13 +64,13 @@ export function MemoryCard({
setMore(!more)}
diff --git a/src/app/(features)/alphabet/page.tsx b/src/app/(features)/alphabet/page.tsx
index 4e594e0..c1031a0 100644
--- a/src/app/(features)/alphabet/page.tsx
+++ b/src/app/(features)/alphabet/page.tsx
@@ -4,7 +4,7 @@ import { useState, useEffect } from "react";
import { useTranslations } from "next-intl";
import { Letter, SupportedAlphabets } from "@/lib/interfaces";
import { PageLayout } from "@/components/ui/PageLayout";
-import { LightButton } from "@/components/ui/buttons";
+import { LightButton } from "@/design-system/base/button";
import { AlphabetCard } from "./AlphabetCard";
export default function Alphabet() {
diff --git a/src/app/(features)/dictionary/SearchForm.tsx b/src/app/(features)/dictionary/SearchForm.tsx
index 71bf3b8..687b7a9 100644
--- a/src/app/(features)/dictionary/SearchForm.tsx
+++ b/src/app/(features)/dictionary/SearchForm.tsx
@@ -1,7 +1,7 @@
"use client";
-import { LightButton } from "@/components/ui/buttons";
-import { Input } from "@/components/ui/Input";
+import { LightButton } from "@/design-system/base/button";
+import { Input } from "@/design-system/base/input";
import { useTranslations } from "next-intl";
import { useState } from "react";
import { useRouter } from "next/navigation";
diff --git a/src/app/(features)/dictionary/SearchResult.client.tsx b/src/app/(features)/dictionary/SearchResult.client.tsx
index 08c7131..97609f2 100644
--- a/src/app/(features)/dictionary/SearchResult.client.tsx
+++ b/src/app/(features)/dictionary/SearchResult.client.tsx
@@ -1,7 +1,7 @@
"use client";
import { Plus, RefreshCw } from "lucide-react";
-import { CircleButton, LightButton } from "@/components/ui/buttons";
+import { CircleButton, LightButton } from "@/design-system/base/button";
import { toast } from "sonner";
import { actionCreatePair } from "@/modules/folder/folder-aciton";
import { TSharedItem } from "@/shared/dictionary-type";
diff --git a/src/app/(features)/memorize/FolderSelector.tsx b/src/app/(features)/memorize/FolderSelector.tsx
index 09e74f9..91c1475 100644
--- a/src/app/(features)/memorize/FolderSelector.tsx
+++ b/src/app/(features)/memorize/FolderSelector.tsx
@@ -6,7 +6,7 @@ import Link from "next/link";
import { Folder as Fd } from "lucide-react";
import { TSharedFolderWithTotalPairs } from "@/shared/folder-type";
import { PageLayout } from "@/components/ui/PageLayout";
-import { PrimaryButton } from "@/components/ui/buttons";
+import { PrimaryButton } from "@/design-system/base/button";
interface FolderSelectorProps {
folders: TSharedFolderWithTotalPairs[];
@@ -37,7 +37,7 @@ const FolderSelector: React.FC = ({ folders }) => {
{t("selectFolder")}
{/* 文件夹列表 */}
-
+
{folders
.toSorted((a, b) => a.id - b.id)
.map((folder) => (
@@ -50,7 +50,7 @@ const FolderSelector: React.FC
= ({ folders }) => {
>
{/* 文件夹图标 */}
-
+
{/* 文件夹信息 */}
diff --git a/src/app/(features)/memorize/Memorize.tsx b/src/app/(features)/memorize/Memorize.tsx
index e6bb377..48846da 100644
--- a/src/app/(features)/memorize/Memorize.tsx
+++ b/src/app/(features)/memorize/Memorize.tsx
@@ -1,7 +1,7 @@
"use client";
import { useState } from "react";
-import { LinkButton, CircleToggleButton, LightButton } from "@/components/ui/buttons";
+import { LinkButton, CircleToggleButton, LightButton } from "@/design-system/base/button";
import { useAudioPlayer } from "@/hooks/useAudioPlayer";
import { getTTSUrl, TTS_SUPPORTED_LANGUAGES } from "@/lib/bigmodel/tts";
import { useTranslations } from "next-intl";
@@ -29,7 +29,7 @@ const Memorize: React.FC
= ({ textPairs }) => {
if (textPairs.length === 0) {
return (
-
+
{t("noTextPairs")}
);
diff --git a/src/app/(features)/srt-player/VideoPlayer/VideoPanel.tsx b/src/app/(features)/srt-player/VideoPlayer/VideoPanel.tsx
index b9161d6..26db463 100644
--- a/src/app/(features)/srt-player/VideoPlayer/VideoPanel.tsx
+++ b/src/app/(features)/srt-player/VideoPlayer/VideoPanel.tsx
@@ -1,6 +1,6 @@
import { useState, useRef, forwardRef, useEffect, useCallback } from "react";
import { SubtitleDisplay } from "./SubtitleDisplay";
-import { LightButton } from "@/components/ui/buttons";
+import { LightButton } from "@/design-system/base/button";
import { RangeInput } from "@/components/ui/RangeInput";
import { getIndex, parseSrt, getNearistIndex } from "../subtitle";
import { useTranslations } from "next-intl";
diff --git a/src/app/(features)/srt-player/components/atoms/FileInput.tsx b/src/app/(features)/srt-player/components/atoms/FileInput.tsx
index 079a7ae..fcf0e43 100644
--- a/src/app/(features)/srt-player/components/atoms/FileInput.tsx
+++ b/src/app/(features)/srt-player/components/atoms/FileInput.tsx
@@ -1,7 +1,7 @@
"use client";
import React, { useRef } from "react";
-import { Button } from "@/components/ui/Button";
+import { Button } from "@/design-system/base/button";
import { FileInputProps } from "../../types/controls";
interface FileInputComponentProps extends FileInputProps {
diff --git a/src/app/(features)/srt-player/components/atoms/PlayButton.tsx b/src/app/(features)/srt-player/components/atoms/PlayButton.tsx
index e967859..32534ef 100644
--- a/src/app/(features)/srt-player/components/atoms/PlayButton.tsx
+++ b/src/app/(features)/srt-player/components/atoms/PlayButton.tsx
@@ -2,7 +2,7 @@
import React from "react";
import { useTranslations } from "next-intl";
-import { LightButton } from "@/components/ui/buttons";
+import { LightButton } from "@/design-system/base/button";
import { PlayButtonProps } from "../../types/player";
export function PlayButton({ isPlaying, onToggle, disabled, className }: PlayButtonProps) {
diff --git a/src/app/(features)/srt-player/components/atoms/SpeedControl.tsx b/src/app/(features)/srt-player/components/atoms/SpeedControl.tsx
index 3664557..d266e92 100644
--- a/src/app/(features)/srt-player/components/atoms/SpeedControl.tsx
+++ b/src/app/(features)/srt-player/components/atoms/SpeedControl.tsx
@@ -1,7 +1,7 @@
"use client";
import React from "react";
-import { LightButton } from "@/components/ui/buttons";
+import { LightButton } from "@/design-system/base/button";
import { SpeedControlProps } from "../../types/player";
import { getPlaybackRateOptions, getPlaybackRateLabel } from "../../utils/timeUtils";
diff --git a/src/app/(features)/srt-player/components/compounds/ControlBar.tsx b/src/app/(features)/srt-player/components/compounds/ControlBar.tsx
index 75276db..b24470c 100644
--- a/src/app/(features)/srt-player/components/compounds/ControlBar.tsx
+++ b/src/app/(features)/srt-player/components/compounds/ControlBar.tsx
@@ -3,7 +3,7 @@
import React from "react";
import { useTranslations } from "next-intl";
import { ChevronLeft, ChevronRight, RotateCcw, Pause } from "lucide-react";
-import { LightButton } from "@/components/ui/buttons";
+import { LightButton } from "@/design-system/base/button";
import { ControlBarProps } from "../../types/controls";
import { PlayButton } from "../atoms/PlayButton";
import { SpeedControl } from "../atoms/SpeedControl";
diff --git a/src/app/(features)/srt-player/components/compounds/UploadZone.tsx b/src/app/(features)/srt-player/components/compounds/UploadZone.tsx
index 45b063b..450850e 100644
--- a/src/app/(features)/srt-player/components/compounds/UploadZone.tsx
+++ b/src/app/(features)/srt-player/components/compounds/UploadZone.tsx
@@ -4,7 +4,7 @@ import React from "react";
import { useTranslations } from "next-intl";
import { toast } from "sonner";
import { Video, FileText } from "lucide-react";
-import { LightButton } from "@/components/ui/buttons";
+import { LightButton } from "@/design-system/base/button";
import { FileUploadProps } from "../../types/controls";
import { useFileUpload } from "../../hooks/useFileUpload";
diff --git a/src/app/(features)/srt-player/page.tsx b/src/app/(features)/srt-player/page.tsx
index c245655..8184861 100644
--- a/src/app/(features)/srt-player/page.tsx
+++ b/src/app/(features)/srt-player/page.tsx
@@ -15,7 +15,7 @@ import { SubtitleArea } from "./components/compounds/SubtitleArea";
import { ControlBar } from "./components/compounds/ControlBar";
import { UploadZone } from "./components/compounds/UploadZone";
import { SeekBar } from "./components/atoms/SeekBar";
-import { LightButton } from "@/components/ui/buttons";
+import { LightButton } from "@/design-system/base/button";
export default function SrtPlayerPage() {
const t = useTranslations("home");
@@ -119,7 +119,7 @@ export default function SrtPlayerPage() {
{/* 视频播放器区域 */}
-
+
{(!state.video.url || !state.subtitle.url || state.subtitle.data.length === 0) && (
diff --git a/src/app/(features)/text-speaker/SaveList.tsx b/src/app/(features)/text-speaker/SaveList.tsx
index 15282f8..98674f5 100644
--- a/src/app/(features)/text-speaker/SaveList.tsx
+++ b/src/app/(features)/text-speaker/SaveList.tsx
@@ -6,7 +6,7 @@ import {
TextSpeakerArraySchema,
TextSpeakerItemSchema,
} from "@/lib/interfaces";
-import { IconClick } from "@/components/ui/buttons";
+import { IconClick } from "@/design-system/base/button";
import { IMAGES } from "@/config/images";
import { useTranslations } from "next-intl";
import { getLocalStorageOperator } from "@/lib/browser/localStorageOperators";
@@ -24,7 +24,7 @@ function TextCard({ item, handleUse, handleDel }: TextCardProps) {
handleDel(item);
};
return (
-
+
{item.text}
@@ -39,7 +39,7 @@ function TextCard({ item, handleUse, handleDel }: TextCardProps) {
alt="delete"
onClick={onDelClick}
className="place-self-center"
- size={42}
+ size="lg"
>
@@ -81,7 +81,7 @@ export function SaveList({ show = false, handleUse }: SaveListProps) {
if (show)
return (
@@ -89,14 +89,14 @@ export function SaveList({ show = false, handleUse }: SaveListProps) {
src={IMAGES.refresh}
alt="refresh"
onClick={refresh}
- size={48}
+ size="lg"
className=""
>
diff --git a/src/app/(features)/text-speaker/page.tsx b/src/app/(features)/text-speaker/page.tsx
index 182ad1b..d683e47 100644
--- a/src/app/(features)/text-speaker/page.tsx
+++ b/src/app/(features)/text-speaker/page.tsx
@@ -1,7 +1,7 @@
"use client";
-import { LightButton } from "@/components/ui/buttons";
-import { IconClick } from "@/components/ui/buttons";
+import { LightButton } from "@/design-system/base/button";
+import { IconClick } from "@/design-system/base/button";
import { IMAGES } from "@/config/images";
import { useAudioPlayer } from "@/hooks/useAudioPlayer";
import {
@@ -222,7 +222,7 @@ export default function TextSpeakerPage() {
{/* 文本输入区域 */}
{/* 文本输入框 */}
@@ -242,37 +242,37 @@ export default function TextSpeakerPage() {
{/* 速度调节面板 */}
{showSpeedAdjust && (
-
+
{/* 自动暂停按钮 */}
{
setAutopause(!autopause);
if (objurlRef) {
@@ -303,7 +303,7 @@ export default function TextSpeakerPage() {
>
{/* 速度调节按钮 */}
setShowSpeedAdjust(!showSpeedAdjust)}
src={IMAGES.speed}
alt="speed"
@@ -311,7 +311,7 @@ export default function TextSpeakerPage() {
>
{/* 保存按钮 */}
{/* 保存列表 */}
{showSaveList && (
-
+
)}
diff --git a/src/app/(features)/translator/page.tsx b/src/app/(features)/translator/page.tsx
index 527b9f0..2cf5f6b 100644
--- a/src/app/(features)/translator/page.tsx
+++ b/src/app/(features)/translator/page.tsx
@@ -1,7 +1,6 @@
"use client";
-import { LightButton, PrimaryButton } from "@/components/ui/buttons";
-import { IconClick } from "@/components/ui/buttons";
+import { LightButton, PrimaryButton, IconClick } from "@/design-system/base/button";
import { IMAGES } from "@/config/images";
import { useAudioPlayer } from "@/hooks/useAudioPlayer";
import { useTranslations } from "next-intl";
@@ -101,7 +100,7 @@ export default function TranslatorPage() {
{/* Card Component - Left Side */}
{/* ICard1 Component */}
-