@lobehub/lobehub 2.0.0-next.90 → 2.0.0-next.91

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.
@@ -1,8 +1,10 @@
1
1
  'use client';
2
2
 
3
+ import dynamic from 'next/dynamic';
3
4
  import { useEffect } from 'react';
4
- import { type LoaderFunction, createBrowserRouter, redirect, useNavigate } from 'react-router-dom';
5
+ import { createBrowserRouter, redirect, useNavigate, useRouteError } from 'react-router-dom';
5
6
 
7
+ import ErrorCapture from '@/components/Error';
6
8
  import Loading from '@/components/Loading/BrandTextLoading';
7
9
  import { useGlobalStore } from '@/store/global';
8
10
  import type { Locales } from '@/types/locale';
@@ -10,6 +12,213 @@ import type { Locales } from '@/types/locale';
10
12
  import DesktopMainLayout from './(main)/layouts/desktop';
11
13
  import { idLoader, slugLoader } from './loaders/routeParams';
12
14
 
15
+ /**
16
+ * Desktop Router Configuration - Pure CSR Mode
17
+ *
18
+ * IMPORTANT: This router runs ONLY in the browser (client-side).
19
+ *
20
+ * Key characteristics:
21
+ * - createBrowserRouter uses window.history API (client-only)
22
+ * - All loaders execute in the browser during navigation
23
+ * - No server-side rendering or hydration involved
24
+ * - Route data fetching happens on-demand during client navigation
25
+ *
26
+ * The entire router tree is wrapped with Next.js dynamic import (ssr: false),
27
+ * ensuring this code never executes on the server.
28
+ */
29
+
30
+ // Chat components
31
+ const DesktopChatPage = dynamic(
32
+ () => import('./(main)/chat/index').then((m) => m.DesktopChatPage),
33
+ {
34
+ loading: () => <Loading />,
35
+ ssr: false,
36
+ },
37
+ );
38
+ const ChatLayout = dynamic(() => import('./(main)/chat/_layout/Desktop'), {
39
+ loading: () => <Loading />,
40
+ ssr: false,
41
+ });
42
+
43
+ // Discover List components
44
+ const DesktopHomePage = dynamic(
45
+ () => import('./(main)/discover/(list)/(home)/index').then((m) => m.DesktopHomePage),
46
+ {
47
+ loading: () => <Loading />,
48
+ ssr: false,
49
+ },
50
+ );
51
+ const DesktopAssistantPage = dynamic(
52
+ () => import('./(main)/discover/(list)/assistant/index').then((m) => m.DesktopAssistantPage),
53
+ {
54
+ loading: () => <Loading />,
55
+ ssr: false,
56
+ },
57
+ );
58
+ const DiscoverAssistantLayout = dynamic(
59
+ () => import('./(main)/discover/(list)/assistant/_layout/Desktop'),
60
+ {
61
+ loading: () => <Loading />,
62
+ ssr: false,
63
+ },
64
+ );
65
+ const DiscoverListMcpPage = dynamic(
66
+ () => import('./(main)/discover/(list)/mcp/index').then((m) => m.DesktopMcpPage),
67
+ {
68
+ loading: () => <Loading />,
69
+ ssr: false,
70
+ },
71
+ );
72
+ const DiscoverMcpLayout = dynamic(() => import('./(main)/discover/(list)/mcp/_layout/Desktop'), {
73
+ loading: () => <Loading />,
74
+ ssr: false,
75
+ });
76
+ const DiscoverListModelPage = dynamic(
77
+ () => import('./(main)/discover/(list)/model/index').then((m) => m.DesktopModelPage),
78
+ {
79
+ loading: () => <Loading />,
80
+ ssr: false,
81
+ },
82
+ );
83
+ const DiscoverModelLayout = dynamic(
84
+ () => import('./(main)/discover/(list)/model/_layout/Desktop'),
85
+ {
86
+ loading: () => <Loading />,
87
+ ssr: false,
88
+ },
89
+ );
90
+ const DiscoverListProviderPage = dynamic(
91
+ () => import('./(main)/discover/(list)/provider/index').then((m) => m.DesktopProviderPage),
92
+ {
93
+ loading: () => <Loading />,
94
+ ssr: false,
95
+ },
96
+ );
97
+ const DiscoverListLayout = dynamic(() => import('./(main)/discover/(list)/_layout/Desktop/index'), {
98
+ loading: () => <Loading />,
99
+ ssr: false,
100
+ });
101
+
102
+ // Discover Detail components
103
+ const DesktopDiscoverAssistantDetailPage = dynamic(
104
+ () =>
105
+ import('./(main)/discover/(detail)/assistant/index').then(
106
+ (m) => m.DesktopDiscoverAssistantDetailPage,
107
+ ),
108
+ {
109
+ loading: () => <Loading />,
110
+ ssr: false,
111
+ },
112
+ );
113
+ const DiscoverDetailMcpPage = dynamic(
114
+ () => import('./(main)/discover/(detail)/mcp/index').then((m) => m.DesktopMcpPage),
115
+ {
116
+ loading: () => <Loading />,
117
+ ssr: false,
118
+ },
119
+ );
120
+ const DiscoverDetailModelPage = dynamic(
121
+ () => import('./(main)/discover/(detail)/model/index').then((m) => m.DesktopModelPage),
122
+ {
123
+ loading: () => <Loading />,
124
+ ssr: false,
125
+ },
126
+ );
127
+ const DiscoverDetailProviderPage = dynamic(
128
+ () => import('./(main)/discover/(detail)/provider/index').then((m) => m.DesktopProviderPage),
129
+ {
130
+ loading: () => <Loading />,
131
+ ssr: false,
132
+ },
133
+ );
134
+ const DiscoverDetailLayout = dynamic(() => import('./(main)/discover/(detail)/_layout/Desktop'), {
135
+ loading: () => <Loading />,
136
+ ssr: false,
137
+ });
138
+ const DiscoverLayout = dynamic(() => import('./(main)/discover/_layout/Desktop/index'), {
139
+ loading: () => <Loading />,
140
+ ssr: false,
141
+ });
142
+
143
+ // Knowledge components
144
+ const KnowledgeHome = dynamic(() => import('./(main)/knowledge/routes/KnowledgeHome'), {
145
+ loading: () => <Loading />,
146
+ ssr: false,
147
+ });
148
+ const KnowledgeBasesList = dynamic(() => import('./(main)/knowledge/routes/KnowledgeBasesList'), {
149
+ loading: () => <Loading />,
150
+ ssr: false,
151
+ });
152
+ const KnowledgeBaseDetail = dynamic(() => import('./(main)/knowledge/routes/KnowledgeBaseDetail'), {
153
+ loading: () => <Loading />,
154
+ ssr: false,
155
+ });
156
+ const KnowledgeLayout = dynamic(() => import('./(main)/knowledge/_layout/Desktop'), {
157
+ loading: () => <Loading />,
158
+ ssr: false,
159
+ });
160
+
161
+ // Settings components
162
+ const SettingsLayout = dynamic(() => import('./(main)/settings/_layout/Desktop'), {
163
+ loading: () => <Loading />,
164
+ ssr: false,
165
+ });
166
+ const SettingsLayoutWrapper = dynamic(() => import('./(main)/settings/_layout/DesktopWrapper'), {
167
+ loading: () => <Loading />,
168
+ ssr: false,
169
+ });
170
+
171
+ // Image components
172
+ const ImagePage = dynamic(() => import('./(main)/image'), {
173
+ loading: () => <Loading />,
174
+ ssr: false,
175
+ });
176
+ const ImageLayoutWrapper = dynamic(() => import('./(main)/image/_layout/DesktopWrapper'), {
177
+ loading: () => <Loading />,
178
+ ssr: false,
179
+ });
180
+
181
+ // Labs components
182
+ const LabsPage = dynamic(() => import('./(main)/labs'), {
183
+ loading: () => <Loading />,
184
+ ssr: false,
185
+ });
186
+
187
+ // Profile components
188
+ const ProfileHomePage = dynamic(() => import('./(main)/profile/(home)/desktop'), {
189
+ loading: () => <Loading />,
190
+ ssr: false,
191
+ });
192
+ const ProfileApikeyPage = dynamic(() => import('./(main)/profile/apikey/index'), {
193
+ loading: () => <Loading />,
194
+ ssr: false,
195
+ });
196
+ const DesktopProfileSecurityPage = dynamic(
197
+ () => import('./(main)/profile/security/index').then((m) => m.DesktopProfileSecurityPage),
198
+ {
199
+ loading: () => <Loading />,
200
+ ssr: false,
201
+ },
202
+ );
203
+ const DesktopProfileStatsPage = dynamic(
204
+ () => import('./(main)/profile/stats/index').then((m) => m.DesktopProfileStatsPage),
205
+ {
206
+ loading: () => <Loading />,
207
+ ssr: false,
208
+ },
209
+ );
210
+ const DesktopProfileUsagePage = dynamic(
211
+ () => import('./(main)/profile/usage/index').then((m) => m.DesktopProfileUsagePage),
212
+ {
213
+ loading: () => <Loading />,
214
+ ssr: false,
215
+ },
216
+ );
217
+ const ProfileLayoutWrapper = dynamic(() => import('./(main)/profile/_layout/DesktopWrapper'), {
218
+ loading: () => <Loading />,
219
+ ssr: false,
220
+ });
221
+
13
222
  // Component to register navigate function in global store
14
223
  const NavigatorRegistrar = () => {
15
224
  const navigate = useNavigate();
@@ -25,50 +234,57 @@ const NavigatorRegistrar = () => {
25
234
  return null;
26
235
  };
27
236
 
28
- // Root layout wrapper component - just registers navigator and renders outlet
29
- // Note: Desktop layout is provided by individual route components
30
- const RootLayout = (props: { locale: Locales }) => {
31
- return (
32
- <>
33
- <NavigatorRegistrar />
34
- <DesktopMainLayout locale={props.locale} />
35
- </>
36
- );
37
- };
237
+ // Error boundary factory for React Router errorElement
238
+ const createErrorBoundary = (resetPath: string) => {
239
+ const ErrorBoundary = () => {
240
+ const error = useRouteError() as Error;
241
+ const navigate = useNavigate();
38
242
 
39
- // Hydration gate loader -always return true to bypass hydration gate
40
- const hydrationGateLoader: LoaderFunction = () => {
41
- return true
243
+ const reset = () => {
244
+ navigate(resetPath);
245
+ };
246
+
247
+ return <ErrorCapture error={error} reset={reset} />;
248
+ };
249
+ return ErrorBoundary;
42
250
  };
43
251
 
252
+ // Create error boundaries for each route
253
+ const ChatErrorBoundary = createErrorBoundary('/chat');
254
+ const DiscoverErrorBoundary = createErrorBoundary('/discover');
255
+ const KnowledgeErrorBoundary = createErrorBoundary('/knowledge');
256
+ const SettingsErrorBoundary = createErrorBoundary('/settings');
257
+ const ImageErrorBoundary = createErrorBoundary('/image');
258
+ const ProfileErrorBoundary = createErrorBoundary('/profile');
259
+ const RootErrorBoundary = createErrorBoundary('/chat'); // Root level falls back to chat
260
+
261
+ // Root layout wrapper component
262
+ const RootLayout = (props: { locale: Locales }) => (
263
+ <>
264
+ <NavigatorRegistrar />
265
+ <DesktopMainLayout locale={props.locale} />
266
+ </>
267
+ );
268
+
44
269
  // Create desktop router configuration
45
270
  export const createDesktopRouter = (locale: Locales) =>
46
271
  createBrowserRouter([
47
272
  {
48
- HydrateFallback: () => <Loading />,
49
273
  children: [
50
274
  // Chat routes
51
275
  {
52
276
  children: [
53
277
  {
278
+ element: <DesktopChatPage />,
54
279
  index: true,
55
- lazy: () =>
56
- import('./(main)/chat/index').then((m) => ({
57
- Component: m.DesktopChatPage,
58
- })),
59
280
  },
60
281
  {
61
- lazy: () =>
62
- import('./(main)/chat/index').then((m) => ({
63
- Component: m.DesktopChatPage,
64
- })),
282
+ element: <DesktopChatPage />,
65
283
  path: '*',
66
284
  },
67
285
  ],
68
- lazy: () =>
69
- import('./(main)/chat/_layout/Desktop').then((m) => ({
70
- Component: m.default,
71
- })),
286
+ element: <ChatLayout />,
287
+ errorElement: <ChatErrorBoundary />,
72
288
  path: 'chat',
73
289
  },
74
290
 
@@ -81,117 +297,73 @@ export const createDesktopRouter = (locale: Locales) =>
81
297
  {
82
298
  children: [
83
299
  {
300
+ element: <DesktopAssistantPage />,
84
301
  index: true,
85
- lazy: () =>
86
- import('./(main)/discover/(list)/assistant/index').then((m) => ({
87
- Component: m.DesktopAssistantPage,
88
- })),
89
302
  },
90
303
  ],
91
- lazy: () =>
92
- import('./(main)/discover/(list)/assistant/_layout/Desktop').then((m) => ({
93
- Component: m.default,
94
- })),
304
+ element: <DiscoverAssistantLayout />,
95
305
  path: 'assistant',
96
306
  },
97
307
  {
98
308
  children: [
99
309
  {
310
+ element: <DiscoverListModelPage />,
100
311
  index: true,
101
- lazy: () =>
102
- import('./(main)/discover/(list)/model/index').then((m) => ({
103
- Component: m.DesktopModelPage,
104
- })),
105
312
  },
106
313
  ],
107
- lazy: () =>
108
- import('./(main)/discover/(list)/model/_layout/Desktop').then((m) => ({
109
- Component: m.default,
110
- })),
314
+ element: <DiscoverModelLayout />,
111
315
  path: 'model',
112
316
  },
113
317
  {
114
- lazy: () =>
115
- import('./(main)/discover/(list)/provider/index').then((m) => ({
116
- Component: m.DesktopProviderPage,
117
- })),
318
+ element: <DiscoverListProviderPage />,
118
319
  path: 'provider',
119
320
  },
120
321
  {
121
322
  children: [
122
323
  {
324
+ element: <DiscoverListMcpPage />,
123
325
  index: true,
124
- lazy: () =>
125
- import('./(main)/discover/(list)/mcp/index').then((m) => ({
126
- Component: m.DesktopMcpPage,
127
- })),
128
326
  },
129
327
  ],
130
- lazy: () =>
131
- import('./(main)/discover/(list)/mcp/_layout/Desktop').then((m) => ({
132
- Component: m.default,
133
- })),
328
+ element: <DiscoverMcpLayout />,
134
329
  path: 'mcp',
135
330
  },
136
331
  {
332
+ element: <DesktopHomePage />,
137
333
  index: true,
138
- lazy: () =>
139
- import('./(main)/discover/(list)/(home)/index').then((m) => ({
140
- Component: m.DesktopHomePage,
141
- })),
142
334
  },
143
335
  ],
144
- lazy: () =>
145
- import('./(main)/discover/(list)/_layout/Desktop/index').then((m) => ({
146
- Component: m.default,
147
- })),
336
+ element: <DiscoverListLayout />,
148
337
  },
149
338
  // Detail routes (with DetailLayout)
150
339
  {
151
340
  children: [
152
341
  {
153
- lazy: () =>
154
- import('./(main)/discover/(detail)/assistant/index').then((m) => ({
155
- Component: m.DesktopDiscoverAssistantDetailPage,
156
- })),
342
+ element: <DesktopDiscoverAssistantDetailPage />,
157
343
  loader: slugLoader,
158
344
  path: 'assistant/:slug',
159
345
  },
160
346
  {
161
- lazy: () =>
162
- import('./(main)/discover/(detail)/model/index').then((m) => ({
163
- Component: m.DesktopModelPage,
164
- })),
347
+ element: <DiscoverDetailModelPage />,
165
348
  loader: slugLoader,
166
349
  path: 'model/:slug',
167
350
  },
168
351
  {
169
- lazy: () =>
170
- import('./(main)/discover/(detail)/provider/index').then((m) => ({
171
- Component: m.DesktopProviderPage,
172
- })),
352
+ element: <DiscoverDetailProviderPage />,
173
353
  loader: slugLoader,
174
354
  path: 'provider/:slug',
175
355
  },
176
356
  {
177
- lazy: () =>
178
- import('./(main)/discover/(detail)/mcp/index').then((m) => ({
179
- Component: m.DesktopMcpPage,
180
- })),
357
+ element: <DiscoverDetailMcpPage />,
181
358
  loader: slugLoader,
182
359
  path: 'mcp/:slug',
183
360
  },
184
361
  ],
185
- lazy: () =>
186
- import('./(main)/discover/(detail)/_layout/Desktop').then((m) => ({
187
- Component: m.default,
188
- })),
362
+ element: <DiscoverDetailLayout />,
189
363
  },
190
364
  ],
191
- lazy: () =>
192
- import('./(main)/discover/_layout/Desktop/index').then((m) => ({
193
- Component: m.default,
194
- })),
365
+ element: <DiscoverLayout />,
366
+ errorElement: <DiscoverErrorBoundary />,
195
367
  path: 'discover',
196
368
  },
197
369
 
@@ -199,40 +371,26 @@ export const createDesktopRouter = (locale: Locales) =>
199
371
  {
200
372
  children: [
201
373
  {
374
+ element: <KnowledgeHome />,
202
375
  index: true,
203
- lazy: () =>
204
- import('./(main)/knowledge/routes/KnowledgeHome').then((m) => ({
205
- Component: m.default,
206
- })),
207
376
  },
208
377
  {
209
- lazy: () =>
210
- import('./(main)/knowledge/routes/KnowledgeBasesList').then((m) => ({
211
- Component: m.default,
212
- })),
378
+ element: <KnowledgeBasesList />,
213
379
  path: 'bases',
214
380
  },
215
381
  {
216
- lazy: () =>
217
- import('./(main)/knowledge/routes/KnowledgeBaseDetail').then((m) => ({
218
- Component: m.default,
219
- })),
382
+ element: <KnowledgeBaseDetail />,
220
383
  loader: idLoader,
221
384
  path: 'bases/:id',
222
385
  },
223
386
  {
224
- lazy: () =>
225
- import('./(main)/knowledge/routes/KnowledgeBaseDetail').then((m) => ({
226
- Component: m.default,
227
- })),
387
+ element: <KnowledgeBaseDetail />,
228
388
  loader: idLoader,
229
389
  path: '*',
230
390
  },
231
391
  ],
232
- lazy: () =>
233
- import('./(main)/knowledge/_layout/Desktop').then((m) => ({
234
- Component: m.default,
235
- })),
392
+ element: <KnowledgeLayout />,
393
+ errorElement: <KnowledgeErrorBoundary />,
236
394
  path: 'knowledge',
237
395
  },
238
396
 
@@ -240,17 +398,12 @@ export const createDesktopRouter = (locale: Locales) =>
240
398
  {
241
399
  children: [
242
400
  {
401
+ element: <SettingsLayout />,
243
402
  index: true,
244
- lazy: () =>
245
- import('./(main)/settings/_layout/Desktop').then((m) => ({
246
- Component: m.default,
247
- })),
248
403
  },
249
404
  ],
250
- lazy: () =>
251
- import('./(main)/settings/_layout/DesktopWrapper').then((m) => ({
252
- Component: m.default,
253
- })),
405
+ element: <SettingsLayoutWrapper />,
406
+ errorElement: <SettingsErrorBoundary />,
254
407
  path: 'settings',
255
408
  },
256
409
 
@@ -258,26 +411,18 @@ export const createDesktopRouter = (locale: Locales) =>
258
411
  {
259
412
  children: [
260
413
  {
414
+ element: <ImagePage />,
261
415
  index: true,
262
- lazy: () =>
263
- import('./(main)/image').then((m) => ({
264
- Component: m.default,
265
- })),
266
416
  },
267
417
  ],
268
- lazy: () =>
269
- import('./(main)/image/_layout/DesktopWrapper').then((m) => ({
270
- Component: m.default,
271
- })),
418
+ element: <ImageLayoutWrapper />,
419
+ errorElement: <ImageErrorBoundary />,
272
420
  path: 'image',
273
421
  },
274
422
 
275
423
  // Labs routes
276
424
  {
277
- lazy: () =>
278
- import('./(main)/labs').then((m) => ({
279
- Component: m.default,
280
- })),
425
+ element: <LabsPage />,
281
426
  path: 'labs',
282
427
  },
283
428
 
@@ -285,45 +430,28 @@ export const createDesktopRouter = (locale: Locales) =>
285
430
  {
286
431
  children: [
287
432
  {
433
+ element: <ProfileHomePage />,
288
434
  index: true,
289
- lazy: () =>
290
- import('./(main)/profile/(home)/desktop').then((m) => ({
291
- Component: m.default,
292
- })),
293
435
  },
294
436
  {
295
- lazy: () =>
296
- import('./(main)/profile/apikey/index').then((m) => ({
297
- Component: m.default,
298
- })),
437
+ element: <ProfileApikeyPage />,
299
438
  path: 'apikey',
300
439
  },
301
440
  {
302
- lazy: () =>
303
- import('./(main)/profile/security/index').then((m) => ({
304
- Component: m.DesktopProfileSecurityPage,
305
- })),
441
+ element: <DesktopProfileSecurityPage />,
306
442
  path: 'security',
307
443
  },
308
444
  {
309
- lazy: () =>
310
- import('./(main)/profile/stats/index').then((m) => ({
311
- Component: m.DesktopProfileStatsPage,
312
- })),
445
+ element: <DesktopProfileStatsPage />,
313
446
  path: 'stats',
314
447
  },
315
448
  {
316
- lazy: () =>
317
- import('./(main)/profile/usage/index').then((m) => ({
318
- Component: m.DesktopProfileUsagePage,
319
- })),
449
+ element: <DesktopProfileUsagePage />,
320
450
  path: 'usage',
321
451
  },
322
452
  ],
323
- lazy: () =>
324
- import('./(main)/profile/_layout/DesktopWrapper').then((m) => ({
325
- Component: m.default,
326
- })),
453
+ element: <ProfileLayoutWrapper />,
454
+ errorElement: <ProfileErrorBoundary />,
327
455
  path: 'profile',
328
456
  },
329
457
 
@@ -340,7 +468,7 @@ export const createDesktopRouter = (locale: Locales) =>
340
468
  },
341
469
  ],
342
470
  element: <RootLayout locale={locale} />,
343
- loader: hydrationGateLoader,
471
+ errorElement: <RootErrorBoundary />,
344
472
  path: '/',
345
473
  },
346
474
  ]);