@lobehub/lobehub 2.0.0-next.209 → 2.0.0-next.210

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 (26) hide show
  1. package/.vscode/settings.json +2 -17
  2. package/CHANGELOG.md +25 -0
  3. package/apps/desktop/src/main/controllers/SystemCtr.ts +10 -0
  4. package/apps/desktop/src/main/core/App.ts +10 -188
  5. package/apps/desktop/src/main/core/__tests__/App.test.ts +6 -42
  6. package/apps/desktop/src/main/core/browser/Browser.ts +17 -9
  7. package/apps/desktop/src/main/core/infrastructure/RendererUrlManager.ts +126 -0
  8. package/apps/desktop/src/main/core/infrastructure/__tests__/RendererUrlManager.test.ts +72 -0
  9. package/changelog/v1.json +5 -0
  10. package/package.json +1 -1
  11. package/packages/builtin-tool-web-browsing/src/client/Inspector/Search/index.tsx +1 -1
  12. package/packages/desktop-bridge/src/index.ts +0 -2
  13. package/packages/desktop-bridge/src/routeVariants.ts +0 -2
  14. package/packages/electron-client-ipc/src/types/system.ts +1 -0
  15. package/packages/model-bank/src/aiModels/lobehub.ts +0 -3
  16. package/packages/model-runtime/src/core/streams/openai/openai.test.ts +167 -0
  17. package/packages/model-runtime/src/core/streams/openai/openai.ts +30 -6
  18. package/packages/model-runtime/src/core/streams/protocol.ts +5 -0
  19. package/packages/model-runtime/src/core/streams/qwen.test.ts +131 -2
  20. package/packages/model-runtime/src/core/streams/qwen.ts +9 -1
  21. package/scripts/electronWorkflow/modifiers/index.mts +2 -0
  22. package/scripts/electronWorkflow/modifiers/nextConfig.mts +1 -1
  23. package/scripts/electronWorkflow/modifiers/staticExport.mts +174 -0
  24. package/src/layout/GlobalProvider/Locale.tsx +1 -1
  25. package/src/store/electron/actions/app.ts +6 -0
  26. package/src/utils/server/routeVariants.ts +2 -2
@@ -0,0 +1,174 @@
1
+ import { Lang, parse } from '@ast-grep/napi';
2
+ import fs from 'fs-extra';
3
+ import path from 'node:path';
4
+
5
+ import { isDirectRun, runStandalone, updateFile } from './utils.mjs';
6
+
7
+ /**
8
+ * Remove the URL rewrite logic from the proxy middleware.
9
+ * For Electron static export, we don't need URL rewriting since pages are pre-rendered.
10
+ */
11
+ const removeUrlRewriteLogic = (code: string): string => {
12
+ const ast = parse(Lang.TypeScript, code);
13
+ const root = ast.root();
14
+ const edits: Array<{ end: number; start: number; text: string }> = [];
15
+
16
+ // Find the defaultMiddleware arrow function
17
+ const defaultMiddleware = root.find({
18
+ rule: {
19
+ pattern: 'const defaultMiddleware = ($REQ) => { $$$ }',
20
+ },
21
+ });
22
+
23
+ if (!defaultMiddleware) {
24
+ console.warn(' ⚠️ defaultMiddleware not found, skipping URL rewrite removal');
25
+ return code;
26
+ }
27
+
28
+ // Replace the entire defaultMiddleware function with a simplified version
29
+ // that just returns NextResponse.next() for non-API routes
30
+ const range = defaultMiddleware.range();
31
+
32
+ const simplifiedMiddleware = `const defaultMiddleware = (request: NextRequest) => {
33
+ const url = new URL(request.url);
34
+ logDefault('Processing request: %s %s', request.method, request.url);
35
+
36
+ // skip all api requests
37
+ if (backendApiEndpoints.some((path) => url.pathname.startsWith(path))) {
38
+ logDefault('Skipping API request: %s', url.pathname);
39
+ return NextResponse.next();
40
+ }
41
+
42
+ return NextResponse.next();
43
+ }`;
44
+
45
+ edits.push({ end: range.end.index, start: range.start.index, text: simplifiedMiddleware });
46
+
47
+ // Apply edits
48
+ if (edits.length === 0) return code;
49
+
50
+ edits.sort((a, b) => b.start - a.start);
51
+ let result = code;
52
+ for (const edit of edits) {
53
+ result = result.slice(0, edit.start) + edit.text + result.slice(edit.end);
54
+ }
55
+
56
+ return result;
57
+ };
58
+
59
+ const assertUrlRewriteRemoved = (code: string): boolean =>
60
+ // Ensure the URL rewrite related code is removed
61
+ !/NextResponse\.rewrite\(/.test(code) &&
62
+ !/RouteVariants\.serializeVariants/.test(code) &&
63
+ !/url\.pathname = nextPathname/.test(code);
64
+
65
+ /**
66
+ * Rename [variants] directories to (variants) under src/app
67
+ */
68
+ const renameVariantsDirectories = async (TEMP_DIR: string): Promise<void> => {
69
+ const srcAppPath = path.join(TEMP_DIR, 'src', 'app');
70
+
71
+ // Recursively find and rename [variants] directories
72
+ const renameRecursively = async (dir: string): Promise<void> => {
73
+ const entries = await fs.readdir(dir, { withFileTypes: true });
74
+
75
+ for (const entry of entries) {
76
+ if (entry.isDirectory()) {
77
+ const oldPath = path.join(dir, entry.name);
78
+
79
+ if (entry.name === '[variants]') {
80
+ const newPath = path.join(dir, '(variants)');
81
+
82
+ // If (variants) already exists, remove it first
83
+ if (await fs.pathExists(newPath)) {
84
+ console.log(` Removing existing: ${path.relative(TEMP_DIR, newPath)}`);
85
+ await fs.remove(newPath);
86
+ }
87
+
88
+ console.log(
89
+ ` Renaming: ${path.relative(TEMP_DIR, oldPath)} -> ${path.relative(TEMP_DIR, newPath)}`,
90
+ );
91
+ await fs.rename(oldPath, newPath);
92
+ // Continue searching in the renamed directory
93
+ await renameRecursively(newPath);
94
+ } else {
95
+ // Continue searching in subdirectories
96
+ await renameRecursively(oldPath);
97
+ }
98
+ }
99
+ }
100
+ };
101
+
102
+ await renameRecursively(srcAppPath);
103
+ };
104
+
105
+ /**
106
+ * Update all imports that reference [variants] to use (variants)
107
+ */
108
+ const updateVariantsImports = async (TEMP_DIR: string): Promise<void> => {
109
+ const srcPath = path.join(TEMP_DIR, 'src');
110
+
111
+ // Pattern to match imports containing [variants]
112
+ const variantsImportPattern = /(\[variants])/g;
113
+
114
+ const processFile = async (filePath: string): Promise<void> => {
115
+ const content = await fs.readFile(filePath, 'utf8');
116
+
117
+ if (!content.includes('[variants]')) {
118
+ return;
119
+ }
120
+
121
+ const updated = content.replaceAll('[variants]', '(variants)');
122
+
123
+ if (updated !== content) {
124
+ console.log(` Updated imports: ${path.relative(TEMP_DIR, filePath)}`);
125
+ await fs.writeFile(filePath, updated);
126
+ }
127
+ };
128
+
129
+ const processDirectory = async (dir: string): Promise<void> => {
130
+ const entries = await fs.readdir(dir, { withFileTypes: true });
131
+
132
+ for (const entry of entries) {
133
+ const fullPath = path.join(dir, entry.name);
134
+
135
+ if (entry.isDirectory()) {
136
+ // Skip node_modules and other non-source directories
137
+ if (entry.name === 'node_modules' || entry.name === '.git') {
138
+ continue;
139
+ }
140
+ await processDirectory(fullPath);
141
+ } else if (entry.isFile() && /\.(ts|tsx|js|jsx|mts|mjs)$/.test(entry.name)) {
142
+ await processFile(fullPath);
143
+ }
144
+ }
145
+ };
146
+
147
+ await processDirectory(srcPath);
148
+ };
149
+
150
+ export const modifyStaticExport = async (TEMP_DIR: string): Promise<void> => {
151
+ // 1. Remove URL rewrite logic from define-config.ts
152
+ const defineConfigPath = path.join(TEMP_DIR, 'src', 'libs', 'next', 'proxy', 'define-config.ts');
153
+ console.log(' Processing src/libs/next/proxy/define-config.ts...');
154
+ await updateFile({
155
+ assertAfter: assertUrlRewriteRemoved,
156
+ filePath: defineConfigPath,
157
+ name: 'modifyStaticExport:removeUrlRewrite',
158
+ transformer: removeUrlRewriteLogic,
159
+ });
160
+
161
+ // 2. Rename [variants] directories to (variants)
162
+ console.log(' Renaming [variants] directories to (variants)...');
163
+ await renameVariantsDirectories(TEMP_DIR);
164
+
165
+ // 3. Update all imports referencing [variants]
166
+ console.log(' Updating imports referencing [variants]...');
167
+ await updateVariantsImports(TEMP_DIR);
168
+ };
169
+
170
+ if (isDirectRun(import.meta.url)) {
171
+ await runStandalone('modifyStaticExport', modifyStaticExport, [
172
+ { lang: Lang.TypeScript, path: 'src/libs/next/proxy/define-config.ts' },
173
+ ]);
174
+ }
@@ -34,7 +34,7 @@ interface LocaleLayoutProps extends PropsWithChildren {
34
34
  }
35
35
 
36
36
  const Locale = memo<LocaleLayoutProps>(({ children, defaultLang, antdLocale }) => {
37
- const [i18n] = useState(createI18nNext(defaultLang));
37
+ const [i18n] = useState(() => createI18nNext(defaultLang));
38
38
  const [lang, setLang] = useState(defaultLang);
39
39
  const [locale, setLocale] = useState(antdLocale);
40
40
 
@@ -6,6 +6,8 @@ import { globalAgentContextManager } from '@/helpers/GlobalAgentContextManager';
6
6
  import { useOnlyFetchOnceSWR } from '@/libs/swr';
7
7
  // Import for type usage
8
8
  import { electronSystemService } from '@/services/electron/system';
9
+ import { type LocaleMode } from '@/types/locale';
10
+ import { switchLang } from '@/utils/client/switchLang';
9
11
  import { merge } from '@/utils/merge';
10
12
 
11
13
  import type { ElectronStore } from '../store';
@@ -55,6 +57,10 @@ export const createElectronAppSlice: StateCreator<
55
57
  userDataPath: result.userPath!.userData,
56
58
  videosPath: result.userPath!.videos,
57
59
  });
60
+
61
+ // Initialize i18n with the stored locale, falling back to auto detection.
62
+ const locale = (result.locale ?? 'auto') as LocaleMode;
63
+ switchLang(locale);
58
64
  },
59
65
  },
60
66
  ),
@@ -2,12 +2,12 @@ import { RouteVariants } from '@lobechat/desktop-bridge';
2
2
 
3
3
  import { type DynamicLayoutProps } from '@/types/next';
4
4
 
5
+ export { LOBE_LOCALE_COOKIE } from '@/const/locale';
6
+ export { LOBE_THEME_APPEARANCE } from '@/const/theme';
5
7
  export {
6
8
  DEFAULT_LANG,
7
9
  DEFAULT_VARIANTS,
8
10
  type IRouteVariants,
9
- LOBE_LOCALE_COOKIE,
10
- LOBE_THEME_APPEARANCE,
11
11
  type Locales,
12
12
  locales,
13
13
  } from '@lobechat/desktop-bridge';