@xenide-io/the-old-ui-theme 0.7.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/README.md +9 -10
  2. package/dist/{Chip-Cq3AiAji.d.mts → Chip-BzuNgJIa.d.mts} +81 -23
  3. package/dist/{Chip-Cq3AiAji.d.ts → Chip-BzuNgJIa.d.ts} +81 -23
  4. package/dist/{ai-message-blocknote-WABTHFY7.mjs → ai-message-blocknote-D4Z6DUCJ.mjs} +9 -3
  5. package/dist/{chunk-YB4TBWHV.mjs → chunk-2EO5VCXP.mjs} +688 -388
  6. package/dist/chunk-APKRZQRO.mjs +251 -0
  7. package/dist/chunk-BS7BZI5W.mjs +19 -0
  8. package/dist/chunk-IULORI3A.mjs +8 -0
  9. package/dist/index.d.mts +3 -3
  10. package/dist/index.d.ts +3 -3
  11. package/dist/index.js +997 -576
  12. package/dist/index.mjs +7 -1
  13. package/dist/suite.d.mts +136 -57
  14. package/dist/suite.d.ts +136 -57
  15. package/dist/suite.js +3115 -882
  16. package/dist/suite.mjs +2566 -661
  17. package/dist/theme-boot-P-oqKVZF.d.mts +74 -0
  18. package/dist/theme-boot-P-oqKVZF.d.ts +74 -0
  19. package/dist/theme-boot.d.mts +2 -0
  20. package/dist/theme-boot.d.ts +2 -0
  21. package/dist/theme-boot.js +44 -0
  22. package/dist/theme-boot.mjs +8 -0
  23. package/dist/ui.d.mts +5 -29
  24. package/dist/ui.d.ts +5 -29
  25. package/dist/ui.js +647 -352
  26. package/dist/ui.mjs +1 -1
  27. package/docs/README.md +34 -0
  28. package/docs/ai-quick-reference.md +56 -0
  29. package/docs/component-library-improvement-plan.md +751 -0
  30. package/docs/component-reference.md +81 -0
  31. package/docs/components.md +48 -0
  32. package/docs/demos.md +19 -0
  33. package/docs/installation.md +117 -0
  34. package/docs/suite-audit.md +34 -0
  35. package/docs/theme-token-reference.md +67 -0
  36. package/docs/themes.md +164 -0
  37. package/package.json +22 -21
  38. package/public/fonts/OpenRunde-Bold.woff2 +0 -0
  39. package/public/fonts/OpenRunde-Medium.woff2 +0 -0
  40. package/public/fonts/OpenRunde-Regular.woff2 +0 -0
  41. package/public/fonts/OpenRunde-Semibold.woff2 +0 -0
  42. package/scripts/package-smoke.mjs +43 -0
  43. package/src/app/globals.css +2 -1
  44. package/src/components/ui/Accordion.tsx +20 -6
  45. package/src/components/ui/Avatar.tsx +5 -3
  46. package/src/components/ui/Calendar.tsx +187 -63
  47. package/src/components/ui/Card.test.tsx +38 -0
  48. package/src/components/ui/Card.tsx +77 -14
  49. package/src/components/ui/CollapsibleSection.tsx +12 -4
  50. package/src/components/ui/CommandPalette.test.tsx +91 -4
  51. package/src/components/ui/CommandPalette.tsx +138 -128
  52. package/src/components/ui/EmptyState.tsx +2 -2
  53. package/src/components/ui/FormField.tsx +1 -1
  54. package/src/components/ui/Input.tsx +42 -18
  55. package/src/components/ui/Kbd.tsx +18 -3
  56. package/src/components/ui/Modal.tsx +3 -1
  57. package/src/components/ui/Progress.tsx +14 -5
  58. package/src/components/ui/SegmentedControl.tsx +2 -2
  59. package/src/components/ui/SettingsLayout.tsx +1 -1
  60. package/src/components/ui/Stat.tsx +3 -2
  61. package/src/components/ui/Table.tsx +8 -3
  62. package/src/components/ui/Tooltip.test.tsx +70 -1
  63. package/src/components/ui/Tooltip.tsx +53 -29
  64. package/src/components/ui/Typography.tsx +103 -49
  65. package/src/components/ui/avatar-stat.test.tsx +23 -0
  66. package/src/components/ui/calendar.test.tsx +88 -0
  67. package/src/components/ui/disclosure.test.tsx +39 -0
  68. package/src/components/ui/index.ts +44 -6
  69. package/src/components/ui/progress.test.tsx +15 -0
  70. package/src/components/ui/typography.test.tsx +26 -1
  71. package/src/styles/suite-skin.css +8 -2
  72. package/src/styles/themes.css +184 -68
  73. package/src/suite/components/ai-message-blocknote.tsx +13 -2
  74. package/src/suite/components/ai-panel.tsx +41 -10
  75. package/src/suite/components/app-switcher.test.tsx +21 -6
  76. package/src/suite/components/app-switcher.tsx +7 -11
  77. package/src/suite/components/suite-app-layout.tsx +12 -3
  78. package/src/suite/components/suite-bottom-nav.tsx +0 -1
  79. package/src/suite/components/suite-clients-projects-directory.test.tsx +71 -0
  80. package/src/suite/components/suite-clients-projects-directory.tsx +943 -0
  81. package/src/suite/components/suite-integration-picker.test.tsx +224 -0
  82. package/src/suite/components/suite-integration-picker.tsx +889 -18
  83. package/src/suite/components/suite-integration-rules.test.tsx +157 -0
  84. package/src/suite/components/suite-integration-rules.tsx +471 -0
  85. package/src/suite/components/suite-mobile-drawer.test.tsx +18 -2
  86. package/src/suite/components/suite-mobile-drawer.tsx +65 -98
  87. package/src/suite/components/suite-notification-bell.tsx +29 -4
  88. package/src/suite/components/suite-sidebar.tsx +25 -25
  89. package/src/suite/components/suite-skeleton.tsx +2 -2
  90. package/src/suite/components/suite-user-menu.tsx +9 -8
  91. package/src/suite/components/theme-provider.test.tsx +87 -3
  92. package/src/suite/components/theme-provider.tsx +144 -23
  93. package/src/suite/components/today-ui.tsx +79 -68
  94. package/src/suite/components/workspace-switcher.test.tsx +56 -0
  95. package/src/suite/components/workspace-switcher.tsx +3 -3
  96. package/src/suite/icons/app-accents.ts +12 -12
  97. package/src/suite/icons/glyphs.ts +6 -12
  98. package/src/suite/index.ts +22 -0
  99. package/src/suite/lib/apps.test.ts +9 -1
  100. package/src/suite/lib/apps.ts +14 -8
  101. package/src/suite/lib/cookies.ts +53 -0
  102. package/src/suite/lib/theme-boot.ts +32 -0
  103. package/src/suite/lib/theme-cookie.ts +7 -0
  104. package/src/suite/lib/use-sidebar-width.ts +5 -51
  105. package/dist/chunk-NOI42JNZ.mjs +0 -151
  106. package/src/suite/lib/use-lock-mobile-page-zoom.test.tsx +0 -65
  107. package/src/suite/lib/use-lock-mobile-page-zoom.ts +0 -51
package/dist/suite.mjs CHANGED
@@ -1,15 +1,31 @@
1
1
  import {
2
+ Alert,
3
+ Badge,
2
4
  Button,
5
+ Card,
3
6
  DropdownItem,
4
7
  DropdownMenu,
8
+ FileUpload,
5
9
  Input,
6
10
  Modal,
7
- Tooltip
8
- } from "./chunk-YB4TBWHV.mjs";
11
+ Select,
12
+ Toggle,
13
+ Tooltip,
14
+ cn
15
+ } from "./chunk-2EO5VCXP.mjs";
16
+ import {
17
+ suiteThemeBootScript
18
+ } from "./chunk-BS7BZI5W.mjs";
9
19
  import {
10
20
  SuiteThemeProvider,
11
- useSuiteTheme
12
- } from "./chunk-NOI42JNZ.mjs";
21
+ clearCookie,
22
+ readCookie,
23
+ useSuiteTheme,
24
+ writeCookie
25
+ } from "./chunk-APKRZQRO.mjs";
26
+ import {
27
+ SUITE_THEME_COOKIE
28
+ } from "./chunk-IULORI3A.mjs";
13
29
  import {
14
30
  __spreadProps,
15
31
  __spreadValues
@@ -59,7 +75,7 @@ import { Refresh as RefreshCw } from "iconoir-react";
59
75
  // src/suite/lib/cn.ts
60
76
  import { clsx } from "clsx";
61
77
  import { twMerge } from "tailwind-merge";
62
- function cn(...inputs) {
78
+ function cn2(...inputs) {
63
79
  return twMerge(clsx(inputs));
64
80
  }
65
81
 
@@ -88,10 +104,10 @@ var APP_ACCENTS = {
88
104
  },
89
105
  shellstack: {
90
106
  label: "ShellStack",
91
- accent: "#5e5893",
92
- tile: "#5e5893",
93
- onTile: "#ffffff",
94
- onTileAccent: "#c4a7e7"
107
+ accent: "#0078D4",
108
+ tile: "#FFFFFF",
109
+ onTile: "#111114",
110
+ onTileAccent: "#0078D4"
95
111
  },
96
112
  crew: {
97
113
  label: "Crew",
@@ -100,12 +116,12 @@ var APP_ACCENTS = {
100
116
  onTile: "#ffffff",
101
117
  onTileAccent: "#a5f3fc"
102
118
  },
103
- nakama: {
104
- label: "Nakama",
105
- accent: "#b8eb44",
106
- tile: "#17101f",
107
- onTile: "#fff2e2",
108
- onTileAccent: "#f54b87"
119
+ shelly: {
120
+ label: "Shelly",
121
+ accent: "#0078D4",
122
+ tile: "#1B1B1F",
123
+ onTile: "#ECECF1",
124
+ onTileAccent: "#00BCF2"
109
125
  }
110
126
  };
111
127
  var APP_GLYPHS = {
@@ -114,7 +130,7 @@ var APP_GLYPHS = {
114
130
  kraken: "squid-doc",
115
131
  shellstack: "stack-hex",
116
132
  crew: "crew-bot",
117
- nakama: "nakama-fist"
133
+ shelly: "shelly-snail"
118
134
  };
119
135
 
120
136
  // src/suite/icons/glyph-parts.tsx
@@ -237,22 +253,15 @@ var SUITE_GLYPHS = {
237
253
  { kind: "path", d: "M9.6 14.6q2.4 1.7 4.8 0", accent: true }
238
254
  ]
239
255
  },
240
- "nakama-fist": {
241
- description: "Nakama \u2014 raised fist with neon crew signal lines",
256
+ "shelly-snail": {
257
+ description: "Shelly \u2014 smiling snail with spiral shell and ball-tipped antennae",
242
258
  elements: [
243
259
  {
244
260
  kind: "path",
245
- d: "M192 0c17.7 0 32 14.3 32 32v112h-64V32c0-17.7 14.3-32 32-32zM64 64c0-17.7-32-32 32-32s32 14.3 32 32v80H64V64zm192 0c0-17.7 14.3-32 32-32s32 14.3 32 32v96c0 17.7-14.3 32-32 32s-32-14.3-32-32V64zm96 64c0-17.7 14.3-32 32-32s32 14.3 32 32v64c0 17.7-14.3 32-32 32s-32-14.3-32-32v-64zm-96 88v-.6c9.4 5.4 20.3 8.6 32 8.6 13.2 0 25.4-4 35.6-10.8 8.7 24.9 32.5 42.8 60.4 42.8 11.7 0 22.6-3.1 32-8.6v8.6c0 52.3-25.1 98.8-64 128v96c0 17.7-14.3 32-32 32H160c-17.7 0-32-14.3-32-32v-78.4c-17.3-7.9-33.2-18.8-46.9-32.5L69.5 357.5C45.5 333.5 32 300.9 32 267v-27c0-35.3 28.7-64 64-64h88c22.1 0 40 17.9 40 40s-17.9 40-40 40h-56c-8.8 0-16 7.2-16 16s7.2 16 16 16h56c39.8 0 72-32.2 72-72z",
261
+ d: "M2931 37263 c-1542 -737 -1730 -2870 -339 -3838 427 -297 339 1 1730 -5802 102 -425 101 -430 -391 -950 -1003 -1062 -1175 -1507 -1834 -4748 -1525 -7505 -1398 -10142 668 -13875 2061 -3724 4835 -5715 9185 -6592 2869 -579 4928 -582 8750 -15 1293 192 3408 506 4700 697 6839 1012 8768 1422 9385 1993 1103 1021 425 2357 -1733 3414 l-944 462 916 620 c5469 3705 5358 13657 -209 18536 -2592 2272 -5133 3185 -8865 3185 -3361 0 -5965 -948 -8226 -2994 -501 -453 -824 -546 -824 -235 0 70 301 1416 668 2991 668 2863 668 2863 1053 3101 1156 713 1317 2506 305 3395 -1583 1390 -3787 234 -3592 -1885 61 -673 61 -673 -1165 -3013 l-1227 -2340 -1303 -10 -1303 -10 -1267 2463 c-1202 2337 -1261 2480 -1155 2784 610 1750 -1309 3465 -2983 2666z m1743 -1216 c309 -354 379 -713 232 -1191 -205 -669 -183 -731 1300 -3583 1821 -3503 1488 -3127 2631 -2970 816 112 1053 109 1729 -25 587 -117 813 -127 901 -42 64 63 778 1394 1586 2958 1470 2844 1470 2844 1345 3365 -208 868 106 1458 852 1598 1380 258 1829 -1605 545 -2260 -501 -255 -427 -36 -1292 -3813 l-725 -3166 513 -617 c1210 -1453 1328 -2014 1709 -8068 224 -3562 368 -4603 790 -5688 820 -2113 2775 -3739 5643 -4695 2847 -948 5589 -1186 7567 -654 l850 228 1070 -471 c2352 -1034 2946 -1824 1719 -2288 -1707 -647 -14950 -2665 -17489 -2665 -5500 0 -9506 1872 -11922 5573 -2564 3926 -2753 6473 -1075 14490 542 2586 754 3104 1682 4102 778 837 757 677 322 2415 -188 754 -529 2180 -757 3170 -228 990 -436 1878 -463 1973 -27 95 -246 260 -486 366 -685 303 -961 1115 -610 1795 335 647 1332 736 1833 163z m21523 -6801 c6385 -1036 10732 -7090 9753 -13583 -628 -4167 -2948 -6812 -6688 -7626 -6108 -1329 -10963 4878 -8256 10556 1608 3373 6202 4320 8566 1766 2055 -2219 1699 -6037 -662 -7096 -2082 -935 -4058 98 -4184 2187 -93 1553 1143 2685 2287 2093 607 -313 709 -1340 148 -1481 -564 -142 -462 -962 120 -962 1033 2 1610 1420 1015 2496 -814 1471 -2971 1452 -3972 -37 -1601 -2380 159 -5650 3046 -5658 2778 -8 4680 2269 4503 5390 -265 4668 -5206 7028 -9332 4457 -3911 -2438 -4439 -8515 -1057 -12167 l561 -607 -647 327 c-2556 1290 -3718 2796 -4088 5301 -61 411 -201 2166 -311 3898 -312 4898 -436 5802 -952 6962 l-261 588 532 493 c1419 1314 3729 2427 5782 2786 639 112 3218 59 4097 -83z M5574 23846 c-476 -475 -431 -1229 97 -1644 835 -658 1687 739 970 1590 -326 388 -715 407 -1067 54z M12075 23992 c-669 -390 -584 -1598 134 -1896 608 -252 1135 289 1057 1085 -72 728 -654 1124 -1191 811z M7801 22451 c-626 -754 1585 -1648 2460 -995 485 362 676 688 546 932 -155 288 -335 267 -719 -84 -549 -502 -1237 -469 -1728 82 -224 251 -389 271 -559 65z",
246
262
  filled: true,
247
- transform: "translate(2.5 1.75) scale(.0425 .0375)"
248
- },
249
- { kind: "path", d: "M7 18h3.5M12.2 18h3M8.5 19.5h6", accent: true },
250
- {
251
- kind: "path",
252
- d: "M4.8 5.8h2.7M4.8 7.4h1.4M18.5 17.1h1.7",
253
- accent: true
254
- },
255
- { kind: "circle", cx: 18.5, cy: 4.5, r: 0.8, accent: true, filled: true }
263
+ transform: "translate(0.88 22.8) scale(0.0005714 -0.0005714)"
264
+ }
256
265
  ]
257
266
  },
258
267
  workspace: {
@@ -366,7 +375,7 @@ function SuiteAppIcon({
366
375
  width: px,
367
376
  height: px,
368
377
  viewBox: "0 0 24 24",
369
- className: cn("shrink-0", className),
378
+ className: cn2("shrink-0", className),
370
379
  style,
371
380
  role: label ? "img" : void 0,
372
381
  "aria-label": label,
@@ -394,104 +403,55 @@ function SuiteAppIcon({
394
403
  );
395
404
  }
396
405
 
397
- // src/suite/icons/suite-icon.tsx
398
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
399
- var TODAY_SUN_STYLE = `
400
- @media (prefers-reduced-motion: no-preference) {
401
- .suite-icon-today-sun-rays {
402
- animation: suite-icon-today-sun-spin 18s linear infinite;
403
- transform-box: fill-box;
404
- transform-origin: center;
405
- }
406
- }
407
- @keyframes suite-icon-today-sun-spin {
408
- to { transform: rotate(360deg); }
409
- }
410
- `;
411
- function SuiteIcon({
412
- name,
413
- size = 24,
414
- accent,
415
- strokeWidth = 2,
416
- animated = true,
417
- label,
418
- className,
419
- style
420
- }) {
421
- const glyph = SUITE_GLYPHS[name];
422
- const accentHex = accent ? APP_ACCENTS[accent].accent : void 0;
423
- const mergedStyle = accentHex ? __spreadProps(__spreadValues({}, style), { "--suite-icon-accent": accentHex }) : style;
424
- return /* @__PURE__ */ jsxs4(
425
- "svg",
426
- {
427
- width: size,
428
- height: size,
429
- viewBox: "0 0 24 24",
430
- fill: "none",
431
- stroke: "currentColor",
432
- strokeWidth,
433
- strokeLinecap: "round",
434
- strokeLinejoin: "round",
435
- className: cn("shrink-0", className),
436
- style: mergedStyle,
437
- role: label ? "img" : void 0,
438
- "aria-label": label,
439
- "aria-hidden": label ? void 0 : true,
440
- children: [
441
- glyph.animatedAccent && animated ? /* @__PURE__ */ jsx4("style", { children: TODAY_SUN_STYLE }) : null,
442
- /* @__PURE__ */ jsx4(
443
- GlyphParts,
444
- {
445
- elements: glyph.elements,
446
- accentClassName: glyph.animatedAccent && animated ? "suite-icon-today-sun-rays" : void 0
447
- }
448
- )
449
- ]
450
- }
451
- );
452
- }
453
-
454
406
  // src/suite/components/today-ui.tsx
455
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
407
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
456
408
  var APP_META = {
457
409
  shellstack: { name: "ShellStack" },
458
410
  tides: { name: "Tides" },
459
411
  turtletime: { name: "TurtleTime" },
460
- kraken: { name: "Kraken" }
412
+ kraken: { name: "Kraken" },
413
+ shelly: { name: "Shelly" }
461
414
  };
462
415
  var SUITE_APPS = [
463
416
  "shellstack",
464
417
  "tides",
465
418
  "turtletime",
466
- "kraken"
419
+ "kraken",
420
+ "shelly"
467
421
  ];
468
422
  function sourceToSuiteApp(source) {
469
- if (source === "tides" || source === "turtletime" || source === "kraken") {
470
- return source;
423
+ const normalised = source.trim().toLowerCase();
424
+ if (normalised === "tides" || normalised === "turtletime" || normalised === "kraken" || normalised === "shelly") {
425
+ return normalised;
471
426
  }
472
427
  return "shellstack";
473
428
  }
474
429
  function suiteAppLabel(source) {
475
430
  return APP_META[sourceToSuiteApp(source)].name;
476
431
  }
477
- var SOURCE_APP_GLYPH = {
478
- shellstack: "stack-hex",
479
- tides: "kanban-wave",
480
- turtletime: "timer-shell",
481
- kraken: "squid-doc"
432
+ var SOURCE_APP_LOGOS = {
433
+ shellstack: "/icon-shellstack.svg",
434
+ tides: "/tides-icon.svg",
435
+ turtletime: "/turtletime-icon.svg",
436
+ kraken: "/kraken-icon.svg",
437
+ shelly: "/shelly-icon.svg"
482
438
  };
483
439
  function SourceAppGlyph({
484
440
  source,
485
441
  className
486
442
  }) {
487
443
  const app = sourceToSuiteApp(source);
488
- return /* @__PURE__ */ jsx5(
489
- SuiteIcon,
490
- {
491
- name: SOURCE_APP_GLYPH[app],
492
- accent: app,
493
- className: cn("h-4 w-4 shrink-0 text-ph-subtle", className)
494
- }
444
+ return (
445
+ // These assets are copied into each product's public directory.
446
+ // eslint-disable-next-line @next/next/no-img-element
447
+ /* @__PURE__ */ jsx4(
448
+ "img",
449
+ {
450
+ src: SOURCE_APP_LOGOS[app],
451
+ alt: "",
452
+ className: cn2("h-7 w-7 shrink-0 rounded-lg object-contain", className)
453
+ }
454
+ )
495
455
  );
496
456
  }
497
457
  function formatLongDate(date = /* @__PURE__ */ new Date()) {
@@ -504,23 +464,23 @@ function SuiteAppStrip({
504
464
  className,
505
465
  icons
506
466
  }) {
507
- return /* @__PURE__ */ jsx5(
467
+ return /* @__PURE__ */ jsx4(
508
468
  "div",
509
469
  {
510
- className: cn(
470
+ className: cn2(
511
471
  "flex flex-wrap items-center justify-center gap-2.5",
512
472
  className
513
473
  ),
514
474
  "aria-label": "ShellStack suite apps",
515
475
  children: SUITE_APPS.map((app) => {
516
476
  var _a;
517
- return /* @__PURE__ */ jsxs5(
477
+ return /* @__PURE__ */ jsxs4(
518
478
  "div",
519
479
  {
520
480
  className: "flex items-center gap-2 rounded-full border border-ph-border bg-ph-surface px-2.5 py-1.5 shadow-sm",
521
481
  children: [
522
- (_a = icons == null ? void 0 : icons[app]) != null ? _a : /* @__PURE__ */ jsx5(SuiteAppIcon, { app, size: "sm" }),
523
- /* @__PURE__ */ jsx5("span", { className: "text-xs font-medium text-ph-subtle", children: APP_META[app].name })
482
+ (_a = icons == null ? void 0 : icons[app]) != null ? _a : /* @__PURE__ */ jsx4(SuiteAppIcon, { app, size: "sm" }),
483
+ /* @__PURE__ */ jsx4("span", { className: "text-xs font-medium text-ph-subtle", children: APP_META[app].name })
524
484
  ]
525
485
  },
526
486
  app
@@ -530,7 +490,7 @@ function SuiteAppStrip({
530
490
  );
531
491
  }
532
492
  function AnimatedSun({ className }) {
533
- return /* @__PURE__ */ jsxs5(
493
+ return /* @__PURE__ */ jsxs4(
534
494
  "svg",
535
495
  {
536
496
  width: "40",
@@ -538,14 +498,14 @@ function AnimatedSun({ className }) {
538
498
  viewBox: "0 0 40 40",
539
499
  fill: "none",
540
500
  xmlns: "http://www.w3.org/2000/svg",
541
- className: cn(className),
501
+ className: cn2(className),
542
502
  "aria-hidden": "true",
543
503
  children: [
544
- /* @__PURE__ */ jsx5("defs", { children: /* @__PURE__ */ jsxs5("linearGradient", { id: "animatedSunCenter", x1: "0", y1: "0", x2: "40", y2: "40", children: [
545
- /* @__PURE__ */ jsx5("stop", { stopColor: "#f59e0b" }),
546
- /* @__PURE__ */ jsx5("stop", { offset: "1", stopColor: "#fb923c" })
504
+ /* @__PURE__ */ jsx4("defs", { children: /* @__PURE__ */ jsxs4("linearGradient", { id: "animatedSunCenter", x1: "0", y1: "0", x2: "40", y2: "40", children: [
505
+ /* @__PURE__ */ jsx4("stop", { stopColor: "#f59e0b" }),
506
+ /* @__PURE__ */ jsx4("stop", { offset: "1", stopColor: "#fb923c" })
547
507
  ] }) }),
548
- /* @__PURE__ */ jsx5("g", { className: "origin-center opacity-80 motion-safe:animate-[pulse_8s_ease-in-out_infinite] motion-reduce:animate-none", children: [0, 45, 90, 135, 180, 225, 270, 315].map((angle) => /* @__PURE__ */ jsx5(
508
+ /* @__PURE__ */ jsx4("g", { className: "origin-center opacity-80 motion-safe:animate-[pulse_8s_ease-in-out_infinite] motion-reduce:animate-none", children: [0, 45, 90, 135, 180, 225, 270, 315].map((angle) => /* @__PURE__ */ jsx4(
549
509
  "line",
550
510
  {
551
511
  x1: "20",
@@ -559,13 +519,13 @@ function AnimatedSun({ className }) {
559
519
  },
560
520
  angle
561
521
  )) }),
562
- /* @__PURE__ */ jsx5("circle", { cx: "20", cy: "20", r: "9", fill: "url(#animatedSunCenter)" })
522
+ /* @__PURE__ */ jsx4("circle", { cx: "20", cy: "20", r: "9", fill: "url(#animatedSunCenter)" })
563
523
  ]
564
524
  }
565
525
  );
566
526
  }
567
527
  function AnimatedMoon({ className }) {
568
- return /* @__PURE__ */ jsxs5(
528
+ return /* @__PURE__ */ jsxs4(
569
529
  "svg",
570
530
  {
571
531
  width: "40",
@@ -573,23 +533,23 @@ function AnimatedMoon({ className }) {
573
533
  viewBox: "0 0 40 40",
574
534
  fill: "none",
575
535
  xmlns: "http://www.w3.org/2000/svg",
576
- className: cn(className),
536
+ className: cn2(className),
577
537
  "aria-hidden": "true",
578
538
  children: [
579
- /* @__PURE__ */ jsx5("defs", { children: /* @__PURE__ */ jsxs5("linearGradient", { id: "animatedMoonBody", x1: "0", y1: "0", x2: "40", y2: "40", children: [
580
- /* @__PURE__ */ jsx5("stop", { stopColor: "#a5b4fc" }),
581
- /* @__PURE__ */ jsx5("stop", { offset: "1", stopColor: "#818cf8" })
539
+ /* @__PURE__ */ jsx4("defs", { children: /* @__PURE__ */ jsxs4("linearGradient", { id: "animatedMoonBody", x1: "0", y1: "0", x2: "40", y2: "40", children: [
540
+ /* @__PURE__ */ jsx4("stop", { stopColor: "#a5b4fc" }),
541
+ /* @__PURE__ */ jsx4("stop", { offset: "1", stopColor: "#818cf8" })
582
542
  ] }) }),
583
- /* @__PURE__ */ jsx5(
543
+ /* @__PURE__ */ jsx4(
584
544
  "path",
585
545
  {
586
546
  d: "M27.5 25.5A11 11 0 1 1 14.5 9.6a9 9 0 1 0 13 15.9Z",
587
547
  fill: "url(#animatedMoonBody)"
588
548
  }
589
549
  ),
590
- /* @__PURE__ */ jsxs5("g", { className: "opacity-70 motion-safe:animate-[pulse_10s_ease-in-out_infinite] motion-reduce:animate-none", children: [
591
- /* @__PURE__ */ jsx5("circle", { cx: "29", cy: "10", r: "1.2", fill: "#c7d2fe" }),
592
- /* @__PURE__ */ jsx5("circle", { cx: "34", cy: "16", r: "0.9", fill: "#c7d2fe" })
550
+ /* @__PURE__ */ jsxs4("g", { className: "opacity-70 motion-safe:animate-[pulse_10s_ease-in-out_infinite] motion-reduce:animate-none", children: [
551
+ /* @__PURE__ */ jsx4("circle", { cx: "29", cy: "10", r: "1.2", fill: "#c7d2fe" }),
552
+ /* @__PURE__ */ jsx4("circle", { cx: "34", cy: "16", r: "0.9", fill: "#c7d2fe" })
593
553
  ] })
594
554
  ]
595
555
  }
@@ -598,7 +558,7 @@ function AnimatedMoon({ className }) {
598
558
  function TodayTimeIcon({ className }) {
599
559
  const hour = (/* @__PURE__ */ new Date()).getHours();
600
560
  const isNight = hour >= 18 || hour < 6;
601
- return isNight ? /* @__PURE__ */ jsx5(AnimatedMoon, { className }) : /* @__PURE__ */ jsx5(AnimatedSun, { className });
561
+ return isNight ? /* @__PURE__ */ jsx4(AnimatedMoon, { className }) : /* @__PURE__ */ jsx4(AnimatedSun, { className });
602
562
  }
603
563
  function TodayTopBar({
604
564
  refreshing,
@@ -607,17 +567,17 @@ function TodayTopBar({
607
567
  appIcon,
608
568
  appName
609
569
  }) {
610
- const icon = appIcon != null ? appIcon : /* @__PURE__ */ jsx5(TodayTimeIcon, { className: "h-6 w-6 sm:h-7 sm:w-7 text-amber-500" });
611
- return /* @__PURE__ */ jsxs5("header", { className: "flex items-center justify-between gap-4", children: [
612
- /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 items-center gap-3", children: [
613
- /* @__PURE__ */ jsx5("div", { className: "hidden shrink-0 sm:block", "aria-hidden": "true", children: icon }),
614
- /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
615
- /* @__PURE__ */ jsx5("h1", { className: "font-display text-[1.625rem] font-bold leading-tight tracking-tight text-ph-ink sm:text-[2.25rem]", children: "Today" }),
616
- appName ? /* @__PURE__ */ jsx5("span", { className: "rounded-md bg-ph-muted px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-ph-subtle", children: appName }) : null,
617
- /* @__PURE__ */ jsx5("span", { className: "text-sm text-ph-subtle", children: formatLongDate() })
570
+ const icon = appIcon != null ? appIcon : /* @__PURE__ */ jsx4(TodayTimeIcon, { className: "h-6 w-6 sm:h-7 sm:w-7 text-amber-500" });
571
+ return /* @__PURE__ */ jsxs4("header", { className: "flex items-center justify-between gap-4", children: [
572
+ /* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 items-center gap-3", children: [
573
+ /* @__PURE__ */ jsx4("div", { className: "hidden shrink-0 sm:block", "aria-hidden": "true", children: icon }),
574
+ /* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
575
+ /* @__PURE__ */ jsx4("h1", { className: "font-display text-[1.625rem] font-bold leading-tight tracking-tight text-ph-ink sm:text-[2.25rem]", children: "Today" }),
576
+ appName ? /* @__PURE__ */ jsx4("span", { className: "rounded-md bg-ph-muted px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-ph-subtle", children: appName }) : null,
577
+ /* @__PURE__ */ jsx4("span", { className: "text-sm text-ph-subtle", children: formatLongDate() })
618
578
  ] })
619
579
  ] }),
620
- /* @__PURE__ */ jsx5("div", { className: "flex shrink-0 items-center", children: /* @__PURE__ */ jsx5(
580
+ /* @__PURE__ */ jsx4("div", { className: "flex shrink-0 items-center", children: /* @__PURE__ */ jsx4(
621
581
  "button",
622
582
  {
623
583
  type: "button",
@@ -626,10 +586,10 @@ function TodayTopBar({
626
586
  className: "press-subtle inline-flex h-10 w-10 items-center justify-center rounded-lg text-ph-subtle transition hover:bg-ph-surface hover:text-ph-ink disabled:opacity-50",
627
587
  "data-test": "today-refresh",
628
588
  "aria-label": "Refresh today",
629
- children: /* @__PURE__ */ jsx5(
589
+ children: /* @__PURE__ */ jsx4(
630
590
  RefreshCw,
631
591
  {
632
- className: cn("h-4 w-4", refreshing && "animate-spin"),
592
+ className: cn2("h-4 w-4", refreshing && "animate-spin"),
633
593
  "aria-hidden": true
634
594
  }
635
595
  )
@@ -669,8 +629,8 @@ function TodayHero({
669
629
  brief,
670
630
  briefMeta
671
631
  }) {
672
- return /* @__PURE__ */ jsxs5("div", { className: "mt-4 sm:mt-6", children: [
673
- /* @__PURE__ */ jsx5(
632
+ return /* @__PURE__ */ jsxs4("div", { className: "mt-4 sm:mt-6", children: [
633
+ /* @__PURE__ */ jsx4(
674
634
  "p",
675
635
  {
676
636
  className: "today-hero max-w-2xl font-display text-lg font-semibold tracking-tight text-ph-ink sm:text-xl sm:leading-snug",
@@ -678,9 +638,9 @@ function TodayHero({
678
638
  },
679
639
  typeof message === "string" ? message : "hero"
680
640
  ),
681
- brief ? /* @__PURE__ */ jsxs5("div", { className: "mt-4 max-w-2xl", "data-test": "today-focus-brief", children: [
682
- /* @__PURE__ */ jsx5("p", { className: "text-[15px] leading-[1.65] text-ph-ink", children: brief }),
683
- briefMeta ? /* @__PURE__ */ jsx5("p", { className: "mt-1.5 text-xs text-ph-mutedtext", children: briefMeta }) : null
641
+ brief ? /* @__PURE__ */ jsxs4("div", { className: "mt-4 max-w-2xl", "data-test": "today-focus-brief", children: [
642
+ /* @__PURE__ */ jsx4("p", { className: "text-[15px] leading-[1.65] text-ph-ink", children: brief }),
643
+ briefMeta ? /* @__PURE__ */ jsx4("p", { className: "mt-1.5 text-xs text-ph-mutedtext", children: briefMeta }) : null
684
644
  ] }) : null
685
645
  ] });
686
646
  }
@@ -696,20 +656,20 @@ function TodayPrimaryAction({
696
656
  className,
697
657
  dataTest = "today-primary-action"
698
658
  }) {
699
- const classes = cn(
659
+ const classes = cn2(
700
660
  "today-primary-action group relative flex w-full max-w-md items-center gap-3 overflow-hidden rounded-xl px-3.5 py-2.5 text-left transition sm:px-4 sm:py-3",
701
661
  `today-primary-action--${accent}`,
702
662
  active && "today-primary-action--active",
703
663
  loading && "pointer-events-none opacity-70",
704
664
  className
705
665
  );
706
- const body = /* @__PURE__ */ jsxs5(Fragment2, { children: [
707
- /* @__PURE__ */ jsx5("span", { className: "today-primary-action__icon inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-lg sm:h-10 sm:w-10", children: icon }),
708
- /* @__PURE__ */ jsxs5("span", { className: "min-w-0 flex-1", children: [
709
- /* @__PURE__ */ jsx5("span", { className: "block text-sm font-semibold tracking-tight sm:text-base", children: label }),
710
- description ? /* @__PURE__ */ jsx5("span", { className: "mt-0.5 block text-xs opacity-80 sm:text-sm", children: description }) : null
666
+ const body = /* @__PURE__ */ jsxs4(Fragment2, { children: [
667
+ /* @__PURE__ */ jsx4("span", { className: "today-primary-action__icon inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-lg sm:h-10 sm:w-10", children: icon }),
668
+ /* @__PURE__ */ jsxs4("span", { className: "min-w-0 flex-1", children: [
669
+ /* @__PURE__ */ jsx4("span", { className: "block text-sm font-semibold tracking-tight sm:text-base", children: label }),
670
+ description ? /* @__PURE__ */ jsx4("span", { className: "mt-0.5 block text-xs opacity-80 sm:text-sm", children: description }) : null
711
671
  ] }),
712
- /* @__PURE__ */ jsx5(
672
+ /* @__PURE__ */ jsx4(
713
673
  "span",
714
674
  {
715
675
  className: "today-primary-action__chevron shrink-0 text-base opacity-60 transition group-hover:translate-x-0.5 group-hover:opacity-100",
@@ -719,7 +679,7 @@ function TodayPrimaryAction({
719
679
  )
720
680
  ] });
721
681
  if (href && !onClick) {
722
- return /* @__PURE__ */ jsx5(
682
+ return /* @__PURE__ */ jsx4(
723
683
  "a",
724
684
  {
725
685
  href,
@@ -730,7 +690,7 @@ function TodayPrimaryAction({
730
690
  }
731
691
  );
732
692
  }
733
- return /* @__PURE__ */ jsx5(
693
+ return /* @__PURE__ */ jsx4(
734
694
  "button",
735
695
  {
736
696
  type: "button",
@@ -746,33 +706,33 @@ function TodayPrimaryAction({
746
706
  function TodayEmptyAction({
747
707
  title,
748
708
  description,
749
- actionLabel,
709
+ actionLabel: actionLabel2,
750
710
  href,
751
711
  onClick,
752
712
  dataTest
753
713
  }) {
754
- return /* @__PURE__ */ jsxs5(
714
+ return /* @__PURE__ */ jsxs4(
755
715
  "div",
756
716
  {
757
717
  className: "rounded-lg border border-dashed border-ph-border/80 bg-ph-muted/30 px-4 py-4 text-center",
758
718
  "data-test": dataTest,
759
719
  children: [
760
- /* @__PURE__ */ jsx5("p", { className: "text-sm font-semibold text-ph-ink", children: title }),
761
- description ? /* @__PURE__ */ jsx5("p", { className: "mx-auto mt-1 max-w-sm text-xs leading-relaxed text-ph-subtle sm:text-sm", children: description }) : null,
762
- actionLabel && (href || onClick) ? href ? /* @__PURE__ */ jsx5(
720
+ /* @__PURE__ */ jsx4("p", { className: "text-sm font-semibold text-ph-ink", children: title }),
721
+ description ? /* @__PURE__ */ jsx4("p", { className: "mx-auto mt-1 max-w-sm text-xs leading-relaxed text-ph-subtle sm:text-sm", children: description }) : null,
722
+ actionLabel2 && (href || onClick) ? href ? /* @__PURE__ */ jsx4(
763
723
  "a",
764
724
  {
765
725
  href,
766
726
  className: "mt-2.5 inline-flex text-sm font-semibold text-ph-brand hover:underline",
767
- children: actionLabel
727
+ children: actionLabel2
768
728
  }
769
- ) : /* @__PURE__ */ jsx5(
729
+ ) : /* @__PURE__ */ jsx4(
770
730
  "button",
771
731
  {
772
732
  type: "button",
773
733
  onClick,
774
734
  className: "mt-2.5 inline-flex text-sm font-semibold text-ph-brand hover:underline",
775
- children: actionLabel
735
+ children: actionLabel2
776
736
  }
777
737
  ) : null
778
738
  ]
@@ -785,16 +745,16 @@ function TodayLayout({
785
745
  aside,
786
746
  className
787
747
  }) {
788
- return /* @__PURE__ */ jsxs5(
748
+ return /* @__PURE__ */ jsxs4(
789
749
  "div",
790
750
  {
791
- className: cn("mt-5 space-y-6 sm:mt-6", className),
751
+ className: cn2("mt-5 space-y-6 sm:mt-6", className),
792
752
  "data-test": "today-layout",
793
753
  children: [
794
- primary ? /* @__PURE__ */ jsx5("div", { className: "max-w-md", children: primary }) : null,
795
- /* @__PURE__ */ jsxs5("div", { className: "grid gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(16rem,20rem)] lg:items-start lg:gap-8", children: [
796
- /* @__PURE__ */ jsx5("div", { className: "min-w-0 space-y-8", children: main }),
797
- aside ? /* @__PURE__ */ jsx5("aside", { className: "space-y-3 lg:sticky lg:top-6 lg:self-start", children: aside }) : null
754
+ primary ? /* @__PURE__ */ jsx4("div", { className: "max-w-md", children: primary }) : null,
755
+ /* @__PURE__ */ jsxs4("div", { className: "grid gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(16rem,20rem)] lg:items-start lg:gap-8", children: [
756
+ /* @__PURE__ */ jsx4("div", { className: "min-w-0 space-y-8", children: main }),
757
+ aside ? /* @__PURE__ */ jsx4("aside", { className: "space-y-3 lg:sticky lg:top-6 lg:self-start", children: aside }) : null
798
758
  ] })
799
759
  ]
800
760
  }
@@ -808,22 +768,30 @@ function TodayAskAiCard({
808
768
  onClick,
809
769
  href
810
770
  }) {
811
- const classes = cn(
771
+ const classes = cn2(
812
772
  "group flex w-full items-start gap-3.5 rounded-2xl border border-ph-border/70 bg-ph-surface px-4 py-3.5 text-left shadow-[0_1px_12px_-8px_rgba(15,23,42,0.1)] transition hover:border-ph-brand/35 hover:bg-[color-mix(in_oklab,var(--ph-accent)_6%,var(--ph-surface))] sm:px-5",
813
773
  className
814
774
  );
815
- const body = /* @__PURE__ */ jsxs5(Fragment2, { children: [
816
- /* @__PURE__ */ jsx5("span", { className: "inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-2xl bg-ph-brand/15 text-ph-brand shadow-sm ring-1 ring-black/[0.06]", children: /* @__PURE__ */ jsx5(SuiteIcon, { name: "stack-hex", accent: "shellstack", className: "h-5 w-5" }) }),
817
- /* @__PURE__ */ jsxs5("span", { className: "min-w-0 flex-1", children: [
818
- /* @__PURE__ */ jsxs5("span", { className: "flex flex-wrap items-baseline justify-between gap-x-3 gap-y-1", children: [
819
- /* @__PURE__ */ jsx5("span", { className: "font-display text-sm font-bold tracking-tight text-ph-ink", children: title }),
820
- /* @__PURE__ */ jsx5("span", { className: "text-sm font-medium text-ph-brand transition group-hover:underline", children: cta })
775
+ const body = /* @__PURE__ */ jsxs4(Fragment2, { children: [
776
+ /* @__PURE__ */ jsx4(
777
+ "img",
778
+ {
779
+ src: "/shelly-icon.svg",
780
+ alt: "",
781
+ "aria-hidden": true,
782
+ className: "h-5 w-5 shrink-0 object-contain"
783
+ }
784
+ ),
785
+ /* @__PURE__ */ jsxs4("span", { className: "min-w-0 flex-1", children: [
786
+ /* @__PURE__ */ jsxs4("span", { className: "flex flex-wrap items-baseline justify-between gap-x-3 gap-y-1", children: [
787
+ /* @__PURE__ */ jsx4("span", { className: "font-display text-sm font-bold tracking-tight text-ph-ink", children: title }),
788
+ /* @__PURE__ */ jsx4("span", { className: "text-sm font-medium text-ph-brand transition group-hover:underline", children: cta })
821
789
  ] }),
822
- /* @__PURE__ */ jsx5("span", { className: "mt-1 block text-sm leading-relaxed text-ph-subtle", children: description })
790
+ /* @__PURE__ */ jsx4("span", { className: "mt-1 block text-sm leading-relaxed text-ph-subtle", children: description })
823
791
  ] })
824
792
  ] });
825
793
  if (onClick) {
826
- return /* @__PURE__ */ jsx5(
794
+ return /* @__PURE__ */ jsx4(
827
795
  "button",
828
796
  {
829
797
  type: "button",
@@ -835,9 +803,9 @@ function TodayAskAiCard({
835
803
  );
836
804
  }
837
805
  if (href) {
838
- return /* @__PURE__ */ jsx5("a", { href, className: classes, "data-test": "today-ask-ai", children: body });
806
+ return /* @__PURE__ */ jsx4("a", { href, className: classes, "data-test": "today-ask-ai", children: body });
839
807
  }
840
- return /* @__PURE__ */ jsx5("div", { className: classes, "data-test": "today-ask-ai", children: body });
808
+ return /* @__PURE__ */ jsx4("div", { className: classes, "data-test": "today-ask-ai", children: body });
841
809
  }
842
810
  function TodaySection({
843
811
  icon: Icon,
@@ -848,22 +816,22 @@ function TodaySection({
848
816
  testId,
849
817
  children
850
818
  }) {
851
- return /* @__PURE__ */ jsxs5("section", { "data-test": testId, className: "space-y-5", children: [
852
- /* @__PURE__ */ jsxs5("div", { className: "flex items-start justify-between gap-4", children: [
853
- /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 items-start gap-3.5", children: [
854
- /* @__PURE__ */ jsx5(
819
+ return /* @__PURE__ */ jsxs4("section", { "data-test": testId, className: "space-y-5", children: [
820
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-start justify-between gap-4", children: [
821
+ /* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 items-start gap-3.5", children: [
822
+ /* @__PURE__ */ jsx4(
855
823
  "span",
856
824
  {
857
- className: cn(
825
+ className: cn2(
858
826
  "inline-flex h-10 w-10 sm:h-11 sm:w-11 shrink-0 items-center justify-center rounded-2xl shadow-sm ring-1 ring-black/[0.06]",
859
827
  iconClassName != null ? iconClassName : "bg-ph-muted text-ph-ink"
860
828
  ),
861
- children: /* @__PURE__ */ jsx5(Icon, { className: "h-5 w-5", "aria-hidden": true })
829
+ children: /* @__PURE__ */ jsx4(Icon, { className: "h-5 w-5", "aria-hidden": true })
862
830
  }
863
831
  ),
864
- /* @__PURE__ */ jsxs5("div", { className: "min-w-0 pt-0.5", children: [
865
- /* @__PURE__ */ jsx5("h2", { className: "font-display text-lg font-bold tracking-tight text-ph-ink", children: title }),
866
- description ? /* @__PURE__ */ jsx5("p", { className: "mt-1 max-w-xl text-sm leading-relaxed text-ph-subtle", children: description }) : null
832
+ /* @__PURE__ */ jsxs4("div", { className: "min-w-0 pt-0.5", children: [
833
+ /* @__PURE__ */ jsx4("h2", { className: "font-display text-lg font-bold tracking-tight text-ph-ink", children: title }),
834
+ description ? /* @__PURE__ */ jsx4("p", { className: "mt-1 max-w-xl text-sm leading-relaxed text-ph-subtle", children: description }) : null
867
835
  ] })
868
836
  ] }),
869
837
  action
@@ -875,10 +843,10 @@ function TodayPanel({
875
843
  children,
876
844
  className
877
845
  }) {
878
- return /* @__PURE__ */ jsx5(
846
+ return /* @__PURE__ */ jsx4(
879
847
  "div",
880
848
  {
881
- className: cn(
849
+ className: cn2(
882
850
  "border-ph-border/70 rounded-2xl border bg-ph-surface p-5 shadow-[0_1px_16px_-8px_rgba(15,23,42,0.12)] sm:p-5",
883
851
  className
884
852
  ),
@@ -890,41 +858,38 @@ function TodayEmptyState({
890
858
  title,
891
859
  description
892
860
  }) {
893
- return /* @__PURE__ */ jsxs5("div", { className: "border-ph-border/80 rounded-xl border border-dashed px-5 py-8 text-center", children: [
894
- /* @__PURE__ */ jsx5("p", { className: "text-sm font-semibold text-ph-ink", children: title }),
895
- description ? /* @__PURE__ */ jsx5("p", { className: "mt-1.5 text-sm leading-relaxed text-ph-subtle", children: description }) : null
861
+ return /* @__PURE__ */ jsxs4("div", { className: "border-ph-border/80 rounded-xl border border-dashed px-5 py-8 text-center", children: [
862
+ /* @__PURE__ */ jsx4("p", { className: "text-sm font-semibold text-ph-ink", children: title }),
863
+ description ? /* @__PURE__ */ jsx4("p", { className: "mt-1.5 text-sm leading-relaxed text-ph-subtle", children: description }) : null
896
864
  ] });
897
865
  }
898
866
 
899
867
  // src/suite/components/app-switcher.tsx
900
868
  import Image from "next/image";
901
869
  import { useState } from "react";
902
- import {
903
- NavArrowDown as ChevronDown,
904
- OpenNewWindow
905
- } from "iconoir-react";
906
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
870
+ import { NavArrowDown as ChevronDown, OpenNewWindow } from "iconoir-react";
871
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
907
872
  function appSwitcherTriggerClass(open, collapsed = false) {
908
- return cn(
873
+ return cn2(
909
874
  "group flex min-w-0 items-center gap-2 rounded-lg border px-2 py-1.5 text-left transition-all duration-200",
910
875
  collapsed ? "h-11 w-11 justify-center px-0 py-0" : "w-full",
911
876
  open ? "border-ph-brand/30 bg-ph-muted shadow-[0_1px_2px_rgba(0,0,0,0.06)] ring-1 ring-ph-brand/15" : "border-transparent hover:border-ph-border hover:bg-ph-muted/80 hover:shadow-sm"
912
877
  );
913
878
  }
914
879
  function appSwitcherMarkClass() {
915
- return "h-8 w-8 shrink-0 rounded-lg shadow-[0_1px_2px_rgba(0,0,0,0.1),0_4px_12px_rgba(0,0,0,0.08)] ring-1 ring-black/[0.06]";
880
+ return "h-8 w-8 shrink-0 rounded-lg";
916
881
  }
917
882
  function appSwitcherMenuItemClass() {
918
- return "rounded-lg px-2 py-2 data-[highlighted]:bg-transparent hover:bg-ph-muted/80";
883
+ return "rounded-lg px-2 py-2 data-[highlighted]:bg-transparent hover:bg-ph-muted/80 focus-visible:shadow-none";
919
884
  }
920
885
  function AppSwitcherMark({
921
886
  children,
922
887
  collapsed
923
888
  }) {
924
- return /* @__PURE__ */ jsx6(
889
+ return /* @__PURE__ */ jsx5(
925
890
  "span",
926
891
  {
927
- className: cn(
892
+ className: cn2(
928
893
  "relative shrink-0",
929
894
  collapsed && "group-hover:scale-[1.02]"
930
895
  ),
@@ -937,18 +902,18 @@ function AppSwitcherChevron({
937
902
  collapsed = false
938
903
  }) {
939
904
  if (collapsed) {
940
- return /* @__PURE__ */ jsx6(
905
+ return /* @__PURE__ */ jsx5(
941
906
  "span",
942
907
  {
943
- className: cn(
908
+ className: cn2(
944
909
  "absolute bottom-0 right-0 flex h-3 w-3 items-center justify-center rounded-full border border-ph-border bg-ph-surface shadow-sm transition-colors",
945
910
  open && "border-ph-brand/40 bg-ph-brand/10"
946
911
  ),
947
912
  "aria-hidden": true,
948
- children: /* @__PURE__ */ jsx6(
913
+ children: /* @__PURE__ */ jsx5(
949
914
  ChevronDown,
950
915
  {
951
- className: cn(
916
+ className: cn2(
952
917
  "h-2 w-2 text-ph-mutedtext transition-transform duration-200",
953
918
  open && "rotate-180 text-ph-brand"
954
919
  )
@@ -957,18 +922,18 @@ function AppSwitcherChevron({
957
922
  }
958
923
  );
959
924
  }
960
- return /* @__PURE__ */ jsx6(
925
+ return /* @__PURE__ */ jsx5(
961
926
  "span",
962
927
  {
963
- className: cn(
928
+ className: cn2(
964
929
  "border-ph-border/80 flex h-5 w-5 shrink-0 items-center justify-center rounded-lg border bg-ph-canvas shadow-sm transition-all duration-200",
965
930
  open ? "border-ph-brand/35 bg-ph-brand/10 text-ph-brand" : "text-ph-mutedtext group-hover:border-ph-border group-hover:bg-ph-surface"
966
931
  ),
967
932
  "aria-hidden": true,
968
- children: /* @__PURE__ */ jsx6(
933
+ children: /* @__PURE__ */ jsx5(
969
934
  ChevronDown,
970
935
  {
971
- className: cn(
936
+ className: cn2(
972
937
  "h-3 w-3 transition-transform duration-200",
973
938
  open && "rotate-180"
974
939
  )
@@ -990,7 +955,7 @@ function AppSwitcher({
990
955
  triggerDataTest
991
956
  }) {
992
957
  const [open, setOpen] = useState(false);
993
- return /* @__PURE__ */ jsx6(
958
+ return /* @__PURE__ */ jsx5(
994
959
  DropdownMenu2,
995
960
  {
996
961
  "aria-label": "Switch application",
@@ -1001,26 +966,25 @@ function AppSwitcher({
1001
966
  align: "start",
1002
967
  open,
1003
968
  onOpenChange: setOpen,
1004
- trigger: /* @__PURE__ */ jsxs6(
969
+ trigger: /* @__PURE__ */ jsxs5(
1005
970
  "span",
1006
971
  {
1007
972
  className: appSwitcherTriggerClass(open, collapsed),
1008
973
  "data-test": "app-switcher-trigger",
1009
- "aria-expanded": open,
1010
974
  children: [
1011
- /* @__PURE__ */ jsxs6(AppSwitcherMark, { collapsed, children: [
975
+ /* @__PURE__ */ jsxs5(AppSwitcherMark, { collapsed, children: [
1012
976
  mark,
1013
- collapsed ? /* @__PURE__ */ jsx6(AppSwitcherChevron, { open, collapsed: true }) : null
977
+ collapsed ? /* @__PURE__ */ jsx5(AppSwitcherChevron, { open, collapsed: true }) : null
1014
978
  ] }),
1015
979
  !collapsed ? title : null,
1016
- !collapsed ? /* @__PURE__ */ jsx6(AppSwitcherChevron, { open }) : null
980
+ !collapsed ? /* @__PURE__ */ jsx5(AppSwitcherChevron, { open }) : null
1017
981
  ]
1018
982
  }
1019
983
  ),
1020
- children: /* @__PURE__ */ jsxs6("div", { id: "app-switcher-menu", className: "p-1", "data-test": "app-switcher-menu", children: [
1021
- /* @__PURE__ */ jsx6("p", { className: "px-3 py-2 text-[10px] font-semibold uppercase tracking-[0.14em] text-ph-mutedtext", children: "Switch application" }),
1022
- apps.filter((app) => app.slug !== currentApp).map((app) => /* @__PURE__ */ jsxs6("div", { className: "relative", children: [
1023
- /* @__PURE__ */ jsx6(
984
+ children: /* @__PURE__ */ jsxs5("div", { id: "app-switcher-menu", className: "p-1", "data-test": "app-switcher-menu", children: [
985
+ /* @__PURE__ */ jsx5("p", { className: "px-3 py-2 text-[10px] font-semibold uppercase tracking-[0.14em] text-ph-mutedtext", children: "Switch application" }),
986
+ apps.filter((app) => app.slug !== currentApp).map((app) => /* @__PURE__ */ jsxs5("div", { className: "relative", children: [
987
+ /* @__PURE__ */ jsx5(
1024
988
  DropdownItem2,
1025
989
  {
1026
990
  id: `switch-app-${app.slug}`,
@@ -1030,9 +994,9 @@ function AppSwitcher({
1030
994
  setOpen(false);
1031
995
  onSelect(app);
1032
996
  },
1033
- className: cn(appSwitcherMenuItemClass(), "pr-11"),
1034
- children: /* @__PURE__ */ jsxs6("span", { className: "flex min-w-0 items-center gap-3", children: [
1035
- /* @__PURE__ */ jsx6(
997
+ className: cn2(appSwitcherMenuItemClass(), "pr-11"),
998
+ children: /* @__PURE__ */ jsxs5("span", { className: "flex min-w-0 items-center gap-3", children: [
999
+ /* @__PURE__ */ jsx5(
1036
1000
  Image,
1037
1001
  {
1038
1002
  src: app.icon,
@@ -1042,17 +1006,16 @@ function AppSwitcher({
1042
1006
  className: "h-7 w-7 shrink-0 rounded-md object-contain"
1043
1007
  }
1044
1008
  ),
1045
- /* @__PURE__ */ jsxs6("span", { className: "min-w-0 flex-1", children: [
1046
- /* @__PURE__ */ jsx6("span", { className: "block truncate text-sm font-medium text-ph-ink", children: app.name }),
1047
- /* @__PURE__ */ jsx6("span", { className: "block truncate text-xs text-ph-mutedtext", children: app.description })
1009
+ /* @__PURE__ */ jsxs5("span", { className: "min-w-0 flex-1", children: [
1010
+ /* @__PURE__ */ jsx5("span", { className: "block truncate text-sm font-medium text-ph-ink", children: app.name }),
1011
+ /* @__PURE__ */ jsx5("span", { className: "block truncate text-xs text-ph-mutedtext", children: app.description })
1048
1012
  ] })
1049
1013
  ] })
1050
1014
  }
1051
1015
  ),
1052
- /* @__PURE__ */ jsx6(
1053
- "button",
1016
+ /* @__PURE__ */ jsx5(
1017
+ DropdownItem2,
1054
1018
  {
1055
- type: "button",
1056
1019
  id: `switch-app-${app.slug}-new-tab`,
1057
1020
  "data-test": `switch-app-${app.slug}-new-tab`,
1058
1021
  "aria-label": `Open ${app.name} in a new tab`,
@@ -1061,8 +1024,9 @@ function AppSwitcher({
1061
1024
  setOpen(false);
1062
1025
  onSelect(app, { newTab: true });
1063
1026
  },
1064
- className: "absolute right-1.5 top-1/2 z-[1] flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-md text-ph-mutedtext transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ph-brand/35",
1065
- children: /* @__PURE__ */ jsx6(OpenNewWindow, { className: "h-4 w-4", "aria-hidden": "true" })
1027
+ className: "absolute right-1.5 top-1/2 z-[1] flex h-8 -translate-y-1/2 justify-center rounded-md p-0 text-ph-mutedtext transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:shadow-none focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ph-brand/35",
1028
+ style: { width: "2rem" },
1029
+ children: /* @__PURE__ */ jsx5(OpenNewWindow, { className: "h-4 w-4", "aria-hidden": "true" })
1066
1030
  }
1067
1031
  )
1068
1032
  ] }, app.slug))
@@ -1084,7 +1048,7 @@ var SUITE_APPS2 = [
1084
1048
  description: "Organisation and billing",
1085
1049
  icon: "/icon-shellstack.svg",
1086
1050
  landing: "/home",
1087
- baseUrlEnv: "NEXT_PUBLIC_SHELLSTACK_URL",
1051
+ baseUrlEnv: "NEXT_PUBLIC_PORTAL_URL",
1088
1052
  baseUrlFallback: "http://localhost:3002"
1089
1053
  },
1090
1054
  {
@@ -1115,12 +1079,12 @@ var SUITE_APPS2 = [
1115
1079
  baseUrlFallback: "http://localhost:3003"
1116
1080
  },
1117
1081
  {
1118
- slug: "nakama",
1119
- name: "Nakama",
1082
+ slug: "shelly",
1083
+ name: "Shelly",
1120
1084
  description: "AI teammates",
1121
- icon: "/nakama-icon.svg",
1085
+ icon: "/shelly-icon.svg",
1122
1086
  landing: "/today",
1123
- baseUrlEnv: "NEXT_PUBLIC_NAKAMA_URL",
1087
+ baseUrlEnv: "NEXT_PUBLIC_SHELLY_URL",
1124
1088
  baseUrlFallback: "http://localhost:3004"
1125
1089
  }
1126
1090
  ];
@@ -1134,16 +1098,20 @@ var SOURCE_APP_SLUG = {
1134
1098
  turtletime: "turtletime",
1135
1099
  tides: "tides",
1136
1100
  kraken: "kraken",
1137
- nakama: "nakama",
1101
+ shelly: "shelly",
1138
1102
  portal: "shellstack",
1139
1103
  shellstack: "shellstack"
1140
1104
  };
1105
+ function suiteAppSlugForNotificationSource(sourceApp) {
1106
+ var _a;
1107
+ return (_a = SOURCE_APP_SLUG[(sourceApp || "").toLowerCase()]) != null ? _a : null;
1108
+ }
1141
1109
  function resolveSuiteNotificationHref(href, sourceApp) {
1142
1110
  const value = (href || "").trim();
1143
1111
  if (!value) return "";
1144
1112
  if (/^(https?:|mailto:|\/\/)/i.test(value)) return value;
1145
1113
  const path = value.startsWith("/") ? value : `/${value}`;
1146
- const slug = SOURCE_APP_SLUG[(sourceApp || "").toLowerCase()];
1114
+ const slug = suiteAppSlugForNotificationSource(sourceApp);
1147
1115
  if (!slug) return path;
1148
1116
  return `${suiteAppBaseUrl(slug)}${path}`;
1149
1117
  }
@@ -1174,7 +1142,7 @@ function classifySuiteHref(href, bases, currentOrigin = "") {
1174
1142
  }
1175
1143
 
1176
1144
  // src/suite/components/suite-notification-bell.tsx
1177
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1145
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1178
1146
  var POLL_MS = 6e4;
1179
1147
  function timeAgo(iso) {
1180
1148
  const then = new Date(iso).getTime();
@@ -1270,6 +1238,28 @@ function SuiteNotificationBell({
1270
1238
  const target = resolveSuiteNotificationHref(n.href, n.source_app);
1271
1239
  if (!target) return;
1272
1240
  if (/^https?:\/\//i.test(target)) {
1241
+ try {
1242
+ const url = new URL(target);
1243
+ const sourceApp = suiteAppSlugForNotificationSource(n.source_app);
1244
+ const path = `${url.pathname}${url.search}${url.hash}`;
1245
+ if (url.origin === window.location.origin) {
1246
+ router.push(path);
1247
+ return;
1248
+ }
1249
+ if (sourceApp && sourceApp !== "shellstack") {
1250
+ const sourceOrigin = new URL(suiteAppBaseUrl(sourceApp)).origin;
1251
+ if (url.origin === sourceOrigin) {
1252
+ const launchUrl = new URL(
1253
+ `/launch/${sourceApp}`,
1254
+ suiteAppBaseUrl("shellstack")
1255
+ );
1256
+ launchUrl.searchParams.set("next", path);
1257
+ window.location.assign(launchUrl.toString());
1258
+ return;
1259
+ }
1260
+ }
1261
+ } catch (e) {
1262
+ }
1273
1263
  window.location.assign(target);
1274
1264
  return;
1275
1265
  }
@@ -1292,7 +1282,7 @@ function SuiteNotificationBell({
1292
1282
  } catch (e) {
1293
1283
  }
1294
1284
  }, [markAllRead]);
1295
- return /* @__PURE__ */ jsxs7(
1285
+ return /* @__PURE__ */ jsxs6(
1296
1286
  DropdownMenu2,
1297
1287
  {
1298
1288
  "aria-label": unread > 0 ? `Notifications, ${unread} unread` : "Notifications",
@@ -1302,14 +1292,14 @@ function SuiteNotificationBell({
1302
1292
  panelClassName: "w-80 overflow-hidden p-0",
1303
1293
  open,
1304
1294
  onOpenChange,
1305
- trigger: /* @__PURE__ */ jsx7("span", { className: "relative inline-flex h-11 w-11 items-center justify-center overflow-visible rounded-full text-ph-mutedtext transition hover:bg-ph-muted hover:text-ph-ink", children: /* @__PURE__ */ jsxs7("span", { className: "relative inline-flex h-5 w-5", children: [
1306
- /* @__PURE__ */ jsx7(Bell, { className: "h-5 w-5", strokeWidth: 1.75, "aria-hidden": true }),
1307
- unread > 0 ? /* @__PURE__ */ jsx7("span", { className: "pointer-events-none absolute -right-2 -top-2 z-[1] flex h-4 min-w-4 items-center justify-center rounded-full bg-ph-brand px-1 text-[9px] font-semibold leading-none text-white ring-2 ring-ph-surface", children: unread > 9 ? "9+" : unread }) : null
1295
+ trigger: /* @__PURE__ */ jsx6("span", { className: "relative inline-flex h-11 w-11 items-center justify-center overflow-visible rounded-full text-ph-mutedtext transition hover:bg-ph-muted hover:text-ph-ink", children: /* @__PURE__ */ jsxs6("span", { className: "relative inline-flex h-5 w-5", children: [
1296
+ /* @__PURE__ */ jsx6(Bell, { className: "h-5 w-5", strokeWidth: 1.75, "aria-hidden": true }),
1297
+ unread > 0 ? /* @__PURE__ */ jsx6("span", { className: "pointer-events-none absolute -right-2 -top-2 z-[1] flex h-4 min-w-4 items-center justify-center rounded-full bg-ph-brand px-1 text-[9px] font-semibold leading-none text-[var(--ph-on-accent)] ring-2 ring-ph-surface", children: unread > 9 ? "9+" : unread }) : null
1308
1298
  ] }) }),
1309
1299
  children: [
1310
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between border-b border-ph-border px-3 py-2", children: [
1311
- /* @__PURE__ */ jsx7("span", { className: "text-sm font-semibold text-ph-ink", children: "Notifications" }),
1312
- unread > 0 ? /* @__PURE__ */ jsx7(
1300
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between border-b border-ph-border px-3 py-2", children: [
1301
+ /* @__PURE__ */ jsx6("span", { className: "text-sm font-semibold text-ph-ink", children: "Notifications" }),
1302
+ unread > 0 ? /* @__PURE__ */ jsx6(
1313
1303
  "button",
1314
1304
  {
1315
1305
  type: "button",
@@ -1321,27 +1311,26 @@ function SuiteNotificationBell({
1321
1311
  }
1322
1312
  ) : null
1323
1313
  ] }),
1324
- /* @__PURE__ */ jsx7("div", { className: "max-h-80 overflow-y-auto py-1", children: items.length === 0 ? /* @__PURE__ */ jsx7("p", { className: "px-3 py-6 text-center text-sm text-ph-mutedtext", children: "You're all caught up." }) : items.map((n) => /* @__PURE__ */ jsxs7(
1314
+ /* @__PURE__ */ jsx6("div", { className: "max-h-80 overflow-y-auto py-1", children: items.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "px-3 py-6 text-center text-sm text-ph-mutedtext", children: "You're all caught up." }) : items.map((n) => /* @__PURE__ */ jsxs6(
1325
1315
  "button",
1326
1316
  {
1327
- type: "button",
1328
1317
  id: `${dataTest}-item-${n.id}`,
1329
1318
  "data-test": `${dataTest}-item-${n.id}`,
1330
1319
  onClick: () => void openItem(n),
1331
1320
  className: "flex w-full flex-col gap-0.5 px-3 py-2 text-left transition hover:bg-ph-muted",
1332
1321
  children: [
1333
- /* @__PURE__ */ jsxs7("span", { className: "flex items-center gap-2", children: [
1334
- !n.read_at ? /* @__PURE__ */ jsx7(
1322
+ /* @__PURE__ */ jsxs6("span", { className: "flex items-center gap-2", children: [
1323
+ !n.read_at ? /* @__PURE__ */ jsx6(
1335
1324
  "span",
1336
1325
  {
1337
1326
  className: "h-1.5 w-1.5 shrink-0 rounded-full bg-ph-brand",
1338
1327
  "aria-hidden": true
1339
1328
  }
1340
1329
  ) : null,
1341
- /* @__PURE__ */ jsx7("span", { className: "truncate text-sm font-medium text-ph-ink", children: n.title })
1330
+ /* @__PURE__ */ jsx6("span", { className: "truncate text-sm font-medium text-ph-ink", children: n.title })
1342
1331
  ] }),
1343
- n.body ? /* @__PURE__ */ jsx7("span", { className: "line-clamp-2 text-xs text-ph-mutedtext", children: n.body }) : null,
1344
- /* @__PURE__ */ jsxs7("span", { className: "text-[11px] uppercase tracking-wide text-ph-subtle", children: [
1332
+ n.body ? /* @__PURE__ */ jsx6("span", { className: "line-clamp-2 text-xs text-ph-mutedtext", children: n.body }) : null,
1333
+ /* @__PURE__ */ jsxs6("span", { className: "text-[11px] uppercase tracking-wide text-ph-subtle", children: [
1345
1334
  n.source_app,
1346
1335
  " \xB7 ",
1347
1336
  timeAgo(n.created_at)
@@ -1357,7 +1346,7 @@ function SuiteNotificationBell({
1357
1346
 
1358
1347
  // src/suite/components/command-palette-host.tsx
1359
1348
  import { useEffect as useEffect2, useState as useState3 } from "react";
1360
- import { jsx as jsx8 } from "react/jsx-runtime";
1349
+ import { jsx as jsx7 } from "react/jsx-runtime";
1361
1350
  function isEditableTarget() {
1362
1351
  if (typeof document === "undefined") return false;
1363
1352
  const el = document.activeElement;
@@ -1389,7 +1378,7 @@ function CommandPaletteHost({
1389
1378
  return () => window.removeEventListener("keydown", onKey);
1390
1379
  }, [open]);
1391
1380
  if (!isAuthenticated) return null;
1392
- return /* @__PURE__ */ jsx8(
1381
+ return /* @__PURE__ */ jsx7(
1393
1382
  CommandPalette,
1394
1383
  {
1395
1384
  items,
@@ -1423,17 +1412,18 @@ function useIdleMount(timeoutMs = 1200) {
1423
1412
  }
1424
1413
 
1425
1414
  // src/suite/components/deferred-chrome.tsx
1426
- import { Fragment as Fragment3, jsx as jsx9 } from "react/jsx-runtime";
1415
+ import { Fragment as Fragment3, jsx as jsx8 } from "react/jsx-runtime";
1427
1416
  function DeferredChrome({ children }) {
1428
1417
  const ready = useIdleMount();
1429
1418
  if (!ready) return null;
1430
- return /* @__PURE__ */ jsx9(Fragment3, { children });
1419
+ return /* @__PURE__ */ jsx8(Fragment3, { children });
1431
1420
  }
1432
1421
 
1433
1422
  // src/suite/components/suite-mobile-drawer.tsx
1434
- import { useEffect as useEffect4, useRef, useState as useState5 } from "react";
1423
+ import * as Dialog from "@radix-ui/react-dialog";
1424
+ import { useEffect as useEffect4, useState as useState5 } from "react";
1435
1425
  import { Xmark as X } from "iconoir-react";
1436
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1426
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
1437
1427
  function SuiteMobileDrawer({
1438
1428
  open,
1439
1429
  onClose,
@@ -1455,11 +1445,6 @@ function SuiteMobileDrawer({
1455
1445
  }) {
1456
1446
  const [rendered, setRendered] = useState5(open);
1457
1447
  const [shown, setShown] = useState5(open);
1458
- const panelRef = useRef(null);
1459
- const onCloseRef = useRef(onClose);
1460
- useEffect4(() => {
1461
- onCloseRef.current = onClose;
1462
- }, [onClose]);
1463
1448
  useEffect4(() => {
1464
1449
  if (open) {
1465
1450
  queueMicrotask(() => setRendered(true));
@@ -1476,86 +1461,61 @@ function SuiteMobileDrawer({
1476
1461
  const timeout = window.setTimeout(() => setRendered(false), durationMs);
1477
1462
  return () => window.clearTimeout(timeout);
1478
1463
  }, [open, durationMs]);
1479
- useEffect4(() => {
1480
- if (!open) return;
1481
- const onKeyDown = (event) => {
1482
- if (event.key === "Escape") onCloseRef.current();
1483
- };
1484
- window.addEventListener("keydown", onKeyDown);
1485
- const previousOverflow = document.body.style.overflow;
1486
- document.body.style.overflow = "hidden";
1487
- const previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
1488
- const frame = requestAnimationFrame(() => {
1489
- var _a;
1490
- return (_a = panelRef.current) == null ? void 0 : _a.focus();
1491
- });
1492
- return () => {
1493
- window.removeEventListener("keydown", onKeyDown);
1494
- document.body.style.overflow = previousOverflow;
1495
- cancelAnimationFrame(frame);
1496
- if (previouslyFocused == null ? void 0 : previouslyFocused.isConnected) previouslyFocused.focus();
1497
- };
1498
- }, [open]);
1499
1464
  if (!rendered) return null;
1500
- return /* @__PURE__ */ jsxs8("div", { "data-test": dataTest, className: "no-print fixed inset-0 z-50 lg:hidden", children: [
1501
- /* @__PURE__ */ jsx10(
1502
- "button",
1465
+ return /* @__PURE__ */ jsx9(Dialog.Root, { open, onOpenChange: (nextOpen) => !nextOpen && onClose(), children: /* @__PURE__ */ jsxs7(Dialog.Portal, { forceMount: true, children: [
1466
+ /* @__PURE__ */ jsx9(
1467
+ Dialog.Overlay,
1503
1468
  {
1504
- type: "button",
1469
+ forceMount: true,
1505
1470
  id: backdropId,
1506
- "data-test": backdropDataTest,
1507
- tabIndex: -1,
1508
- className: cn(
1509
- "absolute inset-0 bg-black/25 backdrop-blur-sm motion-safe:transition-opacity motion-safe:ease-spring-subtle",
1471
+ "data-test": backdropDataTest != null ? backdropDataTest : dataTest,
1472
+ "aria-label": backdropLabel,
1473
+ className: cn2(
1474
+ "no-print fixed inset-0 z-50 bg-black/25 backdrop-blur-sm motion-safe:transition-opacity motion-safe:ease-spring-subtle lg:hidden",
1510
1475
  shown ? "opacity-100" : "opacity-0"
1511
1476
  ),
1512
- style: { transitionDuration: `${durationMs}ms` },
1513
- onClick: onClose,
1514
- "aria-label": backdropLabel
1477
+ style: { transitionDuration: `${durationMs}ms` }
1515
1478
  }
1516
1479
  ),
1517
- /* @__PURE__ */ jsxs8(
1480
+ /* @__PURE__ */ jsx9(Dialog.Content, { forceMount: true, asChild: true, children: /* @__PURE__ */ jsxs7(
1518
1481
  "aside",
1519
1482
  {
1520
- ref: panelRef,
1521
1483
  role: "dialog",
1522
- "aria-modal": "true",
1523
- "aria-label": dialogLabel,
1524
- tabIndex: -1,
1525
1484
  "data-test": panelDataTest,
1526
- className: cn(
1527
- "relative flex h-dvh max-h-dvh w-[86%] max-w-72 flex-col overflow-hidden bg-ph-surface shadow-xl outline-none motion-safe:transition-transform motion-safe:ease-spring-fast",
1528
- side === "right" && "ml-auto",
1485
+ "aria-modal": "true",
1486
+ className: cn2(
1487
+ "fixed inset-y-0 left-0 z-50 flex h-dvh max-h-dvh w-[86%] max-w-72 flex-col overflow-hidden bg-ph-surface shadow-xl outline-none motion-safe:transition-transform motion-safe:ease-spring-fast lg:hidden",
1488
+ side === "right" && "left-auto right-0",
1529
1489
  shown ? "translate-x-0" : side === "right" ? "translate-x-full" : "-translate-x-full",
1530
1490
  panelClassName
1531
1491
  ),
1532
1492
  style: { transitionDuration: `${durationMs}ms` },
1533
1493
  children: [
1534
- showCloseButton ? /* @__PURE__ */ jsxs8(
1494
+ /* @__PURE__ */ jsx9(Dialog.Title, { className: "sr-only", children: dialogLabel }),
1495
+ showCloseButton ? /* @__PURE__ */ jsxs7(
1535
1496
  "div",
1536
1497
  {
1537
- className: cn(
1498
+ className: cn2(
1538
1499
  "flex h-14 shrink-0 items-center border-b border-ph-border px-3",
1539
1500
  title ? "justify-between" : "justify-end"
1540
1501
  ),
1541
1502
  children: [
1542
- title ? /* @__PURE__ */ jsx10("span", { className: "truncate text-sm font-semibold text-ph-ink", children: title }) : null,
1543
- /* @__PURE__ */ jsx10(
1503
+ title ? /* @__PURE__ */ jsx9("span", { className: "truncate text-sm font-semibold text-ph-ink", children: title }) : null,
1504
+ /* @__PURE__ */ jsx9(Dialog.Close, { asChild: true, children: /* @__PURE__ */ jsx9(
1544
1505
  "button",
1545
1506
  {
1546
1507
  type: "button",
1547
1508
  id: closeButtonId,
1548
1509
  "data-test": closeButtonDataTest,
1549
- onClick: onClose,
1550
1510
  className: "-mr-1.5 inline-flex h-11 w-11 items-center justify-center rounded-lg text-ph-mutedtext transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
1551
1511
  "aria-label": closeLabel,
1552
- children: /* @__PURE__ */ jsx10(X, { className: "h-4 w-4", "aria-hidden": true })
1512
+ children: /* @__PURE__ */ jsx9(X, { className: "h-4 w-4", "aria-hidden": true })
1553
1513
  }
1554
- )
1514
+ ) })
1555
1515
  ]
1556
1516
  }
1557
1517
  ) : null,
1558
- /* @__PURE__ */ jsx10(
1518
+ /* @__PURE__ */ jsx9(
1559
1519
  "div",
1560
1520
  {
1561
1521
  "data-test": "suite-mobile-drawer-body",
@@ -1565,13 +1525,13 @@ function SuiteMobileDrawer({
1565
1525
  )
1566
1526
  ]
1567
1527
  }
1568
- )
1569
- ] });
1528
+ ) })
1529
+ ] }) });
1570
1530
  }
1571
1531
 
1572
1532
  // src/suite/components/suite-mobile-header.tsx
1573
1533
  import { Menu } from "iconoir-react";
1574
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1534
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1575
1535
  function SuiteMobileHeader({
1576
1536
  onMenuClick,
1577
1537
  menuIcon: MenuIcon = Menu,
@@ -1583,16 +1543,16 @@ function SuiteMobileHeader({
1583
1543
  dataTest,
1584
1544
  className
1585
1545
  }) {
1586
- return /* @__PURE__ */ jsxs9(
1546
+ return /* @__PURE__ */ jsxs8(
1587
1547
  "header",
1588
1548
  {
1589
1549
  "data-test": dataTest,
1590
- className: cn(
1550
+ className: cn2(
1591
1551
  "suite-app-bar no-print sticky top-0 z-30 flex min-h-[56px] shrink-0 items-center gap-1 border-b border-ph-border px-2 lg:hidden",
1592
1552
  className
1593
1553
  ),
1594
1554
  children: [
1595
- onMenuClick ? /* @__PURE__ */ jsx11(
1555
+ onMenuClick ? /* @__PURE__ */ jsx10(
1596
1556
  "button",
1597
1557
  {
1598
1558
  type: "button",
@@ -1601,20 +1561,20 @@ function SuiteMobileHeader({
1601
1561
  className: "suite-press inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-xl text-ph-ink transition-colors hover:bg-ph-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
1602
1562
  onClick: onMenuClick,
1603
1563
  "aria-label": menuLabel,
1604
- children: /* @__PURE__ */ jsx11(MenuIcon, { className: "h-5 w-5", "aria-hidden": true })
1564
+ children: /* @__PURE__ */ jsx10(MenuIcon, { className: "h-5 w-5", "aria-hidden": true })
1605
1565
  }
1606
1566
  ) : null,
1607
- /* @__PURE__ */ jsx11(
1567
+ /* @__PURE__ */ jsx10(
1608
1568
  "div",
1609
1569
  {
1610
- className: cn(
1570
+ className: cn2(
1611
1571
  "flex min-w-0 flex-1 items-center",
1612
1572
  !onMenuClick && "pl-2"
1613
1573
  ),
1614
1574
  children: title
1615
1575
  }
1616
1576
  ),
1617
- actions ? /* @__PURE__ */ jsx11("div", { className: "flex shrink-0 items-center gap-1", children: actions }) : null
1577
+ actions ? /* @__PURE__ */ jsx10("div", { className: "flex shrink-0 items-center gap-1", children: actions }) : null
1618
1578
  ]
1619
1579
  }
1620
1580
  );
@@ -1623,26 +1583,26 @@ function SuiteMobileHeader({
1623
1583
  // src/suite/components/suite-layout.tsx
1624
1584
  import {
1625
1585
  useEffect as useEffect5,
1626
- useRef as useRef2,
1586
+ useRef,
1627
1587
  useState as useState6
1628
1588
  } from "react";
1629
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1589
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1630
1590
  function SuiteBreadcrumbs({ items, className }) {
1631
- return /* @__PURE__ */ jsx12("nav", { "aria-label": "Breadcrumb", className, children: /* @__PURE__ */ jsx12("ol", { className: "flex min-w-0 items-center gap-1.5", children: items.map((item, index) => {
1591
+ return /* @__PURE__ */ jsx11("nav", { "aria-label": "Breadcrumb", className, children: /* @__PURE__ */ jsx11("ol", { className: "flex min-w-0 items-center gap-1.5", children: items.map((item, index) => {
1632
1592
  const isLast = index === items.length - 1;
1633
- return /* @__PURE__ */ jsxs10("li", { className: "flex min-w-0 items-center gap-1.5", children: [
1634
- index > 0 ? /* @__PURE__ */ jsx12("span", { className: "select-none text-ph-border", "aria-hidden": "true", children: "/" }) : null,
1635
- item.href && !isLast ? /* @__PURE__ */ jsx12(
1593
+ return /* @__PURE__ */ jsxs9("li", { className: "flex min-w-0 items-center gap-1.5", children: [
1594
+ index > 0 ? /* @__PURE__ */ jsx11("span", { className: "select-none text-ph-border", "aria-hidden": "true", children: "/" }) : null,
1595
+ item.href && !isLast ? /* @__PURE__ */ jsx11(
1636
1596
  "a",
1637
1597
  {
1638
1598
  href: item.href,
1639
1599
  className: "truncate transition hover:text-ph-ink",
1640
1600
  children: item.label
1641
1601
  }
1642
- ) : /* @__PURE__ */ jsx12(
1602
+ ) : /* @__PURE__ */ jsx11(
1643
1603
  "span",
1644
1604
  {
1645
- className: cn(
1605
+ className: cn2(
1646
1606
  "truncate",
1647
1607
  isLast ? "font-medium text-ph-ink" : "text-ph-subtle"
1648
1608
  ),
@@ -1664,7 +1624,7 @@ function SuitePage({
1664
1624
  role,
1665
1625
  "aria-live": ariaLive
1666
1626
  }) {
1667
- return /* @__PURE__ */ jsx12(
1627
+ return /* @__PURE__ */ jsx11(
1668
1628
  Component,
1669
1629
  {
1670
1630
  id,
@@ -1672,7 +1632,7 @@ function SuitePage({
1672
1632
  "data-width": width,
1673
1633
  role,
1674
1634
  "aria-live": ariaLive,
1675
- className: cn(
1635
+ className: cn2(
1676
1636
  "suite-page-stage relative flex w-full min-w-0 flex-col",
1677
1637
  inset && "suite-page-inset",
1678
1638
  className
@@ -1704,7 +1664,7 @@ function SuitePageHeader({
1704
1664
  */
1705
1665
  sticky = true
1706
1666
  }) {
1707
- const headerRef = useRef2(null);
1667
+ const headerRef = useRef(null);
1708
1668
  const [stuck, setStuck] = useState6(false);
1709
1669
  useEffect5(() => {
1710
1670
  if (!sticky) return;
@@ -1732,33 +1692,33 @@ function SuitePageHeader({
1732
1692
  sentinel.remove();
1733
1693
  };
1734
1694
  }, [sticky]);
1735
- const resolvedIcon = todayIcon ? /* @__PURE__ */ jsx12(
1695
+ const resolvedIcon = todayIcon ? /* @__PURE__ */ jsx11(
1736
1696
  TodayTimeIcon,
1737
1697
  {
1738
- className: cn(
1698
+ className: cn2(
1739
1699
  "h-6 w-6 text-amber-500",
1740
1700
  todayIconClassName
1741
1701
  )
1742
1702
  }
1743
1703
  ) : icon;
1744
- return /* @__PURE__ */ jsxs10(
1704
+ return /* @__PURE__ */ jsxs9(
1745
1705
  "header",
1746
1706
  {
1747
1707
  ref: headerRef,
1748
1708
  "data-test": dataTest,
1749
1709
  "data-stuck": sticky ? stuck ? "true" : "false" : void 0,
1750
- className: cn(
1710
+ className: cn2(
1751
1711
  "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",
1752
1712
  // No extra pt here — page inset sets the shared title baseline.
1753
1713
  sticky && "suite-page-header-sticky",
1754
1714
  className
1755
1715
  ),
1756
1716
  children: [
1757
- /* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
1758
- eyebrow ? /* @__PURE__ */ jsx12("div", { className: "mb-0.5 flex min-w-0 items-center gap-1.5 text-xs text-ph-subtle", children: eyebrow }) : null,
1759
- breadcrumb ? /* @__PURE__ */ jsx12("div", { className: "mb-0.5 flex min-w-0 items-center gap-1.5 text-xs text-ph-subtle", children: breadcrumb }) : null,
1760
- /* @__PURE__ */ jsxs10("div", { className: "flex min-w-0 items-center gap-3", children: [
1761
- resolvedIcon ? /* @__PURE__ */ jsx12(
1717
+ /* @__PURE__ */ jsxs9("div", { className: "min-w-0 flex-1", children: [
1718
+ eyebrow ? /* @__PURE__ */ jsx11("div", { className: "mb-0.5 flex min-w-0 items-center gap-1.5 text-xs text-ph-subtle", children: eyebrow }) : null,
1719
+ breadcrumb ? /* @__PURE__ */ jsx11("div", { className: "mb-0.5 flex min-w-0 items-center gap-1.5 text-xs text-ph-subtle", children: breadcrumb }) : null,
1720
+ /* @__PURE__ */ jsxs9("div", { className: "flex min-w-0 items-center gap-3", children: [
1721
+ resolvedIcon ? /* @__PURE__ */ jsx11(
1762
1722
  "div",
1763
1723
  {
1764
1724
  className: "hidden shrink-0 sm:flex sm:items-center [&_svg]:h-6 [&_svg]:w-6",
@@ -1766,24 +1726,24 @@ function SuitePageHeader({
1766
1726
  children: resolvedIcon
1767
1727
  }
1768
1728
  ) : null,
1769
- /* @__PURE__ */ jsxs10("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
1770
- /* @__PURE__ */ jsx12(
1729
+ /* @__PURE__ */ jsxs9("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
1730
+ /* @__PURE__ */ jsx11(
1771
1731
  "h1",
1772
1732
  {
1773
- className: cn(
1733
+ className: cn2(
1774
1734
  "min-w-0 break-words font-display text-[1.375rem] font-semibold leading-tight tracking-tight text-ph-ink text-balance sm:text-2xl",
1775
1735
  titleClassName
1776
1736
  ),
1777
1737
  children: title
1778
1738
  }
1779
1739
  ),
1780
- badge ? /* @__PURE__ */ jsx12("span", { className: "rounded-md bg-ph-muted px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-ph-subtle", children: badge }) : null
1740
+ badge ? /* @__PURE__ */ jsx11("span", { className: "rounded-md bg-ph-muted px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-ph-subtle", children: badge }) : null
1781
1741
  ] })
1782
1742
  ] }),
1783
- description ? /* @__PURE__ */ jsx12(
1743
+ description ? /* @__PURE__ */ jsx11(
1784
1744
  "div",
1785
1745
  {
1786
- className: cn(
1746
+ className: cn2(
1787
1747
  "mt-1.5 max-w-2xl text-pretty text-sm leading-5 text-ph-subtle",
1788
1748
  resolvedIcon && "sm:pl-9"
1789
1749
  ),
@@ -1791,7 +1751,7 @@ function SuitePageHeader({
1791
1751
  }
1792
1752
  ) : null
1793
1753
  ] }),
1794
- actions ? /* @__PURE__ */ jsx12("div", { className: "flex max-w-full items-center gap-2 self-stretch overflow-x-auto pb-1 sm:shrink-0 sm:self-center sm:overflow-visible sm:pb-0", children: actions }) : null
1754
+ actions ? /* @__PURE__ */ jsx11("div", { className: "flex max-w-full items-center gap-2 self-stretch overflow-x-auto pb-1 sm:shrink-0 sm:self-center sm:overflow-visible sm:pb-0", children: actions }) : null
1795
1755
  ]
1796
1756
  }
1797
1757
  );
@@ -1802,13 +1762,13 @@ function SuiteToolbar({
1802
1762
  label = "Page tools",
1803
1763
  dataTest
1804
1764
  }) {
1805
- return /* @__PURE__ */ jsx12(
1765
+ return /* @__PURE__ */ jsx11(
1806
1766
  "div",
1807
1767
  {
1808
1768
  role: "toolbar",
1809
1769
  "aria-label": label,
1810
1770
  "data-test": dataTest,
1811
- className: cn("flex min-w-0 flex-wrap items-center gap-2", className),
1771
+ className: cn2("flex min-w-0 flex-wrap items-center gap-2", className),
1812
1772
  children
1813
1773
  }
1814
1774
  );
@@ -1819,12 +1779,12 @@ function SuiteTabList({
1819
1779
  label,
1820
1780
  dataTest
1821
1781
  }) {
1822
- return /* @__PURE__ */ jsx12(
1782
+ return /* @__PURE__ */ jsx11(
1823
1783
  "nav",
1824
1784
  {
1825
1785
  "aria-label": label,
1826
1786
  "data-test": dataTest,
1827
- className: cn(
1787
+ className: cn2(
1828
1788
  "flex min-w-0 flex-wrap gap-2 [&_a]:min-h-[48px] [&_button]:min-h-[48px] sm:[&_a]:min-h-[44px] sm:[&_button]:min-h-[44px]",
1829
1789
  className
1830
1790
  ),
@@ -1838,26 +1798,26 @@ function SuiteSectionHeader({
1838
1798
  action,
1839
1799
  className
1840
1800
  }) {
1841
- return /* @__PURE__ */ jsxs10(
1801
+ return /* @__PURE__ */ jsxs9(
1842
1802
  "div",
1843
1803
  {
1844
- className: cn(
1804
+ className: cn2(
1845
1805
  "flex min-w-0 flex-col gap-4 sm:flex-row sm:items-end sm:justify-between",
1846
1806
  className
1847
1807
  ),
1848
1808
  children: [
1849
- /* @__PURE__ */ jsxs10("div", { className: "min-w-0", children: [
1850
- /* @__PURE__ */ jsx12("h2", { className: "font-display text-balance text-lg font-semibold tracking-tight text-ph-ink sm:text-xl", children: title }),
1851
- description ? /* @__PURE__ */ jsx12("div", { className: "mt-1 max-w-2xl text-pretty text-sm leading-5 text-ph-subtle", children: description }) : null
1809
+ /* @__PURE__ */ jsxs9("div", { className: "min-w-0", children: [
1810
+ /* @__PURE__ */ jsx11("h2", { className: "font-display text-balance text-lg font-semibold tracking-tight text-ph-ink sm:text-xl", children: title }),
1811
+ description ? /* @__PURE__ */ jsx11("div", { className: "mt-1 max-w-2xl text-pretty text-sm leading-5 text-ph-subtle", children: description }) : null
1852
1812
  ] }),
1853
- action ? /* @__PURE__ */ jsx12("div", { className: "flex min-h-[44px] shrink-0 items-center", children: action }) : null
1813
+ action ? /* @__PURE__ */ jsx11("div", { className: "flex min-h-[44px] shrink-0 items-center", children: action }) : null
1854
1814
  ]
1855
1815
  }
1856
1816
  );
1857
1817
  }
1858
1818
 
1859
1819
  // src/suite/components/suite-settings-layout.tsx
1860
- import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1820
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1861
1821
  function SuiteSettingsLayout({
1862
1822
  children,
1863
1823
  sidebar,
@@ -1871,13 +1831,13 @@ function SuiteSettingsLayout({
1871
1831
  className,
1872
1832
  contentClassName
1873
1833
  }) {
1874
- return /* @__PURE__ */ jsxs11(
1834
+ return /* @__PURE__ */ jsxs10(
1875
1835
  SuitePage,
1876
1836
  {
1877
1837
  dataTest,
1878
- className: cn("flex h-full flex-col overflow-hidden", className),
1838
+ className: cn2("flex h-full flex-col overflow-hidden", className),
1879
1839
  children: [
1880
- header != null ? header : /* @__PURE__ */ jsx13(
1840
+ header != null ? header : /* @__PURE__ */ jsx12(
1881
1841
  SuitePageHeader,
1882
1842
  {
1883
1843
  sticky: true,
@@ -1886,10 +1846,10 @@ function SuiteSettingsLayout({
1886
1846
  description
1887
1847
  }
1888
1848
  ),
1889
- loading != null ? loading : /* @__PURE__ */ jsxs11("div", { className: "suite-page-body mt-6 flex min-h-0 flex-1 flex-col", children: [
1890
- mobileNav ? /* @__PURE__ */ jsx13("div", { className: "sticky top-0 z-20 mb-2 shrink-0 bg-ph-canvas/95 backdrop-blur lg:hidden", children: mobileNav }) : null,
1891
- /* @__PURE__ */ jsxs11("div", { className: "flex min-h-0 flex-1 flex-col gap-6 lg:flex-row lg:gap-8", children: [
1892
- /* @__PURE__ */ jsx13(
1849
+ loading != null ? loading : /* @__PURE__ */ jsxs10("div", { className: "suite-page-body mt-6 flex min-h-0 flex-1 flex-col", children: [
1850
+ mobileNav ? /* @__PURE__ */ jsx12("div", { className: "sticky top-0 z-20 mb-2 shrink-0 bg-ph-canvas/95 backdrop-blur lg:hidden", children: mobileNav }) : null,
1851
+ /* @__PURE__ */ jsxs10("div", { className: "flex min-h-0 flex-1 flex-col gap-6 lg:flex-row lg:gap-8", children: [
1852
+ /* @__PURE__ */ jsx12(
1893
1853
  "aside",
1894
1854
  {
1895
1855
  "aria-label": "Settings",
@@ -1897,10 +1857,10 @@ function SuiteSettingsLayout({
1897
1857
  children: sidebar
1898
1858
  }
1899
1859
  ),
1900
- /* @__PURE__ */ jsx13(
1860
+ /* @__PURE__ */ jsx12(
1901
1861
  "div",
1902
1862
  {
1903
- className: cn(
1863
+ className: cn2(
1904
1864
  "min-h-0 min-w-0 flex-1 overflow-y-auto overscroll-contain pb-6",
1905
1865
  contentClassName
1906
1866
  ),
@@ -1918,24 +1878,25 @@ function SuiteSettingsLayout({
1918
1878
  import Image2 from "next/image";
1919
1879
  import { useRouter as useRouter2 } from "next/navigation";
1920
1880
  import { LogOut, Settings } from "iconoir-react";
1921
- import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1881
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1922
1882
  function computeInitials(name, email) {
1923
1883
  const source = (name || email || "?").trim();
1924
1884
  return source.split(/[\s@]+/).filter(Boolean).slice(0, 2).map((part) => part[0]).join("").toUpperCase();
1925
1885
  }
1926
1886
  var AVATAR_TOKENS = [
1927
- "bg-ph-brand",
1928
- "bg-ph-violet",
1929
- "bg-ph-info",
1930
- "bg-ph-success",
1931
- "bg-ph-danger"
1887
+ ["bg-ph-brand", "text-[var(--ph-on-accent)]"],
1888
+ ["bg-ph-violet", "text-[var(--ph-on-violet)]"],
1889
+ ["bg-ph-info", "text-[var(--ph-on-info)]"],
1890
+ ["bg-ph-success", "text-[var(--ph-on-success)]"],
1891
+ ["bg-ph-danger", "text-[var(--ph-on-danger)]"]
1932
1892
  ];
1933
1893
  function avatarColorClass(seed) {
1934
1894
  let hash = 0;
1935
1895
  for (let i = 0; i < seed.length; i += 1) {
1936
1896
  hash = seed.charCodeAt(i) + ((hash << 5) - hash);
1937
1897
  }
1938
- return AVATAR_TOKENS[Math.abs(hash) % AVATAR_TOKENS.length];
1898
+ const [bg, ink] = AVATAR_TOKENS[Math.abs(hash) % AVATAR_TOKENS.length];
1899
+ return `${bg} ${ink}`;
1939
1900
  }
1940
1901
  function SuiteUserMenu({
1941
1902
  name,
@@ -1953,7 +1914,7 @@ function SuiteUserMenu({
1953
1914
  const router = useRouter2();
1954
1915
  const initials = fallbackInitials != null ? fallbackInitials : computeInitials(name, email);
1955
1916
  const fallbackColor = avatarColorClass(email || name || "?");
1956
- 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(
1917
+ const avatar = /* @__PURE__ */ jsx13("span", { className: "inline-flex h-10 w-10 items-center justify-center overflow-hidden rounded-full border border-ph-border", children: image ? /* @__PURE__ */ jsx13(
1957
1918
  Image2,
1958
1919
  {
1959
1920
  src: image,
@@ -1963,17 +1924,17 @@ function SuiteUserMenu({
1963
1924
  className: "h-full w-full object-cover",
1964
1925
  unoptimized: true
1965
1926
  }
1966
- ) : /* @__PURE__ */ jsx14(
1927
+ ) : /* @__PURE__ */ jsx13(
1967
1928
  "span",
1968
1929
  {
1969
- className: cn(
1970
- "flex h-full w-full items-center justify-center text-sm font-semibold text-white",
1930
+ className: cn2(
1931
+ "flex h-full w-full items-center justify-center text-sm font-semibold",
1971
1932
  fallbackColor
1972
1933
  ),
1973
1934
  children: initials
1974
1935
  }
1975
1936
  ) });
1976
- const accountMenu = /* @__PURE__ */ jsxs12(
1937
+ const accountMenu = /* @__PURE__ */ jsxs11(
1977
1938
  DropdownMenu,
1978
1939
  {
1979
1940
  trigger: avatar,
@@ -1985,30 +1946,30 @@ function SuiteUserMenu({
1985
1946
  sideOffset: 6,
1986
1947
  collisionPadding: 16,
1987
1948
  modal: false,
1988
- className: cn(
1949
+ className: cn2(
1989
1950
  "[&_.ph-dropdown-trigger]:rounded-full",
1990
1951
  showSignOutAction ? void 0 : className
1991
1952
  ),
1992
1953
  "data-test": dataTest,
1993
1954
  children: [
1994
- /* @__PURE__ */ jsxs12("div", { className: "px-2 py-1.5", children: [
1995
- /* @__PURE__ */ jsx14("p", { className: "truncate text-sm font-medium text-ph-ink", children: name || email || "Account" }),
1996
- email ? /* @__PURE__ */ jsx14("p", { className: "truncate text-xs text-ph-mutedtext", children: email }) : null
1955
+ /* @__PURE__ */ jsxs11("div", { className: "px-2 py-1.5", children: [
1956
+ /* @__PURE__ */ jsx13("p", { className: "truncate text-sm font-medium text-ph-ink", children: name || email || "Account" }),
1957
+ email ? /* @__PURE__ */ jsx13("p", { className: "truncate text-xs text-ph-mutedtext", children: email }) : null
1997
1958
  ] }),
1998
- /* @__PURE__ */ jsx14("div", { className: "my-1 border-t border-ph-border" }),
1999
- /* @__PURE__ */ jsxs12(
1959
+ /* @__PURE__ */ jsx13("div", { className: "my-1 border-t border-ph-border" }),
1960
+ /* @__PURE__ */ jsxs11(
2000
1961
  DropdownItem,
2001
1962
  {
2002
1963
  "data-test": `${dataTest}-settings`,
2003
1964
  onClick: () => router.push(settingsHref),
2004
1965
  children: [
2005
- /* @__PURE__ */ jsx14(Settings, { className: "h-4 w-4 text-ph-mutedtext", "aria-hidden": true }),
1966
+ /* @__PURE__ */ jsx13(Settings, { className: "h-4 w-4 text-ph-mutedtext", "aria-hidden": true }),
2006
1967
  "Profile settings"
2007
1968
  ]
2008
1969
  }
2009
1970
  ),
2010
- /* @__PURE__ */ jsxs12(DropdownItem, { "data-test": `${dataTest}-sign-out`, onClick: onSignOut, children: [
2011
- /* @__PURE__ */ jsx14(LogOut, { className: "h-4 w-4 text-ph-mutedtext", "aria-hidden": true }),
1971
+ /* @__PURE__ */ jsxs11(DropdownItem, { "data-test": `${dataTest}-sign-out`, onClick: onSignOut, children: [
1972
+ /* @__PURE__ */ jsx13(LogOut, { className: "h-4 w-4 text-ph-mutedtext", "aria-hidden": true }),
2012
1973
  "Sign out"
2013
1974
  ] })
2014
1975
  ]
@@ -2017,16 +1978,16 @@ function SuiteUserMenu({
2017
1978
  if (!showSignOutAction) {
2018
1979
  return accountMenu;
2019
1980
  }
2020
- return /* @__PURE__ */ jsxs12(
1981
+ return /* @__PURE__ */ jsxs11(
2021
1982
  "div",
2022
1983
  {
2023
- className: cn(
1984
+ className: cn2(
2024
1985
  "flex w-full min-w-0 flex-1 items-center justify-between gap-2",
2025
1986
  className
2026
1987
  ),
2027
1988
  children: [
2028
- /* @__PURE__ */ jsx14("div", { className: "shrink-0", children: accountMenu }),
2029
- /* @__PURE__ */ jsx14(
1989
+ /* @__PURE__ */ jsx13("div", { className: "shrink-0", children: accountMenu }),
1990
+ /* @__PURE__ */ jsx13(
2030
1991
  "button",
2031
1992
  {
2032
1993
  type: "button",
@@ -2035,7 +1996,7 @@ function SuiteUserMenu({
2035
1996
  "aria-label": "Sign out",
2036
1997
  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",
2037
1998
  onClick: onSignOut,
2038
- children: /* @__PURE__ */ jsx14(LogOut, { className: "h-4 w-4", "aria-hidden": true })
1999
+ children: /* @__PURE__ */ jsx13(LogOut, { className: "h-4 w-4", "aria-hidden": true })
2039
2000
  }
2040
2001
  )
2041
2002
  ]
@@ -2045,7 +2006,7 @@ function SuiteUserMenu({
2045
2006
 
2046
2007
  // src/suite/components/suite-bottom-nav.tsx
2047
2008
  import Link from "next/link";
2048
- import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
2009
+ import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
2049
2010
  function SuiteBottomNav({
2050
2011
  items,
2051
2012
  ariaLabel = "Primary navigation",
@@ -2058,8 +2019,8 @@ function SuiteBottomNav({
2058
2019
  );
2059
2020
  }
2060
2021
  const activeIndex = items.findIndex((item) => item.active);
2061
- return /* @__PURE__ */ jsxs13(Fragment4, { children: [
2062
- /* @__PURE__ */ jsx15(
2022
+ return /* @__PURE__ */ jsxs12(Fragment4, { children: [
2023
+ /* @__PURE__ */ jsx14(
2063
2024
  "div",
2064
2025
  {
2065
2026
  "aria-hidden": true,
@@ -2067,12 +2028,12 @@ function SuiteBottomNav({
2067
2028
  style: { height: "calc(60px + env(safe-area-inset-bottom))" }
2068
2029
  }
2069
2030
  ),
2070
- /* @__PURE__ */ jsxs13(
2031
+ /* @__PURE__ */ jsxs12(
2071
2032
  "nav",
2072
2033
  {
2073
2034
  "aria-label": ariaLabel,
2074
2035
  "data-test": dataTest,
2075
- className: cn(
2036
+ className: cn2(
2076
2037
  "suite-bottom-nav no-print z-40 grid auto-cols-fr grid-flow-col lg:hidden",
2077
2038
  className
2078
2039
  ),
@@ -2082,31 +2043,31 @@ function SuiteBottomNav({
2082
2043
  "--suite-nav-active": activeIndex >= 0 ? activeIndex : 0
2083
2044
  },
2084
2045
  children: [
2085
- activeIndex >= 0 ? /* @__PURE__ */ jsx15("span", { "aria-hidden": true, className: "suite-bottom-nav__indicator" }) : null,
2046
+ activeIndex >= 0 ? /* @__PURE__ */ jsx14("span", { "aria-hidden": true, className: "suite-bottom-nav__indicator" }) : null,
2086
2047
  items.map((item) => {
2087
2048
  var _a;
2088
2049
  const Icon = item.icon;
2089
2050
  const active = Boolean(item.active);
2090
- return /* @__PURE__ */ jsxs13(
2051
+ return /* @__PURE__ */ jsxs12(
2091
2052
  Link,
2092
2053
  {
2093
2054
  href: item.href,
2094
2055
  id: item.id,
2095
2056
  "data-test": (_a = item.dataTest) != null ? _a : `suite-nav-${item.label.toLowerCase().replace(/\s+/g, "-")}`,
2096
2057
  "aria-current": active ? "page" : void 0,
2097
- className: cn(
2058
+ className: cn2(
2098
2059
  "suite-bottom-nav__item relative flex min-h-[60px] min-w-0 flex-col items-center justify-center gap-1 px-1 pb-1.5 pt-2 text-[10px] font-medium leading-none text-ph-mutedtext transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ph-brand motion-reduce:transition-none",
2099
2060
  active && "font-semibold text-ph-brand"
2100
2061
  ),
2101
2062
  children: [
2102
- /* @__PURE__ */ jsx15(
2063
+ /* @__PURE__ */ jsx14(
2103
2064
  "span",
2104
2065
  {
2105
- className: cn(
2066
+ className: cn2(
2106
2067
  "suite-bottom-nav__icon relative z-10 inline-flex items-center justify-center",
2107
2068
  active && "suite-bottom-nav__icon--active"
2108
2069
  ),
2109
- children: /* @__PURE__ */ jsx15(
2070
+ children: /* @__PURE__ */ jsx14(
2110
2071
  Icon,
2111
2072
  {
2112
2073
  className: "h-6 w-6",
@@ -2116,7 +2077,7 @@ function SuiteBottomNav({
2116
2077
  )
2117
2078
  }
2118
2079
  ),
2119
- /* @__PURE__ */ jsx15("span", { className: "relative z-10 max-w-full truncate", children: item.label })
2080
+ /* @__PURE__ */ jsx14("span", { className: "relative z-10 max-w-full truncate", children: item.label })
2120
2081
  ]
2121
2082
  },
2122
2083
  item.href
@@ -2130,7 +2091,7 @@ function SuiteBottomNav({
2130
2091
 
2131
2092
  // src/suite/components/today-calibrating.tsx
2132
2093
  import { Refresh as RefreshCw2, SunLight as Sun } from "iconoir-react";
2133
- import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
2094
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
2134
2095
  function TodayCalibrating({
2135
2096
  onRetry,
2136
2097
  retrying = false,
@@ -2140,27 +2101,27 @@ function TodayCalibrating({
2140
2101
  retrySpinnerClassName = "text-ph-brand"
2141
2102
  }) {
2142
2103
  void _variant;
2143
- return /* @__PURE__ */ jsxs14(
2104
+ return /* @__PURE__ */ jsxs13(
2144
2105
  "div",
2145
2106
  {
2146
- className: cn(
2107
+ className: cn2(
2147
2108
  "suite-auth-handoff relative flex min-h-[14rem] flex-col items-center justify-center overflow-hidden rounded-2xl border border-ph-border bg-ph-surface px-5 py-8 text-center shadow-sm",
2148
2109
  className
2149
2110
  ),
2150
2111
  "data-test": "today-calibrating",
2151
2112
  children: [
2152
- /* @__PURE__ */ jsx16(
2113
+ /* @__PURE__ */ jsx15(
2153
2114
  "div",
2154
2115
  {
2155
2116
  className: "pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_50%_35%,rgba(251,191,36,0.1),transparent_55%)]",
2156
2117
  "aria-hidden": true
2157
2118
  }
2158
2119
  ),
2159
- /* @__PURE__ */ jsx16("div", { className: "relative mb-5 flex h-14 w-14 items-center justify-center rounded-2xl border border-ph-border bg-ph-surface text-ph-ink shadow-sm", children: /* @__PURE__ */ jsx16(Sun, { className: "h-8 w-8", strokeWidth: 2, "aria-hidden": true }) }),
2160
- /* @__PURE__ */ jsx16("h2", { className: "relative text-base font-semibold tracking-tight text-ph-ink sm:text-lg md:text-xl", children: "Calibrating your day" }),
2161
- /* @__PURE__ */ jsx16("p", { className: "relative mt-1.5 max-w-sm text-sm sm:text-[15px] leading-relaxed text-ph-subtle", children: "Pulling together tasks, time and updates from your workspace\u2026" }),
2162
- /* @__PURE__ */ jsx16("div", { className: "relative mt-5 flex justify-center", children: /* @__PURE__ */ jsx16(Spinner, { size: "sm", className: "opacity-70", label: "Loading today" }) }),
2163
- onRetry ? /* @__PURE__ */ jsxs14(
2120
+ /* @__PURE__ */ jsx15("div", { className: "relative mb-5 flex h-14 w-14 items-center justify-center rounded-2xl border border-ph-border bg-ph-surface text-ph-ink shadow-sm", children: /* @__PURE__ */ jsx15(Sun, { className: "h-8 w-8", strokeWidth: 2, "aria-hidden": true }) }),
2121
+ /* @__PURE__ */ jsx15("h2", { className: "relative text-base font-semibold tracking-tight text-ph-ink sm:text-lg md:text-xl", children: "Calibrating your day" }),
2122
+ /* @__PURE__ */ jsx15("p", { className: "relative mt-1.5 max-w-sm text-sm sm:text-[15px] leading-relaxed text-ph-subtle", children: "Pulling together tasks, time and updates from your workspace\u2026" }),
2123
+ /* @__PURE__ */ jsx15("div", { className: "relative mt-5 flex justify-center", children: /* @__PURE__ */ jsx15(Spinner, { size: "sm", className: "opacity-70", label: "Loading today" }) }),
2124
+ onRetry ? /* @__PURE__ */ jsxs13(
2164
2125
  "button",
2165
2126
  {
2166
2127
  type: "button",
@@ -2168,7 +2129,7 @@ function TodayCalibrating({
2168
2129
  disabled: retrying,
2169
2130
  className: "relative mt-6 inline-flex items-center gap-2 rounded-xl border border-ph-border bg-ph-canvas px-4 py-2 text-sm font-medium text-ph-ink transition hover:bg-ph-muted disabled:opacity-60",
2170
2131
  children: [
2171
- retrying ? /* @__PURE__ */ jsx16(Spinner, { size: "sm", className: retrySpinnerClassName }) : /* @__PURE__ */ jsx16(RefreshCw2, { className: "h-4 w-4", "aria-hidden": true }),
2132
+ retrying ? /* @__PURE__ */ jsx15(Spinner, { size: "sm", className: retrySpinnerClassName }) : /* @__PURE__ */ jsx15(RefreshCw2, { className: "h-4 w-4", "aria-hidden": true }),
2172
2133
  "Try again"
2173
2134
  ]
2174
2135
  }
@@ -2186,7 +2147,7 @@ import {
2186
2147
  useEffect as useEffect6,
2187
2148
  useId,
2188
2149
  useLayoutEffect,
2189
- useRef as useRef3,
2150
+ useRef as useRef2,
2190
2151
  useState as useState7
2191
2152
  } from "react";
2192
2153
  import {
@@ -2197,8 +2158,8 @@ import {
2197
2158
  Square,
2198
2159
  WarningTriangle
2199
2160
  } from "iconoir-react";
2200
- import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
2201
- var SuiteAiBlockNoteMessage = lazy(() => import("./ai-message-blocknote-WABTHFY7.mjs"));
2161
+ import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
2162
+ var SuiteAiBlockNoteMessage = lazy(() => import("./ai-message-blocknote-D4Z6DUCJ.mjs"));
2202
2163
  var SUITE_OPEN_ASK_AI_EVENT = "shellstack:open-ask-ai";
2203
2164
  var SUITE_ASK_AI_OPEN_KEY = "shellstack:shelly-open";
2204
2165
  var SUITE_ASK_AI_OPEN_QUERY = "shelly";
@@ -2280,7 +2241,8 @@ function SuiteAiPanel({
2280
2241
  suiteAppBases,
2281
2242
  onSwitchApp,
2282
2243
  onSameAppNavigate,
2283
- resolveAction
2244
+ resolveAction,
2245
+ isWaitingForReply = false
2284
2246
  }) {
2285
2247
  const inputId = useId();
2286
2248
  const [uncontrolledOpen, setUncontrolledOpen] = useState7(false);
@@ -2300,10 +2262,11 @@ function SuiteAiPanel({
2300
2262
  const [hydrating, setHydrating] = useState7(false);
2301
2263
  const [error, setError] = useState7("");
2302
2264
  const [atBottom, setAtBottom] = useState7(true);
2303
- const scrollerRef = useRef3(null);
2304
- const textareaRef = useRef3(null);
2305
- const abortRef = useRef3(null);
2306
- const pendingPromptRef = useRef3(null);
2265
+ const scrollerRef = useRef2(null);
2266
+ const textareaRef = useRef2(null);
2267
+ const abortRef = useRef2(null);
2268
+ const pendingPromptRef = useRef2(null);
2269
+ const waiting = loading || isWaitingForReply;
2307
2270
  useEffect6(() => {
2308
2271
  function onOpen(event) {
2309
2272
  var _a;
@@ -2331,7 +2294,7 @@ function SuiteAiPanel({
2331
2294
  setAtBottom(distance < 64);
2332
2295
  }, []);
2333
2296
  const hydrate = useCallback2(async () => {
2334
- var _a;
2297
+ var _a, _b, _c;
2335
2298
  setHydrating(true);
2336
2299
  setError("");
2337
2300
  try {
@@ -2339,6 +2302,11 @@ function SuiteAiPanel({
2339
2302
  setMessages((_a = data.messages) != null ? _a : []);
2340
2303
  if (!data.configured) {
2341
2304
  setError("ShellStack AI is not configured for this workspace yet.");
2305
+ } else {
2306
+ const latestJob = (_b = data.jobs) == null ? void 0 : _b[0];
2307
+ if ((latestJob == null ? void 0 : latestJob.status) === "failed" && latestJob.error && latestJob.error !== "Chat cleared." && ((_c = data.messages.at(-1)) == null ? void 0 : _c.role) !== "assistant") {
2308
+ setError(latestJob.error);
2309
+ }
2342
2310
  }
2343
2311
  } catch (err) {
2344
2312
  setError(
@@ -2361,7 +2329,7 @@ function SuiteAiPanel({
2361
2329
  }, [open, hydrate]);
2362
2330
  useEffect6(() => {
2363
2331
  if (open && atBottom) scrollToBottom();
2364
- }, [messages, loading, open, atBottom, scrollToBottom]);
2332
+ }, [messages, waiting, open, atBottom, scrollToBottom]);
2365
2333
  useEffect6(() => {
2366
2334
  var _a;
2367
2335
  if (open && !hydrating) (_a = textareaRef.current) == null ? void 0 : _a.focus();
@@ -2375,7 +2343,7 @@ function SuiteAiPanel({
2375
2343
  async function ask(text) {
2376
2344
  var _a;
2377
2345
  const trimmed = text.trim();
2378
- if (!trimmed || loading) return;
2346
+ if (!trimmed || waiting) return;
2379
2347
  const controller = new AbortController();
2380
2348
  abortRef.current = controller;
2381
2349
  setLoading(true);
@@ -2491,32 +2459,32 @@ function SuiteAiPanel({
2491
2459
  }
2492
2460
  if (!open) return null;
2493
2461
  const Icon = BrandIcon != null ? BrandIcon : Sparks;
2494
- return /* @__PURE__ */ jsxs15(
2462
+ return /* @__PURE__ */ jsxs14(
2495
2463
  "aside",
2496
2464
  {
2497
2465
  className: "flex h-full min-h-0 flex-col bg-ph-canvas",
2498
2466
  "data-test": "suite-ask-ai-panel",
2499
2467
  "aria-label": title,
2500
2468
  children: [
2501
- /* @__PURE__ */ jsxs15("header", { className: "flex items-center gap-2 px-2 pb-2", children: [
2502
- /* @__PURE__ */ jsx17("h2", { className: "sr-only", children: title }),
2503
- messages.length > 0 ? /* @__PURE__ */ jsxs15(
2469
+ /* @__PURE__ */ jsxs14("header", { className: "flex items-center gap-2 px-2 pb-2", children: [
2470
+ /* @__PURE__ */ jsx16("h2", { className: "sr-only", children: title }),
2471
+ messages.length > 0 ? /* @__PURE__ */ jsxs14(
2504
2472
  "button",
2505
2473
  {
2506
2474
  type: "button",
2507
2475
  onClick: () => void handleClear(),
2508
- disabled: loading,
2476
+ disabled: waiting,
2509
2477
  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",
2510
2478
  "data-test": "ask-ai-clear",
2511
2479
  children: [
2512
- /* @__PURE__ */ jsx17(Erase, { className: "h-3.5 w-3.5" }),
2480
+ /* @__PURE__ */ jsx16(Erase, { className: "h-3.5 w-3.5" }),
2513
2481
  "Clear"
2514
2482
  ]
2515
2483
  }
2516
2484
  ) : null
2517
2485
  ] }),
2518
- /* @__PURE__ */ jsxs15("div", { className: "relative min-h-0 flex-1", children: [
2519
- /* @__PURE__ */ jsxs15(
2486
+ /* @__PURE__ */ jsxs14("div", { className: "relative min-h-0 flex-1", children: [
2487
+ /* @__PURE__ */ jsxs14(
2520
2488
  "div",
2521
2489
  {
2522
2490
  ref: scrollerRef,
@@ -2527,25 +2495,25 @@ function SuiteAiPanel({
2527
2495
  "aria-label": "Conversation",
2528
2496
  className: "suite-scroll-lock h-full space-y-5 overflow-y-auto px-2 py-3",
2529
2497
  children: [
2530
- hydrating && messages.length === 0 ? /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-sm text-ph-subtle", children: [
2531
- /* @__PURE__ */ jsx17(Spinner, { className: "h-4 w-4 animate-spin" }),
2498
+ hydrating && messages.length === 0 ? /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2 text-sm text-ph-subtle", children: [
2499
+ /* @__PURE__ */ jsx16(Spinner, { className: "h-4 w-4 animate-spin" }),
2532
2500
  "Loading your chat\u2026"
2533
2501
  ] }) : null,
2534
- !hydrating && messages.length === 0 && !error ? /* @__PURE__ */ jsxs15("div", { className: "mx-auto mt-4 flex max-w-sm flex-col items-center px-1 text-center", children: [
2535
- /* @__PURE__ */ jsx17("span", { className: "flex h-10 w-10 items-center justify-center rounded-2xl bg-ph-brand/10 text-ph-brand", children: /* @__PURE__ */ jsx17(Icon, { className: "h-5 w-5" }) }),
2536
- /* @__PURE__ */ jsx17("p", { className: "mt-3 font-display text-base font-semibold text-ph-ink", children: "How can I help?" }),
2537
- /* @__PURE__ */ jsx17("p", { className: "mt-1 text-sm leading-relaxed text-ph-subtle", children: emptyState }),
2538
- presets.length > 0 ? /* @__PURE__ */ jsx17("div", { className: "mt-4 flex w-full flex-col gap-2", children: presets.map((preset) => /* @__PURE__ */ jsxs15(
2502
+ !hydrating && messages.length === 0 && !error ? /* @__PURE__ */ jsxs14("div", { className: "mx-auto mt-4 flex max-w-sm flex-col items-center px-1 text-center", children: [
2503
+ /* @__PURE__ */ jsx16("span", { className: "flex h-10 w-10 items-center justify-center rounded-2xl bg-ph-brand/10 text-ph-brand", children: /* @__PURE__ */ jsx16(Icon, { className: "h-5 w-5" }) }),
2504
+ /* @__PURE__ */ jsx16("p", { className: "mt-3 font-display text-base font-semibold text-ph-ink", children: "How can I help?" }),
2505
+ /* @__PURE__ */ jsx16("p", { className: "mt-1 text-sm leading-relaxed text-ph-subtle", children: emptyState }),
2506
+ presets.length > 0 ? /* @__PURE__ */ jsx16("div", { className: "mt-4 flex w-full flex-col gap-2", children: presets.map((preset) => /* @__PURE__ */ jsxs14(
2539
2507
  "button",
2540
2508
  {
2541
2509
  type: "button",
2542
2510
  onClick: () => void ask(preset.prompt),
2543
- disabled: loading || hydrating,
2511
+ disabled: waiting || hydrating,
2544
2512
  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",
2545
2513
  children: [
2546
- /* @__PURE__ */ jsx17(Sparks, { className: "h-4 w-4 shrink-0 text-ph-brand" }),
2547
- /* @__PURE__ */ jsx17("span", { className: "min-w-0 flex-1 truncate", children: preset.label }),
2548
- /* @__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" })
2514
+ /* @__PURE__ */ jsx16(Sparks, { className: "h-4 w-4 shrink-0 text-ph-brand" }),
2515
+ /* @__PURE__ */ jsx16("span", { className: "min-w-0 flex-1 truncate", children: preset.label }),
2516
+ /* @__PURE__ */ jsx16(ArrowUp, { className: "h-3.5 w-3.5 shrink-0 rotate-45 text-ph-mutedtext transition-transform group-hover:translate-x-0.5" })
2549
2517
  ]
2550
2518
  },
2551
2519
  preset.label
@@ -2555,48 +2523,61 @@ function SuiteAiPanel({
2555
2523
  var _a;
2556
2524
  const isUser = message.role === "user";
2557
2525
  if (isUser) {
2558
- return /* @__PURE__ */ jsx17(
2526
+ return /* @__PURE__ */ jsx16(
2559
2527
  "div",
2560
2528
  {
2561
2529
  className: "flex justify-end",
2562
2530
  "data-test": "ask-ai-user",
2563
- children: /* @__PURE__ */ jsx17("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 })
2531
+ children: /* @__PURE__ */ jsx16("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 })
2564
2532
  },
2565
2533
  `user-${index}`
2566
2534
  );
2567
2535
  }
2568
- return /* @__PURE__ */ jsxs15(
2536
+ return /* @__PURE__ */ jsxs14(
2569
2537
  "div",
2570
2538
  {
2571
2539
  className: "flex items-end gap-2",
2572
2540
  "data-test": "ask-ai-assistant",
2573
2541
  children: [
2574
- /* @__PURE__ */ jsx17(
2542
+ /* @__PURE__ */ jsx16(
2575
2543
  "span",
2576
2544
  {
2577
2545
  className: "mb-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-ph-brand/10 text-ph-brand",
2578
2546
  "aria-hidden": true,
2579
- children: /* @__PURE__ */ jsx17(Icon, { className: "h-3.5 w-3.5" })
2547
+ children: /* @__PURE__ */ jsx16(Icon, { className: "h-3.5 w-3.5" })
2580
2548
  }
2581
2549
  ),
2582
- /* @__PURE__ */ jsxs15("div", { className: "min-w-0 max-w-[85%] space-y-2", children: [
2583
- /* @__PURE__ */ jsx17("div", { className: "rounded-2xl rounded-bl-md bg-ph-muted px-3 py-2 text-sm leading-relaxed text-ph-ink", children: /* @__PURE__ */ jsx17(
2550
+ /* @__PURE__ */ jsxs14("div", { className: "min-w-0 max-w-[85%] space-y-2", children: [
2551
+ /* @__PURE__ */ jsx16("div", { className: "rounded-2xl rounded-bl-md bg-ph-muted px-3 py-2 text-sm leading-relaxed text-ph-ink", children: /* @__PURE__ */ jsx16(
2584
2552
  Suspense,
2585
2553
  {
2586
- fallback: /* @__PURE__ */ jsx17("p", { className: "whitespace-pre-wrap break-words", children: message.content }),
2587
- children: /* @__PURE__ */ jsx17(SuiteAiBlockNoteMessage, { markdown: message.content })
2554
+ fallback: /* @__PURE__ */ jsxs14(
2555
+ "div",
2556
+ {
2557
+ className: "space-y-2 py-0.5",
2558
+ role: "status",
2559
+ "aria-label": "Rendering response",
2560
+ children: [
2561
+ /* @__PURE__ */ jsx16("span", { className: "sr-only", children: "Rendering response\u2026" }),
2562
+ /* @__PURE__ */ jsx16("div", { className: "h-3 w-11/12 rounded bg-ph-mutedtext/20 motion-safe:animate-pulse" }),
2563
+ /* @__PURE__ */ jsx16("div", { className: "h-3 w-full rounded bg-ph-mutedtext/20 motion-safe:animate-pulse" }),
2564
+ /* @__PURE__ */ jsx16("div", { className: "h-3 w-3/5 rounded bg-ph-mutedtext/20 motion-safe:animate-pulse" })
2565
+ ]
2566
+ }
2567
+ ),
2568
+ children: /* @__PURE__ */ jsx16(SuiteAiBlockNoteMessage, { markdown: message.content })
2588
2569
  }
2589
2570
  ) }),
2590
- ((_a = message.actions) != null ? _a : []).map((action) => /* @__PURE__ */ jsxs15(
2571
+ ((_a = message.actions) != null ? _a : []).map((action) => /* @__PURE__ */ jsxs14(
2591
2572
  "div",
2592
2573
  {
2593
2574
  "data-test": "ask-ai-action",
2594
2575
  "data-status": action.status,
2595
2576
  className: "rounded-xl border border-ph-border bg-ph-surface px-3 py-2 text-sm text-ph-ink",
2596
2577
  children: [
2597
- /* @__PURE__ */ jsx17("p", { className: "font-medium", children: action.summary }),
2598
- action.status === "proposed" ? /* @__PURE__ */ jsxs15("div", { className: "mt-2 flex flex-wrap gap-2", children: [
2599
- /* @__PURE__ */ jsx17(
2578
+ /* @__PURE__ */ jsx16("p", { className: "font-medium", children: action.summary }),
2579
+ action.status === "proposed" ? /* @__PURE__ */ jsxs14("div", { className: "mt-2 flex flex-wrap gap-2", children: [
2580
+ /* @__PURE__ */ jsx16(
2600
2581
  "button",
2601
2582
  {
2602
2583
  type: "button",
@@ -2607,7 +2588,7 @@ function SuiteAiPanel({
2607
2588
  children: resolvingId === action.id ? "Working\u2026" : "Confirm"
2608
2589
  }
2609
2590
  ),
2610
- /* @__PURE__ */ jsx17(
2591
+ /* @__PURE__ */ jsx16(
2611
2592
  "button",
2612
2593
  {
2613
2594
  type: "button",
@@ -2619,11 +2600,11 @@ function SuiteAiPanel({
2619
2600
  }
2620
2601
  )
2621
2602
  ] }) : null,
2622
- action.status === "applied" ? /* @__PURE__ */ jsxs15("p", { className: "mt-1 text-xs text-ph-subtle", children: [
2603
+ action.status === "applied" ? /* @__PURE__ */ jsxs14("p", { className: "mt-1 text-xs text-ph-subtle", children: [
2623
2604
  "Done",
2624
- action.href ? /* @__PURE__ */ jsxs15(Fragment5, { children: [
2605
+ action.href ? /* @__PURE__ */ jsxs14(Fragment5, { children: [
2625
2606
  " \xB7 ",
2626
- /* @__PURE__ */ jsx17(
2607
+ /* @__PURE__ */ jsx16(
2627
2608
  "a",
2628
2609
  {
2629
2610
  href: action.href,
@@ -2633,8 +2614,8 @@ function SuiteAiPanel({
2633
2614
  )
2634
2615
  ] }) : null
2635
2616
  ] }) : null,
2636
- action.status === "cancelled" ? /* @__PURE__ */ jsx17("p", { className: "mt-1 text-xs text-ph-subtle", children: "Cancelled" }) : null,
2637
- action.status === "failed" ? /* @__PURE__ */ jsx17("p", { className: "mt-1 text-xs text-ph-danger", children: action.error || "Could not apply that." }) : null
2617
+ action.status === "cancelled" ? /* @__PURE__ */ jsx16("p", { className: "mt-1 text-xs text-ph-subtle", children: "Cancelled" }) : null,
2618
+ action.status === "failed" ? /* @__PURE__ */ jsx16("p", { className: "mt-1 text-xs text-ph-danger", children: action.error || "Could not apply that." }) : null
2638
2619
  ]
2639
2620
  },
2640
2621
  action.id
@@ -2645,31 +2626,31 @@ function SuiteAiPanel({
2645
2626
  `assistant-${index}`
2646
2627
  );
2647
2628
  }),
2648
- loading ? /* @__PURE__ */ jsxs15("div", { className: "flex items-end gap-2", "aria-hidden": true, children: [
2649
- /* @__PURE__ */ jsx17("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__ */ jsx17(Icon, { className: "h-3.5 w-3.5" }) }),
2650
- /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-1.5 rounded-2xl rounded-bl-md bg-ph-muted px-3 py-2.5", children: [
2651
- /* @__PURE__ */ jsx17("span", { className: "h-1.5 w-1.5 rounded-full bg-ph-mutedtext motion-safe:animate-bounce" }),
2652
- /* @__PURE__ */ jsx17("span", { className: "h-1.5 w-1.5 rounded-full bg-ph-mutedtext motion-safe:animate-bounce [animation-delay:0.15s]" }),
2653
- /* @__PURE__ */ jsx17("span", { className: "h-1.5 w-1.5 rounded-full bg-ph-mutedtext motion-safe:animate-bounce [animation-delay:0.3s]" })
2629
+ waiting ? /* @__PURE__ */ jsxs14("div", { className: "flex items-end gap-2", "aria-hidden": true, children: [
2630
+ /* @__PURE__ */ jsx16("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__ */ jsx16(Icon, { className: "h-3.5 w-3.5" }) }),
2631
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1.5 rounded-2xl rounded-bl-md bg-ph-muted px-3 py-2.5", children: [
2632
+ /* @__PURE__ */ jsx16("span", { className: "h-1.5 w-1.5 rounded-full bg-ph-mutedtext motion-safe:animate-bounce" }),
2633
+ /* @__PURE__ */ jsx16("span", { className: "h-1.5 w-1.5 rounded-full bg-ph-mutedtext motion-safe:animate-bounce [animation-delay:0.15s]" }),
2634
+ /* @__PURE__ */ jsx16("span", { className: "h-1.5 w-1.5 rounded-full bg-ph-mutedtext motion-safe:animate-bounce [animation-delay:0.3s]" })
2654
2635
  ] })
2655
2636
  ] }) : null,
2656
- loading ? /* @__PURE__ */ jsx17("span", { className: "sr-only", "aria-live": "polite", children: "Assistant is responding\u2026" }) : null,
2657
- error ? /* @__PURE__ */ jsxs15(
2637
+ waiting ? /* @__PURE__ */ jsx16("span", { className: "sr-only", "aria-live": "polite", children: "Assistant is responding\u2026" }) : null,
2638
+ error ? /* @__PURE__ */ jsxs14(
2658
2639
  "div",
2659
2640
  {
2660
2641
  role: "alert",
2661
2642
  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",
2662
2643
  children: [
2663
- /* @__PURE__ */ jsx17(WarningTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-ph-danger" }),
2664
- /* @__PURE__ */ jsxs15("div", { className: "min-w-0 flex-1", children: [
2665
- /* @__PURE__ */ jsx17("p", { className: "font-medium text-ph-danger", children: "Something went wrong" }),
2666
- /* @__PURE__ */ jsx17("p", { className: "mt-0.5 text-ph-subtle", children: error }),
2667
- /* @__PURE__ */ jsx17(
2644
+ /* @__PURE__ */ jsx16(WarningTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-ph-danger" }),
2645
+ /* @__PURE__ */ jsxs14("div", { className: "min-w-0 flex-1", children: [
2646
+ /* @__PURE__ */ jsx16("p", { className: "font-medium text-ph-danger", children: "Something went wrong" }),
2647
+ /* @__PURE__ */ jsx16("p", { className: "mt-0.5 text-ph-subtle", children: error }),
2648
+ /* @__PURE__ */ jsx16(
2668
2649
  "button",
2669
2650
  {
2670
2651
  type: "button",
2671
2652
  onClick: retry,
2672
- disabled: loading,
2653
+ disabled: waiting,
2673
2654
  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",
2674
2655
  children: "Try again"
2675
2656
  }
@@ -2681,20 +2662,20 @@ function SuiteAiPanel({
2681
2662
  ]
2682
2663
  }
2683
2664
  ),
2684
- !atBottom ? /* @__PURE__ */ jsx17("div", { className: "absolute bottom-3 left-1/2 -translate-x-1/2", children: /* @__PURE__ */ jsx17(Tooltip, { content: "Scroll to latest", children: /* @__PURE__ */ jsx17(
2665
+ !atBottom ? /* @__PURE__ */ jsx16("div", { className: "absolute bottom-3 left-1/2 -translate-x-1/2", children: /* @__PURE__ */ jsx16(Tooltip, { content: "Scroll to latest", children: /* @__PURE__ */ jsx16(
2685
2666
  "button",
2686
2667
  {
2687
2668
  type: "button",
2688
2669
  onClick: () => scrollToBottom("smooth"),
2689
2670
  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",
2690
2671
  "aria-label": "Scroll to latest",
2691
- children: /* @__PURE__ */ jsx17(ArrowDown, { className: "h-4 w-4" })
2672
+ children: /* @__PURE__ */ jsx16(ArrowDown, { className: "h-4 w-4" })
2692
2673
  }
2693
2674
  ) }) }) : null
2694
2675
  ] }),
2695
- /* @__PURE__ */ jsx17("div", { className: "px-2 pt-2 pb-[max(2rem,calc(env(safe-area-inset-bottom)+1rem))]", children: /* @__PURE__ */ jsxs15("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: [
2696
- /* @__PURE__ */ jsx17("label", { htmlFor: inputId, className: "sr-only", children: "Ask a question" }),
2697
- /* @__PURE__ */ jsx17(
2676
+ /* @__PURE__ */ jsx16("div", { className: "px-2 pt-2 pb-[max(2rem,calc(env(safe-area-inset-bottom)+1rem))]", children: /* @__PURE__ */ jsxs14("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: [
2677
+ /* @__PURE__ */ jsx16("label", { htmlFor: inputId, className: "sr-only", children: "Ask a question" }),
2678
+ /* @__PURE__ */ jsx16(
2698
2679
  "textarea",
2699
2680
  {
2700
2681
  id: inputId,
@@ -2713,7 +2694,7 @@ function SuiteAiPanel({
2713
2694
  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"
2714
2695
  }
2715
2696
  ),
2716
- loading ? /* @__PURE__ */ jsx17(Tooltip, { content: "Stop generating", children: /* @__PURE__ */ jsx17(
2697
+ loading ? /* @__PURE__ */ jsx16(Tooltip, { content: "Stop generating", children: /* @__PURE__ */ jsx16(
2717
2698
  "button",
2718
2699
  {
2719
2700
  type: "button",
@@ -2721,9 +2702,18 @@ function SuiteAiPanel({
2721
2702
  "data-test": "ask-ai-stop",
2722
2703
  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",
2723
2704
  "aria-label": "Stop generating",
2724
- children: /* @__PURE__ */ jsx17(Square, { className: "h-3.5 w-3.5 fill-current" })
2705
+ children: /* @__PURE__ */ jsx16(Square, { className: "h-3.5 w-3.5 fill-current" })
2706
+ }
2707
+ ) }) : isWaitingForReply ? /* @__PURE__ */ jsx16(
2708
+ "span",
2709
+ {
2710
+ className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-ph-muted text-ph-subtle",
2711
+ "data-test": "ask-ai-waiting",
2712
+ role: "status",
2713
+ "aria-label": "Shelly is responding",
2714
+ children: /* @__PURE__ */ jsx16(Spinner, { className: "h-4 w-4 animate-spin" })
2725
2715
  }
2726
- ) }) : /* @__PURE__ */ jsx17(Tooltip, { content: "Send message", children: /* @__PURE__ */ jsx17(
2716
+ ) : /* @__PURE__ */ jsx16(Tooltip, { content: "Send message", children: /* @__PURE__ */ jsx16(
2727
2717
  "button",
2728
2718
  {
2729
2719
  type: "button",
@@ -2732,7 +2722,7 @@ function SuiteAiPanel({
2732
2722
  "data-test": "ask-ai-send",
2733
2723
  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",
2734
2724
  "aria-label": "Send message",
2735
- children: /* @__PURE__ */ jsx17(ArrowUp, { className: "h-4 w-4" })
2725
+ children: /* @__PURE__ */ jsx16(ArrowUp, { className: "h-4 w-4" })
2736
2726
  }
2737
2727
  ) })
2738
2728
  ] }) })
@@ -2744,7 +2734,7 @@ function SuiteAiPanel({
2744
2734
  // src/suite/components/workspace-switcher.tsx
2745
2735
  import { useState as useState8 } from "react";
2746
2736
  import { Building, Check, NavArrowDown } from "iconoir-react";
2747
- import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
2737
+ import { Fragment as Fragment6, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
2748
2738
  function SuiteWorkspaceSwitcher({
2749
2739
  workspaces = [],
2750
2740
  groups,
@@ -2762,7 +2752,7 @@ function SuiteWorkspaceSwitcher({
2762
2752
  var _a;
2763
2753
  const [open, setOpen] = useState8(false);
2764
2754
  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 }] : [];
2765
- return /* @__PURE__ */ jsx18(
2755
+ return /* @__PURE__ */ jsx17(
2766
2756
  DropdownMenu2,
2767
2757
  {
2768
2758
  "aria-label": "Switch workspace",
@@ -2773,24 +2763,23 @@ function SuiteWorkspaceSwitcher({
2773
2763
  align: "start",
2774
2764
  open,
2775
2765
  onOpenChange: setOpen,
2776
- trigger: /* @__PURE__ */ jsxs16(
2766
+ trigger: /* @__PURE__ */ jsxs15(
2777
2767
  "span",
2778
2768
  {
2779
- className: cn(
2769
+ className: cn2(
2780
2770
  "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",
2781
2771
  collapsed ? "h-10 w-10 justify-center p-0" : "w-full px-3 py-2",
2782
2772
  open && "border-ph-brand/40"
2783
2773
  ),
2784
2774
  "data-test": "workspace-switcher-trigger",
2785
- "aria-expanded": open,
2786
2775
  children: [
2787
- /* @__PURE__ */ jsx18(Building, { className: "h-4 w-4 shrink-0 text-ph-ink", "aria-hidden": true }),
2788
- !collapsed ? /* @__PURE__ */ jsxs16(Fragment6, { children: [
2789
- /* @__PURE__ */ jsx18("span", { className: "min-w-0 flex-1 truncate text-sm font-semibold text-ph-ink", children: name }),
2790
- /* @__PURE__ */ jsx18(
2776
+ /* @__PURE__ */ jsx17(Building, { className: "h-4 w-4 shrink-0 text-ph-ink", "aria-hidden": true }),
2777
+ !collapsed ? /* @__PURE__ */ jsxs15(Fragment6, { children: [
2778
+ /* @__PURE__ */ jsx17("span", { className: "min-w-0 flex-1 truncate text-sm font-semibold text-ph-ink", children: name }),
2779
+ /* @__PURE__ */ jsx17(
2791
2780
  NavArrowDown,
2792
2781
  {
2793
- className: cn(
2782
+ className: cn2(
2794
2783
  "h-3.5 w-3.5 shrink-0 text-ph-mutedtext transition-transform",
2795
2784
  open && "rotate-180"
2796
2785
  ),
@@ -2801,21 +2790,20 @@ function SuiteWorkspaceSwitcher({
2801
2790
  ]
2802
2791
  }
2803
2792
  ),
2804
- children: /* @__PURE__ */ jsxs16(
2793
+ children: /* @__PURE__ */ jsxs15(
2805
2794
  "div",
2806
2795
  {
2807
2796
  id: "workspace-switcher-menu",
2808
2797
  className: "p-1",
2809
2798
  "data-test": "workspace-switcher-menu",
2810
2799
  children: [
2811
- sections.length === 0 ? /* @__PURE__ */ jsx18("p", { className: "px-3 py-2 text-sm text-ph-mutedtext", children: emptyLabel }) : sections.map((section) => /* @__PURE__ */ jsxs16("div", { children: [
2812
- /* @__PURE__ */ jsx18("p", { className: "px-3 py-2 text-[10px] font-semibold uppercase tracking-[0.14em] text-ph-mutedtext", children: section.label }),
2813
- section.workspaces.map((workspace) => /* @__PURE__ */ jsx18(
2800
+ sections.length === 0 ? /* @__PURE__ */ jsx17("p", { className: "px-3 py-2 text-sm text-ph-mutedtext", children: emptyLabel }) : sections.map((section) => /* @__PURE__ */ jsxs15("div", { children: [
2801
+ /* @__PURE__ */ jsx17("p", { className: "px-3 py-2 text-[10px] font-semibold uppercase tracking-[0.14em] text-ph-mutedtext", children: section.label }),
2802
+ section.workspaces.map((workspace) => /* @__PURE__ */ jsx17(
2814
2803
  DropdownItem2,
2815
2804
  {
2816
2805
  id: `workspace-option-${workspace.id}`,
2817
- role: "option",
2818
- "aria-selected": workspace.id === currentId,
2806
+ "aria-current": workspace.id === currentId ? "true" : void 0,
2819
2807
  "data-test": "workspace-option",
2820
2808
  "data-workspace-id": workspace.id,
2821
2809
  onMouseDown: (event) => event.preventDefault(),
@@ -2824,12 +2812,12 @@ function SuiteWorkspaceSwitcher({
2824
2812
  onSelect(workspace);
2825
2813
  },
2826
2814
  className: appSwitcherMenuItemClass(),
2827
- children: /* @__PURE__ */ jsxs16("span", { className: "flex min-w-0 w-full items-center gap-3", children: [
2828
- /* @__PURE__ */ jsxs16("span", { className: "min-w-0 flex-1", children: [
2829
- /* @__PURE__ */ jsx18("span", { className: "block truncate text-sm font-medium text-ph-ink", children: workspace.name }),
2830
- workspace.description ? /* @__PURE__ */ jsx18("span", { className: "block truncate text-xs text-ph-mutedtext", children: workspace.description }) : null
2815
+ children: /* @__PURE__ */ jsxs15("span", { className: "flex min-w-0 w-full items-center gap-3", children: [
2816
+ /* @__PURE__ */ jsxs15("span", { className: "min-w-0 flex-1", children: [
2817
+ /* @__PURE__ */ jsx17("span", { className: "block truncate text-sm font-medium text-ph-ink", children: workspace.name }),
2818
+ workspace.description ? /* @__PURE__ */ jsx17("span", { className: "block truncate text-xs text-ph-mutedtext", children: workspace.description }) : null
2831
2819
  ] }),
2832
- workspace.id === currentId ? /* @__PURE__ */ jsx18(
2820
+ workspace.id === currentId ? /* @__PURE__ */ jsx17(
2833
2821
  Check,
2834
2822
  {
2835
2823
  className: "h-4 w-4 shrink-0 text-ph-brand",
@@ -2841,7 +2829,7 @@ function SuiteWorkspaceSwitcher({
2841
2829
  workspace.id
2842
2830
  ))
2843
2831
  ] }, section.id)),
2844
- footer ? /* @__PURE__ */ jsx18("div", { className: "mt-1 border-t border-ph-border pt-1", children: footer }) : null
2832
+ footer ? /* @__PURE__ */ jsx17("div", { className: "mt-1 border-t border-ph-border pt-1", children: footer }) : null
2845
2833
  ]
2846
2834
  }
2847
2835
  )
@@ -2849,6 +2837,63 @@ function SuiteWorkspaceSwitcher({
2849
2837
  );
2850
2838
  }
2851
2839
 
2840
+ // src/suite/icons/suite-icon.tsx
2841
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
2842
+ var TODAY_SUN_STYLE = `
2843
+ @media (prefers-reduced-motion: no-preference) {
2844
+ .suite-icon-today-sun-rays {
2845
+ animation: suite-icon-today-sun-spin 18s linear infinite;
2846
+ transform-box: fill-box;
2847
+ transform-origin: center;
2848
+ }
2849
+ }
2850
+ @keyframes suite-icon-today-sun-spin {
2851
+ to { transform: rotate(360deg); }
2852
+ }
2853
+ `;
2854
+ function SuiteIcon({
2855
+ name,
2856
+ size = 24,
2857
+ accent,
2858
+ strokeWidth = 2,
2859
+ animated = true,
2860
+ label,
2861
+ className,
2862
+ style
2863
+ }) {
2864
+ const glyph = SUITE_GLYPHS[name];
2865
+ const accentHex = accent ? APP_ACCENTS[accent].accent : void 0;
2866
+ const mergedStyle = accentHex ? __spreadProps(__spreadValues({}, style), { "--suite-icon-accent": accentHex }) : style;
2867
+ return /* @__PURE__ */ jsxs16(
2868
+ "svg",
2869
+ {
2870
+ width: size,
2871
+ height: size,
2872
+ viewBox: "0 0 24 24",
2873
+ fill: "none",
2874
+ stroke: "currentColor",
2875
+ strokeWidth,
2876
+ strokeLinecap: "round",
2877
+ strokeLinejoin: "round",
2878
+ className: cn2("shrink-0", className),
2879
+ style: mergedStyle,
2880
+ role: label ? "img" : void 0,
2881
+ "aria-label": label,
2882
+ "aria-hidden": label ? void 0 : true,
2883
+ children: [
2884
+ glyph.animatedAccent && animated ? /* @__PURE__ */ jsx18("style", { children: TODAY_SUN_STYLE }) : null,
2885
+ /* @__PURE__ */ jsx18(
2886
+ GlyphParts,
2887
+ {
2888
+ elements: glyph.elements,
2889
+ accentClassName: glyph.animatedAccent && animated ? "suite-icon-today-sun-rays" : void 0
2890
+ }
2891
+ )
2892
+ ]
2893
+ }
2894
+ );
2895
+ }
2896
+
2852
2897
  // src/suite/lib/use-sidebar-width.ts
2853
2898
  import {
2854
2899
  useCallback as useCallback3,
@@ -2866,8 +2911,7 @@ var LEGACY_SIDEBAR_WIDTH_KEYS = [
2866
2911
  "tides-sidebar-width",
2867
2912
  "tt-sidebar-width",
2868
2913
  "kraken-sidebar-width",
2869
- "shellstack-sidebar-width",
2870
- "nakama-sidebar-width"
2914
+ "shellstack-sidebar-width"
2871
2915
  ];
2872
2916
  function isSuiteSidebarKey(storageKey) {
2873
2917
  return storageKey === SUITE_SIDEBAR_WIDTH_KEY || LEGACY_SIDEBAR_WIDTH_KEYS.includes(
@@ -2875,6 +2919,7 @@ function isSuiteSidebarKey(storageKey) {
2875
2919
  );
2876
2920
  }
2877
2921
  function sidebarColumnWidth(width, collapsed, _chatOpen = false) {
2922
+ void _chatOpen;
2878
2923
  if (collapsed) return SIDEBAR_RAIL_WIDTH;
2879
2924
  return width;
2880
2925
  }
@@ -2891,50 +2936,6 @@ function parseWidth(raw) {
2891
2936
  const parsed = Number.parseInt(raw, 10);
2892
2937
  return Number.isNaN(parsed) ? null : parsed;
2893
2938
  }
2894
- function suiteCookieDomainAttr() {
2895
- var _a;
2896
- const host = window.location.hostname;
2897
- if (host === "localhost" || /^\d{1,3}(?:\.\d{1,3}){3}$/.test(host)) {
2898
- return "";
2899
- }
2900
- if (host.endsWith(".localhost")) {
2901
- return "; Domain=.localhost";
2902
- }
2903
- const known = /* @__PURE__ */ new Set([
2904
- "tides",
2905
- "kraken",
2906
- "turtletime",
2907
- "time",
2908
- "nakama",
2909
- "app",
2910
- "shellstack",
2911
- "portal"
2912
- ]);
2913
- const parts = host.split(".");
2914
- if (parts.length >= 3 && known.has((_a = parts[0]) != null ? _a : "")) {
2915
- return `; Domain=.${parts.slice(1).join(".")}`;
2916
- }
2917
- if (parts.length >= 2) return `; Domain=.${parts.slice(1).join(".")}`;
2918
- return "";
2919
- }
2920
- function readCookie(name) {
2921
- const prefix = `${name}=`;
2922
- for (const part of document.cookie.split(";")) {
2923
- const trimmed = part.trim();
2924
- if (trimmed.startsWith(prefix)) {
2925
- return decodeURIComponent(trimmed.slice(prefix.length));
2926
- }
2927
- }
2928
- return null;
2929
- }
2930
- function writeCookie(name, value) {
2931
- const secure = window.location.protocol === "https:" ? "; Secure" : "";
2932
- document.cookie = `${name}=${encodeURIComponent(value)}; Path=/; Max-Age=31536000; SameSite=Lax${suiteCookieDomainAttr()}${secure}`;
2933
- }
2934
- function clearCookie(name) {
2935
- const secure = window.location.protocol === "https:" ? "; Secure" : "";
2936
- document.cookie = `${name}=; Path=/; Max-Age=0; SameSite=Lax${suiteCookieDomainAttr()}${secure}`;
2937
- }
2938
2939
  function readSidebarWidth(storageKey) {
2939
2940
  try {
2940
2941
  if (isSuiteSidebarKey(storageKey)) {
@@ -3090,7 +3091,7 @@ function SuiteSkeleton({
3090
3091
  return /* @__PURE__ */ jsx20(
3091
3092
  "div",
3092
3093
  {
3093
- className: cn("space-y-2", circle ? "flex flex-col items-center" : ""),
3094
+ className: cn2("space-y-2", circle ? "flex flex-col items-center" : ""),
3094
3095
  "aria-hidden": true,
3095
3096
  children: Array.from({ length: lines }).map((_, index) => /* @__PURE__ */ jsx20(
3096
3097
  "div",
@@ -3100,7 +3101,7 @@ function SuiteSkeleton({
3100
3101
  children: /* @__PURE__ */ jsx20(
3101
3102
  "div",
3102
3103
  {
3103
- className: cn(
3104
+ className: cn2(
3104
3105
  "ph-skeleton",
3105
3106
  circle ? "h-10 w-10 rounded-full" : "h-3",
3106
3107
  className
@@ -3124,7 +3125,7 @@ function SuiteSkeletonCard({
3124
3125
  return /* @__PURE__ */ jsxs17(
3125
3126
  "div",
3126
3127
  {
3127
- className: cn(
3128
+ className: cn2(
3128
3129
  "ph-skeleton-card animate-[skeleton-stagger_0.5s_ease-out_both] rounded-2xl border border-ph-border bg-ph-surface p-6 shadow-sm",
3129
3130
  className
3130
3131
  ),
@@ -3167,7 +3168,7 @@ function SuiteSkeletonList({
3167
3168
  className,
3168
3169
  items = 4
3169
3170
  }) {
3170
- return /* @__PURE__ */ jsx20("div", { className: cn("space-y-3", className), "aria-hidden": true, children: Array.from({ length: items }).map((_, index) => /* @__PURE__ */ jsx20(
3171
+ return /* @__PURE__ */ jsx20("div", { className: cn2("space-y-3", className), "aria-hidden": true, children: Array.from({ length: items }).map((_, index) => /* @__PURE__ */ jsx20(
3171
3172
  "div",
3172
3173
  {
3173
3174
  className: "animate-[skeleton-stagger_0.6s_ease-out_both]",
@@ -3194,7 +3195,7 @@ function SuiteEmptyState({
3194
3195
  return /* @__PURE__ */ jsxs17(
3195
3196
  "div",
3196
3197
  {
3197
- className: cn(
3198
+ className: cn2(
3198
3199
  "relative overflow-hidden rounded-2xl border border-ph-border bg-ph-surface text-center",
3199
3200
  size === "sm" ? "px-5 py-8" : "px-6 py-12 sm:px-10 sm:py-16",
3200
3201
  className
@@ -3207,9 +3208,9 @@ function SuiteEmptyState({
3207
3208
  /* @__PURE__ */ jsxs17("div", { className: "relative", children: [
3208
3209
  icon ? /* @__PURE__ */ jsx20("div", { className: "mx-auto mb-4 inline-flex items-center justify-center rounded-2xl bg-ph-muted p-3 shadow-sm ring-1 ring-black/[0.06]", children: icon }) : null,
3209
3210
  /* @__PURE__ */ jsx20(
3210
- "h3",
3211
+ "h2",
3211
3212
  {
3212
- className: cn(
3213
+ className: cn2(
3213
3214
  "font-semibold text-ph-ink",
3214
3215
  size === "sm" ? "text-base" : "text-lg"
3215
3216
  ),
@@ -3240,14 +3241,14 @@ function SuiteSettingsMobileNav({
3240
3241
  {
3241
3242
  "data-test": dataTest,
3242
3243
  "aria-label": ariaLabel,
3243
- className: cn("suite-settings-mobile-nav lg:hidden", className),
3244
+ className: cn2("suite-settings-mobile-nav lg:hidden", className),
3244
3245
  children: /* @__PURE__ */ jsx21("div", { className: "flex snap-x gap-2 overflow-x-auto pb-3 pt-1 scrollbar-hide [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: items.map((item) => {
3245
3246
  const Icon = item.icon;
3246
3247
  const active = activeHref === item.href || activeHref.startsWith(`${item.href}/`);
3247
3248
  const iconNode = isValidElement(item.icon) ? item.icon : /* @__PURE__ */ jsx21(
3248
3249
  Icon,
3249
3250
  {
3250
- className: cn(
3251
+ className: cn2(
3251
3252
  "h-4 w-4 shrink-0 transition",
3252
3253
  active ? "text-ph-surface" : "text-ph-mutedtext"
3253
3254
  ),
@@ -3262,7 +3263,7 @@ function SuiteSettingsMobileNav({
3262
3263
  "data-test": item.testId,
3263
3264
  onClick: () => onSelect(item.href),
3264
3265
  "aria-current": active ? "page" : void 0,
3265
- className: cn(
3266
+ className: cn2(
3266
3267
  "group relative flex shrink-0 snap-start items-center gap-2 rounded-full px-3.5 py-2.5 text-sm font-medium transition-colors",
3267
3268
  "min-h-[44px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
3268
3269
  active ? "bg-ph-ink text-ph-surface shadow-sm" : "bg-ph-surface text-ph-subtle ring-1 ring-ph-border hover:bg-ph-muted hover:text-ph-ink"
@@ -3347,7 +3348,7 @@ function SuiteSidebar({
3347
3348
  "aria-pressed": askAiOpen,
3348
3349
  "aria-label": askAiOpen ? "Back to navigation" : "Shelly AI",
3349
3350
  onClick: toggleAskAi,
3350
- className: cn(
3351
+ className: cn2(
3351
3352
  "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",
3352
3353
  askAiOpen ? "bg-ph-brand/10 text-ph-brand" : "text-ph-mutedtext hover:bg-ph-muted hover:text-ph-ink"
3353
3354
  ),
@@ -3359,7 +3360,7 @@ function SuiteSidebar({
3359
3360
  "div",
3360
3361
  {
3361
3362
  "data-suite-chat-open": "",
3362
- className: cn(
3363
+ className: cn2(
3363
3364
  "flex h-full w-full min-w-0 flex-col text-ph-ink",
3364
3365
  surface ? "bg-ph-surface" : "bg-ph-canvas",
3365
3366
  className
@@ -3368,12 +3369,12 @@ function SuiteSidebar({
3368
3369
  /* @__PURE__ */ jsxs19(
3369
3370
  "div",
3370
3371
  {
3371
- className: cn(
3372
+ className: cn2(
3372
3373
  "flex h-14 shrink-0 items-center gap-1 border-b border-ph-border px-2",
3373
3374
  surface ? "bg-ph-surface" : "bg-ph-canvas"
3374
3375
  ),
3375
3376
  children: [
3376
- /* @__PURE__ */ jsx22(Tooltip, { content: "Back to navigation", children: /* @__PURE__ */ jsx22(
3377
+ /* @__PURE__ */ jsx22(
3377
3378
  "button",
3378
3379
  {
3379
3380
  type: "button",
@@ -3383,7 +3384,7 @@ function SuiteSidebar({
3383
3384
  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",
3384
3385
  children: /* @__PURE__ */ jsx22(NavArrowLeft, { className: "h-4 w-4", "aria-hidden": true })
3385
3386
  }
3386
- ) }),
3387
+ ),
3387
3388
  /* @__PURE__ */ jsx22("span", { className: "min-w-0 flex-1 truncate text-sm font-semibold text-ph-ink", children: "Shelly AI" })
3388
3389
  ]
3389
3390
  }
@@ -3396,7 +3397,7 @@ function SuiteSidebar({
3396
3397
  return /* @__PURE__ */ jsxs19(
3397
3398
  "div",
3398
3399
  {
3399
- className: cn(
3400
+ className: cn2(
3400
3401
  "flex h-full w-full min-w-0 flex-col text-ph-ink",
3401
3402
  surface ? "bg-ph-surface" : "bg-ph-canvas",
3402
3403
  className
@@ -3405,13 +3406,13 @@ function SuiteSidebar({
3405
3406
  appSwitcher || notificationBell ? /* @__PURE__ */ jsxs19(
3406
3407
  "div",
3407
3408
  {
3408
- className: cn(
3409
+ className: cn2(
3409
3410
  "flex shrink-0 border-b border-ph-border",
3410
3411
  surface ? "bg-ph-surface" : "bg-ph-canvas",
3411
3412
  collapsed ? "flex-col items-center gap-1 px-1 py-2" : "h-14 items-center justify-between gap-2 px-3"
3412
3413
  ),
3413
3414
  children: [
3414
- /* @__PURE__ */ jsx22("div", { className: cn("min-w-0", collapsed ? "shrink-0" : "flex-1"), children: renderNode(appSwitcher, collapsed) }),
3415
+ /* @__PURE__ */ jsx22("div", { className: cn2("min-w-0", collapsed ? "shrink-0" : "flex-1"), children: renderNode(appSwitcher, collapsed) }),
3415
3416
  collapsed ? null : /* @__PURE__ */ jsx22("div", { className: "shrink-0", children: renderNode(notificationBell, collapsed) })
3416
3417
  ]
3417
3418
  }
@@ -3419,14 +3420,23 @@ function SuiteSidebar({
3419
3420
  /* @__PURE__ */ jsxs19(
3420
3421
  "div",
3421
3422
  {
3422
- className: cn(
3423
+ className: cn2(
3423
3424
  "flex min-h-0 flex-1 flex-col",
3424
3425
  surface ? "bg-ph-surface" : "bg-ph-canvas",
3425
3426
  chatOpen ? "min-h-0" : "overflow-y-auto",
3426
- collapsed ? "px-1.5 py-2" : "p-2"
3427
+ collapsed ? "px-1.5 py-2" : "p-3"
3427
3428
  ),
3428
3429
  children: [
3429
- /* @__PURE__ */ jsx22("div", { className: cn("mb-3 w-full shrink-0", collapsed && "flex justify-center"), children: renderNode(contextSwitcher, collapsed) }),
3430
+ contextSwitcher ? /* @__PURE__ */ jsx22(
3431
+ "div",
3432
+ {
3433
+ className: cn2(
3434
+ "mb-4 w-full shrink-0",
3435
+ collapsed && "flex justify-center"
3436
+ ),
3437
+ children: renderNode(contextSwitcher, collapsed)
3438
+ }
3439
+ ) : null,
3430
3440
  chatOpen ? /* @__PURE__ */ jsxs19("div", { className: "flex min-h-0 flex-1 flex-col", children: [
3431
3441
  /* @__PURE__ */ jsxs19("div", { className: "mb-2 flex shrink-0 items-center gap-1", children: [
3432
3442
  /* @__PURE__ */ jsxs19(
@@ -3446,7 +3456,7 @@ function SuiteSidebar({
3446
3456
  ] }),
3447
3457
  /* @__PURE__ */ jsx22("div", { className: "min-h-0 flex-1", children: askAi })
3448
3458
  ] }) : /* @__PURE__ */ jsxs19(Fragment7, { children: [
3449
- /* @__PURE__ */ jsx22("nav", { "aria-label": "Pages", className: "shrink-0 space-y-0.5", children: /* @__PURE__ */ jsx22("div", { className: "space-y-0.5", children: navItems.map((item) => {
3459
+ /* @__PURE__ */ jsx22("nav", { "aria-label": "Pages", className: "shrink-0 space-y-1", children: /* @__PURE__ */ jsx22("div", { className: "space-y-1", children: navItems.map((item) => {
3450
3460
  var _a;
3451
3461
  const Icon = item.icon;
3452
3462
  const active = Boolean(item.active);
@@ -3458,7 +3468,7 @@ function SuiteSidebar({
3458
3468
  "data-test": (_a = item.dataTest) != null ? _a : "nav-link",
3459
3469
  "aria-current": active ? "page" : void 0,
3460
3470
  onClick: item.onClick,
3461
- className: cn(
3471
+ className: cn2(
3462
3472
  "group relative flex min-h-11 touch-manipulation items-center gap-2.5 rounded-[var(--ph-radius-app)] text-sm font-medium transition-colors",
3463
3473
  active ? "bg-ph-muted" : "text-ph-subtle hover:bg-ph-muted hover:text-ph-ink",
3464
3474
  collapsed ? "mx-auto size-11 shrink-0 justify-center gap-0 px-0 py-0" : "w-full px-2.5 py-2"
@@ -3467,7 +3477,7 @@ function SuiteSidebar({
3467
3477
  /* @__PURE__ */ jsx22(
3468
3478
  "span",
3469
3479
  {
3470
- className: cn(
3480
+ className: cn2(
3471
3481
  "relative flex h-4 w-4 shrink-0 items-center justify-center",
3472
3482
  item.iconClassName
3473
3483
  ),
@@ -3484,7 +3494,7 @@ function SuiteSidebar({
3484
3494
  /* @__PURE__ */ jsx22(
3485
3495
  "span",
3486
3496
  {
3487
- className: cn(
3497
+ className: cn2(
3488
3498
  "relative truncate",
3489
3499
  active ? "text-ph-ink" : "text-ph-subtle group-hover:text-ph-ink",
3490
3500
  collapsed && "sr-only"
@@ -3514,7 +3524,7 @@ function SuiteSidebar({
3514
3524
  /* @__PURE__ */ jsx22(
3515
3525
  "div",
3516
3526
  {
3517
- className: cn(
3527
+ className: cn2(
3518
3528
  "shrink-0 border-t border-ph-border",
3519
3529
  surface ? "bg-ph-surface" : "bg-ph-canvas",
3520
3530
  collapsed ? "p-1.5" : "p-3"
@@ -3522,20 +3532,14 @@ function SuiteSidebar({
3522
3532
  children: /* @__PURE__ */ jsxs19(
3523
3533
  "div",
3524
3534
  {
3525
- className: cn(
3535
+ className: cn2(
3526
3536
  "flex items-center gap-2",
3527
3537
  collapsed ? "flex-col items-center justify-center [&>div]:w-auto" : "w-full min-w-0"
3528
3538
  ),
3529
3539
  children: [
3530
3540
  renderNode(userMenu, collapsed),
3531
- showAskAi ? /* @__PURE__ */ jsx22(
3532
- "div",
3533
- {
3534
- className: cn("shrink-0", collapsed ? "mt-2" : "ml-auto"),
3535
- children: /* @__PURE__ */ jsx22(Tooltip, { content: "Shelly AI", side: collapsed ? "right" : "top", children: askAiButton })
3536
- }
3537
- ) : footerExtras ? /* @__PURE__ */ jsx22("div", { className: cn("shrink-0", collapsed ? "mt-2" : "ml-auto"), children: renderNode(footerExtras, collapsed) }) : null,
3538
- showAskAi && footerExtras ? /* @__PURE__ */ jsx22("div", { className: cn("shrink-0", collapsed && "mt-2"), children: renderNode(footerExtras, collapsed) }) : null
3541
+ showAskAi ? /* @__PURE__ */ jsx22("div", { className: cn2("shrink-0", collapsed ? "mt-2" : "ml-auto"), children: /* @__PURE__ */ jsx22(Tooltip, { content: "Shelly AI", side: collapsed ? "right" : "top", children: askAiButton }) }) : footerExtras ? /* @__PURE__ */ jsx22("div", { className: cn2("shrink-0", collapsed ? "mt-2" : "ml-auto"), children: renderNode(footerExtras, collapsed) }) : null,
3542
+ showAskAi && footerExtras ? /* @__PURE__ */ jsx22("div", { className: cn2("shrink-0", collapsed && "mt-2"), children: renderNode(footerExtras, collapsed) }) : null
3539
3543
  ]
3540
3544
  }
3541
3545
  )
@@ -3565,7 +3569,7 @@ function SuiteAppLayout({
3565
3569
  "div",
3566
3570
  {
3567
3571
  "data-test": "app-shell",
3568
- className: cn(
3572
+ className: cn2(
3569
3573
  "suite-app-layout flex h-dvh overflow-hidden",
3570
3574
  className
3571
3575
  ),
@@ -3573,6 +3577,7 @@ function SuiteAppLayout({
3573
3577
  /* @__PURE__ */ jsxs20(
3574
3578
  "aside",
3575
3579
  {
3580
+ "aria-label": "Sidebar",
3576
3581
  className: "relative hidden shrink-0 flex-col overflow-visible border-r border-ph-border ease-out lg:flex",
3577
3582
  style: { width: `${sidebarWidth}px` },
3578
3583
  "data-collapsed": collapsed ? "true" : "false",
@@ -3584,6 +3589,10 @@ function SuiteAppLayout({
3584
3589
  role: "separator",
3585
3590
  "aria-orientation": "vertical",
3586
3591
  "aria-label": "Resize sidebar",
3592
+ "aria-valuenow": sidebarWidth,
3593
+ "aria-valuemin": SIDEBAR_RAIL_WIDTH,
3594
+ "aria-valuemax": SIDEBAR_MAX_WIDTH,
3595
+ "aria-valuetext": `${sidebarWidth} pixels`,
3587
3596
  "data-test": "sidebar-resize-handle",
3588
3597
  tabIndex: onResizeBy ? 0 : void 0,
3589
3598
  onPointerDown: onStartResize,
@@ -3600,7 +3609,7 @@ function SuiteAppLayout({
3600
3609
  onResizeBy == null ? void 0 : onResizeBy(16);
3601
3610
  }
3602
3611
  },
3603
- 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)]"
3612
+ className: "absolute inset-y-0 right-0 z-20 w-2 cursor-col-resize touch-none transition-colors before:absolute before:-left-2 before:-right-2 before:inset-y-0 before:content-[''] hover:bg-[color-mix(in_oklab,var(--ph-accent)_30%,transparent)]"
3604
3613
  }
3605
3614
  ) : null
3606
3615
  ]
@@ -3612,10 +3621,10 @@ function SuiteAppLayout({
3612
3621
  "main",
3613
3622
  {
3614
3623
  id: "main-content",
3615
- tabIndex: -1,
3624
+ tabIndex: 0,
3616
3625
  "data-lock-scroll": lockMainScroll ? "true" : void 0,
3617
- className: cn(
3618
- "flex min-h-0 min-w-0 flex-1 flex-col overflow-x-hidden overscroll-contain bg-ph-canvas focus:outline-none",
3626
+ className: cn2(
3627
+ "flex min-h-0 min-w-0 flex-1 flex-col overflow-x-hidden overscroll-contain bg-ph-canvas focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ph-focus",
3619
3628
  "suite-app-layout__main",
3620
3629
  lockMainScroll ? "overflow-hidden" : "overflow-y-auto"
3621
3630
  ),
@@ -3630,78 +3639,1970 @@ function SuiteAppLayout({
3630
3639
  }
3631
3640
 
3632
3641
  // src/suite/components/suite-integration-picker.tsx
3633
- import { useMemo, useState as useState10 } from "react";
3634
- import { NavArrowDown as NavArrowDown2, NavArrowUp, Server as Plug, Xmark } from "iconoir-react";
3635
- import { jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
3642
+ import {
3643
+ useEffect as useEffect8,
3644
+ useMemo,
3645
+ useRef as useRef3,
3646
+ useState as useState10
3647
+ } from "react";
3648
+ import {
3649
+ EvPlug as Plug,
3650
+ Filter,
3651
+ NavArrowDown as NavArrowDown2,
3652
+ NavArrowUp,
3653
+ Search,
3654
+ Xmark
3655
+ } from "iconoir-react";
3656
+ import { Fragment as Fragment8, jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
3636
3657
  var DESCRIPTION_LIMIT = 90;
3637
- function SuiteIntegrationPicker({
3638
- open,
3639
- items,
3640
- connectedItems = [],
3641
- loading = false,
3642
- onClose,
3643
- onConnect,
3644
- onDisconnect,
3645
- title = "Integrate an app",
3646
- description = "Connect an app to this workspace securely.",
3647
- dataTest = "suite-integration-picker"
3648
- }) {
3649
- const [query, setQuery] = useState10("");
3650
- const [category, setCategory] = useState10("all");
3651
- const [expanded, setExpanded] = useState10(null);
3652
- const categories = useMemo(
3653
- () => Array.from(new Set(items.flatMap((item) => {
3654
- var _a;
3655
- return (_a = item.categories) != null ? _a : [];
3656
- }))).sort(),
3657
- [items]
3658
+ function IntegrationSkeletonCard() {
3659
+ return /* @__PURE__ */ jsxs21(
3660
+ "div",
3661
+ {
3662
+ className: "flex min-h-36 min-w-0 animate-pulse flex-col justify-between rounded-xl border border-ph-border bg-ph-surface p-4",
3663
+ "aria-hidden": "true",
3664
+ children: [
3665
+ /* @__PURE__ */ jsxs21("div", { className: "flex items-start gap-3", children: [
3666
+ /* @__PURE__ */ jsx24("div", { className: "h-10 w-10 shrink-0 rounded-lg bg-ph-muted" }),
3667
+ /* @__PURE__ */ jsxs21("div", { className: "min-w-0 flex-1 space-y-2 pt-1", children: [
3668
+ /* @__PURE__ */ jsx24("div", { className: "h-4 w-3/5 rounded bg-ph-muted" }),
3669
+ /* @__PURE__ */ jsx24("div", { className: "h-3 w-full rounded bg-ph-muted" }),
3670
+ /* @__PURE__ */ jsx24("div", { className: "h-3 w-4/5 rounded bg-ph-muted" })
3671
+ ] })
3672
+ ] }),
3673
+ /* @__PURE__ */ jsx24("div", { className: "mt-3 h-9 w-20 rounded bg-ph-muted" })
3674
+ ]
3675
+ }
3676
+ );
3677
+ }
3678
+ var SKELETON_COUNT = 6;
3679
+ var AUTH_SCHEME_LABELS = {
3680
+ API_KEY: "API key",
3681
+ BASIC_AUTH: "Basic auth",
3682
+ BEARER_TOKEN: "Bearer token",
3683
+ OAUTH2: "OAuth 2.0"
3684
+ };
3685
+ function authSchemeLabel(scheme) {
3686
+ var _a;
3687
+ const normalised = scheme.trim().toUpperCase();
3688
+ return (_a = AUTH_SCHEME_LABELS[normalised]) != null ? _a : normalised.replace(/[-_]/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
3689
+ }
3690
+ function authSchemesFor(item) {
3691
+ var _a;
3692
+ return Array.from(
3693
+ new Set(
3694
+ ((_a = item.authSchemes) != null ? _a : []).map((scheme) => scheme.trim().toUpperCase()).filter(Boolean)
3695
+ )
3696
+ );
3697
+ }
3698
+ var CATEGORY_APP_AFFINITY = {
3699
+ calendar: ["turtletime", "tides"],
3700
+ email: ["turtletime", "tides"],
3701
+ code: ["tides", "kraken"],
3702
+ chat: ["tides", "kraken"],
3703
+ documents: ["kraken"],
3704
+ notes: ["kraken"],
3705
+ knowledge: ["kraken"],
3706
+ file_storage: ["kraken"],
3707
+ crm: ["shellstack", "tides"],
3708
+ finance: ["shellstack", "tides"],
3709
+ payment: ["shellstack", "tides"],
3710
+ monitoring: ["tides", "kraken"],
3711
+ design: ["tides", "kraken"],
3712
+ support: ["tides", "shellstack"],
3713
+ ticketing: ["tides", "kraken"]
3714
+ };
3715
+ var DEFAULT_APP_AFFINITY = ["tides", "kraken"];
3716
+ function suiteAppsForIntegration(categories) {
3717
+ var _a;
3718
+ const apps = /* @__PURE__ */ new Set();
3719
+ for (const category of categories != null ? categories : []) {
3720
+ for (const app of (_a = CATEGORY_APP_AFFINITY[category.trim().toLowerCase()]) != null ? _a : []) {
3721
+ apps.add(app);
3722
+ }
3723
+ }
3724
+ return apps.size > 0 ? SUITE_APPS2.filter((app) => apps.has(app.slug)).map((app) => app.slug) : [...DEFAULT_APP_AFFINITY];
3725
+ }
3726
+ function SuiteIntegrationPicker({
3727
+ open,
3728
+ items,
3729
+ connectedItems = [],
3730
+ loading = false,
3731
+ catalogLoading = false,
3732
+ loadingMore = false,
3733
+ hasMore = false,
3734
+ onEndReached,
3735
+ search: controlledSearch,
3736
+ onSearchChange,
3737
+ category: controlledCategory,
3738
+ categories: controlledCategories,
3739
+ onCategoryChange,
3740
+ onClose,
3741
+ onConnect,
3742
+ onFetchAuthFields,
3743
+ onDisconnect,
3744
+ title = "Integrate an app",
3745
+ description = "Connect an app to this workspace securely.",
3746
+ dataTest = "suite-integration-picker"
3747
+ }) {
3748
+ const [query, setQuery] = useState10("");
3749
+ const [category, setCategory] = useState10("all");
3750
+ const [categoryQuery, setCategoryQuery] = useState10("");
3751
+ const [categoryMenuOpen, setCategoryMenuOpen] = useState10(false);
3752
+ const [expanded, setExpanded] = useState10(null);
3753
+ const categoryMenuRef = useRef3(null);
3754
+ const categoryTriggerRef = useRef3(null);
3755
+ const categorySearchRef = useRef3(null);
3756
+ const categoryOptionRefs = useRef3({});
3757
+ const pendingCategoryFocus = useRef3(
3758
+ null
3759
+ );
3760
+ const scrollViewportRef = useRef3(null);
3761
+ const loadMoreSentinelRef = useRef3(null);
3762
+ const loadMoreInFlightRef = useRef3(false);
3763
+ const authRequestRef = useRef3(0);
3764
+ const [methodItem, setMethodItem] = useState10(null);
3765
+ const [authItem, setAuthItem] = useState10(null);
3766
+ const [authValues, setAuthValues] = useState10({});
3767
+ const [authLoading, setAuthLoading] = useState10(false);
3768
+ const [authSubmitting, setAuthSubmitting] = useState10(false);
3769
+ const [authError, setAuthError] = useState10(null);
3770
+ const [connectionError, setConnectionError] = useState10(null);
3771
+ const [itemToDisconnect, setItemToDisconnect] = useState10(null);
3772
+ const [disconnecting, setDisconnecting] = useState10(false);
3773
+ const [disconnectError, setDisconnectError] = useState10(null);
3774
+ const itemCategories = useMemo(
3775
+ () => Array.from(
3776
+ new Set(items.flatMap((item) => {
3777
+ var _a;
3778
+ return (_a = item.categories) != null ? _a : [];
3779
+ }))
3780
+ ).sort(),
3781
+ [items]
3658
3782
  );
3659
- const filtered = items.filter((item) => {
3783
+ const categories = controlledCategories != null ? controlledCategories : itemCategories;
3784
+ const activeSearch = controlledSearch != null ? controlledSearch : query;
3785
+ const activeCategory = controlledCategory != null ? controlledCategory : category;
3786
+ const categoryLabel = (value) => value.replace(/[-_]/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
3787
+ const filtered = onSearchChange || onCategoryChange ? items : items.filter((item) => {
3660
3788
  var _a;
3661
- const haystack = `${item.label} ${item.description}`.toLowerCase();
3662
- return haystack.includes(query.trim().toLowerCase()) && (category === "all" || ((_a = item.categories) != null ? _a : []).includes(category));
3789
+ return item.label.toLowerCase().includes(activeSearch.trim().toLowerCase()) && (activeCategory === "all" || ((_a = item.categories) != null ? _a : []).includes(activeCategory));
3663
3790
  });
3664
- return /* @__PURE__ */ jsx24(Modal, { open, onClose, title, description, size: "xl", dataTest, children: /* @__PURE__ */ jsxs21("div", { className: "space-y-4", children: [
3665
- /* @__PURE__ */ jsx24(Input, { value: query, onChange: (event) => setQuery(event.target.value), placeholder: "Search apps...", "aria-label": "Search apps" }),
3666
- /* @__PURE__ */ jsx24("div", { className: "flex flex-wrap gap-2", role: "group", "aria-label": "Integration categories", children: ["all", ...categories].map((value) => /* @__PURE__ */ jsx24("button", { type: "button", onClick: () => setCategory(value), className: category === value ? "rounded-full bg-ph-brand px-3 py-1.5 text-xs font-semibold text-white" : "rounded-full border border-ph-border px-3 py-1.5 text-xs text-ph-subtle hover:bg-ph-muted", children: value === "all" ? "All apps" : value }, value)) }),
3667
- loading ? /* @__PURE__ */ jsx24("p", { className: "text-sm text-ph-subtle", children: "Loading apps..." }) : /* @__PURE__ */ jsx24("div", { className: "grid max-h-[60vh] gap-3 overflow-y-auto sm:grid-cols-2 lg:grid-cols-3", children: filtered.map((item) => {
3668
- var _a, _b;
3669
- const isExpanded = expanded === item.id;
3670
- const longDescription = item.description.length > DESCRIPTION_LIMIT;
3671
- const text = isExpanded || !longDescription ? item.description : `${item.description.slice(0, DESCRIPTION_LIMIT).trimEnd()}...`;
3672
- return /* @__PURE__ */ jsxs21("div", { className: "flex min-h-36 flex-col justify-between rounded-xl border border-ph-border bg-ph-surface p-4", children: [
3673
- /* @__PURE__ */ jsx24("button", { type: "button", className: "text-left", onClick: () => longDescription && setExpanded(isExpanded ? null : item.id), "aria-expanded": isExpanded, children: /* @__PURE__ */ jsxs21("span", { className: "flex items-start gap-3", children: [
3674
- /* @__PURE__ */ jsx24("span", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-ph-muted text-ph-ink", children: (_a = item.icon) != null ? _a : /* @__PURE__ */ jsx24(Plug, { className: "h-5 w-5", "aria-hidden": true }) }),
3675
- /* @__PURE__ */ jsxs21("span", { className: "min-w-0", children: [
3676
- /* @__PURE__ */ jsx24("span", { className: "block font-medium text-ph-ink", children: item.label }),
3677
- /* @__PURE__ */ jsx24("span", { className: "mt-1 block text-xs text-ph-subtle", children: text }),
3678
- longDescription ? /* @__PURE__ */ jsxs21("span", { className: "mt-1 block text-xs font-medium text-ph-brand", children: [
3679
- isExpanded ? "See less" : "See more",
3680
- " ",
3681
- isExpanded ? /* @__PURE__ */ jsx24(NavArrowUp, { className: "inline h-3 w-3" }) : /* @__PURE__ */ jsx24(NavArrowDown2, { className: "inline h-3 w-3" })
3682
- ] }) : null
3683
- ] })
3684
- ] }) }),
3685
- /* @__PURE__ */ jsx24(Button, { type: "button", variant: "primary", className: "mt-3 self-start", disabled: Boolean(item.unavailableReason) || item.connecting, onClick: () => onConnect(item), children: /* @__PURE__ */ jsxs21("span", { className: "flex items-center gap-1.5", children: [
3686
- /* @__PURE__ */ jsx24(Plug, { className: "h-3.5 w-3.5", "aria-hidden": true }),
3687
- item.connecting ? "Connecting..." : (_b = item.unavailableReason) != null ? _b : "Connect"
3688
- ] }) })
3689
- ] }, item.id);
3690
- }) }),
3691
- connectedItems.length > 0 && onDisconnect ? /* @__PURE__ */ jsxs21("div", { className: "space-y-2 border-t border-ph-border pt-4", children: [
3692
- /* @__PURE__ */ jsx24("h3", { className: "font-medium text-ph-ink", children: "Connected apps" }),
3693
- connectedItems.map((item) => /* @__PURE__ */ jsxs21("div", { className: "flex items-center justify-between gap-3 rounded-lg border border-ph-border bg-ph-muted p-3", children: [
3694
- /* @__PURE__ */ jsxs21("span", { className: "flex min-w-0 items-center gap-2 text-sm text-ph-ink", children: [
3695
- item.icon,
3696
- /* @__PURE__ */ jsx24("span", { className: "truncate", children: item.label })
3791
+ const visibleCategories = categories.filter(
3792
+ (value) => categoryLabel(value).toLowerCase().includes(categoryQuery.trim().toLowerCase())
3793
+ );
3794
+ const categoryValues = ["all", ...visibleCategories];
3795
+ const focusableCategory = categoryValues.includes(activeCategory) ? activeCategory : "all";
3796
+ function focusCategory(value) {
3797
+ pendingCategoryFocus.current = value;
3798
+ }
3799
+ function openCategoryMenu(focus = null) {
3800
+ setCategoryQuery("");
3801
+ if (focus) focusCategory(focus);
3802
+ setCategoryMenuOpen(true);
3803
+ }
3804
+ function closeCategoryMenu(restoreFocus = false) {
3805
+ if (restoreFocus) focusCategory("trigger");
3806
+ setCategoryMenuOpen(false);
3807
+ }
3808
+ function selectCategory(value) {
3809
+ setCategory(value);
3810
+ onCategoryChange == null ? void 0 : onCategoryChange(value);
3811
+ closeCategoryMenu(true);
3812
+ }
3813
+ function moveCategoryFocus(event, value) {
3814
+ var _a;
3815
+ const index = categoryValues.indexOf(value);
3816
+ let nextIndex = null;
3817
+ if (event.key === "ArrowDown") nextIndex = (index + 1) % categoryValues.length;
3818
+ else if (event.key === "ArrowUp")
3819
+ nextIndex = (index - 1 + categoryValues.length) % categoryValues.length;
3820
+ else if (event.key === "Home") nextIndex = 0;
3821
+ else if (event.key === "End") nextIndex = categoryValues.length - 1;
3822
+ if (nextIndex === null) return;
3823
+ event.preventDefault();
3824
+ (_a = categoryOptionRefs.current[categoryValues[nextIndex]]) == null ? void 0 : _a.focus();
3825
+ }
3826
+ function handleCategoryTriggerKeyDown(event) {
3827
+ var _a;
3828
+ if (event.key === "ArrowDown") {
3829
+ event.preventDefault();
3830
+ openCategoryMenu(activeCategory === "all" ? "all" : activeCategory);
3831
+ } else if (event.key === "ArrowUp") {
3832
+ event.preventDefault();
3833
+ openCategoryMenu((_a = categoryValues.at(-1)) != null ? _a : "all");
3834
+ } else if (event.key === "Enter" || event.key === " ") {
3835
+ event.preventDefault();
3836
+ openCategoryMenu("search");
3837
+ }
3838
+ }
3839
+ function resetAuthForm() {
3840
+ authRequestRef.current += 1;
3841
+ setAuthItem(null);
3842
+ setAuthValues({});
3843
+ setAuthLoading(false);
3844
+ setAuthError(null);
3845
+ }
3846
+ function closeAuthForm() {
3847
+ if (authSubmitting) return;
3848
+ resetAuthForm();
3849
+ }
3850
+ async function prepareConnection(item, scheme) {
3851
+ var _a;
3852
+ setMethodItem(null);
3853
+ setAuthError(null);
3854
+ setConnectionError(null);
3855
+ setAuthValues({});
3856
+ if (!onFetchAuthFields) {
3857
+ try {
3858
+ await onConnect(item, { auth_scheme: scheme });
3859
+ } catch (cause) {
3860
+ setConnectionError(
3861
+ cause instanceof Error ? cause.message : "Could not connect the app."
3862
+ );
3863
+ }
3864
+ return;
3865
+ }
3866
+ const requestId = authRequestRef.current + 1;
3867
+ authRequestRef.current = requestId;
3868
+ setAuthItem({ item, scheme, fields: [] });
3869
+ setAuthLoading(true);
3870
+ try {
3871
+ const details = await onFetchAuthFields(item, scheme);
3872
+ if (requestId !== authRequestRef.current) return;
3873
+ const resolvedScheme = (_a = details.auth_scheme) != null ? _a : scheme;
3874
+ if (details.managed) {
3875
+ setAuthItem(null);
3876
+ await onConnect(item, { auth_scheme: resolvedScheme });
3877
+ } else if (details.fields.length === 0) {
3878
+ setAuthItem(null);
3879
+ await onConnect(item, {
3880
+ auth_scheme: resolvedScheme,
3881
+ auth_fields: {}
3882
+ });
3883
+ } else {
3884
+ setAuthItem({ item, scheme: resolvedScheme, fields: details.fields });
3885
+ }
3886
+ } catch (cause) {
3887
+ if (requestId === authRequestRef.current) {
3888
+ const message = cause instanceof Error ? cause.message : "Could not load authentication details.";
3889
+ setAuthError(message);
3890
+ setConnectionError(message);
3891
+ }
3892
+ } finally {
3893
+ if (requestId === authRequestRef.current) setAuthLoading(false);
3894
+ }
3895
+ }
3896
+ function connectItem(item) {
3897
+ setConnectionError(null);
3898
+ const schemes = authSchemesFor(item);
3899
+ if (schemes.length > 1) {
3900
+ setMethodItem(item);
3901
+ return;
3902
+ }
3903
+ if (schemes.length === 1) {
3904
+ void prepareConnection(item, schemes[0]);
3905
+ return;
3906
+ }
3907
+ void (async () => {
3908
+ try {
3909
+ await onConnect(item);
3910
+ } catch (cause) {
3911
+ setConnectionError(
3912
+ cause instanceof Error ? cause.message : "Could not connect the app."
3913
+ );
3914
+ }
3915
+ })();
3916
+ }
3917
+ function requestDisconnect(item) {
3918
+ setDisconnectError(null);
3919
+ setItemToDisconnect(item);
3920
+ }
3921
+ async function disconnectItem() {
3922
+ if (!onDisconnect || !itemToDisconnect) return;
3923
+ setDisconnecting(true);
3924
+ setDisconnectError(null);
3925
+ try {
3926
+ await onDisconnect(itemToDisconnect);
3927
+ setItemToDisconnect(null);
3928
+ } catch (cause) {
3929
+ setDisconnectError(
3930
+ cause instanceof Error ? cause.message : "Could not disconnect the app."
3931
+ );
3932
+ } finally {
3933
+ setDisconnecting(false);
3934
+ }
3935
+ }
3936
+ async function submitAuthForm(event) {
3937
+ event.preventDefault();
3938
+ if (!authItem) return;
3939
+ setAuthSubmitting(true);
3940
+ setAuthError(null);
3941
+ try {
3942
+ await onConnect(authItem.item, {
3943
+ auth_scheme: authItem.scheme,
3944
+ auth_fields: authValues
3945
+ });
3946
+ resetAuthForm();
3947
+ } catch (cause) {
3948
+ setAuthError(
3949
+ cause instanceof Error ? cause.message : "Could not connect the app."
3950
+ );
3951
+ } finally {
3952
+ setAuthSubmitting(false);
3953
+ }
3954
+ }
3955
+ useEffect8(() => {
3956
+ if (!categoryMenuOpen) return;
3957
+ const handlePointerDown = (event) => {
3958
+ var _a;
3959
+ if (!((_a = categoryMenuRef.current) == null ? void 0 : _a.contains(event.target))) {
3960
+ closeCategoryMenu();
3961
+ }
3962
+ };
3963
+ const handleKeyDown = (event) => {
3964
+ if (event.key === "Escape") closeCategoryMenu(true);
3965
+ };
3966
+ document.addEventListener("pointerdown", handlePointerDown);
3967
+ document.addEventListener("keydown", handleKeyDown);
3968
+ return () => {
3969
+ document.removeEventListener("pointerdown", handlePointerDown);
3970
+ document.removeEventListener("keydown", handleKeyDown);
3971
+ };
3972
+ }, [categoryMenuOpen]);
3973
+ useEffect8(() => {
3974
+ var _a, _b, _c;
3975
+ const target = pendingCategoryFocus.current;
3976
+ if (!target) return;
3977
+ pendingCategoryFocus.current = null;
3978
+ if (target === "trigger") (_a = categoryTriggerRef.current) == null ? void 0 : _a.focus();
3979
+ else if (target === "search") (_b = categorySearchRef.current) == null ? void 0 : _b.focus();
3980
+ else (_c = categoryOptionRefs.current[target]) == null ? void 0 : _c.focus();
3981
+ }, [activeCategory, categoryMenuOpen, categoryQuery, visibleCategories]);
3982
+ useEffect8(() => {
3983
+ if (!loadingMore) loadMoreInFlightRef.current = false;
3984
+ }, [loadingMore]);
3985
+ useEffect8(() => {
3986
+ const root = scrollViewportRef.current;
3987
+ const sentinel = loadMoreSentinelRef.current;
3988
+ if (!open || !root || !sentinel || !hasMore || !onEndReached || loading || catalogLoading || loadingMore) {
3989
+ return;
3990
+ }
3991
+ const observer = new IntersectionObserver(
3992
+ ([entry]) => {
3993
+ if (!entry.isIntersecting || loadMoreInFlightRef.current) return;
3994
+ loadMoreInFlightRef.current = true;
3995
+ observer.unobserve(entry.target);
3996
+ onEndReached();
3997
+ },
3998
+ { root, rootMargin: "0px 0px 80px", threshold: 0 }
3999
+ );
4000
+ observer.observe(sentinel);
4001
+ return () => observer.disconnect();
4002
+ }, [catalogLoading, hasMore, loading, loadingMore, onEndReached, open]);
4003
+ return /* @__PURE__ */ jsxs21(Fragment8, { children: [
4004
+ /* @__PURE__ */ jsx24(
4005
+ Modal,
4006
+ {
4007
+ open,
4008
+ onClose,
4009
+ title,
4010
+ description,
4011
+ size: "xl",
4012
+ dataTest,
4013
+ bodyClassName: "!overflow-hidden",
4014
+ children: /* @__PURE__ */ jsxs21("div", { className: "space-y-4", children: [
4015
+ connectionError ? /* @__PURE__ */ jsx24("p", { className: "text-sm text-ph-danger", role: "alert", children: connectionError }) : null,
4016
+ /* @__PURE__ */ jsxs21("div", { className: "grid gap-4 md:grid-cols-2", children: [
4017
+ /* @__PURE__ */ jsxs21("div", { className: "relative", children: [
4018
+ /* @__PURE__ */ jsx24(
4019
+ Search,
4020
+ {
4021
+ className: "pointer-events-none absolute left-3 top-1/2 z-10 h-4 w-4 -translate-y-1/2 text-ph-subtle",
4022
+ "aria-hidden": true
4023
+ }
4024
+ ),
4025
+ /* @__PURE__ */ jsx24(
4026
+ Input,
4027
+ {
4028
+ value: activeSearch,
4029
+ onChange: (event) => {
4030
+ setQuery(event.target.value);
4031
+ onSearchChange == null ? void 0 : onSearchChange(event.target.value);
4032
+ },
4033
+ placeholder: "Search apps...",
4034
+ "aria-label": "Search apps",
4035
+ className: "pl-10"
4036
+ }
4037
+ )
4038
+ ] }),
4039
+ /* @__PURE__ */ jsxs21("div", { ref: categoryMenuRef, className: "relative", children: [
4040
+ /* @__PURE__ */ jsxs21(
4041
+ "button",
4042
+ {
4043
+ ref: categoryTriggerRef,
4044
+ id: `${dataTest}-category`,
4045
+ type: "button",
4046
+ className: "ph-input flex w-full items-center justify-between text-left",
4047
+ "aria-haspopup": "listbox",
4048
+ "aria-expanded": categoryMenuOpen,
4049
+ "aria-controls": categoryMenuOpen ? `${dataTest}-category-options` : void 0,
4050
+ "aria-label": "Filter by category",
4051
+ onClick: () => {
4052
+ if (categoryMenuOpen) closeCategoryMenu();
4053
+ else openCategoryMenu();
4054
+ },
4055
+ onKeyDown: handleCategoryTriggerKeyDown,
4056
+ children: [
4057
+ /* @__PURE__ */ jsxs21("span", { className: "flex items-center gap-2", children: [
4058
+ /* @__PURE__ */ jsx24(
4059
+ Filter,
4060
+ {
4061
+ className: "h-4 w-4 shrink-0 text-ph-subtle",
4062
+ "aria-hidden": true
4063
+ }
4064
+ ),
4065
+ /* @__PURE__ */ jsx24("span", { children: activeCategory === "all" ? "All apps" : categoryLabel(activeCategory) })
4066
+ ] }),
4067
+ /* @__PURE__ */ jsx24(
4068
+ NavArrowDown2,
4069
+ {
4070
+ className: `h-4 w-4 transition-transform ${categoryMenuOpen ? "rotate-180" : ""}`,
4071
+ "aria-hidden": true
4072
+ }
4073
+ )
4074
+ ]
4075
+ }
4076
+ ),
4077
+ categoryMenuOpen ? /* @__PURE__ */ jsxs21(
4078
+ "div",
4079
+ {
4080
+ className: "absolute z-20 mt-2 max-h-[min(22rem,calc(100dvh-12rem))] w-full max-w-full overflow-hidden rounded-lg border border-ph-border bg-ph-surface p-2 shadow-lg",
4081
+ children: [
4082
+ /* @__PURE__ */ jsx24(
4083
+ Input,
4084
+ {
4085
+ ref: categorySearchRef,
4086
+ value: categoryQuery,
4087
+ onChange: (event) => setCategoryQuery(event.target.value),
4088
+ placeholder: "Search categories...",
4089
+ "aria-label": "Search categories",
4090
+ onKeyDown: (event) => {
4091
+ var _a;
4092
+ if (event.key === "ArrowDown") {
4093
+ event.preventDefault();
4094
+ (_a = categoryOptionRefs.current[categoryValues[0]]) == null ? void 0 : _a.focus();
4095
+ } else if (event.key === "Escape") {
4096
+ event.preventDefault();
4097
+ closeCategoryMenu(true);
4098
+ }
4099
+ }
4100
+ }
4101
+ ),
4102
+ /* @__PURE__ */ jsx24(
4103
+ "div",
4104
+ {
4105
+ id: `${dataTest}-category-options`,
4106
+ role: "listbox",
4107
+ "aria-label": "Integration categories",
4108
+ className: "mt-2 max-h-52 overflow-y-auto",
4109
+ "data-test": "integration-category-options",
4110
+ children: categoryValues.map((value) => /* @__PURE__ */ jsx24(
4111
+ "button",
4112
+ {
4113
+ type: "button",
4114
+ role: "option",
4115
+ "aria-selected": activeCategory === value,
4116
+ tabIndex: value === focusableCategory ? 0 : -1,
4117
+ ref: (element) => {
4118
+ categoryOptionRefs.current[value] = element;
4119
+ },
4120
+ className: `flex w-full min-w-0 items-center rounded-md px-3 py-2 text-left text-sm break-words ${activeCategory === value ? "bg-ph-brand text-[var(--ph-on-accent)]" : "text-ph-ink hover:bg-ph-muted"}`,
4121
+ onClick: () => selectCategory(value),
4122
+ onKeyDown: (event) => {
4123
+ if (event.key === "Escape") {
4124
+ event.preventDefault();
4125
+ closeCategoryMenu(true);
4126
+ } else if (event.key === "Enter" || event.key === " ") {
4127
+ event.preventDefault();
4128
+ selectCategory(value);
4129
+ } else moveCategoryFocus(event, value);
4130
+ },
4131
+ children: value === "all" ? "All apps" : categoryLabel(value)
4132
+ },
4133
+ value
4134
+ ))
4135
+ }
4136
+ )
4137
+ ]
4138
+ }
4139
+ ) : null
4140
+ ] })
4141
+ ] }),
4142
+ /* @__PURE__ */ jsxs21(
4143
+ "div",
4144
+ {
4145
+ ref: scrollViewportRef,
4146
+ className: "min-h-0 max-h-[min(60vh,32rem)] w-full overflow-y-auto pr-1",
4147
+ children: [
4148
+ loading || catalogLoading ? /* @__PURE__ */ jsx24(
4149
+ "div",
4150
+ {
4151
+ className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3",
4152
+ "aria-label": "Loading apps",
4153
+ children: Array.from({ length: SKELETON_COUNT }, (_, index) => /* @__PURE__ */ jsx24(IntegrationSkeletonCard, {}, index))
4154
+ }
4155
+ ) : /* @__PURE__ */ jsxs21("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3", children: [
4156
+ filtered.map((item) => {
4157
+ var _a, _b;
4158
+ const isExpanded = expanded === item.id;
4159
+ const longDescription = item.description.length > DESCRIPTION_LIMIT;
4160
+ const text = isExpanded || !longDescription ? item.description : `${item.description.slice(0, DESCRIPTION_LIMIT).trimEnd()}...`;
4161
+ return /* @__PURE__ */ jsxs21(
4162
+ "div",
4163
+ {
4164
+ className: "flex min-h-36 min-w-0 flex-col justify-between rounded-xl border border-ph-border bg-ph-surface p-4",
4165
+ children: [
4166
+ /* @__PURE__ */ jsx24(
4167
+ "button",
4168
+ {
4169
+ type: "button",
4170
+ className: "min-w-0 text-left",
4171
+ onClick: () => longDescription && setExpanded(isExpanded ? null : item.id),
4172
+ "aria-expanded": isExpanded,
4173
+ children: /* @__PURE__ */ jsxs21("span", { className: "flex items-start gap-3", children: [
4174
+ /* @__PURE__ */ jsx24("span", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-ph-muted text-ph-ink", children: (_a = item.icon) != null ? _a : /* @__PURE__ */ jsx24(Plug, { className: "h-5 w-5", "aria-hidden": true }) }),
4175
+ /* @__PURE__ */ jsxs21("span", { className: "min-w-0", children: [
4176
+ /* @__PURE__ */ jsx24("span", { className: "block truncate font-medium text-ph-ink", children: item.label }),
4177
+ /* @__PURE__ */ jsx24("span", { className: "mt-1 block text-xs text-ph-subtle", children: text }),
4178
+ longDescription ? /* @__PURE__ */ jsxs21("span", { className: "mt-1 block text-xs font-medium text-ph-brand", children: [
4179
+ isExpanded ? "See less" : "See more",
4180
+ " ",
4181
+ isExpanded ? /* @__PURE__ */ jsx24(NavArrowUp, { className: "inline h-3 w-3" }) : /* @__PURE__ */ jsx24(NavArrowDown2, { className: "inline h-3 w-3" })
4182
+ ] }) : null
4183
+ ] })
4184
+ ] })
4185
+ }
4186
+ ),
4187
+ suiteAppsForIntegration(item.categories).length > 0 ? /* @__PURE__ */ jsx24(
4188
+ "div",
4189
+ {
4190
+ className: "mt-3 flex flex-wrap items-center gap-1.5",
4191
+ "aria-label": "Works with ShellStack apps",
4192
+ children: suiteAppsForIntegration(item.categories).map(
4193
+ (app) => /* @__PURE__ */ jsx24(
4194
+ "span",
4195
+ {
4196
+ "data-test": `${dataTest}-${item.id}-app-${app}`,
4197
+ children: /* @__PURE__ */ jsx24(
4198
+ SuiteAppIcon,
4199
+ {
4200
+ app,
4201
+ size: 16,
4202
+ label: `Works with ${SUITE_APP_MAP[app].name}`,
4203
+ className: "rounded-[4px]"
4204
+ }
4205
+ )
4206
+ },
4207
+ app
4208
+ )
4209
+ )
4210
+ }
4211
+ ) : null,
4212
+ authSchemesFor(item).length > 0 ? /* @__PURE__ */ jsx24(
4213
+ "div",
4214
+ {
4215
+ className: "mt-3 flex flex-wrap gap-1.5",
4216
+ "aria-label": "Connection methods",
4217
+ children: authSchemesFor(item).map((scheme) => /* @__PURE__ */ jsx24(
4218
+ "span",
4219
+ {
4220
+ className: "rounded-full border border-ph-border bg-ph-muted px-2 py-1 text-[0.6875rem] font-medium text-ph-subtle",
4221
+ "data-test": `${dataTest}-${item.id}-auth-${scheme.toLowerCase()}`,
4222
+ children: authSchemeLabel(scheme)
4223
+ },
4224
+ scheme
4225
+ ))
4226
+ }
4227
+ ) : null,
4228
+ /* @__PURE__ */ jsx24(
4229
+ Button,
4230
+ {
4231
+ type: "button",
4232
+ variant: "primary",
4233
+ className: "mt-3 self-start",
4234
+ disabled: Boolean(item.unavailableReason) || item.connecting,
4235
+ onClick: () => connectItem(item),
4236
+ children: /* @__PURE__ */ jsxs21("span", { className: "flex items-center gap-1.5", children: [
4237
+ /* @__PURE__ */ jsx24(Plug, { className: "h-3.5 w-3.5", "aria-hidden": true }),
4238
+ item.connecting ? "Connecting..." : (_b = item.unavailableReason) != null ? _b : "Connect"
4239
+ ] })
4240
+ }
4241
+ )
4242
+ ]
4243
+ },
4244
+ item.id
4245
+ );
4246
+ }),
4247
+ loadingMore ? Array.from({ length: 3 }, (_, index) => /* @__PURE__ */ jsx24(IntegrationSkeletonCard, {}, `loading-${index}`)) : null
4248
+ ] }),
4249
+ /* @__PURE__ */ jsx24("div", { ref: loadMoreSentinelRef, className: "h-1", "aria-hidden": "true" })
4250
+ ]
4251
+ }
4252
+ ),
4253
+ connectedItems.length > 0 && onDisconnect ? /* @__PURE__ */ jsxs21("div", { className: "space-y-2 border-t border-ph-border pt-4", children: [
4254
+ /* @__PURE__ */ jsx24("h3", { className: "font-medium text-ph-ink", children: "Connected apps" }),
4255
+ connectedItems.map((item) => /* @__PURE__ */ jsxs21(
4256
+ "div",
4257
+ {
4258
+ className: "flex items-center justify-between gap-3 rounded-lg border border-ph-border bg-ph-muted p-3",
4259
+ children: [
4260
+ /* @__PURE__ */ jsxs21("span", { className: "flex min-w-0 items-center gap-2 text-sm text-ph-ink", children: [
4261
+ item.icon,
4262
+ /* @__PURE__ */ jsx24("span", { className: "truncate", children: item.label })
4263
+ ] }),
4264
+ /* @__PURE__ */ jsx24(
4265
+ Button,
4266
+ {
4267
+ type: "button",
4268
+ variant: "danger",
4269
+ onClick: () => requestDisconnect(item),
4270
+ children: /* @__PURE__ */ jsxs21("span", { className: "flex items-center gap-1.5", children: [
4271
+ /* @__PURE__ */ jsx24(Xmark, { className: "h-4 w-4", "aria-hidden": true }),
4272
+ "Disconnect"
4273
+ ] })
4274
+ }
4275
+ )
4276
+ ]
4277
+ },
4278
+ item.id
4279
+ ))
4280
+ ] }) : null
4281
+ ] })
4282
+ }
4283
+ ),
4284
+ /* @__PURE__ */ jsxs21(
4285
+ Modal,
4286
+ {
4287
+ open: itemToDisconnect !== null,
4288
+ onClose: () => {
4289
+ if (!disconnecting) setItemToDisconnect(null);
4290
+ },
4291
+ title: "Disconnect app",
4292
+ description: "This will stop syncing data from this app.",
4293
+ size: "sm",
4294
+ zIndex: 220,
4295
+ dataTest: `${dataTest}-disconnect-confirmation`,
4296
+ footer: /* @__PURE__ */ jsxs21("div", { className: "flex justify-end gap-2", children: [
4297
+ /* @__PURE__ */ jsx24(
4298
+ Button,
4299
+ {
4300
+ type: "button",
4301
+ variant: "tertiary",
4302
+ onClick: () => setItemToDisconnect(null),
4303
+ disabled: disconnecting,
4304
+ children: "Cancel"
4305
+ }
4306
+ ),
4307
+ /* @__PURE__ */ jsx24(
4308
+ Button,
4309
+ {
4310
+ type: "button",
4311
+ variant: "danger",
4312
+ onClick: () => void disconnectItem(),
4313
+ disabled: disconnecting,
4314
+ "data-test": `${dataTest}-disconnect-confirm`,
4315
+ children: disconnecting ? "Disconnecting..." : "Disconnect app"
4316
+ }
4317
+ )
3697
4318
  ] }),
3698
- /* @__PURE__ */ jsx24(Button, { type: "button", variant: "danger", onClick: () => onDisconnect(item), children: /* @__PURE__ */ jsxs21("span", { className: "flex items-center gap-1.5", children: [
3699
- /* @__PURE__ */ jsx24(Xmark, { className: "h-4 w-4", "aria-hidden": true }),
3700
- "Disconnect"
3701
- ] }) })
3702
- ] }, item.id))
3703
- ] }) : null
3704
- ] }) });
4319
+ children: [
4320
+ disconnectError ? /* @__PURE__ */ jsx24("p", { className: "text-sm text-ph-danger", role: "alert", children: disconnectError }) : null,
4321
+ /* @__PURE__ */ jsxs21("p", { className: "text-sm text-ph-ink", children: [
4322
+ "Disconnect ",
4323
+ itemToDisconnect == null ? void 0 : itemToDisconnect.label,
4324
+ "? You can reconnect it later."
4325
+ ] })
4326
+ ]
4327
+ }
4328
+ ),
4329
+ /* @__PURE__ */ jsx24(
4330
+ Modal,
4331
+ {
4332
+ open: Boolean(methodItem),
4333
+ onClose: () => setMethodItem(null),
4334
+ title: methodItem ? `Connect ${methodItem.label}` : "Choose connection method",
4335
+ description: "Choose how you want to connect this app.",
4336
+ size: "sm",
4337
+ zIndex: 200,
4338
+ dataTest: `${dataTest}-auth-method`,
4339
+ children: methodItem ? /* @__PURE__ */ jsx24("div", { className: "space-y-2", children: authSchemesFor(methodItem).map((scheme) => /* @__PURE__ */ jsxs21(
4340
+ "button",
4341
+ {
4342
+ type: "button",
4343
+ className: "flex w-full items-center justify-between rounded-lg border border-ph-border bg-ph-surface px-3 py-3 text-left text-sm text-ph-ink transition hover:border-ph-brand hover:bg-ph-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
4344
+ onClick: () => void prepareConnection(methodItem, scheme),
4345
+ "data-test": `${dataTest}-choose-${scheme.toLowerCase()}`,
4346
+ children: [
4347
+ /* @__PURE__ */ jsx24("span", { className: "font-medium", children: authSchemeLabel(scheme) }),
4348
+ /* @__PURE__ */ jsx24("span", { className: "text-xs text-ph-subtle", children: "Select" })
4349
+ ]
4350
+ },
4351
+ scheme
4352
+ )) }) : null
4353
+ }
4354
+ ),
4355
+ /* @__PURE__ */ jsx24(
4356
+ Modal,
4357
+ {
4358
+ open: Boolean(authItem),
4359
+ onClose: closeAuthForm,
4360
+ title: authItem ? `${authItem.item.label} authentication` : "Authentication",
4361
+ description: "Enter the credentials required by Composio. They are sent securely to the ShellStack API.",
4362
+ size: "md",
4363
+ zIndex: 210,
4364
+ dataTest: `${dataTest}-auth-form`,
4365
+ children: authLoading ? /* @__PURE__ */ jsx24("p", { className: "text-sm text-ph-subtle", children: "Loading authentication fields..." }) : authItem ? /* @__PURE__ */ jsxs21(
4366
+ "form",
4367
+ {
4368
+ className: "space-y-4",
4369
+ onSubmit: (event) => void submitAuthForm(event),
4370
+ children: [
4371
+ authError ? /* @__PURE__ */ jsx24("p", { className: "text-sm text-ph-danger", role: "alert", children: authError }) : null,
4372
+ authItem.fields.length === 0 && authError ? /* @__PURE__ */ jsx24(
4373
+ Button,
4374
+ {
4375
+ type: "button",
4376
+ variant: "tertiary",
4377
+ onClick: () => void prepareConnection(authItem.item, authItem.scheme),
4378
+ children: "Try again"
4379
+ }
4380
+ ) : null,
4381
+ authItem.fields.map((field) => {
4382
+ var _a, _b;
4383
+ return /* @__PURE__ */ jsx24(
4384
+ Input,
4385
+ {
4386
+ label: field.label,
4387
+ type: (_a = field.type) != null ? _a : "password",
4388
+ required: true,
4389
+ value: (_b = authValues[field.name]) != null ? _b : "",
4390
+ onChange: (event) => setAuthValues((current) => __spreadProps(__spreadValues({}, current), {
4391
+ [field.name]: event.target.value
4392
+ })),
4393
+ autoComplete: "off",
4394
+ "data-test": `${dataTest}-auth-field-${field.name}`
4395
+ },
4396
+ field.name
4397
+ );
4398
+ }),
4399
+ /* @__PURE__ */ jsxs21("div", { className: "flex justify-end gap-2 border-t border-ph-border pt-4", children: [
4400
+ /* @__PURE__ */ jsx24(
4401
+ Button,
4402
+ {
4403
+ type: "button",
4404
+ variant: "tertiary",
4405
+ onClick: closeAuthForm,
4406
+ disabled: authSubmitting,
4407
+ children: "Cancel"
4408
+ }
4409
+ ),
4410
+ /* @__PURE__ */ jsx24(Button, { type: "submit", variant: "primary", disabled: authSubmitting, children: authSubmitting ? "Connecting..." : "Connect" })
4411
+ ] })
4412
+ ]
4413
+ }
4414
+ ) : null
4415
+ }
4416
+ )
4417
+ ] });
4418
+ }
4419
+
4420
+ // src/suite/components/suite-integration-rules.tsx
4421
+ import { useState as useState11 } from "react";
4422
+ import { Plus, Xmark as Xmark2 } from "iconoir-react";
4423
+ import { jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
4424
+ var ACTION_LABELS = {
4425
+ create_tides_task: "Create a Tides task",
4426
+ draft_time_entry: "Draft a time entry",
4427
+ create_suite_notification: "Send a suite notification"
4428
+ };
4429
+ function actionLabel(actionType) {
4430
+ var _a;
4431
+ return (_a = ACTION_LABELS[actionType]) != null ? _a : actionType;
4432
+ }
4433
+ function formatDateTime(value) {
4434
+ if (!value) return "\u2014";
4435
+ return new Date(value).toLocaleString(void 0, {
4436
+ dateStyle: "medium",
4437
+ timeStyle: "short"
4438
+ });
4439
+ }
4440
+ function SuiteIntegrationRules({
4441
+ canManage,
4442
+ projects,
4443
+ rules,
4444
+ events,
4445
+ loading = false,
4446
+ error = null,
4447
+ onCreate,
4448
+ onUpdate,
4449
+ onDelete,
4450
+ onRefresh,
4451
+ dataTest = "integration-rules"
4452
+ }) {
4453
+ const [formOpen, setFormOpen] = useState11(false);
4454
+ const [name, setName] = useState11("");
4455
+ const [trigger, setTrigger] = useState11("");
4456
+ const [actionType, setActionType] = useState11("create_tides_task");
4457
+ const [projectId, setProjectId] = useState11("");
4458
+ const [minutes, setMinutes] = useState11("");
4459
+ const [priority, setPriority] = useState11("low");
4460
+ const [submitting, setSubmitting] = useState11(false);
4461
+ const [formError, setFormError] = useState11(null);
4462
+ const [busyId, setBusyId] = useState11(null);
4463
+ const [ruleToDelete, setRuleToDelete] = useState11(null);
4464
+ const [deleteError, setDeleteError] = useState11(null);
4465
+ const needsProject = actionType === "create_tides_task" || actionType === "draft_time_entry";
4466
+ async function submitForm(event) {
4467
+ event.preventDefault();
4468
+ if (!onCreate) return;
4469
+ setSubmitting(true);
4470
+ setFormError(null);
4471
+ try {
4472
+ const actionConfig = {};
4473
+ if (projectId) actionConfig.project_id = projectId;
4474
+ if (actionType === "draft_time_entry" && minutes)
4475
+ actionConfig.minutes = minutes;
4476
+ if (actionType === "create_tides_task" && priority !== "low")
4477
+ actionConfig.priority = priority;
4478
+ await onCreate({
4479
+ name: name.trim(),
4480
+ trigger_event_type: trigger.trim(),
4481
+ action_type: actionType,
4482
+ action_config: actionConfig
4483
+ });
4484
+ setName("");
4485
+ setTrigger("");
4486
+ setProjectId("");
4487
+ setMinutes("");
4488
+ setPriority("low");
4489
+ setFormOpen(false);
4490
+ onRefresh == null ? void 0 : onRefresh();
4491
+ } catch (cause) {
4492
+ setFormError(
4493
+ cause instanceof Error ? cause.message : "Could not create the rule."
4494
+ );
4495
+ } finally {
4496
+ setSubmitting(false);
4497
+ }
4498
+ }
4499
+ async function toggleRule(rule) {
4500
+ if (!onUpdate) return;
4501
+ setBusyId(rule.id);
4502
+ try {
4503
+ await onUpdate(rule.id, { enabled: !rule.enabled });
4504
+ onRefresh == null ? void 0 : onRefresh();
4505
+ } finally {
4506
+ setBusyId(null);
4507
+ }
4508
+ }
4509
+ function requestDelete(rule) {
4510
+ setDeleteError(null);
4511
+ setRuleToDelete(rule);
4512
+ }
4513
+ async function deleteRule() {
4514
+ if (!onDelete || !ruleToDelete) return;
4515
+ setBusyId(ruleToDelete.id);
4516
+ setDeleteError(null);
4517
+ try {
4518
+ await onDelete(ruleToDelete.id);
4519
+ onRefresh == null ? void 0 : onRefresh();
4520
+ setRuleToDelete(null);
4521
+ } catch (cause) {
4522
+ setDeleteError(
4523
+ cause instanceof Error ? cause.message : "Could not delete the rule."
4524
+ );
4525
+ } finally {
4526
+ setBusyId(null);
4527
+ }
4528
+ }
4529
+ return /* @__PURE__ */ jsxs22("section", { className: "space-y-4", "data-test": dataTest, children: [
4530
+ /* @__PURE__ */ jsxs22(
4531
+ Card,
4532
+ {
4533
+ variant: "outlined",
4534
+ bodyClassName: "p-5",
4535
+ "data-test": `${dataTest}-card`,
4536
+ children: [
4537
+ /* @__PURE__ */ jsxs22("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between", children: [
4538
+ /* @__PURE__ */ jsxs22("div", { children: [
4539
+ /* @__PURE__ */ jsx25("h3", { className: "font-medium text-ph-ink", children: "Automation rules" }),
4540
+ /* @__PURE__ */ jsx25("p", { className: "mt-1 text-sm text-ph-subtle", children: "Turn incoming integration events into tasks, time entries, or notifications for this workspace." })
4541
+ ] }),
4542
+ canManage && onCreate ? /* @__PURE__ */ jsx25(
4543
+ Button,
4544
+ {
4545
+ type: "button",
4546
+ variant: "tertiary",
4547
+ size: "sm",
4548
+ onClick: () => setFormOpen((open) => !open),
4549
+ "data-test": `${dataTest}-new`,
4550
+ children: /* @__PURE__ */ jsxs22("span", { className: "flex items-center gap-1.5", children: [
4551
+ /* @__PURE__ */ jsx25(Plus, { className: "h-4 w-4", "aria-hidden": true }),
4552
+ "New rule"
4553
+ ] })
4554
+ }
4555
+ ) : null
4556
+ ] }),
4557
+ error ? /* @__PURE__ */ jsx25(
4558
+ Alert,
4559
+ {
4560
+ status: "danger",
4561
+ title: "Could not load rules",
4562
+ description: error
4563
+ }
4564
+ ) : null,
4565
+ loading ? /* @__PURE__ */ jsx25("p", { className: "mt-4 text-sm text-ph-subtle", children: "Loading rules..." }) : rules.length === 0 ? /* @__PURE__ */ jsx25(
4566
+ "div",
4567
+ {
4568
+ className: "mt-4 rounded-lg border border-dashed border-ph-border px-4 py-6 text-center",
4569
+ "data-test": `${dataTest}-empty`,
4570
+ children: /* @__PURE__ */ jsxs22("p", { className: "text-sm text-ph-subtle", children: [
4571
+ "No automation rules yet.",
4572
+ canManage ? " Create one to get started." : ""
4573
+ ] })
4574
+ }
4575
+ ) : /* @__PURE__ */ jsx25("ul", { className: "mt-4 space-y-2", "data-test": `${dataTest}-list`, children: rules.map((rule) => /* @__PURE__ */ jsxs22(
4576
+ "li",
4577
+ {
4578
+ className: "flex items-center justify-between gap-3 rounded-lg border border-ph-border bg-ph-muted px-3 py-3",
4579
+ "data-test": `${dataTest}-rule-${rule.id}`,
4580
+ children: [
4581
+ /* @__PURE__ */ jsxs22("div", { className: "min-w-0", children: [
4582
+ /* @__PURE__ */ jsx25("p", { className: "truncate text-sm font-medium text-ph-ink", children: rule.name }),
4583
+ /* @__PURE__ */ jsxs22("p", { className: "mt-0.5 truncate text-xs text-ph-subtle", children: [
4584
+ rule.trigger_event_type,
4585
+ " \u2192 ",
4586
+ actionLabel(rule.action_type)
4587
+ ] }),
4588
+ /* @__PURE__ */ jsxs22("p", { className: "mt-0.5 truncate text-xs text-ph-subtle", children: [
4589
+ "Last fired: ",
4590
+ formatDateTime(rule.last_fired_at)
4591
+ ] })
4592
+ ] }),
4593
+ canManage ? /* @__PURE__ */ jsxs22("div", { className: "flex shrink-0 items-center gap-2", children: [
4594
+ /* @__PURE__ */ jsx25(
4595
+ Toggle,
4596
+ {
4597
+ checked: rule.enabled,
4598
+ disabled: busyId === rule.id,
4599
+ onChange: () => void toggleRule(rule),
4600
+ "aria-label": `Enable ${rule.name}`
4601
+ }
4602
+ ),
4603
+ /* @__PURE__ */ jsx25(
4604
+ Button,
4605
+ {
4606
+ type: "button",
4607
+ variant: "tertiary",
4608
+ size: "sm",
4609
+ disabled: busyId === rule.id,
4610
+ onClick: () => requestDelete(rule),
4611
+ "aria-label": `Delete ${rule.name}`,
4612
+ children: /* @__PURE__ */ jsx25(Xmark2, { className: "h-4 w-4", "aria-hidden": true })
4613
+ }
4614
+ )
4615
+ ] }) : /* @__PURE__ */ jsx25(
4616
+ Badge,
4617
+ {
4618
+ variant: rule.enabled ? "success" : "neutral",
4619
+ size: "sm",
4620
+ children: rule.enabled ? "Enabled" : "Disabled"
4621
+ }
4622
+ )
4623
+ ]
4624
+ },
4625
+ rule.id
4626
+ )) }),
4627
+ canManage && formOpen && onCreate ? /* @__PURE__ */ jsxs22(
4628
+ "form",
4629
+ {
4630
+ className: "mt-4 space-y-3 rounded-lg border border-ph-border bg-ph-surface p-4",
4631
+ onSubmit: (event) => void submitForm(event),
4632
+ "data-test": `${dataTest}-form`,
4633
+ children: [
4634
+ formError ? /* @__PURE__ */ jsx25("p", { className: "text-sm text-ph-danger", role: "alert", children: formError }) : null,
4635
+ /* @__PURE__ */ jsx25(
4636
+ Input,
4637
+ {
4638
+ label: "Rule name",
4639
+ required: true,
4640
+ value: name,
4641
+ onChange: (event) => setName(event.target.value),
4642
+ placeholder: "GitHub issues to tasks",
4643
+ "data-test": `${dataTest}-name`
4644
+ }
4645
+ ),
4646
+ /* @__PURE__ */ jsx25(
4647
+ Input,
4648
+ {
4649
+ label: "Trigger event type",
4650
+ required: true,
4651
+ value: trigger,
4652
+ onChange: (event) => setTrigger(event.target.value),
4653
+ placeholder: "github.issue_assigned",
4654
+ helperText: "Provider event type, for example github.issue_assigned or googlecalendar.event_ended.",
4655
+ "data-test": `${dataTest}-trigger`
4656
+ }
4657
+ ),
4658
+ /* @__PURE__ */ jsx25(
4659
+ Select,
4660
+ {
4661
+ label: "Action",
4662
+ value: actionType,
4663
+ onChange: (event) => setActionType(
4664
+ event.target.value
4665
+ ),
4666
+ "data-test": `${dataTest}-action`,
4667
+ children: Object.entries(ACTION_LABELS).map(([value, label]) => /* @__PURE__ */ jsx25("option", { value, children: label }, value))
4668
+ }
4669
+ ),
4670
+ needsProject ? /* @__PURE__ */ jsxs22(
4671
+ Select,
4672
+ {
4673
+ label: "Project",
4674
+ value: projectId,
4675
+ onChange: (event) => setProjectId(event.target.value),
4676
+ "data-test": `${dataTest}-project`,
4677
+ children: [
4678
+ /* @__PURE__ */ jsx25("option", { value: "", children: "Choose a project..." }),
4679
+ projects.map((project) => /* @__PURE__ */ jsx25("option", { value: project.id, children: project.name }, project.id))
4680
+ ]
4681
+ }
4682
+ ) : null,
4683
+ actionType === "draft_time_entry" ? /* @__PURE__ */ jsx25(
4684
+ Input,
4685
+ {
4686
+ label: "Minutes (optional)",
4687
+ type: "number",
4688
+ min: "1",
4689
+ value: minutes,
4690
+ onChange: (event) => setMinutes(event.target.value),
4691
+ "data-test": `${dataTest}-minutes`
4692
+ }
4693
+ ) : null,
4694
+ actionType === "create_tides_task" ? /* @__PURE__ */ jsxs22(
4695
+ Select,
4696
+ {
4697
+ label: "Priority",
4698
+ value: priority,
4699
+ onChange: (event) => setPriority(event.target.value),
4700
+ "data-test": `${dataTest}-priority`,
4701
+ children: [
4702
+ /* @__PURE__ */ jsx25("option", { value: "low", children: "Low" }),
4703
+ /* @__PURE__ */ jsx25("option", { value: "medium", children: "Medium" }),
4704
+ /* @__PURE__ */ jsx25("option", { value: "high", children: "High" }),
4705
+ /* @__PURE__ */ jsx25("option", { value: "urgent", children: "Urgent" })
4706
+ ]
4707
+ }
4708
+ ) : null,
4709
+ /* @__PURE__ */ jsxs22("div", { className: "flex justify-end gap-2 border-t border-ph-border pt-3", children: [
4710
+ /* @__PURE__ */ jsx25(
4711
+ Button,
4712
+ {
4713
+ type: "button",
4714
+ variant: "tertiary",
4715
+ onClick: () => setFormOpen(false),
4716
+ disabled: submitting,
4717
+ children: "Cancel"
4718
+ }
4719
+ ),
4720
+ /* @__PURE__ */ jsx25(Button, { type: "submit", variant: "primary", disabled: submitting, children: submitting ? "Creating..." : "Create rule" })
4721
+ ] })
4722
+ ]
4723
+ }
4724
+ ) : null
4725
+ ]
4726
+ }
4727
+ ),
4728
+ /* @__PURE__ */ jsxs22(
4729
+ Modal,
4730
+ {
4731
+ open: ruleToDelete !== null,
4732
+ onClose: () => {
4733
+ if (busyId !== (ruleToDelete == null ? void 0 : ruleToDelete.id)) setRuleToDelete(null);
4734
+ },
4735
+ title: "Delete automation rule",
4736
+ description: "This action cannot be undone.",
4737
+ dataTest: `${dataTest}-delete-confirmation`,
4738
+ footer: /* @__PURE__ */ jsxs22("div", { className: "flex justify-end gap-2", children: [
4739
+ /* @__PURE__ */ jsx25(
4740
+ Button,
4741
+ {
4742
+ type: "button",
4743
+ variant: "tertiary",
4744
+ onClick: () => setRuleToDelete(null),
4745
+ disabled: busyId === (ruleToDelete == null ? void 0 : ruleToDelete.id),
4746
+ children: "Cancel"
4747
+ }
4748
+ ),
4749
+ /* @__PURE__ */ jsx25(
4750
+ Button,
4751
+ {
4752
+ type: "button",
4753
+ variant: "danger",
4754
+ onClick: () => void deleteRule(),
4755
+ disabled: busyId === (ruleToDelete == null ? void 0 : ruleToDelete.id),
4756
+ "data-test": `${dataTest}-delete-confirm`,
4757
+ children: busyId === (ruleToDelete == null ? void 0 : ruleToDelete.id) ? "Deleting..." : "Delete rule"
4758
+ }
4759
+ )
4760
+ ] }),
4761
+ children: [
4762
+ deleteError ? /* @__PURE__ */ jsx25("p", { className: "text-sm text-ph-danger", role: "alert", children: deleteError }) : null,
4763
+ /* @__PURE__ */ jsxs22("p", { className: "text-sm text-ph-ink", children: [
4764
+ "Delete ",
4765
+ ruleToDelete == null ? void 0 : ruleToDelete.name,
4766
+ "? This cannot be undone."
4767
+ ] })
4768
+ ]
4769
+ }
4770
+ ),
4771
+ /* @__PURE__ */ jsxs22(
4772
+ Card,
4773
+ {
4774
+ variant: "outlined",
4775
+ bodyClassName: "p-5",
4776
+ "data-test": `${dataTest}-events-card`,
4777
+ children: [
4778
+ /* @__PURE__ */ jsx25("h3", { className: "font-medium text-ph-ink", children: "Recent events" }),
4779
+ /* @__PURE__ */ jsx25("p", { className: "mt-1 text-sm text-ph-subtle", children: "The latest events received from your connected apps." }),
4780
+ events.length === 0 ? /* @__PURE__ */ jsx25(
4781
+ "p",
4782
+ {
4783
+ className: "mt-4 rounded-lg border border-dashed border-ph-border px-4 py-6 text-center text-sm text-ph-subtle",
4784
+ "data-test": `${dataTest}-events-empty`,
4785
+ children: "No integration events yet."
4786
+ }
4787
+ ) : /* @__PURE__ */ jsx25("ul", { className: "mt-4 space-y-2", "data-test": `${dataTest}-events-list`, children: events.map((event) => /* @__PURE__ */ jsxs22(
4788
+ "li",
4789
+ {
4790
+ className: "flex items-center justify-between gap-3 rounded-lg border border-ph-border bg-ph-muted px-3 py-2",
4791
+ children: [
4792
+ /* @__PURE__ */ jsxs22("div", { className: "min-w-0", children: [
4793
+ /* @__PURE__ */ jsxs22("p", { className: "truncate text-sm text-ph-ink", children: [
4794
+ event.provider,
4795
+ " \xB7 ",
4796
+ event.event_type
4797
+ ] }),
4798
+ /* @__PURE__ */ jsx25("p", { className: "mt-0.5 text-xs text-ph-subtle", children: formatDateTime(event.created_at) })
4799
+ ] }),
4800
+ /* @__PURE__ */ jsx25(
4801
+ Badge,
4802
+ {
4803
+ variant: event.processed_at ? "success" : "warning",
4804
+ size: "sm",
4805
+ children: event.processed_at ? "Processed" : "Pending"
4806
+ }
4807
+ )
4808
+ ]
4809
+ },
4810
+ event.id
4811
+ )) })
4812
+ ]
4813
+ }
4814
+ )
4815
+ ] });
4816
+ }
4817
+
4818
+ // src/suite/components/suite-clients-projects-directory.tsx
4819
+ import {
4820
+ useEffect as useEffect9,
4821
+ useMemo as useMemo2,
4822
+ useRef as useRef4,
4823
+ useState as useState12
4824
+ } from "react";
4825
+ import {
4826
+ ArrowLeft,
4827
+ MediaImagePlus as ImagePlus,
4828
+ MoreHoriz as MoreHorizontal,
4829
+ Plus as Plus2,
4830
+ Search as Search2,
4831
+ Trash
4832
+ } from "iconoir-react";
4833
+ import { Fragment as Fragment9, jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
4834
+ var DEFAULT_ICON = "castle-emblem";
4835
+ var DEFAULT_COLOR = "#6366f1";
4836
+ var DIRECTORY_ICON_OPTIONS = [
4837
+ ["arena", "Arena"],
4838
+ ["bridge", "Bridge"],
4839
+ ["capitol", "Capitol"],
4840
+ ["castle-emblem", "Castle"],
4841
+ ["castle-flag", "Castle flag"],
4842
+ ["lighthouse", "Lighthouse"],
4843
+ ["tower", "Tower"],
4844
+ ["wooden-sign", "Sign"],
4845
+ ["anvil", "Anvil"],
4846
+ ["book", "Book"],
4847
+ ["chessboard", "Chessboard"],
4848
+ ["cog", "Settings"],
4849
+ ["hammer", "Build"],
4850
+ ["light-bulb", "Idea"],
4851
+ ["microphone", "Communication"],
4852
+ ["quill-ink", "Writing"],
4853
+ ["scroll-unfurled", "Notes"],
4854
+ ["hourglass", "Planning"],
4855
+ ["wrench", "Tools"],
4856
+ ["archer", "Archer"],
4857
+ ["archery-target", "Target"],
4858
+ ["axe", "Axe"],
4859
+ ["campfire", "Campfire"],
4860
+ ["compass", "Compass"],
4861
+ ["crossed-swords", "Battle"],
4862
+ ["knight-helmet", "Knight"],
4863
+ ["mountains", "Mountains"],
4864
+ ["shield", "Shield"],
4865
+ ["ship-emblem", "Voyage"],
4866
+ ["sword", "Sword"],
4867
+ ["trophy", "Trophy"],
4868
+ ["apple", "Apple"],
4869
+ ["butterfly", "Butterfly"],
4870
+ ["clover", "Clover"],
4871
+ ["dragon", "Dragon"],
4872
+ ["fire", "Fire"],
4873
+ ["flower", "Flower"],
4874
+ ["flowers", "Flowers"],
4875
+ ["grass", "Grass"],
4876
+ ["leaf", "Leaf"],
4877
+ ["pine-tree", "Pine tree"],
4878
+ ["sun", "Sun"],
4879
+ ["water-drop", "Water"],
4880
+ ["aura", "Aura"],
4881
+ ["crystal-ball", "Crystal ball"],
4882
+ ["diamond", "Diamond"],
4883
+ ["explosion", "Explosion"],
4884
+ ["fire-symbol", "Fire symbol"],
4885
+ ["gem", "Gem"],
4886
+ ["lightning-trio", "Lightning"],
4887
+ ["potion", "Potion"],
4888
+ ["rune-stone", "Rune stone"],
4889
+ ["skull", "Skull"],
4890
+ ["tentacle", "Tentacle"],
4891
+ ["wolf-head", "Wolf"],
4892
+ ["anchor", "Anchor"],
4893
+ ["beer", "Beer"],
4894
+ ["carrot", "Carrot"],
4895
+ ["cat", "Cat"],
4896
+ ["crown", "Crown"],
4897
+ ["gold-bar", "Gold bar"],
4898
+ ["helmet", "Helmet"],
4899
+ ["key", "Key"],
4900
+ ["three-keys", "Keys"]
4901
+ ];
4902
+ function isDirectoryIcon(value) {
4903
+ return DIRECTORY_ICON_OPTIONS.some(([name]) => name === value);
4904
+ }
4905
+ function DirectoryIconPicker({
4906
+ value,
4907
+ preview,
4908
+ onFileChange,
4909
+ onClear,
4910
+ onChange
4911
+ }) {
4912
+ const iconRefs = useRef4({});
4913
+ function moveSelection(event, index) {
4914
+ var _a;
4915
+ let nextIndex = null;
4916
+ if (event.key === "ArrowRight" || event.key === "ArrowDown") {
4917
+ nextIndex = (index + 1) % DIRECTORY_ICON_OPTIONS.length;
4918
+ } else if (event.key === "ArrowLeft" || event.key === "ArrowUp") {
4919
+ nextIndex = (index - 1 + DIRECTORY_ICON_OPTIONS.length) % DIRECTORY_ICON_OPTIONS.length;
4920
+ } else if (event.key === "Home") {
4921
+ nextIndex = 0;
4922
+ } else if (event.key === "End") {
4923
+ nextIndex = DIRECTORY_ICON_OPTIONS.length - 1;
4924
+ }
4925
+ if (nextIndex === null) return;
4926
+ event.preventDefault();
4927
+ const [nextIcon] = DIRECTORY_ICON_OPTIONS[nextIndex];
4928
+ onChange(nextIcon);
4929
+ (_a = iconRefs.current[nextIcon]) == null ? void 0 : _a.focus();
4930
+ }
4931
+ return /* @__PURE__ */ jsxs23("div", { className: "space-y-2", children: [
4932
+ /* @__PURE__ */ jsx26("span", { className: "mb-1.5 block text-sm font-medium text-ph-ink", children: "Icon" }),
4933
+ /* @__PURE__ */ jsx26(
4934
+ "div",
4935
+ {
4936
+ role: "radiogroup",
4937
+ "aria-label": "Icon",
4938
+ className: "grid grid-cols-6 gap-1.5 rounded-lg border border-ph-border p-1.5 sm:grid-cols-8",
4939
+ children: DIRECTORY_ICON_OPTIONS.map(([name, label], index) => {
4940
+ const selected = value === name;
4941
+ return /* @__PURE__ */ jsx26(
4942
+ "button",
4943
+ {
4944
+ type: "button",
4945
+ role: "radio",
4946
+ "aria-label": label,
4947
+ "aria-checked": selected,
4948
+ tabIndex: selected ? 0 : -1,
4949
+ title: label,
4950
+ onClick: () => onChange(name),
4951
+ onKeyDown: (event) => moveSelection(event, index),
4952
+ ref: (element) => {
4953
+ iconRefs.current[name] = element;
4954
+ },
4955
+ className: cn(
4956
+ "flex h-9 items-center justify-center rounded-md text-ph-mutedtext hover:bg-ph-muted hover:text-ph-ink",
4957
+ selected && "bg-ph-muted text-ph-ink ring-2 ring-ph-brand ring-inset"
4958
+ ),
4959
+ children: /* @__PURE__ */ jsx26(
4960
+ "i",
4961
+ {
4962
+ className: cn("ra", `ra-${name}`, "text-base leading-none"),
4963
+ "aria-hidden": true
4964
+ }
4965
+ )
4966
+ },
4967
+ name
4968
+ );
4969
+ })
4970
+ }
4971
+ ),
4972
+ /* @__PURE__ */ jsxs23("div", { className: "rounded-lg border border-ph-border bg-ph-muted p-2", children: [
4973
+ /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-2 text-sm font-medium text-ph-ink", children: [
4974
+ /* @__PURE__ */ jsx26(ImagePlus, { className: "h-4 w-4", "aria-hidden": true }),
4975
+ " Custom image"
4976
+ ] }),
4977
+ /* @__PURE__ */ jsx26("p", { className: "mt-1 text-xs text-ph-mutedtext", children: "PNG, JPG, GIF, WebP, or SVG \u2014 max 512 KB." }),
4978
+ /* @__PURE__ */ jsx26(
4979
+ FileUpload,
4980
+ {
4981
+ accept: "image/png,image/jpeg,image/gif,image/webp,image/svg+xml,.svg",
4982
+ onChange: (event) => {
4983
+ var _a, _b;
4984
+ return onFileChange((_b = (_a = event.target.files) == null ? void 0 : _a[0]) != null ? _b : null);
4985
+ }
4986
+ }
4987
+ ),
4988
+ preview ? /* @__PURE__ */ jsxs23("div", { className: "mt-2 flex items-center gap-2", children: [
4989
+ /* @__PURE__ */ jsx26(
4990
+ "img",
4991
+ {
4992
+ src: preview,
4993
+ alt: "",
4994
+ className: "h-8 w-8 rounded object-cover"
4995
+ }
4996
+ ),
4997
+ /* @__PURE__ */ jsxs23(
4998
+ "button",
4999
+ {
5000
+ type: "button",
5001
+ className: "text-xs text-ph-mutedtext hover:text-ph-ink",
5002
+ onClick: onClear,
5003
+ children: [
5004
+ /* @__PURE__ */ jsx26(Trash, { className: "mr-1 inline h-3.5 w-3.5", "aria-hidden": true }),
5005
+ " Remove custom image"
5006
+ ]
5007
+ }
5008
+ )
5009
+ ] }) : null
5010
+ ] })
5011
+ ] });
5012
+ }
5013
+ function DirectoryIcon({
5014
+ item
5015
+ }) {
5016
+ const icon = item.icon && isDirectoryIcon(item.icon) ? item.icon : DEFAULT_ICON;
5017
+ return /* @__PURE__ */ jsx26(
5018
+ "span",
5019
+ {
5020
+ className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-white",
5021
+ style: { backgroundColor: item.color || "var(--ph-brand)" },
5022
+ children: item.icon_image ? (
5023
+ // eslint-disable-next-line @next/next/no-img-element
5024
+ /* @__PURE__ */ jsx26(
5025
+ "img",
5026
+ {
5027
+ src: item.icon_image,
5028
+ alt: "",
5029
+ className: "h-full w-full rounded-lg object-cover"
5030
+ }
5031
+ )
5032
+ ) : /* @__PURE__ */ jsx26(
5033
+ "i",
5034
+ {
5035
+ className: cn("ra", `ra-${icon}`, "text-base leading-none"),
5036
+ "aria-hidden": true
5037
+ }
5038
+ )
5039
+ }
5040
+ );
5041
+ }
5042
+ function SuiteClientsProjectsDirectory({
5043
+ clients,
5044
+ projects,
5045
+ canEdit = false,
5046
+ loading = false,
5047
+ error,
5048
+ onCreateClient,
5049
+ onUpdateClient,
5050
+ onDeleteClient,
5051
+ onCreateProject,
5052
+ onUpdateProject,
5053
+ onDeleteProject,
5054
+ dataTest = "suite-clients-projects-directory"
5055
+ }) {
5056
+ var _a;
5057
+ const [query, setQuery] = useState12("");
5058
+ const [selectedClientId, setSelectedClientId] = useState12(null);
5059
+ const [mobileDetail, setMobileDetail] = useState12(false);
5060
+ const [editing, setEditing] = useState12(null);
5061
+ const [draft, setDraft] = useState12({
5062
+ name: "",
5063
+ icon: DEFAULT_ICON,
5064
+ color: DEFAULT_COLOR
5065
+ });
5066
+ const [draftClientId, setDraftClientId] = useState12(null);
5067
+ const [preview, setPreview] = useState12(null);
5068
+ const [draftArchived, setDraftArchived] = useState12(false);
5069
+ const [confirming, setConfirming] = useState12(null);
5070
+ const [saving, setSaving] = useState12(false);
5071
+ const [mutationError, setMutationError] = useState12(null);
5072
+ const clientButtonRefs = useRef4({});
5073
+ const backToClientsRef = useRef4(null);
5074
+ const mobileFocusTarget = useRef4(null);
5075
+ useEffect9(() => {
5076
+ var _a2, _b;
5077
+ if (selectedClientId && clients.some((client) => client.id === selectedClientId))
5078
+ return;
5079
+ setSelectedClientId((_b = (_a2 = clients[0]) == null ? void 0 : _a2.id) != null ? _b : null);
5080
+ }, [clients, selectedClientId]);
5081
+ useEffect9(() => {
5082
+ var _a2, _b;
5083
+ const target = mobileFocusTarget.current;
5084
+ if (!target) return;
5085
+ mobileFocusTarget.current = null;
5086
+ if (target === "detail") (_a2 = backToClientsRef.current) == null ? void 0 : _a2.focus();
5087
+ else if (selectedClientId)
5088
+ (_b = clientButtonRefs.current[selectedClientId]) == null ? void 0 : _b.focus();
5089
+ }, [mobileDetail, selectedClientId]);
5090
+ const filteredClients = useMemo2(() => {
5091
+ const value = query.trim().toLowerCase();
5092
+ return [...clients].sort((a, b) => a.name.localeCompare(b.name)).filter((client) => !value || client.name.toLowerCase().includes(value));
5093
+ }, [clients, query]);
5094
+ const selectedClient = (_a = clients.find((client) => client.id === selectedClientId)) != null ? _a : null;
5095
+ const selectedProjects = projects.filter((project) => project.clientId === selectedClientId).sort((a, b) => a.name.localeCompare(b.name));
5096
+ function isMobileDirectoryView() {
5097
+ var _a2, _b;
5098
+ return (_b = (_a2 = window.matchMedia) == null ? void 0 : _a2.call(window, "(max-width: 1023px)").matches) != null ? _b : false;
5099
+ }
5100
+ function selectClient(client) {
5101
+ setSelectedClientId(client.id);
5102
+ setMobileDetail(true);
5103
+ if (isMobileDirectoryView()) mobileFocusTarget.current = "detail";
5104
+ }
5105
+ function moveClientSelection(event, index) {
5106
+ var _a2;
5107
+ let nextIndex = null;
5108
+ if (event.key === "ArrowDown") {
5109
+ nextIndex = (index + 1) % filteredClients.length;
5110
+ } else if (event.key === "ArrowUp") {
5111
+ nextIndex = (index - 1 + filteredClients.length) % filteredClients.length;
5112
+ } else if (event.key === "Home") {
5113
+ nextIndex = 0;
5114
+ } else if (event.key === "End") {
5115
+ nextIndex = filteredClients.length - 1;
5116
+ }
5117
+ if (nextIndex === null) return;
5118
+ event.preventDefault();
5119
+ const client = filteredClients[nextIndex];
5120
+ selectClient(client);
5121
+ if (!isMobileDirectoryView()) (_a2 = clientButtonRefs.current[client.id]) == null ? void 0 : _a2.focus();
5122
+ }
5123
+ function openNew(kind) {
5124
+ setMutationError(null);
5125
+ setDraft({
5126
+ name: "",
5127
+ icon: DEFAULT_ICON,
5128
+ color: DEFAULT_COLOR,
5129
+ iconImageFile: null,
5130
+ clearIconImage: false
5131
+ });
5132
+ setPreview(null);
5133
+ setDraftArchived(false);
5134
+ setDraftClientId(kind === "project" ? selectedClientId : null);
5135
+ setEditing({ kind: kind === "client" ? "new-client" : "new-project" });
5136
+ }
5137
+ function openEdit(item, kind) {
5138
+ var _a2;
5139
+ setMutationError(null);
5140
+ setDraft({
5141
+ name: item.name,
5142
+ icon: item.icon && isDirectoryIcon(item.icon) ? item.icon : DEFAULT_ICON,
5143
+ color: item.color || DEFAULT_COLOR,
5144
+ iconImageFile: null,
5145
+ clearIconImage: false
5146
+ });
5147
+ setPreview(item.icon_image || null);
5148
+ setDraftArchived(
5149
+ kind === "project" && Boolean(item.isArchived)
5150
+ );
5151
+ setDraftClientId(
5152
+ kind === "project" ? (_a2 = item.clientId) != null ? _a2 : null : null
5153
+ );
5154
+ setEditing({ kind, item });
5155
+ }
5156
+ function chooseImage(file) {
5157
+ if (!file) return;
5158
+ if (file.size > 512 * 1024) {
5159
+ setMutationError("Icon must be 512 KB or smaller.");
5160
+ return;
5161
+ }
5162
+ setMutationError(null);
5163
+ if (preview == null ? void 0 : preview.startsWith("blob:")) URL.revokeObjectURL(preview);
5164
+ setDraft((current) => __spreadProps(__spreadValues({}, current), {
5165
+ iconImageFile: file,
5166
+ clearIconImage: false
5167
+ }));
5168
+ setPreview(URL.createObjectURL(file));
5169
+ }
5170
+ function clearImage() {
5171
+ if (preview == null ? void 0 : preview.startsWith("blob:")) URL.revokeObjectURL(preview);
5172
+ setPreview(null);
5173
+ setDraft((current) => __spreadProps(__spreadValues({}, current), {
5174
+ iconImageFile: null,
5175
+ clearIconImage: true
5176
+ }));
5177
+ }
5178
+ function closeEditor() {
5179
+ if (!saving) setEditing(null);
5180
+ }
5181
+ async function save() {
5182
+ if (!editing || !draft.name.trim() || saving) return;
5183
+ setSaving(true);
5184
+ setMutationError(null);
5185
+ try {
5186
+ const fields = __spreadProps(__spreadValues({}, draft), { name: draft.name.trim() });
5187
+ if (editing.kind === "new-client") await (onCreateClient == null ? void 0 : onCreateClient(fields));
5188
+ else if (editing.kind === "new-project")
5189
+ await (onCreateProject == null ? void 0 : onCreateProject(__spreadProps(__spreadValues({}, fields), {
5190
+ clientId: draftClientId,
5191
+ isArchived: draftArchived
5192
+ })));
5193
+ else if (editing.kind === "client")
5194
+ await (onUpdateClient == null ? void 0 : onUpdateClient(editing.item, fields));
5195
+ else
5196
+ await (onUpdateProject == null ? void 0 : onUpdateProject(editing.item, __spreadProps(__spreadValues({}, fields), {
5197
+ clientId: draftClientId,
5198
+ isArchived: draftArchived
5199
+ })));
5200
+ setEditing(null);
5201
+ } catch (err) {
5202
+ setMutationError(
5203
+ err instanceof Error ? err.message : "Failed to save directory item."
5204
+ );
5205
+ } finally {
5206
+ setSaving(false);
5207
+ }
5208
+ }
5209
+ function remove(item, kind) {
5210
+ setConfirming({ item, kind });
5211
+ }
5212
+ async function toggleArchive(project) {
5213
+ var _a2;
5214
+ if (!onUpdateProject) return;
5215
+ try {
5216
+ await onUpdateProject(project, {
5217
+ name: project.name,
5218
+ icon: project.icon && isDirectoryIcon(project.icon) ? project.icon : DEFAULT_ICON,
5219
+ color: project.color || DEFAULT_COLOR,
5220
+ clientId: (_a2 = project.clientId) != null ? _a2 : null,
5221
+ isArchived: !project.isArchived
5222
+ });
5223
+ } catch (err) {
5224
+ setMutationError(
5225
+ err instanceof Error ? err.message : "Failed to update project."
5226
+ );
5227
+ }
5228
+ }
5229
+ async function confirmRemove() {
5230
+ if (!confirming) return;
5231
+ const { item, kind } = confirming;
5232
+ setConfirming(null);
5233
+ try {
5234
+ if (kind === "client")
5235
+ await (onDeleteClient == null ? void 0 : onDeleteClient(item));
5236
+ else await (onDeleteProject == null ? void 0 : onDeleteProject(item));
5237
+ } catch (err) {
5238
+ setMutationError(
5239
+ err instanceof Error ? err.message : "Failed to delete directory item."
5240
+ );
5241
+ }
5242
+ }
5243
+ return /* @__PURE__ */ jsxs23("div", { "data-test": dataTest, className: "space-y-3", children: [
5244
+ error || mutationError ? /* @__PURE__ */ jsx26(Alert, { status: "danger", live: "assertive", children: error || mutationError }) : null,
5245
+ loading ? /* @__PURE__ */ jsx26("p", { className: "text-sm text-ph-mutedtext", children: "Loading directory..." }) : /* @__PURE__ */ jsxs23("div", { className: "overflow-hidden rounded-xl border border-ph-border bg-ph-surface lg:grid lg:min-h-[22rem] lg:grid-cols-[minmax(14rem,18rem)_1fr]", children: [
5246
+ /* @__PURE__ */ jsxs23(
5247
+ "section",
5248
+ {
5249
+ className: cn(
5250
+ "border-ph-border lg:border-r",
5251
+ mobileDetail ? "hidden lg:block" : "block"
5252
+ ),
5253
+ children: [
5254
+ /* @__PURE__ */ jsxs23("div", { className: "flex h-14 items-center gap-2 border-b border-ph-border px-3", children: [
5255
+ /* @__PURE__ */ jsxs23("div", { className: "relative min-w-0 flex-1", children: [
5256
+ /* @__PURE__ */ jsx26(
5257
+ Search2,
5258
+ {
5259
+ className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-ph-mutedtext",
5260
+ "aria-hidden": true
5261
+ }
5262
+ ),
5263
+ /* @__PURE__ */ jsx26(
5264
+ Input,
5265
+ {
5266
+ hideLabel: true,
5267
+ "aria-label": "Search clients",
5268
+ placeholder: "Search clients...",
5269
+ value: query,
5270
+ onChange: (event) => setQuery(event.target.value),
5271
+ className: "pl-9"
5272
+ }
5273
+ )
5274
+ ] }),
5275
+ canEdit && onCreateClient ? /* @__PURE__ */ jsx26(
5276
+ Button,
5277
+ {
5278
+ size: "sm",
5279
+ variant: "primary",
5280
+ icon: /* @__PURE__ */ jsx26(Plus2, { className: "h-4 w-4" }),
5281
+ onClick: () => openNew("client"),
5282
+ children: "New client"
5283
+ }
5284
+ ) : null
5285
+ ] }),
5286
+ /* @__PURE__ */ jsx26(
5287
+ "ul",
5288
+ {
5289
+ role: "listbox",
5290
+ "aria-label": "Clients",
5291
+ className: "max-h-[28rem] overflow-y-auto p-1.5",
5292
+ children: filteredClients.length ? filteredClients.map((client, index) => {
5293
+ const selected = client.id === selectedClientId;
5294
+ const count = projects.filter(
5295
+ (project) => project.clientId === client.id
5296
+ ).length;
5297
+ return /* @__PURE__ */ jsx26("li", { role: "none", children: /* @__PURE__ */ jsxs23(
5298
+ "button",
5299
+ {
5300
+ type: "button",
5301
+ role: "option",
5302
+ "aria-selected": selected,
5303
+ tabIndex: selected ? 0 : -1,
5304
+ onClick: () => selectClient(client),
5305
+ onKeyDown: (event) => moveClientSelection(event, index),
5306
+ ref: (element) => {
5307
+ clientButtonRefs.current[client.id] = element;
5308
+ },
5309
+ className: cn(
5310
+ "flex w-full items-center gap-2.5 rounded-lg px-2.5 py-2 text-left",
5311
+ selected ? "bg-ph-muted" : "hover:bg-ph-canvas"
5312
+ ),
5313
+ children: [
5314
+ /* @__PURE__ */ jsx26(DirectoryIcon, { item: client }),
5315
+ /* @__PURE__ */ jsxs23("span", { className: "min-w-0 flex-1", children: [
5316
+ /* @__PURE__ */ jsx26("span", { className: "block truncate text-sm font-medium text-ph-ink", children: client.name }),
5317
+ /* @__PURE__ */ jsxs23("span", { className: "text-xs text-ph-mutedtext", children: [
5318
+ count,
5319
+ " project",
5320
+ count === 1 ? "" : "s"
5321
+ ] })
5322
+ ] })
5323
+ ]
5324
+ }
5325
+ ) }, client.id);
5326
+ }) : /* @__PURE__ */ jsx26("li", { className: "px-3 py-8 text-center text-sm text-ph-mutedtext", children: clients.length ? "No matches." : "Add a client to get started." })
5327
+ }
5328
+ )
5329
+ ]
5330
+ }
5331
+ ),
5332
+ /* @__PURE__ */ jsxs23(
5333
+ "section",
5334
+ {
5335
+ className: cn(
5336
+ "flex min-w-0 flex-col",
5337
+ mobileDetail ? "flex" : "hidden lg:flex"
5338
+ ),
5339
+ children: [
5340
+ /* @__PURE__ */ jsx26("div", { className: "flex min-h-14 items-center gap-2 border-b border-ph-border px-3 py-2", children: selectedClient ? /* @__PURE__ */ jsxs23(Fragment9, { children: [
5341
+ /* @__PURE__ */ jsx26(
5342
+ "button",
5343
+ {
5344
+ type: "button",
5345
+ ref: backToClientsRef,
5346
+ className: "inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-ph-mutedtext hover:bg-ph-muted lg:hidden",
5347
+ onClick: () => {
5348
+ mobileFocusTarget.current = "list";
5349
+ setMobileDetail(false);
5350
+ },
5351
+ "aria-label": "Back to clients",
5352
+ children: /* @__PURE__ */ jsx26(ArrowLeft, { className: "h-4 w-4" })
5353
+ }
5354
+ ),
5355
+ /* @__PURE__ */ jsx26(DirectoryIcon, { item: selectedClient }),
5356
+ /* @__PURE__ */ jsxs23("div", { className: "min-w-0 flex-1", children: [
5357
+ /* @__PURE__ */ jsx26("h3", { className: "truncate text-sm font-semibold text-ph-ink", children: selectedClient.name }),
5358
+ /* @__PURE__ */ jsxs23("p", { className: "text-xs text-ph-mutedtext", children: [
5359
+ selectedProjects.length,
5360
+ " project",
5361
+ selectedProjects.length === 1 ? "" : "s"
5362
+ ] })
5363
+ ] }),
5364
+ canEdit && (onUpdateClient || onDeleteClient) ? /* @__PURE__ */ jsx26("div", { onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsx26(
5365
+ DropdownMenu,
5366
+ {
5367
+ "aria-label": `Actions for ${selectedClient.name}`,
5368
+ align: "end",
5369
+ trigger: /* @__PURE__ */ jsxs23("span", { className: "inline-flex h-8 w-8 items-center justify-center rounded-md text-ph-mutedtext hover:bg-ph-muted hover:text-ph-ink", children: [
5370
+ /* @__PURE__ */ jsx26(MoreHorizontal, { className: "h-4 w-4", "aria-hidden": true }),
5371
+ /* @__PURE__ */ jsx26("span", { className: "sr-only", children: "Client actions" })
5372
+ ] }),
5373
+ children: /* @__PURE__ */ jsxs23("div", { className: "p-1", children: [
5374
+ onUpdateClient ? /* @__PURE__ */ jsx26(
5375
+ DropdownItem,
5376
+ {
5377
+ onClick: () => openEdit(selectedClient, "client"),
5378
+ children: "Edit client"
5379
+ }
5380
+ ) : null,
5381
+ onDeleteClient ? /* @__PURE__ */ jsx26(
5382
+ DropdownItem,
5383
+ {
5384
+ className: "text-red-600",
5385
+ onClick: () => void remove(selectedClient, "client"),
5386
+ children: "Delete client"
5387
+ }
5388
+ ) : null
5389
+ ] })
5390
+ }
5391
+ ) }) : null
5392
+ ] }) : /* @__PURE__ */ jsx26("p", { className: "text-sm text-ph-mutedtext", children: "Select a client" }) }),
5393
+ canEdit && onCreateProject ? /* @__PURE__ */ jsx26("div", { className: "border-b border-ph-border p-3", children: /* @__PURE__ */ jsx26(
5394
+ Button,
5395
+ {
5396
+ variant: "primary",
5397
+ icon: /* @__PURE__ */ jsx26(Plus2, { className: "h-4 w-4" }),
5398
+ onClick: () => openNew("project"),
5399
+ children: "Add project"
5400
+ }
5401
+ ) }) : null,
5402
+ /* @__PURE__ */ jsx26("ul", { className: "flex-1 p-2", children: selectedClient && selectedProjects.length ? selectedProjects.map((project) => /* @__PURE__ */ jsx26("li", { children: /* @__PURE__ */ jsxs23(
5403
+ "div",
5404
+ {
5405
+ className: "flex items-center gap-3 rounded-lg px-2 py-1 hover:bg-ph-canvas",
5406
+ onClick: () => openEdit(project, "project"),
5407
+ children: [
5408
+ /* @__PURE__ */ jsxs23(
5409
+ "button",
5410
+ {
5411
+ type: "button",
5412
+ className: "flex min-w-0 flex-1 items-center gap-3 rounded-lg py-1 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
5413
+ onClick: () => openEdit(project, "project"),
5414
+ children: [
5415
+ /* @__PURE__ */ jsx26(DirectoryIcon, { item: project }),
5416
+ /* @__PURE__ */ jsxs23("span", { className: "min-w-0 flex-1", children: [
5417
+ /* @__PURE__ */ jsx26(
5418
+ "span",
5419
+ {
5420
+ className: cn(
5421
+ "block truncate text-sm font-medium",
5422
+ project.isArchived ? "text-ph-mutedtext line-through" : "text-ph-ink"
5423
+ ),
5424
+ children: project.name
5425
+ }
5426
+ ),
5427
+ /* @__PURE__ */ jsx26("span", { className: "text-xs text-ph-mutedtext", children: project.isArchived ? "Archived" : "Active" })
5428
+ ] })
5429
+ ]
5430
+ }
5431
+ ),
5432
+ canEdit && (onUpdateProject || onDeleteProject) ? /* @__PURE__ */ jsx26("div", { onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsx26(
5433
+ DropdownMenu,
5434
+ {
5435
+ "aria-label": `Actions for ${project.name}`,
5436
+ align: "end",
5437
+ trigger: /* @__PURE__ */ jsxs23("span", { className: "inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-ph-mutedtext hover:bg-ph-muted hover:text-ph-ink", children: [
5438
+ /* @__PURE__ */ jsx26(
5439
+ MoreHorizontal,
5440
+ {
5441
+ className: "h-4 w-4",
5442
+ "aria-hidden": true
5443
+ }
5444
+ ),
5445
+ /* @__PURE__ */ jsx26("span", { className: "sr-only", children: "Project actions" })
5446
+ ] }),
5447
+ children: /* @__PURE__ */ jsxs23("div", { className: "p-1", children: [
5448
+ onUpdateProject ? /* @__PURE__ */ jsxs23(Fragment9, { children: [
5449
+ /* @__PURE__ */ jsx26(
5450
+ DropdownItem,
5451
+ {
5452
+ onClick: () => openEdit(project, "project"),
5453
+ children: "Edit project"
5454
+ }
5455
+ ),
5456
+ /* @__PURE__ */ jsx26(
5457
+ DropdownItem,
5458
+ {
5459
+ onClick: () => void toggleArchive(project),
5460
+ children: project.isArchived ? "Unarchive project" : "Archive project"
5461
+ }
5462
+ )
5463
+ ] }) : null,
5464
+ onDeleteProject ? /* @__PURE__ */ jsx26(
5465
+ DropdownItem,
5466
+ {
5467
+ className: "text-red-600",
5468
+ onClick: () => void remove(project, "project"),
5469
+ children: "Delete project"
5470
+ }
5471
+ ) : null
5472
+ ] })
5473
+ }
5474
+ ) }) : null
5475
+ ]
5476
+ }
5477
+ ) }, project.id)) : /* @__PURE__ */ jsx26("li", { className: "px-3 py-8 text-center text-sm text-ph-mutedtext", children: selectedClient ? "No projects yet." : "Select a client to see its projects." }) })
5478
+ ]
5479
+ }
5480
+ )
5481
+ ] }),
5482
+ /* @__PURE__ */ jsx26(
5483
+ Modal,
5484
+ {
5485
+ open: editing !== null,
5486
+ onClose: closeEditor,
5487
+ title: (editing == null ? void 0 : editing.kind.includes("project")) ? editing.kind === "new-project" ? "New project" : "Edit project" : (editing == null ? void 0 : editing.kind) === "new-client" ? "New client" : "Edit client",
5488
+ footer: /* @__PURE__ */ jsxs23("div", { className: "flex justify-end gap-2", children: [
5489
+ /* @__PURE__ */ jsx26(Button, { variant: "tertiary", onClick: closeEditor, disabled: saving, children: "Cancel" }),
5490
+ /* @__PURE__ */ jsx26(
5491
+ Button,
5492
+ {
5493
+ variant: "primary",
5494
+ onClick: () => void save(),
5495
+ disabled: saving || !draft.name.trim(),
5496
+ children: saving ? "Saving..." : "Save"
5497
+ }
5498
+ )
5499
+ ] }),
5500
+ children: /* @__PURE__ */ jsxs23("div", { className: "space-y-3", children: [
5501
+ /* @__PURE__ */ jsx26(
5502
+ Input,
5503
+ {
5504
+ label: "Name",
5505
+ autoFocus: true,
5506
+ value: draft.name,
5507
+ onChange: (event) => setDraft(__spreadProps(__spreadValues({}, draft), { name: event.target.value }))
5508
+ }
5509
+ ),
5510
+ /* @__PURE__ */ jsx26(
5511
+ DirectoryIconPicker,
5512
+ {
5513
+ value: isDirectoryIcon(draft.icon) ? draft.icon : DEFAULT_ICON,
5514
+ preview,
5515
+ onFileChange: chooseImage,
5516
+ onClear: clearImage,
5517
+ onChange: (icon) => {
5518
+ if (preview) clearImage();
5519
+ setDraft((current) => __spreadProps(__spreadValues({}, current), { icon }));
5520
+ }
5521
+ }
5522
+ ),
5523
+ /* @__PURE__ */ jsxs23("div", { children: [
5524
+ /* @__PURE__ */ jsx26("span", { className: "mb-1.5 block text-sm font-medium text-ph-ink", children: "Colour" }),
5525
+ /* @__PURE__ */ jsxs23("div", { className: "flex flex-wrap gap-2", children: [
5526
+ [
5527
+ "#6366f1",
5528
+ "#0ea5e9",
5529
+ "#10b981",
5530
+ "#f59e0b",
5531
+ "#ef4444",
5532
+ "#a855f7",
5533
+ "#64748b"
5534
+ ].map((color) => /* @__PURE__ */ jsx26(
5535
+ "button",
5536
+ {
5537
+ type: "button",
5538
+ "aria-label": `Colour ${color}`,
5539
+ "aria-pressed": draft.color === color,
5540
+ onClick: () => setDraft(__spreadProps(__spreadValues({}, draft), { color })),
5541
+ className: cn(
5542
+ "h-8 w-8 rounded-full ring-offset-2",
5543
+ draft.color === color && "ring-2 ring-ph-brand"
5544
+ ),
5545
+ style: { backgroundColor: color }
5546
+ },
5547
+ color
5548
+ )),
5549
+ /* @__PURE__ */ jsx26(
5550
+ Input,
5551
+ {
5552
+ hideLabel: true,
5553
+ label: "Custom colour",
5554
+ type: "color",
5555
+ value: draft.color,
5556
+ onChange: (event) => setDraft(__spreadProps(__spreadValues({}, draft), { color: event.target.value })),
5557
+ className: "h-8 w-8 p-0"
5558
+ }
5559
+ )
5560
+ ] })
5561
+ ] }),
5562
+ (editing == null ? void 0 : editing.kind.includes("project")) ? /* @__PURE__ */ jsxs23(
5563
+ Select,
5564
+ {
5565
+ label: "Client",
5566
+ value: draftClientId != null ? draftClientId : "",
5567
+ onChange: (event) => setDraftClientId(event.target.value || null),
5568
+ children: [
5569
+ /* @__PURE__ */ jsx26("option", { value: "", children: "No client" }),
5570
+ clients.map((client) => /* @__PURE__ */ jsx26("option", { value: client.id, children: client.name }, client.id))
5571
+ ]
5572
+ }
5573
+ ) : null,
5574
+ (editing == null ? void 0 : editing.kind.includes("project")) ? /* @__PURE__ */ jsxs23("label", { className: "flex items-center gap-2 text-sm text-ph-ink", children: [
5575
+ /* @__PURE__ */ jsx26(
5576
+ "input",
5577
+ {
5578
+ type: "checkbox",
5579
+ checked: draftArchived,
5580
+ onChange: (event) => setDraftArchived(event.target.checked)
5581
+ }
5582
+ ),
5583
+ "Archived"
5584
+ ] }) : null
5585
+ ] })
5586
+ }
5587
+ ),
5588
+ /* @__PURE__ */ jsx26(
5589
+ Modal,
5590
+ {
5591
+ open: confirming !== null,
5592
+ onClose: () => setConfirming(null),
5593
+ title: "Confirm deletion",
5594
+ footer: /* @__PURE__ */ jsxs23("div", { className: "flex justify-end gap-2", children: [
5595
+ /* @__PURE__ */ jsx26(Button, { variant: "tertiary", onClick: () => setConfirming(null), children: "Cancel" }),
5596
+ /* @__PURE__ */ jsx26(Button, { variant: "primary", onClick: () => void confirmRemove(), children: "Delete" })
5597
+ ] }),
5598
+ children: /* @__PURE__ */ jsxs23("p", { className: "text-sm text-ph-ink", children: [
5599
+ "Delete ",
5600
+ confirming == null ? void 0 : confirming.item.name,
5601
+ "? This cannot be undone."
5602
+ ] })
5603
+ }
5604
+ )
5605
+ ] });
3705
5606
  }
3706
5607
  export {
3707
5608
  APP_ACCENTS,
@@ -3729,6 +5630,7 @@ export {
3729
5630
  SUITE_SIDEBAR_WIDTH_COOKIE,
3730
5631
  SUITE_SIDEBAR_WIDTH_KEY,
3731
5632
  SUITE_SPRINGS,
5633
+ SUITE_THEME_COOKIE,
3732
5634
  SourceAppGlyph,
3733
5635
  SuiteAiPanel,
3734
5636
  SuiteAppIcon,
@@ -3736,9 +5638,11 @@ export {
3736
5638
  SuiteAppStrip,
3737
5639
  SuiteBottomNav,
3738
5640
  SuiteBreadcrumbs,
5641
+ SuiteClientsProjectsDirectory,
3739
5642
  SuiteEmptyState,
3740
5643
  SuiteIcon,
3741
5644
  SuiteIntegrationPicker,
5645
+ SuiteIntegrationRules,
3742
5646
  SuiteMobileDrawer,
3743
5647
  SuiteMobileHeader,
3744
5648
  SuiteMotionProvider,
@@ -3774,7 +5678,7 @@ export {
3774
5678
  appSwitcherMenuItemClass,
3775
5679
  appSwitcherTriggerClass,
3776
5680
  classifySuiteHref,
3777
- cn,
5681
+ cn2 as cn,
3778
5682
  consumePendingAskAiPrompt,
3779
5683
  consumeSuiteAskAiOpenFromLocation,
3780
5684
  getTodayGreeting,
@@ -3791,6 +5695,7 @@ export {
3791
5695
  sourceToSuiteApp,
3792
5696
  suiteAppBaseUrl,
3793
5697
  suiteAppLabel,
5698
+ suiteThemeBootScript,
3794
5699
  useIdleMount,
3795
5700
  useSidebarWidth,
3796
5701
  useSuiteTheme