lucy-cli 2.0.0-alpha.2 → 2.0.0-alpha.3

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 (51) hide show
  1. package/dist/init.js +29 -11
  2. package/files/expo/.prettierrc.js +16 -0
  3. package/files/expo/.yarnrc.yml +1 -1
  4. package/files/expo/app/(tabs)/_layout.tsx +38 -33
  5. package/files/expo/app/(tabs)/explore.tsx +114 -0
  6. package/files/expo/app/(tabs)/index.tsx +80 -0
  7. package/files/expo/app/+not-found.tsx +37 -0
  8. package/files/expo/app/_layout.tsx +39 -30
  9. package/files/expo/assets/fonts/SpaceMono-Regular.ttf +0 -0
  10. package/files/expo/assets/images/adaptive-icon.png +0 -0
  11. package/files/expo/assets/images/favicon.png +0 -0
  12. package/files/expo/assets/images/icon.png +0 -0
  13. package/files/expo/assets/images/partial-react-logo.png +0 -0
  14. package/files/expo/assets/images/react-logo.png +0 -0
  15. package/files/expo/assets/images/react-logo@2x.png +0 -0
  16. package/files/expo/assets/images/react-logo@3x.png +0 -0
  17. package/files/expo/assets/images/splash-icon.png +0 -0
  18. package/files/expo/babel.config.js +7 -6
  19. package/files/expo/components/Collapsible.tsx +52 -0
  20. package/files/expo/components/ExternalLink.tsx +32 -0
  21. package/files/expo/components/HapticTab.tsx +24 -0
  22. package/files/expo/components/HelloWave.tsx +35 -0
  23. package/files/expo/components/ParallaxScrollView.tsx +85 -0
  24. package/files/expo/components/ThemedText.tsx +70 -0
  25. package/files/expo/components/ThemedView.tsx +23 -0
  26. package/files/expo/components/ui/IconSymbol.ios.tsx +32 -0
  27. package/files/expo/components/ui/IconSymbol.tsx +41 -0
  28. package/files/expo/components/ui/TabBarBackground.ios.tsx +19 -0
  29. package/files/expo/components/ui/TabBarBackground.tsx +6 -0
  30. package/files/expo/constants/Colors.ts +17 -17
  31. package/files/expo/constants/theme.ts +17 -17
  32. package/files/expo/{eslint.config.js → eslint.config.mjs} +15 -19
  33. package/files/expo/hooks/useColorScheme.ts +13 -7
  34. package/files/expo/hooks/useColorScheme.web.ts +12 -9
  35. package/files/expo/hooks/useThemeColor.ts +19 -10
  36. package/files/expo/lib/data.ts +36 -33
  37. package/files/expo/lib/utils/index.ts +7 -2
  38. package/files/expo/lib/utils/polyfills.ts +1 -1
  39. package/files/expo/lib/wix/client.ts +3 -5
  40. package/files/expo/lib/wix/error.ts +3 -0
  41. package/files/expo/lib/wix/index.ts +1 -0
  42. package/files/expo/metro.config.js +31 -0
  43. package/files/expo/patches/@wix-sdk-npm-1.15.24-1adbec98e9.patch +20 -0
  44. package/files/expo/scripts/reset-project.ts +116 -0
  45. package/files/expo/tailwind.config.js +61 -196
  46. package/files/expo/tsconfig.json +31 -26
  47. package/package.json +1 -1
  48. package/src/init.ts +41 -16
  49. package/files/expo/.prettierrc.json +0 -16
  50. /package/files/expo/{readme.md → README.md} +0 -0
  51. /package/files/expo/{types/nativewind-env.d.ts → nativewind-env.d.ts} +0 -0
@@ -0,0 +1,35 @@
1
+ import { useEffect } from 'react';
2
+ import { StyleSheet } from 'react-native';
3
+ import Animated, { useAnimatedStyle, useSharedValue, withRepeat, withSequence, withTiming } from 'react-native-reanimated';
4
+
5
+ import { ThemedText } from '@/components/ThemedText';
6
+
7
+ /** Renders an animated waving hand emoji. */
8
+ export function HelloWave() {
9
+ const rotationAnimation = useSharedValue(0);
10
+
11
+ useEffect(() => {
12
+ rotationAnimation.value = withRepeat(
13
+ withSequence(withTiming(25, { duration: 150 }), withTiming(0, { duration: 150 })),
14
+ 4 // Run the animation 4 times
15
+ );
16
+ }, [rotationAnimation]);
17
+
18
+ const animatedStyle = useAnimatedStyle(() => ({
19
+ transform: [{ rotate: `${rotationAnimation.value}deg` }],
20
+ }));
21
+
22
+ return (
23
+ <Animated.View style={animatedStyle}>
24
+ <ThemedText style={styles.text}>👋</ThemedText>
25
+ </Animated.View>
26
+ );
27
+ }
28
+
29
+ const styles = StyleSheet.create({
30
+ text: {
31
+ fontSize: 28,
32
+ lineHeight: 32,
33
+ marginTop: -6,
34
+ },
35
+ });
@@ -0,0 +1,85 @@
1
+ import type { PropsWithChildren, ReactElement } from 'react';
2
+ import { StyleSheet } from 'react-native';
3
+ import Animated, { interpolate, useAnimatedRef, useAnimatedStyle, useScrollViewOffset } from 'react-native-reanimated';
4
+
5
+ import { ThemedView } from '@/components/ThemedView';
6
+ import { useBottomTabOverflow } from '@/components/ui/TabBarBackground';
7
+ import { useColorScheme } from '@/hooks/useColorScheme';
8
+
9
+ const HEADER_HEIGHT = 250;
10
+
11
+ type Props = PropsWithChildren<{
12
+ headerImage: ReactElement;
13
+ headerBackgroundColor: { dark: string; light: string };
14
+ }>;
15
+
16
+ /**
17
+ * Renders a parallax scroll view with a header image and background color.
18
+ * @param root0 - The props for the ParallaxScrollView component.
19
+ * @param root0.children - The content to display within the scroll view.
20
+ * @param root0.headerImage - The image to display in the header, which will have a parallax effect.
21
+ * @param root0.headerBackgroundColor - The background color for the header, which changes based on the color scheme.
22
+ * @returns The ParallaxScrollView component.
23
+ */
24
+ export default function ParallaxScrollView({
25
+ children,
26
+ headerImage,
27
+ headerBackgroundColor,
28
+ }: Props) {
29
+ const colorScheme = useColorScheme() ?? 'light';
30
+ const scrollRef = useAnimatedRef<Animated.ScrollView>();
31
+ const scrollOffset = useScrollViewOffset(scrollRef);
32
+ const bottom = useBottomTabOverflow();
33
+ const headerAnimatedStyle = useAnimatedStyle(() => {
34
+ return {
35
+ transform: [
36
+ {
37
+ translateY: interpolate(
38
+ scrollOffset.value,
39
+ [-HEADER_HEIGHT, 0, HEADER_HEIGHT],
40
+ [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75]
41
+ ),
42
+ },
43
+ {
44
+ scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]),
45
+ },
46
+ ],
47
+ };
48
+ });
49
+
50
+ return (
51
+ <ThemedView style={styles.container}>
52
+ <Animated.ScrollView
53
+ ref={scrollRef}
54
+ scrollEventThrottle={16}
55
+ scrollIndicatorInsets={{ bottom }}
56
+ contentContainerStyle={{ paddingBottom: bottom }}>
57
+ <Animated.View
58
+ style={[
59
+ styles.header,
60
+ { backgroundColor: headerBackgroundColor[colorScheme] },
61
+ headerAnimatedStyle,
62
+ ]}>
63
+ {headerImage}
64
+ </Animated.View>
65
+ <ThemedView style={styles.content}>{children}</ThemedView>
66
+ </Animated.ScrollView>
67
+ </ThemedView>
68
+ );
69
+ }
70
+
71
+ const styles = StyleSheet.create({
72
+ container: {
73
+ flex: 1,
74
+ },
75
+ header: {
76
+ height: HEADER_HEIGHT,
77
+ overflow: 'hidden',
78
+ },
79
+ content: {
80
+ flex: 1,
81
+ padding: 32,
82
+ gap: 16,
83
+ overflow: 'hidden',
84
+ },
85
+ });
@@ -0,0 +1,70 @@
1
+ import { StyleSheet, Text, type TextProps } from 'react-native';
2
+
3
+ import { useThemeColor } from '@/hooks/useThemeColor';
4
+
5
+ export type ThemedTextProps = TextProps & {
6
+ lightColor?: string;
7
+ darkColor?: string;
8
+ type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
9
+ };
10
+
11
+ /**
12
+ * Renders themed text with different styles based on the type and color scheme.
13
+ * @param root0 - The props for the themed text component.
14
+ * @param root0.style - Additional styles to apply to the text.
15
+ * @param root0.lightColor - The color to use in light mode.
16
+ * @param root0.darkColor - The color to use in dark mode.
17
+ * @param root0.type - The type of text to render, which determines the style.
18
+ * @param root0.rest - Additional props for the text component.
19
+ * @returns The themed text component.
20
+ */
21
+ export function ThemedText({
22
+ style,
23
+ lightColor,
24
+ darkColor,
25
+ type = 'default',
26
+ ...rest
27
+ }: ThemedTextProps) {
28
+ const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');
29
+
30
+ return (
31
+ <Text
32
+ style={[
33
+ { color },
34
+ type === 'default' ? styles.default : undefined,
35
+ type === 'title' ? styles.title : undefined,
36
+ type === 'defaultSemiBold' ? styles.defaultSemiBold : undefined,
37
+ type === 'subtitle' ? styles.subtitle : undefined,
38
+ type === 'link' ? styles.link : undefined,
39
+ style,
40
+ ]}
41
+ {...rest}
42
+ />
43
+ );
44
+ }
45
+
46
+ const styles = StyleSheet.create({
47
+ default: {
48
+ fontSize: 16,
49
+ lineHeight: 24,
50
+ },
51
+ defaultSemiBold: {
52
+ fontSize: 16,
53
+ lineHeight: 24,
54
+ fontWeight: '600',
55
+ },
56
+ title: {
57
+ fontSize: 32,
58
+ fontWeight: 'bold',
59
+ lineHeight: 32,
60
+ },
61
+ subtitle: {
62
+ fontSize: 20,
63
+ fontWeight: 'bold',
64
+ },
65
+ link: {
66
+ lineHeight: 30,
67
+ fontSize: 16,
68
+ color: '#0a7ea4',
69
+ },
70
+ });
@@ -0,0 +1,23 @@
1
+ import { View, type ViewProps } from 'react-native';
2
+
3
+ import { useThemeColor } from '@/hooks/useThemeColor';
4
+
5
+ export type ThemedViewProps = ViewProps & {
6
+ lightColor?: string;
7
+ darkColor?: string;
8
+ };
9
+
10
+ /**
11
+ * Renders a themed view with a background color based on the color scheme.
12
+ * @param root0 - The props for the themed view component.
13
+ * @param root0.style - Additional styles to apply to the view.
14
+ * @param root0.lightColor - The color to use in light mode.
15
+ * @param root0.darkColor - The color to use in dark mode.
16
+ * @param root0.rest - Additional props for the view component.
17
+ * @returns The themed view component.
18
+ */
19
+ export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) {
20
+ const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
21
+
22
+ return <View style={[{ backgroundColor }, style]} {...otherProps} />;
23
+ }
@@ -0,0 +1,32 @@
1
+ import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols';
2
+ import { StyleProp, ViewStyle } from 'react-native';
3
+
4
+ export function IconSymbol({
5
+ name,
6
+ size = 24,
7
+ color,
8
+ style,
9
+ weight = 'regular',
10
+ }: {
11
+ name: SymbolViewProps['name'];
12
+ size?: number;
13
+ color: string;
14
+ style?: StyleProp<ViewStyle>;
15
+ weight?: SymbolWeight;
16
+ }) {
17
+ return (
18
+ <SymbolView
19
+ weight={weight}
20
+ tintColor={color}
21
+ resizeMode="scaleAspectFit"
22
+ name={name}
23
+ style={[
24
+ {
25
+ width: size,
26
+ height: size,
27
+ },
28
+ style,
29
+ ]}
30
+ />
31
+ );
32
+ }
@@ -0,0 +1,41 @@
1
+ // Fallback for using MaterialIcons on Android and web.
2
+
3
+ import MaterialIcons from '@expo/vector-icons/MaterialIcons';
4
+ import { SymbolWeight, SymbolViewProps } from 'expo-symbols';
5
+ import { ComponentProps } from 'react';
6
+ import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native';
7
+
8
+ type IconMapping = Record<SymbolViewProps['name'], ComponentProps<typeof MaterialIcons>['name']>;
9
+ type IconSymbolName = keyof typeof MAPPING;
10
+
11
+ /**
12
+ * Add your SF Symbols to Material Icons mappings here.
13
+ * - see Material Icons in the [Icons Directory](https://icons.expo.fyi).
14
+ * - see SF Symbols in the [SF Symbols](https://developer.apple.com/sf-symbols/) app.
15
+ */
16
+ const MAPPING = {
17
+ 'house.fill': 'home',
18
+ 'paperplane.fill': 'send',
19
+ 'chevron.left.forwardslash.chevron.right': 'code',
20
+ 'chevron.right': 'chevron-right',
21
+ } as IconMapping;
22
+
23
+ /**
24
+ * An icon component that uses native SF Symbols on iOS, and Material Icons on Android and web.
25
+ * This ensures a consistent look across platforms, and optimal resource usage.
26
+ * Icon `name`s are based on SF Symbols and require manual mapping to Material Icons.
27
+ */
28
+ export function IconSymbol({
29
+ name,
30
+ size = 24,
31
+ color,
32
+ style,
33
+ }: {
34
+ name: IconSymbolName;
35
+ size?: number;
36
+ color: string | OpaqueColorValue;
37
+ style?: StyleProp<TextStyle>;
38
+ weight?: SymbolWeight;
39
+ }) {
40
+ return <MaterialIcons color={color} size={size} name={MAPPING[name]} style={style} />;
41
+ }
@@ -0,0 +1,19 @@
1
+ import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
2
+ import { BlurView } from 'expo-blur';
3
+ import { StyleSheet } from 'react-native';
4
+
5
+ export default function BlurTabBarBackground() {
6
+ return (
7
+ <BlurView
8
+ // System chrome material automatically adapts to the system's theme
9
+ // and matches the native tab bar appearance on iOS.
10
+ tint="systemChromeMaterial"
11
+ intensity={100}
12
+ style={StyleSheet.absoluteFill}
13
+ />
14
+ );
15
+ }
16
+
17
+ export function useBottomTabOverflow() {
18
+ return useBottomTabBarHeight();
19
+ }
@@ -0,0 +1,6 @@
1
+ // This is a shim for web and Android where the tab bar is generally opaque.
2
+ export default undefined;
3
+
4
+ export function useBottomTabOverflow() {
5
+ return 0;
6
+ }
@@ -6,22 +6,22 @@
6
6
  const tintColorLight = '#0a7ea4';
7
7
  const tintColorDark = '#fff';
8
8
 
9
- export const Colors = {
10
- light: {
11
- text: '#11181C',
12
- background: '#fff',
13
- tint: tintColorLight,
14
- icon: '#687076',
15
- tabIconDefault: '#687076',
16
- tabIconSelected: tintColorLight,
17
- },
18
- dark: {
19
- text: '#ECEDEE',
20
- background: '#151718',
21
- tint: tintColorDark,
22
- icon: '#9BA1A6',
23
- tabIconDefault: '#9BA1A6',
24
- tabIconSelected: tintColorDark,
25
- },
9
+ export const colors = {
10
+ light: {
11
+ text: '#11181C',
12
+ background: '#fff',
13
+ tint: tintColorLight,
14
+ icon: '#687076',
15
+ tabIconDefault: '#687076',
16
+ tabIconSelected: tintColorLight,
17
+ },
18
+ dark: {
19
+ text: '#ECEDEE',
20
+ background: '#151718',
21
+ tint: tintColorDark,
22
+ icon: '#9BA1A6',
23
+ tabIconDefault: '#9BA1A6',
24
+ tabIconSelected: tintColorDark,
25
+ },
26
26
  };
27
27
 
@@ -1,18 +1,18 @@
1
1
  export const NAV_THEME = {
2
- light: {
3
- background: 'hsl(0 0% 100%)', // background
4
- border: 'hsl(240 5.9% 90%)', // border
5
- card: 'hsl(0 0% 100%)', // card
6
- notification: 'hsl(0 84.2% 60.2%)', // destructive
7
- primary: 'hsl(240 5.9% 10%)', // primary
8
- text: 'hsl(240 10% 3.9%)', // foreground
9
- },
10
- dark: {
11
- background: 'hsl(240 10% 3.9%)', // background
12
- border: 'hsl(240 3.7% 15.9%)', // border
13
- card: 'hsl(240 10% 3.9%)', // card
14
- notification: 'hsl(0 72% 51%)', // destructive
15
- primary: 'hsl(0 0% 98%)', // primary
16
- text: 'hsl(0 0% 98%)', // foreground
17
- },
18
- };
2
+ light: {
3
+ background: 'hsl(0 0% 100%)', // background
4
+ border: 'hsl(240 5.9% 90%)', // border
5
+ card: 'hsl(0 0% 100%)', // card
6
+ notification: 'hsl(0 84.2% 60.2%)', // destructive
7
+ primary: 'hsl(240 5.9% 10%)', // primary
8
+ text: 'hsl(240 10% 3.9%)', // foreground
9
+ },
10
+ dark: {
11
+ background: 'hsl(240 10% 3.9%)', // background
12
+ border: 'hsl(240 3.7% 15.9%)', // border
13
+ card: 'hsl(240 10% 3.9%)', // card
14
+ notification: 'hsl(0 72% 51%)', // destructive
15
+ primary: 'hsl(0 0% 98%)', // primary
16
+ text: 'hsl(0 0% 98%)', // foreground
17
+ },
18
+ };
@@ -1,10 +1,8 @@
1
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
1
2
  // https://docs.expo.dev/guides/using-eslint/
2
- const { defineConfig } = require('eslint/config');
3
- const expoConfig = require('eslint-config-expo/flat');
4
-
5
3
  import eslint from '@eslint/js';
4
+ import expoConfig from 'eslint-config-expo/flat.js';
6
5
  // @ts-ignore
7
- import importPlugin from 'eslint-plugin-import';
8
6
  import jsdoc from 'eslint-plugin-jsdoc';
9
7
  import namedImportSpacing from 'eslint-plugin-named-import-spacing';
10
8
  import simpleImportSort from 'eslint-plugin-simple-import-sort';
@@ -13,31 +11,28 @@ import tseslint from 'typescript-eslint';
13
11
 
14
12
  export default tseslint.config(
15
13
  eslint.configs.recommended,
14
+ // eslint-disable-next-line import/no-named-as-default-member
16
15
  tseslint.configs.recommendedTypeChecked,
17
16
  jsdoc.configs['flat/recommended-typescript'],
18
- expoConfig,
17
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
18
+ expoConfig,
19
19
  {
20
20
  ignores: ['dist/*'],
21
21
  },
22
22
  {
23
23
  plugins: {
24
- '@typescript-eslint': tseslint.plugin,
25
24
  'simple-import-sort': simpleImportSort,
26
- import: importPlugin,
27
25
  'named-import-spacing': namedImportSpacing,
28
- jsdoc,
29
26
  },
30
27
  settings: {
31
28
  'import/resolver': {
32
29
  typescript: {
33
- project: [
34
- 'typescript/tsconfig.json',
35
- 'lib/tsconfig.json'
36
- ],
30
+ project: './tsconfig.json',
37
31
  }
38
32
  }
39
33
  },
40
34
  languageOptions: {
35
+ // eslint-disable-next-line import/no-named-as-default-member
41
36
  parser: tseslint.parser,
42
37
  parserOptions: {
43
38
  projectService: true,
@@ -99,7 +94,7 @@ export default tseslint.config(
99
94
  'no-multi-spaces': 'error',
100
95
  'import/newline-after-import': ['error', { count: 1 }],
101
96
  'named-import-spacing/named-import-spacing': 2,
102
- 'no-unused-vars': 'warn',
97
+ '@typescript-eslint/no-unused-vars': 'warn',
103
98
  'import/no-unresolved': [0],
104
99
  'no-forbidden-relative-imports': [0],
105
100
  '@typescript-eslint/triple-slash-reference': 'off',
@@ -119,8 +114,13 @@ export default tseslint.config(
119
114
  '@typescript-eslint/naming-convention': [
120
115
  'error',
121
116
  {
122
- selector: ['variable', 'function'],
123
- format: ['camelCase'],
117
+ selector: ['variable'],
118
+ format: ['camelCase', 'UPPER_CASE'],
119
+ leadingUnderscore: 'allow',
120
+ },
121
+ {
122
+ selector: ['function'],
123
+ format: ['camelCase', 'PascalCase'],
124
124
  leadingUnderscore: 'allow',
125
125
  },
126
126
  {
@@ -175,10 +175,6 @@ export default tseslint.config(
175
175
  selector: 'typeLike',
176
176
  format: ['PascalCase'],
177
177
  },
178
- {
179
- selector: 'function',
180
- format: ['UPPER_CASE'],
181
- },
182
178
  ],
183
179
  },
184
180
  },
@@ -1,11 +1,17 @@
1
1
  import { useColorScheme as useNativewindColorScheme } from 'nativewind';
2
2
 
3
+ /**
4
+ * Custom hook to get the current color scheme and provide methods to change it.
5
+ * This hook uses the nativewind's useColorScheme to access the color scheme.
6
+ * @returns An object containing the current color scheme, a method to set the color scheme,
7
+ */
3
8
  export function useColorScheme() {
4
- const { colorScheme, setColorScheme, toggleColorScheme } = useNativewindColorScheme();
5
- return {
6
- colorScheme: colorScheme ?? 'dark',
7
- isDarkColorScheme: colorScheme === 'dark',
8
- setColorScheme,
9
- toggleColorScheme,
10
- };
9
+ const { colorScheme, setColorScheme, toggleColorScheme } = useNativewindColorScheme();
10
+
11
+ return {
12
+ colorScheme: colorScheme ?? 'dark',
13
+ isDarkColorScheme: colorScheme === 'dark',
14
+ setColorScheme,
15
+ toggleColorScheme,
16
+ };
11
17
  }
@@ -3,19 +3,22 @@ import { useColorScheme as useRNColorScheme } from 'react-native';
3
3
 
4
4
  /**
5
5
  * To support static rendering, this value needs to be re-calculated on the client side for web
6
+ * This hook returns the color scheme after the component has mounted, ensuring that the initial render
7
+ * does not depend on the color scheme.
8
+ * @returns The current color scheme ('light' or 'dark') after hydration.
6
9
  */
7
10
  export function useColorScheme() {
8
- const [hasHydrated, setHasHydrated] = useState(false);
11
+ const [hasHydrated, setHasHydrated] = useState(false);
9
12
 
10
- useEffect(() => {
11
- setHasHydrated(true);
12
- }, []);
13
+ useEffect(() => {
14
+ setHasHydrated(true);
15
+ }, []);
13
16
 
14
- const colorScheme = useRNColorScheme();
17
+ const colorScheme = useRNColorScheme();
15
18
 
16
- if (hasHydrated) {
17
- return colorScheme;
18
- }
19
+ if (hasHydrated) {
20
+ return colorScheme;
21
+ }
19
22
 
20
- return 'light';
23
+ return 'light';
21
24
  }
@@ -3,19 +3,28 @@
3
3
  * https://docs.expo.dev/guides/color-schemes/
4
4
  */
5
5
 
6
- import { Colors } from '@/constants/Colors';
6
+ import { colors } from '@/constants/Colors';
7
+
7
8
  import { useColorScheme } from './useColorSchemeRN';
8
9
 
10
+ /**
11
+ * Returns the theme color based on the current color scheme and the provided color name.
12
+ * @param props - An object containing light and dark color properties.
13
+ * @param props.light - The color to use in light mode.
14
+ * @param props.dark - The color to use in dark mode.
15
+ * @param colorName - The name of the color to retrieve from the colors object.
16
+ * @returns The color corresponding to the current color scheme.
17
+ */
9
18
  export function useThemeColor(
10
- props: { light?: string; dark?: string },
11
- colorName: keyof typeof Colors.light & keyof typeof Colors.dark
19
+ props: { light?: string; dark?: string },
20
+ colorName: keyof typeof colors.light
12
21
  ) {
13
- const theme = useColorScheme() ?? 'light';
14
- const colorFromProps = props[theme];
22
+ const theme = useColorScheme() ?? 'light';
23
+ const colorFromProps = props[theme];
15
24
 
16
- if (colorFromProps) {
17
- return colorFromProps;
18
- } else {
19
- return Colors[theme][colorName];
20
- }
25
+ if (colorFromProps) {
26
+ return colorFromProps;
27
+ } else {
28
+ return colors[theme][colorName];
29
+ }
21
30
  }
@@ -1,45 +1,48 @@
1
1
  import { Effect, Schedule, Schema } from 'effect';
2
- import { client, ClientError } from './wixClient';
2
+
3
+ import { client, ClientError } from './wix';
3
4
 
4
5
  const dataSchema = Schema.Struct({
5
- source: Schema.String,
6
- content: Schema.String,
7
- _id: Schema.String,
8
- _owner: Schema.String,
9
- _createdDate: Schema.Any,
10
- _updatedDate: Schema.Any,
6
+ source: Schema.String,
7
+ content: Schema.String,
8
+ _id: Schema.String,
9
+ _owner: Schema.String,
10
+ _createdDate: Schema.Any,
11
+ _updatedDate: Schema.Any,
11
12
  });
12
13
 
13
- const COLLECTION_NAME = "dailySpiritQuotes";
14
+ const COLLECTION_NAME = 'dailySpiritQuotes';
14
15
 
15
16
  export const getQuote = () => Effect.gen(function* () {
16
- const count = yield* Effect.retry(Effect.tryPromise({
17
- try: () => client.items.query(COLLECTION_NAME).count(),
18
- catch: (error) => {
19
- console.error('Error fetching quotes:', error);
20
- Effect.fail(new ClientError());
21
- }
22
- }), Schedule.fromDelays(50, 100, 200, 400, 800));
23
-
24
- if (count === 0) {
25
- return yield* Effect.fail(new Error("No quotes found in the collection."));
26
- }
17
+ const count = yield* Effect.retry(Effect.tryPromise({
18
+ try: () => client.items.query(COLLECTION_NAME).count(),
19
+ catch: (error) => {
20
+ console.error('Error fetching quotes:', error);
21
+ Effect.fail(new ClientError());
22
+ }
23
+ }), Schedule.fromDelays(50, 100, 200, 400, 800));
24
+
25
+ if (count === 0) {
26
+ return yield* Effect.fail(new Error('No quotes found in the collection.'));
27
+ }
27
28
 
28
- const randomIndex = Math.floor(Math.random() * count);
29
+ const randomIndex = Math.floor(Math.random() * count);
29
30
 
30
- const data = yield* Effect.retry(Effect.tryPromise({
31
- try: () => client.items.query(COLLECTION_NAME).skip(randomIndex).limit(1).find(),
32
- catch: (error) => {
33
- console.error('Error fetching quotes:', error);
34
- Effect.fail(new ClientError());
35
- }
36
- }), Schedule.fromDelays(50, 100, 200, 400, 800));
31
+ const data = yield* Effect.retry(Effect.tryPromise({
32
+ try: () => client.items.query(COLLECTION_NAME).skip(randomIndex).limit(1).find(),
33
+ catch: (error) => {
34
+ console.error('Error fetching quotes:', error);
35
+ Effect.fail(new ClientError());
36
+ }
37
+ }), Schedule.fromDelays(50, 100, 200, 400, 800));
37
38
 
38
- yield* Effect.logDebug(`Fetched ${JSON.stringify(data, null, 2)}`);
39
+ yield* Effect.logDebug(`Fetched ${JSON.stringify(data, null, 2)}`);
39
40
 
40
- const quote = yield* Schema.decodeUnknown(dataSchema)(data.items[0]);
41
- return { source: quote.source, content: quote.content }
41
+ const quote = yield* Schema.decodeUnknown(dataSchema)(data.items[0]);
42
+
43
+ return { source: quote.source, content: quote.content };
42
44
  }).pipe(Effect.catchAll((error) => {
43
- console.error('Failed to fetch quote:', error);
44
- return Effect.succeed({ source: 'The Lord', content: 'The ways of the Lord are inscrutable.'});
45
- }))
45
+ console.error('Failed to fetch quote:', error);
46
+
47
+ return Effect.succeed({ source: 'The Lord', content: 'The ways of the Lord are inscrutable.' });
48
+ }));
@@ -1,6 +1,11 @@
1
- import { clsx, type ClassValue } from 'clsx';
1
+ import { type ClassValue, clsx } from 'clsx';
2
2
  import { twMerge } from 'tailwind-merge';
3
3
 
4
+ /**
5
+ * Combines class names and merges Tailwind CSS classes.
6
+ * @param inputs - The class names to combine.
7
+ * @returns A string of combined class names.
8
+ */
4
9
  export function cn(...inputs: ClassValue[]) {
5
- return twMerge(clsx(inputs));
10
+ return twMerge(clsx(inputs));
6
11
  }