feat: Wired /embed/:videoId route outside AppShell for chrome-free rend…

- "frontend/src/App.tsx"
- "frontend/src/pages/WatchPage.tsx"
- "frontend/src/App.css"

GSD-Task: S03/T02
This commit is contained in:
jlightner 2026-04-04 10:59:14 +00:00
parent cf7c8b26a0
commit ddb283cc28
4 changed files with 58 additions and 4 deletions

View file

@ -6449,6 +6449,13 @@ a.app-footer__about:hover,
margin-bottom: 1.25rem;
}
.watch-page__header-top {
display: flex;
align-items: baseline;
gap: 1rem;
flex-wrap: wrap;
}
.watch-page__title {
font-size: 1.25rem;
font-weight: 600;
@ -6456,6 +6463,29 @@ a.app-footer__about:hover,
margin: 0 0 0.25rem;
}
.watch-page__embed-btn {
font-size: 0.8rem;
padding: 0.3rem 0.75rem;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--surface-alt, var(--surface));
color: var(--text-secondary);
cursor: pointer;
white-space: nowrap;
transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.watch-page__embed-btn:hover {
border-color: var(--accent);
color: var(--text-primary);
}
.watch-page__embed-btn--copied {
background: var(--accent);
color: var(--surface);
border-color: var(--accent);
}
.watch-page__creator {
font-size: 0.9rem;
color: var(--accent);

View file

@ -28,6 +28,7 @@ const CreatorTiers = React.lazy(() => import("./pages/CreatorTiers"));
const PostEditor = React.lazy(() => import("./pages/PostEditor"));
const PostsList = React.lazy(() => import("./pages/PostsList"));
const ShortPlayer = React.lazy(() => import("./pages/ShortPlayer"));
const EmbedPlayer = React.lazy(() => import("./pages/EmbedPlayer"));
import AdminDropdown from "./components/AdminDropdown";
import ImpersonationBanner from "./components/ImpersonationBanner";
import AppFooter from "./components/AppFooter";
@ -228,7 +229,10 @@ function AppShell() {
export default function App() {
return (
<AuthProvider>
<AppShell />
<Routes>
<Route path="/embed/:videoId" element={<Suspense fallback={<LoadingFallback />}><EmbedPlayer /></Suspense>} />
<Route path="/*" element={<AppShell />} />
</Routes>
</AuthProvider>
);
}

View file

@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useEffect, useState, useCallback } from "react";
import { useParams, useSearchParams, Link } from "react-router-dom";
import { useDocumentTitle } from "../hooks/useDocumentTitle";
import { useMediaSync } from "../hooks/useMediaSync";
@ -10,6 +10,7 @@ import VideoPlayer from "../components/VideoPlayer";
import AudioWaveform from "../components/AudioWaveform";
import PlayerControls from "../components/PlayerControls";
import TranscriptSidebar from "../components/TranscriptSidebar";
import { copyToClipboard } from "../utils/clipboard";
export default function WatchPage() {
const { videoId } = useParams<{ videoId: string }>();
@ -25,9 +26,20 @@ export default function WatchPage() {
const [error, setError] = useState<string | null>(null);
const [transcriptError, setTranscriptError] = useState(false);
const [chapters, setChapters] = useState<Chapter[]>([]);
const [embedCopied, setEmbedCopied] = useState(false);
const mediaSync = useMediaSync();
const handleCopyEmbed = useCallback(async () => {
const height = video?.video_url ? 405 : 120;
const snippet = `<iframe src="${window.location.origin}/embed/${videoId}" width="720" height="${height}" frameborder="0" allowfullscreen></iframe>`;
const ok = await copyToClipboard(snippet);
if (ok) {
setEmbedCopied(true);
setTimeout(() => setEmbedCopied(false), 2000);
}
}, [videoId, video?.video_url]);
useDocumentTitle(video ? `${video.filename} — Chrysopedia` : "Loading…");
// Fetch video detail
@ -106,7 +118,15 @@ export default function WatchPage() {
return (
<div className="watch-page">
<header className="watch-page__header">
<div className="watch-page__header-top">
<h1 className="watch-page__title">{video.filename}</h1>
<button
className={`watch-page__embed-btn${embedCopied ? " watch-page__embed-btn--copied" : ""}`}
onClick={handleCopyEmbed}
>
{embedCopied ? "Copied!" : "Copy Embed Code"}
</button>
</div>
{video.creator_name && video.creator_slug && (
<Link
to={`/creators/${video.creator_slug}`}

View file

@ -1 +1 @@
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/admin-pipeline.ts","./src/api/admin-techniques.ts","./src/api/auth.ts","./src/api/chat.ts","./src/api/client.ts","./src/api/consent.ts","./src/api/creator-dashboard.ts","./src/api/creators.ts","./src/api/follows.ts","./src/api/highlights.ts","./src/api/index.ts","./src/api/posts.ts","./src/api/reports.ts","./src/api/search.ts","./src/api/shorts.ts","./src/api/stats.ts","./src/api/techniques.ts","./src/api/topics.ts","./src/api/videos.ts","./src/components/AdminDropdown.tsx","./src/components/AppFooter.tsx","./src/components/AudioWaveform.tsx","./src/components/CategoryIcons.tsx","./src/components/ChapterMarkers.tsx","./src/components/ChatWidget.tsx","./src/components/ConfirmModal.tsx","./src/components/CopyLinkButton.tsx","./src/components/CreatorAvatar.tsx","./src/components/ImpersonationBanner.tsx","./src/components/PersonalityProfile.tsx","./src/components/PlayerControls.tsx","./src/components/PostsFeed.tsx","./src/components/ProtectedRoute.tsx","./src/components/ReportIssueModal.tsx","./src/components/SearchAutocomplete.tsx","./src/components/SocialIcons.tsx","./src/components/SortDropdown.tsx","./src/components/TableOfContents.tsx","./src/components/TagList.tsx","./src/components/ToggleSwitch.tsx","./src/components/TranscriptSidebar.tsx","./src/components/VideoPlayer.tsx","./src/context/AuthContext.tsx","./src/hooks/useCountUp.ts","./src/hooks/useDocumentTitle.ts","./src/hooks/useMediaSync.ts","./src/hooks/useSortPreference.ts","./src/pages/About.tsx","./src/pages/AdminAuditLog.tsx","./src/pages/AdminPipeline.tsx","./src/pages/AdminReports.tsx","./src/pages/AdminTechniquePages.tsx","./src/pages/AdminUsers.tsx","./src/pages/ChapterReview.tsx","./src/pages/ChatPage.tsx","./src/pages/ConsentDashboard.tsx","./src/pages/CreatorDashboard.tsx","./src/pages/CreatorDetail.tsx","./src/pages/CreatorSettings.tsx","./src/pages/CreatorTiers.tsx","./src/pages/CreatorsBrowse.tsx","./src/pages/HighlightQueue.tsx","./src/pages/Home.tsx","./src/pages/Login.tsx","./src/pages/PostEditor.tsx","./src/pages/PostsList.tsx","./src/pages/Register.tsx","./src/pages/SearchResults.tsx","./src/pages/ShortPlayer.tsx","./src/pages/SubTopicPage.tsx","./src/pages/TechniquePage.tsx","./src/pages/TopicsBrowse.tsx","./src/pages/WatchPage.tsx","./src/utils/catSlug.ts","./src/utils/citations.tsx"],"version":"5.6.3"}
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/admin-pipeline.ts","./src/api/admin-techniques.ts","./src/api/auth.ts","./src/api/chat.ts","./src/api/client.ts","./src/api/consent.ts","./src/api/creator-dashboard.ts","./src/api/creators.ts","./src/api/follows.ts","./src/api/highlights.ts","./src/api/index.ts","./src/api/posts.ts","./src/api/reports.ts","./src/api/search.ts","./src/api/shorts.ts","./src/api/stats.ts","./src/api/techniques.ts","./src/api/topics.ts","./src/api/videos.ts","./src/components/AdminDropdown.tsx","./src/components/AppFooter.tsx","./src/components/AudioWaveform.tsx","./src/components/CategoryIcons.tsx","./src/components/ChapterMarkers.tsx","./src/components/ChatWidget.tsx","./src/components/ConfirmModal.tsx","./src/components/CopyLinkButton.tsx","./src/components/CreatorAvatar.tsx","./src/components/ImpersonationBanner.tsx","./src/components/PersonalityProfile.tsx","./src/components/PlayerControls.tsx","./src/components/PostsFeed.tsx","./src/components/ProtectedRoute.tsx","./src/components/ReportIssueModal.tsx","./src/components/SearchAutocomplete.tsx","./src/components/SocialIcons.tsx","./src/components/SortDropdown.tsx","./src/components/TableOfContents.tsx","./src/components/TagList.tsx","./src/components/ToggleSwitch.tsx","./src/components/TranscriptSidebar.tsx","./src/components/VideoPlayer.tsx","./src/context/AuthContext.tsx","./src/hooks/useCountUp.ts","./src/hooks/useDocumentTitle.ts","./src/hooks/useMediaSync.ts","./src/hooks/useSortPreference.ts","./src/pages/About.tsx","./src/pages/AdminAuditLog.tsx","./src/pages/AdminPipeline.tsx","./src/pages/AdminReports.tsx","./src/pages/AdminTechniquePages.tsx","./src/pages/AdminUsers.tsx","./src/pages/ChapterReview.tsx","./src/pages/ChatPage.tsx","./src/pages/ConsentDashboard.tsx","./src/pages/CreatorDashboard.tsx","./src/pages/CreatorDetail.tsx","./src/pages/CreatorSettings.tsx","./src/pages/CreatorTiers.tsx","./src/pages/CreatorsBrowse.tsx","./src/pages/EmbedPlayer.tsx","./src/pages/HighlightQueue.tsx","./src/pages/Home.tsx","./src/pages/Login.tsx","./src/pages/PostEditor.tsx","./src/pages/PostsList.tsx","./src/pages/Register.tsx","./src/pages/SearchResults.tsx","./src/pages/ShortPlayer.tsx","./src/pages/SubTopicPage.tsx","./src/pages/TechniquePage.tsx","./src/pages/TopicsBrowse.tsx","./src/pages/WatchPage.tsx","./src/utils/catSlug.ts","./src/utils/citations.tsx","./src/utils/clipboard.ts"],"version":"5.6.3"}