95 Commits

Author SHA1 Message Date
281f53c049 Optimized markdown preview performance 2025-12-07 00:09:52 +08:00
71ca541f78 🚧 Added support for markdown preview table 2025-12-04 00:47:51 +08:00
91f4f4afac Merge branch 'markdown'
# Conflicts:
#	frontend/package-lock.json
2025-12-03 00:46:17 +08:00
fc5639d7bd 🚧 Added support for markdown preview math 2025-12-03 00:45:01 +08:00
dependabot[bot]
6668c11846 ⬆️ Bump mdast-util-to-hast from 13.2.0 to 13.2.1 in /frontend
Bumps [mdast-util-to-hast](https://github.com/syntax-tree/mdast-util-to-hast) from 13.2.0 to 13.2.1.
- [Release notes](https://github.com/syntax-tree/mdast-util-to-hast/releases)
- [Commits](https://github.com/syntax-tree/mdast-util-to-hast/compare/13.2.0...13.2.1)

---
updated-dependencies:
- dependency-name: mdast-util-to-hast
  dependency-version: 13.2.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-02 04:14:35 +00:00
17f3351cea 🚧 Added support for markdown preview footnotes 2025-12-02 00:22:22 +08:00
dd3dd4ddb2 🚧 Refactor markdown preview extension 2025-12-01 00:00:05 +08:00
60d1494d45 🚧 Refactor markdown preview extension 2025-11-30 01:09:31 +08:00
1ef5350b3f 🚧 Refactor markdown preview extension 2025-11-29 22:54:38 +08:00
3521e5787b 🚧 Refactor markdown preview extension 2025-11-29 19:24:20 +08:00
8d9bcdad7e 🚧 Refactor markdown preview extension 2025-11-28 00:38:38 +08:00
ac086db1ed ♻️ Updated markdown preview extension 2025-11-26 22:11:16 +08:00
6dff0181d2 ♻️ Refactored markdown preview extension 2025-11-24 00:10:28 +08:00
ad24d3a140 ♻️ Refactored translation extension 2025-11-23 18:45:49 +08:00
4b0f39d747 Merge branch 'master' into dev 2025-11-21 23:37:36 +08:00
096cc1da94 🎨 Optimize hyperlink extension 2025-11-21 23:35:42 +08:00
2d3200ad97 ♻️ Refactor context menu 2025-11-21 22:30:47 +08:00
4e82e2f6f7 ♻️ Refactor the Markdown preview theme application logic 2025-11-21 20:20:06 +08:00
339ed53c2e ♻️ Refactor theme module 2025-11-21 00:03:03 +08:00
fc7c162e2f ♻️ Refactor theme module 2025-11-20 23:07:12 +08:00
dependabot[bot]
24f1549730 ⬆️ Bump golang.org/x/crypto from 0.44.0 to 0.45.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.44.0 to 0.45.0.
- [Commits](https://github.com/golang/crypto/compare/v0.44.0...v0.45.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.45.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-20 03:04:41 +00:00
5584a46ca2 ♻️ Refactor theme module 2025-11-20 00:39:00 +08:00
4471441d6f ♻️ Refactor some code 2025-11-19 20:54:58 +08:00
991a89147e 🎨 Optimize code 2025-11-17 23:14:58 +08:00
a08c0d8448 🎨 Modify code block logic 2025-11-17 22:11:16 +08:00
59db8dd177 Added Monocraft font 2025-11-16 22:04:02 +08:00
29693f1baf 💄 Modify some styles 2025-11-16 21:23:59 +08:00
5d6f157ae1 🎨 Update initial code block definition
Some checks failed
Build and Release Voidraft / prepare (push) Failing after 2s
Build and Release Voidraft / build (push) Has been cancelled
Build and Release Voidraft / release (push) Has been cancelled
2025-11-16 17:49:19 +08:00
afda3d5301 Merge remote-tracking branch 'github/dependabot/npm_and_yarn/frontend/js-yaml-4.1.1' 2025-11-16 15:44:58 +08:00
5d4ba757aa Optimize HTTP client 2025-11-16 15:41:16 +08:00
d12d58b15a 🐛 Fixed markdown preview issue 2025-11-16 15:22:49 +08:00
627c3dc71f 🐛 Fixed markdown preview issue 2025-11-16 15:16:49 +08:00
26c7a3241c 🐛 Fixed module path issues 2025-11-16 02:42:59 +08:00
46c5e3dd1a Added markdown and mermaid preview 2025-11-16 02:42:01 +08:00
dependabot[bot]
92a6c6bfdb ⬆️ Bump js-yaml from 4.1.0 to 4.1.1 in /frontend
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.1.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-15 18:38:56 +00:00
031aa49f9f Added markdown and mermaid preview 2025-11-16 02:37:30 +08:00
1d7aee4cea 🐛 Fixed data migration issues 2025-11-14 20:16:16 +08:00
dec3ef5ef4 Added cursor protection extension 2025-11-13 21:00:35 +08:00
d42f913250 Added editor cursor state persistence 2025-11-13 20:44:47 +08:00
bae4e663fb ⬆️ Upgrade dependencies 2025-11-13 20:02:22 +08:00
a17e060d16 ⬆️ Upgrade dependencies 2025-11-13 19:36:11 +08:00
71946965eb 🐛 Fixed database constraint issues 2025-11-08 17:35:29 +08:00
d4cd22d234 🚀 Update build and release workflows 2025-11-08 17:17:07 +08:00
05f2f7d46d 🚀 Update build and release workflows 2025-11-08 17:05:31 +08:00
9deb2744a9 🚀 Update build and release workflows 2025-11-08 16:24:22 +08:00
6fac7c42d6 🚀 Update build and release workflows 2025-11-08 16:03:26 +08:00
3393bc84e3 🚀 Add build and release workflows 2025-11-08 15:50:30 +08:00
286b0159d7 🎨 Optimize update services
Some checks failed
Deploy VitePress site to Pages / build (push) Has been cancelled
Deploy VitePress site to Pages / Deploy (push) Has been cancelled
2025-11-08 00:00:08 +08:00
cc98e556c6 ♻️ Optimize code
Some checks failed
Deploy VitePress site to Pages / build (push) Has been cancelled
Deploy VitePress site to Pages / Deploy (push) Has been cancelled
2025-11-07 22:34:12 +08:00
5902f482d9 ♻️ Refactor configuration change notification service 2025-11-07 00:35:11 +08:00
551e7e2cfd Optimize hotkey service 2025-11-06 22:42:44 +08:00
e0179b5838 🎨 Optimize hotkey service 2025-11-06 00:08:26 +08:00
df79267e16 Optimize multi-window services 2025-11-05 22:07:43 +08:00
1f0254822f 🎨 Optimize multi-window services 2025-11-05 00:10:26 +08:00
e9b6fef3cd Added mermaid language support 2025-11-04 22:58:36 +08:00
689b0d5d14 🚀 Fixed resource path issues 2025-11-04 01:21:10 +08:00
a058e62595 🚀 Fixed resource path issues 2025-11-04 01:07:28 +08:00
8571fc0f5c 🚀 Fixed resource path issues 2025-11-04 00:57:49 +08:00
4dad0a86b3 🚀 Improve deployment scripts 2025-11-04 00:36:32 +08:00
3168b7ff43 🚀 Modify deployment command 2025-11-04 00:27:34 +08:00
d002a5be5a 🚀 Adjust base path 2025-11-04 00:16:39 +08:00
24a550463c 🚀 Remove dead links 2025-11-03 23:58:27 +08:00
14ae3e80c4 Beautify style and add automatic deployment scripts 2025-11-03 23:38:15 +08:00
e4d3969e95 📝 Build document directory structure 2025-11-03 22:49:38 +08:00
0b16d1d4ac Merge branch 'master' into docs 2025-11-03 22:18:06 +08:00
300514531d Improve HTTP client function 2025-11-03 22:15:45 +08:00
6a4780b002 📝 Replace official website framework 2025-11-02 22:48:33 +08:00
5688304817 🎨 Add variable support for http client 2025-11-02 17:20:22 +08:00
4380ad010c 🎨 Supports file upload 2025-11-02 01:53:11 +08:00
4fa6bb42e3 ⬆️ Upgrade dependencies 2025-11-02 00:16:57 +08:00
7aa3a7e37f 🚧 Improve HTTP language parser 2025-11-02 00:01:16 +08:00
94306497a9 🚧 Improve HTTP language parser 2025-11-01 21:40:51 +08:00
93c85b800b 🚧 Optimized HTTP language parser 2025-11-01 17:42:22 +08:00
8ac78e39f1 🚧 Added HTTP language parser 2025-10-31 19:39:44 +08:00
61a23fe7f2 🐛 Fixed tab and multi-window issues 2025-10-23 19:07:11 +08:00
87fea58102 Merge pull request #6 from landaiqing/dependabot/npm_and_yarn/frontend/vite-7.1.11
⬆️ Bump vite from 7.1.10 to 7.1.11 in /frontend
2025-10-21 10:25:54 +08:00
dependabot[bot]
edeac01bee ⬆️ Bump vite from 7.1.10 to 7.1.11 in /frontend
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.1.10 to 7.1.11.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.11/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.11
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-21 01:50:20 +00:00
852424356a 🐛 Fixed startup failure issue 2025-10-20 23:07:29 +08:00
b704dd2438 🎨 Optimize code 2025-10-20 23:00:04 +08:00
aa8139884b 🎨 Optimize preset theme code 2025-10-20 21:58:37 +08:00
9a15df01ee Added preset theme 2025-10-19 23:57:03 +08:00
03780b5bc7 ⬆️ Upgrade dependencies 2025-10-10 23:40:23 +08:00
b5d90cc59a Added the ability to automatically scroll to active tabs 2025-10-05 18:53:49 +08:00
d49ffc20df 🎨 Refactor and optimize code 2025-10-05 00:58:27 +08:00
c22e349181 🐛 Fixed tab switch issue 2025-10-04 14:31:06 +08:00
45968cd353 Added tab functionality and optimized related configurations 2025-10-04 02:27:32 +08:00
2d02bf7f1f 🚧 Optimize 2025-10-01 22:32:57 +08:00
1216b0b67c 🚧 Optimize 2025-10-01 18:15:22 +08:00
cf8bf688bf 🚧 Optimize 2025-09-30 00:28:15 +08:00
4d6a4ff79f 🐛 Fixed SQLite time field issue 2025-09-29 00:59:59 +08:00
3077d5a7c5 ♻️ Refactor document selector and cache management logic 2025-09-29 00:26:05 +08:00
bc0569af93 🎨 Optimize code structure 2025-09-28 01:09:20 +08:00
0188b618f2 🐛 Fixed docker prettier plugin issue 2025-09-27 20:11:31 +08:00
08860e9a5c ⬆️ Update dependencies 2025-09-24 23:57:22 +08:00
a56d4ef379 Adds multiple code block language support 2025-09-24 23:05:21 +08:00
432 changed files with 37698 additions and 15212 deletions

325
.github/workflows/build-release.yml vendored Normal file
View File

@@ -0,0 +1,325 @@
name: Build and Release Voidraft
on:
# 推送标签时触发(用于正式发布)
push:
tags:
- 'v*' # 触发条件:推送 v 开头的标签,如 v1.0.0
branches:
- main # 仅当标签在 main 分支时触发
# 手动触发(用于测试)
workflow_dispatch:
inputs:
release:
description: '是否创建 Release测试时选 false'
required: true
type: boolean
default: false
platforms:
description: '要构建的平台用逗号分隔windows,linux,macos-intel,macos-arm'
required: true
type: string
default: 'windows,linux'
env:
NODE_OPTIONS: "--max-old-space-size=4096" # 防止 Node.js 内存溢出
jobs:
# 准备构建配置
prepare:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
should_release: ${{ steps.check-release.outputs.should_release }}
steps:
- name: 确定构建平台
id: set-matrix
run: |
# 如果是手动触发,根据输入决定平台
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
PLATFORMS="${{ github.event.inputs.platforms }}"
else
# 标签触发,构建所有平台
PLATFORMS="windows,linux,macos-intel,macos-arm"
fi
echo "构建平台: $PLATFORMS"
# 构建矩阵 JSON
MATRIX='{"include":[]}'
if [[ "$PLATFORMS" == *"windows"* ]]; then
MATRIX=$(echo "$MATRIX" | jq -c '.include += [{"platform":"windows-latest","os":"windows","arch":"amd64","platform_dir":"windows","id":"windows"}]')
fi
if [[ "$PLATFORMS" == *"linux"* ]]; then
MATRIX=$(echo "$MATRIX" | jq -c '.include += [{"platform":"ubuntu-22.04","os":"linux","arch":"amd64","platform_dir":"linux","id":"linux"}]')
fi
if [[ "$PLATFORMS" == *"macos-intel"* ]]; then
MATRIX=$(echo "$MATRIX" | jq -c '.include += [{"platform":"macos-latest","os":"darwin","arch":"amd64","platform_dir":"darwin","id":"macos-intel"}]')
fi
if [[ "$PLATFORMS" == *"macos-arm"* ]]; then
MATRIX=$(echo "$MATRIX" | jq -c '.include += [{"platform":"macos-latest","os":"darwin","arch":"arm64","platform_dir":"darwin","id":"macos-arm"}]')
fi
# 使用 -c 确保输出紧凑的单行 JSON符合 GitHub Actions 输出格式
echo "matrix=$(echo "$MATRIX" | jq -c .)" >> $GITHUB_OUTPUT
# 输出美化的 JSON 用于日志查看
echo "生成的矩阵:"
echo "$MATRIX" | jq .
- name: 检查是否创建 Release
id: check-release
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
# 手动触发,根据输入决定
echo "should_release=${{ github.event.inputs.release }}" >> $GITHUB_OUTPUT
else
# 标签触发,自动创建 Release
echo "should_release=true" >> $GITHUB_OUTPUT
fi
build:
needs: prepare
if: ${{ fromJson(needs.prepare.outputs.matrix).include[0] != null }}
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.prepare.outputs.matrix) }}
runs-on: ${{ matrix.platform }}
steps:
- name: 检出代码
uses: actions/checkout@v4
with:
submodules: recursive
- name: 设置 Go 环境
uses: actions/setup-go@v5
with:
go-version: '1.21'
cache: true
- name: 设置 Node.js 环境
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
# Linux 平台依赖
- name: 安装 Linux 依赖
if: matrix.os == 'linux'
run: |
sudo apt-get update
# Wails 3 + 项目特定依赖
sudo apt-get install -y \
build-essential \
pkg-config \
libgtk-3-dev \
libwebkit2gtk-4.1-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf \
libx11-dev \
rpm \
fuse \
file
# 依赖说明:
# - build-essential: C/C++ 编译工具链
# - pkg-config: 包配置工具
# - libgtk-3-dev: GTK3 GUI 框架
# - libwebkit2gtk-4.1-dev: WebKit2GTK 4.1 (Wails 3 要求)
# - libayatana-appindicator3-dev: 系统托盘支持
# - librsvg2-dev: SVG 图标支持
# - patchelf: 修改 ELF 二进制文件
# - libx11-dev: X11 库 (热键服务依赖)
# - rpm: RPM 打包工具
# - fuse: AppImage 运行依赖
# - file: 文件类型检测工具
# Windows 平台依赖
- name: 设置 Windows 构建环境
if: matrix.os == 'windows'
run: |
# 安装 NSIS (用于创建安装程序)
choco install nsis -y
# 将 NSIS 添加到 PATH
echo "C:\Program Files (x86)\NSIS" >> $GITHUB_PATH
shell: bash
# macOS 平台依赖
- name: 设置 macOS 构建环境
if: matrix.os == 'darwin'
run: |
# Xcode 命令行工具通常已安装
xcode-select --install 2>/dev/null || true
# 安装 Wails CLI
- name: 安装 Wails CLI
run: go install github.com/wailsapp/wails/v3/cmd/wails3@latest
# 安装前端依赖
- name: 安装前端依赖
working-directory: frontend
run: npm ci
# 构建前端
- name: 构建前端
working-directory: frontend
run: npm run build
# 使用 Wails Task 构建和打包应用
- name: 构建和打包 Wails 应用
run: wails3 task ${{ matrix.id }}:package PRODUCTION=true ARCH=${{ matrix.arch }}
env:
CGO_ENABLED: 1
APP_NAME: voidraft
BIN_DIR: bin
ROOT_DIR: .
shell: bash
# 整理构建产物
- name: 整理构建产物
id: organize_artifacts
run: |
echo "=== 构建产物列表 ==="
ls -lhR bin/ || echo "bin 目录不存在"
# 创建输出目录
mkdir -p artifacts
# 根据平台复制产物
if [ "${{ matrix.os }}" = "windows" ]; then
echo "Windows 平台:查找 NSIS 安装程序"
find . -name "*.exe" -type f
cp bin/*.exe artifacts/ 2>/dev/null || echo "未找到 .exe 文件"
elif [ "${{ matrix.os }}" = "linux" ]; then
echo "Linux 平台:查找 AppImage, deb, rpm, archlinux 包"
find bin -type f
cp bin/*.AppImage artifacts/ 2>/dev/null || echo "未找到 AppImage"
cp bin/*.deb artifacts/ 2>/dev/null || echo "未找到 deb"
cp bin/*.rpm artifacts/ 2>/dev/null || echo "未找到 rpm"
cp bin/*.pkg.tar.zst artifacts/ 2>/dev/null || echo "未找到 archlinux 包"
elif [ "${{ matrix.os }}" = "darwin" ]; then
echo "macOS 平台:查找 .app bundle"
find bin -name "*.app" -type d
# macOS: .app bundle打包成 zip
if [ -d "bin/voidraft.app" ]; then
cd bin
zip -r ../artifacts/voidraft-darwin-${{ matrix.arch }}.app.zip voidraft.app
cd ..
else
echo "未找到 .app bundle"
fi
fi
echo "=== 最终产物 ==="
ls -lh artifacts/ || echo "artifacts 目录为空"
shell: bash
# 上传构建产物到 Artifacts
- name: 上传构建产物
uses: actions/upload-artifact@v4
with:
name: voidraft-${{ matrix.id }}
path: artifacts/*
if-no-files-found: warn
# 创建 GitHub Release 并上传所有构建产物
release:
needs: [prepare, build]
if: ${{ needs.prepare.outputs.should_release == 'true' }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 下载所有构建产物
uses: actions/download-artifact@v4
with:
path: artifacts
- name: 显示下载的文件
run: |
echo "下载的构建产物:"
ls -R artifacts/
- name: 准备 Release 文件
run: |
mkdir -p release
find artifacts -type f -exec cp {} release/ \;
ls -lh release/
- name: 生成 Release 说明
id: release_notes
run: |
# 获取版本号
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ github.sha }}"
VERSION_NAME="测试构建 ${VERSION:0:7}"
else
VERSION="${{ github.ref_name }}"
VERSION_NAME="${VERSION}"
fi
cat > release_notes.md << EOF
## Voidraft ${VERSION_NAME}
### 📦 下载
根据你的操作系统选择对应的版本:
- **Windows (64位)**: \`voidraft-windows-amd64.exe\`
- **Linux (64位)**: \`voidraft-linux-amd64\`
- **macOS (Intel)**: \`voidraft-darwin-amd64.zip\`
- **macOS (Apple Silicon)**: \`voidraft-darwin-arm64.zip\`
### 📝 更新内容
请查看 [提交历史](../../commits/${{ github.ref_name }}) 了解本次更新的详细内容。
### 💡 使用说明
#### Windows
1. 下载 `voidraft-windows-amd64.exe`
2. 直接运行即可
#### Linux
1. 下载 `voidraft-linux-amd64`
2. 添加执行权限:`chmod +x voidraft-linux-amd64`
3. 运行:`./voidraft-linux-amd64`
#### macOS
1. 下载对应架构的 zip 文件
2. 解压后运行
3. 如果提示无法打开,请在 系统偏好设置 > 安全性与隐私 中允许运行
---
构建时间: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
EOF
- name: 创建 GitHub Release
uses: softprops/action-gh-release@v2
with:
files: release/*
body_path: release_notes.md
draft: false
prerelease: ${{ github.event_name == 'workflow_dispatch' }}
tag_name: ${{ github.event_name == 'workflow_dispatch' && format('test-{0}', github.sha) || github.ref_name }}
name: ${{ github.event_name == 'workflow_dispatch' && format('测试构建 {0}', github.sha) || github.ref_name }}
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

67
.github/workflows/deploy.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
# 构建 VitePress 站点并将其部署到 GitHub Pages 的示例工作流程
#
name: Deploy VitePress site to Pages
on:
# 在针对 `main` 分支的推送上运行。如果你
# 使用 `master` 分支作为默认分支,请将其更改为 `master`
push:
branches: [master]
# 允许你从 Actions 选项卡手动运行此工作流程
workflow_dispatch:
# 设置 GITHUB_TOKEN 的权限,以允许部署到 GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# 只允许同时进行一次部署,跳过正在运行和最新队列之间的运行队列
# 但是,不要取消正在进行的运行,因为我们希望允许这些生产部署完成
concurrency:
group: pages
cancel-in-progress: false
jobs:
# 构建工作
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # 如果未启用 lastUpdated则不需要
# - uses: pnpm/action-setup@v3 # 如果使用 pnpm请取消此区域注释
# with:
# version: 9
# - uses: oven-sh/setup-bun@v1 # 如果使用 Bun请取消注释
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Install dependencies
run: cd frontend && npm ci
- name: Build with VitePress
run: cd frontend && npm run docs:build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: frontend/docs/.vitepress/dist
# 部署工作
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
name: Deploy
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

2
.gitignore vendored
View File

@@ -5,3 +5,5 @@ frontend/node_modules
build/linux/appimage/build build/linux/appimage/build
build/windows/nsis/MicrosoftEdgeWebview2Setup.exe build/windows/nsis/MicrosoftEdgeWebview2Setup.exe
.idea .idea
frontend/docs/.vitepress/cache/
frontend/docs/.vitepress/dist/

View File

@@ -12,25 +12,13 @@ vars:
VITE_PORT: '{{.WAILS_VITE_PORT | default 9245}}' VITE_PORT: '{{.WAILS_VITE_PORT | default 9245}}'
tasks: tasks:
version:
summary: Generate version information
cmds:
- '{{if eq OS "windows"}}cmd /c ".\scripts\version.bat"{{else}}bash ./scripts/version.sh{{end}}'
sources:
- scripts/version.bat
- scripts/version.sh
generates:
- version.txt
build: build:
summary: Builds the application summary: Builds the application
deps: [version]
cmds: cmds:
- task: "{{OS}}:build" - task: "{{OS}}:build"
package: package:
summary: Packages a production build of the application summary: Packages a production build of the application
deps: [version]
cmds: cmds:
- task: "{{OS}}:package" - task: "{{OS}}:package"

293
build/COMMANDS.md Normal file
View File

@@ -0,0 +1,293 @@
# Wails 3 命令参考表
本文档列出了 Voidraft 项目中使用的所有 Wails 3 命令和参数。
## 📋 命令总览
| 类别 | 命令 | 用途 |
|------|------|------|
| 生成工具 | `wails3 generate` | 生成项目所需的各种资源文件 |
| 打包工具 | `wails3 tool package` | 使用 nfpm 打包应用程序 |
| 任务执行 | `wails3 task` | 执行 Taskfile.yml 中定义的任务 |
---
## 🔧 详细命令参数
### 1. `wails3 generate bindings`
**生成 TypeScript 绑定文件**
| 参数 | 说明 | 示例值 |
|------|------|--------|
| `-f` | 构建标志 | `''` (空字符串) |
| `-clean` | 清理旧绑定 | `true` |
| `-ts` | 生成 TypeScript | 无需值 |
**使用位置:** `build/Taskfile.yml:53`
**完整命令:**
```bash
wails3 generate bindings -f '' -clean=true -ts
```
---
### 2. `wails3 generate icons`
**从单个图片生成多平台图标**
| 参数 | 说明 | 示例值 |
|------|------|--------|
| `-input` | 输入图片路径 | `appicon.png` |
| `-macfilename` | macOS 图标输出路径 | `darwin/icons.icns` |
| `-windowsfilename` | Windows 图标输出路径 | `windows/icons.ico` |
**使用位置:** `build/Taskfile.yml:64`
**完整命令:**
```bash
wails3 generate icons \
-input appicon.png \
-macfilename darwin/icons.icns \
-windowsfilename windows/icons.ico
```
---
### 3. `wails3 generate syso`
**生成 Windows .syso 资源文件**
| 参数 | 说明 | 示例值 |
|------|------|--------|
| `-arch` | 目标架构 | `amd64` / `arm64` |
| `-icon` | 图标文件 | `windows/icon.ico` |
| `-manifest` | 清单文件 | `windows/wails.exe.manifest` |
| `-info` | 应用信息 JSON | `windows/info.json` |
| `-out` | 输出文件路径 | `../wails_windows_amd64.syso` |
**使用位置:** `build/windows/Taskfile.yml:42`
**完整命令:**
```bash
wails3 generate syso \
-arch amd64 \
-icon windows/icon.ico \
-manifest windows/wails.exe.manifest \
-info windows/info.json \
-out ../wails_windows_amd64.syso
```
---
### 4. `wails3 generate webview2bootstrapper`
**生成 Windows WebView2 引导程序**
| 参数 | 说明 | 示例值 |
|------|------|--------|
| `-dir` | 输出目录 | `build/windows/nsis` |
**使用位置:** `build/windows/Taskfile.yml:55`
**完整命令:**
```bash
wails3 generate webview2bootstrapper -dir "build/windows/nsis"
```
**说明:** 下载 Microsoft Edge WebView2 运行时安装程序,用于 NSIS 打包。
---
### 5. `wails3 generate .desktop`
**生成 Linux .desktop 桌面文件**
| 参数 | 说明 | 示例值 |
|------|------|--------|
| `-name` | 应用名称 | `voidraft` |
| `-exec` | 可执行文件名 | `voidraft` |
| `-icon` | 图标名称 | `appicon` |
| `-outputfile` | 输出文件路径 | `build/linux/voidraft.desktop` |
| `-categories` | 应用分类 | `Development;` |
**使用位置:** `build/linux/Taskfile.yml:107`
**完整命令:**
```bash
wails3 generate .desktop \
-name "voidraft" \
-exec "voidraft" \
-icon "appicon" \
-outputfile build/linux/voidraft.desktop \
-categories "Development;"
```
---
### 6. `wails3 generate appimage`
**生成 Linux AppImage 包**
| 参数 | 说明 | 示例值 |
|------|------|--------|
| `-binary` | 二进制文件名 | `voidraft` |
| `-icon` | 图标文件路径 | `../../appicon.png` |
| `-desktopfile` | .desktop 文件路径 | `../voidraft.desktop` |
| `-outputdir` | 输出目录 | `../../../bin` |
| `-builddir` | 构建临时目录 | `build/linux/appimage/build` |
**使用位置:** `build/linux/Taskfile.yml:49`
**完整命令:**
```bash
wails3 generate appimage \
-binary voidraft \
-icon ../../appicon.png \
-desktopfile ../voidraft.desktop \
-outputdir ../../../bin \
-builddir build/linux/appimage/build
```
**说明:** 自动下载 linuxdeploy 工具并创建 AppImage。
---
### 7. `wails3 tool package` (deb)
**创建 Debian/Ubuntu .deb 包**
| 参数 | 说明 | 示例值 |
|------|------|--------|
| `-name` | 包名称 | `voidraft` |
| `-format` | 包格式 | `deb` |
| `-config` | nfpm 配置文件 | `./build/linux/nfpm/nfpm.yaml` |
| `-out` | 输出目录 | `./bin` |
**使用位置:** `build/linux/Taskfile.yml:90`
**完整命令:**
```bash
wails3 tool package \
-name voidraft \
-format deb \
-config ./build/linux/nfpm/nfpm.yaml \
-out ./bin
```
---
### 8. `wails3 tool package` (rpm)
**创建 RedHat/CentOS/Fedora .rpm 包**
| 参数 | 说明 | 示例值 |
|------|------|--------|
| `-name` | 包名称 | `voidraft` |
| `-format` | 包格式 | `rpm` |
| `-config` | nfpm 配置文件 | `./build/linux/nfpm/nfpm.yaml` |
| `-out` | 输出目录 | `./bin` |
**使用位置:** `build/linux/Taskfile.yml:95`
**完整命令:**
```bash
wails3 tool package \
-name voidraft \
-format rpm \
-config ./build/linux/nfpm/nfpm.yaml \
-out ./bin
```
---
### 9. `wails3 tool package` (archlinux)
**创建 Arch Linux .pkg.tar.zst 包**
| 参数 | 说明 | 示例值 |
|------|------|--------|
| `-name` | 包名称 | `voidraft` |
| `-format` | 包格式 | `archlinux` |
| `-config` | nfpm 配置文件 | `./build/linux/nfpm/nfpm.yaml` |
| `-out` | 输出目录 | `./bin` |
**使用位置:** `build/linux/Taskfile.yml:100`
**完整命令:**
```bash
wails3 tool package \
-name voidraft \
-format archlinux \
-config ./build/linux/nfpm/nfpm.yaml \
-out ./bin
```
---
### 10. `wails3 task`
**执行 Taskfile.yml 中定义的任务**
| 参数 | 说明 | 示例值 |
|------|------|--------|
| `[taskname]` | 任务名称 | `build`, `package`, `run` |
| `[VAR=value]` | 变量赋值 | `PRODUCTION=true`, `ARCH=amd64` |
**常用任务:**
| 任务 | 说明 | 命令 |
|------|------|------|
| `build` | 构建应用 | `wails3 task build PRODUCTION=true` |
| `package` | 打包应用 | `wails3 task package` |
| `run` | 运行应用 | `wails3 task run` |
**使用位置:** `build/config.yml`
---
## 📊 平台对应命令表
| 平台 | 主要命令 | 产物 |
|------|----------|------|
| **Windows** | `generate syso`<br>`generate webview2bootstrapper` | `.syso` 资源文件<br>NSIS 安装程序 |
| **Linux** | `generate .desktop`<br>`generate appimage`<br>`tool package -format deb/rpm/archlinux` | `.desktop` 文件<br>`.AppImage`<br>`.deb` / `.rpm` / `.pkg.tar.zst` |
| **macOS** | `generate icons` | `.icns` 图标<br>`.app` 应用包 |
| **通用** | `generate bindings`<br>`generate icons` | TypeScript 绑定<br>多平台图标 |
---
## 🚀 快速参考
### 完整构建流程
```bash
# 1. 生成绑定和图标
wails3 task common:generate:bindings
wails3 task common:generate:icons
# 2. 构建前端
cd frontend
npm install
npm run build
cd ..
# 3. 构建应用(各平台)
cd build/windows && wails3 task build PRODUCTION=true # Windows
cd build/linux && wails3 task build PRODUCTION=true # Linux
cd build/darwin && wails3 task build PRODUCTION=true # macOS
# 4. 打包应用(各平台)
cd build/windows && wails3 task package # NSIS 安装程序
cd build/linux && wails3 task package # AppImage + deb + rpm + archlinux
cd build/darwin && wails3 task package # .app bundle
```
---
## 📝 注意事项
1. **变量传递:** Task 命令支持通过 `VAR=value` 格式传递变量
2. **路径问题:** 相对路径基于 Taskfile.yml 所在目录
3. **依赖顺序:** 某些任务有依赖关系(通过 `deps:` 定义)
4. **环境变量:** 使用 `env:` 定义的环境变量会自动设置
---
## 🔗 相关文档
- [Wails 3 官方文档](https://v3alpha.wails.io/)
- [Taskfile 语法](https://taskfile.dev/)
- [nfpm 打包工具](https://nfpm.goreleaser.com/)

View File

@@ -1 +0,0 @@
voidraft.landaiqing.cn

View File

@@ -1,75 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>voidraft - Changelog</title>
<link rel="stylesheet" href="css/styles.css">
<link rel="stylesheet" href="css/changelog.css">
<link rel="icon" href="img/favicon.ico">
<link href="https://fonts.googleapis.com/css2?family=Space+Mono&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body class="theme-dark">
<div class="container">
<!-- 主卡片 -->
<div class="card">
<div class="card-header">
<h1 class="card-title" data-en="voidraft Changelog" data-zh="voidraft 更新日志">voidraft Changelog</h1>
<div class="card-controls">
<button id="theme-toggle" class="btn btn-secondary" title="切换主题">
<i class="fas fa-sun"></i> <span data-en="Theme" data-zh="主题">Theme</span>
</button>
<button id="lang-toggle" class="btn btn-secondary">
<i class="fas fa-language"></i> 中/EN
</button>
</div>
</div>
<div class="card-content">
<!-- 导航区域 -->
<div class="nav-links">
<a href="index.html" class="btn btn-secondary">
<i class="fas fa-home"></i> <span data-en="Home" data-zh="首页">Home</span>
</a>
<a href="https://github.com/landaiqing/voidraft" class="btn btn-secondary">
<i class="fab fa-github"></i> <span data-en="Source Code" data-zh="源代码">Source Code</span>
</a>
</div>
<!-- 加载中提示 -->
<div id="loading" class="loading-container">
<div class="loading-spinner"></div>
<p data-en="Loading releases..." data-zh="正在加载版本信息...">Loading releases...</p>
</div>
<!-- 更新日志内容 -->
<div id="changelog" class="changelog-container">
<!-- 通过JavaScript动态填充内容 -->
</div>
<!-- 错误信息 -->
<div id="error-message" class="error-container" style="display: none;">
<i class="fas fa-exclamation-triangle"></i>
<p data-en="Failed to load release information. Please try again later."
data-zh="加载版本信息失败,请稍后再试。">Failed to load release information. Please try again later.</p>
</div>
</div>
<!-- 页脚 -->
<footer class="footer">
<p class="footer-text" data-en="© 2025 voidraft - An elegant text snippet recording tool designed for developers" data-zh="© 2025 voidraft - 专为开发者打造的优雅文本片段记录工具">© 2023-2024 voidraft - An elegant text snippet recording tool designed for developers</p>
<div class="footer-links">
<a href="https://github.com/landaiqing/voidraft" target="_blank" class="footer-link">GitHub</a>
<a href="https://github.com/landaiqing/voidraft/issues" target="_blank" class="footer-link" data-en="Issues" data-zh="问题反馈">Issues</a>
<a href="https://github.com/landaiqing/voidraft/releases" target="_blank" class="footer-link" data-en="Releases" data-zh="版本发布">Releases</a>
</div>
</footer>
</div>
</div>
<script src="js/script.js"></script>
<script src="js/changelog.js"></script>
</body>
</html>

View File

@@ -1,347 +0,0 @@
/* 更新日志页面样式 */
.nav-links {
margin-bottom: 30px;
display: flex;
gap: 15px;
}
.loading-container {
text-align: center;
padding: 40px 0;
background-color: transparent;
}
.loading-spinner {
border: 4px solid rgba(0, 0, 0, 0.1);
border-left-color: var(--primary-color);
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
.theme-dark .loading-spinner {
border-color: rgba(255, 255, 255, 0.1);
border-left-color: var(--primary-color);
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error-container {
text-align: center;
color: var(--error-color);
padding: 20px;
border: 2px dashed var(--error-color);
margin: 20px 0;
border-radius: 4px;
background-color: rgba(var(--card-bg-rgb), 0.7);
}
.error-container i {
font-size: 24px;
margin-bottom: 10px;
}
/* 更新日志容器 */
.changelog-container {
display: none;
position: relative;
z-index: 1;
}
.release {
margin-bottom: 40px;
border-left: 4px solid var(--primary-color);
padding-left: 20px;
background-color: rgba(var(--card-bg-rgb), 0.5);
padding: 15px 20px;
border-radius: 4px;
}
.release-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.release-version {
font-size: 24px;
font-weight: bold;
color: var(--primary-color);
}
.release-date {
color: var(--text-color);
opacity: 0.7;
font-size: 14px;
}
.release-badge {
display: inline-block;
padding: 3px 8px;
border-radius: 12px;
font-size: 12px;
margin-left: 10px;
background-color: var(--primary-color);
color: #000;
}
.release-badge.pre-release {
background-color: var(--warning-color);
}
.release-description {
margin-bottom: 20px;
line-height: 1.6;
}
.release-assets {
background-color: rgba(var(--light-bg-rgb), 0.7);
padding: 15px;
border-radius: 4px;
margin-top: 15px;
}
.release-assets-title {
font-size: 16px;
margin-bottom: 10px;
}
.asset-list {
list-style-type: none;
padding: 0;
margin: 0;
}
.asset-item {
display: flex;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid rgba(128, 128, 128, 0.2);
}
.asset-item:last-child {
border-bottom: none;
}
.asset-icon {
margin-right: 10px;
color: var(--accent-color);
}
.asset-name {
flex-grow: 1;
}
.asset-size {
font-size: 12px;
color: var(--text-color);
opacity: 0.7;
}
/* 资源下载按钮 */
.download-btn {
margin-left: 10px;
padding: 3px 10px;
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 4px;
text-decoration: none;
font-size: 12px;
transition: all 0.2s ease;
display: inline-block;
text-align: center;
}
.download-btn:hover {
background-color: var(--secondary-color);
}
.markdown-content {
line-height: 1.8;
overflow-wrap: break-word;
background-color: transparent;
}
.markdown-content h1,
.markdown-content h2,
.markdown-content h3 {
margin-top: 20px;
margin-bottom: 10px;
}
.markdown-content ul,
.markdown-content ol {
padding-left: 20px;
margin: 10px 0;
}
.markdown-content li {
margin-bottom: 8px;
}
.markdown-content li:last-child {
margin-bottom: 0;
}
.markdown-content hr {
border: none;
border-top: 2px dashed var(--border-color);
margin: 20px 0;
}
.markdown-content br {
display: block;
content: "";
margin-top: 10px;
}
.markdown-content code {
font-family: 'IBM Plex Mono', monospace;
background-color: rgba(128, 128, 128, 0.1);
padding: 2px 4px;
border-radius: 3px;
font-size: 90%;
}
.markdown-content pre {
background-color: rgba(128, 128, 128, 0.1);
padding: 15px;
border-radius: 4px;
overflow-x: auto;
margin: 15px 0;
}
.markdown-content pre code {
background-color: transparent;
padding: 0;
}
.markdown-content a {
color: var(--primary-color);
text-decoration: none;
}
.markdown-content a:hover {
text-decoration: underline;
}
.data-source {
padding: 10px 15px;
margin-bottom: 20px;
background-color: rgba(var(--light-bg-rgb), 0.7);
border-radius: 4px;
font-size: 14px;
text-align: right;
opacity: 0.7;
}
.data-source a {
color: var(--primary-color);
text-decoration: none;
font-weight: bold;
}
.data-source a:hover {
text-decoration: underline;
}
/* Markdown内容样式增强 */
.markdown-content blockquote {
border-left: 4px solid var(--primary-color);
padding: 10px 15px;
margin: 15px 0;
background-color: rgba(var(--light-bg-rgb), 0.5);
border-radius: 0 4px 4px 0;
}
.markdown-content ul,
.markdown-content ol {
padding-left: 20px;
margin: 10px 0;
}
/* 移动设备响应式优化 */
@media (max-width: 768px) {
.release-header {
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.release-assets {
padding: 12px 8px;
}
.asset-item {
flex-wrap: wrap;
padding: 12px 0;
position: relative;
}
.asset-name {
width: 100%;
margin-bottom: 8px;
word-break: break-all;
}
.asset-size {
margin-left: 25px;
}
.download-btn {
margin-left: 10px;
padding: 5px 12px;
}
}
@media (max-width: 480px) {
.release {
padding-left: 12px;
}
.asset-item {
flex-direction: column;
align-items: flex-start;
}
.asset-icon {
margin-bottom: 5px;
}
.asset-size {
margin-left: 0;
margin-top: 5px;
}
.download-btn {
margin-left: 0;
margin-top: 10px;
width: 100%;
text-align: center;
padding: 8px;
}
.markdown-content pre {
padding: 10px;
margin: 10px 0;
}
}
/* 确保日志页面页脚样式一致 */
.footer {
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.footer-text {
margin: 0 0 15px 0;
}

View File

@@ -1,45 +0,0 @@
/* cyrillic-ext */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(../font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1iIq129k.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(../font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1isq129k.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* vietnamese */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(../font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1iAq129k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(../font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1iEq129k.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(../font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1i8q1w.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

View File

@@ -1,27 +0,0 @@
/* vietnamese */
@font-face {
font-family: 'Space Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(../font/space-mono/i7dPIFZifjKcF5UAWdDRYE58RWq7.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Space Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(../font/space-mono/i7dPIFZifjKcF5UAWdDRYE98RWq7.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Space Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(../font/space-mono/i7dPIFZifjKcF5UAWdDRYEF8RQ.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

View File

@@ -1,717 +0,0 @@
@import url('./space-mono-font.css');
@import url('./ibm-plex-mono-font.css');
/* 浅色主题 */
:root {
--bg-color: #fefefe;
--text-color: #000000;
--primary-color: #F08080;
--primary-color-rgb: 240, 128, 128;
--secondary-color: #ff006e;
--accent-color: #073B4C;
--card-bg: #ffffff;
--card-bg-rgb: 255, 255, 255;
--border-color: #000000;
--light-bg: #f0f0f0;
--light-bg-rgb: 240, 240, 240;
--shadow-color: rgba(240, 128, 128, 0.5);
--success-color: #27c93f;
--warning-color: #FFD166;
--error-color: #ff006e;
--info-color: #118ab2;
--code-bg: #ffffff;
--code-bg-rgb: 255, 255, 255;
--preview-header-bg: #f0f0f0;
--preview-header-bg-rgb: 240, 240, 240;
--grid-color-1: rgba(0, 0, 0, 0.08);
--grid-color-2: rgba(0, 0, 0, 0.05);
--header-title-color: #000000;
}
/* 暗色主题变量 */
.theme-dark {
--bg-color: #121212;
--text-color: #ffffff;
--primary-color: #F08080;
--primary-color-rgb: 240, 128, 128;
--secondary-color: #ff006e;
--accent-color: #118ab2;
--card-bg: #1e1e1e;
--card-bg-rgb: 30, 30, 30;
--border-color: #ffffff;
--light-bg: #2a2a2a;
--light-bg-rgb: 42, 42, 42;
--shadow-color: rgba(240, 128, 128, 0.5);
--success-color: #27c93f;
--warning-color: #FFD166;
--error-color: #ff006e;
--info-color: #118ab2;
--code-bg: #1e1e1e;
--code-bg-rgb: 30, 30, 30;
--preview-header-bg: #252526;
--preview-header-bg-rgb: 37, 37, 38;
--grid-color-1: rgba(255, 255, 255, 0.08);
--grid-color-2: rgba(255, 255, 255, 0.05);
--header-title-color: #000000;
}
/* 主题切换和语言切换的过渡效果 */
.theme-transition,
.theme-transition *,
.lang-transition,
.lang-transition * {
transition: all 0.3s ease !important;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
@keyframes gridMove {
0% {
background-position: 0px 0px, 0px 0px, 0px 0px, 0px 0px;
}
100% {
background-position: 80px 80px, 80px 80px, 20px 20px, 20px 20px;
}
}
body {
background-color: var(--bg-color);
background-image:
linear-gradient(var(--grid-color-1) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-color-1) 1px, transparent 1px),
linear-gradient(var(--grid-color-2) 0.5px, transparent 0.5px),
linear-gradient(90deg, var(--grid-color-2) 0.5px, transparent 0.5px);
background-size: 80px 80px, 80px 80px, 20px 20px, 20px 20px;
background-position: center;
animation: gridMove 40s linear infinite;
font-family: 'Space Mono', monospace;
color: var(--text-color);
line-height: 1.6;
padding: 20px;
transition: background-color 0.3s ease, color 0.3s ease;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
/* 卡片容器 */
.card {
background-color: var(--card-bg);
background-image:
linear-gradient(var(--grid-color-1) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-color-1) 1px, transparent 1px),
linear-gradient(var(--grid-color-2) 0.5px, transparent 0.5px),
linear-gradient(90deg, var(--grid-color-2) 0.5px, transparent 0.5px);
background-size: 80px 80px, 80px 80px, 20px 20px, 20px 20px;
background-position: center;
border: 4px solid var(--border-color);
box-shadow: 12px 12px 0 var(--shadow-color);
margin-bottom: 40px;
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
position: relative;
z-index: 10;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 16px 16px 0 var(--shadow-color);
}
/* 卡片头部 */
.card-header {
background-color: rgba(var(--primary-color-rgb), 0.9);
border-bottom: 4px solid var(--border-color);
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
z-index: 1;
}
.card-title {
font-size: 24px;
font-weight: bold;
margin: 0;
color: var(--header-title-color);
}
.card-controls {
display: flex;
gap: 10px;
}
.btn {
display: inline-block;
padding: 10px 20px;
background: var(--secondary-color);
color: #fff;
text-decoration: none;
font-weight: bold;
border: 3px solid var(--border-color);
box-shadow: 4px 4px 0 var(--shadow-color);
transition: all 0.2s ease;
cursor: pointer;
font-family: 'Space Mono', monospace;
font-size: 14px;
}
.btn:hover {
background: var(--card-bg);
color: var(--primary-color);
border: 3px solid var(--primary-color);
box-shadow: none;
}
.btn-secondary {
background: var(--light-bg);
color: var(--text-color);
}
.btn-secondary:hover {
background: var(--card-bg);
color: var(--primary-color);
border: 3px solid var(--primary-color);
}
/* 卡片内容 */
.card-content {
padding: 30px;
position: relative;
z-index: 1;
background-color: rgba(var(--card-bg-rgb), 0.5);
}
/* Logo区域 */
.logo-container {
text-align: center;
margin-bottom: 40px;
}
.logo-frame {
width: 150px;
height: 150px;
background: var(--card-bg);
border: 4px solid var(--border-color);
margin: 0 auto;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
}
.logo-image {
width: 130px;
height: 130px;
object-fit: contain;
border: 2px solid var(--border-color);
}
.logo-text {
font-size: 32px;
font-weight: bold;
margin: 0;
}
.tagline {
font-size: 16px;
margin: 10px 0 0;
color: var(--accent-color);
}
/* 介绍区域 */
.intro-box {
border: 2px dashed var(--border-color);
padding: 20px;
background-color: rgba(var(--light-bg-rgb), 0.7);
margin-bottom: 30px;
text-align: center;
}
.intro-text {
font-size: 16px;
margin-bottom: 0;
}
/* 按钮组 */
.button-group {
display: flex;
justify-content: center;
gap: 20px;
margin: 30px 0;
}
/* 特性网格 */
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 30px;
margin: 40px 0;
}
/* 特性卡片 */
.feature-card {
background-color: rgba(var(--card-bg-rgb), 0.8);
border: 3px solid var(--border-color);
box-shadow: 5px 5px 0 var(--shadow-color);
padding: 20px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.feature-card:hover {
transform: translateY(-3px);
box-shadow: 7px 7px 0 var(--shadow-color);
}
.feature-icon {
font-size: 24px;
margin-bottom: 15px;
color: var(--secondary-color);
}
.feature-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
}
.feature-desc {
font-size: 14px;
}
/* 预览区域 */
.preview-container {
display: flex;
flex-wrap: wrap;
gap: 20px;
margin: 30px 0;
}
@media (max-width: 768px) {
.preview-container {
grid-template-columns: 1fr;
}
}
/* 预览窗口 */
.preview-window {
border: 3px solid var(--border-color);
border-radius: 8px;
overflow: hidden;
margin: 10px;
flex: 1;
min-width: 300px;
background-color: rgba(var(--card-bg-rgb), 0.7);
display: flex;
flex-direction: column;
box-shadow: 5px 5px 0 var(--shadow-color);
}
/* 预览头部 */
.preview-header {
background-color: rgba(var(--preview-header-bg-rgb), 0.9);
padding: 10px;
display: flex;
align-items: center;
border-bottom: 2px solid var(--border-color);
}
.preview-controls {
display: flex;
gap: 6px;
margin-right: 15px;
}
.preview-btn {
width: 12px;
height: 12px;
border-radius: 50%;
border: 0.5px solid rgba(0, 0, 0, 0.1);
}
.preview-btn:nth-child(1) {
background-color: #ff5f56;
}
.preview-btn:nth-child(2) {
background-color: #ffbd2e;
}
.preview-btn:nth-child(3) {
background-color: #27c93f;
}
.preview-title {
font-size: 13px;
opacity: 0.8;
color: var(--text-color);
font-weight: normal;
}
/* 预览内容 */
.preview-content {
padding: 15px;
flex-grow: 1;
overflow: auto;
background-color: rgba(var(--code-bg-rgb), 0.5);
}
/* 代码块容器 */
.code-block-wrapper {
background-color: rgba(var(--code-bg-rgb), 0.8);
border: 2px solid var(--border-color);
border-radius: 4px;
overflow: hidden;
margin-bottom: 8px;
}
/* 块头部 */
.block-header {
background-color: rgba(var(--light-bg-rgb), 0.8);
padding: 8px 12px;
border-bottom: 2px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
}
.block-language {
color: rgba(128, 128, 128, 0.8);
font-family: 'IBM Plex Mono', monospace;
display: flex;
align-items: center;
}
.block-language::before {
content: '';
display: inline-block;
width: 12px;
height: 12px;
margin-right: 5px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23888'%3E%3Cpath d='M9.7,16.7L5.3,12.3C4.9,11.9 4.9,11.1 5.3,10.7C5.7,10.3 6.3,10.3 6.7,10.7L10.5,14.5L17.3,7.7C17.7,7.3 18.3,7.3 18.7,7.7C19.1,8.1 19.1,8.7 18.7,9.1L11.3,16.7C10.9,17.1 10.1,17.1 9.7,16.7Z'/%3E%3C/svg%3E");
background-size: contain;
background-repeat: no-repeat;
}
.code-block {
font-family: 'IBM Plex Mono', monospace;
font-size: 13px;
line-height: 1.6;
margin: 0;
white-space: pre;
tab-size: 4;
-moz-tab-size: 4;
padding: 10px;
}
.theme-dark .code-block-wrapper {
border-color: rgba(255, 255, 255, 0.15);
}
.theme-dark .block-header {
background-color: rgba(255, 255, 255, 0.05);
border-color: rgba(255, 255, 255, 0.15);
}
.theme-dark .block-language {
color: rgba(255, 255, 255, 0.6);
}
.theme-dark .block-language::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23aaa'%3E%3Cpath d='M9.7,16.7L5.3,12.3C4.9,11.9 4.9,11.1 5.3,10.7C5.7,10.3 6.3,10.3 6.7,10.7L10.5,14.5L17.3,7.7C17.7,7.3 18.3,7.3 18.7,7.7C19.1,8.1 19.1,8.7 18.7,9.1L11.3,16.7C10.9,17.1 10.1,17.1 9.7,16.7Z'/%3E%3C/svg%3E");
}
.theme-dark .code-block {
color: #d4d4d4;
}
/* 代码高亮 */
.theme-dark .keyword { color: #c586c0; }
.theme-dark .function { color: #dcdcaa; }
.theme-dark .variable { color: #9cdcfe; }
.theme-dark .string { color: #ce9178; }
.theme-dark .comment { color: #6a9955; }
.theme-dark .class { color: #4ec9b0; }
.theme-dark .parameter { color: #9cdcfe; }
.theme-dark .built-in { color: #4ec9b0; }
/* 浅色主题代码高亮 */
.keyword { color: #af00db; }
.function { color: #795e26; }
.variable { color: #001080; }
.string { color: #a31515; }
.comment { color: #008000; }
.class { color: #267f99; }
.parameter { color: #001080; }
.built-in { color: #267f99; }
.preview-image {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
border: none;
transition: opacity 0.3s ease;
}
.theme-dark .light-theme-img {
display: none !important;
}
.theme-dark .dark-theme-img {
display: block;
}
body:not(.theme-dark) .dark-theme-img {
display: none !important;
}
body:not(.theme-dark) .light-theme-img {
display: block !important;
}
/* 技术栈列表 */
.tech-list {
list-style: none;
padding: 0;
margin: 0;
}
/* 技术栈列表 */
.tech-item {
padding: 15px;
margin-bottom: 15px;
border: 2px solid var(--border-color);
background-color: rgba(var(--light-bg-rgb), 0.7);
display: flex;
align-items: center;
}
.tech-icon {
margin-right: 15px;
color: var(--secondary-color);
font-size: 20px;
width: 30px;
text-align: center;
}
.tech-name {
font-weight: bold;
margin-right: 10px;
}
.tech-desc {
font-size: 14px;
color: var(--accent-color);
}
/* 页脚 */
.footer {
border-top: 2px solid var(--border-color);
padding: 20px 0;
margin-top: 40px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
background-color: transparent;
position: relative;
z-index: 1;
}
.footer-text {
margin: 0 0 15px 0;
font-size: 14px;
opacity: 0.7;
}
.footer-links {
display: flex;
gap: 15px;
justify-content: center;
}
.footer-link {
color: var(--secondary-color);
text-decoration: none;
font-size: 14px;
transition: color 0.3s;
}
.footer-link:hover {
color: var(--primary-color);
text-decoration: underline;
}
/* 响应式设计 */
@media (max-width: 768px) {
.button-group {
flex-direction: column;
align-items: center;
}
.btn {
width: 100%;
text-align: center;
}
.features-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 480px) {
.card-header {
flex-direction: column;
gap: 15px;
}
.card-controls {
width: 100%;
}
.logo-frame {
width: 120px;
height: 120px;
}
.logo-image {
width: 100px;
height: 100px;
}
}
/* 针对移动设备的响应式优化 */
@media (max-width: 768px) {
body {
padding: 10px;
}
.container {
padding: 10px;
}
.card {
margin-bottom: 30px;
}
.card-header {
flex-direction: column;
gap: 15px;
text-align: center;
}
.card-controls {
width: 100%;
justify-content: center;
}
.button-group {
flex-wrap: wrap;
gap: 15px;
}
/* 预览区域优化 */
.preview-content {
max-width: 100%;
overflow-x: auto;
}
.code-block {
white-space: pre-wrap;
word-break: break-word;
font-size: 13px;
line-height: 1.4;
}
.block-header {
padding: 6px 10px;
}
/* 日志界面导航链接优化 */
.nav-links {
flex-direction: column;
gap: 10px;
align-items: stretch;
}
.nav-links .btn {
width: 100%;
text-align: center;
}
}
@media (max-width: 480px) {
/* 特性卡片优化 */
.features-grid {
grid-template-columns: 1fr;
gap: 20px;
}
/* 预览窗口优化 */
.preview-container {
flex-direction: column;
}
.preview-window {
margin-bottom: 20px;
width: 100%;
}
/* 技术栈列表小屏幕优化 */
.tech-item {
flex-wrap: wrap;
}
.tech-desc {
width: 100%;
padding-left: 40px; /* 图标宽度+右边距 */
margin-top: 5px;
}
/* 日志界面资源列表项优化 */
.asset-item {
flex-wrap: wrap;
padding: 15px 0;
}
.asset-name {
width: 100%;
word-break: break-all;
margin-bottom: 10px;
}
.asset-size {
order: 2;
margin-top: 10px;
}
.download-btn {
order: 3;
margin-left: 0;
margin-top: 10px;
width: 100%;
text-align: center;
padding: 8px;
}
/* 页脚链接优化 */
.footer {
flex-direction: column;
text-align: center;
}
.footer-links {
margin-top: 15px;
justify-content: center;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -1,256 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>voidraft - An elegant text snippet recording tool designed for developers.</title>
<meta name="description" content="voidraft is an elegant text snippet recording tool designed for developers. Features multi-language code blocks, syntax highlighting, code formatting, custom themes, and more.">
<meta name="keywords" content="text editor, code snippets, developer tools, syntax highlighting, code formatting, multi-language, voidraft">
<meta name="author" content="voidraft Team">
<meta name="robots" content="index, follow">
<link rel="canonical" href="https://landaiqing.github.io/voidraft/">
<!-- Internationalization / hreflang -->
<link rel="alternate" hreflang="en" href="https://landaiqing.github.io/voidraft/">
<link rel="alternate" hreflang="zh" href="https://landaiqing.github.io/voidraft/?lang=zh">
<link rel="alternate" hreflang="x-default" href="https://landaiqing.github.io/voidraft/">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://landaiqing.github.io/voidraft/">
<meta property="og:title" content="voidraft - An elegant text snippet recording tool designed for developers">
<meta property="og:description" content="voidraft is an elegant text snippet recording tool designed for developers. Features multi-language code blocks, syntax highlighting, code formatting, custom themes, and more.">
<meta property="og:image" content="https://landaiqing.github.io/voidraft/img/screenshot-dark.png">
<meta property="og:site_name" content="voidraft">
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="https://landaiqing.github.io/voidraft/">
<meta property="twitter:title" content="voidraft - An elegant text snippet recording tool designed for developers">
<meta property="twitter:description" content="voidraft is an elegant text snippet recording tool designed for developers. Features multi-language code blocks, syntax highlighting, code formatting, custom themes, and more.">
<meta property="twitter:image" content="https://landaiqing.github.io/voidraft/img/screenshot-dark.png">
<link rel="stylesheet" href="./css/styles.css">
<link rel="icon" href="./img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "voidraft",
"description": "An elegant text snippet recording tool designed for developers. Features multi-language code blocks, syntax highlighting, code formatting, custom themes, and more.",
"url": "https://landaiqing.github.io/voidraft/",
"downloadUrl": "https://github.com/landaiqing/voidraft/releases",
"author": {
"@type": "Organization",
"name": "voidraft"
},
"operatingSystem": ["Windows", "macOS", "Linux"],
"applicationCategory": "DeveloperApplication",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"screenshot": "https://landaiqing.github.io/voidraft/img/screenshot-dark.png",
"softwareVersion": "Latest",
"programmingLanguage": ["Go", "TypeScript", "Vue.js"],
"codeRepository": "https://github.com/landaiqing/voidraft"
}
</script>
</head>
<body class="theme-dark">
<div class="container">
<!-- 主卡片 -->
<div class="card">
<div class="card-header">
<h1 class="card-title">voidraft</h1>
<div class="card-controls">
<button id="theme-toggle" class="btn btn-secondary" title="切换主题">
<i class="fas fa-sun"></i> <span data-en="Theme" data-zh="主题">Theme</span>
</button>
<button id="lang-toggle" class="btn btn-secondary">
<i class="fas fa-language"></i> 中/EN
</button>
</div>
</div>
<div class="card-content">
<!-- Logo和介绍 -->
<div class="logo-container">
<div class="logo-frame">
<img src="img/logo.png" alt="voidraft Logo" class="logo-image">
</div>
<h2 class="logo-text" data-en="voidraft" data-zh="voidraft">voidraft</h2>
<p class="tagline" data-en="An elegant text snippet recording tool" data-zh="优雅的文本片段记录工具">An elegant text snippet recording tool</p>
</div>
<div class="intro-box">
<p class="intro-text" data-en="Designed for developers to record, organize, and manage various text snippets anytime, anywhere." data-zh="专为开发者打造,随时随地记录、整理和管理各种文本片段。">Designed for developers to record, organize, and manage various text snippets anytime, anywhere.</p>
</div>
<div class="button-group">
<a href="https://github.com/landaiqing/voidraft/releases" class="btn" data-en="Download" data-zh="下载">
<i class="fas fa-download"></i> Download
</a>
<a href="https://github.com/landaiqing/voidraft" class="btn btn-secondary" data-en="Source Code" data-zh="源代码">
<i class="fab fa-github"></i> Source Code
</a>
<a href="changelog.html" class="btn btn-secondary" data-en="Changelog" data-zh="更新日志">
<i class="fas fa-history"></i> Changelog
</a>
</div>
<!-- 特性部分 -->
<h2 data-en="Core Features" data-zh="核心特性">Core Features</h2>
<div class="features-grid">
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-code"></i>
</div>
<h3 class="feature-title" data-en="Developer-Friendly" data-zh="开发者友好">Developer-Friendly</h3>
<p class="feature-desc" data-en="Multi-language code blocks with syntax highlighting for 30+ programming languages" data-zh="多语言代码块支持为30+种编程语言提供语法高亮">Multi-language code blocks with syntax highlighting for 30+ programming languages</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-magic"></i>
</div>
<h3 class="feature-title" data-en="Code Formatting" data-zh="代码格式化">Code Formatting</h3>
<p class="feature-desc" data-en="Built-in Prettier support for one-click code beautification" data-zh="内置Prettier支持一键美化代码">Built-in Prettier support for one-click code beautification</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-palette"></i>
</div>
<h3 class="feature-title" data-en="Custom Themes" data-zh="自定义主题">Custom Themes</h3>
<p class="feature-desc" data-en="Dark/Light themes with full customization options" data-zh="深色/浅色主题,支持完全自定义">Dark/Light themes with full customization options</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-clone"></i>
</div>
<h3 class="feature-title" data-en="Multi-Window" data-zh="多窗口支持">Multi-Window</h3>
<p class="feature-desc" data-en="Edit multiple documents simultaneously" data-zh="同时编辑多个文档">Edit multiple documents simultaneously</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-layer-group"></i>
</div>
<h3 class="feature-title" data-en="Block Editing" data-zh="块状编辑">Block Editing</h3>
<p class="feature-desc" data-en="Split content into independent code blocks with different language settings" data-zh="将内容分割为独立的代码块,每个块可设置不同语言">Split content into independent code blocks with different language settings</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-puzzle-piece"></i>
</div>
<h3 class="feature-title" data-en="Extensions" data-zh="丰富扩展">Extensions</h3>
<p class="feature-desc" data-en="Rainbow brackets, VSCode-style search, color picker, translation tool, and more" data-zh="彩虹括号、VSCode风格搜索、颜色选择器、翻译工具等多种扩展">Rainbow brackets, VSCode-style search, color picker, translation tool, and more</p>
</div>
</div>
<!-- 预览部分 -->
<h2 data-en="Preview" data-zh="预览">Preview</h2>
<div class="preview-container">
<div class="preview-window">
<div class="preview-header">
<div class="preview-controls">
<span class="preview-btn"></span>
<span class="preview-btn"></span>
<span class="preview-btn"></span>
</div>
<div class="preview-title">voidraft</div>
</div>
<div class="preview-content">
<div class="code-block-wrapper">
<div class="block-header">
<div class="block-language">javascript</div>
</div>
<pre class="code-block">
<span class="keyword">function</span> <span class="function">createDocument</span>() {
<span class="keyword">const</span> <span class="variable">doc</span> = <span class="keyword">new</span> <span class="class">Document</span>();
<span class="variable">doc</span>.<span class="function">addCodeBlock</span>(<span class="string">'javascript'</span>, <span class="string">`
<span class="keyword">function</span> <span class="function">greeting</span>(<span class="parameter">name</span>) {
<span class="keyword">return</span> <span class="string">`Hello, </span>${<span class="parameter">name</span>}<span class="string">!`</span>;
}
<span class="built-in">console</span>.<span class="function">log</span>(<span class="function">greeting</span>(<span class="string">'World'</span>));
`</span>);
<span class="keyword">return</span> <span class="variable">doc</span>;
}</pre>
</div>
<div class="code-block-wrapper" style="margin-top: 10px;">
<div class="block-header">
<div class="block-language">text</div>
</div>
<pre class="code-block">
<span class="comment">// voidraft - An elegant text snippet recording tool</span>
<span class="comment">// Multi-language support | Code formatting | Custom themes</span>
<span class="comment">// A modern text editor designed for developers</span></pre>
</div>
</div>
</div>
<div class="preview-window">
<img src="img/screenshot-dark.png" alt="voidraft 界面预览" class="preview-image dark-theme-img">
<img src="img/screenshot-light.png" alt="voidraft 界面预览" class="preview-image light-theme-img" style="display: none;">
</div>
</div>
<!-- 技术栈部分 -->
<h2 data-en="Technical Stack" data-zh="技术栈">Technical Stack</h2>
<ul class="tech-list">
<li class="tech-item">
<div class="tech-icon"><i class="fas fa-desktop"></i></div>
<span class="tech-name">Wails3</span>
<span class="tech-desc" data-en="Cross-platform desktop application framework" data-zh="跨平台桌面应用框架">Cross-platform desktop application framework</span>
</li>
<li class="tech-item">
<div class="tech-icon"><i class="fas fa-cogs"></i></div>
<span class="tech-name">Go 1.21+</span>
<span class="tech-desc" data-en="Fast and efficient backend language" data-zh="快速高效的后端语言">Fast and efficient backend language</span>
</li>
<li class="tech-item">
<div class="tech-icon"><i class="fab fa-vuejs"></i></div>
<span class="tech-name">Vue 3 + TypeScript</span>
<span class="tech-desc" data-en="Modern frontend framework" data-zh="现代化前端框架">Modern frontend framework</span>
</li>
<li class="tech-item">
<div class="tech-icon"><i class="fas fa-edit"></i></div>
<span class="tech-name">CodeMirror 6</span>
<span class="tech-desc" data-en="Modern code editor with extension support" data-zh="支持扩展的现代化代码编辑器">Modern code editor with extension support</span>
</li>
<li class="tech-item">
<div class="tech-icon"><i class="fas fa-database"></i></div>
<span class="tech-name">SQLite</span>
<span class="tech-desc" data-en="Lightweight database for document storage" data-zh="轻量级文档存储数据库">Lightweight database for document storage</span>
</li>
</ul>
</div>
<!-- 页脚 -->
<footer class="footer">
<p class="footer-text" data-en="© 2025 voidraft - An elegant text snippet recording tool designed for developers" data-zh="© 2025 voidraft - 专为开发者打造的优雅文本片段记录工具">© 2025 voidraft - An elegant text snippet recording tool designed for developers</p>
<div class="footer-links">
<a href="https://github.com/landaiqing/voidraft" target="_blank" class="footer-link">GitHub</a>
<a href="https://github.com/landaiqing/voidraft/issues" target="_blank" class="footer-link" data-en="Issues" data-zh="问题反馈">Issues</a>
<a href="https://github.com/landaiqing/voidraft/releases" target="_blank" class="footer-link" data-en="Releases" data-zh="版本发布">Releases</a>
</div>
</footer>
</div>
</div>
<script src="js/script.js"></script>
</body>
</html>

View File

@@ -1,705 +0,0 @@
/**
* voidraft - Changelog Script
* 从GitHub API获取发布信息支持Gitea备用源
*/
/**
* 仓库配置类
*/
class RepositoryConfig {
constructor() {
this.repos = {
github: {
owner: 'landaiqing',
name: 'voidraft',
apiUrl: 'https://api.github.com/repos/landaiqing/voidraft/releases',
releasesUrl: 'https://github.com/landaiqing/voidraft/releases'
},
gitea: {
owner: 'landaiqing',
name: 'voidraft',
domain: 'git.landaiqing.cn',
apiUrl: 'https://git.landaiqing.cn/api/v1/repos/landaiqing/voidraft/releases',
releasesUrl: 'https://git.landaiqing.cn/landaiqing/voidraft/releases'
}
};
}
/**
* 获取仓库配置
* @param {string} source - 'github' 或 'gitea'
*/
getRepo(source) {
return this.repos[source];
}
/**
* 获取所有仓库配置
*/
getAllRepos() {
return this.repos;
}
}
/**
* 国际化消息管理类
*/
class I18nMessages {
constructor() {
this.messages = {
loading: {
en: 'Loading releases...',
zh: '正在加载版本信息...'
},
noReleases: {
en: 'No release information found',
zh: '没有找到版本发布信息'
},
fetchError: {
en: 'Failed to load release information. Please try again later.',
zh: '无法获取版本信息,请稍后再试'
},
githubApiError: {
en: 'GitHub API returned an error status: ',
zh: 'GitHub API返回错误状态: '
},
giteaApiError: {
en: 'Gitea API returned an error status: ',
zh: 'Gitea API返回错误状态: '
},
dataSource: {
en: 'Data source: ',
zh: '数据来源: '
},
downloads: {
en: 'Downloads',
zh: '下载资源'
},
download: {
en: 'Download',
zh: '下载'
},
preRelease: {
en: 'Pre-release',
zh: '预发布'
}
};
}
/**
* 获取消息
* @param {string} key - 消息键
* @param {string} lang - 语言代码
*/
getMessage(key, lang = 'en') {
return this.messages[key] && this.messages[key][lang] || this.messages[key]['en'] || '';
}
/**
* 获取当前语言
*/
getCurrentLang() {
return window.currentLang || 'en';
}
}
/**
* API客户端类
*/
class APIClient {
constructor(repositoryConfig, i18nMessages) {
this.repositoryConfig = repositoryConfig;
this.i18nMessages = i18nMessages;
}
/**
* 从指定源获取发布信息
* @param {string} source - 'github' 或 'gitea'
*/
async fetchReleases(source) {
const repo = this.repositoryConfig.getRepo(source);
const errorMessageKey = source === 'github' ? 'githubApiError' : 'giteaApiError';
const options = {
headers: { 'Accept': 'application/json' }
};
if (source === 'github') {
return this.fetchFromGitHub(repo, options, errorMessageKey);
} else {
return this.fetchFromGitea(repo, options, errorMessageKey);
}
}
/**
* 从GitHub获取数据
* @param {Object} repo - 仓库配置
* @param {Object} options - 请求选项
* @param {string} errorMessageKey - 错误消息键
*/
async fetchFromGitHub(repo, options, errorMessageKey) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
options.signal = controller.signal;
options.headers['Accept'] = 'application/vnd.github.v3+json';
try {
const response = await fetch(repo.apiUrl, options);
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`${this.i18nMessages.getMessage(errorMessageKey, this.i18nMessages.getCurrentLang())}${response.status}`);
}
const releases = await response.json();
if (!releases || releases.length === 0) {
throw new Error(this.i18nMessages.getMessage('noReleases', this.i18nMessages.getCurrentLang()));
}
return releases;
} catch (error) {
clearTimeout(timeoutId);
throw error;
}
}
/**
* 从Gitea获取数据
* @param {Object} repo - 仓库配置
* @param {Object} options - 请求选项
* @param {string} errorMessageKey - 错误消息键
*/
async fetchFromGitea(repo, options, errorMessageKey) {
const response = await fetch(repo.apiUrl, options);
if (!response.ok) {
throw new Error(`${this.i18nMessages.getMessage(errorMessageKey, this.i18nMessages.getCurrentLang())}${response.status}`);
}
const releases = await response.json();
if (!releases || releases.length === 0) {
throw new Error(this.i18nMessages.getMessage('noReleases', this.i18nMessages.getCurrentLang()));
}
return releases;
}
}
/**
* UI管理类
*/
class UIManager {
constructor(i18nMessages) {
this.i18nMessages = i18nMessages;
this.elements = {
loading: document.getElementById('loading'),
changelog: document.getElementById('changelog'),
error: document.getElementById('error-message')
};
}
/**
* 显示加载状态
*/
showLoading() {
this.elements.loading.style.display = 'block';
this.elements.error.style.display = 'none';
this.elements.changelog.innerHTML = '';
}
/**
* 隐藏加载状态
*/
hideLoading() {
this.elements.loading.style.display = 'none';
}
/**
* 显示错误消息
* @param {string} message - 错误消息
*/
showError(message) {
const errorMessageElement = this.elements.error.querySelector('p');
if (errorMessageElement) {
errorMessageElement.textContent = message;
} else {
this.elements.error.textContent = message;
}
this.elements.error.style.display = 'block';
this.hideLoading();
}
/**
* 显示发布信息
* @param {Array} releases - 发布信息数组
* @param {string} source - 数据源
*/
displayReleases(releases, source) {
this.hideLoading();
// 清除现有内容
this.elements.changelog.innerHTML = '';
// 创建数据源元素
const sourceElement = this.createSourceElement(source);
this.elements.changelog.appendChild(sourceElement);
// 创建发布信息元素
releases.forEach(release => {
const releaseElement = this.createReleaseElement(release, source);
this.elements.changelog.appendChild(releaseElement);
});
this.elements.changelog.style.display = 'block';
}
/**
* 创建数据源元素
* @param {string} source - 数据源
*/
createSourceElement(source) {
const sourceElement = document.createElement('div');
sourceElement.className = 'data-source';
// 创建带有国际化支持的源标签
const sourceLabel = document.createElement('span');
sourceLabel.setAttribute('data-en', this.i18nMessages.getMessage('dataSource', 'en'));
sourceLabel.setAttribute('data-zh', this.i18nMessages.getMessage('dataSource', 'zh'));
sourceLabel.textContent = this.i18nMessages.getMessage('dataSource', this.i18nMessages.getCurrentLang());
// 创建链接
const sourceLink = document.createElement('a');
const repositoryConfig = new RepositoryConfig();
sourceLink.href = repositoryConfig.getRepo(source).releasesUrl;
sourceLink.textContent = source === 'github' ? 'GitHub' : 'Gitea';
sourceLink.target = '_blank';
// 组装元素
sourceElement.appendChild(sourceLabel);
sourceElement.appendChild(sourceLink);
return sourceElement;
}
/**
* 创建发布信息元素
* @param {Object} release - 发布信息对象
* @param {string} source - 数据源
*/
createReleaseElement(release, source) {
const releaseElement = document.createElement('div');
releaseElement.className = 'release';
// 格式化发布日期
const releaseDate = new Date(release.published_at || release.created_at);
const formattedDate = DateFormatter.formatDate(releaseDate);
// 创建头部
const headerElement = this.createReleaseHeader(release, formattedDate);
releaseElement.appendChild(headerElement);
// 添加发布说明
if (release.body) {
const descriptionElement = document.createElement('div');
descriptionElement.className = 'release-description markdown-content';
descriptionElement.innerHTML = MarkdownParser.parseMarkdown(release.body);
releaseElement.appendChild(descriptionElement);
}
// 添加下载资源
const assets = AssetManager.getAssetsFromRelease(release, source);
if (assets && assets.length > 0) {
const assetsElement = this.createAssetsElement(assets);
releaseElement.appendChild(assetsElement);
}
return releaseElement;
}
/**
* 创建发布信息头部
*/
createReleaseHeader(release, formattedDate) {
const headerElement = document.createElement('div');
headerElement.className = 'release-header';
// 版本元素
const versionElement = document.createElement('div');
versionElement.className = 'release-version';
// 版本文本
const versionText = document.createElement('span');
versionText.textContent = release.name || release.tag_name;
versionElement.appendChild(versionText);
// 预发布标记
if (release.prerelease) {
const preReleaseTag = document.createElement('span');
preReleaseTag.className = 'release-badge pre-release';
preReleaseTag.setAttribute('data-en', this.i18nMessages.getMessage('preRelease', 'en'));
preReleaseTag.setAttribute('data-zh', this.i18nMessages.getMessage('preRelease', 'zh'));
preReleaseTag.textContent = this.i18nMessages.getMessage('preRelease', this.i18nMessages.getCurrentLang());
versionElement.appendChild(preReleaseTag);
}
// 日期元素
const dateElement = document.createElement('div');
dateElement.className = 'release-date';
dateElement.textContent = formattedDate;
headerElement.appendChild(versionElement);
headerElement.appendChild(dateElement);
return headerElement;
}
/**
* 创建资源文件元素
* @param {Array} assets - 资源文件数组
*/
createAssetsElement(assets) {
const assetsElement = document.createElement('div');
assetsElement.className = 'release-assets';
// 资源标题
const assetsTitle = document.createElement('div');
assetsTitle.className = 'release-assets-title';
assetsTitle.setAttribute('data-en', this.i18nMessages.getMessage('downloads', 'en'));
assetsTitle.setAttribute('data-zh', this.i18nMessages.getMessage('downloads', 'zh'));
assetsTitle.textContent = this.i18nMessages.getMessage('downloads', this.i18nMessages.getCurrentLang());
// 资源列表
const assetList = document.createElement('ul');
assetList.className = 'asset-list';
// 添加每个资源
assets.forEach(asset => {
const assetItem = this.createAssetItem(asset);
assetList.appendChild(assetItem);
});
assetsElement.appendChild(assetsTitle);
assetsElement.appendChild(assetList);
return assetsElement;
}
/**
* 创建资源文件项
* @param {Object} asset - 资源文件对象
*/
createAssetItem(asset) {
const assetItem = document.createElement('li');
assetItem.className = 'asset-item';
// 文件图标
const iconElement = document.createElement('i');
iconElement.className = `asset-icon fas fa-${FileIconHelper.getFileIcon(asset.name)}`;
// 文件名
const nameElement = document.createElement('span');
nameElement.className = 'asset-name';
nameElement.textContent = asset.name;
// 文件大小
const sizeElement = document.createElement('span');
sizeElement.className = 'asset-size';
sizeElement.textContent = FileSizeFormatter.formatFileSize(asset.size);
// 下载链接
const downloadLink = document.createElement('a');
downloadLink.className = 'download-btn';
downloadLink.href = asset.browser_download_url;
downloadLink.target = '_blank';
downloadLink.setAttribute('data-en', this.i18nMessages.getMessage('download', 'en'));
downloadLink.setAttribute('data-zh', this.i18nMessages.getMessage('download', 'zh'));
downloadLink.textContent = this.i18nMessages.getMessage('download', this.i18nMessages.getCurrentLang());
// 组装资源项
assetItem.appendChild(iconElement);
assetItem.appendChild(nameElement);
assetItem.appendChild(sizeElement);
assetItem.appendChild(downloadLink);
return assetItem;
}
}
/**
* 资源管理器类
*/
class AssetManager {
/**
* 从发布信息中获取资源文件
* @param {Object} release - 发布信息对象
* @param {string} source - 数据源
*/
static getAssetsFromRelease(release, source) {
let assets = [];
if (source === 'github') {
assets = release.assets || [];
} else { // Gitea
assets = release.assets || [];
// 检查Gitea特定的资源结构
if (!assets.length && release.attachments) {
assets = release.attachments.map(attachment => ({
name: attachment.name,
size: attachment.size,
browser_download_url: attachment.browser_download_url
}));
}
}
return assets;
}
}
/**
* 文件图标助手类
*/
class FileIconHelper {
/**
* 根据文件扩展名获取图标
* @param {string} filename - 文件名
*/
static getFileIcon(filename) {
const extension = filename.split('.').pop().toLowerCase();
const iconMap = {
'exe': 'download',
'msi': 'download',
'dmg': 'download',
'pkg': 'download',
'deb': 'download',
'rpm': 'download',
'tar': 'file-archive',
'gz': 'file-archive',
'zip': 'file-archive',
'7z': 'file-archive',
'rar': 'file-archive',
'pdf': 'file-pdf',
'txt': 'file-alt',
'md': 'file-alt',
'json': 'file-code',
'xml': 'file-code',
'yml': 'file-code',
'yaml': 'file-code'
};
return iconMap[extension] || 'file';
}
}
/**
* 文件大小格式化器类
*/
class FileSizeFormatter {
/**
* 格式化文件大小
* @param {number} bytes - 字节数
*/
static formatFileSize(bytes) {
if (!bytes) return '';
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
}
}
/**
* 日期格式化器类
*/
class DateFormatter {
/**
* 格式化日期
* @param {Date} date - 日期对象
*/
static formatDate(date) {
const options = {
year: 'numeric',
month: 'long',
day: 'numeric'
};
const lang = window.currentLang || 'en';
const locale = lang === 'zh' ? 'zh-CN' : 'en-US';
return date.toLocaleDateString(locale, options);
}
}
/**
* Markdown解析器类
*/
class MarkdownParser {
/**
* 简单的Markdown解析
* @param {string} markdown - Markdown文本
*/
static parseMarkdown(markdown) {
if (!markdown) return '';
// 预处理:保留原始换行符,用特殊标记替换
const preservedLineBreaks = '___LINE_BREAK___';
markdown = markdown.replace(/\n/g, preservedLineBreaks);
// 引用块 - > text
markdown = markdown.replace(/&gt;\s*(.*?)(?=&gt;|$)/g, '<blockquote>$1</blockquote>');
markdown = markdown.replace(/>\s*(.*?)(?=>|$)/g, '<blockquote>$1</blockquote>');
// 链接 - [text](url)
markdown = markdown.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>');
// 标题 - # Heading
markdown = markdown.replace(/^### (.*?)(?=___LINE_BREAK___|$)/gm, '<h3>$1</h3>');
markdown = markdown.replace(/^## (.*?)(?=___LINE_BREAK___|$)/gm, '<h2>$1</h2>');
markdown = markdown.replace(/^# (.*?)(?=___LINE_BREAK___|$)/gm, '<h1>$1</h1>');
// 粗体 - **text**
markdown = markdown.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
// 斜体 - *text*
markdown = markdown.replace(/\*(.*?)\*/g, '<em>$1</em>');
// 代码块 - ```code```
markdown = markdown.replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>');
// 行内代码 - `code`
markdown = markdown.replace(/`([^`]+)`/g, '<code>$1</code>');
// 处理列表项
// 先将每个列表项转换为HTML
markdown = markdown.replace(/- (.*?)(?=___LINE_BREAK___- |___LINE_BREAK___$|$)/g, '<li>$1</li>');
markdown = markdown.replace(/\* (.*?)(?=___LINE_BREAK___\* |___LINE_BREAK___$|$)/g, '<li>$1</li>');
markdown = markdown.replace(/\d+\. (.*?)(?=___LINE_BREAK___\d+\. |___LINE_BREAK___$|$)/g, '<li>$1</li>');
// 然后将连续的列表项包装在ul或ol中
const listItemRegex = /<li>.*?<\/li>/g;
const listItems = markdown.match(listItemRegex) || [];
if (listItems.length > 0) {
// 将连续的列表项组合在一起
let lastIndex = 0;
let result = '';
let inList = false;
listItems.forEach(item => {
const itemIndex = markdown.indexOf(item, lastIndex);
// 添加列表项之前的内容
if (itemIndex > lastIndex) {
result += markdown.substring(lastIndex, itemIndex);
}
// 如果不在列表中,开始一个新列表
if (!inList) {
result += '<ul>';
inList = true;
}
// 添加列表项
result += item;
// 更新lastIndex
lastIndex = itemIndex + item.length;
// 检查下一个内容是否是列表项
const nextItemIndex = markdown.indexOf('<li>', lastIndex);
if (nextItemIndex === -1 || nextItemIndex > lastIndex + 20) { // 如果下一个列表项不紧邻
result += '</ul>';
inList = false;
}
});
// 添加剩余内容
if (lastIndex < markdown.length) {
result += markdown.substring(lastIndex);
}
markdown = result;
}
// 处理水平分隔线
markdown = markdown.replace(/---/g, '<hr>');
// 恢复换行符
markdown = markdown.replace(/___LINE_BREAK___/g, '<br>');
// 处理段落
markdown = markdown.replace(/<br><br>/g, '</p><p>');
// 包装在段落标签中
if (!markdown.startsWith('<p>')) {
markdown = `<p>${markdown}</p>`;
}
return markdown;
}
}
/**
* 更新日志主应用类
*/
class ChangelogApp {
constructor() {
this.repositoryConfig = new RepositoryConfig();
this.i18nMessages = new I18nMessages();
this.apiClient = new APIClient(this.repositoryConfig, this.i18nMessages);
this.uiManager = new UIManager(this.i18nMessages);
this.init();
}
/**
* 初始化应用
*/
init() {
this.uiManager.showLoading();
// 首先尝试GitHub API
this.apiClient.fetchReleases('github')
.then(releases => {
this.uiManager.displayReleases(releases, 'github');
})
.catch(() => {
// GitHub失败时尝试Gitea
return this.apiClient.fetchReleases('gitea')
.then(releases => {
this.uiManager.displayReleases(releases, 'gitea');
});
})
.catch(error => {
console.error('获取发布信息失败:', error);
this.uiManager.showError(this.i18nMessages.getMessage('fetchError', this.i18nMessages.getCurrentLang()));
});
// 监听语言变化事件
document.addEventListener('languageChanged', () => this.updateUI());
}
/**
* 更新UI元素当语言变化时
*/
updateUI() {
const elementsToUpdate = document.querySelectorAll('[data-en][data-zh]');
const currentLang = this.i18nMessages.getCurrentLang();
elementsToUpdate.forEach(element => {
const text = element.getAttribute(`data-${currentLang}`);
if (text) {
element.textContent = text;
}
});
}
}
// 当DOM加载完成时初始化应用
document.addEventListener('DOMContentLoaded', () => {
new ChangelogApp();
});

View File

@@ -1,443 +0,0 @@
/**
* voidraft - Website Script
*/
/**
* 主题管理类
*/
class ThemeManager {
constructor() {
this.themeToggle = document.getElementById('theme-toggle');
this.currentTheme = this.getInitialTheme();
this.init();
}
/**
* 获取初始主题
*/
getInitialTheme() {
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
const savedTheme = localStorage.getItem('theme');
return savedTheme || (prefersDarkScheme.matches ? 'dark' : 'light');
}
/**
* 初始化主题管理器
*/
init() {
if (!this.themeToggle) return;
this.setTheme(this.currentTheme);
this.bindEvents();
}
/**
* 绑定事件
*/
bindEvents() {
this.themeToggle.addEventListener('click', () => {
this.toggleTheme();
});
}
/**
* 切换主题
*/
toggleTheme() {
document.body.classList.add('theme-transition');
const newTheme = this.currentTheme === 'dark' ? 'light' : 'dark';
this.setTheme(newTheme);
this.saveTheme(newTheme);
setTimeout(() => document.body.classList.remove('theme-transition'), 300);
}
/**
* 设置主题
* @param {string} theme - 'dark' 或 'light'
*/
setTheme(theme) {
this.currentTheme = theme;
const isDark = theme === 'dark';
document.body.classList.toggle('theme-dark', isDark);
document.body.classList.toggle('theme-light', !isDark);
this.updateToggleIcon(isDark);
}
/**
* 更新切换按钮图标
* @param {boolean} isDark - 是否为暗色主题
*/
updateToggleIcon(isDark) {
if (this.themeToggle) {
const icon = this.themeToggle.querySelector('i');
if (icon) {
icon.className = isDark ? 'fas fa-sun' : 'fas fa-moon';
}
}
}
/**
* 保存主题到本地存储
* @param {string} theme - 主题名称
*/
saveTheme(theme) {
localStorage.setItem('theme', theme);
}
}
/**
* 语言管理类
*/
class LanguageManager {
constructor() {
this.langToggle = document.getElementById('lang-toggle');
this.currentLang = this.getInitialLanguage();
this.init();
}
/**
* 获取初始语言
*/
getInitialLanguage() {
const urlParams = new URLSearchParams(window.location.search);
const urlLang = urlParams.get('lang');
const savedLang = localStorage.getItem('lang');
const browserLang = navigator.language.startsWith('zh') ? 'zh' : 'en';
return urlLang || savedLang || browserLang;
}
/**
* 初始化语言管理器
*/
init() {
if (!this.langToggle) return;
window.currentLang = this.currentLang;
this.setLanguage(this.currentLang);
this.bindEvents();
}
/**
* 绑定事件
*/
bindEvents() {
this.langToggle.addEventListener('click', () => {
this.toggleLanguage();
});
}
/**
* 切换语言
*/
toggleLanguage() {
document.body.classList.add('lang-transition');
const newLang = this.currentLang === 'zh' ? 'en' : 'zh';
this.setLanguage(newLang);
this.saveLanguage(newLang);
this.updateURL(newLang);
this.notifyLanguageChange(newLang);
setTimeout(() => document.body.classList.remove('lang-transition'), 300);
}
/**
* 设置页面语言
* @param {string} lang - 'zh' 或 'en'
*/
setLanguage(lang) {
this.currentLang = lang;
window.currentLang = lang;
this.updatePageElements(lang);
this.updateHTMLLang(lang);
this.updateToggleButton(lang);
}
/**
* 更新页面元素文本
* @param {string} lang - 语言代码
*/
updatePageElements(lang) {
document.querySelectorAll('[data-zh][data-en]').forEach(el => {
el.textContent = el.getAttribute(`data-${lang}`);
});
}
/**
* 更新HTML语言属性
* @param {string} lang - 语言代码
*/
updateHTMLLang(lang) {
document.documentElement.lang = lang === 'zh' ? 'zh-CN' : 'en';
}
/**
* 更新切换按钮文本
* @param {string} lang - 语言代码
*/
updateToggleButton(lang) {
if (this.langToggle) {
const text = lang === 'zh' ? 'EN/中' : '中/EN';
this.langToggle.innerHTML = `<i class="fas fa-language"></i> ${text}`;
}
}
/**
* 保存语言到本地存储
* @param {string} lang - 语言代码
*/
saveLanguage(lang) {
localStorage.setItem('lang', lang);
}
/**
* 更新URL参数
* @param {string} lang - 语言代码
*/
updateURL(lang) {
const newUrl = new URL(window.location);
if (lang === 'zh') {
newUrl.searchParams.set('lang', 'zh');
} else {
newUrl.searchParams.delete('lang');
}
window.history.replaceState({}, '', newUrl);
}
/**
* 通知语言变更
* @param {string} lang - 语言代码
*/
notifyLanguageChange(lang) {
window.dispatchEvent(new CustomEvent('languageChanged', { detail: { lang } }));
}
/**
* 获取当前语言
*/
getCurrentLanguage() {
return this.currentLang;
}
}
/**
* SEO管理类
*/
class SEOManager {
constructor(languageManager) {
this.languageManager = languageManager;
this.metaTexts = {
en: {
description: 'voidraft is an elegant text snippet recording tool designed for developers. Features multi-language code blocks, syntax highlighting, code formatting, custom themes, and more.',
title: 'voidraft - An elegant text snippet recording tool designed for developers.',
ogTitle: 'voidraft - An elegant text snippet recording tool designed for developers'
},
zh: {
description: 'voidraft 是专为开发者打造的优雅文本片段记录工具。支持多语言代码块、语法高亮、代码格式化、自定义主题等功能。',
title: 'voidraft - 专为开发者打造的优雅文本片段记录工具',
ogTitle: 'voidraft - 专为开发者打造的优雅文本片段记录工具'
}
};
this.init();
}
/**
* 初始化SEO管理器
*/
init() {
this.bindEvents();
this.updateMetaTags(this.languageManager.getCurrentLanguage());
}
/**
* 绑定事件
*/
bindEvents() {
window.addEventListener('languageChanged', (event) => {
this.updateMetaTags(event.detail.lang);
});
}
/**
* 更新SEO元标签
* @param {string} lang - 当前语言
*/
updateMetaTags(lang) {
const texts = this.metaTexts[lang];
this.updateMetaDescription(texts.description);
this.updateOpenGraphTags(texts.ogTitle, texts.description);
this.updateTwitterCardTags(texts.ogTitle, texts.description);
this.updatePageTitle(texts.title);
}
/**
* 更新meta描述
* @param {string} description - 描述文本
*/
updateMetaDescription(description) {
const metaDesc = document.querySelector('meta[name="description"]');
if (metaDesc) {
metaDesc.content = description;
}
}
/**
* 更新Open Graph标签
* @param {string} title - 标题
* @param {string} description - 描述
*/
updateOpenGraphTags(title, description) {
const ogTitle = document.querySelector('meta[property="og:title"]');
const ogDesc = document.querySelector('meta[property="og:description"]');
if (ogTitle) ogTitle.content = title;
if (ogDesc) ogDesc.content = description;
}
/**
* 更新Twitter Card标签
* @param {string} title - 标题
* @param {string} description - 描述
*/
updateTwitterCardTags(title, description) {
const twitterTitle = document.querySelector('meta[property="twitter:title"]');
const twitterDesc = document.querySelector('meta[property="twitter:description"]');
if (twitterTitle) twitterTitle.content = title;
if (twitterDesc) twitterDesc.content = description;
}
/**
* 更新页面标题
* @param {string} title - 标题
*/
updatePageTitle(title) {
document.title = title;
}
}
/**
* UI效果管理类
*/
class UIEffects {
constructor() {
this.init();
}
/**
* 初始化UI效果
*/
init() {
this.initCardEffects();
}
/**
* 初始化卡片悬停效果
*/
initCardEffects() {
const cards = document.querySelectorAll('.feature-card');
cards.forEach(card => {
card.addEventListener('mouseenter', () => {
this.animateCardHover(card, true);
});
card.addEventListener('mouseleave', () => {
this.animateCardHover(card, false);
});
});
}
/**
* 卡片悬停动画
* @param {Element} card - 卡片元素
* @param {boolean} isHover - 是否悬停
*/
animateCardHover(card, isHover) {
if (isHover) {
card.style.transform = 'translateY(-8px)';
card.style.boxShadow = '7px 7px 0 var(--shadow-color)';
} else {
card.style.transform = 'translateY(0)';
card.style.boxShadow = '5px 5px 0 var(--shadow-color)';
}
}
}
/**
* voidraft主应用类
*/
class voidraftApp {
constructor() {
this.themeManager = null;
this.languageManager = null;
this.seoManager = null;
this.uiEffects = null;
this.init();
}
/**
* 初始化应用
*/
init() {
this.initializeManagers();
this.showConsoleBranding();
}
/**
* 初始化各个管理器
*/
initializeManagers() {
this.themeManager = new ThemeManager();
this.languageManager = new LanguageManager();
this.seoManager = new SEOManager(this.languageManager);
this.uiEffects = new UIEffects();
}
/**
* 显示控制台品牌信息
*/
showConsoleBranding() {
console.log('%c voidraft', 'color: #ff006e; font-size: 20px; font-family: "Space Mono", monospace;');
console.log('%c An elegant text snippet recording tool designed for developers.', 'color: #073B4C; font-family: "Space Mono", monospace;');
}
/**
* 获取主题管理器
*/
getThemeManager() {
return this.themeManager;
}
/**
* 获取语言管理器
*/
getLanguageManager() {
return this.languageManager;
}
/**
* 获取SEO管理器
*/
getSEOManager() {
return this.seoManager;
}
/**
* 获取UI效果管理器
*/
getUIEffects() {
return this.uiEffects;
}
}
// 当DOM加载完成时初始化应用
document.addEventListener('DOMContentLoaded', () => {
window.voidRaftApp = new voidraftApp();
});

View File

@@ -5,303 +5,6 @@
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import {Create as $Create} from "@wailsio/runtime"; import {Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as slog$0 from "../../../../../../log/slog/models.js";
export class App {
/**
* Manager pattern for organized API
*/
"Window": WindowManager | null;
"ContextMenu": ContextMenuManager | null;
"KeyBinding": KeyBindingManager | null;
"Browser": BrowserManager | null;
"Env": EnvironmentManager | null;
"Dialog": DialogManager | null;
"Event": EventManager | null;
"Menu": MenuManager | null;
"Screen": ScreenManager | null;
"Clipboard": ClipboardManager | null;
"SystemTray": SystemTrayManager | null;
"Logger": slog$0.Logger | null;
/** Creates a new App instance. */
constructor($$source: Partial<App> = {}) {
if (!("Window" in $$source)) {
this["Window"] = null;
}
if (!("ContextMenu" in $$source)) {
this["ContextMenu"] = null;
}
if (!("KeyBinding" in $$source)) {
this["KeyBinding"] = null;
}
if (!("Browser" in $$source)) {
this["Browser"] = null;
}
if (!("Env" in $$source)) {
this["Env"] = null;
}
if (!("Dialog" in $$source)) {
this["Dialog"] = null;
}
if (!("Event" in $$source)) {
this["Event"] = null;
}
if (!("Menu" in $$source)) {
this["Menu"] = null;
}
if (!("Screen" in $$source)) {
this["Screen"] = null;
}
if (!("Clipboard" in $$source)) {
this["Clipboard"] = null;
}
if (!("SystemTray" in $$source)) {
this["SystemTray"] = null;
}
if (!("Logger" in $$source)) {
this["Logger"] = null;
}
Object.assign(this, $$source);
}
/**
* Creates a new App instance from a string or object.
*/
static createFrom($$source: any = {}): App {
const $$createField0_0 = $$createType1;
const $$createField1_0 = $$createType3;
const $$createField2_0 = $$createType5;
const $$createField3_0 = $$createType7;
const $$createField4_0 = $$createType9;
const $$createField5_0 = $$createType11;
const $$createField6_0 = $$createType13;
const $$createField7_0 = $$createType15;
const $$createField8_0 = $$createType17;
const $$createField9_0 = $$createType19;
const $$createField10_0 = $$createType21;
const $$createField11_0 = $$createType23;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("Window" in $$parsedSource) {
$$parsedSource["Window"] = $$createField0_0($$parsedSource["Window"]);
}
if ("ContextMenu" in $$parsedSource) {
$$parsedSource["ContextMenu"] = $$createField1_0($$parsedSource["ContextMenu"]);
}
if ("KeyBinding" in $$parsedSource) {
$$parsedSource["KeyBinding"] = $$createField2_0($$parsedSource["KeyBinding"]);
}
if ("Browser" in $$parsedSource) {
$$parsedSource["Browser"] = $$createField3_0($$parsedSource["Browser"]);
}
if ("Env" in $$parsedSource) {
$$parsedSource["Env"] = $$createField4_0($$parsedSource["Env"]);
}
if ("Dialog" in $$parsedSource) {
$$parsedSource["Dialog"] = $$createField5_0($$parsedSource["Dialog"]);
}
if ("Event" in $$parsedSource) {
$$parsedSource["Event"] = $$createField6_0($$parsedSource["Event"]);
}
if ("Menu" in $$parsedSource) {
$$parsedSource["Menu"] = $$createField7_0($$parsedSource["Menu"]);
}
if ("Screen" in $$parsedSource) {
$$parsedSource["Screen"] = $$createField8_0($$parsedSource["Screen"]);
}
if ("Clipboard" in $$parsedSource) {
$$parsedSource["Clipboard"] = $$createField9_0($$parsedSource["Clipboard"]);
}
if ("SystemTray" in $$parsedSource) {
$$parsedSource["SystemTray"] = $$createField10_0($$parsedSource["SystemTray"]);
}
if ("Logger" in $$parsedSource) {
$$parsedSource["Logger"] = $$createField11_0($$parsedSource["Logger"]);
}
return new App($$parsedSource as Partial<App>);
}
}
/**
* BrowserManager manages browser-related operations
*/
export class BrowserManager {
/** Creates a new BrowserManager instance. */
constructor($$source: Partial<BrowserManager> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new BrowserManager instance from a string or object.
*/
static createFrom($$source: any = {}): BrowserManager {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new BrowserManager($$parsedSource as Partial<BrowserManager>);
}
}
/**
* ClipboardManager manages clipboard operations
*/
export class ClipboardManager {
/** Creates a new ClipboardManager instance. */
constructor($$source: Partial<ClipboardManager> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new ClipboardManager instance from a string or object.
*/
static createFrom($$source: any = {}): ClipboardManager {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new ClipboardManager($$parsedSource as Partial<ClipboardManager>);
}
}
/**
* ContextMenuManager manages all context menu operations
*/
export class ContextMenuManager {
/** Creates a new ContextMenuManager instance. */
constructor($$source: Partial<ContextMenuManager> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new ContextMenuManager instance from a string or object.
*/
static createFrom($$source: any = {}): ContextMenuManager {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new ContextMenuManager($$parsedSource as Partial<ContextMenuManager>);
}
}
/**
* DialogManager manages dialog-related operations
*/
export class DialogManager {
/** Creates a new DialogManager instance. */
constructor($$source: Partial<DialogManager> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new DialogManager instance from a string or object.
*/
static createFrom($$source: any = {}): DialogManager {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new DialogManager($$parsedSource as Partial<DialogManager>);
}
}
/**
* EnvironmentManager manages environment-related operations
*/
export class EnvironmentManager {
/** Creates a new EnvironmentManager instance. */
constructor($$source: Partial<EnvironmentManager> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new EnvironmentManager instance from a string or object.
*/
static createFrom($$source: any = {}): EnvironmentManager {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new EnvironmentManager($$parsedSource as Partial<EnvironmentManager>);
}
}
/**
* EventManager manages event-related operations
*/
export class EventManager {
/** Creates a new EventManager instance. */
constructor($$source: Partial<EventManager> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new EventManager instance from a string or object.
*/
static createFrom($$source: any = {}): EventManager {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new EventManager($$parsedSource as Partial<EventManager>);
}
}
/**
* KeyBindingManager manages all key binding operations
*/
export class KeyBindingManager {
/** Creates a new KeyBindingManager instance. */
constructor($$source: Partial<KeyBindingManager> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new KeyBindingManager instance from a string or object.
*/
static createFrom($$source: any = {}): KeyBindingManager {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new KeyBindingManager($$parsedSource as Partial<KeyBindingManager>);
}
}
/**
* MenuManager manages menu-related operations
*/
export class MenuManager {
/** Creates a new MenuManager instance. */
constructor($$source: Partial<MenuManager> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new MenuManager instance from a string or object.
*/
static createFrom($$source: any = {}): MenuManager {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new MenuManager($$parsedSource as Partial<MenuManager>);
}
}
export class ScreenManager {
/** Creates a new ScreenManager instance. */
constructor($$source: Partial<ScreenManager> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new ScreenManager instance from a string or object.
*/
static createFrom($$source: any = {}): ScreenManager {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new ScreenManager($$parsedSource as Partial<ScreenManager>);
}
}
/** /**
* ServiceOptions provides optional parameters for calls to [NewService]. * ServiceOptions provides optional parameters for calls to [NewService].
*/ */
@@ -359,85 +62,4 @@ export class ServiceOptions {
} }
} }
/** export type Window = any;
* SystemTrayManager manages system tray-related operations
*/
export class SystemTrayManager {
/** Creates a new SystemTrayManager instance. */
constructor($$source: Partial<SystemTrayManager> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new SystemTrayManager instance from a string or object.
*/
static createFrom($$source: any = {}): SystemTrayManager {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new SystemTrayManager($$parsedSource as Partial<SystemTrayManager>);
}
}
export class WebviewWindow {
/** Creates a new WebviewWindow instance. */
constructor($$source: Partial<WebviewWindow> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new WebviewWindow instance from a string or object.
*/
static createFrom($$source: any = {}): WebviewWindow {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new WebviewWindow($$parsedSource as Partial<WebviewWindow>);
}
}
/**
* WindowManager manages all window-related operations
*/
export class WindowManager {
/** Creates a new WindowManager instance. */
constructor($$source: Partial<WindowManager> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new WindowManager instance from a string or object.
*/
static createFrom($$source: any = {}): WindowManager {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new WindowManager($$parsedSource as Partial<WindowManager>);
}
}
// Private type creation functions
const $$createType0 = WindowManager.createFrom;
const $$createType1 = $Create.Nullable($$createType0);
const $$createType2 = ContextMenuManager.createFrom;
const $$createType3 = $Create.Nullable($$createType2);
const $$createType4 = KeyBindingManager.createFrom;
const $$createType5 = $Create.Nullable($$createType4);
const $$createType6 = BrowserManager.createFrom;
const $$createType7 = $Create.Nullable($$createType6);
const $$createType8 = EnvironmentManager.createFrom;
const $$createType9 = $Create.Nullable($$createType8);
const $$createType10 = DialogManager.createFrom;
const $$createType11 = $Create.Nullable($$createType10);
const $$createType12 = EventManager.createFrom;
const $$createType13 = $Create.Nullable($$createType12);
const $$createType14 = MenuManager.createFrom;
const $$createType15 = $Create.Nullable($$createType14);
const $$createType16 = ScreenManager.createFrom;
const $$createType17 = $Create.Nullable($$createType16);
const $$createType18 = ClipboardManager.createFrom;
const $$createType19 = $Create.Nullable($$createType18);
const $$createType20 = SystemTrayManager.createFrom;
const $$createType21 = $Create.Nullable($$createType20);
const $$createType22 = slog$0.Logger.createFrom;
const $$createType23 = $Create.Nullable($$createType22);

View File

@@ -2,7 +2,7 @@
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
/** /**
* Service represents the notifications service * Service represents the dock service
* @module * @module
*/ */
@@ -18,11 +18,19 @@ import * as application$0 from "../../application/models.js";
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import * as $models from "./models.js"; import * as $models from "./models.js";
/**
* HideAppIcon hides the app icon in the dock/taskbar.
*/
export function HideAppIcon(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3413658144) as any;
return $resultPromise;
}
/** /**
* RemoveBadge removes the badge label from the application icon. * RemoveBadge removes the badge label from the application icon.
*/ */
export function RemoveBadge(): Promise<void> & { cancel(): void } { export function RemoveBadge(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2374916939) as any; let $resultPromise = $Call.ByID(2752757297) as any;
return $resultPromise; return $resultPromise;
} }
@@ -30,7 +38,7 @@ export function RemoveBadge(): Promise<void> & { cancel(): void } {
* ServiceName returns the name of the service. * ServiceName returns the name of the service.
*/ */
export function ServiceName(): Promise<string> & { cancel(): void } { export function ServiceName(): Promise<string> & { cancel(): void } {
let $resultPromise = $Call.ByID(2428202016) as any; let $resultPromise = $Call.ByID(2949906614) as any;
return $resultPromise; return $resultPromise;
} }
@@ -38,7 +46,7 @@ export function ServiceName(): Promise<string> & { cancel(): void } {
* ServiceShutdown is called when the service is unloaded. * ServiceShutdown is called when the service is unloaded.
*/ */
export function ServiceShutdown(): Promise<void> & { cancel(): void } { export function ServiceShutdown(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3893755233) as any; let $resultPromise = $Call.ByID(307064411) as any;
return $resultPromise; return $resultPromise;
} }
@@ -46,7 +54,7 @@ export function ServiceShutdown(): Promise<void> & { cancel(): void } {
* ServiceStartup is called when the service is loaded. * ServiceStartup is called when the service is loaded.
*/ */
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } { export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(4078800764, options) as any; let $resultPromise = $Call.ByID(1350118426, options) as any;
return $resultPromise; return $resultPromise;
} }
@@ -54,11 +62,22 @@ export function ServiceStartup(options: application$0.ServiceOptions): Promise<v
* SetBadge sets the badge label on the application icon. * SetBadge sets the badge label on the application icon.
*/ */
export function SetBadge(label: string): Promise<void> & { cancel(): void } { export function SetBadge(label: string): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(784276339, label) as any; let $resultPromise = $Call.ByID(1717705661, label) as any;
return $resultPromise; return $resultPromise;
} }
export function SetCustomBadge(label: string, options: $models.Options): Promise<void> & { cancel(): void } { /**
let $resultPromise = $Call.ByID(3058653106, label, options) as any; * SetCustomBadge sets the badge label on the application icon with custom options.
*/
export function SetCustomBadge(label: string, options: $models.BadgeOptions): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2730169760, label, options) as any;
return $resultPromise;
}
/**
* ShowAppIcon shows the app icon in the dock/taskbar.
*/
export function ShowAppIcon(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3409697379) as any;
return $resultPromise; return $resultPromise;
} }

View File

@@ -1,9 +1,9 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
import * as BadgeService from "./badgeservice.js"; import * as DockService from "./dockservice.js";
export { export {
BadgeService DockService
}; };
export * from "./models.js"; export * from "./models.js";

View File

@@ -9,15 +9,18 @@ import {Create as $Create} from "@wailsio/runtime";
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import * as color$0 from "../../../../../../../image/color/models.js"; import * as color$0 from "../../../../../../../image/color/models.js";
export class Options { /**
* BadgeOptions represents options for customizing badge appearance
*/
export class BadgeOptions {
"TextColour": color$0.RGBA; "TextColour": color$0.RGBA;
"BackgroundColour": color$0.RGBA; "BackgroundColour": color$0.RGBA;
"FontName": string; "FontName": string;
"FontSize": number; "FontSize": number;
"SmallFontSize": number; "SmallFontSize": number;
/** Creates a new Options instance. */ /** Creates a new BadgeOptions instance. */
constructor($$source: Partial<Options> = {}) { constructor($$source: Partial<BadgeOptions> = {}) {
if (!("TextColour" in $$source)) { if (!("TextColour" in $$source)) {
this["TextColour"] = (new color$0.RGBA()); this["TextColour"] = (new color$0.RGBA());
} }
@@ -38,9 +41,9 @@ export class Options {
} }
/** /**
* Creates a new Options instance from a string or object. * Creates a new BadgeOptions instance from a string or object.
*/ */
static createFrom($$source: any = {}): Options { static createFrom($$source: any = {}): BadgeOptions {
const $$createField0_0 = $$createType0; const $$createField0_0 = $$createType0;
const $$createField1_0 = $$createType0; const $$createField1_0 = $$createType0;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
@@ -50,7 +53,7 @@ export class Options {
if ("BackgroundColour" in $$parsedSource) { if ("BackgroundColour" in $$parsedSource) {
$$parsedSource["BackgroundColour"] = $$createField1_0($$parsedSource["BackgroundColour"]); $$parsedSource["BackgroundColour"] = $$createField1_0($$parsedSource["BackgroundColour"]);
} }
return new Options($$parsedSource as Partial<Options>); return new BadgeOptions($$parsedSource as Partial<BadgeOptions>);
} }
} }

View File

@@ -1,31 +0,0 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Create as $Create} from "@wailsio/runtime";
/**
* A Logger records structured information about each call to its
* Log, Debug, Info, Warn, and Error methods.
* For each call, it creates a [Record] and passes it to a [Handler].
*
* To create a new Logger, call [New] or a Logger method
* that begins "With".
*/
export class Logger {
/** Creates a new Logger instance. */
constructor($$source: Partial<Logger> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new Logger instance from a string or object.
*/
static createFrom($$source: any = {}): Logger {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new Logger($$parsedSource as Partial<Logger>);
}
}

View File

@@ -0,0 +1,14 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Create as $Create} from "@wailsio/runtime";
/**
* A Header represents the key-value pairs in an HTTP header.
*
* The keys should be in canonical form, as returned by
* [CanonicalHeaderKey].
*/
export type Header = { [_: string]: string[] };

View File

@@ -68,4 +68,9 @@ export enum TranslatorType {
* DeeplTranslatorType DeepL翻译器 * DeeplTranslatorType DeepL翻译器
*/ */
DeeplTranslatorType = "deepl", DeeplTranslatorType = "deepl",
/**
* TartuNLPTranslatorType TartuNLP翻译器
*/
TartuNLPTranslatorType = "tartunlp",
}; };

View File

@@ -5,10 +5,6 @@
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import {Create as $Create} from "@wailsio/runtime"; import {Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as time$0 from "../../../time/models.js";
/** /**
* AppConfig 应用配置 - 按照前端设置页面分类组织 * AppConfig 应用配置 - 按照前端设置页面分类组织
*/ */
@@ -114,6 +110,11 @@ export class AppearanceConfig {
*/ */
"systemTheme": SystemThemeType; "systemTheme": SystemThemeType;
/**
* 当前选择的预设主题名称
*/
"currentTheme": string;
/** Creates a new AppearanceConfig instance. */ /** Creates a new AppearanceConfig instance. */
constructor($$source: Partial<AppearanceConfig> = {}) { constructor($$source: Partial<AppearanceConfig> = {}) {
if (!("language" in $$source)) { if (!("language" in $$source)) {
@@ -122,6 +123,9 @@ export class AppearanceConfig {
if (!("systemTheme" in $$source)) { if (!("systemTheme" in $$source)) {
this["systemTheme"] = ("" as SystemThemeType); this["systemTheme"] = ("" as SystemThemeType);
} }
if (!("currentTheme" in $$source)) {
this["currentTheme"] = "";
}
Object.assign(this, $$source); Object.assign(this, $$source);
} }
@@ -196,8 +200,8 @@ export class Document {
"id": number; "id": number;
"title": string; "title": string;
"content": string; "content": string;
"createdAt": time$0.Time; "createdAt": string;
"updatedAt": time$0.Time; "updatedAt": string;
"is_deleted": boolean; "is_deleted": boolean;
/** /**
@@ -217,10 +221,10 @@ export class Document {
this["content"] = ""; this["content"] = "";
} }
if (!("createdAt" in $$source)) { if (!("createdAt" in $$source)) {
this["createdAt"] = null; this["createdAt"] = "";
} }
if (!("updatedAt" in $$source)) { if (!("updatedAt" in $$source)) {
this["updatedAt"] = null; this["updatedAt"] = "";
} }
if (!("is_deleted" in $$source)) { if (!("is_deleted" in $$source)) {
this["is_deleted"] = false; this["is_deleted"] = false;
@@ -490,6 +494,11 @@ export class GeneralConfig {
*/ */
"enableLoadingAnimation": boolean; "enableLoadingAnimation": boolean;
/**
* 是否启用标签页模式
*/
"enableTabs": boolean;
/** Creates a new GeneralConfig instance. */ /** Creates a new GeneralConfig instance. */
constructor($$source: Partial<GeneralConfig> = {}) { constructor($$source: Partial<GeneralConfig> = {}) {
if (!("alwaysOnTop" in $$source)) { if (!("alwaysOnTop" in $$source)) {
@@ -516,6 +525,9 @@ export class GeneralConfig {
if (!("enableLoadingAnimation" in $$source)) { if (!("enableLoadingAnimation" in $$source)) {
this["enableLoadingAnimation"] = false; this["enableLoadingAnimation"] = false;
} }
if (!("enableTabs" in $$source)) {
this["enableTabs"] = false;
}
Object.assign(this, $$source); Object.assign(this, $$source);
} }
@@ -1143,8 +1155,8 @@ export class Theme {
"type": ThemeType; "type": ThemeType;
"colors": ThemeColorConfig; "colors": ThemeColorConfig;
"isDefault": boolean; "isDefault": boolean;
"createdAt": time$0.Time; "createdAt": string;
"updatedAt": time$0.Time; "updatedAt": string;
/** Creates a new Theme instance. */ /** Creates a new Theme instance. */
constructor($$source: Partial<Theme> = {}) { constructor($$source: Partial<Theme> = {}) {
@@ -1158,16 +1170,16 @@ export class Theme {
this["type"] = ("" as ThemeType); this["type"] = ("" as ThemeType);
} }
if (!("colors" in $$source)) { if (!("colors" in $$source)) {
this["colors"] = (new ThemeColorConfig()); this["colors"] = ({} as ThemeColorConfig);
} }
if (!("isDefault" in $$source)) { if (!("isDefault" in $$source)) {
this["isDefault"] = false; this["isDefault"] = false;
} }
if (!("createdAt" in $$source)) { if (!("createdAt" in $$source)) {
this["createdAt"] = null; this["createdAt"] = "";
} }
if (!("updatedAt" in $$source)) { if (!("updatedAt" in $$source)) {
this["updatedAt"] = null; this["updatedAt"] = "";
} }
Object.assign(this, $$source); Object.assign(this, $$source);
@@ -1187,212 +1199,9 @@ export class Theme {
} }
/** /**
* ThemeColorConfig 主题颜色配置 * ThemeColorConfig 使用与前端 ThemeColors 相同的结构,存储任意主题键值
*/ */
export class ThemeColorConfig { export type ThemeColorConfig = { [_: string]: any };
/**
* 基础色调
* 主背景色
*/
"background": string;
/**
* 次要背景色
*/
"backgroundSecondary": string;
/**
* 面板背景
*/
"surface": string;
/**
* 主文本色
*/
"foreground": string;
/**
* 次要文本色
*/
"foregroundSecondary": string;
/**
* 语法高亮
* 注释色
*/
"comment": string;
/**
* 关键字
*/
"keyword": string;
/**
* 字符串
*/
"string": string;
/**
* 函数名
*/
"function": string;
/**
* 数字
*/
"number": string;
/**
* 操作符
*/
"operator": string;
/**
* 变量
*/
"variable": string;
/**
* 类型
*/
"type": string;
/**
* 界面元素
* 光标
*/
"cursor": string;
/**
* 选中背景
*/
"selection": string;
/**
* 失焦选中背景
*/
"selectionBlur": string;
/**
* 当前行高亮
*/
"activeLine": string;
/**
* 行号
*/
"lineNumber": string;
/**
* 活动行号
*/
"activeLineNumber": string;
/**
* 边框分割线
* 边框色
*/
"borderColor": string;
/**
* 浅色边框
*/
"borderLight": string;
/**
* 搜索匹配
* 搜索匹配
*/
"searchMatch": string;
/**
* 匹配括号
*/
"matchingBracket": string;
/** Creates a new ThemeColorConfig instance. */
constructor($$source: Partial<ThemeColorConfig> = {}) {
if (!("background" in $$source)) {
this["background"] = "";
}
if (!("backgroundSecondary" in $$source)) {
this["backgroundSecondary"] = "";
}
if (!("surface" in $$source)) {
this["surface"] = "";
}
if (!("foreground" in $$source)) {
this["foreground"] = "";
}
if (!("foregroundSecondary" in $$source)) {
this["foregroundSecondary"] = "";
}
if (!("comment" in $$source)) {
this["comment"] = "";
}
if (!("keyword" in $$source)) {
this["keyword"] = "";
}
if (!("string" in $$source)) {
this["string"] = "";
}
if (!("function" in $$source)) {
this["function"] = "";
}
if (!("number" in $$source)) {
this["number"] = "";
}
if (!("operator" in $$source)) {
this["operator"] = "";
}
if (!("variable" in $$source)) {
this["variable"] = "";
}
if (!("type" in $$source)) {
this["type"] = "";
}
if (!("cursor" in $$source)) {
this["cursor"] = "";
}
if (!("selection" in $$source)) {
this["selection"] = "";
}
if (!("selectionBlur" in $$source)) {
this["selectionBlur"] = "";
}
if (!("activeLine" in $$source)) {
this["activeLine"] = "";
}
if (!("lineNumber" in $$source)) {
this["lineNumber"] = "";
}
if (!("activeLineNumber" in $$source)) {
this["activeLineNumber"] = "";
}
if (!("borderColor" in $$source)) {
this["borderColor"] = "";
}
if (!("borderLight" in $$source)) {
this["borderLight"] = "";
}
if (!("searchMatch" in $$source)) {
this["searchMatch"] = "";
}
if (!("matchingBracket" in $$source)) {
this["matchingBracket"] = "";
}
Object.assign(this, $$source);
}
/**
* Creates a new ThemeColorConfig instance from a string or object.
*/
static createFrom($$source: any = {}): ThemeColorConfig {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new ThemeColorConfig($$parsedSource as Partial<ThemeColorConfig>);
}
}
/** /**
* ThemeType 主题类型枚举 * ThemeType 主题类型枚举
@@ -1533,6 +1342,11 @@ var $$createType6 = (function $$initCreateType6(...args): any {
}); });
const $$createType7 = $Create.Map($Create.Any, $Create.Any); const $$createType7 = $Create.Map($Create.Any, $Create.Any);
const $$createType8 = HotkeyCombo.createFrom; const $$createType8 = HotkeyCombo.createFrom;
const $$createType9 = ThemeColorConfig.createFrom; var $$createType9 = (function $$initCreateType9(...args): any {
if ($$createType9 === $$initCreateType9) {
$$createType9 = $$createType7;
}
return $$createType9(...args);
});
const $$createType10 = GithubConfig.createFrom; const $$createType10 = GithubConfig.createFrom;
const $$createType11 = GiteaConfig.createFrom; const $$createType11 = GiteaConfig.createFrom;

View File

@@ -10,6 +10,9 @@
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import {Call as $Call, Create as $Create} from "@wailsio/runtime"; import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import * as models$0 from "../models/models.js"; import * as models$0 from "../models/models.js";
@@ -54,6 +57,11 @@ export function ServiceShutdown(): Promise<void> & { cancel(): void } {
return $resultPromise; return $resultPromise;
} }
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2900331732, options) as any;
return $resultPromise;
}
/** /**
* StartAutoBackup 启动自动备份定时器 * StartAutoBackup 启动自动备份定时器
*/ */

View File

@@ -10,10 +10,17 @@
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import {Call as $Call, Create as $Create} from "@wailsio/runtime"; import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import * as models$0 from "../models/models.js"; import * as models$0 from "../models/models.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as $models from "./models.js";
/** /**
* Get 获取配置项 * Get 获取配置项
*/ */
@@ -34,22 +41,6 @@ export function GetConfig(): Promise<models$0.AppConfig | null> & { cancel(): vo
return $typingPromise; return $typingPromise;
} }
/**
* GetConfigDir 获取配置目录
*/
export function GetConfigDir(): Promise<string> & { cancel(): void } {
let $resultPromise = $Call.ByID(2275626561) as any;
return $resultPromise;
}
/**
* GetSettingsPath 获取设置文件路径
*/
export function GetSettingsPath(): Promise<string> & { cancel(): void } {
let $resultPromise = $Call.ByID(2175583370) as any;
return $resultPromise;
}
/** /**
* MigrateConfig 执行配置迁移 * MigrateConfig 执行配置迁移
*/ */
@@ -74,6 +65,14 @@ export function ServiceShutdown(): Promise<void> & { cancel(): void } {
return $resultPromise; return $resultPromise;
} }
/**
* ServiceStartup initializes the service when the application starts
*/
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3311949428, options) as any;
return $resultPromise;
}
/** /**
* Set 设置配置项 * Set 设置配置项
*/ */
@@ -83,34 +82,18 @@ export function Set(key: string, value: any): Promise<void> & { cancel(): void }
} }
/** /**
* SetBackupConfigChangeCallback 设置备份配置变更回调 * Watch 注册配置变更监听器
*/ */
export function SetBackupConfigChangeCallback(callback: any): Promise<void> & { cancel(): void } { export function Watch(path: string, callback: $models.ObserverCallback): Promise<$models.CancelFunc> & { cancel(): void } {
let $resultPromise = $Call.ByID(3264871659, callback) as any; let $resultPromise = $Call.ByID(1143583035, path, callback) as any;
return $resultPromise; return $resultPromise;
} }
/** /**
* SetDataPathChangeCallback 设置数据路径配置变更回调 * WatchWithContext 使用 Context 注册监听器
*/ */
export function SetDataPathChangeCallback(callback: any): Promise<void> & { cancel(): void } { export function WatchWithContext(path: string, callback: $models.ObserverCallback): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(393017412, callback) as any; let $resultPromise = $Call.ByID(1454973098, path, callback) as any;
return $resultPromise;
}
/**
* SetHotkeyChangeCallback 设置热键配置变更回调
*/
export function SetHotkeyChangeCallback(callback: any): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(283872321, callback) as any;
return $resultPromise;
}
/**
* SetWindowSnapConfigChangeCallback 设置窗口吸附配置变更回调
*/
export function SetWindowSnapConfigChangeCallback(callback: any): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2324961653, callback) as any;
return $resultPromise; return $resultPromise;
} }

View File

@@ -14,14 +14,6 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js"; import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
/**
* OnDataPathChanged handles data path changes
*/
export function OnDataPathChanged(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3652863491) as any;
return $resultPromise;
}
/** /**
* RegisterModel 注册模型与表的映射关系 * RegisterModel 注册模型与表的映射关系
*/ */

View File

@@ -10,10 +10,6 @@
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import {Call as $Call, Create as $Create} from "@wailsio/runtime"; import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
/** /**
* SelectDirectory 打开目录选择对话框 * SelectDirectory 打开目录选择对话框
*/ */
@@ -29,11 +25,3 @@ export function SelectFile(): Promise<string> & { cancel(): void } {
let $resultPromise = $Call.ByID(37302920) as any; let $resultPromise = $Call.ByID(37302920) as any;
return $resultPromise; return $resultPromise;
} }
/**
* SetWindow 设置绑定的窗口
*/
export function SetWindow(window: application$0.WebviewWindow | null): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(968177170, window) as any;
return $resultPromise;
}

View File

@@ -49,14 +49,6 @@ export function GetDocumentByID(id: number): Promise<models$0.Document | null> &
return $typingPromise; return $typingPromise;
} }
/**
* GetFirstDocumentID gets the first active document's ID for frontend initialization
*/
export function GetFirstDocumentID(): Promise<number> & { cancel(): void } {
let $resultPromise = $Call.ByID(2970773833) as any;
return $resultPromise;
}
/** /**
* ListAllDocumentsMeta lists all active (non-deleted) document metadata * ListAllDocumentsMeta lists all active (non-deleted) document metadata
*/ */

View File

@@ -2,7 +2,7 @@
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
/** /**
* HotkeyService Windows全局热键服务 * HotkeyService 全局热键服务
* @module * @module
*/ */
@@ -32,8 +32,8 @@ export function GetCurrentHotkey(): Promise<models$0.HotkeyCombo | null> & { can
/** /**
* Initialize 初始化热键服务 * Initialize 初始化热键服务
*/ */
export function Initialize(app: application$0.App | null, mainWindow: application$0.WebviewWindow | null): Promise<void> & { cancel(): void } { export function Initialize(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3671360458, app, mainWindow) as any; let $resultPromise = $Call.ByID(3671360458) as any;
return $resultPromise; return $resultPromise;
} }
@@ -48,8 +48,8 @@ export function IsRegistered(): Promise<boolean> & { cancel(): void } {
/** /**
* RegisterHotkey 注册全局热键 * RegisterHotkey 注册全局热键
*/ */
export function RegisterHotkey(hotkey: models$0.HotkeyCombo | null): Promise<void> & { cancel(): void } { export function RegisterHotkey(combo: models$0.HotkeyCombo | null): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1103945691, hotkey) as any; let $resultPromise = $Call.ByID(1103945691, combo) as any;
return $resultPromise; return $resultPromise;
} }
@@ -61,6 +61,14 @@ export function ServiceShutdown(): Promise<void> & { cancel(): void } {
return $resultPromise; return $resultPromise;
} }
/**
* ServiceStartup 服务启动时初始化
*/
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3079990808, options) as any;
return $resultPromise;
}
/** /**
* UnregisterHotkey 取消注册全局热键 * UnregisterHotkey 取消注册全局热键
*/ */
@@ -72,8 +80,8 @@ export function UnregisterHotkey(): Promise<void> & { cancel(): void } {
/** /**
* UpdateHotkey 更新热键配置 * UpdateHotkey 更新热键配置
*/ */
export function UpdateHotkey(enable: boolean, hotkey: models$0.HotkeyCombo | null): Promise<void> & { cancel(): void } { export function UpdateHotkey(enable: boolean, combo: models$0.HotkeyCombo | null): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(823285555, enable, hotkey) as any; let $resultPromise = $Call.ByID(823285555, enable, combo) as any;
return $resultPromise; return $resultPromise;
} }

View File

@@ -0,0 +1,31 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
/**
* HttpClientService HTTP客户端服务
* @module
*/
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as $models from "./models.js";
/**
* ExecuteRequest 执行HTTP请求
*/
export function ExecuteRequest(request: $models.HttpRequest | null): Promise<$models.HttpResponse | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(3143343977, request) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType1($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
// Private type creation functions
const $$createType0 = $models.HttpResponse.createFrom;
const $$createType1 = $Create.Nullable($$createType0);

View File

@@ -8,6 +8,7 @@ import * as DialogService from "./dialogservice.js";
import * as DocumentService from "./documentservice.js"; import * as DocumentService from "./documentservice.js";
import * as ExtensionService from "./extensionservice.js"; import * as ExtensionService from "./extensionservice.js";
import * as HotkeyService from "./hotkeyservice.js"; import * as HotkeyService from "./hotkeyservice.js";
import * as HttpClientService from "./httpclientservice.js";
import * as KeyBindingService from "./keybindingservice.js"; import * as KeyBindingService from "./keybindingservice.js";
import * as MigrationService from "./migrationservice.js"; import * as MigrationService from "./migrationservice.js";
import * as SelfUpdateService from "./selfupdateservice.js"; import * as SelfUpdateService from "./selfupdateservice.js";
@@ -26,6 +27,7 @@ export {
DocumentService, DocumentService,
ExtensionService, ExtensionService,
HotkeyService, HotkeyService,
HttpClientService,
KeyBindingService, KeyBindingService,
MigrationService, MigrationService,
SelfUpdateService, SelfUpdateService,

View File

@@ -7,7 +7,118 @@ import {Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js"; import * as http$0 from "../../../net/http/models.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as time$0 from "../../../time/models.js";
/**
* CancelFunc 取消订阅函数
* 调用此函数可以取消对配置的监听
*/
export type CancelFunc = any;
/**
* HttpRequest HTTP请求结构
*/
export class HttpRequest {
"method": string;
"url": string;
"headers": { [_: string]: string };
/**
* json, formdata, urlencoded, text, params, xml, html, javascript, binary
*/
"bodyType"?: string;
"body"?: any;
/** Creates a new HttpRequest instance. */
constructor($$source: Partial<HttpRequest> = {}) {
if (!("method" in $$source)) {
this["method"] = "";
}
if (!("url" in $$source)) {
this["url"] = "";
}
if (!("headers" in $$source)) {
this["headers"] = {};
}
Object.assign(this, $$source);
}
/**
* Creates a new HttpRequest instance from a string or object.
*/
static createFrom($$source: any = {}): HttpRequest {
const $$createField2_0 = $$createType0;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("headers" in $$parsedSource) {
$$parsedSource["headers"] = $$createField2_0($$parsedSource["headers"]);
}
return new HttpRequest($$parsedSource as Partial<HttpRequest>);
}
}
/**
* HttpResponse HTTP响应结构
*/
export class HttpResponse {
/**
* 使用resp.Status()返回完整状态如"200 OK"
*/
"status": string;
/**
* 响应时间(毫秒)
*/
"time": number;
/**
* 请求大小
*/
"requestSize": string;
"body": any;
"headers": http$0.Header;
"timestamp": time$0.Time;
"error"?: any;
/** Creates a new HttpResponse instance. */
constructor($$source: Partial<HttpResponse> = {}) {
if (!("status" in $$source)) {
this["status"] = "";
}
if (!("time" in $$source)) {
this["time"] = 0;
}
if (!("requestSize" in $$source)) {
this["requestSize"] = "";
}
if (!("body" in $$source)) {
this["body"] = null;
}
if (!("headers" in $$source)) {
this["headers"] = ({} as http$0.Header);
}
if (!("timestamp" in $$source)) {
this["timestamp"] = null;
}
Object.assign(this, $$source);
}
/**
* Creates a new HttpResponse instance from a string or object.
*/
static createFrom($$source: any = {}): HttpResponse {
const $$createField4_0 = $$createType1;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("headers" in $$parsedSource) {
$$parsedSource["headers"] = $$createField4_0($$parsedSource["headers"]);
}
return new HttpResponse($$parsedSource as Partial<HttpResponse>);
}
}
/** /**
* MemoryStats 内存统计信息 * MemoryStats 内存统计信息
@@ -119,6 +230,47 @@ export enum MigrationStatus {
MigrationStatusFailed = "failed", MigrationStatusFailed = "failed",
}; };
/**
* OSInfo 操作系统信息
*/
export class OSInfo {
"id": string;
"name": string;
"version": string;
"branding": string;
/** Creates a new OSInfo instance. */
constructor($$source: Partial<OSInfo> = {}) {
if (!("id" in $$source)) {
this["id"] = "";
}
if (!("name" in $$source)) {
this["name"] = "";
}
if (!("version" in $$source)) {
this["version"] = "";
}
if (!("branding" in $$source)) {
this["branding"] = "";
}
Object.assign(this, $$source);
}
/**
* Creates a new OSInfo instance from a string or object.
*/
static createFrom($$source: any = {}): OSInfo {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new OSInfo($$parsedSource as Partial<OSInfo>);
}
}
/**
* ObserverCallback 观察者回调函数
*/
export type ObserverCallback = any;
/** /**
* SelfUpdateResult 自我更新结果 * SelfUpdateResult 自我更新结果
*/ */
@@ -203,61 +355,63 @@ export class SelfUpdateResult {
} }
/** /**
* WindowInfo 窗口信息(简化版) * SystemInfo 系统信息
*/ */
export class WindowInfo { export class SystemInfo {
"Window": application$0.WebviewWindow | null; "os": string;
"DocumentID": number; "arch": string;
"Title": string; "debug": boolean;
"osInfo": OSInfo | null;
"platformInfo": { [_: string]: any };
/** Creates a new WindowInfo instance. */ /** Creates a new SystemInfo instance. */
constructor($$source: Partial<WindowInfo> = {}) { constructor($$source: Partial<SystemInfo> = {}) {
if (!("Window" in $$source)) { if (!("os" in $$source)) {
this["Window"] = null; this["os"] = "";
} }
if (!("DocumentID" in $$source)) { if (!("arch" in $$source)) {
this["DocumentID"] = 0; this["arch"] = "";
} }
if (!("Title" in $$source)) { if (!("debug" in $$source)) {
this["Title"] = ""; this["debug"] = false;
}
if (!("osInfo" in $$source)) {
this["osInfo"] = null;
}
if (!("platformInfo" in $$source)) {
this["platformInfo"] = {};
} }
Object.assign(this, $$source); Object.assign(this, $$source);
} }
/** /**
* Creates a new WindowInfo instance from a string or object. * Creates a new SystemInfo instance from a string or object.
*/ */
static createFrom($$source: any = {}): WindowInfo { static createFrom($$source: any = {}): SystemInfo {
const $$createField0_0 = $$createType1; const $$createField3_0 = $$createType5;
const $$createField4_0 = $$createType6;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("Window" in $$parsedSource) { if ("osInfo" in $$parsedSource) {
$$parsedSource["Window"] = $$createField0_0($$parsedSource["Window"]); $$parsedSource["osInfo"] = $$createField3_0($$parsedSource["osInfo"]);
} }
return new WindowInfo($$parsedSource as Partial<WindowInfo>); if ("platformInfo" in $$parsedSource) {
} $$parsedSource["platformInfo"] = $$createField4_0($$parsedSource["platformInfo"]);
} }
return new SystemInfo($$parsedSource as Partial<SystemInfo>);
/**
* WindowSnapService 窗口吸附服务
*/
export class WindowSnapService {
/** Creates a new WindowSnapService instance. */
constructor($$source: Partial<WindowSnapService> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new WindowSnapService instance from a string or object.
*/
static createFrom($$source: any = {}): WindowSnapService {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new WindowSnapService($$parsedSource as Partial<WindowSnapService>);
} }
} }
// Private type creation functions // Private type creation functions
const $$createType0 = application$0.WebviewWindow.createFrom; const $$createType0 = $Create.Map($Create.Any, $Create.Any);
const $$createType1 = $Create.Nullable($$createType0); var $$createType1 = (function $$initCreateType1(...args): any {
if ($$createType1 === $$initCreateType1) {
$$createType1 = $$createType3;
}
return $$createType1(...args);
});
const $$createType2 = $Create.Array($Create.Any);
const $$createType3 = $Create.Map($Create.Any, $$createType2);
const $$createType4 = OSInfo.createFrom;
const $$createType5 = $Create.Nullable($$createType4);
const $$createType6 = $Create.Map($Create.Any, $Create.Any);

View File

@@ -34,6 +34,18 @@ export function GetMemoryStats(): Promise<$models.MemoryStats> & { cancel(): voi
return $typingPromise; return $typingPromise;
} }
/**
* GetSystemInfo 获取系统环境信息
*/
export function GetSystemInfo(): Promise<$models.SystemInfo | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(2629436820) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
/** /**
* TriggerGC 手动触发垃圾回收 * TriggerGC 手动触发垃圾回收
*/ */
@@ -44,3 +56,5 @@ export function TriggerGC(): Promise<void> & { cancel(): void } {
// Private type creation functions // Private type creation functions
const $$createType0 = $models.MemoryStats.createFrom; const $$createType0 = $models.MemoryStats.createFrom;
const $$createType1 = $models.SystemInfo.createFrom;
const $$createType2 = $Create.Nullable($$createType1);

View File

@@ -18,10 +18,10 @@ import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/applic
import * as models$0 from "../models/models.js"; import * as models$0 from "../models/models.js";
/** /**
* CreateTheme 创建新主题 * GetThemeByName 通过名称获取主题覆盖,若不存在则返回 nil
*/ */
export function CreateTheme(theme: models$0.Theme | null): Promise<models$0.Theme | null> & { cancel(): void } { export function GetThemeByName(name: string): Promise<models$0.Theme | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(3274757686, theme) as any; let $resultPromise = $Call.ByID(1938954770, name) as any;
let $typingPromise = $resultPromise.then(($result: any) => { let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType1($result); return $$createType1($result);
}) as any; }) as any;
@@ -30,46 +30,10 @@ export function CreateTheme(theme: models$0.Theme | null): Promise<models$0.Them
} }
/** /**
* GetAllThemes 获取所有主题 * ResetTheme 删除指定主题的覆盖配置
*/ */
export function GetAllThemes(): Promise<(models$0.Theme | null)[]> & { cancel(): void } { export function ResetTheme(name: string): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2425053076) as any; let $resultPromise = $Call.ByID(1806334457, name) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
/**
* GetDefaultThemes 获取默认主题
*/
export function GetDefaultThemes(): Promise<{ [_: string]: models$0.Theme | null }> & { cancel(): void } {
let $resultPromise = $Call.ByID(3801788118) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType3($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
/**
* GetThemeByType 根据类型获取默认主题
*/
export function GetThemeByType(themeType: models$0.ThemeType): Promise<models$0.Theme | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(1680465265, themeType) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType1($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
/**
* ResetThemeColors 重置主题颜色为默认值
*/
export function ResetThemeColors(themeType: models$0.ThemeType): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(342461245, themeType) as any;
return $resultPromise; return $resultPromise;
} }
@@ -82,7 +46,7 @@ export function ServiceShutdown(): Promise<void> & { cancel(): void } {
} }
/** /**
* ServiceStartup 服务启动时初始化 * ServiceStartup 服务启动
*/ */
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } { export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2915959937, options) as any; let $resultPromise = $Call.ByID(2915959937, options) as any;
@@ -90,15 +54,13 @@ export function ServiceStartup(options: application$0.ServiceOptions): Promise<v
} }
/** /**
* UpdateThemeColors 更新主题颜色 * UpdateTheme 保存或更新主题覆盖
*/ */
export function UpdateThemeColors(themeType: models$0.ThemeType, colors: models$0.ThemeColorConfig): Promise<void> & { cancel(): void } { export function UpdateTheme(name: string, colors: models$0.ThemeColorConfig): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2750902529, themeType, colors) as any; let $resultPromise = $Call.ByID(70189749, name, colors) as any;
return $resultPromise; return $resultPromise;
} }
// Private type creation functions // Private type creation functions
const $$createType0 = models$0.Theme.createFrom; const $$createType0 = models$0.Theme.createFrom;
const $$createType1 = $Create.Nullable($$createType0); const $$createType1 = $Create.Nullable($$createType0);
const $$createType2 = $Create.Array($$createType1);
const $$createType3 = $Create.Map($Create.Any, $$createType1);

View File

@@ -14,27 +14,6 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import * as translator$0 from "../common/translator/models.js"; import * as translator$0 from "../common/translator/models.js";
/**
* GetAvailableTranslators 获取所有可用翻译器类型
* @returns {[]string} 翻译器类型列表
*/
export function GetAvailableTranslators(): Promise<string[]> & { cancel(): void } {
let $resultPromise = $Call.ByID(1186597995) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType0($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
/**
* GetStandardLanguageCode 获取标准化的语言代码
*/
export function GetStandardLanguageCode(translatorType: translator$0.TranslatorType, languageCode: string): Promise<string> & { cancel(): void } {
let $resultPromise = $Call.ByID(1158131995, translatorType, languageCode) as any;
return $resultPromise;
}
/** /**
* GetTranslatorLanguages 获取翻译器的语言列表 * GetTranslatorLanguages 获取翻译器的语言列表
* @param {string} translatorType - 翻译器类型 ("google", "bing", "youdao", "deepl") * @param {string} translatorType - 翻译器类型 ("google", "bing", "youdao", "deepl")
@@ -43,6 +22,19 @@ export function GetStandardLanguageCode(translatorType: translator$0.TranslatorT
*/ */
export function GetTranslatorLanguages(translatorType: translator$0.TranslatorType): Promise<{ [_: string]: translator$0.LanguageInfo }> & { cancel(): void } { export function GetTranslatorLanguages(translatorType: translator$0.TranslatorType): Promise<{ [_: string]: translator$0.LanguageInfo }> & { cancel(): void } {
let $resultPromise = $Call.ByID(3976114458, translatorType) as any; let $resultPromise = $Call.ByID(3976114458, translatorType) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType1($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
/**
* GetTranslators 获取所有可用翻译器类型
* @returns {[]string} 翻译器类型列表
*/
export function GetTranslators(): Promise<string[]> & { cancel(): void } {
let $resultPromise = $Call.ByID(3720069432) as any;
let $typingPromise = $resultPromise.then(($result: any) => { let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result); return $$createType2($result);
}) as any; }) as any;
@@ -73,6 +65,6 @@ export function TranslateWith(text: string, $from: string, to: string, translato
} }
// Private type creation functions // Private type creation functions
const $$createType0 = $Create.Array($Create.Any); const $$createType0 = translator$0.LanguageInfo.createFrom;
const $$createType1 = translator$0.LanguageInfo.createFrom; const $$createType1 = $Create.Map($Create.Any, $$createType0);
const $$createType2 = $Create.Map($Create.Any, $$createType1); const $$createType2 = $Create.Array($Create.Any);

View File

@@ -10,9 +10,13 @@
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import {Call as $Call, Create as $Create} from "@wailsio/runtime"; import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment /**
// @ts-ignore: Unused imports * AutoShowHide 自动显示/隐藏主窗口
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js"; */
export function AutoShowHide(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(4044219428) as any;
return $resultPromise;
}
/** /**
* HandleWindowClose 处理窗口关闭事件 * HandleWindowClose 处理窗口关闭事件
@@ -38,14 +42,6 @@ export function MinimizeButtonClicked(): Promise<void> & { cancel(): void } {
return $resultPromise; return $resultPromise;
} }
/**
* SetAppReferences 设置应用引用
*/
export function SetAppReferences(app: application$0.App | null, mainWindow: application$0.WebviewWindow | null): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3544515719, app, mainWindow) as any;
return $resultPromise;
}
/** /**
* ShouldMinimizeToTray 检查是否应该最小化到托盘 * ShouldMinimizeToTray 检查是否应该最小化到托盘
*/ */

View File

@@ -2,7 +2,7 @@
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
/** /**
* WindowService 窗口管理服务(专注于窗口生命周期管理) * WindowService 窗口管理服务
* @module * @module
*/ */
@@ -14,17 +14,13 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js"; import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as $models from "./models.js";
/** /**
* GetOpenWindows 获取所有打开的窗口信息 * GetOpenWindows 获取所有打开的文档窗口
*/ */
export function GetOpenWindows(): Promise<$models.WindowInfo[]> & { cancel(): void } { export function GetOpenWindows(): Promise<application$0.Window[]> & { cancel(): void } {
let $resultPromise = $Call.ByID(1464997251) as any; let $resultPromise = $Call.ByID(1464997251) as any;
let $typingPromise = $resultPromise.then(($result: any) => { let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType1($result); return $$createType0($result);
}) as any; }) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise); $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise; return $typingPromise;
@@ -55,21 +51,12 @@ export function ServiceShutdown(): Promise<void> & { cancel(): void } {
} }
/** /**
* SetAppReferences 设置应用和主窗口引用 * ServiceStartup 服务启动时初始化
*/ */
export function SetAppReferences(app: application$0.App | null, mainWindow: application$0.WebviewWindow | null): Promise<void> & { cancel(): void } { export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1120840759, app, mainWindow) as any; let $resultPromise = $Call.ByID(2432987694, options) as any;
return $resultPromise;
}
/**
* SetWindowSnapService 设置窗口吸附服务引用
*/
export function SetWindowSnapService(snapService: $models.WindowSnapService | null): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1105193745, snapService) as any;
return $resultPromise; return $resultPromise;
} }
// Private type creation functions // Private type creation functions
const $$createType0 = $models.WindowInfo.createFrom; const $$createType0 = $Create.Array($Create.Any);
const $$createType1 = $Create.Array($$createType0);

View File

@@ -1,8 +1,11 @@
/* eslint-disable */ /* eslint-disable */
// @ts-nocheck // @ts-nocheck
// biome-ignore lint: disable
// oxlint-disable
// ------
// Generated by unplugin-vue-components // Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399 // Read more: https://github.com/vuejs/core/pull/3399
// biome-ignore lint: disable
export {} export {}
/* prettier-ignore */ /* prettier-ignore */
@@ -16,6 +19,9 @@ declare module 'vue' {
MemoryMonitor: typeof import('./src/components/monitor/MemoryMonitor.vue')['default'] MemoryMonitor: typeof import('./src/components/monitor/MemoryMonitor.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
TabContainer: typeof import('./src/components/tabs/TabContainer.vue')['default']
TabContextMenu: typeof import('./src/components/tabs/TabContextMenu.vue')['default']
TabItem: typeof import('./src/components/tabs/TabItem.vue')['default']
Toolbar: typeof import('./src/components/toolbar/Toolbar.vue')['default'] Toolbar: typeof import('./src/components/toolbar/Toolbar.vue')['default']
WindowsTitleBar: typeof import('./src/components/titlebar/WindowsTitleBar.vue')['default'] WindowsTitleBar: typeof import('./src/components/titlebar/WindowsTitleBar.vue')['default']
WindowTitleBar: typeof import('./src/components/titlebar/WindowTitleBar.vue')['default'] WindowTitleBar: typeof import('./src/components/titlebar/WindowTitleBar.vue')['default']

View File

@@ -0,0 +1,130 @@
import {defineConfig} from 'vitepress'
const base = '/'
// https://vitepress.dev/reference/site-config
export default defineConfig({
base: base,
title: "voidraft",
description: "An elegant text snippet recording tool designed for developers.",
srcDir: 'src',
assetsDir: 'assets',
cacheDir: './.vitepress/cache',
outDir: './.vitepress/dist',
srcExclude: [],
ignoreDeadLinks: false,
head: [
["link", {rel: "icon", type: "image/png", href: "/icon/favicon-96x96.png", sizes: "96x96"}],
["link", {rel: "icon", type: "image/svg+xml", href: "/icon/favicon.svg"}],
["link", {rel: "shortcut icon", href: "/icon/favicon.ico"}],
["link", {rel: "apple-touch-icon", sizes: "180x180", href: "/icon/apple-touch-icon.png"}],
["meta", {name: "apple-mobile-web-app-title", content: "voidraft"}],
["link", {rel: "manifest", href: "/icon/site.webmanifest"}],
['meta', {name: 'viewport', content: 'width=device-width,initial-scale=1'}]
],
// 国际化配置
locales: {
root: {
label: 'English',
lang: 'en-US',
description: 'An elegant text snippet recording tool designed for developers.',
themeConfig: {
logo: '/icon/logo.png',
siteTitle: 'voidraft',
nav: [
{text: 'Home', link: '/'},
{text: 'Guide', link: '/guide/introduction'}
],
sidebar: {
'/guide/': [
{
text: 'Getting Started',
items: [
{text: 'Introduction', link: '/guide/introduction'},
{text: 'Installation', link: '/guide/installation'},
{text: 'Quick Start', link: '/guide/getting-started'}
]
},
{
text: 'Features',
items: [
{text: 'Overview', link: '/guide/features'}
]
}
]
},
socialLinks: [
{icon: 'github', link: 'https://github.com/landaiqing/voidraft'}
],
outline: {
label: 'On this page'
},
lastUpdated: {
text: 'Last updated'
},
docFooter: {
prev: 'Previous',
next: 'Next'
},
darkModeSwitchLabel: 'Appearance',
sidebarMenuLabel: 'Menu',
returnToTopLabel: 'Return to top',
footer: {
message: 'Released under the MIT License.',
copyright: 'Copyright © 2025-present landaiqing'
}
}
},
zh: {
label: '简体中文',
lang: 'zh-CN',
link: '/zh/',
description: '一个为开发者设计的优雅文本片段记录工具',
themeConfig: {
logo: '/icon/logo.png',
siteTitle: 'voidraft',
nav: [
{text: '首页', link: '/zh/'},
{text: '指南', link: '/zh/guide/introduction'}
],
sidebar: {
'/zh/guide/': [
{
text: '开始使用',
items: [
{text: '简介', link: '/zh/guide/introduction'},
{text: '安装', link: '/zh/guide/installation'},
{text: '快速开始', link: '/zh/guide/getting-started'}
]
},
{
text: '功能特性',
items: [
{text: '功能概览', link: '/zh/guide/features'}
]
}
]
},
socialLinks: [
{icon: 'github', link: 'https://github.com/landaiqing/voidraft'}
],
outline: {
label: '本页目录'
},
lastUpdated: {
text: '最后更新'
},
docFooter: {
prev: '上一页',
next: '下一页'
},
darkModeSwitchLabel: '外观',
sidebarMenuLabel: '菜单',
returnToTopLabel: '返回顶部',
footer: {
message: 'Released under the MIT License.',
copyright: 'Copyright © 2025-present landaiqing'
}
}
}
}
})

View File

@@ -0,0 +1,6 @@
@import "style/var.css";
@import "style/blur.css";
@import "style/badge.css";
@import "style/grid.css";

View File

@@ -0,0 +1,17 @@
// https://vitepress.dev/guide/custom-theme
import { h } from 'vue'
import type { Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import './index.css'
export default {
extends: DefaultTheme,
Layout: () => {
return h(DefaultTheme.Layout, null, {
// https://vitepress.dev/guide/extending-default-theme#layout-slots
})
},
enhanceApp({ app, router, siteData }) {
// ...
}
} satisfies Theme

View File

@@ -0,0 +1,21 @@
/* 提示框背景颜色 */
:root {
--vp-custom-block-tip-bg: var(--vp-c-green-soft);
}
/* 提示框 */
.custom-block.tip {
border-color: var(--vp-c-green-2);
}
/* 警告框 */
.custom-block.warning {
/* border-color: #d97706; */
border-color: var(--vp-c-yellow-2);
}
/* 危险框 */
.custom-block.danger {
/* border-color: #f43f5e; */
border-color: var(--vp-c-red-2);
}

View File

@@ -0,0 +1,73 @@
/* .vitepress/theme/style/blur.css */
:root {
/* 首页导航 */
.VPNavBar {
background-color: rgba(255, 255, 255, 0);
backdrop-filter: blur(10px);
}
/* 文档页导航两侧 */
.VPNavBar:not(.home) {
background-color: rgba(255, 255, 255, 0);
backdrop-filter: blur(10px);
}
@media (min-width: 960px) {
/* 文档页导航两侧 */
.VPNavBar:not(.home) {
background-color: rgba(255, 255, 255, 0);
backdrop-filter: blur(10px);
}
/* 首页下滑后导航两侧 */
.VPNavBar:not(.has-sidebar):not(.home.top) {
background-color: rgba(255, 255, 255, 0);
backdrop-filter: blur(10px);
}
}
@media (min-width: 960px) {
/* 文档页导航中间 */
.VPNavBar:not(.home.top) .content-body {
background-color: rgba(255, 255, 255, 0);
backdrop-filter: blur(10px);
}
/* 首页下滑后导航中间 */
.VPNavBar:not(.has-sidebar):not(.home.top) .content-body {
background-color: rgba(255, 255, 255, 0);
backdrop-filter: blur(10px);
}
}
/* 分割线 */
@media (min-width: 960px) {
/* 文档页分割线 */
.VPNavBar:not(.home.top) .divider-line {
background-color: rgba(255, 255, 255, 0);
backdrop-filter: blur(10px);
}
/* 首页分割线 */
.VPNavBar:not(.has-sidebar):not(.home.top) .divider {
background-color: rgba(255, 255, 255, 0);
backdrop-filter: blur(10px);
}
}
/* 搜索框 VPNavBarSearchButton.vue */
.DocSearch-Button {
background-color: rgba(255, 255, 255, 0);
backdrop-filter: blur(10px);
}
/* 移动端大纲栏 */
.VPLocalNav {
background-color: rgba(255, 255, 255, 0);
backdrop-filter: blur(10px);
/* 隐藏分割线 */
/* border-bottom: 5px solid var(--vp-c-gutter); */
border-bottom: 0px;
}
}

View File

@@ -0,0 +1,40 @@
/**
* Grid Background
* 网格背景样式 - 为文档页面添加简约的网格背景
* -------------------------------------------------------------------------- */
.VPDoc,
.VPHome {
position: relative;
}
.VPDoc::before,
.VPHome::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
pointer-events: none;
}
/* 亮色模式网格 */
:root:not(.dark) .VPDoc::before,
:root:not(.dark) .VPHome::before {
background-image:
linear-gradient(rgba(0, 0, 0, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(0, 0, 0, 0.05) 1px, transparent 1px);
background-size: 60px 60px;
}
/* 暗色模式网格 */
.dark .VPDoc::before,
.dark .VPHome::before {
background-image:
linear-gradient(rgba(255, 255, 255, 0.06) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.06) 1px, transparent 1px);
background-size: 60px 60px;
}

View File

@@ -0,0 +1,137 @@
/**
* Customize default theme styling by overriding CSS variables:
* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css
*/
/**
* Colors
*
* Each colors have exact same color scale system with 3 levels of solid
* colors with different brightness, and 1 soft color.
*
* - `XXX-1`: The most solid color used mainly for colored text. It must
* satisfy the contrast ratio against when used on top of `XXX-soft`.
*
* - `XXX-2`: The color used mainly for hover state of the button.
*
* - `XXX-3`: The color for solid background, such as bg color of the button.
* It must satisfy the contrast ratio with pure white (#ffffff) text on
* top of it.
*
* - `XXX-soft`: The color used for subtle background such as custom container
* or badges. It must satisfy the contrast ratio when putting `XXX-1` colors
* on top of it.
*
* The soft color must be semi transparent alpha channel. This is crucial
* because it allows adding multiple "soft" colors on top of each other
* to create an accent, such as when having inline code block inside
* custom containers.
*
* - `default`: The color used purely for subtle indication without any
* special meanings attached to it such as bg color for menu hover state.
*
* - `brand`: Used for primary brand colors, such as link text, button with
* brand theme, etc.
*
* - `tip`: Used to indicate useful information. The default theme uses the
* brand color for this by default.
*
* - `warning`: Used to indicate warning to the users. Used in custom
* container, badges, etc.
*
* - `danger`: Used to show error, or dangerous message to the users. Used
* in custom container, badges, etc.
* -------------------------------------------------------------------------- */
:root {
--vp-c-default-1: var(--vp-c-gray-1);
--vp-c-default-2: var(--vp-c-gray-2);
--vp-c-default-3: var(--vp-c-gray-3);
--vp-c-default-soft: var(--vp-c-gray-soft);
--vp-c-brand-1: var(--vp-c-indigo-1);
--vp-c-brand-2: var(--vp-c-indigo-2);
--vp-c-brand-3: var(--vp-c-indigo-3);
--vp-c-brand-soft: var(--vp-c-indigo-soft);
--vp-c-tip-1: var(--vp-c-brand-1);
--vp-c-tip-2: var(--vp-c-brand-2);
--vp-c-tip-3: var(--vp-c-brand-3);
--vp-c-tip-soft: var(--vp-c-brand-soft);
--vp-c-warning-1: var(--vp-c-yellow-1);
--vp-c-warning-2: var(--vp-c-yellow-2);
--vp-c-warning-3: var(--vp-c-yellow-3);
--vp-c-warning-soft: var(--vp-c-yellow-soft);
--vp-c-danger-1: var(--vp-c-red-1);
--vp-c-danger-2: var(--vp-c-red-2);
--vp-c-danger-3: var(--vp-c-red-3);
--vp-c-danger-soft: var(--vp-c-red-soft);
}
/**
* Component: Button
* -------------------------------------------------------------------------- */
:root {
--vp-button-brand-border: transparent;
--vp-button-brand-text: var(--vp-c-white);
--vp-button-brand-bg: var(--vp-c-brand-3);
--vp-button-brand-hover-border: transparent;
--vp-button-brand-hover-text: var(--vp-c-white);
--vp-button-brand-hover-bg: var(--vp-c-brand-2);
--vp-button-brand-active-border: transparent;
--vp-button-brand-active-text: var(--vp-c-white);
--vp-button-brand-active-bg: var(--vp-c-brand-1);
}
/**
* Component: Home
* -------------------------------------------------------------------------- */
:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(
120deg,
#bd34fe 30%,
#41d1ff
);
--vp-home-hero-image-background-image: linear-gradient(
-45deg,
#bd34fe 50%,
#47caff 50%
);
--vp-home-hero-image-filter: blur(44px);
}
@media (min-width: 640px) {
:root {
--vp-home-hero-image-filter: blur(56px);
}
}
@media (min-width: 960px) {
:root {
--vp-home-hero-image-filter: blur(68px);
}
}
/**
* Component: Custom Block
* -------------------------------------------------------------------------- */
:root {
--vp-custom-block-tip-border: transparent;
--vp-custom-block-tip-text: var(--vp-c-text-1);
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
}
/**
* Component: Algolia
* -------------------------------------------------------------------------- */
.DocSearch {
--docsearch-primary-color: var(--vp-c-brand-1) !important;
}

View File

@@ -0,0 +1,163 @@
# Features
Explore the powerful features that make voidraft a great tool for developers.
## Block-Based Editing
voidraft's core feature is its block-based editing system:
- Each block can have a different programming language
- Blocks are separated by delimiters (`∞∞∞language`)
- Navigate quickly between blocks
- Format each block independently
## Syntax Highlighting
Professional syntax highlighting for 30+ languages:
- Automatic language detection
- Customizable color schemes
- Support for nested languages
- Code folding support
## HTTP Client
Built-in HTTP client for API testing:
### Request Types
- GET, POST, PUT, DELETE, PATCH
- Custom headers
- Multiple body formats: JSON, FormData, URL-encoded, XML, Text
### Request Variables
Define and reuse variables:
```http
@var {
baseUrl: "https://api.example.com",
token: "your-api-token"
}
GET "{{baseUrl}}/users" {
authorization: "Bearer {{token}}"
}
```
### Response Handling
- View formatted JSON responses
- See response time and size
- Inspect headers
- Save responses for later
## Code Formatting
Integrated Prettier support:
- Format on save (optional)
- Format selection or entire block
- Supports JavaScript, TypeScript, CSS, HTML, JSON, and more
- Customizable formatting rules
## Editor Extensions
### VSCode-Style Search
- Find and replace with regex support
- Case-sensitive and whole word options
- Search across all blocks
### Minimap
- Bird's-eye view of your document
- Quick navigation
- Customizable size and position
### Rainbow Brackets
- Color-coded bracket pairs
- Easier to match brackets
- Customizable colors
### Color Picker
- Visual color selection
- Supports hex, RGB, HSL
- Live preview
### Translation Tool
- Translate selected text
- Multiple language support
- Quick keyboard access
### Text Highlighting
- Highlight important text
- Multiple highlight colors
- Persistent highlights
## Multi-Window Support
Work efficiently with multiple windows:
- Each window is independent
- Separate documents
- Synchronized settings
- Window state persistence
## Theme Customization
Full control over editor appearance:
### Built-in Themes
- Dark mode
- Light mode
- Auto-switch based on system
### Custom Themes
- Create your own themes
- Customize every color
- Save and share themes
- Import community themes
## Auto-Update System
Stay current with automatic updates:
- Background update checks
- Notification of new versions
- One-click update
- Update history
- Support for multiple update sources (GitHub, Gitea)
## Data Backup
Secure your data with Git-based backup:
- Automatic backups
- Manual backup triggers
- Support for GitHub and Gitea
- Multiple authentication methods (SSH, Token, Password)
- Configurable backup intervals
## Keyboard Shortcuts
Extensive keyboard support:
- Customizable shortcuts
- Vim/Emacs keybindings (planned)
- Quick command palette
- Context-aware shortcuts
## Performance
Built for speed:
- Fast startup time
- Smooth scrolling
- Efficient memory usage
- Large file support
## Privacy & Security
Your data is safe:
- Local-first storage
- Optional cloud backup
- No telemetry or tracking
- Open source codebase

View File

@@ -0,0 +1,107 @@
# Getting Started
Learn the basics of using voidraft and create your first document.
## The Editor Interface
When you open voidraft, you'll see:
- **Main Editor**: The central area where you write and edit
- **Toolbar**: Quick access to common actions
- **Status Bar**: Shows current block language and other info
## Creating Code Blocks
voidraft uses a block-based editing system. Each block can have a different language:
1. Press `Ctrl+Enter` to create a new block
2. Type `∞∞∞` followed by a language name (e.g., `∞∞∞javascript`)
3. Start coding in that block
### Supported Languages
voidraft supports 30+ programming languages including:
- JavaScript, TypeScript
- Python, Go, Rust
- HTML, CSS, Sass
- SQL, YAML, JSON
- And many more...
## Basic Operations
### Navigation
- `Ctrl+Up/Down`: Move between blocks
- `Ctrl+Home/End`: Jump to first/last block
- `Ctrl+F`: Search within document
### Editing
- `Ctrl+D`: Duplicate current line
- `Ctrl+/`: Toggle comment
- `Alt+Up/Down`: Move line up/down
- `Ctrl+Shift+F`: Format code (if language supports Prettier)
### Block Management
- `Ctrl+Enter`: Create new block
- `Ctrl+Shift+Enter`: Create block above
- `Alt+Delete`: Delete current block
## Using the HTTP Client
voidraft includes a built-in HTTP client for testing APIs:
1. Create a block with HTTP language
2. Write your HTTP request:
```http
POST "https://api.example.com/users" {
content-type: "application/json"
@json {
name: "John Doe",
email: "john@example.com"
}
}
```
3. Click the run button to execute the request
4. View the response inline
## Multi-Window Support
Work on multiple documents simultaneously:
1. Go to `File > New Window` (or `Ctrl+Shift+N`)
2. Each window is independent
3. Changes are saved automatically
## Customizing Themes
Personalize your editor:
1. Open Settings (`Ctrl+,`)
2. Go to Appearance
3. Choose a theme or create your own
4. Customize colors to your preference
## Keyboard Shortcuts
Learn essential shortcuts:
| Action | Shortcut |
|--------|----------|
| New Window | `Ctrl+Shift+N` |
| Search | `Ctrl+F` |
| Replace | `Ctrl+H` |
| Format Code | `Ctrl+Shift+F` |
| Toggle Theme | `Ctrl+Shift+T` |
| Command Palette | `Ctrl+Shift+P` |
## Next Steps
Now that you know the basics:
- Explore [Features](/guide/features) in detail

View File

@@ -0,0 +1,63 @@
# Installation
This guide will help you install voidraft on your system.
## System Requirements
- **Operating System**: Windows 10 or later (macOS and Linux support planned)
- **RAM**: 4GB minimum, 8GB recommended
- **Disk Space**: 200MB free space
## Download
Visit the [releases page](https://github.com/landaiqing/voidraft/releases) and download the latest version for your platform:
- **Windows**: `voidraft-windows-amd64-installer.exe`
## Installation Steps
### Windows
1. Download the installer from the releases page
2. Run the `voidraft-windows-amd64-installer.exe` file
3. Follow the installation wizard
4. Launch voidraft from the Start menu or desktop shortcut
## First Launch
When you first launch voidraft:
1. The application will create a data directory to store your documents
2. You'll see the main editor interface with a welcome block
3. Start typing or create your first code block!
## Configuration
voidraft stores its configuration and data in:
- **Windows**: `%APPDATA%/voidraft/`
You can customize various settings including:
- Editor theme (dark/light mode)
- Code formatting preferences
- Backup settings
- Keyboard shortcuts
## Updating
voidraft includes an auto-update feature that will notify you when new versions are available. You can:
- Check for updates manually from the settings
- Enable automatic updates
- Choose your preferred update source
## Troubleshooting
If you encounter any issues during installation:
1. Make sure you have administrator privileges
2. Check that your antivirus isn't blocking the installation
3. Visit our [GitHub issues](https://github.com/landaiqing/voidraft/issues) page for help
Next: [Getting Started →](/guide/getting-started)

View File

@@ -0,0 +1,50 @@
# Introduction
Welcome to voidraft - an elegant text snippet recording tool designed specifically for developers.
## What is voidraft?
voidraft is a modern desktop application that helps developers manage text snippets, code blocks, API responses, meeting notes, and daily to-do lists. It provides a smooth and elegant editing experience with powerful features tailored for development workflows.
## Key Features
### Block-Based Editing
voidraft uses a unique block-based editing system inspired by Heynote. You can split your content into independent code blocks, each with:
- Different programming language settings
- Syntax highlighting
- Independent formatting
- Easy navigation between blocks
### Developer Tools
- **HTTP Client**: Test APIs directly within the editor
- **Code Formatting**: Built-in Prettier support for multiple languages
- **Syntax Highlighting**: Support for 30+ programming languages
- **Auto Language Detection**: Automatically recognizes code block language types
### Customization
- **Custom Themes**: Create and save your own editor themes
- **Extensions**: Rich set of editor extensions including minimap, rainbow brackets, color picker, and more
- **Multi-Window**: Work on multiple documents simultaneously
### Data Management
- **Git-Based Backup**: Automatic backup using Git repositories
- **Cloud Sync**: Sync your data across devices
- **Auto-Update**: Stay up-to-date with the latest features
## Why voidraft?
- **Developer-Focused**: Built with developers' needs in mind
- **Modern Stack**: Uses cutting-edge technologies (Wails3, Vue 3, CodeMirror 6)
- **Cross-Platform**: Works on Windows (macOS and Linux support planned)
- **Open Source**: MIT licensed, community-driven development
## Getting Started
Ready to start? Download the latest version from our [releases page](https://github.com/landaiqing/voidraft/releases) or continue reading the documentation to learn more.
Next: [Installation →](/guide/installation)

View File

@@ -0,0 +1,56 @@
---
layout: home
hero:
name: "voidraft"
text: "An elegant text snippet recording tool"
tagline: Designed for developers, built with modern technology
image:
src: /img/hero.png
alt: "voidraft"
actions:
- theme: brand
text: Get Started
link: https://github.com/landaiqing/voidraft/releases
- theme: alt
text: Documentation
link: /guide/introduction
features:
- icon: 📝
title: Block-Based Editing
details: Split your content into independent code blocks, each with different language settings. Inspired by Heynote's innovative design philosophy.
- icon: 🎨
title: Syntax Highlighting
details: Built-in support for 30+ programming languages with automatic language detection and Prettier integration for code formatting.
- icon: 🌐
title: HTTP Client
details: Integrated HTTP client with support for multiple request formats including JSON, FormData, XML, and more. Test APIs directly within the editor.
- icon: 🎯
title: Multi-Window Support
details: Work on multiple documents simultaneously with independent windows. Each window maintains its own state and configuration.
- icon: 🎭
title: Customizable Themes
details: Full theme customization support with dark/light modes. Create and save your own editor themes to match your preferences.
- icon: 🔧
title: Rich Extensions
details: VSCode-style search and replace, rainbow brackets, minimap, color picker, translation tool, text highlighting, and more.
- icon: 🔄
title: Auto-Update System
details: Built-in self-update mechanism with support for multiple update sources. Stay up-to-date with the latest features and improvements.
- icon: ☁️
title: Git-Based Backup
details: Automatic data backup using Git repositories. Supports GitHub, Gitea, with multiple authentication methods including SSH and tokens.
- icon: ⚡
title: Modern Architecture
details: Built with Wails3, Vue 3, and CodeMirror 6. Cross-platform desktop application with native performance and modern UI.
---

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -0,0 +1,21 @@
{
"name": "voidraft",
"short_name": "voidraft",
"icons": [
{
"src": "/img/web-app-manifest-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/img/web-app-manifest-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -0,0 +1,163 @@
# 功能特性
探索 voidraft 的强大功能,让它成为开发者的优秀工具。
## 块状编辑
voidraft 的核心功能是其块状编辑系统:
- 每个块可以有不同的编程语言
- 块之间由分隔符分隔(`∞∞∞语言`
- 快速在块之间导航
- 独立格式化每个块
## 语法高亮
支持 30+ 种语言的专业语法高亮:
- 自动语言检测
- 可自定义配色方案
- 支持嵌套语言
- 代码折叠支持
## HTTP 客户端
用于 API 测试的内置 HTTP 客户端:
### 请求类型
- GET、POST、PUT、DELETE、PATCH
- 自定义请求头
- 多种请求体格式JSON、FormData、URL 编码、XML、文本
### 请求变量
定义和重用变量:
```http
@var {
baseUrl: "https://api.example.com",
token: "your-api-token"
}
GET "{{baseUrl}}/users" {
authorization: "Bearer {{token}}"
}
```
### 响应处理
- 查看格式化的 JSON 响应
- 查看响应时间和大小
- 检查响应头
- 保存响应以供日后使用
## 代码格式化
集成 Prettier 支持:
- 保存时格式化(可选)
- 格式化选区或整个块
- 支持 JavaScript、TypeScript、CSS、HTML、JSON 等
- 可自定义格式化规则
## 编辑器扩展
### VSCode 风格搜索
- 查找和替换,支持正则表达式
- 区分大小写和全字匹配选项
- 跨所有块搜索
### 小地图
- 文档的鸟瞰图
- 快速导航
- 可自定义大小和位置
### 彩虹括号
- 彩色括号配对
- 更容易匹配括号
- 可自定义颜色
### 颜色选择器
- 可视化颜色选择
- 支持 hex、RGB、HSL
- 实时预览
### 翻译工具
- 翻译选定的文本
- 支持多种语言
- 快速键盘访问
### 文本高亮
- 高亮重要文本
- 多种高亮颜色
- 持久化高亮
## 多窗口支持
高效使用多个窗口:
- 每个窗口都是独立的
- 独立的文档
- 同步的设置
- 窗口状态持久化
## 主题自定义
完全控制编辑器外观:
### 内置主题
- 深色模式
- 浅色模式
- 根据系统自动切换
### 自定义主题
- 创建你自己的主题
- 自定义每种颜色
- 保存和分享主题
- 导入社区主题
## 自动更新系统
通过自动更新保持最新:
- 后台更新检查
- 新版本通知
- 一键更新
- 更新历史
- 支持多个更新源GitHub、Gitea
## 数据备份
使用基于 Git 的备份保护你的数据:
- 自动备份
- 手动触发备份
- 支持 GitHub 和 Gitea
- 多种认证方式SSH、Token、密码
- 可配置备份间隔
## 键盘快捷键
广泛的键盘支持:
- 可自定义快捷键
- Vim/Emacs 按键绑定(计划中)
- 快速命令面板
- 上下文感知快捷键
## 性能
专为速度而构建:
- 快速启动时间
- 流畅滚动
- 高效内存使用
- 支持大文件
## 隐私与安全
你的数据是安全的:
- 本地优先存储
- 可选云备份
- 无遥测或跟踪
- 开源代码库

View File

@@ -0,0 +1,107 @@
# 快速开始
学习使用 voidraft 的基础知识并创建你的第一个文档。
## 编辑器界面
当你打开 voidraft 时,你将看到:
- **主编辑器**:编写和编辑的中心区域
- **工具栏**:快速访问常用操作
- **状态栏**:显示当前块的语言和其他信息
## 创建代码块
voidraft 使用基于块的编辑系统。每个块可以有不同的语言:
1.`Ctrl+Enter` 创建新块
2. 输入 `∞∞∞` 后跟语言名称(例如 `∞∞∞javascript`
3. 在该块中开始编码
### 支持的语言
voidraft 支持 30+ 种编程语言,包括:
- JavaScript、TypeScript
- Python、Go、Rust
- HTML、CSS、Sass
- SQL、YAML、JSON
- 以及更多...
## 基本操作
### 导航
- `Ctrl+Up/Down`:在块之间移动
- `Ctrl+Home/End`:跳转到第一个/最后一个块
- `Ctrl+F`:在文档中搜索
### 编辑
- `Ctrl+D`:复制当前行
- `Ctrl+/`:切换注释
- `Alt+Up/Down`:向上/向下移动行
- `Ctrl+Shift+F`:格式化代码(如果语言支持 Prettier
### 块管理
- `Ctrl+Enter`:创建新块
- `Ctrl+Shift+Enter`:在上方创建块
- `Alt+Delete`:删除当前块
## 使用 HTTP 客户端
voidraft 包含用于测试 API 的内置 HTTP 客户端:
1. 创建一个 HTTP 语言的块
2. 编写你的 HTTP 请求:
```http
POST "https://api.example.com/users" {
content-type: "application/json"
@json {
name: "",
email: "zhangsan@example.com"
}
}
```
3. 点击运行按钮执行请求
4. 内联查看响应
## 多窗口支持
同时处理多个文档:
1. 转到 `文件 > 新建窗口`(或 `Ctrl+Shift+N`
2. 每个窗口都是独立的
3. 更改会自动保存
## 自定义主题
个性化你的编辑器:
1. 打开设置(`Ctrl+,`
2. 转到外观
3. 选择主题或创建自己的主题
4. 根据你的偏好自定义颜色
## 键盘快捷键
学习基本快捷键:
| 操作 | 快捷键 |
|-----|--------|
| 新建窗口 | `Ctrl+Shift+N` |
| 搜索 | `Ctrl+F` |
| 替换 | `Ctrl+H` |
| 格式化代码 | `Ctrl+Shift+F` |
| 切换主题 | `Ctrl+Shift+T` |
| 命令面板 | `Ctrl+Shift+P` |
## 下一步
现在你已经了解了基础知识:
- 详细探索[功能特性](/zh/guide/features)

View File

@@ -0,0 +1,63 @@
# 安装
本指南将帮助你在系统上安装 voidraft。
## 系统要求
- **操作系统**Windows 10 或更高版本macOS 和 Linux 支持计划中)
- **内存**:最低 4GB推荐 8GB
- **磁盘空间**200MB 可用空间
## 下载
访问[发布页面](https://github.com/landaiqing/voidraft/releases)并下载适合你平台的最新版本:
- **Windows**`voidraft-windows-amd64-installer.exe`
## 安装步骤
### Windows
1. 从发布页面下载安装程序
2. 运行 `voidraft-windows-amd64-installer.exe` 文件
3. 按照安装向导操作
4. 从开始菜单或桌面快捷方式启动 voidraft
## 首次启动
首次启动 voidraft 时:
1. 应用程序将创建一个数据目录来存储你的文档
2. 你将看到带有欢迎块的主编辑器界面
3. 开始输入或创建你的第一个代码块!
## 配置
voidraft 将其配置和数据存储在:
- **Windows**`%APPDATA%/voidraft/`
你可以自定义各种设置,包括:
- 编辑器主题(深色/浅色模式)
- 代码格式化偏好
- 备份设置
- 键盘快捷键
## 更新
voidraft 包含自动更新功能,会在有新版本时通知你。你可以:
- 从设置中手动检查更新
- 启用自动更新
- 选择首选的更新源
## 故障排除
如果在安装过程中遇到任何问题:
1. 确保你有管理员权限
2. 检查杀毒软件是否阻止了安装
3. 访问我们的 [GitHub issues](https://github.com/landaiqing/voidraft/issues) 页面寻求帮助
下一步:[快速开始 →](/zh/guide/getting-started)

View File

@@ -0,0 +1,50 @@
# 简介
欢迎使用 voidraft —— 一个专为开发者设计的优雅文本片段记录工具。
## 什么是 voidraft
voidraft 是一个现代化的桌面应用程序帮助开发者管理文本片段、代码块、API 响应、会议笔记和日常待办事项。它为开发工作流程提供了流畅而优雅的编辑体验和强大的功能。
## 核心特性
### 块状编辑模式
voidraft 使用受 Heynote 启发的独特块状编辑系统。你可以将内容分割为独立的代码块,每个块具有:
- 不同的编程语言设置
- 语法高亮
- 独立格式化
- 轻松在块之间导航
### 开发者工具
- **HTTP 客户端**:直接在编辑器中测试 API
- **代码格式化**:内置 Prettier 支持多种语言
- **语法高亮**:支持 30+ 种编程语言
- **自动语言检测**:自动识别代码块语言类型
### 自定义
- **自定义主题**:创建并保存你自己的编辑器主题
- **扩展功能**:丰富的编辑器扩展,包括小地图、彩虹括号、颜色选择器等
- **多窗口**:同时处理多个文档
### 数据管理
- **Git 备份**:使用 Git 仓库自动备份
- **云同步**:跨设备同步你的数据
- **自动更新**:及时获取最新功能
## 为什么选择 voidraft
- **专注开发者**:考虑开发者需求而构建
- **现代技术栈**使用前沿技术Wails3、Vue 3、CodeMirror 6
- **跨平台**:支持 WindowsmacOS 和 Linux 支持计划中)
- **开源**MIT 许可证,社区驱动开发
## 开始使用
准备好开始了吗?从我们的[发布页面](https://github.com/landaiqing/voidraft/releases)下载最新版本,或继续阅读文档了解更多。
下一步:[安装 →](/zh/guide/installation)

View File

@@ -0,0 +1,56 @@
---
layout: home
hero:
name: "voidraft"
text: "优雅的文本片段记录工具"
tagline: 为开发者设计,用现代技术打造
image:
src: /img/hero.png
alt: "voidraft"
actions:
- theme: brand
text: 开始使用
link: https://github.com/landaiqing/voidraft/releases
- theme: alt
text: 使用文档
link: /zh/guide/introduction
features:
- icon: 📝
title: 块状编辑模式
details: 将内容分割为独立的代码块,每个块可设置不同语言。继承了 Heynote 优雅的块状编辑理念。
- icon: 🎨
title: 语法高亮
details: 内置支持 30+ 种编程语言的语法高亮,自动语言检测,集成 Prettier 代码格式化工具。
- icon: 🌐
title: HTTP 客户端
details: 集成 HTTP 客户端,支持 JSON、FormData、XML 等多种请求格式。直接在编辑器中测试 API。
- icon: 🎯
title: 多窗口支持
details: 同时编辑多个文档,每个窗口独立维护自己的状态和配置。
- icon: 🎭
title: 主题自定义
details: 完整的主题自定义支持,支持深色/浅色模式。创建并保存你自己的编辑器主题。
- icon: 🔧
title: 丰富的扩展
details: VSCode 风格搜索替换、彩虹括号、小地图、颜色选择器、翻译工具、文本高亮等实用扩展。
- icon: 🔄
title: 自动更新系统
details: 内置自我更新机制,支持多个更新源。及时获取最新功能和改进。
- icon: ☁️
title: Git 备份
details: 基于 Git 的自动数据备份。支持 GitHub、Gitea提供 SSH、Token 等多种认证方式。
- icon: ⚡
title: 现代化架构
details: 采用 Wails3、Vue 3 和 CodeMirror 6 构建。跨平台桌面应用,原生性能,现代化界面。
---

File diff suppressed because it is too large Load Diff

View File

@@ -9,82 +9,99 @@
"build": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" vue-tsc && cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" vite build --mode production", "build": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" vue-tsc && cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" vite build --mode production",
"preview": "vite preview", "preview": "vite preview",
"lint": "eslint", "lint": "eslint",
"lint:fix": "eslint --fix" "lint:fix": "eslint --fix",
"build:lang-parser": "node src/views/editor/extensions/codeblock/lang-parser/build-parser.js",
"build:mermaid-parser": "node src/views/editor/language/mermaid/build-parsers.js",
"test": "vitest",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs",
"app:dev": "cd .. &&wails3 dev",
"app:build": "cd .. && wails3 task build",
"app:package": "cd .. && wails3 package",
"app:generate": "cd .. && wails3 generate bindings -ts"
}, },
"dependencies": { "dependencies": {
"@codemirror/autocomplete": "^6.18.7", "@codemirror/autocomplete": "^6.20.0",
"@codemirror/commands": "^6.8.1", "@codemirror/commands": "^6.10.0",
"@codemirror/lang-angular": "^0.1.4", "@codemirror/lang-angular": "^0.1.4",
"@codemirror/lang-cpp": "^6.0.3", "@codemirror/lang-cpp": "^6.0.3",
"@codemirror/lang-css": "^6.3.1", "@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-go": "^6.0.1", "@codemirror/lang-go": "^6.0.1",
"@codemirror/lang-html": "^6.4.10", "@codemirror/lang-html": "^6.4.11",
"@codemirror/lang-java": "^6.0.2", "@codemirror/lang-java": "^6.0.2",
"@codemirror/lang-javascript": "^6.2.4", "@codemirror/lang-javascript": "^6.2.4",
"@codemirror/lang-json": "^6.0.2", "@codemirror/lang-json": "^6.0.2",
"@codemirror/lang-less": "^6.0.2", "@codemirror/lang-less": "^6.0.2",
"@codemirror/lang-lezer": "^6.0.2", "@codemirror/lang-lezer": "^6.0.2",
"@codemirror/lang-liquid": "^6.3.0", "@codemirror/lang-liquid": "^6.3.0",
"@codemirror/lang-markdown": "^6.3.4", "@codemirror/lang-markdown": "^6.5.0",
"@codemirror/lang-php": "^6.0.2", "@codemirror/lang-php": "^6.0.2",
"@codemirror/lang-python": "^6.2.1", "@codemirror/lang-python": "^6.2.1",
"@codemirror/lang-rust": "^6.0.2", "@codemirror/lang-rust": "^6.0.2",
"@codemirror/lang-sass": "^6.0.2", "@codemirror/lang-sass": "^6.0.2",
"@codemirror/lang-sql": "^6.9.1", "@codemirror/lang-sql": "^6.10.0",
"@codemirror/lang-vue": "^0.1.3", "@codemirror/lang-vue": "^0.1.3",
"@codemirror/lang-wast": "^6.0.2", "@codemirror/lang-wast": "^6.0.2",
"@codemirror/lang-yaml": "^6.1.2", "@codemirror/lang-yaml": "^6.1.2",
"@codemirror/language": "^6.11.3", "@codemirror/language": "^6.11.3",
"@codemirror/language-data": "^6.5.1", "@codemirror/language-data": "^6.5.2",
"@codemirror/legacy-modes": "^6.5.1", "@codemirror/legacy-modes": "^6.5.2",
"@codemirror/lint": "^6.8.5", "@codemirror/lint": "^6.9.2",
"@codemirror/search": "^6.5.11", "@codemirror/search": "^6.5.11",
"@codemirror/state": "^6.5.2", "@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.38.2", "@codemirror/view": "^6.38.8",
"@cospaia/prettier-plugin-clojure": "^0.0.2", "@cospaia/prettier-plugin-clojure": "^0.0.2",
"@lezer/highlight": "^1.2.1", "@lezer/highlight": "^1.2.3",
"@lezer/lr": "^1.4.2", "@lezer/lr": "^1.4.4",
"@prettier/plugin-xml": "^3.4.2", "@prettier/plugin-xml": "^3.4.2",
"@replit/codemirror-lang-svelte": "^6.0.0",
"@toml-tools/lexer": "^1.0.0", "@toml-tools/lexer": "^1.0.0",
"@toml-tools/parser": "^1.0.0", "@toml-tools/parser": "^1.0.0",
"@types/katex": "^0.16.7",
"codemirror": "^6.0.2", "codemirror": "^6.0.2",
"codemirror-lang-elixir": "^4.0.0", "codemirror-lang-elixir": "^4.0.0",
"colors-named": "^1.0.2", "colors-named": "^1.0.2",
"colors-named-hex": "^1.0.2", "colors-named-hex": "^1.0.2",
"franc-min": "^6.2.0",
"groovy-beautify": "^0.0.17", "groovy-beautify": "^0.0.17",
"hsl-matcher": "^1.2.4", "hsl-matcher": "^1.2.4",
"java-parser": "^3.0.1", "java-parser": "^3.0.1",
"jsox": "^1.2.123", "katex": "^0.16.25",
"linguist-languages": "^9.0.0", "linguist-languages": "^9.1.0",
"marked": "^17.0.1",
"mermaid": "^11.12.1",
"php-parser": "^3.2.5", "php-parser": "^3.2.5",
"pinia": "^3.0.3", "pinia": "^3.0.4",
"pinia-plugin-persistedstate": "^4.5.0", "pinia-plugin-persistedstate": "^4.7.1",
"prettier": "^3.6.2", "prettier": "^3.7.2",
"remarkable": "^2.0.1", "sass": "^1.94.2",
"sass": "^1.92.1", "vue": "^3.5.25",
"vue": "^3.5.21", "vue-i18n": "^11.2.2",
"vue-i18n": "^11.1.12",
"vue-pick-colors": "^1.8.0", "vue-pick-colors": "^1.8.0",
"vue-router": "^4.5.1" "vue-router": "^4.6.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.35.0", "@eslint/js": "^9.39.1",
"@lezer/generator": "^1.8.0", "@lezer/generator": "^1.8.0",
"@types/node": "^24.3.1", "@types/node": "^24.10.1",
"@types/remarkable": "^2.0.8", "@vitejs/plugin-vue": "^6.0.2",
"@vitejs/plugin-vue": "^6.0.1",
"@wailsio/runtime": "latest", "@wailsio/runtime": "latest",
"cross-env": "^7.0.3", "cross-env": "^10.1.0",
"eslint": "^9.35.0", "eslint": "^9.39.1",
"eslint-plugin-vue": "^10.4.0", "eslint-plugin-vue": "^10.6.2",
"globals": "^16.4.0", "globals": "^16.5.0",
"typescript": "^5.9.2", "happy-dom": "^20.0.11",
"typescript-eslint": "^8.43.0", "typescript": "^5.9.3",
"unplugin-vue-components": "^29.0.0", "typescript-eslint": "^8.48.0",
"vite": "^7.1.5", "unplugin-vue-components": "^30.0.0",
"vite": "npm:rolldown-vite@latest",
"vite-plugin-node-polyfills": "^0.24.0", "vite-plugin-node-polyfills": "^0.24.0",
"vitepress": "^2.0.0-alpha.12",
"vitest": "^4.0.14",
"vue-eslint-parser": "^10.2.0", "vue-eslint-parser": "^10.2.0",
"vue-tsc": "^3.0.6" "vue-tsc": "^3.1.5"
},
"overrides": {
"vite": "npm:rolldown-vite@latest"
} }
} }

View File

@@ -1,22 +1,21 @@
<script setup lang="ts"> <script setup lang="ts">
import {onMounted} from 'vue'; import {onBeforeMount} from 'vue';
import {useConfigStore} from '@/stores/configStore'; import {useConfigStore} from '@/stores/configStore';
import {useSystemStore} from '@/stores/systemStore'; import {useSystemStore} from '@/stores/systemStore';
import {useKeybindingStore} from '@/stores/keybindingStore'; import {useKeybindingStore} from '@/stores/keybindingStore';
import {useThemeStore} from '@/stores/themeStore'; import {useThemeStore} from '@/stores/themeStore';
import {useUpdateStore} from '@/stores/updateStore'; import {useUpdateStore} from '@/stores/updateStore';
import {useBackupStore} from '@/stores/backupStore';
import WindowTitleBar from '@/components/titlebar/WindowTitleBar.vue'; import WindowTitleBar from '@/components/titlebar/WindowTitleBar.vue';
import {useTranslationStore} from "@/stores/translationStore";
const configStore = useConfigStore(); const configStore = useConfigStore();
const systemStore = useSystemStore(); const systemStore = useSystemStore();
const keybindingStore = useKeybindingStore(); const keybindingStore = useKeybindingStore();
const themeStore = useThemeStore(); const themeStore = useThemeStore();
const updateStore = useUpdateStore(); const updateStore = useUpdateStore();
const backupStore = useBackupStore(); const translationStore = useTranslationStore();
// 应用启动时加载配置和初始化系统信息 onBeforeMount(async () => {
onMounted(async () => {
// 并行初始化配置、系统信息和快捷键配置 // 并行初始化配置、系统信息和快捷键配置
await Promise.all([ await Promise.all([
configStore.initConfig(), configStore.initConfig(),
@@ -26,10 +25,8 @@ onMounted(async () => {
// 初始化语言和主题 // 初始化语言和主题
await configStore.initializeLanguage(); await configStore.initializeLanguage();
themeStore.initializeTheme(); await themeStore.initializeTheme();
await translationStore.loadTranslators();
// 初始化备份服务
await backupStore.initialize();
// 启动时检查更新 // 启动时检查更新
await updateStore.checkOnStartup(); await updateStore.checkOnStartup();

View File

@@ -1,254 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
鸿蒙字体压缩工具
使用 fonttools 库压缩 TTF 字体文件,减小文件大小
"""
import os
import sys
import subprocess
import shutil
from pathlib import Path
from typing import List, Tuple
def check_dependencies():
"""检查必要的依赖是否已安装"""
missing_packages = []
# 检查 fonttools
try:
import fontTools
except ImportError:
missing_packages.append('fonttools')
# 检查 brotli
try:
import brotli
except ImportError:
missing_packages.append('brotli')
# 检查 pyftsubset 命令是否可用
try:
result = subprocess.run(['pyftsubset', '--help'], capture_output=True, text=True)
if result.returncode != 0:
missing_packages.append('fonttools[subset]')
except FileNotFoundError:
if 'fonttools' not in missing_packages:
missing_packages.append('fonttools[subset]')
if missing_packages:
print(f"缺少必要的依赖包: {', '.join(missing_packages)}")
print("请运行以下命令安装:")
print(f"pip install {' '.join(missing_packages)}")
return False
return True
def get_file_size(file_path: str) -> int:
"""获取文件大小(字节)"""
return os.path.getsize(file_path)
def format_file_size(size_bytes: int) -> str:
"""格式化文件大小显示"""
if size_bytes < 1024:
return f"{size_bytes} B"
elif size_bytes < 1024 * 1024:
return f"{size_bytes / 1024:.2f} KB"
else:
return f"{size_bytes / (1024 * 1024):.2f} MB"
def compress_font(input_path: str, output_path: str, compression_level: str = "basic") -> bool:
"""
压缩单个字体文件
Args:
input_path: 输入字体文件路径
output_path: 输出字体文件路径
compression_level: 压缩级别 ("basic", "medium", "aggressive")
Returns:
bool: 压缩是否成功
"""
try:
# 基础压缩参数
base_args = [
"pyftsubset", input_path,
"--output-file=" + output_path,
"--flavor=woff2", # 输出为 WOFF2 格式,压缩率更高
"--with-zopfli", # 使用 Zopfli 算法进一步压缩
]
# 根据压缩级别设置不同的参数
if compression_level == "basic":
# 基础压缩:保留常用字符和功能
args = base_args + [
"--unicodes=U+0020-007F,U+00A0-00FF,U+2000-206F,U+2070-209F,U+20A0-20CF", # 基本拉丁字符、标点符号等
"--layout-features=*", # 保留所有布局特性
"--glyph-names", # 保留字形名称
"--symbol-cmap", # 保留符号映射
"--legacy-cmap", # 保留传统字符映射
"--notdef-glyph", # 保留 .notdef 字形
"--recommended-glyphs", # 保留推荐字形
"--name-IDs=*", # 保留所有名称ID
"--name-legacy", # 保留传统名称
]
elif compression_level == "medium":
# 中等压缩:移除一些不常用的功能
args = base_args + [
"--unicodes=U+0020-007F,U+00A0-00FF,U+2000-206F", # 减少字符范围
"--layout-features=kern,liga,clig", # 只保留关键布局特性
"--no-glyph-names", # 移除字形名称
"--notdef-glyph",
"--name-IDs=1,2,3,4,5,6", # 只保留基本名称ID
]
else: # aggressive
# 激进压缩:最大程度减小文件大小
args = base_args + [
"--unicodes=U+0020-007F", # 只保留基本ASCII字符
"--no-layout-features", # 移除所有布局特性
"--no-glyph-names", # 移除字形名称
"--no-symbol-cmap", # 移除符号映射
"--no-legacy-cmap", # 移除传统映射
"--notdef-glyph",
"--name-IDs=1,2", # 只保留最基本的名称
"--desubroutinize", # 去子程序化可能减小CFF字体大小
]
# 执行压缩命令
result = subprocess.run(args, capture_output=True, text=True)
if result.returncode == 0:
return True
else:
print(f"压缩失败: {result.stderr}")
return False
except Exception as e:
print(f"压缩过程中出现错误: {str(e)}")
return False
def find_font_files(directory: str) -> List[str]:
"""查找目录中的所有字体文件"""
font_extensions = ['.ttf', '.otf', '.woff', '.woff2']
font_files = []
for root, dirs, files in os.walk(directory):
for file in files:
if any(file.lower().endswith(ext) for ext in font_extensions):
font_files.append(os.path.join(root, file))
return font_files
def compress_fonts_batch(font_directory: str, compression_level: str = "basic"):
"""
批量压缩字体文件
Args:
font_directory: 字体文件目录
compression_level: 压缩级别
"""
if not os.path.exists(font_directory):
print(f"错误: 目录 {font_directory} 不存在")
return
# 查找所有字体文件
font_files = find_font_files(font_directory)
if not font_files:
print("未找到字体文件")
return
print(f"找到 {len(font_files)} 个字体文件")
print(f"压缩级别: {compression_level}")
print(f"压缩后的文件将与源文件放在同一目录,扩展名为 .woff2")
print("-" * 60)
total_original_size = 0
total_compressed_size = 0
successful_compressions = 0
for i, font_file in enumerate(font_files, 1):
print(f"[{i}/{len(font_files)}] 处理: {os.path.basename(font_file)}")
# 获取原始文件大小
original_size = get_file_size(font_file)
total_original_size += original_size
# 生成输出文件名(保持原文件名,只改变扩展名)
file_dir = os.path.dirname(font_file)
base_name = os.path.splitext(os.path.basename(font_file))[0]
output_file = os.path.join(file_dir, f"{base_name}.woff2")
# 压缩字体
if compress_font(font_file, output_file, compression_level):
if os.path.exists(output_file):
compressed_size = get_file_size(output_file)
total_compressed_size += compressed_size
successful_compressions += 1
# 计算压缩率
compression_ratio = (1 - compressed_size / original_size) * 100
print(f" ✓ 成功: {format_file_size(original_size)}{format_file_size(compressed_size)} "
f"(压缩 {compression_ratio:.1f}%)")
else:
print(f" ✗ 失败: 输出文件未生成")
else:
print(f" ✗ 失败: 压缩过程出错")
print()
# 显示总结
print("=" * 60)
print("压缩完成!")
print(f"成功压缩: {successful_compressions}/{len(font_files)} 个文件")
if successful_compressions > 0:
total_compression_ratio = (1 - total_compressed_size / total_original_size) * 100
print(f"总大小: {format_file_size(total_original_size)}{format_file_size(total_compressed_size)}")
print(f"总压缩率: {total_compression_ratio:.1f}%")
print(f"节省空间: {format_file_size(total_original_size - total_compressed_size)}")
def main():
"""主函数"""
print("鸿蒙字体压缩工具")
print("=" * 60)
# 检查依赖
if not check_dependencies():
return
# 获取当前脚本所在目录
current_dir = os.path.dirname(os.path.abspath(__file__))
# 设置默认字体目录
font_directory = current_dir
print(f"字体目录: {font_directory}")
# 让用户选择压缩级别
print("\n请选择压缩级别:")
print("1. 基础压缩 (保留大部分功能,适合网页使用)")
print("2. 中等压缩 (平衡文件大小和功能)")
print("3. 激进压缩 (最小文件大小,可能影响显示效果)")
while True:
choice = input("\n请输入选择 (1-3): ").strip()
if choice == "1":
compression_level = "basic"
break
elif choice == "2":
compression_level = "medium"
break
elif choice == "3":
compression_level = "aggressive"
break
else:
print("无效选择,请输入 1、2 或 3")
# 开始批量压缩
compress_fonts_batch(font_directory, compression_level=compression_level)
if __name__ == "__main__":
main()

Some files were not shown because too many files have changed in this diff Show More