mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-10-07 02:58:11 +00:00
Match desktop shell to web dashboard
This commit is contained in:
parent
9cd894a8f2
commit
5d7dc85886
6 changed files with 331 additions and 117 deletions
|
|
@ -9,12 +9,12 @@ import { Titlebar } from "@/components/titlebar"
|
|||
export default function AppLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<AuthGuard>
|
||||
<div className="flex h-screen flex-col">
|
||||
<div className="desktop-shell flex h-screen flex-col overflow-hidden">
|
||||
<Titlebar />
|
||||
<div className="flex min-h-0 flex-1">
|
||||
<AppNav />
|
||||
<main className="min-w-0 flex-1 overflow-auto">{children}</main>
|
||||
</div>
|
||||
<AppNav />
|
||||
<main className="relative z-10 min-h-0 flex-1 overflow-auto">
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
</AuthGuard>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import { Button } from "@ui/components/button"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
|
|
@ -9,7 +8,16 @@ import {
|
|||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@ui/components/card"
|
||||
import { Clock, FileText, RefreshCcw, Search } from "lucide-react"
|
||||
import {
|
||||
ArrowRight,
|
||||
Clock,
|
||||
FileText,
|
||||
Link2,
|
||||
Lightbulb,
|
||||
MessageCircle,
|
||||
RefreshCcw,
|
||||
Search,
|
||||
} from "lucide-react"
|
||||
import { useMemo, useState } from "react"
|
||||
import { listDocuments, type DocumentWithMemories } from "@/lib/api"
|
||||
import type { SearchResult } from "@/lib/search"
|
||||
|
|
@ -43,91 +51,163 @@ export default function DashboardPage() {
|
|||
const subtitle = useMemo(() => {
|
||||
if (documentsQuery.isPending) return "Loading your recent memories."
|
||||
if (typeof totalCount === "number") return `${totalCount} memories indexed.`
|
||||
return "Your recent memories."
|
||||
return "Your memory is ready."
|
||||
}, [documentsQuery.isPending, totalCount])
|
||||
|
||||
return (
|
||||
<div className="mx-auto flex h-full w-full max-w-6xl flex-col p-8">
|
||||
<div className="mb-8 flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="font-semibold text-2xl">Your memories</h1>
|
||||
<p className="text-muted-foreground text-sm">{subtitle}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
aria-label="Refresh memories"
|
||||
<div className="min-h-full px-4 pt-2 pb-16 md:px-6">
|
||||
<div className="mx-auto w-full max-w-4xl space-y-5">
|
||||
<header className="flex items-end justify-between gap-4 border-surface-border border-b pb-4">
|
||||
<div className="space-y-0.5">
|
||||
<p className="font-medium text-[10px] text-fg-faint uppercase tracking-[0.12em]">
|
||||
Home
|
||||
</p>
|
||||
<h1 className="max-w-2xl font-medium text-2xl text-white leading-tight tracking-tight">
|
||||
Welcome back, your saved context is ready.
|
||||
</h1>
|
||||
<p className="text-fg-subtle text-sm">{subtitle}</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => documentsQuery.refetch()}
|
||||
disabled={documentsQuery.isFetching}
|
||||
className="group relative hidden h-14 w-36 shrink-0 overflow-hidden rounded-xl border border-surface-border bg-surface-card transition-all hover:scale-[1.02] hover:border-[#3A4A63] md:block"
|
||||
aria-label="Refresh memories"
|
||||
>
|
||||
<div className="absolute inset-0 bg-[radial-gradient(circle_at_28%_50%,rgba(75,160,250,0.35),transparent_22%),radial-gradient(circle_at_58%_42%,rgba(139,198,255,0.22),transparent_18%),radial-gradient(circle_at_78%_62%,rgba(15,240,210,0.16),transparent_16%)]" />
|
||||
<div className="absolute inset-x-0 bottom-0 h-10 bg-gradient-to-t from-black/45 to-transparent" />
|
||||
<RefreshCcw
|
||||
className={documentsQuery.isFetching ? "animate-spin" : undefined}
|
||||
className={[
|
||||
"absolute right-3 bottom-3 size-4 text-[#8BC6FF]",
|
||||
documentsQuery.isFetching ? "animate-spin" : "",
|
||||
].join(" ")}
|
||||
/>
|
||||
</Button>
|
||||
<Button variant="outline" onClick={() => setOpen(true)}>
|
||||
<Search />
|
||||
Search
|
||||
<kbd className="ml-1 rounded bg-muted px-1.5 py-0.5 text-xs">
|
||||
⌘K
|
||||
</kbd>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div className="grid min-h-0 flex-1 grid-cols-1 gap-4 lg:grid-cols-[minmax(320px,420px)_1fr]">
|
||||
<Card className="min-h-0 gap-0 overflow-hidden py-0">
|
||||
<CardHeader className="border-border/60 border-b py-4">
|
||||
<CardTitle className="text-base">Recent</CardTitle>
|
||||
<CardDescription>
|
||||
Latest documents from Supermemory.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="min-h-0 overflow-auto p-2">
|
||||
{documentsQuery.isPending ? (
|
||||
<div className="p-4 text-muted-foreground text-sm">
|
||||
Loading memories...
|
||||
<section className="space-y-2">
|
||||
<p className="font-medium text-[10px] text-fg-faint uppercase tracking-[0.12em]">
|
||||
Daily brief
|
||||
</p>
|
||||
<div className="grid gap-3 sm:grid-cols-[1fr_220px]">
|
||||
<div className="rounded-[18px] bg-surface-card/60 p-3 shadow-[0_12px_40px_rgba(0,0,0,0.22)] backdrop-blur-md">
|
||||
<div className="flex min-h-[132px] flex-col justify-between gap-4">
|
||||
<div className="space-y-2">
|
||||
<p className="font-medium text-fg-primary text-sm">
|
||||
{activeMemory?.title ?? "No memory selected yet"}
|
||||
</p>
|
||||
<p className="line-clamp-3 text-[12px] text-fg-subtle leading-relaxed">
|
||||
{activeMemory?.summary ??
|
||||
activeMemory?.content ??
|
||||
"Save or search memories and Nova will surface the most useful context here."}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(true)}
|
||||
className="text-[11px] text-fg-subtle transition-colors hover:text-fg-primary"
|
||||
>
|
||||
Ask or search related →
|
||||
</button>
|
||||
<MessageCircle className="size-4 text-[#4BA0FA]" />
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{documentsQuery.isError ? (
|
||||
<div className="p-4 text-destructive text-sm">
|
||||
{documentsQuery.error.message}
|
||||
</div>
|
||||
) : null}
|
||||
{!documentsQuery.isPending && documents.length === 0 ? (
|
||||
<div className="p-4 text-muted-foreground text-sm">
|
||||
No memories found.
|
||||
</div>
|
||||
) : null}
|
||||
<div className="space-y-1">
|
||||
{documents.map((document) => (
|
||||
<button
|
||||
type="button"
|
||||
key={document.id}
|
||||
onClick={() => setSelectedMemory(toMemoryPreview(document))}
|
||||
className={[
|
||||
"flex w-full items-start gap-3 rounded-md px-3 py-2 text-left transition-colors",
|
||||
activeMemory?.id === document.id
|
||||
? "bg-accent text-accent-foreground"
|
||||
: "hover:bg-accent/50",
|
||||
].join(" ")}
|
||||
>
|
||||
<FileText className="mt-0.5 size-4 shrink-0 text-muted-foreground" />
|
||||
<span className="min-w-0">
|
||||
<span className="block truncate font-medium text-sm">
|
||||
{document.title ?? document.url ?? "Untitled memory"}
|
||||
</span>
|
||||
<span className="mt-1 line-clamp-2 block text-muted-foreground text-xs">
|
||||
{document.summary ?? document.content ?? document.type}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="hidden rounded-[18px] bg-surface-card/60 p-3 shadow-[0_12px_40px_rgba(0,0,0,0.22)] backdrop-blur-md sm:flex sm:flex-col sm:justify-between">
|
||||
<span className="self-start rounded-full bg-[#4BA0FA]/16 px-2 py-0.5 font-semibold text-[#8BC6FF] text-[9px] uppercase tracking-[0.12em]">
|
||||
Desktop
|
||||
</span>
|
||||
<p className="text-[12px] text-fg-secondary leading-relaxed">
|
||||
Use Command-K to search your memory without leaving the native
|
||||
shell.
|
||||
</p>
|
||||
<span className="text-[10px] text-fg-faint">Nova-ready</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<DocumentPreview memory={activeMemory} />
|
||||
<section className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div className="grid grid-cols-3 gap-1 sm:-mx-2.5 sm:flex sm:items-center sm:gap-0.5">
|
||||
<QuickAction icon={Link2} label="Save link" />
|
||||
<span className="hidden text-[#3A4455] sm:inline">·</span>
|
||||
<QuickAction icon={FileText} label="Write note" />
|
||||
<span className="hidden text-[#3A4455] sm:inline">·</span>
|
||||
<QuickAction
|
||||
icon={Search}
|
||||
label="Search"
|
||||
onClick={() => setOpen(true)}
|
||||
/>
|
||||
</div>
|
||||
<p className="hidden min-w-0 items-center gap-1.5 overflow-hidden text-[11px] text-fg-subtle sm:flex">
|
||||
<Lightbulb className="size-3 shrink-0 text-[#3374FF]" />
|
||||
<span className="truncate">
|
||||
Search by meaning, not just exact titles.
|
||||
</span>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-2">
|
||||
<div className="flex gap-4">
|
||||
<div className="min-w-0 flex-[4]">
|
||||
<p className="font-medium text-[10px] text-fg-faint uppercase tracking-[0.12em]">
|
||||
Recents
|
||||
</p>
|
||||
</div>
|
||||
<div className="hidden min-w-0 flex-[2] sm:block">
|
||||
<p className="font-medium text-[10px] text-fg-faint uppercase tracking-[0.12em]">
|
||||
Preview
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="min-w-0 flex-[4]">
|
||||
{documentsQuery.isPending ? (
|
||||
<div className="px-2.5 py-2 text-fg-subtle text-sm">
|
||||
Loading memories...
|
||||
</div>
|
||||
) : null}
|
||||
{documentsQuery.isError ? (
|
||||
<div className="px-2.5 py-2 text-destructive text-sm">
|
||||
{documentsQuery.error.message}
|
||||
</div>
|
||||
) : null}
|
||||
{!documentsQuery.isPending && documents.length === 0 ? (
|
||||
<div className="px-2.5 py-2 text-fg-subtle text-sm">
|
||||
No memories found.
|
||||
</div>
|
||||
) : null}
|
||||
<ul className="space-y-0.5">
|
||||
{documents.map((document) => (
|
||||
<li key={document.id}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setSelectedMemory(toMemoryPreview(document))
|
||||
}
|
||||
className="group flex w-full items-center gap-3 rounded-lg px-2.5 py-2 text-left transition-colors hover:bg-surface-hover"
|
||||
>
|
||||
<span className="flex size-6 shrink-0 items-center justify-center rounded-md bg-surface-card ring-1 ring-surface-border transition-colors group-hover:bg-[#182333]">
|
||||
<FileText className="size-3 text-fg-subtle" />
|
||||
</span>
|
||||
<span className="min-w-0 flex-1 truncate text-fg-muted text-sm transition-colors group-hover:text-white">
|
||||
{document.title?.trim() ||
|
||||
document.url ||
|
||||
"Untitled memory"}
|
||||
</span>
|
||||
<ArrowRight className="size-3.5 shrink-0 text-fg-faint transition-colors group-hover:text-fg-muted" />
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="hidden min-w-0 flex-[2] sm:block">
|
||||
<DocumentPreview memory={activeMemory} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<SearchCommand
|
||||
|
|
@ -141,11 +221,32 @@ export default function DashboardPage() {
|
|||
)
|
||||
}
|
||||
|
||||
function QuickAction({
|
||||
icon: Icon,
|
||||
label,
|
||||
onClick,
|
||||
}: {
|
||||
icon: typeof Link2
|
||||
label: string
|
||||
onClick?: () => void
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className="flex min-w-0 items-center justify-center gap-1 rounded-lg px-1 py-1.5 text-[11px] text-fg-subtle leading-none transition-colors hover:bg-surface-hover hover:text-white sm:gap-1.5 sm:px-2.5 sm:text-sm"
|
||||
>
|
||||
<Icon className="size-3.5 shrink-0" />
|
||||
<span className="min-w-0 truncate whitespace-nowrap">{label}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function DocumentPreview({ memory }: { memory: MemoryPreview | null }) {
|
||||
if (!memory) {
|
||||
return (
|
||||
<Card className="min-h-0">
|
||||
<CardContent className="flex h-full min-h-[360px] items-center justify-center text-muted-foreground text-sm">
|
||||
<Card className="min-h-0 rounded-lg border-white/[0.08] bg-[#0D121A]/74 shadow-[0_24px_80px_rgba(0,0,0,0.24),inset_1px_1px_1px_rgba(255,255,255,0.05)] backdrop-blur-xl">
|
||||
<CardContent className="flex h-full min-h-[360px] items-center justify-center text-[#8A929E] text-sm">
|
||||
Select a memory to preview it.
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -153,46 +254,46 @@ function DocumentPreview({ memory }: { memory: MemoryPreview | null }) {
|
|||
}
|
||||
|
||||
return (
|
||||
<Card className="min-h-0 overflow-hidden">
|
||||
<CardHeader className="border-border/60 border-b">
|
||||
<Card className="border-0 bg-surface-card/60 shadow-[0_12px_40px_rgba(0,0,0,0.22)] backdrop-blur-md">
|
||||
<CardHeader className="p-3 pb-2">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="min-w-0">
|
||||
<CardTitle className="truncate text-lg">
|
||||
<CardTitle className="truncate text-fg-primary text-sm">
|
||||
{memory.title ?? "Untitled memory"}
|
||||
</CardTitle>
|
||||
<CardDescription className="mt-1 flex items-center gap-2">
|
||||
<CardDescription className="mt-1 flex items-center gap-1.5 text-fg-faint text-[10px]">
|
||||
<Clock className="size-3.5" />
|
||||
{formatDate(memory.createdAt)}
|
||||
</CardDescription>
|
||||
</div>
|
||||
{memory.type ? (
|
||||
<span className="rounded-md bg-muted px-2 py-1 text-muted-foreground text-xs">
|
||||
<span className="rounded-full bg-[#4BA0FA]/16 px-2 py-0.5 font-semibold text-[#8BC6FF] text-[9px] uppercase tracking-[0.08em]">
|
||||
{memory.type}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="min-h-0 overflow-auto p-6">
|
||||
<CardContent className="px-3 pt-0 pb-3">
|
||||
{memory.url ? (
|
||||
<a
|
||||
href={memory.url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="mb-4 block truncate text-primary text-sm underline-offset-4 hover:underline"
|
||||
className="mb-3 block truncate text-[#8BC6FF] text-[11px] underline-offset-4 hover:underline"
|
||||
>
|
||||
{memory.url}
|
||||
</a>
|
||||
) : null}
|
||||
<div className="space-y-4 text-sm leading-6">
|
||||
<div className="space-y-3 text-[12px] leading-relaxed">
|
||||
{memory.summary ? (
|
||||
<section>
|
||||
<h2 className="mb-2 font-medium">Summary</h2>
|
||||
<p className="text-muted-foreground">{memory.summary}</p>
|
||||
<h2 className="mb-1 font-medium text-fg-primary">Summary</h2>
|
||||
<p className="line-clamp-4 text-fg-subtle">{memory.summary}</p>
|
||||
</section>
|
||||
) : null}
|
||||
<section>
|
||||
<h2 className="mb-2 font-medium">Content</h2>
|
||||
<p className="whitespace-pre-wrap text-muted-foreground">
|
||||
<h2 className="mb-1 font-medium text-fg-primary">Content</h2>
|
||||
<p className="line-clamp-6 whitespace-pre-wrap text-fg-subtle">
|
||||
{memory.content ?? memory.raw ?? "No content available."}
|
||||
</p>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,72 @@ body {
|
|||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
.desktop-shell {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
background:
|
||||
radial-gradient(
|
||||
ellipse 90% 46% at 50% -4%,
|
||||
rgba(75, 160, 250, 0.22),
|
||||
transparent 60%
|
||||
),
|
||||
radial-gradient(
|
||||
ellipse 52% 32% at 78% 12%,
|
||||
rgba(127, 108, 255, 0.14),
|
||||
transparent 70%
|
||||
),
|
||||
radial-gradient(
|
||||
ellipse 42% 30% at 18% 20%,
|
||||
rgba(15, 240, 210, 0.08),
|
||||
transparent 74%
|
||||
),
|
||||
linear-gradient(180deg, #05080d 0%, #070b12 44%, #05070a 100%);
|
||||
color: #fafafa;
|
||||
}
|
||||
|
||||
.desktop-shell::before {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -2;
|
||||
content: "";
|
||||
background-image: radial-gradient(
|
||||
circle,
|
||||
rgba(255, 255, 255, 0.13) 1px,
|
||||
transparent 1px
|
||||
);
|
||||
background-size: 18px 18px;
|
||||
mask-image: linear-gradient(
|
||||
to bottom,
|
||||
rgba(255, 255, 255, 0.72),
|
||||
rgba(255, 255, 255, 0.2) 44%,
|
||||
transparent 88%
|
||||
);
|
||||
-webkit-mask-image: linear-gradient(
|
||||
to bottom,
|
||||
rgba(255, 255, 255, 0.72),
|
||||
rgba(255, 255, 255, 0.2) 44%,
|
||||
transparent 88%
|
||||
);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.desktop-shell::after {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
content: "";
|
||||
background:
|
||||
radial-gradient(
|
||||
ellipse 86% 32% at 50% 100%,
|
||||
rgba(65, 115, 255, 0.1),
|
||||
transparent 70%
|
||||
),
|
||||
linear-gradient(90deg, transparent, rgba(75, 160, 250, 0.08), transparent)
|
||||
50% 0 / 60% 1px no-repeat,
|
||||
linear-gradient(180deg, rgba(5, 8, 13, 0), #05070a 92%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* The draggable title bar should feel like native chrome, not selectable text. */
|
||||
[data-tauri-drag-region] {
|
||||
cursor: default;
|
||||
|
|
|
|||
|
|
@ -1,38 +1,80 @@
|
|||
"use client"
|
||||
|
||||
import { Logo } from "@ui/assets/Logo"
|
||||
import { cn } from "@lib/utils"
|
||||
import { Cog, House } from "lucide-react"
|
||||
import { ChevronRight, Cog, Home, LayoutGrid, SearchIcon } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
|
||||
const LINKS = [
|
||||
{ href: "/", label: "Home", icon: House },
|
||||
{ href: "/", label: "Home", icon: Home },
|
||||
{ href: "/settings", label: "Settings", icon: Cog },
|
||||
] as const
|
||||
|
||||
export function AppNav() {
|
||||
const pathname = usePathname()
|
||||
const openSearch = () => {
|
||||
window.dispatchEvent(new Event("supermemory:open-search"))
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="flex w-48 shrink-0 flex-col gap-1 border-border/60 border-r p-3">
|
||||
{LINKS.map(({ href, label, icon: Icon }) => {
|
||||
const active = pathname === href
|
||||
return (
|
||||
<Link
|
||||
key={href}
|
||||
href={href}
|
||||
className={cn(
|
||||
"flex items-center gap-2 rounded-md px-3 py-2 text-sm transition-colors",
|
||||
active
|
||||
? "bg-accent text-accent-foreground"
|
||||
: "text-muted-foreground hover:bg-accent/50 hover:text-foreground",
|
||||
)}
|
||||
<header className="relative z-10 flex shrink-0 items-center justify-between gap-1.5 p-2.5 md:gap-2 md:p-3">
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<Link
|
||||
href="/"
|
||||
className="relative flex shrink-0 items-center rounded-lg px-1.5 py-1 transition-colors hover:bg-white/[0.05]"
|
||||
>
|
||||
<Logo className="h-6 shrink-0" />
|
||||
<div className="ml-2 min-w-0">
|
||||
<div className="truncate text-[10px] leading-tight text-[#6B6B6B]">
|
||||
My
|
||||
</div>
|
||||
<div className="-mt-0.5 truncate font-medium text-lg text-white/90 leading-none">
|
||||
supermemory
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<ChevronRight className="hidden size-4 shrink-0 text-[#3F4853] md:block" />
|
||||
|
||||
<div className="hidden items-center gap-1 rounded-full border border-[#161F2C] bg-[#0D121A]/80 p-1 md:flex">
|
||||
{LINKS.map(({ href, label, icon: Icon }) => {
|
||||
const active = pathname === href
|
||||
return (
|
||||
<Link
|
||||
key={href}
|
||||
href={href}
|
||||
className={cn(
|
||||
"inline-flex h-8 items-center gap-1.5 rounded-full border border-transparent px-3 font-medium text-sm transition-colors",
|
||||
active
|
||||
? "border-[#2261CA33] bg-[#00173C] text-white"
|
||||
: "text-[#D0DAE7] hover:bg-white/[0.05] hover:text-white",
|
||||
)}
|
||||
>
|
||||
<Icon className="size-4" />
|
||||
{label}
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex h-8 items-center gap-1.5 rounded-full border border-transparent px-3 font-medium text-[#D0DAE7] text-sm transition-colors hover:bg-white/[0.05] hover:text-white"
|
||||
>
|
||||
<Icon className="size-4" />
|
||||
{label}
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
<LayoutGrid className="size-4" />
|
||||
Memories
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 items-center gap-1.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={openSearch}
|
||||
className="flex size-9 items-center justify-center rounded-full border border-[#161F2C] bg-[#0D121A]/80 text-[#D0DAE7] transition-colors hover:bg-white/[0.05] hover:text-white"
|
||||
aria-label="Search"
|
||||
>
|
||||
<SearchIcon className="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,8 +111,13 @@ export function useCommandK() {
|
|||
setOpen((prev) => !prev)
|
||||
}
|
||||
}
|
||||
const onOpenSearch = () => setOpen(true)
|
||||
document.addEventListener("keydown", onKeyDown)
|
||||
return () => document.removeEventListener("keydown", onKeyDown)
|
||||
window.addEventListener("supermemory:open-search", onOpenSearch)
|
||||
return () => {
|
||||
document.removeEventListener("keydown", onKeyDown)
|
||||
window.removeEventListener("supermemory:open-search", onOpenSearch)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return { open, setOpen }
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export function Titlebar({ title = "Supermemory" }: { title?: string }) {
|
|||
return (
|
||||
<header
|
||||
data-tauri-drag-region
|
||||
className="flex h-10 shrink-0 items-center justify-center border-border/60 border-b bg-background/80 pl-20 font-medium text-muted-foreground text-xs backdrop-blur"
|
||||
className="flex h-10 shrink-0 items-center justify-center border-white/[0.06] border-b bg-[#05080D]/70 pl-20 font-medium text-[#737B86] text-xs backdrop-blur-md"
|
||||
>
|
||||
{title}
|
||||
</header>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue