diff --git a/apps/web/src/app/app/page.tsx b/apps/web/src/app/app/page.tsx deleted file mode 100644 index a8b0b9a5d..000000000 --- a/apps/web/src/app/app/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import type { Metadata } from 'next'; -import { redirect } from 'next/navigation'; -import { CANONICAL_STUDIO_PATH } from '@/lib/auth-paths'; - -export const metadata: Metadata = { - title: 'App', - description: 'Redirect to the UVAI workbench.', - alternates: { canonical: '/studio' }, - robots: { index: false, follow: true }, -}; - -export default function AppRedirect() { - redirect(CANONICAL_STUDIO_PATH); -} diff --git a/apps/web/src/app/dashboard/agents/page.tsx b/apps/web/src/app/dashboard/agents/page.tsx deleted file mode 100644 index cad6fa780..000000000 --- a/apps/web/src/app/dashboard/agents/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import type { Metadata } from 'next'; -import { redirect } from 'next/navigation'; -import { CANONICAL_STUDIO_PATH } from '@/lib/auth-paths'; - -export const metadata: Metadata = { - title: 'Studio', - description: 'Redirect to the UVAI workbench.', - alternates: { canonical: '/studio' }, - robots: { index: false, follow: true }, -}; - -export default function LegacyAgentsRedirect() { - redirect(CANONICAL_STUDIO_PATH); -} diff --git a/apps/web/src/app/dashboard/page.tsx b/apps/web/src/app/dashboard/page.tsx deleted file mode 100644 index aee324e24..000000000 --- a/apps/web/src/app/dashboard/page.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import type { Metadata } from 'next'; -import { redirect } from 'next/navigation'; -import { canonicalStudioPath } from '@/lib/auth-paths'; - -export const metadata: Metadata = { - title: 'Studio', - description: 'Redirect to the UVAI workbench.', - alternates: { canonical: '/studio' }, - robots: { index: false, follow: true }, -}; - -export default async function LegacyDashboardRedirect({ - searchParams, -}: { - searchParams: Promise>; -}) { - const params = await searchParams; - const qs = new URLSearchParams(); - for (const [key, value] of Object.entries(params)) { - if (typeof value === 'string' && value.length > 0) { - qs.set(key, value); - } else if (Array.isArray(value) && value[0]) { - qs.set(key, value[0]); - } - } - const suffix = qs.toString(); - redirect(canonicalStudioPath(suffix ? `?${suffix}` : '')); -} diff --git a/apps/web/src/app/features/page.tsx b/apps/web/src/app/features/page.tsx deleted file mode 100644 index fe92e8ea0..000000000 --- a/apps/web/src/app/features/page.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { Metadata } from 'next'; -import { redirect } from 'next/navigation'; - -export const metadata: Metadata = { - title: 'UVAI', - description: 'Redirect to UVAI home.', - alternates: { canonical: '/' }, - robots: { index: false, follow: true }, -}; - -export default function FeaturesRedirect() { - redirect('/'); -} diff --git a/apps/web/src/app/playground/page.tsx b/apps/web/src/app/playground/page.tsx deleted file mode 100644 index 8e0c3da39..000000000 --- a/apps/web/src/app/playground/page.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { Metadata } from 'next'; -import { redirect } from 'next/navigation'; - -export const metadata: Metadata = { - title: 'UVAI', - description: 'Redirect to UVAI home.', - alternates: { canonical: '/' }, - robots: { index: false, follow: true }, -}; - -export default function PlaygroundRedirect() { - redirect('/'); -} diff --git a/apps/web/src/app/prototype/layout.tsx b/apps/web/src/app/prototype/layout.tsx deleted file mode 100644 index 03ddb3bdb..000000000 --- a/apps/web/src/app/prototype/layout.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { Metadata } from 'next'; - -export const metadata: Metadata = { - title: 'Prototype spec', - description: - 'Internal prototype spec for the UVAI build flow. Not indexed.', - robots: { index: false, follow: false, nocache: true }, -}; - -export default function PrototypeLayout({ children }: { children: React.ReactNode }) { - return children; -} diff --git a/apps/web/src/app/prototype/page.tsx b/apps/web/src/app/prototype/page.tsx deleted file mode 100644 index 417334fbc..000000000 --- a/apps/web/src/app/prototype/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { redirect } from 'next/navigation'; -import { CANONICAL_STUDIO_PATH } from '@/lib/auth-paths'; - -/** - * The former route was an interactive simulation that could be mistaken for a - * verified workflow. Keep old bookmarks working while routing users to the - * canonical OneLoopStudio workbench. - */ -export default function PrototypeRedirect() { - redirect(CANONICAL_STUDIO_PATH); -} diff --git a/apps/web/src/components/AgentDashboard.tsx b/apps/web/src/components/AgentDashboard.tsx deleted file mode 100644 index 1cd8962ae..000000000 --- a/apps/web/src/components/AgentDashboard.tsx +++ /dev/null @@ -1,127 +0,0 @@ -'use client'; - -import { clsx } from 'clsx'; -import { Loader2, RefreshCw, Check, X } from 'lucide-react'; -import type { AgentExecution } from '@/lib/types'; - -interface AgentDashboardProps { - executions: AgentExecution[]; - loading?: boolean; - className?: string; -} - -const STATUS_STYLES: Record = { - queued: { bg: 'bg-white/[0.03]', text: 'text-white/50', dot: 'bg-white/30' }, - running: { bg: 'bg-primary-500/5', text: 'text-primary-400', dot: 'bg-primary-400' }, - complete: { bg: 'bg-green-500/5', text: 'text-green-400', dot: 'bg-green-400' }, - failed: { bg: 'bg-red-500/5', text: 'text-red-400', dot: 'bg-red-400' }, -}; - -/** - * Renders the agent execution dashboard. - * - * @param executions - Agent execution records to display. - * @param className - Additional classes applied to the top-level container. - * @returns The dashboard content, or `null` when there are no executions and loading is disabled. - */ -export default function AgentDashboard({ executions, loading, className }: AgentDashboardProps) { - if (loading) { - return ( -
-

- Agent Executions -

-
-
-
- ); - } - - if (executions.length === 0) return null; - - const complete = executions.filter((e) => e.status === 'complete').length; - const running = executions.filter((e) => e.status === 'running').length; - const failed = executions.filter((e) => e.status === 'failed').length; - - return ( -
-
-

- Agent Executions -

-
- {running > 0 && ( - - - )} - {complete > 0 && ( - - - )} - {failed > 0 && ( - - - )} -
-
- -
- {executions.map((exec) => { - const style = STATUS_STYLES[exec.status] || STATUS_STYLES.queued; - return ( -
-
-
- - {exec.agent_type} -
- - {exec.status} - -
- - {/* Progress bar */} -
-
-
- - {/* Result preview */} - {exec.result && ( -

- {String(exec.result.summary || exec.result.output || JSON.stringify(exec.result).slice(0, 120))} -

- )} - {exec.error && ( -

{exec.error}

- )} -
- ); - })} -
-
- ); -} diff --git a/apps/web/src/components/EventList.tsx b/apps/web/src/components/EventList.tsx deleted file mode 100644 index 5bd9af9d8..000000000 --- a/apps/web/src/components/EventList.tsx +++ /dev/null @@ -1,121 +0,0 @@ -'use client'; - -import { clsx } from 'clsx'; -import { Zap, Pin, Code2, AlertTriangle, Loader2, type LucideIcon } from 'lucide-react'; -import type { ExtractedEvent } from '@/lib/types'; - -interface EventListProps { - events: ExtractedEvent[]; - loading?: boolean; - onExtract?: () => void; - className?: string; -} - -// Event Classification Taxonomy: ACTION (blue), TOPIC (purple), CODE (green), ALERT (red) -// Legacy types (mention, insight) map to the new taxonomy for backward compat -const TYPE_STYLES: Record = { - action: { bg: 'bg-blue-500/10 border-blue-500/20', text: 'text-blue-400', icon: Zap }, - topic: { bg: 'bg-purple-500/10 border-purple-500/20', text: 'text-purple-400', icon: Pin }, - code: { bg: 'bg-green-500/10 border-green-500/20', text: 'text-green-400', icon: Code2 }, - alert: { bg: 'bg-red-500/10 border-red-500/20', text: 'text-red-400', icon: AlertTriangle }, - // Legacy type mappings - mention: { bg: 'bg-purple-500/10 border-purple-500/20', text: 'text-purple-400', icon: Pin }, - insight: { bg: 'bg-red-500/10 border-red-500/20', text: 'text-red-400', icon: AlertTriangle }, -}; - -/** - * Displays a list of extracted events with loading and empty states. - * - * @param events - The events to display. - * @param loading - Whether the event extraction state is being shown. - * @param onExtract - Called when the extract events button is clicked. - * @param className - Additional classes for the container. - * @returns The rendered event list. - */ -export default function EventList({ events, loading, onExtract, className }: EventListProps) { - if (loading) { - return ( -
-

- Extracted Events -

-
-
-
- ); - } - - return ( -
-
-

- Extracted Events -

- {events.length === 0 && onExtract && ( - - )} - {events.length > 0 && ( - {events.length} events - )} -
- - {events.length === 0 && !onExtract && ( -

No events extracted yet.

- )} - -
- {events.map((event) => { - const style = TYPE_STYLES[event.type] || TYPE_STYLES.topic; - const Icon = style.icon; - return ( -
-
-
-
- ); - })} -
-
- ); -} diff --git a/apps/web/src/components/FeedbackWidget.tsx b/apps/web/src/components/FeedbackWidget.tsx deleted file mode 100644 index 66024f0e3..000000000 --- a/apps/web/src/components/FeedbackWidget.tsx +++ /dev/null @@ -1,159 +0,0 @@ -'use client'; - -import { useState, useCallback, useId } from 'react'; -import { Check } from 'lucide-react'; -import { submitFeedback } from '@/lib/feedback'; - -interface FeedbackWidgetProps { - videoId: string; - tab: string; - compact?: boolean; -} - -/** - * Displays a star-based feedback form for a video tab and shows a success state after submission. - * - * @param videoId - The video identifier included with the feedback submission. - * @param tab - The current tab name included with the feedback submission. - */ -export default function FeedbackWidget({ videoId, tab, compact = false }: FeedbackWidgetProps) { - const [rating, setRating] = useState(0); - const [hoveredStar, setHoveredStar] = useState(0); - const [comment, setComment] = useState(''); - const [submitted, setSubmitted] = useState(false); - const [submitting, setSubmitting] = useState(false); - const [submitError, setSubmitError] = useState(null); - const [expanded, setExpanded] = useState(false); - const commentId = useId(); - - const handleSubmit = useCallback(async () => { - if (rating === 0) return; - setSubmitting(true); - setSubmitError(null); - try { - await submitFeedback({ - videoId, - tab, - rating, - comment: comment.trim() || undefined, - }); - setSubmitted(true); - } catch (err) { - console.error('Feedback submission failed:', err); - setSubmitError('Feedback could not be saved. Please try again.'); - } finally { - setSubmitting(false); - } - }, [videoId, tab, rating, comment]); - - if (submitted) { - return ( -
-
- ); - } - - return ( -
-
- - Rate this {tab} - - - {/* Star rating */} -
- {[1, 2, 3, 4, 5].map((star) => ( - - ))} -
-
- - {/* Expandable comment area */} - {expanded && ( -
- -