From 0bf6e2c53763dfad1003d172afd1f6e84057065b Mon Sep 17 00:00:00 2001
From: liuhuapiaoyuan <278780765@qq.com>
Date: Fri, 25 Oct 2024 14:18:04 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E7=A7=BB=E5=8A=A8=E7=AB=AF=E5=85=BC?=
=?UTF-8?q?=E5=AE=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
frontend/package.json | 1 +
frontend/pnpm-lock.yaml | 38 +++++++
frontend/src/App.tsx | 13 ++-
frontend/src/components/audio.tsx | 30 ++++--
frontend/src/components/content.tsx | 9 +-
frontend/src/components/episode.tsx | 22 ++--
frontend/src/components/menu.tsx | 4 +-
frontend/src/components/mobile-menu.tsx | 52 +++++++++
frontend/src/components/transcript.tsx | 18 ++--
frontend/src/components/ui/sheet.tsx | 138 ++++++++++++++++++++++++
frontend/src/index.css | 11 ++
frontend/tailwind.config.js | 4 +-
12 files changed, 301 insertions(+), 39 deletions(-)
create mode 100644 frontend/src/components/mobile-menu.tsx
create mode 100644 frontend/src/components/ui/sheet.tsx
diff --git a/frontend/package.json b/frontend/package.json
index aad42c6..3d60828 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -11,6 +11,7 @@
},
"dependencies": {
"@madzadev/audio-player": "^2.1.14",
+ "@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-select": "^2.1.2",
diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml
index 95e28c7..2dc25e9 100644
--- a/frontend/pnpm-lock.yaml
+++ b/frontend/pnpm-lock.yaml
@@ -11,6 +11,9 @@ importers:
'@madzadev/audio-player':
specifier: ^2.1.14
version: 2.1.14
+ '@radix-ui/react-dialog':
+ specifier: ^1.1.2
+ version: 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-dropdown-menu':
specifier: ^2.1.2
version: 2.1.2(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -559,6 +562,19 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-dialog@1.1.2':
+ resolution: {integrity: sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-direction@1.1.0':
resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==}
peerDependencies:
@@ -2787,6 +2803,28 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.11
+ '@radix-ui/react-dialog@1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.0
+ '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.11)(react@18.3.1)
+ '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@18.3.1)
+ '@radix-ui/react-dismissable-layer': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.11)(react@18.3.1)
+ '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-id': 1.1.0(@types/react@18.3.11)(react@18.3.1)
+ '@radix-ui/react-portal': 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-presence': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-slot': 1.1.0(@types/react@18.3.11)(react@18.3.1)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@18.3.1)
+ aria-hidden: 1.2.4
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ react-remove-scroll: 2.6.0(@types/react@18.3.11)(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.11
+ '@types/react-dom': 18.3.0
+
'@radix-ui/react-direction@1.1.0(@types/react@18.3.11)(react@18.3.1)':
dependencies:
react: 18.3.1
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index ad9483e..ecfb0c3 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -4,6 +4,7 @@ import Menu from "./components/menu";
import { useJsonData } from "./hooks/useJsonData";
import { useStreamText } from './hooks/useStreamText';
import { BASE_URL } from "./lib/constant";
+import MobileMenu from "./components/mobile-menu";
function App() {
const [isGenerating, setIsGenerating] = useState(false);
@@ -23,7 +24,7 @@ function App() {
data: podInfoData,
error: podInfoError,
isLoading: isPodInfoLoading,
- fetchJsonData: fetchPodInfo,
+ fetchJsonData: fetchPodInfo,
} = useJsonData();
@@ -37,13 +38,13 @@ function App() {
return (
-
+ />
}
/>
diff --git a/frontend/src/components/audio.tsx b/frontend/src/components/audio.tsx
index f92e729..0ce9a01 100644
--- a/frontend/src/components/audio.tsx
+++ b/frontend/src/components/audio.tsx
@@ -26,27 +26,36 @@ export default function Audio({ audioUrl, isAudioLoading, audioError }: { audioU
}
};
+ const MOBILE_TEMPLATE_AREA = {
+ "trackInfo": "row1-1",
+ "playButton": "row2-1",
+ "repeatType": "row2-2",
+ "playList": "row2-3",
+ "progress": "row3-1",
+ "trackTimeDuration": "row1-3",
+ "trackTimeCurrent": "row1-2",
+ };
return (
-
+
-
+
{isAudioLoading ? (
-
-
-
-
-
+
+
+
+
+
Generating conversation...
This may take a few minutes. No need to stick around!
-
+
) : audioError ? (
) : audioUrl ? (
-
+
void;
+ mobileMenu?:React.ReactNode;
}
export default function Content({
formData,
@@ -35,7 +36,8 @@ export default function Content({
setIsGenerating,
activeTab,
setActiveTab,
-}: ContentProps) {
+ mobileMenu
+}:ContentProps ) {
const {
error: audioError,
isLoading: isAudioLoading,
@@ -93,9 +95,10 @@ export default function Content({
isPodInfoLoading={isPodInfoLoading}
podInfoError={podInfoError}
podInfoData={podInfoData}
+ mobileMenu={mobileMenu}
/>
-
+
-
+
{
@@ -34,7 +37,7 @@ export default function Episode({ isPodInfoLoading, podInfoError, podInfoData }:
Error
- {podInfoError || "An error occurred while loading the episode data."}
+ {podInfoError || "An error occurred while loading the episode data."}
)
@@ -43,9 +46,10 @@ export default function Episode({ isPodInfoLoading, podInfoError, podInfoData }:
!isPodInfoLoading && !podInfoError && (
{podInfoData?.title || "播客标题"}
-
主讲人: {podInfoData?.host_name || "未知"}
-
- )}
+ 主讲人: {podInfoData?.host_name || "未知"}
+
+ )}
+ {mobileMenu}
)
}
diff --git a/frontend/src/components/menu.tsx b/frontend/src/components/menu.tsx
index 24830d7..82449b8 100644
--- a/frontend/src/components/menu.tsx
+++ b/frontend/src/components/menu.tsx
@@ -9,7 +9,7 @@ import { useSpeeker } from '@/hooks/useSpeeker';
const MAX_FILE_SIZE = 1 * 1024 * 1024; // 5MB in bytes
const DEMO_PDF_URL = '/demo.pdf'; // 替换为你的演示 PDF 文件的实际路径
-export default function Menu({ handleGenerate, isGenerating }: { handleGenerate: (formData: FormData) => void, isGenerating: boolean }) {
+export default function Menu({ handleGenerate,className, isGenerating }: { className?:string,handleGenerate: (formData: FormData) => void, isGenerating: boolean }) {
const [pdfFile, setPdfFile] = useState
(null);
const [textInput, setTextInput] = useState('');
const [tone, setTone] = useState('neutral');
@@ -69,7 +69,7 @@ export default function Menu({ handleGenerate, isGenerating }: { handleGenerate:
};
return (
-
+
上传 PDF *
diff --git a/frontend/src/components/mobile-menu.tsx b/frontend/src/components/mobile-menu.tsx
new file mode 100644
index 0000000..28851f0
--- /dev/null
+++ b/frontend/src/components/mobile-menu.tsx
@@ -0,0 +1,52 @@
+import {
+ Sheet,
+ SheetContent,
+ SheetDescription,
+ SheetHeader,
+ SheetTitle,
+ SheetTrigger,
+} from "@/components/ui/sheet"
+import Menu from "./menu"
+import { Button } from "./ui/button"
+import { Sparkles } from "lucide-react"
+import { useState } from "react"
+
+/**
+ * smallest component possible for mobile menu
+ * @param props
+ * @returns
+ */
+export default function MobileMenu(
+ { handleGenerate,
+ isGenerating }: { handleGenerate: (formData: FormData) => void, isGenerating: boolean }) {
+ const [open,setOpen] = useState(false)
+ return
+
+
+
+
+
+ 开始制作
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/frontend/src/components/transcript.tsx b/frontend/src/components/transcript.tsx
index 8a46f43..37a1a7b 100644
--- a/frontend/src/components/transcript.tsx
+++ b/frontend/src/components/transcript.tsx
@@ -45,8 +45,8 @@ export default function Transcript({
}, [transcriptTextChunks]);
return (
-
-
+
+
-
-
-
+
+
{
renderContent(
@@ -78,9 +76,9 @@ export default function Transcript({
-
-
-
+
+
+
,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
+
+const sheetVariants = cva(
+ "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out",
+ {
+ variants: {
+ side: {
+ top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
+ bottom:
+ "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
+ left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
+ right:
+ "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
+ },
+ },
+ defaultVariants: {
+ side: "right",
+ },
+ }
+)
+
+interface SheetContentProps
+ extends React.ComponentPropsWithoutRef,
+ VariantProps {}
+
+const SheetContent = React.forwardRef<
+ React.ElementRef,
+ SheetContentProps
+>(({ side = "right", className, children, ...props }, ref) => (
+
+
+
+
+
+ Close
+
+ {children}
+
+
+))
+SheetContent.displayName = SheetPrimitive.Content.displayName
+
+const SheetHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+SheetHeader.displayName = "SheetHeader"
+
+const SheetFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+SheetFooter.displayName = "SheetFooter"
+
+const SheetTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SheetTitle.displayName = SheetPrimitive.Title.displayName
+
+const SheetDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SheetDescription.displayName = SheetPrimitive.Description.displayName
+
+export {
+ Sheet,
+ SheetPortal,
+ SheetOverlay,
+ SheetTrigger,
+ SheetClose,
+ SheetContent,
+ SheetHeader,
+ SheetFooter,
+ SheetTitle,
+ SheetDescription,
+}
diff --git a/frontend/src/index.css b/frontend/src/index.css
index 6aef233..eed21a3 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -10,6 +10,17 @@
--rm-audio-player-interface-container: #fff;
--primary: 240 5.9% 10%;
}
+:root {
+ --background: #ffffff;
+ --foreground: #171717;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --background: #0a0a0a;
+ --foreground: #ededed;
+ }
+}
.kuNzeN img {
border-radius: 100% !important;
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
index 86c0e3d..3ebf778 100644
--- a/frontend/tailwind.config.js
+++ b/frontend/tailwind.config.js
@@ -12,8 +12,8 @@ export default {
sm: 'calc(var(--radius) - 4px)'
},
colors: {
- background: 'hsl(var(--background))',
- foreground: 'hsl(var(--foreground))',
+ background: 'var(--background)',
+ foreground: 'var(--foreground)',
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'