@coze-arch/cli 0.0.1-alpha.f5dbe4 → 0.0.1-alpha.f62945

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 (83) hide show
  1. package/lib/__templates__/expo/.cozeproj/scripts/dev_run.sh +13 -12
  2. package/lib/__templates__/expo/.cozeproj/scripts/server_dev_run.sh +9 -8
  3. package/lib/__templates__/expo/README.md +3 -5
  4. package/lib/__templates__/expo/client/app/+not-found.tsx +19 -4
  5. package/lib/__templates__/expo/client/app/_layout.tsx +17 -16
  6. package/lib/__templates__/expo/client/components/ThemedText.tsx +33 -0
  7. package/lib/__templates__/expo/client/components/ThemedView.tsx +37 -0
  8. package/lib/__templates__/expo/client/constants/theme.ts +177 -0
  9. package/lib/__templates__/expo/client/hooks/useColorScheme.tsx +48 -0
  10. package/lib/__templates__/expo/client/hooks/useTheme.ts +33 -0
  11. package/lib/__templates__/expo/client/metro.config.js +4 -8
  12. package/lib/__templates__/expo/client/package.json +29 -31
  13. package/lib/__templates__/expo/client/screens/demo/index.tsx +13 -7
  14. package/lib/__templates__/expo/package.json +1 -1
  15. package/lib/__templates__/expo/patches/{expo@54.0.32.patch → expo@54.0.33.patch} +3 -2
  16. package/lib/__templates__/expo/pnpm-lock.yaml +340 -1736
  17. package/lib/__templates__/expo/server/package.json +9 -7
  18. package/lib/__templates__/expo/server/src/index.ts +1 -0
  19. package/lib/__templates__/expo/template.config.js +56 -0
  20. package/lib/__templates__/native-static/.coze +11 -0
  21. package/lib/__templates__/native-static/index.html +33 -0
  22. package/lib/__templates__/native-static/styles/main.css +136 -0
  23. package/lib/__templates__/native-static/template.config.js +22 -0
  24. package/lib/__templates__/nextjs/package.json +3 -1
  25. package/lib/__templates__/nextjs/pnpm-lock.yaml +119 -106
  26. package/lib/__templates__/nextjs/src/app/page.tsx +18 -60
  27. package/lib/__templates__/nextjs/template.config.js +49 -14
  28. package/lib/__templates__/taro/.coze +14 -0
  29. package/lib/__templates__/taro/.cozeproj/scripts/deploy_build.sh +19 -0
  30. package/lib/__templates__/taro/.cozeproj/scripts/deploy_run.sh +14 -0
  31. package/lib/__templates__/taro/.cozeproj/scripts/dev_build.sh +2 -0
  32. package/lib/__templates__/taro/.cozeproj/scripts/dev_run.sh +151 -0
  33. package/lib/__templates__/taro/.cozeproj/scripts/init_env.sh +5 -0
  34. package/lib/__templates__/taro/.cozeproj/scripts/pack.sh +24 -0
  35. package/lib/__templates__/taro/README.md +751 -0
  36. package/lib/__templates__/taro/_gitignore +40 -0
  37. package/lib/__templates__/taro/_npmrc +18 -0
  38. package/lib/__templates__/taro/babel.config.js +12 -0
  39. package/lib/__templates__/taro/config/dev.ts +9 -0
  40. package/lib/__templates__/taro/config/index.ts +223 -0
  41. package/lib/__templates__/taro/config/prod.ts +34 -0
  42. package/lib/__templates__/taro/eslint.config.mjs +80 -0
  43. package/lib/__templates__/taro/key/private.appid.key +0 -0
  44. package/lib/__templates__/taro/package.json +107 -0
  45. package/lib/__templates__/taro/patches/@tarojs__plugin-mini-ci@4.1.9.patch +30 -0
  46. package/lib/__templates__/taro/pnpm-lock.yaml +23100 -0
  47. package/lib/__templates__/taro/pnpm-workspace.yaml +2 -0
  48. package/lib/__templates__/taro/project.config.json +15 -0
  49. package/lib/__templates__/taro/server/nest-cli.json +10 -0
  50. package/lib/__templates__/taro/server/package.json +40 -0
  51. package/lib/__templates__/taro/server/src/app.controller.ts +23 -0
  52. package/lib/__templates__/taro/server/src/app.module.ts +10 -0
  53. package/lib/__templates__/taro/server/src/app.service.ts +8 -0
  54. package/lib/__templates__/taro/server/src/interceptors/http-status.interceptor.ts +23 -0
  55. package/lib/__templates__/taro/server/src/main.ts +49 -0
  56. package/lib/__templates__/taro/server/tsconfig.json +24 -0
  57. package/lib/__templates__/taro/src/app.config.ts +11 -0
  58. package/lib/__templates__/taro/src/app.css +52 -0
  59. package/lib/__templates__/taro/src/app.tsx +9 -0
  60. package/lib/__templates__/taro/src/index.html +39 -0
  61. package/lib/__templates__/taro/src/network.ts +39 -0
  62. package/lib/__templates__/taro/src/pages/index/index.config.ts +3 -0
  63. package/lib/__templates__/taro/src/pages/index/index.css +1 -0
  64. package/lib/__templates__/taro/src/pages/index/index.tsx +33 -0
  65. package/lib/__templates__/taro/src/presets/dev-debug.ts +23 -0
  66. package/lib/__templates__/taro/src/presets/h5-container.tsx +15 -0
  67. package/lib/__templates__/taro/src/presets/h5-navbar.tsx +201 -0
  68. package/lib/__templates__/taro/src/presets/h5-styles.ts +142 -0
  69. package/lib/__templates__/taro/src/presets/index.tsx +18 -0
  70. package/lib/__templates__/taro/stylelint.config.mjs +4 -0
  71. package/lib/__templates__/taro/template.config.js +68 -0
  72. package/lib/__templates__/taro/tsconfig.json +29 -0
  73. package/lib/__templates__/taro/types/global.d.ts +32 -0
  74. package/lib/__templates__/templates.json +75 -0
  75. package/lib/__templates__/vite/package.json +5 -1
  76. package/lib/__templates__/vite/pnpm-lock.yaml +146 -1659
  77. package/lib/__templates__/vite/src/main.ts +17 -47
  78. package/lib/__templates__/vite/template.config.js +49 -14
  79. package/lib/__templates__/vite/vite.config.ts +1 -0
  80. package/lib/cli.js +62 -68
  81. package/package.json +2 -1
  82. package/lib/__templates__/expo/client/global.css +0 -76
  83. package/lib/__templates__/expo/client/uniwind-types.d.ts +0 -10
@@ -1,7 +1,7 @@
1
1
  ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
2
2
  PREVIEW_DIR="${COZE_PREVIEW_DIR:-/source/preview}"
3
3
  LOG_DIR="${COZE_LOG_DIR:-$ROOT_DIR/logs}"
4
- LOG_FILE="$LOG_DIR/app.log"
4
+ LOG_CLIENT_FILE="$LOG_DIR/client.log"
5
5
  mkdir -p "$LOG_DIR"
6
6
 
7
7
  # ==================== 配置项 ====================
@@ -89,14 +89,17 @@ ensure_port() {
89
89
  pipe_to_log() {
90
90
  local source="${1:-CLIENT}"
91
91
  local raw_log="${2:-}"
92
- local line timestamp ts msg record
92
+ local line clean_line timestamp
93
93
  while IFS= read -r line || [ -n "$line" ]; do
94
+ clean_line=$(printf '%s' "$line" | sed 's/\x1b\[[0-9;]*[mA-Za-z]//g')
95
+ if echo "$clean_line" | grep -qE '(^(Web|iOS|Android) node_modules/.*expo-router.*entry\.js.*%|^(Web|iOS|Android) Bundled [0-9]+ms node_modules/.*expo-router.*entry\.js|Logs will appear in the browser)'; then
96
+ continue
97
+ fi
94
98
  if [ -n "$raw_log" ]; then
95
- echo "$line" >> "$raw_log"
99
+ timestamp=$(date '+%Y-%m-%d %H:%M:%S')
100
+ echo "[$timestamp] $clean_line" >> "$raw_log"
96
101
  fi
97
- line=$(echo "[$source] $line" | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g; s/\x1b\[[0-9;]*m//g')
98
- msg="${line}"
99
- echo "$msg"
102
+ printf '[%s] %s\n' "$source" "$clean_line"
100
103
  done
101
104
  }
102
105
 
@@ -116,10 +119,10 @@ start_expo() {
116
119
 
117
120
  if [ "$offline" = "1" ]; then
118
121
  ( EXPO_OFFLINE=1 EXPO_NO_DEPENDENCY_VALIDATION=1 EXPO_PUBLIC_BACKEND_BASE_URL="$EXPO_PUBLIC_BACKEND_BASE_URL" EXPO_PACKAGER_PROXY_URL="$EXPO_PACKAGER_PROXY_URL" EXPO_PUBLIC_COZE_PROJECT_ID="$EXPO_PUBLIC_COZE_PROJECT_ID" \
119
- nohup npx expo start --clear --port "$EXPO_PORT" 2>&1 | pipe_to_log "CLIENT" "$ROOT_DIR/logs/client.log" ) &
122
+ nohup npx expo start --clear --port "$EXPO_PORT" 2>&1 | pipe_to_log "CLIENT" "$LOG_CLIENT_FILE" ) &
120
123
  else
121
124
  ( EXPO_NO_DEPENDENCY_VALIDATION=1 EXPO_PUBLIC_BACKEND_BASE_URL="$EXPO_PUBLIC_BACKEND_BASE_URL" EXPO_PACKAGER_PROXY_URL="$EXPO_PACKAGER_PROXY_URL" EXPO_PUBLIC_COZE_PROJECT_ID="$EXPO_PUBLIC_COZE_PROJECT_ID" \
122
- nohup npx expo start --clear --port "$EXPO_PORT" 2>&1 | pipe_to_log "CLIENT" "$ROOT_DIR/logs/client.log" ) &
125
+ nohup npx expo start --clear --port "$EXPO_PORT" 2>&1 | pipe_to_log "CLIENT" "$LOG_CLIENT_FILE" ) &
123
126
  fi
124
127
  EXPO_PID=$!
125
128
  disown $EXPO_PID 2>/dev/null || true
@@ -130,9 +133,9 @@ start_expo() {
130
133
  detect_expo_fetch_failed() {
131
134
  local timeout="${1:-8}"
132
135
  local waited=0
133
- local log_file="$ROOT_DIR/logs/client.log"
136
+ local log_file="$LOG_CLIENT_FILE"
134
137
  while [ "$waited" -lt "$timeout" ]; do
135
- if [ -f "$log_file" ] && grep -q "TypeError: fetch failed" "$log_file" 2>/dev/null; then
138
+ if [ -f "$log_file" ] && tail -n 100 "$log_file" 2>/dev/null | grep -q "TypeError: fetch failed"; then
136
139
  return 0
137
140
  fi
138
141
  sleep 1
@@ -166,8 +169,6 @@ check_command "pnpm"
166
169
  check_command "lsof"
167
170
  check_command "bash"
168
171
 
169
- echo "准备日志目录:$ROOT_DIR/logs"
170
- mkdir -p "$ROOT_DIR/logs"
171
172
  # 端口占用预检查与处理
172
173
  ensure_port SERVER_PORT "$SERVER_PORT"
173
174
  ensure_port EXPO_PORT "$EXPO_PORT"
@@ -2,8 +2,8 @@
2
2
 
3
3
  ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
4
4
  SERVER_DIR="$ROOT_DIR/server"
5
- LOG_DIR="$ROOT_DIR/logs"
6
- LOG_FILE="$LOG_DIR/server.log"
5
+ LOG_DIR="${COZE_LOG_DIR:-$ROOT_DIR/logs}"
6
+ LOG_SERVER_FILE="$LOG_DIR/server.log"
7
7
  SERVER_PORT="${SERVER_PORT:-9091}"
8
8
 
9
9
  mkdir -p "$LOG_DIR"
@@ -11,13 +11,14 @@ mkdir -p "$LOG_DIR"
11
11
  pipe_to_log() {
12
12
  local source="${1:-SERVER}"
13
13
  local raw_log="${2:-}"
14
- local line
14
+ local line clean_line timestamp
15
15
  while IFS= read -r line || [ -n "$line" ]; do
16
+ clean_line=$(printf '%s' "$line" | sed 's/\x1b\[[0-9;]*[mA-Za-z]//g')
16
17
  if [ -n "$raw_log" ]; then
17
- echo "$line" >> "$raw_log"
18
+ timestamp=$(date '+%Y-%m-%d %H:%M:%S')
19
+ echo "[$timestamp] $clean_line" >> "$raw_log"
18
20
  fi
19
- line=$(echo "[$source] $line" | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g; s/\x1b\[[0-9;]*m//g')
20
- echo "$line"
21
+ printf '[%s] %s\n' "$source" "$clean_line"
21
22
  done
22
23
  }
23
24
 
@@ -36,10 +37,10 @@ kill_old_server() {
36
37
  echo "==================== Server Dev Run ===================="
37
38
  echo "Server 目录:$SERVER_DIR"
38
39
  echo "Server 端口:$SERVER_PORT"
39
- echo "日志文件:$LOG_FILE"
40
+ echo "日志文件:$LOG_SERVER_FILE"
40
41
 
41
42
  kill_old_server
42
43
 
43
44
  echo "启动 server 服务..."
44
45
  cd "$SERVER_DIR"
45
- NODE_ENV=development PORT="$SERVER_PORT" npx tsx ./src/index.ts 2>&1 | pipe_to_log "SERVER" "$LOG_FILE"
46
+ NODE_ENV=development PORT="$SERVER_PORT" npx tsx watch ./src/index.ts 2>&1 | pipe_to_log "SERVER" "$LOG_SERVER_FILE"
@@ -20,11 +20,13 @@
20
20
  │ │ └── index.tsx # re-export home.tsx
21
21
  │ ├── screens/ # 页面实现目录(与 app/ 路由对应)
22
22
  │ │ └── demo/ # demo 示例页面
23
- │ │ └── index.tsx # 页面组件实现
23
+ │ │ ├── index.tsx # 页面组件实现
24
+ │ │ └── styles.ts # 页面样式
24
25
  │ ├── components/ # 可复用组件
25
26
  │ │ └── Screen.tsx # 页面容器组件(必用)
26
27
  │ ├── hooks/ # 自定义 Hooks
27
28
  │ ├── contexts/ # React Context 代码
29
+ │ ├── constants/ # 常量定义(如主题配置)
28
30
  │ ├── utils/ # 工具函数
29
31
  │ ├── assets/ # 静态资源
30
32
  | └── package.json # Expo 应用 package.json
@@ -32,10 +34,6 @@
32
34
  ├── .cozeproj # 预置脚手架脚本(禁止修改)
33
35
  └── .coze # 配置文件(禁止修改)
34
36
 
35
- ## 样式方案(TailwindCSS v4)
36
-
37
- 本项目通过 uniwind 实现了 react-native 对 TailwindCSS v4 的支持,在开始开发前,应先查看 client/global.css 了解必要的信息
38
-
39
37
  ## 安装依赖
40
38
 
41
39
  ### 命令
@@ -1,15 +1,30 @@
1
- import { View, Text } from 'react-native';
1
+ import { View, Text, StyleSheet } from 'react-native';
2
2
  import { Link } from 'expo-router';
3
+ import { useTheme } from '@/hooks/useTheme';
4
+ import { Spacing } from '@/constants/theme';
3
5
 
4
6
  export default function NotFoundScreen() {
7
+ const { theme } = useTheme();
8
+
5
9
  return (
6
- <View className="flex-1 justify-center items-center bg-background">
7
- <Text className="text-foreground">
10
+ <View style={[styles.container, { backgroundColor: theme.backgroundRoot }]}>
11
+ <Text>
8
12
  页面不存在
9
13
  </Text>
10
- <Link href="/" className="text-accent mt-6">
14
+ <Link href="/" style={[styles.gohome]}>
11
15
  返回首页
12
16
  </Link>
13
17
  </View>
14
18
  );
15
19
  }
20
+
21
+ const styles = StyleSheet.create({
22
+ container: {
23
+ flex: 1,
24
+ justifyContent: 'center',
25
+ alignItems: 'center',
26
+ },
27
+ gohome: {
28
+ marginTop: Spacing['2xl'],
29
+ },
30
+ });
@@ -5,8 +5,7 @@ import { StatusBar } from 'expo-status-bar';
5
5
  import { LogBox } from 'react-native';
6
6
  import Toast from 'react-native-toast-message';
7
7
  import { AuthProvider } from "@/contexts/AuthContext";
8
-
9
- import '../global.css';
8
+ import { ColorSchemeProvider } from '@/hooks/useColorScheme';
10
9
 
11
10
  LogBox.ignoreLogs([
12
11
  "TurboModuleRegistry.getEnforcing(...): 'RNMapsAirModule' could not be found",
@@ -16,20 +15,22 @@ LogBox.ignoreLogs([
16
15
  export default function RootLayout() {
17
16
  return (
18
17
  <AuthProvider>
19
- <GestureHandlerRootView style={{ flex: 1 }}>
20
- <StatusBar style="dark"></StatusBar>
21
- <Stack screenOptions={{
22
- // 设置所有页面的切换动画为从右侧滑入,适用于iOS 和 Android
23
- animation: 'slide_from_right',
24
- gestureEnabled: true,
25
- gestureDirection: 'horizontal',
26
- // 隐藏自带的头部
27
- headerShown: false
28
- }}>
29
- <Stack.Screen name="index" options={{ title: "" }} />
30
- </Stack>
31
- <Toast />
32
- </GestureHandlerRootView>
18
+ <ColorSchemeProvider>
19
+ <GestureHandlerRootView style={{ flex: 1 }}>
20
+ <StatusBar style="dark"></StatusBar>
21
+ <Stack screenOptions={{
22
+ // 设置所有页面的切换动画为从右侧滑入,适用于iOS 和 Android
23
+ animation: 'slide_from_right',
24
+ gestureEnabled: true,
25
+ gestureDirection: 'horizontal',
26
+ // 隐藏自带的头部
27
+ headerShown: false
28
+ }}>
29
+ <Stack.Screen name="index" options={{ title: "" }} />
30
+ </Stack>
31
+ <Toast />
32
+ </GestureHandlerRootView>
33
+ </ColorSchemeProvider>
33
34
  </AuthProvider>
34
35
  );
35
36
  }
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { Text, TextProps, TextStyle } from 'react-native';
3
+ import { useTheme } from '@/hooks/useTheme';
4
+ import { Typography } from '@/constants/theme';
5
+
6
+ type TypographyVariant = keyof typeof Typography;
7
+
8
+ interface ThemedTextProps extends TextProps {
9
+ variant?: TypographyVariant;
10
+ color?: string;
11
+ }
12
+
13
+ export function ThemedText({
14
+ variant = 'body',
15
+ color,
16
+ style,
17
+ children,
18
+ ...props
19
+ }: ThemedTextProps) {
20
+ const { theme } = useTheme();
21
+ const typographyStyle = Typography[variant];
22
+
23
+ const textStyle: TextStyle = {
24
+ ...typographyStyle,
25
+ color: color ?? theme.textPrimary,
26
+ };
27
+
28
+ return (
29
+ <Text style={[textStyle, style]} {...props}>
30
+ {children}
31
+ </Text>
32
+ );
33
+ }
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import { View, ViewProps, ViewStyle } from 'react-native';
3
+ import { useTheme } from '@/hooks/useTheme';
4
+
5
+ type BackgroundLevel = 'root' | 'default' | 'tertiary';
6
+
7
+ interface ThemedViewProps extends ViewProps {
8
+ level?: BackgroundLevel;
9
+ backgroundColor?: string;
10
+ }
11
+
12
+ const backgroundMap: Record<BackgroundLevel, string> = {
13
+ root: 'backgroundRoot',
14
+ default: 'backgroundDefault',
15
+ tertiary: 'backgroundTertiary',
16
+ };
17
+
18
+ export function ThemedView({
19
+ level = 'root',
20
+ backgroundColor,
21
+ style,
22
+ children,
23
+ ...props
24
+ }: ThemedViewProps) {
25
+ const { theme } = useTheme();
26
+ const bgColor = backgroundColor ?? (theme as any)[backgroundMap[level]];
27
+
28
+ const viewStyle: ViewStyle = {
29
+ backgroundColor: bgColor,
30
+ };
31
+
32
+ return (
33
+ <View style={[viewStyle, style]} {...props}>
34
+ {children}
35
+ </View>
36
+ );
37
+ }
@@ -0,0 +1,177 @@
1
+ export const Colors = {
2
+ light: {
3
+ textPrimary: "#1C1917",
4
+ textSecondary: "#78716c",
5
+ textMuted: "#9CA3AF",
6
+ primary: "#4F46E5", // Indigo-600 - 品牌主色,代表科技与智能
7
+ accent: "#8B5CF6", // Violet-500 - 辅助色,代表创造力
8
+ success: "#10B981", // Emerald-500
9
+ error: "#EF4444",
10
+ backgroundRoot: "#FAFAFA",
11
+ backgroundDefault: "#FFFFFF",
12
+ backgroundTertiary: "#F9FAFB", // 更浅的背景色,用于去线留白
13
+ buttonPrimaryText: "#FFFFFF",
14
+ tabIconSelected: "#4F46E5",
15
+ border: "#E5E7EB",
16
+ borderLight: "#F3F4F6",
17
+ },
18
+ dark: {
19
+ textPrimary: "#FAFAF9",
20
+ textSecondary: "#A8A29E",
21
+ textMuted: "#6F767E",
22
+ primary: "#818CF8", // Indigo-400 - 暗色模式品牌主色
23
+ accent: "#A78BFA", // Violet-400
24
+ success: "#34D399",
25
+ error: "#F87171",
26
+ backgroundRoot: "#09090B", // 更深的背景色
27
+ backgroundDefault: "#1C1C1E",
28
+ backgroundTertiary: "#1F1F22", // 暗色模式去线留白背景
29
+ buttonPrimaryText: "#09090B",
30
+ tabIconSelected: "#818CF8",
31
+ border: "#3F3F46",
32
+ borderLight: "#27272A",
33
+ },
34
+ };
35
+
36
+ export const Spacing = {
37
+ xs: 4,
38
+ sm: 8,
39
+ md: 12,
40
+ lg: 16,
41
+ xl: 20,
42
+ "2xl": 24,
43
+ "3xl": 32,
44
+ "4xl": 40,
45
+ "5xl": 48,
46
+ "6xl": 64,
47
+ };
48
+
49
+ export const BorderRadius = {
50
+ xs: 4,
51
+ sm: 8,
52
+ md: 12,
53
+ lg: 16,
54
+ xl: 20,
55
+ "2xl": 24,
56
+ "3xl": 28,
57
+ "4xl": 32,
58
+ full: 9999,
59
+ };
60
+
61
+ export const Typography = {
62
+ display: {
63
+ fontSize: 112,
64
+ lineHeight: 112,
65
+ fontWeight: "200" as const,
66
+ letterSpacing: -4,
67
+ },
68
+ displayLarge: {
69
+ fontSize: 112,
70
+ lineHeight: 112,
71
+ fontWeight: "200" as const,
72
+ letterSpacing: -2,
73
+ },
74
+ displayMedium: {
75
+ fontSize: 48,
76
+ lineHeight: 56,
77
+ fontWeight: "200" as const,
78
+ },
79
+ h1: {
80
+ fontSize: 32,
81
+ lineHeight: 40,
82
+ fontWeight: "700" as const,
83
+ },
84
+ h2: {
85
+ fontSize: 28,
86
+ lineHeight: 36,
87
+ fontWeight: "700" as const,
88
+ },
89
+ h3: {
90
+ fontSize: 24,
91
+ lineHeight: 32,
92
+ fontWeight: "300" as const,
93
+ },
94
+ h4: {
95
+ fontSize: 20,
96
+ lineHeight: 28,
97
+ fontWeight: "600" as const,
98
+ },
99
+ title: {
100
+ fontSize: 18,
101
+ lineHeight: 24,
102
+ fontWeight: "700" as const,
103
+ },
104
+ body: {
105
+ fontSize: 16,
106
+ lineHeight: 24,
107
+ fontWeight: "400" as const,
108
+ },
109
+ bodyMedium: {
110
+ fontSize: 16,
111
+ lineHeight: 24,
112
+ fontWeight: "500" as const,
113
+ },
114
+ small: {
115
+ fontSize: 14,
116
+ lineHeight: 20,
117
+ fontWeight: "400" as const,
118
+ },
119
+ smallMedium: {
120
+ fontSize: 14,
121
+ lineHeight: 20,
122
+ fontWeight: "500" as const,
123
+ },
124
+ caption: {
125
+ fontSize: 12,
126
+ lineHeight: 16,
127
+ fontWeight: "400" as const,
128
+ },
129
+ captionMedium: {
130
+ fontSize: 12,
131
+ lineHeight: 16,
132
+ fontWeight: "500" as const,
133
+ },
134
+ label: {
135
+ fontSize: 14,
136
+ lineHeight: 20,
137
+ fontWeight: "500" as const,
138
+ letterSpacing: 2,
139
+ textTransform: "uppercase" as const,
140
+ },
141
+ labelSmall: {
142
+ fontSize: 12,
143
+ lineHeight: 16,
144
+ fontWeight: "500" as const,
145
+ letterSpacing: 1,
146
+ textTransform: "uppercase" as const,
147
+ },
148
+ labelTitle: {
149
+ fontSize: 14,
150
+ lineHeight: 20,
151
+ fontWeight: "700" as const,
152
+ letterSpacing: 2,
153
+ textTransform: "uppercase" as const,
154
+ },
155
+ link: {
156
+ fontSize: 16,
157
+ lineHeight: 24,
158
+ fontWeight: "400" as const,
159
+ },
160
+ stat: {
161
+ fontSize: 30,
162
+ lineHeight: 36,
163
+ fontWeight: "300" as const,
164
+ },
165
+ tiny: {
166
+ fontSize: 10,
167
+ lineHeight: 14,
168
+ fontWeight: "400" as const,
169
+ },
170
+ navLabel: {
171
+ fontSize: 10,
172
+ lineHeight: 14,
173
+ fontWeight: "500" as const,
174
+ },
175
+ };
176
+
177
+ export type Theme = typeof Colors.light;
@@ -0,0 +1,48 @@
1
+ import { createContext, Dispatch, ReactNode, SetStateAction, useContext, useEffect, useState } from 'react';
2
+ import { ColorSchemeName, useColorScheme as useReactNativeColorScheme, Platform } from 'react-native';
3
+
4
+ const ColorSchemeContext = createContext<'light' | 'dark' | null | undefined>(null);
5
+
6
+ const ColorSchemeProvider = function ({ children }: { children?: ReactNode }) {
7
+ const systemColorScheme = useReactNativeColorScheme();
8
+ const [colorScheme, setColorScheme] = useState(systemColorScheme);
9
+
10
+ useEffect(() => {
11
+ setColorScheme(systemColorScheme);
12
+ }, [systemColorScheme]);
13
+
14
+ useEffect(() => {
15
+ function handleMessage(e: MessageEvent<{ event: string; colorScheme: ColorSchemeName; } | undefined>) {
16
+ if (e.data?.event === 'coze.workbench.colorScheme') {
17
+ const cs = e.data.colorScheme;
18
+ if (typeof cs === 'string' && typeof setColorScheme === 'function') {
19
+ setColorScheme(cs);
20
+ }
21
+ }
22
+ }
23
+
24
+ if (Platform.OS === 'web') {
25
+ window.addEventListener('message', handleMessage, false);
26
+ }
27
+
28
+ return () => {
29
+ if (Platform.OS === 'web') {
30
+ window.removeEventListener('message', handleMessage, false);
31
+ }
32
+ }
33
+ }, [setColorScheme]);
34
+
35
+ return <ColorSchemeContext.Provider value={colorScheme}>
36
+ {children}
37
+ </ColorSchemeContext.Provider>
38
+ };
39
+
40
+ function useColorScheme() {
41
+ const colorScheme = useContext(ColorSchemeContext);
42
+ return colorScheme;
43
+ }
44
+
45
+ export {
46
+ ColorSchemeProvider,
47
+ useColorScheme,
48
+ }
@@ -0,0 +1,33 @@
1
+ import { Colors } from '@/constants/theme';
2
+ import { useColorScheme } from '@/hooks/useColorScheme';
3
+
4
+ enum COLOR_SCHEME_CHOICE {
5
+ FOLLOW_SYSTEM = 'follow-system', // 跟随系统自动变化
6
+ DARK = 'dark', // 固定为 dark 主题,不随系统变化
7
+ LIGHT = 'light', // 固定为 light 主题,不随系统变化
8
+ };
9
+
10
+ const userPreferColorScheme: COLOR_SCHEME_CHOICE = COLOR_SCHEME_CHOICE.FOLLOW_SYSTEM;
11
+
12
+ function getTheme(colorScheme?: 'dark' | 'light' | null) {
13
+ const isDark = colorScheme === 'dark';
14
+ const theme = Colors[colorScheme ?? 'light'];
15
+
16
+ return {
17
+ theme,
18
+ isDark,
19
+ };
20
+ }
21
+
22
+ function useTheme() {
23
+ const systemColorScheme = useColorScheme()
24
+ const colorScheme = userPreferColorScheme === COLOR_SCHEME_CHOICE.FOLLOW_SYSTEM ?
25
+ systemColorScheme :
26
+ userPreferColorScheme;
27
+
28
+ return getTheme(colorScheme);
29
+ }
30
+
31
+ export {
32
+ useTheme,
33
+ }
@@ -1,7 +1,6 @@
1
1
  const { getDefaultConfig } = require('expo/metro-config');
2
2
  const { createProxyMiddleware } = require('http-proxy-middleware');
3
3
  const connect = require('connect');
4
- const { withUniwindConfig } = require('uniwind/metro');
5
4
 
6
5
  const config = getDefaultConfig(__dirname);
7
6
 
@@ -27,6 +26,9 @@ config.resolver.blockList = [
27
26
  // 4. 通用规则
28
27
  /.*\/__tests__\/.*/, // 排除所有测试目录
29
28
  /.*\.git\/.*/, // 排除 Git 目录
29
+
30
+ // 5. pnpm 临时目录(避免 ENOENT 错误)
31
+ /.*node_modules\/\.pnpm\/.*_tmp_\d+.*/,
30
32
  ];
31
33
 
32
34
  const BACKEND_TARGET = 'http://localhost:9091';
@@ -119,10 +121,4 @@ config.server = {
119
121
  },
120
122
  };
121
123
 
122
- module.exports = withUniwindConfig(config, {
123
- // relative path to your global.css file (from previous step)
124
- cssEntryFile: './global.css',
125
- // (optional) path where we gonna auto-generate typings
126
- // defaults to project's root
127
- dtsFile: './uniwind-types.d.ts'
128
- });
124
+ module.exports = config;
@@ -15,35 +15,35 @@
15
15
  "preset": "jest-expo"
16
16
  },
17
17
  "dependencies": {
18
- "@expo/metro-runtime": "^6.1.2",
19
- "@expo/vector-icons": "^15.0.0",
20
- "@react-native-async-storage/async-storage": "^2.2.0",
21
- "@react-native-community/datetimepicker": "^8.5.0",
22
- "@react-native-community/slider": "^5.0.1",
23
- "@react-native-masked-view/masked-view": "^0.3.2",
24
- "@react-native-picker/picker": "^2.11.0",
18
+ "@expo/metro-runtime": "~6.1.2",
19
+ "@expo/vector-icons": "^15.0.3",
20
+ "@react-native-async-storage/async-storage": "2.2.0",
21
+ "@react-native-community/datetimepicker": "8.4.4",
22
+ "@react-native-community/slider": "5.0.1",
23
+ "@react-native-masked-view/masked-view": "0.3.2",
24
+ "@react-native-picker/picker": "2.11.1",
25
25
  "@react-navigation/bottom-tabs": "^7.2.0",
26
26
  "@react-navigation/native": "^7.0.14",
27
27
  "dayjs": "^1.11.19",
28
- "expo": "54.0.32",
29
- "expo-auth-session": "^7.0.9",
30
- "expo-av": "~16.0.6",
31
- "expo-blur": "~15.0.6",
28
+ "expo": "54.0.33",
29
+ "expo-auth-session": "~7.0.10",
30
+ "expo-av": "~16.0.8",
31
+ "expo-blur": "~15.0.8",
32
32
  "expo-camera": "~17.0.10",
33
- "expo-constants": "~18.0.8",
34
- "expo-crypto": "^15.0.7",
33
+ "expo-constants": "~18.0.13",
34
+ "expo-crypto": "~15.0.8",
35
35
  "expo-file-system": "~19.0.21",
36
- "expo-font": "~14.0.7",
37
- "expo-haptics": "~15.0.6",
38
- "expo-image": "^3.0.11",
39
- "expo-image-picker": "~17.0.7",
40
- "expo-linear-gradient": "~15.0.6",
41
- "expo-linking": "~8.0.7",
42
- "expo-location": "~19.0.7",
43
- "expo-router": "~6.0.0",
44
- "expo-splash-screen": "~31.0.8",
45
- "expo-status-bar": "~3.0.7",
46
- "expo-symbols": "~1.0.6",
36
+ "expo-font": "~14.0.11",
37
+ "expo-haptics": "~15.0.8",
38
+ "expo-image": "~3.0.11",
39
+ "expo-image-picker": "~17.0.10",
40
+ "expo-linear-gradient": "~15.0.8",
41
+ "expo-linking": "~8.0.11",
42
+ "expo-location": "~19.0.8",
43
+ "expo-router": "~6.0.23",
44
+ "expo-splash-screen": "~31.0.13",
45
+ "expo-status-bar": "~3.0.9",
46
+ "expo-symbols": "~1.0.8",
47
47
  "expo-system-ui": "~6.0.9",
48
48
  "expo-web-browser": "~15.0.10",
49
49
  "js-base64": "^3.7.7",
@@ -54,16 +54,14 @@
54
54
  "react-native-gesture-handler": "~2.28.0",
55
55
  "react-native-keyboard-aware-scroll-view": "^0.9.5",
56
56
  "react-native-modal-datetime-picker": "18.0.0",
57
- "react-native-reanimated": "~4.1.0",
57
+ "react-native-reanimated": "~4.1.1",
58
58
  "react-native-safe-area-context": "~5.6.0",
59
59
  "react-native-screens": "~4.16.0",
60
- "react-native-svg": "15.15.0",
60
+ "react-native-svg": "15.12.1",
61
61
  "react-native-toast-message": "^2.3.3",
62
- "react-native-web": "^0.21.2",
63
- "react-native-webview": "~13.15.0",
62
+ "react-native-web": "~0.21.0",
63
+ "react-native-webview": "13.15.0",
64
64
  "react-native-worklets": "0.5.1",
65
- "tailwindcss": "^4.1.18",
66
- "uniwind": "^1.2.7",
67
65
  "zod": "^4.2.1"
68
66
  },
69
67
  "devDependencies": {
@@ -88,7 +86,7 @@
88
86
  "globals": "^16.1.0",
89
87
  "http-proxy-middleware": "^3.0.5",
90
88
  "jest": "^29.2.1",
91
- "jest-expo": "~54.0.10",
89
+ "jest-expo": "~54.0.17",
92
90
  "react-test-renderer": "19.1.0",
93
91
  "tsx": "^4.21.0",
94
92
  "typescript": "^5.8.3",