Skip to content

Commit 1969a3c

Browse files
committed
feat: v0.10.0 — 苏格拉底式学习、仪表盘深海主题、SQLite 迁移与 AI 降级
苏格拉底式学习(费曼增强): - 新增 SocraticDialogue 苏格拉底对话组件 - 新增 BrainstormPanel 头脑风暴面板 - 新增 DeepeningZone 深化区 - 新增 SocraticSessionPage 苏格拉底会话页 - useSocraticFlow Hook 管理对话流程 - 后端新增 3 条苏格拉底链(brainstorm/deepening/evaluate) 仪表盘深海主题重构: - 新增 HeatmapChart / RadarChart / TrendChart 可视化组件 - 新增 StartupRitual 启动仪式组件 - deep-sea 主题子目录与 hooks - 仪表盘页面全面重构 笔记智能增强: - 新增 AnchorPoint 锚点组件 - 新增 PredictionPanel 预测面板与 PredictionResult - useEditActivityTracker 编辑活动追踪 番茄钟 AI 推荐: - 新增 useAIRecommendation Hook - 番茄钟 lib 子模块扩展 AI 系统增强: - 新增 AI 降级管理器(aiFallbackManager / aiServiceFallback) - 新增 useAIFallback / useAIService Hooks - AIPluginLoader / ElectronAIPlugin / RemoteAIPlugin 优化 - 新增 AI hooks 子目录 - visionWorker 优化 存储层升级: - 新增 IpcStorageAdapter(IPC 存储适配器) - 新增 electron/db/ 数据库模块 - 新增 MigrationProgress 迁移进度组件 - storageFactory 优化 前端基础设施: - 新增 AIThinkingIndicator AI 思考指示器 - 新增 RescuePanel 救援面板 - 新增 useAIGatewayHealth / useStuckTimer Hooks - 新增 workers/ 工作线程目录 - UI 组件全面更新(Button/Card/Modal/Toast/ErrorBoundary 等) - 自定义标题栏、侧边栏、页面过渡增强 后端与部署: - 新增苏格拉底路由与测试 - AI 网关配置与推荐链优化 - docker-compose.prod.yml 更新 - Nginx 配置调整 - deploy.sh 更新 其他: - 应用图标更新 - electron-builder 构建配置调整 - Tailwind 设计令牌更新 - PWA 安装提示优化 - 隐私政策与服务条款更新
1 parent 0e6861d commit 1969a3c

178 files changed

Lines changed: 12715 additions & 1713 deletions

File tree

Some content is hidden

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

‎client/.env.example‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ VITE_SUPABASE_ANON_KEY=
1212
# ---- API 服务地址 ----
1313
# 同步服务(sync-service)基础地址
1414
# 开发环境: http://127.0.0.1:8080
15-
# 生产环境: 你的服务器地址
16-
VITE_API_BASE_URL=
15+
# 生产环境: https://entropydecrease.com
16+
VITE_API_BASE_URL=https://entropydecrease.com
1717

1818
# 同步服务健康检查端点
19-
VITE_API_HEALTH_URL=
19+
VITE_API_HEALTH_URL=https://entropydecrease.com/health
2020

2121
# ---- AI 网关 ----
2222
# AI Gateway 服务地址
2323
# 开发环境: http://127.0.0.1:8000
24-
# 生产环境: 你的服务器地址
25-
VITE_AI_GATEWAY_URL=
24+
# 生产环境: https://entropydecrease.com
25+
VITE_AI_GATEWAY_URL=https://entropydecrease.com

‎client/app-icon.png‎

Lines changed: 2 additions & 2 deletions
Loading

‎client/electron-builder.yml‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
# Electron Builder 打包配置 - 课伴桌面端
2-
appId: com.keban.app
3-
productName: "KeBan"
4-
# publish 配置用于自动更新发布,待部署更新服务器后启用
5-
# publish:
6-
# provider: generic
7-
# url: ${env.PUBLISH_URL}
1+
# Electron Builder 打包配置 - 熵减桌面端
2+
appId: com.entropydecrease.app
3+
productName: "Entropy decrease"
4+
publish:
5+
provider: github
6+
owner: YourGitHubUsername
7+
repo: KeBan
88
directories:
99
output: release
1010
buildResources: build
11+
asarUnpack:
12+
- "**/*.node"
1113
files:
1214
- dist/**/*
1315
- dist-electron/**/*
@@ -34,6 +36,6 @@ nsis:
3436
# installerIcon/uninstallerIcon/installerHeaderIcon 省略
3537
# electron-builder 会自动从 win.icon 派生 NSIS 所需的 .ico 文件
3638
createDesktopShortcut: true
37-
shortcutName: "课伴"
39+
shortcutName: "Entropy decrease"
3840
perMachine: false
3941
language: "2052"

‎client/electron/ai/handlers/feynmanHandler.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ function register(): void {
125125

126126
export const feature: AIFeatureDef = {
127127
id: 'ai_feynman',
128-
name: '费曼学习法',
128+
name: '浮出水面',
129129
version: '1.0.0',
130130
register,
131131
};

‎client/electron/ai/handlers/flashcardHandler.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ function register(): void {
8282

8383
export const feature: AIFeatureDef = {
8484
id: 'ai_generate_cards',
85-
name: 'AI 闪卡生成',
85+
name: 'AI 反衰减呼吸生成',
8686
version: '1.0.0',
8787
register,
8888
};

‎client/electron/ai/handlers/tagHandler.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,10 @@ function register(): void {
8383
};
8484

8585
interface SortSuggestionResp {
86-
type: string;
86+
category: string;
8787
reason: string;
8888
confidence: number;
89+
suggested_action: string;
8990
}
9091
interface SortInspirationResp {
9192
suggestions: SortSuggestionResp[];
@@ -104,9 +105,10 @@ function register(): void {
104105
logger.info(`[IPC] ai_sort_inspiration end, suggestions_count=${resp.suggestions.length}`);
105106
return {
106107
suggestions: resp.suggestions.map((s) => ({
107-
type: s.type,
108+
category: s.category,
108109
reason: s.reason,
109110
confidence: s.confidence,
111+
suggestedAction: s.suggested_action,
110112
})),
111113
model: resp.model,
112114
tokensUsed: resp.tokens_used,
@@ -123,7 +125,7 @@ function register(): void {
123125

124126
export const feature: AIFeatureDef = {
125127
id: 'ai_tag',
126-
name: 'AI 标签与灵感分类',
128+
name: 'AI 标签与萤火海沟分类',
127129
version: '1.0.0',
128130
register,
129131
};

‎client/electron/captureHandlers.ts‎

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { ScreenCapture } from './screenCapture.js';
99
import type { ScreenCaptureOptions, ScreenshotFrameData } from './screenCapture.js';
1010
import { AudioCapture, listAudioSources } from './audioCapture.js';
1111
import type { AudioCaptureOptions, AudioChunk } from './audioCapture.js';
12-
import { safeHandle } from './ipcUtils.js';
12+
import { safeHandle, getMainWindowId } from './ipcUtils.js';
1313
import { logger } from './logger.js';
1414

1515
// ================================================================
@@ -22,6 +22,13 @@ let activeCapture: ScreenCapture | null = null;
2222
/** 当前活跃的音频捕获实例 */
2323
let activeAudioCapture: AudioCapture | null = null;
2424

25+
/** screen_capture_start 防抖:500ms 内多次调用只响应最后一次 */
26+
let startDebounceTimer: ReturnType<typeof setTimeout> | null = null;
27+
const START_DEBOUNCE_MS = 500;
28+
29+
/** 单调递增会话标识,用于防止 stop 后残留的 debounce 重启采集 */
30+
let captureSessionToken = 0;
31+
2532
// ================================================================
2633
// 公共 API
2734
// ================================================================
@@ -35,26 +42,58 @@ export function registerCaptureHandlers(): void {
3542
safeHandle(
3643
'screen_capture_start',
3744
async (event, options: ScreenCaptureOptions) => {
38-
if (activeCapture) {
39-
activeCapture.dispose();
40-
activeCapture = null;
45+
// 防抖:500ms 内多次调用只响应最后一次
46+
if (startDebounceTimer) {
47+
clearTimeout(startDebounceTimer);
48+
startDebounceTimer = null;
4149
}
4250

43-
const senderWin = BrowserWindow.fromWebContents(event.sender);
44-
45-
activeCapture = new ScreenCapture(options, (frame: ScreenshotFrameData) => {
46-
if (senderWin && !senderWin.isDestroyed()) {
47-
senderWin.webContents.send('screen_capture_frame', frame);
48-
}
51+
// 记录本次会话 token,用于 debounce 回调时校验是否仍然有效
52+
const token = ++captureSessionToken;
53+
54+
return new Promise<{ success: boolean }>((resolve) => {
55+
startDebounceTimer = setTimeout(() => {
56+
startDebounceTimer = null;
57+
58+
// 如果在 debounce 期间调用了 stop(token 已变),放弃本次启动
59+
if (token !== captureSessionToken) {
60+
logger.info('[IPC] screen_capture_start debounce 已过期(stop 后残留),跳过');
61+
resolve({ success: false });
62+
return;
63+
}
64+
65+
// 幂等:先清理旧实例
66+
if (activeCapture) {
67+
activeCapture.dispose();
68+
activeCapture = null;
69+
}
70+
71+
const senderWin = BrowserWindow.fromWebContents(event.sender);
72+
73+
activeCapture = new ScreenCapture(options, (frame: ScreenshotFrameData) => {
74+
if (senderWin && !senderWin.isDestroyed()) {
75+
senderWin.webContents.send('screen_capture_frame', frame);
76+
}
77+
});
78+
79+
activeCapture.start();
80+
logger.info('[IPC] screen_capture_start 已启动(防抖后)');
81+
resolve({ success: true });
82+
}, START_DEBOUNCE_MS);
4983
});
50-
51-
activeCapture.start();
52-
logger.info('[IPC] screen_capture_start 已启动');
53-
return { success: true };
5484
},
5585
);
5686

5787
safeHandle('screen_capture_stop', async () => {
88+
// 递增 token,使残留的 debounce 回调失效
89+
captureSessionToken++;
90+
91+
// 清理防抖定时器
92+
if (startDebounceTimer) {
93+
clearTimeout(startDebounceTimer);
94+
startDebounceTimer = null;
95+
}
96+
5897
if (activeCapture) {
5998
activeCapture.dispose();
6099
activeCapture = null;
@@ -143,6 +182,15 @@ export function registerCaptureHandlers(): void {
143182
ipcMain.on(
144183
'audio_capture_chunk',
145184
(_event, data: { audioBuffer: ArrayBuffer; sampleRate: number; channels: number; durationMs: number }) => {
185+
// SEC-005: sender 验证,仅接受主窗口的音频数据
186+
const mainId = getMainWindowId();
187+
if (mainId !== null && _event.sender.id !== mainId) {
188+
logger.warn(
189+
`[IPC] Sender verification failed for "audio_capture_chunk": ` +
190+
`expected sender.id=${mainId}, got ${_event.sender.id}`
191+
);
192+
return;
193+
}
146194
if (activeAudioCapture && activeAudioCapture.isCapturing) {
147195
activeAudioCapture.handleRendererChunk(data);
148196
}
@@ -158,6 +206,11 @@ export function registerCaptureHandlers(): void {
158206
* 释放所有活跃的采集实例
159207
*/
160208
export function disposeCaptureHandlers(): void {
209+
// 清理防抖定时器
210+
if (startDebounceTimer) {
211+
clearTimeout(startDebounceTimer);
212+
startDebounceTimer = null;
213+
}
161214
if (activeCapture) {
162215
activeCapture.dispose();
163216
activeCapture = null;

0 commit comments

Comments
 (0)