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

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 (50) hide show
  1. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +108 -121
  2. package/corpus/templates/assets/actions/create-generation-session.ts +2 -6
  3. package/corpus/templates/assets/actions/generate-image.ts +3 -12
  4. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +87 -220
  5. package/corpus/templates/assets/server/lib/library-access.ts +23 -0
  6. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +83 -221
  7. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +324 -460
  8. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +57 -247
  9. package/corpus/templates/clips/app/components/library/library-layout.tsx +15 -48
  10. package/corpus/templates/clips/changelog/2026-09-10-open-mcp-connections-in-new-tabs.md +1 -0
  11. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +84 -94
  12. package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +1 -1
  13. package/corpus/templates/crm/app/components/layout/CrmSidebar.tsx +108 -296
  14. package/corpus/templates/design/app/components/layout/Sidebar.tsx +55 -212
  15. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +72 -260
  16. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +143 -362
  17. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +64 -159
  18. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +152 -165
  19. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +82 -207
  20. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +61 -234
  21. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +73 -225
  22. package/dist/client/EnvironmentBadge.d.ts +7 -4
  23. package/dist/client/EnvironmentBadge.js +26 -18
  24. package/dist/client/FeedbackButton.js +23 -8
  25. package/dist/client/app-providers.js +1 -1
  26. package/dist/client/auth/AuthPage.js +0 -38
  27. package/dist/client/components/icons/AgentNativeIcon.js +1 -1
  28. package/dist/client/index.d.ts +1 -0
  29. package/dist/client/index.js +1 -0
  30. package/dist/client/ui/AppSidebar.d.ts +15 -0
  31. package/dist/client/ui/AppSidebar.js +34 -0
  32. package/dist/client/ui/index.d.ts +1 -0
  33. package/dist/client/ui/index.js +1 -0
  34. package/dist/collab/routes.d.ts +1 -1
  35. package/dist/config.d.ts +4 -0
  36. package/dist/config.js +19 -3
  37. package/dist/file-upload/actions/upload-image.d.ts +3 -3
  38. package/dist/notifications/routes.d.ts +2 -2
  39. package/dist/observability/routes.d.ts +5 -5
  40. package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
  41. package/dist/provider-api/actions/provider-api.d.ts +15 -15
  42. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  43. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  44. package/dist/server/beta-opt-out-html.js +2 -2
  45. package/dist/templates/chat/app/components/layout/Sidebar.tsx +57 -247
  46. package/dist/vite/client.js +4 -1
  47. package/docs/content/template-clips-anonymous-intake.mdx +7 -5
  48. package/docs/content/template-clips-developers.mdx +1 -1
  49. package/package.json +2 -2
  50. package/src/templates/chat/app/components/layout/Sidebar.tsx +57 -247
@@ -3,8 +3,12 @@ import { useT } from "@agent-native/core/client/i18n";
3
3
  import { startWorkspaceProviderOAuth } from "@agent-native/core/client/integrations";
4
4
  import { openCommandMenu } from "@agent-native/core/client/navigation";
5
5
  import { OrgSwitcher } from "@agent-native/core/client/org";
6
- import { AgentNativeIcon, FeedbackButton } from "@agent-native/core/client/ui";
7
- import { SidebarFooterActions } from "@agent-native/toolkit/app-shell";
6
+ import {
7
+ AppSidebar,
8
+ AppSidebarNavItem,
9
+ FeedbackButton,
10
+ type AppSidebarItemDefinition,
11
+ } from "@agent-native/core/client/ui";
8
12
  import { getWeekdayOrder, getWeekStartsOn } from "@shared/calendar-week";
9
13
  import {
10
14
  IconCalendar,
@@ -21,8 +25,6 @@ import {
21
25
  IconCheck,
22
26
  IconEye,
23
27
  IconEyeOff,
24
- IconLayoutSidebarLeftCollapse,
25
- IconLayoutSidebarLeftExpand,
26
28
  IconSearch,
27
29
  IconAlertTriangle,
28
30
  } from "@tabler/icons-react";
@@ -484,7 +486,7 @@ function GoogleCalendarsSections({
484
486
  return (
485
487
  <div
486
488
  key={preferenceKey}
487
- className="group flex min-h-7 items-center gap-2 px-3 text-xs"
489
+ className="group flex min-h-7 items-center gap-2 px-2 text-xs"
488
490
  >
489
491
  <Popover>
490
492
  <PopoverTrigger asChild>
@@ -720,33 +722,6 @@ export function Sidebar({
720
722
  onClose();
721
723
  }
722
724
 
723
- const collapseButton = onCollapsedChange ? (
724
- <Tooltip>
725
- <TooltipTrigger asChild>
726
- <Button
727
- type="button"
728
- variant="ghost"
729
- size="icon"
730
- className="h-8 w-8 shrink-0 text-muted-foreground"
731
- onClick={() => onCollapsedChange(!collapsed)}
732
- aria-label={
733
- collapsed
734
- ? t("sidebar.expandSidebar")
735
- : t("sidebar.collapseSidebar")
736
- }
737
- >
738
- {collapsed ? (
739
- <IconLayoutSidebarLeftExpand className="h-4 w-4 rtl:-scale-x-100" />
740
- ) : (
741
- <IconLayoutSidebarLeftCollapse className="h-4 w-4 rtl:-scale-x-100" />
742
- )}
743
- </Button>
744
- </TooltipTrigger>
745
- <TooltipContent side="top">
746
- {collapsed ? t("sidebar.expandSidebar") : t("sidebar.collapseSidebar")}
747
- </TooltipContent>
748
- </Tooltip>
749
- ) : null;
750
725
  const searchButton = (
751
726
  <Tooltip>
752
727
  <TooltipTrigger asChild>
@@ -764,14 +739,22 @@ export function Sidebar({
764
739
  <TooltipContent side="top">{t("root.commandSearch")}</TooltipContent>
765
740
  </Tooltip>
766
741
  );
742
+ const secondaryItems: AppSidebarItemDefinition[] = bottomNavItems.map(
743
+ (item) => ({
744
+ to: item.path,
745
+ label: t(item.labelKey),
746
+ icon: item.icon,
747
+ active: location.pathname.startsWith(item.path),
748
+ onClick: onClose,
749
+ }),
750
+ );
751
+
767
752
  const feedbackButton = (
768
- <FeedbackButton
769
- variant={collapsed ? "icon" : "sidebar"}
770
- side="right"
771
- className={collapsed ? "h-8 w-8" : "min-w-0"}
772
- />
753
+ <FeedbackButton variant={collapsed ? "icon" : "sidebar"} side="right" />
773
754
  );
774
755
 
756
+ const orgSwitcher = <OrgSwitcher compact={collapsed} />;
757
+
775
758
  return (
776
759
  <>
777
760
  {/* Mobile overlay */}
@@ -782,467 +765,348 @@ export function Sidebar({
782
765
  />
783
766
  )}
784
767
 
785
- <aside
786
- data-open={open ? "true" : "false"}
787
- data-collapsed={collapsed ? "true" : "false"}
768
+ <AppSidebar
769
+ collapsed={collapsed}
770
+ collapsible={Boolean(onCollapsedChange)}
771
+ onCollapsedChange={onCollapsedChange}
772
+ brandName={t("navigation.brand")}
773
+ brandHref="/home"
774
+ secondaryItems={secondaryItems}
775
+ feedback={feedbackButton}
776
+ orgSwitcher={orgSwitcher}
777
+ footerExtras={
778
+ <>
779
+ {searchButton}
780
+ <DevDatabaseLink />
781
+ </>
782
+ }
788
783
  className={cn(
789
- "agent-layout-left-drawer calendar-app-sidebar fixed start-0 top-0 z-50 flex h-full min-w-0 flex-col overflow-hidden bg-sidebar transition-[width,translate] duration-200 ease-out lg:static",
790
- collapsed ? "w-12" : "w-56",
784
+ "calendar-app-sidebar",
785
+ // Match calendar's lg mobile breakpoint (shared sidebar defaults to md).
786
+ "max-lg:!fixed max-lg:inset-y-0 max-lg:start-0 max-lg:z-50 lg:!static",
787
+ open
788
+ ? "translate-x-0"
789
+ : "-translate-x-full rtl:translate-x-full lg:translate-x-0",
791
790
  )}
792
791
  >
793
- {/* Logo */}
794
- <div
795
- className={cn(
796
- "flex h-12 shrink-0 items-center justify-between gap-2.5",
797
- collapsed ? "px-1" : "px-4",
798
- )}
799
- >
800
- <Link
801
- to="/home"
802
- onClick={(event) => {
803
- onClose();
804
- if (
805
- !onCollapsedChange ||
806
- event.metaKey ||
807
- event.ctrlKey ||
808
- event.shiftKey ||
809
- event.altKey ||
810
- event.button !== 0
811
- ) {
812
- return;
813
- }
814
- event.preventDefault();
815
- onCollapsedChange(!collapsed);
816
- }}
817
- className={cn(
818
- "flex items-center gap-2 rounded text-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring",
819
- collapsed ? "size-8 justify-center" : "flex-1",
820
- )}
821
- aria-label={
822
- onCollapsedChange
823
- ? collapsed
824
- ? t("sidebar.expandSidebar")
825
- : t("sidebar.collapseSidebar")
826
- : collapsed
827
- ? t("navigation.brand")
828
- : undefined
829
- }
830
- data-sidebar-brand-toggle
831
- >
832
- <AgentNativeIcon
833
- aria-hidden="true"
834
- className="h-3.5 w-6 shrink-0 text-foreground"
792
+ {collapsed ? (
793
+ navItems.map((item) => {
794
+ const isActive =
795
+ item.path === "/"
796
+ ? location.pathname === "/"
797
+ : location.pathname.startsWith(item.path);
798
+ return (
799
+ <AppSidebarNavItem
800
+ key={item.path}
801
+ to={item.path}
802
+ label={t(item.labelKey)}
803
+ icon={item.icon}
804
+ active={isActive}
805
+ onClick={onClose}
806
+ />
807
+ );
808
+ })
809
+ ) : (
810
+ <>
811
+ {/* Mini calendar */}
812
+ <MiniCalendar
813
+ selectedDate={selectedDate}
814
+ onDateSelect={handleMiniCalendarDateSelect}
835
815
  />
836
- {!collapsed && (
837
- <span className="text-base font-semibold tracking-tight">
838
- {t("navigation.brand")}
839
- </span>
840
- )}
841
- </Link>
842
- </div>
843
816
 
844
- <div className="min-h-0 flex-1 overflow-y-auto">
845
- {collapsed ? (
846
- <nav className="flex flex-col items-center gap-1 px-1 py-2">
817
+ {/* Nav */}
818
+ <div className="space-y-0.5">
847
819
  {navItems.map((item) => {
848
820
  const isActive =
849
821
  item.path === "/"
850
822
  ? location.pathname === "/"
851
823
  : location.pathname.startsWith(item.path);
824
+
852
825
  return (
853
- <Tooltip key={item.path}>
854
- <TooltipTrigger asChild>
855
- <Link
856
- to={item.path}
857
- onClick={onClose}
858
- aria-label={t(item.labelKey)}
859
- className={cn(
860
- "flex h-10 w-10 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent/60 hover:text-foreground",
861
- isActive && "bg-primary/10 text-primary",
862
- )}
863
- >
864
- <item.icon className="h-4 w-4" />
865
- </Link>
866
- </TooltipTrigger>
867
- <TooltipContent side="right">
868
- {t(item.labelKey)}
869
- </TooltipContent>
870
- </Tooltip>
826
+ <AppSidebarNavItem
827
+ key={item.path}
828
+ to={item.path}
829
+ label={t(item.labelKey)}
830
+ icon={item.icon}
831
+ active={isActive}
832
+ onClick={onClose}
833
+ />
871
834
  );
872
835
  })}
873
- </nav>
874
- ) : (
875
- <>
876
- {/* Mini calendar */}
877
- <MiniCalendar
878
- selectedDate={selectedDate}
879
- onDateSelect={handleMiniCalendarDateSelect}
880
- />
881
-
882
- {/* Nav */}
883
- <nav className="space-y-0.5 p-2.5">
884
- {navItems.map((item) => {
885
- const isActive =
886
- item.path === "/"
887
- ? location.pathname === "/"
888
- : location.pathname.startsWith(item.path);
836
+ </div>
889
837
 
890
- return (
891
- <Link
892
- key={item.path}
893
- to={item.path}
894
- onClick={onClose}
895
- className={cn(
896
- "flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors",
897
- isActive
898
- ? "bg-primary/10 text-primary"
899
- : "text-muted-foreground hover:bg-accent/60 hover:text-foreground",
900
- )}
901
- >
902
- <item.icon className="h-4 w-4" />
903
- {t(item.labelKey)}
904
- </Link>
905
- );
906
- })}
907
- </nav>
838
+ {/* Google status / connect CTA */}
839
+ {!googleStatus.isLoading &&
840
+ !isConnected &&
841
+ (googleStatus.data?.configured === true ||
842
+ canOfferGoogleOAuthSetup) && <GoogleConnectSidebarButton />}
908
843
 
909
- {/* Google status / connect CTA */}
910
- {!googleStatus.isLoading &&
911
- !isConnected &&
912
- (googleStatus.data?.configured === true ||
913
- canOfferGoogleOAuthSetup) && <GoogleConnectSidebarButton />}
844
+ {isConnected && (googleStatus.data?.accounts?.length ?? 0) > 0 && (
845
+ <GoogleCalendarsSections onClose={onClose} />
846
+ )}
914
847
 
848
+ {/* Other Calendars — people overlays + external ICS feeds combined */}
849
+ <div className="px-1.5 py-1.5">
850
+ <div className="flex min-h-8 items-center justify-between px-3">
851
+ <div className="flex items-center gap-1">
852
+ <span className="text-[11px] font-medium uppercase tracking-wider text-muted-foreground">
853
+ {t("sidebar.otherCalendars")}
854
+ </span>
855
+ <Tooltip>
856
+ <TooltipTrigger asChild>
857
+ <span className="flex items-center text-muted-foreground/40 hover:text-muted-foreground cursor-default">
858
+ <IconInfoCircle className="h-3 w-3" />
859
+ </span>
860
+ </TooltipTrigger>
861
+ <TooltipContent
862
+ side="right"
863
+ className="pointer-events-none"
864
+ >
865
+ <p>{t("sidebar.otherCalendarsDescription")}</p>
866
+ </TooltipContent>
867
+ </Tooltip>
868
+ </div>
869
+ <button
870
+ type="button"
871
+ aria-label={t("eventForm.addCalendar")}
872
+ onClick={() => {
873
+ setAddCalendarDefaultTab("people");
874
+ setAddCalendarOpen(true);
875
+ }}
876
+ className="flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:text-foreground"
877
+ >
878
+ <IconPlus className="h-3.5 w-3.5" />
879
+ </button>
880
+ </div>
915
881
  {isConnected &&
916
882
  (googleStatus.data?.accounts?.length ?? 0) > 0 && (
917
- <GoogleCalendarsSections onClose={onClose} />
883
+ <GoogleCalendarsSections onClose={onClose} section="other" />
918
884
  )}
919
-
920
- {/* Other Calendars — people overlays + external ICS feeds combined */}
921
- <div className="px-1.5 py-1.5">
922
- <div className="flex min-h-8 items-center justify-between px-3">
923
- <div className="flex items-center gap-1">
924
- <span className="text-[11px] font-medium uppercase tracking-wider text-muted-foreground">
925
- {t("sidebar.otherCalendars")}
926
- </span>
927
- <Tooltip>
928
- <TooltipTrigger asChild>
929
- <span className="flex items-center text-muted-foreground/40 hover:text-muted-foreground cursor-default">
930
- <IconInfoCircle className="h-3 w-3" />
931
- </span>
932
- </TooltipTrigger>
933
- <TooltipContent
934
- side="right"
935
- className="pointer-events-none"
936
- >
937
- <p>{t("sidebar.otherCalendarsDescription")}</p>
938
- </TooltipContent>
939
- </Tooltip>
940
- </div>
941
- <button
942
- type="button"
943
- aria-label={t("eventForm.addCalendar")}
944
- onClick={() => {
945
- setAddCalendarDefaultTab("people");
946
- setAddCalendarOpen(true);
947
- }}
948
- className="flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:text-foreground"
949
- >
950
- <IconPlus className="h-3.5 w-3.5" />
951
- </button>
952
- </div>
953
- {isConnected &&
954
- (googleStatus.data?.accounts?.length ?? 0) > 0 && (
955
- <GoogleCalendarsSections
956
- onClose={onClose}
957
- section="other"
958
- />
959
- )}
960
- {(overlayPeople.length > 0 || externalCalendars.length > 0) && (
961
- <div className="mt-1 space-y-1">
962
- {overlayPeople.length > 0 && (
963
- <Collapsible
964
- open={peopleGroupOpen}
965
- onOpenChange={setPeopleGroupOpen}
966
- >
967
- <CollapsibleTrigger asChild>
968
- <button
969
- type="button"
970
- className="flex h-7 w-full items-center gap-1 rounded px-3 text-xs text-muted-foreground hover:bg-accent/50 hover:text-foreground"
885
+ {(overlayPeople.length > 0 || externalCalendars.length > 0) && (
886
+ <div className="mt-1 space-y-1">
887
+ {overlayPeople.length > 0 && (
888
+ <Collapsible
889
+ open={peopleGroupOpen}
890
+ onOpenChange={setPeopleGroupOpen}
891
+ >
892
+ <CollapsibleTrigger asChild>
893
+ <button
894
+ type="button"
895
+ className="flex h-7 w-full items-center gap-1 rounded px-3 text-xs text-muted-foreground hover:bg-accent/50 hover:text-foreground"
896
+ >
897
+ {peopleGroupOpen ? (
898
+ <IconChevronDown className="h-3 w-3" />
899
+ ) : (
900
+ <IconChevronRight className="h-3 w-3 rtl:-scale-x-100" />
901
+ )}
902
+ <span className="min-w-0 flex-1 text-start">
903
+ People
904
+ </span>
905
+ <span className="text-[10px]">
906
+ {overlayPeople.length}
907
+ </span>
908
+ </button>
909
+ </CollapsibleTrigger>
910
+ <CollapsibleContent className="space-y-0.5">
911
+ {overlayPeople.map((person) => (
912
+ <div
913
+ key={person.email}
914
+ className="group flex min-h-7 items-center gap-2 px-2 text-xs"
971
915
  >
972
- {peopleGroupOpen ? (
973
- <IconChevronDown className="h-3 w-3" />
974
- ) : (
975
- <IconChevronRight className="h-3 w-3 rtl:-scale-x-100" />
976
- )}
977
- <span className="min-w-0 flex-1 text-start">
978
- People
979
- </span>
980
- <span className="text-[10px]">
981
- {overlayPeople.length}
982
- </span>
983
- </button>
984
- </CollapsibleTrigger>
985
- <CollapsibleContent className="space-y-0.5">
986
- {overlayPeople.map((person) => (
987
- <div
988
- key={person.email}
989
- className="group flex min-h-7 items-center gap-2 px-3 text-xs"
916
+ <ColorPickerPopover
917
+ color={person.color}
918
+ onColorChange={(color) =>
919
+ updatePersonColor.mutate({
920
+ email: person.email,
921
+ color,
922
+ })
923
+ }
990
924
  >
991
- <ColorPickerPopover
992
- color={person.color}
993
- onColorChange={(color) =>
994
- updatePersonColor.mutate({
995
- email: person.email,
996
- color,
997
- })
998
- }
925
+ <button
926
+ type="button"
927
+ className="shrink-0 cursor-pointer rounded-full p-0.5 hover:ring-2 hover:ring-border"
999
928
  >
1000
- <button
1001
- type="button"
1002
- className="shrink-0 cursor-pointer rounded-full p-0.5 hover:ring-2 hover:ring-border"
1003
- >
929
+ <span
930
+ className={cn(
931
+ "block h-2.5 w-2.5 rounded-full",
932
+ isHiddenCalendar("people", person.email) &&
933
+ "opacity-40",
934
+ )}
935
+ style={{ backgroundColor: person.color }}
936
+ />
937
+ </button>
938
+ </ColorPickerPopover>
939
+ <span
940
+ className={cn(
941
+ "min-w-0 flex-1 truncate",
942
+ isHiddenCalendar("people", person.email)
943
+ ? "text-muted-foreground/40"
944
+ : "text-muted-foreground",
945
+ )}
946
+ >
947
+ {person.name || person.email}
948
+ </span>
949
+ {overlayStatusByEmail?.get(
950
+ person.email.toLowerCase(),
951
+ )?.status === "error" && (
952
+ <Tooltip>
953
+ <TooltipTrigger asChild>
1004
954
  <span
1005
- className={cn(
1006
- "block h-2.5 w-2.5 rounded-full",
1007
- isHiddenCalendar(
1008
- "people",
1009
- person.email,
1010
- ) && "opacity-40",
955
+ tabIndex={0}
956
+ className="inline-flex shrink-0 rounded-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
957
+ aria-label={t(
958
+ "sidebar.overlayCalendarUnavailable",
959
+ { email: person.name || person.email },
1011
960
  )}
1012
- style={{ backgroundColor: person.color }}
1013
- />
1014
- </button>
1015
- </ColorPickerPopover>
1016
- <span
1017
- className={cn(
1018
- "min-w-0 flex-1 truncate",
961
+ >
962
+ <IconAlertTriangle
963
+ className="h-3 w-3 text-muted-foreground/60"
964
+ aria-hidden="true"
965
+ />
966
+ </span>
967
+ </TooltipTrigger>
968
+ <TooltipContent side="right">
969
+ {t("sidebar.overlayCalendarUnavailable", {
970
+ email: person.name || person.email,
971
+ })}
972
+ </TooltipContent>
973
+ </Tooltip>
974
+ )}
975
+ <div className="flex items-center">
976
+ <button
977
+ type="button"
978
+ onClick={() =>
979
+ toggleHiddenCalendar("people", person.email)
980
+ }
981
+ className="flex h-6 w-6 items-center justify-center rounded text-muted-foreground/40 hover:text-foreground group-hover:text-muted-foreground/80"
982
+ aria-label={
1019
983
  isHiddenCalendar("people", person.email)
1020
- ? "text-muted-foreground/40"
1021
- : "text-muted-foreground",
984
+ ? t("sidebar.showCalendar")
985
+ : t("sidebar.hideCalendar")
986
+ }
987
+ >
988
+ {isHiddenCalendar("people", person.email) ? (
989
+ <IconEyeOff className="h-3 w-3" />
990
+ ) : (
991
+ <IconEye className="h-3 w-3" />
1022
992
  )}
993
+ </button>
994
+ <button
995
+ type="button"
996
+ onClick={() =>
997
+ removePerson.mutate(person.email)
998
+ }
999
+ className="flex h-6 w-6 items-center justify-center rounded text-muted-foreground/60 opacity-0 hover:text-foreground group-hover:opacity-100"
1023
1000
  >
1024
- {person.name || person.email}
1025
- </span>
1026
- {overlayStatusByEmail?.get(
1027
- person.email.toLowerCase(),
1028
- )?.status === "error" && (
1029
- <Tooltip>
1030
- <TooltipTrigger asChild>
1031
- <span
1032
- tabIndex={0}
1033
- className="inline-flex shrink-0 rounded-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
1034
- aria-label={t(
1035
- "sidebar.overlayCalendarUnavailable",
1036
- { email: person.name || person.email },
1037
- )}
1038
- >
1039
- <IconAlertTriangle
1040
- className="h-3 w-3 text-muted-foreground/60"
1041
- aria-hidden="true"
1042
- />
1043
- </span>
1044
- </TooltipTrigger>
1045
- <TooltipContent side="right">
1046
- {t("sidebar.overlayCalendarUnavailable", {
1047
- email: person.name || person.email,
1048
- })}
1049
- </TooltipContent>
1050
- </Tooltip>
1051
- )}
1052
- <div className="flex items-center">
1053
- <button
1054
- type="button"
1055
- onClick={() =>
1056
- toggleHiddenCalendar("people", person.email)
1057
- }
1058
- className="flex h-6 w-6 items-center justify-center rounded text-muted-foreground/40 hover:text-foreground group-hover:text-muted-foreground/80"
1059
- aria-label={
1060
- isHiddenCalendar("people", person.email)
1061
- ? t("sidebar.showCalendar")
1062
- : t("sidebar.hideCalendar")
1063
- }
1064
- >
1065
- {isHiddenCalendar("people", person.email) ? (
1066
- <IconEyeOff className="h-3 w-3" />
1067
- ) : (
1068
- <IconEye className="h-3 w-3" />
1069
- )}
1070
- </button>
1071
- <button
1072
- type="button"
1073
- onClick={() =>
1074
- removePerson.mutate(person.email)
1075
- }
1076
- className="flex h-6 w-6 items-center justify-center rounded text-muted-foreground/60 opacity-0 hover:text-foreground group-hover:opacity-100"
1077
- >
1078
- <IconX className="h-3 w-3" />
1079
- </button>
1080
- </div>
1001
+ <IconX className="h-3 w-3" />
1002
+ </button>
1081
1003
  </div>
1082
- ))}
1083
- </CollapsibleContent>
1084
- </Collapsible>
1085
- )}
1004
+ </div>
1005
+ ))}
1006
+ </CollapsibleContent>
1007
+ </Collapsible>
1008
+ )}
1086
1009
 
1087
- {externalCalendars.length > 0 && (
1088
- <Collapsible
1089
- open={feedsGroupOpen}
1090
- onOpenChange={setFeedsGroupOpen}
1091
- >
1092
- <CollapsibleTrigger asChild>
1093
- <button
1094
- type="button"
1095
- className="flex h-7 w-full items-center gap-1 rounded px-3 text-xs text-muted-foreground hover:bg-accent/50 hover:text-foreground"
1010
+ {externalCalendars.length > 0 && (
1011
+ <Collapsible
1012
+ open={feedsGroupOpen}
1013
+ onOpenChange={setFeedsGroupOpen}
1014
+ >
1015
+ <CollapsibleTrigger asChild>
1016
+ <button
1017
+ type="button"
1018
+ className="flex h-7 w-full items-center gap-1 rounded px-3 text-xs text-muted-foreground hover:bg-accent/50 hover:text-foreground"
1019
+ >
1020
+ {feedsGroupOpen ? (
1021
+ <IconChevronDown className="h-3 w-3" />
1022
+ ) : (
1023
+ <IconChevronRight className="h-3 w-3 rtl:-scale-x-100" />
1024
+ )}
1025
+ <span className="min-w-0 flex-1 text-start">
1026
+ Feeds
1027
+ </span>
1028
+ <span className="text-[10px]">
1029
+ {externalCalendars.length}
1030
+ </span>
1031
+ </button>
1032
+ </CollapsibleTrigger>
1033
+ <CollapsibleContent className="space-y-0.5">
1034
+ {externalCalendars.map((cal) => (
1035
+ <div
1036
+ key={cal.id}
1037
+ className="group flex min-h-7 items-center gap-2 px-2 text-xs"
1096
1038
  >
1097
- {feedsGroupOpen ? (
1098
- <IconChevronDown className="h-3 w-3" />
1099
- ) : (
1100
- <IconChevronRight className="h-3 w-3 rtl:-scale-x-100" />
1101
- )}
1102
- <span className="min-w-0 flex-1 text-start">
1103
- Feeds
1104
- </span>
1105
- <span className="text-[10px]">
1106
- {externalCalendars.length}
1107
- </span>
1108
- </button>
1109
- </CollapsibleTrigger>
1110
- <CollapsibleContent className="space-y-0.5">
1111
- {externalCalendars.map((cal) => (
1112
- <div
1113
- key={cal.id}
1114
- className="group flex min-h-7 items-center gap-2 px-3 text-xs"
1039
+ <ColorPickerPopover
1040
+ color={cal.color}
1041
+ onColorChange={(color) =>
1042
+ updateExternalColor.mutate({
1043
+ id: cal.id,
1044
+ color,
1045
+ })
1046
+ }
1115
1047
  >
1116
- <ColorPickerPopover
1117
- color={cal.color}
1118
- onColorChange={(color) =>
1119
- updateExternalColor.mutate({
1120
- id: cal.id,
1121
- color,
1122
- })
1123
- }
1048
+ <button
1049
+ type="button"
1050
+ className="shrink-0 cursor-pointer rounded-full p-0.5 hover:ring-2 hover:ring-border"
1124
1051
  >
1125
- <button
1126
- type="button"
1127
- className="shrink-0 cursor-pointer rounded-full p-0.5 hover:ring-2 hover:ring-border"
1128
- >
1129
- <span
1130
- className={cn(
1131
- "block h-2.5 w-2.5 rounded-full",
1132
- isHiddenCalendar("external", cal.id) &&
1133
- "opacity-40",
1134
- )}
1135
- style={{ backgroundColor: cal.color }}
1136
- />
1137
- </button>
1138
- </ColorPickerPopover>
1139
- <span
1140
- className={cn(
1141
- "min-w-0 flex-1 truncate",
1052
+ <span
1053
+ className={cn(
1054
+ "block h-2.5 w-2.5 rounded-full",
1055
+ isHiddenCalendar("external", cal.id) &&
1056
+ "opacity-40",
1057
+ )}
1058
+ style={{ backgroundColor: cal.color }}
1059
+ />
1060
+ </button>
1061
+ </ColorPickerPopover>
1062
+ <span
1063
+ className={cn(
1064
+ "min-w-0 flex-1 truncate",
1065
+ isHiddenCalendar("external", cal.id)
1066
+ ? "text-muted-foreground/40"
1067
+ : "text-muted-foreground",
1068
+ )}
1069
+ >
1070
+ {cal.name}
1071
+ </span>
1072
+ <div className="flex items-center">
1073
+ <button
1074
+ type="button"
1075
+ onClick={() =>
1076
+ toggleHiddenCalendar("external", cal.id)
1077
+ }
1078
+ className="flex h-6 w-6 items-center justify-center rounded text-muted-foreground/40 hover:text-foreground group-hover:text-muted-foreground/80"
1079
+ aria-label={
1142
1080
  isHiddenCalendar("external", cal.id)
1143
- ? "text-muted-foreground/40"
1144
- : "text-muted-foreground",
1081
+ ? t("sidebar.showCalendar")
1082
+ : t("sidebar.hideCalendar")
1083
+ }
1084
+ >
1085
+ {isHiddenCalendar("external", cal.id) ? (
1086
+ <IconEyeOff className="h-3 w-3" />
1087
+ ) : (
1088
+ <IconEye className="h-3 w-3" />
1145
1089
  )}
1090
+ </button>
1091
+ <button
1092
+ type="button"
1093
+ onClick={() => removeExternal.mutate(cal.id)}
1094
+ className="flex h-6 w-6 items-center justify-center rounded text-muted-foreground/60 opacity-0 hover:text-foreground group-hover:opacity-100"
1146
1095
  >
1147
- {cal.name}
1148
- </span>
1149
- <div className="flex items-center">
1150
- <button
1151
- type="button"
1152
- onClick={() =>
1153
- toggleHiddenCalendar("external", cal.id)
1154
- }
1155
- className="flex h-6 w-6 items-center justify-center rounded text-muted-foreground/40 hover:text-foreground group-hover:text-muted-foreground/80"
1156
- aria-label={
1157
- isHiddenCalendar("external", cal.id)
1158
- ? t("sidebar.showCalendar")
1159
- : t("sidebar.hideCalendar")
1160
- }
1161
- >
1162
- {isHiddenCalendar("external", cal.id) ? (
1163
- <IconEyeOff className="h-3 w-3" />
1164
- ) : (
1165
- <IconEye className="h-3 w-3" />
1166
- )}
1167
- </button>
1168
- <button
1169
- type="button"
1170
- onClick={() => removeExternal.mutate(cal.id)}
1171
- className="flex h-6 w-6 items-center justify-center rounded text-muted-foreground/60 opacity-0 hover:text-foreground group-hover:opacity-100"
1172
- >
1173
- <IconX className="h-3 w-3" />
1174
- </button>
1175
- </div>
1096
+ <IconX className="h-3 w-3" />
1097
+ </button>
1176
1098
  </div>
1177
- ))}
1178
- </CollapsibleContent>
1179
- </Collapsible>
1180
- )}
1181
- </div>
1182
- )}
1183
- </div>
1184
- </>
1185
- )}
1186
- </div>
1187
-
1188
- <nav
1189
- className={cn(
1190
- "shrink-0",
1191
- collapsed
1192
- ? "flex flex-col items-center gap-1 px-1 py-2"
1193
- : "space-y-0.5 px-2.5 pt-2.5",
1194
- )}
1195
- >
1196
- {bottomNavItems.map((item) => {
1197
- const isActive = location.pathname.startsWith(item.path);
1198
- const link = (
1199
- <Link
1200
- to={item.path}
1201
- onClick={onClose}
1202
- aria-label={collapsed ? t(item.labelKey) : undefined}
1203
- className={cn(
1204
- "flex items-center rounded-lg font-medium transition-colors",
1205
- collapsed
1206
- ? "h-10 w-10 justify-center"
1207
- : "gap-3 px-3 py-2 text-sm",
1208
- isActive
1209
- ? "bg-primary/10 text-primary"
1210
- : "text-muted-foreground hover:bg-accent/60 hover:text-foreground",
1211
- )}
1212
- >
1213
- <item.icon className="h-4 w-4" />
1214
- {!collapsed && t(item.labelKey)}
1215
- </Link>
1216
- );
1217
- return collapsed ? (
1218
- <Tooltip key={item.path}>
1219
- <TooltipTrigger asChild>{link}</TooltipTrigger>
1220
- <TooltipContent side="right">{t(item.labelKey)}</TooltipContent>
1221
- </Tooltip>
1222
- ) : (
1223
- <div key={item.path}>{link}</div>
1224
- );
1225
- })}
1226
- </nav>
1227
-
1228
- {!collapsed ? (
1229
- <>
1230
- <div className="px-3 py-2 empty:hidden">
1231
- <OrgSwitcher />
1232
- </div>
1233
-
1234
- <div className="px-3 py-2 empty:hidden">
1235
- <DevDatabaseLink />
1099
+ </div>
1100
+ ))}
1101
+ </CollapsibleContent>
1102
+ </Collapsible>
1103
+ )}
1104
+ </div>
1105
+ )}
1236
1106
  </div>
1237
1107
  </>
1238
- ) : null}
1239
- <SidebarFooterActions
1240
- collapsed={collapsed}
1241
- feedback={feedbackButton}
1242
- search={searchButton}
1243
- collapse={collapseButton}
1244
- />
1245
- </aside>
1108
+ )}
1109
+ </AppSidebar>
1246
1110
  </>
1247
1111
  );
1248
1112
  }