@tastic/hud 0.1.6 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,39 +1,260 @@
1
+ // src/BaseSettingsDialog.tsx
2
+ import { AutoAppearancePicker, Dialog, useAutoPaperTheme } from "@rific/auto-paper";
3
+ import { Button, SoundContext, TouchableRipple, useHapticSettings, useSoundSettings, useVibration } from "@rific/feedback-press";
4
+ import { useUpdater } from "@rific/updater";
5
+ import { useIsTouchPrimaryDevice } from "@tastic/core";
6
+ import { useContext, useState } from "react";
7
+ import { Platform, ScrollView, StyleSheet, View } from "react-native";
8
+ import { Icon, Portal, Text } from "react-native-paper";
9
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ function SettingIcon({ source, color, containerColor }) {
11
+ return /* @__PURE__ */ jsx(View, { style: [styles.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ jsx(Icon, { source, size: 18, color }) });
12
+ }
13
+ function BaseSettingsDialog({ visible, onDismiss, rotation = 0, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, children }) {
14
+ const { dark, colors } = useAutoPaperTheme();
15
+ const isTouchPrimary = useIsTouchPrimaryDevice();
16
+ const { settings: hapticSettings, set: setHapticSettings } = useHapticSettings();
17
+ const { settings: soundSettings, set: setSoundSettings } = useSoundSettings();
18
+ const sound = useContext(SoundContext);
19
+ const { forceShort: forceHapticFeedback } = useVibration();
20
+ const [infoMessage, setInfoMessage] = useState(null);
21
+ const { check, checking, updateReady } = useUpdater({
22
+ autoCheck: false,
23
+ autoPrompt: false,
24
+ onError: onUpdateError,
25
+ onInfo: (title, message) => setInfoMessage({ title, message })
26
+ });
27
+ const showLockOrientation = isTouchPrimary && lockOrientation !== void 0 && onLockOrientationChange !== void 0;
28
+ const showEdgeGuard = Platform.OS === "ios" && deferBottomEdgeGestures !== void 0 && onDeferBottomEdgeGestures !== void 0;
29
+ const showHaptics = Platform.OS !== "web" && !hideHaptics;
30
+ const showSound = !hideSound;
31
+ const showAppearance = !hideAppearance;
32
+ const showUpdateCheck = Platform.OS !== "web" && !hideUpdateCheck;
33
+ const fg = dark ? "#FFFFFF" : "#000000";
34
+ const cardBg = dark ? "#111111" : "#F2F2F2";
35
+ const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
36
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
37
+ /* @__PURE__ */ jsxs(Dialog, { visible, onDismiss, style: [styles.dialog, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: [
38
+ /* @__PURE__ */ jsx(Dialog.Title, { children: "Settings" }),
39
+ /* @__PURE__ */ jsx(Dialog.ScrollArea, { style: styles.scrollArea, children: /* @__PURE__ */ jsxs(ScrollView, { contentContainerStyle: styles.content, showsVerticalScrollIndicator: false, children: [
40
+ /* @__PURE__ */ jsxs(View, { style: styles.toggleGroup, children: [
41
+ showLockOrientation && /* @__PURE__ */ jsx(TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ jsxs(View, { style: styles.toggleContent, children: [
42
+ /* @__PURE__ */ jsx(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.secondary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.secondaryContainer : colors.surfaceVariant }),
43
+ /* @__PURE__ */ jsxs(View, { style: styles.flexShrink, children: [
44
+ /* @__PURE__ */ jsx(Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
45
+ /* @__PURE__ */ jsx(Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
46
+ ] })
47
+ ] }) }),
48
+ showEdgeGuard && /* @__PURE__ */ jsx(TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ jsxs(View, { style: styles.toggleContent, children: [
49
+ /* @__PURE__ */ jsx(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.secondary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.secondaryContainer : colors.surfaceVariant }),
50
+ /* @__PURE__ */ jsxs(View, { style: styles.flexShrink, children: [
51
+ /* @__PURE__ */ jsx(Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
52
+ /* @__PURE__ */ jsx(Text, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
53
+ ] })
54
+ ] }) }),
55
+ (showSound || showHaptics) && /* @__PURE__ */ jsxs(View, { style: styles.toggleRow, children: [
56
+ showSound && /* @__PURE__ */ jsx(
57
+ TouchableRipple,
58
+ {
59
+ soundDisabled: true,
60
+ onPress: () => {
61
+ const enabled = !soundSettings.enabled;
62
+ setSoundSettings({ enabled });
63
+ if (enabled) sound.selection?.();
64
+ },
65
+ style: styles.toggleButtonInRow,
66
+ accessibilityLabel: `Sound ${soundSettings.enabled ? "on" : "off"}`,
67
+ children: /* @__PURE__ */ jsxs(View, { style: styles.toggleContent, children: [
68
+ /* @__PURE__ */ jsx(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
69
+ /* @__PURE__ */ jsx(Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
70
+ ] })
71
+ }
72
+ ),
73
+ showHaptics && /* @__PURE__ */ jsx(
74
+ TouchableRipple,
75
+ {
76
+ hapticDisabled: true,
77
+ onPress: () => {
78
+ const vibrate = !hapticSettings.vibrate;
79
+ setHapticSettings({ vibrate });
80
+ if (vibrate) forceHapticFeedback();
81
+ },
82
+ style: styles.toggleButtonInRow,
83
+ accessibilityLabel: `Haptics ${hapticSettings.vibrate ? "on" : "off"}`,
84
+ children: /* @__PURE__ */ jsxs(View, { style: styles.toggleContent, children: [
85
+ /* @__PURE__ */ jsx(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
86
+ /* @__PURE__ */ jsx(Text, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
87
+ ] })
88
+ }
89
+ )
90
+ ] })
91
+ ] }),
92
+ showAppearance && /* @__PURE__ */ jsxs(View, { style: styles.section, children: [
93
+ /* @__PURE__ */ jsx(Text, { variant: "labelMedium", style: [styles.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
94
+ /* @__PURE__ */ jsx(AutoAppearancePicker, { showLabels: false })
95
+ ] }),
96
+ children,
97
+ showUpdateCheck && /* @__PURE__ */ jsxs(View, { style: styles.section, children: [
98
+ /* @__PURE__ */ jsxs(Text, { variant: "labelSmall", style: [styles.sectionLabel, { color: colors.onSurfaceVariant }], children: [
99
+ "VERSION ",
100
+ version,
101
+ updateReady ? " \xB7 UPDATE READY" : ""
102
+ ] }),
103
+ /* @__PURE__ */ jsx(Button, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
104
+ ] })
105
+ ] }) })
106
+ ] }),
107
+ infoMessage && /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(View, { style: styles.overlay, children: /* @__PURE__ */ jsxs(View, { style: [styles.overlayCard, { backgroundColor: cardBg, borderColor: cardBorder }, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: [
108
+ /* @__PURE__ */ jsx(Icon, { source: "information-outline", size: 64, color: colors.secondary }),
109
+ /* @__PURE__ */ jsx(Text, { variant: "headlineLarge", style: [styles.overlayTitle, { color: colors.secondary }], children: infoMessage.title }),
110
+ /* @__PURE__ */ jsx(Text, { variant: "bodyLarge", style: [styles.overlayBody, { color: fg }], children: infoMessage.message }),
111
+ /* @__PURE__ */ jsx(Button, { mode: "contained", onPress: () => setInfoMessage(null), style: styles.overlayButton, buttonColor: colors.primary, textColor: colors.onPrimary, children: "OK" })
112
+ ] }) }) })
113
+ ] });
114
+ }
115
+ var styles = StyleSheet.create({
116
+ content: {
117
+ gap: 24,
118
+ paddingVertical: 20
119
+ },
120
+ // Caps the card so it never grows past the screen — without this the dialog just keeps
121
+ // growing to fit its content and the overflow gets clipped by the screen edge, which is
122
+ // what happened in landscape where there's less height to work with. Dialog.ScrollArea +
123
+ // ScrollView below then take over and let the content scroll within that bound.
124
+ dialog: {
125
+ maxHeight: "90%"
126
+ },
127
+ flexShrink: {
128
+ flexShrink: 1
129
+ },
130
+ iconBadge: {
131
+ alignItems: "center",
132
+ borderRadius: 10,
133
+ height: 36,
134
+ justifyContent: "center",
135
+ width: 36
136
+ },
137
+ overlay: {
138
+ alignItems: "center",
139
+ backgroundColor: "rgba(0,0,0,0.72)",
140
+ bottom: 0,
141
+ justifyContent: "center",
142
+ left: 0,
143
+ position: "absolute",
144
+ right: 0,
145
+ top: 0
146
+ },
147
+ overlayBody: { textAlign: "center" },
148
+ overlayButton: { width: 160 },
149
+ overlayCard: {
150
+ alignItems: "center",
151
+ borderRadius: 20,
152
+ borderWidth: 1,
153
+ gap: 16,
154
+ maxWidth: 360,
155
+ padding: 32
156
+ },
157
+ overlayTitle: { fontWeight: "bold" },
158
+ scrollArea: {
159
+ marginBottom: 0
160
+ },
161
+ section: {
162
+ gap: 12
163
+ },
164
+ sectionLabel: {
165
+ letterSpacing: 2
166
+ },
167
+ // Negative margin cancels the padding so the icon still lines up with APPEARANCE/SOUND &
168
+ // HAPTICS below, while the ripple/hover highlight itself gets room to breathe on both sides
169
+ // instead of a flush edge-to-edge slab. overflow: 'hidden' makes sure that highlight actually
170
+ // clips to borderRadius instead of drawing as a plain rectangle.
171
+ toggleButton: {
172
+ borderRadius: 12,
173
+ marginHorizontal: -12,
174
+ overflow: "hidden",
175
+ paddingHorizontal: 12,
176
+ paddingVertical: 10
177
+ },
178
+ // Same shape as toggleButton but `flex: 1` instead of its own `marginHorizontal: -12` — two of
179
+ // these side by side in toggleRow would otherwise both pull inward and collide in the middle.
180
+ // toggleRow carries that edge-alignment margin once, for the row as a whole, instead.
181
+ toggleButtonInRow: {
182
+ borderRadius: 12,
183
+ flex: 1,
184
+ overflow: "hidden",
185
+ paddingHorizontal: 12,
186
+ paddingVertical: 10
187
+ },
188
+ toggleContent: {
189
+ alignItems: "center",
190
+ flexDirection: "row",
191
+ gap: 12
192
+ },
193
+ toggleGroup: {
194
+ gap: 4
195
+ },
196
+ toggleRow: {
197
+ flexDirection: "row",
198
+ gap: 4,
199
+ marginHorizontal: -12
200
+ }
201
+ });
202
+
203
+ // src/CornerActionButtons.tsx
204
+ import { IconButton } from "@rific/feedback-press";
205
+ import { StyleSheet as StyleSheet2 } from "react-native";
206
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
207
+ function CornerActionButtons({ onBack, onSettings, fg, insets }) {
208
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
209
+ /* @__PURE__ */ jsx2(IconButton, { icon: "arrow-left", iconColor: fg, size: 24, style: [styles2.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: "Back" }),
210
+ /* @__PURE__ */ jsx2(IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles2.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
211
+ ] });
212
+ }
213
+ var styles2 = StyleSheet2.create({
214
+ back: {
215
+ position: "absolute"
216
+ },
217
+ topRight: {
218
+ position: "absolute"
219
+ }
220
+ });
221
+
1
222
  // src/fonts.ts
2
- import { Platform } from "react-native";
3
- var MONO_FONT = Platform.select({ ios: "Menlo", android: "monospace", default: "monospace" });
223
+ import { Platform as Platform2 } from "react-native";
224
+ var MONO_FONT = Platform2.select({ ios: "Menlo", android: "monospace", default: "monospace" });
4
225
 
5
226
  // src/InlineColorPicker.tsx
6
227
  import { defaultColors, getContrastColor } from "@rific/auto-paper";
7
- import { TouchableRipple } from "@rific/feedback-press";
228
+ import { TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
8
229
  import { clamp } from "@tastic/core";
9
- import { ScrollView, StyleSheet as StyleSheet2, useWindowDimensions as useWindowDimensions2, View as View3 } from "react-native";
10
- import { Icon, Text } from "react-native-paper";
230
+ import { ScrollView as ScrollView2, StyleSheet as StyleSheet4, useWindowDimensions as useWindowDimensions2, View as View4 } from "react-native";
231
+ import { Icon as Icon2, Text as Text2 } from "react-native-paper";
11
232
 
12
233
  // src/PopoverBody.tsx
13
- import { StyleSheet, View } from "react-native";
14
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
234
+ import { StyleSheet as StyleSheet3, View as View2 } from "react-native";
235
+ import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
15
236
  var CARET_SIZE = 7;
16
237
  var CARET_RING_OFFSET = 2;
17
238
  var CARET_DIP = 1;
18
239
  function PopoverBody({ visible, children, align = "center", verticalAlign = "below", caretColor, caretBorderColor, caretBorderWidth = 1, triggerSize = 0 }) {
19
240
  if (!visible) return null;
20
241
  const above = verticalAlign === "above";
21
- const alignStyle = align === "left" ? styles.contentLeft : align === "right" ? styles.contentRight : styles.contentCenter;
22
- const verticalStyle = above ? styles.contentAbove : styles.contentBelow;
242
+ const alignStyle = align === "left" ? styles3.contentLeft : align === "right" ? styles3.contentRight : styles3.contentCenter;
243
+ const verticalStyle = above ? styles3.contentAbove : styles3.contentBelow;
23
244
  const ringSize = CARET_SIZE + caretBorderWidth;
24
245
  const caretOffset = (halfFootprint) => align === "right" ? { right: triggerSize / 2 - halfFootprint } : { left: triggerSize / 2 - halfFootprint };
25
246
  const caretFill = above ? "borderTopColor" : "borderBottomColor";
26
247
  const caretWidthProp = above ? "borderTopWidth" : "borderBottomWidth";
27
248
  const caretEdge = (size) => above ? { bottom: -size + CARET_DIP } : { top: -size + CARET_DIP };
28
- return /* @__PURE__ */ jsxs(View, { style: [styles.content, alignStyle, verticalStyle], children: [
249
+ return /* @__PURE__ */ jsxs3(View2, { style: [styles3.content, alignStyle, verticalStyle], children: [
29
250
  children,
30
- caretColor && /* @__PURE__ */ jsxs(Fragment, { children: [
31
- /* @__PURE__ */ jsx(View, { style: [styles.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
32
- /* @__PURE__ */ jsx(View, { style: [styles.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
251
+ caretColor && /* @__PURE__ */ jsxs3(Fragment3, { children: [
252
+ /* @__PURE__ */ jsx3(View2, { style: [styles3.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
253
+ /* @__PURE__ */ jsx3(View2, { style: [styles3.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
33
254
  ] })
34
255
  ] });
35
256
  }
36
- var styles = StyleSheet.create({
257
+ var styles3 = StyleSheet3.create({
37
258
  caret: {
38
259
  borderLeftColor: "transparent",
39
260
  borderRightColor: "transparent",
@@ -76,17 +297,17 @@ var styles = StyleSheet.create({
76
297
  });
77
298
 
78
299
  // src/useAutoAlign.ts
79
- import { useCallback, useEffect, useRef, useState } from "react";
300
+ import { useCallback, useEffect, useRef, useState as useState2 } from "react";
80
301
  import { useWindowDimensions } from "react-native";
81
302
  var EDGE_MARGIN = 12;
82
303
  function useAutoAlign(open, contentWidth, contentHeight) {
83
304
  const triggerRef = useRef(null);
84
305
  const { width: windowWidth, height: windowHeight } = useWindowDimensions();
85
- const [align, setAlign] = useState("center");
86
- const [verticalAlign, setVerticalAlign] = useState("below");
87
- const [maxHeight, setMaxHeight] = useState(contentHeight);
88
- const [measured, setMeasured] = useState(false);
89
- const [prevOpen, setPrevOpen] = useState(open);
306
+ const [align, setAlign] = useState2("center");
307
+ const [verticalAlign, setVerticalAlign] = useState2("below");
308
+ const [maxHeight, setMaxHeight] = useState2(contentHeight);
309
+ const [measured, setMeasured] = useState2(false);
310
+ const [prevOpen, setPrevOpen] = useState2(open);
90
311
  if (open !== prevOpen) {
91
312
  setPrevOpen(open);
92
313
  if (open) setMeasured(false);
@@ -114,7 +335,7 @@ function useAutoAlign(open, contentWidth, contentHeight) {
114
335
  }
115
336
 
116
337
  // src/InlineColorPicker.tsx
117
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
338
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
118
339
  var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
119
340
  var DEFAULT_SIZE = 48;
120
341
  var SWATCH_SIZE = 28;
@@ -140,14 +361,14 @@ function InlineColorPicker({ id, host, value, onChange, swatches = defaultColors
140
361
  const align = alignOverride ?? autoAlign;
141
362
  const contrastColor = getContrastColor(value);
142
363
  const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : 0.4);
143
- return /* @__PURE__ */ jsxs2(View3, { style: [styles2.anchor, open && styles2.anchorOpen], children: [
144
- /* @__PURE__ */ jsx2(TouchableRipple, { onPress: () => host.toggle(id), borderless: true, style: [styles2.trigger, { backgroundColor: value, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ jsx2(View3, { ref: triggerRef, collapsable: false, style: styles2.triggerMeasure, children: tag ? /* @__PURE__ */ jsx2(Text, { style: [styles2.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ jsx2(Icon, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
145
- /* @__PURE__ */ jsx2(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: value, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ jsx2(ScrollView, { style: [styles2.swatches, { backgroundColor: menuBg, borderColor: value, width: swatchesWidth, maxHeight }], contentContainerStyle: styles2.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
364
+ return /* @__PURE__ */ jsxs4(View4, { style: [styles4.anchor, open && styles4.anchorOpen], children: [
365
+ /* @__PURE__ */ jsx4(TouchableRipple2, { onPress: () => host.toggle(id), borderless: true, style: [styles4.trigger, { backgroundColor: value, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ jsx4(View4, { ref: triggerRef, collapsable: false, style: styles4.triggerMeasure, children: tag ? /* @__PURE__ */ jsx4(Text2, { style: [styles4.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ jsx4(Icon2, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
366
+ /* @__PURE__ */ jsx4(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: value, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ jsx4(ScrollView2, { style: [styles4.swatches, { backgroundColor: menuBg, borderColor: value, width: swatchesWidth, maxHeight }], contentContainerStyle: styles4.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
146
367
  const selected = swatch.value.toLowerCase() === value.toLowerCase();
147
368
  const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
148
369
  const swappable = taken && allowSwapTaken;
149
- return /* @__PURE__ */ jsx2(
150
- TouchableRipple,
370
+ return /* @__PURE__ */ jsx4(
371
+ TouchableRipple2,
151
372
  {
152
373
  disabled: taken && !swappable,
153
374
  onPress: () => {
@@ -155,15 +376,15 @@ function InlineColorPicker({ id, host, value, onChange, swatches = defaultColors
155
376
  if (autoDismiss) host.close();
156
377
  },
157
378
  borderless: true,
158
- style: [styles2.swatch, { backgroundColor: swatch.value }, selected && styles2.swatchSelected, taken && !swappable && styles2.swatchTaken],
159
- children: selected ? /* @__PURE__ */ jsx2(Icon, { source: "check", size: 16, color: getContrastColor(swatch.value) }) : swappable ? /* @__PURE__ */ jsx2(Icon, { source: "swap-horizontal", size: 16, color: getContrastColor(swatch.value) }) : taken ? /* @__PURE__ */ jsx2(Icon, { source: "close", size: 16, color: getContrastColor(swatch.value) }) : /* @__PURE__ */ jsx2(View3, {})
379
+ style: [styles4.swatch, { backgroundColor: swatch.value }, selected && styles4.swatchSelected, taken && !swappable && styles4.swatchTaken],
380
+ children: selected ? /* @__PURE__ */ jsx4(Icon2, { source: "check", size: 16, color: getContrastColor(swatch.value) }) : swappable ? /* @__PURE__ */ jsx4(Icon2, { source: "swap-horizontal", size: 16, color: getContrastColor(swatch.value) }) : taken ? /* @__PURE__ */ jsx4(Icon2, { source: "close", size: 16, color: getContrastColor(swatch.value) }) : /* @__PURE__ */ jsx4(View4, {})
160
381
  },
161
382
  swatch.value
162
383
  );
163
384
  }) }) })
164
385
  ] });
165
386
  }
166
- var styles2 = StyleSheet2.create({
387
+ var styles4 = StyleSheet4.create({
167
388
  anchor: {
168
389
  position: "relative"
169
390
  },
@@ -234,31 +455,126 @@ var styles2 = StyleSheet2.create({
234
455
  }
235
456
  });
236
457
 
458
+ // src/LabeledDropdown.tsx
459
+ import { getContrastColor as getContrastColor2 } from "@rific/auto-paper";
460
+ import { TouchableRipple as TouchableRipple3 } from "@rific/feedback-press";
461
+ import { ScrollView as ScrollView3, StyleSheet as StyleSheet5, View as View5 } from "react-native";
462
+ import { Icon as Icon3, Text as Text3 } from "react-native-paper";
463
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
464
+ var TRIGGER_HEIGHT = 28;
465
+ var POPOVER_WIDTH = 180;
466
+ var ROW_HEIGHT = 36;
467
+ var LIST_PADDING = 8;
468
+ var LABELED_DROPDOWN_POPOVER_WIDTH = POPOVER_WIDTH;
469
+ function getLabeledDropdownContentHeight(optionCount) {
470
+ return LIST_PADDING * 2 + optionCount * ROW_HEIGHT;
471
+ }
472
+ function LabeledDropdown({ id, host, options, value, onChange, color, dark, align: forcedAlign, alignOverride }) {
473
+ const menuBg = dark ? "#000000" : "#FFFFFF";
474
+ const fg = dark ? "#FFFFFF" : "#000000";
475
+ const open = host.openId === id;
476
+ const selected = options.find((o) => o.value === value) ?? null;
477
+ const contentHeight = LIST_PADDING * 2 + options.length * ROW_HEIGHT;
478
+ const auto = useAutoAlign(open, POPOVER_WIDTH, contentHeight);
479
+ const placement = alignOverride ?? auto;
480
+ const { maxHeight, measured, triggerRef, verticalAlign } = placement;
481
+ const align = forcedAlign ?? placement.align;
482
+ const handleSelect = (option) => {
483
+ onChange(option.value);
484
+ host.close();
485
+ };
486
+ return /* @__PURE__ */ jsxs5(View5, { style: [styles5.anchor, open && styles5.anchorOpen], children: [
487
+ /* @__PURE__ */ jsx5(TouchableRipple3, { onPress: () => host.toggle(id), style: styles5.trigger, children: /* @__PURE__ */ jsxs5(View5, { ref: triggerRef, collapsable: false, style: styles5.triggerInner, children: [
488
+ /* @__PURE__ */ jsx5(Text3, { style: [styles5.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
489
+ /* @__PURE__ */ jsx5(Icon3, { source: "menu-down", size: 16, color })
490
+ ] }) }),
491
+ /* @__PURE__ */ jsx5(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ jsx5(ScrollView3, { style: [styles5.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles5.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
492
+ const isSelected = option.value === value;
493
+ const onColor = getContrastColor2(color);
494
+ return /* @__PURE__ */ jsx5(TouchableRipple3, { onPress: () => handleSelect(option), style: [styles5.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ jsxs5(View5, { style: styles5.rowInner, children: [
495
+ option.icon && /* @__PURE__ */ jsx5(Icon3, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
496
+ /* @__PURE__ */ jsx5(Text3, { style: [styles5.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
497
+ ] }) }, option.value);
498
+ }) }) })
499
+ ] });
500
+ }
501
+ var styles5 = StyleSheet5.create({
502
+ anchor: {
503
+ position: "relative"
504
+ },
505
+ // React Native Web gives every position:'relative' view its own stacking context, so the
506
+ // elevation has to live on the anchor itself — see PopoverBody's own stacking-context notes.
507
+ anchorOpen: {
508
+ zIndex: 100
509
+ },
510
+ menu: {
511
+ borderRadius: 12,
512
+ borderWidth: 2,
513
+ elevation: 8,
514
+ shadowColor: "#000000",
515
+ shadowOffset: { height: 2, width: 0 },
516
+ shadowOpacity: 0.3,
517
+ shadowRadius: 8
518
+ },
519
+ menuContent: {
520
+ padding: LIST_PADDING
521
+ },
522
+ row: {
523
+ borderRadius: 8,
524
+ height: ROW_HEIGHT
525
+ },
526
+ rowInner: {
527
+ alignItems: "center",
528
+ flexDirection: "row",
529
+ flex: 1,
530
+ gap: 8,
531
+ paddingHorizontal: 12
532
+ },
533
+ rowLabel: {
534
+ flex: 1,
535
+ fontSize: 14
536
+ },
537
+ trigger: {
538
+ alignSelf: "flex-start"
539
+ },
540
+ triggerInner: {
541
+ alignItems: "center",
542
+ flexDirection: "row",
543
+ gap: 2,
544
+ height: TRIGGER_HEIGHT
545
+ },
546
+ triggerLabel: {
547
+ fontSize: 13,
548
+ fontWeight: "700",
549
+ letterSpacing: 1
550
+ }
551
+ });
552
+
237
553
  // src/loadSkiaWeb.ts
238
554
  function loadSkiaWeb() {
239
555
  return Promise.resolve();
240
556
  }
241
557
 
242
558
  // src/PressAwayOverlay.tsx
243
- import { Pressable, StyleSheet as StyleSheet3 } from "react-native";
244
- import { jsx as jsx3 } from "react/jsx-runtime";
559
+ import { Pressable, StyleSheet as StyleSheet6 } from "react-native";
560
+ import { jsx as jsx6 } from "react/jsx-runtime";
245
561
  function PressAwayOverlay({ active, onPress, style }) {
246
562
  if (!active) return null;
247
- return /* @__PURE__ */ jsx3(Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [StyleSheet3.absoluteFill, style] });
563
+ return /* @__PURE__ */ jsx6(Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [StyleSheet6.absoluteFill, style] });
248
564
  }
249
565
 
250
566
  // src/ReadyButton.tsx
251
- import { TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
252
- import { StyleSheet as StyleSheet4 } from "react-native";
253
- import { Text as Text2 } from "react-native-paper";
254
- import { jsx as jsx4 } from "react/jsx-runtime";
567
+ import { TouchableRipple as TouchableRipple4 } from "@rific/feedback-press";
568
+ import { StyleSheet as StyleSheet7 } from "react-native";
569
+ import { Text as Text4 } from "react-native-paper";
570
+ import { jsx as jsx7 } from "react/jsx-runtime";
255
571
  function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
256
- const readyButtonStyle = [styles3.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
572
+ const readyButtonStyle = [styles6.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
257
573
  const labelTextStyle = { fontFamily: labelFontFamily, fontWeight: "bold" };
258
574
  const labelColorStyle = { color: ready ? "#000000" : color };
259
- return /* @__PURE__ */ jsx4(TouchableRipple2, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ jsx4(Text2, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
575
+ return /* @__PURE__ */ jsx7(TouchableRipple4, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ jsx7(Text4, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
260
576
  }
261
- var styles3 = StyleSheet4.create({
577
+ var styles6 = StyleSheet7.create({
262
578
  readyButton: {
263
579
  alignItems: "center",
264
580
  borderRadius: 12,
@@ -272,20 +588,20 @@ var styles3 = StyleSheet4.create({
272
588
 
273
589
  // src/SectionedDropdown.tsx
274
590
  import { getBlendedColor, getColorRoles } from "@rific/auto-paper";
275
- import { IconButton, TouchableRipple as TouchableRipple3 } from "@rific/feedback-press";
276
- import { ScrollView as ScrollView2, StyleSheet as StyleSheet5, View as View4 } from "react-native";
277
- import { Icon as Icon2, Text as Text3 } from "react-native-paper";
591
+ import { IconButton as IconButton2, TouchableRipple as TouchableRipple5 } from "@rific/feedback-press";
592
+ import { ScrollView as ScrollView4, StyleSheet as StyleSheet8, View as View6 } from "react-native";
593
+ import { Icon as Icon4, Text as Text5 } from "react-native-paper";
278
594
 
279
595
  // src/TriggerGaugeHost.tsx
280
596
  import { lazy, Suspense } from "react";
281
- import { jsx as jsx5 } from "react/jsx-runtime";
597
+ import { jsx as jsx8 } from "react/jsx-runtime";
282
598
  var LazyTriggerGauge = lazy(() => loadSkiaWeb().then(() => import("./TriggerGauge-2OJOOF6I.mjs").then((m) => ({ default: m.TriggerGauge }))));
283
599
  function TriggerGaugeHost(props) {
284
- return /* @__PURE__ */ jsx5(Suspense, { fallback: null, children: /* @__PURE__ */ jsx5(LazyTriggerGauge, { ...props }) });
600
+ return /* @__PURE__ */ jsx8(Suspense, { fallback: null, children: /* @__PURE__ */ jsx8(LazyTriggerGauge, { ...props }) });
285
601
  }
286
602
 
287
603
  // src/SectionedDropdown.tsx
288
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
604
+ import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
289
605
  var MENU_BORDER_WIDTH = 1;
290
606
  var TRIGGER_SIZE = 44;
291
607
  var MENU_MIN_WIDTH = 200;
@@ -331,17 +647,17 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
331
647
  }
332
648
  gaugeOffset += section.options.length;
333
649
  }
334
- const anchorStyle = [styles4.anchor, open && styles4.anchorOpen];
650
+ const anchorStyle = [styles7.anchor, open && styles7.anchorOpen];
335
651
  const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
336
652
  const dividerStyle = { backgroundColor: menuBorderColor };
337
- return /* @__PURE__ */ jsxs3(View4, { style: anchorStyle, children: [
338
- /* @__PURE__ */ jsxs3(View4, { ref: triggerRef, collapsable: false, style: styles4.triggerBox, children: [
339
- /* @__PURE__ */ jsx6(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
340
- /* @__PURE__ */ jsx6(IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
653
+ return /* @__PURE__ */ jsxs6(View6, { style: anchorStyle, children: [
654
+ /* @__PURE__ */ jsxs6(View6, { ref: triggerRef, collapsable: false, style: styles7.triggerBox, children: [
655
+ /* @__PURE__ */ jsx9(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
656
+ /* @__PURE__ */ jsx9(IconButton2, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
341
657
  ] }),
342
- /* @__PURE__ */ jsx6(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ jsx6(ScrollView2, { style: [styles4.menu, menuStyle], contentContainerStyle: styles4.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs3(View4, { children: [
343
- sectionIndex > 0 && /* @__PURE__ */ jsx6(View4, { style: [styles4.divider, dividerStyle] }),
344
- section.kind === "single" ? /* @__PURE__ */ jsx6(
658
+ /* @__PURE__ */ jsx9(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ jsx9(ScrollView4, { style: [styles7.menu, menuStyle], contentContainerStyle: styles7.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs6(View6, { children: [
659
+ sectionIndex > 0 && /* @__PURE__ */ jsx9(View6, { style: [styles7.divider, dividerStyle] }),
660
+ section.kind === "single" ? /* @__PURE__ */ jsx9(
345
661
  SingleSection,
346
662
  {
347
663
  section,
@@ -354,7 +670,7 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
354
670
  if (autoDismiss) host.close();
355
671
  }
356
672
  }
357
- ) : /* @__PURE__ */ jsx6(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
673
+ ) : /* @__PURE__ */ jsx9(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
358
674
  ] }, section.id)) }) })
359
675
  ] });
360
676
  }
@@ -366,17 +682,17 @@ function SingleSection({
366
682
  labelFontFamily,
367
683
  onSelect
368
684
  }) {
369
- return /* @__PURE__ */ jsx6(Fragment2, { children: section.options.map((option) => {
685
+ return /* @__PURE__ */ jsx9(Fragment4, { children: section.options.map((option) => {
370
686
  const selected = option.value === section.value;
371
687
  const rowColor = selected ? onAccentColor : mutedColor;
372
- const itemStyle = [styles4.item, selected && { backgroundColor: accentColor }];
688
+ const itemStyle = [styles7.item, selected && { backgroundColor: accentColor }];
373
689
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
374
690
  const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
375
- return /* @__PURE__ */ jsx6(TouchableRipple3, { onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ jsxs3(View4, { style: styles4.itemRow, children: [
376
- option.icon && /* @__PURE__ */ jsx6(Icon2, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
377
- /* @__PURE__ */ jsxs3(View4, { style: styles4.itemLabelColumn, children: [
378
- /* @__PURE__ */ jsx6(Text3, { variant: "labelLarge", style: labelStyle, children: option.label }),
379
- option.description && /* @__PURE__ */ jsx6(Text3, { variant: "labelSmall", style: descriptionStyle, children: option.description })
691
+ return /* @__PURE__ */ jsx9(TouchableRipple5, { onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ jsxs6(View6, { style: styles7.itemRow, children: [
692
+ option.icon && /* @__PURE__ */ jsx9(Icon4, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
693
+ /* @__PURE__ */ jsxs6(View6, { style: styles7.itemLabelColumn, children: [
694
+ /* @__PURE__ */ jsx9(Text5, { variant: "labelLarge", style: labelStyle, children: option.label }),
695
+ option.description && /* @__PURE__ */ jsx9(Text5, { variant: "labelSmall", style: descriptionStyle, children: option.description })
380
696
  ] })
381
697
  ] }) }, option.value);
382
698
  }) });
@@ -390,23 +706,23 @@ function MultiSection({
390
706
  allClearLabels
391
707
  }) {
392
708
  const allSelected = section.value.length === section.options.length;
393
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
709
+ return /* @__PURE__ */ jsxs6(Fragment4, { children: [
394
710
  section.options.map((option, i) => {
395
711
  const selected = section.value.includes(option.value);
396
712
  const rowColor = selected ? onAccentColor : mutedColor;
397
713
  const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value);
398
714
  const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
399
- const itemStyle = [styles4.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
715
+ const itemStyle = [styles7.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
400
716
  const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
401
- return /* @__PURE__ */ jsx6(TouchableRipple3, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ jsxs3(View4, { style: styles4.itemRow, children: [
402
- option.icon && /* @__PURE__ */ jsx6(Icon2, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
403
- /* @__PURE__ */ jsx6(Text3, { variant: "labelLarge", style: [styles4.itemLabelFlex, labelStyle], children: option.label })
717
+ return /* @__PURE__ */ jsx9(TouchableRipple5, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ jsxs6(View6, { style: styles7.itemRow, children: [
718
+ option.icon && /* @__PURE__ */ jsx9(Icon4, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
719
+ /* @__PURE__ */ jsx9(Text5, { variant: "labelLarge", style: [styles7.itemLabelFlex, labelStyle], children: option.label })
404
720
  ] }) }, option.value);
405
721
  }),
406
- section.allClear && /* @__PURE__ */ jsx6(TouchableRipple3, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles4.item, children: /* @__PURE__ */ jsx6(Text3, { variant: "labelLarge", style: [styles4.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
722
+ section.allClear && /* @__PURE__ */ jsx9(TouchableRipple5, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles7.item, children: /* @__PURE__ */ jsx9(Text5, { variant: "labelLarge", style: [styles7.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
407
723
  ] });
408
724
  }
409
- var styles4 = StyleSheet5.create({
725
+ var styles7 = StyleSheet8.create({
410
726
  allClearLabel: {
411
727
  fontWeight: "bold",
412
728
  textAlign: "center"
@@ -423,7 +739,7 @@ var styles4 = StyleSheet5.create({
423
739
  zIndex: 100
424
740
  },
425
741
  divider: {
426
- height: StyleSheet5.hairlineWidth,
742
+ height: StyleSheet8.hairlineWidth,
427
743
  marginVertical: 4
428
744
  },
429
745
  item: {
@@ -470,10 +786,40 @@ var styles4 = StyleSheet5.create({
470
786
  }
471
787
  });
472
788
 
789
+ // src/SharedActionBand.tsx
790
+ import { IconButton as IconButton3 } from "@rific/feedback-press";
791
+ import { StyleSheet as StyleSheet9, View as View7 } from "react-native";
792
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
793
+ function SharedActionBand({ onBack, onSettings, onRandomize, onReset, fg, popoverOpen, children }) {
794
+ return /* @__PURE__ */ jsxs7(View7, { style: [styles8.column, popoverOpen && styles8.columnOpen], children: [
795
+ /* @__PURE__ */ jsxs7(View7, { style: styles8.actionsRow, children: [
796
+ /* @__PURE__ */ jsx10(IconButton3, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
797
+ onRandomize && /* @__PURE__ */ jsx10(IconButton3, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
798
+ onReset && /* @__PURE__ */ jsx10(IconButton3, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
799
+ /* @__PURE__ */ jsx10(IconButton3, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
800
+ ] }),
801
+ children
802
+ ] });
803
+ }
804
+ var styles8 = StyleSheet9.create({
805
+ actionsRow: {
806
+ alignItems: "center",
807
+ flexDirection: "row",
808
+ gap: 16
809
+ },
810
+ column: {
811
+ alignItems: "center",
812
+ gap: 8
813
+ },
814
+ columnOpen: {
815
+ zIndex: 100
816
+ }
817
+ });
818
+
473
819
  // src/usePopoverHost.ts
474
- import { useCallback as useCallback2, useState as useState2 } from "react";
820
+ import { useCallback as useCallback2, useState as useState3 } from "react";
475
821
  function usePopoverHost() {
476
- const [openId, setOpenId] = useState2(null);
822
+ const [openId, setOpenId] = useState3(null);
477
823
  const toggle = useCallback2((id) => {
478
824
  setOpenId((prev) => prev === id ? null : id);
479
825
  }, []);
@@ -481,13 +827,19 @@ function usePopoverHost() {
481
827
  return { openId, toggle, close };
482
828
  }
483
829
  export {
830
+ BaseSettingsDialog,
831
+ CornerActionButtons,
484
832
  InlineColorPicker,
833
+ LABELED_DROPDOWN_POPOVER_WIDTH,
834
+ LabeledDropdown,
485
835
  MONO_FONT,
486
836
  PopoverBody,
487
837
  PressAwayOverlay,
488
838
  ReadyButton,
489
839
  SectionedDropdown,
840
+ SharedActionBand,
490
841
  TriggerGaugeHost,
842
+ getLabeledDropdownContentHeight,
491
843
  loadSkiaWeb,
492
844
  useAutoAlign,
493
845
  usePopoverHost