mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-05-19 07:42:43 +00:00
feat: nova dashboard improvements with theme (#905)

This commit is contained in:
parent
7d762806a9
commit
8c8a6e130f
9 changed files with 107 additions and 93 deletions
|
|
@ -56,6 +56,7 @@ import {
|
|||
qParam,
|
||||
docParam,
|
||||
fullscreenParam,
|
||||
threadParam,
|
||||
type IntegrationParamValue,
|
||||
} from "@/lib/search-params"
|
||||
import { getChatSpaceDisplayLabel } from "@/lib/chat-space-label"
|
||||
|
|
@ -152,6 +153,7 @@ export default function NewPage() {
|
|||
"fullscreen",
|
||||
fullscreenParam,
|
||||
)
|
||||
const [, setThreadIdUrl] = useQueryState("thread", threadParam)
|
||||
|
||||
// Ephemeral local state (not worth URL-encoding)
|
||||
const [fullscreenInitialContent, setFullscreenInitialContent] = useState("")
|
||||
|
|
@ -168,6 +170,10 @@ export default function NewPage() {
|
|||
if (!docId) setSelectedDocument(null)
|
||||
}, [docId])
|
||||
|
||||
useEffect(() => {
|
||||
if (viewMode === "dashboard") void setThreadIdUrl(null)
|
||||
}, [viewMode, setThreadIdUrl])
|
||||
|
||||
// Resolve document from cache when loading with ?doc=<id> (deep link / refresh)
|
||||
useEffect(() => {
|
||||
if (!docId || selectedDocument) return
|
||||
|
|
@ -512,7 +518,7 @@ export default function NewPage() {
|
|||
const isGraphMode = viewMode === "graph" && !isMobile
|
||||
const isMemoriesDesktop = viewMode === "list" && !isMobile
|
||||
const isHomeDesktop = viewMode === "dashboard" && !isMobile
|
||||
const showNovaBackdrop = isGraphMode || isMemoriesDesktop
|
||||
const showNovaBackdrop = isGraphMode || isMemoriesDesktop || isHomeDesktop
|
||||
const isDashboardShell =
|
||||
viewMode === "dashboard" || (viewMode === "graph" && isMobile)
|
||||
|
||||
|
|
@ -520,31 +526,23 @@ export default function NewPage() {
|
|||
<HotkeysProvider>
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex min-h-dvh flex-col bg-black",
|
||||
"relative flex min-h-dvh flex-col bg-[#05080D]",
|
||||
isGraphMode && "h-dvh overflow-hidden",
|
||||
)}
|
||||
>
|
||||
{showNovaBackdrop && (
|
||||
<>
|
||||
<AnimatedGradientBackground
|
||||
animateFromBottom={isHomeDesktop}
|
||||
animateFromBottom={false}
|
||||
topPosition={gradientTopPosition}
|
||||
/>
|
||||
<div
|
||||
className={cn(
|
||||
"pointer-events-none absolute inset-0 z-0",
|
||||
isHomeDesktop
|
||||
? "bg-[linear-gradient(to_top,rgb(0_0_0/0.88)_0%,rgb(0_0_0/0.52)_38%,rgb(0_0_0/0.42)_100%)]"
|
||||
: "bg-black/50",
|
||||
)}
|
||||
className="pointer-events-none absolute inset-0 z-0 bg-[#05080D]/50"
|
||||
aria-hidden
|
||||
/>
|
||||
<div
|
||||
id="graph-dotted-grid"
|
||||
className={cn(
|
||||
"pointer-events-none absolute inset-0 z-[1] bg-[radial-gradient(circle_at_center,rgba(105,167,240,0.25)_1px,transparent_1px)] bg-size-[32px_32px] mask-[radial-gradient(ellipse_at_center,black_60%,transparent_100%)]",
|
||||
isHomeDesktop && "opacity-70",
|
||||
)}
|
||||
className="pointer-events-none absolute inset-0 z-[1] bg-[radial-gradient(circle_at_center,rgba(105,167,240,0.25)_1px,transparent_1px)] bg-size-[32px_32px] mask-[radial-gradient(ellipse_at_center,black_60%,transparent_100%)]"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
|
@ -716,7 +714,7 @@ export default function NewPage() {
|
|||
{isDashboardShell && (
|
||||
<div
|
||||
className={cn(
|
||||
"pointer-events-none fixed inset-x-0 z-30 bg-gradient-to-t from-black via-black/80 to-transparent pt-12",
|
||||
"pointer-events-none fixed inset-x-0 z-30 bg-gradient-to-t from-black via-black/40 to-transparent pt-12",
|
||||
isMobile ? "bottom-[4.5rem]" : "bottom-0",
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import { useMemo } from "react"
|
||||
import type { UIMessage } from "@ai-sdk/react"
|
||||
import { MemoryGraph } from "@/components/memory-graph"
|
||||
import { AnimatedGradientBackground } from "@/components/animated-gradient-background"
|
||||
import { useProject } from "@/stores"
|
||||
import { extractHighlightDocumentIdsFromMessages } from "@/lib/chat-highlight-documents"
|
||||
import { cn } from "@lib/utils"
|
||||
|
|
@ -25,11 +26,14 @@ export function ChatGraphContextRail({
|
|||
<div
|
||||
id="chat-graph-context-rail"
|
||||
className={cn(
|
||||
"relative flex min-h-0 min-w-0 flex-1 flex-col bg-black",
|
||||
"relative flex min-h-0 min-w-0 flex-1 flex-col bg-[#05080D] overflow-hidden",
|
||||
dmSansClassName(),
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<AnimatedGradientBackground animateFromBottom={false} topPosition="55%" />
|
||||
<div className="pointer-events-none absolute inset-0 z-0 bg-[#05080D]/50" />
|
||||
<div className="pointer-events-none absolute inset-0 z-[1] bg-[radial-gradient(circle_at_center,rgba(105,167,240,0.25)_1px,transparent_1px)] bg-size-[32px_32px] mask-[radial-gradient(ellipse_at_center,black_60%,transparent_100%)]" />
|
||||
<div className="pointer-events-none absolute top-3 left-4 z-20">
|
||||
<p className="text-xs font-medium text-white/70">Memory map</p>
|
||||
<p className="mt-0.5 max-w-[14rem] text-[10px] leading-snug text-white/35">
|
||||
|
|
@ -38,8 +42,8 @@ export function ChatGraphContextRail({
|
|||
: "Memories used by Nova will be highlighted here"}
|
||||
</p>
|
||||
</div>
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 z-10 w-24 bg-gradient-to-r from-transparent to-black" />
|
||||
<div className="min-h-0 flex-1 pt-10">
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 z-10 w-24 bg-gradient-to-r from-transparent to-[#05080D]" />
|
||||
<div className="relative z-[2] min-h-0 flex-1 pt-10">
|
||||
<MemoryGraph
|
||||
containerTags={effectiveContainerTags}
|
||||
variant="consumer"
|
||||
|
|
|
|||
|
|
@ -64,12 +64,12 @@ export function HomeChatComposer({
|
|||
/>
|
||||
<div
|
||||
className={cn(
|
||||
"inline-flex max-w-[min(160px,35vw)] min-w-0 shrink items-center rounded-full border border-[#161F2C] bg-[#000000] px-3 py-1.5",
|
||||
"inline-flex max-w-[min(160px,35vw)] min-w-0 shrink items-center rounded-full bg-fg-primary/5 px-3 py-1.5",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
title={chatSpaceLabel}
|
||||
>
|
||||
<span className="truncate text-sm text-[#FAFAFA]">
|
||||
<span className="truncate text-sm text-fg-primary">
|
||||
{chatSpaceLabel}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ export function SendButton({
|
|||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
"bg-[#000000] border-[#161F2C] border p-2 rounded-lg shrink-0 transition-opacity",
|
||||
"bg-surface-card border-surface-border border p-2 rounded-lg shrink-0 transition-opacity",
|
||||
disabled
|
||||
? "opacity-50 cursor-not-allowed"
|
||||
: "cursor-pointer hover:bg-[#161F2C]",
|
||||
: "cursor-pointer hover:bg-surface-hover",
|
||||
)}
|
||||
>
|
||||
<svg
|
||||
|
|
@ -42,7 +42,7 @@ export function StopButton({ onClick }: { onClick: () => void }) {
|
|||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className="bg-[#000000] border-[#161F2C] border p-2 rounded-lg shrink-0 cursor-pointer hover:bg-[#161F2C] transition-opacity"
|
||||
className="bg-surface-card border-surface-border border p-2 rounded-lg shrink-0 cursor-pointer hover:bg-surface-hover transition-opacity"
|
||||
>
|
||||
<SquareIcon className="size-4 text-white fill-white" />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -122,14 +122,14 @@ export default function ChatInput({
|
|||
</>
|
||||
) : null}
|
||||
{stackedToolbar ? (
|
||||
<div className="flex flex-col gap-2 rounded-xl border border-[#52596633] bg-[#070E1B] p-2 transition-all duration-200 focus-within:outline-1 focus-within:outline-[#525D6EB2]">
|
||||
<div className="flex flex-col gap-2 rounded-xl bg-surface-card/60 backdrop-blur-md p-2 shadow-[0_16px_48px_rgba(0,0,0,0.34)] transition-all duration-200 focus-within:ring-1 focus-within:ring-fg-primary/10">
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
onKeyDown={onKeyDown}
|
||||
placeholder="Ask your supermemory..."
|
||||
className="w-full resize-none overflow-y-auto bg-transparent p-2 transition-all duration-200 placeholder:text-[#525D6E] focus:outline-none"
|
||||
className="w-full resize-none overflow-y-auto bg-transparent p-2 text-fg-primary transition-all duration-200 placeholder:text-fg-faint focus:outline-none"
|
||||
style={{ minHeight: "36px" }}
|
||||
rows={1}
|
||||
disabled={isResponding}
|
||||
|
|
@ -150,7 +150,7 @@ export default function ChatInput({
|
|||
) : (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-end gap-2 rounded-xl border border-[#52596633] bg-[#070E1B] p-2 transition-all duration-200 focus-within:outline-1 focus-within:outline-[#525D6EB2]",
|
||||
"flex items-end gap-2 rounded-xl bg-surface-card/60 backdrop-blur-md p-2 shadow-[0_16px_48px_rgba(0,0,0,0.34)] transition-all duration-200 focus-within:ring-1 focus-within:ring-fg-primary/10",
|
||||
isMultiline && "flex-col",
|
||||
)}
|
||||
>
|
||||
|
|
@ -160,7 +160,7 @@ export default function ChatInput({
|
|||
onChange={handleChange}
|
||||
onKeyDown={onKeyDown}
|
||||
placeholder="Ask your supermemory..."
|
||||
className="w-full resize-none overflow-y-auto bg-transparent p-2 transition-all duration-200 placeholder:text-[#525D6E] focus:outline-none"
|
||||
className="w-full resize-none overflow-y-auto bg-transparent p-2 text-fg-primary transition-all duration-200 placeholder:text-fg-faint focus:outline-none"
|
||||
style={{ minHeight: "36px" }}
|
||||
rows={1}
|
||||
disabled={isResponding}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { analytics } from "@/lib/analytics"
|
|||
interface ChatModelSelectorProps {
|
||||
selectedModel?: ModelId
|
||||
onModelChange?: (model: ModelId) => void
|
||||
/** Compact pill matching inline send control (black + #161F2C border, rounded-full) */
|
||||
/** Compact pill matching inline send control. */
|
||||
minimal?: boolean
|
||||
}
|
||||
|
||||
|
|
@ -41,22 +41,22 @@ export default function ChatModelSelector({
|
|||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"flex max-w-[min(100%,220px)] min-w-0 shrink cursor-pointer items-center gap-1.5 rounded-full border border-[#161F2C] bg-[#000000] px-3 py-1.5 text-sm transition-colors hover:bg-[#161F2C]",
|
||||
"flex max-w-[min(100%,220px)] min-w-0 shrink cursor-pointer items-center gap-1.5 rounded-full bg-fg-primary/5 px-3 py-1.5 text-sm transition-colors hover:bg-fg-primary/10",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<p className="min-w-0 truncate text-left text-[#FAFAFA]">
|
||||
<p className="min-w-0 truncate text-left text-fg-primary">
|
||||
{currentModelData.name}{" "}
|
||||
<span className="text-[#525D6E]">{currentModelData.version}</span>
|
||||
<span className="text-fg-subtle">{currentModelData.version}</span>
|
||||
</p>
|
||||
<ChevronDownIcon className="size-3.5 shrink-0 text-[#525D6E]" />
|
||||
<ChevronDownIcon className="size-3.5 shrink-0 text-fg-subtle" />
|
||||
</button>
|
||||
) : (
|
||||
<Button
|
||||
variant="headers"
|
||||
className={cn(
|
||||
"h-10! max-w-[min(100%,220px)] shrink gap-1 rounded-full border-[#73737333] bg-[#0D121A] text-base",
|
||||
"h-10! max-w-[min(100%,220px)] shrink gap-1 rounded-full border-[#73737333] bg-surface-base text-base",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
style={{
|
||||
|
|
@ -86,7 +86,7 @@ export default function ChatModelSelector({
|
|||
aria-label="Close model selector"
|
||||
/>
|
||||
|
||||
<div className="absolute bottom-full left-0 mb-2 w-64 bg-[#0D121A] backdrop-blur-xl border border-[#73737333] rounded-lg shadow-xl z-50 overflow-hidden">
|
||||
<div className="absolute bottom-full left-0 mb-2 w-64 bg-surface-card backdrop-blur-xl border border-surface-border rounded-lg shadow-xl z-50 overflow-hidden">
|
||||
<div className="p-2 space-y-1">
|
||||
{models.map((model) => {
|
||||
const modelData = modelNames[model.id]
|
||||
|
|
@ -107,11 +107,11 @@ export default function ChatModelSelector({
|
|||
>
|
||||
<div className="text-sm font-medium text-white">
|
||||
{modelData.name}{" "}
|
||||
<span className="text-[#737373]">
|
||||
<span className="text-fg-subtle">
|
||||
{modelData.version}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-[#737373] truncate w-full">
|
||||
<div className="text-xs text-fg-muted truncate w-full">
|
||||
{model.description}
|
||||
</div>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -294,13 +294,13 @@ function RecommendedPluginsCard({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"bg-[#050709] border border-[#0F1621] rounded-xl px-3 py-2 flex flex-col gap-1",
|
||||
"bg-surface-card/60 backdrop-blur-md rounded-xl px-3 py-2 flex flex-col gap-1 shadow-[0_12px_40px_rgba(0,0,0,0.22)]",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
>
|
||||
{showPicker ? (
|
||||
<div className="px-1 py-2 flex flex-col gap-2.5">
|
||||
<p className="text-[11px] text-[#737373]">
|
||||
<p className="text-[11px] text-fg-muted">
|
||||
{isEditing ? "Change your field:" : "What's your field?"}
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
|
|
@ -315,8 +315,8 @@ function RecommendedPluginsCard({
|
|||
className={cn(
|
||||
"rounded-full border px-2.5 py-1 text-[11px] font-medium transition-all cursor-pointer",
|
||||
profession === value
|
||||
? "border-[#3374FF]/40 bg-[#3374FF]/10 text-[#6BB0FF]"
|
||||
: "border-[#161F2C] text-[#525D6E] hover:border-[#3374FF]/25 hover:text-[#4BA0FA]",
|
||||
? "border-[#4BA0FA]/55 bg-[#3374FF]/15 text-[#8BC6FF]"
|
||||
: "border-surface-border text-fg-subtle hover:border-[#4BA0FA]/40 hover:text-[#6BB0FF]",
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
|
|
@ -327,7 +327,7 @@ function RecommendedPluginsCard({
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setIsEditing(false)}
|
||||
className="text-[10px] text-[#3A4455] hover:text-[#525D6E] transition-colors text-left cursor-pointer"
|
||||
className="text-[10px] text-fg-faint hover:text-fg-muted transition-colors text-left cursor-pointer"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
|
|
@ -335,7 +335,7 @@ function RecommendedPluginsCard({
|
|||
</div>
|
||||
) : suggestions.length === 0 ? (
|
||||
<div className="flex items-center justify-center py-4">
|
||||
<p className="text-[11px] text-[#525D6E] text-center">
|
||||
<p className="text-[11px] text-fg-subtle text-center">
|
||||
You're all set ✓
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -347,18 +347,18 @@ function RecommendedPluginsCard({
|
|||
<button
|
||||
type="button"
|
||||
onClick={plugin.onClick}
|
||||
className="group w-full flex items-center gap-2.5 rounded-lg px-2 py-2 hover:bg-[#0D121A] transition-colors cursor-pointer"
|
||||
className="group w-full flex items-center gap-2.5 rounded-lg px-2 py-2 hover:bg-surface-hover transition-colors cursor-pointer"
|
||||
>
|
||||
<plugin.Icon className="size-4 shrink-0 text-[#525D6E]" />
|
||||
<plugin.Icon className="size-4 shrink-0 text-fg-subtle" />
|
||||
<div className="flex-1 min-w-0 text-left">
|
||||
<p className="text-[12px] text-[#737373] group-hover:text-white transition-colors leading-tight">
|
||||
<p className="text-[12px] text-fg-secondary group-hover:text-white transition-colors leading-tight">
|
||||
{plugin.name}
|
||||
</p>
|
||||
<p className="text-[11px] text-[#525D6E] leading-tight mt-0.5">
|
||||
<p className="text-[11px] text-fg-subtle leading-tight mt-0.5">
|
||||
{PLUGIN_TAGLINES[profession][plugin.id] ?? plugin.tagline}
|
||||
</p>
|
||||
</div>
|
||||
<span className="shrink-0 text-[10px] font-medium text-[#3374FF] group-hover:text-[#6BB0FF] transition-colors">
|
||||
<span className="shrink-0 text-[10px] font-medium text-[#5EA8FF] group-hover:text-[#8BC6FF] transition-colors">
|
||||
{plugin.cta} →
|
||||
</span>
|
||||
</button>
|
||||
|
|
@ -368,7 +368,7 @@ function RecommendedPluginsCard({
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setIsEditing(true)}
|
||||
className="text-left px-2 pb-1 text-[10px] text-[#3A4455] hover:text-[#525D6E] transition-colors cursor-pointer"
|
||||
className="text-left px-2 pb-1 text-[10px] text-fg-faint hover:text-fg-muted transition-colors cursor-pointer"
|
||||
>
|
||||
Not a{" "}
|
||||
{PROFESSION_LABELS.find(
|
||||
|
|
@ -398,20 +398,20 @@ function MemoryOfDayCard({ data }: { data: MemoryOfDay }) {
|
|||
type="button"
|
||||
onClick={() => router.push(href)}
|
||||
className={cn(
|
||||
"group w-full h-full text-left bg-[#0B1017] border border-[rgba(255,255,255,0.05)] rounded-[18px] p-3 flex flex-col justify-between hover:border-[rgba(255,255,255,0.10)] transition-colors cursor-pointer",
|
||||
"group w-full h-full text-left bg-surface-card/60 backdrop-blur-md rounded-[18px] p-3 flex flex-col justify-between transition-colors cursor-pointer shadow-[0_12px_40px_rgba(0,0,0,0.22)]",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col gap-2.5">
|
||||
<span className="self-start text-[9px] font-semibold tracking-[0.12em] uppercase text-[#4BA0FA] bg-[#4BA0FA]/10 rounded-full px-2 py-0.5">
|
||||
<span className="self-start text-[9px] font-semibold tracking-[0.12em] uppercase text-[#8BC6FF] bg-[#4BA0FA]/16 rounded-full px-2 py-0.5">
|
||||
{data.timeLabel}
|
||||
</span>
|
||||
<p className="text-[12px] text-[#8B9DB5] leading-relaxed line-clamp-4">
|
||||
<p className="text-[12px] text-fg-secondary leading-relaxed line-clamp-4">
|
||||
{memory}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<span className="text-[10px] text-[#2A3A50] group-hover:text-[#4A6A80] transition-colors">
|
||||
<span className="text-[10px] text-fg-faint group-hover:text-fg-muted transition-colors">
|
||||
View memories →
|
||||
</span>
|
||||
</button>
|
||||
|
|
@ -475,7 +475,7 @@ function PluginPromoCard({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"bg-[#0B1017] border border-[rgba(255,255,255,0.05)] rounded-[18px] p-3 flex flex-col justify-between gap-3 h-full",
|
||||
"bg-surface-card/60 backdrop-blur-md rounded-[18px] p-3 flex flex-col justify-between gap-3 h-full shadow-[0_12px_40px_rgba(0,0,0,0.22)]",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
>
|
||||
|
|
@ -511,10 +511,10 @@ function PluginPromoCard({
|
|||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-[11px] font-semibold text-[#FAFAFA] leading-tight">
|
||||
<p className="text-[11px] font-semibold text-fg-primary leading-tight">
|
||||
{plugin.name}
|
||||
</p>
|
||||
<p className="text-[10px] text-[#525D6E] leading-normal">
|
||||
<p className="text-[10px] text-fg-muted leading-normal">
|
||||
{plugin.tagline}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -524,7 +524,7 @@ function PluginPromoCard({
|
|||
<button
|
||||
type="button"
|
||||
onClick={plugin.onClick}
|
||||
className="w-full bg-[#0D121A] rounded-lg px-3 py-1.5 text-[11px] font-medium text-[#4BA0FA] hover:text-white hover:bg-[#141C28] transition-colors cursor-pointer text-left flex items-center justify-between group"
|
||||
className="w-full bg-surface-card border border-surface-border rounded-lg px-3 py-1.5 text-[11px] font-medium text-[#6BB0FF] hover:text-white hover:bg-surface-hover transition-colors cursor-pointer text-left flex items-center justify-between group"
|
||||
style={{ boxShadow: "inset 1px 1px 2px rgba(0,0,0,0.5)" }}
|
||||
>
|
||||
<span>{plugin.cta}</span>
|
||||
|
|
@ -533,8 +533,8 @@ function PluginPromoCard({
|
|||
</>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center h-full gap-1.5 text-center">
|
||||
<Terminal className="size-4 text-[#3A4455]" />
|
||||
<p className="text-[10px] text-[#3A4455]">
|
||||
<Terminal className="size-4 text-fg-faint" />
|
||||
<p className="text-[10px] text-fg-subtle">
|
||||
All integrations connected
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -654,10 +654,10 @@ export function DashboardView({
|
|||
<motion.header
|
||||
{...fadeUp}
|
||||
transition={{ ...fadeUp.transition, delay: 0 }}
|
||||
className="flex items-end justify-between gap-4 border-b border-[#0F1621] pb-4"
|
||||
className="flex items-end justify-between gap-4 border-b border-surface-border pb-4"
|
||||
>
|
||||
<div className="space-y-0.5">
|
||||
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-[#3A4455]">
|
||||
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-fg-faint">
|
||||
Home
|
||||
</p>
|
||||
<h1 className="text-xl font-medium tracking-tight text-white md:text-2xl">
|
||||
|
|
@ -670,7 +670,7 @@ export function DashboardView({
|
|||
<button
|
||||
type="button"
|
||||
onClick={onNavigateToGraph}
|
||||
className="group relative shrink-0 w-[140px] h-[56px] rounded-xl overflow-hidden border border-[rgba(255,255,255,0.05)] hover:border-[rgba(255,255,255,0.14)] transition-all bg-[#0B1017] hover:scale-[1.02]"
|
||||
className="group relative shrink-0 w-[140px] h-[56px] rounded-xl overflow-hidden border border-surface-border hover:border-[#3A4A63] transition-all bg-surface-card hover:scale-[1.02]"
|
||||
aria-label="Open graph view"
|
||||
>
|
||||
<StaticGraphPreview
|
||||
|
|
@ -696,7 +696,7 @@ export function DashboardView({
|
|||
className="space-y-2"
|
||||
>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-[#3A4455]">
|
||||
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-fg-faint">
|
||||
Daily brief
|
||||
</p>
|
||||
<Tooltip>
|
||||
|
|
@ -704,7 +704,7 @@ export function DashboardView({
|
|||
<button
|
||||
type="button"
|
||||
onClick={onResetHighlights}
|
||||
className="text-[#2A3040] hover:text-[#5A6478] transition-colors cursor-pointer"
|
||||
className="text-fg-faint hover:text-fg-muted transition-colors cursor-pointer"
|
||||
aria-label="Refresh daily brief"
|
||||
>
|
||||
<RotateCcw className="size-3" />
|
||||
|
|
@ -750,28 +750,28 @@ export function DashboardView({
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => onAddMemory("link")}
|
||||
className="flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm text-[#5A6478] hover:bg-[#0D121A] hover:text-white transition-colors cursor-pointer"
|
||||
className="flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm text-fg-subtle hover:bg-surface-hover hover:text-white transition-colors cursor-pointer"
|
||||
>
|
||||
<Link2 className="size-3.5 shrink-0" />
|
||||
{personalizedCopy.saveLink}
|
||||
</button>
|
||||
<span className="text-[#1A2030] select-none">·</span>
|
||||
<span className="text-[#3A4455] select-none">·</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onAddMemory("note")}
|
||||
className="flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm text-[#5A6478] hover:bg-[#0D121A] hover:text-white transition-colors cursor-pointer"
|
||||
className="flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm text-fg-subtle hover:bg-surface-hover hover:text-white transition-colors cursor-pointer"
|
||||
>
|
||||
<FileText className="size-3.5 shrink-0" />
|
||||
{personalizedCopy.writeNote}
|
||||
</button>
|
||||
<span className="text-[#1A2030] select-none">·</span>
|
||||
<span className="text-[#3A4455] select-none">·</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
analytics.searchOpened({ source: "header" })
|
||||
onOpenSearch()
|
||||
}}
|
||||
className="flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm text-[#5A6478] hover:bg-[#0D121A] hover:text-white transition-colors cursor-pointer"
|
||||
className="flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm text-fg-subtle hover:bg-surface-hover hover:text-white transition-colors cursor-pointer"
|
||||
>
|
||||
<SearchIcon className="size-3.5 shrink-0" />
|
||||
Search
|
||||
|
|
@ -779,7 +779,7 @@ export function DashboardView({
|
|||
</div>
|
||||
|
||||
{/* Tip of the day */}
|
||||
<p className="hidden sm:flex items-center gap-1.5 text-[11px] text-[#525D6E] min-w-0 overflow-hidden">
|
||||
<p className="hidden sm:flex items-center gap-1.5 text-[11px] text-fg-subtle min-w-0 overflow-hidden">
|
||||
<Lightbulb className="size-3 shrink-0 text-[#3374FF]" />
|
||||
<span className="truncate">{tip}</span>
|
||||
</p>
|
||||
|
|
@ -796,12 +796,12 @@ export function DashboardView({
|
|||
{/* Shared header row — both labels aligned */}
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-[3] min-w-0">
|
||||
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-[#3A4455]">
|
||||
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-fg-faint">
|
||||
Recently saved
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-[2] min-w-0 hidden sm:block">
|
||||
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-[#3A4455]">
|
||||
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-fg-faint">
|
||||
Suggested for you
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -817,19 +817,19 @@ export function DashboardView({
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => onOpenDocument(doc)}
|
||||
className="group flex w-full items-center gap-3 rounded-lg px-2.5 py-2 text-left transition-colors hover:bg-[#0D121A]"
|
||||
className="group flex w-full items-center gap-3 rounded-lg px-2.5 py-2 text-left transition-colors hover:bg-surface-hover"
|
||||
>
|
||||
<div className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-[#0D121A] group-hover:bg-[#131B28] transition-colors">
|
||||
<div className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-surface-card ring-1 ring-surface-border group-hover:bg-[#182333] transition-colors">
|
||||
{isLink ? (
|
||||
<ExternalLink className="size-3 text-[#3A4455]" />
|
||||
<ExternalLink className="size-3 text-fg-subtle" />
|
||||
) : (
|
||||
<FileText className="size-3 text-[#3A4455]" />
|
||||
<FileText className="size-3 text-fg-subtle" />
|
||||
)}
|
||||
</div>
|
||||
<span className="min-w-0 flex-1 truncate text-sm text-[#737373] group-hover:text-white transition-colors">
|
||||
<span className="min-w-0 flex-1 truncate text-sm text-fg-muted group-hover:text-white transition-colors">
|
||||
{doc.title?.trim() || "Untitled"}
|
||||
</span>
|
||||
<ArrowRight className="size-3.5 shrink-0 text-[#1E2736] group-hover:text-[#3A4455] transition-colors" />
|
||||
<ArrowRight className="size-3.5 shrink-0 text-fg-faint group-hover:text-fg-muted transition-colors" />
|
||||
</button>
|
||||
</li>
|
||||
)
|
||||
|
|
@ -851,7 +851,7 @@ export function DashboardView({
|
|||
) : (
|
||||
/* No recents yet — show suggestions full-width */
|
||||
<>
|
||||
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-[#3A4455]">
|
||||
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-fg-faint">
|
||||
Suggested for you
|
||||
</p>
|
||||
<div className="max-w-sm">
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ export function HighlightsCard({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"bg-[#0B1017] border border-[rgba(255,255,255,0.05)] rounded-[18px] p-3 flex flex-col gap-3",
|
||||
"bg-surface-card/60 backdrop-blur-md rounded-[18px] p-3 flex flex-col gap-3 shadow-[0_12px_40px_rgba(0,0,0,0.22)]",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
>
|
||||
|
|
@ -128,7 +128,7 @@ export function HighlightsCard({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"bg-[#0B1017] border border-[rgba(255,255,255,0.05)] rounded-[18px] p-3 flex flex-col gap-3 min-h-[180px]",
|
||||
"bg-surface-card/60 backdrop-blur-md rounded-[18px] p-3 flex flex-col gap-3 min-h-[180px] shadow-[0_12px_40px_rgba(0,0,0,0.22)]",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
>
|
||||
|
|
@ -146,7 +146,7 @@ export function HighlightsCard({
|
|||
</div>
|
||||
</div>
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<p className="text-[11px] text-[#737373] text-center">
|
||||
<p className="text-[11px] text-fg-muted text-center">
|
||||
Add some documents to see highlights here
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -157,7 +157,7 @@ export function HighlightsCard({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"bg-[#0B1017] border border-[rgba(255,255,255,0.05)] rounded-[18px] p-3 flex flex-col gap-3",
|
||||
"bg-surface-card/60 backdrop-blur-md rounded-[18px] p-3 flex flex-col gap-3 shadow-[0_12px_40px_rgba(0,0,0,0.22)]",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
>
|
||||
|
|
@ -173,14 +173,14 @@ export function HighlightsCard({
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<Info className="size-[14px] text-[#737373]" />
|
||||
<Info className="size-[14px] text-fg-subtle" />
|
||||
</div>
|
||||
|
||||
<div id="highlights-body" className="flex flex-col gap-1.5">
|
||||
<p className="text-[12px] font-semibold text-[#FAFAFA] leading-tight truncate">
|
||||
<p className="text-[12px] font-semibold text-fg-primary leading-tight truncate">
|
||||
{currentItem.title}
|
||||
</p>
|
||||
<div className="text-[12px] text-[#FAFAFA] leading-normal line-clamp-5">
|
||||
<div className="text-[12px] text-fg-primary leading-normal line-clamp-5">
|
||||
{renderContent(currentItem.content, currentItem.format)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -190,27 +190,27 @@ export function HighlightsCard({
|
|||
<button
|
||||
type="button"
|
||||
onClick={handleChat}
|
||||
className="bg-[#1B1F24] rounded-[8px] px-2 py-1.5 flex items-center gap-1.5 cursor-pointer relative"
|
||||
className="bg-[#182333] border border-surface-border rounded-[8px] px-2 py-1.5 flex items-center gap-1.5 cursor-pointer relative"
|
||||
style={{
|
||||
boxShadow: "0 4px 20px 0 rgba(0, 0, 0, 0.25)",
|
||||
}}
|
||||
aria-label="Chat with Nova"
|
||||
>
|
||||
<MessageSquare className="size-3.5 text-[#FAFAFA]" />
|
||||
<span className="text-[11px] text-[#FAFAFA]">Chat</span>
|
||||
<MessageSquare className="size-3.5 text-fg-primary" />
|
||||
<span className="text-[11px] text-fg-primary">Chat</span>
|
||||
<div className="absolute inset-0 pointer-events-none rounded-[inherit] shadow-[inset_1px_1px_1px_0_rgba(255,255,255,0.1)]" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleShowRelated}
|
||||
className="bg-[#1B1F24] rounded-[8px] px-2 py-1.5 flex items-center gap-1.5 cursor-pointer relative"
|
||||
className="bg-[#182333] border border-surface-border rounded-[8px] px-2 py-1.5 flex items-center gap-1.5 cursor-pointer relative"
|
||||
style={{
|
||||
boxShadow: "0 4px 20px 0 rgba(0, 0, 0, 0.25)",
|
||||
}}
|
||||
aria-label="Show related"
|
||||
>
|
||||
<Link2 className="size-3.5 text-[#FAFAFA]" />
|
||||
<span className="text-[11px] text-[#FAFAFA]">Related</span>
|
||||
<Link2 className="size-3.5 text-fg-primary" />
|
||||
<span className="text-[11px] text-fg-primary">Related</span>
|
||||
<div className="absolute inset-0 pointer-events-none rounded-[inherit] shadow-[inset_1px_1px_1px_0_rgba(255,255,255,0.1)]" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -220,7 +220,7 @@ export function HighlightsCard({
|
|||
<button
|
||||
type="button"
|
||||
onClick={handlePrev}
|
||||
className="text-[#737373] hover:text-white transition-colors cursor-pointer"
|
||||
className="text-fg-subtle hover:text-white transition-colors cursor-pointer"
|
||||
aria-label="Previous item"
|
||||
>
|
||||
<ChevronLeft className="size-4" />
|
||||
|
|
@ -235,7 +235,7 @@ export function HighlightsCard({
|
|||
"rounded-full transition-all cursor-pointer",
|
||||
idx === activeIndex
|
||||
? "w-4 h-1.5 bg-[#4BA0FA]"
|
||||
: "size-1.5 bg-[#737373] hover:bg-[#999999]",
|
||||
: "size-1.5 bg-fg-subtle hover:bg-fg-secondary",
|
||||
)}
|
||||
aria-label={`Go to item ${idx + 1}`}
|
||||
/>
|
||||
|
|
@ -244,7 +244,7 @@ export function HighlightsCard({
|
|||
<button
|
||||
type="button"
|
||||
onClick={handleNext}
|
||||
className="text-[#737373] hover:text-white transition-colors cursor-pointer"
|
||||
className="text-fg-subtle hover:text-white transition-colors cursor-pointer"
|
||||
aria-label="Next item"
|
||||
>
|
||||
<ChevronRight className="size-4" />
|
||||
|
|
|
|||
|
|
@ -4,6 +4,18 @@
|
|||
|
||||
@theme {
|
||||
--color-onboarding: #525966;
|
||||
|
||||
--color-fg-primary: #fafafa;
|
||||
--color-fg-secondary: #e2e8f0;
|
||||
--color-fg-muted: #d0dae7;
|
||||
--color-fg-subtle: #b5c2d3;
|
||||
--color-fg-faint: #a0aec4;
|
||||
|
||||
--color-surface-base: #0b1119;
|
||||
--color-surface-card: #101822;
|
||||
--color-surface-hover: #131b28;
|
||||
--color-surface-border: #263348;
|
||||
|
||||
--animate-file-upload-grow: file-upload-grow 6s cubic-bezier(0.22, 1, 0.36, 1)
|
||||
forwards;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue