@xenide-io/the-old-ui-theme 0.6.3 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/suite.mjs CHANGED
@@ -19,12 +19,12 @@ function TodayPageFrame({ children }) {
19
19
  "div",
20
20
  {
21
21
  "data-test": "today-page",
22
- className: "today-page-frame relative flex min-h-0 w-full flex-1 flex-col overflow-x-hidden",
22
+ className: "today-page-frame relative flex min-h-0 w-full flex-1 flex-col overflow-x-clip",
23
23
  children: [
24
24
  /* @__PURE__ */ jsxs(
25
25
  "div",
26
26
  {
27
- className: "pointer-events-none absolute inset-0 overflow-hidden",
27
+ className: "today-page-frame__backdrop pointer-events-none overflow-hidden",
28
28
  "aria-hidden": true,
29
29
  children: [
30
30
  /* @__PURE__ */ jsx("div", { className: "today-page-frame__glow absolute inset-0" }),
@@ -1094,7 +1094,7 @@ var SUITE_APPS2 = [
1094
1094
  name: "Kraken",
1095
1095
  description: "Documents and knowledge",
1096
1096
  icon: "/kraken-icon.svg",
1097
- landing: "/today",
1097
+ landing: "/ask",
1098
1098
  baseUrlEnv: "NEXT_PUBLIC_KRAKEN_URL",
1099
1099
  baseUrlFallback: "http://localhost:3003"
1100
1100
  },
@@ -1625,7 +1625,7 @@ function SuitePage({
1625
1625
  role,
1626
1626
  "aria-live": ariaLive,
1627
1627
  className: cn(
1628
- "suite-page-stage flex w-full min-w-0 flex-col",
1628
+ "suite-page-stage relative flex w-full min-w-0 flex-col",
1629
1629
  inset && "suite-page-inset",
1630
1630
  className
1631
1631
  ),
@@ -1650,20 +1650,28 @@ function SuitePageHeader({
1650
1650
  badge,
1651
1651
  /** Breadcrumb line rendered above the title. */
1652
1652
  breadcrumb,
1653
- /** Stick to the top of the scrollport (desktop page titles). */
1654
- sticky = false
1653
+ /**
1654
+ * Keep the title parked at the top of the scrollport while the page scrolls.
1655
+ * On by default — opt out only for pages that are not the page's own title.
1656
+ */
1657
+ sticky = true
1655
1658
  }) {
1656
1659
  const headerRef = useRef2(null);
1657
1660
  const [stuck, setStuck] = useState6(false);
1658
1661
  useEffect5(() => {
1659
- var _a;
1660
1662
  if (!sticky) return;
1663
+ if (typeof IntersectionObserver === "undefined") return;
1661
1664
  const el = headerRef.current;
1662
1665
  if (!el) return;
1666
+ const parent = el.parentElement;
1667
+ if (!parent) return;
1663
1668
  const sentinel = document.createElement("div");
1664
1669
  sentinel.setAttribute("aria-hidden", "true");
1665
- sentinel.style.cssText = "position:relative;height:1px;width:100%;margin:0;padding:0;pointer-events:none";
1666
- (_a = el.parentElement) == null ? void 0 : _a.insertBefore(sentinel, el);
1670
+ sentinel.style.cssText = "position:absolute;top:0;left:0;height:1px;width:1px;margin:0;padding:0;pointer-events:none";
1671
+ if (getComputedStyle(parent).position === "static") {
1672
+ parent.style.position = "relative";
1673
+ }
1674
+ parent.insertBefore(sentinel, el);
1667
1675
  const observer = new IntersectionObserver(
1668
1676
  ([entry]) => {
1669
1677
  setStuck(!entry.isIntersecting);
@@ -1680,7 +1688,7 @@ function SuitePageHeader({
1680
1688
  TodayTimeIcon,
1681
1689
  {
1682
1690
  className: cn(
1683
- "h-7 w-7 text-amber-500 sm:h-8 sm:w-8",
1691
+ "h-6 w-6 text-amber-500",
1684
1692
  todayIconClassName
1685
1693
  )
1686
1694
  }
@@ -1692,9 +1700,8 @@ function SuitePageHeader({
1692
1700
  "data-test": dataTest,
1693
1701
  "data-stuck": sticky ? stuck ? "true" : "false" : void 0,
1694
1702
  className: cn(
1695
- "flex min-w-0 flex-col items-start gap-3 pb-2 sm:flex-row sm:justify-between sm:gap-4 sm:pb-3",
1703
+ "flex min-w-0 shrink-0 flex-col items-start gap-3 pb-2 sm:flex-row sm:justify-between sm:gap-4 sm:pb-3",
1696
1704
  // No extra pt here — page inset sets the shared title baseline.
1697
- // Stuck state adds pad via .suite-page-header-sticky[data-stuck].
1698
1705
  sticky && "suite-page-header-sticky",
1699
1706
  className
1700
1707
  ),
@@ -1706,7 +1713,7 @@ function SuitePageHeader({
1706
1713
  resolvedIcon ? /* @__PURE__ */ jsx12(
1707
1714
  "div",
1708
1715
  {
1709
- className: "hidden shrink-0 sm:flex sm:items-center [&_svg]:h-7 [&_svg]:w-7 sm:[&_svg]:h-8 sm:[&_svg]:w-8",
1716
+ className: "hidden shrink-0 sm:flex sm:items-center [&_svg]:h-6 [&_svg]:w-6",
1710
1717
  "aria-hidden": "true",
1711
1718
  children: resolvedIcon
1712
1719
  }
@@ -1716,7 +1723,7 @@ function SuitePageHeader({
1716
1723
  "h1",
1717
1724
  {
1718
1725
  className: cn(
1719
- "min-w-0 break-words font-display text-[1.625rem] font-bold leading-none tracking-tight text-ph-ink text-balance sm:text-[2.25rem]",
1726
+ "min-w-0 break-words font-display text-[1.375rem] font-semibold leading-tight tracking-tight text-ph-ink text-balance sm:text-2xl",
1720
1727
  titleClassName
1721
1728
  ),
1722
1729
  children: title
@@ -1730,7 +1737,7 @@ function SuitePageHeader({
1730
1737
  {
1731
1738
  className: cn(
1732
1739
  "mt-1.5 max-w-2xl text-pretty text-sm leading-5 text-ph-subtle",
1733
- resolvedIcon && "sm:pl-11"
1740
+ resolvedIcon && "sm:pl-9"
1734
1741
  ),
1735
1742
  children: description
1736
1743
  }
@@ -1868,6 +1875,20 @@ function computeInitials(name, email) {
1868
1875
  const source = (name || email || "?").trim();
1869
1876
  return source.split(/[\s@]+/).filter(Boolean).slice(0, 2).map((part) => part[0]).join("").toUpperCase();
1870
1877
  }
1878
+ var AVATAR_TOKENS = [
1879
+ "bg-ph-brand",
1880
+ "bg-ph-violet",
1881
+ "bg-ph-info",
1882
+ "bg-ph-success",
1883
+ "bg-ph-danger"
1884
+ ];
1885
+ function avatarColorClass(seed) {
1886
+ let hash = 0;
1887
+ for (let i = 0; i < seed.length; i += 1) {
1888
+ hash = seed.charCodeAt(i) + ((hash << 5) - hash);
1889
+ }
1890
+ return AVATAR_TOKENS[Math.abs(hash) % AVATAR_TOKENS.length];
1891
+ }
1871
1892
  function SuiteUserMenu({
1872
1893
  name,
1873
1894
  email,
@@ -1883,17 +1904,27 @@ function SuiteUserMenu({
1883
1904
  }) {
1884
1905
  const router = useRouter2();
1885
1906
  const initials = fallbackInitials != null ? fallbackInitials : computeInitials(name, email);
1886
- const avatar = /* @__PURE__ */ jsx14("span", { className: "inline-flex h-11 w-11 items-center justify-center rounded-full ring-2 ring-ph-border ring-offset-1 ring-offset-ph-canvas transition-colors hover:bg-ph-muted", children: image ? /* @__PURE__ */ jsx14(
1907
+ const fallbackColor = avatarColorClass(email || name || "?");
1908
+ const avatar = /* @__PURE__ */ jsx14("span", { className: "inline-flex h-10 w-10 items-center justify-center overflow-hidden rounded-full border border-ph-border", children: image ? /* @__PURE__ */ jsx14(
1887
1909
  Image2,
1888
1910
  {
1889
1911
  src: image,
1890
1912
  alt: "",
1891
1913
  width: 40,
1892
1914
  height: 40,
1893
- className: "h-10 w-10 rounded-full object-cover",
1915
+ className: "h-full w-full object-cover",
1894
1916
  unoptimized: true
1895
1917
  }
1896
- ) : /* @__PURE__ */ jsx14("span", { className: "flex h-10 w-10 items-center justify-center rounded-full bg-ph-muted text-sm font-semibold text-ph-brand", children: initials }) });
1918
+ ) : /* @__PURE__ */ jsx14(
1919
+ "span",
1920
+ {
1921
+ className: cn(
1922
+ "flex h-full w-full items-center justify-center text-sm font-semibold text-white",
1923
+ fallbackColor
1924
+ ),
1925
+ children: initials
1926
+ }
1927
+ ) });
1897
1928
  const accountMenu = /* @__PURE__ */ jsxs12(
1898
1929
  DropdownMenu,
1899
1930
  {
@@ -1906,7 +1937,10 @@ function SuiteUserMenu({
1906
1937
  sideOffset: 6,
1907
1938
  collisionPadding: 16,
1908
1939
  modal: false,
1909
- className: showSignOutAction ? void 0 : className,
1940
+ className: cn(
1941
+ "[&_.ph-dropdown-trigger]:rounded-full",
1942
+ showSignOutAction ? void 0 : className
1943
+ ),
1910
1944
  "data-test": dataTest,
1911
1945
  children: [
1912
1946
  /* @__PURE__ */ jsxs12("div", { className: "px-2 py-1.5", children: [
@@ -1935,21 +1969,30 @@ function SuiteUserMenu({
1935
1969
  if (!showSignOutAction) {
1936
1970
  return accountMenu;
1937
1971
  }
1938
- return /* @__PURE__ */ jsxs12("div", { className: cn("flex min-w-0 items-center gap-2", className), children: [
1939
- accountMenu,
1940
- /* @__PURE__ */ jsx14(
1941
- "button",
1942
- {
1943
- type: "button",
1944
- "data-test": `${dataTest}-visible-sign-out`,
1945
- title: "Sign out",
1946
- "aria-label": "Sign out",
1947
- className: "inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-lg text-ph-mutedtext transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ph-focus-ring",
1948
- onClick: onSignOut,
1949
- children: /* @__PURE__ */ jsx14(LogOut, { className: "h-4 w-4", "aria-hidden": true })
1950
- }
1951
- )
1952
- ] });
1972
+ return /* @__PURE__ */ jsxs12(
1973
+ "div",
1974
+ {
1975
+ className: cn(
1976
+ "flex w-full min-w-0 flex-1 items-center justify-between gap-2",
1977
+ className
1978
+ ),
1979
+ children: [
1980
+ /* @__PURE__ */ jsx14("div", { className: "shrink-0", children: accountMenu }),
1981
+ /* @__PURE__ */ jsx14(
1982
+ "button",
1983
+ {
1984
+ type: "button",
1985
+ "data-test": `${dataTest}-visible-sign-out`,
1986
+ title: "Sign out",
1987
+ "aria-label": "Sign out",
1988
+ className: "inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-lg text-ph-mutedtext transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ph-focus-ring",
1989
+ onClick: onSignOut,
1990
+ children: /* @__PURE__ */ jsx14(LogOut, { className: "h-4 w-4", "aria-hidden": true })
1991
+ }
1992
+ )
1993
+ ]
1994
+ }
1995
+ );
1953
1996
  }
1954
1997
 
1955
1998
  // src/suite/components/suite-bottom-nav.tsx
@@ -2093,10 +2136,21 @@ import {
2093
2136
  Suspense,
2094
2137
  useCallback as useCallback2,
2095
2138
  useEffect as useEffect6,
2139
+ useLayoutEffect,
2096
2140
  useRef as useRef3,
2097
2141
  useState as useState7
2098
2142
  } from "react";
2099
- import { Check as Check2, Copy, Sparks, Xmark as X2 } from "iconoir-react";
2143
+ import {
2144
+ ArrowDown,
2145
+ ArrowUp,
2146
+ Check as Check2,
2147
+ Copy,
2148
+ Erase,
2149
+ Sparks,
2150
+ Square,
2151
+ WarningTriangle,
2152
+ Xmark as X2
2153
+ } from "iconoir-react";
2100
2154
  import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
2101
2155
  var SuiteAiBlockNoteMessage = lazy(() => import("./ai-message-blocknote-4Q4U2T2W.mjs"));
2102
2156
  var SUITE_OPEN_ASK_AI_EVENT = "shellstack:open-ask-ai";
@@ -2168,11 +2222,14 @@ function SuiteAiPanel({
2168
2222
  const [hydrating, setHydrating] = useState7(false);
2169
2223
  const [error, setError] = useState7("");
2170
2224
  const [copiedId, setCopiedId] = useState7(null);
2225
+ const [atBottom, setAtBottom] = useState7(true);
2171
2226
  const [launcherSide, setLauncherSide] = useState7("right");
2172
2227
  const [panelSide, setPanelSide] = useState7("right");
2173
2228
  const [launcherPosition, setLauncherPosition] = useState7(null);
2174
2229
  const [draggingLauncher, setDraggingLauncher] = useState7(false);
2175
2230
  const scrollerRef = useRef3(null);
2231
+ const textareaRef = useRef3(null);
2232
+ const abortRef = useRef3(null);
2176
2233
  const launcherRef = useRef3(null);
2177
2234
  const launcherSideRef = useRef3("right");
2178
2235
  const launcherPositionRef = useRef3(null);
@@ -2224,10 +2281,17 @@ function SuiteAiPanel({
2224
2281
  window.addEventListener("resize", placeLauncher);
2225
2282
  return () => window.removeEventListener("resize", placeLauncher);
2226
2283
  }, []);
2227
- const scrollToBottom = useCallback2(() => {
2284
+ const scrollToBottom = useCallback2((behavior = "auto") => {
2228
2285
  const el = scrollerRef.current;
2229
2286
  if (!el) return;
2230
- el.scrollTop = el.scrollHeight;
2287
+ el.scrollTo({ top: el.scrollHeight, behavior });
2288
+ setAtBottom(true);
2289
+ }, []);
2290
+ const handleScroll = useCallback2(() => {
2291
+ const el = scrollerRef.current;
2292
+ if (!el) return;
2293
+ const distance = el.scrollHeight - el.scrollTop - el.clientHeight;
2294
+ setAtBottom(distance < 64);
2231
2295
  }, []);
2232
2296
  const hydrate = useCallback2(async () => {
2233
2297
  var _a;
@@ -2271,20 +2335,46 @@ function SuiteAiPanel({
2271
2335
  });
2272
2336
  }, [open, hydrate]);
2273
2337
  useEffect6(() => {
2274
- if (open) scrollToBottom();
2275
- }, [messages, loading, open, scrollToBottom]);
2338
+ if (open && atBottom) scrollToBottom();
2339
+ }, [messages, loading, open, atBottom, scrollToBottom]);
2340
+ useEffect6(() => {
2341
+ var _a;
2342
+ if (open && !hydrating) (_a = textareaRef.current) == null ? void 0 : _a.focus();
2343
+ }, [open, hydrating]);
2344
+ useEffect6(() => {
2345
+ if (!open) return;
2346
+ function onKey(event) {
2347
+ if (event.key === "Escape") setOpen(false);
2348
+ }
2349
+ window.addEventListener("keydown", onKey);
2350
+ return () => window.removeEventListener("keydown", onKey);
2351
+ }, [open]);
2352
+ useLayoutEffect(() => {
2353
+ const el = textareaRef.current;
2354
+ if (!el) return;
2355
+ el.style.height = "auto";
2356
+ el.style.height = `${Math.min(el.scrollHeight, 176)}px`;
2357
+ }, [prompt, open]);
2276
2358
  async function ask(text) {
2277
2359
  var _a;
2278
2360
  const trimmed = text.trim();
2279
2361
  if (!trimmed || loading) return;
2362
+ const controller = new AbortController();
2363
+ abortRef.current = controller;
2280
2364
  setLoading(true);
2281
2365
  setError("");
2282
2366
  setPrompt("");
2367
+ setAtBottom(true);
2283
2368
  setMessages((prev) => [...prev, { role: "user", content: trimmed }]);
2284
2369
  try {
2285
- const result = await sendMessage({ prompt: trimmed });
2370
+ const result = await sendMessage({
2371
+ prompt: trimmed,
2372
+ signal: controller.signal
2373
+ });
2374
+ if (controller.signal.aborted) return;
2286
2375
  setMessages((_a = result.messages) != null ? _a : []);
2287
2376
  } catch (err) {
2377
+ if (controller.signal.aborted) return;
2288
2378
  setMessages(
2289
2379
  (prev) => {
2290
2380
  var _a2, _b;
@@ -2296,9 +2386,23 @@ function SuiteAiPanel({
2296
2386
  err instanceof Error ? err.message : "The AI request failed. Try again."
2297
2387
  );
2298
2388
  } finally {
2389
+ if (abortRef.current === controller) abortRef.current = null;
2299
2390
  setLoading(false);
2300
2391
  }
2301
2392
  }
2393
+ function stop() {
2394
+ var _a;
2395
+ (_a = abortRef.current) == null ? void 0 : _a.abort();
2396
+ abortRef.current = null;
2397
+ setLoading(false);
2398
+ }
2399
+ function retry() {
2400
+ var _a;
2401
+ const lastUser = (_a = [...messages].reverse().find((m2) => m2.role === "user")) == null ? void 0 : _a.content;
2402
+ setError("");
2403
+ if (lastUser) void ask(lastUser);
2404
+ else void hydrate();
2405
+ }
2302
2406
  async function handleClear() {
2303
2407
  if (loading) return;
2304
2408
  try {
@@ -2460,154 +2564,258 @@ function SuiteAiPanel({
2460
2564
  ),
2461
2565
  /* @__PURE__ */ jsx17("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." })
2462
2566
  ] }) : null,
2463
- open ? /* @__PURE__ */ jsxs15("div", { className: "fixed inset-0 z-40", role: "dialog", "aria-modal": "true", children: [
2464
- /* @__PURE__ */ jsx17(
2465
- "button",
2466
- {
2467
- type: "button",
2468
- className: "absolute inset-0 bg-black/25",
2469
- onClick: () => setOpen(false),
2470
- "aria-label": "Close Ask AI"
2471
- }
2472
- ),
2473
- /* @__PURE__ */ jsxs15(
2474
- "aside",
2475
- {
2476
- className: `absolute inset-y-0 flex w-full max-w-md flex-col bg-ph-surface shadow-2xl ${panelSide === "left" ? "left-0 border-r border-ph-border" : "right-0 border-l border-ph-border"}`,
2477
- "data-test": "suite-ask-ai-panel",
2478
- children: [
2479
- /* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between border-b border-ph-border px-4 py-3", children: [
2480
- /* @__PURE__ */ jsxs15("div", { className: "min-w-0", children: [
2481
- /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
2482
- /* @__PURE__ */ jsx17(Icon, { className: "h-4 w-4 shrink-0 text-ph-brand" }),
2483
- /* @__PURE__ */ jsx17("span", { className: "text-sm font-semibold text-ph-ink", children: title })
2567
+ open ? /* @__PURE__ */ jsxs15(
2568
+ "div",
2569
+ {
2570
+ className: "fixed inset-0 z-40",
2571
+ role: "dialog",
2572
+ "aria-modal": "true",
2573
+ "aria-label": title,
2574
+ children: [
2575
+ /* @__PURE__ */ jsx17(
2576
+ "button",
2577
+ {
2578
+ type: "button",
2579
+ className: "absolute inset-0 bg-ph-ink/25",
2580
+ onClick: () => setOpen(false),
2581
+ "aria-label": "Close Ask AI",
2582
+ tabIndex: -1
2583
+ }
2584
+ ),
2585
+ /* @__PURE__ */ jsxs15(
2586
+ "aside",
2587
+ {
2588
+ 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"}`,
2589
+ "data-test": "suite-ask-ai-panel",
2590
+ children: [
2591
+ /* @__PURE__ */ jsxs15("header", { className: "flex items-center gap-2 px-4 py-3", children: [
2592
+ /* @__PURE__ */ jsx17("h2", { className: "min-w-0 flex-1 truncate text-sm font-semibold text-ph-ink", children: title }),
2593
+ messages.length > 0 ? /* @__PURE__ */ jsxs15(
2594
+ "button",
2595
+ {
2596
+ type: "button",
2597
+ onClick: () => void handleClear(),
2598
+ disabled: loading,
2599
+ 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",
2600
+ "data-test": "ask-ai-clear",
2601
+ children: [
2602
+ /* @__PURE__ */ jsx17(Erase, { className: "h-3.5 w-3.5" }),
2603
+ "Clear"
2604
+ ]
2605
+ }
2606
+ ) : null,
2607
+ /* @__PURE__ */ jsx17(
2608
+ "button",
2609
+ {
2610
+ type: "button",
2611
+ onClick: () => setOpen(false),
2612
+ 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",
2613
+ "aria-label": "Close Ask AI",
2614
+ children: /* @__PURE__ */ jsx17(X2, { className: "h-4 w-4" })
2615
+ }
2616
+ )
2484
2617
  ] }),
2485
- /* @__PURE__ */ jsx17("p", { className: "mt-0.5 text-[11px] text-ph-mutedtext", children: "Shared across your suite \xB7 same chat in every app" })
2486
- ] }),
2487
- /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-1", children: [
2488
- messages.length > 0 ? /* @__PURE__ */ jsx17(
2489
- "button",
2490
- {
2491
- type: "button",
2492
- onClick: () => void handleClear(),
2493
- disabled: loading,
2494
- className: "rounded-md px-2 py-1 text-xs text-ph-mutedtext hover:bg-ph-muted hover:text-ph-ink disabled:opacity-50",
2495
- "data-test": "ask-ai-clear",
2496
- children: "Clear"
2497
- }
2498
- ) : null,
2499
- /* @__PURE__ */ jsx17(
2500
- "button",
2501
- {
2502
- type: "button",
2503
- onClick: () => setOpen(false),
2504
- className: "rounded p-1 hover:bg-ph-muted",
2505
- "aria-label": "Close",
2506
- children: /* @__PURE__ */ jsx17(X2, { className: "h-4 w-4" })
2507
- }
2508
- )
2509
- ] })
2510
- ] }),
2511
- presets.length > 0 && messages.length === 0 ? /* @__PURE__ */ jsx17("div", { className: "flex flex-wrap gap-1.5 border-b border-ph-border px-4 py-2", children: presets.map((preset) => /* @__PURE__ */ jsx17(
2512
- "button",
2513
- {
2514
- type: "button",
2515
- onClick: () => void ask(preset.prompt),
2516
- disabled: loading || hydrating,
2517
- className: "rounded-full border border-ph-border px-2.5 py-1 text-xs text-ph-ink hover:bg-ph-muted disabled:opacity-50",
2518
- children: preset.label
2519
- },
2520
- preset.label
2521
- )) }) : null,
2522
- /* @__PURE__ */ jsxs15(
2523
- "div",
2524
- {
2525
- ref: scrollerRef,
2526
- className: "flex-1 space-y-3 overflow-y-auto p-4",
2527
- children: [
2528
- error ? /* @__PURE__ */ jsx17("p", { className: "bg-ph-danger/10 rounded-lg px-3 py-2 text-sm text-ph-danger", children: error }) : null,
2529
- hydrating && messages.length === 0 ? /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-sm text-ph-mutedtext", children: [
2530
- /* @__PURE__ */ jsx17(Spinner, { className: "h-4 w-4 animate-spin" }),
2531
- "Loading chat\u2026"
2532
- ] }) : null,
2533
- !hydrating && messages.length === 0 && !error ? /* @__PURE__ */ jsxs15("div", { className: "mt-10 px-2 text-center", children: [
2534
- /* @__PURE__ */ jsx17("p", { className: "font-display text-base font-semibold text-ph-ink", children: "How can I help?" }),
2535
- /* @__PURE__ */ jsx17("p", { className: "mt-2 text-sm leading-relaxed text-ph-subtle", children: emptyState })
2536
- ] }) : null,
2537
- messages.map((message, index) => {
2538
- const isUser = message.role === "user";
2539
- return /* @__PURE__ */ jsxs15(
2540
- "div",
2541
- {
2542
- className: isUser ? "ml-8 rounded-2xl bg-ph-brand/10 px-3.5 py-2.5 text-sm text-ph-ink" : "mr-4 rounded-2xl bg-ph-muted px-3.5 py-2.5 text-sm text-ph-ink",
2543
- "data-test": isUser ? "ask-ai-user" : "ask-ai-assistant",
2544
- children: [
2545
- isUser ? /* @__PURE__ */ jsx17("p", { className: "whitespace-pre-wrap", children: message.content }) : /* @__PURE__ */ jsx17(
2546
- Suspense,
2547
- {
2548
- fallback: /* @__PURE__ */ jsx17("p", { className: "whitespace-pre-wrap", children: message.content }),
2549
- children: /* @__PURE__ */ jsx17(SuiteAiBlockNoteMessage, { markdown: message.content })
2550
- }
2551
- ),
2552
- !isUser ? /* @__PURE__ */ jsxs15(
2618
+ /* @__PURE__ */ jsxs15("div", { className: "relative min-h-0 flex-1", children: [
2619
+ /* @__PURE__ */ jsxs15(
2620
+ "div",
2621
+ {
2622
+ ref: scrollerRef,
2623
+ onScroll: handleScroll,
2624
+ role: "log",
2625
+ "aria-live": "polite",
2626
+ "aria-label": "Conversation",
2627
+ className: "suite-scroll-lock h-full space-y-5 overflow-y-auto px-4 py-5",
2628
+ children: [
2629
+ hydrating && messages.length === 0 ? /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-sm text-ph-subtle", children: [
2630
+ /* @__PURE__ */ jsx17(Spinner, { className: "h-4 w-4 animate-spin" }),
2631
+ "Loading your chat\u2026"
2632
+ ] }) : null,
2633
+ !hydrating && messages.length === 0 && !error ? /* @__PURE__ */ jsxs15("div", { className: "mx-auto mt-6 flex max-w-sm flex-col items-center px-2 text-center", children: [
2634
+ /* @__PURE__ */ jsx17("span", { className: "flex h-12 w-12 items-center justify-center rounded-2xl bg-ph-brand/10 text-ph-brand", children: /* @__PURE__ */ jsx17(Icon, { className: "h-6 w-6" }) }),
2635
+ /* @__PURE__ */ jsx17("p", { className: "mt-4 font-display text-lg font-semibold text-ph-ink", children: "How can I help?" }),
2636
+ /* @__PURE__ */ jsx17("p", { className: "mt-1.5 text-sm leading-relaxed text-ph-subtle", children: emptyState }),
2637
+ presets.length > 0 ? /* @__PURE__ */ jsx17("div", { className: "mt-5 flex w-full flex-col gap-2", children: presets.map((preset) => /* @__PURE__ */ jsxs15(
2553
2638
  "button",
2554
2639
  {
2555
2640
  type: "button",
2556
- onClick: () => void copyMessage(index, message.content),
2557
- className: "mt-2 inline-flex items-center gap-1 text-xs text-ph-mutedtext hover:text-ph-ink",
2641
+ onClick: () => void ask(preset.prompt),
2642
+ disabled: loading || hydrating,
2643
+ 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",
2558
2644
  children: [
2559
- copiedId === index ? /* @__PURE__ */ jsx17(Check2, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx17(Copy, { className: "h-3 w-3" }),
2560
- copiedId === index ? "Copied" : "Copy"
2645
+ /* @__PURE__ */ jsx17(Sparks, { className: "h-4 w-4 shrink-0 text-ph-brand" }),
2646
+ /* @__PURE__ */ jsx17("span", { className: "min-w-0 flex-1 truncate", children: preset.label }),
2647
+ /* @__PURE__ */ jsx17(ArrowUp, { className: "h-3.5 w-3.5 shrink-0 rotate-45 text-ph-mutedtext transition-transform group-hover:translate-x-0.5" })
2561
2648
  ]
2562
- }
2563
- ) : null
2564
- ]
2565
- },
2566
- `${message.role}-${index}-${message.content.slice(0, 24)}`
2567
- );
2568
- }),
2569
- loading ? /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-sm text-ph-mutedtext", children: [
2570
- /* @__PURE__ */ jsx17(Spinner, { className: "h-4 w-4 animate-spin" }),
2571
- "Thinking\u2026"
2572
- ] }) : null
2573
- ]
2574
- }
2575
- ),
2576
- /* @__PURE__ */ jsx17("div", { className: "border-t border-ph-border p-3", children: /* @__PURE__ */ jsxs15("div", { className: "flex gap-2", children: [
2577
- /* @__PURE__ */ jsx17(
2578
- "textarea",
2579
- {
2580
- value: prompt,
2581
- onChange: (e) => setPrompt(e.target.value),
2582
- onKeyDown: (e) => {
2583
- if (e.key === "Enter" && !e.shiftKey) {
2584
- e.preventDefault();
2585
- void ask(prompt);
2649
+ },
2650
+ preset.label
2651
+ )) }) : null
2652
+ ] }) : null,
2653
+ messages.map((message, index) => {
2654
+ const isUser = message.role === "user";
2655
+ if (isUser) {
2656
+ return /* @__PURE__ */ jsx17(
2657
+ "div",
2658
+ {
2659
+ className: "flex justify-end",
2660
+ "data-test": "ask-ai-user",
2661
+ children: /* @__PURE__ */ jsx17("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 })
2662
+ },
2663
+ `user-${index}`
2664
+ );
2665
+ }
2666
+ return /* @__PURE__ */ jsxs15(
2667
+ "div",
2668
+ {
2669
+ className: "flex gap-3",
2670
+ "data-test": "ask-ai-assistant",
2671
+ children: [
2672
+ /* @__PURE__ */ jsx17(
2673
+ "span",
2674
+ {
2675
+ className: "mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-ph-brand/10 text-ph-brand",
2676
+ "aria-hidden": true,
2677
+ children: /* @__PURE__ */ jsx17(Icon, { className: "h-4 w-4" })
2678
+ }
2679
+ ),
2680
+ /* @__PURE__ */ jsxs15("div", { className: "min-w-0 flex-1", children: [
2681
+ /* @__PURE__ */ jsx17("div", { className: "text-sm leading-relaxed text-ph-ink", children: /* @__PURE__ */ jsx17(
2682
+ Suspense,
2683
+ {
2684
+ fallback: /* @__PURE__ */ jsx17("p", { className: "whitespace-pre-wrap break-words", children: message.content }),
2685
+ children: /* @__PURE__ */ jsx17(
2686
+ SuiteAiBlockNoteMessage,
2687
+ {
2688
+ markdown: message.content
2689
+ }
2690
+ )
2691
+ }
2692
+ ) }),
2693
+ /* @__PURE__ */ jsxs15(
2694
+ "button",
2695
+ {
2696
+ type: "button",
2697
+ onClick: () => void copyMessage(index, message.content),
2698
+ 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",
2699
+ children: [
2700
+ copiedId === index ? /* @__PURE__ */ jsx17(Check2, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx17(Copy, { className: "h-3 w-3" }),
2701
+ copiedId === index ? "Copied" : "Copy"
2702
+ ]
2703
+ }
2704
+ )
2705
+ ] })
2706
+ ]
2707
+ },
2708
+ `assistant-${index}`
2709
+ );
2710
+ }),
2711
+ loading ? /* @__PURE__ */ jsxs15("div", { className: "flex gap-3", "aria-hidden": true, children: [
2712
+ /* @__PURE__ */ jsx17("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__ */ jsx17(Icon, { className: "h-4 w-4" }) }),
2713
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-1.5 py-1.5", children: [
2714
+ /* @__PURE__ */ jsx17("span", { className: "h-1.5 w-1.5 rounded-full bg-ph-mutedtext motion-safe:animate-bounce" }),
2715
+ /* @__PURE__ */ jsx17("span", { className: "h-1.5 w-1.5 rounded-full bg-ph-mutedtext motion-safe:animate-bounce [animation-delay:0.15s]" }),
2716
+ /* @__PURE__ */ jsx17("span", { className: "h-1.5 w-1.5 rounded-full bg-ph-mutedtext motion-safe:animate-bounce [animation-delay:0.3s]" })
2717
+ ] })
2718
+ ] }) : null,
2719
+ loading ? /* @__PURE__ */ jsx17("span", { className: "sr-only", "aria-live": "polite", children: "Assistant is responding\u2026" }) : null,
2720
+ error ? /* @__PURE__ */ jsxs15(
2721
+ "div",
2722
+ {
2723
+ role: "alert",
2724
+ 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",
2725
+ children: [
2726
+ /* @__PURE__ */ jsx17(WarningTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-ph-danger" }),
2727
+ /* @__PURE__ */ jsxs15("div", { className: "min-w-0 flex-1", children: [
2728
+ /* @__PURE__ */ jsx17("p", { className: "font-medium text-ph-danger", children: "Something went wrong" }),
2729
+ /* @__PURE__ */ jsx17("p", { className: "mt-0.5 text-ph-subtle", children: error }),
2730
+ /* @__PURE__ */ jsx17(
2731
+ "button",
2732
+ {
2733
+ type: "button",
2734
+ onClick: retry,
2735
+ disabled: loading,
2736
+ 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",
2737
+ children: "Try again"
2738
+ }
2739
+ )
2740
+ ] })
2741
+ ]
2742
+ }
2743
+ ) : null
2744
+ ]
2586
2745
  }
2587
- },
2588
- placeholder: "Ask anything\u2026",
2589
- rows: 2,
2590
- disabled: loading,
2591
- "data-test": "ask-ai-input",
2592
- className: "flex-1 resize-none rounded-xl border border-ph-border bg-ph-canvas p-2.5 text-sm text-ph-ink outline-none focus:ring-1 focus:ring-ph-brand"
2593
- }
2594
- ),
2595
- /* @__PURE__ */ jsx17(
2596
- "button",
2597
- {
2598
- type: "button",
2599
- onClick: () => void ask(prompt),
2600
- disabled: loading || !prompt.trim(),
2601
- "data-test": "ask-ai-send",
2602
- className: "self-end rounded-xl bg-ph-brand px-3.5 py-2 text-sm font-medium text-white disabled:opacity-50",
2603
- children: "Ask"
2604
- }
2605
- )
2606
- ] }) })
2607
- ]
2608
- }
2609
- )
2610
- ] }) : null
2746
+ ),
2747
+ !atBottom ? /* @__PURE__ */ jsx17(
2748
+ "button",
2749
+ {
2750
+ type: "button",
2751
+ onClick: () => scrollToBottom("smooth"),
2752
+ 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]",
2753
+ "aria-label": "Scroll to latest",
2754
+ children: /* @__PURE__ */ jsx17(ArrowDown, { className: "h-4 w-4" })
2755
+ }
2756
+ ) : null
2757
+ ] }),
2758
+ /* @__PURE__ */ jsxs15("div", { className: "px-3 pb-[max(0.75rem,env(safe-area-inset-bottom))] pt-1", children: [
2759
+ /* @__PURE__ */ jsxs15("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: [
2760
+ /* @__PURE__ */ jsx17("label", { htmlFor: "suite-ask-ai-input", className: "sr-only", children: "Ask a question" }),
2761
+ /* @__PURE__ */ jsx17(
2762
+ "textarea",
2763
+ {
2764
+ id: "suite-ask-ai-input",
2765
+ ref: textareaRef,
2766
+ value: prompt,
2767
+ onChange: (e) => setPrompt(e.target.value),
2768
+ onKeyDown: (e) => {
2769
+ if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
2770
+ e.preventDefault();
2771
+ void ask(prompt);
2772
+ }
2773
+ },
2774
+ placeholder: "Ask anything\u2026",
2775
+ rows: 1,
2776
+ "data-test": "ask-ai-input",
2777
+ 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"
2778
+ }
2779
+ ),
2780
+ loading ? /* @__PURE__ */ jsx17(
2781
+ "button",
2782
+ {
2783
+ type: "button",
2784
+ onClick: stop,
2785
+ "data-test": "ask-ai-stop",
2786
+ 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",
2787
+ "aria-label": "Stop generating",
2788
+ children: /* @__PURE__ */ jsx17(Square, { className: "h-3.5 w-3.5 fill-current" })
2789
+ }
2790
+ ) : /* @__PURE__ */ jsx17(
2791
+ "button",
2792
+ {
2793
+ type: "button",
2794
+ onClick: () => void ask(prompt),
2795
+ disabled: !prompt.trim(),
2796
+ "data-test": "ask-ai-send",
2797
+ 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",
2798
+ "aria-label": "Send message",
2799
+ children: /* @__PURE__ */ jsx17(ArrowUp, { className: "h-[18px] w-[18px]" })
2800
+ }
2801
+ )
2802
+ ] }),
2803
+ /* @__PURE__ */ jsxs15("p", { className: "mt-1.5 px-1 text-center text-xs text-ph-mutedtext", children: [
2804
+ /* @__PURE__ */ jsx17("kbd", { className: "font-mono", children: "Enter" }),
2805
+ " to send \xB7",
2806
+ " ",
2807
+ /* @__PURE__ */ jsx17("kbd", { className: "font-mono", children: "Shift" }),
2808
+ "+",
2809
+ /* @__PURE__ */ jsx17("kbd", { className: "font-mono", children: "Enter" }),
2810
+ " for a new line"
2811
+ ] })
2812
+ ] })
2813
+ ]
2814
+ }
2815
+ )
2816
+ ]
2817
+ }
2818
+ ) : null
2611
2819
  ] });
2612
2820
  }
2613
2821
 
@@ -3059,7 +3267,7 @@ function SuiteSidebar({
3059
3267
  {
3060
3268
  className: cn(
3061
3269
  "flex items-center gap-2",
3062
- collapsed ? "flex-col justify-center" : "min-w-0"
3270
+ collapsed ? "flex-col items-center justify-center [&_[data-test$='-visible-sign-out']]:hidden [&>div]:w-auto" : "w-full min-w-0"
3063
3271
  ),
3064
3272
  children: [
3065
3273
  renderNode(userMenu, collapsed),