chore: tidy repo - DESIGN.md & scoring rules into docs/, icon generators into tools/, drop one-off junk, refresh app README
ci / test (push) Canceled after 0s

This commit is contained in:
root
2026-10-16 22:35:28 +08:00
parent f938870ac5
commit cc32a3d362
7 changed files with 106 additions and 80 deletions
+17 -27
View File
@@ -1,45 +1,35 @@
# ---- 依赖与构建产物 ----
node_modules/
dist/
src-tauri/target/
src-tauri/gen/
# ---- 运行时数据(本地库 / 附件 / 服务端数据)----
.data/
data/
# ---- 测试运行时数据 ----
.regr/
.factory*/
.wire-*/
.builtin-test/
build-log.txt
.builtin-test/
*.tmp.log
.factory-test/
# ---- 测试与调试临时目录 ----
.tmp-test/
.seed-dbg/
.seed-*/
.dbg-*/
.regr/
.regression/
.regression-s/
.dbg-x/
.grade-test/
.regression-test/
.builtin-test/
.builtin-test/
.factory*/
.factory-test/
.seed-*/
.wire-*/
.grade-test/
sync-server/.data-*/
# ---- 工具与本地二进制 ----
tools/pnpm.exe
tools/pnpm.zip
%SystemDrive%/
# ---- Rust ----
src-tauri/target/
src-tauri/gen/
# ---- 自包含 pnpm:保留 pnpm.cjs,忽略解包目录 ----
tools/pnpm-tgz/
# ---- 临时脚本与产物 ----
gen_*.py
parse_docx.py
check_yaml.py
docx_content.txt
# ---- 日志 ----
*.log
# 集成测试临时目录
.tmp-test/
build-log.txt
+3 -2
View File
@@ -123,7 +123,8 @@ CI:push 自动跑 check + 集成测试(见 `.gitea/workflows/ci.yml`,需
├─ scripts/ # check / test-integration / smoke
├─ .gitea/workflows/ # CI
├─ .githooks/ # pre-push 钩子
└─ docs/api.md # HTTP API 参考
├─ docs/ # api.md(HTTP API)· DESIGN.md(设计底稿)· examples/
└─ .gitea/ .githooks/ # CI workflow 与 pre-push 钩子
```
## 环境变量(sync-server)
@@ -140,7 +141,7 @@ CI:push 自动跑 check + 集成测试(见 `.gitea/workflows/ci.yml`,需
## 文档
- [HTTP API 参考](docs/api.md)
- [设计方案(v0.5 底稿)](DESIGN.md) —— 项目的原始设计思想;现产品已演进为云优先多用户架构
- [设计方案(v0.5 底稿)](docs/DESIGN.md) —— 项目的原始设计思想;现产品已演进为云优先多用户架构
## Roadmap
+11 -51
View File
@@ -1,56 +1,16 @@
# 足迹 Footprints
# 足迹 Footprints — 应用目录
个人留痕系统 · 桌面应用(Tauri 2 + React 19 + SQLite)+ 移动端 PWA。
设计文档见仓库根目录 `DESIGN.md`;同步服务端见 `sync-server/`。
React 19 前端 + Tauri 2 桌面壳 + 共用 Node 命令层。
**云优先架构**:业务数据全部存服务器(`../sync-server`),浏览器 / PWA / 桌面三端共享同一命令契约。
## 目录结构
## 结构
app/
src/ React 前端(时间线 / 记录 / 统计 / 类型 / 字典)
src-tauri/ Tauri 2 桌面壳 + Rust 后端(rusqlite,26 个 IPC 命令 + 同步地基)
public/ PWA 清单 / Service Worker / 图标
server/dev-bridge.mjs 开发桥接:HTTP 暴露与 Tauri IPC 相同的命令契约
server/db.mjs 桥接与种子脚本共用的数据层(node:sqlite)
server/seed.mjs 演示数据(仅当库为空时写入)
schema.sql 数据库 schema(Rust 与 Node 共用同一份,v2 含同步元数据)
tools/pnpm.cjs 自包含 pnpm(本机无全局 npm/pnpm 时使用)
sync-server/ 自建同步服务(Docker 部署,协议已冻结)
src/ React 前端(时间线 / 记录 / 成绩 / 统计 / 类型 / 字典 / 管理)
public/ PWA:sw.js(v3,接口类请求绝不缓存)/ manifest / 图标
src-tauri/ Tauri 2 桌面壳(Windows NSIS 安装包构建)
server/ db.mjs 命令层(37 个命令,Web 与桌面共用)+ seed.mjs(本地演示数据)
schema.sql 数据库 schema(Rust 与 Node 共用同一份,v2 含同步元数据)
## 浏览器预览(无需 Rust)
图标再生成:`python ../tools/gen_icons.py`(桌面)与 `python ../tools/gen_pwa_icons.py`(PWA)。
node ../tools/pnpm.cjs bridge # 终端 1:数据桥接(含 dist 静态托管)
node ../tools/pnpm.cjs dev # 终端 2:前端热更(http://localhost:5173)
或构建后直接访问桥接托管页:
node ../tools/pnpm.cjs build # tsc 类型检查 + vite 打包(含 PWA 资源)
node server/seed.mjs # 可选:写入演示数据
## 桌面应用打包(Windows)
前置:Rust stable-msvc(scripts/setup-rust.ps1 一键安装,MSVC Build Tools 需已具备)。
node ../tools/pnpm.cjs build # 先产出 dist
node ../tools/pnpm.cjs tauri build
产物:`src-tauri/target/release/bundle/nsis/*-setup.exe`(NSIS 安装包)。
开发模式:`node ../tools/pnpm.cjs tauri dev`。
## 移动端(PWA)
前端已做响应式与 PWA 改造(manifest + Service Worker + 图标):
- 桌面壳内运行时自动跳过 SW 注册
- 浏览器/手机访问托管页时自动注册 SW,可"添加到主屏幕"离线壳启动
- 同步服务部署后,手机 PWA 直连同一账号数据
原生 Android 壳(Tauri mobile)为可选后续项,需要 Android SDK/NDK;iOS 构建需 macOS。
## 命令契约
前端统一经 `src/lib/api.ts` 调用:浏览器模式走 `POST /api/<command>`(开发桥接),
桌面模式走 Tauri `invoke(command, { payload })`。两端实现同一组命令,
含多端同步地基:每行携带 uuid/hlc/origin,删除为墓碑,写入进 sync_oplog。
数据与附件位置:桌面版在系统应用数据目录(`%APPDATA%/com.footprints.app`),
开发预览在 `app/.data/`。备份 = 复制该目录。
文档:[`../docs/DESIGN.md`](../docs/DESIGN.md)(设计底稿)· [`../docs/api.md`](../docs/api.md)(HTTP API)· [`../sync-server/`](../sync-server/)(服务器)。
View File
+41
View File
@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
# 生成 Footprints 应用图标:品牌蓝圆角方块 + 白色抽象足迹
from PIL import Image, ImageDraw
import os
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'app', 'src-tauri', 'icons')
os.makedirs(OUT, exist_ok=True)
BRAND = (48, 86, 211)
BRAND_DARK = (35, 62, 160)
def make(size):
ss = size * 4 # 超采样抗锯齿
img = Image.new('RGBA', (ss, ss), (0, 0, 0, 0))
d = ImageDraw.Draw(img)
radius = ss // 5
# 背景圆角方块(上浅下深的纵向渐变)
grad = Image.new('RGBA', (ss, ss), (0, 0, 0, 0))
gd = ImageDraw.Draw(grad)
for y in range(ss):
t = y / ss
c = tuple(int(BRAND[i] + (BRAND_DARK[i] - BRAND[i]) * t) for i in range(3)) + (255,)
gd.line([(0, y), (ss, y)], fill=c)
mask = Image.new('L', (ss, ss), 0)
md = ImageDraw.Draw(mask)
md.rounded_rectangle([0, 0, ss - 1, ss - 1], radius=radius, fill=255)
img.paste(grad, (0, 0), mask)
# 抽象足迹:前掌大椭圆 + 后跟小椭圆
white = (255, 255, 255, 240)
d.ellipse([ss*0.24, ss*0.16, ss*0.60, ss*0.62], fill=white) # 前掌
d.ellipse([ss*0.56, ss*0.58, ss*0.86, ss*0.84], fill=(255, 255, 255, 200)) # 后跟
return img.resize((size, size), Image.LANCZOS)
for name, size in [('32x32.png', 32), ('128x128.png', 128), ('128x128@2x.png', 256), ('icon.png', 512)]:
make(size).save(os.path.join(OUT, name))
ico_sizes = [16, 24, 32, 48, 64, 128, 256]
imgs = [make(s) for s in ico_sizes]
imgs[-1].save(os.path.join(OUT, 'icon.ico'), format='ICO', sizes=[(s, s) for s in ico_sizes])
print('icons generated:', os.listdir(OUT))
+34
View File
@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
from PIL import Image, ImageDraw
import os
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'app', 'public', 'icons')
os.makedirs(OUT, exist_ok=True)
BRAND = (48, 86, 211)
BRAND_DARK = (35, 62, 160)
def make(size, safe=1.0):
ss = size * 4
img = Image.new('RGBA', (ss, ss), (0, 0, 0, 0))
grad = Image.new('RGBA', (ss, ss), (0, 0, 0, 0))
gd = ImageDraw.Draw(grad)
for y in range(ss):
t = y / ss
c = tuple(int(BRAND[i] + (BRAND_DARK[i] - BRAND[i]) * t) for i in range(3)) + (255,)
gd.line([(0, y), (ss, y)], fill=c)
mask = Image.new('L', (ss, ss), 0)
ImageDraw.Draw(mask).rounded_rectangle([0, 0, ss - 1, ss - 1], radius=ss // 5, fill=255)
img.paste(grad, (0, 0), mask)
d = ImageDraw.Draw(img)
white = (255, 255, 255, 240)
# safe 缩放:maskable 时内容收缩到中心 70% 区域
cx, cy, k = ss / 2, ss / 2, safe
d.ellipse([cx - 0.18*ss*k, cy - 0.34*ss*k, cx + 0.18*ss*k, cy + 0.10*ss*k], fill=white)
d.ellipse([cx + 0.03*ss*k, cy + 0.08*ss*k, cx + 0.33*ss*k, cy + 0.34*ss*k], fill=(255, 255, 255, 200))
return img.resize((size, size), Image.LANCZOS)
make(192).save(os.path.join(OUT, 'pwa-192.png'))
make(512).save(os.path.join(OUT, 'pwa-512.png'))
make(512, safe=0.7).save(os.path.join(OUT, 'maskable-512.png'))
print('pwa icons:', os.listdir(OUT))