@dloizides/ui-nav 1.16.0 → 1.17.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.
package/dist/index.js CHANGED
@@ -1,18 +1,99 @@
1
1
  'use strict';
2
2
 
3
- var React3 = require('react');
3
+ var React = require('react');
4
4
  var reactNative = require('react-native');
5
5
  var uiFeedback = require('@dloizides/ui-feedback');
6
- var uiMotion = require('@dloizides/ui-motion');
7
6
  var jsxRuntime = require('react/jsx-runtime');
7
+ var uiMotion = require('@dloizides/ui-motion');
8
8
  var uiLayout = require('@dloizides/ui-layout');
9
9
  var authWeb = require('@dloizides/auth-web');
10
10
 
11
11
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
12
 
13
- var React3__default = /*#__PURE__*/_interopDefault(React3);
13
+ var React__default = /*#__PURE__*/_interopDefault(React);
14
14
 
15
15
  // src/Sidebar.tsx
16
+ var IS_WEB = reactNative.Platform.OS === "web";
17
+ var FOCUS_RING_WIDTH = 2;
18
+ var FOCUS_RING_OFFSET = 2;
19
+ var HOVER_TRANSITION_MS = 150;
20
+ var TINT_TRANSITION_MS = 160;
21
+ function focusRingStyle(focused, ringColor) {
22
+ if (!IS_WEB || !focused) return void 0;
23
+ const ring = {
24
+ outlineWidth: FOCUS_RING_WIDTH,
25
+ outlineStyle: "solid",
26
+ outlineColor: ringColor,
27
+ outlineOffset: FOCUS_RING_OFFSET
28
+ };
29
+ return ring;
30
+ }
31
+ function webTransition(properties, durationMs, reducedMotion) {
32
+ if (!IS_WEB) return void 0;
33
+ const transition = {
34
+ transitionProperty: properties,
35
+ transitionDuration: reducedMotion ? "0ms" : `${durationMs}ms`
36
+ };
37
+ return transition;
38
+ }
39
+ function hoverTransitionStyle(reducedMotion) {
40
+ return webTransition("color, background-color", HOVER_TRANSITION_MS, reducedMotion);
41
+ }
42
+ function tintTransitionStyle(reducedMotion) {
43
+ return webTransition("opacity", TINT_TRANSITION_MS, reducedMotion);
44
+ }
45
+ function rotateTransitionStyle(reducedMotion) {
46
+ return webTransition("transform", TINT_TRANSITION_MS, reducedMotion);
47
+ }
48
+ var MIN_TARGET = 36;
49
+ var styles = reactNative.StyleSheet.create({
50
+ trigger: {
51
+ flexDirection: "row",
52
+ alignItems: "center",
53
+ justifyContent: "space-between",
54
+ columnGap: 8,
55
+ paddingHorizontal: 12,
56
+ minHeight: MIN_TARGET,
57
+ borderWidth: 1,
58
+ borderRadius: 8
59
+ },
60
+ label: { fontSize: 14 },
61
+ badge: { paddingHorizontal: 6, paddingVertical: 2, borderRadius: 4, borderWidth: 1 },
62
+ badgeText: { fontSize: 11, fontWeight: "700" }
63
+ });
64
+ var CommandPaletteTrigger = ({
65
+ label,
66
+ hint,
67
+ shortcut,
68
+ onPress,
69
+ testID
70
+ }) => {
71
+ const { theme } = uiFeedback.useUi();
72
+ const colors = theme.colors;
73
+ const primary = theme.palette.primary["500"];
74
+ const [focused, setFocused] = React__default.default.useState(false);
75
+ return /* @__PURE__ */ jsxRuntime.jsxs(
76
+ reactNative.Pressable,
77
+ {
78
+ accessibilityHint: hint,
79
+ accessibilityLabel: label,
80
+ accessibilityRole: "button",
81
+ style: [
82
+ styles.trigger,
83
+ { backgroundColor: colors.surface, borderColor: colors.border },
84
+ focusRingStyle(focused, primary)
85
+ ],
86
+ testID,
87
+ onBlur: () => setFocused(false),
88
+ onFocus: () => setFocused(true),
89
+ onPress,
90
+ children: [
91
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.label, { color: colors.textSecondary }], children: label }),
92
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.badge, { borderColor: colors.border }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.badgeText, { color: colors.textSecondary }], children: shortcut }) })
93
+ ]
94
+ }
95
+ );
96
+ };
16
97
 
17
98
  // src/sidebarFilter.ts
18
99
  function searchTokens(query) {
@@ -56,6 +137,10 @@ var ACTIVE_ACCENT_WIDTH = 3;
56
137
  var NAV_ROW_RADIUS = 8;
57
138
  var ACTIVE_TINT_OPACITY = 0.14;
58
139
  var HOVER_TINT_OPACITY = 0.06;
140
+ function rowTintOpacity(isActive, hovered) {
141
+ if (isActive) return ACTIVE_TINT_OPACITY;
142
+ return hovered ? HOVER_TINT_OPACITY : 0;
143
+ }
59
144
  var NAV_LINK_GAP = 4;
60
145
  var navStyles = reactNative.StyleSheet.create({
61
146
  // --- Sidebar ---
@@ -81,6 +166,12 @@ var navStyles = reactNative.StyleSheet.create({
81
166
  sidebarSpacer: {
82
167
  flex: 1
83
168
  },
169
+ // Bottom rhythm under the ⌘K palette trigger (the narrow-viewport search
170
+ // affordance), matching the inline field's own `marginBottom` so swapping
171
+ // affordances by width keeps the same gap above the nav list.
172
+ sidebarTriggerSlot: {
173
+ marginBottom: 12
174
+ },
84
175
  // Inline-search no-match text — sits where the nav list would, muted + padded.
85
176
  sidebarEmpty: {
86
177
  fontSize: 13,
@@ -343,46 +434,14 @@ var expandableStyles = reactNative.StyleSheet.create({
343
434
  var BASE_INDENT = 12;
344
435
  var NAV_ICON_SIZE = 14;
345
436
  var CHEVRON_ICON_SIZE = 12;
346
- var IS_WEB = reactNative.Platform.OS === "web";
347
- var FOCUS_RING_WIDTH = 2;
348
- var FOCUS_RING_OFFSET = 2;
349
- var HOVER_TRANSITION_MS = 150;
350
- var TINT_TRANSITION_MS = 160;
351
- function focusRingStyle(focused, ringColor) {
352
- if (!IS_WEB || !focused) return void 0;
353
- const ring = {
354
- outlineWidth: FOCUS_RING_WIDTH,
355
- outlineStyle: "solid",
356
- outlineColor: ringColor,
357
- outlineOffset: FOCUS_RING_OFFSET
358
- };
359
- return ring;
360
- }
361
- function webTransition(properties, durationMs, reducedMotion) {
362
- if (!IS_WEB) return void 0;
363
- const transition = {
364
- transitionProperty: properties,
365
- transitionDuration: reducedMotion ? "0ms" : `${durationMs}ms`
366
- };
367
- return transition;
368
- }
369
- function hoverTransitionStyle(reducedMotion) {
370
- return webTransition("color, background-color", HOVER_TRANSITION_MS, reducedMotion);
371
- }
372
- function tintTransitionStyle(reducedMotion) {
373
- return webTransition("opacity", TINT_TRANSITION_MS, reducedMotion);
374
- }
375
- function rotateTransitionStyle(reducedMotion) {
376
- return webTransition("transform", TINT_TRANSITION_MS, reducedMotion);
377
- }
378
437
  var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
379
438
  function getReducedMotionQuery() {
380
439
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return void 0;
381
440
  return window.matchMedia(REDUCED_MOTION_QUERY);
382
441
  }
383
442
  function useReducedMotion() {
384
- const [reduced, setReduced] = React3.useState(() => getReducedMotionQuery()?.matches ?? false);
385
- React3.useEffect(() => {
443
+ const [reduced, setReduced] = React.useState(() => getReducedMotionQuery()?.matches ?? false);
444
+ React.useEffect(() => {
386
445
  const mql = getReducedMotionQuery();
387
446
  if (mql === void 0 || mql.addEventListener === void 0) return void 0;
388
447
  const onChange = () => setReduced(mql.matches);
@@ -428,9 +487,19 @@ function hasActiveDescendant(item, pathname) {
428
487
  if (isRouteActive(pathname, item.route)) return true;
429
488
  return (item.children ?? []).some((child) => hasActiveDescendant(child, pathname));
430
489
  }
431
- function TintOverlay({ opacity, color }) {
490
+ function TintOverlay({
491
+ opacity,
492
+ color,
493
+ testID
494
+ }) {
432
495
  const reducedMotion = useReducedMotion();
433
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [expandableStyles.tintOverlay, { backgroundColor: color, opacity }, tintTransitionStyle(reducedMotion)] });
496
+ return /* @__PURE__ */ jsxRuntime.jsx(
497
+ reactNative.View,
498
+ {
499
+ style: [expandableStyles.tintOverlay, { backgroundColor: color, opacity }, tintTransitionStyle(reducedMotion)],
500
+ testID
501
+ }
502
+ );
434
503
  }
435
504
  var NavExpandableItem = ({
436
505
  item,
@@ -442,30 +511,31 @@ var NavExpandableItem = ({
442
511
  renderChevron,
443
512
  depth = 0
444
513
  }) => {
445
- const [expanded, setExpanded] = React3.useState(() => hasActiveDescendant(item, pathname));
446
- const [focused, setFocused] = React3.useState(false);
447
- const [hovered, setHovered] = React3.useState(false);
514
+ const [expanded, setExpanded] = React.useState(() => hasActiveDescendant(item, pathname));
515
+ const [focused, setFocused] = React.useState(false);
516
+ const [hovered, setHovered] = React.useState(false);
448
517
  const { theme } = uiFeedback.useUi();
449
518
  const colors = theme.colors;
450
519
  const primaryColor = theme.palette.primary["500"];
451
- const toggle = React3.useCallback(() => setExpanded((v) => !v), []);
452
- const onHoverIn = React3.useCallback(() => setHovered(true), []);
453
- const onHoverOut = React3.useCallback(() => setHovered(false), []);
520
+ const toggle = React.useCallback(() => setExpanded((v) => !v), []);
521
+ const onHoverIn = React.useCallback(() => setHovered(true), []);
522
+ const onHoverOut = React.useCallback(() => setHovered(false), []);
454
523
  const indent = depth * BASE_INDENT;
455
524
  const hasChildren = Array.isArray(item.children) && item.children.length > 0;
456
525
  const isActive = isRouteActive(pathname, item.route);
457
526
  const isSectionHeader = hasChildren && depth === 0;
458
- const activeAccentStyle = React3.useMemo(
527
+ const activeAccentStyle = React.useMemo(
459
528
  () => ({ borderLeftWidth: ACTIVE_ACCENT_WIDTH, borderLeftColor: primaryColor }),
460
529
  [primaryColor]
461
530
  );
462
- const paddingStyle = React3.useMemo(() => ({ paddingLeft: indent + BASE_INDENT }), [indent]);
463
- const headerPaddingStyle = React3.useMemo(() => ({ paddingLeft: indent }), [indent]);
464
- const leafTintOpacity = isActive ? ACTIVE_TINT_OPACITY : hovered ? HOVER_TINT_OPACITY : 0;
465
- const headerTintOpacity = hovered ? HOVER_TINT_OPACITY : 0;
531
+ const paddingStyle = React.useMemo(() => ({ paddingLeft: indent + BASE_INDENT }), [indent]);
532
+ const headerPaddingStyle = React.useMemo(() => ({ paddingLeft: indent }), [indent]);
533
+ const leafTintOpacity = rowTintOpacity(isActive, hovered);
534
+ const headerTintOpacity = rowTintOpacity(false, hovered);
535
+ const rowTestID = item.testID ?? item.key;
466
536
  if (!hasChildren)
467
537
  return /* @__PURE__ */ jsxRuntime.jsxs(
468
- reactNative.TouchableOpacity,
538
+ reactNative.Pressable,
469
539
  {
470
540
  accessibilityHint: navigateHint(item.label),
471
541
  accessibilityLabel: item.label,
@@ -477,14 +547,14 @@ var NavExpandableItem = ({
477
547
  isActive ? activeAccentStyle : void 0,
478
548
  focusRingStyle(focused, primaryColor)
479
549
  ],
480
- testID: item.testID ?? item.key,
550
+ testID: rowTestID,
481
551
  onBlur: () => setFocused(false),
482
552
  onFocus: () => setFocused(true),
483
553
  onPress: () => onNavigate(item.route),
484
554
  ...hoverHandlerProps(onHoverIn, onHoverOut),
485
555
  ...ariaCurrentProps(isActive),
486
556
  children: [
487
- /* @__PURE__ */ jsxRuntime.jsx(TintOverlay, { color: primaryColor, opacity: leafTintOpacity }),
557
+ /* @__PURE__ */ jsxRuntime.jsx(TintOverlay, { color: primaryColor, opacity: leafTintOpacity, testID: `${rowTestID}-tint` }),
488
558
  typeof item.renderIcon === "function" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: expandableStyles.iconWrapper, children: item.renderIcon(colors.textSecondary, NAV_ICON_SIZE) }) : null,
489
559
  /* @__PURE__ */ jsxRuntime.jsx(
490
560
  reactNative.Text,
@@ -502,7 +572,7 @@ var NavExpandableItem = ({
502
572
  const chevronColor = colors.textSecondary;
503
573
  return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { children: [
504
574
  /* @__PURE__ */ jsxRuntime.jsxs(
505
- reactNative.TouchableOpacity,
575
+ reactNative.Pressable,
506
576
  {
507
577
  accessibilityHint: expanded ? collapseHint : expandHint,
508
578
  accessibilityLabel: item.label,
@@ -514,14 +584,14 @@ var NavExpandableItem = ({
514
584
  headerPaddingStyle,
515
585
  focusRingStyle(focused, primaryColor)
516
586
  ],
517
- testID: item.testID ?? item.key,
587
+ testID: rowTestID,
518
588
  onBlur: () => setFocused(false),
519
589
  onFocus: () => setFocused(true),
520
590
  onPress: toggle,
521
591
  ...hoverHandlerProps(onHoverIn, onHoverOut),
522
592
  ...ariaExpandedProps(expanded),
523
593
  children: [
524
- /* @__PURE__ */ jsxRuntime.jsx(TintOverlay, { color: primaryColor, opacity: headerTintOpacity }),
594
+ /* @__PURE__ */ jsxRuntime.jsx(TintOverlay, { color: primaryColor, opacity: headerTintOpacity, testID: `${rowTestID}-tint` }),
525
595
  typeof item.renderIcon === "function" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: expandableStyles.iconWrapper, children: item.renderIcon(isSectionHeader ? colors.textSecondary : colors.text, NAV_ICON_SIZE) }) : null,
526
596
  /* @__PURE__ */ jsxRuntime.jsx(
527
597
  reactNative.Text,
@@ -553,10 +623,16 @@ var NavExpandableItem = ({
553
623
  )) }) })
554
624
  ] });
555
625
  };
626
+
627
+ // src/searchAffordance.ts
628
+ var DEFAULT_SEARCH_BREAKPOINT = 768;
629
+ function resolveSearchAffordance(viewport, breakpoint = DEFAULT_SEARCH_BREAKPOINT) {
630
+ return viewport >= breakpoint ? "inline" : "palette";
631
+ }
556
632
  var FIELD_MIN_HEIGHT = 38;
557
633
  var CLEAR_GLYPH = "\u2715";
558
634
  var CLEAR_MIN_TARGET = 28;
559
- var styles = reactNative.StyleSheet.create({
635
+ var styles2 = reactNative.StyleSheet.create({
560
636
  wrap: {
561
637
  flexDirection: "row",
562
638
  alignItems: "center",
@@ -591,13 +667,13 @@ var SidebarSearch = ({
591
667
  const { theme } = uiFeedback.useUi();
592
668
  const colors = theme.colors;
593
669
  const primary = theme.palette.primary["500"];
594
- const [focused, setFocused] = React3.useState(false);
670
+ const [focused, setFocused] = React.useState(false);
595
671
  const hasQuery = value.length > 0;
596
672
  return /* @__PURE__ */ jsxRuntime.jsxs(
597
673
  reactNative.View,
598
674
  {
599
675
  style: [
600
- styles.wrap,
676
+ styles2.wrap,
601
677
  { backgroundColor: colors.surface, borderColor: focused ? primary : colors.border },
602
678
  focusRingStyle(focused, primary)
603
679
  ],
@@ -609,7 +685,7 @@ var SidebarSearch = ({
609
685
  accessibilityLabel: labels.placeholder,
610
686
  placeholder: labels.placeholder,
611
687
  placeholderTextColor: colors.textSecondary,
612
- style: [styles.input, { color: colors.text }],
688
+ style: [styles2.input, { color: colors.text }],
613
689
  testID,
614
690
  value,
615
691
  onBlur: () => setFocused(false),
@@ -623,10 +699,10 @@ var SidebarSearch = ({
623
699
  accessibilityHint: labels.clearHint,
624
700
  accessibilityLabel: labels.clearLabel,
625
701
  accessibilityRole: "button",
626
- style: styles.clear,
702
+ style: styles2.clear,
627
703
  testID: `${testID}-clear`,
628
704
  onPress: onClear,
629
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.clearGlyph, { color: colors.textSecondary }], children: CLEAR_GLYPH })
705
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.clearGlyph, { color: colors.textSecondary }], children: CLEAR_GLYPH })
630
706
  }
631
707
  ) : null
632
708
  ]
@@ -639,11 +715,12 @@ function isBareText(node) {
639
715
  function renderTextSlot(node, style) {
640
716
  if (node === void 0 || node === null) return node;
641
717
  if (isBareText(node)) return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style, children: node });
642
- return React3__default.default.Children.map(
718
+ return React__default.default.Children.map(
643
719
  node,
644
720
  (child) => isBareText(child) ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style, children: child }) : child
645
721
  );
646
722
  }
723
+ var DEFAULT_TRIGGER_TEST_ID = "sidebar-command-palette-trigger";
647
724
  var Sidebar = ({
648
725
  items,
649
726
  pathname,
@@ -656,6 +733,7 @@ var Sidebar = ({
656
733
  renderChevron,
657
734
  enableInlineSearch = false,
658
735
  search,
736
+ paletteTrigger,
659
737
  searchQuery,
660
738
  onSearchChange,
661
739
  header,
@@ -664,20 +742,24 @@ var Sidebar = ({
664
742
  }) => {
665
743
  const { theme } = uiFeedback.useUi();
666
744
  const colors = theme.colors;
667
- const [internalQuery, setInternalQuery] = React3.useState("");
745
+ const { width } = reactNative.useWindowDimensions();
746
+ const [internalQuery, setInternalQuery] = React.useState("");
668
747
  const controlled = searchQuery !== void 0;
669
748
  const query = controlled ? searchQuery : internalQuery;
670
- const setQuery = React3.useCallback(
749
+ const setQuery = React.useCallback(
671
750
  (next) => {
672
751
  if (!controlled) setInternalQuery(next);
673
752
  onSearchChange?.(next);
674
753
  },
675
754
  [controlled, onSearchChange]
676
755
  );
677
- const clearQuery = React3.useCallback(() => setQuery(""), [setQuery]);
678
- const searchOn = enableInlineSearch && search !== void 0;
679
- const visibleItems = searchOn ? filterNavItems(items, query) : items;
680
- const showEmpty = searchOn && isFilterActive(query) && visibleItems.length === 0;
756
+ const clearQuery = React.useCallback(() => setQuery(""), [setQuery]);
757
+ const affordance = resolveSearchAffordance(width);
758
+ const isInlineAffordance = affordance === "inline";
759
+ const showInlineSearch = enableInlineSearch && search !== void 0 && isInlineAffordance;
760
+ const showPaletteTrigger = paletteTrigger !== void 0 && !isInlineAffordance;
761
+ const visibleItems = showInlineSearch ? filterNavItems(items, query) : items;
762
+ const showEmpty = showInlineSearch && isFilterActive(query) && visibleItems.length === 0;
681
763
  return /* @__PURE__ */ jsxRuntime.jsxs(
682
764
  reactNative.View,
683
765
  {
@@ -691,7 +773,7 @@ var Sidebar = ({
691
773
  ],
692
774
  children: [
693
775
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { accessibilityRole: "header", style: [navStyles.sidebarTitle, { color: colors.text }], children: title }),
694
- searchOn && search !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
776
+ showInlineSearch && search !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
695
777
  SidebarSearch,
696
778
  {
697
779
  labels: search,
@@ -701,6 +783,16 @@ var Sidebar = ({
701
783
  onClear: clearQuery
702
784
  }
703
785
  ) : null,
786
+ showPaletteTrigger && paletteTrigger !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: navStyles.sidebarTriggerSlot, children: /* @__PURE__ */ jsxRuntime.jsx(
787
+ CommandPaletteTrigger,
788
+ {
789
+ hint: paletteTrigger.hint,
790
+ label: paletteTrigger.label,
791
+ shortcut: paletteTrigger.shortcut,
792
+ testID: paletteTrigger.testID ?? DEFAULT_TRIGGER_TEST_ID,
793
+ onPress: paletteTrigger.onOpen
794
+ }
795
+ ) }) : null,
704
796
  renderTextSlot(header, { color: colors.text }),
705
797
  showEmpty && search !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [navStyles.sidebarEmpty, { color: colors.textSecondary }], testID: "sidebar-search-empty", children: search.emptyText }) : visibleItems.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
706
798
  NavExpandableItem,
@@ -722,12 +814,6 @@ var Sidebar = ({
722
814
  );
723
815
  };
724
816
 
725
- // src/searchAffordance.ts
726
- var DEFAULT_SEARCH_BREAKPOINT = 768;
727
- function resolveSearchAffordance(viewport, breakpoint = DEFAULT_SEARCH_BREAKPOINT) {
728
- return viewport >= breakpoint ? "inline" : "palette";
729
- }
730
-
731
817
  // src/constants.ts
732
818
  var NAV_TEST_IDS = {
733
819
  /** displayName line in the rich account header (tappable when `onAccount` set). */
@@ -775,7 +861,7 @@ var FocusableTouchable = ({
775
861
  testID,
776
862
  children
777
863
  }) => {
778
- const [focused, setFocused] = React3.useState(false);
864
+ const [focused, setFocused] = React.useState(false);
779
865
  return /* @__PURE__ */ jsxRuntime.jsx(
780
866
  reactNative.Pressable,
781
867
  {
@@ -928,8 +1014,8 @@ var NavBarLink = ({
928
1014
  navigateHint,
929
1015
  onPress
930
1016
  }) => {
931
- const [hovered, setHovered] = React3.useState(false);
932
- const [focused, setFocused] = React3.useState(false);
1017
+ const [hovered, setHovered] = React.useState(false);
1018
+ const [focused, setFocused] = React.useState(false);
933
1019
  const isHovered = hovered && !isActive;
934
1020
  const textColor = isActive ? TEXT_ON_PRIMARY2 : isHovered ? colors.hoverText : colors.rest;
935
1021
  const pillStyle = isActive ? { backgroundColor: colors.activeBg } : isHovered ? { backgroundColor: colors.hoverBg } : void 0;
@@ -973,12 +1059,12 @@ var NavOverflowMenu = ({
973
1059
  testID,
974
1060
  variant
975
1061
  }) => {
976
- const options = React3.useMemo(() => items.map((item) => ({ label: item.label, value: item.route })), [items]);
977
- const activeRoute = React3.useMemo(
1062
+ const options = React.useMemo(() => items.map((item) => ({ label: item.label, value: item.route })), [items]);
1063
+ const activeRoute = React.useMemo(
978
1064
  () => items.find((item) => isRouteActive(pathname, item.route))?.route ?? NO_ACTIVE_ROUTE,
979
1065
  [items, pathname]
980
1066
  );
981
- const optionTestID = React3.useMemo(() => {
1067
+ const optionTestID = React.useMemo(() => {
982
1068
  const byRoute = new Map(items.map((item) => [item.route, item.testID ?? item.key]));
983
1069
  return (route) => byRoute.get(route) ?? `${testID}-option-${route}`;
984
1070
  }, [items, testID]);
@@ -1035,13 +1121,13 @@ function resizeWidthBuffer(previous, count) {
1035
1121
  return next;
1036
1122
  }
1037
1123
  function useNavOverflow(itemCount, gap) {
1038
- const [availableWidth, setAvailableWidthState] = React3.useState(0);
1039
- const [moreWidth, setMoreWidthState] = React3.useState(0);
1040
- const [itemWidths, setItemWidths] = React3.useState(() => new Array(itemCount).fill(0));
1041
- React3.useEffect(() => {
1124
+ const [availableWidth, setAvailableWidthState] = React.useState(0);
1125
+ const [moreWidth, setMoreWidthState] = React.useState(0);
1126
+ const [itemWidths, setItemWidths] = React.useState(() => new Array(itemCount).fill(0));
1127
+ React.useEffect(() => {
1042
1128
  setItemWidths((previous) => previous.length === itemCount ? previous : resizeWidthBuffer(previous, itemCount));
1043
1129
  }, [itemCount]);
1044
- const setItemWidth = React3.useCallback((index, width) => {
1130
+ const setItemWidth = React.useCallback((index, width) => {
1045
1131
  setItemWidths((previous) => {
1046
1132
  if (index < 0 || index >= previous.length || previous[index] === width) return previous;
1047
1133
  const next = previous.slice();
@@ -1049,15 +1135,15 @@ function useNavOverflow(itemCount, gap) {
1049
1135
  return next;
1050
1136
  });
1051
1137
  }, []);
1052
- const setAvailableWidth = React3.useCallback(
1138
+ const setAvailableWidth = React.useCallback(
1053
1139
  (width) => setAvailableWidthState((previous) => previous === width ? previous : width),
1054
1140
  []
1055
1141
  );
1056
- const setMoreWidth = React3.useCallback(
1142
+ const setMoreWidth = React.useCallback(
1057
1143
  (width) => setMoreWidthState((previous) => previous === width ? previous : width),
1058
1144
  []
1059
1145
  );
1060
- const visibleCount = React3.useMemo(
1146
+ const visibleCount = React.useMemo(
1061
1147
  () => computeVisibleCount({ availableWidth, itemWidths, moreWidth, gap }),
1062
1148
  [availableWidth, itemWidths, moreWidth, gap]
1063
1149
  );
@@ -1091,24 +1177,24 @@ var NavBarInner = ({
1091
1177
  const primaryColor = theme.palette.primary["500"];
1092
1178
  const { width } = reactNative.useWindowDimensions();
1093
1179
  const reducedMotion = useReducedMotion();
1094
- const [open, setOpen] = React3.useState(false);
1095
- const [toggleFocused, setToggleFocused] = React3.useState(false);
1180
+ const [open, setOpen] = React.useState(false);
1181
+ const [toggleFocused, setToggleFocused] = React.useState(false);
1096
1182
  const { visibleCount, setAvailableWidth, setItemWidth, setMoreWidth } = useNavOverflow(items.length, NAV_LINK_GAP);
1097
1183
  const collapsed = width < collapseBelow;
1098
1184
  const showLinks = !collapsed || open;
1099
- const innerCapStyle = React3.useMemo(
1185
+ const innerCapStyle = React.useMemo(
1100
1186
  () => contentMaxWidth === void 0 ? null : { maxWidth: contentMaxWidth, width: "100%", alignSelf: "center" },
1101
1187
  [contentMaxWidth]
1102
1188
  );
1103
- const toggleMenu = React3.useCallback(() => setOpen((v) => !v), []);
1104
- const handlePress = React3.useCallback(
1189
+ const toggleMenu = React.useCallback(() => setOpen((v) => !v), []);
1190
+ const handlePress = React.useCallback(
1105
1191
  (route) => {
1106
1192
  setOpen(false);
1107
1193
  onNavigate(route);
1108
1194
  },
1109
1195
  [onNavigate]
1110
1196
  );
1111
- const linkColors = React3.useMemo(
1197
+ const linkColors = React.useMemo(
1112
1198
  () => ({
1113
1199
  rest: colors.textSecondary,
1114
1200
  hoverText: colors.text,
@@ -1163,7 +1249,7 @@ var NavBarInner = ({
1163
1249
  }
1164
1250
  );
1165
1251
  if (collapsed) {
1166
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { nativeID: LINKS_REGION_ID, style: navBarStyles.linksStacked, testID: NAV_TEST_IDS.navBarLinks, children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(React3__default.default.Fragment, { children: renderLink(item) }, item.key)) });
1252
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { nativeID: LINKS_REGION_ID, style: navBarStyles.linksStacked, testID: NAV_TEST_IDS.navBarLinks, children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(React__default.default.Fragment, { children: renderLink(item) }, item.key)) });
1167
1253
  }
1168
1254
  const visibleItems = items.slice(0, visibleCount);
1169
1255
  const overflowItems = items.slice(visibleCount);
@@ -1229,7 +1315,7 @@ var CARD_BORDER_WIDTH = 1;
1229
1315
  var CARD_TITLE_FONT_SIZE = 16;
1230
1316
  var CARD_MESSAGE_FONT_SIZE = 14;
1231
1317
  var CARD_TITLE_MARGIN_BOTTOM = 8;
1232
- var styles2 = reactNative.StyleSheet.create({
1318
+ var styles3 = reactNative.StyleSheet.create({
1233
1319
  centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
1234
1320
  card: {
1235
1321
  padding: CARD_PADDING,
@@ -1246,9 +1332,9 @@ function MessageCard({
1246
1332
  }) {
1247
1333
  const { theme } = uiFeedback.useUi();
1248
1334
  const colors = theme.colors;
1249
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles2.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
1250
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.cardTitle, { color: accentColor }], children: message.titleText }),
1251
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.cardMessage, { color: colors.textSecondary }], children: message.messageText })
1335
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles3.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
1336
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.cardTitle, { color: accentColor }], children: message.titleText }),
1337
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.cardMessage, { color: colors.textSecondary }], children: message.messageText })
1252
1338
  ] });
1253
1339
  }
1254
1340
  function useContentBody(state, children, testID) {
@@ -1260,7 +1346,7 @@ function useContentBody(state, children, testID) {
1260
1346
  if (state?.error)
1261
1347
  return /* @__PURE__ */ jsxRuntime.jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
1262
1348
  if (state?.loading === true)
1263
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles2.centerFill, testID: `${testID}${APP_SHELL_SUFFIX.loading}`, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" }) });
1349
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.centerFill, testID: `${testID}${APP_SHELL_SUFFIX.loading}`, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" }) });
1264
1350
  return children;
1265
1351
  }
1266
1352
  var SCRIM_COLOR = "rgba(0, 0, 0, 0.5)";
@@ -1284,7 +1370,7 @@ var DEFAULT_DRAWER_LABELS = {
1284
1370
  closeLabel: "Close menu",
1285
1371
  closeHint: "Close the navigation menu"
1286
1372
  };
1287
- var styles3 = reactNative.StyleSheet.create({
1373
+ var styles4 = reactNative.StyleSheet.create({
1288
1374
  overlay: { ...reactNative.StyleSheet.absoluteFillObject, zIndex: DRAWER_Z_INDEX },
1289
1375
  // The scrim's tap-to-close hit area is anchored to the RIGHT of the panel
1290
1376
  // (`left: DRAWER_WIDTH`), so it NEVER overlaps the panel: nav-item taps always
@@ -1319,10 +1405,10 @@ var MenuToggle = ({ label, hint, onOpen, testID }) => {
1319
1405
  accessibilityHint: hint,
1320
1406
  accessibilityLabel: label,
1321
1407
  ringColor: theme.palette.primary["500"],
1322
- style: styles3.menuToggle,
1408
+ style: styles4.menuToggle,
1323
1409
  testID,
1324
1410
  onPress: onOpen,
1325
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
1411
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
1326
1412
  }
1327
1413
  );
1328
1414
  };
@@ -1334,8 +1420,8 @@ var MobileDrawer = ({
1334
1420
  scrimTestID
1335
1421
  }) => {
1336
1422
  const { theme } = uiFeedback.useUi();
1337
- const panelRef = React3.useRef(null);
1338
- React3.useEffect(() => {
1423
+ const panelRef = React.useRef(null);
1424
+ React.useEffect(() => {
1339
1425
  const node = panelRef.current;
1340
1426
  if (node === null || typeof node.addEventListener !== "function") return void 0;
1341
1427
  const handleClick = (event) => {
@@ -1348,14 +1434,14 @@ var MobileDrawer = ({
1348
1434
  node.addEventListener("click", handleClick);
1349
1435
  return () => node.removeEventListener("click", handleClick);
1350
1436
  }, [onClose]);
1351
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.overlay, children: [
1437
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.overlay, children: [
1352
1438
  /* @__PURE__ */ jsxRuntime.jsx(
1353
1439
  reactNative.Pressable,
1354
1440
  {
1355
1441
  accessibilityHint: labels.closeHint,
1356
1442
  accessibilityLabel: labels.closeLabel,
1357
1443
  accessibilityRole: "button",
1358
- style: styles3.scrim,
1444
+ style: styles4.scrim,
1359
1445
  testID: scrimTestID,
1360
1446
  onPress: onClose
1361
1447
  }
@@ -1366,7 +1452,7 @@ var MobileDrawer = ({
1366
1452
  ref: panelRef,
1367
1453
  "aria-modal": true,
1368
1454
  role: "dialog",
1369
- style: [styles3.panel, { backgroundColor: theme.colors.surface }],
1455
+ style: [styles4.panel, { backgroundColor: theme.colors.surface }],
1370
1456
  testID: drawerTestID,
1371
1457
  children: renderTextSlot(sidebar, { color: theme.colors.text })
1372
1458
  }
@@ -1397,7 +1483,7 @@ function useContentMaxWidth(width) {
1397
1483
  return resolveContentMaxWidth(width, viewport);
1398
1484
  }
1399
1485
  var DEFAULT_CONTENT_PADDING = 24;
1400
- var styles4 = reactNative.StyleSheet.create({
1486
+ var styles5 = reactNative.StyleSheet.create({
1401
1487
  root: { flex: 1 },
1402
1488
  centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
1403
1489
  scroll: { flex: 1 },
@@ -1442,32 +1528,32 @@ var AppShell = ({
1442
1528
  range: railRange
1443
1529
  });
1444
1530
  const useDrawer = railMode === "drawer";
1445
- const [drawerOpen, setDrawerOpen] = React3.useState(false);
1446
- const openDrawer = React3.useCallback(() => setDrawerOpen(true), []);
1447
- const closeDrawer = React3.useCallback(() => setDrawerOpen(false), []);
1448
- React3.useEffect(() => {
1531
+ const [drawerOpen, setDrawerOpen] = React.useState(false);
1532
+ const openDrawer = React.useCallback(() => setDrawerOpen(true), []);
1533
+ const closeDrawer = React.useCallback(() => setDrawerOpen(false), []);
1534
+ React.useEffect(() => {
1449
1535
  if (!useDrawer && drawerOpen) setDrawerOpen(false);
1450
1536
  }, [useDrawer, drawerOpen]);
1451
1537
  const drawerLabels = { ...DEFAULT_DRAWER_LABELS, ...mobileMenu };
1452
1538
  const isUnauthenticated = gate !== void 0 && !gate.pending && !gate.authenticated;
1453
- React3.useEffect(() => {
1539
+ React.useEffect(() => {
1454
1540
  if (isUnauthenticated && gate !== void 0) gate.onRedirect();
1455
1541
  }, [isUnauthenticated, gate]);
1456
1542
  if (gate?.pending === true)
1457
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles4.root, styles4.centerFill, { backgroundColor: theme.colors.background }], testID: `${testID}${APP_SHELL_SUFFIX.pending}`, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" }) });
1543
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles5.root, styles5.centerFill, { backgroundColor: theme.colors.background }], testID: `${testID}${APP_SHELL_SUFFIX.pending}`, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" }) });
1458
1544
  if (isUnauthenticated) return null;
1459
- const columnStyle = maxWidth === "full" ? styles4.columnFull : [styles4.columnCapped, { maxWidth }];
1460
- const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles4.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
1545
+ const columnStyle = maxWidth === "full" ? styles5.columnFull : [styles5.columnCapped, { maxWidth }];
1546
+ const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles5.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
1461
1547
  const scroller = /* @__PURE__ */ jsxRuntime.jsx(
1462
1548
  reactNative.ScrollView,
1463
1549
  {
1464
- contentContainerStyle: [styles4.scrollContent, { padding: contentPadding }],
1465
- style: styles4.scroll,
1550
+ contentContainerStyle: [styles5.scrollContent, { padding: contentPadding }],
1551
+ style: styles5.scroll,
1466
1552
  testID: `${testID}${APP_SHELL_SUFFIX.content}`,
1467
1553
  children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: columnStyle, children: body })
1468
1554
  }
1469
1555
  );
1470
- const headerRegion = useDrawer ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.headerRow, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
1556
+ const headerRegion = useDrawer ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.headerRow, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
1471
1557
  /* @__PURE__ */ jsxRuntime.jsx(
1472
1558
  MenuToggle,
1473
1559
  {
@@ -1477,14 +1563,14 @@ var AppShell = ({
1477
1563
  onOpen: openDrawer
1478
1564
  }
1479
1565
  ),
1480
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles4.headerFill, children: renderTextSlot(header, { color: theme.colors.text }) })
1566
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles5.headerFill, children: renderTextSlot(header, { color: theme.colors.text }) })
1481
1567
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: renderTextSlot(header, { color: theme.colors.text }) });
1482
1568
  const railRegion = railMode === "full" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.sidebar}`, children: renderTextSlot(sidebar, { color: theme.colors.text }) }) : railMode === "collapsed" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.collapsedSidebar}`, children: renderTextSlot(collapsedSidebar, { color: theme.colors.text }) }) : null;
1483
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles4.root, { backgroundColor: theme.colors.background }], testID, children: [
1569
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles5.root, { backgroundColor: theme.colors.background }], testID, children: [
1484
1570
  headerRegion,
1485
1571
  nav !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.nav}`, children: navInnerStyle !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: navInnerStyle, children: renderTextSlot(nav, { color: theme.colors.text }) }) : renderTextSlot(nav, { color: theme.colors.text }) }) : null,
1486
1572
  banner !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.banner}`, children: renderTextSlot(banner, { color: theme.colors.text }) }) : null,
1487
- railRegion !== null ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.bodyRow, children: [
1573
+ railRegion !== null ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.bodyRow, children: [
1488
1574
  railRegion,
1489
1575
  scroller
1490
1576
  ] }) : scroller,
@@ -1601,16 +1687,21 @@ var NavShell = ({
1601
1687
  {
1602
1688
  collapseHint: sideRail.collapseHint,
1603
1689
  containerStyle: sideRail.containerStyle,
1690
+ enableInlineSearch: sideRail.enableInlineSearch,
1604
1691
  expandHint: sideRail.expandHint,
1605
1692
  footer: sideRail.footer,
1606
1693
  header: sideRail.header,
1607
1694
  items: sideRail.items,
1608
1695
  navigateHint,
1696
+ paletteTrigger: sideRail.paletteTrigger,
1609
1697
  pathname,
1610
1698
  regionLabel,
1611
1699
  renderChevron: sideRail.renderChevron,
1700
+ search: sideRail.search,
1701
+ searchQuery: sideRail.searchQuery,
1612
1702
  title: sideRail.title,
1613
- onNavigate
1703
+ onNavigate,
1704
+ onSearchChange: sideRail.onSearchChange
1614
1705
  }
1615
1706
  ) : void 0;
1616
1707
  const collapsedCfg = showSideRail ? sideRail?.collapsed : void 0;
@@ -1702,7 +1793,7 @@ var DarkModeControl = ({
1702
1793
  const primaryColor = theme.palette.primary["500"];
1703
1794
  const currentIndex = options.findIndex((option) => option.value === value);
1704
1795
  const current = currentIndex >= 0 ? options[currentIndex] : options[0];
1705
- const advance = React3.useCallback(() => {
1796
+ const advance = React.useCallback(() => {
1706
1797
  if (options.length === 0) return;
1707
1798
  const from = currentIndex >= 0 ? currentIndex : 0;
1708
1799
  const next = options[(from + 1) % options.length];
@@ -1778,7 +1869,7 @@ var KEY_DOWN = "ArrowDown";
1778
1869
  var KEY_UP = "ArrowUp";
1779
1870
  var KEY_ENTER = "Enter";
1780
1871
  var KEY_ESCAPE = "Escape";
1781
- var styles5 = reactNative.StyleSheet.create({
1872
+ var styles6 = reactNative.StyleSheet.create({
1782
1873
  overlay: { ...reactNative.StyleSheet.absoluteFillObject, zIndex: OVERLAY_Z_INDEX, alignItems: "center" },
1783
1874
  scrim: { ...reactNative.StyleSheet.absoluteFillObject, backgroundColor: SCRIM_COLOR2 },
1784
1875
  panel: {
@@ -1807,14 +1898,14 @@ var PaletteRow = ({ item, selected, onChoose }) => {
1807
1898
  accessibilityLabel: item.label,
1808
1899
  accessibilityRole: "button",
1809
1900
  accessibilityState: { selected },
1810
- style: [styles5.row, selected ? { backgroundColor: colors.border } : void 0],
1901
+ style: [styles6.row, selected ? { backgroundColor: colors.border } : void 0],
1811
1902
  testID: item.testID ?? item.key,
1812
1903
  onPress: () => onChoose(item),
1813
1904
  children: [
1814
1905
  typeof item.renderIcon === "function" ? item.renderIcon(colors.textSecondary, NAV_ICON_SIZE) : null,
1815
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.rowText, children: [
1816
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.rowLabel, { color: colors.text }], children: item.label }),
1817
- item.hint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.rowHint, { color: colors.textSecondary }], children: item.hint }) : null
1906
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.rowText, children: [
1907
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.rowLabel, { color: colors.text }], children: item.label }),
1908
+ item.hint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.rowHint, { color: colors.textSecondary }], children: item.hint }) : null
1818
1909
  ] })
1819
1910
  ]
1820
1911
  }
@@ -1823,27 +1914,27 @@ var PaletteRow = ({ item, selected, onChoose }) => {
1823
1914
  var CommandPalette = ({ open, items, onClose, labels, testID }) => {
1824
1915
  const { theme } = uiFeedback.useUi();
1825
1916
  const colors = theme.colors;
1826
- const [query, setQuery] = React3.useState("");
1827
- const [selected, setSelected] = React3.useState(0);
1828
- const inputRef = React3.useRef(null);
1829
- const results = React3.useMemo(() => filterCommands(items, query), [items, query]);
1830
- React3.useEffect(() => {
1917
+ const [query, setQuery] = React.useState("");
1918
+ const [selected, setSelected] = React.useState(0);
1919
+ const inputRef = React.useRef(null);
1920
+ const results = React.useMemo(() => filterCommands(items, query), [items, query]);
1921
+ React.useEffect(() => {
1831
1922
  if (!open) return;
1832
1923
  setQuery("");
1833
1924
  setSelected(0);
1834
1925
  inputRef.current?.focus();
1835
1926
  }, [open]);
1836
- React3.useEffect(() => {
1927
+ React.useEffect(() => {
1837
1928
  setSelected((prev) => Math.min(prev, Math.max(0, results.length - 1)));
1838
1929
  }, [results.length]);
1839
- const choose = React3.useCallback(
1930
+ const choose = React.useCallback(
1840
1931
  (item) => {
1841
1932
  item.onSelect();
1842
1933
  onClose();
1843
1934
  },
1844
1935
  [onClose]
1845
1936
  );
1846
- const onKeyPress = React3.useCallback(
1937
+ const onKeyPress = React.useCallback(
1847
1938
  (event) => {
1848
1939
  const key = event.nativeEvent.key;
1849
1940
  if (key === KEY_DOWN) setSelected((i) => Math.min(i + 1, results.length - 1));
@@ -1854,14 +1945,14 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
1854
1945
  [results, selected, choose, onClose]
1855
1946
  );
1856
1947
  if (!open) return null;
1857
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.overlay, children: [
1948
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.overlay, children: [
1858
1949
  /* @__PURE__ */ jsxRuntime.jsx(
1859
1950
  reactNative.Pressable,
1860
1951
  {
1861
1952
  accessibilityHint: labels.closeHint,
1862
1953
  accessibilityLabel: labels.closeLabel,
1863
1954
  accessibilityRole: "button",
1864
- style: styles5.scrim,
1955
+ style: styles6.scrim,
1865
1956
  testID: `${testID ?? "command-palette"}-scrim`,
1866
1957
  onPress: onClose
1867
1958
  }
@@ -1872,7 +1963,7 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
1872
1963
  "aria-modal": true,
1873
1964
  "aria-label": labels.regionLabel,
1874
1965
  role: "dialog",
1875
- style: [styles5.panel, { backgroundColor: colors.surface, borderColor: colors.border }],
1966
+ style: [styles6.panel, { backgroundColor: colors.surface, borderColor: colors.border }],
1876
1967
  testID: testID ?? "command-palette",
1877
1968
  children: [
1878
1969
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1882,71 +1973,22 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
1882
1973
  accessibilityLabel: labels.placeholder,
1883
1974
  placeholder: labels.placeholder,
1884
1975
  placeholderTextColor: colors.textSecondary,
1885
- style: [styles5.input, { color: colors.text, borderBottomColor: colors.border }],
1976
+ style: [styles6.input, { color: colors.text, borderBottomColor: colors.border }],
1886
1977
  testID: `${testID ?? "command-palette"}-input`,
1887
1978
  value: query,
1888
1979
  onChangeText: setQuery,
1889
1980
  onKeyPress
1890
1981
  }
1891
1982
  ),
1892
- results.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.empty, { color: colors.textSecondary }], children: labels.emptyText }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.ScrollView, { keyboardShouldPersistTaps: "handled", style: styles5.list, children: results.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(PaletteRow, { item, selected: index === selected, onChoose: choose }, item.key)) })
1983
+ results.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.empty, { color: colors.textSecondary }], children: labels.emptyText }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.ScrollView, { keyboardShouldPersistTaps: "handled", style: styles6.list, children: results.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(PaletteRow, { item, selected: index === selected, onChoose: choose }, item.key)) })
1893
1984
  ]
1894
1985
  }
1895
1986
  )
1896
1987
  ] });
1897
1988
  };
1898
- var MIN_TARGET = 36;
1899
- var styles6 = reactNative.StyleSheet.create({
1900
- trigger: {
1901
- flexDirection: "row",
1902
- alignItems: "center",
1903
- justifyContent: "space-between",
1904
- columnGap: 8,
1905
- paddingHorizontal: 12,
1906
- minHeight: MIN_TARGET,
1907
- borderWidth: 1,
1908
- borderRadius: 8
1909
- },
1910
- label: { fontSize: 14 },
1911
- badge: { paddingHorizontal: 6, paddingVertical: 2, borderRadius: 4, borderWidth: 1 },
1912
- badgeText: { fontSize: 11, fontWeight: "700" }
1913
- });
1914
- var CommandPaletteTrigger = ({
1915
- label,
1916
- hint,
1917
- shortcut,
1918
- onPress,
1919
- testID
1920
- }) => {
1921
- const { theme } = uiFeedback.useUi();
1922
- const colors = theme.colors;
1923
- const primary = theme.palette.primary["500"];
1924
- const [focused, setFocused] = React3__default.default.useState(false);
1925
- return /* @__PURE__ */ jsxRuntime.jsxs(
1926
- reactNative.Pressable,
1927
- {
1928
- accessibilityHint: hint,
1929
- accessibilityLabel: label,
1930
- accessibilityRole: "button",
1931
- style: [
1932
- styles6.trigger,
1933
- { backgroundColor: colors.surface, borderColor: colors.border },
1934
- focusRingStyle(focused, primary)
1935
- ],
1936
- testID,
1937
- onBlur: () => setFocused(false),
1938
- onFocus: () => setFocused(true),
1939
- onPress,
1940
- children: [
1941
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.label, { color: colors.textSecondary }], children: label }),
1942
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles6.badge, { borderColor: colors.border }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.badgeText, { color: colors.textSecondary }], children: shortcut }) })
1943
- ]
1944
- }
1945
- );
1946
- };
1947
1989
  var LAUNCH_KEY = "k";
1948
1990
  function useCommandPaletteHotkey(onOpen, enabled = true) {
1949
- React3.useEffect(() => {
1991
+ React.useEffect(() => {
1950
1992
  if (!enabled || typeof document === "undefined") return void 0;
1951
1993
  const handler = (event) => {
1952
1994
  const isLauncher = event.key.toLowerCase() === LAUNCH_KEY && (event.metaKey || event.ctrlKey);