Skip to content

Commit b909331

Browse files
committed
feat: v0.10.1 — AI 错误处理增强、主页导航修复与后端路由优化
AI 错误处理增强: - 新增 errorClassifier 错误分类器 - 新增 errorMessages 统一错误消息 - 新增 useAIErrorHandler Hook - 新增 useAIFeature Hook - ElectronAIPlugin / RemoteAIPlugin 错误处理优化 - AI 配置与类型系统扩展 主页与导航修复: - 主页导航动效修复 - 弹性翻页时序优化 - AppLayout / PageTransition 交互增强 设置页优化: - AIProviderSettings / DataSettings / ModeSettings 改进 - useSettingsStore 状态管理优化 后端路由优化: - 全部路由模块增强(evaluate/feynman/cards/inspiration/learning/recommend/socratic/summarize/tag/transcribe/vision) - auth / rate_limit 中间件优化 - AI 网关配置更新 部署配置: - docker-compose.prod.yml 调整 - Nginx 配置更新 README 更新至 v0.10.1
1 parent 1969a3c commit b909331

51 files changed

Lines changed: 1197 additions & 237 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎README.md‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<p align="center"><strong>你的 AI 智能学习伙伴 — 让每一分钟学习都高效有价值</strong></p>
44
<p align="center">
55
<img src="https://img.shields.io/badge/status-beta-green.svg" alt="Status" />
6-
<img src="https://img.shields.io/badge/version-v0.9.0-blue.svg" alt="Version" />
6+
<img src="https://img.shields.io/badge/version-v0.10.1-blue.svg" alt="Version" />
77
<img src="https://img.shields.io/badge/license-Apache--2.0-blue.svg" alt="License" />
88
</p>
99
</p>
@@ -216,6 +216,8 @@ KeBan/
216216
| **v0.7.0** | ✅ 已完成 | 自定义标题栏 + 用户体系 + 邮箱验证 + 灵感 AI 整理 + 会话管理 |
217217
| **v0.8.0** | ✅ 已完成 | UI 视觉升级(深海静谧) + Electron 安全加固 + 标题栏增强 + 音效分类控制 + BUG 清零 |
218218
| **v0.9.0** | ✅ 已完成 | 笔记搜索优化 + 标签筛选 + 闪卡堆叠组件 + 多项 BUG 修复 |
219+
| **v0.10.0** | ✅ 已完成 | 主页氛围化问候 + 深海认知景深层 + 弹性翻页交互 + 启动缓冲带 + 动效优化 |
220+
| **v0.10.1** | ✅ 已完成 | 主页导航动效修复 + 弹性翻页时序优化 |
219221
| **正式上线** | 🎯 规划中 | 生产就绪版 |
220222

221223
### 🔮 未来方向
@@ -234,8 +236,8 @@ KeBan/
234236
| 组成部分 | 含义 | 示例 |
235237
|----------|------|------|
236238
| `MAJOR` | 不兼容的 API 变更 | `1.0.0` |
237-
| `MINOR` | 向后兼容的功能新增 | `0.9.0` |
238-
| `PATCH` | 向后兼容的缺陷修复 | `0.9.1` |
239+
| `MINOR` | 向后兼容的功能新增 | `0.10.0` |
240+
| `PATCH` | 向后兼容的缺陷修复 | `0.10.1` |
239241

240242
发布 Tag 前请确保:`npm run lint` / `npm run test` / `npm run build` 全部通过。
241243

‎client/electron/ai/utils.ts‎

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,64 @@
55
* POST 请求辅助函数、网关地址管理和功能注册表接口定义。
66
*/
77

8+
import { app } from 'electron';
9+
import * as path from 'path';
10+
import { readFile, writeFile } from 'fs/promises';
811
import { logger } from '../logger.js';
912

1013
// ================================================================
1114
// 常量
1215
// ================================================================
1316

1417
const DEFAULT_GATEWAY_URL = '';
18+
const GATEWAY_CONFIG_FILE = 'ai-gateway-config.json';
1519

16-
/** 获取 AI 网关地址,优先读取环境变量 VITE_AI_GATEWAY_URL */
20+
// ── 运行时网关地址(渲染进程通过 IPC 同步) ──
21+
let _runtimeGatewayUrl: string | null = null;
22+
23+
/**
24+
* 获取 AI 网关地址
25+
* 优先级:运行时设置 > 持久化文件 > 环境变量 > 默认空值
26+
*/
1727
export function gatewayUrl(): string {
28+
if (_runtimeGatewayUrl) return _runtimeGatewayUrl;
1829
return process.env.VITE_AI_GATEWAY_URL || DEFAULT_GATEWAY_URL;
1930
}
2031

32+
/**
33+
* 设置运行时网关地址(由渲染进程通过 IPC 调用)
34+
* 同时持久化到 userData 目录,确保主进程重启后仍可用
35+
*/
36+
export async function setRuntimeGatewayUrl(url: string): Promise<void> {
37+
_runtimeGatewayUrl = url;
38+
logger.info(`[AI-Gateway] Runtime gateway URL set: ${url}`);
39+
// 持久化到文件
40+
try {
41+
const configPath = path.join(app.getPath('userData'), GATEWAY_CONFIG_FILE);
42+
await writeFile(configPath, JSON.stringify({ gatewayUrl: url }), 'utf-8');
43+
} catch (err) {
44+
logger.error('[AI-Gateway] Failed to persist gateway URL', err);
45+
}
46+
}
47+
48+
/**
49+
* 应用启动时从持久化文件加载网关地址
50+
* 在 registerAIHandlers 之前调用
51+
*/
52+
export async function loadPersistedGatewayUrl(): Promise<void> {
53+
try {
54+
const configPath = path.join(app.getPath('userData'), GATEWAY_CONFIG_FILE);
55+
const raw = await readFile(configPath, 'utf-8');
56+
const config = JSON.parse(raw);
57+
if (config.gatewayUrl) {
58+
_runtimeGatewayUrl = config.gatewayUrl;
59+
logger.info(`[AI-Gateway] Loaded persisted gateway URL: ${config.gatewayUrl}`);
60+
}
61+
} catch {
62+
// 文件不存在或解析失败,静默忽略
63+
}
64+
}
65+
2166
// ================================================================
2267
// AI 功能注册表接口定义
2368
// ================================================================

‎client/electron/main.ts‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { writeFile, readFile } from 'fs/promises';
1414
import { safeHandle, setMainWindowId } from './ipcUtils.js';
1515
import { logger } from './logger.js';
1616
import { registerAIHandlers } from './ai/index.js';
17+
import { loadPersistedGatewayUrl, setRuntimeGatewayUrl } from './ai/utils.js';
1718
import { initAutoUpdater, checkForUpdate, downloadUpdate, installUpdate, destroyAutoUpdater, setAutoCheckEnabled } from './updater.js';
1819
import { createMainWindow, saveCloseChoice } from './windowManager.js';
1920
import { destroyTray } from './trayManager.js';
@@ -99,8 +100,8 @@ if (!gotTheLock) {
99100
// 开发环境:允许 unsafe-inline/unsafe-eval(Vite HMR 需要)
100101
// 生产环境:禁止 unsafe-eval,保留 unsafe-inline(Tailwind 运行时需要)
101102
const csp = isDev
102-
? "default-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:* ws://localhost:*; connect-src 'self' http://localhost:* ws://localhost:* https://*.supabase.co wss://*.supabase.co; img-src 'self' data: blob: https://*.supabase.co; font-src 'self' data:;"
103-
: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://*.supabase.co; font-src 'self' data:; connect-src 'self' https://*.supabase.co wss://*.supabase.co; frame-ancestors 'none';";
103+
? "default-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:* ws://localhost:*; connect-src 'self' http://localhost:* ws://localhost:* https://*.supabase.co wss://*.supabase.co https://entropydecrease.com wss://entropydecrease.com; img-src 'self' data: blob: https://*.supabase.co; font-src 'self' data:;"
104+
: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://*.supabase.co; font-src 'self' data:; connect-src 'self' https://*.supabase.co wss://*.supabase.co https://entropydecrease.com wss://entropydecrease.com; frame-ancestors 'none';";
104105

105106
callback({
106107
responseHeaders: {
@@ -115,6 +116,8 @@ if (!gotTheLock) {
115116
logger.error('[SEC] Failed to inject CSP policy', err);
116117
}
117118

119+
// 加载持久化的 AI 网关地址(在注册 handler 之前,确保 handler 可用正确的 URL)
120+
await loadPersistedGatewayUrl();
118121
registerAIHandlers();
119122
registerCaptureHandlers();
120123

@@ -130,6 +133,12 @@ if (!gotTheLock) {
130133
// 隐藏默认 Electron 菜单栏
131134
Menu.setApplicationMenu(null);
132135

136+
// AI 网关地址同步(渲染进程 → 主进程)
137+
safeHandle('ai:set-gateway-url', async (_event, url: string) => {
138+
await setRuntimeGatewayUrl(url);
139+
return { success: true };
140+
});
141+
133142
// 通用 IPC handlers
134143
safeHandle('get-app-version', async () => {
135144
return app.getVersion();

‎client/electron/preload.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const ALLOWED_CHANNELS = [
1515
'ai_recommend_duration',
1616
'ai_feynman_question',
1717
'ai_feynman_evaluate_answers',
18+
'ai:set-gateway-url',
1819
'screen_list_windows',
1920
'screen_capture_start',
2021
'screen_capture_stop',

‎client/electron/windowManager.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export function createMainWindow(
105105
if (isDev) {
106106
// 开发模式:连接 Vite 开发服务器
107107
win.loadURL('http://localhost:5173');
108-
win.webContents.openDevTools();
108+
win.webContents.openDevTools({ mode: 'detach' });
109109
} else {
110110
// 生产模式:加载打包后的 index.html
111111
win.loadFile(path.join(app.getAppPath(), 'dist', 'index.html'));

‎client/package-lock.json‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎client/package.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "entropy-decrease",
33
"productName": "Entropy decrease",
44
"private": true,
5-
"version": "0.9.0",
5+
"version": "0.10.1",
66
"description": "熵减 - 跨端学习平台",
77
"author": "Entropy Decrease Team",
88
"main": "dist-electron/electron/main.js",
@@ -11,7 +11,7 @@
1111
"build": "tsc -b && vite build",
1212
"lint": "oxlint",
1313
"preview": "vite preview",
14-
"electron:dev": "concurrently -k -n vite,electron \"cross-env ELECTRON_BUILD=1 vite\" \"wait-on http://localhost:5173 && tsc -p electron/tsconfig.json && cross-env ELECTRON_BUILD=1 electron .\"",
14+
"electron:dev": "concurrently -k -n vite,electron \"cross-env ELECTRON_BUILD=1 vite\" \"wait-on http://localhost:5173 && tsc -p electron/tsconfig.json && cross-env ELECTRON_BUILD=1 NODE_ENV=development electron .\"",
1515
"electron:build": "cross-env ELECTRON_BUILD=1 vite build && tsc -p electron/tsconfig.json && electron-builder",
1616
"test": "vitest run",
1717
"test:watch": "vitest",

‎client/src/App.tsx‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { router } from '@/routes';
1212
import { useTheme } from '@/hooks/useTheme';
1313
import { db } from '@/lib/storage/database';
1414
import { soundPlayer } from '@/lib/audio/SoundPlayer';
15+
import { getAIConfig } from '@/lib/ai/config';
1516
import '@/stores/useSettingsStore'; // 导入以触发音效设置初始化
1617

1718
// 启动时预加载所有音效(不阻塞渲染)
@@ -57,6 +58,11 @@ function App() {
5758
// 应用空闲时预检测 AI 网关健康状态(不阻塞首屏)
5859
const idle = window.requestIdleCallback ?? ((cb: () => void) => setTimeout(cb, 1500));
5960
idle(() => {
61+
// 同步网关地址到主进程(主进程无法访问 localStorage,需显式传递)
62+
const aiConfig = getAIConfig();
63+
if (aiConfig.gatewayUrl && window.electronAPI) {
64+
window.electronAPI.invoke('ai:set-gateway-url', aiConfig.gatewayUrl).catch(() => {});
65+
}
6066
import('./hooks/useAIGatewayHealth').then(({ precheckGatewayHealth }) => {
6167
precheckGatewayHealth();
6268
});

‎client/src/components/RescuePanel.tsx‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useState, useCallback, useEffect } from 'react';
2+
import { useNavigate } from 'react-router-dom';
23
import { motion, AnimatePresence } from 'framer-motion';
34
import {
45
X, HelpCircle, Lightbulb, Layers, BookOpen,
@@ -38,7 +39,8 @@ const SUGGESTIONS = [
3839
export function RescuePanel({ isOpen, onClose, context, onSuggestion }: RescuePanelProps) {
3940
const [activeLevel, setActiveLevel] = useState<RescueLevel>(1);
4041
const [showIncubation, setShowIncubation] = useState(false);
41-
const { loading, data, error, rescue } = useAIRescue();
42+
const { loading, data, error, needsConfig, rescue } = useAIRescue();
43+
const navigate = useNavigate();
4244

4345
// 面板打开时触发救援请求
4446
useEffect(() => {
@@ -149,6 +151,14 @@ export function RescuePanel({ isOpen, onClose, context, onSuggestion }: RescuePa
149151
{error && !loading && (
150152
<div className="p-3 rounded-kb-md bg-semantic-error/10 border border-semantic-error/20 text-b2 text-semantic-error">
151153
{error}
154+
{needsConfig && (
155+
<button
156+
onClick={() => navigate('/settings')}
157+
className="mt-2 block text-b3 underline hover:no-underline"
158+
>
159+
前往设置页配置 API Key
160+
</button>
161+
)}
152162
</div>
153163
)}
154164

‎client/src/components/layout/AppLayout.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export default function AppLayout() {
8383
{/* Page content */}
8484
<main className="flex-1 overflow-y-auto overflow-x-hidden pb-16 md:pb-0 relative">
8585
<AnimatePresence mode="wait" initial={false}>
86-
<PageTransition key={pathname}>
86+
<PageTransition key={pathname} variant={pathname === '/' ? 'none' : 'default'}>
8787
<Outlet />
8888
</PageTransition>
8989
</AnimatePresence>

0 commit comments

Comments
 (0)