From d21c2356f398d61c19e787800991cc9b494ca80b Mon Sep 17 00:00:00 2001 From: Satsuki Akiba Date: Mon, 9 Jun 2025 11:52:26 +0900 Subject: [PATCH] Support dark mode in the frontend --- frontend/src/App.tsx | 31 +- frontend/src/components/EpisodeList.tsx | 128 ++------ frontend/src/styles.css | 392 ++++++++++++++++++++---- 3 files changed, 392 insertions(+), 159 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index f17fb27..fd0cbdf 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,4 +1,5 @@ import { Link, Route, Routes, useLocation } from "react-router-dom"; +import { useEffect, useState } from "react"; import EpisodeDetail from "./components/EpisodeDetail"; import EpisodeList from "./components/EpisodeList"; import FeedDetail from "./components/FeedDetail"; @@ -8,6 +9,19 @@ import RSSEndpoints from "./components/RSSEndpoints"; function App() { const location = useLocation(); + const [isDarkMode, setIsDarkMode] = useState(() => { + const saved = localStorage.getItem('darkMode'); + return saved ? JSON.parse(saved) : false; + }); + + useEffect(() => { + document.documentElement.setAttribute('data-theme', isDarkMode ? 'dark' : 'light'); + localStorage.setItem('darkMode', JSON.stringify(isDarkMode)); + }, [isDarkMode]); + + const toggleDarkMode = () => { + setIsDarkMode(!isDarkMode); + }; const isMainPage = [ "/", "/feeds", @@ -20,9 +34,20 @@ function App() { {isMainPage && ( <>
-
Voice RSS Summary
-
- RSS フィードから自動生成された音声ポッドキャスト +
+
+
Voice RSS Summary
+
+ RSS フィードから自動生成された音声ポッドキャスト +
+
+
diff --git a/frontend/src/components/EpisodeList.tsx b/frontend/src/components/EpisodeList.tsx index 06a698a..f3205ac 100644 --- a/frontend/src/components/EpisodeList.tsx +++ b/frontend/src/components/EpisodeList.tsx @@ -345,20 +345,9 @@ function EpisodeList() { return (
-
-
-

+
+
+

エピソード一覧 ( {searchQuery ? `検索結果: ${filteredEpisodes.length}件` @@ -369,8 +358,8 @@ function EpisodeList() { : `${episodes.length}件`} )

-
- +
+ データソース: {useDatabase ? "データベース" : "XML"}
-
+
setSearchQuery(e.target.value)} - style={{ - flex: "1", - minWidth: "200px", - padding: "8px 12px", - fontSize: "14px", - border: "1px solid #ccc", - borderRadius: "4px", - }} + className="episode-search-input" /> {searchQuery && (
@@ -456,24 +425,18 @@ function EpisodeList() { {episode.title}
{episode.feedTitle && ( -
+
フィード:{" "} {episode.feedTitle} @@ -481,7 +444,7 @@ function EpisodeList() { @@ -492,13 +455,7 @@ function EpisodeList() { )} {episode.articleTitle && episode.articleTitle !== episode.title && ( -
+
元記事: {episode.articleTitle}
)} @@ -507,46 +464,26 @@ function EpisodeList() { href={episode.articleLink} target="_blank" rel="noopener noreferrer" - style={{ fontSize: "12px", color: "#666" }} + className="episode-article-link" > 元記事を見る )} -
+
{episode.description || "No description"}
{episode.fileSize && ( -
+
{formatFileSize(episode.fileSize)}
)} {formatDate(episode.createdAt)} -
-
+
+
{/* Page numbers */} -
+
{/* First page */} {currentPage > 3 && ( <> @@ -635,11 +563,8 @@ function EpisodeList() { return ( @@ -669,7 +594,7 @@ function EpisodeList() { {/* Page size selector */} -
+
表示件数: