@coze-arch/cli 0.0.6 → 0.0.7
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/lib/__templates__/taro/eslint.config.mjs +52 -2
- package/lib/__templates__/taro/src/app.tsx +2 -0
- package/lib/__templates__/taro/src/components/ui/carousel.tsx +4 -4
- package/lib/__templates__/taro/src/components/ui/sheet.tsx +1 -1
- package/lib/__templates__/taro/src/components/ui/toast.tsx +1 -1
- package/lib/__templates__/taro/src/presets/env.ts +1 -0
- package/lib/__templates__/taro/src/presets/h5-container.tsx +2 -1
- package/lib/__templates__/taro/src/presets/h5-error-boundary.tsx +391 -0
- package/lib/__templates__/taro/src/presets/h5-navbar.tsx +4 -3
- package/lib/__templates__/taro/src/presets/h5-styles.ts +3 -1
- package/lib/__templates__/taro/src/presets/index.tsx +15 -2
- package/lib/cli.js +1 -1
- package/package.json +1 -1
- package/lib/__templates__/taro/components.md +0 -1686
|
@@ -123,14 +123,64 @@ export default [
|
|
|
123
123
|
},
|
|
124
124
|
},
|
|
125
125
|
{
|
|
126
|
-
files: ['src/pages/
|
|
126
|
+
files: ['src/pages/index/index.tsx'],
|
|
127
127
|
rules: {
|
|
128
128
|
'no-restricted-syntax': [
|
|
129
129
|
'error',
|
|
130
130
|
{
|
|
131
131
|
selector: 'JSXText[value=/应用开发中/]',
|
|
132
132
|
message:
|
|
133
|
-
'工程规范:检测到首页 (src/pages/
|
|
133
|
+
'工程规范:检测到首页 (src/pages/index/index.tsx) 仍为默认占位页面,这会导致用户无法进入新增页面,请根据用户需求开发实际的首页功能 and 界面。如果已经开发了新的首页,也需要删除旧首页,并更新 src/app.config.ts 文件',
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
files: ['src/pages/**/*.tsx'],
|
|
140
|
+
rules: {
|
|
141
|
+
'no-restricted-syntax': [
|
|
142
|
+
'error',
|
|
143
|
+
{
|
|
144
|
+
selector:
|
|
145
|
+
"ImportDeclaration[source.value='@tarojs/components'] ImportSpecifier[imported.name='Button']",
|
|
146
|
+
message:
|
|
147
|
+
"组件规范:Button 优先使用 '@/components/ui/button',不要在页面中直接使用 '@tarojs/components' 的 Button。",
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
selector:
|
|
151
|
+
"ImportDeclaration[source.value='@tarojs/components'] ImportSpecifier[imported.name='Input']",
|
|
152
|
+
message:
|
|
153
|
+
"组件规范:Input 优先使用 '@/components/ui/input',不要在页面中直接使用 '@tarojs/components' 的 Input。",
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
selector:
|
|
157
|
+
"ImportDeclaration[source.value='@tarojs/components'] ImportSpecifier[imported.name='Textarea']",
|
|
158
|
+
message:
|
|
159
|
+
"组件规范:Textarea 优先使用 '@/components/ui/textarea',不要在页面中直接使用 '@tarojs/components' 的 Textarea。",
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
selector:
|
|
163
|
+
"ImportDeclaration[source.value='@tarojs/components'] ImportSpecifier[imported.name='Label']",
|
|
164
|
+
message:
|
|
165
|
+
"组件规范:Label 优先使用 '@/components/ui/label',不要在页面中直接使用 '@tarojs/components' 的 Label。",
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
selector:
|
|
169
|
+
"ImportDeclaration[source.value='@tarojs/components'] ImportSpecifier[imported.name='Switch']",
|
|
170
|
+
message:
|
|
171
|
+
"组件规范:Switch 优先使用 '@/components/ui/switch',不要在页面中直接使用 '@tarojs/components' 的 Switch。",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
selector:
|
|
175
|
+
"ImportDeclaration[source.value='@tarojs/components'] ImportSpecifier[imported.name='Slider']",
|
|
176
|
+
message:
|
|
177
|
+
"组件规范:Slider 优先使用 '@/components/ui/slider',不要在页面中直接使用 '@tarojs/components' 的 Slider。",
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
selector:
|
|
181
|
+
"ImportDeclaration[source.value='@tarojs/components'] ImportSpecifier[imported.name='Progress']",
|
|
182
|
+
message:
|
|
183
|
+
"组件规范:Progress 优先使用 '@/components/ui/progress',不要在页面中直接使用 '@tarojs/components' 的 Progress。",
|
|
134
184
|
},
|
|
135
185
|
],
|
|
136
186
|
},
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { PropsWithChildren } from 'react';
|
|
2
2
|
import { LucideTaroProvider } from 'lucide-react-taro';
|
|
3
3
|
import '@/app.css';
|
|
4
|
+
import { Toaster } from '@/components/ui/toast';
|
|
4
5
|
import { Preset } from './presets';
|
|
5
6
|
|
|
6
7
|
const App = ({ children }: PropsWithChildren) => {
|
|
7
8
|
return (
|
|
8
9
|
<LucideTaroProvider defaultColor="#000" defaultSize={24}>
|
|
9
10
|
<Preset>{children}</Preset>
|
|
11
|
+
<Toaster />
|
|
10
12
|
</LucideTaroProvider>
|
|
11
13
|
);
|
|
12
14
|
};
|
|
@@ -175,8 +175,8 @@ const CarouselPrevious = React.forwardRef<
|
|
|
175
175
|
className={cn(
|
|
176
176
|
"absolute h-8 w-8 rounded-full z-10 bg-background bg-opacity-80 backdrop-blur-sm",
|
|
177
177
|
orientation === "horizontal"
|
|
178
|
-
? "-left-12 top-
|
|
179
|
-
: "-top-12 left-
|
|
178
|
+
? "-left-12 top-0 bottom-0 my-auto"
|
|
179
|
+
: "-top-12 left-0 right-0 mx-auto rotate-90",
|
|
180
180
|
className
|
|
181
181
|
)}
|
|
182
182
|
disabled={!canScrollPrev}
|
|
@@ -204,8 +204,8 @@ const CarouselNext = React.forwardRef<
|
|
|
204
204
|
className={cn(
|
|
205
205
|
"absolute h-8 w-8 rounded-full z-10 bg-background bg-opacity-80 backdrop-blur-sm",
|
|
206
206
|
orientation === "horizontal"
|
|
207
|
-
? "-right-12 top-
|
|
208
|
-
: "-bottom-12 left-
|
|
207
|
+
? "-right-12 top-0 bottom-0 my-auto"
|
|
208
|
+
: "-bottom-12 left-0 right-0 mx-auto rotate-90",
|
|
209
209
|
className
|
|
210
210
|
)}
|
|
211
211
|
disabled={!canScrollNext}
|
|
@@ -179,7 +179,7 @@ const SheetContent = React.forwardRef<
|
|
|
179
179
|
>
|
|
180
180
|
{children}
|
|
181
181
|
<View
|
|
182
|
-
className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"
|
|
182
|
+
className="absolute right-4 top-4 flex h-8 w-8 items-center justify-center rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"
|
|
183
183
|
data-state={state}
|
|
184
184
|
onClick={(e) => {
|
|
185
185
|
e.stopPropagation()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const IS_H5_ENV = TARO_ENV === 'h5';
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { PropsWithChildren } from 'react';
|
|
2
|
+
import { IS_H5_ENV } from './env';
|
|
2
3
|
import { H5NavBar } from './h5-navbar';
|
|
3
4
|
|
|
4
5
|
export const H5Container = ({ children }: PropsWithChildren) => {
|
|
5
|
-
if (
|
|
6
|
+
if (!IS_H5_ENV) {
|
|
6
7
|
return <>{children}</>;
|
|
7
8
|
}
|
|
8
9
|
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { View } from '@tarojs/components';
|
|
2
|
+
import { Component, PropsWithChildren, useSyncExternalStore } from 'react';
|
|
3
|
+
import { CircleAlert, Copy, RefreshCw, X } from 'lucide-react-taro';
|
|
4
|
+
import { Badge } from '@/components/ui/badge';
|
|
5
|
+
import { Button } from '@/components/ui/button';
|
|
6
|
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
7
|
+
import { Portal } from '@/components/ui/portal';
|
|
8
|
+
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
9
|
+
import { toast } from '@/components/ui/toast';
|
|
10
|
+
import { cn } from '@/lib/utils';
|
|
11
|
+
import { IS_H5_ENV } from './env';
|
|
12
|
+
|
|
13
|
+
type ErrorState = {
|
|
14
|
+
error: Error | null;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type ErrorBoundaryProps = PropsWithChildren;
|
|
18
|
+
|
|
19
|
+
type ErrorReportOptions = {
|
|
20
|
+
componentStack?: string;
|
|
21
|
+
source?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type OverlayStore = {
|
|
25
|
+
error: Error | null;
|
|
26
|
+
report: string;
|
|
27
|
+
source: string;
|
|
28
|
+
visible: boolean;
|
|
29
|
+
open: boolean;
|
|
30
|
+
timestamp: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const EMPTY_STORE: OverlayStore = {
|
|
34
|
+
error: null,
|
|
35
|
+
report: '',
|
|
36
|
+
source: '',
|
|
37
|
+
visible: false,
|
|
38
|
+
open: false,
|
|
39
|
+
timestamp: '',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const ERROR_ACCENT_COLOR = 'hsl(360, 100%, 45%)';
|
|
43
|
+
|
|
44
|
+
let handlersInstalled = false;
|
|
45
|
+
let overlayStore: OverlayStore = EMPTY_STORE;
|
|
46
|
+
const listeners = new Set<() => void>();
|
|
47
|
+
|
|
48
|
+
const emitChange = () => {
|
|
49
|
+
listeners.forEach(listener => listener());
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const subscribe = (listener: () => void) => {
|
|
53
|
+
listeners.add(listener);
|
|
54
|
+
return () => listeners.delete(listener);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const getSnapshot = () => overlayStore;
|
|
58
|
+
|
|
59
|
+
const setOverlayStore = (nextStore: OverlayStore) => {
|
|
60
|
+
overlayStore = nextStore;
|
|
61
|
+
emitChange();
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const copyText = async (text: string) => {
|
|
65
|
+
if (typeof window === 'undefined') return false;
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
if (navigator.clipboard?.writeText) {
|
|
69
|
+
await navigator.clipboard.writeText(text);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.warn('[H5ErrorBoundary] Clipboard API copy failed:', error);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const textarea = document.createElement('textarea');
|
|
78
|
+
textarea.value = text;
|
|
79
|
+
textarea.setAttribute('readonly', 'true');
|
|
80
|
+
textarea.style.position = 'fixed';
|
|
81
|
+
textarea.style.opacity = '0';
|
|
82
|
+
document.body.appendChild(textarea);
|
|
83
|
+
textarea.select();
|
|
84
|
+
const copied = document.execCommand('copy');
|
|
85
|
+
document.body.removeChild(textarea);
|
|
86
|
+
return copied;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.warn('[H5ErrorBoundary] Fallback copy failed:', error);
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const normalizeError = (value: unknown) => {
|
|
94
|
+
if (value instanceof Error) {
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (typeof value === 'string') {
|
|
99
|
+
return new Error(value);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
return new Error(JSON.stringify(value));
|
|
104
|
+
} catch {
|
|
105
|
+
return new Error(String(value));
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const buildErrorReport = (error: Error, options: ErrorReportOptions = {}) => {
|
|
110
|
+
const lines = [
|
|
111
|
+
'[H5 Runtime Error]',
|
|
112
|
+
`Time: ${new Date().toISOString()}`,
|
|
113
|
+
options.source ? `Source: ${options.source}` : '',
|
|
114
|
+
`Name: ${error.name}`,
|
|
115
|
+
`Message: ${error.message}`,
|
|
116
|
+
error.stack ? `Stack:\n${error.stack}` : '',
|
|
117
|
+
options.componentStack ? `Component Stack:\n${options.componentStack}` : '',
|
|
118
|
+
typeof navigator !== 'undefined'
|
|
119
|
+
? `User Agent: ${navigator.userAgent}`
|
|
120
|
+
: '',
|
|
121
|
+
].filter(Boolean);
|
|
122
|
+
|
|
123
|
+
return lines.join('\n\n');
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const setPanelOpen = (open: boolean) => {
|
|
127
|
+
if (!overlayStore.visible) return;
|
|
128
|
+
|
|
129
|
+
setOverlayStore({
|
|
130
|
+
...overlayStore,
|
|
131
|
+
open,
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export const showH5ErrorOverlay = (
|
|
136
|
+
input: unknown,
|
|
137
|
+
options: ErrorReportOptions = {},
|
|
138
|
+
) => {
|
|
139
|
+
if (typeof window === 'undefined') {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const error = normalizeError(input);
|
|
144
|
+
const report = buildErrorReport(error, options);
|
|
145
|
+
const timestamp = new Date().toLocaleTimeString('zh-CN', {
|
|
146
|
+
hour: '2-digit',
|
|
147
|
+
minute: '2-digit',
|
|
148
|
+
second: '2-digit',
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
setOverlayStore({
|
|
152
|
+
error,
|
|
153
|
+
report,
|
|
154
|
+
source: options.source || 'runtime',
|
|
155
|
+
timestamp,
|
|
156
|
+
visible: true,
|
|
157
|
+
open: false,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
console.error('[H5ErrorOverlay] Showing error overlay:', error, options);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const handleWindowError = (event: ErrorEvent) => {
|
|
164
|
+
const error =
|
|
165
|
+
event.error || new Error(event.message || 'Unknown H5 runtime error');
|
|
166
|
+
showH5ErrorOverlay(error, { source: 'window.error' });
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const handleUnhandledRejection = (event: PromiseRejectionEvent) => {
|
|
170
|
+
showH5ErrorOverlay(event.reason, { source: 'window.unhandledrejection' });
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export const initializeH5ErrorHandling = () => {
|
|
174
|
+
if (!IS_H5_ENV || typeof window === 'undefined' || handlersInstalled) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
handlersInstalled = true;
|
|
179
|
+
window.addEventListener('error', handleWindowError);
|
|
180
|
+
window.addEventListener('unhandledrejection', handleUnhandledRejection);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const H5ErrorOverlayHost = () => {
|
|
184
|
+
const store = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
185
|
+
|
|
186
|
+
if (!IS_H5_ENV || !store.visible) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const errorName = store.error?.name || 'Error';
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<Portal>
|
|
194
|
+
<View className="pointer-events-none fixed inset-0 z-[2147483646]">
|
|
195
|
+
<View className="pointer-events-auto fixed bottom-5 left-5">
|
|
196
|
+
<Button
|
|
197
|
+
variant="outline"
|
|
198
|
+
size="icon"
|
|
199
|
+
className={cn(
|
|
200
|
+
'h-11 w-11 rounded-full shadow-md transition-transform',
|
|
201
|
+
)}
|
|
202
|
+
style={{
|
|
203
|
+
backgroundColor: 'hsl(359, 100%, 97%)',
|
|
204
|
+
borderColor: 'hsl(359, 100%, 94%)',
|
|
205
|
+
color: ERROR_ACCENT_COLOR,
|
|
206
|
+
}}
|
|
207
|
+
onClick={() => setPanelOpen(!store.open)}
|
|
208
|
+
>
|
|
209
|
+
<CircleAlert size={22} color={ERROR_ACCENT_COLOR} />
|
|
210
|
+
</Button>
|
|
211
|
+
</View>
|
|
212
|
+
|
|
213
|
+
{store.open && (
|
|
214
|
+
<View className="pointer-events-none fixed inset-0 bg-white bg-opacity-15 supports-[backdrop-filter]:backdrop-blur-md">
|
|
215
|
+
<View className="absolute inset-0 flex items-center justify-center px-4 py-4">
|
|
216
|
+
<View
|
|
217
|
+
className="w-full max-w-md"
|
|
218
|
+
style={{
|
|
219
|
+
width:
|
|
220
|
+
'min(calc(100vw - 32px), var(--h5-phone-width, 390px))',
|
|
221
|
+
height: 'min(calc(100vh - 32px), 900px)',
|
|
222
|
+
}}
|
|
223
|
+
>
|
|
224
|
+
<Card
|
|
225
|
+
className={cn(
|
|
226
|
+
'pointer-events-auto h-full rounded-2xl border border-border bg-background text-foreground shadow-2xl',
|
|
227
|
+
)}
|
|
228
|
+
>
|
|
229
|
+
<View className="relative flex h-full flex-col">
|
|
230
|
+
<CardHeader className="gap-2 p-4 pb-2">
|
|
231
|
+
<View className="flex items-start justify-between gap-3">
|
|
232
|
+
<View className="flex flex-wrap items-center gap-2">
|
|
233
|
+
<Badge
|
|
234
|
+
variant="destructive"
|
|
235
|
+
className="border-none bg-red-500 px-3 py-1 text-xs font-medium text-white"
|
|
236
|
+
>
|
|
237
|
+
Runtime Error
|
|
238
|
+
</Badge>
|
|
239
|
+
<Badge
|
|
240
|
+
variant="outline"
|
|
241
|
+
className="px-3 py-1 text-xs"
|
|
242
|
+
>
|
|
243
|
+
{store.source}
|
|
244
|
+
</Badge>
|
|
245
|
+
</View>
|
|
246
|
+
|
|
247
|
+
<View className="flex shrink-0 items-center gap-1">
|
|
248
|
+
<Button
|
|
249
|
+
variant="ghost"
|
|
250
|
+
size="icon"
|
|
251
|
+
className="h-8 w-8 rounded-full"
|
|
252
|
+
onClick={() => window.location.reload()}
|
|
253
|
+
>
|
|
254
|
+
<RefreshCw size={15} color="inherit" />
|
|
255
|
+
</Button>
|
|
256
|
+
<Button
|
|
257
|
+
variant="ghost"
|
|
258
|
+
size="icon"
|
|
259
|
+
className="h-8 w-8 rounded-full"
|
|
260
|
+
onClick={() => setPanelOpen(false)}
|
|
261
|
+
>
|
|
262
|
+
<X size={17} color="inherit" />
|
|
263
|
+
</Button>
|
|
264
|
+
</View>
|
|
265
|
+
</View>
|
|
266
|
+
|
|
267
|
+
<View className="flex items-center justify-between gap-3">
|
|
268
|
+
<CardTitle className="text-left text-lg">
|
|
269
|
+
{errorName}
|
|
270
|
+
</CardTitle>
|
|
271
|
+
|
|
272
|
+
<Button
|
|
273
|
+
variant="outline"
|
|
274
|
+
size="sm"
|
|
275
|
+
className="shrink-0 rounded-lg"
|
|
276
|
+
onClick={async () => {
|
|
277
|
+
const copied = await copyText(store.report);
|
|
278
|
+
if (copied) {
|
|
279
|
+
toast.success('已复制错误信息', {
|
|
280
|
+
description: '可发送给 Agent 进行自动修复',
|
|
281
|
+
position: 'top-center',
|
|
282
|
+
});
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
toast.warning('复制失败', {
|
|
287
|
+
description: '请直接选中文本后手动复制。',
|
|
288
|
+
position: 'top-center',
|
|
289
|
+
});
|
|
290
|
+
}}
|
|
291
|
+
>
|
|
292
|
+
<Copy size={15} color="inherit" />
|
|
293
|
+
<View>复制错误</View>
|
|
294
|
+
</Button>
|
|
295
|
+
</View>
|
|
296
|
+
</CardHeader>
|
|
297
|
+
|
|
298
|
+
<CardContent className="min-h-0 flex-1 overflow-hidden px-4 pb-4 pt-2">
|
|
299
|
+
<View className="flex h-full min-h-0 flex-col gap-2">
|
|
300
|
+
<View className="flex flex-wrap items-center gap-x-4 gap-y-2 rounded-lg border border-border px-3 py-2 text-sm">
|
|
301
|
+
<View className="flex items-center gap-2">
|
|
302
|
+
<View className="text-muted-foreground">Error</View>
|
|
303
|
+
<View className="font-medium text-foreground">
|
|
304
|
+
{store.error?.name || 'Error'}
|
|
305
|
+
</View>
|
|
306
|
+
</View>
|
|
307
|
+
<View className="h-4 w-px bg-border" />
|
|
308
|
+
<View className="flex items-center gap-2">
|
|
309
|
+
<View className="text-muted-foreground">
|
|
310
|
+
Source
|
|
311
|
+
</View>
|
|
312
|
+
<View className="font-medium text-foreground">
|
|
313
|
+
{store.source}
|
|
314
|
+
</View>
|
|
315
|
+
</View>
|
|
316
|
+
</View>
|
|
317
|
+
|
|
318
|
+
<View className="min-h-0 flex flex-1 flex-col overflow-hidden rounded-xl border border-border bg-black text-white">
|
|
319
|
+
<View className="flex items-center justify-between border-b border-white border-opacity-10 px-3 py-3">
|
|
320
|
+
<View className="text-xs font-medium uppercase tracking-wide text-zinc-400">
|
|
321
|
+
Full Report
|
|
322
|
+
</View>
|
|
323
|
+
<Badge
|
|
324
|
+
variant="outline"
|
|
325
|
+
className="border-zinc-700 bg-transparent px-2 py-1 text-xs text-zinc-400"
|
|
326
|
+
>
|
|
327
|
+
{store.timestamp}
|
|
328
|
+
</Badge>
|
|
329
|
+
</View>
|
|
330
|
+
|
|
331
|
+
<ScrollArea
|
|
332
|
+
className="min-h-0 flex-1 w-full"
|
|
333
|
+
orientation="both"
|
|
334
|
+
>
|
|
335
|
+
<View className="inline-block min-w-full whitespace-pre px-3 py-3 pb-8 font-mono text-xs leading-6 text-zinc-200">
|
|
336
|
+
{store.report}
|
|
337
|
+
</View>
|
|
338
|
+
</ScrollArea>
|
|
339
|
+
</View>
|
|
340
|
+
</View>
|
|
341
|
+
</CardContent>
|
|
342
|
+
</View>
|
|
343
|
+
</Card>
|
|
344
|
+
</View>
|
|
345
|
+
</View>
|
|
346
|
+
</View>
|
|
347
|
+
)}
|
|
348
|
+
</View>
|
|
349
|
+
</Portal>
|
|
350
|
+
);
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
class H5ErrorBoundaryInner extends Component<ErrorBoundaryProps, ErrorState> {
|
|
354
|
+
static getDerivedStateFromError(error: Error): Partial<ErrorState> {
|
|
355
|
+
return { error };
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
state: ErrorState = {
|
|
359
|
+
error: null,
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
componentDidUpdate(prevProps: ErrorBoundaryProps) {
|
|
363
|
+
if (this.state.error && prevProps.children !== this.props.children) {
|
|
364
|
+
this.setState({ error: null });
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
componentDidCatch(error: Error, info: { componentStack: string }) {
|
|
369
|
+
showH5ErrorOverlay(error, {
|
|
370
|
+
source: 'React Error Boundary',
|
|
371
|
+
componentStack: info.componentStack || '',
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
render() {
|
|
376
|
+
return (
|
|
377
|
+
<>
|
|
378
|
+
<H5ErrorOverlayHost />
|
|
379
|
+
{this.state.error ? null : this.props.children}
|
|
380
|
+
</>
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export const H5ErrorBoundary = ({ children }: PropsWithChildren) => {
|
|
386
|
+
if (!IS_H5_ENV) {
|
|
387
|
+
return <>{children}</>;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return <H5ErrorBoundaryInner>{children}</H5ErrorBoundaryInner>;
|
|
391
|
+
};
|
|
@@ -2,6 +2,7 @@ import { View, Text } from '@tarojs/components';
|
|
|
2
2
|
import Taro, { useDidShow, usePageScroll } from '@tarojs/taro';
|
|
3
3
|
import { useState, useEffect, useCallback } from 'react';
|
|
4
4
|
import { ChevronLeft, House } from 'lucide-react-taro';
|
|
5
|
+
import { IS_H5_ENV } from './env';
|
|
5
6
|
|
|
6
7
|
interface NavConfig {
|
|
7
8
|
navigationBarTitleText?: string;
|
|
@@ -144,7 +145,7 @@ export const H5NavBar = () => {
|
|
|
144
145
|
});
|
|
145
146
|
|
|
146
147
|
useEffect(() => {
|
|
147
|
-
if (
|
|
148
|
+
if (!IS_H5_ENV) return;
|
|
148
149
|
|
|
149
150
|
let timer: NodeJS.Timeout | null = null;
|
|
150
151
|
const observer = new MutationObserver(() => {
|
|
@@ -169,10 +170,10 @@ export const H5NavBar = () => {
|
|
|
169
170
|
}, [updateNavState]);
|
|
170
171
|
|
|
171
172
|
const shouldRender =
|
|
172
|
-
|
|
173
|
+
IS_H5_ENV && navState.visible && navState.navStyle !== 'custom';
|
|
173
174
|
|
|
174
175
|
useEffect(() => {
|
|
175
|
-
if (
|
|
176
|
+
if (!IS_H5_ENV) return;
|
|
176
177
|
if (shouldRender) {
|
|
177
178
|
document.body.classList.add('h5-navbar-visible');
|
|
178
179
|
} else {
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* 如无必要,请勿修改本文件
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { IS_H5_ENV } from './env';
|
|
7
|
+
|
|
6
8
|
const H5_BASE_STYLES = `
|
|
7
9
|
/* H5 端隐藏 TabBar 空图标(只隐藏没有 src 的图标) */
|
|
8
10
|
.weui-tabbar__icon:not([src]),
|
|
@@ -213,7 +215,7 @@ function setupTabbarDetection() {
|
|
|
213
215
|
}
|
|
214
216
|
|
|
215
217
|
export function injectH5Styles() {
|
|
216
|
-
if (
|
|
218
|
+
if (!IS_H5_ENV) return;
|
|
217
219
|
|
|
218
220
|
injectStyles();
|
|
219
221
|
setupTabbarDetection();
|
|
@@ -3,15 +3,28 @@ import { PropsWithChildren } from 'react';
|
|
|
3
3
|
import { injectH5Styles } from './h5-styles';
|
|
4
4
|
import { devDebug } from './dev-debug';
|
|
5
5
|
import { H5Container } from './h5-container';
|
|
6
|
+
import {
|
|
7
|
+
H5ErrorBoundary,
|
|
8
|
+
initializeH5ErrorHandling,
|
|
9
|
+
} from './h5-error-boundary';
|
|
10
|
+
import { IS_H5_ENV } from './env';
|
|
6
11
|
|
|
7
12
|
export const Preset = ({ children }: PropsWithChildren) => {
|
|
13
|
+
if (IS_H5_ENV) {
|
|
14
|
+
initializeH5ErrorHandling();
|
|
15
|
+
}
|
|
16
|
+
|
|
8
17
|
useLaunch(() => {
|
|
9
18
|
devDebug();
|
|
10
19
|
injectH5Styles();
|
|
11
20
|
});
|
|
12
21
|
|
|
13
|
-
if (
|
|
14
|
-
return
|
|
22
|
+
if (IS_H5_ENV) {
|
|
23
|
+
return (
|
|
24
|
+
<H5ErrorBoundary>
|
|
25
|
+
<H5Container>{children}</H5Container>
|
|
26
|
+
</H5ErrorBoundary>
|
|
27
|
+
);
|
|
15
28
|
}
|
|
16
29
|
|
|
17
30
|
return <>{children}</>;
|
package/lib/cli.js
CHANGED
|
@@ -2105,7 +2105,7 @@ const EventBuilder = {
|
|
|
2105
2105
|
};
|
|
2106
2106
|
|
|
2107
2107
|
var name = "@coze-arch/cli";
|
|
2108
|
-
var version = "0.0.
|
|
2108
|
+
var version = "0.0.7";
|
|
2109
2109
|
var description = "coze coding devtools cli";
|
|
2110
2110
|
var license = "MIT";
|
|
2111
2111
|
var author = "fanwenjie.fe@bytedance.com";
|