Skip to content

Commit babc350

Browse files
committed
docs: 重写用户使用 README
0 parents  commit babc350

100 files changed

Lines changed: 13972 additions & 0 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.

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* text=auto
2+

.github/workflows/ci.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
test:
9+
runs-on: windows-latest
10+
11+
steps:
12+
- name: Check out repository
13+
uses: actions/checkout@v5
14+
15+
- name: Set up Python
16+
uses: actions/setup-python@v6
17+
with:
18+
python-version: "3.13"
19+
20+
- name: Install dependencies
21+
shell: pwsh
22+
run: |
23+
python -m pip install --upgrade pip
24+
python -m pip install -e .[dev]
25+
26+
- name: Run tests
27+
shell: pwsh
28+
run: |
29+
python -m pytest tests -q

.github/workflows/release.yml

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
tags:
8+
- "v*"
9+
workflow_dispatch:
10+
inputs:
11+
tag:
12+
description: "Release tag. Leave empty to update latest."
13+
required: false
14+
type: string
15+
16+
permissions:
17+
contents: write
18+
19+
jobs:
20+
build-windows:
21+
runs-on: windows-latest
22+
23+
steps:
24+
- name: Check out repository
25+
uses: actions/checkout@v5
26+
27+
- name: Set up Python
28+
uses: actions/setup-python@v6
29+
with:
30+
python-version: "3.13"
31+
32+
- name: Set up Node.js
33+
uses: actions/setup-node@v6
34+
with:
35+
node-version: "24"
36+
37+
- name: Enable pnpm
38+
shell: pwsh
39+
run: |
40+
corepack enable
41+
corepack prepare pnpm@10 --activate
42+
43+
- name: Install dependencies
44+
shell: pwsh
45+
run: |
46+
python -m pip install --upgrade pip
47+
python -m pip install -e .[dev]
48+
pnpm install --frozen-lockfile
49+
50+
- name: Run tests
51+
shell: pwsh
52+
run: |
53+
python -m pytest tests -q
54+
55+
- name: Build Electron package
56+
shell: pwsh
57+
run: |
58+
pnpm run electron:pack
59+
60+
- name: Upload release artifact
61+
uses: actions/upload-artifact@v6
62+
with:
63+
name: weekflow-windows-release
64+
path: |
65+
dist/WeekFlow-V*.zip
66+
67+
publish-release:
68+
runs-on: ubuntu-latest
69+
needs:
70+
- build-windows
71+
72+
steps:
73+
- name: Check out repository
74+
uses: actions/checkout@v5
75+
76+
- name: Resolve release tag
77+
id: release
78+
shell: bash
79+
run: |
80+
PACKAGE_VERSION="$(python -c 'import json; print(json.load(open("package.json", encoding="utf-8"))["version"])')"
81+
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" && -n "${{ inputs.tag }}" ]]; then
82+
TAG_NAME="${{ inputs.tag }}"
83+
elif [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
84+
TAG_NAME="${GITHUB_REF_NAME}"
85+
else
86+
TAG_NAME="latest"
87+
fi
88+
TITLE="WeekFlow-V$PACKAGE_VERSION"
89+
echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT"
90+
echo "title=$TITLE" >> "$GITHUB_OUTPUT"
91+
92+
- name: Download built artifacts
93+
uses: actions/download-artifact@v6
94+
with:
95+
path: release-assets
96+
merge-multiple: true
97+
98+
- name: Publish GitHub release assets
99+
shell: bash
100+
env:
101+
GH_TOKEN: ${{ github.token }}
102+
GH_REPO: ${{ github.repository }}
103+
TAG_NAME: ${{ steps.release.outputs.tag_name }}
104+
TITLE: ${{ steps.release.outputs.title }}
105+
run: |
106+
ls -la release-assets
107+
if gh release view "$TAG_NAME" --repo "$GH_REPO" >/dev/null 2>&1; then
108+
gh release edit "$TAG_NAME" --repo "$GH_REPO" --title "$TITLE"
109+
if [[ "$TAG_NAME" == "latest" ]]; then
110+
gh api --method PATCH "repos/$GH_REPO/git/refs/tags/$TAG_NAME" -f sha="$GITHUB_SHA" -F force=true
111+
fi
112+
gh release view "$TAG_NAME" --repo "$GH_REPO" --json assets --jq '.assets[].name' |
113+
while read -r asset_name; do
114+
if [[ "$asset_name" == WeekFlow-* ]]; then
115+
gh release delete-asset "$TAG_NAME" "$asset_name" --repo "$GH_REPO" --yes
116+
fi
117+
done
118+
gh release upload "$TAG_NAME" release-assets/* --repo "$GH_REPO" --clobber
119+
else
120+
gh release create "$TAG_NAME" release-assets/* --repo "$GH_REPO" --title "$TITLE" --generate-notes --latest
121+
fi

.gitignore

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Local Python environments and caches
2+
.venv/
3+
.worktrees/
4+
.pytest_cache/
5+
__pycache__/
6+
*.py[cod]
7+
*.egg-info/
8+
9+
# Build outputs
10+
build/
11+
dist/
12+
dist-electron/
13+
releases/
14+
WeekFlow.spec
15+
16+
# JavaScript/Electron dependencies
17+
node_modules/
18+
# Local environment files
19+
.env
20+
21+
# OS-generated files
22+
.DS_Store
23+
Thumbs.db
24+
25+
# Generated app icons
26+
src/weekflow_logo.icns

.scripts/build_electron_bridge.ps1

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
$ErrorActionPreference = "Stop"
2+
3+
$root = Split-Path -Parent $PSScriptRoot
4+
Set-Location $root
5+
6+
$python = ".\.venv\Scripts\python.exe"
7+
if (-not (Test-Path $python)) {
8+
$python = "python"
9+
}
10+
11+
$src = Join-Path $root "src\WeekFlow\electron_bridge.py"
12+
$distPath = Join-Path $root "dist\bridge"
13+
$workPath = Join-Path $root "build\electron-bridge"
14+
$iconPath = Join-Path $root "src\weekflow_logo.ico"
15+
16+
if (-not (Test-Path $src)) {
17+
throw "Electron bridge entry not found at $src"
18+
}
19+
20+
if (Test-Path $distPath) {
21+
Remove-Item -LiteralPath $distPath -Recurse -Force
22+
}
23+
24+
$args = @(
25+
"-m", "PyInstaller",
26+
"--noconfirm",
27+
"--clean",
28+
"--onefile",
29+
"--name", "WeekFlowBridge",
30+
"--distpath", $distPath,
31+
"--workpath", $workPath,
32+
"--specpath", $workPath,
33+
"--paths", (Join-Path $root "src")
34+
)
35+
36+
if (Test-Path $iconPath) {
37+
$args += @("--icon", $iconPath)
38+
}
39+
40+
$args += $src
41+
42+
& $python @args
43+
44+
$bridgeExe = Join-Path $distPath "WeekFlowBridge.exe"
45+
if (-not (Test-Path $bridgeExe)) {
46+
throw "Electron bridge build did not create $bridgeExe"
47+
}
48+
49+
if (Test-Path $workPath) {
50+
Remove-Item -LiteralPath $workPath -Recurse -Force
51+
}
52+
53+
Write-Output $bridgeExe

.scripts/build_exe.ps1

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
$ErrorActionPreference = "Stop"
2+
3+
$root = Split-Path -Parent $PSScriptRoot
4+
Set-Location $root
5+
6+
$python = ".\.venv\Scripts\python.exe"
7+
if (-not (Test-Path $python)) {
8+
$python = "python"
9+
}
10+
11+
$icoIcon = Join-Path $root "src\weekflow_logo.ico"
12+
13+
if (-not (Test-Path $icoIcon)) {
14+
throw "App icon not found at $icoIcon"
15+
}
16+
17+
& $python -m PyInstaller `
18+
--noconfirm `
19+
--clean `
20+
--windowed `
21+
--name WeekFlow `
22+
--icon $icoIcon `
23+
--add-data "src\weekflow_logo.ico;src" `
24+
run_app.py
25+
26+
$appDataDir = Join-Path $root "dist\WeekFlow\data"
27+
New-Item -ItemType Directory -Force -Path $appDataDir | Out-Null

.scripts/build_macos_app.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5+
cd "$ROOT"
6+
7+
PYTHON_BIN="$ROOT/.venv/bin/python"
8+
ICO_ICON="$ROOT/src/weekflow_logo.ico"
9+
ICNS_ICON="$ROOT/src/weekflow_logo.icns"
10+
11+
if [[ ! -x "$PYTHON_BIN" ]]; then
12+
echo "Project virtual environment not found. Create .venv and install dependencies first." >&2
13+
exit 1
14+
fi
15+
16+
if [[ ! -f "$ICO_ICON" ]]; then
17+
echo "App icon not found at $ICO_ICON" >&2
18+
exit 1
19+
fi
20+
21+
"$PYTHON_BIN" "$ROOT/.scripts/export_app_icon.py" "$ICO_ICON" "$ICNS_ICON"
22+
23+
"$PYTHON_BIN" -m PyInstaller \
24+
--noconfirm \
25+
--clean \
26+
--windowed \
27+
--name WeekFlow \
28+
--icon "$ICNS_ICON" \
29+
--add-data "src/weekflow_logo.ico:src" \
30+
--osx-bundle-identifier "com.cimorn.weekflow" \
31+
run_app.py

.scripts/export_app_icon.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from __future__ import annotations
2+
3+
import sys
4+
from pathlib import Path
5+
6+
from PySide6.QtCore import QRectF, Qt
7+
from PySide6.QtGui import QColor, QGuiApplication, QImage, QPainter
8+
from PySide6.QtSvg import QSvgRenderer
9+
10+
11+
FORMAT_BY_SUFFIX = {
12+
".ico": "ICO",
13+
".icns": "ICNS",
14+
}
15+
16+
17+
def export_icon(source: Path, destination: Path, size: int = 256) -> None:
18+
image_format = FORMAT_BY_SUFFIX.get(destination.suffix.lower())
19+
if image_format is None:
20+
supported = ", ".join(sorted(FORMAT_BY_SUFFIX))
21+
raise RuntimeError(f"Unsupported icon format for {destination}. Expected one of: {supported}")
22+
23+
if source.suffix.lower() == ".svg":
24+
renderer = QSvgRenderer(str(source))
25+
if not renderer.isValid():
26+
raise RuntimeError(f"Invalid SVG icon: {source}")
27+
28+
image = QImage(size, size, QImage.Format_ARGB32)
29+
image.fill(Qt.GlobalColor.transparent)
30+
31+
painter = QPainter(image)
32+
try:
33+
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
34+
painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform, True)
35+
painter.fillRect(image.rect(), QColor(0, 0, 0, 0))
36+
renderer.render(painter, QRectF(0, 0, size, size))
37+
finally:
38+
painter.end()
39+
else:
40+
image = QImage(str(source))
41+
if image.isNull():
42+
raise RuntimeError(f"Invalid icon source: {source}")
43+
44+
destination.parent.mkdir(parents=True, exist_ok=True)
45+
if not image.save(str(destination), image_format):
46+
raise RuntimeError(f"Failed to write icon file: {destination}")
47+
48+
49+
def main() -> int:
50+
if len(sys.argv) != 3:
51+
raise SystemExit("Usage: export_app_icon.py <source-image> <destination-icon>")
52+
53+
app = QGuiApplication([])
54+
source = Path(sys.argv[1]).resolve()
55+
destination = Path(sys.argv[2]).resolve()
56+
export_icon(source, destination)
57+
app.quit()
58+
return 0
59+
60+
61+
if __name__ == "__main__":
62+
raise SystemExit(main())

0 commit comments

Comments
 (0)