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
ci / test (push) Canceled after 0s
This commit is contained in:
+17
-27
@@ -1,45 +1,35 @@
|
|||||||
# ---- 依赖与构建产物 ----
|
# ---- 依赖与构建产物 ----
|
||||||
node_modules/
|
node_modules/
|
||||||
dist/
|
dist/
|
||||||
|
src-tauri/target/
|
||||||
|
src-tauri/gen/
|
||||||
|
|
||||||
# ---- 运行时数据(本地库 / 附件 / 服务端数据)----
|
# ---- 运行时数据(本地库 / 附件 / 服务端数据)----
|
||||||
.data/
|
.data/
|
||||||
data/
|
data/
|
||||||
|
|
||||||
# ---- 测试运行时数据 ----
|
# ---- 测试与调试临时目录 ----
|
||||||
.regr/
|
.tmp-test/
|
||||||
.factory*/
|
|
||||||
.wire-*/
|
|
||||||
.builtin-test/
|
|
||||||
build-log.txt
|
|
||||||
.builtin-test/
|
|
||||||
*.tmp.log
|
|
||||||
.factory-test/
|
|
||||||
.seed-dbg/
|
.seed-dbg/
|
||||||
.seed-*/
|
.dbg-*/
|
||||||
|
.regr/
|
||||||
.regression/
|
.regression/
|
||||||
.regression-s/
|
.regression-s/
|
||||||
.dbg-x/
|
|
||||||
.grade-test/
|
|
||||||
.regression-test/
|
.regression-test/
|
||||||
|
.builtin-test/
|
||||||
|
.builtin-test/
|
||||||
|
.factory*/
|
||||||
|
.factory-test/
|
||||||
|
.seed-*/
|
||||||
|
.wire-*/
|
||||||
|
.grade-test/
|
||||||
sync-server/.data-*/
|
sync-server/.data-*/
|
||||||
|
|
||||||
|
# ---- 工具与本地二进制 ----
|
||||||
tools/pnpm.exe
|
tools/pnpm.exe
|
||||||
tools/pnpm.zip
|
tools/pnpm.zip
|
||||||
%SystemDrive%/
|
|
||||||
|
|
||||||
# ---- Rust ----
|
|
||||||
src-tauri/target/
|
|
||||||
src-tauri/gen/
|
|
||||||
|
|
||||||
# ---- 自包含 pnpm:保留 pnpm.cjs,忽略解包目录 ----
|
|
||||||
tools/pnpm-tgz/
|
tools/pnpm-tgz/
|
||||||
|
|
||||||
# ---- 临时脚本与产物 ----
|
# ---- 日志 ----
|
||||||
gen_*.py
|
|
||||||
parse_docx.py
|
|
||||||
check_yaml.py
|
|
||||||
docx_content.txt
|
|
||||||
*.log
|
*.log
|
||||||
|
build-log.txt
|
||||||
# 集成测试临时目录
|
|
||||||
.tmp-test/
|
|
||||||
|
|||||||
@@ -123,7 +123,8 @@ CI:push 自动跑 check + 集成测试(见 `.gitea/workflows/ci.yml`,需
|
|||||||
├─ scripts/ # check / test-integration / smoke
|
├─ scripts/ # check / test-integration / smoke
|
||||||
├─ .gitea/workflows/ # CI
|
├─ .gitea/workflows/ # CI
|
||||||
├─ .githooks/ # pre-push 钩子
|
├─ .githooks/ # pre-push 钩子
|
||||||
└─ docs/api.md # HTTP API 参考
|
├─ docs/ # api.md(HTTP API)· DESIGN.md(设计底稿)· examples/
|
||||||
|
└─ .gitea/ .githooks/ # CI workflow 与 pre-push 钩子
|
||||||
```
|
```
|
||||||
|
|
||||||
## 环境变量(sync-server)
|
## 环境变量(sync-server)
|
||||||
@@ -140,7 +141,7 @@ CI:push 自动跑 check + 集成测试(见 `.gitea/workflows/ci.yml`,需
|
|||||||
## 文档
|
## 文档
|
||||||
|
|
||||||
- [HTTP API 参考](docs/api.md)
|
- [HTTP API 参考](docs/api.md)
|
||||||
- [设计方案(v0.5 底稿)](DESIGN.md) —— 项目的原始设计思想;现产品已演进为云优先多用户架构
|
- [设计方案(v0.5 底稿)](docs/DESIGN.md) —— 项目的原始设计思想;现产品已演进为云优先多用户架构
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
|
|||||||
+11
-51
@@ -1,56 +1,16 @@
|
|||||||
# 足迹 Footprints
|
# 足迹 Footprints — 应用目录
|
||||||
|
|
||||||
个人留痕系统 · 桌面应用(Tauri 2 + React 19 + SQLite)+ 移动端 PWA。
|
React 19 前端 + Tauri 2 桌面壳 + 共用 Node 命令层。
|
||||||
设计文档见仓库根目录 `DESIGN.md`;同步服务端见 `sync-server/`。
|
**云优先架构**:业务数据全部存服务器(`../sync-server`),浏览器 / PWA / 桌面三端共享同一命令契约。
|
||||||
|
|
||||||
## 目录结构
|
## 结构
|
||||||
|
|
||||||
app/
|
src/ React 前端(时间线 / 记录 / 成绩 / 统计 / 类型 / 字典 / 管理)
|
||||||
src/ React 前端(时间线 / 记录 / 统计 / 类型 / 字典)
|
public/ PWA:sw.js(v3,接口类请求绝不缓存)/ manifest / 图标
|
||||||
src-tauri/ Tauri 2 桌面壳 + Rust 后端(rusqlite,26 个 IPC 命令 + 同步地基)
|
src-tauri/ Tauri 2 桌面壳(Windows NSIS 安装包构建)
|
||||||
public/ PWA 清单 / Service Worker / 图标
|
server/ db.mjs 命令层(37 个命令,Web 与桌面共用)+ seed.mjs(本地演示数据)
|
||||||
server/dev-bridge.mjs 开发桥接:HTTP 暴露与 Tauri IPC 相同的命令契约
|
schema.sql 数据库 schema(Rust 与 Node 共用同一份,v2 含同步元数据)
|
||||||
server/db.mjs 桥接与种子脚本共用的数据层(node:sqlite)
|
|
||||||
server/seed.mjs 演示数据(仅当库为空时写入)
|
|
||||||
schema.sql 数据库 schema(Rust 与 Node 共用同一份,v2 含同步元数据)
|
|
||||||
tools/pnpm.cjs 自包含 pnpm(本机无全局 npm/pnpm 时使用)
|
|
||||||
sync-server/ 自建同步服务(Docker 部署,协议已冻结)
|
|
||||||
|
|
||||||
## 浏览器预览(无需 Rust)
|
图标再生成:`python ../tools/gen_icons.py`(桌面)与 `python ../tools/gen_pwa_icons.py`(PWA)。
|
||||||
|
|
||||||
node ../tools/pnpm.cjs bridge # 终端 1:数据桥接(含 dist 静态托管)
|
文档:[`../docs/DESIGN.md`](../docs/DESIGN.md)(设计底稿)· [`../docs/api.md`](../docs/api.md)(HTTP API)· [`../sync-server/`](../sync-server/)(服务器)。
|
||||||
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/`。备份 = 复制该目录。
|
|
||||||
|
|||||||
@@ -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))
|
||||||
@@ -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))
|
||||||
Reference in New Issue
Block a user