@xenide-io/the-old-ui-theme 0.4.4 → 0.4.8

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 (76) hide show
  1. package/dist/{chunk-54ZR4VL5.mjs → chunk-RZXHZWUB.mjs} +44 -17
  2. package/dist/{index-B5NOyoKS.d.mts → index-Od4pIP4F.d.mts} +7 -1
  3. package/dist/{index-B5NOyoKS.d.ts → index-Od4pIP4F.d.ts} +7 -1
  4. package/dist/index.d.mts +2 -2
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.js +37 -10
  7. package/dist/index.mjs +1 -1
  8. package/dist/suite.d.mts +152 -41
  9. package/dist/suite.d.ts +152 -41
  10. package/dist/suite.js +1366 -671
  11. package/dist/suite.mjs +1304 -610
  12. package/dist/ui.d.mts +1 -1
  13. package/dist/ui.d.ts +1 -1
  14. package/dist/ui.js +37 -10
  15. package/dist/ui.mjs +1 -1
  16. package/package.json +7 -3
  17. package/src/components/ui/Modal.tsx +70 -26
  18. package/src/styles/suite-skin.css +284 -24
  19. package/src/suite/components/ai-panel.test.ts +20 -0
  20. package/src/suite/components/ai-panel.tsx +494 -103
  21. package/src/suite/components/suite-app-layout.tsx +48 -28
  22. package/src/suite/components/suite-layout.tsx +84 -40
  23. package/src/suite/components/suite-mobile-drawer.tsx +35 -22
  24. package/src/suite/components/suite-notification-bell.tsx +9 -3
  25. package/src/suite/components/suite-settings-layout.tsx +82 -0
  26. package/src/suite/components/suite-settings-mobile-nav.tsx +18 -17
  27. package/src/suite/components/suite-skeleton.tsx +3 -3
  28. package/src/suite/components/today-calibrating.tsx +11 -35
  29. package/src/suite/components/today-page-frame.tsx +19 -11
  30. package/src/suite/components/today-ui.tsx +276 -19
  31. package/src/suite/index.ts +41 -25
  32. package/src/suite/lib/apps.ts +32 -2
  33. package/src/suite/lib/use-sidebar-width.ts +114 -0
  34. package/src/components/sections/AccordionShowcase.tsx +0 -45
  35. package/src/components/sections/AlertShowcase.tsx +0 -40
  36. package/src/components/sections/AvatarShowcase.tsx +0 -80
  37. package/src/components/sections/BadgeShowcase.tsx +0 -65
  38. package/src/components/sections/ButtonShowcase.tsx +0 -308
  39. package/src/components/sections/CalendarShowcase.tsx +0 -35
  40. package/src/components/sections/CardShowcase.tsx +0 -159
  41. package/src/components/sections/CodeMockupShowcase.tsx +0 -56
  42. package/src/components/sections/ColorPalette.tsx +0 -117
  43. package/src/components/sections/CommandPaletteShowcase.tsx +0 -48
  44. package/src/components/sections/CountdownShowcase.tsx +0 -43
  45. package/src/components/sections/DiffShowcase.tsx +0 -70
  46. package/src/components/sections/DrawerShowcase.tsx +0 -97
  47. package/src/components/sections/DropdownShowcase.tsx +0 -98
  48. package/src/components/sections/EmptyStateShowcase.tsx +0 -50
  49. package/src/components/sections/FilterChipsShowcase.tsx +0 -98
  50. package/src/components/sections/FormShowcase.tsx +0 -127
  51. package/src/components/sections/HoverCardShowcase.tsx +0 -50
  52. package/src/components/sections/IconShowcase.tsx +0 -121
  53. package/src/components/sections/IndicatorShowcase.tsx +0 -52
  54. package/src/components/sections/KbdShowcase.tsx +0 -57
  55. package/src/components/sections/ModalShowcase.tsx +0 -211
  56. package/src/components/sections/NavigationShowcase.tsx +0 -199
  57. package/src/components/sections/NewComponentsShowcase.tsx +0 -1052
  58. package/src/components/sections/ProductLayoutsShowcase.tsx +0 -78
  59. package/src/components/sections/ProgressShowcase.tsx +0 -82
  60. package/src/components/sections/QuillChartShowcase.tsx +0 -92
  61. package/src/components/sections/QuillDashboardShowcase.tsx +0 -149
  62. package/src/components/sections/SegmentedControlShowcase.tsx +0 -49
  63. package/src/components/sections/SetupThemeShowcase.tsx +0 -262
  64. package/src/components/sections/SidebarShowcase.tsx +0 -152
  65. package/src/components/sections/StackShowcase.tsx +0 -33
  66. package/src/components/sections/StepperShowcase.tsx +0 -37
  67. package/src/components/sections/SuiteShowcase.tsx +0 -669
  68. package/src/components/sections/SwapShowcase.tsx +0 -99
  69. package/src/components/sections/TabShowcase.tsx +0 -84
  70. package/src/components/sections/TableShowcase.tsx +0 -142
  71. package/src/components/sections/TimelineShowcase.tsx +0 -83
  72. package/src/components/sections/ToastShowcase.tsx +0 -108
  73. package/src/components/sections/TooltipShowcase.tsx +0 -38
  74. package/src/components/sections/UtilityShowcase.tsx +0 -81
  75. package/src/styles/excel-themes.css +0 -110
  76. package/src/styles/lemon-primitives.css +0 -550
package/dist/suite.js CHANGED
@@ -68,10 +68,16 @@ __export(suite_exports, {
68
68
  AppSwitcherMark: () => AppSwitcherMark,
69
69
  CommandPaletteHost: () => CommandPaletteHost,
70
70
  DeferredChrome: () => DeferredChrome,
71
+ SIDEBAR_COLLAPSE_THRESHOLD: () => SIDEBAR_COLLAPSE_THRESHOLD,
72
+ SIDEBAR_DEFAULT_WIDTH: () => SIDEBAR_DEFAULT_WIDTH,
73
+ SIDEBAR_MAX_WIDTH: () => SIDEBAR_MAX_WIDTH,
74
+ SIDEBAR_MIN_EXPANDED_WIDTH: () => SIDEBAR_MIN_EXPANDED_WIDTH,
75
+ SIDEBAR_RAIL_WIDTH: () => SIDEBAR_RAIL_WIDTH,
71
76
  SUITE_APPS: () => SUITE_APPS2,
72
77
  SUITE_APP_MAP: () => SUITE_APP_MAP,
73
78
  SUITE_GLYPHS: () => SUITE_GLYPHS,
74
79
  SUITE_ICON_NAMES: () => SUITE_ICON_NAMES,
80
+ SUITE_OPEN_ASK_AI_EVENT: () => SUITE_OPEN_ASK_AI_EVENT,
75
81
  SUITE_SPRINGS: () => SUITE_SPRINGS,
76
82
  SourceAppGlyph: () => SourceAppGlyph,
77
83
  SuiteAiPanel: () => SuiteAiPanel,
@@ -89,6 +95,7 @@ __export(suite_exports, {
89
95
  SuitePage: () => SuitePage,
90
96
  SuitePageHeader: () => SuitePageHeader,
91
97
  SuiteSectionHeader: () => SuiteSectionHeader,
98
+ SuiteSettingsLayout: () => SuiteSettingsLayout,
92
99
  SuiteSettingsMobileNav: () => SuiteSettingsMobileNav,
93
100
  SuiteSidebar: () => SuiteSidebar,
94
101
  SuiteSkeleton: () => SuiteSkeleton,
@@ -98,11 +105,15 @@ __export(suite_exports, {
98
105
  SuiteThemeProvider: () => SuiteThemeProvider,
99
106
  SuiteToolbar: () => SuiteToolbar,
100
107
  SuiteUserMenu: () => SuiteUserMenu,
108
+ TodayAskAiCard: () => TodayAskAiCard,
101
109
  TodayCalibrating: () => TodayCalibrating,
110
+ TodayEmptyAction: () => TodayEmptyAction,
102
111
  TodayEmptyState: () => TodayEmptyState,
103
112
  TodayHero: () => TodayHero,
113
+ TodayLayout: () => TodayLayout,
104
114
  TodayPageFrame: () => TodayPageFrame,
105
115
  TodayPanel: () => TodayPanel,
116
+ TodayPrimaryAction: () => TodayPrimaryAction,
106
117
  TodaySection: () => TodaySection,
107
118
  TodayTimeIcon: () => TodayTimeIcon,
108
119
  TodayTopBar: () => TodayTopBar,
@@ -111,11 +122,14 @@ __export(suite_exports, {
111
122
  appSwitcherTriggerClass: () => appSwitcherTriggerClass,
112
123
  cn: () => cn,
113
124
  getTodayGreeting: () => getTodayGreeting,
114
- m: () => import_react8.m,
125
+ m: () => import_react10.m,
126
+ openSuiteAskAi: () => openSuiteAskAi,
127
+ resolveSuiteNotificationHref: () => resolveSuiteNotificationHref,
115
128
  sourceToSuiteApp: () => sourceToSuiteApp,
116
129
  suiteAppBaseUrl: () => suiteAppBaseUrl,
117
130
  suiteAppLabel: () => suiteAppLabel,
118
131
  useIdleMount: () => useIdleMount,
132
+ useSidebarWidth: () => useSidebarWidth,
119
133
  useSuiteTheme: () => useSuiteTheme
120
134
  });
121
135
  module.exports = __toCommonJS(suite_exports);
@@ -127,24 +141,32 @@ function TodayPageFrame({ children }) {
127
141
  "div",
128
142
  {
129
143
  "data-test": "today-page",
130
- className: "today-page-frame relative flex min-h-0 flex-1 flex-col overflow-y-auto overflow-x-hidden",
144
+ className: "today-page-frame relative flex min-h-0 w-full flex-1 flex-col overflow-x-hidden",
131
145
  children: [
132
146
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
133
147
  "div",
134
148
  {
135
- className: "pointer-events-none absolute inset-x-0 top-0 h-[34rem] overflow-hidden",
149
+ className: "pointer-events-none absolute inset-0 overflow-hidden",
136
150
  "aria-hidden": true,
137
151
  children: [
138
152
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__glow absolute inset-0" }),
139
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__sun absolute -right-16 -top-16 h-72 w-72 rounded-full bg-gradient-to-br from-amber-300/30 via-rose-300/20 to-rose-400/10 blur-3xl opacity-60 motion-safe:animate-[pulse_8s_ease-in-out_infinite] motion-reduce:opacity-40" }),
140
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__top-fade absolute inset-x-0 top-0 h-64" }),
141
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__cloud today-page-frame__cloud--1 absolute -left-24 top-28 h-80 w-[26rem] rounded-[45%]" }),
142
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__cloud today-page-frame__cloud--2 absolute -right-20 top-40 h-72 w-80 rounded-[40%]" }),
143
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__cloud today-page-frame__cloud--3 absolute left-[28%] top-[58%] h-64 w-[22rem] rounded-[50%]" })
153
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__top-fade absolute inset-x-0 top-0 h-[28rem]" }),
154
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__cloud today-page-frame__cloud--1 absolute -left-28 top-16 h-96 w-[30rem] rounded-[45%]" }),
155
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__cloud today-page-frame__cloud--2 absolute -right-24 top-24 h-80 w-[22rem] rounded-[40%]" }),
156
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__cloud today-page-frame__cloud--3 absolute left-[22%] top-48 h-72 w-[24rem] rounded-[50%]" }),
157
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__cloud today-page-frame__cloud--4 absolute right-[18%] top-[42%] h-64 w-[20rem] rounded-[48%]" }),
158
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__cloud today-page-frame__cloud--5 absolute left-[8%] top-[68%] h-56 w-[18rem] rounded-[42%]" })
144
159
  ]
145
160
  }
146
161
  ),
147
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "relative flex-1 px-4 pb-5 pt-2 sm:px-6 sm:pb-6 sm:pt-3 lg:px-8 [&_.list-row]:min-h-[52px] [&_.list-row]:sm:min-h-[44px]", children })
162
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
163
+ "div",
164
+ {
165
+ "data-width": "full",
166
+ className: "suite-page-stage suite-page-inset relative z-[1] flex w-full min-w-0 flex-1 flex-col [&_.list-row]:min-h-[52px] [&_.list-row]:sm:min-h-[44px]",
167
+ children
168
+ }
169
+ )
148
170
  ]
149
171
  }
150
172
  );
@@ -597,15 +619,15 @@ function AnimatedSun({ className }) {
597
619
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("stop", { stopColor: "#f59e0b" }),
598
620
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("stop", { offset: "1", stopColor: "#fb923c" })
599
621
  ] }) }),
600
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("g", { className: "origin-center motion-safe:animate-[spin_20s_linear_infinite] motion-reduce:animate-none", children: [0, 45, 90, 135, 180, 225, 270, 315].map((angle) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
622
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("g", { className: "origin-center opacity-80 motion-safe:animate-[pulse_8s_ease-in-out_infinite] motion-reduce:animate-none", children: [0, 45, 90, 135, 180, 225, 270, 315].map((angle) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
601
623
  "line",
602
624
  {
603
625
  x1: "20",
604
- y1: "4",
626
+ y1: "5",
605
627
  x2: "20",
606
628
  y2: "9",
607
629
  stroke: "#fbbf24",
608
- strokeWidth: "2",
630
+ strokeWidth: "1.75",
609
631
  strokeLinecap: "round",
610
632
  transform: `rotate(${angle} 20 20)`
611
633
  },
@@ -639,10 +661,9 @@ function AnimatedMoon({ className }) {
639
661
  fill: "url(#animatedMoonBody)"
640
662
  }
641
663
  ),
642
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { className: "motion-safe:animate-[pulse_4s_ease-in-out_infinite] motion-reduce:animate-none", children: [
643
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "29", cy: "10", r: "1.4", fill: "#c7d2fe" }),
644
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "34", cy: "16", r: "1", fill: "#c7d2fe" }),
645
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "25", cy: "5.5", r: "0.9", fill: "#c7d2fe" })
664
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { className: "opacity-70 motion-safe:animate-[pulse_10s_ease-in-out_infinite] motion-reduce:animate-none", children: [
665
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "29", cy: "10", r: "1.2", fill: "#c7d2fe" }),
666
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "34", cy: "16", r: "0.9", fill: "#c7d2fe" })
646
667
  ] })
647
668
  ]
648
669
  }
@@ -704,30 +725,193 @@ function getTodayGreeting(user, stats) {
704
725
  const overdueCount = (_d = stats == null ? void 0 : stats.overdueCount) != null ? _d : 0;
705
726
  const hours = (_e = stats == null ? void 0 : stats.hours) != null ? _e : 0;
706
727
  if (overdueCount > 0) {
707
- return `${greeting}. You have ${taskCount} task${taskCount === 1 ? "" : "s"} due today, ${overdueCount} overdue.`;
728
+ return `${greeting}. ${taskCount} task${taskCount === 1 ? "" : "s"} due today, ${overdueCount} overdue.`;
708
729
  }
709
730
  if (taskCount > 0) {
710
- return `${greeting}. You have ${taskCount} task${taskCount === 1 ? "" : "s"} due today.`;
731
+ return `${greeting}. ${taskCount} task${taskCount === 1 ? "" : "s"} due today.`;
711
732
  }
712
733
  if (hours > 0) {
713
- return `${greeting}. You logged ${hours} hour${hours === 1 ? "" : "s"} today.`;
734
+ return `${greeting}. ${hours} hour${hours === 1 ? "" : "s"} logged today.`;
714
735
  }
715
736
  if (period === "evening" || period === "night") {
716
- return `${greeting}. Time to wrap up.`;
737
+ return `${greeting}. Nice work \u2014 time to wrap up.`;
717
738
  }
718
- return `${greeting}. Nothing overdue \u2014 take a nice, deep breath.`;
739
+ return `${greeting}. Clear queue \u2014 take a nice, deep breath.`;
719
740
  }
720
741
  function TodayHero({
721
- message = "Take a nice, deep breath."
742
+ message = "Take a nice, deep breath.",
743
+ brief,
744
+ briefMeta
745
+ }) {
746
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mt-4 sm:mt-6", children: [
747
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
748
+ "p",
749
+ {
750
+ className: "today-hero max-w-2xl font-display text-lg font-semibold tracking-tight text-ph-ink sm:text-xl sm:leading-snug",
751
+ children: message
752
+ },
753
+ typeof message === "string" ? message : "hero"
754
+ ),
755
+ brief ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mt-4 max-w-2xl", "data-test": "today-focus-brief", children: [
756
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-[15px] leading-[1.65] text-ph-ink", children: brief }),
757
+ briefMeta ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "mt-1.5 text-xs text-ph-mutedtext", children: briefMeta }) : null
758
+ ] }) : null
759
+ ] });
760
+ }
761
+ function TodayPrimaryAction({
762
+ label,
763
+ description,
764
+ icon,
765
+ accent = "neutral",
766
+ onClick,
767
+ href,
768
+ loading = false,
769
+ active = false,
770
+ className,
771
+ dataTest = "today-primary-action"
722
772
  }) {
723
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mt-8 text-center sm:mt-10", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
724
- "p",
773
+ const classes = cn(
774
+ "today-primary-action group relative flex w-full max-w-md items-center gap-3 overflow-hidden rounded-xl px-3.5 py-2.5 text-left transition sm:px-4 sm:py-3",
775
+ `today-primary-action--${accent}`,
776
+ active && "today-primary-action--active",
777
+ loading && "pointer-events-none opacity-70",
778
+ className
779
+ );
780
+ const body = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
781
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "today-primary-action__icon inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-lg sm:h-10 sm:w-10", children: icon }),
782
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "min-w-0 flex-1", children: [
783
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "block text-sm font-semibold tracking-tight sm:text-base", children: label }),
784
+ description ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "mt-0.5 block text-xs opacity-80 sm:text-sm", children: description }) : null
785
+ ] }),
786
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
787
+ "span",
788
+ {
789
+ className: "today-primary-action__chevron shrink-0 text-base opacity-60 transition group-hover:translate-x-0.5 group-hover:opacity-100",
790
+ "aria-hidden": true,
791
+ children: "\u2192"
792
+ }
793
+ )
794
+ ] });
795
+ if (href && !onClick) {
796
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
797
+ "a",
798
+ {
799
+ href,
800
+ className: classes,
801
+ "data-test": dataTest,
802
+ "aria-busy": loading,
803
+ children: body
804
+ }
805
+ );
806
+ }
807
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
808
+ "button",
725
809
  {
726
- className: "today-hero font-display text-2xl font-bold tracking-tight text-ph-ink sm:text-[1.75rem] sm:leading-tight",
727
- children: message
728
- },
729
- typeof message === "string" ? message : "hero"
730
- ) });
810
+ type: "button",
811
+ onClick,
812
+ disabled: loading,
813
+ className: classes,
814
+ "data-test": dataTest,
815
+ "aria-busy": loading,
816
+ children: body
817
+ }
818
+ );
819
+ }
820
+ function TodayEmptyAction({
821
+ title,
822
+ description,
823
+ actionLabel,
824
+ href,
825
+ onClick,
826
+ dataTest
827
+ }) {
828
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
829
+ "div",
830
+ {
831
+ className: "rounded-lg border border-dashed border-ph-border/80 bg-ph-muted/30 px-4 py-4 text-center",
832
+ "data-test": dataTest,
833
+ children: [
834
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-sm font-semibold text-ph-ink", children: title }),
835
+ description ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "mx-auto mt-1 max-w-sm text-xs leading-relaxed text-ph-subtle sm:text-sm", children: description }) : null,
836
+ actionLabel && (href || onClick) ? href ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
837
+ "a",
838
+ {
839
+ href,
840
+ className: "mt-2.5 inline-flex text-sm font-semibold text-ph-brand hover:underline",
841
+ children: actionLabel
842
+ }
843
+ ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
844
+ "button",
845
+ {
846
+ type: "button",
847
+ onClick,
848
+ className: "mt-2.5 inline-flex text-sm font-semibold text-ph-brand hover:underline",
849
+ children: actionLabel
850
+ }
851
+ ) : null
852
+ ]
853
+ }
854
+ );
855
+ }
856
+ function TodayLayout({
857
+ primary,
858
+ main,
859
+ aside,
860
+ className
861
+ }) {
862
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
863
+ "div",
864
+ {
865
+ className: cn("mt-5 space-y-6 sm:mt-6", className),
866
+ "data-test": "today-layout",
867
+ children: [
868
+ primary ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "max-w-md", children: primary }) : null,
869
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "grid gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(16rem,20rem)] lg:items-start lg:gap-8", children: [
870
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "min-w-0 space-y-8", children: main }),
871
+ aside ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("aside", { className: "space-y-3 lg:sticky lg:top-6 lg:self-start", children: aside }) : null
872
+ ] })
873
+ ]
874
+ }
875
+ );
876
+ }
877
+ function TodayAskAiCard({
878
+ title = "Ask AI",
879
+ description = "Ask about your workspace, draft something, or look a fact up online.",
880
+ cta = "Ask",
881
+ className,
882
+ onClick,
883
+ href
884
+ }) {
885
+ const classes = cn(
886
+ "group flex w-full items-start gap-3.5 rounded-2xl border border-ph-border/70 bg-ph-surface px-4 py-3.5 text-left shadow-[0_1px_12px_-8px_rgba(15,23,42,0.1)] transition hover:border-ph-brand/35 hover:bg-[color-mix(in_oklab,var(--ph-accent)_6%,var(--ph-surface))] sm:px-5",
887
+ className
888
+ );
889
+ const body = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
890
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-2xl bg-ph-brand/15 text-ph-brand shadow-sm ring-1 ring-black/[0.06]", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SuiteIcon, { name: "stack-hex", accent: "shellstack", className: "h-5 w-5" }) }),
891
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "min-w-0 flex-1", children: [
892
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "flex flex-wrap items-baseline justify-between gap-x-3 gap-y-1", children: [
893
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-display text-sm font-bold tracking-tight text-ph-ink", children: title }),
894
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-sm font-medium text-ph-brand transition group-hover:underline", children: cta })
895
+ ] }),
896
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "mt-1 block text-sm leading-relaxed text-ph-subtle", children: description })
897
+ ] })
898
+ ] });
899
+ if (onClick) {
900
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
901
+ "button",
902
+ {
903
+ type: "button",
904
+ onClick,
905
+ className: classes,
906
+ "data-test": "today-ask-ai",
907
+ children: body
908
+ }
909
+ );
910
+ }
911
+ if (href) {
912
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("a", { href, className: classes, "data-test": "today-ask-ai", children: body });
913
+ }
914
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: classes, "data-test": "today-ask-ai", children: body });
731
915
  }
732
916
  function TodaySection({
733
917
  icon: Icon,
@@ -738,7 +922,7 @@ function TodaySection({
738
922
  testId,
739
923
  children
740
924
  }) {
741
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { "data-test": testId, className: "space-y-4", children: [
925
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { "data-test": testId, className: "space-y-5", children: [
742
926
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-start justify-between gap-4", children: [
743
927
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex min-w-0 items-start gap-3.5", children: [
744
928
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -769,8 +953,7 @@ function TodayPanel({
769
953
  "div",
770
954
  {
771
955
  className: cn(
772
- "border-ph-border/70 rounded-2xl border bg-ph-surface p-6 shadow-[0_2px_28px_-10px_rgba(15,23,42,0.14)] transition-all duration-200 ease-out sm:p-6",
773
- "hover:shadow-[0_8px_32px_-12px_rgba(15,23,42,0.18)] hover:-translate-y-0.5 motion-reduce:transition-none motion-reduce:hover:transform-none",
956
+ "border-ph-border/70 rounded-2xl border bg-ph-surface p-5 shadow-[0_1px_16px_-8px_rgba(15,23,42,0.12)] sm:p-5",
774
957
  className
775
958
  ),
776
959
  children
@@ -948,6 +1131,70 @@ function AppSwitcher({
948
1131
  var import_react2 = require("react");
949
1132
  var import_navigation = require("next/navigation");
950
1133
  var import_iconoir_react3 = require("iconoir-react");
1134
+
1135
+ // src/suite/lib/apps.ts
1136
+ var SUITE_APPS2 = [
1137
+ {
1138
+ slug: "shellstack",
1139
+ name: "ShellStack",
1140
+ description: "Organisation and billing",
1141
+ icon: "/icon-shellstack.svg",
1142
+ landing: "/home",
1143
+ baseUrlEnv: "NEXT_PUBLIC_SHELLSTACK_URL",
1144
+ baseUrlFallback: "http://localhost:3002"
1145
+ },
1146
+ {
1147
+ slug: "tides",
1148
+ name: "Tides",
1149
+ description: "Projects and tasks",
1150
+ icon: "/tides-icon.svg",
1151
+ landing: "/today",
1152
+ baseUrlEnv: "NEXT_PUBLIC_TIDES_URL",
1153
+ baseUrlFallback: "http://localhost:3001"
1154
+ },
1155
+ {
1156
+ slug: "turtletime",
1157
+ name: "TurtleTime",
1158
+ description: "Time tracking",
1159
+ icon: "/turtletime-icon.svg",
1160
+ landing: "/today",
1161
+ baseUrlEnv: "NEXT_PUBLIC_TURTLETIME_URL",
1162
+ baseUrlFallback: "http://localhost:3000"
1163
+ },
1164
+ {
1165
+ slug: "kraken",
1166
+ name: "Kraken",
1167
+ description: "Documents and knowledge",
1168
+ icon: "/kraken-icon.svg",
1169
+ landing: "/today",
1170
+ baseUrlEnv: "NEXT_PUBLIC_KRAKEN_URL",
1171
+ baseUrlFallback: "http://localhost:3003"
1172
+ }
1173
+ ];
1174
+ var SUITE_APP_MAP = Object.fromEntries(SUITE_APPS2.map((app) => [app.slug, app]));
1175
+ function suiteAppBaseUrl(slug) {
1176
+ const def = SUITE_APP_MAP[slug];
1177
+ const fromEnv = typeof process !== "undefined" ? process.env[def.baseUrlEnv] : void 0;
1178
+ return (fromEnv || def.baseUrlFallback).replace(/\/$/, "");
1179
+ }
1180
+ var SOURCE_APP_SLUG = {
1181
+ turtletime: "turtletime",
1182
+ tides: "tides",
1183
+ kraken: "kraken",
1184
+ portal: "shellstack",
1185
+ shellstack: "shellstack"
1186
+ };
1187
+ function resolveSuiteNotificationHref(href, sourceApp) {
1188
+ const value = (href || "").trim();
1189
+ if (!value) return "";
1190
+ if (/^(https?:|mailto:|\/\/)/i.test(value)) return value;
1191
+ const path = value.startsWith("/") ? value : `/${value}`;
1192
+ const slug = SOURCE_APP_SLUG[(sourceApp || "").toLowerCase()];
1193
+ if (!slug) return path;
1194
+ return `${suiteAppBaseUrl(slug)}${path}`;
1195
+ }
1196
+
1197
+ // src/suite/components/suite-notification-bell.tsx
951
1198
  var import_jsx_runtime7 = require("react/jsx-runtime");
952
1199
  var POLL_MS = 6e4;
953
1200
  function timeAgo(iso) {
@@ -1038,9 +1285,13 @@ function SuiteNotificationBell({
1038
1285
  } catch (e) {
1039
1286
  }
1040
1287
  }
1041
- if (!n.href) return;
1042
- if (/^https?:\/\//i.test(n.href)) window.location.assign(n.href);
1043
- else router.push(n.href);
1288
+ const target = resolveSuiteNotificationHref(n.href, n.source_app);
1289
+ if (!target) return;
1290
+ if (/^https?:\/\//i.test(target)) {
1291
+ window.location.assign(target);
1292
+ return;
1293
+ }
1294
+ router.push(target);
1044
1295
  },
1045
1296
  [router, markRead]
1046
1297
  );
@@ -1193,6 +1444,7 @@ function SuiteMobileDrawer({
1193
1444
  children,
1194
1445
  side = "left",
1195
1446
  showCloseButton = false,
1447
+ title,
1196
1448
  closeLabel = "Close navigation",
1197
1449
  backdropLabel = "Close navigation",
1198
1450
  dialogLabel = "Navigation",
@@ -1270,23 +1522,35 @@ function SuiteMobileDrawer({
1270
1522
  tabIndex: -1,
1271
1523
  "data-test": panelDataTest,
1272
1524
  className: cn(
1273
- "suite-scroll-lock relative flex h-full w-[86%] max-w-64 flex-col overflow-hidden bg-ph-surface shadow-xl outline-none motion-safe:transition-transform motion-safe:ease-spring-fast",
1525
+ "suite-scroll-lock relative flex h-full w-[86%] max-w-72 flex-col overflow-hidden bg-ph-surface shadow-xl outline-none motion-safe:transition-transform motion-safe:ease-spring-fast",
1274
1526
  side === "right" && "ml-auto",
1275
1527
  shown ? "translate-x-0" : side === "right" ? "translate-x-full" : "-translate-x-full",
1276
1528
  panelClassName
1277
1529
  ),
1278
1530
  style: { transitionDuration: `${durationMs}ms` },
1279
1531
  children: [
1280
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex h-14 shrink-0 items-center justify-end border-b border-ph-border px-3", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1281
- "button",
1532
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1533
+ "div",
1282
1534
  {
1283
- type: "button",
1284
- onClick: onClose,
1285
- className: "-mr-1.5 inline-flex h-11 w-11 items-center justify-center rounded-lg text-ph-mutedtext transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
1286
- "aria-label": closeLabel,
1287
- children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_iconoir_react4.Xmark, { className: "h-4 w-4", "aria-hidden": true })
1535
+ className: cn(
1536
+ "flex h-14 shrink-0 items-center border-b border-ph-border px-3",
1537
+ title ? "justify-between" : "justify-end"
1538
+ ),
1539
+ children: [
1540
+ title ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "truncate text-sm font-semibold text-ph-ink", children: title }) : null,
1541
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1542
+ "button",
1543
+ {
1544
+ type: "button",
1545
+ onClick: onClose,
1546
+ className: "-mr-1.5 inline-flex h-11 w-11 items-center justify-center rounded-lg text-ph-mutedtext transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
1547
+ "aria-label": closeLabel,
1548
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_iconoir_react4.Xmark, { className: "h-4 w-4", "aria-hidden": true })
1549
+ }
1550
+ )
1551
+ ]
1288
1552
  }
1289
- ) }) : null,
1553
+ ) : null,
1290
1554
  children
1291
1555
  ]
1292
1556
  }
@@ -1343,95 +1607,378 @@ function SuiteMobileHeader({
1343
1607
  );
1344
1608
  }
1345
1609
 
1346
- // src/suite/components/suite-user-menu.tsx
1347
- var import_image2 = __toESM(require("next/image"));
1348
- var import_navigation2 = require("next/navigation");
1349
- var import_iconoir_react6 = require("iconoir-react");
1350
-
1351
- // src/components/ui/DropdownMenu.tsx
1352
- var DropdownPrimitive = __toESM(require("@radix-ui/react-dropdown-menu"));
1353
-
1354
- // src/lib/cn.ts
1355
- function cn2(...parts) {
1356
- return parts.filter(Boolean).join(" ");
1357
- }
1358
-
1359
- // src/components/ui/DropdownMenu.tsx
1610
+ // src/suite/components/suite-layout.tsx
1611
+ var import_react6 = require("react");
1360
1612
  var import_jsx_runtime12 = require("react/jsx-runtime");
1361
- function DropdownContent({
1613
+ function SuiteBreadcrumbs({ items, className }) {
1614
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("nav", { "aria-label": "Breadcrumb", className, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ol", { className: "flex min-w-0 items-center gap-1.5", children: items.map((item, index) => {
1615
+ const isLast = index === items.length - 1;
1616
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("li", { className: "flex min-w-0 items-center gap-1.5", children: [
1617
+ index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "select-none text-ph-border", "aria-hidden": "true", children: "/" }) : null,
1618
+ item.href && !isLast ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1619
+ "a",
1620
+ {
1621
+ href: item.href,
1622
+ className: "truncate transition hover:text-ph-ink",
1623
+ children: item.label
1624
+ }
1625
+ ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1626
+ "span",
1627
+ {
1628
+ className: cn(
1629
+ "truncate",
1630
+ isLast ? "font-medium text-ph-ink" : "text-ph-subtle"
1631
+ ),
1632
+ "aria-current": isLast ? "page" : void 0,
1633
+ children: item.label
1634
+ }
1635
+ )
1636
+ ] }, index);
1637
+ }) }) });
1638
+ }
1639
+ function SuitePage({
1362
1640
  children,
1363
- align = "start",
1364
- side = "bottom",
1365
- sideOffset = 7,
1366
- alignOffset = 0,
1367
- collisionPadding = 8,
1368
- avoidCollisions = true,
1369
- panelClassName
1641
+ width = "full",
1642
+ inset = true,
1643
+ className,
1644
+ as: Component = "div",
1645
+ dataTest,
1646
+ id,
1647
+ role,
1648
+ "aria-live": ariaLive
1370
1649
  }) {
1371
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DropdownPrimitive.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1372
- DropdownPrimitive.Content,
1650
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1651
+ Component,
1373
1652
  {
1374
- className: cn2("ph-dropdown-panel", panelClassName),
1375
- align,
1376
- side,
1377
- sideOffset,
1378
- alignOffset,
1379
- collisionPadding,
1380
- avoidCollisions,
1381
- sticky: "partial",
1382
- children: [
1383
- children,
1384
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DropdownPrimitive.Arrow, { className: "ph-dropdown-arrow", width: 12, height: 6 })
1385
- ]
1653
+ id,
1654
+ "data-test": dataTest,
1655
+ "data-width": width,
1656
+ role,
1657
+ "aria-live": ariaLive,
1658
+ className: cn(
1659
+ "suite-page-stage flex w-full min-w-0 flex-col",
1660
+ inset && "suite-page-inset",
1661
+ className
1662
+ ),
1663
+ children
1386
1664
  }
1387
- ) });
1665
+ );
1388
1666
  }
1389
- function DropdownMenu(_a) {
1390
- var _b = _a, {
1391
- trigger,
1392
- children,
1393
- align,
1394
- side,
1395
- sideOffset,
1396
- alignOffset,
1397
- collisionPadding,
1398
- avoidCollisions,
1399
- className,
1400
- panelClassName,
1401
- "aria-label": ariaLabel,
1402
- open,
1403
- defaultOpen,
1404
- onOpenChange,
1405
- modal = false,
1406
- disabled
1407
- } = _b, props = __objRest(_b, [
1408
- "trigger",
1409
- "children",
1410
- "align",
1411
- "side",
1412
- "sideOffset",
1413
- "alignOffset",
1414
- "collisionPadding",
1415
- "avoidCollisions",
1416
- "className",
1417
- "panelClassName",
1418
- "aria-label",
1419
- "open",
1420
- "defaultOpen",
1421
- "onOpenChange",
1422
- "modal",
1423
- "disabled"
1424
- ]);
1425
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", __spreadProps(__spreadValues({ className: cn2("ph-dropdown", className) }, props), { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1426
- DropdownPrimitive.Root,
1427
- {
1667
+ function SuitePageHeader({
1668
+ title,
1669
+ description,
1670
+ eyebrow,
1671
+ actions,
1672
+ className,
1673
+ titleClassName,
1674
+ dataTest,
1675
+ /** Optional icon rendered before the title. */
1676
+ icon,
1677
+ /** Use the animated sun/moon icon from the Today page. */
1678
+ todayIcon,
1679
+ todayIconClassName,
1680
+ /** Small badge/pill next to the title (e.g. "Beta", org name). */
1681
+ badge,
1682
+ /** Breadcrumb line rendered above the title. */
1683
+ breadcrumb,
1684
+ /** Stick to the top of the scrollport (desktop page titles). */
1685
+ sticky = false
1686
+ }) {
1687
+ const headerRef = (0, import_react6.useRef)(null);
1688
+ const [stuck, setStuck] = (0, import_react6.useState)(false);
1689
+ (0, import_react6.useEffect)(() => {
1690
+ var _a;
1691
+ if (!sticky) return;
1692
+ const el = headerRef.current;
1693
+ if (!el) return;
1694
+ const sentinel = document.createElement("div");
1695
+ sentinel.setAttribute("aria-hidden", "true");
1696
+ sentinel.style.cssText = "position:relative;height:1px;width:100%;margin:0;padding:0;pointer-events:none";
1697
+ (_a = el.parentElement) == null ? void 0 : _a.insertBefore(sentinel, el);
1698
+ const observer = new IntersectionObserver(
1699
+ ([entry]) => {
1700
+ setStuck(!entry.isIntersecting);
1701
+ },
1702
+ { threshold: [1], root: null }
1703
+ );
1704
+ observer.observe(sentinel);
1705
+ return () => {
1706
+ observer.disconnect();
1707
+ sentinel.remove();
1708
+ };
1709
+ }, [sticky]);
1710
+ const resolvedIcon = todayIcon ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1711
+ TodayTimeIcon,
1712
+ {
1713
+ className: cn(
1714
+ "h-7 w-7 text-amber-500 sm:h-8 sm:w-8",
1715
+ todayIconClassName
1716
+ )
1717
+ }
1718
+ ) : icon;
1719
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1720
+ "header",
1721
+ {
1722
+ ref: headerRef,
1723
+ "data-test": dataTest,
1724
+ "data-stuck": sticky ? stuck ? "true" : "false" : void 0,
1725
+ className: cn(
1726
+ "flex items-start justify-between gap-4 pb-2 sm:pb-3",
1727
+ // No extra pt here — page inset sets the shared title baseline.
1728
+ // Stuck state adds pad via .suite-page-header-sticky[data-stuck].
1729
+ sticky && "suite-page-header-sticky",
1730
+ className
1731
+ ),
1732
+ children: [
1733
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "min-w-0 flex-1", children: [
1734
+ eyebrow ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mb-0.5 flex min-w-0 items-center gap-1.5 text-xs text-ph-subtle", children: eyebrow }) : null,
1735
+ breadcrumb ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mb-0.5 flex min-w-0 items-center gap-1.5 text-xs text-ph-subtle", children: breadcrumb }) : null,
1736
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex min-w-0 items-center gap-3", children: [
1737
+ resolvedIcon ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1738
+ "div",
1739
+ {
1740
+ className: "hidden shrink-0 sm:flex sm:items-center [&_svg]:h-7 [&_svg]:w-7 sm:[&_svg]:h-8 sm:[&_svg]:w-8",
1741
+ "aria-hidden": "true",
1742
+ children: resolvedIcon
1743
+ }
1744
+ ) : null,
1745
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
1746
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1747
+ "h1",
1748
+ {
1749
+ className: cn(
1750
+ "font-display text-[1.625rem] font-bold leading-none tracking-tight text-ph-ink sm:text-[2.25rem]",
1751
+ titleClassName
1752
+ ),
1753
+ children: title
1754
+ }
1755
+ ),
1756
+ badge ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rounded-md bg-ph-muted px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-ph-subtle", children: badge }) : null
1757
+ ] })
1758
+ ] }),
1759
+ description ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1760
+ "div",
1761
+ {
1762
+ className: cn(
1763
+ "mt-1.5 max-w-2xl text-pretty text-sm leading-5 text-ph-subtle",
1764
+ resolvedIcon && "sm:pl-11"
1765
+ ),
1766
+ children: description
1767
+ }
1768
+ ) : null
1769
+ ] }),
1770
+ actions ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex shrink-0 items-center gap-2 self-center", children: actions }) : null
1771
+ ]
1772
+ }
1773
+ );
1774
+ }
1775
+ function SuiteToolbar({
1776
+ children,
1777
+ className,
1778
+ label = "Page tools",
1779
+ dataTest
1780
+ }) {
1781
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1782
+ "div",
1783
+ {
1784
+ role: "toolbar",
1785
+ "aria-label": label,
1786
+ "data-test": dataTest,
1787
+ className: cn("flex min-w-0 flex-wrap items-center gap-2", className),
1788
+ children
1789
+ }
1790
+ );
1791
+ }
1792
+ function SuiteTabList({
1793
+ children,
1794
+ className,
1795
+ label,
1796
+ dataTest
1797
+ }) {
1798
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1799
+ "nav",
1800
+ {
1801
+ "aria-label": label,
1802
+ "data-test": dataTest,
1803
+ className: cn(
1804
+ "flex min-w-0 flex-wrap gap-2 [&_a]:min-h-[48px] [&_button]:min-h-[48px] sm:[&_a]:min-h-[44px] sm:[&_button]:min-h-[44px]",
1805
+ className
1806
+ ),
1807
+ children
1808
+ }
1809
+ );
1810
+ }
1811
+ function SuiteSectionHeader({
1812
+ title,
1813
+ description,
1814
+ action,
1815
+ className
1816
+ }) {
1817
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1818
+ "div",
1819
+ {
1820
+ className: cn(
1821
+ "flex min-w-0 flex-col gap-4 sm:flex-row sm:items-end sm:justify-between",
1822
+ className
1823
+ ),
1824
+ children: [
1825
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "min-w-0", children: [
1826
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { className: "font-display text-balance text-lg font-semibold tracking-tight text-ph-ink sm:text-xl", children: title }),
1827
+ description ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mt-1 max-w-2xl text-pretty text-sm leading-5 text-ph-subtle", children: description }) : null
1828
+ ] }),
1829
+ action ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex min-h-[44px] shrink-0 items-center", children: action }) : null
1830
+ ]
1831
+ }
1832
+ );
1833
+ }
1834
+
1835
+ // src/suite/components/suite-settings-layout.tsx
1836
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1837
+ function SuiteSettingsLayout({
1838
+ children,
1839
+ sidebar,
1840
+ mobileNav,
1841
+ loading,
1842
+ title = "Settings",
1843
+ description,
1844
+ header,
1845
+ dataTest = "settings-page",
1846
+ headerDataTest = "settings-page-header",
1847
+ className,
1848
+ contentClassName
1849
+ }) {
1850
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1851
+ SuitePage,
1852
+ {
1853
+ dataTest,
1854
+ className: cn("flex h-full flex-col overflow-hidden", className),
1855
+ children: [
1856
+ header != null ? header : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1857
+ SuitePageHeader,
1858
+ {
1859
+ sticky: true,
1860
+ dataTest: headerDataTest,
1861
+ title,
1862
+ description
1863
+ }
1864
+ ),
1865
+ loading != null ? loading : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "suite-page-body mt-6 flex min-h-0 flex-1 flex-col", children: [
1866
+ mobileNav ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "sticky top-0 z-20 mb-2 shrink-0 bg-ph-canvas/95 backdrop-blur lg:hidden", children: mobileNav }) : null,
1867
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex min-h-0 flex-1 flex-col gap-6 lg:flex-row lg:gap-8", children: [
1868
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1869
+ "aside",
1870
+ {
1871
+ "aria-label": "Settings",
1872
+ className: "hidden w-56 shrink-0 bg-transparent lg:block lg:self-stretch lg:overflow-y-auto lg:overscroll-contain",
1873
+ children: sidebar
1874
+ }
1875
+ ),
1876
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1877
+ "div",
1878
+ {
1879
+ className: cn(
1880
+ "min-h-0 min-w-0 flex-1 overflow-y-auto overscroll-contain pb-6",
1881
+ contentClassName
1882
+ ),
1883
+ children
1884
+ }
1885
+ )
1886
+ ] })
1887
+ ] })
1888
+ ]
1889
+ }
1890
+ );
1891
+ }
1892
+
1893
+ // src/suite/components/suite-user-menu.tsx
1894
+ var import_image2 = __toESM(require("next/image"));
1895
+ var import_navigation2 = require("next/navigation");
1896
+ var import_iconoir_react6 = require("iconoir-react");
1897
+
1898
+ // src/components/ui/DropdownMenu.tsx
1899
+ var DropdownPrimitive = __toESM(require("@radix-ui/react-dropdown-menu"));
1900
+
1901
+ // src/lib/cn.ts
1902
+ function cn2(...parts) {
1903
+ return parts.filter(Boolean).join(" ");
1904
+ }
1905
+
1906
+ // src/components/ui/DropdownMenu.tsx
1907
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1908
+ function DropdownContent({
1909
+ children,
1910
+ align = "start",
1911
+ side = "bottom",
1912
+ sideOffset = 7,
1913
+ alignOffset = 0,
1914
+ collisionPadding = 8,
1915
+ avoidCollisions = true,
1916
+ panelClassName
1917
+ }) {
1918
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DropdownPrimitive.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1919
+ DropdownPrimitive.Content,
1920
+ {
1921
+ className: cn2("ph-dropdown-panel", panelClassName),
1922
+ align,
1923
+ side,
1924
+ sideOffset,
1925
+ alignOffset,
1926
+ collisionPadding,
1927
+ avoidCollisions,
1928
+ sticky: "partial",
1929
+ children: [
1930
+ children,
1931
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DropdownPrimitive.Arrow, { className: "ph-dropdown-arrow", width: 12, height: 6 })
1932
+ ]
1933
+ }
1934
+ ) });
1935
+ }
1936
+ function DropdownMenu(_a) {
1937
+ var _b = _a, {
1938
+ trigger,
1939
+ children,
1940
+ align,
1941
+ side,
1942
+ sideOffset,
1943
+ alignOffset,
1944
+ collisionPadding,
1945
+ avoidCollisions,
1946
+ className,
1947
+ panelClassName,
1948
+ "aria-label": ariaLabel,
1949
+ open,
1950
+ defaultOpen,
1951
+ onOpenChange,
1952
+ modal = false,
1953
+ disabled
1954
+ } = _b, props = __objRest(_b, [
1955
+ "trigger",
1956
+ "children",
1957
+ "align",
1958
+ "side",
1959
+ "sideOffset",
1960
+ "alignOffset",
1961
+ "collisionPadding",
1962
+ "avoidCollisions",
1963
+ "className",
1964
+ "panelClassName",
1965
+ "aria-label",
1966
+ "open",
1967
+ "defaultOpen",
1968
+ "onOpenChange",
1969
+ "modal",
1970
+ "disabled"
1971
+ ]);
1972
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", __spreadProps(__spreadValues({ className: cn2("ph-dropdown", className) }, props), { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1973
+ DropdownPrimitive.Root,
1974
+ {
1428
1975
  open,
1429
1976
  defaultOpen,
1430
1977
  onOpenChange,
1431
1978
  modal,
1432
1979
  children: [
1433
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DropdownPrimitive.Trigger, { asChild: true, disabled, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "ph-dropdown-trigger", "aria-label": ariaLabel, children: trigger }) }),
1434
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1980
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DropdownPrimitive.Trigger, { asChild: true, disabled, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "ph-dropdown-trigger", "aria-label": ariaLabel, children: trigger }) }),
1981
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1435
1982
  DropdownContent,
1436
1983
  {
1437
1984
  align,
@@ -1450,7 +1997,7 @@ function DropdownMenu(_a) {
1450
1997
  }
1451
1998
  function DropdownItem(_a) {
1452
1999
  var _b = _a, { className, children, disabled } = _b, props = __objRest(_b, ["className", "children", "disabled"]);
1453
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DropdownPrimitive.Item, { asChild: true, disabled, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2000
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DropdownPrimitive.Item, { asChild: true, disabled, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1454
2001
  "button",
1455
2002
  __spreadProps(__spreadValues({
1456
2003
  type: "button",
@@ -1463,7 +2010,7 @@ function DropdownItem(_a) {
1463
2010
  }
1464
2011
 
1465
2012
  // src/suite/components/suite-user-menu.tsx
1466
- var import_jsx_runtime13 = require("react/jsx-runtime");
2013
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1467
2014
  function computeInitials(name, email) {
1468
2015
  const source = (name || email || "?").trim();
1469
2016
  return source.split(/[\s@]+/).filter(Boolean).slice(0, 2).map((part) => part[0]).join("").toUpperCase();
@@ -1480,7 +2027,7 @@ function SuiteUserMenu({
1480
2027
  }) {
1481
2028
  const router = (0, import_navigation2.useRouter)();
1482
2029
  const initials = fallbackInitials != null ? fallbackInitials : computeInitials(name, email);
1483
- const avatar = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "inline-flex h-11 w-11 items-center justify-center rounded-full transition-colors hover:bg-ph-muted", children: image ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2030
+ const avatar = /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "inline-flex h-11 w-11 items-center justify-center rounded-full transition-colors hover:bg-ph-muted", children: image ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1484
2031
  import_image2.default,
1485
2032
  {
1486
2033
  src: image,
@@ -1490,8 +2037,8 @@ function SuiteUserMenu({
1490
2037
  className: "h-8 w-8 rounded-full object-cover",
1491
2038
  unoptimized: true
1492
2039
  }
1493
- ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-ph-muted text-sm font-semibold text-ph-brand", children: initials }) });
1494
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2040
+ ) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-ph-muted text-sm font-semibold text-ph-brand", children: initials }) });
2041
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1495
2042
  DropdownMenu,
1496
2043
  {
1497
2044
  trigger: avatar,
@@ -1504,29 +2051,29 @@ function SuiteUserMenu({
1504
2051
  className,
1505
2052
  "data-test": dataTest,
1506
2053
  children: [
1507
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "px-2 py-1.5", children: [
1508
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "truncate text-sm font-medium text-ph-ink", children: name || email || "Account" }),
1509
- email ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "truncate text-xs text-ph-mutedtext", children: email }) : null
2054
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "px-2 py-1.5", children: [
2055
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "truncate text-sm font-medium text-ph-ink", children: name || email || "Account" }),
2056
+ email ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "truncate text-xs text-ph-mutedtext", children: email }) : null
1510
2057
  ] }),
1511
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "my-1 border-t border-ph-border" }),
1512
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2058
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "my-1 border-t border-ph-border" }),
2059
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1513
2060
  DropdownItem,
1514
2061
  {
1515
2062
  "data-test": `${dataTest}-settings`,
1516
2063
  onClick: () => router.push(settingsHref),
1517
2064
  children: [
1518
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_iconoir_react6.Settings, { className: "h-4 w-4 text-ph-mutedtext", "aria-hidden": true }),
2065
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_iconoir_react6.Settings, { className: "h-4 w-4 text-ph-mutedtext", "aria-hidden": true }),
1519
2066
  "Profile settings"
1520
2067
  ]
1521
2068
  }
1522
2069
  ),
1523
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2070
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1524
2071
  DropdownItem,
1525
2072
  {
1526
2073
  "data-test": `${dataTest}-sign-out`,
1527
2074
  onClick: onSignOut,
1528
2075
  children: [
1529
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_iconoir_react6.LogOut, { className: "h-4 w-4 text-ph-mutedtext", "aria-hidden": true }),
2076
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_iconoir_react6.LogOut, { className: "h-4 w-4 text-ph-mutedtext", "aria-hidden": true }),
1530
2077
  "Sign out"
1531
2078
  ]
1532
2079
  }
@@ -1538,7 +2085,7 @@ function SuiteUserMenu({
1538
2085
 
1539
2086
  // src/suite/components/suite-bottom-nav.tsx
1540
2087
  var import_link = __toESM(require("next/link"));
1541
- var import_jsx_runtime14 = require("react/jsx-runtime");
2088
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1542
2089
  function SuiteBottomNav({
1543
2090
  items,
1544
2091
  ariaLabel = "Primary navigation",
@@ -1551,8 +2098,8 @@ function SuiteBottomNav({
1551
2098
  );
1552
2099
  }
1553
2100
  const activeIndex = items.findIndex((item) => item.active);
1554
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
1555
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2101
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
2102
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1556
2103
  "div",
1557
2104
  {
1558
2105
  "aria-hidden": true,
@@ -1560,7 +2107,7 @@ function SuiteBottomNav({
1560
2107
  style: { height: "calc(60px + env(safe-area-inset-bottom))" }
1561
2108
  }
1562
2109
  ),
1563
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2110
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
1564
2111
  "nav",
1565
2112
  {
1566
2113
  "aria-label": ariaLabel,
@@ -1575,12 +2122,12 @@ function SuiteBottomNav({
1575
2122
  "--suite-nav-active": activeIndex >= 0 ? activeIndex : 0
1576
2123
  },
1577
2124
  children: [
1578
- activeIndex >= 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { "aria-hidden": true, className: "suite-bottom-nav__indicator" }) : null,
2125
+ activeIndex >= 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { "aria-hidden": true, className: "suite-bottom-nav__indicator" }) : null,
1579
2126
  items.map((item) => {
1580
2127
  var _a;
1581
2128
  const Icon = item.icon;
1582
2129
  const active = Boolean(item.active);
1583
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2130
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
1584
2131
  import_link.default,
1585
2132
  {
1586
2133
  href: item.href,
@@ -1591,14 +2138,14 @@ function SuiteBottomNav({
1591
2138
  active && "font-semibold text-ph-brand"
1592
2139
  ),
1593
2140
  children: [
1594
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2141
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1595
2142
  "span",
1596
2143
  {
1597
2144
  className: cn(
1598
2145
  "suite-bottom-nav__icon relative z-10 inline-flex items-center justify-center",
1599
2146
  active && "suite-bottom-nav__icon--active"
1600
2147
  ),
1601
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2148
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1602
2149
  Icon,
1603
2150
  {
1604
2151
  className: "h-6 w-6",
@@ -1608,7 +2155,7 @@ function SuiteBottomNav({
1608
2155
  )
1609
2156
  }
1610
2157
  ),
1611
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "relative z-10 max-w-full truncate", children: item.label })
2158
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "relative z-10 max-w-full truncate", children: item.label })
1612
2159
  ]
1613
2160
  },
1614
2161
  item.href
@@ -1620,117 +2167,39 @@ function SuiteBottomNav({
1620
2167
  ] });
1621
2168
  }
1622
2169
 
1623
- // src/suite/lib/apps.ts
1624
- var SUITE_APPS2 = [
1625
- {
1626
- slug: "shellstack",
1627
- name: "ShellStack",
1628
- description: "Organisation and billing",
1629
- icon: "/icon-shellstack.svg",
1630
- landing: "/home",
1631
- baseUrlEnv: "NEXT_PUBLIC_SHELLSTACK_URL",
1632
- baseUrlFallback: "http://localhost:3002"
1633
- },
1634
- {
1635
- slug: "tides",
1636
- name: "Tides",
1637
- description: "Projects and tasks",
1638
- icon: "/tides-icon.svg",
1639
- landing: "/today",
1640
- baseUrlEnv: "NEXT_PUBLIC_TIDES_URL",
1641
- baseUrlFallback: "http://localhost:3001"
1642
- },
1643
- {
1644
- slug: "turtletime",
1645
- name: "TurtleTime",
1646
- description: "Time tracking",
1647
- icon: "/turtletime-icon.svg",
1648
- landing: "/tracker",
1649
- baseUrlEnv: "NEXT_PUBLIC_TURTLETIME_URL",
1650
- baseUrlFallback: "http://localhost:3000"
1651
- },
1652
- {
1653
- slug: "kraken",
1654
- name: "Kraken",
1655
- description: "Documents and knowledge",
1656
- icon: "/kraken-icon.svg",
1657
- landing: "/today",
1658
- baseUrlEnv: "NEXT_PUBLIC_KRAKEN_URL",
1659
- baseUrlFallback: "http://localhost:3003"
1660
- }
1661
- ];
1662
- var SUITE_APP_MAP = Object.fromEntries(SUITE_APPS2.map((app) => [app.slug, app]));
1663
- function suiteAppBaseUrl(slug) {
1664
- const def = SUITE_APP_MAP[slug];
1665
- const fromEnv = typeof process !== "undefined" ? process.env[def.baseUrlEnv] : void 0;
1666
- return (fromEnv || def.baseUrlFallback).replace(/\/$/, "");
1667
- }
1668
-
1669
2170
  // src/suite/components/today-calibrating.tsx
1670
2171
  var import_iconoir_react7 = require("iconoir-react");
1671
- var import_jsx_runtime15 = require("react/jsx-runtime");
2172
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1672
2173
  function TodayCalibrating({
1673
2174
  onRetry,
1674
2175
  retrying = false,
1675
2176
  className,
1676
2177
  spinner: Spinner,
1677
- variant = "ring",
2178
+ variant: _variant = "ring",
1678
2179
  retrySpinnerClassName = "text-ph-brand"
1679
2180
  }) {
1680
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2181
+ void _variant;
2182
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1681
2183
  "div",
1682
2184
  {
1683
2185
  className: cn(
1684
- "relative flex min-h-[14rem] flex-col items-center justify-center overflow-hidden rounded-2xl border border-ph-border bg-ph-surface px-5 py-8 text-center shadow-sm",
2186
+ "suite-auth-handoff relative flex min-h-[14rem] flex-col items-center justify-center overflow-hidden rounded-2xl border border-ph-border bg-ph-surface px-5 py-8 text-center shadow-sm",
1685
2187
  className
1686
2188
  ),
1687
2189
  "data-test": "today-calibrating",
1688
2190
  children: [
1689
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2191
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1690
2192
  "div",
1691
2193
  {
1692
- className: "pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_50%_35%,rgba(251,191,36,0.12),transparent_55%)]",
2194
+ className: "pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_50%_35%,rgba(251,191,36,0.1),transparent_55%)]",
1693
2195
  "aria-hidden": true
1694
2196
  }
1695
2197
  ),
1696
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "relative mb-6 flex h-24 w-24 items-center justify-center", children: [
1697
- variant === "ring" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
1698
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1699
- Spinner,
1700
- {
1701
- size: "lg",
1702
- className: "absolute inset-0 m-auto opacity-40",
1703
- label: "Loading today"
1704
- }
1705
- ),
1706
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1707
- "span",
1708
- {
1709
- className: "bg-ph-muted/30 absolute inset-2 animate-ping rounded-full",
1710
- "aria-hidden": true
1711
- }
1712
- )
1713
- ] }) : null,
1714
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "relative flex h-14 w-14 items-center justify-center rounded-2xl border border-ph-border bg-ph-surface text-ph-ink shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1715
- import_iconoir_react7.SunLight,
1716
- {
1717
- className: cn("h-9 w-9", variant === "pulse" && "animate-pulse"),
1718
- strokeWidth: 2,
1719
- "aria-hidden": true
1720
- }
1721
- ) })
1722
- ] }),
1723
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h2", { className: "relative text-base font-semibold tracking-tight text-ph-ink sm:text-lg md:text-xl", children: "Calibrating your day" }),
1724
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "relative mt-1.5 max-w-sm text-sm sm:text-[15px] leading-relaxed text-ph-subtle", children: "Pulling together tasks, time and updates from your workspace\u2026" }),
1725
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "relative mt-5 flex gap-1.5", "aria-hidden": true, children: [0, 1, 2, 3, 4].map((i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1726
- "span",
1727
- {
1728
- className: "h-1.5 w-5 animate-pulse rounded-full bg-ph-muted",
1729
- style: { animationDelay: `${i * 120}ms` }
1730
- },
1731
- i
1732
- )) }),
1733
- onRetry ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2198
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "relative mb-5 flex h-14 w-14 items-center justify-center rounded-2xl border border-ph-border bg-ph-surface text-ph-ink shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_iconoir_react7.SunLight, { className: "h-8 w-8", strokeWidth: 2, "aria-hidden": true }) }),
2199
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("h2", { className: "relative text-base font-semibold tracking-tight text-ph-ink sm:text-lg md:text-xl", children: "Calibrating your day" }),
2200
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "relative mt-1.5 max-w-sm text-sm sm:text-[15px] leading-relaxed text-ph-subtle", children: "Pulling together tasks, time and updates from your workspace\u2026" }),
2201
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "relative mt-5 flex justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Spinner, { size: "sm", className: "opacity-70", label: "Loading today" }) }),
2202
+ onRetry ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1734
2203
  "button",
1735
2204
  {
1736
2205
  type: "button",
@@ -1738,7 +2207,7 @@ function TodayCalibrating({
1738
2207
  disabled: retrying,
1739
2208
  className: "relative mt-6 inline-flex items-center gap-2 rounded-xl border border-ph-border bg-ph-canvas px-4 py-2 text-sm font-medium text-ph-ink transition hover:bg-ph-muted disabled:opacity-60",
1740
2209
  children: [
1741
- retrying ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Spinner, { size: "sm", className: retrySpinnerClassName }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_iconoir_react7.Refresh, { className: "h-4 w-4", "aria-hidden": true }),
2210
+ retrying ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Spinner, { size: "sm", className: retrySpinnerClassName }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_iconoir_react7.Refresh, { className: "h-4 w-4", "aria-hidden": true }),
1742
2211
  "Try again"
1743
2212
  ]
1744
2213
  }
@@ -1749,9 +2218,9 @@ function TodayCalibrating({
1749
2218
  }
1750
2219
 
1751
2220
  // src/suite/components/theme-provider.tsx
1752
- var import_react6 = require("react");
1753
- var import_jsx_runtime16 = require("react/jsx-runtime");
1754
- var SuiteThemeContext = (0, import_react6.createContext)(null);
2221
+ var import_react7 = require("react");
2222
+ var import_jsx_runtime18 = require("react/jsx-runtime");
2223
+ var SuiteThemeContext = (0, import_react7.createContext)(null);
1755
2224
  function systemTheme(fallback) {
1756
2225
  if (typeof window === "undefined") return fallback;
1757
2226
  return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
@@ -1788,7 +2257,7 @@ function SuiteThemeProvider({
1788
2257
  config,
1789
2258
  children
1790
2259
  }) {
1791
- const [state, setState] = (0, import_react6.useState)(() => {
2260
+ const [state, setState] = (0, import_react7.useState)(() => {
1792
2261
  const theme2 = readStoredTheme(config.storageKey);
1793
2262
  if (typeof document === "undefined") {
1794
2263
  return { theme: theme2, resolvedTheme: config.fallbackTheme };
@@ -1798,7 +2267,7 @@ function SuiteThemeProvider({
1798
2267
  return { theme: theme2, resolvedTheme: resolvedTheme2 };
1799
2268
  });
1800
2269
  const { theme, resolvedTheme } = state;
1801
- (0, import_react6.useEffect)(() => {
2270
+ (0, import_react7.useEffect)(() => {
1802
2271
  if (theme !== "system") return;
1803
2272
  const media = window.matchMedia("(prefers-color-scheme: dark)");
1804
2273
  const update = () => {
@@ -1808,7 +2277,7 @@ function SuiteThemeProvider({
1808
2277
  media.addEventListener("change", update);
1809
2278
  return () => media.removeEventListener("change", update);
1810
2279
  }, [theme, config]);
1811
- const setTheme = (0, import_react6.useCallback)(
2280
+ const setTheme = (0, import_react7.useCallback)(
1812
2281
  (next) => {
1813
2282
  const resolved = applyTheme(next, config);
1814
2283
  setState({ theme: next, resolvedTheme: resolved });
@@ -1816,7 +2285,7 @@ function SuiteThemeProvider({
1816
2285
  },
1817
2286
  [config]
1818
2287
  );
1819
- const toggleTheme = (0, import_react6.useCallback)(() => {
2288
+ const toggleTheme = (0, import_react7.useCallback)(() => {
1820
2289
  setState((prev) => {
1821
2290
  const next = resolveTheme(prev.theme, config.fallbackTheme) === "dark" ? "light" : "dark";
1822
2291
  const resolved = applyTheme(next, config);
@@ -1824,52 +2293,178 @@ function SuiteThemeProvider({
1824
2293
  return { theme: next, resolvedTheme: resolved };
1825
2294
  });
1826
2295
  }, [config]);
1827
- const value = (0, import_react6.useMemo)(
2296
+ const value = (0, import_react7.useMemo)(
1828
2297
  () => ({ theme, resolvedTheme, setTheme, toggleTheme }),
1829
2298
  [theme, resolvedTheme, setTheme, toggleTheme]
1830
2299
  );
1831
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SuiteThemeContext.Provider, { value, children });
2300
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SuiteThemeContext.Provider, { value, children });
1832
2301
  }
1833
2302
  function useSuiteTheme() {
1834
- const ctx = (0, import_react6.useContext)(SuiteThemeContext);
2303
+ const ctx = (0, import_react7.useContext)(SuiteThemeContext);
1835
2304
  if (!ctx) throw new Error("useTheme must be used within ThemeProvider");
1836
2305
  return ctx;
1837
2306
  }
1838
2307
 
1839
2308
  // src/suite/components/ai-panel.tsx
1840
- var import_react7 = require("react");
2309
+ var import_react8 = require("react");
1841
2310
  var import_iconoir_react8 = require("iconoir-react");
1842
- var import_jsx_runtime17 = require("react/jsx-runtime");
2311
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2312
+ var SUITE_OPEN_ASK_AI_EVENT = "shellstack:open-ask-ai";
2313
+ function openSuiteAskAi(detail) {
2314
+ if (typeof window === "undefined") return;
2315
+ window.dispatchEvent(
2316
+ new CustomEvent(SUITE_OPEN_ASK_AI_EVENT, {
2317
+ detail: detail != null ? detail : {}
2318
+ })
2319
+ );
2320
+ }
2321
+ var LAUNCHER_EDGE_GAP = 12;
2322
+ var LAUNCHER_STORAGE_KEY = "suite-ask-ai-launcher-position";
2323
+ function resolveSuiteAiLauncherPosition(side, desiredY, viewportWidth, viewportHeight, launcherWidth, launcherHeight) {
2324
+ const maxX = Math.max(
2325
+ LAUNCHER_EDGE_GAP,
2326
+ viewportWidth - launcherWidth - LAUNCHER_EDGE_GAP
2327
+ );
2328
+ const maxY = Math.max(
2329
+ LAUNCHER_EDGE_GAP,
2330
+ viewportHeight - launcherHeight - LAUNCHER_EDGE_GAP
2331
+ );
2332
+ return {
2333
+ x: side === "left" ? LAUNCHER_EDGE_GAP : maxX,
2334
+ y: Math.min(Math.max(desiredY, LAUNCHER_EDGE_GAP), maxY)
2335
+ };
2336
+ }
1843
2337
  function SuiteAiPanel({
1844
- presets,
1845
- emptyState,
1846
- fetchStatus,
1847
- complete,
2338
+ presets = [],
2339
+ emptyState = "Ask anything about your workspace, or look something up online.",
2340
+ title = "Ask AI",
2341
+ fetchChat,
2342
+ sendMessage,
2343
+ clearChat,
1848
2344
  brandIcon: BrandIcon,
1849
- spinner: Spinner
2345
+ spinner: Spinner,
2346
+ hideLauncher = false
1850
2347
  }) {
1851
- const [open, setOpen] = (0, import_react7.useState)(false);
1852
- const [prompt, setPrompt] = (0, import_react7.useState)("");
1853
- const [loading, setLoading] = (0, import_react7.useState)(false);
1854
- const [answer, setAnswer] = (0, import_react7.useState)("");
1855
- const [error, setError] = (0, import_react7.useState)("");
1856
- const [copied, setCopied] = (0, import_react7.useState)(false);
2348
+ const [open, setOpen] = (0, import_react8.useState)(false);
2349
+ const [prompt, setPrompt] = (0, import_react8.useState)("");
2350
+ const [messages, setMessages] = (0, import_react8.useState)([]);
2351
+ const [loading, setLoading] = (0, import_react8.useState)(false);
2352
+ const [hydrating, setHydrating] = (0, import_react8.useState)(false);
2353
+ const [error, setError] = (0, import_react8.useState)("");
2354
+ const [copiedId, setCopiedId] = (0, import_react8.useState)(null);
2355
+ const [launcherSide, setLauncherSide] = (0, import_react8.useState)("right");
2356
+ const [launcherPosition, setLauncherPosition] = (0, import_react8.useState)(null);
2357
+ const [draggingLauncher, setDraggingLauncher] = (0, import_react8.useState)(false);
2358
+ const scrollerRef = (0, import_react8.useRef)(null);
2359
+ const launcherRef = (0, import_react8.useRef)(null);
2360
+ const launcherSideRef = (0, import_react8.useRef)("right");
2361
+ const launcherPositionRef = (0, import_react8.useRef)(null);
2362
+ const launcherDragRef = (0, import_react8.useRef)(null);
2363
+ const suppressLauncherClickRef = (0, import_react8.useRef)(false);
2364
+ const pendingPromptRef = (0, import_react8.useRef)(null);
2365
+ (0, import_react8.useEffect)(() => {
2366
+ const launcher = launcherRef.current;
2367
+ if (!launcher) return;
2368
+ let side = "right";
2369
+ let desiredY = window.innerHeight - launcher.offsetHeight - 20;
2370
+ try {
2371
+ const stored = JSON.parse(
2372
+ localStorage.getItem(LAUNCHER_STORAGE_KEY) || "null"
2373
+ );
2374
+ if ((stored == null ? void 0 : stored.side) === "left" || (stored == null ? void 0 : stored.side) === "right")
2375
+ side = stored.side;
2376
+ if (typeof (stored == null ? void 0 : stored.y) === "number") desiredY = stored.y;
2377
+ } catch (e) {
2378
+ }
2379
+ launcherSideRef.current = side;
2380
+ setLauncherSide(side);
2381
+ const placeLauncher = () => {
2382
+ var _a, _b;
2383
+ const element = launcherRef.current;
2384
+ if (!element) return;
2385
+ const next = resolveSuiteAiLauncherPosition(
2386
+ launcherSideRef.current,
2387
+ (_b = (_a = launcherPositionRef.current) == null ? void 0 : _a.y) != null ? _b : desiredY,
2388
+ window.innerWidth,
2389
+ window.innerHeight,
2390
+ element.offsetWidth,
2391
+ element.offsetHeight
2392
+ );
2393
+ launcherPositionRef.current = next;
2394
+ setLauncherPosition(next);
2395
+ };
2396
+ placeLauncher();
2397
+ window.addEventListener("resize", placeLauncher);
2398
+ return () => window.removeEventListener("resize", placeLauncher);
2399
+ }, []);
2400
+ const scrollToBottom = (0, import_react8.useCallback)(() => {
2401
+ const el = scrollerRef.current;
2402
+ if (!el) return;
2403
+ el.scrollTop = el.scrollHeight;
2404
+ }, []);
2405
+ const hydrate = (0, import_react8.useCallback)(async () => {
2406
+ var _a;
2407
+ setHydrating(true);
2408
+ setError("");
2409
+ try {
2410
+ const data = await fetchChat();
2411
+ setMessages((_a = data.messages) != null ? _a : []);
2412
+ if (!data.configured) {
2413
+ setError("ShellStack AI is not configured for this workspace yet.");
2414
+ }
2415
+ } catch (err) {
2416
+ setError(
2417
+ err instanceof Error ? err.message : "Could not load your Ask AI chat."
2418
+ );
2419
+ } finally {
2420
+ setHydrating(false);
2421
+ }
2422
+ }, [fetchChat]);
2423
+ (0, import_react8.useEffect)(() => {
2424
+ function onOpen(event) {
2425
+ var _a;
2426
+ const detail = event.detail;
2427
+ setOpen(true);
2428
+ if ((_a = detail == null ? void 0 : detail.prompt) == null ? void 0 : _a.trim()) {
2429
+ pendingPromptRef.current = detail.prompt.trim();
2430
+ setPrompt(detail.prompt.trim());
2431
+ }
2432
+ }
2433
+ window.addEventListener(SUITE_OPEN_ASK_AI_EVENT, onOpen);
2434
+ return () => window.removeEventListener(SUITE_OPEN_ASK_AI_EVENT, onOpen);
2435
+ }, []);
2436
+ (0, import_react8.useEffect)(() => {
2437
+ if (!open) return;
2438
+ void hydrate().then(() => {
2439
+ const pending = pendingPromptRef.current;
2440
+ pendingPromptRef.current = null;
2441
+ if (pending) {
2442
+ setPrompt(pending);
2443
+ }
2444
+ });
2445
+ }, [open, hydrate]);
2446
+ (0, import_react8.useEffect)(() => {
2447
+ if (open) scrollToBottom();
2448
+ }, [messages, loading, open, scrollToBottom]);
1857
2449
  async function ask(text) {
2450
+ var _a;
1858
2451
  const trimmed = text.trim();
1859
2452
  if (!trimmed || loading) return;
1860
2453
  setLoading(true);
1861
2454
  setError("");
1862
- setAnswer("");
1863
- setCopied(false);
2455
+ setPrompt("");
2456
+ setMessages((prev) => [...prev, { role: "user", content: trimmed }]);
1864
2457
  try {
1865
- const status = await fetchStatus();
1866
- if (!status.configured) {
1867
- setError("ShellStack AI is not configured for this workspace yet.");
1868
- return;
1869
- }
1870
- const result = await complete({ prompt: trimmed });
1871
- setAnswer(result.text);
2458
+ const result = await sendMessage({ prompt: trimmed });
2459
+ setMessages((_a = result.messages) != null ? _a : []);
1872
2460
  } catch (err) {
2461
+ setMessages(
2462
+ (prev) => {
2463
+ var _a2, _b;
2464
+ return ((_a2 = prev[prev.length - 1]) == null ? void 0 : _a2.role) === "user" && ((_b = prev[prev.length - 1]) == null ? void 0 : _b.content) === trimmed ? prev.slice(0, -1) : prev;
2465
+ }
2466
+ );
2467
+ setPrompt(trimmed);
1873
2468
  setError(
1874
2469
  err instanceof Error ? err.message : "The AI request failed. Try again."
1875
2470
  );
@@ -1877,133 +2472,391 @@ function SuiteAiPanel({
1877
2472
  setLoading(false);
1878
2473
  }
1879
2474
  }
1880
- async function copyAnswer() {
1881
- if (!answer) return;
2475
+ async function handleClear() {
2476
+ if (loading) return;
1882
2477
  try {
1883
- await navigator.clipboard.writeText(answer);
1884
- setCopied(true);
1885
- window.setTimeout(() => setCopied(false), 1500);
2478
+ await clearChat();
2479
+ setMessages([]);
2480
+ setError("");
2481
+ } catch (err) {
2482
+ setError(
2483
+ err instanceof Error ? err.message : "Could not clear this chat."
2484
+ );
2485
+ }
2486
+ }
2487
+ async function copyMessage(index, content) {
2488
+ try {
2489
+ await navigator.clipboard.writeText(content);
2490
+ setCopiedId(index);
2491
+ window.setTimeout(() => setCopiedId(null), 1500);
1886
2492
  } catch (e) {
1887
2493
  }
1888
2494
  }
1889
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
1890
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1891
- "button",
1892
- {
1893
- type: "button",
1894
- "data-test": "ai-panel-launcher",
1895
- onClick: () => setOpen(true),
1896
- className: "fixed bottom-5 right-5 z-30 flex items-center gap-2 rounded-full border border-ph-border bg-ph-surface px-4 py-2.5 text-sm font-medium text-ph-ink shadow-ph hover:bg-ph-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
1897
- "aria-label": "Open ShellStack AI",
1898
- children: [
1899
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(BrandIcon, { className: "h-4 w-4 text-ph-brand" }),
1900
- "Ask AI"
1901
- ]
1902
- }
1903
- ),
1904
- open && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "fixed inset-0 z-40", role: "dialog", "aria-modal": "true", children: [
1905
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2495
+ function saveLauncherPosition(side, position) {
2496
+ try {
2497
+ localStorage.setItem(
2498
+ LAUNCHER_STORAGE_KEY,
2499
+ JSON.stringify({ side, y: position.y })
2500
+ );
2501
+ } catch (e) {
2502
+ }
2503
+ }
2504
+ function dockLauncher(side, desiredY) {
2505
+ const launcher = launcherRef.current;
2506
+ if (!launcher) return;
2507
+ const next = resolveSuiteAiLauncherPosition(
2508
+ side,
2509
+ desiredY,
2510
+ window.innerWidth,
2511
+ window.innerHeight,
2512
+ launcher.offsetWidth,
2513
+ launcher.offsetHeight
2514
+ );
2515
+ launcherSideRef.current = side;
2516
+ launcherPositionRef.current = next;
2517
+ setLauncherSide(side);
2518
+ setLauncherPosition(next);
2519
+ saveLauncherPosition(side, next);
2520
+ }
2521
+ function startLauncherDrag(event) {
2522
+ if (event.pointerType === "mouse" && event.button !== 0) return;
2523
+ const rect = event.currentTarget.getBoundingClientRect();
2524
+ launcherDragRef.current = {
2525
+ pointerId: event.pointerId,
2526
+ offsetX: event.clientX - rect.left,
2527
+ offsetY: event.clientY - rect.top,
2528
+ startX: event.clientX,
2529
+ startY: event.clientY
2530
+ };
2531
+ suppressLauncherClickRef.current = false;
2532
+ event.currentTarget.setPointerCapture(event.pointerId);
2533
+ setDraggingLauncher(true);
2534
+ }
2535
+ function moveLauncher(event) {
2536
+ const drag = launcherDragRef.current;
2537
+ if (!drag || drag.pointerId !== event.pointerId) return;
2538
+ const launcher = event.currentTarget;
2539
+ const maxX = Math.max(
2540
+ LAUNCHER_EDGE_GAP,
2541
+ window.innerWidth - launcher.offsetWidth - LAUNCHER_EDGE_GAP
2542
+ );
2543
+ const maxY = Math.max(
2544
+ LAUNCHER_EDGE_GAP,
2545
+ window.innerHeight - launcher.offsetHeight - LAUNCHER_EDGE_GAP
2546
+ );
2547
+ const next = {
2548
+ x: Math.min(
2549
+ Math.max(event.clientX - drag.offsetX, LAUNCHER_EDGE_GAP),
2550
+ maxX
2551
+ ),
2552
+ y: Math.min(
2553
+ Math.max(event.clientY - drag.offsetY, LAUNCHER_EDGE_GAP),
2554
+ maxY
2555
+ )
2556
+ };
2557
+ if (Math.hypot(event.clientX - drag.startX, event.clientY - drag.startY) > 4) {
2558
+ suppressLauncherClickRef.current = true;
2559
+ }
2560
+ launcherPositionRef.current = next;
2561
+ setLauncherPosition(next);
2562
+ }
2563
+ function finishLauncherDrag(event) {
2564
+ var _a, _b, _c, _d;
2565
+ const drag = launcherDragRef.current;
2566
+ if (!drag || drag.pointerId !== event.pointerId) return;
2567
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
2568
+ event.currentTarget.releasePointerCapture(event.pointerId);
2569
+ }
2570
+ launcherDragRef.current = null;
2571
+ setDraggingLauncher(false);
2572
+ const rect = event.currentTarget.getBoundingClientRect();
2573
+ const currentX = (_b = (_a = launcherPositionRef.current) == null ? void 0 : _a.x) != null ? _b : rect.left;
2574
+ const side = currentX + rect.width / 2 < window.innerWidth / 2 ? "left" : "right";
2575
+ dockLauncher(side, (_d = (_c = launcherPositionRef.current) == null ? void 0 : _c.y) != null ? _d : rect.top);
2576
+ }
2577
+ function moveLauncherWithKeyboard(event) {
2578
+ var _a, _b;
2579
+ if (!["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].includes(event.key))
2580
+ return;
2581
+ event.preventDefault();
2582
+ const currentY = (_b = (_a = launcherPositionRef.current) == null ? void 0 : _a.y) != null ? _b : event.currentTarget.getBoundingClientRect().top;
2583
+ if (event.key === "ArrowLeft") dockLauncher("left", currentY);
2584
+ else if (event.key === "ArrowRight") dockLauncher("right", currentY);
2585
+ else
2586
+ dockLauncher(
2587
+ launcherSideRef.current,
2588
+ currentY + (event.key === "ArrowUp" ? -24 : 24)
2589
+ );
2590
+ }
2591
+ const Icon = BrandIcon != null ? BrandIcon : import_iconoir_react8.Sparks;
2592
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
2593
+ !hideLauncher ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
2594
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2595
+ "button",
2596
+ {
2597
+ ref: launcherRef,
2598
+ type: "button",
2599
+ "data-test": "ai-panel-launcher",
2600
+ onClick: () => {
2601
+ if (suppressLauncherClickRef.current) {
2602
+ suppressLauncherClickRef.current = false;
2603
+ return;
2604
+ }
2605
+ setOpen(true);
2606
+ },
2607
+ onPointerDown: startLauncherDrag,
2608
+ onPointerMove: moveLauncher,
2609
+ onPointerUp: finishLauncherDrag,
2610
+ onPointerCancel: finishLauncherDrag,
2611
+ onKeyDown: moveLauncherWithKeyboard,
2612
+ className: `fixed z-30 flex touch-none select-none items-center gap-2 rounded-full border border-ph-border bg-ph-surface px-4 py-2.5 text-sm font-medium text-ph-ink shadow-ph hover:bg-ph-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand ${draggingLauncher ? "cursor-grabbing" : "cursor-grab"}`,
2613
+ style: launcherPosition ? { left: launcherPosition.x, top: launcherPosition.y } : { bottom: 20, right: 20 },
2614
+ "aria-label": "Open Ask AI",
2615
+ "aria-describedby": "suite-ask-ai-launcher-help",
2616
+ children: [
2617
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Icon, { className: "h-4 w-4 text-ph-brand" }),
2618
+ "Ask AI"
2619
+ ]
2620
+ }
2621
+ ),
2622
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { id: "suite-ask-ai-launcher-help", className: "sr-only", children: "Drag to reposition, or use arrow keys. The launcher docks to the nearest screen edge." })
2623
+ ] }) : null,
2624
+ open ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "fixed inset-0 z-40", role: "dialog", "aria-modal": "true", children: [
2625
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1906
2626
  "button",
1907
2627
  {
1908
2628
  type: "button",
1909
2629
  className: "absolute inset-0 bg-black/25",
1910
2630
  onClick: () => setOpen(false),
1911
- "aria-label": "Close AI panel"
2631
+ "aria-label": "Close Ask AI"
1912
2632
  }
1913
2633
  ),
1914
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("aside", { className: "absolute inset-y-0 right-0 flex w-full max-w-md flex-col border-l border-ph-border bg-ph-surface shadow-2xl", children: [
1915
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center justify-between border-b border-ph-border px-4 py-3", children: [
1916
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-2", children: [
1917
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(BrandIcon, { className: "h-4 w-4 text-ph-brand" }),
1918
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-sm font-semibold text-ph-ink", children: "ShellStack AI" })
1919
- ] }),
1920
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1921
- "button",
1922
- {
1923
- type: "button",
1924
- onClick: () => setOpen(false),
1925
- className: "rounded p-1 hover:bg-ph-muted",
1926
- "aria-label": "Close",
1927
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_iconoir_react8.Xmark, { className: "h-4 w-4" })
1928
- }
1929
- )
1930
- ] }),
1931
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex flex-wrap gap-1.5 border-b border-ph-border px-4 py-2", children: presets.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1932
- "button",
1933
- {
1934
- type: "button",
1935
- onClick: () => {
1936
- setPrompt(preset.prompt);
1937
- void ask(preset.prompt);
1938
- },
1939
- disabled: loading,
1940
- className: "rounded-md border border-ph-border px-2 py-1 text-xs text-ph-ink hover:bg-ph-muted disabled:opacity-50",
1941
- children: preset.label
1942
- },
1943
- preset.label
1944
- )) }),
1945
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [
1946
- error && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "bg-ph-danger/10 rounded-lg px-3 py-2 text-sm text-ph-danger", children: error }),
1947
- loading && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-2 text-sm text-ph-mutedtext", children: [
1948
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Spinner, { className: "h-4 w-4 animate-spin" }),
1949
- "Thinking\u2026"
1950
- ] }),
1951
- answer && !loading && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "rounded-lg bg-ph-muted px-3 py-2 text-sm text-ph-ink", children: [
1952
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "whitespace-pre-wrap", children: answer }),
1953
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2634
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2635
+ "aside",
2636
+ {
2637
+ className: `absolute inset-y-0 flex w-full max-w-md flex-col bg-ph-surface shadow-2xl ${launcherSide === "left" ? "left-0 border-r border-ph-border" : "right-0 border-l border-ph-border"}`,
2638
+ "data-test": "suite-ask-ai-panel",
2639
+ children: [
2640
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center justify-between border-b border-ph-border px-4 py-3", children: [
2641
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "min-w-0", children: [
2642
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2", children: [
2643
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Icon, { className: "h-4 w-4 shrink-0 text-ph-brand" }),
2644
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-sm font-semibold text-ph-ink", children: title })
2645
+ ] }),
2646
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "mt-0.5 text-[11px] text-ph-mutedtext", children: "Shared across your suite \xB7 same chat in every app" })
2647
+ ] }),
2648
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-1", children: [
2649
+ messages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2650
+ "button",
2651
+ {
2652
+ type: "button",
2653
+ onClick: () => void handleClear(),
2654
+ disabled: loading,
2655
+ className: "rounded-md px-2 py-1 text-xs text-ph-mutedtext hover:bg-ph-muted hover:text-ph-ink disabled:opacity-50",
2656
+ "data-test": "ask-ai-clear",
2657
+ children: "Clear"
2658
+ }
2659
+ ) : null,
2660
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2661
+ "button",
2662
+ {
2663
+ type: "button",
2664
+ onClick: () => setOpen(false),
2665
+ className: "rounded p-1 hover:bg-ph-muted",
2666
+ "aria-label": "Close",
2667
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_iconoir_react8.Xmark, { className: "h-4 w-4" })
2668
+ }
2669
+ )
2670
+ ] })
2671
+ ] }),
2672
+ presets.length > 0 && messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "flex flex-wrap gap-1.5 border-b border-ph-border px-4 py-2", children: presets.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1954
2673
  "button",
1955
2674
  {
1956
2675
  type: "button",
1957
- onClick: () => void copyAnswer(),
1958
- className: "mt-2 inline-flex items-center gap-1 text-xs text-ph-mutedtext hover:text-ph-ink",
2676
+ onClick: () => void ask(preset.prompt),
2677
+ disabled: loading || hydrating,
2678
+ className: "rounded-full border border-ph-border px-2.5 py-1 text-xs text-ph-ink hover:bg-ph-muted disabled:opacity-50",
2679
+ children: preset.label
2680
+ },
2681
+ preset.label
2682
+ )) }) : null,
2683
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2684
+ "div",
2685
+ {
2686
+ ref: scrollerRef,
2687
+ className: "flex-1 space-y-3 overflow-y-auto p-4",
1959
2688
  children: [
1960
- copied ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_iconoir_react8.Check, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_iconoir_react8.Copy, { className: "h-3 w-3" }),
1961
- copied ? "Copied" : "Copy"
2689
+ error ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "bg-ph-danger/10 rounded-lg px-3 py-2 text-sm text-ph-danger", children: error }) : null,
2690
+ hydrating && messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2 text-sm text-ph-mutedtext", children: [
2691
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Spinner, { className: "h-4 w-4 animate-spin" }),
2692
+ "Loading chat\u2026"
2693
+ ] }) : null,
2694
+ !hydrating && messages.length === 0 && !error ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "mt-10 px-2 text-center", children: [
2695
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "font-display text-base font-semibold text-ph-ink", children: "How can I help?" }),
2696
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "mt-2 text-sm leading-relaxed text-ph-subtle", children: emptyState })
2697
+ ] }) : null,
2698
+ messages.map((message, index) => {
2699
+ const isUser = message.role === "user";
2700
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2701
+ "div",
2702
+ {
2703
+ className: isUser ? "ml-8 rounded-2xl bg-ph-brand/10 px-3.5 py-2.5 text-sm text-ph-ink" : "mr-4 rounded-2xl bg-ph-muted px-3.5 py-2.5 text-sm text-ph-ink",
2704
+ "data-test": isUser ? "ask-ai-user" : "ask-ai-assistant",
2705
+ children: [
2706
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "whitespace-pre-wrap", children: message.content }),
2707
+ !isUser ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2708
+ "button",
2709
+ {
2710
+ type: "button",
2711
+ onClick: () => void copyMessage(index, message.content),
2712
+ className: "mt-2 inline-flex items-center gap-1 text-xs text-ph-mutedtext hover:text-ph-ink",
2713
+ children: [
2714
+ copiedId === index ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_iconoir_react8.Check, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_iconoir_react8.Copy, { className: "h-3 w-3" }),
2715
+ copiedId === index ? "Copied" : "Copy"
2716
+ ]
2717
+ }
2718
+ ) : null
2719
+ ]
2720
+ },
2721
+ `${message.role}-${index}-${message.content.slice(0, 24)}`
2722
+ );
2723
+ }),
2724
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2 text-sm text-ph-mutedtext", children: [
2725
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Spinner, { className: "h-4 w-4 animate-spin" }),
2726
+ "Thinking\u2026"
2727
+ ] }) : null
1962
2728
  ]
1963
2729
  }
1964
- )
1965
- ] }),
1966
- !answer && !loading && !error && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "mt-8 text-center text-xs text-ph-mutedtext", children: emptyState })
1967
- ] }),
1968
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "border-t border-ph-border p-3", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex gap-2", children: [
1969
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1970
- "textarea",
1971
- {
1972
- value: prompt,
1973
- onChange: (e) => setPrompt(e.target.value),
1974
- onKeyDown: (e) => {
1975
- if (e.key === "Enter" && !e.shiftKey) {
1976
- e.preventDefault();
1977
- void ask(prompt);
2730
+ ),
2731
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "border-t border-ph-border p-3", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex gap-2", children: [
2732
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2733
+ "textarea",
2734
+ {
2735
+ value: prompt,
2736
+ onChange: (e) => setPrompt(e.target.value),
2737
+ onKeyDown: (e) => {
2738
+ if (e.key === "Enter" && !e.shiftKey) {
2739
+ e.preventDefault();
2740
+ void ask(prompt);
2741
+ }
2742
+ },
2743
+ placeholder: "Ask anything\u2026",
2744
+ rows: 2,
2745
+ disabled: loading,
2746
+ "data-test": "ask-ai-input",
2747
+ className: "flex-1 resize-none rounded-xl border border-ph-border bg-ph-canvas p-2.5 text-sm text-ph-ink outline-none focus:ring-1 focus:ring-ph-brand"
1978
2748
  }
1979
- },
1980
- placeholder: "Ask anything\u2026",
1981
- rows: 2,
1982
- disabled: loading,
1983
- className: "flex-1 resize-none rounded-lg border border-ph-border bg-ph-canvas p-2 text-sm text-ph-ink outline-none focus:ring-1 focus:ring-ph-brand"
1984
- }
1985
- ),
1986
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1987
- "button",
1988
- {
1989
- type: "button",
1990
- onClick: () => void ask(prompt),
1991
- disabled: loading || !prompt.trim(),
1992
- className: "self-end rounded-lg bg-ph-brand px-3 py-2 text-sm font-medium text-white disabled:opacity-50",
1993
- children: "Ask"
1994
- }
1995
- )
1996
- ] }) })
1997
- ] })
1998
- ] })
2749
+ ),
2750
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2751
+ "button",
2752
+ {
2753
+ type: "button",
2754
+ onClick: () => void ask(prompt),
2755
+ disabled: loading || !prompt.trim(),
2756
+ "data-test": "ask-ai-send",
2757
+ className: "self-end rounded-xl bg-ph-brand px-3.5 py-2 text-sm font-medium text-white disabled:opacity-50",
2758
+ children: "Ask"
2759
+ }
2760
+ )
2761
+ ] }) })
2762
+ ]
2763
+ }
2764
+ )
2765
+ ] }) : null
1999
2766
  ] });
2000
2767
  }
2001
2768
 
2769
+ // src/suite/lib/use-sidebar-width.ts
2770
+ var import_react9 = require("react");
2771
+ var SIDEBAR_RAIL_WIDTH = 56;
2772
+ var SIDEBAR_COLLAPSE_THRESHOLD = 80;
2773
+ var SIDEBAR_MIN_EXPANDED_WIDTH = 180;
2774
+ var SIDEBAR_MAX_WIDTH = 320;
2775
+ var SIDEBAR_DEFAULT_WIDTH = 320;
2776
+ function clampWidth(width) {
2777
+ return Math.min(SIDEBAR_MAX_WIDTH, Math.max(SIDEBAR_RAIL_WIDTH, width));
2778
+ }
2779
+ function snapWidth(width) {
2780
+ if (width <= SIDEBAR_COLLAPSE_THRESHOLD) return SIDEBAR_RAIL_WIDTH;
2781
+ if (width < SIDEBAR_MIN_EXPANDED_WIDTH) return SIDEBAR_MIN_EXPANDED_WIDTH;
2782
+ return Math.min(SIDEBAR_MAX_WIDTH, width);
2783
+ }
2784
+ function useSidebarWidth(storageKey) {
2785
+ const [width, setWidth] = (0, import_react9.useState)(SIDEBAR_DEFAULT_WIDTH);
2786
+ const [narrowViewport, setNarrowViewport] = (0, import_react9.useState)(false);
2787
+ const collapsed = narrowViewport || width <= SIDEBAR_COLLAPSE_THRESHOLD;
2788
+ (0, import_react9.useEffect)(() => {
2789
+ let stored = null;
2790
+ try {
2791
+ stored = localStorage.getItem(storageKey);
2792
+ } catch (e) {
2793
+ }
2794
+ if (!stored) return;
2795
+ const parsed = Number.parseInt(stored, 10);
2796
+ if (!Number.isNaN(parsed)) {
2797
+ queueMicrotask(() => setWidth(snapWidth(clampWidth(parsed))));
2798
+ }
2799
+ }, [storageKey]);
2800
+ (0, import_react9.useEffect)(() => {
2801
+ const media = window.matchMedia("(max-width: 639px)");
2802
+ const sync = () => setNarrowViewport(media.matches);
2803
+ sync();
2804
+ media.addEventListener("change", sync);
2805
+ return () => media.removeEventListener("change", sync);
2806
+ }, []);
2807
+ const persist = (0, import_react9.useCallback)(
2808
+ (value) => {
2809
+ try {
2810
+ localStorage.setItem(storageKey, String(value));
2811
+ } catch (e) {
2812
+ }
2813
+ },
2814
+ [storageKey]
2815
+ );
2816
+ const setCollapsed = (0, import_react9.useCallback)(
2817
+ (value) => {
2818
+ setWidth((current) => {
2819
+ const next = value ? SIDEBAR_RAIL_WIDTH : current <= SIDEBAR_COLLAPSE_THRESHOLD ? SIDEBAR_DEFAULT_WIDTH : snapWidth(current);
2820
+ persist(next);
2821
+ return next;
2822
+ });
2823
+ },
2824
+ [persist]
2825
+ );
2826
+ const resetWidth = (0, import_react9.useCallback)(() => {
2827
+ setWidth(SIDEBAR_DEFAULT_WIDTH);
2828
+ persist(SIDEBAR_DEFAULT_WIDTH);
2829
+ }, [persist]);
2830
+ const startResize = (0, import_react9.useCallback)(
2831
+ (event) => {
2832
+ event.preventDefault();
2833
+ const startX = event.clientX;
2834
+ const startWidth = width;
2835
+ const onMove = (moveEvent) => {
2836
+ setWidth(clampWidth(startWidth + moveEvent.clientX - startX));
2837
+ };
2838
+ const onUp = () => {
2839
+ document.removeEventListener("pointermove", onMove);
2840
+ document.removeEventListener("pointerup", onUp);
2841
+ setWidth((current) => {
2842
+ const snapped = snapWidth(current);
2843
+ persist(snapped);
2844
+ return snapped;
2845
+ });
2846
+ };
2847
+ document.addEventListener("pointermove", onMove);
2848
+ document.addEventListener("pointerup", onUp);
2849
+ },
2850
+ [persist, width]
2851
+ );
2852
+ return { width, collapsed, setCollapsed, resetWidth, startResize };
2853
+ }
2854
+
2002
2855
  // src/suite/lib/motion.tsx
2003
- var import_react8 = require("motion/react");
2004
- var import_jsx_runtime18 = require("react/jsx-runtime");
2856
+ var import_react10 = require("motion/react");
2857
+ var import_jsx_runtime20 = require("react/jsx-runtime");
2005
2858
  function SuiteMotionProvider({ children }) {
2006
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react8.LazyMotion, { features: import_react8.domAnimation, strict: true, children });
2859
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react10.LazyMotion, { features: import_react10.domAnimation, strict: true, children });
2007
2860
  }
2008
2861
  var SUITE_SPRINGS = {
2009
2862
  /** Snappy panel/sheet entrances — small overshoot. */
@@ -2014,217 +2867,25 @@ var SUITE_SPRINGS = {
2014
2867
  press: { type: "spring", stiffness: 600, damping: 35, mass: 0.5 }
2015
2868
  };
2016
2869
 
2017
- // src/suite/components/suite-layout.tsx
2018
- var import_jsx_runtime19 = require("react/jsx-runtime");
2019
- var WIDTHS = {
2020
- full: "max-w-none",
2021
- wide: "max-w-7xl",
2022
- content: "max-w-6xl",
2023
- narrow: "max-w-4xl"
2024
- };
2025
- function SuiteBreadcrumbs({ items, className }) {
2026
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("nav", { "aria-label": "Breadcrumb", className, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ol", { className: "flex min-w-0 items-center gap-1.5", children: items.map((item, index) => {
2027
- const isLast = index === items.length - 1;
2028
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("li", { className: "flex min-w-0 items-center gap-1.5", children: [
2029
- index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "select-none text-ph-border", "aria-hidden": "true", children: "/" }) : null,
2030
- item.href && !isLast ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2031
- "a",
2032
- {
2033
- href: item.href,
2034
- className: "truncate transition hover:text-ph-ink",
2035
- children: item.label
2036
- }
2037
- ) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2038
- "span",
2039
- {
2040
- className: cn(
2041
- "truncate",
2042
- isLast ? "font-medium text-ph-ink" : "text-ph-subtle"
2043
- ),
2044
- "aria-current": isLast ? "page" : void 0,
2045
- children: item.label
2046
- }
2047
- )
2048
- ] }, index);
2049
- }) }) });
2050
- }
2051
- function SuitePage({
2052
- children,
2053
- width = "wide",
2054
- inset = true,
2055
- className,
2056
- as: Component = "div",
2057
- dataTest,
2058
- id,
2059
- role,
2060
- "aria-live": ariaLive
2061
- }) {
2062
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2063
- Component,
2064
- {
2065
- id,
2066
- "data-test": dataTest,
2067
- role,
2068
- "aria-live": ariaLive,
2069
- className: cn(
2070
- "mx-auto flex w-full min-w-0 flex-col",
2071
- WIDTHS[width],
2072
- // Tight inset keeps page titles aligned without excessive whitespace.
2073
- inset && "px-4 pb-5 pt-2 sm:px-6 sm:pb-6 sm:pt-3 lg:px-8",
2074
- className
2075
- ),
2076
- children
2077
- }
2078
- );
2079
- }
2080
- function SuitePageHeader({
2081
- title,
2082
- description,
2083
- eyebrow,
2084
- actions,
2085
- className,
2086
- titleClassName,
2087
- dataTest,
2088
- /** Optional icon rendered before the title. */
2089
- icon,
2090
- /** Use the animated sun/moon icon from the Today page. */
2091
- todayIcon,
2092
- todayIconClassName,
2093
- /** Small badge/pill next to the title (e.g. "Beta", org name). */
2094
- badge,
2095
- /** Breadcrumb line rendered above the title. */
2096
- breadcrumb
2097
- }) {
2098
- const resolvedIcon = todayIcon ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2099
- TodayTimeIcon,
2100
- {
2101
- className: cn(
2102
- "h-6 w-6 sm:h-7 sm:w-7 text-amber-500",
2103
- todayIconClassName
2104
- )
2105
- }
2106
- ) : icon;
2107
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2108
- "header",
2109
- {
2110
- "data-test": dataTest,
2111
- className: cn(
2112
- "flex items-start justify-between gap-4 pb-2 sm:pb-3",
2113
- className
2114
- ),
2115
- children: [
2116
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex min-w-0 items-start gap-3", children: [
2117
- resolvedIcon ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2118
- "div",
2119
- {
2120
- className: "hidden shrink-0 pt-1 sm:block",
2121
- "aria-hidden": "true",
2122
- children: resolvedIcon
2123
- }
2124
- ) : null,
2125
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex min-w-0 flex-col gap-1", children: [
2126
- eyebrow ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "mb-0.5 flex min-w-0 items-center gap-1.5 text-xs text-ph-subtle", children: eyebrow }) : null,
2127
- breadcrumb ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "mb-0.5 flex min-w-0 items-center gap-1.5 text-xs text-ph-subtle", children: breadcrumb }) : null,
2128
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
2129
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2130
- "h1",
2131
- {
2132
- className: cn(
2133
- "font-display text-[1.625rem] font-bold leading-tight tracking-tight text-ph-ink sm:text-[2.25rem]",
2134
- titleClassName
2135
- ),
2136
- children: title
2137
- }
2138
- ),
2139
- badge ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "rounded-md bg-ph-muted px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-ph-subtle", children: badge }) : null
2140
- ] }),
2141
- description ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "mt-1 max-w-2xl text-pretty text-sm leading-5 text-ph-subtle", children: description }) : null
2142
- ] })
2143
- ] }),
2144
- actions ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "flex shrink-0 items-center gap-2 pt-1", children: actions }) : null
2145
- ]
2146
- }
2147
- );
2148
- }
2149
- function SuiteToolbar({
2150
- children,
2151
- className,
2152
- label = "Page tools",
2153
- dataTest
2154
- }) {
2155
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2156
- "div",
2157
- {
2158
- role: "toolbar",
2159
- "aria-label": label,
2160
- "data-test": dataTest,
2161
- className: cn("flex min-w-0 flex-wrap items-center gap-2", className),
2162
- children
2163
- }
2164
- );
2165
- }
2166
- function SuiteTabList({
2167
- children,
2168
- className,
2169
- label,
2170
- dataTest
2171
- }) {
2172
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2173
- "nav",
2174
- {
2175
- "aria-label": label,
2176
- "data-test": dataTest,
2177
- className: cn(
2178
- "flex min-w-0 flex-wrap gap-2 [&_a]:min-h-[48px] [&_button]:min-h-[48px] sm:[&_a]:min-h-[44px] sm:[&_button]:min-h-[44px]",
2179
- className
2180
- ),
2181
- children
2182
- }
2183
- );
2184
- }
2185
- function SuiteSectionHeader({
2186
- title,
2187
- description,
2188
- action,
2189
- className
2190
- }) {
2191
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2192
- "div",
2193
- {
2194
- className: cn(
2195
- "flex min-w-0 flex-col gap-4 sm:flex-row sm:items-end sm:justify-between",
2196
- className
2197
- ),
2198
- children: [
2199
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "min-w-0", children: [
2200
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h2", { className: "font-display text-balance text-lg font-semibold tracking-tight text-ph-ink sm:text-xl", children: title }),
2201
- description ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "mt-1 max-w-2xl text-pretty text-sm leading-5 text-ph-subtle", children: description }) : null
2202
- ] }),
2203
- action ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "flex min-h-[44px] shrink-0 items-center", children: action }) : null
2204
- ]
2205
- }
2206
- );
2207
- }
2208
-
2209
2870
  // src/suite/components/suite-skeleton.tsx
2210
- var import_jsx_runtime20 = require("react/jsx-runtime");
2871
+ var import_jsx_runtime21 = require("react/jsx-runtime");
2211
2872
  function SuiteSkeleton({
2212
2873
  className,
2213
2874
  lines = 1,
2214
2875
  circle,
2215
2876
  width
2216
2877
  }) {
2217
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2878
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2218
2879
  "div",
2219
2880
  {
2220
2881
  className: cn("space-y-2", circle ? "flex flex-col items-center" : ""),
2221
2882
  "aria-hidden": true,
2222
- children: Array.from({ length: lines }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2883
+ children: Array.from({ length: lines }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2223
2884
  "div",
2224
2885
  {
2225
2886
  className: "animate-[skeleton-stagger_0.6s_ease-out_both]",
2226
2887
  style: { animationDelay: `${index * 80}ms` },
2227
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2888
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2228
2889
  "div",
2229
2890
  {
2230
2891
  className: cn(
@@ -2248,35 +2909,35 @@ function SuiteSkeletonCard({
2248
2909
  rows = 3,
2249
2910
  header = true
2250
2911
  }) {
2251
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
2912
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2252
2913
  "div",
2253
2914
  {
2254
2915
  className: cn(
2255
- "ph-skeleton-card animate-[skeleton-stagger_0.5s_ease-out_both] rounded-2xl border border-ph-border/70 bg-ph-surface p-6 shadow-sm",
2916
+ "ph-skeleton-card animate-[skeleton-stagger_0.5s_ease-out_both] rounded-2xl border border-ph-border bg-ph-surface p-6 shadow-sm",
2256
2917
  className
2257
2918
  ),
2258
2919
  "aria-hidden": true,
2259
2920
  children: [
2260
- header ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
2921
+ header ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2261
2922
  "div",
2262
2923
  {
2263
2924
  className: "mb-5 flex items-center gap-3 animate-[skeleton-stagger_0.5s_ease-out_both]",
2264
2925
  style: { animationDelay: "100ms" },
2265
2926
  children: [
2266
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "ph-skeleton h-10 w-10 rounded-xl" }),
2267
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex-1 space-y-2", children: [
2268
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "ph-skeleton h-4 w-1/3" }),
2269
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "ph-skeleton h-3 w-1/2" })
2927
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "ph-skeleton h-10 w-10 rounded-xl" }),
2928
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex-1 space-y-2", children: [
2929
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "ph-skeleton h-4 w-1/3" }),
2930
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "ph-skeleton h-3 w-1/2" })
2270
2931
  ] })
2271
2932
  ]
2272
2933
  }
2273
2934
  ) : null,
2274
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "space-y-3", children: Array.from({ length: rows }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2935
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "space-y-3", children: Array.from({ length: rows }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2275
2936
  "div",
2276
2937
  {
2277
2938
  className: "animate-[skeleton-stagger_0.5s_ease-out_both]",
2278
2939
  style: { animationDelay: `${200 + index * 80}ms` },
2279
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2940
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2280
2941
  "div",
2281
2942
  {
2282
2943
  className: "ph-skeleton h-3",
@@ -2294,16 +2955,16 @@ function SuiteSkeletonList({
2294
2955
  className,
2295
2956
  items = 4
2296
2957
  }) {
2297
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: cn("space-y-3", className), "aria-hidden": true, children: Array.from({ length: items }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2958
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: cn("space-y-3", className), "aria-hidden": true, children: Array.from({ length: items }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2298
2959
  "div",
2299
2960
  {
2300
2961
  className: "animate-[skeleton-stagger_0.6s_ease-out_both]",
2301
2962
  style: { animationDelay: `${index * 100}ms` },
2302
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-3 rounded-xl border border-ph-border/60 bg-ph-surface p-4", children: [
2303
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "ph-skeleton h-10 w-10 rounded-lg" }),
2304
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "min-w-0 flex-1 space-y-2", children: [
2305
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "ph-skeleton h-3.5 w-3/4" }),
2306
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "ph-skeleton h-3 w-1/2" })
2963
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-3 rounded-xl border border-ph-border bg-ph-surface p-4", children: [
2964
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "ph-skeleton h-10 w-10 rounded-lg" }),
2965
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "min-w-0 flex-1 space-y-2", children: [
2966
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "ph-skeleton h-3.5 w-3/4" }),
2967
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "ph-skeleton h-3 w-1/2" })
2307
2968
  ] })
2308
2969
  ] })
2309
2970
  },
@@ -2318,22 +2979,22 @@ function SuiteEmptyState({
2318
2979
  className,
2319
2980
  size = "md"
2320
2981
  }) {
2321
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
2982
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2322
2983
  "div",
2323
2984
  {
2324
2985
  className: cn(
2325
- "relative overflow-hidden rounded-2xl border border-ph-border/80 bg-ph-surface text-center",
2986
+ "relative overflow-hidden rounded-2xl border border-ph-border bg-ph-surface text-center",
2326
2987
  size === "sm" ? "px-5 py-8" : "px-6 py-12 sm:px-10 sm:py-16",
2327
2988
  className
2328
2989
  ),
2329
2990
  children: [
2330
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "pointer-events-none absolute inset-0 overflow-hidden opacity-60", "aria-hidden": true, children: [
2331
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "absolute -left-10 -top-10 h-40 w-40 rounded-full bg-ph-brand/10 blur-3xl" }),
2332
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "absolute -bottom-10 -right-10 h-40 w-40 rounded-full bg-ph-accent/10 blur-3xl" })
2991
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "pointer-events-none absolute inset-0 overflow-hidden opacity-60", "aria-hidden": true, children: [
2992
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "absolute -left-10 -top-10 h-40 w-40 rounded-full bg-ph-brand/10 blur-3xl" }),
2993
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "absolute -bottom-10 -right-10 h-40 w-40 rounded-full bg-ph-accent/10 blur-3xl" })
2333
2994
  ] }),
2334
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "relative", children: [
2335
- icon ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "mx-auto mb-4 inline-flex items-center justify-center rounded-2xl bg-ph-muted p-3 shadow-sm ring-1 ring-black/[0.06]", children: icon }) : null,
2336
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2995
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "relative", children: [
2996
+ icon ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "mx-auto mb-4 inline-flex items-center justify-center rounded-2xl bg-ph-muted p-3 shadow-sm ring-1 ring-black/[0.06]", children: icon }) : null,
2997
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2337
2998
  "h3",
2338
2999
  {
2339
3000
  className: cn(
@@ -2343,8 +3004,8 @@ function SuiteEmptyState({
2343
3004
  children: title
2344
3005
  }
2345
3006
  ),
2346
- description ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "mx-auto mt-2 max-w-sm text-sm leading-relaxed text-ph-subtle", children: description }) : null,
2347
- action ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "mt-5 flex flex-wrap items-center justify-center gap-2", children: action }) : null
3007
+ description ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "mx-auto mt-2 max-w-sm text-sm leading-relaxed text-ph-subtle", children: description }) : null,
3008
+ action ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "mt-5 flex flex-wrap items-center justify-center gap-2", children: action }) : null
2348
3009
  ] })
2349
3010
  ]
2350
3011
  }
@@ -2352,24 +3013,26 @@ function SuiteEmptyState({
2352
3013
  }
2353
3014
 
2354
3015
  // src/suite/components/suite-settings-mobile-nav.tsx
2355
- var import_react9 = require("react");
2356
- var import_jsx_runtime21 = require("react/jsx-runtime");
3016
+ var import_react11 = require("react");
3017
+ var import_jsx_runtime22 = require("react/jsx-runtime");
2357
3018
  function SuiteSettingsMobileNav({
2358
3019
  items,
2359
3020
  activeHref,
2360
3021
  onSelect,
2361
- dataTest
3022
+ dataTest,
3023
+ ariaLabel = "Settings sections",
3024
+ className
2362
3025
  }) {
2363
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3026
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2364
3027
  "nav",
2365
3028
  {
2366
3029
  "data-test": dataTest,
2367
- "aria-label": "Settings sections",
2368
- className: "-mx-4 px-4 lg:hidden",
2369
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex snap-x gap-2 overflow-x-auto pb-3 pt-1 scrollbar-hide [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: items.map((item) => {
3030
+ "aria-label": ariaLabel,
3031
+ className: cn("suite-settings-mobile-nav lg:hidden", className),
3032
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex snap-x gap-2 overflow-x-auto pb-3 pt-1 scrollbar-hide [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: items.map((item) => {
2370
3033
  const Icon = item.icon;
2371
3034
  const active = activeHref === item.href || activeHref.startsWith(`${item.href}/`);
2372
- const iconNode = (0, import_react9.isValidElement)(item.icon) ? item.icon : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3035
+ const iconNode = (0, import_react11.isValidElement)(item.icon) ? item.icon : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2373
3036
  Icon,
2374
3037
  {
2375
3038
  className: cn(
@@ -2379,7 +3042,7 @@ function SuiteSettingsMobileNav({
2379
3042
  "aria-hidden": true
2380
3043
  }
2381
3044
  );
2382
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
3045
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2383
3046
  "button",
2384
3047
  {
2385
3048
  type: "button",
@@ -2387,14 +3050,14 @@ function SuiteSettingsMobileNav({
2387
3050
  onClick: () => onSelect(item.href),
2388
3051
  "aria-current": active ? "page" : void 0,
2389
3052
  className: cn(
2390
- "group relative flex shrink-0 snap-start items-center gap-2 rounded-full px-3.5 py-2.5 text-sm font-medium transition",
3053
+ "group relative flex shrink-0 snap-start items-center gap-2 rounded-full px-3.5 py-2.5 text-sm font-medium transition-colors",
2391
3054
  "min-h-[44px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
2392
3055
  active ? "bg-ph-ink text-ph-surface shadow-sm" : "bg-ph-surface text-ph-subtle ring-1 ring-ph-border hover:bg-ph-muted hover:text-ph-ink"
2393
3056
  ),
2394
3057
  children: [
2395
3058
  iconNode,
2396
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "whitespace-nowrap", children: item.label }),
2397
- active ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "absolute inset-x-0 -bottom-3 h-0.5 rounded-full bg-ph-ink lg:hidden" }) : null
3059
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "whitespace-nowrap", children: item.label }),
3060
+ active ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "absolute inset-x-0 -bottom-3 h-0.5 rounded-full bg-ph-ink lg:hidden" }) : null
2398
3061
  ]
2399
3062
  },
2400
3063
  item.href
@@ -2408,10 +3071,10 @@ function SuiteSettingsMobileNav({
2408
3071
  var import_link2 = __toESM(require("next/link"));
2409
3072
 
2410
3073
  // src/components/ui/Tooltip.tsx
2411
- var import_react10 = require("react");
3074
+ var import_react12 = require("react");
2412
3075
  var import_react_dom = require("react-dom");
2413
- var import_jsx_runtime22 = require("react/jsx-runtime");
2414
- var TooltipDelayContext = (0, import_react10.createContext)(0);
3076
+ var import_jsx_runtime23 = require("react/jsx-runtime");
3077
+ var TooltipDelayContext = (0, import_react12.createContext)(0);
2415
3078
  function tooltipPosition(trigger, side, align, offset) {
2416
3079
  const horizontal = align === "start" ? trigger.left : align === "end" ? trigger.right : trigger.left + trigger.width / 2;
2417
3080
  const vertical = align === "start" ? trigger.top : align === "end" ? trigger.bottom : trigger.top + trigger.height / 2;
@@ -2451,12 +3114,12 @@ function Tooltip({
2451
3114
  onOpenChange,
2452
3115
  delayDuration
2453
3116
  }) {
2454
- const providerDelay = (0, import_react10.useContext)(TooltipDelayContext);
2455
- const generatedId = (0, import_react10.useId)().replace(/:/g, "");
2456
- const triggerRef = (0, import_react10.useRef)(null);
2457
- const timerRef = (0, import_react10.useRef)(null);
2458
- const [internalOpen, setInternalOpen] = (0, import_react10.useState)(defaultOpen);
2459
- const [position, setPosition] = (0, import_react10.useState)(null);
3117
+ const providerDelay = (0, import_react12.useContext)(TooltipDelayContext);
3118
+ const generatedId = (0, import_react12.useId)().replace(/:/g, "");
3119
+ const triggerRef = (0, import_react12.useRef)(null);
3120
+ const timerRef = (0, import_react12.useRef)(null);
3121
+ const [internalOpen, setInternalOpen] = (0, import_react12.useState)(defaultOpen);
3122
+ const [position, setPosition] = (0, import_react12.useState)(null);
2460
3123
  const isOpen = open != null ? open : internalOpen;
2461
3124
  const triggerId = children.props.id;
2462
3125
  const tooltipId = triggerId ? `${triggerId}-tooltip` : `ph-tooltip-${generatedId}`;
@@ -2490,7 +3153,7 @@ function Tooltip({
2490
3153
  clearTimer();
2491
3154
  setOpen(false);
2492
3155
  }
2493
- (0, import_react10.useEffect)(() => {
3156
+ (0, import_react12.useEffect)(() => {
2494
3157
  if (!isOpen) return;
2495
3158
  const reposition = () => updatePosition();
2496
3159
  window.addEventListener("resize", reposition);
@@ -2500,11 +3163,11 @@ function Tooltip({
2500
3163
  window.removeEventListener("scroll", reposition, true);
2501
3164
  };
2502
3165
  });
2503
- (0, import_react10.useEffect)(() => () => clearTimer(), []);
3166
+ (0, import_react12.useEffect)(() => () => clearTimer(), []);
2504
3167
  const describedBy = [children.props["aria-describedby"], tooltipId].filter(Boolean).join(" ");
2505
- const trigger = (0, import_react10.cloneElement)(children, { "aria-describedby": describedBy });
2506
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
2507
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3168
+ const trigger = (0, import_react12.cloneElement)(children, { "aria-describedby": describedBy });
3169
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
3170
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2508
3171
  "span",
2509
3172
  {
2510
3173
  ref: triggerRef,
@@ -2520,12 +3183,12 @@ function Tooltip({
2520
3183
  }
2521
3184
  ),
2522
3185
  isOpen && position && typeof document !== "undefined" ? (0, import_react_dom.createPortal)(
2523
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3186
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2524
3187
  "span",
2525
3188
  {
2526
3189
  style: __spreadValues({ position: "fixed" }, position),
2527
3190
  className: "pointer-events-none z-[200]",
2528
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3191
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2529
3192
  "span",
2530
3193
  {
2531
3194
  id: tooltipId,
@@ -2543,7 +3206,7 @@ function Tooltip({
2543
3206
  }
2544
3207
 
2545
3208
  // src/suite/components/suite-sidebar.tsx
2546
- var import_jsx_runtime23 = require("react/jsx-runtime");
3209
+ var import_jsx_runtime24 = require("react/jsx-runtime");
2547
3210
  function renderNode(node, collapsed) {
2548
3211
  if (typeof node === "function") return node(collapsed);
2549
3212
  return node;
@@ -2560,7 +3223,7 @@ function SuiteSidebar({
2560
3223
  className,
2561
3224
  surface = false
2562
3225
  }) {
2563
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3226
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
2564
3227
  "div",
2565
3228
  {
2566
3229
  className: cn(
@@ -2569,7 +3232,7 @@ function SuiteSidebar({
2569
3232
  className
2570
3233
  ),
2571
3234
  children: [
2572
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3235
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
2573
3236
  "div",
2574
3237
  {
2575
3238
  className: cn(
@@ -2578,12 +3241,12 @@ function SuiteSidebar({
2578
3241
  collapsed ? "flex-col items-center gap-1 px-1 py-2" : "h-14 items-center justify-between gap-2 px-3"
2579
3242
  ),
2580
3243
  children: [
2581
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: cn("min-w-0", collapsed ? "shrink-0" : "flex-1"), children: renderNode(appSwitcher, collapsed) }),
2582
- collapsed ? null : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "shrink-0", children: renderNode(notificationBell, collapsed) })
3244
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn("min-w-0", collapsed ? "shrink-0" : "flex-1"), children: renderNode(appSwitcher, collapsed) }),
3245
+ collapsed ? null : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "shrink-0", children: renderNode(notificationBell, collapsed) })
2583
3246
  ]
2584
3247
  }
2585
3248
  ),
2586
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3249
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
2587
3250
  "div",
2588
3251
  {
2589
3252
  className: cn(
@@ -2592,17 +3255,17 @@ function SuiteSidebar({
2592
3255
  collapsed ? "px-1.5 py-2" : "p-2"
2593
3256
  ),
2594
3257
  children: [
2595
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3258
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
2596
3259
  "nav",
2597
3260
  {
2598
3261
  "aria-label": "Pages",
2599
3262
  className: "shrink-0 space-y-0.5",
2600
3263
  children: [
2601
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: cn("mb-3", collapsed && "flex justify-center"), children: renderNode(contextSwitcher, collapsed) }),
2602
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "space-y-0.5", children: navItems.map((item) => {
3264
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn("mb-3", collapsed && "flex justify-center"), children: renderNode(contextSwitcher, collapsed) }),
3265
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "space-y-0.5", children: navItems.map((item) => {
2603
3266
  const Icon = item.icon;
2604
3267
  const active = Boolean(item.active);
2605
- const link = /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3268
+ const link = /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
2606
3269
  import_link2.default,
2607
3270
  {
2608
3271
  href: item.href,
@@ -2615,7 +3278,7 @@ function SuiteSidebar({
2615
3278
  collapsed ? "mx-auto size-10 shrink-0 justify-center gap-0 px-0 py-0" : "w-full px-2.5 py-2"
2616
3279
  ),
2617
3280
  children: [
2618
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3281
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2619
3282
  "span",
2620
3283
  {
2621
3284
  className: cn(
@@ -2623,7 +3286,7 @@ function SuiteSidebar({
2623
3286
  item.iconClassName
2624
3287
  ),
2625
3288
  "aria-hidden": true,
2626
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3289
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2627
3290
  Icon,
2628
3291
  {
2629
3292
  className: "h-full w-full",
@@ -2632,7 +3295,7 @@ function SuiteSidebar({
2632
3295
  )
2633
3296
  }
2634
3297
  ),
2635
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3298
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2636
3299
  "span",
2637
3300
  {
2638
3301
  className: cn(
@@ -2643,17 +3306,17 @@ function SuiteSidebar({
2643
3306
  children: item.label
2644
3307
  }
2645
3308
  ),
2646
- !collapsed && item.badge ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "relative ml-auto shrink-0", children: item.badge }) : null
3309
+ !collapsed && item.badge ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "relative ml-auto shrink-0", children: item.badge }) : null
2647
3310
  ]
2648
3311
  },
2649
3312
  item.label
2650
3313
  );
2651
- return collapsed ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Tooltip, { content: item.label, side: "right", children: link }, item.label) : link;
3314
+ return collapsed ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Tooltip, { content: item.label, side: "right", children: link }, item.label) : link;
2652
3315
  }) })
2653
3316
  ]
2654
3317
  }
2655
3318
  ),
2656
- secondaryNav ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3319
+ secondaryNav ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2657
3320
  "div",
2658
3321
  {
2659
3322
  className: "min-h-0 flex-1",
@@ -2664,7 +3327,7 @@ function SuiteSidebar({
2664
3327
  ]
2665
3328
  }
2666
3329
  ),
2667
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3330
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2668
3331
  "div",
2669
3332
  {
2670
3333
  className: cn(
@@ -2672,7 +3335,7 @@ function SuiteSidebar({
2672
3335
  surface ? "bg-ph-surface" : "bg-ph-canvas",
2673
3336
  collapsed ? "p-1.5" : "p-3"
2674
3337
  ),
2675
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3338
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
2676
3339
  "div",
2677
3340
  {
2678
3341
  className: cn(
@@ -2681,7 +3344,7 @@ function SuiteSidebar({
2681
3344
  ),
2682
3345
  children: [
2683
3346
  renderNode(userMenu, collapsed),
2684
- !collapsed && footerExtras ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "ml-auto shrink-0", children: renderNode(footerExtras, collapsed) }) : null
3347
+ !collapsed && footerExtras ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "ml-auto shrink-0", children: renderNode(footerExtras, collapsed) }) : null
2685
3348
  ]
2686
3349
  }
2687
3350
  )
@@ -2693,7 +3356,7 @@ function SuiteSidebar({
2693
3356
  }
2694
3357
 
2695
3358
  // src/suite/components/suite-app-layout.tsx
2696
- var import_jsx_runtime24 = require("react/jsx-runtime");
3359
+ var import_jsx_runtime25 = require("react/jsx-runtime");
2697
3360
  function SuiteAppLayout({
2698
3361
  sidebar,
2699
3362
  children,
@@ -2701,41 +3364,59 @@ function SuiteAppLayout({
2701
3364
  bottomNav,
2702
3365
  sidebarWidth = 240,
2703
3366
  collapsed = false,
3367
+ lockMainScroll = false,
2704
3368
  onStartResize,
2705
3369
  onResetWidth,
2706
3370
  className
2707
3371
  }) {
2708
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { "data-test": "app-shell", className: cn("min-h-screen", className), children: [
2709
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "hidden lg:flex h-screen overflow-hidden", children: [
2710
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
2711
- "aside",
2712
- {
2713
- className: "relative flex shrink-0 flex-col border-r border-ph-border transition-[width] duration-200 ease-out",
2714
- style: { width: `${sidebarWidth}px` },
2715
- children: [
2716
- sidebar,
2717
- onStartResize ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2718
- "div",
2719
- {
2720
- role: "separator",
2721
- "aria-orientation": "vertical",
2722
- "aria-label": "Resize sidebar",
2723
- onPointerDown: onStartResize,
2724
- onDoubleClick: onResetWidth,
2725
- className: "absolute inset-y-0 right-0 z-10 w-1 cursor-col-resize transition-colors hover:bg-[color-mix(in_oklab,var(--ph-accent)_30%,transparent)]"
2726
- }
2727
- ) : null
2728
- ]
2729
- }
2730
- ),
2731
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("main", { className: "min-w-0 flex-1 overflow-y-auto bg-ph-canvas", children })
2732
- ] }),
2733
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex min-h-screen flex-col lg:hidden", children: [
2734
- mobileHeader ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "shrink-0", children: mobileHeader }) : null,
2735
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("main", { className: "min-w-0 flex-1 overflow-y-auto bg-ph-canvas", children }),
2736
- bottomNav ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "shrink-0", children: bottomNav }) : null
2737
- ] })
2738
- ] });
3372
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
3373
+ "div",
3374
+ {
3375
+ "data-test": "app-shell",
3376
+ className: cn("flex min-h-dvh lg:h-screen lg:overflow-hidden", className),
3377
+ children: [
3378
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
3379
+ "aside",
3380
+ {
3381
+ className: "relative hidden shrink-0 flex-col overflow-hidden border-r border-ph-border transition-[width] duration-200 ease-out lg:flex",
3382
+ style: { width: `${sidebarWidth}px` },
3383
+ "data-collapsed": collapsed ? "true" : "false",
3384
+ children: [
3385
+ sidebar,
3386
+ onStartResize ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3387
+ "div",
3388
+ {
3389
+ role: "separator",
3390
+ "aria-orientation": "vertical",
3391
+ "aria-label": "Resize sidebar",
3392
+ onPointerDown: onStartResize,
3393
+ onDoubleClick: onResetWidth,
3394
+ className: "absolute inset-y-0 right-0 z-10 w-1 cursor-col-resize transition-colors hover:bg-[color-mix(in_oklab,var(--ph-accent)_30%,transparent)]"
3395
+ }
3396
+ ) : null
3397
+ ]
3398
+ }
3399
+ ),
3400
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: [
3401
+ mobileHeader ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "shrink-0 lg:hidden", children: mobileHeader }) : null,
3402
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3403
+ "main",
3404
+ {
3405
+ id: "main-content",
3406
+ tabIndex: -1,
3407
+ "data-lock-scroll": lockMainScroll ? "true" : void 0,
3408
+ className: cn(
3409
+ "flex min-h-0 min-w-0 flex-1 flex-col overflow-x-hidden overscroll-contain bg-ph-canvas focus:outline-none",
3410
+ lockMainScroll ? "overflow-hidden" : "overflow-y-auto"
3411
+ ),
3412
+ children
3413
+ }
3414
+ ),
3415
+ bottomNav ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "shrink-0 lg:hidden", children: bottomNav }) : null
3416
+ ] })
3417
+ ]
3418
+ }
3419
+ );
2739
3420
  }
2740
3421
  // Annotate the CommonJS export names for ESM import in node:
2741
3422
  0 && (module.exports = {
@@ -2748,10 +3429,16 @@ function SuiteAppLayout({
2748
3429
  AppSwitcherMark,
2749
3430
  CommandPaletteHost,
2750
3431
  DeferredChrome,
3432
+ SIDEBAR_COLLAPSE_THRESHOLD,
3433
+ SIDEBAR_DEFAULT_WIDTH,
3434
+ SIDEBAR_MAX_WIDTH,
3435
+ SIDEBAR_MIN_EXPANDED_WIDTH,
3436
+ SIDEBAR_RAIL_WIDTH,
2751
3437
  SUITE_APPS,
2752
3438
  SUITE_APP_MAP,
2753
3439
  SUITE_GLYPHS,
2754
3440
  SUITE_ICON_NAMES,
3441
+ SUITE_OPEN_ASK_AI_EVENT,
2755
3442
  SUITE_SPRINGS,
2756
3443
  SourceAppGlyph,
2757
3444
  SuiteAiPanel,
@@ -2769,6 +3456,7 @@ function SuiteAppLayout({
2769
3456
  SuitePage,
2770
3457
  SuitePageHeader,
2771
3458
  SuiteSectionHeader,
3459
+ SuiteSettingsLayout,
2772
3460
  SuiteSettingsMobileNav,
2773
3461
  SuiteSidebar,
2774
3462
  SuiteSkeleton,
@@ -2778,11 +3466,15 @@ function SuiteAppLayout({
2778
3466
  SuiteThemeProvider,
2779
3467
  SuiteToolbar,
2780
3468
  SuiteUserMenu,
3469
+ TodayAskAiCard,
2781
3470
  TodayCalibrating,
3471
+ TodayEmptyAction,
2782
3472
  TodayEmptyState,
2783
3473
  TodayHero,
3474
+ TodayLayout,
2784
3475
  TodayPageFrame,
2785
3476
  TodayPanel,
3477
+ TodayPrimaryAction,
2786
3478
  TodaySection,
2787
3479
  TodayTimeIcon,
2788
3480
  TodayTopBar,
@@ -2792,9 +3484,12 @@ function SuiteAppLayout({
2792
3484
  cn,
2793
3485
  getTodayGreeting,
2794
3486
  m,
3487
+ openSuiteAskAi,
3488
+ resolveSuiteNotificationHref,
2795
3489
  sourceToSuiteApp,
2796
3490
  suiteAppBaseUrl,
2797
3491
  suiteAppLabel,
2798
3492
  useIdleMount,
3493
+ useSidebarWidth,
2799
3494
  useSuiteTheme
2800
3495
  });