78 Commits

Author SHA1 Message Date
sepakeloudest 485ef089df feat: upgrade HiFast theme and optimize loading 2026-10-31 22:48:21 +08:00
sepakeloudest bc60d22782 修改设备显示字段
site-dist-deploy / build-and-deploy (push) Successful in 1m6s
2026-09-17 11:28:03 +08:00
sepakeloudest 7a3ad1154f 切换openinstall cdn
site-dist-deploy / build-and-deploy (push) Successful in 1m32s
2026-09-15 10:50:11 +08:00
sepakeloudest 6c2cc33620 官网发布活动价格样式适配
site-dist-deploy / build-and-deploy (push) Failing after 13m26s
2026-06-17 12:07:20 +08:00
sepakeloudest 82f41b1731 删除限速说明
site-dist-deploy / build-and-deploy (push) Successful in 2m6s
2026-05-19 22:34:17 +08:00
sepakeloudest 2e4c6c2329 处理邀请链接
site-dist-deploy / build-and-deploy (push) Successful in 1m42s
2026-05-05 04:47:38 +03:00
sepakeloudest 06587fb47b 账号
site-dist-deploy / build-and-deploy (push) Successful in 1m16s
2026-05-02 19:45:06 +03:00
sepakeloudest cd307c3297 尝鲜价
site-dist-deploy / build-and-deploy (push) Successful in 1m16s
2026-05-02 18:44:53 +03:00
sepakeloudest d025ab2d33 样式兼容方案
site-dist-deploy / build-and-deploy (push) Successful in 1m17s
2026-04-28 06:24:56 +03:00
sepakeloudest 90699d7fe1 删除网站自动打开app的行为
site-dist-deploy / build-and-deploy (push) Successful in 1m16s
2026-04-23 19:30:41 +03:00
sepakeloudest 8d0d609494 增加系统标识
site-dist-deploy / build-and-deploy (push) Successful in 1m19s
2026-04-22 06:05:01 +03:00
sepakeloudest 8175f19da0 更换苹果账号
site-dist-deploy / build-and-deploy (push) Successful in 1m21s
2026-04-20 03:42:01 +03:00
sepakeloudest 6c296dbaaf 处理下载链接
site-dist-deploy / build-and-deploy (push) Successful in 1m17s
2026-04-17 14:35:48 +03:00
sepakeloudest 3313b95ab1 添加谷歌tag链接
site-dist-deploy / build-and-deploy (push) Successful in 1m15s
2026-04-11 15:14:29 +03:00
sepakeloudest 4dc5cb4dd9 添加默认邀请人,website@hifast.biz 'uSSfg1Y1vt'
site-dist-deploy / build-and-deploy (push) Successful in 1m25s
2026-04-10 06:36:26 +03:00
sepakeloudest aeeea4c5d2 增加链接带参数
site-dist-deploy / build-and-deploy (push) Successful in 1m51s
2026-04-03 18:52:48 +03:00
sepakeloudest 1414444175 增加链接带参数
site-dist-deploy / build-and-deploy (push) Successful in 1m41s
2026-03-30 06:03:15 +03:00
sepakeloudest 5f67cf76bd 替换评论样式
site-dist-deploy / build-and-deploy (push) Successful in 2m6s
2026-03-25 10:12:08 +02:00
sepakeloudest b6caf1c38b 调整细节
site-dist-deploy / build-and-deploy (push) Successful in 1m51s
2026-03-25 09:42:52 +02:00
sepakeloudest 784cabaddc 增加评论样式
site-dist-deploy / build-and-deploy (push) Successful in 2m9s
2026-03-23 12:09:21 +02:00
sepakeloudest 73afc3c435 增加评论样式
site-dist-deploy / build-and-deploy (push) Successful in 2m27s
2026-03-21 02:09:05 +02:00
sepakeloudest fb7e5031c8 提交尝鲜价套餐
site-dist-deploy / build-and-deploy (push) Successful in 1m44s
2026-03-20 14:30:23 +02:00
sepakeloudest a350863a3e 增加android下载链接
site-dist-deploy / build-and-deploy (push) Successful in 1m51s
2026-03-20 11:40:13 +02:00
sepakeloudest f33e4a727a 增加android下载链接
site-dist-deploy / build-and-deploy (push) Successful in 1m55s
2026-03-20 10:23:53 +02:00
sepakeloudest 26bfcaa5f3 增加android下载链接
site-dist-deploy / build-and-deploy (push) Successful in 1m59s
2026-03-20 10:18:29 +02:00
sepakeloudest 0ed394ea67 修改下载链接
site-dist-deploy / build-and-deploy (push) Successful in 1m51s
2026-03-20 08:27:33 +02:00
sepakeloudest 1e017b2453 修改下载链接
site-dist-deploy / build-and-deploy (push) Successful in 1m41s
2026-03-20 06:00:15 +02:00
sepakeloudest 3bed820d1d 修改下载链接
site-dist-deploy / build-and-deploy (push) Successful in 1m57s
2026-03-20 05:49:25 +02:00
sepakeloudest 169338cfad 替换mac 应用市场下载
site-dist-deploy / build-and-deploy (push) Successful in 2m1s
2026-03-12 05:16:05 +02:00
sepakeloudest 1fa5d49572 登陆页面获取逻辑修改
site-dist-deploy / build-and-deploy (push) Successful in 1m46s
2026-03-07 11:47:45 +02:00
sepakeloudest 9e00434eb6 删除下载链接请求
site-dist-deploy / build-and-deploy (push) Successful in 1m48s
2026-02-26 10:46:06 +02:00
sepakeloudest dd58e3ce55 删除下载链接请求
site-dist-deploy / build-and-deploy (push) Successful in 1m41s
2026-02-25 05:07:16 +02:00
sepakeloudest 466a7c8592 删除谷歌
site-dist-deploy / build-and-deploy (push) Successful in 2m6s
2026-02-25 04:28:59 +02:00
sepakeloudest 719f3bc35b 增加公司名称
site-dist-deploy / build-and-deploy (push) Successful in 1m34s
2026-02-06 05:42:46 +02:00
sepakeloudest 08b08574b9 增加公司名称
site-dist-deploy / build-and-deploy (push) Successful in 1m49s
2026-02-06 05:36:35 +02:00
sepakeloudest 50ad1852ab 谷歌链接
site-dist-deploy / build-and-deploy (push) Successful in 1m30s
2026-02-01 21:50:27 -08:00
sepakeloudest 4578e27d38 修改下载链接
site-dist-deploy / build-and-deploy (push) Successful in 1m34s
2026-01-29 00:06:47 -08:00
sepakeloudest 08a16a68c6 修改下载链接
site-dist-deploy / build-and-deploy (push) Successful in 1m34s
2026-01-27 07:52:44 -08:00
sepakeloudest 7a2cf6b8f8 接口进行加密
site-dist-deploy / build-and-deploy (push) Successful in 1m42s
2026-01-26 03:38:18 -08:00
sepakeloudest e768e0f9f0 首页登录增加loading
site-dist-deploy / build-and-deploy (push) Successful in 1m34s
2026-01-25 00:18:05 -08:00
sepakeloudest 0af2e0f896 增加login- redirect路径
site-dist-deploy / build-and-deploy (push) Successful in 1m27s
2026-01-23 06:27:16 -08:00
sepakeloudest 77e7d81c4b 更新下载链接
site-dist-deploy / build-and-deploy (push) Successful in 1m8s
2026-01-23 06:02:24 -08:00
sepakeloudest c4f967affa 更新下载链接
site-dist-deploy / build-and-deploy (push) Successful in 1m37s
2026-01-23 05:59:36 -08:00
sepakeloudest 0c8afb05e3 更新下载链接
site-dist-deploy / build-and-deploy (push) Successful in 1m25s
2026-01-23 05:39:59 -08:00
shanshanzhong147 3f61439a96 fix: 修改落地页服务器
site-dist-deploy / build-and-deploy (push) Successful in 1m21s
2026-01-22 00:33:35 -08:00
sepakeloudest 7f12066ec4 更改客服到chatwoot
site-dist-deploy / build-and-deploy (push) Successful in 1m25s
2026-01-19 17:41:35 -08:00
sepakeloudest 833cd9e305 处理到期时间
site-dist-deploy / build-and-deploy (push) Successful in 1m17s
2026-01-07 18:47:49 -08:00
sepakeloudest 686be3a855 订单弹窗
site-dist-deploy / build-and-deploy (push) Successful in 1m0s
2026-01-07 06:52:00 -08:00
sepakeloudest c0e22f5a6e 订单弹窗
site-dist-deploy / build-and-deploy (push) Successful in 1m3s
2026-01-07 06:48:47 -08:00
sepakeloudest 32bda7da80 去掉前后空格
site-dist-deploy / build-and-deploy (push) Has been cancelled
2026-01-07 06:48:11 -08:00
sepakeloudest 5f9b23c112 去掉前后空格
site-dist-deploy / build-and-deploy (push) Successful in 1m3s
2026-01-07 00:30:33 -08:00
sepakeloudest dfd236a70b 去掉前后空格
site-dist-deploy / build-and-deploy (push) Successful in 59s
2026-01-07 00:14:36 -08:00
sepakeloudest 8153fdab79 优化选择建议
site-dist-deploy / build-and-deploy (push) Successful in 1m15s
2026-01-07 00:11:52 -08:00
sepakeloudest efafec56e0 增加按钮
site-dist-deploy / build-and-deploy (push) Successful in 1m16s
2026-01-07 00:01:48 -08:00
sepakeloudest 0aee63988c 订单和订阅修改
site-dist-deploy / build-and-deploy (push) Successful in 1m4s
2026-01-06 23:13:39 -08:00
sepakeloudest 7e12363976 样式修改
site-dist-deploy / build-and-deploy (push) Successful in 1m10s
2026-01-06 22:41:33 -08:00
sepakeloudest a3e58a01a1 样式修改
site-dist-deploy / build-and-deploy (push) Successful in 1m12s
2026-01-06 20:01:20 -08:00
sepakeloudest e3cca4c51c bugfix
site-dist-deploy / build-and-deploy (push) Successful in 1m10s
2026-01-06 19:52:48 -08:00
shanshanzhong147 fa29905c7c fix(ci): 修复基础URL配置问题
site-dist-deploy / build-and-deploy (push) Successful in 1m16s
将VITE_APP_BASE_URL默认值设置为'/',避免环境变量未定义时请求失败
2026-01-06 19:36:37 -08:00
sepakeloudest 8b5444919a 修改路由地址
site-dist-deploy / build-and-deploy (push) Successful in 1m2s
2026-01-05 21:25:03 -08:00
sepakeloudest 595d1d0932 增加邮箱校验
site-dist-deploy / build-and-deploy (push) Successful in 1m54s
2026-01-05 18:37:02 -08:00
sepakeloudest be35ecb62a 优化图片大小
site-dist-deploy / build-and-deploy (push) Successful in 1m1s
2026-01-05 05:17:00 -08:00
sepakeloudest 69d10f88a0 meta 无障碍
site-dist-deploy / build-and-deploy (push) Successful in 1m5s
2026-01-05 04:52:00 -08:00
sepakeloudest 09bc1df55c 样式修改
site-dist-deploy / build-and-deploy (push) Successful in 1m31s
2026-01-05 04:21:25 -08:00
sepakeloudest 4e73b7a864 样式修改
site-dist-deploy / build-and-deploy (push) Successful in 54s
2026-01-05 03:54:12 -08:00
sepakeloudest 7a03955aac 样式修改
site-dist-deploy / build-and-deploy (push) Successful in 1m14s
2026-01-05 03:50:22 -08:00
sepakeloudest e86cfd0881 样式修改
site-dist-deploy / build-and-deploy (push) Successful in 1m12s
2026-01-05 02:56:39 -08:00
sepakeloudest 27ebf5adc5 样式修改
site-dist-deploy / build-and-deploy (push) Successful in 1m12s
2026-01-05 01:15:16 -08:00
sepakeloudest 3578d3fb4a 登录入口
site-dist-deploy / build-and-deploy (push) Successful in 1m10s
2026-01-05 00:28:23 -08:00
sepakeloudest e6c3e563a8 登录入口
site-dist-deploy / build-and-deploy (push) Successful in 53s
2026-01-05 00:17:17 -08:00
sepakeloudest dc40c24b73 更新代码
site-dist-deploy / build-and-deploy (push) Successful in 1m6s
2026-01-05 00:14:16 -08:00
sepakeloudest 96dbe0c108 桌面端样式
site-dist-deploy / build-and-deploy (push) Successful in 1m8s
2026-01-04 22:52:06 -08:00
sepakeloudest 02da9d975f 提交热区点击
site-dist-deploy / build-and-deploy (push) Successful in 1m0s
2026-01-04 22:38:23 -08:00
sepakeloudest 8e3076e113 提交热区点击
site-dist-deploy / build-and-deploy (push) Successful in 1m11s
2026-01-04 22:31:35 -08:00
shanshanzhong147 1cae216052 fix: 更新API和基础URL至h.hifastapp.com
site-dist-deploy / build-and-deploy (push) Successful in 59s
修复登录表单的HTML结构和类型定义
将API目标服务器和基础URL从h.hifastvpn.com更新为h.hifastapp.com
优化登录表单的HTML结构,添加表单提交处理和自动完成属性
2026-01-04 22:22:00 -08:00
shanshanzhong147 3fddc11b17 ci(workflow): 更新docker工作流中的基础URL为h.hifastvpn.com
site-dist-deploy / build-and-deploy (push) Successful in 53s
2026-01-04 22:17:53 -08:00
shanshanzhong147 ea20a1016e fix: 更新环境变量和API目标地址
site-dist-deploy / build-and-deploy (push) Successful in 1m12s
- 统一各环境中的VITE_APP_BASE_URL值为/
- 将vite配置中的API目标地址从api.hifast.biz更新为h.hifastvpn.com
- 删除无用的aaa.txt文件
2026-01-04 22:13:37 -08:00
shanshanzhong147 77e7ec603b ci: 更新Docker工作流中的基础URL为h.hifastvpn.com
site-dist-deploy / build-and-deploy (push) Successful in 1m10s
2026-01-04 22:08:24 -08:00
219 changed files with 52856 additions and 6595 deletions
+1 -2
View File
@@ -1,2 +1 @@
# https://api.hifast.biz
VITE_APP_BASE_URL=
VITE_APP_BASE_URL=/
+1 -2
View File
@@ -1,2 +1 @@
# https://h.hifastapp.com
VITE_APP_BASE_URL=
VITE_APP_BASE_URL=/
+1 -2
View File
@@ -1,2 +1 @@
https://h.hifastapp.com
VITE_APP_BASE_URL=
VITE_APP_BASE_URL=/
+3 -3
View File
@@ -12,12 +12,12 @@ on:
env:
VITE_APP_BASE_URL: https://h.hifast.biz
VITE_APP_BASE_URL: /
SSH_HOST: ${{ vars.PRO_SSH_HOST }}
SSH_PORT: ${{ vars.PRO_SSH_PORT }}
SSH_USER: ${{ vars.PRO_SSH_USER }}
SSH_PASSWORD: ${{ vars.PRO_SSH_PASSWORD }}
DEPLOY_PATH: /var/www/down
DEPLOY_PATH: /var/www/hi-landing-hero
# TG通知
TG_BOT_TOKEN: 8114337882:AAHkEx03HSu7RxN4IHBJJEnsK9aPPzNLIk0
TG_CHAT_ID: "-4940243803"
@@ -42,7 +42,7 @@ jobs:
- name: Build dist with Unified Script
env:
VITE_APP_BASE_URL: "https://h.hifast.biz"
VITE_APP_BASE_URL: "/"
run: |
chmod +x scripts/ci-build.sh
./scripts/ci-build.sh
+3
View File
@@ -34,3 +34,6 @@ coverage
# Vitest
__screenshots__/
# Reproducible comparison candidates; selected production GLB lives in public/hifast.
docs/model-comparison/*.glb
+1
View File
@@ -0,0 +1 @@
engine-strict=true
+1
View File
@@ -0,0 +1 @@
24
+26 -34
View File
@@ -1,48 +1,40 @@
# hi-landing-hreo
# HiFast 官网
This template should help get you started developing with Vue 3 in Vite.
Vue 3 + Vue Router + Reka UI + Tailwind CSS,业务以 `main` 为基准。
## Recommended IDE Setup
## 开发
[VS Code](https://code.visualstudio.com/) + [Vue (Official)](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
## Recommended Browser Setup
- Chromium-based browsers (Chrome, Edge, Brave, etc.):
- [Vue.js devtools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd)
- [Turn on Custom Object Formatter in Chrome DevTools](http://bit.ly/object-formatters)
- Firefox:
- [Vue.js devtools](https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/)
- [Turn on Custom Object Formatter in Firefox DevTools](https://fxdx.dev/firefox-devtools-custom-object-formatters/)
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
## Customize configuration
See [Vite Configuration Reference](https://vite.dev/config/).
## Project Setup
使用 **Node.js 24 或更高版本**。仓库通过 `.nvmrc` 选择 Node 24,`.npmrc` 强制检查版本。统一使用 npm 和 `package-lock.json`。
```sh
pnpm install
nvm use
npm ci
npm run dev -- --host 127.0.0.1 --port 5186 --strictPort
```
### Compile and Hot-Reload for Development
本地 `/api/v1` 代理沿用 `https://api.hifast.biz`,请求加密、Token、邀请码、套餐及付款契约不变。请勿用真实付款测试界面。
```sh
pnpm dev
npm run type-check
npm test
npm run build:prod
npm run lint:check
```
### Type-Check, Compile and Minify for Production
`build:prod` 继续使用仓库原有的 `pord` mode 和 `.env.pord`。CI 检查 Node >=24、运行 `npm ci`、类型检查及构建。需提前将 CI runner 的 Node 更新至 24+;系统包源若仅提供旧版本,脚本会明确失败。
```sh
pnpm build
```
## 主题
### Lint with [ESLint](https://eslint.org/)
- 首页 `/`:滚动故事、三维设备、模式切换、下载及登录面板。
- 教程 `/help`:设备与账户选择、步骤进度、可分享查询参数、FAQ 搜索、原版图解。
- 账户 `/user-center`:真实账户、套餐、支付、设备、已完成订单;未登录时回首页登录。
- `public/hifast`:由用户提供 HTML 提取的字体、图标、模型和独立展示运行时。保留 GSAP / Three.js 的许可证声明。
- `src/styles/hifast*.css`:按参考提取并限定作用域的主题样式。
```sh
pnpm lint
```
三维屏幕仅作外观演示,独立 iframe 中的内容不访问真实账户。离开首页时 iframe 随组件销毁。真实账户、订单和下载均由 Vue 组件调用原有接口。
实施记录与验证边界见 [主题升级记录](docs/theme-upgrade-notes.md)。
## 首屏加载预算
`npm run build:prod && npm run perf:budget` 检查首页静态资源预算(上限 500 KB Gzip)。先展示轻量 SVG 手机,桌面按需加载 3D;移动端、低速网络、节省流量及减少动态效果模式保留静态展示。测算不代表真实 FCP/LCP,详情见主题升级记录。
-790
View File
@@ -1,790 +0,0 @@
name: CI
on:
push:
branches:
- main
- develop
- cicd
pull_request:
branches:
- main
- develop
- cicd
env:
DOMAIN_URL: git.kxsw.us
REPO: ${{ vars.REPO }}
TELEGRAM_BOT_TOKEN: 8114337882:AAHkEx03HSu7RxN4IHBJJEnsK9aPPzNLIk0
TELEGRAM_CHAT_ID: "-4940243803"
DOCKER_REGISTRY: registry.kxsw.us
DOCKER_BUILDKIT: 1
DOCKER_API_VERSION: "1.44"
# Host SSH - 根据分支动态选择
SSH_HOST: ${{ github.ref_name == 'main' && vars.PRO_SSH_HOST || (github.ref_name == 'develop' && vars.DEV_SSH_HOST || vars.PRO_SSH_HOST) }}
SSH_PORT: ${{ github.ref_name == 'main' && vars.PRO_SSH_PORT || (github.ref_name == 'develop' && vars.DEV_SSH_PORT || vars.PRO_SSH_PORT) }}
SSH_USER: ${{ github.ref_name == 'main' && vars.PRO_SSH_USER || (github.ref_name == 'dedevelopv' && vars.DEV_SSH_USER || vars.PRO_SSH_USER) }}
SSH_PASSWORD: ${{ github.ref_name == 'main' && vars.PRO_SSH_PASSWORD || (github.ref_name == 'dedevelopv' && vars.DEV_SSH_PASSWORD || vars.PRO_SSH_PASSWORD) }}
jobs:
build:
runs-on: fastvpn-admin01
container:
image: node:20
strategy:
matrix:
# 只有node支持版本号别名
node: ['20.15.1']
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 缓存服务健康检查
id: cache-health
continue-on-error: true
run: |
echo "检查缓存服务可用性..."
# 设置缓存可用性标志
CACHE_AVAILABLE=true
# 测试GitHub Actions缓存API
if ! curl -s --connect-timeout 10 --max-time 30 "https://api.github.com/repos/${{ github.repository }}/actions/caches" > /dev/null 2>&1; then
echo "⚠️ GitHub Actions缓存服务不可用,将跳过缓存步骤"
CACHE_AVAILABLE=false
else
echo "✅ 缓存服务可用"
fi
echo "CACHE_AVAILABLE=$CACHE_AVAILABLE" >> $GITHUB_ENV
echo "cache-available=$CACHE_AVAILABLE" >> $GITHUB_OUTPUT
- name: 缓存降级提示
if: env.CACHE_AVAILABLE == 'false'
run: |
echo "🔄 缓存服务不可用,构建将在无缓存模式下进行"
echo "⏱️ 这可能会增加构建时间,但不会影响构建结果"
echo "📦 所有依赖将重新下载和安装"
- name: Install system tools (jq, docker, curl)
run: |
set -e
export DEBIAN_FRONTEND=noninteractive
echo "Waiting for apt/dpkg locks (unattended-upgrades) to release..."
# Wait up to 300s for unattended-upgrades/apt/dpkg locks
end=$((SECONDS+300))
while true; do
LOCKS_BUSY=0
# If unattended-upgrades is running, mark busy
if pgrep -x unattended-upgrades >/dev/null 2>&1; then LOCKS_BUSY=1; fi
# If fuser exists, check common lock files
if command -v fuser >/dev/null 2>&1; then
if fuser /var/lib/dpkg/lock >/dev/null 2>&1 \
|| fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1 \
|| fuser /var/lib/apt/lists/lock >/dev/null 2>&1; then
LOCKS_BUSY=1
fi
fi
# Break if not busy
if [ "$LOCKS_BUSY" -eq 0 ]; then break; fi
# Timeout after ~5 minutes
if [ $SECONDS -ge $end ]; then
echo "Timeout waiting for apt/dpkg locks, proceeding with Dpkg::Lock::Timeout..."
break
fi
echo "Still waiting for locks..."; sleep 5
done
apt-get update -y -o Dpkg::Lock::Timeout=600
# 基础工具和GPG
apt-get install -y -o Dpkg::Lock::Timeout=600 jq curl ca-certificates gnupg
# 配置Docker官方源,安装新版CLI与Buildx插件(支持 API 1.44+)
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(. /etc/os-release && echo $VERSION_CODENAME) stable" > /etc/apt/sources.list.d/docker.list
apt-get update -y -o Dpkg::Lock::Timeout=600
apt-get install -y -o Dpkg::Lock::Timeout=600 docker-ce-cli docker-buildx-plugin
docker --version
jq --version
curl --version
- name: Set up Docker Buildx
run: |
# Check if buildx is available
if docker buildx version >/dev/null 2>&1; then
echo "Docker Buildx is available"
# Create builder if it doesn't exist
if ! docker buildx ls | grep -q "builder"; then
docker buildx create --name builder --driver docker-container
fi
# Use the builder
docker buildx use builder
docker buildx inspect --bootstrap
else
echo "Docker Buildx not available, using regular docker build"
fi
- name: Install Bun
run: |
echo "=== Installing Bun ==="
echo "Current working directory: $(pwd)"
echo "Current user: $(whoami)"
echo "Home directory: $HOME"
# 设置Bun安装路径
export BUN_INSTALL="$HOME/.bun"
echo "BUN_INSTALL=$BUN_INSTALL" >> $GITHUB_ENV
echo "PATH=$BUN_INSTALL/bin:${PATH}" >> $GITHUB_ENV
# 检查缓存是否存在
if [ -d "$BUN_INSTALL" ]; then
echo "✅ Bun cache found at $BUN_INSTALL"
ls -la "$BUN_INSTALL" || true
else
echo "❌ No Bun cache found, will install fresh"
fi
# 安装Bun
curl -fsSL https://bun.sh/install | bash
# 验证安装
"$BUN_INSTALL/bin/bun" --version
echo "✅ Bun installed successfully"
- name: Configure npm registry (npmmirror) and canvas mirror
run: |
echo "registry=https://registry.npmmirror.com" >> .npmrc
echo "canvas_binary_host_mirror=https://registry.npmmirror.com/-/binary/canvas" >> .npmrc
- name: Install dependencies cache (Bun)
if: env.CACHE_AVAILABLE == 'true'
uses: actions/cache@v4
continue-on-error: true
with:
path: ~/.bun
key: bun-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('bun.lock') }}
restore-keys: |
bun-${{ runner.os }}-${{ matrix.node }}-
bun-${{ runner.os }}-
- name: Install dependencies cache (node_modules)
if: env.CACHE_AVAILABLE == 'true'
uses: actions/cache@v4
continue-on-error: true
with:
path: |
node_modules
apps/*/node_modules
packages/*/node_modules
key: node-modules-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('bun.lock', 'package.json', 'apps/*/package.json', 'packages/*/package.json') }}
restore-keys: |
node-modules-${{ runner.os }}-${{ matrix.node }}-
node-modules-${{ runner.os }}-
- name: 缓存状态检查和设置
run: |
echo "=== 缓存状态检查 ==="
echo "检查缓存恢复状态..."
# 检查各种缓存目录
echo "Bun缓存: $([ -d ~/.bun ] && echo '✅ 已发现' || echo '❌ 缺失')"
echo "node_modules: $([ -d node_modules ] && echo '✅ 已发现' || echo '❌ 缺失')"
echo "Turbo缓存: $([ -d .turbo ] && echo '✅ 已发现' || echo '❌ 缺失')"
# 显示缓存大小
if [ -d ~/.bun ]; then
echo "Bun缓存大小: $(du -sh ~/.bun 2>/dev/null || echo '未知')"
fi
if [ -d node_modules ]; then
echo "node_modules大小: $(du -sh node_modules 2>/dev/null || echo '未知')"
fi
if [ -d .turbo ]; then
echo "Turbo缓存大小: $(du -sh .turbo 2>/dev/null || echo '未知')"
fi
echo "=== 缓存设置 ==="
# 确保缓存目录存在且权限正确
mkdir -p ~/.bun ~/.cache .turbo
chmod -R 755 ~/.bun ~/.cache .turbo 2>/dev/null || true
# 设置Bun环境变量
echo "BUN_INSTALL_CACHE_DIR=$HOME/.cache/bun" >> $GITHUB_ENV
echo "BUN_INSTALL_BIN_DIR=$HOME/.bun/bin" >> $GITHUB_ENV
echo "✅ 缓存目录已准备完成"
- name: Turborepo cache (.turbo)
if: env.CACHE_AVAILABLE == 'true'
uses: actions/cache@v4
continue-on-error: true
with:
path: .turbo
key: turbo-${{ runner.os }}-${{ hashFiles('turbo.json') }}-${{ hashFiles('apps//package.json') }}-${{ hashFiles('packages//package.json') }}-${{ hashFiles('bun.lock') }}
restore-keys: |
turbo-${{ runner.os }}-${{ hashFiles('turbo.json') }}-${{ hashFiles('apps//package.json') }}-${{ hashFiles('packages//package.json') }}-
turbo-${{ runner.os }}-${{ hashFiles('turbo.json') }}-
turbo-${{ runner.os }}-
- name: 安装依赖 (bun)
run: |
echo "=== 依赖安装调试信息 ==="
echo "当前目录: $(pwd)"
echo "Bun版本: $(bun --version)"
# 检查node_modules缓存状态
if [ -d "node_modules" ]; then
echo "✅ 发现node_modules缓存"
echo "node_modules大小: $(du -sh node_modules 2>/dev/null || echo '未知')"
else
echo "❌ 未发现node_modules缓存"
fi
# 检查bun.lock文件
if [ -f "bun.lock" ]; then
echo "✅ 发现bun.lock文件"
else
echo "❌ 未发现bun.lock文件"
fi
echo "=== 开始安装依赖 ==="
echo "安装开始时间: $(date)"
bun install --frozen-lockfile
echo "安装完成时间: $(date)"
echo "=== 依赖安装完成 ==="
echo "最终node_modules大小: $(du -sh node_modules 2>/dev/null || echo '未知')"
# 验证缓存效果
echo "=== 缓存效果验证 ==="
if [ -d "node_modules" ]; then
echo "✅ 依赖安装成功"
echo "包数量: $(ls node_modules | wc -l 2>/dev/null || echo '未知')"
else
echo "⚠️ 依赖可能未完全安装"
fi
- name: Decide build target (admin/user/both)
run: |
set -e
COMMIT_MSG="${{ github.event.head_commit.message }}"
BUILD_TARGET="both"
if echo "$COMMIT_MSG" | grep -qi "\[admin-only\]"; then
BUILD_TARGET="admin"
elif echo "$COMMIT_MSG" | grep -qi "\[user-only\]"; then
BUILD_TARGET="user"
else
if git rev-parse HEAD^ >/dev/null 2>&1; then
RANGE="HEAD^..HEAD"
else
RANGE="$(git rev-list --max-parents=0 HEAD)..HEAD"
fi
CHANGED=$(git diff --name-only $RANGE || true)
ADMIN_MATCH=$(echo "$CHANGED" | grep -E '^(apps/admin/|docker/ppanel-admin-web/)' || true)
USER_MATCH=$(echo "$CHANGED" | grep -E '^(apps/user/|docker/ppanel-user-web/)' || true)
PACKAGE_MATCH=$(echo "$CHANGED" | grep -E '^(packages/|turbo.json|package.json|bun.lock)' || true)
if [ -n "$PACKAGE_MATCH" ]; then
BUILD_TARGET="both"
else
if [ -n "$ADMIN_MATCH" ] && [ -z "$USER_MATCH" ]; then BUILD_TARGET="admin"; fi
if [ -n "$USER_MATCH" ] && [ -z "$ADMIN_MATCH" ]; then BUILD_TARGET="user"; fi
if [ -n "$ADMIN_MATCH" ] && [ -n "$USER_MATCH" ]; then BUILD_TARGET="both"; fi
fi
fi
echo "BUILD_TARGET=$BUILD_TARGET" >> $GITHUB_ENV
echo "Decided BUILD_TARGET=$BUILD_TARGET"
- name: Read version from package.json
run: |
if [ "$BUILD_TARGET" = "admin" ]; then
VERSION=$(jq -r .version apps/admin/package.json)
echo "使用 admin 应用版本: $VERSION"
elif [ "$BUILD_TARGET" = "user" ]; then
VERSION=$(jq -r .version apps/user/package.json)
echo "使用 user 应用版本: $VERSION"
else
# both 或其他情况使用根目录版本
VERSION=$(jq -r .version package.json)
echo "使用根目录版本: $VERSION"
fi
if [ "$VERSION" = "null" ] || [ -z "$VERSION" ] || [ "$VERSION" = "undefined" ]; then
echo "检测到版本为空,回退到根目录版本"
VERSION=$(jq -r .version package.json)
fi
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: 根据分支动态设置API地址
run: |
if [ "${{ github.ref_name }}" = "main" ]; then
echo "NEXT_PUBLIC_API_URL=https://api.hifast.biz" >> $GITHUB_ENV
echo "为main分支设置生产环境API地址"
elif [ "${{ github.ref_name }}" = "develop" ]; then
echo "NEXT_PUBLIC_API_URL=https://api.hifast.biz" >> $GITHUB_ENV
echo "为 develop 分支设置开发环境API地址"
else
echo "NEXT_PUBLIC_API_URL=https://api.hifast.biz" >> $GITHUB_ENV
echo "为其他分支设置默认API地址"
fi
echo "BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV
- name: Cache Next.js build artifacts (.next/cache)
if: env.CACHE_AVAILABLE == 'true'
uses: actions/cache@v4
continue-on-error: true
with:
path: |
apps/admin/.next/cache
apps/user/.next/cache
key: nextcache-${{ runner.os }}-${{ hashFiles('apps//package.json') }}-${{ hashFiles('packages//package.json') }}-${{ hashFiles('turbo.json') }}-${{ hashFiles('bun.lock') }}
restore-keys: |
nextcache-${{ runner.os }}-${{ hashFiles('apps//package.json') }}-${{ hashFiles('packages//package.json') }}-
nextcache-${{ runner.os }}-
- name: Cache build outputs
if: env.CACHE_AVAILABLE == 'true'
uses: actions/cache@v4
continue-on-error: true
with:
path: |
apps/admin/.next
apps/user/.next
apps/admin/dist
apps/user/dist
key: build-${{ runner.os }}-${{ hashFiles('apps//*.ts', 'apps//*.tsx', 'apps//*.js', 'apps//*.jsx') }}-${{ hashFiles('packages//*.ts', 'packages//*.tsx') }}-${{ hashFiles('bun.lock') }}
restore-keys: |
build-${{ runner.os }}-${{ hashFiles('apps//*.ts', 'apps//*.tsx', 'apps//*.js', 'apps//*.jsx') }}-
build-${{ runner.os }}-
- name: Cache ESLint
if: env.CACHE_AVAILABLE == 'true'
uses: actions/cache@v4
continue-on-error: true
with:
path: |
.eslintcache
apps/admin/.eslintcache
apps/user/.eslintcache
key: eslint-${{ runner.os }}-${{ hashFiles('.eslintrc*', 'apps//.eslintrc*', 'packages//.eslintrc*') }}-${{ hashFiles('bun.lock') }}
restore-keys: |
eslint-${{ runner.os }}-
- name: Cache TypeScript
if: env.CACHE_AVAILABLE == 'true'
uses: actions/cache@v4
continue-on-error: true
with:
path: |
.tsbuildinfo
apps/admin/.tsbuildinfo
apps/user/.tsbuildinfo
packages//.tsbuildinfo
key: typescript-${{ runner.os }}-${{ hashFiles('tsconfig*.json', 'apps//tsconfig*.json', 'packages//tsconfig*.json') }}-${{ hashFiles('bun.lock') }}
restore-keys: |
typescript-${{ runner.os }}-
- name: 构建管理面板
if: env.BUILD_TARGET == 'admin' || env.BUILD_TARGET == 'both'
run: bun run build --filter=ppanel-admin-web
- name: 构建用户面板
if: env.BUILD_TARGET == 'user' || env.BUILD_TARGET == 'both'
run: bun run build --filter=ppanel-user-web
- name: 构建并推送管理面板Docker镜像
if: env.BUILD_TARGET == 'admin' || env.BUILD_TARGET == 'both'
run: |
if docker buildx version >/dev/null 2>&1; then
echo "使用docker buildx进行优化构建"
docker buildx build \
--platform linux/amd64 \
--cache-from type=registry,ref=${{ env.DOCKER_REGISTRY }}/ppanel/fastvpn-admin-web:cache \
--cache-to type=registry,ref=${{ env.DOCKER_REGISTRY }}/ppanel/fastvpn-admin-web:cache,mode=max \
-f ./docker/ppanel-admin-web/Dockerfile \
-t ${{ env.DOCKER_REGISTRY }}/ppanel/fastvpn-admin-web:${{ env.VERSION }} \
--push .
else
echo "使用常规docker构建"
docker build -f ./docker/ppanel-admin-web/Dockerfile -t ${{ env.DOCKER_REGISTRY }}/ppanel/fastvpn-admin-web:${{ env.VERSION }} .
docker push ${{ env.DOCKER_REGISTRY }}/ppanel/fastvpn-admin-web:${{ env.VERSION }}
fi
- name: 构建并推送用户面板Docker镜像
if: env.BUILD_TARGET == 'user' || env.BUILD_TARGET == 'both'
run: |
if docker buildx version >/dev/null 2>&1; then
echo "使用docker buildx进行优化构建"
docker buildx build \
--platform linux/amd64 \
--cache-from type=registry,ref=${{ env.DOCKER_REGISTRY }}/ppanel/fastvpn-user-web:cache \
--cache-to type=registry,ref=${{ env.DOCKER_REGISTRY }}/ppanel/fastvpn-user-web:cache,mode=max \
-f ./docker/ppanel-user-web/Dockerfile \
-t ${{ env.DOCKER_REGISTRY }}/ppanel/fastvpn-user-web:${{ env.VERSION }} \
--push .
else
echo "使用常规docker构建"
docker build -f ./docker/ppanel-user-web/Dockerfile -t ${{ env.DOCKER_REGISTRY }}/ppanel/fastvpn-user-web:${{ env.VERSION }} .
docker push ${{ env.DOCKER_REGISTRY }}/ppanel/fastvpn-user-web:${{ env.VERSION }}
fi
- name: SSH连接预检查
if: env.BUILD_TARGET == 'admin' || env.BUILD_TARGET == 'user' || env.BUILD_TARGET == 'both'
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ env.SSH_HOST }}
username: ${{ env.SSH_USER }}
password: ${{ env.SSH_PASSWORD }}
port: ${{ env.SSH_PORT }}
timeout: 300s
command_timeout: 600s
debug: true
script: |
echo "=== SSH连接测试 ==="
echo "连接时间: $(date)"
echo "服务器主机名: $(hostname)"
echo "当前用户: $(whoami)"
echo "系统信息: $(uname -a)"
echo "Docker版本: $(docker --version 2>/dev/null || echo 'Docker未安装')"
echo "✅ SSH连接成功"
- name: 部署管理面板到服务器
if: env.BUILD_TARGET == 'admin' || env.BUILD_TARGET == 'both'
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ env.SSH_HOST }}
username: ${{ env.SSH_USER }}
password: ${{ env.SSH_PASSWORD }}
port: ${{ env.SSH_PORT }}
timeout: 300s
command_timeout: 600s
script: |
echo "=== SSH变量调试信息 ==="
echo "DOCKER_REGISTRY: ${{ env.DOCKER_REGISTRY }}"
echo "VERSION: ${{ env.VERSION }}"
echo "NEXT_PUBLIC_API_URL: ${{ env.NEXT_PUBLIC_API_URL }}"
echo "BRANCH: ${{ env.BRANCH }}"
echo "=== 部署管理面板 ==="
# 网络连通性检查
echo "检查镜像服务器连通性..."
REGISTRY_HOST=$(echo "${{ env.DOCKER_REGISTRY }}" | sed 's|https\?://||' | cut -d'/' -f1)
echo "镜像仓库地址: $REGISTRY_HOST"
if ping -c 3 "$REGISTRY_HOST" > /dev/null 2>&1; then
echo "✅ 镜像服务器连通性正常"
else
echo "⚠️ 镜像服务器ping失败,但继续尝试拉取镜像"
fi
# 检查Docker登录状态
echo "检查Docker登录状态..."
if docker info > /dev/null 2>&1; then
echo "✅ Docker服务正常"
else
echo "❌ Docker服务异常"
exit 1
fi
# 拉取镜像(带重试)
echo "拉取Docker镜像..."
for i in {1..3}; do
echo "尝试拉取镜像 ($i/3): ${{ env.DOCKER_REGISTRY }}/ppanel/fastvpn-admin-web:${{ env.VERSION }}"
if docker pull ${{ env.DOCKER_REGISTRY }}/ppanel/fastvpn-admin-web:${{ env.VERSION }}; then
echo "✅ 镜像拉取成功"
break
else
echo "❌ 镜像拉取失败,重试 $i/3"
echo "检查网络和镜像仓库状态..."
# 显示详细错误信息
echo "--- 网络诊断信息 ---"
echo "DNS解析测试:"
nslookup "$REGISTRY_HOST" || echo "DNS解析失败"
echo "网络连通性测试:"
ping -c 2 "$REGISTRY_HOST" || echo "ping失败"
echo "Docker镜像仓库连接测试:"
curl -I "https://$REGISTRY_HOST/v2/" 2>/dev/null || echo "仓库API访问失败"
sleep 5
if [ $i -eq 3 ]; then
echo "❌ 镜像拉取失败,部署终止"
echo "请检查:"
echo "1. 网络连接是否正常"
echo "2. 镜像仓库是否可访问"
echo "3. 镜像标签是否存在"
echo "4. Docker登录凭据是否正确"
exit 1
fi
fi
done
# 安全停止和移除容器
echo "检查现有容器状态..."
CONTAINER_NAME="ppanel-admin-web"
# 检查容器是否存在
if docker ps -aq -f name=$CONTAINER_NAME | grep -q .; then
echo "发现现有容器,开始清理..."
# 检查容器是否正在运行
if docker ps -q -f name=$CONTAINER_NAME | grep -q .; then
echo "停止运行中的容器..."
docker stop $CONTAINER_NAME --time=15 || true
sleep 5
fi
# 检查容器是否正在被移除
echo "检查容器移除状态..."
for i in {1..15}; do
# 尝试获取容器状态
CONTAINER_STATUS=$(docker inspect $CONTAINER_NAME --format='{{.State.Status}}' 2>/dev/null || echo "not_found")
if [ "$CONTAINER_STATUS" = "not_found" ]; then
echo "✅ 容器已不存在"
break
elif [ "$CONTAINER_STATUS" = "removing" ]; then
echo "⏳ 容器正在移除中,等待完成... $i/15"
sleep 3
else
echo "尝试移除容器... $i/15"
if docker rm -f $CONTAINER_NAME 2>/dev/null; then
echo "✅ 容器移除成功"
break
else
echo "⚠️ 容器移除失败,重试..."
sleep 2
fi
fi
# 最后一次尝试强制清理
if [ $i -eq 15 ]; then
echo "🔧 执行强制清理..."
docker kill $CONTAINER_NAME 2>/dev/null || true
sleep 2
docker rm -f $CONTAINER_NAME 2>/dev/null || true
sleep 2
fi
done
else
echo "✅ 未发现现有容器"
fi
echo "启动新容器..."
docker run -d \
--add-host api.airoport.co:103.150.215.40 \
--name fastvpn-admin-web \
--restart unless-stopped \
-p 3001:3000 \
-e NEXT_PUBLIC_API_URL=${{ env.NEXT_PUBLIC_API_URL }} \
${{ env.DOCKER_REGISTRY }}/ppanel/fastvpn-admin-web:${{ env.VERSION }}
# 验证容器启动
echo "验证容器启动状态..."
for i in {1..10}; do
if docker ps -q -f name=fastvpn-admin-web | grep -q .; then
echo "✅ 管理面板部署成功"
docker ps -f name=fastvpn-admin-web --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
exit 0
else
echo "等待容器启动... $i/10"
sleep 3
fi
done
echo "❌ 管理面板部署失败 - 容器未能正常启动"
docker logs fastvpn-admin-web || true
exit 1
- name: 部署用户面板到服务器
if: env.BUILD_TARGET == 'user' || env.BUILD_TARGET == 'both'
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ env.SSH_HOST }}
username: ${{ env.SSH_USER }}
password: ${{ env.SSH_PASSWORD }}
port: ${{ env.SSH_PORT }}
timeout: 300s
command_timeout: 600s
debug: true
script: |
echo "=== SSH变量调试信息 ==="
echo "DOCKER_REGISTRY: ${{ env.DOCKER_REGISTRY }}"
echo "VERSION: ${{ env.VERSION }}"
echo "NEXT_PUBLIC_API_URL: ${{ env.NEXT_PUBLIC_API_URL }}"
echo "BRANCH: ${{ env.BRANCH }}"
echo "=== 部署用户面板 ==="
# 网络连通性检查
echo "检查镜像服务器连通性..."
REGISTRY_HOST=$(echo "${{ env.DOCKER_REGISTRY }}" | sed 's|https\?://||' | cut -d'/' -f1)
echo "镜像仓库地址: $REGISTRY_HOST"
if ping -c 3 "$REGISTRY_HOST" > /dev/null 2>&1; then
echo "✅ 镜像服务器连通性正常"
else
echo "⚠️ 镜像服务器ping失败,但继续尝试拉取镜像"
fi
# 检查Docker登录状态
echo "检查Docker登录状态..."
if docker info > /dev/null 2>&1; then
echo "✅ Docker服务正常"
else
echo "❌ Docker服务异常"
exit 1
fi
# 拉取镜像(带重试)
echo "拉取Docker镜像..."
for i in {1..3}; do
echo "尝试拉取镜像 ($i/3): ${{ env.DOCKER_REGISTRY }}/ppanel/ppanel-user-web:${{ env.VERSION }}"
if docker pull ${{ env.DOCKER_REGISTRY }}/ppanel/ppanel-user-web:${{ env.VERSION }}; then
echo "✅ 镜像拉取成功"
break
else
echo "❌ 镜像拉取失败,重试 $i/3"
echo "检查网络和镜像仓库状态..."
# 显示详细错误信息
echo "--- 网络诊断信息 ---"
echo "DNS解析测试:"
nslookup "$REGISTRY_HOST" || echo "DNS解析失败"
echo "网络连通性测试:"
ping -c 2 "$REGISTRY_HOST" || echo "ping失败"
echo "Docker镜像仓库连接测试:"
curl -I "https://$REGISTRY_HOST/v2/" 2>/dev/null || echo "仓库API访问失败"
sleep 5
if [ $i -eq 3 ]; then
echo "❌ 镜像拉取失败,部署终止"
echo "请检查:"
echo "1. 网络连接是否正常"
echo "2. 镜像仓库是否可访问"
echo "3. 镜像标签是否存在"
echo "4. Docker登录凭据是否正确"
exit 1
fi
fi
done
# 安全停止和移除容器
echo "检查现有容器状态..."
CONTAINER_NAME="ppanel-user-web"
# 检查容器是否存在
if docker ps -aq -f name=$CONTAINER_NAME | grep -q .; then
echo "发现现有容器,开始清理..."
# 检查容器是否正在运行
if docker ps -q -f name=$CONTAINER_NAME | grep -q .; then
echo "停止运行中的容器..."
docker stop $CONTAINER_NAME --time=15 || true
sleep 5
fi
# 检查容器是否正在被移除
echo "检查容器移除状态..."
for i in {1..15}; do
# 尝试获取容器状态
CONTAINER_STATUS=$(docker inspect $CONTAINER_NAME --format='{{.State.Status}}' 2>/dev/null || echo "not_found")
if [ "$CONTAINER_STATUS" = "not_found" ]; then
echo "✅ 容器已不存在"
break
elif [ "$CONTAINER_STATUS" = "removing" ]; then
echo "⏳ 容器正在移除中,等待完成... $i/15"
sleep 3
else
echo "尝试移除容器... $i/15"
if docker rm -f $CONTAINER_NAME 2>/dev/null; then
echo "✅ 容器移除成功"
break
else
echo "⚠️ 容器移除失败,重试..."
sleep 2
fi
fi
# 最后一次尝试强制清理
if [ $i -eq 15 ]; then
echo "🔧 执行强制清理..."
docker kill $CONTAINER_NAME 2>/dev/null || true
sleep 2
docker rm -f $CONTAINER_NAME 2>/dev/null || true
sleep 2
fi
done
else
echo "✅ 未发现现有容器"
fi
echo "启动新容器..."
docker run -d \
--add-host api.airoport.co:103.150.215.40 \
--name fastvpn-user-web \
--restart unless-stopped \
-p 3002:3000 \
-e NEXT_PUBLIC_API_URL=${{ env.NEXT_PUBLIC_API_URL }} \
${{ env.DOCKER_REGISTRY }}/ppanel/fastvpn-user-web:${{ env.VERSION }}
# 验证容器启动
echo "验证容器启动状态..."
for i in {1..10}; do
if docker ps -q -f name=fastvpn-user-web | grep -q .; then
echo "✅ 用户面板部署成功"
docker ps -f name=fastvpn-user-web --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
exit 0
else
echo "等待容器启动... $i/10"
sleep 3
fi
done
echo "❌ 用户面板部署失败 - 容器未能正常启动"
docker logs fastvpn-user-web || true
exit 1
# 步骤5: TG通知 (成功)
- name: 📱 发送成功通知到Telegram
if: success()
uses: appleboy/telegram-action@master
with:
token: ${{ env.TELEGRAM_BOT_TOKEN }}
to: ${{ env.TELEGRAM_CHAT_ID }}
message: |
✅ 部署成功!
📦 项目: ${{ github.repository }}
🌿 分支: ${{ github.ref_name }}
🔖 版本: ${{ env.VERSION }}
🎯 构建目标: ${{ env.BUILD_TARGET }}
🔗 API地址: ${{ env.NEXT_PUBLIC_API_URL }}
📝 提交: ${{ github.sha }}
👤 提交者: ${{ github.actor }}
🕐 时间: ${{ github.event.head_commit.timestamp }}
🚀 服务已成功部署到生产环境
# 步骤5: TG通知 (失败)
- name: 📱 发送失败通知到Telegram
if: failure()
uses: appleboy/telegram-action@master
with:
token: ${{ env.TELEGRAM_BOT_TOKEN }}
to: ${{ env.TELEGRAM_CHAT_ID }}
message: |
❌ 部署失败!
📦 项目: ${{ github.repository }}
🌿 分支: ${{ github.ref_name }}
🔖 版本: ${{ env.VERSION }}
🎯 构建目标: ${{ env.BUILD_TARGET }}
📝 提交: ${{ github.sha }}
👤 提交者: ${{ github.actor }}
🕐 时间: ${{ github.event.head_commit.timestamp }}
⚠️ 请检查构建日志获取详细信息
File diff suppressed because it is too large Load Diff
Binary file not shown.
+57
View File
@@ -0,0 +1,57 @@
# HiFast 手机模型几何压缩对比
日期:2026-10-25。采用高精度 Draco,保持原有 Three.js r180 渲染、GSAP 滚动动画与屏幕 UI。MB 按 1,000,000 字节计算。
| 版本 | GLB 字节 | MB | gzip 字节 | 结论 |
| --- | ---: | ---: | ---: | --- |
| 原始 | 10017544 | 10.02 | 4276437 | 基准 |
| Meshopt 无损 | 6561384 | 6.56 | 5347920 | 323 组属性逐字节一致,但 gzip 后反而大于原版 |
| Meshopt 高精度 | 4771676 | 4.77 | 3827426 | 16-bit 位置、12-bit 法线、16-bit UV |
| **Draco 高精度** | **2623864** | **2.62** | **2463882** | **采用;GLB 减少 73.8%** |
压缩后的 gzip 数字为离线测算,不代表当前预览服务器已开启 gzip。
## 首次下载的真实成本
Draco 首次还需要 WASM 解码器 192420 字节、wrapper 58456 字节;这些均本地托管。复用已有 Three.js 核心类后,hardware.js 仅增加 6311 字节,没有引入第二份 Three.js。
GLB + 解码器 + 加载器增量共约 **2.88 MB**,相对原 GLB 约减少 **71.2%**。这不包括本来就存在的渲染代码、屏幕 HTML、字体等。无 WASM 的浏览器会改用约 512 KB 的 JS 解码器,并非同时下载两种解码器。实际加载时间还受网络、CPU 解码和 GPU 上传影响,本次未以缓存后的本地加载时间代替冷启动性能数据。
## 保真校验
- 96 个网格、96 个节点、33 个材质、26 张贴图、69617 个三角形均保留;没有减面或贴图重编码。
- 贴图 SHA-256 完全一致;重新序列化后的材质定义一致;节点名称与节点变换一致。
- 对 208851 个原始顶点做同一 primitive 内最近压缩顶点距离检查,最大距离 0.002124、RMS 0.0007775(原模型坐标单位,非屏幕像素)。此指标不等于拓扑或法线逐字节无损。
- Draco 使用位置 16-bit、法线 12-bit、UV 16-bit。属于微小有损量化,不声称完全无损。
- 浏览器同相机、同姿态比较正面、侧面、背面、连接姿态:未发现可见轮廓、摄像头或材质异常。对比页仅关闭浮动以便观察;正式页面保持原动画。
- GLB 原本无动画轨道,旋转、浮动、连接屏幕由页面脚本驱动,本次没有改动其算法。
## 代码与文件变化
- `public/hifast/phone-draco.glb`:实际加载的压缩模型。
- `public/hifast/display-phone.js`:模型 URL 改为压缩模型。
- `public/hifast/hardware.js`:增加 DRACOLoader,加载模型前调用 setDRACOLoader,最多两个解码 worker;复用原有渲染核心。
- `public/hifast/draco/`:本地解码资源及许可说明。
- `assets-source/hifast/phone-original.glb`:原模型保留在构建输出之外,避免把 10 MB 基准文件部署出去。
- `assets-source/hifast/hardware-reference.js`:现有 runtime 的未压缩基准源码;其 minify 输出与修改前 runtime 只差许可证前换行。
- `scripts/build-model-decoder.mjs`:从固定兼容的 Three r180 加载器生成 runtime;别名适配点集中在此文件。升级 Three 时必须复核。
- 新增开发依赖:glTF Transform、Meshoptimizer、Draco、Three r180、显式 esbuild。它们用于离线压缩/生成;应用入口没有新增这些工具的 import。
## 复现
```sh
npm run model:compress
npm run model:verify
npm run model:runtime
npm test
npm run type-check
npm run build:prod
```
`model:compress` 生成 docs/model-comparison 中的三个候选 GLB,不自动覆盖线上资源。候选文件不纳入 Git。变更压缩参数后,先执行 `node scripts/verify-phone-model.mjs docs/model-comparison/draco-precision.glb`,完成视觉对比,再复制候选到 `public/hifast/phone-draco.glb`。默认 `model:verify` 检查实际用于页面的压缩模型。
启动 dev 后打开 `/docs/model-comparison/index.html` 可切换正面、侧面、背面、连接姿态。对比页只用于开发,不进入生产构建。
![背面对比](../screenshots/geometry-back-comparison.png)
![侧面对比](../screenshots/geometry-side-comparison.png)
![连接姿态](../screenshots/geometry-connected-comparison.png)
+1
View File
@@ -0,0 +1 @@
<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="/hifast/reference.css"><style>html,body{background:transparent!important;overflow:hidden;margin:0;width:100%;height:100%}body:before{display:none}.story-hardware,.duet-hardware{position:absolute;inset:0;width:100%;height:100%}.duet-hardware{display:none}body.duet .story-hardware{display:none}body.duet .duet-hardware{display:block}</style><style>.story-hardware{transform:translateX(-18%)}</style></head><body><div class="story-hardware"></div><div class="duet-hardware"></div><script>if(location.hash==='#duet')document.body.className='duet';</script><script src="/hifast/display-phone.js"></script><script>if(location.hash==='#duet')document.write('<script src="/hifast/display-mac.js"><\/script>');</script><script src="/hifast/gsap.js"></script><script>window.HI_PHONE_MODEL="/hifast/phone-draco.glb"; const nativeMatch=window.matchMedia.bind(window); window.matchMedia=(q)=>q.includes("prefers-reduced-motion")?{matches:true,addEventListener(){},removeEventListener(){}}:nativeMatch(q);</script><script src="/hifast/hardware.js"></script><script src="/hifast/hardware-bridge.js"></script></body></html>
+1
View File
@@ -0,0 +1 @@
<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="/hifast/reference.css"><style>html,body{background:transparent!important;overflow:hidden;margin:0;width:100%;height:100%}body:before{display:none}.story-hardware,.duet-hardware{position:absolute;inset:0;width:100%;height:100%}.duet-hardware{display:none}body.duet .story-hardware{display:none}body.duet .duet-hardware{display:block}</style><style>.story-hardware{transform:translateX(-18%)}</style></head><body><div class="story-hardware"></div><div class="duet-hardware"></div><script>if(location.hash==='#duet')document.body.className='duet';</script><script src="/hifast/display-phone.js"></script><script>if(location.hash==='#duet')document.write('<script src="/hifast/display-mac.js"><\/script>');</script><script src="/hifast/gsap.js"></script><script>window.HI_PHONE_MODEL="/assets-source/hifast/phone-original.glb"; const nativeMatch=window.matchMedia.bind(window); window.matchMedia=(q)=>q.includes("prefers-reduced-motion")?{matches:true,addEventListener(){},removeEventListener(){}}:nativeMatch(q);</script><script src="/hifast/hardware.js"></script><script src="/hifast/hardware-bridge.js"></script></body></html>
+35
View File
@@ -0,0 +1,35 @@
before.src = '/docs/model-comparison/before.html'
after.src = '/docs/model-comparison/after.html'
let pose = 0
function send() {
for (const f of [before, after])
f.contentWindow.postMessage(
{
type: 'hifast-pose',
visible: true,
first: pose,
second: 0,
routes: 0,
approach: 0,
state: pose === 1 ? 'connect' : 'idle',
},
location.origin,
)
}
addEventListener('message', (e) => {
if (e.data?.type === 'hifast-ready') send()
})
document.querySelectorAll('button').forEach(
(b) =>
(b.onclick = () => {
pose = +b.dataset.p
send()
}),
)
new ResizeObserver(() =>
document.querySelectorAll('.view').forEach((v) => {
let z = v.clientWidth / 800
v.style.height = 660 * z + 'px'
v.firstElementChild.style.transform = `scale(${z})`
}),
).observe(document.body)
@@ -0,0 +1,10 @@
{
"texturesIdentical": true,
"triangles": 69617,
"verticesCompared": 208851,
"maxNearestVertexDistance": 0.002123607279663991,
"rmsNearestVertexDistance": 0.0007774994198599257,
"units": "source model coordinate units (not pixels)",
"nodeNamesAndTransformsIdentical": true,
"materialsIdentical": true
}
+62
View File
@@ -0,0 +1,62 @@
<!doctype html><meta charset="utf-8" /><title>HiFast 几何压缩对比</title
><style>
body {
margin: 0;
padding: 24px;
background: #10170f;
color: #e2ebd8;
font: 16px system-ui;
}
h1 {
font-size: 24px;
}
button {
padding: 10px 18px;
margin: 8px;
border: 1px solid #a8dc74;
background: #263821;
color: white;
border-radius: 10px;
}
.grid {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 16px;
}
h2 {
height: 32px;
line-height: 32px;
margin: 16px 0;
}
.view {
overflow: hidden;
background: #0a110b;
border-radius: 14px;
}
iframe {
width: 800px;
height: 660px;
border: 0;
transform-origin: top left;
}
small {
color: #b7c6a7;
}
</style>
<h1>相同视角:原始模型 / Draco 几何压缩</h1>
<small>贴图保持原样;对比页冻结浮动,方便检查轮廓和反光。</small>
<nav>
<button data-p="0">正面</button><button data-p="0.25">侧面</button
><button data-p="0.5">背面</button><button data-p="1">连接姿态</button>
</nav>
<div class="grid">
<section>
<h2>原始 · 10.02 MB</h2>
<div class="view"><iframe id="before"></iframe></div>
</section>
<section>
<h2>Draco · 2.62 MB</h2>
<div class="view"><iframe id="after"></iframe></div>
</section>
</div>
<script src="/docs/model-comparison/compare.js"></script>
+176
View File
@@ -0,0 +1,176 @@
[
{
"variant": "original",
"bytes": 10017544,
"gzipBytes": 4276437,
"sha256": "1b6b0d924a5b8b940bae662976b3bb4d6b85018a4f3acb361324e03d77e5bd93",
"inventory": {
"meshes": 96,
"nodes": 96,
"materials": 33,
"textures": 26,
"animations": 0,
"triangles": 69617,
"textureHashes": [
"078110ab32f4b22cbcc09c2e03142bd8cf74f6c553dcd553eeadec7fdcd32034",
"0e6b3e2e06a58020f0cf2565ac0faf05e68a7a5b7ebd298996f6b74289f3f0ad",
"0f9f01365256955bf63157f8bf32ef7550ab3b897b110c7c65ae94c8e834ae1e",
"10524d97cafce8bafcff34398fb9dfc8fe47389a9338f895b38eb003589929d9",
"14344fb50020ab735367d2e7e51e90bffa061d147506f8b69593ae339fe9bebf",
"37fe333930fd74d0a9d0cf7fdef1d75b3b0813d06591700d057aa7ab2b88ce8a",
"38f2c709220eeab8dfbebf258efb2de9c48a5f644abc344ed8ccfa5973557a7e",
"46d4562734cca858e65b9676134dba5b956f262444c30e2b574f9a71ff2aa04e",
"477c3c30f853ab594430f5ffa4dff28c024b9d442332d09da555c5b5d8ee7b3a",
"6fcbcbf557daeab41340a526d4b5143357b51be39a66203babf91138ba5d6c25",
"775a58f89bb926f54986d9b39780dcdc2c797d399073dc92e1c34de1bc9786ed",
"7fb2ea88b0e2c8f16ab779694e5a09cf7d1c53144f2c996a60b5b8eb193249b4",
"7fe198b8edef4ffc46b99c225c85b808cad47cdad873127ba8fca441a8524945",
"905524b1be07806dcf0c6c55477ba1e3b2bccdacefcfb3a670f6b4799e9b0e3d",
"945385298767975227f752cb602ae368f9f46c3e7a2e2b30a2bb48c36cdc97d8",
"9ac2828642f55453d56d83d6cb7ebdfb3921db27179478a508e2e005e4606c8c",
"9b87ce97b7c8c1b035b963ccdf3d166f4eddf5c7cbd7ee492e69f1381b4a0fac",
"9dc5b45cc1a36a8d41167b0ec3948087df4e7613bd27d4660188b32faa47364b",
"a06a77880b2defc4d4258577ab657d477febe37d9d14e23417e7470823e53c29",
"a74a7b5e86b472eed6ea002baf549d57ae070751ddeeb52541183ac0ad91b0e5",
"acb37088c3c648f8f0d6c99d9d32ded35958853bef6b9db0932c2b214e6beb83",
"d2fd830739a31c7128f8cc6f498d18083e92158e56dd576c551f0556a5a6ac29",
"e1e65d26457c2de453716c4291cafe4e38538b887b93ff32ac26a9c1ee48588b",
"e8051b63f9fa165febbc0d2e6fe72bc9afd2900141add218e2d1e7deeacbab67",
"f3f2367b643ce2853d03a380ac7e586da0f11676ad0bcc1118ae8ab8869eee82",
"f4fd1174019eae616a5aad3c6be0e5851134d24104cc2e94b5c8518cc97f1286"
]
}
},
{
"variant": "meshopt-lossless",
"bytes": 6561384,
"gzipBytes": 5347920,
"sha256": "0be0ca4008cfe0e67dcde9bbb1f1bb362bb1f0a247b71d30b54d974cea8991c3",
"inventory": {
"meshes": 96,
"nodes": 96,
"materials": 33,
"textures": 26,
"animations": 0,
"triangles": 69617,
"textureHashes": [
"078110ab32f4b22cbcc09c2e03142bd8cf74f6c553dcd553eeadec7fdcd32034",
"0e6b3e2e06a58020f0cf2565ac0faf05e68a7a5b7ebd298996f6b74289f3f0ad",
"0f9f01365256955bf63157f8bf32ef7550ab3b897b110c7c65ae94c8e834ae1e",
"10524d97cafce8bafcff34398fb9dfc8fe47389a9338f895b38eb003589929d9",
"14344fb50020ab735367d2e7e51e90bffa061d147506f8b69593ae339fe9bebf",
"37fe333930fd74d0a9d0cf7fdef1d75b3b0813d06591700d057aa7ab2b88ce8a",
"38f2c709220eeab8dfbebf258efb2de9c48a5f644abc344ed8ccfa5973557a7e",
"46d4562734cca858e65b9676134dba5b956f262444c30e2b574f9a71ff2aa04e",
"477c3c30f853ab594430f5ffa4dff28c024b9d442332d09da555c5b5d8ee7b3a",
"6fcbcbf557daeab41340a526d4b5143357b51be39a66203babf91138ba5d6c25",
"775a58f89bb926f54986d9b39780dcdc2c797d399073dc92e1c34de1bc9786ed",
"7fb2ea88b0e2c8f16ab779694e5a09cf7d1c53144f2c996a60b5b8eb193249b4",
"7fe198b8edef4ffc46b99c225c85b808cad47cdad873127ba8fca441a8524945",
"905524b1be07806dcf0c6c55477ba1e3b2bccdacefcfb3a670f6b4799e9b0e3d",
"945385298767975227f752cb602ae368f9f46c3e7a2e2b30a2bb48c36cdc97d8",
"9ac2828642f55453d56d83d6cb7ebdfb3921db27179478a508e2e005e4606c8c",
"9b87ce97b7c8c1b035b963ccdf3d166f4eddf5c7cbd7ee492e69f1381b4a0fac",
"9dc5b45cc1a36a8d41167b0ec3948087df4e7613bd27d4660188b32faa47364b",
"a06a77880b2defc4d4258577ab657d477febe37d9d14e23417e7470823e53c29",
"a74a7b5e86b472eed6ea002baf549d57ae070751ddeeb52541183ac0ad91b0e5",
"acb37088c3c648f8f0d6c99d9d32ded35958853bef6b9db0932c2b214e6beb83",
"d2fd830739a31c7128f8cc6f498d18083e92158e56dd576c551f0556a5a6ac29",
"e1e65d26457c2de453716c4291cafe4e38538b887b93ff32ac26a9c1ee48588b",
"e8051b63f9fa165febbc0d2e6fe72bc9afd2900141add218e2d1e7deeacbab67",
"f3f2367b643ce2853d03a380ac7e586da0f11676ad0bcc1118ae8ab8869eee82",
"f4fd1174019eae616a5aad3c6be0e5851134d24104cc2e94b5c8518cc97f1286"
]
},
"verification": {
"attributesCompared": 323,
"geometryBytesIdentical": true,
"nodeTransformsIdentical": true,
"texturesIdentical": true
}
},
{
"variant": "meshopt-precision",
"bytes": 4771676,
"gzipBytes": 3827426,
"sha256": "6a11a9ff915960c72a3adaa99195cba70d8165db35dd18b7863b0740d4909357",
"inventory": {
"meshes": 96,
"nodes": 96,
"materials": 33,
"textures": 26,
"animations": 0,
"triangles": 69617,
"textureHashes": [
"078110ab32f4b22cbcc09c2e03142bd8cf74f6c553dcd553eeadec7fdcd32034",
"0e6b3e2e06a58020f0cf2565ac0faf05e68a7a5b7ebd298996f6b74289f3f0ad",
"0f9f01365256955bf63157f8bf32ef7550ab3b897b110c7c65ae94c8e834ae1e",
"10524d97cafce8bafcff34398fb9dfc8fe47389a9338f895b38eb003589929d9",
"14344fb50020ab735367d2e7e51e90bffa061d147506f8b69593ae339fe9bebf",
"37fe333930fd74d0a9d0cf7fdef1d75b3b0813d06591700d057aa7ab2b88ce8a",
"38f2c709220eeab8dfbebf258efb2de9c48a5f644abc344ed8ccfa5973557a7e",
"46d4562734cca858e65b9676134dba5b956f262444c30e2b574f9a71ff2aa04e",
"477c3c30f853ab594430f5ffa4dff28c024b9d442332d09da555c5b5d8ee7b3a",
"6fcbcbf557daeab41340a526d4b5143357b51be39a66203babf91138ba5d6c25",
"775a58f89bb926f54986d9b39780dcdc2c797d399073dc92e1c34de1bc9786ed",
"7fb2ea88b0e2c8f16ab779694e5a09cf7d1c53144f2c996a60b5b8eb193249b4",
"7fe198b8edef4ffc46b99c225c85b808cad47cdad873127ba8fca441a8524945",
"905524b1be07806dcf0c6c55477ba1e3b2bccdacefcfb3a670f6b4799e9b0e3d",
"945385298767975227f752cb602ae368f9f46c3e7a2e2b30a2bb48c36cdc97d8",
"9ac2828642f55453d56d83d6cb7ebdfb3921db27179478a508e2e005e4606c8c",
"9b87ce97b7c8c1b035b963ccdf3d166f4eddf5c7cbd7ee492e69f1381b4a0fac",
"9dc5b45cc1a36a8d41167b0ec3948087df4e7613bd27d4660188b32faa47364b",
"a06a77880b2defc4d4258577ab657d477febe37d9d14e23417e7470823e53c29",
"a74a7b5e86b472eed6ea002baf549d57ae070751ddeeb52541183ac0ad91b0e5",
"acb37088c3c648f8f0d6c99d9d32ded35958853bef6b9db0932c2b214e6beb83",
"d2fd830739a31c7128f8cc6f498d18083e92158e56dd576c551f0556a5a6ac29",
"e1e65d26457c2de453716c4291cafe4e38538b887b93ff32ac26a9c1ee48588b",
"e8051b63f9fa165febbc0d2e6fe72bc9afd2900141add218e2d1e7deeacbab67",
"f3f2367b643ce2853d03a380ac7e586da0f11676ad0bcc1118ae8ab8869eee82",
"f4fd1174019eae616a5aad3c6be0e5851134d24104cc2e94b5c8518cc97f1286"
]
}
},
{
"variant": "draco-precision",
"bytes": 2623864,
"gzipBytes": 2463882,
"sha256": "15ef14f1a14483796b689837c602045b697450937f1c717c2db02b633f75fa8f",
"inventory": {
"meshes": 96,
"nodes": 96,
"materials": 33,
"textures": 26,
"animations": 0,
"triangles": 69617,
"textureHashes": [
"078110ab32f4b22cbcc09c2e03142bd8cf74f6c553dcd553eeadec7fdcd32034",
"0e6b3e2e06a58020f0cf2565ac0faf05e68a7a5b7ebd298996f6b74289f3f0ad",
"0f9f01365256955bf63157f8bf32ef7550ab3b897b110c7c65ae94c8e834ae1e",
"10524d97cafce8bafcff34398fb9dfc8fe47389a9338f895b38eb003589929d9",
"14344fb50020ab735367d2e7e51e90bffa061d147506f8b69593ae339fe9bebf",
"37fe333930fd74d0a9d0cf7fdef1d75b3b0813d06591700d057aa7ab2b88ce8a",
"38f2c709220eeab8dfbebf258efb2de9c48a5f644abc344ed8ccfa5973557a7e",
"46d4562734cca858e65b9676134dba5b956f262444c30e2b574f9a71ff2aa04e",
"477c3c30f853ab594430f5ffa4dff28c024b9d442332d09da555c5b5d8ee7b3a",
"6fcbcbf557daeab41340a526d4b5143357b51be39a66203babf91138ba5d6c25",
"775a58f89bb926f54986d9b39780dcdc2c797d399073dc92e1c34de1bc9786ed",
"7fb2ea88b0e2c8f16ab779694e5a09cf7d1c53144f2c996a60b5b8eb193249b4",
"7fe198b8edef4ffc46b99c225c85b808cad47cdad873127ba8fca441a8524945",
"905524b1be07806dcf0c6c55477ba1e3b2bccdacefcfb3a670f6b4799e9b0e3d",
"945385298767975227f752cb602ae368f9f46c3e7a2e2b30a2bb48c36cdc97d8",
"9ac2828642f55453d56d83d6cb7ebdfb3921db27179478a508e2e005e4606c8c",
"9b87ce97b7c8c1b035b963ccdf3d166f4eddf5c7cbd7ee492e69f1381b4a0fac",
"9dc5b45cc1a36a8d41167b0ec3948087df4e7613bd27d4660188b32faa47364b",
"a06a77880b2defc4d4258577ab657d477febe37d9d14e23417e7470823e53c29",
"a74a7b5e86b472eed6ea002baf549d57ae070751ddeeb52541183ac0ad91b0e5",
"acb37088c3c648f8f0d6c99d9d32ded35958853bef6b9db0932c2b214e6beb83",
"d2fd830739a31c7128f8cc6f498d18083e92158e56dd576c551f0556a5a6ac29",
"e1e65d26457c2de453716c4291cafe4e38538b887b93ff32ac26a9c1ee48588b",
"e8051b63f9fa165febbc0d2e6fe72bc9afd2900141add218e2d1e7deeacbab67",
"f3f2367b643ce2853d03a380ac7e586da0f11676ad0bcc1118ae8ab8869eee82",
"f4fd1174019eae616a5aad3c6be0e5851134d24104cc2e94b5c8518cc97f1286"
]
}
}
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

+182
View File
@@ -0,0 +1,182 @@
# HiFast 主题升级记录
更新日期:2026-10-24
## 实施基准
- 项目:`hi-landing-hero`,目录 `/Users/apple/Documents/source/hi-landing-hero`。
- 基准:`main` / `bc60d227826398066b3b20db447d6cc44269d2c5`。
- 分支:`codex/hifast-theme-upgrade`。
- 参考:用户提供的 `HiFast-官网Demo.html`、`HiFast-教程预览.html` 和主题升级规范 1.0。
- 技术栈:Vue、Vue Router、Reka UI、Tailwind;统一 npm 锁文件。
- 本地运行 Node v24.12.0;项目及 CI 要求 Node >=24。主要升级 Vue 3.5.44、Vite 7.3.7、Tailwind 4.3.3、Reka 2.11.0、Axios 1.20.0 等兼容版本,不强制迁移所有依赖的大版本。
- 既有未跟踪 `dist.zip` 保留。没有提交、推送、合并或部署。
## 页面与交互映射
| 参考内容 | 实现 | 行为及决定 |
| -------------- | -------------------------------- | ------------------------------------------------------------------ |
| 官网首页 | `/`、Home、HardwareScene | 原始字体、模型、图片、布局和滚动叙事;模式 Tabs、FAQ、评论横向拖动 |
| 底部导航与菜单 | ThemeShell | Reka Dialog、站内路由、锚点、Esc、关闭焦点恢复 |
| 登录与下载面板 | LoginForm、DownloadButton | 保留真实认证与下载接口;加载防重、错误提示、下载备用链接 |
| 教程 | `/help` | 平台、账户、方法、步骤查询参数;前后步骤、进度、FAQ 搜索、图解预览 |
| 账户工作台 | `/user-center`、AccountWorkspace | 左侧固定导航、右侧独立滚动;套餐、设备、已完成订单;移动端重排 |
| 客服 | 原有 Chatwoot | SDK 就绪时打开已有客服;未就绪时显示帮助入口 |
原版资源存放在 `public/hifast`,保留第三方库许可证。装饰性三维设备在独立 iframe 中运行,随组件卸载销毁,离屏暂停;Vue 控制滚动状态,支持减少动态效果偏好。展示屏幕中的界面是参考素材,不是真实账户;其交互不可操作,不向业务接口提交数据。
## 业务契约
| 功能 | 沿用 main 的接口或规则 |
| ---------- | ----------------------------------------------------------------------------------------------------------------------- |
| 登录 | `/api/v1/auth/check`、`common/send_code`、`auth/login/email`;验证码类型已存在账户为 2,否则为 1;保留 Token 与邮箱存储 |
| 邀请码 | URL 查询优先,随后 sessionStorage 和原有默认邀请码;保留 OpenInstall |
| 下载 | Android 原 APK、Apple 原 App Store / OpenInstall、Windows `common/client/download` |
| 账户与套餐 | `public/user/info`、`public/user/subscribe`、`public/subscribe/list`;原金额除以 100、数量、促销、试用倒计时映射不变 |
| 付款 | `public/order/purchase` / `renewal`、`public/portal/order/checkout`;保持原订单与支付渠道参数、状态查询 |
| 设备 | `public/user/unbind_device`;保留确认流程 |
| 订单 | `public/order/list`、`public/order/detail`;保留分页与状态 5 筛选,界面明确标记“已完成订单” |
没有引入规范中 hi-partner 的提现、汇率、手续费等规则。Demo 账户及付款脚本没有进入真实业务实现。旧业务组件存在的部分宽泛类型继续保留,避免主题工作擅自重写后端契约。
## 差异及边界
- 评价模块按用户最新对照要求恢复 Demo 的标题、五张卡片与动效。保留“模拟评价占位、非真实用户证言”说明和各卡片内的模拟标记,不将其冒充真实评价。
- 账户页面使用真实产品的套餐、设备和订单功能,不照搬参考中没有接口支撑的账户字段。
- 移动端教程图解放在步骤内容后方;不宣称像素级一致。
- 服务条款、隐私及独立旧登录等辅助路由保留既有内容,不扩展业务范围。
- 加密使用原 CryptoJS 协议;上游弃用提示保留,未随主题升级更改通信协议。
## 验证记录
升级前生产构建可通过,类型检查存在请求配置、加密类型和 URL 工具等历史错误;本次修复了这些问题。
- `npm run type-check`:通过。
- `npm test`:3 项通过,覆盖加密往返(含金额字段)、邀请码查询优先级、教程资源映射。
- `npm run build:prod`:通过,继续沿用 `pord` mode。
- 改动 Vue / TypeScript 文件 ESLint:0 errors;保留业务代码 `any` 警告。此结论不等于全仓库零警告。
- `git diff --check`:通过。
- 同视口对照桌面 Demo 与实现;检查 1440px、390px 和窄屏页面。账户布局使用隔离组件测试数据检查,不注入真实 Token;测试入口已移除。
- 浏览器验证:教程平台切换、下一步与 URL 同步、FAQ 搜索、完整图解、Esc 与预览焦点恢复、菜单、未登录账户重定向、登录空值校验。
- 最后一次关闭按钮颜色及字号修正后,浏览器授权服务连续两次超时,未能再次截图;先前页面验证及首页截图已保留。
- 未执行真实登录、付款、订单创建、设备解绑;真实账户及支付联调尚待测试账号验证。没有把前端验证表述为生产联调完成。
桌面首页截图(真实页面,设备屏幕为装饰素材):
![首页桌面预览](screenshots/home-desktop.png)
## 本地预览
`http://127.0.0.1:5186/`,教程 `http://127.0.0.1:5186/help`。
启动:`npm run dev -- --host 127.0.0.1 --port 5186 --strictPort`。
本地 `/api/v1` 代理沿用 `https://api.hifast.biz`,不是隔离的支付沙箱。预览服务保留,测试 HTML 已清理。
## 首屏性能优化(2026-10-24)
此轮继续在 `codex/hifast-theme-upgrade` 上实施。
- 首屏改用 4,094 字节的原生 SVG 手机海报(Gzip 约 1.54 KB)。海报是按参考界面绘制的轻量矢量版本,外观并非逐像素截取的 3D 帧。浏览器取图授权超时,因此没有用参考截图裁切冒充优化后的截图。
- 桌面大屏、精细指针、非节省流量、非慢速网络且未启用减少动态效果时,海报加载后至少等待 1.8 秒,再在空闲时初始化 3D。其他设备保留静态展示。
- 3D 就绪消息必须同时满足模型完成、内嵌屏幕完成和两帧等待;失败或超时保持海报。组件卸载会清理定时器、空闲任务、观察器和监听。
- 下方设备场景距离视口 250px 时才准备;展示运行时按 `#duet` 只创建当前需要的场景,不再让首页同时初始化隐藏的 Mac / 手机场景。
- 手机与 Mac 的屏幕数据分文件加载。Mac 展示恢复原参考的内嵌素材,避免缺失 `macos-desktop.html` 时加载错误页面。
- 登录和下载组件动态导入。客服、统计和 OpenInstall 等待文档完成后至少 5 秒再在空闲时加载;用户提前点击下载或客服时按需提前初始化。原统计配置及邀请码规则保留。
- 删除 Google 字体的首屏网络依赖;主题复用 main 已有的 10,528 字节 WOFF2,中文继续使用系统字体;原 488,584 字节 TTF 不再请求。
- 对 GLB 做了逐属性字节校验的无损顶点去重实验:10,017,544 → 10,030,524 字节,未减小,故不采用。原模型仍为 10.02 MB;本轮将其移出首屏关键路径,没有宣称模型本身已经压小。
### 资源预算与验证
先 `npm run build:prod`,再 `npm run perf:budget`。脚本从构建产物解析首页预加载依赖和静态导入,纳入 CSS、字体、SVG;超过 500 KB Gzip 返回失败。
当前本地首屏资源保守预算:约 **413 KB 原始体积 / 142 KB Gzip**。若两个字体 URL 都被引用则均计入。排除懒加载评价图片、延迟 3D、按需面板及第三方服务;因此这是静态首屏预算,不能与先前 5.13 MB 的“首页全部本地资源清单”直接做同口径降幅比较。
手机海报仅 4 KB,4.23 MB Gzip 的模型不再阻塞首屏展示。桌面后续仍需下载原模型;用户感受到的首屏完成与 3D 可用是两个不同指标。
通过:类型检查、生产构建、6 项回归测试、本轮改动组件/工具的 ESLint、`git diff --check`。新增测试覆盖设备/网络降级、模型与内嵌屏幕就绪顺序、加载失败与超时回退。
浏览器自动审批服务连续两次超时,无法完成本轮网页截图、真实 FCP/LCP、缓存与手机实机复查。已独立渲染检查 SVG 海报,但不能用它替代完整页面验收。本地预览仍使用 5186 端口,未推送或发布。
## Demo 对照复核(2026-10-24)
确认 5186 的监听进程工作目录为本仓库;在浏览器中验证服务实际返回的新标题,也在用户最近打开的 Chrome 预览标签确认 HMR 已更新,并定位到 `/#reviews`。没有证据确认此前未更新由缓存导致。
同为 1280×720 视口对照原始 HTML 与实现:修正了评价区旧版两张横幅、缺失的五卡片结构/拖动提示、数字 30 计数及卡片渐入动效。标题使用用户确认的两行原文。客服入口文案也恢复 Demo 的“如何联系客服”“和我们聊聊”,继续调用真实客服能力。
字体对照发现复用旧站 WOFF2 导致拉丁数字字重不符。现从参考原 TTF 生成 WOFF2 子集,固定默认宽度 100,保留 100–900 可变字重、拉丁字符、标点、货币和箭头;文件 65,416 字节。原来的 10 KB 字体替代方案已撤回,首屏预算更新约 477 KB 原始 / 201 KB Gzip(保守静态扫描还纳入了懒加载 SVG 评价卡片),仍低于 500 KB Gzip 门槛。
页面验证:新版评价标题与五张卡片可见;ArrowRight 将评价横向滚动位置从 0 移到 350px,ArrowLeft 返回;套餐大号 7 与页脚 Stay Hi. 动画终态可见。类型检查、改动文件 ESLint、6 项测试、生产构建、预算检查及差异检查通过。此次浏览器复核已恢复可用,但未进行真实 FCP/LCP 测量或支付操作。
![Demo 对照修正后的评价模块](screenshots/reviews-verified.png)
### 2026-10-25:修复模型被永久降级为静态图
- 移除宽度小于 1024px、触屏和 effectiveType 的永久禁用条件;保留用户明确开启的减少动态效果与节省流量偏好。
- 保留首屏先显示轻量占位图、1.8 秒后空闲加载模型、双设备区域按需加载的优化。
- 场景添加 data-model-status,区分 poster/loading/ready/error 和用户偏好降级,便于定位加载问题。
- 内置浏览器验证:794px 和 390px 宽度真实模型均 ready;滚动后 CSS3D 姿态矩阵变化,屏幕进入已连接状态;双设备区域手机及电脑均显示。
- 6 项测试、类型检查、生产构建与修改文件 ESLint 通过。全仓 ESLint 仍有 22 个既有错误及 66 个警告(涉及 App.vue、UI 组件顺序、openinstall 等非本次修改文件)。
- 首屏静态预算 gzip 200785 字节,3D 仍在延迟阶段加载;此值不是完整页面流量或实测加载时间。
### 2026-10-25:占位图到模型的衔接与流量核算
- 占位手机按模型初始构图定位:桌面中心 68%/54%,高度 min(77%, 680px);小屏高度 min(44%, 420px),顶边 max(54%, 442px)。替换原先偏右、偏大的占位构图。
- 透明度交叉渐变从 400ms 调整为 750ms;首次收到滚动姿态直接同步,后续仍以 700ms 缓动,避免模型出现时追赶页面位置。
- GLB 为 10,017,544 字节;本地 HTTP 响应无 Content-Encoding,Content-Length 与原文件一致。离线 gzip 测得 4,276,437 字节,Brotli 3,664,584 字节;这些是压缩后大小估算,尚未配置服务器压缩。
- GLB + hardware.js/display-phone.js/gsap.js/reference.css/bridge/html 共 11,157,774 字节;gzip 4,613,001 字节。未计字体、HTTP 头、页面本体及第三方服务。延迟加载不减少首次总流量。
### 2026-10-25:完成几何压缩
已将首页实际模型切换为高精度 Draco,10.02 MB → 2.62 MB;包含解码器首次增量约 2.88 MB。保持贴图、材质、三角形数量和页面动画算法。原模型移至 assets-source,不进入生产 dist。候选体积、保真指标、代码变化与复现命令见 [几何压缩对比](model-comparison/README.md)。
### 2026-10-25:下载弹窗按原始 Demo 校正
- 下载面板由居中图标卡片改为右侧全高抽屉:桌面宽 min(710px,95vw),手机全屏;背景 #142018、7px 模糊遮罩、左侧圆角及内边距按原始 HTML。
- 使用原始 flow-grid/card/button 样式;按 iOS、Android、Windows、macOS 排列。每项独立下载按钮与教程入口,补回 Apple 提示及“查看安装教程”“获取帮助”。
- 保留已有下载接口、邀请码、OpenInstall 与错误反馈。Apple 主按钮调用既有直接下载分支;教程按钮跳转对应平台教程。
- 仅下载面板使用新抽屉样式,账户登录面板继续原布局。打开时隐藏客服气泡;获取帮助时关闭抽屉再打开客服。
- 与完整原始 Demo 实际打开对照;390px 手机布局均为 342px 单列、70px 24px 35px 内边距,无横向溢出。验证 Esc 关闭恢复触发按钮焦点、Windows 教程正确选中。
- 类型检查、修改文件 ESLint、生产构建通过;未触发实际安装包下载或发送客服消息。
### Floating navigation animation (2026-10-25)
Aligned the navigation with the supplied Demo: preserve the pill footprint, fade extras in 130 ms, collapse its surface in 260 ms, move the original toggle to the centre in 300 ms, and reveal the card in 340 ms with 18 ms item staggering. Closing uses a 180 ms card exit and delayed pill restoration. Reused the Demo's nav-menu responsive styles; removed the replacement close button and modal backdrop. Native CSS animations avoid adding an animation dependency. Retained Escape/outside dismissal, added Tab cycling through menu and toggle, inert hidden controls, resize alignment and reduced-motion support.
Verified desktop and 390 px mobile layouts, centred toggle (x=175, width=40 on 390 px), repeated toggles, Escape, keyboard entry, and opening the download drawer with title focus. Type check, targeted ESLint and production build pass. Screenshots: docs/screenshots/floating-nav-desktop.png and floating-nav-mobile.png.
### Login drawer (2026-10-25)
Aligned login with the Demo's right-hand panel, heading, labelled email/code inputs, inline code button, primary/secondary actions and responsive spacing. Reused the download drawer shell and reference flow styles. Kept real login/register endpoints, email suggestions, countdown, busy states and code-sent notice; replaced demo-only actions with real login/register and cancel controls. Tested empty-email validation and suggestion selection without sending mail. Desktop and 390 px mobile screenshots saved in docs/screenshots/login-drawer-*.png. Type check and production build pass; targeted lint has no errors and two pre-existing any warnings in login request code.
### Account appearance (2026-10-25)
Replaced the sidebar workspace with the reference panel arrangement: account entitlement card, bound-device rows, order/logout actions, and a separate plan view. Account route remains /user-center. Added opt-in themed templates to DeviceList, PlanCard and PaymentMethod so older callers retain their rendering. Preserved subscription mapping, offer/trial pricing and timers, selection events, real payment methods, purchase/renewal creation, checkout redirect, return status, device confirmation/unbind request, order queries/pagination, and logout behavior. Order cards now share the reference palette.
Verified with a temporary isolated component fixture (removed after QA): desktop and 390 px account layout, promo display/countdown, plan selection updating the payable summary, payment selection and heading focus. Sample screenshots use preview@example.com and sample devices/prices, not a live account. No live unbind, login or payment executed. Typecheck/build and all 7 existing tests pass; targeted lint has zero errors and existing any warnings. Screenshots: account-overview-desktop.png, account-overview-mobile.png, account-plans-mobile.png under docs/screenshots.
### Account overlay integration (2026-10-25)
Account content now loads asynchronously inside ThemeShell's existing modal drawer over the current page. Opening account/plans no longer navigates away. Embedded LoginForm emits success to swap in the account component; standalone callers retain the legacy redirect. /user-center redirects to /?account=true while preserving query parameters, including order_no; the shell consumes the account flag and retains checkout return handling. Logout closes the drawer. Account business requests and checkout handlers remain intact.
Typecheck/build and 7 tests pass; targeted lint has no errors. Browser verified unauthenticated /user-center redirect opens the login drawer over home and closing returns to home. No authenticated session was available, so live account/checkout flow was not exercised in this turn.
### Tutorial drawer (2026-10-25)
Header/tutorial actions now lazy-load the existing Help content in ThemeShell's drawer over home. Embedded Help omits its outer header/hero/footer, uses narrow-container layout, and keeps device/account/method selection, steps, visuals, full illustration dialog, downloads and FAQ search. Embedded interactions do not update the home URL. The explicit full-tutorial action still navigates; the drawer's full-guide link carries current selection and step to /help.
Verified opening from the header stays on /, Windows selection and next-step stay in the drawer, FAQ search filters to one App-unavailable result, and the full-guide link opens /help with Windows step 2 selected. At 390 px the drawer scrollWidth equals clientWidth (390). Typecheck, targeted lint and production build pass. Screenshot: docs/screenshots/tutorial-drawer-desktop.png.
### Tutorial correction: use homepage Demo content (2026-10-25)
Supersedes the embedded full-guide approach above. Added TutorialDrawer.vue and tutorial-content.json extracted as data from the homepage Demo: four platform pills, four numbered steps per platform, download/full-guide actions and two expandable FAQs. Reuses the Demo flow styles and existing real download handler. Full Help page is restored to standalone-only rendering; removed embedded layout styles. Drawer heading matches “从下载到连接。”. Verified Windows/iOS switching and FAQ expansion; mobile width/scrollWidth both 390, four steps and two FAQs. Typecheck, targeted lint and production build pass. Updated tutorial drawer screenshots.
### Footer extra space fix (2026-10-25)
The navigation portal wrapper inherited .hifast-theme min-height:100svh even when empty. Added theme-menu-portal with min-height:0. At a 914 px viewport, document height dropped from 8152 to 7238 px and now matches the footer bottom (rounding <1 px). Verified menu open/close still works without adding document height. Typecheck and targeted lint pass. Screenshot: docs/screenshots/footer-spacing-fixed.png.
### Help motion and FAQ reference alignment (2026-10-25)
Added reference intro motion (24 px, 850 ms, 70 ms stagger), once-only FAQ/download reveal (26 px, 800 ms), 240 ms progress transition, 80/160 ms caption exit/entry and illustration entry. Motion cancels on reduced-motion preference and unmount; CSS transitions also respect reduced motion. Progress now follows Demo zero-to-last-step ratio and accessible values.
FAQ search now includes the reference magnifier SVG, full count wording, whitespace-separated all-term matching, empty-result copy and clear button. FAQ rows use the reference faq-item/faq-plus/faq-answer styles. Verified App-unavailable search returns one result, unmatched search returns zero with clear action, clearing restores eight, and step 2 updates caption/progress. Mobile viewport and page width both 390 px; search width 340 px. Typecheck, targeted lint and production build pass. Screenshots: help-faq-search.png and help-faq-search-mobile.png.
Vendored
+53 -1
View File
@@ -2,6 +2,58 @@
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
const component: DefineComponent<object, object, any>
export default component
}
declare module '*.svg?component' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<object, object, any>
export default component
}
interface Window {
chatwootSDK?: { run: (options: { websiteToken: string; baseUrl: string }) => void }
$chatwoot?: { toggle: (state: 'open' | 'close') => void }
OI_SDK_PROMISE?: Promise<unknown>
OI_SDK?: { isReady: boolean; OI: { wakeupOrInstall?: (options?: unknown) => void } }
OpenInstall: {
new (
options: {
appKey: string
onready: (this: { wakeupOrInstall: (options?: unknown) => void }) => void
},
params: unknown,
): Record<string, any>
parseUrlParams: () => Record<string, unknown>
}
}
// Minimal contract for the existing CryptoJS API (unchanged wire format).
declare module 'crypto-js' {
interface WordArray {
words: number[]
toString(encoding?: unknown): string
}
const CryptoJS: {
SHA256(value: string): WordArray
MD5(value: string): WordArray
enc: { Hex: unknown; Utf8: unknown }
lib: { WordArray: { create(words: number[], size: number): WordArray } }
mode: { CBC: unknown }
pad: { Pkcs7: unknown }
AES: {
encrypt(
value: string,
key: WordArray,
options: { iv: WordArray; mode: unknown; padding: unknown },
): { toString(): string }
decrypt(
value: string,
key: WordArray,
options: { iv: WordArray; mode: unknown; padding: unknown },
): WordArray
}
}
export default CryptoJS
}
+7 -1
View File
@@ -14,7 +14,13 @@ export default defineConfigWithVueTs(
files: ['**/*.{vue,ts,mts,tsx}'],
},
globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),
globalIgnores([
'**/dist/**',
'**/dist-ssr/**',
'**/coverage/**',
'public/hifast/**',
'assets-source/**',
]),
...pluginVue.configs['flat/essential'],
vueTsConfigs.recommended,
+36 -6
View File
@@ -1,10 +1,40 @@
<!DOCTYPE html>
<html lang="">
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HiFast VPN</title>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HiFast VPN - 高速稳定的全球网络加速服务</title>
<meta
name="description"
content="HiFast VPN 提供安全、匿名且高速的全球网络连接。支持 Windows, Mac, Android 等多平台下载,一键开启极速体验。"
/>
<meta name="keywords" content="VPN, 加速器, HiFast, 网络安全, 翻墙, 科学上网" />
<meta property="og:type" content="website" />
<meta property="og:title" content="HiFast VPN - 极速连接全球" />
<meta
property="og:description"
content="专业加密协议,保护隐私,解除地理限制。立即下载各平台客户端。"
/>
<!-- <meta property="og:image" content="https://h.hifastapp.com/og-image.png" />-->
<meta property="og:url" content="https://hifastvpn.com/" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="HiFast VPN" />
<!-- <link rel="apple-touch-icon" href="/icon-192x192.png" />-->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- Google tag (gtag.js) -->
<script>
window.dataLayer = window.dataLayer || []
function gtag() {
dataLayer.push(arguments)
}
gtag('js', new Date())
gtag('config', 'AW-17916853811')
</script>
</head>
<body>
<div id="app"></div>
+3671 -689
View File
File diff suppressed because it is too large Load Diff
+44 -24
View File
@@ -4,7 +4,7 @@
"private": true,
"type": "module",
"engines": {
"node": "^20.19.0 || >=22.12.0"
"node": ">=24.0.0"
},
"scripts": {
"dev": "vite --mode dev",
@@ -13,42 +13,62 @@
"preview": "vite preview",
"type-check": "vue-tsc --build",
"lint": "eslint . --fix --cache",
"format": "prettier --write --experimental-cli src/"
"format": "prettier --write src/",
"lint:check": "eslint .",
"test": "node --experimental-strip-types --test tests/*.test.ts",
"perf:budget": "node scripts/measure-home-budget.mjs",
"model:compress": "node scripts/compress-phone-model.mjs",
"model:verify": "node scripts/verify-phone-model.mjs",
"model:runtime": "node scripts/build-model-decoder.mjs"
},
"dependencies": {
"@tailwindcss/vite": "^4.1.18",
"@vueuse/core": "^14.1.0",
"axios": "^1.13.2",
"@tailwindcss/vite": "^4.3.3",
"@vueuse/core": "^14.4.0",
"axios": "^1.20.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"crisp-sdk-web": "^1.0.27",
"crypto-js": "^4.2.0",
"lucide-vue-next": "^0.562.0",
"reka-ui": "^2.7.0",
"tailwind-merge": "^3.4.0",
"tailwindcss": "^4.1.18",
"vue": "^3.5.26",
"reka-ui": "^2.11.0",
"tailwind-merge": "^3.7.0",
"tailwindcss": "^4.3.3",
"vue": "^3.5.44",
"vue-router": "^4.6.4",
"vue-sonner": "^2.0.9"
},
"devDependencies": {
"@tsconfig/node24": "^24.0.3",
"@types/node": "^24.10.4",
"@vitejs/plugin-vue": "^6.0.3",
"@csstools/postcss-cascade-layers": "^5.0.2",
"@gltf-transform/core": "^4.5.1",
"@gltf-transform/extensions": "^4.5.1",
"@gltf-transform/functions": "^4.5.1",
"@tsconfig/node24": "^24.0.5",
"@types/node": "^24.19.2",
"@vitejs/plugin-vue": "^6.0.10",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.6.0",
"@vue/eslint-config-typescript": "^14.9.0",
"@vue/tsconfig": "^0.8.1",
"eslint": "^9.39.2",
"eslint-plugin-vue": "~10.6.2",
"jiti": "^2.6.1",
"draco3dgltf": "^1.5.9",
"esbuild": "^0.28.2",
"eslint": "^9.39.5",
"eslint-plugin-vue": "^10.11.1",
"jiti": "^2.7.0",
"lightningcss": "^1.33.0",
"meshoptimizer": "^1.3.0",
"npm-run-all2": "^8.0.4",
"prettier": "3.7.4",
"prettier-plugin-tailwindcss": "^0.7.2",
"prettier": "^3.9.10",
"prettier-plugin-tailwindcss": "^0.7.4",
"three": "^0.180.0",
"tw-animate-css": "^1.4.0",
"typescript": "~5.9.3",
"vite": "^7.3.0",
"vite-plugin-vue-devtools": "^8.0.5",
"vite-svg-loader": "^5.1.0",
"vue-tsc": "^3.2.1"
}
"vite": "^7.3.7",
"vite-plugin-vue-devtools": "^8.2.1",
"vite-svg-loader": "^5.1.3",
"vue-tsc": "^3.3.13"
},
"browserslist": [
"chrome >= 90",
"edge >= 90",
"safari >= 15",
"firefox >= 90"
]
}
-3976
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

+21
View File
@@ -0,0 +1,21 @@
<!--
tags: [help, question, support, faq, info, question-mark, assistance, unknown]
category: System
version: "1.1"
unicode: "eabf"
-->
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M3 12a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" />
<path d="M12 17l0 .01" />
<path d="M12 13.5a1.5 1.5 0 0 1 1 -1.5a2.6 2.6 0 1 0 -3 -4" />
</svg>

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

+11
View File
@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-vn" viewBox="0 0 640 480">
<defs>
<clipPath id="vn-a">
<path fill-opacity=".7" d="M-85.3 0h682.6v512H-85.3z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" clip-path="url(#vn-a)" transform="translate(80)scale(.9375)">
<path fill="#da251d" d="M-128 0h768v512h-768z"/>
<path fill="#ff0" d="M349.6 381 260 314.3l-89 67.3L204 272l-89-67.7 110.1-1 34.2-109.4L294 203l110.1.1-88.5 68.4 33.9 109.6z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 490 B

+11
View File
@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-jp" viewBox="0 0 640 480">
<defs>
<clipPath id="jp-a">
<path fill-opacity=".7" d="M-88 32h640v480H-88z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" stroke-width="1pt" clip-path="url(#jp-a)" transform="translate(88 -32)">
<path fill="#fff" d="M-128 32h720v480h-720z"/>
<circle cx="523.1" cy="344.1" r="194.9" fill="#bc002d" transform="translate(-168.4 8.6)scale(.76554)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 470 B

+25
View File
@@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-sa" viewBox="0 0 640 480">
<defs>
<clipPath id="sa-a">
<path fill-opacity=".7" d="M-85.3 0h682.6v512H-85.3z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" clip-path="url(#sa-a)" transform="translate(80)scale(.9375)">
<path fill="#165d31" d="M-128 0h768v512h-768z"/>
<path fill="#fff" d="M65.5 145.1c-.8 12-2 33 8.3 35.2 12.3 1.2 5.5-20.8 10-24.8.8-2 2.3-2 2.4.5v18.7c0 6 4 7.8 7 9 3.2-.2 5.4 0 6.6 3l1.6 32.3s7.4 2.2 7.8-18.1c.3-12-2.4-21.9-.8-24.2 0-2.3 3-2.4 5-1.3 3.2 2.2 4.6 5 9.6 4 7.6-2.2 12.2-5.9 12.3-11.7a47 47 0 0 0-3.5-16.6c.4-1-1.4-3.7-1-4.7 1.3 2.2 3.4 2 3.8 0-1.3-4.2-3.3-8.3-6.5-10-2.7-2.4-6.7-2-8 3-.8 5.7 2 12.4 6.1 18 .9 2.1 2.1 5.7 1.6 8.9q-3.4 1.8-6.3-1.2s-6-4.5-6-5.6c1.6-10.2.3-11.4-.6-14.3-.6-3.9-2.5-5.2-4-7.8-1.5-1.6-3.5-1.6-4.5 0-2.7 4.6-1.4 14.5.5 19 1.4 4.1 3.5 6.7 2.5 6.7-.8 2.3-2.5 1.7-3.8-1a67 67 0 0 1-2.1-17.4c-.5-4.6-1.1-14.4-4.2-17-1.8-2.4-4.5-1.2-5.5 1a82 82 0 0 0 .3 13.4c2 7.4 2.7 14 3.7 21.5.3 10.1-5.8 4.4-5.5-.7a45 45 0 0 0-.3-19.4c-1-2.6-2.1-3.2-4.6-2.8-1.9 0-6.8 5.3-8.2 14.3 0 0-1.2 4.6-1.7 8.7-.7 4.6-3.7 8-5.9-.6-1.8-6.3-3-21.6-6-18z"/>
<path fill="#fff" d="m99 194.2-32 15.4c.3-7.3 15.1-20.4 25.3-20.5 6.5.1 4.9 2.5 6.6 5.1z"/>
<path fill="#fff" d="M93.3 204.2c-16.8 43.5 39.5 49.6 45.8 1.8.6-2 3-3.9 3.4-.7-1.3 43.3-43.6 46.2-50.8 32.6a42 42 0 0 1-2.5-14.6c-1-8.5-5.5-5.2-6.2 3.2-.7 4.7-.5 6-.5 10.5 2.2 34.2 56.7 19.5 65.6-8.7 4.7-15.6-.8-27.1 1.7-27.1 5.4 5.8 13 .8 14.7-1.2.7-1 2.5-1.7 3.7-.4 4.2 3 11.6 1.6 13.2-3.7q1.4-8 1.8-16.2c-3.5 1-6 1.7-6.3 3.2l-.7 4.6c-.3 1.5-3.2 1.5-3.4-.4-1.3-6-6.7-6.7-10 2.5-2.1 1.8-6.1 2.2-6.5-.5.5-6.2-2-7-7-4.1l-4.8-36.2c2 0 4 1.5 5.9-.9-2-6.5-6.5-19.7-9-20.7-1.1-1.4-2.1-.5-3.7-.1-2.6.8-5 3-4.2 7.4 3 18.8 5 33.1 8.1 52 .5 2.1-1.3 5-3.7 4.7-4-2.7-5-8.2-12-8-5 0-10.6 5.5-11.3 10.7-.9 4.2-1.2 8.7 0 12.3 3.5 4.2 7.7 3.8 11.4 2.9 3-1.3 5.5-4.3 6.6-3.6.7.9.1 10.9-14.3 18.5-8.7 4-15.7 4.8-19.4-2.3-2.3-4.5.2-21.4-5.6-17.5"/>
<path fill="#fff" d="M165 160c3.3-1.2 19.3-19.6 19.3-19.6l-2.4-2q-1.3-1.1 0-2.2c4-2.4 2.7-7.4.7-9.8a10 10 0 0 0-8.7.1c-2.8 2.7-3.4 7-1.2 9.6 2.1 1 4.2 3.2 2.8 4.4-6.6 7-24.5 19.1-22.4 19.5.4.6 11.5.6 11.8 0zm-97 65c-6 9.6-6.5 23.9-3.2 28.2 1.8 2 4.7 2.9 6.8 2.2 3.8-1.6 5.5-9.3 4.6-12q-1.8-3-3.6-.7c-2.6 5.4-3.7 1.7-4-1.3a70 70 0 0 1 .8-15.2c.7-4.2 0-3-1.4-1.2m257.1-15.3c-5.8-12.6-13.9-25-16.4-29.7a558 558 0 0 0-24.8-36c-6.2-7.4 10.2 3.1-2-11.7l-8.9-7.5c-2-1.4-6.8-4-7.6.2-.4 3.8-.2 5.8.4 8.9.5 2 3.5 5.5 5 7.5a565 565 0 0 1 53.8 86.5c2.6-1.3 2-16.1.5-18.2"/>
<path fill="#fff" d="M299.6 251.5c-1.2 1.3 2.8 6.8 8 6.8 8.6-1 16.2-5.8 23.2-18.6a33 33 0 0 0 5.3-14.2 317 317 0 0 0-5.8-72.4c-.3-2 0-4.4.2-5 .6-.7 2.5 0 3.5-1.7 1.5-1.5-4-14-7-18.7-1-2.2-1.5-3.6-3.3.2a27 27 0 0 0-3 13.6c4.1 28.5 5.4 53.4 8 81.9.3 2.8-.1 6.8-2 8.4a80 80 0 0 1-27.1 19.7m116.5-.1c-6.2 3.6-6.2 7.7-1.2 7.8 8.6-1 18.8-1.7 25.8-12.3a41 41 0 0 0 4.2-16 303 303 0 0 0-4.7-71.4c-.2-2-1.1-6.7-.8-7.3.6-1.4 3.4.1 4.4-1.5 1.4-1.5-7.3-12.7-10.4-17.5-1-2.2-1.4-3.6-3.3.2a22 22 0 0 0-1.8 13.6c4.6 31 8 54.2 8.7 81.6-.4 2.6-.5 4-1.7 7.3-2.7 3.4-5.7 7.8-8.5 9.9-2.8 2-8.8 4-10.7 5.6"/>
<path fill="#fff" d="M420.7 223.7q.1-10.9-.1-19a34 34 0 0 0-3-13.5c-1.8-4.1-.7-7.4-1.6-11.8-.8-4.4-.6-11-1.8-16.1-.4-2-1.4-8.5-1.1-9.2.5-1.4 2.4 0 3.4-1.6 1.4-1.5-5-18-8.2-22.7-1.1-2.1-3.3-1.4-5.8 2-2.5 2.3-1.6 7.4-.6 12.3 6.1 32.3 10.8 61.6 9.8 92.3-.4 2.6 9-7.8 9-12.7m-45.7-40c-3.9-.2-12-7.7-14.4-12a8 8 0 0 1 .4-6.5c1.5-1 3.7-2 5.4-1 0 0 1.7 2.4 1.4 2.7 2 1 3 .5 3.2-.4.1-1.5-.6-2.4-.6-4 .9-4.6 6-5.3 8-2.4 1.4 1.8 2 5.5 2.1 8 0 1.3-2-.2-3.3 0-1.1.4-1.4 1.8-1.5 3-.2 3.3-.6 8.6-.7 12.5zm-71.8 48c1-9.8-.4-27.3-.5-33.1A477 477 0 0 0 299 154c-1.2-8.4 3.4.9 2.8-4-1.5-8.3-6.1-14-11.6-21.5-1.7-2.5-1.7-3-4.4.6-3 6.7-.4 11.4.4 16.7 3.9 17.2 6.2 33 7.3 48.7a393 393 0 0 1 .4 49c3 .1 7.6-4.7 9.3-11.8"/>
<path fill="#fff" d="M434 216c-6.9-11.6-17.2-24-20-28.7a658 658 0 0 0-29.2-37.8c-8.5-9 4-1.5-1.6-8.5-4.7-5.1-6-6.8-10.1-9.9-2-1.3-3.2-3.8-4 .5a83 83 0 0 0-.2 11.2c0 1.7 1.8 5 3.4 7 20.7 25.5 43.4 51.5 61.6 84.2 2.6-1.3 1.7-16 0-18z"/>
<path fill="#165d31" d="M122.6 194.7c-.5.9-1.6 2-1.2 3.1q1 1.4 2.6 1.3c1.1 0 2.7.3 3-.3q1-1.2.6-3.3c-1.2-3-4.4-1.8-5-.8"/>
<path fill="#fff" d="M354.2 362.5c9.2.4 15.2.5 23.3 1.4l9.6-1c10.6-1 11 15.1 11 15.1 0 9.5-3.7 10-8.4 11-2.7.4-4-1.6-5.5-3.6a14 14 0 0 1-7 .4l-11.5-.5c-4-.3-6.2.5-10.3.1-.8 1.3-2 3.1-4.4 2.6-2-.3-4.5-6-3.8-10.5 1.5-3.2 1-2.1 1-3.5-37.6-1-75.5-2.7-112.3-2.2-28.8.1-57.2 1.3-85.7 2.5-15.2-.2-26.8-2.6-34.8-14.3.8 0 38.8 2.1 49.9 1.4 20.5-.2 39.3-1.9 60.2-2.5 41.2.7 82.1.7 123.3 3.6-4-2.7-4-9 2-10.6.5-.4.8 3.1 1.7 3 4.9-.3 2.7 6.3 1.7 7.6M188.6 135.3c-6.2 17.8 3.6 37.4 10.4 35.5 5 2 8-7.4 10-17.6 1.5-2.9 2.5-3.2 3.2-1.7-.2 13.6 1 16.7 4.5 20.8 7.8 6 14.3.8 14.8.3l6-6.1q2.2-2.2 5.1-.3c1.9 1.7 1.6 4.6 5.6 6.6 3.4 1.4 10.5.4 12.2-2.5 2.2-3.9 2.8-5.2 3.8-6.6 1.6-2.1 4.3-1.2 4.3-.5-.3 1.2-1.9 2.3-.8 4.5 2 1.4 2.4.5 3.5.2 4-2 7-10.6 7-10.6.1-3.2-1.7-3-2.9-2.2l-3.1 2.1c-2 .3-5.7 1.6-7.6-1.3-1.9-3.4-1.9-8.3-3.3-11.8 0-.2-2.6-5.5-.2-5.8 1.2.2 3.7.9 4.1-1.2 1.2-2.1-2.6-8-5.3-11-2.3-2.5-5.5-2.8-8.6-.2-2.2 2-1.9 4.2-2.3 6.3a10 10 0 0 0 2 8.7c2.2 4.2 6.1 9.7 4.8 17.5 0 0-2.3 3.6-6.3 3.1-1.7-.3-4.4-1-5.8-11.8-1.1-8 .2-19.4-3.2-24.7-1.3-3.3-2.2-6.4-5.2-.9-.8 2.2-4.3 5.5-1.8 12.2a36 36 0 0 1 2 19c-1.5 2.2-1.8 2.9-3.7 5-2.6 3-5.5 2.2-7.7 1.1-2-1.3-3.6-2-4.6-6.5.2-7 .6-18.5-.7-20.9-1.9-3.8-5-2.4-6.3-1.2a48 48 0 0 0-11.5 23.5c-1.8 5.8-3.7 4.1-5 1.8-3.2-3-3.5-26.7-7.4-22.8"/>
<path fill="#fff" d="M207.4 174.1c2.9-2 1.6-3.4 5.8.8a72 72 0 0 1 9.2 31.3c-.2 2.6 1.6 4.2 2.5 3.6.4-6 15.1-14.4 28.6-15.6 2-.5 1-4.4 1.3-6.4-.8-7.5 4.2-14.3 11.2-14.8 9.6 1.4 12.8 6.5 13 14.2-1.1 15-16.7 17.5-25.4 18.7-1.3.5-1.9 1.1 0 1.8l36.6.2 1.9 1c.2 1-.6.2-2 2.6a30 30 0 0 0-3.7 11.5c-10.9 3.6-22.2 5-33.6 6.5-4 2-6 4.7-5.2 7.7 1.4 3.3 10.2 6.7 10.2 6.8 1.7 1 3.6 3.5-.5 8.6-17.8-.8-31.7-8.4-36.5-19.1-1.4-1.1-3 0-4 1.4-7 9-13.8 17-25.7 21.4-7 1.8-14.3-1.1-17.7-5.7-2.3-2.7-2.2-5.6-3-6.2-3.9 1.7-36.9 15.7-32.7 9.1 8-8.5 22-14.9 34.2-23.3.9-2.9 2.5-12.5 7.3-15.6.3 0-.7 5.6-.6 8 0 2-.2 2.7.2 2.2.9-.5 15.7-12.2 17-15.8 1.4-2 .3-7.2.3-7.4-2.8-7.2-6.7-7.8-8.1-11.4-1.3-4.7-.7-10.1 2-11.7q3.7-3.1 7.9.5c3 2.7 5.6 8 6.4 11.9-.5 1.5-4-1-5-.3a16 16 0 0 1 3.7 7.8c2 8.2 1.4 11.4-.6 16.7-6.6 13.9-15 18-22.4 23.2-.2 0-.3 3.5 2.4 5.4 1 1 4.9 1.5 9.4 0a55 55 0 0 0 22.3-23.3 51 51 0 0 0-2.4-22.2c-2.9-6.7-6.3-16.2-6.3-16.4-.1-4.2.2-5.6 2-7.7m-95.8-38.6c4.2 2 12.2 1.1 11.8-5.7l-.2-3.1c-.8-2-3.2-1.5-3.7.5-.2.7.3 1.8-.3 2.1-.4.4-1.7.2-1.7-1.7q-.1-1-.7-1.6-.2-.2-.9-.2c-.6 0-.6.1-.9.6l-.3 1.6q-.1.9-.8 1c-.6 0-.5 0-1-.2q-.4-.3-.5-1l-.3-1.6q-.3-.4-1-.6c-2.3 0-2.5 2.7-2.3 3.7-.2.2-.3 4.9 2.8 6.2"/>
<path fill="#fff" d="M235.1 187.7c4.2 2 14.3.9 11.8-5.6l-.2-3.2c-.9-2-3.2-1.5-3.7.6-.2.6.3 1.7-.4 2-.3.4-1.7.2-1.6-1.6q-.1-1-.7-1.7-.3-.2-1-.2c-.5 0-.5.2-.8.7q-.3.7-.3 1.6-.2.8-.9 1c-.5 0-.4 0-.8-.3q-.5-.3-.6-.9l-.3-1.6q-.3-.5-1-.6c-2.3 0-2.5 2.6-2.4 3.6-.1.2-.2 5 3 6.2zm72-21.6c4.2 2 12.1 1.1 11.8-5.6l-.2-3.2c-.9-2-3.2-1.5-3.7.5-.2.7.3 1.8-.4 2.2-.3.3-1.7.1-1.6-1.8q-.1-1-.7-1.6-.3-.2-1-.2t-.8.7l-.3 1.5q-.1 1-.9 1c-.8 0-.4 0-.8-.2q-.5-.3-.6-.9 0-1-.3-1.7-.4-.4-1-.5c-2.3 0-2.5 2.6-2.4 3.6-.1.2-.2 4.9 3 6.2zm37.3 54.3c-7.3 8.3-4.1 22-2.4 25 2.4 4.8 4.3 7.9 9 10.3 4.3 3.1 7.7 1.2 9.5-1 4.3-4.5 4.4-16 6.4-18.2 1.4-4.2 5-3.5 6.7-1.6a17 17 0 0 0 6.2 5.3c4 3.5 8.8 4.2 13.6 1 3.2-1.9 5.3-4.2 7.2-8.9 2-5.6 1-31.6.5-47l-4.2-21.5c0-.2-.5-10.2-1-12.5 0-1-.3-1.3.7-1.2 1.1 1 1.2 1 2 1.3 1 .2 2-1.7 1.3-3.3l-10-18.6c-.8-.8-1.9-1.6-3.2.2a7 7 0 0 0-2.4 5.5l1.3 13.3 4 22.6c1.3 16 1.6 29.2 2.9 45.3-.2 6.8-2.3 12.7-4.3 13.6 0 0-3 1.7-5-.2-1.5-.6-7.4-9.9-7.4-9.9-3-2.7-5-2-7.1 0-6 5.8-8.6 16.4-12.7 23.8-1 1.7-4 3-7.2-.1-8.2-11.3-3.4-27.3-4.4-23.2M309 126.7c3.8 1.5 6.4 9.2 5.6 13-.8 4.5-2.8 9.5-4.2 8.9-1.6-.6 1-4.6-.5-8.8-.8-2.8-6-7.8-5.4-9.2-1-3.1 2.2-4.5 4.5-4z"/>
<path fill="#fff" d="M356.6 225c.7-9.2-.6-14.8-.8-20.2s-6.1-46.6-7.3-50.6c-1.5-7.8 5.7-1 4.9-5.6-2.5-5.6-8.6-13.9-10.5-18.8-1.2-2-.7-4-3.3-.5a42 42 0 0 0-2.3 19.2c6.2 32.3 12.5 59.1 11.5 89.8 3 0 6.3-6.7 7.8-13.3m64.4-85.3c3.5 1.7 5.5 11.3 5.1 14-.7 5-2.5 10.4-3.8 9.7-1.5-.6.3-7.4-.4-9.5-.8-3-5.5-8.4-5-10-1-3.4 2-4.8 4.1-4.2m-255.7 67.9c3.3 1.3 5.3 8.3 5 10.3-.8 3.7-2.5 7.7-3.8 7.1-1.3-.4.3-5.4-.3-7-.3-3.7-4.9-5.7-4.8-7.3-.8-3 2-3.5 4-3.1z"/>
<path fill="#165d31" d="M244.9 218.2c4.2.2 6.3 3.6 2.4 5-4 1.3-7.7 2.4-7.8 8 1.5 8-2 5.2-4 4.2-2.4-1.8-9.2-6-10.2-15-.1-2.1 1.6-4 4.3-4 4 1.1 10 1.2 15.3 1.8"/>
<path fill="#fff" d="M77.4 124.4c4.8 1.4 5.1 8.6 4.8 10.7-.7 3.8-2.4 7.9-3.6 7.4-1.4-.5 0-5.7-.7-7.3-.7-2.2-4.8-6.4-4.4-7.6-.9-2.5 2-3.7 3.9-3.2m95.9 33.6c-3.8 2-5.2 8-2.9 11.6 2.2 3 5.6 1.9 6 1.9 3.7.4 5.9-6.9 5.9-6.9s.1-2-4.2 1.9c-1.9.3-2-.4-2.5-1.4a9 9 0 0 1 .5-5.7c.7-1.8-.7-2.6-2.8-1.4m28-36.4c-2 1.3-5.7 5.2-5.8 9.6-.1 2.5-.6 2.5 1 4 1.3 1.8 2.4 1.7 4.8.4a5 5 0 0 0 2.3-3.4c.6-2.8-3 1.4-3.4-1.8-.8-3 1.5-4.2 3.7-7 0-2 0-3.3-2.7-1.8zm22.4 4a60 60 0 0 0-1.6 11.1c-.6 2.8 3 4 4.5.4 2.4-6.5 2.4-9.3 2.6-12-.7-4.3-3.6-4.2-5.5.5m142 72.3c.4-.5 20-14.4 20-14.4 2-.7 1.5 7.2.6 7.1a78 78 0 0 1-20.7 14.3c-1 .7-1.9-5.3 0-7zm17.7-.2c3.5 1.7 4.9 11.8 4.5 14.5 0 5.4-3.3 9.6-4.7 9-1.4-.7.2-6.7-.5-8.8-.8-3-3.7-8.5-3.2-10.1-1-3.4 1.8-5.2 4-4.6zm-116 43.4a26 26 0 0 1 5.6-4.9c2-1 3.8.8 3.7.7.3 2-1.2 3.7-.7 6.3.4 1 .7 2.2 2.6 1.8 3.1-2.5 6-2.7 9-2.8 2.5.1 2.6 4.2 1 4.2-5.7 1.2-8.2 2.8-12.3 4.3-2 1.2-3.6-.3-3.6-.4s-1.1-1.1-.4-3.7q.3-3.2-2.4-3c-1.2.8-2.4 1.2-3-.3q-.6-1.4.5-2.2m136.6 5.4c.8 1 1.4 2-.1 3.8l-3.7 3.2c-.6 1-1 2.8 1 3.3 3.6 1 12-4.5 12-4.6 1.4-1 1-3 .8-3-.8-.9-2.6-.3-3.8-.5-.6 0-2.5-.2-1.6-2a11 11 0 0 0 1.6-2.9q.9-1.7-2-2.7c-2.1-.4-3-.2-5.3 0q-1.6.2-1.9 2.3c.1 2.3 1.5 2.2 3 3z"/>
<path fill="#165d31" d="M268.1 189.7c-.5 1-2.3 1-4 0s-2.7-2.6-2.1-3.5 2.3-.9 4 0 2.6 2.6 2.1 3.5m-89-53.6c-1 .3-2.4-.6-3-2s-.3-2.6.7-2.9 2.3.7 3 2 .3 2.7-.8 3zM355.2 375c9.4.4 18.2 0 27.5.5 1.7 1.5.5 5-.6 4.8l-7.8-.3c-.1-3-7.7-2.5-7.5.1-4.1.5-7.8-.1-12-.3-1.2-1.5-1-4.2.4-4.8"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.7 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="720" height="680" viewBox="0 0 720 680"><rect width="720" height="680" rx="40" fill="#18231c"/><circle cx="90" cy="90" r="36" fill="#d8d4be"/><text x="90" y="103" text-anchor="middle" font-family="Arial" font-size="34" fill="#24372a">Z</text><text x="146" y="85" font-family="PingFang SC,sans-serif" font-size="28" fill="#e7eee0">小周</text><text x="146" y="120" font-family="sans-serif" font-size="18" fill="#a8b49f">模拟评价 · 待替换</text><text x="54" y="211" font-family="sans-serif" font-size="33" fill="#b2ff55">★★★★★</text><text x="54" y="295" font-family="PingFang SC,Microsoft Yahei,sans-serif" font-size="28" fill="#d5dfce">临时出门几天,选七天套餐刚好。用多</text><text x="54" y="348" font-family="PingFang SC,Microsoft Yahei,sans-serif" font-size="28" fill="#d5dfce">久买多久,对我来说很合适。</text><text x="54" y="615" font-family="sans-serif" font-size="18" fill="#8d9e83">Hi快 · 界面演示占位内容</text></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-mo" viewBox="0 0 640 480">
<path fill="#00785e" d="M0 0h640v480H0z"/>
<path fill="#fbd116" d="m295 108.7 40.5 29.5L320 90.5l-15.5 47.7 40.6-29.5z"/>
<g id="mo-a">
<path fill="#fff" d="M320 331.6H217.5l-3.8-4H320a2 2 0 0 1 1.4 2q0 1.4-1.4 2m0-31.3a13 13 0 0 0 1.2-7.6 12 12 0 0 0-1.2-3.8 82 82 0 0 1-32.5 19 81 81 0 0 1-23.5 3.5h-63.1l5.8 8h61c20 0 38.2-7.2 52.3-19.1m-109.6-24.7a32 32 0 0 1-9.7 2 81 81 0 0 0 60.8 27.5 81 81 0 0 0 58.5-25 441 441 0 0 0 4.5-58.8 441 441 0 0 0-4.5-67.7c-6.6 6-19 18.7-24.8 38.3A81 81 0 0 0 292 215a81 81 0 0 0 13.7 45 81 81 0 0 1-17-49.5q.1-18.8 7.8-34.7a33 33 0 0 1-7.5-13 81 81 0 0 0-10.5 40c0 18 5.9 34.7 15.9 48.1a95 95 0 0 0-73.4-29.4 33 33 0 0 1 6.8 8.9 95 95 0 0 1 68.6 29.4 95 95 0 0 0-61-22.2 95 95 0 0 0-36.7 7.3 81 81 0 0 0 82.6 52.2q-7 1.3-14.4 1.3a81 81 0 0 1-56.4-22.8zM320 364.4h-53.1a144 144 0 0 0 53.1 10.1 11 11 0 0 0 1.3-5 11 11 0 0 0-1.3-5.1m0-24.5h-93.6l7.8 6.2H320a5 5 0 0 0 1.3-3.1 4 4 0 0 0-1.3-3.1m0 12.5h-76.7a144 144 0 0 0 14.4 8H320a8 8 0 0 0 1.2-4.2 8 8 0 0 0-1.2-3.8"/>
<path fill="#fbd116" d="m200.5 174.8 25.4 23.6-6.7-34-14.6 31.4 30.3-16.8zm36.9-32 34.7.6-27.7-21 10.1 33.3 11.3-32.9z"/>
</g>
<use xlink:href="#mo-a" width="100%" height="100%" transform="matrix(-1 0 0 1 640 0)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-my" viewBox="0 0 640 480">
<g clip-path="url(#my-a)">
<path fill="#C00" d="M0 0h640v480H0z"/>
<path fill="#C00" d="M0 0h640v34.3H0z"/>
<path fill="#fff" d="M0 34.3h640v34.3H0z"/>
<path fill="#C00" d="M0 68.6h640v34.3H0z"/>
<path fill="#fff" d="M0 102.9h640V137H0z"/>
<path fill="#C00" d="M0 137.1h640v34.3H0z"/>
<path fill="#fff" d="M0 171.4h640v34.3H0z"/>
<path fill="#C00" d="M0 205.7h640V240H0z"/>
<path fill="#fff" d="M0 240h640v34.3H0z"/>
<path fill="#C00" d="M0 274.3h640v34.3H0z"/>
<path fill="#fff" d="M0 308.6h640v34.3H0z"/>
<path fill="#C00" d="M0 342.9h640V377H0z"/>
<path fill="#fff" d="M0 377.1h640v34.3H0z"/>
<path fill="#C00" d="M0 411.4h640v34.3H0z"/>
<path fill="#fff" d="M0 445.7h640V480H0z"/>
<path fill="#006" d="M0 .5h320v274.3H0z"/>
<path fill="#FC0" d="m207.5 73.8 6 40.7 23-34-12.4 39.2 35.5-20.8-28.1 30 41-3.2-38.3 14.8 38.3 14.8-41-3.2 28.1 30-35.5-20.8 12.3 39.3-23-34.1-6 40.7-5.9-40.7-23 34 12.4-39.2-35.5 20.8 28-30-41 3.2 38.4-14.8-38.3-14.8 41 3.2-28.1-30 35.5 20.8-12.4-39.3 23 34.1zm-33.3 1.7a71 71 0 0 0-100 65 71 71 0 0 0 100 65 80 80 0 0 1-83.2 6.2 80 80 0 0 1-43.4-71.2 80 80 0 0 1 126.6-65"/>
</g>
<defs>
<clipPath id="my-a">
<path fill="#fff" d="M0 0h640v480H0z"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="720" height="680" viewBox="0 0 720 680"><rect width="720" height="680" rx="40" fill="#18231c"/><circle cx="90" cy="90" r="36" fill="#cde5ba"/><text x="90" y="103" text-anchor="middle" font-family="Arial" font-size="34" fill="#24372a">L</text><text x="146" y="85" font-family="PingFang SC,sans-serif" font-size="28" fill="#e7eee0">林间</text><text x="146" y="120" font-family="sans-serif" font-size="18" fill="#a8b49f">模拟评价 · 待替换</text><text x="54" y="211" font-family="sans-serif" font-size="33" fill="#b2ff55">★★★★★</text><text x="54" y="295" font-family="PingFang SC,Microsoft Yahei,sans-serif" font-size="28" fill="#d5dfce">平时主要在手机上用,回家切到电脑也</text><text x="54" y="348" font-family="PingFang SC,Microsoft Yahei,sans-serif" font-size="28" fill="#d5dfce">很顺手。界面清楚,不用研究太多设置</text><text x="54" y="401" font-family="PingFang SC,Microsoft Yahei,sans-serif" font-size="28" fill="#d5dfce">。</text><text x="54" y="615" font-family="sans-serif" font-size="18" fill="#8d9e83">Hi快 · 界面演示占位内容</text></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+34
View File
@@ -0,0 +1,34 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-tw" viewBox="0 0 640 480">
<clipPath id="tw-a">
<path d="M0 0h640v480H0z"/>
</clipPath>
<g clip-path="url(#tw-a)">
<path fill="red" d="M0 0h720v480H0z"/>
<path fill="#000095" d="M0 0h360v240H0z"/>
<g fill="#fff">
<path d="m154 126.9-2.5 9.6 9.4 2.6-1.8-7.1zm46.9 5.1-1.8 7.1 9.4-2.6-2.5-9.6zm-41.8-24-5.1 5.1 1.9 6.9z"/>
<path d="m155.9 120-1.9 6.9 5.1 5.1z"/>
<path d="m154 113.1-6.9 6.9 6.9 6.9 1.9-6.9zm14 27.8 5.1 5.1 6.9-1.9zm18.9 5.1 9.6 2.5 2.6-9.4-7.1 1.8z"/>
<path d="m192 140.9 7.1-1.8 1.8-7.1zm-31.1-1.8 2.6 9.4 9.6-2.5-5.1-5.1zm19.1 5 6.9 1.9 5.1-5.1z"/>
<path d="m173.1 146 6.9 6.9 6.9-6.9-6.9-1.9zm-12.2-45.1-9.4 2.6 2.5 9.6 5.1-5.1zm-1.8 31.1 1.8 7.1 7.1 1.8zm45-12 1.9-6.9-5.1-5.1z"/>
<path d="m168 99.1-7.1 1.8-1.8 7.1zm32.9 8.9-1.8-7.1-7.1-1.8zm5.1 18.9 6.9-6.9-6.9-6.9-1.9 6.9z"/>
<path d="m200.9 108-8.9-8.9-12-3.2-12 3.2-8.9 8.9-3.2 12 3.2 12 8.9 8.9 12 3.2 12-3.2 8.9-8.9 3.2-12z"/>
<path d="m200.9 132 5.1-5.1-1.9-6.9zm5.1-18.9 2.5-9.6-9.4-2.6 1.8 7.1zm-6.9-12.2-2.6-9.4-9.6 2.5 5.1 5.1zm-26-6.9-9.6-2.5-2.6 9.4 7.1-1.8zm6.9 1.9-6.9-1.9-5.1 5.1z"/>
<path d="m186.9 94-6.9-6.9-6.9 6.9 6.9 1.9z"/>
<path d="m192 99.1-5.1-5.1-6.9 1.9zM173.1 146l-9.6 2.5 4.5 16.6 12-12.2zm-5.1 19.1 12 44.9 12-44.9-12-12.2zm-7.1-26-9.4-2.6-4.4 16.4 16.4-4.4z"/>
<path d="m147.1 152.9-12 45.1 32.9-32.9-4.5-16.6zm-12-20.9L102 165.1l45.1-12.2 4.4-16.4z"/>
<path d="m154 126.9-6.9-6.9-12 12 16.4 4.5zm0-13.8-2.5-9.6-16.4 4.5 12 12z"/>
<path d="M135.1 108 90 120l45.1 12 12-12zm90 24-16.6 4.5 4.4 16.4 45.1 12.2z"/>
<path d="m199.1 139.1-2.6 9.4 16.4 4.4-4.4-16.4zm-12.2 6.9-6.9 6.9 12 12.2 4.5-16.6zm19.1-19.1 2.5 9.6 16.6-4.5-12.2-12z"/>
<path d="m192 165.1 33.1 32.9-12.2-45.1-16.4-4.4zm7.1-64.2 9.4 2.6 4.4-16.4-16.4 4.4z"/>
<path d="M225.1 108 258 75.1l-45.1 12-4.4 16.4zm-12.2-20.9L225.1 42 192 75.1l4.5 16.4zm12.2 44.9 44.9-12-44.9-12-12.2 12z"/>
<path d="m206 113.1 6.9 6.9 12.2-12-16.6-4.5zm-38-38L135.1 42l12 45.1 16.4 4.4z"/>
<path d="m160.9 100.9 2.6-9.4-16.4-4.4 4.4 16.4z"/>
<path d="m147.1 87.1-45.1-12 33.1 32.9 16.4-4.5zm39.8 6.9 9.6-2.5-4.5-16.4-12 12z"/>
<path d="M192 75.1 180 30l-12 45.1 12 12z"/>
<path d="m173.1 94 6.9-6.9-12-12-4.5 16.4z"/>
</g>
<circle cx="180" cy="120" r="51.1" fill="#000095"/>
<circle cx="180" cy="120" r="45.1" fill="#fff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

+8
View File
@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-tr" viewBox="0 0 640 480">
<g fill-rule="evenodd">
<path fill="#e30a17" d="M0 0h640v480H0z"/>
<path fill="#fff" d="M407 247.5c0 66.2-54.6 119.9-122 119.9s-122-53.7-122-120 54.6-119.8 122-119.8 122 53.7 122 119.9"/>
<path fill="#e30a17" d="M413 247.5c0 53-43.6 95.9-97.5 95.9s-97.6-43-97.6-96 43.7-95.8 97.6-95.8 97.6 42.9 97.6 95.9z"/>
<path fill="#fff" d="m430.7 191.5-1 44.3-41.3 11.2 40.8 14.5-1 40.7 26.5-31.8 40.2 14-23.2-34.1 28.3-33.9-43.5 12-25.8-37z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 549 B

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-gb" viewBox="0 0 640 480">
<path fill="#012169" d="M0 0h640v480H0z"/>
<path fill="#FFF" d="m75 0 244 181L562 0h78v62L400 241l240 178v61h-80L320 301 81 480H0v-60l239-178L0 64V0z"/>
<path fill="#C8102E" d="m424 281 216 159v40L369 281zm-184 20 6 35L54 480H0zM640 0v3L391 191l2-44L590 0zM0 0l239 176h-60L0 42z"/>
<path fill="#FFF" d="M241 0v480h160V0zM0 160v160h640V160z"/>
<path fill="#C8102E" d="M0 193v96h640v-96zM273 0v480h96V0z"/>
</svg>

After

Width:  |  Height:  |  Size: 504 B

+20
View File
@@ -0,0 +1,20 @@
<!--
tags: [person, account, profile, avatar, member, people, human, login, identity, user]
category: System
version: "1.0"
unicode: "eb4d"
-->
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0" />
<path d="M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2" />
</svg>

After

Width:  |  Height:  |  Size: 458 B

+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-id" viewBox="0 0 640 480">
<path fill="#e70011" d="M0 0h640v240H0Z"/>
<path fill="#fff" d="M0 240h640v240H0Z"/>
</svg>

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ph" viewBox="0 0 640 480">
<path fill="#0038a8" d="M0 0h640v240H0z"/>
<path fill="#ce1126" d="M0 240h640v240H0z"/>
<path fill="#fff" d="M415.7 240 0 480V0"/>
<path fill="#fcd116" d="M26.7 42.4 41 55l16.6-9.2-7.4 17.5 14 13-19-1.6-8.1 17.2-4.3-18.5L14 71l16.3-10zm323.8 172.3.4 19 18 6.3-18 6.2-.4 19-11.5-15.1-18.2 5.5 10.8-15.6-10.8-15.6 18.2 5.5zM37.2 388.1l8 17.2 19-1.6-13.9 13 7.4 17.5-16.6-9.1-14.4 12.4 3.6-18.7L14 409l18.9-2.4zm114.2-249-6.2 6.2 3.1 47-3 .3-5.7-42.9-5.1 5 7.6 38.4a48 48 0 0 0-17.2 7.1l-21.7-32.4H96l26.4 34.3-2.4 2-31.1-35.5h-8.8v8.8l35.4 31-2 2.5-34.3-26.3v7.1l32.5 21.7q-5.2 7.8-7.1 17.2L66.3 223l-5.1 5 42.9 5.7q-.3 1.6-.3 3.1l-47-3-6.2 6.2 6.2 6.2 47-3.1.3 3.1-42.9 5.7 5 5 38.4-7.6a48 48 0 0 0 7.1 17.2l-32.5 21.7v7.2l34.3-26.3 2 2.4-35.4 31v8.8H89l31-35.4 2.5 2L96 312.2h7.2l21.7-32.5q7.8 5.2 17.2 7.1l-7.6 38.4 5 5 5.7-42.9q1.5.3 3.1.3l-3 47 6.1 6.2 6.3-6.2-3.1-47 3-.3 5.7 43 5.1-5.1-7.6-38.4a48 48 0 0 0 17.2-7.1l21.7 32.5h7.2l-26.4-34.3 2.4-2 31.1 35.4h8.8v-8.8l-35.4-31 2-2.4 34.3 26.3v-7.2l-32.5-21.7q5.2-7.8 7.1-17.2l38.3 7.6 5.1-5-42.9-5.7q.3-1.5.3-3.1l47 3 6.2-6.1-6.2-6.2-47 3-.3-3 42.9-5.7-5-5-38.4 7.5a48 48 0 0 0-7.1-17.2l32.5-21.7v-7.1l-34.3 26.3-2-2.4 35.4-31v-8.9H214l-31 35.5-2.5-2 26.4-34.3h-7.2L178 200.2q-7.8-5.2-17.2-7.1l7.6-38.3-5-5-5.7 42.8-3.1-.3 3-47z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

+24
View File
@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-kr" viewBox="0 0 640 480">
<defs>
<clipPath id="kr-a">
<path fill-opacity=".7" d="M-95.8-.4h682.7v512H-95.8z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" clip-path="url(#kr-a)" transform="translate(89.8 .4)scale(.9375)">
<path fill="#fff" d="M-95.8-.4H587v512H-95.8Z"/>
<g transform="rotate(-56.3 361.6 -101.3)scale(10.66667)">
<g id="kr-c">
<path id="kr-b" fill="#000001" d="M-6-26H6v2H-6Zm0 3H6v2H-6Zm0 3H6v2H-6Z"/>
<use xlink:href="#kr-b" width="100%" height="100%" y="44"/>
</g>
<path stroke="#fff" d="M0 17v10"/>
<path fill="#cd2e3a" d="M0-12a12 12 0 0 1 0 24Z"/>
<path fill="#0047a0" d="M0-12a12 12 0 0 0 0 24A6 6 0 0 0 0 0Z"/>
<circle cy="-6" r="6" fill="#cd2e3a"/>
</g>
<g transform="rotate(-123.7 191.2 62.2)scale(10.66667)">
<use xlink:href="#kr-c" width="100%" height="100%"/>
<path stroke="#fff" d="M0-23.5v3M0 17v3.5m0 3v3"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+8
View File
@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-hk" viewBox="0 0 640 480">
<path fill="#EC1B2E" d="M0 0h640v480H0"/>
<path id="hk-a" fill="#fff" d="M346.3 103.1C267 98 230.6 201.9 305.6 240.3c-26-22.4-20.6-55.3-10.1-72.4l1.9 1.1c-13.8 23.5-11.2 52.7 11.1 71-12.7-12.3-9.5-39 12.1-48.9s23.6-39.3 16.4-49.1q-14.7-25.6 9.3-38.9M307.9 164l-4.7 7.4-1.8-8.6-8.6-2.3 7.8-4.3-.6-8.9 6.5 6.1 8.3-3.3-3.7 8.1 5.6 6.8z"/>
<use xlink:href="#hk-a" transform="rotate(72 312.5 243.5)"/>
<use xlink:href="#hk-a" transform="rotate(144 312.5 243.5)"/>
<use xlink:href="#hk-a" transform="rotate(216 312.5 243.5)"/>
<use xlink:href="#hk-a" transform="rotate(288 312.5 243.5)"/>
</svg>

After

Width:  |  Height:  |  Size: 727 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

+20
View File
@@ -0,0 +1,20 @@
<!--
tags: [direction, northeast, diagonal, external-link, open-in-new, trend, pointer, navigation, arrow, right]
category: Arrows
version: "1.0"
unicode: "ea24"
-->
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M17 7l-10 10" />
<path d="M8 7l9 0l0 9" />
</svg>

After

Width:  |  Height:  |  Size: 430 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="7 7 69 56"><path d="M27.3185 29.4202H40.2102L44.6543 7.1001H58.1465L47.0965 62.9978H33.5642L37.9682 40.7167H25.0765L20.6324 62.9978H7.1001L16.5887 15.2803L12.3849 7.1001H31.7225L27.3185 29.4202Z" fill="#10180B"></path><path d="M64.6324 62.9978H51.1402L59.0307 23.1098H72.5513L64.6324 62.9978Z" fill="#10180B"></path><path d="M73.2119 19.7858H59.6879L62.1902 7.13905H75.7225L73.2119 19.7858Z" fill="#10180B"></path></svg>

After

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="720" height="680" viewBox="0 0 720 680"><rect width="720" height="680" rx="40" fill="#18231c"/><circle cx="90" cy="90" r="36" fill="#d8d4be"/><text x="90" y="103" text-anchor="middle" font-family="Arial" font-size="34" fill="#24372a">X</text><text x="146" y="85" font-family="PingFang SC,sans-serif" font-size="28" fill="#e7eee0">夏天</text><text x="146" y="120" font-family="sans-serif" font-size="18" fill="#a8b49f">模拟评价 · 待替换</text><text x="54" y="211" font-family="sans-serif" font-size="33" fill="#b2ff55">★★★★★</text><text x="54" y="295" font-family="PingFang SC,Microsoft Yahei,sans-serif" font-size="28" fill="#d5dfce">遇到安装问题看了教程,一步一步跟着</text><text x="54" y="348" font-family="PingFang SC,Microsoft Yahei,sans-serif" font-size="28" fill="#d5dfce">做就完成了,希望以后越来越好。</text><text x="54" y="615" font-family="sans-serif" font-size="18" fill="#8d9e83">Hi快 · 界面演示占位内容</text></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-de" viewBox="0 0 640 480">
<path fill="#fc0" d="M0 320h640v160H0z"/>
<path fill="#000001" d="M0 0h640v160H0z"/>
<path fill="red" d="M0 160h640v160H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 221 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="720" height="680" viewBox="0 0 720 680"><rect width="720" height="680" rx="40" fill="#18231c"/><circle cx="90" cy="90" r="36" fill="#cde5ba"/><text x="90" y="103" text-anchor="middle" font-family="Arial" font-size="34" fill="#24372a">Y</text><text x="146" y="85" font-family="PingFang SC,sans-serif" font-size="28" fill="#e7eee0">阿远</text><text x="146" y="120" font-family="sans-serif" font-size="18" fill="#a8b49f">模拟评价 · 待替换</text><text x="54" y="211" font-family="sans-serif" font-size="33" fill="#b2ff55">★★★★★</text><text x="54" y="295" font-family="PingFang SC,Microsoft Yahei,sans-serif" font-size="28" fill="#d5dfce">换电脑时用邮箱找回账户,移除旧设备</text><text x="54" y="348" font-family="PingFang SC,Microsoft Yahei,sans-serif" font-size="28" fill="#d5dfce">后就可以继续用了。</text><text x="54" y="615" font-family="sans-serif" font-size="18" fill="#8d9e83">Hi快 · 界面演示占位内容</text></svg>

After

Width:  |  Height:  |  Size: 1019 B

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