@utilitywarehouse/hearth-react-native 0.35.1 → 0.35.2
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/SKILL.md +40 -3
- package/build/components/Modal/Modal.d.ts +1 -1
- package/build/components/Modal/Modal.js +2 -2
- package/build/components/Modal/Modal.shared.types.d.ts +1 -0
- package/build/components/Modal/Modal.web.d.ts +1 -1
- package/build/components/Modal/Modal.web.js +2 -2
- package/package.json +3 -3
- package/public/llms/components/modal.md +1 -0
- package/public/llms/docs/ai-tooling.md +19 -94
- package/public/llms/docs/introduction.md +1 -1
- package/public/llms.txt +1 -1
package/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hearth-react-native
|
|
3
|
-
description:
|
|
3
|
+
description: MANDATORY prerequisite — you MUST load this skill BEFORE writing or editing any file that imports from `@utilitywarehouse/hearth-react-native` or `@utilitywarehouse/hearth-react-native-icons`, BEFORE adapting Figma `get_design_context` reference output into a React Native screen in a Hearth app, and BEFORE delegating research about a Hearth component's props or usage to a subagent. NEVER substitute node_modules type definitions or a grep of existing usage for this skill's own component docs — that is how this skill gets silently bypassed. This skill IS the "target project's conventions" that figma-design-to-code hands off to; loading that skill does NOT satisfy this requirement. Do not wait for an explicit mention of "Hearth React Native" — any React Native UI work in an app with the library installed requires this skill first.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Building with Hearth React Native
|
|
@@ -9,6 +9,31 @@ You are acting as a React Native engineer. Your job is to turn requirements —
|
|
|
9
9
|
either Figma designs or written requirements — into UI React Native code using
|
|
10
10
|
Hearth — Utility Warehouse's Design Systems libraries.
|
|
11
11
|
|
|
12
|
+
## MANDATORY — when you must load this skill
|
|
13
|
+
|
|
14
|
+
Load this skill — do not skip it — at each of these moments:
|
|
15
|
+
|
|
16
|
+
- **Before writing or editing any file that imports from
|
|
17
|
+
`@utilitywarehouse/hearth-react-native` or
|
|
18
|
+
`@utilitywarehouse/hearth-react-native-icons`.** If the file touches either
|
|
19
|
+
package, this skill MUST be loaded first, no exceptions.
|
|
20
|
+
- **Before adapting Figma `get_design_context` reference output into this
|
|
21
|
+
codebase.** `figma-design-to-code` hands off with "adapt the reference code
|
|
22
|
+
to the target project's conventions" without saying what those conventions
|
|
23
|
+
are. This skill IS that answer. Loading `figma-design-to-code` does NOT
|
|
24
|
+
satisfy this requirement — load both, this one before you touch the
|
|
25
|
+
reference code.
|
|
26
|
+
- **Before delegating research about a Hearth component's props, variants, or
|
|
27
|
+
usage to a subagent.** Reading `node_modules` type definitions or grepping
|
|
28
|
+
existing usage in the app is NOT a substitute for this skill's own component
|
|
29
|
+
docs (see "Before you implement" below). Spawning a research subagent to
|
|
30
|
+
answer a question this skill already answers is how this skill gets
|
|
31
|
+
silently bypassed — load this skill, or pass its instructions into the
|
|
32
|
+
subagent's prompt, before delegating.
|
|
33
|
+
|
|
34
|
+
Treat each moment above as a hard gate, not a vague "this is UI work" prompt
|
|
35
|
+
to get around to eventually.
|
|
36
|
+
|
|
12
37
|
## Package imports
|
|
13
38
|
|
|
14
39
|
| Purpose | Package |
|
|
@@ -106,6 +131,8 @@ pairing with someone, ask for it here too.
|
|
|
106
131
|
|
|
107
132
|
## Use layout components
|
|
108
133
|
|
|
134
|
+
(Source of truth: [`public/llms/docs/layout-components.md`](public/llms/docs/layout-components.md))
|
|
135
|
+
|
|
109
136
|
Use layout components to structure and space content. Do not add margin to
|
|
110
137
|
individual UI elements to achieve spacing — keep spacing decisions at the layout
|
|
111
138
|
level so UI components stay composable.
|
|
@@ -172,6 +199,8 @@ alongside their own layout-specific props (`direction`, `align`, `justify`,
|
|
|
172
199
|
|
|
173
200
|
## Styling: Unistyles, not `StyleSheet` from `react-native`
|
|
174
201
|
|
|
202
|
+
(Source of truth: [`public/llms/docs/styling.md`](public/llms/docs/styling.md))
|
|
203
|
+
|
|
175
204
|
Custom styles use `react-native-unistyles`'s `StyleSheet.create(theme => ({...}))`,
|
|
176
205
|
re-exported from the package. Only reach for this when utility props and
|
|
177
206
|
existing component variants don't cover the need.
|
|
@@ -235,6 +264,8 @@ const styles = StyleSheet.create(theme => ({
|
|
|
235
264
|
|
|
236
265
|
## Responsive breakpoints
|
|
237
266
|
|
|
267
|
+
(Source of truth: [`public/llms/docs/styling.md`](public/llms/docs/styling.md#responsive-design-with-breakpoints))
|
|
268
|
+
|
|
238
269
|
Breakpoints are configured once (via Unistyles' `StyleSheet.configure`) and
|
|
239
270
|
cascade **mobile-first**: a value applies from that breakpoint upward until
|
|
240
271
|
overridden by a larger one. See the
|
|
@@ -264,6 +295,8 @@ const styles = StyleSheet.create(theme => ({
|
|
|
264
295
|
|
|
265
296
|
## Required app-level providers
|
|
266
297
|
|
|
298
|
+
(Source of truth: [`public/llms/docs/getting-started.md`](public/llms/docs/getting-started.md))
|
|
299
|
+
|
|
267
300
|
Hearth React Native components rely on these wrapping the app root:
|
|
268
301
|
|
|
269
302
|
- `GestureHandlerRootView` (from `react-native-gesture-handler`) — required for
|
|
@@ -299,11 +332,13 @@ applies before reaching for `react-native-safe-area-context` directly:
|
|
|
299
332
|
`SafeAreaView` applies the `top` edge. Set `useSafeAreaInsets={false}` on
|
|
300
333
|
`BottomSheetModalProvider` only if the parent screen already handles
|
|
301
334
|
safe-area insets itself — don't look for the prop on the individual
|
|
302
|
-
components.
|
|
335
|
+
components. (Source of truth:
|
|
336
|
+
[`public/llms/components/modal.md`](public/llms/components/modal.md))
|
|
303
337
|
- **`NavModal`** (used when a screen is already presented by React Navigation)
|
|
304
338
|
reads safe-area insets directly from the Unistyles runtime
|
|
305
339
|
(`rt.insets.top`/`rt.insets.bottom` inside `StyleSheet.create((theme, rt) =>
|
|
306
|
-
...)`) rather than rendering a `SafeAreaView`.
|
|
340
|
+
...)`) rather than rendering a `SafeAreaView`. (Source of truth:
|
|
341
|
+
[`public/llms/components/nav-modal.md`](public/llms/components/nav-modal.md))
|
|
307
342
|
|
|
308
343
|
Don't wrap Hearth components in your own `SafeAreaView` on top of these —
|
|
309
344
|
check `BottomSheetModalProvider`'s `useSafeAreaInsets` prop (or `NavModal`'s
|
|
@@ -344,6 +379,8 @@ Compound components share state via a `<Component>Context` (check for a
|
|
|
344
379
|
|
|
345
380
|
### Themed images that adapt to light/dark mode
|
|
346
381
|
|
|
382
|
+
(Source of truth: [`public/llms/docs/dark-mode-best-practice.md`](public/llms/docs/dark-mode-best-practice.md))
|
|
383
|
+
|
|
347
384
|
```tsx
|
|
348
385
|
<ThemedImage light={require('./logo-light.png')} dark={require('./logo-dark.png')} />
|
|
349
386
|
```
|
|
@@ -3,5 +3,5 @@ import ModalProps from './Modal.props';
|
|
|
3
3
|
type Modal<T = any> = BottomSheetModalMethods<T> & {
|
|
4
4
|
triggerCloseAnimation?: () => void;
|
|
5
5
|
};
|
|
6
|
-
declare const Modal: ({ ref, children, heading, description, showCloseButton, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress, closeOnSecondaryButtonPress, loading, loadingHeading, loadingDescription, fullscreen, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, stickyFooter, ...props }: ModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
declare const Modal: ({ ref, children, heading, description, showCloseButton, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress, closeOnSecondaryButtonPress, loading, loadingHeading, loadingDescription, fullscreen, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, stickyFooter, testID, ...props }: ModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
export default Modal;
|
|
@@ -12,7 +12,7 @@ import { Button } from '../Button';
|
|
|
12
12
|
import { Heading } from '../Heading';
|
|
13
13
|
import { Spinner } from '../Spinner';
|
|
14
14
|
import { UnstyledIconButton } from '../UnstyledIconButton';
|
|
15
|
-
const Modal = ({ ref, children, heading, description, showCloseButton = true, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress = true, closeOnSecondaryButtonPress = true, loading, loadingHeading = 'Loading...', loadingDescription, fullscreen = false, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, stickyFooter = true, ...props }) => {
|
|
15
|
+
const Modal = ({ ref, children, heading, description, showCloseButton = true, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress = true, closeOnSecondaryButtonPress = true, loading, loadingHeading = 'Loading...', loadingDescription, fullscreen = false, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, stickyFooter = true, testID, ...props }) => {
|
|
16
16
|
const theme = useTheme();
|
|
17
17
|
const bottomSheetModalRef = useRef(null);
|
|
18
18
|
const viewRef = useRef(null);
|
|
@@ -90,7 +90,7 @@ const Modal = ({ ref, children, heading, description, showCloseButton = true, pr
|
|
|
90
90
|
secondaryButtonProps,
|
|
91
91
|
secondaryButtonText,
|
|
92
92
|
]);
|
|
93
|
-
const content = (_jsx(_Fragment, { children: loading ? (_jsxs(View, { style: styles.loadingContainer, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? (loadingHeading ?? 'Loading') : undefined, accessibilityHint: Platform.OS === 'android' && loadingDescription ? loadingDescription : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsx(Spinner, { size: "lg" }), _jsx(Heading, { size: "lg", textAlign: "center", children: loadingHeading }), loadingDescription ? _jsx(BodyText, { textAlign: "center", children: loadingDescription }) : null] })) : (_jsxs(View, { style: styles.container, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Modal content' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsxs(View, { style: styles.header, children: [_jsxs(View, { style: styles.headerTextContent, children: [heading && !image ? (_jsx(Heading, { size: "lg", accessible: true, children: heading })) : null, hasDescription && !image ? (descriptionIsText ? (_jsx(BodyText, { accessible: true, children: description })) : (description)) : null] }), showCloseButton ? (_jsx(UnstyledIconButton, { icon: CloseMediumIcon, onPress: handleCloseButtonPress, accessibilityLabel: "Close modal", ...closeButtonProps })) : null] }), image ? (_jsxs(View, { style: styles.imageContainer, children: [image, _jsxs(View, { style: styles.textContent, children: [heading ? (_jsx(Heading, { size: "lg", textAlign: "center", accessible: true, children: heading })) : null, hasDescription ? (descriptionIsText ? (_jsx(BodyText, { textAlign: "center", accessible: true, children: description })) : (description)) : null] })] })) : null, children, !stickyFooter && shouldShowFooter ? (_jsx(View, { style: footerStyle, children: footerContent })) : null] })) }));
|
|
93
|
+
const content = (_jsx(_Fragment, { children: loading ? (_jsxs(View, { style: styles.loadingContainer, testID: testID, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? (loadingHeading ?? 'Loading') : undefined, accessibilityHint: Platform.OS === 'android' && loadingDescription ? loadingDescription : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsx(Spinner, { size: "lg" }), _jsx(Heading, { size: "lg", textAlign: "center", children: loadingHeading }), loadingDescription ? _jsx(BodyText, { textAlign: "center", children: loadingDescription }) : null] })) : (_jsxs(View, { style: styles.container, testID: testID, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Modal content' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsxs(View, { style: styles.header, children: [_jsxs(View, { style: styles.headerTextContent, children: [heading && !image ? (_jsx(Heading, { size: "lg", accessible: true, children: heading })) : null, hasDescription && !image ? (descriptionIsText ? (_jsx(BodyText, { accessible: true, children: description })) : (description)) : null] }), showCloseButton ? (_jsx(UnstyledIconButton, { icon: CloseMediumIcon, onPress: handleCloseButtonPress, accessibilityLabel: "Close modal", ...closeButtonProps })) : null] }), image ? (_jsxs(View, { style: styles.imageContainer, children: [image, _jsxs(View, { style: styles.textContent, children: [heading ? (_jsx(Heading, { size: "lg", textAlign: "center", accessible: true, children: heading })) : null, hasDescription ? (descriptionIsText ? (_jsx(BodyText, { textAlign: "center", accessible: true, children: description })) : (description)) : null] })] })) : null, children, !stickyFooter && shouldShowFooter ? (_jsx(View, { style: footerStyle, children: footerContent })) : null] })) }));
|
|
94
94
|
const renderFooter = useCallback((bottomSheetFooterProps) => (_jsx(BottomSheetFooter, { ...bottomSheetFooterProps, children: _jsx(View, { onLayout: handleStickyFooterLayout, style: [styles.footerWrap, footerStyle], children: footerContent }) })), [footerContent, footerStyle, handleStickyFooterLayout]);
|
|
95
95
|
return (_jsxs(_Fragment, { children: [stickyFooter && shouldShowFooter && stickyFooterHeight === 0 ? (_jsx(View, { accessible: false, importantForAccessibility: "no-hide-descendants", pointerEvents: "none", style: styles.footerMeasurementContainer, children: _jsx(View, { onLayout: handleStickyFooterLayout, style: [styles.footerWrap, footerStyle], children: footerContent }) })) : null, _jsxs(BottomSheetModal, { ref: bottomSheetModalRef, enableDynamicSizing: true, snapPoints: image || fullscreen ? ['90%'] : props.snapPoints, showHandle: typeof loading !== 'undefined' && loading ? false : props.showHandle, accessible: false, style: styles.modal, footerComponent: stickyFooter && shouldShowFooter ? renderFooter : undefined, ...props, onChange: handleChange, children: [loading ? _jsx(View, { style: styles.loadingTop }) : null, _jsx(BottomSheetScrollView, { contentContainerStyle: [
|
|
96
96
|
styles.scrollView,
|
|
@@ -3,6 +3,7 @@ import { StyleProp, ViewProps, ViewStyle } from 'react-native';
|
|
|
3
3
|
import { ButtonWithoutChildrenProps } from '../Button/Button.props';
|
|
4
4
|
import { UnstyledIconButtonProps } from '../UnstyledIconButton';
|
|
5
5
|
export interface ModalCommonBaseProps {
|
|
6
|
+
testID?: string;
|
|
6
7
|
loading?: boolean;
|
|
7
8
|
image?: ReactNode;
|
|
8
9
|
showCloseButton?: boolean;
|
|
@@ -3,5 +3,5 @@ import ModalProps from './Modal.props';
|
|
|
3
3
|
type Modal<T = any> = BottomSheetModalMethods<T> & {
|
|
4
4
|
triggerCloseAnimation?: () => void;
|
|
5
5
|
};
|
|
6
|
-
declare const Modal: ({ ref, children, heading, description, showCloseButton, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress, closeOnSecondaryButtonPress, loading, loadingHeading, fullscreen, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, ...props }: ModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
declare const Modal: ({ ref, children, heading, description, showCloseButton, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress, closeOnSecondaryButtonPress, loading, loadingHeading, fullscreen, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, testID, ...props }: ModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
export default Modal;
|
|
@@ -9,7 +9,7 @@ import { Button } from '../Button';
|
|
|
9
9
|
import { Heading } from '../Heading';
|
|
10
10
|
import { Spinner } from '../Spinner';
|
|
11
11
|
import { UnstyledIconButton } from '../UnstyledIconButton';
|
|
12
|
-
const Modal = ({ ref, children, heading, description, showCloseButton = true, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress = true, closeOnSecondaryButtonPress = true, loading, loadingHeading = 'Loading...', fullscreen = false, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, ...props }) => {
|
|
12
|
+
const Modal = ({ ref, children, heading, description, showCloseButton = true, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress = true, closeOnSecondaryButtonPress = true, loading, loadingHeading = 'Loading...', fullscreen = false, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, testID, ...props }) => {
|
|
13
13
|
const bottomSheetModalRef = useRef(null);
|
|
14
14
|
const viewRef = useRef(null);
|
|
15
15
|
const scrollViewRef = useRef(null);
|
|
@@ -62,7 +62,7 @@ const Modal = ({ ref, children, heading, description, showCloseButton = true, pr
|
|
|
62
62
|
const descriptionIsText = typeof description === 'string' || typeof description === 'number';
|
|
63
63
|
const hasDescription = description !== undefined && description !== null;
|
|
64
64
|
const footerContent = footer ?? (_jsxs(View, { style: styles.footer, children: [hasPrimaryButton ? (_jsx(Button, { onPress: handlePrimaryButtonPress, text: primaryButtonText, ...primaryButtonProps, variant: primaryButtonProps?.variant ?? 'solid', colorScheme: primaryButtonProps?.colorScheme ?? 'highlight' })) : null, hasSecondaryButton ? (_jsx(Button, { onPress: handleSecondaryButtonPress, text: secondaryButtonText, ...secondaryButtonProps, variant: secondaryButtonProps?.variant ?? 'outline', colorScheme: secondaryButtonProps?.colorScheme ?? 'functional' })) : null] }));
|
|
65
|
-
const content = (_jsx(_Fragment, { children: loading ? (_jsxs(View, { style: styles.loadingContainer, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Loading' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsx(Spinner, { size: "lg" }), _jsx(Heading, { size: "lg", textAlign: "center", children: loadingHeading })] })) : (_jsxs(View, { style: styles.container, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Modal content' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsxs(View, { style: styles.header, children: [_jsxs(View, { style: styles.headerTextContent, children: [heading && !image ? (_jsx(Heading, { size: "lg", accessible: true, children: heading })) : null, hasDescription && !image ? (descriptionIsText ? (_jsx(BodyText, { accessible: true, children: description })) : (description)) : null] }), showCloseButton ? (_jsx(UnstyledIconButton, { icon: CloseMediumIcon, onPress: handleCloseButtonPress, accessibilityLabel: "Close modal", ...closeButtonProps })) : null] }), image ? (_jsxs(View, { style: styles.imageContainer, children: [image, _jsxs(View, { style: styles.textContent, children: [heading ? (_jsx(Heading, { size: "lg", textAlign: "center", accessible: true, children: heading })) : null, hasDescription ? (descriptionIsText ? (_jsx(BodyText, { textAlign: "center", accessible: true, children: description })) : (description)) : null] })] })) : null, children, hasFooter ? _jsx(View, { style: footerStyle, children: footerContent }) : null] })) }));
|
|
65
|
+
const content = (_jsx(_Fragment, { children: loading ? (_jsxs(View, { style: styles.loadingContainer, testID: testID, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Loading' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsx(Spinner, { size: "lg" }), _jsx(Heading, { size: "lg", textAlign: "center", children: loadingHeading })] })) : (_jsxs(View, { style: styles.container, testID: testID, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Modal content' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsxs(View, { style: styles.header, children: [_jsxs(View, { style: styles.headerTextContent, children: [heading && !image ? (_jsx(Heading, { size: "lg", accessible: true, children: heading })) : null, hasDescription && !image ? (descriptionIsText ? (_jsx(BodyText, { accessible: true, children: description })) : (description)) : null] }), showCloseButton ? (_jsx(UnstyledIconButton, { icon: CloseMediumIcon, onPress: handleCloseButtonPress, accessibilityLabel: "Close modal", ...closeButtonProps })) : null] }), image ? (_jsxs(View, { style: styles.imageContainer, children: [image, _jsxs(View, { style: styles.textContent, children: [heading ? (_jsx(Heading, { size: "lg", textAlign: "center", accessible: true, children: heading })) : null, hasDescription ? (descriptionIsText ? (_jsx(BodyText, { textAlign: "center", accessible: true, children: description })) : (description)) : null] })] })) : null, children, hasFooter ? _jsx(View, { style: footerStyle, children: footerContent }) : null] })) }));
|
|
66
66
|
return (_jsx(BottomSheetModal, { ref: bottomSheetModalRef, enableDynamicSizing: true, snapPoints: image || fullscreen ? ['90%'] : props.snapPoints, showHandle: typeof loading !== 'undefined' && loading ? false : props.showHandle, accessible: false, ...props, onChange: handleChange, children: _jsx(BottomSheetScrollView, { contentContainerStyle: styles.container, ref: scrollViewRef, children: content }) }));
|
|
67
67
|
};
|
|
68
68
|
const styles = StyleSheet.create(theme => ({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utilitywarehouse/hearth-react-native",
|
|
3
|
-
"version": "0.35.
|
|
3
|
+
"version": "0.35.2",
|
|
4
4
|
"description": "Utility Warehouse React Native UI library",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
"vitest": "^4.1.7",
|
|
79
79
|
"@utilitywarehouse/hearth-storybook-utils": "0.0.0",
|
|
80
80
|
"@utilitywarehouse/hearth-fonts": "^0.1.1",
|
|
81
|
-
"@utilitywarehouse/hearth-react-icons": "^0.
|
|
82
|
-
"@utilitywarehouse/hearth-react-native-icons": "^0.
|
|
81
|
+
"@utilitywarehouse/hearth-react-icons": "^0.9.0",
|
|
82
|
+
"@utilitywarehouse/hearth-react-native-icons": "^0.9.0",
|
|
83
83
|
"@utilitywarehouse/hearth-svg-assets": "^0.6.5",
|
|
84
84
|
"@utilitywarehouse/hearth-tokens": "^0.4.3"
|
|
85
85
|
},
|
|
@@ -106,6 +106,7 @@ The Modal component extends the `BottomSheetModal` component and accepts all of
|
|
|
106
106
|
| `footerStyle` | `StyleProp<ViewStyle>` | Styles applied to the footer container, useful for sticky footer shadows or custom spacing | - |
|
|
107
107
|
| `closeButtonProps` | `Omit<UnstyledIconButtonProps, 'children'>` | Additional props to pass to the close button | - |
|
|
108
108
|
| `fullscreen` | `boolean` | Whether the modal should take up the full screen height | `false` |
|
|
109
|
+
| `testID` | `string` | Applied to the modal content container, useful for locating the modal in e2e tests | - |
|
|
109
110
|
|
|
110
111
|
When `footer` is provided, the primary and secondary button props are not available. Build your footer actions directly inside the custom footer content instead.
|
|
111
112
|
|
|
@@ -1,110 +1,35 @@
|
|
|
1
1
|
# AI Tooling
|
|
2
2
|
|
|
3
|
-
- [
|
|
3
|
+
- [Plugin](#plugin)
|
|
4
4
|
- [llms.txt and markdown docs](#llms-txt-and-markdown-docs)
|
|
5
5
|
- [Agent Skill](#agent-skill)
|
|
6
6
|
- [Hearth React Native MCP Server](#hearth-react-native-mcp-server)
|
|
7
7
|
|
|
8
|
-
The Hearth React Native AI Tools are intended to help your AI agents use Hearth
|
|
9
|
-
components to build Utility Warehouse UIs. They provide access to
|
|
10
|
-
component documentation and usage guidelines, so your agent has the
|
|
11
|
-
needs to build with Hearth correctly.
|
|
8
|
+
The Hearth React Native AI Tools are intended to help your AI agents use Hearth
|
|
9
|
+
React Native components to build Utility Warehouse UIs. They provide access to
|
|
10
|
+
APIs, component documentation and usage guidelines, so your agent has the
|
|
11
|
+
context it needs to build with Hearth correctly.
|
|
12
12
|
|
|
13
|
-
If you're building UI from a Figma
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
If you're building UI from a design in Figma, start with Figma's MCP Server.
|
|
14
|
+
This should be the first step, since it gives your agent direct access to the
|
|
15
|
+
design you're implementing. You can read more information about setting this up
|
|
16
|
+
in the root AI Toolkit documentation.
|
|
17
|
+
|
|
18
|
+
Hearth's own AI tooling then supplements it for most use cases, filling in the
|
|
19
|
+
parts of the Hearth React Native API and usage guidance that the design alone
|
|
20
|
+
doesn't capture:
|
|
19
21
|
|
|
20
22
|
- [llms.txt](https://hearth.prod.uw.systems/react-native/llms.txt) and markdown docs
|
|
21
23
|
- [SKILL.md](https://github.com/utilitywarehouse/hearth/blob/main/packages/react-native/SKILL.md)
|
|
22
24
|
- [Hearth MCP Server](https://main--68e3ad5c6e80b57678cad6c6.chromatic.com/mcp)
|
|
23
25
|
|
|
24
|
-
##
|
|
25
|
-
|
|
26
|
-
If you're implementing a Figma design, this should be the first tool you reach
|
|
27
|
-
for. Figma's [Dev Mode MCP Server](https://developers.figma.com/docs/figma-mcp-server/)
|
|
28
|
-
gives your agent direct access to the design file itself, layout, variables,
|
|
29
|
-
and generated code for a selected frame or component. This is a Figma-provided
|
|
30
|
-
tool, separate from the Hearth React Native MCP Server below.
|
|
31
|
-
|
|
32
|
-
### If you're using Claude
|
|
33
|
-
|
|
34
|
-
Utility Warehouse has the [Figma connector](https://claude.com/connectors/figma)
|
|
35
|
-
enabled at an organisation level. It already includes the Figma MCP Server's
|
|
36
|
-
tools, so if you have it connected there's nothing else to set up — just
|
|
37
|
-
prompt Claude with a link to the Figma design you want to build, and it will
|
|
38
|
-
pull in the design context itself.
|
|
39
|
-
|
|
40
|
-
To check it's connected for you:
|
|
41
|
-
|
|
42
|
-
1. In Claude, go to **Customize > Connectors**.
|
|
43
|
-
2. Find **Figma** and click **Connect**, authenticating with your Figma account if prompted.
|
|
44
|
-
3. In a conversation, open the **+** menu (or type `/`) in the chat box, then **Connectors**, and toggle **Figma** on. Once it's connected, Claude can also bring it into a conversation on its own when a Figma link is relevant, so you don't have to toggle it every time.
|
|
45
|
-
|
|
46
|
-
If you're not using Claude, or don't have the connector available, set up the
|
|
47
|
-
Figma MCP Server manually instead.
|
|
48
|
-
|
|
49
|
-
### Manual setup
|
|
50
|
-
|
|
51
|
-
Figma recommends the **remote server** for most people, as it supports the
|
|
52
|
-
broadest set of features. Add an entry to your `mcp.json` file:
|
|
53
|
-
|
|
54
|
-
```json
|
|
55
|
-
{
|
|
56
|
-
"servers": {
|
|
57
|
-
"figma": {
|
|
58
|
-
"url": "https://mcp.figma.com/mcp",
|
|
59
|
-
"type": "http"
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
See Figma's [remote server installation guide](https://developers.figma.com/docs/figma-mcp-server/remote-server-installation/)
|
|
66
|
-
for the full setup flow, including the OAuth step required to grant the server
|
|
67
|
-
access to your Figma account.
|
|
68
|
-
|
|
69
|
-
Alternatively, a **local server** runs from the Figma desktop app itself (Dev
|
|
70
|
-
Mode → enable desktop MCP server), which some organisations use instead of the
|
|
71
|
-
remote option:
|
|
72
|
-
|
|
73
|
-
```json
|
|
74
|
-
{
|
|
75
|
-
"servers": {
|
|
76
|
-
"figma-desktop": {
|
|
77
|
-
"url": "http://127.0.0.1:3845/mcp",
|
|
78
|
-
"type": "http"
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
See Figma's [local server installation guide](https://developers.figma.com/docs/figma-mcp-server/local-server-installation/)
|
|
85
|
-
for prerequisites and setup.
|
|
86
|
-
|
|
87
|
-
### Works with Hearth components
|
|
88
|
-
|
|
89
|
-
Hearth publishes [Figma Code Connect](https://help.figma.com/hc/en-us/articles/23920389749655-Code-Connect)
|
|
90
|
-
mappings for its React Native components. In practice, this means: when the
|
|
91
|
-
Figma frame you're implementing uses a component we've mapped, the Figma MCP
|
|
92
|
-
Server returns real Hearth code, for example `<Button variant="emphasis">`,
|
|
93
|
-
rather than generic markup reconstructed from layer names and styles. You
|
|
94
|
-
don't need to know how these mappings work under the hood, just that a mapped
|
|
95
|
-
component gets you code that's ready to use, with little to no rework needed.
|
|
96
|
-
|
|
97
|
-
### Supplement it with Hearth's AI tooling
|
|
98
|
-
|
|
99
|
-
The Figma MCP Server tells your agent what design it's looking at, and, via
|
|
100
|
-
Code Connect, which Hearth component to use. It doesn't know the rest of the
|
|
101
|
-
Hearth React Native API: prop combinations a mapping doesn't cover,
|
|
102
|
-
accessibility guidance, or usage conventions.
|
|
26
|
+
## Plugin
|
|
103
27
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
28
|
+
The Hearth AI Toolkit plugin is the recommended way to set up your agent with
|
|
29
|
+
the Hearth React Native AI Tools. It will automatically add the skill and MCP
|
|
30
|
+
server to your agent configuration, and ensure that your agent has access to
|
|
31
|
+
the latest Hearth React Native documentation and usage guidance. How to install
|
|
32
|
+
and use the plugin is documented in the root AI Toolkit documentation.
|
|
108
33
|
|
|
109
34
|
## llms.txt and markdown docs
|
|
110
35
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Hearth React Native
|
|
2
2
|
|
|
3
3
|
React Native component library for building UIs at Utility Warehouse.<br/>
|
|
4
|
-
Current version: v0.35.
|
|
4
|
+
Current version: v0.35.2
|
|
5
5
|
|
|
6
6
|
Hearth React Native is a comprehensive design system library for React Native, providing reusable components, consistent styling, and tools to streamline UI development. It is built to enhance productivity and maintain design consistency across projects.
|
|
7
7
|
|