@xenide-io/the-old-ui-theme 0.6.7 → 0.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/suite.js CHANGED
@@ -166,8 +166,8 @@ function SuiteAiBlockNoteMessage({
166
166
  markdown
167
167
  }) {
168
168
  const { resolvedTheme } = useSuiteTheme();
169
- const editor = (0, import_react9.useCreateBlockNote)({ schema: assistantSchema });
170
- (0, import_react8.useEffect)(() => {
169
+ const editor = (0, import_react10.useCreateBlockNote)({ schema: assistantSchema });
170
+ (0, import_react9.useEffect)(() => {
171
171
  try {
172
172
  const blocks = editor.tryParseMarkdownToBlocks(markdown);
173
173
  editor.replaceBlocks(
@@ -180,8 +180,8 @@ function SuiteAiBlockNoteMessage({
180
180
  ]);
181
181
  }
182
182
  }, [editor, markdown]);
183
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
184
- import_react9.BlockNoteViewRaw,
183
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
184
+ import_react10.BlockNoteViewRaw,
185
185
  {
186
186
  editor,
187
187
  editable: false,
@@ -197,17 +197,17 @@ function SuiteAiBlockNoteMessage({
197
197
  }
198
198
  );
199
199
  }
200
- var import_react8, import_core, import_react9, import_style, import_jsx_runtime19, assistantSchema;
200
+ var import_react9, import_core, import_react10, import_style, import_jsx_runtime20, assistantSchema;
201
201
  var init_ai_message_blocknote = __esm({
202
202
  "src/suite/components/ai-message-blocknote.tsx"() {
203
203
  "use strict";
204
204
  "use client";
205
- import_react8 = require("react");
205
+ import_react9 = require("react");
206
206
  import_core = require("@blocknote/core");
207
- import_react9 = require("@blocknote/react");
207
+ import_react10 = require("@blocknote/react");
208
208
  import_style = require("@blocknote/react/style.css");
209
209
  init_theme_provider();
210
- import_jsx_runtime19 = require("react/jsx-runtime");
210
+ import_jsx_runtime20 = require("react/jsx-runtime");
211
211
  assistantSchema = import_core.BlockNoteSchema.create({
212
212
  blockSpecs: {
213
213
  paragraph: import_core.defaultBlockSpecs.paragraph,
@@ -273,6 +273,7 @@ __export(suite_exports, {
273
273
  SuiteThemeProvider: () => SuiteThemeProvider,
274
274
  SuiteToolbar: () => SuiteToolbar,
275
275
  SuiteUserMenu: () => SuiteUserMenu,
276
+ SuiteWorkspaceSwitcher: () => SuiteWorkspaceSwitcher,
276
277
  TodayAskAiCard: () => TodayAskAiCard,
277
278
  TodayCalibrating: () => TodayCalibrating,
278
279
  TodayEmptyAction: () => TodayEmptyAction,
@@ -288,11 +289,14 @@ __export(suite_exports, {
288
289
  appSwitcherMarkClass: () => appSwitcherMarkClass,
289
290
  appSwitcherMenuItemClass: () => appSwitcherMenuItemClass,
290
291
  appSwitcherTriggerClass: () => appSwitcherTriggerClass,
292
+ classifySuiteHref: () => classifySuiteHref,
291
293
  cn: () => cn,
294
+ consumePendingAskAiPrompt: () => consumePendingAskAiPrompt,
292
295
  getTodayGreeting: () => getTodayGreeting,
293
- m: () => import_react12.m,
296
+ m: () => import_react14.m,
294
297
  openSuiteAskAi: () => openSuiteAskAi,
295
298
  resolveSuiteNotificationHref: () => resolveSuiteNotificationHref,
299
+ sidebarColumnWidth: () => sidebarColumnWidth,
296
300
  sourceToSuiteApp: () => sourceToSuiteApp,
297
301
  suiteAppBaseUrl: () => suiteAppBaseUrl,
298
302
  suiteAppLabel: () => suiteAppLabel,
@@ -1088,7 +1092,7 @@ function TodayLayout({
1088
1092
  );
1089
1093
  }
1090
1094
  function TodayAskAiCard({
1091
- title = "Ask AI",
1095
+ title = "Shelly AI",
1092
1096
  description = "Ask about your workspace, draft something, or look a fact up online.",
1093
1097
  cta = "Ask",
1094
1098
  className,
@@ -1421,6 +1425,31 @@ function resolveSuiteNotificationHref(href, sourceApp) {
1421
1425
  if (!slug) return path;
1422
1426
  return `${suiteAppBaseUrl(slug)}${path}`;
1423
1427
  }
1428
+ function classifySuiteHref(href, bases, currentOrigin = "") {
1429
+ let url;
1430
+ try {
1431
+ url = new URL(href, currentOrigin || "http://local.invalid");
1432
+ } catch (e) {
1433
+ return null;
1434
+ }
1435
+ if (url.protocol !== "http:" && url.protocol !== "https:") return null;
1436
+ for (const slug of Object.keys(bases)) {
1437
+ const base = bases[slug];
1438
+ if (!base) continue;
1439
+ try {
1440
+ if (new URL(base).origin !== url.origin) continue;
1441
+ } catch (e) {
1442
+ continue;
1443
+ }
1444
+ const path = `${url.pathname}${url.search}${url.hash}` || "/";
1445
+ return {
1446
+ slug,
1447
+ path,
1448
+ sameApp: Boolean(currentOrigin && url.origin === currentOrigin)
1449
+ };
1450
+ }
1451
+ return null;
1452
+ }
1424
1453
 
1425
1454
  // src/suite/components/suite-notification-bell.tsx
1426
1455
  var import_jsx_runtime7 = require("react/jsx-runtime");
@@ -2565,12 +2594,152 @@ function TodayCalibrating({
2565
2594
  init_theme_provider();
2566
2595
 
2567
2596
  // src/suite/components/ai-panel.tsx
2568
- var import_react10 = require("react");
2597
+ var import_react11 = require("react");
2569
2598
  var import_iconoir_react8 = require("iconoir-react");
2570
- var import_jsx_runtime20 = require("react/jsx-runtime");
2571
- var SuiteAiBlockNoteMessage2 = (0, import_react10.lazy)(() => Promise.resolve().then(() => (init_ai_message_blocknote(), ai_message_blocknote_exports)));
2599
+
2600
+ // src/components/ui/Tooltip.tsx
2601
+ var import_react8 = require("react");
2602
+ var import_react_dom = require("react-dom");
2603
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2604
+ var TooltipDelayContext = (0, import_react8.createContext)(0);
2605
+ function tooltipPosition(trigger, side, align, offset) {
2606
+ const horizontal = align === "start" ? trigger.left : align === "end" ? trigger.right : trigger.left + trigger.width / 2;
2607
+ const vertical = align === "start" ? trigger.top : align === "end" ? trigger.bottom : trigger.top + trigger.height / 2;
2608
+ if (side === "top")
2609
+ return {
2610
+ left: horizontal,
2611
+ top: trigger.top - offset,
2612
+ transform: "translate(-50%, -100%)"
2613
+ };
2614
+ if (side === "bottom")
2615
+ return {
2616
+ left: horizontal,
2617
+ top: trigger.bottom + offset,
2618
+ transform: "translate(-50%, 0)"
2619
+ };
2620
+ if (side === "left")
2621
+ return {
2622
+ left: trigger.left - offset,
2623
+ top: vertical,
2624
+ transform: "translate(-100%, -50%)"
2625
+ };
2626
+ return {
2627
+ left: trigger.right + offset,
2628
+ top: vertical,
2629
+ transform: "translate(0, -50%)"
2630
+ };
2631
+ }
2632
+ function Tooltip({
2633
+ content,
2634
+ children,
2635
+ side = "top",
2636
+ align = "center",
2637
+ sideOffset = 7,
2638
+ className,
2639
+ open,
2640
+ defaultOpen = false,
2641
+ onOpenChange,
2642
+ delayDuration
2643
+ }) {
2644
+ const providerDelay = (0, import_react8.useContext)(TooltipDelayContext);
2645
+ const generatedId = (0, import_react8.useId)().replace(/:/g, "");
2646
+ const triggerRef = (0, import_react8.useRef)(null);
2647
+ const timerRef = (0, import_react8.useRef)(null);
2648
+ const [internalOpen, setInternalOpen] = (0, import_react8.useState)(defaultOpen);
2649
+ const [position, setPosition] = (0, import_react8.useState)(null);
2650
+ const isOpen = open != null ? open : internalOpen;
2651
+ const triggerId = children.props.id;
2652
+ const tooltipId = triggerId ? `${triggerId}-tooltip` : `ph-tooltip-${generatedId}`;
2653
+ function setOpen(next) {
2654
+ if (open === void 0) setInternalOpen(next);
2655
+ onOpenChange == null ? void 0 : onOpenChange(next);
2656
+ }
2657
+ function clearTimer() {
2658
+ if (timerRef.current) clearTimeout(timerRef.current);
2659
+ timerRef.current = null;
2660
+ }
2661
+ function updatePosition() {
2662
+ if (!triggerRef.current) return;
2663
+ setPosition(
2664
+ tooltipPosition(
2665
+ triggerRef.current.getBoundingClientRect(),
2666
+ side,
2667
+ align,
2668
+ sideOffset
2669
+ )
2670
+ );
2671
+ }
2672
+ function show(immediate) {
2673
+ clearTimer();
2674
+ updatePosition();
2675
+ const delay = immediate ? 0 : delayDuration != null ? delayDuration : providerDelay;
2676
+ if (!delay) setOpen(true);
2677
+ else timerRef.current = setTimeout(() => setOpen(true), delay);
2678
+ }
2679
+ function hide() {
2680
+ clearTimer();
2681
+ setOpen(false);
2682
+ }
2683
+ (0, import_react8.useEffect)(() => {
2684
+ if (!isOpen) return;
2685
+ const reposition = () => updatePosition();
2686
+ window.addEventListener("resize", reposition);
2687
+ window.addEventListener("scroll", reposition, true);
2688
+ return () => {
2689
+ window.removeEventListener("resize", reposition);
2690
+ window.removeEventListener("scroll", reposition, true);
2691
+ };
2692
+ });
2693
+ (0, import_react8.useEffect)(() => () => clearTimer(), []);
2694
+ const describedBy = [children.props["aria-describedby"], tooltipId].filter(Boolean).join(" ");
2695
+ const trigger = (0, import_react8.cloneElement)(children, { "aria-describedby": describedBy });
2696
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
2697
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2698
+ "span",
2699
+ {
2700
+ ref: triggerRef,
2701
+ className: "inline-flex",
2702
+ onMouseEnter: () => show(false),
2703
+ onMouseLeave: hide,
2704
+ onFocusCapture: () => show(true),
2705
+ onBlurCapture: hide,
2706
+ onKeyDown: (event) => {
2707
+ if (event.key === "Escape") hide();
2708
+ },
2709
+ children: trigger
2710
+ }
2711
+ ),
2712
+ isOpen && position && typeof document !== "undefined" ? (0, import_react_dom.createPortal)(
2713
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2714
+ "span",
2715
+ {
2716
+ style: __spreadValues({ position: "fixed" }, position),
2717
+ className: "pointer-events-none z-[200]",
2718
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2719
+ "span",
2720
+ {
2721
+ id: tooltipId,
2722
+ role: "tooltip",
2723
+ "data-side": side,
2724
+ className: cn2("ph-tooltip-content", className),
2725
+ children: content
2726
+ }
2727
+ )
2728
+ }
2729
+ ),
2730
+ document.body
2731
+ ) : null
2732
+ ] });
2733
+ }
2734
+
2735
+ // src/suite/components/ai-panel.tsx
2736
+ var import_jsx_runtime21 = require("react/jsx-runtime");
2737
+ var SuiteAiBlockNoteMessage2 = (0, import_react11.lazy)(() => Promise.resolve().then(() => (init_ai_message_blocknote(), ai_message_blocknote_exports)));
2572
2738
  var SUITE_OPEN_ASK_AI_EVENT = "shellstack:open-ask-ai";
2739
+ var pendingAskAiPrompt = null;
2573
2740
  function openSuiteAskAi(detail) {
2741
+ var _a;
2742
+ pendingAskAiPrompt = ((_a = detail == null ? void 0 : detail.prompt) == null ? void 0 : _a.trim()) || null;
2574
2743
  if (typeof window === "undefined") return;
2575
2744
  window.dispatchEvent(
2576
2745
  new CustomEvent(SUITE_OPEN_ASK_AI_EVENT, {
@@ -2578,138 +2747,72 @@ function openSuiteAskAi(detail) {
2578
2747
  })
2579
2748
  );
2580
2749
  }
2581
- var LAUNCHER_EDGE_GAP = 12;
2582
- var LAUNCHER_STORAGE_KEY = "suite-ask-ai-launcher-position";
2583
- function resolveSuiteAiLauncherPosition(edge, desiredX, desiredY, viewportWidth, viewportHeight, launcherWidth, launcherHeight) {
2584
- const maxX = Math.max(
2585
- LAUNCHER_EDGE_GAP,
2586
- viewportWidth - launcherWidth - LAUNCHER_EDGE_GAP
2587
- );
2588
- const maxY = Math.max(
2589
- LAUNCHER_EDGE_GAP,
2590
- viewportHeight - launcherHeight - LAUNCHER_EDGE_GAP
2591
- );
2592
- const clampX = (x) => Math.min(Math.max(x, LAUNCHER_EDGE_GAP), maxX);
2593
- const clampY = (y) => Math.min(Math.max(y, LAUNCHER_EDGE_GAP), maxY);
2594
- switch (edge) {
2595
- case "left":
2596
- return { x: LAUNCHER_EDGE_GAP, y: clampY(desiredY) };
2597
- case "right":
2598
- return { x: maxX, y: clampY(desiredY) };
2599
- case "top":
2600
- return { x: clampX(desiredX), y: LAUNCHER_EDGE_GAP };
2601
- case "bottom":
2602
- return { x: clampX(desiredX), y: maxY };
2603
- }
2604
- }
2605
- function resolveSuiteAiNearestEdge(x, y, width, height, viewportWidth, viewportHeight) {
2606
- const centerX = x + width / 2;
2607
- const centerY = y + height / 2;
2608
- const candidates = [
2609
- { edge: "left", distance: centerX },
2610
- { edge: "right", distance: viewportWidth - centerX },
2611
- { edge: "top", distance: centerY },
2612
- { edge: "bottom", distance: viewportHeight - centerY }
2613
- ];
2614
- return candidates.reduce(
2615
- (nearest, candidate) => candidate.distance < nearest.distance ? candidate : nearest
2616
- ).edge;
2617
- }
2618
- function resolveSuiteAiPanelSide(edge, x, viewportWidth) {
2619
- if (edge === "left") return "left";
2620
- if (edge === "right") return "right";
2621
- return x < viewportWidth / 2 ? "left" : "right";
2750
+ function consumePendingAskAiPrompt() {
2751
+ const next = pendingAskAiPrompt;
2752
+ pendingAskAiPrompt = null;
2753
+ return next;
2622
2754
  }
2623
2755
  function SuiteAiPanel({
2624
2756
  presets = [],
2625
2757
  emptyState = "Ask anything about your workspace, or look something up online.",
2626
- title = "Ask AI",
2758
+ title = "Shelly AI",
2627
2759
  fetchChat,
2628
2760
  sendMessage,
2629
2761
  clearChat,
2630
2762
  brandIcon: BrandIcon,
2631
2763
  spinner: Spinner,
2632
- hideLauncher = false
2764
+ open: openProp,
2765
+ onOpenChange,
2766
+ suiteAppBases,
2767
+ onSwitchApp
2633
2768
  }) {
2634
- const [open, setOpen] = (0, import_react10.useState)(false);
2635
- const [prompt, setPrompt] = (0, import_react10.useState)("");
2636
- const [messages, setMessages] = (0, import_react10.useState)([]);
2637
- const [loading, setLoading] = (0, import_react10.useState)(false);
2638
- const [hydrating, setHydrating] = (0, import_react10.useState)(false);
2639
- const [error, setError] = (0, import_react10.useState)("");
2640
- const [copiedId, setCopiedId] = (0, import_react10.useState)(null);
2641
- const [atBottom, setAtBottom] = (0, import_react10.useState)(true);
2642
- const [launcherSide, setLauncherSide] = (0, import_react10.useState)("right");
2643
- const [panelSide, setPanelSide] = (0, import_react10.useState)("right");
2644
- const [launcherPosition, setLauncherPosition] = (0, import_react10.useState)(null);
2645
- const [draggingLauncher, setDraggingLauncher] = (0, import_react10.useState)(false);
2646
- const scrollerRef = (0, import_react10.useRef)(null);
2647
- const textareaRef = (0, import_react10.useRef)(null);
2648
- const abortRef = (0, import_react10.useRef)(null);
2649
- const launcherRef = (0, import_react10.useRef)(null);
2650
- const launcherSideRef = (0, import_react10.useRef)("right");
2651
- const launcherPositionRef = (0, import_react10.useRef)(null);
2652
- const launcherDragRef = (0, import_react10.useRef)(null);
2653
- const suppressLauncherClickRef = (0, import_react10.useRef)(false);
2654
- const pendingPromptRef = (0, import_react10.useRef)(null);
2655
- (0, import_react10.useEffect)(() => {
2656
- const launcher = launcherRef.current;
2657
- if (!launcher) return;
2658
- let side = "right";
2659
- let desiredX = LAUNCHER_EDGE_GAP;
2660
- let desiredY = window.innerHeight - launcher.offsetHeight - 20;
2661
- try {
2662
- const stored = JSON.parse(
2663
- localStorage.getItem(LAUNCHER_STORAGE_KEY) || "null"
2664
- );
2665
- if ((stored == null ? void 0 : stored.side) === "top" || (stored == null ? void 0 : stored.side) === "right" || (stored == null ? void 0 : stored.side) === "bottom" || (stored == null ? void 0 : stored.side) === "left")
2666
- side = stored.side;
2667
- if (typeof (stored == null ? void 0 : stored.x) === "number") desiredX = stored.x;
2668
- if (typeof (stored == null ? void 0 : stored.y) === "number") desiredY = stored.y;
2669
- } catch (e) {
2769
+ const inputId = (0, import_react11.useId)();
2770
+ const [uncontrolledOpen, setUncontrolledOpen] = (0, import_react11.useState)(false);
2771
+ const open = openProp != null ? openProp : uncontrolledOpen;
2772
+ const setOpen = (0, import_react11.useCallback)(
2773
+ (next) => {
2774
+ if (openProp === void 0) setUncontrolledOpen(next);
2775
+ onOpenChange == null ? void 0 : onOpenChange(next);
2776
+ },
2777
+ [onOpenChange, openProp]
2778
+ );
2779
+ const [prompt, setPrompt] = (0, import_react11.useState)("");
2780
+ const [messages, setMessages] = (0, import_react11.useState)([]);
2781
+ const [loading, setLoading] = (0, import_react11.useState)(false);
2782
+ const [hydrating, setHydrating] = (0, import_react11.useState)(false);
2783
+ const [error, setError] = (0, import_react11.useState)("");
2784
+ const [atBottom, setAtBottom] = (0, import_react11.useState)(true);
2785
+ const scrollerRef = (0, import_react11.useRef)(null);
2786
+ const textareaRef = (0, import_react11.useRef)(null);
2787
+ const abortRef = (0, import_react11.useRef)(null);
2788
+ const pendingPromptRef = (0, import_react11.useRef)(null);
2789
+ (0, import_react11.useEffect)(() => {
2790
+ function onOpen(event) {
2791
+ var _a;
2792
+ const detail = event.detail;
2793
+ const queued = ((_a = detail == null ? void 0 : detail.prompt) == null ? void 0 : _a.trim()) || consumePendingAskAiPrompt();
2794
+ setOpen(true);
2795
+ if (queued) {
2796
+ pendingPromptRef.current = queued;
2797
+ setPrompt(queued);
2798
+ }
2670
2799
  }
2671
- launcherSideRef.current = side;
2672
- setLauncherSide(side);
2673
- const placeLauncher = () => {
2674
- var _a, _b, _c, _d;
2675
- const element = launcherRef.current;
2676
- if (!element) return;
2677
- const next = resolveSuiteAiLauncherPosition(
2678
- launcherSideRef.current,
2679
- (_b = (_a = launcherPositionRef.current) == null ? void 0 : _a.x) != null ? _b : desiredX,
2680
- (_d = (_c = launcherPositionRef.current) == null ? void 0 : _c.y) != null ? _d : desiredY,
2681
- window.innerWidth,
2682
- window.innerHeight,
2683
- element.offsetWidth,
2684
- element.offsetHeight
2685
- );
2686
- launcherPositionRef.current = next;
2687
- setLauncherPosition(next);
2688
- setPanelSide(
2689
- resolveSuiteAiPanelSide(
2690
- launcherSideRef.current,
2691
- next.x,
2692
- window.innerWidth
2693
- )
2694
- );
2695
- };
2696
- placeLauncher();
2697
- window.addEventListener("resize", placeLauncher);
2698
- return () => window.removeEventListener("resize", placeLauncher);
2699
- }, []);
2700
- const scrollToBottom = (0, import_react10.useCallback)((behavior = "auto") => {
2800
+ window.addEventListener(SUITE_OPEN_ASK_AI_EVENT, onOpen);
2801
+ return () => window.removeEventListener(SUITE_OPEN_ASK_AI_EVENT, onOpen);
2802
+ }, [setOpen]);
2803
+ const scrollToBottom = (0, import_react11.useCallback)((behavior = "auto") => {
2701
2804
  const el = scrollerRef.current;
2702
2805
  if (!el) return;
2703
2806
  el.scrollTo({ top: el.scrollHeight, behavior });
2704
2807
  setAtBottom(true);
2705
2808
  }, []);
2706
- const handleScroll = (0, import_react10.useCallback)(() => {
2809
+ const handleScroll = (0, import_react11.useCallback)(() => {
2707
2810
  const el = scrollerRef.current;
2708
2811
  if (!el) return;
2709
2812
  const distance = el.scrollHeight - el.scrollTop - el.clientHeight;
2710
2813
  setAtBottom(distance < 64);
2711
2814
  }, []);
2712
- const hydrate = (0, import_react10.useCallback)(async () => {
2815
+ const hydrate = (0, import_react11.useCallback)(async () => {
2713
2816
  var _a;
2714
2817
  setHydrating(true);
2715
2818
  setError("");
@@ -2721,51 +2824,31 @@ function SuiteAiPanel({
2721
2824
  }
2722
2825
  } catch (err) {
2723
2826
  setError(
2724
- err instanceof Error ? err.message : "Could not load your Ask AI chat."
2827
+ err instanceof Error ? err.message : "Could not load Shelly AI."
2725
2828
  );
2726
2829
  } finally {
2727
2830
  setHydrating(false);
2728
2831
  }
2729
2832
  }, [fetchChat]);
2730
- (0, import_react10.useEffect)(() => {
2731
- function onOpen(event) {
2732
- var _a;
2733
- const detail = event.detail;
2734
- setOpen(true);
2735
- if ((_a = detail == null ? void 0 : detail.prompt) == null ? void 0 : _a.trim()) {
2736
- pendingPromptRef.current = detail.prompt.trim();
2737
- setPrompt(detail.prompt.trim());
2738
- }
2739
- }
2740
- window.addEventListener(SUITE_OPEN_ASK_AI_EVENT, onOpen);
2741
- return () => window.removeEventListener(SUITE_OPEN_ASK_AI_EVENT, onOpen);
2742
- }, []);
2743
- (0, import_react10.useEffect)(() => {
2833
+ (0, import_react11.useEffect)(() => {
2834
+ var _a;
2744
2835
  if (!open) return;
2836
+ const queued = (_a = pendingPromptRef.current) != null ? _a : consumePendingAskAiPrompt();
2837
+ pendingPromptRef.current = queued;
2745
2838
  void hydrate().then(() => {
2746
2839
  const pending = pendingPromptRef.current;
2747
2840
  pendingPromptRef.current = null;
2748
- if (pending) {
2749
- setPrompt(pending);
2750
- }
2841
+ if (pending) setPrompt(pending);
2751
2842
  });
2752
2843
  }, [open, hydrate]);
2753
- (0, import_react10.useEffect)(() => {
2844
+ (0, import_react11.useEffect)(() => {
2754
2845
  if (open && atBottom) scrollToBottom();
2755
2846
  }, [messages, loading, open, atBottom, scrollToBottom]);
2756
- (0, import_react10.useEffect)(() => {
2847
+ (0, import_react11.useEffect)(() => {
2757
2848
  var _a;
2758
2849
  if (open && !hydrating) (_a = textareaRef.current) == null ? void 0 : _a.focus();
2759
2850
  }, [open, hydrating]);
2760
- (0, import_react10.useEffect)(() => {
2761
- if (!open) return;
2762
- function onKey(event) {
2763
- if (event.key === "Escape") setOpen(false);
2764
- }
2765
- window.addEventListener("keydown", onKey);
2766
- return () => window.removeEventListener("keydown", onKey);
2767
- }, [open]);
2768
- (0, import_react10.useLayoutEffect)(() => {
2851
+ (0, import_react11.useLayoutEffect)(() => {
2769
2852
  const el = textareaRef.current;
2770
2853
  if (!el) return;
2771
2854
  el.style.height = "auto";
@@ -2814,7 +2897,7 @@ function SuiteAiPanel({
2814
2897
  }
2815
2898
  function retry() {
2816
2899
  var _a;
2817
- const lastUser = (_a = [...messages].reverse().find((m2) => m2.role === "user")) == null ? void 0 : _a.content;
2900
+ const lastUser = (_a = [...messages].reverse().find((message) => message.role === "user")) == null ? void 0 : _a.content;
2818
2901
  setError("");
2819
2902
  if (lastUser) void ask(lastUser);
2820
2903
  else void hydrate();
@@ -2831,417 +2914,346 @@ function SuiteAiPanel({
2831
2914
  );
2832
2915
  }
2833
2916
  }
2834
- async function copyMessage(index, content) {
2835
- try {
2836
- await navigator.clipboard.writeText(content);
2837
- setCopiedId(index);
2838
- window.setTimeout(() => setCopiedId(null), 1500);
2839
- } catch (e) {
2840
- }
2841
- }
2842
- function saveLauncherPosition(edge, position) {
2843
- try {
2844
- localStorage.setItem(
2845
- LAUNCHER_STORAGE_KEY,
2846
- JSON.stringify({ side: edge, x: position.x, y: position.y })
2847
- );
2848
- } catch (e) {
2849
- }
2850
- }
2851
- function dockLauncher(edge, desiredX, desiredY) {
2852
- const launcher = launcherRef.current;
2853
- if (!launcher) return;
2854
- const next = resolveSuiteAiLauncherPosition(
2855
- edge,
2856
- desiredX,
2857
- desiredY,
2858
- window.innerWidth,
2859
- window.innerHeight,
2860
- launcher.offsetWidth,
2861
- launcher.offsetHeight
2862
- );
2863
- launcherSideRef.current = edge;
2864
- launcherPositionRef.current = next;
2865
- setLauncherSide(edge);
2866
- setLauncherPosition(next);
2867
- setPanelSide(resolveSuiteAiPanelSide(edge, next.x, window.innerWidth));
2868
- saveLauncherPosition(edge, next);
2869
- }
2870
- function startLauncherDrag(event) {
2871
- if (event.pointerType === "mouse" && event.button !== 0) return;
2872
- const rect = event.currentTarget.getBoundingClientRect();
2873
- launcherDragRef.current = {
2874
- pointerId: event.pointerId,
2875
- offsetX: event.clientX - rect.left,
2876
- offsetY: event.clientY - rect.top,
2877
- startX: event.clientX,
2878
- startY: event.clientY
2879
- };
2880
- suppressLauncherClickRef.current = false;
2881
- event.currentTarget.setPointerCapture(event.pointerId);
2882
- setDraggingLauncher(true);
2883
- }
2884
- function moveLauncher(event) {
2885
- const drag = launcherDragRef.current;
2886
- if (!drag || drag.pointerId !== event.pointerId) return;
2887
- const launcher = event.currentTarget;
2888
- const maxX = Math.max(
2889
- LAUNCHER_EDGE_GAP,
2890
- window.innerWidth - launcher.offsetWidth - LAUNCHER_EDGE_GAP
2891
- );
2892
- const maxY = Math.max(
2893
- LAUNCHER_EDGE_GAP,
2894
- window.innerHeight - launcher.offsetHeight - LAUNCHER_EDGE_GAP
2895
- );
2896
- const next = {
2897
- x: Math.min(
2898
- Math.max(event.clientX - drag.offsetX, LAUNCHER_EDGE_GAP),
2899
- maxX
2900
- ),
2901
- y: Math.min(
2902
- Math.max(event.clientY - drag.offsetY, LAUNCHER_EDGE_GAP),
2903
- maxY
2904
- )
2905
- };
2906
- if (Math.hypot(event.clientX - drag.startX, event.clientY - drag.startY) > 4) {
2907
- suppressLauncherClickRef.current = true;
2917
+ function onConversationClick(event) {
2918
+ var _a;
2919
+ if (event.defaultPrevented) return;
2920
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
2921
+ return;
2908
2922
  }
2909
- launcherPositionRef.current = next;
2910
- setLauncherPosition(next);
2911
- }
2912
- function finishLauncherDrag(event) {
2913
- var _a, _b, _c, _d;
2914
- const drag = launcherDragRef.current;
2915
- if (!drag || drag.pointerId !== event.pointerId) return;
2916
- if (event.currentTarget.hasPointerCapture(event.pointerId)) {
2917
- event.currentTarget.releasePointerCapture(event.pointerId);
2918
- }
2919
- launcherDragRef.current = null;
2920
- setDraggingLauncher(false);
2921
- const rect = event.currentTarget.getBoundingClientRect();
2922
- const currentX = (_b = (_a = launcherPositionRef.current) == null ? void 0 : _a.x) != null ? _b : rect.left;
2923
- const currentY = (_d = (_c = launcherPositionRef.current) == null ? void 0 : _c.y) != null ? _d : rect.top;
2924
- const edge = resolveSuiteAiNearestEdge(
2925
- currentX,
2926
- currentY,
2927
- rect.width,
2928
- rect.height,
2929
- window.innerWidth,
2930
- window.innerHeight
2923
+ const anchor = (_a = event.target) == null ? void 0 : _a.closest("a");
2924
+ if (!anchor || !suiteAppBases) return;
2925
+ const href = anchor.href;
2926
+ if (!href) return;
2927
+ const match = classifySuiteHref(
2928
+ href,
2929
+ suiteAppBases,
2930
+ window.location.origin
2931
2931
  );
2932
- dockLauncher(edge, currentX, currentY);
2933
- }
2934
- function moveLauncherWithKeyboard(event) {
2935
- var _a, _b, _c, _d;
2936
- const edgeByKey = {
2937
- ArrowLeft: "left",
2938
- ArrowRight: "right",
2939
- ArrowUp: "top",
2940
- ArrowDown: "bottom"
2941
- };
2942
- const edge = edgeByKey[event.key];
2943
- if (!edge) return;
2932
+ if (!match) return;
2944
2933
  event.preventDefault();
2945
- const rect = event.currentTarget.getBoundingClientRect();
2946
- const currentX = (_b = (_a = launcherPositionRef.current) == null ? void 0 : _a.x) != null ? _b : rect.left;
2947
- const currentY = (_d = (_c = launcherPositionRef.current) == null ? void 0 : _c.y) != null ? _d : rect.top;
2948
- dockLauncher(edge, currentX, currentY);
2934
+ if (match.sameApp) {
2935
+ window.location.assign(match.path);
2936
+ return;
2937
+ }
2938
+ if (onSwitchApp) onSwitchApp(match.slug, match.path);
2939
+ else window.location.assign(anchor.href);
2949
2940
  }
2941
+ if (!open) return null;
2950
2942
  const Icon = BrandIcon != null ? BrandIcon : import_iconoir_react8.Sparks;
2951
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
2952
- !hideLauncher ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
2953
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
2954
- "button",
2955
- {
2956
- ref: launcherRef,
2957
- type: "button",
2958
- "data-test": "ai-panel-launcher",
2959
- onClick: () => {
2960
- if (suppressLauncherClickRef.current) {
2961
- suppressLauncherClickRef.current = false;
2962
- return;
2963
- }
2964
- setOpen(true);
2965
- },
2966
- onPointerDown: startLauncherDrag,
2967
- onPointerMove: moveLauncher,
2968
- onPointerUp: finishLauncherDrag,
2969
- onPointerCancel: finishLauncherDrag,
2970
- onKeyDown: moveLauncherWithKeyboard,
2971
- className: `fixed z-30 flex touch-none select-none items-center gap-2 rounded-full border border-ph-border bg-ph-surface px-4 py-2.5 text-sm font-medium text-ph-ink shadow-ph hover:bg-ph-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand ${draggingLauncher ? "cursor-grabbing" : "cursor-grab"}`,
2972
- style: launcherPosition ? { left: launcherPosition.x, top: launcherPosition.y } : { bottom: 20, right: 20 },
2973
- "aria-label": "Open Ask AI",
2974
- "aria-describedby": "suite-ask-ai-launcher-help",
2975
- children: [
2976
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Icon, { className: "h-4 w-4 text-ph-brand" }),
2977
- "Ask AI"
2978
- ]
2979
- }
2980
- ),
2981
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { id: "suite-ask-ai-launcher-help", className: "sr-only", children: "Drag to reposition, or use arrow keys. The launcher docks to the nearest screen edge." })
2982
- ] }) : null,
2983
- open ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
2984
- "div",
2985
- {
2986
- className: "fixed inset-0 z-40",
2987
- role: "dialog",
2988
- "aria-modal": "true",
2989
- "aria-label": title,
2990
- children: [
2991
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2943
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2944
+ "aside",
2945
+ {
2946
+ className: "flex h-full min-h-0 flex-col bg-ph-canvas",
2947
+ "data-test": "suite-ask-ai-panel",
2948
+ "aria-label": title,
2949
+ children: [
2950
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("header", { className: "flex items-center gap-2 px-2 pb-2", children: [
2951
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h2", { className: "sr-only", children: title }),
2952
+ messages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2992
2953
  "button",
2993
2954
  {
2994
2955
  type: "button",
2995
- className: "absolute inset-0 bg-ph-ink/25",
2996
- onClick: () => setOpen(false),
2997
- "aria-label": "Close Ask AI",
2998
- tabIndex: -1
2956
+ onClick: () => void handleClear(),
2957
+ disabled: loading,
2958
+ className: "ml-auto inline-flex h-9 items-center gap-1.5 rounded-lg px-2 text-xs font-medium text-ph-subtle transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-focus disabled:opacity-40",
2959
+ "data-test": "ask-ai-clear",
2960
+ children: [
2961
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_iconoir_react8.Erase, { className: "h-3.5 w-3.5" }),
2962
+ "Clear"
2963
+ ]
2999
2964
  }
3000
- ),
3001
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
3002
- "aside",
2965
+ ) : null
2966
+ ] }),
2967
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "relative min-h-0 flex-1", children: [
2968
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2969
+ "div",
3003
2970
  {
3004
- className: `absolute inset-y-0 flex w-full flex-col bg-ph-canvas shadow-2xl sm:max-w-xl ${panelSide === "left" ? "left-0 border-r border-ph-border" : "right-0 border-l border-ph-border"}`,
3005
- "data-test": "suite-ask-ai-panel",
2971
+ ref: scrollerRef,
2972
+ onScroll: handleScroll,
2973
+ onClick: onConversationClick,
2974
+ role: "log",
2975
+ "aria-live": "polite",
2976
+ "aria-label": "Conversation",
2977
+ className: "suite-scroll-lock h-full space-y-5 overflow-y-auto px-2 py-3",
3006
2978
  children: [
3007
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("header", { className: "flex items-center gap-2 px-4 py-3", children: [
3008
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h2", { className: "min-w-0 flex-1 truncate text-sm font-semibold text-ph-ink", children: title }),
3009
- messages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
2979
+ hydrating && messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2 text-sm text-ph-subtle", children: [
2980
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Spinner, { className: "h-4 w-4 animate-spin" }),
2981
+ "Loading your chat\u2026"
2982
+ ] }) : null,
2983
+ !hydrating && messages.length === 0 && !error ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "mx-auto mt-4 flex max-w-sm flex-col items-center px-1 text-center", children: [
2984
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex h-10 w-10 items-center justify-center rounded-2xl bg-ph-brand/10 text-ph-brand", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Icon, { className: "h-5 w-5" }) }),
2985
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "mt-3 font-display text-base font-semibold text-ph-ink", children: "How can I help?" }),
2986
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "mt-1 text-sm leading-relaxed text-ph-subtle", children: emptyState }),
2987
+ presets.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "mt-4 flex w-full flex-col gap-2", children: presets.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
3010
2988
  "button",
3011
2989
  {
3012
2990
  type: "button",
3013
- onClick: () => void handleClear(),
3014
- disabled: loading,
3015
- className: "inline-flex h-10 items-center gap-1.5 rounded-lg px-2.5 text-xs font-medium text-ph-subtle transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-focus disabled:opacity-40",
3016
- "data-test": "ask-ai-clear",
2991
+ onClick: () => void ask(preset.prompt),
2992
+ disabled: loading || hydrating,
2993
+ className: "group flex w-full items-center gap-2 rounded-xl border border-ph-border bg-ph-surface px-3 py-2 text-left text-sm text-ph-ink transition-colors hover:border-ph-brand/40 hover:bg-ph-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-focus disabled:opacity-40",
3017
2994
  children: [
3018
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_iconoir_react8.Erase, { className: "h-3.5 w-3.5" }),
3019
- "Clear"
2995
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_iconoir_react8.Sparks, { className: "h-4 w-4 shrink-0 text-ph-brand" }),
2996
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "min-w-0 flex-1 truncate", children: preset.label }),
2997
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_iconoir_react8.ArrowUp, { className: "h-3.5 w-3.5 shrink-0 rotate-45 text-ph-mutedtext transition-transform group-hover:translate-x-0.5" })
3020
2998
  ]
3021
- }
3022
- ) : null,
3023
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3024
- "button",
3025
- {
3026
- type: "button",
3027
- onClick: () => setOpen(false),
3028
- className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-lg text-ph-subtle transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-focus",
3029
- "aria-label": "Close Ask AI",
3030
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_iconoir_react8.Xmark, { className: "h-4 w-4" })
3031
- }
3032
- )
3033
- ] }),
3034
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "relative min-h-0 flex-1", children: [
3035
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
2999
+ },
3000
+ preset.label
3001
+ )) }) : null
3002
+ ] }) : null,
3003
+ messages.map((message, index) => {
3004
+ const isUser = message.role === "user";
3005
+ if (isUser) {
3006
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3007
+ "div",
3008
+ {
3009
+ className: "flex justify-end",
3010
+ "data-test": "ask-ai-user",
3011
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "w-fit max-w-[85%] whitespace-pre-wrap break-words rounded-2xl rounded-br-md bg-ph-brand px-3 py-2 text-sm leading-relaxed text-[var(--ph-on-accent)] [overflow-wrap:anywhere]", children: message.content })
3012
+ },
3013
+ `user-${index}`
3014
+ );
3015
+ }
3016
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
3036
3017
  "div",
3037
3018
  {
3038
- ref: scrollerRef,
3039
- onScroll: handleScroll,
3040
- role: "log",
3041
- "aria-live": "polite",
3042
- "aria-label": "Conversation",
3043
- className: "suite-scroll-lock h-full space-y-5 overflow-y-auto px-4 py-5",
3019
+ className: "flex items-end gap-2",
3020
+ "data-test": "ask-ai-assistant",
3044
3021
  children: [
3045
- hydrating && messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-2 text-sm text-ph-subtle", children: [
3046
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Spinner, { className: "h-4 w-4 animate-spin" }),
3047
- "Loading your chat\u2026"
3048
- ] }) : null,
3049
- !hydrating && messages.length === 0 && !error ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "mx-auto mt-6 flex max-w-sm flex-col items-center px-2 text-center", children: [
3050
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "flex h-12 w-12 items-center justify-center rounded-2xl bg-ph-brand/10 text-ph-brand", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Icon, { className: "h-6 w-6" }) }),
3051
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "mt-4 font-display text-lg font-semibold text-ph-ink", children: "How can I help?" }),
3052
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "mt-1.5 text-sm leading-relaxed text-ph-subtle", children: emptyState }),
3053
- presets.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "mt-5 flex w-full flex-col gap-2", children: presets.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
3054
- "button",
3055
- {
3056
- type: "button",
3057
- onClick: () => void ask(preset.prompt),
3058
- disabled: loading || hydrating,
3059
- className: "group flex w-full items-center gap-2 rounded-xl border border-ph-border bg-ph-surface px-3.5 py-2.5 text-left text-sm text-ph-ink transition-colors hover:border-ph-brand/40 hover:bg-ph-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-focus disabled:opacity-40",
3060
- children: [
3061
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_iconoir_react8.Sparks, { className: "h-4 w-4 shrink-0 text-ph-brand" }),
3062
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "min-w-0 flex-1 truncate", children: preset.label }),
3063
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_iconoir_react8.ArrowUp, { className: "h-3.5 w-3.5 shrink-0 rotate-45 text-ph-mutedtext transition-transform group-hover:translate-x-0.5" })
3064
- ]
3065
- },
3066
- preset.label
3067
- )) }) : null
3068
- ] }) : null,
3069
- messages.map((message, index) => {
3070
- const isUser = message.role === "user";
3071
- if (isUser) {
3072
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3073
- "div",
3074
- {
3075
- className: "flex justify-end",
3076
- "data-test": "ask-ai-user",
3077
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "max-w-[min(85%,42rem)] whitespace-pre-wrap break-words rounded-2xl rounded-br-md bg-ph-brand/12 px-3.5 py-2.5 text-sm leading-relaxed text-ph-ink", children: message.content })
3078
- },
3079
- `user-${index}`
3080
- );
3022
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3023
+ "span",
3024
+ {
3025
+ className: "mb-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-ph-brand/10 text-ph-brand",
3026
+ "aria-hidden": true,
3027
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Icon, { className: "h-3.5 w-3.5" })
3081
3028
  }
3082
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
3083
- "div",
3084
- {
3085
- className: "flex gap-3",
3086
- "data-test": "ask-ai-assistant",
3087
- children: [
3088
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3089
- "span",
3090
- {
3091
- className: "mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-ph-brand/10 text-ph-brand",
3092
- "aria-hidden": true,
3093
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Icon, { className: "h-4 w-4" })
3094
- }
3095
- ),
3096
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "min-w-0 flex-1", children: [
3097
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "text-sm leading-relaxed text-ph-ink", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3098
- import_react10.Suspense,
3099
- {
3100
- fallback: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "whitespace-pre-wrap break-words", children: message.content }),
3101
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3102
- SuiteAiBlockNoteMessage2,
3103
- {
3104
- markdown: message.content
3105
- }
3106
- )
3107
- }
3108
- ) }),
3109
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
3110
- "button",
3111
- {
3112
- type: "button",
3113
- onClick: () => void copyMessage(index, message.content),
3114
- className: "mt-1.5 inline-flex items-center gap-1 rounded-md px-1.5 py-0.5 text-xs 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-focus",
3115
- children: [
3116
- copiedId === index ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_iconoir_react8.Check, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_iconoir_react8.Copy, { className: "h-3 w-3" }),
3117
- copiedId === index ? "Copied" : "Copy"
3118
- ]
3119
- }
3120
- )
3121
- ] })
3122
- ]
3123
- },
3124
- `assistant-${index}`
3125
- );
3126
- }),
3127
- loading ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex gap-3", "aria-hidden": true, children: [
3128
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-ph-brand/10 text-ph-brand", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Icon, { className: "h-4 w-4" }) }),
3129
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1.5 py-1.5", children: [
3130
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-ph-mutedtext motion-safe:animate-bounce" }),
3131
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-ph-mutedtext motion-safe:animate-bounce [animation-delay:0.15s]" }),
3132
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-ph-mutedtext motion-safe:animate-bounce [animation-delay:0.3s]" })
3133
- ] })
3134
- ] }) : null,
3135
- loading ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "sr-only", "aria-live": "polite", children: "Assistant is responding\u2026" }) : null,
3136
- error ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
3137
- "div",
3029
+ ),
3030
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "min-w-0 max-w-[85%] rounded-2xl rounded-bl-md bg-ph-muted px-3 py-2 text-sm leading-relaxed text-ph-ink", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3031
+ import_react11.Suspense,
3138
3032
  {
3139
- role: "alert",
3140
- className: "flex items-start gap-2.5 rounded-xl border border-ph-danger/30 bg-ph-danger/10 px-3.5 py-3 text-sm text-ph-ink",
3141
- children: [
3142
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_iconoir_react8.WarningTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-ph-danger" }),
3143
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "min-w-0 flex-1", children: [
3144
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "font-medium text-ph-danger", children: "Something went wrong" }),
3145
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "mt-0.5 text-ph-subtle", children: error }),
3146
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3147
- "button",
3148
- {
3149
- type: "button",
3150
- onClick: retry,
3151
- disabled: loading,
3152
- className: "mt-2 inline-flex min-h-8 items-center gap-1.5 rounded-lg border border-ph-border bg-ph-surface px-2.5 text-xs font-medium text-ph-ink transition-colors hover:bg-ph-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-focus disabled:opacity-40",
3153
- children: "Try again"
3154
- }
3155
- )
3156
- ] })
3157
- ]
3033
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "whitespace-pre-wrap break-words", children: message.content }),
3034
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SuiteAiBlockNoteMessage2, { markdown: message.content })
3158
3035
  }
3159
- ) : null
3036
+ ) })
3160
3037
  ]
3161
- }
3162
- ),
3163
- !atBottom ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3164
- "button",
3165
- {
3166
- type: "button",
3167
- onClick: () => scrollToBottom("smooth"),
3168
- className: "absolute bottom-3 left-1/2 flex h-9 w-9 -translate-x-1/2 items-center justify-center rounded-full border border-ph-border bg-ph-surface text-ph-subtle shadow-ph-md transition-colors hover:text-ph-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-focus motion-safe:animate-[suite-ai-fade_150ms_ease-out]",
3169
- "aria-label": "Scroll to latest",
3170
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_iconoir_react8.ArrowDown, { className: "h-4 w-4" })
3171
- }
3172
- ) : null
3173
- ] }),
3174
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "px-3 pb-[max(0.75rem,env(safe-area-inset-bottom))] pt-1", children: [
3175
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-end gap-2 rounded-3xl border border-ph-border bg-ph-surface px-2 py-1 shadow-sm transition-colors focus-within:border-ph-brand/45 focus-within:ring-2 focus-within:ring-ph-focus/35", children: [
3176
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("label", { htmlFor: "suite-ask-ai-input", className: "sr-only", children: "Ask a question" }),
3177
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3178
- "textarea",
3179
- {
3180
- id: "suite-ask-ai-input",
3181
- ref: textareaRef,
3182
- value: prompt,
3183
- onChange: (e) => setPrompt(e.target.value),
3184
- onKeyDown: (e) => {
3185
- if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
3186
- e.preventDefault();
3187
- void ask(prompt);
3188
- }
3189
- },
3190
- placeholder: "Ask anything\u2026",
3191
- rows: 1,
3192
- "data-test": "ask-ai-input",
3193
- className: "max-h-44 min-h-[44px] flex-1 resize-none bg-transparent px-2 py-2.5 text-sm leading-relaxed text-ph-ink outline-none placeholder:text-ph-mutedtext"
3194
- }
3195
- ),
3196
- loading ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3197
- "button",
3198
- {
3199
- type: "button",
3200
- onClick: stop,
3201
- "data-test": "ask-ai-stop",
3202
- className: "flex h-11 w-11 shrink-0 items-center justify-center rounded-full bg-ph-muted text-ph-ink transition-colors hover:bg-ph-border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-focus",
3203
- "aria-label": "Stop generating",
3204
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_iconoir_react8.Square, { className: "h-3.5 w-3.5 fill-current" })
3205
- }
3206
- ) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3207
- "button",
3208
- {
3209
- type: "button",
3210
- onClick: () => void ask(prompt),
3211
- disabled: !prompt.trim(),
3212
- "data-test": "ask-ai-send",
3213
- className: "flex h-11 w-11 shrink-0 items-center justify-center rounded-full bg-ph-brand text-[color:var(--ph-on-accent)] transition-opacity hover:bg-ph-brand-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-focus disabled:opacity-40",
3214
- "aria-label": "Send message",
3215
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_iconoir_react8.ArrowUp, { className: "h-[18px] w-[18px]" })
3216
- }
3217
- )
3218
- ] }),
3219
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("p", { className: "mt-1.5 px-1 text-center text-xs text-ph-mutedtext", children: [
3220
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("kbd", { className: "font-mono", children: "Enter" }),
3221
- " to send \xB7",
3222
- " ",
3223
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("kbd", { className: "font-mono", children: "Shift" }),
3224
- "+",
3225
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("kbd", { className: "font-mono", children: "Enter" }),
3226
- " for a new line"
3038
+ },
3039
+ `assistant-${index}`
3040
+ );
3041
+ }),
3042
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-end gap-2", "aria-hidden": true, children: [
3043
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "mb-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-ph-brand/10 text-ph-brand", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Icon, { className: "h-3.5 w-3.5" }) }),
3044
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 rounded-2xl rounded-bl-md bg-ph-muted px-3 py-2.5", children: [
3045
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-ph-mutedtext motion-safe:animate-bounce" }),
3046
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-ph-mutedtext motion-safe:animate-bounce [animation-delay:0.15s]" }),
3047
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-ph-mutedtext motion-safe:animate-bounce [animation-delay:0.3s]" })
3227
3048
  ] })
3228
- ] })
3049
+ ] }) : null,
3050
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "sr-only", "aria-live": "polite", children: "Assistant is responding\u2026" }) : null,
3051
+ error ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
3052
+ "div",
3053
+ {
3054
+ role: "alert",
3055
+ className: "flex items-start gap-2 rounded-xl border border-ph-danger/30 bg-ph-danger/10 px-3 py-2.5 text-sm text-ph-ink",
3056
+ children: [
3057
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_iconoir_react8.WarningTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-ph-danger" }),
3058
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "min-w-0 flex-1", children: [
3059
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "font-medium text-ph-danger", children: "Something went wrong" }),
3060
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "mt-0.5 text-ph-subtle", children: error }),
3061
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3062
+ "button",
3063
+ {
3064
+ type: "button",
3065
+ onClick: retry,
3066
+ disabled: loading,
3067
+ className: "mt-2 inline-flex min-h-8 items-center gap-1.5 rounded-lg border border-ph-border bg-ph-surface px-2.5 text-xs font-medium text-ph-ink transition-colors hover:bg-ph-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-focus disabled:opacity-40",
3068
+ children: "Try again"
3069
+ }
3070
+ )
3071
+ ] })
3072
+ ]
3073
+ }
3074
+ ) : null
3229
3075
  ]
3230
3076
  }
3231
- )
3232
- ]
3233
- }
3234
- ) : null
3235
- ] });
3077
+ ),
3078
+ !atBottom ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "absolute bottom-3 left-1/2 -translate-x-1/2", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Tooltip, { content: "Scroll to latest", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3079
+ "button",
3080
+ {
3081
+ type: "button",
3082
+ onClick: () => scrollToBottom("smooth"),
3083
+ className: "flex h-9 w-9 items-center justify-center rounded-full border border-ph-border bg-ph-surface text-ph-subtle shadow-ph-md transition-colors hover:text-ph-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-focus",
3084
+ "aria-label": "Scroll to latest",
3085
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_iconoir_react8.ArrowDown, { className: "h-4 w-4" })
3086
+ }
3087
+ ) }) }) : null
3088
+ ] }),
3089
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "px-2 pt-2 pb-[max(2rem,calc(env(safe-area-inset-bottom)+1rem))]", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-end gap-1.5 rounded-2xl border border-ph-border bg-ph-surface px-1.5 py-1 transition-colors focus-within:border-ph-brand/45 focus-within:ring-2 focus-within:ring-ph-focus/35", children: [
3090
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { htmlFor: inputId, className: "sr-only", children: "Ask a question" }),
3091
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3092
+ "textarea",
3093
+ {
3094
+ id: inputId,
3095
+ ref: textareaRef,
3096
+ value: prompt,
3097
+ onChange: (event) => setPrompt(event.target.value),
3098
+ onKeyDown: (event) => {
3099
+ if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
3100
+ event.preventDefault();
3101
+ void ask(prompt);
3102
+ }
3103
+ },
3104
+ placeholder: "Ask anything\u2026",
3105
+ rows: 1,
3106
+ "data-test": "ask-ai-input",
3107
+ className: "max-h-36 min-h-10 flex-1 resize-none bg-transparent px-1.5 py-2 text-sm leading-relaxed text-ph-ink outline-none placeholder:text-ph-mutedtext"
3108
+ }
3109
+ ),
3110
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Tooltip, { content: "Stop generating", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3111
+ "button",
3112
+ {
3113
+ type: "button",
3114
+ onClick: stop,
3115
+ "data-test": "ask-ai-stop",
3116
+ className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-ph-muted text-ph-ink transition-colors hover:bg-ph-border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-focus",
3117
+ "aria-label": "Stop generating",
3118
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_iconoir_react8.Square, { className: "h-3.5 w-3.5 fill-current" })
3119
+ }
3120
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Tooltip, { content: "Send message", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3121
+ "button",
3122
+ {
3123
+ type: "button",
3124
+ onClick: () => void ask(prompt),
3125
+ disabled: !prompt.trim(),
3126
+ "data-test": "ask-ai-send",
3127
+ className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-ph-brand text-[color:var(--ph-on-accent)] transition-opacity hover:bg-ph-brand-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-focus disabled:opacity-40",
3128
+ "aria-label": "Send message",
3129
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_iconoir_react8.ArrowUp, { className: "h-4 w-4" })
3130
+ }
3131
+ ) })
3132
+ ] }) })
3133
+ ]
3134
+ }
3135
+ );
3136
+ }
3137
+
3138
+ // src/suite/components/workspace-switcher.tsx
3139
+ var import_react12 = require("react");
3140
+ var import_iconoir_react9 = require("iconoir-react");
3141
+ var import_jsx_runtime22 = require("react/jsx-runtime");
3142
+ function SuiteWorkspaceSwitcher({
3143
+ workspaces = [],
3144
+ groups,
3145
+ currentId,
3146
+ onSelect,
3147
+ name,
3148
+ collapsed = false,
3149
+ emptyLabel = "No workspaces yet.",
3150
+ dropdownMenu: DropdownMenu2,
3151
+ dropdownItem: DropdownItem2,
3152
+ triggerId,
3153
+ triggerDataTest,
3154
+ footer
3155
+ }) {
3156
+ var _a;
3157
+ const [open, setOpen] = (0, import_react12.useState)(false);
3158
+ const sections = (_a = groups == null ? void 0 : groups.filter((group) => group.workspaces.length > 0)) != null ? _a : workspaces.length > 0 ? [{ id: "workspaces", label: "Switch workspace", workspaces }] : [];
3159
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3160
+ DropdownMenu2,
3161
+ {
3162
+ "aria-label": "Switch workspace",
3163
+ triggerId: triggerId != null ? triggerId : "workspace-switcher-trigger",
3164
+ triggerDataTest: triggerDataTest != null ? triggerDataTest : "workspace-switcher-trigger",
3165
+ className: collapsed ? "w-11 shrink-0" : "w-full min-w-0 max-w-full [&_.ph-dropdown-trigger]:w-full",
3166
+ panelClassName: "w-64 max-w-full",
3167
+ align: "start",
3168
+ open,
3169
+ onOpenChange: setOpen,
3170
+ trigger: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
3171
+ "span",
3172
+ {
3173
+ className: cn(
3174
+ "flex min-w-0 max-w-full items-center gap-2 rounded-lg border border-ph-border bg-ph-muted text-left transition-colors hover:bg-ph-muted",
3175
+ collapsed ? "h-10 w-10 justify-center p-0" : "w-full px-3 py-2",
3176
+ open && "border-ph-brand/40"
3177
+ ),
3178
+ "data-test": "workspace-switcher-trigger",
3179
+ "aria-expanded": open,
3180
+ children: [
3181
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_iconoir_react9.Building, { className: "h-4 w-4 shrink-0 text-ph-ink", "aria-hidden": true }),
3182
+ !collapsed ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
3183
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm font-semibold text-ph-ink", children: name }),
3184
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3185
+ import_iconoir_react9.NavArrowDown,
3186
+ {
3187
+ className: cn(
3188
+ "h-3.5 w-3.5 shrink-0 text-ph-mutedtext transition-transform",
3189
+ open && "rotate-180"
3190
+ ),
3191
+ "aria-hidden": true
3192
+ }
3193
+ )
3194
+ ] }) : null
3195
+ ]
3196
+ }
3197
+ ),
3198
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
3199
+ "div",
3200
+ {
3201
+ id: "workspace-switcher-menu",
3202
+ className: "p-1",
3203
+ "data-test": "workspace-switcher-menu",
3204
+ children: [
3205
+ sections.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "px-3 py-2 text-sm text-ph-mutedtext", children: emptyLabel }) : sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { children: [
3206
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "px-3 py-2 text-[10px] font-semibold uppercase tracking-[0.14em] text-ph-mutedtext", children: section.label }),
3207
+ section.workspaces.map((workspace) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3208
+ DropdownItem2,
3209
+ {
3210
+ id: `workspace-option-${workspace.id}`,
3211
+ role: "option",
3212
+ "aria-selected": workspace.id === currentId,
3213
+ "data-test": "workspace-option",
3214
+ "data-workspace-id": workspace.id,
3215
+ onMouseDown: (event) => event.preventDefault(),
3216
+ onClick: () => {
3217
+ setOpen(false);
3218
+ onSelect(workspace);
3219
+ },
3220
+ className: appSwitcherMenuItemClass(),
3221
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "flex min-w-0 w-full items-center gap-3", children: [
3222
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "min-w-0 flex-1", children: [
3223
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "block truncate text-sm font-medium text-ph-ink", children: workspace.name }),
3224
+ workspace.description ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "block truncate text-xs text-ph-mutedtext", children: workspace.description }) : null
3225
+ ] }),
3226
+ workspace.id === currentId ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3227
+ import_iconoir_react9.Check,
3228
+ {
3229
+ className: "h-4 w-4 shrink-0 text-ph-brand",
3230
+ "aria-hidden": "true"
3231
+ }
3232
+ ) : null
3233
+ ] })
3234
+ },
3235
+ workspace.id
3236
+ ))
3237
+ ] }, section.id)),
3238
+ footer ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "mt-1 border-t border-ph-border pt-1", children: footer }) : null
3239
+ ]
3240
+ }
3241
+ )
3242
+ }
3243
+ );
3236
3244
  }
3237
3245
 
3238
3246
  // src/suite/lib/use-sidebar-width.ts
3239
- var import_react11 = require("react");
3247
+ var import_react13 = require("react");
3240
3248
  var SIDEBAR_RAIL_WIDTH = 56;
3241
3249
  var SIDEBAR_COLLAPSE_THRESHOLD = 80;
3242
3250
  var SIDEBAR_MIN_EXPANDED_WIDTH = 180;
3243
- var SIDEBAR_MAX_WIDTH = 320;
3251
+ var SIDEBAR_MAX_WIDTH = 640;
3244
3252
  var SIDEBAR_DEFAULT_WIDTH = 320;
3253
+ function sidebarColumnWidth(width, collapsed, _chatOpen = false) {
3254
+ if (collapsed) return SIDEBAR_RAIL_WIDTH;
3255
+ return width;
3256
+ }
3245
3257
  function clampWidth(width) {
3246
3258
  return Math.min(SIDEBAR_MAX_WIDTH, Math.max(SIDEBAR_RAIL_WIDTH, width));
3247
3259
  }
@@ -3252,10 +3264,10 @@ function snapWidth(width) {
3252
3264
  }
3253
3265
  function useSidebarWidth(storageKey, defaultWidth = SIDEBAR_DEFAULT_WIDTH) {
3254
3266
  const fallbackWidth = snapWidth(clampWidth(defaultWidth));
3255
- const [width, setWidth] = (0, import_react11.useState)(fallbackWidth);
3256
- const [narrowViewport, setNarrowViewport] = (0, import_react11.useState)(false);
3267
+ const [width, setWidth] = (0, import_react13.useState)(fallbackWidth);
3268
+ const [narrowViewport, setNarrowViewport] = (0, import_react13.useState)(false);
3257
3269
  const collapsed = narrowViewport || width <= SIDEBAR_COLLAPSE_THRESHOLD;
3258
- (0, import_react11.useEffect)(() => {
3270
+ (0, import_react13.useEffect)(() => {
3259
3271
  let stored = null;
3260
3272
  try {
3261
3273
  stored = localStorage.getItem(storageKey);
@@ -3267,14 +3279,14 @@ function useSidebarWidth(storageKey, defaultWidth = SIDEBAR_DEFAULT_WIDTH) {
3267
3279
  queueMicrotask(() => setWidth(snapWidth(clampWidth(parsed))));
3268
3280
  }
3269
3281
  }, [storageKey]);
3270
- (0, import_react11.useEffect)(() => {
3282
+ (0, import_react13.useEffect)(() => {
3271
3283
  const media = window.matchMedia("(max-width: 639px)");
3272
3284
  const sync = () => setNarrowViewport(media.matches);
3273
3285
  sync();
3274
3286
  media.addEventListener("change", sync);
3275
3287
  return () => media.removeEventListener("change", sync);
3276
3288
  }, []);
3277
- const persist = (0, import_react11.useCallback)(
3289
+ const persist = (0, import_react13.useCallback)(
3278
3290
  (value) => {
3279
3291
  try {
3280
3292
  localStorage.setItem(storageKey, String(value));
@@ -3283,7 +3295,7 @@ function useSidebarWidth(storageKey, defaultWidth = SIDEBAR_DEFAULT_WIDTH) {
3283
3295
  },
3284
3296
  [storageKey]
3285
3297
  );
3286
- const setCollapsed = (0, import_react11.useCallback)(
3298
+ const setCollapsed = (0, import_react13.useCallback)(
3287
3299
  (value) => {
3288
3300
  setWidth((current) => {
3289
3301
  const next = value ? SIDEBAR_RAIL_WIDTH : current <= SIDEBAR_COLLAPSE_THRESHOLD ? fallbackWidth : snapWidth(current);
@@ -3293,11 +3305,11 @@ function useSidebarWidth(storageKey, defaultWidth = SIDEBAR_DEFAULT_WIDTH) {
3293
3305
  },
3294
3306
  [fallbackWidth, persist]
3295
3307
  );
3296
- const resetWidth = (0, import_react11.useCallback)(() => {
3308
+ const resetWidth = (0, import_react13.useCallback)(() => {
3297
3309
  setWidth(fallbackWidth);
3298
3310
  persist(fallbackWidth);
3299
3311
  }, [fallbackWidth, persist]);
3300
- const startResize = (0, import_react11.useCallback)(
3312
+ const startResize = (0, import_react13.useCallback)(
3301
3313
  (event) => {
3302
3314
  event.preventDefault();
3303
3315
  const startX = event.clientX;
@@ -3319,7 +3331,7 @@ function useSidebarWidth(storageKey, defaultWidth = SIDEBAR_DEFAULT_WIDTH) {
3319
3331
  },
3320
3332
  [persist, width]
3321
3333
  );
3322
- const resizeBy = (0, import_react11.useCallback)(
3334
+ const resizeBy = (0, import_react13.useCallback)(
3323
3335
  (delta) => {
3324
3336
  setWidth((current) => {
3325
3337
  const next = clampWidth(current + delta);
@@ -3333,10 +3345,10 @@ function useSidebarWidth(storageKey, defaultWidth = SIDEBAR_DEFAULT_WIDTH) {
3333
3345
  }
3334
3346
 
3335
3347
  // src/suite/lib/motion.tsx
3336
- var import_react12 = require("motion/react");
3337
- var import_jsx_runtime21 = require("react/jsx-runtime");
3348
+ var import_react14 = require("motion/react");
3349
+ var import_jsx_runtime23 = require("react/jsx-runtime");
3338
3350
  function SuiteMotionProvider({ children }) {
3339
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react12.LazyMotion, { features: import_react12.domAnimation, strict: true, children });
3351
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react14.LazyMotion, { features: import_react14.domAnimation, strict: true, children });
3340
3352
  }
3341
3353
  var SUITE_SPRINGS = {
3342
3354
  /** Snappy panel/sheet entrances — small overshoot. */
@@ -3348,24 +3360,24 @@ var SUITE_SPRINGS = {
3348
3360
  };
3349
3361
 
3350
3362
  // src/suite/components/suite-skeleton.tsx
3351
- var import_jsx_runtime22 = require("react/jsx-runtime");
3363
+ var import_jsx_runtime24 = require("react/jsx-runtime");
3352
3364
  function SuiteSkeleton({
3353
3365
  className,
3354
3366
  lines = 1,
3355
3367
  circle,
3356
3368
  width
3357
3369
  }) {
3358
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3370
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3359
3371
  "div",
3360
3372
  {
3361
3373
  className: cn("space-y-2", circle ? "flex flex-col items-center" : ""),
3362
3374
  "aria-hidden": true,
3363
- children: Array.from({ length: lines }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3375
+ children: Array.from({ length: lines }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3364
3376
  "div",
3365
3377
  {
3366
3378
  className: "animate-[skeleton-stagger_0.6s_ease-out_both]",
3367
3379
  style: { animationDelay: `${index * 80}ms` },
3368
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3380
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3369
3381
  "div",
3370
3382
  {
3371
3383
  className: cn(
@@ -3389,7 +3401,7 @@ function SuiteSkeletonCard({
3389
3401
  rows = 3,
3390
3402
  header = true
3391
3403
  }) {
3392
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
3404
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
3393
3405
  "div",
3394
3406
  {
3395
3407
  className: cn(
@@ -3398,26 +3410,26 @@ function SuiteSkeletonCard({
3398
3410
  ),
3399
3411
  "aria-hidden": true,
3400
3412
  children: [
3401
- header ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
3413
+ header ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
3402
3414
  "div",
3403
3415
  {
3404
3416
  className: "mb-5 flex items-center gap-3 animate-[skeleton-stagger_0.5s_ease-out_both]",
3405
3417
  style: { animationDelay: "100ms" },
3406
3418
  children: [
3407
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "ph-skeleton h-10 w-10 rounded-xl" }),
3408
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex-1 space-y-2", children: [
3409
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "ph-skeleton h-4 w-1/3" }),
3410
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "ph-skeleton h-3 w-1/2" })
3419
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "ph-skeleton h-10 w-10 rounded-xl" }),
3420
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex-1 space-y-2", children: [
3421
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "ph-skeleton h-4 w-1/3" }),
3422
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "ph-skeleton h-3 w-1/2" })
3411
3423
  ] })
3412
3424
  ]
3413
3425
  }
3414
3426
  ) : null,
3415
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "space-y-3", children: Array.from({ length: rows }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3427
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "space-y-3", children: Array.from({ length: rows }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3416
3428
  "div",
3417
3429
  {
3418
3430
  className: "animate-[skeleton-stagger_0.5s_ease-out_both]",
3419
3431
  style: { animationDelay: `${200 + index * 80}ms` },
3420
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3432
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3421
3433
  "div",
3422
3434
  {
3423
3435
  className: "ph-skeleton h-3",
@@ -3435,16 +3447,16 @@ function SuiteSkeletonList({
3435
3447
  className,
3436
3448
  items = 4
3437
3449
  }) {
3438
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn("space-y-3", className), "aria-hidden": true, children: Array.from({ length: items }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3450
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn("space-y-3", className), "aria-hidden": true, children: Array.from({ length: items }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3439
3451
  "div",
3440
3452
  {
3441
3453
  className: "animate-[skeleton-stagger_0.6s_ease-out_both]",
3442
3454
  style: { animationDelay: `${index * 100}ms` },
3443
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-3 rounded-xl border border-ph-border bg-ph-surface p-4", children: [
3444
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "ph-skeleton h-10 w-10 rounded-lg" }),
3445
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "min-w-0 flex-1 space-y-2", children: [
3446
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "ph-skeleton h-3.5 w-3/4" }),
3447
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "ph-skeleton h-3 w-1/2" })
3455
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex items-center gap-3 rounded-xl border border-ph-border bg-ph-surface p-4", children: [
3456
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "ph-skeleton h-10 w-10 rounded-lg" }),
3457
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "min-w-0 flex-1 space-y-2", children: [
3458
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "ph-skeleton h-3.5 w-3/4" }),
3459
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "ph-skeleton h-3 w-1/2" })
3448
3460
  ] })
3449
3461
  ] })
3450
3462
  },
@@ -3459,7 +3471,7 @@ function SuiteEmptyState({
3459
3471
  className,
3460
3472
  size = "md"
3461
3473
  }) {
3462
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
3474
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
3463
3475
  "div",
3464
3476
  {
3465
3477
  className: cn(
@@ -3468,13 +3480,13 @@ function SuiteEmptyState({
3468
3480
  className
3469
3481
  ),
3470
3482
  children: [
3471
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "pointer-events-none absolute inset-0 overflow-hidden opacity-60", "aria-hidden": true, children: [
3472
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "absolute -left-10 -top-10 h-40 w-40 rounded-full bg-ph-brand/10 blur-3xl" }),
3473
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "absolute -bottom-10 -right-10 h-40 w-40 rounded-full bg-ph-accent/10 blur-3xl" })
3483
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "pointer-events-none absolute inset-0 overflow-hidden opacity-60", "aria-hidden": true, children: [
3484
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "absolute -left-10 -top-10 h-40 w-40 rounded-full bg-ph-brand/10 blur-3xl" }),
3485
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "absolute -bottom-10 -right-10 h-40 w-40 rounded-full bg-ph-accent/10 blur-3xl" })
3474
3486
  ] }),
3475
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "relative", children: [
3476
- icon ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "mx-auto mb-4 inline-flex items-center justify-center rounded-2xl bg-ph-muted p-3 shadow-sm ring-1 ring-black/[0.06]", children: icon }) : null,
3477
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3487
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "relative", children: [
3488
+ icon ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "mx-auto mb-4 inline-flex items-center justify-center rounded-2xl bg-ph-muted p-3 shadow-sm ring-1 ring-black/[0.06]", children: icon }) : null,
3489
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3478
3490
  "h3",
3479
3491
  {
3480
3492
  className: cn(
@@ -3484,8 +3496,8 @@ function SuiteEmptyState({
3484
3496
  children: title
3485
3497
  }
3486
3498
  ),
3487
- description ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "mx-auto mt-2 max-w-sm text-sm leading-relaxed text-ph-subtle", children: description }) : null,
3488
- action ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "mt-5 flex flex-wrap items-center justify-center gap-2", children: action }) : null
3499
+ description ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "mx-auto mt-2 max-w-sm text-sm leading-relaxed text-ph-subtle", children: description }) : null,
3500
+ action ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "mt-5 flex flex-wrap items-center justify-center gap-2", children: action }) : null
3489
3501
  ] })
3490
3502
  ]
3491
3503
  }
@@ -3493,8 +3505,8 @@ function SuiteEmptyState({
3493
3505
  }
3494
3506
 
3495
3507
  // src/suite/components/suite-settings-mobile-nav.tsx
3496
- var import_react13 = require("react");
3497
- var import_jsx_runtime23 = require("react/jsx-runtime");
3508
+ var import_react15 = require("react");
3509
+ var import_jsx_runtime25 = require("react/jsx-runtime");
3498
3510
  function SuiteSettingsMobileNav({
3499
3511
  items,
3500
3512
  activeHref,
@@ -3503,16 +3515,16 @@ function SuiteSettingsMobileNav({
3503
3515
  ariaLabel = "Settings sections",
3504
3516
  className
3505
3517
  }) {
3506
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3518
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3507
3519
  "nav",
3508
3520
  {
3509
3521
  "data-test": dataTest,
3510
3522
  "aria-label": ariaLabel,
3511
3523
  className: cn("suite-settings-mobile-nav lg:hidden", className),
3512
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex snap-x gap-2 overflow-x-auto pb-3 pt-1 scrollbar-hide [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: items.map((item) => {
3524
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex snap-x gap-2 overflow-x-auto pb-3 pt-1 scrollbar-hide [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: items.map((item) => {
3513
3525
  const Icon = item.icon;
3514
3526
  const active = activeHref === item.href || activeHref.startsWith(`${item.href}/`);
3515
- const iconNode = (0, import_react13.isValidElement)(item.icon) ? item.icon : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3527
+ const iconNode = (0, import_react15.isValidElement)(item.icon) ? item.icon : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3516
3528
  Icon,
3517
3529
  {
3518
3530
  className: cn(
@@ -3522,7 +3534,7 @@ function SuiteSettingsMobileNav({
3522
3534
  "aria-hidden": true
3523
3535
  }
3524
3536
  );
3525
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3537
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
3526
3538
  "button",
3527
3539
  {
3528
3540
  type: "button",
@@ -3537,8 +3549,8 @@ function SuiteSettingsMobileNav({
3537
3549
  ),
3538
3550
  children: [
3539
3551
  iconNode,
3540
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "whitespace-nowrap", children: item.label }),
3541
- active ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "absolute inset-x-0 -bottom-3 h-0.5 rounded-full bg-ph-ink lg:hidden" }) : null
3552
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "whitespace-nowrap", children: item.label }),
3553
+ active ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "absolute inset-x-0 -bottom-3 h-0.5 rounded-full bg-ph-ink lg:hidden" }) : null
3542
3554
  ]
3543
3555
  },
3544
3556
  item.href
@@ -3550,144 +3562,9 @@ function SuiteSettingsMobileNav({
3550
3562
 
3551
3563
  // src/suite/components/suite-sidebar.tsx
3552
3564
  var import_link2 = __toESM(require("next/link"));
3553
-
3554
- // src/components/ui/Tooltip.tsx
3555
- var import_react14 = require("react");
3556
- var import_react_dom = require("react-dom");
3557
- var import_jsx_runtime24 = require("react/jsx-runtime");
3558
- var TooltipDelayContext = (0, import_react14.createContext)(0);
3559
- function tooltipPosition(trigger, side, align, offset) {
3560
- const horizontal = align === "start" ? trigger.left : align === "end" ? trigger.right : trigger.left + trigger.width / 2;
3561
- const vertical = align === "start" ? trigger.top : align === "end" ? trigger.bottom : trigger.top + trigger.height / 2;
3562
- if (side === "top")
3563
- return {
3564
- left: horizontal,
3565
- top: trigger.top - offset,
3566
- transform: "translate(-50%, -100%)"
3567
- };
3568
- if (side === "bottom")
3569
- return {
3570
- left: horizontal,
3571
- top: trigger.bottom + offset,
3572
- transform: "translate(-50%, 0)"
3573
- };
3574
- if (side === "left")
3575
- return {
3576
- left: trigger.left - offset,
3577
- top: vertical,
3578
- transform: "translate(-100%, -50%)"
3579
- };
3580
- return {
3581
- left: trigger.right + offset,
3582
- top: vertical,
3583
- transform: "translate(0, -50%)"
3584
- };
3585
- }
3586
- function Tooltip({
3587
- content,
3588
- children,
3589
- side = "top",
3590
- align = "center",
3591
- sideOffset = 7,
3592
- className,
3593
- open,
3594
- defaultOpen = false,
3595
- onOpenChange,
3596
- delayDuration
3597
- }) {
3598
- const providerDelay = (0, import_react14.useContext)(TooltipDelayContext);
3599
- const generatedId = (0, import_react14.useId)().replace(/:/g, "");
3600
- const triggerRef = (0, import_react14.useRef)(null);
3601
- const timerRef = (0, import_react14.useRef)(null);
3602
- const [internalOpen, setInternalOpen] = (0, import_react14.useState)(defaultOpen);
3603
- const [position, setPosition] = (0, import_react14.useState)(null);
3604
- const isOpen = open != null ? open : internalOpen;
3605
- const triggerId = children.props.id;
3606
- const tooltipId = triggerId ? `${triggerId}-tooltip` : `ph-tooltip-${generatedId}`;
3607
- function setOpen(next) {
3608
- if (open === void 0) setInternalOpen(next);
3609
- onOpenChange == null ? void 0 : onOpenChange(next);
3610
- }
3611
- function clearTimer() {
3612
- if (timerRef.current) clearTimeout(timerRef.current);
3613
- timerRef.current = null;
3614
- }
3615
- function updatePosition() {
3616
- if (!triggerRef.current) return;
3617
- setPosition(
3618
- tooltipPosition(
3619
- triggerRef.current.getBoundingClientRect(),
3620
- side,
3621
- align,
3622
- sideOffset
3623
- )
3624
- );
3625
- }
3626
- function show(immediate) {
3627
- clearTimer();
3628
- updatePosition();
3629
- const delay = immediate ? 0 : delayDuration != null ? delayDuration : providerDelay;
3630
- if (!delay) setOpen(true);
3631
- else timerRef.current = setTimeout(() => setOpen(true), delay);
3632
- }
3633
- function hide() {
3634
- clearTimer();
3635
- setOpen(false);
3636
- }
3637
- (0, import_react14.useEffect)(() => {
3638
- if (!isOpen) return;
3639
- const reposition = () => updatePosition();
3640
- window.addEventListener("resize", reposition);
3641
- window.addEventListener("scroll", reposition, true);
3642
- return () => {
3643
- window.removeEventListener("resize", reposition);
3644
- window.removeEventListener("scroll", reposition, true);
3645
- };
3646
- });
3647
- (0, import_react14.useEffect)(() => () => clearTimer(), []);
3648
- const describedBy = [children.props["aria-describedby"], tooltipId].filter(Boolean).join(" ");
3649
- const trigger = (0, import_react14.cloneElement)(children, { "aria-describedby": describedBy });
3650
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
3651
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3652
- "span",
3653
- {
3654
- ref: triggerRef,
3655
- className: "inline-flex",
3656
- onMouseEnter: () => show(false),
3657
- onMouseLeave: hide,
3658
- onFocusCapture: () => show(true),
3659
- onBlurCapture: hide,
3660
- onKeyDown: (event) => {
3661
- if (event.key === "Escape") hide();
3662
- },
3663
- children: trigger
3664
- }
3665
- ),
3666
- isOpen && position && typeof document !== "undefined" ? (0, import_react_dom.createPortal)(
3667
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3668
- "span",
3669
- {
3670
- style: __spreadValues({ position: "fixed" }, position),
3671
- className: "pointer-events-none z-[200]",
3672
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3673
- "span",
3674
- {
3675
- id: tooltipId,
3676
- role: "tooltip",
3677
- "data-side": side,
3678
- className: cn2("ph-tooltip-content", className),
3679
- children: content
3680
- }
3681
- )
3682
- }
3683
- ),
3684
- document.body
3685
- ) : null
3686
- ] });
3687
- }
3688
-
3689
- // src/suite/components/suite-sidebar.tsx
3690
- var import_jsx_runtime25 = require("react/jsx-runtime");
3565
+ var import_react16 = require("react");
3566
+ var import_iconoir_react10 = require("iconoir-react");
3567
+ var import_jsx_runtime26 = require("react/jsx-runtime");
3691
3568
  function renderNode(node, collapsed) {
3692
3569
  if (typeof node === "function") return node(collapsed);
3693
3570
  return node;
@@ -3702,9 +3579,94 @@ function SuiteSidebar({
3702
3579
  footerExtras,
3703
3580
  collapsed = false,
3704
3581
  className,
3705
- surface = false
3582
+ surface = false,
3583
+ askAiOpen = false,
3584
+ onAskAiOpenChange,
3585
+ askAi,
3586
+ onRequestExpand
3706
3587
  }) {
3707
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
3588
+ const showAskAi = Boolean(onAskAiOpenChange && askAi);
3589
+ const chatOpen = showAskAi && askAiOpen && !collapsed;
3590
+ (0, import_react16.useEffect)(() => {
3591
+ if (!onAskAiOpenChange) return;
3592
+ function onOpen() {
3593
+ onRequestExpand == null ? void 0 : onRequestExpand();
3594
+ onAskAiOpenChange == null ? void 0 : onAskAiOpenChange(true);
3595
+ }
3596
+ window.addEventListener(SUITE_OPEN_ASK_AI_EVENT, onOpen);
3597
+ return () => window.removeEventListener(SUITE_OPEN_ASK_AI_EVENT, onOpen);
3598
+ }, [onAskAiOpenChange, onRequestExpand]);
3599
+ (0, import_react16.useEffect)(() => {
3600
+ if (!chatOpen) return;
3601
+ function onKey(event) {
3602
+ if (event.key === "Escape") onAskAiOpenChange == null ? void 0 : onAskAiOpenChange(false);
3603
+ }
3604
+ window.addEventListener("keydown", onKey);
3605
+ return () => window.removeEventListener("keydown", onKey);
3606
+ }, [chatOpen, onAskAiOpenChange]);
3607
+ function toggleAskAi() {
3608
+ if (askAiOpen) {
3609
+ onAskAiOpenChange == null ? void 0 : onAskAiOpenChange(false);
3610
+ return;
3611
+ }
3612
+ if (collapsed) onRequestExpand == null ? void 0 : onRequestExpand();
3613
+ onAskAiOpenChange == null ? void 0 : onAskAiOpenChange(true);
3614
+ }
3615
+ const askAiButton = /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3616
+ "button",
3617
+ {
3618
+ type: "button",
3619
+ "data-test": "suite-ask-ai-toggle",
3620
+ "aria-pressed": askAiOpen,
3621
+ "aria-label": askAiOpen ? "Back to navigation" : "Shelly AI",
3622
+ onClick: toggleAskAi,
3623
+ className: cn(
3624
+ "inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-lg transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ph-focus-ring",
3625
+ askAiOpen ? "bg-ph-brand/10 text-ph-brand" : "text-ph-mutedtext hover:bg-ph-muted hover:text-ph-ink"
3626
+ ),
3627
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_iconoir_react10.Sparks, { className: "h-4 w-4", "aria-hidden": true })
3628
+ }
3629
+ );
3630
+ if (chatOpen) {
3631
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
3632
+ "div",
3633
+ {
3634
+ "data-suite-chat-open": "",
3635
+ className: cn(
3636
+ "flex h-full w-full min-w-0 flex-col text-ph-ink",
3637
+ surface ? "bg-ph-surface" : "bg-ph-canvas",
3638
+ className
3639
+ ),
3640
+ children: [
3641
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
3642
+ "div",
3643
+ {
3644
+ className: cn(
3645
+ "flex h-14 shrink-0 items-center gap-1 border-b border-ph-border px-2",
3646
+ surface ? "bg-ph-surface" : "bg-ph-canvas"
3647
+ ),
3648
+ children: [
3649
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Tooltip, { content: "Back to navigation", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3650
+ "button",
3651
+ {
3652
+ type: "button",
3653
+ "data-test": "suite-ask-ai-back",
3654
+ "aria-label": "Back to navigation",
3655
+ onClick: () => onAskAiOpenChange == null ? void 0 : onAskAiOpenChange(false),
3656
+ className: "inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-lg text-ph-subtle transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
3657
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_iconoir_react10.NavArrowLeft, { className: "h-4 w-4", "aria-hidden": true })
3658
+ }
3659
+ ) }),
3660
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm font-semibold text-ph-ink", children: "Shelly AI" })
3661
+ ]
3662
+ }
3663
+ ),
3664
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "min-h-0 flex-1", children: askAi })
3665
+ ]
3666
+ }
3667
+ );
3668
+ }
3669
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
3708
3670
  "div",
3709
3671
  {
3710
3672
  className: cn(
@@ -3713,7 +3675,7 @@ function SuiteSidebar({
3713
3675
  className
3714
3676
  ),
3715
3677
  children: [
3716
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
3678
+ appSwitcher || notificationBell ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
3717
3679
  "div",
3718
3680
  {
3719
3681
  className: cn(
@@ -3722,27 +3684,46 @@ function SuiteSidebar({
3722
3684
  collapsed ? "flex-col items-center gap-1 px-1 py-2" : "h-14 items-center justify-between gap-2 px-3"
3723
3685
  ),
3724
3686
  children: [
3725
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: cn("min-w-0", collapsed ? "shrink-0" : "flex-1"), children: renderNode(appSwitcher, collapsed) }),
3726
- collapsed ? null : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "shrink-0", children: renderNode(notificationBell, collapsed) })
3687
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: cn("min-w-0", collapsed ? "shrink-0" : "flex-1"), children: renderNode(appSwitcher, collapsed) }),
3688
+ collapsed ? null : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "shrink-0", children: renderNode(notificationBell, collapsed) })
3727
3689
  ]
3728
3690
  }
3729
- ),
3730
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
3691
+ ) : null,
3692
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
3731
3693
  "div",
3732
3694
  {
3733
3695
  className: cn(
3734
- "flex min-h-0 flex-1 flex-col overflow-y-auto",
3696
+ "flex min-h-0 flex-1 flex-col",
3735
3697
  surface ? "bg-ph-surface" : "bg-ph-canvas",
3698
+ chatOpen ? "min-h-0" : "overflow-y-auto",
3736
3699
  collapsed ? "px-1.5 py-2" : "p-2"
3737
3700
  ),
3738
3701
  children: [
3739
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("nav", { "aria-label": "Pages", className: "shrink-0 space-y-0.5", children: [
3740
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: cn("mb-3", collapsed && "flex justify-center"), children: renderNode(contextSwitcher, collapsed) }),
3741
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "space-y-0.5", children: navItems.map((item) => {
3702
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: cn("mb-3 w-full shrink-0", collapsed && "flex justify-center"), children: renderNode(contextSwitcher, collapsed) }),
3703
+ chatOpen ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex min-h-0 flex-1 flex-col", children: [
3704
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "mb-2 flex shrink-0 items-center gap-1", children: [
3705
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
3706
+ "button",
3707
+ {
3708
+ type: "button",
3709
+ "data-test": "suite-ask-ai-back",
3710
+ onClick: () => onAskAiOpenChange == null ? void 0 : onAskAiOpenChange(false),
3711
+ className: "inline-flex h-9 items-center gap-1 rounded-lg px-2 text-sm font-medium text-ph-subtle transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
3712
+ children: [
3713
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_iconoir_react10.NavArrowLeft, { className: "h-4 w-4", "aria-hidden": true }),
3714
+ "Nav"
3715
+ ]
3716
+ }
3717
+ ),
3718
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "truncate text-sm font-semibold text-ph-ink", children: "Shelly AI" })
3719
+ ] }),
3720
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "min-h-0 flex-1", children: askAi })
3721
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
3722
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("nav", { "aria-label": "Pages", className: "shrink-0 space-y-0.5", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "space-y-0.5", children: navItems.map((item) => {
3742
3723
  var _a;
3743
3724
  const Icon = item.icon;
3744
3725
  const active = Boolean(item.active);
3745
- const link = /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
3726
+ const link = /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
3746
3727
  import_link2.default,
3747
3728
  {
3748
3729
  href: item.href,
@@ -3756,7 +3737,7 @@ function SuiteSidebar({
3756
3737
  collapsed ? "mx-auto size-11 shrink-0 justify-center gap-0 px-0 py-0" : "w-full px-2.5 py-2"
3757
3738
  ),
3758
3739
  children: [
3759
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3740
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3760
3741
  "span",
3761
3742
  {
3762
3743
  className: cn(
@@ -3764,7 +3745,7 @@ function SuiteSidebar({
3764
3745
  item.iconClassName
3765
3746
  ),
3766
3747
  "aria-hidden": true,
3767
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3748
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3768
3749
  Icon,
3769
3750
  {
3770
3751
  className: "h-full w-full",
@@ -3773,7 +3754,7 @@ function SuiteSidebar({
3773
3754
  )
3774
3755
  }
3775
3756
  ),
3776
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3757
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3777
3758
  "span",
3778
3759
  {
3779
3760
  className: cn(
@@ -3784,26 +3765,26 @@ function SuiteSidebar({
3784
3765
  children: item.label
3785
3766
  }
3786
3767
  ),
3787
- !collapsed && item.badge ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "relative ml-auto shrink-0", children: item.badge }) : null
3768
+ !collapsed && item.badge ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "relative ml-auto shrink-0", children: item.badge }) : null
3788
3769
  ]
3789
3770
  },
3790
3771
  item.label
3791
3772
  );
3792
- return collapsed ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Tooltip, { content: item.label, side: "right", children: link }, item.label) : link;
3793
- }) })
3794
- ] }),
3795
- secondaryNav ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3796
- "div",
3797
- {
3798
- className: "min-h-0 flex-1",
3799
- "data-test": "suite-sidebar-secondary-nav",
3800
- children: secondaryNav
3801
- }
3802
- ) : null
3773
+ return collapsed ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Tooltip, { content: item.label, side: "right", children: link }, item.label) : link;
3774
+ }) }) }),
3775
+ secondaryNav ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3776
+ "div",
3777
+ {
3778
+ className: "min-h-0 flex-1",
3779
+ "data-test": "suite-sidebar-secondary-nav",
3780
+ children: secondaryNav
3781
+ }
3782
+ ) : null
3783
+ ] })
3803
3784
  ]
3804
3785
  }
3805
3786
  ),
3806
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3787
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3807
3788
  "div",
3808
3789
  {
3809
3790
  className: cn(
@@ -3811,16 +3792,23 @@ function SuiteSidebar({
3811
3792
  surface ? "bg-ph-surface" : "bg-ph-canvas",
3812
3793
  collapsed ? "p-1.5" : "p-3"
3813
3794
  ),
3814
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
3795
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
3815
3796
  "div",
3816
3797
  {
3817
3798
  className: cn(
3818
3799
  "flex items-center gap-2",
3819
- collapsed ? "flex-col items-center justify-center [&_[data-test$='-visible-sign-out']]:hidden [&>div]:w-auto" : "w-full min-w-0"
3800
+ collapsed ? "flex-col items-center justify-center [&>div]:w-auto" : "w-full min-w-0"
3820
3801
  ),
3821
3802
  children: [
3822
3803
  renderNode(userMenu, collapsed),
3823
- footerExtras ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: cn("shrink-0", collapsed ? "mt-2" : "ml-auto"), children: renderNode(footerExtras, collapsed) }) : null
3804
+ showAskAi ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3805
+ "div",
3806
+ {
3807
+ className: cn("shrink-0", collapsed ? "mt-2" : "ml-auto"),
3808
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Tooltip, { content: "Shelly AI", side: collapsed ? "right" : "top", children: askAiButton })
3809
+ }
3810
+ ) : footerExtras ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: cn("shrink-0", collapsed ? "mt-2" : "ml-auto"), children: renderNode(footerExtras, collapsed) }) : null,
3811
+ showAskAi && footerExtras ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: cn("shrink-0", collapsed && "mt-2"), children: renderNode(footerExtras, collapsed) }) : null
3824
3812
  ]
3825
3813
  }
3826
3814
  )
@@ -3832,7 +3820,7 @@ function SuiteSidebar({
3832
3820
  }
3833
3821
 
3834
3822
  // src/suite/components/suite-app-layout.tsx
3835
- var import_jsx_runtime26 = require("react/jsx-runtime");
3823
+ var import_jsx_runtime27 = require("react/jsx-runtime");
3836
3824
  function SuiteAppLayout({
3837
3825
  sidebar,
3838
3826
  children,
@@ -3846,7 +3834,7 @@ function SuiteAppLayout({
3846
3834
  onResetWidth,
3847
3835
  className
3848
3836
  }) {
3849
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
3837
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
3850
3838
  "div",
3851
3839
  {
3852
3840
  "data-test": "app-shell",
@@ -3855,15 +3843,15 @@ function SuiteAppLayout({
3855
3843
  className
3856
3844
  ),
3857
3845
  children: [
3858
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
3846
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
3859
3847
  "aside",
3860
3848
  {
3861
3849
  className: "relative hidden shrink-0 flex-col overflow-visible border-r border-ph-border ease-out lg:flex",
3862
3850
  style: { width: `${sidebarWidth}px` },
3863
3851
  "data-collapsed": collapsed ? "true" : "false",
3864
3852
  children: [
3865
- sidebar,
3866
- onStartResize ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3853
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "relative z-0 h-full min-h-0 isolate", children: sidebar }),
3854
+ onStartResize ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3867
3855
  "div",
3868
3856
  {
3869
3857
  role: "separator",
@@ -3885,15 +3873,15 @@ function SuiteAppLayout({
3885
3873
  onResizeBy == null ? void 0 : onResizeBy(16);
3886
3874
  }
3887
3875
  },
3888
- className: "absolute inset-y-0 -right-1 z-10 w-2 cursor-col-resize touch-none transition-colors hover:bg-[color-mix(in_oklab,var(--ph-accent)_30%,transparent)]"
3876
+ className: "absolute inset-y-0 right-0 z-20 w-2 cursor-col-resize touch-none transition-colors hover:bg-[color-mix(in_oklab,var(--ph-accent)_30%,transparent)]"
3889
3877
  }
3890
3878
  ) : null
3891
3879
  ]
3892
3880
  }
3893
3881
  ),
3894
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: [
3895
- mobileHeader ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "shrink-0 lg:hidden", children: mobileHeader }) : null,
3896
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3882
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: [
3883
+ mobileHeader ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "shrink-0 lg:hidden", children: mobileHeader }) : null,
3884
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3897
3885
  "main",
3898
3886
  {
3899
3887
  id: "main-content",
@@ -3907,7 +3895,7 @@ function SuiteAppLayout({
3907
3895
  children
3908
3896
  }
3909
3897
  ),
3910
- bottomNav ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "shrink-0 lg:hidden", children: bottomNav }) : null
3898
+ bottomNav ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "shrink-0 lg:hidden", children: bottomNav }) : null
3911
3899
  ] })
3912
3900
  ]
3913
3901
  }
@@ -3961,6 +3949,7 @@ function SuiteAppLayout({
3961
3949
  SuiteThemeProvider,
3962
3950
  SuiteToolbar,
3963
3951
  SuiteUserMenu,
3952
+ SuiteWorkspaceSwitcher,
3964
3953
  TodayAskAiCard,
3965
3954
  TodayCalibrating,
3966
3955
  TodayEmptyAction,
@@ -3976,11 +3965,14 @@ function SuiteAppLayout({
3976
3965
  appSwitcherMarkClass,
3977
3966
  appSwitcherMenuItemClass,
3978
3967
  appSwitcherTriggerClass,
3968
+ classifySuiteHref,
3979
3969
  cn,
3970
+ consumePendingAskAiPrompt,
3980
3971
  getTodayGreeting,
3981
3972
  m,
3982
3973
  openSuiteAskAi,
3983
3974
  resolveSuiteNotificationHref,
3975
+ sidebarColumnWidth,
3984
3976
  sourceToSuiteApp,
3985
3977
  suiteAppBaseUrl,
3986
3978
  suiteAppLabel,