@xenide-io/the-old-ui-theme 0.4.3 → 0.4.6

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 (75) 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 +1184 -661
  11. package/dist/suite.mjs +1123 -601
  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.tsx +269 -103
  20. package/src/suite/components/suite-app-layout.tsx +48 -28
  21. package/src/suite/components/suite-layout.tsx +85 -41
  22. package/src/suite/components/suite-mobile-drawer.tsx +35 -22
  23. package/src/suite/components/suite-notification-bell.tsx +9 -3
  24. package/src/suite/components/suite-settings-layout.tsx +82 -0
  25. package/src/suite/components/suite-settings-mobile-nav.tsx +18 -17
  26. package/src/suite/components/suite-skeleton.tsx +3 -3
  27. package/src/suite/components/today-calibrating.tsx +11 -35
  28. package/src/suite/components/today-page-frame.tsx +19 -11
  29. package/src/suite/components/today-ui.tsx +276 -19
  30. package/src/suite/index.ts +41 -25
  31. package/src/suite/lib/apps.ts +32 -2
  32. package/src/suite/lib/use-sidebar-width.ts +114 -0
  33. package/src/components/sections/AccordionShowcase.tsx +0 -45
  34. package/src/components/sections/AlertShowcase.tsx +0 -40
  35. package/src/components/sections/AvatarShowcase.tsx +0 -80
  36. package/src/components/sections/BadgeShowcase.tsx +0 -65
  37. package/src/components/sections/ButtonShowcase.tsx +0 -308
  38. package/src/components/sections/CalendarShowcase.tsx +0 -35
  39. package/src/components/sections/CardShowcase.tsx +0 -159
  40. package/src/components/sections/CodeMockupShowcase.tsx +0 -56
  41. package/src/components/sections/ColorPalette.tsx +0 -117
  42. package/src/components/sections/CommandPaletteShowcase.tsx +0 -48
  43. package/src/components/sections/CountdownShowcase.tsx +0 -43
  44. package/src/components/sections/DiffShowcase.tsx +0 -70
  45. package/src/components/sections/DrawerShowcase.tsx +0 -97
  46. package/src/components/sections/DropdownShowcase.tsx +0 -98
  47. package/src/components/sections/EmptyStateShowcase.tsx +0 -50
  48. package/src/components/sections/FilterChipsShowcase.tsx +0 -98
  49. package/src/components/sections/FormShowcase.tsx +0 -127
  50. package/src/components/sections/HoverCardShowcase.tsx +0 -50
  51. package/src/components/sections/IconShowcase.tsx +0 -121
  52. package/src/components/sections/IndicatorShowcase.tsx +0 -52
  53. package/src/components/sections/KbdShowcase.tsx +0 -57
  54. package/src/components/sections/ModalShowcase.tsx +0 -211
  55. package/src/components/sections/NavigationShowcase.tsx +0 -199
  56. package/src/components/sections/NewComponentsShowcase.tsx +0 -1052
  57. package/src/components/sections/ProductLayoutsShowcase.tsx +0 -78
  58. package/src/components/sections/ProgressShowcase.tsx +0 -82
  59. package/src/components/sections/QuillChartShowcase.tsx +0 -92
  60. package/src/components/sections/QuillDashboardShowcase.tsx +0 -149
  61. package/src/components/sections/SegmentedControlShowcase.tsx +0 -49
  62. package/src/components/sections/SetupThemeShowcase.tsx +0 -262
  63. package/src/components/sections/SidebarShowcase.tsx +0 -152
  64. package/src/components/sections/StackShowcase.tsx +0 -33
  65. package/src/components/sections/StepperShowcase.tsx +0 -37
  66. package/src/components/sections/SuiteShowcase.tsx +0 -669
  67. package/src/components/sections/SwapShowcase.tsx +0 -99
  68. package/src/components/sections/TabShowcase.tsx +0 -84
  69. package/src/components/sections/TableShowcase.tsx +0 -142
  70. package/src/components/sections/TimelineShowcase.tsx +0 -83
  71. package/src/components/sections/ToastShowcase.tsx +0 -108
  72. package/src/components/sections/TooltipShowcase.tsx +0 -38
  73. package/src/components/sections/UtilityShowcase.tsx +0 -81
  74. package/src/styles/excel-themes.css +0 -110
  75. package/src/styles/lemon-primitives.css +0 -550
package/dist/suite.mjs CHANGED
@@ -15,24 +15,32 @@ function TodayPageFrame({ children }) {
15
15
  "div",
16
16
  {
17
17
  "data-test": "today-page",
18
- className: "today-page-frame relative flex min-h-0 flex-1 flex-col overflow-y-auto overflow-x-hidden",
18
+ className: "today-page-frame relative flex min-h-0 w-full flex-1 flex-col overflow-x-hidden",
19
19
  children: [
20
20
  /* @__PURE__ */ jsxs(
21
21
  "div",
22
22
  {
23
- className: "pointer-events-none absolute inset-x-0 top-0 h-[34rem] overflow-hidden",
23
+ className: "pointer-events-none absolute inset-0 overflow-hidden",
24
24
  "aria-hidden": true,
25
25
  children: [
26
26
  /* @__PURE__ */ jsx("div", { className: "today-page-frame__glow absolute inset-0" }),
27
- /* @__PURE__ */ 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" }),
28
- /* @__PURE__ */ jsx("div", { className: "today-page-frame__top-fade absolute inset-x-0 top-0 h-64" }),
29
- /* @__PURE__ */ jsx("div", { className: "today-page-frame__cloud today-page-frame__cloud--1 absolute -left-24 top-28 h-80 w-[26rem] rounded-[45%]" }),
30
- /* @__PURE__ */ jsx("div", { className: "today-page-frame__cloud today-page-frame__cloud--2 absolute -right-20 top-40 h-72 w-80 rounded-[40%]" }),
31
- /* @__PURE__ */ jsx("div", { className: "today-page-frame__cloud today-page-frame__cloud--3 absolute left-[28%] top-[58%] h-64 w-[22rem] rounded-[50%]" })
27
+ /* @__PURE__ */ jsx("div", { className: "today-page-frame__top-fade absolute inset-x-0 top-0 h-[28rem]" }),
28
+ /* @__PURE__ */ jsx("div", { className: "today-page-frame__cloud today-page-frame__cloud--1 absolute -left-28 top-16 h-96 w-[30rem] rounded-[45%]" }),
29
+ /* @__PURE__ */ jsx("div", { className: "today-page-frame__cloud today-page-frame__cloud--2 absolute -right-24 top-24 h-80 w-[22rem] rounded-[40%]" }),
30
+ /* @__PURE__ */ jsx("div", { className: "today-page-frame__cloud today-page-frame__cloud--3 absolute left-[22%] top-48 h-72 w-[24rem] rounded-[50%]" }),
31
+ /* @__PURE__ */ jsx("div", { className: "today-page-frame__cloud today-page-frame__cloud--4 absolute right-[18%] top-[42%] h-64 w-[20rem] rounded-[48%]" }),
32
+ /* @__PURE__ */ jsx("div", { className: "today-page-frame__cloud today-page-frame__cloud--5 absolute left-[8%] top-[68%] h-56 w-[18rem] rounded-[42%]" })
32
33
  ]
33
34
  }
34
35
  ),
35
- /* @__PURE__ */ jsx("div", { className: "relative flex-1 px-4 pb-10 pt-5 sm:px-6 sm:pt-6 lg:px-8 [&_.list-row]:min-h-[52px] [&_.list-row]:sm:min-h-[44px]", children })
36
+ /* @__PURE__ */ jsx(
37
+ "div",
38
+ {
39
+ "data-width": "full",
40
+ 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]",
41
+ children
42
+ }
43
+ )
36
44
  ]
37
45
  }
38
46
  );
@@ -392,7 +400,7 @@ function SuiteIcon({
392
400
  }
393
401
 
394
402
  // src/suite/components/today-ui.tsx
395
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
403
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
396
404
  var APP_META = {
397
405
  shellstack: { name: "ShellStack" },
398
406
  tides: { name: "Tides" },
@@ -485,15 +493,15 @@ function AnimatedSun({ className }) {
485
493
  /* @__PURE__ */ jsx5("stop", { stopColor: "#f59e0b" }),
486
494
  /* @__PURE__ */ jsx5("stop", { offset: "1", stopColor: "#fb923c" })
487
495
  ] }) }),
488
- /* @__PURE__ */ jsx5("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__ */ jsx5(
496
+ /* @__PURE__ */ jsx5("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__ */ jsx5(
489
497
  "line",
490
498
  {
491
499
  x1: "20",
492
- y1: "4",
500
+ y1: "5",
493
501
  x2: "20",
494
502
  y2: "9",
495
503
  stroke: "#fbbf24",
496
- strokeWidth: "2",
504
+ strokeWidth: "1.75",
497
505
  strokeLinecap: "round",
498
506
  transform: `rotate(${angle} 20 20)`
499
507
  },
@@ -527,10 +535,9 @@ function AnimatedMoon({ className }) {
527
535
  fill: "url(#animatedMoonBody)"
528
536
  }
529
537
  ),
530
- /* @__PURE__ */ jsxs5("g", { className: "motion-safe:animate-[pulse_4s_ease-in-out_infinite] motion-reduce:animate-none", children: [
531
- /* @__PURE__ */ jsx5("circle", { cx: "29", cy: "10", r: "1.4", fill: "#c7d2fe" }),
532
- /* @__PURE__ */ jsx5("circle", { cx: "34", cy: "16", r: "1", fill: "#c7d2fe" }),
533
- /* @__PURE__ */ jsx5("circle", { cx: "25", cy: "5.5", r: "0.9", fill: "#c7d2fe" })
538
+ /* @__PURE__ */ jsxs5("g", { className: "opacity-70 motion-safe:animate-[pulse_10s_ease-in-out_infinite] motion-reduce:animate-none", children: [
539
+ /* @__PURE__ */ jsx5("circle", { cx: "29", cy: "10", r: "1.2", fill: "#c7d2fe" }),
540
+ /* @__PURE__ */ jsx5("circle", { cx: "34", cy: "16", r: "0.9", fill: "#c7d2fe" })
534
541
  ] })
535
542
  ]
536
543
  }
@@ -592,30 +599,193 @@ function getTodayGreeting(user, stats) {
592
599
  const overdueCount = (_d = stats == null ? void 0 : stats.overdueCount) != null ? _d : 0;
593
600
  const hours = (_e = stats == null ? void 0 : stats.hours) != null ? _e : 0;
594
601
  if (overdueCount > 0) {
595
- return `${greeting}. You have ${taskCount} task${taskCount === 1 ? "" : "s"} due today, ${overdueCount} overdue.`;
602
+ return `${greeting}. ${taskCount} task${taskCount === 1 ? "" : "s"} due today, ${overdueCount} overdue.`;
596
603
  }
597
604
  if (taskCount > 0) {
598
- return `${greeting}. You have ${taskCount} task${taskCount === 1 ? "" : "s"} due today.`;
605
+ return `${greeting}. ${taskCount} task${taskCount === 1 ? "" : "s"} due today.`;
599
606
  }
600
607
  if (hours > 0) {
601
- return `${greeting}. You logged ${hours} hour${hours === 1 ? "" : "s"} today.`;
608
+ return `${greeting}. ${hours} hour${hours === 1 ? "" : "s"} logged today.`;
602
609
  }
603
610
  if (period === "evening" || period === "night") {
604
- return `${greeting}. Time to wrap up.`;
611
+ return `${greeting}. Nice work \u2014 time to wrap up.`;
605
612
  }
606
- return `${greeting}. Nothing overdue \u2014 take a nice, deep breath.`;
613
+ return `${greeting}. Clear queue \u2014 take a nice, deep breath.`;
607
614
  }
608
615
  function TodayHero({
609
- message = "Take a nice, deep breath."
616
+ message = "Take a nice, deep breath.",
617
+ brief,
618
+ briefMeta
619
+ }) {
620
+ return /* @__PURE__ */ jsxs5("div", { className: "mt-4 sm:mt-6", children: [
621
+ /* @__PURE__ */ jsx5(
622
+ "p",
623
+ {
624
+ className: "today-hero max-w-2xl font-display text-lg font-semibold tracking-tight text-ph-ink sm:text-xl sm:leading-snug",
625
+ children: message
626
+ },
627
+ typeof message === "string" ? message : "hero"
628
+ ),
629
+ brief ? /* @__PURE__ */ jsxs5("div", { className: "mt-4 max-w-2xl", "data-test": "today-focus-brief", children: [
630
+ /* @__PURE__ */ jsx5("p", { className: "text-[15px] leading-[1.65] text-ph-ink", children: brief }),
631
+ briefMeta ? /* @__PURE__ */ jsx5("p", { className: "mt-1.5 text-xs text-ph-mutedtext", children: briefMeta }) : null
632
+ ] }) : null
633
+ ] });
634
+ }
635
+ function TodayPrimaryAction({
636
+ label,
637
+ description,
638
+ icon,
639
+ accent = "neutral",
640
+ onClick,
641
+ href,
642
+ loading = false,
643
+ active = false,
644
+ className,
645
+ dataTest = "today-primary-action"
610
646
  }) {
611
- return /* @__PURE__ */ jsx5("div", { className: "mt-8 text-center sm:mt-10", children: /* @__PURE__ */ jsx5(
612
- "p",
647
+ const classes = cn(
648
+ "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",
649
+ `today-primary-action--${accent}`,
650
+ active && "today-primary-action--active",
651
+ loading && "pointer-events-none opacity-70",
652
+ className
653
+ );
654
+ const body = /* @__PURE__ */ jsxs5(Fragment2, { children: [
655
+ /* @__PURE__ */ jsx5("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 }),
656
+ /* @__PURE__ */ jsxs5("span", { className: "min-w-0 flex-1", children: [
657
+ /* @__PURE__ */ jsx5("span", { className: "block text-sm font-semibold tracking-tight sm:text-base", children: label }),
658
+ description ? /* @__PURE__ */ jsx5("span", { className: "mt-0.5 block text-xs opacity-80 sm:text-sm", children: description }) : null
659
+ ] }),
660
+ /* @__PURE__ */ jsx5(
661
+ "span",
662
+ {
663
+ className: "today-primary-action__chevron shrink-0 text-base opacity-60 transition group-hover:translate-x-0.5 group-hover:opacity-100",
664
+ "aria-hidden": true,
665
+ children: "\u2192"
666
+ }
667
+ )
668
+ ] });
669
+ if (href && !onClick) {
670
+ return /* @__PURE__ */ jsx5(
671
+ "a",
672
+ {
673
+ href,
674
+ className: classes,
675
+ "data-test": dataTest,
676
+ "aria-busy": loading,
677
+ children: body
678
+ }
679
+ );
680
+ }
681
+ return /* @__PURE__ */ jsx5(
682
+ "button",
613
683
  {
614
- className: "today-hero font-display text-2xl font-bold tracking-tight text-ph-ink sm:text-[1.75rem] sm:leading-tight",
615
- children: message
616
- },
617
- typeof message === "string" ? message : "hero"
618
- ) });
684
+ type: "button",
685
+ onClick,
686
+ disabled: loading,
687
+ className: classes,
688
+ "data-test": dataTest,
689
+ "aria-busy": loading,
690
+ children: body
691
+ }
692
+ );
693
+ }
694
+ function TodayEmptyAction({
695
+ title,
696
+ description,
697
+ actionLabel,
698
+ href,
699
+ onClick,
700
+ dataTest
701
+ }) {
702
+ return /* @__PURE__ */ jsxs5(
703
+ "div",
704
+ {
705
+ className: "rounded-lg border border-dashed border-ph-border/80 bg-ph-muted/30 px-4 py-4 text-center",
706
+ "data-test": dataTest,
707
+ children: [
708
+ /* @__PURE__ */ jsx5("p", { className: "text-sm font-semibold text-ph-ink", children: title }),
709
+ description ? /* @__PURE__ */ jsx5("p", { className: "mx-auto mt-1 max-w-sm text-xs leading-relaxed text-ph-subtle sm:text-sm", children: description }) : null,
710
+ actionLabel && (href || onClick) ? href ? /* @__PURE__ */ jsx5(
711
+ "a",
712
+ {
713
+ href,
714
+ className: "mt-2.5 inline-flex text-sm font-semibold text-ph-brand hover:underline",
715
+ children: actionLabel
716
+ }
717
+ ) : /* @__PURE__ */ jsx5(
718
+ "button",
719
+ {
720
+ type: "button",
721
+ onClick,
722
+ className: "mt-2.5 inline-flex text-sm font-semibold text-ph-brand hover:underline",
723
+ children: actionLabel
724
+ }
725
+ ) : null
726
+ ]
727
+ }
728
+ );
729
+ }
730
+ function TodayLayout({
731
+ primary,
732
+ main,
733
+ aside,
734
+ className
735
+ }) {
736
+ return /* @__PURE__ */ jsxs5(
737
+ "div",
738
+ {
739
+ className: cn("mt-5 space-y-6 sm:mt-6", className),
740
+ "data-test": "today-layout",
741
+ children: [
742
+ primary ? /* @__PURE__ */ jsx5("div", { className: "max-w-md", children: primary }) : null,
743
+ /* @__PURE__ */ jsxs5("div", { className: "grid gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(16rem,20rem)] lg:items-start lg:gap-8", children: [
744
+ /* @__PURE__ */ jsx5("div", { className: "min-w-0 space-y-8", children: main }),
745
+ aside ? /* @__PURE__ */ jsx5("aside", { className: "space-y-3 lg:sticky lg:top-6 lg:self-start", children: aside }) : null
746
+ ] })
747
+ ]
748
+ }
749
+ );
750
+ }
751
+ function TodayAskAiCard({
752
+ title = "Ask AI",
753
+ description = "Ask about your workspace, draft something, or look a fact up online.",
754
+ cta = "Ask",
755
+ className,
756
+ onClick,
757
+ href
758
+ }) {
759
+ const classes = cn(
760
+ "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",
761
+ className
762
+ );
763
+ const body = /* @__PURE__ */ jsxs5(Fragment2, { children: [
764
+ /* @__PURE__ */ jsx5("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__ */ jsx5(SuiteIcon, { name: "stack-hex", accent: "shellstack", className: "h-5 w-5" }) }),
765
+ /* @__PURE__ */ jsxs5("span", { className: "min-w-0 flex-1", children: [
766
+ /* @__PURE__ */ jsxs5("span", { className: "flex flex-wrap items-baseline justify-between gap-x-3 gap-y-1", children: [
767
+ /* @__PURE__ */ jsx5("span", { className: "font-display text-sm font-bold tracking-tight text-ph-ink", children: title }),
768
+ /* @__PURE__ */ jsx5("span", { className: "text-sm font-medium text-ph-brand transition group-hover:underline", children: cta })
769
+ ] }),
770
+ /* @__PURE__ */ jsx5("span", { className: "mt-1 block text-sm leading-relaxed text-ph-subtle", children: description })
771
+ ] })
772
+ ] });
773
+ if (onClick) {
774
+ return /* @__PURE__ */ jsx5(
775
+ "button",
776
+ {
777
+ type: "button",
778
+ onClick,
779
+ className: classes,
780
+ "data-test": "today-ask-ai",
781
+ children: body
782
+ }
783
+ );
784
+ }
785
+ if (href) {
786
+ return /* @__PURE__ */ jsx5("a", { href, className: classes, "data-test": "today-ask-ai", children: body });
787
+ }
788
+ return /* @__PURE__ */ jsx5("div", { className: classes, "data-test": "today-ask-ai", children: body });
619
789
  }
620
790
  function TodaySection({
621
791
  icon: Icon,
@@ -626,7 +796,7 @@ function TodaySection({
626
796
  testId,
627
797
  children
628
798
  }) {
629
- return /* @__PURE__ */ jsxs5("section", { "data-test": testId, className: "space-y-4", children: [
799
+ return /* @__PURE__ */ jsxs5("section", { "data-test": testId, className: "space-y-5", children: [
630
800
  /* @__PURE__ */ jsxs5("div", { className: "flex items-start justify-between gap-4", children: [
631
801
  /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 items-start gap-3.5", children: [
632
802
  /* @__PURE__ */ jsx5(
@@ -657,8 +827,7 @@ function TodayPanel({
657
827
  "div",
658
828
  {
659
829
  className: cn(
660
- "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",
661
- "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",
830
+ "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",
662
831
  className
663
832
  ),
664
833
  children
@@ -836,6 +1005,70 @@ function AppSwitcher({
836
1005
  import { useCallback, useEffect, useState as useState2 } from "react";
837
1006
  import { useRouter } from "next/navigation";
838
1007
  import { Bell } from "iconoir-react";
1008
+
1009
+ // src/suite/lib/apps.ts
1010
+ var SUITE_APPS2 = [
1011
+ {
1012
+ slug: "shellstack",
1013
+ name: "ShellStack",
1014
+ description: "Organisation and billing",
1015
+ icon: "/icon-shellstack.svg",
1016
+ landing: "/home",
1017
+ baseUrlEnv: "NEXT_PUBLIC_SHELLSTACK_URL",
1018
+ baseUrlFallback: "http://localhost:3002"
1019
+ },
1020
+ {
1021
+ slug: "tides",
1022
+ name: "Tides",
1023
+ description: "Projects and tasks",
1024
+ icon: "/tides-icon.svg",
1025
+ landing: "/today",
1026
+ baseUrlEnv: "NEXT_PUBLIC_TIDES_URL",
1027
+ baseUrlFallback: "http://localhost:3001"
1028
+ },
1029
+ {
1030
+ slug: "turtletime",
1031
+ name: "TurtleTime",
1032
+ description: "Time tracking",
1033
+ icon: "/turtletime-icon.svg",
1034
+ landing: "/today",
1035
+ baseUrlEnv: "NEXT_PUBLIC_TURTLETIME_URL",
1036
+ baseUrlFallback: "http://localhost:3000"
1037
+ },
1038
+ {
1039
+ slug: "kraken",
1040
+ name: "Kraken",
1041
+ description: "Documents and knowledge",
1042
+ icon: "/kraken-icon.svg",
1043
+ landing: "/today",
1044
+ baseUrlEnv: "NEXT_PUBLIC_KRAKEN_URL",
1045
+ baseUrlFallback: "http://localhost:3003"
1046
+ }
1047
+ ];
1048
+ var SUITE_APP_MAP = Object.fromEntries(SUITE_APPS2.map((app) => [app.slug, app]));
1049
+ function suiteAppBaseUrl(slug) {
1050
+ const def = SUITE_APP_MAP[slug];
1051
+ const fromEnv = typeof process !== "undefined" ? process.env[def.baseUrlEnv] : void 0;
1052
+ return (fromEnv || def.baseUrlFallback).replace(/\/$/, "");
1053
+ }
1054
+ var SOURCE_APP_SLUG = {
1055
+ turtletime: "turtletime",
1056
+ tides: "tides",
1057
+ kraken: "kraken",
1058
+ portal: "shellstack",
1059
+ shellstack: "shellstack"
1060
+ };
1061
+ function resolveSuiteNotificationHref(href, sourceApp) {
1062
+ const value = (href || "").trim();
1063
+ if (!value) return "";
1064
+ if (/^(https?:|mailto:|\/\/)/i.test(value)) return value;
1065
+ const path = value.startsWith("/") ? value : `/${value}`;
1066
+ const slug = SOURCE_APP_SLUG[(sourceApp || "").toLowerCase()];
1067
+ if (!slug) return path;
1068
+ return `${suiteAppBaseUrl(slug)}${path}`;
1069
+ }
1070
+
1071
+ // src/suite/components/suite-notification-bell.tsx
839
1072
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
840
1073
  var POLL_MS = 6e4;
841
1074
  function timeAgo(iso) {
@@ -926,9 +1159,13 @@ function SuiteNotificationBell({
926
1159
  } catch (e) {
927
1160
  }
928
1161
  }
929
- if (!n.href) return;
930
- if (/^https?:\/\//i.test(n.href)) window.location.assign(n.href);
931
- else router.push(n.href);
1162
+ const target = resolveSuiteNotificationHref(n.href, n.source_app);
1163
+ if (!target) return;
1164
+ if (/^https?:\/\//i.test(target)) {
1165
+ window.location.assign(target);
1166
+ return;
1167
+ }
1168
+ router.push(target);
932
1169
  },
933
1170
  [router, markRead]
934
1171
  );
@@ -1064,11 +1301,11 @@ function useIdleMount(timeoutMs = 1200) {
1064
1301
  }
1065
1302
 
1066
1303
  // src/suite/components/deferred-chrome.tsx
1067
- import { Fragment as Fragment2, jsx as jsx9 } from "react/jsx-runtime";
1304
+ import { Fragment as Fragment3, jsx as jsx9 } from "react/jsx-runtime";
1068
1305
  function DeferredChrome({ children }) {
1069
1306
  const ready = useIdleMount();
1070
1307
  if (!ready) return null;
1071
- return /* @__PURE__ */ jsx9(Fragment2, { children });
1308
+ return /* @__PURE__ */ jsx9(Fragment3, { children });
1072
1309
  }
1073
1310
 
1074
1311
  // src/suite/components/suite-mobile-drawer.tsx
@@ -1081,6 +1318,7 @@ function SuiteMobileDrawer({
1081
1318
  children,
1082
1319
  side = "left",
1083
1320
  showCloseButton = false,
1321
+ title,
1084
1322
  closeLabel = "Close navigation",
1085
1323
  backdropLabel = "Close navigation",
1086
1324
  dialogLabel = "Navigation",
@@ -1158,23 +1396,35 @@ function SuiteMobileDrawer({
1158
1396
  tabIndex: -1,
1159
1397
  "data-test": panelDataTest,
1160
1398
  className: cn(
1161
- "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",
1399
+ "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",
1162
1400
  side === "right" && "ml-auto",
1163
1401
  shown ? "translate-x-0" : side === "right" ? "translate-x-full" : "-translate-x-full",
1164
1402
  panelClassName
1165
1403
  ),
1166
1404
  style: { transitionDuration: `${durationMs}ms` },
1167
1405
  children: [
1168
- showCloseButton ? /* @__PURE__ */ jsx10("div", { className: "flex h-14 shrink-0 items-center justify-end border-b border-ph-border px-3", children: /* @__PURE__ */ jsx10(
1169
- "button",
1406
+ showCloseButton ? /* @__PURE__ */ jsxs8(
1407
+ "div",
1170
1408
  {
1171
- type: "button",
1172
- onClick: onClose,
1173
- 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",
1174
- "aria-label": closeLabel,
1175
- children: /* @__PURE__ */ jsx10(X, { className: "h-4 w-4", "aria-hidden": true })
1409
+ className: cn(
1410
+ "flex h-14 shrink-0 items-center border-b border-ph-border px-3",
1411
+ title ? "justify-between" : "justify-end"
1412
+ ),
1413
+ children: [
1414
+ title ? /* @__PURE__ */ jsx10("span", { className: "truncate text-sm font-semibold text-ph-ink", children: title }) : null,
1415
+ /* @__PURE__ */ jsx10(
1416
+ "button",
1417
+ {
1418
+ type: "button",
1419
+ onClick: onClose,
1420
+ 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",
1421
+ "aria-label": closeLabel,
1422
+ children: /* @__PURE__ */ jsx10(X, { className: "h-4 w-4", "aria-hidden": true })
1423
+ }
1424
+ )
1425
+ ]
1176
1426
  }
1177
- ) }) : null,
1427
+ ) : null,
1178
1428
  children
1179
1429
  ]
1180
1430
  }
@@ -1231,74 +1481,361 @@ function SuiteMobileHeader({
1231
1481
  );
1232
1482
  }
1233
1483
 
1234
- // src/suite/components/suite-user-menu.tsx
1235
- import Image2 from "next/image";
1236
- import { useRouter as useRouter2 } from "next/navigation";
1237
- import { LogOut, Settings } from "iconoir-react";
1484
+ // src/suite/components/suite-layout.tsx
1485
+ import {
1486
+ useEffect as useEffect5,
1487
+ useRef as useRef2,
1488
+ useState as useState6
1489
+ } from "react";
1238
1490
  import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1239
- function computeInitials(name, email) {
1240
- const source = (name || email || "?").trim();
1241
- return source.split(/[\s@]+/).filter(Boolean).slice(0, 2).map((part) => part[0]).join("").toUpperCase();
1491
+ function SuiteBreadcrumbs({ items, className }) {
1492
+ return /* @__PURE__ */ jsx12("nav", { "aria-label": "Breadcrumb", className, children: /* @__PURE__ */ jsx12("ol", { className: "flex min-w-0 items-center gap-1.5", children: items.map((item, index) => {
1493
+ const isLast = index === items.length - 1;
1494
+ return /* @__PURE__ */ jsxs10("li", { className: "flex min-w-0 items-center gap-1.5", children: [
1495
+ index > 0 ? /* @__PURE__ */ jsx12("span", { className: "select-none text-ph-border", "aria-hidden": "true", children: "/" }) : null,
1496
+ item.href && !isLast ? /* @__PURE__ */ jsx12(
1497
+ "a",
1498
+ {
1499
+ href: item.href,
1500
+ className: "truncate transition hover:text-ph-ink",
1501
+ children: item.label
1502
+ }
1503
+ ) : /* @__PURE__ */ jsx12(
1504
+ "span",
1505
+ {
1506
+ className: cn(
1507
+ "truncate",
1508
+ isLast ? "font-medium text-ph-ink" : "text-ph-subtle"
1509
+ ),
1510
+ "aria-current": isLast ? "page" : void 0,
1511
+ children: item.label
1512
+ }
1513
+ )
1514
+ ] }, index);
1515
+ }) }) });
1242
1516
  }
1243
- function SuiteUserMenu({
1244
- name,
1245
- email,
1246
- image,
1247
- settingsHref,
1248
- onSignOut,
1249
- fallbackInitials,
1250
- dataTest = "suite-user-menu",
1251
- className
1517
+ function SuitePage({
1518
+ children,
1519
+ width = "full",
1520
+ inset = true,
1521
+ className,
1522
+ as: Component = "div",
1523
+ dataTest,
1524
+ id,
1525
+ role,
1526
+ "aria-live": ariaLive
1252
1527
  }) {
1253
- const router = useRouter2();
1254
- const initials = fallbackInitials != null ? fallbackInitials : computeInitials(name, email);
1255
- const avatar = /* @__PURE__ */ jsx12("span", { className: "inline-flex h-11 w-11 items-center justify-center rounded-full transition-colors hover:bg-ph-muted", children: image ? /* @__PURE__ */ jsx12(
1256
- Image2,
1528
+ return /* @__PURE__ */ jsx12(
1529
+ Component,
1257
1530
  {
1258
- src: image,
1259
- alt: "",
1260
- width: 32,
1261
- height: 32,
1262
- className: "h-8 w-8 rounded-full object-cover",
1263
- unoptimized: true
1531
+ id,
1532
+ "data-test": dataTest,
1533
+ "data-width": width,
1534
+ role,
1535
+ "aria-live": ariaLive,
1536
+ className: cn(
1537
+ "suite-page-stage flex w-full min-w-0 flex-col",
1538
+ inset && "suite-page-inset",
1539
+ className
1540
+ ),
1541
+ children
1542
+ }
1543
+ );
1544
+ }
1545
+ function SuitePageHeader({
1546
+ title,
1547
+ description,
1548
+ eyebrow,
1549
+ actions,
1550
+ className,
1551
+ titleClassName,
1552
+ dataTest,
1553
+ /** Optional icon rendered before the title. */
1554
+ icon,
1555
+ /** Use the animated sun/moon icon from the Today page. */
1556
+ todayIcon,
1557
+ todayIconClassName,
1558
+ /** Small badge/pill next to the title (e.g. "Beta", org name). */
1559
+ badge,
1560
+ /** Breadcrumb line rendered above the title. */
1561
+ breadcrumb,
1562
+ /** Stick to the top of the scrollport (desktop page titles). */
1563
+ sticky = false
1564
+ }) {
1565
+ const headerRef = useRef2(null);
1566
+ const [stuck, setStuck] = useState6(false);
1567
+ useEffect5(() => {
1568
+ var _a;
1569
+ if (!sticky) return;
1570
+ const el = headerRef.current;
1571
+ if (!el) return;
1572
+ const sentinel = document.createElement("div");
1573
+ sentinel.setAttribute("aria-hidden", "true");
1574
+ sentinel.style.cssText = "position:relative;height:1px;width:100%;margin:0;padding:0;pointer-events:none";
1575
+ (_a = el.parentElement) == null ? void 0 : _a.insertBefore(sentinel, el);
1576
+ const observer = new IntersectionObserver(
1577
+ ([entry]) => {
1578
+ setStuck(!entry.isIntersecting);
1579
+ },
1580
+ { threshold: [1], root: null }
1581
+ );
1582
+ observer.observe(sentinel);
1583
+ return () => {
1584
+ observer.disconnect();
1585
+ sentinel.remove();
1586
+ };
1587
+ }, [sticky]);
1588
+ const resolvedIcon = todayIcon ? /* @__PURE__ */ jsx12(
1589
+ TodayTimeIcon,
1590
+ {
1591
+ className: cn(
1592
+ "h-7 w-7 text-amber-500 sm:h-8 sm:w-8",
1593
+ todayIconClassName
1594
+ )
1264
1595
  }
1265
- ) : /* @__PURE__ */ jsx12("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 }) });
1596
+ ) : icon;
1266
1597
  return /* @__PURE__ */ jsxs10(
1267
- DropdownMenu,
1598
+ "header",
1268
1599
  {
1269
- trigger: avatar,
1270
- "aria-label": "Account menu",
1271
- align: "end",
1272
- side: "bottom",
1273
- sideOffset: 6,
1274
- collisionPadding: 16,
1275
- modal: false,
1276
- className,
1600
+ ref: headerRef,
1277
1601
  "data-test": dataTest,
1602
+ "data-stuck": sticky ? stuck ? "true" : "false" : void 0,
1603
+ className: cn(
1604
+ "flex items-start justify-between gap-4 pb-2 sm:pb-3",
1605
+ // No extra pt here — page inset sets the shared title baseline.
1606
+ // Stuck state adds pad via .suite-page-header-sticky[data-stuck].
1607
+ sticky && "suite-page-header-sticky",
1608
+ className
1609
+ ),
1278
1610
  children: [
1279
- /* @__PURE__ */ jsxs10("div", { className: "px-2 py-1.5", children: [
1280
- /* @__PURE__ */ jsx12("p", { className: "truncate text-sm font-medium text-ph-ink", children: name || email || "Account" }),
1281
- email ? /* @__PURE__ */ jsx12("p", { className: "truncate text-xs text-ph-mutedtext", children: email }) : null
1282
- ] }),
1283
- /* @__PURE__ */ jsx12("div", { className: "my-1 border-t border-ph-border" }),
1284
- /* @__PURE__ */ jsxs10(
1285
- DropdownItem,
1286
- {
1287
- "data-test": `${dataTest}-settings`,
1288
- onClick: () => router.push(settingsHref),
1289
- children: [
1290
- /* @__PURE__ */ jsx12(Settings, { className: "h-4 w-4 text-ph-mutedtext", "aria-hidden": true }),
1611
+ /* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
1612
+ eyebrow ? /* @__PURE__ */ jsx12("div", { className: "mb-0.5 flex min-w-0 items-center gap-1.5 text-xs text-ph-subtle", children: eyebrow }) : null,
1613
+ breadcrumb ? /* @__PURE__ */ jsx12("div", { className: "mb-0.5 flex min-w-0 items-center gap-1.5 text-xs text-ph-subtle", children: breadcrumb }) : null,
1614
+ /* @__PURE__ */ jsxs10("div", { className: "flex min-w-0 items-center gap-3", children: [
1615
+ resolvedIcon ? /* @__PURE__ */ jsx12(
1616
+ "div",
1617
+ {
1618
+ className: "hidden shrink-0 sm:flex sm:items-center [&_svg]:h-7 [&_svg]:w-7 sm:[&_svg]:h-8 sm:[&_svg]:w-8",
1619
+ "aria-hidden": "true",
1620
+ children: resolvedIcon
1621
+ }
1622
+ ) : null,
1623
+ /* @__PURE__ */ jsxs10("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
1624
+ /* @__PURE__ */ jsx12(
1625
+ "h1",
1626
+ {
1627
+ className: cn(
1628
+ "font-display text-[1.625rem] font-bold leading-none tracking-tight text-ph-ink sm:text-[2.25rem]",
1629
+ titleClassName
1630
+ ),
1631
+ children: title
1632
+ }
1633
+ ),
1634
+ badge ? /* @__PURE__ */ jsx12("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
1635
+ ] })
1636
+ ] }),
1637
+ description ? /* @__PURE__ */ jsx12(
1638
+ "div",
1639
+ {
1640
+ className: cn(
1641
+ "mt-1.5 max-w-2xl text-pretty text-sm leading-5 text-ph-subtle",
1642
+ resolvedIcon && "sm:pl-11"
1643
+ ),
1644
+ children: description
1645
+ }
1646
+ ) : null
1647
+ ] }),
1648
+ actions ? /* @__PURE__ */ jsx12("div", { className: "flex shrink-0 items-center gap-2 self-center", children: actions }) : null
1649
+ ]
1650
+ }
1651
+ );
1652
+ }
1653
+ function SuiteToolbar({
1654
+ children,
1655
+ className,
1656
+ label = "Page tools",
1657
+ dataTest
1658
+ }) {
1659
+ return /* @__PURE__ */ jsx12(
1660
+ "div",
1661
+ {
1662
+ role: "toolbar",
1663
+ "aria-label": label,
1664
+ "data-test": dataTest,
1665
+ className: cn("flex min-w-0 flex-wrap items-center gap-2", className),
1666
+ children
1667
+ }
1668
+ );
1669
+ }
1670
+ function SuiteTabList({
1671
+ children,
1672
+ className,
1673
+ label,
1674
+ dataTest
1675
+ }) {
1676
+ return /* @__PURE__ */ jsx12(
1677
+ "nav",
1678
+ {
1679
+ "aria-label": label,
1680
+ "data-test": dataTest,
1681
+ className: cn(
1682
+ "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]",
1683
+ className
1684
+ ),
1685
+ children
1686
+ }
1687
+ );
1688
+ }
1689
+ function SuiteSectionHeader({
1690
+ title,
1691
+ description,
1692
+ action,
1693
+ className
1694
+ }) {
1695
+ return /* @__PURE__ */ jsxs10(
1696
+ "div",
1697
+ {
1698
+ className: cn(
1699
+ "flex min-w-0 flex-col gap-4 sm:flex-row sm:items-end sm:justify-between",
1700
+ className
1701
+ ),
1702
+ children: [
1703
+ /* @__PURE__ */ jsxs10("div", { className: "min-w-0", children: [
1704
+ /* @__PURE__ */ jsx12("h2", { className: "font-display text-balance text-lg font-semibold tracking-tight text-ph-ink sm:text-xl", children: title }),
1705
+ description ? /* @__PURE__ */ jsx12("div", { className: "mt-1 max-w-2xl text-pretty text-sm leading-5 text-ph-subtle", children: description }) : null
1706
+ ] }),
1707
+ action ? /* @__PURE__ */ jsx12("div", { className: "flex min-h-[44px] shrink-0 items-center", children: action }) : null
1708
+ ]
1709
+ }
1710
+ );
1711
+ }
1712
+
1713
+ // src/suite/components/suite-settings-layout.tsx
1714
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1715
+ function SuiteSettingsLayout({
1716
+ children,
1717
+ sidebar,
1718
+ mobileNav,
1719
+ loading,
1720
+ title = "Settings",
1721
+ description,
1722
+ header,
1723
+ dataTest = "settings-page",
1724
+ headerDataTest = "settings-page-header",
1725
+ className,
1726
+ contentClassName
1727
+ }) {
1728
+ return /* @__PURE__ */ jsxs11(
1729
+ SuitePage,
1730
+ {
1731
+ dataTest,
1732
+ className: cn("flex h-full flex-col overflow-hidden", className),
1733
+ children: [
1734
+ header != null ? header : /* @__PURE__ */ jsx13(
1735
+ SuitePageHeader,
1736
+ {
1737
+ sticky: true,
1738
+ dataTest: headerDataTest,
1739
+ title,
1740
+ description
1741
+ }
1742
+ ),
1743
+ loading != null ? loading : /* @__PURE__ */ jsxs11("div", { className: "suite-page-body mt-6 flex min-h-0 flex-1 flex-col", children: [
1744
+ mobileNav ? /* @__PURE__ */ jsx13("div", { className: "sticky top-0 z-20 mb-2 shrink-0 bg-ph-canvas/95 backdrop-blur lg:hidden", children: mobileNav }) : null,
1745
+ /* @__PURE__ */ jsxs11("div", { className: "flex min-h-0 flex-1 flex-col gap-6 lg:flex-row lg:gap-8", children: [
1746
+ /* @__PURE__ */ jsx13(
1747
+ "aside",
1748
+ {
1749
+ "aria-label": "Settings",
1750
+ className: "hidden w-56 shrink-0 bg-transparent lg:block lg:self-stretch lg:overflow-y-auto lg:overscroll-contain",
1751
+ children: sidebar
1752
+ }
1753
+ ),
1754
+ /* @__PURE__ */ jsx13(
1755
+ "div",
1756
+ {
1757
+ className: cn(
1758
+ "min-h-0 min-w-0 flex-1 overflow-y-auto overscroll-contain pb-6",
1759
+ contentClassName
1760
+ ),
1761
+ children
1762
+ }
1763
+ )
1764
+ ] })
1765
+ ] })
1766
+ ]
1767
+ }
1768
+ );
1769
+ }
1770
+
1771
+ // src/suite/components/suite-user-menu.tsx
1772
+ import Image2 from "next/image";
1773
+ import { useRouter as useRouter2 } from "next/navigation";
1774
+ import { LogOut, Settings } from "iconoir-react";
1775
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1776
+ function computeInitials(name, email) {
1777
+ const source = (name || email || "?").trim();
1778
+ return source.split(/[\s@]+/).filter(Boolean).slice(0, 2).map((part) => part[0]).join("").toUpperCase();
1779
+ }
1780
+ function SuiteUserMenu({
1781
+ name,
1782
+ email,
1783
+ image,
1784
+ settingsHref,
1785
+ onSignOut,
1786
+ fallbackInitials,
1787
+ dataTest = "suite-user-menu",
1788
+ className
1789
+ }) {
1790
+ const router = useRouter2();
1791
+ const initials = fallbackInitials != null ? fallbackInitials : computeInitials(name, email);
1792
+ const avatar = /* @__PURE__ */ jsx14("span", { className: "inline-flex h-11 w-11 items-center justify-center rounded-full transition-colors hover:bg-ph-muted", children: image ? /* @__PURE__ */ jsx14(
1793
+ Image2,
1794
+ {
1795
+ src: image,
1796
+ alt: "",
1797
+ width: 32,
1798
+ height: 32,
1799
+ className: "h-8 w-8 rounded-full object-cover",
1800
+ unoptimized: true
1801
+ }
1802
+ ) : /* @__PURE__ */ jsx14("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 }) });
1803
+ return /* @__PURE__ */ jsxs12(
1804
+ DropdownMenu,
1805
+ {
1806
+ trigger: avatar,
1807
+ "aria-label": "Account menu",
1808
+ align: "end",
1809
+ side: "bottom",
1810
+ sideOffset: 6,
1811
+ collisionPadding: 16,
1812
+ modal: false,
1813
+ className,
1814
+ "data-test": dataTest,
1815
+ children: [
1816
+ /* @__PURE__ */ jsxs12("div", { className: "px-2 py-1.5", children: [
1817
+ /* @__PURE__ */ jsx14("p", { className: "truncate text-sm font-medium text-ph-ink", children: name || email || "Account" }),
1818
+ email ? /* @__PURE__ */ jsx14("p", { className: "truncate text-xs text-ph-mutedtext", children: email }) : null
1819
+ ] }),
1820
+ /* @__PURE__ */ jsx14("div", { className: "my-1 border-t border-ph-border" }),
1821
+ /* @__PURE__ */ jsxs12(
1822
+ DropdownItem,
1823
+ {
1824
+ "data-test": `${dataTest}-settings`,
1825
+ onClick: () => router.push(settingsHref),
1826
+ children: [
1827
+ /* @__PURE__ */ jsx14(Settings, { className: "h-4 w-4 text-ph-mutedtext", "aria-hidden": true }),
1291
1828
  "Profile settings"
1292
1829
  ]
1293
1830
  }
1294
1831
  ),
1295
- /* @__PURE__ */ jsxs10(
1832
+ /* @__PURE__ */ jsxs12(
1296
1833
  DropdownItem,
1297
1834
  {
1298
1835
  "data-test": `${dataTest}-sign-out`,
1299
1836
  onClick: onSignOut,
1300
1837
  children: [
1301
- /* @__PURE__ */ jsx12(LogOut, { className: "h-4 w-4 text-ph-mutedtext", "aria-hidden": true }),
1838
+ /* @__PURE__ */ jsx14(LogOut, { className: "h-4 w-4 text-ph-mutedtext", "aria-hidden": true }),
1302
1839
  "Sign out"
1303
1840
  ]
1304
1841
  }
@@ -1310,7 +1847,7 @@ function SuiteUserMenu({
1310
1847
 
1311
1848
  // src/suite/components/suite-bottom-nav.tsx
1312
1849
  import Link from "next/link";
1313
- import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1850
+ import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1314
1851
  function SuiteBottomNav({
1315
1852
  items,
1316
1853
  ariaLabel = "Primary navigation",
@@ -1323,8 +1860,8 @@ function SuiteBottomNav({
1323
1860
  );
1324
1861
  }
1325
1862
  const activeIndex = items.findIndex((item) => item.active);
1326
- return /* @__PURE__ */ jsxs11(Fragment3, { children: [
1327
- /* @__PURE__ */ jsx13(
1863
+ return /* @__PURE__ */ jsxs13(Fragment4, { children: [
1864
+ /* @__PURE__ */ jsx15(
1328
1865
  "div",
1329
1866
  {
1330
1867
  "aria-hidden": true,
@@ -1332,7 +1869,7 @@ function SuiteBottomNav({
1332
1869
  style: { height: "calc(60px + env(safe-area-inset-bottom))" }
1333
1870
  }
1334
1871
  ),
1335
- /* @__PURE__ */ jsxs11(
1872
+ /* @__PURE__ */ jsxs13(
1336
1873
  "nav",
1337
1874
  {
1338
1875
  "aria-label": ariaLabel,
@@ -1347,12 +1884,12 @@ function SuiteBottomNav({
1347
1884
  "--suite-nav-active": activeIndex >= 0 ? activeIndex : 0
1348
1885
  },
1349
1886
  children: [
1350
- activeIndex >= 0 ? /* @__PURE__ */ jsx13("span", { "aria-hidden": true, className: "suite-bottom-nav__indicator" }) : null,
1887
+ activeIndex >= 0 ? /* @__PURE__ */ jsx15("span", { "aria-hidden": true, className: "suite-bottom-nav__indicator" }) : null,
1351
1888
  items.map((item) => {
1352
1889
  var _a;
1353
1890
  const Icon = item.icon;
1354
1891
  const active = Boolean(item.active);
1355
- return /* @__PURE__ */ jsxs11(
1892
+ return /* @__PURE__ */ jsxs13(
1356
1893
  Link,
1357
1894
  {
1358
1895
  href: item.href,
@@ -1363,14 +1900,14 @@ function SuiteBottomNav({
1363
1900
  active && "font-semibold text-ph-brand"
1364
1901
  ),
1365
1902
  children: [
1366
- /* @__PURE__ */ jsx13(
1903
+ /* @__PURE__ */ jsx15(
1367
1904
  "span",
1368
1905
  {
1369
1906
  className: cn(
1370
1907
  "suite-bottom-nav__icon relative z-10 inline-flex items-center justify-center",
1371
1908
  active && "suite-bottom-nav__icon--active"
1372
1909
  ),
1373
- children: /* @__PURE__ */ jsx13(
1910
+ children: /* @__PURE__ */ jsx15(
1374
1911
  Icon,
1375
1912
  {
1376
1913
  className: "h-6 w-6",
@@ -1380,7 +1917,7 @@ function SuiteBottomNav({
1380
1917
  )
1381
1918
  }
1382
1919
  ),
1383
- /* @__PURE__ */ jsx13("span", { className: "relative z-10 max-w-full truncate", children: item.label })
1920
+ /* @__PURE__ */ jsx15("span", { className: "relative z-10 max-w-full truncate", children: item.label })
1384
1921
  ]
1385
1922
  },
1386
1923
  item.href
@@ -1392,117 +1929,39 @@ function SuiteBottomNav({
1392
1929
  ] });
1393
1930
  }
1394
1931
 
1395
- // src/suite/lib/apps.ts
1396
- var SUITE_APPS2 = [
1397
- {
1398
- slug: "shellstack",
1399
- name: "ShellStack",
1400
- description: "Organisation and billing",
1401
- icon: "/icon-shellstack.svg",
1402
- landing: "/home",
1403
- baseUrlEnv: "NEXT_PUBLIC_SHELLSTACK_URL",
1404
- baseUrlFallback: "http://localhost:3002"
1405
- },
1406
- {
1407
- slug: "tides",
1408
- name: "Tides",
1409
- description: "Projects and tasks",
1410
- icon: "/tides-icon.svg",
1411
- landing: "/today",
1412
- baseUrlEnv: "NEXT_PUBLIC_TIDES_URL",
1413
- baseUrlFallback: "http://localhost:3001"
1414
- },
1415
- {
1416
- slug: "turtletime",
1417
- name: "TurtleTime",
1418
- description: "Time tracking",
1419
- icon: "/turtletime-icon.svg",
1420
- landing: "/tracker",
1421
- baseUrlEnv: "NEXT_PUBLIC_TURTLETIME_URL",
1422
- baseUrlFallback: "http://localhost:3000"
1423
- },
1424
- {
1425
- slug: "kraken",
1426
- name: "Kraken",
1427
- description: "Documents and knowledge",
1428
- icon: "/kraken-icon.svg",
1429
- landing: "/today",
1430
- baseUrlEnv: "NEXT_PUBLIC_KRAKEN_URL",
1431
- baseUrlFallback: "http://localhost:3003"
1432
- }
1433
- ];
1434
- var SUITE_APP_MAP = Object.fromEntries(SUITE_APPS2.map((app) => [app.slug, app]));
1435
- function suiteAppBaseUrl(slug) {
1436
- const def = SUITE_APP_MAP[slug];
1437
- const fromEnv = typeof process !== "undefined" ? process.env[def.baseUrlEnv] : void 0;
1438
- return (fromEnv || def.baseUrlFallback).replace(/\/$/, "");
1439
- }
1440
-
1441
1932
  // src/suite/components/today-calibrating.tsx
1442
1933
  import { Refresh as RefreshCw2, SunLight as Sun } from "iconoir-react";
1443
- import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1934
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
1444
1935
  function TodayCalibrating({
1445
1936
  onRetry,
1446
1937
  retrying = false,
1447
1938
  className,
1448
1939
  spinner: Spinner,
1449
- variant = "ring",
1940
+ variant: _variant = "ring",
1450
1941
  retrySpinnerClassName = "text-ph-brand"
1451
1942
  }) {
1452
- return /* @__PURE__ */ jsxs12(
1943
+ void _variant;
1944
+ return /* @__PURE__ */ jsxs14(
1453
1945
  "div",
1454
1946
  {
1455
1947
  className: cn(
1456
- "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",
1948
+ "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",
1457
1949
  className
1458
1950
  ),
1459
1951
  "data-test": "today-calibrating",
1460
1952
  children: [
1461
- /* @__PURE__ */ jsx14(
1953
+ /* @__PURE__ */ jsx16(
1462
1954
  "div",
1463
1955
  {
1464
- className: "pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_50%_35%,rgba(251,191,36,0.12),transparent_55%)]",
1956
+ className: "pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_50%_35%,rgba(251,191,36,0.1),transparent_55%)]",
1465
1957
  "aria-hidden": true
1466
1958
  }
1467
1959
  ),
1468
- /* @__PURE__ */ jsxs12("div", { className: "relative mb-6 flex h-24 w-24 items-center justify-center", children: [
1469
- variant === "ring" ? /* @__PURE__ */ jsxs12(Fragment4, { children: [
1470
- /* @__PURE__ */ jsx14(
1471
- Spinner,
1472
- {
1473
- size: "lg",
1474
- className: "absolute inset-0 m-auto opacity-40",
1475
- label: "Loading today"
1476
- }
1477
- ),
1478
- /* @__PURE__ */ jsx14(
1479
- "span",
1480
- {
1481
- className: "bg-ph-muted/30 absolute inset-2 animate-ping rounded-full",
1482
- "aria-hidden": true
1483
- }
1484
- )
1485
- ] }) : null,
1486
- /* @__PURE__ */ jsx14("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__ */ jsx14(
1487
- Sun,
1488
- {
1489
- className: cn("h-9 w-9", variant === "pulse" && "animate-pulse"),
1490
- strokeWidth: 2,
1491
- "aria-hidden": true
1492
- }
1493
- ) })
1494
- ] }),
1495
- /* @__PURE__ */ jsx14("h2", { className: "relative text-base font-semibold tracking-tight text-ph-ink sm:text-lg md:text-xl", children: "Calibrating your day" }),
1496
- /* @__PURE__ */ jsx14("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" }),
1497
- /* @__PURE__ */ jsx14("div", { className: "relative mt-5 flex gap-1.5", "aria-hidden": true, children: [0, 1, 2, 3, 4].map((i) => /* @__PURE__ */ jsx14(
1498
- "span",
1499
- {
1500
- className: "h-1.5 w-5 animate-pulse rounded-full bg-ph-muted",
1501
- style: { animationDelay: `${i * 120}ms` }
1502
- },
1503
- i
1504
- )) }),
1505
- onRetry ? /* @__PURE__ */ jsxs12(
1960
+ /* @__PURE__ */ jsx16("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__ */ jsx16(Sun, { className: "h-8 w-8", strokeWidth: 2, "aria-hidden": true }) }),
1961
+ /* @__PURE__ */ jsx16("h2", { className: "relative text-base font-semibold tracking-tight text-ph-ink sm:text-lg md:text-xl", children: "Calibrating your day" }),
1962
+ /* @__PURE__ */ jsx16("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" }),
1963
+ /* @__PURE__ */ jsx16("div", { className: "relative mt-5 flex justify-center", children: /* @__PURE__ */ jsx16(Spinner, { size: "sm", className: "opacity-70", label: "Loading today" }) }),
1964
+ onRetry ? /* @__PURE__ */ jsxs14(
1506
1965
  "button",
1507
1966
  {
1508
1967
  type: "button",
@@ -1510,7 +1969,7 @@ function TodayCalibrating({
1510
1969
  disabled: retrying,
1511
1970
  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",
1512
1971
  children: [
1513
- retrying ? /* @__PURE__ */ jsx14(Spinner, { size: "sm", className: retrySpinnerClassName }) : /* @__PURE__ */ jsx14(RefreshCw2, { className: "h-4 w-4", "aria-hidden": true }),
1972
+ retrying ? /* @__PURE__ */ jsx16(Spinner, { size: "sm", className: retrySpinnerClassName }) : /* @__PURE__ */ jsx16(RefreshCw2, { className: "h-4 w-4", "aria-hidden": true }),
1514
1973
  "Try again"
1515
1974
  ]
1516
1975
  }
@@ -1525,11 +1984,11 @@ import {
1525
1984
  createContext,
1526
1985
  useCallback as useCallback2,
1527
1986
  useContext,
1528
- useEffect as useEffect5,
1987
+ useEffect as useEffect6,
1529
1988
  useMemo,
1530
- useState as useState6
1989
+ useState as useState7
1531
1990
  } from "react";
1532
- import { jsx as jsx15 } from "react/jsx-runtime";
1991
+ import { jsx as jsx17 } from "react/jsx-runtime";
1533
1992
  var SuiteThemeContext = createContext(null);
1534
1993
  function systemTheme(fallback) {
1535
1994
  if (typeof window === "undefined") return fallback;
@@ -1567,7 +2026,7 @@ function SuiteThemeProvider({
1567
2026
  config,
1568
2027
  children
1569
2028
  }) {
1570
- const [state, setState] = useState6(() => {
2029
+ const [state, setState] = useState7(() => {
1571
2030
  const theme2 = readStoredTheme(config.storageKey);
1572
2031
  if (typeof document === "undefined") {
1573
2032
  return { theme: theme2, resolvedTheme: config.fallbackTheme };
@@ -1577,7 +2036,7 @@ function SuiteThemeProvider({
1577
2036
  return { theme: theme2, resolvedTheme: resolvedTheme2 };
1578
2037
  });
1579
2038
  const { theme, resolvedTheme } = state;
1580
- useEffect5(() => {
2039
+ useEffect6(() => {
1581
2040
  if (theme !== "system") return;
1582
2041
  const media = window.matchMedia("(prefers-color-scheme: dark)");
1583
2042
  const update = () => {
@@ -1607,7 +2066,7 @@ function SuiteThemeProvider({
1607
2066
  () => ({ theme, resolvedTheme, setTheme, toggleTheme }),
1608
2067
  [theme, resolvedTheme, setTheme, toggleTheme]
1609
2068
  );
1610
- return /* @__PURE__ */ jsx15(SuiteThemeContext.Provider, { value, children });
2069
+ return /* @__PURE__ */ jsx17(SuiteThemeContext.Provider, { value, children });
1611
2070
  }
1612
2071
  function useSuiteTheme() {
1613
2072
  const ctx = useContext(SuiteThemeContext);
@@ -1616,39 +2075,111 @@ function useSuiteTheme() {
1616
2075
  }
1617
2076
 
1618
2077
  // src/suite/components/ai-panel.tsx
1619
- import { useState as useState7 } from "react";
1620
- import { Check as Check2, Copy, Xmark as X2 } from "iconoir-react";
1621
- import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
2078
+ import {
2079
+ useCallback as useCallback3,
2080
+ useEffect as useEffect7,
2081
+ useRef as useRef3,
2082
+ useState as useState8
2083
+ } from "react";
2084
+ import { Check as Check2, Copy, Sparks, Xmark as X2 } from "iconoir-react";
2085
+ import { Fragment as Fragment5, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
2086
+ var SUITE_OPEN_ASK_AI_EVENT = "shellstack:open-ask-ai";
2087
+ function openSuiteAskAi(detail) {
2088
+ if (typeof window === "undefined") return;
2089
+ window.dispatchEvent(
2090
+ new CustomEvent(SUITE_OPEN_ASK_AI_EVENT, {
2091
+ detail: detail != null ? detail : {}
2092
+ })
2093
+ );
2094
+ }
1622
2095
  function SuiteAiPanel({
1623
- presets,
1624
- emptyState,
1625
- fetchStatus,
1626
- complete,
2096
+ presets = [],
2097
+ emptyState = "Ask anything about your workspace, or look something up online.",
2098
+ title = "Ask AI",
2099
+ fetchChat,
2100
+ sendMessage,
2101
+ clearChat,
1627
2102
  brandIcon: BrandIcon,
1628
- spinner: Spinner
2103
+ spinner: Spinner,
2104
+ hideLauncher = false
1629
2105
  }) {
1630
- const [open, setOpen] = useState7(false);
1631
- const [prompt, setPrompt] = useState7("");
1632
- const [loading, setLoading] = useState7(false);
1633
- const [answer, setAnswer] = useState7("");
1634
- const [error, setError] = useState7("");
1635
- const [copied, setCopied] = useState7(false);
2106
+ const [open, setOpen] = useState8(false);
2107
+ const [prompt, setPrompt] = useState8("");
2108
+ const [messages, setMessages] = useState8([]);
2109
+ const [loading, setLoading] = useState8(false);
2110
+ const [hydrating, setHydrating] = useState8(false);
2111
+ const [error, setError] = useState8("");
2112
+ const [copiedId, setCopiedId] = useState8(null);
2113
+ const scrollerRef = useRef3(null);
2114
+ const pendingPromptRef = useRef3(null);
2115
+ const scrollToBottom = useCallback3(() => {
2116
+ const el = scrollerRef.current;
2117
+ if (!el) return;
2118
+ el.scrollTop = el.scrollHeight;
2119
+ }, []);
2120
+ const hydrate = useCallback3(async () => {
2121
+ var _a;
2122
+ setHydrating(true);
2123
+ setError("");
2124
+ try {
2125
+ const data = await fetchChat();
2126
+ setMessages((_a = data.messages) != null ? _a : []);
2127
+ if (!data.configured) {
2128
+ setError("ShellStack AI is not configured for this workspace yet.");
2129
+ }
2130
+ } catch (err) {
2131
+ setError(
2132
+ err instanceof Error ? err.message : "Could not load your Ask AI chat."
2133
+ );
2134
+ } finally {
2135
+ setHydrating(false);
2136
+ }
2137
+ }, [fetchChat]);
2138
+ useEffect7(() => {
2139
+ function onOpen(event) {
2140
+ var _a;
2141
+ const detail = event.detail;
2142
+ setOpen(true);
2143
+ if ((_a = detail == null ? void 0 : detail.prompt) == null ? void 0 : _a.trim()) {
2144
+ pendingPromptRef.current = detail.prompt.trim();
2145
+ setPrompt(detail.prompt.trim());
2146
+ }
2147
+ }
2148
+ window.addEventListener(SUITE_OPEN_ASK_AI_EVENT, onOpen);
2149
+ return () => window.removeEventListener(SUITE_OPEN_ASK_AI_EVENT, onOpen);
2150
+ }, []);
2151
+ useEffect7(() => {
2152
+ if (!open) return;
2153
+ void hydrate().then(() => {
2154
+ const pending = pendingPromptRef.current;
2155
+ pendingPromptRef.current = null;
2156
+ if (pending) {
2157
+ setPrompt(pending);
2158
+ }
2159
+ });
2160
+ }, [open, hydrate]);
2161
+ useEffect7(() => {
2162
+ if (open) scrollToBottom();
2163
+ }, [messages, loading, open, scrollToBottom]);
1636
2164
  async function ask(text) {
2165
+ var _a;
1637
2166
  const trimmed = text.trim();
1638
2167
  if (!trimmed || loading) return;
1639
2168
  setLoading(true);
1640
2169
  setError("");
1641
- setAnswer("");
1642
- setCopied(false);
2170
+ setPrompt("");
2171
+ setMessages((prev) => [...prev, { role: "user", content: trimmed }]);
1643
2172
  try {
1644
- const status = await fetchStatus();
1645
- if (!status.configured) {
1646
- setError("ShellStack AI is not configured for this workspace yet.");
1647
- return;
1648
- }
1649
- const result = await complete({ prompt: trimmed });
1650
- setAnswer(result.text);
2173
+ const result = await sendMessage({ prompt: trimmed });
2174
+ setMessages((_a = result.messages) != null ? _a : []);
1651
2175
  } catch (err) {
2176
+ setMessages(
2177
+ (prev) => {
2178
+ var _a2, _b;
2179
+ 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;
2180
+ }
2181
+ );
2182
+ setPrompt(trimmed);
1652
2183
  setError(
1653
2184
  err instanceof Error ? err.message : "The AI request failed. Try again."
1654
2185
  );
@@ -1656,133 +2187,282 @@ function SuiteAiPanel({
1656
2187
  setLoading(false);
1657
2188
  }
1658
2189
  }
1659
- async function copyAnswer() {
1660
- if (!answer) return;
2190
+ async function handleClear() {
2191
+ if (loading) return;
1661
2192
  try {
1662
- await navigator.clipboard.writeText(answer);
1663
- setCopied(true);
1664
- window.setTimeout(() => setCopied(false), 1500);
2193
+ await clearChat();
2194
+ setMessages([]);
2195
+ setError("");
2196
+ } catch (err) {
2197
+ setError(
2198
+ err instanceof Error ? err.message : "Could not clear this chat."
2199
+ );
2200
+ }
2201
+ }
2202
+ async function copyMessage(index, content) {
2203
+ try {
2204
+ await navigator.clipboard.writeText(content);
2205
+ setCopiedId(index);
2206
+ window.setTimeout(() => setCopiedId(null), 1500);
1665
2207
  } catch (e) {
1666
2208
  }
1667
2209
  }
1668
- return /* @__PURE__ */ jsxs13(Fragment5, { children: [
1669
- /* @__PURE__ */ jsxs13(
2210
+ const Icon = BrandIcon != null ? BrandIcon : Sparks;
2211
+ return /* @__PURE__ */ jsxs15(Fragment5, { children: [
2212
+ !hideLauncher ? /* @__PURE__ */ jsxs15(
1670
2213
  "button",
1671
2214
  {
1672
2215
  type: "button",
1673
2216
  "data-test": "ai-panel-launcher",
1674
2217
  onClick: () => setOpen(true),
1675
2218
  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",
1676
- "aria-label": "Open ShellStack AI",
2219
+ "aria-label": "Open Ask AI",
1677
2220
  children: [
1678
- /* @__PURE__ */ jsx16(BrandIcon, { className: "h-4 w-4 text-ph-brand" }),
2221
+ /* @__PURE__ */ jsx18(Icon, { className: "h-4 w-4 text-ph-brand" }),
1679
2222
  "Ask AI"
1680
2223
  ]
1681
2224
  }
1682
- ),
1683
- open && /* @__PURE__ */ jsxs13("div", { className: "fixed inset-0 z-40", role: "dialog", "aria-modal": "true", children: [
1684
- /* @__PURE__ */ jsx16(
2225
+ ) : null,
2226
+ open ? /* @__PURE__ */ jsxs15("div", { className: "fixed inset-0 z-40", role: "dialog", "aria-modal": "true", children: [
2227
+ /* @__PURE__ */ jsx18(
1685
2228
  "button",
1686
2229
  {
1687
2230
  type: "button",
1688
2231
  className: "absolute inset-0 bg-black/25",
1689
2232
  onClick: () => setOpen(false),
1690
- "aria-label": "Close AI panel"
2233
+ "aria-label": "Close Ask AI"
1691
2234
  }
1692
2235
  ),
1693
- /* @__PURE__ */ jsxs13("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: [
1694
- /* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-between border-b border-ph-border px-4 py-3", children: [
1695
- /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2", children: [
1696
- /* @__PURE__ */ jsx16(BrandIcon, { className: "h-4 w-4 text-ph-brand" }),
1697
- /* @__PURE__ */ jsx16("span", { className: "text-sm font-semibold text-ph-ink", children: "ShellStack AI" })
1698
- ] }),
1699
- /* @__PURE__ */ jsx16(
1700
- "button",
1701
- {
1702
- type: "button",
1703
- onClick: () => setOpen(false),
1704
- className: "rounded p-1 hover:bg-ph-muted",
1705
- "aria-label": "Close",
1706
- children: /* @__PURE__ */ jsx16(X2, { className: "h-4 w-4" })
1707
- }
1708
- )
1709
- ] }),
1710
- /* @__PURE__ */ jsx16("div", { className: "flex flex-wrap gap-1.5 border-b border-ph-border px-4 py-2", children: presets.map((preset) => /* @__PURE__ */ jsx16(
1711
- "button",
1712
- {
1713
- type: "button",
1714
- onClick: () => {
1715
- setPrompt(preset.prompt);
1716
- void ask(preset.prompt);
1717
- },
1718
- disabled: loading,
1719
- className: "rounded-md border border-ph-border px-2 py-1 text-xs text-ph-ink hover:bg-ph-muted disabled:opacity-50",
1720
- children: preset.label
1721
- },
1722
- preset.label
1723
- )) }),
1724
- /* @__PURE__ */ jsxs13("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [
1725
- error && /* @__PURE__ */ jsx16("p", { className: "bg-ph-danger/10 rounded-lg px-3 py-2 text-sm text-ph-danger", children: error }),
1726
- loading && /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2 text-sm text-ph-mutedtext", children: [
1727
- /* @__PURE__ */ jsx16(Spinner, { className: "h-4 w-4 animate-spin" }),
1728
- "Thinking\u2026"
1729
- ] }),
1730
- answer && !loading && /* @__PURE__ */ jsxs13("div", { className: "rounded-lg bg-ph-muted px-3 py-2 text-sm text-ph-ink", children: [
1731
- /* @__PURE__ */ jsx16("p", { className: "whitespace-pre-wrap", children: answer }),
1732
- /* @__PURE__ */ jsxs13(
2236
+ /* @__PURE__ */ jsxs15(
2237
+ "aside",
2238
+ {
2239
+ 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",
2240
+ "data-test": "suite-ask-ai-panel",
2241
+ children: [
2242
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between border-b border-ph-border px-4 py-3", children: [
2243
+ /* @__PURE__ */ jsxs15("div", { className: "min-w-0", children: [
2244
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
2245
+ /* @__PURE__ */ jsx18(Icon, { className: "h-4 w-4 shrink-0 text-ph-brand" }),
2246
+ /* @__PURE__ */ jsx18("span", { className: "text-sm font-semibold text-ph-ink", children: title })
2247
+ ] }),
2248
+ /* @__PURE__ */ jsx18("p", { className: "mt-0.5 text-[11px] text-ph-mutedtext", children: "Shared across your suite \xB7 same chat in every app" })
2249
+ ] }),
2250
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-1", children: [
2251
+ messages.length > 0 ? /* @__PURE__ */ jsx18(
2252
+ "button",
2253
+ {
2254
+ type: "button",
2255
+ onClick: () => void handleClear(),
2256
+ disabled: loading,
2257
+ className: "rounded-md px-2 py-1 text-xs text-ph-mutedtext hover:bg-ph-muted hover:text-ph-ink disabled:opacity-50",
2258
+ "data-test": "ask-ai-clear",
2259
+ children: "Clear"
2260
+ }
2261
+ ) : null,
2262
+ /* @__PURE__ */ jsx18(
2263
+ "button",
2264
+ {
2265
+ type: "button",
2266
+ onClick: () => setOpen(false),
2267
+ className: "rounded p-1 hover:bg-ph-muted",
2268
+ "aria-label": "Close",
2269
+ children: /* @__PURE__ */ jsx18(X2, { className: "h-4 w-4" })
2270
+ }
2271
+ )
2272
+ ] })
2273
+ ] }),
2274
+ presets.length > 0 && messages.length === 0 ? /* @__PURE__ */ jsx18("div", { className: "flex flex-wrap gap-1.5 border-b border-ph-border px-4 py-2", children: presets.map((preset) => /* @__PURE__ */ jsx18(
1733
2275
  "button",
1734
2276
  {
1735
2277
  type: "button",
1736
- onClick: () => void copyAnswer(),
1737
- className: "mt-2 inline-flex items-center gap-1 text-xs text-ph-mutedtext hover:text-ph-ink",
2278
+ onClick: () => void ask(preset.prompt),
2279
+ disabled: loading || hydrating,
2280
+ className: "rounded-full border border-ph-border px-2.5 py-1 text-xs text-ph-ink hover:bg-ph-muted disabled:opacity-50",
2281
+ children: preset.label
2282
+ },
2283
+ preset.label
2284
+ )) }) : null,
2285
+ /* @__PURE__ */ jsxs15(
2286
+ "div",
2287
+ {
2288
+ ref: scrollerRef,
2289
+ className: "flex-1 space-y-3 overflow-y-auto p-4",
1738
2290
  children: [
1739
- copied ? /* @__PURE__ */ jsx16(Check2, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx16(Copy, { className: "h-3 w-3" }),
1740
- copied ? "Copied" : "Copy"
2291
+ error ? /* @__PURE__ */ jsx18("p", { className: "bg-ph-danger/10 rounded-lg px-3 py-2 text-sm text-ph-danger", children: error }) : null,
2292
+ hydrating && messages.length === 0 ? /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-sm text-ph-mutedtext", children: [
2293
+ /* @__PURE__ */ jsx18(Spinner, { className: "h-4 w-4 animate-spin" }),
2294
+ "Loading chat\u2026"
2295
+ ] }) : null,
2296
+ !hydrating && messages.length === 0 && !error ? /* @__PURE__ */ jsxs15("div", { className: "mt-10 px-2 text-center", children: [
2297
+ /* @__PURE__ */ jsx18("p", { className: "font-display text-base font-semibold text-ph-ink", children: "How can I help?" }),
2298
+ /* @__PURE__ */ jsx18("p", { className: "mt-2 text-sm leading-relaxed text-ph-subtle", children: emptyState })
2299
+ ] }) : null,
2300
+ messages.map((message, index) => {
2301
+ const isUser = message.role === "user";
2302
+ return /* @__PURE__ */ jsxs15(
2303
+ "div",
2304
+ {
2305
+ 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",
2306
+ "data-test": isUser ? "ask-ai-user" : "ask-ai-assistant",
2307
+ children: [
2308
+ /* @__PURE__ */ jsx18("p", { className: "whitespace-pre-wrap", children: message.content }),
2309
+ !isUser ? /* @__PURE__ */ jsxs15(
2310
+ "button",
2311
+ {
2312
+ type: "button",
2313
+ onClick: () => void copyMessage(index, message.content),
2314
+ className: "mt-2 inline-flex items-center gap-1 text-xs text-ph-mutedtext hover:text-ph-ink",
2315
+ children: [
2316
+ copiedId === index ? /* @__PURE__ */ jsx18(Check2, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx18(Copy, { className: "h-3 w-3" }),
2317
+ copiedId === index ? "Copied" : "Copy"
2318
+ ]
2319
+ }
2320
+ ) : null
2321
+ ]
2322
+ },
2323
+ `${message.role}-${index}-${message.content.slice(0, 24)}`
2324
+ );
2325
+ }),
2326
+ loading ? /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-sm text-ph-mutedtext", children: [
2327
+ /* @__PURE__ */ jsx18(Spinner, { className: "h-4 w-4 animate-spin" }),
2328
+ "Thinking\u2026"
2329
+ ] }) : null
1741
2330
  ]
1742
2331
  }
1743
- )
1744
- ] }),
1745
- !answer && !loading && !error && /* @__PURE__ */ jsx16("p", { className: "mt-8 text-center text-xs text-ph-mutedtext", children: emptyState })
1746
- ] }),
1747
- /* @__PURE__ */ jsx16("div", { className: "border-t border-ph-border p-3", children: /* @__PURE__ */ jsxs13("div", { className: "flex gap-2", children: [
1748
- /* @__PURE__ */ jsx16(
1749
- "textarea",
1750
- {
1751
- value: prompt,
1752
- onChange: (e) => setPrompt(e.target.value),
1753
- onKeyDown: (e) => {
1754
- if (e.key === "Enter" && !e.shiftKey) {
1755
- e.preventDefault();
1756
- void ask(prompt);
2332
+ ),
2333
+ /* @__PURE__ */ jsx18("div", { className: "border-t border-ph-border p-3", children: /* @__PURE__ */ jsxs15("div", { className: "flex gap-2", children: [
2334
+ /* @__PURE__ */ jsx18(
2335
+ "textarea",
2336
+ {
2337
+ value: prompt,
2338
+ onChange: (e) => setPrompt(e.target.value),
2339
+ onKeyDown: (e) => {
2340
+ if (e.key === "Enter" && !e.shiftKey) {
2341
+ e.preventDefault();
2342
+ void ask(prompt);
2343
+ }
2344
+ },
2345
+ placeholder: "Ask anything\u2026",
2346
+ rows: 2,
2347
+ disabled: loading,
2348
+ "data-test": "ask-ai-input",
2349
+ 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"
1757
2350
  }
1758
- },
1759
- placeholder: "Ask anything\u2026",
1760
- rows: 2,
1761
- disabled: loading,
1762
- 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"
1763
- }
1764
- ),
1765
- /* @__PURE__ */ jsx16(
1766
- "button",
1767
- {
1768
- type: "button",
1769
- onClick: () => void ask(prompt),
1770
- disabled: loading || !prompt.trim(),
1771
- className: "self-end rounded-lg bg-ph-brand px-3 py-2 text-sm font-medium text-white disabled:opacity-50",
1772
- children: "Ask"
1773
- }
1774
- )
1775
- ] }) })
1776
- ] })
1777
- ] })
2351
+ ),
2352
+ /* @__PURE__ */ jsx18(
2353
+ "button",
2354
+ {
2355
+ type: "button",
2356
+ onClick: () => void ask(prompt),
2357
+ disabled: loading || !prompt.trim(),
2358
+ "data-test": "ask-ai-send",
2359
+ className: "self-end rounded-xl bg-ph-brand px-3.5 py-2 text-sm font-medium text-white disabled:opacity-50",
2360
+ children: "Ask"
2361
+ }
2362
+ )
2363
+ ] }) })
2364
+ ]
2365
+ }
2366
+ )
2367
+ ] }) : null
1778
2368
  ] });
1779
2369
  }
1780
2370
 
2371
+ // src/suite/lib/use-sidebar-width.ts
2372
+ import {
2373
+ useCallback as useCallback4,
2374
+ useEffect as useEffect8,
2375
+ useState as useState9
2376
+ } from "react";
2377
+ var SIDEBAR_RAIL_WIDTH = 56;
2378
+ var SIDEBAR_COLLAPSE_THRESHOLD = 80;
2379
+ var SIDEBAR_MIN_EXPANDED_WIDTH = 180;
2380
+ var SIDEBAR_MAX_WIDTH = 320;
2381
+ var SIDEBAR_DEFAULT_WIDTH = 320;
2382
+ function clampWidth(width) {
2383
+ return Math.min(SIDEBAR_MAX_WIDTH, Math.max(SIDEBAR_RAIL_WIDTH, width));
2384
+ }
2385
+ function snapWidth(width) {
2386
+ if (width <= SIDEBAR_COLLAPSE_THRESHOLD) return SIDEBAR_RAIL_WIDTH;
2387
+ if (width < SIDEBAR_MIN_EXPANDED_WIDTH) return SIDEBAR_MIN_EXPANDED_WIDTH;
2388
+ return Math.min(SIDEBAR_MAX_WIDTH, width);
2389
+ }
2390
+ function useSidebarWidth(storageKey) {
2391
+ const [width, setWidth] = useState9(SIDEBAR_DEFAULT_WIDTH);
2392
+ const [narrowViewport, setNarrowViewport] = useState9(false);
2393
+ const collapsed = narrowViewport || width <= SIDEBAR_COLLAPSE_THRESHOLD;
2394
+ useEffect8(() => {
2395
+ let stored = null;
2396
+ try {
2397
+ stored = localStorage.getItem(storageKey);
2398
+ } catch (e) {
2399
+ }
2400
+ if (!stored) return;
2401
+ const parsed = Number.parseInt(stored, 10);
2402
+ if (!Number.isNaN(parsed)) {
2403
+ queueMicrotask(() => setWidth(snapWidth(clampWidth(parsed))));
2404
+ }
2405
+ }, [storageKey]);
2406
+ useEffect8(() => {
2407
+ const media = window.matchMedia("(max-width: 639px)");
2408
+ const sync = () => setNarrowViewport(media.matches);
2409
+ sync();
2410
+ media.addEventListener("change", sync);
2411
+ return () => media.removeEventListener("change", sync);
2412
+ }, []);
2413
+ const persist = useCallback4(
2414
+ (value) => {
2415
+ try {
2416
+ localStorage.setItem(storageKey, String(value));
2417
+ } catch (e) {
2418
+ }
2419
+ },
2420
+ [storageKey]
2421
+ );
2422
+ const setCollapsed = useCallback4(
2423
+ (value) => {
2424
+ setWidth((current) => {
2425
+ const next = value ? SIDEBAR_RAIL_WIDTH : current <= SIDEBAR_COLLAPSE_THRESHOLD ? SIDEBAR_DEFAULT_WIDTH : snapWidth(current);
2426
+ persist(next);
2427
+ return next;
2428
+ });
2429
+ },
2430
+ [persist]
2431
+ );
2432
+ const resetWidth = useCallback4(() => {
2433
+ setWidth(SIDEBAR_DEFAULT_WIDTH);
2434
+ persist(SIDEBAR_DEFAULT_WIDTH);
2435
+ }, [persist]);
2436
+ const startResize = useCallback4(
2437
+ (event) => {
2438
+ event.preventDefault();
2439
+ const startX = event.clientX;
2440
+ const startWidth = width;
2441
+ const onMove = (moveEvent) => {
2442
+ setWidth(clampWidth(startWidth + moveEvent.clientX - startX));
2443
+ };
2444
+ const onUp = () => {
2445
+ document.removeEventListener("pointermove", onMove);
2446
+ document.removeEventListener("pointerup", onUp);
2447
+ setWidth((current) => {
2448
+ const snapped = snapWidth(current);
2449
+ persist(snapped);
2450
+ return snapped;
2451
+ });
2452
+ };
2453
+ document.addEventListener("pointermove", onMove);
2454
+ document.addEventListener("pointerup", onUp);
2455
+ },
2456
+ [persist, width]
2457
+ );
2458
+ return { width, collapsed, setCollapsed, resetWidth, startResize };
2459
+ }
2460
+
1781
2461
  // src/suite/lib/motion.tsx
1782
2462
  import { LazyMotion, domAnimation, m } from "motion/react";
1783
- import { jsx as jsx17 } from "react/jsx-runtime";
2463
+ import { jsx as jsx19 } from "react/jsx-runtime";
1784
2464
  function SuiteMotionProvider({ children }) {
1785
- return /* @__PURE__ */ jsx17(LazyMotion, { features: domAnimation, strict: true, children });
2465
+ return /* @__PURE__ */ jsx19(LazyMotion, { features: domAnimation, strict: true, children });
1786
2466
  }
1787
2467
  var SUITE_SPRINGS = {
1788
2468
  /** Snappy panel/sheet entrances — small overshoot. */
@@ -1793,217 +2473,25 @@ var SUITE_SPRINGS = {
1793
2473
  press: { type: "spring", stiffness: 600, damping: 35, mass: 0.5 }
1794
2474
  };
1795
2475
 
1796
- // src/suite/components/suite-layout.tsx
1797
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
1798
- var WIDTHS = {
1799
- full: "max-w-none",
1800
- wide: "max-w-7xl",
1801
- content: "max-w-6xl",
1802
- narrow: "max-w-4xl"
1803
- };
1804
- function SuiteBreadcrumbs({ items, className }) {
1805
- return /* @__PURE__ */ jsx18("nav", { "aria-label": "Breadcrumb", className, children: /* @__PURE__ */ jsx18("ol", { className: "flex min-w-0 items-center gap-1.5", children: items.map((item, index) => {
1806
- const isLast = index === items.length - 1;
1807
- return /* @__PURE__ */ jsxs14("li", { className: "flex min-w-0 items-center gap-1.5", children: [
1808
- index > 0 ? /* @__PURE__ */ jsx18("span", { className: "select-none text-ph-border", "aria-hidden": "true", children: "/" }) : null,
1809
- item.href && !isLast ? /* @__PURE__ */ jsx18(
1810
- "a",
1811
- {
1812
- href: item.href,
1813
- className: "truncate transition hover:text-ph-ink",
1814
- children: item.label
1815
- }
1816
- ) : /* @__PURE__ */ jsx18(
1817
- "span",
1818
- {
1819
- className: cn(
1820
- "truncate",
1821
- isLast ? "font-medium text-ph-ink" : "text-ph-subtle"
1822
- ),
1823
- "aria-current": isLast ? "page" : void 0,
1824
- children: item.label
1825
- }
1826
- )
1827
- ] }, index);
1828
- }) }) });
1829
- }
1830
- function SuitePage({
1831
- children,
1832
- width = "wide",
1833
- inset = true,
1834
- className,
1835
- as: Component = "div",
1836
- dataTest,
1837
- id,
1838
- role,
1839
- "aria-live": ariaLive
1840
- }) {
1841
- return /* @__PURE__ */ jsx18(
1842
- Component,
1843
- {
1844
- id,
1845
- "data-test": dataTest,
1846
- role,
1847
- "aria-live": ariaLive,
1848
- className: cn(
1849
- "mx-auto flex w-full min-w-0 flex-col",
1850
- WIDTHS[width],
1851
- // Tight inset keeps page titles aligned without excessive whitespace.
1852
- inset && "px-4 pb-6 pt-4 sm:px-6 sm:pb-7 sm:pt-5 lg:px-8",
1853
- className
1854
- ),
1855
- children
1856
- }
1857
- );
1858
- }
1859
- function SuitePageHeader({
1860
- title,
1861
- description,
1862
- eyebrow,
1863
- actions,
1864
- className,
1865
- titleClassName,
1866
- dataTest,
1867
- /** Optional icon rendered before the title. */
1868
- icon,
1869
- /** Use the animated sun/moon icon from the Today page. */
1870
- todayIcon,
1871
- todayIconClassName,
1872
- /** Small badge/pill next to the title (e.g. "Beta", org name). */
1873
- badge,
1874
- /** Breadcrumb line rendered above the title. */
1875
- breadcrumb
1876
- }) {
1877
- const resolvedIcon = todayIcon ? /* @__PURE__ */ jsx18(
1878
- TodayTimeIcon,
1879
- {
1880
- className: cn(
1881
- "h-6 w-6 sm:h-7 sm:w-7 text-amber-500",
1882
- todayIconClassName
1883
- )
1884
- }
1885
- ) : icon;
1886
- return /* @__PURE__ */ jsxs14(
1887
- "header",
1888
- {
1889
- "data-test": dataTest,
1890
- className: cn(
1891
- "flex items-start justify-between gap-4 pb-4 sm:pb-5",
1892
- className
1893
- ),
1894
- children: [
1895
- /* @__PURE__ */ jsxs14("div", { className: "flex min-w-0 items-start gap-3", children: [
1896
- resolvedIcon ? /* @__PURE__ */ jsx18(
1897
- "div",
1898
- {
1899
- className: "hidden shrink-0 pt-1 sm:block",
1900
- "aria-hidden": "true",
1901
- children: resolvedIcon
1902
- }
1903
- ) : null,
1904
- /* @__PURE__ */ jsxs14("div", { className: "flex min-w-0 flex-col gap-1", children: [
1905
- eyebrow ? /* @__PURE__ */ jsx18("div", { className: "mb-0.5 flex min-w-0 items-center gap-1.5 text-xs text-ph-subtle", children: eyebrow }) : null,
1906
- breadcrumb ? /* @__PURE__ */ jsx18("div", { className: "mb-0.5 flex min-w-0 items-center gap-1.5 text-xs text-ph-subtle", children: breadcrumb }) : null,
1907
- /* @__PURE__ */ jsxs14("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
1908
- /* @__PURE__ */ jsx18(
1909
- "h1",
1910
- {
1911
- className: cn(
1912
- "font-display text-[1.625rem] font-bold leading-tight tracking-tight text-ph-ink sm:text-[2.25rem]",
1913
- titleClassName
1914
- ),
1915
- children: title
1916
- }
1917
- ),
1918
- badge ? /* @__PURE__ */ jsx18("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
1919
- ] }),
1920
- description ? /* @__PURE__ */ jsx18("div", { className: "mt-1 max-w-2xl text-pretty text-sm leading-5 text-ph-subtle", children: description }) : null
1921
- ] })
1922
- ] }),
1923
- actions ? /* @__PURE__ */ jsx18("div", { className: "flex shrink-0 items-center gap-2 pt-1", children: actions }) : null
1924
- ]
1925
- }
1926
- );
1927
- }
1928
- function SuiteToolbar({
1929
- children,
1930
- className,
1931
- label = "Page tools",
1932
- dataTest
1933
- }) {
1934
- return /* @__PURE__ */ jsx18(
1935
- "div",
1936
- {
1937
- role: "toolbar",
1938
- "aria-label": label,
1939
- "data-test": dataTest,
1940
- className: cn("flex min-w-0 flex-wrap items-center gap-2", className),
1941
- children
1942
- }
1943
- );
1944
- }
1945
- function SuiteTabList({
1946
- children,
1947
- className,
1948
- label,
1949
- dataTest
1950
- }) {
1951
- return /* @__PURE__ */ jsx18(
1952
- "nav",
1953
- {
1954
- "aria-label": label,
1955
- "data-test": dataTest,
1956
- className: cn(
1957
- "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]",
1958
- className
1959
- ),
1960
- children
1961
- }
1962
- );
1963
- }
1964
- function SuiteSectionHeader({
1965
- title,
1966
- description,
1967
- action,
1968
- className
1969
- }) {
1970
- return /* @__PURE__ */ jsxs14(
1971
- "div",
1972
- {
1973
- className: cn(
1974
- "flex min-w-0 flex-col gap-4 sm:flex-row sm:items-end sm:justify-between",
1975
- className
1976
- ),
1977
- children: [
1978
- /* @__PURE__ */ jsxs14("div", { className: "min-w-0", children: [
1979
- /* @__PURE__ */ jsx18("h2", { className: "font-display text-balance text-lg font-semibold tracking-tight text-ph-ink sm:text-xl", children: title }),
1980
- description ? /* @__PURE__ */ jsx18("div", { className: "mt-1 max-w-2xl text-pretty text-sm leading-5 text-ph-subtle", children: description }) : null
1981
- ] }),
1982
- action ? /* @__PURE__ */ jsx18("div", { className: "flex min-h-[44px] shrink-0 items-center", children: action }) : null
1983
- ]
1984
- }
1985
- );
1986
- }
1987
-
1988
2476
  // src/suite/components/suite-skeleton.tsx
1989
- import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
2477
+ import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
1990
2478
  function SuiteSkeleton({
1991
2479
  className,
1992
2480
  lines = 1,
1993
2481
  circle,
1994
2482
  width
1995
2483
  }) {
1996
- return /* @__PURE__ */ jsx19(
2484
+ return /* @__PURE__ */ jsx20(
1997
2485
  "div",
1998
2486
  {
1999
2487
  className: cn("space-y-2", circle ? "flex flex-col items-center" : ""),
2000
2488
  "aria-hidden": true,
2001
- children: Array.from({ length: lines }).map((_, index) => /* @__PURE__ */ jsx19(
2489
+ children: Array.from({ length: lines }).map((_, index) => /* @__PURE__ */ jsx20(
2002
2490
  "div",
2003
2491
  {
2004
2492
  className: "animate-[skeleton-stagger_0.6s_ease-out_both]",
2005
2493
  style: { animationDelay: `${index * 80}ms` },
2006
- children: /* @__PURE__ */ jsx19(
2494
+ children: /* @__PURE__ */ jsx20(
2007
2495
  "div",
2008
2496
  {
2009
2497
  className: cn(
@@ -2027,35 +2515,35 @@ function SuiteSkeletonCard({
2027
2515
  rows = 3,
2028
2516
  header = true
2029
2517
  }) {
2030
- return /* @__PURE__ */ jsxs15(
2518
+ return /* @__PURE__ */ jsxs16(
2031
2519
  "div",
2032
2520
  {
2033
2521
  className: cn(
2034
- "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",
2522
+ "ph-skeleton-card animate-[skeleton-stagger_0.5s_ease-out_both] rounded-2xl border border-ph-border bg-ph-surface p-6 shadow-sm",
2035
2523
  className
2036
2524
  ),
2037
2525
  "aria-hidden": true,
2038
2526
  children: [
2039
- header ? /* @__PURE__ */ jsxs15(
2527
+ header ? /* @__PURE__ */ jsxs16(
2040
2528
  "div",
2041
2529
  {
2042
2530
  className: "mb-5 flex items-center gap-3 animate-[skeleton-stagger_0.5s_ease-out_both]",
2043
2531
  style: { animationDelay: "100ms" },
2044
2532
  children: [
2045
- /* @__PURE__ */ jsx19("div", { className: "ph-skeleton h-10 w-10 rounded-xl" }),
2046
- /* @__PURE__ */ jsxs15("div", { className: "flex-1 space-y-2", children: [
2047
- /* @__PURE__ */ jsx19("div", { className: "ph-skeleton h-4 w-1/3" }),
2048
- /* @__PURE__ */ jsx19("div", { className: "ph-skeleton h-3 w-1/2" })
2533
+ /* @__PURE__ */ jsx20("div", { className: "ph-skeleton h-10 w-10 rounded-xl" }),
2534
+ /* @__PURE__ */ jsxs16("div", { className: "flex-1 space-y-2", children: [
2535
+ /* @__PURE__ */ jsx20("div", { className: "ph-skeleton h-4 w-1/3" }),
2536
+ /* @__PURE__ */ jsx20("div", { className: "ph-skeleton h-3 w-1/2" })
2049
2537
  ] })
2050
2538
  ]
2051
2539
  }
2052
2540
  ) : null,
2053
- /* @__PURE__ */ jsx19("div", { className: "space-y-3", children: Array.from({ length: rows }).map((_, index) => /* @__PURE__ */ jsx19(
2541
+ /* @__PURE__ */ jsx20("div", { className: "space-y-3", children: Array.from({ length: rows }).map((_, index) => /* @__PURE__ */ jsx20(
2054
2542
  "div",
2055
2543
  {
2056
2544
  className: "animate-[skeleton-stagger_0.5s_ease-out_both]",
2057
2545
  style: { animationDelay: `${200 + index * 80}ms` },
2058
- children: /* @__PURE__ */ jsx19(
2546
+ children: /* @__PURE__ */ jsx20(
2059
2547
  "div",
2060
2548
  {
2061
2549
  className: "ph-skeleton h-3",
@@ -2073,16 +2561,16 @@ function SuiteSkeletonList({
2073
2561
  className,
2074
2562
  items = 4
2075
2563
  }) {
2076
- return /* @__PURE__ */ jsx19("div", { className: cn("space-y-3", className), "aria-hidden": true, children: Array.from({ length: items }).map((_, index) => /* @__PURE__ */ jsx19(
2564
+ return /* @__PURE__ */ jsx20("div", { className: cn("space-y-3", className), "aria-hidden": true, children: Array.from({ length: items }).map((_, index) => /* @__PURE__ */ jsx20(
2077
2565
  "div",
2078
2566
  {
2079
2567
  className: "animate-[skeleton-stagger_0.6s_ease-out_both]",
2080
2568
  style: { animationDelay: `${index * 100}ms` },
2081
- children: /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-3 rounded-xl border border-ph-border/60 bg-ph-surface p-4", children: [
2082
- /* @__PURE__ */ jsx19("div", { className: "ph-skeleton h-10 w-10 rounded-lg" }),
2083
- /* @__PURE__ */ jsxs15("div", { className: "min-w-0 flex-1 space-y-2", children: [
2084
- /* @__PURE__ */ jsx19("div", { className: "ph-skeleton h-3.5 w-3/4" }),
2085
- /* @__PURE__ */ jsx19("div", { className: "ph-skeleton h-3 w-1/2" })
2569
+ children: /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-3 rounded-xl border border-ph-border bg-ph-surface p-4", children: [
2570
+ /* @__PURE__ */ jsx20("div", { className: "ph-skeleton h-10 w-10 rounded-lg" }),
2571
+ /* @__PURE__ */ jsxs16("div", { className: "min-w-0 flex-1 space-y-2", children: [
2572
+ /* @__PURE__ */ jsx20("div", { className: "ph-skeleton h-3.5 w-3/4" }),
2573
+ /* @__PURE__ */ jsx20("div", { className: "ph-skeleton h-3 w-1/2" })
2086
2574
  ] })
2087
2575
  ] })
2088
2576
  },
@@ -2097,22 +2585,22 @@ function SuiteEmptyState({
2097
2585
  className,
2098
2586
  size = "md"
2099
2587
  }) {
2100
- return /* @__PURE__ */ jsxs15(
2588
+ return /* @__PURE__ */ jsxs16(
2101
2589
  "div",
2102
2590
  {
2103
2591
  className: cn(
2104
- "relative overflow-hidden rounded-2xl border border-ph-border/80 bg-ph-surface text-center",
2592
+ "relative overflow-hidden rounded-2xl border border-ph-border bg-ph-surface text-center",
2105
2593
  size === "sm" ? "px-5 py-8" : "px-6 py-12 sm:px-10 sm:py-16",
2106
2594
  className
2107
2595
  ),
2108
2596
  children: [
2109
- /* @__PURE__ */ jsxs15("div", { className: "pointer-events-none absolute inset-0 overflow-hidden opacity-60", "aria-hidden": true, children: [
2110
- /* @__PURE__ */ jsx19("div", { className: "absolute -left-10 -top-10 h-40 w-40 rounded-full bg-ph-brand/10 blur-3xl" }),
2111
- /* @__PURE__ */ jsx19("div", { className: "absolute -bottom-10 -right-10 h-40 w-40 rounded-full bg-ph-accent/10 blur-3xl" })
2597
+ /* @__PURE__ */ jsxs16("div", { className: "pointer-events-none absolute inset-0 overflow-hidden opacity-60", "aria-hidden": true, children: [
2598
+ /* @__PURE__ */ jsx20("div", { className: "absolute -left-10 -top-10 h-40 w-40 rounded-full bg-ph-brand/10 blur-3xl" }),
2599
+ /* @__PURE__ */ jsx20("div", { className: "absolute -bottom-10 -right-10 h-40 w-40 rounded-full bg-ph-accent/10 blur-3xl" })
2112
2600
  ] }),
2113
- /* @__PURE__ */ jsxs15("div", { className: "relative", children: [
2114
- icon ? /* @__PURE__ */ jsx19("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,
2115
- /* @__PURE__ */ jsx19(
2601
+ /* @__PURE__ */ jsxs16("div", { className: "relative", children: [
2602
+ icon ? /* @__PURE__ */ jsx20("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,
2603
+ /* @__PURE__ */ jsx20(
2116
2604
  "h3",
2117
2605
  {
2118
2606
  className: cn(
@@ -2122,8 +2610,8 @@ function SuiteEmptyState({
2122
2610
  children: title
2123
2611
  }
2124
2612
  ),
2125
- description ? /* @__PURE__ */ jsx19("p", { className: "mx-auto mt-2 max-w-sm text-sm leading-relaxed text-ph-subtle", children: description }) : null,
2126
- action ? /* @__PURE__ */ jsx19("div", { className: "mt-5 flex flex-wrap items-center justify-center gap-2", children: action }) : null
2613
+ description ? /* @__PURE__ */ jsx20("p", { className: "mx-auto mt-2 max-w-sm text-sm leading-relaxed text-ph-subtle", children: description }) : null,
2614
+ action ? /* @__PURE__ */ jsx20("div", { className: "mt-5 flex flex-wrap items-center justify-center gap-2", children: action }) : null
2127
2615
  ] })
2128
2616
  ]
2129
2617
  }
@@ -2132,23 +2620,25 @@ function SuiteEmptyState({
2132
2620
 
2133
2621
  // src/suite/components/suite-settings-mobile-nav.tsx
2134
2622
  import { isValidElement } from "react";
2135
- import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
2623
+ import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
2136
2624
  function SuiteSettingsMobileNav({
2137
2625
  items,
2138
2626
  activeHref,
2139
2627
  onSelect,
2140
- dataTest
2628
+ dataTest,
2629
+ ariaLabel = "Settings sections",
2630
+ className
2141
2631
  }) {
2142
- return /* @__PURE__ */ jsx20(
2632
+ return /* @__PURE__ */ jsx21(
2143
2633
  "nav",
2144
2634
  {
2145
2635
  "data-test": dataTest,
2146
- "aria-label": "Settings sections",
2147
- className: "-mx-4 px-4 lg:hidden",
2148
- children: /* @__PURE__ */ jsx20("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) => {
2636
+ "aria-label": ariaLabel,
2637
+ className: cn("suite-settings-mobile-nav lg:hidden", className),
2638
+ children: /* @__PURE__ */ jsx21("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) => {
2149
2639
  const Icon = item.icon;
2150
2640
  const active = activeHref === item.href || activeHref.startsWith(`${item.href}/`);
2151
- const iconNode = isValidElement(item.icon) ? item.icon : /* @__PURE__ */ jsx20(
2641
+ const iconNode = isValidElement(item.icon) ? item.icon : /* @__PURE__ */ jsx21(
2152
2642
  Icon,
2153
2643
  {
2154
2644
  className: cn(
@@ -2158,7 +2648,7 @@ function SuiteSettingsMobileNav({
2158
2648
  "aria-hidden": true
2159
2649
  }
2160
2650
  );
2161
- return /* @__PURE__ */ jsxs16(
2651
+ return /* @__PURE__ */ jsxs17(
2162
2652
  "button",
2163
2653
  {
2164
2654
  type: "button",
@@ -2166,14 +2656,14 @@ function SuiteSettingsMobileNav({
2166
2656
  onClick: () => onSelect(item.href),
2167
2657
  "aria-current": active ? "page" : void 0,
2168
2658
  className: cn(
2169
- "group relative flex shrink-0 snap-start items-center gap-2 rounded-full px-3.5 py-2.5 text-sm font-medium transition",
2659
+ "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",
2170
2660
  "min-h-[44px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
2171
2661
  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"
2172
2662
  ),
2173
2663
  children: [
2174
2664
  iconNode,
2175
- /* @__PURE__ */ jsx20("span", { className: "whitespace-nowrap", children: item.label }),
2176
- active ? /* @__PURE__ */ jsx20("span", { className: "absolute inset-x-0 -bottom-3 h-0.5 rounded-full bg-ph-ink lg:hidden" }) : null
2665
+ /* @__PURE__ */ jsx21("span", { className: "whitespace-nowrap", children: item.label }),
2666
+ active ? /* @__PURE__ */ jsx21("span", { className: "absolute inset-x-0 -bottom-3 h-0.5 rounded-full bg-ph-ink lg:hidden" }) : null
2177
2667
  ]
2178
2668
  },
2179
2669
  item.href
@@ -2185,7 +2675,7 @@ function SuiteSettingsMobileNav({
2185
2675
 
2186
2676
  // src/suite/components/suite-sidebar.tsx
2187
2677
  import Link2 from "next/link";
2188
- import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
2678
+ import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
2189
2679
  function renderNode(node, collapsed) {
2190
2680
  if (typeof node === "function") return node(collapsed);
2191
2681
  return node;
@@ -2202,7 +2692,7 @@ function SuiteSidebar({
2202
2692
  className,
2203
2693
  surface = false
2204
2694
  }) {
2205
- return /* @__PURE__ */ jsxs17(
2695
+ return /* @__PURE__ */ jsxs18(
2206
2696
  "div",
2207
2697
  {
2208
2698
  className: cn(
@@ -2211,7 +2701,7 @@ function SuiteSidebar({
2211
2701
  className
2212
2702
  ),
2213
2703
  children: [
2214
- /* @__PURE__ */ jsxs17(
2704
+ /* @__PURE__ */ jsxs18(
2215
2705
  "div",
2216
2706
  {
2217
2707
  className: cn(
@@ -2220,12 +2710,12 @@ function SuiteSidebar({
2220
2710
  collapsed ? "flex-col items-center gap-1 px-1 py-2" : "h-14 items-center justify-between gap-2 px-3"
2221
2711
  ),
2222
2712
  children: [
2223
- /* @__PURE__ */ jsx21("div", { className: cn("min-w-0", collapsed ? "shrink-0" : "flex-1"), children: renderNode(appSwitcher, collapsed) }),
2224
- collapsed ? null : /* @__PURE__ */ jsx21("div", { className: "shrink-0", children: renderNode(notificationBell, collapsed) })
2713
+ /* @__PURE__ */ jsx22("div", { className: cn("min-w-0", collapsed ? "shrink-0" : "flex-1"), children: renderNode(appSwitcher, collapsed) }),
2714
+ collapsed ? null : /* @__PURE__ */ jsx22("div", { className: "shrink-0", children: renderNode(notificationBell, collapsed) })
2225
2715
  ]
2226
2716
  }
2227
2717
  ),
2228
- /* @__PURE__ */ jsxs17(
2718
+ /* @__PURE__ */ jsxs18(
2229
2719
  "div",
2230
2720
  {
2231
2721
  className: cn(
@@ -2234,17 +2724,17 @@ function SuiteSidebar({
2234
2724
  collapsed ? "px-1.5 py-2" : "p-2"
2235
2725
  ),
2236
2726
  children: [
2237
- /* @__PURE__ */ jsxs17(
2727
+ /* @__PURE__ */ jsxs18(
2238
2728
  "nav",
2239
2729
  {
2240
2730
  "aria-label": "Pages",
2241
2731
  className: "shrink-0 space-y-0.5",
2242
2732
  children: [
2243
- /* @__PURE__ */ jsx21("div", { className: cn("mb-3", collapsed && "flex justify-center"), children: renderNode(contextSwitcher, collapsed) }),
2244
- /* @__PURE__ */ jsx21("div", { className: "space-y-0.5", children: navItems.map((item) => {
2733
+ /* @__PURE__ */ jsx22("div", { className: cn("mb-3", collapsed && "flex justify-center"), children: renderNode(contextSwitcher, collapsed) }),
2734
+ /* @__PURE__ */ jsx22("div", { className: "space-y-0.5", children: navItems.map((item) => {
2245
2735
  const Icon = item.icon;
2246
2736
  const active = Boolean(item.active);
2247
- const link = /* @__PURE__ */ jsxs17(
2737
+ const link = /* @__PURE__ */ jsxs18(
2248
2738
  Link2,
2249
2739
  {
2250
2740
  href: item.href,
@@ -2257,7 +2747,7 @@ function SuiteSidebar({
2257
2747
  collapsed ? "mx-auto size-10 shrink-0 justify-center gap-0 px-0 py-0" : "w-full px-2.5 py-2"
2258
2748
  ),
2259
2749
  children: [
2260
- /* @__PURE__ */ jsx21(
2750
+ /* @__PURE__ */ jsx22(
2261
2751
  "span",
2262
2752
  {
2263
2753
  className: cn(
@@ -2265,7 +2755,7 @@ function SuiteSidebar({
2265
2755
  item.iconClassName
2266
2756
  ),
2267
2757
  "aria-hidden": true,
2268
- children: /* @__PURE__ */ jsx21(
2758
+ children: /* @__PURE__ */ jsx22(
2269
2759
  Icon,
2270
2760
  {
2271
2761
  className: "h-full w-full",
@@ -2274,7 +2764,7 @@ function SuiteSidebar({
2274
2764
  )
2275
2765
  }
2276
2766
  ),
2277
- /* @__PURE__ */ jsx21(
2767
+ /* @__PURE__ */ jsx22(
2278
2768
  "span",
2279
2769
  {
2280
2770
  className: cn(
@@ -2285,17 +2775,17 @@ function SuiteSidebar({
2285
2775
  children: item.label
2286
2776
  }
2287
2777
  ),
2288
- !collapsed && item.badge ? /* @__PURE__ */ jsx21("span", { className: "relative ml-auto shrink-0", children: item.badge }) : null
2778
+ !collapsed && item.badge ? /* @__PURE__ */ jsx22("span", { className: "relative ml-auto shrink-0", children: item.badge }) : null
2289
2779
  ]
2290
2780
  },
2291
2781
  item.label
2292
2782
  );
2293
- return collapsed ? /* @__PURE__ */ jsx21(Tooltip, { content: item.label, side: "right", children: link }, item.label) : link;
2783
+ return collapsed ? /* @__PURE__ */ jsx22(Tooltip, { content: item.label, side: "right", children: link }, item.label) : link;
2294
2784
  }) })
2295
2785
  ]
2296
2786
  }
2297
2787
  ),
2298
- secondaryNav ? /* @__PURE__ */ jsx21(
2788
+ secondaryNav ? /* @__PURE__ */ jsx22(
2299
2789
  "div",
2300
2790
  {
2301
2791
  className: "min-h-0 flex-1",
@@ -2306,7 +2796,7 @@ function SuiteSidebar({
2306
2796
  ]
2307
2797
  }
2308
2798
  ),
2309
- /* @__PURE__ */ jsx21(
2799
+ /* @__PURE__ */ jsx22(
2310
2800
  "div",
2311
2801
  {
2312
2802
  className: cn(
@@ -2314,7 +2804,7 @@ function SuiteSidebar({
2314
2804
  surface ? "bg-ph-surface" : "bg-ph-canvas",
2315
2805
  collapsed ? "p-1.5" : "p-3"
2316
2806
  ),
2317
- children: /* @__PURE__ */ jsxs17(
2807
+ children: /* @__PURE__ */ jsxs18(
2318
2808
  "div",
2319
2809
  {
2320
2810
  className: cn(
@@ -2323,7 +2813,7 @@ function SuiteSidebar({
2323
2813
  ),
2324
2814
  children: [
2325
2815
  renderNode(userMenu, collapsed),
2326
- !collapsed && footerExtras ? /* @__PURE__ */ jsx21("div", { className: "ml-auto shrink-0", children: renderNode(footerExtras, collapsed) }) : null
2816
+ !collapsed && footerExtras ? /* @__PURE__ */ jsx22("div", { className: "ml-auto shrink-0", children: renderNode(footerExtras, collapsed) }) : null
2327
2817
  ]
2328
2818
  }
2329
2819
  )
@@ -2335,7 +2825,7 @@ function SuiteSidebar({
2335
2825
  }
2336
2826
 
2337
2827
  // src/suite/components/suite-app-layout.tsx
2338
- import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
2828
+ import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
2339
2829
  function SuiteAppLayout({
2340
2830
  sidebar,
2341
2831
  children,
@@ -2343,41 +2833,59 @@ function SuiteAppLayout({
2343
2833
  bottomNav,
2344
2834
  sidebarWidth = 240,
2345
2835
  collapsed = false,
2836
+ lockMainScroll = false,
2346
2837
  onStartResize,
2347
2838
  onResetWidth,
2348
2839
  className
2349
2840
  }) {
2350
- return /* @__PURE__ */ jsxs18("div", { "data-test": "app-shell", className: cn("min-h-screen", className), children: [
2351
- /* @__PURE__ */ jsxs18("div", { className: "hidden lg:flex h-screen overflow-hidden", children: [
2352
- /* @__PURE__ */ jsxs18(
2353
- "aside",
2354
- {
2355
- className: "relative flex shrink-0 flex-col border-r border-ph-border transition-[width] duration-200 ease-out",
2356
- style: { width: `${sidebarWidth}px` },
2357
- children: [
2358
- sidebar,
2359
- onStartResize ? /* @__PURE__ */ jsx22(
2360
- "div",
2361
- {
2362
- role: "separator",
2363
- "aria-orientation": "vertical",
2364
- "aria-label": "Resize sidebar",
2365
- onPointerDown: onStartResize,
2366
- onDoubleClick: onResetWidth,
2367
- 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)]"
2368
- }
2369
- ) : null
2370
- ]
2371
- }
2372
- ),
2373
- /* @__PURE__ */ jsx22("main", { className: "min-w-0 flex-1 overflow-y-auto bg-ph-canvas", children })
2374
- ] }),
2375
- /* @__PURE__ */ jsxs18("div", { className: "flex min-h-screen flex-col lg:hidden", children: [
2376
- mobileHeader ? /* @__PURE__ */ jsx22("div", { className: "shrink-0", children: mobileHeader }) : null,
2377
- /* @__PURE__ */ jsx22("main", { className: "min-w-0 flex-1 overflow-y-auto bg-ph-canvas", children }),
2378
- bottomNav ? /* @__PURE__ */ jsx22("div", { className: "shrink-0", children: bottomNav }) : null
2379
- ] })
2380
- ] });
2841
+ return /* @__PURE__ */ jsxs19(
2842
+ "div",
2843
+ {
2844
+ "data-test": "app-shell",
2845
+ className: cn("flex min-h-dvh lg:h-screen lg:overflow-hidden", className),
2846
+ children: [
2847
+ /* @__PURE__ */ jsxs19(
2848
+ "aside",
2849
+ {
2850
+ className: "relative hidden shrink-0 flex-col overflow-hidden border-r border-ph-border transition-[width] duration-200 ease-out lg:flex",
2851
+ style: { width: `${sidebarWidth}px` },
2852
+ "data-collapsed": collapsed ? "true" : "false",
2853
+ children: [
2854
+ sidebar,
2855
+ onStartResize ? /* @__PURE__ */ jsx23(
2856
+ "div",
2857
+ {
2858
+ role: "separator",
2859
+ "aria-orientation": "vertical",
2860
+ "aria-label": "Resize sidebar",
2861
+ onPointerDown: onStartResize,
2862
+ onDoubleClick: onResetWidth,
2863
+ 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)]"
2864
+ }
2865
+ ) : null
2866
+ ]
2867
+ }
2868
+ ),
2869
+ /* @__PURE__ */ jsxs19("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: [
2870
+ mobileHeader ? /* @__PURE__ */ jsx23("div", { className: "shrink-0 lg:hidden", children: mobileHeader }) : null,
2871
+ /* @__PURE__ */ jsx23(
2872
+ "main",
2873
+ {
2874
+ id: "main-content",
2875
+ tabIndex: -1,
2876
+ "data-lock-scroll": lockMainScroll ? "true" : void 0,
2877
+ className: cn(
2878
+ "flex min-h-0 min-w-0 flex-1 flex-col overflow-x-hidden overscroll-contain bg-ph-canvas focus:outline-none",
2879
+ lockMainScroll ? "overflow-hidden" : "overflow-y-auto"
2880
+ ),
2881
+ children
2882
+ }
2883
+ ),
2884
+ bottomNav ? /* @__PURE__ */ jsx23("div", { className: "shrink-0 lg:hidden", children: bottomNav }) : null
2885
+ ] })
2886
+ ]
2887
+ }
2888
+ );
2381
2889
  }
2382
2890
  export {
2383
2891
  APP_ACCENTS,
@@ -2389,10 +2897,16 @@ export {
2389
2897
  AppSwitcherMark,
2390
2898
  CommandPaletteHost,
2391
2899
  DeferredChrome,
2900
+ SIDEBAR_COLLAPSE_THRESHOLD,
2901
+ SIDEBAR_DEFAULT_WIDTH,
2902
+ SIDEBAR_MAX_WIDTH,
2903
+ SIDEBAR_MIN_EXPANDED_WIDTH,
2904
+ SIDEBAR_RAIL_WIDTH,
2392
2905
  SUITE_APPS2 as SUITE_APPS,
2393
2906
  SUITE_APP_MAP,
2394
2907
  SUITE_GLYPHS,
2395
2908
  SUITE_ICON_NAMES,
2909
+ SUITE_OPEN_ASK_AI_EVENT,
2396
2910
  SUITE_SPRINGS,
2397
2911
  SourceAppGlyph,
2398
2912
  SuiteAiPanel,
@@ -2410,6 +2924,7 @@ export {
2410
2924
  SuitePage,
2411
2925
  SuitePageHeader,
2412
2926
  SuiteSectionHeader,
2927
+ SuiteSettingsLayout,
2413
2928
  SuiteSettingsMobileNav,
2414
2929
  SuiteSidebar,
2415
2930
  SuiteSkeleton,
@@ -2419,11 +2934,15 @@ export {
2419
2934
  SuiteThemeProvider,
2420
2935
  SuiteToolbar,
2421
2936
  SuiteUserMenu,
2937
+ TodayAskAiCard,
2422
2938
  TodayCalibrating,
2939
+ TodayEmptyAction,
2423
2940
  TodayEmptyState,
2424
2941
  TodayHero,
2942
+ TodayLayout,
2425
2943
  TodayPageFrame,
2426
2944
  TodayPanel,
2945
+ TodayPrimaryAction,
2427
2946
  TodaySection,
2428
2947
  TodayTimeIcon,
2429
2948
  TodayTopBar,
@@ -2433,9 +2952,12 @@ export {
2433
2952
  cn,
2434
2953
  getTodayGreeting,
2435
2954
  m,
2955
+ openSuiteAskAi,
2956
+ resolveSuiteNotificationHref,
2436
2957
  sourceToSuiteApp,
2437
2958
  suiteAppBaseUrl,
2438
2959
  suiteAppLabel,
2439
2960
  useIdleMount,
2961
+ useSidebarWidth,
2440
2962
  useSuiteTheme
2441
2963
  };