panelui-native 0.13.0 → 0.16.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.
Files changed (115) hide show
  1. package/README.md +41 -23
  2. package/lib/module/components/button/index.js +12 -9
  3. package/lib/module/components/button/index.js.map +1 -1
  4. package/lib/module/components/chip/index.js +313 -0
  5. package/lib/module/components/chip/index.js.map +1 -0
  6. package/lib/module/components/direction/index.js +86 -0
  7. package/lib/module/components/direction/index.js.map +1 -0
  8. package/lib/module/components/frame/index.js +41 -39
  9. package/lib/module/components/frame/index.js.map +1 -1
  10. package/lib/module/components/heatmap-chart/index.js +772 -0
  11. package/lib/module/components/heatmap-chart/index.js.map +1 -0
  12. package/lib/module/components/input/index.js +4 -0
  13. package/lib/module/components/input/index.js.map +1 -1
  14. package/lib/module/components/message/index.js +16 -1
  15. package/lib/module/components/message/index.js.map +1 -1
  16. package/lib/module/components/scroll-canvas/index.js +175 -0
  17. package/lib/module/components/scroll-canvas/index.js.map +1 -0
  18. package/lib/module/components/scroll-text/index.js +213 -0
  19. package/lib/module/components/scroll-text/index.js.map +1 -0
  20. package/lib/module/components/select/index.js +20 -24
  21. package/lib/module/components/select/index.js.map +1 -1
  22. package/lib/module/components/slider/index.js +2 -5
  23. package/lib/module/components/slider/index.js.map +1 -1
  24. package/lib/module/components/soundwave/index.js +813 -0
  25. package/lib/module/components/soundwave/index.js.map +1 -0
  26. package/lib/module/components/switch/index.js +15 -17
  27. package/lib/module/components/switch/index.js.map +1 -1
  28. package/lib/module/components/thinking-orb/index.js +741 -0
  29. package/lib/module/components/thinking-orb/index.js.map +1 -0
  30. package/lib/module/components/toggle-button/index.js +12 -3
  31. package/lib/module/components/toggle-button/index.js.map +1 -1
  32. package/lib/module/components/typography/index.js +196 -5
  33. package/lib/module/components/typography/index.js.map +1 -1
  34. package/lib/module/hooks/index.js +1 -0
  35. package/lib/module/hooks/index.js.map +1 -1
  36. package/lib/module/hooks/use-keyboard-avoidance.js +99 -59
  37. package/lib/module/hooks/use-keyboard-avoidance.js.map +1 -1
  38. package/lib/module/hooks/use-reveal-progress.js +75 -0
  39. package/lib/module/hooks/use-reveal-progress.js.map +1 -0
  40. package/lib/module/icons/index.js +69 -0
  41. package/lib/module/icons/index.js.map +1 -1
  42. package/lib/module/index.js +9 -1
  43. package/lib/module/index.js.map +1 -1
  44. package/lib/module/native/index.js.map +1 -1
  45. package/lib/module/primitives/keyboard-avoider.js +21 -2
  46. package/lib/module/primitives/keyboard-avoider.js.map +1 -1
  47. package/lib/module/primitives/scroll-progress.js +114 -0
  48. package/lib/module/primitives/scroll-progress.js.map +1 -0
  49. package/lib/typescript/src/components/button/index.d.ts +3 -4
  50. package/lib/typescript/src/components/button/index.d.ts.map +1 -1
  51. package/lib/typescript/src/components/chip/index.d.ts +304 -0
  52. package/lib/typescript/src/components/chip/index.d.ts.map +1 -0
  53. package/lib/typescript/src/components/direction/index.d.ts +64 -0
  54. package/lib/typescript/src/components/direction/index.d.ts.map +1 -0
  55. package/lib/typescript/src/components/frame/index.d.ts +15 -13
  56. package/lib/typescript/src/components/frame/index.d.ts.map +1 -1
  57. package/lib/typescript/src/components/heatmap-chart/index.d.ts +260 -0
  58. package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -0
  59. package/lib/typescript/src/components/input/index.d.ts +19 -4
  60. package/lib/typescript/src/components/input/index.d.ts.map +1 -1
  61. package/lib/typescript/src/components/message/index.d.ts.map +1 -1
  62. package/lib/typescript/src/components/scroll-canvas/index.d.ts +66 -0
  63. package/lib/typescript/src/components/scroll-canvas/index.d.ts.map +1 -0
  64. package/lib/typescript/src/components/scroll-text/index.d.ts +46 -0
  65. package/lib/typescript/src/components/scroll-text/index.d.ts.map +1 -0
  66. package/lib/typescript/src/components/select/index.d.ts.map +1 -1
  67. package/lib/typescript/src/components/slider/index.d.ts.map +1 -1
  68. package/lib/typescript/src/components/soundwave/index.d.ts +91 -0
  69. package/lib/typescript/src/components/soundwave/index.d.ts.map +1 -0
  70. package/lib/typescript/src/components/switch/index.d.ts.map +1 -1
  71. package/lib/typescript/src/components/thinking-orb/index.d.ts +28 -0
  72. package/lib/typescript/src/components/thinking-orb/index.d.ts.map +1 -0
  73. package/lib/typescript/src/components/toggle-button/index.d.ts +12 -0
  74. package/lib/typescript/src/components/toggle-button/index.d.ts.map +1 -1
  75. package/lib/typescript/src/components/typography/index.d.ts +154 -1
  76. package/lib/typescript/src/components/typography/index.d.ts.map +1 -1
  77. package/lib/typescript/src/hooks/index.d.ts +2 -1
  78. package/lib/typescript/src/hooks/index.d.ts.map +1 -1
  79. package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts +16 -3
  80. package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts.map +1 -1
  81. package/lib/typescript/src/hooks/use-reveal-progress.d.ts +56 -0
  82. package/lib/typescript/src/hooks/use-reveal-progress.d.ts.map +1 -0
  83. package/lib/typescript/src/icons/index.d.ts +6 -0
  84. package/lib/typescript/src/icons/index.d.ts.map +1 -1
  85. package/lib/typescript/src/index.d.ts +9 -1
  86. package/lib/typescript/src/index.d.ts.map +1 -1
  87. package/lib/typescript/src/native/index.d.ts +15 -7
  88. package/lib/typescript/src/native/index.d.ts.map +1 -1
  89. package/lib/typescript/src/primitives/keyboard-avoider.d.ts +30 -3
  90. package/lib/typescript/src/primitives/keyboard-avoider.d.ts.map +1 -1
  91. package/lib/typescript/src/primitives/scroll-progress.d.ts +61 -0
  92. package/lib/typescript/src/primitives/scroll-progress.d.ts.map +1 -0
  93. package/package.json +1 -1
  94. package/src/components/button/index.tsx +13 -11
  95. package/src/components/direction/index.tsx +90 -0
  96. package/src/components/frame/index.tsx +39 -50
  97. package/src/components/heatmap-chart/index.tsx +1024 -0
  98. package/src/components/input/index.tsx +23 -4
  99. package/src/components/message/index.tsx +17 -1
  100. package/src/components/scroll-canvas/index.tsx +220 -0
  101. package/src/components/scroll-text/index.tsx +277 -0
  102. package/src/components/select/index.tsx +4 -9
  103. package/src/components/slider/index.tsx +4 -7
  104. package/src/components/soundwave/index.tsx +1029 -0
  105. package/src/components/switch/index.tsx +5 -8
  106. package/src/components/thinking-orb/index.tsx +792 -0
  107. package/src/components/typography/index.tsx +231 -10
  108. package/src/hooks/index.ts +6 -0
  109. package/src/hooks/use-keyboard-avoidance.ts +111 -65
  110. package/src/hooks/use-reveal-progress.ts +106 -0
  111. package/src/icons/index.tsx +36 -0
  112. package/src/index.ts +53 -0
  113. package/src/native/index.ts +15 -7
  114. package/src/primitives/keyboard-avoider.tsx +41 -4
  115. package/src/primitives/scroll-progress.tsx +167 -0
@@ -0,0 +1,61 @@
1
+ /**
2
+ * ScrollProgress — publishes a scroll container's position so children can
3
+ * animate against it.
4
+ *
5
+ * Scroll-driven effects all need the same two numbers: where the scroller is,
6
+ * and how tall its viewport is. Every component that wanted them measuring for
7
+ * itself would mean one scroll listener per effect and one measurement pass per
8
+ * effect per frame, for two values that are the same for all of them.
9
+ *
10
+ * It wraps the scroll view you already have rather than replacing it — the
11
+ * child is cloned with an animated scroll handler composed onto it, the same
12
+ * way `ScrollFade` does — so a `FlatList`, a `SectionList` or your own
13
+ * scrollable all work, and nothing has to be rewritten to adopt it.
14
+ *
15
+ * ```tsx
16
+ * <ScrollProgress>
17
+ * <ScrollView>
18
+ * <ScrollText>…</ScrollText>
19
+ * </ScrollView>
20
+ * </ScrollProgress>
21
+ * ```
22
+ *
23
+ * Both values are shared values, so the whole chain from scroll event to
24
+ * animated style stays on the UI thread and nothing re-renders as you scroll.
25
+ */
26
+ import { type ReactNode } from 'react';
27
+ import { type ViewProps } from 'react-native';
28
+ import { type SharedValue } from 'react-native-reanimated';
29
+ export interface ScrollProgressValue {
30
+ /** Distance scrolled, in pixels. */
31
+ offset: SharedValue<number>;
32
+ /** Height of the visible area. */
33
+ viewport: SharedValue<number>;
34
+ /** Total height of the content. */
35
+ content: SharedValue<number>;
36
+ /**
37
+ * Window-space top edge of the scroller. What turns an element's measured
38
+ * `pageY` into a position inside *this* viewport rather than the screen's —
39
+ * they differ by whatever sits above the scroller, and assuming they do not
40
+ * is why scroll effects drift under a header.
41
+ */
42
+ top: SharedValue<number>;
43
+ }
44
+ /**
45
+ * The nearest enclosing scroll container's position, or `null` outside one.
46
+ *
47
+ * It returns null rather than throwing because every consumer of this also
48
+ * takes an explicit `progress` prop — a component that can be driven by hand
49
+ * should not insist on a provider it does not need.
50
+ */
51
+ export declare function useScrollProgress(): ScrollProgressValue | null;
52
+ export interface ScrollProgressProps extends ViewProps {
53
+ className?: string;
54
+ /** Exactly one scrollable — a ScrollView, FlatList, or anything like them. */
55
+ children?: ReactNode;
56
+ }
57
+ export declare function ScrollProgress({ className, children, ...props }: ScrollProgressProps): import("react").JSX.Element;
58
+ export declare namespace ScrollProgress {
59
+ var displayName: string;
60
+ }
61
+ //# sourceMappingURL=scroll-progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-progress.d.ts","sourceRoot":"","sources":["../../../../src/primitives/scroll-progress.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EASL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAC5E,OAAiB,EAKf,KAAK,WAAW,EACjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,mBAAmB;IAClC,oCAAoC;IACpC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5B,kCAAkC;IAClC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC9B,mCAAmC;IACnC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7B;;;;;OAKG;IACH,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC1B;AAID;;;;;;GAMG;AACH,wBAAgB,iBAAiB,IAAI,mBAAmB,GAAG,IAAI,CAE9D;AASD,MAAM,WAAW,mBAAoB,SAAQ,SAAS;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,wBAAgB,cAAc,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,mBAAmB,+BA6EpF;yBA7Ee,cAAc"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "panelui-native",
3
- "version": "0.13.0",
3
+ "version": "0.16.0",
4
4
  "description": "High-performance React Native UI components for Expo. Semantic design tokens, powered by Uniwind (Tailwind v4) and Reanimated.",
5
5
  "main": "./lib/module/index.js",
6
6
  "module": "./lib/module/index.js",
@@ -115,22 +115,23 @@ export interface ButtonProps
115
115
  * `primary`/`destructive` → filled, `outline` → outlined, everything else
116
116
  * → text; `size` sets the height.
117
117
  *
118
- * A native button **fills the width it is given**, because the host is told
119
- * its size rather than left to measure the platform's content — which is
120
- * what made it collapse and then jump on first press. Put it in a row to
121
- * share the width, or in a narrower parent to shrink it.
118
+ * A native button **sizes itself to its label**, the way a platform button
119
+ * is supposed to. It does not stretch to fill its container, and `fullWidth`
120
+ * has no effect on it.
122
121
  */
123
122
  native?: boolean;
124
123
  }
125
124
 
126
125
  /**
127
- * Height given to the native host, matching the styled scale above.
126
+ * Height given to the platform button, matching the styled scale above.
128
127
  *
129
- * The host is told its size rather than asked to work it out. `matchContents`
130
- * measures the platform's own content a frame late and again when that content
131
- * changes — so the button renders at nothing, collapses against whatever sits
132
- * above it, and jolts into place on the first press. Width comes from ordinary
133
- * layout instead, which is why a native button fills its container.
128
+ * It goes on the button rather than on the host, and that distinction is the
129
+ * whole fix for the jump. A host sized to a number with an unsized control
130
+ * inside it hands the platform a box the control never agreed to: SwiftUI and
131
+ * Compose both lay out against their own intrinsic size and only settle into
132
+ * the box when something forces a second pass — which for a button is the
133
+ * first press. Given a definite height of its own, the button has nothing left
134
+ * to recompute, and the host follows it with `matchContents`.
134
135
  */
135
136
  const NATIVE_HEIGHT: Record<NonNullable<ButtonVariantProps['size']>, number> = {
136
137
  sm: 36,
@@ -199,11 +200,12 @@ export const Button = forwardRef<View, ButtonProps>(
199
200
  const isStringLabel = typeof children === 'string';
200
201
 
201
202
  return (
202
- <Host style={{ height: NATIVE_HEIGHT[size ?? 'md'] }}>
203
+ <Host matchContents>
203
204
  <NativeButton
204
205
  label={isStringLabel ? children : undefined}
205
206
  variant={NATIVE_VARIANT[variant ?? 'primary']}
206
207
  disabled={isDisabled}
208
+ style={{ height: NATIVE_HEIGHT[size ?? 'md'] }}
207
209
  onPress={props.onPress}
208
210
  >
209
211
  {/* Non-string children are React Native views, and the native
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Direction — reading direction for everything below it.
3
+ *
4
+ * ```tsx
5
+ * <Direction dir="rtl">
6
+ * <App />
7
+ * </Direction>
8
+ * ```
9
+ *
10
+ * The layout flip is Yoga's, not ours: the wrapper carries the `direction`
11
+ * style, and every row, `start`/`end` inset and horizontal padding underneath
12
+ * it mirrors natively. That is the whole reason this is a component and not a
13
+ * bare context — `direction` is a *style*, so a subtree is the unit it applies
14
+ * to.
15
+ *
16
+ * It is also the reason to prefer it over flipping the process with
17
+ * `I18nManager.forceRTL`, which needs an app restart to take effect, cannot be
18
+ * scoped to part of a screen, and cannot be previewed side by side. Here the
19
+ * value is a prop: change it and the next frame is mirrored.
20
+ *
21
+ * ## What Yoga does not flip
22
+ *
23
+ * Anything measured in raw pixels rather than laid out — a drag translation, an
24
+ * indicator offset, the direction a sweep travels. Those have to consult the
25
+ * value and negate it themselves, which is what `useDirection()` is for:
26
+ *
27
+ * ```tsx
28
+ * const dir = useDirection();
29
+ * const delta = (dir === 'rtl' ? -event.translationX : event.translationX) / width;
30
+ * ```
31
+ *
32
+ * With no provider mounted, and for a `<Direction>` given no `dir`, the value
33
+ * comes from the device — so an app that did force RTL the old way still reads
34
+ * back the truth.
35
+ */
36
+ import { createContext, useContext, useMemo, type ReactNode } from 'react';
37
+ import { I18nManager, View, type ViewProps } from 'react-native';
38
+ import { cn } from '../../utils/cn';
39
+
40
+ export type DirectionValue = 'ltr' | 'rtl';
41
+
42
+ /** What the device is set to, for anything outside a provider. */
43
+ const deviceDirection = (): DirectionValue => (I18nManager.isRTL ? 'rtl' : 'ltr');
44
+
45
+ const DirectionContext = createContext<DirectionValue | null>(null);
46
+
47
+ export interface DirectionProps extends ViewProps {
48
+ className?: string;
49
+ children?: ReactNode;
50
+ /**
51
+ * Reading direction for this subtree. Defaults to the nearest enclosing
52
+ * `Direction`, or to the device when there is none — so a nested provider
53
+ * with no `dir` inherits rather than resetting to left-to-right.
54
+ */
55
+ dir?: DirectionValue;
56
+ }
57
+
58
+ /**
59
+ * Provider. Renders one `View` — it has to, since the flip is a style — which
60
+ * fills its parent by default so it can wrap an app. Pass `className` to change
61
+ * that: `flex-none` or a width class both win over the default.
62
+ */
63
+ export function Direction({ className, children, dir, style, ...props }: DirectionProps) {
64
+ const inherited = useContext(DirectionContext);
65
+ const value = dir ?? inherited ?? deviceDirection();
66
+
67
+ // The style object is memoised because it is the thing that invalidates
68
+ // layout for the entire subtree — a fresh one each render would re-run Yoga
69
+ // on every parent render, for a value that almost never changes.
70
+ const directionStyle = useMemo(() => ({ direction: value }) as const, [value]);
71
+
72
+ return (
73
+ <DirectionContext.Provider value={value}>
74
+ <View className={cn('flex-1', className)} style={[directionStyle, style]} {...props}>
75
+ {children}
76
+ </View>
77
+ </DirectionContext.Provider>
78
+ );
79
+ }
80
+
81
+ Direction.displayName = 'Direction';
82
+
83
+ /**
84
+ * The reading direction in force, for a component that has to flip maths Yoga
85
+ * cannot flip for it. Safe with no provider mounted: it falls back to the
86
+ * device's own setting.
87
+ */
88
+ export function useDirection(): DirectionValue {
89
+ return useContext(DirectionContext) ?? deviceDirection();
90
+ }
@@ -1,23 +1,29 @@
1
1
  /**
2
- * Frame — a widget shell: a titled surface wrapping an inset panel of rows,
3
- * with an optional action on the header row and a caption underneath.
2
+ * Frame — a widget shell: a card of rows sitting in a tray, with the tray's
3
+ * one exposed strip along the top carrying the title.
4
4
  *
5
- * Layout follows the nested-radius rule (inner radius = outer radius minus the
6
- * gap between them), which is why the panel is `rounded-xl` inside a
7
- * `rounded-2xl` shell. Since v0.4.0 the radius scale is theme-scoped, so this
8
- * relationship holds while the absolute values change per theme.
5
+ * The two surfaces are nested rather than stacked, and only one edge of the
6
+ * outer one is ever visible. The panel is flush to the shell's left, right and
7
+ * bottom, so the shell reads as something the card is *sitting in* rather than
8
+ * as a border around it — and the strip left at the top is the header, which
9
+ * is why the header needs no rule under it and no background of its own.
10
+ *
11
+ * The shell's radius is the larger of the two, and the panel's top corners are
12
+ * tighter. That is the reverse of the usual nested-radius rule, and it is
13
+ * deliberate: with only the top corners free, matching them would make the two
14
+ * surfaces read as one misdrawn shape. The panel's bottom corners are not set
15
+ * at all — the shell clips them, so they take its radius exactly.
9
16
  *
10
17
  * ```tsx
11
18
  * <Frame>
12
19
  * <Frame.Header>
13
- * <Frame.Title>Usage Type</Frame.Title>
14
- * <Frame.Action>Amount</Frame.Action>
20
+ * <Frame.Title>Agent monitor</Frame.Title>
21
+ * <Frame.Action>All agents under 25% token limit</Frame.Action>
15
22
  * </Frame.Header>
16
23
  * <Frame.Panel>
17
24
  * <Frame.Row>…</Frame.Row>
18
25
  * <Frame.Row>…</Frame.Row>
19
26
  * </Frame.Panel>
20
- * <Frame.Footer>Updated 2 minutes ago</Frame.Footer>
21
27
  * </Frame>
22
28
  * ```
23
29
  *
@@ -41,15 +47,24 @@ import { cn } from '../../utils/cn';
41
47
  const frameVariants = tv({
42
48
  slots: {
43
49
  root: '',
44
- panel: 'overflow-hidden rounded-xl border border-border bg-card',
50
+ // Flush left, right and bottom: no side or bottom border, because the
51
+ // shell's own edge is already there, and no bottom radius, because the
52
+ // shell clips it. Only the top corners and the rule under the header are
53
+ // the panel's to draw.
54
+ panel: 'overflow-hidden rounded-t-2xl border-t border-border bg-card',
45
55
  },
46
56
  variants: {
47
57
  variant: {
48
- default: { root: 'rounded-2xl border border-border bg-surface p-1.5' },
58
+ // `overflow-hidden` is load-bearing — it is what makes the panel's
59
+ // bottom corners take the shell's radius instead of squaring off
60
+ // against it.
61
+ default: {
62
+ root: 'overflow-hidden rounded-3xl border border-border bg-surface',
63
+ },
49
64
  // No shell: the panel is the whole widget. For a Frame nested inside a
50
65
  // card that already draws a border, where the shell's own edge sitting
51
66
  // just inside it reads as a double line.
52
- plain: { root: '', panel: 'rounded-2xl' },
67
+ plain: { root: '', panel: 'rounded-3xl border border-border' },
53
68
  },
54
69
  },
55
70
  defaultVariants: {
@@ -83,15 +98,16 @@ export interface FrameHeaderProps extends FrameProps {
83
98
  }
84
99
 
85
100
  /**
86
- * The header row: title on the left, `Frame.Action` on the right. Add
87
- * `className="flex-col items-start"` when you want a description underneath.
101
+ * The header row — the strip of shell left exposed above the panel. Title on
102
+ * the left, `Frame.Action` on the right. Add `className="flex-col items-start"`
103
+ * when you want a description underneath.
88
104
  */
89
105
  const FrameHeader = forwardRef<View, FrameHeaderProps>(
90
106
  ({ className, ...props }, ref) => (
91
107
  <View
92
108
  ref={ref}
93
109
  className={cn(
94
- 'flex-row items-center justify-between gap-3 px-3 pb-2.5 pt-2',
110
+ 'flex-row items-center justify-between gap-3 px-4 pb-3 pt-2.5',
95
111
  className
96
112
  )}
97
113
  {...props}
@@ -100,14 +116,12 @@ const FrameHeader = forwardRef<View, FrameHeaderProps>(
100
116
  );
101
117
  FrameHeader.displayName = 'Frame.Header';
102
118
 
119
+ /**
120
+ * Muted by default. The header is a caption on the tray the card sits in, not
121
+ * a heading over a section — the card's own rows carry the weight.
122
+ */
103
123
  const FrameTitle = forwardRef<RNText, TextProps>(({ className, ...props }, ref) => (
104
- <Text
105
- ref={ref}
106
- size="base"
107
- weight="medium"
108
- className={cn('text-foreground', className)}
109
- {...props}
110
- />
124
+ <Text ref={ref} size="sm" muted className={className} {...props} />
111
125
  ));
112
126
  FrameTitle.displayName = 'Frame.Title';
113
127
 
@@ -123,7 +137,7 @@ const FrameAction = forwardRef<View, FrameActionProps>(
123
137
  ({ className, children, ...props }, ref) => (
124
138
  <View ref={ref} className={cn('flex-row items-center gap-2', className)} {...props}>
125
139
  {typeof children === 'string' ? (
126
- <Text size="base" muted>
140
+ <Text size="sm" muted>
127
141
  {children}
128
142
  </Text>
129
143
  ) : (
@@ -177,8 +191,8 @@ export interface FramePanelProps extends FrameProps {
177
191
  }
178
192
 
179
193
  /**
180
- * The inset card holding the frame's content — the raised surface sitting in
181
- * from the shell's edges, with its own smaller radius.
194
+ * The card holding the frame's content — flush to the shell on three sides,
195
+ * with the header strip above it.
182
196
  */
183
197
  const FramePanel = forwardRef<View, FramePanelProps>(
184
198
  ({ className, dividers = true, children, ...props }, ref) => (
@@ -284,30 +298,6 @@ FrameSection.displayName = 'Frame.Section';
284
298
  /** Parts the panel divides. Declared after them, since it holds references. */
285
299
  const DIVIDABLE = new Set<unknown>([FrameRow, FrameSection]);
286
300
 
287
- export interface FrameFooterProps extends FrameProps {
288
- children?: ReactNode;
289
- }
290
-
291
- /** Muted caption under the panel. Strings are wrapped for you. */
292
- const FrameFooter = forwardRef<View, FrameFooterProps>(
293
- ({ className, children, ...props }, ref) => (
294
- <View
295
- ref={ref}
296
- className={cn('flex-row items-center gap-2 px-3 pb-1.5 pt-2.5', className)}
297
- {...props}
298
- >
299
- {typeof children === 'string' ? (
300
- <Text size="sm" muted>
301
- {children}
302
- </Text>
303
- ) : (
304
- children
305
- )}
306
- </View>
307
- )
308
- );
309
- FrameFooter.displayName = 'Frame.Footer';
310
-
311
301
  export const Frame = Object.assign(FrameRoot, {
312
302
  Header: FrameHeader,
313
303
  Title: FrameTitle,
@@ -316,5 +306,4 @@ export const Frame = Object.assign(FrameRoot, {
316
306
  Panel: FramePanel,
317
307
  Section: FrameSection,
318
308
  Row: FrameRow,
319
- Footer: FrameFooter,
320
309
  });