diff --git a/apps/desktop/src/renderer/src/main.tsx b/apps/desktop/src/renderer/src/main.tsx index d71be06d..11de77fa 100644 --- a/apps/desktop/src/renderer/src/main.tsx +++ b/apps/desktop/src/renderer/src/main.tsx @@ -126,6 +126,115 @@ const FilterCodexIcon = () => ( ); +// Navigation Shell Types and Icons +type Route = "dashboard" | "pets" | "settings" | "plugins" | "integrations" | "onboarding"; + +const DashboardIcon = () => ( + + + + + + +); + +const PetsIcon = () => ( + + + + + + +); + +const SettingsIcon = () => ( + + + + +); + +const PluginsIcon = () => ( + + + + +); + +const IntegrationsIcon = () => ( + + + + + +); + +const OnboardingIcon = () => ( + + + + + +); + +const navTabs = [ + { id: "dashboard" as const, label: "Dashboard", icon: }, + { id: "pets" as const, label: "Pets", icon: }, + { id: "settings" as const, label: "Settings", icon: }, + { id: "plugins" as const, label: "Plugins", icon: }, + { id: "integrations" as const, label: "Integrations", icon: }, + { id: "onboarding" as const, label: "Onboarding", icon: }, +]; + +const routeMetadata: Record = { + dashboard: { + title: "Dashboard", + description: "Overview of your active companions, status, and system metrics.", + }, + pets: { + title: "Pets", + description: "Install, import, preview, and choose your default desktop companion.", + }, + settings: { + title: "Settings", + description: "Configure startup behaviors, scale preferences, and animation settings.", + }, + plugins: { + title: "Plugins", + description: "Extend your desktop experience with custom tools and behaviors.", + }, + integrations: { + title: "Integrations", + description: "Connect your companions to Claude Code, VS Code, Cursor, and more.", + }, + onboarding: { + title: "Onboarding", + description: "A quick walkthrough to configure and personalize your first pet.", + }, +}; + +function PlaceholderView({ route }: { route: Exclude }) { + const meta = routeMetadata[route]; + return ( +
+ +
+ {route === "dashboard" && } + {route === "settings" && } + {route === "plugins" && } + {route === "integrations" && } + {route === "onboarding" && } +
+

{meta.title}

+

{meta.description}

+ + Coming Soon • Next Migration Target + +
+
+ ); +} + const filterIcons: Record = { all: , installed: , @@ -279,6 +388,7 @@ function PetImage({ src, alt = "", debugLabel }: { src?: string; alt?: string; d } function App() { + const [currentRoute, setCurrentRoute] = useState("pets"); const [state, setState] = useState(null); const [catalog, setCatalog] = useState(null); const [catalogPages, setCatalogPages] = useState>({}); @@ -456,181 +566,202 @@ function App() { finally { setBusy(""); } } + const currentMeta = routeMetadata[currentRoute]; + return

Control Center Preview

-

Pets

-

Install, import, preview, and choose your default desktop companion.

+

{currentMeta.title}

+

{currentMeta.description}

OpenPets
+ + + {error &&
{error}
} -
- -
setQuery(e.target.value)} />
-
- {(["all", "installed", "featured", "originals", "western", "asian", "codex"] as Filter[]).map((f) => ( - - ))} -
-
{pets.map((pet) => { - const isBuiltIn = pet.builtIn; - const hasDistinctPreview = pet.preview && pet.preview !== pet.spritesheet; - const useSpritesheetFrame = !isBuiltIn && !hasDistinctPreview && !!pet.spritesheet; - return ( - + ))} +
+
{pets.map((pet) => { + const isBuiltIn = pet.builtIn; + const hasDistinctPreview = pet.preview && pet.preview !== pet.spritesheet; + const useSpritesheetFrame = !isBuiltIn && !hasDistinctPreview && !!pet.spritesheet; + return ( +
- - ); - })}
-
- {!!catalog?.pageCount && catalog.pageCount > 1 ? ( - - ) : } - {pets.length} pets{!!catalog?.pageCount && catalog.pageCount > 1 ? ` · Page ${catalogPage + 1} of ${catalog.pageCount}` : ""} - {!!catalog?.pageCount && catalog.pageCount > 1 ? ( - - ) : } -
- - - {selected ? <>

Pet detail

{selected.displayName}

{selected.description || selected.id}

-
- {safePetImage(selected.spritesheet) ? ( - - ) : ( - - )} -
-
- {selected.broken && Broken} - {selected.installed && !selected.broken && Ready} - {selected.builtIn && Originals} - {selected.original && !selected.builtIn && Original} - {selected.featured && !selected.original && Featured} - {selected.category === "western" && !selected.original && !selected.featured && Western} - {selected.category === "asian" && !selected.original && !selected.featured && Asian} -
- {statusText &&

{statusText}

} - - {safePetImage(selected.spritesheet) && ( - <> -

Preview Animations

-
- {[ - { label: "Thinking", state: "thinking" as const }, - { label: "Happy", state: "happy" as const }, - { label: "Wave", state: "wave" as const } - ].map((preview) => ( -
- - {preview.label} -
- ))} -
- - )} - -
- {/* Main Action (Install, Import, Set Default) */} - {!selected.installed && selected.sourceKind === "catalog" && ( - - )} - {!selected.installed && selected.sourceKind === "codex" && ( - - )} - {selected.installed && selected.id !== defaultId && !selected.broken && ( - - )} - - {/* Secondary Actions (Remove, Refresh) */} -
- {selected.installed && !selected.builtIn && !selected.protected && ( - - )} +
+ + {pet.displayName} + +

{pet.description || pet.id}

+
{pet.id === defaultId && Default}{pet.original || pet.builtIn ? Original : pet.featured ? Featured : null}{pet.category === "western" && !pet.original && !pet.featured && Western}{pet.category === "asian" && !pet.original && !pet.featured && Asian}{pet.installed && Installed}{pet.sourceKind === "codex" && Codex}
+
+ + ); + })}
+
+ {!!catalog?.pageCount && catalog.pageCount > 1 ? ( + ) : } + {pets.length} pets{!!catalog?.pageCount && catalog.pageCount > 1 ? ` · Page ${catalogPage + 1} of ${catalog.pageCount}` : ""} + {!!catalog?.pageCount && catalog.pageCount > 1 ? ( + + ) : } +
+ + + {selected ? <>

Pet detail

{selected.displayName}

{selected.description || selected.id}

+
+ {safePetImage(selected.spritesheet) ? ( + + ) : ( + + )}
-
:

No pets available.

} -
- +
+ {selected.broken && Broken} + {selected.installed && !selected.broken && Ready} + {selected.builtIn && Originals} + {selected.original && !selected.builtIn && Original} + {selected.featured && !selected.original && Featured} + {selected.category === "western" && !selected.original && !selected.featured && Western} + {selected.category === "asian" && !selected.original && !selected.featured && Asian} +
+ {statusText &&

{statusText}

} + + {safePetImage(selected.spritesheet) && ( + <> +

Preview Animations

+
+ {[ + { label: "Thinking", state: "thinking" as const }, + { label: "Happy", state: "happy" as const }, + { label: "Wave", state: "wave" as const } + ].map((preview) => ( +
+ + {preview.label} +
+ ))} +
+ + )} + +
+ {/* Main Action (Install, Import, Set Default) */} + {!selected.installed && selected.sourceKind === "catalog" && ( + + )} + {!selected.installed && selected.sourceKind === "codex" && ( + + )} + {selected.installed && selected.id !== defaultId && !selected.broken && ( + + )} + + {/* Secondary Actions (Remove, Refresh) */} +
+ {selected.installed && !selected.builtIn && !selected.protected && ( + + )} + +
+
:

No pets available.

} + + + )}
; } diff --git a/apps/desktop/src/renderer/src/styles.css b/apps/desktop/src/renderer/src/styles.css index adfcb368..0e8b5de2 100644 --- a/apps/desktop/src/renderer/src/styles.css +++ b/apps/desktop/src/renderer/src/styles.css @@ -14,7 +14,7 @@ .hero { @apply mb-6 flex items-center justify-between gap-6; } .hero-content { @apply flex flex-col justify-center; } .hero h1 { @apply m-0 font-monoDisplay text-5xl font-black tracking-tight text-navy leading-none; } - .hero-desc { @apply m-0 mt-1.5 text-sm text-slatecopy max-w-md font-medium; } + .hero-desc { @apply m-0 mt-1.5 min-h-10 max-w-md text-sm font-medium leading-5 text-slatecopy; } .hero-logo-container { @apply flex shrink-0 items-center justify-end; } .hero-brand-logo { @apply h-28 w-auto max-w-[480px] object-contain select-none; filter: drop-shadow(0 6px 12px rgba(61, 99, 160, 0.10)); } .eyebrow { @apply mb-1.5 font-monoDisplay text-xs font-black uppercase tracking-[.18em] text-brand; } @@ -77,4 +77,12 @@ .btn-icon { @apply h-4 w-4 stroke-[2.5]; } .pager-text { @apply text-xs font-bold uppercase tracking-wider text-slatecopy/80; } .error { @apply mb-4 rounded-2xl border border-red-200 bg-red-50 px-4 py-3 text-red-700; } + + .nav-bar { @apply mb-5 flex items-center gap-1.5 border-b border-blue-200/30 pb-3.5 flex-wrap; } + .nav-tab { @apply rounded-xl border px-3.5 py-2 font-monoDisplay text-xs font-black uppercase tracking-wider text-slate-600 active:scale-[0.96] transition-[transform,background-color,border-color,box-shadow] duration-150 flex items-center gap-2 cursor-pointer select-none h-9 shadow-sm; border-color: rgba(126, 161, 210, 0.25); background: linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, rgba(241, 245, 249, 0.8) 100%); box-shadow: inset 0 1px 0 rgba(255,255,255,1), 0 2px 4px rgba(61,99,160,0.04); } + .nav-tab:hover:not(.active) { @apply text-brand; border-color: rgba(37, 99, 235, 0.3); background: linear-gradient(180deg, rgba(255, 255, 255, 0.95) 0%, rgba(239, 246, 255, 0.8) 100%); box-shadow: inset 0 1px 0 rgba(255,255,255,1), 0 2px 4px rgba(61,99,160,0.06); } + .nav-tab.active { @apply text-white; border-color: rgba(29, 78, 216, 0.5); background: linear-gradient(180deg, #3b96ff 0%, #176df2 100%); box-shadow: inset 0 1.5px 0 rgba(255,255,255,0.35), 0 2px 4px rgba(61,99,160,0.10); } + .nav-tab.active:hover { background: linear-gradient(180deg, #55a6ff 0%, #176df2 100%); } + .nav-icon { @apply h-3.5 w-3.5 stroke-[2.5] opacity-75 shrink-0; } + .nav-tab.active .nav-icon { @apply opacity-100; } }