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