Skip to content

Commit 0e6861d

Browse files
committed
feat: v0.9.0 — 笔记搜索优化、标签筛选、闪卡生成式复习与 AI 链路扩展
笔记模块增强: - 新增 NoteSearchBar 全文搜索组件 - 新增 NoteTagFilter 标签筛选组件 - 笔记编辑页与列表页交互优化 闪卡系统升级: - 新增 ConfidenceSelector 置信度选择器 - 新增 GoldenErrorPanel 黄金错题面板 - 新增 GenerativeReviewPage 生成式复习页面 - useGenerativeReview Hook 支持生成式复习流程 - 闪卡学习与会话存储优化 AI 网关扩展: - 新增 anchor_point_chain(锚点提取链) - 新增 inspiration_draft_chain(灵感草稿链) - 新增 predict_chain(预测链) - 新增 rescue_chain(救援链) - 新增 socratic_chain(苏格拉底式提问链) - 新增灵感草稿与学习路由 - Prompt 模板文件统一管理 前端基础设施: - 新增 AmbientLightPool 氛围光组件 - 新增 VirtualList 虚拟列表组件 - 新增 useDeviceCapability / useVirtualList Hooks - 新增 textDiff 文本差异对比工具 - 新增 backupCrypto 备份加密模块 - 新增 ModeSettings / StoragePathSettings 设置页 - 自定义标题栏、页面过渡动效增强 - 番茄钟沉浸式计时器优化 后端与配置: - AI 网关配置与路由扩展 - 灵感整理链优化 - electron-builder 构建配置调整 - 存储层与导出导入优化 - 加密模块扩展 README 更新至 v0.9.0
1 parent 98794ce commit 0e6861d

80 files changed

Lines changed: 6649 additions & 666 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.

‎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.7.0-blue.svg" alt="Version" />
6+
<img src="https://img.shields.io/badge/version-v0.9.0-blue.svg" alt="Version" />
77
<img src="https://img.shields.io/badge/license-Apache--2.0-blue.svg" alt="License" />
88
</p>
99
</p>
@@ -214,6 +214,8 @@ KeBan/
214214
| **v0.5.0** | ✅ 已完成 | 安全加固 + 效率分析 + 全局命令面板 + 灵感空间 + 上云部署 |
215215
| **v0.6.0** | ✅ 已完成 | 沉浸式计时 + 自动更新 + 音效系统 + 隐私政策 + 品牌视觉升级 |
216216
| **v0.7.0** | ✅ 已完成 | 自定义标题栏 + 用户体系 + 邮箱验证 + 灵感 AI 整理 + 会话管理 |
217+
| **v0.8.0** | ✅ 已完成 | UI 视觉升级(深海静谧) + Electron 安全加固 + 标题栏增强 + 音效分类控制 + BUG 清零 |
218+
| **v0.9.0** | ✅ 已完成 | 笔记搜索优化 + 标签筛选 + 闪卡堆叠组件 + 多项 BUG 修复 |
217219
| **正式上线** | 🎯 规划中 | 生产就绪版 |
218220

219221
### 🔮 未来方向
@@ -232,8 +234,8 @@ KeBan/
232234
| 组成部分 | 含义 | 示例 |
233235
|----------|------|------|
234236
| `MAJOR` | 不兼容的 API 变更 | `1.0.0` |
235-
| `MINOR` | 向后兼容的功能新增 | `0.7.0` |
236-
| `PATCH` | 向后兼容的缺陷修复 | `0.7.1` |
237+
| `MINOR` | 向后兼容的功能新增 | `0.9.0` |
238+
| `PATCH` | 向后兼容的缺陷修复 | `0.9.1` |
237239

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

‎client/electron-builder.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Electron Builder 打包配置 - 课伴桌面端
22
appId: com.keban.app
3-
productName: "课伴"
3+
productName: "KeBan"
44
# publish 配置用于自动更新发布,待部署更新服务器后启用
55
# publish:
66
# provider: generic

‎client/electron/main.ts‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010

1111
import { app, BrowserWindow, Menu, dialog, session } from 'electron';
12+
import { writeFile, readFile } from 'fs/promises';
1213
import { safeHandle, setMainWindowId } from './ipcUtils.js';
1314
import { logger } from './logger.js';
1415
import { registerAIHandlers } from './ai/index.js';
@@ -196,6 +197,64 @@ if (!gotTheLock) {
196197
return { success: true };
197198
});
198199

200+
// ================================================================
201+
// v0.9.0: 备份相关 IPC handlers
202+
// ================================================================
203+
204+
/**
205+
* 显示保存对话框并将备份数据写入文件
206+
*/
207+
safeHandle('backup:save', async (_event, data: string, defaultName?: string) => {
208+
const filename = defaultName || `keban-backup-${new Date().toISOString().slice(0, 10)}.json`;
209+
210+
const result = await dialog.showSaveDialog({
211+
title: '保存备份文件',
212+
defaultPath: filename,
213+
filters: [
214+
{ name: 'KeBan 备份文件', extensions: ['json'] },
215+
{ name: '所有文件', extensions: ['*'] },
216+
],
217+
});
218+
219+
if (result.canceled || !result.filePath) {
220+
return { success: false, canceled: true, path: null };
221+
}
222+
223+
try {
224+
await writeFile(result.filePath, data, 'utf-8');
225+
return { success: true, canceled: false, path: result.filePath };
226+
} catch (err) {
227+
const msg = err instanceof Error ? err.message : '写入失败';
228+
return { success: false, canceled: false, path: null, error: msg };
229+
}
230+
});
231+
232+
/**
233+
* 显示打开对话框,选择备份文件并读取内容
234+
*/
235+
safeHandle('backup:open', async () => {
236+
const result = await dialog.showOpenDialog({
237+
title: '选择备份文件',
238+
filters: [
239+
{ name: 'KeBan 备份文件', extensions: ['json'] },
240+
{ name: '所有文件', extensions: ['*'] },
241+
],
242+
properties: ['openFile'],
243+
});
244+
245+
if (result.canceled || result.filePaths.length === 0) {
246+
return { success: false, canceled: true, content: null };
247+
}
248+
249+
try {
250+
const content = await readFile(result.filePaths[0], 'utf-8');
251+
return { success: true, canceled: false, content, path: result.filePaths[0] };
252+
} catch (err) {
253+
const msg = err instanceof Error ? err.message : '读取失败';
254+
return { success: false, canceled: false, content: null, error: msg };
255+
}
256+
});
257+
199258
app.on('activate', () => {
200259
if (BrowserWindow.getAllWindows().length === 0) {
201260
mainWindow = createMainWindow(isQuittingRef, performQuit);

‎client/electron/preload.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ const ALLOWED_CHANNELS = [
3232
'window:maximize',
3333
'window:close',
3434
'window:isMaximized',
35+
// v0.9.0: 备份相关 IPC channel
36+
'backup:save',
37+
'backup:open',
3538
] as const;
3639

3740
/** 允许渲染进程监听的事件 channel 白名单(主进程 → 渲染进程推送) */
@@ -104,4 +107,9 @@ contextBridge.exposeInMainWorld('electronAPI', {
104107
// ---- 自动更新 API ----
105108
/** 设置是否自动检查更新 */
106109
setAutoUpdate: (enabled: boolean) => ipcRenderer.invoke('update:set-auto-check', enabled),
110+
// ---- v0.9.0: 备份 API ----
111+
/** 保存备份文件(显示系统保存对话框) */
112+
backupSave: (data: string, defaultName?: string) => ipcRenderer.invoke('backup:save', data, defaultName),
113+
/** 打开备份文件(显示系统打开对话框,返回文件内容) */
114+
backupOpen: () => ipcRenderer.invoke('backup:open'),
107115
});

‎client/package-lock.json‎

Lines changed: 28 additions & 0 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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "client",
33
"private": true,
4-
"version": "0.8.0",
4+
"version": "0.9.0",
55
"description": "课伴 - 跨端学习平台",
66
"author": "KeBan Team",
77
"main": "dist-electron/main.js",
@@ -22,6 +22,7 @@
2222
"@radix-ui/react-toast": "^1.2.19",
2323
"@supabase/supabase-js": "^2.110.1",
2424
"@tanstack/react-query": "^5.101.2",
25+
"@tanstack/react-virtual": "^3.14.5",
2526
"@tiptap/extension-color": "^3.27.3",
2627
"@tiptap/extension-highlight": "^3.27.3",
2728
"@tiptap/extension-image": "^3.27.3",

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ interface CustomTitlebarProps {
1818
* - 拖拽区域必须使用 CSS 类 `.drag-region`(禁止内联 style)
1919
* - 交互元素必须使用 CSS 类 `.no-drag`(禁止内联 style)
2020
* - 双击拖拽区域触发最大化/还原
21-
* - 深海静谧主题:深色背景 + 微妙边框 + 品牌渐变 Logo
21+
* - 自适应主题:跟随深浅模式变化
2222
*
2323
* @param props - 组件属性
2424
* @returns 自定义标题栏 JSX 元素
@@ -53,17 +53,17 @@ export function CustomTitlebar({ className }: CustomTitlebarProps) {
5353
<div
5454
className={cn(
5555
'drag-region flex items-center h-9 select-none',
56-
'bg-[#0f1f3a] border-b border-[#1e3456]',
56+
'bg-bg-secondary border-b border-border-default',
5757
className,
5858
)}
5959
>
6060
{/* 左侧:品牌 Logo + 应用名称(拖拽区域,双击最大化) */}
6161
<div className="flex items-center gap-2 pl-3 h-full" onDoubleClick={handleMaximize}>
6262
{/* 品牌 Logo — 深海青渐变圆角方块 + 内圈弧线 */}
6363
<div className="w-4 h-4 rounded-[4px] bg-gradient-to-br from-brand-400 to-brand-600 relative flex-shrink-0 shadow-[0_0_8px_rgba(91,138,114,0.3)]">
64-
<div className="absolute top-[3px] left-[3px] w-2.5 h-2.5 border border-white/50 rounded-full" />
64+
<div className="absolute top-[3px] left-[3px] w-2.5 h-2.5 border border-white/30 dark:border-white/50 rounded-full" />
6565
</div>
66-
<span className="text-xs font-medium text-[#e8edf5] tracking-wide">
66+
<span className="text-xs font-medium text-text-primary tracking-wide">
6767
课伴
6868
</span>
6969
</div>
@@ -75,14 +75,14 @@ export function CustomTitlebar({ className }: CustomTitlebarProps) {
7575
<div className="no-drag flex h-full">
7676
<button
7777
onClick={handleMinimize}
78-
className="w-11 h-9 flex items-center justify-center text-[#e8edf5]/60 hover:text-[#818cf8] hover:bg-white/5 transition-all duration-200 active:scale-90"
78+
className="w-11 h-9 flex items-center justify-center text-text-tertiary hover:text-brand-500 hover:bg-black/5 dark:hover:bg-white/5 transition-all duration-200 active:scale-90"
7979
aria-label="最小化"
8080
>
8181
<Minus className="w-3.5 h-3.5" />
8282
</button>
8383
<button
8484
onClick={handleMaximize}
85-
className="w-11 h-9 flex items-center justify-center text-[#e8edf5]/60 hover:text-[#818cf8] hover:bg-white/5 transition-all duration-200 active:scale-90"
85+
className="w-11 h-9 flex items-center justify-center text-text-tertiary hover:text-brand-500 hover:bg-black/5 dark:hover:bg-white/5 transition-all duration-200 active:scale-90"
8686
aria-label={isMaximized ? '还原' : '最大化'}
8787
>
8888
{isMaximized ? (
@@ -93,7 +93,7 @@ export function CustomTitlebar({ className }: CustomTitlebarProps) {
9393
</button>
9494
<button
9595
onClick={handleClose}
96-
className="w-11 h-9 flex items-center justify-center text-[#e8edf5]/60 hover:text-[#ef4444] hover:bg-[#ef4444]/10 transition-all duration-200 active:scale-90"
96+
className="w-11 h-9 flex items-center justify-center text-text-tertiary hover:text-error hover:bg-error/10 transition-all duration-200 active:scale-90"
9797
aria-label="关闭"
9898
>
9999
<X className="w-3.5 h-3.5" />

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ interface PageTransitionProps {
66
className?: string;
77
}
88

9+
/**
10+
* 页面过渡动画
11+
*
12+
* 性能优化:仅使用 opacity + transform(scale / translateY),
13+
* 全部命中 GPU composite 层,不触发 paint/layout 重算。
14+
* (旧版 filter: blur(4px) 会强制 paint,已在 v0.9.0 移除)
15+
*/
916
export function PageTransition({ children, className }: PageTransitionProps) {
1017
const prefersReduced = useReducedMotion();
1118

@@ -16,22 +23,20 @@ export function PageTransition({ children, className }: PageTransitionProps) {
1623
return (
1724
<motion.div
1825
className={className}
19-
initial={{ opacity: 0, y: 8, scale: 0.98, filter: 'blur(4px)' }}
26+
initial={{ opacity: 0, y: 12, scale: 0.97 }}
2027
animate={{
2128
opacity: 1,
2229
y: 0,
2330
scale: 1,
24-
filter: 'blur(0px)',
2531
transition: {
2632
duration: 0.35,
2733
ease: [0.25, 0.1, 0.25, 1],
2834
},
2935
}}
3036
exit={{
3137
opacity: 0,
32-
y: -4,
38+
y: -6,
3339
scale: 1.01,
34-
filter: 'blur(2px)',
3540
transition: { duration: 0.15, ease: [0.4, 0, 1, 1] },
3641
}}
3742
>

0 commit comments

Comments
 (0)