diff --git a/apps/desktop-ui/src/components/bookmarks/folder-tree.tsx b/apps/desktop-ui/src/components/bookmarks/folder-tree.tsx
index 4858c11e..5b0fe0c2 100644
--- a/apps/desktop-ui/src/components/bookmarks/folder-tree.tsx
+++ b/apps/desktop-ui/src/components/bookmarks/folder-tree.tsx
@@ -1,6 +1,6 @@
"use client"
-import { useState } from "react"
+import { useMemo, useState } from "react"
import { motion, AnimatePresence } from "framer-motion"
import {
IconFolder,
@@ -43,6 +43,7 @@ import {
import EditFolderDialog from "./edit-folder-dialog"
import AddFolderDialog from "./add-folder-dialog"
import { useTranslations } from "next-intl"
+import { useToolSidebarRail } from "@/components/tools/tool-sidebar"
interface FolderTreeProps {
onSelectFolder: (id: string | null) => void
@@ -51,9 +52,37 @@ interface FolderTreeProps {
export default function FolderTree({ onSelectFolder }: FolderTreeProps) {
const t = useTranslations("Bookmarks.folderTree")
const { selectedFolderId, bookmarks } = useBookmarkStore()
+ // Memoized selector (bookmark-store.ts:506), so calling it here as well as
+ // in RootFolders costs nothing.
+ const rootFolders = useChildFolders(null)
const totalBookmarks = bookmarks.length
+ // "All bookmarks" plus the root folders, for the collapsed rail.
+ useToolSidebarRail(
+ "folders",
+ useMemo(
+ () => [
+ {
+ id: "all",
+ label: t("allBookmarks"),
+ icon: IconBookmarks,
+ count: totalBookmarks,
+ active: selectedFolderId === null,
+ onSelect: () => onSelectFolder(null),
+ },
+ ...rootFolders.slice(0, 7).map(f => ({
+ id: f.id,
+ label: f.name,
+ icon: IconFolder,
+ active: selectedFolderId === f.id,
+ onSelect: () => onSelectFolder(f.id),
+ })),
+ ],
+ [rootFolders, totalBookmarks, selectedFolderId, onSelectFolder, t],
+ ),
+ )
+
return (
{/* All Bookmarks */}
diff --git a/apps/desktop-ui/src/components/dashboard/__tests__/dashboard-greeting.test.ts b/apps/desktop-ui/src/components/dashboard/__tests__/dashboard-greeting.test.ts
new file mode 100644
index 00000000..5789da6b
--- /dev/null
+++ b/apps/desktop-ui/src/components/dashboard/__tests__/dashboard-greeting.test.ts
@@ -0,0 +1,27 @@
+/**
+ * The dashboard greeting takes its name from the local profile (user
+ * preferences), never from `useAuth` — that identity is a fixed object whose
+ * displayName is always null by design (utils/useAuth.tsx:29).
+ */
+import { greetingFirstName } from "../types"
+
+describe("greetingFirstName", () => {
+ it("uses the first word of a full name", () => {
+ expect(greetingFirstName("Akhil Edathadan")).toBe("Akhil")
+ })
+
+ it("passes a single-word name through", () => {
+ expect(greetingFirstName("Akhil")).toBe("Akhil")
+ })
+
+ it("ignores surrounding and repeated whitespace", () => {
+ expect(greetingFirstName(" Akhil Edathadan ")).toBe("Akhil")
+ })
+
+ it("returns null for an unset name so the greeting falls back", () => {
+ expect(greetingFirstName("")).toBeNull()
+ expect(greetingFirstName(" ")).toBeNull()
+ expect(greetingFirstName(undefined)).toBeNull()
+ expect(greetingFirstName(null)).toBeNull()
+ })
+})
diff --git a/apps/desktop-ui/src/components/dashboard/dashboard-hero.tsx b/apps/desktop-ui/src/components/dashboard/dashboard-hero.tsx
index befde774..ed2b3884 100644
--- a/apps/desktop-ui/src/components/dashboard/dashboard-hero.tsx
+++ b/apps/desktop-ui/src/components/dashboard/dashboard-hero.tsx
@@ -4,12 +4,12 @@ import React from 'react'
import Link from 'next/link'
import { Layers, Zap, Pin, Clock, History } from 'lucide-react'
import { useTranslations } from 'next-intl'
-import { dashboardGreeting } from './types'
+import { dashboardGreeting, greetingFirstName } from './types'
+import { useAppUser } from '@/hooks/use-app-user'
import { useCountUp } from '@/hooks/use-count-up'
import { cn } from '@/lib/utils'
interface DashboardHeroProps {
- user: { displayName?: string | null } | null
totalTools: number
pinnedCount: number
recentCount: number
@@ -67,7 +67,6 @@ function Stat({
}
export function DashboardHero({
- user,
totalTools,
pinnedCount,
recentCount,
@@ -75,6 +74,10 @@ export function DashboardHero({
desktopOnly,
}: DashboardHeroProps) {
const t = useTranslations('Dashboard')
+ // The editable profile name lives in local preferences. `useAuth` is identity
+ // only — its displayName is a hardcoded null, so greeting off it never named
+ // anyone.
+ const firstName = greetingFirstName(useAppUser().name)
return (
<>
@@ -92,9 +95,7 @@ export function DashboardHero({
{dashboardGreeting(t)}
- {user?.displayName
- ? t('commaName', { name: user.displayName.split(' ')[0] })
- : ''}
+ {firstName ? t('commaName', { name: firstName }) : ''}