@tastic/hud 0.17.0 → 0.18.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 CHANGED
@@ -79,6 +79,8 @@ corner of the screen.
79
79
  - **`OverlayCard`** / **`OverlayBackdrop`** / **`useOverlayCardColors`** - the card and dim behind
80
80
  `ConfirmDialog` and the how-to-play guide, in the app's own theme colors and blurred when the
81
81
  player's Blur setting allows it. See Overlay cards below.
82
+ - **`DialogHeader`** / **`useCompactDialog`** - the one header every dialog opens with (an icon over
83
+ a bold title in the theme's primary), and the rule for its compact form. See Overlay cards below.
82
84
 
83
85
  ## Rotation
84
86
 
@@ -244,14 +246,15 @@ export function SettingsDialog({ visible, onDismiss, rotation = 0, settings, set
244
246
  own overlay (a result banner, a game-over card) should too, so every card in the fleet looks the same:
245
247
 
246
248
  ```tsx
247
- import { OverlayBackdrop, OverlayCard, overlayActionStyles, useOverlayCardColors } from '@tastic/hud'
249
+ import { DialogHeader, OverlayBackdrop, OverlayCard, overlayActionStyles, useCompactDialog, useOverlayCardColors } from '@tastic/hud'
248
250
 
249
251
  function GameOverCard({ onRestart }: { onRestart: () => void }) {
250
- const { fg, fgMuted } = useOverlayCardColors()
252
+ const { fgMuted } = useOverlayCardColors()
253
+ const { compact, onLayout } = useCompactDialog()
251
254
  return (
252
- <OverlayBackdrop>
255
+ <OverlayBackdrop onLayout={onLayout}>
253
256
  <OverlayCard accessibilityViewIsModal>
254
- <Text style={{ color: fg }}>Game Over</Text>
257
+ <DialogHeader icon='flag-checkered' title='Game Over' compact={compact} />
255
258
  <Text style={{ color: fgMuted }}>Score 42</Text>
256
259
  <View style={overlayActionStyles.actions}>...</View>
257
260
  </OverlayCard>
@@ -261,9 +264,18 @@ function GameOverCard({ onRestart }: { onRestart: () => void }) {
261
264
  ```
262
265
 
263
266
  - **Colors come from the app's theme.** Text is `onSurface` (`fg`) and `onSurfaceVariant` (`fgMuted`),
264
- the edge is `outlineVariant`, and the dim is the theme's `backdrop`, the same scrim auto-paper's own
265
- `Dialog` uses. The card is auto-paper's `BlurView` at elevation 3: the theme's surface with a light
266
- wash of its primary, MD3's dialog color. So a card follows each game's palette and light/dark mode.
267
+ and the dim is the theme's `backdrop`, the same scrim auto-paper's own `Dialog` uses. The card is the
268
+ Settings sheet's surface: auto-paper's `BlurView` with no elevation (the theme's plain surface, no
269
+ wash of the primary), no border, and the `Dialog`'s corner radius. So every card and the Settings
270
+ sheet look like one family, in each game's palette and light/dark mode.
271
+ - **Every dialog opens with `DialogHeader`.** A 64pt icon over a large bold title, both in the theme's
272
+ primary (pass `color` for another role, as `ConfirmDialog` does with danger when `destructive`).
273
+ `ConfirmDialog`, the how-to-play guide (titled "How to Play") and `BaseSettingsDialog` all use it;
274
+ an app's own cards should too. With `compact` the icon shrinks to 32pt and sits beside a smaller
275
+ title. `useCompactDialog(rotation?)` decides it: put its `onLayout` on the `OverlayBackdrop`, and it
276
+ is compact when the space the dialog lays out in is under `COMPACT_DIALOG_HEIGHT` (528pt) along the
277
+ card's own vertical axis, so a phone held sideways. One-button dialogs end in a single contained
278
+ primary button (`overlayActionStyles.actionButtonSingle`), the Settings sheet's Done included.
267
279
  - **Blur is automatic.** The card blurs what's behind it when the player's Blur setting is on and the
268
280
  app hands `expo-blur` to auto-paper's `Provider` (`expoBlur={ExpoBlur}`). Otherwise it paints that
269
281
  raised surface color instead: on Android (`BlurView` needs a blur target there, which a card has
@@ -273,9 +285,8 @@ function GameOverCard({ onRestart }: { onRestart: () => void }) {
273
285
  appearance picker that flips auto-paper's own `blur` theme setting, which the settings sheet, Paper
274
286
  menus and these cards all follow. The row only shows where blur can actually happen: not on
275
287
  Android, not in an app that never passed `expoBlur`, and not with `hideAppearance`.
276
- - **`getOverlayCardColors(dark)` is deprecated.** It returns the old fixed black/white card
277
- (`#F2F2F2`/`#111111`), which ignores the theme and never blurs. It stays until every game has moved
278
- its own cards onto `OverlayCard`.
288
+ - **`getOverlayCardColors` is gone.** It returned the old fixed black/white card (`#F2F2F2`/`#111111`),
289
+ which ignored the theme and never blurred. Use `useOverlayCardColors` for a card's text and edges.
279
290
 
280
291
  ## Zone-aware popovers (`useZoneClampedAlign`)
281
292
 
@@ -607,8 +618,8 @@ touches the main barrel. It's for an app that has `@tastic/core` and `@rific/upd
607
618
  - Stats: `BaseStatsScreen`, `StatSection`, `StatRow`, `AchievementRow`, `LOCKED_BADGE_COLOR`,
608
619
  `AchievementCatalogSection`, `ActivityStatSection`
609
620
  - Chrome: `CornerActionButtons`, `ContentGutter`, `StaggeredWord`, `getStaggeredWordDuration`
610
- - Overlay cards: `OverlayCard`, `OverlayBackdrop`, `useOverlayCardColors`
611
- - Style tokens: `overlayStyles`, `overlayActionStyles`, `MONO_FONT`, and the deprecated `getOverlayCardColors`
621
+ - Overlay cards: `OverlayCard`, `OverlayBackdrop`, `useOverlayCardColors`, `DialogHeader`, `useCompactDialog`, `isCompactDialogHeight`, `COMPACT_DIALOG_HEIGHT`
622
+ - Style tokens: `overlayStyles`, `overlayActionStyles`, `MONO_FONT`
612
623
 
613
624
  Each is the same component the main entry exports and is documented above. Only the import path changes:
614
625
 
@@ -669,5 +680,5 @@ resolve module".
669
680
  | `@rific/updater` | >=0.4.0, optional | main entry and `./chrome`: `BaseSettingsDialog`, `UpdateDialog` |
670
681
  | `@tastic/profile` | >=0.4.0, optional | main entry: `PlayerSetupPanel`, `AchievementUnlockList` (`ProfileChip`) |
671
682
  | `@tastic/split-screen` | >=0.5.0, optional | main entry: `useZoneClampedAlign` |
672
- | `@shopify/react-native-skia` | >=1.5.0, optional | `TriggerGaugeHost`'s gauge (lazy-loaded behind `SkiaGate`) and `./skia-gate` |
683
+ | `@shopify/react-native-skia` | >=2.6.0, optional | `TriggerGaugeHost`'s gauge (lazy-loaded behind `SkiaGate`) and `./skia-gate` |
673
684
  | `@tastic/achievements` | >=0.2.0, optional | `AchievementCatalogSection`, `AchievementUnlockList`, type-only (`AchievementCatalogRow`, first exported in 0.2.0; `AchievementTier`); `./chrome` needs it installed only to type-check |
@@ -54,7 +54,7 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
54
54
  const canvasSize = diameter + LIT_THICKNESS;
55
55
  const oval = Skia.XYWHRect(LIT_THICKNESS / 2, LIT_THICKNESS / 2, diameter, diameter);
56
56
  const unlitPath = useDerivedValue(() => {
57
- const path = Skia.Path.Make();
57
+ const path = Skia.PathBuilder.Make();
58
58
  for (let i = 0; i < segments; i++) {
59
59
  if (litIndices.includes(i)) continue;
60
60
  let local = 1;
@@ -68,13 +68,13 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
68
68
  const dashCenterDeg = startDeg + (i + 0.5) * step;
69
69
  path.addArc(oval, dashCenterDeg - 90 - sweep / 2, sweep);
70
70
  }
71
- return path;
71
+ return path.detach();
72
72
  }, [segments, litIndicesKey, step, startDeg, dashAngleDeg, totalDurationMs, oval.x, oval.y, oval.width, oval.height]);
73
73
  const litPath = useDerivedValue(() => {
74
- const path = Skia.Path.Make();
74
+ const path = Skia.PathBuilder.Make();
75
75
  if (mountProgress.value >= 1 && singleSelect) {
76
76
  path.addArc(oval, angleDeg.value - 90 - dashAngleDeg / 2, dashAngleDeg);
77
- return path;
77
+ return path.detach();
78
78
  }
79
79
  for (let i = 0; i < segments; i++) {
80
80
  const isLitNow = litIndices.includes(i);
@@ -98,7 +98,7 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
98
98
  const dashCenterDeg = startDeg + (i + 0.5) * step;
99
99
  path.addArc(oval, dashCenterDeg - 90 - sweep / 2, sweep);
100
100
  }
101
- return path;
101
+ return path.detach();
102
102
  }, [segments, litIndicesKey, transitionFromKey, singleSelect, step, startDeg, dashAngleDeg, totalDurationMs, oval.x, oval.y, oval.width, oval.height]);
103
103
  if (segments < 2) return null;
104
104
  const gaugeSizeStyle = {
@@ -2,7 +2,7 @@ import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { AchievementCatalogRow } from '@tastic/achievements';
4
4
  import { ViewRotation } from '@tastic/core';
5
- import { StyleProp, ViewStyle, Insets, TextStyle, LayoutChangeEvent } from 'react-native';
5
+ import { StyleProp, ViewStyle, Insets, LayoutChangeEvent, TextStyle } from 'react-native';
6
6
 
7
7
  interface Props$7 {
8
8
  rows: AchievementCatalogRow[];
@@ -117,25 +117,23 @@ interface Props$2 {
117
117
  }
118
118
  declare function CornerActionButtons({ onBack, onSettings, fg, insets, backIcon, backLabel, hitSlop }: Props$2): react.JSX.Element;
119
119
 
120
+ declare const COMPACT_DIALOG_HEIGHT = 528;
121
+ declare function isCompactDialogHeight(height: number): boolean;
122
+ interface DialogHeaderProps {
123
+ icon: string;
124
+ title: string;
125
+ color?: string;
126
+ compact?: boolean;
127
+ style?: StyleProp<ViewStyle>;
128
+ }
129
+ declare function DialogHeader({ icon, title, color, compact, style }: DialogHeaderProps): react.JSX.Element;
130
+
120
131
  declare const MONO_FONT: string;
121
132
 
122
- interface OverlayCardColors {
123
- fg: string;
124
- fgMuted: string;
125
- cardBg: string;
126
- cardBorder: string;
127
- }
128
- /**
129
- * @deprecated The literal black/white card colors. hud's own cards moved to the theme-driven, blur-aware
130
- * OverlayCard / OverlayBackdrop / useOverlayCardColors (OverlaySurface.tsx) on 2026-10-15; this stays
131
- * only until every app's own result cards have moved over too.
132
- */
133
- declare function getOverlayCardColors(dark: boolean): OverlayCardColors;
134
133
  declare const overlayStyles: {
135
134
  card: {
136
135
  alignItems: "center";
137
136
  borderRadius: number;
138
- borderWidth: number;
139
137
  gap: number;
140
138
  maxWidth: number;
141
139
  padding: number;
@@ -178,8 +176,9 @@ declare function useOverlayCardColors(): OverlayCardThemeColors;
178
176
  interface OverlayBackdropProps {
179
177
  children?: ReactNode;
180
178
  style?: StyleProp<ViewStyle>;
179
+ onLayout?: (e: LayoutChangeEvent) => void;
181
180
  }
182
- declare function OverlayBackdrop({ children, style }: OverlayBackdropProps): react.JSX.Element;
181
+ declare function OverlayBackdrop({ children, style, onLayout }: OverlayBackdropProps): react.JSX.Element;
183
182
  interface OverlayCardProps {
184
183
  children?: ReactNode;
185
184
  style?: StyleProp<ViewStyle>;
@@ -262,4 +261,9 @@ interface UpdateDialogProps {
262
261
  }
263
262
  declare function UpdateDialog({ rotation, autoCheck, autoPrompt, onError }: UpdateDialogProps): react.JSX.Element;
264
263
 
265
- export { AchievementCatalogSection, AchievementRow, ActivityStatSection, BaseSettingsDialog, type BaseSettingsDialogProps, BaseStatsScreen, ConfirmDialog, type ConfirmDialogProps, ContentGutter, CornerActionButtons, LOCKED_BADGE_COLOR, MONO_FONT, OverlayBackdrop, type OverlayBackdropProps, OverlayCard, type OverlayCardColors, type OverlayCardProps, type OverlayCardThemeColors, RotatedFrame, type RotatedFrameProps, StaggeredWord, type StaggeredWordProps, StatRow, StatSection, UpdateDialog, type UpdateDialogProps, getOverlayCardColors, getStaggeredWordDuration, overlayActionStyles, overlayStyles, useOverlayCardColors };
264
+ declare function useCompactDialog(rotation?: ViewRotation): {
265
+ compact: boolean;
266
+ onLayout: (e: LayoutChangeEvent) => void;
267
+ };
268
+
269
+ export { AchievementCatalogSection, AchievementRow, ActivityStatSection, BaseSettingsDialog, type BaseSettingsDialogProps, BaseStatsScreen, COMPACT_DIALOG_HEIGHT, ConfirmDialog, type ConfirmDialogProps, ContentGutter, CornerActionButtons, DialogHeader, type DialogHeaderProps, LOCKED_BADGE_COLOR, MONO_FONT, OverlayBackdrop, type OverlayBackdropProps, OverlayCard, type OverlayCardProps, type OverlayCardThemeColors, RotatedFrame, type RotatedFrameProps, StaggeredWord, type StaggeredWordProps, StatRow, StatSection, UpdateDialog, type UpdateDialogProps, getStaggeredWordDuration, isCompactDialogHeight, overlayActionStyles, overlayStyles, useCompactDialog, useOverlayCardColors };
@@ -2,7 +2,7 @@ import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { AchievementCatalogRow } from '@tastic/achievements';
4
4
  import { ViewRotation } from '@tastic/core';
5
- import { StyleProp, ViewStyle, Insets, TextStyle, LayoutChangeEvent } from 'react-native';
5
+ import { StyleProp, ViewStyle, Insets, LayoutChangeEvent, TextStyle } from 'react-native';
6
6
 
7
7
  interface Props$7 {
8
8
  rows: AchievementCatalogRow[];
@@ -117,25 +117,23 @@ interface Props$2 {
117
117
  }
118
118
  declare function CornerActionButtons({ onBack, onSettings, fg, insets, backIcon, backLabel, hitSlop }: Props$2): react.JSX.Element;
119
119
 
120
+ declare const COMPACT_DIALOG_HEIGHT = 528;
121
+ declare function isCompactDialogHeight(height: number): boolean;
122
+ interface DialogHeaderProps {
123
+ icon: string;
124
+ title: string;
125
+ color?: string;
126
+ compact?: boolean;
127
+ style?: StyleProp<ViewStyle>;
128
+ }
129
+ declare function DialogHeader({ icon, title, color, compact, style }: DialogHeaderProps): react.JSX.Element;
130
+
120
131
  declare const MONO_FONT: string;
121
132
 
122
- interface OverlayCardColors {
123
- fg: string;
124
- fgMuted: string;
125
- cardBg: string;
126
- cardBorder: string;
127
- }
128
- /**
129
- * @deprecated The literal black/white card colors. hud's own cards moved to the theme-driven, blur-aware
130
- * OverlayCard / OverlayBackdrop / useOverlayCardColors (OverlaySurface.tsx) on 2026-10-15; this stays
131
- * only until every app's own result cards have moved over too.
132
- */
133
- declare function getOverlayCardColors(dark: boolean): OverlayCardColors;
134
133
  declare const overlayStyles: {
135
134
  card: {
136
135
  alignItems: "center";
137
136
  borderRadius: number;
138
- borderWidth: number;
139
137
  gap: number;
140
138
  maxWidth: number;
141
139
  padding: number;
@@ -178,8 +176,9 @@ declare function useOverlayCardColors(): OverlayCardThemeColors;
178
176
  interface OverlayBackdropProps {
179
177
  children?: ReactNode;
180
178
  style?: StyleProp<ViewStyle>;
179
+ onLayout?: (e: LayoutChangeEvent) => void;
181
180
  }
182
- declare function OverlayBackdrop({ children, style }: OverlayBackdropProps): react.JSX.Element;
181
+ declare function OverlayBackdrop({ children, style, onLayout }: OverlayBackdropProps): react.JSX.Element;
183
182
  interface OverlayCardProps {
184
183
  children?: ReactNode;
185
184
  style?: StyleProp<ViewStyle>;
@@ -262,4 +261,9 @@ interface UpdateDialogProps {
262
261
  }
263
262
  declare function UpdateDialog({ rotation, autoCheck, autoPrompt, onError }: UpdateDialogProps): react.JSX.Element;
264
263
 
265
- export { AchievementCatalogSection, AchievementRow, ActivityStatSection, BaseSettingsDialog, type BaseSettingsDialogProps, BaseStatsScreen, ConfirmDialog, type ConfirmDialogProps, ContentGutter, CornerActionButtons, LOCKED_BADGE_COLOR, MONO_FONT, OverlayBackdrop, type OverlayBackdropProps, OverlayCard, type OverlayCardColors, type OverlayCardProps, type OverlayCardThemeColors, RotatedFrame, type RotatedFrameProps, StaggeredWord, type StaggeredWordProps, StatRow, StatSection, UpdateDialog, type UpdateDialogProps, getOverlayCardColors, getStaggeredWordDuration, overlayActionStyles, overlayStyles, useOverlayCardColors };
264
+ declare function useCompactDialog(rotation?: ViewRotation): {
265
+ compact: boolean;
266
+ onLayout: (e: LayoutChangeEvent) => void;
267
+ };
268
+
269
+ export { AchievementCatalogSection, AchievementRow, ActivityStatSection, BaseSettingsDialog, type BaseSettingsDialogProps, BaseStatsScreen, COMPACT_DIALOG_HEIGHT, ConfirmDialog, type ConfirmDialogProps, ContentGutter, CornerActionButtons, DialogHeader, type DialogHeaderProps, LOCKED_BADGE_COLOR, MONO_FONT, OverlayBackdrop, type OverlayBackdropProps, OverlayCard, type OverlayCardProps, type OverlayCardThemeColors, RotatedFrame, type RotatedFrameProps, StaggeredWord, type StaggeredWordProps, StatRow, StatSection, UpdateDialog, type UpdateDialogProps, getStaggeredWordDuration, isCompactDialogHeight, overlayActionStyles, overlayStyles, useCompactDialog, useOverlayCardColors };