Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,9 @@ ty = (0.5 - vp.cy) × 100%
| `scripts/sync/AudioSyncer.js` | FFT sync, `syncMultiple` | Add FFT tie-breaking (Phase 0) |
| `scripts/wizard.js` | Interactive pipeline runner (60KB) | Replace with DAG runner (Phase 2) |
| `scripts/camera/setup-camera.js` | Frame extraction, face detection, `angles.json` | |
| `app/camera/page.tsx` | Camera GUI (face box editor, angle tabs) | |
| `app/editor/page.tsx` | Transcript editor | Add PreviewPlayer + scroll-sync (Phase 8) |
| `app/editor/Timeline.tsx` | Timeline component (630 lines) | Decompose + add waveform (Phase 8) |
| `app/(internal)/camera/page.tsx` | Camera GUI (face box editor, angle tabs) | |
| `app/(internal)/editor/page.tsx` | Transcript editor | Add PreviewPlayer + scroll-sync (Phase 8) |
| `app/(internal)/editor/Timeline.tsx` | Timeline component (630 lines) | Decompose + add waveform (Phase 8) |
| `app/components/AutoCarouselForm.tsx` | Carousel generator (810 lines) | Decompose (Phase 8) |
| `app/context/AuthContext.tsx` | Auth context with hardcoded credentials | Fix (Phase 7) |
| `vscode-transcript-language/src/extension.js` | VSCode transcript extension | Add Wrap-in-cut command (Phase 0.5) |
Expand Down
3 changes: 3 additions & 0 deletions app/camera/page.tsx → app/(internal)/camera/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ function CameraPageContent() {
const isShorts = searchParams.get('mode') === 'shorts';
const isThumbnail = searchParams.get('mode') === 'thumbnail';
const isPortraitMode = isShorts;
// eslint-disable-next-line react-hooks/exhaustive-deps
const outputDims = isShorts ? { w: 1080, h: 1920 } : null;

// ── Load data ───────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -402,6 +403,7 @@ function CameraPageContent() {
window.removeEventListener('mousemove', onMove);
window.removeEventListener('mouseup', onUp);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [drag.mode, getNorm]);

// ── SVG interaction ──────────────────────────────────────────────────────────
Expand Down Expand Up @@ -768,6 +770,7 @@ function CameraPageContent() {
)}

<Box style={{ position: 'relative' }}>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
key={frameSrc}
src={frameSrc}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/* eslint-disable react-hooks/refs */
'use client';

import React, { useRef, useEffect, useCallback } from 'react';
import { Box, Text } from '@mantine/core';
import type { Transcript, TimeCut, CameraCue } from '../../remotion/types/transcript';
import type { CameraProfiles } from '../../remotion/types/camera';
import type { Transcript, TimeCut, CameraCue } from '../../../remotion/types/transcript';
import type { CameraProfiles } from '../../../remotion/types/camera';
import {
CANVAS_H, RULER_H, TRACK_H, TRACK_GAP, TRIM_W, N_TRACKS, CLIP_PAD,
CAM_TRACK_Y, CAM_TRACK_H, CAM_HANDLE_HIT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const TimelineScrollbar: React.FC<Props> = ({ total, zoom, panOffset, onP
if (!el) return;
const ratio = (e.clientX - el.getBoundingClientRect().left) / el.offsetWidth;
const clickedTime = ratio * total;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const thumbMid = panOffset + visDur / 2;
// If click lands inside the thumb, start a drag; otherwise page-jump
const thumbLPx = (panOffset / total) * el.offsetWidth;
Expand Down Expand Up @@ -93,6 +94,7 @@ export const TimelineScrollbar: React.FC<Props> = ({ total, zoom, panOffset, onP
left: `${thumbL}%`,
width: `${thumbW}%`,
height: 6,
// eslint-disable-next-line react-hooks/refs
background: dragRef.current ? 'rgba(255,255,255,0.45)' : 'rgba(255,255,255,0.25)',
borderRadius: 3,
cursor: 'grab',
Expand Down
4 changes: 2 additions & 2 deletions app/editor/page.tsx → app/(internal)/editor/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {
IconPlayerPlay, IconPlayerPause, IconScissors, IconDeviceFloppy,
IconArrowsMaximize,
} from '@tabler/icons-react';
import type { Transcript, TimeCut, CameraCue } from '../../remotion/types/transcript';
import type { CameraProfiles } from '../../remotion/types/camera';
import type { Transcript, TimeCut, CameraCue } from '../../../remotion/types/transcript';
import type { CameraProfiles } from '../../../remotion/types/camera';
import { Timeline, fmtTimecode } from './Timeline';
import { useTimelineNav } from './useTimelineNav';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Segment, TimeCut, CameraCue } from '../../remotion/types/transcript';
import type { CameraProfiles } from '../../remotion/types/camera';
import type { Segment, TimeCut, CameraCue } from '../../../remotion/types/transcript';
import type { CameraProfiles } from '../../../remotion/types/camera';

// ─── Layout constants ────────────────────────────────────────────────────────
export const RULER_H = 24;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ export function useTimelineNav({
if (preset === 'full') { fitAll(); return; }
if (preset === 'clips') { fitContent(); return; }
// 'frames': maximum zoom centred on current time
setNav(prev => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
setNav(_prev => {
const z = MAX_ZOOM;
const visDur = total / z;
return { zoom: z, pan: clamp(currentTime - visDur * 0.35, 0, Math.max(0, total - visDur)) };
Expand All @@ -101,6 +102,7 @@ export function useTimelineNav({
// ── Auto-follow: keep playhead at 35% during playback ─────────────────────
useEffect(() => {
if (!isPlaying) return;
// eslint-disable-next-line react-hooks/set-state-in-effect
setNav(prev => {
const visDur = total / prev.zoom;
const ratio = (currentTime - prev.pan) / visDur;
Expand Down
4 changes: 2 additions & 2 deletions app/about/page.tsx → app/(public)/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
IconLeaf,
IconUsers,
} from '@tabler/icons-react';
import { Header } from '../components/Header';
import { Header } from '../../components/Header';

export default function AboutPage() {
return (
Expand Down Expand Up @@ -198,7 +198,7 @@ export default function AboutPage() {
<Anchor href="/carousel" underline="never">
<Button size="md">Create Carousel</Button>
</Anchor>
<Anchor href="/transcribe" underline="never">
<Anchor href="/get-youtube-captions" underline="never">
<Button size="md" variant="light">Transcribe Video</Button>
</Anchor>
</Group>
Expand Down
6 changes: 3 additions & 3 deletions app/carousel/page.tsx → app/(public)/carousel/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Container, Stack, Title, Text } from '@mantine/core';
import { Header } from '../components/Header';
import { CarouselForm } from '../components/CarouselForm';
import { InstagramEmbed } from '../components/InstagramEmbed';
import { Header } from '../../components/Header';
import { CarouselForm } from '../../components/CarouselForm';
import { InstagramEmbed } from '../../components/InstagramEmbed';

export default function CarouselPage() {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Container } from '@mantine/core';
import { Header } from '../components/Header';
import { TranscriptionForm } from '../components/TranscriptionForm';
import { Header } from '../../components/Header';
import { TranscriptionForm } from '../../components/TranscriptionForm';

export default function TranscribePage() {
return (
Expand Down
8 changes: 4 additions & 4 deletions app/page.tsx → app/(public)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Container, Title, Text, Stack, Group, Paper, Button, Badge, ThemeIcon, SimpleGrid, Box, Anchor } from '@mantine/core';
import { Container, Title, Text, Stack, Group, Paper, Button, Badge, ThemeIcon, SimpleGrid, Anchor } from '@mantine/core';
import { IconPhoto, IconFileText, IconLeaf, IconSparkles, IconArrowRight, IconBrandYoutube, IconWand, IconLock } from '@tabler/icons-react';
import { Header } from './components/Header';
import { InstagramEmbed } from './components/InstagramEmbed';
import { Header } from '../components/Header';
import { InstagramEmbed } from '../components/InstagramEmbed';

export default function Home() {
return (
Expand Down Expand Up @@ -95,7 +95,7 @@ export default function Home() {
<Badge size="xs" variant="dot" color="primary">Timestamps</Badge>
<Badge size="xs" variant="dot" color="secondary">Filler removal</Badge>
</Group>
<Anchor href="/transcribe" underline="never">
<Anchor href="/get-youtube-captions" underline="never">
<Button
fullWidth
size="md"
Expand Down
33 changes: 33 additions & 0 deletions app/components/Header.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { render } from '@testing-library/react';
import { MantineProvider } from '@mantine/core';
import { Header } from './Header';

function renderHeader() {
return render(
<MantineProvider>
<Header />
</MantineProvider>
);
}

describe('Header nav links', () => {
it('renders without throwing', () => {
expect(() => renderHeader()).not.toThrow();
});

it('includes the renamed /get-youtube-captions route', () => {
const { container } = renderHeader();
expect(container.querySelector('a[href="/get-youtube-captions"]')).not.toBeNull();
});

it('does not link to the old /transcribe route', () => {
const { container } = renderHeader();
expect(container.querySelector('a[href="/transcribe"]')).toBeNull();
});

it('does not link to /login', () => {
const { container } = renderHeader();
expect(container.querySelector('a[href="/login"]')).toBeNull();
});
});
56 changes: 3 additions & 53 deletions app/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { usePathname, useRouter } from 'next/navigation';
import { usePathname } from 'next/navigation';
import {
Container,
Group,
Expand All @@ -14,24 +14,20 @@ import {
UnstyledButton,
Text,
Badge,
Button,
} from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { IconSun, IconMoon, IconPhoto, IconFileText, IconInfoCircle, IconWand, IconLogin, IconLogout } from '@tabler/icons-react';
import { useAuth } from '../context/AuthContext';
import { IconSun, IconMoon, IconPhoto, IconFileText, IconInfoCircle, IconWand } from '@tabler/icons-react';

const NAV_LINKS = [
{ href: '/carousel', label: 'Carousel', icon: IconPhoto, pro: false },
{ href: '/transcribe', label: 'Transcription', icon: IconFileText, pro: false },
{ href: '/get-youtube-captions', label: 'Transcription', icon: IconFileText, pro: false },
{ href: '/auto-carousel', label: 'Auto Carousel', icon: IconWand, pro: true },
{ href: '/about', label: 'About', icon: IconInfoCircle, pro: false },
];

export function Header() {
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
const pathname = usePathname();
const router = useRouter();
const { user, logout } = useAuth();
const [drawerOpened, { toggle: toggleDrawer, close: closeDrawer }] = useDisclosure(false);

return (
Expand Down Expand Up @@ -95,27 +91,6 @@ export function Header() {
</Anchor>
);
})}
{user ? (
<Button
variant="subtle"
size="compact-sm"
color="dimmed"
leftSection={<IconLogout size={14} />}
onClick={() => { logout(); router.push('/'); }}
>
Sign Out
</Button>
) : (
<Button
variant="subtle"
size="compact-sm"
color="dimmed"
leftSection={<IconLogin size={14} />}
onClick={() => router.push('/login')}
>
Sign In
</Button>
)}
<ActionIcon
variant="subtle"
size="lg"
Expand Down Expand Up @@ -194,31 +169,6 @@ export function Header() {
</Anchor>
);
})}
{user ? (
<Button
variant="subtle"
size="sm"
color="dimmed"
leftSection={<IconLogout size={16} />}
onClick={() => { logout(); closeDrawer(); router.push('/'); }}
fullWidth
justify="flex-start"
>
Sign Out
</Button>
) : (
<Button
variant="subtle"
size="sm"
color="dimmed"
leftSection={<IconLogin size={16} />}
onClick={() => { closeDrawer(); router.push('/login'); }}
fullWidth
justify="flex-start"
>
Sign In
</Button>
)}
</Stack>
</Drawer>
</>
Expand Down
Loading
Loading