Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Agent Instructions

Before changing this project, read `ARCHITECTURE.md` at the repository root.

Use that file as the source of truth for:

- Electron main/preload/renderer/shared boundaries
- local JSON persistence and backup behavior
- overlay BrowserWindow architecture
- release and local install verification notes

Do not treat `release`, `downloads`, `backups`, `coverage`, `out`, `node_modules`, or `sources` as normal source areas unless the user explicitly asks to work on those artifacts.
222 changes: 222 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
# Timetable Architecture Notes

This file is the local architecture handoff for future Codex sessions. Read it before changing the app.

## Product Shape

Timetable is a local-first Windows desktop planner built with Electron, React, TypeScript, electron-vite, Tailwind CSS, Zustand, and local JSON persistence.

The app has no backend. The important runtime artifact is the Electron `userData` directory and its `app-data.json` file, not the source checkout itself.

## Runtime Layers

```text
Electron main process
-> AppStorage: local data, schema migration, atomic saves, backups
-> WindowManager facade: coordinates main window and desktop overlay BrowserWindows
-> windowing controllers: main window, overlay windows, pure geometry helpers
-> IPC handlers: bridge renderer requests to storage/windows/system APIs
-> BrowserUsageTracker: Windows active-browser and AI-tool usage sampling
-> Tray/startup helpers

Preload
-> exposes window.timeable via contextBridge
-> renderer never calls Electron or Node APIs directly

Renderer
-> React app using HashRouter
-> Zustand appStore wraps window.timeable calls
-> normal pages under src/renderer/pages
-> desktop widgets under src/renderer/overlay

Shared
-> AppData types, IPC types, reducer, defaults, migrations, pure utilities
-> imported by both main and renderer where safe
```

## Key Entrypoints

- `src/main/index.ts`: app bootstrap. Creates storage, windows, tray, IPC, and browser usage tracking.
- `src/main/storage.ts`: authoritative local data service. Handles load, normalize, migration, atomic save, backup, restore, export, and usage snapshots.
- `src/main/windows.ts`: public WindowManager facade used by bootstrap and IPC.
- `src/main/windowing/mainWindowController.ts`: owns the frameless main window.
- `src/main/windowing/overlayWindowController.ts`: owns separate transparent overlay windows.
- `src/main/windowing/geometry.ts`: pure window sizing, bounds, opacity, and edge-collapse helpers.
- `src/main/ipc.ts`: registers all IPC handlers.
- `src/preload/index.ts`: exposes the typed `window.timeable` API.
- `src/renderer/App.tsx`: route tree for normal pages and overlay routes.
- `src/renderer/store/appStore.ts`: renderer state and async actions around `window.timeable`.
- `src/shared/types/app.ts`: `AppData` and domain model types.
- `src/shared/ipc.ts`: shared renderer/main API contract.
- `src/shared/data/defaults.ts`: default `AppData`.
- `src/shared/data/migrations.ts`: `schemaVersion` migration entrypoint.
- `src/shared/data/reducer.ts`: pure mutations for courses, tasks, goals, memos, settings, and widgets.

## Data Model And Persistence

The main persisted object is `AppData`.

Current important fields:

- `schemaVersion`: top-level data schema version.
- `courses`
- `dailyTasks`
- `longTermGoals`
- `memos`
- `countdownItems`: user-created countdown targets.
- `principleCard`
- `countdownCard`: countdown widget settings plus optional `pinnedItemId`.
- `desktopSettings`
- `appSettings`
- `browserUsage`

Persistence rules:

- Data file path is `app.getPath('userData')/app-data.json`.
- Saves are atomic: write a temp file beside `app-data.json`, then rename it over the target.
- Corrupt JSON is preserved as `app-data.corrupt-<timestamp>.json`, then defaults are recreated.
- Legacy or missing schema data flows through `migrateAppData()` before normalization.
- Daily, migration, and manual backups live under `userData/backups`.
- Backup summaries use `DataBackupSummary` from `src/shared/ipc.ts`.
- The "Data and Startup" renderer page exposes backup listing and restore UI.

Important local-machine constraint:

- This machine has previously had both `AppData/Roaming/Timetable` and `AppData/Roaming/timeable`.
- Do not assume the lowercase or uppercase directory is authoritative.
- For upgrade/replacement work, validate JSON contents and the actual Electron `userData` path.

## Window And Overlay Model

The main app window is a frameless BrowserWindow loading the normal React route tree.

Desktop widgets are not DOM overlays inside the main window. They are separate transparent BrowserWindows managed by `WindowManager`, each loading:

```text
#/overlay/<widgetKey>
```

Known widget keys:

- `mainPanel`
- `dailyTasks`
- `memo`
- `countdown`
- `principle`

Overlay behavior owned by `OverlayWindowController` behind the `WindowManager` facade:

- create/destroy overlay windows based on `desktopSettings.widgets`
- always-on-top and opacity
- drag/resize bounds persistence
- screen-bound constraints
- edge auto-hide and hover expansion

Be careful changing overlay behavior. It couples renderer widget config, Electron window bounds, and persisted widget settings.

Keep `WindowManager` public methods stable unless all callers are updated. `index.ts` and `ipc.ts` should not need to know whether main-window or overlay-window internals changed.

## Renderer State Flow

Normal update path:

```text
page component
-> useAppStore action
-> window.timeable API
-> IPC handler
-> AppStorage update
-> WindowManager sync/broadcast
-> renderer receives data:changed
```

Full-data updates still use `data:changed`.

High-frequency updates may use `data:patched`:

- `widget/replace` patches update one desktop widget after overlay movement, resize, or widget config changes.
- `browserUsage/dayReplace` patches update one browser-usage day after background sampling.

The full `data:changed` path remains the compatibility contract for startup, ordinary business mutations, settings, restore, export, and any change that is not explicitly patchable. Renderer code should apply patches only on top of already-loaded `AppData`; if data is not loaded yet, wait for `loadData()` or the next full `data:changed`.

## Countdown Items

The countdown page supports multiple user-created `countdownItems`.

The desktop countdown overlay still uses the single `countdown` widget key and one BrowserWindow. It displays `countdownCard.pinnedItemId` when that item exists. If no item is pinned, the pinned item was deleted, or there are no custom countdowns, the overlay falls back to the original "today remaining" countdown plus task completion summary.

## Browser Usage Tracking

`src/main/browserUsageTracker.ts` is Windows-specific and uses PowerShell/UIAutomation to inspect the foreground window.

It detects:

- browser URLs
- AI web services such as ChatGPT, Claude, Gemini, DeepSeek, Kimi
- AI desktop/terminal tools by process/title heuristics

This should remain isolated from app startup correctness. Failures should degrade to no usage sample, not prevent the app from running.

## Build And Test

Scripts:

- `npm run dev`: electron-vite dev
- `npm run typecheck`: app and node TypeScript checks
- `npm test`: Vitest with coverage
- `npm run build`: typecheck plus electron-vite build
- `npm run pack:win`: unpacked Windows build
- `npm run dist:win`: NSIS installer
- `npm run dist:portable`: portable executable
- `npm run release:win`: installer plus portable

`vitest.config.ts` excludes historical and generated folders such as `sources`, `release`, `downloads`, and `backups`. Do not remove those excludes unless the historical snapshots are intentionally brought under test.

## Release And Local Install Notes

For local replacement or upgrade tasks:

- First verify the actual running executable:

```powershell
Get-Process Timetable,Timeable -ErrorAction SilentlyContinue | Select-Object ProcessName,Id,Path
```

- Do not infer the active app from newest folder names.
- Verify the desktop shortcut target if the user launches through a shortcut.
- Preserve and validate local user data before switching builds.
- The active local release can be outside the current source build output.

## Current Optimization State

Implemented:

- top-level `AppData.schemaVersion`
- migration entrypoint
- atomic `app-data.json` saves
- corrupt JSON preservation
- daily/migration/manual backups
- backup listing and restore API
- backup/restore UI on the Data and Startup page
- user-created countdown items with one pinned desktop countdown
- settings widget deep-merge behavior
- `WindowManager` split into main-window controller, overlay-window controller, and pure geometry helpers
- `data:patched` broadcasts for high-frequency widget and browser-usage updates
- tests for storage safety and reducer widget merge behavior
- tests for pure window geometry behavior

Still good next steps:

- isolate browser usage tracking further from storage/UI broadcast load
- clean repository hygiene around generated artifacts and historical source snapshots
- add renderer tests if a renderer test harness is introduced

## Change Guidelines

- Prefer existing patterns over new frameworks.
- Keep data migrations pure and testable.
- Keep renderer access to system APIs behind `window.timeable`.
- Never bypass `AppStorage` for persisted app data.
- When modifying user data behavior, add storage tests first.
- When modifying overlays, test bounds, resize, hide/show, and persisted widget settings.
- Keep release folders, backups, downloads, and source snapshots out of normal app logic.
97 changes: 46 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,59 +1,26 @@
# Timetable

Timetable 是一个本地优先的 Windows 桌面规划工具,用来管理课程表、每日任务、长期目标、备忘录、倒计时、道理卡片、桌面挂件和时间统计
Timetable 是一个本地 Windows 桌面应用,用来统一管理课程表、每日任务、长期目标、备忘录、倒计时卡片和桌面悬浮挂件

## 直接下载
## 技术栈

[点击下载 Windows 安装包 setup.exe](https://github.com/Evander764/Timetable/releases/download/v0.3.3/Timetable-0.3.3-x64-setup.exe)
- Electron
- React
- TypeScript
- electron-vite
- Tailwind CSS
- Zustand
- lucide-react
- 本地 JSON 持久化

不想安装时,也可以下载解压版:

[下载 win-unpacked 压缩包](https://github.com/Evander764/Timetable/releases/download/v0.3.3/Timetable-win-unpacked-v0.3.3.zip)

## 运行提示

- 当前版本支持 Windows x64。
- 安装包是普通用户安装,不需要管理员权限。
- 当前安装包没有数字签名,Windows 可能显示“未知发布者”或 SmartScreen 提示。
- 如果你信任这个仓库,可以在提示中点击“更多信息”,然后选择“仍要运行”。
- Release 附带 `SHA256SUMS.txt`,可用于校验下载文件。

## 主要功能

- 课程表:支持学期开始日期、总周数、单/双周课程、自定义课表时间。
- 今日行动中心:自动判断正在上课、下一节课、今日课程结束或今日无课。
- 道理卡片:支持多张卡片、手动切换、自动轮换和翻页动画。
- 桌面挂件:支持主面板、倒计时、备忘录、任务卡片和道理卡片。
- 时间统计:按天统计前台网页和 AI 应用使用时间,AdsPower 不会被计入 Claude。
- 托盘退出:可设置右上角关闭按钮是退出程序还是隐藏到托盘,也可开启“仅托盘退出”。
- 数据备份:支持自动备份、手动备份、备份恢复和恢复前保护备份。

## 数据与隐私

- 这个仓库不包含任何个人应用数据。
- 应用运行数据保存在 Electron 的 `userData/app-data.json`。
- 备份文件保存在 `userData/backups`。
- 更新应用包不会覆盖原来的 JSON 数据。
- 导出的备份、本地构建产物和运行日志不会提交到 Git。

## 自动更新

打包后的应用启动时会检查 GitHub 最新 Release:

```text
https://github.com/Evander764/Timetable/releases/latest
```

发现新版本后,应用会显示版本说明和确认按钮。用户确认后,程序会先备份当前用户数据,再下载 Release 中的 `app.asar`,校验 `SHA256SUMS.txt`,替换资源包并自动重启。用户数据仍然保留在本机 `userData` 目录,新版本会继续使用原来的数据。

## 本地开发
## 开发命令

```bash
npm install
npm run dev
```

常用命令
其他命令

```bash
npm run lint
Expand All @@ -65,11 +32,39 @@ npm run dist:portable
npm run release:win
```

## 发布新版本
## 数据存储

- 默认数据文件位于 Electron `userData` 目录下的 `app-data.json`
- 所有数据仅保存在本地
- 修改后默认自动保存,带 500ms debounce
- 支持导出当前数据为 JSON 文件

## 主要功能

- 控制中心窗口:总览、桌面面板、课程表、每日任务、长期任务、备忘录、倒计时卡片、道理卡片、背景与设置、数据与启动
- 桌面透明挂件:主面板、每日任务、进行中备忘、倒计时、道理卡片
- 桌面卡片位置、尺寸、透明度和显隐状态持久化
- 开机启动、本地背景图切换、数据导出

## Windows 打包

- `npm run pack:win`:生成未安装的目录版本,便于本地检查打包结果
- `npm run dist:win`:生成 Windows `nsis` 安装包
- `npm run dist:portable`:生成 Windows 便携版可执行文件
- `npm run release:win`:一次性生成安装包和便携版

默认输出目录会带版本号和时间戳,避免覆盖旧产物:

```text
release/<version>-<yyyymmddhhmmss>/
```

## 测试覆盖

1. 提高 `package.json` 和 `package-lock.json` 的版本号。
2. 执行 `npm run release:win`。
3. 上传 `app.asar`、`setup.exe`、便携版、win-unpacked zip 和 `SHA256SUMS.txt`。
4. 创建 GitHub Release,例如 `v0.3.3`。
当前包含以下自动化测试:

当前最新公开版本是 `v0.3.3`。
- 课程显示规则与下一节课计算
- 每日任务完成率、连续打卡与重复规则
- 长期目标阶段推进
- 数据 reducer 更新
- JSON 默认数据创建与自动保存
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "timetable",
"name": "timeable",
"productName": "Timetable",
"private": false,
"version": "0.3.3",
"version": "0.2.0",
"description": "A local Windows desktop planner for courses, tasks, goals, memos, countdowns, and desktop widgets.",
"author": "Timetable Local Builder",
"main": "out/main/index.js",
Expand Down
Loading