@agent-native/core 0.178.1 → 0.178.2-nightly-20260910195009

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +108 -121
  2. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +87 -220
  3. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +83 -221
  4. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +324 -460
  5. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +57 -247
  6. package/corpus/templates/clips/app/components/library/library-layout.tsx +15 -48
  7. package/corpus/templates/clips/changelog/2026-09-10-open-mcp-connections-in-new-tabs.md +1 -0
  8. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +84 -94
  9. package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +1 -1
  10. package/corpus/templates/crm/app/components/layout/CrmSidebar.tsx +108 -296
  11. package/corpus/templates/design/app/components/layout/Sidebar.tsx +55 -212
  12. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +72 -260
  13. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +143 -362
  14. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +64 -159
  15. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +152 -165
  16. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +82 -207
  17. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +61 -234
  18. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +73 -225
  19. package/dist/automation/index.d.ts +1 -1
  20. package/dist/client/EnvironmentBadge.d.ts +7 -4
  21. package/dist/client/EnvironmentBadge.js +26 -18
  22. package/dist/client/FeedbackButton.js +23 -8
  23. package/dist/client/app-providers.js +1 -1
  24. package/dist/client/auth/AuthPage.js +0 -38
  25. package/dist/client/components/icons/AgentNativeIcon.js +1 -1
  26. package/dist/client/index.d.ts +1 -0
  27. package/dist/client/index.js +1 -0
  28. package/dist/client/ui/AppSidebar.d.ts +15 -0
  29. package/dist/client/ui/AppSidebar.js +34 -0
  30. package/dist/client/ui/index.d.ts +1 -0
  31. package/dist/client/ui/index.js +1 -0
  32. package/dist/collab/awareness.d.ts +2 -2
  33. package/dist/collab/struct-routes.d.ts +1 -1
  34. package/dist/config.d.ts +4 -0
  35. package/dist/config.js +19 -3
  36. package/dist/progress/routes.d.ts +1 -1
  37. package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
  38. package/dist/provider-api/actions/provider-api.d.ts +15 -15
  39. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  40. package/dist/resources/handlers.d.ts +1 -1
  41. package/dist/secrets/routes.d.ts +9 -9
  42. package/dist/server/beta-opt-out-html.js +2 -2
  43. package/dist/templates/chat/app/components/layout/Sidebar.tsx +57 -247
  44. package/dist/vite/client.js +4 -1
  45. package/docs/content/template-clips-anonymous-intake.mdx +7 -5
  46. package/docs/content/template-clips-developers.mdx +1 -1
  47. package/package.json +3 -3
  48. package/src/templates/chat/app/components/layout/Sidebar.tsx +57 -247
@@ -13,11 +13,13 @@ import { useT } from "@agent-native/core/client/i18n";
13
13
  import { openCommandMenu } from "@agent-native/core/client/navigation";
14
14
  import { OrgSwitcher } from "@agent-native/core/client/org";
15
15
  import {
16
+ AppSidebar,
17
+ AppSidebarNavItem,
16
18
  AgentNativeIcon,
17
19
  buildSignInReturnHref,
18
20
  FeedbackButton,
21
+ type AppSidebarItemDefinition,
19
22
  } from "@agent-native/core/client/ui";
20
- import { SidebarFooterActions } from "@agent-native/toolkit/app-shell";
21
23
  import {
22
24
  ChatHistoryRail,
23
25
  type ChatHistoryItem,
@@ -25,13 +27,10 @@ import {
25
27
  import {
26
28
  IconClipboardCheck,
27
29
  IconEdit,
28
- IconLayoutSidebarLeftCollapse,
29
- IconLayoutSidebarLeftExpand,
30
30
  IconMessageCircle,
31
31
  IconPlus,
32
32
  IconRefresh,
33
33
  IconSettings,
34
- IconSearch,
35
34
  } from "@tabler/icons-react";
36
35
  import { useEffect, useMemo, useState, type MouseEvent } from "react";
37
36
  import { Link, useLocation, useNavigate } from "react-router";
@@ -540,216 +539,92 @@ export function Sidebar({
540
539
  const { session, isLoading: sessionLoading } = useSession();
541
540
  const t = useT();
542
541
  const returnPath = planReturnPathFromLocation(location);
543
- const ToggleIcon = collapsed
544
- ? IconLayoutSidebarLeftExpand
545
- : IconLayoutSidebarLeftCollapse;
546
- const collapseButton = collapsible ? (
547
- <Tooltip>
548
- <TooltipTrigger asChild>
549
- <Button
550
- type="button"
551
- size="icon"
552
- variant="ghost"
553
- className="size-8 shrink-0 text-muted-foreground"
554
- onClick={() => onCollapsedChange?.(!collapsed)}
555
- aria-label={
556
- collapsed
557
- ? t("sidebar.expandSidebar")
558
- : t("sidebar.collapseSidebar")
559
- }
560
- >
561
- <ToggleIcon className="size-4" />
562
- </Button>
563
- </TooltipTrigger>
564
- <TooltipContent side="right">
565
- {collapsed ? t("sidebar.expandSidebar") : t("sidebar.collapseSidebar")}
566
- </TooltipContent>
567
- </Tooltip>
568
- ) : null;
569
- const searchButton = (
570
- <Tooltip>
571
- <TooltipTrigger asChild>
572
- <Button
573
- type="button"
574
- size="icon"
575
- variant="ghost"
576
- className="size-8 shrink-0 text-muted-foreground"
577
- onClick={openCommandMenu}
578
- aria-label={t("plansPage.overview.searchPlaceholder")}
579
- >
580
- <IconSearch className="size-4" />
581
- </Button>
582
- </TooltipTrigger>
583
- <TooltipContent side="right">
584
- {t("plansPage.overview.searchPlaceholder")}
585
- </TooltipContent>
586
- </Tooltip>
587
- );
542
+
543
+ const secondaryItems: AppSidebarItemDefinition[] = [
544
+ {
545
+ to: "/settings",
546
+ label: t("navigation.settings"),
547
+ icon: IconSettings,
548
+ active: pathname.startsWith("/settings"),
549
+ },
550
+ ];
551
+
588
552
  const feedbackButton = (
589
- <FeedbackButton
590
- variant={collapsed ? "icon" : "sidebar"}
591
- side="right"
592
- className={collapsed ? "size-8" : "min-w-0"}
593
- />
553
+ <FeedbackButton variant={collapsed ? "icon" : "sidebar"} side="right" />
594
554
  );
595
555
 
596
- return (
597
- <aside
598
- data-collapsed={collapsed ? "true" : "false"}
599
- className={cn(
600
- "flex h-full min-w-0 shrink-0 flex-col overflow-hidden border-e border-border bg-sidebar text-sidebar-foreground transition-[width] duration-200 ease-out",
601
- collapsed ? "w-14" : "w-60",
602
- )}
556
+ const orgSwitcher = session ? (
557
+ <OrgSwitcher compact={collapsed} />
558
+ ) : !sessionLoading ? (
559
+ <Button
560
+ type="button"
561
+ size="sm"
562
+ variant="outline"
563
+ className={collapsed ? "!size-9 !p-0 text-xs" : "h-8 w-full px-3 text-xs"}
564
+ onClick={() => signInWithReturnPath(returnPath)}
603
565
  >
604
- <div
605
- className={cn(
606
- "group/brand flex h-12 shrink-0 items-center border-b border-border",
607
- collapsed ? "justify-center px-0" : "gap-2 px-3",
608
- )}
609
- >
610
- <button
611
- type="button"
612
- onClick={() => onCollapsedChange?.(!collapsed)}
613
- className={cn(
614
- "flex min-w-0 items-center gap-2 rounded outline-none focus-visible:ring-2 focus-visible:ring-ring",
615
- collapsed ? "justify-center" : "flex-1 text-start",
616
- )}
617
- aria-label={
618
- collapsible
619
- ? collapsed
620
- ? t("sidebar.expandSidebar")
621
- : t("sidebar.collapseSidebar")
622
- : undefined
623
- }
624
- disabled={!collapsible || !onCollapsedChange}
625
- data-sidebar-brand-toggle
626
- >
627
- <AgentNativeIcon
628
- aria-hidden="true"
629
- className="h-3.5 w-6 shrink-0 text-sidebar-foreground"
630
- />
631
- {!collapsed && (
632
- <span className="truncate text-sm font-semibold tracking-tight">
633
- {APP_TITLE}
634
- </span>
635
- )}
636
- </button>
637
- {!collapsed && <BrandingCustomizePopover />}
638
- </div>
639
-
640
- <nav className="flex-1 space-y-1 overflow-y-auto px-2 py-2">
641
- {navItems.map((item) => {
642
- const Icon = item.icon;
643
- const isActive =
644
- item.href === "/chat"
645
- ? pathname === "/chat"
646
- : item.href === "/plans"
647
- ? pathname.startsWith("/plans") ||
648
- pathname.startsWith("/recaps") ||
649
- pathname.startsWith("/local-plans")
650
- : pathname.startsWith(item.href);
651
- const link = (
652
- <Link
653
- to={item.href}
654
- className={cn(
655
- "flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors",
656
- isActive
657
- ? "bg-sidebar-accent text-sidebar-accent-foreground"
658
- : "text-muted-foreground hover:bg-sidebar-accent/50 hover:text-foreground",
659
- collapsed && "justify-center gap-0 px-0",
660
- )}
661
- >
662
- <Icon className="h-4 w-4 shrink-0" />
663
- {collapsed ? (
664
- <span className="sr-only">{t(item.labelKey)}</span>
665
- ) : (
666
- t(item.labelKey)
667
- )}
668
- </Link>
669
- );
670
- return (
671
- <div key={item.href}>
672
- {link}
673
- {item.href === "/chat" ? (
674
- <PlanChatsSection collapsed={collapsed} open={isActive} />
675
- ) : null}
676
- {item.href === "/plans" && isActive ? (
677
- <PlansSidebarSection collapsed={collapsed} />
678
- ) : null}
679
- </div>
680
- );
681
- })}
682
- </nav>
683
-
684
- <nav className="grid shrink-0 gap-1 px-2 py-1">
685
- {bottomNavItems.map((item) => {
686
- const Icon = item.icon;
687
- const isActive = pathname.startsWith(item.href);
688
- const link = (
689
- <Link
690
- to={item.href}
691
- className={cn(
692
- "flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors",
693
- isActive
694
- ? "bg-sidebar-accent text-sidebar-accent-foreground"
695
- : "text-muted-foreground hover:bg-sidebar-accent/50 hover:text-foreground",
696
- collapsed && "justify-center gap-0 px-0",
697
- )}
698
- aria-label={collapsed ? t(item.labelKey) : undefined}
699
- >
700
- <Icon className="h-4 w-4 shrink-0" />
701
- {collapsed ? (
702
- <span className="sr-only">{t(item.labelKey)}</span>
703
- ) : (
704
- t(item.labelKey)
705
- )}
706
- </Link>
707
- );
708
- return collapsed ? (
709
- <Tooltip key={item.href}>
710
- <TooltipTrigger asChild>{link}</TooltipTrigger>
711
- <TooltipContent side="right">{t(item.labelKey)}</TooltipContent>
712
- </Tooltip>
713
- ) : (
714
- <div key={item.href}>{link}</div>
715
- );
716
- })}
717
- </nav>
718
-
719
- {!collapsed && session ? (
720
- <div className="space-y-2 px-3 py-2 empty:hidden">
721
- <DevDatabaseLink />
722
- <OrgSwitcher />
723
- </div>
724
- ) : null}
566
+ {collapsed ? "In" : t("sidebar.signIn")}
567
+ </Button>
568
+ ) : null;
725
569
 
726
- {!collapsed && !sessionLoading && !session ? (
727
- <div className="space-y-2 px-3 py-2">
728
- <DevDatabaseLink />
729
- <Button
730
- type="button"
731
- size="sm"
732
- variant="outline"
733
- className="h-8 px-3 text-xs"
734
- onClick={() => signInWithReturnPath(returnPath)}
570
+ return (
571
+ <AppSidebar
572
+ collapsed={collapsed}
573
+ collapsible={collapsible}
574
+ onCollapsedChange={onCollapsedChange}
575
+ brandName={APP_TITLE}
576
+ brandHref="/plans"
577
+ brandLink={
578
+ <div className="group/brand flex min-w-0 items-center gap-1">
579
+ <Link
580
+ to="/plans"
581
+ className="flex min-w-0 items-center gap-2 rounded text-start outline-none focus-visible:ring-2 focus-visible:ring-ring"
735
582
  >
736
- {t("sidebar.signIn")}
737
- </Button>
583
+ <AgentNativeIcon
584
+ aria-hidden="true"
585
+ className="h-3.5 w-6 shrink-0 text-primary"
586
+ />
587
+ {!collapsed && (
588
+ <span className="truncate text-sm font-semibold text-primary">
589
+ {APP_TITLE}
590
+ </span>
591
+ )}
592
+ </Link>
593
+ {!collapsed ? <BrandingCustomizePopover /> : null}
738
594
  </div>
739
- ) : null}
595
+ }
596
+ secondaryItems={secondaryItems}
597
+ feedback={feedbackButton}
598
+ orgSwitcher={orgSwitcher}
599
+ footerExtras={<DevDatabaseLink />}
600
+ >
601
+ <div>
602
+ <AppSidebarNavItem
603
+ to="/chat"
604
+ label={t("navigation.ask")}
605
+ icon={IconMessageCircle}
606
+ active={pathname === "/chat"}
607
+ />
608
+ <PlanChatsSection collapsed={collapsed} open={pathname === "/chat"} />
609
+ </div>
740
610
 
741
- {!collapsed && sessionLoading ? (
742
- <div className="px-3 py-2">
743
- <DevDatabaseLink />
744
- </div>
745
- ) : null}
746
-
747
- <SidebarFooterActions
748
- collapsed={collapsed}
749
- feedback={feedbackButton}
750
- search={searchButton}
751
- collapse={collapseButton}
752
- />
753
- </aside>
611
+ <div>
612
+ <AppSidebarNavItem
613
+ to="/plans"
614
+ label={t("navigation.plan")}
615
+ icon={IconClipboardCheck}
616
+ active={
617
+ pathname.startsWith("/plans") ||
618
+ pathname.startsWith("/recaps") ||
619
+ pathname.startsWith("/local-plans")
620
+ }
621
+ />
622
+ {pathname.startsWith("/plans") ||
623
+ pathname.startsWith("/recaps") ||
624
+ pathname.startsWith("/local-plans") ? (
625
+ <PlansSidebarSection collapsed={collapsed} />
626
+ ) : null}
627
+ </div>
628
+ </AppSidebar>
754
629
  );
755
630
  }
@@ -2,42 +2,28 @@ import { DevDatabaseLink } from "@agent-native/core/client/db-admin";
2
2
  import { useT } from "@agent-native/core/client/i18n";
3
3
  import { openCommandMenu } from "@agent-native/core/client/navigation";
4
4
  import { OrgSwitcher } from "@agent-native/core/client/org";
5
- import { AgentNativeIcon, FeedbackButton } from "@agent-native/core/client/ui";
6
- import { SidebarFooterActions } from "@agent-native/toolkit/app-shell";
5
+ import {
6
+ AppSidebar,
7
+ FeedbackButton,
8
+ type AppSidebarItemDefinition,
9
+ } from "@agent-native/core/client/ui";
7
10
  import {
8
11
  IconLayoutGrid,
9
12
  IconComponents,
10
- IconSettings,
11
- IconLayoutSidebarLeftCollapse,
12
- IconLayoutSidebarLeftExpand,
13
13
  IconSearch,
14
+ IconSettings,
14
15
  } from "@tabler/icons-react";
15
- import { Link, useLocation } from "react-router";
16
+ import { useLocation } from "react-router";
16
17
 
18
+ import { Button } from "@/components/ui/button";
17
19
  import {
18
20
  Tooltip,
19
21
  TooltipContent,
20
22
  TooltipTrigger,
21
23
  } from "@/components/ui/tooltip";
22
- import { cn } from "@/lib/utils";
23
-
24
- const navItems = [
25
- { icon: IconLayoutGrid, labelKey: "navigation.decks", href: "/home" },
26
- {
27
- icon: IconComponents,
28
- labelKey: "navigation.designSystems",
29
- href: "/design-systems",
30
- },
31
- ];
32
-
33
- const bottomNavItems = [
34
- { icon: IconSettings, labelKey: "navigation.settings", href: "/settings" },
35
- ];
36
24
 
37
25
  interface SidebarProps {
38
26
  collapsed: boolean;
39
- /** Omit to hide the collapse/expand toggle (e.g. inside the mobile drawer,
40
- * where toggling the desktop preference is meaningless). */
41
27
  onToggleCollapsed?: () => void;
42
28
  }
43
29
 
@@ -50,230 +36,71 @@ export function Sidebar({ collapsed, onToggleCollapsed }: SidebarProps) {
50
36
  ? location.pathname === "/home"
51
37
  : location.pathname.startsWith(href);
52
38
 
53
- const collapseButton = onToggleCollapsed ? (
54
- <Tooltip>
55
- <TooltipTrigger asChild>
56
- <button
57
- type="button"
58
- onClick={onToggleCollapsed}
59
- aria-label={
60
- collapsed
61
- ? t("sidebar.expandSidebar")
62
- : t("sidebar.collapseSidebar")
63
- }
64
- className="flex size-8 items-center justify-center rounded-md text-muted-foreground hover:bg-sidebar-accent/50 hover:text-foreground"
65
- >
66
- {collapsed ? (
67
- <IconLayoutSidebarLeftExpand className="h-4 w-4 rtl:-scale-x-100" />
68
- ) : (
69
- <IconLayoutSidebarLeftCollapse className="h-4 w-4 rtl:-scale-x-100" />
70
- )}
71
- </button>
72
- </TooltipTrigger>
73
- <TooltipContent side="right">
74
- {collapsed ? t("sidebar.expandSidebar") : t("sidebar.collapseSidebar")}
75
- </TooltipContent>
76
- </Tooltip>
77
- ) : null;
39
+ const items: AppSidebarItemDefinition[] = [
40
+ {
41
+ to: "/home",
42
+ label: t("navigation.decks"),
43
+ icon: IconLayoutGrid,
44
+ active: isItemActive("/home"),
45
+ },
46
+ {
47
+ to: "/design-systems",
48
+ label: t("navigation.designSystems"),
49
+ icon: IconComponents,
50
+ active: isItemActive("/design-systems"),
51
+ },
52
+ ];
53
+
54
+ const secondaryItems: AppSidebarItemDefinition[] = [
55
+ {
56
+ to: "/settings",
57
+ label: t("navigation.settings"),
58
+ icon: IconSettings,
59
+ active: isItemActive("/settings"),
60
+ },
61
+ ];
62
+
63
+ const feedbackButton = (
64
+ <FeedbackButton variant={collapsed ? "icon" : "sidebar"} side="right" />
65
+ );
66
+
67
+ const orgSwitcher = <OrgSwitcher compact={collapsed} />;
68
+
78
69
  const searchButton = (
79
70
  <Tooltip>
80
71
  <TooltipTrigger asChild>
81
- <button
72
+ <Button
82
73
  type="button"
74
+ variant="ghost"
75
+ size="icon"
76
+ className="size-9 shrink-0 text-primary hover:bg-accent/60 hover:text-primary"
83
77
  onClick={openCommandMenu}
84
78
  aria-label={t("root.searchDecks")}
85
- className="flex size-8 items-center justify-center rounded-md text-muted-foreground hover:bg-sidebar-accent/50 hover:text-foreground"
86
79
  >
87
- <IconSearch className="h-4 w-4" />
88
- </button>
80
+ <IconSearch className="size-4" />
81
+ </Button>
89
82
  </TooltipTrigger>
90
83
  <TooltipContent side="right">{t("root.searchDecks")}</TooltipContent>
91
84
  </Tooltip>
92
85
  );
93
- const feedbackButton = (
94
- <FeedbackButton
95
- variant={collapsed ? "icon" : "sidebar"}
96
- side="right"
97
- className={collapsed ? "size-8" : "min-w-0"}
98
- />
99
- );
100
-
101
- const brand = (
102
- <div
103
- className={cn(
104
- "flex items-center gap-2",
105
- collapsed ? "size-8 justify-center" : "min-w-0 flex-1",
106
- )}
107
- >
108
- <AgentNativeIcon
109
- aria-hidden="true"
110
- className="h-3.5 w-6 shrink-0 text-sidebar-foreground"
111
- />
112
- {!collapsed && (
113
- <span className="truncate text-sm font-semibold tracking-tight">
114
- {t("navigation.brand")}
115
- </span>
116
- )}
117
- </div>
118
- );
119
- const brandControl = onToggleCollapsed ? (
120
- <Tooltip delayDuration={0}>
121
- <TooltipTrigger asChild>
122
- <button
123
- type="button"
124
- onClick={onToggleCollapsed}
125
- aria-label={
126
- collapsed
127
- ? t("sidebar.expandSidebar")
128
- : t("sidebar.collapseSidebar")
129
- }
130
- className={cn(
131
- "flex min-w-0 items-center rounded-md text-foreground outline-none transition-colors hover:bg-sidebar-accent/50 focus-visible:ring-2 focus-visible:ring-ring",
132
- collapsed
133
- ? "size-8 justify-center"
134
- : "w-full gap-2 px-1 py-1 text-start",
135
- )}
136
- data-sidebar-brand-toggle
137
- >
138
- {brand}
139
- </button>
140
- </TooltipTrigger>
141
- <TooltipContent side="right">
142
- {collapsed ? t("sidebar.expandSidebar") : t("sidebar.collapseSidebar")}
143
- </TooltipContent>
144
- </Tooltip>
145
- ) : (
146
- brand
147
- );
148
-
149
- if (collapsed) {
150
- return (
151
- <aside className="flex h-full w-12 shrink-0 flex-col items-center overflow-hidden bg-sidebar text-sidebar-foreground transition-[width] duration-200 ease-out">
152
- <div className="flex h-12 w-full shrink-0 items-center justify-center">
153
- {brandControl}
154
- </div>
155
- <nav className="flex min-h-0 flex-1 flex-col items-center gap-1 overflow-y-auto pt-1">
156
- {navItems.map((item) => {
157
- const Icon = item.icon;
158
- const isActive = isItemActive(item.href);
159
- return (
160
- <Tooltip key={item.href} delayDuration={0}>
161
- <TooltipTrigger asChild>
162
- <Link
163
- to={item.href}
164
- aria-label={t(item.labelKey)}
165
- className={cn(
166
- "flex h-10 w-10 items-center justify-center rounded-md transition-colors",
167
- isActive
168
- ? "bg-sidebar-accent text-sidebar-accent-foreground"
169
- : "text-muted-foreground hover:bg-sidebar-accent/50 hover:text-foreground",
170
- )}
171
- >
172
- <Icon className="h-4 w-4" />
173
- </Link>
174
- </TooltipTrigger>
175
- <TooltipContent side="right">{t(item.labelKey)}</TooltipContent>
176
- </Tooltip>
177
- );
178
- })}
179
- </nav>
180
- <nav className="flex shrink-0 flex-col items-center gap-1 py-1">
181
- {bottomNavItems.map((item) => {
182
- const Icon = item.icon;
183
- return (
184
- <Tooltip key={item.href} delayDuration={0}>
185
- <TooltipTrigger asChild>
186
- <Link
187
- to={item.href}
188
- aria-label={t(item.labelKey)}
189
- className={cn(
190
- "flex h-9 w-9 items-center justify-center rounded-md text-muted-foreground hover:bg-sidebar-accent/50 hover:text-foreground",
191
- isItemActive(item.href) &&
192
- "bg-sidebar-accent text-sidebar-accent-foreground",
193
- )}
194
- >
195
- <Icon className="h-4 w-4 shrink-0" />
196
- </Link>
197
- </TooltipTrigger>
198
- <TooltipContent side="right">{t(item.labelKey)}</TooltipContent>
199
- </Tooltip>
200
- );
201
- })}
202
- </nav>
203
- <SidebarFooterActions
204
- collapsed
205
- feedback={feedbackButton}
206
- search={searchButton}
207
- collapse={collapseButton}
208
- />
209
- </aside>
210
- );
211
- }
212
86
 
213
87
  return (
214
- <aside className="flex h-full w-56 min-w-0 shrink-0 flex-col overflow-hidden bg-sidebar text-sidebar-foreground transition-[width] duration-200 ease-out">
215
- <div className="flex h-12 shrink-0 items-center px-4">{brandControl}</div>
216
-
217
- <div className="flex min-h-0 flex-1 flex-col overflow-y-auto">
218
- <nav className="space-y-1 px-2 py-2">
219
- {navItems.map((item) => {
220
- const Icon = item.icon;
221
- const isActive = isItemActive(item.href);
222
- return (
223
- <Link
224
- key={item.href}
225
- to={item.href}
226
- className={cn(
227
- "flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors",
228
- isActive
229
- ? "bg-sidebar-accent text-sidebar-accent-foreground"
230
- : "text-muted-foreground hover:bg-sidebar-accent/50 hover:text-foreground",
231
- )}
232
- >
233
- <Icon className="h-4 w-4 shrink-0" />
234
- {t(item.labelKey)}
235
- </Link>
236
- );
237
- })}
238
- </nav>
239
-
240
- <div className="mt-auto shrink-0">
241
- <nav className="space-y-1 px-2 py-1">
242
- {bottomNavItems.map((item) => {
243
- const Icon = item.icon;
244
- const isActive = isItemActive(item.href);
245
- return (
246
- <Link
247
- key={item.href}
248
- to={item.href}
249
- className={cn(
250
- "flex items-center gap-3 rounded-lg px-3 py-2 text-sm",
251
- isActive
252
- ? "bg-sidebar-accent text-sidebar-accent-foreground"
253
- : "text-muted-foreground hover:bg-sidebar-accent/50 hover:text-foreground",
254
- )}
255
- >
256
- <Icon className="h-4 w-4 shrink-0" />
257
- {t(item.labelKey)}
258
- </Link>
259
- );
260
- })}
261
- </nav>
262
-
263
- <div className="px-3 py-2 empty:hidden">
264
- <OrgSwitcher />
265
- </div>
266
-
267
- <div className="px-3 py-2 empty:hidden">
268
- <DevDatabaseLink />
269
- </div>
270
- <SidebarFooterActions
271
- feedback={feedbackButton}
272
- search={searchButton}
273
- collapse={collapseButton}
274
- />
275
- </div>
276
- </div>
277
- </aside>
88
+ <AppSidebar
89
+ collapsed={collapsed}
90
+ collapsible={Boolean(onToggleCollapsed)}
91
+ onCollapsedChange={onToggleCollapsed}
92
+ brandName={t("navigation.brand")}
93
+ brandHref="/home"
94
+ items={items}
95
+ secondaryItems={secondaryItems}
96
+ feedback={feedbackButton}
97
+ orgSwitcher={orgSwitcher}
98
+ footerExtras={
99
+ <>
100
+ {searchButton}
101
+ <DevDatabaseLink />
102
+ </>
103
+ }
104
+ />
278
105
  );
279
106
  }