@gnome-ui/react-native 1.12.0 → 1.13.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/README.md +210 -0
- package/dist/components/Calendar/Calendar.d.ts +95 -0
- package/dist/components/Calendar/CalendarBase.d.ts +83 -0
- package/dist/components/Calendar/calendarUtils.d.ts +66 -0
- package/dist/components/Calendar/index.d.ts +2 -0
- package/dist/components/CalendarRange/CalendarRange.d.ts +75 -0
- package/dist/components/CalendarRange/index.d.ts +2 -0
- package/dist/components/CalendarRange/rangeUtils.d.ts +31 -0
- package/dist/components/DatePicker/DatePicker.d.ts +75 -0
- package/dist/components/DatePicker/index.d.ts +2 -0
- package/dist/components/TimePicker/TimeFields.d.ts +36 -0
- package/dist/components/TimePicker/TimePicker.d.ts +55 -0
- package/dist/components/TimePicker/index.d.ts +2 -0
- package/dist/components/TimePicker/timeUtils.d.ts +36 -0
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4762 -4111
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../src/theme/tokens.generated.ts","../src/GnomeProvider/GnomeContext.ts","../src/theme/resolveTheme.ts","../src/GnomeProvider/resolveContext.ts","../src/GnomeProvider/GnomeProvider.tsx","../src/components/Text/Text.tsx","../src/components/ActionRow/ActionRow.tsx","../src/components/Icon/shared.ts","../src/components/Icon/Icon.tsx","../src/components/AnimatedIcon/AnimatedIcon.tsx","../src/components/Avatar/Avatar.tsx","../src/components/AvatarGroup/AvatarGroup.tsx","../src/components/AvatarRotator/AvatarRotator.tsx","../src/components/Badge/Badge.tsx","../src/components/Banner/Banner.tsx","../src/components/Bin/Bin.tsx","../src/components/Blockquote/Blockquote.tsx","../src/components/BottomSheet/BottomSheet.tsx","../src/components/BottomTabBar/BottomTabBar.tsx","../src/components/Box/Box.tsx","../src/components/Separator/Separator.tsx","../src/components/BoxedList/BoxedList.tsx","../src/components/BreakpointBin/BreakpointBin.tsx","../src/components/Button/Button.tsx","../src/components/ButtonContent/ButtonContent.tsx","../src/components/ButtonRow/ButtonRow.tsx","../src/components/Callout/Callout.tsx","../src/components/Card/Card.tsx","../src/components/Checkbox/Checkbox.tsx","../src/components/CheckRow/CheckRow.tsx","../src/components/Chip/Chip.tsx","../src/components/Clamp/Clamp.tsx","../src/components/CoachMark/coachMarkUtils.ts","../src/components/CoachMark/CoachMark.tsx","../src/components/CoachMark/CoachMarkTour.tsx","../src/components/WrapBox/WrapBox.tsx","../src/components/ColorPicker/ColorSwatch.tsx","../src/components/ColorPicker/ColorPicker.tsx","../src/components/Dropdown/Dropdown.tsx","../src/components/ComboRow/ComboRow.tsx","../src/components/Tooltip/Tooltip.tsx","../src/components/IconButton/IconButton.tsx","../src/components/CopyButton/CopyButton.tsx","../src/components/Dialog/Dialog.tsx","../src/components/Divider/Divider.tsx","../src/components/Drawer/Drawer.tsx","../src/components/EntryRow/EntryRow.tsx","../src/components/Expander/Expander.tsx","../src/components/ExpanderRow/ExpanderRow.tsx","../src/components/FieldGroup/FieldGroup.tsx","../src/components/FileTypeIcon/fileType.ts","../src/components/FileTypeIcon/FileTypeIcon.tsx","../src/components/FilterableMultiSelectDropdown/FilterableMultiSelectDropdown.tsx","../src/components/HeaderBar/HeaderBar.tsx","../src/components/Highlight/Highlight.tsx","../src/components/InlineViewSwitcher/variants.ts","../src/components/InlineViewSwitcher/InlineViewSwitcher.tsx","../src/components/InlineViewSwitcher/InlineViewSwitcherItem.tsx","../src/components/LevelBar/LevelBar.tsx","../src/components/Link/Link.tsx","../src/components/LinkedGroup/LinkedGroup.tsx","../src/components/MultiSelectDropdown/MultiSelectDropdown.tsx","../src/hooks/useBreakpoint.ts","../src/components/NavigationSplitView/NavigationSplitView.tsx","../src/components/Overlay/Overlay.tsx","../src/components/PasswordEntryRow/PasswordEntryRow.tsx","../src/components/PasswordField/PasswordField.tsx","../src/components/PathBar/PathBar.tsx","../src/components/Popover/Popover.tsx","../src/components/PreferencesGroup/PreferencesGroup.tsx","../src/components/ProgressBar/ProgressBar.tsx","../src/components/RadioButton/RadioButton.tsx","../src/components/RangeSlider/RangeSlider.tsx","../src/components/RatingStars/RatingStars.tsx","../src/components/ScrollToTop/ScrollToTop.tsx","../src/components/SearchBar/SearchBar.tsx","../src/components/SegmentedBar/SegmentedBar.tsx","../src/components/Sidebar/filterUtils.ts","../src/components/Sidebar/Sidebar.tsx","../src/components/Sidebar/SidebarItem.tsx","../src/components/Sidebar/SidebarSection.tsx","../src/components/Skeleton/Skeleton.tsx","../src/components/Slider/Slider.tsx","../src/components/Spacer/Spacer.tsx","../src/components/SpinButton/SpinButton.tsx","../src/components/Spinner/Spinner.tsx","../src/components/SpinRow/SpinRow.tsx","../src/components/SplitButton/SplitButton.tsx","../src/components/StatusBadge/StatusBadge.tsx","../src/components/StatusPage/StatusPage.tsx","../src/components/StepIndicator/StepIndicator.tsx","../src/components/Switch/Switch.tsx","../src/components/SwitchRow/SwitchRow.tsx","../src/components/Tabs/TabBar.tsx","../src/components/Tabs/TabItem.tsx","../src/components/Tabs/TabPanel.tsx","../src/components/TagInput/TagInput.tsx","../src/components/TextField/TextField.tsx","../src/components/Timeline/Timeline.tsx","../src/components/Toast/Toast.tsx","../src/components/Toast/Toaster.tsx","../src/components/ToggleGroup/ToggleGroup.tsx","../src/components/ToggleGroup/ToggleGroupItem.tsx","../src/components/Toolbar/Toolbar.tsx","../src/components/ViewSwitcher/ViewSwitcher.tsx","../src/components/ViewSwitcher/ViewSwitcherItem.tsx","../src/components/WidgetManager/WidgetManager.tsx"],"sourcesContent":["/**\n * AUTO-GENERATED — do not edit by hand.\n * Run `npm run theme:generate` (from packages/react-native) to regenerate\n * from @gnome-ui/core's `src/tokens.css`.\n */\n\nexport interface GnomeThemeTokens {\n readonly accentBgColor: string;\n readonly accentColor: string;\n readonly accentFgColor: string;\n readonly activeOverlay: string;\n readonly blockquoteBorderWidth: number;\n readonly blue1: string;\n readonly blue2: string;\n readonly blue3: string;\n readonly blue4: string;\n readonly blue5: string;\n readonly borderSubtle: string;\n readonly brown1: string;\n readonly brown2: string;\n readonly brown3: string;\n readonly brown4: string;\n readonly brown5: string;\n readonly cardBgColor: string;\n readonly cardFgColor: string;\n readonly cardShadeColor: string;\n readonly dark1: string;\n readonly dark2: string;\n readonly dark3: string;\n readonly dark4: string;\n readonly dark5: string;\n readonly destructiveBgColor: string;\n readonly destructiveColor: string;\n readonly destructiveFgColor: string;\n readonly dialogBackdropBlur: number;\n readonly dialogBackdropColor: string;\n readonly dialogBgColor: string;\n readonly dialogFgColor: string;\n readonly dividerColor: string;\n readonly durationFast: number;\n readonly durationNormal: number;\n readonly durationSlow: number;\n readonly easingDefault: readonly number[];\n readonly errorBgColor: string;\n readonly errorColor: string;\n readonly errorFgColor: string;\n readonly focusRingColor: string;\n readonly focusRingOffset: number;\n readonly focusRingWidth: number;\n readonly fontFamily: string;\n readonly fontFamilyMono: string;\n readonly fontSizeBody: number;\n readonly fontSizeCaption: number;\n readonly fontSizeLargeTitle: number;\n readonly fontSizeTitle1: number;\n readonly fontSizeTitle2: number;\n readonly fontSizeTitle3: number;\n readonly fontSizeTitle4: number;\n readonly fontWeightBold: number;\n readonly fontWeightNormal: number;\n readonly fontWeightSemibold: number;\n readonly green1: string;\n readonly green2: string;\n readonly green3: string;\n readonly green4: string;\n readonly green5: string;\n readonly headerbarBgColor: string;\n readonly headerbarBorderColor: string;\n readonly headerbarFgColor: string;\n readonly headerbarShadeColor: string;\n readonly hoverOverlay: string;\n readonly layoutBreakpointMedium: number;\n readonly layoutBreakpointNarrow: number;\n readonly layoutBreakpointWide: number;\n readonly layoutContentPadding: number;\n readonly layoutContentPaddingCompact: number;\n readonly layoutContentPaddingSpacious: number;\n readonly layoutSidebarDefaultWidth: number;\n readonly layoutSidebarMaxWidth: number;\n readonly layoutSidebarMinWidth: number;\n readonly layoutSidebarRailWidth: number;\n readonly light1: string;\n readonly light2: string;\n readonly light3: string;\n readonly light4: string;\n readonly light5: string;\n readonly lineHeightBody: number;\n readonly lineHeightHeading: number;\n readonly lineHeightTitle: number;\n readonly newBgColor: string;\n readonly newColor: string;\n readonly newFgColor: string;\n readonly opacityBorder: number;\n readonly opacityDim: number;\n readonly opacityDisabled: number;\n readonly orange1: string;\n readonly orange2: string;\n readonly orange3: string;\n readonly orange4: string;\n readonly orange5: string;\n readonly popoverBgColor: string;\n readonly popoverFgColor: string;\n readonly purple1: string;\n readonly purple2: string;\n readonly purple3: string;\n readonly purple4: string;\n readonly purple5: string;\n readonly radiusLg: number;\n readonly radiusMd: number;\n readonly radiusPill: number;\n readonly radiusSm: number;\n readonly radiusXl: number;\n readonly red1: string;\n readonly red2: string;\n readonly red3: string;\n readonly red4: string;\n readonly red5: string;\n readonly sidebarBgColor: string;\n readonly sidebarFgColor: string;\n readonly skeletonBaseColor: string;\n readonly skeletonHighlightColor: string;\n readonly space1: number;\n readonly space2: number;\n readonly space3: number;\n readonly space4: number;\n readonly space5: number;\n readonly space6: number;\n readonly successBgColor: string;\n readonly successColor: string;\n readonly successFgColor: string;\n readonly viewBgColor: string;\n readonly viewFgColor: string;\n readonly warningBgColor: string;\n readonly warningColor: string;\n readonly warningFgColor: string;\n readonly windowBgColor: string;\n readonly windowFgColor: string;\n readonly yellow1: string;\n readonly yellow2: string;\n readonly yellow3: string;\n readonly yellow4: string;\n readonly yellow5: string;\n}\n\nexport const lightTheme: GnomeThemeTokens = {\n accentBgColor: '#3584e4',\n accentColor: '#3584e4',\n accentFgColor: '#fff',\n activeOverlay: 'rgba(0, 0, 0, 0.12)',\n blockquoteBorderWidth: 3,\n blue1: '#99c1f1',\n blue2: '#62a0ea',\n blue3: '#3584e4',\n blue4: '#1c71d8',\n blue5: '#1a5fb4',\n borderSubtle: 'rgba(0, 0, 0, 0.15)',\n brown1: '#cdab8f',\n brown2: '#b5835a',\n brown3: '#986a44',\n brown4: '#865e3c',\n brown5: '#63452c',\n cardBgColor: '#fff',\n cardFgColor: 'rgba(0, 0, 0, 0.8)',\n cardShadeColor: 'rgba(0, 0, 0, 0.12)',\n dark1: '#77767b',\n dark2: '#5e5c64',\n dark3: '#3d3846',\n dark4: '#241f31',\n dark5: '#000',\n destructiveBgColor: '#e01b24',\n destructiveColor: '#e01b24',\n destructiveFgColor: '#fff',\n dialogBackdropBlur: 4,\n dialogBackdropColor: 'rgba(0, 0, 0, 0.5)',\n dialogBgColor: '#fafafa',\n dialogFgColor: 'rgba(0, 0, 0, 0.8)',\n dividerColor: 'rgba(0, 0, 0, 0.07)',\n durationFast: 100,\n durationNormal: 200,\n durationSlow: 400,\n easingDefault: [0.25, 0.46, 0.45, 0.94],\n errorBgColor: '#e01b24',\n errorColor: '#e01b24',\n errorFgColor: '#fff',\n focusRingColor: '#3584e4',\n focusRingOffset: 2,\n focusRingWidth: 3,\n fontFamily: 'Adwaita Sans',\n fontFamilyMono: 'Adwaita Mono',\n fontSizeBody: 16,\n fontSizeCaption: 12,\n fontSizeLargeTitle: 36,\n fontSizeTitle1: 30,\n fontSizeTitle2: 24,\n fontSizeTitle3: 20,\n fontSizeTitle4: 18,\n fontWeightBold: 700,\n fontWeightNormal: 400,\n fontWeightSemibold: 600,\n green1: '#8ff0a4',\n green2: '#57e389',\n green3: '#33d17a',\n green4: '#2ec27e',\n green5: '#26a269',\n headerbarBgColor: '#ebebeb',\n headerbarBorderColor: 'rgba(0, 0, 0, 0.12)',\n headerbarFgColor: 'rgba(0, 0, 0, 0.8)',\n headerbarShadeColor: 'rgba(0, 0, 0, 0.12)',\n hoverOverlay: 'rgba(0, 0, 0, 0.06)',\n layoutBreakpointMedium: 550,\n layoutBreakpointNarrow: 400,\n layoutBreakpointWide: 860,\n layoutContentPadding: 24,\n layoutContentPaddingCompact: 12,\n layoutContentPaddingSpacious: 36,\n layoutSidebarDefaultWidth: 240,\n layoutSidebarMaxWidth: 280,\n layoutSidebarMinWidth: 180,\n layoutSidebarRailWidth: 56,\n light1: '#fff',\n light2: '#f6f5f4',\n light3: '#deddda',\n light4: '#c0bfbc',\n light5: '#9a9996',\n lineHeightBody: 1.5,\n lineHeightHeading: 1.2,\n lineHeightTitle: 1.2,\n newBgColor: '#9141ac',\n newColor: '#9141ac',\n newFgColor: '#fff',\n opacityBorder: 0.15,\n opacityDim: 0.55,\n opacityDisabled: 0.5,\n orange1: '#ffbe6f',\n orange2: '#ffa348',\n orange3: '#ff7800',\n orange4: '#e66100',\n orange5: '#c64600',\n popoverBgColor: '#fff',\n popoverFgColor: 'rgba(0, 0, 0, 0.8)',\n purple1: '#dc8add',\n purple2: '#c061cb',\n purple3: '#9141ac',\n purple4: '#813d9c',\n purple5: '#613583',\n radiusLg: 12,\n radiusMd: 8,\n radiusPill: 9999,\n radiusSm: 4,\n radiusXl: 15,\n red1: '#f66151',\n red2: '#ed333b',\n red3: '#e01b24',\n red4: '#c01c28',\n red5: '#a51d2d',\n sidebarBgColor: '#ebebeb',\n sidebarFgColor: 'rgba(0, 0, 0, 0.8)',\n skeletonBaseColor: 'rgba(0, 0, 0, 0.09)',\n skeletonHighlightColor: 'rgba(255, 255, 255, 0.55)',\n space1: 6,\n space2: 12,\n space3: 18,\n space4: 24,\n space5: 36,\n space6: 48,\n successBgColor: '#2ec27e',\n successColor: '#2ec27e',\n successFgColor: '#fff',\n viewBgColor: '#fff',\n viewFgColor: 'rgba(0, 0, 0, 0.8)',\n warningBgColor: '#f6d32d',\n warningColor: '#e5a50a',\n warningFgColor: 'rgba(0, 0, 0, 0.8)',\n windowBgColor: '#fafafa',\n windowFgColor: 'rgba(0, 0, 0, 0.8)',\n yellow1: '#f9f06b',\n yellow2: '#f8e45c',\n yellow3: '#f6d32d',\n yellow4: '#f5c211',\n yellow5: '#e5a50a',\n} as const;\n\nexport const darkTheme: GnomeThemeTokens = {\n accentBgColor: '#3584e4',\n accentColor: '#62a0ea',\n accentFgColor: '#fff',\n activeOverlay: 'rgba(255, 255, 255, 0.14)',\n blockquoteBorderWidth: 3,\n blue1: '#99c1f1',\n blue2: '#62a0ea',\n blue3: '#3584e4',\n blue4: '#1c71d8',\n blue5: '#1a5fb4',\n borderSubtle: 'rgba(255, 255, 255, 0.15)',\n brown1: '#cdab8f',\n brown2: '#b5835a',\n brown3: '#986a44',\n brown4: '#865e3c',\n brown5: '#63452c',\n cardBgColor: '#383838',\n cardFgColor: 'rgba(255, 255, 255, 0.87)',\n cardShadeColor: 'rgba(255, 255, 255, 0.12)',\n dark1: '#77767b',\n dark2: '#5e5c64',\n dark3: '#3d3846',\n dark4: '#241f31',\n dark5: '#000',\n destructiveBgColor: '#e01b24',\n destructiveColor: '#e01b24',\n destructiveFgColor: '#fff',\n dialogBackdropBlur: 4,\n dialogBackdropColor: 'rgba(0, 0, 0, 0.65)',\n dialogBgColor: '#242424',\n dialogFgColor: 'rgba(255, 255, 255, 0.87)',\n dividerColor: 'rgba(255, 255, 255, 0.07)',\n durationFast: 100,\n durationNormal: 200,\n durationSlow: 400,\n easingDefault: [0.25, 0.46, 0.45, 0.94],\n errorBgColor: '#e01b24',\n errorColor: '#e01b24',\n errorFgColor: '#fff',\n focusRingColor: '#62a0ea',\n focusRingOffset: 2,\n focusRingWidth: 3,\n fontFamily: 'Adwaita Sans',\n fontFamilyMono: 'Adwaita Mono',\n fontSizeBody: 16,\n fontSizeCaption: 12,\n fontSizeLargeTitle: 36,\n fontSizeTitle1: 30,\n fontSizeTitle2: 24,\n fontSizeTitle3: 20,\n fontSizeTitle4: 18,\n fontWeightBold: 700,\n fontWeightNormal: 400,\n fontWeightSemibold: 600,\n green1: '#8ff0a4',\n green2: '#57e389',\n green3: '#33d17a',\n green4: '#2ec27e',\n green5: '#26a269',\n headerbarBgColor: '#303030',\n headerbarBorderColor: 'rgba(255, 255, 255, 0.1)',\n headerbarFgColor: 'rgba(255, 255, 255, 0.87)',\n headerbarShadeColor: 'rgba(0, 0, 0, 0.36)',\n hoverOverlay: 'rgba(255, 255, 255, 0.08)',\n layoutBreakpointMedium: 550,\n layoutBreakpointNarrow: 400,\n layoutBreakpointWide: 860,\n layoutContentPadding: 24,\n layoutContentPaddingCompact: 12,\n layoutContentPaddingSpacious: 36,\n layoutSidebarDefaultWidth: 240,\n layoutSidebarMaxWidth: 280,\n layoutSidebarMinWidth: 180,\n layoutSidebarRailWidth: 56,\n light1: '#fff',\n light2: '#f6f5f4',\n light3: '#deddda',\n light4: '#c0bfbc',\n light5: '#9a9996',\n lineHeightBody: 1.5,\n lineHeightHeading: 1.2,\n lineHeightTitle: 1.2,\n newBgColor: '#9141ac',\n newColor: '#9141ac',\n newFgColor: '#fff',\n opacityBorder: 0.15,\n opacityDim: 0.55,\n opacityDisabled: 0.5,\n orange1: '#ffbe6f',\n orange2: '#ffa348',\n orange3: '#ff7800',\n orange4: '#e66100',\n orange5: '#c64600',\n popoverBgColor: '#383838',\n popoverFgColor: 'rgba(255, 255, 255, 0.87)',\n purple1: '#dc8add',\n purple2: '#c061cb',\n purple3: '#9141ac',\n purple4: '#813d9c',\n purple5: '#613583',\n radiusLg: 12,\n radiusMd: 8,\n radiusPill: 9999,\n radiusSm: 4,\n radiusXl: 15,\n red1: '#f66151',\n red2: '#ed333b',\n red3: '#e01b24',\n red4: '#c01c28',\n red5: '#a51d2d',\n sidebarBgColor: '#303030',\n sidebarFgColor: 'rgba(255, 255, 255, 0.87)',\n skeletonBaseColor: 'rgba(255, 255, 255, 0.12)',\n skeletonHighlightColor: 'rgba(255, 255, 255, 0.18)',\n space1: 6,\n space2: 12,\n space3: 18,\n space4: 24,\n space5: 36,\n space6: 48,\n successBgColor: '#2ec27e',\n successColor: '#2ec27e',\n successFgColor: '#fff',\n viewBgColor: '#1e1e1e',\n viewFgColor: 'rgba(255, 255, 255, 0.87)',\n warningBgColor: '#f6d32d',\n warningColor: '#e5a50a',\n warningFgColor: 'rgba(0, 0, 0, 0.8)',\n windowBgColor: '#242424',\n windowFgColor: 'rgba(255, 255, 255, 0.87)',\n yellow1: '#f9f06b',\n yellow2: '#f8e45c',\n yellow3: '#f6d32d',\n yellow4: '#f5c211',\n yellow5: '#e5a50a',\n} as const;\n\nexport const highContrastTheme: GnomeThemeTokens = {\n accentBgColor: '#1a5fb4',\n accentColor: '#1a5fb4',\n accentFgColor: '#fff',\n activeOverlay: 'rgba(0, 0, 0, 0.22)',\n blockquoteBorderWidth: 3,\n blue1: '#99c1f1',\n blue2: '#62a0ea',\n blue3: '#3584e4',\n blue4: '#1c71d8',\n blue5: '#1a5fb4',\n borderSubtle: 'rgba(0, 0, 0, 0.5)',\n brown1: '#cdab8f',\n brown2: '#b5835a',\n brown3: '#986a44',\n brown4: '#865e3c',\n brown5: '#63452c',\n cardBgColor: '#fff',\n cardFgColor: 'rgba(0, 0, 0, 0.8)',\n cardShadeColor: 'rgba(0, 0, 0, 0.12)',\n dark1: '#77767b',\n dark2: '#5e5c64',\n dark3: '#3d3846',\n dark4: '#241f31',\n dark5: '#000',\n destructiveBgColor: '#a51d2d',\n destructiveColor: '#a51d2d',\n destructiveFgColor: '#fff',\n dialogBackdropBlur: 4,\n dialogBackdropColor: 'rgba(0, 0, 0, 0.5)',\n dialogBgColor: '#fafafa',\n dialogFgColor: 'rgba(0, 0, 0, 0.8)',\n dividerColor: 'rgba(0, 0, 0, 0.2)',\n durationFast: 100,\n durationNormal: 200,\n durationSlow: 400,\n easingDefault: [0.25, 0.46, 0.45, 0.94],\n errorBgColor: '#a51d2d',\n errorColor: '#a51d2d',\n errorFgColor: '#fff',\n focusRingColor: '#1a5fb4',\n focusRingOffset: 2,\n focusRingWidth: 3,\n fontFamily: 'Adwaita Sans',\n fontFamilyMono: 'Adwaita Mono',\n fontSizeBody: 16,\n fontSizeCaption: 12,\n fontSizeLargeTitle: 36,\n fontSizeTitle1: 30,\n fontSizeTitle2: 24,\n fontSizeTitle3: 20,\n fontSizeTitle4: 18,\n fontWeightBold: 700,\n fontWeightNormal: 400,\n fontWeightSemibold: 600,\n green1: '#8ff0a4',\n green2: '#57e389',\n green3: '#33d17a',\n green4: '#2ec27e',\n green5: '#26a269',\n headerbarBgColor: '#ebebeb',\n headerbarBorderColor: 'rgba(0, 0, 0, 0.12)',\n headerbarFgColor: 'rgba(0, 0, 0, 0.8)',\n headerbarShadeColor: 'rgba(0, 0, 0, 0.12)',\n hoverOverlay: 'rgba(0, 0, 0, 0.12)',\n layoutBreakpointMedium: 550,\n layoutBreakpointNarrow: 400,\n layoutBreakpointWide: 860,\n layoutContentPadding: 24,\n layoutContentPaddingCompact: 12,\n layoutContentPaddingSpacious: 36,\n layoutSidebarDefaultWidth: 240,\n layoutSidebarMaxWidth: 280,\n layoutSidebarMinWidth: 180,\n layoutSidebarRailWidth: 56,\n light1: '#fff',\n light2: '#f6f5f4',\n light3: '#deddda',\n light4: '#c0bfbc',\n light5: '#9a9996',\n lineHeightBody: 1.5,\n lineHeightHeading: 1.2,\n lineHeightTitle: 1.2,\n newBgColor: '#613583',\n newColor: '#613583',\n newFgColor: '#fff',\n opacityBorder: 0.4,\n opacityDim: 0.7,\n opacityDisabled: 0.6,\n orange1: '#ffbe6f',\n orange2: '#ffa348',\n orange3: '#ff7800',\n orange4: '#e66100',\n orange5: '#c64600',\n popoverBgColor: '#fff',\n popoverFgColor: 'rgba(0, 0, 0, 0.8)',\n purple1: '#dc8add',\n purple2: '#c061cb',\n purple3: '#9141ac',\n purple4: '#813d9c',\n purple5: '#613583',\n radiusLg: 12,\n radiusMd: 8,\n radiusPill: 9999,\n radiusSm: 4,\n radiusXl: 15,\n red1: '#f66151',\n red2: '#ed333b',\n red3: '#e01b24',\n red4: '#c01c28',\n red5: '#a51d2d',\n sidebarBgColor: '#ebebeb',\n sidebarFgColor: 'rgba(0, 0, 0, 0.8)',\n skeletonBaseColor: 'rgba(0, 0, 0, 0.09)',\n skeletonHighlightColor: 'rgba(255, 255, 255, 0.55)',\n space1: 6,\n space2: 12,\n space3: 18,\n space4: 24,\n space5: 36,\n space6: 48,\n successBgColor: '#26a269',\n successColor: '#26a269',\n successFgColor: '#fff',\n viewBgColor: '#fff',\n viewFgColor: 'rgba(0, 0, 0, 0.8)',\n warningBgColor: '#e66100',\n warningColor: '#c64600',\n warningFgColor: 'rgba(0, 0, 0, 0.8)',\n windowBgColor: '#fafafa',\n windowFgColor: 'rgba(0, 0, 0, 0.8)',\n yellow1: '#f9f06b',\n yellow2: '#f8e45c',\n yellow3: '#f6d32d',\n yellow4: '#f5c211',\n yellow5: '#e5a50a',\n} as const;\n\nexport const highContrastDarkTheme: GnomeThemeTokens = {\n accentBgColor: '#62a0ea',\n accentColor: '#99c1f1',\n accentFgColor: '#fff',\n activeOverlay: 'rgba(255, 255, 255, 0.22)',\n blockquoteBorderWidth: 3,\n blue1: '#99c1f1',\n blue2: '#62a0ea',\n blue3: '#3584e4',\n blue4: '#1c71d8',\n blue5: '#1a5fb4',\n borderSubtle: 'rgba(255, 255, 255, 0.5)',\n brown1: '#cdab8f',\n brown2: '#b5835a',\n brown3: '#986a44',\n brown4: '#865e3c',\n brown5: '#63452c',\n cardBgColor: '#383838',\n cardFgColor: '#fff',\n cardShadeColor: 'rgba(255, 255, 255, 0.12)',\n dark1: '#77767b',\n dark2: '#5e5c64',\n dark3: '#3d3846',\n dark4: '#241f31',\n dark5: '#000',\n destructiveBgColor: '#ed333b',\n destructiveColor: '#f66151',\n destructiveFgColor: '#fff',\n dialogBackdropBlur: 4,\n dialogBackdropColor: 'rgba(0, 0, 0, 0.65)',\n dialogBgColor: '#242424',\n dialogFgColor: '#fff',\n dividerColor: 'rgba(255, 255, 255, 0.2)',\n durationFast: 100,\n durationNormal: 200,\n durationSlow: 400,\n easingDefault: [0.25, 0.46, 0.45, 0.94],\n errorBgColor: '#ed333b',\n errorColor: '#f66151',\n errorFgColor: '#fff',\n focusRingColor: '#99c1f1',\n focusRingOffset: 2,\n focusRingWidth: 3,\n fontFamily: 'Adwaita Sans',\n fontFamilyMono: 'Adwaita Mono',\n fontSizeBody: 16,\n fontSizeCaption: 12,\n fontSizeLargeTitle: 36,\n fontSizeTitle1: 30,\n fontSizeTitle2: 24,\n fontSizeTitle3: 20,\n fontSizeTitle4: 18,\n fontWeightBold: 700,\n fontWeightNormal: 400,\n fontWeightSemibold: 600,\n green1: '#8ff0a4',\n green2: '#57e389',\n green3: '#33d17a',\n green4: '#2ec27e',\n green5: '#26a269',\n headerbarBgColor: '#303030',\n headerbarBorderColor: 'rgba(255, 255, 255, 0.3)',\n headerbarFgColor: '#fff',\n headerbarShadeColor: 'rgba(0, 0, 0, 0.36)',\n hoverOverlay: 'rgba(255, 255, 255, 0.12)',\n layoutBreakpointMedium: 550,\n layoutBreakpointNarrow: 400,\n layoutBreakpointWide: 860,\n layoutContentPadding: 24,\n layoutContentPaddingCompact: 12,\n layoutContentPaddingSpacious: 36,\n layoutSidebarDefaultWidth: 240,\n layoutSidebarMaxWidth: 280,\n layoutSidebarMinWidth: 180,\n layoutSidebarRailWidth: 56,\n light1: '#fff',\n light2: '#f6f5f4',\n light3: '#deddda',\n light4: '#c0bfbc',\n light5: '#9a9996',\n lineHeightBody: 1.5,\n lineHeightHeading: 1.2,\n lineHeightTitle: 1.2,\n newBgColor: '#c061cb',\n newColor: '#dc8add',\n newFgColor: '#fff',\n opacityBorder: 0.4,\n opacityDim: 0.7,\n opacityDisabled: 0.6,\n orange1: '#ffbe6f',\n orange2: '#ffa348',\n orange3: '#ff7800',\n orange4: '#e66100',\n orange5: '#c64600',\n popoverBgColor: '#383838',\n popoverFgColor: '#fff',\n purple1: '#dc8add',\n purple2: '#c061cb',\n purple3: '#9141ac',\n purple4: '#813d9c',\n purple5: '#613583',\n radiusLg: 12,\n radiusMd: 8,\n radiusPill: 9999,\n radiusSm: 4,\n radiusXl: 15,\n red1: '#f66151',\n red2: '#ed333b',\n red3: '#e01b24',\n red4: '#c01c28',\n red5: '#a51d2d',\n sidebarBgColor: '#303030',\n sidebarFgColor: '#fff',\n skeletonBaseColor: 'rgba(255, 255, 255, 0.12)',\n skeletonHighlightColor: 'rgba(255, 255, 255, 0.18)',\n space1: 6,\n space2: 12,\n space3: 18,\n space4: 24,\n space5: 36,\n space6: 48,\n successBgColor: '#57e389',\n successColor: '#8ff0a4',\n successFgColor: '#fff',\n viewBgColor: '#1e1e1e',\n viewFgColor: '#fff',\n warningBgColor: '#f8e45c',\n warningColor: '#f9f06b',\n warningFgColor: 'rgba(0, 0, 0, 0.9)',\n windowBgColor: '#242424',\n windowFgColor: '#fff',\n yellow1: '#f9f06b',\n yellow2: '#f8e45c',\n yellow3: '#f6d32d',\n yellow4: '#f5c211',\n yellow5: '#e5a50a',\n} as const;\n\nexport const lightRawTokens = {\n '--gnome-accent-bg-color': '#3584e4',\n '--gnome-accent-color': '#3584e4',\n '--gnome-accent-fg-color': '#fff',\n '--gnome-active-overlay': 'rgb(0 0 0 / 0.12)',\n '--gnome-blockquote-border-width': '3px',\n '--gnome-blue-1': '#99c1f1',\n '--gnome-blue-2': '#62a0ea',\n '--gnome-blue-3': '#3584e4',\n '--gnome-blue-4': '#1c71d8',\n '--gnome-blue-5': '#1a5fb4',\n '--gnome-border-subtle': 'rgb(0 0 0 / 0.15)',\n '--gnome-brown-1': '#cdab8f',\n '--gnome-brown-2': '#b5835a',\n '--gnome-brown-3': '#986a44',\n '--gnome-brown-4': '#865e3c',\n '--gnome-brown-5': '#63452c',\n '--gnome-card-bg-color': '#fff',\n '--gnome-card-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-card-shade-color': 'rgb(0 0 0 / 0.12)',\n '--gnome-dark-1': '#77767b',\n '--gnome-dark-2': '#5e5c64',\n '--gnome-dark-3': '#3d3846',\n '--gnome-dark-4': '#241f31',\n '--gnome-dark-5': '#000',\n '--gnome-destructive-bg-color': '#e01b24',\n '--gnome-destructive-color': '#e01b24',\n '--gnome-destructive-fg-color': '#fff',\n '--gnome-dialog-backdrop-blur': '4px',\n '--gnome-dialog-backdrop-color': 'rgb(0 0 0 / 0.5)',\n '--gnome-dialog-bg-color': '#fafafa',\n '--gnome-dialog-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-divider-color': 'rgb(0 0 0 / 0.07)',\n '--gnome-duration-fast': '100ms',\n '--gnome-duration-normal': '200ms',\n '--gnome-duration-slow': '400ms',\n '--gnome-easing-default': 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',\n '--gnome-error-bg-color': '#e01b24',\n '--gnome-error-color': '#e01b24',\n '--gnome-error-fg-color': '#fff',\n '--gnome-focus-ring-color': '#3584e4',\n '--gnome-focus-ring-offset': '2px',\n '--gnome-focus-ring-width': '3px',\n '--gnome-font-family': '\"Adwaita Sans\", cantarell, \"Inter\", system-ui, sans-serif',\n '--gnome-font-family-mono': '\"Adwaita Mono\", \"Source Code Pro\", monospace',\n '--gnome-font-size-body': '1rem',\n '--gnome-font-size-caption': '0.75rem',\n '--gnome-font-size-large-title': '2.25rem',\n '--gnome-font-size-title-1': '1.875rem',\n '--gnome-font-size-title-2': '1.5rem',\n '--gnome-font-size-title-3': '1.25rem',\n '--gnome-font-size-title-4': '1.125rem',\n '--gnome-font-weight-bold': '700',\n '--gnome-font-weight-normal': '400',\n '--gnome-font-weight-semibold': '600',\n '--gnome-green-1': '#8ff0a4',\n '--gnome-green-2': '#57e389',\n '--gnome-green-3': '#33d17a',\n '--gnome-green-4': '#2ec27e',\n '--gnome-green-5': '#26a269',\n '--gnome-headerbar-bg-color': '#ebebeb',\n '--gnome-headerbar-border-color': 'rgb(0 0 0 / 0.12)',\n '--gnome-headerbar-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-headerbar-shade-color': 'rgb(0 0 0 / 0.12)',\n '--gnome-hover-overlay': 'rgb(0 0 0 / 0.06)',\n '--gnome-layout-breakpoint-medium': '550px',\n '--gnome-layout-breakpoint-narrow': '400px',\n '--gnome-layout-breakpoint-wide': '860px',\n '--gnome-layout-content-padding': '24px',\n '--gnome-layout-content-padding-compact': '12px',\n '--gnome-layout-content-padding-spacious': '36px',\n '--gnome-layout-sidebar-default-width': '240px',\n '--gnome-layout-sidebar-max-width': '280px',\n '--gnome-layout-sidebar-min-width': '180px',\n '--gnome-layout-sidebar-overlay-width': 'clamp( 180px, 75%, 280px )',\n '--gnome-layout-sidebar-rail-width': '56px',\n '--gnome-layout-sidebar-width': 'clamp( 180px, 25%, 280px )',\n '--gnome-light-1': '#fff',\n '--gnome-light-2': '#f6f5f4',\n '--gnome-light-3': '#deddda',\n '--gnome-light-4': '#c0bfbc',\n '--gnome-light-5': '#9a9996',\n '--gnome-line-height-body': '1.5',\n '--gnome-line-height-heading': '1.2',\n '--gnome-line-height-title': '1.2',\n '--gnome-new-bg-color': '#9141ac',\n '--gnome-new-color': '#9141ac',\n '--gnome-new-fg-color': '#fff',\n '--gnome-opacity-border': '0.15',\n '--gnome-opacity-dim': '0.55',\n '--gnome-opacity-disabled': '0.5',\n '--gnome-orange-1': '#ffbe6f',\n '--gnome-orange-2': '#ffa348',\n '--gnome-orange-3': '#ff7800',\n '--gnome-orange-4': '#e66100',\n '--gnome-orange-5': '#c64600',\n '--gnome-popover-bg-color': '#fff',\n '--gnome-popover-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-purple-1': '#dc8add',\n '--gnome-purple-2': '#c061cb',\n '--gnome-purple-3': '#9141ac',\n '--gnome-purple-4': '#813d9c',\n '--gnome-purple-5': '#613583',\n '--gnome-radius-lg': '12px',\n '--gnome-radius-md': '8px',\n '--gnome-radius-pill': '9999px',\n '--gnome-radius-sm': '4px',\n '--gnome-radius-xl': '15px',\n '--gnome-red-1': '#f66151',\n '--gnome-red-2': '#ed333b',\n '--gnome-red-3': '#e01b24',\n '--gnome-red-4': '#c01c28',\n '--gnome-red-5': '#a51d2d',\n '--gnome-shadow-lg':\n '0 3px 9px 1px rgb(0 0 0 / 0.1), 0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(28 31 31 / 0.9)',\n '--gnome-shadow-md': '0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(0 0 0 / 0.08)',\n '--gnome-shadow-sm': '0 1px 2px rgb(0 0 0 / 0.12)',\n '--gnome-sidebar-bg-color': '#ebebeb',\n '--gnome-sidebar-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-skeleton-base-color': 'rgb(0 0 0 / 0.09)',\n '--gnome-skeleton-highlight-color': 'rgb(255 255 255 / 0.55)',\n '--gnome-space-1': '6px',\n '--gnome-space-2': '12px',\n '--gnome-space-3': '18px',\n '--gnome-space-4': '24px',\n '--gnome-space-5': '36px',\n '--gnome-space-6': '48px',\n '--gnome-success-bg-color': '#2ec27e',\n '--gnome-success-color': '#2ec27e',\n '--gnome-success-fg-color': '#fff',\n '--gnome-view-bg-color': '#fff',\n '--gnome-view-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-warning-bg-color': '#f6d32d',\n '--gnome-warning-color': '#e5a50a',\n '--gnome-warning-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-window-bg-color': '#fafafa',\n '--gnome-window-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-yellow-1': '#f9f06b',\n '--gnome-yellow-2': '#f8e45c',\n '--gnome-yellow-3': '#f6d32d',\n '--gnome-yellow-4': '#f5c211',\n '--gnome-yellow-5': '#e5a50a',\n} as const;\n\nexport const darkRawTokens = {\n '--gnome-accent-bg-color': '#3584e4',\n '--gnome-accent-color': '#62a0ea',\n '--gnome-accent-fg-color': '#fff',\n '--gnome-active-overlay': 'rgb(255 255 255 / 0.14)',\n '--gnome-blockquote-border-width': '3px',\n '--gnome-blue-1': '#99c1f1',\n '--gnome-blue-2': '#62a0ea',\n '--gnome-blue-3': '#3584e4',\n '--gnome-blue-4': '#1c71d8',\n '--gnome-blue-5': '#1a5fb4',\n '--gnome-border-subtle': 'rgb(255 255 255 / 0.15)',\n '--gnome-brown-1': '#cdab8f',\n '--gnome-brown-2': '#b5835a',\n '--gnome-brown-3': '#986a44',\n '--gnome-brown-4': '#865e3c',\n '--gnome-brown-5': '#63452c',\n '--gnome-card-bg-color': '#383838',\n '--gnome-card-fg-color': 'rgb(255 255 255 / 0.87)',\n '--gnome-card-shade-color': 'rgb(255 255 255 / 0.12)',\n '--gnome-dark-1': '#77767b',\n '--gnome-dark-2': '#5e5c64',\n '--gnome-dark-3': '#3d3846',\n '--gnome-dark-4': '#241f31',\n '--gnome-dark-5': '#000',\n '--gnome-destructive-bg-color': '#e01b24',\n '--gnome-destructive-color': '#e01b24',\n '--gnome-destructive-fg-color': '#fff',\n '--gnome-dialog-backdrop-blur': '4px',\n '--gnome-dialog-backdrop-color': 'rgb(0 0 0 / 0.65)',\n '--gnome-dialog-bg-color': '#242424',\n '--gnome-dialog-fg-color': 'rgb(255 255 255 / 0.87)',\n '--gnome-divider-color': 'rgb(255 255 255 / 0.07)',\n '--gnome-duration-fast': '100ms',\n '--gnome-duration-normal': '200ms',\n '--gnome-duration-slow': '400ms',\n '--gnome-easing-default': 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',\n '--gnome-error-bg-color': '#e01b24',\n '--gnome-error-color': '#e01b24',\n '--gnome-error-fg-color': '#fff',\n '--gnome-focus-ring-color': '#62a0ea',\n '--gnome-focus-ring-offset': '2px',\n '--gnome-focus-ring-width': '3px',\n '--gnome-font-family': '\"Adwaita Sans\", cantarell, \"Inter\", system-ui, sans-serif',\n '--gnome-font-family-mono': '\"Adwaita Mono\", \"Source Code Pro\", monospace',\n '--gnome-font-size-body': '1rem',\n '--gnome-font-size-caption': '0.75rem',\n '--gnome-font-size-large-title': '2.25rem',\n '--gnome-font-size-title-1': '1.875rem',\n '--gnome-font-size-title-2': '1.5rem',\n '--gnome-font-size-title-3': '1.25rem',\n '--gnome-font-size-title-4': '1.125rem',\n '--gnome-font-weight-bold': '700',\n '--gnome-font-weight-normal': '400',\n '--gnome-font-weight-semibold': '600',\n '--gnome-green-1': '#8ff0a4',\n '--gnome-green-2': '#57e389',\n '--gnome-green-3': '#33d17a',\n '--gnome-green-4': '#2ec27e',\n '--gnome-green-5': '#26a269',\n '--gnome-headerbar-bg-color': '#303030',\n '--gnome-headerbar-border-color': 'rgb(255 255 255 / 0.1)',\n '--gnome-headerbar-fg-color': 'rgb(255 255 255 / 0.87)',\n '--gnome-headerbar-shade-color': 'rgb(0 0 0 / 0.36)',\n '--gnome-hover-overlay': 'rgb(255 255 255 / 0.08)',\n '--gnome-layout-breakpoint-medium': '550px',\n '--gnome-layout-breakpoint-narrow': '400px',\n '--gnome-layout-breakpoint-wide': '860px',\n '--gnome-layout-content-padding': '24px',\n '--gnome-layout-content-padding-compact': '12px',\n '--gnome-layout-content-padding-spacious': '36px',\n '--gnome-layout-sidebar-default-width': '240px',\n '--gnome-layout-sidebar-max-width': '280px',\n '--gnome-layout-sidebar-min-width': '180px',\n '--gnome-layout-sidebar-overlay-width': 'clamp( 180px, 75%, 280px )',\n '--gnome-layout-sidebar-rail-width': '56px',\n '--gnome-layout-sidebar-width': 'clamp( 180px, 25%, 280px )',\n '--gnome-light-1': '#fff',\n '--gnome-light-2': '#f6f5f4',\n '--gnome-light-3': '#deddda',\n '--gnome-light-4': '#c0bfbc',\n '--gnome-light-5': '#9a9996',\n '--gnome-line-height-body': '1.5',\n '--gnome-line-height-heading': '1.2',\n '--gnome-line-height-title': '1.2',\n '--gnome-new-bg-color': '#9141ac',\n '--gnome-new-color': '#9141ac',\n '--gnome-new-fg-color': '#fff',\n '--gnome-opacity-border': '0.15',\n '--gnome-opacity-dim': '0.55',\n '--gnome-opacity-disabled': '0.5',\n '--gnome-orange-1': '#ffbe6f',\n '--gnome-orange-2': '#ffa348',\n '--gnome-orange-3': '#ff7800',\n '--gnome-orange-4': '#e66100',\n '--gnome-orange-5': '#c64600',\n '--gnome-popover-bg-color': '#383838',\n '--gnome-popover-fg-color': 'rgb(255 255 255 / 0.87)',\n '--gnome-purple-1': '#dc8add',\n '--gnome-purple-2': '#c061cb',\n '--gnome-purple-3': '#9141ac',\n '--gnome-purple-4': '#813d9c',\n '--gnome-purple-5': '#613583',\n '--gnome-radius-lg': '12px',\n '--gnome-radius-md': '8px',\n '--gnome-radius-pill': '9999px',\n '--gnome-radius-sm': '4px',\n '--gnome-radius-xl': '15px',\n '--gnome-red-1': '#f66151',\n '--gnome-red-2': '#ed333b',\n '--gnome-red-3': '#e01b24',\n '--gnome-red-4': '#c01c28',\n '--gnome-red-5': '#a51d2d',\n '--gnome-shadow-lg':\n '0 3px 9px 1px rgb(0 0 0 / 0.1), 0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(28 31 31 / 0.9)',\n '--gnome-shadow-md': '0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(0 0 0 / 0.08)',\n '--gnome-shadow-sm': '0 1px 2px rgb(0 0 0 / 0.12)',\n '--gnome-sidebar-bg-color': '#303030',\n '--gnome-sidebar-fg-color': 'rgb(255 255 255 / 0.87)',\n '--gnome-skeleton-base-color': 'rgb(255 255 255 / 0.12)',\n '--gnome-skeleton-highlight-color': 'rgb(255 255 255 / 0.18)',\n '--gnome-space-1': '6px',\n '--gnome-space-2': '12px',\n '--gnome-space-3': '18px',\n '--gnome-space-4': '24px',\n '--gnome-space-5': '36px',\n '--gnome-space-6': '48px',\n '--gnome-success-bg-color': '#2ec27e',\n '--gnome-success-color': '#2ec27e',\n '--gnome-success-fg-color': '#fff',\n '--gnome-view-bg-color': '#1e1e1e',\n '--gnome-view-fg-color': 'rgb(255 255 255 / 0.87)',\n '--gnome-warning-bg-color': '#f6d32d',\n '--gnome-warning-color': '#e5a50a',\n '--gnome-warning-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-window-bg-color': '#242424',\n '--gnome-window-fg-color': 'rgb(255 255 255 / 0.87)',\n '--gnome-yellow-1': '#f9f06b',\n '--gnome-yellow-2': '#f8e45c',\n '--gnome-yellow-3': '#f6d32d',\n '--gnome-yellow-4': '#f5c211',\n '--gnome-yellow-5': '#e5a50a',\n} as const;\n\nexport const highContrastRawTokens = {\n '--gnome-accent-bg-color': '#1a5fb4',\n '--gnome-accent-color': '#1a5fb4',\n '--gnome-accent-fg-color': '#fff',\n '--gnome-active-overlay': 'rgb(0 0 0 / 0.22)',\n '--gnome-blockquote-border-width': '3px',\n '--gnome-blue-1': '#99c1f1',\n '--gnome-blue-2': '#62a0ea',\n '--gnome-blue-3': '#3584e4',\n '--gnome-blue-4': '#1c71d8',\n '--gnome-blue-5': '#1a5fb4',\n '--gnome-border-subtle': 'rgb(0 0 0 / 0.5)',\n '--gnome-brown-1': '#cdab8f',\n '--gnome-brown-2': '#b5835a',\n '--gnome-brown-3': '#986a44',\n '--gnome-brown-4': '#865e3c',\n '--gnome-brown-5': '#63452c',\n '--gnome-card-bg-color': '#fff',\n '--gnome-card-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-card-shade-color': 'rgb(0 0 0 / 0.12)',\n '--gnome-dark-1': '#77767b',\n '--gnome-dark-2': '#5e5c64',\n '--gnome-dark-3': '#3d3846',\n '--gnome-dark-4': '#241f31',\n '--gnome-dark-5': '#000',\n '--gnome-destructive-bg-color': '#a51d2d',\n '--gnome-destructive-color': '#a51d2d',\n '--gnome-destructive-fg-color': '#fff',\n '--gnome-dialog-backdrop-blur': '4px',\n '--gnome-dialog-backdrop-color': 'rgb(0 0 0 / 0.5)',\n '--gnome-dialog-bg-color': '#fafafa',\n '--gnome-dialog-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-divider-color': 'rgb(0 0 0 / 0.2)',\n '--gnome-duration-fast': '100ms',\n '--gnome-duration-normal': '200ms',\n '--gnome-duration-slow': '400ms',\n '--gnome-easing-default': 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',\n '--gnome-error-bg-color': '#a51d2d',\n '--gnome-error-color': '#a51d2d',\n '--gnome-error-fg-color': '#fff',\n '--gnome-focus-ring-color': '#1a5fb4',\n '--gnome-focus-ring-offset': '2px',\n '--gnome-focus-ring-width': '3px',\n '--gnome-font-family': '\"Adwaita Sans\", cantarell, \"Inter\", system-ui, sans-serif',\n '--gnome-font-family-mono': '\"Adwaita Mono\", \"Source Code Pro\", monospace',\n '--gnome-font-size-body': '1rem',\n '--gnome-font-size-caption': '0.75rem',\n '--gnome-font-size-large-title': '2.25rem',\n '--gnome-font-size-title-1': '1.875rem',\n '--gnome-font-size-title-2': '1.5rem',\n '--gnome-font-size-title-3': '1.25rem',\n '--gnome-font-size-title-4': '1.125rem',\n '--gnome-font-weight-bold': '700',\n '--gnome-font-weight-normal': '400',\n '--gnome-font-weight-semibold': '600',\n '--gnome-green-1': '#8ff0a4',\n '--gnome-green-2': '#57e389',\n '--gnome-green-3': '#33d17a',\n '--gnome-green-4': '#2ec27e',\n '--gnome-green-5': '#26a269',\n '--gnome-headerbar-bg-color': '#ebebeb',\n '--gnome-headerbar-border-color': 'rgb(0 0 0 / 0.12)',\n '--gnome-headerbar-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-headerbar-shade-color': 'rgb(0 0 0 / 0.12)',\n '--gnome-hover-overlay': 'rgb(0 0 0 / 0.12)',\n '--gnome-layout-breakpoint-medium': '550px',\n '--gnome-layout-breakpoint-narrow': '400px',\n '--gnome-layout-breakpoint-wide': '860px',\n '--gnome-layout-content-padding': '24px',\n '--gnome-layout-content-padding-compact': '12px',\n '--gnome-layout-content-padding-spacious': '36px',\n '--gnome-layout-sidebar-default-width': '240px',\n '--gnome-layout-sidebar-max-width': '280px',\n '--gnome-layout-sidebar-min-width': '180px',\n '--gnome-layout-sidebar-overlay-width': 'clamp( 180px, 75%, 280px )',\n '--gnome-layout-sidebar-rail-width': '56px',\n '--gnome-layout-sidebar-width': 'clamp( 180px, 25%, 280px )',\n '--gnome-light-1': '#fff',\n '--gnome-light-2': '#f6f5f4',\n '--gnome-light-3': '#deddda',\n '--gnome-light-4': '#c0bfbc',\n '--gnome-light-5': '#9a9996',\n '--gnome-line-height-body': '1.5',\n '--gnome-line-height-heading': '1.2',\n '--gnome-line-height-title': '1.2',\n '--gnome-new-bg-color': '#613583',\n '--gnome-new-color': '#613583',\n '--gnome-new-fg-color': '#fff',\n '--gnome-opacity-border': '0.4',\n '--gnome-opacity-dim': '0.7',\n '--gnome-opacity-disabled': '0.6',\n '--gnome-orange-1': '#ffbe6f',\n '--gnome-orange-2': '#ffa348',\n '--gnome-orange-3': '#ff7800',\n '--gnome-orange-4': '#e66100',\n '--gnome-orange-5': '#c64600',\n '--gnome-popover-bg-color': '#fff',\n '--gnome-popover-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-purple-1': '#dc8add',\n '--gnome-purple-2': '#c061cb',\n '--gnome-purple-3': '#9141ac',\n '--gnome-purple-4': '#813d9c',\n '--gnome-purple-5': '#613583',\n '--gnome-radius-lg': '12px',\n '--gnome-radius-md': '8px',\n '--gnome-radius-pill': '9999px',\n '--gnome-radius-sm': '4px',\n '--gnome-radius-xl': '15px',\n '--gnome-red-1': '#f66151',\n '--gnome-red-2': '#ed333b',\n '--gnome-red-3': '#e01b24',\n '--gnome-red-4': '#c01c28',\n '--gnome-red-5': '#a51d2d',\n '--gnome-shadow-lg':\n '0 3px 9px 1px rgb(0 0 0 / 0.1), 0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(28 31 31 / 0.9)',\n '--gnome-shadow-md': '0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(0 0 0 / 0.08)',\n '--gnome-shadow-sm': '0 1px 2px rgb(0 0 0 / 0.12)',\n '--gnome-sidebar-bg-color': '#ebebeb',\n '--gnome-sidebar-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-skeleton-base-color': 'rgb(0 0 0 / 0.09)',\n '--gnome-skeleton-highlight-color': 'rgb(255 255 255 / 0.55)',\n '--gnome-space-1': '6px',\n '--gnome-space-2': '12px',\n '--gnome-space-3': '18px',\n '--gnome-space-4': '24px',\n '--gnome-space-5': '36px',\n '--gnome-space-6': '48px',\n '--gnome-success-bg-color': '#26a269',\n '--gnome-success-color': '#26a269',\n '--gnome-success-fg-color': '#fff',\n '--gnome-view-bg-color': '#fff',\n '--gnome-view-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-warning-bg-color': '#e66100',\n '--gnome-warning-color': '#c64600',\n '--gnome-warning-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-window-bg-color': '#fafafa',\n '--gnome-window-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-yellow-1': '#f9f06b',\n '--gnome-yellow-2': '#f8e45c',\n '--gnome-yellow-3': '#f6d32d',\n '--gnome-yellow-4': '#f5c211',\n '--gnome-yellow-5': '#e5a50a',\n} as const;\n\nexport const highContrastDarkRawTokens = {\n '--gnome-accent-bg-color': '#62a0ea',\n '--gnome-accent-color': '#99c1f1',\n '--gnome-accent-fg-color': '#fff',\n '--gnome-active-overlay': 'rgb(255 255 255 / 0.22)',\n '--gnome-blockquote-border-width': '3px',\n '--gnome-blue-1': '#99c1f1',\n '--gnome-blue-2': '#62a0ea',\n '--gnome-blue-3': '#3584e4',\n '--gnome-blue-4': '#1c71d8',\n '--gnome-blue-5': '#1a5fb4',\n '--gnome-border-subtle': 'rgb(255 255 255 / 0.5)',\n '--gnome-brown-1': '#cdab8f',\n '--gnome-brown-2': '#b5835a',\n '--gnome-brown-3': '#986a44',\n '--gnome-brown-4': '#865e3c',\n '--gnome-brown-5': '#63452c',\n '--gnome-card-bg-color': '#383838',\n '--gnome-card-fg-color': '#fff',\n '--gnome-card-shade-color': 'rgb(255 255 255 / 0.12)',\n '--gnome-dark-1': '#77767b',\n '--gnome-dark-2': '#5e5c64',\n '--gnome-dark-3': '#3d3846',\n '--gnome-dark-4': '#241f31',\n '--gnome-dark-5': '#000',\n '--gnome-destructive-bg-color': '#ed333b',\n '--gnome-destructive-color': '#f66151',\n '--gnome-destructive-fg-color': '#fff',\n '--gnome-dialog-backdrop-blur': '4px',\n '--gnome-dialog-backdrop-color': 'rgb(0 0 0 / 0.65)',\n '--gnome-dialog-bg-color': '#242424',\n '--gnome-dialog-fg-color': '#fff',\n '--gnome-divider-color': 'rgb(255 255 255 / 0.2)',\n '--gnome-duration-fast': '100ms',\n '--gnome-duration-normal': '200ms',\n '--gnome-duration-slow': '400ms',\n '--gnome-easing-default': 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',\n '--gnome-error-bg-color': '#ed333b',\n '--gnome-error-color': '#f66151',\n '--gnome-error-fg-color': '#fff',\n '--gnome-focus-ring-color': '#99c1f1',\n '--gnome-focus-ring-offset': '2px',\n '--gnome-focus-ring-width': '3px',\n '--gnome-font-family': '\"Adwaita Sans\", cantarell, \"Inter\", system-ui, sans-serif',\n '--gnome-font-family-mono': '\"Adwaita Mono\", \"Source Code Pro\", monospace',\n '--gnome-font-size-body': '1rem',\n '--gnome-font-size-caption': '0.75rem',\n '--gnome-font-size-large-title': '2.25rem',\n '--gnome-font-size-title-1': '1.875rem',\n '--gnome-font-size-title-2': '1.5rem',\n '--gnome-font-size-title-3': '1.25rem',\n '--gnome-font-size-title-4': '1.125rem',\n '--gnome-font-weight-bold': '700',\n '--gnome-font-weight-normal': '400',\n '--gnome-font-weight-semibold': '600',\n '--gnome-green-1': '#8ff0a4',\n '--gnome-green-2': '#57e389',\n '--gnome-green-3': '#33d17a',\n '--gnome-green-4': '#2ec27e',\n '--gnome-green-5': '#26a269',\n '--gnome-headerbar-bg-color': '#303030',\n '--gnome-headerbar-border-color': 'rgb(255 255 255 / 0.3)',\n '--gnome-headerbar-fg-color': '#fff',\n '--gnome-headerbar-shade-color': 'rgb(0 0 0 / 0.36)',\n '--gnome-hover-overlay': 'rgb(255 255 255 / 0.12)',\n '--gnome-layout-breakpoint-medium': '550px',\n '--gnome-layout-breakpoint-narrow': '400px',\n '--gnome-layout-breakpoint-wide': '860px',\n '--gnome-layout-content-padding': '24px',\n '--gnome-layout-content-padding-compact': '12px',\n '--gnome-layout-content-padding-spacious': '36px',\n '--gnome-layout-sidebar-default-width': '240px',\n '--gnome-layout-sidebar-max-width': '280px',\n '--gnome-layout-sidebar-min-width': '180px',\n '--gnome-layout-sidebar-overlay-width': 'clamp( 180px, 75%, 280px )',\n '--gnome-layout-sidebar-rail-width': '56px',\n '--gnome-layout-sidebar-width': 'clamp( 180px, 25%, 280px )',\n '--gnome-light-1': '#fff',\n '--gnome-light-2': '#f6f5f4',\n '--gnome-light-3': '#deddda',\n '--gnome-light-4': '#c0bfbc',\n '--gnome-light-5': '#9a9996',\n '--gnome-line-height-body': '1.5',\n '--gnome-line-height-heading': '1.2',\n '--gnome-line-height-title': '1.2',\n '--gnome-new-bg-color': '#c061cb',\n '--gnome-new-color': '#dc8add',\n '--gnome-new-fg-color': '#fff',\n '--gnome-opacity-border': '0.4',\n '--gnome-opacity-dim': '0.7',\n '--gnome-opacity-disabled': '0.6',\n '--gnome-orange-1': '#ffbe6f',\n '--gnome-orange-2': '#ffa348',\n '--gnome-orange-3': '#ff7800',\n '--gnome-orange-4': '#e66100',\n '--gnome-orange-5': '#c64600',\n '--gnome-popover-bg-color': '#383838',\n '--gnome-popover-fg-color': '#fff',\n '--gnome-purple-1': '#dc8add',\n '--gnome-purple-2': '#c061cb',\n '--gnome-purple-3': '#9141ac',\n '--gnome-purple-4': '#813d9c',\n '--gnome-purple-5': '#613583',\n '--gnome-radius-lg': '12px',\n '--gnome-radius-md': '8px',\n '--gnome-radius-pill': '9999px',\n '--gnome-radius-sm': '4px',\n '--gnome-radius-xl': '15px',\n '--gnome-red-1': '#f66151',\n '--gnome-red-2': '#ed333b',\n '--gnome-red-3': '#e01b24',\n '--gnome-red-4': '#c01c28',\n '--gnome-red-5': '#a51d2d',\n '--gnome-shadow-lg':\n '0 3px 9px 1px rgb(0 0 0 / 0.1), 0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(28 31 31 / 0.9)',\n '--gnome-shadow-md': '0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(0 0 0 / 0.08)',\n '--gnome-shadow-sm': '0 1px 2px rgb(0 0 0 / 0.12)',\n '--gnome-sidebar-bg-color': '#303030',\n '--gnome-sidebar-fg-color': '#fff',\n '--gnome-skeleton-base-color': 'rgb(255 255 255 / 0.12)',\n '--gnome-skeleton-highlight-color': 'rgb(255 255 255 / 0.18)',\n '--gnome-space-1': '6px',\n '--gnome-space-2': '12px',\n '--gnome-space-3': '18px',\n '--gnome-space-4': '24px',\n '--gnome-space-5': '36px',\n '--gnome-space-6': '48px',\n '--gnome-success-bg-color': '#57e389',\n '--gnome-success-color': '#8ff0a4',\n '--gnome-success-fg-color': '#fff',\n '--gnome-view-bg-color': '#1e1e1e',\n '--gnome-view-fg-color': '#fff',\n '--gnome-warning-bg-color': '#f8e45c',\n '--gnome-warning-color': '#f9f06b',\n '--gnome-warning-fg-color': 'rgb(0 0 0 / 0.9)',\n '--gnome-window-bg-color': '#242424',\n '--gnome-window-fg-color': '#fff',\n '--gnome-yellow-1': '#f9f06b',\n '--gnome-yellow-2': '#f8e45c',\n '--gnome-yellow-3': '#f6d32d',\n '--gnome-yellow-4': '#f5c211',\n '--gnome-yellow-5': '#e5a50a',\n} as const;\n","import { createContext, useContext, useMemo } from 'react';\nimport type { GnomeResolvedColorScheme, GnomeResolvedContrast } from '@/theme/resolveTheme';\nimport type { GnomeThemeTokens } from '@/theme/tokens.generated';\nimport { lightTheme } from '@/theme/tokens.generated';\nimport type { GnomeAccentColor, GnomeColorScheme, GnomeContrast, GnomeDir } from './resolveContext';\n\nexport interface GnomeContextValue {\n locale: string | undefined;\n dir: GnomeDir;\n numberFormat: Intl.NumberFormatOptions | undefined;\n dateTimeFormat: Intl.DateTimeFormatOptions | undefined;\n colorScheme: GnomeColorScheme;\n resolvedColorScheme: GnomeResolvedColorScheme;\n contrast: GnomeContrast;\n resolvedContrast: GnomeResolvedContrast;\n accentColor: GnomeAccentColor;\n theme: GnomeThemeTokens;\n}\n\nexport const GnomeContext = createContext<GnomeContextValue>({\n locale: undefined,\n dir: 'ltr',\n numberFormat: undefined,\n dateTimeFormat: undefined,\n colorScheme: 'system',\n resolvedColorScheme: 'light',\n contrast: 'system',\n resolvedContrast: 'normal',\n accentColor: 'blue',\n theme: lightTheme,\n});\n\n/** Returns the locale set by the nearest `GnomeProvider`, or `undefined` to use the device locale. */\nexport function useLocale(): string | undefined {\n return useContext(GnomeContext).locale;\n}\n\n/** Returns the text direction set by the nearest `GnomeProvider`. Defaults to the app's `I18nManager.isRTL` state. */\nexport function useDir(): GnomeDir {\n return useContext(GnomeContext).dir;\n}\n\n/** Returns an `Intl.NumberFormat` configured from `GnomeProvider` defaults plus local overrides. */\nexport function useNumberFormatter(options?: Intl.NumberFormatOptions): Intl.NumberFormat {\n const { locale, numberFormat } = useContext(GnomeContext);\n\n return useMemo(\n () => new Intl.NumberFormat(locale, { ...numberFormat, ...options }),\n [locale, numberFormat, options],\n );\n}\n\n/** Returns an `Intl.DateTimeFormat` configured from `GnomeProvider` defaults plus local overrides. */\nexport function useDateTimeFormatter(options?: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n const { locale, dateTimeFormat } = useContext(GnomeContext);\n\n return useMemo(\n () => new Intl.DateTimeFormat(locale, { ...dateTimeFormat, ...options }),\n [locale, dateTimeFormat, options],\n );\n}\n\n/** Returns the color scheme preference set by the nearest `GnomeProvider`. Defaults to `\"system\"`. */\nexport function useColorScheme(): GnomeColorScheme {\n return useContext(GnomeContext).colorScheme;\n}\n\n/** Returns the resolved color scheme (`\"light\"` or `\"dark\"`), accounting for the OS preference when `colorScheme` is `\"system\"`. */\nexport function useResolvedColorScheme(): GnomeResolvedColorScheme {\n return useContext(GnomeContext).resolvedColorScheme;\n}\n\n/** Returns the contrast preference set by the nearest `GnomeProvider`. Defaults to `\"system\"`. */\nexport function useContrast(): GnomeContrast {\n return useContext(GnomeContext).contrast;\n}\n\n/** Returns the resolved contrast (`\"normal\"` or `\"more\"`), accounting for the OS accessibility setting when `contrast` is `\"system\"`. */\nexport function useResolvedContrast(): GnomeResolvedContrast {\n return useContext(GnomeContext).resolvedContrast;\n}\n\n/** Returns the accent color set by the nearest `GnomeProvider`. Defaults to `\"blue\"`. */\nexport function useAccentColor(): GnomeAccentColor {\n return useContext(GnomeContext).accentColor;\n}\n\n/** Returns the fully-resolved theme tokens (colors, spacing, typography, …) for the nearest `GnomeProvider`, accent-adjusted. */\nexport function useGnomeTheme(): GnomeThemeTokens {\n return useContext(GnomeContext).theme;\n}\n","import type { GnomeThemeTokens } from './tokens.generated';\nimport {\n darkTheme,\n highContrastDarkTheme,\n highContrastTheme,\n lightTheme,\n} from './tokens.generated';\n\nexport type GnomeResolvedColorScheme = 'light' | 'dark';\nexport type GnomeResolvedContrast = 'normal' | 'more';\n\nexport interface ResolveGnomeThemeOptions {\n colorScheme: GnomeResolvedColorScheme;\n /** @default 'normal' */\n contrast?: GnomeResolvedContrast;\n}\n\nexport function resolveGnomeTheme({\n colorScheme,\n contrast = 'normal',\n}: ResolveGnomeThemeOptions): GnomeThemeTokens {\n if (contrast === 'more') {\n return colorScheme === 'dark' ? highContrastDarkTheme : highContrastTheme;\n }\n\n return colorScheme === 'dark' ? darkTheme : lightTheme;\n}\n","import type { GnomeResolvedColorScheme, GnomeResolvedContrast } from '@/theme/resolveTheme';\nimport type { GnomeThemeTokens } from '@/theme/tokens.generated';\n\nexport type GnomeColorScheme = GnomeResolvedColorScheme | 'system';\nexport type GnomeContrast = GnomeResolvedContrast | 'system';\nexport type GnomeDir = 'ltr' | 'rtl';\nexport type GnomeNamedAccentColor =\n | 'blue'\n | 'green'\n | 'yellow'\n | 'orange'\n | 'red'\n | 'purple'\n | 'brown';\nexport type GnomeAccentColor = GnomeNamedAccentColor | (string & {});\n\nexport const NAMED_ACCENT_COLORS = new Set<GnomeNamedAccentColor>([\n 'blue',\n 'green',\n 'yellow',\n 'orange',\n 'red',\n 'purple',\n 'brown',\n]);\n\n/**\n * Resolves a `'light' | 'dark' | 'system'` preference against the OS color\n * scheme. `systemColorScheme` is typed loosely (rather than as\n * `GnomeResolvedColorScheme`) so RN's `useColorScheme()` — which can also\n * report `'unspecified'` on Android, besides `null`/`undefined` — can be\n * passed straight through; anything other than `'dark'` resolves to light.\n */\nexport function resolveColorScheme(\n preference: GnomeColorScheme,\n systemColorScheme: string | null | undefined,\n): GnomeResolvedColorScheme {\n if (preference !== 'system') {\n return preference;\n }\n\n return systemColorScheme === 'dark' ? 'dark' : 'light';\n}\n\n/** Resolves a `'normal' | 'more' | 'system'` preference against the OS high-contrast setting. */\nexport function resolveContrast(\n preference: GnomeContrast,\n systemHighContrast: boolean,\n): GnomeResolvedContrast {\n if (preference !== 'system') {\n return preference;\n }\n\n return systemHighContrast ? 'more' : 'normal';\n}\n\n/**\n * Returns `theme` with `accentColor`/`accentBgColor` (and, outside\n * high-contrast, `focusRingColor`) overridden for a non-default accent.\n *\n * Named palette colors (`\"green\"`, `\"red\"`, …) resolve to the matching\n * Adwaita shade for the current color scheme, mirroring\n * `@gnome-ui/react`'s `GnomeProvider`. High-contrast variants keep their\n * own fixed, maximum-contrast focus ring regardless of accent — the same\n * behavior `tokens.css` encodes by not deriving it from `--gnome-accent-color`\n * under `prefers-contrast: more`.\n */\nexport function applyAccentColor(\n theme: GnomeThemeTokens,\n accentColor: GnomeAccentColor,\n resolvedColorScheme: GnomeResolvedColorScheme,\n resolvedContrast: GnomeResolvedContrast,\n): GnomeThemeTokens {\n if (accentColor === 'blue') {\n return theme;\n }\n\n const isNamed = NAMED_ACCENT_COLORS.has(accentColor as GnomeNamedAccentColor);\n const shade = resolvedColorScheme === 'dark' ? 2 : 3;\n const accent = isNamed\n ? (theme[`${accentColor}${shade}` as keyof GnomeThemeTokens] as string)\n : accentColor;\n const accentBg = isNamed\n ? (theme[`${accentColor}3` as keyof GnomeThemeTokens] as string)\n : accentColor;\n\n return {\n ...theme,\n accentColor: accent,\n accentBgColor: accentBg,\n ...(resolvedContrast === 'normal' ? { focusRingColor: accent } : {}),\n };\n}\n","import type { ReactNode } from 'react';\nimport { useEffect, useMemo, useState } from 'react';\nimport {\n AccessibilityInfo,\n I18nManager,\n Platform,\n useColorScheme as useSystemColorScheme,\n} from 'react-native';\nimport { resolveGnomeTheme } from '@/theme/resolveTheme';\nimport { GnomeContext } from './GnomeContext';\nimport type { GnomeAccentColor, GnomeColorScheme, GnomeContrast, GnomeDir } from './resolveContext';\nimport { applyAccentColor, resolveColorScheme, resolveContrast } from './resolveContext';\n\nexport interface GnomeProviderProps {\n /** BCP 47 locale tag (e.g. `\"es-ES\"`, `\"de-DE\"`). When omitted, components fall back to the device locale. */\n locale?: string;\n /**\n * Text direction exposed via context for consumers to branch on (e.g.\n * mirroring icons or flipping a row's layout). Defaults to the app's\n * actual `I18nManager.isRTL` state.\n *\n * Unlike the web `GnomeProvider`, this does **not** call\n * `I18nManager.forceRTL()` — RN's layout direction is a single global\n * flag that requires an app reload to take effect and is normally set\n * once at app bootstrap, not scoped per provider tree.\n */\n dir?: GnomeDir;\n /**\n * Global defaults for number formatting.\n *\n * Use `{ notation: \"compact\", compactDisplay: \"short\" }` for compact\n * values such as `1K`; omit it or set `{ notation: \"standard\" }` for\n * full values such as `1,000`.\n */\n numberFormat?: Intl.NumberFormatOptions;\n /** Global defaults for date/time formatting. */\n dateTimeFormat?: Intl.DateTimeFormatOptions;\n /**\n * Color scheme preference. `\"system\"` (default) follows\n * `Appearance`/`useColorScheme()`. `\"light\"`/`\"dark\"` force that scheme\n * regardless of the OS setting.\n * @default 'system'\n */\n colorScheme?: GnomeColorScheme;\n /**\n * High-contrast preference. `\"system\"` (default) follows the OS\n * accessibility setting where one exists — Android's \"High text\n * contrast\" (`AccessibilityInfo.isHighTextContrastEnabled`) or iOS's\n * \"Increase Contrast\" (`AccessibilityInfo.isDarkerSystemColorsEnabled`)\n * — and falls back to `\"normal\"` where neither is available (e.g. web).\n * @default 'system'\n */\n contrast?: GnomeContrast;\n /**\n * Accent color. Accepts a named GNOME palette color (`\"blue\"`, `\"green\"`,\n * `\"yellow\"`, `\"orange\"`, `\"red\"`, `\"purple\"`, `\"brown\"`) or any RN color\n * string (e.g. `\"#ff0000\"`, `\"rgba(0, 0, 0, 0.5)\"`). Named colors are\n * theme-aware and use the correct shade for light/dark mode\n * automatically. Defaults to `\"blue\"`.\n */\n accentColor?: GnomeAccentColor;\n children: ReactNode;\n}\n\n/**\n * Subscribes to the OS high-contrast accessibility setting.\n *\n * Android exposes \"High text contrast\"; iOS exposes \"Increase Contrast\"\n * (surfaced through `AccessibilityInfo`'s \"darker system colors\" API,\n * which is what that setting maps to natively). Neither exists on other\n * platforms, so this resolves to `false` there.\n */\nfunction useSystemHighContrast(): boolean {\n const [enabled, setEnabled] = useState(false);\n\n useEffect(() => {\n const query =\n Platform.OS === 'android'\n ? AccessibilityInfo.isHighTextContrastEnabled\n : Platform.OS === 'ios'\n ? AccessibilityInfo.isDarkerSystemColorsEnabled\n : undefined;\n\n if (!query) {\n return;\n }\n\n let mounted = true;\n\n (async () => {\n const value = await query();\n\n if (mounted) {\n setEnabled(value);\n }\n })();\n\n const eventName =\n Platform.OS === 'android' ? 'highTextContrastChanged' : 'darkerSystemColorsChanged';\n const subscription = AccessibilityInfo.addEventListener(eventName, setEnabled);\n\n return () => {\n mounted = false;\n subscription.remove();\n };\n }, []);\n\n return enabled;\n}\n\n/**\n * Returns `true` when the OS \"Reduce Motion\" accessibility setting is on\n * (iOS Settings > Accessibility > Motion, Android Settings >\n * Accessibility > Remove animations), for components with a continuously\n * looping `Animated` value (e.g. `Spinner`) to slow down or skip that\n * animation. Unlike `contrast`/`colorScheme`, the web `GnomeProvider` has\n * no corresponding override prop — `prefers-reduced-motion` is a pure CSS\n * media query there, always OS-driven — so this hook mirrors that: it\n * reads `AccessibilityInfo` directly and isn't scoped to `GnomeProvider`'s\n * context, unlike every other hook in this package. `isReduceMotionEnabled`\n * / `reduceMotionChanged` are supported on both iOS and Android (unlike\n * the high-contrast APIs above, which are platform-specific), so no\n * `Platform.OS` branch is needed.\n */\n// eslint-disable-next-line react-refresh/only-export-components\nexport function useReducedMotion(): boolean {\n const [enabled, setEnabled] = useState(false);\n\n useEffect(() => {\n let mounted = true;\n\n (async () => {\n const value = await AccessibilityInfo.isReduceMotionEnabled();\n\n if (mounted) {\n setEnabled(value);\n }\n })();\n\n const subscription = AccessibilityInfo.addEventListener('reduceMotionChanged', setEnabled);\n\n return () => {\n mounted = false;\n subscription.remove();\n };\n }, []);\n\n return enabled;\n}\n\n/** Provides locale, text direction, color scheme, contrast, accent color, and resolved theme tokens to all descendant gnome-ui components. */\nexport const GnomeProvider = ({\n locale,\n dir,\n numberFormat,\n dateTimeFormat,\n colorScheme = 'system',\n contrast = 'system',\n accentColor = 'blue',\n children,\n}: GnomeProviderProps) => {\n const systemColorScheme = useSystemColorScheme();\n const systemHighContrast = useSystemHighContrast();\n\n const resolvedColorScheme = resolveColorScheme(colorScheme, systemColorScheme);\n const resolvedContrast = resolveContrast(contrast, systemHighContrast);\n const resolvedDir = dir ?? (I18nManager.isRTL ? 'rtl' : 'ltr');\n\n const value = useMemo(() => {\n const variantTheme = resolveGnomeTheme({\n colorScheme: resolvedColorScheme,\n contrast: resolvedContrast,\n });\n const theme = applyAccentColor(\n variantTheme,\n accentColor,\n resolvedColorScheme,\n resolvedContrast,\n );\n\n return {\n locale,\n dir: resolvedDir,\n numberFormat,\n dateTimeFormat,\n colorScheme,\n resolvedColorScheme,\n contrast,\n resolvedContrast,\n accentColor,\n theme,\n };\n }, [\n locale,\n resolvedDir,\n numberFormat,\n dateTimeFormat,\n colorScheme,\n resolvedColorScheme,\n contrast,\n resolvedContrast,\n accentColor,\n ]);\n\n return <GnomeContext.Provider value={value}>{children}</GnomeContext.Provider>;\n};\n","import { forwardRef } from 'react';\nimport type { AccessibilityRole, TextProps as RNTextProps, TextStyle } from 'react-native';\nimport { Text as RNText } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type TextVariant =\n | 'large-title'\n | 'title-1'\n | 'title-2'\n | 'title-3'\n | 'title-4'\n | 'heading'\n | 'body'\n | 'document'\n | 'caption'\n | 'caption-heading'\n | 'monospace'\n | 'numeric';\n\nexport type TextColor =\n | 'default'\n | 'dim'\n | 'accent'\n | 'destructive'\n | 'success'\n | 'warning'\n | 'error';\n\n/**\n * Variants the web component renders as `<h1>`–`<h4>`. RN has no heading\n * elements, so they default to the `header` accessibility role instead —\n * the closest native equivalent, and what VoiceOver/TalkBack use to build\n * their heading rotor.\n */\nconst HEADING_VARIANTS: ReadonlySet<TextVariant> = new Set<TextVariant>([\n 'large-title',\n 'title-1',\n 'title-2',\n 'title-3',\n 'title-4',\n 'heading',\n]);\n\nexport interface TextProps extends RNTextProps {\n /** Typography style. Mirrors Adwaita / GNOME HIG text styles. */\n variant?: TextVariant;\n /** Semantic color. */\n color?: TextColor;\n children?: RNTextProps['children'];\n}\n\n/**\n * CSS `line-height` ratios and `letter-spacing` `em` values are relative;\n * RN wants absolute dp for both, so they're resolved against the variant's\n * own font size here.\n */\nfunction getVariantStyle(theme: GnomeThemeTokens, variant: TextVariant): TextStyle {\n const weight = (value: number) => String(value) as TextStyle['fontWeight'];\n\n switch (variant) {\n case 'large-title':\n return {\n fontSize: theme.fontSizeLargeTitle,\n fontWeight: weight(300),\n lineHeight: Math.round(theme.fontSizeLargeTitle * theme.lineHeightHeading),\n letterSpacing: theme.fontSizeLargeTitle * -0.02,\n };\n case 'title-1':\n return {\n fontSize: theme.fontSizeTitle1,\n fontWeight: weight(theme.fontWeightBold),\n lineHeight: Math.round(theme.fontSizeTitle1 * theme.lineHeightHeading),\n };\n case 'title-2':\n return {\n fontSize: theme.fontSizeTitle2,\n fontWeight: weight(theme.fontWeightBold),\n lineHeight: Math.round(theme.fontSizeTitle2 * theme.lineHeightHeading),\n };\n case 'title-3':\n return {\n fontSize: theme.fontSizeTitle3,\n fontWeight: weight(theme.fontWeightBold),\n lineHeight: Math.round(theme.fontSizeTitle3 * theme.lineHeightHeading),\n };\n case 'title-4':\n return {\n fontSize: theme.fontSizeTitle4,\n fontWeight: weight(theme.fontWeightSemibold),\n lineHeight: Math.round(theme.fontSizeTitle4 * theme.lineHeightHeading),\n };\n case 'heading':\n return {\n fontSize: theme.fontSizeBody,\n fontWeight: weight(theme.fontWeightBold),\n lineHeight: Math.round(theme.fontSizeBody * theme.lineHeightHeading),\n };\n case 'document':\n return {\n fontSize: theme.fontSizeBody,\n fontWeight: weight(theme.fontWeightNormal),\n lineHeight: Math.round(theme.fontSizeBody * 1.65),\n };\n case 'caption':\n return {\n fontSize: theme.fontSizeCaption,\n fontWeight: weight(theme.fontWeightNormal),\n lineHeight: Math.round(theme.fontSizeCaption * theme.lineHeightBody),\n };\n case 'caption-heading':\n return {\n fontSize: theme.fontSizeCaption,\n fontWeight: weight(theme.fontWeightSemibold),\n lineHeight: Math.round(theme.fontSizeCaption * theme.lineHeightHeading),\n letterSpacing: theme.fontSizeCaption * 0.06,\n textTransform: 'uppercase',\n };\n case 'monospace':\n return {\n fontFamily: theme.fontFamilyMono,\n fontSize: 14,\n lineHeight: Math.round(14 * theme.lineHeightBody),\n };\n case 'numeric':\n return {\n fontSize: theme.fontSizeBody,\n fontWeight: weight(theme.fontWeightNormal),\n lineHeight: Math.round(theme.fontSizeBody * theme.lineHeightBody),\n fontVariant: ['tabular-nums'],\n };\n default:\n return {\n fontSize: theme.fontSizeBody,\n fontWeight: weight(theme.fontWeightNormal),\n lineHeight: Math.round(theme.fontSizeBody * theme.lineHeightBody),\n };\n }\n}\n\n/**\n * `dim` is an opacity in the web stylesheet rather than a color, so it stays\n * an opacity here — that keeps it correct against any background, the same\n * way `.color-dim` does.\n */\nfunction getColorStyle(theme: GnomeThemeTokens, color: TextColor): TextStyle {\n switch (color) {\n case 'dim':\n return { color: theme.windowFgColor, opacity: theme.opacityDim };\n case 'accent':\n return { color: theme.accentColor };\n case 'destructive':\n return { color: theme.destructiveColor };\n case 'success':\n return { color: theme.successColor };\n case 'warning':\n return { color: theme.warningColor };\n case 'error':\n return { color: theme.errorColor };\n default:\n return { color: theme.windowFgColor };\n }\n}\n\n/**\n * Text component following GNOME Human Interface Guidelines typography styles.\n *\n * Variants map directly to Adwaita style classes:\n * `large-title`, `title-1`–`title-4`, `heading`, `body`, `document`,\n * `caption`, `caption-heading`, `monospace`, `numeric`.\n *\n * Rebuilt on RN's `Text` rather than ported from `@gnome-ui/react`'s DOM-based\n * JSX: there is no element to pick, so `@gnome-ui/react`'s `as` prop is\n * replaced by `accessibilityRole`, which the heading variants default to\n * `\"header\"`.\n *\n * @see https://developer.gnome.org/hig/guidelines/typography.html\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/style-classes.html\n */\nexport const Text = forwardRef<RNText, TextProps>(function Text(\n { variant = 'body', color = 'default', style, children, accessibilityRole, ...textProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const role: AccessibilityRole | undefined =\n accessibilityRole ?? (HEADING_VARIANTS.has(variant) ? 'header' : undefined);\n\n return (\n <RNText\n ref={ref}\n accessibilityRole={role}\n style={[\n { fontFamily: theme.fontFamily },\n getVariantStyle(theme, variant),\n getColorStyle(theme, color),\n style,\n ]}\n {...textProps}\n >\n {children}\n </RNText>\n );\n});\n","import { forwardRef, type ReactNode } from 'react';\nimport type { PressableProps, StyleProp, View, ViewStyle } from 'react-native';\nimport { Pressable, View as RNView, StyleSheet } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type ActionRowVariant = 'default' | 'property';\n\nexport interface ActionRowProps extends Omit<PressableProps, 'children' | 'style'> {\n /** Primary label. */\n title: string;\n /** Secondary line below the title. */\n subtitle?: string;\n /** Icon or image placed at the leading edge. Rendered as-is — size/color it yourself. */\n leading?: ReactNode;\n /**\n * Widget placed at the trailing edge (`Switch`, `Button`, `Text`…).\n * For interactive end widgets prefer a controlled component and stop\n * event propagation inside it so the row's own `onPress` isn't triggered.\n */\n trailing?: ReactNode;\n /**\n * When true the entire row becomes pressable (renders as `Pressable`\n * with `accessibilityRole=\"button\"`). Use for rows that navigate or\n * trigger an action.\n */\n interactive?: boolean;\n /**\n * `\"property\"` flips the visual hierarchy: the subtitle becomes the\n * prominent value and the title shrinks to a dim label above it.\n * Use for read-only property display (e.g. \"OS Version / GNOME 50\").\n * Mirrors the `.property` style class.\n */\n variant?: ActionRowVariant;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Standard settings row with title, optional subtitle, and end widget.\n *\n * Mirrors the Adwaita `AdwActionRow` pattern — the fundamental building\n * block inside a `BoxedList`.\n *\n * Rebuilt with `Pressable`/`View`/`Text` rather than ported from\n * `@gnome-ui/react`'s `<button>`/`<div>` — same `interactive` split as\n * `Card`: a pressed-state overlay tinted by `theme.activeOverlay` stands in\n * for the web's `:hover`/`:active` `background-color` transitions, since RN\n * has no hover state on touch devices and no `color-mix()`. `title`/\n * `subtitle` reuse the library's own `Text` component (`variant=\"body\"`/\n * `\"caption\"`, `color=\"dim\"`) instead of hand-rolled styles, with\n * `numberOfLines={1}` standing in for the web's CSS\n * `text-overflow: ellipsis` truncation.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ActionRow.html\n * @see https://developer.gnome.org/hig/patterns/containers.html\n */\nexport const ActionRow = forwardRef<View, ActionRowProps>(function ActionRow(\n {\n title,\n subtitle,\n leading,\n trailing,\n interactive = false,\n variant = 'default',\n style,\n disabled,\n ...pressableProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const property = variant === 'property';\n\n const baseStyle: ViewStyle = {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: theme.space2,\n paddingHorizontal: theme.space4,\n minHeight: 52,\n width: '100%',\n };\n\n const content = (\n <>\n {leading && <RNView style={{ flexShrink: 0 }}>{leading}</RNView>}\n\n <RNView style={{ flex: 1, gap: 2 }}>\n <Text\n variant={property ? 'caption' : 'body'}\n color={property ? 'dim' : 'default'}\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n >\n {title}\n </Text>\n {subtitle && (\n <Text\n variant={property ? 'body' : 'caption'}\n color={property ? 'default' : 'dim'}\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n >\n {subtitle}\n </Text>\n )}\n </RNView>\n\n {trailing && (\n <RNView\n style={{ flexShrink: 0, flexDirection: 'row', alignItems: 'center', gap: theme.space1 }}\n >\n {trailing}\n </RNView>\n )}\n </>\n );\n\n if (!interactive) {\n return (\n <RNView ref={ref} style={[baseStyle, style]} {...pressableProps}>\n {content}\n </RNView>\n );\n }\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled: !!disabled }}\n style={[\n baseStyle,\n { position: 'relative', opacity: disabled ? theme.opacityDisabled : 1 },\n style,\n ]}\n {...pressableProps}\n >\n {({ pressed }) => (\n <>\n {content}\n {pressed && !disabled ? (\n <RNView\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { backgroundColor: theme.activeOverlay }]}\n />\n ) : null}\n </>\n )}\n </Pressable>\n );\n});\n","import type { useGnomeTheme } from '@/GnomeProvider';\nimport type { IconColor, IconSize } from './Icon';\n\n/**\n * Non-component helpers shared between `Icon` and `AnimatedIcon` — kept in\n * their own module (not exported from `Icon.tsx` itself) so both files stay\n * component-only for Fast Refresh, per the project's `react-refresh/\n * only-export-components` lint rule.\n */\n\nexport const ICON_SIZE_MAP: Record<IconSize, number> = { sm: 12, md: 16, lg: 20 };\n\nexport function resolveIconColor(\n theme: ReturnType<typeof useGnomeTheme>,\n color: IconColor | undefined,\n) {\n switch (color) {\n case 'blue':\n return theme.blue3;\n case 'green':\n return theme.green4;\n case 'yellow':\n return theme.yellow4;\n case 'orange':\n return theme.orange3;\n case 'red':\n return theme.red3;\n case 'purple':\n return theme.purple3;\n case 'brown':\n return theme.brown3;\n default:\n return theme.windowFgColor;\n }\n}\n\nexport function iconAccessibilityProps(label: string | undefined) {\n return label\n ? { accessible: true as const, accessibilityLabel: label, accessibilityRole: 'image' as const }\n : { accessible: false as const, accessibilityRole: 'none' as const };\n}\n","import type { AnyIconDefinition, IconDefinition } from '@gnome-ui/icons';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Path, Svg, SvgXml } from 'react-native-svg';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nimport { ICON_SIZE_MAP, iconAccessibilityProps, resolveIconColor } from './shared';\n\nexport type IconSize = 'sm' | 'md' | 'lg';\n\n/** Named GNOME palette color for the icon. `\"default\"` (or omitting the prop) resolves to the theme's default foreground color. */\nexport type IconColor =\n | 'default'\n | 'blue'\n | 'green'\n | 'yellow'\n | 'orange'\n | 'red'\n | 'purple'\n | 'brown';\n\nexport interface IconProps {\n /** Icon from `@gnome-ui/icons`, a `simple-icons` icon, or a raw `{ path }` object. */\n icon: AnyIconDefinition;\n /**\n * Rendered size.\n * - `sm` — 12 px\n * - `md` — 16 px (default)\n * - `lg` — 20 px\n *\n * Override with `width`/`height` for non-standard sizes.\n */\n size?: IconSize;\n width?: number;\n height?: number;\n /** Accessible label. Omit for decorative icons — they are hidden from screen readers. */\n label?: string;\n /**\n * Named GNOME palette color. Omit (or pass `\"default\"`) to use the\n * theme's default foreground color — RN has no `currentColor`\n * equivalent to inherit from a parent, unlike the web version.\n */\n color?: IconColor;\n /**\n * Arbitrary fill color, overriding `color`. Escape hatch for the rare\n * case where a fixed named-palette swatch (e.g. `color=\"blue\"` →\n * `theme.blue3`) isn't good enough — most concretely, tinting an icon\n * with the app's actual *configurable* accent (`theme.accentColor`),\n * which `blue3` never tracks. Added for `BottomTabBar`'s active-tab\n * icon, the same precedent as `style` being added specifically for\n * `Dropdown`'s chevron rotation need.\n */\n tintColor?: string;\n /** Forwarded to the underlying `Svg` — useful for a `transform` (e.g. a rotated disclosure chevron) or `margin`. */\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * `icon.paths` present → `IconDefinition` with structured path data.\n * `icon.svg` present (and no `paths`) → `IconDefinition` carrying raw markup\n * (the `animated` icons — see `AnimatedIcon`).\n * Neither → `RawPathIconDefinition` (e.g. a `simple-icons` `SimpleIcon`,\n * which has its own unrelated `svg` field alongside `path` — checking\n * `path` is absent, not just that `svg` is a string, is what keeps those\n * correctly classified below instead of misread as an internal\n * `IconDefinition`). Duplicated from `@gnome-ui/react`'s `Icon.tsx` rather\n * than shared — see that component for the full reasoning.\n */\nfunction isIconDefinition(icon: AnyIconDefinition): icon is IconDefinition {\n if ('paths' in icon && Array.isArray((icon as IconDefinition).paths)) {\n return true;\n }\n\n return typeof (icon as IconDefinition).svg === 'string' && !('path' in icon);\n}\n\n/** `react-native-svg`'s `FillRule` has no `\"inherit\"` member (unlike `IconPath`'s, kept for CSS-source-of-truth completeness) — a leaf `<path>` has nothing to inherit from, so it maps to the SVG default (omitted attribute). */\nfunction fillRule(rule: 'nonzero' | 'evenodd' | 'inherit' | undefined) {\n return rule === 'nonzero' || rule === 'evenodd' ? rule : undefined;\n}\n\n/**\n * Renders an icon as an inline SVG, via `react-native-svg`.\n *\n * Accepts icons from `@gnome-ui/icons`, any `simple-icons` icon, or a plain\n * `{ path }` object — same `AnyIconDefinition` union as `@gnome-ui/react`'s\n * `Icon`. Pass `label` only when the icon conveys meaning on its own.\n *\n * `animated` icons (`Syncing`, `Recording`, …) carry raw `svg` markup\n * instead of `paths` — rendered here through `SvgXml`, which parses the\n * structural elements (`<g>`/`<path>`/`<circle>`) but has no CSS engine, so\n * the embedded `<style>`/`@keyframes` block is silently dropped and the\n * shapes render at their authored rest position. That happens to be exactly\n * the desired \"inert, static frame\" behavior for a plain `<Icon>` — no\n * special-casing needed here; wrap in `<AnimatedIcon>` to actually play the\n * motion (RN has to hand-build that with `Animated`, since there is no CSS\n * animation engine to interpret the markup's keyframes).\n *\n * @example\n * import { Search } from \"@gnome-ui/icons\";\n * <Icon icon={Search} label=\"Search\" />\n *\n * @example\n * import { siGithub } from \"simple-icons\";\n * <Icon icon={siGithub} label=\"GitHub\" />\n */\nexport const Icon = ({\n icon,\n size = 'md',\n width,\n height,\n label,\n color,\n tintColor,\n style,\n}: IconProps) => {\n const theme = useGnomeTheme();\n const px = ICON_SIZE_MAP[size];\n const fill = tintColor ?? resolveIconColor(theme, color);\n\n const resolvedViewBox = isIconDefinition(icon) ? icon.viewBox : (icon.viewBox ?? '0 0 24 24');\n const rawSvg = isIconDefinition(icon) ? icon.svg : undefined;\n const paths = isIconDefinition(icon)\n ? icon.svg\n ? undefined\n : icon.paths?.map((p, i) => (\n <Path\n key={i}\n d={p.d}\n fillRule={fillRule(p.fillRule)}\n clipRule={fillRule(p.clipRule)}\n transform={p.transform}\n />\n ))\n : [<Path key={0} d={icon.path} />];\n\n const accessibilityProps = iconAccessibilityProps(label);\n\n if (rawSvg) {\n return (\n <SvgXml\n xml={`<svg viewBox=\"${resolvedViewBox}\">${rawSvg}</svg>`}\n width={width ?? px}\n height={height ?? px}\n fill={fill}\n style={style}\n {...accessibilityProps}\n />\n );\n }\n\n return (\n <Svg\n viewBox={resolvedViewBox}\n width={width ?? px}\n height={height ?? px}\n fill={fill}\n style={style}\n {...accessibilityProps}\n >\n {paths}\n </Svg>\n );\n};\n","import { Connecting, Downloading, type IconDefinition, Recording, Syncing } from '@gnome-ui/icons';\nimport { type ComponentType, useEffect, useRef } from 'react';\nimport { Animated, Easing } from 'react-native';\nimport { Circle, G, Path, Svg } from 'react-native-svg';\nimport type { IconProps } from '@/components/Icon';\nimport { Icon } from '@/components/Icon';\nimport { ICON_SIZE_MAP, iconAccessibilityProps, resolveIconColor } from '@/components/Icon/shared';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport interface AnimatedIconProps extends Omit<IconProps, 'icon'> {\n /** Animated icon from `@gnome-ui/icons` (`animated: true` — `Syncing`, `Recording`, `Downloading`, `Connecting`). */\n icon: IconDefinition;\n /**\n * Whether the animation plays. Defaults to `true`.\n *\n * Regardless of this prop, the animation is always paused when the OS\n * reduced-motion setting is on — callers don't need to check\n * `useReducedMotion()` themselves.\n */\n playing?: boolean;\n}\n\n/**\n * `Animated`'s style/prop interpolation only takes effect on `Animated.*`\n * host components — the same gotcha already documented on `Toast`/`Dialog`,\n * here applied to `react-native-svg`'s shape components instead of `View`/\n * `Pressable`. Hoisted to module scope so they aren't recreated per render.\n */\nconst AnimatedG = Animated.createAnimatedComponent(G);\nconst AnimatedPath = Animated.createAnimatedComponent(Path);\nconst AnimatedCircle = Animated.createAnimatedComponent(Circle);\n\ninterface MotionProps {\n fill: string;\n}\n\n/** Drives a `0 → 1` value on an infinite loop. Used wherever the whole motion can be expressed as one `interpolate()` off a single linear driver. */\nfunction useLoopingProgress(duration: number) {\n const value = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n const animation = Animated.loop(\n Animated.timing(value, {\n toValue: 1,\n duration,\n easing: Easing.linear,\n useNativeDriver: false,\n }),\n );\n\n animation.start();\n\n return () => animation.stop();\n }, [value, duration]);\n\n return value;\n}\n\n/** Same as `useLoopingProgress`, but the repeating loop only starts after an initial one-time `delay` — for staggering identical cycles (`Connecting`'s three dots) out of phase with each other. */\nfunction useDelayedLoopingProgress(duration: number, delay: number) {\n const value = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n const animation = Animated.sequence([\n Animated.delay(delay),\n Animated.loop(\n Animated.timing(value, {\n toValue: 1,\n duration,\n easing: Easing.linear,\n useNativeDriver: false,\n }),\n ),\n ]);\n\n animation.start();\n\n return () => animation.stop();\n }, [value, duration, delay]);\n\n return value;\n}\n\n/**\n * `emblem-synchronizing-symbolic` — rotates a full turn, 1.5s linear, mirroring\n * `@gnome-ui/icons`' `Syncing.svg` `@keyframes gicon-syncing-rotate`.\n * Reuses `Refresh`'s exact path data, same as the source icon.\n */\nconst SyncingMotion = ({ fill }: MotionProps) => {\n const progress = useLoopingProgress(1500);\n const transform = progress.interpolate({\n inputRange: [0, 1],\n outputRange: ['rotate(0, 8, 8)', 'rotate(360, 8, 8)'],\n });\n\n return (\n <AnimatedG transform={transform}>\n <Path\n fill={fill}\n fillRule=\"evenodd\"\n d=\"M8 2a6 6 0 0 0-6 6h2a4 4 0 0 1 4-4 4 4 0 0 1 4 4 4 4 0 0 1-4 4v-2L5 13l3 3v-2a6 6 0 0 0 6-6 6 6 0 0 0-6-6z\"\n />\n </AnimatedG>\n );\n};\n\n/**\n * `media-record-symbolic` — opacity pulse `1 → 0.35 → 1`, 1.2s ease-in-out,\n * mirroring `Recording.svg`'s `@keyframes gicon-recording-pulse`.\n */\nconst RecordingMotion = ({ fill }: MotionProps) => {\n const opacity = useRef(new Animated.Value(1)).current;\n\n useEffect(() => {\n const easing = Easing.inOut(Easing.ease);\n const animation = Animated.loop(\n Animated.sequence([\n Animated.timing(opacity, { toValue: 0.35, duration: 600, easing, useNativeDriver: false }),\n Animated.timing(opacity, { toValue: 1, duration: 600, easing, useNativeDriver: false }),\n ]),\n );\n\n animation.start();\n\n return () => animation.stop();\n }, [opacity]);\n\n return (\n <AnimatedPath\n fill={fill}\n opacity={opacity}\n d=\"m 15 8 c 0 3.867188 -3.132812 7 -7 7 s -7 -3.132812 -7 -7 s 3.132812 -7 7 -7 s 7 3.132812 7 7 z m 0 0\"\n />\n );\n};\n\n/**\n * Animated download indicator — static tray bar, arrow drops into it on a\n * loop (translateY + opacity), 1.4s, mirroring `Downloading.svg`'s\n * `@keyframes gicon-downloading-drop`. The five CSS keyframe stops\n * (`0%, 20%, 50%, 55%, 80%, 100%`) become one `interpolate()`'s\n * `inputRange` off a single linear `0 → 1` driver — good enough fidelity\n * for a decorative micro-interaction without chaining five separately-eased\n * `Animated.timing` segments.\n */\nconst DownloadingMotion = ({ fill }: MotionProps) => {\n const progress = useLoopingProgress(1400);\n const stops = [0, 0.2, 0.5, 0.55, 0.8, 1];\n const transform = progress.interpolate({\n inputRange: stops,\n outputRange: [\n 'translate(0, 0)',\n 'translate(0, 0)',\n 'translate(0, 3)',\n 'translate(0, -3)',\n 'translate(0, 0)',\n 'translate(0, 0)',\n ],\n });\n const opacity = progress.interpolate({ inputRange: stops, outputRange: [1, 1, 0, 0, 1, 1] });\n\n return (\n <>\n <Path fill={fill} d=\"M2 13h12v2H2z\" />\n <AnimatedG transform={transform} opacity={opacity}>\n <Path fill={fill} fillRule=\"evenodd\" d=\"m2.5 5 5.5 6 5.5-6z\" />\n </AnimatedG>\n </>\n );\n};\n\n/** `scale(s)` around an arbitrary `(cx, cy)` pivot, composed as SVG's own translate/scale/translate-back — `react-native-svg`'s dedicated `scale` prop takes a `NumberArray`, not a single animatable number, and its non-deprecated `scaleX`/`scaleY` still can't share one `Animated.Value` with `origin` cleanly, so a plain `transform` string is the most direct fit. */\nfunction scaleAround(cx: number, cy: number, scale: number) {\n return `translate(${cx}, ${cy}) scale(${scale}) translate(${-cx}, ${-cy})`;\n}\n\ninterface ConnectingDotProps {\n cx: number;\n cy: number;\n fill: string;\n delay: number;\n}\n\n/** One of `Connecting`'s three signal dots — opacity + scale pulse, 1.2s ease-in-out, staggered via `delay` so the three read as a left-to-right sweep, mirroring `Connecting.svg`'s `animation-delay: 0/0.15s/0.3s`. */\nconst ConnectingDot = ({ cx, cy, fill, delay }: ConnectingDotProps) => {\n const progress = useDelayedLoopingProgress(1200, delay);\n const stops = [0, 0.2, 0.4, 1];\n const opacity = progress.interpolate({ inputRange: stops, outputRange: [1, 0.3, 1, 1] });\n const transform = progress.interpolate({\n inputRange: stops,\n outputRange: [\n scaleAround(cx, cy, 1),\n scaleAround(cx, cy, 0.7),\n scaleAround(cx, cy, 1),\n scaleAround(cx, cy, 1),\n ],\n });\n\n return (\n <AnimatedCircle cx={cx} cy={cy} r={2} fill={fill} opacity={opacity} transform={transform} />\n );\n};\n\n/**\n * `network-wireless-acquiring-symbolic` — static device/arc path, three\n * signal dots pulsing in a staggered sweep, mirroring `Connecting.svg`.\n */\nconst ConnectingMotion = ({ fill }: MotionProps) => {\n return (\n <>\n <Path\n fill={fill}\n d=\"m 8 1.992188 c -2.617188 0 -5.238281 0.933593 -7.195312 2.808593 l -0.496094 0.480469 c -0.3984378 0.378906 -0.410156 1.011719 -0.03125 1.410156 c 0.003906 0.007813 0.011718 0.011719 0.019531 0.015625 c 0.480469 -1.011719 1.503906 -1.707031 2.703125 -1.707031 h 0.9375 c 2.480469 -1.292969 5.644531 -1.292969 8.125 0 h 0.9375 c 1.199219 0 2.222656 0.695312 2.707031 1.707031 c 0.003907 -0.007812 0.011719 -0.011719 0.015625 -0.015625 c 0.378906 -0.398437 0.367188 -1.03125 -0.03125 -1.410156 l -0.496094 -0.480469 c -1.957031 -1.875 -4.578124 -2.808593 -7.195312 -2.808593 z m -1.730469 9.007812 c -0.441406 0.765625 -0.339843 1.757812 0.316407 2.414062 c 0.78125 0.78125 2.046874 0.78125 2.828124 0 c 0.65625 -0.65625 0.757813 -1.648437 0.316407 -2.414062 z m 0 0\"\n />\n <ConnectingDot cx={3} cy={8} fill={fill} delay={0} />\n <ConnectingDot cx={8} cy={8} fill={fill} delay={150} />\n <ConnectingDot cx={13} cy={8} fill={fill} delay={300} />\n </>\n );\n};\n\n/**\n * Known animated icons, matched by referential identity against\n * `@gnome-ui/icons`' own exports. There is no generic way to interpret an\n * arbitrary icon's `svg`-embedded CSS `@keyframes` in RN (no CSS engine),\n * so each recipe is hand-built — this registry only covers the 4 icons\n * `@gnome-ui/icons` currently ships as `animated: true`. An icon that isn't\n * in it (a future 5th animated icon, or a consumer-authored one) falls back\n * to the static `<Icon>` frame — `playing` silently has no visible effect\n * rather than throwing.\n */\nconst RECIPES = new Map<IconDefinition, ComponentType<MotionProps>>([\n [Syncing, SyncingMotion],\n [Recording, RecordingMotion],\n [Downloading, DownloadingMotion],\n [Connecting, ConnectingMotion],\n]);\n\n/**\n * Plays the motion for a known `animated` icon from `@gnome-ui/icons`\n * (`Syncing`, `Recording`, `Downloading`, `Connecting`) — rendered through\n * plain `<Icon>`, these show a static frame instead.\n *\n * Unlike `@gnome-ui/react`'s `AnimatedIcon` (which plays a CSS animation\n * embedded in the icon's raw `svg` markup via a `--gnome-icon-play-state`\n * custom property), RN has no CSS engine to interpret `@keyframes` at all —\n * `<Icon>` already drops the `<style>` block silently (see its own doc\n * comment), and this component instead hand-builds each of the 4 known\n * icons' exact motion with `Animated`, matched against `RECIPES` by\n * reference. When playing and reduced motion is off, it renders its own\n * `<Svg>` with the matching `*Motion` recipe as children; otherwise (not\n * playing, reduced motion on, or an icon `RECIPES` doesn't recognize) it\n * defers straight to `<Icon>`.\n *\n * Useful for progress, sync, recording, download, and connection states, or\n * cross-fading between icons by swapping `icon` while `playing` stays true.\n *\n * @example\n * import { Syncing } from \"@gnome-ui/icons\";\n * <AnimatedIcon icon={Syncing} playing={isSyncing} label=\"Syncing\" />\n */\nexport const AnimatedIcon = ({\n icon,\n playing = true,\n size = 'md',\n width,\n height,\n label,\n color,\n}: AnimatedIconProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n const isPlaying = playing && !reducedMotion;\n const Motion = isPlaying ? RECIPES.get(icon) : undefined;\n\n if (!Motion) {\n return (\n <Icon icon={icon} size={size} width={width} height={height} label={label} color={color} />\n );\n }\n\n const px = ICON_SIZE_MAP[size];\n const fill = resolveIconColor(theme, color);\n const accessibilityProps = iconAccessibilityProps(label);\n\n return (\n <Svg viewBox={icon.viewBox} width={width ?? px} height={height ?? px} {...accessibilityProps}>\n <Motion fill={fill} />\n </Svg>\n );\n};\n","import type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Image, Text, View } from 'react-native';\n\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type AvatarSize = 'sm' | 'md' | 'lg' | 'xl';\n\n/**\n * Named color palette for the initials fallback.\n * Mirrors libadwaita's avatar color set.\n */\nexport type AvatarColor =\n | 'blue'\n | 'green'\n | 'yellow'\n | 'orange'\n | 'red'\n | 'purple'\n | 'brown'\n | 'teal'\n | 'slate';\n\nexport interface AvatarProps {\n /**\n * Full name used to generate initials and — when `color` is omitted —\n * to deterministically pick a background color.\n */\n name?: string;\n /** Image URL. When provided the initials fallback is hidden. */\n src?: string;\n /** Accessible label. Defaults to `name`, then `\"Avatar\"`. */\n alt?: string;\n /** Size of the avatar. Defaults to `\"md\"`. */\n size?: AvatarSize;\n /**\n * Override the auto-derived background color for the initials fallback.\n * When omitted a color is derived from `name` via a stable hash.\n */\n color?: AvatarColor;\n style?: StyleProp<ViewStyle>;\n}\n\nconst COLOR_LIST: AvatarColor[] = [\n 'blue',\n 'green',\n 'yellow',\n 'orange',\n 'red',\n 'purple',\n 'brown',\n 'teal',\n 'slate',\n];\n\nconst SIZE_BOX: Record<AvatarSize, number> = { sm: 24, md: 32, lg: 48, xl: 64 };\n\n/**\n * Initials font size as a multiple of the theme's base body size — ports the\n * source CSS's `.initials { font-size: 0.42em }` overridden per size\n * (`sm` 0.5em, `md` 0.6em, `lg` 1em, `xl` 1.4em), which is relative to the\n * *inherited* ambient font size (the avatar box itself sets none), not to\n * the avatar's own box size.\n */\nconst SIZE_INITIALS_RATIO: Record<AvatarSize, number> = { sm: 0.5, md: 0.6, lg: 1, xl: 1.4 };\n\n/** Stable, non-cryptographic hash → index into COLOR_LIST. */\nfunction nameToColor(name: string): AvatarColor {\n let hash = 0;\n for (let i = 0; i < name.length; i++) {\n hash = (hash * 31 + name.charCodeAt(i)) >>> 0;\n }\n\n return COLOR_LIST[hash % COLOR_LIST.length];\n}\n\n/** Extract up to 2 initials from a name string. */\nfunction getInitials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n\n if (parts.length === 0) {\n return '';\n }\n\n if (parts.length === 1) {\n return parts[0].charAt(0).toUpperCase();\n }\n\n return parts[0].charAt(0).toUpperCase() + parts[parts.length - 1].charAt(0).toUpperCase();\n}\n\ninterface ColorSwatch {\n backgroundColor: string;\n initialsColor: string;\n}\n\nfunction getColorSwatch(theme: GnomeThemeTokens, color: AvatarColor): ColorSwatch {\n switch (color) {\n case 'blue':\n return { backgroundColor: theme.blue3, initialsColor: '#fff' };\n case 'green':\n return { backgroundColor: theme.green4, initialsColor: '#fff' };\n case 'yellow':\n // Yellow needs dark initials for contrast, independent of color scheme.\n return { backgroundColor: theme.yellow4, initialsColor: 'rgba(0, 0, 0, 0.8)' };\n case 'orange':\n return { backgroundColor: theme.orange4, initialsColor: '#fff' };\n case 'red':\n return { backgroundColor: theme.red3, initialsColor: '#fff' };\n case 'purple':\n return { backgroundColor: theme.purple3, initialsColor: '#fff' };\n case 'brown':\n return { backgroundColor: theme.brown3, initialsColor: '#fff' };\n case 'teal':\n // Not a real @gnome-ui/core token — the web CSS hardcodes this hex too.\n return { backgroundColor: '#2190a4', initialsColor: '#fff' };\n case 'slate':\n return { backgroundColor: theme.dark2, initialsColor: '#fff' };\n }\n}\n\n/**\n * Circular avatar with image or initials fallback.\n *\n * Follows the Adwaita `AdwAvatar` pattern — deterministic color from name,\n * up to two initials when no image is supplied. Rebuilt with `View`/`Image`/\n * `Text` rather than ported from `@gnome-ui/react`'s DOM-based JSX, but\n * mirrors its prop API and color-hash/initials math (pure JS, ported\n * verbatim).\n *\n * The outer container carries `role=\"img\"` + `accessibilityLabel` — RN's\n * newer web-aligned `Role` union has an `\"img\"` value, a direct 1:1 port of\n * the web version's `role=\"img\"`, no substitution needed (same as\n * `ProgressBar`'s `role=\"progressbar\"`). The image/initials underneath are\n * hidden from the accessibility tree\n * (`accessibilityElementsHidden`/`importantForAccessibility=\"no\"`, mirroring\n * the web version's `aria-hidden` on both), so a screen reader gets one\n * stop, not two — same reasoning as `SpinButton`'s hidden −/+ buttons.\n *\n * The web CSS's `box-shadow: inset 0 0 0 1px …` ring (drawn on top, doesn't\n * affect layout) becomes a real 1px `borderWidth`/`borderColor` here (RN has\n * no inset shadow) — the same substitution `Slider`'s thumb border already\n * used for a ring effect, at the cost of a negligible 1px content-box inset\n * the web version doesn't have.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Avatar.html\n */\nexport const Avatar = ({ name = '', src, alt, size = 'md', color, style }: AvatarProps) => {\n const theme = useGnomeTheme();\n const resolvedColorScheme = useResolvedColorScheme();\n\n const resolvedColor = color ?? (name ? nameToColor(name) : 'blue');\n const initials = getInitials(name);\n const box = SIZE_BOX[size];\n const { backgroundColor, initialsColor } = getColorSwatch(theme, resolvedColor);\n const ringColor =\n resolvedColorScheme === 'dark' ? 'rgba(255, 255, 255, 0.08)' : 'rgba(0, 0, 0, 0.08)';\n\n return (\n <View\n accessible\n role=\"img\"\n accessibilityLabel={alt ?? (name || undefined) ?? 'Avatar'}\n style={[\n {\n width: box,\n height: box,\n borderRadius: theme.radiusPill,\n overflow: 'hidden',\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: src ? undefined : backgroundColor,\n borderWidth: 1,\n borderColor: ringColor,\n },\n style,\n ]}\n >\n {src ? (\n <Image\n testID=\"avatar-image\"\n source={{ uri: src }}\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n resizeMode=\"cover\"\n style={{ width: '100%', height: '100%' }}\n />\n ) : (\n <Text\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n fontFamily: theme.fontFamily,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n fontSize: theme.fontSizeBody * SIZE_INITIALS_RATIO[size],\n lineHeight: theme.fontSizeBody * SIZE_INITIALS_RATIO[size],\n color: initialsColor,\n }}\n >\n {initials}\n </Text>\n )}\n </View>\n );\n};\n","import type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Text, View } from 'react-native';\n\nimport { Avatar, type AvatarColor, type AvatarSize } from '@/components/Avatar';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface AvatarGroupItem {\n name?: string;\n src?: string;\n alt?: string;\n color?: AvatarColor;\n}\n\nexport interface AvatarGroupProps {\n avatars: AvatarGroupItem[];\n /** Max visible avatars before showing the overflow chip. Defaults to `5`. */\n max?: number;\n /** Size applied to all avatars and the overflow chip. Defaults to `\"md\"`. */\n size?: AvatarSize;\n /** Accessible label for the group. Auto-generated from names when omitted. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst OVERLAP: Record<AvatarSize, number> = { sm: 6, md: 8, lg: 12, xl: 16 };\nconst CHIP_BOX: Record<AvatarSize, number> = { sm: 24, md: 32, lg: 48, xl: 64 };\nconst CHIP_FONT_SIZE: Record<AvatarSize, number> = { sm: 8, md: 9.6, lg: 14, xl: 17.6 };\n\n/**\n * Overlapping stack of `Avatar`s with a \"+N\" overflow indicator. Mirrors\n * `@gnome-ui/react`'s `AvatarGroup`.\n *\n * The web version's separating ring around each overlapping avatar is two\n * layered `box-shadow`s (an inset 1px dark/light border plus an outset 2px\n * window-colored ring) — RN can only give a `View` one border, so this\n * keeps just the outer window-colored ring (`borderWidth: 2,\n * borderColor: theme.windowBgColor`, overriding `Avatar`'s own subtle 1px\n * ring via its `style` prop), since that's the ring doing the actual\n * \"stay visually distinct from the avatar behind you\" work — the inner\n * hairline is a decorative nicety, not a behavior gap. The overflow chip\n * reuses `Avatar`'s own per-size box dimensions (`sm`/`md`/`lg`/`xl` →\n * 24/32/48/64) so it lines up exactly with the avatars beside it.\n *\n * `role=\"group\"` + `accessibilityLabel` ports 1:1 from RN's newer\n * web-aligned `Role` union (the same `Avatar`/`Badge`/`LevelBar`\n * precedent) — the label is auto-generated from `avatars[].name` (joined,\n * plus \"and N more\" when overflowing) exactly like the web version, unless\n * overridden. The overflow chip's `+N` text is hidden from the\n * accessibility tree (`accessibilityElementsHidden`/\n * `importantForAccessibility=\"no\"`) in favor of the chip's own\n * `accessibilityLabel=\"+N more\"` — the same one-stop-not-two reasoning\n * `SpinButton`/`Avatar` already established for a decorative inner glyph.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Avatar.html\n */\nexport const AvatarGroup = ({\n avatars,\n max = 5,\n size = 'md',\n accessibilityLabel,\n style,\n testID,\n}: AvatarGroupProps) => {\n const theme = useGnomeTheme();\n\n const visible = avatars.length > max ? avatars.slice(0, max) : avatars;\n const overflowCount = avatars.length > max ? avatars.length - max : 0;\n\n const allNames = avatars\n .map((a) => a.name)\n .filter(Boolean)\n .join(', ');\n\n const label =\n accessibilityLabel ??\n (allNames\n ? overflowCount > 0\n ? `${allNames} and ${overflowCount} more`\n : allNames\n : 'Avatar group');\n\n const overlap = OVERLAP[size];\n\n return (\n <View\n testID={testID}\n accessible\n role=\"group\"\n accessibilityLabel={label}\n style={[{ flexDirection: 'row', alignItems: 'center' }, style]}\n >\n {visible.map((item, index) => (\n <Avatar\n key={`${item.name ?? ''}-${item.src ?? ''}-${index}`}\n name={item.name}\n src={item.src}\n alt={item.alt ?? item.name}\n color={item.color}\n size={size}\n style={{\n marginStart: index === 0 ? 0 : -overlap,\n borderWidth: 2,\n borderColor: theme.windowBgColor,\n }}\n />\n ))}\n\n {overflowCount > 0 && (\n <View\n accessible\n accessibilityLabel={`+${overflowCount} more`}\n style={{\n width: CHIP_BOX[size],\n height: CHIP_BOX[size],\n marginStart: -overlap,\n borderRadius: theme.radiusPill,\n borderWidth: 2,\n borderColor: theme.windowBgColor,\n backgroundColor: theme.dark2,\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n fontFamily: theme.fontFamily,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n fontSize: CHIP_FONT_SIZE[size],\n color: '#fff',\n }}\n >\n {`+${overflowCount}`}\n </Text>\n </View>\n )}\n </View>\n );\n};\n","import { type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Animated, Pressable, StyleSheet } from 'react-native';\n\nimport { Avatar, type AvatarColor, type AvatarSize } from '@/components/Avatar';\nimport { useReducedMotion } from '@/GnomeProvider';\n\nexport interface AvatarRotatorProps {\n /** Full name used for the accessible label and initials fallback. */\n name?: string;\n /** Image URLs to rotate through. */\n avatars?: string[];\n /** Accessible label. Defaults to `name`. */\n alt?: string;\n /** Size of the avatar. Defaults to `\"md\"`. */\n size?: AvatarSize;\n /** Fallback initials color when no avatar image is available. */\n color?: AvatarColor;\n /** Time between avatar changes in milliseconds. Defaults to `3000`. */\n interval?: number;\n /** Crossfade duration in milliseconds. Defaults to `240`. */\n transitionDuration?: number;\n /** Pause automatic rotation while pressed and held. Defaults to `true`. */\n pauseOnPress?: boolean;\n /**\n * Controlled active avatar index.\n * When omitted the rotator manages index state internally.\n */\n activeIndex?: number;\n /** Initial active avatar index for uncontrolled usage. Defaults to `0`. */\n defaultActiveIndex?: number;\n /** Called when the active avatar changes. */\n onIndexChange?: (index: number) => void;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nfunction clampIndex(index: number, length: number) {\n if (length <= 0) {\n return 0;\n }\n\n return ((index % length) + length) % length;\n}\n\nconst SIZE_BOX: Record<AvatarSize, number> = { sm: 24, md: 32, lg: 48, xl: 64 };\n\ninterface RotatorLayerProps {\n active: boolean;\n duration: number;\n reducedMotion: boolean;\n children: ReactNode;\n}\n\n/**\n * One crossfading layer — owns its own `Animated.Value` rather than the\n * parent tracking an array of them, the same \"each item animates itself\"\n * shape `Toast`/`Toaster` already established for a list of independently\n * transitioning items.\n */\nconst RotatorLayer = ({ active, duration, reducedMotion, children }: RotatorLayerProps) => {\n const opacity = useRef(new Animated.Value(active ? 1 : 0)).current;\n\n useEffect(() => {\n if (reducedMotion) {\n opacity.setValue(active ? 1 : 0);\n\n return;\n }\n\n Animated.timing(opacity, {\n toValue: active ? 1 : 0,\n duration,\n useNativeDriver: true,\n }).start();\n }, [active, duration, reducedMotion, opacity]);\n\n return (\n <Animated.View\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={[StyleSheet.absoluteFill, { opacity }]}\n >\n {children}\n </Animated.View>\n );\n};\n\n/**\n * Single avatar surface that crossfades through multiple image sources.\n * Mirrors `@gnome-ui/react`'s `AvatarRotator`. Keeps `Avatar` focused on\n * rendering one identity, while this component owns timing, crossfade\n * animation, and pause behavior.\n *\n * Each source renders as its own absolutely-positioned `Avatar`, layered\n * via `StyleSheet.absoluteFill` and crossfaded with `Animated.timing` on\n * `useNativeDriver: true` — an exact reproduction of the web version's\n * stacked-`.layer`-elements-with-opacity-transition technique, just with\n * `RotatorLayer` (see above) owning each layer's own `Animated.Value`\n * instead of a single shared CSS custom property driving them all.\n *\n * **`prefers-reduced-motion` stops the rotation outright, not just the\n * fade** — ported exactly: the web source's own auto-advance `useEffect`\n * bails out early when reduced motion is on, the same as when paused, so\n * this isn't merely an instant-swap-instead-of-crossfade case like\n * `ProgressBar`'s determinate transitions.\n *\n * The web version's `pauseOnHover` (mouseEnter/mouseLeave, focus/blur)\n * becomes `pauseOnPress` (`onPressIn`/`onPressOut`) — the same touch\n * substitution `Toast`'s own press-and-hold pause already established,\n * kept as a real toggleable prop here (unlike `Toast`, where the web\n * source bakes the behavior in without an escape hatch).\n * `usePrefersReducedMotion` (the web version's `@gnome-ui/hooks` import)\n * has no bearing here — this package's own `useReducedMotion()` from\n * `GnomeProvider` is the correct, already-established source for this.\n *\n * `role=\"img\"` + `accessibilityLabel` ports 1:1 from RN's newer\n * web-aligned `Role` union (the same `Avatar`/`AvatarGroup` precedent).\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Avatar.html\n */\nexport const AvatarRotator = ({\n name = '',\n avatars = [],\n alt,\n size = 'md',\n color,\n interval = 3000,\n transitionDuration = 240,\n pauseOnPress = true,\n activeIndex,\n defaultActiveIndex = 0,\n onIndexChange,\n style,\n testID,\n}: AvatarRotatorProps) => {\n const reducedMotion = useReducedMotion();\n const sources = avatars.map((src) => src.trim()).filter(Boolean);\n const isControlled = activeIndex !== undefined;\n const [internalIndex, setInternalIndex] = useState(defaultActiveIndex);\n const [isPaused, setIsPaused] = useState(false);\n const currentIndex = clampIndex(isControlled ? activeIndex : internalIndex, sources.length);\n\n useEffect(() => {\n if (sources.length <= 1 || interval <= 0 || isPaused || reducedMotion) {\n return;\n }\n\n const timer = setInterval(() => {\n const nextIndex = clampIndex(currentIndex + 1, sources.length);\n\n if (!isControlled) {\n setInternalIndex(nextIndex);\n }\n\n onIndexChange?.(nextIndex);\n }, interval);\n\n return () => clearInterval(timer);\n }, [\n currentIndex,\n interval,\n isControlled,\n isPaused,\n onIndexChange,\n reducedMotion,\n sources.length,\n ]);\n\n if (sources.length === 0) {\n return <Avatar name={name} alt={alt} size={size} color={color} style={style} />;\n }\n\n const label = alt ?? (name || undefined) ?? 'Avatar';\n const box = SIZE_BOX[size];\n\n return (\n <Pressable\n testID={testID}\n accessible\n role=\"img\"\n accessibilityLabel={label}\n onPressIn={() => pauseOnPress && setIsPaused(true)}\n onPressOut={() => pauseOnPress && setIsPaused(false)}\n style={[{ width: box, height: box, borderRadius: box / 2, overflow: 'hidden' }, style]}\n >\n {sources.map((src, index) => (\n <RotatorLayer\n key={`${src}-${index}`}\n active={index === currentIndex}\n duration={Math.max(0, transitionDuration)}\n reducedMotion={reducedMotion}\n >\n <Avatar name={name} src={src} size={size} />\n </RotatorLayer>\n ))}\n </Pressable>\n );\n};\n","import type { ReactNode } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Text, View } from 'react-native';\n\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\nimport type { GnomeResolvedColorScheme, GnomeThemeTokens } from '@/theme';\n\nexport type BadgeVariant = 'accent' | 'success' | 'warning' | 'error' | 'neutral';\n\nexport interface BadgeProps {\n /**\n * Visual style. Defaults to `\"accent\"`.\n * - `accent` — blue, for counts and highlights.\n * - `success` — green, for positive status.\n * - `warning` — yellow, for cautionary status.\n * - `error` — red, for failures or urgent counts.\n * - `neutral` — gray, for inactive or secondary counts.\n */\n variant?: BadgeVariant;\n /**\n * When true, renders a small dot with no label — used for unread/online indicators.\n * The `children` are ignored in dot mode.\n */\n dot?: boolean;\n /** Number or short text to display. Keep to 1–3 characters. String/number render as a themed label; other nodes render as-is. */\n children?: ReactNode;\n /**\n * When provided, the badge is positioned over this child element at the\n * top-right corner.\n */\n anchor?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\ninterface VariantColors {\n backgroundColor: string;\n color: string;\n}\n\nfunction getVariantColors(\n theme: GnomeThemeTokens,\n colorScheme: GnomeResolvedColorScheme,\n variant: BadgeVariant,\n): VariantColors {\n switch (variant) {\n case 'accent':\n return { backgroundColor: theme.accentBgColor, color: theme.accentFgColor };\n case 'success':\n return { backgroundColor: theme.successBgColor, color: theme.successFgColor };\n case 'warning':\n return { backgroundColor: theme.warningBgColor, color: theme.warningFgColor };\n case 'error':\n return { backgroundColor: theme.errorBgColor, color: theme.errorFgColor };\n case 'neutral':\n return {\n backgroundColor: colorScheme === 'dark' ? theme.dark2 : theme.light4,\n color: theme.windowFgColor,\n };\n }\n}\n\n/**\n * Counter or status indicator, optionally overlaid on another element.\n *\n * Rebuilt with `View`/`Text` rather than ported from `@gnome-ui/react`'s\n * DOM-based JSX, but mirrors its prop API. `children` renders as a themed\n * `Text` label when it's a string or number (the common case — counts and\n * short text); any other node renders as-is, the same convention `Button`'s\n * `children` already established.\n *\n * The web CSS's `box-shadow: 0 0 0 2px var(--gnome-window-bg-color)` ring\n * (always present, separating the badge from whatever's behind it) has no\n * RN equivalent that avoids affecting layout — RN's `border*` shrinks the\n * content box instead of drawing outside it. Reproduced instead with an\n * outer wrapping `View` (2px padding, `theme.windowBgColor` background,\n * pill radius) around the actual colored badge, so the ring appears to\n * spread outward exactly like the web version's non-blurred shadow, without\n * eating into the badge's own text padding.\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/badges.html\n */\nexport const Badge = ({ variant = 'accent', dot = false, children, anchor, style }: BadgeProps) => {\n const theme = useGnomeTheme();\n const colorScheme = useResolvedColorScheme();\n const { backgroundColor, color } = getVariantColors(theme, colorScheme, variant);\n\n const badgeEl = (\n <View\n testID=\"badge-ring\"\n style={[\n {\n padding: 2,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.windowBgColor,\n },\n anchor\n ? {\n position: 'absolute' as const,\n top: dot ? -2 : -4,\n right: dot ? -2 : -4,\n zIndex: 1,\n }\n : null,\n ]}\n >\n <View\n testID=\"badge\"\n style={[\n {\n minWidth: dot ? 10 : 18,\n height: dot ? 10 : 18,\n paddingHorizontal: dot ? 0 : 5,\n borderRadius: theme.radiusPill,\n backgroundColor,\n alignItems: 'center',\n justifyContent: 'center',\n },\n style,\n ]}\n >\n {!dot &&\n (typeof children === 'string' || typeof children === 'number' ? (\n <Text\n style={{\n fontFamily: theme.fontFamily,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n fontSize: 11,\n lineHeight: 13,\n color,\n }}\n >\n {children}\n </Text>\n ) : (\n children\n ))}\n </View>\n </View>\n );\n\n if (anchor) {\n return (\n <View style={{ alignSelf: 'flex-start' }}>\n {anchor}\n {badgeEl}\n </View>\n );\n }\n\n return badgeEl;\n};\n","import type { ReactNode } from 'react';\nimport type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native';\nimport { Pressable, Text as RNText, View } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type BannerVariant = 'info' | 'warning' | 'error' | 'success';\n\nexport interface BannerProps extends Omit<ViewProps, 'style'> {\n /**\n * Visual emphasis level.\n * - `info` (default) — neutral, accent-colored. Use for tips and notices.\n * - `warning` — yellow. Use for recoverable problems.\n * - `error` — red. Use for failures that need attention.\n * - `success` — green. Use for confirmations.\n */\n variant?: BannerVariant;\n /** The message text. Keep it short — one or two sentences. */\n children: ReactNode;\n /**\n * Label for the optional action button placed at the trailing end.\n * When provided, `onAction` should also be supplied.\n */\n actionLabel?: string;\n /** Called when the user presses the action button. */\n onAction?: () => void;\n /**\n * When true a dismiss (×) button is shown at the trailing edge.\n * Provide `onDismiss` to handle removal from the list.\n */\n dismissible?: boolean;\n /** Called when the user presses the dismiss button. */\n onDismiss?: () => void;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Persistent message strip displayed at the top of a view, following the\n * Adwaita `AdwBanner` pattern. Use for important information that persists\n * until the user acts or explicitly dismisses it — unlike `Toast`, it never\n * auto-dismisses.\n *\n * RN's `AccessibilityRole` union has no \"status\" value (the web version's\n * `role=\"status\"`); `\"alert\"` is the closest available role, paired with\n * `accessibilityLiveRegion=\"polite\"` (Android's live-region API) as the\n * nearest match to `aria-live=\"polite\"` — the same substitution `Toast`\n * already established for the identical web role pair. Since the banner\n * itself is a plain `View` (not `Pressable` — only its buttons are\n * interactive), `accessible` is set explicitly alongside `accessibilityRole`,\n * per the `BoxedList` lesson that a bare `View` isn't an accessibility\n * element by default.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Banner.html\n * @see https://developer.gnome.org/hig/patterns/feedback/banners.html\n */\nexport const Banner = ({\n variant = 'info',\n children,\n actionLabel,\n onAction,\n dismissible = false,\n onDismiss,\n style,\n ...viewProps\n}: BannerProps) => {\n const theme = useGnomeTheme();\n\n const colors = {\n info: { bg: theme.accentBgColor, fg: theme.accentFgColor },\n warning: { bg: theme.warningBgColor, fg: theme.warningFgColor },\n error: { bg: theme.errorBgColor, fg: theme.errorFgColor },\n success: { bg: theme.successBgColor, fg: theme.successFgColor },\n }[variant];\n\n // Mirrors the source CSS's per-variant :active overlay: a light overlay on\n // the darker info/error/success backgrounds, a dark one on the light\n // warning background.\n const pressedOverlay =\n variant === 'warning' ? 'rgba(0, 0, 0, 0.14)' : 'rgba(255, 255, 255, 0.25)';\n\n return (\n <View\n accessible\n accessibilityRole=\"alert\"\n accessibilityLiveRegion=\"polite\"\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: 10,\n paddingHorizontal: theme.space4,\n minHeight: 44,\n width: '100%',\n backgroundColor: colors.bg,\n },\n style,\n ]}\n {...viewProps}\n >\n <Text variant=\"body\" style={{ flex: 1, minWidth: 0, color: colors.fg }}>\n {children}\n </Text>\n\n {(actionLabel || dismissible) && (\n <View\n style={{ flexDirection: 'row', alignItems: 'center', gap: theme.space1, flexShrink: 0 }}\n >\n {actionLabel && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={actionLabel}\n onPress={onAction}\n style={({ pressed }) => ({\n paddingVertical: 4,\n paddingHorizontal: theme.space2,\n borderRadius: theme.radiusMd,\n backgroundColor: pressed ? pressedOverlay : 'transparent',\n })}\n >\n <RNText\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n color: colors.fg,\n }}\n >\n {actionLabel}\n </RNText>\n </Pressable>\n )}\n\n {dismissible && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel=\"Dismiss\"\n hitSlop={4}\n onPress={onDismiss}\n style={({ pressed }) => ({\n width: 28,\n height: 28,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusMd,\n opacity: pressed ? 1 : 0.6,\n backgroundColor: pressed ? pressedOverlay : 'transparent',\n })}\n >\n <RNText style={{ fontSize: theme.fontSizeBody, color: colors.fg }}>{'×'}</RNText>\n </Pressable>\n )}\n </View>\n )}\n </View>\n );\n};\n","import { forwardRef } from 'react';\nimport type { View, ViewProps } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nexport type BinProps = ViewProps;\n\n/**\n * Single-child container with no visual styling.\n *\n * A transparent wrapper that forwards all `View` props (and a ref to the\n * underlying `View`) straight through — useful as a neutral base for custom\n * components that need to apply layout or size constraints without\n * introducing any chrome of their own.\n *\n * Mirrors `AdwBin` and `@gnome-ui/react`'s own `Bin`. A plain RN `View`\n * already has no default visual styling (no background, no border), so\n * unlike the web port there's no CSS reset to strip — this is a pure\n * passthrough.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Bin.html\n */\nexport const Bin = forwardRef<View, BinProps>(function Bin(props, ref) {\n return <RNView ref={ref} {...props} />;\n});\n","import type { ReactNode } from 'react';\nimport type { StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { View } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type BlockquoteVariant = 'default' | 'info' | 'warning' | 'error' | 'success';\n\nexport interface BlockquoteProps extends Omit<ViewProps, 'style'> {\n /**\n * Visual emphasis style.\n * - `\"default\"` (default) — neutral left border; use for general quotations.\n * - `\"info\"` — accent blue; use for tips and noteworthy passages.\n * - `\"warning\"` — yellow; use for cautionary content.\n * - `\"error\"` — red; use for critical or deprecated content.\n * - `\"success\"` — green; use for positive or approved content.\n */\n variant?: BlockquoteVariant;\n /** Optional icon rendered to the left of the quoted text, e.g. an `<Icon>`. */\n icon?: ReactNode;\n /** Attribution rendered below the quote — author name, source title, etc. */\n cite?: ReactNode;\n /** The quoted content. */\n children: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * The source CSS's `color-mix(in srgb, <color> 8%, transparent)` /\n * `14%` tints (warning is the one outlier at 14%, every other variant is\n * 8%) have no RN equivalent — resolved as an 8-digit `#RRGGBBAA` alpha\n * suffix on the variant's own hex token instead, the same substitution\n * `Chip`'s selected-state tint already established. `0x14` ≈ 8%, `0x24` ≈ 14%.\n * `default` has no background tint at all in the source CSS, and its\n * border color (`theme.borderSubtle`) is already an `rgba(...)` string, not\n * a hex token — appending a hex alpha suffix to it would be invalid, so it's\n * handled separately below rather than through this table.\n */\nconst VARIANT_TINT: Record<Exclude<BlockquoteVariant, 'default'>, string> = {\n info: '14',\n warning: '24',\n error: '14',\n success: '14',\n};\n\n/**\n * Styled pull-quote with a colored left border, mirroring `@gnome-ui/react`'s\n * `Blockquote`. Five visual variants share the same severity scale as\n * `Banner`/`Chip`.\n *\n * `<blockquote>`/`<footer>`/`<cite>` have no RN element equivalent — this\n * renders as a plain `View` with no semantic role, the same \"no landmark\n * role available\" gap `PathBar`'s dropped `<nav>` and `HeaderBar`'s dropped\n * `<header>` already established; the quoted text and citation are plain\n * `Text` reads with no assistive-tech grouping to port.\n *\n * @example\n * <Blockquote variant=\"info\" cite=\"Ada Lovelace, 1842\">\n * The Analytical Engine has no pretensions to originate anything.\n * </Blockquote>\n *\n * @see https://developer.gnome.org/hig/\n */\nexport const Blockquote = ({\n variant = 'default',\n icon,\n cite,\n children,\n style,\n ...viewProps\n}: BlockquoteProps) => {\n const theme = useGnomeTheme();\n\n let borderColor = theme.borderSubtle;\n let backgroundColor = 'transparent';\n\n if (variant !== 'default') {\n const variantColor = {\n info: theme.accentBgColor,\n warning: theme.warningBgColor,\n error: theme.errorBgColor,\n success: theme.successBgColor,\n }[variant];\n\n borderColor = variantColor;\n backgroundColor = `${variantColor}${VARIANT_TINT[variant]}`;\n }\n\n return (\n <View\n style={[\n {\n paddingVertical: theme.space2,\n paddingHorizontal: theme.space3,\n borderLeftWidth: 3,\n borderLeftColor: borderColor,\n borderTopRightRadius: theme.radiusSm,\n borderBottomRightRadius: theme.radiusSm,\n backgroundColor,\n },\n style,\n ]}\n {...viewProps}\n >\n <View style={{ flexDirection: 'row', alignItems: 'flex-start', gap: theme.space2 }}>\n {icon && (\n <View style={{ marginTop: 2 }} accessibilityElementsHidden importantForAccessibility=\"no\">\n {icon}\n </View>\n )}\n\n <Text variant=\"body\" style={{ flex: 1, minWidth: 0 }}>\n {children}\n </Text>\n </View>\n\n {cite !== undefined && (\n <View style={{ marginTop: theme.space1 }}>\n <Text variant=\"caption\" color=\"dim\">\n {cite}\n </Text>\n </View>\n )}\n </View>\n );\n};\n","import { type ReactNode, useEffect, useMemo, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport {\n Animated,\n BackHandler,\n Dimensions,\n Modal,\n PanResponder,\n Pressable,\n View,\n} from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\n/**\n * `Animated`'s style interpolation only takes effect on `Animated.*` host\n * components — a plain `Pressable` would silently ignore the animated\n * `opacity` in its style, the same gotcha already documented on `Toast`/\n * `Dialog`. Hoisted to module scope so it isn't recreated on every render.\n */\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable);\n\nconst DRAG_CLOSE_THRESHOLD = 150;\n\nexport interface BottomSheetProps {\n /** Whether the sheet is visible. */\n open: boolean;\n /** Optional heading shown below the drag handle. */\n title?: ReactNode;\n children?: ReactNode;\n /** Called when the user dismisses the sheet (backdrop press, Android back button, or drag down). */\n onClose?: () => void;\n /** Whether pressing the backdrop closes the sheet. Defaults to `true`. */\n closeOnBackdrop?: boolean;\n style?: StyleProp<ViewStyle>;\n /** Applied to the backdrop, mirroring `Dialog`'s own `testID`. */\n testID?: string;\n}\n\n/**\n * Slide-up panel that overlays content from the bottom edge, mirroring\n * `AdwBottomSheet` (libadwaita 1.6+) and `@gnome-ui/react`'s `BottomSheet`.\n *\n * Rebuilt with `View`/`Modal`/`PanResponder` rather than ported from the web\n * version's DOM `Portal` + manual focus trap + raw pointer events —\n * reusing this package's own established pieces: `Dialog`'s backdrop-\n * opacity-on-an-`AnimatedPressable` + no-op-`Pressable`-around-the-card\n * (so a tap on the card never bubbles to the backdrop) recipe, and\n * `BackHandler`'s `hardwareBackPress` as the Android analog of the web\n * version's Escape listener (same pattern `Dialog`/`Popover` already use).\n * Focus-trapping and focus-restore-on-close have no port — no DOM\n * `document.activeElement`/`querySelector` equivalent exists in RN, the\n * same gap already present in every other floating component here.\n *\n * **Real drag-to-dismiss, not a fixed-panel simplification**: unlike the\n * web version's raw `PointerEvent` handlers directly mutating\n * `style.transform`, this uses `PanResponder` (the same core RN API\n * `Slider` already proved handles a threshold-based drag gesture) driving\n * a single `Animated.Value` — the *same* value used for the slide-in/\n * slide-out entrance animation. `PanResponder`'s `gestureState.dy` is the\n * cumulative vertical delta since the gesture started, so a plain\n * `translateY.setValue(Math.max(0, gestureState.dy))` during\n * `onPanResponderMove` reproduces the web version's own\n * `Math.max(0, e.clientY - dragStartY.current)` clamp exactly, and a\n * release past `DRAG_CLOSE_THRESHOLD` (150 px, same constant as the web\n * version) animates the rest of the way down before calling `onClose`,\n * otherwise it springs back to `0`. The gesture responder is attached only\n * to the handle bar `View`, not the whole sheet, mirroring the web\n * version's `onPointerDown` being scoped to `.handle` alone.\n *\n * **A real slide-up needs the sheet's own height first** — RN's\n * `transform` has no percentage-of-self units (the same `Slider`/`Avatar`\n * pitfall), unlike the web version's `translateY(100%)`. The sheet renders\n * once off-screen (measured via `onLayout`, starting `translateY` at that\n * measured height) before animating to `0` — the same \"resolve own\n * rendered size, then animate\" two-step every other sized-on-open\n * component in this package (`Dialog`, `Dropdown`, `Popover`) already\n * needs, just for a transform offset instead of a floating position.\n *\n * **Real, timed exit animation, unlike `Dialog`**: `Dialog`'s web source\n * has no exit keyframes at all (`Modal`'s `visible={false}` unmounts\n * immediately, matching the web version's plain `return null`) — but\n * `BottomSheet`'s web source explicitly animates both the backdrop fade\n * and the sheet's slide-out before removing it. Ported with a local\n * `visible` state that lags one animation behind the `open` prop: closing\n * starts the exit `Animated.timing`s and only flips `visible` to `false`\n * (unmounting the `Modal`'s content) in the animation's own completion\n * callback — not a `setTimeout` racing a hardcoded duration like the web\n * version, since `Animated`'s callback already fires exactly when the\n * animation actually finishes.\n *\n * The web version's `backdrop-filter: blur(4px)` has no port — this\n * package has no native blur view dependency, the same reasoning that\n * already dropped `Sidebar`'s blurred `variant`. `useBodyScrollLock` has no\n * RN equivalent needed: `Modal` already blocks all interaction with\n * whatever's behind it, there is no scrollable \"body\" to lock separately.\n *\n * `role=\"dialog\"` + `accessibilityViewIsModal` port 1:1 from `Dialog`'s own\n * precedent. `children`, when a plain string, must be wrapped in `Text`\n * before rendering — RN throws if a raw string is a `View`'s child, unlike\n * the web version's `<div>{children}</div>`, which needed no such check.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.BottomSheet.html\n */\nexport const BottomSheet = ({\n open,\n title,\n children,\n onClose,\n closeOnBackdrop = true,\n style,\n testID,\n}: BottomSheetProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const [visible, setVisible] = useState(open);\n const [sheetHeight, setSheetHeight] = useState(0);\n\n const translateY = useRef(new Animated.Value(Dimensions.get('window').height)).current;\n const backdropOpacity = useRef(new Animated.Value(0)).current;\n const isDragging = useRef(false);\n\n useEffect(() => {\n if (open) {\n setVisible(true);\n }\n }, [open]);\n\n // Animate in once the sheet is mounted and measured; animate out (then\n // actually unmount) when `open` flips false — skipped mid-drag, since the\n // gesture handlers below already own `translateY` in that case.\n useEffect(() => {\n if (!visible || isDragging.current) {\n return;\n }\n\n if (open) {\n if (reducedMotion) {\n translateY.setValue(0);\n backdropOpacity.setValue(1);\n\n return;\n }\n\n if (sheetHeight > 0) {\n translateY.setValue(sheetHeight);\n Animated.timing(translateY, {\n toValue: 0,\n duration: theme.durationNormal,\n useNativeDriver: true,\n }).start();\n }\n\n Animated.timing(backdropOpacity, {\n toValue: 1,\n duration: theme.durationNormal,\n useNativeDriver: true,\n }).start();\n } else {\n if (reducedMotion) {\n setVisible(false);\n\n return;\n }\n\n Animated.timing(translateY, {\n toValue: sheetHeight || Dimensions.get('window').height,\n duration: theme.durationNormal,\n useNativeDriver: true,\n }).start(({ finished }) => {\n if (finished) {\n setVisible(false);\n }\n });\n Animated.timing(backdropOpacity, {\n toValue: 0,\n duration: theme.durationNormal,\n useNativeDriver: true,\n }).start();\n }\n }, [\n open,\n visible,\n sheetHeight,\n reducedMotion,\n translateY,\n backdropOpacity,\n theme.durationNormal,\n ]);\n\n // Android hardware back button closes the sheet — the analog of the web\n // version's document-level Escape listener, same pattern `Dialog` uses.\n useEffect(() => {\n if (!open) {\n return;\n }\n\n const subscription = BackHandler.addEventListener('hardwareBackPress', () => {\n onClose?.();\n\n return true;\n });\n\n return () => subscription.remove();\n }, [open, onClose]);\n\n const panResponder = useMemo(\n () =>\n PanResponder.create({\n onStartShouldSetPanResponder: () => true,\n onPanResponderGrant: () => {\n isDragging.current = true;\n },\n onPanResponderMove: (_event, gestureState) => {\n translateY.setValue(Math.max(0, gestureState.dy));\n },\n onPanResponderRelease: (_event, gestureState) => {\n isDragging.current = false;\n\n const dy = Math.max(0, gestureState.dy);\n\n if (dy >= DRAG_CLOSE_THRESHOLD) {\n // Just request the close — the `open` → `false` effect above\n // owns the actual exit animation (and reduced-motion handling),\n // continuing smoothly from wherever the drag left `translateY`\n // rather than duplicating that animation here.\n onClose?.();\n } else {\n Animated.timing(translateY, {\n toValue: 0,\n duration: theme.durationNormal,\n useNativeDriver: true,\n }).start();\n }\n },\n }),\n [translateY, theme.durationNormal, onClose],\n );\n\n const handleSheetLayout = (e: LayoutChangeEvent) => {\n setSheetHeight(e.nativeEvent.layout.height);\n };\n\n if (!visible) {\n return null;\n }\n\n return (\n <Modal\n visible={visible}\n transparent\n animationType=\"none\"\n statusBarTranslucent\n onRequestClose={onClose}\n >\n <AnimatedPressable\n testID={testID}\n onPress={closeOnBackdrop ? onClose : undefined}\n accessible={false}\n style={{\n flex: 1,\n justifyContent: 'flex-end',\n backgroundColor: theme.dialogBackdropColor,\n opacity: backdropOpacity,\n }}\n >\n <Pressable onPress={() => {}} accessible={false}>\n <Animated.View\n onLayout={handleSheetLayout}\n accessible\n role=\"dialog\"\n accessibilityViewIsModal\n style={[\n {\n alignSelf: 'center',\n width: '100%',\n maxWidth: 560,\n backgroundColor: theme.dialogBgColor,\n borderTopLeftRadius: theme.radiusXl,\n borderTopRightRadius: theme.radiusXl,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: -4 },\n shadowOpacity: 0.18,\n shadowRadius: 24,\n elevation: 12,\n transform: [{ translateY }],\n },\n style,\n ]}\n >\n <View\n testID=\"bottom-sheet-handle\"\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n alignItems: 'center',\n paddingTop: theme.space2,\n paddingBottom: theme.space1,\n }}\n {...panResponder.panHandlers}\n >\n <View\n style={{\n width: 36,\n height: 4,\n borderRadius: 2,\n backgroundColor: theme.windowFgColor,\n opacity: 0.2,\n }}\n />\n </View>\n\n {title && (\n <Text\n variant=\"title-4\"\n style={{\n paddingHorizontal: theme.space4,\n paddingBottom: theme.space2,\n textAlign: 'center',\n fontWeight: String(theme.fontWeightBold) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n )}\n\n {children && (\n <View style={{ paddingHorizontal: theme.space4, paddingBottom: theme.space4 }}>\n {typeof children === 'string' ? <Text variant=\"body\">{children}</Text> : children}\n </View>\n )}\n </Animated.View>\n </Pressable>\n </AnimatedPressable>\n </Modal>\n );\n};\n","import type { AnyIconDefinition } from '@gnome-ui/icons';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Pressable, View } from 'react-native';\n\nimport { Badge } from '@/components/Badge';\nimport { Icon } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface BottomTabBarItem<V extends string = string> {\n /** The value reported to `onChange` when this tab is pressed. */\n value: V;\n /** Label shown below the icon. */\n label: string;\n /** Icon shown while this tab is not the selected one. */\n icon: AnyIconDefinition;\n /**\n * Icon shown while this tab *is* selected — the filled-vs-outline\n * convention iOS/Android system tab bars both use (e.g. a hollow heart\n * outline that becomes solid when active). Optional: omit to reuse\n * `icon` for both states, tinted differently.\n */\n activeIcon?: AnyIconDefinition;\n /**\n * Notification indicator. `true` renders a small dot (unread/pending,\n * no count); a `number` renders a counted badge (values above 99 render\n * as `\"99+\"`, matching `TabItem`'s own count-badge convention).\n */\n badge?: number | boolean;\n /** Disables this one tab. */\n disabled?: boolean;\n}\n\nexport interface BottomTabBarProps<V extends string = string> {\n /** The tabs to render, in order. */\n items: BottomTabBarItem<V>[];\n /** The currently selected tab's value. */\n value: V;\n /** Called when the user presses a tab. */\n onChange: (value: V) => void;\n /**\n * Extra bottom padding, in addition to the bar's own vertical padding —\n * pass your own `useSafeAreaInsets().bottom` here so the bar clears the\n * home indicator / gesture area on notched devices. This package takes\n * no dependency on `react-native-safe-area-context` itself (the same\n * \"no new peer dependency without a deliberate decision\" standard\n * `AnimatedIcon`'s `react-native-svg` addition was held to) — the\n * consumer's own app almost certainly already has it for its root\n * layout, so threading the value in here is cheaper than this package\n * depending on it too. Defaults to `0`.\n */\n bottomInset?: number;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Fixed bottom navigation bar — the iOS/Android \"tab bar\" pattern (Music,\n * Instagram, most system apps): a small, fixed set of top-level\n * destinations, each an icon + label, always visible at the foot of the\n * screen. No GNOME/libadwaita widget mirrors this (desktop apps don't use\n * bottom navigation), so this is an original component for this package,\n * not a port — built to fill a real gap once mobile \"modern app shell\"\n * navigation was requested.\n *\n * Distinct from the existing `TabBar`/`TabItem` (an in-page, horizontally\n * scrollable content switcher mirroring `@gnome-ui/react`'s `Tabs`) — that\n * one reuses `headerbarFgColor` for every tab's label regardless of\n * selection state, signaling \"active\" purely via a background pill +\n * bold weight + accent underline, never by tinting the icon/label\n * themselves. A bottom tab bar's whole visual signature is the opposite:\n * the active icon+label *are* the app's accent color. `Icon`'s `color`\n * prop can't express that on its own — it only resolves to a fixed\n * named-palette swatch (`color=\"blue\"` always means `theme.blue3`, never\n * whatever accent the app actually configured via `GnomeProvider\n * accentColor`) — so this is the first consumer of `Icon`'s new\n * `tintColor` prop, passing `theme.accentColor` directly for the selected\n * tab and leaving it unset (dimmed default) for the rest.\n *\n * The optional notification indicator reuses the real `Badge` component\n * for the dot/pill itself, but positions it manually rather than through\n * `Badge`'s own `anchor` mode — `anchor` wraps `[anchor, badge]` in a\n * `View` with a hardcoded `alignSelf: 'flex-start'`, which is fine for\n * `Badge`'s usual context (a row where the anchor is meant to hug the\n * start) but silently broke centering here: a bare icon (no badge) is a\n * direct child of this component's centered column, while a badged icon's\n * extra `anchor`-mode wrapper overrode that centering and pinned itself to\n * the left edge — the two looked visibly misaligned side by side. Every\n * tab now gets the identical `position: 'relative'` wrapper shape\n * regardless of whether it has a badge, so centering behaves identically\n * across all of them; the badge itself is a plain (non-`anchor`) `Badge`,\n * absolutely positioned and re-sized for this icon's actual scale (`size=\"lg\"`\n * is only 20px — `Badge`'s own default dot/offset sizing assumes a much\n * larger anchor like an avatar or `IconButton`, and would otherwise nearly\n * cover the icon rather than sit as a small corner accent). Press feedback\n * is the same `pressed ? theme.activeOverlay : 'transparent'` recipe every\n * other `Pressable` in this package already uses.\n *\n * `bottomInset` stands in for real safe-area awareness without this\n * package taking on `react-native-safe-area-context` as a dependency —\n * see its own doc comment.\n *\n * @example\n * <BottomTabBar\n * items={[\n * { value: 'home', label: 'Home', icon: HomeOutline, activeIcon: HomeFilled },\n * { value: 'search', label: 'Search', icon: Search },\n * { value: 'profile', label: 'Profile', icon: Person, badge: true },\n * ]}\n * value={tab}\n * onChange={setTab}\n * bottomInset={insets.bottom}\n * />\n */\nexport const BottomTabBar = <V extends string = string>({\n items,\n value,\n onChange,\n bottomInset = 0,\n style,\n testID,\n}: BottomTabBarProps<V>) => {\n const theme = useGnomeTheme();\n\n return (\n <View\n testID={testID}\n accessible\n role=\"tablist\"\n style={[\n {\n flexDirection: 'row',\n backgroundColor: theme.headerbarBgColor,\n borderTopWidth: 1,\n borderTopColor: theme.headerbarBorderColor,\n paddingTop: 8,\n paddingBottom: 4 + bottomInset,\n },\n style,\n ]}\n >\n {items.map((item) => {\n const selected = item.value === value;\n const icon = selected ? (item.activeIcon ?? item.icon) : item.icon;\n const tint = selected ? theme.accentColor : theme.windowFgColor;\n\n const iconEl = (\n <Icon\n icon={icon}\n size=\"lg\"\n tintColor={selected ? theme.accentColor : undefined}\n style={selected ? undefined : { opacity: theme.opacityDim }}\n />\n );\n\n return (\n <Pressable\n key={item.value}\n testID={testID ? `${testID}-${item.value}` : undefined}\n disabled={item.disabled}\n role=\"tab\"\n accessibilityLabel={item.label}\n accessibilityState={{ selected, disabled: !!item.disabled }}\n onPress={() => onChange(item.value)}\n style={({ pressed }) => ({\n flex: 1,\n alignItems: 'center',\n gap: 2,\n paddingVertical: 4,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n opacity: item.disabled ? theme.opacityDisabled : 1,\n })}\n >\n <View style={{ position: 'relative' }}>\n {iconEl}\n\n {item.badge !== undefined && item.badge !== false && (\n <View style={{ position: 'absolute', top: -4, end: -6 }}>\n <Badge\n variant=\"error\"\n dot={item.badge === true}\n style={\n item.badge === true\n ? { minWidth: 8, height: 8 }\n : { minWidth: 14, height: 14, paddingHorizontal: 3 }\n }\n >\n {typeof item.badge === 'number'\n ? item.badge > 99\n ? '99+'\n : item.badge\n : undefined}\n </Badge>\n </View>\n )}\n </View>\n\n <Text\n numberOfLines={1}\n style={{\n fontSize: theme.fontSizeCaption,\n fontWeight: String(\n selected ? theme.fontWeightSemibold : theme.fontWeightNormal,\n ) as TextStyle['fontWeight'],\n color: tint,\n opacity: selected ? 1 : theme.opacityDim,\n }}\n >\n {item.label}\n </Text>\n </Pressable>\n );\n })}\n </View>\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { FlexStyle, StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\n/** GNOME HIG standard spacing values (matches `GtkBox` spacing tokens). */\nexport type BoxSpacing = 3 | 6 | 12 | 18 | 24 | 32 | 48;\n\n/** Alias of `BoxSpacing` for use as a padding scale. */\nexport type BoxPadding = BoxSpacing;\n\nexport type BoxOrientation = 'horizontal' | 'vertical';\nexport type BoxAlign = 'start' | 'center' | 'end' | 'stretch' | 'baseline';\nexport type BoxJustify =\n | 'start'\n | 'center'\n | 'end'\n | 'space-between'\n | 'space-around'\n | 'space-evenly';\n\nexport interface BoxProps extends Omit<ViewProps, 'style'> {\n /**\n * Direction children are arranged.\n * `\"vertical\"` → `flexDirection: 'column'` (default).\n * `\"horizontal\"` → `flexDirection: 'row'`.\n */\n orientation?: BoxOrientation;\n /**\n * Gap between children, in density-independent pixels.\n * Accepts any of the GNOME HIG standard spacing values or any number.\n * Defaults to `6` (the HIG \"standard\" inner spacing).\n */\n spacing?: BoxSpacing | number;\n /**\n * Cross-axis alignment (`alignItems`).\n * Defaults to `\"stretch\"` for vertical, `\"center\"` for horizontal.\n */\n align?: BoxAlign;\n /**\n * Main-axis distribution (`justifyContent`).\n * Defaults to `\"start\"`.\n */\n justify?: BoxJustify;\n /** Inner padding applied to all sides, in density-independent pixels. */\n padding?: BoxPadding | number;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Yoga's own edge names for the two values CSS spells differently — the web\n * version passes `align`/`justify` straight through to CSS, which accepts\n * the bare `start`/`end` keywords RN's `FlexStyle` union doesn't have.\n */\nconst ALIGN: Record<BoxAlign, FlexStyle['alignItems']> = {\n start: 'flex-start',\n center: 'center',\n end: 'flex-end',\n stretch: 'stretch',\n baseline: 'baseline',\n};\n\nconst JUSTIFY: Record<BoxJustify, FlexStyle['justifyContent']> = {\n start: 'flex-start',\n center: 'center',\n end: 'flex-end',\n 'space-between': 'space-between',\n 'space-around': 'space-around',\n 'space-evenly': 'space-evenly',\n};\n\n/**\n * Fundamental flex layout primitive — the RN equivalent of `GtkBox`, and a\n * 1:1 mirror of `@gnome-ui/react`'s own `Box`.\n *\n * Arranges children in a single row or column with consistent spacing\n * following the GNOME Human Interface Guidelines spacing scale:\n *\n * | Token | dp | Use |\n * |-------|----|-----|\n * | tight | 3 | Dense UI, icon + label pairs |\n * | standard | 6 | Default inner spacing |\n * | medium | 12 | Between related groups |\n * | large | 18 | Between loosely related sections |\n * | section | 24 | Page-level section gaps |\n * | loose | 32 | Large content separation |\n * | jumbo | 48 | Hero / splash spacing |\n *\n * `BoxSpacing` keeps the web package's exact seven values rather than being\n * remapped onto this package's own `theme.space1`–`space6` scale — the two\n * overlap at 6/12/18/24/48 but not at 3 or 32/36, and `BoxSpacing` is a\n * published type consumers may already be importing, so it ports verbatim.\n *\n * Two things the web version accepts don't survive the platform: `spacing`\n * and `padding` are numbers only (RN's `gap`/`padding` take dp, not CSS\n * strings like `\"1rem\"`), and `align`/`justify` — which the web passes\n * straight through to CSS — are mapped from their bare `start`/`end`\n * keywords onto Yoga's `flex-start`/`flex-end`. The prop values stay the\n * web ones so the API reads identically across both packages; only the\n * internal translation differs.\n *\n * `display: 'flex'` has no port and needs none — every RN `View` is already\n * a flex container.\n *\n * @example\n * // Vertical section (heading + content)\n * <Box spacing={12}>\n * <Text variant=\"caption-heading\" color=\"dim\">Devices</Text>\n * <BoxedList>…</BoxedList>\n * </Box>\n *\n * @example\n * // Horizontal icon + label\n * <Box orientation=\"horizontal\" spacing={6} align=\"center\">\n * <Icon icon={Folder} />\n * <Text>Documents</Text>\n * </Box>\n *\n * @see https://developer.gnome.org/hig/guidelines/spacing.html\n */\nexport const Box = forwardRef<View, BoxProps>(function Box(\n {\n orientation = 'vertical',\n spacing = 6,\n align,\n justify = 'start',\n padding,\n children,\n style,\n ...viewProps\n },\n ref,\n) {\n const defaultAlign: BoxAlign = orientation === 'horizontal' ? 'center' : 'stretch';\n\n return (\n <RNView\n ref={ref}\n style={[\n {\n flexDirection: orientation === 'horizontal' ? 'row' : 'column',\n gap: spacing,\n alignItems: ALIGN[align ?? defaultAlign],\n justifyContent: JUSTIFY[justify],\n },\n padding !== undefined && { padding },\n style,\n ]}\n {...viewProps}\n >\n {children}\n </RNView>\n );\n});\n","import { forwardRef } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type SeparatorOrientation = 'horizontal' | 'vertical';\n\nexport interface SeparatorProps extends Omit<ViewProps, 'style'> {\n /** Direction of the dividing line. Defaults to `\"horizontal\"`. */\n orientation?: SeparatorOrientation;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Thin dividing line that separates groups of content.\n *\n * Color is driven entirely by `theme.cardShadeColor`, which already\n * resolves correctly per color scheme, so — unlike `Switch`/`Checkbox`/\n * `RadioButton` — there's no `useResolvedColorScheme()` branching needed\n * here.\n *\n * Rebuilt as a plain `View` rather than ported from `@gnome-ui/react`'s\n * `<hr>`/`<div role=\"separator\">`: RN's `AccessibilityRole` union has no\n * `\"separator\"` value, so — since a divider carries no information a\n * screen reader user needs — it's excluded from the accessibility tree\n * entirely with `accessible={false}`, the RN-idiomatic way to mark a\n * purely decorative element, rather than reaching for a role that doesn't\n * exist.\n *\n * @see https://developer.gnome.org/hig/patterns/containers.html\n */\nexport const Separator = forwardRef<View, SeparatorProps>(function Separator(\n { orientation = 'horizontal', style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <RNView\n ref={ref}\n accessible={false}\n style={[\n { backgroundColor: theme.cardShadeColor, flexShrink: 0 },\n orientation === 'vertical'\n ? { width: 1, height: '100%', alignSelf: 'stretch' }\n : { width: '100%', height: 1 },\n style,\n ]}\n {...viewProps}\n />\n );\n});\n","import { Children, forwardRef, type ReactNode } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { Separator } from '@/components/Separator';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type BoxedListVariant = 'default' | 'separate';\n\nexport interface BoxedListProps extends Omit<ViewProps, 'style'> {\n children?: ReactNode;\n /**\n * `\"separate\"` renders each child as its own standalone rounded card\n * instead of a single joined list — mirrors `.boxed-list-separate`.\n * Use when rows are independent items rather than a continuous group.\n */\n variant?: BoxedListVariant;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Rounded bordered list — the most common container pattern in GNOME apps.\n *\n * Mirrors the Adwaita `.boxed-list` style applied to a `GtkListBox`.\n * Separators between rows are inserted automatically. Pair with `ActionRow`\n * or any row-shaped element.\n *\n * Rebuilt with `View` rather than ported from `@gnome-ui/react`'s\n * `<ul>`/`<li>` — RN has no list-semantics elements, so the outer `View`\n * gets `accessibilityRole=\"list\"` (there's no RN equivalent of `listitem`\n * for rows), paired with `accessible` — a plain `View` isn't an\n * accessibility element by default, so without it screen readers (and\n * `@testing-library/react-native`'s `getByRole`) would skip over the role\n * entirely. The web version applies `border-radius` directly to each\n * first/last child via a `:first-child > *` CSS selector, since its `<ul>`\n * has no `overflow: hidden` of its own; RN's `overflow: 'hidden'` on the\n * outer `View` clips every row to the container's rounded corners\n * uniformly, so that per-child trick isn't needed here.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/style-classes.html#boxed-lists-cards\n * @see https://developer.gnome.org/hig/patterns/containers.html\n */\nexport const BoxedList = forwardRef<View, BoxedListProps>(function BoxedList(\n { children, variant = 'default', style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const items = Children.toArray(children).filter(Boolean);\n\n if (variant === 'separate') {\n return (\n <RNView\n ref={ref}\n accessible\n accessibilityRole=\"list\"\n style={[{ gap: theme.space2 }, style]}\n {...viewProps}\n >\n {items.map((child, i) => (\n <RNView\n // biome-ignore lint/suspicious/noArrayIndexKey: children have no natural stable id\n key={i}\n style={{\n backgroundColor: theme.cardBgColor,\n borderWidth: 1,\n borderColor: theme.dividerColor,\n borderRadius: theme.radiusLg,\n overflow: 'hidden',\n }}\n >\n {child}\n </RNView>\n ))}\n </RNView>\n );\n }\n\n return (\n <RNView\n ref={ref}\n accessible\n accessibilityRole=\"list\"\n style={[\n {\n backgroundColor: theme.cardBgColor,\n borderWidth: 1,\n borderColor: theme.dividerColor,\n borderRadius: theme.radiusLg,\n overflow: 'hidden',\n },\n style,\n ]}\n {...viewProps}\n >\n {items.map((child, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: children have no natural stable id\n <RNView key={i}>\n {i > 0 && <Separator testID=\"boxed-list-separator\" />}\n {child}\n </RNView>\n ))}\n </RNView>\n );\n});\n","import type { ReactNode } from 'react';\nimport { useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { View } from 'react-native';\n\nexport interface BreakpointDefinition {\n /** Identifier for this breakpoint, e.g. `\"compact\"` or `\"narrow\"`. */\n name: string;\n /**\n * Container width threshold in dp.\n * This breakpoint becomes active when the container width is ≤ this value.\n */\n maxWidth: number;\n}\n\nexport interface BreakpointBinState {\n /**\n * Name of the currently active breakpoint, or `null` when the container\n * is wider than all defined breakpoints.\n */\n activeBreakpoint: string | null;\n /** Current container width in dp (from `onLayout`). */\n width: number;\n}\n\nexport interface BreakpointBinProps extends Omit<ViewProps, 'children' | 'style'> {\n /**\n * Breakpoint definitions. The active breakpoint is the smallest\n * `maxWidth` ≥ the container's current width — declaration order doesn't\n * matter, they're sorted internally.\n */\n breakpoints: BreakpointDefinition[];\n /**\n * Render prop that receives the current breakpoint state.\n *\n * @example\n * ```tsx\n * <BreakpointBin breakpoints={[{ name: \"compact\", maxWidth: 400 }]}>\n * {({ activeBreakpoint }) =>\n * activeBreakpoint === \"compact\" ? <CompactLayout /> : <WideLayout />\n * }\n * </BreakpointBin>\n * ```\n */\n children: (state: BreakpointBinState) => ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\nfunction evaluate(breakpoints: BreakpointDefinition[], width: number): BreakpointBinState {\n const sorted = [...breakpoints].sort((a, b) => a.maxWidth - b.maxWidth);\n const match = sorted.find((bp) => width <= bp.maxWidth) ?? null;\n\n return { activeBreakpoint: match?.name ?? null, width };\n}\n\n/**\n * Container that fires layout changes when **its own width** crosses\n * defined thresholds — the CSS container-query equivalent of\n * `AdwBreakpointBin` (libadwaita 1.9 / GNOME 50), and the per-*component*\n * sibling of `useBreakpoint` (which watches the window instead).\n *\n * `@gnome-ui/react`'s version watches its own width via `ResizeObserver`;\n * RN has no such API, so this measures the same thing via `onLayout` —\n * fired on mount and again on every subsequent resize of the `View` itself\n * (e.g. the parent's flex layout reflowing, or a device rotation changing\n * how much space this container is given). Composable the same way: the\n * same component can render differently depending on how much space its\n * parent gives it, regardless of the window size — two `BreakpointBin`s\n * side by side with identical `breakpoints` can be in different states.\n *\n * Unlike the web version, there's no `data-breakpoint` attribute to expose\n * for CSS targeting (RN has no attribute selectors) — branch on\n * `activeBreakpoint` directly inside the render prop instead, exactly like\n * every story below already does.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.BreakpointBin.html\n */\nexport const BreakpointBin = ({\n breakpoints,\n children,\n style,\n onLayout,\n ...viewProps\n}: BreakpointBinProps) => {\n const [width, setWidth] = useState(0);\n\n const handleLayout = (event: LayoutChangeEvent) => {\n setWidth(event.nativeEvent.layout.width);\n onLayout?.(event);\n };\n\n return (\n <View onLayout={handleLayout} style={style} {...viewProps}>\n {children(evaluate(breakpoints, width))}\n </View>\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { PressableProps, StyleProp, TextStyle, View, ViewStyle } from 'react-native';\nimport { Pressable, Text } from 'react-native';\n\nimport { useGnomeTheme, useResolvedContrast } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type ButtonVariant = 'default' | 'suggested' | 'destructive' | 'flat' | 'raised';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\nexport type ButtonShape = 'default' | 'pill' | 'circular';\n\nexport interface ButtonProps extends Omit<PressableProps, 'children' | 'style'> {\n /** Visual style of the button. Follows GNOME HIG button types. */\n variant?: ButtonVariant;\n /** Size of the button. */\n size?: ButtonSize;\n /** Shape of the button. \"pill\" for primary actions in open space, \"circular\" for icon-only buttons. */\n shape?: ButtonShape;\n /**\n * Dark semi-transparent overlay style for buttons placed on top of media\n * or images — mirrors `@gnome-ui/react`'s `osd` prop.\n */\n osd?: boolean;\n /**\n * Icon placed before the label. Rendered as-is — RN has no CSS\n * `currentColor` equivalent, so size and color it yourself (`theme.*FgColor`\n * from `useGnomeTheme()` matches the resolved label color for each variant).\n */\n leadingIcon?: ReactNode;\n /** Icon placed after the label. See `leadingIcon` for color/size notes. */\n trailingIcon?: ReactNode;\n /** String children render as a themed `Text` label; other nodes render as-is. */\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n /** Style applied to the label `Text` when `children` is a string. */\n textStyle?: StyleProp<TextStyle>;\n}\n\ninterface SizeMetrics {\n minHeight: number;\n minWidth: number;\n paddingVertical: number;\n paddingHorizontal: number;\n fontSize: number;\n}\n\nfunction getSizeMetrics(theme: GnomeThemeTokens, size: ButtonSize): SizeMetrics {\n switch (size) {\n case 'sm':\n return {\n minHeight: 28,\n minWidth: 64,\n paddingVertical: 4,\n paddingHorizontal: theme.space1,\n fontSize: theme.fontSizeCaption,\n };\n case 'lg':\n return {\n minHeight: 42,\n minWidth: 88,\n paddingVertical: theme.space1,\n paddingHorizontal: theme.space3,\n fontSize: theme.fontSizeTitle4,\n };\n default:\n return {\n minHeight: 34,\n minWidth: 88,\n paddingVertical: theme.space1,\n paddingHorizontal: theme.space2,\n fontSize: theme.fontSizeBody,\n };\n }\n}\n\nfunction getCircularSize(size: ButtonSize): number {\n switch (size) {\n case 'sm':\n return 28;\n case 'lg':\n return 42;\n default:\n return 34;\n }\n}\n\ninterface VariantColors {\n backgroundColor: string;\n borderColor: string;\n color: string;\n}\n\nfunction getVariantColors(\n theme: GnomeThemeTokens,\n variant: ButtonVariant,\n pressed: boolean,\n osd: boolean,\n): VariantColors {\n if (osd) {\n return {\n backgroundColor: pressed ? 'rgba(0, 0, 0, 0.85)' : 'rgba(0, 0, 0, 0.65)',\n borderColor: 'rgba(255, 255, 255, 0.12)',\n color: 'rgba(255, 255, 255, 0.95)',\n };\n }\n\n switch (variant) {\n case 'suggested':\n return {\n backgroundColor: theme.accentBgColor,\n borderColor: 'transparent',\n color: theme.accentFgColor,\n };\n case 'destructive':\n return {\n backgroundColor: theme.destructiveBgColor,\n borderColor: 'transparent',\n color: theme.destructiveFgColor,\n };\n case 'flat':\n return {\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n borderColor: 'transparent',\n color: theme.windowFgColor,\n };\n default:\n return {\n backgroundColor: pressed ? theme.light3 : theme.cardBgColor,\n borderColor: theme.light3,\n color: theme.windowFgColor,\n };\n }\n}\n\n/**\n * Button component following GNOME Human Interface Guidelines.\n *\n * Variants:\n * - `default` — Standard action, flat appearance with subtle border.\n * - `suggested` — Affirmative/primary action (accent color). Use at most once per view.\n * - `destructive` — Dangerous or irreversible action (red). Use sparingly.\n * - `flat` — No border or background; ideal for header bars and toolbars.\n * - `raised` — Explicit raised look inside flat/toolbar contexts.\n *\n * Rebuilt with `Pressable`/`View`/`Text` rather than ported from\n * `@gnome-ui/react`'s DOM-based JSX, but mirrors its prop API.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/buttons.html\n */\nexport const Button = forwardRef<View, ButtonProps>(function Button(\n {\n variant = 'default',\n size = 'md',\n shape = 'default',\n osd = false,\n leadingIcon,\n trailingIcon,\n children,\n style,\n textStyle,\n disabled,\n ...pressableProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const contrast = useResolvedContrast();\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled: !!disabled }}\n style={({ pressed }) => {\n const { backgroundColor, borderColor } = getVariantColors(theme, variant, pressed, osd);\n const solidVariant = !osd && (variant === 'suggested' || variant === 'destructive');\n const circularSize = shape === 'circular' ? getCircularSize(size) : undefined;\n const { minHeight, minWidth, paddingVertical, paddingHorizontal } = getSizeMetrics(\n theme,\n size,\n );\n\n return [\n {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n gap: theme.space1,\n minHeight: circularSize ?? minHeight,\n minWidth: circularSize ?? (shape === 'pill' ? undefined : minWidth),\n width: circularSize,\n height: circularSize,\n paddingVertical: circularSize ? theme.space1 : paddingVertical,\n paddingHorizontal: circularSize\n ? theme.space1\n : shape === 'pill'\n ? theme.space3\n : paddingHorizontal,\n borderRadius: shape === 'default' ? theme.radiusMd : theme.radiusPill,\n borderWidth: variant === 'default' && contrast === 'more' ? 2 : 1,\n borderColor,\n backgroundColor,\n opacity: disabled ? theme.opacityDisabled : pressed && solidVariant ? 0.85 : 1,\n } satisfies ViewStyle,\n style,\n ];\n }}\n {...pressableProps}\n >\n {({ pressed }) => {\n const { fontSize } = getSizeMetrics(theme, size);\n const { color } = getVariantColors(theme, variant, pressed, osd);\n\n return (\n <>\n {leadingIcon}\n {typeof children === 'string' ? (\n <Text\n style={[\n {\n fontFamily: theme.fontFamily,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n fontSize,\n color,\n },\n textStyle,\n ]}\n >\n {children}\n </Text>\n ) : (\n children\n )}\n {trailingIcon}\n </>\n );\n }}\n </Pressable>\n );\n});\n","import type { ReactNode } from 'react';\nimport type { StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { View } from 'react-native';\n\nimport type { TextColor } from '@/components/Text';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type ButtonContentIconPosition = 'start' | 'end';\n\nexport interface ButtonContentProps extends Omit<ViewProps, 'style'> {\n /** Icon placed next to the label. Rendered as-is — size/color it yourself. */\n icon?: ReactNode;\n /** Text label. */\n label: string;\n /**\n * Position of the icon relative to the label.\n * @default 'start'\n */\n iconPosition?: ButtonContentIconPosition;\n /**\n * Label color. RN has no `currentColor` equivalent, so unlike the web\n * version (which inherits the parent button's text color via CSS), this\n * needs to be told explicitly which color to match — e.g. `\"accent\"`\n * when placed inside a `suggested` `Button`. Defaults to `\"default\"`.\n */\n color?: TextColor;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Icon + label layout helper for buttons that contain both an icon and\n * text — the same 6 dp gap / vertically-centered row every button in this\n * package already produces internally. Mirrors `AdwButtonContent`.\n *\n * **Mostly redundant with `Button`'s own `leadingIcon`/`trailingIcon`\n * props** — pass those instead for anything that's actually a `Button`,\n * they already lay the icon and (themed, variant-colored) label out\n * identically and need no separate color prop. Reach for `ButtonContent`\n * when composing icon+label content for something that *isn't* this\n * package's `Button` — a bespoke `Pressable`, a `ButtonRow` title slot, a\n * `Chip`'s custom content — anywhere the exact same Adwaita icon/label\n * spacing convention is wanted outside `Button` itself.\n *\n * @example\n * ```tsx\n * <Pressable onPress={save}>\n * <ButtonContent icon={<Icon icon={DocumentSave} />} label=\"Save\" />\n * </Pressable>\n * ```\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ButtonContent.html\n */\nexport const ButtonContent = ({\n icon,\n label,\n iconPosition = 'start',\n color = 'default',\n style,\n ...viewProps\n}: ButtonContentProps) => {\n const theme = useGnomeTheme();\n\n const iconSlot = icon ? (\n <View accessibilityElementsHidden importantForAccessibility=\"no-hide-descendants\">\n {icon}\n </View>\n ) : null;\n\n return (\n <View\n style={[{ flexDirection: 'row', alignItems: 'center', gap: theme.space1 }, style]}\n {...viewProps}\n >\n {iconPosition === 'start' && iconSlot}\n <Text variant=\"body\" color={color}>\n {label}\n </Text>\n {iconPosition === 'end' && iconSlot}\n </View>\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { PressableProps, StyleProp, TextStyle, View, ViewStyle } from 'react-native';\nimport { Pressable, View as RNView, StyleSheet } from 'react-native';\nimport type { TextColor } from '@/components/Text';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type ButtonRowVariant = 'default' | 'suggested' | 'destructive';\n\nexport interface ButtonRowProps extends Omit<PressableProps, 'children' | 'style'> {\n /** Label displayed centered in the row. */\n title: string;\n /** Visual style that colours the title text. */\n variant?: ButtonRowVariant;\n /** Icon placed at the leading edge. Rendered as-is — size/color it yourself. */\n leading?: ReactNode;\n /** Icon placed at the trailing edge. Rendered as-is — size/color it yourself. */\n trailing?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/** `ButtonRowVariant` maps 1:1 onto `Text`'s own `TextColor` union — no separate color table needed. */\nconst TEXT_COLOR: Record<ButtonRowVariant, TextColor> = {\n default: 'default',\n suggested: 'accent',\n destructive: 'destructive',\n};\n\n/**\n * Full-width activatable row styled as a button, for use inside a\n * `BoxedList`. Mirrors `AdwButtonRow` and `@gnome-ui/react`'s own\n * `ButtonRow` — use when an entire list row should trigger a single action\n * with a centered label; prefer `ActionRow` with `interactive` when the row\n * also needs a title/subtitle layout.\n *\n * Rebuilt with `Pressable` rather than ported from the web `<button>` —\n * same pressed-state-overlay recipe `ActionRow`/`Card` already established\n * (`theme.activeOverlay` stands in for the web's `:hover`/`:active`\n * `background-color` transition, since RN has no hover state). The title's\n * color reuses `Text`'s own `TextColor` union (`\"accent\"`/`\"destructive\"`\n * already resolve to the exact same `theme.accentColor`/`destructiveColor`\n * tokens the source CSS's `suggested`/`destructive` variants reference) —\n * no separate color-resolution logic needed. Unlike the web version, the\n * variant color does *not* propagate to `leading`/`trailing` — RN's `Icon`\n * has no `currentColor` equivalent and only accepts a fixed named-swatch\n * palette, the same dropped nicety `Chip` already accepted for its own\n * leading/remove icons. The title gets `flex: 1` + `textAlign: 'center'`\n * (the source CSS's own `.title { flex: 1; text-align: center }`) rather\n * than centering the row via `justifyContent` — that keeps the label\n * centered in the row's full width even when only one of `leading`/\n * `trailing` is present, matching the web behavior exactly.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ButtonRow.html\n */\nexport const ButtonRow = forwardRef<View, ButtonRowProps>(function ButtonRow(\n { title, variant = 'default', leading, trailing, style, disabled, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled: !!disabled }}\n accessibilityLabel={title}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: theme.space2,\n paddingHorizontal: theme.space4,\n minHeight: 52,\n width: '100%',\n opacity: disabled ? theme.opacityDisabled : 1,\n },\n style,\n ]}\n {...pressableProps}\n >\n {({ pressed }) => (\n <>\n {leading && <RNView style={{ flexShrink: 0 }}>{leading}</RNView>}\n\n <Text\n variant=\"body\"\n color={TEXT_COLOR[variant]}\n style={{\n flex: 1,\n textAlign: 'center',\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n\n {trailing && <RNView style={{ flexShrink: 0 }}>{trailing}</RNView>}\n\n {pressed && !disabled ? (\n <RNView\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { backgroundColor: theme.activeOverlay }]}\n />\n ) : null}\n </>\n )}\n </Pressable>\n );\n});\n","import { Close, DialogInformation, DialogWarning } from '@gnome-ui/icons';\nimport type { ReactNode } from 'react';\nimport type { StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { Pressable, View } from 'react-native';\n\nimport { Icon, type IconColor } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type CalloutVariant = 'info' | 'warning' | 'tip';\n\nexport interface CalloutProps extends Omit<ViewProps, 'style'> {\n /**\n * Visual emphasis level.\n * - `info` (default) — neutral, accent-colored. General contextual notes.\n * - `warning` — yellow. Recoverable problems or things to double-check.\n * - `tip` — green. Optional suggestions or shortcuts.\n */\n variant?: CalloutVariant;\n /** The message content. */\n children: ReactNode;\n /** When true a dismiss (×) button is shown at the trailing edge. */\n dismissible?: boolean;\n /** Called when the user presses the dismiss button. */\n onDismiss?: () => void;\n style?: StyleProp<ViewStyle>;\n}\n\nconst VARIANT_ICON = {\n info: DialogInformation,\n warning: DialogWarning,\n tip: DialogInformation,\n};\n\n/**\n * `Icon`'s `color` prop only accepts a fixed named-swatch palette (no\n * `currentColor`/arbitrary-hex equivalent in RN) — but `blue3`/`green4`\n * resolve to the exact same hex as `accentBgColor`/`successBgColor` in\n * every theme variant this package ships, so `info`/`tip` are an exact\n * match, not an approximation. `warning`'s `yellow4` is deliberately\n * darker than `warningBgColor` itself, the same contrast intent as the\n * source CSS's own `color-mix(in srgb, warning 70%, black)` icon tint.\n */\nconst VARIANT_ICON_COLOR: Record<CalloutVariant, IconColor> = {\n info: 'blue',\n warning: 'yellow',\n tip: 'green',\n};\n\n/**\n * The source CSS's `color-mix(in srgb, <color> 10%/15%, transparent)`\n * tinted backgrounds (`info`/`tip` are 10%, `warning` is 15%) have no RN\n * equivalent — resolved as an 8-digit `#RRGGBBAA` alpha suffix on the\n * variant's own hex token instead, the same substitution `Chip`'s\n * selected-state tint and `Blockquote`'s tinted backgrounds already\n * established. `0x1A` ≈ 10%, `0x26` ≈ 15%.\n */\nconst VARIANT_TINT: Record<CalloutVariant, string> = {\n info: '1a',\n warning: '26',\n tip: '1a',\n};\n\n/**\n * Inline, dismissible admonition box for contextual help text within forms\n * and cards. Mirrors `@gnome-ui/react`'s `Callout`.\n *\n * Unlike `Banner` (a persistent, edge-to-edge strip at the top of a view)\n * and `Toast` (a temporary notification), `Callout` is a contained, tinted\n * box meant to sit inline alongside the content it annotates.\n *\n * `role=\"note\"` ports 1:1 from RN's newer web-aligned `Role` union (the\n * same one `Dialog`/`Tooltip` already use) — `accessible` is set alongside\n * it, per the `BoxedList` lesson that a bare `View` isn't an accessibility\n * element by default. The leading icon is decorative, hidden from\n * assistive tech the same way `Blockquote`'s own icon is.\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/\n */\nexport const Callout = ({\n variant = 'info',\n children,\n dismissible = false,\n onDismiss,\n style,\n ...viewProps\n}: CalloutProps) => {\n const theme = useGnomeTheme();\n\n const variantColor = {\n info: theme.accentBgColor,\n warning: theme.warningBgColor,\n tip: theme.successBgColor,\n }[variant];\n\n return (\n <View\n role=\"note\"\n accessible\n style={[\n {\n flexDirection: 'row',\n alignItems: 'flex-start',\n gap: theme.space2,\n padding: theme.space2,\n paddingHorizontal: theme.space3,\n borderRadius: theme.radiusMd,\n borderStartWidth: 3,\n borderStartColor: variantColor,\n backgroundColor: `${variantColor}${VARIANT_TINT[variant]}`,\n },\n style,\n ]}\n {...viewProps}\n >\n <View style={{ marginTop: 2 }} accessibilityElementsHidden importantForAccessibility=\"no\">\n <Icon icon={VARIANT_ICON[variant]} size=\"md\" color={VARIANT_ICON_COLOR[variant]} />\n </View>\n\n <Text variant=\"body\" style={{ flex: 1, minWidth: 0 }}>\n {children}\n </Text>\n\n {dismissible && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel=\"Dismiss\"\n hitSlop={4}\n onPress={onDismiss}\n style={({ pressed }) => [\n {\n flexShrink: 0,\n marginTop: -2,\n padding: 4,\n borderRadius: theme.radiusMd,\n opacity: pressed ? 1 : 0.6,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n },\n ]}\n >\n <View pointerEvents=\"none\">\n <Icon icon={Close} size=\"sm\" />\n </View>\n </Pressable>\n )}\n </View>\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { PressableProps, StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { Pressable, View as RNView, StyleSheet } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type CardPadding = 'none' | 'sm' | 'md' | 'lg';\n\nexport interface CardProps extends Omit<PressableProps, 'style' | 'children'> {\n /**\n * When true the card becomes pressable (Adwaita `.activatable`).\n * Renders as `Pressable` with `accessibilityRole=\"button\"`; a plain\n * `View` otherwise. `onPress`/`onLongPress`/etc. only take effect when\n * `interactive` is true — RN's `View` has no touch handling of its own,\n * unlike a DOM `<div>`'s `onClick`.\n */\n interactive?: boolean;\n /** Internal spacing. Defaults to `\"md\"`. */\n padding?: CardPadding;\n style?: StyleProp<ViewStyle>;\n children?: ReactNode;\n}\n\nfunction getPadding(theme: GnomeThemeTokens, padding: CardPadding): number {\n switch (padding) {\n case 'none':\n return 0;\n case 'sm':\n return theme.space2;\n case 'lg':\n return theme.space5;\n default:\n return theme.space4;\n }\n}\n\n/**\n * Card component following the GNOME HIG and Adwaita `.card` style class.\n *\n * Use for grouping related content on an elevated surface. Set\n * `interactive` for clickable cards (e.g. grid item, settings shortcut).\n *\n * Rebuilt with `Pressable`/`View` rather than ported from `@gnome-ui/react`'s\n * DOM-based `<button>`/`<div>` + `as` prop — RN has no polymorphic-element\n * equivalent, so `as` is dropped. The web version's `:hover`/`:active`\n * `color-mix()` darken/lighten is reproduced with an absolutely-positioned\n * overlay `View` tinted by `theme.activeOverlay` while pressed, since RN\n * has no `color-mix()` to blend two flat colors directly.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/style-classes.html\n * @see https://developer.gnome.org/hig/patterns/containers.html\n */\nexport const Card = forwardRef<View, CardProps>(function Card(\n { interactive = false, padding = 'md', style, children, disabled, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n const baseStyle: ViewStyle = {\n backgroundColor: theme.cardBgColor,\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusLg,\n overflow: 'hidden',\n padding: getPadding(theme, padding),\n };\n\n if (!interactive) {\n return (\n <RNView ref={ref} style={[baseStyle, style]} {...(pressableProps as ViewProps)}>\n {children}\n </RNView>\n );\n }\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled: !!disabled }}\n style={[\n baseStyle,\n { position: 'relative', opacity: disabled ? theme.opacityDisabled : 1 },\n style,\n ]}\n {...pressableProps}\n >\n {({ pressed }) => (\n <>\n {children}\n {pressed && !disabled ? (\n <RNView\n pointerEvents=\"none\"\n style={[\n StyleSheet.absoluteFill,\n { backgroundColor: theme.activeOverlay, borderRadius: theme.radiusLg },\n ]}\n />\n ) : null}\n </>\n )}\n </Pressable>\n );\n});\n","import { forwardRef, useEffect, useRef } from 'react';\nimport type { PressableProps, StyleProp, View, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable } from 'react-native';\n\nimport { useGnomeTheme, useResolvedColorScheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface CheckboxProps extends Omit<PressableProps, 'children' | 'style' | 'onPress'> {\n /** Whether the checkbox is checked. */\n value: boolean;\n /**\n * Indeterminate state — shown when only some items in a group are\n * checked. Takes visual precedence over `value`.\n */\n indeterminate?: boolean;\n /** Called with the next value when the checkbox is pressed. */\n onValueChange?: (value: boolean) => void;\n /** Accessible label. Required when no visible label is associated. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\nconst SIZE = 18;\n\n/**\n * Checkbox for multi-selection, following the GNOME HIG and Adwaita style.\n *\n * Supports three states: unchecked, checked, and indeterminate (mixed).\n * Use `indeterminate` for \"select all\" controls when only some items are\n * selected.\n *\n * Rebuilt on `Pressable`/`Animated.View` rather than ported from\n * `@gnome-ui/react`'s `<input type=\"checkbox\">`: RN has no `indeterminate`\n * DOM property to set imperatively (the web version's whole reason for a\n * ref + effect), so it's just a rendering branch here — `indeterminate`\n * draws a short bar, otherwise a checkmark, both fading in via the same\n * `Animated.Value` used for the border/background transition. The\n * checkmark itself is a `✓` glyph in a plain (non-themed) `Text` rather\n * than the web version's `clip-path` polygon — this package has no SVG\n * dependency to draw one exactly, and `Link`'s external-link indicator\n * already established a Unicode glyph as this codebase's fallback for a\n * small decorative mark with no icon library available.\n *\n * `value`/`onValueChange` mirror the same convention as `Switch`, RN's own\n * toggle-control shape, rather than the web version's `checked`/`onChange`.\n * The idle border color is another `Switch`-style case where the source\n * CSS hardcodes a palette swatch per color scheme instead of a token that\n * already resolves per theme, so it branches on `useResolvedColorScheme()`\n * the same way.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/checkboxes.html\n */\nexport const Checkbox = forwardRef<View, CheckboxProps>(function Checkbox(\n { value, indeterminate = false, onValueChange, disabled, style, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const colorScheme = useResolvedColorScheme();\n const contrast = useResolvedContrast();\n const marked = value || indeterminate;\n const progress = useRef(new Animated.Value(marked ? 1 : 0)).current;\n const isInitialMount = useRef(true);\n\n useEffect(() => {\n // The initial `Animated.Value` above already matches `marked` — skip\n // animating into a state the checkbox is already rendering, so it\n // never plays a transition before the user has touched it.\n if (isInitialMount.current) {\n isInitialMount.current = false;\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: marked ? 1 : 0,\n duration: theme.durationFast,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: false,\n }).start();\n }, [marked, progress, theme.durationFast, theme.easingDefault]);\n\n const idleBorderColor =\n contrast === 'more' ? theme.windowFgColor : colorScheme === 'dark' ? theme.dark1 : theme.light4;\n\n const borderColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [idleBorderColor, theme.accentBgColor],\n });\n const backgroundColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.cardBgColor, theme.accentBgColor],\n });\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"checkbox\"\n accessibilityState={{ checked: indeterminate ? 'mixed' : value, disabled: !!disabled }}\n onPress={() => onValueChange?.(!value)}\n style={[{ opacity: disabled ? theme.opacityDisabled : 1 }, style]}\n {...pressableProps}\n >\n <Animated.View\n style={{\n width: SIZE,\n height: SIZE,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusSm,\n borderWidth: contrast === 'more' ? 2 : 1.5,\n borderColor,\n backgroundColor,\n }}\n >\n {indeterminate ? (\n <Animated.View\n style={{\n width: 10,\n height: 2,\n borderRadius: 1,\n backgroundColor: '#fff',\n opacity: progress,\n }}\n />\n ) : (\n <Animated.Text\n style={{\n opacity: progress,\n color: '#fff',\n fontSize: 13,\n fontWeight: 'bold',\n lineHeight: 13,\n }}\n >\n {'✓'}\n </Animated.Text>\n )}\n </Animated.View>\n </Pressable>\n );\n});\n","import { forwardRef, type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { PressableProps, StyleProp, TextStyle, View, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable, View as RNView, StyleSheet } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useResolvedColorScheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface CheckRowProps extends Omit<PressableProps, 'children' | 'style' | 'onPress'> {\n /** Primary label. */\n title: string;\n /** Secondary line below the title. */\n subtitle?: string;\n /** Icon or image placed at the leading edge, after the checkbox. */\n leading?: ReactNode;\n /** Controlled checked state. */\n checked?: boolean;\n /** Initial checked state when uncontrolled. Defaults to `false`. */\n defaultChecked?: boolean;\n /** Called with the next value when the row is pressed. */\n onCheckedChange?: (checked: boolean) => void;\n style?: StyleProp<ViewStyle>;\n}\n\nconst CHECKBOX_SIZE = 20;\n\n/**\n * Activatable row with an integrated checkbox, mirroring\n * `@gnome-ui/react`'s `CheckRow`. The entire row is a single pressable —\n * pressing anywhere toggles the checked state. Use inside a `BoxedList`\n * when a user must select or deselect individual items in a list; prefer a\n * `SwitchRow` (once ported) for a single on/off setting.\n *\n * Supports both controlled (`checked`) and uncontrolled (`defaultChecked`)\n * modes, the same `isControlled`/internal-state-fallback shape already\n * established by `Expander`/`ComboRow`/`Popover`.\n *\n * The checkbox visual reuses `Checkbox`'s exact border/background\n * `Animated.Value` interpolation and checkmark-fade-in recipe — but as a\n * plain, non-interactive `View` rather than importing the real `Checkbox`\n * component, since `Checkbox` is itself a `Pressable` and nesting one\n * touchable inside another (the row's own `Pressable`) would create two\n * overlapping tap targets. `aria-labelledby` (pointing the web button's\n * `role=\"checkbox\"` at the title/subtitle content) has no RN equivalent —\n * `accessibilityLabel` combining title and subtitle is the substitution,\n * the same \"no relationship attribute\" gap `Tooltip`'s dropped\n * `aria-describedby` already established. The title reads `theme.cardFgColor`\n * directly (not `Text`'s own `\"default\"`, which resolves to\n * `windowFgColor`) to match the source CSS's `--gnome-card-fg-color`\n * ambient row color exactly — the two tokens happen to share the same\n * value in every theme this package ships, but the source CSS's intent is\n * specifically \"card foreground,\" so the port keeps that distinction.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.CheckButton.html\n */\nexport const CheckRow = forwardRef<View, CheckRowProps>(function CheckRow(\n {\n title,\n subtitle,\n leading,\n checked: controlledChecked,\n defaultChecked = false,\n onCheckedChange,\n disabled,\n style,\n ...pressableProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const colorScheme = useResolvedColorScheme();\n const contrast = useResolvedContrast();\n\n const isControlled = controlledChecked !== undefined;\n const [uncontrolledChecked, setUncontrolledChecked] = useState(defaultChecked);\n const checked = isControlled ? controlledChecked : uncontrolledChecked;\n\n const progress = useRef(new Animated.Value(checked ? 1 : 0)).current;\n const isInitialMount = useRef(true);\n\n useEffect(() => {\n if (isInitialMount.current) {\n isInitialMount.current = false;\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: checked ? 1 : 0,\n duration: theme.durationFast,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: false,\n }).start();\n }, [checked, progress, theme.durationFast, theme.easingDefault]);\n\n const idleBorderColor =\n contrast === 'more' ? theme.windowFgColor : colorScheme === 'dark' ? theme.dark1 : theme.light4;\n\n const borderColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [idleBorderColor, theme.accentBgColor],\n });\n const backgroundColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: ['transparent', theme.accentBgColor],\n });\n\n const handlePress = () => {\n const next = !checked;\n\n if (!isControlled) {\n setUncontrolledChecked(next);\n }\n\n onCheckedChange?.(next);\n };\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"checkbox\"\n accessibilityState={{ checked, disabled: !!disabled }}\n accessibilityLabel={subtitle ? `${title}, ${subtitle}` : title}\n onPress={handlePress}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: theme.space2,\n paddingHorizontal: theme.space4,\n minHeight: 52,\n width: '100%',\n opacity: disabled ? theme.opacityDisabled : 1,\n },\n style,\n ]}\n {...pressableProps}\n >\n {({ pressed }) => (\n <>\n <Animated.View\n style={{\n width: CHECKBOX_SIZE,\n height: CHECKBOX_SIZE,\n flexShrink: 0,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusSm,\n borderWidth: contrast === 'more' ? 2 : 1.5,\n borderColor,\n backgroundColor,\n }}\n >\n <Animated.Text\n style={{\n opacity: progress,\n color: '#fff',\n fontSize: 13,\n fontWeight: 'bold',\n lineHeight: 13,\n }}\n >\n {'✓'}\n </Animated.Text>\n </Animated.View>\n\n {leading && <RNView style={{ flexShrink: 0 }}>{leading}</RNView>}\n\n <RNView style={{ flex: 1, gap: 2 }}>\n <Text\n variant=\"body\"\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n color: theme.cardFgColor,\n fontWeight: String(theme.fontWeightNormal) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n {subtitle && (\n <Text variant=\"caption\" color=\"dim\" numberOfLines={1} ellipsizeMode=\"tail\">\n {subtitle}\n </Text>\n )}\n </RNView>\n\n {pressed && !disabled ? (\n <RNView\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { backgroundColor: theme.activeOverlay }]}\n />\n ) : null}\n </>\n )}\n </Pressable>\n );\n});\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport { Close } from '@gnome-ui/icons';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Pressable, StyleSheet, View } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface ChipProps {\n /** Text label displayed inside the chip. */\n label: string;\n /** Leading icon from `@gnome-ui/icons`. */\n icon?: IconDefinition;\n /**\n * When provided, renders a remove (×) button and calls this handler.\n * The chip root becomes a plain `View`; only the remove button is\n * interactive.\n */\n onRemove?: () => void;\n /**\n * When true the chip renders as a toggle button.\n * Use `selected` + `onToggle` to control its state.\n */\n selectable?: boolean;\n /** Active/selected state. Only relevant when `selectable` is true. */\n selected?: boolean;\n /**\n * Called when a selectable chip is pressed.\n * Only relevant when `selectable` is true.\n */\n onToggle?: () => void;\n /** Disabled state — applies to both selectable chips and the remove button. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Compact pill-shaped label for tags, filters, and selection states.\n * Mirrors `@gnome-ui/react`'s `Chip`.\n *\n * Three usage modes:\n * - **Static** — just a visual label (no `onRemove`, no `selectable`).\n * - **Removable** — add `onRemove` to show a × button.\n * - **Selectable** — add `selectable` + `selected` + `onToggle` for toggle\n * behavior. Same `isInteractive = selectable && !onRemove` precedence as\n * the web version: passing both `selectable` and `onRemove` renders the\n * remove button, not a toggle.\n *\n * Pair with `WrapBox` for multi-chip layouts.\n *\n * Rebuilt with `Pressable`/`View`/`Text` rather than ported from\n * `@gnome-ui/react`'s `<button>`/`<span>`: the selected background/border\n * tint (`color-mix(in srgb, accent 15%/50%, transparent)`) has no RN\n * equivalent, resolved to a literal 8-digit `#RRGGBBAA` hex instead — the\n * same `Highlight` precedent, since `accentBgColor` is always a plain\n * 6-digit hex. The `:hover`/`:active` background transitions collapse into\n * a single pressed-state overlay tinted by `theme.activeOverlay` (the same\n * `ActionRow`/`Card` recipe), since touch has no hover. The leading icon\n * and remove (×) icon don't recolor to match the selected accent text\n * (`color: inherit` on the web) — RN's `Icon` has no `currentColor`\n * equivalent and only accepts a fixed named-swatch palette, none of which\n * tracks the app's configurable accent color, so both icons stay in the\n * default foreground color; a decorative nicety dropped, not a behavior\n * gap. `accessibilityRole=\"checkbox\"` on the selectable form ports 1:1\n * (the same `Checkbox` precedent).\n *\n * @see https://developer.gnome.org/hig/patterns/selection.html\n */\nexport const Chip = ({\n label,\n icon,\n onRemove,\n selectable = false,\n selected = false,\n onToggle,\n disabled = false,\n style,\n testID,\n}: ChipProps) => {\n const theme = useGnomeTheme();\n const isInteractive = selectable && !onRemove;\n\n const baseStyle: ViewStyle = {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space1,\n paddingVertical: 3,\n paddingHorizontal: theme.space2,\n borderRadius: theme.radiusPill,\n borderWidth: 1,\n borderColor: selected ? `${theme.accentBgColor}80` : theme.light3,\n backgroundColor: selected ? `${theme.accentBgColor}26` : theme.cardBgColor,\n overflow: 'hidden',\n };\n\n const labelStyle: TextStyle = {\n color: selected ? theme.accentColor : theme.windowFgColor,\n fontWeight: String(\n selected ? theme.fontWeightSemibold : theme.fontWeightNormal,\n ) as TextStyle['fontWeight'],\n };\n\n const content = (\n <>\n {icon && (\n <View style={{ marginStart: -2 }}>\n <Icon icon={icon} size=\"sm\" />\n </View>\n )}\n\n <Text variant=\"caption\" style={labelStyle}>\n {label}\n </Text>\n\n {onRemove && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={`Remove ${label}`}\n disabled={disabled}\n onPress={onRemove}\n hitSlop={8}\n style={{ marginEnd: -4, opacity: disabled ? theme.opacityDisabled : 1 }}\n >\n <Icon icon={Close} size=\"sm\" />\n </Pressable>\n )}\n </>\n );\n\n if (isInteractive) {\n return (\n <Pressable\n testID={testID}\n disabled={disabled}\n accessibilityRole=\"checkbox\"\n accessibilityState={{ checked: selected, disabled }}\n accessibilityLabel={label}\n onPress={onToggle}\n style={[baseStyle, { opacity: disabled ? theme.opacityDisabled : 1 }, style]}\n >\n {({ pressed }) => (\n <>\n {content}\n {pressed && !disabled && (\n <View\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { backgroundColor: theme.activeOverlay }]}\n />\n )}\n </>\n )}\n </Pressable>\n );\n }\n\n return (\n <View\n testID={testID}\n style={[baseStyle, { opacity: disabled ? theme.opacityDisabled : 1 }, style]}\n >\n {content}\n </View>\n );\n};\n","import type { ReactNode } from 'react';\nimport { forwardRef } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nexport interface ClampProps extends Omit<ViewProps, 'style'> {\n /**\n * Maximum content width in density-independent pixels.\n * The container shrinks freely below this value.\n * Defaults to `600` — the Adwaita recommended narrow-content width.\n */\n maximumSize?: number;\n /**\n * Fractional width (`0`–`1`) of the available space to use while that\n * space is narrower than `maximumSize` — useful for keeping a\n * comfortable margin on medium-width screens. Defaults to `1` (always\n * fill the width).\n */\n tighteningThreshold?: number;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/** Keeps a nonsensical `tighteningThreshold` from collapsing the container. */\nconst clampFraction = (value: number) => Math.min(Math.max(value, 0), 1);\n\n/**\n * Constrains its children to a maximum width while letting them shrink\n * freely, mirroring the Adwaita `AdwClamp` widget and `@gnome-ui/react`'s\n * own `Clamp`.\n *\n * Use it on settings pages and forms so content never becomes too wide to\n * read comfortably on a tablet or a landscape phone, while still filling\n * the available width on a narrow one.\n *\n * The web version's `margin-inline: auto` centering becomes\n * `alignSelf: 'center'` here rather than `marginHorizontal: 'auto'` —\n * RN auto-margin support was left unverified for this Yoga version when\n * `Drawer` needed the same trick, so this follows `Drawer`'s resolution of\n * using flex alignment instead. The one consequence is that `Clamp`\n * expects a column-direction parent (RN's default): `alignSelf` acts on\n * the cross axis, so inside a `flexDirection: 'row'` parent it would\n * centre vertically instead. Wrap it in a plain `View` there.\n *\n * `tighteningThreshold` is a real percentage width here, unlike in\n * `@gnome-ui/react` where the prop is declared and documented but never\n * reaches the DOM — implementing it exactly as that package documents it\n * (a fraction of the available width, still capped by `maximumSize`)\n * costs nothing on RN and avoids shipping a dead prop.\n *\n * Adds no padding of its own — wrap the content in its own padded\n * container as needed.\n *\n * @example\n * // Settings page — content never wider than 600 dp\n * <Clamp>\n * <BoxedList>…</BoxedList>\n * </Clamp>\n *\n * @example\n * // Leave a 10% margin while the screen is narrower than 480 dp\n * <Clamp maximumSize={480} tighteningThreshold={0.9}>\n * <Text>…</Text>\n * </Clamp>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Clamp.html\n */\nexport const Clamp = forwardRef<View, ClampProps>(function Clamp(\n { maximumSize = 600, tighteningThreshold = 1, children, style, ...viewProps },\n ref,\n) {\n const widthFraction = clampFraction(tighteningThreshold);\n\n return (\n <RNView\n ref={ref}\n style={[\n {\n width: `${widthFraction * 100}%`,\n maxWidth: maximumSize,\n alignSelf: 'center',\n },\n style,\n ]}\n {...viewProps}\n >\n {children}\n </RNView>\n );\n});\n","/**\n * Positioning helpers for `CoachMark`. Kept pure and measurement-free so\n * they can be unit-tested directly; the component feeds them real rects\n * (from `measureInWindow` instead of the web version's\n * `getBoundingClientRect`, but the shape is identical either way).\n *\n * Duplicated verbatim from `@gnome-ui/react` rather than imported\n * cross-package — the same `fileType.ts`/`Icon.tsx` precedent for\n * DOM-free logic that isn't worth a shared package for one file's worth\n * of code.\n *\n * The algorithm mirrors `Popover`'s viewport-aware flip: try the preferred\n * side, then its opposite, then the rest, picking the first that fits;\n * clamp the cross-axis and shift the arrow when nothing fits perfectly.\n */\n\nexport type CoachMarkPlacement = 'top' | 'bottom' | 'left' | 'right';\n\nexport interface Rect {\n top: number;\n left: number;\n width: number;\n height: number;\n}\n\nexport interface BubblePosition {\n top: number;\n left: number;\n placement: CoachMarkPlacement;\n /** Arrow centre offset in px from the near edge of the bubble. */\n arrowOffset: number;\n}\n\nconst GAP = 12;\nconst MARGIN = 12;\nconst ARROW_HALF = 7;\n\n/** Grow a rect outward by `pad` on every side, e.g. the spotlight cutout. */\nexport const padRect = (rect: Rect, pad: number): Rect => ({\n top: rect.top - pad,\n left: rect.left - pad,\n width: rect.width + pad * 2,\n height: rect.height + pad * 2,\n});\n\n/**\n * Place the callout bubble around `target`, flipping to stay inside a\n * `viewport` (width × height). `bubble` is the measured bubble size.\n */\nexport const computeBubblePosition = (\n target: Rect,\n bubble: { width: number; height: number },\n viewport: { width: number; height: number },\n preferred: CoachMarkPlacement,\n): BubblePosition => {\n const targetRight = target.left + target.width;\n const targetBottom = target.top + target.height;\n const targetCX = target.left + target.width / 2;\n const targetCY = target.top + target.height / 2;\n\n const opposite: Record<CoachMarkPlacement, CoachMarkPlacement> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n };\n const candidates = [\n ...new Set<CoachMarkPlacement>([\n preferred,\n opposite[preferred],\n 'bottom',\n 'top',\n 'right',\n 'left',\n ]),\n ];\n\n const raw = (p: CoachMarkPlacement) => {\n switch (p) {\n case 'bottom':\n return { top: targetBottom + GAP, left: targetCX - bubble.width / 2 };\n case 'top':\n return { top: target.top - bubble.height - GAP, left: targetCX - bubble.width / 2 };\n case 'left':\n return { top: targetCY - bubble.height / 2, left: target.left - bubble.width - GAP };\n default:\n return { top: targetCY - bubble.height / 2, left: targetRight + GAP };\n }\n };\n\n // First pass: a side that fits both axes cleanly.\n for (const p of candidates) {\n const { top, left } = raw(p);\n const fitsH = left >= MARGIN && left + bubble.width <= viewport.width - MARGIN;\n const fitsV = top >= MARGIN && top + bubble.height <= viewport.height - MARGIN;\n\n if (fitsH && fitsV) {\n const arrowOffset = p === 'top' || p === 'bottom' ? targetCX - left : targetCY - top;\n\n return { top, left, placement: p, arrowOffset };\n }\n }\n\n // Second pass: primary axis fits; clamp the cross axis, keep the arrow on target.\n for (const p of candidates) {\n const { top, left } = raw(p);\n\n if (p === 'top' || p === 'bottom') {\n const fitsV = top >= MARGIN && top + bubble.height <= viewport.height - MARGIN;\n\n if (!fitsV) {\n continue;\n }\n\n const clampedLeft = Math.max(MARGIN, Math.min(left, viewport.width - bubble.width - MARGIN));\n const offset = targetCX - clampedLeft;\n\n return {\n top,\n left: clampedLeft,\n placement: p,\n arrowOffset: Math.max(ARROW_HALF + 4, Math.min(offset, bubble.width - ARROW_HALF - 4)),\n };\n }\n\n const fitsH = left >= MARGIN && left + bubble.width <= viewport.width - MARGIN;\n\n if (!fitsH) {\n continue;\n }\n\n const clampedTop = Math.max(MARGIN, Math.min(top, viewport.height - bubble.height - MARGIN));\n const offset = targetCY - clampedTop;\n\n return {\n top: clampedTop,\n left,\n placement: p,\n arrowOffset: Math.max(ARROW_HALF + 4, Math.min(offset, bubble.height - ARROW_HALF - 4)),\n };\n }\n\n // Fallback: clamp a bottom placement into the viewport.\n const fbTop = targetBottom + GAP;\n const fbLeft = targetCX - bubble.width / 2;\n const clampedTop = Math.max(MARGIN, Math.min(fbTop, viewport.height - bubble.height - MARGIN));\n const clampedLeft = Math.max(MARGIN, Math.min(fbLeft, viewport.width - bubble.width - MARGIN));\n const offset = targetCX - clampedLeft;\n\n return {\n top: clampedTop,\n left: clampedLeft,\n placement: 'bottom',\n arrowOffset: Math.max(ARROW_HALF + 4, Math.min(offset, bubble.width - ARROW_HALF - 4)),\n };\n};\n","import { type ReactNode, type RefObject, useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport {\n Animated,\n BackHandler,\n Dimensions,\n Modal,\n Pressable,\n StyleSheet,\n View,\n} from 'react-native';\n\nimport { Button } from '@/components/Button';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nimport {\n type BubblePosition,\n type CoachMarkPlacement,\n computeBubblePosition,\n padRect,\n type Rect,\n} from './coachMarkUtils';\n\nexport interface CoachMarkAction {\n label: string;\n onPress: () => void;\n}\n\nexport interface CoachMarkProps {\n /** Whether the coach mark is shown. */\n open: boolean;\n /** The element to highlight and anchor to. */\n targetRef: RefObject<View | null>;\n /** Heading text. */\n title?: ReactNode;\n /** Body copy explaining the highlighted element. */\n description?: ReactNode;\n /** Preferred side of the target for the bubble. Flips to stay on-screen. Defaults to `'bottom'`. */\n placement?: CoachMarkPlacement;\n /** Dim the rest of the screen and cut a spotlight around the target. Defaults to `true`. */\n spotlight?: boolean;\n /** Extra px around the target inside the spotlight cutout. Defaults to `8`. */\n spotlightPadding?: number;\n /** Close when the dimmed backdrop is pressed. Defaults to `false` (guided). */\n dismissOnBackdrop?: boolean;\n /** 1-based index of this step within a tour, for the \"X of N\" counter. */\n step?: number;\n /** Total number of steps in the tour. */\n stepCount?: number;\n /** Primary (suggested) action, e.g. Next / Got it. */\n primaryAction?: CoachMarkAction;\n /** Secondary (flat) action, e.g. Back. */\n secondaryAction?: CoachMarkAction;\n /** Called when the Android back button is pressed, or the backdrop is dismissed. */\n onDismiss?: () => void;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst ARROW = 7;\nconst SCRIM_COLOR = 'rgba(0, 0, 0, 0.55)';\n\n/**\n * A single onboarding coach mark: it spotlights a target element and\n * anchors a callout bubble (title, description, step counter, actions)\n * beside it, guiding a user to one feature. Compose several with\n * `CoachMarkTour`, or drive one directly with `open`. Mirrors\n * `@gnome-ui/react`'s `CoachMark` — not a GNOME HIG widget, a pragmatic\n * feature-discovery pattern.\n *\n * Rebuilt on RN's own `Modal` rather than the web version's DOM `Portal` —\n * no `container` prop, the same `Overlay`/`Drawer` precedent for \"no RN\n * portal-target concept.\" Positions with the same two-pass viewport-aware\n * flip as the web version (`coachMarkUtils.ts`, duplicated verbatim — pure\n * math, no DOM), resolved from `targetRef.current?.measureInWindow(...)`\n * and the bubble's own `onLayout` size, combined once both arrive — the\n * same \"resolve two independent async things, then combine\" shape\n * `Tooltip`/`Popover`/`Dropdown` already established. No focus trap (no\n * DOM `Tab` concept in RN) and no scroll/resize re-positioning (RN has no\n * global scroll event, the same `Tooltip` precedent for a transient\n * floating element).\n *\n * **The spotlight cutout has no CSS `box-shadow: 0 0 0 100vmax` port** —\n * that trick paints an opaque scrim everywhere *except* inside a rounded\n * rect by using a huge spread shadow, which RN's `shadow*` props (real OS\n * shadows, not a scrim generator) can't reproduce. Rebuilt as four plain\n * `View` bands (top/bottom/left/right of the padded target rect) filling\n * the screen minus a rectangular hole, plus a separate rounded\n * `accentColor`-bordered ring `View` drawn on top at the target rect —\n * visually equivalent (dims everything but the target, with an accent\n * ring around it), just assembled from ordinary rects instead of one\n * masked shape. The whole overlay (bands + ring) sits inside a single\n * full-screen `Pressable`, so — matching the web version exactly — a tap\n * anywhere within it (including visually \"in the hole,\" since the web\n * version's backdrop is one full-bleed element with the spotlight only\n * painted on top, `pointer-events: none`) triggers `dismissOnBackdrop`,\n * never the real content underneath.\n *\n * **`dismissOnBackdrop` has no effect when `spotlight` is `false`** —\n * ported faithfully, not fixed: the web source only renders a backdrop\n * element at all when `spotlight` is true, so with `spotlight={false}`\n * there is nothing to press to dismiss via backdrop either way, on both\n * platforms.\n *\n * The arrow reuses `Popover`/`Tooltip`'s transparent-border-triangle trick\n * rather than the web CSS's rotated-45°-square, the same established RN\n * substitution for every floating-bubble arrow in this package — offset\n * along the bubble's edge by `arrowOffset` (from `computeBubblePosition`,\n * unlike `Tooltip`/`Popover`'s simpler always-centered arrow).\n *\n * `role=\"dialog\"` + `accessibilityViewIsModal` port 1:1 from `Dialog`'s\n * own precedent. `BackHandler`'s `hardwareBackPress` is the Android analog\n * of the web version's Escape listener.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Window.html\n */\nexport const CoachMark = ({\n open,\n targetRef,\n title,\n description,\n placement = 'bottom',\n spotlight = true,\n spotlightPadding = 8,\n dismissOnBackdrop = false,\n step,\n stepCount,\n primaryAction,\n secondaryAction,\n onDismiss,\n style,\n testID,\n}: CoachMarkProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const [targetRect, setTargetRect] = useState<Rect | null>(null);\n const [bubbleSize, setBubbleSize] = useState<{ width: number; height: number } | null>(null);\n const [pos, setPos] = useState<BubblePosition | null>(null);\n\n const progress = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n if (!open) {\n setTargetRect(null);\n setBubbleSize(null);\n setPos(null);\n progress.setValue(0);\n\n return;\n }\n\n // Measuring immediately (or even one `requestAnimationFrame` later)\n // catches a stale rect when the target sits below sibling content\n // whose own size isn't final yet on the first commit (e.g. a\n // multi-line description `Text` above it) — confirmed on-device: the\n // reported `y` matched exactly where the target would sit *without*\n // that sibling's height, off by precisely that amount. A single rAF\n // still landed before the follow-up layout pass that accounts for it;\n // a short real delay reliably lands after. The web version's\n // `useLayoutEffect` measurement has no equivalent race, since DOM\n // layout is synchronous by the time it runs — RN's `measureInWindow`\n // is a native-bridge call with no such guarantee, and Yoga's own\n // text-driven relayout can still be in flight after one JS frame.\n const timer = setTimeout(() => {\n targetRef.current?.measureInWindow((x, y, width, height) => {\n setTargetRect({ top: y, left: x, width, height });\n });\n }, 50);\n\n return () => clearTimeout(timer);\n // `targetRef` is a real dependency, not just a stable-ref formality —\n // `CoachMarkTour` passes a *different* ref object per step, and this\n // effect must re-measure when the target changes even though `open`\n // stays `true` across the whole tour. Missing it here was a real bug:\n // advancing steps left the spotlight/bubble stuck on the first step's\n // target (found via the app, not just this file's own tests, which\n // only ever exercise a single step's fixed ref).\n }, [open, targetRef, progress]);\n\n useEffect(() => {\n if (!targetRect || !bubbleSize) {\n return;\n }\n\n const { width: vw, height: vh } = Dimensions.get('window');\n\n setPos(computeBubblePosition(targetRect, bubbleSize, { width: vw, height: vh }, placement));\n }, [targetRect, bubbleSize, placement]);\n\n useEffect(() => {\n if (!pos) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationFast,\n useNativeDriver: true,\n }).start();\n }, [pos, reducedMotion, progress, theme.durationFast]);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n const subscription = BackHandler.addEventListener('hardwareBackPress', () => {\n onDismiss?.();\n\n return true;\n });\n\n return () => subscription.remove();\n }, [open, onDismiss]);\n\n const handleBubbleLayout = (e: LayoutChangeEvent) => {\n const { width, height } = e.nativeEvent.layout;\n\n setBubbleSize({ width, height });\n };\n\n if (!open) {\n return null;\n }\n\n const spotRect = targetRect ? padRect(targetRect, spotlightPadding) : null;\n const { width: vw } = Dimensions.get('window');\n\n const arrowBase = {\n position: 'absolute' as const,\n width: 0,\n height: 0,\n borderWidth: ARROW,\n borderColor: 'transparent',\n };\n\n const arrowStyle = (() => {\n if (!pos) {\n return null;\n }\n\n switch (pos.placement) {\n case 'top':\n return {\n ...arrowBase,\n bottom: -ARROW * 2,\n left: pos.arrowOffset - ARROW,\n borderTopColor: theme.cardBgColor,\n };\n case 'bottom':\n return {\n ...arrowBase,\n top: -ARROW * 2,\n left: pos.arrowOffset - ARROW,\n borderBottomColor: theme.cardBgColor,\n };\n case 'left':\n return {\n ...arrowBase,\n right: -ARROW * 2,\n top: pos.arrowOffset - ARROW,\n borderLeftColor: theme.cardBgColor,\n };\n default:\n return {\n ...arrowBase,\n left: -ARROW * 2,\n top: pos.arrowOffset - ARROW,\n borderRightColor: theme.cardBgColor,\n };\n }\n })();\n\n return (\n <Modal\n visible={open}\n transparent\n animationType=\"none\"\n statusBarTranslucent\n onRequestClose={onDismiss}\n >\n <Animated.View pointerEvents=\"box-none\" style={StyleSheet.absoluteFill}>\n {spotlight && (\n <Pressable\n testID={testID}\n onPress={dismissOnBackdrop ? onDismiss : undefined}\n accessible={false}\n style={StyleSheet.absoluteFill}\n >\n <Animated.View\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { opacity: progress }]}\n >\n {spotRect && (\n <>\n <View\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n height: Math.max(0, spotRect.top),\n backgroundColor: SCRIM_COLOR,\n }}\n />\n <View\n style={{\n position: 'absolute',\n top: Math.max(0, spotRect.top + spotRect.height),\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: SCRIM_COLOR,\n }}\n />\n <View\n style={{\n position: 'absolute',\n top: Math.max(0, spotRect.top),\n left: 0,\n width: Math.max(0, spotRect.left),\n height: spotRect.height,\n backgroundColor: SCRIM_COLOR,\n }}\n />\n <View\n style={{\n position: 'absolute',\n top: Math.max(0, spotRect.top),\n left: Math.max(0, spotRect.left + spotRect.width),\n right: 0,\n height: spotRect.height,\n backgroundColor: SCRIM_COLOR,\n }}\n />\n <View\n pointerEvents=\"none\"\n style={{\n position: 'absolute',\n top: spotRect.top,\n left: spotRect.left,\n width: spotRect.width,\n height: spotRect.height,\n borderRadius: theme.radiusMd,\n borderWidth: 2,\n borderColor: theme.accentColor,\n }}\n />\n </>\n )}\n </Animated.View>\n </Pressable>\n )}\n\n <Animated.View\n onLayout={handleBubbleLayout}\n accessible\n role=\"dialog\"\n accessibilityViewIsModal\n style={[\n {\n position: 'absolute',\n maxWidth: Math.min(320, vw - 24),\n padding: theme.space3,\n backgroundColor: theme.cardBgColor,\n borderRadius: theme.radiusLg,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 4 },\n shadowOpacity: 0.2,\n shadowRadius: 16,\n elevation: 8,\n },\n pos\n ? {\n top: pos.top,\n left: pos.left,\n opacity: progress,\n transform: [\n { scale: progress.interpolate({ inputRange: [0, 1], outputRange: [0.96, 1] }) },\n ],\n }\n : { top: -9999, left: -9999, opacity: 0 },\n style,\n ]}\n >\n {arrowStyle && <View style={arrowStyle} />}\n\n {step !== undefined && stepCount !== undefined && (\n <Text\n variant=\"caption\"\n style={{\n marginBottom: theme.space1,\n color: theme.accentColor,\n fontWeight: String(theme.fontWeightBold) as TextStyle['fontWeight'],\n }}\n >\n {`${step} of ${stepCount}`}\n </Text>\n )}\n\n {title && (\n <Text\n variant=\"title-4\"\n style={{\n marginBottom: theme.space1,\n fontWeight: String(theme.fontWeightBold) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n )}\n\n {description && (\n <Text variant=\"body\" color=\"dim\">\n {description}\n </Text>\n )}\n\n {(primaryAction || secondaryAction) && (\n <View\n style={{\n flexDirection: 'row',\n justifyContent: 'flex-end',\n gap: theme.space2,\n marginTop: theme.space3,\n }}\n >\n {secondaryAction && (\n <Button variant=\"flat\" size=\"sm\" onPress={secondaryAction.onPress}>\n {secondaryAction.label}\n </Button>\n )}\n {primaryAction && (\n <Button variant=\"suggested\" size=\"sm\" onPress={primaryAction.onPress}>\n {primaryAction.label}\n </Button>\n )}\n </View>\n )}\n </Animated.View>\n </Animated.View>\n </Modal>\n );\n};\n","import { type ReactNode, type RefObject, useEffect, useState } from 'react';\nimport type { View } from 'react-native';\n\nimport { CoachMark } from './CoachMark';\nimport type { CoachMarkPlacement } from './coachMarkUtils';\n\nexport interface CoachMarkStep {\n /** The element this step highlights. */\n targetRef: RefObject<View | null>;\n /** Heading for the step. */\n title?: ReactNode;\n /** Body copy for the step. */\n description?: ReactNode;\n /** Preferred bubble side for this step. Defaults to the tour's placement. */\n placement?: CoachMarkPlacement;\n}\n\nexport interface CoachMarkTourProps {\n /** Ordered steps of the tour. */\n steps: CoachMarkStep[];\n /** Whether the tour is running. */\n open: boolean;\n /** Step to start on when the tour opens. Defaults to `0`. */\n startIndex?: number;\n /** Called after the primary action on the final step. */\n onFinish?: () => void;\n /** Called when the user skips (Skip button or Android back) before finishing. */\n onSkip?: () => void;\n /** Called with the new index whenever the active step changes. */\n onStepChange?: (index: number) => void;\n /** Default preferred bubble side for steps that don't set their own. Defaults to `'bottom'`. */\n placement?: CoachMarkPlacement;\n /** Spotlight the target. Defaults to `true`. */\n spotlight?: boolean;\n /** Close the tour when the dimmed backdrop is pressed. Defaults to `false`. */\n dismissOnBackdrop?: boolean;\n /** Override the action-button labels (for i18n). */\n labels?: Partial<{ next: string; back: string; skip: string; finish: string }>;\n testID?: string;\n}\n\nconst DEFAULT_LABELS = { next: 'Next', back: 'Back', skip: 'Skip', finish: 'Done' };\n\n/**\n * Sequential onboarding tour built from `CoachMark` steps. Renders the\n * mark for the active step, wires Next/Back/Skip/Done and the \"X of N\"\n * counter, and advances through `steps` until finished or skipped.\n * Mirrors `@gnome-ui/react`'s `CoachMarkTour` verbatim — pure state\n * orchestration on top of `CoachMark`, nothing platform-specific to\n * change.\n *\n * Uncontrolled step index: the tour tracks its own position and resets to\n * `startIndex` each time it opens. Drive visibility with `open`; react to\n * completion with `onFinish`/`onSkip`.\n */\nexport const CoachMarkTour = ({\n steps,\n open,\n startIndex = 0,\n onFinish,\n onSkip,\n onStepChange,\n placement = 'bottom',\n spotlight = true,\n dismissOnBackdrop = false,\n labels,\n testID,\n}: CoachMarkTourProps) => {\n const [index, setIndex] = useState(startIndex);\n\n // Restart from the top each time the tour opens.\n useEffect(() => {\n if (open) {\n setIndex(startIndex);\n }\n }, [open, startIndex]);\n\n if (!open || steps.length === 0) {\n return null;\n }\n\n const clampedIndex = Math.min(index, steps.length - 1);\n const step = steps[clampedIndex];\n const isFirst = clampedIndex === 0;\n const isLast = clampedIndex === steps.length - 1;\n const text = { ...DEFAULT_LABELS, ...labels };\n\n const goTo = (next: number) => {\n setIndex(next);\n onStepChange?.(next);\n };\n\n const primaryAction = {\n label: isLast ? text.finish : text.next,\n onPress: () => (isLast ? onFinish?.() : goTo(clampedIndex + 1)),\n };\n\n // Later steps offer Back; the first step offers Skip in the same slot.\n const secondaryAction = isFirst\n ? { label: text.skip, onPress: () => onSkip?.() }\n : { label: text.back, onPress: () => goTo(clampedIndex - 1) };\n\n return (\n <CoachMark\n open={open}\n targetRef={step.targetRef}\n title={step.title}\n description={step.description}\n placement={step.placement ?? placement}\n spotlight={spotlight}\n dismissOnBackdrop={dismissOnBackdrop}\n step={clampedIndex + 1}\n stepCount={steps.length}\n primaryAction={primaryAction}\n secondaryAction={secondaryAction}\n onDismiss={() => onSkip?.()}\n testID={testID}\n />\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { FlexStyle, StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nexport type WrapBoxJustify =\n | 'start'\n | 'center'\n | 'end'\n | 'space-between'\n | 'space-around'\n | 'space-evenly';\nexport type WrapBoxAlign = 'start' | 'center' | 'end' | 'stretch';\n\nexport interface WrapBoxProps extends Omit<ViewProps, 'style'> {\n /** Gap between children on the same line, in dp. Default: `6`. */\n childSpacing?: number;\n /** Gap between lines, in dp. Defaults to `childSpacing`. */\n lineSpacing?: number;\n /** Horizontal distribution of children within each line. Default: `\"start\"`. */\n justify?: WrapBoxJustify;\n /** Cross-axis alignment of children within each line. Default: `\"center\"`. */\n align?: WrapBoxAlign;\n /** When true children wrap in the reverse direction (bottom to top). */\n wrapReverse?: boolean;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Yoga's own edge names — the same `start`/`end` → `flex-start`/`flex-end`\n * translation `Box` establishes, duplicated here rather than shared: these\n * are two small pure lookups, and `WrapBoxAlign` is deliberately a narrower\n * union than `BoxAlign` (no `baseline`), mirroring the web package's own\n * two separate types.\n */\nconst ALIGN: Record<WrapBoxAlign, FlexStyle['alignItems']> = {\n start: 'flex-start',\n center: 'center',\n end: 'flex-end',\n stretch: 'stretch',\n};\n\nconst JUSTIFY: Record<WrapBoxJustify, FlexStyle['justifyContent']> = {\n start: 'flex-start',\n center: 'center',\n end: 'flex-end',\n 'space-between': 'space-between',\n 'space-around': 'space-around',\n 'space-evenly': 'space-evenly',\n};\n\n/**\n * Flexible wrapping layout container — children flow horizontally and wrap\n * to new lines when they don't fit, like words in a paragraph, without\n * locking them into a grid. Mirrors `AdwWrapBox` (libadwaita 1.7 / GNOME\n * 48) and `@gnome-ui/react`'s own `WrapBox`.\n *\n * Pair with `Chip` for tag lists and filter rows, or use standalone for any\n * collection of variable-width items.\n *\n * The web version ships its values as CSS custom properties consumed by a\n * stylesheet (`--wrapbox-gap`, `--wrapbox-justify`, …) because a CSS module\n * can't take runtime values any other way; RN has no such indirection, so\n * they're written straight onto the style object here. Everything else is a\n * direct translation: `flex-flow: row wrap` becomes\n * `flexDirection: 'row'` + `flexWrap`, and the shorthand\n * `gap: <row> <column>` splits into RN's separate `rowGap`/`columnGap`\n * (the single `gap` property would set both, which is exactly what this\n * component must be able to avoid).\n *\n * As in `Box`, `childSpacing`/`lineSpacing` are numbers only — RN's gap\n * properties take dp, not CSS strings — and `align`/`justify` keep the\n * web's bare `start`/`end` keywords in the public API while mapping\n * internally onto Yoga's `flex-start`/`flex-end`.\n *\n * `alignContent: 'stretch'` is set explicitly even though neither package\n * exposes an `alignContent` prop — **CSS defaults it to `stretch`, Yoga\n * defaults it to `flex-start`**, so without this line `align=\"stretch\"`\n * silently does nothing on RN whenever the children have no cross-size of\n * their own: the line collapses to zero height before `alignItems` ever\n * gets to stretch anything into it. Caught on-device (the stretch row of\n * the example app's demo screen rendered empty); this restores the web\n * version's behaviour exactly. It's a no-op in the ordinary case where the\n * container hugs its content rather than having a fixed height.\n *\n * @example\n * // Tag list\n * <WrapBox childSpacing={6}>\n * {tags.map((tag) => <Chip key={tag}>{tag}</Chip>)}\n * </WrapBox>\n *\n * @example\n * // Looser gap between lines than between items\n * <WrapBox childSpacing={6} lineSpacing={12} justify=\"center\">\n * {filters.map((filter) => <Chip key={filter}>{filter}</Chip>)}\n * </WrapBox>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.WrapBox.html\n */\nexport const WrapBox = forwardRef<View, WrapBoxProps>(function WrapBox(\n {\n childSpacing = 6,\n lineSpacing,\n justify = 'start',\n align = 'center',\n wrapReverse = false,\n children,\n style,\n ...viewProps\n },\n ref,\n) {\n return (\n <RNView\n ref={ref}\n style={[\n {\n flexDirection: 'row',\n flexWrap: wrapReverse ? 'wrap-reverse' : 'wrap',\n columnGap: childSpacing,\n rowGap: lineSpacing ?? childSpacing,\n justifyContent: JUSTIFY[justify],\n alignItems: ALIGN[align],\n alignContent: 'stretch',\n },\n style,\n ]}\n {...viewProps}\n >\n {children}\n </RNView>\n );\n});\n","import { forwardRef } from 'react';\nimport type { PressableProps, StyleProp, View, ViewStyle } from 'react-native';\nimport { Pressable, View as RNView } from 'react-native';\nimport Svg, { Path } from 'react-native-svg';\n\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\n\nexport type ColorSwatchSize = 'sm' | 'md' | 'lg';\n\n/** `.swatch-sm` / `-md` / `-lg`. */\n// eslint-disable-next-line react-refresh/only-export-components\nexport const SWATCH_DIAMETER: Record<ColorSwatchSize, number> = {\n sm: 22,\n md: 30,\n lg: 38,\n};\n\n/**\n * Width of the selected state's outer ring. Reserved as padding on every\n * swatch, selected or not, so selecting one never reflows the row — the web\n * gets this for free because `box-shadow` rings don't take up space.\n */\nexport const RING_WIDTH = 2;\n\nexport interface ColorSwatchProps\n extends Omit<PressableProps, 'children' | 'style' | 'onPress' | 'disabled'> {\n /** Color value displayed as the swatch background. */\n color: string;\n /** Whether this swatch is the currently selected color. */\n selected?: boolean;\n /** Swatch diameter. Defaults to `\"md\"`. */\n size?: ColorSwatchSize;\n /** Called with `color` when the swatch is pressed. */\n onSelect?: (color: string) => void;\n /** Accessible name. Defaults to the color value. */\n accessibilityLabel?: string;\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Single circular color swatch. Usable standalone or composed inside\n * `ColorPicker`, and shows a white checkmark when `selected`.\n *\n * The web's three `box-shadow` rings collapse into real box-model pieces,\n * since RN gives a `View` exactly one border: the resting\n * `inset 0 0 0 1px` hairline becomes `borderWidth: 1`, the selected state's\n * `inset 0 0 0 2px rgb(255 255 255 / .9)` becomes a 2 dp white border, and\n * the outer `0 0 0 2px var(--swatch-color)` becomes a wrapper painted in the\n * swatch color. That wrapper is always rendered with the same 2 dp padding\n * and only changes color, because a `box-shadow` ring costs no layout space\n * on the web while a real padded wrapper does — reserving it unconditionally\n * is what keeps the row from reflowing as the selection moves.\n *\n * The checkmark is hand-drawn with `react-native-svg` rather than taken from\n * `@gnome-ui/icons`, mirroring the web version, which also hand-draws it:\n * it's a stroked path, and `Icon`'s palette has no white to give it anyway.\n * `filter: drop-shadow(...)` has no RN counterpart, so the path is drawn\n * twice — a translucent black copy offset 1 dp down, then the white one on\n * top — which is what that filter renders and is why it exists: without it\n * the check disappears on a yellow swatch.\n *\n * `:hover { transform: scale(1.12) }` drops with hover; the selected\n * `scale(1.05)` ports as-is.\n */\nexport const ColorSwatch = forwardRef<View, ColorSwatchProps>(function ColorSwatch(\n {\n color,\n selected = false,\n size = 'md',\n onSelect,\n accessibilityLabel,\n disabled = false,\n style,\n ...pressableProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const isDark = useResolvedColorScheme() === 'dark';\n const diameter = SWATCH_DIAMETER[size];\n const hairline = isDark ? 'rgba(255, 255, 255, 0.12)' : 'rgba(0, 0, 0, 0.15)';\n const checkSize = Math.round(diameter * 0.55);\n\n return (\n <RNView\n style={[\n {\n padding: RING_WIDTH,\n borderRadius: theme.radiusPill,\n backgroundColor: selected ? color : 'transparent',\n alignSelf: 'flex-start',\n opacity: disabled ? theme.opacityDisabled : 1,\n },\n // Kept as its own entry so an unselected swatch carries no\n // `transform` key at all, rather than one set to `undefined` —\n // RN's transform validator is unforgiving about what reaches it.\n selected ? { transform: [{ scale: 1.05 }] } : null,\n style,\n ]}\n >\n <Pressable\n ref={ref}\n disabled={disabled}\n onPress={() => onSelect?.(color)}\n accessibilityRole=\"radio\"\n accessibilityState={{ checked: selected, disabled }}\n accessibilityLabel={accessibilityLabel ?? color}\n style={{\n width: diameter,\n height: diameter,\n borderRadius: theme.radiusPill,\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: color,\n borderWidth: selected ? 2 : 1,\n borderColor: selected ? 'rgba(255, 255, 255, 0.9)' : hairline,\n }}\n {...pressableProps}\n >\n {selected ? (\n <Svg width={checkSize} height={checkSize} viewBox=\"0 0 16 16\">\n <Path\n d=\"M3 8l3.5 3.5L13 5\"\n stroke=\"rgba(0, 0, 0, 0.35)\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n fill=\"none\"\n translateY={1}\n />\n <Path\n d=\"M3 8l3.5 3.5L13 5\"\n stroke=\"white\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n fill=\"none\"\n />\n </Svg>\n ) : null}\n </Pressable>\n </RNView>\n );\n});\n","import { Add } from '@gnome-ui/icons';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Pressable, View as RNView } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { WrapBox } from '@/components/WrapBox';\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\n\nimport { ColorSwatch, type ColorSwatchSize, RING_WIDTH, SWATCH_DIAMETER } from './ColorSwatch';\n\nexport interface ColorPickerColor {\n /** Color value (hex recommended). */\n value: string;\n /** Human-readable name, used as the swatch's accessible label. */\n label?: string;\n}\n\n/** Default Adwaita-named palette (matches the `Avatar` color set). */\n// eslint-disable-next-line react-refresh/only-export-components\nexport const GNOME_PALETTE: ColorPickerColor[] = [\n { value: '#3584e4', label: 'Blue' },\n { value: '#2ec27e', label: 'Green' },\n { value: '#f6d32d', label: 'Yellow' },\n { value: '#ff7800', label: 'Orange' },\n { value: '#e01b24', label: 'Red' },\n { value: '#9141ac', label: 'Purple' },\n { value: '#986a44', label: 'Brown' },\n { value: '#2190a4', label: 'Teal' },\n { value: '#5e5c64', label: 'Slate' },\n];\n\nexport interface ColorPickerProps {\n /** Currently selected color value. */\n value?: string;\n /** Called when the user selects a color. */\n onChange?: (value: string) => void;\n /** Palette to display. Defaults to `GNOME_PALETTE` (the 9 Adwaita colors). */\n colors?: ColorPickerColor[];\n /**\n * Show a \"+\" button after the palette, and render any `value` outside the\n * palette as its own selected swatch. Pressing either calls\n * `onRequestCustom` — RN has no `<input type=\"color\">`, so the picker UI\n * itself is the consuming app's to provide. Defaults to `false`.\n */\n allowCustom?: boolean;\n /**\n * Called when the \"+\" button (or the current custom swatch) is pressed.\n * Open your own color picker here and feed the result back through\n * `value`/`onChange`.\n */\n onRequestCustom?: () => void;\n /** Swatch size. Defaults to `\"md\"`. */\n size?: ColorSwatchSize;\n /** Accessible name for the group. Defaults to `\"Color\"`. */\n accessibilityLabel?: string;\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Color palette picker following the Adwaita `GtkColorButton` + swatch\n * pattern, mirroring `@gnome-ui/react`'s own `ColorPicker`. Renders a\n * wrapping row of circular `ColorSwatch` items backed by a radio group.\n *\n * **`allowCustom` is the one prop that changes meaning.** On the web it\n * wires a hidden `<input type=\"color\">` and the browser supplies the whole\n * picker UI; RN has no such control, and building an HSV picker would be a\n * component in its own right rather than a detail of this one. So the prop\n * keeps its *visible* behaviour — the \"+\" button, and a `value` outside the\n * palette shown as its own selected swatch — while the press is handed to\n * `onRequestCustom` for the app to answer with whatever picker it has. Round\n * trips through `value`/`onChange` exactly as before.\n *\n * The container is a `WrapBox` (`display: flex; flex-wrap: wrap; gap: 8`\n * with nothing else in `.picker`), and the \"+\" button's `border: 1.5px\n * dashed` ports directly — `borderStyle: 'dashed'` is one of the few CSS\n * border tricks RN does support. Its plus glyph comes from `@gnome-ui/icons`\n * rather than the web's hand-drawn path, since `Add` is the same mark and\n * already resolves to the foreground color `.customButton` asks for.\n *\n * As in `ToggleGroup`, the group takes `accessibilityRole=\"radiogroup\"` but\n * deliberately not `accessible`, which on iOS would collapse the swatches\n * into one unreachable element. Arrow-key navigation and the roving\n * `tabIndex` drop, as everywhere else here.\n *\n * @example\n * const [color, setColor] = useState('#3584e4');\n *\n * <ColorPicker value={color} onChange={setColor} />\n *\n * @example\n * // Custom colors, with your own picker behind the \"+\"\n * <ColorPicker\n * value={color}\n * onChange={setColor}\n * allowCustom\n * onRequestCustom={() => setPickerOpen(true)}\n * />\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ColorButton.html\n */\nexport const ColorPicker = ({\n value,\n onChange,\n colors = GNOME_PALETTE,\n allowCustom = false,\n onRequestCustom,\n size = 'md',\n accessibilityLabel = 'Color',\n disabled = false,\n style,\n testID,\n}: ColorPickerProps) => {\n const theme = useGnomeTheme();\n const isDark = useResolvedColorScheme() === 'dark';\n const diameter = SWATCH_DIAMETER[size];\n\n const isCustom = value !== undefined && !colors.some((color) => color.value === value);\n\n return (\n <WrapBox\n testID={testID}\n accessibilityRole=\"radiogroup\"\n accessibilityLabel={accessibilityLabel}\n childSpacing={8}\n align=\"center\"\n style={style}\n >\n {colors.map((color) => (\n <ColorSwatch\n key={color.value}\n color={color.value}\n size={size}\n selected={color.value === value}\n disabled={disabled}\n onSelect={onChange}\n accessibilityLabel={color.label ?? color.value}\n />\n ))}\n\n {allowCustom ? (\n <>\n {isCustom && value ? (\n <ColorSwatch\n color={value}\n size={size}\n selected\n disabled={disabled}\n onSelect={onRequestCustom}\n accessibilityLabel=\"Custom color\"\n />\n ) : null}\n\n {/* Same reserved ring padding as a swatch, so the row stays aligned. */}\n <RNView style={{ padding: RING_WIDTH, alignSelf: 'flex-start' }}>\n <Pressable\n disabled={disabled}\n onPress={onRequestCustom}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Choose custom color\"\n accessibilityState={{ disabled }}\n style={({ pressed }) => ({\n width: diameter,\n height: diameter,\n borderRadius: theme.radiusPill,\n alignItems: 'center',\n justifyContent: 'center',\n borderWidth: 1.5,\n borderStyle: 'dashed',\n borderColor: isDark ? 'rgba(255, 255, 255, 0.25)' : theme.cardShadeColor,\n backgroundColor: pressed && !disabled ? theme.activeOverlay : theme.cardBgColor,\n opacity: disabled ? theme.opacityDisabled : 1,\n })}\n >\n <Icon\n icon={Add}\n width={Math.round(diameter * 0.5)}\n height={Math.round(diameter * 0.5)}\n />\n </Pressable>\n </RNView>\n </>\n ) : null}\n </WrapBox>\n );\n};\n","import { Check, PanDown } from '@gnome-ui/icons';\nimport { useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Animated, Dimensions, Modal, Pressable, ScrollView, View } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport interface DropdownOption<V extends string = string> {\n /** The value submitted / returned on selection. */\n value: V;\n /** Display label shown in the list and trigger. */\n label: string;\n /** Optional descriptive text shown below the label. */\n description?: string;\n /** Whether the option is selectable. */\n disabled?: boolean;\n}\n\nexport interface DropdownProps<V extends string = string> {\n /** The list of selectable options. */\n options: DropdownOption<V>[];\n /** The currently selected value. */\n value?: V;\n /** Called when the user selects an option. */\n onChange?: (value: V) => void;\n /** Placeholder shown when no option is selected. */\n placeholder?: string;\n /** Accessible label for the control. */\n accessibilityLabel?: string;\n /** Disables the entire control. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\ninterface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface Position {\n top: number;\n left: number;\n width: number;\n flipUp: boolean;\n}\n\nconst GAP = 4;\nconst MARGIN = 8;\nconst MAX_LIST_HEIGHT = 280;\n\nfunction computePosition(trigger: Rect, panelHeight: number): Position {\n const { height: vh } = Dimensions.get('window');\n const spaceBelow = vh - (trigger.y + trigger.height);\n const flipUp = spaceBelow < panelHeight && trigger.y > panelHeight;\n const top = flipUp ? trigger.y - panelHeight - GAP : trigger.y + trigger.height + GAP;\n\n return {\n top: Math.max(MARGIN, Math.min(top, vh - panelHeight - MARGIN)),\n left: trigger.x,\n width: trigger.width,\n flipUp,\n };\n}\n\n/**\n * Expandable option list following the Adwaita combo-row / drop-down\n * pattern, mirroring `@gnome-ui/react`'s `Dropdown`.\n *\n * Built on RN's own `Modal` (transparent) — the same portal-substitute\n * `Dialog`/`Tooltip` already use — with a full-screen backdrop `Pressable`\n * (not `pointerEvents=\"box-none\"` like `Tooltip`'s: unlike a tooltip, this\n * panel is meant to catch and close on an outside tap, the RN analog of the\n * web version's document-level \"click outside\" listener). `open` flips\n * synchronously on trigger press; the trigger's on-screen rect\n * (`measureInWindow`) and the panel's own rendered height (`onLayout`) each\n * resolve independently into state, combined by a separate effect into the\n * final `top`/`left`/`width`/`flipUp` — the same two-independent-\n * async-measurements pattern `Tooltip` established (and for the same\n * reason: gating `open` itself on the native measurement callback would\n * make the component untestable in this package's Jest environment, where\n * `measureInWindow` never calls back).\n *\n * Keyboard navigation (↑/↓ roving highlight, Home/End, type-ahead) has no\n * port — RN's touch-first model has no keyboard focus to drive it, the\n * same reasoning that already dropped `TabBar`'s roving-tabindex arrow\n * keys. Selection is by direct tap only.\n *\n * `role=\"combobox\"` on the trigger ports 1:1. RN's `Role` union has no\n * `\"listbox\"` value (unlike `\"option\"`, which does exist) — the panel uses\n * `role=\"list\"` instead, the same closest-available substitution `BoxedList`\n * already established for a plain list container.\n */\nexport const Dropdown = <V extends string = string>({\n options,\n value,\n onChange,\n placeholder = 'Select an option',\n accessibilityLabel,\n disabled,\n style,\n testID,\n}: DropdownProps<V>) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const [open, setOpen] = useState(false);\n const [triggerRect, setTriggerRect] = useState<Rect | null>(null);\n const [panelHeight, setPanelHeight] = useState<number | null>(null);\n const [pos, setPos] = useState<Position | null>(null);\n\n const triggerRef = useRef<View>(null);\n const progress = useRef(new Animated.Value(0)).current;\n\n const selected = options.find((o) => o.value === value);\n\n const openList = () => {\n if (disabled) {\n return;\n }\n\n setOpen(true);\n };\n\n const closeList = () => setOpen(false);\n\n const selectOption = (opt: DropdownOption<V>) => {\n if (opt.disabled) {\n return;\n }\n\n onChange?.(opt.value);\n closeList();\n };\n\n useEffect(() => {\n if (!open) {\n setTriggerRect(null);\n setPanelHeight(null);\n setPos(null);\n\n return;\n }\n\n triggerRef.current?.measureInWindow((x, y, width, height) => {\n setTriggerRect({ x, y, width, height });\n });\n }, [open]);\n\n useEffect(() => {\n if (triggerRect === null || panelHeight === null) {\n return;\n }\n\n setPos(computePosition(triggerRect, panelHeight));\n }, [triggerRect, panelHeight]);\n\n useEffect(() => {\n if (!pos) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n progress.setValue(0);\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationFast,\n useNativeDriver: true,\n }).start();\n }, [pos, reducedMotion, progress, theme.durationFast]);\n\n const handlePanelLayout = (e: LayoutChangeEvent) => {\n setPanelHeight(Math.min(e.nativeEvent.layout.height, MAX_LIST_HEIGHT));\n };\n\n return (\n <View style={style}>\n <Pressable\n ref={triggerRef}\n testID={testID}\n role=\"combobox\"\n accessibilityLabel={accessibilityLabel}\n accessibilityState={{ expanded: open, disabled: !!disabled }}\n disabled={disabled}\n onPress={() => (open ? closeList() : openList())}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: theme.space1,\n minHeight: 36,\n minWidth: 160,\n paddingVertical: 7,\n paddingHorizontal: theme.space2,\n backgroundColor: pressed ? theme.activeOverlay : theme.cardBgColor,\n borderWidth: 1,\n borderColor: open ? theme.accentColor : theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n opacity: disabled ? theme.opacityDisabled : 1,\n })}\n >\n <Text\n numberOfLines={1}\n style={{\n flex: 1,\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n opacity: selected ? 1 : theme.opacityDim,\n }}\n >\n {selected?.label ?? placeholder}\n </Text>\n <Icon\n icon={PanDown}\n size=\"md\"\n color=\"default\"\n style={{ transform: [{ rotate: open ? '180deg' : '0deg' }] }}\n />\n </Pressable>\n\n <Modal visible={open} transparent animationType=\"none\" statusBarTranslucent>\n <Pressable\n onPress={closeList}\n accessible={false}\n style={{ flex: 1 }}\n testID={testID ? `${testID}-backdrop` : undefined}\n >\n <Animated.View\n onLayout={handlePanelLayout}\n role=\"list\"\n accessible\n style={[\n {\n position: 'absolute',\n maxHeight: MAX_LIST_HEIGHT,\n backgroundColor: theme.popoverBgColor,\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n paddingVertical: 4,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 4 },\n shadowOpacity: 0.15,\n shadowRadius: 12,\n elevation: 6,\n },\n pos\n ? { top: pos.top, left: pos.left, width: pos.width, opacity: progress }\n : { top: -9999, left: -9999, width: 200, opacity: 0 },\n ]}\n >\n <ScrollView bounces={false}>\n {options.map((opt) => {\n const isSelected = opt.value === value;\n\n return (\n <Pressable\n key={opt.value}\n role=\"option\"\n accessibilityState={{ selected: isSelected, disabled: !!opt.disabled }}\n disabled={opt.disabled}\n onPress={() => selectOption(opt)}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n minHeight: 40,\n paddingVertical: 8,\n paddingHorizontal: theme.space2,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n opacity: opt.disabled ? theme.opacityDisabled : 1,\n })}\n >\n <View style={{ flex: 1, gap: 1 }}>\n <Text\n numberOfLines={1}\n style={{\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n fontWeight: isSelected\n ? (String(theme.fontWeightSemibold) as TextStyle['fontWeight'])\n : undefined,\n }}\n >\n {opt.label}\n </Text>\n {opt.description && (\n <Text\n numberOfLines={1}\n style={{\n fontSize: theme.fontSizeCaption,\n color: theme.windowFgColor,\n opacity: theme.opacityDim,\n }}\n >\n {opt.description}\n </Text>\n )}\n </View>\n {isSelected && <Icon icon={Check} size=\"md\" color=\"blue\" />}\n </Pressable>\n );\n })}\n </ScrollView>\n </Animated.View>\n </Pressable>\n </Modal>\n </View>\n );\n};\n","import { type ReactNode, useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\n\nimport { ActionRow } from '@/components/ActionRow';\nimport { Dropdown, type DropdownOption } from '@/components/Dropdown';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\n/** Same shape as `Dropdown`'s own option — re-exported so `ComboRow` reads self-contained. */\nexport type ComboRowOption<V extends string = string> = DropdownOption<V>;\n\nexport interface ComboRowProps<V extends string = string> {\n /** Primary label. */\n title: string;\n /** Secondary line below the title. */\n subtitle?: string;\n /** Icon or image placed at the leading edge. */\n leading?: ReactNode;\n /** The list of selectable options. */\n options: ComboRowOption<V>[];\n /** The currently selected value (controlled). */\n value?: V;\n /** Initial value when uncontrolled. */\n defaultValue?: V;\n /** Called when the user selects an option. */\n onValueChange?: (value: V) => void;\n /** Shown in the trigger while nothing is selected. Defaults to `\"—\"`. */\n placeholder?: string;\n /** Accessible name for the selector. Defaults to `title`. */\n accessibilityLabel?: string;\n /** Disables the row and its selector. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/** `.comboWrap { min-width: 120px }`. */\nconst TRIGGER_MIN_WIDTH = 120;\n\n/**\n * Settings row with an inline combo selector at the trailing edge, mirroring\n * `AdwComboRow` and `@gnome-ui/react`'s own `ComboRow`. Use inside a\n * `BoxedList` for a setting that picks one of a set of options.\n *\n * **This is a composition of `ActionRow` + `Dropdown`, where the web version\n * hand-rolls its own listbox inline** — around 200 lines re-implementing the\n * trigger, the flip-up placement, outside-click dismissal, roving\n * `aria-activedescendant` and the whole keyboard layer, none of which is\n * meaningfully different from that package's own `Dropdown`. Nothing forced\n * the duplication visually either: `.row` is `ActionRow`'s exact metrics\n * (12/24 dp padding, 52 dp min-height) and `.trigger` is `Dropdown`'s exact\n * trigger (card background, 1 px shade border turning accent when open,\n * `radius-md`, chevron). So this port composes the two already-shipped,\n * already-verified components instead — which also means the flip-to-fit\n * placement, the tap-outside dismissal and the `Modal`-based list all come\n * along for free rather than being rebuilt and re-debugged.\n *\n * The web keeps its own `useState` for the uncontrolled case; `Dropdown` is\n * controlled-only, so that state lives here — same behaviour, one level up.\n *\n * The keyboard layer (`↑`/`↓`/`Home`/`End`/`Enter`/`Escape`) drops as it\n * does everywhere else in this package, and `Dropdown` already provides the\n * touch equivalents it was built with.\n *\n * The dimming for `disabled` is applied here rather than left to\n * `ActionRow`, which only dims in its `interactive` branch — on a plain\n * (non-pressable) row its `disabled` prop currently reaches a `View` that\n * does nothing with it. Making this row `interactive` isn't the answer: the\n * `Dropdown` is the control, and the row itself has nothing to press.\n *\n * @example\n * <BoxedList>\n * <ComboRow\n * title=\"Language\"\n * subtitle=\"Used across the whole app\"\n * value={language}\n * onValueChange={setLanguage}\n * options={[\n * { value: 'en', label: 'English' },\n * { value: 'es', label: 'Español' },\n * ]}\n * />\n * </BoxedList>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ComboRow.html\n */\nexport const ComboRow = <V extends string = string>({\n title,\n subtitle,\n leading,\n options,\n value: controlledValue,\n defaultValue,\n onValueChange,\n placeholder = '—',\n accessibilityLabel,\n disabled = false,\n style,\n testID,\n}: ComboRowProps<V>) => {\n const theme = useGnomeTheme();\n const isControlled = controlledValue !== undefined;\n const [uncontrolledValue, setUncontrolledValue] = useState<V | undefined>(defaultValue);\n const value = isControlled ? controlledValue : uncontrolledValue;\n\n return (\n <ActionRow\n testID={testID}\n title={title}\n subtitle={subtitle}\n leading={leading}\n disabled={disabled}\n style={[disabled ? { opacity: theme.opacityDisabled } : null, style]}\n trailing={\n <Dropdown\n options={options}\n value={value}\n placeholder={placeholder}\n accessibilityLabel={accessibilityLabel ?? title}\n disabled={disabled}\n onChange={(next) => {\n if (!isControlled) {\n setUncontrolledValue(next);\n }\n\n onValueChange?.(next);\n }}\n style={{ minWidth: TRIGGER_MIN_WIDTH }}\n />\n }\n />\n );\n};\n","import {\n cloneElement,\n type ReactElement,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport type {\n GestureResponderEvent,\n LayoutChangeEvent,\n MouseEvent,\n NativeSyntheticEvent,\n PressableProps,\n TargetedEvent,\n TextStyle,\n} from 'react-native';\nimport { Animated, Dimensions, Modal, StyleSheet, View } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\n\nexport type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';\n\nexport interface TooltipProps {\n /**\n * The tooltip label. Keep it short — a noun phrase or brief description.\n * Do not duplicate information already visible on screen.\n */\n label: string;\n /**\n * Preferred placement relative to the trigger.\n * The tooltip flips automatically if there is not enough space.\n * Defaults to `\"top\"`.\n */\n placement?: TooltipPlacement;\n /**\n * Delay in milliseconds before the tooltip appears, on both the\n * long-press and hover/focus triggers. Defaults to `500`. Set to `0` for\n * instant.\n */\n delay?: number;\n /**\n * The element that triggers the tooltip.\n * Must be a single element built on `Pressable` (e.g. `Button`, `Card`)\n * that forwards its `ref` to the underlying `View`.\n */\n children: ReactElement<PressableProps & { ref?: Ref<View> }>;\n}\n\ninterface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface Size {\n width: number;\n height: number;\n}\n\ninterface Position {\n top: number;\n left: number;\n placement: TooltipPlacement;\n}\n\nconst GAP = 6;\nconst MARGIN = 8;\nconst ARROW = 5;\n\nfunction assignRef<T>(ref: Ref<T> | undefined, value: T | null) {\n if (!ref) {\n return;\n }\n\n if (typeof ref === 'function') {\n ref(value);\n\n return;\n }\n\n (ref as { current: T | null }).current = value;\n}\n\nfunction placementRect(trigger: Rect, tooltip: Size, placement: TooltipPlacement) {\n switch (placement) {\n case 'top':\n return {\n top: trigger.y - tooltip.height - GAP,\n left: trigger.x + trigger.width / 2 - tooltip.width / 2,\n };\n case 'bottom':\n return {\n top: trigger.y + trigger.height + GAP,\n left: trigger.x + trigger.width / 2 - tooltip.width / 2,\n };\n case 'left':\n return {\n top: trigger.y + trigger.height / 2 - tooltip.height / 2,\n left: trigger.x - tooltip.width - GAP,\n };\n case 'right':\n return {\n top: trigger.y + trigger.height / 2 - tooltip.height / 2,\n left: trigger.x + trigger.width + GAP,\n };\n }\n}\n\nfunction computePosition(trigger: Rect, tooltip: Size, preferred: TooltipPlacement): Position {\n const { width: vw, height: vh } = Dimensions.get('window');\n\n const opposite: TooltipPlacement =\n preferred === 'top'\n ? 'bottom'\n : preferred === 'bottom'\n ? 'top'\n : preferred === 'left'\n ? 'right'\n : 'left';\n const placements: TooltipPlacement[] = [preferred, opposite, 'top', 'bottom', 'left', 'right'];\n\n for (const p of placements) {\n const { top, left } = placementRect(trigger, tooltip, p);\n const fitsH = left >= MARGIN && left + tooltip.width <= vw - MARGIN;\n const fitsV = top >= MARGIN && top + tooltip.height <= vh - MARGIN;\n\n if (fitsH && fitsV) {\n return {\n top: Math.max(MARGIN, Math.min(top, vh - tooltip.height - MARGIN)),\n left: Math.max(MARGIN, Math.min(left, vw - tooltip.width - MARGIN)),\n placement: p,\n };\n }\n }\n\n const { top, left } = placementRect(trigger, tooltip, preferred);\n\n return {\n top: Math.max(MARGIN, Math.min(top, vh - tooltip.height - MARGIN)),\n left: Math.max(MARGIN, Math.min(left, vw - tooltip.width - MARGIN)),\n placement: preferred,\n };\n}\n\n/**\n * Informational tooltip following the Adwaita / GNOME HIG pattern.\n *\n * Wraps a single trigger element and shows a floating label. Positioned\n * automatically and flips if there is not enough space, mirroring\n * `@gnome-ui/react`'s `Tooltip`.\n *\n * **Trigger differs from the web version by necessity**: the web `Tooltip`\n * shows only on mouse hover / keyboard focus — touch has no hover state, so\n * it explicitly never shows on touch. RN is touch-first, so the primary\n * trigger here is long-press (`delayLongPress={delay}`, released via\n * `onPressOut`) — the standard mobile \"peek\" idiom. `onHoverIn`/`onHoverOut`\n * are also wired for hover-capable input (trackpad/mouse on iPad or a\n * pointer-driven RN target) and `onFocus`/`onBlur` for external-keyboard\n * accessibility, both delayed the same way the web version delays hover.\n *\n * Built on RN's own `Modal` (transparent, `pointerEvents=\"box-none\"`), the\n * same portal-substitute `Dialog` uses — no DOM `Portal` equivalent exists.\n * Unlike `Dialog`, `visible` is set synchronously on trigger (not gated on\n * the async `measureInWindow` result): the trigger's on-screen rect and the\n * tooltip's own rendered size each resolve independently into state, and a\n * separate effect computes the final position only once both have arrived.\n * Gating `visible` itself on `measureInWindow`'s callback was tried first\n * and dropped — that callback never fires in this package's Jest\n * environment (no real native view to measure), which would make the\n * component untestable, and in production it would also lose a real\n * quick-tap-then-release press if the callback resolved after `onPressOut`\n * had already fired.\n *\n * `role=\"tooltip\"` ports 1:1 — RN's newer web-aligned `Role` union (the\n * same one `Dialog` uses for `role=\"dialog\"`) already has a `\"tooltip\"`\n * value, no substitution needed. `aria-describedby` has no RN equivalent\n * (no cross-platform description-relationship prop exists), so the label is\n * set as the trigger's `accessibilityHint` instead — read after the\n * trigger's own label by VoiceOver/TalkBack, the nearest analog. The CSS\n * arrow reuses the same zero-size / transparent-border-on-three-sides\n * triangle trick as the web version — RN Views support per-side\n * `border*Color` just like CSS, the same technique `Spinner`'s ring\n * already relies on, applied here to a triangle instead of an arc.\n *\n * Not ported: repositioning on scroll/resize while visible (the web\n * version listens for both) — RN has no global scroll event, and a\n * long-press is naturally cancelled by a scroll gesture starting, so the\n * window to go stale is negligible for the touch trigger; orientation\n * change mid-hover is an accepted edge case for a component this transient.\n * `tooltipBgColor`/`tooltipFgColor` are not real `@gnome-ui/core` tokens\n * (only referenced as CSS var fallbacks, never defined) — hardcoded to the\n * same literal light/dark values as the web fallback, the same workaround\n * `Spinner`'s track color already established for this class of gap.\n *\n * @example\n * <Tooltip label=\"Save file\">\n * <Button accessibilityLabel=\"Save\"><Icon icon={Save} /></Button>\n * </Tooltip>\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/tooltips.html\n */\nexport const Tooltip = ({\n label,\n placement: preferredPlacement = 'top',\n delay = 500,\n children,\n}: TooltipProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const reducedMotion = useReducedMotion();\n\n const [visible, setVisible] = useState(false);\n const [triggerRect, setTriggerRect] = useState<Rect | null>(null);\n const [tooltipSize, setTooltipSize] = useState<Size | null>(null);\n const [pos, setPos] = useState<Position | null>(null);\n\n const triggerRef = useRef<View>(null);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const progress = useRef(new Animated.Value(0)).current;\n\n const clearTimer = useCallback(() => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n const open = useCallback(() => {\n clearTimer();\n setVisible(true);\n }, [clearTimer]);\n\n const openDelayed = useCallback(() => {\n clearTimer();\n timerRef.current = setTimeout(open, delay);\n }, [clearTimer, open, delay]);\n\n const close = useCallback(() => {\n clearTimer();\n setVisible(false);\n }, [clearTimer]);\n\n // Measure the trigger's on-screen rect as soon as it's asked to show.\n useEffect(() => {\n if (!visible) {\n setTriggerRect(null);\n setTooltipSize(null);\n setPos(null);\n\n return;\n }\n\n triggerRef.current?.measureInWindow((x, y, width, height) => {\n setTriggerRect({ x, y, width, height });\n });\n }, [visible]);\n\n // Resolve the final position once both the trigger's rect and the\n // tooltip's own rendered size are known.\n useEffect(() => {\n if (!triggerRect || !tooltipSize) {\n return;\n }\n\n setPos(computePosition(triggerRect, tooltipSize, preferredPlacement));\n }, [triggerRect, tooltipSize, preferredPlacement]);\n\n useEffect(() => {\n if (!pos) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n progress.setValue(0);\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationFast,\n useNativeDriver: true,\n }).start();\n }, [pos, reducedMotion, progress, theme.durationFast]);\n\n useEffect(() => clearTimer, [clearTimer]);\n\n const handleTooltipLayout = (e: LayoutChangeEvent) => {\n const { width, height } = e.nativeEvent.layout;\n\n setTooltipSize({ width, height });\n };\n\n const bg = scheme === 'dark' ? '#c0bfbc' : '#3d3d3d';\n const fg = scheme === 'dark' ? 'rgba(0, 0, 0, 0.8)' : '#fff';\n const shadow =\n scheme === 'dark'\n ? { shadowOpacity: 0.4, shadowRadius: 8, elevation: 6 }\n : { shadowOpacity: 0.2, shadowRadius: 8, elevation: 4 };\n\n const arrowBase = {\n position: 'absolute' as const,\n width: 0,\n height: 0,\n borderWidth: ARROW,\n borderColor: 'transparent',\n };\n\n const arrowStyle = (() => {\n switch (pos?.placement) {\n case 'top':\n return {\n ...arrowBase,\n top: '100%' as const,\n left: '50%' as const,\n marginLeft: -ARROW,\n borderTopColor: bg,\n };\n case 'bottom':\n return {\n ...arrowBase,\n bottom: '100%' as const,\n left: '50%' as const,\n marginLeft: -ARROW,\n borderBottomColor: bg,\n };\n case 'left':\n return {\n ...arrowBase,\n left: '100%' as const,\n top: '50%' as const,\n marginTop: -ARROW,\n borderLeftColor: bg,\n };\n case 'right':\n return {\n ...arrowBase,\n right: '100%' as const,\n top: '50%' as const,\n marginTop: -ARROW,\n borderRightColor: bg,\n };\n default:\n return null;\n }\n })();\n\n const existingRef = (children.props as { ref?: Ref<View> }).ref;\n\n const child = cloneElement(children, {\n ref: (node: View | null) => {\n (triggerRef as { current: View | null }).current = node;\n assignRef(existingRef, node);\n },\n delayLongPress: delay,\n accessibilityHint: children.props.accessibilityHint ?? label,\n onLongPress: (e: GestureResponderEvent) => {\n open();\n children.props.onLongPress?.(e);\n },\n onPressOut: (e: GestureResponderEvent) => {\n close();\n children.props.onPressOut?.(e);\n },\n onHoverIn: (e: MouseEvent) => {\n openDelayed();\n children.props.onHoverIn?.(e);\n },\n onHoverOut: (e: MouseEvent) => {\n close();\n children.props.onHoverOut?.(e);\n },\n onFocus: (e: NativeSyntheticEvent<TargetedEvent>) => {\n openDelayed();\n children.props.onFocus?.(e);\n },\n onBlur: (e: NativeSyntheticEvent<TargetedEvent>) => {\n close();\n children.props.onBlur?.(e);\n },\n });\n\n return (\n <>\n {child}\n <Modal visible={visible} transparent animationType=\"none\" statusBarTranslucent>\n <View pointerEvents=\"box-none\" style={StyleSheet.absoluteFill}>\n <Animated.View\n onLayout={handleTooltipLayout}\n accessible\n role=\"tooltip\"\n style={[\n {\n position: 'absolute',\n maxWidth: 240,\n paddingVertical: 4,\n paddingHorizontal: theme.space2,\n backgroundColor: bg,\n borderRadius: theme.radiusSm,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 2 },\n ...shadow,\n },\n pos\n ? { top: pos.top, left: pos.left, opacity: progress }\n : { top: -9999, left: -9999, opacity: 0 },\n ]}\n >\n <Text\n style={{\n color: fg,\n fontSize: theme.fontSizeCaption,\n fontWeight: String(theme.fontWeightNormal) as TextStyle['fontWeight'],\n lineHeight: theme.fontSizeCaption * theme.lineHeightBody,\n textAlign: 'center',\n }}\n >\n {label}\n </Text>\n {arrowStyle && <View style={arrowStyle} />}\n </Animated.View>\n </View>\n </Modal>\n </>\n );\n};\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport { forwardRef } from 'react';\nimport type { View } from 'react-native';\n\nimport { Button, type ButtonProps, type ButtonSize, type ButtonVariant } from '@/components/Button';\nimport { Icon, type IconSize } from '@/components/Icon';\nimport { Tooltip, type TooltipPlacement } from '@/components/Tooltip';\n\nexport type IconButtonVariant = ButtonVariant | 'osd';\nexport type IconButtonSize = ButtonSize;\n\nexport interface IconButtonProps\n extends Omit<\n ButtonProps,\n 'accessibilityLabel' | 'children' | 'leadingIcon' | 'osd' | 'shape' | 'trailingIcon' | 'variant'\n > {\n /** Icon definition imported from `@gnome-ui/icons`. */\n icon: IconDefinition;\n /** Accessible name for the icon-only button. */\n label: string;\n /** Visual style of the button. Use `\"osd\"` for media overlay controls. */\n variant?: IconButtonVariant;\n /** Size of the button. */\n size?: IconButtonSize;\n /** Override the rendered icon size. Defaults to a size matched to `size`. */\n iconSize?: IconSize;\n /** Optional tooltip label shown on long-press/hover/focus. */\n tooltip?: string;\n /** Preferred tooltip placement. */\n tooltipPlacement?: TooltipPlacement;\n /** Tooltip delay in milliseconds. */\n tooltipDelay?: number;\n}\n\nconst ICON_SIZE_BY_BUTTON_SIZE: Record<IconButtonSize, IconSize> = {\n sm: 'sm',\n md: 'md',\n lg: 'lg',\n};\n\n/**\n * Icon-only action button composed from `Button`, `Icon`, and optionally\n * `Tooltip` — mirrors `@gnome-ui/react`'s `IconButton`, itself already just\n * a thin composition of those same three pieces (its own JSDoc example\n * shows the identical `<Tooltip><Button><Icon /></Button></Tooltip>`\n * nesting `IconButton` here just formalizes into a named, reusable export).\n * `label` is required since the button has no visible text.\n */\nexport const IconButton = forwardRef<View, IconButtonProps>(function IconButton(\n {\n icon,\n label,\n variant = 'default',\n size = 'md',\n iconSize,\n tooltip,\n tooltipPlacement,\n tooltipDelay,\n ...props\n },\n ref,\n) {\n const buttonVariant = variant === 'osd' ? 'flat' : variant;\n\n const button = (\n <Button\n ref={ref}\n accessibilityLabel={label}\n shape=\"circular\"\n size={size}\n variant={buttonVariant}\n osd={variant === 'osd'}\n {...props}\n >\n <Icon icon={icon} size={iconSize ?? ICON_SIZE_BY_BUTTON_SIZE[size]} />\n </Button>\n );\n\n if (!tooltip) {\n return button;\n }\n\n return (\n <Tooltip label={tooltip} placement={tooltipPlacement} delay={tooltipDelay}>\n {button}\n </Tooltip>\n );\n});\n","import { Check, Copy } from '@gnome-ui/icons';\nimport type ClipboardModule from '@react-native-clipboard/clipboard';\nimport { useEffect, useRef, useState } from 'react';\nimport { Text } from 'react-native';\n\nimport { IconButton, type IconButtonProps } from '@/components/IconButton';\n\nexport interface CopyButtonProps\n extends Omit<IconButtonProps, 'icon' | 'label' | 'onPress' | 'tooltip'> {\n /** The text copied to the clipboard when the button is activated. */\n value: string;\n /** Accessible label and tooltip shown before copying. Defaults to `\"Copy\"`. */\n label?: string;\n /** Accessible label and tooltip shown briefly after a successful copy. Defaults to `\"Copied!\"`. */\n copiedLabel?: string;\n /** How long the \"copied\" confirmation state is shown, in milliseconds. Defaults to `2000`. */\n resetDelay?: number;\n /**\n * Called after `value` is written to the clipboard.\n * Named `onCopied` (not `onCopy`), mirroring the web version's own naming.\n */\n onCopied?: (value: string) => void;\n /**\n * Called if the copy attempt throws. Kept for API parity with the web\n * version's `onCopyError` (whose `navigator.clipboard.writeText` can\n * reject, e.g. on permission denial) — but `@react-native-clipboard/\n * clipboard`'s `setString` is a synchronous, void-returning native call\n * with no error channel at all, so in normal operation this won't fire\n * the way it can on web. Kept as a defensive catch around the native\n * call (a real, if unlikely, synchronous throw) rather than removed.\n */\n onCopyError?: (error: unknown) => void;\n}\n\n// Visually hides the live-region announcement without removing it from the\n// accessibility tree — same 1x1/overflow-hidden recipe as the web version's\n// `LIVE_REGION_STYLE`.\nconst LIVE_REGION_STYLE = {\n position: 'absolute' as const,\n width: 1,\n height: 1,\n overflow: 'hidden' as const,\n};\n\n/**\n * Icon button that copies `value` to the clipboard, swapping to a checkmark\n * and a \"Copied!\" tooltip for `resetDelay` ms as confirmation — mirrors\n * `@gnome-ui/react`'s `CopyButton`.\n *\n * RN has no `navigator.clipboard`, so this is built on\n * `@react-native-clipboard/clipboard` (the community-standard clipboard\n * module, not `expo-clipboard` — chosen so this package works the same in\n * bare RN and Expo, not just this repo's own Expo example app). Its\n * `setString` is synchronous and void — see `onCopyError`'s doc for what\n * that means for error handling.\n *\n * The live-region announcement uses `role=\"status\"` directly (RN's newer\n * `Role` union does include `\"status\"`, unlike the older\n * `AccessibilityRole` enum `Toast` had to substitute `\"alert\"` for) plus\n * `accessibilityLiveRegion=\"polite\"` for Android's announcement mechanism.\n */\nexport const CopyButton = ({\n value,\n label = 'Copy',\n copiedLabel = 'Copied!',\n resetDelay = 2000,\n onCopied,\n onCopyError,\n ...props\n}: CopyButtonProps) => {\n const [copied, setCopied] = useState(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(\n () => () => {\n if (timeoutRef.current !== null) {\n clearTimeout(timeoutRef.current);\n }\n },\n [],\n );\n\n const handlePress = () => {\n try {\n // Required lazily, not imported at module scope: the native binding's\n // own top-level code calls `TurboModuleRegistry.getEnforcing(...)`\n // eagerly, which throws the instant the module is evaluated if the\n // native side isn't registered — as in Expo Go, which doesn't bundle\n // this community module. A static `import` would throw as soon as\n // this file is loaded, crashing every screen (not just this one),\n // since example apps import every component screen up front. Deferring\n // the require to press-time means Expo Go only fails if this specific\n // button is actually pressed, caught below like any other native error.\n const Clipboard: typeof ClipboardModule =\n // eslint-disable-next-line @typescript-eslint/no-require-imports -- see comment above\n require('@react-native-clipboard/clipboard').default;\n\n Clipboard.setString(value);\n } catch (error) {\n onCopyError?.(error);\n\n return;\n }\n\n onCopied?.(value);\n setCopied(true);\n\n if (timeoutRef.current !== null) {\n clearTimeout(timeoutRef.current);\n }\n\n timeoutRef.current = setTimeout(() => {\n setCopied(false);\n timeoutRef.current = null;\n }, resetDelay);\n };\n\n return (\n <>\n <IconButton\n {...props}\n icon={copied ? Check : Copy}\n label={copied ? copiedLabel : label}\n tooltip={copied ? copiedLabel : label}\n onPress={handlePress}\n />\n <Text role=\"status\" accessibilityLiveRegion=\"polite\" style={LIVE_REGION_STYLE}>\n {copied ? copiedLabel : ''}\n </Text>\n </>\n );\n};\n","import { type ReactNode, useEffect, useRef } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Animated, BackHandler, Easing, Modal, Pressable, View } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\n\n/**\n * `Animated`'s style interpolation only takes effect on `Animated.*` host\n * components — a plain `Pressable` would silently ignore the animated\n * `opacity` in its style, the same gotcha already documented on `Toast`.\n * Hoisted to module scope so it isn't recreated on every render.\n */\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable);\n\nexport type DialogButtonVariant = 'default' | 'suggested' | 'destructive';\n\nexport interface DialogButton {\n /** Button label. */\n label: string;\n /** Visual variant. Defaults to `\"default\"`. */\n variant?: DialogButtonVariant;\n /** Called when the button is pressed. */\n onPress: () => void;\n /** Whether the button is disabled. */\n disabled?: boolean;\n}\n\nexport type AlertDialogResponseVariant = DialogButtonVariant;\n\nexport interface AlertDialogResponse {\n /**\n * Unique identifier returned via `onResponse`.\n * Use `\"cancel\"` by convention for the dismissive action.\n */\n id: string;\n /** Button label. */\n label: string;\n /** Visual emphasis. Defaults to `\"default\"`. */\n variant?: AlertDialogResponseVariant;\n /** Disables the button. */\n disabled?: boolean;\n}\n\nexport interface DialogProps {\n /** Whether the dialog is visible. */\n open: boolean;\n\n // ── Standard dialog ────────────────────────────────────────────────────\n\n /** Dialog heading. */\n title?: ReactNode;\n /** Body content. */\n children?: ReactNode;\n /** Action buttons (standard dialog API). */\n buttons?: DialogButton[];\n /** Called on Android back button / backdrop press. */\n onClose?: () => void;\n /** Whether pressing the backdrop closes the dialog. Defaults to `true`. */\n closeOnBackdrop?: boolean;\n\n // ── AlertDialog extension ───────────────────────────────────────────────\n\n /**\n * Use `\"alertdialog\"` for confirmations and destructive warnings — screen\n * readers announce it immediately. Defaults to `\"dialog\"`.\n *\n * When using `role=\"alertdialog\"`, prefer the `responses`/`onResponse` API\n * over `buttons`/`onPress` for semantic clarity.\n */\n role?: 'dialog' | 'alertdialog';\n\n /**\n * Response buttons (AlertDialog API). Alternative to `buttons` — each\n * response has a semantic `id` returned via `onResponse`. The Android back\n * button and backdrop press fire the first non-destructive response.\n */\n responses?: AlertDialogResponse[];\n\n /**\n * Called with the `id` of the response button pressed.\n * Required when `responses` is provided.\n */\n onResponse?: (id: string) => void;\n\n style?: StyleProp<ViewStyle>;\n /** Forwarded to the backdrop — useful for testing. */\n testID?: string;\n}\n\n/**\n * Blocking modal dialog following the Adwaita pattern.\n *\n * **Standard** — `title` + `children` + `buttons[]`.\n *\n * **Alert** — add `role=\"alertdialog\"` + `responses[]` + `onResponse`.\n * Uses a semantic response id instead of per-button `onPress`. The Android\n * back button / backdrop press fire the first non-destructive response.\n * Mirrors `AdwAlertDialog`.\n *\n * Built on RN's own `Modal` rather than `@gnome-ui/react`'s DOM `Portal` +\n * manual focus trap: `Modal` already floats above everything with no portal\n * target needed, already blocks interaction with the screen behind it (no\n * `useBodyScrollLock` equivalent needed), and its `onRequestClose` fires on\n * the Android hardware back button — the direct analog of the web version's\n * document-level Escape listener. Focus-trapping (`Tab`/`Shift+Tab` cycling)\n * has no port: there is no keyboard `Tab` concept in RN's touch-first model,\n * the same reasoning that already dropped `TabBar`'s roving-tabindex arrow\n * keys.\n *\n * `role` is passed straight through as RN's own `role` prop (not\n * `accessibilityRole`) — RN's newer, web-aligned `Role` union has real\n * `\"dialog\"`/`\"alertdialog\"` values, unlike the older `AccessibilityRole`\n * enum `Toast`/`Banner` had to substitute `\"alert\"` into for the web's\n * `role=\"status\"`. `accessibilityViewIsModal` (iOS) is the closest match to\n * `aria-modal=\"true\"`, restricting VoiceOver to the dialog's subtree.\n *\n * There is no exit animation on either platform — the source CSS only\n * defines entrance keyframes, and `Modal`'s `visible={false}` unmounts\n * immediately, matching the web version returning `null` outright when\n * `!open`. Entrance is an `Animated.timing` fading + scaling + sliding the\n * card in (mirrors `@keyframes dialog-in`) alongside a plain backdrop fade\n * (`@keyframes backdrop-in`), replayed via a `useEffect` keyed on `open`\n * since — unlike `Toast`, which mounts once per instance — the same\n * `Dialog` element toggles `open` repeatedly while `Modal` itself\n * mounts/unmounts internally. `useReducedMotion()` skips straight to the\n * settled state.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Dialog.html\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.AlertDialog.html\n */\nexport const Dialog = ({\n open,\n title,\n children,\n buttons = [],\n onClose,\n closeOnBackdrop = true,\n role = 'dialog',\n responses,\n onResponse,\n style,\n testID,\n}: DialogProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const reducedMotion = useReducedMotion();\n const progress = useRef(new Animated.Value(reducedMotion ? 1 : 0)).current;\n\n const isAlert = !!responses;\n\n const dismissAlert = () => {\n const cancel = responses?.find((r) => r.variant !== 'destructive' && !r.disabled);\n\n if (cancel) {\n onResponse?.(cancel.id);\n }\n };\n\n const handleEscape = () => {\n if (isAlert) {\n dismissAlert();\n } else {\n onClose?.();\n }\n };\n\n const handleBackdropPress = isAlert ? dismissAlert : closeOnBackdrop ? onClose : undefined;\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n progress.setValue(0);\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationNormal,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: true,\n }).start();\n }, [open, reducedMotion, progress, theme.durationNormal, theme.easingDefault]);\n\n // Only the topmost dialog reacts to the Android back button, mirroring\n // the web version's \"topmost modal only\" Escape behavior for stacked\n // dialogs.\n useEffect(() => {\n if (!open) {\n return;\n }\n\n const subscription = BackHandler.addEventListener('hardwareBackPress', () => {\n handleEscape();\n\n return true;\n });\n\n return () => subscription.remove();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, isAlert, responses, onResponse, onClose]);\n\n const renderButtons = () => {\n const rows = isAlert && responses ? responses : buttons;\n\n if (rows.length === 0) {\n return null;\n }\n\n return (\n <View style={{ borderTopWidth: 1, borderTopColor: theme.cardShadeColor }}>\n {rows.map((row, index) => {\n const variant = row.variant ?? 'default';\n const color = variant === 'destructive' ? theme.destructiveColor : theme.accentColor;\n const onPress = isAlert\n ? () => onResponse?.((row as AlertDialogResponse).id)\n : (row as DialogButton).onPress;\n\n return (\n <Pressable\n key={isAlert ? (row as AlertDialogResponse).id : row.label}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled: row.disabled }}\n disabled={row.disabled}\n onPress={onPress}\n style={({ pressed }) => ({\n paddingVertical: 14,\n paddingHorizontal: theme.space4,\n borderBottomWidth: index < rows.length - 1 ? 1 : 0,\n borderBottomColor: theme.cardShadeColor,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n opacity: row.disabled ? theme.opacityDisabled : 1,\n })}\n >\n <Text\n variant=\"body\"\n style={{\n textAlign: 'center',\n color,\n fontWeight:\n variant === 'suggested'\n ? (String(theme.fontWeightSemibold) as TextStyle['fontWeight'])\n : undefined,\n }}\n >\n {row.label}\n </Text>\n </Pressable>\n );\n })}\n </View>\n );\n };\n\n const shadow =\n scheme === 'dark'\n ? { shadowOpacity: 0.5, shadowRadius: 32, elevation: 12 }\n : { shadowOpacity: 0.25, shadowRadius: 32, elevation: 8 };\n\n return (\n <Modal\n visible={open}\n transparent\n animationType=\"none\"\n statusBarTranslucent\n onRequestClose={handleEscape}\n >\n <AnimatedPressable\n testID={testID}\n onPress={handleBackdropPress}\n style={{\n flex: 1,\n alignItems: 'center',\n justifyContent: 'center',\n paddingHorizontal: 24,\n backgroundColor: theme.dialogBackdropColor,\n opacity: progress,\n }}\n >\n <Pressable onPress={() => {}} accessible={false} style={{ width: '100%', maxWidth: 480 }}>\n <Animated.View\n accessible\n role={role}\n accessibilityViewIsModal\n style={[\n {\n backgroundColor: theme.dialogBgColor,\n borderRadius: theme.radiusXl,\n overflow: 'hidden',\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 8 },\n ...shadow,\n opacity: progress,\n transform: [\n {\n translateY: progress.interpolate({ inputRange: [0, 1], outputRange: [-8, 0] }),\n },\n { scale: progress.interpolate({ inputRange: [0, 1], outputRange: [0.92, 1] }) },\n ],\n },\n style,\n ]}\n >\n {title && (\n <Text\n variant=\"title-4\"\n style={{\n padding: theme.space4,\n paddingBottom: children ? 0 : theme.space4,\n textAlign: 'center',\n fontWeight: String(theme.fontWeightBold) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n )}\n\n {children && (\n <View style={{ paddingVertical: theme.space2, paddingHorizontal: theme.space4 }}>\n {typeof children === 'string' ? (\n <Text variant=\"body\" style={{ textAlign: 'center', opacity: theme.opacityDim }}>\n {children}\n </Text>\n ) : (\n children\n )}\n </View>\n )}\n\n {renderButtons()}\n </Animated.View>\n </Pressable>\n </AnimatedPressable>\n </Modal>\n );\n};\n","import type { ReactNode } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { View } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface DividerProps {\n /** Optional centered label (e.g. `\"OR\"`, `\"Continue with\"`). */\n children?: ReactNode;\n /** Accessible name. Defaults to `children` when it's a string. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Horizontal rule with an optional centered label — common auth/login-form\n * pattern (\"Sign in\" / **OR** / \"Continue with Google\"). Mirrors\n * `@gnome-ui/react`'s `Divider`.\n *\n * For a bare dividing line with no label, use `Separator` instead — it also\n * supports a vertical orientation, which `Divider` does not.\n *\n * `role=\"separator\"` ports 1:1 from RN's newer web-aligned `Role` union\n * (the same one `Avatar`/`Badge`/`LevelBar` already reach for), unlike\n * `Separator`'s own choice to render as `accessible={false}` — that\n * component is purely decorative with nothing for a screen reader to\n * announce, while a labelled `Divider` (\"OR\") is exactly the kind of\n * content a screen reader user needs read aloud, so it stays in the\n * accessibility tree instead. The label reuses `Text`'s `variant=\"caption\"\n * color=\"dim\"` verbatim rather than hand-rolled styles, since that\n * combination already resolves to the same font-size/weight/dim-opacity\n * the web version's `.label` class hard-codes.\n *\n * The web version's `aria-orientation=\"horizontal\"` has no port — `Divider`\n * has no `orientation` prop at all (unlike `Separator`), so there is only\n * ever one orientation to announce.\n *\n * @see https://developer.gnome.org/hig/patterns/containers.html\n */\nexport const Divider = ({ children, accessibilityLabel, style, testID }: DividerProps) => {\n const theme = useGnomeTheme();\n const hasLabel = Boolean(children);\n\n return (\n <View\n testID={testID}\n accessible\n role=\"separator\"\n accessibilityLabel={\n accessibilityLabel ?? (typeof children === 'string' ? children : undefined)\n }\n style={[\n { flexDirection: 'row', alignItems: 'center', width: '100%', gap: theme.space2 },\n style,\n ]}\n >\n <View style={{ flex: 1, height: 1, backgroundColor: theme.cardShadeColor }} />\n {hasLabel && (\n <>\n {typeof children === 'string' ? (\n <Text variant=\"caption\" color=\"dim\">\n {children}\n </Text>\n ) : (\n children\n )}\n <View style={{ flex: 1, height: 1, backgroundColor: theme.cardShadeColor }} />\n </>\n )}\n </View>\n );\n};\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport { Close } from '@gnome-ui/icons';\nimport { createContext, type ReactNode, useContext, useEffect, useRef } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Animated, BackHandler, Dimensions, Easing, Modal, Pressable, View } from 'react-native';\n\nimport { Button } from '@/components/Button';\nimport { Icon } from '@/components/Icon';\nimport { IconButton } from '@/components/IconButton';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\n\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable);\n\nexport type DrawerSide = 'left' | 'right';\nexport type DrawerSize = 'classic' | 'wide';\n\nexport interface DrawerRailItem {\n /** Stable unique identifier. */\n id: string;\n /** Icon shown for this rail entry. */\n icon: IconDefinition;\n /** Accessible name, also used as the tooltip. */\n label: string;\n /** Whether this entry represents the currently visible drawer/panel. */\n active?: boolean;\n disabled?: boolean;\n onPress: () => void;\n}\n\nexport interface DrawerProps {\n /** Whether the drawer is visible. */\n open: boolean;\n /** Edge that the drawer slides in from. Defaults to `\"right\"`. */\n side?: DrawerSide;\n /** Preset drawer width. Defaults to `\"classic\"`. */\n size?: DrawerSize;\n /** Optional drawer heading. */\n title?: ReactNode;\n /** Drawer content when a prop is preferred over `children`. */\n content?: ReactNode;\n /** Drawer content. Used when `content` is not provided. */\n children?: ReactNode;\n /** Called when the user dismisses the drawer with the Android back button or the backdrop. */\n onClose?: () => void;\n /** Whether pressing the backdrop closes the drawer. Defaults to `true`. */\n closeOnBackdrop?: boolean;\n /**\n * Narrow icon rail rendered on the drawer's inner edge (the edge facing\n * the backdrop), for switching between related drawers or panels without\n * closing the drawer. Purely presentational — pressing an entry only\n * calls its `onPress`; the caller decides what happens (swap `content`,\n * open a different drawer, etc).\n */\n rail?: DrawerRailItem[];\n style?: StyleProp<ViewStyle>;\n /** Forwarded to the backdrop — useful for testing. */\n testID?: string;\n}\n\nconst DrawerDepthContext = createContext(0);\n\nconst DRAWER_PRESET_WIDTH: Record<DrawerSize, number> = { classic: 420, wide: 640 };\nconst DRAWER_DEPTH_SCALE = 0.85;\nconst DRAWER_MIN_WIDTH = 240;\n\n/**\n * Slide-in panel for supplementary content, anchored to the left or right\n * edge. Mirrors `@gnome-ui/react`'s `Drawer`.\n *\n * Rebuilt with `View`/`Modal` rather than ported from the web version's\n * `createPortal(document.body)` + manual focus trap + Escape listener:\n * `Modal` already floats above everything with no portal target needed,\n * and `BackHandler`'s `hardwareBackPress` is the Android analog of the\n * Escape listener (the same `Dialog`/`BottomSheet` precedent). Focus\n * trapping has no port — no DOM `Tab`/`document.activeElement` concept in\n * RN's touch-first model.\n *\n * **Floats with a margin on every side, all four corners rounded** — the\n * backdrop `Pressable` carries `padding: theme.space3` (matching the\n * `@gnome-ui/react` source's own recent update to the same floating-card\n * look, not a divergence), and the drawer itself gets a uniform\n * `borderRadius` instead of the flat-edge-on-the-anchored-side look a\n * flush-to-the-screen-edge panel would need. Positioning within that\n * padded backdrop uses `justifyContent: 'flex-end'`/`'flex-start'` on the\n * backdrop (not the web CSS's `margin-left/right: auto` on the drawer\n * itself) — confirmed empirically (a throwaway build with saturated debug\n * colors standing in for the real theme colors, screenshotted on-device)\n * that `justifyContent` renders correctly while auto-margins were, at\n * best, unverified for this RN/Yoga version; `BottomSheet` already proves\n * the same `justifyContent: 'flex-end'` mechanism works on this exact\n * setup, just on the vertical axis instead of horizontal. On a phone-width\n * screen the `classic`/`wide` presets (420/640, sized for wider viewports)\n * get capped to fill essentially the entire available width after the\n * margin either way, so the anchored side becomes visually obvious mainly\n * on tablets — the same responsive behavior the web version would show at\n * an equally narrow browser width, not an RN-specific gap.\n *\n * **No drag-to-dismiss, unlike `BottomSheet`**: the web source only\n * defines entrance keyframes for both the backdrop and the panel, so this\n * follows `Dialog`'s simpler shape (a single `progress` `Animated.Value`\n * replayed via `useEffect` keyed on `open`, no separate exit animation or\n * lagging `visible` state) rather than `BottomSheet`'s\n * `PanResponder`-plus-timed-exit machinery.\n *\n * **The slide distance needs no `onLayout` measurement**, unlike\n * `BottomSheet`'s content-driven height: the drawer's width is a value\n * this component already computes in JS (`size`'s preset, scaled down by\n * `DrawerDepthContext` depth, capped by the available space after the\n * backdrop's margin) — RN's `transform` has no percentage-of-self units\n * (the same `BottomSheet`/`Avatar`/`Slider` pitfall), but since the exact\n * pixel width is already known synchronously, `translateX` can animate\n * from that known offset to `0` immediately, with no first-frame\n * imprecision to accept.\n *\n * **`DrawerDepthContext` (nested-drawer width auto-scaling) ports 1:1** —\n * pure React Context state, no DOM dependency at all. A `Drawer` opened\n * from within another drawer's `content`/`children` is detected via\n * context and scales its own preset width down (`0.85^depth`, floored at\n * `DRAWER_MIN_WIDTH`) so stacked drawers read as a drill-in hierarchy\n * instead of identical overlapping panels.\n *\n * **`rail` reuses the newly-added `IconButton`** (itself just `Button` +\n * `Icon` + optional `Tooltip`, the same composition `@gnome-ui/react`'s own\n * `IconButton` already is) — `aria-pressed` becomes\n * `accessibilityState={{ selected: item.active }}`, the closest RN\n * equivalent for a toggleable icon button with no dedicated visual\n * \"pressed\" style on either platform's source.\n *\n * `backdrop-filter: blur(4px)` has no port — no native blur view\n * dependency exists in this package, the same gap already dropped from\n * `Sidebar`'s blurred `variant`/`BottomSheet`'s backdrop. `role=\"dialog\"` +\n * `accessibilityViewIsModal` port 1:1 from `Dialog`'s own precedent.\n *\n * @see https://developer.gnome.org/hig/patterns/containers.html\n */\nexport const Drawer = ({\n open,\n side = 'right',\n size = 'classic',\n title,\n content,\n children,\n onClose,\n closeOnBackdrop = true,\n rail,\n style,\n testID,\n}: DrawerProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const reducedMotion = useReducedMotion();\n const depth = useContext(DrawerDepthContext);\n\n const body = content !== undefined ? content : children;\n\n const presetWidth = DRAWER_PRESET_WIDTH[size];\n const scaledWidth =\n depth > 0\n ? Math.max(DRAWER_MIN_WIDTH, Math.round(presetWidth * DRAWER_DEPTH_SCALE ** depth))\n : presetWidth;\n const maxAvailableWidth = Dimensions.get('window').width - theme.space3 * 2;\n const drawerWidth = Math.min(scaledWidth, maxAvailableWidth);\n\n const progress = useRef(new Animated.Value(reducedMotion ? 1 : 0)).current;\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n progress.setValue(0);\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationNormal,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: true,\n }).start();\n }, [open, reducedMotion, progress, theme.durationNormal, theme.easingDefault]);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n const subscription = BackHandler.addEventListener('hardwareBackPress', () => {\n onClose?.();\n\n return true;\n });\n\n return () => subscription.remove();\n }, [open, onClose]);\n\n const offset = side === 'left' ? -drawerWidth : drawerWidth;\n const translateX = progress.interpolate({ inputRange: [0, 1], outputRange: [offset, 0] });\n\n const shadow =\n scheme === 'dark'\n ? { shadowOpacity: 0.45, shadowRadius: 24, elevation: 12 }\n : { shadowOpacity: 0.18, shadowRadius: 24, elevation: 8 };\n\n const rail_ =\n rail && rail.length > 0 ? (\n <View\n style={{\n width: 56,\n flexShrink: 0,\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: theme.space3,\n [side === 'right' ? 'borderRightWidth' : 'borderLeftWidth']: 1,\n borderColor: theme.cardShadeColor,\n }}\n >\n {rail.map((item) => (\n <IconButton\n key={item.id}\n icon={item.icon}\n label={item.label}\n tooltip={item.label}\n tooltipPlacement={side === 'right' ? 'left' : 'right'}\n variant=\"flat\"\n size=\"sm\"\n disabled={item.disabled}\n accessibilityState={{ selected: !!item.active, disabled: !!item.disabled }}\n onPress={item.onPress}\n />\n ))}\n </View>\n ) : null;\n\n return (\n <Modal\n visible={open}\n transparent\n animationType=\"none\"\n statusBarTranslucent\n onRequestClose={onClose}\n >\n <AnimatedPressable\n testID={testID}\n onPress={closeOnBackdrop ? onClose : undefined}\n accessible={false}\n style={{\n flex: 1,\n flexDirection: 'row',\n justifyContent: side === 'left' ? 'flex-start' : 'flex-end',\n padding: theme.space3,\n backgroundColor: theme.dialogBackdropColor,\n opacity: progress,\n }}\n >\n <Pressable onPress={() => {}} accessible={false} style={{ width: drawerWidth }}>\n <Animated.View\n accessible\n role=\"dialog\"\n accessibilityViewIsModal\n style={[\n {\n flex: 1,\n flexDirection: 'row',\n overflow: 'hidden',\n borderRadius: theme.radiusXl,\n backgroundColor: theme.dialogBgColor,\n shadowColor: '#000',\n shadowOffset: { width: side === 'left' ? 4 : -4, height: 0 },\n ...shadow,\n transform: [{ translateX }],\n },\n style,\n ]}\n >\n {side === 'right' && rail_}\n\n <View style={{ flex: 1, minWidth: 0 }}>\n {title && (\n <View\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n padding: theme.space4,\n paddingBottom: theme.space2,\n }}\n >\n <Text\n variant=\"title-4\"\n style={{\n flex: 1,\n fontWeight: String(theme.fontWeightBold) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n {onClose && (\n <Button\n variant=\"flat\"\n shape=\"circular\"\n size=\"sm\"\n accessibilityLabel=\"Close\"\n onPress={onClose}\n >\n <Icon icon={Close} size=\"sm\" />\n </Button>\n )}\n </View>\n )}\n\n {body !== undefined && (\n <View\n style={{\n flex: 1,\n padding: theme.space4,\n paddingTop: title ? 0 : theme.space4,\n }}\n >\n <DrawerDepthContext.Provider value={depth + 1}>\n {typeof body === 'string' ? <Text variant=\"body\">{body}</Text> : body}\n </DrawerDepthContext.Provider>\n </View>\n )}\n </View>\n\n {side === 'left' && rail_}\n </Animated.View>\n </Pressable>\n </AnimatedPressable>\n </Modal>\n );\n};\n","import { forwardRef, type ReactNode, useEffect, useRef, useState } from 'react';\nimport type {\n LayoutChangeEvent,\n TextInput as RNTextInput,\n StyleProp,\n TextInputProps,\n ViewStyle,\n} from 'react-native';\nimport { Animated, Pressable, View as RNView, TextInput } from 'react-native';\n\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport interface EntryRowProps\n extends Omit<TextInputProps, 'style' | 'value' | 'defaultValue' | 'editable' | 'testID'> {\n /**\n * Acts as a floating label: shown small above the input once the field has\n * content or focus, and as the placeholder while it's empty and unfocused.\n */\n title: string;\n /** Controlled value. Omit for an uncontrolled field. */\n value?: string;\n /** Initial value when uncontrolled. */\n defaultValue?: string;\n /** Called when the input value changes. */\n onValueChange?: (value: string) => void;\n /** Icon or widget placed at the leading edge. */\n leading?: ReactNode;\n /** Icon or widget placed at the trailing edge (e.g. a clear or reveal button). */\n trailing?: ReactNode;\n disabled?: boolean;\n /**\n * Applied to the row, not the input — the convention every other component\n * in this package follows. Reach the field itself with the accessible name\n * (`getByLabelText(title)`).\n */\n testID?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/** `.labelFloated { top: 6px }`. */\nconst FLOATED_TOP = 6;\n\n/**\n * Row with an inline text entry field, mirroring `AdwEntryRow` and\n * `@gnome-ui/react`'s own `EntryRow`. The `title` rises above the input as a\n * small label once the field is focused or has content, and stands in for\n * the placeholder until then. Use inside a `BoxedList` for settings that\n * take free-form text.\n *\n * The float is one JS-driven `Animated.Value` (`useNativeDriver: false`):\n * `fontSize` is part of the transition and can't be native-driven, and\n * mixing a native with a JS value on one component throws — the same\n * trade-off `Expander` and `InlineViewSwitcher` already accepted.\n * `useReducedMotion()` snaps between the two states instead.\n *\n * **The label's travel is measured, not hardcoded.** The web can express its\n * resting position as `top: 50%; transform: translateY(-50%)` and its\n * floated one as `top: 6px`, but RN can't interpolate between a percentage\n * and a fixed offset, so the row reports its own height through `onLayout`\n * and the distance is derived from it. That also keeps the label centred if\n * a consumer makes the row taller than the 56 dp minimum.\n *\n * The `:focus` inset ring is dropped rather than approximated. `TextField`'s\n * own precedent — recolor the border on focus — doesn't transfer, because an\n * `EntryRow` has no border of its own to recolor: it's a row inside a\n * `BoxedList`, and adding one would shift the list's geometry. On a touch\n * device the state is already unmistakable anyway: the label floats up, the\n * text fades in, and the keyboard opens. The `prefers-contrast: more` block\n * is a variation on that same ring, so it goes with it.\n *\n * Tapping anywhere on the row focuses the field, the same affordance the web\n * version's row-level `onClick` provides.\n *\n * The visible label is hidden from assistive tech and the `title` becomes\n * the input's `accessibilityLabel` instead. On the web the two are bound by\n * `<label htmlFor>`, which RN has no equivalent for — left as-is, the label\n * would be announced as loose text next to an unnamed field. `testID` lands\n * on the row rather than the input (unlike the web version, which spreads\n * every remaining prop onto the `<input>`), matching what every other\n * component in this package does; reach the field itself by its accessible\n * name.\n *\n * @example\n * const [name, setName] = useState('');\n *\n * <BoxedList>\n * <EntryRow title=\"Display name\" value={name} onValueChange={setName} />\n * </BoxedList>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.EntryRow.html\n */\nexport const EntryRow = forwardRef<RNTextInput, EntryRowProps>(function EntryRow(\n {\n title,\n value: controlledValue,\n defaultValue = '',\n onValueChange,\n onChangeText,\n leading,\n trailing,\n disabled = false,\n onFocus,\n onBlur,\n accessibilityLabel,\n testID,\n style,\n ...inputProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const isControlled = controlledValue !== undefined;\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const inputValue = isControlled ? controlledValue : uncontrolledValue;\n\n const [focused, setFocused] = useState(false);\n const [fieldHeight, setFieldHeight] = useState(56);\n const inputRef = useRef<RNTextInput | null>(null);\n\n const floated = focused || inputValue.length > 0;\n const float = useRef(new Animated.Value(floated ? 1 : 0)).current;\n\n useEffect(() => {\n if (reducedMotion) {\n float.setValue(floated ? 1 : 0);\n return;\n }\n\n const animation = Animated.timing(float, {\n toValue: floated ? 1 : 0,\n duration: theme.durationFast,\n useNativeDriver: false,\n });\n\n animation.start();\n\n return () => animation.stop();\n }, [floated, reducedMotion, float, theme.durationFast]);\n\n const restingLineHeight = Math.round(theme.fontSizeBody * theme.lineHeightBody);\n const floatedLineHeight = Math.round(theme.fontSizeCaption * theme.lineHeightBody);\n // Centred resting position → `FLOATED_TOP`, both expressed as the label's\n // own top edge so the travel is a single translateY.\n const restingTop = (fieldHeight - restingLineHeight) / 2;\n\n return (\n <Pressable\n accessible={false}\n testID={testID}\n disabled={disabled}\n onPress={() => inputRef.current?.focus()}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingHorizontal: theme.space4,\n minHeight: 56,\n width: '100%',\n opacity: disabled ? theme.opacityDisabled : 1,\n },\n style,\n ]}\n >\n {leading ? <RNView style={{ flexShrink: 0 }}>{leading}</RNView> : null}\n\n <RNView\n onLayout={(event: LayoutChangeEvent) => setFieldHeight(event.nativeEvent.layout.height)}\n style={{ flex: 1, justifyContent: 'center', minHeight: 56 }}\n >\n <Animated.Text\n pointerEvents=\"none\"\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n numberOfLines={1}\n style={{\n position: 'absolute',\n left: 0,\n right: 0,\n top: restingTop,\n fontFamily: theme.fontFamily,\n color: theme.windowFgColor,\n fontSize: float.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.fontSizeBody, theme.fontSizeCaption],\n }),\n lineHeight: float.interpolate({\n inputRange: [0, 1],\n outputRange: [restingLineHeight, floatedLineHeight],\n }),\n opacity: float.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.opacityDim, 0.7],\n }),\n transform: [\n {\n translateY: float.interpolate({\n inputRange: [0, 1],\n outputRange: [0, FLOATED_TOP - restingTop],\n }),\n },\n ],\n }}\n >\n {title}\n </Animated.Text>\n\n <Animated.View style={{ opacity: float }}>\n <TextInput\n ref={(node) => {\n inputRef.current = node;\n\n if (typeof ref === 'function') {\n ref(node);\n } else if (ref) {\n ref.current = node;\n }\n }}\n value={inputValue}\n editable={!disabled}\n accessibilityLabel={accessibilityLabel ?? title}\n onFocus={(event) => {\n setFocused(true);\n onFocus?.(event);\n }}\n onBlur={(event) => {\n setFocused(false);\n onBlur?.(event);\n }}\n onChangeText={(next) => {\n if (!isControlled) {\n setUncontrolledValue(next);\n }\n\n onValueChange?.(next);\n onChangeText?.(next);\n }}\n style={{\n padding: 0,\n margin: 0,\n width: '100%',\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n lineHeight: restingLineHeight,\n color: theme.cardFgColor,\n paddingTop: theme.space4,\n paddingBottom: 4,\n }}\n {...inputProps}\n />\n </Animated.View>\n </RNView>\n\n {trailing ? (\n <RNView style={{ flexDirection: 'row', alignItems: 'center', gap: theme.space1 }}>\n {trailing}\n </RNView>\n ) : null}\n </Pressable>\n );\n});\n","import { PanEnd } from '@gnome-ui/icons';\nimport { type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable, View } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport interface ExpanderProps {\n /** Clickable header label. String children render as a themed `Text` label; other nodes render as-is. */\n label: ReactNode;\n /** Content revealed when expanded. */\n children?: ReactNode;\n /** Controlled expanded state. */\n expanded?: boolean;\n /** Initial expanded state when uncontrolled. Defaults to `false`. */\n defaultExpanded?: boolean;\n /** Called when the expanded state changes. */\n onExpandedChange?: (expanded: boolean) => void;\n /** Disables the toggle. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Standalone disclosure triangle + collapsible content — mirrors\n * `GtkExpander` and `@gnome-ui/react`'s `Expander`.\n *\n * A bare, unstyled counterpart to `ExpanderRow`. Use `Expander` outside a\n * settings-row context, e.g. \"Show advanced options\" in a form, or \"Show\n * details\" under an error message.\n *\n * The web version clips the panel with a CSS grid-height animation and\n * rides the content's `padding-top` on a second, separate transition (so a\n * collapsed expander doesn't reserve blank space for padding that belongs\n * to hidden content). RN has no CSS grid to lean on, so the panel is a\n * single `Animated.View` whose numeric `height` is driven directly —\n * `useNativeDriver: false`, the same accepted (and documented) trade-off\n * `Checkbox`/`RadioButton`/`Switch`/`AnimatedIcon` already make for\n * non-transform properties. Because the inner content `View`'s `onLayout`\n * measurement already includes its own `paddingTop`, animating that single\n * height value reproduces the web version's two-transition result with one\n * `Animated.Value` instead of two. Content stays mounted while collapsed\n * (matching the web version's `inert`, unmounted-from-the-tab-order-only\n * behavior) — `accessibilityElementsHidden` / `importantForAccessibility`\n * is RN's closest equivalent, applied to the panel wrapper.\n *\n * On first mount with `defaultExpanded`, the panel briefly renders at its\n * natural (unmeasured) height instead of the animated value, so the initial\n * reveal doesn't start from a wrong, pre-measurement `0` and pop once\n * `onLayout` resolves — the same \"let layout report the truth once, then\n * hand control to `Animated`\" shape `Dropdown`/`Tooltip` use for their own\n * `onLayout`-measured dimensions.\n *\n * The chevron is `PanEnd` (GNOME's own `pan-end-symbolic` disclosure\n * triangle) rather than a chevron glyph, matching the web version's\n * hand-drawn arrow shape. It rotates 0deg → 90deg on an `Animated.Value`\n * (the same `interpolate`-to-`rotate` recipe `Spinner` uses for its own\n * indeterminate spin), unlike `Dropdown`'s chevron, which snaps instantly\n * since that panel's own reveal has no comparable \"settle\" affordance.\n *\n * `role=\"region\"` on the panel and `accessibilityState={{ expanded }}` on\n * the header port 1:1 from the web version's `aria-controls`/`aria-expanded`\n * pair — RN has no DOM ids, so the id-based `aria-labelledby`/`aria-controls`\n * relationship itself has no port, the same `ProgressBar`/`LevelBar`\n * precedent for dropping id-relationship-only ARIA props.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Expander.html\n */\nexport const Expander = ({\n label,\n children,\n expanded: controlledExpanded,\n defaultExpanded = false,\n onExpandedChange,\n disabled = false,\n style,\n testID,\n}: ExpanderProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const isControlled = controlledExpanded !== undefined;\n const [uncontrolledExpanded, setUncontrolledExpanded] = useState(defaultExpanded);\n const expanded = isControlled ? controlledExpanded : uncontrolledExpanded;\n\n const [contentHeight, setContentHeight] = useState(0);\n const hasMeasured = useRef(false);\n const heightAnim = useRef(new Animated.Value(0)).current;\n const chevronAnim = useRef(new Animated.Value(defaultExpanded ? 1 : 0)).current;\n\n const toggle = () => {\n if (disabled) {\n return;\n }\n\n const next = !expanded;\n\n if (!isControlled) {\n setUncontrolledExpanded(next);\n }\n\n onExpandedChange?.(next);\n };\n\n const handleContentLayout = (e: LayoutChangeEvent) => {\n const h = e.nativeEvent.layout.height;\n\n if (h === contentHeight) {\n hasMeasured.current = true;\n\n return;\n }\n\n if (expanded && !hasMeasured.current) {\n heightAnim.setValue(h);\n }\n\n hasMeasured.current = true;\n setContentHeight(h);\n };\n\n useEffect(() => {\n const [x1, y1, x2, y2] = theme.easingDefault;\n const easing = Easing.bezier(x1, y1, x2, y2);\n const target = expanded ? contentHeight : 0;\n\n if (reducedMotion) {\n heightAnim.setValue(target);\n chevronAnim.setValue(expanded ? 1 : 0);\n\n return;\n }\n\n Animated.parallel([\n Animated.timing(heightAnim, {\n toValue: target,\n duration: theme.durationNormal,\n easing,\n useNativeDriver: false,\n }),\n Animated.timing(chevronAnim, {\n toValue: expanded ? 1 : 0,\n duration: theme.durationFast,\n easing,\n useNativeDriver: true,\n }),\n ]).start();\n }, [\n expanded,\n contentHeight,\n reducedMotion,\n heightAnim,\n chevronAnim,\n theme.durationNormal,\n theme.durationFast,\n theme.easingDefault,\n ]);\n\n const showsNaturalHeight = expanded && !hasMeasured.current;\n const rotate = chevronAnim.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '90deg'] });\n\n return (\n <View testID={testID} style={[{ width: '100%' }, style]}>\n <Pressable\n role=\"button\"\n disabled={disabled}\n accessibilityState={{ expanded, disabled }}\n onPress={toggle}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n alignSelf: 'flex-start',\n gap: theme.space2,\n padding: theme.space1,\n margin: -theme.space1,\n borderRadius: theme.radiusSm,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n opacity: disabled ? theme.opacityDisabled : 1,\n })}\n >\n <Animated.View style={{ opacity: 0.55, transform: [{ rotate }] }}>\n <Icon icon={PanEnd} size=\"md\" />\n </Animated.View>\n\n {typeof label === 'string' ? (\n <Text style={{ color: theme.windowFgColor }}>{label}</Text>\n ) : (\n label\n )}\n </Pressable>\n\n <Animated.View\n style={{ height: showsNaturalHeight ? undefined : heightAnim, overflow: 'hidden' }}\n accessibilityElementsHidden={!expanded}\n importantForAccessibility={expanded ? 'yes' : 'no'}\n >\n <View\n accessible\n role=\"region\"\n onLayout={handleContentLayout}\n style={{ paddingTop: theme.space2 }}\n >\n {children}\n </View>\n </Animated.View>\n </View>\n );\n};\n","import { PanDown } from '@gnome-ui/icons';\nimport { Children, forwardRef, type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, TextStyle, View, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable, View as RNView } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { Separator } from '@/components/Separator';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport interface ExpanderRowProps {\n /** Primary label. */\n title: string;\n /** Secondary line below the title. */\n subtitle?: string;\n /** Icon or image placed at the leading edge of the header row. */\n leading?: ReactNode;\n /**\n * Widget placed at the trailing edge of the header row, before the\n * chevron (e.g. a value label or a `Switch`). Stop event propagation\n * inside it so the row's toggle isn't triggered.\n */\n trailing?: ReactNode;\n /**\n * Nested rows revealed when expanded. Use `ActionRow`, `ButtonRow`, or\n * any row-shaped element — separators are inserted automatically.\n */\n children?: ReactNode;\n /** Controlled expanded state. */\n expanded?: boolean;\n /** Initial expanded state when uncontrolled. Defaults to `false`. */\n defaultExpanded?: boolean;\n /** Called when the expanded state changes. */\n onExpandedChange?: (expanded: boolean) => void;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Collapsible `ActionRow` that reveals nested rows on activation, mirroring\n * `@gnome-ui/react`'s `ExpanderRow`. The header row toggles a smooth reveal\n * animation exposing child rows. Supports both controlled (`expanded`) and\n * uncontrolled (`defaultExpanded`) modes.\n *\n * The reveal panel reuses the standalone `Expander`'s exact recipe — a\n * single `Animated.View` with a directly-driven numeric `height` (RN has no\n * CSS grid to lean on for the web version's `grid-template-rows: 0fr → 1fr`\n * trick), content staying mounted while collapsed\n * (`accessibilityElementsHidden`/`importantForAccessibility` standing in\n * for the web's `inert`), and the \"show natural height until first\n * `onLayout` measurement lands, then hand control to `Animated`\" guard for\n * a `defaultExpanded` initial mount. The only difference from `Expander`\n * itself: the chevron is `PanDown` (a straight down-arrow, matching the\n * source CSS's hand-drawn `M4 6l4 4 4-4` path and rotating 0deg → 180deg)\n * rather than `Expander`'s `PanEnd` triangle rotating 0deg → 90deg — the\n * same icon/rotation pair `Dropdown`'s own chevron already established.\n *\n * Nested children get a `Separator` inserted before each one (including\n * the first, directly under the header) via `Children.toArray(children)\n * .filter(Boolean)`, the same falsy-child-filtering `ExpanderRow`'s web\n * version already does. Unlike `CheckRow`/`ButtonRow`, this component has\n * no `disabled` prop — the source `@gnome-ui/react` version doesn't expose\n * one either, so none is added here.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ExpanderRow.html\n */\nexport const ExpanderRow = forwardRef<View, ExpanderRowProps>(function ExpanderRow(\n {\n title,\n subtitle,\n leading,\n trailing,\n children,\n expanded: controlledExpanded,\n defaultExpanded = false,\n onExpandedChange,\n style,\n testID,\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const isControlled = controlledExpanded !== undefined;\n const [uncontrolledExpanded, setUncontrolledExpanded] = useState(defaultExpanded);\n const expanded = isControlled ? controlledExpanded : uncontrolledExpanded;\n\n const [contentHeight, setContentHeight] = useState(0);\n const hasMeasured = useRef(false);\n const heightAnim = useRef(new Animated.Value(0)).current;\n const chevronAnim = useRef(new Animated.Value(defaultExpanded ? 1 : 0)).current;\n\n const toggle = () => {\n const next = !expanded;\n\n if (!isControlled) {\n setUncontrolledExpanded(next);\n }\n\n onExpandedChange?.(next);\n };\n\n const handleContentLayout = (e: LayoutChangeEvent) => {\n const h = e.nativeEvent.layout.height;\n\n if (h === contentHeight) {\n hasMeasured.current = true;\n\n return;\n }\n\n if (expanded && !hasMeasured.current) {\n heightAnim.setValue(h);\n }\n\n hasMeasured.current = true;\n setContentHeight(h);\n };\n\n useEffect(() => {\n const [x1, y1, x2, y2] = theme.easingDefault;\n const easing = Easing.bezier(x1, y1, x2, y2);\n const target = expanded ? contentHeight : 0;\n\n if (reducedMotion) {\n heightAnim.setValue(target);\n chevronAnim.setValue(expanded ? 1 : 0);\n\n return;\n }\n\n Animated.parallel([\n Animated.timing(heightAnim, {\n toValue: target,\n duration: theme.durationNormal,\n easing,\n useNativeDriver: false,\n }),\n Animated.timing(chevronAnim, {\n toValue: expanded ? 1 : 0,\n duration: theme.durationFast,\n easing,\n useNativeDriver: true,\n }),\n ]).start();\n }, [\n expanded,\n contentHeight,\n reducedMotion,\n heightAnim,\n chevronAnim,\n theme.durationNormal,\n theme.durationFast,\n theme.easingDefault,\n ]);\n\n const showsNaturalHeight = expanded && !hasMeasured.current;\n const rotate = chevronAnim.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '180deg'] });\n const childItems = Children.toArray(children).filter(Boolean);\n\n return (\n <RNView ref={ref} testID={testID} style={[{ width: '100%' }, style]}>\n <Pressable\n role=\"button\"\n accessibilityState={{ expanded }}\n onPress={toggle}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: theme.space2,\n paddingHorizontal: theme.space4,\n minHeight: 52,\n width: '100%',\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n })}\n >\n {leading && <RNView style={{ flexShrink: 0 }}>{leading}</RNView>}\n\n <RNView style={{ flex: 1, gap: 2 }}>\n <Text\n variant=\"body\"\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n color: theme.cardFgColor,\n fontWeight: String(theme.fontWeightNormal) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n {subtitle && (\n <Text variant=\"caption\" color=\"dim\" numberOfLines={1} ellipsizeMode=\"tail\">\n {subtitle}\n </Text>\n )}\n </RNView>\n\n {trailing && (\n <RNView\n style={{ flexShrink: 0, flexDirection: 'row', alignItems: 'center', gap: theme.space1 }}\n >\n {trailing}\n </RNView>\n )}\n\n <Animated.View style={{ opacity: 0.55, transform: [{ rotate }] }}>\n <Icon icon={PanDown} size=\"md\" />\n </Animated.View>\n </Pressable>\n\n <Animated.View\n style={{ height: showsNaturalHeight ? undefined : heightAnim, overflow: 'hidden' }}\n accessibilityElementsHidden={!expanded}\n importantForAccessibility={expanded ? 'yes' : 'no'}\n >\n <RNView accessible role=\"region\" onLayout={handleContentLayout}>\n {childItems.map((child, i) => (\n <RNView key={i}>\n <Separator testID=\"expander-row-separator\" />\n {child}\n </RNView>\n ))}\n </RNView>\n </Animated.View>\n </RNView>\n );\n});\n","import type { ReactNode } from 'react';\nimport type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native';\nimport { View } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface FieldGroupProps extends Omit<ViewProps, 'style'> {\n /** Group heading, rendered above the grouped content. */\n label: string;\n /** Helper text shown below the label. Hidden when `error` is set. */\n helperText?: string;\n /**\n * Error message shown below the label in place of `helperText`.\n * Announced via `role=\"alert\"` when it appears.\n */\n error?: string;\n /**\n * Visually dims the group. Unlike the web version's native\n * `<fieldset disabled>`, this does not automatically disable descendant\n * controls — RN has no equivalent of a fieldset's built-in\n * disabled-propagates-to-descendants behavior, so each child control\n * must still be disabled individually.\n */\n disabled?: boolean;\n /** Arbitrary field content — checkboxes, radios, a custom composite field, etc. */\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Generic form-field grouping with a shared label, help text, and error\n * message, for arbitrary fields outside a `BoxedList`. Mirrors\n * `@gnome-ui/react`'s `FieldGroup`.\n *\n * `PreferencesGroup` is scoped specifically to wrapping settings rows\n * inside a `BoxedList` — use `FieldGroup` for a plain labeled grouping\n * around any set of related form controls (e.g. a `RadioButton` group or\n * several `Checkbox`es sharing one label and error), independent of the\n * settings-page layout.\n *\n * `<fieldset>`/`<legend>` have no RN element equivalent — this renders as\n * a plain `View` with `role=\"group\"` (the closest match to a fieldset's\n * implicit ARIA role; RN's `Role` union has it natively, no substitution\n * needed) and a themed `Text` label in place of the `<legend>`. The web\n * version's `aria-describedby` (an id-relationship prop) has no RN\n * equivalent and is dropped, the same \"no relationship attribute\" gap\n * `ProgressBar`/`LevelBar`/`Expander` already established. The hint/error\n * text reuses `TextField`'s exact recipe verbatim — `error ?? helperText`\n * picks the message, `error ? theme.errorColor : theme.windowFgColor` plus\n * `error ? 1 : theme.opacityDim` picks the color/opacity — rather than\n * re-deriving the same dim-vs-error styling logic independently.\n * `disabled` only dims the group visually (`theme.opacityDisabled`); see\n * the prop doc for why it can't also disable descendants automatically.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/\n */\nexport const FieldGroup = ({\n label,\n helperText,\n error,\n disabled = false,\n children,\n style,\n ...viewProps\n}: FieldGroupProps) => {\n const theme = useGnomeTheme();\n const hint = error ?? helperText;\n\n return (\n <View\n role=\"group\"\n style={[{ gap: theme.space1, opacity: disabled ? theme.opacityDisabled : 1 }, style]}\n {...viewProps}\n >\n <Text\n variant=\"body\"\n style={{\n color: theme.windowFgColor,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n }}\n >\n {label}\n </Text>\n\n {hint && (\n <Text\n role={error ? 'alert' : undefined}\n variant=\"caption\"\n style={{\n color: error ? theme.errorColor : theme.windowFgColor,\n opacity: error ? 1 : theme.opacityDim,\n }}\n >\n {hint}\n </Text>\n )}\n\n <View style={{ gap: theme.space2 }}>{children}</View>\n </View>\n );\n};\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport {\n ApplicationXExecutable,\n AudioXGeneric,\n Document,\n Folder,\n FontXGeneric,\n ImageXGeneric,\n PackageXGeneric,\n TextXGeneric,\n VideoXGeneric,\n XOfficeDocument,\n XOfficePresentation,\n XOfficeSpreadsheet,\n} from '@gnome-ui/icons';\n\nexport type FileTypeCategory =\n | 'folder'\n | 'image'\n | 'audio'\n | 'video'\n | 'text'\n | 'pdf'\n | 'archive'\n | 'document'\n | 'spreadsheet'\n | 'presentation'\n | 'font'\n | 'executable'\n | 'unknown';\n\nconst CATEGORY_ICON: Record<FileTypeCategory, IconDefinition> = {\n folder: Folder,\n image: ImageXGeneric,\n audio: AudioXGeneric,\n video: VideoXGeneric,\n text: TextXGeneric,\n pdf: Document,\n archive: PackageXGeneric,\n document: XOfficeDocument,\n spreadsheet: XOfficeSpreadsheet,\n presentation: XOfficePresentation,\n font: FontXGeneric,\n executable: ApplicationXExecutable,\n unknown: TextXGeneric,\n};\n\nconst CATEGORY_LABEL: Record<FileTypeCategory, string> = {\n folder: 'Folder',\n image: 'Image file',\n audio: 'Audio file',\n video: 'Video file',\n text: 'Text file',\n pdf: 'PDF document',\n archive: 'Archive',\n document: 'Document',\n spreadsheet: 'Spreadsheet',\n presentation: 'Presentation',\n font: 'Font file',\n executable: 'Executable',\n unknown: 'File',\n};\n\nconst EXTENSION_CATEGORY: Record<string, FileTypeCategory> = {\n // Image\n png: 'image',\n jpg: 'image',\n jpeg: 'image',\n gif: 'image',\n webp: 'image',\n svg: 'image',\n bmp: 'image',\n ico: 'image',\n tiff: 'image',\n heic: 'image',\n // Audio\n mp3: 'audio',\n wav: 'audio',\n ogg: 'audio',\n flac: 'audio',\n m4a: 'audio',\n aac: 'audio',\n // Video\n mp4: 'video',\n mov: 'video',\n avi: 'video',\n mkv: 'video',\n webm: 'video',\n m4v: 'video',\n // PDF\n pdf: 'pdf',\n // Archive\n zip: 'archive',\n tar: 'archive',\n gz: 'archive',\n rar: 'archive',\n '7z': 'archive',\n xz: 'archive',\n // Office document\n doc: 'document',\n docx: 'document',\n odt: 'document',\n rtf: 'document',\n // Spreadsheet\n xls: 'spreadsheet',\n xlsx: 'spreadsheet',\n ods: 'spreadsheet',\n csv: 'spreadsheet',\n // Presentation\n ppt: 'presentation',\n pptx: 'presentation',\n odp: 'presentation',\n // Font\n ttf: 'font',\n otf: 'font',\n woff: 'font',\n woff2: 'font',\n // Executable\n exe: 'executable',\n app: 'executable',\n sh: 'executable',\n bin: 'executable',\n // Text / code — freedesktop groups source code under the generic text-x-generic icon.\n txt: 'text',\n md: 'text',\n log: 'text',\n json: 'text',\n yaml: 'text',\n yml: 'text',\n toml: 'text',\n xml: 'text',\n html: 'text',\n css: 'text',\n js: 'text',\n jsx: 'text',\n ts: 'text',\n tsx: 'text',\n py: 'text',\n rb: 'text',\n go: 'text',\n rs: 'text',\n c: 'text',\n cpp: 'text',\n h: 'text',\n java: 'text',\n sql: 'text',\n};\n\nconst MIME_TOP_LEVEL_CATEGORY: Partial<Record<string, FileTypeCategory>> = {\n image: 'image',\n audio: 'audio',\n video: 'video',\n text: 'text',\n font: 'font',\n};\n\nconst MIME_SUBTYPE_CATEGORY: Record<string, FileTypeCategory> = {\n pdf: 'pdf',\n zip: 'archive',\n gzip: 'archive',\n 'x-tar': 'archive',\n 'x-7z-compressed': 'archive',\n 'x-rar-compressed': 'archive',\n 'vnd.rar': 'archive',\n msword: 'document',\n 'vnd.openxmlformats-officedocument.wordprocessingml.document': 'document',\n 'vnd.oasis.opendocument.text': 'document',\n 'vnd.ms-excel': 'spreadsheet',\n 'vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'spreadsheet',\n 'vnd.oasis.opendocument.spreadsheet': 'spreadsheet',\n 'vnd.ms-powerpoint': 'presentation',\n 'vnd.openxmlformats-officedocument.presentationml.presentation': 'presentation',\n 'vnd.oasis.opendocument.presentation': 'presentation',\n 'x-sh': 'executable',\n 'x-executable': 'executable',\n};\n\n/** Resolves a file-type category from a MIME type (e.g. `\"image/png\"`). */\nexport function categoryFromMimeType(mimeType: string): FileTypeCategory | null {\n const normalized = mimeType.trim().toLowerCase();\n\n if (normalized === 'inode/directory') {\n return 'folder';\n }\n\n const [topLevel, subtype] = normalized.split('/');\n\n if (subtype && MIME_SUBTYPE_CATEGORY[subtype]) {\n return MIME_SUBTYPE_CATEGORY[subtype];\n }\n\n return MIME_TOP_LEVEL_CATEGORY[topLevel] ?? null;\n}\n\n/** Resolves a file-type category from a file name's extension (e.g. `\"report.pdf\"`). */\nexport function categoryFromName(name: string): FileTypeCategory | null {\n const trimmed = name.trim().replace(/^\\.+/, '');\n const lastDot = trimmed.lastIndexOf('.');\n\n if (lastDot <= 0) {\n return null;\n }\n\n const extension = trimmed.slice(lastDot + 1).toLowerCase();\n\n return EXTENSION_CATEGORY[extension] ?? null;\n}\n\nexport function getFileTypeIcon(category: FileTypeCategory): IconDefinition {\n return CATEGORY_ICON[category];\n}\n\nexport function getFileTypeLabel(category: FileTypeCategory): string {\n return CATEGORY_LABEL[category];\n}\n","import type { StyleProp, ViewStyle } from 'react-native';\nimport { Image, View } from 'react-native';\n\nimport { Icon, type IconSize } from '@/components/Icon';\nimport { ICON_SIZE_MAP } from '@/components/Icon/shared';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nimport {\n categoryFromMimeType,\n categoryFromName,\n getFileTypeIcon,\n getFileTypeLabel,\n} from './fileType';\n\nexport interface FileTypeIconProps {\n /** File name (e.g. `\"report.pdf\"`) — resolves the icon from its extension. */\n name?: string;\n /**\n * MIME type (e.g. `\"application/pdf\"`, `\"inode/directory\"`).\n * Takes precedence over `name` when both are provided.\n */\n mimeType?: string;\n /** Renders the folder icon regardless of `name`/`mimeType`. */\n isFolder?: boolean;\n /** Thumbnail image URL. When provided, renders the image instead of the resolved icon. */\n thumbnail?: string;\n /** Accessible label. Defaults to a generated description (e.g. `\"PDF document\"`). */\n label?: string;\n /** Icon size. Defaults to `\"md\"`. */\n size?: IconSize;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Small icon — optionally a thumbnail — resolved from a file's MIME type\n * or name extension. Useful for file-manager-style listings. Mirrors\n * `@gnome-ui/react`'s `FileTypeIcon`.\n *\n * Falls back to the generic file icon (mirrors freedesktop's\n * `text-x-generic`) when the type can't be resolved.\n *\n * `fileType.ts`'s category-resolution logic (MIME type / extension → one of\n * 13 categories, plus the freedesktop icon and generated label per\n * category) is pure, DOM-free TS — duplicated verbatim from\n * `@gnome-ui/react` rather than imported cross-package, the same\n * `isIconDefinition`/`Icon.tsx` precedent already established for\n * DOM-independent logic that still isn't worth a shared package for one\n * function's worth of code.\n *\n * `role=\"img\"` + `accessibilityLabel` ports 1:1 (the same `Avatar`/\n * `LevelBar` precedent for RN's newer web-aligned `Role` union). The\n * thumbnail reuses `Avatar`'s own `Image`/`resizeMode=\"cover\"` recipe,\n * sized from `Icon`'s own `ICON_SIZE_MAP` so swapping between the resolved\n * icon and a thumbnail never shifts layout — the same reasoning the web\n * version's `.sm`/`.md`/`.lg` classes document.\n */\nexport const FileTypeIcon = ({\n name,\n mimeType,\n isFolder = false,\n thumbnail,\n label,\n size = 'md',\n style,\n testID,\n}: FileTypeIconProps) => {\n const theme = useGnomeTheme();\n const box = ICON_SIZE_MAP[size];\n\n const category = isFolder\n ? 'folder'\n : (mimeType && categoryFromMimeType(mimeType)) || (name && categoryFromName(name)) || 'unknown';\n\n const resolvedLabel = label ?? getFileTypeLabel(category);\n\n return (\n <View\n testID={testID}\n accessible\n role=\"img\"\n accessibilityLabel={resolvedLabel}\n style={[{ width: box, height: box, alignItems: 'center', justifyContent: 'center' }, style]}\n >\n {thumbnail ? (\n <Image\n testID={testID ? `${testID}-thumbnail` : undefined}\n source={{ uri: thumbnail }}\n resizeMode=\"cover\"\n style={{ width: box, height: box, borderRadius: theme.radiusSm }}\n />\n ) : (\n <Icon icon={getFileTypeIcon(category)} size={size} />\n )}\n </View>\n );\n};\n","import { PanDown, Search } from '@gnome-ui/icons';\nimport { useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Animated, Dimensions, Modal, Pressable, ScrollView, TextInput, View } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport type { MultiSelectDropdownOption } from '@/components/MultiSelectDropdown';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport interface FilterableMultiSelectDropdownProps<V extends string = string> {\n /** The list of selectable options. */\n options: MultiSelectDropdownOption<V>[];\n /** The currently selected values. */\n value: V[];\n /** Called with the full updated selection whenever an option is toggled. */\n onChange: (value: V[]) => void;\n /** Placeholder shown on the trigger when no option is selected. */\n placeholder?: string;\n /** Placeholder for the filter field shown once the list is open. */\n filterPlaceholder?: string;\n /** Accessible label for the control. */\n accessibilityLabel?: string;\n /** Disables the entire control. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\ninterface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface Position {\n top: number;\n left: number;\n width: number;\n flipUp: boolean;\n}\n\nconst GAP = 4;\nconst MARGIN = 8;\nconst MAX_PANEL_HEIGHT = 320;\n\nfunction computePosition(trigger: Rect, panelHeight: number): Position {\n const { height: vh } = Dimensions.get('window');\n const spaceBelow = vh - (trigger.y + trigger.height);\n const flipUp = spaceBelow < panelHeight && trigger.y > panelHeight;\n const top = flipUp ? trigger.y - panelHeight - GAP : trigger.y + trigger.height + GAP;\n\n return {\n top: Math.max(MARGIN, Math.min(top, vh - panelHeight - MARGIN)),\n left: trigger.x,\n width: trigger.width,\n flipUp,\n };\n}\n\nfunction summarize(count: number, selected: { label: string }[]): string {\n if (count === 1) {\n return selected[0].label;\n }\n\n return `${count} selected`;\n}\n\nfunction filterOptions<V extends string>(\n options: MultiSelectDropdownOption<V>[],\n query: string,\n): MultiSelectDropdownOption<V>[] {\n const normalized = query.trim().toLowerCase();\n\n if (!normalized) {\n return options;\n }\n\n return options.filter(\n (o) =>\n o.label.toLowerCase().includes(normalized) ||\n o.description?.toLowerCase().includes(normalized),\n );\n}\n\n/**\n * `MultiSelectDropdown` plus a filter field for narrowing long option\n * lists, mirroring `@gnome-ui/react`'s `FilterableMultiSelectDropdown`.\n *\n * Same overall shape and positioning recipe as `MultiSelectDropdown` —\n * duplicated rather than composed, matching how the web source itself\n * relates to `MultiSelectDropdown` (a parallel implementation sharing only\n * the `MultiSelectDropdownOption` type, not a wrapper around it) — with a\n * filter `TextInput` pinned above the list, auto-focused on open via\n * `autoFocus`. Typing narrows `options` to those whose label or\n * description contains the query (case-insensitive); filtering only\n * affects what's shown, never the underlying selection — values selected\n * before a query hides their option stay selected. An empty filtered\n * result renders a centered \"No results\" message instead of the list.\n *\n * The web version's filter-field keyboard handler (↑/↓ roving highlight,\n * Home/End, Enter-to-toggle) has no RN port — same \"RN's touch-first model\n * has no keyboard focus to drive it\" reasoning `Dropdown`/`TabBar` already\n * established; RN's own `TextInput` still handles the actual typing (and\n * its software keyboard) natively, only the roving-highlight navigation on\n * top of it is dropped. Selection is by direct tap only, same as\n * `MultiSelectDropdown`.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/drop-down-lists.html\n */\nexport const FilterableMultiSelectDropdown = <V extends string = string>({\n options,\n value,\n onChange,\n placeholder = 'Select options',\n filterPlaceholder = 'Filter options…',\n accessibilityLabel,\n disabled,\n style,\n testID,\n}: FilterableMultiSelectDropdownProps<V>) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState('');\n const [triggerRect, setTriggerRect] = useState<Rect | null>(null);\n const [panelHeight, setPanelHeight] = useState<number | null>(null);\n const [pos, setPos] = useState<Position | null>(null);\n\n const triggerRef = useRef<View>(null);\n const progress = useRef(new Animated.Value(0)).current;\n\n const selectedOptions = options.filter((o) => value.includes(o.value));\n const filteredOptions = filterOptions(options, query);\n\n const openList = () => {\n if (disabled) {\n return;\n }\n\n setQuery('');\n setOpen(true);\n };\n\n const closeList = () => setOpen(false);\n\n const toggleOption = (opt: MultiSelectDropdownOption<V>) => {\n if (opt.disabled) {\n return;\n }\n\n onChange(\n value.includes(opt.value) ? value.filter((v) => v !== opt.value) : [...value, opt.value],\n );\n };\n\n useEffect(() => {\n if (!open) {\n setTriggerRect(null);\n setPanelHeight(null);\n setPos(null);\n\n return;\n }\n\n triggerRef.current?.measureInWindow((x, y, width, height) => {\n setTriggerRect({ x, y, width, height });\n });\n }, [open]);\n\n useEffect(() => {\n if (triggerRect === null || panelHeight === null) {\n return;\n }\n\n setPos(computePosition(triggerRect, panelHeight));\n }, [triggerRect, panelHeight]);\n\n useEffect(() => {\n if (!pos) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n progress.setValue(0);\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationFast,\n useNativeDriver: true,\n }).start();\n }, [pos, reducedMotion, progress, theme.durationFast]);\n\n const handlePanelLayout = (e: LayoutChangeEvent) => {\n setPanelHeight(Math.min(e.nativeEvent.layout.height, MAX_PANEL_HEIGHT));\n };\n\n return (\n <View style={style}>\n <Pressable\n ref={triggerRef}\n testID={testID}\n role=\"combobox\"\n accessibilityLabel={accessibilityLabel}\n accessibilityState={{ expanded: open, disabled: !!disabled }}\n disabled={disabled}\n onPress={() => (open ? closeList() : openList())}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: theme.space1,\n minHeight: 36,\n minWidth: 160,\n paddingVertical: 7,\n paddingHorizontal: theme.space2,\n backgroundColor: pressed ? theme.activeOverlay : theme.cardBgColor,\n borderWidth: 1,\n borderColor: open ? theme.accentColor : theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n opacity: disabled ? theme.opacityDisabled : 1,\n })}\n >\n <Text\n numberOfLines={1}\n style={{\n flex: 1,\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n opacity: selectedOptions.length === 0 ? theme.opacityDim : 1,\n }}\n >\n {selectedOptions.length === 0\n ? placeholder\n : summarize(selectedOptions.length, selectedOptions)}\n </Text>\n <Icon\n icon={PanDown}\n size=\"md\"\n color=\"default\"\n style={{ transform: [{ rotate: open ? '180deg' : '0deg' }] }}\n />\n </Pressable>\n\n <Modal visible={open} transparent animationType=\"none\" statusBarTranslucent>\n <Pressable\n onPress={closeList}\n accessible={false}\n style={{ flex: 1 }}\n testID={testID ? `${testID}-backdrop` : undefined}\n >\n <Animated.View\n onLayout={handlePanelLayout}\n style={[\n {\n position: 'absolute',\n maxHeight: MAX_PANEL_HEIGHT,\n backgroundColor: theme.popoverBgColor,\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 4 },\n shadowOpacity: 0.15,\n shadowRadius: 12,\n elevation: 6,\n },\n pos\n ? { top: pos.top, left: pos.left, width: pos.width, opacity: progress }\n : { top: -9999, left: -9999, width: 200, opacity: 0 },\n ]}\n >\n <View\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n padding: 8,\n borderBottomWidth: 1,\n borderBottomColor: theme.cardShadeColor,\n }}\n >\n <View style={{ position: 'absolute', left: 16, zIndex: 1 }}>\n <Icon\n icon={Search}\n size=\"sm\"\n color=\"default\"\n style={{ opacity: theme.opacityDim }}\n />\n </View>\n <TextInput\n value={query}\n onChangeText={setQuery}\n placeholder={filterPlaceholder}\n placeholderTextColor={theme.windowFgColor}\n accessibilityLabel={filterPlaceholder}\n autoFocus\n style={{\n flex: 1,\n paddingVertical: 6,\n paddingHorizontal: 8,\n paddingLeft: 28,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n color: theme.viewFgColor,\n backgroundColor: theme.viewBgColor,\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n }}\n />\n </View>\n\n {filteredOptions.length === 0 ? (\n <Text\n style={{\n marginVertical: 16,\n textAlign: 'center',\n color: theme.windowFgColor,\n opacity: theme.opacityDim,\n }}\n >\n No results\n </Text>\n ) : (\n <ScrollView role=\"list\" accessible bounces={false} style={{ paddingVertical: 4 }}>\n {filteredOptions.map((opt) => {\n const checked = value.includes(opt.value);\n\n return (\n <Pressable\n key={opt.value}\n role=\"option\"\n accessibilityState={{ selected: checked, disabled: !!opt.disabled }}\n disabled={opt.disabled}\n onPress={() => toggleOption(opt)}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n minHeight: 40,\n paddingVertical: 8,\n paddingHorizontal: theme.space2,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n opacity: opt.disabled ? theme.opacityDisabled : 1,\n })}\n >\n <View\n style={{\n width: 16,\n height: 16,\n flexShrink: 0,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusSm,\n borderWidth: 1.5,\n borderColor: checked ? theme.accentBgColor : theme.cardShadeColor,\n backgroundColor: checked ? theme.accentBgColor : 'transparent',\n }}\n >\n {checked && (\n <Text\n style={{\n color: '#fff',\n fontSize: 11,\n fontWeight: 'bold',\n lineHeight: 11,\n }}\n >\n {'✓'}\n </Text>\n )}\n </View>\n\n <View style={{ flex: 1, gap: 1 }}>\n <Text\n numberOfLines={1}\n style={{\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n fontWeight: checked\n ? (String(theme.fontWeightSemibold) as TextStyle['fontWeight'])\n : undefined,\n }}\n >\n {opt.label}\n </Text>\n {opt.description && (\n <Text\n numberOfLines={1}\n style={{\n fontSize: theme.fontSizeCaption,\n color: theme.windowFgColor,\n opacity: theme.opacityDim,\n }}\n >\n {opt.description}\n </Text>\n )}\n </View>\n </Pressable>\n );\n })}\n </ScrollView>\n )}\n </Animated.View>\n </Pressable>\n </Modal>\n </View>\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { StyleProp, TextStyle, View, ViewProps, ViewStyle } from 'react-native';\nimport { Text as RNText, View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface HeaderBarProps extends Omit<ViewProps, 'style'> {\n /** Centered title. Pass a string or a custom element. */\n title?: ReactNode;\n /** Controls placed at the leading (left) edge — back button, menu, etc. */\n start?: ReactNode;\n /** Controls placed at the trailing (right) edge — actions, overflow menu, etc. */\n end?: ReactNode;\n /**\n * When true the header bar blends into the window chrome (no bottom\n * border). Use for the topmost bar of a full-window layout.\n */\n flat?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Title bar with centered title and leading/trailing action slots.\n *\n * Mirrors the Adwaita `AdwHeaderBar` pattern. Use `flat` buttons\n * (`<Button variant=\"flat\">`) inside the header bar per GNOME HIG.\n *\n * Rebuilt with flexbox rather than ported from `@gnome-ui/react`'s CSS\n * Grid (`1fr auto 1fr`) — RN has no grid layout, but the same true-centering\n * effect (the title stays centered in the full width regardless of how\n * wide `start`/`end` are, rather than centering only in the leftover gap\n * between them) comes from giving both side slots `flex: 1` with the title\n * left unflexed in between, since both sides then always claim equal\n * leftover space. The web version's `<header>` element has no RN\n * equivalent; `accessibilityRole=\"header\"` is deliberately *not* applied to\n * the outer `View` since that role means \"text heading\" in RN's\n * accessibility tree (see `Text`'s heading variants), not \"landmark\n * container\" the way HTML's `<header>` does.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.HeaderBar.html\n * @see https://developer.gnome.org/hig/patterns/containers/header-bars.html\n */\nexport const HeaderBar = forwardRef<View, HeaderBarProps>(function HeaderBar(\n { title, start, end, flat = false, style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <RNView\n ref={ref}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space1,\n paddingHorizontal: theme.space2,\n minHeight: 47,\n backgroundColor: theme.headerbarBgColor,\n borderBottomWidth: flat ? 0 : 1,\n borderBottomColor: theme.headerbarBorderColor,\n },\n style,\n ]}\n {...viewProps}\n >\n <RNView style={{ flex: 1, flexDirection: 'row', alignItems: 'center', gap: theme.space1 }}>\n {start}\n </RNView>\n\n <RNView\n accessibilityLiveRegion=\"polite\"\n style={{ alignItems: 'center', justifyContent: 'center', paddingHorizontal: theme.space1 }}\n >\n {typeof title === 'string' ? (\n <RNText\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n lineHeight: Math.round(theme.fontSizeBody * theme.lineHeightHeading),\n color: theme.headerbarFgColor,\n }}\n >\n {title}\n </RNText>\n ) : (\n title\n )}\n </RNView>\n\n <RNView\n style={{\n flex: 1,\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'flex-end',\n gap: theme.space1,\n }}\n >\n {end}\n </RNView>\n </RNView>\n );\n});\n","import { Text as RNText, type TextStyle } from 'react-native';\n\nimport { Text, type TextProps } from '@/components/Text';\nimport { useGnomeTheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface HighlightProps extends Omit<TextProps, 'children'> {\n /** Full text to render. */\n text: string;\n /**\n * Term or terms to highlight within `text`. Pass an array to highlight\n * multiple distinct terms at once (e.g. each word of a multi-word search\n * query). Empty or whitespace-only terms are ignored.\n */\n query: string | string[];\n /** Match case-sensitively. Defaults to `false`. */\n caseSensitive?: boolean;\n}\n\nfunction escapeRegExp(s: string) {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Wraps every occurrence of `query` within `text` in a highlighted inline\n * run — mirrors `@gnome-ui/react`'s `Highlight`, which wraps matches in a\n * `<mark>`. Pairs with `SearchBar`'s suggestion list and any filterable\n * list to show users which part of a result matched what they typed.\n *\n * The outer span is the themed `Text` component (so callers get the same\n * `variant`/`color` API as everywhere else), but each matched run is a\n * plain, unthemed RN `Text` carrying only the highlight's own overrides —\n * RN's `Text` is the one primitive that inherits ambient `fontSize`/\n * `color`/`fontFamily` from a parent `Text` when nested, the same way the\n * web version's `<mark>` inherits from its surrounding text and only\n * overrides `background-color`/`font-weight`. Reaching for the themed\n * `Text` for the marked runs too would reset them to its own default\n * `variant=\"body\"` sizing instead of inheriting whatever variant the\n * caller chose for the whole string.\n *\n * The web version's translucent `color-mix(in srgb, accent 30%,\n * transparent)` background has no RN equivalent (`color-mix` is CSS-only)\n * — resolved to a literal 8-digit `#RRGGBBAA` hex instead, since\n * `accentBgColor` is always a plain 6-digit hex across all four theme\n * variants. `border-radius` on the `<mark>` has no reliable port either:\n * RN only paints `backgroundColor` on an inline (nested) `Text` run, not\n * `borderRadius` — a decorative nicety dropped here, not a behavior gap.\n * `prefers-contrast: more`'s solid-background/white-text swap ports via\n * `useResolvedContrast()`, the same hook `Button` already uses for its own\n * high-contrast branching.\n */\nexport const Highlight = ({ text, query, caseSensitive = false, ...textProps }: HighlightProps) => {\n const theme = useGnomeTheme();\n const contrast = useResolvedContrast();\n\n const terms = (Array.isArray(query) ? query : [query]).map((q) => q.trim()).filter(Boolean);\n\n if (terms.length === 0) {\n return <Text {...textProps}>{text}</Text>;\n }\n\n const pattern = new RegExp(`(${terms.map(escapeRegExp).join('|')})`, caseSensitive ? 'g' : 'gi');\n const parts = text.split(pattern);\n\n const markStyle: TextStyle = {\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n ...(contrast === 'more'\n ? { backgroundColor: theme.accentBgColor, color: '#fff' }\n : { backgroundColor: `${theme.accentBgColor}4D` }),\n };\n\n return (\n <Text {...textProps}>\n {parts.map((part, i) =>\n i % 2 === 1 ? (\n <RNText key={i} style={markStyle}>\n {part}\n </RNText>\n ) : (\n part\n ),\n )}\n </Text>\n );\n};\n","import type { TextStyle, ViewStyle } from 'react-native';\n\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type InlineViewSwitcherVariant = 'default' | 'flat' | 'round' | 'pill';\n\nexport interface VariantStyles {\n container: ViewStyle;\n /** Same values as `container.gap`/`container.padding`, kept as plain\n * numbers so the overflow math can add them up — `ViewStyle` types them\n * as `DimensionValue`, which may be a percentage string. */\n gap: number;\n padding: number;\n item: ViewStyle;\n iconOnlyItem: ViewStyle;\n indicator: ViewStyle;\n /** Inset of the indicator from the container's top/bottom edge. */\n indicatorInset: number;\n activeTextColor: string;\n idleTextColor: string;\n}\n\n/**\n * The four `.default`/`.flat`/`.round`/`.pill` CSS blocks, resolved against\n * the theme. Every `color-mix(in srgb, accent N%, transparent)` becomes an\n * 8-digit `#RRGGBBAA` hex off `theme.accentBgColor` (the `Chip`/`ToggleGroup`\n * precedent, which keeps the tint following the app's configurable accent),\n * and every `box-shadow` is dropped — the theme generator keeps shadow\n * tokens in `raw` only and `Card` already settled that a border, or the\n * surface contrast itself, carries the same separation on RN.\n */\nexport function getVariantStyles(\n theme: GnomeThemeTokens,\n variant: InlineViewSwitcherVariant,\n isDark: boolean,\n): VariantStyles {\n const surfaceBorder = isDark ? 'rgba(255, 255, 255, 0.12)' : theme.light3;\n const accentTint = `${theme.accentBgColor}${isDark ? '40' : '26'}`;\n const accentRing = `${theme.accentBgColor}${isDark ? '80' : '66'}`;\n\n switch (variant) {\n case 'flat':\n return {\n container: {\n backgroundColor: 'transparent',\n borderRadius: theme.radiusMd,\n padding: 2,\n gap: 2,\n },\n gap: 2,\n padding: 2,\n item: { borderRadius: theme.radiusMd - 2, paddingHorizontal: theme.space2 },\n iconOnlyItem: { paddingHorizontal: 4, minWidth: 28 },\n indicator: { backgroundColor: accentTint, borderRadius: theme.radiusMd - 2 },\n indicatorInset: 2,\n activeTextColor: theme.accentColor,\n idleTextColor: theme.windowFgColor,\n };\n\n case 'round':\n return {\n container: {\n backgroundColor: theme.cardBgColor,\n borderWidth: 1,\n borderColor: surfaceBorder,\n borderRadius: theme.radiusPill,\n padding: 2,\n gap: 2,\n },\n gap: 2,\n padding: 2,\n item: { borderRadius: theme.radiusPill, paddingHorizontal: theme.space2 },\n iconOnlyItem: { paddingHorizontal: 4, minWidth: 28 },\n // The only variant whose indicator is the solid accent, not a tint.\n indicator: { backgroundColor: theme.accentBgColor, borderRadius: theme.radiusPill },\n indicatorInset: 2,\n activeTextColor: theme.accentFgColor,\n idleTextColor: theme.windowFgColor,\n };\n\n case 'pill':\n return {\n container: {\n backgroundColor: theme.hoverOverlay,\n borderRadius: theme.radiusPill,\n padding: 3,\n gap: 2,\n },\n gap: 2,\n padding: 3,\n item: {\n borderRadius: theme.radiusPill,\n paddingVertical: 5,\n paddingHorizontal: theme.space2,\n },\n iconOnlyItem: { paddingHorizontal: 5, minWidth: 28 },\n // Segmented-control look: the indicator is the raised card surface\n // itself, with no accent color anywhere.\n indicator: { backgroundColor: theme.cardBgColor, borderRadius: theme.radiusPill },\n indicatorInset: 3,\n activeTextColor: theme.windowFgColor,\n idleTextColor: theme.windowFgColor,\n };\n\n default:\n return {\n container: {\n backgroundColor: theme.cardBgColor,\n borderWidth: 1,\n borderColor: surfaceBorder,\n borderRadius: theme.radiusMd,\n padding: 2,\n gap: 2,\n },\n gap: 2,\n padding: 2,\n item: { borderRadius: theme.radiusMd - 2, paddingHorizontal: theme.space2 },\n iconOnlyItem: { paddingHorizontal: 4, minWidth: 28 },\n indicator: {\n backgroundColor: accentTint,\n borderRadius: theme.radiusMd - 2,\n borderWidth: 1,\n borderColor: accentRing,\n },\n indicatorInset: 2,\n activeTextColor: theme.accentColor,\n idleTextColor: theme.windowFgColor,\n };\n }\n}\n\n/** Shared by the item label and the menu-sheet rows. */\nexport function labelTextStyle(theme: GnomeThemeTokens, active: boolean, color: string): TextStyle {\n return {\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(\n active ? theme.fontWeightSemibold : theme.fontWeightNormal,\n ) as TextStyle['fontWeight'],\n color,\n };\n}\n","import { Check, PanDown } from '@gnome-ui/icons';\nimport {\n Children,\n createContext,\n forwardRef,\n isValidElement,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { LayoutChangeEvent, StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { Animated, Pressable, Text as RNText, View as RNView, ScrollView } from 'react-native';\n\nimport { BottomSheet } from '@/components/BottomSheet';\nimport { Icon } from '@/components/Icon';\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\n\nimport type { InlineViewSwitcherItemProps } from './InlineViewSwitcherItem';\nimport {\n getVariantStyles,\n type InlineViewSwitcherVariant,\n labelTextStyle,\n type VariantStyles,\n} from './variants';\n\nexport type { InlineViewSwitcherVariant } from './variants';\n\nexport type InlineViewSwitcherOverflow = 'wrap' | 'scroll' | 'compact' | 'menu';\n\n/** Measured position of one item inside the switcher row. */\nexport interface ItemLayout {\n x: number;\n width: number;\n}\n\n// ─── Internal context ──────────────────────────────────────────────────────────\n\ninterface InlineViewSwitcherContextValue {\n value: string;\n onValueChange: (value: string) => void;\n compact: boolean;\n styles: VariantStyles;\n onItemLayout: (name: string, layout: ItemLayout) => void;\n}\n\nconst InlineViewSwitcherContext = createContext<InlineViewSwitcherContextValue | null>(null);\n\n// eslint-disable-next-line react-refresh/only-export-components\nexport function useInlineViewSwitcher() {\n const ctx = useContext(InlineViewSwitcherContext);\n\n if (!ctx) {\n throw new Error('InlineViewSwitcherItem must be used inside InlineViewSwitcher');\n }\n\n return ctx;\n}\n\n// ─── InlineViewSwitcher ────────────────────────────────────────────────────────\n\nexport interface InlineViewSwitcherProps extends Omit<ViewProps, 'style'> {\n /** Currently active view name. */\n value: string;\n /** Called with the new value when a view is selected. */\n onValueChange: (value: string) => void;\n /**\n * Visual style of the switcher.\n * - `default` — card background with border (same shape as `ToggleGroup`).\n * - `flat` — no background or border; active indicator only.\n * - `round` — pill-shaped container and items, solid accent indicator.\n * - `pill` — segmented-control style; active item appears lifted, no accent color.\n */\n variant?: InlineViewSwitcherVariant;\n /**\n * Overflow strategy when the container is too narrow for all items.\n * - `wrap` — default; items simply overflow.\n * - `scroll` — horizontal scroll, snapping each item to the start edge.\n * - `compact` — collapses item labels to icons-only when overflowing (needs icons on all items).\n * - `menu` — shows the active item and a chevron; all items open in a `BottomSheet`.\n */\n overflow?: InlineViewSwitcherOverflow;\n /** Accessible label for the group. */\n accessibilityLabel?: string;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/** Matches the web's `transform 160ms cubic-bezier(0.34, 1.2, 0.64, 1)`. */\nconst INDICATOR_DURATION = 160;\n/** Stay collapsed until the container is comfortably wider — ported verbatim. */\nconst HYSTERESIS = 30;\n\n/**\n * Compact inline view switcher for content areas, cards, and toolbars —\n * wherever `ViewSwitcher` (header-bar sized) would be too heavy. Mirrors\n * `AdwInlineViewSwitcher` (libadwaita 1.7 / GNOME 48) and\n * `@gnome-ui/react`'s own `InlineViewSwitcher`.\n *\n * All four variants and all four overflow strategies port, but almost none\n * of the *mechanism* does — this is a rebuild, not a transliteration:\n *\n * - **The sliding indicator** is measured, not laid out. The web reads the\n * active button's `offsetLeft`/`offsetWidth` in a `useLayoutEffect`; here\n * each item reports its own `onLayout` up through the context, and the\n * indicator animates `translateX` + `width` to the active entry. Both run\n * on **one JS-driven animation** (`useNativeDriver: false`): `width` can't\n * be native-driven, and mixing a native and a JS value on one component\n * throws — the same trade-off `Expander` accepted for its own animated\n * height. `scaleX` would have been native-driveable but distorts the\n * indicator's corner radii, which is exactly what the variants shape.\n * `useReducedMotion()` snaps it into place instead, per this package's\n * per-component convention.\n * - **Overflow detection** replaces `ResizeObserver` + `scrollWidth` vs\n * `clientWidth` with the item measurements already being collected: their\n * summed natural widths (RN defaults `flexShrink` to 0, so an overflowing\n * row reports each item's *natural* width rather than a squeezed one)\n * against the row's own `onLayout` width. The web's `naturalWidthRef`\n * capture and 30 px hysteresis port verbatim — without them, collapsing\n * the labels shrinks the content and would immediately re-expand it.\n * - **`overflow=\"scroll\"`** becomes a horizontal `ScrollView` with the\n * scrollbar hidden. `scroll-snap-align: start` has no RN style, but the\n * measured item offsets feed `snapToOffsets`, which reproduces it exactly.\n * - **`overflow=\"menu\"`** reuses the already-shipped `BottomSheet`, the same\n * component the web version reaches for.\n *\n * The ←/→/Home/End keyboard layer drops, as everywhere else in this package.\n * As in `ToggleGroup`, the group takes `accessibilityRole=\"radiogroup\"` but\n * deliberately not `accessible`, which on iOS would collapse the items into\n * a single unreachable element.\n *\n * @example\n * const [view, setView] = useState('grid');\n *\n * <InlineViewSwitcher value={view} onValueChange={setView} variant=\"pill\">\n * <InlineViewSwitcherItem name=\"grid\" label=\"Grid\" />\n * <InlineViewSwitcherItem name=\"list\" label=\"List\" />\n * </InlineViewSwitcher>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.InlineViewSwitcher.html\n */\nexport const InlineViewSwitcher = forwardRef<View, InlineViewSwitcherProps>(\n function InlineViewSwitcher(\n {\n value,\n onValueChange,\n variant = 'default',\n overflow = 'wrap',\n accessibilityLabel = 'View switcher',\n children,\n style,\n ...viewProps\n },\n ref,\n ) {\n const theme = useGnomeTheme();\n const isDark = useResolvedColorScheme() === 'dark';\n const reducedMotion = useReducedMotion();\n const styles = useMemo(\n () => getVariantStyles(theme, variant, isDark),\n [theme, variant, isDark],\n );\n\n const [layouts, setLayouts] = useState<Record<string, ItemLayout>>({});\n const [availableWidth, setAvailableWidth] = useState(0);\n const [isOverflowing, setIsOverflowing] = useState(false);\n const [sheetOpen, setSheetOpen] = useState(false);\n const naturalWidthRef = useRef(0);\n\n const translateX = useRef(new Animated.Value(0)).current;\n const indicatorWidth = useRef(new Animated.Value(0)).current;\n\n const onItemLayout = useCallback((name: string, layout: ItemLayout) => {\n setLayouts((prev) => {\n const current = prev[name];\n\n if (current && current.x === layout.x && current.width === layout.width) {\n return prev;\n }\n\n return { ...prev, [name]: layout };\n });\n }, []);\n\n const detectsOverflow = overflow === 'compact' || overflow === 'menu';\n\n const items = useMemo(\n () =>\n Children.toArray(children)\n .filter(isValidElement)\n .filter(\n (child) =>\n (child.type as { displayName?: string }).displayName === 'InlineViewSwitcherItem',\n )\n .map((child) => (child as ReactElement<InlineViewSwitcherItemProps>).props),\n [children],\n );\n\n // Natural width of the row: RN leaves `flexShrink` at 0, so an\n // overflowing row still reports each item at its own full width.\n const naturalWidth = useMemo(() => {\n const measured = items.map((item) => layouts[item.name]?.width ?? 0);\n\n if (measured.some((width) => width === 0)) {\n return 0;\n }\n\n const gaps = Math.max(items.length - 1, 0) * styles.gap;\n\n return measured.reduce((total, width) => total + width, 0) + gaps + 2 * styles.padding;\n }, [items, layouts, styles.gap, styles.padding]);\n\n useEffect(() => {\n if (!detectsOverflow || availableWidth === 0 || naturalWidth === 0) {\n if (!detectsOverflow) {\n setIsOverflowing(false);\n }\n return;\n }\n\n setIsOverflowing((prev) => {\n if (!prev) {\n if (naturalWidth > availableWidth) {\n naturalWidthRef.current = naturalWidth;\n return true;\n }\n return false;\n }\n\n return naturalWidthRef.current > availableWidth + HYSTERESIS;\n });\n }, [detectsOverflow, availableWidth, naturalWidth]);\n\n const compact = overflow === 'compact' && isOverflowing;\n const inMenuMode = overflow === 'menu' && isOverflowing;\n const activeItem = items.find((item) => item.name === value) ?? items[0];\n const activeLayout = layouts[value];\n\n useEffect(() => {\n if (inMenuMode || !activeLayout) {\n return;\n }\n\n if (reducedMotion) {\n translateX.setValue(activeLayout.x);\n indicatorWidth.setValue(activeLayout.width);\n return;\n }\n\n const animation = Animated.parallel([\n Animated.timing(translateX, {\n toValue: activeLayout.x,\n duration: INDICATOR_DURATION,\n useNativeDriver: false,\n }),\n Animated.timing(indicatorWidth, {\n toValue: activeLayout.width,\n duration: INDICATOR_DURATION,\n useNativeDriver: false,\n }),\n ]);\n\n animation.start();\n\n // Stop on unmount (and before restarting): a JS-driven animation keeps\n // its own timer alive, which would otherwise go on ticking against a\n // torn-down tree.\n return () => animation.stop();\n }, [activeLayout, inMenuMode, reducedMotion, translateX, indicatorWidth]);\n\n const context = useMemo(\n () => ({ value, onValueChange, compact, styles, onItemLayout }),\n [value, onValueChange, compact, styles, onItemLayout],\n );\n\n const handleRowLayout = useCallback(\n (event: LayoutChangeEvent) => {\n if (detectsOverflow) {\n setAvailableWidth(event.nativeEvent.layout.width);\n }\n },\n [detectsOverflow],\n );\n\n const snapToOffsets = useMemo(\n () =>\n overflow === 'scroll'\n ? items\n .map((item) => layouts[item.name]?.x ?? 0)\n .filter((x, index) => index === 0 || x > 0)\n : undefined,\n [overflow, items, layouts],\n );\n\n const row = (\n <RNView\n ref={ref}\n accessibilityRole={inMenuMode ? undefined : 'radiogroup'}\n accessibilityLabel={accessibilityLabel}\n onLayout={handleRowLayout}\n style={[\n { flexDirection: 'row', alignItems: 'stretch' },\n styles.container,\n // `.overflowDetect { width: 100% }` — the row has to be\n // constrained for there to be an overflow to detect at all.\n detectsOverflow ? { width: '100%' } : { alignSelf: 'flex-start' },\n style,\n ]}\n {...viewProps}\n >\n {inMenuMode ? (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={`${accessibilityLabel}: ${activeItem?.label ?? activeItem?.name}`}\n onPress={() => setSheetOpen(true)}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n gap: theme.space1,\n minHeight: 28,\n paddingVertical: 4,\n },\n styles.item,\n ]}\n >\n {activeItem?.icon ? <Icon icon={activeItem.icon} size=\"md\" /> : null}\n {activeItem?.label ? (\n // Idle color, not `activeTextColor`: menu mode hides the\n // indicator, so there's no accent surface behind the trigger.\n // The web version applies its `.active` class here anyway,\n // which paints `round`'s label in `accent-fg` (#fff) on a plain\n // card — white on white. Fixed rather than ported.\n <RNText style={labelTextStyle(theme, true, styles.idleTextColor)}>\n {activeItem.label}\n </RNText>\n ) : null}\n <Icon icon={PanDown} size=\"sm\" />\n </Pressable>\n ) : (\n <>\n <Animated.View\n pointerEvents=\"none\"\n style={[\n {\n position: 'absolute',\n left: 0,\n top: styles.indicatorInset,\n bottom: styles.indicatorInset,\n width: indicatorWidth,\n transform: [{ translateX }],\n },\n styles.indicator,\n ]}\n />\n {children}\n </>\n )}\n </RNView>\n );\n\n return (\n <InlineViewSwitcherContext.Provider value={context}>\n {overflow === 'scroll' ? (\n <ScrollView\n horizontal\n showsHorizontalScrollIndicator={false}\n snapToOffsets={snapToOffsets}\n decelerationRate=\"fast\"\n contentContainerStyle={{ flexGrow: 0 }}\n style={{ flexGrow: 0 }}\n >\n {row}\n </ScrollView>\n ) : (\n row\n )}\n\n {overflow === 'menu' ? (\n <BottomSheet\n open={sheetOpen}\n title={accessibilityLabel}\n onClose={() => setSheetOpen(false)}\n >\n <RNView accessibilityRole=\"radiogroup\" accessibilityLabel={accessibilityLabel}>\n {items.map((item) => {\n const active = item.name === value;\n\n return (\n <Pressable\n key={item.name}\n accessibilityRole=\"radio\"\n accessibilityState={{ checked: active }}\n accessibilityLabel={item.label ?? item.name}\n onPress={() => {\n onValueChange(item.name);\n setSheetOpen(false);\n }}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: theme.space2,\n paddingHorizontal: theme.space3,\n borderRadius: theme.radiusMd,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n })}\n >\n {item.icon ? <Icon icon={item.icon} size=\"md\" /> : null}\n <RNText\n style={[\n labelTextStyle(\n theme,\n active,\n active ? theme.accentColor : theme.windowFgColor,\n ),\n { flex: 1 },\n ]}\n >\n {item.label ?? item.name}\n </RNText>\n {active ? <Icon icon={Check} size=\"md\" /> : null}\n </Pressable>\n );\n })}\n </RNView>\n </BottomSheet>\n ) : null}\n </InlineViewSwitcherContext.Provider>\n );\n },\n);\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport { forwardRef, useCallback } from 'react';\nimport type { LayoutChangeEvent, PressableProps, View } from 'react-native';\nimport { Pressable, Text as RNText } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nimport { useInlineViewSwitcher } from './InlineViewSwitcher';\nimport { labelTextStyle } from './variants';\n\nexport interface InlineViewSwitcherItemProps\n extends Omit<PressableProps, 'children' | 'style' | 'disabled' | 'onPress'> {\n /** String identifier — becomes the switcher's `value` when this item is active. */\n name: string;\n /** Visible label. */\n label?: string;\n /** Icon from `@gnome-ui/icons`. */\n icon?: IconDefinition;\n /** Accessible name. Required for icon-only items; defaults to `label`. */\n accessibilityLabel?: string;\n disabled?: boolean;\n}\n\n/**\n * Individual view option inside an `InlineViewSwitcher`. Can be icon-only,\n * label-only, or icon + label — for icon-only items always pass an\n * `accessibilityLabel` so screen readers can identify the view.\n *\n * The item paints no background of its own for the active state: that's the\n * parent's sliding indicator, which this component feeds by reporting its\n * `onLayout` position and width up through the context. Only the label's\n * color and weight change, exactly as in the web version — where the\n * `.active` class also sets nothing but `color` and `font-weight`.\n */\nexport const InlineViewSwitcherItem = forwardRef<View, InlineViewSwitcherItemProps>(\n function InlineViewSwitcherItem(\n { name, label, icon, accessibilityLabel, disabled = false, ...pressableProps },\n ref,\n ) {\n const theme = useGnomeTheme();\n const { value, onValueChange, compact, styles, onItemLayout } = useInlineViewSwitcher();\n\n const active = value === name;\n // In compact mode the label collapses, but only when an icon can stand\n // in for it — ported verbatim from the web fallback.\n const showLabel = Boolean(label) && !(compact && icon);\n const iconOnly = Boolean(icon) && !showLabel;\n\n const handleLayout = useCallback(\n (event: LayoutChangeEvent) => {\n const { x, width } = event.nativeEvent.layout;\n\n onItemLayout(name, { x, width });\n },\n [name, onItemLayout],\n );\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n onPress={() => onValueChange(name)}\n onLayout={handleLayout}\n accessibilityRole=\"radio\"\n accessibilityState={{ checked: active, disabled }}\n accessibilityLabel={accessibilityLabel ?? label}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n gap: theme.space1,\n minHeight: 28,\n paddingVertical: 4,\n opacity: disabled ? theme.opacityDisabled : 1,\n },\n styles.item,\n iconOnly ? styles.iconOnlyItem : null,\n ]}\n {...pressableProps}\n >\n {icon ? <Icon icon={icon} size=\"md\" /> : null}\n\n {showLabel ? (\n <RNText\n numberOfLines={1}\n style={labelTextStyle(\n theme,\n active,\n active ? styles.activeTextColor : styles.idleTextColor,\n )}\n >\n {label}\n </RNText>\n ) : null}\n </Pressable>\n );\n },\n);\n\nInlineViewSwitcherItem.displayName = 'InlineViewSwitcherItem';\n","import { useEffect, useRef } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Animated, Easing, View } from 'react-native';\n\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type LevelBarVariant = 'accent' | 'success' | 'warning' | 'error';\n\nexport interface LevelBarProps {\n /** Current value, between `min` and `max`. */\n value: number;\n /** Minimum value. Defaults to `0`. */\n min?: number;\n /** Maximum value. Defaults to `1`. */\n max?: number;\n /** Threshold at or below which the bar renders in `lowVariant`. */\n low?: number;\n /** Color used when `value <= low`. Defaults to `\"warning\"`. */\n lowVariant?: LevelBarVariant;\n /** Threshold at or above which the bar renders in `highVariant`. */\n high?: number;\n /** Color used when `value >= high`. Defaults to `\"error\"`. */\n highVariant?: LevelBarVariant;\n /** Color used between `low` and `high`. Defaults to `\"accent\"`. */\n variant?: LevelBarVariant;\n /**\n * Render as a row of discrete blocks instead of a continuous fill —\n * mirrors `GtkLevelBar`'s discrete mode (e.g. signal-strength indicators).\n */\n discrete?: boolean;\n /** Number of blocks when `discrete` is true. Defaults to `10`. */\n numBlocks?: number;\n /** Accessible label describing what the level represents. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nfunction resolveVariant(\n value: number,\n low: number | undefined,\n lowVariant: LevelBarVariant,\n high: number | undefined,\n highVariant: LevelBarVariant,\n variant: LevelBarVariant,\n): LevelBarVariant {\n if (low !== undefined && value <= low) {\n return lowVariant;\n }\n\n if (high !== undefined && value >= high) {\n return highVariant;\n }\n\n return variant;\n}\n\nfunction variantColor(theme: GnomeThemeTokens, variant: LevelBarVariant): string {\n switch (variant) {\n case 'accent':\n return theme.accentBgColor;\n case 'success':\n return theme.successBgColor;\n case 'warning':\n return theme.warningBgColor;\n case 'error':\n return theme.errorBgColor;\n }\n}\n\n/**\n * Discrete level indicator with color-coded low/high offset zones —\n * mirrors `GtkLevelBar` and `@gnome-ui/react`'s `LevelBar`. Use for a\n * gauge/measurement display (disk usage, battery, signal strength), not\n * for task progress (see `ProgressBar`) or a proportional category\n * breakdown (see `SegmentedBar`).\n *\n * The continuous fill reuses `ProgressBar`'s exact animation technique\n * rather than animating `width` directly: a fixed `width: '100%'` fill\n * with `transformOrigin: 'left'` and an animated `transform: [{ scaleX }]`,\n * so the whole thing runs on `useNativeDriver: true` — a JS-driven\n * (`useNativeDriver: false`) `width` animation schedules its next frame via\n * a plain `setTimeout` that routinely fires after a test's `render()`\n * returns but before unmount, producing a real \"update not wrapped in\n * act()\" warning, the same reasoning `ProgressBar`'s own docstring\n * documents. `useReducedMotion()` mirrors `ProgressBar`'s determinate\n * behavior: the transition duration drops to `0` (an immediate jump)\n * rather than the animation being skipped in some other way.\n *\n * Discrete mode's per-block `background-color` transition has no port —\n * unlike the continuous fill's width change, a value change in discrete\n * mode is a binary color swap per block with no established `Animated`\n * color-interpolation precedent elsewhere in this package, so it's a\n * plain, unanimated style swap; a decorative nicety, not a behavior gap.\n * Discrete blocks are hidden from the accessibility tree\n * (`accessibilityElementsHidden`/`importantForAccessibility=\"no\"`,\n * mirroring the web version's `aria-hidden`) since the meter's value is\n * already exposed once via `accessibilityValue` on the container.\n *\n * `role=\"meter\"` ports 1:1 from RN's newer web-aligned `Role` union (unlike\n * `AccessibilityRole`, which has no `\"meter\"` value at all) — the same\n * precedent `Dialog`/`Avatar`/`Badge` already established for reaching for\n * `role` over `accessibilityRole` when only the newer union has the value\n * needed. Web's `aria-labelledby` (an id-relationship prop) has no RN\n * equivalent — RN has no DOM ids — so only `aria-label`\n * (`accessibilityLabel`) is ported, the same `ProgressBar`/`Slider`\n * precedent.\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/progress.html\n */\nexport const LevelBar = ({\n value,\n min = 0,\n max = 1,\n low,\n lowVariant = 'warning',\n high,\n highVariant = 'error',\n variant = 'accent',\n discrete = false,\n numBlocks = 10,\n accessibilityLabel,\n style,\n testID,\n}: LevelBarProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const reducedMotion = useReducedMotion();\n\n const range = max - min;\n const clamped = Math.min(max, Math.max(min, value));\n const fraction = range > 0 ? (clamped - min) / range : 0;\n const resolvedVariant = resolveVariant(clamped, low, lowVariant, high, highVariant, variant);\n\n const scaleX = useRef(new Animated.Value(fraction)).current;\n\n useEffect(() => {\n if (discrete) {\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(scaleX, {\n toValue: fraction,\n duration: reducedMotion ? 0 : theme.durationNormal,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: true,\n }).start();\n }, [fraction, discrete, reducedMotion, scaleX, theme.durationNormal, theme.easingDefault]);\n\n const trackColor = scheme === 'dark' ? theme.dark2 : theme.light3;\n\n return (\n <View\n testID={testID}\n accessible\n role=\"meter\"\n accessibilityLabel={accessibilityLabel}\n accessibilityValue={{ min, max, now: clamped }}\n style={[\n {\n flexDirection: 'row',\n width: '100%',\n height: 6,\n borderRadius: discrete ? 0 : theme.radiusPill,\n backgroundColor: discrete ? 'transparent' : trackColor,\n overflow: discrete ? 'visible' : 'hidden',\n gap: discrete ? 2 : 0,\n },\n style,\n ]}\n >\n {discrete ? (\n Array.from({ length: numBlocks }, (_, i) => {\n const blockFraction = (i + 1) / numBlocks;\n const filled = blockFraction <= fraction;\n\n return (\n <View\n key={i}\n testID=\"level-bar-block\"\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n flex: 1,\n height: '100%',\n minWidth: 2,\n borderRadius: 1,\n backgroundColor: filled ? variantColor(theme, resolvedVariant) : trackColor,\n }}\n />\n );\n })\n ) : (\n <Animated.View\n testID={testID ? `${testID}-fill` : undefined}\n style={{\n width: '100%',\n height: '100%',\n borderRadius: theme.radiusPill,\n backgroundColor: variantColor(theme, resolvedVariant),\n transformOrigin: 'left',\n transform: [{ scaleX }],\n }}\n />\n )}\n </View>\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { PressableProps, StyleProp, TextStyle, View, ViewStyle } from 'react-native';\nimport { Linking, Pressable, Text as RNText } from 'react-native';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface LinkProps extends Omit<PressableProps, 'children' | 'style'> {\n /**\n * Target URL. Passed to `Linking.openURL` when pressed, unless a custom\n * `onPress` is supplied (e.g. to hand internal links to a router like\n * React Navigation instead of the device browser).\n */\n href: string;\n /**\n * When true the link is treated as external: appends a trailing ↗\n * indicator and an \"Opens in browser\" accessibility hint. RN has no tab\n * concept, so unlike the web `Link` this doesn't change navigation\n * behaviour — `href` always opens via `Linking.openURL` regardless.\n */\n external?: boolean;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n /** Style applied to the label `Text`. */\n textStyle?: StyleProp<TextStyle>;\n}\n\n/**\n * Inline hyperlink following GNOME HIG.\n *\n * Rebuilt on `Pressable`/themed `Text` rather than ported from\n * `@gnome-ui/react`'s `<a>`: touch devices have no `:hover`, so the\n * underline that the web version reveals on hover instead reveals on\n * press — the closest native equivalent — alongside the same `0.7`\n * press-opacity dip as `@gnome-ui/react`'s `:active` state.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/links.html\n */\nexport const Link = forwardRef<View, LinkProps>(function Link(\n { href, external = false, children, style, textStyle, onPress, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <Pressable\n ref={ref}\n accessibilityRole=\"link\"\n accessibilityHint={external ? 'Opens in browser' : undefined}\n onPress={onPress ?? (() => Linking.openURL(href))}\n style={style}\n {...pressableProps}\n >\n {({ pressed }) => (\n <Text\n style={[\n {\n color: theme.accentColor,\n textDecorationLine: pressed ? 'underline' : 'none',\n opacity: pressed ? 0.7 : 1,\n },\n textStyle,\n ]}\n >\n {children}\n {external && (\n <RNText\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{ fontSize: 12 }}\n >\n {' ↗'}\n </RNText>\n )}\n </Text>\n )}\n </Pressable>\n );\n});\n","import {\n Children,\n cloneElement,\n forwardRef,\n isValidElement,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport type { StyleProp, View, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface LinkedGroupProps {\n /**\n * Each child must accept and merge a `style` prop the way every\n * `@gnome-ui/react-native` component already does (`style` last in its\n * own internal style array) — see the component doc for why.\n */\n children: ReactNode;\n /** Stack children vertically instead of horizontally. */\n vertical?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\ntype StyleableElement = ReactElement<{ style?: StyleProp<ViewStyle> }>;\n\n/**\n * Renders children as a single visually-connected unit with no gap and\n * merged borders — the canonical GNOME pattern for button groups and\n * segmented inputs. Mirrors `@gnome-ui/react`'s `LinkedGroup`, itself\n * mirroring the libadwaita `.linked` style class.\n *\n * The web version reaches every child's border-radius via a CSS\n * `> *` universal child selector — RN has no equivalent way for a parent\n * `View` to reach into an arbitrary child's own internally-computed\n * styles. Reimagined instead as the same `cloneElement`-onto-children\n * technique `Popover`/`Tooltip` already use on their own trigger:\n * each child gets a computed corner-radius/negative-margin override\n * merged onto whatever `style` it already has, using the same\n * \"zero the shared inner corners, keep `theme.radiusMd` on the outer\n * ones, overlap by 1 dp to collapse the shared border\" recipe\n * `SplitButton` already proved for its own two-piece connected border —\n * generalized here from a fixed two children to an arbitrary list. This\n * only works because every component in this package already merges a\n * passed-in `style` prop last, the same assumption `Popover`'s own\n * trigger-cloning already depends on.\n *\n * The web CSS also raises a hovered/focused child's `z-index` so its own\n * border isn't visually covered by the next sibling's overlapping edge —\n * dropped here: RN is touch-first (no `:hover`), and no component in this\n * package currently renders an escaping focus ring that overlap could\n * clip, so there's nothing yet for the z-index bump to protect.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest/style-classes.html#linked-style-class\n *\n * @example\n * <LinkedGroup>\n * <Button variant=\"flat\">Bold</Button>\n * <Button variant=\"flat\">Italic</Button>\n * </LinkedGroup>\n */\nexport const LinkedGroup = forwardRef<View, LinkedGroupProps>(function LinkedGroup(\n { children, vertical = false, style, testID },\n ref,\n) {\n const theme = useGnomeTheme();\n const items = Children.toArray(children).filter(isValidElement) as StyleableElement[];\n const count = items.length;\n\n return (\n <RNView\n ref={ref}\n testID={testID}\n style={[{ flexDirection: vertical ? 'column' : 'row', alignItems: 'stretch' }, style]}\n >\n {items.map((child, index) => {\n const isFirst = index === 0;\n const isLast = index === count - 1;\n\n const cornerStyle: ViewStyle = vertical\n ? {\n borderTopLeftRadius: isFirst ? theme.radiusMd : 0,\n borderTopRightRadius: isFirst ? theme.radiusMd : 0,\n borderBottomLeftRadius: isLast ? theme.radiusMd : 0,\n borderBottomRightRadius: isLast ? theme.radiusMd : 0,\n marginTop: isFirst ? 0 : -1,\n }\n : {\n borderTopLeftRadius: isFirst ? theme.radiusMd : 0,\n borderBottomLeftRadius: isFirst ? theme.radiusMd : 0,\n borderTopRightRadius: isLast ? theme.radiusMd : 0,\n borderBottomRightRadius: isLast ? theme.radiusMd : 0,\n marginLeft: isFirst ? 0 : -1,\n };\n\n return cloneElement(child, {\n key: index,\n style: [child.props.style, cornerStyle],\n });\n })}\n </RNView>\n );\n});\n","import { PanDown } from '@gnome-ui/icons';\nimport { useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Animated, Dimensions, Modal, Pressable, ScrollView, View } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport interface MultiSelectDropdownOption<V extends string = string> {\n /** The value included in `value` when this option is selected. */\n value: V;\n /** Display label shown in the list and (when only one is selected) the trigger. */\n label: string;\n /** Optional descriptive text shown below the label. */\n description?: string;\n /** Whether the option is selectable. */\n disabled?: boolean;\n}\n\nexport interface MultiSelectDropdownProps<V extends string = string> {\n /** The list of selectable options. */\n options: MultiSelectDropdownOption<V>[];\n /** The currently selected values. */\n value: V[];\n /** Called with the full updated selection whenever an option is toggled. */\n onChange: (value: V[]) => void;\n /** Placeholder shown on the trigger when no option is selected. */\n placeholder?: string;\n /** Accessible label for the control. */\n accessibilityLabel?: string;\n /** Disables the entire control. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\ninterface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface Position {\n top: number;\n left: number;\n width: number;\n flipUp: boolean;\n}\n\nconst GAP = 4;\nconst MARGIN = 8;\nconst MAX_LIST_HEIGHT = 280;\n\nfunction computePosition(trigger: Rect, panelHeight: number): Position {\n const { height: vh } = Dimensions.get('window');\n const spaceBelow = vh - (trigger.y + trigger.height);\n const flipUp = spaceBelow < panelHeight && trigger.y > panelHeight;\n const top = flipUp ? trigger.y - panelHeight - GAP : trigger.y + trigger.height + GAP;\n\n return {\n top: Math.max(MARGIN, Math.min(top, vh - panelHeight - MARGIN)),\n left: trigger.x,\n width: trigger.width,\n flipUp,\n };\n}\n\nfunction summarize(count: number, selected: { label: string }[]): string {\n if (count === 1) {\n return selected[0].label;\n }\n\n return `${count} selected`;\n}\n\n/**\n * Checkbox-list variant of `Dropdown` for selecting multiple values from a\n * single trigger, mirroring `@gnome-ui/react`'s `MultiSelectDropdown`.\n *\n * `Dropdown`/`ComboRow` are single-select only — use `MultiSelectDropdown`\n * when more than one value can be chosen at once. Toggling an option keeps\n * the panel open so the user can pick several in a row (unlike `Dropdown`,\n * whose `selectOption` closes the panel); close it via the backdrop tap.\n *\n * The `Modal` + backdrop + independently-measured-then-combined\n * trigger-rect/panel-height positioning (`Rect`/`Position`/\n * `computePosition`/`GAP`/`MARGIN`/`MAX_LIST_HEIGHT`) is duplicated\n * verbatim from `Dropdown` rather than extracted to a shared hook — same\n * \"genuinely different, not save-able\" judgment call already applied to\n * `Tooltip`/`Dropdown`'s own position-code duplication, though this is now\n * the third near-identical copy of the up/down-flip-clamp-to-width shape\n * specifically (not `Tooltip`'s 4-directional cascade) — worth extracting\n * into a shared hook in a future pass if a fourth caller needs it, since\n * unlike `Tooltip` these two are not just similarly-shaped but identical.\n *\n * Each option row gets a leading checkbox-square visual (border/background\n * only, no animation — `Dropdown`'s own per-option content has none\n * either) instead of `Dropdown`'s single trailing checkmark-when-selected,\n * since more than one row can be checked at once.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/drop-down-lists.html\n */\nexport const MultiSelectDropdown = <V extends string = string>({\n options,\n value,\n onChange,\n placeholder = 'Select options',\n accessibilityLabel,\n disabled,\n style,\n testID,\n}: MultiSelectDropdownProps<V>) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const [open, setOpen] = useState(false);\n const [triggerRect, setTriggerRect] = useState<Rect | null>(null);\n const [panelHeight, setPanelHeight] = useState<number | null>(null);\n const [pos, setPos] = useState<Position | null>(null);\n\n const triggerRef = useRef<View>(null);\n const progress = useRef(new Animated.Value(0)).current;\n\n const selectedOptions = options.filter((o) => value.includes(o.value));\n\n const openList = () => {\n if (disabled) {\n return;\n }\n\n setOpen(true);\n };\n\n const closeList = () => setOpen(false);\n\n const toggleOption = (opt: MultiSelectDropdownOption<V>) => {\n if (opt.disabled) {\n return;\n }\n\n onChange(\n value.includes(opt.value) ? value.filter((v) => v !== opt.value) : [...value, opt.value],\n );\n };\n\n useEffect(() => {\n if (!open) {\n setTriggerRect(null);\n setPanelHeight(null);\n setPos(null);\n\n return;\n }\n\n triggerRef.current?.measureInWindow((x, y, width, height) => {\n setTriggerRect({ x, y, width, height });\n });\n }, [open]);\n\n useEffect(() => {\n if (triggerRect === null || panelHeight === null) {\n return;\n }\n\n setPos(computePosition(triggerRect, panelHeight));\n }, [triggerRect, panelHeight]);\n\n useEffect(() => {\n if (!pos) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n progress.setValue(0);\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationFast,\n useNativeDriver: true,\n }).start();\n }, [pos, reducedMotion, progress, theme.durationFast]);\n\n const handlePanelLayout = (e: LayoutChangeEvent) => {\n setPanelHeight(Math.min(e.nativeEvent.layout.height, MAX_LIST_HEIGHT));\n };\n\n return (\n <View style={style}>\n <Pressable\n ref={triggerRef}\n testID={testID}\n role=\"combobox\"\n accessibilityLabel={accessibilityLabel}\n accessibilityState={{ expanded: open, disabled: !!disabled }}\n disabled={disabled}\n onPress={() => (open ? closeList() : openList())}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: theme.space1,\n minHeight: 36,\n minWidth: 160,\n paddingVertical: 7,\n paddingHorizontal: theme.space2,\n backgroundColor: pressed ? theme.activeOverlay : theme.cardBgColor,\n borderWidth: 1,\n borderColor: open ? theme.accentColor : theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n opacity: disabled ? theme.opacityDisabled : 1,\n })}\n >\n <Text\n numberOfLines={1}\n style={{\n flex: 1,\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n opacity: selectedOptions.length === 0 ? theme.opacityDim : 1,\n }}\n >\n {selectedOptions.length === 0\n ? placeholder\n : summarize(selectedOptions.length, selectedOptions)}\n </Text>\n <Icon\n icon={PanDown}\n size=\"md\"\n color=\"default\"\n style={{ transform: [{ rotate: open ? '180deg' : '0deg' }] }}\n />\n </Pressable>\n\n <Modal visible={open} transparent animationType=\"none\" statusBarTranslucent>\n <Pressable\n onPress={closeList}\n accessible={false}\n style={{ flex: 1 }}\n testID={testID ? `${testID}-backdrop` : undefined}\n >\n <Animated.View\n onLayout={handlePanelLayout}\n role=\"list\"\n accessible\n style={[\n {\n position: 'absolute',\n maxHeight: MAX_LIST_HEIGHT,\n backgroundColor: theme.popoverBgColor,\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n paddingVertical: 4,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 4 },\n shadowOpacity: 0.15,\n shadowRadius: 12,\n elevation: 6,\n },\n pos\n ? { top: pos.top, left: pos.left, width: pos.width, opacity: progress }\n : { top: -9999, left: -9999, width: 200, opacity: 0 },\n ]}\n >\n <ScrollView bounces={false}>\n {options.map((opt) => {\n const checked = value.includes(opt.value);\n\n return (\n <Pressable\n key={opt.value}\n role=\"option\"\n accessibilityState={{ selected: checked, disabled: !!opt.disabled }}\n disabled={opt.disabled}\n onPress={() => toggleOption(opt)}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n minHeight: 40,\n paddingVertical: 8,\n paddingHorizontal: theme.space2,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n opacity: opt.disabled ? theme.opacityDisabled : 1,\n })}\n >\n <View\n style={{\n width: 16,\n height: 16,\n flexShrink: 0,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusSm,\n borderWidth: 1.5,\n borderColor: checked ? theme.accentBgColor : theme.cardShadeColor,\n backgroundColor: checked ? theme.accentBgColor : 'transparent',\n }}\n >\n {checked && (\n <Text\n style={{\n color: '#fff',\n fontSize: 11,\n fontWeight: 'bold',\n lineHeight: 11,\n }}\n >\n {'✓'}\n </Text>\n )}\n </View>\n\n <View style={{ flex: 1, gap: 1 }}>\n <Text\n numberOfLines={1}\n style={{\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n fontWeight: checked\n ? (String(theme.fontWeightSemibold) as TextStyle['fontWeight'])\n : undefined,\n }}\n >\n {opt.label}\n </Text>\n {opt.description && (\n <Text\n numberOfLines={1}\n style={{\n fontSize: theme.fontSizeCaption,\n color: theme.windowFgColor,\n opacity: theme.opacityDim,\n }}\n >\n {opt.description}\n </Text>\n )}\n </View>\n </Pressable>\n );\n })}\n </ScrollView>\n </Animated.View>\n </Pressable>\n </Modal>\n </View>\n );\n};\n","import { useWindowDimensions } from 'react-native';\n\n/**\n * GNOME / libadwaita canonical breakpoints (in dp, assuming 1 sp = 1 dp at 1× density).\n *\n * | Name | Max width | Pattern triggered |\n * |----------|-----------|--------------------|\n * | `narrow` | ≤ 400 dp | Collapse split views; sidebar becomes overlay |\n * | `medium` | ≤ 550 dp | Move ViewSwitcher to a bottom bar |\n * | `wide` | ≤ 860 dp | Collapse outer pane in nested split views |\n *\n * Identical thresholds to `@gnome-ui/react`'s `useBreakpoint` — only the\n * measurement source differs (`useWindowDimensions` instead of\n * `window.innerWidth`/a `resize` listener).\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Breakpoint.html\n */\nexport const GNOME_BREAKPOINTS = {\n /** ≤ 400 dp — split views collapse to single pane */\n narrow: 400,\n /** ≤ 550 dp — ViewSwitcher moves to bottom bar */\n medium: 550,\n /** ≤ 860 dp — outer pane of nested split views collapses */\n wide: 860,\n} as const;\n\nexport type GnomeBreakpointName = keyof typeof GNOME_BREAKPOINTS;\n\nexport interface BreakpointState {\n /** Width ≤ 400 dp — split views are collapsed. */\n isNarrow: boolean;\n /** Width ≤ 550 dp — medium or narrower. */\n isMedium: boolean;\n /** Width ≤ 860 dp — wide or narrower. */\n isWide: boolean;\n /** Current window width in dp. */\n width: number;\n}\n\nfunction computeBreakpointState(width: number): BreakpointState {\n return {\n isNarrow: width <= GNOME_BREAKPOINTS.narrow,\n isMedium: width <= GNOME_BREAKPOINTS.medium,\n isWide: width <= GNOME_BREAKPOINTS.wide,\n width,\n };\n}\n\n/**\n * Tracks the window width against GNOME / libadwaita breakpoints.\n *\n * Built on `useWindowDimensions` (not `Dimensions.get` + a manual listener),\n * since it already re-renders its subscribers on every rotation/resize —\n * there is no CSS media query to lean on here, unlike the web version.\n *\n * @example\n * const { isNarrow, isMedium } = useBreakpoint();\n * // isNarrow → true when the window is ≤ 400 dp wide (split views should collapse)\n * // isMedium → true when ≤ 550 dp (use a bottom ViewSwitcher instead)\n */\nexport function useBreakpoint(): BreakpointState {\n const { width } = useWindowDimensions();\n\n return computeBreakpointState(width);\n}\n\n// ─── Responsive values ────────────────────────────────────────────────────────\n\n/** The widest bucket, above every breakpoint, is `base`. */\nexport type GnomeBreakpointBucket = GnomeBreakpointName | 'base';\n\n/**\n * A value that may vary by breakpoint: either the value itself, or a map of\n * breakpoint names to values.\n *\n * The buckets are max-widths, so the map reads like stacked `max-width`\n * media queries — `base` is the widest, and the narrowest matching entry\n * wins:\n *\n * ```ts\n * { base: 3, wide: 2, narrow: 1 }\n * // ≤ 400 dp → 1 | ≤ 860 dp → 2 | wider → 3\n * // (550 dp matches `wide`, since no `medium` entry is given)\n * ```\n */\nexport type ResponsiveValue<T> = T | ({ base?: T } & Partial<Record<GnomeBreakpointName, T>>);\n\n/** Narrowest first, so a lookup can walk from the current bucket outwards. */\nconst BUCKET_FALLBACKS: Record<GnomeBreakpointBucket, GnomeBreakpointBucket[]> = {\n narrow: ['narrow', 'medium', 'wide', 'base'],\n medium: ['medium', 'wide', 'base'],\n wide: ['wide', 'base'],\n base: ['base'],\n};\n\n/**\n * Bucket a width falls into. The same thresholds serve `useBreakpoint` and\n * `BreakpointBin` — a breakpoint applies to whatever it's measured against,\n * window or container alike.\n *\n * A width of `0` means \"not measured yet\" and reports `base`, so the first\n * render matches the widest layout rather than flashing the narrowest one.\n */\nexport function bucketForWidth(width: number): GnomeBreakpointBucket {\n if (width <= 0) {\n return 'base';\n }\n if (width <= GNOME_BREAKPOINTS.narrow) {\n return 'narrow';\n }\n if (width <= GNOME_BREAKPOINTS.medium) {\n return 'medium';\n }\n if (width <= GNOME_BREAKPOINTS.wide) {\n return 'wide';\n }\n return 'base';\n}\n\n/** True when `value` is a breakpoint map rather than a plain value. */\nexport function isResponsiveMap<T>(value: ResponsiveValue<T> | undefined): boolean {\n return typeof value === 'object' && value !== null;\n}\n\n/** Pick the entry for `bucket`, falling back outwards to the wider ones. */\nexport function resolveResponsive<T>(\n value: ResponsiveValue<T> | undefined,\n bucket: GnomeBreakpointBucket,\n fallback: T,\n): T {\n if (value === undefined) {\n return fallback;\n }\n if (!isResponsiveMap(value)) {\n return value as T;\n }\n\n const map = value as Partial<Record<GnomeBreakpointBucket, T>>;\n for (const key of BUCKET_FALLBACKS[bucket]) {\n const entry = map[key];\n if (entry !== undefined) {\n return entry;\n }\n }\n return fallback;\n}\n","import { type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, ViewStyle } from 'react-native';\nimport { Animated, Easing, View } from 'react-native';\n\nimport { Separator } from '@/components/Separator';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\nimport { useBreakpoint } from '@/hooks/useBreakpoint';\n\nexport interface NavigationSplitViewProps {\n /**\n * The sidebar / list pane (left side on wide screens).\n * On narrow screens this is the \"list\" view shown when `showContent` is false.\n */\n sidebar: ReactNode;\n /**\n * The detail / content pane (right side on wide screens).\n * On narrow screens this is the \"detail\" view shown when `showContent` is true.\n */\n content: ReactNode;\n /**\n * Controls which pane is visible on narrow screens (≤ 400 dp).\n * - `false` (default) — show the sidebar list.\n * - `true` — show the content detail.\n *\n * Has no effect on wide screens where both panes are visible simultaneously.\n */\n showContent?: boolean;\n /** Minimum sidebar width in dp. Defaults to `180`. */\n minSidebarWidth?: number;\n /** Maximum sidebar width in dp. Defaults to `280`. */\n maxSidebarWidth?: number;\n /** Fraction of total width given to the sidebar (0–1). Defaults to `0.25`. */\n sidebarWidthFraction?: number;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Two-pane sidebar + content layout following the Adwaita\n * `AdwNavigationSplitView` pattern — mirrors `@gnome-ui/react`'s\n * `NavigationSplitView`.\n *\n * On **wide** screens (`useBreakpoint().isNarrow === false`, > 400 dp) both\n * panes are visible side-by-side, separated by a `Separator`. On **narrow**\n * screens only one pane is shown at a time; `showContent` switches between\n * the sidebar list and the detail view.\n *\n * The web version's `clamp(min, fraction * 100%, max)` sidebar width has no\n * RN equivalent (`StyleSheet` values aren't CSS `calc`/`clamp` expressions),\n * so the container measures its own width via `onLayout` and the same clamp\n * is computed in JS — `0` until the first layout pass resolves, the same\n * one-frame imprecision this package already accepts elsewhere (e.g.\n * `ProgressBar`'s `trackWidth`-dependent math).\n *\n * Narrow-mode pane switching is animated (`translateX`, matching the web\n * CSS's own `transition: transform`), so — unlike `TabPanel`'s simpler\n * `display: 'none'` swap — both panes stay laid out and absolutely\n * positioned rather than being removed from flow, using the same measured\n * container width for the slide distance (RN `transform` has no\n * percentage-of-self units, so this can't be a bare `-100%`/`100%` the way\n * the web version's CSS is). One shared `Animated.Value` drives both\n * panes' opposite-direction translation, the same \"skip the animation on\n * initial mount, only animate subsequent prop changes\" guard `Switch`/\n * `StepIndicator` already established for a controlled boolean prop.\n *\n * The web's `inert` attribute (removes the hidden pane from both the a11y\n * tree and the tab order while it stays mounted off-screen) has no single\n * RN equivalent — reproduced with `accessibilityElementsHidden` +\n * `importantForAccessibility=\"no-hide-descendants\"` (the whole-subtree a11y\n * exclusion, not just the single-element `\"no\"` `PathBar`'s decorative\n * separator uses) plus `pointerEvents=\"none\"` so the off-screen pane can't\n * intercept touches meant for the visible one.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.NavigationSplitView.html\n */\nexport const NavigationSplitView = ({\n sidebar,\n content,\n showContent = false,\n minSidebarWidth = 180,\n maxSidebarWidth = 280,\n sidebarWidthFraction = 0.25,\n style,\n testID,\n}: NavigationSplitViewProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n const { isNarrow } = useBreakpoint();\n\n const [containerWidth, setContainerWidth] = useState(0);\n const progress = useRef(new Animated.Value(showContent ? 1 : 0)).current;\n const isInitialMount = useRef(true);\n\n useEffect(() => {\n if (isInitialMount.current) {\n isInitialMount.current = false;\n\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(showContent ? 1 : 0);\n\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: showContent ? 1 : 0,\n duration: theme.durationNormal,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: true,\n }).start();\n }, [showContent, reducedMotion, progress, theme.durationNormal, theme.easingDefault]);\n\n const handleLayout = (event: LayoutChangeEvent) => {\n setContainerWidth(event.nativeEvent.layout.width);\n };\n\n const sidebarWidth = Math.min(\n Math.max(containerWidth * sidebarWidthFraction, minSidebarWidth),\n maxSidebarWidth,\n );\n\n const sidebarHidden = isNarrow && showContent;\n const contentHidden = isNarrow && !showContent;\n\n const sidebarTranslateX = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [0, -containerWidth],\n });\n const contentTranslateX = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [containerWidth, 0],\n });\n\n return (\n <View\n testID={testID}\n onLayout={handleLayout}\n style={[\n {\n width: '100%',\n height: '100%',\n flexDirection: isNarrow ? 'column' : 'row',\n overflow: 'hidden',\n },\n style,\n ]}\n >\n <Animated.View\n accessibilityElementsHidden={sidebarHidden}\n importantForAccessibility={sidebarHidden ? 'no-hide-descendants' : 'yes'}\n pointerEvents={sidebarHidden ? 'none' : 'auto'}\n style={\n isNarrow\n ? {\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n transform: [{ translateX: sidebarTranslateX }],\n }\n : { width: sidebarWidth, flexShrink: 0 }\n }\n >\n {sidebar}\n </Animated.View>\n\n {!isNarrow && <Separator orientation=\"vertical\" testID={testID && `${testID}-divider`} />}\n\n <Animated.View\n accessibilityElementsHidden={contentHidden}\n importantForAccessibility={contentHidden ? 'no-hide-descendants' : 'yes'}\n pointerEvents={contentHidden ? 'none' : 'auto'}\n style={\n isNarrow\n ? {\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n transform: [{ translateX: contentTranslateX }],\n }\n : { flex: 1, minWidth: 0 }\n }\n >\n {content}\n </Animated.View>\n </View>\n );\n};\n","import { type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Animated, Modal, Pressable } from 'react-native';\n\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\n/**\n * `Animated`'s style interpolation only takes effect on `Animated.*` host\n * components — a plain `Pressable` would silently ignore the animated\n * `opacity` in its style, the same gotcha already documented on `Toast`/\n * `Dialog`/`BottomSheet`. Hoisted to module scope so it isn't recreated on\n * every render.\n */\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable);\n\nexport interface OverlayProps {\n /** Whether the overlay is visible. */\n open: boolean;\n /** Called when the backdrop itself (not its content) is pressed. */\n onDismiss?: () => void;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Standalone backdrop/scrim layer with a fade transition and\n * press-to-dismiss — the shared building block behind `Dialog`,\n * `Dropdown`, `Popover`, and `BottomSheet`'s backdrops, extracted here for\n * building custom overlay UI, mirroring `@gnome-ui/react`'s `Overlay`.\n *\n * Deliberately minimal, same as the web version: no focus trap, no\n * `BackHandler`/Escape handling, no `role`. Use `Dialog`/`Popover`/\n * `BottomSheet` directly when you need those — this is only the fade +\n * dismiss-on-backdrop-tap primitive underneath them.\n *\n * Built on `Modal` rather than the web version's `createPortal` — no\n * `container` prop exists here, since RN's `Modal` has no equivalent\n * mount-target concept (it always renders at the top of the native view\n * hierarchy). Reuses `Dialog`'s exact backdrop recipe: an\n * `AnimatedPressable` backdrop whose `onPress` fires `onDismiss`, wrapping\n * `children` in a no-op `Pressable` so a tap on the content itself never\n * bubbles to the backdrop and dismisses it — the RN analog of the web\n * version's `e.target === e.currentTarget` check, which has no meaning in\n * RN's touch-responder model.\n *\n * **Real, timed exit animation, same technique as `BottomSheet`**: a local\n * `visible` state lags one animation behind the `open` prop, flipping to\n * `false` only in the fade-out `Animated.timing`'s own completion callback\n * — not a `setTimeout` racing a hardcoded duration like the web version,\n * since `Animated`'s callback already fires exactly when the animation\n * actually finishes. `useBodyScrollLock` has no RN equivalent needed —\n * `Modal` already blocks all interaction with whatever's behind it.\n */\nexport const Overlay = ({ open, onDismiss, children, style, testID }: OverlayProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const [visible, setVisible] = useState(open);\n const opacity = useRef(new Animated.Value(open ? 1 : 0)).current;\n\n useEffect(() => {\n if (open) {\n setVisible(true);\n }\n }, [open]);\n\n useEffect(() => {\n if (!visible) {\n return;\n }\n\n if (open) {\n if (reducedMotion) {\n opacity.setValue(1);\n\n return;\n }\n\n opacity.setValue(0);\n Animated.timing(opacity, {\n toValue: 1,\n duration: theme.durationNormal,\n useNativeDriver: true,\n }).start();\n } else {\n if (reducedMotion) {\n setVisible(false);\n\n return;\n }\n\n Animated.timing(opacity, {\n toValue: 0,\n duration: theme.durationNormal,\n useNativeDriver: true,\n }).start(({ finished }) => {\n if (finished) {\n setVisible(false);\n }\n });\n }\n }, [open, visible, reducedMotion, opacity, theme.durationNormal]);\n\n if (!visible) {\n return null;\n }\n\n return (\n <Modal visible={visible} transparent animationType=\"none\" statusBarTranslucent>\n <AnimatedPressable\n testID={testID}\n onPress={onDismiss}\n accessible={false}\n style={[\n {\n flex: 1,\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: theme.dialogBackdropColor,\n opacity,\n },\n style,\n ]}\n >\n <Pressable onPress={() => {}} accessible={false}>\n {children}\n </Pressable>\n </AnimatedPressable>\n </Modal>\n );\n};\n","import { ViewConceal, ViewReveal } from '@gnome-ui/icons';\nimport { forwardRef, type ReactNode, useState } from 'react';\nimport type { TextInput } from 'react-native';\n\nimport { EntryRow, type EntryRowProps } from '@/components/EntryRow';\nimport { IconButton } from '@/components/IconButton';\n\nexport interface PasswordEntryRowProps extends Omit<EntryRowProps, 'secureTextEntry' | 'trailing'> {\n /** Additional trailing widgets placed before the reveal button. */\n trailing?: ReactNode;\n}\n\n/**\n * Password entry row with a built-in reveal/conceal toggle, mirroring\n * `AdwPasswordEntryRow` and `@gnome-ui/react`'s own `PasswordEntryRow`. It's\n * an `EntryRow` that masks its input and always carries a trailing button to\n * show or hide what's been typed. Use inside a `BoxedList` for password\n * settings fields — and don't add your own reveal button through `trailing`,\n * which is for anything that should sit *before* this one.\n *\n * `type={revealed ? 'text' : 'password'}` becomes RN's own\n * `secureTextEntry`, and `autoComplete=\"current-password\"` ports as-is —\n * RN's `autoComplete` accepts the same value and is what lets password\n * managers and the platform keyboard offer a saved credential. Pass\n * `\"new-password\"` on registration or change-password forms.\n *\n * The reveal control is the already-shipped `IconButton` rather than a\n * hand-rolled pressable, which costs one visual detail: `IconButton` is\n * circular (it's `Button` at `shape=\"circular\"`), where the web's\n * `.revealButton` is a 32 dp square with a 6 dp radius. A circular flat icon\n * button is the idiomatic touch control and keeps this row consistent with\n * every other icon action in the package, so it's the better trade than\n * introducing a fifth flat-pressable implementation. The CSS's resting\n * `opacity: 0.55` is dropped too: it exists so the button can brighten on\n * hover, and with no hover on a touch device a permanently dimmed control is\n * just harder to see — the same reasoning that collapses `:hover` everywhere\n * else here.\n *\n * The web needs `e.stopPropagation()` so pressing the button doesn't also\n * trigger the row's focus-the-input click. RN's responder system routes a\n * touch to the innermost pressable, so there's nothing to stop.\n *\n * @example\n * <BoxedList>\n * <PasswordEntryRow title=\"Password\" value={password} onValueChange={setPassword} />\n * </BoxedList>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.PasswordEntryRow.html\n */\nexport const PasswordEntryRow = forwardRef<TextInput, PasswordEntryRowProps>(\n function PasswordEntryRow(\n { trailing, disabled = false, autoComplete = 'current-password', ...props },\n ref,\n ) {\n const [revealed, setRevealed] = useState(false);\n\n return (\n <EntryRow\n ref={ref}\n {...props}\n autoComplete={autoComplete}\n secureTextEntry={!revealed}\n disabled={disabled}\n trailing={\n <>\n {trailing}\n <IconButton\n icon={revealed ? ViewConceal : ViewReveal}\n label={revealed ? 'Conceal password' : 'Reveal password'}\n variant=\"flat\"\n disabled={disabled}\n accessibilityState={{ selected: revealed, disabled }}\n onPress={() => setRevealed((current) => !current)}\n />\n </>\n }\n />\n );\n },\n);\n","import { ViewConceal, ViewReveal } from '@gnome-ui/icons';\nimport { forwardRef, useState } from 'react';\nimport type { StyleProp, TextInput, TextInputProps, TextStyle, ViewStyle } from 'react-native';\nimport { TextInput as RNTextInput, View } from 'react-native';\n\nimport { IconButton } from '@/components/IconButton';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface PasswordFieldProps extends Omit<TextInputProps, 'style' | 'secureTextEntry'> {\n /** Visible label rendered above the input. */\n label?: string;\n /** Helper text rendered below the input. Hidden when `error` is set. */\n helperText?: string;\n /**\n * Error message rendered below the input in place of `helperText`.\n * Also applies the error visual state to the input border.\n */\n error?: string;\n /**\n * Show the peek toggle button that reveals the password as plain text.\n * Mirrors `GtkPasswordEntry`'s `show-peek-icon` property. Defaults to `true`.\n */\n revealable?: boolean;\n /** Accessible label for the toggle button while the password is hidden. */\n revealLabel?: string;\n /** Accessible label for the toggle button while the password is revealed. */\n concealLabel?: string;\n /** Style applied to the wrapping `View`. */\n style?: StyleProp<ViewStyle>;\n /** Style applied to the `TextInput` itself. */\n inputStyle?: StyleProp<TextStyle>;\n}\n\n/**\n * Duplicated from `TextField` rather than shared — same small pure helper,\n * two call sites, not worth extracting (the same call already duplicated\n * verbatim between `TextField` and `SearchBar`).\n */\nfunction dimColor(color: string, factor: number): string {\n const match = color.match(/^rgba?\\(([^,]+),([^,]+),([^,]+),?([^)]+)?\\)$/);\n\n if (!match) {\n return color;\n }\n\n const [, r, g, b, a] = match;\n const alpha = (a ? Number.parseFloat(a) : 1) * factor;\n\n return `rgba(${r.trim()}, ${g.trim()}, ${b.trim()}, ${alpha})`;\n}\n\nconst TOGGLE_SIZE = 28;\n\n/**\n * Single-line password input with a peek toggle to reveal the value as\n * plain text, mirroring `@gnome-ui/react`'s `PasswordField`.\n *\n * `TextField` plus the exact reveal/conceal recipe `PasswordEntryRow`\n * already established for this package: `secureTextEntry` (not `revealed\n * ? 'text' : 'password'`, which has no RN equivalent), and the shipped\n * `IconButton` for the toggle rather than a hand-rolled pressable — a\n * circular icon button at `size=\"sm\"` (28 dp) here, matching the web\n * version's own `size=\"sm\"` on this component specifically (as opposed to\n * `PasswordEntryRow`'s default size, since that one sits inside a taller\n * row). The toggle is positioned absolutely at the input's trailing edge,\n * vertically centered — the RN port of the source CSS's `position:\n * absolute; inset-block-start: 50%; transform: translateY(-50%)` — and the\n * input gets extra trailing padding (`.hasToggle`'s `padding-inline-end`)\n * so typed text never runs under the button.\n *\n * Use this over `TextField` with a manually-set `secureTextEntry` (which\n * has no reveal affordance at all).\n *\n * @see https://docs.gtk.org/gtk4/class.PasswordEntry.html\n */\nexport const PasswordField = forwardRef<TextInput, PasswordFieldProps>(function PasswordField(\n {\n label,\n helperText,\n error,\n revealable = true,\n revealLabel = 'Show password',\n concealLabel = 'Hide password',\n style,\n inputStyle,\n editable = true,\n onFocus,\n onBlur,\n accessibilityLabel,\n accessibilityHint,\n ...inputProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const contrast = useResolvedContrast();\n const [focused, setFocused] = useState(false);\n const [revealed, setRevealed] = useState(false);\n const disabled = !editable;\n const hint = error ?? helperText;\n const showToggle = revealable;\n\n const borderColor = error ? theme.errorColor : focused ? theme.accentColor : theme.borderSubtle;\n\n return (\n <View style={[{ gap: theme.space1, opacity: disabled ? theme.opacityDisabled : 1 }, style]}>\n {label && (\n <Text\n variant=\"body\"\n style={{ fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'] }}\n >\n {label}\n </Text>\n )}\n\n <View style={{ position: 'relative', justifyContent: 'center' }}>\n <RNTextInput\n ref={ref}\n editable={editable}\n secureTextEntry={!revealed}\n accessibilityLabel={accessibilityLabel ?? label}\n accessibilityHint={accessibilityHint ?? hint}\n placeholderTextColor={dimColor(theme.windowFgColor, theme.opacityDim)}\n onFocus={(event) => {\n setFocused(true);\n onFocus?.(event);\n }}\n onBlur={(event) => {\n setFocused(false);\n onBlur?.(event);\n }}\n style={[\n {\n width: '100%',\n paddingVertical: 8,\n paddingHorizontal: theme.space2,\n paddingEnd: showToggle ? TOGGLE_SIZE + theme.space2 + 6 : theme.space2,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n lineHeight: Math.round(theme.fontSizeBody * theme.lineHeightBody),\n color: theme.viewFgColor,\n backgroundColor: theme.viewBgColor,\n borderRadius: theme.radiusMd,\n borderWidth: contrast === 'more' ? 2 : 1,\n borderColor,\n } satisfies TextStyle,\n inputStyle,\n ]}\n {...inputProps}\n />\n\n {showToggle && (\n <View style={{ position: 'absolute', end: 6 }}>\n <IconButton\n icon={revealed ? ViewConceal : ViewReveal}\n label={revealed ? concealLabel : revealLabel}\n variant=\"flat\"\n size=\"sm\"\n disabled={disabled}\n accessibilityState={{ selected: revealed, disabled }}\n onPress={() => setRevealed((current) => !current)}\n />\n </View>\n )}\n </View>\n\n {hint && (\n <Text\n variant=\"caption\"\n style={{\n color: error ? theme.errorColor : theme.windowFgColor,\n opacity: error ? 1 : theme.opacityDim,\n }}\n >\n {hint}\n </Text>\n )}\n </View>\n );\n});\n","import type { ReactNode } from 'react';\nimport { Fragment } from 'react';\nimport type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native';\nimport { Pressable, Text as RNText, View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface PathBarSegment {\n /** Display label for this path segment. */\n label: string;\n /** Opaque path value passed to `onNavigate` when the segment is pressed. */\n path: string;\n /** Optional icon placed before the label (e.g. a folder icon). Rendered as-is. */\n icon?: ReactNode;\n}\n\nexport interface PathBarProps extends Omit<ViewProps, 'style'> {\n /**\n * Ordered path segments from root to current location. The last segment\n * is the current folder and is rendered non-interactive.\n */\n segments: PathBarSegment[];\n /**\n * Called when the user presses a non-current segment. Receives the\n * `path` and zero-based `index` of the pressed segment.\n */\n onNavigate?: (path: string, index: number) => void;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Breadcrumb path bar for navigating a hierarchical location.\n *\n * Mirrors the location bar in GNOME Files (Nautilus). Segments are\n * separated by chevron dividers. All segments except the last are\n * interactive — pressing them calls `onNavigate`. The last segment\n * represents the current location and renders as a static, bold label.\n *\n * Rebuilt with `Pressable`/`View`/`Text` rather than ported from\n * `@gnome-ui/react`'s `<nav><ol><li>`: RN's `AccessibilityRole` union has\n * neither a \"navigation\" landmark nor a breadcrumb-list role (the same gap\n * that dropped `Sidebar`'s `<nav>` role), so those are dropped rather than\n * faked — each interactive segment still gets its own\n * `accessibilityRole=\"button\"` and `accessibilityLabel`. The separator\n * chevron is a Unicode `›` glyph instead of the web version's inline SVG\n * path, matching this package's established no-SVG-dependency convention.\n *\n * @see https://developer.gnome.org/hig/patterns/nav/search.html\n */\nexport const PathBar = ({ segments, onNavigate, style, ...viewProps }: PathBarProps) => {\n const theme = useGnomeTheme();\n\n return (\n <RNView\n style={[{ flexDirection: 'row', flexWrap: 'wrap', alignItems: 'center' }, style]}\n {...viewProps}\n >\n {segments.map((segment, index) => {\n const isCurrent = index === segments.length - 1;\n\n return (\n <Fragment key={segment.path}>\n {index > 0 && (\n <RNText\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n color: theme.windowFgColor,\n opacity: 0.5,\n marginHorizontal: 1,\n fontSize: theme.fontSizeBody,\n }}\n >\n {'›'}\n </RNText>\n )}\n\n {isCurrent ? (\n <RNView\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n gap: 4,\n paddingVertical: 2,\n paddingHorizontal: theme.space1,\n maxWidth: 200,\n }}\n >\n {segment.icon && <RNView style={{ flexShrink: 0 }}>{segment.icon}</RNView>}\n <RNText\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n color: theme.windowFgColor,\n }}\n >\n {segment.label}\n </RNText>\n </RNView>\n ) : (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={segment.label}\n onPress={() => onNavigate?.(segment.path, index)}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n gap: 4,\n paddingVertical: 2,\n paddingHorizontal: theme.space1,\n maxWidth: 160,\n borderRadius: theme.radiusSm,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n })}\n >\n {segment.icon && <RNView style={{ flexShrink: 0 }}>{segment.icon}</RNView>}\n <RNText\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n }}\n >\n {segment.label}\n </RNText>\n </Pressable>\n )}\n </Fragment>\n );\n })}\n </RNView>\n );\n};\n","import {\n cloneElement,\n type ReactElement,\n type ReactNode,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport type {\n GestureResponderEvent,\n LayoutChangeEvent,\n PressableProps,\n StyleProp,\n ViewStyle,\n} from 'react-native';\nimport { Animated, BackHandler, Dimensions, Modal, Pressable, View } from 'react-native';\n\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport type PopoverPlacement = 'top' | 'bottom' | 'left' | 'right';\n\nexport interface PopoverProps {\n /**\n * The rich content rendered inside the popover panel.\n * Can include interactive elements (buttons, links, forms).\n */\n content: ReactNode;\n /**\n * Preferred placement relative to the trigger.\n * Flips automatically when there is not enough viewport space.\n * Defaults to `\"bottom\"`.\n */\n placement?: PopoverPlacement;\n /**\n * Whether the popover is open (controlled mode).\n * Omit to use uncontrolled mode where the trigger toggles it.\n */\n open?: boolean;\n /** Called when open state should change. */\n onOpenChange?: (open: boolean) => void;\n /** Extra style on the popover panel itself. */\n panelStyle?: StyleProp<ViewStyle>;\n /**\n * The trigger element. Must be a single element built on `Pressable`\n * (e.g. `Button`, `Card`) that forwards its `ref` to the underlying `View`.\n */\n children: ReactElement<PressableProps & { ref?: Ref<View> }>;\n}\n\ninterface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface Size {\n width: number;\n height: number;\n}\n\ninterface Position {\n top: number;\n left: number;\n placement: PopoverPlacement;\n}\n\nconst GAP = 8;\nconst MARGIN = 10;\nconst ARROW = 6;\n\nfunction assignRef<T>(ref: Ref<T> | undefined, value: T | null) {\n if (!ref) {\n return;\n }\n\n if (typeof ref === 'function') {\n ref(value);\n\n return;\n }\n\n (ref as { current: T | null }).current = value;\n}\n\nfunction placementRect(trigger: Rect, panel: Size, placement: PopoverPlacement) {\n switch (placement) {\n case 'top':\n return {\n top: trigger.y - panel.height - GAP,\n left: trigger.x + trigger.width / 2 - panel.width / 2,\n };\n case 'bottom':\n return {\n top: trigger.y + trigger.height + GAP,\n left: trigger.x + trigger.width / 2 - panel.width / 2,\n };\n case 'left':\n return {\n top: trigger.y + trigger.height / 2 - panel.height / 2,\n left: trigger.x - panel.width - GAP,\n };\n case 'right':\n return {\n top: trigger.y + trigger.height / 2 - panel.height / 2,\n left: trigger.x + trigger.width + GAP,\n };\n }\n}\n\nfunction computePosition(trigger: Rect, panel: Size, preferred: PopoverPlacement): Position {\n const { width: vw, height: vh } = Dimensions.get('window');\n\n const opposite: PopoverPlacement =\n preferred === 'top'\n ? 'bottom'\n : preferred === 'bottom'\n ? 'top'\n : preferred === 'left'\n ? 'right'\n : 'left';\n const placements: PopoverPlacement[] = [preferred, opposite, 'bottom', 'top', 'left', 'right'];\n\n for (const p of placements) {\n const { top, left } = placementRect(trigger, panel, p);\n const fitsH = left >= MARGIN && left + panel.width <= vw - MARGIN;\n const fitsV = top >= MARGIN && top + panel.height <= vh - MARGIN;\n\n if (fitsH && fitsV) {\n return {\n top: Math.max(MARGIN, Math.min(top, vh - panel.height - MARGIN)),\n left: Math.max(MARGIN, Math.min(left, vw - panel.width - MARGIN)),\n placement: p,\n };\n }\n }\n\n const { top, left } = placementRect(trigger, panel, preferred);\n\n return {\n top: Math.max(MARGIN, Math.min(top, vh - panel.height - MARGIN)),\n left: Math.max(MARGIN, Math.min(left, vw - panel.width - MARGIN)),\n placement: preferred,\n };\n}\n\n/**\n * Floating panel anchored to a trigger element, following the Adwaita\n * `GtkPopover` pattern. Unlike `Tooltip`, a popover can contain rich\n * interactive content (buttons, links, forms).\n *\n * Rebuilt with `View`/`Pressable`/`Modal` rather than ported from\n * `@gnome-ui/react`'s DOM `Portal` + manual focus trap, reusing this\n * package's own established pieces rather than re-deriving them: `Tooltip`'s\n * `cloneElement`-onto-an-arbitrary-trigger architecture and 4-placement\n * fallback-cascade positioning (`computePosition`, same shape, no arrow-\n * offset-shift-when-clamped complexity — same simplification `Tooltip`\n * already accepted), and `Dropdown`'s toggle-on-press + full-screen backdrop\n * `Pressable` that closes on an outside tap (the RN analog of the web\n * version's document-level \"click outside\" listener) plus reduced-motion\n * fade-in.\n *\n * **Deliberate divergence from `Dropdown`'s backdrop structure**: `Dropdown`\n * nests its panel directly inside the backdrop `Pressable` and gets away\n * with it because almost every pixel of its panel is itself a `Pressable`\n * option row, which claims the touch responder before it can bubble to the\n * backdrop. A popover's `content` is arbitrary — likely to have inert\n * padding/whitespace/text with no `Pressable` of its own — so nesting the\n * same way would let a tap on inert panel space fall through to the\n * backdrop and close the popover, unlike the web version's `.contains()`\n * check (which never closes on *any* tap inside the panel). Fixed with\n * `onStartShouldSetResponder={() => true}` on the panel itself: it claims\n * the touch responder for any touch RN's negotiation hasn't already given to\n * a deeper `Pressable` inside `content`, without making the panel itself\n * behave like a button.\n *\n * `BackHandler`'s `hardwareBackPress` (wired the same way `Dialog` already\n * does) is the Android analog of the web version's document-level Escape\n * listener — there is no keyboard `Escape` to catch on a touch-first device.\n * Focus-trapping and focus-restore-on-close have no port: there is no DOM\n * `document.activeElement`/`querySelector` equivalent in RN, the same gap\n * that already left every other floating component in this package (\n * `Dialog`, `Tooltip`, `Dropdown`) without them.\n *\n * The web version's rotated-square-with-matching-background arrow (relying\n * on same-color blending across a straddled panel edge and CSS stacking\n * order) is replaced with `Tooltip`'s simpler transparent-border-triangle\n * technique — same visual affordance (a pointer toward the trigger), a much\n * simpler RN-native primitive.\n *\n * `role=\"dialog\"` on the panel ports 1:1 from RN's newer web-aligned `Role`\n * union (the same one `Dialog`/`Tooltip` already use) — no substitution\n * needed. `aria-haspopup`/`aria-controls` have no RN equivalent (no\n * cross-platform relationship-attribute prop); only `accessibilityState.\n * expanded` is wired on the trigger, the same subset `Dropdown`'s own\n * trigger already exposes.\n *\n * @example\n * <Popover content={<Text>Rich content here</Text>}>\n * <Button>Open</Button>\n * </Popover>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Popover.html\n */\nexport const Popover = ({\n content,\n placement: preferredPlacement = 'bottom',\n open: controlledOpen,\n onOpenChange,\n panelStyle,\n children,\n}: PopoverProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const isControlled = controlledOpen !== undefined;\n const [uncontrolledOpen, setUncontrolledOpen] = useState(false);\n const open = isControlled ? controlledOpen : uncontrolledOpen;\n\n const [triggerRect, setTriggerRect] = useState<Rect | null>(null);\n const [panelSize, setPanelSize] = useState<Size | null>(null);\n const [pos, setPos] = useState<Position | null>(null);\n\n const triggerRef = useRef<View>(null);\n const progress = useRef(new Animated.Value(0)).current;\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (!isControlled) {\n setUncontrolledOpen(next);\n }\n\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange],\n );\n\n const close = useCallback(() => setOpen(false), [setOpen]);\n const toggle = useCallback(() => setOpen(!open), [open, setOpen]);\n\n // Measure the trigger's on-screen rect as soon as it's asked to show.\n useEffect(() => {\n if (!open) {\n setTriggerRect(null);\n setPanelSize(null);\n setPos(null);\n\n return;\n }\n\n triggerRef.current?.measureInWindow((x, y, width, height) => {\n setTriggerRect({ x, y, width, height });\n });\n }, [open]);\n\n // Resolve the final position once both the trigger's rect and the panel's\n // own rendered size are known.\n useEffect(() => {\n if (!triggerRect || !panelSize) {\n return;\n }\n\n setPos(computePosition(triggerRect, panelSize, preferredPlacement));\n }, [triggerRect, panelSize, preferredPlacement]);\n\n useEffect(() => {\n if (!pos) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n progress.setValue(0);\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationFast,\n useNativeDriver: true,\n }).start();\n }, [pos, reducedMotion, progress, theme.durationFast]);\n\n // Android hardware back button closes the popover — the analog of the web\n // version's document-level Escape listener, same pattern `Dialog` uses.\n useEffect(() => {\n if (!open) {\n return;\n }\n\n const subscription = BackHandler.addEventListener('hardwareBackPress', () => {\n close();\n\n return true;\n });\n\n return () => subscription.remove();\n }, [open, close]);\n\n const handlePanelLayout = (e: LayoutChangeEvent) => {\n const { width, height } = e.nativeEvent.layout;\n\n setPanelSize({ width, height });\n };\n\n const arrowBase = {\n position: 'absolute' as const,\n width: 0,\n height: 0,\n borderWidth: ARROW,\n borderColor: 'transparent',\n };\n\n const arrowStyle = (() => {\n switch (pos?.placement) {\n case 'top':\n return {\n ...arrowBase,\n top: '100%' as const,\n left: '50%' as const,\n marginLeft: -ARROW,\n borderTopColor: theme.popoverBgColor,\n };\n case 'bottom':\n return {\n ...arrowBase,\n bottom: '100%' as const,\n left: '50%' as const,\n marginLeft: -ARROW,\n borderBottomColor: theme.popoverBgColor,\n };\n case 'left':\n return {\n ...arrowBase,\n left: '100%' as const,\n top: '50%' as const,\n marginTop: -ARROW,\n borderLeftColor: theme.popoverBgColor,\n };\n case 'right':\n return {\n ...arrowBase,\n right: '100%' as const,\n top: '50%' as const,\n marginTop: -ARROW,\n borderRightColor: theme.popoverBgColor,\n };\n default:\n return null;\n }\n })();\n\n const existingRef = (children.props as { ref?: Ref<View> }).ref;\n\n const trigger = cloneElement(children, {\n ref: (node: View | null) => {\n (triggerRef as { current: View | null }).current = node;\n assignRef(existingRef, node);\n },\n accessibilityState: { expanded: open, ...children.props.accessibilityState },\n onPress: (e: GestureResponderEvent) => {\n toggle();\n children.props.onPress?.(e);\n },\n });\n\n return (\n <>\n {trigger}\n <Modal\n visible={open}\n transparent\n animationType=\"none\"\n statusBarTranslucent\n onRequestClose={close}\n >\n <Pressable onPress={close} accessible={false} style={{ flex: 1 }}>\n <Animated.View\n onLayout={handlePanelLayout}\n role=\"dialog\"\n accessible\n onStartShouldSetResponder={() => true}\n style={[\n {\n position: 'absolute',\n minWidth: 180,\n maxWidth: 320,\n backgroundColor: theme.popoverBgColor,\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusLg,\n padding: theme.space2,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 4 },\n shadowOpacity: 0.15,\n shadowRadius: 12,\n elevation: 6,\n },\n pos\n ? { top: pos.top, left: pos.left, opacity: progress }\n : { top: -9999, left: -9999, opacity: 0 },\n panelStyle,\n ]}\n >\n {content}\n {arrowStyle && <View style={arrowStyle} />}\n </Animated.View>\n </Pressable>\n </Modal>\n </>\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { StyleProp, TextStyle, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface PreferencesGroupProps extends Omit<ViewProps, 'style'> {\n /** Group heading. */\n title?: string;\n /** Optional description rendered below the title. */\n description?: string;\n /** Widget placed at the trailing edge of the title row (e.g. a reset `Button`). */\n headerSuffix?: ReactNode;\n /** `BoxedList` rows or any row-shaped content. */\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Titled section that wraps a `BoxedList` with an optional description,\n * mirroring `AdwPreferencesGroup` and `@gnome-ui/react`'s own\n * `PreferencesGroup`.\n *\n * Use it to group related settings under a named heading. The group is\n * purely a layout and labelling wrapper — it doesn't render the `BoxedList`\n * itself; pass one as `children`.\n *\n * The web's empty `.content` wrapper looks like dead markup but is\n * load-bearing, so it's kept: the group is a 12 dp-gap flex column, and\n * without that wrapper every child would become a flex item of the group and\n * pick up a 12 dp gap between the rows themselves, instead of one gap\n * between the header and the content as a whole.\n *\n * The title is `Text variant=\"body\"` with an explicit semibold weight rather\n * than `variant=\"heading\"`, which is body-sized but **bold** and on the\n * tighter heading line-height — the CSS `.title` is specifically semibold at\n * the body line-height. It keeps the `header` accessibility role anyway\n * (passed explicitly), since a settings-group heading is exactly the kind of\n * landmark a screen reader rotor should list — the same call `StatusPage`\n * makes for its own title.\n *\n * `min-width: 0` on the header text has no port and needs none: it's the\n * classic CSS flexbox override for a min-content floor that Yoga doesn't\n * apply in the first place.\n *\n * @example\n * <PreferencesGroup\n * title=\"Appearance\"\n * description=\"How the app looks on this device.\"\n * headerSuffix={<Button variant=\"flat\" onPress={reset}>Reset</Button>}\n * >\n * <BoxedList>{rows}</BoxedList>\n * </PreferencesGroup>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.PreferencesGroup.html\n */\nexport const PreferencesGroup = forwardRef<View, PreferencesGroupProps>(function PreferencesGroup(\n { title, description, headerSuffix, children, style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const hasHeader = Boolean(title || description || headerSuffix);\n\n return (\n <RNView ref={ref} style={[{ gap: theme.space2 }, style]} {...viewProps}>\n {hasHeader ? (\n <RNView style={{ flexDirection: 'row', alignItems: 'baseline', gap: theme.space2 }}>\n <RNView style={{ flex: 1, gap: 2 }}>\n {title ? (\n <Text\n variant=\"body\"\n accessibilityRole=\"header\"\n style={{\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n ) : null}\n\n {description ? (\n <Text variant=\"caption\" color=\"dim\">\n {description}\n </Text>\n ) : null}\n </RNView>\n\n {headerSuffix ? <RNView style={{ flexShrink: 0 }}>{headerSuffix}</RNView> : null}\n </RNView>\n ) : null}\n\n <RNView>{children}</RNView>\n </RNView>\n );\n});\n","import { useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { Animated, Easing } from 'react-native';\n\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\n\nexport type ProgressBarVariant = 'accent' | 'success' | 'warning' | 'error';\n\nexport interface ProgressBarProps extends Omit<ViewProps, 'style'> {\n /**\n * Current progress value between `0` and `1` (e.g. `0.6` = 60%). Omit\n * or set to `undefined` for the indeterminate (pulsing) state.\n */\n value?: number;\n /**\n * Fill color of the progress indicator.\n * - `\"accent\"` (default) — blue, general-purpose progress.\n * - `\"success\"` — green, completed or healthy state.\n * - `\"warning\"` — yellow, approaching a limit.\n * - `\"error\"` — red, failed or critical state.\n */\n variant?: ProgressBarVariant;\n /** Accessible label describing what is loading. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Determinate and indeterminate progress bar following the Adwaita style.\n *\n * - **Determinate** — pass `value` (0–1) to show exact progress; the fill\n * width animates on every change, matching the web version's\n * `transition: width`.\n * - **Indeterminate** — omit `value` for a 40%-wide bar that slides left\n * to right on an `Animated.loop`, matching the web version's CSS\n * `@keyframes pulse`.\n *\n * `useReducedMotion()` is honored per the *source CSS's own* per-state\n * behavior rather than one uniform rule: determinate width changes simply\n * skip the transition (duration `0`, still an immediate jump like the CSS\n * `transition: none`), while the indeterminate pulse stops entirely and\n * freezes as a static, full-width, 50%-opacity bar — exactly what\n * `@media (prefers-reduced-motion: reduce) { .indeterminate { animation:\n * none; left: 0; width: 100%; opacity: 0.5 } }` does. This differs from\n * `Spinner`, whose reduced-motion behavior *slows* its animation instead\n * of stopping it — each component mirrors its own source CSS rather than\n * applying a single reduced-motion policy across the package.\n *\n * Both animated states are driven entirely by `transform` rather than by\n * layout properties (`width`/`left`), so both can run\n * `useNativeDriver: true`: RN's native animation driver can only animate\n * transforms, and a JS-driven (`useNativeDriver: false`) `Animated.timing`\n * or `Animated.loop` schedules its next frame via a plain `setTimeout` —\n * one that routinely fires a tick after a test's `render()` returns but\n * before unmount, outside any `act()` boundary, producing a real (if\n * harmless) \"update not wrapped in act()\" warning under\n * `@testing-library/react-native` on every render of this component. The\n * determinate fill stays a constant `width: '100%'` and instead animates\n * `transform: [{ scaleX }]` with `transformOrigin: 'left'` (so it grows\n * from the left edge, not from center); the indeterminate pulse similarly\n * animates `transform: [{ translateX }]` from `-0.4 * trackWidth` to\n * `trackWidth` (measured via `onLayout`, since a transform's `translateX`\n * needs a pixel offset, not a percentage) rather than animating `left`.\n * Both reproduce the exact same visual motion as the source CSS's\n * `width`/`left` transitions while running fully off the JS thread, which\n * eliminates the warning at its root rather than suppressing it.\n *\n * `role=\"progressbar\"` maps directly to RN's own `accessibilityRole`\n * (unlike `Spinner`, which had to substitute for the web's `role=\"status\"`\n * — `ProgressBar`'s web role already has a 1:1 RN equivalent).\n * `accessibilityValue` carries `now`/`min`/`max` for the determinate case;\n * the indeterminate case omits all three, RN's equivalent of the web\n * version omitting `aria-valuenow`/`aria-valuemin`/`aria-valuemax`. Web's\n * `aria-labelledby` (an id-relationship prop) has no RN equivalent — RN\n * has no DOM ids — so only `aria-label` (`accessibilityLabel`) is ported.\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/progress.html\n */\nexport const ProgressBar = ({\n value,\n variant = 'accent',\n accessibilityLabel,\n style,\n onLayout,\n ...viewProps\n}: ProgressBarProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const reducedMotion = useReducedMotion();\n\n const isIndeterminate = value === undefined || value === null;\n const clamped = isIndeterminate ? undefined : Math.min(1, Math.max(0, value));\n const percent = clamped !== undefined ? clamped * 100 : undefined;\n\n const width = useRef(new Animated.Value(clamped ?? 0)).current;\n const pulse = useRef(new Animated.Value(0)).current;\n const [trackWidth, setTrackWidth] = useState(0);\n\n const handleTrackLayout = (event: LayoutChangeEvent) => {\n setTrackWidth(event.nativeEvent.layout.width);\n onLayout?.(event);\n };\n\n useEffect(() => {\n if (isIndeterminate) {\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(width, {\n toValue: clamped ?? 0,\n duration: reducedMotion ? 0 : theme.durationNormal,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: true,\n }).start();\n }, [clamped, isIndeterminate, reducedMotion, width, theme.durationNormal, theme.easingDefault]);\n\n useEffect(() => {\n if (!isIndeterminate || reducedMotion) {\n return;\n }\n\n pulse.setValue(0);\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n const animation = Animated.loop(\n Animated.timing(pulse, {\n toValue: 1,\n duration: 1400,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: true,\n }),\n );\n\n animation.start();\n\n return () => animation.stop();\n }, [isIndeterminate, reducedMotion, pulse, theme.easingDefault]);\n\n const trackColor = scheme === 'dark' ? theme.dark2 : theme.light3;\n const variantColor: Record<ProgressBarVariant, string> = {\n accent: theme.accentBgColor,\n success: theme.successBgColor,\n warning: theme.warningBgColor,\n error: theme.errorBgColor,\n };\n\n const fillStyle = isIndeterminate\n ? reducedMotion\n ? { left: 0, width: '100%', opacity: 0.5 }\n : {\n left: 0,\n width: '40%',\n transform: [\n {\n translateX: pulse.interpolate({\n inputRange: [0, 1],\n outputRange: [-0.4 * trackWidth, trackWidth],\n }),\n },\n ],\n }\n : {\n width: '100%',\n transformOrigin: 'left',\n transform: [{ scaleX: width }],\n };\n\n return (\n <Animated.View\n accessible\n accessibilityRole=\"progressbar\"\n accessibilityLabel={accessibilityLabel}\n accessibilityValue={\n clamped !== undefined ? { min: 0, max: 100, now: Math.round(percent as number) } : undefined\n }\n onLayout={handleTrackLayout}\n style={[\n {\n width: '100%',\n height: 6,\n borderRadius: theme.radiusPill,\n backgroundColor: trackColor,\n overflow: 'hidden',\n },\n style,\n ]}\n {...viewProps}\n >\n <Animated.View\n style={[\n {\n position: isIndeterminate ? 'absolute' : 'relative',\n top: 0,\n height: '100%',\n borderRadius: theme.radiusPill,\n backgroundColor: variantColor[variant],\n },\n fillStyle as ViewStyle,\n ]}\n />\n </Animated.View>\n );\n};\n","import { forwardRef, useEffect, useRef } from 'react';\nimport type { PressableProps, StyleProp, View, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable } from 'react-native';\n\nimport { useGnomeTheme, useResolvedColorScheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface RadioButtonProps extends Omit<PressableProps, 'children' | 'style' | 'onPress'> {\n /** Whether this radio button is the selected one in its group. */\n value: boolean;\n /**\n * Called when pressed while unselected. RN has no `name`-attribute\n * grouping like the web `<input type=\"radio\">`, so mutual exclusivity\n * within a group is the consumer's responsibility — render one\n * `RadioButton` per option and drive `value` from shared selection\n * state, flipping it in `onSelect`.\n */\n onSelect?: () => void;\n /** Accessible label. Required when no visible label is associated. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\nconst SIZE = 18;\nconst DOT_SIZE = 6;\n\n/**\n * Single-selection radio button following the GNOME HIG and Adwaita style.\n *\n * Rebuilt on `Pressable`/`Animated.View` rather than ported from\n * `@gnome-ui/react`'s `<input type=\"radio\">`: reuses `Checkbox`'s exact\n * border/background transition technique (same `Animated.Value`, same\n * mount-skip guard so it never animates before the user touches it, same\n * `useResolvedColorScheme()`/`useResolvedContrast()` branching for the\n * idle border color, since the source CSS hardcodes those the same way\n * `Checkbox.module.css` does) — just circular, with a filled dot instead\n * of a checkmark, and no indeterminate state.\n *\n * The web version's `name`-attribute grouping has no RN equivalent, so\n * grouping is fully manual here: render one `RadioButton` per option and\n * manage the selected option in the parent, same as any other controlled\n * list of options.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/radio-buttons.html\n */\nexport const RadioButton = forwardRef<View, RadioButtonProps>(function RadioButton(\n { value, onSelect, disabled, style, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const colorScheme = useResolvedColorScheme();\n const contrast = useResolvedContrast();\n const progress = useRef(new Animated.Value(value ? 1 : 0)).current;\n const isInitialMount = useRef(true);\n\n useEffect(() => {\n // The initial `Animated.Value` above already matches `value` — skip\n // animating into a state the radio button is already rendering, so it\n // never plays a transition before the user has touched it.\n if (isInitialMount.current) {\n isInitialMount.current = false;\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: value ? 1 : 0,\n duration: theme.durationFast,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: false,\n }).start();\n }, [value, progress, theme.durationFast, theme.easingDefault]);\n\n const idleBorderColor =\n contrast === 'more' ? theme.windowFgColor : colorScheme === 'dark' ? theme.dark1 : theme.light4;\n\n const borderColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [idleBorderColor, theme.accentBgColor],\n });\n const backgroundColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.cardBgColor, theme.accentBgColor],\n });\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"radio\"\n accessibilityState={{ checked: value, disabled: !!disabled }}\n onPress={() => {\n if (!value) {\n onSelect?.();\n }\n }}\n style={[{ opacity: disabled ? theme.opacityDisabled : 1 }, style]}\n {...pressableProps}\n >\n <Animated.View\n style={{\n width: SIZE,\n height: SIZE,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusPill,\n borderWidth: contrast === 'more' ? 2 : 1.5,\n borderColor,\n backgroundColor,\n }}\n >\n <Animated.View\n style={{\n width: DOT_SIZE,\n height: DOT_SIZE,\n borderRadius: theme.radiusPill,\n backgroundColor: '#fff',\n opacity: progress,\n }}\n />\n </Animated.View>\n </Pressable>\n );\n});\n","import { useCallback, useMemo, useRef, useState } from 'react';\nimport type {\n AccessibilityActionEvent,\n GestureResponderEvent,\n LayoutChangeEvent,\n StyleProp,\n ViewProps,\n ViewStyle,\n} from 'react-native';\nimport { PanResponder, Text, View } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nconst TRACK_HEIGHT = 6;\nconst THUMB_SIZE = 22;\nconst TICK_SIZE = 4;\n\nexport interface RangeSliderMark {\n value: number;\n label?: string;\n}\n\nexport interface RangeSliderProps extends Omit<ViewProps, 'style'> {\n /** Current `[lower, upper]` values. Both must be between `min` and `max`. */\n value: [number, number];\n /** Called when either thumb moves. */\n onChange: (value: [number, number]) => void;\n /** Minimum value. Defaults to `0`. */\n min?: number;\n /** Maximum value. Defaults to `100`. */\n max?: number;\n /**\n * Granularity of each step.\n * - The `adjustable` accessibility action (VoiceOver/TalkBack swipe up/down) moves by one step.\n * Defaults to `1`.\n */\n step?: number;\n /**\n * Minimum allowed gap between the lower and upper thumb, in value units.\n * Prevents the thumbs from crossing or overlapping. Defaults to `0`.\n */\n minDistance?: number;\n /** Disables the control. */\n disabled?: boolean;\n /**\n * Marks to display along the track.\n * Each mark can have an optional label rendered below the track.\n */\n marks?: RangeSliderMark[];\n /** Accessible label for the lower-bound thumb. Defaults to `\"Minimum value\"`. */\n minLabel?: string;\n /** Accessible label for the upper-bound thumb. Defaults to `\"Maximum value\"`. */\n maxLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\nfunction clamp(v: number, min: number, max: number) {\n return Math.min(max, Math.max(min, v));\n}\n\nfunction snapToStep(v: number, min: number, step: number, decimals: number) {\n return parseFloat((Math.round((v - min) / step) * step + min).toFixed(decimals));\n}\n\nfunction countDecimals(n: number) {\n const s = n.toString();\n const dot = s.indexOf('.');\n\n return dot === -1 ? 0 : s.length - dot - 1;\n}\n\n/**\n * Dual-thumb slider for selecting a min/max range, following the Adwaita\n * `GtkScale` pattern used by `Slider` — mirrors `@gnome-ui/react`'s\n * `RangeSlider`. Distinct from `Slider` (single value) — use this for\n * range filters (price, date range, etc.) where both bounds are\n * adjustable.\n *\n * Reuses `Slider`'s exact `PanResponder`/`locationX` pixel-positioning\n * technique (no `%`-of-self transform, since RN's `transform` only takes\n * pixel offsets) rather than two separate per-thumb responders: a single\n * `PanResponder` spans the whole track and, on `onPanResponderGrant`,\n * picks whichever thumb is closer to the touch point — the same\n * nearest-thumb logic the web version's `handleTrackPointerDown` uses —\n * then that same thumb keeps following the touch for the rest of the\n * gesture (`onPanResponderMove`), exactly matching the web behavior of\n * \"the thumb chosen at touch-down stays locked to the gesture even if the\n * pointer drifts closer to the other thumb mid-drag.\" This single-\n * responder shape also stands in for the web version's separate \"drag the\n * track itself\" affordance — touching anywhere on the track immediately\n * jumps the nearest thumb there, so no separate interaction is needed.\n *\n * The web's keyboard (← / → / Page Up/Down / Home/End) has no RN\n * equivalent — same \"no keyboard to drive it\" reasoning `Slider` already\n * established — replaced by two independent `accessibilityRole=\"adjustable\"`\n * elements (one per thumb, each with its own `accessibilityValue`/\n * `onAccessibilityAction`), the native VoiceOver/TalkBack increment-\n * decrement analog. Each thumb's own visual `View` stays\n * `pointerEvents=\"none\"` (dragging is the track responder's job, not a\n * per-thumb touch target) — screen readers still reach it independently\n * through the accessibility tree, which is separate from RN's touch hit-\n * testing.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/sliders.html\n */\nexport const RangeSlider = ({\n value,\n onChange,\n min = 0,\n max = 100,\n step = 1,\n minDistance = 0,\n disabled = false,\n marks,\n minLabel = 'Minimum value',\n maxLabel = 'Maximum value',\n style,\n onLayout,\n testID,\n ...viewProps\n}: RangeSliderProps) => {\n const theme = useGnomeTheme();\n const [trackWidth, setTrackWidth] = useState(0);\n const [labelWidths, setLabelWidths] = useState<Record<number, number>>({});\n const dragIndexRef = useRef<0 | 1 | null>(null);\n\n const dp = countDecimals(step);\n const [lo, hi] = value;\n const clampedLo = clamp(lo, min, max);\n const clampedHi = clamp(hi, min, max);\n const loPct = ((clampedLo - min) / (max - min)) * 100;\n const hiPct = ((clampedHi - min) / (max - min)) * 100;\n\n const handleTrackLayout = useCallback(\n (event: LayoutChangeEvent) => {\n setTrackWidth(event.nativeEvent.layout.width);\n onLayout?.(event);\n },\n [onLayout],\n );\n\n const setValueAt = useCallback(\n (index: 0 | 1, raw: number) => {\n const snapped = snapToStep(raw, min, step, dp);\n\n if (index === 0) {\n onChange([clamp(snapped, min, hi - minDistance), hi]);\n } else {\n onChange([lo, clamp(snapped, lo + minDistance, max)]);\n }\n },\n [min, max, step, dp, minDistance, lo, hi, onChange],\n );\n\n const valueFromLocationX = useCallback(\n (locationX: number) => (locationX / trackWidth) * (max - min) + min,\n [trackWidth, min, max],\n );\n\n const nearestThumbIndex = useCallback(\n (locationX: number): 0 | 1 => {\n const raw = valueFromLocationX(locationX);\n\n return Math.abs(raw - lo) <= Math.abs(raw - hi) ? 0 : 1;\n },\n [valueFromLocationX, lo, hi],\n );\n\n const panResponder = useMemo(\n () =>\n PanResponder.create({\n onStartShouldSetPanResponder: () => !disabled,\n onMoveShouldSetPanResponder: () => !disabled,\n onPanResponderGrant: (event: GestureResponderEvent) => {\n if (trackWidth <= 0) {\n return;\n }\n\n const { locationX } = event.nativeEvent;\n const index = nearestThumbIndex(locationX);\n\n dragIndexRef.current = index;\n setValueAt(index, valueFromLocationX(locationX));\n },\n onPanResponderMove: (event: GestureResponderEvent) => {\n if (dragIndexRef.current === null || trackWidth <= 0) {\n return;\n }\n\n setValueAt(dragIndexRef.current, valueFromLocationX(event.nativeEvent.locationX));\n },\n onPanResponderRelease: () => {\n dragIndexRef.current = null;\n },\n onPanResponderTerminate: () => {\n dragIndexRef.current = null;\n },\n }),\n [disabled, trackWidth, nearestThumbIndex, setValueAt, valueFromLocationX],\n );\n\n const makeAccessibilityAction = useCallback(\n (index: 0 | 1) => (event: AccessibilityActionEvent) => {\n if (disabled) {\n return;\n }\n\n const current = index === 0 ? lo : hi;\n\n switch (event.nativeEvent.actionName) {\n case 'increment':\n setValueAt(index, current + step);\n break;\n case 'decrement':\n setValueAt(index, current - step);\n break;\n }\n },\n [disabled, lo, hi, step, setValueAt],\n );\n\n const hasMarks = marks && marks.length > 0;\n const hasLabels = hasMarks && marks.some((m) => m.label);\n\n const loThumbLeft = (loPct / 100) * trackWidth - THUMB_SIZE / 2;\n const hiThumbLeft = (hiPct / 100) * trackWidth - THUMB_SIZE / 2;\n\n return (\n <View\n testID={testID}\n style={[{ width: '100%', paddingTop: 10, paddingBottom: hasLabels ? 0 : 10 }, style]}\n {...viewProps}\n >\n <View\n testID={testID ? `${testID}-track` : undefined}\n onLayout={handleTrackLayout}\n style={{\n justifyContent: 'center',\n height: THUMB_SIZE,\n opacity: disabled ? theme.opacityDisabled : 1,\n }}\n {...panResponder.panHandlers}\n >\n <View\n pointerEvents=\"none\"\n style={{\n width: '100%',\n height: TRACK_HEIGHT,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.cardShadeColor,\n overflow: 'hidden',\n }}\n >\n <View\n style={{\n position: 'absolute',\n height: '100%',\n left: `${loPct}%`,\n width: `${hiPct - loPct}%`,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.accentBgColor,\n }}\n />\n </View>\n\n <View\n pointerEvents=\"none\"\n accessible\n accessibilityRole=\"adjustable\"\n accessibilityLabel={minLabel}\n accessibilityValue={{ min, max: hi, now: clampedLo }}\n accessibilityState={{ disabled }}\n accessibilityActions={[\n { name: 'increment', label: 'increment' },\n { name: 'decrement', label: 'decrement' },\n ]}\n onAccessibilityAction={makeAccessibilityAction(0)}\n style={{\n position: 'absolute',\n left: loThumbLeft,\n width: THUMB_SIZE,\n height: THUMB_SIZE,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.accentBgColor,\n borderWidth: 2,\n borderColor: theme.windowBgColor,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 1 },\n shadowOpacity: 0.25,\n shadowRadius: 3,\n elevation: 3,\n }}\n />\n\n <View\n pointerEvents=\"none\"\n accessible\n accessibilityRole=\"adjustable\"\n accessibilityLabel={maxLabel}\n accessibilityValue={{ min: lo, max, now: clampedHi }}\n accessibilityState={{ disabled }}\n accessibilityActions={[\n { name: 'increment', label: 'increment' },\n { name: 'decrement', label: 'decrement' },\n ]}\n onAccessibilityAction={makeAccessibilityAction(1)}\n style={{\n position: 'absolute',\n left: hiThumbLeft,\n width: THUMB_SIZE,\n height: THUMB_SIZE,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.accentBgColor,\n borderWidth: 2,\n borderColor: theme.windowBgColor,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 1 },\n shadowOpacity: 0.25,\n shadowRadius: 3,\n elevation: 3,\n }}\n />\n\n {hasMarks &&\n marks.map((m) => {\n const mPct = ((clamp(m.value, min, max) - min) / (max - min)) * 100;\n const tickLeft = (mPct / 100) * trackWidth - TICK_SIZE / 2;\n\n return (\n <View\n key={m.value}\n pointerEvents=\"none\"\n importantForAccessibility=\"no\"\n style={{\n position: 'absolute',\n left: tickLeft,\n width: TICK_SIZE,\n height: TICK_SIZE,\n borderRadius: TICK_SIZE / 2,\n backgroundColor: theme.windowBgColor,\n }}\n />\n );\n })}\n </View>\n\n {hasLabels && (\n <View\n importantForAccessibility=\"no\"\n style={{ position: 'relative', width: '100%', height: 20, marginTop: 6 }}\n >\n {marks.map((m) => {\n const mPct = ((clamp(m.value, min, max) - min) / (max - min)) * 100;\n const centerX = (mPct / 100) * trackWidth;\n const labelWidth = labelWidths[m.value] ?? 0;\n\n return (\n <Text\n key={m.value}\n numberOfLines={1}\n onLayout={(event) => {\n const {\n layout: { width },\n } = event.nativeEvent;\n\n setLabelWidths((prev) =>\n prev[m.value] === width ? prev : { ...prev, [m.value]: width },\n );\n }}\n style={{\n position: 'absolute',\n left: centerX - labelWidth / 2,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeCaption,\n color: theme.windowFgColor,\n opacity: theme.opacityDim,\n }}\n >\n {m.label}\n </Text>\n );\n })}\n </View>\n )}\n </View>\n );\n};\n","import { Star, StarOutline } from '@gnome-ui/icons';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Pressable, View } from 'react-native';\n\nimport { Icon, type IconSize } from '@/components/Icon';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface RatingStarsProps {\n /** Current rating, between `0` and `max`. */\n value: number;\n /** Number of stars. Defaults to `5`. */\n max?: number;\n /**\n * Called when the user picks a rating. Omit to render a read-only\n * display (e.g. an average rating) instead of an interactive input.\n */\n onChange?: (value: number) => void;\n /** Star size. Defaults to `\"md\"`. */\n size?: IconSize;\n /** Renders as read-only even when `onChange` is provided. */\n disabled?: boolean;\n /** Accessible label. Defaults to `\"Rating\"` (interactive) or a generated `\"N out of M stars\"` (read-only). */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Star rating display and input. Mirrors `@gnome-ui/react`'s `RatingStars`.\n *\n * Renders `role=\"radiogroup\"` of `role=\"radio\"` stars when `onChange` is\n * provided, or a static `role=\"img\"` when it isn't — e.g. for showing an\n * average/read-only rating.\n *\n * The web version's `role=\"radiogroup\"` layer also owns an `onKeyDown`\n * handler for arrow-key roving-tabindex navigation and a mouse-hover\n * preview that doesn't commit until clicked; neither has a touch\n * counterpart, so both drop — same standing convention `ToggleGroup`\n * already established for this package (its own doc comment covers the\n * reasoning). What's left, tapping a star to commit that rating, is a\n * strict subset of the web interaction, not an approximation of it.\n *\n * Each star's fill color is `tintColor={theme.warningBgColor}` rather than\n * `Icon`'s own fixed `color=\"yellow\"` (→ `theme.yellow4`, a different hex):\n * the source CSS reads `var(--gnome-warning-bg-color, #f6d32d)` directly\n * with no `color-mix()` darkening step, so the semantic warning token\n * itself — already tracking dark mode and every contrast level — is the\n * exact match, not an approximation through the fixed palette. Stars carry\n * no `label` on `Icon` (decorative — `iconAccessibilityProps` already hides\n * an unlabeled icon from the tree); the accessible name lives on the\n * container (read-only) or each `Pressable` (interactive) instead, mirroring\n * `ToggleGroupItem`'s icon-only items.\n */\nexport const RatingStars = ({\n value,\n max = 5,\n onChange,\n size = 'md',\n disabled = false,\n accessibilityLabel,\n style,\n testID,\n}: RatingStarsProps) => {\n const theme = useGnomeTheme();\n const interactive = Boolean(onChange) && !disabled;\n const clamped = Math.min(max, Math.max(0, value));\n const stars = Array.from({ length: max }, (_, i) => i + 1);\n\n if (!interactive) {\n return (\n <View\n testID={testID}\n accessible\n role=\"img\"\n accessibilityLabel={accessibilityLabel ?? `${clamped} out of ${max} stars`}\n style={[{ flexDirection: 'row', alignItems: 'center', gap: 2 }, style]}\n >\n {stars.map((star) => (\n <Icon\n key={star}\n icon={star <= clamped ? Star : StarOutline}\n size={size}\n tintColor={theme.warningBgColor}\n />\n ))}\n </View>\n );\n }\n\n return (\n <View\n testID={testID}\n role=\"radiogroup\"\n accessibilityLabel={accessibilityLabel ?? 'Rating'}\n style={[{ flexDirection: 'row', alignItems: 'center', gap: 2 }, style]}\n >\n {stars.map((star) => (\n <Pressable\n key={star}\n role=\"radio\"\n accessibilityState={{ checked: star === clamped }}\n accessibilityLabel={`${star} ${star === 1 ? 'star' : 'stars'}`}\n onPress={() => onChange?.(star)}\n style={{ padding: 2, borderRadius: theme.radiusSm }}\n >\n <Icon\n icon={star <= clamped ? Star : StarOutline}\n size={size}\n tintColor={theme.warningBgColor}\n />\n </Pressable>\n ))}\n </View>\n );\n};\n","import { GoUp } from '@gnome-ui/icons';\nimport { forwardRef } from 'react';\nimport type { StyleProp, View, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { IconButton } from '@/components/IconButton';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\n/**\n * Controls when the button is rendered.\n *\n * - `\"auto\"` — hidden until `scrollY` exceeds `threshold` (default).\n * - `\"always\"` — always rendered regardless of scroll position.\n */\nexport type ScrollToTopVisible = 'always' | 'auto';\n\n/**\n * Anchor corner or edge for the absolutely-positioned container.\n * The button is inset `theme.space4` (24 dp) from each named edge.\n */\nexport type ScrollToTopPosition =\n | 'bottom-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'top-right'\n | 'top-left'\n | 'top-center';\n\nexport interface ScrollToTopProps {\n /**\n * Controls when the button is visible.\n *\n * - `\"auto\"` (default) — shown only once `scrollY` exceeds `threshold`.\n * - `\"always\"` — permanently visible.\n */\n visible?: ScrollToTopVisible;\n /**\n * Corner or edge where the button is anchored. Default: `\"bottom-right\"`.\n */\n position?: ScrollToTopPosition;\n /**\n * Offset the user must scroll past before the button appears.\n * Only relevant when `visible=\"auto\"`. Default: `300`.\n */\n threshold?: number;\n /**\n * The observed `ScrollView`/`FlatList`'s current vertical scroll offset —\n * pass `nativeEvent.contentOffset.y` from its `onScroll` handler. Only\n * relevant when `visible=\"auto\"`; ignored (and safe to omit) otherwise.\n * Default: `0`.\n */\n scrollY?: number;\n /**\n * Called when the button is pressed. There's no RN equivalent of the web\n * version's own `scrollTarget.scrollTo({ top: 0, behavior: 'smooth' })` —\n * `ScrollView`/`FlatList` are scrolled through a ref's imperative\n * `scrollTo`/`scrollToOffset`, which this component has no way to hold on\n * a consumer's behalf. Typically\n * `() => scrollViewRef.current?.scrollTo({ y: 0, animated: true })`.\n */\n onPress: () => void;\n /**\n * Extra inset added on top of the base edge spacing when anchored to a\n * `\"top-*\"` position — pass your own `useSafeAreaInsets().top` so the\n * button clears a notch/status bar. Same \"no new peer dependency\" call\n * `BottomTabBar`'s `bottomInset` already made. Default: `0`.\n */\n topInset?: number;\n /** Same as `topInset`, for a `\"bottom-*\"` position. Default: `0`. */\n bottomInset?: number;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nfunction getPositionStyle(\n position: ScrollToTopPosition,\n space4: number,\n topInset: number,\n bottomInset: number,\n): ViewStyle {\n switch (position) {\n case 'bottom-right':\n return { bottom: space4 + bottomInset, right: space4 };\n case 'bottom-left':\n return { bottom: space4 + bottomInset, left: space4 };\n case 'bottom-center':\n return { bottom: space4 + bottomInset, left: 0, right: 0, alignItems: 'center' };\n case 'top-right':\n return { top: space4 + topInset, right: space4 };\n case 'top-left':\n return { top: space4 + topInset, left: space4 };\n case 'top-center':\n return { top: space4 + topInset, left: 0, right: 0, alignItems: 'center' };\n }\n}\n\n/**\n * Absolutely-positioned OSD button that scrolls a `ScrollView`/`FlatList`\n * back to the top — mirrors `@gnome-ui/react`'s `ScrollToTop`, reimagined\n * rather than ported 1:1 per this package's own ROADMAP note: RN has no\n * page-level scroll event to observe internally the way the web version's\n * `useScrollToTopVisibility` attaches a `window`/element `scroll` listener,\n * so that hook doesn't port at all — `visible=\"auto\"` is instead a pure\n * function of a `scrollY` prop the consumer feeds from their own\n * `ScrollView`'s `onScroll`, re-evaluated on every render with no internal\n * state or listener needed.\n *\n * Same \"no `document.body`/portal target\" gap `Toaster` already\n * documents — mount this yourself as the last child of the `View` wrapping\n * your scrollable content (left at its default relative positioning) so it\n * paints on top; `pointerEvents=\"box-none\"` (the same technique `Toaster`\n * uses) keeps the empty space around the button from intercepting touches\n * meant for the content underneath.\n *\n * The web version's resting `opacity: 0.5`-until-hover/focus is dropped —\n * the same call `PasswordEntryRow`'s reveal button already made: that\n * effect exists purely so the control can brighten on hover, and touch has\n * no hover, so a permanently dimmed control would just be harder to see.\n *\n * Forwards `ref` to the root positioning `View`, not the inner button —\n * matching the web version's own `forwardRef` target.\n *\n * @example\n * // Minimal — appears once `scrollY` exceeds 300, anchored bottom-right\n * const [scrollY, setScrollY] = useState(0);\n * const scrollRef = useRef<ScrollView>(null);\n * <View style={{ flex: 1 }}>\n * <ScrollView\n * ref={scrollRef}\n * onScroll={(e) => setScrollY(e.nativeEvent.contentOffset.y)}\n * scrollEventThrottle={16}\n * >\n * ...\n * </ScrollView>\n * <ScrollToTop\n * scrollY={scrollY}\n * onPress={() => scrollRef.current?.scrollTo({ y: 0, animated: true })}\n * />\n * </View>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ScrollToTop.html\n */\nexport const ScrollToTop = forwardRef<View, ScrollToTopProps>(function ScrollToTop(\n {\n visible = 'auto',\n position = 'bottom-right',\n threshold = 300,\n scrollY = 0,\n onPress,\n topInset = 0,\n bottomInset = 0,\n style,\n testID,\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const isVisible = visible === 'always' || scrollY > threshold;\n\n if (!isVisible) {\n return null;\n }\n\n return (\n <RNView\n ref={ref}\n testID={testID}\n pointerEvents=\"box-none\"\n style={[\n { position: 'absolute', zIndex: 9999 },\n getPositionStyle(position, theme.space4, topInset, bottomInset),\n style,\n ]}\n >\n <IconButton icon={GoUp} label=\"Scroll to top\" variant=\"osd\" onPress={onPress} />\n </RNView>\n );\n});\n","import { forwardRef, type ReactNode, useEffect, useImperativeHandle, useRef } from 'react';\nimport type { StyleProp, TextInput, TextInputProps, TextStyle, ViewStyle } from 'react-native';\nimport { Pressable, Text as RNText, TextInput as RNTextInput, View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface SearchBarProps extends Omit<TextInputProps, 'style'> {\n /** Whether the search bar is rendered. `false` renders nothing. */\n open: boolean;\n /**\n * Renders a trailing \"Cancel\" button (the RN-idiomatic stand-in for the\n * web version's Escape-to-close — touch keyboards have no reliable\n * Escape key) and calls this when it's pressed.\n */\n onClose?: () => void;\n /** Called when the clear (×) button is pressed. Also clears the input value. */\n onClear?: () => void;\n /**\n * Content rendered below the bar (e.g. filter chips). Only rendered\n * while `open` is true.\n */\n children?: ReactNode;\n /** Removes the header-bar background so the bar blends into any surface. */\n inline?: boolean;\n /** Accessible label for the close button. Defaults to `\"Cancel\"`. */\n closeLabel?: string;\n /** Style applied to the wrapping `View`. */\n style?: StyleProp<ViewStyle>;\n /** Style applied to the `TextInput` itself. */\n inputStyle?: StyleProp<TextStyle>;\n}\n\n/**\n * Multiplies an `rgba(r, g, b, a)` token's alpha channel. Duplicated from\n * `TextField` rather than shared — see that component for the full\n * rationale; it's small and self-contained enough that a shared module\n * isn't worth it for two call sites.\n */\nfunction dimColor(color: string, factor: number): string {\n const match = color.match(/^rgba?\\(([^,]+),([^,]+),([^,]+),?([^)]+)?\\)$/);\n\n if (!match) {\n return color;\n }\n\n const [, r, g, b, a] = match;\n const alpha = (a ? Number.parseFloat(a) : 1) * factor;\n\n return `rgba(${r.trim()}, ${g.trim()}, ${b.trim()}, ${alpha})`;\n}\n\n/**\n * Collapsible search input following the Adwaita `AdwSearchBar` pattern.\n *\n * Rebuilt on RN's `TextInput` rather than ported from `@gnome-ui/react`'s\n * `<input type=\"search\">`: `open` no longer drives a CSS height/opacity\n * transition (no established animated-height pattern exists yet in this\n * package — same trade-off `SidebarSection` made for its collapsible body)\n * — `open={false}` renders nothing at all, and mounting on `open={true}`\n * auto-focuses the input, standing in for the web version's\n * `requestAnimationFrame`-on-open focus effect.\n *\n * Dropped relative to `@gnome-ui/react`'s `SearchBar`: the `suggestions` /\n * `onSuggestionSelect` / `loadingSuggestions` / `renderSuggestion` /\n * `suggestionsLabel` autocomplete popover — it depends on a portal +\n * viewport-anchored positioning primitive (`createPortal` +\n * `getBoundingClientRect`) this package doesn't have yet, the same gap\n * that dropped `SidebarItem`'s `menuItems` context menu — and a `Spinner`\n * component, not yet ported. The search/clear icons are Unicode glyphs\n * (`🔍`/`×`) rather than `@gnome-ui/icons`, matching every other\n * no-SVG-dependency component in this package.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.SearchBar.html\n */\nexport const SearchBar = forwardRef<TextInput, SearchBarProps>(function SearchBar(\n {\n open,\n onClose,\n onClear,\n children,\n value,\n placeholder = 'Search…',\n editable = true,\n inline = false,\n closeLabel = 'Cancel',\n style,\n inputStyle,\n ...inputProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const inputRef = useRef<TextInput>(null);\n\n useImperativeHandle(ref, () => inputRef.current as TextInput, []);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n const id = requestAnimationFrame(() => inputRef.current?.focus());\n\n return () => cancelAnimationFrame(id);\n }, [open]);\n\n if (!open) {\n return null;\n }\n\n const disabled = !editable;\n const hasValue = typeof value === 'string' && value.length > 0;\n\n const barBg = inline ? 'transparent' : theme.headerbarBgColor;\n const barBorder = inline ? 'transparent' : theme.headerbarShadeColor;\n\n return (\n <RNView style={style}>\n <RNView style={{ flexDirection: 'row', alignItems: 'center', gap: theme.space1 }}>\n <RNView\n style={{\n flex: 1,\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space1,\n paddingVertical: theme.space1,\n paddingHorizontal: theme.space2,\n backgroundColor: barBg,\n borderBottomWidth: 1,\n borderBottomColor: barBorder,\n opacity: disabled ? theme.opacityDisabled : 1,\n }}\n >\n <RNText\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{ opacity: theme.opacityDim, fontSize: theme.fontSizeBody }}\n >\n {'🔍'}\n </RNText>\n\n <RNTextInput\n ref={inputRef}\n editable={editable}\n value={value}\n placeholder={placeholder}\n placeholderTextColor={dimColor(theme.windowFgColor, theme.opacityDim)}\n style={[\n {\n flex: 1,\n padding: 0,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n lineHeight: Math.round(theme.fontSizeBody * theme.lineHeightBody),\n color: theme.windowFgColor,\n } satisfies TextStyle,\n inputStyle,\n ]}\n {...inputProps}\n />\n\n {hasValue && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel=\"Clear search\"\n disabled={disabled}\n hitSlop={8}\n onPress={() => {\n onClear?.();\n inputRef.current?.focus();\n }}\n style={({ pressed }) => ({\n width: 24,\n height: 24,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusSm,\n opacity: disabled ? theme.opacityDisabled : 1,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n })}\n >\n <RNText style={{ fontSize: theme.fontSizeBody, color: theme.windowFgColor }}>\n {'×'}\n </RNText>\n </Pressable>\n )}\n </RNView>\n\n {onClose && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={closeLabel}\n onPress={onClose}\n hitSlop={8}\n >\n <RNText\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n color: theme.accentColor,\n }}\n >\n {closeLabel}\n </RNText>\n </Pressable>\n )}\n </RNView>\n\n {children && (\n <RNView\n style={{\n flexDirection: 'row',\n flexWrap: 'wrap',\n gap: theme.space1,\n padding: theme.space1,\n paddingHorizontal: theme.space2,\n backgroundColor: barBg,\n borderBottomWidth: 1,\n borderBottomColor: barBorder,\n }}\n >\n {children}\n </RNView>\n )}\n </RNView>\n );\n});\n","import { useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Pressable, View } from 'react-native';\n\nimport { Tooltip } from '@/components/Tooltip';\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport interface SegmentedBarSegment {\n /** Category name shown in the tooltip. */\n label: string;\n /**\n * Percentage value 0–100.\n * The sum of all segments should equal 100.\n * If it does not, values are redistributed proportionally.\n */\n value: number;\n /**\n * Color for this segment.\n * When omitted, a cycling palette of GNOME design tokens is used.\n */\n color?: string;\n}\n\nexport interface SegmentedBarProps {\n /** Segments to display. Each segment contributes its share of the full bar width. */\n values: SegmentedBarSegment[];\n /**\n * Accessible label for the bar as a whole.\n * Auto-generated from `values` when omitted (e.g. \"TypeScript 60%, JavaScript 30%\").\n */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst COLOR_COUNT = 8;\n\nfunction paletteColor(theme: GnomeThemeTokens, index: number): string {\n switch (index % COLOR_COUNT) {\n case 0:\n return theme.blue3;\n case 1:\n return theme.green3;\n case 2:\n return theme.purple3;\n case 3:\n return theme.orange3;\n case 4:\n return theme.red3;\n case 5:\n return theme.brown3;\n case 6:\n return theme.blue5;\n default:\n return theme.green5;\n }\n}\n\n/**\n * Horizontal bar split into proportional segments, one per category.\n * Mirrors `@gnome-ui/react`'s `SegmentedBar`. Typical use case: repository\n * language distribution.\n *\n * ```tsx\n * <SegmentedBar\n * values={[\n * { label: 'TypeScript', value: 60, color: '#3178c6' },\n * { label: 'JavaScript', value: 30, color: '#f7df1e' },\n * { label: 'CSS', value: 10, color: '#563d7c' },\n * ]}\n * />\n * ```\n *\n * The web version's hover interaction (dim every segment but the one under\n * the pointer, brighten that one via `filter: brightness()`) is rebuilt for\n * touch rather than dropped: each segment is a `Pressable`, and touching\n * one dims the rest immediately via `onPressIn`/`onPressOut` — deliberately\n * not gated behind `Tooltip`'s own long-press delay, since this feedback is\n * the RN analog of a `Pressable`'s own instant `pressed` state, not the\n * \"peek\" affordance a tooltip reveal is. Each segment is also wrapped in\n * `Tooltip` (`label`/`placement=\"top\"`/`delay={200}`, ported 1:1 from the\n * web version's own tooltip) for the actual label/percentage readout,\n * composing cleanly with the dim/highlight `onPressIn`/`onPressOut` since\n * `Tooltip` clones its own handlers onto the child *and* still calls the\n * child's original ones. `filter: brightness(1.15)` on the actively-touched\n * segment has no RN equivalent (no `filter` support) — dropped as a\n * decorative nicety, since the touched segment already reads as\n * highlighted by contrast once every other segment dims to 35% opacity.\n *\n * `role=\"img\"` + `accessibilityLabel` ports 1:1 from RN's newer web-aligned\n * `Role` union (the same `Avatar`/`LevelBar` precedent).\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/progress.html\n */\nexport const SegmentedBar = ({ values, accessibilityLabel, style, testID }: SegmentedBarProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n\n const total = values.reduce((sum, s) => sum + s.value, 0) || 100;\n const defaultLabel = values.map((s) => `${s.label} ${s.value}%`).join(', ');\n const trackColor = scheme === 'dark' ? theme.dark2 : theme.light3;\n\n return (\n <View\n testID={testID}\n accessible\n role=\"img\"\n accessibilityLabel={accessibilityLabel ?? defaultLabel}\n style={[\n {\n flexDirection: 'row',\n width: '100%',\n height: 8,\n borderRadius: theme.radiusPill,\n overflow: 'hidden',\n gap: 2,\n backgroundColor: trackColor,\n },\n style,\n ]}\n >\n {values.map((segment, i) => {\n const width = `${(segment.value / total) * 100}%` as const;\n const pct = segment.value % 1 === 0 ? segment.value : segment.value.toFixed(1);\n const tooltipLabel = `${segment.label}: ${pct}%`;\n\n return (\n <Tooltip key={segment.label} label={tooltipLabel} placement=\"top\" delay={200}>\n <Pressable\n testID=\"segmented-bar-segment\"\n onPressIn={() => setActiveIndex(i)}\n onPressOut={() => setActiveIndex(null)}\n style={{\n width,\n height: '100%',\n backgroundColor: segment.color ?? paletteColor(theme, i),\n opacity: activeIndex !== null && activeIndex !== i ? 0.35 : 1,\n }}\n />\n </Tooltip>\n );\n })}\n </View>\n );\n};\n","import { Children, isValidElement, type ReactNode } from 'react';\n\n/** Recursively count items whose `label` prop matches `filter` (case-insensitive). */\nexport function countMatchingItems(children: ReactNode, filter: string): number {\n let count = 0;\n\n Children.forEach(children, (child) => {\n if (!isValidElement(child)) {\n return;\n }\n\n const props = child.props as Record<string, unknown>;\n\n if (typeof props.label === 'string') {\n if (props.label.toLowerCase().includes(filter.toLowerCase())) {\n count++;\n }\n } else if (props.children) {\n count += countMatchingItems(props.children as ReactNode, filter);\n }\n });\n\n return count;\n}\n\n/**\n * Whether a single top-level `Sidebar` child would render something under\n * `filter` — a bare `SidebarItem` if its own `label` matches, a\n * `SidebarSection` (or anything else with `children`) if any descendant\n * matches. Used to decide which children get a `Separator` between them, so\n * a filtered-out (null-rendering) item doesn't still claim a divider slot.\n */\nexport function isChildVisible(child: ReactNode, filter: string): boolean {\n if (!filter) {\n return true;\n }\n\n if (!isValidElement(child)) {\n return true;\n }\n\n const props = child.props as Record<string, unknown>;\n\n if (typeof props.label === 'string') {\n return props.label.toLowerCase().includes(filter.toLowerCase());\n }\n\n if (props.children) {\n return countMatchingItems(props.children as ReactNode, filter) > 0;\n }\n\n return true;\n}\n","import { Children, createContext, Fragment, forwardRef, type ReactNode, useContext } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView, ScrollView } from 'react-native';\n\nimport { Separator } from '@/components/Separator';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nimport { isChildVisible } from './filterUtils';\n\n// ─── Contexts ───────────────────────────────────────────────────────────────\n\n/** Provides the collapsed state to all descendant `SidebarSection`/`SidebarItem`. */\n// eslint-disable-next-line react-refresh/only-export-components\nexport const SidebarCollapsedContext = createContext(false);\n\n/** Returns `true` when the nearest `Sidebar` is in collapsed (icon-only/rail) mode. */\n// eslint-disable-next-line react-refresh/only-export-components\nexport function useSidebarCollapsed() {\n return useContext(SidebarCollapsedContext);\n}\n\n/** Provides the active filter string to all descendant `SidebarItem`/`SidebarSection`. */\n// eslint-disable-next-line react-refresh/only-export-components\nexport const SidebarFilterContext = createContext('');\n\n// ─── Props ──────────────────────────────────────────────────────────────────\n\nexport interface SidebarProps extends Omit<ViewProps, 'style'> {\n children?: ReactNode;\n /**\n * When `true`, collapses the sidebar to icon-only (rail) mode. Labels and\n * section titles are hidden.\n */\n collapsed?: boolean;\n /**\n * Controlled filter string. `SidebarItem`s whose `label` does not match\n * (case-insensitive substring) are hidden, and empty `SidebarSection`s are\n * hidden entirely. Unlike the web version this never renders a search\n * input itself — pair it with your own `TextField` (or the future\n * `SearchBar` port) and drive this prop from its value.\n */\n filter?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Lateral navigation panel following the Adwaita `.navigation-sidebar` style.\n *\n * Compose with `SidebarSection` (named groups) and `SidebarItem` (rows).\n * Consecutive top-level children get a `Separator` inserted between them —\n * same divider-on-index-boundary technique `BoxedList` uses for its rows —\n * standing in for the web version's `.section + .section` adjacent-sibling\n * CSS rule, which RN has no equivalent of.\n *\n * Dropped relative to `@gnome-ui/react`'s `Sidebar`: `searchable` (would\n * pull in a `SearchBar`, not yet ported to this package — use `filter` with\n * your own input instead), `mode`/auto page-layout switch (depends on the\n * web-only `useBreakpoint` hook), `variant` (tinted/blurred backgrounds —\n * the blurred variant needs a native blur view this package doesn't depend\n * on), and the `<nav>` landmark role (RN's `AccessibilityRole` union has no\n * \"navigation\" value).\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Sidebar.html\n */\nexport const Sidebar = forwardRef<View, SidebarProps>(function Sidebar(\n { children, collapsed = false, filter = '', style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const isFilterActive = filter.length > 0;\n const items = Children.toArray(children)\n .filter(Boolean)\n .filter((child) => isChildVisible(child, filter));\n\n const hasMatches = !isFilterActive || items.length > 0;\n\n return (\n <SidebarCollapsedContext.Provider value={collapsed}>\n <SidebarFilterContext.Provider value={filter}>\n <RNView\n ref={ref}\n style={[\n {\n flexShrink: 0,\n width: collapsed ? theme.layoutSidebarRailWidth : theme.layoutSidebarDefaultWidth,\n backgroundColor: theme.sidebarBgColor,\n borderRightWidth: 1,\n borderRightColor: theme.headerbarBorderColor,\n },\n style,\n ]}\n {...viewProps}\n >\n <ScrollView contentContainerStyle={{ padding: theme.space1 }}>\n {isFilterActive && !hasMatches ? (\n <Text\n variant=\"caption\"\n color=\"dim\"\n style={{ textAlign: 'center', padding: theme.space4 }}\n >\n No items match your search.\n </Text>\n ) : (\n items.map((child, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: children have no natural stable id\n <Fragment key={i}>\n {i > 0 && (\n <Separator\n testID=\"sidebar-separator\"\n style={{ marginVertical: theme.space1 }}\n />\n )}\n {child}\n </Fragment>\n ))\n )}\n </ScrollView>\n </RNView>\n </SidebarFilterContext.Provider>\n </SidebarCollapsedContext.Provider>\n );\n});\n","import { forwardRef, type ReactNode, useContext } from 'react';\nimport type { PressableProps, StyleProp, TextStyle, View, ViewStyle } from 'react-native';\nimport { Pressable, View as RNView } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nimport { SidebarFilterContext, useSidebarCollapsed } from './Sidebar';\n\nexport interface SidebarItemProps extends Omit<PressableProps, 'children' | 'style' | 'disabled'> {\n /** Primary label. */\n label: string;\n /**\n * Icon placed at the leading edge. Rendered as-is — size/color it\n * yourself. Web's `icon` takes an `@gnome-ui/icons` `IconDefinition`;\n * this package has no SVG icon system, so it's a plain `ReactNode` here,\n * same as `ActionRow`'s `leading` and `TabItem`'s `icon`.\n */\n icon?: ReactNode;\n /** Marks this item as the currently active view. */\n active?: boolean;\n /** Trailing widget — badge, count, button, icon… */\n suffix?: ReactNode;\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Individual navigation row inside a `Sidebar` or `SidebarSection`.\n *\n * Automatically hidden (renders nothing) when the nearest `Sidebar`'s\n * `filter` is active and `label` doesn't match. In collapsed (rail) mode\n * the label and suffix are hidden and the icon centers — `label` still\n * reaches screen readers via `accessibilityLabel`.\n *\n * Dropped relative to `@gnome-ui/react`'s `SidebarItem`: `tooltip` (no\n * `Tooltip` port yet, and RN's touch-first model has no hover to trigger\n * one from), `menuItems` (context menu — no portal/positioning primitive\n * exists in this package yet), and `onDrop`/`acceptTypes` (HTML5\n * drag-and-drop has no RN equivalent without a gesture-handler dependency\n * this package doesn't have).\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Sidebar.html\n */\nexport const SidebarItem = forwardRef<View, SidebarItemProps>(function SidebarItem(\n { label, icon, active = false, suffix, disabled, style, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const collapsed = useSidebarCollapsed();\n const filterValue = useContext(SidebarFilterContext);\n\n const isFiltered =\n filterValue.length > 0 && !label.toLowerCase().includes(filterValue.toLowerCase());\n\n if (isFiltered) {\n return null;\n }\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"button\"\n accessibilityState={{ selected: active, disabled: !!disabled }}\n accessibilityLabel={label}\n style={({ pressed }) => [\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: 8,\n paddingHorizontal: theme.space2,\n borderRadius: theme.radiusMd,\n opacity: disabled ? theme.opacityDisabled : 1,\n backgroundColor: active\n ? theme.accentBgColor\n : pressed\n ? theme.hoverOverlay\n : 'transparent',\n },\n collapsed && { justifyContent: 'center', paddingHorizontal: 0 },\n style,\n ]}\n {...pressableProps}\n >\n {icon && <RNView style={{ flexShrink: 0, opacity: active ? 1 : 0.85 }}>{icon}</RNView>}\n\n {!collapsed && (\n <Text\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n flex: 1,\n fontSize: theme.fontSizeBody,\n fontWeight: String(\n active ? theme.fontWeightSemibold : theme.fontWeightNormal,\n ) as TextStyle['fontWeight'],\n color: active ? theme.accentFgColor : theme.sidebarFgColor,\n }}\n >\n {label}\n </Text>\n )}\n\n {!collapsed && suffix && <RNView style={{ flexShrink: 0 }}>{suffix}</RNView>}\n </Pressable>\n );\n});\n","import { forwardRef, type ReactNode, useContext, useImperativeHandle, useState } from 'react';\nimport type { StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { Pressable, View as RNView } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\nimport { countMatchingItems } from './filterUtils';\nimport { SidebarFilterContext, useSidebarCollapsed } from './Sidebar';\n\nexport interface SidebarSectionHandle {\n expand: () => void;\n collapse: () => void;\n toggle: () => void;\n}\n\nexport interface SidebarSectionProps extends Omit<ViewProps, 'style'> {\n /** Section heading. Omit for an untitled section (e.g. the first group in a sidebar). */\n title?: string;\n /** Icon rendered left of the title. Rendered as-is — size/color it yourself. */\n icon?: ReactNode;\n /** Whether the section body can be toggled open/closed. Defaults to `false`. */\n collapsible?: boolean;\n /** Initial open state when `collapsible` is true. Defaults to `true`. */\n defaultOpen?: boolean;\n /** Controlled open state. */\n open?: boolean;\n /** Called when open state changes. */\n onOpenChange?: (open: boolean) => void;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Named group of `SidebarItem` entries inside a `Sidebar`.\n *\n * When `collapsible`, the body toggles via the header `Pressable` or\n * imperatively via a `ref` (`expand`/`collapse`/`toggle`) — same\n * `useImperativeHandle` shape as the web version's `SidebarSectionHandle`.\n * The body stays mounted and toggles `display: 'none'` rather than\n * unmounting, the same \"stays mounted but hidden\" approach `TabPanel` uses,\n * instead of porting the web version's animated CSS-grid collapse (no\n * established animated-height pattern exists yet in this package).\n *\n * In rail (`collapsed`) mode the header — and with it the collapse toggle —\n * is hidden and the body is always shown, matching the web version's rail\n * behavior. When a `Sidebar` filter is active and no descendant matches,\n * the whole section renders nothing.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Sidebar.html\n */\nexport const SidebarSection = forwardRef<SidebarSectionHandle, SidebarSectionProps>(\n function SidebarSection(\n {\n title,\n icon,\n collapsible = false,\n defaultOpen = true,\n open: controlledOpen,\n onOpenChange,\n children,\n style,\n ...viewProps\n },\n ref,\n ) {\n const theme = useGnomeTheme();\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const isControlled = controlledOpen !== undefined;\n const open = isControlled ? controlledOpen : internalOpen;\n\n const collapsed = useSidebarCollapsed();\n const filterValue = useContext(SidebarFilterContext);\n\n const setAndNotify = (next: boolean) => {\n if (!isControlled) {\n setInternalOpen(next);\n }\n\n onOpenChange?.(next);\n };\n\n useImperativeHandle(ref, () => ({\n expand: () => setAndNotify(true),\n collapse: () => setAndNotify(false),\n toggle: () => setAndNotify(!open),\n }));\n\n const hasVisibleChildren =\n filterValue.length === 0 || countMatchingItems(children, filterValue) > 0;\n\n if (!hasVisibleChildren) {\n return null;\n }\n\n const isOpen = collapsed ? true : open;\n const showHeader = !collapsed && !!(title || icon);\n\n const header = (title || icon) && (\n <RNView\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space1,\n paddingHorizontal: theme.space1,\n paddingVertical: 2,\n marginTop: 4,\n marginBottom: 2,\n }}\n >\n {icon && <RNView style={{ flexShrink: 0, opacity: 0.7 }}>{icon}</RNView>}\n {title && (\n <Text variant=\"caption-heading\" color=\"dim\" style={{ flex: 1 }} numberOfLines={1}>\n {title}\n </Text>\n )}\n {collapsible && (\n <Text variant=\"caption\" color=\"dim\" style={{ flexShrink: 0 }}>\n {open ? '▾' : '▸'}\n </Text>\n )}\n </RNView>\n );\n\n return (\n <RNView style={style} {...viewProps}>\n {showHeader &&\n (collapsible ? (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ expanded: open }}\n accessibilityLabel={title}\n onPress={() => setAndNotify(!open)}\n >\n {header}\n </Pressable>\n ) : (\n header\n ))}\n\n <RNView\n accessible\n accessibilityRole=\"list\"\n style={{ display: isOpen ? 'flex' : 'none', gap: 2 }}\n >\n {children}\n </RNView>\n </RNView>\n );\n },\n);\n","import { useEffect, useRef } from 'react';\nimport type { DimensionValue, StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { Animated, Easing, View as RNView } from 'react-native';\n\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport type SkeletonVariant = 'rect' | 'circle' | 'text';\n\nexport interface SkeletonProps extends Omit<ViewProps, 'style'> {\n /** Shape of the placeholder. Defaults to `\"rect\"`. */\n variant?: SkeletonVariant;\n /** Width for rectangular placeholders. Defaults to `\"100%\"`. */\n width?: DimensionValue;\n /** Height for rectangular placeholders. Defaults to `16`. */\n height?: DimensionValue;\n /** Diameter for circular placeholders. Defaults to `40`. */\n size?: number;\n /** Number of rows for text placeholders. Defaults to `3`. */\n lines?: number;\n /** Enables the pulse animation. Respects `useReducedMotion()`. Defaults to `true`. */\n animated?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Loading placeholder for content-shaped skeleton screens.\n *\n * GNOME HIG recommends `Spinner` or `ProgressBar` for loading states; this\n * is a pragmatic web-style extension for layouts that benefit from\n * placeholder shape, ported as-is from `@gnome-ui/react`.\n *\n * The web version's shimmer is a `linear-gradient` swept across the shape\n * via `transform: translateX()`; this package has no gradient dependency\n * (no `expo-linear-gradient`/`react-native-linear-gradient` in its\n * dependency tree, and adding one for a single component would be scope\n * creep), so the animation is a plain opacity pulse instead — animating\n * `theme.skeletonBaseColor`'s own opacity between `1` and `0.5` on a\n * 1.4s round trip, the same cycle length as the web shimmer. This is a\n * common RN-idiomatic substitute for a CSS shimmer (compare Tailwind's own\n * `animate-pulse` utility, which uses the identical technique). Unlike\n * `Spinner` (slows) and `ProgressBar` (stops one state, slows the other),\n * `useReducedMotion()` here fully disables the pulse and shows a static\n * base color — mirroring the source CSS's own\n * `@media (prefers-reduced-motion: reduce) { animation: none }`, which has\n * no partial-motion in-between state to preserve.\n *\n * `theme.skeletonBaseColor` already resolves correctly per color scheme\n * (unlike `Spinner`'s track color, which the source CSS never actually\n * tokenizes), so no `useResolvedColorScheme()` branching is needed here.\n *\n * `accessible={false}` mirrors the web version's `aria-hidden=\"true\"` — a\n * loading placeholder carries no information a screen reader user needs,\n * same reasoning `Separator` already established for a purely decorative\n * element.\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/progress.html\n */\nexport const Skeleton = ({\n variant = 'rect',\n width = '100%',\n height = 16,\n size = 40,\n lines = 3,\n animated = true,\n style,\n ...viewProps\n}: SkeletonProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n const pulse = useRef(new Animated.Value(1)).current;\n\n const isPulsing = animated && !reducedMotion;\n\n useEffect(() => {\n if (!isPulsing) {\n pulse.setValue(1);\n\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n const easing = Easing.bezier(x1, y1, x2, y2);\n\n const animation = Animated.loop(\n Animated.sequence([\n Animated.timing(pulse, { toValue: 0.5, duration: 700, easing, useNativeDriver: true }),\n Animated.timing(pulse, { toValue: 1, duration: 700, easing, useNativeDriver: true }),\n ]),\n );\n\n animation.start();\n\n return () => animation.stop();\n }, [isPulsing, pulse, theme.easingDefault]);\n\n if (variant === 'text') {\n const lineCount = Math.max(1, Math.floor(lines));\n\n return (\n <RNView accessible={false} style={[{ gap: 8 }, style]} {...viewProps}>\n {Array.from({ length: lineCount }, (_, index) => (\n <Animated.View\n // biome-ignore lint/suspicious/noArrayIndexKey: lines have no natural stable id\n key={index}\n style={{\n width: index === lineCount - 1 ? '60%' : '100%',\n height: 12,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.skeletonBaseColor,\n opacity: pulse,\n }}\n />\n ))}\n </RNView>\n );\n }\n\n const shapeStyle: ViewStyle =\n variant === 'circle'\n ? { width: size, height: size, borderRadius: size / 2 }\n : { width, height, borderRadius: theme.radiusMd };\n\n return (\n <Animated.View\n accessible={false}\n style={[shapeStyle, { backgroundColor: theme.skeletonBaseColor, opacity: pulse }, style]}\n {...viewProps}\n />\n );\n};\n","import { useCallback, useMemo, useState } from 'react';\nimport type {\n AccessibilityActionEvent,\n GestureResponderEvent,\n LayoutChangeEvent,\n StyleProp,\n ViewProps,\n ViewStyle,\n} from 'react-native';\nimport { PanResponder, Text, View } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nconst TRACK_HEIGHT = 6;\nconst THUMB_SIZE = 22;\nconst TICK_SIZE = 4;\n\nexport interface SliderMark {\n value: number;\n label?: string;\n}\n\nexport interface SliderProps extends Omit<ViewProps, 'style'> {\n /** Current value. Must be between `min` and `max`. */\n value: number;\n /** Called when the value changes. */\n onChange: (value: number) => void;\n /** Minimum value. Defaults to `0`. */\n min?: number;\n /** Maximum value. Defaults to `100`. */\n max?: number;\n /**\n * Granularity of each step.\n * - The `adjustable` accessibility action (VoiceOver/TalkBack swipe up/down) moves by one step.\n * Defaults to `1`.\n */\n step?: number;\n /** Disables the control. */\n disabled?: boolean;\n /**\n * Marks to display along the track.\n * Each mark can have an optional label rendered below the track.\n */\n marks?: SliderMark[];\n /** Accessible label. Required — RN has no visible-label association to fall back on. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\nfunction clamp(v: number, min: number, max: number) {\n return Math.min(max, Math.max(min, v));\n}\n\nfunction snapToStep(v: number, min: number, step: number, decimals: number) {\n return parseFloat((Math.round((v - min) / step) * step + min).toFixed(decimals));\n}\n\nfunction countDecimals(n: number) {\n const s = n.toString();\n const dot = s.indexOf('.');\n\n return dot === -1 ? 0 : s.length - dot - 1;\n}\n\n/**\n * Draggable range slider following the Adwaita `GtkScale` pattern.\n *\n * - Touch drag (via `PanResponder`) sets the value continuously.\n * - `accessibilityRole=\"adjustable\"` + `onAccessibilityAction` handles the\n * \"increment\"/\"decrement\" actions VoiceOver (swipe up/down) and TalkBack\n * generate for an adjustable element — RN's native analog of the web\n * version's ← / → keyboard stepping, since a touch-first device has no\n * keyboard to drive `ArrowLeft`/`ArrowRight`/`Home`/`End`/`PageUp`/\n * `PageDown` with. Those bigger/edge jumps have no equivalent gesture on\n * either platform's screen reader, so only single-step increment/decrement\n * is ported.\n * - Optional tick marks with labels.\n * - Fills the track from the left up to the thumb (accent colour).\n *\n * RN's `transform` only accepts pixel offsets, not the CSS `%` units the\n * web version's `left: X%; transform: translate(-50%, -50%)` centering\n * trick relies on — so the thumb and tick marks are positioned with a\n * plain pixel `left` (`(pct / 100) * trackWidth - size / 2`, measured via\n * `onLayout`) instead, and vertical centering comes from the track's own\n * `justifyContent: 'center'` rather than a transform.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/sliders.html\n */\nexport const Slider = ({\n value,\n onChange,\n min = 0,\n max = 100,\n step = 1,\n disabled = false,\n marks,\n accessibilityLabel,\n style,\n onLayout,\n ...viewProps\n}: SliderProps) => {\n const theme = useGnomeTheme();\n const [trackWidth, setTrackWidth] = useState(0);\n const [labelWidths, setLabelWidths] = useState<Record<number, number>>({});\n\n const dp = countDecimals(step);\n const clamped = clamp(value, min, max);\n const pct = ((clamped - min) / (max - min)) * 100;\n\n const handleTrackLayout = useCallback(\n (event: LayoutChangeEvent) => {\n setTrackWidth(event.nativeEvent.layout.width);\n onLayout?.(event);\n },\n [onLayout],\n );\n\n const commit = useCallback(\n (locationX: number) => {\n if (disabled || trackWidth <= 0) {\n return;\n }\n\n const raw = (locationX / trackWidth) * (max - min) + min;\n\n onChange(clamp(snapToStep(raw, min, step, dp), min, max));\n },\n [disabled, trackWidth, min, max, step, dp, onChange],\n );\n\n const panResponder = useMemo(\n () =>\n PanResponder.create({\n onStartShouldSetPanResponder: () => !disabled,\n onMoveShouldSetPanResponder: () => !disabled,\n onPanResponderGrant: (event: GestureResponderEvent) => commit(event.nativeEvent.locationX),\n onPanResponderMove: (event: GestureResponderEvent) => commit(event.nativeEvent.locationX),\n }),\n [disabled, commit],\n );\n\n const handleAccessibilityAction = useCallback(\n (event: AccessibilityActionEvent) => {\n if (disabled) {\n return;\n }\n\n switch (event.nativeEvent.actionName) {\n case 'increment':\n onChange(clamp(snapToStep(value + step, min, step, dp), min, max));\n break;\n case 'decrement':\n onChange(clamp(snapToStep(value - step, min, step, dp), min, max));\n break;\n }\n },\n [disabled, value, step, min, max, dp, onChange],\n );\n\n const hasMarks = marks && marks.length > 0;\n const hasLabels = hasMarks && marks.some((m) => m.label);\n\n const thumbLeft = (pct / 100) * trackWidth - THUMB_SIZE / 2;\n\n return (\n <View\n style={[{ width: '100%', paddingTop: 10, paddingBottom: hasLabels ? 0 : 10 }, style]}\n {...viewProps}\n >\n <View\n onLayout={handleTrackLayout}\n accessible\n accessibilityRole=\"adjustable\"\n accessibilityLabel={accessibilityLabel}\n accessibilityValue={{ min, max, now: clamped }}\n accessibilityState={{ disabled }}\n accessibilityActions={[\n { name: 'increment', label: 'increment' },\n { name: 'decrement', label: 'decrement' },\n ]}\n onAccessibilityAction={handleAccessibilityAction}\n style={{\n justifyContent: 'center',\n height: THUMB_SIZE,\n opacity: disabled ? theme.opacityDisabled : 1,\n }}\n {...panResponder.panHandlers}\n >\n <View\n pointerEvents=\"none\"\n style={{\n width: '100%',\n height: TRACK_HEIGHT,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.cardShadeColor,\n overflow: 'hidden',\n }}\n >\n <View\n style={{\n height: '100%',\n width: `${pct}%`,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.accentBgColor,\n }}\n />\n </View>\n\n <View\n pointerEvents=\"none\"\n style={{\n position: 'absolute',\n left: thumbLeft,\n width: THUMB_SIZE,\n height: THUMB_SIZE,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.accentBgColor,\n borderWidth: 2,\n borderColor: theme.windowBgColor,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 1 },\n shadowOpacity: 0.25,\n shadowRadius: 3,\n elevation: 3,\n }}\n />\n\n {hasMarks &&\n marks.map((m) => {\n const mPct = ((clamp(m.value, min, max) - min) / (max - min)) * 100;\n const tickLeft = (mPct / 100) * trackWidth - TICK_SIZE / 2;\n\n return (\n <View\n key={m.value}\n pointerEvents=\"none\"\n importantForAccessibility=\"no\"\n style={{\n position: 'absolute',\n left: tickLeft,\n width: TICK_SIZE,\n height: TICK_SIZE,\n borderRadius: TICK_SIZE / 2,\n backgroundColor: theme.windowBgColor,\n }}\n />\n );\n })}\n </View>\n\n {hasLabels && (\n <View\n importantForAccessibility=\"no\"\n style={{ position: 'relative', width: '100%', height: 20, marginTop: 6 }}\n >\n {marks.map((m) => {\n const mPct = ((clamp(m.value, min, max) - min) / (max - min)) * 100;\n const centerX = (mPct / 100) * trackWidth;\n const labelWidth = labelWidths[m.value] ?? 0;\n\n return (\n // Positioned by a pixel `left` computed from the label's own\n // measured width (0 until its first `onLayout`, so it starts\n // left-aligned to the mark and snaps to centered a frame\n // later) — a zero-width `View` with `alignItems: 'center'`\n // was tried first hoping Yoga would center an overflowing\n // child around it the way a CSS `%`-of-self `transform:\n // translateX(-50%)` does, but the iOS Simulator screenshot\n // check (never skip it) showed the labels not rendering at\n // all, so self-measurement replaced it.\n <Text\n key={m.value}\n numberOfLines={1}\n onLayout={(event) => {\n const {\n layout: { width },\n } = event.nativeEvent;\n\n setLabelWidths((prev) =>\n prev[m.value] === width ? prev : { ...prev, [m.value]: width },\n );\n }}\n style={{\n position: 'absolute',\n left: centerX - labelWidth / 2,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeCaption,\n color: theme.windowFgColor,\n opacity: theme.opacityDim,\n }}\n >\n {m.label}\n </Text>\n );\n })}\n </View>\n )}\n </View>\n );\n};\n","import { forwardRef } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nexport interface SpacerProps extends Omit<ViewProps, 'style'> {\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Invisible `flex: 1` filler for `Toolbar` and `HeaderBar` — mirrors\n * `@gnome-ui/react`'s `Spacer`. Place between leading and trailing groups\n * to push trailing items to the end.\n *\n * `accessible={false}` mirrors the web version's `aria-hidden=\"true\"` —\n * same \"purely decorative, exclude from the accessibility tree entirely\"\n * call `Separator` already made, rather than reaching for `role`'s newer\n * `\"separator\"` value (which exists but implies a divider a screen\n * reader user might care about; a plain flex filler has no such meaning).\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest/style-classes.html#spacer\n *\n * @example\n * <Toolbar>\n * <Button variant=\"flat\">Back</Button>\n * <Spacer />\n * <Button variant=\"flat\">Done</Button>\n * </Toolbar>\n */\nexport const Spacer = forwardRef<View, SpacerProps>(function Spacer({ style, ...viewProps }, ref) {\n return (\n <RNView ref={ref} accessible={false} style={[{ flex: 1, minWidth: 0 }, style]} {...viewProps} />\n );\n});\n","import { useCallback } from 'react';\nimport type { AccessibilityActionEvent, StyleProp, ViewStyle } from 'react-native';\nimport { Pressable, Text, View } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nconst BTN_SIZE = 36;\n\nexport interface SpinButtonProps {\n /** Current value. */\n value: number;\n /** Called when the value changes. */\n onChange: (value: number) => void;\n /** Minimum allowed value. Defaults to `0`. */\n min?: number;\n /** Maximum allowed value. Defaults to `100`. */\n max?: number;\n /** Amount to increment/decrement per step. Defaults to `1`. */\n step?: number;\n /** Number of decimal places shown. Derived from `step` when omitted. */\n decimals?: number;\n /**\n * Wrap around instead of clamping at the edges — stepping past `max` returns\n * to `min` and vice versa, and the −/+ buttons never disable. Mirrors\n * `GtkSpinButton:wrap`; used for cyclic values like hours and minutes.\n */\n wrap?: boolean;\n /**\n * Custom display formatter for the current value (e.g. zero-padding, or\n * mapping a numeric value to `AM`/`PM`). Defaults to fixed-decimal text. When\n * provided, its result is also exposed as the accessibility value's `text`.\n */\n format?: (value: number) => string;\n /** Disables the control. */\n disabled?: boolean;\n /** Accessible label. Required — RN has no visible-label association to fall back on. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\nfunction countDecimals(n: number): number {\n const s = n.toString();\n const dot = s.indexOf('.');\n\n return dot === -1 ? 0 : s.length - dot - 1;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n\nfunction wrapValue(value: number, min: number, max: number, step: number): number {\n const range = max - min + step;\n return ((((value - min) % range) + range) % range) + min;\n}\n\n/**\n * Numeric input with − and + buttons following the Adwaita `GtkSpinButton` style.\n *\n * Rebuilt with `View`/`Pressable`/`Text` rather than ported from\n * `@gnome-ui/react`'s DOM-based JSX, but mirrors its prop API and clamp/wrap/\n * decimal math (pure JS, ported verbatim).\n *\n * The primary interaction is tapping the visible −/+ buttons, same as a\n * sighted mouse user on the web version. The web version's keyboard\n * interaction (↑/↓ one step, Page Up/Down ten steps, Home/End to bounds) has\n * no RN equivalent — a touch-first device has no keyboard to drive it, the\n * same reasoning `Slider` already applied. Rather than dropping value\n * adjustment accessibility entirely, single-step increment/decrement is\n * wired through `accessibilityRole=\"adjustable\"` + `onAccessibilityAction`\n * (VoiceOver's swipe-up/down, TalkBack's local-context menu), reusing the\n * exact recipe `Slider` already proved works — the bigger Page Up/Down and\n * Home/End jumps have no equivalent screen-reader gesture on either\n * platform, so those alone are dropped, same as `Slider`. The visible\n * buttons are hidden from the accessibility tree (mirrors the web version's\n * `aria-hidden`/`tabIndex={-1}` on both `<button>`s) so a screen reader user\n * gets one adjustable stop, not three.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/spin-buttons.html\n */\nexport const SpinButton = ({\n value,\n onChange,\n min = 0,\n max = 100,\n step = 1,\n decimals,\n wrap = false,\n format,\n disabled = false,\n accessibilityLabel,\n style,\n}: SpinButtonProps) => {\n const theme = useGnomeTheme();\n const dp = decimals ?? countDecimals(step);\n\n const set = useCallback(\n (next: number) => {\n const bounded = wrap ? wrapValue(next, min, max, step) : clamp(next, min, max);\n onChange(parseFloat(bounded.toFixed(dp)));\n },\n [onChange, min, max, step, dp, wrap],\n );\n\n const displayText = format ? format(value) : value.toFixed(dp);\n\n const handleAccessibilityAction = useCallback(\n (event: AccessibilityActionEvent) => {\n if (disabled) {\n return;\n }\n\n switch (event.nativeEvent.actionName) {\n case 'increment':\n set(value + step);\n break;\n case 'decrement':\n set(value - step);\n break;\n }\n },\n [disabled, value, step, set],\n );\n\n const decrementDisabled = disabled || (!wrap && value <= min);\n const incrementDisabled = disabled || (!wrap && value >= max);\n\n return (\n <View\n accessible\n accessibilityRole=\"adjustable\"\n accessibilityLabel={accessibilityLabel}\n accessibilityValue={{ min, max, now: value, text: format ? displayText : undefined }}\n accessibilityState={{ disabled }}\n accessibilityActions={[\n { name: 'increment', label: 'increment' },\n { name: 'decrement', label: 'decrement' },\n ]}\n onAccessibilityAction={handleAccessibilityAction}\n style={[\n {\n flexDirection: 'row',\n alignSelf: 'flex-start',\n alignItems: 'center',\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n backgroundColor: theme.viewBgColor,\n overflow: 'hidden',\n opacity: disabled ? theme.opacityDisabled : 1,\n },\n style,\n ]}\n >\n <Pressable\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n disabled={decrementDisabled}\n onPress={() => set(value - step)}\n style={({ pressed }) => ({\n width: BTN_SIZE,\n height: BTN_SIZE,\n alignItems: 'center',\n justifyContent: 'center',\n borderRightWidth: 1,\n borderRightColor: theme.cardShadeColor,\n backgroundColor: pressed && !decrementDisabled ? theme.activeOverlay : 'transparent',\n opacity: decrementDisabled ? theme.opacityDisabled : 1,\n })}\n >\n <Text style={{ fontSize: 17, lineHeight: 20, color: theme.viewFgColor }}>−</Text>\n </Pressable>\n\n <Text\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n minWidth: 56,\n textAlign: 'center',\n paddingHorizontal: theme.space2,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontVariant: ['tabular-nums'],\n color: theme.viewFgColor,\n }}\n >\n {displayText}\n </Text>\n\n <Pressable\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n disabled={incrementDisabled}\n onPress={() => set(value + step)}\n style={({ pressed }) => ({\n width: BTN_SIZE,\n height: BTN_SIZE,\n alignItems: 'center',\n justifyContent: 'center',\n borderLeftWidth: 1,\n borderLeftColor: theme.cardShadeColor,\n backgroundColor: pressed && !incrementDisabled ? theme.activeOverlay : 'transparent',\n opacity: incrementDisabled ? theme.opacityDisabled : 1,\n })}\n >\n <Text style={{ fontSize: 17, lineHeight: 20, color: theme.viewFgColor }}>+</Text>\n </Pressable>\n </View>\n );\n};\n","import { useEffect, useRef } from 'react';\nimport type { StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { Animated, Easing } from 'react-native';\n\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\n\nexport type SpinnerSize = 'sm' | 'md' | 'lg';\n\nconst SIZES: Record<SpinnerSize, { dimension: number; borderWidth: number }> = {\n sm: { dimension: 16, borderWidth: 2 },\n md: { dimension: 24, borderWidth: 2.5 },\n lg: { dimension: 36, borderWidth: 3 },\n};\n\nexport interface SpinnerProps extends Omit<ViewProps, 'style'> {\n /** Size of the spinner. Defaults to `\"md\"`. */\n size?: SpinnerSize;\n /**\n * Accessible label announced by screen readers. Defaults to\n * `\"Loading…\"`. Set to `\"\"` to silence if a sibling label is present.\n */\n label?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Indeterminate loading indicator following the Adwaita spinner style.\n *\n * Rebuilt on `Animated.View` rather than ported from `@gnome-ui/react`'s\n * pure-CSS `@keyframes spin` animation: the ring itself reuses the same\n * per-side-border trick the CSS does (`borderColor` for the track,\n * `borderTopColor` for the accent-colored \"head\" of the spinner, on a\n * `borderRadius: 50%` circle) — that part translates directly, since RN's\n * `View` supports independent per-side border colors too. The rotation\n * itself is an `Animated.loop`d `Animated.timing` driving a `rotate`\n * transform, `useNativeDriver: true` since only a transform is animated.\n *\n * `useReducedMotion()` mirrors the source CSS's own\n * `@media (prefers-reduced-motion: reduce) { animation-duration: 2s }` —\n * slowed to 2s, not stopped outright, exactly matching the web behavior\n * rather than dropping the animation entirely.\n *\n * RN's `AccessibilityRole` union has no \"status\" value (the web version's\n * `role=\"status\"`); `\"progressbar\"` is the closest match for an\n * indeterminate loading indicator and is what's used here — omitting\n * `accessibilityValue` is RN's equivalent of omitting `aria-valuenow` for\n * an indeterminate progress bar. `@gnome-ui/react`'s two hardcoded\n * per-color-scheme track colors (the `--gnome-spinner-track-color` CSS\n * variable is referenced but never actually defined in `@gnome-ui/core`'s\n * tokens, so the CSS fallback value is always what renders) are ported as\n * the same hardcoded `rgba()` pair, branched on `useResolvedColorScheme()`\n * — the same pattern `Switch`/`Checkbox`/`RadioButton` already use for\n * colors the source CSS hardcodes rather than tokenizes.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/style-classes.html\n */\nexport const Spinner = ({ size = 'md', label = 'Loading…', style, ...viewProps }: SpinnerProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const reducedMotion = useReducedMotion();\n\n const spin = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n spin.setValue(0);\n\n const animation = Animated.loop(\n Animated.timing(spin, {\n toValue: 1,\n duration: reducedMotion ? 2000 : 750,\n easing: Easing.linear,\n useNativeDriver: true,\n }),\n );\n\n animation.start();\n\n return () => animation.stop();\n }, [spin, reducedMotion]);\n\n const rotate = spin.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '360deg'] });\n const { dimension, borderWidth } = SIZES[size];\n const trackColor = scheme === 'dark' ? 'rgba(255, 255, 255, 0.15)' : 'rgba(0, 0, 0, 0.15)';\n\n return (\n <Animated.View\n accessible\n accessibilityRole=\"progressbar\"\n accessibilityLabel={label || undefined}\n accessibilityElementsHidden={label === ''}\n importantForAccessibility={label === '' ? 'no-hide-descendants' : 'yes'}\n style={[\n {\n width: dimension,\n height: dimension,\n borderRadius: dimension / 2,\n borderWidth,\n borderColor: trackColor,\n borderTopColor: theme.accentColor,\n transform: [{ rotate }],\n },\n style,\n ]}\n {...viewProps}\n />\n );\n};\n","import { type ReactNode, useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\n\nimport { ActionRow } from '@/components/ActionRow';\nimport { SpinButton } from '@/components/SpinButton';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface SpinRowProps {\n /** Primary label. */\n title: string;\n /** Secondary line below the title. */\n subtitle?: string;\n /** Icon or image placed at the leading edge. */\n leading?: ReactNode;\n /** Current value (controlled). */\n value?: number;\n /** Initial value when uncontrolled. Defaults to `0`. */\n defaultValue?: number;\n /** Called when the value changes. */\n onValueChange?: (value: number) => void;\n /** Minimum allowed value. Defaults to `0`. */\n min?: number;\n /** Maximum allowed value. Defaults to `100`. */\n max?: number;\n /** Amount to increment/decrement per step. Defaults to `1`. */\n step?: number;\n /** Number of decimal places shown. Derived from `step` when omitted. */\n decimals?: number;\n /** Accessible name for the spin button. Defaults to `title`. */\n accessibilityLabel?: string;\n /** Disables the row and its spin button. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Settings row with an integrated spin button for numeric values, mirroring\n * `AdwSpinRow` and `@gnome-ui/react`'s own `SpinRow`. Use inside a\n * `BoxedList` for settings with numeric ranges (volume, timeout duration,\n * count limits, etc.).\n *\n * **This is a composition of `ActionRow` + `SpinButton`** — same reasoning\n * as `ComboRow` (`ActionRow` + `Dropdown`): the row layout and the numeric\n * stepper are each already shipped, verified components, so nothing here is\n * rebuilt. `SpinButton` is controlled-only, so the uncontrolled `defaultValue`\n * state lives here, one level up, the same shape `ComboRow` already\n * established for `Dropdown`.\n *\n * The web version's keyboard interaction (↑/↓ one step, Page Up/Down ten\n * steps, Home/End to bounds) drops as it does everywhere else in this\n * package — `SpinButton` already provides the touch/screen-reader\n * equivalents it was built with (tap the visible −/+ buttons, or the\n * `accessibilityRole=\"adjustable\"` increment/decrement actions).\n *\n * @example\n * <BoxedList>\n * <SpinRow\n * title=\"Volume\"\n * subtitle=\"Output level\"\n * value={volume}\n * onValueChange={setVolume}\n * min={0}\n * max={100}\n * />\n * </BoxedList>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.SpinRow.html\n */\nexport const SpinRow = ({\n title,\n subtitle,\n leading,\n value: controlledValue,\n defaultValue = 0,\n onValueChange,\n min = 0,\n max = 100,\n step = 1,\n decimals,\n accessibilityLabel,\n disabled = false,\n style,\n testID,\n}: SpinRowProps) => {\n const theme = useGnomeTheme();\n const isControlled = controlledValue !== undefined;\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const value = isControlled ? controlledValue : uncontrolledValue;\n\n return (\n <ActionRow\n testID={testID}\n title={title}\n subtitle={subtitle}\n leading={leading}\n disabled={disabled}\n style={[disabled ? { opacity: theme.opacityDisabled } : null, style]}\n trailing={\n <SpinButton\n value={value}\n min={min}\n max={max}\n step={step}\n decimals={decimals}\n disabled={disabled}\n accessibilityLabel={accessibilityLabel ?? title}\n onChange={(next) => {\n if (!isControlled) {\n setUncontrolledValue(next);\n }\n\n onValueChange?.(next);\n }}\n />\n }\n />\n );\n};\n","import { PanDown } from '@gnome-ui/icons';\nimport { type ReactNode, useState } from 'react';\nimport type { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native';\nimport { Pressable, View } from 'react-native';\n\nimport { Button } from '@/components/Button';\nimport { Icon } from '@/components/Icon';\nimport { Popover } from '@/components/Popover';\nimport { useGnomeTheme } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type SplitButtonVariant = 'default' | 'suggested' | 'destructive';\n\nexport interface SplitButtonProps {\n /** Label shown in the primary button. */\n label: string;\n /** Visual style. Applies to both the primary and toggle halves. */\n variant?: SplitButtonVariant;\n /** Content rendered inside the popover panel when the arrow is pressed. */\n dropdownContent: ReactNode;\n /** Accessible label for the dropdown toggle button. Defaults to `\"More options\"`. */\n dropdownLabel?: string;\n /** Called when the primary (label) half is pressed. */\n onPress?: (event: GestureResponderEvent) => void;\n /** Disables both halves. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\ninterface VariantColors {\n backgroundColor: string;\n borderColor: string;\n color: string;\n separatorColor: string;\n}\n\n/**\n * Mirrors `Button`'s own (unexported) `getVariantColors` for the\n * non-`osd`/non-pressed resting state, so the hand-rolled toggle half below\n * renders pixel-identical colors to the real `Button` used for the primary\n * half — small enough to duplicate rather than export/import across\n * components, same call as `dimColor()`'s duplication elsewhere in this\n * package.\n */\nfunction getVariantColors(theme: GnomeThemeTokens, variant: SplitButtonVariant): VariantColors {\n switch (variant) {\n case 'suggested':\n return {\n backgroundColor: theme.accentBgColor,\n borderColor: 'transparent',\n color: theme.accentFgColor,\n separatorColor: 'rgba(255, 255, 255, 0.25)',\n };\n case 'destructive':\n return {\n backgroundColor: theme.destructiveBgColor,\n borderColor: 'transparent',\n color: theme.destructiveFgColor,\n separatorColor: 'rgba(255, 255, 255, 0.25)',\n };\n default:\n return {\n backgroundColor: theme.cardBgColor,\n borderColor: theme.light3,\n color: theme.windowFgColor,\n separatorColor: theme.light3,\n };\n }\n}\n\n/**\n * Primary action button with an attached dropdown arrow, mirroring\n * `AdwSplitButton` and `@gnome-ui/react`'s own `SplitButton`. Pressing the\n * label half fires `onPress`; pressing the arrow half opens a floating\n * panel with `dropdownContent` (menus, options, etc.).\n *\n * The primary half is a real `Button` — its resting/pressed/disabled colors\n * come for free. The arrow half is a hand-rolled `Pressable` rather than a\n * second `Button`: nesting `Popover` (whose `children` clones a prop-level\n * `accessibilityState`/`onPress` onto its trigger) around a full `Button`\n * would silently clobber `Button`'s own internal `accessibilityState={{\n * disabled }}` — RN merges a spread prop object outright, it doesn't merge\n * key-by-key — so the arrow half derives the exact same variant colors via\n * `getVariantColors` above instead, and owns `accessibilityState` itself\n * (only `{ disabled }`, letting `Popover`'s clone merge in `expanded`).\n * The two halves are visually connected by zeroing the shared inner corner\n * radii and painting a 1px separator between them, the RN equivalent of the\n * web CSS's `border-radius` split + `.separator` span.\n *\n * `Popover` supplies the floating panel, `Modal`-based positioning,\n * outside-tap dismissal, and Android back-button handling — no repositioning\n * math of its own needed here, unlike the web version's manual\n * `getBoundingClientRect`/scroll-and-resize-tracking `place()` effect.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.SplitButton.html\n */\nexport const SplitButton = ({\n label,\n variant = 'default',\n dropdownContent,\n dropdownLabel = 'More options',\n onPress,\n disabled = false,\n style,\n testID,\n}: SplitButtonProps) => {\n const theme = useGnomeTheme();\n const [open, setOpen] = useState(false);\n const colors = getVariantColors(theme, variant);\n const solid = variant === 'suggested' || variant === 'destructive';\n\n return (\n <View\n testID={testID}\n style={[\n {\n flexDirection: 'row',\n alignSelf: 'flex-start',\n alignItems: 'stretch',\n borderRadius: theme.radiusMd,\n overflow: 'hidden',\n },\n style,\n ]}\n >\n <Button\n variant={variant}\n disabled={disabled}\n onPress={onPress}\n style={{ borderTopRightRadius: 0, borderBottomRightRadius: 0 }}\n >\n {label}\n </Button>\n\n <View style={{ width: 1, backgroundColor: colors.separatorColor }} />\n\n <Popover content={dropdownContent} open={open} onOpenChange={setOpen} placement=\"bottom\">\n <Pressable\n disabled={disabled}\n accessibilityRole=\"button\"\n accessibilityLabel={dropdownLabel}\n accessibilityState={{ disabled }}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: 34,\n minWidth: 36,\n paddingHorizontal: theme.space1,\n borderWidth: 1,\n borderColor: colors.borderColor,\n borderTopRightRadius: theme.radiusMd,\n borderBottomRightRadius: theme.radiusMd,\n backgroundColor: pressed && !solid ? theme.light3 : colors.backgroundColor,\n opacity: disabled ? theme.opacityDisabled : pressed && solid ? 0.85 : 1,\n })}\n >\n <Icon\n icon={PanDown}\n size=\"md\"\n tintColor={colors.color}\n style={{ transform: [{ rotate: open ? '180deg' : '0deg' }] }}\n />\n </Pressable>\n </Popover>\n </View>\n );\n};\n","import type { ReactNode } from 'react';\nimport type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native';\nimport { Text, View } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type StatusBadgeVariant = 'success' | 'warning' | 'error' | 'new' | 'accent' | 'neutral';\n\nexport interface StatusBadgeProps extends Omit<ViewProps, 'style'> {\n /**\n * Visual style. Defaults to `\"neutral\"`.\n * - `success` — green, for published / active states.\n * - `warning` — yellow, for beta / pending states.\n * - `error` — red, for failed / rejected states.\n * - `new` — purple, for newly released / featured items.\n * - `accent` — blue, for highlighted / primary states.\n * - `neutral` — muted overlay, for draft / archived / inactive states.\n */\n variant?: StatusBadgeVariant;\n /** Short human-readable label. String/number render as a themed label; other nodes render as-is. */\n children: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\ninterface VariantColors {\n backgroundColor: string;\n color: string;\n}\n\nfunction getVariantColors(theme: GnomeThemeTokens, variant: StatusBadgeVariant): VariantColors {\n switch (variant) {\n case 'success':\n return { backgroundColor: theme.successBgColor, color: theme.successFgColor };\n case 'warning':\n return { backgroundColor: theme.warningBgColor, color: theme.warningFgColor };\n case 'error':\n return { backgroundColor: theme.errorBgColor, color: theme.errorFgColor };\n case 'new':\n return { backgroundColor: theme.newBgColor, color: theme.newFgColor };\n case 'accent':\n return { backgroundColor: theme.accentBgColor, color: theme.accentFgColor };\n case 'neutral':\n return { backgroundColor: theme.hoverOverlay, color: theme.windowFgColor };\n }\n}\n\n/**\n * Pill-shaped text label for entity status — published, beta, new, etc.,\n * mirroring `@gnome-ui/react`'s `StatusBadge`.\n *\n * Unlike `Badge` (numeric counts, optional anchor/dot mode), `StatusBadge`\n * is a simpler sibling designed purely for short human-readable state\n * labels — no anchor positioning, no dot mode, no counter. The variant\n * color mapping reuses `Badge`'s exact `getVariantColors` shape (this\n * component gets its own copy rather than importing `Badge`'s internal\n * helper, since it isn't exported — same small-duplicated-helper precedent\n * as `dimColor` across `TextField`/`SearchBar`/`PasswordField`), plus a\n * `new` (purple) variant `Badge` doesn't have. `neutral`'s background is\n * `theme.hoverOverlay` — a translucent overlay, matching the source CSS's\n * `--gnome-hover-overlay` exactly — rather than `Badge`'s own flat\n * `light4`/`dark2` neutral, since this component's source CSS genuinely\n * differs from `Badge`'s on that one variant.\n *\n * `children` follows the same convention `Badge` already established:\n * string/number renders as a themed `Text` label, any other node renders\n * as-is.\n *\n * @example\n * <StatusBadge variant=\"success\">published</StatusBadge>\n * <StatusBadge variant=\"warning\">beta</StatusBadge>\n * <StatusBadge variant=\"new\">new</StatusBadge>\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/badges.html\n */\nexport const StatusBadge = ({\n variant = 'neutral',\n children,\n style,\n ...viewProps\n}: StatusBadgeProps) => {\n const theme = useGnomeTheme();\n const { backgroundColor, color } = getVariantColors(theme, variant);\n\n return (\n <View\n style={[\n {\n alignSelf: 'flex-start',\n paddingVertical: 2,\n paddingHorizontal: 8,\n borderRadius: theme.radiusPill,\n backgroundColor,\n },\n style,\n ]}\n {...viewProps}\n >\n {typeof children === 'string' || typeof children === 'number' ? (\n <Text\n style={{\n fontFamily: theme.fontFamily,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n fontSize: 11,\n lineHeight: 15,\n letterSpacing: 0.22,\n color,\n }}\n >\n {children}\n </Text>\n ) : (\n children\n )}\n </View>\n );\n};\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport { forwardRef, type ReactNode } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { WrapBox } from '@/components/WrapBox';\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\n\nexport interface StatusPageProps extends Omit<ViewProps, 'style'> {\n /**\n * Large icon displayed above the title.\n * Use an icon from `@gnome-ui/icons` or omit for a text-only page.\n */\n icon?: IconDefinition;\n /**\n * Custom icon node. Use when you need an image, emoji, or a rendered\n * SVG that is not part of `@gnome-ui/icons`.\n * Ignored when `icon` is also provided.\n */\n iconNode?: ReactNode;\n /** Main heading. Keep it short — one noun phrase. */\n title: string;\n /** Supporting description rendered below the title. */\n description?: string;\n /**\n * Optional action area — typically one or two `Button`s.\n * Rendered below the description, wrapping onto a second line if needed.\n */\n children?: ReactNode;\n /**\n * Reduces padding, icon size, and title scale for use in compact\n * contexts such as sidebars, popovers, and small panels.\n */\n compact?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * The description's `max-width: 36ch` has no RN unit to port to. `ch` is\n * the advance width of \"0\", ≈ 0.5em in the sans faces Adwaita uses, so the\n * cap is resolved against the description's own font size — keeping the\n * measure font-relative the way the CSS is, rather than freezing it at one\n * pixel value that would be wrong in `compact`.\n */\nconst measureFor = (fontSize: number) => Math.round(36 * 0.5 * fontSize);\n\n/**\n * Empty-state / status page following the Adwaita `AdwStatusPage` pattern,\n * mirroring `@gnome-ui/react`'s own `StatusPage`.\n *\n * Use to fill a view with no content yet, an error state, or a completion\n * confirmation. Always explain *why* the view is empty and *what the user\n * can do* about it — don't use it for loading states, where `Spinner` or\n * `ProgressBar` belong instead.\n *\n * Centres its content on both axes, but — exactly as in the web version —\n * the vertical centring only does anything once a parent gives it height:\n * put it in a `flex: 1` container to fill the view.\n *\n * The title renders through this package's `Text` at `variant=\"title-1\"`\n * (`\"title-4\"` when `compact`), which means it also picks up `Text`'s\n * automatic `header` accessibility role — a deliberate divergence from the\n * web version's `<p class=\"title\">`. That `<p>` exists because HTML forces\n * you to pick a concrete `h1`–`h6` level for a component that has no idea\n * where it sits in the document outline; RN's `header` role carries no\n * level, so the dilemma disappears and the title can be what it actually\n * is. On a touch device the rotor is the only structural navigation a\n * screen reader user has, so this is worth having.\n *\n * The icon is dimmed by its wrapper's `opacity` (0.55 light / 0.45 dark,\n * the two values the web version's own `@media (prefers-color-scheme)`\n * block hardcodes) and hidden from assistive tech with the\n * `accessibilityElementsHidden` + `importantForAccessibility=\"no\"` pair\n * this package already uses in place of `aria-hidden`. Its color needs no\n * handling at all: `Icon` already defaults to the theme foreground, which\n * is what `.iconWrap`'s `color` sets.\n *\n * The action area is a `WrapBox` rather than a hand-rolled row — `.actions`\n * is `display: flex; flex-wrap: wrap; justify-content: center; gap` and\n * nothing else, which is exactly what that component already is.\n *\n * @example\n * <StatusPage\n * icon={StarOutline}\n * title=\"No favorites yet\"\n * description=\"Packages you star will show up here.\"\n * >\n * <Button variant=\"suggested\" onPress={onAdd}>Add a package</Button>\n * </StatusPage>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.StatusPage.html\n * @see https://developer.gnome.org/hig/patterns/feedback/empty-states.html\n */\nexport const StatusPage = forwardRef<View, StatusPageProps>(function StatusPage(\n { icon, iconNode, title, description, children, compact = false, style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const colorScheme = useResolvedColorScheme();\n\n const iconSize = compact ? 64 : 128;\n const iconContent = icon ? <Icon icon={icon} width={iconSize} height={iconSize} /> : iconNode;\n const descriptionFontSize = compact ? theme.fontSizeCaption : theme.fontSizeBody;\n\n return (\n <RNView\n ref={ref}\n style={[\n {\n width: '100%',\n alignItems: 'center',\n justifyContent: 'center',\n paddingVertical: compact ? theme.space3 : theme.space6,\n paddingHorizontal: compact ? theme.space3 : theme.space4,\n },\n style,\n ]}\n {...viewProps}\n >\n {iconContent ? (\n <RNView\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n alignItems: 'center',\n justifyContent: 'center',\n marginBottom: compact ? theme.space2 : theme.space4,\n opacity: colorScheme === 'dark' ? 0.45 : 0.55,\n }}\n >\n {iconContent}\n </RNView>\n ) : null}\n\n <Text variant={compact ? 'title-4' : 'title-1'} style={{ textAlign: 'center' }}>\n {title}\n </Text>\n\n {description ? (\n <Text\n variant={compact ? 'caption' : 'body'}\n color=\"dim\"\n style={{\n textAlign: 'center',\n maxWidth: measureFor(descriptionFontSize),\n marginTop: compact ? theme.space1 : theme.space2,\n }}\n >\n {description}\n </Text>\n ) : null}\n\n {children ? (\n <WrapBox\n justify=\"center\"\n childSpacing={compact ? theme.space1 : theme.space2}\n style={{ marginTop: compact ? theme.space2 : theme.space4 }}\n >\n {children}\n </WrapBox>\n ) : null}\n </RNView>\n );\n});\n","import { Check } from '@gnome-ui/icons';\nimport { useEffect, useRef } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable, View } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport type StepIndicatorOrientation = 'horizontal' | 'vertical';\n\nexport interface StepIndicatorProps {\n /**\n * Total number of steps, or an array of per-step labels rendered beside\n * (vertical) or beneath (horizontal) each circle. Pass a plain number for\n * an unlabelled sequence (only the \"Step X of Y\" caption is shown); pass\n * an array of strings to label each step.\n */\n steps: number | string[];\n /** Zero-based index of the current/active step. */\n currentStep: number;\n /** Layout direction. Default: `\"horizontal\"`. */\n orientation?: StepIndicatorOrientation;\n /**\n * Called when a completed step's circle is pressed, letting the user jump\n * back to a step they've already finished. Omit to make steps\n * non-interactive. The current and upcoming steps are never pressable.\n */\n onStepClick?: (index: number) => void;\n /** Accessible label for the indicator. Default: `\"Progress\"`. */\n label?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst CIRCLE_SIZE = 28;\nconst CONNECTOR_THICKNESS = 2;\n/** Matches `@gnome-ui/react`'s `.stepItem::after { inset-block-start: 14px }` (horizontal). */\nconst HORIZONTAL_CONNECTOR_TOP = 14;\n/** Matches the vertical `::after`'s `inset-inline-start`/`inset-block-start`. */\nconst VERTICAL_CONNECTOR_LEFT = 14;\nconst VERTICAL_CONNECTOR_TOP = 30;\n\ninterface StepCircleProps {\n index: number;\n label?: string;\n isCompleted: boolean;\n isCurrent: boolean;\n onStepClick?: (index: number) => void;\n}\n\n/**\n * One step's circle — its own component (not inlined in the parent's\n * `.map()`) purely so each circle owns an independent pair of\n * `Animated.Value`s, the same \"each item animates itself\" shape\n * `AvatarRotator`'s `RotatorLayer` already established for a\n * variable-length, data-driven list.\n */\nconst StepCircle = ({ index, label, isCompleted, isCurrent, onStepClick }: StepCircleProps) => {\n const theme = useGnomeTheme();\n const contrast = useResolvedContrast();\n const clickable = Boolean(onStepClick) && isCompleted;\n\n // Two independent 0/1 progress values rather than one three-state value:\n // \"accented\" (border) is true for current OR completed, \"filled\"\n // (background) only for current — they change on different transitions\n // (upcoming→current flips both; current→completed flips only \"filled\").\n const accentProgress = useRef(new Animated.Value(isCompleted || isCurrent ? 1 : 0)).current;\n const fillProgress = useRef(new Animated.Value(isCurrent ? 1 : 0)).current;\n const isInitialMount = useRef(true);\n\n useEffect(() => {\n if (isInitialMount.current) {\n isInitialMount.current = false;\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n const easing = Easing.bezier(x1, y1, x2, y2);\n\n Animated.timing(accentProgress, {\n toValue: isCompleted || isCurrent ? 1 : 0,\n duration: theme.durationFast,\n easing,\n useNativeDriver: false,\n }).start();\n\n Animated.timing(fillProgress, {\n toValue: isCurrent ? 1 : 0,\n duration: theme.durationFast,\n easing,\n useNativeDriver: false,\n }).start();\n }, [\n isCompleted,\n isCurrent,\n accentProgress,\n fillProgress,\n theme.durationFast,\n theme.easingDefault,\n ]);\n\n const borderColor = accentProgress.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.borderSubtle, theme.accentBgColor],\n });\n const backgroundColor = fillProgress.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.viewBgColor, theme.accentBgColor],\n });\n\n const accessibilityLabel = label ?? `Step ${index + 1}`;\n\n const circleStyle: Animated.WithAnimatedValue<ViewStyle> = {\n width: CIRCLE_SIZE,\n height: CIRCLE_SIZE,\n flexShrink: 0,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusPill,\n borderWidth: contrast === 'more' ? 3 : 2,\n borderColor,\n backgroundColor,\n };\n\n const content = isCompleted ? (\n // `tintColor`, not `color`, since RN has no `currentColor` to inherit\n // from the circle the way the web icon does — same call BottomTabBar's\n // active-tab icon already made for tracking the *configurable* accent.\n <Icon icon={Check} size=\"sm\" tintColor={theme.accentBgColor} />\n ) : (\n <Text\n variant=\"caption\"\n style={{\n color: isCurrent ? '#fff' : theme.windowFgColor,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n }}\n >\n {index + 1}\n </Text>\n );\n\n if (clickable) {\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={accessibilityLabel}\n onPress={() => onStepClick?.(index)}\n >\n <Animated.View style={circleStyle}>{content}</Animated.View>\n </Pressable>\n );\n }\n\n return (\n <Animated.View\n accessible\n accessibilityState={{ selected: isCurrent }}\n accessibilityLabel={accessibilityLabel}\n style={circleStyle}\n >\n {content}\n </Animated.View>\n );\n};\n\n/**\n * Numbered \"Step X of Y\" progress indicator for onboarding/wizard flows —\n * mirrors `@gnome-ui/react`'s `StepIndicator`. Directly portable per this\n * package's own ROADMAP note: no web-only APIs are involved, just derived\n * dot/number state from `currentStep`.\n *\n * The outer container sets `role=\"navigation\"` **without** `accessible` —\n * the corrected pattern `ToggleGroup` established over the earlier\n * `BoxedList`/`ViewSwitcher` precedent: `accessible` on a *grouping*\n * container with multiple independently-focusable children (each step\n * circle here) collapses the whole subtree into one VoiceOver stop on iOS.\n * The role still groups on Android, and every step stays individually\n * reachable; assert `element.props.role` on a `testID` in tests instead of\n * `getByRole('navigation')`.\n *\n * The connecting line between circles reuses the exact CSS trick verbatim\n * (`position: absolute; left: 50%; width: 100%` inside each equal-width\n * flex item, so the line runs from one circle's center to the next's) —\n * `left`/`width` percentages are supported for RN position/dimension\n * props, unlike the `transform: translateX('50%')` trick this package has\n * hit real bugs with elsewhere (that limitation is specific to `transform`).\n *\n * The web version's checkmark/number content swap and connector-line color\n * change have no CSS `transition` at all (only `.circle`'s own\n * `background-color`/`border-color` do), so only those two colors animate\n * here — the content swap and connector recolor are instant, matching the\n * source exactly rather than adding an unrequested fade.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.StepIndicator.html\n */\nexport const StepIndicator = ({\n steps,\n currentStep,\n orientation = 'horizontal',\n onStepClick,\n label = 'Progress',\n style,\n testID,\n}: StepIndicatorProps) => {\n const theme = useGnomeTheme();\n const isVertical = orientation === 'vertical';\n\n const stepLabels = Array.isArray(steps) ? steps : undefined;\n const stepCount = Array.isArray(steps) ? steps.length : steps;\n const clampedCurrent = Math.max(0, Math.min(currentStep, stepCount - 1));\n\n return (\n <View\n testID={testID}\n role=\"navigation\"\n accessibilityLabel={label}\n style={[{ gap: theme.space2 }, style]}\n >\n {!stepLabels && (\n <Text\n variant=\"caption\"\n color=\"dim\"\n style={{ fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'] }}\n >\n {`Step ${clampedCurrent + 1} of ${stepCount}`}\n </Text>\n )}\n\n <View\n style={{\n flexDirection: isVertical ? 'column' : 'row',\n alignItems: isVertical ? 'stretch' : 'flex-start',\n }}\n >\n {Array.from({ length: stepCount }, (_, i) => {\n const isCompleted = i < clampedCurrent;\n const isCurrent = i === clampedCurrent;\n const isLast = i === stepCount - 1;\n const stepLabel = stepLabels?.[i];\n const connectorColor = isCompleted ? theme.accentBgColor : theme.borderSubtle;\n\n return (\n <View\n key={i}\n style={\n isVertical\n ? {\n position: 'relative',\n flexDirection: 'row',\n alignItems: 'flex-start',\n paddingBottom: isLast ? 0 : theme.space3,\n }\n : {\n position: 'relative',\n flex: 1,\n flexDirection: 'column',\n alignItems: 'center',\n gap: theme.space1,\n }\n }\n >\n {!isLast && (\n <View\n pointerEvents=\"none\"\n style={\n isVertical\n ? {\n position: 'absolute',\n left: VERTICAL_CONNECTOR_LEFT,\n top: VERTICAL_CONNECTOR_TOP,\n width: CONNECTOR_THICKNESS,\n height: '100%',\n backgroundColor: connectorColor,\n zIndex: -1,\n }\n : {\n position: 'absolute',\n left: '50%',\n top: HORIZONTAL_CONNECTOR_TOP,\n width: '100%',\n height: CONNECTOR_THICKNESS,\n backgroundColor: connectorColor,\n zIndex: -1,\n }\n }\n />\n )}\n\n <StepCircle\n index={i}\n label={stepLabel}\n isCompleted={isCompleted}\n isCurrent={isCurrent}\n onStepClick={onStepClick}\n />\n\n {stepLabel && (\n <Text\n variant=\"caption\"\n numberOfLines={isVertical ? undefined : 1}\n ellipsizeMode={isVertical ? undefined : 'tail'}\n style={[\n isVertical\n ? { paddingLeft: theme.space2, textAlign: 'left' as const }\n : { maxWidth: 96, textAlign: 'center' as const },\n isCurrent && {\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n },\n ]}\n >\n {stepLabel}\n </Text>\n )}\n </View>\n );\n })}\n </View>\n </View>\n );\n};\n","import { forwardRef, useEffect, useRef } from 'react';\nimport type { PressableProps, StyleProp, View, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable } from 'react-native';\n\nimport { useGnomeTheme, useResolvedColorScheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface SwitchProps extends Omit<PressableProps, 'children' | 'style' | 'onPress'> {\n /** Whether the switch is on. */\n value: boolean;\n /** Called with the next value when the switch is pressed. */\n onValueChange?: (value: boolean) => void;\n /** Accessible label. Required when no visible label is associated. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\nconst TRACK_WIDTH = 56;\nconst TRACK_HEIGHT = 28;\nconst THUMB_SIZE = 20;\nconst THUMB_INSET = 3;\n/** Matches `@gnome-ui/react`'s `.switch:checked::before { transform: translateX(28px) }`. */\nconst THUMB_TRAVEL = 28;\n\n/**\n * On/off toggle following the Adwaita switch style.\n *\n * Rebuilt on `Pressable`/`Animated.View` rather than ported from\n * `@gnome-ui/react`'s `<input type=\"checkbox\" role=\"switch\">`: RN has no\n * checkbox primitive to style, and the platform-supplied `Switch` can't be\n * skinned to match Adwaita, so the track and thumb are drawn by hand.\n * `value`/`onValueChange` (not `checked`/`onChange`) mirror RN's own\n * `Switch` API, which is the ecosystem convention this component overlaps\n * with — it's fully controlled, with no `defaultValue` escape hatch.\n *\n * Two of `Switch.module.css`'s colors are hardcoded per color scheme\n * inside a component-level `@media (prefers-color-scheme: dark)` block\n * rather than driven by a semantic token, so — unlike `Button`/`Link`/\n * `TextField`, which read a single token that already resolves correctly\n * per theme — the unchecked track/thumb colors here branch explicitly on\n * `useResolvedColorScheme()` to match. Track background/border color\n * animate (mirroring the CSS `transition` on `.switch`); the thumb's own\n * fill color does not, since the source CSS only transitions its\n * `transform`, not its `background-color`.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/switches.html\n */\nexport const Switch = forwardRef<View, SwitchProps>(function Switch(\n { value, onValueChange, disabled, style, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const colorScheme = useResolvedColorScheme();\n const contrast = useResolvedContrast();\n const progress = useRef(new Animated.Value(value ? 1 : 0)).current;\n const isInitialMount = useRef(true);\n\n useEffect(() => {\n // The initial `Animated.Value` above already matches `value` — skip\n // animating into a state the switch is already rendering, so it never\n // plays a transition before the user has touched it.\n if (isInitialMount.current) {\n isInitialMount.current = false;\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: value ? 1 : 0,\n duration: theme.durationFast,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: false,\n }).start();\n }, [value, progress, theme.durationFast, theme.easingDefault]);\n\n const trackColorOff = colorScheme === 'dark' ? theme.dark2 : theme.light4;\n const thumbColorOff = colorScheme === 'dark' ? theme.light3 : '#fff';\n\n const backgroundColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [trackColorOff, theme.accentBgColor],\n });\n const borderColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.borderSubtle, theme.accentBgColor],\n });\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"switch\"\n accessibilityState={{ checked: value, disabled: !!disabled }}\n onPress={() => onValueChange?.(!value)}\n style={[{ opacity: disabled ? theme.opacityDisabled : 1 }, style]}\n {...pressableProps}\n >\n <Animated.View\n style={{\n width: TRACK_WIDTH,\n height: TRACK_HEIGHT,\n borderRadius: theme.radiusPill,\n borderWidth: contrast === 'more' ? 2 : 1,\n borderColor,\n backgroundColor,\n }}\n >\n <Animated.View\n style={[\n {\n position: 'absolute',\n top: THUMB_INSET,\n left: THUMB_INSET,\n width: THUMB_SIZE,\n height: THUMB_SIZE,\n borderRadius: theme.radiusPill,\n backgroundColor: value ? '#fff' : thumbColorOff,\n transform: [\n {\n translateX: progress.interpolate({\n inputRange: [0, 1],\n outputRange: [0, THUMB_TRAVEL],\n }),\n },\n ],\n },\n contrast !== 'more' && {\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 1 },\n shadowOpacity: 0.25,\n shadowRadius: 3,\n elevation: 2,\n },\n ]}\n />\n </Animated.View>\n </Pressable>\n );\n});\n","import { forwardRef, type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { PressableProps, StyleProp, TextStyle, View, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable, View as RNView, StyleSheet } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useResolvedColorScheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface SwitchRowProps extends Omit<PressableProps, 'children' | 'style' | 'onPress'> {\n /** Primary label. */\n title: string;\n /** Secondary line below the title. */\n subtitle?: string;\n /** Icon or image placed at the leading edge. */\n leading?: ReactNode;\n /** Controlled checked state. */\n checked?: boolean;\n /** Initial checked state when uncontrolled. Defaults to `false`. */\n defaultChecked?: boolean;\n /** Called with the next value when the row is pressed. */\n onCheckedChange?: (checked: boolean) => void;\n style?: StyleProp<ViewStyle>;\n}\n\nconst TRACK_WIDTH = 56;\nconst TRACK_HEIGHT = 28;\nconst THUMB_SIZE = 20;\nconst THUMB_INSET = 3;\nconst THUMB_TRAVEL = 28;\n\n/**\n * Activatable row with an integrated switch, mirroring `@gnome-ui/react`'s\n * `SwitchRow`. The entire row is a single pressable — pressing anywhere\n * toggles the switch, which is why this isn't `ActionRow` + a trailing\n * `Switch` (the ROADMAP's own guess): `AdwSwitchRow` makes the whole row the\n * interactive element, the same shape `CheckRow` already established for\n * its checkbox. Prefer this over `CheckRow` for a single on/off setting,\n * and `CheckRow` for selecting/deselecting individual items in a list.\n *\n * The switch visual reuses `Switch`'s exact track/thumb `Animated.Value`\n * interpolation, but as plain non-interactive `Animated.View`s rather than\n * importing the real `Switch` component — `Switch` is itself a `Pressable`,\n * and nesting one touchable inside another (the row's own `Pressable`)\n * would create two overlapping tap targets, the same reasoning `CheckRow`\n * already applied to `Checkbox`. `aria-labelledby` (pointing the web\n * button's `role=\"switch\"` at the title/subtitle content) has no RN\n * equivalent — `accessibilityLabel` combining title and subtitle is the\n * substitution, same as `CheckRow`.\n *\n * Supports both controlled (`checked`) and uncontrolled (`defaultChecked`)\n * modes, the same `isControlled`/internal-state-fallback shape already\n * established by `Expander`/`ComboRow`/`Popover`/`CheckRow`.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.SwitchRow.html\n */\nexport const SwitchRow = forwardRef<View, SwitchRowProps>(function SwitchRow(\n {\n title,\n subtitle,\n leading,\n checked: controlledChecked,\n defaultChecked = false,\n onCheckedChange,\n disabled,\n style,\n ...pressableProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const colorScheme = useResolvedColorScheme();\n const contrast = useResolvedContrast();\n\n const isControlled = controlledChecked !== undefined;\n const [uncontrolledChecked, setUncontrolledChecked] = useState(defaultChecked);\n const checked = isControlled ? controlledChecked : uncontrolledChecked;\n\n const progress = useRef(new Animated.Value(checked ? 1 : 0)).current;\n const isInitialMount = useRef(true);\n\n useEffect(() => {\n if (isInitialMount.current) {\n isInitialMount.current = false;\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: checked ? 1 : 0,\n duration: theme.durationFast,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: false,\n }).start();\n }, [checked, progress, theme.durationFast, theme.easingDefault]);\n\n const trackColorOff = colorScheme === 'dark' ? theme.dark2 : theme.light4;\n const thumbColorOff = colorScheme === 'dark' ? theme.light3 : '#fff';\n\n const trackBackgroundColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [trackColorOff, theme.accentBgColor],\n });\n const trackBorderColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.borderSubtle, theme.accentBgColor],\n });\n\n const handlePress = () => {\n const next = !checked;\n\n if (!isControlled) {\n setUncontrolledChecked(next);\n }\n\n onCheckedChange?.(next);\n };\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"switch\"\n accessibilityState={{ checked, disabled: !!disabled }}\n accessibilityLabel={subtitle ? `${title}, ${subtitle}` : title}\n onPress={handlePress}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: theme.space2,\n paddingHorizontal: theme.space4,\n minHeight: 52,\n width: '100%',\n opacity: disabled ? theme.opacityDisabled : 1,\n },\n style,\n ]}\n {...pressableProps}\n >\n {({ pressed }) => (\n <>\n {leading && <RNView style={{ flexShrink: 0 }}>{leading}</RNView>}\n\n <RNView style={{ flex: 1, gap: 2 }}>\n <Text\n variant=\"body\"\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n color: theme.cardFgColor,\n fontWeight: String(theme.fontWeightNormal) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n {subtitle && (\n <Text variant=\"caption\" color=\"dim\" numberOfLines={1} ellipsizeMode=\"tail\">\n {subtitle}\n </Text>\n )}\n </RNView>\n\n <Animated.View\n style={{\n width: TRACK_WIDTH,\n height: TRACK_HEIGHT,\n flexShrink: 0,\n borderRadius: theme.radiusPill,\n borderWidth: contrast === 'more' ? 2 : 1,\n borderColor: trackBorderColor,\n backgroundColor: trackBackgroundColor,\n }}\n >\n <Animated.View\n style={[\n {\n position: 'absolute',\n top: THUMB_INSET,\n left: THUMB_INSET,\n width: THUMB_SIZE,\n height: THUMB_SIZE,\n borderRadius: theme.radiusPill,\n backgroundColor: checked ? '#fff' : thumbColorOff,\n transform: [\n {\n translateX: progress.interpolate({\n inputRange: [0, 1],\n outputRange: [0, THUMB_TRAVEL],\n }),\n },\n ],\n },\n contrast !== 'more' && {\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 1 },\n shadowOpacity: 0.25,\n shadowRadius: 3,\n elevation: 2,\n },\n ]}\n />\n </Animated.View>\n\n {pressed && !disabled ? (\n <RNView\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { backgroundColor: theme.activeOverlay }]}\n />\n ) : null}\n </>\n )}\n </Pressable>\n );\n});\n","import { forwardRef, type ReactNode } from 'react';\nimport type { ScrollView, ScrollViewProps, StyleProp, ViewStyle } from 'react-native';\nimport { ScrollView as RNScrollView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface TabBarProps extends Omit<ScrollViewProps, 'style' | 'horizontal' | 'children'> {\n children?: ReactNode;\n /** Accessible label for the tab list. */\n accessibilityLabel?: string;\n /**\n * Removes the header-bar background so the tab bar blends into any surface.\n * Use when placing the bar inside a card, content area, or custom container.\n * Mirrors the `.inline` style class.\n */\n inline?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Horizontal, horizontally-scrollable tab bar that holds `TabItem` elements.\n *\n * Rebuilt on a horizontal `ScrollView` rather than ported from\n * `@gnome-ui/react`'s `overflow-x: auto` `<div role=\"tablist\">` — RN's\n * `AccessibilityRole` union does include `\"tablist\"`, so that semantic\n * carries over directly, but the web version's roving-tabindex ← → / Home /\n * End keyboard navigation doesn't: RN's touch-first focus model has no\n * per-app arrow-key convention to port, so it's dropped here rather than\n * faking it.\n *\n * `style={{ flexGrow: 0 }}` on the `ScrollView` is deliberate, not\n * decorative — a horizontal `ScrollView` with no explicit `flexGrow` can\n * stretch to fill the cross-axis of whatever flex column it's placed in\n * (confirmed the hard way debugging the example app's own tab-like\n * `ControlsBar`), so it's baked in here once rather than left for every\n * consumer to rediscover.\n *\n * @see https://developer.gnome.org/hig/patterns/nav/tabs.html\n */\nexport const TabBar = forwardRef<ScrollView, TabBarProps>(function TabBar(\n { children, inline = false, accessibilityLabel = 'Tabs', style, ...scrollViewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <RNScrollView\n ref={ref}\n horizontal\n showsHorizontalScrollIndicator={false}\n contentInsetAdjustmentBehavior=\"never\"\n accessible\n accessibilityRole=\"tablist\"\n accessibilityLabel={accessibilityLabel}\n style={[\n {\n flexGrow: 0,\n backgroundColor: inline ? 'transparent' : theme.headerbarBgColor,\n borderBottomWidth: inline ? 0 : 1,\n borderBottomColor: theme.headerbarBorderColor,\n },\n style,\n ]}\n contentContainerStyle={{\n flexDirection: 'row',\n alignItems: 'stretch',\n paddingHorizontal: theme.space1,\n gap: 2,\n minHeight: 40,\n }}\n {...scrollViewProps}\n >\n {children}\n </RNScrollView>\n );\n});\n","import { forwardRef, type ReactNode } from 'react';\nimport type { PressableProps, TextStyle, View } from 'react-native';\nimport { Pressable, Text as RNText, View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface TabItemProps extends Omit<PressableProps, 'children' | 'style' | 'disabled'> {\n /** Tab label. */\n label: string;\n /**\n * Icon placed before the label. Rendered as-is — size/color it yourself.\n * Web's `icon` takes an `@gnome-ui/icons` `IconDefinition`; this package\n * has no SVG icon system (see [[react-native-port-status]]), so it's a\n * plain `ReactNode` here instead, same as `ActionRow`'s `leading`.\n */\n icon?: ReactNode;\n /** Marks this tab as the currently selected one. */\n active?: boolean;\n /**\n * When provided, a close (×) button is rendered at the trailing edge.\n * Called when the user taps the close button.\n */\n onClose?: () => void;\n /** Accessible label for the close button. Defaults to `\"Close tab\"`. */\n closeLabel?: string;\n /** Optional count shown as a small badge next to the label. Values above 99 render as \"99+\". */\n count?: number;\n disabled?: boolean;\n}\n\n/**\n * Individual tab button inside a `TabBar`.\n *\n * Web's `panelId` (which sets `aria-controls`) is dropped — RN has no ARIA\n * relationship attributes, so there's nothing for it to wire up; keep the\n * tab ↔ panel link in your own state instead. The count badge is a small\n * inline pill rather than the web version's separate `Badge` component,\n * since `Badge` hasn't been ported to this package yet and pulling in a\n * whole new component just for this one pill would be scope creep.\n *\n * The close button is a nested `Pressable` inside the tab's own\n * `Pressable` — unlike the web version, no explicit \"stop propagation\" is\n * needed: RN's touch responder system grants an in-progress touch to the\n * innermost view that claims it, so pressing the close button never also\n * fires the outer tab's `onPress`. It also gets a `hitSlop` the web\n * version has no equivalent of, since its 18×18 visual size is well under\n * the ~44pt touch target guidance both major mobile platforms recommend.\n *\n * The label `Text` deliberately has no `flex: 1` (unlike the web version's\n * `.tabLabel`) — the outer `Pressable`'s width here is content-driven\n * (`minWidth`/`maxWidth`, not a definite width), and giving a `flex: 1`\n * child to a row whose own width isn't definite collapses that child to a\n * near-zero width in Yoga (confirmed visually: every label truncated to\n * 3-4 characters despite ample room). CSS flexbox resolves this fine\n * because browsers can grow a flex child within an intrinsically-sized\n * container; Yoga can't. `numberOfLines`/`ellipsizeMode` alone are enough\n * to truncate genuinely-too-long labels against `maxWidth`.\n */\nexport const TabItem = forwardRef<View, TabItemProps>(function TabItem(\n {\n label,\n icon,\n active = false,\n onClose,\n closeLabel = 'Close tab',\n count,\n disabled,\n ...pressableProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"tab\"\n accessibilityState={{ selected: active, disabled: !!disabled }}\n accessibilityLabel={label}\n style={({ pressed }) => ({\n position: 'relative',\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space1,\n paddingHorizontal: theme.space2,\n minWidth: 80,\n maxWidth: 200,\n minHeight: 40,\n borderRadius: theme.radiusMd,\n opacity: disabled ? theme.opacityDisabled : active ? 1 : 0.7,\n backgroundColor: active\n ? theme.activeOverlay\n : pressed\n ? theme.hoverOverlay\n : 'transparent',\n })}\n {...pressableProps}\n >\n {icon && <RNView style={{ flexShrink: 0 }}>{icon}</RNView>}\n\n <RNText\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(\n active ? theme.fontWeightSemibold : theme.fontWeightNormal,\n ) as TextStyle['fontWeight'],\n color: theme.headerbarFgColor,\n }}\n >\n {label}\n </RNText>\n\n {count !== undefined && (\n <RNView\n style={{\n flexShrink: 0,\n minWidth: 18,\n height: 18,\n paddingHorizontal: 5,\n borderRadius: 9,\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: theme.accentBgColor,\n }}\n >\n <RNText\n style={{\n fontFamily: theme.fontFamily,\n fontSize: 11,\n fontWeight: '600' as TextStyle['fontWeight'],\n color: theme.accentFgColor,\n }}\n >\n {count > 99 ? '99+' : count}\n </RNText>\n </RNView>\n )}\n\n {onClose && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={closeLabel}\n hitSlop={8}\n onPress={onClose}\n style={({ pressed }) => ({\n flexShrink: 0,\n width: 18,\n height: 18,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusSm,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n })}\n >\n <RNText\n style={{\n fontFamily: theme.fontFamily,\n fontSize: 15,\n lineHeight: 16,\n color: theme.headerbarFgColor,\n }}\n >\n {'×'}\n </RNText>\n </Pressable>\n )}\n\n {active && (\n <RNView\n pointerEvents=\"none\"\n style={{\n position: 'absolute',\n bottom: 0,\n left: theme.space2,\n right: theme.space2,\n height: 2,\n borderRadius: 1,\n backgroundColor: theme.accentBgColor,\n }}\n />\n )}\n </Pressable>\n );\n});\n","import { forwardRef, type ReactNode } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nexport interface TabPanelProps extends Omit<ViewProps, 'style'> {\n /** Controls whether this panel is visible. */\n active?: boolean;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Content panel associated with a `TabItem`.\n *\n * Web's `id` (paired with `TabItem`'s `panelId`, together wiring up\n * `aria-controls`) is dropped on both sides here — RN has no ARIA\n * relationship attributes for it to drive, so track which tab is active in\n * your own state (index, key, whatever) and pass the matching panel\n * `active={true}` directly, rather than reaching for an id-matching\n * convention with nothing underneath it.\n *\n * Hidden panels stay mounted with `display: 'none'` rather than being\n * conditionally unmounted — same as the web version keeping them in the\n * DOM via the `hidden` attribute — so a panel's internal state (scroll\n * position, form input, etc.) survives switching tabs away and back. RN\n * has no `\"tabpanel\"` value in its `AccessibilityRole` union (unlike\n * `TabBar`'s `\"tablist\"` and `TabItem`'s `\"tab\"`, both of which do exist),\n * so no role is set here — same reasoning as `Separator` dropping a role\n * RN doesn't have rather than reaching for an inaccurate substitute.\n */\nexport const TabPanel = forwardRef<View, TabPanelProps>(function TabPanel(\n { active = false, style, children, ...viewProps },\n ref,\n) {\n return (\n <RNView ref={ref} style={[{ display: active ? 'flex' : 'none' }, style]} {...viewProps}>\n {children}\n </RNView>\n );\n});\n","import { useRef, useState } from 'react';\nimport type {\n NativeSyntheticEvent,\n StyleProp,\n TextInput,\n TextInputKeyPressEventData,\n TextStyle,\n ViewStyle,\n} from 'react-native';\nimport { Pressable, TextInput as RNTextInput, View } from 'react-native';\n\nimport { Chip } from '@/components/Chip';\nimport { Text } from '@/components/Text';\nimport { WrapBox } from '@/components/WrapBox';\nimport { useGnomeTheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface TagInputProps {\n /** Current list of tags. */\n value: string[];\n /** Called when a tag is added or removed. */\n onChange: (value: string[]) => void;\n /** Visible label rendered above the input. */\n label?: string;\n /** Placeholder shown in the draft input while empty. */\n placeholder?: string;\n /** Helper text rendered below the input. Hidden when `error` is set. */\n helperText?: string;\n /**\n * Error message rendered below the input in place of `helperText`.\n * Also applies the error visual state to the border.\n */\n error?: string;\n /** Maximum number of tags allowed. Once reached, the draft input is hidden. */\n maxTags?: number;\n /** Reject a new tag that already exists (case-insensitive). Defaults to `true`. */\n preventDuplicates?: boolean;\n /** Disables the whole control. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/** Duplicated from `TextField.tsx` — see that component for the full reasoning. */\nfunction dimColor(color: string, factor: number): string {\n const match = color.match(/^rgba?\\(([^,]+),([^,]+),([^,]+),?([^)]+)?\\)$/);\n\n if (!match) {\n return color;\n }\n\n const [, r, g, b, a] = match;\n const alpha = (a ? Number.parseFloat(a) : 1) * factor;\n\n return `rgba(${r.trim()}, ${g.trim()}, ${b.trim()}, ${alpha})`;\n}\n\n/**\n * Type-to-add multi-value input rendering entries as removable `Chip`s in a\n * `WrapBox`, mirroring `@gnome-ui/react`'s own `TagInput`.\n *\n * `WrapBox`/`Chip` alone only support static, pre-populated display —\n * `TagInput` adds interactive entry: type and press Return, or type a `,`,\n * to commit a tag; paste a comma/newline-separated list to add several at\n * once; Backspace on an empty draft removes the last one.\n *\n * The web version wires typed-`,` and pasted-list handling as two separate\n * handlers (`onKeyDown`'s `,` case, `onPaste`). RN's `TextInput` has no\n * `paste` event to mirror — but a paste still flows through `onChangeText`\n * with the full resulting text, exactly like a typed `,` does, so both\n * collapse into one `handleChangeText`: whenever the text contains a `,` or\n * newline, split on it and commit every non-empty part (typing \"foo,\"\n * commits \"foo\" via the same path a paste of \"foo,bar\" commits both).\n * Return is handled separately via `onSubmitEditing`, since RN has no\n * `Enter` keystroke to catch on a single-line field the way the web version\n * catches it in `onKeyDown`. Backspace-on-empty uses `onKeyPress`, the one\n * RN `TextInput` event that still fires with the field already empty\n * (`onChangeText` doesn't fire deleting from nothing).\n *\n * The tag box is a `Pressable` (mirrors the web version's\n * `onClick={() => inputRef.current?.focus()}` on the container) with\n * `accessible={false}` explicitly — `Pressable` defaults `accessible` to\n * `true`, which would collapse every `Chip`'s remove button and the draft\n * input into a single VoiceOver stop, the same container-swallows-subtree\n * trap already documented for `ToggleGroup`/`Sidebar`'s bare-`View`-plus-role\n * case, here hit via `Pressable`'s own default instead.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/text-fields.html\n */\nexport const TagInput = ({\n value,\n onChange,\n label,\n placeholder,\n helperText,\n error,\n maxTags,\n preventDuplicates = true,\n disabled = false,\n style,\n testID,\n}: TagInputProps) => {\n const theme = useGnomeTheme();\n const contrast = useResolvedContrast();\n const [draft, setDraft] = useState('');\n const [focused, setFocused] = useState(false);\n const inputRef = useRef<TextInput>(null);\n\n const atMax = maxTags !== undefined && value.length >= maxTags;\n const hint = error ?? helperText;\n\n const isDuplicate = (tag: string) =>\n preventDuplicates && value.some((v) => v.toLowerCase() === tag.toLowerCase());\n\n const commit = (raw: string) => {\n const tag = raw.trim();\n\n setDraft('');\n\n if (!tag || atMax || isDuplicate(tag)) {\n return;\n }\n\n onChange([...value, tag]);\n };\n\n const handleChangeText = (text: string) => {\n if (!text.includes(',') && !text.includes('\\n')) {\n setDraft(text);\n\n return;\n }\n\n const next = [...value];\n\n for (const part of text.split(/[,\\n]/)) {\n const tag = part.trim();\n\n if (!tag || (maxTags !== undefined && next.length >= maxTags)) {\n continue;\n }\n if (preventDuplicates && next.some((v) => v.toLowerCase() === tag.toLowerCase())) {\n continue;\n }\n next.push(tag);\n }\n\n setDraft('');\n onChange(next);\n };\n\n const handleKeyPress = (e: NativeSyntheticEvent<TextInputKeyPressEventData>) => {\n if (e.nativeEvent.key === 'Backspace' && draft === '' && value.length > 0) {\n onChange(value.slice(0, -1));\n }\n };\n\n const removeAt = (index: number) => {\n onChange(value.filter((_, i) => i !== index));\n };\n\n return (\n <View\n testID={testID}\n style={[{ gap: theme.space1, opacity: disabled ? theme.opacityDisabled : 1 }, style]}\n >\n {label && (\n <Text\n variant=\"body\"\n style={{ fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'] }}\n >\n {label}\n </Text>\n )}\n\n <Pressable\n accessible={false}\n disabled={disabled}\n onPress={() => inputRef.current?.focus()}\n style={{\n width: '100%',\n minHeight: 40,\n paddingVertical: 6,\n paddingHorizontal: theme.space2,\n backgroundColor: theme.viewBgColor,\n borderRadius: theme.radiusMd,\n borderWidth: contrast === 'more' ? 2 : 1,\n borderColor: error ? theme.errorColor : focused ? theme.accentColor : theme.borderSubtle,\n }}\n >\n <WrapBox childSpacing={6}>\n {value.map((tag, i) => (\n <Chip\n key={`${tag}-${i}`}\n label={tag}\n disabled={disabled}\n onRemove={disabled ? undefined : () => removeAt(i)}\n />\n ))}\n\n {!atMax && (\n <RNTextInput\n ref={inputRef}\n value={draft}\n editable={!disabled}\n placeholder={placeholder}\n placeholderTextColor={dimColor(theme.windowFgColor, theme.opacityDim)}\n accessibilityLabel={label}\n accessibilityHint={hint}\n onChangeText={handleChangeText}\n onSubmitEditing={() => commit(draft)}\n onKeyPress={handleKeyPress}\n onFocus={() => setFocused(true)}\n onBlur={() => setFocused(false)}\n style={{\n flexGrow: 1,\n minWidth: 80,\n paddingVertical: 2,\n paddingHorizontal: 4,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n color: theme.viewFgColor,\n }}\n />\n )}\n </WrapBox>\n </Pressable>\n\n {hint && (\n <Text\n variant=\"caption\"\n style={{\n color: error ? theme.errorColor : theme.windowFgColor,\n opacity: error ? 1 : theme.opacityDim,\n }}\n >\n {hint}\n </Text>\n )}\n </View>\n );\n};\n","import { forwardRef, useState } from 'react';\nimport type { StyleProp, TextInput, TextInputProps, TextStyle, ViewStyle } from 'react-native';\nimport { TextInput as RNTextInput, View } from 'react-native';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface TextFieldProps extends Omit<TextInputProps, 'style'> {\n /** Visible label rendered above the input. */\n label?: string;\n /** Helper text rendered below the input. Hidden when `error` is set. */\n helperText?: string;\n /**\n * Error message rendered below the input in place of `helperText`.\n * Also applies the error visual state to the input border.\n */\n error?: string;\n /** Style applied to the wrapping `View`. */\n style?: StyleProp<ViewStyle>;\n /** Style applied to the `TextInput` itself. */\n inputStyle?: StyleProp<TextStyle>;\n}\n\n/**\n * Multiplies an `rgba(r, g, b, a)` token's alpha channel. Tokens that\n * aren't `rgba()` (e.g. `windowFgColor` in some variants resolves to a flat\n * hex) are returned unchanged — good enough here since this is only ever\n * used to dim `windowFgColor`, which the generated theme always emits as\n * `rgba()`.\n */\nfunction dimColor(color: string, factor: number): string {\n const match = color.match(/^rgba?\\(([^,]+),([^,]+),([^,]+),?([^)]+)?\\)$/);\n\n if (!match) {\n return color;\n }\n\n const [, r, g, b, a] = match;\n const alpha = (a ? Number.parseFloat(a) : 1) * factor;\n\n return `rgba(${r.trim()}, ${g.trim()}, ${b.trim()}, ${alpha})`;\n}\n\n/**\n * Single-line text input with label, helper text, and error state.\n *\n * Follows the Adwaita `GtkEntry` / `.entry` style class.\n *\n * Rebuilt on RN's `TextInput` rather than ported from `@gnome-ui/react`'s\n * `<input>`: there's no `<label htmlFor>`/`aria-describedby` pairing on RN,\n * so `label` doubles as `accessibilityLabel` and `error`/`helperText`\n * double as `accessibilityHint` — announced together with the input the\n * same way `aria-describedby` reads them on the web. `:focus-visible`'s\n * accent border becomes plain `onFocus`/`onBlur` state — RN has no\n * hover/keyboard-vs-pointer focus distinction to mirror, and no outer\n * `box-shadow` ring, so focus is just a border-color change rather than a\n * grown ring. The web version's `disabled` prop is RN's own\n * `editable={false}` — mirrored here as a dimmed wrapper the same way\n * `@gnome-ui/react`'s `.disabled` class dims the label and hint alongside\n * the input.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/text-fields.html\n */\nexport const TextField = forwardRef<TextInput, TextFieldProps>(function TextField(\n {\n label,\n helperText,\n error,\n style,\n inputStyle,\n editable = true,\n onFocus,\n onBlur,\n accessibilityLabel,\n accessibilityHint,\n ...inputProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const contrast = useResolvedContrast();\n const [focused, setFocused] = useState(false);\n const disabled = !editable;\n const hint = error ?? helperText;\n\n const borderColor = error ? theme.errorColor : focused ? theme.accentColor : theme.borderSubtle;\n\n return (\n <View style={[{ gap: theme.space1, opacity: disabled ? theme.opacityDisabled : 1 }, style]}>\n {label && (\n <Text\n variant=\"body\"\n style={{ fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'] }}\n >\n {label}\n </Text>\n )}\n\n <RNTextInput\n ref={ref}\n editable={editable}\n accessibilityLabel={accessibilityLabel ?? label}\n accessibilityHint={accessibilityHint ?? hint}\n placeholderTextColor={dimColor(theme.windowFgColor, theme.opacityDim)}\n onFocus={(event) => {\n setFocused(true);\n onFocus?.(event);\n }}\n onBlur={(event) => {\n setFocused(false);\n onBlur?.(event);\n }}\n style={[\n {\n width: '100%',\n paddingVertical: 8,\n paddingHorizontal: theme.space2,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n lineHeight: Math.round(theme.fontSizeBody * theme.lineHeightBody),\n color: theme.viewFgColor,\n backgroundColor: theme.viewBgColor,\n borderRadius: theme.radiusMd,\n borderWidth: contrast === 'more' ? 2 : 1,\n borderColor,\n } satisfies TextStyle,\n inputStyle,\n ]}\n {...inputProps}\n />\n\n {hint && (\n <Text\n variant=\"caption\"\n style={{\n color: error ? theme.errorColor : theme.windowFgColor,\n opacity: error ? 1 : theme.opacityDim,\n }}\n >\n {hint}\n </Text>\n )}\n </View>\n );\n});\n","import type { ReactNode } from 'react';\nimport { useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, ViewStyle } from 'react-native';\nimport { ScrollView, View } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type TimelineOrientation = 'vertical' | 'horizontal';\nexport type TimelineVariant = 'default' | 'dotted' | 'none';\n\nexport interface TimelineItem {\n /** Icon rendered inside the timeline node. Sized/colored by the consumer — see the component doc. */\n icon?: ReactNode;\n /**\n * Content at the leading edge of the node.\n * - **Vertical:** rendered to the left of the connector track.\n * - **Horizontal:** rendered above the node.\n *\n * Typical use: timestamp, badge, short label.\n */\n leading?: ReactNode;\n /** Main event content rendered adjacent to the node (title, description…). */\n content: ReactNode;\n}\n\nexport interface TimelineProps {\n /** Ordered list of timeline events. */\n items: TimelineItem[];\n /**\n * Axis direction of the connector line.\n * - `\"vertical\"` — events stack top-to-bottom (default).\n * - `\"horizontal\"` — events flow left-to-right, in a horizontal `ScrollView`.\n */\n orientation?: TimelineOrientation;\n /**\n * Visual style of the connector between nodes.\n * - `\"default\"` — solid thin line.\n * - `\"dotted\"` — dotted line; useful for future or pending events.\n * - `\"none\"` — no connector; each node stands alone.\n */\n variant?: TimelineVariant;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst NODE_DOT_SIZE = 12;\nconst NODE_ICON_SIZE = 28;\nconst LINE_THICKNESS = 2;\nconst VERTICAL_MIN_LINE_LENGTH = 16;\n/** Cross-dimension of the dotted line's box — see `lineStyle`'s doc comment. */\nconst DOTTED_LINE_CROSS_SIZE = 6;\n/** Matches `@gnome-ui/react`'s `grid-template-columns: auto 24px 1fr` (fixed track width). */\nconst VERTICAL_NODE_TRACK_WIDTH = 24;\n/** No fixed value in the source CSS (an `auto` grid row) — the larger of the two node sizes. */\nconst HORIZONTAL_NODE_TRACK_HEIGHT = NODE_ICON_SIZE;\n/** Matches the source CSS's literal (non-tokenized) `padding-bottom: 20px`. */\nconst VERTICAL_ITEM_GAP = 20;\n/** Matches `grid-auto-columns: minmax(72px, 1fr)` — the `1fr` growth half doesn't\n * port (see the component doc), only the `72px` floor. */\nconst HORIZONTAL_MIN_ITEM_WIDTH = 72;\nconst HORIZONTAL_ITEM_GAP = 8;\n\n/**\n * Ordered sequence of events connected by a visual timeline — mirrors\n * `@gnome-ui/react`'s `Timeline`. An original composition (no direct\n * libadwaita widget), following GNOME HIG activity-feed/stepper patterns.\n *\n * The web version aligns every item's `leading` column (vertical) or row\n * (horizontal) via CSS subgrid, so timestamps/labels line up across items\n * regardless of how wide/tall any single one of them is. RN/Yoga has no\n * grid or subgrid at all, so that alignment is reproduced by measurement\n * instead — the same `onLayout` + `Record<index, size>` +\n * \"largest-so-far wins\" technique `Slider`'s mark labels already\n * established: every item's `leading` cell (always rendered, even when\n * empty, matching the web version's own \"same 3 children\" comment)\n * reports its own rendered width (vertical) or height (horizontal), and\n * every cell gets a `minWidth`/`minHeight` equal to the largest one\n * measured so far, so the node column/row starts at the same position in\n * every item. The node track itself additionally gets a *fixed*\n * width (vertical, matching the source CSS's literal `24px` column) or\n * height (horizontal, the larger of the dot/icon node sizes, since the\n * source's row there is CSS `auto` with no fixed value to port) — without\n * it, an item with an icon node (28 dp) and one with a plain dot (12 dp)\n * would each size their own track differently, misaligning `content`'s\n * start position between them.\n *\n * `orientation=\"horizontal\"` wraps itself in a horizontal `ScrollView`,\n * reimagining the web CSS's `overflow-x: auto` — the direct native\n * equivalent for \"scroll instead of overflow when items don't fit\". The\n * web version's `grid-auto-columns: minmax(72px, 1fr)` also grows items\n * to fill leftover space when the row *doesn't* overflow; that half\n * doesn't port (a `ScrollView`'s content isn't bounded the way a CSS grid\n * track is, so there's no \"leftover space\" to distribute) — each item\n * just gets a flat 72 dp `minWidth` instead, unconditionally scrollable\n * like `overflow-x: auto`'s own fallback path.\n *\n * `icon`/`leading`/`content` are plain `ReactNode`, the same as\n * `PathBar`'s segment `icon` — the consumer sizes and colors their own\n * icon (e.g. `tintColor={theme.accentFgColor}` on their own `<Icon>`),\n * since RN has no `currentColor` for this component to tint an arbitrary\n * child with the way the web version's CSS `color: accent-fg-color`\n * does via inheritance onto the icon's SVG.\n *\n * `role=\"list\"`/`role=\"listitem\"` are set **without** `accessible` — the\n * `ToggleGroup`/`StepIndicator`-established pattern for a grouping role\n * over children that may themselves contain focusable content (an\n * item's `content` is arbitrary `ReactNode` and could include one), so\n * nothing nested inside gets swallowed into a single VoiceOver stop.\n * Assert `element.props.role` on a `testID` in tests instead of\n * `getByRole('list'/'listitem')`.\n */\nexport const Timeline = ({\n items,\n orientation = 'vertical',\n variant = 'default',\n style,\n testID,\n}: TimelineProps) => {\n const theme = useGnomeTheme();\n const horizontal = orientation === 'horizontal';\n const showLine = variant !== 'none';\n const dotted = variant === 'dotted';\n\n const [leadingExtents, setLeadingExtents] = useState<Record<number, number>>({});\n const maxLeadingExtent = Object.values(leadingExtents).reduce((max, v) => Math.max(max, v), 0);\n\n const handleLeadingLayout = (index: number) => (event: LayoutChangeEvent) => {\n const size = horizontal ? event.nativeEvent.layout.height : event.nativeEvent.layout.width;\n\n setLeadingExtents((prev) => (prev[index] === size ? prev : { ...prev, [index]: size }));\n };\n\n const lineStyle = (hidden: boolean): StyleProp<ViewStyle> => {\n // The web CSS draws the dotted line via a single `border-left`/`border-top`\n // (zero-width box, one bordered side) — confirmed on-device (iOS Simulator,\n // saturated debug colors) that RN's `borderStyle: 'dotted'` renders nothing\n // at all unless every side shares the same width AND color (even three\n // sides at `borderWidth: 0` or `borderColor: 'transparent'` suppresses it\n // entirely). Reimagined as a narrow box with a *uniform* dotted border on\n // all four sides instead — visually indistinguishable from a single\n // dotted line at this thickness, and the only combination that actually\n // renders. **Any future dotted/dashed RN border needs all sides\n // width+color-uniform — a single-side border in that style silently\n // renders invisible, no error.**\n const base: ViewStyle = dotted\n ? horizontal\n ? {\n flex: 1,\n height: DOTTED_LINE_CROSS_SIZE,\n borderWidth: LINE_THICKNESS,\n borderStyle: 'dotted',\n borderColor: theme.borderSubtle,\n }\n : {\n flex: 1,\n width: DOTTED_LINE_CROSS_SIZE,\n minHeight: VERTICAL_MIN_LINE_LENGTH,\n borderWidth: LINE_THICKNESS,\n borderStyle: 'dotted',\n borderColor: theme.borderSubtle,\n }\n : horizontal\n ? { flex: 1, height: LINE_THICKNESS, backgroundColor: theme.borderSubtle }\n : {\n flex: 1,\n width: LINE_THICKNESS,\n minHeight: VERTICAL_MIN_LINE_LENGTH,\n backgroundColor: theme.borderSubtle,\n };\n\n return hidden || !showLine ? { ...base, opacity: 0 } : base;\n };\n\n const renderedItems = items.map((item, i) => {\n const isFirst = i === 0;\n const isLast = i === items.length - 1;\n const hasIcon = Boolean(item.icon);\n\n const node = (\n <View\n style={{\n width: hasIcon ? NODE_ICON_SIZE : NODE_DOT_SIZE,\n height: hasIcon ? NODE_ICON_SIZE : NODE_DOT_SIZE,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.accentBgColor,\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n }}\n >\n {item.icon}\n </View>\n );\n\n const leadingCell = (\n <View\n onLayout={handleLeadingLayout(i)}\n style={\n horizontal\n ? {\n minHeight: maxLeadingExtent,\n justifyContent: 'flex-end',\n alignItems: 'center',\n paddingBottom: theme.space2,\n }\n : {\n minWidth: maxLeadingExtent,\n flexShrink: 0,\n alignSelf: 'flex-start',\n alignItems: 'flex-end',\n paddingTop: 4,\n }\n }\n >\n {item.leading}\n </View>\n );\n\n const nodeTrack = horizontal ? (\n <View\n style={{\n height: HORIZONTAL_NODE_TRACK_HEIGHT,\n flexDirection: 'row',\n alignItems: 'center',\n width: '100%',\n }}\n >\n <View style={lineStyle(isFirst)} />\n {node}\n <View style={lineStyle(isLast)} />\n </View>\n ) : (\n <View\n style={{ width: VERTICAL_NODE_TRACK_WIDTH, flexShrink: 0, alignItems: 'center', flex: 1 }}\n >\n {node}\n {!isLast && showLine && <View style={lineStyle(false)} />}\n </View>\n );\n\n const contentCell = (\n <View\n style={\n horizontal\n ? { alignItems: 'center', paddingTop: theme.space2 }\n : {\n flex: 1,\n minWidth: 0,\n alignSelf: 'flex-start',\n paddingTop: 2,\n paddingBottom: isLast ? 0 : VERTICAL_ITEM_GAP,\n }\n }\n >\n {item.content}\n </View>\n );\n\n return (\n <View\n key={i}\n testID={testID ? `${testID}-item-${i}` : undefined}\n role=\"listitem\"\n style={\n horizontal\n ? { minWidth: HORIZONTAL_MIN_ITEM_WIDTH, marginRight: isLast ? 0 : HORIZONTAL_ITEM_GAP }\n : { flexDirection: 'row', columnGap: theme.space2 }\n }\n >\n {leadingCell}\n {nodeTrack}\n {contentCell}\n </View>\n );\n });\n\n if (horizontal) {\n return (\n <ScrollView\n testID={testID}\n role=\"list\"\n horizontal\n showsHorizontalScrollIndicator={false}\n style={style}\n contentContainerStyle={{ flexDirection: 'row' }}\n >\n {renderedItems}\n </ScrollView>\n );\n }\n\n return (\n <View testID={testID} role=\"list\" style={style}>\n {renderedItems}\n </View>\n );\n};\n","import { type ReactNode, useCallback, useEffect, useRef } from 'react';\nimport type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable, Text as RNText } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\n\n/**\n * `Animated`'s style interpolation only takes effect on `Animated.*`\n * host components — a plain `Pressable` would silently ignore the\n * animated `opacity`/`transform` in its style. Hoisted to module scope so\n * it isn't recreated on every render.\n */\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable);\n\nexport interface ToastProps extends Omit<ViewProps, 'style'> {\n /** The notification message. Keep it short — one sentence. */\n title: ReactNode;\n /**\n * Auto-dismiss timeout in milliseconds.\n * - Set to `0` to disable auto-dismiss (user must press action or dismiss).\n * - Defaults to `3000`.\n */\n duration?: number;\n /** Called when the toast should be removed — after timeout or user action. */\n onDismiss?: () => void;\n /** Label for the optional action button. */\n actionLabel?: string;\n /** Called when the user presses the action button. */\n onAction?: () => void;\n /** Whether to show a manual dismiss (×) button. */\n dismissible?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Non-blocking temporary notification following the Adwaita `AdwToast`\n * pattern. Auto-dismisses after `duration` ms (default 3s, `0` disables\n * it); the timer pauses while the toast is pressed and held. Use inside\n * `Toaster` for correct positioning and stacking.\n *\n * The auto-dismiss/pause timer logic (`timerRef`/`remainingRef`/\n * `startedAtRef`) is ported verbatim from `@gnome-ui/react` — it's plain\n * `setTimeout`/`Date.now()` bookkeeping, no DOM API involved, so nothing\n * platform-specific to change. The pause trigger is: RN has no hover, so\n * `onPressIn`/`onPressOut` (touch-down/touch-up) stand in for the web\n * version's `onMouseEnter`/`onMouseLeave` — pausing while the user is\n * actively touching the toast, the closest RN analog to \"giving the user\n * time to read it.\" `onFocus`/`onBlur`-triggered pausing (keyboard\n * accessibility on web) has no port here — the outer card itself isn't a\n * focusable element in RN's touch-first model, only its action/dismiss\n * buttons are, and RN has no \"focus-within\" primitive to detect that\n * without an extra dependency.\n *\n * The entrance is an `Animated.timing` fading + sliding + scaling in\n * (`opacity`/`translateY`/`scale`, all transform-safe for\n * `useNativeDriver: true`), matching the web version's `@keyframes\n * toast-in`; `useReducedMotion()` skips straight to the settled state,\n * matching the source CSS's `animation: none`. There's no exit animation\n * to port either — the web version doesn't define one, removal is\n * immediate once the consumer drops the `Toast` from its list.\n *\n * RN's `AccessibilityRole` union has no \"status\" value (the web version's\n * `role=\"status\"`); `\"alert\"` is the closest available role for content\n * that should interrupt and be announced, paired with\n * `accessibilityLiveRegion=\"polite\"` (Android's live-region API) as the\n * nearest match to the web's `aria-live=\"polite\"` — deliberately not\n * `\"assertive\"`, matching the same politeness level the source chose.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Toast.html\n */\nexport const Toast = ({\n title,\n duration = 3000,\n onDismiss,\n actionLabel,\n onAction,\n dismissible = false,\n style,\n ...viewProps\n}: ToastProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const reducedMotion = useReducedMotion();\n\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const remainingRef = useRef(duration);\n const startedAtRef = useRef(0);\n const progress = useRef(new Animated.Value(reducedMotion ? 1 : 0)).current;\n\n const clearTimer = useCallback(() => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n const startTimer = useCallback(\n (ms: number) => {\n if (ms <= 0 || !onDismiss) {\n return;\n }\n\n clearTimer();\n startedAtRef.current = Date.now();\n timerRef.current = setTimeout(() => onDismiss(), ms);\n },\n [onDismiss, clearTimer],\n );\n\n useEffect(() => {\n if (duration > 0) {\n remainingRef.current = duration;\n startTimer(duration);\n }\n\n return clearTimer;\n }, [duration, startTimer, clearTimer]);\n\n useEffect(() => {\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationNormal,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: true,\n }).start();\n }, [reducedMotion, progress, theme.durationNormal, theme.easingDefault]);\n\n const handlePressIn = () => {\n if (timerRef.current) {\n const elapsed = Date.now() - startedAtRef.current;\n\n remainingRef.current = Math.max(0, remainingRef.current - elapsed);\n clearTimer();\n }\n };\n\n const handlePressOut = () => {\n startTimer(remainingRef.current);\n };\n\n const handleAction = () => {\n clearTimer();\n onAction?.();\n onDismiss?.();\n };\n\n const shadow =\n scheme === 'dark'\n ? { shadowOpacity: 0.4, shadowRadius: 12, elevation: 6 }\n : { shadowOpacity: 0.15, shadowRadius: 8, elevation: 4 };\n\n return (\n <AnimatedPressable\n accessible\n accessibilityRole=\"alert\"\n accessibilityLiveRegion=\"polite\"\n onPressIn={handlePressIn}\n onPressOut={handlePressOut}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: 10,\n paddingRight: theme.space2,\n paddingLeft: theme.space3,\n minHeight: 44,\n maxWidth: 480,\n minWidth: 240,\n backgroundColor: theme.cardBgColor,\n borderRadius: theme.radiusLg,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 2 },\n opacity: progress,\n transform: [\n {\n translateY: progress.interpolate({ inputRange: [0, 1], outputRange: [12, 0] }),\n },\n { scale: progress.interpolate({ inputRange: [0, 1], outputRange: [0.96, 1] }) },\n ],\n ...shadow,\n },\n style,\n ]}\n {...viewProps}\n >\n <Text\n variant=\"body\"\n style={{ flex: 1, minWidth: 0 }}\n numberOfLines={typeof title === 'string' ? 2 : undefined}\n >\n {title}\n </Text>\n\n {(actionLabel || dismissible) && (\n <>\n {actionLabel && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={actionLabel}\n onPress={handleAction}\n style={({ pressed }) => ({\n paddingVertical: 4,\n paddingHorizontal: theme.space1,\n borderRadius: theme.radiusSm,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n })}\n >\n <RNText\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n color: theme.accentColor,\n }}\n >\n {actionLabel}\n </RNText>\n </Pressable>\n )}\n\n {dismissible && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel=\"Dismiss\"\n hitSlop={4}\n onPress={() => {\n clearTimer();\n onDismiss?.();\n }}\n style={({ pressed }) => ({\n width: 28,\n height: 28,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusSm,\n opacity: pressed ? 1 : 0.55,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n })}\n >\n <RNText style={{ fontSize: theme.fontSizeBody, color: theme.windowFgColor }}>\n {'×'}\n </RNText>\n </Pressable>\n )}\n </>\n )}\n </AnimatedPressable>\n );\n};\n","import type { ReactNode } from 'react';\nimport type { StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface ToasterProps extends Omit<ViewProps, 'style'> {\n /**\n * Where to position the toast stack within its container.\n * - `\"bottom\"` (default) — bottom-center, following the GNOME HIG.\n * - `\"top\"` — top-center.\n */\n position?: 'bottom' | 'top';\n /** `Toast` elements to display. */\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Absolutely-positioned container that stacks `Toast` notifications.\n *\n * RN has no `document.body`/portal target to render into the way the web\n * version's `createPortal(node, container ?? document.body)` does, so\n * there's no `container` prop here — mount `Toaster` yourself as the\n * **last** child of your app's root-level `View` (so it paints on top of\n * everything else) with that root `View` given `position: 'relative'` (or\n * left as the default relative positioning). `Toaster` then positions\n * itself with `position: 'absolute'`, spanning the full width and\n * anchored to `top`/`bottom` by `position`.\n *\n * `pointerEvents=\"box-none\"` is RN's equivalent of the web version's\n * `pointer-events: none` on the container — the empty space around the\n * toast stack doesn't intercept touches, but each `Toast` inside (a\n * `Pressable`) still handles its own.\n *\n * @example\n * ```tsx\n * <View style={{ flex: 1 }}>\n * <YourAppContent />\n * <Toaster>\n * {toasts.map((t) => (\n * <Toast key={t.id} title={t.message} onDismiss={() => remove(t.id)} />\n * ))}\n * </Toaster>\n * </View>\n * ```\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Toast.html\n */\nexport const Toaster = ({ position = 'bottom', children, style, ...viewProps }: ToasterProps) => {\n const theme = useGnomeTheme();\n\n return (\n <RNView\n pointerEvents=\"box-none\"\n accessibilityLabel=\"Notifications\"\n style={[\n {\n position: 'absolute',\n left: 0,\n right: 0,\n alignItems: 'center',\n gap: theme.space1,\n zIndex: 9999,\n },\n position === 'top' ? { top: theme.space4 } : { bottom: theme.space4 },\n style,\n ]}\n {...viewProps}\n >\n {children}\n </RNView>\n );\n};\n","import { createContext, forwardRef, type ReactNode, useContext, useMemo } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\n\n// ─── Context ──────────────────────────────────────────────────────────────────\n\ninterface ToggleGroupContextValue {\n value: string;\n onValueChange: (value: string) => void;\n}\n\nconst ToggleGroupContext = createContext<ToggleGroupContextValue | null>(null);\n\n/** Internal — `ToggleGroupItem` reads the selected value and setter from here. */\n// eslint-disable-next-line react-refresh/only-export-components\nexport function useToggleGroup() {\n const ctx = useContext(ToggleGroupContext);\n\n if (!ctx) {\n throw new Error('ToggleGroupItem must be used inside ToggleGroup');\n }\n\n return ctx;\n}\n\n// ─── ToggleGroup ──────────────────────────────────────────────────────────────\n\nexport interface ToggleGroupProps extends Omit<ViewProps, 'style'> {\n /** Name of the currently active toggle. */\n value: string;\n /** Called with the new value when a toggle is selected. */\n onValueChange: (value: string) => void;\n /** Accessible label for the group. */\n accessibilityLabel?: string;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Mutually-exclusive group of toggle buttons for in-place option selection.\n * Mirrors `AdwToggleGroup` (libadwaita 1.7 / GNOME 48) and\n * `@gnome-ui/react`'s own `ToggleGroup`.\n *\n * Use for formatting controls, view-mode selectors, and toolbar options —\n * wherever a `ViewSwitcher` would be too heavy or doesn't belong in a\n * `HeaderBar`. Compose with `ToggleGroupItem`.\n *\n * The context and its `value`/`onValueChange` shape port 1:1 — pure React,\n * no DOM involved. What doesn't port is the keyboard layer: the web version\n * owns an `onKeyDown` handler implementing ← / → cycling and Home / End\n * jumps over a roving `tabIndex`, none of which has a touch counterpart.\n * That's this package's standing convention, already set by `ViewSwitcher`\n * and `TabBar`; the `radiogroup`/`radio` + `checked` accessibility pairing\n * that VoiceOver and TalkBack actually announce is what carries the\n * semantics here instead.\n *\n * The group carries `accessibilityRole=\"radiogroup\"` but deliberately\n * **not** `accessible` — on iOS, `accessible` on a container collapses the\n * whole subtree into one accessibility element, which would make the\n * individual toggles unreachable for VoiceOver and defeat the point of the\n * role. Without it the role still groups on Android while every item stays\n * individually focusable. (`ViewSwitcher`, built earlier, does set\n * `accessible` alongside the same role — see this package's ROADMAP note;\n * worth revisiting there.) The visible trade-off is that the group won't\n * match a `getByRole('radiogroup')` query, since Testing Library only\n * matches roles on accessible elements — the items are what matter to a\n * screen reader, and they each match `getByRole('radio')`.\n *\n * `display: inline-flex` becomes `alignSelf: 'flex-start'` (the same\n * hug-your-content trick `ViewSwitcher` uses), and `box-shadow:\n * var(--gnome-shadow-sm)` is dropped rather than approximated — the theme\n * generator deliberately keeps the shadow tokens in `raw` only, and `Card`\n * already established that a border carries the same separation on RN.\n * The dark-mode border color is hardcoded per scheme (`rgba(255,255,255,\n * 0.12)`) exactly as the source CSS hardcodes it, rather than read from\n * `theme.light3`, which stays `#deddda` in every theme.\n *\n * @example\n * const [align, setAlign] = useState('left');\n *\n * <ToggleGroup value={align} onValueChange={setAlign} accessibilityLabel=\"Alignment\">\n * <ToggleGroupItem name=\"left\" icon={FormatJustifyLeft} accessibilityLabel=\"Left\" />\n * <ToggleGroupItem name=\"center\" icon={FormatJustifyCenter} accessibilityLabel=\"Center\" />\n * <ToggleGroupItem name=\"right\" icon={FormatJustifyRight} accessibilityLabel=\"Right\" />\n * </ToggleGroup>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ToggleGroup.html\n */\nexport const ToggleGroup = forwardRef<View, ToggleGroupProps>(function ToggleGroup(\n { value, onValueChange, accessibilityLabel = 'Options', children, style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const context = useMemo(() => ({ value, onValueChange }), [value, onValueChange]);\n\n return (\n <ToggleGroupContext.Provider value={context}>\n <RNView\n ref={ref}\n accessibilityRole=\"radiogroup\"\n accessibilityLabel={accessibilityLabel}\n style={[\n {\n alignSelf: 'flex-start',\n flexDirection: 'row',\n alignItems: 'stretch',\n gap: 2,\n padding: 2,\n borderRadius: theme.radiusMd,\n borderWidth: 1,\n borderColor: scheme === 'dark' ? 'rgba(255, 255, 255, 0.12)' : theme.light3,\n backgroundColor: theme.cardBgColor,\n },\n style,\n ]}\n {...viewProps}\n >\n {children}\n </RNView>\n </ToggleGroupContext.Provider>\n );\n});\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport { forwardRef } from 'react';\nimport type { PressableProps, TextStyle, View } from 'react-native';\nimport { Pressable, Text as RNText, View as RNView } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\n\nimport { useToggleGroup } from './ToggleGroup';\n\nexport interface ToggleGroupItemProps\n extends Omit<PressableProps, 'children' | 'style' | 'disabled' | 'onPress'> {\n /** String identifier — becomes the group's `value` when this item is selected. */\n name: string;\n /** Visible label. Omit for icon-only items (pass `accessibilityLabel` instead). */\n label?: string;\n /** Icon from `@gnome-ui/icons`. */\n icon?: IconDefinition;\n /** Accessible name. Required for icon-only items; defaults to `label`. */\n accessibilityLabel?: string;\n disabled?: boolean;\n}\n\n/**\n * Individual toggle inside a `ToggleGroup`. Can be icon-only, label-only, or\n * icon + label — for icon-only items always pass an `accessibilityLabel` so\n * screen readers can identify the option.\n *\n * The active state's three `color-mix(in srgb, accent N%, transparent)`\n * values resolve to 8-digit `#RRGGBBAA` hexes off `theme.accentBgColor`\n * (always a plain 6-digit hex), the technique `Chip` already established for\n * exactly this selected-tint problem — `26` for 15% and `66` for 40% in\n * light, `40` for 25% and `80` for 50% in dark. The CSS paints its active\n * ring as an `inset 0 0 0 1px` box-shadow, which RN has no equivalent for;\n * it becomes a real `borderWidth: 1` that every item carries at all times\n * (transparent when inactive) so selecting one never shifts the row's\n * layout — the same substitution `AvatarGroup` made for its own ring.\n *\n * `:hover` has no touch counterpart and collapses away; `:active` maps to\n * `Pressable`'s `pressed` state using `theme.activeOverlay`, whose light\n * (`rgba(0, 0, 0, 0.12)`) and dark (`rgba(255, 255, 255, 0.14)`) values are\n * character-for-character the CSS's own `:active` colors — the same\n * coincidence `ViewSwitcherItem` documented and reused. The\n * `background-color`/`box-shadow` transition is dropped rather than\n * rebuilt on `Animated`: `ViewSwitcherItem`, the closest sibling, doesn't\n * animate its own selection either, so there is no reduced-motion handling\n * to wire up here.\n *\n * The icon keeps the default foreground color instead of tracking the\n * active accent text: `Icon` has no `currentColor` equivalent, and its\n * `color` prop is a fixed GNOME palette (`blue`, `green`, …) with no\n * `accent` member — which couldn't follow the app's configurable accent\n * anyway. Same call, same reason, as `Chip`.\n *\n * `icon` is an `IconDefinition` rather than the `ReactNode` the older\n * `ViewSwitcherItem` takes; that component predates `Icon` shipping in this\n * package, while `Chip` and the web version both type it this way.\n */\nexport const ToggleGroupItem = forwardRef<View, ToggleGroupItemProps>(function ToggleGroupItem(\n { name, label, icon, accessibilityLabel, disabled = false, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const { value, onValueChange } = useToggleGroup();\n\n const active = value === name;\n const iconOnly = Boolean(icon) && !label;\n const isDark = scheme === 'dark';\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n onPress={() => onValueChange(name)}\n accessibilityRole=\"radio\"\n accessibilityState={{ checked: active, disabled }}\n accessibilityLabel={accessibilityLabel ?? label}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n gap: theme.space1,\n minHeight: 28,\n minWidth: iconOnly ? 28 : undefined,\n paddingVertical: 4,\n paddingHorizontal: iconOnly ? 4 : theme.space2,\n borderRadius: theme.radiusMd - 2,\n borderWidth: 1,\n borderColor: active ? `${theme.accentBgColor}${isDark ? '80' : '66'}` : 'transparent',\n backgroundColor: active\n ? `${theme.accentBgColor}${isDark ? '40' : '26'}`\n : pressed && !disabled\n ? theme.activeOverlay\n : 'transparent',\n opacity: disabled ? theme.opacityDisabled : 1,\n })}\n {...pressableProps}\n >\n {icon ? (\n <RNView style={{ flexShrink: 0 }}>\n <Icon icon={icon} size=\"md\" />\n </RNView>\n ) : null}\n\n {label ? (\n <RNText\n numberOfLines={1}\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(\n active ? theme.fontWeightSemibold : theme.fontWeightNormal,\n ) as TextStyle['fontWeight'],\n color: active ? theme.accentColor : theme.windowFgColor,\n }}\n >\n {label}\n </RNText>\n ) : null}\n </Pressable>\n );\n});\n","import { forwardRef } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface ToolbarProps extends Omit<ViewProps, 'style'> {\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Horizontal action bar following the libadwaita `.toolbar` pattern —\n * mirrors `@gnome-ui/react`'s `Toolbar`. Directly portable: no web-only\n * APIs, just a flex row with fixed padding/gap.\n *\n * Provides `theme.space1` (6 dp) padding and gap — the standard spacing\n * for rows of flat buttons in header bars, action bars, and tool rows.\n * Place a `Spacer` between leading and trailing groups to push trailing\n * items to the end. Use `Button variant=\"flat\"` for buttons that blend\n * into the bar, or `variant=\"raised\"` for one that needs explicit\n * elevation within a flat context.\n *\n * The web CSS's `color`/`font-family` on `.toolbar` are dropped — RN has\n * no style inheritance from a parent `View` down to child `Text`\n * elements the way CSS `color` cascades, so a value here would reach\n * nothing (every child, e.g. `Button`, already sets its own explicit\n * text/icon colors).\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest/style-classes.html#toolbar-style-class\n */\nexport const Toolbar = forwardRef<View, ToolbarProps>(function Toolbar(\n { style, children, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <RNView\n ref={ref}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space1,\n padding: theme.space1,\n minHeight: 46,\n },\n style,\n ]}\n {...viewProps}\n >\n {children}\n </RNView>\n );\n});\n","import { forwardRef, type ReactNode } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView, StyleSheet } from 'react-native';\n\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\n\nexport interface ViewSwitcherProps extends Omit<ViewProps, 'style'> {\n children?: ReactNode;\n /** Accessible label for the group. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Segmented control for switching between major views.\n *\n * Mirrors the Adwaita `AdwViewSwitcher` pattern. Place as a `HeaderBar`'s\n * `title` for the canonical GNOME layout. Compose with `ViewSwitcherItem`\n * for each option.\n *\n * The web version's pill background is `color-mix(in srgb,\n * var(--gnome-headerbar-bg-color) 75%, black 25%)` (70/30 in dark mode) —\n * RN has no `color-mix()`, but alpha-compositing a flat semi-transparent\n * black `View` over an opaque background produces the exact same math\n * (`background × (1 − alpha) + black × alpha`), so that's what's layered\n * here instead, matching `Card`'s established color-mix-avoidance pattern.\n * `useResolvedColorScheme()` picks the 25%/30% split to match the web\n * value exactly rather than reusing a nearby overlay token.\n *\n * Roving-tabindex ← → / Home / End keyboard navigation isn't ported —\n * same reasoning as `TabBar`: no per-app arrow-key convention exists in\n * RN's touch-first focus model to port it to.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ViewSwitcher.html\n * @see https://developer.gnome.org/hig/patterns/nav/view-switchers.html\n */\nexport const ViewSwitcher = forwardRef<View, ViewSwitcherProps>(function ViewSwitcher(\n { children, accessibilityLabel = 'View switcher', style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n\n return (\n <RNView\n ref={ref}\n accessible\n accessibilityRole=\"radiogroup\"\n accessibilityLabel={accessibilityLabel}\n style={[\n {\n alignSelf: 'flex-start',\n borderRadius: theme.radiusPill,\n overflow: 'hidden',\n backgroundColor: theme.headerbarBgColor,\n },\n style,\n ]}\n {...viewProps}\n >\n <RNView\n pointerEvents=\"none\"\n style={[\n StyleSheet.absoluteFill,\n { backgroundColor: scheme === 'dark' ? 'rgba(0, 0, 0, 0.3)' : 'rgba(0, 0, 0, 0.25)' },\n ]}\n />\n <RNView style={{ flexDirection: 'row', alignItems: 'center', gap: 2, padding: 3 }}>\n {children}\n </RNView>\n </RNView>\n );\n});\n","import { forwardRef, type ReactNode } from 'react';\nimport type { PressableProps, TextStyle, View } from 'react-native';\nimport { Pressable, Text as RNText, View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface ViewSwitcherItemProps\n extends Omit<PressableProps, 'children' | 'style' | 'disabled'> {\n /** Display label. */\n label: string;\n /**\n * Icon placed before the label. Rendered as-is — size/color it yourself.\n * Web's `icon` takes an `@gnome-ui/icons` `IconDefinition`; this package\n * has no SVG icon system, so it's a plain `ReactNode` here, same as\n * `TabItem`'s `icon`.\n */\n icon?: ReactNode;\n /** Marks this item as the currently active view. */\n active?: boolean;\n disabled?: boolean;\n}\n\n/**\n * Individual option inside a `ViewSwitcher`.\n *\n * Renders with `accessibilityRole=\"radio\"` so the group reads as a proper\n * radio group. The pressed-state tint on non-active items\n * (`theme.activeOverlay`) isn't a stand-in approximation here — its light\n * (`rgba(0, 0, 0, 0.12)`) and dark (`rgba(255, 255, 255, 0.14)`) values\n * happen to match the web version's `:active` colors exactly, so it's\n * reused directly rather than hand-rolled.\n */\nexport const ViewSwitcherItem = forwardRef<View, ViewSwitcherItemProps>(function ViewSwitcherItem(\n { label, icon, active = false, disabled, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"radio\"\n accessibilityState={{ checked: active, disabled: !!disabled }}\n accessibilityLabel={label}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n gap: theme.space1,\n paddingVertical: 5,\n paddingHorizontal: theme.space2,\n borderRadius: theme.radiusPill - 3,\n opacity: disabled ? theme.opacityDisabled : 1,\n backgroundColor: active\n ? theme.headerbarBgColor\n : pressed\n ? theme.activeOverlay\n : 'transparent',\n })}\n {...pressableProps}\n >\n {icon && <RNView style={{ flexShrink: 0 }}>{icon}</RNView>}\n\n <RNText\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(\n active ? theme.fontWeightSemibold : theme.fontWeightNormal,\n ) as TextStyle['fontWeight'],\n color: theme.headerbarFgColor,\n }}\n >\n {label}\n </RNText>\n </Pressable>\n );\n});\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport { DocumentEdit } from '@gnome-ui/icons';\nimport { type ReactNode, useState } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Pressable, ScrollView, View } from 'react-native';\n\nimport { ActionRow } from '@/components/ActionRow';\nimport { BottomSheet } from '@/components/BottomSheet';\nimport { BoxedList } from '@/components/BoxedList';\nimport { Button } from '@/components/Button';\nimport { Dialog } from '@/components/Dialog';\nimport { Drawer } from '@/components/Drawer';\nimport { Icon } from '@/components/Icon';\nimport { IconButton } from '@/components/IconButton';\nimport { StatusPage } from '@/components/StatusPage';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface WidgetDefinition {\n /** Stable unique identifier. Also the value stored in `value`. */\n id: string;\n /** Row label shown in the picker catalog and above the widget once added. */\n label: string;\n /** Optional secondary line shown below `label` in the picker row. */\n description?: string;\n /** Icon shown in the picker row and in the widget's own header once added. */\n icon?: IconDefinition;\n /** Renders the widget's body once it has been added to the manager. */\n render: () => ReactNode;\n}\n\n/**\n * `@gnome-ui/react`'s `pickerSurface` calls this option `\"modal\"`, naming it\n * after its own `Modal` component. This package's `Modal` counterpart is\n * `Dialog` (RN's own `Modal` primitive is a different, lower-level thing —\n * see the main `ROADMAP.md`'s note on that exact naming trap), so the option\n * is named after what it actually renders here instead of ported verbatim.\n */\nexport type WidgetManagerPickerSurface = 'dialog' | 'bottomSheet' | 'drawer';\n\nexport interface WidgetManagerProps {\n /** Full catalog of widgets available to add. */\n catalog: WidgetDefinition[];\n /** Controlled list of added widget ids, in display order. */\n value: string[];\n /** Called with the new id list when the user confirms the picker. */\n onChange: (value: string[]) => void;\n /** Header title. */\n title: string;\n /** Icon shown at the leading edge of the header. */\n icon?: IconDefinition;\n /** Which overlay renders the catalog picker. Defaults to `\"dialog\"`. */\n pickerSurface?: WidgetManagerPickerSurface;\n /** Label for the dashed \"add widget\" trigger. Defaults to `\"Add Widget\"`. */\n addTriggerLabel?: string;\n /** Message shown when there are no widgets and not in edit mode. Defaults to `\"No widgets added\"`. */\n emptyStateLabel?: string;\n /** Heading of the picker overlay. Defaults to `\"Widgets\"`. */\n pickerTitle?: string;\n /** Label for a catalog row's add action. Defaults to `\"Add\"`. */\n addLabel?: string;\n /** Label for a catalog row's remove action (already staged). Defaults to `\"Remove\"`. */\n removeLabel?: string;\n /** Label for the picker's confirm action. Defaults to `\"Accept\"`. */\n confirmLabel?: string;\n /** Label for the picker's cancel action. Defaults to `\"Cancel\"`. */\n cancelLabel?: string;\n /** Accessible name of the header's edit-mode toggle button. Defaults to `\"Edit widgets\"`. */\n editLabel?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst semibold = (theme: { fontWeightSemibold: number }): TextStyle['fontWeight'] =>\n String(theme.fontWeightSemibold) as TextStyle['fontWeight'];\n\n/**\n * Card that manages a controlled collection of \"widgets\" — pick which ones\n * are visible from a catalog, each rendering its own arbitrary content.\n * Mirrors `@gnome-ui/react`'s own `WidgetManager`.\n *\n * The header's edit button toggles a local `editing` state: in view mode\n * only the added widgets (or an empty-state message) are shown; in edit\n * mode a dashed \"add widget\" trigger also appears, opening a catalog picker\n * (`pickerSurface`: `\"dialog\"`, `\"bottomSheet\"`, or `\"drawer\"`, each backed\n * by the already-shipped `Dialog`/`BottomSheet`/`Drawer`). Adding/removing\n * is staged inside the picker and only applied — via `onChange` — when the\n * user confirms; canceling or dismissing the picker discards the staging.\n * Widgets can only be removed through the picker, never inline in the card.\n *\n * The catalog list is wrapped in its own `ScrollView` (capped at 360dp)\n * before being handed to whichever picker surface renders it — unlike the\n * web version's `overflow-y: auto` on the `Modal`/`BottomSheet`/`Drawer`\n * body, none of this package's three overlay components scroll their\n * `children` for you, so a long catalog needs that scroll container built\n * in here rather than assumed.\n *\n * `Dialog` already renders its own confirm/cancel action row from a\n * `buttons` array, so only `bottomSheet`/`drawer` need the hand-rolled\n * footer row the web version calls `pickerFooter` — the exact same split\n * the web source itself documents (\"Modal uses its own actions\").\n *\n * Not ported: `aria-pressed` on the edit toggle — `Button`/`IconButton` set\n * their own internal `accessibilityState={{ disabled }}` on the underlying\n * `Pressable`, and RN merges a spread prop object outright rather than\n * key-by-key, so passing a second `accessibilityState` here would silently\n * replace rather than merge with it (the same `Popover`-trigger clobber\n * `SplitButton` already worked around) — dropped rather than routed around\n * for one decorative toggle-state announcement.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.WidgetManager.html\n */\nexport const WidgetManager = ({\n catalog,\n value,\n onChange,\n title,\n icon,\n pickerSurface = 'dialog',\n addTriggerLabel = 'Add Widget',\n emptyStateLabel = 'No widgets added',\n pickerTitle = 'Widgets',\n addLabel = 'Add',\n removeLabel = 'Remove',\n confirmLabel = 'Accept',\n cancelLabel = 'Cancel',\n editLabel = 'Edit widgets',\n style,\n testID,\n}: WidgetManagerProps) => {\n const theme = useGnomeTheme();\n const [editing, setEditing] = useState(false);\n const [pickerOpen, setPickerOpen] = useState(false);\n const [stagedIds, setStagedIds] = useState<string[]>(value);\n\n const openPicker = () => {\n setStagedIds(value);\n setPickerOpen(true);\n };\n\n const toggleStaged = (id: string) => {\n setStagedIds((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]));\n };\n\n const confirm = () => {\n onChange(stagedIds);\n setPickerOpen(false);\n };\n\n const cancel = () => setPickerOpen(false);\n\n const addedWidgets = value\n .map((id) => catalog.find((widget) => widget.id === id))\n .filter((widget): widget is WidgetDefinition => widget !== undefined);\n\n const hasWidgets = addedWidgets.length > 0;\n const showEmptyState = !editing && !hasWidgets;\n\n const badge = (widgetIcon: IconDefinition, background: string) => (\n <View\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n width: 28,\n height: 28,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 14,\n backgroundColor: background,\n }}\n >\n <Icon icon={widgetIcon} />\n </View>\n );\n\n const catalogList = (\n <ScrollView style={{ maxHeight: 360 }}>\n <BoxedList>\n {catalog.map((widget) => {\n const staged = stagedIds.includes(widget.id);\n\n return (\n <ActionRow\n key={widget.id}\n leading={widget.icon && <Icon icon={widget.icon} />}\n title={widget.label}\n subtitle={widget.description}\n trailing={\n <Button\n variant={staged ? 'default' : 'suggested'}\n size=\"sm\"\n onPress={() => toggleStaged(widget.id)}\n >\n {staged ? removeLabel : addLabel}\n </Button>\n }\n />\n );\n })}\n </BoxedList>\n </ScrollView>\n );\n\n const pickerFooter = (\n <View\n style={{\n flexDirection: 'row',\n justifyContent: 'flex-end',\n gap: theme.space2,\n paddingTop: theme.space2,\n }}\n >\n <Button variant=\"flat\" onPress={cancel}>\n {cancelLabel}\n </Button>\n <Button variant=\"suggested\" onPress={confirm}>\n {confirmLabel}\n </Button>\n </View>\n );\n\n return (\n <View\n testID={testID}\n style={[\n {\n gap: theme.space3,\n padding: theme.space4,\n backgroundColor: theme.cardBgColor,\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusLg,\n },\n style,\n ]}\n >\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: theme.space2 }}>\n <View\n style={{\n flex: 1,\n minWidth: 0,\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n }}\n >\n {icon && badge(icon, theme.viewBgColor)}\n <Text variant=\"body\" numberOfLines={1} style={{ fontWeight: semibold(theme) }}>\n {title}\n </Text>\n </View>\n\n <IconButton\n icon={DocumentEdit}\n label={editLabel}\n variant=\"flat\"\n size=\"sm\"\n onPress={() => setEditing((prev) => !prev)}\n />\n </View>\n\n <View style={{ gap: theme.space3 }}>\n {showEmptyState && <StatusPage compact title={emptyStateLabel} />}\n\n {hasWidgets && (\n <View style={{ gap: theme.space3 }}>\n {addedWidgets.map((widget) => (\n <View\n key={widget.id}\n style={{\n gap: theme.space2,\n padding: theme.space3,\n backgroundColor: theme.viewBgColor,\n borderRadius: theme.radiusMd,\n }}\n >\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: theme.space2 }}>\n {widget.icon && badge(widget.icon, theme.cardBgColor)}\n <Text variant=\"body\" style={{ fontWeight: semibold(theme) }}>\n {widget.label}\n </Text>\n </View>\n <View style={{ minWidth: 0 }}>{widget.render()}</View>\n </View>\n ))}\n </View>\n )}\n\n {editing && (\n <Pressable\n accessibilityRole=\"button\"\n onPress={openPicker}\n style={({ pressed }) => ({\n width: '100%',\n paddingVertical: theme.space4,\n alignItems: 'center',\n justifyContent: 'center',\n borderWidth: 2,\n borderStyle: 'dashed',\n borderColor: pressed ? theme.accentColor : theme.cardShadeColor,\n borderRadius: theme.radiusLg,\n opacity: pressed ? 1 : theme.opacityDim,\n })}\n >\n <Text variant=\"body\" style={{ fontWeight: semibold(theme) }}>\n {addTriggerLabel}\n </Text>\n </Pressable>\n )}\n </View>\n\n {pickerSurface === 'dialog' && (\n <Dialog\n open={pickerOpen}\n title={pickerTitle}\n onClose={cancel}\n buttons={[\n { label: cancelLabel, onPress: cancel },\n { label: confirmLabel, variant: 'suggested', onPress: confirm },\n ]}\n >\n {catalogList}\n </Dialog>\n )}\n\n {pickerSurface === 'bottomSheet' && (\n <BottomSheet open={pickerOpen} title={pickerTitle} onClose={cancel}>\n {catalogList}\n {pickerFooter}\n </BottomSheet>\n )}\n\n {pickerSurface === 'drawer' && (\n <Drawer open={pickerOpen} title={pickerTitle} onClose={cancel}>\n {catalogList}\n {pickerFooter}\n </Drawer>\n )}\n </View>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/theme/tokens.generated.ts","../src/GnomeProvider/GnomeContext.ts","../src/theme/resolveTheme.ts","../src/GnomeProvider/resolveContext.ts","../src/GnomeProvider/GnomeProvider.tsx","../src/components/Text/Text.tsx","../src/components/ActionRow/ActionRow.tsx","../src/components/Icon/shared.ts","../src/components/Icon/Icon.tsx","../src/components/AnimatedIcon/AnimatedIcon.tsx","../src/components/Avatar/Avatar.tsx","../src/components/AvatarGroup/AvatarGroup.tsx","../src/components/AvatarRotator/AvatarRotator.tsx","../src/components/Badge/Badge.tsx","../src/components/Banner/Banner.tsx","../src/components/Bin/Bin.tsx","../src/components/Blockquote/Blockquote.tsx","../src/components/BottomSheet/BottomSheet.tsx","../src/components/BottomTabBar/BottomTabBar.tsx","../src/components/Box/Box.tsx","../src/components/Separator/Separator.tsx","../src/components/BoxedList/BoxedList.tsx","../src/components/BreakpointBin/BreakpointBin.tsx","../src/components/Button/Button.tsx","../src/components/ButtonContent/ButtonContent.tsx","../src/components/ButtonRow/ButtonRow.tsx","../src/components/Tooltip/Tooltip.tsx","../src/components/IconButton/IconButton.tsx","../src/components/Calendar/calendarUtils.ts","../src/components/Calendar/CalendarBase.tsx","../src/components/Calendar/Calendar.tsx","../src/components/CalendarRange/rangeUtils.ts","../src/components/CalendarRange/CalendarRange.tsx","../src/components/Callout/Callout.tsx","../src/components/Card/Card.tsx","../src/components/Checkbox/Checkbox.tsx","../src/components/CheckRow/CheckRow.tsx","../src/components/Chip/Chip.tsx","../src/components/Clamp/Clamp.tsx","../src/components/CoachMark/coachMarkUtils.ts","../src/components/CoachMark/CoachMark.tsx","../src/components/CoachMark/CoachMarkTour.tsx","../src/components/WrapBox/WrapBox.tsx","../src/components/ColorPicker/ColorSwatch.tsx","../src/components/ColorPicker/ColorPicker.tsx","../src/components/Dropdown/Dropdown.tsx","../src/components/ComboRow/ComboRow.tsx","../src/components/CopyButton/CopyButton.tsx","../src/components/Popover/Popover.tsx","../src/components/SpinButton/SpinButton.tsx","../src/components/TimePicker/timeUtils.ts","../src/components/TimePicker/TimeFields.tsx","../src/components/DatePicker/DatePicker.tsx","../src/components/Dialog/Dialog.tsx","../src/components/Divider/Divider.tsx","../src/components/Drawer/Drawer.tsx","../src/components/EntryRow/EntryRow.tsx","../src/components/Expander/Expander.tsx","../src/components/ExpanderRow/ExpanderRow.tsx","../src/components/FieldGroup/FieldGroup.tsx","../src/components/FileTypeIcon/fileType.ts","../src/components/FileTypeIcon/FileTypeIcon.tsx","../src/components/FilterableMultiSelectDropdown/FilterableMultiSelectDropdown.tsx","../src/components/HeaderBar/HeaderBar.tsx","../src/components/Highlight/Highlight.tsx","../src/components/InlineViewSwitcher/variants.ts","../src/components/InlineViewSwitcher/InlineViewSwitcher.tsx","../src/components/InlineViewSwitcher/InlineViewSwitcherItem.tsx","../src/components/LevelBar/LevelBar.tsx","../src/components/Link/Link.tsx","../src/components/LinkedGroup/LinkedGroup.tsx","../src/components/MultiSelectDropdown/MultiSelectDropdown.tsx","../src/hooks/useBreakpoint.ts","../src/components/NavigationSplitView/NavigationSplitView.tsx","../src/components/Overlay/Overlay.tsx","../src/components/PasswordEntryRow/PasswordEntryRow.tsx","../src/components/PasswordField/PasswordField.tsx","../src/components/PathBar/PathBar.tsx","../src/components/PreferencesGroup/PreferencesGroup.tsx","../src/components/ProgressBar/ProgressBar.tsx","../src/components/RadioButton/RadioButton.tsx","../src/components/RangeSlider/RangeSlider.tsx","../src/components/RatingStars/RatingStars.tsx","../src/components/ScrollToTop/ScrollToTop.tsx","../src/components/SearchBar/SearchBar.tsx","../src/components/SegmentedBar/SegmentedBar.tsx","../src/components/Sidebar/filterUtils.ts","../src/components/Sidebar/Sidebar.tsx","../src/components/Sidebar/SidebarItem.tsx","../src/components/Sidebar/SidebarSection.tsx","../src/components/Skeleton/Skeleton.tsx","../src/components/Slider/Slider.tsx","../src/components/Spacer/Spacer.tsx","../src/components/Spinner/Spinner.tsx","../src/components/SpinRow/SpinRow.tsx","../src/components/SplitButton/SplitButton.tsx","../src/components/StatusBadge/StatusBadge.tsx","../src/components/StatusPage/StatusPage.tsx","../src/components/StepIndicator/StepIndicator.tsx","../src/components/Switch/Switch.tsx","../src/components/SwitchRow/SwitchRow.tsx","../src/components/Tabs/TabBar.tsx","../src/components/Tabs/TabItem.tsx","../src/components/Tabs/TabPanel.tsx","../src/components/TagInput/TagInput.tsx","../src/components/TextField/TextField.tsx","../src/components/Timeline/Timeline.tsx","../src/components/TimePicker/TimePicker.tsx","../src/components/Toast/Toast.tsx","../src/components/Toast/Toaster.tsx","../src/components/ToggleGroup/ToggleGroup.tsx","../src/components/ToggleGroup/ToggleGroupItem.tsx","../src/components/Toolbar/Toolbar.tsx","../src/components/ViewSwitcher/ViewSwitcher.tsx","../src/components/ViewSwitcher/ViewSwitcherItem.tsx","../src/components/WidgetManager/WidgetManager.tsx"],"sourcesContent":["/**\n * AUTO-GENERATED — do not edit by hand.\n * Run `npm run theme:generate` (from packages/react-native) to regenerate\n * from @gnome-ui/core's `src/tokens.css`.\n */\n\nexport interface GnomeThemeTokens {\n readonly accentBgColor: string;\n readonly accentColor: string;\n readonly accentFgColor: string;\n readonly activeOverlay: string;\n readonly blockquoteBorderWidth: number;\n readonly blue1: string;\n readonly blue2: string;\n readonly blue3: string;\n readonly blue4: string;\n readonly blue5: string;\n readonly borderSubtle: string;\n readonly brown1: string;\n readonly brown2: string;\n readonly brown3: string;\n readonly brown4: string;\n readonly brown5: string;\n readonly cardBgColor: string;\n readonly cardFgColor: string;\n readonly cardShadeColor: string;\n readonly dark1: string;\n readonly dark2: string;\n readonly dark3: string;\n readonly dark4: string;\n readonly dark5: string;\n readonly destructiveBgColor: string;\n readonly destructiveColor: string;\n readonly destructiveFgColor: string;\n readonly dialogBackdropBlur: number;\n readonly dialogBackdropColor: string;\n readonly dialogBgColor: string;\n readonly dialogFgColor: string;\n readonly dividerColor: string;\n readonly durationFast: number;\n readonly durationNormal: number;\n readonly durationSlow: number;\n readonly easingDefault: readonly number[];\n readonly errorBgColor: string;\n readonly errorColor: string;\n readonly errorFgColor: string;\n readonly focusRingColor: string;\n readonly focusRingOffset: number;\n readonly focusRingWidth: number;\n readonly fontFamily: string;\n readonly fontFamilyMono: string;\n readonly fontSizeBody: number;\n readonly fontSizeCaption: number;\n readonly fontSizeLargeTitle: number;\n readonly fontSizeTitle1: number;\n readonly fontSizeTitle2: number;\n readonly fontSizeTitle3: number;\n readonly fontSizeTitle4: number;\n readonly fontWeightBold: number;\n readonly fontWeightNormal: number;\n readonly fontWeightSemibold: number;\n readonly green1: string;\n readonly green2: string;\n readonly green3: string;\n readonly green4: string;\n readonly green5: string;\n readonly headerbarBgColor: string;\n readonly headerbarBorderColor: string;\n readonly headerbarFgColor: string;\n readonly headerbarShadeColor: string;\n readonly hoverOverlay: string;\n readonly layoutBreakpointMedium: number;\n readonly layoutBreakpointNarrow: number;\n readonly layoutBreakpointWide: number;\n readonly layoutContentPadding: number;\n readonly layoutContentPaddingCompact: number;\n readonly layoutContentPaddingSpacious: number;\n readonly layoutSidebarDefaultWidth: number;\n readonly layoutSidebarMaxWidth: number;\n readonly layoutSidebarMinWidth: number;\n readonly layoutSidebarRailWidth: number;\n readonly light1: string;\n readonly light2: string;\n readonly light3: string;\n readonly light4: string;\n readonly light5: string;\n readonly lineHeightBody: number;\n readonly lineHeightHeading: number;\n readonly lineHeightTitle: number;\n readonly newBgColor: string;\n readonly newColor: string;\n readonly newFgColor: string;\n readonly opacityBorder: number;\n readonly opacityDim: number;\n readonly opacityDisabled: number;\n readonly orange1: string;\n readonly orange2: string;\n readonly orange3: string;\n readonly orange4: string;\n readonly orange5: string;\n readonly popoverBgColor: string;\n readonly popoverFgColor: string;\n readonly purple1: string;\n readonly purple2: string;\n readonly purple3: string;\n readonly purple4: string;\n readonly purple5: string;\n readonly radiusLg: number;\n readonly radiusMd: number;\n readonly radiusPill: number;\n readonly radiusSm: number;\n readonly radiusXl: number;\n readonly red1: string;\n readonly red2: string;\n readonly red3: string;\n readonly red4: string;\n readonly red5: string;\n readonly sidebarBgColor: string;\n readonly sidebarFgColor: string;\n readonly skeletonBaseColor: string;\n readonly skeletonHighlightColor: string;\n readonly space1: number;\n readonly space2: number;\n readonly space3: number;\n readonly space4: number;\n readonly space5: number;\n readonly space6: number;\n readonly successBgColor: string;\n readonly successColor: string;\n readonly successFgColor: string;\n readonly viewBgColor: string;\n readonly viewFgColor: string;\n readonly warningBgColor: string;\n readonly warningColor: string;\n readonly warningFgColor: string;\n readonly windowBgColor: string;\n readonly windowFgColor: string;\n readonly yellow1: string;\n readonly yellow2: string;\n readonly yellow3: string;\n readonly yellow4: string;\n readonly yellow5: string;\n}\n\nexport const lightTheme: GnomeThemeTokens = {\n accentBgColor: '#3584e4',\n accentColor: '#3584e4',\n accentFgColor: '#fff',\n activeOverlay: 'rgba(0, 0, 0, 0.12)',\n blockquoteBorderWidth: 3,\n blue1: '#99c1f1',\n blue2: '#62a0ea',\n blue3: '#3584e4',\n blue4: '#1c71d8',\n blue5: '#1a5fb4',\n borderSubtle: 'rgba(0, 0, 0, 0.15)',\n brown1: '#cdab8f',\n brown2: '#b5835a',\n brown3: '#986a44',\n brown4: '#865e3c',\n brown5: '#63452c',\n cardBgColor: '#fff',\n cardFgColor: 'rgba(0, 0, 0, 0.8)',\n cardShadeColor: 'rgba(0, 0, 0, 0.12)',\n dark1: '#77767b',\n dark2: '#5e5c64',\n dark3: '#3d3846',\n dark4: '#241f31',\n dark5: '#000',\n destructiveBgColor: '#e01b24',\n destructiveColor: '#e01b24',\n destructiveFgColor: '#fff',\n dialogBackdropBlur: 4,\n dialogBackdropColor: 'rgba(0, 0, 0, 0.5)',\n dialogBgColor: '#fafafa',\n dialogFgColor: 'rgba(0, 0, 0, 0.8)',\n dividerColor: 'rgba(0, 0, 0, 0.07)',\n durationFast: 100,\n durationNormal: 200,\n durationSlow: 400,\n easingDefault: [0.25, 0.46, 0.45, 0.94],\n errorBgColor: '#e01b24',\n errorColor: '#e01b24',\n errorFgColor: '#fff',\n focusRingColor: '#3584e4',\n focusRingOffset: 2,\n focusRingWidth: 3,\n fontFamily: 'Adwaita Sans',\n fontFamilyMono: 'Adwaita Mono',\n fontSizeBody: 16,\n fontSizeCaption: 12,\n fontSizeLargeTitle: 36,\n fontSizeTitle1: 30,\n fontSizeTitle2: 24,\n fontSizeTitle3: 20,\n fontSizeTitle4: 18,\n fontWeightBold: 700,\n fontWeightNormal: 400,\n fontWeightSemibold: 600,\n green1: '#8ff0a4',\n green2: '#57e389',\n green3: '#33d17a',\n green4: '#2ec27e',\n green5: '#26a269',\n headerbarBgColor: '#ebebeb',\n headerbarBorderColor: 'rgba(0, 0, 0, 0.12)',\n headerbarFgColor: 'rgba(0, 0, 0, 0.8)',\n headerbarShadeColor: 'rgba(0, 0, 0, 0.12)',\n hoverOverlay: 'rgba(0, 0, 0, 0.06)',\n layoutBreakpointMedium: 550,\n layoutBreakpointNarrow: 400,\n layoutBreakpointWide: 860,\n layoutContentPadding: 24,\n layoutContentPaddingCompact: 12,\n layoutContentPaddingSpacious: 36,\n layoutSidebarDefaultWidth: 240,\n layoutSidebarMaxWidth: 280,\n layoutSidebarMinWidth: 180,\n layoutSidebarRailWidth: 56,\n light1: '#fff',\n light2: '#f6f5f4',\n light3: '#deddda',\n light4: '#c0bfbc',\n light5: '#9a9996',\n lineHeightBody: 1.5,\n lineHeightHeading: 1.2,\n lineHeightTitle: 1.2,\n newBgColor: '#9141ac',\n newColor: '#9141ac',\n newFgColor: '#fff',\n opacityBorder: 0.15,\n opacityDim: 0.55,\n opacityDisabled: 0.5,\n orange1: '#ffbe6f',\n orange2: '#ffa348',\n orange3: '#ff7800',\n orange4: '#e66100',\n orange5: '#c64600',\n popoverBgColor: '#fff',\n popoverFgColor: 'rgba(0, 0, 0, 0.8)',\n purple1: '#dc8add',\n purple2: '#c061cb',\n purple3: '#9141ac',\n purple4: '#813d9c',\n purple5: '#613583',\n radiusLg: 12,\n radiusMd: 8,\n radiusPill: 9999,\n radiusSm: 4,\n radiusXl: 15,\n red1: '#f66151',\n red2: '#ed333b',\n red3: '#e01b24',\n red4: '#c01c28',\n red5: '#a51d2d',\n sidebarBgColor: '#ebebeb',\n sidebarFgColor: 'rgba(0, 0, 0, 0.8)',\n skeletonBaseColor: 'rgba(0, 0, 0, 0.09)',\n skeletonHighlightColor: 'rgba(255, 255, 255, 0.55)',\n space1: 6,\n space2: 12,\n space3: 18,\n space4: 24,\n space5: 36,\n space6: 48,\n successBgColor: '#2ec27e',\n successColor: '#2ec27e',\n successFgColor: '#fff',\n viewBgColor: '#fff',\n viewFgColor: 'rgba(0, 0, 0, 0.8)',\n warningBgColor: '#f6d32d',\n warningColor: '#e5a50a',\n warningFgColor: 'rgba(0, 0, 0, 0.8)',\n windowBgColor: '#fafafa',\n windowFgColor: 'rgba(0, 0, 0, 0.8)',\n yellow1: '#f9f06b',\n yellow2: '#f8e45c',\n yellow3: '#f6d32d',\n yellow4: '#f5c211',\n yellow5: '#e5a50a',\n} as const;\n\nexport const darkTheme: GnomeThemeTokens = {\n accentBgColor: '#3584e4',\n accentColor: '#62a0ea',\n accentFgColor: '#fff',\n activeOverlay: 'rgba(255, 255, 255, 0.14)',\n blockquoteBorderWidth: 3,\n blue1: '#99c1f1',\n blue2: '#62a0ea',\n blue3: '#3584e4',\n blue4: '#1c71d8',\n blue5: '#1a5fb4',\n borderSubtle: 'rgba(255, 255, 255, 0.15)',\n brown1: '#cdab8f',\n brown2: '#b5835a',\n brown3: '#986a44',\n brown4: '#865e3c',\n brown5: '#63452c',\n cardBgColor: '#383838',\n cardFgColor: 'rgba(255, 255, 255, 0.87)',\n cardShadeColor: 'rgba(255, 255, 255, 0.12)',\n dark1: '#77767b',\n dark2: '#5e5c64',\n dark3: '#3d3846',\n dark4: '#241f31',\n dark5: '#000',\n destructiveBgColor: '#e01b24',\n destructiveColor: '#e01b24',\n destructiveFgColor: '#fff',\n dialogBackdropBlur: 4,\n dialogBackdropColor: 'rgba(0, 0, 0, 0.65)',\n dialogBgColor: '#242424',\n dialogFgColor: 'rgba(255, 255, 255, 0.87)',\n dividerColor: 'rgba(255, 255, 255, 0.07)',\n durationFast: 100,\n durationNormal: 200,\n durationSlow: 400,\n easingDefault: [0.25, 0.46, 0.45, 0.94],\n errorBgColor: '#e01b24',\n errorColor: '#e01b24',\n errorFgColor: '#fff',\n focusRingColor: '#62a0ea',\n focusRingOffset: 2,\n focusRingWidth: 3,\n fontFamily: 'Adwaita Sans',\n fontFamilyMono: 'Adwaita Mono',\n fontSizeBody: 16,\n fontSizeCaption: 12,\n fontSizeLargeTitle: 36,\n fontSizeTitle1: 30,\n fontSizeTitle2: 24,\n fontSizeTitle3: 20,\n fontSizeTitle4: 18,\n fontWeightBold: 700,\n fontWeightNormal: 400,\n fontWeightSemibold: 600,\n green1: '#8ff0a4',\n green2: '#57e389',\n green3: '#33d17a',\n green4: '#2ec27e',\n green5: '#26a269',\n headerbarBgColor: '#303030',\n headerbarBorderColor: 'rgba(255, 255, 255, 0.1)',\n headerbarFgColor: 'rgba(255, 255, 255, 0.87)',\n headerbarShadeColor: 'rgba(0, 0, 0, 0.36)',\n hoverOverlay: 'rgba(255, 255, 255, 0.08)',\n layoutBreakpointMedium: 550,\n layoutBreakpointNarrow: 400,\n layoutBreakpointWide: 860,\n layoutContentPadding: 24,\n layoutContentPaddingCompact: 12,\n layoutContentPaddingSpacious: 36,\n layoutSidebarDefaultWidth: 240,\n layoutSidebarMaxWidth: 280,\n layoutSidebarMinWidth: 180,\n layoutSidebarRailWidth: 56,\n light1: '#fff',\n light2: '#f6f5f4',\n light3: '#deddda',\n light4: '#c0bfbc',\n light5: '#9a9996',\n lineHeightBody: 1.5,\n lineHeightHeading: 1.2,\n lineHeightTitle: 1.2,\n newBgColor: '#9141ac',\n newColor: '#9141ac',\n newFgColor: '#fff',\n opacityBorder: 0.15,\n opacityDim: 0.55,\n opacityDisabled: 0.5,\n orange1: '#ffbe6f',\n orange2: '#ffa348',\n orange3: '#ff7800',\n orange4: '#e66100',\n orange5: '#c64600',\n popoverBgColor: '#383838',\n popoverFgColor: 'rgba(255, 255, 255, 0.87)',\n purple1: '#dc8add',\n purple2: '#c061cb',\n purple3: '#9141ac',\n purple4: '#813d9c',\n purple5: '#613583',\n radiusLg: 12,\n radiusMd: 8,\n radiusPill: 9999,\n radiusSm: 4,\n radiusXl: 15,\n red1: '#f66151',\n red2: '#ed333b',\n red3: '#e01b24',\n red4: '#c01c28',\n red5: '#a51d2d',\n sidebarBgColor: '#303030',\n sidebarFgColor: 'rgba(255, 255, 255, 0.87)',\n skeletonBaseColor: 'rgba(255, 255, 255, 0.12)',\n skeletonHighlightColor: 'rgba(255, 255, 255, 0.18)',\n space1: 6,\n space2: 12,\n space3: 18,\n space4: 24,\n space5: 36,\n space6: 48,\n successBgColor: '#2ec27e',\n successColor: '#2ec27e',\n successFgColor: '#fff',\n viewBgColor: '#1e1e1e',\n viewFgColor: 'rgba(255, 255, 255, 0.87)',\n warningBgColor: '#f6d32d',\n warningColor: '#e5a50a',\n warningFgColor: 'rgba(0, 0, 0, 0.8)',\n windowBgColor: '#242424',\n windowFgColor: 'rgba(255, 255, 255, 0.87)',\n yellow1: '#f9f06b',\n yellow2: '#f8e45c',\n yellow3: '#f6d32d',\n yellow4: '#f5c211',\n yellow5: '#e5a50a',\n} as const;\n\nexport const highContrastTheme: GnomeThemeTokens = {\n accentBgColor: '#1a5fb4',\n accentColor: '#1a5fb4',\n accentFgColor: '#fff',\n activeOverlay: 'rgba(0, 0, 0, 0.22)',\n blockquoteBorderWidth: 3,\n blue1: '#99c1f1',\n blue2: '#62a0ea',\n blue3: '#3584e4',\n blue4: '#1c71d8',\n blue5: '#1a5fb4',\n borderSubtle: 'rgba(0, 0, 0, 0.5)',\n brown1: '#cdab8f',\n brown2: '#b5835a',\n brown3: '#986a44',\n brown4: '#865e3c',\n brown5: '#63452c',\n cardBgColor: '#fff',\n cardFgColor: 'rgba(0, 0, 0, 0.8)',\n cardShadeColor: 'rgba(0, 0, 0, 0.12)',\n dark1: '#77767b',\n dark2: '#5e5c64',\n dark3: '#3d3846',\n dark4: '#241f31',\n dark5: '#000',\n destructiveBgColor: '#a51d2d',\n destructiveColor: '#a51d2d',\n destructiveFgColor: '#fff',\n dialogBackdropBlur: 4,\n dialogBackdropColor: 'rgba(0, 0, 0, 0.5)',\n dialogBgColor: '#fafafa',\n dialogFgColor: 'rgba(0, 0, 0, 0.8)',\n dividerColor: 'rgba(0, 0, 0, 0.2)',\n durationFast: 100,\n durationNormal: 200,\n durationSlow: 400,\n easingDefault: [0.25, 0.46, 0.45, 0.94],\n errorBgColor: '#a51d2d',\n errorColor: '#a51d2d',\n errorFgColor: '#fff',\n focusRingColor: '#1a5fb4',\n focusRingOffset: 2,\n focusRingWidth: 3,\n fontFamily: 'Adwaita Sans',\n fontFamilyMono: 'Adwaita Mono',\n fontSizeBody: 16,\n fontSizeCaption: 12,\n fontSizeLargeTitle: 36,\n fontSizeTitle1: 30,\n fontSizeTitle2: 24,\n fontSizeTitle3: 20,\n fontSizeTitle4: 18,\n fontWeightBold: 700,\n fontWeightNormal: 400,\n fontWeightSemibold: 600,\n green1: '#8ff0a4',\n green2: '#57e389',\n green3: '#33d17a',\n green4: '#2ec27e',\n green5: '#26a269',\n headerbarBgColor: '#ebebeb',\n headerbarBorderColor: 'rgba(0, 0, 0, 0.12)',\n headerbarFgColor: 'rgba(0, 0, 0, 0.8)',\n headerbarShadeColor: 'rgba(0, 0, 0, 0.12)',\n hoverOverlay: 'rgba(0, 0, 0, 0.12)',\n layoutBreakpointMedium: 550,\n layoutBreakpointNarrow: 400,\n layoutBreakpointWide: 860,\n layoutContentPadding: 24,\n layoutContentPaddingCompact: 12,\n layoutContentPaddingSpacious: 36,\n layoutSidebarDefaultWidth: 240,\n layoutSidebarMaxWidth: 280,\n layoutSidebarMinWidth: 180,\n layoutSidebarRailWidth: 56,\n light1: '#fff',\n light2: '#f6f5f4',\n light3: '#deddda',\n light4: '#c0bfbc',\n light5: '#9a9996',\n lineHeightBody: 1.5,\n lineHeightHeading: 1.2,\n lineHeightTitle: 1.2,\n newBgColor: '#613583',\n newColor: '#613583',\n newFgColor: '#fff',\n opacityBorder: 0.4,\n opacityDim: 0.7,\n opacityDisabled: 0.6,\n orange1: '#ffbe6f',\n orange2: '#ffa348',\n orange3: '#ff7800',\n orange4: '#e66100',\n orange5: '#c64600',\n popoverBgColor: '#fff',\n popoverFgColor: 'rgba(0, 0, 0, 0.8)',\n purple1: '#dc8add',\n purple2: '#c061cb',\n purple3: '#9141ac',\n purple4: '#813d9c',\n purple5: '#613583',\n radiusLg: 12,\n radiusMd: 8,\n radiusPill: 9999,\n radiusSm: 4,\n radiusXl: 15,\n red1: '#f66151',\n red2: '#ed333b',\n red3: '#e01b24',\n red4: '#c01c28',\n red5: '#a51d2d',\n sidebarBgColor: '#ebebeb',\n sidebarFgColor: 'rgba(0, 0, 0, 0.8)',\n skeletonBaseColor: 'rgba(0, 0, 0, 0.09)',\n skeletonHighlightColor: 'rgba(255, 255, 255, 0.55)',\n space1: 6,\n space2: 12,\n space3: 18,\n space4: 24,\n space5: 36,\n space6: 48,\n successBgColor: '#26a269',\n successColor: '#26a269',\n successFgColor: '#fff',\n viewBgColor: '#fff',\n viewFgColor: 'rgba(0, 0, 0, 0.8)',\n warningBgColor: '#e66100',\n warningColor: '#c64600',\n warningFgColor: 'rgba(0, 0, 0, 0.8)',\n windowBgColor: '#fafafa',\n windowFgColor: 'rgba(0, 0, 0, 0.8)',\n yellow1: '#f9f06b',\n yellow2: '#f8e45c',\n yellow3: '#f6d32d',\n yellow4: '#f5c211',\n yellow5: '#e5a50a',\n} as const;\n\nexport const highContrastDarkTheme: GnomeThemeTokens = {\n accentBgColor: '#62a0ea',\n accentColor: '#99c1f1',\n accentFgColor: '#fff',\n activeOverlay: 'rgba(255, 255, 255, 0.22)',\n blockquoteBorderWidth: 3,\n blue1: '#99c1f1',\n blue2: '#62a0ea',\n blue3: '#3584e4',\n blue4: '#1c71d8',\n blue5: '#1a5fb4',\n borderSubtle: 'rgba(255, 255, 255, 0.5)',\n brown1: '#cdab8f',\n brown2: '#b5835a',\n brown3: '#986a44',\n brown4: '#865e3c',\n brown5: '#63452c',\n cardBgColor: '#383838',\n cardFgColor: '#fff',\n cardShadeColor: 'rgba(255, 255, 255, 0.12)',\n dark1: '#77767b',\n dark2: '#5e5c64',\n dark3: '#3d3846',\n dark4: '#241f31',\n dark5: '#000',\n destructiveBgColor: '#ed333b',\n destructiveColor: '#f66151',\n destructiveFgColor: '#fff',\n dialogBackdropBlur: 4,\n dialogBackdropColor: 'rgba(0, 0, 0, 0.65)',\n dialogBgColor: '#242424',\n dialogFgColor: '#fff',\n dividerColor: 'rgba(255, 255, 255, 0.2)',\n durationFast: 100,\n durationNormal: 200,\n durationSlow: 400,\n easingDefault: [0.25, 0.46, 0.45, 0.94],\n errorBgColor: '#ed333b',\n errorColor: '#f66151',\n errorFgColor: '#fff',\n focusRingColor: '#99c1f1',\n focusRingOffset: 2,\n focusRingWidth: 3,\n fontFamily: 'Adwaita Sans',\n fontFamilyMono: 'Adwaita Mono',\n fontSizeBody: 16,\n fontSizeCaption: 12,\n fontSizeLargeTitle: 36,\n fontSizeTitle1: 30,\n fontSizeTitle2: 24,\n fontSizeTitle3: 20,\n fontSizeTitle4: 18,\n fontWeightBold: 700,\n fontWeightNormal: 400,\n fontWeightSemibold: 600,\n green1: '#8ff0a4',\n green2: '#57e389',\n green3: '#33d17a',\n green4: '#2ec27e',\n green5: '#26a269',\n headerbarBgColor: '#303030',\n headerbarBorderColor: 'rgba(255, 255, 255, 0.3)',\n headerbarFgColor: '#fff',\n headerbarShadeColor: 'rgba(0, 0, 0, 0.36)',\n hoverOverlay: 'rgba(255, 255, 255, 0.12)',\n layoutBreakpointMedium: 550,\n layoutBreakpointNarrow: 400,\n layoutBreakpointWide: 860,\n layoutContentPadding: 24,\n layoutContentPaddingCompact: 12,\n layoutContentPaddingSpacious: 36,\n layoutSidebarDefaultWidth: 240,\n layoutSidebarMaxWidth: 280,\n layoutSidebarMinWidth: 180,\n layoutSidebarRailWidth: 56,\n light1: '#fff',\n light2: '#f6f5f4',\n light3: '#deddda',\n light4: '#c0bfbc',\n light5: '#9a9996',\n lineHeightBody: 1.5,\n lineHeightHeading: 1.2,\n lineHeightTitle: 1.2,\n newBgColor: '#c061cb',\n newColor: '#dc8add',\n newFgColor: '#fff',\n opacityBorder: 0.4,\n opacityDim: 0.7,\n opacityDisabled: 0.6,\n orange1: '#ffbe6f',\n orange2: '#ffa348',\n orange3: '#ff7800',\n orange4: '#e66100',\n orange5: '#c64600',\n popoverBgColor: '#383838',\n popoverFgColor: '#fff',\n purple1: '#dc8add',\n purple2: '#c061cb',\n purple3: '#9141ac',\n purple4: '#813d9c',\n purple5: '#613583',\n radiusLg: 12,\n radiusMd: 8,\n radiusPill: 9999,\n radiusSm: 4,\n radiusXl: 15,\n red1: '#f66151',\n red2: '#ed333b',\n red3: '#e01b24',\n red4: '#c01c28',\n red5: '#a51d2d',\n sidebarBgColor: '#303030',\n sidebarFgColor: '#fff',\n skeletonBaseColor: 'rgba(255, 255, 255, 0.12)',\n skeletonHighlightColor: 'rgba(255, 255, 255, 0.18)',\n space1: 6,\n space2: 12,\n space3: 18,\n space4: 24,\n space5: 36,\n space6: 48,\n successBgColor: '#57e389',\n successColor: '#8ff0a4',\n successFgColor: '#fff',\n viewBgColor: '#1e1e1e',\n viewFgColor: '#fff',\n warningBgColor: '#f8e45c',\n warningColor: '#f9f06b',\n warningFgColor: 'rgba(0, 0, 0, 0.9)',\n windowBgColor: '#242424',\n windowFgColor: '#fff',\n yellow1: '#f9f06b',\n yellow2: '#f8e45c',\n yellow3: '#f6d32d',\n yellow4: '#f5c211',\n yellow5: '#e5a50a',\n} as const;\n\nexport const lightRawTokens = {\n '--gnome-accent-bg-color': '#3584e4',\n '--gnome-accent-color': '#3584e4',\n '--gnome-accent-fg-color': '#fff',\n '--gnome-active-overlay': 'rgb(0 0 0 / 0.12)',\n '--gnome-blockquote-border-width': '3px',\n '--gnome-blue-1': '#99c1f1',\n '--gnome-blue-2': '#62a0ea',\n '--gnome-blue-3': '#3584e4',\n '--gnome-blue-4': '#1c71d8',\n '--gnome-blue-5': '#1a5fb4',\n '--gnome-border-subtle': 'rgb(0 0 0 / 0.15)',\n '--gnome-brown-1': '#cdab8f',\n '--gnome-brown-2': '#b5835a',\n '--gnome-brown-3': '#986a44',\n '--gnome-brown-4': '#865e3c',\n '--gnome-brown-5': '#63452c',\n '--gnome-card-bg-color': '#fff',\n '--gnome-card-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-card-shade-color': 'rgb(0 0 0 / 0.12)',\n '--gnome-dark-1': '#77767b',\n '--gnome-dark-2': '#5e5c64',\n '--gnome-dark-3': '#3d3846',\n '--gnome-dark-4': '#241f31',\n '--gnome-dark-5': '#000',\n '--gnome-destructive-bg-color': '#e01b24',\n '--gnome-destructive-color': '#e01b24',\n '--gnome-destructive-fg-color': '#fff',\n '--gnome-dialog-backdrop-blur': '4px',\n '--gnome-dialog-backdrop-color': 'rgb(0 0 0 / 0.5)',\n '--gnome-dialog-bg-color': '#fafafa',\n '--gnome-dialog-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-divider-color': 'rgb(0 0 0 / 0.07)',\n '--gnome-duration-fast': '100ms',\n '--gnome-duration-normal': '200ms',\n '--gnome-duration-slow': '400ms',\n '--gnome-easing-default': 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',\n '--gnome-error-bg-color': '#e01b24',\n '--gnome-error-color': '#e01b24',\n '--gnome-error-fg-color': '#fff',\n '--gnome-focus-ring-color': '#3584e4',\n '--gnome-focus-ring-offset': '2px',\n '--gnome-focus-ring-width': '3px',\n '--gnome-font-family': '\"Adwaita Sans\", cantarell, \"Inter\", system-ui, sans-serif',\n '--gnome-font-family-mono': '\"Adwaita Mono\", \"Source Code Pro\", monospace',\n '--gnome-font-size-body': '1rem',\n '--gnome-font-size-caption': '0.75rem',\n '--gnome-font-size-large-title': '2.25rem',\n '--gnome-font-size-title-1': '1.875rem',\n '--gnome-font-size-title-2': '1.5rem',\n '--gnome-font-size-title-3': '1.25rem',\n '--gnome-font-size-title-4': '1.125rem',\n '--gnome-font-weight-bold': '700',\n '--gnome-font-weight-normal': '400',\n '--gnome-font-weight-semibold': '600',\n '--gnome-green-1': '#8ff0a4',\n '--gnome-green-2': '#57e389',\n '--gnome-green-3': '#33d17a',\n '--gnome-green-4': '#2ec27e',\n '--gnome-green-5': '#26a269',\n '--gnome-headerbar-bg-color': '#ebebeb',\n '--gnome-headerbar-border-color': 'rgb(0 0 0 / 0.12)',\n '--gnome-headerbar-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-headerbar-shade-color': 'rgb(0 0 0 / 0.12)',\n '--gnome-hover-overlay': 'rgb(0 0 0 / 0.06)',\n '--gnome-layout-breakpoint-medium': '550px',\n '--gnome-layout-breakpoint-narrow': '400px',\n '--gnome-layout-breakpoint-wide': '860px',\n '--gnome-layout-content-padding': '24px',\n '--gnome-layout-content-padding-compact': '12px',\n '--gnome-layout-content-padding-spacious': '36px',\n '--gnome-layout-sidebar-default-width': '240px',\n '--gnome-layout-sidebar-max-width': '280px',\n '--gnome-layout-sidebar-min-width': '180px',\n '--gnome-layout-sidebar-overlay-width': 'clamp( 180px, 75%, 280px )',\n '--gnome-layout-sidebar-rail-width': '56px',\n '--gnome-layout-sidebar-width': 'clamp( 180px, 25%, 280px )',\n '--gnome-light-1': '#fff',\n '--gnome-light-2': '#f6f5f4',\n '--gnome-light-3': '#deddda',\n '--gnome-light-4': '#c0bfbc',\n '--gnome-light-5': '#9a9996',\n '--gnome-line-height-body': '1.5',\n '--gnome-line-height-heading': '1.2',\n '--gnome-line-height-title': '1.2',\n '--gnome-new-bg-color': '#9141ac',\n '--gnome-new-color': '#9141ac',\n '--gnome-new-fg-color': '#fff',\n '--gnome-opacity-border': '0.15',\n '--gnome-opacity-dim': '0.55',\n '--gnome-opacity-disabled': '0.5',\n '--gnome-orange-1': '#ffbe6f',\n '--gnome-orange-2': '#ffa348',\n '--gnome-orange-3': '#ff7800',\n '--gnome-orange-4': '#e66100',\n '--gnome-orange-5': '#c64600',\n '--gnome-popover-bg-color': '#fff',\n '--gnome-popover-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-purple-1': '#dc8add',\n '--gnome-purple-2': '#c061cb',\n '--gnome-purple-3': '#9141ac',\n '--gnome-purple-4': '#813d9c',\n '--gnome-purple-5': '#613583',\n '--gnome-radius-lg': '12px',\n '--gnome-radius-md': '8px',\n '--gnome-radius-pill': '9999px',\n '--gnome-radius-sm': '4px',\n '--gnome-radius-xl': '15px',\n '--gnome-red-1': '#f66151',\n '--gnome-red-2': '#ed333b',\n '--gnome-red-3': '#e01b24',\n '--gnome-red-4': '#c01c28',\n '--gnome-red-5': '#a51d2d',\n '--gnome-shadow-lg':\n '0 3px 9px 1px rgb(0 0 0 / 0.1), 0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(28 31 31 / 0.9)',\n '--gnome-shadow-md': '0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(0 0 0 / 0.08)',\n '--gnome-shadow-sm': '0 1px 2px rgb(0 0 0 / 0.12)',\n '--gnome-sidebar-bg-color': '#ebebeb',\n '--gnome-sidebar-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-skeleton-base-color': 'rgb(0 0 0 / 0.09)',\n '--gnome-skeleton-highlight-color': 'rgb(255 255 255 / 0.55)',\n '--gnome-space-1': '6px',\n '--gnome-space-2': '12px',\n '--gnome-space-3': '18px',\n '--gnome-space-4': '24px',\n '--gnome-space-5': '36px',\n '--gnome-space-6': '48px',\n '--gnome-success-bg-color': '#2ec27e',\n '--gnome-success-color': '#2ec27e',\n '--gnome-success-fg-color': '#fff',\n '--gnome-view-bg-color': '#fff',\n '--gnome-view-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-warning-bg-color': '#f6d32d',\n '--gnome-warning-color': '#e5a50a',\n '--gnome-warning-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-window-bg-color': '#fafafa',\n '--gnome-window-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-yellow-1': '#f9f06b',\n '--gnome-yellow-2': '#f8e45c',\n '--gnome-yellow-3': '#f6d32d',\n '--gnome-yellow-4': '#f5c211',\n '--gnome-yellow-5': '#e5a50a',\n} as const;\n\nexport const darkRawTokens = {\n '--gnome-accent-bg-color': '#3584e4',\n '--gnome-accent-color': '#62a0ea',\n '--gnome-accent-fg-color': '#fff',\n '--gnome-active-overlay': 'rgb(255 255 255 / 0.14)',\n '--gnome-blockquote-border-width': '3px',\n '--gnome-blue-1': '#99c1f1',\n '--gnome-blue-2': '#62a0ea',\n '--gnome-blue-3': '#3584e4',\n '--gnome-blue-4': '#1c71d8',\n '--gnome-blue-5': '#1a5fb4',\n '--gnome-border-subtle': 'rgb(255 255 255 / 0.15)',\n '--gnome-brown-1': '#cdab8f',\n '--gnome-brown-2': '#b5835a',\n '--gnome-brown-3': '#986a44',\n '--gnome-brown-4': '#865e3c',\n '--gnome-brown-5': '#63452c',\n '--gnome-card-bg-color': '#383838',\n '--gnome-card-fg-color': 'rgb(255 255 255 / 0.87)',\n '--gnome-card-shade-color': 'rgb(255 255 255 / 0.12)',\n '--gnome-dark-1': '#77767b',\n '--gnome-dark-2': '#5e5c64',\n '--gnome-dark-3': '#3d3846',\n '--gnome-dark-4': '#241f31',\n '--gnome-dark-5': '#000',\n '--gnome-destructive-bg-color': '#e01b24',\n '--gnome-destructive-color': '#e01b24',\n '--gnome-destructive-fg-color': '#fff',\n '--gnome-dialog-backdrop-blur': '4px',\n '--gnome-dialog-backdrop-color': 'rgb(0 0 0 / 0.65)',\n '--gnome-dialog-bg-color': '#242424',\n '--gnome-dialog-fg-color': 'rgb(255 255 255 / 0.87)',\n '--gnome-divider-color': 'rgb(255 255 255 / 0.07)',\n '--gnome-duration-fast': '100ms',\n '--gnome-duration-normal': '200ms',\n '--gnome-duration-slow': '400ms',\n '--gnome-easing-default': 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',\n '--gnome-error-bg-color': '#e01b24',\n '--gnome-error-color': '#e01b24',\n '--gnome-error-fg-color': '#fff',\n '--gnome-focus-ring-color': '#62a0ea',\n '--gnome-focus-ring-offset': '2px',\n '--gnome-focus-ring-width': '3px',\n '--gnome-font-family': '\"Adwaita Sans\", cantarell, \"Inter\", system-ui, sans-serif',\n '--gnome-font-family-mono': '\"Adwaita Mono\", \"Source Code Pro\", monospace',\n '--gnome-font-size-body': '1rem',\n '--gnome-font-size-caption': '0.75rem',\n '--gnome-font-size-large-title': '2.25rem',\n '--gnome-font-size-title-1': '1.875rem',\n '--gnome-font-size-title-2': '1.5rem',\n '--gnome-font-size-title-3': '1.25rem',\n '--gnome-font-size-title-4': '1.125rem',\n '--gnome-font-weight-bold': '700',\n '--gnome-font-weight-normal': '400',\n '--gnome-font-weight-semibold': '600',\n '--gnome-green-1': '#8ff0a4',\n '--gnome-green-2': '#57e389',\n '--gnome-green-3': '#33d17a',\n '--gnome-green-4': '#2ec27e',\n '--gnome-green-5': '#26a269',\n '--gnome-headerbar-bg-color': '#303030',\n '--gnome-headerbar-border-color': 'rgb(255 255 255 / 0.1)',\n '--gnome-headerbar-fg-color': 'rgb(255 255 255 / 0.87)',\n '--gnome-headerbar-shade-color': 'rgb(0 0 0 / 0.36)',\n '--gnome-hover-overlay': 'rgb(255 255 255 / 0.08)',\n '--gnome-layout-breakpoint-medium': '550px',\n '--gnome-layout-breakpoint-narrow': '400px',\n '--gnome-layout-breakpoint-wide': '860px',\n '--gnome-layout-content-padding': '24px',\n '--gnome-layout-content-padding-compact': '12px',\n '--gnome-layout-content-padding-spacious': '36px',\n '--gnome-layout-sidebar-default-width': '240px',\n '--gnome-layout-sidebar-max-width': '280px',\n '--gnome-layout-sidebar-min-width': '180px',\n '--gnome-layout-sidebar-overlay-width': 'clamp( 180px, 75%, 280px )',\n '--gnome-layout-sidebar-rail-width': '56px',\n '--gnome-layout-sidebar-width': 'clamp( 180px, 25%, 280px )',\n '--gnome-light-1': '#fff',\n '--gnome-light-2': '#f6f5f4',\n '--gnome-light-3': '#deddda',\n '--gnome-light-4': '#c0bfbc',\n '--gnome-light-5': '#9a9996',\n '--gnome-line-height-body': '1.5',\n '--gnome-line-height-heading': '1.2',\n '--gnome-line-height-title': '1.2',\n '--gnome-new-bg-color': '#9141ac',\n '--gnome-new-color': '#9141ac',\n '--gnome-new-fg-color': '#fff',\n '--gnome-opacity-border': '0.15',\n '--gnome-opacity-dim': '0.55',\n '--gnome-opacity-disabled': '0.5',\n '--gnome-orange-1': '#ffbe6f',\n '--gnome-orange-2': '#ffa348',\n '--gnome-orange-3': '#ff7800',\n '--gnome-orange-4': '#e66100',\n '--gnome-orange-5': '#c64600',\n '--gnome-popover-bg-color': '#383838',\n '--gnome-popover-fg-color': 'rgb(255 255 255 / 0.87)',\n '--gnome-purple-1': '#dc8add',\n '--gnome-purple-2': '#c061cb',\n '--gnome-purple-3': '#9141ac',\n '--gnome-purple-4': '#813d9c',\n '--gnome-purple-5': '#613583',\n '--gnome-radius-lg': '12px',\n '--gnome-radius-md': '8px',\n '--gnome-radius-pill': '9999px',\n '--gnome-radius-sm': '4px',\n '--gnome-radius-xl': '15px',\n '--gnome-red-1': '#f66151',\n '--gnome-red-2': '#ed333b',\n '--gnome-red-3': '#e01b24',\n '--gnome-red-4': '#c01c28',\n '--gnome-red-5': '#a51d2d',\n '--gnome-shadow-lg':\n '0 3px 9px 1px rgb(0 0 0 / 0.1), 0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(28 31 31 / 0.9)',\n '--gnome-shadow-md': '0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(0 0 0 / 0.08)',\n '--gnome-shadow-sm': '0 1px 2px rgb(0 0 0 / 0.12)',\n '--gnome-sidebar-bg-color': '#303030',\n '--gnome-sidebar-fg-color': 'rgb(255 255 255 / 0.87)',\n '--gnome-skeleton-base-color': 'rgb(255 255 255 / 0.12)',\n '--gnome-skeleton-highlight-color': 'rgb(255 255 255 / 0.18)',\n '--gnome-space-1': '6px',\n '--gnome-space-2': '12px',\n '--gnome-space-3': '18px',\n '--gnome-space-4': '24px',\n '--gnome-space-5': '36px',\n '--gnome-space-6': '48px',\n '--gnome-success-bg-color': '#2ec27e',\n '--gnome-success-color': '#2ec27e',\n '--gnome-success-fg-color': '#fff',\n '--gnome-view-bg-color': '#1e1e1e',\n '--gnome-view-fg-color': 'rgb(255 255 255 / 0.87)',\n '--gnome-warning-bg-color': '#f6d32d',\n '--gnome-warning-color': '#e5a50a',\n '--gnome-warning-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-window-bg-color': '#242424',\n '--gnome-window-fg-color': 'rgb(255 255 255 / 0.87)',\n '--gnome-yellow-1': '#f9f06b',\n '--gnome-yellow-2': '#f8e45c',\n '--gnome-yellow-3': '#f6d32d',\n '--gnome-yellow-4': '#f5c211',\n '--gnome-yellow-5': '#e5a50a',\n} as const;\n\nexport const highContrastRawTokens = {\n '--gnome-accent-bg-color': '#1a5fb4',\n '--gnome-accent-color': '#1a5fb4',\n '--gnome-accent-fg-color': '#fff',\n '--gnome-active-overlay': 'rgb(0 0 0 / 0.22)',\n '--gnome-blockquote-border-width': '3px',\n '--gnome-blue-1': '#99c1f1',\n '--gnome-blue-2': '#62a0ea',\n '--gnome-blue-3': '#3584e4',\n '--gnome-blue-4': '#1c71d8',\n '--gnome-blue-5': '#1a5fb4',\n '--gnome-border-subtle': 'rgb(0 0 0 / 0.5)',\n '--gnome-brown-1': '#cdab8f',\n '--gnome-brown-2': '#b5835a',\n '--gnome-brown-3': '#986a44',\n '--gnome-brown-4': '#865e3c',\n '--gnome-brown-5': '#63452c',\n '--gnome-card-bg-color': '#fff',\n '--gnome-card-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-card-shade-color': 'rgb(0 0 0 / 0.12)',\n '--gnome-dark-1': '#77767b',\n '--gnome-dark-2': '#5e5c64',\n '--gnome-dark-3': '#3d3846',\n '--gnome-dark-4': '#241f31',\n '--gnome-dark-5': '#000',\n '--gnome-destructive-bg-color': '#a51d2d',\n '--gnome-destructive-color': '#a51d2d',\n '--gnome-destructive-fg-color': '#fff',\n '--gnome-dialog-backdrop-blur': '4px',\n '--gnome-dialog-backdrop-color': 'rgb(0 0 0 / 0.5)',\n '--gnome-dialog-bg-color': '#fafafa',\n '--gnome-dialog-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-divider-color': 'rgb(0 0 0 / 0.2)',\n '--gnome-duration-fast': '100ms',\n '--gnome-duration-normal': '200ms',\n '--gnome-duration-slow': '400ms',\n '--gnome-easing-default': 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',\n '--gnome-error-bg-color': '#a51d2d',\n '--gnome-error-color': '#a51d2d',\n '--gnome-error-fg-color': '#fff',\n '--gnome-focus-ring-color': '#1a5fb4',\n '--gnome-focus-ring-offset': '2px',\n '--gnome-focus-ring-width': '3px',\n '--gnome-font-family': '\"Adwaita Sans\", cantarell, \"Inter\", system-ui, sans-serif',\n '--gnome-font-family-mono': '\"Adwaita Mono\", \"Source Code Pro\", monospace',\n '--gnome-font-size-body': '1rem',\n '--gnome-font-size-caption': '0.75rem',\n '--gnome-font-size-large-title': '2.25rem',\n '--gnome-font-size-title-1': '1.875rem',\n '--gnome-font-size-title-2': '1.5rem',\n '--gnome-font-size-title-3': '1.25rem',\n '--gnome-font-size-title-4': '1.125rem',\n '--gnome-font-weight-bold': '700',\n '--gnome-font-weight-normal': '400',\n '--gnome-font-weight-semibold': '600',\n '--gnome-green-1': '#8ff0a4',\n '--gnome-green-2': '#57e389',\n '--gnome-green-3': '#33d17a',\n '--gnome-green-4': '#2ec27e',\n '--gnome-green-5': '#26a269',\n '--gnome-headerbar-bg-color': '#ebebeb',\n '--gnome-headerbar-border-color': 'rgb(0 0 0 / 0.12)',\n '--gnome-headerbar-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-headerbar-shade-color': 'rgb(0 0 0 / 0.12)',\n '--gnome-hover-overlay': 'rgb(0 0 0 / 0.12)',\n '--gnome-layout-breakpoint-medium': '550px',\n '--gnome-layout-breakpoint-narrow': '400px',\n '--gnome-layout-breakpoint-wide': '860px',\n '--gnome-layout-content-padding': '24px',\n '--gnome-layout-content-padding-compact': '12px',\n '--gnome-layout-content-padding-spacious': '36px',\n '--gnome-layout-sidebar-default-width': '240px',\n '--gnome-layout-sidebar-max-width': '280px',\n '--gnome-layout-sidebar-min-width': '180px',\n '--gnome-layout-sidebar-overlay-width': 'clamp( 180px, 75%, 280px )',\n '--gnome-layout-sidebar-rail-width': '56px',\n '--gnome-layout-sidebar-width': 'clamp( 180px, 25%, 280px )',\n '--gnome-light-1': '#fff',\n '--gnome-light-2': '#f6f5f4',\n '--gnome-light-3': '#deddda',\n '--gnome-light-4': '#c0bfbc',\n '--gnome-light-5': '#9a9996',\n '--gnome-line-height-body': '1.5',\n '--gnome-line-height-heading': '1.2',\n '--gnome-line-height-title': '1.2',\n '--gnome-new-bg-color': '#613583',\n '--gnome-new-color': '#613583',\n '--gnome-new-fg-color': '#fff',\n '--gnome-opacity-border': '0.4',\n '--gnome-opacity-dim': '0.7',\n '--gnome-opacity-disabled': '0.6',\n '--gnome-orange-1': '#ffbe6f',\n '--gnome-orange-2': '#ffa348',\n '--gnome-orange-3': '#ff7800',\n '--gnome-orange-4': '#e66100',\n '--gnome-orange-5': '#c64600',\n '--gnome-popover-bg-color': '#fff',\n '--gnome-popover-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-purple-1': '#dc8add',\n '--gnome-purple-2': '#c061cb',\n '--gnome-purple-3': '#9141ac',\n '--gnome-purple-4': '#813d9c',\n '--gnome-purple-5': '#613583',\n '--gnome-radius-lg': '12px',\n '--gnome-radius-md': '8px',\n '--gnome-radius-pill': '9999px',\n '--gnome-radius-sm': '4px',\n '--gnome-radius-xl': '15px',\n '--gnome-red-1': '#f66151',\n '--gnome-red-2': '#ed333b',\n '--gnome-red-3': '#e01b24',\n '--gnome-red-4': '#c01c28',\n '--gnome-red-5': '#a51d2d',\n '--gnome-shadow-lg':\n '0 3px 9px 1px rgb(0 0 0 / 0.1), 0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(28 31 31 / 0.9)',\n '--gnome-shadow-md': '0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(0 0 0 / 0.08)',\n '--gnome-shadow-sm': '0 1px 2px rgb(0 0 0 / 0.12)',\n '--gnome-sidebar-bg-color': '#ebebeb',\n '--gnome-sidebar-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-skeleton-base-color': 'rgb(0 0 0 / 0.09)',\n '--gnome-skeleton-highlight-color': 'rgb(255 255 255 / 0.55)',\n '--gnome-space-1': '6px',\n '--gnome-space-2': '12px',\n '--gnome-space-3': '18px',\n '--gnome-space-4': '24px',\n '--gnome-space-5': '36px',\n '--gnome-space-6': '48px',\n '--gnome-success-bg-color': '#26a269',\n '--gnome-success-color': '#26a269',\n '--gnome-success-fg-color': '#fff',\n '--gnome-view-bg-color': '#fff',\n '--gnome-view-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-warning-bg-color': '#e66100',\n '--gnome-warning-color': '#c64600',\n '--gnome-warning-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-window-bg-color': '#fafafa',\n '--gnome-window-fg-color': 'rgb(0 0 0 / 0.8)',\n '--gnome-yellow-1': '#f9f06b',\n '--gnome-yellow-2': '#f8e45c',\n '--gnome-yellow-3': '#f6d32d',\n '--gnome-yellow-4': '#f5c211',\n '--gnome-yellow-5': '#e5a50a',\n} as const;\n\nexport const highContrastDarkRawTokens = {\n '--gnome-accent-bg-color': '#62a0ea',\n '--gnome-accent-color': '#99c1f1',\n '--gnome-accent-fg-color': '#fff',\n '--gnome-active-overlay': 'rgb(255 255 255 / 0.22)',\n '--gnome-blockquote-border-width': '3px',\n '--gnome-blue-1': '#99c1f1',\n '--gnome-blue-2': '#62a0ea',\n '--gnome-blue-3': '#3584e4',\n '--gnome-blue-4': '#1c71d8',\n '--gnome-blue-5': '#1a5fb4',\n '--gnome-border-subtle': 'rgb(255 255 255 / 0.5)',\n '--gnome-brown-1': '#cdab8f',\n '--gnome-brown-2': '#b5835a',\n '--gnome-brown-3': '#986a44',\n '--gnome-brown-4': '#865e3c',\n '--gnome-brown-5': '#63452c',\n '--gnome-card-bg-color': '#383838',\n '--gnome-card-fg-color': '#fff',\n '--gnome-card-shade-color': 'rgb(255 255 255 / 0.12)',\n '--gnome-dark-1': '#77767b',\n '--gnome-dark-2': '#5e5c64',\n '--gnome-dark-3': '#3d3846',\n '--gnome-dark-4': '#241f31',\n '--gnome-dark-5': '#000',\n '--gnome-destructive-bg-color': '#ed333b',\n '--gnome-destructive-color': '#f66151',\n '--gnome-destructive-fg-color': '#fff',\n '--gnome-dialog-backdrop-blur': '4px',\n '--gnome-dialog-backdrop-color': 'rgb(0 0 0 / 0.65)',\n '--gnome-dialog-bg-color': '#242424',\n '--gnome-dialog-fg-color': '#fff',\n '--gnome-divider-color': 'rgb(255 255 255 / 0.2)',\n '--gnome-duration-fast': '100ms',\n '--gnome-duration-normal': '200ms',\n '--gnome-duration-slow': '400ms',\n '--gnome-easing-default': 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',\n '--gnome-error-bg-color': '#ed333b',\n '--gnome-error-color': '#f66151',\n '--gnome-error-fg-color': '#fff',\n '--gnome-focus-ring-color': '#99c1f1',\n '--gnome-focus-ring-offset': '2px',\n '--gnome-focus-ring-width': '3px',\n '--gnome-font-family': '\"Adwaita Sans\", cantarell, \"Inter\", system-ui, sans-serif',\n '--gnome-font-family-mono': '\"Adwaita Mono\", \"Source Code Pro\", monospace',\n '--gnome-font-size-body': '1rem',\n '--gnome-font-size-caption': '0.75rem',\n '--gnome-font-size-large-title': '2.25rem',\n '--gnome-font-size-title-1': '1.875rem',\n '--gnome-font-size-title-2': '1.5rem',\n '--gnome-font-size-title-3': '1.25rem',\n '--gnome-font-size-title-4': '1.125rem',\n '--gnome-font-weight-bold': '700',\n '--gnome-font-weight-normal': '400',\n '--gnome-font-weight-semibold': '600',\n '--gnome-green-1': '#8ff0a4',\n '--gnome-green-2': '#57e389',\n '--gnome-green-3': '#33d17a',\n '--gnome-green-4': '#2ec27e',\n '--gnome-green-5': '#26a269',\n '--gnome-headerbar-bg-color': '#303030',\n '--gnome-headerbar-border-color': 'rgb(255 255 255 / 0.3)',\n '--gnome-headerbar-fg-color': '#fff',\n '--gnome-headerbar-shade-color': 'rgb(0 0 0 / 0.36)',\n '--gnome-hover-overlay': 'rgb(255 255 255 / 0.12)',\n '--gnome-layout-breakpoint-medium': '550px',\n '--gnome-layout-breakpoint-narrow': '400px',\n '--gnome-layout-breakpoint-wide': '860px',\n '--gnome-layout-content-padding': '24px',\n '--gnome-layout-content-padding-compact': '12px',\n '--gnome-layout-content-padding-spacious': '36px',\n '--gnome-layout-sidebar-default-width': '240px',\n '--gnome-layout-sidebar-max-width': '280px',\n '--gnome-layout-sidebar-min-width': '180px',\n '--gnome-layout-sidebar-overlay-width': 'clamp( 180px, 75%, 280px )',\n '--gnome-layout-sidebar-rail-width': '56px',\n '--gnome-layout-sidebar-width': 'clamp( 180px, 25%, 280px )',\n '--gnome-light-1': '#fff',\n '--gnome-light-2': '#f6f5f4',\n '--gnome-light-3': '#deddda',\n '--gnome-light-4': '#c0bfbc',\n '--gnome-light-5': '#9a9996',\n '--gnome-line-height-body': '1.5',\n '--gnome-line-height-heading': '1.2',\n '--gnome-line-height-title': '1.2',\n '--gnome-new-bg-color': '#c061cb',\n '--gnome-new-color': '#dc8add',\n '--gnome-new-fg-color': '#fff',\n '--gnome-opacity-border': '0.4',\n '--gnome-opacity-dim': '0.7',\n '--gnome-opacity-disabled': '0.6',\n '--gnome-orange-1': '#ffbe6f',\n '--gnome-orange-2': '#ffa348',\n '--gnome-orange-3': '#ff7800',\n '--gnome-orange-4': '#e66100',\n '--gnome-orange-5': '#c64600',\n '--gnome-popover-bg-color': '#383838',\n '--gnome-popover-fg-color': '#fff',\n '--gnome-purple-1': '#dc8add',\n '--gnome-purple-2': '#c061cb',\n '--gnome-purple-3': '#9141ac',\n '--gnome-purple-4': '#813d9c',\n '--gnome-purple-5': '#613583',\n '--gnome-radius-lg': '12px',\n '--gnome-radius-md': '8px',\n '--gnome-radius-pill': '9999px',\n '--gnome-radius-sm': '4px',\n '--gnome-radius-xl': '15px',\n '--gnome-red-1': '#f66151',\n '--gnome-red-2': '#ed333b',\n '--gnome-red-3': '#e01b24',\n '--gnome-red-4': '#c01c28',\n '--gnome-red-5': '#a51d2d',\n '--gnome-shadow-lg':\n '0 3px 9px 1px rgb(0 0 0 / 0.1), 0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(28 31 31 / 0.9)',\n '--gnome-shadow-md': '0 2px 6px 2px rgb(0 0 0 / 0.2), 0 0 0 1px rgb(0 0 0 / 0.08)',\n '--gnome-shadow-sm': '0 1px 2px rgb(0 0 0 / 0.12)',\n '--gnome-sidebar-bg-color': '#303030',\n '--gnome-sidebar-fg-color': '#fff',\n '--gnome-skeleton-base-color': 'rgb(255 255 255 / 0.12)',\n '--gnome-skeleton-highlight-color': 'rgb(255 255 255 / 0.18)',\n '--gnome-space-1': '6px',\n '--gnome-space-2': '12px',\n '--gnome-space-3': '18px',\n '--gnome-space-4': '24px',\n '--gnome-space-5': '36px',\n '--gnome-space-6': '48px',\n '--gnome-success-bg-color': '#57e389',\n '--gnome-success-color': '#8ff0a4',\n '--gnome-success-fg-color': '#fff',\n '--gnome-view-bg-color': '#1e1e1e',\n '--gnome-view-fg-color': '#fff',\n '--gnome-warning-bg-color': '#f8e45c',\n '--gnome-warning-color': '#f9f06b',\n '--gnome-warning-fg-color': 'rgb(0 0 0 / 0.9)',\n '--gnome-window-bg-color': '#242424',\n '--gnome-window-fg-color': '#fff',\n '--gnome-yellow-1': '#f9f06b',\n '--gnome-yellow-2': '#f8e45c',\n '--gnome-yellow-3': '#f6d32d',\n '--gnome-yellow-4': '#f5c211',\n '--gnome-yellow-5': '#e5a50a',\n} as const;\n","import { createContext, useContext, useMemo } from 'react';\nimport type { GnomeResolvedColorScheme, GnomeResolvedContrast } from '@/theme/resolveTheme';\nimport type { GnomeThemeTokens } from '@/theme/tokens.generated';\nimport { lightTheme } from '@/theme/tokens.generated';\nimport type { GnomeAccentColor, GnomeColorScheme, GnomeContrast, GnomeDir } from './resolveContext';\n\nexport interface GnomeContextValue {\n locale: string | undefined;\n dir: GnomeDir;\n numberFormat: Intl.NumberFormatOptions | undefined;\n dateTimeFormat: Intl.DateTimeFormatOptions | undefined;\n colorScheme: GnomeColorScheme;\n resolvedColorScheme: GnomeResolvedColorScheme;\n contrast: GnomeContrast;\n resolvedContrast: GnomeResolvedContrast;\n accentColor: GnomeAccentColor;\n theme: GnomeThemeTokens;\n}\n\nexport const GnomeContext = createContext<GnomeContextValue>({\n locale: undefined,\n dir: 'ltr',\n numberFormat: undefined,\n dateTimeFormat: undefined,\n colorScheme: 'system',\n resolvedColorScheme: 'light',\n contrast: 'system',\n resolvedContrast: 'normal',\n accentColor: 'blue',\n theme: lightTheme,\n});\n\n/** Returns the locale set by the nearest `GnomeProvider`, or `undefined` to use the device locale. */\nexport function useLocale(): string | undefined {\n return useContext(GnomeContext).locale;\n}\n\n/** Returns the text direction set by the nearest `GnomeProvider`. Defaults to the app's `I18nManager.isRTL` state. */\nexport function useDir(): GnomeDir {\n return useContext(GnomeContext).dir;\n}\n\n/** Returns an `Intl.NumberFormat` configured from `GnomeProvider` defaults plus local overrides. */\nexport function useNumberFormatter(options?: Intl.NumberFormatOptions): Intl.NumberFormat {\n const { locale, numberFormat } = useContext(GnomeContext);\n\n return useMemo(\n () => new Intl.NumberFormat(locale, { ...numberFormat, ...options }),\n [locale, numberFormat, options],\n );\n}\n\n/** Returns an `Intl.DateTimeFormat` configured from `GnomeProvider` defaults plus local overrides. */\nexport function useDateTimeFormatter(options?: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n const { locale, dateTimeFormat } = useContext(GnomeContext);\n\n return useMemo(\n () => new Intl.DateTimeFormat(locale, { ...dateTimeFormat, ...options }),\n [locale, dateTimeFormat, options],\n );\n}\n\n/** Returns the color scheme preference set by the nearest `GnomeProvider`. Defaults to `\"system\"`. */\nexport function useColorScheme(): GnomeColorScheme {\n return useContext(GnomeContext).colorScheme;\n}\n\n/** Returns the resolved color scheme (`\"light\"` or `\"dark\"`), accounting for the OS preference when `colorScheme` is `\"system\"`. */\nexport function useResolvedColorScheme(): GnomeResolvedColorScheme {\n return useContext(GnomeContext).resolvedColorScheme;\n}\n\n/** Returns the contrast preference set by the nearest `GnomeProvider`. Defaults to `\"system\"`. */\nexport function useContrast(): GnomeContrast {\n return useContext(GnomeContext).contrast;\n}\n\n/** Returns the resolved contrast (`\"normal\"` or `\"more\"`), accounting for the OS accessibility setting when `contrast` is `\"system\"`. */\nexport function useResolvedContrast(): GnomeResolvedContrast {\n return useContext(GnomeContext).resolvedContrast;\n}\n\n/** Returns the accent color set by the nearest `GnomeProvider`. Defaults to `\"blue\"`. */\nexport function useAccentColor(): GnomeAccentColor {\n return useContext(GnomeContext).accentColor;\n}\n\n/** Returns the fully-resolved theme tokens (colors, spacing, typography, …) for the nearest `GnomeProvider`, accent-adjusted. */\nexport function useGnomeTheme(): GnomeThemeTokens {\n return useContext(GnomeContext).theme;\n}\n","import type { GnomeThemeTokens } from './tokens.generated';\nimport {\n darkTheme,\n highContrastDarkTheme,\n highContrastTheme,\n lightTheme,\n} from './tokens.generated';\n\nexport type GnomeResolvedColorScheme = 'light' | 'dark';\nexport type GnomeResolvedContrast = 'normal' | 'more';\n\nexport interface ResolveGnomeThemeOptions {\n colorScheme: GnomeResolvedColorScheme;\n /** @default 'normal' */\n contrast?: GnomeResolvedContrast;\n}\n\nexport function resolveGnomeTheme({\n colorScheme,\n contrast = 'normal',\n}: ResolveGnomeThemeOptions): GnomeThemeTokens {\n if (contrast === 'more') {\n return colorScheme === 'dark' ? highContrastDarkTheme : highContrastTheme;\n }\n\n return colorScheme === 'dark' ? darkTheme : lightTheme;\n}\n","import type { GnomeResolvedColorScheme, GnomeResolvedContrast } from '@/theme/resolveTheme';\nimport type { GnomeThemeTokens } from '@/theme/tokens.generated';\n\nexport type GnomeColorScheme = GnomeResolvedColorScheme | 'system';\nexport type GnomeContrast = GnomeResolvedContrast | 'system';\nexport type GnomeDir = 'ltr' | 'rtl';\nexport type GnomeNamedAccentColor =\n | 'blue'\n | 'green'\n | 'yellow'\n | 'orange'\n | 'red'\n | 'purple'\n | 'brown';\nexport type GnomeAccentColor = GnomeNamedAccentColor | (string & {});\n\nexport const NAMED_ACCENT_COLORS = new Set<GnomeNamedAccentColor>([\n 'blue',\n 'green',\n 'yellow',\n 'orange',\n 'red',\n 'purple',\n 'brown',\n]);\n\n/**\n * Resolves a `'light' | 'dark' | 'system'` preference against the OS color\n * scheme. `systemColorScheme` is typed loosely (rather than as\n * `GnomeResolvedColorScheme`) so RN's `useColorScheme()` — which can also\n * report `'unspecified'` on Android, besides `null`/`undefined` — can be\n * passed straight through; anything other than `'dark'` resolves to light.\n */\nexport function resolveColorScheme(\n preference: GnomeColorScheme,\n systemColorScheme: string | null | undefined,\n): GnomeResolvedColorScheme {\n if (preference !== 'system') {\n return preference;\n }\n\n return systemColorScheme === 'dark' ? 'dark' : 'light';\n}\n\n/** Resolves a `'normal' | 'more' | 'system'` preference against the OS high-contrast setting. */\nexport function resolveContrast(\n preference: GnomeContrast,\n systemHighContrast: boolean,\n): GnomeResolvedContrast {\n if (preference !== 'system') {\n return preference;\n }\n\n return systemHighContrast ? 'more' : 'normal';\n}\n\n/**\n * Returns `theme` with `accentColor`/`accentBgColor` (and, outside\n * high-contrast, `focusRingColor`) overridden for a non-default accent.\n *\n * Named palette colors (`\"green\"`, `\"red\"`, …) resolve to the matching\n * Adwaita shade for the current color scheme, mirroring\n * `@gnome-ui/react`'s `GnomeProvider`. High-contrast variants keep their\n * own fixed, maximum-contrast focus ring regardless of accent — the same\n * behavior `tokens.css` encodes by not deriving it from `--gnome-accent-color`\n * under `prefers-contrast: more`.\n */\nexport function applyAccentColor(\n theme: GnomeThemeTokens,\n accentColor: GnomeAccentColor,\n resolvedColorScheme: GnomeResolvedColorScheme,\n resolvedContrast: GnomeResolvedContrast,\n): GnomeThemeTokens {\n if (accentColor === 'blue') {\n return theme;\n }\n\n const isNamed = NAMED_ACCENT_COLORS.has(accentColor as GnomeNamedAccentColor);\n const shade = resolvedColorScheme === 'dark' ? 2 : 3;\n const accent = isNamed\n ? (theme[`${accentColor}${shade}` as keyof GnomeThemeTokens] as string)\n : accentColor;\n const accentBg = isNamed\n ? (theme[`${accentColor}3` as keyof GnomeThemeTokens] as string)\n : accentColor;\n\n return {\n ...theme,\n accentColor: accent,\n accentBgColor: accentBg,\n ...(resolvedContrast === 'normal' ? { focusRingColor: accent } : {}),\n };\n}\n","import type { ReactNode } from 'react';\nimport { useEffect, useMemo, useState } from 'react';\nimport {\n AccessibilityInfo,\n I18nManager,\n Platform,\n useColorScheme as useSystemColorScheme,\n} from 'react-native';\nimport { resolveGnomeTheme } from '@/theme/resolveTheme';\nimport { GnomeContext } from './GnomeContext';\nimport type { GnomeAccentColor, GnomeColorScheme, GnomeContrast, GnomeDir } from './resolveContext';\nimport { applyAccentColor, resolveColorScheme, resolveContrast } from './resolveContext';\n\nexport interface GnomeProviderProps {\n /** BCP 47 locale tag (e.g. `\"es-ES\"`, `\"de-DE\"`). When omitted, components fall back to the device locale. */\n locale?: string;\n /**\n * Text direction exposed via context for consumers to branch on (e.g.\n * mirroring icons or flipping a row's layout). Defaults to the app's\n * actual `I18nManager.isRTL` state.\n *\n * Unlike the web `GnomeProvider`, this does **not** call\n * `I18nManager.forceRTL()` — RN's layout direction is a single global\n * flag that requires an app reload to take effect and is normally set\n * once at app bootstrap, not scoped per provider tree.\n */\n dir?: GnomeDir;\n /**\n * Global defaults for number formatting.\n *\n * Use `{ notation: \"compact\", compactDisplay: \"short\" }` for compact\n * values such as `1K`; omit it or set `{ notation: \"standard\" }` for\n * full values such as `1,000`.\n */\n numberFormat?: Intl.NumberFormatOptions;\n /** Global defaults for date/time formatting. */\n dateTimeFormat?: Intl.DateTimeFormatOptions;\n /**\n * Color scheme preference. `\"system\"` (default) follows\n * `Appearance`/`useColorScheme()`. `\"light\"`/`\"dark\"` force that scheme\n * regardless of the OS setting.\n * @default 'system'\n */\n colorScheme?: GnomeColorScheme;\n /**\n * High-contrast preference. `\"system\"` (default) follows the OS\n * accessibility setting where one exists — Android's \"High text\n * contrast\" (`AccessibilityInfo.isHighTextContrastEnabled`) or iOS's\n * \"Increase Contrast\" (`AccessibilityInfo.isDarkerSystemColorsEnabled`)\n * — and falls back to `\"normal\"` where neither is available (e.g. web).\n * @default 'system'\n */\n contrast?: GnomeContrast;\n /**\n * Accent color. Accepts a named GNOME palette color (`\"blue\"`, `\"green\"`,\n * `\"yellow\"`, `\"orange\"`, `\"red\"`, `\"purple\"`, `\"brown\"`) or any RN color\n * string (e.g. `\"#ff0000\"`, `\"rgba(0, 0, 0, 0.5)\"`). Named colors are\n * theme-aware and use the correct shade for light/dark mode\n * automatically. Defaults to `\"blue\"`.\n */\n accentColor?: GnomeAccentColor;\n children: ReactNode;\n}\n\n/**\n * Subscribes to the OS high-contrast accessibility setting.\n *\n * Android exposes \"High text contrast\"; iOS exposes \"Increase Contrast\"\n * (surfaced through `AccessibilityInfo`'s \"darker system colors\" API,\n * which is what that setting maps to natively). Neither exists on other\n * platforms, so this resolves to `false` there.\n */\nfunction useSystemHighContrast(): boolean {\n const [enabled, setEnabled] = useState(false);\n\n useEffect(() => {\n const query =\n Platform.OS === 'android'\n ? AccessibilityInfo.isHighTextContrastEnabled\n : Platform.OS === 'ios'\n ? AccessibilityInfo.isDarkerSystemColorsEnabled\n : undefined;\n\n if (!query) {\n return;\n }\n\n let mounted = true;\n\n (async () => {\n const value = await query();\n\n if (mounted) {\n setEnabled(value);\n }\n })();\n\n const eventName =\n Platform.OS === 'android' ? 'highTextContrastChanged' : 'darkerSystemColorsChanged';\n const subscription = AccessibilityInfo.addEventListener(eventName, setEnabled);\n\n return () => {\n mounted = false;\n subscription.remove();\n };\n }, []);\n\n return enabled;\n}\n\n/**\n * Returns `true` when the OS \"Reduce Motion\" accessibility setting is on\n * (iOS Settings > Accessibility > Motion, Android Settings >\n * Accessibility > Remove animations), for components with a continuously\n * looping `Animated` value (e.g. `Spinner`) to slow down or skip that\n * animation. Unlike `contrast`/`colorScheme`, the web `GnomeProvider` has\n * no corresponding override prop — `prefers-reduced-motion` is a pure CSS\n * media query there, always OS-driven — so this hook mirrors that: it\n * reads `AccessibilityInfo` directly and isn't scoped to `GnomeProvider`'s\n * context, unlike every other hook in this package. `isReduceMotionEnabled`\n * / `reduceMotionChanged` are supported on both iOS and Android (unlike\n * the high-contrast APIs above, which are platform-specific), so no\n * `Platform.OS` branch is needed.\n */\n// eslint-disable-next-line react-refresh/only-export-components\nexport function useReducedMotion(): boolean {\n const [enabled, setEnabled] = useState(false);\n\n useEffect(() => {\n let mounted = true;\n\n (async () => {\n const value = await AccessibilityInfo.isReduceMotionEnabled();\n\n if (mounted) {\n setEnabled(value);\n }\n })();\n\n const subscription = AccessibilityInfo.addEventListener('reduceMotionChanged', setEnabled);\n\n return () => {\n mounted = false;\n subscription.remove();\n };\n }, []);\n\n return enabled;\n}\n\n/** Provides locale, text direction, color scheme, contrast, accent color, and resolved theme tokens to all descendant gnome-ui components. */\nexport const GnomeProvider = ({\n locale,\n dir,\n numberFormat,\n dateTimeFormat,\n colorScheme = 'system',\n contrast = 'system',\n accentColor = 'blue',\n children,\n}: GnomeProviderProps) => {\n const systemColorScheme = useSystemColorScheme();\n const systemHighContrast = useSystemHighContrast();\n\n const resolvedColorScheme = resolveColorScheme(colorScheme, systemColorScheme);\n const resolvedContrast = resolveContrast(contrast, systemHighContrast);\n const resolvedDir = dir ?? (I18nManager.isRTL ? 'rtl' : 'ltr');\n\n const value = useMemo(() => {\n const variantTheme = resolveGnomeTheme({\n colorScheme: resolvedColorScheme,\n contrast: resolvedContrast,\n });\n const theme = applyAccentColor(\n variantTheme,\n accentColor,\n resolvedColorScheme,\n resolvedContrast,\n );\n\n return {\n locale,\n dir: resolvedDir,\n numberFormat,\n dateTimeFormat,\n colorScheme,\n resolvedColorScheme,\n contrast,\n resolvedContrast,\n accentColor,\n theme,\n };\n }, [\n locale,\n resolvedDir,\n numberFormat,\n dateTimeFormat,\n colorScheme,\n resolvedColorScheme,\n contrast,\n resolvedContrast,\n accentColor,\n ]);\n\n return <GnomeContext.Provider value={value}>{children}</GnomeContext.Provider>;\n};\n","import { forwardRef } from 'react';\nimport type { AccessibilityRole, TextProps as RNTextProps, TextStyle } from 'react-native';\nimport { Text as RNText } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type TextVariant =\n | 'large-title'\n | 'title-1'\n | 'title-2'\n | 'title-3'\n | 'title-4'\n | 'heading'\n | 'body'\n | 'document'\n | 'caption'\n | 'caption-heading'\n | 'monospace'\n | 'numeric';\n\nexport type TextColor =\n | 'default'\n | 'dim'\n | 'accent'\n | 'destructive'\n | 'success'\n | 'warning'\n | 'error';\n\n/**\n * Variants the web component renders as `<h1>`–`<h4>`. RN has no heading\n * elements, so they default to the `header` accessibility role instead —\n * the closest native equivalent, and what VoiceOver/TalkBack use to build\n * their heading rotor.\n */\nconst HEADING_VARIANTS: ReadonlySet<TextVariant> = new Set<TextVariant>([\n 'large-title',\n 'title-1',\n 'title-2',\n 'title-3',\n 'title-4',\n 'heading',\n]);\n\nexport interface TextProps extends RNTextProps {\n /** Typography style. Mirrors Adwaita / GNOME HIG text styles. */\n variant?: TextVariant;\n /** Semantic color. */\n color?: TextColor;\n children?: RNTextProps['children'];\n}\n\n/**\n * CSS `line-height` ratios and `letter-spacing` `em` values are relative;\n * RN wants absolute dp for both, so they're resolved against the variant's\n * own font size here.\n */\nfunction getVariantStyle(theme: GnomeThemeTokens, variant: TextVariant): TextStyle {\n const weight = (value: number) => String(value) as TextStyle['fontWeight'];\n\n switch (variant) {\n case 'large-title':\n return {\n fontSize: theme.fontSizeLargeTitle,\n fontWeight: weight(300),\n lineHeight: Math.round(theme.fontSizeLargeTitle * theme.lineHeightHeading),\n letterSpacing: theme.fontSizeLargeTitle * -0.02,\n };\n case 'title-1':\n return {\n fontSize: theme.fontSizeTitle1,\n fontWeight: weight(theme.fontWeightBold),\n lineHeight: Math.round(theme.fontSizeTitle1 * theme.lineHeightHeading),\n };\n case 'title-2':\n return {\n fontSize: theme.fontSizeTitle2,\n fontWeight: weight(theme.fontWeightBold),\n lineHeight: Math.round(theme.fontSizeTitle2 * theme.lineHeightHeading),\n };\n case 'title-3':\n return {\n fontSize: theme.fontSizeTitle3,\n fontWeight: weight(theme.fontWeightBold),\n lineHeight: Math.round(theme.fontSizeTitle3 * theme.lineHeightHeading),\n };\n case 'title-4':\n return {\n fontSize: theme.fontSizeTitle4,\n fontWeight: weight(theme.fontWeightSemibold),\n lineHeight: Math.round(theme.fontSizeTitle4 * theme.lineHeightHeading),\n };\n case 'heading':\n return {\n fontSize: theme.fontSizeBody,\n fontWeight: weight(theme.fontWeightBold),\n lineHeight: Math.round(theme.fontSizeBody * theme.lineHeightHeading),\n };\n case 'document':\n return {\n fontSize: theme.fontSizeBody,\n fontWeight: weight(theme.fontWeightNormal),\n lineHeight: Math.round(theme.fontSizeBody * 1.65),\n };\n case 'caption':\n return {\n fontSize: theme.fontSizeCaption,\n fontWeight: weight(theme.fontWeightNormal),\n lineHeight: Math.round(theme.fontSizeCaption * theme.lineHeightBody),\n };\n case 'caption-heading':\n return {\n fontSize: theme.fontSizeCaption,\n fontWeight: weight(theme.fontWeightSemibold),\n lineHeight: Math.round(theme.fontSizeCaption * theme.lineHeightHeading),\n letterSpacing: theme.fontSizeCaption * 0.06,\n textTransform: 'uppercase',\n };\n case 'monospace':\n return {\n fontFamily: theme.fontFamilyMono,\n fontSize: 14,\n lineHeight: Math.round(14 * theme.lineHeightBody),\n };\n case 'numeric':\n return {\n fontSize: theme.fontSizeBody,\n fontWeight: weight(theme.fontWeightNormal),\n lineHeight: Math.round(theme.fontSizeBody * theme.lineHeightBody),\n fontVariant: ['tabular-nums'],\n };\n default:\n return {\n fontSize: theme.fontSizeBody,\n fontWeight: weight(theme.fontWeightNormal),\n lineHeight: Math.round(theme.fontSizeBody * theme.lineHeightBody),\n };\n }\n}\n\n/**\n * `dim` is an opacity in the web stylesheet rather than a color, so it stays\n * an opacity here — that keeps it correct against any background, the same\n * way `.color-dim` does.\n */\nfunction getColorStyle(theme: GnomeThemeTokens, color: TextColor): TextStyle {\n switch (color) {\n case 'dim':\n return { color: theme.windowFgColor, opacity: theme.opacityDim };\n case 'accent':\n return { color: theme.accentColor };\n case 'destructive':\n return { color: theme.destructiveColor };\n case 'success':\n return { color: theme.successColor };\n case 'warning':\n return { color: theme.warningColor };\n case 'error':\n return { color: theme.errorColor };\n default:\n return { color: theme.windowFgColor };\n }\n}\n\n/**\n * Text component following GNOME Human Interface Guidelines typography styles.\n *\n * Variants map directly to Adwaita style classes:\n * `large-title`, `title-1`–`title-4`, `heading`, `body`, `document`,\n * `caption`, `caption-heading`, `monospace`, `numeric`.\n *\n * Rebuilt on RN's `Text` rather than ported from `@gnome-ui/react`'s DOM-based\n * JSX: there is no element to pick, so `@gnome-ui/react`'s `as` prop is\n * replaced by `accessibilityRole`, which the heading variants default to\n * `\"header\"`.\n *\n * @see https://developer.gnome.org/hig/guidelines/typography.html\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/style-classes.html\n */\nexport const Text = forwardRef<RNText, TextProps>(function Text(\n { variant = 'body', color = 'default', style, children, accessibilityRole, ...textProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const role: AccessibilityRole | undefined =\n accessibilityRole ?? (HEADING_VARIANTS.has(variant) ? 'header' : undefined);\n\n return (\n <RNText\n ref={ref}\n accessibilityRole={role}\n style={[\n { fontFamily: theme.fontFamily },\n getVariantStyle(theme, variant),\n getColorStyle(theme, color),\n style,\n ]}\n {...textProps}\n >\n {children}\n </RNText>\n );\n});\n","import { forwardRef, type ReactNode } from 'react';\nimport type { PressableProps, StyleProp, View, ViewStyle } from 'react-native';\nimport { Pressable, View as RNView, StyleSheet } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type ActionRowVariant = 'default' | 'property';\n\nexport interface ActionRowProps extends Omit<PressableProps, 'children' | 'style'> {\n /** Primary label. */\n title: string;\n /** Secondary line below the title. */\n subtitle?: string;\n /** Icon or image placed at the leading edge. Rendered as-is — size/color it yourself. */\n leading?: ReactNode;\n /**\n * Widget placed at the trailing edge (`Switch`, `Button`, `Text`…).\n * For interactive end widgets prefer a controlled component and stop\n * event propagation inside it so the row's own `onPress` isn't triggered.\n */\n trailing?: ReactNode;\n /**\n * When true the entire row becomes pressable (renders as `Pressable`\n * with `accessibilityRole=\"button\"`). Use for rows that navigate or\n * trigger an action.\n */\n interactive?: boolean;\n /**\n * `\"property\"` flips the visual hierarchy: the subtitle becomes the\n * prominent value and the title shrinks to a dim label above it.\n * Use for read-only property display (e.g. \"OS Version / GNOME 50\").\n * Mirrors the `.property` style class.\n */\n variant?: ActionRowVariant;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Standard settings row with title, optional subtitle, and end widget.\n *\n * Mirrors the Adwaita `AdwActionRow` pattern — the fundamental building\n * block inside a `BoxedList`.\n *\n * Rebuilt with `Pressable`/`View`/`Text` rather than ported from\n * `@gnome-ui/react`'s `<button>`/`<div>` — same `interactive` split as\n * `Card`: a pressed-state overlay tinted by `theme.activeOverlay` stands in\n * for the web's `:hover`/`:active` `background-color` transitions, since RN\n * has no hover state on touch devices and no `color-mix()`. `title`/\n * `subtitle` reuse the library's own `Text` component (`variant=\"body\"`/\n * `\"caption\"`, `color=\"dim\"`) instead of hand-rolled styles, with\n * `numberOfLines={1}` standing in for the web's CSS\n * `text-overflow: ellipsis` truncation.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ActionRow.html\n * @see https://developer.gnome.org/hig/patterns/containers.html\n */\nexport const ActionRow = forwardRef<View, ActionRowProps>(function ActionRow(\n {\n title,\n subtitle,\n leading,\n trailing,\n interactive = false,\n variant = 'default',\n style,\n disabled,\n ...pressableProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const property = variant === 'property';\n\n const baseStyle: ViewStyle = {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: theme.space2,\n paddingHorizontal: theme.space4,\n minHeight: 52,\n width: '100%',\n };\n\n const content = (\n <>\n {leading && <RNView style={{ flexShrink: 0 }}>{leading}</RNView>}\n\n <RNView style={{ flex: 1, gap: 2 }}>\n <Text\n variant={property ? 'caption' : 'body'}\n color={property ? 'dim' : 'default'}\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n >\n {title}\n </Text>\n {subtitle && (\n <Text\n variant={property ? 'body' : 'caption'}\n color={property ? 'default' : 'dim'}\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n >\n {subtitle}\n </Text>\n )}\n </RNView>\n\n {trailing && (\n <RNView\n style={{ flexShrink: 0, flexDirection: 'row', alignItems: 'center', gap: theme.space1 }}\n >\n {trailing}\n </RNView>\n )}\n </>\n );\n\n if (!interactive) {\n return (\n <RNView ref={ref} style={[baseStyle, style]} {...pressableProps}>\n {content}\n </RNView>\n );\n }\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled: !!disabled }}\n style={[\n baseStyle,\n { position: 'relative', opacity: disabled ? theme.opacityDisabled : 1 },\n style,\n ]}\n {...pressableProps}\n >\n {({ pressed }) => (\n <>\n {content}\n {pressed && !disabled ? (\n <RNView\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { backgroundColor: theme.activeOverlay }]}\n />\n ) : null}\n </>\n )}\n </Pressable>\n );\n});\n","import type { useGnomeTheme } from '@/GnomeProvider';\nimport type { IconColor, IconSize } from './Icon';\n\n/**\n * Non-component helpers shared between `Icon` and `AnimatedIcon` — kept in\n * their own module (not exported from `Icon.tsx` itself) so both files stay\n * component-only for Fast Refresh, per the project's `react-refresh/\n * only-export-components` lint rule.\n */\n\nexport const ICON_SIZE_MAP: Record<IconSize, number> = { sm: 12, md: 16, lg: 20 };\n\nexport function resolveIconColor(\n theme: ReturnType<typeof useGnomeTheme>,\n color: IconColor | undefined,\n) {\n switch (color) {\n case 'blue':\n return theme.blue3;\n case 'green':\n return theme.green4;\n case 'yellow':\n return theme.yellow4;\n case 'orange':\n return theme.orange3;\n case 'red':\n return theme.red3;\n case 'purple':\n return theme.purple3;\n case 'brown':\n return theme.brown3;\n default:\n return theme.windowFgColor;\n }\n}\n\nexport function iconAccessibilityProps(label: string | undefined) {\n return label\n ? { accessible: true as const, accessibilityLabel: label, accessibilityRole: 'image' as const }\n : { accessible: false as const, accessibilityRole: 'none' as const };\n}\n","import type { AnyIconDefinition, IconDefinition } from '@gnome-ui/icons';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Path, Svg, SvgXml } from 'react-native-svg';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nimport { ICON_SIZE_MAP, iconAccessibilityProps, resolveIconColor } from './shared';\n\nexport type IconSize = 'sm' | 'md' | 'lg';\n\n/** Named GNOME palette color for the icon. `\"default\"` (or omitting the prop) resolves to the theme's default foreground color. */\nexport type IconColor =\n | 'default'\n | 'blue'\n | 'green'\n | 'yellow'\n | 'orange'\n | 'red'\n | 'purple'\n | 'brown';\n\nexport interface IconProps {\n /** Icon from `@gnome-ui/icons`, a `simple-icons` icon, or a raw `{ path }` object. */\n icon: AnyIconDefinition;\n /**\n * Rendered size.\n * - `sm` — 12 px\n * - `md` — 16 px (default)\n * - `lg` — 20 px\n *\n * Override with `width`/`height` for non-standard sizes.\n */\n size?: IconSize;\n width?: number;\n height?: number;\n /** Accessible label. Omit for decorative icons — they are hidden from screen readers. */\n label?: string;\n /**\n * Named GNOME palette color. Omit (or pass `\"default\"`) to use the\n * theme's default foreground color — RN has no `currentColor`\n * equivalent to inherit from a parent, unlike the web version.\n */\n color?: IconColor;\n /**\n * Arbitrary fill color, overriding `color`. Escape hatch for the rare\n * case where a fixed named-palette swatch (e.g. `color=\"blue\"` →\n * `theme.blue3`) isn't good enough — most concretely, tinting an icon\n * with the app's actual *configurable* accent (`theme.accentColor`),\n * which `blue3` never tracks. Added for `BottomTabBar`'s active-tab\n * icon, the same precedent as `style` being added specifically for\n * `Dropdown`'s chevron rotation need.\n */\n tintColor?: string;\n /** Forwarded to the underlying `Svg` — useful for a `transform` (e.g. a rotated disclosure chevron) or `margin`. */\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * `icon.paths` present → `IconDefinition` with structured path data.\n * `icon.svg` present (and no `paths`) → `IconDefinition` carrying raw markup\n * (the `animated` icons — see `AnimatedIcon`).\n * Neither → `RawPathIconDefinition` (e.g. a `simple-icons` `SimpleIcon`,\n * which has its own unrelated `svg` field alongside `path` — checking\n * `path` is absent, not just that `svg` is a string, is what keeps those\n * correctly classified below instead of misread as an internal\n * `IconDefinition`). Duplicated from `@gnome-ui/react`'s `Icon.tsx` rather\n * than shared — see that component for the full reasoning.\n */\nfunction isIconDefinition(icon: AnyIconDefinition): icon is IconDefinition {\n if ('paths' in icon && Array.isArray((icon as IconDefinition).paths)) {\n return true;\n }\n\n return typeof (icon as IconDefinition).svg === 'string' && !('path' in icon);\n}\n\n/** `react-native-svg`'s `FillRule` has no `\"inherit\"` member (unlike `IconPath`'s, kept for CSS-source-of-truth completeness) — a leaf `<path>` has nothing to inherit from, so it maps to the SVG default (omitted attribute). */\nfunction fillRule(rule: 'nonzero' | 'evenodd' | 'inherit' | undefined) {\n return rule === 'nonzero' || rule === 'evenodd' ? rule : undefined;\n}\n\n/**\n * Renders an icon as an inline SVG, via `react-native-svg`.\n *\n * Accepts icons from `@gnome-ui/icons`, any `simple-icons` icon, or a plain\n * `{ path }` object — same `AnyIconDefinition` union as `@gnome-ui/react`'s\n * `Icon`. Pass `label` only when the icon conveys meaning on its own.\n *\n * `animated` icons (`Syncing`, `Recording`, …) carry raw `svg` markup\n * instead of `paths` — rendered here through `SvgXml`, which parses the\n * structural elements (`<g>`/`<path>`/`<circle>`) but has no CSS engine, so\n * the embedded `<style>`/`@keyframes` block is silently dropped and the\n * shapes render at their authored rest position. That happens to be exactly\n * the desired \"inert, static frame\" behavior for a plain `<Icon>` — no\n * special-casing needed here; wrap in `<AnimatedIcon>` to actually play the\n * motion (RN has to hand-build that with `Animated`, since there is no CSS\n * animation engine to interpret the markup's keyframes).\n *\n * @example\n * import { Search } from \"@gnome-ui/icons\";\n * <Icon icon={Search} label=\"Search\" />\n *\n * @example\n * import { siGithub } from \"simple-icons\";\n * <Icon icon={siGithub} label=\"GitHub\" />\n */\nexport const Icon = ({\n icon,\n size = 'md',\n width,\n height,\n label,\n color,\n tintColor,\n style,\n}: IconProps) => {\n const theme = useGnomeTheme();\n const px = ICON_SIZE_MAP[size];\n const fill = tintColor ?? resolveIconColor(theme, color);\n\n const resolvedViewBox = isIconDefinition(icon) ? icon.viewBox : (icon.viewBox ?? '0 0 24 24');\n const rawSvg = isIconDefinition(icon) ? icon.svg : undefined;\n const paths = isIconDefinition(icon)\n ? icon.svg\n ? undefined\n : icon.paths?.map((p, i) => (\n <Path\n key={i}\n d={p.d}\n fillRule={fillRule(p.fillRule)}\n clipRule={fillRule(p.clipRule)}\n transform={p.transform}\n />\n ))\n : [<Path key={0} d={icon.path} />];\n\n const accessibilityProps = iconAccessibilityProps(label);\n\n if (rawSvg) {\n return (\n <SvgXml\n xml={`<svg viewBox=\"${resolvedViewBox}\">${rawSvg}</svg>`}\n width={width ?? px}\n height={height ?? px}\n fill={fill}\n style={style}\n {...accessibilityProps}\n />\n );\n }\n\n return (\n <Svg\n viewBox={resolvedViewBox}\n width={width ?? px}\n height={height ?? px}\n fill={fill}\n style={style}\n {...accessibilityProps}\n >\n {paths}\n </Svg>\n );\n};\n","import { Connecting, Downloading, type IconDefinition, Recording, Syncing } from '@gnome-ui/icons';\nimport { type ComponentType, useEffect, useRef } from 'react';\nimport { Animated, Easing } from 'react-native';\nimport { Circle, G, Path, Svg } from 'react-native-svg';\nimport type { IconProps } from '@/components/Icon';\nimport { Icon } from '@/components/Icon';\nimport { ICON_SIZE_MAP, iconAccessibilityProps, resolveIconColor } from '@/components/Icon/shared';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport interface AnimatedIconProps extends Omit<IconProps, 'icon'> {\n /** Animated icon from `@gnome-ui/icons` (`animated: true` — `Syncing`, `Recording`, `Downloading`, `Connecting`). */\n icon: IconDefinition;\n /**\n * Whether the animation plays. Defaults to `true`.\n *\n * Regardless of this prop, the animation is always paused when the OS\n * reduced-motion setting is on — callers don't need to check\n * `useReducedMotion()` themselves.\n */\n playing?: boolean;\n}\n\n/**\n * `Animated`'s style/prop interpolation only takes effect on `Animated.*`\n * host components — the same gotcha already documented on `Toast`/`Dialog`,\n * here applied to `react-native-svg`'s shape components instead of `View`/\n * `Pressable`. Hoisted to module scope so they aren't recreated per render.\n */\nconst AnimatedG = Animated.createAnimatedComponent(G);\nconst AnimatedPath = Animated.createAnimatedComponent(Path);\nconst AnimatedCircle = Animated.createAnimatedComponent(Circle);\n\ninterface MotionProps {\n fill: string;\n}\n\n/** Drives a `0 → 1` value on an infinite loop. Used wherever the whole motion can be expressed as one `interpolate()` off a single linear driver. */\nfunction useLoopingProgress(duration: number) {\n const value = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n const animation = Animated.loop(\n Animated.timing(value, {\n toValue: 1,\n duration,\n easing: Easing.linear,\n useNativeDriver: false,\n }),\n );\n\n animation.start();\n\n return () => animation.stop();\n }, [value, duration]);\n\n return value;\n}\n\n/** Same as `useLoopingProgress`, but the repeating loop only starts after an initial one-time `delay` — for staggering identical cycles (`Connecting`'s three dots) out of phase with each other. */\nfunction useDelayedLoopingProgress(duration: number, delay: number) {\n const value = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n const animation = Animated.sequence([\n Animated.delay(delay),\n Animated.loop(\n Animated.timing(value, {\n toValue: 1,\n duration,\n easing: Easing.linear,\n useNativeDriver: false,\n }),\n ),\n ]);\n\n animation.start();\n\n return () => animation.stop();\n }, [value, duration, delay]);\n\n return value;\n}\n\n/**\n * `emblem-synchronizing-symbolic` — rotates a full turn, 1.5s linear, mirroring\n * `@gnome-ui/icons`' `Syncing.svg` `@keyframes gicon-syncing-rotate`.\n * Reuses `Refresh`'s exact path data, same as the source icon.\n */\nconst SyncingMotion = ({ fill }: MotionProps) => {\n const progress = useLoopingProgress(1500);\n const transform = progress.interpolate({\n inputRange: [0, 1],\n outputRange: ['rotate(0, 8, 8)', 'rotate(360, 8, 8)'],\n });\n\n return (\n <AnimatedG transform={transform}>\n <Path\n fill={fill}\n fillRule=\"evenodd\"\n d=\"M8 2a6 6 0 0 0-6 6h2a4 4 0 0 1 4-4 4 4 0 0 1 4 4 4 4 0 0 1-4 4v-2L5 13l3 3v-2a6 6 0 0 0 6-6 6 6 0 0 0-6-6z\"\n />\n </AnimatedG>\n );\n};\n\n/**\n * `media-record-symbolic` — opacity pulse `1 → 0.35 → 1`, 1.2s ease-in-out,\n * mirroring `Recording.svg`'s `@keyframes gicon-recording-pulse`.\n */\nconst RecordingMotion = ({ fill }: MotionProps) => {\n const opacity = useRef(new Animated.Value(1)).current;\n\n useEffect(() => {\n const easing = Easing.inOut(Easing.ease);\n const animation = Animated.loop(\n Animated.sequence([\n Animated.timing(opacity, { toValue: 0.35, duration: 600, easing, useNativeDriver: false }),\n Animated.timing(opacity, { toValue: 1, duration: 600, easing, useNativeDriver: false }),\n ]),\n );\n\n animation.start();\n\n return () => animation.stop();\n }, [opacity]);\n\n return (\n <AnimatedPath\n fill={fill}\n opacity={opacity}\n d=\"m 15 8 c 0 3.867188 -3.132812 7 -7 7 s -7 -3.132812 -7 -7 s 3.132812 -7 7 -7 s 7 3.132812 7 7 z m 0 0\"\n />\n );\n};\n\n/**\n * Animated download indicator — static tray bar, arrow drops into it on a\n * loop (translateY + opacity), 1.4s, mirroring `Downloading.svg`'s\n * `@keyframes gicon-downloading-drop`. The five CSS keyframe stops\n * (`0%, 20%, 50%, 55%, 80%, 100%`) become one `interpolate()`'s\n * `inputRange` off a single linear `0 → 1` driver — good enough fidelity\n * for a decorative micro-interaction without chaining five separately-eased\n * `Animated.timing` segments.\n */\nconst DownloadingMotion = ({ fill }: MotionProps) => {\n const progress = useLoopingProgress(1400);\n const stops = [0, 0.2, 0.5, 0.55, 0.8, 1];\n const transform = progress.interpolate({\n inputRange: stops,\n outputRange: [\n 'translate(0, 0)',\n 'translate(0, 0)',\n 'translate(0, 3)',\n 'translate(0, -3)',\n 'translate(0, 0)',\n 'translate(0, 0)',\n ],\n });\n const opacity = progress.interpolate({ inputRange: stops, outputRange: [1, 1, 0, 0, 1, 1] });\n\n return (\n <>\n <Path fill={fill} d=\"M2 13h12v2H2z\" />\n <AnimatedG transform={transform} opacity={opacity}>\n <Path fill={fill} fillRule=\"evenodd\" d=\"m2.5 5 5.5 6 5.5-6z\" />\n </AnimatedG>\n </>\n );\n};\n\n/** `scale(s)` around an arbitrary `(cx, cy)` pivot, composed as SVG's own translate/scale/translate-back — `react-native-svg`'s dedicated `scale` prop takes a `NumberArray`, not a single animatable number, and its non-deprecated `scaleX`/`scaleY` still can't share one `Animated.Value` with `origin` cleanly, so a plain `transform` string is the most direct fit. */\nfunction scaleAround(cx: number, cy: number, scale: number) {\n return `translate(${cx}, ${cy}) scale(${scale}) translate(${-cx}, ${-cy})`;\n}\n\ninterface ConnectingDotProps {\n cx: number;\n cy: number;\n fill: string;\n delay: number;\n}\n\n/** One of `Connecting`'s three signal dots — opacity + scale pulse, 1.2s ease-in-out, staggered via `delay` so the three read as a left-to-right sweep, mirroring `Connecting.svg`'s `animation-delay: 0/0.15s/0.3s`. */\nconst ConnectingDot = ({ cx, cy, fill, delay }: ConnectingDotProps) => {\n const progress = useDelayedLoopingProgress(1200, delay);\n const stops = [0, 0.2, 0.4, 1];\n const opacity = progress.interpolate({ inputRange: stops, outputRange: [1, 0.3, 1, 1] });\n const transform = progress.interpolate({\n inputRange: stops,\n outputRange: [\n scaleAround(cx, cy, 1),\n scaleAround(cx, cy, 0.7),\n scaleAround(cx, cy, 1),\n scaleAround(cx, cy, 1),\n ],\n });\n\n return (\n <AnimatedCircle cx={cx} cy={cy} r={2} fill={fill} opacity={opacity} transform={transform} />\n );\n};\n\n/**\n * `network-wireless-acquiring-symbolic` — static device/arc path, three\n * signal dots pulsing in a staggered sweep, mirroring `Connecting.svg`.\n */\nconst ConnectingMotion = ({ fill }: MotionProps) => {\n return (\n <>\n <Path\n fill={fill}\n d=\"m 8 1.992188 c -2.617188 0 -5.238281 0.933593 -7.195312 2.808593 l -0.496094 0.480469 c -0.3984378 0.378906 -0.410156 1.011719 -0.03125 1.410156 c 0.003906 0.007813 0.011718 0.011719 0.019531 0.015625 c 0.480469 -1.011719 1.503906 -1.707031 2.703125 -1.707031 h 0.9375 c 2.480469 -1.292969 5.644531 -1.292969 8.125 0 h 0.9375 c 1.199219 0 2.222656 0.695312 2.707031 1.707031 c 0.003907 -0.007812 0.011719 -0.011719 0.015625 -0.015625 c 0.378906 -0.398437 0.367188 -1.03125 -0.03125 -1.410156 l -0.496094 -0.480469 c -1.957031 -1.875 -4.578124 -2.808593 -7.195312 -2.808593 z m -1.730469 9.007812 c -0.441406 0.765625 -0.339843 1.757812 0.316407 2.414062 c 0.78125 0.78125 2.046874 0.78125 2.828124 0 c 0.65625 -0.65625 0.757813 -1.648437 0.316407 -2.414062 z m 0 0\"\n />\n <ConnectingDot cx={3} cy={8} fill={fill} delay={0} />\n <ConnectingDot cx={8} cy={8} fill={fill} delay={150} />\n <ConnectingDot cx={13} cy={8} fill={fill} delay={300} />\n </>\n );\n};\n\n/**\n * Known animated icons, matched by referential identity against\n * `@gnome-ui/icons`' own exports. There is no generic way to interpret an\n * arbitrary icon's `svg`-embedded CSS `@keyframes` in RN (no CSS engine),\n * so each recipe is hand-built — this registry only covers the 4 icons\n * `@gnome-ui/icons` currently ships as `animated: true`. An icon that isn't\n * in it (a future 5th animated icon, or a consumer-authored one) falls back\n * to the static `<Icon>` frame — `playing` silently has no visible effect\n * rather than throwing.\n */\nconst RECIPES = new Map<IconDefinition, ComponentType<MotionProps>>([\n [Syncing, SyncingMotion],\n [Recording, RecordingMotion],\n [Downloading, DownloadingMotion],\n [Connecting, ConnectingMotion],\n]);\n\n/**\n * Plays the motion for a known `animated` icon from `@gnome-ui/icons`\n * (`Syncing`, `Recording`, `Downloading`, `Connecting`) — rendered through\n * plain `<Icon>`, these show a static frame instead.\n *\n * Unlike `@gnome-ui/react`'s `AnimatedIcon` (which plays a CSS animation\n * embedded in the icon's raw `svg` markup via a `--gnome-icon-play-state`\n * custom property), RN has no CSS engine to interpret `@keyframes` at all —\n * `<Icon>` already drops the `<style>` block silently (see its own doc\n * comment), and this component instead hand-builds each of the 4 known\n * icons' exact motion with `Animated`, matched against `RECIPES` by\n * reference. When playing and reduced motion is off, it renders its own\n * `<Svg>` with the matching `*Motion` recipe as children; otherwise (not\n * playing, reduced motion on, or an icon `RECIPES` doesn't recognize) it\n * defers straight to `<Icon>`.\n *\n * Useful for progress, sync, recording, download, and connection states, or\n * cross-fading between icons by swapping `icon` while `playing` stays true.\n *\n * @example\n * import { Syncing } from \"@gnome-ui/icons\";\n * <AnimatedIcon icon={Syncing} playing={isSyncing} label=\"Syncing\" />\n */\nexport const AnimatedIcon = ({\n icon,\n playing = true,\n size = 'md',\n width,\n height,\n label,\n color,\n}: AnimatedIconProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n const isPlaying = playing && !reducedMotion;\n const Motion = isPlaying ? RECIPES.get(icon) : undefined;\n\n if (!Motion) {\n return (\n <Icon icon={icon} size={size} width={width} height={height} label={label} color={color} />\n );\n }\n\n const px = ICON_SIZE_MAP[size];\n const fill = resolveIconColor(theme, color);\n const accessibilityProps = iconAccessibilityProps(label);\n\n return (\n <Svg viewBox={icon.viewBox} width={width ?? px} height={height ?? px} {...accessibilityProps}>\n <Motion fill={fill} />\n </Svg>\n );\n};\n","import type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Image, Text, View } from 'react-native';\n\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type AvatarSize = 'sm' | 'md' | 'lg' | 'xl';\n\n/**\n * Named color palette for the initials fallback.\n * Mirrors libadwaita's avatar color set.\n */\nexport type AvatarColor =\n | 'blue'\n | 'green'\n | 'yellow'\n | 'orange'\n | 'red'\n | 'purple'\n | 'brown'\n | 'teal'\n | 'slate';\n\nexport interface AvatarProps {\n /**\n * Full name used to generate initials and — when `color` is omitted —\n * to deterministically pick a background color.\n */\n name?: string;\n /** Image URL. When provided the initials fallback is hidden. */\n src?: string;\n /** Accessible label. Defaults to `name`, then `\"Avatar\"`. */\n alt?: string;\n /** Size of the avatar. Defaults to `\"md\"`. */\n size?: AvatarSize;\n /**\n * Override the auto-derived background color for the initials fallback.\n * When omitted a color is derived from `name` via a stable hash.\n */\n color?: AvatarColor;\n style?: StyleProp<ViewStyle>;\n}\n\nconst COLOR_LIST: AvatarColor[] = [\n 'blue',\n 'green',\n 'yellow',\n 'orange',\n 'red',\n 'purple',\n 'brown',\n 'teal',\n 'slate',\n];\n\nconst SIZE_BOX: Record<AvatarSize, number> = { sm: 24, md: 32, lg: 48, xl: 64 };\n\n/**\n * Initials font size as a multiple of the theme's base body size — ports the\n * source CSS's `.initials { font-size: 0.42em }` overridden per size\n * (`sm` 0.5em, `md` 0.6em, `lg` 1em, `xl` 1.4em), which is relative to the\n * *inherited* ambient font size (the avatar box itself sets none), not to\n * the avatar's own box size.\n */\nconst SIZE_INITIALS_RATIO: Record<AvatarSize, number> = { sm: 0.5, md: 0.6, lg: 1, xl: 1.4 };\n\n/** Stable, non-cryptographic hash → index into COLOR_LIST. */\nfunction nameToColor(name: string): AvatarColor {\n let hash = 0;\n for (let i = 0; i < name.length; i++) {\n hash = (hash * 31 + name.charCodeAt(i)) >>> 0;\n }\n\n return COLOR_LIST[hash % COLOR_LIST.length];\n}\n\n/** Extract up to 2 initials from a name string. */\nfunction getInitials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n\n if (parts.length === 0) {\n return '';\n }\n\n if (parts.length === 1) {\n return parts[0].charAt(0).toUpperCase();\n }\n\n return parts[0].charAt(0).toUpperCase() + parts[parts.length - 1].charAt(0).toUpperCase();\n}\n\ninterface ColorSwatch {\n backgroundColor: string;\n initialsColor: string;\n}\n\nfunction getColorSwatch(theme: GnomeThemeTokens, color: AvatarColor): ColorSwatch {\n switch (color) {\n case 'blue':\n return { backgroundColor: theme.blue3, initialsColor: '#fff' };\n case 'green':\n return { backgroundColor: theme.green4, initialsColor: '#fff' };\n case 'yellow':\n // Yellow needs dark initials for contrast, independent of color scheme.\n return { backgroundColor: theme.yellow4, initialsColor: 'rgba(0, 0, 0, 0.8)' };\n case 'orange':\n return { backgroundColor: theme.orange4, initialsColor: '#fff' };\n case 'red':\n return { backgroundColor: theme.red3, initialsColor: '#fff' };\n case 'purple':\n return { backgroundColor: theme.purple3, initialsColor: '#fff' };\n case 'brown':\n return { backgroundColor: theme.brown3, initialsColor: '#fff' };\n case 'teal':\n // Not a real @gnome-ui/core token — the web CSS hardcodes this hex too.\n return { backgroundColor: '#2190a4', initialsColor: '#fff' };\n case 'slate':\n return { backgroundColor: theme.dark2, initialsColor: '#fff' };\n }\n}\n\n/**\n * Circular avatar with image or initials fallback.\n *\n * Follows the Adwaita `AdwAvatar` pattern — deterministic color from name,\n * up to two initials when no image is supplied. Rebuilt with `View`/`Image`/\n * `Text` rather than ported from `@gnome-ui/react`'s DOM-based JSX, but\n * mirrors its prop API and color-hash/initials math (pure JS, ported\n * verbatim).\n *\n * The outer container carries `role=\"img\"` + `accessibilityLabel` — RN's\n * newer web-aligned `Role` union has an `\"img\"` value, a direct 1:1 port of\n * the web version's `role=\"img\"`, no substitution needed (same as\n * `ProgressBar`'s `role=\"progressbar\"`). The image/initials underneath are\n * hidden from the accessibility tree\n * (`accessibilityElementsHidden`/`importantForAccessibility=\"no\"`, mirroring\n * the web version's `aria-hidden` on both), so a screen reader gets one\n * stop, not two — same reasoning as `SpinButton`'s hidden −/+ buttons.\n *\n * The web CSS's `box-shadow: inset 0 0 0 1px …` ring (drawn on top, doesn't\n * affect layout) becomes a real 1px `borderWidth`/`borderColor` here (RN has\n * no inset shadow) — the same substitution `Slider`'s thumb border already\n * used for a ring effect, at the cost of a negligible 1px content-box inset\n * the web version doesn't have.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Avatar.html\n */\nexport const Avatar = ({ name = '', src, alt, size = 'md', color, style }: AvatarProps) => {\n const theme = useGnomeTheme();\n const resolvedColorScheme = useResolvedColorScheme();\n\n const resolvedColor = color ?? (name ? nameToColor(name) : 'blue');\n const initials = getInitials(name);\n const box = SIZE_BOX[size];\n const { backgroundColor, initialsColor } = getColorSwatch(theme, resolvedColor);\n const ringColor =\n resolvedColorScheme === 'dark' ? 'rgba(255, 255, 255, 0.08)' : 'rgba(0, 0, 0, 0.08)';\n\n return (\n <View\n accessible\n role=\"img\"\n accessibilityLabel={alt ?? (name || undefined) ?? 'Avatar'}\n style={[\n {\n width: box,\n height: box,\n borderRadius: theme.radiusPill,\n overflow: 'hidden',\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: src ? undefined : backgroundColor,\n borderWidth: 1,\n borderColor: ringColor,\n },\n style,\n ]}\n >\n {src ? (\n <Image\n testID=\"avatar-image\"\n source={{ uri: src }}\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n resizeMode=\"cover\"\n style={{ width: '100%', height: '100%' }}\n />\n ) : (\n <Text\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n fontFamily: theme.fontFamily,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n fontSize: theme.fontSizeBody * SIZE_INITIALS_RATIO[size],\n lineHeight: theme.fontSizeBody * SIZE_INITIALS_RATIO[size],\n color: initialsColor,\n }}\n >\n {initials}\n </Text>\n )}\n </View>\n );\n};\n","import type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Text, View } from 'react-native';\n\nimport { Avatar, type AvatarColor, type AvatarSize } from '@/components/Avatar';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface AvatarGroupItem {\n name?: string;\n src?: string;\n alt?: string;\n color?: AvatarColor;\n}\n\nexport interface AvatarGroupProps {\n avatars: AvatarGroupItem[];\n /** Max visible avatars before showing the overflow chip. Defaults to `5`. */\n max?: number;\n /** Size applied to all avatars and the overflow chip. Defaults to `\"md\"`. */\n size?: AvatarSize;\n /** Accessible label for the group. Auto-generated from names when omitted. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst OVERLAP: Record<AvatarSize, number> = { sm: 6, md: 8, lg: 12, xl: 16 };\nconst CHIP_BOX: Record<AvatarSize, number> = { sm: 24, md: 32, lg: 48, xl: 64 };\nconst CHIP_FONT_SIZE: Record<AvatarSize, number> = { sm: 8, md: 9.6, lg: 14, xl: 17.6 };\n\n/**\n * Overlapping stack of `Avatar`s with a \"+N\" overflow indicator. Mirrors\n * `@gnome-ui/react`'s `AvatarGroup`.\n *\n * The web version's separating ring around each overlapping avatar is two\n * layered `box-shadow`s (an inset 1px dark/light border plus an outset 2px\n * window-colored ring) — RN can only give a `View` one border, so this\n * keeps just the outer window-colored ring (`borderWidth: 2,\n * borderColor: theme.windowBgColor`, overriding `Avatar`'s own subtle 1px\n * ring via its `style` prop), since that's the ring doing the actual\n * \"stay visually distinct from the avatar behind you\" work — the inner\n * hairline is a decorative nicety, not a behavior gap. The overflow chip\n * reuses `Avatar`'s own per-size box dimensions (`sm`/`md`/`lg`/`xl` →\n * 24/32/48/64) so it lines up exactly with the avatars beside it.\n *\n * `role=\"group\"` + `accessibilityLabel` ports 1:1 from RN's newer\n * web-aligned `Role` union (the same `Avatar`/`Badge`/`LevelBar`\n * precedent) — the label is auto-generated from `avatars[].name` (joined,\n * plus \"and N more\" when overflowing) exactly like the web version, unless\n * overridden. The overflow chip's `+N` text is hidden from the\n * accessibility tree (`accessibilityElementsHidden`/\n * `importantForAccessibility=\"no\"`) in favor of the chip's own\n * `accessibilityLabel=\"+N more\"` — the same one-stop-not-two reasoning\n * `SpinButton`/`Avatar` already established for a decorative inner glyph.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Avatar.html\n */\nexport const AvatarGroup = ({\n avatars,\n max = 5,\n size = 'md',\n accessibilityLabel,\n style,\n testID,\n}: AvatarGroupProps) => {\n const theme = useGnomeTheme();\n\n const visible = avatars.length > max ? avatars.slice(0, max) : avatars;\n const overflowCount = avatars.length > max ? avatars.length - max : 0;\n\n const allNames = avatars\n .map((a) => a.name)\n .filter(Boolean)\n .join(', ');\n\n const label =\n accessibilityLabel ??\n (allNames\n ? overflowCount > 0\n ? `${allNames} and ${overflowCount} more`\n : allNames\n : 'Avatar group');\n\n const overlap = OVERLAP[size];\n\n return (\n <View\n testID={testID}\n accessible\n role=\"group\"\n accessibilityLabel={label}\n style={[{ flexDirection: 'row', alignItems: 'center' }, style]}\n >\n {visible.map((item, index) => (\n <Avatar\n key={`${item.name ?? ''}-${item.src ?? ''}-${index}`}\n name={item.name}\n src={item.src}\n alt={item.alt ?? item.name}\n color={item.color}\n size={size}\n style={{\n marginStart: index === 0 ? 0 : -overlap,\n borderWidth: 2,\n borderColor: theme.windowBgColor,\n }}\n />\n ))}\n\n {overflowCount > 0 && (\n <View\n accessible\n accessibilityLabel={`+${overflowCount} more`}\n style={{\n width: CHIP_BOX[size],\n height: CHIP_BOX[size],\n marginStart: -overlap,\n borderRadius: theme.radiusPill,\n borderWidth: 2,\n borderColor: theme.windowBgColor,\n backgroundColor: theme.dark2,\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n fontFamily: theme.fontFamily,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n fontSize: CHIP_FONT_SIZE[size],\n color: '#fff',\n }}\n >\n {`+${overflowCount}`}\n </Text>\n </View>\n )}\n </View>\n );\n};\n","import { type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Animated, Pressable, StyleSheet } from 'react-native';\n\nimport { Avatar, type AvatarColor, type AvatarSize } from '@/components/Avatar';\nimport { useReducedMotion } from '@/GnomeProvider';\n\nexport interface AvatarRotatorProps {\n /** Full name used for the accessible label and initials fallback. */\n name?: string;\n /** Image URLs to rotate through. */\n avatars?: string[];\n /** Accessible label. Defaults to `name`. */\n alt?: string;\n /** Size of the avatar. Defaults to `\"md\"`. */\n size?: AvatarSize;\n /** Fallback initials color when no avatar image is available. */\n color?: AvatarColor;\n /** Time between avatar changes in milliseconds. Defaults to `3000`. */\n interval?: number;\n /** Crossfade duration in milliseconds. Defaults to `240`. */\n transitionDuration?: number;\n /** Pause automatic rotation while pressed and held. Defaults to `true`. */\n pauseOnPress?: boolean;\n /**\n * Controlled active avatar index.\n * When omitted the rotator manages index state internally.\n */\n activeIndex?: number;\n /** Initial active avatar index for uncontrolled usage. Defaults to `0`. */\n defaultActiveIndex?: number;\n /** Called when the active avatar changes. */\n onIndexChange?: (index: number) => void;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nfunction clampIndex(index: number, length: number) {\n if (length <= 0) {\n return 0;\n }\n\n return ((index % length) + length) % length;\n}\n\nconst SIZE_BOX: Record<AvatarSize, number> = { sm: 24, md: 32, lg: 48, xl: 64 };\n\ninterface RotatorLayerProps {\n active: boolean;\n duration: number;\n reducedMotion: boolean;\n children: ReactNode;\n}\n\n/**\n * One crossfading layer — owns its own `Animated.Value` rather than the\n * parent tracking an array of them, the same \"each item animates itself\"\n * shape `Toast`/`Toaster` already established for a list of independently\n * transitioning items.\n */\nconst RotatorLayer = ({ active, duration, reducedMotion, children }: RotatorLayerProps) => {\n const opacity = useRef(new Animated.Value(active ? 1 : 0)).current;\n\n useEffect(() => {\n if (reducedMotion) {\n opacity.setValue(active ? 1 : 0);\n\n return;\n }\n\n Animated.timing(opacity, {\n toValue: active ? 1 : 0,\n duration,\n useNativeDriver: true,\n }).start();\n }, [active, duration, reducedMotion, opacity]);\n\n return (\n <Animated.View\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={[StyleSheet.absoluteFill, { opacity }]}\n >\n {children}\n </Animated.View>\n );\n};\n\n/**\n * Single avatar surface that crossfades through multiple image sources.\n * Mirrors `@gnome-ui/react`'s `AvatarRotator`. Keeps `Avatar` focused on\n * rendering one identity, while this component owns timing, crossfade\n * animation, and pause behavior.\n *\n * Each source renders as its own absolutely-positioned `Avatar`, layered\n * via `StyleSheet.absoluteFill` and crossfaded with `Animated.timing` on\n * `useNativeDriver: true` — an exact reproduction of the web version's\n * stacked-`.layer`-elements-with-opacity-transition technique, just with\n * `RotatorLayer` (see above) owning each layer's own `Animated.Value`\n * instead of a single shared CSS custom property driving them all.\n *\n * **`prefers-reduced-motion` stops the rotation outright, not just the\n * fade** — ported exactly: the web source's own auto-advance `useEffect`\n * bails out early when reduced motion is on, the same as when paused, so\n * this isn't merely an instant-swap-instead-of-crossfade case like\n * `ProgressBar`'s determinate transitions.\n *\n * The web version's `pauseOnHover` (mouseEnter/mouseLeave, focus/blur)\n * becomes `pauseOnPress` (`onPressIn`/`onPressOut`) — the same touch\n * substitution `Toast`'s own press-and-hold pause already established,\n * kept as a real toggleable prop here (unlike `Toast`, where the web\n * source bakes the behavior in without an escape hatch).\n * `usePrefersReducedMotion` (the web version's `@gnome-ui/hooks` import)\n * has no bearing here — this package's own `useReducedMotion()` from\n * `GnomeProvider` is the correct, already-established source for this.\n *\n * `role=\"img\"` + `accessibilityLabel` ports 1:1 from RN's newer\n * web-aligned `Role` union (the same `Avatar`/`AvatarGroup` precedent).\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Avatar.html\n */\nexport const AvatarRotator = ({\n name = '',\n avatars = [],\n alt,\n size = 'md',\n color,\n interval = 3000,\n transitionDuration = 240,\n pauseOnPress = true,\n activeIndex,\n defaultActiveIndex = 0,\n onIndexChange,\n style,\n testID,\n}: AvatarRotatorProps) => {\n const reducedMotion = useReducedMotion();\n const sources = avatars.map((src) => src.trim()).filter(Boolean);\n const isControlled = activeIndex !== undefined;\n const [internalIndex, setInternalIndex] = useState(defaultActiveIndex);\n const [isPaused, setIsPaused] = useState(false);\n const currentIndex = clampIndex(isControlled ? activeIndex : internalIndex, sources.length);\n\n useEffect(() => {\n if (sources.length <= 1 || interval <= 0 || isPaused || reducedMotion) {\n return;\n }\n\n const timer = setInterval(() => {\n const nextIndex = clampIndex(currentIndex + 1, sources.length);\n\n if (!isControlled) {\n setInternalIndex(nextIndex);\n }\n\n onIndexChange?.(nextIndex);\n }, interval);\n\n return () => clearInterval(timer);\n }, [\n currentIndex,\n interval,\n isControlled,\n isPaused,\n onIndexChange,\n reducedMotion,\n sources.length,\n ]);\n\n if (sources.length === 0) {\n return <Avatar name={name} alt={alt} size={size} color={color} style={style} />;\n }\n\n const label = alt ?? (name || undefined) ?? 'Avatar';\n const box = SIZE_BOX[size];\n\n return (\n <Pressable\n testID={testID}\n accessible\n role=\"img\"\n accessibilityLabel={label}\n onPressIn={() => pauseOnPress && setIsPaused(true)}\n onPressOut={() => pauseOnPress && setIsPaused(false)}\n style={[{ width: box, height: box, borderRadius: box / 2, overflow: 'hidden' }, style]}\n >\n {sources.map((src, index) => (\n <RotatorLayer\n key={`${src}-${index}`}\n active={index === currentIndex}\n duration={Math.max(0, transitionDuration)}\n reducedMotion={reducedMotion}\n >\n <Avatar name={name} src={src} size={size} />\n </RotatorLayer>\n ))}\n </Pressable>\n );\n};\n","import type { ReactNode } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Text, View } from 'react-native';\n\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\nimport type { GnomeResolvedColorScheme, GnomeThemeTokens } from '@/theme';\n\nexport type BadgeVariant = 'accent' | 'success' | 'warning' | 'error' | 'neutral';\n\nexport interface BadgeProps {\n /**\n * Visual style. Defaults to `\"accent\"`.\n * - `accent` — blue, for counts and highlights.\n * - `success` — green, for positive status.\n * - `warning` — yellow, for cautionary status.\n * - `error` — red, for failures or urgent counts.\n * - `neutral` — gray, for inactive or secondary counts.\n */\n variant?: BadgeVariant;\n /**\n * When true, renders a small dot with no label — used for unread/online indicators.\n * The `children` are ignored in dot mode.\n */\n dot?: boolean;\n /** Number or short text to display. Keep to 1–3 characters. String/number render as a themed label; other nodes render as-is. */\n children?: ReactNode;\n /**\n * When provided, the badge is positioned over this child element at the\n * top-right corner.\n */\n anchor?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\ninterface VariantColors {\n backgroundColor: string;\n color: string;\n}\n\nfunction getVariantColors(\n theme: GnomeThemeTokens,\n colorScheme: GnomeResolvedColorScheme,\n variant: BadgeVariant,\n): VariantColors {\n switch (variant) {\n case 'accent':\n return { backgroundColor: theme.accentBgColor, color: theme.accentFgColor };\n case 'success':\n return { backgroundColor: theme.successBgColor, color: theme.successFgColor };\n case 'warning':\n return { backgroundColor: theme.warningBgColor, color: theme.warningFgColor };\n case 'error':\n return { backgroundColor: theme.errorBgColor, color: theme.errorFgColor };\n case 'neutral':\n return {\n backgroundColor: colorScheme === 'dark' ? theme.dark2 : theme.light4,\n color: theme.windowFgColor,\n };\n }\n}\n\n/**\n * Counter or status indicator, optionally overlaid on another element.\n *\n * Rebuilt with `View`/`Text` rather than ported from `@gnome-ui/react`'s\n * DOM-based JSX, but mirrors its prop API. `children` renders as a themed\n * `Text` label when it's a string or number (the common case — counts and\n * short text); any other node renders as-is, the same convention `Button`'s\n * `children` already established.\n *\n * The web CSS's `box-shadow: 0 0 0 2px var(--gnome-window-bg-color)` ring\n * (always present, separating the badge from whatever's behind it) has no\n * RN equivalent that avoids affecting layout — RN's `border*` shrinks the\n * content box instead of drawing outside it. Reproduced instead with an\n * outer wrapping `View` (2px padding, `theme.windowBgColor` background,\n * pill radius) around the actual colored badge, so the ring appears to\n * spread outward exactly like the web version's non-blurred shadow, without\n * eating into the badge's own text padding.\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/badges.html\n */\nexport const Badge = ({ variant = 'accent', dot = false, children, anchor, style }: BadgeProps) => {\n const theme = useGnomeTheme();\n const colorScheme = useResolvedColorScheme();\n const { backgroundColor, color } = getVariantColors(theme, colorScheme, variant);\n\n const badgeEl = (\n <View\n testID=\"badge-ring\"\n style={[\n {\n padding: 2,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.windowBgColor,\n },\n anchor\n ? {\n position: 'absolute' as const,\n top: dot ? -2 : -4,\n right: dot ? -2 : -4,\n zIndex: 1,\n }\n : null,\n ]}\n >\n <View\n testID=\"badge\"\n style={[\n {\n minWidth: dot ? 10 : 18,\n height: dot ? 10 : 18,\n paddingHorizontal: dot ? 0 : 5,\n borderRadius: theme.radiusPill,\n backgroundColor,\n alignItems: 'center',\n justifyContent: 'center',\n },\n style,\n ]}\n >\n {!dot &&\n (typeof children === 'string' || typeof children === 'number' ? (\n <Text\n style={{\n fontFamily: theme.fontFamily,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n fontSize: 11,\n lineHeight: 13,\n color,\n }}\n >\n {children}\n </Text>\n ) : (\n children\n ))}\n </View>\n </View>\n );\n\n if (anchor) {\n return (\n <View style={{ alignSelf: 'flex-start' }}>\n {anchor}\n {badgeEl}\n </View>\n );\n }\n\n return badgeEl;\n};\n","import type { ReactNode } from 'react';\nimport type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native';\nimport { Pressable, Text as RNText, View } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type BannerVariant = 'info' | 'warning' | 'error' | 'success';\n\nexport interface BannerProps extends Omit<ViewProps, 'style'> {\n /**\n * Visual emphasis level.\n * - `info` (default) — neutral, accent-colored. Use for tips and notices.\n * - `warning` — yellow. Use for recoverable problems.\n * - `error` — red. Use for failures that need attention.\n * - `success` — green. Use for confirmations.\n */\n variant?: BannerVariant;\n /** The message text. Keep it short — one or two sentences. */\n children: ReactNode;\n /**\n * Label for the optional action button placed at the trailing end.\n * When provided, `onAction` should also be supplied.\n */\n actionLabel?: string;\n /** Called when the user presses the action button. */\n onAction?: () => void;\n /**\n * When true a dismiss (×) button is shown at the trailing edge.\n * Provide `onDismiss` to handle removal from the list.\n */\n dismissible?: boolean;\n /** Called when the user presses the dismiss button. */\n onDismiss?: () => void;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Persistent message strip displayed at the top of a view, following the\n * Adwaita `AdwBanner` pattern. Use for important information that persists\n * until the user acts or explicitly dismisses it — unlike `Toast`, it never\n * auto-dismisses.\n *\n * RN's `AccessibilityRole` union has no \"status\" value (the web version's\n * `role=\"status\"`); `\"alert\"` is the closest available role, paired with\n * `accessibilityLiveRegion=\"polite\"` (Android's live-region API) as the\n * nearest match to `aria-live=\"polite\"` — the same substitution `Toast`\n * already established for the identical web role pair. Since the banner\n * itself is a plain `View` (not `Pressable` — only its buttons are\n * interactive), `accessible` is set explicitly alongside `accessibilityRole`,\n * per the `BoxedList` lesson that a bare `View` isn't an accessibility\n * element by default.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Banner.html\n * @see https://developer.gnome.org/hig/patterns/feedback/banners.html\n */\nexport const Banner = ({\n variant = 'info',\n children,\n actionLabel,\n onAction,\n dismissible = false,\n onDismiss,\n style,\n ...viewProps\n}: BannerProps) => {\n const theme = useGnomeTheme();\n\n const colors = {\n info: { bg: theme.accentBgColor, fg: theme.accentFgColor },\n warning: { bg: theme.warningBgColor, fg: theme.warningFgColor },\n error: { bg: theme.errorBgColor, fg: theme.errorFgColor },\n success: { bg: theme.successBgColor, fg: theme.successFgColor },\n }[variant];\n\n // Mirrors the source CSS's per-variant :active overlay: a light overlay on\n // the darker info/error/success backgrounds, a dark one on the light\n // warning background.\n const pressedOverlay =\n variant === 'warning' ? 'rgba(0, 0, 0, 0.14)' : 'rgba(255, 255, 255, 0.25)';\n\n return (\n <View\n accessible\n accessibilityRole=\"alert\"\n accessibilityLiveRegion=\"polite\"\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: 10,\n paddingHorizontal: theme.space4,\n minHeight: 44,\n width: '100%',\n backgroundColor: colors.bg,\n },\n style,\n ]}\n {...viewProps}\n >\n <Text variant=\"body\" style={{ flex: 1, minWidth: 0, color: colors.fg }}>\n {children}\n </Text>\n\n {(actionLabel || dismissible) && (\n <View\n style={{ flexDirection: 'row', alignItems: 'center', gap: theme.space1, flexShrink: 0 }}\n >\n {actionLabel && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={actionLabel}\n onPress={onAction}\n style={({ pressed }) => ({\n paddingVertical: 4,\n paddingHorizontal: theme.space2,\n borderRadius: theme.radiusMd,\n backgroundColor: pressed ? pressedOverlay : 'transparent',\n })}\n >\n <RNText\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n color: colors.fg,\n }}\n >\n {actionLabel}\n </RNText>\n </Pressable>\n )}\n\n {dismissible && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel=\"Dismiss\"\n hitSlop={4}\n onPress={onDismiss}\n style={({ pressed }) => ({\n width: 28,\n height: 28,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusMd,\n opacity: pressed ? 1 : 0.6,\n backgroundColor: pressed ? pressedOverlay : 'transparent',\n })}\n >\n <RNText style={{ fontSize: theme.fontSizeBody, color: colors.fg }}>{'×'}</RNText>\n </Pressable>\n )}\n </View>\n )}\n </View>\n );\n};\n","import { forwardRef } from 'react';\nimport type { View, ViewProps } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nexport type BinProps = ViewProps;\n\n/**\n * Single-child container with no visual styling.\n *\n * A transparent wrapper that forwards all `View` props (and a ref to the\n * underlying `View`) straight through — useful as a neutral base for custom\n * components that need to apply layout or size constraints without\n * introducing any chrome of their own.\n *\n * Mirrors `AdwBin` and `@gnome-ui/react`'s own `Bin`. A plain RN `View`\n * already has no default visual styling (no background, no border), so\n * unlike the web port there's no CSS reset to strip — this is a pure\n * passthrough.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Bin.html\n */\nexport const Bin = forwardRef<View, BinProps>(function Bin(props, ref) {\n return <RNView ref={ref} {...props} />;\n});\n","import type { ReactNode } from 'react';\nimport type { StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { View } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type BlockquoteVariant = 'default' | 'info' | 'warning' | 'error' | 'success';\n\nexport interface BlockquoteProps extends Omit<ViewProps, 'style'> {\n /**\n * Visual emphasis style.\n * - `\"default\"` (default) — neutral left border; use for general quotations.\n * - `\"info\"` — accent blue; use for tips and noteworthy passages.\n * - `\"warning\"` — yellow; use for cautionary content.\n * - `\"error\"` — red; use for critical or deprecated content.\n * - `\"success\"` — green; use for positive or approved content.\n */\n variant?: BlockquoteVariant;\n /** Optional icon rendered to the left of the quoted text, e.g. an `<Icon>`. */\n icon?: ReactNode;\n /** Attribution rendered below the quote — author name, source title, etc. */\n cite?: ReactNode;\n /** The quoted content. */\n children: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * The source CSS's `color-mix(in srgb, <color> 8%, transparent)` /\n * `14%` tints (warning is the one outlier at 14%, every other variant is\n * 8%) have no RN equivalent — resolved as an 8-digit `#RRGGBBAA` alpha\n * suffix on the variant's own hex token instead, the same substitution\n * `Chip`'s selected-state tint already established. `0x14` ≈ 8%, `0x24` ≈ 14%.\n * `default` has no background tint at all in the source CSS, and its\n * border color (`theme.borderSubtle`) is already an `rgba(...)` string, not\n * a hex token — appending a hex alpha suffix to it would be invalid, so it's\n * handled separately below rather than through this table.\n */\nconst VARIANT_TINT: Record<Exclude<BlockquoteVariant, 'default'>, string> = {\n info: '14',\n warning: '24',\n error: '14',\n success: '14',\n};\n\n/**\n * Styled pull-quote with a colored left border, mirroring `@gnome-ui/react`'s\n * `Blockquote`. Five visual variants share the same severity scale as\n * `Banner`/`Chip`.\n *\n * `<blockquote>`/`<footer>`/`<cite>` have no RN element equivalent — this\n * renders as a plain `View` with no semantic role, the same \"no landmark\n * role available\" gap `PathBar`'s dropped `<nav>` and `HeaderBar`'s dropped\n * `<header>` already established; the quoted text and citation are plain\n * `Text` reads with no assistive-tech grouping to port.\n *\n * @example\n * <Blockquote variant=\"info\" cite=\"Ada Lovelace, 1842\">\n * The Analytical Engine has no pretensions to originate anything.\n * </Blockquote>\n *\n * @see https://developer.gnome.org/hig/\n */\nexport const Blockquote = ({\n variant = 'default',\n icon,\n cite,\n children,\n style,\n ...viewProps\n}: BlockquoteProps) => {\n const theme = useGnomeTheme();\n\n let borderColor = theme.borderSubtle;\n let backgroundColor = 'transparent';\n\n if (variant !== 'default') {\n const variantColor = {\n info: theme.accentBgColor,\n warning: theme.warningBgColor,\n error: theme.errorBgColor,\n success: theme.successBgColor,\n }[variant];\n\n borderColor = variantColor;\n backgroundColor = `${variantColor}${VARIANT_TINT[variant]}`;\n }\n\n return (\n <View\n style={[\n {\n paddingVertical: theme.space2,\n paddingHorizontal: theme.space3,\n borderLeftWidth: 3,\n borderLeftColor: borderColor,\n borderTopRightRadius: theme.radiusSm,\n borderBottomRightRadius: theme.radiusSm,\n backgroundColor,\n },\n style,\n ]}\n {...viewProps}\n >\n <View style={{ flexDirection: 'row', alignItems: 'flex-start', gap: theme.space2 }}>\n {icon && (\n <View style={{ marginTop: 2 }} accessibilityElementsHidden importantForAccessibility=\"no\">\n {icon}\n </View>\n )}\n\n <Text variant=\"body\" style={{ flex: 1, minWidth: 0 }}>\n {children}\n </Text>\n </View>\n\n {cite !== undefined && (\n <View style={{ marginTop: theme.space1 }}>\n <Text variant=\"caption\" color=\"dim\">\n {cite}\n </Text>\n </View>\n )}\n </View>\n );\n};\n","import { type ReactNode, useEffect, useMemo, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport {\n Animated,\n BackHandler,\n Dimensions,\n Modal,\n PanResponder,\n Pressable,\n View,\n} from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\n/**\n * `Animated`'s style interpolation only takes effect on `Animated.*` host\n * components — a plain `Pressable` would silently ignore the animated\n * `opacity` in its style, the same gotcha already documented on `Toast`/\n * `Dialog`. Hoisted to module scope so it isn't recreated on every render.\n */\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable);\n\nconst DRAG_CLOSE_THRESHOLD = 150;\n\nexport interface BottomSheetProps {\n /** Whether the sheet is visible. */\n open: boolean;\n /** Optional heading shown below the drag handle. */\n title?: ReactNode;\n children?: ReactNode;\n /** Called when the user dismisses the sheet (backdrop press, Android back button, or drag down). */\n onClose?: () => void;\n /** Whether pressing the backdrop closes the sheet. Defaults to `true`. */\n closeOnBackdrop?: boolean;\n style?: StyleProp<ViewStyle>;\n /** Applied to the backdrop, mirroring `Dialog`'s own `testID`. */\n testID?: string;\n}\n\n/**\n * Slide-up panel that overlays content from the bottom edge, mirroring\n * `AdwBottomSheet` (libadwaita 1.6+) and `@gnome-ui/react`'s `BottomSheet`.\n *\n * Rebuilt with `View`/`Modal`/`PanResponder` rather than ported from the web\n * version's DOM `Portal` + manual focus trap + raw pointer events —\n * reusing this package's own established pieces: `Dialog`'s backdrop-\n * opacity-on-an-`AnimatedPressable` + no-op-`Pressable`-around-the-card\n * (so a tap on the card never bubbles to the backdrop) recipe, and\n * `BackHandler`'s `hardwareBackPress` as the Android analog of the web\n * version's Escape listener (same pattern `Dialog`/`Popover` already use).\n * Focus-trapping and focus-restore-on-close have no port — no DOM\n * `document.activeElement`/`querySelector` equivalent exists in RN, the\n * same gap already present in every other floating component here.\n *\n * **Real drag-to-dismiss, not a fixed-panel simplification**: unlike the\n * web version's raw `PointerEvent` handlers directly mutating\n * `style.transform`, this uses `PanResponder` (the same core RN API\n * `Slider` already proved handles a threshold-based drag gesture) driving\n * a single `Animated.Value` — the *same* value used for the slide-in/\n * slide-out entrance animation. `PanResponder`'s `gestureState.dy` is the\n * cumulative vertical delta since the gesture started, so a plain\n * `translateY.setValue(Math.max(0, gestureState.dy))` during\n * `onPanResponderMove` reproduces the web version's own\n * `Math.max(0, e.clientY - dragStartY.current)` clamp exactly, and a\n * release past `DRAG_CLOSE_THRESHOLD` (150 px, same constant as the web\n * version) animates the rest of the way down before calling `onClose`,\n * otherwise it springs back to `0`. The gesture responder is attached only\n * to the handle bar `View`, not the whole sheet, mirroring the web\n * version's `onPointerDown` being scoped to `.handle` alone.\n *\n * **A real slide-up needs the sheet's own height first** — RN's\n * `transform` has no percentage-of-self units (the same `Slider`/`Avatar`\n * pitfall), unlike the web version's `translateY(100%)`. The sheet renders\n * once off-screen (measured via `onLayout`, starting `translateY` at that\n * measured height) before animating to `0` — the same \"resolve own\n * rendered size, then animate\" two-step every other sized-on-open\n * component in this package (`Dialog`, `Dropdown`, `Popover`) already\n * needs, just for a transform offset instead of a floating position.\n *\n * **Real, timed exit animation, unlike `Dialog`**: `Dialog`'s web source\n * has no exit keyframes at all (`Modal`'s `visible={false}` unmounts\n * immediately, matching the web version's plain `return null`) — but\n * `BottomSheet`'s web source explicitly animates both the backdrop fade\n * and the sheet's slide-out before removing it. Ported with a local\n * `visible` state that lags one animation behind the `open` prop: closing\n * starts the exit `Animated.timing`s and only flips `visible` to `false`\n * (unmounting the `Modal`'s content) in the animation's own completion\n * callback — not a `setTimeout` racing a hardcoded duration like the web\n * version, since `Animated`'s callback already fires exactly when the\n * animation actually finishes.\n *\n * The web version's `backdrop-filter: blur(4px)` has no port — this\n * package has no native blur view dependency, the same reasoning that\n * already dropped `Sidebar`'s blurred `variant`. `useBodyScrollLock` has no\n * RN equivalent needed: `Modal` already blocks all interaction with\n * whatever's behind it, there is no scrollable \"body\" to lock separately.\n *\n * `role=\"dialog\"` + `accessibilityViewIsModal` port 1:1 from `Dialog`'s own\n * precedent. `children`, when a plain string, must be wrapped in `Text`\n * before rendering — RN throws if a raw string is a `View`'s child, unlike\n * the web version's `<div>{children}</div>`, which needed no such check.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.BottomSheet.html\n */\nexport const BottomSheet = ({\n open,\n title,\n children,\n onClose,\n closeOnBackdrop = true,\n style,\n testID,\n}: BottomSheetProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const [visible, setVisible] = useState(open);\n const [sheetHeight, setSheetHeight] = useState(0);\n\n const translateY = useRef(new Animated.Value(Dimensions.get('window').height)).current;\n const backdropOpacity = useRef(new Animated.Value(0)).current;\n const isDragging = useRef(false);\n\n useEffect(() => {\n if (open) {\n setVisible(true);\n }\n }, [open]);\n\n // Animate in once the sheet is mounted and measured; animate out (then\n // actually unmount) when `open` flips false — skipped mid-drag, since the\n // gesture handlers below already own `translateY` in that case.\n useEffect(() => {\n if (!visible || isDragging.current) {\n return;\n }\n\n if (open) {\n if (reducedMotion) {\n translateY.setValue(0);\n backdropOpacity.setValue(1);\n\n return;\n }\n\n if (sheetHeight > 0) {\n translateY.setValue(sheetHeight);\n Animated.timing(translateY, {\n toValue: 0,\n duration: theme.durationNormal,\n useNativeDriver: true,\n }).start();\n }\n\n Animated.timing(backdropOpacity, {\n toValue: 1,\n duration: theme.durationNormal,\n useNativeDriver: true,\n }).start();\n } else {\n if (reducedMotion) {\n setVisible(false);\n\n return;\n }\n\n Animated.timing(translateY, {\n toValue: sheetHeight || Dimensions.get('window').height,\n duration: theme.durationNormal,\n useNativeDriver: true,\n }).start(({ finished }) => {\n if (finished) {\n setVisible(false);\n }\n });\n Animated.timing(backdropOpacity, {\n toValue: 0,\n duration: theme.durationNormal,\n useNativeDriver: true,\n }).start();\n }\n }, [\n open,\n visible,\n sheetHeight,\n reducedMotion,\n translateY,\n backdropOpacity,\n theme.durationNormal,\n ]);\n\n // Android hardware back button closes the sheet — the analog of the web\n // version's document-level Escape listener, same pattern `Dialog` uses.\n useEffect(() => {\n if (!open) {\n return;\n }\n\n const subscription = BackHandler.addEventListener('hardwareBackPress', () => {\n onClose?.();\n\n return true;\n });\n\n return () => subscription.remove();\n }, [open, onClose]);\n\n const panResponder = useMemo(\n () =>\n PanResponder.create({\n onStartShouldSetPanResponder: () => true,\n onPanResponderGrant: () => {\n isDragging.current = true;\n },\n onPanResponderMove: (_event, gestureState) => {\n translateY.setValue(Math.max(0, gestureState.dy));\n },\n onPanResponderRelease: (_event, gestureState) => {\n isDragging.current = false;\n\n const dy = Math.max(0, gestureState.dy);\n\n if (dy >= DRAG_CLOSE_THRESHOLD) {\n // Just request the close — the `open` → `false` effect above\n // owns the actual exit animation (and reduced-motion handling),\n // continuing smoothly from wherever the drag left `translateY`\n // rather than duplicating that animation here.\n onClose?.();\n } else {\n Animated.timing(translateY, {\n toValue: 0,\n duration: theme.durationNormal,\n useNativeDriver: true,\n }).start();\n }\n },\n }),\n [translateY, theme.durationNormal, onClose],\n );\n\n const handleSheetLayout = (e: LayoutChangeEvent) => {\n setSheetHeight(e.nativeEvent.layout.height);\n };\n\n if (!visible) {\n return null;\n }\n\n return (\n <Modal\n visible={visible}\n transparent\n animationType=\"none\"\n statusBarTranslucent\n onRequestClose={onClose}\n >\n <AnimatedPressable\n testID={testID}\n onPress={closeOnBackdrop ? onClose : undefined}\n accessible={false}\n style={{\n flex: 1,\n justifyContent: 'flex-end',\n backgroundColor: theme.dialogBackdropColor,\n opacity: backdropOpacity,\n }}\n >\n <Pressable onPress={() => {}} accessible={false}>\n <Animated.View\n onLayout={handleSheetLayout}\n accessible\n role=\"dialog\"\n accessibilityViewIsModal\n style={[\n {\n alignSelf: 'center',\n width: '100%',\n maxWidth: 560,\n backgroundColor: theme.dialogBgColor,\n borderTopLeftRadius: theme.radiusXl,\n borderTopRightRadius: theme.radiusXl,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: -4 },\n shadowOpacity: 0.18,\n shadowRadius: 24,\n elevation: 12,\n transform: [{ translateY }],\n },\n style,\n ]}\n >\n <View\n testID=\"bottom-sheet-handle\"\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n alignItems: 'center',\n paddingTop: theme.space2,\n paddingBottom: theme.space1,\n }}\n {...panResponder.panHandlers}\n >\n <View\n style={{\n width: 36,\n height: 4,\n borderRadius: 2,\n backgroundColor: theme.windowFgColor,\n opacity: 0.2,\n }}\n />\n </View>\n\n {title && (\n <Text\n variant=\"title-4\"\n style={{\n paddingHorizontal: theme.space4,\n paddingBottom: theme.space2,\n textAlign: 'center',\n fontWeight: String(theme.fontWeightBold) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n )}\n\n {children && (\n <View style={{ paddingHorizontal: theme.space4, paddingBottom: theme.space4 }}>\n {typeof children === 'string' ? <Text variant=\"body\">{children}</Text> : children}\n </View>\n )}\n </Animated.View>\n </Pressable>\n </AnimatedPressable>\n </Modal>\n );\n};\n","import type { AnyIconDefinition } from '@gnome-ui/icons';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Pressable, View } from 'react-native';\n\nimport { Badge } from '@/components/Badge';\nimport { Icon } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface BottomTabBarItem<V extends string = string> {\n /** The value reported to `onChange` when this tab is pressed. */\n value: V;\n /** Label shown below the icon. */\n label: string;\n /** Icon shown while this tab is not the selected one. */\n icon: AnyIconDefinition;\n /**\n * Icon shown while this tab *is* selected — the filled-vs-outline\n * convention iOS/Android system tab bars both use (e.g. a hollow heart\n * outline that becomes solid when active). Optional: omit to reuse\n * `icon` for both states, tinted differently.\n */\n activeIcon?: AnyIconDefinition;\n /**\n * Notification indicator. `true` renders a small dot (unread/pending,\n * no count); a `number` renders a counted badge (values above 99 render\n * as `\"99+\"`, matching `TabItem`'s own count-badge convention).\n */\n badge?: number | boolean;\n /** Disables this one tab. */\n disabled?: boolean;\n}\n\nexport interface BottomTabBarProps<V extends string = string> {\n /** The tabs to render, in order. */\n items: BottomTabBarItem<V>[];\n /** The currently selected tab's value. */\n value: V;\n /** Called when the user presses a tab. */\n onChange: (value: V) => void;\n /**\n * Extra bottom padding, in addition to the bar's own vertical padding —\n * pass your own `useSafeAreaInsets().bottom` here so the bar clears the\n * home indicator / gesture area on notched devices. This package takes\n * no dependency on `react-native-safe-area-context` itself (the same\n * \"no new peer dependency without a deliberate decision\" standard\n * `AnimatedIcon`'s `react-native-svg` addition was held to) — the\n * consumer's own app almost certainly already has it for its root\n * layout, so threading the value in here is cheaper than this package\n * depending on it too. Defaults to `0`.\n */\n bottomInset?: number;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Fixed bottom navigation bar — the iOS/Android \"tab bar\" pattern (Music,\n * Instagram, most system apps): a small, fixed set of top-level\n * destinations, each an icon + label, always visible at the foot of the\n * screen. No GNOME/libadwaita widget mirrors this (desktop apps don't use\n * bottom navigation), so this is an original component for this package,\n * not a port — built to fill a real gap once mobile \"modern app shell\"\n * navigation was requested.\n *\n * Distinct from the existing `TabBar`/`TabItem` (an in-page, horizontally\n * scrollable content switcher mirroring `@gnome-ui/react`'s `Tabs`) — that\n * one reuses `headerbarFgColor` for every tab's label regardless of\n * selection state, signaling \"active\" purely via a background pill +\n * bold weight + accent underline, never by tinting the icon/label\n * themselves. A bottom tab bar's whole visual signature is the opposite:\n * the active icon+label *are* the app's accent color. `Icon`'s `color`\n * prop can't express that on its own — it only resolves to a fixed\n * named-palette swatch (`color=\"blue\"` always means `theme.blue3`, never\n * whatever accent the app actually configured via `GnomeProvider\n * accentColor`) — so this is the first consumer of `Icon`'s new\n * `tintColor` prop, passing `theme.accentColor` directly for the selected\n * tab and leaving it unset (dimmed default) for the rest.\n *\n * The optional notification indicator reuses the real `Badge` component\n * for the dot/pill itself, but positions it manually rather than through\n * `Badge`'s own `anchor` mode — `anchor` wraps `[anchor, badge]` in a\n * `View` with a hardcoded `alignSelf: 'flex-start'`, which is fine for\n * `Badge`'s usual context (a row where the anchor is meant to hug the\n * start) but silently broke centering here: a bare icon (no badge) is a\n * direct child of this component's centered column, while a badged icon's\n * extra `anchor`-mode wrapper overrode that centering and pinned itself to\n * the left edge — the two looked visibly misaligned side by side. Every\n * tab now gets the identical `position: 'relative'` wrapper shape\n * regardless of whether it has a badge, so centering behaves identically\n * across all of them; the badge itself is a plain (non-`anchor`) `Badge`,\n * absolutely positioned and re-sized for this icon's actual scale (`size=\"lg\"`\n * is only 20px — `Badge`'s own default dot/offset sizing assumes a much\n * larger anchor like an avatar or `IconButton`, and would otherwise nearly\n * cover the icon rather than sit as a small corner accent). Press feedback\n * is the same `pressed ? theme.activeOverlay : 'transparent'` recipe every\n * other `Pressable` in this package already uses.\n *\n * `bottomInset` stands in for real safe-area awareness without this\n * package taking on `react-native-safe-area-context` as a dependency —\n * see its own doc comment.\n *\n * @example\n * <BottomTabBar\n * items={[\n * { value: 'home', label: 'Home', icon: HomeOutline, activeIcon: HomeFilled },\n * { value: 'search', label: 'Search', icon: Search },\n * { value: 'profile', label: 'Profile', icon: Person, badge: true },\n * ]}\n * value={tab}\n * onChange={setTab}\n * bottomInset={insets.bottom}\n * />\n */\nexport const BottomTabBar = <V extends string = string>({\n items,\n value,\n onChange,\n bottomInset = 0,\n style,\n testID,\n}: BottomTabBarProps<V>) => {\n const theme = useGnomeTheme();\n\n return (\n <View\n testID={testID}\n accessible\n role=\"tablist\"\n style={[\n {\n flexDirection: 'row',\n backgroundColor: theme.headerbarBgColor,\n borderTopWidth: 1,\n borderTopColor: theme.headerbarBorderColor,\n paddingTop: 8,\n paddingBottom: 4 + bottomInset,\n },\n style,\n ]}\n >\n {items.map((item) => {\n const selected = item.value === value;\n const icon = selected ? (item.activeIcon ?? item.icon) : item.icon;\n const tint = selected ? theme.accentColor : theme.windowFgColor;\n\n const iconEl = (\n <Icon\n icon={icon}\n size=\"lg\"\n tintColor={selected ? theme.accentColor : undefined}\n style={selected ? undefined : { opacity: theme.opacityDim }}\n />\n );\n\n return (\n <Pressable\n key={item.value}\n testID={testID ? `${testID}-${item.value}` : undefined}\n disabled={item.disabled}\n role=\"tab\"\n accessibilityLabel={item.label}\n accessibilityState={{ selected, disabled: !!item.disabled }}\n onPress={() => onChange(item.value)}\n style={({ pressed }) => ({\n flex: 1,\n alignItems: 'center',\n gap: 2,\n paddingVertical: 4,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n opacity: item.disabled ? theme.opacityDisabled : 1,\n })}\n >\n <View style={{ position: 'relative' }}>\n {iconEl}\n\n {item.badge !== undefined && item.badge !== false && (\n <View style={{ position: 'absolute', top: -4, end: -6 }}>\n <Badge\n variant=\"error\"\n dot={item.badge === true}\n style={\n item.badge === true\n ? { minWidth: 8, height: 8 }\n : { minWidth: 14, height: 14, paddingHorizontal: 3 }\n }\n >\n {typeof item.badge === 'number'\n ? item.badge > 99\n ? '99+'\n : item.badge\n : undefined}\n </Badge>\n </View>\n )}\n </View>\n\n <Text\n numberOfLines={1}\n style={{\n fontSize: theme.fontSizeCaption,\n fontWeight: String(\n selected ? theme.fontWeightSemibold : theme.fontWeightNormal,\n ) as TextStyle['fontWeight'],\n color: tint,\n opacity: selected ? 1 : theme.opacityDim,\n }}\n >\n {item.label}\n </Text>\n </Pressable>\n );\n })}\n </View>\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { FlexStyle, StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\n/** GNOME HIG standard spacing values (matches `GtkBox` spacing tokens). */\nexport type BoxSpacing = 3 | 6 | 12 | 18 | 24 | 32 | 48;\n\n/** Alias of `BoxSpacing` for use as a padding scale. */\nexport type BoxPadding = BoxSpacing;\n\nexport type BoxOrientation = 'horizontal' | 'vertical';\nexport type BoxAlign = 'start' | 'center' | 'end' | 'stretch' | 'baseline';\nexport type BoxJustify =\n | 'start'\n | 'center'\n | 'end'\n | 'space-between'\n | 'space-around'\n | 'space-evenly';\n\nexport interface BoxProps extends Omit<ViewProps, 'style'> {\n /**\n * Direction children are arranged.\n * `\"vertical\"` → `flexDirection: 'column'` (default).\n * `\"horizontal\"` → `flexDirection: 'row'`.\n */\n orientation?: BoxOrientation;\n /**\n * Gap between children, in density-independent pixels.\n * Accepts any of the GNOME HIG standard spacing values or any number.\n * Defaults to `6` (the HIG \"standard\" inner spacing).\n */\n spacing?: BoxSpacing | number;\n /**\n * Cross-axis alignment (`alignItems`).\n * Defaults to `\"stretch\"` for vertical, `\"center\"` for horizontal.\n */\n align?: BoxAlign;\n /**\n * Main-axis distribution (`justifyContent`).\n * Defaults to `\"start\"`.\n */\n justify?: BoxJustify;\n /** Inner padding applied to all sides, in density-independent pixels. */\n padding?: BoxPadding | number;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Yoga's own edge names for the two values CSS spells differently — the web\n * version passes `align`/`justify` straight through to CSS, which accepts\n * the bare `start`/`end` keywords RN's `FlexStyle` union doesn't have.\n */\nconst ALIGN: Record<BoxAlign, FlexStyle['alignItems']> = {\n start: 'flex-start',\n center: 'center',\n end: 'flex-end',\n stretch: 'stretch',\n baseline: 'baseline',\n};\n\nconst JUSTIFY: Record<BoxJustify, FlexStyle['justifyContent']> = {\n start: 'flex-start',\n center: 'center',\n end: 'flex-end',\n 'space-between': 'space-between',\n 'space-around': 'space-around',\n 'space-evenly': 'space-evenly',\n};\n\n/**\n * Fundamental flex layout primitive — the RN equivalent of `GtkBox`, and a\n * 1:1 mirror of `@gnome-ui/react`'s own `Box`.\n *\n * Arranges children in a single row or column with consistent spacing\n * following the GNOME Human Interface Guidelines spacing scale:\n *\n * | Token | dp | Use |\n * |-------|----|-----|\n * | tight | 3 | Dense UI, icon + label pairs |\n * | standard | 6 | Default inner spacing |\n * | medium | 12 | Between related groups |\n * | large | 18 | Between loosely related sections |\n * | section | 24 | Page-level section gaps |\n * | loose | 32 | Large content separation |\n * | jumbo | 48 | Hero / splash spacing |\n *\n * `BoxSpacing` keeps the web package's exact seven values rather than being\n * remapped onto this package's own `theme.space1`–`space6` scale — the two\n * overlap at 6/12/18/24/48 but not at 3 or 32/36, and `BoxSpacing` is a\n * published type consumers may already be importing, so it ports verbatim.\n *\n * Two things the web version accepts don't survive the platform: `spacing`\n * and `padding` are numbers only (RN's `gap`/`padding` take dp, not CSS\n * strings like `\"1rem\"`), and `align`/`justify` — which the web passes\n * straight through to CSS — are mapped from their bare `start`/`end`\n * keywords onto Yoga's `flex-start`/`flex-end`. The prop values stay the\n * web ones so the API reads identically across both packages; only the\n * internal translation differs.\n *\n * `display: 'flex'` has no port and needs none — every RN `View` is already\n * a flex container.\n *\n * @example\n * // Vertical section (heading + content)\n * <Box spacing={12}>\n * <Text variant=\"caption-heading\" color=\"dim\">Devices</Text>\n * <BoxedList>…</BoxedList>\n * </Box>\n *\n * @example\n * // Horizontal icon + label\n * <Box orientation=\"horizontal\" spacing={6} align=\"center\">\n * <Icon icon={Folder} />\n * <Text>Documents</Text>\n * </Box>\n *\n * @see https://developer.gnome.org/hig/guidelines/spacing.html\n */\nexport const Box = forwardRef<View, BoxProps>(function Box(\n {\n orientation = 'vertical',\n spacing = 6,\n align,\n justify = 'start',\n padding,\n children,\n style,\n ...viewProps\n },\n ref,\n) {\n const defaultAlign: BoxAlign = orientation === 'horizontal' ? 'center' : 'stretch';\n\n return (\n <RNView\n ref={ref}\n style={[\n {\n flexDirection: orientation === 'horizontal' ? 'row' : 'column',\n gap: spacing,\n alignItems: ALIGN[align ?? defaultAlign],\n justifyContent: JUSTIFY[justify],\n },\n padding !== undefined && { padding },\n style,\n ]}\n {...viewProps}\n >\n {children}\n </RNView>\n );\n});\n","import { forwardRef } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type SeparatorOrientation = 'horizontal' | 'vertical';\n\nexport interface SeparatorProps extends Omit<ViewProps, 'style'> {\n /** Direction of the dividing line. Defaults to `\"horizontal\"`. */\n orientation?: SeparatorOrientation;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Thin dividing line that separates groups of content.\n *\n * Color is driven entirely by `theme.cardShadeColor`, which already\n * resolves correctly per color scheme, so — unlike `Switch`/`Checkbox`/\n * `RadioButton` — there's no `useResolvedColorScheme()` branching needed\n * here.\n *\n * Rebuilt as a plain `View` rather than ported from `@gnome-ui/react`'s\n * `<hr>`/`<div role=\"separator\">`: RN's `AccessibilityRole` union has no\n * `\"separator\"` value, so — since a divider carries no information a\n * screen reader user needs — it's excluded from the accessibility tree\n * entirely with `accessible={false}`, the RN-idiomatic way to mark a\n * purely decorative element, rather than reaching for a role that doesn't\n * exist.\n *\n * @see https://developer.gnome.org/hig/patterns/containers.html\n */\nexport const Separator = forwardRef<View, SeparatorProps>(function Separator(\n { orientation = 'horizontal', style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <RNView\n ref={ref}\n accessible={false}\n style={[\n { backgroundColor: theme.cardShadeColor, flexShrink: 0 },\n orientation === 'vertical'\n ? { width: 1, height: '100%', alignSelf: 'stretch' }\n : { width: '100%', height: 1 },\n style,\n ]}\n {...viewProps}\n />\n );\n});\n","import { Children, forwardRef, type ReactNode } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { Separator } from '@/components/Separator';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type BoxedListVariant = 'default' | 'separate';\n\nexport interface BoxedListProps extends Omit<ViewProps, 'style'> {\n children?: ReactNode;\n /**\n * `\"separate\"` renders each child as its own standalone rounded card\n * instead of a single joined list — mirrors `.boxed-list-separate`.\n * Use when rows are independent items rather than a continuous group.\n */\n variant?: BoxedListVariant;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Rounded bordered list — the most common container pattern in GNOME apps.\n *\n * Mirrors the Adwaita `.boxed-list` style applied to a `GtkListBox`.\n * Separators between rows are inserted automatically. Pair with `ActionRow`\n * or any row-shaped element.\n *\n * Rebuilt with `View` rather than ported from `@gnome-ui/react`'s\n * `<ul>`/`<li>` — RN has no list-semantics elements, so the outer `View`\n * gets `accessibilityRole=\"list\"` (there's no RN equivalent of `listitem`\n * for rows), paired with `accessible` — a plain `View` isn't an\n * accessibility element by default, so without it screen readers (and\n * `@testing-library/react-native`'s `getByRole`) would skip over the role\n * entirely. The web version applies `border-radius` directly to each\n * first/last child via a `:first-child > *` CSS selector, since its `<ul>`\n * has no `overflow: hidden` of its own; RN's `overflow: 'hidden'` on the\n * outer `View` clips every row to the container's rounded corners\n * uniformly, so that per-child trick isn't needed here.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/style-classes.html#boxed-lists-cards\n * @see https://developer.gnome.org/hig/patterns/containers.html\n */\nexport const BoxedList = forwardRef<View, BoxedListProps>(function BoxedList(\n { children, variant = 'default', style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const items = Children.toArray(children).filter(Boolean);\n\n if (variant === 'separate') {\n return (\n <RNView\n ref={ref}\n accessible\n accessibilityRole=\"list\"\n style={[{ gap: theme.space2 }, style]}\n {...viewProps}\n >\n {items.map((child, i) => (\n <RNView\n // biome-ignore lint/suspicious/noArrayIndexKey: children have no natural stable id\n key={i}\n style={{\n backgroundColor: theme.cardBgColor,\n borderWidth: 1,\n borderColor: theme.dividerColor,\n borderRadius: theme.radiusLg,\n overflow: 'hidden',\n }}\n >\n {child}\n </RNView>\n ))}\n </RNView>\n );\n }\n\n return (\n <RNView\n ref={ref}\n accessible\n accessibilityRole=\"list\"\n style={[\n {\n backgroundColor: theme.cardBgColor,\n borderWidth: 1,\n borderColor: theme.dividerColor,\n borderRadius: theme.radiusLg,\n overflow: 'hidden',\n },\n style,\n ]}\n {...viewProps}\n >\n {items.map((child, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: children have no natural stable id\n <RNView key={i}>\n {i > 0 && <Separator testID=\"boxed-list-separator\" />}\n {child}\n </RNView>\n ))}\n </RNView>\n );\n});\n","import type { ReactNode } from 'react';\nimport { useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { View } from 'react-native';\n\nexport interface BreakpointDefinition {\n /** Identifier for this breakpoint, e.g. `\"compact\"` or `\"narrow\"`. */\n name: string;\n /**\n * Container width threshold in dp.\n * This breakpoint becomes active when the container width is ≤ this value.\n */\n maxWidth: number;\n}\n\nexport interface BreakpointBinState {\n /**\n * Name of the currently active breakpoint, or `null` when the container\n * is wider than all defined breakpoints.\n */\n activeBreakpoint: string | null;\n /** Current container width in dp (from `onLayout`). */\n width: number;\n}\n\nexport interface BreakpointBinProps extends Omit<ViewProps, 'children' | 'style'> {\n /**\n * Breakpoint definitions. The active breakpoint is the smallest\n * `maxWidth` ≥ the container's current width — declaration order doesn't\n * matter, they're sorted internally.\n */\n breakpoints: BreakpointDefinition[];\n /**\n * Render prop that receives the current breakpoint state.\n *\n * @example\n * ```tsx\n * <BreakpointBin breakpoints={[{ name: \"compact\", maxWidth: 400 }]}>\n * {({ activeBreakpoint }) =>\n * activeBreakpoint === \"compact\" ? <CompactLayout /> : <WideLayout />\n * }\n * </BreakpointBin>\n * ```\n */\n children: (state: BreakpointBinState) => ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\nfunction evaluate(breakpoints: BreakpointDefinition[], width: number): BreakpointBinState {\n const sorted = [...breakpoints].sort((a, b) => a.maxWidth - b.maxWidth);\n const match = sorted.find((bp) => width <= bp.maxWidth) ?? null;\n\n return { activeBreakpoint: match?.name ?? null, width };\n}\n\n/**\n * Container that fires layout changes when **its own width** crosses\n * defined thresholds — the CSS container-query equivalent of\n * `AdwBreakpointBin` (libadwaita 1.9 / GNOME 50), and the per-*component*\n * sibling of `useBreakpoint` (which watches the window instead).\n *\n * `@gnome-ui/react`'s version watches its own width via `ResizeObserver`;\n * RN has no such API, so this measures the same thing via `onLayout` —\n * fired on mount and again on every subsequent resize of the `View` itself\n * (e.g. the parent's flex layout reflowing, or a device rotation changing\n * how much space this container is given). Composable the same way: the\n * same component can render differently depending on how much space its\n * parent gives it, regardless of the window size — two `BreakpointBin`s\n * side by side with identical `breakpoints` can be in different states.\n *\n * Unlike the web version, there's no `data-breakpoint` attribute to expose\n * for CSS targeting (RN has no attribute selectors) — branch on\n * `activeBreakpoint` directly inside the render prop instead, exactly like\n * every story below already does.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.BreakpointBin.html\n */\nexport const BreakpointBin = ({\n breakpoints,\n children,\n style,\n onLayout,\n ...viewProps\n}: BreakpointBinProps) => {\n const [width, setWidth] = useState(0);\n\n const handleLayout = (event: LayoutChangeEvent) => {\n setWidth(event.nativeEvent.layout.width);\n onLayout?.(event);\n };\n\n return (\n <View onLayout={handleLayout} style={style} {...viewProps}>\n {children(evaluate(breakpoints, width))}\n </View>\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { PressableProps, StyleProp, TextStyle, View, ViewStyle } from 'react-native';\nimport { Pressable, Text } from 'react-native';\n\nimport { useGnomeTheme, useResolvedContrast } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type ButtonVariant = 'default' | 'suggested' | 'destructive' | 'flat' | 'raised';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\nexport type ButtonShape = 'default' | 'pill' | 'circular';\n\nexport interface ButtonProps extends Omit<PressableProps, 'children' | 'style'> {\n /** Visual style of the button. Follows GNOME HIG button types. */\n variant?: ButtonVariant;\n /** Size of the button. */\n size?: ButtonSize;\n /** Shape of the button. \"pill\" for primary actions in open space, \"circular\" for icon-only buttons. */\n shape?: ButtonShape;\n /**\n * Dark semi-transparent overlay style for buttons placed on top of media\n * or images — mirrors `@gnome-ui/react`'s `osd` prop.\n */\n osd?: boolean;\n /**\n * Icon placed before the label. Rendered as-is — RN has no CSS\n * `currentColor` equivalent, so size and color it yourself (`theme.*FgColor`\n * from `useGnomeTheme()` matches the resolved label color for each variant).\n */\n leadingIcon?: ReactNode;\n /** Icon placed after the label. See `leadingIcon` for color/size notes. */\n trailingIcon?: ReactNode;\n /** String children render as a themed `Text` label; other nodes render as-is. */\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n /** Style applied to the label `Text` when `children` is a string. */\n textStyle?: StyleProp<TextStyle>;\n}\n\ninterface SizeMetrics {\n minHeight: number;\n minWidth: number;\n paddingVertical: number;\n paddingHorizontal: number;\n fontSize: number;\n}\n\nfunction getSizeMetrics(theme: GnomeThemeTokens, size: ButtonSize): SizeMetrics {\n switch (size) {\n case 'sm':\n return {\n minHeight: 28,\n minWidth: 64,\n paddingVertical: 4,\n paddingHorizontal: theme.space1,\n fontSize: theme.fontSizeCaption,\n };\n case 'lg':\n return {\n minHeight: 42,\n minWidth: 88,\n paddingVertical: theme.space1,\n paddingHorizontal: theme.space3,\n fontSize: theme.fontSizeTitle4,\n };\n default:\n return {\n minHeight: 34,\n minWidth: 88,\n paddingVertical: theme.space1,\n paddingHorizontal: theme.space2,\n fontSize: theme.fontSizeBody,\n };\n }\n}\n\nfunction getCircularSize(size: ButtonSize): number {\n switch (size) {\n case 'sm':\n return 28;\n case 'lg':\n return 42;\n default:\n return 34;\n }\n}\n\ninterface VariantColors {\n backgroundColor: string;\n borderColor: string;\n color: string;\n}\n\nfunction getVariantColors(\n theme: GnomeThemeTokens,\n variant: ButtonVariant,\n pressed: boolean,\n osd: boolean,\n): VariantColors {\n if (osd) {\n return {\n backgroundColor: pressed ? 'rgba(0, 0, 0, 0.85)' : 'rgba(0, 0, 0, 0.65)',\n borderColor: 'rgba(255, 255, 255, 0.12)',\n color: 'rgba(255, 255, 255, 0.95)',\n };\n }\n\n switch (variant) {\n case 'suggested':\n return {\n backgroundColor: theme.accentBgColor,\n borderColor: 'transparent',\n color: theme.accentFgColor,\n };\n case 'destructive':\n return {\n backgroundColor: theme.destructiveBgColor,\n borderColor: 'transparent',\n color: theme.destructiveFgColor,\n };\n case 'flat':\n return {\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n borderColor: 'transparent',\n color: theme.windowFgColor,\n };\n default:\n return {\n backgroundColor: pressed ? theme.light3 : theme.cardBgColor,\n borderColor: theme.light3,\n color: theme.windowFgColor,\n };\n }\n}\n\n/**\n * Button component following GNOME Human Interface Guidelines.\n *\n * Variants:\n * - `default` — Standard action, flat appearance with subtle border.\n * - `suggested` — Affirmative/primary action (accent color). Use at most once per view.\n * - `destructive` — Dangerous or irreversible action (red). Use sparingly.\n * - `flat` — No border or background; ideal for header bars and toolbars.\n * - `raised` — Explicit raised look inside flat/toolbar contexts.\n *\n * Rebuilt with `Pressable`/`View`/`Text` rather than ported from\n * `@gnome-ui/react`'s DOM-based JSX, but mirrors its prop API.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/buttons.html\n */\nexport const Button = forwardRef<View, ButtonProps>(function Button(\n {\n variant = 'default',\n size = 'md',\n shape = 'default',\n osd = false,\n leadingIcon,\n trailingIcon,\n children,\n style,\n textStyle,\n disabled,\n ...pressableProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const contrast = useResolvedContrast();\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled: !!disabled }}\n style={({ pressed }) => {\n const { backgroundColor, borderColor } = getVariantColors(theme, variant, pressed, osd);\n const solidVariant = !osd && (variant === 'suggested' || variant === 'destructive');\n const circularSize = shape === 'circular' ? getCircularSize(size) : undefined;\n const { minHeight, minWidth, paddingVertical, paddingHorizontal } = getSizeMetrics(\n theme,\n size,\n );\n\n return [\n {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n gap: theme.space1,\n minHeight: circularSize ?? minHeight,\n minWidth: circularSize ?? (shape === 'pill' ? undefined : minWidth),\n width: circularSize,\n height: circularSize,\n paddingVertical: circularSize ? theme.space1 : paddingVertical,\n paddingHorizontal: circularSize\n ? theme.space1\n : shape === 'pill'\n ? theme.space3\n : paddingHorizontal,\n borderRadius: shape === 'default' ? theme.radiusMd : theme.radiusPill,\n borderWidth: variant === 'default' && contrast === 'more' ? 2 : 1,\n borderColor,\n backgroundColor,\n opacity: disabled ? theme.opacityDisabled : pressed && solidVariant ? 0.85 : 1,\n } satisfies ViewStyle,\n style,\n ];\n }}\n {...pressableProps}\n >\n {({ pressed }) => {\n const { fontSize } = getSizeMetrics(theme, size);\n const { color } = getVariantColors(theme, variant, pressed, osd);\n\n return (\n <>\n {leadingIcon}\n {typeof children === 'string' ? (\n <Text\n style={[\n {\n fontFamily: theme.fontFamily,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n fontSize,\n color,\n },\n textStyle,\n ]}\n >\n {children}\n </Text>\n ) : (\n children\n )}\n {trailingIcon}\n </>\n );\n }}\n </Pressable>\n );\n});\n","import type { ReactNode } from 'react';\nimport type { StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { View } from 'react-native';\n\nimport type { TextColor } from '@/components/Text';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type ButtonContentIconPosition = 'start' | 'end';\n\nexport interface ButtonContentProps extends Omit<ViewProps, 'style'> {\n /** Icon placed next to the label. Rendered as-is — size/color it yourself. */\n icon?: ReactNode;\n /** Text label. */\n label: string;\n /**\n * Position of the icon relative to the label.\n * @default 'start'\n */\n iconPosition?: ButtonContentIconPosition;\n /**\n * Label color. RN has no `currentColor` equivalent, so unlike the web\n * version (which inherits the parent button's text color via CSS), this\n * needs to be told explicitly which color to match — e.g. `\"accent\"`\n * when placed inside a `suggested` `Button`. Defaults to `\"default\"`.\n */\n color?: TextColor;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Icon + label layout helper for buttons that contain both an icon and\n * text — the same 6 dp gap / vertically-centered row every button in this\n * package already produces internally. Mirrors `AdwButtonContent`.\n *\n * **Mostly redundant with `Button`'s own `leadingIcon`/`trailingIcon`\n * props** — pass those instead for anything that's actually a `Button`,\n * they already lay the icon and (themed, variant-colored) label out\n * identically and need no separate color prop. Reach for `ButtonContent`\n * when composing icon+label content for something that *isn't* this\n * package's `Button` — a bespoke `Pressable`, a `ButtonRow` title slot, a\n * `Chip`'s custom content — anywhere the exact same Adwaita icon/label\n * spacing convention is wanted outside `Button` itself.\n *\n * @example\n * ```tsx\n * <Pressable onPress={save}>\n * <ButtonContent icon={<Icon icon={DocumentSave} />} label=\"Save\" />\n * </Pressable>\n * ```\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ButtonContent.html\n */\nexport const ButtonContent = ({\n icon,\n label,\n iconPosition = 'start',\n color = 'default',\n style,\n ...viewProps\n}: ButtonContentProps) => {\n const theme = useGnomeTheme();\n\n const iconSlot = icon ? (\n <View accessibilityElementsHidden importantForAccessibility=\"no-hide-descendants\">\n {icon}\n </View>\n ) : null;\n\n return (\n <View\n style={[{ flexDirection: 'row', alignItems: 'center', gap: theme.space1 }, style]}\n {...viewProps}\n >\n {iconPosition === 'start' && iconSlot}\n <Text variant=\"body\" color={color}>\n {label}\n </Text>\n {iconPosition === 'end' && iconSlot}\n </View>\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { PressableProps, StyleProp, TextStyle, View, ViewStyle } from 'react-native';\nimport { Pressable, View as RNView, StyleSheet } from 'react-native';\nimport type { TextColor } from '@/components/Text';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type ButtonRowVariant = 'default' | 'suggested' | 'destructive';\n\nexport interface ButtonRowProps extends Omit<PressableProps, 'children' | 'style'> {\n /** Label displayed centered in the row. */\n title: string;\n /** Visual style that colours the title text. */\n variant?: ButtonRowVariant;\n /** Icon placed at the leading edge. Rendered as-is — size/color it yourself. */\n leading?: ReactNode;\n /** Icon placed at the trailing edge. Rendered as-is — size/color it yourself. */\n trailing?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/** `ButtonRowVariant` maps 1:1 onto `Text`'s own `TextColor` union — no separate color table needed. */\nconst TEXT_COLOR: Record<ButtonRowVariant, TextColor> = {\n default: 'default',\n suggested: 'accent',\n destructive: 'destructive',\n};\n\n/**\n * Full-width activatable row styled as a button, for use inside a\n * `BoxedList`. Mirrors `AdwButtonRow` and `@gnome-ui/react`'s own\n * `ButtonRow` — use when an entire list row should trigger a single action\n * with a centered label; prefer `ActionRow` with `interactive` when the row\n * also needs a title/subtitle layout.\n *\n * Rebuilt with `Pressable` rather than ported from the web `<button>` —\n * same pressed-state-overlay recipe `ActionRow`/`Card` already established\n * (`theme.activeOverlay` stands in for the web's `:hover`/`:active`\n * `background-color` transition, since RN has no hover state). The title's\n * color reuses `Text`'s own `TextColor` union (`\"accent\"`/`\"destructive\"`\n * already resolve to the exact same `theme.accentColor`/`destructiveColor`\n * tokens the source CSS's `suggested`/`destructive` variants reference) —\n * no separate color-resolution logic needed. Unlike the web version, the\n * variant color does *not* propagate to `leading`/`trailing` — RN's `Icon`\n * has no `currentColor` equivalent and only accepts a fixed named-swatch\n * palette, the same dropped nicety `Chip` already accepted for its own\n * leading/remove icons. The title gets `flex: 1` + `textAlign: 'center'`\n * (the source CSS's own `.title { flex: 1; text-align: center }`) rather\n * than centering the row via `justifyContent` — that keeps the label\n * centered in the row's full width even when only one of `leading`/\n * `trailing` is present, matching the web behavior exactly.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ButtonRow.html\n */\nexport const ButtonRow = forwardRef<View, ButtonRowProps>(function ButtonRow(\n { title, variant = 'default', leading, trailing, style, disabled, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled: !!disabled }}\n accessibilityLabel={title}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: theme.space2,\n paddingHorizontal: theme.space4,\n minHeight: 52,\n width: '100%',\n opacity: disabled ? theme.opacityDisabled : 1,\n },\n style,\n ]}\n {...pressableProps}\n >\n {({ pressed }) => (\n <>\n {leading && <RNView style={{ flexShrink: 0 }}>{leading}</RNView>}\n\n <Text\n variant=\"body\"\n color={TEXT_COLOR[variant]}\n style={{\n flex: 1,\n textAlign: 'center',\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n\n {trailing && <RNView style={{ flexShrink: 0 }}>{trailing}</RNView>}\n\n {pressed && !disabled ? (\n <RNView\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { backgroundColor: theme.activeOverlay }]}\n />\n ) : null}\n </>\n )}\n </Pressable>\n );\n});\n","import {\n cloneElement,\n type ReactElement,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport type {\n GestureResponderEvent,\n LayoutChangeEvent,\n MouseEvent,\n NativeSyntheticEvent,\n PressableProps,\n TargetedEvent,\n TextStyle,\n} from 'react-native';\nimport { Animated, Dimensions, Modal, StyleSheet, View } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\n\nexport type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';\n\nexport interface TooltipProps {\n /**\n * The tooltip label. Keep it short — a noun phrase or brief description.\n * Do not duplicate information already visible on screen.\n */\n label: string;\n /**\n * Preferred placement relative to the trigger.\n * The tooltip flips automatically if there is not enough space.\n * Defaults to `\"top\"`.\n */\n placement?: TooltipPlacement;\n /**\n * Delay in milliseconds before the tooltip appears, on both the\n * long-press and hover/focus triggers. Defaults to `500`. Set to `0` for\n * instant.\n */\n delay?: number;\n /**\n * The element that triggers the tooltip.\n * Must be a single element built on `Pressable` (e.g. `Button`, `Card`)\n * that forwards its `ref` to the underlying `View`.\n */\n children: ReactElement<PressableProps & { ref?: Ref<View> }>;\n}\n\ninterface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface Size {\n width: number;\n height: number;\n}\n\ninterface Position {\n top: number;\n left: number;\n placement: TooltipPlacement;\n}\n\nconst GAP = 6;\nconst MARGIN = 8;\nconst ARROW = 5;\n\nfunction assignRef<T>(ref: Ref<T> | undefined, value: T | null) {\n if (!ref) {\n return;\n }\n\n if (typeof ref === 'function') {\n ref(value);\n\n return;\n }\n\n (ref as { current: T | null }).current = value;\n}\n\nfunction placementRect(trigger: Rect, tooltip: Size, placement: TooltipPlacement) {\n switch (placement) {\n case 'top':\n return {\n top: trigger.y - tooltip.height - GAP,\n left: trigger.x + trigger.width / 2 - tooltip.width / 2,\n };\n case 'bottom':\n return {\n top: trigger.y + trigger.height + GAP,\n left: trigger.x + trigger.width / 2 - tooltip.width / 2,\n };\n case 'left':\n return {\n top: trigger.y + trigger.height / 2 - tooltip.height / 2,\n left: trigger.x - tooltip.width - GAP,\n };\n case 'right':\n return {\n top: trigger.y + trigger.height / 2 - tooltip.height / 2,\n left: trigger.x + trigger.width + GAP,\n };\n }\n}\n\nfunction computePosition(trigger: Rect, tooltip: Size, preferred: TooltipPlacement): Position {\n const { width: vw, height: vh } = Dimensions.get('window');\n\n const opposite: TooltipPlacement =\n preferred === 'top'\n ? 'bottom'\n : preferred === 'bottom'\n ? 'top'\n : preferred === 'left'\n ? 'right'\n : 'left';\n const placements: TooltipPlacement[] = [preferred, opposite, 'top', 'bottom', 'left', 'right'];\n\n for (const p of placements) {\n const { top, left } = placementRect(trigger, tooltip, p);\n const fitsH = left >= MARGIN && left + tooltip.width <= vw - MARGIN;\n const fitsV = top >= MARGIN && top + tooltip.height <= vh - MARGIN;\n\n if (fitsH && fitsV) {\n return {\n top: Math.max(MARGIN, Math.min(top, vh - tooltip.height - MARGIN)),\n left: Math.max(MARGIN, Math.min(left, vw - tooltip.width - MARGIN)),\n placement: p,\n };\n }\n }\n\n const { top, left } = placementRect(trigger, tooltip, preferred);\n\n return {\n top: Math.max(MARGIN, Math.min(top, vh - tooltip.height - MARGIN)),\n left: Math.max(MARGIN, Math.min(left, vw - tooltip.width - MARGIN)),\n placement: preferred,\n };\n}\n\n/**\n * Informational tooltip following the Adwaita / GNOME HIG pattern.\n *\n * Wraps a single trigger element and shows a floating label. Positioned\n * automatically and flips if there is not enough space, mirroring\n * `@gnome-ui/react`'s `Tooltip`.\n *\n * **Trigger differs from the web version by necessity**: the web `Tooltip`\n * shows only on mouse hover / keyboard focus — touch has no hover state, so\n * it explicitly never shows on touch. RN is touch-first, so the primary\n * trigger here is long-press (`delayLongPress={delay}`, released via\n * `onPressOut`) — the standard mobile \"peek\" idiom. `onHoverIn`/`onHoverOut`\n * are also wired for hover-capable input (trackpad/mouse on iPad or a\n * pointer-driven RN target) and `onFocus`/`onBlur` for external-keyboard\n * accessibility, both delayed the same way the web version delays hover.\n *\n * Built on RN's own `Modal` (transparent, `pointerEvents=\"box-none\"`), the\n * same portal-substitute `Dialog` uses — no DOM `Portal` equivalent exists.\n * Unlike `Dialog`, `visible` is set synchronously on trigger (not gated on\n * the async `measureInWindow` result): the trigger's on-screen rect and the\n * tooltip's own rendered size each resolve independently into state, and a\n * separate effect computes the final position only once both have arrived.\n * Gating `visible` itself on `measureInWindow`'s callback was tried first\n * and dropped — that callback never fires in this package's Jest\n * environment (no real native view to measure), which would make the\n * component untestable, and in production it would also lose a real\n * quick-tap-then-release press if the callback resolved after `onPressOut`\n * had already fired.\n *\n * `role=\"tooltip\"` ports 1:1 — RN's newer web-aligned `Role` union (the\n * same one `Dialog` uses for `role=\"dialog\"`) already has a `\"tooltip\"`\n * value, no substitution needed. `aria-describedby` has no RN equivalent\n * (no cross-platform description-relationship prop exists), so the label is\n * set as the trigger's `accessibilityHint` instead — read after the\n * trigger's own label by VoiceOver/TalkBack, the nearest analog. The CSS\n * arrow reuses the same zero-size / transparent-border-on-three-sides\n * triangle trick as the web version — RN Views support per-side\n * `border*Color` just like CSS, the same technique `Spinner`'s ring\n * already relies on, applied here to a triangle instead of an arc.\n *\n * Not ported: repositioning on scroll/resize while visible (the web\n * version listens for both) — RN has no global scroll event, and a\n * long-press is naturally cancelled by a scroll gesture starting, so the\n * window to go stale is negligible for the touch trigger; orientation\n * change mid-hover is an accepted edge case for a component this transient.\n * `tooltipBgColor`/`tooltipFgColor` are not real `@gnome-ui/core` tokens\n * (only referenced as CSS var fallbacks, never defined) — hardcoded to the\n * same literal light/dark values as the web fallback, the same workaround\n * `Spinner`'s track color already established for this class of gap.\n *\n * @example\n * <Tooltip label=\"Save file\">\n * <Button accessibilityLabel=\"Save\"><Icon icon={Save} /></Button>\n * </Tooltip>\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/tooltips.html\n */\nexport const Tooltip = ({\n label,\n placement: preferredPlacement = 'top',\n delay = 500,\n children,\n}: TooltipProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const reducedMotion = useReducedMotion();\n\n const [visible, setVisible] = useState(false);\n const [triggerRect, setTriggerRect] = useState<Rect | null>(null);\n const [tooltipSize, setTooltipSize] = useState<Size | null>(null);\n const [pos, setPos] = useState<Position | null>(null);\n\n const triggerRef = useRef<View>(null);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const progress = useRef(new Animated.Value(0)).current;\n\n const clearTimer = useCallback(() => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n const open = useCallback(() => {\n clearTimer();\n setVisible(true);\n }, [clearTimer]);\n\n const openDelayed = useCallback(() => {\n clearTimer();\n timerRef.current = setTimeout(open, delay);\n }, [clearTimer, open, delay]);\n\n const close = useCallback(() => {\n clearTimer();\n setVisible(false);\n }, [clearTimer]);\n\n // Measure the trigger's on-screen rect as soon as it's asked to show.\n useEffect(() => {\n if (!visible) {\n setTriggerRect(null);\n setTooltipSize(null);\n setPos(null);\n\n return;\n }\n\n triggerRef.current?.measureInWindow((x, y, width, height) => {\n setTriggerRect({ x, y, width, height });\n });\n }, [visible]);\n\n // Resolve the final position once both the trigger's rect and the\n // tooltip's own rendered size are known.\n useEffect(() => {\n if (!triggerRect || !tooltipSize) {\n return;\n }\n\n setPos(computePosition(triggerRect, tooltipSize, preferredPlacement));\n }, [triggerRect, tooltipSize, preferredPlacement]);\n\n useEffect(() => {\n if (!pos) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n progress.setValue(0);\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationFast,\n useNativeDriver: true,\n }).start();\n }, [pos, reducedMotion, progress, theme.durationFast]);\n\n useEffect(() => clearTimer, [clearTimer]);\n\n const handleTooltipLayout = (e: LayoutChangeEvent) => {\n const { width, height } = e.nativeEvent.layout;\n\n setTooltipSize({ width, height });\n };\n\n const bg = scheme === 'dark' ? '#c0bfbc' : '#3d3d3d';\n const fg = scheme === 'dark' ? 'rgba(0, 0, 0, 0.8)' : '#fff';\n const shadow =\n scheme === 'dark'\n ? { shadowOpacity: 0.4, shadowRadius: 8, elevation: 6 }\n : { shadowOpacity: 0.2, shadowRadius: 8, elevation: 4 };\n\n const arrowBase = {\n position: 'absolute' as const,\n width: 0,\n height: 0,\n borderWidth: ARROW,\n borderColor: 'transparent',\n };\n\n const arrowStyle = (() => {\n switch (pos?.placement) {\n case 'top':\n return {\n ...arrowBase,\n top: '100%' as const,\n left: '50%' as const,\n marginLeft: -ARROW,\n borderTopColor: bg,\n };\n case 'bottom':\n return {\n ...arrowBase,\n bottom: '100%' as const,\n left: '50%' as const,\n marginLeft: -ARROW,\n borderBottomColor: bg,\n };\n case 'left':\n return {\n ...arrowBase,\n left: '100%' as const,\n top: '50%' as const,\n marginTop: -ARROW,\n borderLeftColor: bg,\n };\n case 'right':\n return {\n ...arrowBase,\n right: '100%' as const,\n top: '50%' as const,\n marginTop: -ARROW,\n borderRightColor: bg,\n };\n default:\n return null;\n }\n })();\n\n const existingRef = (children.props as { ref?: Ref<View> }).ref;\n\n const child = cloneElement(children, {\n ref: (node: View | null) => {\n (triggerRef as { current: View | null }).current = node;\n assignRef(existingRef, node);\n },\n delayLongPress: delay,\n accessibilityHint: children.props.accessibilityHint ?? label,\n onLongPress: (e: GestureResponderEvent) => {\n open();\n children.props.onLongPress?.(e);\n },\n onPressOut: (e: GestureResponderEvent) => {\n close();\n children.props.onPressOut?.(e);\n },\n onHoverIn: (e: MouseEvent) => {\n openDelayed();\n children.props.onHoverIn?.(e);\n },\n onHoverOut: (e: MouseEvent) => {\n close();\n children.props.onHoverOut?.(e);\n },\n onFocus: (e: NativeSyntheticEvent<TargetedEvent>) => {\n openDelayed();\n children.props.onFocus?.(e);\n },\n onBlur: (e: NativeSyntheticEvent<TargetedEvent>) => {\n close();\n children.props.onBlur?.(e);\n },\n });\n\n return (\n <>\n {child}\n <Modal visible={visible} transparent animationType=\"none\" statusBarTranslucent>\n <View pointerEvents=\"box-none\" style={StyleSheet.absoluteFill}>\n <Animated.View\n onLayout={handleTooltipLayout}\n accessible\n role=\"tooltip\"\n style={[\n {\n position: 'absolute',\n maxWidth: 240,\n paddingVertical: 4,\n paddingHorizontal: theme.space2,\n backgroundColor: bg,\n borderRadius: theme.radiusSm,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 2 },\n ...shadow,\n },\n pos\n ? { top: pos.top, left: pos.left, opacity: progress }\n : { top: -9999, left: -9999, opacity: 0 },\n ]}\n >\n <Text\n style={{\n color: fg,\n fontSize: theme.fontSizeCaption,\n fontWeight: String(theme.fontWeightNormal) as TextStyle['fontWeight'],\n lineHeight: theme.fontSizeCaption * theme.lineHeightBody,\n textAlign: 'center',\n }}\n >\n {label}\n </Text>\n {arrowStyle && <View style={arrowStyle} />}\n </Animated.View>\n </View>\n </Modal>\n </>\n );\n};\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport { forwardRef } from 'react';\nimport type { View } from 'react-native';\n\nimport { Button, type ButtonProps, type ButtonSize, type ButtonVariant } from '@/components/Button';\nimport { Icon, type IconSize } from '@/components/Icon';\nimport { Tooltip, type TooltipPlacement } from '@/components/Tooltip';\n\nexport type IconButtonVariant = ButtonVariant | 'osd';\nexport type IconButtonSize = ButtonSize;\n\nexport interface IconButtonProps\n extends Omit<\n ButtonProps,\n 'accessibilityLabel' | 'children' | 'leadingIcon' | 'osd' | 'shape' | 'trailingIcon' | 'variant'\n > {\n /** Icon definition imported from `@gnome-ui/icons`. */\n icon: IconDefinition;\n /** Accessible name for the icon-only button. */\n label: string;\n /** Visual style of the button. Use `\"osd\"` for media overlay controls. */\n variant?: IconButtonVariant;\n /** Size of the button. */\n size?: IconButtonSize;\n /** Override the rendered icon size. Defaults to a size matched to `size`. */\n iconSize?: IconSize;\n /** Optional tooltip label shown on long-press/hover/focus. */\n tooltip?: string;\n /** Preferred tooltip placement. */\n tooltipPlacement?: TooltipPlacement;\n /** Tooltip delay in milliseconds. */\n tooltipDelay?: number;\n}\n\nconst ICON_SIZE_BY_BUTTON_SIZE: Record<IconButtonSize, IconSize> = {\n sm: 'sm',\n md: 'md',\n lg: 'lg',\n};\n\n/**\n * Icon-only action button composed from `Button`, `Icon`, and optionally\n * `Tooltip` — mirrors `@gnome-ui/react`'s `IconButton`, itself already just\n * a thin composition of those same three pieces (its own JSDoc example\n * shows the identical `<Tooltip><Button><Icon /></Button></Tooltip>`\n * nesting `IconButton` here just formalizes into a named, reusable export).\n * `label` is required since the button has no visible text.\n */\nexport const IconButton = forwardRef<View, IconButtonProps>(function IconButton(\n {\n icon,\n label,\n variant = 'default',\n size = 'md',\n iconSize,\n tooltip,\n tooltipPlacement,\n tooltipDelay,\n ...props\n },\n ref,\n) {\n const buttonVariant = variant === 'osd' ? 'flat' : variant;\n\n const button = (\n <Button\n ref={ref}\n accessibilityLabel={label}\n shape=\"circular\"\n size={size}\n variant={buttonVariant}\n osd={variant === 'osd'}\n {...props}\n >\n <Icon icon={icon} size={iconSize ?? ICON_SIZE_BY_BUTTON_SIZE[size]} />\n </Button>\n );\n\n if (!tooltip) {\n return button;\n }\n\n return (\n <Tooltip label={tooltip} placement={tooltipPlacement} delay={tooltipDelay}>\n {button}\n </Tooltip>\n );\n});\n","/**\n * Pure date helpers for `Calendar`. Duplicated verbatim from\n * `@gnome-ui/react`'s `calendarUtils.ts` rather than imported cross-package\n * — the same `fileType.ts`/`coachMarkUtils.ts` precedent for dependency-free\n * logic that isn't worth a shared package for one file's worth of code. Zero\n * DOM dependency on the web side already, so nothing here changes for RN.\n *\n * Every helper operates on the local time zone — a \"day\" is a wall-clock\n * day, never a UTC instant.\n *\n * `fromISODateKey` doesn't make the cut: the web version uses it to decode\n * `data-date` attributes from a single delegated `onMouseOver` listener (a\n * hover-preview feature with no touch equivalent), and nothing else in this\n * package needs to parse an ISO key back into a `Date`. `toISODateKey` stays\n * — every day/week still wants a stable string for React `key`s and test\n * IDs.\n */\n\nexport type WeekStart = 0 | 1 | 2 | 3 | 4 | 5 | 6;\n\n/** Midnight (local) of the given date, as a fresh `Date`. */\nexport const startOfDay = (date: Date): Date =>\n new Date(date.getFullYear(), date.getMonth(), date.getDate());\n\n/** First day of the month containing `date`, at local midnight. */\nexport const startOfMonth = (date: Date): Date => new Date(date.getFullYear(), date.getMonth(), 1);\n\n/** `true` when both dates fall on the same calendar day. */\nexport const isSameDay = (a: Date, b: Date): boolean =>\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate();\n\n/** `true` when both dates fall in the same calendar month. */\nexport const isSameMonth = (a: Date, b: Date): boolean =>\n a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();\n\n/** `date` shifted by `amount` days (may be negative). */\nexport const addDays = (date: Date, amount: number): Date =>\n new Date(date.getFullYear(), date.getMonth(), date.getDate() + amount);\n\n/**\n * The date `year-month-day`, with the day clamped to the last valid day of that\n * month. Used whenever a month or year jump must keep the day-of-month intact.\n */\nexport const clampDayToMonth = (year: number, month: number, day: number): Date => {\n const lastDay = new Date(year, month + 1, 0).getDate();\n return new Date(year, month, Math.min(day, lastDay));\n};\n\n/**\n * `date` shifted by `amount` months, keeping the day-of-month where possible\n * and clamping to the last valid day otherwise (e.g. Jan 31 + 1 month → Feb 28).\n */\nexport const addMonths = (date: Date, amount: number): Date => {\n const target = new Date(date.getFullYear(), date.getMonth() + amount, 1);\n return clampDayToMonth(target.getFullYear(), target.getMonth(), date.getDate());\n};\n\n/**\n * `date` shifted by `amount` years, clamping the day-of-month where the target\n * year is shorter (e.g. 29 Feb 2028 - 1 year → 28 Feb 2027).\n */\nexport const addYears = (date: Date, amount: number): Date =>\n clampDayToMonth(date.getFullYear() + amount, date.getMonth(), date.getDate());\n\n/** How many years one page of the year view shows. */\nexport const YEARS_PER_PAGE = 12;\n\n/** First year of the fixed-size page of years containing `year`. */\nexport const startOfYearPage = (year: number): number =>\n Math.floor(year / YEARS_PER_PAGE) * YEARS_PER_PAGE;\n\n/** Distance in days from the week's start to `date`, given the first weekday. */\nexport const weekdayOffset = (date: Date, weekStartsOn: WeekStart): number =>\n (date.getDay() - weekStartsOn + 7) % 7;\n\n/**\n * The six-week grid (always 6 rows × 7 days) that the web `Calendar` renders\n * for a month — leading/trailing days belong to the adjacent months. A fixed\n * row count avoids the layout jump months of differing length would\n * otherwise cause.\n */\nexport const getCalendarWeeks = (month: Date, weekStartsOn: WeekStart): Date[][] => {\n const first = startOfMonth(month);\n const start = addDays(first, -weekdayOffset(first, weekStartsOn));\n const weeks: Date[][] = [];\n\n for (let w = 0; w < 6; w++) {\n const row: Date[] = [];\n for (let d = 0; d < 7; d++) {\n row.push(addDays(start, w * 7 + d));\n }\n weeks.push(row);\n }\n\n return weeks;\n};\n\n/** ISO-8601 week number (weeks start Monday; week 1 contains the first Thursday). */\nexport const isoWeekNumber = (date: Date): number => {\n // Shift to the Thursday of this ISO week, then count weeks from Jan 1.\n const target = startOfDay(date);\n const dayNr = (target.getDay() + 6) % 7; // Mon = 0 … Sun = 6\n target.setDate(target.getDate() - dayNr + 3);\n const firstThursday = new Date(target.getFullYear(), 0, 4);\n const firstDayNr = (firstThursday.getDay() + 6) % 7;\n firstThursday.setDate(firstThursday.getDate() - firstDayNr + 3);\n const diff = target.getTime() - firstThursday.getTime();\n return 1 + Math.round(diff / (7 * 24 * 60 * 60 * 1000));\n};\n\n/** `true` when `date` is outside the inclusive `[min, max]` range. */\nexport const isOutOfRange = (date: Date, min?: Date, max?: Date): boolean => {\n const day = startOfDay(date).getTime();\n if (min && day < startOfDay(min).getTime()) {\n return true;\n }\n if (max && day > startOfDay(max).getTime()) {\n return true;\n }\n return false;\n};\n\n/** `true` when every day of `date`'s month falls outside the inclusive `[min, max]` range. */\nexport const isMonthOutOfRange = (date: Date, min?: Date, max?: Date): boolean => {\n const first = startOfMonth(date);\n const last = new Date(date.getFullYear(), date.getMonth() + 1, 0);\n return isOutOfRange(last, min, undefined) || isOutOfRange(first, undefined, max);\n};\n\n/** `true` when every day of `date`'s year falls outside the inclusive `[min, max]` range. */\nexport const isYearOutOfRange = (date: Date, min?: Date, max?: Date): boolean => {\n const first = new Date(date.getFullYear(), 0, 1);\n const last = new Date(date.getFullYear(), 11, 31);\n return isOutOfRange(last, min, undefined) || isOutOfRange(first, undefined, max);\n};\n\n/** A stable `YYYY-MM-DD` key for a date, used for React `key`s and test IDs. */\nexport const toISODateKey = (date: Date): string => {\n const y = String(date.getFullYear()).padStart(4, '0');\n const m = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n return `${y}-${m}-${d}`;\n};\n","import { PanDown, PanEnd, PanStart } from '@gnome-ui/icons';\nimport type { ReactNode } from 'react';\nimport { useMemo, useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Pressable, StyleSheet, View } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { IconButton } from '@/components/IconButton';\nimport { Text } from '@/components/Text';\nimport { useDateTimeFormatter, useGnomeTheme } from '@/GnomeProvider';\n\nimport {\n addDays,\n addMonths,\n addYears,\n getCalendarWeeks,\n isMonthOutOfRange,\n isoWeekNumber,\n isSameDay,\n isSameMonth,\n isYearOutOfRange,\n startOfDay,\n startOfMonth,\n startOfYearPage,\n toISODateKey,\n type WeekStart,\n YEARS_PER_PAGE,\n} from './calendarUtils';\n\n/** Which grid the calendar is currently showing. */\nexport type CalendarView = 'days' | 'months' | 'years';\n\n/** How one day cell should be painted, as decided by the selection model. */\nexport interface CalendarDayState {\n /** Endpoint of the selection — filled accent pill. */\n selected?: boolean;\n /** Left edge of a range. */\n rangeStart?: boolean;\n /** Right edge of a range. */\n rangeEnd?: boolean;\n /** Strictly between the two ends of a range. */\n inRange?: boolean;\n /** The range being painted is a preview, not a committed selection. */\n preview?: boolean;\n /** Appended to the day's accessible label, e.g. `', start of range'`. */\n labelSuffix?: string;\n}\n\n/**\n * Everything `CalendarBase` needs to know about *what is selected*, so the\n * grid engine (month/year navigation, day/month/year drill-down) stays free\n * of selection semantics and can drive both `Calendar` and `CalendarRange`.\n *\n * Trimmed from `@gnome-ui/react`'s own `CalendarSelectionModel`: no\n * `seedFocus`/`hoverDay`/`focusDayChange`/`escape` — those exist only to\n * drive the web version's keyboard roving-tabindex and mouse-hover range\n * preview, and this package drops the entire keyboard layer the same way\n * `Calendar` itself already does (no honest RN touch equivalent). Without a\n * hover signal, a range picker's \"preview\" is just its own anchor day until\n * the second tap commits — `CalendarRange` below relies on that rather than\n * tracking a separate preview day.\n */\nexport interface CalendarSelectionModel {\n dayState: (date: Date) => CalendarDayState;\n isDayDisabled?: (date: Date) => boolean;\n activateDay: (date: Date) => void;\n isMonthSelected?: (month: Date) => boolean;\n isYearSelected?: (year: number) => boolean;\n}\n\nexport interface CalendarBaseProps {\n selection: CalendarSelectionModel;\n /** Controlled displayed month (any date within the desired month). */\n month?: Date;\n /**\n * Initial displayed month when uncontrolled. Defaults to `fallbackMonth`,\n * falling back to the current month.\n */\n defaultMonth?: Date;\n /** Called when the displayed month changes via navigation. */\n onMonthChange?: (month: Date) => void;\n /** Seeds the displayed month when neither `month` nor `defaultMonth` is given. */\n fallbackMonth?: Date | null;\n /** Earliest selectable date (inclusive). Also disables out-of-range months/years. */\n min?: Date;\n /** Latest selectable date (inclusive). Also disables out-of-range months/years. */\n max?: Date;\n weekStartsOn?: WeekStart;\n showHeading?: boolean;\n showDayNames?: boolean;\n showWeekNumbers?: boolean;\n showViewSwitcher?: boolean;\n defaultView?: CalendarView;\n onViewChange?: (view: CalendarView) => void;\n accessibilityLabel?: string;\n /** Rendered inside the root, after the grid — status live regions, presets. */\n footer?: ReactNode;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n// A known Sunday, used to derive weekday names in the configured order\n// without depending on today's date.\nconst SUNDAY_REF = new Date(2024, 0, 7);\n\n// Both drill-down grids (12 months, 12 years) are 3 rows × 4 columns, so\n// they read comfortably at phone width without horizontal scrolling.\nconst DRILLDOWN_COLUMNS = 4;\n\n/** Split a flat cell list into the rows of a drill-down grid. */\nconst toRows = <T,>(cells: T[]): T[][] =>\n Array.from({ length: Math.ceil(cells.length / DRILLDOWN_COLUMNS) }, (_, row) =>\n cells.slice(row * DRILLDOWN_COLUMNS, row * DRILLDOWN_COLUMNS + DRILLDOWN_COLUMNS),\n );\n\n// Declared once at module scope (not inside the component) so each is a\n// stable reference — `useDateTimeFormatter`'s `useMemo` keys off `options`\n// identity, and a fresh object literal on every render would defeat it.\nconst MONTH_YEAR_OPTIONS: Intl.DateTimeFormatOptions = { month: 'long', year: 'numeric' };\nconst MONTH_SHORT_OPTIONS: Intl.DateTimeFormatOptions = { month: 'short' };\nconst YEAR_OPTIONS: Intl.DateTimeFormatOptions = { year: 'numeric' };\nconst FULL_DATE_OPTIONS: Intl.DateTimeFormatOptions = {\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n};\nconst WEEKDAY_SHORT_OPTIONS: Intl.DateTimeFormatOptions = { weekday: 'short' };\nconst WEEKDAY_LONG_OPTIONS: Intl.DateTimeFormatOptions = { weekday: 'long' };\n\n/**\n * The shared calendar grid engine: month window, heading navigation, and the\n * day / month / year drill-down grids. It owns *navigation*; `selection`\n * owns *what is selected* — extracted from `Calendar` once `CalendarRange`\n * actually needed the same engine, per the standing note `Calendar` left\n * when it shipped rather than adding this split ahead of need.\n *\n * Not exported from the package barrel — `Calendar` and `CalendarRange` are\n * its public faces, the same \"internal engine, public wrapper\" shape\n * `TimeFields`/`TimePicker` already established.\n */\nexport const CalendarBase = ({\n selection,\n month: controlledMonth,\n defaultMonth,\n onMonthChange,\n fallbackMonth = null,\n min,\n max,\n weekStartsOn = 1,\n showHeading = true,\n showDayNames = true,\n showWeekNumbers = false,\n showViewSwitcher = true,\n defaultView = 'days',\n onViewChange,\n accessibilityLabel,\n footer,\n style,\n testID,\n}: CalendarBaseProps) => {\n const theme = useGnomeTheme();\n\n const isMonthControlled = controlledMonth !== undefined;\n const [uncontrolledMonth, setUncontrolledMonth] = useState<Date>(() =>\n startOfMonth(defaultMonth ?? fallbackMonth ?? new Date()),\n );\n const displayMonth = startOfMonth(isMonthControlled ? controlledMonth : uncontrolledMonth);\n const displayYear = displayMonth.getFullYear();\n\n // Drill-down is only reachable through the heading, so without one the\n // calendar stays on the day grid whatever `defaultView` asks for.\n const canSwitchView = showHeading && showViewSwitcher;\n const [view, setView] = useState<CalendarView>(canSwitchView ? defaultView : 'days');\n\n const today = useMemo(() => startOfDay(new Date()), []);\n\n const weeks = useMemo(\n () => getCalendarWeeks(displayMonth, weekStartsOn),\n [displayMonth, weekStartsOn],\n );\n\n const yearPageStart = startOfYearPage(displayYear);\n const yearPage = useMemo(\n () => Array.from({ length: YEARS_PER_PAGE }, (_, i) => new Date(yearPageStart + i, 0, 1)),\n [yearPageStart],\n );\n const monthsOfYear = useMemo(\n () => Array.from({ length: 12 }, (_, i) => new Date(displayYear, i, 1)),\n [displayYear],\n );\n\n const monthYearFmt = useDateTimeFormatter(MONTH_YEAR_OPTIONS);\n const monthShortFmt = useDateTimeFormatter(MONTH_SHORT_OPTIONS);\n const yearFmt = useDateTimeFormatter(YEAR_OPTIONS);\n const fullDateFmt = useDateTimeFormatter(FULL_DATE_OPTIONS);\n const weekdayShortFmt = useDateTimeFormatter(WEEKDAY_SHORT_OPTIONS);\n const weekdayLongFmt = useDateTimeFormatter(WEEKDAY_LONG_OPTIONS);\n\n const dayNames = useMemo(\n () =>\n Array.from({ length: 7 }, (_, i) => {\n const date = addDays(SUNDAY_REF, (weekStartsOn + i) % 7);\n return { short: weekdayShortFmt.format(date), long: weekdayLongFmt.format(date) };\n }),\n [weekStartsOn, weekdayShortFmt, weekdayLongFmt],\n );\n\n const windowLabel = monthYearFmt.format(displayMonth);\n const yearLabel = yearFmt.format(displayMonth);\n const yearRangeLabel = `${yearFmt.format(yearPage[0])} – ${yearFmt.format(yearPage[YEARS_PER_PAGE - 1])}`;\n const headingLabel =\n view === 'days' ? windowLabel : view === 'months' ? yearLabel : yearRangeLabel;\n\n const changeMonth = (next: Date) => {\n const nextMonth = startOfMonth(next);\n if (!isMonthControlled) {\n setUncontrolledMonth(nextMonth);\n }\n onMonthChange?.(nextMonth);\n };\n\n const changeView = (next: CalendarView) => {\n if (next === view) {\n return;\n }\n setView(next);\n onViewChange?.(next);\n };\n\n const activateDay = (date: Date) => {\n if (selection.isDayDisabled?.(date)) {\n return;\n }\n const day = startOfDay(date);\n if (!isSameMonth(day, displayMonth)) {\n changeMonth(day);\n }\n selection.activateDay(day);\n };\n\n // Picking a month drops back to its day grid; picking a year drops to\n // that year's month grid — the usual two-step way back down from a jump.\n const selectMonth = (date: Date) => {\n if (isMonthOutOfRange(date, min, max)) {\n return;\n }\n changeMonth(date);\n changeView('days');\n };\n\n const selectYear = (date: Date) => {\n if (isYearOutOfRange(date, min, max)) {\n return;\n }\n changeMonth(new Date(date.getFullYear(), displayMonth.getMonth(), 1));\n changeView('months');\n };\n\n // Label tap cycles days → months → years → days, so the day grid is\n // always one more tap away rather than a dead end.\n const nextView: CalendarView = view === 'days' ? 'months' : view === 'months' ? 'years' : 'days';\n const switchHint =\n nextView === 'months'\n ? 'choose a month'\n : nextView === 'years'\n ? 'choose a year'\n : 'back to days';\n\n const navStep = (direction: -1 | 1) => {\n if (view === 'days') {\n changeMonth(addMonths(displayMonth, direction));\n } else if (view === 'months') {\n changeMonth(addYears(displayMonth, direction));\n } else {\n changeMonth(addYears(displayMonth, direction * YEARS_PER_PAGE));\n }\n };\n\n const navLabel = (direction: -1 | 1) => {\n const unit = view === 'days' ? 'month' : view === 'months' ? 'year' : 'years';\n return `${direction === -1 ? 'Previous' : 'Next'} ${unit}`;\n };\n\n const gridLabel =\n accessibilityLabel ??\n (view === 'days'\n ? windowLabel\n : view === 'months'\n ? `Select a month in ${yearLabel}`\n : `Select a year, ${yearRangeLabel}`);\n\n return (\n <View testID={testID} style={[{ gap: theme.space2 }, style]}>\n {showHeading && (\n <View style={styles.heading}>\n <IconButton\n icon={PanStart}\n label={navLabel(-1)}\n variant=\"flat\"\n size=\"sm\"\n onPress={() => navStep(-1)}\n />\n\n {canSwitchView ? (\n <Pressable\n role=\"button\"\n accessibilityLabel={`${headingLabel}, ${switchHint}`}\n onPress={() => changeView(nextView)}\n style={({ pressed }) => [\n styles.headingLabel,\n { borderRadius: theme.radiusMd },\n pressed ? { backgroundColor: theme.activeOverlay } : null,\n ]}\n >\n <Text variant=\"heading\">{headingLabel}</Text>\n <Icon icon={PanDown} size=\"sm\" />\n </Pressable>\n ) : (\n <View style={styles.headingLabel}>\n <Text variant=\"heading\">{headingLabel}</Text>\n </View>\n )}\n\n <IconButton\n icon={PanEnd}\n label={navLabel(1)}\n variant=\"flat\"\n size=\"sm\"\n onPress={() => navStep(1)}\n />\n </View>\n )}\n\n <View\n role=\"grid\"\n accessibilityLabel={gridLabel}\n testID={testID ? `${testID}-grid` : undefined}\n >\n {view === 'days' && (\n <>\n {(showDayNames || showWeekNumbers) && (\n <View role=\"row\" style={styles.row}>\n {showWeekNumbers && (\n <View\n accessible\n role=\"columnheader\"\n accessibilityLabel=\"Week\"\n style={styles.weekNumberCell}\n />\n )}\n {showDayNames &&\n dayNames.map((day) => (\n <View\n key={day.long}\n accessible\n role=\"columnheader\"\n accessibilityLabel={day.long}\n style={styles.headerCell}\n >\n <Text variant=\"caption\" color=\"dim\">\n {day.short}\n </Text>\n </View>\n ))}\n </View>\n )}\n\n {weeks.map((week) => (\n <View role=\"row\" style={styles.row} key={toISODateKey(week[0])}>\n {showWeekNumbers && (\n <View\n accessible\n role=\"rowheader\"\n accessibilityLabel={`Week ${isoWeekNumber(week[0])}`}\n style={styles.weekNumberCell}\n >\n <Text variant=\"caption\" color=\"dim\">\n {isoWeekNumber(week[0])}\n </Text>\n </View>\n )}\n {week.map((date) => {\n const outside = !isSameMonth(date, displayMonth);\n const isToday = isSameDay(date, today);\n const state = selection.dayState(date);\n const disabled = selection.isDayDisabled?.(date) ?? false;\n\n return (\n <DayCell\n key={toISODateKey(date)}\n day={date.getDate()}\n label={`${fullDateFmt.format(date)}${isToday ? ', today' : ''}${state.labelSuffix ?? ''}`}\n outside={outside}\n today={isToday}\n state={state}\n disabled={disabled}\n onPress={() => activateDay(date)}\n testID={testID ? `${testID}-day-${toISODateKey(date)}` : undefined}\n />\n );\n })}\n </View>\n ))}\n </>\n )}\n\n {view === 'months' &&\n toRows(monthsOfYear).map((row) => (\n <View role=\"row\" style={styles.row} key={row[0].getMonth()}>\n {row.map((date) => {\n const isSelected = selection.isMonthSelected?.(date) ?? false;\n const isCurrent = isSameMonth(date, today);\n const disabled = isMonthOutOfRange(date, min, max);\n\n return (\n <DrillCell\n key={date.getMonth()}\n text={monthShortFmt.format(date)}\n label={monthYearFmt.format(date)}\n selected={isSelected}\n current={isCurrent}\n disabled={disabled}\n onPress={() => selectMonth(date)}\n testID={testID ? `${testID}-month-${date.getMonth()}` : undefined}\n />\n );\n })}\n </View>\n ))}\n\n {view === 'years' &&\n toRows(yearPage).map((row) => (\n <View role=\"row\" style={styles.row} key={row[0].getFullYear()}>\n {row.map((date) => {\n const year = date.getFullYear();\n const isSelected = selection.isYearSelected?.(year) ?? false;\n const isCurrent = today.getFullYear() === year;\n const disabled = isYearOutOfRange(date, min, max);\n const text = yearFmt.format(date);\n\n return (\n <DrillCell\n key={year}\n text={text}\n label={text}\n selected={isSelected}\n current={isCurrent}\n disabled={disabled}\n onPress={() => selectYear(date)}\n testID={testID ? `${testID}-year-${year}` : undefined}\n />\n );\n })}\n </View>\n ))}\n </View>\n\n {footer}\n </View>\n );\n};\n\ninterface DayCellProps {\n day: number;\n label: string;\n outside: boolean;\n today: boolean;\n state: CalendarDayState;\n disabled: boolean;\n onPress: () => void;\n testID?: string;\n}\n\n// The range band bleeds half the cell's own padding into each neighbor so\n// consecutive in-range days read as one continuous stripe under the round\n// day buttons — mirrors the web version's `inset-inline: calc(gap/-2)` on\n// the cell's `::before`, ported as a negative inset since RN has no\n// pseudo-elements. Alpha suffixes mirror `Blockquote`/`Chip`'s established\n// `#RRGGBBAA` substitution for the web's `color-mix()` tints (`24` ≈ 14%,\n// `14` ≈ 8% — a preview band reads lighter than a committed one).\nconst BAND_BLEED = 2;\nconst BAND_ALPHA = '24';\nconst BAND_PREVIEW_ALPHA = '14';\nconst END_TINT_ALPHA = '59'; // ≈ 35%, the tentative (not-yet-selected) end of a preview\n\n/** One tappable day-number cell in the day grid. Its own component so each\n * cell reads its theme independently, the same shape `StepIndicator`'s\n * `StepCircle` established for a data-driven grid of same-size buttons. */\nconst DayCell = ({\n day,\n label,\n outside,\n today,\n state,\n disabled,\n onPress,\n testID,\n}: DayCellProps) => {\n const theme = useGnomeTheme();\n const { selected, rangeStart, rangeEnd, inRange, preview } = state;\n const showBand = rangeStart || rangeEnd || inRange;\n\n return (\n <View style={[styles.dayCellWrap, showBand && styles.dayCellWrapRelative]}>\n {showBand && (\n <View\n pointerEvents=\"none\"\n style={[\n styles.rangeBand,\n {\n left: rangeStart ? 0 : -BAND_BLEED,\n right: rangeEnd ? 0 : -BAND_BLEED,\n backgroundColor: `${theme.accentBgColor}${preview ? BAND_PREVIEW_ALPHA : BAND_ALPHA}`,\n borderTopLeftRadius: rangeStart ? theme.radiusPill : 0,\n borderBottomLeftRadius: rangeStart ? theme.radiusPill : 0,\n borderTopRightRadius: rangeEnd ? theme.radiusPill : 0,\n borderBottomRightRadius: rangeEnd ? theme.radiusPill : 0,\n },\n ]}\n />\n )}\n <Pressable\n testID={testID}\n role=\"cell\"\n disabled={disabled}\n accessibilityLabel={label}\n accessibilityState={{ selected: Boolean(selected || inRange), disabled }}\n onPress={onPress}\n style={({ pressed }) => [\n styles.dayButton,\n { borderRadius: theme.radiusPill },\n selected\n ? { backgroundColor: theme.accentBgColor }\n : preview && (rangeStart || rangeEnd)\n ? { backgroundColor: `${theme.accentBgColor}${END_TINT_ALPHA}` }\n : pressed\n ? { backgroundColor: theme.activeOverlay }\n : null,\n today && !selected ? { borderWidth: 1.5, borderColor: theme.accentBgColor } : null,\n { opacity: disabled ? theme.opacityDisabled : outside ? theme.opacityDim : 1 },\n ]}\n >\n <Text variant=\"body\" style={selected ? styles.selectedCellText : undefined}>\n {day}\n </Text>\n </Pressable>\n </View>\n );\n};\n\ninterface DrillCellProps {\n text: string;\n label: string;\n selected: boolean;\n current: boolean;\n disabled: boolean;\n onPress: () => void;\n testID?: string;\n}\n\n/** One tappable month/year cell in a drill-down grid. */\nconst DrillCell = ({\n text,\n label,\n selected,\n current,\n disabled,\n onPress,\n testID,\n}: DrillCellProps) => {\n const theme = useGnomeTheme();\n\n return (\n <View style={styles.drillCellWrap}>\n <Pressable\n testID={testID}\n role=\"cell\"\n disabled={disabled}\n accessibilityLabel={label}\n accessibilityState={{ selected, disabled }}\n onPress={onPress}\n style={({ pressed }) => [\n styles.drillButton,\n { borderRadius: theme.radiusMd },\n selected\n ? { backgroundColor: theme.accentBgColor }\n : pressed\n ? { backgroundColor: theme.activeOverlay }\n : null,\n current && !selected ? { borderWidth: 1.5, borderColor: theme.accentBgColor } : null,\n { opacity: disabled ? theme.opacityDisabled : 1 },\n ]}\n >\n <Text variant=\"body\" style={selected ? styles.selectedCellText : undefined}>\n {text}\n </Text>\n </Pressable>\n </View>\n );\n};\n\nconst styles = StyleSheet.create({\n heading: {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n },\n headingLabel: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 4,\n paddingHorizontal: 8,\n paddingVertical: 4,\n },\n row: {\n flexDirection: 'row',\n },\n headerCell: {\n flex: 1,\n alignItems: 'center',\n justifyContent: 'center',\n paddingVertical: 4,\n },\n weekNumberCell: {\n width: 28,\n alignItems: 'center',\n justifyContent: 'center',\n },\n dayCellWrap: {\n flex: 1,\n aspectRatio: 1,\n alignItems: 'center',\n justifyContent: 'center',\n padding: 2,\n },\n dayCellWrapRelative: {\n position: 'relative',\n },\n rangeBand: {\n position: 'absolute',\n top: 0,\n bottom: 0,\n },\n dayButton: {\n width: '100%',\n height: '100%',\n alignItems: 'center',\n justifyContent: 'center',\n },\n drillCellWrap: {\n flex: 1,\n padding: 4,\n },\n drillButton: {\n alignItems: 'center',\n justifyContent: 'center',\n paddingVertical: 14,\n },\n selectedCellText: {\n color: '#fff',\n },\n});\n","import { useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\n\nimport { CalendarBase, type CalendarSelectionModel, type CalendarView } from './CalendarBase';\nimport { isOutOfRange, isSameDay, isSameMonth, type WeekStart } from './calendarUtils';\n\nexport type { CalendarView } from './CalendarBase';\n\nexport interface CalendarProps {\n /** Controlled selected date. Pass `null` for \"no selection\". */\n value?: Date | null;\n /** Initial selected date when uncontrolled. Defaults to `null`. */\n defaultValue?: Date | null;\n /** Called when the user taps a day. */\n onChange?: (date: Date) => void;\n /** Controlled displayed month (any date within the desired month). */\n month?: Date;\n /**\n * Initial displayed month when uncontrolled. Defaults to the selected\n * date's month, falling back to the current month.\n */\n defaultMonth?: Date;\n /** Called when the displayed month changes via navigation. */\n onMonthChange?: (month: Date) => void;\n /** Earliest selectable date (inclusive). Earlier days are disabled. */\n min?: Date;\n /** Latest selectable date (inclusive). Later days are disabled. */\n max?: Date;\n /**\n * First day of the week: `0` (Sunday) … `6` (Saturday). Defaults to `1`\n * (Monday) — the GNOME default across most locales.\n */\n weekStartsOn?: WeekStart;\n /** Show the month/year heading with prev/next navigation. Mirrors `GtkCalendar:show-heading`. Defaults to `true`. */\n showHeading?: boolean;\n /** Show the abbreviated day-name column headers. Mirrors `GtkCalendar:show-day-names`. Defaults to `true`. */\n showDayNames?: boolean;\n /** Show an ISO week-number column. Mirrors `GtkCalendar:show-week-numbers`. Defaults to `false`. */\n showWeekNumbers?: boolean;\n /**\n * Turn the heading label into a button that drills down day grid → month\n * grid → year grid, the way modern date pickers let you jump years\n * without paging month by month. Requires `showHeading`. Defaults to `true`.\n */\n showViewSwitcher?: boolean;\n /** Grid shown on mount: `'days'`, `'months'` or `'years'`. Defaults to `'days'`. */\n defaultView?: CalendarView;\n /** Called when the drill-down view changes. */\n onViewChange?: (view: CalendarView) => void;\n /** Accessible label for the grid. Defaults to the current heading label. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Month-grid date display — mirrors `GtkCalendar` and `@gnome-ui/react`'s own\n * `Calendar`. Usable standalone (settings, forms) or as the panel inside\n * `DatePicker`.\n *\n * The heading label drills down day grid → month grid → year grid so a\n * distant year is two taps away instead of many pages, exactly as on the web.\n * Leading/trailing days from adjacent months are shown dimmed and remain\n * tappable — tapping one navigates to that month, matching `GtkCalendar`.\n *\n * A thin single-day `CalendarSelectionModel` over `CalendarBase` — the\n * shared grid engine (month window, heading, day/month/year drill-down)\n * extracted once `CalendarRange` actually needed it, per the note this\n * component left when it first shipped rather than adding the split ahead\n * of need.\n *\n * **The web version's entire keyboard layer drops here** (roving tabindex,\n * arrow keys, PageUp/Down, Home/End, Enter/Space) — this package is\n * touch-first, and unlike `Slider`'s 1D `accessibilityRole=\"adjustable\"`\n * escape hatch, there is no screen-reader analog for paging a full 2D date\n * grid. Tap-to-select on each cell is the strict touch subset of the web\n * interaction, the same trade-off already made for `RatingStars`/\n * `ToggleGroup`/`ColorPicker`. `role=\"grid\"`/`\"row\"` port 1:1 from RN's\n * web-aligned `Role` union; `\"gridcell\"` doesn't exist in that union, so\n * cells fall back to `\"cell\"`, the same kind of substitution `BoxedList`\n * (`\"list\"`) and `ComboRow` (`\"listbox\"` → `\"list\"`) already made. The grid\n * and its rows deliberately skip `accessible` — the `ToggleGroup`-corrected\n * pattern: setting it on a container with many independently-focusable\n * children (each day) would collapse the whole month into one VoiceOver\n * stop. Column/row *headers* (day names, week numbers) hold no interactive\n * children, so they do set `accessible` to be announced as a single unit.\n *\n * `visibleMonths` (side-by-side month panels) and `autoFocus`\n * (keyboard-focus-on-mount) are dropped, not merely unimplemented: both are\n * desktop/keyboard concerns with no honest RN counterpart — a phone screen\n * has no room for two month panels side by side, and there is no keyboard to\n * focus. `locale` is dropped too, in favor of the app-wide locale\n * `GnomeProvider` already exposes through `useDateTimeFormatter`.\n *\n * For start/end selection use `CalendarRange`, which shares this same grid\n * engine via `CalendarBase`.\n *\n * @see https://gnome.pages.gitlab.gnome.org/gtk/gtk4/class.Calendar.html\n */\nexport const Calendar = ({\n value: controlledValue,\n defaultValue = null,\n onChange,\n min,\n max,\n ...props\n}: CalendarProps) => {\n const isValueControlled = controlledValue !== undefined;\n const [uncontrolledValue, setUncontrolledValue] = useState<Date | null>(defaultValue);\n const selected = isValueControlled ? controlledValue : uncontrolledValue;\n\n const selection: CalendarSelectionModel = {\n dayState: (date) => (selected && isSameDay(date, selected) ? { selected: true } : {}),\n isDayDisabled: (date) => isOutOfRange(date, min, max),\n activateDay: (day) => {\n if (!isValueControlled) {\n setUncontrolledValue(day);\n }\n onChange?.(day);\n },\n isMonthSelected: (month) => (selected ? isSameMonth(month, selected) : false),\n isYearSelected: (year) => selected?.getFullYear() === year,\n };\n\n return (\n <CalendarBase selection={selection} fallbackMonth={selected} min={min} max={max} {...props} />\n );\n};\n","/**\n * Pure helpers for `CalendarRange`. Kept dependency-free and side-effect-free,\n * mirroring the `calendarUtils.ts` split `Calendar` already uses. Ported\n * verbatim from `@gnome-ui/react`'s own `rangeUtils.ts` — zero DOM\n * dependency there already.\n */\n\nimport { startOfDay } from '@/components/Calendar/calendarUtils';\n\n/** A range under construction — either end may still be missing. */\nexport interface DateRange {\n start: Date | null;\n end: Date | null;\n}\n\n/** A finished range: both ends have a value. The only shape `onChange` emits. */\nexport interface SelectedDateRange {\n start: Date;\n end: Date;\n}\n\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\n\n/** The empty range, as a fresh object so callers never share one. */\nexport const emptyRange = (): DateRange => ({ start: null, end: null });\n\n/** Both dates as a range, oldest first — so picking backwards still works. */\nexport const orderRange = (a: Date, b: Date): SelectedDateRange => {\n const first = startOfDay(a);\n const second = startOfDay(b);\n return first.getTime() <= second.getTime()\n ? { start: first, end: second }\n : { start: second, end: first };\n};\n\n/**\n * Length of the range in days, counting both ends (so a single day is `1`).\n * Rounded, because a DST change makes a \"day\" 23 or 25 hours long.\n */\nexport const rangeLength = (range: SelectedDateRange): number =>\n Math.round((startOfDay(range.end).getTime() - startOfDay(range.start).getTime()) / MS_PER_DAY) +\n 1;\n\n/** `true` when `date` falls on or between both ends. */\nexport const isWithinRange = (date: Date, range: SelectedDateRange): boolean => {\n const day = startOfDay(date).getTime();\n return day >= startOfDay(range.start).getTime() && day <= startOfDay(range.end).getTime();\n};\n\n/** `true` when the range satisfies the `minRange`/`maxRange` day limits. */\nexport const isRangeAllowed = (\n range: SelectedDateRange,\n minRange = 1,\n maxRange?: number,\n): boolean => {\n const length = rangeLength(range);\n return length >= minRange && (maxRange === undefined || length <= maxRange);\n};\n\n/** `true` when both ends have a value — the condition for emitting a change. */\nexport const isRangeComplete = (range: DateRange): range is SelectedDateRange =>\n range.start !== null && range.end !== null;\n","import { useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Text } from 'react-native';\n\nimport {\n CalendarBase,\n type CalendarSelectionModel,\n type CalendarView,\n} from '@/components/Calendar/CalendarBase';\nimport {\n isOutOfRange,\n isSameDay,\n isSameMonth,\n startOfDay,\n type WeekStart,\n} from '@/components/Calendar/calendarUtils';\nimport { useDateTimeFormatter } from '@/GnomeProvider';\n\nimport {\n type DateRange,\n emptyRange,\n isRangeAllowed,\n isWithinRange,\n orderRange,\n type SelectedDateRange,\n} from './rangeUtils';\n\nexport type { DateRange, SelectedDateRange } from './rangeUtils';\n\nexport interface CalendarRangeProps {\n /** Controlled range. Either end may be `null` while nothing is chosen. */\n value?: DateRange | null;\n /** Initial range when uncontrolled. Defaults to an empty range. */\n defaultValue?: DateRange | null;\n /**\n * Called with the finished range — **only once both ends have a value**.\n * The first tap merely anchors the range, so this never fires with a half\n * selection.\n */\n onChange?: (range: SelectedDateRange) => void;\n /** Called with the anchor day when the first of the two taps lands. */\n onRangeStart?: (date: Date) => void;\n /** Controlled displayed month (any date within the desired month). */\n month?: Date;\n /**\n * Initial displayed month when uncontrolled. Defaults to the range's\n * start month, falling back to the current month.\n */\n defaultMonth?: Date;\n /** Called when the displayed month changes via navigation. */\n onMonthChange?: (month: Date) => void;\n /** Earliest selectable date (inclusive). */\n min?: Date;\n /** Latest selectable date (inclusive). */\n max?: Date;\n /** Shortest range the user may commit, in days (both ends counted). Defaults to `1`. */\n minRange?: number;\n /** Longest range the user may commit, in days (both ends counted). Unlimited by default. */\n maxRange?: number;\n weekStartsOn?: WeekStart;\n showHeading?: boolean;\n showDayNames?: boolean;\n showWeekNumbers?: boolean;\n showViewSwitcher?: boolean;\n defaultView?: CalendarView;\n onViewChange?: (view: CalendarView) => void;\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n// Visually hides the live-region status announcement without removing it\n// from the accessibility tree — same recipe `CopyButton`'s own\n// `LIVE_REGION_STYLE` already established (this package has no extracted\n// `VisuallyHidden` component yet — see the ROADMAP's own note on that gap).\nconst LIVE_REGION_STYLE = {\n position: 'absolute' as const,\n width: 1,\n height: 1,\n overflow: 'hidden' as const,\n};\n\n/**\n * Start/end date-range selection driving the same grid engine as `Calendar`\n * — month/year drill-down, `min`/`max`, week numbers — via `CalendarBase`,\n * the shared engine `Calendar` extracted once this component actually\n * needed it.\n *\n * The first tap anchors the range; the second commits it. `onChange`\n * therefore only ever fires with **both** ends filled in. Tapping backwards\n * is fine — the pair is ordered before it is emitted.\n *\n * **No live drag-preview band, unlike the web version.** The web\n * `CalendarRange` grows the band under the mouse/keyboard focus between the\n * two clicks; RN has no hover and this package's whole keyboard layer is\n * already dropped (see `Calendar`'s own doc comment), so there is no signal\n * to preview against. The anchor day simply shows as a normal selected day\n * until the second tap lands and the full band appears at once — the\n * touch-first subset of the same interaction, the same kind of trade-off\n * `RatingStars`/`ToggleGroup`/`Calendar` itself already made for dropped\n * hover/keyboard affordances. `CalendarBase`'s `CalendarSelectionModel` was\n * trimmed to match — no `hoverDay`/`focusDayChange` to wire up here.\n *\n * `visibleMonths` (side-by-side month panels, the usual desktop range-picker\n * shape) has no phone-width equivalent — dropped, the same `Calendar`\n * `visibleMonths` precedent. `locale` is dropped too, in favor of\n * `GnomeProvider`'s app-wide `useDateTimeFormatter`.\n *\n * @see https://gnome.pages.gitlab.gnome.org/gtk/gtk4/class.Calendar.html\n */\nexport const CalendarRange = ({\n value: controlledValue,\n defaultValue,\n onChange,\n onRangeStart,\n min,\n max,\n minRange = 1,\n maxRange,\n ...props\n}: CalendarRangeProps) => {\n const isControlled = controlledValue !== undefined;\n const [uncontrolledValue, setUncontrolledValue] = useState<DateRange>(\n () => defaultValue ?? emptyRange(),\n );\n const committed = (isControlled ? controlledValue : uncontrolledValue) ?? emptyRange();\n\n // The half-made range: the committed first tap, waiting on the second.\n const [anchor, setAnchor] = useState<Date | null>(null);\n\n // What the grid paints right now: the range being drawn wins over the\n // committed one, so a new selection replaces the old band immediately.\n const active = anchor\n ? { ...orderRange(anchor, anchor), pending: true }\n : committed.start && committed.end\n ? { ...orderRange(committed.start, committed.end), pending: false }\n : committed.start || committed.end\n ? (() => {\n const only = startOfDay((committed.start ?? committed.end) as Date);\n return { start: only, end: only, pending: false };\n })()\n : null;\n\n const rangeFmt = useDateTimeFormatter({ dateStyle: 'long' });\n\n const isDayDisabled = (date: Date) => {\n if (isOutOfRange(date, min, max)) {\n return true;\n }\n // While anchoring, days that could only produce a too-short or too-long\n // range are unreachable rather than silently rejected on tap.\n return anchor ? !isRangeAllowed(orderRange(anchor, date), minRange, maxRange) : false;\n };\n\n const selection: CalendarSelectionModel = {\n isDayDisabled,\n dayState: (date) => {\n if (!active) {\n return {};\n }\n const isStart = isSameDay(date, active.start);\n const isEnd = isSameDay(date, active.end);\n if (!isStart && !isEnd) {\n return isWithinRange(date, active)\n ? { inRange: true, preview: active.pending, labelSuffix: ', in selected range' }\n : {};\n }\n return {\n rangeStart: isStart,\n rangeEnd: isEnd,\n preview: active.pending,\n // Mid-selection only the clicked anchor is a real end; the other\n // side is still just a preview, so it must not read as chosen.\n selected: !active.pending || (anchor ? isSameDay(date, anchor) : false),\n labelSuffix:\n isStart && isEnd\n ? ', start and end of range'\n : isStart\n ? ', start of range'\n : ', end of range',\n };\n },\n activateDay: (day) => {\n if (!anchor) {\n setAnchor(day);\n onRangeStart?.(day);\n return;\n }\n const next = orderRange(anchor, day);\n setAnchor(null);\n if (!isControlled) {\n setUncontrolledValue(next);\n }\n onChange?.(next);\n },\n isMonthSelected: (month) =>\n Boolean(\n (committed.start && isSameMonth(month, committed.start)) ||\n (committed.end && isSameMonth(month, committed.end)),\n ),\n isYearSelected: (year) =>\n committed.start?.getFullYear() === year || committed.end?.getFullYear() === year,\n };\n\n const status = anchor\n ? `Start date ${rangeFmt.format(anchor)} selected. Choose an end date.`\n : committed.start && committed.end\n ? `Range ${rangeFmt.format(committed.start)} to ${rangeFmt.format(committed.end)} selected.`\n : '';\n\n return (\n <CalendarBase\n selection={selection}\n fallbackMonth={committed.start ?? committed.end ?? null}\n min={min}\n max={max}\n footer={\n <Text role=\"status\" accessibilityLiveRegion=\"polite\" style={LIVE_REGION_STYLE}>\n {status}\n </Text>\n }\n {...props}\n />\n );\n};\n","import { Close, DialogInformation, DialogWarning } from '@gnome-ui/icons';\nimport type { ReactNode } from 'react';\nimport type { StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { Pressable, View } from 'react-native';\n\nimport { Icon, type IconColor } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type CalloutVariant = 'info' | 'warning' | 'tip';\n\nexport interface CalloutProps extends Omit<ViewProps, 'style'> {\n /**\n * Visual emphasis level.\n * - `info` (default) — neutral, accent-colored. General contextual notes.\n * - `warning` — yellow. Recoverable problems or things to double-check.\n * - `tip` — green. Optional suggestions or shortcuts.\n */\n variant?: CalloutVariant;\n /** The message content. */\n children: ReactNode;\n /** When true a dismiss (×) button is shown at the trailing edge. */\n dismissible?: boolean;\n /** Called when the user presses the dismiss button. */\n onDismiss?: () => void;\n style?: StyleProp<ViewStyle>;\n}\n\nconst VARIANT_ICON = {\n info: DialogInformation,\n warning: DialogWarning,\n tip: DialogInformation,\n};\n\n/**\n * `Icon`'s `color` prop only accepts a fixed named-swatch palette (no\n * `currentColor`/arbitrary-hex equivalent in RN) — but `blue3`/`green4`\n * resolve to the exact same hex as `accentBgColor`/`successBgColor` in\n * every theme variant this package ships, so `info`/`tip` are an exact\n * match, not an approximation. `warning`'s `yellow4` is deliberately\n * darker than `warningBgColor` itself, the same contrast intent as the\n * source CSS's own `color-mix(in srgb, warning 70%, black)` icon tint.\n */\nconst VARIANT_ICON_COLOR: Record<CalloutVariant, IconColor> = {\n info: 'blue',\n warning: 'yellow',\n tip: 'green',\n};\n\n/**\n * The source CSS's `color-mix(in srgb, <color> 10%/15%, transparent)`\n * tinted backgrounds (`info`/`tip` are 10%, `warning` is 15%) have no RN\n * equivalent — resolved as an 8-digit `#RRGGBBAA` alpha suffix on the\n * variant's own hex token instead, the same substitution `Chip`'s\n * selected-state tint and `Blockquote`'s tinted backgrounds already\n * established. `0x1A` ≈ 10%, `0x26` ≈ 15%.\n */\nconst VARIANT_TINT: Record<CalloutVariant, string> = {\n info: '1a',\n warning: '26',\n tip: '1a',\n};\n\n/**\n * Inline, dismissible admonition box for contextual help text within forms\n * and cards. Mirrors `@gnome-ui/react`'s `Callout`.\n *\n * Unlike `Banner` (a persistent, edge-to-edge strip at the top of a view)\n * and `Toast` (a temporary notification), `Callout` is a contained, tinted\n * box meant to sit inline alongside the content it annotates.\n *\n * `role=\"note\"` ports 1:1 from RN's newer web-aligned `Role` union (the\n * same one `Dialog`/`Tooltip` already use) — `accessible` is set alongside\n * it, per the `BoxedList` lesson that a bare `View` isn't an accessibility\n * element by default. The leading icon is decorative, hidden from\n * assistive tech the same way `Blockquote`'s own icon is.\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/\n */\nexport const Callout = ({\n variant = 'info',\n children,\n dismissible = false,\n onDismiss,\n style,\n ...viewProps\n}: CalloutProps) => {\n const theme = useGnomeTheme();\n\n const variantColor = {\n info: theme.accentBgColor,\n warning: theme.warningBgColor,\n tip: theme.successBgColor,\n }[variant];\n\n return (\n <View\n role=\"note\"\n accessible\n style={[\n {\n flexDirection: 'row',\n alignItems: 'flex-start',\n gap: theme.space2,\n padding: theme.space2,\n paddingHorizontal: theme.space3,\n borderRadius: theme.radiusMd,\n borderStartWidth: 3,\n borderStartColor: variantColor,\n backgroundColor: `${variantColor}${VARIANT_TINT[variant]}`,\n },\n style,\n ]}\n {...viewProps}\n >\n <View style={{ marginTop: 2 }} accessibilityElementsHidden importantForAccessibility=\"no\">\n <Icon icon={VARIANT_ICON[variant]} size=\"md\" color={VARIANT_ICON_COLOR[variant]} />\n </View>\n\n <Text variant=\"body\" style={{ flex: 1, minWidth: 0 }}>\n {children}\n </Text>\n\n {dismissible && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel=\"Dismiss\"\n hitSlop={4}\n onPress={onDismiss}\n style={({ pressed }) => [\n {\n flexShrink: 0,\n marginTop: -2,\n padding: 4,\n borderRadius: theme.radiusMd,\n opacity: pressed ? 1 : 0.6,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n },\n ]}\n >\n <View pointerEvents=\"none\">\n <Icon icon={Close} size=\"sm\" />\n </View>\n </Pressable>\n )}\n </View>\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { PressableProps, StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { Pressable, View as RNView, StyleSheet } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type CardPadding = 'none' | 'sm' | 'md' | 'lg';\n\nexport interface CardProps extends Omit<PressableProps, 'style' | 'children'> {\n /**\n * When true the card becomes pressable (Adwaita `.activatable`).\n * Renders as `Pressable` with `accessibilityRole=\"button\"`; a plain\n * `View` otherwise. `onPress`/`onLongPress`/etc. only take effect when\n * `interactive` is true — RN's `View` has no touch handling of its own,\n * unlike a DOM `<div>`'s `onClick`.\n */\n interactive?: boolean;\n /** Internal spacing. Defaults to `\"md\"`. */\n padding?: CardPadding;\n style?: StyleProp<ViewStyle>;\n children?: ReactNode;\n}\n\nfunction getPadding(theme: GnomeThemeTokens, padding: CardPadding): number {\n switch (padding) {\n case 'none':\n return 0;\n case 'sm':\n return theme.space2;\n case 'lg':\n return theme.space5;\n default:\n return theme.space4;\n }\n}\n\n/**\n * Card component following the GNOME HIG and Adwaita `.card` style class.\n *\n * Use for grouping related content on an elevated surface. Set\n * `interactive` for clickable cards (e.g. grid item, settings shortcut).\n *\n * Rebuilt with `Pressable`/`View` rather than ported from `@gnome-ui/react`'s\n * DOM-based `<button>`/`<div>` + `as` prop — RN has no polymorphic-element\n * equivalent, so `as` is dropped. The web version's `:hover`/`:active`\n * `color-mix()` darken/lighten is reproduced with an absolutely-positioned\n * overlay `View` tinted by `theme.activeOverlay` while pressed, since RN\n * has no `color-mix()` to blend two flat colors directly.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/style-classes.html\n * @see https://developer.gnome.org/hig/patterns/containers.html\n */\nexport const Card = forwardRef<View, CardProps>(function Card(\n { interactive = false, padding = 'md', style, children, disabled, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n const baseStyle: ViewStyle = {\n backgroundColor: theme.cardBgColor,\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusLg,\n overflow: 'hidden',\n padding: getPadding(theme, padding),\n };\n\n if (!interactive) {\n return (\n <RNView ref={ref} style={[baseStyle, style]} {...(pressableProps as ViewProps)}>\n {children}\n </RNView>\n );\n }\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled: !!disabled }}\n style={[\n baseStyle,\n { position: 'relative', opacity: disabled ? theme.opacityDisabled : 1 },\n style,\n ]}\n {...pressableProps}\n >\n {({ pressed }) => (\n <>\n {children}\n {pressed && !disabled ? (\n <RNView\n pointerEvents=\"none\"\n style={[\n StyleSheet.absoluteFill,\n { backgroundColor: theme.activeOverlay, borderRadius: theme.radiusLg },\n ]}\n />\n ) : null}\n </>\n )}\n </Pressable>\n );\n});\n","import { forwardRef, useEffect, useRef } from 'react';\nimport type { PressableProps, StyleProp, View, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable } from 'react-native';\n\nimport { useGnomeTheme, useResolvedColorScheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface CheckboxProps extends Omit<PressableProps, 'children' | 'style' | 'onPress'> {\n /** Whether the checkbox is checked. */\n value: boolean;\n /**\n * Indeterminate state — shown when only some items in a group are\n * checked. Takes visual precedence over `value`.\n */\n indeterminate?: boolean;\n /** Called with the next value when the checkbox is pressed. */\n onValueChange?: (value: boolean) => void;\n /** Accessible label. Required when no visible label is associated. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\nconst SIZE = 18;\n\n/**\n * Checkbox for multi-selection, following the GNOME HIG and Adwaita style.\n *\n * Supports three states: unchecked, checked, and indeterminate (mixed).\n * Use `indeterminate` for \"select all\" controls when only some items are\n * selected.\n *\n * Rebuilt on `Pressable`/`Animated.View` rather than ported from\n * `@gnome-ui/react`'s `<input type=\"checkbox\">`: RN has no `indeterminate`\n * DOM property to set imperatively (the web version's whole reason for a\n * ref + effect), so it's just a rendering branch here — `indeterminate`\n * draws a short bar, otherwise a checkmark, both fading in via the same\n * `Animated.Value` used for the border/background transition. The\n * checkmark itself is a `✓` glyph in a plain (non-themed) `Text` rather\n * than the web version's `clip-path` polygon — this package has no SVG\n * dependency to draw one exactly, and `Link`'s external-link indicator\n * already established a Unicode glyph as this codebase's fallback for a\n * small decorative mark with no icon library available.\n *\n * `value`/`onValueChange` mirror the same convention as `Switch`, RN's own\n * toggle-control shape, rather than the web version's `checked`/`onChange`.\n * The idle border color is another `Switch`-style case where the source\n * CSS hardcodes a palette swatch per color scheme instead of a token that\n * already resolves per theme, so it branches on `useResolvedColorScheme()`\n * the same way.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/checkboxes.html\n */\nexport const Checkbox = forwardRef<View, CheckboxProps>(function Checkbox(\n { value, indeterminate = false, onValueChange, disabled, style, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const colorScheme = useResolvedColorScheme();\n const contrast = useResolvedContrast();\n const marked = value || indeterminate;\n const progress = useRef(new Animated.Value(marked ? 1 : 0)).current;\n const isInitialMount = useRef(true);\n\n useEffect(() => {\n // The initial `Animated.Value` above already matches `marked` — skip\n // animating into a state the checkbox is already rendering, so it\n // never plays a transition before the user has touched it.\n if (isInitialMount.current) {\n isInitialMount.current = false;\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: marked ? 1 : 0,\n duration: theme.durationFast,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: false,\n }).start();\n }, [marked, progress, theme.durationFast, theme.easingDefault]);\n\n const idleBorderColor =\n contrast === 'more' ? theme.windowFgColor : colorScheme === 'dark' ? theme.dark1 : theme.light4;\n\n const borderColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [idleBorderColor, theme.accentBgColor],\n });\n const backgroundColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.cardBgColor, theme.accentBgColor],\n });\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"checkbox\"\n accessibilityState={{ checked: indeterminate ? 'mixed' : value, disabled: !!disabled }}\n onPress={() => onValueChange?.(!value)}\n style={[{ opacity: disabled ? theme.opacityDisabled : 1 }, style]}\n {...pressableProps}\n >\n <Animated.View\n style={{\n width: SIZE,\n height: SIZE,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusSm,\n borderWidth: contrast === 'more' ? 2 : 1.5,\n borderColor,\n backgroundColor,\n }}\n >\n {indeterminate ? (\n <Animated.View\n style={{\n width: 10,\n height: 2,\n borderRadius: 1,\n backgroundColor: '#fff',\n opacity: progress,\n }}\n />\n ) : (\n <Animated.Text\n style={{\n opacity: progress,\n color: '#fff',\n fontSize: 13,\n fontWeight: 'bold',\n lineHeight: 13,\n }}\n >\n {'✓'}\n </Animated.Text>\n )}\n </Animated.View>\n </Pressable>\n );\n});\n","import { forwardRef, type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { PressableProps, StyleProp, TextStyle, View, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable, View as RNView, StyleSheet } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useResolvedColorScheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface CheckRowProps extends Omit<PressableProps, 'children' | 'style' | 'onPress'> {\n /** Primary label. */\n title: string;\n /** Secondary line below the title. */\n subtitle?: string;\n /** Icon or image placed at the leading edge, after the checkbox. */\n leading?: ReactNode;\n /** Controlled checked state. */\n checked?: boolean;\n /** Initial checked state when uncontrolled. Defaults to `false`. */\n defaultChecked?: boolean;\n /** Called with the next value when the row is pressed. */\n onCheckedChange?: (checked: boolean) => void;\n style?: StyleProp<ViewStyle>;\n}\n\nconst CHECKBOX_SIZE = 20;\n\n/**\n * Activatable row with an integrated checkbox, mirroring\n * `@gnome-ui/react`'s `CheckRow`. The entire row is a single pressable —\n * pressing anywhere toggles the checked state. Use inside a `BoxedList`\n * when a user must select or deselect individual items in a list; prefer a\n * `SwitchRow` (once ported) for a single on/off setting.\n *\n * Supports both controlled (`checked`) and uncontrolled (`defaultChecked`)\n * modes, the same `isControlled`/internal-state-fallback shape already\n * established by `Expander`/`ComboRow`/`Popover`.\n *\n * The checkbox visual reuses `Checkbox`'s exact border/background\n * `Animated.Value` interpolation and checkmark-fade-in recipe — but as a\n * plain, non-interactive `View` rather than importing the real `Checkbox`\n * component, since `Checkbox` is itself a `Pressable` and nesting one\n * touchable inside another (the row's own `Pressable`) would create two\n * overlapping tap targets. `aria-labelledby` (pointing the web button's\n * `role=\"checkbox\"` at the title/subtitle content) has no RN equivalent —\n * `accessibilityLabel` combining title and subtitle is the substitution,\n * the same \"no relationship attribute\" gap `Tooltip`'s dropped\n * `aria-describedby` already established. The title reads `theme.cardFgColor`\n * directly (not `Text`'s own `\"default\"`, which resolves to\n * `windowFgColor`) to match the source CSS's `--gnome-card-fg-color`\n * ambient row color exactly — the two tokens happen to share the same\n * value in every theme this package ships, but the source CSS's intent is\n * specifically \"card foreground,\" so the port keeps that distinction.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.CheckButton.html\n */\nexport const CheckRow = forwardRef<View, CheckRowProps>(function CheckRow(\n {\n title,\n subtitle,\n leading,\n checked: controlledChecked,\n defaultChecked = false,\n onCheckedChange,\n disabled,\n style,\n ...pressableProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const colorScheme = useResolvedColorScheme();\n const contrast = useResolvedContrast();\n\n const isControlled = controlledChecked !== undefined;\n const [uncontrolledChecked, setUncontrolledChecked] = useState(defaultChecked);\n const checked = isControlled ? controlledChecked : uncontrolledChecked;\n\n const progress = useRef(new Animated.Value(checked ? 1 : 0)).current;\n const isInitialMount = useRef(true);\n\n useEffect(() => {\n if (isInitialMount.current) {\n isInitialMount.current = false;\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: checked ? 1 : 0,\n duration: theme.durationFast,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: false,\n }).start();\n }, [checked, progress, theme.durationFast, theme.easingDefault]);\n\n const idleBorderColor =\n contrast === 'more' ? theme.windowFgColor : colorScheme === 'dark' ? theme.dark1 : theme.light4;\n\n const borderColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [idleBorderColor, theme.accentBgColor],\n });\n const backgroundColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: ['transparent', theme.accentBgColor],\n });\n\n const handlePress = () => {\n const next = !checked;\n\n if (!isControlled) {\n setUncontrolledChecked(next);\n }\n\n onCheckedChange?.(next);\n };\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"checkbox\"\n accessibilityState={{ checked, disabled: !!disabled }}\n accessibilityLabel={subtitle ? `${title}, ${subtitle}` : title}\n onPress={handlePress}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: theme.space2,\n paddingHorizontal: theme.space4,\n minHeight: 52,\n width: '100%',\n opacity: disabled ? theme.opacityDisabled : 1,\n },\n style,\n ]}\n {...pressableProps}\n >\n {({ pressed }) => (\n <>\n <Animated.View\n style={{\n width: CHECKBOX_SIZE,\n height: CHECKBOX_SIZE,\n flexShrink: 0,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusSm,\n borderWidth: contrast === 'more' ? 2 : 1.5,\n borderColor,\n backgroundColor,\n }}\n >\n <Animated.Text\n style={{\n opacity: progress,\n color: '#fff',\n fontSize: 13,\n fontWeight: 'bold',\n lineHeight: 13,\n }}\n >\n {'✓'}\n </Animated.Text>\n </Animated.View>\n\n {leading && <RNView style={{ flexShrink: 0 }}>{leading}</RNView>}\n\n <RNView style={{ flex: 1, gap: 2 }}>\n <Text\n variant=\"body\"\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n color: theme.cardFgColor,\n fontWeight: String(theme.fontWeightNormal) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n {subtitle && (\n <Text variant=\"caption\" color=\"dim\" numberOfLines={1} ellipsizeMode=\"tail\">\n {subtitle}\n </Text>\n )}\n </RNView>\n\n {pressed && !disabled ? (\n <RNView\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { backgroundColor: theme.activeOverlay }]}\n />\n ) : null}\n </>\n )}\n </Pressable>\n );\n});\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport { Close } from '@gnome-ui/icons';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Pressable, StyleSheet, View } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface ChipProps {\n /** Text label displayed inside the chip. */\n label: string;\n /** Leading icon from `@gnome-ui/icons`. */\n icon?: IconDefinition;\n /**\n * When provided, renders a remove (×) button and calls this handler.\n * The chip root becomes a plain `View`; only the remove button is\n * interactive.\n */\n onRemove?: () => void;\n /**\n * When true the chip renders as a toggle button.\n * Use `selected` + `onToggle` to control its state.\n */\n selectable?: boolean;\n /** Active/selected state. Only relevant when `selectable` is true. */\n selected?: boolean;\n /**\n * Called when a selectable chip is pressed.\n * Only relevant when `selectable` is true.\n */\n onToggle?: () => void;\n /** Disabled state — applies to both selectable chips and the remove button. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Compact pill-shaped label for tags, filters, and selection states.\n * Mirrors `@gnome-ui/react`'s `Chip`.\n *\n * Three usage modes:\n * - **Static** — just a visual label (no `onRemove`, no `selectable`).\n * - **Removable** — add `onRemove` to show a × button.\n * - **Selectable** — add `selectable` + `selected` + `onToggle` for toggle\n * behavior. Same `isInteractive = selectable && !onRemove` precedence as\n * the web version: passing both `selectable` and `onRemove` renders the\n * remove button, not a toggle.\n *\n * Pair with `WrapBox` for multi-chip layouts.\n *\n * Rebuilt with `Pressable`/`View`/`Text` rather than ported from\n * `@gnome-ui/react`'s `<button>`/`<span>`: the selected background/border\n * tint (`color-mix(in srgb, accent 15%/50%, transparent)`) has no RN\n * equivalent, resolved to a literal 8-digit `#RRGGBBAA` hex instead — the\n * same `Highlight` precedent, since `accentBgColor` is always a plain\n * 6-digit hex. The `:hover`/`:active` background transitions collapse into\n * a single pressed-state overlay tinted by `theme.activeOverlay` (the same\n * `ActionRow`/`Card` recipe), since touch has no hover. The leading icon\n * and remove (×) icon don't recolor to match the selected accent text\n * (`color: inherit` on the web) — RN's `Icon` has no `currentColor`\n * equivalent and only accepts a fixed named-swatch palette, none of which\n * tracks the app's configurable accent color, so both icons stay in the\n * default foreground color; a decorative nicety dropped, not a behavior\n * gap. `accessibilityRole=\"checkbox\"` on the selectable form ports 1:1\n * (the same `Checkbox` precedent).\n *\n * @see https://developer.gnome.org/hig/patterns/selection.html\n */\nexport const Chip = ({\n label,\n icon,\n onRemove,\n selectable = false,\n selected = false,\n onToggle,\n disabled = false,\n style,\n testID,\n}: ChipProps) => {\n const theme = useGnomeTheme();\n const isInteractive = selectable && !onRemove;\n\n const baseStyle: ViewStyle = {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space1,\n paddingVertical: 3,\n paddingHorizontal: theme.space2,\n borderRadius: theme.radiusPill,\n borderWidth: 1,\n borderColor: selected ? `${theme.accentBgColor}80` : theme.light3,\n backgroundColor: selected ? `${theme.accentBgColor}26` : theme.cardBgColor,\n overflow: 'hidden',\n };\n\n const labelStyle: TextStyle = {\n color: selected ? theme.accentColor : theme.windowFgColor,\n fontWeight: String(\n selected ? theme.fontWeightSemibold : theme.fontWeightNormal,\n ) as TextStyle['fontWeight'],\n };\n\n const content = (\n <>\n {icon && (\n <View style={{ marginStart: -2 }}>\n <Icon icon={icon} size=\"sm\" />\n </View>\n )}\n\n <Text variant=\"caption\" style={labelStyle}>\n {label}\n </Text>\n\n {onRemove && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={`Remove ${label}`}\n disabled={disabled}\n onPress={onRemove}\n hitSlop={8}\n style={{ marginEnd: -4, opacity: disabled ? theme.opacityDisabled : 1 }}\n >\n <Icon icon={Close} size=\"sm\" />\n </Pressable>\n )}\n </>\n );\n\n if (isInteractive) {\n return (\n <Pressable\n testID={testID}\n disabled={disabled}\n accessibilityRole=\"checkbox\"\n accessibilityState={{ checked: selected, disabled }}\n accessibilityLabel={label}\n onPress={onToggle}\n style={[baseStyle, { opacity: disabled ? theme.opacityDisabled : 1 }, style]}\n >\n {({ pressed }) => (\n <>\n {content}\n {pressed && !disabled && (\n <View\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { backgroundColor: theme.activeOverlay }]}\n />\n )}\n </>\n )}\n </Pressable>\n );\n }\n\n return (\n <View\n testID={testID}\n style={[baseStyle, { opacity: disabled ? theme.opacityDisabled : 1 }, style]}\n >\n {content}\n </View>\n );\n};\n","import type { ReactNode } from 'react';\nimport { forwardRef } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nexport interface ClampProps extends Omit<ViewProps, 'style'> {\n /**\n * Maximum content width in density-independent pixels.\n * The container shrinks freely below this value.\n * Defaults to `600` — the Adwaita recommended narrow-content width.\n */\n maximumSize?: number;\n /**\n * Fractional width (`0`–`1`) of the available space to use while that\n * space is narrower than `maximumSize` — useful for keeping a\n * comfortable margin on medium-width screens. Defaults to `1` (always\n * fill the width).\n */\n tighteningThreshold?: number;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/** Keeps a nonsensical `tighteningThreshold` from collapsing the container. */\nconst clampFraction = (value: number) => Math.min(Math.max(value, 0), 1);\n\n/**\n * Constrains its children to a maximum width while letting them shrink\n * freely, mirroring the Adwaita `AdwClamp` widget and `@gnome-ui/react`'s\n * own `Clamp`.\n *\n * Use it on settings pages and forms so content never becomes too wide to\n * read comfortably on a tablet or a landscape phone, while still filling\n * the available width on a narrow one.\n *\n * The web version's `margin-inline: auto` centering becomes\n * `alignSelf: 'center'` here rather than `marginHorizontal: 'auto'` —\n * RN auto-margin support was left unverified for this Yoga version when\n * `Drawer` needed the same trick, so this follows `Drawer`'s resolution of\n * using flex alignment instead. The one consequence is that `Clamp`\n * expects a column-direction parent (RN's default): `alignSelf` acts on\n * the cross axis, so inside a `flexDirection: 'row'` parent it would\n * centre vertically instead. Wrap it in a plain `View` there.\n *\n * `tighteningThreshold` is a real percentage width here, unlike in\n * `@gnome-ui/react` where the prop is declared and documented but never\n * reaches the DOM — implementing it exactly as that package documents it\n * (a fraction of the available width, still capped by `maximumSize`)\n * costs nothing on RN and avoids shipping a dead prop.\n *\n * Adds no padding of its own — wrap the content in its own padded\n * container as needed.\n *\n * @example\n * // Settings page — content never wider than 600 dp\n * <Clamp>\n * <BoxedList>…</BoxedList>\n * </Clamp>\n *\n * @example\n * // Leave a 10% margin while the screen is narrower than 480 dp\n * <Clamp maximumSize={480} tighteningThreshold={0.9}>\n * <Text>…</Text>\n * </Clamp>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Clamp.html\n */\nexport const Clamp = forwardRef<View, ClampProps>(function Clamp(\n { maximumSize = 600, tighteningThreshold = 1, children, style, ...viewProps },\n ref,\n) {\n const widthFraction = clampFraction(tighteningThreshold);\n\n return (\n <RNView\n ref={ref}\n style={[\n {\n width: `${widthFraction * 100}%`,\n maxWidth: maximumSize,\n alignSelf: 'center',\n },\n style,\n ]}\n {...viewProps}\n >\n {children}\n </RNView>\n );\n});\n","/**\n * Positioning helpers for `CoachMark`. Kept pure and measurement-free so\n * they can be unit-tested directly; the component feeds them real rects\n * (from `measureInWindow` instead of the web version's\n * `getBoundingClientRect`, but the shape is identical either way).\n *\n * Duplicated verbatim from `@gnome-ui/react` rather than imported\n * cross-package — the same `fileType.ts`/`Icon.tsx` precedent for\n * DOM-free logic that isn't worth a shared package for one file's worth\n * of code.\n *\n * The algorithm mirrors `Popover`'s viewport-aware flip: try the preferred\n * side, then its opposite, then the rest, picking the first that fits;\n * clamp the cross-axis and shift the arrow when nothing fits perfectly.\n */\n\nexport type CoachMarkPlacement = 'top' | 'bottom' | 'left' | 'right';\n\nexport interface Rect {\n top: number;\n left: number;\n width: number;\n height: number;\n}\n\nexport interface BubblePosition {\n top: number;\n left: number;\n placement: CoachMarkPlacement;\n /** Arrow centre offset in px from the near edge of the bubble. */\n arrowOffset: number;\n}\n\nconst GAP = 12;\nconst MARGIN = 12;\nconst ARROW_HALF = 7;\n\n/** Grow a rect outward by `pad` on every side, e.g. the spotlight cutout. */\nexport const padRect = (rect: Rect, pad: number): Rect => ({\n top: rect.top - pad,\n left: rect.left - pad,\n width: rect.width + pad * 2,\n height: rect.height + pad * 2,\n});\n\n/**\n * Place the callout bubble around `target`, flipping to stay inside a\n * `viewport` (width × height). `bubble` is the measured bubble size.\n */\nexport const computeBubblePosition = (\n target: Rect,\n bubble: { width: number; height: number },\n viewport: { width: number; height: number },\n preferred: CoachMarkPlacement,\n): BubblePosition => {\n const targetRight = target.left + target.width;\n const targetBottom = target.top + target.height;\n const targetCX = target.left + target.width / 2;\n const targetCY = target.top + target.height / 2;\n\n const opposite: Record<CoachMarkPlacement, CoachMarkPlacement> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n };\n const candidates = [\n ...new Set<CoachMarkPlacement>([\n preferred,\n opposite[preferred],\n 'bottom',\n 'top',\n 'right',\n 'left',\n ]),\n ];\n\n const raw = (p: CoachMarkPlacement) => {\n switch (p) {\n case 'bottom':\n return { top: targetBottom + GAP, left: targetCX - bubble.width / 2 };\n case 'top':\n return { top: target.top - bubble.height - GAP, left: targetCX - bubble.width / 2 };\n case 'left':\n return { top: targetCY - bubble.height / 2, left: target.left - bubble.width - GAP };\n default:\n return { top: targetCY - bubble.height / 2, left: targetRight + GAP };\n }\n };\n\n // First pass: a side that fits both axes cleanly.\n for (const p of candidates) {\n const { top, left } = raw(p);\n const fitsH = left >= MARGIN && left + bubble.width <= viewport.width - MARGIN;\n const fitsV = top >= MARGIN && top + bubble.height <= viewport.height - MARGIN;\n\n if (fitsH && fitsV) {\n const arrowOffset = p === 'top' || p === 'bottom' ? targetCX - left : targetCY - top;\n\n return { top, left, placement: p, arrowOffset };\n }\n }\n\n // Second pass: primary axis fits; clamp the cross axis, keep the arrow on target.\n for (const p of candidates) {\n const { top, left } = raw(p);\n\n if (p === 'top' || p === 'bottom') {\n const fitsV = top >= MARGIN && top + bubble.height <= viewport.height - MARGIN;\n\n if (!fitsV) {\n continue;\n }\n\n const clampedLeft = Math.max(MARGIN, Math.min(left, viewport.width - bubble.width - MARGIN));\n const offset = targetCX - clampedLeft;\n\n return {\n top,\n left: clampedLeft,\n placement: p,\n arrowOffset: Math.max(ARROW_HALF + 4, Math.min(offset, bubble.width - ARROW_HALF - 4)),\n };\n }\n\n const fitsH = left >= MARGIN && left + bubble.width <= viewport.width - MARGIN;\n\n if (!fitsH) {\n continue;\n }\n\n const clampedTop = Math.max(MARGIN, Math.min(top, viewport.height - bubble.height - MARGIN));\n const offset = targetCY - clampedTop;\n\n return {\n top: clampedTop,\n left,\n placement: p,\n arrowOffset: Math.max(ARROW_HALF + 4, Math.min(offset, bubble.height - ARROW_HALF - 4)),\n };\n }\n\n // Fallback: clamp a bottom placement into the viewport.\n const fbTop = targetBottom + GAP;\n const fbLeft = targetCX - bubble.width / 2;\n const clampedTop = Math.max(MARGIN, Math.min(fbTop, viewport.height - bubble.height - MARGIN));\n const clampedLeft = Math.max(MARGIN, Math.min(fbLeft, viewport.width - bubble.width - MARGIN));\n const offset = targetCX - clampedLeft;\n\n return {\n top: clampedTop,\n left: clampedLeft,\n placement: 'bottom',\n arrowOffset: Math.max(ARROW_HALF + 4, Math.min(offset, bubble.width - ARROW_HALF - 4)),\n };\n};\n","import { type ReactNode, type RefObject, useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport {\n Animated,\n BackHandler,\n Dimensions,\n Modal,\n Pressable,\n StyleSheet,\n View,\n} from 'react-native';\n\nimport { Button } from '@/components/Button';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nimport {\n type BubblePosition,\n type CoachMarkPlacement,\n computeBubblePosition,\n padRect,\n type Rect,\n} from './coachMarkUtils';\n\nexport interface CoachMarkAction {\n label: string;\n onPress: () => void;\n}\n\nexport interface CoachMarkProps {\n /** Whether the coach mark is shown. */\n open: boolean;\n /** The element to highlight and anchor to. */\n targetRef: RefObject<View | null>;\n /** Heading text. */\n title?: ReactNode;\n /** Body copy explaining the highlighted element. */\n description?: ReactNode;\n /** Preferred side of the target for the bubble. Flips to stay on-screen. Defaults to `'bottom'`. */\n placement?: CoachMarkPlacement;\n /** Dim the rest of the screen and cut a spotlight around the target. Defaults to `true`. */\n spotlight?: boolean;\n /** Extra px around the target inside the spotlight cutout. Defaults to `8`. */\n spotlightPadding?: number;\n /** Close when the dimmed backdrop is pressed. Defaults to `false` (guided). */\n dismissOnBackdrop?: boolean;\n /** 1-based index of this step within a tour, for the \"X of N\" counter. */\n step?: number;\n /** Total number of steps in the tour. */\n stepCount?: number;\n /** Primary (suggested) action, e.g. Next / Got it. */\n primaryAction?: CoachMarkAction;\n /** Secondary (flat) action, e.g. Back. */\n secondaryAction?: CoachMarkAction;\n /** Called when the Android back button is pressed, or the backdrop is dismissed. */\n onDismiss?: () => void;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst ARROW = 7;\nconst SCRIM_COLOR = 'rgba(0, 0, 0, 0.55)';\n\n/**\n * A single onboarding coach mark: it spotlights a target element and\n * anchors a callout bubble (title, description, step counter, actions)\n * beside it, guiding a user to one feature. Compose several with\n * `CoachMarkTour`, or drive one directly with `open`. Mirrors\n * `@gnome-ui/react`'s `CoachMark` — not a GNOME HIG widget, a pragmatic\n * feature-discovery pattern.\n *\n * Rebuilt on RN's own `Modal` rather than the web version's DOM `Portal` —\n * no `container` prop, the same `Overlay`/`Drawer` precedent for \"no RN\n * portal-target concept.\" Positions with the same two-pass viewport-aware\n * flip as the web version (`coachMarkUtils.ts`, duplicated verbatim — pure\n * math, no DOM), resolved from `targetRef.current?.measureInWindow(...)`\n * and the bubble's own `onLayout` size, combined once both arrive — the\n * same \"resolve two independent async things, then combine\" shape\n * `Tooltip`/`Popover`/`Dropdown` already established. No focus trap (no\n * DOM `Tab` concept in RN) and no scroll/resize re-positioning (RN has no\n * global scroll event, the same `Tooltip` precedent for a transient\n * floating element).\n *\n * **The spotlight cutout has no CSS `box-shadow: 0 0 0 100vmax` port** —\n * that trick paints an opaque scrim everywhere *except* inside a rounded\n * rect by using a huge spread shadow, which RN's `shadow*` props (real OS\n * shadows, not a scrim generator) can't reproduce. Rebuilt as four plain\n * `View` bands (top/bottom/left/right of the padded target rect) filling\n * the screen minus a rectangular hole, plus a separate rounded\n * `accentColor`-bordered ring `View` drawn on top at the target rect —\n * visually equivalent (dims everything but the target, with an accent\n * ring around it), just assembled from ordinary rects instead of one\n * masked shape. The whole overlay (bands + ring) sits inside a single\n * full-screen `Pressable`, so — matching the web version exactly — a tap\n * anywhere within it (including visually \"in the hole,\" since the web\n * version's backdrop is one full-bleed element with the spotlight only\n * painted on top, `pointer-events: none`) triggers `dismissOnBackdrop`,\n * never the real content underneath.\n *\n * **`dismissOnBackdrop` has no effect when `spotlight` is `false`** —\n * ported faithfully, not fixed: the web source only renders a backdrop\n * element at all when `spotlight` is true, so with `spotlight={false}`\n * there is nothing to press to dismiss via backdrop either way, on both\n * platforms.\n *\n * The arrow reuses `Popover`/`Tooltip`'s transparent-border-triangle trick\n * rather than the web CSS's rotated-45°-square, the same established RN\n * substitution for every floating-bubble arrow in this package — offset\n * along the bubble's edge by `arrowOffset` (from `computeBubblePosition`,\n * unlike `Tooltip`/`Popover`'s simpler always-centered arrow).\n *\n * `role=\"dialog\"` + `accessibilityViewIsModal` port 1:1 from `Dialog`'s\n * own precedent. `BackHandler`'s `hardwareBackPress` is the Android analog\n * of the web version's Escape listener.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Window.html\n */\nexport const CoachMark = ({\n open,\n targetRef,\n title,\n description,\n placement = 'bottom',\n spotlight = true,\n spotlightPadding = 8,\n dismissOnBackdrop = false,\n step,\n stepCount,\n primaryAction,\n secondaryAction,\n onDismiss,\n style,\n testID,\n}: CoachMarkProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const [targetRect, setTargetRect] = useState<Rect | null>(null);\n const [bubbleSize, setBubbleSize] = useState<{ width: number; height: number } | null>(null);\n const [pos, setPos] = useState<BubblePosition | null>(null);\n\n const progress = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n if (!open) {\n setTargetRect(null);\n setBubbleSize(null);\n setPos(null);\n progress.setValue(0);\n\n return;\n }\n\n // Measuring immediately (or even one `requestAnimationFrame` later)\n // catches a stale rect when the target sits below sibling content\n // whose own size isn't final yet on the first commit (e.g. a\n // multi-line description `Text` above it) — confirmed on-device: the\n // reported `y` matched exactly where the target would sit *without*\n // that sibling's height, off by precisely that amount. A single rAF\n // still landed before the follow-up layout pass that accounts for it;\n // a short real delay reliably lands after. The web version's\n // `useLayoutEffect` measurement has no equivalent race, since DOM\n // layout is synchronous by the time it runs — RN's `measureInWindow`\n // is a native-bridge call with no such guarantee, and Yoga's own\n // text-driven relayout can still be in flight after one JS frame.\n const timer = setTimeout(() => {\n targetRef.current?.measureInWindow((x, y, width, height) => {\n setTargetRect({ top: y, left: x, width, height });\n });\n }, 50);\n\n return () => clearTimeout(timer);\n // `targetRef` is a real dependency, not just a stable-ref formality —\n // `CoachMarkTour` passes a *different* ref object per step, and this\n // effect must re-measure when the target changes even though `open`\n // stays `true` across the whole tour. Missing it here was a real bug:\n // advancing steps left the spotlight/bubble stuck on the first step's\n // target (found via the app, not just this file's own tests, which\n // only ever exercise a single step's fixed ref).\n }, [open, targetRef, progress]);\n\n useEffect(() => {\n if (!targetRect || !bubbleSize) {\n return;\n }\n\n const { width: vw, height: vh } = Dimensions.get('window');\n\n setPos(computeBubblePosition(targetRect, bubbleSize, { width: vw, height: vh }, placement));\n }, [targetRect, bubbleSize, placement]);\n\n useEffect(() => {\n if (!pos) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationFast,\n useNativeDriver: true,\n }).start();\n }, [pos, reducedMotion, progress, theme.durationFast]);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n const subscription = BackHandler.addEventListener('hardwareBackPress', () => {\n onDismiss?.();\n\n return true;\n });\n\n return () => subscription.remove();\n }, [open, onDismiss]);\n\n const handleBubbleLayout = (e: LayoutChangeEvent) => {\n const { width, height } = e.nativeEvent.layout;\n\n setBubbleSize({ width, height });\n };\n\n if (!open) {\n return null;\n }\n\n const spotRect = targetRect ? padRect(targetRect, spotlightPadding) : null;\n const { width: vw } = Dimensions.get('window');\n\n const arrowBase = {\n position: 'absolute' as const,\n width: 0,\n height: 0,\n borderWidth: ARROW,\n borderColor: 'transparent',\n };\n\n const arrowStyle = (() => {\n if (!pos) {\n return null;\n }\n\n switch (pos.placement) {\n case 'top':\n return {\n ...arrowBase,\n bottom: -ARROW * 2,\n left: pos.arrowOffset - ARROW,\n borderTopColor: theme.cardBgColor,\n };\n case 'bottom':\n return {\n ...arrowBase,\n top: -ARROW * 2,\n left: pos.arrowOffset - ARROW,\n borderBottomColor: theme.cardBgColor,\n };\n case 'left':\n return {\n ...arrowBase,\n right: -ARROW * 2,\n top: pos.arrowOffset - ARROW,\n borderLeftColor: theme.cardBgColor,\n };\n default:\n return {\n ...arrowBase,\n left: -ARROW * 2,\n top: pos.arrowOffset - ARROW,\n borderRightColor: theme.cardBgColor,\n };\n }\n })();\n\n return (\n <Modal\n visible={open}\n transparent\n animationType=\"none\"\n statusBarTranslucent\n onRequestClose={onDismiss}\n >\n <Animated.View pointerEvents=\"box-none\" style={StyleSheet.absoluteFill}>\n {spotlight && (\n <Pressable\n testID={testID}\n onPress={dismissOnBackdrop ? onDismiss : undefined}\n accessible={false}\n style={StyleSheet.absoluteFill}\n >\n <Animated.View\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { opacity: progress }]}\n >\n {spotRect && (\n <>\n <View\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n height: Math.max(0, spotRect.top),\n backgroundColor: SCRIM_COLOR,\n }}\n />\n <View\n style={{\n position: 'absolute',\n top: Math.max(0, spotRect.top + spotRect.height),\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: SCRIM_COLOR,\n }}\n />\n <View\n style={{\n position: 'absolute',\n top: Math.max(0, spotRect.top),\n left: 0,\n width: Math.max(0, spotRect.left),\n height: spotRect.height,\n backgroundColor: SCRIM_COLOR,\n }}\n />\n <View\n style={{\n position: 'absolute',\n top: Math.max(0, spotRect.top),\n left: Math.max(0, spotRect.left + spotRect.width),\n right: 0,\n height: spotRect.height,\n backgroundColor: SCRIM_COLOR,\n }}\n />\n <View\n pointerEvents=\"none\"\n style={{\n position: 'absolute',\n top: spotRect.top,\n left: spotRect.left,\n width: spotRect.width,\n height: spotRect.height,\n borderRadius: theme.radiusMd,\n borderWidth: 2,\n borderColor: theme.accentColor,\n }}\n />\n </>\n )}\n </Animated.View>\n </Pressable>\n )}\n\n <Animated.View\n onLayout={handleBubbleLayout}\n accessible\n role=\"dialog\"\n accessibilityViewIsModal\n style={[\n {\n position: 'absolute',\n maxWidth: Math.min(320, vw - 24),\n padding: theme.space3,\n backgroundColor: theme.cardBgColor,\n borderRadius: theme.radiusLg,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 4 },\n shadowOpacity: 0.2,\n shadowRadius: 16,\n elevation: 8,\n },\n pos\n ? {\n top: pos.top,\n left: pos.left,\n opacity: progress,\n transform: [\n { scale: progress.interpolate({ inputRange: [0, 1], outputRange: [0.96, 1] }) },\n ],\n }\n : { top: -9999, left: -9999, opacity: 0 },\n style,\n ]}\n >\n {arrowStyle && <View style={arrowStyle} />}\n\n {step !== undefined && stepCount !== undefined && (\n <Text\n variant=\"caption\"\n style={{\n marginBottom: theme.space1,\n color: theme.accentColor,\n fontWeight: String(theme.fontWeightBold) as TextStyle['fontWeight'],\n }}\n >\n {`${step} of ${stepCount}`}\n </Text>\n )}\n\n {title && (\n <Text\n variant=\"title-4\"\n style={{\n marginBottom: theme.space1,\n fontWeight: String(theme.fontWeightBold) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n )}\n\n {description && (\n <Text variant=\"body\" color=\"dim\">\n {description}\n </Text>\n )}\n\n {(primaryAction || secondaryAction) && (\n <View\n style={{\n flexDirection: 'row',\n justifyContent: 'flex-end',\n gap: theme.space2,\n marginTop: theme.space3,\n }}\n >\n {secondaryAction && (\n <Button variant=\"flat\" size=\"sm\" onPress={secondaryAction.onPress}>\n {secondaryAction.label}\n </Button>\n )}\n {primaryAction && (\n <Button variant=\"suggested\" size=\"sm\" onPress={primaryAction.onPress}>\n {primaryAction.label}\n </Button>\n )}\n </View>\n )}\n </Animated.View>\n </Animated.View>\n </Modal>\n );\n};\n","import { type ReactNode, type RefObject, useEffect, useState } from 'react';\nimport type { View } from 'react-native';\n\nimport { CoachMark } from './CoachMark';\nimport type { CoachMarkPlacement } from './coachMarkUtils';\n\nexport interface CoachMarkStep {\n /** The element this step highlights. */\n targetRef: RefObject<View | null>;\n /** Heading for the step. */\n title?: ReactNode;\n /** Body copy for the step. */\n description?: ReactNode;\n /** Preferred bubble side for this step. Defaults to the tour's placement. */\n placement?: CoachMarkPlacement;\n}\n\nexport interface CoachMarkTourProps {\n /** Ordered steps of the tour. */\n steps: CoachMarkStep[];\n /** Whether the tour is running. */\n open: boolean;\n /** Step to start on when the tour opens. Defaults to `0`. */\n startIndex?: number;\n /** Called after the primary action on the final step. */\n onFinish?: () => void;\n /** Called when the user skips (Skip button or Android back) before finishing. */\n onSkip?: () => void;\n /** Called with the new index whenever the active step changes. */\n onStepChange?: (index: number) => void;\n /** Default preferred bubble side for steps that don't set their own. Defaults to `'bottom'`. */\n placement?: CoachMarkPlacement;\n /** Spotlight the target. Defaults to `true`. */\n spotlight?: boolean;\n /** Close the tour when the dimmed backdrop is pressed. Defaults to `false`. */\n dismissOnBackdrop?: boolean;\n /** Override the action-button labels (for i18n). */\n labels?: Partial<{ next: string; back: string; skip: string; finish: string }>;\n testID?: string;\n}\n\nconst DEFAULT_LABELS = { next: 'Next', back: 'Back', skip: 'Skip', finish: 'Done' };\n\n/**\n * Sequential onboarding tour built from `CoachMark` steps. Renders the\n * mark for the active step, wires Next/Back/Skip/Done and the \"X of N\"\n * counter, and advances through `steps` until finished or skipped.\n * Mirrors `@gnome-ui/react`'s `CoachMarkTour` verbatim — pure state\n * orchestration on top of `CoachMark`, nothing platform-specific to\n * change.\n *\n * Uncontrolled step index: the tour tracks its own position and resets to\n * `startIndex` each time it opens. Drive visibility with `open`; react to\n * completion with `onFinish`/`onSkip`.\n */\nexport const CoachMarkTour = ({\n steps,\n open,\n startIndex = 0,\n onFinish,\n onSkip,\n onStepChange,\n placement = 'bottom',\n spotlight = true,\n dismissOnBackdrop = false,\n labels,\n testID,\n}: CoachMarkTourProps) => {\n const [index, setIndex] = useState(startIndex);\n\n // Restart from the top each time the tour opens.\n useEffect(() => {\n if (open) {\n setIndex(startIndex);\n }\n }, [open, startIndex]);\n\n if (!open || steps.length === 0) {\n return null;\n }\n\n const clampedIndex = Math.min(index, steps.length - 1);\n const step = steps[clampedIndex];\n const isFirst = clampedIndex === 0;\n const isLast = clampedIndex === steps.length - 1;\n const text = { ...DEFAULT_LABELS, ...labels };\n\n const goTo = (next: number) => {\n setIndex(next);\n onStepChange?.(next);\n };\n\n const primaryAction = {\n label: isLast ? text.finish : text.next,\n onPress: () => (isLast ? onFinish?.() : goTo(clampedIndex + 1)),\n };\n\n // Later steps offer Back; the first step offers Skip in the same slot.\n const secondaryAction = isFirst\n ? { label: text.skip, onPress: () => onSkip?.() }\n : { label: text.back, onPress: () => goTo(clampedIndex - 1) };\n\n return (\n <CoachMark\n open={open}\n targetRef={step.targetRef}\n title={step.title}\n description={step.description}\n placement={step.placement ?? placement}\n spotlight={spotlight}\n dismissOnBackdrop={dismissOnBackdrop}\n step={clampedIndex + 1}\n stepCount={steps.length}\n primaryAction={primaryAction}\n secondaryAction={secondaryAction}\n onDismiss={() => onSkip?.()}\n testID={testID}\n />\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { FlexStyle, StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nexport type WrapBoxJustify =\n | 'start'\n | 'center'\n | 'end'\n | 'space-between'\n | 'space-around'\n | 'space-evenly';\nexport type WrapBoxAlign = 'start' | 'center' | 'end' | 'stretch';\n\nexport interface WrapBoxProps extends Omit<ViewProps, 'style'> {\n /** Gap between children on the same line, in dp. Default: `6`. */\n childSpacing?: number;\n /** Gap between lines, in dp. Defaults to `childSpacing`. */\n lineSpacing?: number;\n /** Horizontal distribution of children within each line. Default: `\"start\"`. */\n justify?: WrapBoxJustify;\n /** Cross-axis alignment of children within each line. Default: `\"center\"`. */\n align?: WrapBoxAlign;\n /** When true children wrap in the reverse direction (bottom to top). */\n wrapReverse?: boolean;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Yoga's own edge names — the same `start`/`end` → `flex-start`/`flex-end`\n * translation `Box` establishes, duplicated here rather than shared: these\n * are two small pure lookups, and `WrapBoxAlign` is deliberately a narrower\n * union than `BoxAlign` (no `baseline`), mirroring the web package's own\n * two separate types.\n */\nconst ALIGN: Record<WrapBoxAlign, FlexStyle['alignItems']> = {\n start: 'flex-start',\n center: 'center',\n end: 'flex-end',\n stretch: 'stretch',\n};\n\nconst JUSTIFY: Record<WrapBoxJustify, FlexStyle['justifyContent']> = {\n start: 'flex-start',\n center: 'center',\n end: 'flex-end',\n 'space-between': 'space-between',\n 'space-around': 'space-around',\n 'space-evenly': 'space-evenly',\n};\n\n/**\n * Flexible wrapping layout container — children flow horizontally and wrap\n * to new lines when they don't fit, like words in a paragraph, without\n * locking them into a grid. Mirrors `AdwWrapBox` (libadwaita 1.7 / GNOME\n * 48) and `@gnome-ui/react`'s own `WrapBox`.\n *\n * Pair with `Chip` for tag lists and filter rows, or use standalone for any\n * collection of variable-width items.\n *\n * The web version ships its values as CSS custom properties consumed by a\n * stylesheet (`--wrapbox-gap`, `--wrapbox-justify`, …) because a CSS module\n * can't take runtime values any other way; RN has no such indirection, so\n * they're written straight onto the style object here. Everything else is a\n * direct translation: `flex-flow: row wrap` becomes\n * `flexDirection: 'row'` + `flexWrap`, and the shorthand\n * `gap: <row> <column>` splits into RN's separate `rowGap`/`columnGap`\n * (the single `gap` property would set both, which is exactly what this\n * component must be able to avoid).\n *\n * As in `Box`, `childSpacing`/`lineSpacing` are numbers only — RN's gap\n * properties take dp, not CSS strings — and `align`/`justify` keep the\n * web's bare `start`/`end` keywords in the public API while mapping\n * internally onto Yoga's `flex-start`/`flex-end`.\n *\n * `alignContent: 'stretch'` is set explicitly even though neither package\n * exposes an `alignContent` prop — **CSS defaults it to `stretch`, Yoga\n * defaults it to `flex-start`**, so without this line `align=\"stretch\"`\n * silently does nothing on RN whenever the children have no cross-size of\n * their own: the line collapses to zero height before `alignItems` ever\n * gets to stretch anything into it. Caught on-device (the stretch row of\n * the example app's demo screen rendered empty); this restores the web\n * version's behaviour exactly. It's a no-op in the ordinary case where the\n * container hugs its content rather than having a fixed height.\n *\n * @example\n * // Tag list\n * <WrapBox childSpacing={6}>\n * {tags.map((tag) => <Chip key={tag}>{tag}</Chip>)}\n * </WrapBox>\n *\n * @example\n * // Looser gap between lines than between items\n * <WrapBox childSpacing={6} lineSpacing={12} justify=\"center\">\n * {filters.map((filter) => <Chip key={filter}>{filter}</Chip>)}\n * </WrapBox>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.WrapBox.html\n */\nexport const WrapBox = forwardRef<View, WrapBoxProps>(function WrapBox(\n {\n childSpacing = 6,\n lineSpacing,\n justify = 'start',\n align = 'center',\n wrapReverse = false,\n children,\n style,\n ...viewProps\n },\n ref,\n) {\n return (\n <RNView\n ref={ref}\n style={[\n {\n flexDirection: 'row',\n flexWrap: wrapReverse ? 'wrap-reverse' : 'wrap',\n columnGap: childSpacing,\n rowGap: lineSpacing ?? childSpacing,\n justifyContent: JUSTIFY[justify],\n alignItems: ALIGN[align],\n alignContent: 'stretch',\n },\n style,\n ]}\n {...viewProps}\n >\n {children}\n </RNView>\n );\n});\n","import { forwardRef } from 'react';\nimport type { PressableProps, StyleProp, View, ViewStyle } from 'react-native';\nimport { Pressable, View as RNView } from 'react-native';\nimport Svg, { Path } from 'react-native-svg';\n\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\n\nexport type ColorSwatchSize = 'sm' | 'md' | 'lg';\n\n/** `.swatch-sm` / `-md` / `-lg`. */\n// eslint-disable-next-line react-refresh/only-export-components\nexport const SWATCH_DIAMETER: Record<ColorSwatchSize, number> = {\n sm: 22,\n md: 30,\n lg: 38,\n};\n\n/**\n * Width of the selected state's outer ring. Reserved as padding on every\n * swatch, selected or not, so selecting one never reflows the row — the web\n * gets this for free because `box-shadow` rings don't take up space.\n */\nexport const RING_WIDTH = 2;\n\nexport interface ColorSwatchProps\n extends Omit<PressableProps, 'children' | 'style' | 'onPress' | 'disabled'> {\n /** Color value displayed as the swatch background. */\n color: string;\n /** Whether this swatch is the currently selected color. */\n selected?: boolean;\n /** Swatch diameter. Defaults to `\"md\"`. */\n size?: ColorSwatchSize;\n /** Called with `color` when the swatch is pressed. */\n onSelect?: (color: string) => void;\n /** Accessible name. Defaults to the color value. */\n accessibilityLabel?: string;\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Single circular color swatch. Usable standalone or composed inside\n * `ColorPicker`, and shows a white checkmark when `selected`.\n *\n * The web's three `box-shadow` rings collapse into real box-model pieces,\n * since RN gives a `View` exactly one border: the resting\n * `inset 0 0 0 1px` hairline becomes `borderWidth: 1`, the selected state's\n * `inset 0 0 0 2px rgb(255 255 255 / .9)` becomes a 2 dp white border, and\n * the outer `0 0 0 2px var(--swatch-color)` becomes a wrapper painted in the\n * swatch color. That wrapper is always rendered with the same 2 dp padding\n * and only changes color, because a `box-shadow` ring costs no layout space\n * on the web while a real padded wrapper does — reserving it unconditionally\n * is what keeps the row from reflowing as the selection moves.\n *\n * The checkmark is hand-drawn with `react-native-svg` rather than taken from\n * `@gnome-ui/icons`, mirroring the web version, which also hand-draws it:\n * it's a stroked path, and `Icon`'s palette has no white to give it anyway.\n * `filter: drop-shadow(...)` has no RN counterpart, so the path is drawn\n * twice — a translucent black copy offset 1 dp down, then the white one on\n * top — which is what that filter renders and is why it exists: without it\n * the check disappears on a yellow swatch.\n *\n * `:hover { transform: scale(1.12) }` drops with hover; the selected\n * `scale(1.05)` ports as-is.\n */\nexport const ColorSwatch = forwardRef<View, ColorSwatchProps>(function ColorSwatch(\n {\n color,\n selected = false,\n size = 'md',\n onSelect,\n accessibilityLabel,\n disabled = false,\n style,\n ...pressableProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const isDark = useResolvedColorScheme() === 'dark';\n const diameter = SWATCH_DIAMETER[size];\n const hairline = isDark ? 'rgba(255, 255, 255, 0.12)' : 'rgba(0, 0, 0, 0.15)';\n const checkSize = Math.round(diameter * 0.55);\n\n return (\n <RNView\n style={[\n {\n padding: RING_WIDTH,\n borderRadius: theme.radiusPill,\n backgroundColor: selected ? color : 'transparent',\n alignSelf: 'flex-start',\n opacity: disabled ? theme.opacityDisabled : 1,\n },\n // Kept as its own entry so an unselected swatch carries no\n // `transform` key at all, rather than one set to `undefined` —\n // RN's transform validator is unforgiving about what reaches it.\n selected ? { transform: [{ scale: 1.05 }] } : null,\n style,\n ]}\n >\n <Pressable\n ref={ref}\n disabled={disabled}\n onPress={() => onSelect?.(color)}\n accessibilityRole=\"radio\"\n accessibilityState={{ checked: selected, disabled }}\n accessibilityLabel={accessibilityLabel ?? color}\n style={{\n width: diameter,\n height: diameter,\n borderRadius: theme.radiusPill,\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: color,\n borderWidth: selected ? 2 : 1,\n borderColor: selected ? 'rgba(255, 255, 255, 0.9)' : hairline,\n }}\n {...pressableProps}\n >\n {selected ? (\n <Svg width={checkSize} height={checkSize} viewBox=\"0 0 16 16\">\n <Path\n d=\"M3 8l3.5 3.5L13 5\"\n stroke=\"rgba(0, 0, 0, 0.35)\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n fill=\"none\"\n translateY={1}\n />\n <Path\n d=\"M3 8l3.5 3.5L13 5\"\n stroke=\"white\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n fill=\"none\"\n />\n </Svg>\n ) : null}\n </Pressable>\n </RNView>\n );\n});\n","import { Add } from '@gnome-ui/icons';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Pressable, View as RNView } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { WrapBox } from '@/components/WrapBox';\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\n\nimport { ColorSwatch, type ColorSwatchSize, RING_WIDTH, SWATCH_DIAMETER } from './ColorSwatch';\n\nexport interface ColorPickerColor {\n /** Color value (hex recommended). */\n value: string;\n /** Human-readable name, used as the swatch's accessible label. */\n label?: string;\n}\n\n/** Default Adwaita-named palette (matches the `Avatar` color set). */\n// eslint-disable-next-line react-refresh/only-export-components\nexport const GNOME_PALETTE: ColorPickerColor[] = [\n { value: '#3584e4', label: 'Blue' },\n { value: '#2ec27e', label: 'Green' },\n { value: '#f6d32d', label: 'Yellow' },\n { value: '#ff7800', label: 'Orange' },\n { value: '#e01b24', label: 'Red' },\n { value: '#9141ac', label: 'Purple' },\n { value: '#986a44', label: 'Brown' },\n { value: '#2190a4', label: 'Teal' },\n { value: '#5e5c64', label: 'Slate' },\n];\n\nexport interface ColorPickerProps {\n /** Currently selected color value. */\n value?: string;\n /** Called when the user selects a color. */\n onChange?: (value: string) => void;\n /** Palette to display. Defaults to `GNOME_PALETTE` (the 9 Adwaita colors). */\n colors?: ColorPickerColor[];\n /**\n * Show a \"+\" button after the palette, and render any `value` outside the\n * palette as its own selected swatch. Pressing either calls\n * `onRequestCustom` — RN has no `<input type=\"color\">`, so the picker UI\n * itself is the consuming app's to provide. Defaults to `false`.\n */\n allowCustom?: boolean;\n /**\n * Called when the \"+\" button (or the current custom swatch) is pressed.\n * Open your own color picker here and feed the result back through\n * `value`/`onChange`.\n */\n onRequestCustom?: () => void;\n /** Swatch size. Defaults to `\"md\"`. */\n size?: ColorSwatchSize;\n /** Accessible name for the group. Defaults to `\"Color\"`. */\n accessibilityLabel?: string;\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Color palette picker following the Adwaita `GtkColorButton` + swatch\n * pattern, mirroring `@gnome-ui/react`'s own `ColorPicker`. Renders a\n * wrapping row of circular `ColorSwatch` items backed by a radio group.\n *\n * **`allowCustom` is the one prop that changes meaning.** On the web it\n * wires a hidden `<input type=\"color\">` and the browser supplies the whole\n * picker UI; RN has no such control, and building an HSV picker would be a\n * component in its own right rather than a detail of this one. So the prop\n * keeps its *visible* behaviour — the \"+\" button, and a `value` outside the\n * palette shown as its own selected swatch — while the press is handed to\n * `onRequestCustom` for the app to answer with whatever picker it has. Round\n * trips through `value`/`onChange` exactly as before.\n *\n * The container is a `WrapBox` (`display: flex; flex-wrap: wrap; gap: 8`\n * with nothing else in `.picker`), and the \"+\" button's `border: 1.5px\n * dashed` ports directly — `borderStyle: 'dashed'` is one of the few CSS\n * border tricks RN does support. Its plus glyph comes from `@gnome-ui/icons`\n * rather than the web's hand-drawn path, since `Add` is the same mark and\n * already resolves to the foreground color `.customButton` asks for.\n *\n * As in `ToggleGroup`, the group takes `accessibilityRole=\"radiogroup\"` but\n * deliberately not `accessible`, which on iOS would collapse the swatches\n * into one unreachable element. Arrow-key navigation and the roving\n * `tabIndex` drop, as everywhere else here.\n *\n * @example\n * const [color, setColor] = useState('#3584e4');\n *\n * <ColorPicker value={color} onChange={setColor} />\n *\n * @example\n * // Custom colors, with your own picker behind the \"+\"\n * <ColorPicker\n * value={color}\n * onChange={setColor}\n * allowCustom\n * onRequestCustom={() => setPickerOpen(true)}\n * />\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ColorButton.html\n */\nexport const ColorPicker = ({\n value,\n onChange,\n colors = GNOME_PALETTE,\n allowCustom = false,\n onRequestCustom,\n size = 'md',\n accessibilityLabel = 'Color',\n disabled = false,\n style,\n testID,\n}: ColorPickerProps) => {\n const theme = useGnomeTheme();\n const isDark = useResolvedColorScheme() === 'dark';\n const diameter = SWATCH_DIAMETER[size];\n\n const isCustom = value !== undefined && !colors.some((color) => color.value === value);\n\n return (\n <WrapBox\n testID={testID}\n accessibilityRole=\"radiogroup\"\n accessibilityLabel={accessibilityLabel}\n childSpacing={8}\n align=\"center\"\n style={style}\n >\n {colors.map((color) => (\n <ColorSwatch\n key={color.value}\n color={color.value}\n size={size}\n selected={color.value === value}\n disabled={disabled}\n onSelect={onChange}\n accessibilityLabel={color.label ?? color.value}\n />\n ))}\n\n {allowCustom ? (\n <>\n {isCustom && value ? (\n <ColorSwatch\n color={value}\n size={size}\n selected\n disabled={disabled}\n onSelect={onRequestCustom}\n accessibilityLabel=\"Custom color\"\n />\n ) : null}\n\n {/* Same reserved ring padding as a swatch, so the row stays aligned. */}\n <RNView style={{ padding: RING_WIDTH, alignSelf: 'flex-start' }}>\n <Pressable\n disabled={disabled}\n onPress={onRequestCustom}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Choose custom color\"\n accessibilityState={{ disabled }}\n style={({ pressed }) => ({\n width: diameter,\n height: diameter,\n borderRadius: theme.radiusPill,\n alignItems: 'center',\n justifyContent: 'center',\n borderWidth: 1.5,\n borderStyle: 'dashed',\n borderColor: isDark ? 'rgba(255, 255, 255, 0.25)' : theme.cardShadeColor,\n backgroundColor: pressed && !disabled ? theme.activeOverlay : theme.cardBgColor,\n opacity: disabled ? theme.opacityDisabled : 1,\n })}\n >\n <Icon\n icon={Add}\n width={Math.round(diameter * 0.5)}\n height={Math.round(diameter * 0.5)}\n />\n </Pressable>\n </RNView>\n </>\n ) : null}\n </WrapBox>\n );\n};\n","import { Check, PanDown } from '@gnome-ui/icons';\nimport { useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Animated, Dimensions, Modal, Pressable, ScrollView, View } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport interface DropdownOption<V extends string = string> {\n /** The value submitted / returned on selection. */\n value: V;\n /** Display label shown in the list and trigger. */\n label: string;\n /** Optional descriptive text shown below the label. */\n description?: string;\n /** Whether the option is selectable. */\n disabled?: boolean;\n}\n\nexport interface DropdownProps<V extends string = string> {\n /** The list of selectable options. */\n options: DropdownOption<V>[];\n /** The currently selected value. */\n value?: V;\n /** Called when the user selects an option. */\n onChange?: (value: V) => void;\n /** Placeholder shown when no option is selected. */\n placeholder?: string;\n /** Accessible label for the control. */\n accessibilityLabel?: string;\n /** Disables the entire control. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\ninterface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface Position {\n top: number;\n left: number;\n width: number;\n flipUp: boolean;\n}\n\nconst GAP = 4;\nconst MARGIN = 8;\nconst MAX_LIST_HEIGHT = 280;\n\nfunction computePosition(trigger: Rect, panelHeight: number): Position {\n const { height: vh } = Dimensions.get('window');\n const spaceBelow = vh - (trigger.y + trigger.height);\n const flipUp = spaceBelow < panelHeight && trigger.y > panelHeight;\n const top = flipUp ? trigger.y - panelHeight - GAP : trigger.y + trigger.height + GAP;\n\n return {\n top: Math.max(MARGIN, Math.min(top, vh - panelHeight - MARGIN)),\n left: trigger.x,\n width: trigger.width,\n flipUp,\n };\n}\n\n/**\n * Expandable option list following the Adwaita combo-row / drop-down\n * pattern, mirroring `@gnome-ui/react`'s `Dropdown`.\n *\n * Built on RN's own `Modal` (transparent) — the same portal-substitute\n * `Dialog`/`Tooltip` already use — with a full-screen backdrop `Pressable`\n * (not `pointerEvents=\"box-none\"` like `Tooltip`'s: unlike a tooltip, this\n * panel is meant to catch and close on an outside tap, the RN analog of the\n * web version's document-level \"click outside\" listener). `open` flips\n * synchronously on trigger press; the trigger's on-screen rect\n * (`measureInWindow`) and the panel's own rendered height (`onLayout`) each\n * resolve independently into state, combined by a separate effect into the\n * final `top`/`left`/`width`/`flipUp` — the same two-independent-\n * async-measurements pattern `Tooltip` established (and for the same\n * reason: gating `open` itself on the native measurement callback would\n * make the component untestable in this package's Jest environment, where\n * `measureInWindow` never calls back).\n *\n * Keyboard navigation (↑/↓ roving highlight, Home/End, type-ahead) has no\n * port — RN's touch-first model has no keyboard focus to drive it, the\n * same reasoning that already dropped `TabBar`'s roving-tabindex arrow\n * keys. Selection is by direct tap only.\n *\n * `role=\"combobox\"` on the trigger ports 1:1. RN's `Role` union has no\n * `\"listbox\"` value (unlike `\"option\"`, which does exist) — the panel uses\n * `role=\"list\"` instead, the same closest-available substitution `BoxedList`\n * already established for a plain list container.\n */\nexport const Dropdown = <V extends string = string>({\n options,\n value,\n onChange,\n placeholder = 'Select an option',\n accessibilityLabel,\n disabled,\n style,\n testID,\n}: DropdownProps<V>) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const [open, setOpen] = useState(false);\n const [triggerRect, setTriggerRect] = useState<Rect | null>(null);\n const [panelHeight, setPanelHeight] = useState<number | null>(null);\n const [pos, setPos] = useState<Position | null>(null);\n\n const triggerRef = useRef<View>(null);\n const progress = useRef(new Animated.Value(0)).current;\n\n const selected = options.find((o) => o.value === value);\n\n const openList = () => {\n if (disabled) {\n return;\n }\n\n setOpen(true);\n };\n\n const closeList = () => setOpen(false);\n\n const selectOption = (opt: DropdownOption<V>) => {\n if (opt.disabled) {\n return;\n }\n\n onChange?.(opt.value);\n closeList();\n };\n\n useEffect(() => {\n if (!open) {\n setTriggerRect(null);\n setPanelHeight(null);\n setPos(null);\n\n return;\n }\n\n triggerRef.current?.measureInWindow((x, y, width, height) => {\n setTriggerRect({ x, y, width, height });\n });\n }, [open]);\n\n useEffect(() => {\n if (triggerRect === null || panelHeight === null) {\n return;\n }\n\n setPos(computePosition(triggerRect, panelHeight));\n }, [triggerRect, panelHeight]);\n\n useEffect(() => {\n if (!pos) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n progress.setValue(0);\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationFast,\n useNativeDriver: true,\n }).start();\n }, [pos, reducedMotion, progress, theme.durationFast]);\n\n const handlePanelLayout = (e: LayoutChangeEvent) => {\n setPanelHeight(Math.min(e.nativeEvent.layout.height, MAX_LIST_HEIGHT));\n };\n\n return (\n <View style={style}>\n <Pressable\n ref={triggerRef}\n testID={testID}\n role=\"combobox\"\n accessibilityLabel={accessibilityLabel}\n accessibilityState={{ expanded: open, disabled: !!disabled }}\n disabled={disabled}\n onPress={() => (open ? closeList() : openList())}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: theme.space1,\n minHeight: 36,\n minWidth: 160,\n paddingVertical: 7,\n paddingHorizontal: theme.space2,\n backgroundColor: pressed ? theme.activeOverlay : theme.cardBgColor,\n borderWidth: 1,\n borderColor: open ? theme.accentColor : theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n opacity: disabled ? theme.opacityDisabled : 1,\n })}\n >\n <Text\n numberOfLines={1}\n style={{\n flex: 1,\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n opacity: selected ? 1 : theme.opacityDim,\n }}\n >\n {selected?.label ?? placeholder}\n </Text>\n <Icon\n icon={PanDown}\n size=\"md\"\n color=\"default\"\n style={{ transform: [{ rotate: open ? '180deg' : '0deg' }] }}\n />\n </Pressable>\n\n <Modal visible={open} transparent animationType=\"none\" statusBarTranslucent>\n <Pressable\n onPress={closeList}\n accessible={false}\n style={{ flex: 1 }}\n testID={testID ? `${testID}-backdrop` : undefined}\n >\n <Animated.View\n onLayout={handlePanelLayout}\n role=\"list\"\n accessible\n style={[\n {\n position: 'absolute',\n maxHeight: MAX_LIST_HEIGHT,\n backgroundColor: theme.popoverBgColor,\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n paddingVertical: 4,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 4 },\n shadowOpacity: 0.15,\n shadowRadius: 12,\n elevation: 6,\n },\n pos\n ? { top: pos.top, left: pos.left, width: pos.width, opacity: progress }\n : { top: -9999, left: -9999, width: 200, opacity: 0 },\n ]}\n >\n <ScrollView bounces={false}>\n {options.map((opt) => {\n const isSelected = opt.value === value;\n\n return (\n <Pressable\n key={opt.value}\n role=\"option\"\n accessibilityState={{ selected: isSelected, disabled: !!opt.disabled }}\n disabled={opt.disabled}\n onPress={() => selectOption(opt)}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n minHeight: 40,\n paddingVertical: 8,\n paddingHorizontal: theme.space2,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n opacity: opt.disabled ? theme.opacityDisabled : 1,\n })}\n >\n <View style={{ flex: 1, gap: 1 }}>\n <Text\n numberOfLines={1}\n style={{\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n fontWeight: isSelected\n ? (String(theme.fontWeightSemibold) as TextStyle['fontWeight'])\n : undefined,\n }}\n >\n {opt.label}\n </Text>\n {opt.description && (\n <Text\n numberOfLines={1}\n style={{\n fontSize: theme.fontSizeCaption,\n color: theme.windowFgColor,\n opacity: theme.opacityDim,\n }}\n >\n {opt.description}\n </Text>\n )}\n </View>\n {isSelected && <Icon icon={Check} size=\"md\" color=\"blue\" />}\n </Pressable>\n );\n })}\n </ScrollView>\n </Animated.View>\n </Pressable>\n </Modal>\n </View>\n );\n};\n","import { type ReactNode, useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\n\nimport { ActionRow } from '@/components/ActionRow';\nimport { Dropdown, type DropdownOption } from '@/components/Dropdown';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\n/** Same shape as `Dropdown`'s own option — re-exported so `ComboRow` reads self-contained. */\nexport type ComboRowOption<V extends string = string> = DropdownOption<V>;\n\nexport interface ComboRowProps<V extends string = string> {\n /** Primary label. */\n title: string;\n /** Secondary line below the title. */\n subtitle?: string;\n /** Icon or image placed at the leading edge. */\n leading?: ReactNode;\n /** The list of selectable options. */\n options: ComboRowOption<V>[];\n /** The currently selected value (controlled). */\n value?: V;\n /** Initial value when uncontrolled. */\n defaultValue?: V;\n /** Called when the user selects an option. */\n onValueChange?: (value: V) => void;\n /** Shown in the trigger while nothing is selected. Defaults to `\"—\"`. */\n placeholder?: string;\n /** Accessible name for the selector. Defaults to `title`. */\n accessibilityLabel?: string;\n /** Disables the row and its selector. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/** `.comboWrap { min-width: 120px }`. */\nconst TRIGGER_MIN_WIDTH = 120;\n\n/**\n * Settings row with an inline combo selector at the trailing edge, mirroring\n * `AdwComboRow` and `@gnome-ui/react`'s own `ComboRow`. Use inside a\n * `BoxedList` for a setting that picks one of a set of options.\n *\n * **This is a composition of `ActionRow` + `Dropdown`, where the web version\n * hand-rolls its own listbox inline** — around 200 lines re-implementing the\n * trigger, the flip-up placement, outside-click dismissal, roving\n * `aria-activedescendant` and the whole keyboard layer, none of which is\n * meaningfully different from that package's own `Dropdown`. Nothing forced\n * the duplication visually either: `.row` is `ActionRow`'s exact metrics\n * (12/24 dp padding, 52 dp min-height) and `.trigger` is `Dropdown`'s exact\n * trigger (card background, 1 px shade border turning accent when open,\n * `radius-md`, chevron). So this port composes the two already-shipped,\n * already-verified components instead — which also means the flip-to-fit\n * placement, the tap-outside dismissal and the `Modal`-based list all come\n * along for free rather than being rebuilt and re-debugged.\n *\n * The web keeps its own `useState` for the uncontrolled case; `Dropdown` is\n * controlled-only, so that state lives here — same behaviour, one level up.\n *\n * The keyboard layer (`↑`/`↓`/`Home`/`End`/`Enter`/`Escape`) drops as it\n * does everywhere else in this package, and `Dropdown` already provides the\n * touch equivalents it was built with.\n *\n * The dimming for `disabled` is applied here rather than left to\n * `ActionRow`, which only dims in its `interactive` branch — on a plain\n * (non-pressable) row its `disabled` prop currently reaches a `View` that\n * does nothing with it. Making this row `interactive` isn't the answer: the\n * `Dropdown` is the control, and the row itself has nothing to press.\n *\n * @example\n * <BoxedList>\n * <ComboRow\n * title=\"Language\"\n * subtitle=\"Used across the whole app\"\n * value={language}\n * onValueChange={setLanguage}\n * options={[\n * { value: 'en', label: 'English' },\n * { value: 'es', label: 'Español' },\n * ]}\n * />\n * </BoxedList>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ComboRow.html\n */\nexport const ComboRow = <V extends string = string>({\n title,\n subtitle,\n leading,\n options,\n value: controlledValue,\n defaultValue,\n onValueChange,\n placeholder = '—',\n accessibilityLabel,\n disabled = false,\n style,\n testID,\n}: ComboRowProps<V>) => {\n const theme = useGnomeTheme();\n const isControlled = controlledValue !== undefined;\n const [uncontrolledValue, setUncontrolledValue] = useState<V | undefined>(defaultValue);\n const value = isControlled ? controlledValue : uncontrolledValue;\n\n return (\n <ActionRow\n testID={testID}\n title={title}\n subtitle={subtitle}\n leading={leading}\n disabled={disabled}\n style={[disabled ? { opacity: theme.opacityDisabled } : null, style]}\n trailing={\n <Dropdown\n options={options}\n value={value}\n placeholder={placeholder}\n accessibilityLabel={accessibilityLabel ?? title}\n disabled={disabled}\n onChange={(next) => {\n if (!isControlled) {\n setUncontrolledValue(next);\n }\n\n onValueChange?.(next);\n }}\n style={{ minWidth: TRIGGER_MIN_WIDTH }}\n />\n }\n />\n );\n};\n","import { Check, Copy } from '@gnome-ui/icons';\nimport type ClipboardModule from '@react-native-clipboard/clipboard';\nimport { useEffect, useRef, useState } from 'react';\nimport { Text } from 'react-native';\n\nimport { IconButton, type IconButtonProps } from '@/components/IconButton';\n\nexport interface CopyButtonProps\n extends Omit<IconButtonProps, 'icon' | 'label' | 'onPress' | 'tooltip'> {\n /** The text copied to the clipboard when the button is activated. */\n value: string;\n /** Accessible label and tooltip shown before copying. Defaults to `\"Copy\"`. */\n label?: string;\n /** Accessible label and tooltip shown briefly after a successful copy. Defaults to `\"Copied!\"`. */\n copiedLabel?: string;\n /** How long the \"copied\" confirmation state is shown, in milliseconds. Defaults to `2000`. */\n resetDelay?: number;\n /**\n * Called after `value` is written to the clipboard.\n * Named `onCopied` (not `onCopy`), mirroring the web version's own naming.\n */\n onCopied?: (value: string) => void;\n /**\n * Called if the copy attempt throws. Kept for API parity with the web\n * version's `onCopyError` (whose `navigator.clipboard.writeText` can\n * reject, e.g. on permission denial) — but `@react-native-clipboard/\n * clipboard`'s `setString` is a synchronous, void-returning native call\n * with no error channel at all, so in normal operation this won't fire\n * the way it can on web. Kept as a defensive catch around the native\n * call (a real, if unlikely, synchronous throw) rather than removed.\n */\n onCopyError?: (error: unknown) => void;\n}\n\n// Visually hides the live-region announcement without removing it from the\n// accessibility tree — same 1x1/overflow-hidden recipe as the web version's\n// `LIVE_REGION_STYLE`.\nconst LIVE_REGION_STYLE = {\n position: 'absolute' as const,\n width: 1,\n height: 1,\n overflow: 'hidden' as const,\n};\n\n/**\n * Icon button that copies `value` to the clipboard, swapping to a checkmark\n * and a \"Copied!\" tooltip for `resetDelay` ms as confirmation — mirrors\n * `@gnome-ui/react`'s `CopyButton`.\n *\n * RN has no `navigator.clipboard`, so this is built on\n * `@react-native-clipboard/clipboard` (the community-standard clipboard\n * module, not `expo-clipboard` — chosen so this package works the same in\n * bare RN and Expo, not just this repo's own Expo example app). Its\n * `setString` is synchronous and void — see `onCopyError`'s doc for what\n * that means for error handling.\n *\n * The live-region announcement uses `role=\"status\"` directly (RN's newer\n * `Role` union does include `\"status\"`, unlike the older\n * `AccessibilityRole` enum `Toast` had to substitute `\"alert\"` for) plus\n * `accessibilityLiveRegion=\"polite\"` for Android's announcement mechanism.\n */\nexport const CopyButton = ({\n value,\n label = 'Copy',\n copiedLabel = 'Copied!',\n resetDelay = 2000,\n onCopied,\n onCopyError,\n ...props\n}: CopyButtonProps) => {\n const [copied, setCopied] = useState(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(\n () => () => {\n if (timeoutRef.current !== null) {\n clearTimeout(timeoutRef.current);\n }\n },\n [],\n );\n\n const handlePress = () => {\n try {\n // Required lazily, not imported at module scope: the native binding's\n // own top-level code calls `TurboModuleRegistry.getEnforcing(...)`\n // eagerly, which throws the instant the module is evaluated if the\n // native side isn't registered — as in Expo Go, which doesn't bundle\n // this community module. A static `import` would throw as soon as\n // this file is loaded, crashing every screen (not just this one),\n // since example apps import every component screen up front. Deferring\n // the require to press-time means Expo Go only fails if this specific\n // button is actually pressed, caught below like any other native error.\n const Clipboard: typeof ClipboardModule =\n // eslint-disable-next-line @typescript-eslint/no-require-imports -- see comment above\n require('@react-native-clipboard/clipboard').default;\n\n Clipboard.setString(value);\n } catch (error) {\n onCopyError?.(error);\n\n return;\n }\n\n onCopied?.(value);\n setCopied(true);\n\n if (timeoutRef.current !== null) {\n clearTimeout(timeoutRef.current);\n }\n\n timeoutRef.current = setTimeout(() => {\n setCopied(false);\n timeoutRef.current = null;\n }, resetDelay);\n };\n\n return (\n <>\n <IconButton\n {...props}\n icon={copied ? Check : Copy}\n label={copied ? copiedLabel : label}\n tooltip={copied ? copiedLabel : label}\n onPress={handlePress}\n />\n <Text role=\"status\" accessibilityLiveRegion=\"polite\" style={LIVE_REGION_STYLE}>\n {copied ? copiedLabel : ''}\n </Text>\n </>\n );\n};\n","import {\n cloneElement,\n type ReactElement,\n type ReactNode,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport type {\n GestureResponderEvent,\n LayoutChangeEvent,\n PressableProps,\n StyleProp,\n ViewStyle,\n} from 'react-native';\nimport { Animated, BackHandler, Dimensions, Modal, Pressable, View } from 'react-native';\n\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport type PopoverPlacement = 'top' | 'bottom' | 'left' | 'right';\n\nexport interface PopoverProps {\n /**\n * The rich content rendered inside the popover panel.\n * Can include interactive elements (buttons, links, forms).\n */\n content: ReactNode;\n /**\n * Preferred placement relative to the trigger.\n * Flips automatically when there is not enough viewport space.\n * Defaults to `\"bottom\"`.\n */\n placement?: PopoverPlacement;\n /**\n * Whether the popover is open (controlled mode).\n * Omit to use uncontrolled mode where the trigger toggles it.\n */\n open?: boolean;\n /** Called when open state should change. */\n onOpenChange?: (open: boolean) => void;\n /** Extra style on the popover panel itself. */\n panelStyle?: StyleProp<ViewStyle>;\n /**\n * The trigger element. Must be a single element built on `Pressable`\n * (e.g. `Button`, `Card`) that forwards its `ref` to the underlying `View`.\n */\n children: ReactElement<PressableProps & { ref?: Ref<View> }>;\n}\n\ninterface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface Size {\n width: number;\n height: number;\n}\n\ninterface Position {\n top: number;\n left: number;\n placement: PopoverPlacement;\n}\n\nconst GAP = 8;\nconst MARGIN = 10;\nconst ARROW = 6;\n\nfunction assignRef<T>(ref: Ref<T> | undefined, value: T | null) {\n if (!ref) {\n return;\n }\n\n if (typeof ref === 'function') {\n ref(value);\n\n return;\n }\n\n (ref as { current: T | null }).current = value;\n}\n\nfunction placementRect(trigger: Rect, panel: Size, placement: PopoverPlacement) {\n switch (placement) {\n case 'top':\n return {\n top: trigger.y - panel.height - GAP,\n left: trigger.x + trigger.width / 2 - panel.width / 2,\n };\n case 'bottom':\n return {\n top: trigger.y + trigger.height + GAP,\n left: trigger.x + trigger.width / 2 - panel.width / 2,\n };\n case 'left':\n return {\n top: trigger.y + trigger.height / 2 - panel.height / 2,\n left: trigger.x - panel.width - GAP,\n };\n case 'right':\n return {\n top: trigger.y + trigger.height / 2 - panel.height / 2,\n left: trigger.x + trigger.width + GAP,\n };\n }\n}\n\nfunction computePosition(trigger: Rect, panel: Size, preferred: PopoverPlacement): Position {\n const { width: vw, height: vh } = Dimensions.get('window');\n\n const opposite: PopoverPlacement =\n preferred === 'top'\n ? 'bottom'\n : preferred === 'bottom'\n ? 'top'\n : preferred === 'left'\n ? 'right'\n : 'left';\n const placements: PopoverPlacement[] = [preferred, opposite, 'bottom', 'top', 'left', 'right'];\n\n for (const p of placements) {\n const { top, left } = placementRect(trigger, panel, p);\n const fitsH = left >= MARGIN && left + panel.width <= vw - MARGIN;\n const fitsV = top >= MARGIN && top + panel.height <= vh - MARGIN;\n\n if (fitsH && fitsV) {\n return {\n top: Math.max(MARGIN, Math.min(top, vh - panel.height - MARGIN)),\n left: Math.max(MARGIN, Math.min(left, vw - panel.width - MARGIN)),\n placement: p,\n };\n }\n }\n\n const { top, left } = placementRect(trigger, panel, preferred);\n\n return {\n top: Math.max(MARGIN, Math.min(top, vh - panel.height - MARGIN)),\n left: Math.max(MARGIN, Math.min(left, vw - panel.width - MARGIN)),\n placement: preferred,\n };\n}\n\n/**\n * Floating panel anchored to a trigger element, following the Adwaita\n * `GtkPopover` pattern. Unlike `Tooltip`, a popover can contain rich\n * interactive content (buttons, links, forms).\n *\n * Rebuilt with `View`/`Pressable`/`Modal` rather than ported from\n * `@gnome-ui/react`'s DOM `Portal` + manual focus trap, reusing this\n * package's own established pieces rather than re-deriving them: `Tooltip`'s\n * `cloneElement`-onto-an-arbitrary-trigger architecture and 4-placement\n * fallback-cascade positioning (`computePosition`, same shape, no arrow-\n * offset-shift-when-clamped complexity — same simplification `Tooltip`\n * already accepted), and `Dropdown`'s toggle-on-press + full-screen backdrop\n * `Pressable` that closes on an outside tap (the RN analog of the web\n * version's document-level \"click outside\" listener) plus reduced-motion\n * fade-in.\n *\n * **Deliberate divergence from `Dropdown`'s backdrop structure**: `Dropdown`\n * nests its panel directly inside the backdrop `Pressable` and gets away\n * with it because almost every pixel of its panel is itself a `Pressable`\n * option row, which claims the touch responder before it can bubble to the\n * backdrop. A popover's `content` is arbitrary — likely to have inert\n * padding/whitespace/text with no `Pressable` of its own — so nesting the\n * same way would let a tap on inert panel space fall through to the\n * backdrop and close the popover, unlike the web version's `.contains()`\n * check (which never closes on *any* tap inside the panel). Fixed with\n * `onStartShouldSetResponder={() => true}` on the panel itself: it claims\n * the touch responder for any touch RN's negotiation hasn't already given to\n * a deeper `Pressable` inside `content`, without making the panel itself\n * behave like a button.\n *\n * `BackHandler`'s `hardwareBackPress` (wired the same way `Dialog` already\n * does) is the Android analog of the web version's document-level Escape\n * listener — there is no keyboard `Escape` to catch on a touch-first device.\n * Focus-trapping and focus-restore-on-close have no port: there is no DOM\n * `document.activeElement`/`querySelector` equivalent in RN, the same gap\n * that already left every other floating component in this package (\n * `Dialog`, `Tooltip`, `Dropdown`) without them.\n *\n * The web version's rotated-square-with-matching-background arrow (relying\n * on same-color blending across a straddled panel edge and CSS stacking\n * order) is replaced with `Tooltip`'s simpler transparent-border-triangle\n * technique — same visual affordance (a pointer toward the trigger), a much\n * simpler RN-native primitive.\n *\n * `role=\"dialog\"` on the panel ports 1:1 from RN's newer web-aligned `Role`\n * union (the same one `Dialog`/`Tooltip` already use) — no substitution\n * needed. `aria-haspopup`/`aria-controls` have no RN equivalent (no\n * cross-platform relationship-attribute prop); only `accessibilityState.\n * expanded` is wired on the trigger, the same subset `Dropdown`'s own\n * trigger already exposes.\n *\n * @example\n * <Popover content={<Text>Rich content here</Text>}>\n * <Button>Open</Button>\n * </Popover>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Popover.html\n */\nexport const Popover = ({\n content,\n placement: preferredPlacement = 'bottom',\n open: controlledOpen,\n onOpenChange,\n panelStyle,\n children,\n}: PopoverProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const isControlled = controlledOpen !== undefined;\n const [uncontrolledOpen, setUncontrolledOpen] = useState(false);\n const open = isControlled ? controlledOpen : uncontrolledOpen;\n\n const [triggerRect, setTriggerRect] = useState<Rect | null>(null);\n const [panelSize, setPanelSize] = useState<Size | null>(null);\n const [pos, setPos] = useState<Position | null>(null);\n\n const triggerRef = useRef<View>(null);\n const progress = useRef(new Animated.Value(0)).current;\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (!isControlled) {\n setUncontrolledOpen(next);\n }\n\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange],\n );\n\n const close = useCallback(() => setOpen(false), [setOpen]);\n const toggle = useCallback(() => setOpen(!open), [open, setOpen]);\n\n // Measure the trigger's on-screen rect as soon as it's asked to show.\n useEffect(() => {\n if (!open) {\n setTriggerRect(null);\n setPanelSize(null);\n setPos(null);\n\n return;\n }\n\n triggerRef.current?.measureInWindow((x, y, width, height) => {\n setTriggerRect({ x, y, width, height });\n });\n }, [open]);\n\n // Resolve the final position once both the trigger's rect and the panel's\n // own rendered size are known.\n useEffect(() => {\n if (!triggerRect || !panelSize) {\n return;\n }\n\n setPos(computePosition(triggerRect, panelSize, preferredPlacement));\n }, [triggerRect, panelSize, preferredPlacement]);\n\n useEffect(() => {\n if (!pos) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n progress.setValue(0);\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationFast,\n useNativeDriver: true,\n }).start();\n }, [pos, reducedMotion, progress, theme.durationFast]);\n\n // Android hardware back button closes the popover — the analog of the web\n // version's document-level Escape listener, same pattern `Dialog` uses.\n useEffect(() => {\n if (!open) {\n return;\n }\n\n const subscription = BackHandler.addEventListener('hardwareBackPress', () => {\n close();\n\n return true;\n });\n\n return () => subscription.remove();\n }, [open, close]);\n\n const handlePanelLayout = (e: LayoutChangeEvent) => {\n const { width, height } = e.nativeEvent.layout;\n\n setPanelSize({ width, height });\n };\n\n const arrowBase = {\n position: 'absolute' as const,\n width: 0,\n height: 0,\n borderWidth: ARROW,\n borderColor: 'transparent',\n };\n\n const arrowStyle = (() => {\n switch (pos?.placement) {\n case 'top':\n return {\n ...arrowBase,\n top: '100%' as const,\n left: '50%' as const,\n marginLeft: -ARROW,\n borderTopColor: theme.popoverBgColor,\n };\n case 'bottom':\n return {\n ...arrowBase,\n bottom: '100%' as const,\n left: '50%' as const,\n marginLeft: -ARROW,\n borderBottomColor: theme.popoverBgColor,\n };\n case 'left':\n return {\n ...arrowBase,\n left: '100%' as const,\n top: '50%' as const,\n marginTop: -ARROW,\n borderLeftColor: theme.popoverBgColor,\n };\n case 'right':\n return {\n ...arrowBase,\n right: '100%' as const,\n top: '50%' as const,\n marginTop: -ARROW,\n borderRightColor: theme.popoverBgColor,\n };\n default:\n return null;\n }\n })();\n\n const existingRef = (children.props as { ref?: Ref<View> }).ref;\n\n const trigger = cloneElement(children, {\n ref: (node: View | null) => {\n (triggerRef as { current: View | null }).current = node;\n assignRef(existingRef, node);\n },\n accessibilityState: { expanded: open, ...children.props.accessibilityState },\n onPress: (e: GestureResponderEvent) => {\n toggle();\n children.props.onPress?.(e);\n },\n });\n\n return (\n <>\n {trigger}\n <Modal\n visible={open}\n transparent\n animationType=\"none\"\n statusBarTranslucent\n onRequestClose={close}\n >\n <Pressable onPress={close} accessible={false} style={{ flex: 1 }}>\n <Animated.View\n onLayout={handlePanelLayout}\n role=\"dialog\"\n accessible\n onStartShouldSetResponder={() => true}\n style={[\n {\n position: 'absolute',\n minWidth: 180,\n maxWidth: 320,\n backgroundColor: theme.popoverBgColor,\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusLg,\n padding: theme.space2,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 4 },\n shadowOpacity: 0.15,\n shadowRadius: 12,\n elevation: 6,\n },\n pos\n ? { top: pos.top, left: pos.left, opacity: progress }\n : { top: -9999, left: -9999, opacity: 0 },\n panelStyle,\n ]}\n >\n {content}\n {arrowStyle && <View style={arrowStyle} />}\n </Animated.View>\n </Pressable>\n </Modal>\n </>\n );\n};\n","import { useCallback } from 'react';\nimport type { AccessibilityActionEvent, StyleProp, ViewStyle } from 'react-native';\nimport { Pressable, Text, View } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nconst BTN_SIZE = 36;\n\nexport interface SpinButtonProps {\n /** Current value. */\n value: number;\n /** Called when the value changes. */\n onChange: (value: number) => void;\n /** Minimum allowed value. Defaults to `0`. */\n min?: number;\n /** Maximum allowed value. Defaults to `100`. */\n max?: number;\n /** Amount to increment/decrement per step. Defaults to `1`. */\n step?: number;\n /** Number of decimal places shown. Derived from `step` when omitted. */\n decimals?: number;\n /**\n * Wrap around instead of clamping at the edges — stepping past `max` returns\n * to `min` and vice versa, and the −/+ buttons never disable. Mirrors\n * `GtkSpinButton:wrap`; used for cyclic values like hours and minutes.\n */\n wrap?: boolean;\n /**\n * Custom display formatter for the current value (e.g. zero-padding, or\n * mapping a numeric value to `AM`/`PM`). Defaults to fixed-decimal text. When\n * provided, its result is also exposed as the accessibility value's `text`.\n */\n format?: (value: number) => string;\n /** Disables the control. */\n disabled?: boolean;\n /** Accessible label. Required — RN has no visible-label association to fall back on. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\nfunction countDecimals(n: number): number {\n const s = n.toString();\n const dot = s.indexOf('.');\n\n return dot === -1 ? 0 : s.length - dot - 1;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n\nfunction wrapValue(value: number, min: number, max: number, step: number): number {\n const range = max - min + step;\n return ((((value - min) % range) + range) % range) + min;\n}\n\n/**\n * Numeric input with − and + buttons following the Adwaita `GtkSpinButton` style.\n *\n * Rebuilt with `View`/`Pressable`/`Text` rather than ported from\n * `@gnome-ui/react`'s DOM-based JSX, but mirrors its prop API and clamp/wrap/\n * decimal math (pure JS, ported verbatim).\n *\n * The primary interaction is tapping the visible −/+ buttons, same as a\n * sighted mouse user on the web version. The web version's keyboard\n * interaction (↑/↓ one step, Page Up/Down ten steps, Home/End to bounds) has\n * no RN equivalent — a touch-first device has no keyboard to drive it, the\n * same reasoning `Slider` already applied. Rather than dropping value\n * adjustment accessibility entirely, single-step increment/decrement is\n * wired through `accessibilityRole=\"adjustable\"` + `onAccessibilityAction`\n * (VoiceOver's swipe-up/down, TalkBack's local-context menu), reusing the\n * exact recipe `Slider` already proved works — the bigger Page Up/Down and\n * Home/End jumps have no equivalent screen-reader gesture on either\n * platform, so those alone are dropped, same as `Slider`. The visible\n * buttons are hidden from the accessibility tree (mirrors the web version's\n * `aria-hidden`/`tabIndex={-1}` on both `<button>`s) so a screen reader user\n * gets one adjustable stop, not three.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/spin-buttons.html\n */\nexport const SpinButton = ({\n value,\n onChange,\n min = 0,\n max = 100,\n step = 1,\n decimals,\n wrap = false,\n format,\n disabled = false,\n accessibilityLabel,\n style,\n}: SpinButtonProps) => {\n const theme = useGnomeTheme();\n const dp = decimals ?? countDecimals(step);\n\n const set = useCallback(\n (next: number) => {\n const bounded = wrap ? wrapValue(next, min, max, step) : clamp(next, min, max);\n onChange(parseFloat(bounded.toFixed(dp)));\n },\n [onChange, min, max, step, dp, wrap],\n );\n\n const displayText = format ? format(value) : value.toFixed(dp);\n\n const handleAccessibilityAction = useCallback(\n (event: AccessibilityActionEvent) => {\n if (disabled) {\n return;\n }\n\n switch (event.nativeEvent.actionName) {\n case 'increment':\n set(value + step);\n break;\n case 'decrement':\n set(value - step);\n break;\n }\n },\n [disabled, value, step, set],\n );\n\n const decrementDisabled = disabled || (!wrap && value <= min);\n const incrementDisabled = disabled || (!wrap && value >= max);\n\n return (\n <View\n accessible\n accessibilityRole=\"adjustable\"\n accessibilityLabel={accessibilityLabel}\n accessibilityValue={{ min, max, now: value, text: format ? displayText : undefined }}\n accessibilityState={{ disabled }}\n accessibilityActions={[\n { name: 'increment', label: 'increment' },\n { name: 'decrement', label: 'decrement' },\n ]}\n onAccessibilityAction={handleAccessibilityAction}\n style={[\n {\n flexDirection: 'row',\n alignSelf: 'flex-start',\n alignItems: 'center',\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n backgroundColor: theme.viewBgColor,\n overflow: 'hidden',\n opacity: disabled ? theme.opacityDisabled : 1,\n },\n style,\n ]}\n >\n <Pressable\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n disabled={decrementDisabled}\n onPress={() => set(value - step)}\n style={({ pressed }) => ({\n width: BTN_SIZE,\n height: BTN_SIZE,\n alignItems: 'center',\n justifyContent: 'center',\n borderRightWidth: 1,\n borderRightColor: theme.cardShadeColor,\n backgroundColor: pressed && !decrementDisabled ? theme.activeOverlay : 'transparent',\n opacity: decrementDisabled ? theme.opacityDisabled : 1,\n })}\n >\n <Text style={{ fontSize: 17, lineHeight: 20, color: theme.viewFgColor }}>−</Text>\n </Pressable>\n\n <Text\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n minWidth: 56,\n textAlign: 'center',\n paddingHorizontal: theme.space2,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontVariant: ['tabular-nums'],\n color: theme.viewFgColor,\n }}\n >\n {displayText}\n </Text>\n\n <Pressable\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n disabled={incrementDisabled}\n onPress={() => set(value + step)}\n style={({ pressed }) => ({\n width: BTN_SIZE,\n height: BTN_SIZE,\n alignItems: 'center',\n justifyContent: 'center',\n borderLeftWidth: 1,\n borderLeftColor: theme.cardShadeColor,\n backgroundColor: pressed && !incrementDisabled ? theme.activeOverlay : 'transparent',\n opacity: incrementDisabled ? theme.opacityDisabled : 1,\n })}\n >\n <Text style={{ fontSize: 17, lineHeight: 20, color: theme.viewFgColor }}>+</Text>\n </Pressable>\n </View>\n );\n};\n","/**\n * Pure time helpers shared by `TimePicker` and `DatePicker`'s `showTime`\n * footer. Ported verbatim from `@gnome-ui/react`'s `TimePicker/timeUtils.ts`\n * — no DOM dependency there already, the same `calendarUtils.ts` precedent.\n *\n * Not exported from the package barrel — `DatePicker` imports it directly\n * via `@/components/TimePicker/timeUtils`, the same cross-folder internal\n * import `Calendar/calendarUtils.ts`'s `WeekStart` already established.\n */\n\n/** A wall-clock time, in 24-hour terms. */\nexport interface TimeValue {\n /** Hours, `0`–`23`. */\n hours: number;\n /** Minutes, `0`–`59`. */\n minutes: number;\n}\n\n/** Zero-padded two-digit rendering for a clock column. */\nexport const pad2 = (n: number) => String(n).padStart(2, '0');\n\n/** Split a 0–23 hour into its 12-hour parts (`period`: 0 = AM, 1 = PM). */\nexport const to12 = (hours24: number) => ({\n hour: hours24 % 12 === 0 ? 12 : hours24 % 12,\n period: hours24 < 12 ? 0 : 1,\n});\n\n/** Recombine a 12-hour clock reading into a 0–23 hour. */\nexport const to24 = (hour12: number, period: number) =>\n period === 1 ? (hour12 % 12) + 12 : hour12 % 12;\n\n/** The wall-clock time carried by a `Date`. */\nexport const timeOf = (date: Date): TimeValue => ({\n hours: date.getHours(),\n minutes: date.getMinutes(),\n});\n\n/**\n * `date`'s calendar day at `time`'s wall clock.\n *\n * A local `Date` cannot represent an hour that a DST spring-forward skips, and\n * the platform silently shifts it (02:30 → 03:30). That shift is kept — it is\n * the only real instant for that reading — so callers should read the result\n * back rather than assume the requested hour survived.\n */\nexport const mergeDateAndTime = (date: Date, time: TimeValue): Date =>\n new Date(date.getFullYear(), date.getMonth(), date.getDate(), time.hours, time.minutes);\n","import type { StyleProp, ViewStyle } from 'react-native';\nimport { View } from 'react-native';\n\nimport { SpinButton } from '@/components/SpinButton';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nimport { pad2, type TimeValue, to12, to24 } from './timeUtils';\n\nexport interface TimeFieldsProps {\n /** The time the columns show. Always concrete — callers supply the fallback. */\n value: TimeValue;\n /** Called with the whole time whenever any column moves. */\n onChange: (value: TimeValue) => void;\n /** 12- or 24-hour presentation. Defaults to `24`. */\n hourCycle?: 12 | 24;\n /** Minute increment for the spinner. Defaults to `1`. */\n minuteStep?: number;\n /**\n * Prefix for each column's accessible label — `\"Start\"` gives\n * \"Start hours\". Needed when a panel carries more than one set of columns.\n */\n labelPrefix?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * The hour/minute (and AM/PM) `SpinButton` columns — the panel half of\n * `TimePicker`, split out so `DatePicker`'s `showTime` footer can reuse the\n * same 12/24-hour bookkeeping without re-implementing it.\n *\n * Ported from `@gnome-ui/react`'s `TimePicker/TimeFields.tsx`: mirrors its\n * prop API and `to12`/`to24` bookkeeping exactly, rebuilt on this package's\n * own `SpinButton` (already shipped in Tier 5, with a matching `wrap`\n * boolean and `format: (n: number) => string` callback — confirmed by\n * reading `SpinButton.tsx` before wiring this up, no new prop needed for the\n * AM/PM column's \"numeric spinner whose `format` maps 0/1 to text\" trick).\n *\n * **Not exported from the package**: `TimePicker` is this module's public\n * face; `DatePicker`'s `showTime` footer imports it directly via\n * `@/components/TimePicker/TimeFields`, the same cross-folder internal\n * import `Calendar/calendarUtils.ts`'s `WeekStart` already established.\n */\nexport const TimeFields = ({\n value,\n onChange,\n hourCycle = 24,\n minuteStep = 1,\n labelPrefix,\n style,\n}: TimeFieldsProps) => {\n const theme = useGnomeTheme();\n const twelve = to12(value.hours);\n const label = (name: string) => (labelPrefix ? `${labelPrefix} ${name}` : name);\n\n const setHours24 = (hours: number) => onChange({ hours, minutes: value.minutes });\n const setMinutes = (minutes: number) => onChange({ hours: value.hours, minutes });\n\n return (\n <View\n style={[\n { flexDirection: 'row', alignItems: 'center', gap: theme.space1, flexWrap: 'wrap' },\n style,\n ]}\n >\n {hourCycle === 12 ? (\n <SpinButton\n value={twelve.hour}\n min={1}\n max={12}\n wrap\n format={pad2}\n onChange={(h) => setHours24(to24(h, twelve.period))}\n accessibilityLabel={label('Hours')}\n />\n ) : (\n <SpinButton\n value={value.hours}\n min={0}\n max={23}\n wrap\n format={pad2}\n onChange={setHours24}\n accessibilityLabel={label('Hours')}\n />\n )}\n\n <Text variant=\"body\" accessible={false}>\n :\n </Text>\n\n <SpinButton\n value={value.minutes}\n min={0}\n max={59}\n step={minuteStep}\n wrap\n format={pad2}\n onChange={setMinutes}\n accessibilityLabel={label('Minutes')}\n />\n\n {hourCycle === 12 && (\n <SpinButton\n value={twelve.period}\n min={0}\n max={1}\n wrap\n format={(p) => (p === 1 ? 'PM' : 'AM')}\n onChange={(p) => setHours24(to24(twelve.hour, p))}\n accessibilityLabel={label('AM/PM')}\n />\n )}\n </View>\n );\n};\n","import { XOfficeCalendar } from '@gnome-ui/icons';\nimport { useMemo, useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Pressable, View } from 'react-native';\n\nimport { Button } from '@/components/Button';\nimport { Calendar } from '@/components/Calendar';\nimport type { WeekStart } from '@/components/Calendar/calendarUtils';\nimport { Icon } from '@/components/Icon';\nimport { Popover, type PopoverPlacement } from '@/components/Popover';\nimport { Text } from '@/components/Text';\nimport { TimeFields } from '@/components/TimePicker/TimeFields';\nimport { mergeDateAndTime, type TimeValue, timeOf } from '@/components/TimePicker/timeUtils';\nimport { useDateTimeFormatter, useGnomeTheme } from '@/GnomeProvider';\n\nexport interface DatePickerProps {\n /** Controlled selected date. Pass `null` for \"no selection\". */\n value?: Date | null;\n /** Initial selected date when uncontrolled. Defaults to `null`. */\n defaultValue?: Date | null;\n /** Called when the user picks a date (or, with `showTime`, edits a time column). */\n onChange?: (date: Date) => void;\n /** Earliest selectable date (inclusive). */\n min?: Date;\n /** Latest selectable date (inclusive). */\n max?: Date;\n /** First day of the week: `0` (Sunday) … `6` (Saturday). Defaults to `1` (Monday). */\n weekStartsOn?: WeekStart;\n /**\n * Add hour/minute columns under the calendar, making the emitted `Date` a\n * point in time rather than a civil date. Picking a day then keeps the\n * popover open — the selection is only finished by the Done button.\n */\n showTime?: boolean;\n /** 12- or 24-hour columns when `showTime` is on. Defaults to `24`. */\n hourCycle?: 12 | 24;\n /** Minute increment for the time spinner. Defaults to `1`. */\n minuteStep?: number;\n /** Label on the button that closes a `showTime` popover. Defaults to `'Done'`. */\n doneLabel?: string;\n /** Text shown in the trigger while no date is selected. */\n placeholder?: string;\n /** Visible label rendered above the trigger. */\n label?: string;\n /** Accessible name for the trigger when no visible `label` is provided. */\n accessibilityLabel?: string;\n /** Show an ISO week-number column in the calendar. */\n showWeekNumbers?: boolean;\n /** Disable the control. */\n disabled?: boolean;\n /** Preferred popover placement relative to the trigger. Defaults to `'bottom'`. */\n placement?: PopoverPlacement;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n// Hoisted to module scope so `useDateTimeFormatter`'s `useMemo`-over-`options`\n// identity check actually holds across renders — the same `Calendar`\n// precedent (the first component in this package to seriously exercise that\n// hook). Three fixed combinations cover every `showTime`/`hourCycle` pairing,\n// so there's no need for an arbitrary caller-supplied `formatOptions` prop\n// the way the web version has one: the hook's own `{...dateTimeFormat,\n// ...options}` merge already expresses `dateStyle`+`timeStyle`+`hourCycle`\n// together, confirmed by reading `useDateTimeFormatter` before relying on it.\nconst DATE_ONLY_OPTIONS: Intl.DateTimeFormatOptions = { dateStyle: 'medium' };\nconst DATE_TIME_24_OPTIONS: Intl.DateTimeFormatOptions = {\n dateStyle: 'medium',\n timeStyle: 'short',\n hourCycle: 'h23',\n};\nconst DATE_TIME_12_OPTIONS: Intl.DateTimeFormatOptions = {\n dateStyle: 'medium',\n timeStyle: 'short',\n hourCycle: 'h12',\n};\n\n// The web version widens its popover panel for the `showTime` footer because\n// a 12-hour row of three `SpinButton` columns is wider than the default\n// 320 dp cap. `SpinButton`'s own fixed per-column minimum (two 36 dp\n// buttons plus a 56 dp value `Text`) makes that footer wide enough that it\n// can still approach a narrow phone's screen width even after widening —\n// `TimeFields`' `flexWrap` lets the Done button drop to its own line rather\n// than forcing horizontal scroll or clipping.\nconst TIME_PANEL_STYLE: ViewStyle = { maxWidth: 420 };\n\n/**\n * A `Popover`-anchored `Calendar` behind a text-entry-styled trigger —\n * mirrors the `GtkCalendar` + `GtkPopover` composition GNOME apps use for\n * date entry.\n *\n * Rebuilt on this package's own already-shipped `Popover` (Tier 5) and\n * `Calendar` (Tier 20) rather than reinventing either: the trigger is a\n * `Pressable` styled like `Dropdown`'s own text-entry-look trigger (same\n * bordered row, dimmed placeholder, trailing icon), and the panel is\n * `Calendar` plus an optional `showTime` footer, composed exactly the way\n * `Popover`'s own doc comment expects a rich-content consumer to use it —\n * no new position-computation code, per this package's standing pitfall\n * about not reinventing `Popover`/`Dropdown`'s already-shipped trigger-rect\n * + panel-size positioning.\n *\n * **The web version's entire keyboard layer drops** — no `ArrowDown`-opens,\n * no `Enter`/`Space` on the trigger — the same touch-first convention this\n * whole package follows; `Calendar`'s own `autoFocus` prop was already\n * dropped when `Calendar` was ported (no keyboard focus concept without a\n * keyboard), so it isn't wired here either. `locale`/`formatOptions` are\n * dropped in favor of `GnomeProvider`'s app-wide `useDateTimeFormatter`,\n * following `Calendar`'s own just-shipped convention rather than\n * reintroducing a locale prop pair.\n *\n * Picking a day keeps the popover open when `showTime` is on (only the Done\n * button closes it, so the time columns stay reachable) and closes it\n * immediately otherwise — ported as-is from the web version's\n * `handleSelect`, plain state logic with no web-only API involved.\n *\n * @see https://gnome.pages.gitlab.gnome.org/gtk/gtk4/class.Calendar.html\n */\nexport const DatePicker = ({\n value: controlledValue,\n defaultValue = null,\n onChange,\n min,\n max,\n weekStartsOn = 1,\n showTime = false,\n hourCycle = 24,\n minuteStep = 1,\n doneLabel = 'Done',\n placeholder = 'Select a date',\n label,\n accessibilityLabel,\n showWeekNumbers = false,\n disabled = false,\n placement = 'bottom',\n style,\n testID,\n}: DatePickerProps) => {\n const theme = useGnomeTheme();\n\n const isControlled = controlledValue !== undefined;\n const [uncontrolledValue, setUncontrolledValue] = useState<Date | null>(defaultValue);\n const selected = isControlled ? controlledValue : uncontrolledValue;\n\n const [open, setOpen] = useState(false);\n\n const formatOptions = showTime\n ? hourCycle === 12\n ? DATE_TIME_12_OPTIONS\n : DATE_TIME_24_OPTIONS\n : DATE_ONLY_OPTIONS;\n const formatter = useDateTimeFormatter(formatOptions);\n const displayValue = selected ? formatter.format(selected) : placeholder;\n\n // The columns always need a concrete time; noon is the same neutral\n // fallback `TimePicker` uses, and it isn't treated as a selection until a\n // day lands.\n const draftTime: TimeValue = useMemo(\n () => (selected ? timeOf(selected) : { hours: 12, minutes: 0 }),\n [selected],\n );\n\n const commit = (date: Date) => {\n if (!isControlled) {\n setUncontrolledValue(date);\n }\n onChange?.(date);\n };\n\n const handleSelect = (date: Date) => {\n // `Calendar` hands back a civil date at local midnight, so the time in\n // play has to be carried over rather than reset on every day tap.\n commit(showTime ? mergeDateAndTime(date, draftTime) : date);\n if (!showTime) {\n setOpen(false);\n }\n };\n\n const handleTimeChange = (time: TimeValue) => {\n // Editing the time before any day picks today — otherwise there is\n // nothing to attach the clock reading to.\n commit(mergeDateAndTime(selected ?? new Date(), time));\n };\n\n const panel = (\n <View style={{ gap: theme.space2 }}>\n <Calendar\n value={selected}\n defaultMonth={selected ?? undefined}\n onChange={handleSelect}\n min={min}\n max={max}\n weekStartsOn={weekStartsOn}\n showWeekNumbers={showWeekNumbers}\n testID={testID ? `${testID}-calendar` : undefined}\n />\n\n {showTime && (\n <View\n style={{\n flexDirection: 'row',\n flexWrap: 'wrap',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: theme.space2,\n }}\n >\n <TimeFields\n value={draftTime}\n onChange={handleTimeChange}\n hourCycle={hourCycle}\n minuteStep={minuteStep}\n />\n <Button variant=\"suggested\" size=\"sm\" onPress={() => setOpen(false)}>\n {doneLabel}\n </Button>\n </View>\n )}\n </View>\n );\n\n return (\n <View style={[{ gap: theme.space1 }, style]}>\n {label && <Text variant=\"body\">{label}</Text>}\n\n <Popover\n placement={placement}\n open={open}\n onOpenChange={setOpen}\n panelStyle={showTime ? TIME_PANEL_STYLE : undefined}\n content={panel}\n >\n <Pressable\n testID={testID}\n role=\"button\"\n disabled={disabled}\n accessibilityLabel={accessibilityLabel ?? label}\n accessibilityState={{ disabled }}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: theme.space1,\n minHeight: 36,\n minWidth: 160,\n paddingVertical: 7,\n paddingHorizontal: theme.space2,\n backgroundColor: pressed ? theme.activeOverlay : theme.cardBgColor,\n borderWidth: 1,\n borderColor: open ? theme.accentColor : theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n opacity: disabled ? theme.opacityDisabled : 1,\n })}\n >\n <Text\n numberOfLines={1}\n style={{\n flex: 1,\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n opacity: selected ? 1 : theme.opacityDim,\n }}\n >\n {displayValue}\n </Text>\n <Icon icon={XOfficeCalendar} size=\"sm\" />\n </Pressable>\n </Popover>\n </View>\n );\n};\n","import { type ReactNode, useEffect, useRef } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Animated, BackHandler, Easing, Modal, Pressable, View } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\n\n/**\n * `Animated`'s style interpolation only takes effect on `Animated.*` host\n * components — a plain `Pressable` would silently ignore the animated\n * `opacity` in its style, the same gotcha already documented on `Toast`.\n * Hoisted to module scope so it isn't recreated on every render.\n */\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable);\n\nexport type DialogButtonVariant = 'default' | 'suggested' | 'destructive';\n\nexport interface DialogButton {\n /** Button label. */\n label: string;\n /** Visual variant. Defaults to `\"default\"`. */\n variant?: DialogButtonVariant;\n /** Called when the button is pressed. */\n onPress: () => void;\n /** Whether the button is disabled. */\n disabled?: boolean;\n}\n\nexport type AlertDialogResponseVariant = DialogButtonVariant;\n\nexport interface AlertDialogResponse {\n /**\n * Unique identifier returned via `onResponse`.\n * Use `\"cancel\"` by convention for the dismissive action.\n */\n id: string;\n /** Button label. */\n label: string;\n /** Visual emphasis. Defaults to `\"default\"`. */\n variant?: AlertDialogResponseVariant;\n /** Disables the button. */\n disabled?: boolean;\n}\n\nexport interface DialogProps {\n /** Whether the dialog is visible. */\n open: boolean;\n\n // ── Standard dialog ────────────────────────────────────────────────────\n\n /** Dialog heading. */\n title?: ReactNode;\n /** Body content. */\n children?: ReactNode;\n /** Action buttons (standard dialog API). */\n buttons?: DialogButton[];\n /** Called on Android back button / backdrop press. */\n onClose?: () => void;\n /** Whether pressing the backdrop closes the dialog. Defaults to `true`. */\n closeOnBackdrop?: boolean;\n\n // ── AlertDialog extension ───────────────────────────────────────────────\n\n /**\n * Use `\"alertdialog\"` for confirmations and destructive warnings — screen\n * readers announce it immediately. Defaults to `\"dialog\"`.\n *\n * When using `role=\"alertdialog\"`, prefer the `responses`/`onResponse` API\n * over `buttons`/`onPress` for semantic clarity.\n */\n role?: 'dialog' | 'alertdialog';\n\n /**\n * Response buttons (AlertDialog API). Alternative to `buttons` — each\n * response has a semantic `id` returned via `onResponse`. The Android back\n * button and backdrop press fire the first non-destructive response.\n */\n responses?: AlertDialogResponse[];\n\n /**\n * Called with the `id` of the response button pressed.\n * Required when `responses` is provided.\n */\n onResponse?: (id: string) => void;\n\n style?: StyleProp<ViewStyle>;\n /** Forwarded to the backdrop — useful for testing. */\n testID?: string;\n}\n\n/**\n * Blocking modal dialog following the Adwaita pattern.\n *\n * **Standard** — `title` + `children` + `buttons[]`.\n *\n * **Alert** — add `role=\"alertdialog\"` + `responses[]` + `onResponse`.\n * Uses a semantic response id instead of per-button `onPress`. The Android\n * back button / backdrop press fire the first non-destructive response.\n * Mirrors `AdwAlertDialog`.\n *\n * Built on RN's own `Modal` rather than `@gnome-ui/react`'s DOM `Portal` +\n * manual focus trap: `Modal` already floats above everything with no portal\n * target needed, already blocks interaction with the screen behind it (no\n * `useBodyScrollLock` equivalent needed), and its `onRequestClose` fires on\n * the Android hardware back button — the direct analog of the web version's\n * document-level Escape listener. Focus-trapping (`Tab`/`Shift+Tab` cycling)\n * has no port: there is no keyboard `Tab` concept in RN's touch-first model,\n * the same reasoning that already dropped `TabBar`'s roving-tabindex arrow\n * keys.\n *\n * `role` is passed straight through as RN's own `role` prop (not\n * `accessibilityRole`) — RN's newer, web-aligned `Role` union has real\n * `\"dialog\"`/`\"alertdialog\"` values, unlike the older `AccessibilityRole`\n * enum `Toast`/`Banner` had to substitute `\"alert\"` into for the web's\n * `role=\"status\"`. `accessibilityViewIsModal` (iOS) is the closest match to\n * `aria-modal=\"true\"`, restricting VoiceOver to the dialog's subtree.\n *\n * There is no exit animation on either platform — the source CSS only\n * defines entrance keyframes, and `Modal`'s `visible={false}` unmounts\n * immediately, matching the web version returning `null` outright when\n * `!open`. Entrance is an `Animated.timing` fading + scaling + sliding the\n * card in (mirrors `@keyframes dialog-in`) alongside a plain backdrop fade\n * (`@keyframes backdrop-in`), replayed via a `useEffect` keyed on `open`\n * since — unlike `Toast`, which mounts once per instance — the same\n * `Dialog` element toggles `open` repeatedly while `Modal` itself\n * mounts/unmounts internally. `useReducedMotion()` skips straight to the\n * settled state.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Dialog.html\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.AlertDialog.html\n */\nexport const Dialog = ({\n open,\n title,\n children,\n buttons = [],\n onClose,\n closeOnBackdrop = true,\n role = 'dialog',\n responses,\n onResponse,\n style,\n testID,\n}: DialogProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const reducedMotion = useReducedMotion();\n const progress = useRef(new Animated.Value(reducedMotion ? 1 : 0)).current;\n\n const isAlert = !!responses;\n\n const dismissAlert = () => {\n const cancel = responses?.find((r) => r.variant !== 'destructive' && !r.disabled);\n\n if (cancel) {\n onResponse?.(cancel.id);\n }\n };\n\n const handleEscape = () => {\n if (isAlert) {\n dismissAlert();\n } else {\n onClose?.();\n }\n };\n\n const handleBackdropPress = isAlert ? dismissAlert : closeOnBackdrop ? onClose : undefined;\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n progress.setValue(0);\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationNormal,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: true,\n }).start();\n }, [open, reducedMotion, progress, theme.durationNormal, theme.easingDefault]);\n\n // Only the topmost dialog reacts to the Android back button, mirroring\n // the web version's \"topmost modal only\" Escape behavior for stacked\n // dialogs.\n useEffect(() => {\n if (!open) {\n return;\n }\n\n const subscription = BackHandler.addEventListener('hardwareBackPress', () => {\n handleEscape();\n\n return true;\n });\n\n return () => subscription.remove();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, isAlert, responses, onResponse, onClose]);\n\n const renderButtons = () => {\n const rows = isAlert && responses ? responses : buttons;\n\n if (rows.length === 0) {\n return null;\n }\n\n return (\n <View style={{ borderTopWidth: 1, borderTopColor: theme.cardShadeColor }}>\n {rows.map((row, index) => {\n const variant = row.variant ?? 'default';\n const color = variant === 'destructive' ? theme.destructiveColor : theme.accentColor;\n const onPress = isAlert\n ? () => onResponse?.((row as AlertDialogResponse).id)\n : (row as DialogButton).onPress;\n\n return (\n <Pressable\n key={isAlert ? (row as AlertDialogResponse).id : row.label}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled: row.disabled }}\n disabled={row.disabled}\n onPress={onPress}\n style={({ pressed }) => ({\n paddingVertical: 14,\n paddingHorizontal: theme.space4,\n borderBottomWidth: index < rows.length - 1 ? 1 : 0,\n borderBottomColor: theme.cardShadeColor,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n opacity: row.disabled ? theme.opacityDisabled : 1,\n })}\n >\n <Text\n variant=\"body\"\n style={{\n textAlign: 'center',\n color,\n fontWeight:\n variant === 'suggested'\n ? (String(theme.fontWeightSemibold) as TextStyle['fontWeight'])\n : undefined,\n }}\n >\n {row.label}\n </Text>\n </Pressable>\n );\n })}\n </View>\n );\n };\n\n const shadow =\n scheme === 'dark'\n ? { shadowOpacity: 0.5, shadowRadius: 32, elevation: 12 }\n : { shadowOpacity: 0.25, shadowRadius: 32, elevation: 8 };\n\n return (\n <Modal\n visible={open}\n transparent\n animationType=\"none\"\n statusBarTranslucent\n onRequestClose={handleEscape}\n >\n <AnimatedPressable\n testID={testID}\n onPress={handleBackdropPress}\n style={{\n flex: 1,\n alignItems: 'center',\n justifyContent: 'center',\n paddingHorizontal: 24,\n backgroundColor: theme.dialogBackdropColor,\n opacity: progress,\n }}\n >\n <Pressable onPress={() => {}} accessible={false} style={{ width: '100%', maxWidth: 480 }}>\n <Animated.View\n accessible\n role={role}\n accessibilityViewIsModal\n style={[\n {\n backgroundColor: theme.dialogBgColor,\n borderRadius: theme.radiusXl,\n overflow: 'hidden',\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 8 },\n ...shadow,\n opacity: progress,\n transform: [\n {\n translateY: progress.interpolate({ inputRange: [0, 1], outputRange: [-8, 0] }),\n },\n { scale: progress.interpolate({ inputRange: [0, 1], outputRange: [0.92, 1] }) },\n ],\n },\n style,\n ]}\n >\n {title && (\n <Text\n variant=\"title-4\"\n style={{\n padding: theme.space4,\n paddingBottom: children ? 0 : theme.space4,\n textAlign: 'center',\n fontWeight: String(theme.fontWeightBold) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n )}\n\n {children && (\n <View style={{ paddingVertical: theme.space2, paddingHorizontal: theme.space4 }}>\n {typeof children === 'string' ? (\n <Text variant=\"body\" style={{ textAlign: 'center', opacity: theme.opacityDim }}>\n {children}\n </Text>\n ) : (\n children\n )}\n </View>\n )}\n\n {renderButtons()}\n </Animated.View>\n </Pressable>\n </AnimatedPressable>\n </Modal>\n );\n};\n","import type { ReactNode } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { View } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface DividerProps {\n /** Optional centered label (e.g. `\"OR\"`, `\"Continue with\"`). */\n children?: ReactNode;\n /** Accessible name. Defaults to `children` when it's a string. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Horizontal rule with an optional centered label — common auth/login-form\n * pattern (\"Sign in\" / **OR** / \"Continue with Google\"). Mirrors\n * `@gnome-ui/react`'s `Divider`.\n *\n * For a bare dividing line with no label, use `Separator` instead — it also\n * supports a vertical orientation, which `Divider` does not.\n *\n * `role=\"separator\"` ports 1:1 from RN's newer web-aligned `Role` union\n * (the same one `Avatar`/`Badge`/`LevelBar` already reach for), unlike\n * `Separator`'s own choice to render as `accessible={false}` — that\n * component is purely decorative with nothing for a screen reader to\n * announce, while a labelled `Divider` (\"OR\") is exactly the kind of\n * content a screen reader user needs read aloud, so it stays in the\n * accessibility tree instead. The label reuses `Text`'s `variant=\"caption\"\n * color=\"dim\"` verbatim rather than hand-rolled styles, since that\n * combination already resolves to the same font-size/weight/dim-opacity\n * the web version's `.label` class hard-codes.\n *\n * The web version's `aria-orientation=\"horizontal\"` has no port — `Divider`\n * has no `orientation` prop at all (unlike `Separator`), so there is only\n * ever one orientation to announce.\n *\n * @see https://developer.gnome.org/hig/patterns/containers.html\n */\nexport const Divider = ({ children, accessibilityLabel, style, testID }: DividerProps) => {\n const theme = useGnomeTheme();\n const hasLabel = Boolean(children);\n\n return (\n <View\n testID={testID}\n accessible\n role=\"separator\"\n accessibilityLabel={\n accessibilityLabel ?? (typeof children === 'string' ? children : undefined)\n }\n style={[\n { flexDirection: 'row', alignItems: 'center', width: '100%', gap: theme.space2 },\n style,\n ]}\n >\n <View style={{ flex: 1, height: 1, backgroundColor: theme.cardShadeColor }} />\n {hasLabel && (\n <>\n {typeof children === 'string' ? (\n <Text variant=\"caption\" color=\"dim\">\n {children}\n </Text>\n ) : (\n children\n )}\n <View style={{ flex: 1, height: 1, backgroundColor: theme.cardShadeColor }} />\n </>\n )}\n </View>\n );\n};\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport { Close } from '@gnome-ui/icons';\nimport { createContext, type ReactNode, useContext, useEffect, useRef } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Animated, BackHandler, Dimensions, Easing, Modal, Pressable, View } from 'react-native';\n\nimport { Button } from '@/components/Button';\nimport { Icon } from '@/components/Icon';\nimport { IconButton } from '@/components/IconButton';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\n\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable);\n\nexport type DrawerSide = 'left' | 'right';\nexport type DrawerSize = 'classic' | 'wide';\n\nexport interface DrawerRailItem {\n /** Stable unique identifier. */\n id: string;\n /** Icon shown for this rail entry. */\n icon: IconDefinition;\n /** Accessible name, also used as the tooltip. */\n label: string;\n /** Whether this entry represents the currently visible drawer/panel. */\n active?: boolean;\n disabled?: boolean;\n onPress: () => void;\n}\n\nexport interface DrawerProps {\n /** Whether the drawer is visible. */\n open: boolean;\n /** Edge that the drawer slides in from. Defaults to `\"right\"`. */\n side?: DrawerSide;\n /** Preset drawer width. Defaults to `\"classic\"`. */\n size?: DrawerSize;\n /** Optional drawer heading. */\n title?: ReactNode;\n /** Drawer content when a prop is preferred over `children`. */\n content?: ReactNode;\n /** Drawer content. Used when `content` is not provided. */\n children?: ReactNode;\n /** Called when the user dismisses the drawer with the Android back button or the backdrop. */\n onClose?: () => void;\n /** Whether pressing the backdrop closes the drawer. Defaults to `true`. */\n closeOnBackdrop?: boolean;\n /**\n * Narrow icon rail rendered on the drawer's inner edge (the edge facing\n * the backdrop), for switching between related drawers or panels without\n * closing the drawer. Purely presentational — pressing an entry only\n * calls its `onPress`; the caller decides what happens (swap `content`,\n * open a different drawer, etc).\n */\n rail?: DrawerRailItem[];\n style?: StyleProp<ViewStyle>;\n /** Forwarded to the backdrop — useful for testing. */\n testID?: string;\n}\n\nconst DrawerDepthContext = createContext(0);\n\nconst DRAWER_PRESET_WIDTH: Record<DrawerSize, number> = { classic: 420, wide: 640 };\nconst DRAWER_DEPTH_SCALE = 0.85;\nconst DRAWER_MIN_WIDTH = 240;\n\n/**\n * Slide-in panel for supplementary content, anchored to the left or right\n * edge. Mirrors `@gnome-ui/react`'s `Drawer`.\n *\n * Rebuilt with `View`/`Modal` rather than ported from the web version's\n * `createPortal(document.body)` + manual focus trap + Escape listener:\n * `Modal` already floats above everything with no portal target needed,\n * and `BackHandler`'s `hardwareBackPress` is the Android analog of the\n * Escape listener (the same `Dialog`/`BottomSheet` precedent). Focus\n * trapping has no port — no DOM `Tab`/`document.activeElement` concept in\n * RN's touch-first model.\n *\n * **Floats with a margin on every side, all four corners rounded** — the\n * backdrop `Pressable` carries `padding: theme.space3` (matching the\n * `@gnome-ui/react` source's own recent update to the same floating-card\n * look, not a divergence), and the drawer itself gets a uniform\n * `borderRadius` instead of the flat-edge-on-the-anchored-side look a\n * flush-to-the-screen-edge panel would need. Positioning within that\n * padded backdrop uses `justifyContent: 'flex-end'`/`'flex-start'` on the\n * backdrop (not the web CSS's `margin-left/right: auto` on the drawer\n * itself) — confirmed empirically (a throwaway build with saturated debug\n * colors standing in for the real theme colors, screenshotted on-device)\n * that `justifyContent` renders correctly while auto-margins were, at\n * best, unverified for this RN/Yoga version; `BottomSheet` already proves\n * the same `justifyContent: 'flex-end'` mechanism works on this exact\n * setup, just on the vertical axis instead of horizontal. On a phone-width\n * screen the `classic`/`wide` presets (420/640, sized for wider viewports)\n * get capped to fill essentially the entire available width after the\n * margin either way, so the anchored side becomes visually obvious mainly\n * on tablets — the same responsive behavior the web version would show at\n * an equally narrow browser width, not an RN-specific gap.\n *\n * **No drag-to-dismiss, unlike `BottomSheet`**: the web source only\n * defines entrance keyframes for both the backdrop and the panel, so this\n * follows `Dialog`'s simpler shape (a single `progress` `Animated.Value`\n * replayed via `useEffect` keyed on `open`, no separate exit animation or\n * lagging `visible` state) rather than `BottomSheet`'s\n * `PanResponder`-plus-timed-exit machinery.\n *\n * **The slide distance needs no `onLayout` measurement**, unlike\n * `BottomSheet`'s content-driven height: the drawer's width is a value\n * this component already computes in JS (`size`'s preset, scaled down by\n * `DrawerDepthContext` depth, capped by the available space after the\n * backdrop's margin) — RN's `transform` has no percentage-of-self units\n * (the same `BottomSheet`/`Avatar`/`Slider` pitfall), but since the exact\n * pixel width is already known synchronously, `translateX` can animate\n * from that known offset to `0` immediately, with no first-frame\n * imprecision to accept.\n *\n * **`DrawerDepthContext` (nested-drawer width auto-scaling) ports 1:1** —\n * pure React Context state, no DOM dependency at all. A `Drawer` opened\n * from within another drawer's `content`/`children` is detected via\n * context and scales its own preset width down (`0.85^depth`, floored at\n * `DRAWER_MIN_WIDTH`) so stacked drawers read as a drill-in hierarchy\n * instead of identical overlapping panels.\n *\n * **`rail` reuses the newly-added `IconButton`** (itself just `Button` +\n * `Icon` + optional `Tooltip`, the same composition `@gnome-ui/react`'s own\n * `IconButton` already is) — `aria-pressed` becomes\n * `accessibilityState={{ selected: item.active }}`, the closest RN\n * equivalent for a toggleable icon button with no dedicated visual\n * \"pressed\" style on either platform's source.\n *\n * `backdrop-filter: blur(4px)` has no port — no native blur view\n * dependency exists in this package, the same gap already dropped from\n * `Sidebar`'s blurred `variant`/`BottomSheet`'s backdrop. `role=\"dialog\"` +\n * `accessibilityViewIsModal` port 1:1 from `Dialog`'s own precedent.\n *\n * @see https://developer.gnome.org/hig/patterns/containers.html\n */\nexport const Drawer = ({\n open,\n side = 'right',\n size = 'classic',\n title,\n content,\n children,\n onClose,\n closeOnBackdrop = true,\n rail,\n style,\n testID,\n}: DrawerProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const reducedMotion = useReducedMotion();\n const depth = useContext(DrawerDepthContext);\n\n const body = content !== undefined ? content : children;\n\n const presetWidth = DRAWER_PRESET_WIDTH[size];\n const scaledWidth =\n depth > 0\n ? Math.max(DRAWER_MIN_WIDTH, Math.round(presetWidth * DRAWER_DEPTH_SCALE ** depth))\n : presetWidth;\n const maxAvailableWidth = Dimensions.get('window').width - theme.space3 * 2;\n const drawerWidth = Math.min(scaledWidth, maxAvailableWidth);\n\n const progress = useRef(new Animated.Value(reducedMotion ? 1 : 0)).current;\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n progress.setValue(0);\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationNormal,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: true,\n }).start();\n }, [open, reducedMotion, progress, theme.durationNormal, theme.easingDefault]);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n const subscription = BackHandler.addEventListener('hardwareBackPress', () => {\n onClose?.();\n\n return true;\n });\n\n return () => subscription.remove();\n }, [open, onClose]);\n\n const offset = side === 'left' ? -drawerWidth : drawerWidth;\n const translateX = progress.interpolate({ inputRange: [0, 1], outputRange: [offset, 0] });\n\n const shadow =\n scheme === 'dark'\n ? { shadowOpacity: 0.45, shadowRadius: 24, elevation: 12 }\n : { shadowOpacity: 0.18, shadowRadius: 24, elevation: 8 };\n\n const rail_ =\n rail && rail.length > 0 ? (\n <View\n style={{\n width: 56,\n flexShrink: 0,\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: theme.space3,\n [side === 'right' ? 'borderRightWidth' : 'borderLeftWidth']: 1,\n borderColor: theme.cardShadeColor,\n }}\n >\n {rail.map((item) => (\n <IconButton\n key={item.id}\n icon={item.icon}\n label={item.label}\n tooltip={item.label}\n tooltipPlacement={side === 'right' ? 'left' : 'right'}\n variant=\"flat\"\n size=\"sm\"\n disabled={item.disabled}\n accessibilityState={{ selected: !!item.active, disabled: !!item.disabled }}\n onPress={item.onPress}\n />\n ))}\n </View>\n ) : null;\n\n return (\n <Modal\n visible={open}\n transparent\n animationType=\"none\"\n statusBarTranslucent\n onRequestClose={onClose}\n >\n <AnimatedPressable\n testID={testID}\n onPress={closeOnBackdrop ? onClose : undefined}\n accessible={false}\n style={{\n flex: 1,\n flexDirection: 'row',\n justifyContent: side === 'left' ? 'flex-start' : 'flex-end',\n padding: theme.space3,\n backgroundColor: theme.dialogBackdropColor,\n opacity: progress,\n }}\n >\n <Pressable onPress={() => {}} accessible={false} style={{ width: drawerWidth }}>\n <Animated.View\n accessible\n role=\"dialog\"\n accessibilityViewIsModal\n style={[\n {\n flex: 1,\n flexDirection: 'row',\n overflow: 'hidden',\n borderRadius: theme.radiusXl,\n backgroundColor: theme.dialogBgColor,\n shadowColor: '#000',\n shadowOffset: { width: side === 'left' ? 4 : -4, height: 0 },\n ...shadow,\n transform: [{ translateX }],\n },\n style,\n ]}\n >\n {side === 'right' && rail_}\n\n <View style={{ flex: 1, minWidth: 0 }}>\n {title && (\n <View\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n padding: theme.space4,\n paddingBottom: theme.space2,\n }}\n >\n <Text\n variant=\"title-4\"\n style={{\n flex: 1,\n fontWeight: String(theme.fontWeightBold) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n {onClose && (\n <Button\n variant=\"flat\"\n shape=\"circular\"\n size=\"sm\"\n accessibilityLabel=\"Close\"\n onPress={onClose}\n >\n <Icon icon={Close} size=\"sm\" />\n </Button>\n )}\n </View>\n )}\n\n {body !== undefined && (\n <View\n style={{\n flex: 1,\n padding: theme.space4,\n paddingTop: title ? 0 : theme.space4,\n }}\n >\n <DrawerDepthContext.Provider value={depth + 1}>\n {typeof body === 'string' ? <Text variant=\"body\">{body}</Text> : body}\n </DrawerDepthContext.Provider>\n </View>\n )}\n </View>\n\n {side === 'left' && rail_}\n </Animated.View>\n </Pressable>\n </AnimatedPressable>\n </Modal>\n );\n};\n","import { forwardRef, type ReactNode, useEffect, useRef, useState } from 'react';\nimport type {\n LayoutChangeEvent,\n TextInput as RNTextInput,\n StyleProp,\n TextInputProps,\n ViewStyle,\n} from 'react-native';\nimport { Animated, Pressable, View as RNView, TextInput } from 'react-native';\n\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport interface EntryRowProps\n extends Omit<TextInputProps, 'style' | 'value' | 'defaultValue' | 'editable' | 'testID'> {\n /**\n * Acts as a floating label: shown small above the input once the field has\n * content or focus, and as the placeholder while it's empty and unfocused.\n */\n title: string;\n /** Controlled value. Omit for an uncontrolled field. */\n value?: string;\n /** Initial value when uncontrolled. */\n defaultValue?: string;\n /** Called when the input value changes. */\n onValueChange?: (value: string) => void;\n /** Icon or widget placed at the leading edge. */\n leading?: ReactNode;\n /** Icon or widget placed at the trailing edge (e.g. a clear or reveal button). */\n trailing?: ReactNode;\n disabled?: boolean;\n /**\n * Applied to the row, not the input — the convention every other component\n * in this package follows. Reach the field itself with the accessible name\n * (`getByLabelText(title)`).\n */\n testID?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/** `.labelFloated { top: 6px }`. */\nconst FLOATED_TOP = 6;\n\n/**\n * Row with an inline text entry field, mirroring `AdwEntryRow` and\n * `@gnome-ui/react`'s own `EntryRow`. The `title` rises above the input as a\n * small label once the field is focused or has content, and stands in for\n * the placeholder until then. Use inside a `BoxedList` for settings that\n * take free-form text.\n *\n * The float is one JS-driven `Animated.Value` (`useNativeDriver: false`):\n * `fontSize` is part of the transition and can't be native-driven, and\n * mixing a native with a JS value on one component throws — the same\n * trade-off `Expander` and `InlineViewSwitcher` already accepted.\n * `useReducedMotion()` snaps between the two states instead.\n *\n * **The label's travel is measured, not hardcoded.** The web can express its\n * resting position as `top: 50%; transform: translateY(-50%)` and its\n * floated one as `top: 6px`, but RN can't interpolate between a percentage\n * and a fixed offset, so the row reports its own height through `onLayout`\n * and the distance is derived from it. That also keeps the label centred if\n * a consumer makes the row taller than the 56 dp minimum.\n *\n * The `:focus` inset ring is dropped rather than approximated. `TextField`'s\n * own precedent — recolor the border on focus — doesn't transfer, because an\n * `EntryRow` has no border of its own to recolor: it's a row inside a\n * `BoxedList`, and adding one would shift the list's geometry. On a touch\n * device the state is already unmistakable anyway: the label floats up, the\n * text fades in, and the keyboard opens. The `prefers-contrast: more` block\n * is a variation on that same ring, so it goes with it.\n *\n * Tapping anywhere on the row focuses the field, the same affordance the web\n * version's row-level `onClick` provides.\n *\n * The visible label is hidden from assistive tech and the `title` becomes\n * the input's `accessibilityLabel` instead. On the web the two are bound by\n * `<label htmlFor>`, which RN has no equivalent for — left as-is, the label\n * would be announced as loose text next to an unnamed field. `testID` lands\n * on the row rather than the input (unlike the web version, which spreads\n * every remaining prop onto the `<input>`), matching what every other\n * component in this package does; reach the field itself by its accessible\n * name.\n *\n * @example\n * const [name, setName] = useState('');\n *\n * <BoxedList>\n * <EntryRow title=\"Display name\" value={name} onValueChange={setName} />\n * </BoxedList>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.EntryRow.html\n */\nexport const EntryRow = forwardRef<RNTextInput, EntryRowProps>(function EntryRow(\n {\n title,\n value: controlledValue,\n defaultValue = '',\n onValueChange,\n onChangeText,\n leading,\n trailing,\n disabled = false,\n onFocus,\n onBlur,\n accessibilityLabel,\n testID,\n style,\n ...inputProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const isControlled = controlledValue !== undefined;\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const inputValue = isControlled ? controlledValue : uncontrolledValue;\n\n const [focused, setFocused] = useState(false);\n const [fieldHeight, setFieldHeight] = useState(56);\n const inputRef = useRef<RNTextInput | null>(null);\n\n const floated = focused || inputValue.length > 0;\n const float = useRef(new Animated.Value(floated ? 1 : 0)).current;\n\n useEffect(() => {\n if (reducedMotion) {\n float.setValue(floated ? 1 : 0);\n return;\n }\n\n const animation = Animated.timing(float, {\n toValue: floated ? 1 : 0,\n duration: theme.durationFast,\n useNativeDriver: false,\n });\n\n animation.start();\n\n return () => animation.stop();\n }, [floated, reducedMotion, float, theme.durationFast]);\n\n const restingLineHeight = Math.round(theme.fontSizeBody * theme.lineHeightBody);\n const floatedLineHeight = Math.round(theme.fontSizeCaption * theme.lineHeightBody);\n // Centred resting position → `FLOATED_TOP`, both expressed as the label's\n // own top edge so the travel is a single translateY.\n const restingTop = (fieldHeight - restingLineHeight) / 2;\n\n return (\n <Pressable\n accessible={false}\n testID={testID}\n disabled={disabled}\n onPress={() => inputRef.current?.focus()}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingHorizontal: theme.space4,\n minHeight: 56,\n width: '100%',\n opacity: disabled ? theme.opacityDisabled : 1,\n },\n style,\n ]}\n >\n {leading ? <RNView style={{ flexShrink: 0 }}>{leading}</RNView> : null}\n\n <RNView\n onLayout={(event: LayoutChangeEvent) => setFieldHeight(event.nativeEvent.layout.height)}\n style={{ flex: 1, justifyContent: 'center', minHeight: 56 }}\n >\n <Animated.Text\n pointerEvents=\"none\"\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n numberOfLines={1}\n style={{\n position: 'absolute',\n left: 0,\n right: 0,\n top: restingTop,\n fontFamily: theme.fontFamily,\n color: theme.windowFgColor,\n fontSize: float.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.fontSizeBody, theme.fontSizeCaption],\n }),\n lineHeight: float.interpolate({\n inputRange: [0, 1],\n outputRange: [restingLineHeight, floatedLineHeight],\n }),\n opacity: float.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.opacityDim, 0.7],\n }),\n transform: [\n {\n translateY: float.interpolate({\n inputRange: [0, 1],\n outputRange: [0, FLOATED_TOP - restingTop],\n }),\n },\n ],\n }}\n >\n {title}\n </Animated.Text>\n\n <Animated.View style={{ opacity: float }}>\n <TextInput\n ref={(node) => {\n inputRef.current = node;\n\n if (typeof ref === 'function') {\n ref(node);\n } else if (ref) {\n ref.current = node;\n }\n }}\n value={inputValue}\n editable={!disabled}\n accessibilityLabel={accessibilityLabel ?? title}\n onFocus={(event) => {\n setFocused(true);\n onFocus?.(event);\n }}\n onBlur={(event) => {\n setFocused(false);\n onBlur?.(event);\n }}\n onChangeText={(next) => {\n if (!isControlled) {\n setUncontrolledValue(next);\n }\n\n onValueChange?.(next);\n onChangeText?.(next);\n }}\n style={{\n padding: 0,\n margin: 0,\n width: '100%',\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n lineHeight: restingLineHeight,\n color: theme.cardFgColor,\n paddingTop: theme.space4,\n paddingBottom: 4,\n }}\n {...inputProps}\n />\n </Animated.View>\n </RNView>\n\n {trailing ? (\n <RNView style={{ flexDirection: 'row', alignItems: 'center', gap: theme.space1 }}>\n {trailing}\n </RNView>\n ) : null}\n </Pressable>\n );\n});\n","import { PanEnd } from '@gnome-ui/icons';\nimport { type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable, View } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport interface ExpanderProps {\n /** Clickable header label. String children render as a themed `Text` label; other nodes render as-is. */\n label: ReactNode;\n /** Content revealed when expanded. */\n children?: ReactNode;\n /** Controlled expanded state. */\n expanded?: boolean;\n /** Initial expanded state when uncontrolled. Defaults to `false`. */\n defaultExpanded?: boolean;\n /** Called when the expanded state changes. */\n onExpandedChange?: (expanded: boolean) => void;\n /** Disables the toggle. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Standalone disclosure triangle + collapsible content — mirrors\n * `GtkExpander` and `@gnome-ui/react`'s `Expander`.\n *\n * A bare, unstyled counterpart to `ExpanderRow`. Use `Expander` outside a\n * settings-row context, e.g. \"Show advanced options\" in a form, or \"Show\n * details\" under an error message.\n *\n * The web version clips the panel with a CSS grid-height animation and\n * rides the content's `padding-top` on a second, separate transition (so a\n * collapsed expander doesn't reserve blank space for padding that belongs\n * to hidden content). RN has no CSS grid to lean on, so the panel is a\n * single `Animated.View` whose numeric `height` is driven directly —\n * `useNativeDriver: false`, the same accepted (and documented) trade-off\n * `Checkbox`/`RadioButton`/`Switch`/`AnimatedIcon` already make for\n * non-transform properties. Because the inner content `View`'s `onLayout`\n * measurement already includes its own `paddingTop`, animating that single\n * height value reproduces the web version's two-transition result with one\n * `Animated.Value` instead of two. Content stays mounted while collapsed\n * (matching the web version's `inert`, unmounted-from-the-tab-order-only\n * behavior) — `accessibilityElementsHidden` / `importantForAccessibility`\n * is RN's closest equivalent, applied to the panel wrapper.\n *\n * On first mount with `defaultExpanded`, the panel briefly renders at its\n * natural (unmeasured) height instead of the animated value, so the initial\n * reveal doesn't start from a wrong, pre-measurement `0` and pop once\n * `onLayout` resolves — the same \"let layout report the truth once, then\n * hand control to `Animated`\" shape `Dropdown`/`Tooltip` use for their own\n * `onLayout`-measured dimensions.\n *\n * The chevron is `PanEnd` (GNOME's own `pan-end-symbolic` disclosure\n * triangle) rather than a chevron glyph, matching the web version's\n * hand-drawn arrow shape. It rotates 0deg → 90deg on an `Animated.Value`\n * (the same `interpolate`-to-`rotate` recipe `Spinner` uses for its own\n * indeterminate spin), unlike `Dropdown`'s chevron, which snaps instantly\n * since that panel's own reveal has no comparable \"settle\" affordance.\n *\n * `role=\"region\"` on the panel and `accessibilityState={{ expanded }}` on\n * the header port 1:1 from the web version's `aria-controls`/`aria-expanded`\n * pair — RN has no DOM ids, so the id-based `aria-labelledby`/`aria-controls`\n * relationship itself has no port, the same `ProgressBar`/`LevelBar`\n * precedent for dropping id-relationship-only ARIA props.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Expander.html\n */\nexport const Expander = ({\n label,\n children,\n expanded: controlledExpanded,\n defaultExpanded = false,\n onExpandedChange,\n disabled = false,\n style,\n testID,\n}: ExpanderProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const isControlled = controlledExpanded !== undefined;\n const [uncontrolledExpanded, setUncontrolledExpanded] = useState(defaultExpanded);\n const expanded = isControlled ? controlledExpanded : uncontrolledExpanded;\n\n const [contentHeight, setContentHeight] = useState(0);\n const hasMeasured = useRef(false);\n const heightAnim = useRef(new Animated.Value(0)).current;\n const chevronAnim = useRef(new Animated.Value(defaultExpanded ? 1 : 0)).current;\n\n const toggle = () => {\n if (disabled) {\n return;\n }\n\n const next = !expanded;\n\n if (!isControlled) {\n setUncontrolledExpanded(next);\n }\n\n onExpandedChange?.(next);\n };\n\n const handleContentLayout = (e: LayoutChangeEvent) => {\n const h = e.nativeEvent.layout.height;\n\n if (h === contentHeight) {\n hasMeasured.current = true;\n\n return;\n }\n\n if (expanded && !hasMeasured.current) {\n heightAnim.setValue(h);\n }\n\n hasMeasured.current = true;\n setContentHeight(h);\n };\n\n useEffect(() => {\n const [x1, y1, x2, y2] = theme.easingDefault;\n const easing = Easing.bezier(x1, y1, x2, y2);\n const target = expanded ? contentHeight : 0;\n\n if (reducedMotion) {\n heightAnim.setValue(target);\n chevronAnim.setValue(expanded ? 1 : 0);\n\n return;\n }\n\n Animated.parallel([\n Animated.timing(heightAnim, {\n toValue: target,\n duration: theme.durationNormal,\n easing,\n useNativeDriver: false,\n }),\n Animated.timing(chevronAnim, {\n toValue: expanded ? 1 : 0,\n duration: theme.durationFast,\n easing,\n useNativeDriver: true,\n }),\n ]).start();\n }, [\n expanded,\n contentHeight,\n reducedMotion,\n heightAnim,\n chevronAnim,\n theme.durationNormal,\n theme.durationFast,\n theme.easingDefault,\n ]);\n\n const showsNaturalHeight = expanded && !hasMeasured.current;\n const rotate = chevronAnim.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '90deg'] });\n\n return (\n <View testID={testID} style={[{ width: '100%' }, style]}>\n <Pressable\n role=\"button\"\n disabled={disabled}\n accessibilityState={{ expanded, disabled }}\n onPress={toggle}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n alignSelf: 'flex-start',\n gap: theme.space2,\n padding: theme.space1,\n margin: -theme.space1,\n borderRadius: theme.radiusSm,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n opacity: disabled ? theme.opacityDisabled : 1,\n })}\n >\n <Animated.View style={{ opacity: 0.55, transform: [{ rotate }] }}>\n <Icon icon={PanEnd} size=\"md\" />\n </Animated.View>\n\n {typeof label === 'string' ? (\n <Text style={{ color: theme.windowFgColor }}>{label}</Text>\n ) : (\n label\n )}\n </Pressable>\n\n <Animated.View\n style={{ height: showsNaturalHeight ? undefined : heightAnim, overflow: 'hidden' }}\n accessibilityElementsHidden={!expanded}\n importantForAccessibility={expanded ? 'yes' : 'no'}\n >\n <View\n accessible\n role=\"region\"\n onLayout={handleContentLayout}\n style={{ paddingTop: theme.space2 }}\n >\n {children}\n </View>\n </Animated.View>\n </View>\n );\n};\n","import { PanDown } from '@gnome-ui/icons';\nimport { Children, forwardRef, type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, TextStyle, View, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable, View as RNView } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { Separator } from '@/components/Separator';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport interface ExpanderRowProps {\n /** Primary label. */\n title: string;\n /** Secondary line below the title. */\n subtitle?: string;\n /** Icon or image placed at the leading edge of the header row. */\n leading?: ReactNode;\n /**\n * Widget placed at the trailing edge of the header row, before the\n * chevron (e.g. a value label or a `Switch`). Stop event propagation\n * inside it so the row's toggle isn't triggered.\n */\n trailing?: ReactNode;\n /**\n * Nested rows revealed when expanded. Use `ActionRow`, `ButtonRow`, or\n * any row-shaped element — separators are inserted automatically.\n */\n children?: ReactNode;\n /** Controlled expanded state. */\n expanded?: boolean;\n /** Initial expanded state when uncontrolled. Defaults to `false`. */\n defaultExpanded?: boolean;\n /** Called when the expanded state changes. */\n onExpandedChange?: (expanded: boolean) => void;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Collapsible `ActionRow` that reveals nested rows on activation, mirroring\n * `@gnome-ui/react`'s `ExpanderRow`. The header row toggles a smooth reveal\n * animation exposing child rows. Supports both controlled (`expanded`) and\n * uncontrolled (`defaultExpanded`) modes.\n *\n * The reveal panel reuses the standalone `Expander`'s exact recipe — a\n * single `Animated.View` with a directly-driven numeric `height` (RN has no\n * CSS grid to lean on for the web version's `grid-template-rows: 0fr → 1fr`\n * trick), content staying mounted while collapsed\n * (`accessibilityElementsHidden`/`importantForAccessibility` standing in\n * for the web's `inert`), and the \"show natural height until first\n * `onLayout` measurement lands, then hand control to `Animated`\" guard for\n * a `defaultExpanded` initial mount. The only difference from `Expander`\n * itself: the chevron is `PanDown` (a straight down-arrow, matching the\n * source CSS's hand-drawn `M4 6l4 4 4-4` path and rotating 0deg → 180deg)\n * rather than `Expander`'s `PanEnd` triangle rotating 0deg → 90deg — the\n * same icon/rotation pair `Dropdown`'s own chevron already established.\n *\n * Nested children get a `Separator` inserted before each one (including\n * the first, directly under the header) via `Children.toArray(children)\n * .filter(Boolean)`, the same falsy-child-filtering `ExpanderRow`'s web\n * version already does. Unlike `CheckRow`/`ButtonRow`, this component has\n * no `disabled` prop — the source `@gnome-ui/react` version doesn't expose\n * one either, so none is added here.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ExpanderRow.html\n */\nexport const ExpanderRow = forwardRef<View, ExpanderRowProps>(function ExpanderRow(\n {\n title,\n subtitle,\n leading,\n trailing,\n children,\n expanded: controlledExpanded,\n defaultExpanded = false,\n onExpandedChange,\n style,\n testID,\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const isControlled = controlledExpanded !== undefined;\n const [uncontrolledExpanded, setUncontrolledExpanded] = useState(defaultExpanded);\n const expanded = isControlled ? controlledExpanded : uncontrolledExpanded;\n\n const [contentHeight, setContentHeight] = useState(0);\n const hasMeasured = useRef(false);\n const heightAnim = useRef(new Animated.Value(0)).current;\n const chevronAnim = useRef(new Animated.Value(defaultExpanded ? 1 : 0)).current;\n\n const toggle = () => {\n const next = !expanded;\n\n if (!isControlled) {\n setUncontrolledExpanded(next);\n }\n\n onExpandedChange?.(next);\n };\n\n const handleContentLayout = (e: LayoutChangeEvent) => {\n const h = e.nativeEvent.layout.height;\n\n if (h === contentHeight) {\n hasMeasured.current = true;\n\n return;\n }\n\n if (expanded && !hasMeasured.current) {\n heightAnim.setValue(h);\n }\n\n hasMeasured.current = true;\n setContentHeight(h);\n };\n\n useEffect(() => {\n const [x1, y1, x2, y2] = theme.easingDefault;\n const easing = Easing.bezier(x1, y1, x2, y2);\n const target = expanded ? contentHeight : 0;\n\n if (reducedMotion) {\n heightAnim.setValue(target);\n chevronAnim.setValue(expanded ? 1 : 0);\n\n return;\n }\n\n Animated.parallel([\n Animated.timing(heightAnim, {\n toValue: target,\n duration: theme.durationNormal,\n easing,\n useNativeDriver: false,\n }),\n Animated.timing(chevronAnim, {\n toValue: expanded ? 1 : 0,\n duration: theme.durationFast,\n easing,\n useNativeDriver: true,\n }),\n ]).start();\n }, [\n expanded,\n contentHeight,\n reducedMotion,\n heightAnim,\n chevronAnim,\n theme.durationNormal,\n theme.durationFast,\n theme.easingDefault,\n ]);\n\n const showsNaturalHeight = expanded && !hasMeasured.current;\n const rotate = chevronAnim.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '180deg'] });\n const childItems = Children.toArray(children).filter(Boolean);\n\n return (\n <RNView ref={ref} testID={testID} style={[{ width: '100%' }, style]}>\n <Pressable\n role=\"button\"\n accessibilityState={{ expanded }}\n onPress={toggle}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: theme.space2,\n paddingHorizontal: theme.space4,\n minHeight: 52,\n width: '100%',\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n })}\n >\n {leading && <RNView style={{ flexShrink: 0 }}>{leading}</RNView>}\n\n <RNView style={{ flex: 1, gap: 2 }}>\n <Text\n variant=\"body\"\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n color: theme.cardFgColor,\n fontWeight: String(theme.fontWeightNormal) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n {subtitle && (\n <Text variant=\"caption\" color=\"dim\" numberOfLines={1} ellipsizeMode=\"tail\">\n {subtitle}\n </Text>\n )}\n </RNView>\n\n {trailing && (\n <RNView\n style={{ flexShrink: 0, flexDirection: 'row', alignItems: 'center', gap: theme.space1 }}\n >\n {trailing}\n </RNView>\n )}\n\n <Animated.View style={{ opacity: 0.55, transform: [{ rotate }] }}>\n <Icon icon={PanDown} size=\"md\" />\n </Animated.View>\n </Pressable>\n\n <Animated.View\n style={{ height: showsNaturalHeight ? undefined : heightAnim, overflow: 'hidden' }}\n accessibilityElementsHidden={!expanded}\n importantForAccessibility={expanded ? 'yes' : 'no'}\n >\n <RNView accessible role=\"region\" onLayout={handleContentLayout}>\n {childItems.map((child, i) => (\n <RNView key={i}>\n <Separator testID=\"expander-row-separator\" />\n {child}\n </RNView>\n ))}\n </RNView>\n </Animated.View>\n </RNView>\n );\n});\n","import type { ReactNode } from 'react';\nimport type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native';\nimport { View } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface FieldGroupProps extends Omit<ViewProps, 'style'> {\n /** Group heading, rendered above the grouped content. */\n label: string;\n /** Helper text shown below the label. Hidden when `error` is set. */\n helperText?: string;\n /**\n * Error message shown below the label in place of `helperText`.\n * Announced via `role=\"alert\"` when it appears.\n */\n error?: string;\n /**\n * Visually dims the group. Unlike the web version's native\n * `<fieldset disabled>`, this does not automatically disable descendant\n * controls — RN has no equivalent of a fieldset's built-in\n * disabled-propagates-to-descendants behavior, so each child control\n * must still be disabled individually.\n */\n disabled?: boolean;\n /** Arbitrary field content — checkboxes, radios, a custom composite field, etc. */\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Generic form-field grouping with a shared label, help text, and error\n * message, for arbitrary fields outside a `BoxedList`. Mirrors\n * `@gnome-ui/react`'s `FieldGroup`.\n *\n * `PreferencesGroup` is scoped specifically to wrapping settings rows\n * inside a `BoxedList` — use `FieldGroup` for a plain labeled grouping\n * around any set of related form controls (e.g. a `RadioButton` group or\n * several `Checkbox`es sharing one label and error), independent of the\n * settings-page layout.\n *\n * `<fieldset>`/`<legend>` have no RN element equivalent — this renders as\n * a plain `View` with `role=\"group\"` (the closest match to a fieldset's\n * implicit ARIA role; RN's `Role` union has it natively, no substitution\n * needed) and a themed `Text` label in place of the `<legend>`. The web\n * version's `aria-describedby` (an id-relationship prop) has no RN\n * equivalent and is dropped, the same \"no relationship attribute\" gap\n * `ProgressBar`/`LevelBar`/`Expander` already established. The hint/error\n * text reuses `TextField`'s exact recipe verbatim — `error ?? helperText`\n * picks the message, `error ? theme.errorColor : theme.windowFgColor` plus\n * `error ? 1 : theme.opacityDim` picks the color/opacity — rather than\n * re-deriving the same dim-vs-error styling logic independently.\n * `disabled` only dims the group visually (`theme.opacityDisabled`); see\n * the prop doc for why it can't also disable descendants automatically.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/\n */\nexport const FieldGroup = ({\n label,\n helperText,\n error,\n disabled = false,\n children,\n style,\n ...viewProps\n}: FieldGroupProps) => {\n const theme = useGnomeTheme();\n const hint = error ?? helperText;\n\n return (\n <View\n role=\"group\"\n style={[{ gap: theme.space1, opacity: disabled ? theme.opacityDisabled : 1 }, style]}\n {...viewProps}\n >\n <Text\n variant=\"body\"\n style={{\n color: theme.windowFgColor,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n }}\n >\n {label}\n </Text>\n\n {hint && (\n <Text\n role={error ? 'alert' : undefined}\n variant=\"caption\"\n style={{\n color: error ? theme.errorColor : theme.windowFgColor,\n opacity: error ? 1 : theme.opacityDim,\n }}\n >\n {hint}\n </Text>\n )}\n\n <View style={{ gap: theme.space2 }}>{children}</View>\n </View>\n );\n};\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport {\n ApplicationXExecutable,\n AudioXGeneric,\n Document,\n Folder,\n FontXGeneric,\n ImageXGeneric,\n PackageXGeneric,\n TextXGeneric,\n VideoXGeneric,\n XOfficeDocument,\n XOfficePresentation,\n XOfficeSpreadsheet,\n} from '@gnome-ui/icons';\n\nexport type FileTypeCategory =\n | 'folder'\n | 'image'\n | 'audio'\n | 'video'\n | 'text'\n | 'pdf'\n | 'archive'\n | 'document'\n | 'spreadsheet'\n | 'presentation'\n | 'font'\n | 'executable'\n | 'unknown';\n\nconst CATEGORY_ICON: Record<FileTypeCategory, IconDefinition> = {\n folder: Folder,\n image: ImageXGeneric,\n audio: AudioXGeneric,\n video: VideoXGeneric,\n text: TextXGeneric,\n pdf: Document,\n archive: PackageXGeneric,\n document: XOfficeDocument,\n spreadsheet: XOfficeSpreadsheet,\n presentation: XOfficePresentation,\n font: FontXGeneric,\n executable: ApplicationXExecutable,\n unknown: TextXGeneric,\n};\n\nconst CATEGORY_LABEL: Record<FileTypeCategory, string> = {\n folder: 'Folder',\n image: 'Image file',\n audio: 'Audio file',\n video: 'Video file',\n text: 'Text file',\n pdf: 'PDF document',\n archive: 'Archive',\n document: 'Document',\n spreadsheet: 'Spreadsheet',\n presentation: 'Presentation',\n font: 'Font file',\n executable: 'Executable',\n unknown: 'File',\n};\n\nconst EXTENSION_CATEGORY: Record<string, FileTypeCategory> = {\n // Image\n png: 'image',\n jpg: 'image',\n jpeg: 'image',\n gif: 'image',\n webp: 'image',\n svg: 'image',\n bmp: 'image',\n ico: 'image',\n tiff: 'image',\n heic: 'image',\n // Audio\n mp3: 'audio',\n wav: 'audio',\n ogg: 'audio',\n flac: 'audio',\n m4a: 'audio',\n aac: 'audio',\n // Video\n mp4: 'video',\n mov: 'video',\n avi: 'video',\n mkv: 'video',\n webm: 'video',\n m4v: 'video',\n // PDF\n pdf: 'pdf',\n // Archive\n zip: 'archive',\n tar: 'archive',\n gz: 'archive',\n rar: 'archive',\n '7z': 'archive',\n xz: 'archive',\n // Office document\n doc: 'document',\n docx: 'document',\n odt: 'document',\n rtf: 'document',\n // Spreadsheet\n xls: 'spreadsheet',\n xlsx: 'spreadsheet',\n ods: 'spreadsheet',\n csv: 'spreadsheet',\n // Presentation\n ppt: 'presentation',\n pptx: 'presentation',\n odp: 'presentation',\n // Font\n ttf: 'font',\n otf: 'font',\n woff: 'font',\n woff2: 'font',\n // Executable\n exe: 'executable',\n app: 'executable',\n sh: 'executable',\n bin: 'executable',\n // Text / code — freedesktop groups source code under the generic text-x-generic icon.\n txt: 'text',\n md: 'text',\n log: 'text',\n json: 'text',\n yaml: 'text',\n yml: 'text',\n toml: 'text',\n xml: 'text',\n html: 'text',\n css: 'text',\n js: 'text',\n jsx: 'text',\n ts: 'text',\n tsx: 'text',\n py: 'text',\n rb: 'text',\n go: 'text',\n rs: 'text',\n c: 'text',\n cpp: 'text',\n h: 'text',\n java: 'text',\n sql: 'text',\n};\n\nconst MIME_TOP_LEVEL_CATEGORY: Partial<Record<string, FileTypeCategory>> = {\n image: 'image',\n audio: 'audio',\n video: 'video',\n text: 'text',\n font: 'font',\n};\n\nconst MIME_SUBTYPE_CATEGORY: Record<string, FileTypeCategory> = {\n pdf: 'pdf',\n zip: 'archive',\n gzip: 'archive',\n 'x-tar': 'archive',\n 'x-7z-compressed': 'archive',\n 'x-rar-compressed': 'archive',\n 'vnd.rar': 'archive',\n msword: 'document',\n 'vnd.openxmlformats-officedocument.wordprocessingml.document': 'document',\n 'vnd.oasis.opendocument.text': 'document',\n 'vnd.ms-excel': 'spreadsheet',\n 'vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'spreadsheet',\n 'vnd.oasis.opendocument.spreadsheet': 'spreadsheet',\n 'vnd.ms-powerpoint': 'presentation',\n 'vnd.openxmlformats-officedocument.presentationml.presentation': 'presentation',\n 'vnd.oasis.opendocument.presentation': 'presentation',\n 'x-sh': 'executable',\n 'x-executable': 'executable',\n};\n\n/** Resolves a file-type category from a MIME type (e.g. `\"image/png\"`). */\nexport function categoryFromMimeType(mimeType: string): FileTypeCategory | null {\n const normalized = mimeType.trim().toLowerCase();\n\n if (normalized === 'inode/directory') {\n return 'folder';\n }\n\n const [topLevel, subtype] = normalized.split('/');\n\n if (subtype && MIME_SUBTYPE_CATEGORY[subtype]) {\n return MIME_SUBTYPE_CATEGORY[subtype];\n }\n\n return MIME_TOP_LEVEL_CATEGORY[topLevel] ?? null;\n}\n\n/** Resolves a file-type category from a file name's extension (e.g. `\"report.pdf\"`). */\nexport function categoryFromName(name: string): FileTypeCategory | null {\n const trimmed = name.trim().replace(/^\\.+/, '');\n const lastDot = trimmed.lastIndexOf('.');\n\n if (lastDot <= 0) {\n return null;\n }\n\n const extension = trimmed.slice(lastDot + 1).toLowerCase();\n\n return EXTENSION_CATEGORY[extension] ?? null;\n}\n\nexport function getFileTypeIcon(category: FileTypeCategory): IconDefinition {\n return CATEGORY_ICON[category];\n}\n\nexport function getFileTypeLabel(category: FileTypeCategory): string {\n return CATEGORY_LABEL[category];\n}\n","import type { StyleProp, ViewStyle } from 'react-native';\nimport { Image, View } from 'react-native';\n\nimport { Icon, type IconSize } from '@/components/Icon';\nimport { ICON_SIZE_MAP } from '@/components/Icon/shared';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nimport {\n categoryFromMimeType,\n categoryFromName,\n getFileTypeIcon,\n getFileTypeLabel,\n} from './fileType';\n\nexport interface FileTypeIconProps {\n /** File name (e.g. `\"report.pdf\"`) — resolves the icon from its extension. */\n name?: string;\n /**\n * MIME type (e.g. `\"application/pdf\"`, `\"inode/directory\"`).\n * Takes precedence over `name` when both are provided.\n */\n mimeType?: string;\n /** Renders the folder icon regardless of `name`/`mimeType`. */\n isFolder?: boolean;\n /** Thumbnail image URL. When provided, renders the image instead of the resolved icon. */\n thumbnail?: string;\n /** Accessible label. Defaults to a generated description (e.g. `\"PDF document\"`). */\n label?: string;\n /** Icon size. Defaults to `\"md\"`. */\n size?: IconSize;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Small icon — optionally a thumbnail — resolved from a file's MIME type\n * or name extension. Useful for file-manager-style listings. Mirrors\n * `@gnome-ui/react`'s `FileTypeIcon`.\n *\n * Falls back to the generic file icon (mirrors freedesktop's\n * `text-x-generic`) when the type can't be resolved.\n *\n * `fileType.ts`'s category-resolution logic (MIME type / extension → one of\n * 13 categories, plus the freedesktop icon and generated label per\n * category) is pure, DOM-free TS — duplicated verbatim from\n * `@gnome-ui/react` rather than imported cross-package, the same\n * `isIconDefinition`/`Icon.tsx` precedent already established for\n * DOM-independent logic that still isn't worth a shared package for one\n * function's worth of code.\n *\n * `role=\"img\"` + `accessibilityLabel` ports 1:1 (the same `Avatar`/\n * `LevelBar` precedent for RN's newer web-aligned `Role` union). The\n * thumbnail reuses `Avatar`'s own `Image`/`resizeMode=\"cover\"` recipe,\n * sized from `Icon`'s own `ICON_SIZE_MAP` so swapping between the resolved\n * icon and a thumbnail never shifts layout — the same reasoning the web\n * version's `.sm`/`.md`/`.lg` classes document.\n */\nexport const FileTypeIcon = ({\n name,\n mimeType,\n isFolder = false,\n thumbnail,\n label,\n size = 'md',\n style,\n testID,\n}: FileTypeIconProps) => {\n const theme = useGnomeTheme();\n const box = ICON_SIZE_MAP[size];\n\n const category = isFolder\n ? 'folder'\n : (mimeType && categoryFromMimeType(mimeType)) || (name && categoryFromName(name)) || 'unknown';\n\n const resolvedLabel = label ?? getFileTypeLabel(category);\n\n return (\n <View\n testID={testID}\n accessible\n role=\"img\"\n accessibilityLabel={resolvedLabel}\n style={[{ width: box, height: box, alignItems: 'center', justifyContent: 'center' }, style]}\n >\n {thumbnail ? (\n <Image\n testID={testID ? `${testID}-thumbnail` : undefined}\n source={{ uri: thumbnail }}\n resizeMode=\"cover\"\n style={{ width: box, height: box, borderRadius: theme.radiusSm }}\n />\n ) : (\n <Icon icon={getFileTypeIcon(category)} size={size} />\n )}\n </View>\n );\n};\n","import { PanDown, Search } from '@gnome-ui/icons';\nimport { useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Animated, Dimensions, Modal, Pressable, ScrollView, TextInput, View } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport type { MultiSelectDropdownOption } from '@/components/MultiSelectDropdown';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport interface FilterableMultiSelectDropdownProps<V extends string = string> {\n /** The list of selectable options. */\n options: MultiSelectDropdownOption<V>[];\n /** The currently selected values. */\n value: V[];\n /** Called with the full updated selection whenever an option is toggled. */\n onChange: (value: V[]) => void;\n /** Placeholder shown on the trigger when no option is selected. */\n placeholder?: string;\n /** Placeholder for the filter field shown once the list is open. */\n filterPlaceholder?: string;\n /** Accessible label for the control. */\n accessibilityLabel?: string;\n /** Disables the entire control. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\ninterface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface Position {\n top: number;\n left: number;\n width: number;\n flipUp: boolean;\n}\n\nconst GAP = 4;\nconst MARGIN = 8;\nconst MAX_PANEL_HEIGHT = 320;\n\nfunction computePosition(trigger: Rect, panelHeight: number): Position {\n const { height: vh } = Dimensions.get('window');\n const spaceBelow = vh - (trigger.y + trigger.height);\n const flipUp = spaceBelow < panelHeight && trigger.y > panelHeight;\n const top = flipUp ? trigger.y - panelHeight - GAP : trigger.y + trigger.height + GAP;\n\n return {\n top: Math.max(MARGIN, Math.min(top, vh - panelHeight - MARGIN)),\n left: trigger.x,\n width: trigger.width,\n flipUp,\n };\n}\n\nfunction summarize(count: number, selected: { label: string }[]): string {\n if (count === 1) {\n return selected[0].label;\n }\n\n return `${count} selected`;\n}\n\nfunction filterOptions<V extends string>(\n options: MultiSelectDropdownOption<V>[],\n query: string,\n): MultiSelectDropdownOption<V>[] {\n const normalized = query.trim().toLowerCase();\n\n if (!normalized) {\n return options;\n }\n\n return options.filter(\n (o) =>\n o.label.toLowerCase().includes(normalized) ||\n o.description?.toLowerCase().includes(normalized),\n );\n}\n\n/**\n * `MultiSelectDropdown` plus a filter field for narrowing long option\n * lists, mirroring `@gnome-ui/react`'s `FilterableMultiSelectDropdown`.\n *\n * Same overall shape and positioning recipe as `MultiSelectDropdown` —\n * duplicated rather than composed, matching how the web source itself\n * relates to `MultiSelectDropdown` (a parallel implementation sharing only\n * the `MultiSelectDropdownOption` type, not a wrapper around it) — with a\n * filter `TextInput` pinned above the list, auto-focused on open via\n * `autoFocus`. Typing narrows `options` to those whose label or\n * description contains the query (case-insensitive); filtering only\n * affects what's shown, never the underlying selection — values selected\n * before a query hides their option stay selected. An empty filtered\n * result renders a centered \"No results\" message instead of the list.\n *\n * The web version's filter-field keyboard handler (↑/↓ roving highlight,\n * Home/End, Enter-to-toggle) has no RN port — same \"RN's touch-first model\n * has no keyboard focus to drive it\" reasoning `Dropdown`/`TabBar` already\n * established; RN's own `TextInput` still handles the actual typing (and\n * its software keyboard) natively, only the roving-highlight navigation on\n * top of it is dropped. Selection is by direct tap only, same as\n * `MultiSelectDropdown`.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/drop-down-lists.html\n */\nexport const FilterableMultiSelectDropdown = <V extends string = string>({\n options,\n value,\n onChange,\n placeholder = 'Select options',\n filterPlaceholder = 'Filter options…',\n accessibilityLabel,\n disabled,\n style,\n testID,\n}: FilterableMultiSelectDropdownProps<V>) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState('');\n const [triggerRect, setTriggerRect] = useState<Rect | null>(null);\n const [panelHeight, setPanelHeight] = useState<number | null>(null);\n const [pos, setPos] = useState<Position | null>(null);\n\n const triggerRef = useRef<View>(null);\n const progress = useRef(new Animated.Value(0)).current;\n\n const selectedOptions = options.filter((o) => value.includes(o.value));\n const filteredOptions = filterOptions(options, query);\n\n const openList = () => {\n if (disabled) {\n return;\n }\n\n setQuery('');\n setOpen(true);\n };\n\n const closeList = () => setOpen(false);\n\n const toggleOption = (opt: MultiSelectDropdownOption<V>) => {\n if (opt.disabled) {\n return;\n }\n\n onChange(\n value.includes(opt.value) ? value.filter((v) => v !== opt.value) : [...value, opt.value],\n );\n };\n\n useEffect(() => {\n if (!open) {\n setTriggerRect(null);\n setPanelHeight(null);\n setPos(null);\n\n return;\n }\n\n triggerRef.current?.measureInWindow((x, y, width, height) => {\n setTriggerRect({ x, y, width, height });\n });\n }, [open]);\n\n useEffect(() => {\n if (triggerRect === null || panelHeight === null) {\n return;\n }\n\n setPos(computePosition(triggerRect, panelHeight));\n }, [triggerRect, panelHeight]);\n\n useEffect(() => {\n if (!pos) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n progress.setValue(0);\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationFast,\n useNativeDriver: true,\n }).start();\n }, [pos, reducedMotion, progress, theme.durationFast]);\n\n const handlePanelLayout = (e: LayoutChangeEvent) => {\n setPanelHeight(Math.min(e.nativeEvent.layout.height, MAX_PANEL_HEIGHT));\n };\n\n return (\n <View style={style}>\n <Pressable\n ref={triggerRef}\n testID={testID}\n role=\"combobox\"\n accessibilityLabel={accessibilityLabel}\n accessibilityState={{ expanded: open, disabled: !!disabled }}\n disabled={disabled}\n onPress={() => (open ? closeList() : openList())}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: theme.space1,\n minHeight: 36,\n minWidth: 160,\n paddingVertical: 7,\n paddingHorizontal: theme.space2,\n backgroundColor: pressed ? theme.activeOverlay : theme.cardBgColor,\n borderWidth: 1,\n borderColor: open ? theme.accentColor : theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n opacity: disabled ? theme.opacityDisabled : 1,\n })}\n >\n <Text\n numberOfLines={1}\n style={{\n flex: 1,\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n opacity: selectedOptions.length === 0 ? theme.opacityDim : 1,\n }}\n >\n {selectedOptions.length === 0\n ? placeholder\n : summarize(selectedOptions.length, selectedOptions)}\n </Text>\n <Icon\n icon={PanDown}\n size=\"md\"\n color=\"default\"\n style={{ transform: [{ rotate: open ? '180deg' : '0deg' }] }}\n />\n </Pressable>\n\n <Modal visible={open} transparent animationType=\"none\" statusBarTranslucent>\n <Pressable\n onPress={closeList}\n accessible={false}\n style={{ flex: 1 }}\n testID={testID ? `${testID}-backdrop` : undefined}\n >\n <Animated.View\n onLayout={handlePanelLayout}\n style={[\n {\n position: 'absolute',\n maxHeight: MAX_PANEL_HEIGHT,\n backgroundColor: theme.popoverBgColor,\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 4 },\n shadowOpacity: 0.15,\n shadowRadius: 12,\n elevation: 6,\n },\n pos\n ? { top: pos.top, left: pos.left, width: pos.width, opacity: progress }\n : { top: -9999, left: -9999, width: 200, opacity: 0 },\n ]}\n >\n <View\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n padding: 8,\n borderBottomWidth: 1,\n borderBottomColor: theme.cardShadeColor,\n }}\n >\n <View style={{ position: 'absolute', left: 16, zIndex: 1 }}>\n <Icon\n icon={Search}\n size=\"sm\"\n color=\"default\"\n style={{ opacity: theme.opacityDim }}\n />\n </View>\n <TextInput\n value={query}\n onChangeText={setQuery}\n placeholder={filterPlaceholder}\n placeholderTextColor={theme.windowFgColor}\n accessibilityLabel={filterPlaceholder}\n autoFocus\n style={{\n flex: 1,\n paddingVertical: 6,\n paddingHorizontal: 8,\n paddingLeft: 28,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n color: theme.viewFgColor,\n backgroundColor: theme.viewBgColor,\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n }}\n />\n </View>\n\n {filteredOptions.length === 0 ? (\n <Text\n style={{\n marginVertical: 16,\n textAlign: 'center',\n color: theme.windowFgColor,\n opacity: theme.opacityDim,\n }}\n >\n No results\n </Text>\n ) : (\n <ScrollView role=\"list\" accessible bounces={false} style={{ paddingVertical: 4 }}>\n {filteredOptions.map((opt) => {\n const checked = value.includes(opt.value);\n\n return (\n <Pressable\n key={opt.value}\n role=\"option\"\n accessibilityState={{ selected: checked, disabled: !!opt.disabled }}\n disabled={opt.disabled}\n onPress={() => toggleOption(opt)}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n minHeight: 40,\n paddingVertical: 8,\n paddingHorizontal: theme.space2,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n opacity: opt.disabled ? theme.opacityDisabled : 1,\n })}\n >\n <View\n style={{\n width: 16,\n height: 16,\n flexShrink: 0,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusSm,\n borderWidth: 1.5,\n borderColor: checked ? theme.accentBgColor : theme.cardShadeColor,\n backgroundColor: checked ? theme.accentBgColor : 'transparent',\n }}\n >\n {checked && (\n <Text\n style={{\n color: '#fff',\n fontSize: 11,\n fontWeight: 'bold',\n lineHeight: 11,\n }}\n >\n {'✓'}\n </Text>\n )}\n </View>\n\n <View style={{ flex: 1, gap: 1 }}>\n <Text\n numberOfLines={1}\n style={{\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n fontWeight: checked\n ? (String(theme.fontWeightSemibold) as TextStyle['fontWeight'])\n : undefined,\n }}\n >\n {opt.label}\n </Text>\n {opt.description && (\n <Text\n numberOfLines={1}\n style={{\n fontSize: theme.fontSizeCaption,\n color: theme.windowFgColor,\n opacity: theme.opacityDim,\n }}\n >\n {opt.description}\n </Text>\n )}\n </View>\n </Pressable>\n );\n })}\n </ScrollView>\n )}\n </Animated.View>\n </Pressable>\n </Modal>\n </View>\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { StyleProp, TextStyle, View, ViewProps, ViewStyle } from 'react-native';\nimport { Text as RNText, View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface HeaderBarProps extends Omit<ViewProps, 'style'> {\n /** Centered title. Pass a string or a custom element. */\n title?: ReactNode;\n /** Controls placed at the leading (left) edge — back button, menu, etc. */\n start?: ReactNode;\n /** Controls placed at the trailing (right) edge — actions, overflow menu, etc. */\n end?: ReactNode;\n /**\n * When true the header bar blends into the window chrome (no bottom\n * border). Use for the topmost bar of a full-window layout.\n */\n flat?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Title bar with centered title and leading/trailing action slots.\n *\n * Mirrors the Adwaita `AdwHeaderBar` pattern. Use `flat` buttons\n * (`<Button variant=\"flat\">`) inside the header bar per GNOME HIG.\n *\n * Rebuilt with flexbox rather than ported from `@gnome-ui/react`'s CSS\n * Grid (`1fr auto 1fr`) — RN has no grid layout, but the same true-centering\n * effect (the title stays centered in the full width regardless of how\n * wide `start`/`end` are, rather than centering only in the leftover gap\n * between them) comes from giving both side slots `flex: 1` with the title\n * left unflexed in between, since both sides then always claim equal\n * leftover space. The web version's `<header>` element has no RN\n * equivalent; `accessibilityRole=\"header\"` is deliberately *not* applied to\n * the outer `View` since that role means \"text heading\" in RN's\n * accessibility tree (see `Text`'s heading variants), not \"landmark\n * container\" the way HTML's `<header>` does.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.HeaderBar.html\n * @see https://developer.gnome.org/hig/patterns/containers/header-bars.html\n */\nexport const HeaderBar = forwardRef<View, HeaderBarProps>(function HeaderBar(\n { title, start, end, flat = false, style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <RNView\n ref={ref}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space1,\n paddingHorizontal: theme.space2,\n minHeight: 47,\n backgroundColor: theme.headerbarBgColor,\n borderBottomWidth: flat ? 0 : 1,\n borderBottomColor: theme.headerbarBorderColor,\n },\n style,\n ]}\n {...viewProps}\n >\n <RNView style={{ flex: 1, flexDirection: 'row', alignItems: 'center', gap: theme.space1 }}>\n {start}\n </RNView>\n\n <RNView\n accessibilityLiveRegion=\"polite\"\n style={{ alignItems: 'center', justifyContent: 'center', paddingHorizontal: theme.space1 }}\n >\n {typeof title === 'string' ? (\n <RNText\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n lineHeight: Math.round(theme.fontSizeBody * theme.lineHeightHeading),\n color: theme.headerbarFgColor,\n }}\n >\n {title}\n </RNText>\n ) : (\n title\n )}\n </RNView>\n\n <RNView\n style={{\n flex: 1,\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'flex-end',\n gap: theme.space1,\n }}\n >\n {end}\n </RNView>\n </RNView>\n );\n});\n","import { Text as RNText, type TextStyle } from 'react-native';\n\nimport { Text, type TextProps } from '@/components/Text';\nimport { useGnomeTheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface HighlightProps extends Omit<TextProps, 'children'> {\n /** Full text to render. */\n text: string;\n /**\n * Term or terms to highlight within `text`. Pass an array to highlight\n * multiple distinct terms at once (e.g. each word of a multi-word search\n * query). Empty or whitespace-only terms are ignored.\n */\n query: string | string[];\n /** Match case-sensitively. Defaults to `false`. */\n caseSensitive?: boolean;\n}\n\nfunction escapeRegExp(s: string) {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Wraps every occurrence of `query` within `text` in a highlighted inline\n * run — mirrors `@gnome-ui/react`'s `Highlight`, which wraps matches in a\n * `<mark>`. Pairs with `SearchBar`'s suggestion list and any filterable\n * list to show users which part of a result matched what they typed.\n *\n * The outer span is the themed `Text` component (so callers get the same\n * `variant`/`color` API as everywhere else), but each matched run is a\n * plain, unthemed RN `Text` carrying only the highlight's own overrides —\n * RN's `Text` is the one primitive that inherits ambient `fontSize`/\n * `color`/`fontFamily` from a parent `Text` when nested, the same way the\n * web version's `<mark>` inherits from its surrounding text and only\n * overrides `background-color`/`font-weight`. Reaching for the themed\n * `Text` for the marked runs too would reset them to its own default\n * `variant=\"body\"` sizing instead of inheriting whatever variant the\n * caller chose for the whole string.\n *\n * The web version's translucent `color-mix(in srgb, accent 30%,\n * transparent)` background has no RN equivalent (`color-mix` is CSS-only)\n * — resolved to a literal 8-digit `#RRGGBBAA` hex instead, since\n * `accentBgColor` is always a plain 6-digit hex across all four theme\n * variants. `border-radius` on the `<mark>` has no reliable port either:\n * RN only paints `backgroundColor` on an inline (nested) `Text` run, not\n * `borderRadius` — a decorative nicety dropped here, not a behavior gap.\n * `prefers-contrast: more`'s solid-background/white-text swap ports via\n * `useResolvedContrast()`, the same hook `Button` already uses for its own\n * high-contrast branching.\n */\nexport const Highlight = ({ text, query, caseSensitive = false, ...textProps }: HighlightProps) => {\n const theme = useGnomeTheme();\n const contrast = useResolvedContrast();\n\n const terms = (Array.isArray(query) ? query : [query]).map((q) => q.trim()).filter(Boolean);\n\n if (terms.length === 0) {\n return <Text {...textProps}>{text}</Text>;\n }\n\n const pattern = new RegExp(`(${terms.map(escapeRegExp).join('|')})`, caseSensitive ? 'g' : 'gi');\n const parts = text.split(pattern);\n\n const markStyle: TextStyle = {\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n ...(contrast === 'more'\n ? { backgroundColor: theme.accentBgColor, color: '#fff' }\n : { backgroundColor: `${theme.accentBgColor}4D` }),\n };\n\n return (\n <Text {...textProps}>\n {parts.map((part, i) =>\n i % 2 === 1 ? (\n <RNText key={i} style={markStyle}>\n {part}\n </RNText>\n ) : (\n part\n ),\n )}\n </Text>\n );\n};\n","import type { TextStyle, ViewStyle } from 'react-native';\n\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type InlineViewSwitcherVariant = 'default' | 'flat' | 'round' | 'pill';\n\nexport interface VariantStyles {\n container: ViewStyle;\n /** Same values as `container.gap`/`container.padding`, kept as plain\n * numbers so the overflow math can add them up — `ViewStyle` types them\n * as `DimensionValue`, which may be a percentage string. */\n gap: number;\n padding: number;\n item: ViewStyle;\n iconOnlyItem: ViewStyle;\n indicator: ViewStyle;\n /** Inset of the indicator from the container's top/bottom edge. */\n indicatorInset: number;\n activeTextColor: string;\n idleTextColor: string;\n}\n\n/**\n * The four `.default`/`.flat`/`.round`/`.pill` CSS blocks, resolved against\n * the theme. Every `color-mix(in srgb, accent N%, transparent)` becomes an\n * 8-digit `#RRGGBBAA` hex off `theme.accentBgColor` (the `Chip`/`ToggleGroup`\n * precedent, which keeps the tint following the app's configurable accent),\n * and every `box-shadow` is dropped — the theme generator keeps shadow\n * tokens in `raw` only and `Card` already settled that a border, or the\n * surface contrast itself, carries the same separation on RN.\n */\nexport function getVariantStyles(\n theme: GnomeThemeTokens,\n variant: InlineViewSwitcherVariant,\n isDark: boolean,\n): VariantStyles {\n const surfaceBorder = isDark ? 'rgba(255, 255, 255, 0.12)' : theme.light3;\n const accentTint = `${theme.accentBgColor}${isDark ? '40' : '26'}`;\n const accentRing = `${theme.accentBgColor}${isDark ? '80' : '66'}`;\n\n switch (variant) {\n case 'flat':\n return {\n container: {\n backgroundColor: 'transparent',\n borderRadius: theme.radiusMd,\n padding: 2,\n gap: 2,\n },\n gap: 2,\n padding: 2,\n item: { borderRadius: theme.radiusMd - 2, paddingHorizontal: theme.space2 },\n iconOnlyItem: { paddingHorizontal: 4, minWidth: 28 },\n indicator: { backgroundColor: accentTint, borderRadius: theme.radiusMd - 2 },\n indicatorInset: 2,\n activeTextColor: theme.accentColor,\n idleTextColor: theme.windowFgColor,\n };\n\n case 'round':\n return {\n container: {\n backgroundColor: theme.cardBgColor,\n borderWidth: 1,\n borderColor: surfaceBorder,\n borderRadius: theme.radiusPill,\n padding: 2,\n gap: 2,\n },\n gap: 2,\n padding: 2,\n item: { borderRadius: theme.radiusPill, paddingHorizontal: theme.space2 },\n iconOnlyItem: { paddingHorizontal: 4, minWidth: 28 },\n // The only variant whose indicator is the solid accent, not a tint.\n indicator: { backgroundColor: theme.accentBgColor, borderRadius: theme.radiusPill },\n indicatorInset: 2,\n activeTextColor: theme.accentFgColor,\n idleTextColor: theme.windowFgColor,\n };\n\n case 'pill':\n return {\n container: {\n backgroundColor: theme.hoverOverlay,\n borderRadius: theme.radiusPill,\n padding: 3,\n gap: 2,\n },\n gap: 2,\n padding: 3,\n item: {\n borderRadius: theme.radiusPill,\n paddingVertical: 5,\n paddingHorizontal: theme.space2,\n },\n iconOnlyItem: { paddingHorizontal: 5, minWidth: 28 },\n // Segmented-control look: the indicator is the raised card surface\n // itself, with no accent color anywhere.\n indicator: { backgroundColor: theme.cardBgColor, borderRadius: theme.radiusPill },\n indicatorInset: 3,\n activeTextColor: theme.windowFgColor,\n idleTextColor: theme.windowFgColor,\n };\n\n default:\n return {\n container: {\n backgroundColor: theme.cardBgColor,\n borderWidth: 1,\n borderColor: surfaceBorder,\n borderRadius: theme.radiusMd,\n padding: 2,\n gap: 2,\n },\n gap: 2,\n padding: 2,\n item: { borderRadius: theme.radiusMd - 2, paddingHorizontal: theme.space2 },\n iconOnlyItem: { paddingHorizontal: 4, minWidth: 28 },\n indicator: {\n backgroundColor: accentTint,\n borderRadius: theme.radiusMd - 2,\n borderWidth: 1,\n borderColor: accentRing,\n },\n indicatorInset: 2,\n activeTextColor: theme.accentColor,\n idleTextColor: theme.windowFgColor,\n };\n }\n}\n\n/** Shared by the item label and the menu-sheet rows. */\nexport function labelTextStyle(theme: GnomeThemeTokens, active: boolean, color: string): TextStyle {\n return {\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(\n active ? theme.fontWeightSemibold : theme.fontWeightNormal,\n ) as TextStyle['fontWeight'],\n color,\n };\n}\n","import { Check, PanDown } from '@gnome-ui/icons';\nimport {\n Children,\n createContext,\n forwardRef,\n isValidElement,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { LayoutChangeEvent, StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { Animated, Pressable, Text as RNText, View as RNView, ScrollView } from 'react-native';\n\nimport { BottomSheet } from '@/components/BottomSheet';\nimport { Icon } from '@/components/Icon';\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\n\nimport type { InlineViewSwitcherItemProps } from './InlineViewSwitcherItem';\nimport {\n getVariantStyles,\n type InlineViewSwitcherVariant,\n labelTextStyle,\n type VariantStyles,\n} from './variants';\n\nexport type { InlineViewSwitcherVariant } from './variants';\n\nexport type InlineViewSwitcherOverflow = 'wrap' | 'scroll' | 'compact' | 'menu';\n\n/** Measured position of one item inside the switcher row. */\nexport interface ItemLayout {\n x: number;\n width: number;\n}\n\n// ─── Internal context ──────────────────────────────────────────────────────────\n\ninterface InlineViewSwitcherContextValue {\n value: string;\n onValueChange: (value: string) => void;\n compact: boolean;\n styles: VariantStyles;\n onItemLayout: (name: string, layout: ItemLayout) => void;\n}\n\nconst InlineViewSwitcherContext = createContext<InlineViewSwitcherContextValue | null>(null);\n\n// eslint-disable-next-line react-refresh/only-export-components\nexport function useInlineViewSwitcher() {\n const ctx = useContext(InlineViewSwitcherContext);\n\n if (!ctx) {\n throw new Error('InlineViewSwitcherItem must be used inside InlineViewSwitcher');\n }\n\n return ctx;\n}\n\n// ─── InlineViewSwitcher ────────────────────────────────────────────────────────\n\nexport interface InlineViewSwitcherProps extends Omit<ViewProps, 'style'> {\n /** Currently active view name. */\n value: string;\n /** Called with the new value when a view is selected. */\n onValueChange: (value: string) => void;\n /**\n * Visual style of the switcher.\n * - `default` — card background with border (same shape as `ToggleGroup`).\n * - `flat` — no background or border; active indicator only.\n * - `round` — pill-shaped container and items, solid accent indicator.\n * - `pill` — segmented-control style; active item appears lifted, no accent color.\n */\n variant?: InlineViewSwitcherVariant;\n /**\n * Overflow strategy when the container is too narrow for all items.\n * - `wrap` — default; items simply overflow.\n * - `scroll` — horizontal scroll, snapping each item to the start edge.\n * - `compact` — collapses item labels to icons-only when overflowing (needs icons on all items).\n * - `menu` — shows the active item and a chevron; all items open in a `BottomSheet`.\n */\n overflow?: InlineViewSwitcherOverflow;\n /** Accessible label for the group. */\n accessibilityLabel?: string;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/** Matches the web's `transform 160ms cubic-bezier(0.34, 1.2, 0.64, 1)`. */\nconst INDICATOR_DURATION = 160;\n/** Stay collapsed until the container is comfortably wider — ported verbatim. */\nconst HYSTERESIS = 30;\n\n/**\n * Compact inline view switcher for content areas, cards, and toolbars —\n * wherever `ViewSwitcher` (header-bar sized) would be too heavy. Mirrors\n * `AdwInlineViewSwitcher` (libadwaita 1.7 / GNOME 48) and\n * `@gnome-ui/react`'s own `InlineViewSwitcher`.\n *\n * All four variants and all four overflow strategies port, but almost none\n * of the *mechanism* does — this is a rebuild, not a transliteration:\n *\n * - **The sliding indicator** is measured, not laid out. The web reads the\n * active button's `offsetLeft`/`offsetWidth` in a `useLayoutEffect`; here\n * each item reports its own `onLayout` up through the context, and the\n * indicator animates `translateX` + `width` to the active entry. Both run\n * on **one JS-driven animation** (`useNativeDriver: false`): `width` can't\n * be native-driven, and mixing a native and a JS value on one component\n * throws — the same trade-off `Expander` accepted for its own animated\n * height. `scaleX` would have been native-driveable but distorts the\n * indicator's corner radii, which is exactly what the variants shape.\n * `useReducedMotion()` snaps it into place instead, per this package's\n * per-component convention.\n * - **Overflow detection** replaces `ResizeObserver` + `scrollWidth` vs\n * `clientWidth` with the item measurements already being collected: their\n * summed natural widths (RN defaults `flexShrink` to 0, so an overflowing\n * row reports each item's *natural* width rather than a squeezed one)\n * against the row's own `onLayout` width. The web's `naturalWidthRef`\n * capture and 30 px hysteresis port verbatim — without them, collapsing\n * the labels shrinks the content and would immediately re-expand it.\n * - **`overflow=\"scroll\"`** becomes a horizontal `ScrollView` with the\n * scrollbar hidden. `scroll-snap-align: start` has no RN style, but the\n * measured item offsets feed `snapToOffsets`, which reproduces it exactly.\n * - **`overflow=\"menu\"`** reuses the already-shipped `BottomSheet`, the same\n * component the web version reaches for.\n *\n * The ←/→/Home/End keyboard layer drops, as everywhere else in this package.\n * As in `ToggleGroup`, the group takes `accessibilityRole=\"radiogroup\"` but\n * deliberately not `accessible`, which on iOS would collapse the items into\n * a single unreachable element.\n *\n * @example\n * const [view, setView] = useState('grid');\n *\n * <InlineViewSwitcher value={view} onValueChange={setView} variant=\"pill\">\n * <InlineViewSwitcherItem name=\"grid\" label=\"Grid\" />\n * <InlineViewSwitcherItem name=\"list\" label=\"List\" />\n * </InlineViewSwitcher>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.InlineViewSwitcher.html\n */\nexport const InlineViewSwitcher = forwardRef<View, InlineViewSwitcherProps>(\n function InlineViewSwitcher(\n {\n value,\n onValueChange,\n variant = 'default',\n overflow = 'wrap',\n accessibilityLabel = 'View switcher',\n children,\n style,\n ...viewProps\n },\n ref,\n ) {\n const theme = useGnomeTheme();\n const isDark = useResolvedColorScheme() === 'dark';\n const reducedMotion = useReducedMotion();\n const styles = useMemo(\n () => getVariantStyles(theme, variant, isDark),\n [theme, variant, isDark],\n );\n\n const [layouts, setLayouts] = useState<Record<string, ItemLayout>>({});\n const [availableWidth, setAvailableWidth] = useState(0);\n const [isOverflowing, setIsOverflowing] = useState(false);\n const [sheetOpen, setSheetOpen] = useState(false);\n const naturalWidthRef = useRef(0);\n\n const translateX = useRef(new Animated.Value(0)).current;\n const indicatorWidth = useRef(new Animated.Value(0)).current;\n\n const onItemLayout = useCallback((name: string, layout: ItemLayout) => {\n setLayouts((prev) => {\n const current = prev[name];\n\n if (current && current.x === layout.x && current.width === layout.width) {\n return prev;\n }\n\n return { ...prev, [name]: layout };\n });\n }, []);\n\n const detectsOverflow = overflow === 'compact' || overflow === 'menu';\n\n const items = useMemo(\n () =>\n Children.toArray(children)\n .filter(isValidElement)\n .filter(\n (child) =>\n (child.type as { displayName?: string }).displayName === 'InlineViewSwitcherItem',\n )\n .map((child) => (child as ReactElement<InlineViewSwitcherItemProps>).props),\n [children],\n );\n\n // Natural width of the row: RN leaves `flexShrink` at 0, so an\n // overflowing row still reports each item at its own full width.\n const naturalWidth = useMemo(() => {\n const measured = items.map((item) => layouts[item.name]?.width ?? 0);\n\n if (measured.some((width) => width === 0)) {\n return 0;\n }\n\n const gaps = Math.max(items.length - 1, 0) * styles.gap;\n\n return measured.reduce((total, width) => total + width, 0) + gaps + 2 * styles.padding;\n }, [items, layouts, styles.gap, styles.padding]);\n\n useEffect(() => {\n if (!detectsOverflow || availableWidth === 0 || naturalWidth === 0) {\n if (!detectsOverflow) {\n setIsOverflowing(false);\n }\n return;\n }\n\n setIsOverflowing((prev) => {\n if (!prev) {\n if (naturalWidth > availableWidth) {\n naturalWidthRef.current = naturalWidth;\n return true;\n }\n return false;\n }\n\n return naturalWidthRef.current > availableWidth + HYSTERESIS;\n });\n }, [detectsOverflow, availableWidth, naturalWidth]);\n\n const compact = overflow === 'compact' && isOverflowing;\n const inMenuMode = overflow === 'menu' && isOverflowing;\n const activeItem = items.find((item) => item.name === value) ?? items[0];\n const activeLayout = layouts[value];\n\n useEffect(() => {\n if (inMenuMode || !activeLayout) {\n return;\n }\n\n if (reducedMotion) {\n translateX.setValue(activeLayout.x);\n indicatorWidth.setValue(activeLayout.width);\n return;\n }\n\n const animation = Animated.parallel([\n Animated.timing(translateX, {\n toValue: activeLayout.x,\n duration: INDICATOR_DURATION,\n useNativeDriver: false,\n }),\n Animated.timing(indicatorWidth, {\n toValue: activeLayout.width,\n duration: INDICATOR_DURATION,\n useNativeDriver: false,\n }),\n ]);\n\n animation.start();\n\n // Stop on unmount (and before restarting): a JS-driven animation keeps\n // its own timer alive, which would otherwise go on ticking against a\n // torn-down tree.\n return () => animation.stop();\n }, [activeLayout, inMenuMode, reducedMotion, translateX, indicatorWidth]);\n\n const context = useMemo(\n () => ({ value, onValueChange, compact, styles, onItemLayout }),\n [value, onValueChange, compact, styles, onItemLayout],\n );\n\n const handleRowLayout = useCallback(\n (event: LayoutChangeEvent) => {\n if (detectsOverflow) {\n setAvailableWidth(event.nativeEvent.layout.width);\n }\n },\n [detectsOverflow],\n );\n\n const snapToOffsets = useMemo(\n () =>\n overflow === 'scroll'\n ? items\n .map((item) => layouts[item.name]?.x ?? 0)\n .filter((x, index) => index === 0 || x > 0)\n : undefined,\n [overflow, items, layouts],\n );\n\n const row = (\n <RNView\n ref={ref}\n accessibilityRole={inMenuMode ? undefined : 'radiogroup'}\n accessibilityLabel={accessibilityLabel}\n onLayout={handleRowLayout}\n style={[\n { flexDirection: 'row', alignItems: 'stretch' },\n styles.container,\n // `.overflowDetect { width: 100% }` — the row has to be\n // constrained for there to be an overflow to detect at all.\n detectsOverflow ? { width: '100%' } : { alignSelf: 'flex-start' },\n style,\n ]}\n {...viewProps}\n >\n {inMenuMode ? (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={`${accessibilityLabel}: ${activeItem?.label ?? activeItem?.name}`}\n onPress={() => setSheetOpen(true)}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n gap: theme.space1,\n minHeight: 28,\n paddingVertical: 4,\n },\n styles.item,\n ]}\n >\n {activeItem?.icon ? <Icon icon={activeItem.icon} size=\"md\" /> : null}\n {activeItem?.label ? (\n // Idle color, not `activeTextColor`: menu mode hides the\n // indicator, so there's no accent surface behind the trigger.\n // The web version applies its `.active` class here anyway,\n // which paints `round`'s label in `accent-fg` (#fff) on a plain\n // card — white on white. Fixed rather than ported.\n <RNText style={labelTextStyle(theme, true, styles.idleTextColor)}>\n {activeItem.label}\n </RNText>\n ) : null}\n <Icon icon={PanDown} size=\"sm\" />\n </Pressable>\n ) : (\n <>\n <Animated.View\n pointerEvents=\"none\"\n style={[\n {\n position: 'absolute',\n left: 0,\n top: styles.indicatorInset,\n bottom: styles.indicatorInset,\n width: indicatorWidth,\n transform: [{ translateX }],\n },\n styles.indicator,\n ]}\n />\n {children}\n </>\n )}\n </RNView>\n );\n\n return (\n <InlineViewSwitcherContext.Provider value={context}>\n {overflow === 'scroll' ? (\n <ScrollView\n horizontal\n showsHorizontalScrollIndicator={false}\n snapToOffsets={snapToOffsets}\n decelerationRate=\"fast\"\n contentContainerStyle={{ flexGrow: 0 }}\n style={{ flexGrow: 0 }}\n >\n {row}\n </ScrollView>\n ) : (\n row\n )}\n\n {overflow === 'menu' ? (\n <BottomSheet\n open={sheetOpen}\n title={accessibilityLabel}\n onClose={() => setSheetOpen(false)}\n >\n <RNView accessibilityRole=\"radiogroup\" accessibilityLabel={accessibilityLabel}>\n {items.map((item) => {\n const active = item.name === value;\n\n return (\n <Pressable\n key={item.name}\n accessibilityRole=\"radio\"\n accessibilityState={{ checked: active }}\n accessibilityLabel={item.label ?? item.name}\n onPress={() => {\n onValueChange(item.name);\n setSheetOpen(false);\n }}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: theme.space2,\n paddingHorizontal: theme.space3,\n borderRadius: theme.radiusMd,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n })}\n >\n {item.icon ? <Icon icon={item.icon} size=\"md\" /> : null}\n <RNText\n style={[\n labelTextStyle(\n theme,\n active,\n active ? theme.accentColor : theme.windowFgColor,\n ),\n { flex: 1 },\n ]}\n >\n {item.label ?? item.name}\n </RNText>\n {active ? <Icon icon={Check} size=\"md\" /> : null}\n </Pressable>\n );\n })}\n </RNView>\n </BottomSheet>\n ) : null}\n </InlineViewSwitcherContext.Provider>\n );\n },\n);\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport { forwardRef, useCallback } from 'react';\nimport type { LayoutChangeEvent, PressableProps, View } from 'react-native';\nimport { Pressable, Text as RNText } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nimport { useInlineViewSwitcher } from './InlineViewSwitcher';\nimport { labelTextStyle } from './variants';\n\nexport interface InlineViewSwitcherItemProps\n extends Omit<PressableProps, 'children' | 'style' | 'disabled' | 'onPress'> {\n /** String identifier — becomes the switcher's `value` when this item is active. */\n name: string;\n /** Visible label. */\n label?: string;\n /** Icon from `@gnome-ui/icons`. */\n icon?: IconDefinition;\n /** Accessible name. Required for icon-only items; defaults to `label`. */\n accessibilityLabel?: string;\n disabled?: boolean;\n}\n\n/**\n * Individual view option inside an `InlineViewSwitcher`. Can be icon-only,\n * label-only, or icon + label — for icon-only items always pass an\n * `accessibilityLabel` so screen readers can identify the view.\n *\n * The item paints no background of its own for the active state: that's the\n * parent's sliding indicator, which this component feeds by reporting its\n * `onLayout` position and width up through the context. Only the label's\n * color and weight change, exactly as in the web version — where the\n * `.active` class also sets nothing but `color` and `font-weight`.\n */\nexport const InlineViewSwitcherItem = forwardRef<View, InlineViewSwitcherItemProps>(\n function InlineViewSwitcherItem(\n { name, label, icon, accessibilityLabel, disabled = false, ...pressableProps },\n ref,\n ) {\n const theme = useGnomeTheme();\n const { value, onValueChange, compact, styles, onItemLayout } = useInlineViewSwitcher();\n\n const active = value === name;\n // In compact mode the label collapses, but only when an icon can stand\n // in for it — ported verbatim from the web fallback.\n const showLabel = Boolean(label) && !(compact && icon);\n const iconOnly = Boolean(icon) && !showLabel;\n\n const handleLayout = useCallback(\n (event: LayoutChangeEvent) => {\n const { x, width } = event.nativeEvent.layout;\n\n onItemLayout(name, { x, width });\n },\n [name, onItemLayout],\n );\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n onPress={() => onValueChange(name)}\n onLayout={handleLayout}\n accessibilityRole=\"radio\"\n accessibilityState={{ checked: active, disabled }}\n accessibilityLabel={accessibilityLabel ?? label}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n gap: theme.space1,\n minHeight: 28,\n paddingVertical: 4,\n opacity: disabled ? theme.opacityDisabled : 1,\n },\n styles.item,\n iconOnly ? styles.iconOnlyItem : null,\n ]}\n {...pressableProps}\n >\n {icon ? <Icon icon={icon} size=\"md\" /> : null}\n\n {showLabel ? (\n <RNText\n numberOfLines={1}\n style={labelTextStyle(\n theme,\n active,\n active ? styles.activeTextColor : styles.idleTextColor,\n )}\n >\n {label}\n </RNText>\n ) : null}\n </Pressable>\n );\n },\n);\n\nInlineViewSwitcherItem.displayName = 'InlineViewSwitcherItem';\n","import { useEffect, useRef } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Animated, Easing, View } from 'react-native';\n\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type LevelBarVariant = 'accent' | 'success' | 'warning' | 'error';\n\nexport interface LevelBarProps {\n /** Current value, between `min` and `max`. */\n value: number;\n /** Minimum value. Defaults to `0`. */\n min?: number;\n /** Maximum value. Defaults to `1`. */\n max?: number;\n /** Threshold at or below which the bar renders in `lowVariant`. */\n low?: number;\n /** Color used when `value <= low`. Defaults to `\"warning\"`. */\n lowVariant?: LevelBarVariant;\n /** Threshold at or above which the bar renders in `highVariant`. */\n high?: number;\n /** Color used when `value >= high`. Defaults to `\"error\"`. */\n highVariant?: LevelBarVariant;\n /** Color used between `low` and `high`. Defaults to `\"accent\"`. */\n variant?: LevelBarVariant;\n /**\n * Render as a row of discrete blocks instead of a continuous fill —\n * mirrors `GtkLevelBar`'s discrete mode (e.g. signal-strength indicators).\n */\n discrete?: boolean;\n /** Number of blocks when `discrete` is true. Defaults to `10`. */\n numBlocks?: number;\n /** Accessible label describing what the level represents. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nfunction resolveVariant(\n value: number,\n low: number | undefined,\n lowVariant: LevelBarVariant,\n high: number | undefined,\n highVariant: LevelBarVariant,\n variant: LevelBarVariant,\n): LevelBarVariant {\n if (low !== undefined && value <= low) {\n return lowVariant;\n }\n\n if (high !== undefined && value >= high) {\n return highVariant;\n }\n\n return variant;\n}\n\nfunction variantColor(theme: GnomeThemeTokens, variant: LevelBarVariant): string {\n switch (variant) {\n case 'accent':\n return theme.accentBgColor;\n case 'success':\n return theme.successBgColor;\n case 'warning':\n return theme.warningBgColor;\n case 'error':\n return theme.errorBgColor;\n }\n}\n\n/**\n * Discrete level indicator with color-coded low/high offset zones —\n * mirrors `GtkLevelBar` and `@gnome-ui/react`'s `LevelBar`. Use for a\n * gauge/measurement display (disk usage, battery, signal strength), not\n * for task progress (see `ProgressBar`) or a proportional category\n * breakdown (see `SegmentedBar`).\n *\n * The continuous fill reuses `ProgressBar`'s exact animation technique\n * rather than animating `width` directly: a fixed `width: '100%'` fill\n * with `transformOrigin: 'left'` and an animated `transform: [{ scaleX }]`,\n * so the whole thing runs on `useNativeDriver: true` — a JS-driven\n * (`useNativeDriver: false`) `width` animation schedules its next frame via\n * a plain `setTimeout` that routinely fires after a test's `render()`\n * returns but before unmount, producing a real \"update not wrapped in\n * act()\" warning, the same reasoning `ProgressBar`'s own docstring\n * documents. `useReducedMotion()` mirrors `ProgressBar`'s determinate\n * behavior: the transition duration drops to `0` (an immediate jump)\n * rather than the animation being skipped in some other way.\n *\n * Discrete mode's per-block `background-color` transition has no port —\n * unlike the continuous fill's width change, a value change in discrete\n * mode is a binary color swap per block with no established `Animated`\n * color-interpolation precedent elsewhere in this package, so it's a\n * plain, unanimated style swap; a decorative nicety, not a behavior gap.\n * Discrete blocks are hidden from the accessibility tree\n * (`accessibilityElementsHidden`/`importantForAccessibility=\"no\"`,\n * mirroring the web version's `aria-hidden`) since the meter's value is\n * already exposed once via `accessibilityValue` on the container.\n *\n * `role=\"meter\"` ports 1:1 from RN's newer web-aligned `Role` union (unlike\n * `AccessibilityRole`, which has no `\"meter\"` value at all) — the same\n * precedent `Dialog`/`Avatar`/`Badge` already established for reaching for\n * `role` over `accessibilityRole` when only the newer union has the value\n * needed. Web's `aria-labelledby` (an id-relationship prop) has no RN\n * equivalent — RN has no DOM ids — so only `aria-label`\n * (`accessibilityLabel`) is ported, the same `ProgressBar`/`Slider`\n * precedent.\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/progress.html\n */\nexport const LevelBar = ({\n value,\n min = 0,\n max = 1,\n low,\n lowVariant = 'warning',\n high,\n highVariant = 'error',\n variant = 'accent',\n discrete = false,\n numBlocks = 10,\n accessibilityLabel,\n style,\n testID,\n}: LevelBarProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const reducedMotion = useReducedMotion();\n\n const range = max - min;\n const clamped = Math.min(max, Math.max(min, value));\n const fraction = range > 0 ? (clamped - min) / range : 0;\n const resolvedVariant = resolveVariant(clamped, low, lowVariant, high, highVariant, variant);\n\n const scaleX = useRef(new Animated.Value(fraction)).current;\n\n useEffect(() => {\n if (discrete) {\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(scaleX, {\n toValue: fraction,\n duration: reducedMotion ? 0 : theme.durationNormal,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: true,\n }).start();\n }, [fraction, discrete, reducedMotion, scaleX, theme.durationNormal, theme.easingDefault]);\n\n const trackColor = scheme === 'dark' ? theme.dark2 : theme.light3;\n\n return (\n <View\n testID={testID}\n accessible\n role=\"meter\"\n accessibilityLabel={accessibilityLabel}\n accessibilityValue={{ min, max, now: clamped }}\n style={[\n {\n flexDirection: 'row',\n width: '100%',\n height: 6,\n borderRadius: discrete ? 0 : theme.radiusPill,\n backgroundColor: discrete ? 'transparent' : trackColor,\n overflow: discrete ? 'visible' : 'hidden',\n gap: discrete ? 2 : 0,\n },\n style,\n ]}\n >\n {discrete ? (\n Array.from({ length: numBlocks }, (_, i) => {\n const blockFraction = (i + 1) / numBlocks;\n const filled = blockFraction <= fraction;\n\n return (\n <View\n key={i}\n testID=\"level-bar-block\"\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n flex: 1,\n height: '100%',\n minWidth: 2,\n borderRadius: 1,\n backgroundColor: filled ? variantColor(theme, resolvedVariant) : trackColor,\n }}\n />\n );\n })\n ) : (\n <Animated.View\n testID={testID ? `${testID}-fill` : undefined}\n style={{\n width: '100%',\n height: '100%',\n borderRadius: theme.radiusPill,\n backgroundColor: variantColor(theme, resolvedVariant),\n transformOrigin: 'left',\n transform: [{ scaleX }],\n }}\n />\n )}\n </View>\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { PressableProps, StyleProp, TextStyle, View, ViewStyle } from 'react-native';\nimport { Linking, Pressable, Text as RNText } from 'react-native';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface LinkProps extends Omit<PressableProps, 'children' | 'style'> {\n /**\n * Target URL. Passed to `Linking.openURL` when pressed, unless a custom\n * `onPress` is supplied (e.g. to hand internal links to a router like\n * React Navigation instead of the device browser).\n */\n href: string;\n /**\n * When true the link is treated as external: appends a trailing ↗\n * indicator and an \"Opens in browser\" accessibility hint. RN has no tab\n * concept, so unlike the web `Link` this doesn't change navigation\n * behaviour — `href` always opens via `Linking.openURL` regardless.\n */\n external?: boolean;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n /** Style applied to the label `Text`. */\n textStyle?: StyleProp<TextStyle>;\n}\n\n/**\n * Inline hyperlink following GNOME HIG.\n *\n * Rebuilt on `Pressable`/themed `Text` rather than ported from\n * `@gnome-ui/react`'s `<a>`: touch devices have no `:hover`, so the\n * underline that the web version reveals on hover instead reveals on\n * press — the closest native equivalent — alongside the same `0.7`\n * press-opacity dip as `@gnome-ui/react`'s `:active` state.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/links.html\n */\nexport const Link = forwardRef<View, LinkProps>(function Link(\n { href, external = false, children, style, textStyle, onPress, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <Pressable\n ref={ref}\n accessibilityRole=\"link\"\n accessibilityHint={external ? 'Opens in browser' : undefined}\n onPress={onPress ?? (() => Linking.openURL(href))}\n style={style}\n {...pressableProps}\n >\n {({ pressed }) => (\n <Text\n style={[\n {\n color: theme.accentColor,\n textDecorationLine: pressed ? 'underline' : 'none',\n opacity: pressed ? 0.7 : 1,\n },\n textStyle,\n ]}\n >\n {children}\n {external && (\n <RNText\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{ fontSize: 12 }}\n >\n {' ↗'}\n </RNText>\n )}\n </Text>\n )}\n </Pressable>\n );\n});\n","import {\n Children,\n cloneElement,\n forwardRef,\n isValidElement,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport type { StyleProp, View, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface LinkedGroupProps {\n /**\n * Each child must accept and merge a `style` prop the way every\n * `@gnome-ui/react-native` component already does (`style` last in its\n * own internal style array) — see the component doc for why.\n */\n children: ReactNode;\n /** Stack children vertically instead of horizontally. */\n vertical?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\ntype StyleableElement = ReactElement<{ style?: StyleProp<ViewStyle> }>;\n\n/**\n * Renders children as a single visually-connected unit with no gap and\n * merged borders — the canonical GNOME pattern for button groups and\n * segmented inputs. Mirrors `@gnome-ui/react`'s `LinkedGroup`, itself\n * mirroring the libadwaita `.linked` style class.\n *\n * The web version reaches every child's border-radius via a CSS\n * `> *` universal child selector — RN has no equivalent way for a parent\n * `View` to reach into an arbitrary child's own internally-computed\n * styles. Reimagined instead as the same `cloneElement`-onto-children\n * technique `Popover`/`Tooltip` already use on their own trigger:\n * each child gets a computed corner-radius/negative-margin override\n * merged onto whatever `style` it already has, using the same\n * \"zero the shared inner corners, keep `theme.radiusMd` on the outer\n * ones, overlap by 1 dp to collapse the shared border\" recipe\n * `SplitButton` already proved for its own two-piece connected border —\n * generalized here from a fixed two children to an arbitrary list. This\n * only works because every component in this package already merges a\n * passed-in `style` prop last, the same assumption `Popover`'s own\n * trigger-cloning already depends on.\n *\n * The web CSS also raises a hovered/focused child's `z-index` so its own\n * border isn't visually covered by the next sibling's overlapping edge —\n * dropped here: RN is touch-first (no `:hover`), and no component in this\n * package currently renders an escaping focus ring that overlap could\n * clip, so there's nothing yet for the z-index bump to protect.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest/style-classes.html#linked-style-class\n *\n * @example\n * <LinkedGroup>\n * <Button variant=\"flat\">Bold</Button>\n * <Button variant=\"flat\">Italic</Button>\n * </LinkedGroup>\n */\nexport const LinkedGroup = forwardRef<View, LinkedGroupProps>(function LinkedGroup(\n { children, vertical = false, style, testID },\n ref,\n) {\n const theme = useGnomeTheme();\n const items = Children.toArray(children).filter(isValidElement) as StyleableElement[];\n const count = items.length;\n\n return (\n <RNView\n ref={ref}\n testID={testID}\n style={[{ flexDirection: vertical ? 'column' : 'row', alignItems: 'stretch' }, style]}\n >\n {items.map((child, index) => {\n const isFirst = index === 0;\n const isLast = index === count - 1;\n\n const cornerStyle: ViewStyle = vertical\n ? {\n borderTopLeftRadius: isFirst ? theme.radiusMd : 0,\n borderTopRightRadius: isFirst ? theme.radiusMd : 0,\n borderBottomLeftRadius: isLast ? theme.radiusMd : 0,\n borderBottomRightRadius: isLast ? theme.radiusMd : 0,\n marginTop: isFirst ? 0 : -1,\n }\n : {\n borderTopLeftRadius: isFirst ? theme.radiusMd : 0,\n borderBottomLeftRadius: isFirst ? theme.radiusMd : 0,\n borderTopRightRadius: isLast ? theme.radiusMd : 0,\n borderBottomRightRadius: isLast ? theme.radiusMd : 0,\n marginLeft: isFirst ? 0 : -1,\n };\n\n return cloneElement(child, {\n key: index,\n style: [child.props.style, cornerStyle],\n });\n })}\n </RNView>\n );\n});\n","import { PanDown } from '@gnome-ui/icons';\nimport { useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Animated, Dimensions, Modal, Pressable, ScrollView, View } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport interface MultiSelectDropdownOption<V extends string = string> {\n /** The value included in `value` when this option is selected. */\n value: V;\n /** Display label shown in the list and (when only one is selected) the trigger. */\n label: string;\n /** Optional descriptive text shown below the label. */\n description?: string;\n /** Whether the option is selectable. */\n disabled?: boolean;\n}\n\nexport interface MultiSelectDropdownProps<V extends string = string> {\n /** The list of selectable options. */\n options: MultiSelectDropdownOption<V>[];\n /** The currently selected values. */\n value: V[];\n /** Called with the full updated selection whenever an option is toggled. */\n onChange: (value: V[]) => void;\n /** Placeholder shown on the trigger when no option is selected. */\n placeholder?: string;\n /** Accessible label for the control. */\n accessibilityLabel?: string;\n /** Disables the entire control. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\ninterface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface Position {\n top: number;\n left: number;\n width: number;\n flipUp: boolean;\n}\n\nconst GAP = 4;\nconst MARGIN = 8;\nconst MAX_LIST_HEIGHT = 280;\n\nfunction computePosition(trigger: Rect, panelHeight: number): Position {\n const { height: vh } = Dimensions.get('window');\n const spaceBelow = vh - (trigger.y + trigger.height);\n const flipUp = spaceBelow < panelHeight && trigger.y > panelHeight;\n const top = flipUp ? trigger.y - panelHeight - GAP : trigger.y + trigger.height + GAP;\n\n return {\n top: Math.max(MARGIN, Math.min(top, vh - panelHeight - MARGIN)),\n left: trigger.x,\n width: trigger.width,\n flipUp,\n };\n}\n\nfunction summarize(count: number, selected: { label: string }[]): string {\n if (count === 1) {\n return selected[0].label;\n }\n\n return `${count} selected`;\n}\n\n/**\n * Checkbox-list variant of `Dropdown` for selecting multiple values from a\n * single trigger, mirroring `@gnome-ui/react`'s `MultiSelectDropdown`.\n *\n * `Dropdown`/`ComboRow` are single-select only — use `MultiSelectDropdown`\n * when more than one value can be chosen at once. Toggling an option keeps\n * the panel open so the user can pick several in a row (unlike `Dropdown`,\n * whose `selectOption` closes the panel); close it via the backdrop tap.\n *\n * The `Modal` + backdrop + independently-measured-then-combined\n * trigger-rect/panel-height positioning (`Rect`/`Position`/\n * `computePosition`/`GAP`/`MARGIN`/`MAX_LIST_HEIGHT`) is duplicated\n * verbatim from `Dropdown` rather than extracted to a shared hook — same\n * \"genuinely different, not save-able\" judgment call already applied to\n * `Tooltip`/`Dropdown`'s own position-code duplication, though this is now\n * the third near-identical copy of the up/down-flip-clamp-to-width shape\n * specifically (not `Tooltip`'s 4-directional cascade) — worth extracting\n * into a shared hook in a future pass if a fourth caller needs it, since\n * unlike `Tooltip` these two are not just similarly-shaped but identical.\n *\n * Each option row gets a leading checkbox-square visual (border/background\n * only, no animation — `Dropdown`'s own per-option content has none\n * either) instead of `Dropdown`'s single trailing checkmark-when-selected,\n * since more than one row can be checked at once.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/drop-down-lists.html\n */\nexport const MultiSelectDropdown = <V extends string = string>({\n options,\n value,\n onChange,\n placeholder = 'Select options',\n accessibilityLabel,\n disabled,\n style,\n testID,\n}: MultiSelectDropdownProps<V>) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const [open, setOpen] = useState(false);\n const [triggerRect, setTriggerRect] = useState<Rect | null>(null);\n const [panelHeight, setPanelHeight] = useState<number | null>(null);\n const [pos, setPos] = useState<Position | null>(null);\n\n const triggerRef = useRef<View>(null);\n const progress = useRef(new Animated.Value(0)).current;\n\n const selectedOptions = options.filter((o) => value.includes(o.value));\n\n const openList = () => {\n if (disabled) {\n return;\n }\n\n setOpen(true);\n };\n\n const closeList = () => setOpen(false);\n\n const toggleOption = (opt: MultiSelectDropdownOption<V>) => {\n if (opt.disabled) {\n return;\n }\n\n onChange(\n value.includes(opt.value) ? value.filter((v) => v !== opt.value) : [...value, opt.value],\n );\n };\n\n useEffect(() => {\n if (!open) {\n setTriggerRect(null);\n setPanelHeight(null);\n setPos(null);\n\n return;\n }\n\n triggerRef.current?.measureInWindow((x, y, width, height) => {\n setTriggerRect({ x, y, width, height });\n });\n }, [open]);\n\n useEffect(() => {\n if (triggerRect === null || panelHeight === null) {\n return;\n }\n\n setPos(computePosition(triggerRect, panelHeight));\n }, [triggerRect, panelHeight]);\n\n useEffect(() => {\n if (!pos) {\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n progress.setValue(0);\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationFast,\n useNativeDriver: true,\n }).start();\n }, [pos, reducedMotion, progress, theme.durationFast]);\n\n const handlePanelLayout = (e: LayoutChangeEvent) => {\n setPanelHeight(Math.min(e.nativeEvent.layout.height, MAX_LIST_HEIGHT));\n };\n\n return (\n <View style={style}>\n <Pressable\n ref={triggerRef}\n testID={testID}\n role=\"combobox\"\n accessibilityLabel={accessibilityLabel}\n accessibilityState={{ expanded: open, disabled: !!disabled }}\n disabled={disabled}\n onPress={() => (open ? closeList() : openList())}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: theme.space1,\n minHeight: 36,\n minWidth: 160,\n paddingVertical: 7,\n paddingHorizontal: theme.space2,\n backgroundColor: pressed ? theme.activeOverlay : theme.cardBgColor,\n borderWidth: 1,\n borderColor: open ? theme.accentColor : theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n opacity: disabled ? theme.opacityDisabled : 1,\n })}\n >\n <Text\n numberOfLines={1}\n style={{\n flex: 1,\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n opacity: selectedOptions.length === 0 ? theme.opacityDim : 1,\n }}\n >\n {selectedOptions.length === 0\n ? placeholder\n : summarize(selectedOptions.length, selectedOptions)}\n </Text>\n <Icon\n icon={PanDown}\n size=\"md\"\n color=\"default\"\n style={{ transform: [{ rotate: open ? '180deg' : '0deg' }] }}\n />\n </Pressable>\n\n <Modal visible={open} transparent animationType=\"none\" statusBarTranslucent>\n <Pressable\n onPress={closeList}\n accessible={false}\n style={{ flex: 1 }}\n testID={testID ? `${testID}-backdrop` : undefined}\n >\n <Animated.View\n onLayout={handlePanelLayout}\n role=\"list\"\n accessible\n style={[\n {\n position: 'absolute',\n maxHeight: MAX_LIST_HEIGHT,\n backgroundColor: theme.popoverBgColor,\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n paddingVertical: 4,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 4 },\n shadowOpacity: 0.15,\n shadowRadius: 12,\n elevation: 6,\n },\n pos\n ? { top: pos.top, left: pos.left, width: pos.width, opacity: progress }\n : { top: -9999, left: -9999, width: 200, opacity: 0 },\n ]}\n >\n <ScrollView bounces={false}>\n {options.map((opt) => {\n const checked = value.includes(opt.value);\n\n return (\n <Pressable\n key={opt.value}\n role=\"option\"\n accessibilityState={{ selected: checked, disabled: !!opt.disabled }}\n disabled={opt.disabled}\n onPress={() => toggleOption(opt)}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n minHeight: 40,\n paddingVertical: 8,\n paddingHorizontal: theme.space2,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n opacity: opt.disabled ? theme.opacityDisabled : 1,\n })}\n >\n <View\n style={{\n width: 16,\n height: 16,\n flexShrink: 0,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusSm,\n borderWidth: 1.5,\n borderColor: checked ? theme.accentBgColor : theme.cardShadeColor,\n backgroundColor: checked ? theme.accentBgColor : 'transparent',\n }}\n >\n {checked && (\n <Text\n style={{\n color: '#fff',\n fontSize: 11,\n fontWeight: 'bold',\n lineHeight: 11,\n }}\n >\n {'✓'}\n </Text>\n )}\n </View>\n\n <View style={{ flex: 1, gap: 1 }}>\n <Text\n numberOfLines={1}\n style={{\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n fontWeight: checked\n ? (String(theme.fontWeightSemibold) as TextStyle['fontWeight'])\n : undefined,\n }}\n >\n {opt.label}\n </Text>\n {opt.description && (\n <Text\n numberOfLines={1}\n style={{\n fontSize: theme.fontSizeCaption,\n color: theme.windowFgColor,\n opacity: theme.opacityDim,\n }}\n >\n {opt.description}\n </Text>\n )}\n </View>\n </Pressable>\n );\n })}\n </ScrollView>\n </Animated.View>\n </Pressable>\n </Modal>\n </View>\n );\n};\n","import { useWindowDimensions } from 'react-native';\n\n/**\n * GNOME / libadwaita canonical breakpoints (in dp, assuming 1 sp = 1 dp at 1× density).\n *\n * | Name | Max width | Pattern triggered |\n * |----------|-----------|--------------------|\n * | `narrow` | ≤ 400 dp | Collapse split views; sidebar becomes overlay |\n * | `medium` | ≤ 550 dp | Move ViewSwitcher to a bottom bar |\n * | `wide` | ≤ 860 dp | Collapse outer pane in nested split views |\n *\n * Identical thresholds to `@gnome-ui/react`'s `useBreakpoint` — only the\n * measurement source differs (`useWindowDimensions` instead of\n * `window.innerWidth`/a `resize` listener).\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Breakpoint.html\n */\nexport const GNOME_BREAKPOINTS = {\n /** ≤ 400 dp — split views collapse to single pane */\n narrow: 400,\n /** ≤ 550 dp — ViewSwitcher moves to bottom bar */\n medium: 550,\n /** ≤ 860 dp — outer pane of nested split views collapses */\n wide: 860,\n} as const;\n\nexport type GnomeBreakpointName = keyof typeof GNOME_BREAKPOINTS;\n\nexport interface BreakpointState {\n /** Width ≤ 400 dp — split views are collapsed. */\n isNarrow: boolean;\n /** Width ≤ 550 dp — medium or narrower. */\n isMedium: boolean;\n /** Width ≤ 860 dp — wide or narrower. */\n isWide: boolean;\n /** Current window width in dp. */\n width: number;\n}\n\nfunction computeBreakpointState(width: number): BreakpointState {\n return {\n isNarrow: width <= GNOME_BREAKPOINTS.narrow,\n isMedium: width <= GNOME_BREAKPOINTS.medium,\n isWide: width <= GNOME_BREAKPOINTS.wide,\n width,\n };\n}\n\n/**\n * Tracks the window width against GNOME / libadwaita breakpoints.\n *\n * Built on `useWindowDimensions` (not `Dimensions.get` + a manual listener),\n * since it already re-renders its subscribers on every rotation/resize —\n * there is no CSS media query to lean on here, unlike the web version.\n *\n * @example\n * const { isNarrow, isMedium } = useBreakpoint();\n * // isNarrow → true when the window is ≤ 400 dp wide (split views should collapse)\n * // isMedium → true when ≤ 550 dp (use a bottom ViewSwitcher instead)\n */\nexport function useBreakpoint(): BreakpointState {\n const { width } = useWindowDimensions();\n\n return computeBreakpointState(width);\n}\n\n// ─── Responsive values ────────────────────────────────────────────────────────\n\n/** The widest bucket, above every breakpoint, is `base`. */\nexport type GnomeBreakpointBucket = GnomeBreakpointName | 'base';\n\n/**\n * A value that may vary by breakpoint: either the value itself, or a map of\n * breakpoint names to values.\n *\n * The buckets are max-widths, so the map reads like stacked `max-width`\n * media queries — `base` is the widest, and the narrowest matching entry\n * wins:\n *\n * ```ts\n * { base: 3, wide: 2, narrow: 1 }\n * // ≤ 400 dp → 1 | ≤ 860 dp → 2 | wider → 3\n * // (550 dp matches `wide`, since no `medium` entry is given)\n * ```\n */\nexport type ResponsiveValue<T> = T | ({ base?: T } & Partial<Record<GnomeBreakpointName, T>>);\n\n/** Narrowest first, so a lookup can walk from the current bucket outwards. */\nconst BUCKET_FALLBACKS: Record<GnomeBreakpointBucket, GnomeBreakpointBucket[]> = {\n narrow: ['narrow', 'medium', 'wide', 'base'],\n medium: ['medium', 'wide', 'base'],\n wide: ['wide', 'base'],\n base: ['base'],\n};\n\n/**\n * Bucket a width falls into. The same thresholds serve `useBreakpoint` and\n * `BreakpointBin` — a breakpoint applies to whatever it's measured against,\n * window or container alike.\n *\n * A width of `0` means \"not measured yet\" and reports `base`, so the first\n * render matches the widest layout rather than flashing the narrowest one.\n */\nexport function bucketForWidth(width: number): GnomeBreakpointBucket {\n if (width <= 0) {\n return 'base';\n }\n if (width <= GNOME_BREAKPOINTS.narrow) {\n return 'narrow';\n }\n if (width <= GNOME_BREAKPOINTS.medium) {\n return 'medium';\n }\n if (width <= GNOME_BREAKPOINTS.wide) {\n return 'wide';\n }\n return 'base';\n}\n\n/** True when `value` is a breakpoint map rather than a plain value. */\nexport function isResponsiveMap<T>(value: ResponsiveValue<T> | undefined): boolean {\n return typeof value === 'object' && value !== null;\n}\n\n/** Pick the entry for `bucket`, falling back outwards to the wider ones. */\nexport function resolveResponsive<T>(\n value: ResponsiveValue<T> | undefined,\n bucket: GnomeBreakpointBucket,\n fallback: T,\n): T {\n if (value === undefined) {\n return fallback;\n }\n if (!isResponsiveMap(value)) {\n return value as T;\n }\n\n const map = value as Partial<Record<GnomeBreakpointBucket, T>>;\n for (const key of BUCKET_FALLBACKS[bucket]) {\n const entry = map[key];\n if (entry !== undefined) {\n return entry;\n }\n }\n return fallback;\n}\n","import { type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, ViewStyle } from 'react-native';\nimport { Animated, Easing, View } from 'react-native';\n\nimport { Separator } from '@/components/Separator';\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\nimport { useBreakpoint } from '@/hooks/useBreakpoint';\n\nexport interface NavigationSplitViewProps {\n /**\n * The sidebar / list pane (left side on wide screens).\n * On narrow screens this is the \"list\" view shown when `showContent` is false.\n */\n sidebar: ReactNode;\n /**\n * The detail / content pane (right side on wide screens).\n * On narrow screens this is the \"detail\" view shown when `showContent` is true.\n */\n content: ReactNode;\n /**\n * Controls which pane is visible on narrow screens (≤ 400 dp).\n * - `false` (default) — show the sidebar list.\n * - `true` — show the content detail.\n *\n * Has no effect on wide screens where both panes are visible simultaneously.\n */\n showContent?: boolean;\n /** Minimum sidebar width in dp. Defaults to `180`. */\n minSidebarWidth?: number;\n /** Maximum sidebar width in dp. Defaults to `280`. */\n maxSidebarWidth?: number;\n /** Fraction of total width given to the sidebar (0–1). Defaults to `0.25`. */\n sidebarWidthFraction?: number;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Two-pane sidebar + content layout following the Adwaita\n * `AdwNavigationSplitView` pattern — mirrors `@gnome-ui/react`'s\n * `NavigationSplitView`.\n *\n * On **wide** screens (`useBreakpoint().isNarrow === false`, > 400 dp) both\n * panes are visible side-by-side, separated by a `Separator`. On **narrow**\n * screens only one pane is shown at a time; `showContent` switches between\n * the sidebar list and the detail view.\n *\n * The web version's `clamp(min, fraction * 100%, max)` sidebar width has no\n * RN equivalent (`StyleSheet` values aren't CSS `calc`/`clamp` expressions),\n * so the container measures its own width via `onLayout` and the same clamp\n * is computed in JS — `0` until the first layout pass resolves, the same\n * one-frame imprecision this package already accepts elsewhere (e.g.\n * `ProgressBar`'s `trackWidth`-dependent math).\n *\n * Narrow-mode pane switching is animated (`translateX`, matching the web\n * CSS's own `transition: transform`), so — unlike `TabPanel`'s simpler\n * `display: 'none'` swap — both panes stay laid out and absolutely\n * positioned rather than being removed from flow, using the same measured\n * container width for the slide distance (RN `transform` has no\n * percentage-of-self units, so this can't be a bare `-100%`/`100%` the way\n * the web version's CSS is). One shared `Animated.Value` drives both\n * panes' opposite-direction translation, the same \"skip the animation on\n * initial mount, only animate subsequent prop changes\" guard `Switch`/\n * `StepIndicator` already established for a controlled boolean prop.\n *\n * The web's `inert` attribute (removes the hidden pane from both the a11y\n * tree and the tab order while it stays mounted off-screen) has no single\n * RN equivalent — reproduced with `accessibilityElementsHidden` +\n * `importantForAccessibility=\"no-hide-descendants\"` (the whole-subtree a11y\n * exclusion, not just the single-element `\"no\"` `PathBar`'s decorative\n * separator uses) plus `pointerEvents=\"none\"` so the off-screen pane can't\n * intercept touches meant for the visible one.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.NavigationSplitView.html\n */\nexport const NavigationSplitView = ({\n sidebar,\n content,\n showContent = false,\n minSidebarWidth = 180,\n maxSidebarWidth = 280,\n sidebarWidthFraction = 0.25,\n style,\n testID,\n}: NavigationSplitViewProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n const { isNarrow } = useBreakpoint();\n\n const [containerWidth, setContainerWidth] = useState(0);\n const progress = useRef(new Animated.Value(showContent ? 1 : 0)).current;\n const isInitialMount = useRef(true);\n\n useEffect(() => {\n if (isInitialMount.current) {\n isInitialMount.current = false;\n\n return;\n }\n\n if (reducedMotion) {\n progress.setValue(showContent ? 1 : 0);\n\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: showContent ? 1 : 0,\n duration: theme.durationNormal,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: true,\n }).start();\n }, [showContent, reducedMotion, progress, theme.durationNormal, theme.easingDefault]);\n\n const handleLayout = (event: LayoutChangeEvent) => {\n setContainerWidth(event.nativeEvent.layout.width);\n };\n\n const sidebarWidth = Math.min(\n Math.max(containerWidth * sidebarWidthFraction, minSidebarWidth),\n maxSidebarWidth,\n );\n\n const sidebarHidden = isNarrow && showContent;\n const contentHidden = isNarrow && !showContent;\n\n const sidebarTranslateX = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [0, -containerWidth],\n });\n const contentTranslateX = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [containerWidth, 0],\n });\n\n return (\n <View\n testID={testID}\n onLayout={handleLayout}\n style={[\n {\n width: '100%',\n height: '100%',\n flexDirection: isNarrow ? 'column' : 'row',\n overflow: 'hidden',\n },\n style,\n ]}\n >\n <Animated.View\n accessibilityElementsHidden={sidebarHidden}\n importantForAccessibility={sidebarHidden ? 'no-hide-descendants' : 'yes'}\n pointerEvents={sidebarHidden ? 'none' : 'auto'}\n style={\n isNarrow\n ? {\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n transform: [{ translateX: sidebarTranslateX }],\n }\n : { width: sidebarWidth, flexShrink: 0 }\n }\n >\n {sidebar}\n </Animated.View>\n\n {!isNarrow && <Separator orientation=\"vertical\" testID={testID && `${testID}-divider`} />}\n\n <Animated.View\n accessibilityElementsHidden={contentHidden}\n importantForAccessibility={contentHidden ? 'no-hide-descendants' : 'yes'}\n pointerEvents={contentHidden ? 'none' : 'auto'}\n style={\n isNarrow\n ? {\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n transform: [{ translateX: contentTranslateX }],\n }\n : { flex: 1, minWidth: 0 }\n }\n >\n {content}\n </Animated.View>\n </View>\n );\n};\n","import { type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Animated, Modal, Pressable } from 'react-native';\n\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\n/**\n * `Animated`'s style interpolation only takes effect on `Animated.*` host\n * components — a plain `Pressable` would silently ignore the animated\n * `opacity` in its style, the same gotcha already documented on `Toast`/\n * `Dialog`/`BottomSheet`. Hoisted to module scope so it isn't recreated on\n * every render.\n */\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable);\n\nexport interface OverlayProps {\n /** Whether the overlay is visible. */\n open: boolean;\n /** Called when the backdrop itself (not its content) is pressed. */\n onDismiss?: () => void;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Standalone backdrop/scrim layer with a fade transition and\n * press-to-dismiss — the shared building block behind `Dialog`,\n * `Dropdown`, `Popover`, and `BottomSheet`'s backdrops, extracted here for\n * building custom overlay UI, mirroring `@gnome-ui/react`'s `Overlay`.\n *\n * Deliberately minimal, same as the web version: no focus trap, no\n * `BackHandler`/Escape handling, no `role`. Use `Dialog`/`Popover`/\n * `BottomSheet` directly when you need those — this is only the fade +\n * dismiss-on-backdrop-tap primitive underneath them.\n *\n * Built on `Modal` rather than the web version's `createPortal` — no\n * `container` prop exists here, since RN's `Modal` has no equivalent\n * mount-target concept (it always renders at the top of the native view\n * hierarchy). Reuses `Dialog`'s exact backdrop recipe: an\n * `AnimatedPressable` backdrop whose `onPress` fires `onDismiss`, wrapping\n * `children` in a no-op `Pressable` so a tap on the content itself never\n * bubbles to the backdrop and dismisses it — the RN analog of the web\n * version's `e.target === e.currentTarget` check, which has no meaning in\n * RN's touch-responder model.\n *\n * **Real, timed exit animation, same technique as `BottomSheet`**: a local\n * `visible` state lags one animation behind the `open` prop, flipping to\n * `false` only in the fade-out `Animated.timing`'s own completion callback\n * — not a `setTimeout` racing a hardcoded duration like the web version,\n * since `Animated`'s callback already fires exactly when the animation\n * actually finishes. `useBodyScrollLock` has no RN equivalent needed —\n * `Modal` already blocks all interaction with whatever's behind it.\n */\nexport const Overlay = ({ open, onDismiss, children, style, testID }: OverlayProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n\n const [visible, setVisible] = useState(open);\n const opacity = useRef(new Animated.Value(open ? 1 : 0)).current;\n\n useEffect(() => {\n if (open) {\n setVisible(true);\n }\n }, [open]);\n\n useEffect(() => {\n if (!visible) {\n return;\n }\n\n if (open) {\n if (reducedMotion) {\n opacity.setValue(1);\n\n return;\n }\n\n opacity.setValue(0);\n Animated.timing(opacity, {\n toValue: 1,\n duration: theme.durationNormal,\n useNativeDriver: true,\n }).start();\n } else {\n if (reducedMotion) {\n setVisible(false);\n\n return;\n }\n\n Animated.timing(opacity, {\n toValue: 0,\n duration: theme.durationNormal,\n useNativeDriver: true,\n }).start(({ finished }) => {\n if (finished) {\n setVisible(false);\n }\n });\n }\n }, [open, visible, reducedMotion, opacity, theme.durationNormal]);\n\n if (!visible) {\n return null;\n }\n\n return (\n <Modal visible={visible} transparent animationType=\"none\" statusBarTranslucent>\n <AnimatedPressable\n testID={testID}\n onPress={onDismiss}\n accessible={false}\n style={[\n {\n flex: 1,\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: theme.dialogBackdropColor,\n opacity,\n },\n style,\n ]}\n >\n <Pressable onPress={() => {}} accessible={false}>\n {children}\n </Pressable>\n </AnimatedPressable>\n </Modal>\n );\n};\n","import { ViewConceal, ViewReveal } from '@gnome-ui/icons';\nimport { forwardRef, type ReactNode, useState } from 'react';\nimport type { TextInput } from 'react-native';\n\nimport { EntryRow, type EntryRowProps } from '@/components/EntryRow';\nimport { IconButton } from '@/components/IconButton';\n\nexport interface PasswordEntryRowProps extends Omit<EntryRowProps, 'secureTextEntry' | 'trailing'> {\n /** Additional trailing widgets placed before the reveal button. */\n trailing?: ReactNode;\n}\n\n/**\n * Password entry row with a built-in reveal/conceal toggle, mirroring\n * `AdwPasswordEntryRow` and `@gnome-ui/react`'s own `PasswordEntryRow`. It's\n * an `EntryRow` that masks its input and always carries a trailing button to\n * show or hide what's been typed. Use inside a `BoxedList` for password\n * settings fields — and don't add your own reveal button through `trailing`,\n * which is for anything that should sit *before* this one.\n *\n * `type={revealed ? 'text' : 'password'}` becomes RN's own\n * `secureTextEntry`, and `autoComplete=\"current-password\"` ports as-is —\n * RN's `autoComplete` accepts the same value and is what lets password\n * managers and the platform keyboard offer a saved credential. Pass\n * `\"new-password\"` on registration or change-password forms.\n *\n * The reveal control is the already-shipped `IconButton` rather than a\n * hand-rolled pressable, which costs one visual detail: `IconButton` is\n * circular (it's `Button` at `shape=\"circular\"`), where the web's\n * `.revealButton` is a 32 dp square with a 6 dp radius. A circular flat icon\n * button is the idiomatic touch control and keeps this row consistent with\n * every other icon action in the package, so it's the better trade than\n * introducing a fifth flat-pressable implementation. The CSS's resting\n * `opacity: 0.55` is dropped too: it exists so the button can brighten on\n * hover, and with no hover on a touch device a permanently dimmed control is\n * just harder to see — the same reasoning that collapses `:hover` everywhere\n * else here.\n *\n * The web needs `e.stopPropagation()` so pressing the button doesn't also\n * trigger the row's focus-the-input click. RN's responder system routes a\n * touch to the innermost pressable, so there's nothing to stop.\n *\n * @example\n * <BoxedList>\n * <PasswordEntryRow title=\"Password\" value={password} onValueChange={setPassword} />\n * </BoxedList>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.PasswordEntryRow.html\n */\nexport const PasswordEntryRow = forwardRef<TextInput, PasswordEntryRowProps>(\n function PasswordEntryRow(\n { trailing, disabled = false, autoComplete = 'current-password', ...props },\n ref,\n ) {\n const [revealed, setRevealed] = useState(false);\n\n return (\n <EntryRow\n ref={ref}\n {...props}\n autoComplete={autoComplete}\n secureTextEntry={!revealed}\n disabled={disabled}\n trailing={\n <>\n {trailing}\n <IconButton\n icon={revealed ? ViewConceal : ViewReveal}\n label={revealed ? 'Conceal password' : 'Reveal password'}\n variant=\"flat\"\n disabled={disabled}\n accessibilityState={{ selected: revealed, disabled }}\n onPress={() => setRevealed((current) => !current)}\n />\n </>\n }\n />\n );\n },\n);\n","import { ViewConceal, ViewReveal } from '@gnome-ui/icons';\nimport { forwardRef, useState } from 'react';\nimport type { StyleProp, TextInput, TextInputProps, TextStyle, ViewStyle } from 'react-native';\nimport { TextInput as RNTextInput, View } from 'react-native';\n\nimport { IconButton } from '@/components/IconButton';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface PasswordFieldProps extends Omit<TextInputProps, 'style' | 'secureTextEntry'> {\n /** Visible label rendered above the input. */\n label?: string;\n /** Helper text rendered below the input. Hidden when `error` is set. */\n helperText?: string;\n /**\n * Error message rendered below the input in place of `helperText`.\n * Also applies the error visual state to the input border.\n */\n error?: string;\n /**\n * Show the peek toggle button that reveals the password as plain text.\n * Mirrors `GtkPasswordEntry`'s `show-peek-icon` property. Defaults to `true`.\n */\n revealable?: boolean;\n /** Accessible label for the toggle button while the password is hidden. */\n revealLabel?: string;\n /** Accessible label for the toggle button while the password is revealed. */\n concealLabel?: string;\n /** Style applied to the wrapping `View`. */\n style?: StyleProp<ViewStyle>;\n /** Style applied to the `TextInput` itself. */\n inputStyle?: StyleProp<TextStyle>;\n}\n\n/**\n * Duplicated from `TextField` rather than shared — same small pure helper,\n * two call sites, not worth extracting (the same call already duplicated\n * verbatim between `TextField` and `SearchBar`).\n */\nfunction dimColor(color: string, factor: number): string {\n const match = color.match(/^rgba?\\(([^,]+),([^,]+),([^,]+),?([^)]+)?\\)$/);\n\n if (!match) {\n return color;\n }\n\n const [, r, g, b, a] = match;\n const alpha = (a ? Number.parseFloat(a) : 1) * factor;\n\n return `rgba(${r.trim()}, ${g.trim()}, ${b.trim()}, ${alpha})`;\n}\n\nconst TOGGLE_SIZE = 28;\n\n/**\n * Single-line password input with a peek toggle to reveal the value as\n * plain text, mirroring `@gnome-ui/react`'s `PasswordField`.\n *\n * `TextField` plus the exact reveal/conceal recipe `PasswordEntryRow`\n * already established for this package: `secureTextEntry` (not `revealed\n * ? 'text' : 'password'`, which has no RN equivalent), and the shipped\n * `IconButton` for the toggle rather than a hand-rolled pressable — a\n * circular icon button at `size=\"sm\"` (28 dp) here, matching the web\n * version's own `size=\"sm\"` on this component specifically (as opposed to\n * `PasswordEntryRow`'s default size, since that one sits inside a taller\n * row). The toggle is positioned absolutely at the input's trailing edge,\n * vertically centered — the RN port of the source CSS's `position:\n * absolute; inset-block-start: 50%; transform: translateY(-50%)` — and the\n * input gets extra trailing padding (`.hasToggle`'s `padding-inline-end`)\n * so typed text never runs under the button.\n *\n * Use this over `TextField` with a manually-set `secureTextEntry` (which\n * has no reveal affordance at all).\n *\n * @see https://docs.gtk.org/gtk4/class.PasswordEntry.html\n */\nexport const PasswordField = forwardRef<TextInput, PasswordFieldProps>(function PasswordField(\n {\n label,\n helperText,\n error,\n revealable = true,\n revealLabel = 'Show password',\n concealLabel = 'Hide password',\n style,\n inputStyle,\n editable = true,\n onFocus,\n onBlur,\n accessibilityLabel,\n accessibilityHint,\n ...inputProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const contrast = useResolvedContrast();\n const [focused, setFocused] = useState(false);\n const [revealed, setRevealed] = useState(false);\n const disabled = !editable;\n const hint = error ?? helperText;\n const showToggle = revealable;\n\n const borderColor = error ? theme.errorColor : focused ? theme.accentColor : theme.borderSubtle;\n\n return (\n <View style={[{ gap: theme.space1, opacity: disabled ? theme.opacityDisabled : 1 }, style]}>\n {label && (\n <Text\n variant=\"body\"\n style={{ fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'] }}\n >\n {label}\n </Text>\n )}\n\n <View style={{ position: 'relative', justifyContent: 'center' }}>\n <RNTextInput\n ref={ref}\n editable={editable}\n secureTextEntry={!revealed}\n accessibilityLabel={accessibilityLabel ?? label}\n accessibilityHint={accessibilityHint ?? hint}\n placeholderTextColor={dimColor(theme.windowFgColor, theme.opacityDim)}\n onFocus={(event) => {\n setFocused(true);\n onFocus?.(event);\n }}\n onBlur={(event) => {\n setFocused(false);\n onBlur?.(event);\n }}\n style={[\n {\n width: '100%',\n paddingVertical: 8,\n paddingHorizontal: theme.space2,\n paddingEnd: showToggle ? TOGGLE_SIZE + theme.space2 + 6 : theme.space2,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n lineHeight: Math.round(theme.fontSizeBody * theme.lineHeightBody),\n color: theme.viewFgColor,\n backgroundColor: theme.viewBgColor,\n borderRadius: theme.radiusMd,\n borderWidth: contrast === 'more' ? 2 : 1,\n borderColor,\n } satisfies TextStyle,\n inputStyle,\n ]}\n {...inputProps}\n />\n\n {showToggle && (\n <View style={{ position: 'absolute', end: 6 }}>\n <IconButton\n icon={revealed ? ViewConceal : ViewReveal}\n label={revealed ? concealLabel : revealLabel}\n variant=\"flat\"\n size=\"sm\"\n disabled={disabled}\n accessibilityState={{ selected: revealed, disabled }}\n onPress={() => setRevealed((current) => !current)}\n />\n </View>\n )}\n </View>\n\n {hint && (\n <Text\n variant=\"caption\"\n style={{\n color: error ? theme.errorColor : theme.windowFgColor,\n opacity: error ? 1 : theme.opacityDim,\n }}\n >\n {hint}\n </Text>\n )}\n </View>\n );\n});\n","import type { ReactNode } from 'react';\nimport { Fragment } from 'react';\nimport type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native';\nimport { Pressable, Text as RNText, View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface PathBarSegment {\n /** Display label for this path segment. */\n label: string;\n /** Opaque path value passed to `onNavigate` when the segment is pressed. */\n path: string;\n /** Optional icon placed before the label (e.g. a folder icon). Rendered as-is. */\n icon?: ReactNode;\n}\n\nexport interface PathBarProps extends Omit<ViewProps, 'style'> {\n /**\n * Ordered path segments from root to current location. The last segment\n * is the current folder and is rendered non-interactive.\n */\n segments: PathBarSegment[];\n /**\n * Called when the user presses a non-current segment. Receives the\n * `path` and zero-based `index` of the pressed segment.\n */\n onNavigate?: (path: string, index: number) => void;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Breadcrumb path bar for navigating a hierarchical location.\n *\n * Mirrors the location bar in GNOME Files (Nautilus). Segments are\n * separated by chevron dividers. All segments except the last are\n * interactive — pressing them calls `onNavigate`. The last segment\n * represents the current location and renders as a static, bold label.\n *\n * Rebuilt with `Pressable`/`View`/`Text` rather than ported from\n * `@gnome-ui/react`'s `<nav><ol><li>`: RN's `AccessibilityRole` union has\n * neither a \"navigation\" landmark nor a breadcrumb-list role (the same gap\n * that dropped `Sidebar`'s `<nav>` role), so those are dropped rather than\n * faked — each interactive segment still gets its own\n * `accessibilityRole=\"button\"` and `accessibilityLabel`. The separator\n * chevron is a Unicode `›` glyph instead of the web version's inline SVG\n * path, matching this package's established no-SVG-dependency convention.\n *\n * @see https://developer.gnome.org/hig/patterns/nav/search.html\n */\nexport const PathBar = ({ segments, onNavigate, style, ...viewProps }: PathBarProps) => {\n const theme = useGnomeTheme();\n\n return (\n <RNView\n style={[{ flexDirection: 'row', flexWrap: 'wrap', alignItems: 'center' }, style]}\n {...viewProps}\n >\n {segments.map((segment, index) => {\n const isCurrent = index === segments.length - 1;\n\n return (\n <Fragment key={segment.path}>\n {index > 0 && (\n <RNText\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n color: theme.windowFgColor,\n opacity: 0.5,\n marginHorizontal: 1,\n fontSize: theme.fontSizeBody,\n }}\n >\n {'›'}\n </RNText>\n )}\n\n {isCurrent ? (\n <RNView\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n gap: 4,\n paddingVertical: 2,\n paddingHorizontal: theme.space1,\n maxWidth: 200,\n }}\n >\n {segment.icon && <RNView style={{ flexShrink: 0 }}>{segment.icon}</RNView>}\n <RNText\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n color: theme.windowFgColor,\n }}\n >\n {segment.label}\n </RNText>\n </RNView>\n ) : (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={segment.label}\n onPress={() => onNavigate?.(segment.path, index)}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n gap: 4,\n paddingVertical: 2,\n paddingHorizontal: theme.space1,\n maxWidth: 160,\n borderRadius: theme.radiusSm,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n })}\n >\n {segment.icon && <RNView style={{ flexShrink: 0 }}>{segment.icon}</RNView>}\n <RNText\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n }}\n >\n {segment.label}\n </RNText>\n </Pressable>\n )}\n </Fragment>\n );\n })}\n </RNView>\n );\n};\n","import { forwardRef, type ReactNode } from 'react';\nimport type { StyleProp, TextStyle, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface PreferencesGroupProps extends Omit<ViewProps, 'style'> {\n /** Group heading. */\n title?: string;\n /** Optional description rendered below the title. */\n description?: string;\n /** Widget placed at the trailing edge of the title row (e.g. a reset `Button`). */\n headerSuffix?: ReactNode;\n /** `BoxedList` rows or any row-shaped content. */\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Titled section that wraps a `BoxedList` with an optional description,\n * mirroring `AdwPreferencesGroup` and `@gnome-ui/react`'s own\n * `PreferencesGroup`.\n *\n * Use it to group related settings under a named heading. The group is\n * purely a layout and labelling wrapper — it doesn't render the `BoxedList`\n * itself; pass one as `children`.\n *\n * The web's empty `.content` wrapper looks like dead markup but is\n * load-bearing, so it's kept: the group is a 12 dp-gap flex column, and\n * without that wrapper every child would become a flex item of the group and\n * pick up a 12 dp gap between the rows themselves, instead of one gap\n * between the header and the content as a whole.\n *\n * The title is `Text variant=\"body\"` with an explicit semibold weight rather\n * than `variant=\"heading\"`, which is body-sized but **bold** and on the\n * tighter heading line-height — the CSS `.title` is specifically semibold at\n * the body line-height. It keeps the `header` accessibility role anyway\n * (passed explicitly), since a settings-group heading is exactly the kind of\n * landmark a screen reader rotor should list — the same call `StatusPage`\n * makes for its own title.\n *\n * `min-width: 0` on the header text has no port and needs none: it's the\n * classic CSS flexbox override for a min-content floor that Yoga doesn't\n * apply in the first place.\n *\n * @example\n * <PreferencesGroup\n * title=\"Appearance\"\n * description=\"How the app looks on this device.\"\n * headerSuffix={<Button variant=\"flat\" onPress={reset}>Reset</Button>}\n * >\n * <BoxedList>{rows}</BoxedList>\n * </PreferencesGroup>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.PreferencesGroup.html\n */\nexport const PreferencesGroup = forwardRef<View, PreferencesGroupProps>(function PreferencesGroup(\n { title, description, headerSuffix, children, style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const hasHeader = Boolean(title || description || headerSuffix);\n\n return (\n <RNView ref={ref} style={[{ gap: theme.space2 }, style]} {...viewProps}>\n {hasHeader ? (\n <RNView style={{ flexDirection: 'row', alignItems: 'baseline', gap: theme.space2 }}>\n <RNView style={{ flex: 1, gap: 2 }}>\n {title ? (\n <Text\n variant=\"body\"\n accessibilityRole=\"header\"\n style={{\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n ) : null}\n\n {description ? (\n <Text variant=\"caption\" color=\"dim\">\n {description}\n </Text>\n ) : null}\n </RNView>\n\n {headerSuffix ? <RNView style={{ flexShrink: 0 }}>{headerSuffix}</RNView> : null}\n </RNView>\n ) : null}\n\n <RNView>{children}</RNView>\n </RNView>\n );\n});\n","import { useEffect, useRef, useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { Animated, Easing } from 'react-native';\n\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\n\nexport type ProgressBarVariant = 'accent' | 'success' | 'warning' | 'error';\n\nexport interface ProgressBarProps extends Omit<ViewProps, 'style'> {\n /**\n * Current progress value between `0` and `1` (e.g. `0.6` = 60%). Omit\n * or set to `undefined` for the indeterminate (pulsing) state.\n */\n value?: number;\n /**\n * Fill color of the progress indicator.\n * - `\"accent\"` (default) — blue, general-purpose progress.\n * - `\"success\"` — green, completed or healthy state.\n * - `\"warning\"` — yellow, approaching a limit.\n * - `\"error\"` — red, failed or critical state.\n */\n variant?: ProgressBarVariant;\n /** Accessible label describing what is loading. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Determinate and indeterminate progress bar following the Adwaita style.\n *\n * - **Determinate** — pass `value` (0–1) to show exact progress; the fill\n * width animates on every change, matching the web version's\n * `transition: width`.\n * - **Indeterminate** — omit `value` for a 40%-wide bar that slides left\n * to right on an `Animated.loop`, matching the web version's CSS\n * `@keyframes pulse`.\n *\n * `useReducedMotion()` is honored per the *source CSS's own* per-state\n * behavior rather than one uniform rule: determinate width changes simply\n * skip the transition (duration `0`, still an immediate jump like the CSS\n * `transition: none`), while the indeterminate pulse stops entirely and\n * freezes as a static, full-width, 50%-opacity bar — exactly what\n * `@media (prefers-reduced-motion: reduce) { .indeterminate { animation:\n * none; left: 0; width: 100%; opacity: 0.5 } }` does. This differs from\n * `Spinner`, whose reduced-motion behavior *slows* its animation instead\n * of stopping it — each component mirrors its own source CSS rather than\n * applying a single reduced-motion policy across the package.\n *\n * Both animated states are driven entirely by `transform` rather than by\n * layout properties (`width`/`left`), so both can run\n * `useNativeDriver: true`: RN's native animation driver can only animate\n * transforms, and a JS-driven (`useNativeDriver: false`) `Animated.timing`\n * or `Animated.loop` schedules its next frame via a plain `setTimeout` —\n * one that routinely fires a tick after a test's `render()` returns but\n * before unmount, outside any `act()` boundary, producing a real (if\n * harmless) \"update not wrapped in act()\" warning under\n * `@testing-library/react-native` on every render of this component. The\n * determinate fill stays a constant `width: '100%'` and instead animates\n * `transform: [{ scaleX }]` with `transformOrigin: 'left'` (so it grows\n * from the left edge, not from center); the indeterminate pulse similarly\n * animates `transform: [{ translateX }]` from `-0.4 * trackWidth` to\n * `trackWidth` (measured via `onLayout`, since a transform's `translateX`\n * needs a pixel offset, not a percentage) rather than animating `left`.\n * Both reproduce the exact same visual motion as the source CSS's\n * `width`/`left` transitions while running fully off the JS thread, which\n * eliminates the warning at its root rather than suppressing it.\n *\n * `role=\"progressbar\"` maps directly to RN's own `accessibilityRole`\n * (unlike `Spinner`, which had to substitute for the web's `role=\"status\"`\n * — `ProgressBar`'s web role already has a 1:1 RN equivalent).\n * `accessibilityValue` carries `now`/`min`/`max` for the determinate case;\n * the indeterminate case omits all three, RN's equivalent of the web\n * version omitting `aria-valuenow`/`aria-valuemin`/`aria-valuemax`. Web's\n * `aria-labelledby` (an id-relationship prop) has no RN equivalent — RN\n * has no DOM ids — so only `aria-label` (`accessibilityLabel`) is ported.\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/progress.html\n */\nexport const ProgressBar = ({\n value,\n variant = 'accent',\n accessibilityLabel,\n style,\n onLayout,\n ...viewProps\n}: ProgressBarProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const reducedMotion = useReducedMotion();\n\n const isIndeterminate = value === undefined || value === null;\n const clamped = isIndeterminate ? undefined : Math.min(1, Math.max(0, value));\n const percent = clamped !== undefined ? clamped * 100 : undefined;\n\n const width = useRef(new Animated.Value(clamped ?? 0)).current;\n const pulse = useRef(new Animated.Value(0)).current;\n const [trackWidth, setTrackWidth] = useState(0);\n\n const handleTrackLayout = (event: LayoutChangeEvent) => {\n setTrackWidth(event.nativeEvent.layout.width);\n onLayout?.(event);\n };\n\n useEffect(() => {\n if (isIndeterminate) {\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(width, {\n toValue: clamped ?? 0,\n duration: reducedMotion ? 0 : theme.durationNormal,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: true,\n }).start();\n }, [clamped, isIndeterminate, reducedMotion, width, theme.durationNormal, theme.easingDefault]);\n\n useEffect(() => {\n if (!isIndeterminate || reducedMotion) {\n return;\n }\n\n pulse.setValue(0);\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n const animation = Animated.loop(\n Animated.timing(pulse, {\n toValue: 1,\n duration: 1400,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: true,\n }),\n );\n\n animation.start();\n\n return () => animation.stop();\n }, [isIndeterminate, reducedMotion, pulse, theme.easingDefault]);\n\n const trackColor = scheme === 'dark' ? theme.dark2 : theme.light3;\n const variantColor: Record<ProgressBarVariant, string> = {\n accent: theme.accentBgColor,\n success: theme.successBgColor,\n warning: theme.warningBgColor,\n error: theme.errorBgColor,\n };\n\n const fillStyle = isIndeterminate\n ? reducedMotion\n ? { left: 0, width: '100%', opacity: 0.5 }\n : {\n left: 0,\n width: '40%',\n transform: [\n {\n translateX: pulse.interpolate({\n inputRange: [0, 1],\n outputRange: [-0.4 * trackWidth, trackWidth],\n }),\n },\n ],\n }\n : {\n width: '100%',\n transformOrigin: 'left',\n transform: [{ scaleX: width }],\n };\n\n return (\n <Animated.View\n accessible\n accessibilityRole=\"progressbar\"\n accessibilityLabel={accessibilityLabel}\n accessibilityValue={\n clamped !== undefined ? { min: 0, max: 100, now: Math.round(percent as number) } : undefined\n }\n onLayout={handleTrackLayout}\n style={[\n {\n width: '100%',\n height: 6,\n borderRadius: theme.radiusPill,\n backgroundColor: trackColor,\n overflow: 'hidden',\n },\n style,\n ]}\n {...viewProps}\n >\n <Animated.View\n style={[\n {\n position: isIndeterminate ? 'absolute' : 'relative',\n top: 0,\n height: '100%',\n borderRadius: theme.radiusPill,\n backgroundColor: variantColor[variant],\n },\n fillStyle as ViewStyle,\n ]}\n />\n </Animated.View>\n );\n};\n","import { forwardRef, useEffect, useRef } from 'react';\nimport type { PressableProps, StyleProp, View, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable } from 'react-native';\n\nimport { useGnomeTheme, useResolvedColorScheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface RadioButtonProps extends Omit<PressableProps, 'children' | 'style' | 'onPress'> {\n /** Whether this radio button is the selected one in its group. */\n value: boolean;\n /**\n * Called when pressed while unselected. RN has no `name`-attribute\n * grouping like the web `<input type=\"radio\">`, so mutual exclusivity\n * within a group is the consumer's responsibility — render one\n * `RadioButton` per option and drive `value` from shared selection\n * state, flipping it in `onSelect`.\n */\n onSelect?: () => void;\n /** Accessible label. Required when no visible label is associated. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\nconst SIZE = 18;\nconst DOT_SIZE = 6;\n\n/**\n * Single-selection radio button following the GNOME HIG and Adwaita style.\n *\n * Rebuilt on `Pressable`/`Animated.View` rather than ported from\n * `@gnome-ui/react`'s `<input type=\"radio\">`: reuses `Checkbox`'s exact\n * border/background transition technique (same `Animated.Value`, same\n * mount-skip guard so it never animates before the user touches it, same\n * `useResolvedColorScheme()`/`useResolvedContrast()` branching for the\n * idle border color, since the source CSS hardcodes those the same way\n * `Checkbox.module.css` does) — just circular, with a filled dot instead\n * of a checkmark, and no indeterminate state.\n *\n * The web version's `name`-attribute grouping has no RN equivalent, so\n * grouping is fully manual here: render one `RadioButton` per option and\n * manage the selected option in the parent, same as any other controlled\n * list of options.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/radio-buttons.html\n */\nexport const RadioButton = forwardRef<View, RadioButtonProps>(function RadioButton(\n { value, onSelect, disabled, style, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const colorScheme = useResolvedColorScheme();\n const contrast = useResolvedContrast();\n const progress = useRef(new Animated.Value(value ? 1 : 0)).current;\n const isInitialMount = useRef(true);\n\n useEffect(() => {\n // The initial `Animated.Value` above already matches `value` — skip\n // animating into a state the radio button is already rendering, so it\n // never plays a transition before the user has touched it.\n if (isInitialMount.current) {\n isInitialMount.current = false;\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: value ? 1 : 0,\n duration: theme.durationFast,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: false,\n }).start();\n }, [value, progress, theme.durationFast, theme.easingDefault]);\n\n const idleBorderColor =\n contrast === 'more' ? theme.windowFgColor : colorScheme === 'dark' ? theme.dark1 : theme.light4;\n\n const borderColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [idleBorderColor, theme.accentBgColor],\n });\n const backgroundColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.cardBgColor, theme.accentBgColor],\n });\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"radio\"\n accessibilityState={{ checked: value, disabled: !!disabled }}\n onPress={() => {\n if (!value) {\n onSelect?.();\n }\n }}\n style={[{ opacity: disabled ? theme.opacityDisabled : 1 }, style]}\n {...pressableProps}\n >\n <Animated.View\n style={{\n width: SIZE,\n height: SIZE,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusPill,\n borderWidth: contrast === 'more' ? 2 : 1.5,\n borderColor,\n backgroundColor,\n }}\n >\n <Animated.View\n style={{\n width: DOT_SIZE,\n height: DOT_SIZE,\n borderRadius: theme.radiusPill,\n backgroundColor: '#fff',\n opacity: progress,\n }}\n />\n </Animated.View>\n </Pressable>\n );\n});\n","import { useCallback, useMemo, useRef, useState } from 'react';\nimport type {\n AccessibilityActionEvent,\n GestureResponderEvent,\n LayoutChangeEvent,\n StyleProp,\n ViewProps,\n ViewStyle,\n} from 'react-native';\nimport { PanResponder, Text, View } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nconst TRACK_HEIGHT = 6;\nconst THUMB_SIZE = 22;\nconst TICK_SIZE = 4;\n\nexport interface RangeSliderMark {\n value: number;\n label?: string;\n}\n\nexport interface RangeSliderProps extends Omit<ViewProps, 'style'> {\n /** Current `[lower, upper]` values. Both must be between `min` and `max`. */\n value: [number, number];\n /** Called when either thumb moves. */\n onChange: (value: [number, number]) => void;\n /** Minimum value. Defaults to `0`. */\n min?: number;\n /** Maximum value. Defaults to `100`. */\n max?: number;\n /**\n * Granularity of each step.\n * - The `adjustable` accessibility action (VoiceOver/TalkBack swipe up/down) moves by one step.\n * Defaults to `1`.\n */\n step?: number;\n /**\n * Minimum allowed gap between the lower and upper thumb, in value units.\n * Prevents the thumbs from crossing or overlapping. Defaults to `0`.\n */\n minDistance?: number;\n /** Disables the control. */\n disabled?: boolean;\n /**\n * Marks to display along the track.\n * Each mark can have an optional label rendered below the track.\n */\n marks?: RangeSliderMark[];\n /** Accessible label for the lower-bound thumb. Defaults to `\"Minimum value\"`. */\n minLabel?: string;\n /** Accessible label for the upper-bound thumb. Defaults to `\"Maximum value\"`. */\n maxLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\nfunction clamp(v: number, min: number, max: number) {\n return Math.min(max, Math.max(min, v));\n}\n\nfunction snapToStep(v: number, min: number, step: number, decimals: number) {\n return parseFloat((Math.round((v - min) / step) * step + min).toFixed(decimals));\n}\n\nfunction countDecimals(n: number) {\n const s = n.toString();\n const dot = s.indexOf('.');\n\n return dot === -1 ? 0 : s.length - dot - 1;\n}\n\n/**\n * Dual-thumb slider for selecting a min/max range, following the Adwaita\n * `GtkScale` pattern used by `Slider` — mirrors `@gnome-ui/react`'s\n * `RangeSlider`. Distinct from `Slider` (single value) — use this for\n * range filters (price, date range, etc.) where both bounds are\n * adjustable.\n *\n * Reuses `Slider`'s exact `PanResponder`/`locationX` pixel-positioning\n * technique (no `%`-of-self transform, since RN's `transform` only takes\n * pixel offsets) rather than two separate per-thumb responders: a single\n * `PanResponder` spans the whole track and, on `onPanResponderGrant`,\n * picks whichever thumb is closer to the touch point — the same\n * nearest-thumb logic the web version's `handleTrackPointerDown` uses —\n * then that same thumb keeps following the touch for the rest of the\n * gesture (`onPanResponderMove`), exactly matching the web behavior of\n * \"the thumb chosen at touch-down stays locked to the gesture even if the\n * pointer drifts closer to the other thumb mid-drag.\" This single-\n * responder shape also stands in for the web version's separate \"drag the\n * track itself\" affordance — touching anywhere on the track immediately\n * jumps the nearest thumb there, so no separate interaction is needed.\n *\n * The web's keyboard (← / → / Page Up/Down / Home/End) has no RN\n * equivalent — same \"no keyboard to drive it\" reasoning `Slider` already\n * established — replaced by two independent `accessibilityRole=\"adjustable\"`\n * elements (one per thumb, each with its own `accessibilityValue`/\n * `onAccessibilityAction`), the native VoiceOver/TalkBack increment-\n * decrement analog. Each thumb's own visual `View` stays\n * `pointerEvents=\"none\"` (dragging is the track responder's job, not a\n * per-thumb touch target) — screen readers still reach it independently\n * through the accessibility tree, which is separate from RN's touch hit-\n * testing.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/sliders.html\n */\nexport const RangeSlider = ({\n value,\n onChange,\n min = 0,\n max = 100,\n step = 1,\n minDistance = 0,\n disabled = false,\n marks,\n minLabel = 'Minimum value',\n maxLabel = 'Maximum value',\n style,\n onLayout,\n testID,\n ...viewProps\n}: RangeSliderProps) => {\n const theme = useGnomeTheme();\n const [trackWidth, setTrackWidth] = useState(0);\n const [labelWidths, setLabelWidths] = useState<Record<number, number>>({});\n const dragIndexRef = useRef<0 | 1 | null>(null);\n\n const dp = countDecimals(step);\n const [lo, hi] = value;\n const clampedLo = clamp(lo, min, max);\n const clampedHi = clamp(hi, min, max);\n const loPct = ((clampedLo - min) / (max - min)) * 100;\n const hiPct = ((clampedHi - min) / (max - min)) * 100;\n\n const handleTrackLayout = useCallback(\n (event: LayoutChangeEvent) => {\n setTrackWidth(event.nativeEvent.layout.width);\n onLayout?.(event);\n },\n [onLayout],\n );\n\n const setValueAt = useCallback(\n (index: 0 | 1, raw: number) => {\n const snapped = snapToStep(raw, min, step, dp);\n\n if (index === 0) {\n onChange([clamp(snapped, min, hi - minDistance), hi]);\n } else {\n onChange([lo, clamp(snapped, lo + minDistance, max)]);\n }\n },\n [min, max, step, dp, minDistance, lo, hi, onChange],\n );\n\n const valueFromLocationX = useCallback(\n (locationX: number) => (locationX / trackWidth) * (max - min) + min,\n [trackWidth, min, max],\n );\n\n const nearestThumbIndex = useCallback(\n (locationX: number): 0 | 1 => {\n const raw = valueFromLocationX(locationX);\n\n return Math.abs(raw - lo) <= Math.abs(raw - hi) ? 0 : 1;\n },\n [valueFromLocationX, lo, hi],\n );\n\n const panResponder = useMemo(\n () =>\n PanResponder.create({\n onStartShouldSetPanResponder: () => !disabled,\n onMoveShouldSetPanResponder: () => !disabled,\n onPanResponderGrant: (event: GestureResponderEvent) => {\n if (trackWidth <= 0) {\n return;\n }\n\n const { locationX } = event.nativeEvent;\n const index = nearestThumbIndex(locationX);\n\n dragIndexRef.current = index;\n setValueAt(index, valueFromLocationX(locationX));\n },\n onPanResponderMove: (event: GestureResponderEvent) => {\n if (dragIndexRef.current === null || trackWidth <= 0) {\n return;\n }\n\n setValueAt(dragIndexRef.current, valueFromLocationX(event.nativeEvent.locationX));\n },\n onPanResponderRelease: () => {\n dragIndexRef.current = null;\n },\n onPanResponderTerminate: () => {\n dragIndexRef.current = null;\n },\n }),\n [disabled, trackWidth, nearestThumbIndex, setValueAt, valueFromLocationX],\n );\n\n const makeAccessibilityAction = useCallback(\n (index: 0 | 1) => (event: AccessibilityActionEvent) => {\n if (disabled) {\n return;\n }\n\n const current = index === 0 ? lo : hi;\n\n switch (event.nativeEvent.actionName) {\n case 'increment':\n setValueAt(index, current + step);\n break;\n case 'decrement':\n setValueAt(index, current - step);\n break;\n }\n },\n [disabled, lo, hi, step, setValueAt],\n );\n\n const hasMarks = marks && marks.length > 0;\n const hasLabels = hasMarks && marks.some((m) => m.label);\n\n const loThumbLeft = (loPct / 100) * trackWidth - THUMB_SIZE / 2;\n const hiThumbLeft = (hiPct / 100) * trackWidth - THUMB_SIZE / 2;\n\n return (\n <View\n testID={testID}\n style={[{ width: '100%', paddingTop: 10, paddingBottom: hasLabels ? 0 : 10 }, style]}\n {...viewProps}\n >\n <View\n testID={testID ? `${testID}-track` : undefined}\n onLayout={handleTrackLayout}\n style={{\n justifyContent: 'center',\n height: THUMB_SIZE,\n opacity: disabled ? theme.opacityDisabled : 1,\n }}\n {...panResponder.panHandlers}\n >\n <View\n pointerEvents=\"none\"\n style={{\n width: '100%',\n height: TRACK_HEIGHT,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.cardShadeColor,\n overflow: 'hidden',\n }}\n >\n <View\n style={{\n position: 'absolute',\n height: '100%',\n left: `${loPct}%`,\n width: `${hiPct - loPct}%`,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.accentBgColor,\n }}\n />\n </View>\n\n <View\n pointerEvents=\"none\"\n accessible\n accessibilityRole=\"adjustable\"\n accessibilityLabel={minLabel}\n accessibilityValue={{ min, max: hi, now: clampedLo }}\n accessibilityState={{ disabled }}\n accessibilityActions={[\n { name: 'increment', label: 'increment' },\n { name: 'decrement', label: 'decrement' },\n ]}\n onAccessibilityAction={makeAccessibilityAction(0)}\n style={{\n position: 'absolute',\n left: loThumbLeft,\n width: THUMB_SIZE,\n height: THUMB_SIZE,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.accentBgColor,\n borderWidth: 2,\n borderColor: theme.windowBgColor,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 1 },\n shadowOpacity: 0.25,\n shadowRadius: 3,\n elevation: 3,\n }}\n />\n\n <View\n pointerEvents=\"none\"\n accessible\n accessibilityRole=\"adjustable\"\n accessibilityLabel={maxLabel}\n accessibilityValue={{ min: lo, max, now: clampedHi }}\n accessibilityState={{ disabled }}\n accessibilityActions={[\n { name: 'increment', label: 'increment' },\n { name: 'decrement', label: 'decrement' },\n ]}\n onAccessibilityAction={makeAccessibilityAction(1)}\n style={{\n position: 'absolute',\n left: hiThumbLeft,\n width: THUMB_SIZE,\n height: THUMB_SIZE,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.accentBgColor,\n borderWidth: 2,\n borderColor: theme.windowBgColor,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 1 },\n shadowOpacity: 0.25,\n shadowRadius: 3,\n elevation: 3,\n }}\n />\n\n {hasMarks &&\n marks.map((m) => {\n const mPct = ((clamp(m.value, min, max) - min) / (max - min)) * 100;\n const tickLeft = (mPct / 100) * trackWidth - TICK_SIZE / 2;\n\n return (\n <View\n key={m.value}\n pointerEvents=\"none\"\n importantForAccessibility=\"no\"\n style={{\n position: 'absolute',\n left: tickLeft,\n width: TICK_SIZE,\n height: TICK_SIZE,\n borderRadius: TICK_SIZE / 2,\n backgroundColor: theme.windowBgColor,\n }}\n />\n );\n })}\n </View>\n\n {hasLabels && (\n <View\n importantForAccessibility=\"no\"\n style={{ position: 'relative', width: '100%', height: 20, marginTop: 6 }}\n >\n {marks.map((m) => {\n const mPct = ((clamp(m.value, min, max) - min) / (max - min)) * 100;\n const centerX = (mPct / 100) * trackWidth;\n const labelWidth = labelWidths[m.value] ?? 0;\n\n return (\n <Text\n key={m.value}\n numberOfLines={1}\n onLayout={(event) => {\n const {\n layout: { width },\n } = event.nativeEvent;\n\n setLabelWidths((prev) =>\n prev[m.value] === width ? prev : { ...prev, [m.value]: width },\n );\n }}\n style={{\n position: 'absolute',\n left: centerX - labelWidth / 2,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeCaption,\n color: theme.windowFgColor,\n opacity: theme.opacityDim,\n }}\n >\n {m.label}\n </Text>\n );\n })}\n </View>\n )}\n </View>\n );\n};\n","import { Star, StarOutline } from '@gnome-ui/icons';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Pressable, View } from 'react-native';\n\nimport { Icon, type IconSize } from '@/components/Icon';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface RatingStarsProps {\n /** Current rating, between `0` and `max`. */\n value: number;\n /** Number of stars. Defaults to `5`. */\n max?: number;\n /**\n * Called when the user picks a rating. Omit to render a read-only\n * display (e.g. an average rating) instead of an interactive input.\n */\n onChange?: (value: number) => void;\n /** Star size. Defaults to `\"md\"`. */\n size?: IconSize;\n /** Renders as read-only even when `onChange` is provided. */\n disabled?: boolean;\n /** Accessible label. Defaults to `\"Rating\"` (interactive) or a generated `\"N out of M stars\"` (read-only). */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Star rating display and input. Mirrors `@gnome-ui/react`'s `RatingStars`.\n *\n * Renders `role=\"radiogroup\"` of `role=\"radio\"` stars when `onChange` is\n * provided, or a static `role=\"img\"` when it isn't — e.g. for showing an\n * average/read-only rating.\n *\n * The web version's `role=\"radiogroup\"` layer also owns an `onKeyDown`\n * handler for arrow-key roving-tabindex navigation and a mouse-hover\n * preview that doesn't commit until clicked; neither has a touch\n * counterpart, so both drop — same standing convention `ToggleGroup`\n * already established for this package (its own doc comment covers the\n * reasoning). What's left, tapping a star to commit that rating, is a\n * strict subset of the web interaction, not an approximation of it.\n *\n * Each star's fill color is `tintColor={theme.warningBgColor}` rather than\n * `Icon`'s own fixed `color=\"yellow\"` (→ `theme.yellow4`, a different hex):\n * the source CSS reads `var(--gnome-warning-bg-color, #f6d32d)` directly\n * with no `color-mix()` darkening step, so the semantic warning token\n * itself — already tracking dark mode and every contrast level — is the\n * exact match, not an approximation through the fixed palette. Stars carry\n * no `label` on `Icon` (decorative — `iconAccessibilityProps` already hides\n * an unlabeled icon from the tree); the accessible name lives on the\n * container (read-only) or each `Pressable` (interactive) instead, mirroring\n * `ToggleGroupItem`'s icon-only items.\n */\nexport const RatingStars = ({\n value,\n max = 5,\n onChange,\n size = 'md',\n disabled = false,\n accessibilityLabel,\n style,\n testID,\n}: RatingStarsProps) => {\n const theme = useGnomeTheme();\n const interactive = Boolean(onChange) && !disabled;\n const clamped = Math.min(max, Math.max(0, value));\n const stars = Array.from({ length: max }, (_, i) => i + 1);\n\n if (!interactive) {\n return (\n <View\n testID={testID}\n accessible\n role=\"img\"\n accessibilityLabel={accessibilityLabel ?? `${clamped} out of ${max} stars`}\n style={[{ flexDirection: 'row', alignItems: 'center', gap: 2 }, style]}\n >\n {stars.map((star) => (\n <Icon\n key={star}\n icon={star <= clamped ? Star : StarOutline}\n size={size}\n tintColor={theme.warningBgColor}\n />\n ))}\n </View>\n );\n }\n\n return (\n <View\n testID={testID}\n role=\"radiogroup\"\n accessibilityLabel={accessibilityLabel ?? 'Rating'}\n style={[{ flexDirection: 'row', alignItems: 'center', gap: 2 }, style]}\n >\n {stars.map((star) => (\n <Pressable\n key={star}\n role=\"radio\"\n accessibilityState={{ checked: star === clamped }}\n accessibilityLabel={`${star} ${star === 1 ? 'star' : 'stars'}`}\n onPress={() => onChange?.(star)}\n style={{ padding: 2, borderRadius: theme.radiusSm }}\n >\n <Icon\n icon={star <= clamped ? Star : StarOutline}\n size={size}\n tintColor={theme.warningBgColor}\n />\n </Pressable>\n ))}\n </View>\n );\n};\n","import { GoUp } from '@gnome-ui/icons';\nimport { forwardRef } from 'react';\nimport type { StyleProp, View, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { IconButton } from '@/components/IconButton';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\n/**\n * Controls when the button is rendered.\n *\n * - `\"auto\"` — hidden until `scrollY` exceeds `threshold` (default).\n * - `\"always\"` — always rendered regardless of scroll position.\n */\nexport type ScrollToTopVisible = 'always' | 'auto';\n\n/**\n * Anchor corner or edge for the absolutely-positioned container.\n * The button is inset `theme.space4` (24 dp) from each named edge.\n */\nexport type ScrollToTopPosition =\n | 'bottom-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'top-right'\n | 'top-left'\n | 'top-center';\n\nexport interface ScrollToTopProps {\n /**\n * Controls when the button is visible.\n *\n * - `\"auto\"` (default) — shown only once `scrollY` exceeds `threshold`.\n * - `\"always\"` — permanently visible.\n */\n visible?: ScrollToTopVisible;\n /**\n * Corner or edge where the button is anchored. Default: `\"bottom-right\"`.\n */\n position?: ScrollToTopPosition;\n /**\n * Offset the user must scroll past before the button appears.\n * Only relevant when `visible=\"auto\"`. Default: `300`.\n */\n threshold?: number;\n /**\n * The observed `ScrollView`/`FlatList`'s current vertical scroll offset —\n * pass `nativeEvent.contentOffset.y` from its `onScroll` handler. Only\n * relevant when `visible=\"auto\"`; ignored (and safe to omit) otherwise.\n * Default: `0`.\n */\n scrollY?: number;\n /**\n * Called when the button is pressed. There's no RN equivalent of the web\n * version's own `scrollTarget.scrollTo({ top: 0, behavior: 'smooth' })` —\n * `ScrollView`/`FlatList` are scrolled through a ref's imperative\n * `scrollTo`/`scrollToOffset`, which this component has no way to hold on\n * a consumer's behalf. Typically\n * `() => scrollViewRef.current?.scrollTo({ y: 0, animated: true })`.\n */\n onPress: () => void;\n /**\n * Extra inset added on top of the base edge spacing when anchored to a\n * `\"top-*\"` position — pass your own `useSafeAreaInsets().top` so the\n * button clears a notch/status bar. Same \"no new peer dependency\" call\n * `BottomTabBar`'s `bottomInset` already made. Default: `0`.\n */\n topInset?: number;\n /** Same as `topInset`, for a `\"bottom-*\"` position. Default: `0`. */\n bottomInset?: number;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nfunction getPositionStyle(\n position: ScrollToTopPosition,\n space4: number,\n topInset: number,\n bottomInset: number,\n): ViewStyle {\n switch (position) {\n case 'bottom-right':\n return { bottom: space4 + bottomInset, right: space4 };\n case 'bottom-left':\n return { bottom: space4 + bottomInset, left: space4 };\n case 'bottom-center':\n return { bottom: space4 + bottomInset, left: 0, right: 0, alignItems: 'center' };\n case 'top-right':\n return { top: space4 + topInset, right: space4 };\n case 'top-left':\n return { top: space4 + topInset, left: space4 };\n case 'top-center':\n return { top: space4 + topInset, left: 0, right: 0, alignItems: 'center' };\n }\n}\n\n/**\n * Absolutely-positioned OSD button that scrolls a `ScrollView`/`FlatList`\n * back to the top — mirrors `@gnome-ui/react`'s `ScrollToTop`, reimagined\n * rather than ported 1:1 per this package's own ROADMAP note: RN has no\n * page-level scroll event to observe internally the way the web version's\n * `useScrollToTopVisibility` attaches a `window`/element `scroll` listener,\n * so that hook doesn't port at all — `visible=\"auto\"` is instead a pure\n * function of a `scrollY` prop the consumer feeds from their own\n * `ScrollView`'s `onScroll`, re-evaluated on every render with no internal\n * state or listener needed.\n *\n * Same \"no `document.body`/portal target\" gap `Toaster` already\n * documents — mount this yourself as the last child of the `View` wrapping\n * your scrollable content (left at its default relative positioning) so it\n * paints on top; `pointerEvents=\"box-none\"` (the same technique `Toaster`\n * uses) keeps the empty space around the button from intercepting touches\n * meant for the content underneath.\n *\n * The web version's resting `opacity: 0.5`-until-hover/focus is dropped —\n * the same call `PasswordEntryRow`'s reveal button already made: that\n * effect exists purely so the control can brighten on hover, and touch has\n * no hover, so a permanently dimmed control would just be harder to see.\n *\n * Forwards `ref` to the root positioning `View`, not the inner button —\n * matching the web version's own `forwardRef` target.\n *\n * @example\n * // Minimal — appears once `scrollY` exceeds 300, anchored bottom-right\n * const [scrollY, setScrollY] = useState(0);\n * const scrollRef = useRef<ScrollView>(null);\n * <View style={{ flex: 1 }}>\n * <ScrollView\n * ref={scrollRef}\n * onScroll={(e) => setScrollY(e.nativeEvent.contentOffset.y)}\n * scrollEventThrottle={16}\n * >\n * ...\n * </ScrollView>\n * <ScrollToTop\n * scrollY={scrollY}\n * onPress={() => scrollRef.current?.scrollTo({ y: 0, animated: true })}\n * />\n * </View>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ScrollToTop.html\n */\nexport const ScrollToTop = forwardRef<View, ScrollToTopProps>(function ScrollToTop(\n {\n visible = 'auto',\n position = 'bottom-right',\n threshold = 300,\n scrollY = 0,\n onPress,\n topInset = 0,\n bottomInset = 0,\n style,\n testID,\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const isVisible = visible === 'always' || scrollY > threshold;\n\n if (!isVisible) {\n return null;\n }\n\n return (\n <RNView\n ref={ref}\n testID={testID}\n pointerEvents=\"box-none\"\n style={[\n { position: 'absolute', zIndex: 9999 },\n getPositionStyle(position, theme.space4, topInset, bottomInset),\n style,\n ]}\n >\n <IconButton icon={GoUp} label=\"Scroll to top\" variant=\"osd\" onPress={onPress} />\n </RNView>\n );\n});\n","import { forwardRef, type ReactNode, useEffect, useImperativeHandle, useRef } from 'react';\nimport type { StyleProp, TextInput, TextInputProps, TextStyle, ViewStyle } from 'react-native';\nimport { Pressable, Text as RNText, TextInput as RNTextInput, View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface SearchBarProps extends Omit<TextInputProps, 'style'> {\n /** Whether the search bar is rendered. `false` renders nothing. */\n open: boolean;\n /**\n * Renders a trailing \"Cancel\" button (the RN-idiomatic stand-in for the\n * web version's Escape-to-close — touch keyboards have no reliable\n * Escape key) and calls this when it's pressed.\n */\n onClose?: () => void;\n /** Called when the clear (×) button is pressed. Also clears the input value. */\n onClear?: () => void;\n /**\n * Content rendered below the bar (e.g. filter chips). Only rendered\n * while `open` is true.\n */\n children?: ReactNode;\n /** Removes the header-bar background so the bar blends into any surface. */\n inline?: boolean;\n /** Accessible label for the close button. Defaults to `\"Cancel\"`. */\n closeLabel?: string;\n /** Style applied to the wrapping `View`. */\n style?: StyleProp<ViewStyle>;\n /** Style applied to the `TextInput` itself. */\n inputStyle?: StyleProp<TextStyle>;\n}\n\n/**\n * Multiplies an `rgba(r, g, b, a)` token's alpha channel. Duplicated from\n * `TextField` rather than shared — see that component for the full\n * rationale; it's small and self-contained enough that a shared module\n * isn't worth it for two call sites.\n */\nfunction dimColor(color: string, factor: number): string {\n const match = color.match(/^rgba?\\(([^,]+),([^,]+),([^,]+),?([^)]+)?\\)$/);\n\n if (!match) {\n return color;\n }\n\n const [, r, g, b, a] = match;\n const alpha = (a ? Number.parseFloat(a) : 1) * factor;\n\n return `rgba(${r.trim()}, ${g.trim()}, ${b.trim()}, ${alpha})`;\n}\n\n/**\n * Collapsible search input following the Adwaita `AdwSearchBar` pattern.\n *\n * Rebuilt on RN's `TextInput` rather than ported from `@gnome-ui/react`'s\n * `<input type=\"search\">`: `open` no longer drives a CSS height/opacity\n * transition (no established animated-height pattern exists yet in this\n * package — same trade-off `SidebarSection` made for its collapsible body)\n * — `open={false}` renders nothing at all, and mounting on `open={true}`\n * auto-focuses the input, standing in for the web version's\n * `requestAnimationFrame`-on-open focus effect.\n *\n * Dropped relative to `@gnome-ui/react`'s `SearchBar`: the `suggestions` /\n * `onSuggestionSelect` / `loadingSuggestions` / `renderSuggestion` /\n * `suggestionsLabel` autocomplete popover — it depends on a portal +\n * viewport-anchored positioning primitive (`createPortal` +\n * `getBoundingClientRect`) this package doesn't have yet, the same gap\n * that dropped `SidebarItem`'s `menuItems` context menu — and a `Spinner`\n * component, not yet ported. The search/clear icons are Unicode glyphs\n * (`🔍`/`×`) rather than `@gnome-ui/icons`, matching every other\n * no-SVG-dependency component in this package.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.SearchBar.html\n */\nexport const SearchBar = forwardRef<TextInput, SearchBarProps>(function SearchBar(\n {\n open,\n onClose,\n onClear,\n children,\n value,\n placeholder = 'Search…',\n editable = true,\n inline = false,\n closeLabel = 'Cancel',\n style,\n inputStyle,\n ...inputProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const inputRef = useRef<TextInput>(null);\n\n useImperativeHandle(ref, () => inputRef.current as TextInput, []);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n const id = requestAnimationFrame(() => inputRef.current?.focus());\n\n return () => cancelAnimationFrame(id);\n }, [open]);\n\n if (!open) {\n return null;\n }\n\n const disabled = !editable;\n const hasValue = typeof value === 'string' && value.length > 0;\n\n const barBg = inline ? 'transparent' : theme.headerbarBgColor;\n const barBorder = inline ? 'transparent' : theme.headerbarShadeColor;\n\n return (\n <RNView style={style}>\n <RNView style={{ flexDirection: 'row', alignItems: 'center', gap: theme.space1 }}>\n <RNView\n style={{\n flex: 1,\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space1,\n paddingVertical: theme.space1,\n paddingHorizontal: theme.space2,\n backgroundColor: barBg,\n borderBottomWidth: 1,\n borderBottomColor: barBorder,\n opacity: disabled ? theme.opacityDisabled : 1,\n }}\n >\n <RNText\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{ opacity: theme.opacityDim, fontSize: theme.fontSizeBody }}\n >\n {'🔍'}\n </RNText>\n\n <RNTextInput\n ref={inputRef}\n editable={editable}\n value={value}\n placeholder={placeholder}\n placeholderTextColor={dimColor(theme.windowFgColor, theme.opacityDim)}\n style={[\n {\n flex: 1,\n padding: 0,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n lineHeight: Math.round(theme.fontSizeBody * theme.lineHeightBody),\n color: theme.windowFgColor,\n } satisfies TextStyle,\n inputStyle,\n ]}\n {...inputProps}\n />\n\n {hasValue && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel=\"Clear search\"\n disabled={disabled}\n hitSlop={8}\n onPress={() => {\n onClear?.();\n inputRef.current?.focus();\n }}\n style={({ pressed }) => ({\n width: 24,\n height: 24,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusSm,\n opacity: disabled ? theme.opacityDisabled : 1,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n })}\n >\n <RNText style={{ fontSize: theme.fontSizeBody, color: theme.windowFgColor }}>\n {'×'}\n </RNText>\n </Pressable>\n )}\n </RNView>\n\n {onClose && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={closeLabel}\n onPress={onClose}\n hitSlop={8}\n >\n <RNText\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n color: theme.accentColor,\n }}\n >\n {closeLabel}\n </RNText>\n </Pressable>\n )}\n </RNView>\n\n {children && (\n <RNView\n style={{\n flexDirection: 'row',\n flexWrap: 'wrap',\n gap: theme.space1,\n padding: theme.space1,\n paddingHorizontal: theme.space2,\n backgroundColor: barBg,\n borderBottomWidth: 1,\n borderBottomColor: barBorder,\n }}\n >\n {children}\n </RNView>\n )}\n </RNView>\n );\n});\n","import { useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Pressable, View } from 'react-native';\n\nimport { Tooltip } from '@/components/Tooltip';\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport interface SegmentedBarSegment {\n /** Category name shown in the tooltip. */\n label: string;\n /**\n * Percentage value 0–100.\n * The sum of all segments should equal 100.\n * If it does not, values are redistributed proportionally.\n */\n value: number;\n /**\n * Color for this segment.\n * When omitted, a cycling palette of GNOME design tokens is used.\n */\n color?: string;\n}\n\nexport interface SegmentedBarProps {\n /** Segments to display. Each segment contributes its share of the full bar width. */\n values: SegmentedBarSegment[];\n /**\n * Accessible label for the bar as a whole.\n * Auto-generated from `values` when omitted (e.g. \"TypeScript 60%, JavaScript 30%\").\n */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst COLOR_COUNT = 8;\n\nfunction paletteColor(theme: GnomeThemeTokens, index: number): string {\n switch (index % COLOR_COUNT) {\n case 0:\n return theme.blue3;\n case 1:\n return theme.green3;\n case 2:\n return theme.purple3;\n case 3:\n return theme.orange3;\n case 4:\n return theme.red3;\n case 5:\n return theme.brown3;\n case 6:\n return theme.blue5;\n default:\n return theme.green5;\n }\n}\n\n/**\n * Horizontal bar split into proportional segments, one per category.\n * Mirrors `@gnome-ui/react`'s `SegmentedBar`. Typical use case: repository\n * language distribution.\n *\n * ```tsx\n * <SegmentedBar\n * values={[\n * { label: 'TypeScript', value: 60, color: '#3178c6' },\n * { label: 'JavaScript', value: 30, color: '#f7df1e' },\n * { label: 'CSS', value: 10, color: '#563d7c' },\n * ]}\n * />\n * ```\n *\n * The web version's hover interaction (dim every segment but the one under\n * the pointer, brighten that one via `filter: brightness()`) is rebuilt for\n * touch rather than dropped: each segment is a `Pressable`, and touching\n * one dims the rest immediately via `onPressIn`/`onPressOut` — deliberately\n * not gated behind `Tooltip`'s own long-press delay, since this feedback is\n * the RN analog of a `Pressable`'s own instant `pressed` state, not the\n * \"peek\" affordance a tooltip reveal is. Each segment is also wrapped in\n * `Tooltip` (`label`/`placement=\"top\"`/`delay={200}`, ported 1:1 from the\n * web version's own tooltip) for the actual label/percentage readout,\n * composing cleanly with the dim/highlight `onPressIn`/`onPressOut` since\n * `Tooltip` clones its own handlers onto the child *and* still calls the\n * child's original ones. `filter: brightness(1.15)` on the actively-touched\n * segment has no RN equivalent (no `filter` support) — dropped as a\n * decorative nicety, since the touched segment already reads as\n * highlighted by contrast once every other segment dims to 35% opacity.\n *\n * `role=\"img\"` + `accessibilityLabel` ports 1:1 from RN's newer web-aligned\n * `Role` union (the same `Avatar`/`LevelBar` precedent).\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/progress.html\n */\nexport const SegmentedBar = ({ values, accessibilityLabel, style, testID }: SegmentedBarProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n\n const total = values.reduce((sum, s) => sum + s.value, 0) || 100;\n const defaultLabel = values.map((s) => `${s.label} ${s.value}%`).join(', ');\n const trackColor = scheme === 'dark' ? theme.dark2 : theme.light3;\n\n return (\n <View\n testID={testID}\n accessible\n role=\"img\"\n accessibilityLabel={accessibilityLabel ?? defaultLabel}\n style={[\n {\n flexDirection: 'row',\n width: '100%',\n height: 8,\n borderRadius: theme.radiusPill,\n overflow: 'hidden',\n gap: 2,\n backgroundColor: trackColor,\n },\n style,\n ]}\n >\n {values.map((segment, i) => {\n const width = `${(segment.value / total) * 100}%` as const;\n const pct = segment.value % 1 === 0 ? segment.value : segment.value.toFixed(1);\n const tooltipLabel = `${segment.label}: ${pct}%`;\n\n return (\n <Tooltip key={segment.label} label={tooltipLabel} placement=\"top\" delay={200}>\n <Pressable\n testID=\"segmented-bar-segment\"\n onPressIn={() => setActiveIndex(i)}\n onPressOut={() => setActiveIndex(null)}\n style={{\n width,\n height: '100%',\n backgroundColor: segment.color ?? paletteColor(theme, i),\n opacity: activeIndex !== null && activeIndex !== i ? 0.35 : 1,\n }}\n />\n </Tooltip>\n );\n })}\n </View>\n );\n};\n","import { Children, isValidElement, type ReactNode } from 'react';\n\n/** Recursively count items whose `label` prop matches `filter` (case-insensitive). */\nexport function countMatchingItems(children: ReactNode, filter: string): number {\n let count = 0;\n\n Children.forEach(children, (child) => {\n if (!isValidElement(child)) {\n return;\n }\n\n const props = child.props as Record<string, unknown>;\n\n if (typeof props.label === 'string') {\n if (props.label.toLowerCase().includes(filter.toLowerCase())) {\n count++;\n }\n } else if (props.children) {\n count += countMatchingItems(props.children as ReactNode, filter);\n }\n });\n\n return count;\n}\n\n/**\n * Whether a single top-level `Sidebar` child would render something under\n * `filter` — a bare `SidebarItem` if its own `label` matches, a\n * `SidebarSection` (or anything else with `children`) if any descendant\n * matches. Used to decide which children get a `Separator` between them, so\n * a filtered-out (null-rendering) item doesn't still claim a divider slot.\n */\nexport function isChildVisible(child: ReactNode, filter: string): boolean {\n if (!filter) {\n return true;\n }\n\n if (!isValidElement(child)) {\n return true;\n }\n\n const props = child.props as Record<string, unknown>;\n\n if (typeof props.label === 'string') {\n return props.label.toLowerCase().includes(filter.toLowerCase());\n }\n\n if (props.children) {\n return countMatchingItems(props.children as ReactNode, filter) > 0;\n }\n\n return true;\n}\n","import { Children, createContext, Fragment, forwardRef, type ReactNode, useContext } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView, ScrollView } from 'react-native';\n\nimport { Separator } from '@/components/Separator';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nimport { isChildVisible } from './filterUtils';\n\n// ─── Contexts ───────────────────────────────────────────────────────────────\n\n/** Provides the collapsed state to all descendant `SidebarSection`/`SidebarItem`. */\n// eslint-disable-next-line react-refresh/only-export-components\nexport const SidebarCollapsedContext = createContext(false);\n\n/** Returns `true` when the nearest `Sidebar` is in collapsed (icon-only/rail) mode. */\n// eslint-disable-next-line react-refresh/only-export-components\nexport function useSidebarCollapsed() {\n return useContext(SidebarCollapsedContext);\n}\n\n/** Provides the active filter string to all descendant `SidebarItem`/`SidebarSection`. */\n// eslint-disable-next-line react-refresh/only-export-components\nexport const SidebarFilterContext = createContext('');\n\n// ─── Props ──────────────────────────────────────────────────────────────────\n\nexport interface SidebarProps extends Omit<ViewProps, 'style'> {\n children?: ReactNode;\n /**\n * When `true`, collapses the sidebar to icon-only (rail) mode. Labels and\n * section titles are hidden.\n */\n collapsed?: boolean;\n /**\n * Controlled filter string. `SidebarItem`s whose `label` does not match\n * (case-insensitive substring) are hidden, and empty `SidebarSection`s are\n * hidden entirely. Unlike the web version this never renders a search\n * input itself — pair it with your own `TextField` (or the future\n * `SearchBar` port) and drive this prop from its value.\n */\n filter?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Lateral navigation panel following the Adwaita `.navigation-sidebar` style.\n *\n * Compose with `SidebarSection` (named groups) and `SidebarItem` (rows).\n * Consecutive top-level children get a `Separator` inserted between them —\n * same divider-on-index-boundary technique `BoxedList` uses for its rows —\n * standing in for the web version's `.section + .section` adjacent-sibling\n * CSS rule, which RN has no equivalent of.\n *\n * Dropped relative to `@gnome-ui/react`'s `Sidebar`: `searchable` (would\n * pull in a `SearchBar`, not yet ported to this package — use `filter` with\n * your own input instead), `mode`/auto page-layout switch (depends on the\n * web-only `useBreakpoint` hook), `variant` (tinted/blurred backgrounds —\n * the blurred variant needs a native blur view this package doesn't depend\n * on), and the `<nav>` landmark role (RN's `AccessibilityRole` union has no\n * \"navigation\" value).\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Sidebar.html\n */\nexport const Sidebar = forwardRef<View, SidebarProps>(function Sidebar(\n { children, collapsed = false, filter = '', style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const isFilterActive = filter.length > 0;\n const items = Children.toArray(children)\n .filter(Boolean)\n .filter((child) => isChildVisible(child, filter));\n\n const hasMatches = !isFilterActive || items.length > 0;\n\n return (\n <SidebarCollapsedContext.Provider value={collapsed}>\n <SidebarFilterContext.Provider value={filter}>\n <RNView\n ref={ref}\n style={[\n {\n flexShrink: 0,\n width: collapsed ? theme.layoutSidebarRailWidth : theme.layoutSidebarDefaultWidth,\n backgroundColor: theme.sidebarBgColor,\n borderRightWidth: 1,\n borderRightColor: theme.headerbarBorderColor,\n },\n style,\n ]}\n {...viewProps}\n >\n <ScrollView contentContainerStyle={{ padding: theme.space1 }}>\n {isFilterActive && !hasMatches ? (\n <Text\n variant=\"caption\"\n color=\"dim\"\n style={{ textAlign: 'center', padding: theme.space4 }}\n >\n No items match your search.\n </Text>\n ) : (\n items.map((child, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: children have no natural stable id\n <Fragment key={i}>\n {i > 0 && (\n <Separator\n testID=\"sidebar-separator\"\n style={{ marginVertical: theme.space1 }}\n />\n )}\n {child}\n </Fragment>\n ))\n )}\n </ScrollView>\n </RNView>\n </SidebarFilterContext.Provider>\n </SidebarCollapsedContext.Provider>\n );\n});\n","import { forwardRef, type ReactNode, useContext } from 'react';\nimport type { PressableProps, StyleProp, TextStyle, View, ViewStyle } from 'react-native';\nimport { Pressable, View as RNView } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nimport { SidebarFilterContext, useSidebarCollapsed } from './Sidebar';\n\nexport interface SidebarItemProps extends Omit<PressableProps, 'children' | 'style' | 'disabled'> {\n /** Primary label. */\n label: string;\n /**\n * Icon placed at the leading edge. Rendered as-is — size/color it\n * yourself. Web's `icon` takes an `@gnome-ui/icons` `IconDefinition`;\n * this package has no SVG icon system, so it's a plain `ReactNode` here,\n * same as `ActionRow`'s `leading` and `TabItem`'s `icon`.\n */\n icon?: ReactNode;\n /** Marks this item as the currently active view. */\n active?: boolean;\n /** Trailing widget — badge, count, button, icon… */\n suffix?: ReactNode;\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Individual navigation row inside a `Sidebar` or `SidebarSection`.\n *\n * Automatically hidden (renders nothing) when the nearest `Sidebar`'s\n * `filter` is active and `label` doesn't match. In collapsed (rail) mode\n * the label and suffix are hidden and the icon centers — `label` still\n * reaches screen readers via `accessibilityLabel`.\n *\n * Dropped relative to `@gnome-ui/react`'s `SidebarItem`: `tooltip` (no\n * `Tooltip` port yet, and RN's touch-first model has no hover to trigger\n * one from), `menuItems` (context menu — no portal/positioning primitive\n * exists in this package yet), and `onDrop`/`acceptTypes` (HTML5\n * drag-and-drop has no RN equivalent without a gesture-handler dependency\n * this package doesn't have).\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Sidebar.html\n */\nexport const SidebarItem = forwardRef<View, SidebarItemProps>(function SidebarItem(\n { label, icon, active = false, suffix, disabled, style, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const collapsed = useSidebarCollapsed();\n const filterValue = useContext(SidebarFilterContext);\n\n const isFiltered =\n filterValue.length > 0 && !label.toLowerCase().includes(filterValue.toLowerCase());\n\n if (isFiltered) {\n return null;\n }\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"button\"\n accessibilityState={{ selected: active, disabled: !!disabled }}\n accessibilityLabel={label}\n style={({ pressed }) => [\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: 8,\n paddingHorizontal: theme.space2,\n borderRadius: theme.radiusMd,\n opacity: disabled ? theme.opacityDisabled : 1,\n backgroundColor: active\n ? theme.accentBgColor\n : pressed\n ? theme.hoverOverlay\n : 'transparent',\n },\n collapsed && { justifyContent: 'center', paddingHorizontal: 0 },\n style,\n ]}\n {...pressableProps}\n >\n {icon && <RNView style={{ flexShrink: 0, opacity: active ? 1 : 0.85 }}>{icon}</RNView>}\n\n {!collapsed && (\n <Text\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n flex: 1,\n fontSize: theme.fontSizeBody,\n fontWeight: String(\n active ? theme.fontWeightSemibold : theme.fontWeightNormal,\n ) as TextStyle['fontWeight'],\n color: active ? theme.accentFgColor : theme.sidebarFgColor,\n }}\n >\n {label}\n </Text>\n )}\n\n {!collapsed && suffix && <RNView style={{ flexShrink: 0 }}>{suffix}</RNView>}\n </Pressable>\n );\n});\n","import { forwardRef, type ReactNode, useContext, useImperativeHandle, useState } from 'react';\nimport type { StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { Pressable, View as RNView } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\nimport { countMatchingItems } from './filterUtils';\nimport { SidebarFilterContext, useSidebarCollapsed } from './Sidebar';\n\nexport interface SidebarSectionHandle {\n expand: () => void;\n collapse: () => void;\n toggle: () => void;\n}\n\nexport interface SidebarSectionProps extends Omit<ViewProps, 'style'> {\n /** Section heading. Omit for an untitled section (e.g. the first group in a sidebar). */\n title?: string;\n /** Icon rendered left of the title. Rendered as-is — size/color it yourself. */\n icon?: ReactNode;\n /** Whether the section body can be toggled open/closed. Defaults to `false`. */\n collapsible?: boolean;\n /** Initial open state when `collapsible` is true. Defaults to `true`. */\n defaultOpen?: boolean;\n /** Controlled open state. */\n open?: boolean;\n /** Called when open state changes. */\n onOpenChange?: (open: boolean) => void;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Named group of `SidebarItem` entries inside a `Sidebar`.\n *\n * When `collapsible`, the body toggles via the header `Pressable` or\n * imperatively via a `ref` (`expand`/`collapse`/`toggle`) — same\n * `useImperativeHandle` shape as the web version's `SidebarSectionHandle`.\n * The body stays mounted and toggles `display: 'none'` rather than\n * unmounting, the same \"stays mounted but hidden\" approach `TabPanel` uses,\n * instead of porting the web version's animated CSS-grid collapse (no\n * established animated-height pattern exists yet in this package).\n *\n * In rail (`collapsed`) mode the header — and with it the collapse toggle —\n * is hidden and the body is always shown, matching the web version's rail\n * behavior. When a `Sidebar` filter is active and no descendant matches,\n * the whole section renders nothing.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Sidebar.html\n */\nexport const SidebarSection = forwardRef<SidebarSectionHandle, SidebarSectionProps>(\n function SidebarSection(\n {\n title,\n icon,\n collapsible = false,\n defaultOpen = true,\n open: controlledOpen,\n onOpenChange,\n children,\n style,\n ...viewProps\n },\n ref,\n ) {\n const theme = useGnomeTheme();\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const isControlled = controlledOpen !== undefined;\n const open = isControlled ? controlledOpen : internalOpen;\n\n const collapsed = useSidebarCollapsed();\n const filterValue = useContext(SidebarFilterContext);\n\n const setAndNotify = (next: boolean) => {\n if (!isControlled) {\n setInternalOpen(next);\n }\n\n onOpenChange?.(next);\n };\n\n useImperativeHandle(ref, () => ({\n expand: () => setAndNotify(true),\n collapse: () => setAndNotify(false),\n toggle: () => setAndNotify(!open),\n }));\n\n const hasVisibleChildren =\n filterValue.length === 0 || countMatchingItems(children, filterValue) > 0;\n\n if (!hasVisibleChildren) {\n return null;\n }\n\n const isOpen = collapsed ? true : open;\n const showHeader = !collapsed && !!(title || icon);\n\n const header = (title || icon) && (\n <RNView\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space1,\n paddingHorizontal: theme.space1,\n paddingVertical: 2,\n marginTop: 4,\n marginBottom: 2,\n }}\n >\n {icon && <RNView style={{ flexShrink: 0, opacity: 0.7 }}>{icon}</RNView>}\n {title && (\n <Text variant=\"caption-heading\" color=\"dim\" style={{ flex: 1 }} numberOfLines={1}>\n {title}\n </Text>\n )}\n {collapsible && (\n <Text variant=\"caption\" color=\"dim\" style={{ flexShrink: 0 }}>\n {open ? '▾' : '▸'}\n </Text>\n )}\n </RNView>\n );\n\n return (\n <RNView style={style} {...viewProps}>\n {showHeader &&\n (collapsible ? (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ expanded: open }}\n accessibilityLabel={title}\n onPress={() => setAndNotify(!open)}\n >\n {header}\n </Pressable>\n ) : (\n header\n ))}\n\n <RNView\n accessible\n accessibilityRole=\"list\"\n style={{ display: isOpen ? 'flex' : 'none', gap: 2 }}\n >\n {children}\n </RNView>\n </RNView>\n );\n },\n);\n","import { useEffect, useRef } from 'react';\nimport type { DimensionValue, StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { Animated, Easing, View as RNView } from 'react-native';\n\nimport { useGnomeTheme, useReducedMotion } from '@/GnomeProvider';\n\nexport type SkeletonVariant = 'rect' | 'circle' | 'text';\n\nexport interface SkeletonProps extends Omit<ViewProps, 'style'> {\n /** Shape of the placeholder. Defaults to `\"rect\"`. */\n variant?: SkeletonVariant;\n /** Width for rectangular placeholders. Defaults to `\"100%\"`. */\n width?: DimensionValue;\n /** Height for rectangular placeholders. Defaults to `16`. */\n height?: DimensionValue;\n /** Diameter for circular placeholders. Defaults to `40`. */\n size?: number;\n /** Number of rows for text placeholders. Defaults to `3`. */\n lines?: number;\n /** Enables the pulse animation. Respects `useReducedMotion()`. Defaults to `true`. */\n animated?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Loading placeholder for content-shaped skeleton screens.\n *\n * GNOME HIG recommends `Spinner` or `ProgressBar` for loading states; this\n * is a pragmatic web-style extension for layouts that benefit from\n * placeholder shape, ported as-is from `@gnome-ui/react`.\n *\n * The web version's shimmer is a `linear-gradient` swept across the shape\n * via `transform: translateX()`; this package has no gradient dependency\n * (no `expo-linear-gradient`/`react-native-linear-gradient` in its\n * dependency tree, and adding one for a single component would be scope\n * creep), so the animation is a plain opacity pulse instead — animating\n * `theme.skeletonBaseColor`'s own opacity between `1` and `0.5` on a\n * 1.4s round trip, the same cycle length as the web shimmer. This is a\n * common RN-idiomatic substitute for a CSS shimmer (compare Tailwind's own\n * `animate-pulse` utility, which uses the identical technique). Unlike\n * `Spinner` (slows) and `ProgressBar` (stops one state, slows the other),\n * `useReducedMotion()` here fully disables the pulse and shows a static\n * base color — mirroring the source CSS's own\n * `@media (prefers-reduced-motion: reduce) { animation: none }`, which has\n * no partial-motion in-between state to preserve.\n *\n * `theme.skeletonBaseColor` already resolves correctly per color scheme\n * (unlike `Spinner`'s track color, which the source CSS never actually\n * tokenizes), so no `useResolvedColorScheme()` branching is needed here.\n *\n * `accessible={false}` mirrors the web version's `aria-hidden=\"true\"` — a\n * loading placeholder carries no information a screen reader user needs,\n * same reasoning `Separator` already established for a purely decorative\n * element.\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/progress.html\n */\nexport const Skeleton = ({\n variant = 'rect',\n width = '100%',\n height = 16,\n size = 40,\n lines = 3,\n animated = true,\n style,\n ...viewProps\n}: SkeletonProps) => {\n const theme = useGnomeTheme();\n const reducedMotion = useReducedMotion();\n const pulse = useRef(new Animated.Value(1)).current;\n\n const isPulsing = animated && !reducedMotion;\n\n useEffect(() => {\n if (!isPulsing) {\n pulse.setValue(1);\n\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n const easing = Easing.bezier(x1, y1, x2, y2);\n\n const animation = Animated.loop(\n Animated.sequence([\n Animated.timing(pulse, { toValue: 0.5, duration: 700, easing, useNativeDriver: true }),\n Animated.timing(pulse, { toValue: 1, duration: 700, easing, useNativeDriver: true }),\n ]),\n );\n\n animation.start();\n\n return () => animation.stop();\n }, [isPulsing, pulse, theme.easingDefault]);\n\n if (variant === 'text') {\n const lineCount = Math.max(1, Math.floor(lines));\n\n return (\n <RNView accessible={false} style={[{ gap: 8 }, style]} {...viewProps}>\n {Array.from({ length: lineCount }, (_, index) => (\n <Animated.View\n // biome-ignore lint/suspicious/noArrayIndexKey: lines have no natural stable id\n key={index}\n style={{\n width: index === lineCount - 1 ? '60%' : '100%',\n height: 12,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.skeletonBaseColor,\n opacity: pulse,\n }}\n />\n ))}\n </RNView>\n );\n }\n\n const shapeStyle: ViewStyle =\n variant === 'circle'\n ? { width: size, height: size, borderRadius: size / 2 }\n : { width, height, borderRadius: theme.radiusMd };\n\n return (\n <Animated.View\n accessible={false}\n style={[shapeStyle, { backgroundColor: theme.skeletonBaseColor, opacity: pulse }, style]}\n {...viewProps}\n />\n );\n};\n","import { useCallback, useMemo, useState } from 'react';\nimport type {\n AccessibilityActionEvent,\n GestureResponderEvent,\n LayoutChangeEvent,\n StyleProp,\n ViewProps,\n ViewStyle,\n} from 'react-native';\nimport { PanResponder, Text, View } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nconst TRACK_HEIGHT = 6;\nconst THUMB_SIZE = 22;\nconst TICK_SIZE = 4;\n\nexport interface SliderMark {\n value: number;\n label?: string;\n}\n\nexport interface SliderProps extends Omit<ViewProps, 'style'> {\n /** Current value. Must be between `min` and `max`. */\n value: number;\n /** Called when the value changes. */\n onChange: (value: number) => void;\n /** Minimum value. Defaults to `0`. */\n min?: number;\n /** Maximum value. Defaults to `100`. */\n max?: number;\n /**\n * Granularity of each step.\n * - The `adjustable` accessibility action (VoiceOver/TalkBack swipe up/down) moves by one step.\n * Defaults to `1`.\n */\n step?: number;\n /** Disables the control. */\n disabled?: boolean;\n /**\n * Marks to display along the track.\n * Each mark can have an optional label rendered below the track.\n */\n marks?: SliderMark[];\n /** Accessible label. Required — RN has no visible-label association to fall back on. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\nfunction clamp(v: number, min: number, max: number) {\n return Math.min(max, Math.max(min, v));\n}\n\nfunction snapToStep(v: number, min: number, step: number, decimals: number) {\n return parseFloat((Math.round((v - min) / step) * step + min).toFixed(decimals));\n}\n\nfunction countDecimals(n: number) {\n const s = n.toString();\n const dot = s.indexOf('.');\n\n return dot === -1 ? 0 : s.length - dot - 1;\n}\n\n/**\n * Draggable range slider following the Adwaita `GtkScale` pattern.\n *\n * - Touch drag (via `PanResponder`) sets the value continuously.\n * - `accessibilityRole=\"adjustable\"` + `onAccessibilityAction` handles the\n * \"increment\"/\"decrement\" actions VoiceOver (swipe up/down) and TalkBack\n * generate for an adjustable element — RN's native analog of the web\n * version's ← / → keyboard stepping, since a touch-first device has no\n * keyboard to drive `ArrowLeft`/`ArrowRight`/`Home`/`End`/`PageUp`/\n * `PageDown` with. Those bigger/edge jumps have no equivalent gesture on\n * either platform's screen reader, so only single-step increment/decrement\n * is ported.\n * - Optional tick marks with labels.\n * - Fills the track from the left up to the thumb (accent colour).\n *\n * RN's `transform` only accepts pixel offsets, not the CSS `%` units the\n * web version's `left: X%; transform: translate(-50%, -50%)` centering\n * trick relies on — so the thumb and tick marks are positioned with a\n * plain pixel `left` (`(pct / 100) * trackWidth - size / 2`, measured via\n * `onLayout`) instead, and vertical centering comes from the track's own\n * `justifyContent: 'center'` rather than a transform.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/sliders.html\n */\nexport const Slider = ({\n value,\n onChange,\n min = 0,\n max = 100,\n step = 1,\n disabled = false,\n marks,\n accessibilityLabel,\n style,\n onLayout,\n ...viewProps\n}: SliderProps) => {\n const theme = useGnomeTheme();\n const [trackWidth, setTrackWidth] = useState(0);\n const [labelWidths, setLabelWidths] = useState<Record<number, number>>({});\n\n const dp = countDecimals(step);\n const clamped = clamp(value, min, max);\n const pct = ((clamped - min) / (max - min)) * 100;\n\n const handleTrackLayout = useCallback(\n (event: LayoutChangeEvent) => {\n setTrackWidth(event.nativeEvent.layout.width);\n onLayout?.(event);\n },\n [onLayout],\n );\n\n const commit = useCallback(\n (locationX: number) => {\n if (disabled || trackWidth <= 0) {\n return;\n }\n\n const raw = (locationX / trackWidth) * (max - min) + min;\n\n onChange(clamp(snapToStep(raw, min, step, dp), min, max));\n },\n [disabled, trackWidth, min, max, step, dp, onChange],\n );\n\n const panResponder = useMemo(\n () =>\n PanResponder.create({\n onStartShouldSetPanResponder: () => !disabled,\n onMoveShouldSetPanResponder: () => !disabled,\n onPanResponderGrant: (event: GestureResponderEvent) => commit(event.nativeEvent.locationX),\n onPanResponderMove: (event: GestureResponderEvent) => commit(event.nativeEvent.locationX),\n }),\n [disabled, commit],\n );\n\n const handleAccessibilityAction = useCallback(\n (event: AccessibilityActionEvent) => {\n if (disabled) {\n return;\n }\n\n switch (event.nativeEvent.actionName) {\n case 'increment':\n onChange(clamp(snapToStep(value + step, min, step, dp), min, max));\n break;\n case 'decrement':\n onChange(clamp(snapToStep(value - step, min, step, dp), min, max));\n break;\n }\n },\n [disabled, value, step, min, max, dp, onChange],\n );\n\n const hasMarks = marks && marks.length > 0;\n const hasLabels = hasMarks && marks.some((m) => m.label);\n\n const thumbLeft = (pct / 100) * trackWidth - THUMB_SIZE / 2;\n\n return (\n <View\n style={[{ width: '100%', paddingTop: 10, paddingBottom: hasLabels ? 0 : 10 }, style]}\n {...viewProps}\n >\n <View\n onLayout={handleTrackLayout}\n accessible\n accessibilityRole=\"adjustable\"\n accessibilityLabel={accessibilityLabel}\n accessibilityValue={{ min, max, now: clamped }}\n accessibilityState={{ disabled }}\n accessibilityActions={[\n { name: 'increment', label: 'increment' },\n { name: 'decrement', label: 'decrement' },\n ]}\n onAccessibilityAction={handleAccessibilityAction}\n style={{\n justifyContent: 'center',\n height: THUMB_SIZE,\n opacity: disabled ? theme.opacityDisabled : 1,\n }}\n {...panResponder.panHandlers}\n >\n <View\n pointerEvents=\"none\"\n style={{\n width: '100%',\n height: TRACK_HEIGHT,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.cardShadeColor,\n overflow: 'hidden',\n }}\n >\n <View\n style={{\n height: '100%',\n width: `${pct}%`,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.accentBgColor,\n }}\n />\n </View>\n\n <View\n pointerEvents=\"none\"\n style={{\n position: 'absolute',\n left: thumbLeft,\n width: THUMB_SIZE,\n height: THUMB_SIZE,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.accentBgColor,\n borderWidth: 2,\n borderColor: theme.windowBgColor,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 1 },\n shadowOpacity: 0.25,\n shadowRadius: 3,\n elevation: 3,\n }}\n />\n\n {hasMarks &&\n marks.map((m) => {\n const mPct = ((clamp(m.value, min, max) - min) / (max - min)) * 100;\n const tickLeft = (mPct / 100) * trackWidth - TICK_SIZE / 2;\n\n return (\n <View\n key={m.value}\n pointerEvents=\"none\"\n importantForAccessibility=\"no\"\n style={{\n position: 'absolute',\n left: tickLeft,\n width: TICK_SIZE,\n height: TICK_SIZE,\n borderRadius: TICK_SIZE / 2,\n backgroundColor: theme.windowBgColor,\n }}\n />\n );\n })}\n </View>\n\n {hasLabels && (\n <View\n importantForAccessibility=\"no\"\n style={{ position: 'relative', width: '100%', height: 20, marginTop: 6 }}\n >\n {marks.map((m) => {\n const mPct = ((clamp(m.value, min, max) - min) / (max - min)) * 100;\n const centerX = (mPct / 100) * trackWidth;\n const labelWidth = labelWidths[m.value] ?? 0;\n\n return (\n // Positioned by a pixel `left` computed from the label's own\n // measured width (0 until its first `onLayout`, so it starts\n // left-aligned to the mark and snaps to centered a frame\n // later) — a zero-width `View` with `alignItems: 'center'`\n // was tried first hoping Yoga would center an overflowing\n // child around it the way a CSS `%`-of-self `transform:\n // translateX(-50%)` does, but the iOS Simulator screenshot\n // check (never skip it) showed the labels not rendering at\n // all, so self-measurement replaced it.\n <Text\n key={m.value}\n numberOfLines={1}\n onLayout={(event) => {\n const {\n layout: { width },\n } = event.nativeEvent;\n\n setLabelWidths((prev) =>\n prev[m.value] === width ? prev : { ...prev, [m.value]: width },\n );\n }}\n style={{\n position: 'absolute',\n left: centerX - labelWidth / 2,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeCaption,\n color: theme.windowFgColor,\n opacity: theme.opacityDim,\n }}\n >\n {m.label}\n </Text>\n );\n })}\n </View>\n )}\n </View>\n );\n};\n","import { forwardRef } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nexport interface SpacerProps extends Omit<ViewProps, 'style'> {\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Invisible `flex: 1` filler for `Toolbar` and `HeaderBar` — mirrors\n * `@gnome-ui/react`'s `Spacer`. Place between leading and trailing groups\n * to push trailing items to the end.\n *\n * `accessible={false}` mirrors the web version's `aria-hidden=\"true\"` —\n * same \"purely decorative, exclude from the accessibility tree entirely\"\n * call `Separator` already made, rather than reaching for `role`'s newer\n * `\"separator\"` value (which exists but implies a divider a screen\n * reader user might care about; a plain flex filler has no such meaning).\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest/style-classes.html#spacer\n *\n * @example\n * <Toolbar>\n * <Button variant=\"flat\">Back</Button>\n * <Spacer />\n * <Button variant=\"flat\">Done</Button>\n * </Toolbar>\n */\nexport const Spacer = forwardRef<View, SpacerProps>(function Spacer({ style, ...viewProps }, ref) {\n return (\n <RNView ref={ref} accessible={false} style={[{ flex: 1, minWidth: 0 }, style]} {...viewProps} />\n );\n});\n","import { useEffect, useRef } from 'react';\nimport type { StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { Animated, Easing } from 'react-native';\n\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\n\nexport type SpinnerSize = 'sm' | 'md' | 'lg';\n\nconst SIZES: Record<SpinnerSize, { dimension: number; borderWidth: number }> = {\n sm: { dimension: 16, borderWidth: 2 },\n md: { dimension: 24, borderWidth: 2.5 },\n lg: { dimension: 36, borderWidth: 3 },\n};\n\nexport interface SpinnerProps extends Omit<ViewProps, 'style'> {\n /** Size of the spinner. Defaults to `\"md\"`. */\n size?: SpinnerSize;\n /**\n * Accessible label announced by screen readers. Defaults to\n * `\"Loading…\"`. Set to `\"\"` to silence if a sibling label is present.\n */\n label?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Indeterminate loading indicator following the Adwaita spinner style.\n *\n * Rebuilt on `Animated.View` rather than ported from `@gnome-ui/react`'s\n * pure-CSS `@keyframes spin` animation: the ring itself reuses the same\n * per-side-border trick the CSS does (`borderColor` for the track,\n * `borderTopColor` for the accent-colored \"head\" of the spinner, on a\n * `borderRadius: 50%` circle) — that part translates directly, since RN's\n * `View` supports independent per-side border colors too. The rotation\n * itself is an `Animated.loop`d `Animated.timing` driving a `rotate`\n * transform, `useNativeDriver: true` since only a transform is animated.\n *\n * `useReducedMotion()` mirrors the source CSS's own\n * `@media (prefers-reduced-motion: reduce) { animation-duration: 2s }` —\n * slowed to 2s, not stopped outright, exactly matching the web behavior\n * rather than dropping the animation entirely.\n *\n * RN's `AccessibilityRole` union has no \"status\" value (the web version's\n * `role=\"status\"`); `\"progressbar\"` is the closest match for an\n * indeterminate loading indicator and is what's used here — omitting\n * `accessibilityValue` is RN's equivalent of omitting `aria-valuenow` for\n * an indeterminate progress bar. `@gnome-ui/react`'s two hardcoded\n * per-color-scheme track colors (the `--gnome-spinner-track-color` CSS\n * variable is referenced but never actually defined in `@gnome-ui/core`'s\n * tokens, so the CSS fallback value is always what renders) are ported as\n * the same hardcoded `rgba()` pair, branched on `useResolvedColorScheme()`\n * — the same pattern `Switch`/`Checkbox`/`RadioButton` already use for\n * colors the source CSS hardcodes rather than tokenizes.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/style-classes.html\n */\nexport const Spinner = ({ size = 'md', label = 'Loading…', style, ...viewProps }: SpinnerProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const reducedMotion = useReducedMotion();\n\n const spin = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n spin.setValue(0);\n\n const animation = Animated.loop(\n Animated.timing(spin, {\n toValue: 1,\n duration: reducedMotion ? 2000 : 750,\n easing: Easing.linear,\n useNativeDriver: true,\n }),\n );\n\n animation.start();\n\n return () => animation.stop();\n }, [spin, reducedMotion]);\n\n const rotate = spin.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '360deg'] });\n const { dimension, borderWidth } = SIZES[size];\n const trackColor = scheme === 'dark' ? 'rgba(255, 255, 255, 0.15)' : 'rgba(0, 0, 0, 0.15)';\n\n return (\n <Animated.View\n accessible\n accessibilityRole=\"progressbar\"\n accessibilityLabel={label || undefined}\n accessibilityElementsHidden={label === ''}\n importantForAccessibility={label === '' ? 'no-hide-descendants' : 'yes'}\n style={[\n {\n width: dimension,\n height: dimension,\n borderRadius: dimension / 2,\n borderWidth,\n borderColor: trackColor,\n borderTopColor: theme.accentColor,\n transform: [{ rotate }],\n },\n style,\n ]}\n {...viewProps}\n />\n );\n};\n","import { type ReactNode, useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\n\nimport { ActionRow } from '@/components/ActionRow';\nimport { SpinButton } from '@/components/SpinButton';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface SpinRowProps {\n /** Primary label. */\n title: string;\n /** Secondary line below the title. */\n subtitle?: string;\n /** Icon or image placed at the leading edge. */\n leading?: ReactNode;\n /** Current value (controlled). */\n value?: number;\n /** Initial value when uncontrolled. Defaults to `0`. */\n defaultValue?: number;\n /** Called when the value changes. */\n onValueChange?: (value: number) => void;\n /** Minimum allowed value. Defaults to `0`. */\n min?: number;\n /** Maximum allowed value. Defaults to `100`. */\n max?: number;\n /** Amount to increment/decrement per step. Defaults to `1`. */\n step?: number;\n /** Number of decimal places shown. Derived from `step` when omitted. */\n decimals?: number;\n /** Accessible name for the spin button. Defaults to `title`. */\n accessibilityLabel?: string;\n /** Disables the row and its spin button. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/**\n * Settings row with an integrated spin button for numeric values, mirroring\n * `AdwSpinRow` and `@gnome-ui/react`'s own `SpinRow`. Use inside a\n * `BoxedList` for settings with numeric ranges (volume, timeout duration,\n * count limits, etc.).\n *\n * **This is a composition of `ActionRow` + `SpinButton`** — same reasoning\n * as `ComboRow` (`ActionRow` + `Dropdown`): the row layout and the numeric\n * stepper are each already shipped, verified components, so nothing here is\n * rebuilt. `SpinButton` is controlled-only, so the uncontrolled `defaultValue`\n * state lives here, one level up, the same shape `ComboRow` already\n * established for `Dropdown`.\n *\n * The web version's keyboard interaction (↑/↓ one step, Page Up/Down ten\n * steps, Home/End to bounds) drops as it does everywhere else in this\n * package — `SpinButton` already provides the touch/screen-reader\n * equivalents it was built with (tap the visible −/+ buttons, or the\n * `accessibilityRole=\"adjustable\"` increment/decrement actions).\n *\n * @example\n * <BoxedList>\n * <SpinRow\n * title=\"Volume\"\n * subtitle=\"Output level\"\n * value={volume}\n * onValueChange={setVolume}\n * min={0}\n * max={100}\n * />\n * </BoxedList>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.SpinRow.html\n */\nexport const SpinRow = ({\n title,\n subtitle,\n leading,\n value: controlledValue,\n defaultValue = 0,\n onValueChange,\n min = 0,\n max = 100,\n step = 1,\n decimals,\n accessibilityLabel,\n disabled = false,\n style,\n testID,\n}: SpinRowProps) => {\n const theme = useGnomeTheme();\n const isControlled = controlledValue !== undefined;\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const value = isControlled ? controlledValue : uncontrolledValue;\n\n return (\n <ActionRow\n testID={testID}\n title={title}\n subtitle={subtitle}\n leading={leading}\n disabled={disabled}\n style={[disabled ? { opacity: theme.opacityDisabled } : null, style]}\n trailing={\n <SpinButton\n value={value}\n min={min}\n max={max}\n step={step}\n decimals={decimals}\n disabled={disabled}\n accessibilityLabel={accessibilityLabel ?? title}\n onChange={(next) => {\n if (!isControlled) {\n setUncontrolledValue(next);\n }\n\n onValueChange?.(next);\n }}\n />\n }\n />\n );\n};\n","import { PanDown } from '@gnome-ui/icons';\nimport { type ReactNode, useState } from 'react';\nimport type { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native';\nimport { Pressable, View } from 'react-native';\n\nimport { Button } from '@/components/Button';\nimport { Icon } from '@/components/Icon';\nimport { Popover } from '@/components/Popover';\nimport { useGnomeTheme } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type SplitButtonVariant = 'default' | 'suggested' | 'destructive';\n\nexport interface SplitButtonProps {\n /** Label shown in the primary button. */\n label: string;\n /** Visual style. Applies to both the primary and toggle halves. */\n variant?: SplitButtonVariant;\n /** Content rendered inside the popover panel when the arrow is pressed. */\n dropdownContent: ReactNode;\n /** Accessible label for the dropdown toggle button. Defaults to `\"More options\"`. */\n dropdownLabel?: string;\n /** Called when the primary (label) half is pressed. */\n onPress?: (event: GestureResponderEvent) => void;\n /** Disables both halves. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\ninterface VariantColors {\n backgroundColor: string;\n borderColor: string;\n color: string;\n separatorColor: string;\n}\n\n/**\n * Mirrors `Button`'s own (unexported) `getVariantColors` for the\n * non-`osd`/non-pressed resting state, so the hand-rolled toggle half below\n * renders pixel-identical colors to the real `Button` used for the primary\n * half — small enough to duplicate rather than export/import across\n * components, same call as `dimColor()`'s duplication elsewhere in this\n * package.\n */\nfunction getVariantColors(theme: GnomeThemeTokens, variant: SplitButtonVariant): VariantColors {\n switch (variant) {\n case 'suggested':\n return {\n backgroundColor: theme.accentBgColor,\n borderColor: 'transparent',\n color: theme.accentFgColor,\n separatorColor: 'rgba(255, 255, 255, 0.25)',\n };\n case 'destructive':\n return {\n backgroundColor: theme.destructiveBgColor,\n borderColor: 'transparent',\n color: theme.destructiveFgColor,\n separatorColor: 'rgba(255, 255, 255, 0.25)',\n };\n default:\n return {\n backgroundColor: theme.cardBgColor,\n borderColor: theme.light3,\n color: theme.windowFgColor,\n separatorColor: theme.light3,\n };\n }\n}\n\n/**\n * Primary action button with an attached dropdown arrow, mirroring\n * `AdwSplitButton` and `@gnome-ui/react`'s own `SplitButton`. Pressing the\n * label half fires `onPress`; pressing the arrow half opens a floating\n * panel with `dropdownContent` (menus, options, etc.).\n *\n * The primary half is a real `Button` — its resting/pressed/disabled colors\n * come for free. The arrow half is a hand-rolled `Pressable` rather than a\n * second `Button`: nesting `Popover` (whose `children` clones a prop-level\n * `accessibilityState`/`onPress` onto its trigger) around a full `Button`\n * would silently clobber `Button`'s own internal `accessibilityState={{\n * disabled }}` — RN merges a spread prop object outright, it doesn't merge\n * key-by-key — so the arrow half derives the exact same variant colors via\n * `getVariantColors` above instead, and owns `accessibilityState` itself\n * (only `{ disabled }`, letting `Popover`'s clone merge in `expanded`).\n * The two halves are visually connected by zeroing the shared inner corner\n * radii and painting a 1px separator between them, the RN equivalent of the\n * web CSS's `border-radius` split + `.separator` span.\n *\n * `Popover` supplies the floating panel, `Modal`-based positioning,\n * outside-tap dismissal, and Android back-button handling — no repositioning\n * math of its own needed here, unlike the web version's manual\n * `getBoundingClientRect`/scroll-and-resize-tracking `place()` effect.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.SplitButton.html\n */\nexport const SplitButton = ({\n label,\n variant = 'default',\n dropdownContent,\n dropdownLabel = 'More options',\n onPress,\n disabled = false,\n style,\n testID,\n}: SplitButtonProps) => {\n const theme = useGnomeTheme();\n const [open, setOpen] = useState(false);\n const colors = getVariantColors(theme, variant);\n const solid = variant === 'suggested' || variant === 'destructive';\n\n return (\n <View\n testID={testID}\n style={[\n {\n flexDirection: 'row',\n alignSelf: 'flex-start',\n alignItems: 'stretch',\n borderRadius: theme.radiusMd,\n overflow: 'hidden',\n },\n style,\n ]}\n >\n <Button\n variant={variant}\n disabled={disabled}\n onPress={onPress}\n style={{ borderTopRightRadius: 0, borderBottomRightRadius: 0 }}\n >\n {label}\n </Button>\n\n <View style={{ width: 1, backgroundColor: colors.separatorColor }} />\n\n <Popover content={dropdownContent} open={open} onOpenChange={setOpen} placement=\"bottom\">\n <Pressable\n disabled={disabled}\n accessibilityRole=\"button\"\n accessibilityLabel={dropdownLabel}\n accessibilityState={{ disabled }}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: 34,\n minWidth: 36,\n paddingHorizontal: theme.space1,\n borderWidth: 1,\n borderColor: colors.borderColor,\n borderTopRightRadius: theme.radiusMd,\n borderBottomRightRadius: theme.radiusMd,\n backgroundColor: pressed && !solid ? theme.light3 : colors.backgroundColor,\n opacity: disabled ? theme.opacityDisabled : pressed && solid ? 0.85 : 1,\n })}\n >\n <Icon\n icon={PanDown}\n size=\"md\"\n tintColor={colors.color}\n style={{ transform: [{ rotate: open ? '180deg' : '0deg' }] }}\n />\n </Pressable>\n </Popover>\n </View>\n );\n};\n","import type { ReactNode } from 'react';\nimport type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native';\nimport { Text, View } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\nimport type { GnomeThemeTokens } from '@/theme';\n\nexport type StatusBadgeVariant = 'success' | 'warning' | 'error' | 'new' | 'accent' | 'neutral';\n\nexport interface StatusBadgeProps extends Omit<ViewProps, 'style'> {\n /**\n * Visual style. Defaults to `\"neutral\"`.\n * - `success` — green, for published / active states.\n * - `warning` — yellow, for beta / pending states.\n * - `error` — red, for failed / rejected states.\n * - `new` — purple, for newly released / featured items.\n * - `accent` — blue, for highlighted / primary states.\n * - `neutral` — muted overlay, for draft / archived / inactive states.\n */\n variant?: StatusBadgeVariant;\n /** Short human-readable label. String/number render as a themed label; other nodes render as-is. */\n children: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\ninterface VariantColors {\n backgroundColor: string;\n color: string;\n}\n\nfunction getVariantColors(theme: GnomeThemeTokens, variant: StatusBadgeVariant): VariantColors {\n switch (variant) {\n case 'success':\n return { backgroundColor: theme.successBgColor, color: theme.successFgColor };\n case 'warning':\n return { backgroundColor: theme.warningBgColor, color: theme.warningFgColor };\n case 'error':\n return { backgroundColor: theme.errorBgColor, color: theme.errorFgColor };\n case 'new':\n return { backgroundColor: theme.newBgColor, color: theme.newFgColor };\n case 'accent':\n return { backgroundColor: theme.accentBgColor, color: theme.accentFgColor };\n case 'neutral':\n return { backgroundColor: theme.hoverOverlay, color: theme.windowFgColor };\n }\n}\n\n/**\n * Pill-shaped text label for entity status — published, beta, new, etc.,\n * mirroring `@gnome-ui/react`'s `StatusBadge`.\n *\n * Unlike `Badge` (numeric counts, optional anchor/dot mode), `StatusBadge`\n * is a simpler sibling designed purely for short human-readable state\n * labels — no anchor positioning, no dot mode, no counter. The variant\n * color mapping reuses `Badge`'s exact `getVariantColors` shape (this\n * component gets its own copy rather than importing `Badge`'s internal\n * helper, since it isn't exported — same small-duplicated-helper precedent\n * as `dimColor` across `TextField`/`SearchBar`/`PasswordField`), plus a\n * `new` (purple) variant `Badge` doesn't have. `neutral`'s background is\n * `theme.hoverOverlay` — a translucent overlay, matching the source CSS's\n * `--gnome-hover-overlay` exactly — rather than `Badge`'s own flat\n * `light4`/`dark2` neutral, since this component's source CSS genuinely\n * differs from `Badge`'s on that one variant.\n *\n * `children` follows the same convention `Badge` already established:\n * string/number renders as a themed `Text` label, any other node renders\n * as-is.\n *\n * @example\n * <StatusBadge variant=\"success\">published</StatusBadge>\n * <StatusBadge variant=\"warning\">beta</StatusBadge>\n * <StatusBadge variant=\"new\">new</StatusBadge>\n *\n * @see https://developer.gnome.org/hig/patterns/feedback/badges.html\n */\nexport const StatusBadge = ({\n variant = 'neutral',\n children,\n style,\n ...viewProps\n}: StatusBadgeProps) => {\n const theme = useGnomeTheme();\n const { backgroundColor, color } = getVariantColors(theme, variant);\n\n return (\n <View\n style={[\n {\n alignSelf: 'flex-start',\n paddingVertical: 2,\n paddingHorizontal: 8,\n borderRadius: theme.radiusPill,\n backgroundColor,\n },\n style,\n ]}\n {...viewProps}\n >\n {typeof children === 'string' || typeof children === 'number' ? (\n <Text\n style={{\n fontFamily: theme.fontFamily,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n fontSize: 11,\n lineHeight: 15,\n letterSpacing: 0.22,\n color,\n }}\n >\n {children}\n </Text>\n ) : (\n children\n )}\n </View>\n );\n};\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport { forwardRef, type ReactNode } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { WrapBox } from '@/components/WrapBox';\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\n\nexport interface StatusPageProps extends Omit<ViewProps, 'style'> {\n /**\n * Large icon displayed above the title.\n * Use an icon from `@gnome-ui/icons` or omit for a text-only page.\n */\n icon?: IconDefinition;\n /**\n * Custom icon node. Use when you need an image, emoji, or a rendered\n * SVG that is not part of `@gnome-ui/icons`.\n * Ignored when `icon` is also provided.\n */\n iconNode?: ReactNode;\n /** Main heading. Keep it short — one noun phrase. */\n title: string;\n /** Supporting description rendered below the title. */\n description?: string;\n /**\n * Optional action area — typically one or two `Button`s.\n * Rendered below the description, wrapping onto a second line if needed.\n */\n children?: ReactNode;\n /**\n * Reduces padding, icon size, and title scale for use in compact\n * contexts such as sidebars, popovers, and small panels.\n */\n compact?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * The description's `max-width: 36ch` has no RN unit to port to. `ch` is\n * the advance width of \"0\", ≈ 0.5em in the sans faces Adwaita uses, so the\n * cap is resolved against the description's own font size — keeping the\n * measure font-relative the way the CSS is, rather than freezing it at one\n * pixel value that would be wrong in `compact`.\n */\nconst measureFor = (fontSize: number) => Math.round(36 * 0.5 * fontSize);\n\n/**\n * Empty-state / status page following the Adwaita `AdwStatusPage` pattern,\n * mirroring `@gnome-ui/react`'s own `StatusPage`.\n *\n * Use to fill a view with no content yet, an error state, or a completion\n * confirmation. Always explain *why* the view is empty and *what the user\n * can do* about it — don't use it for loading states, where `Spinner` or\n * `ProgressBar` belong instead.\n *\n * Centres its content on both axes, but — exactly as in the web version —\n * the vertical centring only does anything once a parent gives it height:\n * put it in a `flex: 1` container to fill the view.\n *\n * The title renders through this package's `Text` at `variant=\"title-1\"`\n * (`\"title-4\"` when `compact`), which means it also picks up `Text`'s\n * automatic `header` accessibility role — a deliberate divergence from the\n * web version's `<p class=\"title\">`. That `<p>` exists because HTML forces\n * you to pick a concrete `h1`–`h6` level for a component that has no idea\n * where it sits in the document outline; RN's `header` role carries no\n * level, so the dilemma disappears and the title can be what it actually\n * is. On a touch device the rotor is the only structural navigation a\n * screen reader user has, so this is worth having.\n *\n * The icon is dimmed by its wrapper's `opacity` (0.55 light / 0.45 dark,\n * the two values the web version's own `@media (prefers-color-scheme)`\n * block hardcodes) and hidden from assistive tech with the\n * `accessibilityElementsHidden` + `importantForAccessibility=\"no\"` pair\n * this package already uses in place of `aria-hidden`. Its color needs no\n * handling at all: `Icon` already defaults to the theme foreground, which\n * is what `.iconWrap`'s `color` sets.\n *\n * The action area is a `WrapBox` rather than a hand-rolled row — `.actions`\n * is `display: flex; flex-wrap: wrap; justify-content: center; gap` and\n * nothing else, which is exactly what that component already is.\n *\n * @example\n * <StatusPage\n * icon={StarOutline}\n * title=\"No favorites yet\"\n * description=\"Packages you star will show up here.\"\n * >\n * <Button variant=\"suggested\" onPress={onAdd}>Add a package</Button>\n * </StatusPage>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.StatusPage.html\n * @see https://developer.gnome.org/hig/patterns/feedback/empty-states.html\n */\nexport const StatusPage = forwardRef<View, StatusPageProps>(function StatusPage(\n { icon, iconNode, title, description, children, compact = false, style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const colorScheme = useResolvedColorScheme();\n\n const iconSize = compact ? 64 : 128;\n const iconContent = icon ? <Icon icon={icon} width={iconSize} height={iconSize} /> : iconNode;\n const descriptionFontSize = compact ? theme.fontSizeCaption : theme.fontSizeBody;\n\n return (\n <RNView\n ref={ref}\n style={[\n {\n width: '100%',\n alignItems: 'center',\n justifyContent: 'center',\n paddingVertical: compact ? theme.space3 : theme.space6,\n paddingHorizontal: compact ? theme.space3 : theme.space4,\n },\n style,\n ]}\n {...viewProps}\n >\n {iconContent ? (\n <RNView\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n alignItems: 'center',\n justifyContent: 'center',\n marginBottom: compact ? theme.space2 : theme.space4,\n opacity: colorScheme === 'dark' ? 0.45 : 0.55,\n }}\n >\n {iconContent}\n </RNView>\n ) : null}\n\n <Text variant={compact ? 'title-4' : 'title-1'} style={{ textAlign: 'center' }}>\n {title}\n </Text>\n\n {description ? (\n <Text\n variant={compact ? 'caption' : 'body'}\n color=\"dim\"\n style={{\n textAlign: 'center',\n maxWidth: measureFor(descriptionFontSize),\n marginTop: compact ? theme.space1 : theme.space2,\n }}\n >\n {description}\n </Text>\n ) : null}\n\n {children ? (\n <WrapBox\n justify=\"center\"\n childSpacing={compact ? theme.space1 : theme.space2}\n style={{ marginTop: compact ? theme.space2 : theme.space4 }}\n >\n {children}\n </WrapBox>\n ) : null}\n </RNView>\n );\n});\n","import { Check } from '@gnome-ui/icons';\nimport { useEffect, useRef } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable, View } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport type StepIndicatorOrientation = 'horizontal' | 'vertical';\n\nexport interface StepIndicatorProps {\n /**\n * Total number of steps, or an array of per-step labels rendered beside\n * (vertical) or beneath (horizontal) each circle. Pass a plain number for\n * an unlabelled sequence (only the \"Step X of Y\" caption is shown); pass\n * an array of strings to label each step.\n */\n steps: number | string[];\n /** Zero-based index of the current/active step. */\n currentStep: number;\n /** Layout direction. Default: `\"horizontal\"`. */\n orientation?: StepIndicatorOrientation;\n /**\n * Called when a completed step's circle is pressed, letting the user jump\n * back to a step they've already finished. Omit to make steps\n * non-interactive. The current and upcoming steps are never pressable.\n */\n onStepClick?: (index: number) => void;\n /** Accessible label for the indicator. Default: `\"Progress\"`. */\n label?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst CIRCLE_SIZE = 28;\nconst CONNECTOR_THICKNESS = 2;\n/** Matches `@gnome-ui/react`'s `.stepItem::after { inset-block-start: 14px }` (horizontal). */\nconst HORIZONTAL_CONNECTOR_TOP = 14;\n/** Matches the vertical `::after`'s `inset-inline-start`/`inset-block-start`. */\nconst VERTICAL_CONNECTOR_LEFT = 14;\nconst VERTICAL_CONNECTOR_TOP = 30;\n\ninterface StepCircleProps {\n index: number;\n label?: string;\n isCompleted: boolean;\n isCurrent: boolean;\n onStepClick?: (index: number) => void;\n}\n\n/**\n * One step's circle — its own component (not inlined in the parent's\n * `.map()`) purely so each circle owns an independent pair of\n * `Animated.Value`s, the same \"each item animates itself\" shape\n * `AvatarRotator`'s `RotatorLayer` already established for a\n * variable-length, data-driven list.\n */\nconst StepCircle = ({ index, label, isCompleted, isCurrent, onStepClick }: StepCircleProps) => {\n const theme = useGnomeTheme();\n const contrast = useResolvedContrast();\n const clickable = Boolean(onStepClick) && isCompleted;\n\n // Two independent 0/1 progress values rather than one three-state value:\n // \"accented\" (border) is true for current OR completed, \"filled\"\n // (background) only for current — they change on different transitions\n // (upcoming→current flips both; current→completed flips only \"filled\").\n const accentProgress = useRef(new Animated.Value(isCompleted || isCurrent ? 1 : 0)).current;\n const fillProgress = useRef(new Animated.Value(isCurrent ? 1 : 0)).current;\n const isInitialMount = useRef(true);\n\n useEffect(() => {\n if (isInitialMount.current) {\n isInitialMount.current = false;\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n const easing = Easing.bezier(x1, y1, x2, y2);\n\n Animated.timing(accentProgress, {\n toValue: isCompleted || isCurrent ? 1 : 0,\n duration: theme.durationFast,\n easing,\n useNativeDriver: false,\n }).start();\n\n Animated.timing(fillProgress, {\n toValue: isCurrent ? 1 : 0,\n duration: theme.durationFast,\n easing,\n useNativeDriver: false,\n }).start();\n }, [\n isCompleted,\n isCurrent,\n accentProgress,\n fillProgress,\n theme.durationFast,\n theme.easingDefault,\n ]);\n\n const borderColor = accentProgress.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.borderSubtle, theme.accentBgColor],\n });\n const backgroundColor = fillProgress.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.viewBgColor, theme.accentBgColor],\n });\n\n const accessibilityLabel = label ?? `Step ${index + 1}`;\n\n const circleStyle: Animated.WithAnimatedValue<ViewStyle> = {\n width: CIRCLE_SIZE,\n height: CIRCLE_SIZE,\n flexShrink: 0,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusPill,\n borderWidth: contrast === 'more' ? 3 : 2,\n borderColor,\n backgroundColor,\n };\n\n const content = isCompleted ? (\n // `tintColor`, not `color`, since RN has no `currentColor` to inherit\n // from the circle the way the web icon does — same call BottomTabBar's\n // active-tab icon already made for tracking the *configurable* accent.\n <Icon icon={Check} size=\"sm\" tintColor={theme.accentBgColor} />\n ) : (\n <Text\n variant=\"caption\"\n style={{\n color: isCurrent ? '#fff' : theme.windowFgColor,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n }}\n >\n {index + 1}\n </Text>\n );\n\n if (clickable) {\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={accessibilityLabel}\n onPress={() => onStepClick?.(index)}\n >\n <Animated.View style={circleStyle}>{content}</Animated.View>\n </Pressable>\n );\n }\n\n return (\n <Animated.View\n accessible\n accessibilityState={{ selected: isCurrent }}\n accessibilityLabel={accessibilityLabel}\n style={circleStyle}\n >\n {content}\n </Animated.View>\n );\n};\n\n/**\n * Numbered \"Step X of Y\" progress indicator for onboarding/wizard flows —\n * mirrors `@gnome-ui/react`'s `StepIndicator`. Directly portable per this\n * package's own ROADMAP note: no web-only APIs are involved, just derived\n * dot/number state from `currentStep`.\n *\n * The outer container sets `role=\"navigation\"` **without** `accessible` —\n * the corrected pattern `ToggleGroup` established over the earlier\n * `BoxedList`/`ViewSwitcher` precedent: `accessible` on a *grouping*\n * container with multiple independently-focusable children (each step\n * circle here) collapses the whole subtree into one VoiceOver stop on iOS.\n * The role still groups on Android, and every step stays individually\n * reachable; assert `element.props.role` on a `testID` in tests instead of\n * `getByRole('navigation')`.\n *\n * The connecting line between circles reuses the exact CSS trick verbatim\n * (`position: absolute; left: 50%; width: 100%` inside each equal-width\n * flex item, so the line runs from one circle's center to the next's) —\n * `left`/`width` percentages are supported for RN position/dimension\n * props, unlike the `transform: translateX('50%')` trick this package has\n * hit real bugs with elsewhere (that limitation is specific to `transform`).\n *\n * The web version's checkmark/number content swap and connector-line color\n * change have no CSS `transition` at all (only `.circle`'s own\n * `background-color`/`border-color` do), so only those two colors animate\n * here — the content swap and connector recolor are instant, matching the\n * source exactly rather than adding an unrequested fade.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.StepIndicator.html\n */\nexport const StepIndicator = ({\n steps,\n currentStep,\n orientation = 'horizontal',\n onStepClick,\n label = 'Progress',\n style,\n testID,\n}: StepIndicatorProps) => {\n const theme = useGnomeTheme();\n const isVertical = orientation === 'vertical';\n\n const stepLabels = Array.isArray(steps) ? steps : undefined;\n const stepCount = Array.isArray(steps) ? steps.length : steps;\n const clampedCurrent = Math.max(0, Math.min(currentStep, stepCount - 1));\n\n return (\n <View\n testID={testID}\n role=\"navigation\"\n accessibilityLabel={label}\n style={[{ gap: theme.space2 }, style]}\n >\n {!stepLabels && (\n <Text\n variant=\"caption\"\n color=\"dim\"\n style={{ fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'] }}\n >\n {`Step ${clampedCurrent + 1} of ${stepCount}`}\n </Text>\n )}\n\n <View\n style={{\n flexDirection: isVertical ? 'column' : 'row',\n alignItems: isVertical ? 'stretch' : 'flex-start',\n }}\n >\n {Array.from({ length: stepCount }, (_, i) => {\n const isCompleted = i < clampedCurrent;\n const isCurrent = i === clampedCurrent;\n const isLast = i === stepCount - 1;\n const stepLabel = stepLabels?.[i];\n const connectorColor = isCompleted ? theme.accentBgColor : theme.borderSubtle;\n\n return (\n <View\n key={i}\n style={\n isVertical\n ? {\n position: 'relative',\n flexDirection: 'row',\n alignItems: 'flex-start',\n paddingBottom: isLast ? 0 : theme.space3,\n }\n : {\n position: 'relative',\n flex: 1,\n flexDirection: 'column',\n alignItems: 'center',\n gap: theme.space1,\n }\n }\n >\n {!isLast && (\n <View\n pointerEvents=\"none\"\n style={\n isVertical\n ? {\n position: 'absolute',\n left: VERTICAL_CONNECTOR_LEFT,\n top: VERTICAL_CONNECTOR_TOP,\n width: CONNECTOR_THICKNESS,\n height: '100%',\n backgroundColor: connectorColor,\n zIndex: -1,\n }\n : {\n position: 'absolute',\n left: '50%',\n top: HORIZONTAL_CONNECTOR_TOP,\n width: '100%',\n height: CONNECTOR_THICKNESS,\n backgroundColor: connectorColor,\n zIndex: -1,\n }\n }\n />\n )}\n\n <StepCircle\n index={i}\n label={stepLabel}\n isCompleted={isCompleted}\n isCurrent={isCurrent}\n onStepClick={onStepClick}\n />\n\n {stepLabel && (\n <Text\n variant=\"caption\"\n numberOfLines={isVertical ? undefined : 1}\n ellipsizeMode={isVertical ? undefined : 'tail'}\n style={[\n isVertical\n ? { paddingLeft: theme.space2, textAlign: 'left' as const }\n : { maxWidth: 96, textAlign: 'center' as const },\n isCurrent && {\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n },\n ]}\n >\n {stepLabel}\n </Text>\n )}\n </View>\n );\n })}\n </View>\n </View>\n );\n};\n","import { forwardRef, useEffect, useRef } from 'react';\nimport type { PressableProps, StyleProp, View, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable } from 'react-native';\n\nimport { useGnomeTheme, useResolvedColorScheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface SwitchProps extends Omit<PressableProps, 'children' | 'style' | 'onPress'> {\n /** Whether the switch is on. */\n value: boolean;\n /** Called with the next value when the switch is pressed. */\n onValueChange?: (value: boolean) => void;\n /** Accessible label. Required when no visible label is associated. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\nconst TRACK_WIDTH = 56;\nconst TRACK_HEIGHT = 28;\nconst THUMB_SIZE = 20;\nconst THUMB_INSET = 3;\n/** Matches `@gnome-ui/react`'s `.switch:checked::before { transform: translateX(28px) }`. */\nconst THUMB_TRAVEL = 28;\n\n/**\n * On/off toggle following the Adwaita switch style.\n *\n * Rebuilt on `Pressable`/`Animated.View` rather than ported from\n * `@gnome-ui/react`'s `<input type=\"checkbox\" role=\"switch\">`: RN has no\n * checkbox primitive to style, and the platform-supplied `Switch` can't be\n * skinned to match Adwaita, so the track and thumb are drawn by hand.\n * `value`/`onValueChange` (not `checked`/`onChange`) mirror RN's own\n * `Switch` API, which is the ecosystem convention this component overlaps\n * with — it's fully controlled, with no `defaultValue` escape hatch.\n *\n * Two of `Switch.module.css`'s colors are hardcoded per color scheme\n * inside a component-level `@media (prefers-color-scheme: dark)` block\n * rather than driven by a semantic token, so — unlike `Button`/`Link`/\n * `TextField`, which read a single token that already resolves correctly\n * per theme — the unchecked track/thumb colors here branch explicitly on\n * `useResolvedColorScheme()` to match. Track background/border color\n * animate (mirroring the CSS `transition` on `.switch`); the thumb's own\n * fill color does not, since the source CSS only transitions its\n * `transform`, not its `background-color`.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/switches.html\n */\nexport const Switch = forwardRef<View, SwitchProps>(function Switch(\n { value, onValueChange, disabled, style, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const colorScheme = useResolvedColorScheme();\n const contrast = useResolvedContrast();\n const progress = useRef(new Animated.Value(value ? 1 : 0)).current;\n const isInitialMount = useRef(true);\n\n useEffect(() => {\n // The initial `Animated.Value` above already matches `value` — skip\n // animating into a state the switch is already rendering, so it never\n // plays a transition before the user has touched it.\n if (isInitialMount.current) {\n isInitialMount.current = false;\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: value ? 1 : 0,\n duration: theme.durationFast,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: false,\n }).start();\n }, [value, progress, theme.durationFast, theme.easingDefault]);\n\n const trackColorOff = colorScheme === 'dark' ? theme.dark2 : theme.light4;\n const thumbColorOff = colorScheme === 'dark' ? theme.light3 : '#fff';\n\n const backgroundColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [trackColorOff, theme.accentBgColor],\n });\n const borderColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.borderSubtle, theme.accentBgColor],\n });\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"switch\"\n accessibilityState={{ checked: value, disabled: !!disabled }}\n onPress={() => onValueChange?.(!value)}\n style={[{ opacity: disabled ? theme.opacityDisabled : 1 }, style]}\n {...pressableProps}\n >\n <Animated.View\n style={{\n width: TRACK_WIDTH,\n height: TRACK_HEIGHT,\n borderRadius: theme.radiusPill,\n borderWidth: contrast === 'more' ? 2 : 1,\n borderColor,\n backgroundColor,\n }}\n >\n <Animated.View\n style={[\n {\n position: 'absolute',\n top: THUMB_INSET,\n left: THUMB_INSET,\n width: THUMB_SIZE,\n height: THUMB_SIZE,\n borderRadius: theme.radiusPill,\n backgroundColor: value ? '#fff' : thumbColorOff,\n transform: [\n {\n translateX: progress.interpolate({\n inputRange: [0, 1],\n outputRange: [0, THUMB_TRAVEL],\n }),\n },\n ],\n },\n contrast !== 'more' && {\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 1 },\n shadowOpacity: 0.25,\n shadowRadius: 3,\n elevation: 2,\n },\n ]}\n />\n </Animated.View>\n </Pressable>\n );\n});\n","import { forwardRef, type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { PressableProps, StyleProp, TextStyle, View, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable, View as RNView, StyleSheet } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useResolvedColorScheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface SwitchRowProps extends Omit<PressableProps, 'children' | 'style' | 'onPress'> {\n /** Primary label. */\n title: string;\n /** Secondary line below the title. */\n subtitle?: string;\n /** Icon or image placed at the leading edge. */\n leading?: ReactNode;\n /** Controlled checked state. */\n checked?: boolean;\n /** Initial checked state when uncontrolled. Defaults to `false`. */\n defaultChecked?: boolean;\n /** Called with the next value when the row is pressed. */\n onCheckedChange?: (checked: boolean) => void;\n style?: StyleProp<ViewStyle>;\n}\n\nconst TRACK_WIDTH = 56;\nconst TRACK_HEIGHT = 28;\nconst THUMB_SIZE = 20;\nconst THUMB_INSET = 3;\nconst THUMB_TRAVEL = 28;\n\n/**\n * Activatable row with an integrated switch, mirroring `@gnome-ui/react`'s\n * `SwitchRow`. The entire row is a single pressable — pressing anywhere\n * toggles the switch, which is why this isn't `ActionRow` + a trailing\n * `Switch` (the ROADMAP's own guess): `AdwSwitchRow` makes the whole row the\n * interactive element, the same shape `CheckRow` already established for\n * its checkbox. Prefer this over `CheckRow` for a single on/off setting,\n * and `CheckRow` for selecting/deselecting individual items in a list.\n *\n * The switch visual reuses `Switch`'s exact track/thumb `Animated.Value`\n * interpolation, but as plain non-interactive `Animated.View`s rather than\n * importing the real `Switch` component — `Switch` is itself a `Pressable`,\n * and nesting one touchable inside another (the row's own `Pressable`)\n * would create two overlapping tap targets, the same reasoning `CheckRow`\n * already applied to `Checkbox`. `aria-labelledby` (pointing the web\n * button's `role=\"switch\"` at the title/subtitle content) has no RN\n * equivalent — `accessibilityLabel` combining title and subtitle is the\n * substitution, same as `CheckRow`.\n *\n * Supports both controlled (`checked`) and uncontrolled (`defaultChecked`)\n * modes, the same `isControlled`/internal-state-fallback shape already\n * established by `Expander`/`ComboRow`/`Popover`/`CheckRow`.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.SwitchRow.html\n */\nexport const SwitchRow = forwardRef<View, SwitchRowProps>(function SwitchRow(\n {\n title,\n subtitle,\n leading,\n checked: controlledChecked,\n defaultChecked = false,\n onCheckedChange,\n disabled,\n style,\n ...pressableProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const colorScheme = useResolvedColorScheme();\n const contrast = useResolvedContrast();\n\n const isControlled = controlledChecked !== undefined;\n const [uncontrolledChecked, setUncontrolledChecked] = useState(defaultChecked);\n const checked = isControlled ? controlledChecked : uncontrolledChecked;\n\n const progress = useRef(new Animated.Value(checked ? 1 : 0)).current;\n const isInitialMount = useRef(true);\n\n useEffect(() => {\n if (isInitialMount.current) {\n isInitialMount.current = false;\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: checked ? 1 : 0,\n duration: theme.durationFast,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: false,\n }).start();\n }, [checked, progress, theme.durationFast, theme.easingDefault]);\n\n const trackColorOff = colorScheme === 'dark' ? theme.dark2 : theme.light4;\n const thumbColorOff = colorScheme === 'dark' ? theme.light3 : '#fff';\n\n const trackBackgroundColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [trackColorOff, theme.accentBgColor],\n });\n const trackBorderColor = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [theme.borderSubtle, theme.accentBgColor],\n });\n\n const handlePress = () => {\n const next = !checked;\n\n if (!isControlled) {\n setUncontrolledChecked(next);\n }\n\n onCheckedChange?.(next);\n };\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"switch\"\n accessibilityState={{ checked, disabled: !!disabled }}\n accessibilityLabel={subtitle ? `${title}, ${subtitle}` : title}\n onPress={handlePress}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: theme.space2,\n paddingHorizontal: theme.space4,\n minHeight: 52,\n width: '100%',\n opacity: disabled ? theme.opacityDisabled : 1,\n },\n style,\n ]}\n {...pressableProps}\n >\n {({ pressed }) => (\n <>\n {leading && <RNView style={{ flexShrink: 0 }}>{leading}</RNView>}\n\n <RNView style={{ flex: 1, gap: 2 }}>\n <Text\n variant=\"body\"\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n color: theme.cardFgColor,\n fontWeight: String(theme.fontWeightNormal) as TextStyle['fontWeight'],\n }}\n >\n {title}\n </Text>\n {subtitle && (\n <Text variant=\"caption\" color=\"dim\" numberOfLines={1} ellipsizeMode=\"tail\">\n {subtitle}\n </Text>\n )}\n </RNView>\n\n <Animated.View\n style={{\n width: TRACK_WIDTH,\n height: TRACK_HEIGHT,\n flexShrink: 0,\n borderRadius: theme.radiusPill,\n borderWidth: contrast === 'more' ? 2 : 1,\n borderColor: trackBorderColor,\n backgroundColor: trackBackgroundColor,\n }}\n >\n <Animated.View\n style={[\n {\n position: 'absolute',\n top: THUMB_INSET,\n left: THUMB_INSET,\n width: THUMB_SIZE,\n height: THUMB_SIZE,\n borderRadius: theme.radiusPill,\n backgroundColor: checked ? '#fff' : thumbColorOff,\n transform: [\n {\n translateX: progress.interpolate({\n inputRange: [0, 1],\n outputRange: [0, THUMB_TRAVEL],\n }),\n },\n ],\n },\n contrast !== 'more' && {\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 1 },\n shadowOpacity: 0.25,\n shadowRadius: 3,\n elevation: 2,\n },\n ]}\n />\n </Animated.View>\n\n {pressed && !disabled ? (\n <RNView\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { backgroundColor: theme.activeOverlay }]}\n />\n ) : null}\n </>\n )}\n </Pressable>\n );\n});\n","import { forwardRef, type ReactNode } from 'react';\nimport type { ScrollView, ScrollViewProps, StyleProp, ViewStyle } from 'react-native';\nimport { ScrollView as RNScrollView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface TabBarProps extends Omit<ScrollViewProps, 'style' | 'horizontal' | 'children'> {\n children?: ReactNode;\n /** Accessible label for the tab list. */\n accessibilityLabel?: string;\n /**\n * Removes the header-bar background so the tab bar blends into any surface.\n * Use when placing the bar inside a card, content area, or custom container.\n * Mirrors the `.inline` style class.\n */\n inline?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Horizontal, horizontally-scrollable tab bar that holds `TabItem` elements.\n *\n * Rebuilt on a horizontal `ScrollView` rather than ported from\n * `@gnome-ui/react`'s `overflow-x: auto` `<div role=\"tablist\">` — RN's\n * `AccessibilityRole` union does include `\"tablist\"`, so that semantic\n * carries over directly, but the web version's roving-tabindex ← → / Home /\n * End keyboard navigation doesn't: RN's touch-first focus model has no\n * per-app arrow-key convention to port, so it's dropped here rather than\n * faking it.\n *\n * `style={{ flexGrow: 0 }}` on the `ScrollView` is deliberate, not\n * decorative — a horizontal `ScrollView` with no explicit `flexGrow` can\n * stretch to fill the cross-axis of whatever flex column it's placed in\n * (confirmed the hard way debugging the example app's own tab-like\n * `ControlsBar`), so it's baked in here once rather than left for every\n * consumer to rediscover.\n *\n * @see https://developer.gnome.org/hig/patterns/nav/tabs.html\n */\nexport const TabBar = forwardRef<ScrollView, TabBarProps>(function TabBar(\n { children, inline = false, accessibilityLabel = 'Tabs', style, ...scrollViewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <RNScrollView\n ref={ref}\n horizontal\n showsHorizontalScrollIndicator={false}\n contentInsetAdjustmentBehavior=\"never\"\n accessible\n accessibilityRole=\"tablist\"\n accessibilityLabel={accessibilityLabel}\n style={[\n {\n flexGrow: 0,\n backgroundColor: inline ? 'transparent' : theme.headerbarBgColor,\n borderBottomWidth: inline ? 0 : 1,\n borderBottomColor: theme.headerbarBorderColor,\n },\n style,\n ]}\n contentContainerStyle={{\n flexDirection: 'row',\n alignItems: 'stretch',\n paddingHorizontal: theme.space1,\n gap: 2,\n minHeight: 40,\n }}\n {...scrollViewProps}\n >\n {children}\n </RNScrollView>\n );\n});\n","import { forwardRef, type ReactNode } from 'react';\nimport type { PressableProps, TextStyle, View } from 'react-native';\nimport { Pressable, Text as RNText, View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface TabItemProps extends Omit<PressableProps, 'children' | 'style' | 'disabled'> {\n /** Tab label. */\n label: string;\n /**\n * Icon placed before the label. Rendered as-is — size/color it yourself.\n * Web's `icon` takes an `@gnome-ui/icons` `IconDefinition`; this package\n * has no SVG icon system (see [[react-native-port-status]]), so it's a\n * plain `ReactNode` here instead, same as `ActionRow`'s `leading`.\n */\n icon?: ReactNode;\n /** Marks this tab as the currently selected one. */\n active?: boolean;\n /**\n * When provided, a close (×) button is rendered at the trailing edge.\n * Called when the user taps the close button.\n */\n onClose?: () => void;\n /** Accessible label for the close button. Defaults to `\"Close tab\"`. */\n closeLabel?: string;\n /** Optional count shown as a small badge next to the label. Values above 99 render as \"99+\". */\n count?: number;\n disabled?: boolean;\n}\n\n/**\n * Individual tab button inside a `TabBar`.\n *\n * Web's `panelId` (which sets `aria-controls`) is dropped — RN has no ARIA\n * relationship attributes, so there's nothing for it to wire up; keep the\n * tab ↔ panel link in your own state instead. The count badge is a small\n * inline pill rather than the web version's separate `Badge` component,\n * since `Badge` hasn't been ported to this package yet and pulling in a\n * whole new component just for this one pill would be scope creep.\n *\n * The close button is a nested `Pressable` inside the tab's own\n * `Pressable` — unlike the web version, no explicit \"stop propagation\" is\n * needed: RN's touch responder system grants an in-progress touch to the\n * innermost view that claims it, so pressing the close button never also\n * fires the outer tab's `onPress`. It also gets a `hitSlop` the web\n * version has no equivalent of, since its 18×18 visual size is well under\n * the ~44pt touch target guidance both major mobile platforms recommend.\n *\n * The label `Text` deliberately has no `flex: 1` (unlike the web version's\n * `.tabLabel`) — the outer `Pressable`'s width here is content-driven\n * (`minWidth`/`maxWidth`, not a definite width), and giving a `flex: 1`\n * child to a row whose own width isn't definite collapses that child to a\n * near-zero width in Yoga (confirmed visually: every label truncated to\n * 3-4 characters despite ample room). CSS flexbox resolves this fine\n * because browsers can grow a flex child within an intrinsically-sized\n * container; Yoga can't. `numberOfLines`/`ellipsizeMode` alone are enough\n * to truncate genuinely-too-long labels against `maxWidth`.\n */\nexport const TabItem = forwardRef<View, TabItemProps>(function TabItem(\n {\n label,\n icon,\n active = false,\n onClose,\n closeLabel = 'Close tab',\n count,\n disabled,\n ...pressableProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"tab\"\n accessibilityState={{ selected: active, disabled: !!disabled }}\n accessibilityLabel={label}\n style={({ pressed }) => ({\n position: 'relative',\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space1,\n paddingHorizontal: theme.space2,\n minWidth: 80,\n maxWidth: 200,\n minHeight: 40,\n borderRadius: theme.radiusMd,\n opacity: disabled ? theme.opacityDisabled : active ? 1 : 0.7,\n backgroundColor: active\n ? theme.activeOverlay\n : pressed\n ? theme.hoverOverlay\n : 'transparent',\n })}\n {...pressableProps}\n >\n {icon && <RNView style={{ flexShrink: 0 }}>{icon}</RNView>}\n\n <RNText\n numberOfLines={1}\n ellipsizeMode=\"tail\"\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(\n active ? theme.fontWeightSemibold : theme.fontWeightNormal,\n ) as TextStyle['fontWeight'],\n color: theme.headerbarFgColor,\n }}\n >\n {label}\n </RNText>\n\n {count !== undefined && (\n <RNView\n style={{\n flexShrink: 0,\n minWidth: 18,\n height: 18,\n paddingHorizontal: 5,\n borderRadius: 9,\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: theme.accentBgColor,\n }}\n >\n <RNText\n style={{\n fontFamily: theme.fontFamily,\n fontSize: 11,\n fontWeight: '600' as TextStyle['fontWeight'],\n color: theme.accentFgColor,\n }}\n >\n {count > 99 ? '99+' : count}\n </RNText>\n </RNView>\n )}\n\n {onClose && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={closeLabel}\n hitSlop={8}\n onPress={onClose}\n style={({ pressed }) => ({\n flexShrink: 0,\n width: 18,\n height: 18,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusSm,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n })}\n >\n <RNText\n style={{\n fontFamily: theme.fontFamily,\n fontSize: 15,\n lineHeight: 16,\n color: theme.headerbarFgColor,\n }}\n >\n {'×'}\n </RNText>\n </Pressable>\n )}\n\n {active && (\n <RNView\n pointerEvents=\"none\"\n style={{\n position: 'absolute',\n bottom: 0,\n left: theme.space2,\n right: theme.space2,\n height: 2,\n borderRadius: 1,\n backgroundColor: theme.accentBgColor,\n }}\n />\n )}\n </Pressable>\n );\n});\n","import { forwardRef, type ReactNode } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nexport interface TabPanelProps extends Omit<ViewProps, 'style'> {\n /** Controls whether this panel is visible. */\n active?: boolean;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Content panel associated with a `TabItem`.\n *\n * Web's `id` (paired with `TabItem`'s `panelId`, together wiring up\n * `aria-controls`) is dropped on both sides here — RN has no ARIA\n * relationship attributes for it to drive, so track which tab is active in\n * your own state (index, key, whatever) and pass the matching panel\n * `active={true}` directly, rather than reaching for an id-matching\n * convention with nothing underneath it.\n *\n * Hidden panels stay mounted with `display: 'none'` rather than being\n * conditionally unmounted — same as the web version keeping them in the\n * DOM via the `hidden` attribute — so a panel's internal state (scroll\n * position, form input, etc.) survives switching tabs away and back. RN\n * has no `\"tabpanel\"` value in its `AccessibilityRole` union (unlike\n * `TabBar`'s `\"tablist\"` and `TabItem`'s `\"tab\"`, both of which do exist),\n * so no role is set here — same reasoning as `Separator` dropping a role\n * RN doesn't have rather than reaching for an inaccurate substitute.\n */\nexport const TabPanel = forwardRef<View, TabPanelProps>(function TabPanel(\n { active = false, style, children, ...viewProps },\n ref,\n) {\n return (\n <RNView ref={ref} style={[{ display: active ? 'flex' : 'none' }, style]} {...viewProps}>\n {children}\n </RNView>\n );\n});\n","import { useRef, useState } from 'react';\nimport type {\n NativeSyntheticEvent,\n StyleProp,\n TextInput,\n TextInputKeyPressEventData,\n TextStyle,\n ViewStyle,\n} from 'react-native';\nimport { Pressable, TextInput as RNTextInput, View } from 'react-native';\n\nimport { Chip } from '@/components/Chip';\nimport { Text } from '@/components/Text';\nimport { WrapBox } from '@/components/WrapBox';\nimport { useGnomeTheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface TagInputProps {\n /** Current list of tags. */\n value: string[];\n /** Called when a tag is added or removed. */\n onChange: (value: string[]) => void;\n /** Visible label rendered above the input. */\n label?: string;\n /** Placeholder shown in the draft input while empty. */\n placeholder?: string;\n /** Helper text rendered below the input. Hidden when `error` is set. */\n helperText?: string;\n /**\n * Error message rendered below the input in place of `helperText`.\n * Also applies the error visual state to the border.\n */\n error?: string;\n /** Maximum number of tags allowed. Once reached, the draft input is hidden. */\n maxTags?: number;\n /** Reject a new tag that already exists (case-insensitive). Defaults to `true`. */\n preventDuplicates?: boolean;\n /** Disables the whole control. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/** Duplicated from `TextField.tsx` — see that component for the full reasoning. */\nfunction dimColor(color: string, factor: number): string {\n const match = color.match(/^rgba?\\(([^,]+),([^,]+),([^,]+),?([^)]+)?\\)$/);\n\n if (!match) {\n return color;\n }\n\n const [, r, g, b, a] = match;\n const alpha = (a ? Number.parseFloat(a) : 1) * factor;\n\n return `rgba(${r.trim()}, ${g.trim()}, ${b.trim()}, ${alpha})`;\n}\n\n/**\n * Type-to-add multi-value input rendering entries as removable `Chip`s in a\n * `WrapBox`, mirroring `@gnome-ui/react`'s own `TagInput`.\n *\n * `WrapBox`/`Chip` alone only support static, pre-populated display —\n * `TagInput` adds interactive entry: type and press Return, or type a `,`,\n * to commit a tag; paste a comma/newline-separated list to add several at\n * once; Backspace on an empty draft removes the last one.\n *\n * The web version wires typed-`,` and pasted-list handling as two separate\n * handlers (`onKeyDown`'s `,` case, `onPaste`). RN's `TextInput` has no\n * `paste` event to mirror — but a paste still flows through `onChangeText`\n * with the full resulting text, exactly like a typed `,` does, so both\n * collapse into one `handleChangeText`: whenever the text contains a `,` or\n * newline, split on it and commit every non-empty part (typing \"foo,\"\n * commits \"foo\" via the same path a paste of \"foo,bar\" commits both).\n * Return is handled separately via `onSubmitEditing`, since RN has no\n * `Enter` keystroke to catch on a single-line field the way the web version\n * catches it in `onKeyDown`. Backspace-on-empty uses `onKeyPress`, the one\n * RN `TextInput` event that still fires with the field already empty\n * (`onChangeText` doesn't fire deleting from nothing).\n *\n * The tag box is a `Pressable` (mirrors the web version's\n * `onClick={() => inputRef.current?.focus()}` on the container) with\n * `accessible={false}` explicitly — `Pressable` defaults `accessible` to\n * `true`, which would collapse every `Chip`'s remove button and the draft\n * input into a single VoiceOver stop, the same container-swallows-subtree\n * trap already documented for `ToggleGroup`/`Sidebar`'s bare-`View`-plus-role\n * case, here hit via `Pressable`'s own default instead.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/text-fields.html\n */\nexport const TagInput = ({\n value,\n onChange,\n label,\n placeholder,\n helperText,\n error,\n maxTags,\n preventDuplicates = true,\n disabled = false,\n style,\n testID,\n}: TagInputProps) => {\n const theme = useGnomeTheme();\n const contrast = useResolvedContrast();\n const [draft, setDraft] = useState('');\n const [focused, setFocused] = useState(false);\n const inputRef = useRef<TextInput>(null);\n\n const atMax = maxTags !== undefined && value.length >= maxTags;\n const hint = error ?? helperText;\n\n const isDuplicate = (tag: string) =>\n preventDuplicates && value.some((v) => v.toLowerCase() === tag.toLowerCase());\n\n const commit = (raw: string) => {\n const tag = raw.trim();\n\n setDraft('');\n\n if (!tag || atMax || isDuplicate(tag)) {\n return;\n }\n\n onChange([...value, tag]);\n };\n\n const handleChangeText = (text: string) => {\n if (!text.includes(',') && !text.includes('\\n')) {\n setDraft(text);\n\n return;\n }\n\n const next = [...value];\n\n for (const part of text.split(/[,\\n]/)) {\n const tag = part.trim();\n\n if (!tag || (maxTags !== undefined && next.length >= maxTags)) {\n continue;\n }\n if (preventDuplicates && next.some((v) => v.toLowerCase() === tag.toLowerCase())) {\n continue;\n }\n next.push(tag);\n }\n\n setDraft('');\n onChange(next);\n };\n\n const handleKeyPress = (e: NativeSyntheticEvent<TextInputKeyPressEventData>) => {\n if (e.nativeEvent.key === 'Backspace' && draft === '' && value.length > 0) {\n onChange(value.slice(0, -1));\n }\n };\n\n const removeAt = (index: number) => {\n onChange(value.filter((_, i) => i !== index));\n };\n\n return (\n <View\n testID={testID}\n style={[{ gap: theme.space1, opacity: disabled ? theme.opacityDisabled : 1 }, style]}\n >\n {label && (\n <Text\n variant=\"body\"\n style={{ fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'] }}\n >\n {label}\n </Text>\n )}\n\n <Pressable\n accessible={false}\n disabled={disabled}\n onPress={() => inputRef.current?.focus()}\n style={{\n width: '100%',\n minHeight: 40,\n paddingVertical: 6,\n paddingHorizontal: theme.space2,\n backgroundColor: theme.viewBgColor,\n borderRadius: theme.radiusMd,\n borderWidth: contrast === 'more' ? 2 : 1,\n borderColor: error ? theme.errorColor : focused ? theme.accentColor : theme.borderSubtle,\n }}\n >\n <WrapBox childSpacing={6}>\n {value.map((tag, i) => (\n <Chip\n key={`${tag}-${i}`}\n label={tag}\n disabled={disabled}\n onRemove={disabled ? undefined : () => removeAt(i)}\n />\n ))}\n\n {!atMax && (\n <RNTextInput\n ref={inputRef}\n value={draft}\n editable={!disabled}\n placeholder={placeholder}\n placeholderTextColor={dimColor(theme.windowFgColor, theme.opacityDim)}\n accessibilityLabel={label}\n accessibilityHint={hint}\n onChangeText={handleChangeText}\n onSubmitEditing={() => commit(draft)}\n onKeyPress={handleKeyPress}\n onFocus={() => setFocused(true)}\n onBlur={() => setFocused(false)}\n style={{\n flexGrow: 1,\n minWidth: 80,\n paddingVertical: 2,\n paddingHorizontal: 4,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n color: theme.viewFgColor,\n }}\n />\n )}\n </WrapBox>\n </Pressable>\n\n {hint && (\n <Text\n variant=\"caption\"\n style={{\n color: error ? theme.errorColor : theme.windowFgColor,\n opacity: error ? 1 : theme.opacityDim,\n }}\n >\n {hint}\n </Text>\n )}\n </View>\n );\n};\n","import { forwardRef, useState } from 'react';\nimport type { StyleProp, TextInput, TextInputProps, TextStyle, ViewStyle } from 'react-native';\nimport { TextInput as RNTextInput, View } from 'react-native';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useResolvedContrast } from '@/GnomeProvider';\n\nexport interface TextFieldProps extends Omit<TextInputProps, 'style'> {\n /** Visible label rendered above the input. */\n label?: string;\n /** Helper text rendered below the input. Hidden when `error` is set. */\n helperText?: string;\n /**\n * Error message rendered below the input in place of `helperText`.\n * Also applies the error visual state to the input border.\n */\n error?: string;\n /** Style applied to the wrapping `View`. */\n style?: StyleProp<ViewStyle>;\n /** Style applied to the `TextInput` itself. */\n inputStyle?: StyleProp<TextStyle>;\n}\n\n/**\n * Multiplies an `rgba(r, g, b, a)` token's alpha channel. Tokens that\n * aren't `rgba()` (e.g. `windowFgColor` in some variants resolves to a flat\n * hex) are returned unchanged — good enough here since this is only ever\n * used to dim `windowFgColor`, which the generated theme always emits as\n * `rgba()`.\n */\nfunction dimColor(color: string, factor: number): string {\n const match = color.match(/^rgba?\\(([^,]+),([^,]+),([^,]+),?([^)]+)?\\)$/);\n\n if (!match) {\n return color;\n }\n\n const [, r, g, b, a] = match;\n const alpha = (a ? Number.parseFloat(a) : 1) * factor;\n\n return `rgba(${r.trim()}, ${g.trim()}, ${b.trim()}, ${alpha})`;\n}\n\n/**\n * Single-line text input with label, helper text, and error state.\n *\n * Follows the Adwaita `GtkEntry` / `.entry` style class.\n *\n * Rebuilt on RN's `TextInput` rather than ported from `@gnome-ui/react`'s\n * `<input>`: there's no `<label htmlFor>`/`aria-describedby` pairing on RN,\n * so `label` doubles as `accessibilityLabel` and `error`/`helperText`\n * double as `accessibilityHint` — announced together with the input the\n * same way `aria-describedby` reads them on the web. `:focus-visible`'s\n * accent border becomes plain `onFocus`/`onBlur` state — RN has no\n * hover/keyboard-vs-pointer focus distinction to mirror, and no outer\n * `box-shadow` ring, so focus is just a border-color change rather than a\n * grown ring. The web version's `disabled` prop is RN's own\n * `editable={false}` — mirrored here as a dimmed wrapper the same way\n * `@gnome-ui/react`'s `.disabled` class dims the label and hint alongside\n * the input.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/text-fields.html\n */\nexport const TextField = forwardRef<TextInput, TextFieldProps>(function TextField(\n {\n label,\n helperText,\n error,\n style,\n inputStyle,\n editable = true,\n onFocus,\n onBlur,\n accessibilityLabel,\n accessibilityHint,\n ...inputProps\n },\n ref,\n) {\n const theme = useGnomeTheme();\n const contrast = useResolvedContrast();\n const [focused, setFocused] = useState(false);\n const disabled = !editable;\n const hint = error ?? helperText;\n\n const borderColor = error ? theme.errorColor : focused ? theme.accentColor : theme.borderSubtle;\n\n return (\n <View style={[{ gap: theme.space1, opacity: disabled ? theme.opacityDisabled : 1 }, style]}>\n {label && (\n <Text\n variant=\"body\"\n style={{ fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'] }}\n >\n {label}\n </Text>\n )}\n\n <RNTextInput\n ref={ref}\n editable={editable}\n accessibilityLabel={accessibilityLabel ?? label}\n accessibilityHint={accessibilityHint ?? hint}\n placeholderTextColor={dimColor(theme.windowFgColor, theme.opacityDim)}\n onFocus={(event) => {\n setFocused(true);\n onFocus?.(event);\n }}\n onBlur={(event) => {\n setFocused(false);\n onBlur?.(event);\n }}\n style={[\n {\n width: '100%',\n paddingVertical: 8,\n paddingHorizontal: theme.space2,\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n lineHeight: Math.round(theme.fontSizeBody * theme.lineHeightBody),\n color: theme.viewFgColor,\n backgroundColor: theme.viewBgColor,\n borderRadius: theme.radiusMd,\n borderWidth: contrast === 'more' ? 2 : 1,\n borderColor,\n } satisfies TextStyle,\n inputStyle,\n ]}\n {...inputProps}\n />\n\n {hint && (\n <Text\n variant=\"caption\"\n style={{\n color: error ? theme.errorColor : theme.windowFgColor,\n opacity: error ? 1 : theme.opacityDim,\n }}\n >\n {hint}\n </Text>\n )}\n </View>\n );\n});\n","import type { ReactNode } from 'react';\nimport { useState } from 'react';\nimport type { LayoutChangeEvent, StyleProp, ViewStyle } from 'react-native';\nimport { ScrollView, View } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport type TimelineOrientation = 'vertical' | 'horizontal';\nexport type TimelineVariant = 'default' | 'dotted' | 'none';\n\nexport interface TimelineItem {\n /** Icon rendered inside the timeline node. Sized/colored by the consumer — see the component doc. */\n icon?: ReactNode;\n /**\n * Content at the leading edge of the node.\n * - **Vertical:** rendered to the left of the connector track.\n * - **Horizontal:** rendered above the node.\n *\n * Typical use: timestamp, badge, short label.\n */\n leading?: ReactNode;\n /** Main event content rendered adjacent to the node (title, description…). */\n content: ReactNode;\n}\n\nexport interface TimelineProps {\n /** Ordered list of timeline events. */\n items: TimelineItem[];\n /**\n * Axis direction of the connector line.\n * - `\"vertical\"` — events stack top-to-bottom (default).\n * - `\"horizontal\"` — events flow left-to-right, in a horizontal `ScrollView`.\n */\n orientation?: TimelineOrientation;\n /**\n * Visual style of the connector between nodes.\n * - `\"default\"` — solid thin line.\n * - `\"dotted\"` — dotted line; useful for future or pending events.\n * - `\"none\"` — no connector; each node stands alone.\n */\n variant?: TimelineVariant;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst NODE_DOT_SIZE = 12;\nconst NODE_ICON_SIZE = 28;\nconst LINE_THICKNESS = 2;\nconst VERTICAL_MIN_LINE_LENGTH = 16;\n/** Cross-dimension of the dotted line's box — see `lineStyle`'s doc comment. */\nconst DOTTED_LINE_CROSS_SIZE = 6;\n/** Matches `@gnome-ui/react`'s `grid-template-columns: auto 24px 1fr` (fixed track width). */\nconst VERTICAL_NODE_TRACK_WIDTH = 24;\n/** No fixed value in the source CSS (an `auto` grid row) — the larger of the two node sizes. */\nconst HORIZONTAL_NODE_TRACK_HEIGHT = NODE_ICON_SIZE;\n/** Matches the source CSS's literal (non-tokenized) `padding-bottom: 20px`. */\nconst VERTICAL_ITEM_GAP = 20;\n/** Matches `grid-auto-columns: minmax(72px, 1fr)` — the `1fr` growth half doesn't\n * port (see the component doc), only the `72px` floor. */\nconst HORIZONTAL_MIN_ITEM_WIDTH = 72;\nconst HORIZONTAL_ITEM_GAP = 8;\n\n/**\n * Ordered sequence of events connected by a visual timeline — mirrors\n * `@gnome-ui/react`'s `Timeline`. An original composition (no direct\n * libadwaita widget), following GNOME HIG activity-feed/stepper patterns.\n *\n * The web version aligns every item's `leading` column (vertical) or row\n * (horizontal) via CSS subgrid, so timestamps/labels line up across items\n * regardless of how wide/tall any single one of them is. RN/Yoga has no\n * grid or subgrid at all, so that alignment is reproduced by measurement\n * instead — the same `onLayout` + `Record<index, size>` +\n * \"largest-so-far wins\" technique `Slider`'s mark labels already\n * established: every item's `leading` cell (always rendered, even when\n * empty, matching the web version's own \"same 3 children\" comment)\n * reports its own rendered width (vertical) or height (horizontal), and\n * every cell gets a `minWidth`/`minHeight` equal to the largest one\n * measured so far, so the node column/row starts at the same position in\n * every item. The node track itself additionally gets a *fixed*\n * width (vertical, matching the source CSS's literal `24px` column) or\n * height (horizontal, the larger of the dot/icon node sizes, since the\n * source's row there is CSS `auto` with no fixed value to port) — without\n * it, an item with an icon node (28 dp) and one with a plain dot (12 dp)\n * would each size their own track differently, misaligning `content`'s\n * start position between them.\n *\n * `orientation=\"horizontal\"` wraps itself in a horizontal `ScrollView`,\n * reimagining the web CSS's `overflow-x: auto` — the direct native\n * equivalent for \"scroll instead of overflow when items don't fit\". The\n * web version's `grid-auto-columns: minmax(72px, 1fr)` also grows items\n * to fill leftover space when the row *doesn't* overflow; that half\n * doesn't port (a `ScrollView`'s content isn't bounded the way a CSS grid\n * track is, so there's no \"leftover space\" to distribute) — each item\n * just gets a flat 72 dp `minWidth` instead, unconditionally scrollable\n * like `overflow-x: auto`'s own fallback path.\n *\n * `icon`/`leading`/`content` are plain `ReactNode`, the same as\n * `PathBar`'s segment `icon` — the consumer sizes and colors their own\n * icon (e.g. `tintColor={theme.accentFgColor}` on their own `<Icon>`),\n * since RN has no `currentColor` for this component to tint an arbitrary\n * child with the way the web version's CSS `color: accent-fg-color`\n * does via inheritance onto the icon's SVG.\n *\n * `role=\"list\"`/`role=\"listitem\"` are set **without** `accessible` — the\n * `ToggleGroup`/`StepIndicator`-established pattern for a grouping role\n * over children that may themselves contain focusable content (an\n * item's `content` is arbitrary `ReactNode` and could include one), so\n * nothing nested inside gets swallowed into a single VoiceOver stop.\n * Assert `element.props.role` on a `testID` in tests instead of\n * `getByRole('list'/'listitem')`.\n */\nexport const Timeline = ({\n items,\n orientation = 'vertical',\n variant = 'default',\n style,\n testID,\n}: TimelineProps) => {\n const theme = useGnomeTheme();\n const horizontal = orientation === 'horizontal';\n const showLine = variant !== 'none';\n const dotted = variant === 'dotted';\n\n const [leadingExtents, setLeadingExtents] = useState<Record<number, number>>({});\n const maxLeadingExtent = Object.values(leadingExtents).reduce((max, v) => Math.max(max, v), 0);\n\n const handleLeadingLayout = (index: number) => (event: LayoutChangeEvent) => {\n const size = horizontal ? event.nativeEvent.layout.height : event.nativeEvent.layout.width;\n\n setLeadingExtents((prev) => (prev[index] === size ? prev : { ...prev, [index]: size }));\n };\n\n const lineStyle = (hidden: boolean): StyleProp<ViewStyle> => {\n // The web CSS draws the dotted line via a single `border-left`/`border-top`\n // (zero-width box, one bordered side) — confirmed on-device (iOS Simulator,\n // saturated debug colors) that RN's `borderStyle: 'dotted'` renders nothing\n // at all unless every side shares the same width AND color (even three\n // sides at `borderWidth: 0` or `borderColor: 'transparent'` suppresses it\n // entirely). Reimagined as a narrow box with a *uniform* dotted border on\n // all four sides instead — visually indistinguishable from a single\n // dotted line at this thickness, and the only combination that actually\n // renders. **Any future dotted/dashed RN border needs all sides\n // width+color-uniform — a single-side border in that style silently\n // renders invisible, no error.**\n const base: ViewStyle = dotted\n ? horizontal\n ? {\n flex: 1,\n height: DOTTED_LINE_CROSS_SIZE,\n borderWidth: LINE_THICKNESS,\n borderStyle: 'dotted',\n borderColor: theme.borderSubtle,\n }\n : {\n flex: 1,\n width: DOTTED_LINE_CROSS_SIZE,\n minHeight: VERTICAL_MIN_LINE_LENGTH,\n borderWidth: LINE_THICKNESS,\n borderStyle: 'dotted',\n borderColor: theme.borderSubtle,\n }\n : horizontal\n ? { flex: 1, height: LINE_THICKNESS, backgroundColor: theme.borderSubtle }\n : {\n flex: 1,\n width: LINE_THICKNESS,\n minHeight: VERTICAL_MIN_LINE_LENGTH,\n backgroundColor: theme.borderSubtle,\n };\n\n return hidden || !showLine ? { ...base, opacity: 0 } : base;\n };\n\n const renderedItems = items.map((item, i) => {\n const isFirst = i === 0;\n const isLast = i === items.length - 1;\n const hasIcon = Boolean(item.icon);\n\n const node = (\n <View\n style={{\n width: hasIcon ? NODE_ICON_SIZE : NODE_DOT_SIZE,\n height: hasIcon ? NODE_ICON_SIZE : NODE_DOT_SIZE,\n borderRadius: theme.radiusPill,\n backgroundColor: theme.accentBgColor,\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n }}\n >\n {item.icon}\n </View>\n );\n\n const leadingCell = (\n <View\n onLayout={handleLeadingLayout(i)}\n style={\n horizontal\n ? {\n minHeight: maxLeadingExtent,\n justifyContent: 'flex-end',\n alignItems: 'center',\n paddingBottom: theme.space2,\n }\n : {\n minWidth: maxLeadingExtent,\n flexShrink: 0,\n alignSelf: 'flex-start',\n alignItems: 'flex-end',\n paddingTop: 4,\n }\n }\n >\n {item.leading}\n </View>\n );\n\n const nodeTrack = horizontal ? (\n <View\n style={{\n height: HORIZONTAL_NODE_TRACK_HEIGHT,\n flexDirection: 'row',\n alignItems: 'center',\n width: '100%',\n }}\n >\n <View style={lineStyle(isFirst)} />\n {node}\n <View style={lineStyle(isLast)} />\n </View>\n ) : (\n <View\n style={{ width: VERTICAL_NODE_TRACK_WIDTH, flexShrink: 0, alignItems: 'center', flex: 1 }}\n >\n {node}\n {!isLast && showLine && <View style={lineStyle(false)} />}\n </View>\n );\n\n const contentCell = (\n <View\n style={\n horizontal\n ? { alignItems: 'center', paddingTop: theme.space2 }\n : {\n flex: 1,\n minWidth: 0,\n alignSelf: 'flex-start',\n paddingTop: 2,\n paddingBottom: isLast ? 0 : VERTICAL_ITEM_GAP,\n }\n }\n >\n {item.content}\n </View>\n );\n\n return (\n <View\n key={i}\n testID={testID ? `${testID}-item-${i}` : undefined}\n role=\"listitem\"\n style={\n horizontal\n ? { minWidth: HORIZONTAL_MIN_ITEM_WIDTH, marginRight: isLast ? 0 : HORIZONTAL_ITEM_GAP }\n : { flexDirection: 'row', columnGap: theme.space2 }\n }\n >\n {leadingCell}\n {nodeTrack}\n {contentCell}\n </View>\n );\n });\n\n if (horizontal) {\n return (\n <ScrollView\n testID={testID}\n role=\"list\"\n horizontal\n showsHorizontalScrollIndicator={false}\n style={style}\n contentContainerStyle={{ flexDirection: 'row' }}\n >\n {renderedItems}\n </ScrollView>\n );\n }\n\n return (\n <View testID={testID} role=\"list\" style={style}>\n {renderedItems}\n </View>\n );\n};\n","import { PreferencesSystemTime } from '@gnome-ui/icons';\nimport { useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Pressable, View } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { Popover, type PopoverPlacement } from '@/components/Popover';\nimport { Text } from '@/components/Text';\nimport { useDateTimeFormatter, useGnomeTheme } from '@/GnomeProvider';\n\nimport { TimeFields } from './TimeFields';\nimport type { TimeValue } from './timeUtils';\n\nexport type { TimeValue } from './timeUtils';\n\nexport interface TimePickerProps {\n /** Controlled selected time. Pass `null` for \"no selection\". */\n value?: TimeValue | null;\n /** Initial selected time when uncontrolled. Defaults to `null`. */\n defaultValue?: TimeValue | null;\n /** Called when the user changes the time. */\n onChange?: (value: TimeValue) => void;\n /** 12- or 24-hour presentation. Defaults to `24`. */\n hourCycle?: 12 | 24;\n /** Minute increment for the spinner. Defaults to `1`. */\n minuteStep?: number;\n /** Text shown in the trigger while no time is selected. */\n placeholder?: string;\n /** Visible label rendered above the trigger. */\n label?: string;\n /** Accessible name for the trigger when no visible `label` is provided. */\n accessibilityLabel?: string;\n /** Disable the control. */\n disabled?: boolean;\n /** Preferred popover placement relative to the trigger. Defaults to `'bottom'`. */\n placement?: PopoverPlacement;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n// Hoisted to module scope for the same reason `DatePicker`'s own format\n// option objects are — `useDateTimeFormatter`'s `useMemo` keys off `options`\n// identity, so a fresh object literal every render would defeat it. `hour`/\n// `minute` component options (not `timeStyle`) are used deliberately: they\n// match the web version's own `Intl.DateTimeFormat({hour:'2-digit',\n// minute:'2-digit', hourCycle})` exactly, whereas `DatePicker` needed\n// `dateStyle`+`timeStyle` together since it also renders the day.\nconst TIME_24_OPTIONS: Intl.DateTimeFormatOptions = {\n hour: '2-digit',\n minute: '2-digit',\n hourCycle: 'h23',\n};\nconst TIME_12_OPTIONS: Intl.DateTimeFormatOptions = {\n hour: '2-digit',\n minute: '2-digit',\n hourCycle: 'h12',\n};\n\n/**\n * Hour/minute selection built from paired `SpinButton`s inside a `Popover`,\n * behind an entry-styled trigger — mirrors the `GtkSpinButton` + `GtkPopover`\n * composition GNOME apps use for time entry, with 12- and 24-hour support.\n *\n * Rebuilt on this package's own already-shipped `Popover` (Tier 5) rather\n * than reinventing its trigger-rect + panel-size positioning, the same\n * standing pitfall `DatePicker` already avoided. The panel is just\n * `TimeFields` — no Done button, unlike `DatePicker`'s `showTime` footer:\n * there is no calendar tap to disambiguate from a close here, so each\n * column commits live and the popover only closes the same way `Dropdown`'s\n * does, by tapping outside or the trigger again.\n *\n * **`TimeFields`/`timeUtils` moved here from `DatePicker`'s own folder**,\n * where they first shipped as an internal dependency of its `showTime`\n * footer — this is their intended public home (per the ROADMAP note left\n * when `DatePicker` shipped), `DatePicker` now imports them from here\n * instead of carrying its own copy.\n *\n * The web version's entire keyboard layer drops (no `ArrowDown`-opens focus\n * management, no `Enter`/`Space` on the trigger) — the same touch-first\n * convention `DatePicker` already follows. `locale` is dropped in favor of\n * `GnomeProvider`'s app-wide `useDateTimeFormatter`, same as `DatePicker`.\n *\n * @see https://developer.gnome.org/hig/patterns/controls/spin-buttons.html\n */\nexport const TimePicker = ({\n value: controlledValue,\n defaultValue = null,\n onChange,\n hourCycle = 24,\n minuteStep = 1,\n placeholder = 'Select a time',\n label,\n accessibilityLabel,\n disabled = false,\n placement = 'bottom',\n style,\n testID,\n}: TimePickerProps) => {\n const theme = useGnomeTheme();\n\n const isControlled = controlledValue !== undefined;\n const [uncontrolledValue, setUncontrolledValue] = useState<TimeValue | null>(defaultValue);\n const selected = isControlled ? controlledValue : uncontrolledValue;\n\n const [open, setOpen] = useState(false);\n\n // The columns always need a concrete time; noon is the same neutral\n // fallback `DatePicker` uses, and it isn't treated as a selection.\n const draft: TimeValue = selected ?? { hours: 12, minutes: 0 };\n\n const formatter = useDateTimeFormatter(hourCycle === 12 ? TIME_12_OPTIONS : TIME_24_OPTIONS);\n const displayValue = selected\n ? formatter.format(new Date(2000, 0, 1, selected.hours, selected.minutes))\n : placeholder;\n\n const commit = (next: TimeValue) => {\n if (!isControlled) {\n setUncontrolledValue(next);\n }\n onChange?.(next);\n };\n\n const panel = (\n <TimeFields value={draft} onChange={commit} hourCycle={hourCycle} minuteStep={minuteStep} />\n );\n\n return (\n <View style={[{ gap: theme.space1 }, style]}>\n {label && <Text variant=\"body\">{label}</Text>}\n\n <Popover placement={placement} open={open} onOpenChange={setOpen} content={panel}>\n <Pressable\n testID={testID}\n role=\"button\"\n disabled={disabled}\n accessibilityLabel={accessibilityLabel ?? label}\n accessibilityState={{ disabled }}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: theme.space1,\n minHeight: 36,\n minWidth: 160,\n paddingVertical: 7,\n paddingHorizontal: theme.space2,\n backgroundColor: pressed ? theme.activeOverlay : theme.cardBgColor,\n borderWidth: 1,\n borderColor: open ? theme.accentColor : theme.cardShadeColor,\n borderRadius: theme.radiusMd,\n opacity: disabled ? theme.opacityDisabled : 1,\n })}\n >\n <Text\n numberOfLines={1}\n style={{\n flex: 1,\n fontSize: theme.fontSizeBody,\n color: theme.windowFgColor,\n opacity: selected ? 1 : theme.opacityDim,\n }}\n >\n {displayValue}\n </Text>\n <Icon icon={PreferencesSystemTime} size=\"sm\" />\n </Pressable>\n </Popover>\n </View>\n );\n};\n","import { type ReactNode, useCallback, useEffect, useRef } from 'react';\nimport type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native';\nimport { Animated, Easing, Pressable, Text as RNText } from 'react-native';\n\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme, useReducedMotion, useResolvedColorScheme } from '@/GnomeProvider';\n\n/**\n * `Animated`'s style interpolation only takes effect on `Animated.*`\n * host components — a plain `Pressable` would silently ignore the\n * animated `opacity`/`transform` in its style. Hoisted to module scope so\n * it isn't recreated on every render.\n */\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable);\n\nexport interface ToastProps extends Omit<ViewProps, 'style'> {\n /** The notification message. Keep it short — one sentence. */\n title: ReactNode;\n /**\n * Auto-dismiss timeout in milliseconds.\n * - Set to `0` to disable auto-dismiss (user must press action or dismiss).\n * - Defaults to `3000`.\n */\n duration?: number;\n /** Called when the toast should be removed — after timeout or user action. */\n onDismiss?: () => void;\n /** Label for the optional action button. */\n actionLabel?: string;\n /** Called when the user presses the action button. */\n onAction?: () => void;\n /** Whether to show a manual dismiss (×) button. */\n dismissible?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Non-blocking temporary notification following the Adwaita `AdwToast`\n * pattern. Auto-dismisses after `duration` ms (default 3s, `0` disables\n * it); the timer pauses while the toast is pressed and held. Use inside\n * `Toaster` for correct positioning and stacking.\n *\n * The auto-dismiss/pause timer logic (`timerRef`/`remainingRef`/\n * `startedAtRef`) is ported verbatim from `@gnome-ui/react` — it's plain\n * `setTimeout`/`Date.now()` bookkeeping, no DOM API involved, so nothing\n * platform-specific to change. The pause trigger is: RN has no hover, so\n * `onPressIn`/`onPressOut` (touch-down/touch-up) stand in for the web\n * version's `onMouseEnter`/`onMouseLeave` — pausing while the user is\n * actively touching the toast, the closest RN analog to \"giving the user\n * time to read it.\" `onFocus`/`onBlur`-triggered pausing (keyboard\n * accessibility on web) has no port here — the outer card itself isn't a\n * focusable element in RN's touch-first model, only its action/dismiss\n * buttons are, and RN has no \"focus-within\" primitive to detect that\n * without an extra dependency.\n *\n * The entrance is an `Animated.timing` fading + sliding + scaling in\n * (`opacity`/`translateY`/`scale`, all transform-safe for\n * `useNativeDriver: true`), matching the web version's `@keyframes\n * toast-in`; `useReducedMotion()` skips straight to the settled state,\n * matching the source CSS's `animation: none`. There's no exit animation\n * to port either — the web version doesn't define one, removal is\n * immediate once the consumer drops the `Toast` from its list.\n *\n * RN's `AccessibilityRole` union has no \"status\" value (the web version's\n * `role=\"status\"`); `\"alert\"` is the closest available role for content\n * that should interrupt and be announced, paired with\n * `accessibilityLiveRegion=\"polite\"` (Android's live-region API) as the\n * nearest match to the web's `aria-live=\"polite\"` — deliberately not\n * `\"assertive\"`, matching the same politeness level the source chose.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Toast.html\n */\nexport const Toast = ({\n title,\n duration = 3000,\n onDismiss,\n actionLabel,\n onAction,\n dismissible = false,\n style,\n ...viewProps\n}: ToastProps) => {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const reducedMotion = useReducedMotion();\n\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const remainingRef = useRef(duration);\n const startedAtRef = useRef(0);\n const progress = useRef(new Animated.Value(reducedMotion ? 1 : 0)).current;\n\n const clearTimer = useCallback(() => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n const startTimer = useCallback(\n (ms: number) => {\n if (ms <= 0 || !onDismiss) {\n return;\n }\n\n clearTimer();\n startedAtRef.current = Date.now();\n timerRef.current = setTimeout(() => onDismiss(), ms);\n },\n [onDismiss, clearTimer],\n );\n\n useEffect(() => {\n if (duration > 0) {\n remainingRef.current = duration;\n startTimer(duration);\n }\n\n return clearTimer;\n }, [duration, startTimer, clearTimer]);\n\n useEffect(() => {\n if (reducedMotion) {\n progress.setValue(1);\n\n return;\n }\n\n const [x1, y1, x2, y2] = theme.easingDefault;\n\n Animated.timing(progress, {\n toValue: 1,\n duration: theme.durationNormal,\n easing: Easing.bezier(x1, y1, x2, y2),\n useNativeDriver: true,\n }).start();\n }, [reducedMotion, progress, theme.durationNormal, theme.easingDefault]);\n\n const handlePressIn = () => {\n if (timerRef.current) {\n const elapsed = Date.now() - startedAtRef.current;\n\n remainingRef.current = Math.max(0, remainingRef.current - elapsed);\n clearTimer();\n }\n };\n\n const handlePressOut = () => {\n startTimer(remainingRef.current);\n };\n\n const handleAction = () => {\n clearTimer();\n onAction?.();\n onDismiss?.();\n };\n\n const shadow =\n scheme === 'dark'\n ? { shadowOpacity: 0.4, shadowRadius: 12, elevation: 6 }\n : { shadowOpacity: 0.15, shadowRadius: 8, elevation: 4 };\n\n return (\n <AnimatedPressable\n accessible\n accessibilityRole=\"alert\"\n accessibilityLiveRegion=\"polite\"\n onPressIn={handlePressIn}\n onPressOut={handlePressOut}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n paddingVertical: 10,\n paddingRight: theme.space2,\n paddingLeft: theme.space3,\n minHeight: 44,\n maxWidth: 480,\n minWidth: 240,\n backgroundColor: theme.cardBgColor,\n borderRadius: theme.radiusLg,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 2 },\n opacity: progress,\n transform: [\n {\n translateY: progress.interpolate({ inputRange: [0, 1], outputRange: [12, 0] }),\n },\n { scale: progress.interpolate({ inputRange: [0, 1], outputRange: [0.96, 1] }) },\n ],\n ...shadow,\n },\n style,\n ]}\n {...viewProps}\n >\n <Text\n variant=\"body\"\n style={{ flex: 1, minWidth: 0 }}\n numberOfLines={typeof title === 'string' ? 2 : undefined}\n >\n {title}\n </Text>\n\n {(actionLabel || dismissible) && (\n <>\n {actionLabel && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={actionLabel}\n onPress={handleAction}\n style={({ pressed }) => ({\n paddingVertical: 4,\n paddingHorizontal: theme.space1,\n borderRadius: theme.radiusSm,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n })}\n >\n <RNText\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(theme.fontWeightSemibold) as TextStyle['fontWeight'],\n color: theme.accentColor,\n }}\n >\n {actionLabel}\n </RNText>\n </Pressable>\n )}\n\n {dismissible && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel=\"Dismiss\"\n hitSlop={4}\n onPress={() => {\n clearTimer();\n onDismiss?.();\n }}\n style={({ pressed }) => ({\n width: 28,\n height: 28,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.radiusSm,\n opacity: pressed ? 1 : 0.55,\n backgroundColor: pressed ? theme.activeOverlay : 'transparent',\n })}\n >\n <RNText style={{ fontSize: theme.fontSizeBody, color: theme.windowFgColor }}>\n {'×'}\n </RNText>\n </Pressable>\n )}\n </>\n )}\n </AnimatedPressable>\n );\n};\n","import type { ReactNode } from 'react';\nimport type { StyleProp, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface ToasterProps extends Omit<ViewProps, 'style'> {\n /**\n * Where to position the toast stack within its container.\n * - `\"bottom\"` (default) — bottom-center, following the GNOME HIG.\n * - `\"top\"` — top-center.\n */\n position?: 'bottom' | 'top';\n /** `Toast` elements to display. */\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Absolutely-positioned container that stacks `Toast` notifications.\n *\n * RN has no `document.body`/portal target to render into the way the web\n * version's `createPortal(node, container ?? document.body)` does, so\n * there's no `container` prop here — mount `Toaster` yourself as the\n * **last** child of your app's root-level `View` (so it paints on top of\n * everything else) with that root `View` given `position: 'relative'` (or\n * left as the default relative positioning). `Toaster` then positions\n * itself with `position: 'absolute'`, spanning the full width and\n * anchored to `top`/`bottom` by `position`.\n *\n * `pointerEvents=\"box-none\"` is RN's equivalent of the web version's\n * `pointer-events: none` on the container — the empty space around the\n * toast stack doesn't intercept touches, but each `Toast` inside (a\n * `Pressable`) still handles its own.\n *\n * @example\n * ```tsx\n * <View style={{ flex: 1 }}>\n * <YourAppContent />\n * <Toaster>\n * {toasts.map((t) => (\n * <Toast key={t.id} title={t.message} onDismiss={() => remove(t.id)} />\n * ))}\n * </Toaster>\n * </View>\n * ```\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Toast.html\n */\nexport const Toaster = ({ position = 'bottom', children, style, ...viewProps }: ToasterProps) => {\n const theme = useGnomeTheme();\n\n return (\n <RNView\n pointerEvents=\"box-none\"\n accessibilityLabel=\"Notifications\"\n style={[\n {\n position: 'absolute',\n left: 0,\n right: 0,\n alignItems: 'center',\n gap: theme.space1,\n zIndex: 9999,\n },\n position === 'top' ? { top: theme.space4 } : { bottom: theme.space4 },\n style,\n ]}\n {...viewProps}\n >\n {children}\n </RNView>\n );\n};\n","import { createContext, forwardRef, type ReactNode, useContext, useMemo } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\n\n// ─── Context ──────────────────────────────────────────────────────────────────\n\ninterface ToggleGroupContextValue {\n value: string;\n onValueChange: (value: string) => void;\n}\n\nconst ToggleGroupContext = createContext<ToggleGroupContextValue | null>(null);\n\n/** Internal — `ToggleGroupItem` reads the selected value and setter from here. */\n// eslint-disable-next-line react-refresh/only-export-components\nexport function useToggleGroup() {\n const ctx = useContext(ToggleGroupContext);\n\n if (!ctx) {\n throw new Error('ToggleGroupItem must be used inside ToggleGroup');\n }\n\n return ctx;\n}\n\n// ─── ToggleGroup ──────────────────────────────────────────────────────────────\n\nexport interface ToggleGroupProps extends Omit<ViewProps, 'style'> {\n /** Name of the currently active toggle. */\n value: string;\n /** Called with the new value when a toggle is selected. */\n onValueChange: (value: string) => void;\n /** Accessible label for the group. */\n accessibilityLabel?: string;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Mutually-exclusive group of toggle buttons for in-place option selection.\n * Mirrors `AdwToggleGroup` (libadwaita 1.7 / GNOME 48) and\n * `@gnome-ui/react`'s own `ToggleGroup`.\n *\n * Use for formatting controls, view-mode selectors, and toolbar options —\n * wherever a `ViewSwitcher` would be too heavy or doesn't belong in a\n * `HeaderBar`. Compose with `ToggleGroupItem`.\n *\n * The context and its `value`/`onValueChange` shape port 1:1 — pure React,\n * no DOM involved. What doesn't port is the keyboard layer: the web version\n * owns an `onKeyDown` handler implementing ← / → cycling and Home / End\n * jumps over a roving `tabIndex`, none of which has a touch counterpart.\n * That's this package's standing convention, already set by `ViewSwitcher`\n * and `TabBar`; the `radiogroup`/`radio` + `checked` accessibility pairing\n * that VoiceOver and TalkBack actually announce is what carries the\n * semantics here instead.\n *\n * The group carries `accessibilityRole=\"radiogroup\"` but deliberately\n * **not** `accessible` — on iOS, `accessible` on a container collapses the\n * whole subtree into one accessibility element, which would make the\n * individual toggles unreachable for VoiceOver and defeat the point of the\n * role. Without it the role still groups on Android while every item stays\n * individually focusable. (`ViewSwitcher`, built earlier, does set\n * `accessible` alongside the same role — see this package's ROADMAP note;\n * worth revisiting there.) The visible trade-off is that the group won't\n * match a `getByRole('radiogroup')` query, since Testing Library only\n * matches roles on accessible elements — the items are what matter to a\n * screen reader, and they each match `getByRole('radio')`.\n *\n * `display: inline-flex` becomes `alignSelf: 'flex-start'` (the same\n * hug-your-content trick `ViewSwitcher` uses), and `box-shadow:\n * var(--gnome-shadow-sm)` is dropped rather than approximated — the theme\n * generator deliberately keeps the shadow tokens in `raw` only, and `Card`\n * already established that a border carries the same separation on RN.\n * The dark-mode border color is hardcoded per scheme (`rgba(255,255,255,\n * 0.12)`) exactly as the source CSS hardcodes it, rather than read from\n * `theme.light3`, which stays `#deddda` in every theme.\n *\n * @example\n * const [align, setAlign] = useState('left');\n *\n * <ToggleGroup value={align} onValueChange={setAlign} accessibilityLabel=\"Alignment\">\n * <ToggleGroupItem name=\"left\" icon={FormatJustifyLeft} accessibilityLabel=\"Left\" />\n * <ToggleGroupItem name=\"center\" icon={FormatJustifyCenter} accessibilityLabel=\"Center\" />\n * <ToggleGroupItem name=\"right\" icon={FormatJustifyRight} accessibilityLabel=\"Right\" />\n * </ToggleGroup>\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ToggleGroup.html\n */\nexport const ToggleGroup = forwardRef<View, ToggleGroupProps>(function ToggleGroup(\n { value, onValueChange, accessibilityLabel = 'Options', children, style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const context = useMemo(() => ({ value, onValueChange }), [value, onValueChange]);\n\n return (\n <ToggleGroupContext.Provider value={context}>\n <RNView\n ref={ref}\n accessibilityRole=\"radiogroup\"\n accessibilityLabel={accessibilityLabel}\n style={[\n {\n alignSelf: 'flex-start',\n flexDirection: 'row',\n alignItems: 'stretch',\n gap: 2,\n padding: 2,\n borderRadius: theme.radiusMd,\n borderWidth: 1,\n borderColor: scheme === 'dark' ? 'rgba(255, 255, 255, 0.12)' : theme.light3,\n backgroundColor: theme.cardBgColor,\n },\n style,\n ]}\n {...viewProps}\n >\n {children}\n </RNView>\n </ToggleGroupContext.Provider>\n );\n});\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport { forwardRef } from 'react';\nimport type { PressableProps, TextStyle, View } from 'react-native';\nimport { Pressable, Text as RNText, View as RNView } from 'react-native';\n\nimport { Icon } from '@/components/Icon';\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\n\nimport { useToggleGroup } from './ToggleGroup';\n\nexport interface ToggleGroupItemProps\n extends Omit<PressableProps, 'children' | 'style' | 'disabled' | 'onPress'> {\n /** String identifier — becomes the group's `value` when this item is selected. */\n name: string;\n /** Visible label. Omit for icon-only items (pass `accessibilityLabel` instead). */\n label?: string;\n /** Icon from `@gnome-ui/icons`. */\n icon?: IconDefinition;\n /** Accessible name. Required for icon-only items; defaults to `label`. */\n accessibilityLabel?: string;\n disabled?: boolean;\n}\n\n/**\n * Individual toggle inside a `ToggleGroup`. Can be icon-only, label-only, or\n * icon + label — for icon-only items always pass an `accessibilityLabel` so\n * screen readers can identify the option.\n *\n * The active state's three `color-mix(in srgb, accent N%, transparent)`\n * values resolve to 8-digit `#RRGGBBAA` hexes off `theme.accentBgColor`\n * (always a plain 6-digit hex), the technique `Chip` already established for\n * exactly this selected-tint problem — `26` for 15% and `66` for 40% in\n * light, `40` for 25% and `80` for 50% in dark. The CSS paints its active\n * ring as an `inset 0 0 0 1px` box-shadow, which RN has no equivalent for;\n * it becomes a real `borderWidth: 1` that every item carries at all times\n * (transparent when inactive) so selecting one never shifts the row's\n * layout — the same substitution `AvatarGroup` made for its own ring.\n *\n * `:hover` has no touch counterpart and collapses away; `:active` maps to\n * `Pressable`'s `pressed` state using `theme.activeOverlay`, whose light\n * (`rgba(0, 0, 0, 0.12)`) and dark (`rgba(255, 255, 255, 0.14)`) values are\n * character-for-character the CSS's own `:active` colors — the same\n * coincidence `ViewSwitcherItem` documented and reused. The\n * `background-color`/`box-shadow` transition is dropped rather than\n * rebuilt on `Animated`: `ViewSwitcherItem`, the closest sibling, doesn't\n * animate its own selection either, so there is no reduced-motion handling\n * to wire up here.\n *\n * The icon keeps the default foreground color instead of tracking the\n * active accent text: `Icon` has no `currentColor` equivalent, and its\n * `color` prop is a fixed GNOME palette (`blue`, `green`, …) with no\n * `accent` member — which couldn't follow the app's configurable accent\n * anyway. Same call, same reason, as `Chip`.\n *\n * `icon` is an `IconDefinition` rather than the `ReactNode` the older\n * `ViewSwitcherItem` takes; that component predates `Icon` shipping in this\n * package, while `Chip` and the web version both type it this way.\n */\nexport const ToggleGroupItem = forwardRef<View, ToggleGroupItemProps>(function ToggleGroupItem(\n { name, label, icon, accessibilityLabel, disabled = false, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n const { value, onValueChange } = useToggleGroup();\n\n const active = value === name;\n const iconOnly = Boolean(icon) && !label;\n const isDark = scheme === 'dark';\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n onPress={() => onValueChange(name)}\n accessibilityRole=\"radio\"\n accessibilityState={{ checked: active, disabled }}\n accessibilityLabel={accessibilityLabel ?? label}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n gap: theme.space1,\n minHeight: 28,\n minWidth: iconOnly ? 28 : undefined,\n paddingVertical: 4,\n paddingHorizontal: iconOnly ? 4 : theme.space2,\n borderRadius: theme.radiusMd - 2,\n borderWidth: 1,\n borderColor: active ? `${theme.accentBgColor}${isDark ? '80' : '66'}` : 'transparent',\n backgroundColor: active\n ? `${theme.accentBgColor}${isDark ? '40' : '26'}`\n : pressed && !disabled\n ? theme.activeOverlay\n : 'transparent',\n opacity: disabled ? theme.opacityDisabled : 1,\n })}\n {...pressableProps}\n >\n {icon ? (\n <RNView style={{ flexShrink: 0 }}>\n <Icon icon={icon} size=\"md\" />\n </RNView>\n ) : null}\n\n {label ? (\n <RNText\n numberOfLines={1}\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(\n active ? theme.fontWeightSemibold : theme.fontWeightNormal,\n ) as TextStyle['fontWeight'],\n color: active ? theme.accentColor : theme.windowFgColor,\n }}\n >\n {label}\n </RNText>\n ) : null}\n </Pressable>\n );\n});\n","import { forwardRef } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface ToolbarProps extends Omit<ViewProps, 'style'> {\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Horizontal action bar following the libadwaita `.toolbar` pattern —\n * mirrors `@gnome-ui/react`'s `Toolbar`. Directly portable: no web-only\n * APIs, just a flex row with fixed padding/gap.\n *\n * Provides `theme.space1` (6 dp) padding and gap — the standard spacing\n * for rows of flat buttons in header bars, action bars, and tool rows.\n * Place a `Spacer` between leading and trailing groups to push trailing\n * items to the end. Use `Button variant=\"flat\"` for buttons that blend\n * into the bar, or `variant=\"raised\"` for one that needs explicit\n * elevation within a flat context.\n *\n * The web CSS's `color`/`font-family` on `.toolbar` are dropped — RN has\n * no style inheritance from a parent `View` down to child `Text`\n * elements the way CSS `color` cascades, so a value here would reach\n * nothing (every child, e.g. `Button`, already sets its own explicit\n * text/icon colors).\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest/style-classes.html#toolbar-style-class\n */\nexport const Toolbar = forwardRef<View, ToolbarProps>(function Toolbar(\n { style, children, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <RNView\n ref={ref}\n style={[\n {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space1,\n padding: theme.space1,\n minHeight: 46,\n },\n style,\n ]}\n {...viewProps}\n >\n {children}\n </RNView>\n );\n});\n","import { forwardRef, type ReactNode } from 'react';\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { View as RNView, StyleSheet } from 'react-native';\n\nimport { useGnomeTheme, useResolvedColorScheme } from '@/GnomeProvider';\n\nexport interface ViewSwitcherProps extends Omit<ViewProps, 'style'> {\n children?: ReactNode;\n /** Accessible label for the group. */\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Segmented control for switching between major views.\n *\n * Mirrors the Adwaita `AdwViewSwitcher` pattern. Place as a `HeaderBar`'s\n * `title` for the canonical GNOME layout. Compose with `ViewSwitcherItem`\n * for each option.\n *\n * The web version's pill background is `color-mix(in srgb,\n * var(--gnome-headerbar-bg-color) 75%, black 25%)` (70/30 in dark mode) —\n * RN has no `color-mix()`, but alpha-compositing a flat semi-transparent\n * black `View` over an opaque background produces the exact same math\n * (`background × (1 − alpha) + black × alpha`), so that's what's layered\n * here instead, matching `Card`'s established color-mix-avoidance pattern.\n * `useResolvedColorScheme()` picks the 25%/30% split to match the web\n * value exactly rather than reusing a nearby overlay token.\n *\n * Roving-tabindex ← → / Home / End keyboard navigation isn't ported —\n * same reasoning as `TabBar`: no per-app arrow-key convention exists in\n * RN's touch-first focus model to port it to.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ViewSwitcher.html\n * @see https://developer.gnome.org/hig/patterns/nav/view-switchers.html\n */\nexport const ViewSwitcher = forwardRef<View, ViewSwitcherProps>(function ViewSwitcher(\n { children, accessibilityLabel = 'View switcher', style, ...viewProps },\n ref,\n) {\n const theme = useGnomeTheme();\n const scheme = useResolvedColorScheme();\n\n return (\n <RNView\n ref={ref}\n accessible\n accessibilityRole=\"radiogroup\"\n accessibilityLabel={accessibilityLabel}\n style={[\n {\n alignSelf: 'flex-start',\n borderRadius: theme.radiusPill,\n overflow: 'hidden',\n backgroundColor: theme.headerbarBgColor,\n },\n style,\n ]}\n {...viewProps}\n >\n <RNView\n pointerEvents=\"none\"\n style={[\n StyleSheet.absoluteFill,\n { backgroundColor: scheme === 'dark' ? 'rgba(0, 0, 0, 0.3)' : 'rgba(0, 0, 0, 0.25)' },\n ]}\n />\n <RNView style={{ flexDirection: 'row', alignItems: 'center', gap: 2, padding: 3 }}>\n {children}\n </RNView>\n </RNView>\n );\n});\n","import { forwardRef, type ReactNode } from 'react';\nimport type { PressableProps, TextStyle, View } from 'react-native';\nimport { Pressable, Text as RNText, View as RNView } from 'react-native';\n\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface ViewSwitcherItemProps\n extends Omit<PressableProps, 'children' | 'style' | 'disabled'> {\n /** Display label. */\n label: string;\n /**\n * Icon placed before the label. Rendered as-is — size/color it yourself.\n * Web's `icon` takes an `@gnome-ui/icons` `IconDefinition`; this package\n * has no SVG icon system, so it's a plain `ReactNode` here, same as\n * `TabItem`'s `icon`.\n */\n icon?: ReactNode;\n /** Marks this item as the currently active view. */\n active?: boolean;\n disabled?: boolean;\n}\n\n/**\n * Individual option inside a `ViewSwitcher`.\n *\n * Renders with `accessibilityRole=\"radio\"` so the group reads as a proper\n * radio group. The pressed-state tint on non-active items\n * (`theme.activeOverlay`) isn't a stand-in approximation here — its light\n * (`rgba(0, 0, 0, 0.12)`) and dark (`rgba(255, 255, 255, 0.14)`) values\n * happen to match the web version's `:active` colors exactly, so it's\n * reused directly rather than hand-rolled.\n */\nexport const ViewSwitcherItem = forwardRef<View, ViewSwitcherItemProps>(function ViewSwitcherItem(\n { label, icon, active = false, disabled, ...pressableProps },\n ref,\n) {\n const theme = useGnomeTheme();\n\n return (\n <Pressable\n ref={ref}\n disabled={disabled}\n accessibilityRole=\"radio\"\n accessibilityState={{ checked: active, disabled: !!disabled }}\n accessibilityLabel={label}\n style={({ pressed }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n gap: theme.space1,\n paddingVertical: 5,\n paddingHorizontal: theme.space2,\n borderRadius: theme.radiusPill - 3,\n opacity: disabled ? theme.opacityDisabled : 1,\n backgroundColor: active\n ? theme.headerbarBgColor\n : pressed\n ? theme.activeOverlay\n : 'transparent',\n })}\n {...pressableProps}\n >\n {icon && <RNView style={{ flexShrink: 0 }}>{icon}</RNView>}\n\n <RNText\n style={{\n fontFamily: theme.fontFamily,\n fontSize: theme.fontSizeBody,\n fontWeight: String(\n active ? theme.fontWeightSemibold : theme.fontWeightNormal,\n ) as TextStyle['fontWeight'],\n color: theme.headerbarFgColor,\n }}\n >\n {label}\n </RNText>\n </Pressable>\n );\n});\n","import type { IconDefinition } from '@gnome-ui/icons';\nimport { DocumentEdit } from '@gnome-ui/icons';\nimport { type ReactNode, useState } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { Pressable, ScrollView, View } from 'react-native';\n\nimport { ActionRow } from '@/components/ActionRow';\nimport { BottomSheet } from '@/components/BottomSheet';\nimport { BoxedList } from '@/components/BoxedList';\nimport { Button } from '@/components/Button';\nimport { Dialog } from '@/components/Dialog';\nimport { Drawer } from '@/components/Drawer';\nimport { Icon } from '@/components/Icon';\nimport { IconButton } from '@/components/IconButton';\nimport { StatusPage } from '@/components/StatusPage';\nimport { Text } from '@/components/Text';\nimport { useGnomeTheme } from '@/GnomeProvider';\n\nexport interface WidgetDefinition {\n /** Stable unique identifier. Also the value stored in `value`. */\n id: string;\n /** Row label shown in the picker catalog and above the widget once added. */\n label: string;\n /** Optional secondary line shown below `label` in the picker row. */\n description?: string;\n /** Icon shown in the picker row and in the widget's own header once added. */\n icon?: IconDefinition;\n /** Renders the widget's body once it has been added to the manager. */\n render: () => ReactNode;\n}\n\n/**\n * `@gnome-ui/react`'s `pickerSurface` calls this option `\"modal\"`, naming it\n * after its own `Modal` component. This package's `Modal` counterpart is\n * `Dialog` (RN's own `Modal` primitive is a different, lower-level thing —\n * see the main `ROADMAP.md`'s note on that exact naming trap), so the option\n * is named after what it actually renders here instead of ported verbatim.\n */\nexport type WidgetManagerPickerSurface = 'dialog' | 'bottomSheet' | 'drawer';\n\nexport interface WidgetManagerProps {\n /** Full catalog of widgets available to add. */\n catalog: WidgetDefinition[];\n /** Controlled list of added widget ids, in display order. */\n value: string[];\n /** Called with the new id list when the user confirms the picker. */\n onChange: (value: string[]) => void;\n /** Header title. */\n title: string;\n /** Icon shown at the leading edge of the header. */\n icon?: IconDefinition;\n /** Which overlay renders the catalog picker. Defaults to `\"dialog\"`. */\n pickerSurface?: WidgetManagerPickerSurface;\n /** Label for the dashed \"add widget\" trigger. Defaults to `\"Add Widget\"`. */\n addTriggerLabel?: string;\n /** Message shown when there are no widgets and not in edit mode. Defaults to `\"No widgets added\"`. */\n emptyStateLabel?: string;\n /** Heading of the picker overlay. Defaults to `\"Widgets\"`. */\n pickerTitle?: string;\n /** Label for a catalog row's add action. Defaults to `\"Add\"`. */\n addLabel?: string;\n /** Label for a catalog row's remove action (already staged). Defaults to `\"Remove\"`. */\n removeLabel?: string;\n /** Label for the picker's confirm action. Defaults to `\"Accept\"`. */\n confirmLabel?: string;\n /** Label for the picker's cancel action. Defaults to `\"Cancel\"`. */\n cancelLabel?: string;\n /** Accessible name of the header's edit-mode toggle button. Defaults to `\"Edit widgets\"`. */\n editLabel?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst semibold = (theme: { fontWeightSemibold: number }): TextStyle['fontWeight'] =>\n String(theme.fontWeightSemibold) as TextStyle['fontWeight'];\n\n/**\n * Card that manages a controlled collection of \"widgets\" — pick which ones\n * are visible from a catalog, each rendering its own arbitrary content.\n * Mirrors `@gnome-ui/react`'s own `WidgetManager`.\n *\n * The header's edit button toggles a local `editing` state: in view mode\n * only the added widgets (or an empty-state message) are shown; in edit\n * mode a dashed \"add widget\" trigger also appears, opening a catalog picker\n * (`pickerSurface`: `\"dialog\"`, `\"bottomSheet\"`, or `\"drawer\"`, each backed\n * by the already-shipped `Dialog`/`BottomSheet`/`Drawer`). Adding/removing\n * is staged inside the picker and only applied — via `onChange` — when the\n * user confirms; canceling or dismissing the picker discards the staging.\n * Widgets can only be removed through the picker, never inline in the card.\n *\n * The catalog list is wrapped in its own `ScrollView` (capped at 360dp)\n * before being handed to whichever picker surface renders it — unlike the\n * web version's `overflow-y: auto` on the `Modal`/`BottomSheet`/`Drawer`\n * body, none of this package's three overlay components scroll their\n * `children` for you, so a long catalog needs that scroll container built\n * in here rather than assumed.\n *\n * `Dialog` already renders its own confirm/cancel action row from a\n * `buttons` array, so only `bottomSheet`/`drawer` need the hand-rolled\n * footer row the web version calls `pickerFooter` — the exact same split\n * the web source itself documents (\"Modal uses its own actions\").\n *\n * Not ported: `aria-pressed` on the edit toggle — `Button`/`IconButton` set\n * their own internal `accessibilityState={{ disabled }}` on the underlying\n * `Pressable`, and RN merges a spread prop object outright rather than\n * key-by-key, so passing a second `accessibilityState` here would silently\n * replace rather than merge with it (the same `Popover`-trigger clobber\n * `SplitButton` already worked around) — dropped rather than routed around\n * for one decorative toggle-state announcement.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.WidgetManager.html\n */\nexport const WidgetManager = ({\n catalog,\n value,\n onChange,\n title,\n icon,\n pickerSurface = 'dialog',\n addTriggerLabel = 'Add Widget',\n emptyStateLabel = 'No widgets added',\n pickerTitle = 'Widgets',\n addLabel = 'Add',\n removeLabel = 'Remove',\n confirmLabel = 'Accept',\n cancelLabel = 'Cancel',\n editLabel = 'Edit widgets',\n style,\n testID,\n}: WidgetManagerProps) => {\n const theme = useGnomeTheme();\n const [editing, setEditing] = useState(false);\n const [pickerOpen, setPickerOpen] = useState(false);\n const [stagedIds, setStagedIds] = useState<string[]>(value);\n\n const openPicker = () => {\n setStagedIds(value);\n setPickerOpen(true);\n };\n\n const toggleStaged = (id: string) => {\n setStagedIds((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]));\n };\n\n const confirm = () => {\n onChange(stagedIds);\n setPickerOpen(false);\n };\n\n const cancel = () => setPickerOpen(false);\n\n const addedWidgets = value\n .map((id) => catalog.find((widget) => widget.id === id))\n .filter((widget): widget is WidgetDefinition => widget !== undefined);\n\n const hasWidgets = addedWidgets.length > 0;\n const showEmptyState = !editing && !hasWidgets;\n\n const badge = (widgetIcon: IconDefinition, background: string) => (\n <View\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n style={{\n width: 28,\n height: 28,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 14,\n backgroundColor: background,\n }}\n >\n <Icon icon={widgetIcon} />\n </View>\n );\n\n const catalogList = (\n <ScrollView style={{ maxHeight: 360 }}>\n <BoxedList>\n {catalog.map((widget) => {\n const staged = stagedIds.includes(widget.id);\n\n return (\n <ActionRow\n key={widget.id}\n leading={widget.icon && <Icon icon={widget.icon} />}\n title={widget.label}\n subtitle={widget.description}\n trailing={\n <Button\n variant={staged ? 'default' : 'suggested'}\n size=\"sm\"\n onPress={() => toggleStaged(widget.id)}\n >\n {staged ? removeLabel : addLabel}\n </Button>\n }\n />\n );\n })}\n </BoxedList>\n </ScrollView>\n );\n\n const pickerFooter = (\n <View\n style={{\n flexDirection: 'row',\n justifyContent: 'flex-end',\n gap: theme.space2,\n paddingTop: theme.space2,\n }}\n >\n <Button variant=\"flat\" onPress={cancel}>\n {cancelLabel}\n </Button>\n <Button variant=\"suggested\" onPress={confirm}>\n {confirmLabel}\n </Button>\n </View>\n );\n\n return (\n <View\n testID={testID}\n style={[\n {\n gap: theme.space3,\n padding: theme.space4,\n backgroundColor: theme.cardBgColor,\n borderWidth: 1,\n borderColor: theme.cardShadeColor,\n borderRadius: theme.radiusLg,\n },\n style,\n ]}\n >\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: theme.space2 }}>\n <View\n style={{\n flex: 1,\n minWidth: 0,\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.space2,\n }}\n >\n {icon && badge(icon, theme.viewBgColor)}\n <Text variant=\"body\" numberOfLines={1} style={{ fontWeight: semibold(theme) }}>\n {title}\n </Text>\n </View>\n\n <IconButton\n icon={DocumentEdit}\n label={editLabel}\n variant=\"flat\"\n size=\"sm\"\n onPress={() => setEditing((prev) => !prev)}\n />\n </View>\n\n <View style={{ gap: theme.space3 }}>\n {showEmptyState && <StatusPage compact title={emptyStateLabel} />}\n\n {hasWidgets && (\n <View style={{ gap: theme.space3 }}>\n {addedWidgets.map((widget) => (\n <View\n key={widget.id}\n style={{\n gap: theme.space2,\n padding: theme.space3,\n backgroundColor: theme.viewBgColor,\n borderRadius: theme.radiusMd,\n }}\n >\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: theme.space2 }}>\n {widget.icon && badge(widget.icon, theme.cardBgColor)}\n <Text variant=\"body\" style={{ fontWeight: semibold(theme) }}>\n {widget.label}\n </Text>\n </View>\n <View style={{ minWidth: 0 }}>{widget.render()}</View>\n </View>\n ))}\n </View>\n )}\n\n {editing && (\n <Pressable\n accessibilityRole=\"button\"\n onPress={openPicker}\n style={({ pressed }) => ({\n width: '100%',\n paddingVertical: theme.space4,\n alignItems: 'center',\n justifyContent: 'center',\n borderWidth: 2,\n borderStyle: 'dashed',\n borderColor: pressed ? theme.accentColor : theme.cardShadeColor,\n borderRadius: theme.radiusLg,\n opacity: pressed ? 1 : theme.opacityDim,\n })}\n >\n <Text variant=\"body\" style={{ fontWeight: semibold(theme) }}>\n {addTriggerLabel}\n </Text>\n </Pressable>\n )}\n </View>\n\n {pickerSurface === 'dialog' && (\n <Dialog\n open={pickerOpen}\n title={pickerTitle}\n onClose={cancel}\n buttons={[\n { label: cancelLabel, onPress: cancel },\n { label: confirmLabel, variant: 'suggested', onPress: confirm },\n ]}\n >\n {catalogList}\n </Dialog>\n )}\n\n {pickerSurface === 'bottomSheet' && (\n <BottomSheet open={pickerOpen} title={pickerTitle} onClose={cancel}>\n {catalogList}\n {pickerFooter}\n </BottomSheet>\n )}\n\n {pickerSurface === 'drawer' && (\n <Drawer open={pickerOpen} title={pickerTitle} onClose={cancel}>\n {catalogList}\n {pickerFooter}\n </Drawer>\n )}\n </View>\n );\n};\n"],"mappings":"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"}
|