@xenide-io/the-old-ui-theme 0.5.2 → 0.5.4

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 (59) hide show
  1. package/README.md +47 -34
  2. package/dist/{index-Od4pIP4F.d.mts → Chip-C5hRFAhe.d.mts} +10 -606
  3. package/dist/{index-Od4pIP4F.d.ts → Chip-C5hRFAhe.d.ts} +10 -606
  4. package/dist/{chunk-G53YLHVE.mjs → chunk-E5ZBQYEX.mjs} +59 -9
  5. package/dist/{chunk-RZXHZWUB.mjs → chunk-SBBKXV35.mjs} +174 -222
  6. package/dist/index.d.mts +4 -80
  7. package/dist/index.d.ts +4 -80
  8. package/dist/index.js +562 -3746
  9. package/dist/index.mjs +3 -765
  10. package/dist/suite.d.mts +56 -25
  11. package/dist/suite.d.ts +56 -25
  12. package/dist/suite.js +248 -107
  13. package/dist/suite.mjs +217 -105
  14. package/dist/ui.d.mts +608 -2
  15. package/dist/ui.d.ts +608 -2
  16. package/dist/ui.js +232 -230
  17. package/dist/ui.mjs +2 -2
  18. package/package.json +2 -6
  19. package/scripts/install-theme-dev.sh +60 -0
  20. package/scripts/sync-posthog-icons.mjs +75 -0
  21. package/src/app/globals.css +1 -0
  22. package/src/components/ui/Chip.tsx +36 -18
  23. package/src/components/ui/CommandPalette.test.tsx +41 -0
  24. package/src/components/ui/CommandPalette.tsx +58 -9
  25. package/src/components/ui/DropdownMenu.tsx +50 -11
  26. package/src/components/ui/SegmentedControl.tsx +2 -1
  27. package/src/components/ui/SettingsLayout.tsx +19 -5
  28. package/src/components/ui/ThemeSwitcher.tsx +12 -3
  29. package/src/components/ui/interaction-primitives.test.tsx +52 -0
  30. package/src/styles/suite-skin.css +53 -1
  31. package/src/styles/themes.css +445 -1656
  32. package/src/suite/components/app-switcher.tsx +32 -25
  33. package/src/suite/components/command-palette-host.tsx +20 -8
  34. package/src/suite/components/suite-app-layout.tsx +31 -10
  35. package/src/suite/components/suite-bottom-nav.tsx +23 -18
  36. package/src/suite/components/suite-layout.test.tsx +44 -0
  37. package/src/suite/components/suite-layout.tsx +3 -3
  38. package/src/suite/components/suite-mobile-drawer.tsx +12 -0
  39. package/src/suite/components/suite-mobile-header.tsx +15 -9
  40. package/src/suite/components/suite-notification-bell.tsx +23 -11
  41. package/src/suite/components/suite-settings-mobile-nav.tsx +2 -0
  42. package/src/suite/components/suite-sidebar.tsx +54 -43
  43. package/src/suite/components/suite-user-menu.tsx +54 -26
  44. package/src/suite/components/today-page-frame.tsx +1 -1
  45. package/src/suite/icons/app-accents.ts +38 -43
  46. package/src/suite/icons/glyph-parts.tsx +16 -9
  47. package/src/suite/icons/glyphs.ts +130 -99
  48. package/src/suite/lib/apps.ts +1 -1
  49. package/src/suite/lib/injected.ts +12 -11
  50. package/src/suite/lib/use-sidebar-width.ts +23 -8
  51. package/src/components/charts/index.ts +0 -9
  52. package/src/components/charts/quill/BarChart.tsx +0 -85
  53. package/src/components/charts/quill/FunnelChart.tsx +0 -49
  54. package/src/components/charts/quill/InsightShell.tsx +0 -27
  55. package/src/components/charts/quill/MetricCard.tsx +0 -44
  56. package/src/components/charts/quill/PieChart.tsx +0 -81
  57. package/src/components/charts/quill/Sparkline.tsx +0 -57
  58. package/src/components/charts/quill/TimeSeriesLineChart.tsx +0 -62
  59. package/src/components/charts/quill/index.ts +0 -9
package/dist/ui.js CHANGED
@@ -1252,7 +1252,12 @@ function SettingsNav(_a) {
1252
1252
  "a",
1253
1253
  {
1254
1254
  href: item.href,
1255
- className: cn("ph-settings-nav__item", item.active && "ph-settings-nav__item--active"),
1255
+ id: `settings-nav-${item.id}`,
1256
+ "data-test": `settings-nav-${item.id}`,
1257
+ className: cn(
1258
+ "ph-settings-nav__item",
1259
+ item.active && "ph-settings-nav__item--active"
1260
+ ),
1256
1261
  "aria-current": item.active ? "page" : void 0,
1257
1262
  children: [
1258
1263
  item.icon ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "ph-settings-nav__icon", children: item.icon }) : null,
@@ -1341,7 +1346,14 @@ function DropdownContent({
1341
1346
  sticky: "partial",
1342
1347
  children: [
1343
1348
  children,
1344
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DropdownPrimitive.Arrow, { className: "ph-dropdown-arrow", width: 12, height: 6 })
1349
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1350
+ DropdownPrimitive.Arrow,
1351
+ {
1352
+ className: "ph-dropdown-arrow",
1353
+ width: 12,
1354
+ height: 6
1355
+ }
1356
+ )
1345
1357
  ]
1346
1358
  }
1347
1359
  ) });
@@ -1349,6 +1361,8 @@ function DropdownContent({
1349
1361
  function DropdownMenu(_a) {
1350
1362
  var _b = _a, {
1351
1363
  trigger,
1364
+ triggerId,
1365
+ triggerDataTest,
1352
1366
  children,
1353
1367
  align,
1354
1368
  side,
@@ -1366,6 +1380,8 @@ function DropdownMenu(_a) {
1366
1380
  disabled
1367
1381
  } = _b, props = __objRest(_b, [
1368
1382
  "trigger",
1383
+ "triggerId",
1384
+ "triggerDataTest",
1369
1385
  "children",
1370
1386
  "align",
1371
1387
  "side",
@@ -1390,7 +1406,17 @@ function DropdownMenu(_a) {
1390
1406
  onOpenChange,
1391
1407
  modal,
1392
1408
  children: [
1393
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DropdownPrimitive.Trigger, { asChild: true, disabled, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "ph-dropdown-trigger", "aria-label": ariaLabel, children: trigger }) }),
1409
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DropdownPrimitive.Trigger, { asChild: true, disabled, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1410
+ "button",
1411
+ {
1412
+ type: "button",
1413
+ id: triggerId,
1414
+ "data-test": triggerDataTest,
1415
+ className: "ph-dropdown-trigger",
1416
+ "aria-label": ariaLabel,
1417
+ children: trigger
1418
+ }
1419
+ ) }),
1394
1420
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1395
1421
  DropdownContent,
1396
1422
  {
@@ -1478,7 +1504,15 @@ function DropdownButton(_a) {
1478
1504
  split && "ph-btn-split"
1479
1505
  ),
1480
1506
  "aria-label": ariaLabel,
1481
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ButtonChrome, { label, icon, sideIcon: trailing, split })
1507
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1508
+ ButtonChrome,
1509
+ {
1510
+ label,
1511
+ icon,
1512
+ sideIcon: trailing,
1513
+ split
1514
+ }
1515
+ )
1482
1516
  }
1483
1517
  ) }),
1484
1518
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
@@ -1499,7 +1533,15 @@ function DropdownButton(_a) {
1499
1533
  ) }));
1500
1534
  }
1501
1535
  function DropdownItem(_a) {
1502
- var _b = _a, { className, children, disabled } = _b, props = __objRest(_b, ["className", "children", "disabled"]);
1536
+ var _b = _a, {
1537
+ className,
1538
+ children,
1539
+ disabled
1540
+ } = _b, props = __objRest(_b, [
1541
+ "className",
1542
+ "children",
1543
+ "disabled"
1544
+ ]);
1503
1545
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DropdownPrimitive.Item, { asChild: true, disabled, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1504
1546
  "button",
1505
1547
  __spreadProps(__spreadValues({
@@ -1515,11 +1557,24 @@ function DropdownRadioGroup(props) {
1515
1557
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DropdownPrimitive.RadioGroup, __spreadValues({}, props));
1516
1558
  }
1517
1559
  function DropdownRadioItem(_a) {
1518
- var _b = _a, { className, children } = _b, props = __objRest(_b, ["className", "children"]);
1519
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(DropdownPrimitive.RadioItem, __spreadProps(__spreadValues({ className: cn("ph-dropdown-item ph-dropdown-radio-item", className) }, props), { children: [
1520
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ph-dropdown-item-indicator", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DropdownPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconCheck, { className: "h-3.5 w-3.5" }) }) }),
1521
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children })
1522
- ] }));
1560
+ var _b = _a, {
1561
+ className,
1562
+ children
1563
+ } = _b, props = __objRest(_b, [
1564
+ "className",
1565
+ "children"
1566
+ ]);
1567
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1568
+ DropdownPrimitive.RadioItem,
1569
+ __spreadProps(__spreadValues({
1570
+ className: cn("ph-dropdown-item ph-dropdown-radio-item", className)
1571
+ }, props), {
1572
+ children: [
1573
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ph-dropdown-item-indicator", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DropdownPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconCheck, { className: "h-3.5 w-3.5" }) }) }),
1574
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children })
1575
+ ]
1576
+ })
1577
+ );
1523
1578
  }
1524
1579
 
1525
1580
  // src/components/ui/Input.tsx
@@ -2388,177 +2443,65 @@ var THEMES = [
2388
2443
  colorScheme: "dark",
2389
2444
  group: "HedgeHog"
2390
2445
  },
2391
- {
2392
- id: "catppuccin-light",
2393
- name: "Catppuccin Light",
2394
- description: "Catppuccin Latte \u2014 soft pastels and mauve accent.",
2395
- colorScheme: "light",
2396
- group: "Catppuccin"
2397
- },
2398
- {
2399
- id: "catppuccin-dark",
2400
- name: "Catppuccin Dark",
2401
- description: "Catppuccin Mocha \u2014 deep base and lavender peach.",
2402
- colorScheme: "dark",
2403
- group: "Catppuccin"
2404
- },
2405
- {
2406
- id: "sheets",
2407
- name: "Sheets",
2408
- description: "Spreadsheet green on office neutrals.",
2409
- colorScheme: "light",
2410
- group: "Productivity"
2411
- },
2412
- {
2413
- id: "sheets-dark",
2414
- name: "Sheets Dark",
2415
- description: "Dark shell with spreadsheet green accent.",
2416
- colorScheme: "dark",
2417
- group: "Productivity"
2418
- },
2419
2446
  {
2420
2447
  id: "note",
2421
- name: "Note",
2448
+ name: "Note Light",
2422
2449
  description: "Notebook purple on lavender-white chrome.",
2423
2450
  colorScheme: "light",
2424
- group: "Productivity"
2451
+ group: "Note"
2425
2452
  },
2426
2453
  {
2427
2454
  id: "note-dark",
2428
2455
  name: "Note Dark",
2429
2456
  description: "Dark notebook purple \u2014 night reading mode.",
2430
2457
  colorScheme: "dark",
2431
- group: "Productivity"
2432
- },
2433
- {
2434
- id: "presentation",
2435
- name: "Presentation",
2436
- description: "Orange-red on office neutrals.",
2437
- colorScheme: "light",
2438
- group: "Productivity"
2439
- },
2440
- {
2441
- id: "presentation-dark",
2442
- name: "Presentation Dark",
2443
- description: "Dark shell with orange-red accent.",
2444
- colorScheme: "dark",
2445
- group: "Productivity"
2446
- },
2447
- {
2448
- id: "socials",
2449
- name: "Socials",
2450
- description: "Meta blue accent on familiar social greys.",
2451
- colorScheme: "light",
2452
- group: "Social"
2453
- },
2454
- {
2455
- id: "socials-dark",
2456
- name: "Socials Dark",
2457
- description: "Social dark mode \u2014 #18191A base and Meta blue.",
2458
- colorScheme: "dark",
2459
- group: "Social"
2460
- },
2461
- {
2462
- id: "chats-light",
2463
- name: "Chats Light",
2464
- description: "Aubergine brand on clean workspace neutrals.",
2465
- colorScheme: "light",
2466
- group: "Chats"
2467
- },
2468
- {
2469
- id: "chats-dark",
2470
- name: "Chats Dark",
2471
- description: "Dark sidebar \u2014 #1A1D21 base and green highlights.",
2472
- colorScheme: "dark",
2473
- group: "Chats"
2474
- },
2475
- {
2476
- id: "xenide-light",
2477
- name: "Xenide Light",
2478
- description: "xenide.io light \u2014 Excel green + purple on neutral greys.",
2479
- colorScheme: "light",
2480
- group: "Xenide"
2481
- },
2482
- {
2483
- id: "xenide-dark",
2484
- name: "Xenide Dark",
2485
- description: "xenide.io dark \u2014 #0A0A0F base, emerald green + violet.",
2486
- colorScheme: "dark",
2487
- group: "Xenide"
2488
- },
2489
- {
2490
- id: "bikini-bottom",
2491
- name: "Bikini Bottom",
2492
- description: "SpongeBob palette \u2014 ocean canvas, readable surfaces, character accents.",
2493
- colorScheme: "light",
2494
- group: "Fun"
2495
- },
2496
- {
2497
- id: "bikini-bottom-dark",
2498
- name: "Bikini Bottom Dark",
2499
- description: "Bikini Bottom at night \u2014 deep ocean with neon character accents.",
2500
- colorScheme: "dark",
2501
- group: "Fun"
2458
+ group: "Note"
2502
2459
  },
2503
2460
  {
2504
2461
  id: "turtletime",
2505
- name: "TurtleTime",
2462
+ name: "TurtleTime Light",
2506
2463
  description: "Turtle mascot palette \u2014 shell orange, leaf green, and warm cream.",
2507
2464
  colorScheme: "light",
2508
- group: "Fun"
2465
+ group: "TurtleTime"
2509
2466
  },
2510
2467
  {
2511
2468
  id: "turtletime-dark",
2512
2469
  name: "TurtleTime Dark",
2513
2470
  description: "TurtleTime after dusk \u2014 dark olive shell with bright orange clock accents.",
2514
2471
  colorScheme: "dark",
2515
- group: "Fun"
2516
- },
2517
- {
2518
- id: "github-light",
2519
- name: "GitHub Light",
2520
- description: "GitHub Primer \u2014 #f6f8fa canvas and emphasis blue accents.",
2521
- colorScheme: "light",
2522
- group: "GitHub"
2523
- },
2524
- {
2525
- id: "github-dark",
2526
- name: "GitHub Dark",
2527
- description: "GitHub dark dimmed \u2014 #0d1117 base and #4493f8 links.",
2528
- colorScheme: "dark",
2529
- group: "GitHub"
2472
+ group: "TurtleTime"
2530
2473
  },
2531
2474
  {
2532
2475
  id: "rosepine-light",
2533
- name: "Ros\xE9 Pine Dawn",
2476
+ name: "Ros\xE9 Pine Light",
2534
2477
  description: "Ros\xE9 Pine Dawn \u2014 warm paper base with muted iris accents.",
2535
2478
  colorScheme: "light",
2536
2479
  group: "Ros\xE9 Pine"
2537
2480
  },
2538
2481
  {
2539
2482
  id: "rosepine-dark",
2540
- name: "Ros\xE9 Pine",
2483
+ name: "Ros\xE9 Pine Dark",
2541
2484
  description: "Ros\xE9 Pine Main \u2014 moonlit base with iris and love accents.",
2542
2485
  colorScheme: "dark",
2543
2486
  group: "Ros\xE9 Pine"
2544
2487
  },
2545
2488
  {
2546
- id: "notion",
2547
- name: "Notion",
2548
- description: "Notion light \u2014 warm off-white canvas, Notion blue accent, signature dark text.",
2489
+ id: "kraken-light",
2490
+ name: "Kraken Light",
2491
+ description: "Medium-inspired warm paper, editorial black actions, and publication yellow.",
2549
2492
  colorScheme: "light",
2550
- group: "Productivity"
2493
+ group: "Kraken"
2551
2494
  },
2552
2495
  {
2553
- id: "notion-dark",
2554
- name: "Notion Dark",
2555
- description: "Notion dark mode \u2014 dark charcoal canvas with blue accent and clean typography.",
2496
+ id: "kraken-dark",
2497
+ name: "Kraken Dark",
2498
+ description: "Medium-inspired night reading canvas with white actions and publication yellow.",
2556
2499
  colorScheme: "dark",
2557
- group: "Productivity"
2500
+ group: "Kraken"
2558
2501
  },
2559
2502
  {
2560
2503
  id: "deepsea-light",
2561
- name: "Tokyo Night Day",
2504
+ name: "Tokyo Day",
2562
2505
  description: "Tokyo Night Day \u2014 soft bluish greys with vivid blue and purple accents.",
2563
2506
  colorScheme: "light",
2564
2507
  group: "Tokyo Night"
@@ -2571,21 +2514,23 @@ var THEMES = [
2571
2514
  group: "Tokyo Night"
2572
2515
  },
2573
2516
  {
2574
- id: "kraken-light",
2575
- name: "Kraken Light",
2576
- description: "Medium-inspired warm paper, editorial black actions, and classic publication yellow.",
2517
+ id: "malibu-light",
2518
+ name: "Malibu Light",
2519
+ description: "Sun-bleached GTA San Andreas palette with hot pink, sunset orange, and lime signals.",
2577
2520
  colorScheme: "light",
2578
- group: "Kraken"
2521
+ group: "Malibu"
2579
2522
  },
2580
2523
  {
2581
- id: "kraken-dark",
2582
- name: "Kraken Dark",
2583
- description: "Medium-inspired night reading canvas with white actions and classic publication yellow.",
2524
+ id: "malibu-dark",
2525
+ name: "Malibu Dark",
2526
+ description: "Deep plum command deck with hot pink, lime, cyan, and sunset signals.",
2584
2527
  colorScheme: "dark",
2585
- group: "Kraken"
2528
+ group: "Malibu"
2586
2529
  }
2587
2530
  ];
2588
- var THEME_GROUPS = Array.from(new Set(THEMES.map((theme) => theme.group)));
2531
+ var THEME_GROUPS = Array.from(
2532
+ new Set(THEMES.map((theme) => theme.group))
2533
+ );
2589
2534
  function isThemeId(value) {
2590
2535
  return THEMES.some((theme) => theme.id === value);
2591
2536
  }
@@ -2593,7 +2538,7 @@ function isThemeId(value) {
2593
2538
  // src/themes/init-theme.ts
2594
2539
  var STORAGE_KEY = "ph-theme";
2595
2540
  var VALID_THEMES = THEMES.map((theme) => theme.id);
2596
- var THEME_INIT_SCRIPT = `(function(){try{var k="${STORAGE_KEY}",d="${DEFAULT_THEME_ID}",v=${JSON.stringify(VALID_THEMES)},t=localStorage.getItem(k);if(t==="xenide")t="xenide-light";if(!t||v.indexOf(t)<0)t=d;document.documentElement.setAttribute("data-theme",t)}catch(e){document.documentElement.setAttribute("data-theme","${DEFAULT_THEME_ID}")}})();`;
2541
+ var THEME_INIT_SCRIPT = `(function(){try{var k="${STORAGE_KEY}",d="${DEFAULT_THEME_ID}",v=${JSON.stringify(VALID_THEMES)},t=localStorage.getItem(k);if(!t||v.indexOf(t)<0)t=d;document.documentElement.setAttribute("data-theme",t)}catch(e){document.documentElement.setAttribute("data-theme","${DEFAULT_THEME_ID}")}})();`;
2597
2542
  function persistTheme(themeId) {
2598
2543
  if (typeof window === "undefined") {
2599
2544
  return;
@@ -2609,9 +2554,6 @@ function readStoredTheme() {
2609
2554
  return DEFAULT_THEME_ID;
2610
2555
  }
2611
2556
  const stored = localStorage.getItem(STORAGE_KEY);
2612
- if (stored === "xenide") {
2613
- return "xenide-light";
2614
- }
2615
2557
  return stored && isThemeId(stored) ? stored : DEFAULT_THEME_ID;
2616
2558
  }
2617
2559
 
@@ -2637,7 +2579,7 @@ function ThemeManager({ children }) {
2637
2579
  var import_react15 = require("react");
2638
2580
  var import_jsx_runtime27 = require("react/jsx-runtime");
2639
2581
  function ThemeSwitcher({ className }) {
2640
- const [themeId, setThemeId] = (0, import_react15.useState)("hedgehog-light");
2582
+ const [themeId, setThemeId] = (0, import_react15.useState)(DEFAULT_THEME_ID);
2641
2583
  (0, import_react15.useEffect)(() => {
2642
2584
  const id = readStoredTheme();
2643
2585
  persistTheme(id);
@@ -2788,9 +2730,10 @@ function SegmentedControl({ options, value, onChange, className }) {
2788
2730
  "button",
2789
2731
  {
2790
2732
  type: "button",
2733
+ "aria-pressed": value === option.value,
2791
2734
  onClick: () => onChange(option.value),
2792
2735
  className: cn(
2793
- "relative flex items-center gap-1.5 rounded-md px-3.5 py-1.5 text-sm font-medium transition-all",
2736
+ "relative flex min-h-11 items-center gap-1.5 rounded-md px-3.5 py-1.5 text-sm font-medium transition-all sm:min-h-9",
2794
2737
  value === option.value ? "bg-ph-surface text-ph-ink shadow-ph" : "text-ph-subtle hover:text-ph-ink"
2795
2738
  ),
2796
2739
  children: [
@@ -2812,32 +2755,44 @@ function CommandPalette({
2812
2755
  isOpen,
2813
2756
  onClose,
2814
2757
  placeholder = "Type a command or search...",
2815
- className
2758
+ className,
2759
+ id,
2760
+ dataTest,
2761
+ inputId,
2762
+ inputDataTest
2816
2763
  }) {
2817
2764
  const [query, setQuery] = (0, import_react17.useState)("");
2818
2765
  const [selectedIndex, setSelectedIndex] = (0, import_react17.useState)(0);
2819
2766
  const inputRef = (0, import_react17.useRef)(null);
2767
+ const panelRef = (0, import_react17.useRef)(null);
2768
+ const previousFocusRef = (0, import_react17.useRef)(null);
2820
2769
  const filtered = items.filter(
2821
2770
  (item) => item.label.toLowerCase().includes(query.toLowerCase())
2822
2771
  );
2823
2772
  (0, import_react17.useEffect)(() => {
2824
2773
  var _a;
2825
- if (isOpen) {
2826
- setQuery("");
2827
- setSelectedIndex(0);
2828
- (_a = inputRef.current) == null ? void 0 : _a.focus();
2829
- }
2774
+ if (!isOpen) return;
2775
+ previousFocusRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
2776
+ setQuery("");
2777
+ setSelectedIndex(0);
2778
+ (_a = inputRef.current) == null ? void 0 : _a.focus();
2779
+ return () => {
2780
+ var _a2;
2781
+ return (_a2 = previousFocusRef.current) == null ? void 0 : _a2.focus();
2782
+ };
2830
2783
  }, [isOpen]);
2831
2784
  (0, import_react17.useEffect)(() => {
2832
2785
  const handleKeyDown = (e) => {
2833
- var _a;
2786
+ var _a, _b;
2834
2787
  if (!isOpen) return;
2835
2788
  switch (e.key) {
2836
2789
  case "ArrowDown":
2790
+ if (!filtered.length) break;
2837
2791
  e.preventDefault();
2838
2792
  setSelectedIndex((i) => (i + 1) % filtered.length);
2839
2793
  break;
2840
2794
  case "ArrowUp":
2795
+ if (!filtered.length) break;
2841
2796
  e.preventDefault();
2842
2797
  setSelectedIndex((i) => (i - 1 + filtered.length) % filtered.length);
2843
2798
  break;
@@ -2849,62 +2804,98 @@ function CommandPalette({
2849
2804
  case "Escape":
2850
2805
  onClose();
2851
2806
  break;
2807
+ case "Tab": {
2808
+ const focusable = (_b = panelRef.current) == null ? void 0 : _b.querySelectorAll(
2809
+ 'input, button, [href], [tabindex]:not([tabindex="-1"])'
2810
+ );
2811
+ if (!(focusable == null ? void 0 : focusable.length)) break;
2812
+ const first = focusable[0];
2813
+ const last = focusable[focusable.length - 1];
2814
+ if (e.shiftKey && document.activeElement === first) {
2815
+ e.preventDefault();
2816
+ last.focus();
2817
+ } else if (!e.shiftKey && document.activeElement === last) {
2818
+ e.preventDefault();
2819
+ first.focus();
2820
+ }
2821
+ break;
2822
+ }
2852
2823
  }
2853
2824
  };
2854
2825
  window.addEventListener("keydown", handleKeyDown);
2855
2826
  return () => window.removeEventListener("keydown", handleKeyDown);
2856
2827
  }, [isOpen, filtered, selectedIndex, onClose]);
2857
2828
  if (!isOpen) return null;
2858
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "fixed inset-0 z-50 flex items-start justify-center bg-black/40 pt-[20vh] backdrop-blur-sm", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
2829
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
2859
2830
  "div",
2860
2831
  {
2861
- className: cn(
2862
- "w-full max-w-xl overflow-hidden rounded-xl border border-ph-border bg-ph-surface shadow-ph-md",
2863
- className
2864
- ),
2865
- children: [
2866
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex items-center gap-3 border-b border-ph-border px-4 py-3", children: [
2867
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(IconSearch, { className: "h-5 w-5 text-ph-mutedtext" }),
2868
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
2869
- "input",
2870
- {
2871
- ref: inputRef,
2872
- type: "text",
2873
- value: query,
2874
- onChange: (e) => {
2875
- setQuery(e.target.value);
2876
- setSelectedIndex(0);
2877
- },
2878
- placeholder,
2879
- className: "flex-1 bg-transparent text-ph-ink outline-none placeholder:text-ph-mutedtext"
2880
- }
2832
+ id,
2833
+ "data-test": dataTest,
2834
+ className: "fixed inset-0 z-50 flex items-start justify-center bg-black/40 pt-[20vh] backdrop-blur-sm",
2835
+ onMouseDown: (event) => {
2836
+ if (event.target === event.currentTarget) onClose();
2837
+ },
2838
+ children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
2839
+ "div",
2840
+ {
2841
+ ref: panelRef,
2842
+ role: "dialog",
2843
+ "aria-modal": "true",
2844
+ "aria-label": "Command palette",
2845
+ className: cn(
2846
+ "w-full max-w-xl overflow-hidden rounded-xl border border-ph-border bg-ph-surface shadow-ph-md",
2847
+ className
2881
2848
  ),
2882
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("kbd", { className: "rounded border border-ph-border bg-ph-muted px-1.5 py-0.5 text-xs text-ph-mutedtext", children: "ESC" })
2883
- ] }),
2884
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "max-h-[50vh] overflow-y-auto py-2", children: filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "px-4 py-8 text-center text-sm text-ph-mutedtext", children: "No results found." }) : filtered.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
2885
- "button",
2886
- {
2887
- type: "button",
2888
- onClick: () => {
2889
- item.onSelect();
2890
- onClose();
2891
- },
2892
- onMouseEnter: () => setSelectedIndex(index),
2893
- className: cn(
2894
- "flex w-full items-center gap-3 px-4 py-2.5 text-left text-sm transition-colors",
2895
- index === selectedIndex ? "bg-ph-muted text-ph-ink" : "text-ph-subtle"
2896
- ),
2897
- children: [
2898
- item.icon && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "h-5 w-5", children: item.icon }),
2899
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "flex-1", children: item.label }),
2900
- item.shortcut && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("kbd", { className: "rounded border border-ph-border bg-ph-surface px-1.5 py-0.5 text-xs text-ph-mutedtext", children: item.shortcut })
2901
- ]
2902
- },
2903
- item.id
2904
- )) })
2905
- ]
2849
+ children: [
2850
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex items-center gap-3 border-b border-ph-border px-4 py-3", children: [
2851
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(IconSearch, { className: "h-5 w-5 text-ph-mutedtext" }),
2852
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
2853
+ "input",
2854
+ {
2855
+ ref: inputRef,
2856
+ id: inputId,
2857
+ "data-test": inputDataTest,
2858
+ type: "text",
2859
+ "aria-label": "Search commands",
2860
+ value: query,
2861
+ onChange: (e) => {
2862
+ setQuery(e.target.value);
2863
+ setSelectedIndex(0);
2864
+ },
2865
+ placeholder,
2866
+ className: "flex-1 bg-transparent text-ph-ink outline-none placeholder:text-ph-mutedtext"
2867
+ }
2868
+ ),
2869
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("kbd", { className: "rounded border border-ph-border bg-ph-muted px-1.5 py-0.5 text-xs text-ph-mutedtext", children: "ESC" })
2870
+ ] }),
2871
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "max-h-[50vh] overflow-y-auto py-2", children: filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "px-4 py-8 text-center text-sm text-ph-mutedtext", children: "No results found." }) : filtered.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
2872
+ "button",
2873
+ {
2874
+ type: "button",
2875
+ id: `command-${item.id}`,
2876
+ "data-test": `command-${item.id}`,
2877
+ onClick: () => {
2878
+ item.onSelect();
2879
+ onClose();
2880
+ },
2881
+ onMouseEnter: () => setSelectedIndex(index),
2882
+ className: cn(
2883
+ "flex w-full items-center gap-3 px-4 py-2.5 text-left text-sm transition-colors",
2884
+ index === selectedIndex ? "bg-ph-muted text-ph-ink" : "text-ph-subtle"
2885
+ ),
2886
+ children: [
2887
+ item.icon && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "h-5 w-5", children: item.icon }),
2888
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "flex-1", children: item.label }),
2889
+ item.shortcut && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("kbd", { className: "rounded border border-ph-border bg-ph-surface px-1.5 py-0.5 text-xs text-ph-mutedtext", children: item.shortcut })
2890
+ ]
2891
+ },
2892
+ item.id
2893
+ )) })
2894
+ ]
2895
+ }
2896
+ )
2906
2897
  }
2907
- ) });
2898
+ );
2908
2899
  }
2909
2900
 
2910
2901
  // src/components/ui/Calendar.tsx
@@ -5343,34 +5334,45 @@ var Chip = (0, import_react42.forwardRef)(function Chip2(_a, ref) {
5343
5334
  "children",
5344
5335
  "className"
5345
5336
  ]);
5346
- return /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)(
5337
+ const chipClassName = cn(
5338
+ "ph-chip",
5339
+ selected && "ph-chip--selected",
5340
+ className
5341
+ );
5342
+ if (onRemove) {
5343
+ const removeLabel = typeof children === "string" ? `Remove ${children}` : "Remove";
5344
+ return /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("span", { className: chipClassName, children: [
5345
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
5346
+ "button",
5347
+ __spreadProps(__spreadValues({
5348
+ ref,
5349
+ type: "button",
5350
+ className: "min-w-0 bg-transparent p-0 text-inherit"
5351
+ }, props), {
5352
+ children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "ph-chip__text", children })
5353
+ })
5354
+ ),
5355
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
5356
+ "button",
5357
+ {
5358
+ type: "button",
5359
+ onClick: onRemove,
5360
+ disabled: props.disabled,
5361
+ className: "ph-chip__remove",
5362
+ "aria-label": removeLabel,
5363
+ children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("path", { d: "M2 2l6 6M8 2l-6 6" }) })
5364
+ }
5365
+ )
5366
+ ] });
5367
+ }
5368
+ return /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
5347
5369
  "button",
5348
5370
  __spreadProps(__spreadValues({
5349
5371
  ref,
5350
5372
  type: "button",
5351
- className: cn(
5352
- "ph-chip",
5353
- selected && "ph-chip--selected",
5354
- className
5355
- )
5373
+ className: chipClassName
5356
5374
  }, props), {
5357
- children: [
5358
- /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "ph-chip__text", children }),
5359
- onRemove && /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
5360
- "span",
5361
- {
5362
- role: "button",
5363
- tabIndex: -1,
5364
- onClick: (e) => {
5365
- e.stopPropagation();
5366
- onRemove();
5367
- },
5368
- className: "ph-chip__remove",
5369
- "aria-label": "Remove",
5370
- children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("path", { d: "M2 2l6 6M8 2l-6 6" }) })
5371
- }
5372
- )
5373
- ]
5375
+ children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "ph-chip__text", children })
5374
5376
  })
5375
5377
  );
5376
5378
  });