@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.
- package/.vscode/settings.json +2 -17
- package/CHANGELOG.md +25 -0
- package/apps/desktop/src/main/controllers/SystemCtr.ts +10 -0
- package/apps/desktop/src/main/core/App.ts +10 -188
- package/apps/desktop/src/main/core/__tests__/App.test.ts +6 -42
- package/apps/desktop/src/main/core/browser/Browser.ts +17 -9
- package/apps/desktop/src/main/core/infrastructure/RendererUrlManager.ts +126 -0
- package/apps/desktop/src/main/core/infrastructure/__tests__/RendererUrlManager.test.ts +72 -0
- package/changelog/v1.json +5 -0
- package/package.json +1 -1
- package/packages/builtin-tool-web-browsing/src/client/Inspector/Search/index.tsx +1 -1
- package/packages/desktop-bridge/src/index.ts +0 -2
- package/packages/desktop-bridge/src/routeVariants.ts +0 -2
- package/packages/electron-client-ipc/src/types/system.ts +1 -0
- package/packages/model-bank/src/aiModels/lobehub.ts +0 -3
- package/packages/model-runtime/src/core/streams/openai/openai.test.ts +167 -0
- package/packages/model-runtime/src/core/streams/openai/openai.ts +30 -6
- package/packages/model-runtime/src/core/streams/protocol.ts +5 -0
- package/packages/model-runtime/src/core/streams/qwen.test.ts +131 -2
- package/packages/model-runtime/src/core/streams/qwen.ts +9 -1
- package/scripts/electronWorkflow/modifiers/index.mts +2 -0
- package/scripts/electronWorkflow/modifiers/nextConfig.mts +1 -1
- package/scripts/electronWorkflow/modifiers/staticExport.mts +174 -0
- package/src/layout/GlobalProvider/Locale.tsx +1 -1
- package/src/store/electron/actions/app.ts +6 -0
- 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';
|