{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "template-workspace",
  "title": "Parcel: productivity workspace",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "A five-app productivity suite in one shell: a Today page with agenda, tasks and a focus timer, plus a full mail client, week calendar, drag-and-drop board and settings. A tab bar on phones.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.ballmac.com/r/calendar-1.json",
    "https://ui.ballmac.com/r/command.json",
    "https://ui.ballmac.com/r/kanban-1.json",
    "https://ui.ballmac.com/r/mail-1.json",
    "https://ui.ballmac.com/r/settings-1.json"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/templates/workspace/workspace-fonts.ts",
      "content": "// Ballmac UI: Parcel workspace template fonts. https://ui.ballmac.com/templates/template-workspace\nimport { Albert_Sans } from \"next/font/google\"\n\nexport const workspaceSans = Albert_Sans({ variable: \"--workspace-sans\", subsets: [\"latin\"], display: \"swap\", preload: false })\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/workspace/workspace-fonts.ts"
    },
    {
      "path": "registry/ballmac/components/templates/workspace/workspace-theme.tsx",
      "content": "// Ballmac UI: Parcel workspace template shell. https://ui.ballmac.com/templates/template-workspace\n\"use client\"\n\nimport * as React from \"react\"\nimport { CalendarDays, CheckSquare, Command as CommandIcon, Inbox, LayoutGrid, Plus, Search, Settings } from \"lucide-react\"\n\nimport { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from \"@/components/ballmac/command\"\nimport { workspaceSans } from \"@/components/ballmac/templates/workspace/workspace-fonts\"\nimport { cn } from \"@/lib/utils\"\n\ntype WorkspacePage = \"today\" | \"mail\" | \"calendar\" | \"board\" | \"settings\"\ntype WorkspaceHrefs = Record<WorkspacePage, string>\n\nconst defaultHrefs: WorkspaceHrefs = { today: \"/workspace\", mail: \"/workspace/mail\", calendar: \"/workspace/calendar\", board: \"/workspace/board\", settings: \"/workspace/settings\" }\n\n/** Parcel's palette: cool paper and navy ink with one blue and a touch of amber. */\nconst workspaceCss = `\n.workspace-theme,body:not(:has([data-gallery])):has(.workspace-theme){--background:oklch(0.982 0.004 250);--foreground:oklch(0.21 0.03 262);--card:oklch(1 0 0);--card-foreground:oklch(0.21 0.03 262);--popover:oklch(1 0 0);--popover-foreground:oklch(0.21 0.03 262);--primary:oklch(0.28 0.05 262);--primary-foreground:oklch(0.985 0.004 250);--secondary:oklch(0.958 0.008 250);--secondary-foreground:oklch(0.21 0.03 262);--muted:oklch(0.958 0.008 250);--muted-foreground:oklch(0.48 0.03 258);--accent:oklch(0.94 0.012 250);--accent-foreground:oklch(0.21 0.03 262);--border:oklch(0.21 0.03 262 / 11%);--input:oklch(0.21 0.03 262 / 16%);--ring:oklch(0.5 0.17 258);--surface:oklch(0.966 0.007 250);--destructive:oklch(0.54 0.21 25);--chart-1:oklch(0.5 0.17 258);--chart-2:oklch(0.5 0.12 165);--chart-3:oklch(0.62 0.14 72);--chart-4:oklch(0.5 0.16 310);--chart-5:oklch(0.52 0.15 25);--radius:0.75rem}\n.dark .workspace-theme,.dark body:not(:has([data-gallery])):has(.workspace-theme){--background:oklch(0.17 0.02 262);--foreground:oklch(0.95 0.008 250);--card:oklch(0.21 0.024 262);--card-foreground:oklch(0.95 0.008 250);--popover:oklch(0.23 0.026 262);--popover-foreground:oklch(0.95 0.008 250);--primary:oklch(0.95 0.008 250);--primary-foreground:oklch(0.21 0.03 262);--secondary:oklch(0.26 0.026 262);--secondary-foreground:oklch(0.95 0.008 250);--muted:oklch(0.25 0.026 262);--muted-foreground:oklch(0.73 0.025 255);--accent:oklch(0.28 0.03 262);--accent-foreground:oklch(0.95 0.008 250);--border:oklch(1 0 0 / 10%);--input:oklch(1 0 0 / 14%);--ring:oklch(0.72 0.14 258);--surface:oklch(0.19 0.022 262);--destructive:oklch(0.7 0.19 25);--chart-1:oklch(0.74 0.13 258);--chart-2:oklch(0.78 0.13 165);--chart-3:oklch(0.82 0.13 80);--chart-4:oklch(0.75 0.13 310);--chart-5:oklch(0.76 0.13 25)}\nbody:not(:has([data-gallery])):has(.workspace-theme){font-family:var(--workspace-sans),ui-sans-serif,system-ui,sans-serif}\n`\n\nconst apps: { key: WorkspacePage; label: string; icon: typeof Inbox }[] = [\n  { key: \"today\", label: \"Today\", icon: LayoutGrid },\n  { key: \"mail\", label: \"Mail\", icon: Inbox },\n  { key: \"calendar\", label: \"Calendar\", icon: CalendarDays },\n  { key: \"board\", label: \"Board\", icon: CheckSquare },\n  { key: \"settings\", label: \"Settings\", icon: Settings },\n]\n\nfunction ParcelMark({ className }: { className?: string }) {\n  return (\n    <svg viewBox=\"0 0 28 28\" aria-hidden=\"true\" className={cn(\"size-7\", className)} fill=\"none\">\n      <rect width=\"28\" height=\"28\" rx=\"8\" fill=\"var(--chart-1)\" />\n      <path d=\"M14 6 21 10v8l-7 4-7-4v-8z\" stroke=\"var(--card)\" strokeWidth=\"2\" strokeLinejoin=\"round\" />\n      <path d=\"M7 10l7 4 7-4M14 14v8\" stroke=\"var(--card)\" strokeWidth=\"2\" strokeLinejoin=\"round\" />\n    </svg>\n  )\n}\n\ntype WorkspaceShellProps = Omit<React.ComponentProps<\"div\">, \"title\"> & {\n  /** The app being shown, so its icon is marked current. */\n  page: WorkspacePage\n  /** Override where pages live (used by previews). */\n  hrefs?: Partial<WorkspaceHrefs>\n  /** Let the content fill the window height (mail, calendar and board do). */\n  fill?: boolean\n}\n\n/** Parcel's frame: an app rail (a tab bar on phones), a search and quick-create bar, and a command menu on ⌘K. */\nfunction WorkspaceShell({ page, hrefs: overrides, fill, className, style, children, ...props }: WorkspaceShellProps) {\n  const hrefs = { ...defaultHrefs, ...overrides }\n  const [palette, setPalette] = React.useState(false)\n  React.useEffect(() => {\n    const cls = workspaceSans.variable\n    if (!cls) return\n    document.body.classList.add(cls)\n    return () => document.body.classList.remove(cls)\n  }, [])\n  const current = apps.find((a) => a.key === page)!\n  return (\n    <div\n      data-slot=\"workspace\"\n      className={cn(\"workspace-theme bg-background text-foreground flex min-h-dvh flex-col md:flex-row\", fill && \"h-dvh overflow-hidden\", workspaceSans.variable, className)}\n      style={{ fontFamily: \"var(--workspace-sans), ui-sans-serif, system-ui, sans-serif\", ...style }}\n      {...props}\n    >\n      <style>{workspaceCss}</style>\n      <nav aria-label=\"Apps\" className=\"bg-surface order-last flex shrink-0 items-center justify-around border-t px-2 py-1.5 md:order-none md:w-[4.5rem] md:flex-col md:justify-start md:gap-1 md:border-t-0 md:border-e md:py-3\">\n        <a href={hrefs.today} aria-label=\"Parcel home\" className=\"focus-visible:ring-ring/50 mb-3 hidden rounded-xl outline-none focus-visible:ring-[3px] md:block\"><ParcelMark /></a>\n        {apps.map((a) => (\n          <a key={a.key} href={hrefs[a.key]} aria-current={page === a.key ? \"page\" : undefined} className=\"text-muted-foreground hover:text-foreground hover:bg-accent aria-[current=page]:bg-card aria-[current=page]:text-foreground aria-[current=page]:shadow-sm focus-visible:ring-ring/50 flex min-w-14 flex-col items-center gap-1 rounded-xl px-2 py-1.5 text-[11px] font-medium outline-none transition-colors focus-visible:ring-[3px] md:w-14\">\n            <a.icon className=\"size-5\" aria-hidden=\"true\" />\n            {a.label}\n          </a>\n        ))}\n      </nav>\n      <div className={cn(\"flex min-w-0 flex-1 flex-col\", fill && \"min-h-0\")}>\n        <header className=\"flex h-14 shrink-0 items-center gap-3 border-b px-4 sm:px-6\">\n          <h1 className=\"text-[15px] font-semibold tracking-tight\">{current.label}</h1>\n          <button type=\"button\" onClick={() => setPalette(true)} className=\"text-muted-foreground bg-card hover:bg-accent focus-visible:ring-ring/50 mx-auto flex h-9 w-full max-w-md items-center gap-2.5 rounded-xl border px-3 text-sm outline-none transition-colors focus-visible:ring-[3px]\">\n            <Search className=\"size-4\" aria-hidden=\"true\" />\n            <span className=\"flex-1 text-start\">Search or jump to…</span>\n            <kbd className=\"bg-muted hidden items-center gap-0.5 rounded px-1.5 py-0.5 text-[11px] sm:flex\"><CommandIcon className=\"size-3\" aria-hidden=\"true\" />K</kbd>\n          </button>\n          <a href={hrefs.mail} className=\"bg-primary text-primary-foreground focus-visible:ring-ring/50 inline-flex h-9 shrink-0 items-center gap-1.5 rounded-xl px-3 text-sm font-semibold outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]\"><Plus className=\"size-4\" aria-hidden=\"true\" /><span className=\"hidden sm:inline\">Compose</span><span className=\"sr-only sm:hidden\">Compose</span></a>\n          <span className=\"bg-chart-4/25 flex size-9 shrink-0 items-center justify-center rounded-full text-xs font-bold\" aria-hidden=\"true\">MK</span>\n        </header>\n        <div className={cn(\"flex-1\", fill && \"min-h-0 overflow-hidden\")}>{children}</div>\n      </div>\n      <CommandDialog open={palette} onOpenChange={setPalette} title=\"Search or jump to\">\n        <CommandInput placeholder=\"Search mail, events and tasks…\" />\n        <CommandList>\n          <CommandEmpty>No results.</CommandEmpty>\n          <CommandGroup heading=\"Apps\">\n            {apps.map((a) => <CommandItem key={a.key} value={a.label} onSelect={() => { setPalette(false); window.location.assign(hrefs[a.key]) }}><a.icon aria-hidden=\"true\" /> {a.label}</CommandItem>)}\n          </CommandGroup>\n        </CommandList>\n      </CommandDialog>\n    </div>\n  )\n}\n\nexport { ParcelMark, WorkspaceShell, defaultHrefs as workspaceDefaultHrefs, type WorkspaceHrefs, type WorkspacePage, type WorkspaceShellProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/workspace/workspace-theme.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/workspace/workspace-today.tsx",
      "content": "// Ballmac UI: Parcel today page. https://ui.ballmac.com/templates/template-workspace\n\"use client\"\n\nimport * as React from \"react\"\nimport { Archive, Check, Pause, Play, Plus, Reply, RotateCcw } from \"lucide-react\"\n\nimport { WorkspaceShell, type WorkspaceHrefs } from \"@/components/ballmac/templates/workspace/workspace-theme\"\nimport { cn } from \"@/lib/utils\"\n\ntype Task = { id: string; text: string; done: boolean }\n\nconst startTasks: Task[] = [\n  { id: \"t1\", text: \"Send the Q4 plan to Priya\", done: true },\n  { id: \"t2\", text: \"Review Dev’s onboarding mockups\", done: false },\n  { id: \"t3\", text: \"Book the Lisbon flights\", done: false },\n  { id: \"t4\", text: \"Write Friday’s team update\", done: false },\n]\n\nconst agenda = [\n  { time: \"09:00\", end: \"09:30\", title: \"Design standup\", where: \"Meet\", tone: \"bg-chart-1\" },\n  { time: \"10:30\", end: \"11:30\", title: \"Onboarding review\", where: \"Room 4\", tone: \"bg-chart-2\" },\n  { time: \"13:00\", end: \"13:45\", title: \"Lunch with Amara\", where: \"Bar Lucia\", tone: \"bg-chart-3\" },\n  { time: \"15:00\", end: \"16:00\", title: \"1:1 with Dev\", where: \"Meet\", tone: \"bg-chart-4\" },\n]\n\nconst inbox = [\n  { id: \"m1\", from: \"Priya Raman\", subject: \"Re: Q4 plan — two small edits\", preview: \"Love it. Can we add owners to the risks?\", time: \"8:12 AM\" },\n  { id: \"m2\", from: \"Amara Okafor\", subject: \"Lunch today?\", preview: \"Still on for 13:00 at Bar Lucia?\", time: \"7:48 AM\" },\n  { id: \"m3\", from: \"Billing\", subject: \"Your October invoice\", preview: \"Your invoice for $48.00 is ready.\", time: \"Yesterday\" },\n]\n\nconst NOW = \"09:41\"\nconst toMin = (t: string) => Number(t.slice(0, 2)) * 60 + Number(t.slice(3))\n\nconst card = \"bg-card rounded-2xl border\"\n\ntype WorkspaceTodayProps = React.ComponentProps<\"div\"> & { hrefs?: Partial<WorkspaceHrefs> }\n\n/** The Parcel today page: your agenda with a now line, tasks you can tick and add, inbox highlights you can archive, and a focus timer. */\nfunction WorkspaceToday({ hrefs, ...props }: WorkspaceTodayProps) {\n  const [tasks, setTasks] = React.useState(startTasks)\n  const [draft, setDraft] = React.useState(\"\")\n  const [mail, setMail] = React.useState(inbox)\n  const [secs, setSecs] = React.useState(25 * 60)\n  const [running, setRunning] = React.useState(false)\n  const [note, setNote] = React.useState(\"\")\n\n  React.useEffect(() => {\n    if (!running) return\n    const id = setInterval(() => setSecs((s) => (s <= 1 ? 0 : s - 1)), 1000)\n    return () => clearInterval(id)\n  }, [running])\n  const finished = secs === 0\n  const done = tasks.filter((t) => t.done).length\n  const pct = tasks.length ? (done / tasks.length) * 100 : 0\n  const mm = String(Math.floor(secs / 60)).padStart(2, \"0\")\n  const ss = String(secs % 60).padStart(2, \"0\")\n\n  function add(e: React.FormEvent) {\n    e.preventDefault()\n    const text = draft.trim()\n    if (!text) return\n    setTasks((all) => [...all, { id: `n${all.length}`, text, done: false }])\n    setDraft(\"\")\n  }\n\n  return (\n    <WorkspaceShell page=\"today\" hrefs={hrefs} {...props}>\n      <main className=\"mx-auto max-w-6xl px-4 py-6 pb-10 sm:px-6\">\n        <p className=\"text-muted-foreground text-sm font-medium\">Thursday, October 1</p>\n        <h2 className=\"mt-1 text-3xl font-extrabold tracking-[-0.03em] sm:text-4xl\">Good morning, Mina.</h2>\n        <p className=\"text-muted-foreground mt-2 text-pretty\">You have {agenda.length} meetings, {tasks.length - done} tasks left and {mail.length} messages worth a look.</p>\n\n        <div className=\"mt-8 grid grid-cols-[minmax(0,1fr)] gap-4 lg:grid-cols-3\">\n          <section aria-labelledby=\"wt-agenda\" className={cn(card, \"p-5 lg:row-span-2\")}>\n            <h3 id=\"wt-agenda\" className=\"font-bold\">Agenda</h3>\n            <ol className=\"mt-4 space-y-1\">\n              {agenda.map((e, i) => {\n                const past = toMin(e.end) <= toMin(NOW)\n                const live = toMin(e.time) <= toMin(NOW) && toMin(NOW) < toMin(e.end)\n                const showNow = !past && !live && (i === 0 || toMin(agenda[i - 1].end) <= toMin(NOW))\n                return (\n                  <React.Fragment key={e.title}>\n                    {showNow && (\n                      <li aria-label={`Now, ${NOW}`} className=\"flex items-center gap-2 py-1.5\"><span className=\"bg-destructive size-2 rounded-full\" aria-hidden=\"true\" /><span className=\"bg-destructive/50 h-px flex-1\" aria-hidden=\"true\" /><span className=\"text-destructive text-xs font-semibold tabular-nums\">{NOW}</span></li>\n                    )}\n                    <li className={cn(\"flex gap-3 rounded-xl p-2.5\", live && \"bg-accent\", past && \"text-muted-foreground\")}>\n                      <span className={cn(\"w-1 shrink-0 rounded-full\", e.tone)} aria-hidden=\"true\" />\n                      <div className=\"min-w-0 flex-1\"><p className=\"text-sm font-semibold\">{e.title}</p><p className=\"text-muted-foreground text-xs\">{e.time}–{e.end} · {e.where}</p></div>\n                      {past && <Check className=\"text-chart-2 size-4 shrink-0\" aria-label=\"Done\" />}\n                    </li>\n                  </React.Fragment>\n                )\n              })}\n            </ol>\n          </section>\n\n          <section aria-labelledby=\"wt-tasks\" className={cn(card, \"p-5\")}>\n            <div className=\"flex items-center justify-between\"><h3 id=\"wt-tasks\" className=\"font-bold\">Tasks</h3><p className=\"text-muted-foreground text-sm tabular-nums\" role=\"status\">{done} of {tasks.length} done</p></div>\n            <div className=\"bg-muted mt-3 h-1.5 rounded-full\" aria-hidden=\"true\"><div className=\"bg-chart-2 h-full rounded-full transition-[width] duration-500 motion-reduce:transition-none\" style={{ width: `${pct}%` }} /></div>\n            <ul className=\"mt-4 space-y-1\">\n              {tasks.map((t) => (\n                <li key={t.id}>\n                  <label className=\"hover:bg-accent flex cursor-pointer items-center gap-3 rounded-lg px-2 py-2 text-sm\">\n                    <input type=\"checkbox\" checked={t.done} onChange={() => setTasks((all) => all.map((x) => (x.id === t.id ? { ...x, done: !x.done } : x)))} className=\"accent-chart-1 size-4\" />\n                    <span className={cn(t.done && \"text-muted-foreground line-through\")}>{t.text}</span>\n                  </label>\n                </li>\n              ))}\n            </ul>\n            <form onSubmit={add} className=\"mt-3 flex gap-2\">\n              <label htmlFor=\"wt-new\" className=\"sr-only\">Add a task</label>\n              <input id=\"wt-new\" value={draft} onChange={(e) => setDraft(e.target.value)} placeholder=\"Add a task\" className=\"bg-background focus-visible:ring-ring/50 placeholder:text-muted-foreground h-9 min-w-0 flex-1 rounded-lg border px-3 text-sm outline-none focus-visible:ring-[3px]\" />\n              <button type=\"submit\" aria-label=\"Add task\" className=\"bg-primary text-primary-foreground focus-visible:ring-ring/50 inline-flex size-9 items-center justify-center rounded-lg outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]\"><Plus className=\"size-4\" aria-hidden=\"true\" /></button>\n            </form>\n          </section>\n\n          <section aria-labelledby=\"wt-focus\" className={cn(card, \"flex flex-col items-center p-5 text-center\")}>\n            <h3 id=\"wt-focus\" className=\"self-start font-bold\">Focus</h3>\n            <p className=\"mt-6 text-6xl font-extrabold tracking-[-0.04em] tabular-nums\" role=\"timer\" aria-label={`${mm} minutes ${ss} seconds left`}>{mm}:{ss}</p>\n            <p className=\"text-muted-foreground mt-2 text-sm\" aria-live=\"polite\">{finished ? \"Nice work. Take five.\" : running ? \"Stay with one thing.\" : \"Pick a task and begin.\"}</p>\n            <div className=\"mt-5 flex gap-2\">\n              <button type=\"button\" onClick={() => { if (finished) setSecs(25 * 60); setRunning((r) => !r) }} className=\"bg-primary text-primary-foreground focus-visible:ring-ring/50 inline-flex h-10 items-center gap-2 rounded-xl px-5 text-sm font-semibold outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]\">\n                {running ? <><Pause className=\"size-4\" aria-hidden=\"true\" />Pause</> : <><Play className=\"size-4\" aria-hidden=\"true\" />{secs === 25 * 60 ? \"Start\" : \"Resume\"}</>}\n              </button>\n              <button type=\"button\" aria-label=\"Reset timer\" onClick={() => { setRunning(false); setSecs(25 * 60) }} className=\"hover:bg-accent focus-visible:ring-ring/50 inline-flex size-10 items-center justify-center rounded-xl border outline-none focus-visible:ring-[3px]\"><RotateCcw className=\"size-4\" aria-hidden=\"true\" /></button>\n            </div>\n          </section>\n\n          <section aria-labelledby=\"wt-inbox\" className={cn(card, \"p-5 lg:col-span-2\")}>\n            <div className=\"flex items-center justify-between\"><h3 id=\"wt-inbox\" className=\"font-bold\">Worth a look</h3><a href={hrefs?.mail ?? \"/workspace/mail\"} className=\"text-chart-1 rounded text-sm font-semibold underline underline-offset-4\">Open inbox</a></div>\n            <p className=\"sr-only\" role=\"status\">{note}</p>\n            {mail.length === 0 ? (\n              <p className=\"text-muted-foreground mt-6 rounded-xl border border-dashed p-8 text-center text-sm\">Inbox zero. Enjoy the quiet.</p>\n            ) : (\n              <ul className=\"mt-3 divide-y\">\n                {mail.map((m) => (\n                  <li key={m.id} className=\"flex items-center gap-3 py-3\">\n                    <span className=\"bg-chart-1/15 flex size-9 shrink-0 items-center justify-center rounded-full text-xs font-bold\" aria-hidden=\"true\">{m.from.split(\" \").map((w) => w[0]).join(\"\")}</span>\n                    <div className=\"min-w-0 flex-1\"><p className=\"truncate text-sm\"><span className=\"font-semibold\">{m.from}</span> <span className=\"text-muted-foreground\">· {m.time}</span></p><p className=\"truncate text-sm font-medium\">{m.subject}</p><p className=\"text-muted-foreground truncate text-xs\">{m.preview}</p></div>\n                    <div className=\"flex shrink-0 gap-1\">\n                      <a href={hrefs?.mail ?? \"/workspace/mail\"} aria-label={`Reply to ${m.from}`} className=\"hover:bg-accent focus-visible:ring-ring/50 inline-flex size-8 items-center justify-center rounded-lg outline-none focus-visible:ring-[3px]\"><Reply className=\"size-4 rtl:-scale-x-100\" aria-hidden=\"true\" /></a>\n                      <button type=\"button\" aria-label={`Archive: ${m.subject}`} onClick={() => { setMail((all) => all.filter((x) => x.id !== m.id)); setNote(`Archived ${m.subject}`) }} className=\"hover:bg-accent focus-visible:ring-ring/50 inline-flex size-8 items-center justify-center rounded-lg outline-none focus-visible:ring-[3px]\"><Archive className=\"size-4\" aria-hidden=\"true\" /></button>\n                    </div>\n                  </li>\n                ))}\n              </ul>\n            )}\n          </section>\n        </div>\n      </main>\n    </WorkspaceShell>\n  )\n}\n\nexport { WorkspaceToday, type WorkspaceTodayProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/workspace/workspace-today.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/workspace/workspace-mail.tsx",
      "content": "// Ballmac UI: Parcel mail page. https://ui.ballmac.com/templates/template-workspace\nimport * as React from \"react\"\n\nimport { Mail1 } from \"@/components/ballmac/blocks/mail-1/mail-1\"\nimport { WorkspaceShell, type WorkspaceHrefs } from \"@/components/ballmac/templates/workspace/workspace-theme\"\n\ntype WorkspaceMailProps = React.ComponentProps<\"div\"> & { hrefs?: Partial<WorkspaceHrefs> }\n\n/** Parcel mail: the three-pane inbox with search, compose and keyboard shortcuts, filling the window. */\nfunction WorkspaceMail({ hrefs, ...props }: WorkspaceMailProps) {\n  return (\n    <WorkspaceShell page=\"mail\" hrefs={hrefs} fill {...props}>\n      <Mail1 height=\"100%\" className=\"rounded-none border-0\" />\n    </WorkspaceShell>\n  )\n}\n\nexport { WorkspaceMail, type WorkspaceMailProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/workspace/workspace-mail.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/workspace/workspace-calendar.tsx",
      "content": "// Ballmac UI: Parcel calendar page. https://ui.ballmac.com/templates/template-workspace\nimport * as React from \"react\"\n\nimport { Calendar1 } from \"@/components/ballmac/blocks/calendar-1/calendar-1\"\nimport { WorkspaceShell, type WorkspaceHrefs } from \"@/components/ballmac/templates/workspace/workspace-theme\"\n\ntype WorkspaceCalendarProps = React.ComponentProps<\"div\"> & { hrefs?: Partial<WorkspaceHrefs> }\n\n/** Parcel calendar: the week view with overlapping events, a now line and a new-event dialog, filling the window. */\nfunction WorkspaceCalendar({ hrefs, ...props }: WorkspaceCalendarProps) {\n  return (\n    <WorkspaceShell page=\"calendar\" hrefs={hrefs} fill {...props}>\n      <Calendar1 height=\"100%\" today=\"2026-10-01\" now=\"09:41\" className=\"rounded-none border-0\" />\n    </WorkspaceShell>\n  )\n}\n\nexport { WorkspaceCalendar, type WorkspaceCalendarProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/workspace/workspace-calendar.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/workspace/workspace-board.tsx",
      "content": "// Ballmac UI: Parcel board page. https://ui.ballmac.com/templates/template-workspace\nimport * as React from \"react\"\n\nimport { Kanban1 } from \"@/components/ballmac/blocks/kanban-1/kanban-1\"\nimport { WorkspaceShell, type WorkspaceHrefs } from \"@/components/ballmac/templates/workspace/workspace-theme\"\n\ntype WorkspaceBoardProps = React.ComponentProps<\"div\"> & { hrefs?: Partial<WorkspaceHrefs> }\n\n/** Parcel board: drag cards between columns, move with the keyboard, add and filter by person. */\nfunction WorkspaceBoard({ hrefs, ...props }: WorkspaceBoardProps) {\n  return (\n    <WorkspaceShell page=\"board\" hrefs={hrefs} {...props}>\n      <Kanban1 title=\"Q4 launch\" description=\"Drag cards between columns, or use the Move to menu on any card.\" height=\"calc(100dvh - 15rem)\" />\n    </WorkspaceShell>\n  )\n}\n\nexport { WorkspaceBoard, type WorkspaceBoardProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/workspace/workspace-board.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/workspace/workspace-settings.tsx",
      "content": "// Ballmac UI: Parcel settings page. https://ui.ballmac.com/templates/template-workspace\nimport * as React from \"react\"\n\nimport { Settings1 } from \"@/components/ballmac/blocks/settings-1/settings-1\"\nimport { WorkspaceShell, type WorkspaceHrefs } from \"@/components/ballmac/templates/workspace/workspace-theme\"\n\ntype WorkspaceSettingsProps = React.ComponentProps<\"div\"> & { hrefs?: Partial<WorkspaceHrefs> }\n\n/** Parcel settings: profile, preferences and account actions with a save bar that appears only when something changed. */\nfunction WorkspaceSettings({ hrefs, ...props }: WorkspaceSettingsProps) {\n  return (\n    <WorkspaceShell page=\"settings\" hrefs={hrefs} {...props}>\n      <Settings1 title=\"Your account\" description=\"Manage your profile and how Parcel works for you.\" />\n    </WorkspaceShell>\n  )\n}\n\nexport { WorkspaceSettings, type WorkspaceSettingsProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/workspace/workspace-settings.tsx"
    },
    {
      "path": "registry/ballmac/app/workspace/page.tsx",
      "content": "// Ballmac UI: Workspace template route. https://ui.ballmac.com/templates/template-workspace\nimport type { Metadata } from \"next\"\n\nimport { WorkspaceToday } from \"@/components/ballmac/templates/workspace/workspace-today\"\n\nexport const metadata: Metadata = {\n  title: \"Parcel\",\n  description: \"Mail, calendar and tasks in one calm place.\",\n}\n\nexport default function Page() {\n  return <WorkspaceToday />\n}\n",
      "type": "registry:page",
      "target": "app/workspace/page.tsx"
    },
    {
      "path": "registry/ballmac/app/workspace/mail/page.tsx",
      "content": "// Ballmac UI: Workspace template route. https://ui.ballmac.com/templates/template-workspace\nimport type { Metadata } from \"next\"\n\nimport { WorkspaceMail } from \"@/components/ballmac/templates/workspace/workspace-mail\"\n\nexport const metadata: Metadata = {\n  title: \"Mail · Parcel\",\n  description: \"A fast three-pane inbox.\",\n}\n\nexport default function Page() {\n  return <WorkspaceMail />\n}\n",
      "type": "registry:page",
      "target": "app/workspace/mail/page.tsx"
    },
    {
      "path": "registry/ballmac/app/workspace/calendar/page.tsx",
      "content": "// Ballmac UI: Workspace template route. https://ui.ballmac.com/templates/template-workspace\nimport type { Metadata } from \"next\"\n\nimport { WorkspaceCalendar } from \"@/components/ballmac/templates/workspace/workspace-calendar\"\n\nexport const metadata: Metadata = {\n  title: \"Calendar · Parcel\",\n  description: \"Your week at a glance.\",\n}\n\nexport default function Page() {\n  return <WorkspaceCalendar />\n}\n",
      "type": "registry:page",
      "target": "app/workspace/calendar/page.tsx"
    },
    {
      "path": "registry/ballmac/app/workspace/board/page.tsx",
      "content": "// Ballmac UI: Workspace template route. https://ui.ballmac.com/templates/template-workspace\nimport type { Metadata } from \"next\"\n\nimport { WorkspaceBoard } from \"@/components/ballmac/templates/workspace/workspace-board\"\n\nexport const metadata: Metadata = {\n  title: \"Board · Parcel\",\n  description: \"Tasks on a board you can drag.\",\n}\n\nexport default function Page() {\n  return <WorkspaceBoard />\n}\n",
      "type": "registry:page",
      "target": "app/workspace/board/page.tsx"
    },
    {
      "path": "registry/ballmac/app/workspace/settings/page.tsx",
      "content": "// Ballmac UI: Workspace template route. https://ui.ballmac.com/templates/template-workspace\nimport type { Metadata } from \"next\"\n\nimport { WorkspaceSettings } from \"@/components/ballmac/templates/workspace/workspace-settings\"\n\nexport const metadata: Metadata = {\n  title: \"Settings · Parcel\",\n  description: \"Profile and preferences.\",\n}\n\nexport default function Page() {\n  return <WorkspaceSettings />\n}\n",
      "type": "registry:page",
      "target": "app/workspace/settings/page.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "template",
      "productivity",
      "mail",
      "calendar",
      "kanban",
      "tasks",
      "workspace",
      "app shell"
    ],
    "version": "1.0.0",
    "updated": "2026-10-01",
    "ai": {
      "summary": "Installs a multi-app productivity shell. WorkspaceShell gives every page an app rail (a tab bar on phones) and a command menu; each app page wraps a Ballmac block you can feed with your own data.",
      "whenToUse": [
        "Suites that combine several tools under one navigation",
        "Starting point for team inboxes, planners and internal tools"
      ],
      "whenNotToUse": [
        "A single dashboard (use template-atlas)"
      ],
      "composesWith": [
        "mail-1",
        "calendar-1",
        "kanban-1"
      ],
      "a11y": [
        {
          "keys": "⌘K / Ctrl+K",
          "action": "Opens the command menu to jump between apps"
        },
        {
          "keys": "J / K in mail",
          "action": "Moves between messages"
        }
      ],
      "customization": [
        "Edit workspaceCss for the palette",
        "Add an app to the apps array in workspace-theme.tsx",
        "Pass data props to the Mail1, Calendar1 and Kanban1 blocks"
      ]
    },
    "examples": [
      "template-workspace-demo",
      "template-workspace-mail",
      "template-workspace-calendar",
      "template-workspace-board",
      "template-workspace-settings"
    ],
    "url": "https://ui.ballmac.com/components/template-workspace"
  },
  "docs": "Pages are at /workspace, /workspace/mail, /workspace/calendar, /workspace/board and /workspace/settings. Mail, calendar, board and settings are the Ballmac blocks: pass them your data through their props.",
  "categories": [
    "templates"
  ],
  "type": "registry:block"
}