Spaces:
Running
Running
File size: 6,266 Bytes
5f3e9f5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | import { Link } from 'react-router-dom'
import { ArrowRight, Code2, FileText, Image as ImageIcon, Images, Info } from 'lucide-react'
const TOOLS = [
{
to: '/workspace/text',
title: 'Text β Video',
tagline: 'Guided 6-step wizard',
description:
'Paste source text, pick project metadata, tune AI + rendering, and start a run. The richest flow β exposes every backend knob.',
icon: FileText,
accent: 'brand',
highlights: [
'AI content generation',
'Full wizard + preflight',
],
},
{
to: '/workspace/html',
title: 'HTML β Video',
tagline: 'Skip the AI step',
description:
'Paste or upload raw HTML. Backed by Playwright rendering. Use it when you already have a layout and just want crisp screenshots.',
icon: Code2,
accent: 'sky',
highlights: ['Beautify / minify', 'Bring your own HTML', 'Instant render'],
},
{
to: '/workspace/image',
title: 'Image / PDF β Video',
tagline: 'Vision AI extraction',
description:
'Upload a photo, screenshot, or PDF. Vision AI extracts the text, formats it as HTML, then renders screenshots. Up to 10 PDF pages per run.',
icon: ImageIcon,
accent: 'violet',
highlights: ['OCR via Vision AI', 'PDF support', 'Automatic cleanup'],
},
{
to: '/workspace/screenshots',
title: 'Screenshots β Video',
tagline: 'Skip AI + capture',
description:
'Upload screenshots you already have and run only the MP4 / PPTX export. Same canonical filename + queue + ETA tracking as Text β Video.',
icon: Images,
accent: 'amber',
highlights: ['Drag-drop multiple PNG/JPG', 'Same export pipeline', 'Canonical filenames'],
},
] as const
const ACCENT_CLASSES: Record<
string,
{ border: string; bg: string; text: string; ring: string }
> = {
brand: {
border: 'border-brand-200 dark:border-brand-500/30',
bg: 'bg-brand-50 dark:bg-brand-500/10',
text: 'text-brand-700 dark:text-brand-200',
ring: 'group-hover:ring-brand-200 dark:group-hover:ring-brand-500/30',
},
sky: {
border: 'border-sky-200 dark:border-sky-500/30',
bg: 'bg-sky-50 dark:bg-sky-500/10',
text: 'text-sky-700 dark:text-sky-200',
ring: 'group-hover:ring-sky-200 dark:group-hover:ring-sky-500/30',
},
violet: {
border: 'border-violet-200 dark:border-violet-500/30',
bg: 'bg-violet-50 dark:bg-violet-500/10',
text: 'text-violet-700 dark:text-violet-200',
ring: 'group-hover:ring-violet-200 dark:group-hover:ring-violet-500/30',
},
amber: {
border: 'border-amber-200 dark:border-amber-500/30',
bg: 'bg-amber-50 dark:bg-amber-500/10',
text: 'text-amber-700 dark:text-amber-200',
ring: 'group-hover:ring-amber-200 dark:group-hover:ring-amber-500/30',
},
}
export default function Workspace() {
return (
<div className="container-page space-y-10">
<header>
<div className="eyebrow">
<span className="h-1 w-1 rounded-full bg-brand-500" />
Workspace
</div>
<h1 className="mt-3 h-page">Pick a tool to start a run</h1>
<p className="mt-2 max-w-2xl text-[14.5px] text-muted">
All four tools share the same MP4 / PPTX export pipeline β they only differ in
how the screenshots are produced (or, for Screenshots β Video, where they come from).
</p>
</header>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
{TOOLS.map((t) => {
const c = ACCENT_CLASSES[t.accent]
return (
<Link
key={t.to}
to={t.to}
className={
'group surface relative flex flex-col gap-5 p-6 transition-all duration-150 ' +
'hover:-translate-y-0.5 hover:shadow-glass-lg ring-1 ring-transparent ' +
c.ring
}
>
<div
className={`flex h-11 w-11 items-center justify-center rounded-lg border ${c.border} ${c.bg} ${c.text}`}
>
<t.icon size={20} />
</div>
<div>
<div
className={`text-[10.5px] font-semibold uppercase tracking-[0.14em] ${c.text}`}
>
{t.tagline}
</div>
<div className="mt-1.5 font-display text-[17px] font-semibold tracking-tight text-[rgb(var(--text-strong))]">
{t.title}
</div>
<p className="mt-2 text-[13.5px] leading-relaxed text-muted">
{t.description}
</p>
</div>
<ul className="space-y-1.5 text-[12.5px] text-muted">
{t.highlights.map((h) => (
<li key={h} className="flex items-center gap-2">
<span className="h-1 w-1 rounded-full bg-[rgb(var(--text-faint))]" />
{h}
</li>
))}
</ul>
<div
className={`mt-auto inline-flex items-center gap-1 text-[13px] font-medium ${c.text}`}
>
Open
<ArrowRight
size={14}
className="transition-transform group-hover:translate-x-0.5"
/>
</div>
</Link>
)
})}
</div>
<aside
className="surface flex items-start gap-3 p-5"
aria-label="How tools share state"
>
<div
className="mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-faint"
style={{ backgroundColor: 'rgb(var(--bg-muted))' }}
>
<Info size={15} />
</div>
<div className="min-w-0 flex-1 text-[13.5px]">
<div className="font-medium text-[rgb(var(--text-strong))]">
How tools share state
</div>
<p className="mt-1 leading-relaxed text-muted">
Every run is logged to your local <em>Processes</em> timeline and
served assets land in the <em>Library</em>. Pick one of the three
tools above β the output ends up in the same place either way.
</p>
</div>
</aside>
</div>
)
}
|