Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ name: Pages
# 官网(website/)发布:push 到 master 时组装并部署到 GitHub Pages。
#
# 站点是零构建的静态页 —— 没有 npm 依赖、没有打包步骤,唯一的「构建」是把
# public/screenshots 与图标复制进 website/assets/(见 scripts/site-assets.mjs)。
# screenshots 与图标复制进 website/assets/(见 scripts/site-assets.mjs)。
# 那一步同时也是校验:页面上引用的每个本地资源都必须真的存在,否则这个 job 失败。
# 截图名写错在浏览器里只表现为一张破图,评审时看不出来,让它在这里断掉。
#
# paths 过滤器里必须带上 public/screenshots/** 与组装脚本本身:换一张截图同样
# 应该重新发布,否则站点上还是旧图,而「只改了 public/」看起来与官网无关。
# paths 过滤器里必须带上 screenshots/** 与组装脚本本身:换一张截图同样
# 应该重新发布,否则站点上还是旧图,而「只改了 screenshots/」看起来与官网无关。
# docs/privacy.html 同理 —— 它是站点根上 privacy.html 的来源,而那个地址是
# Microsoft Store 登记的隐私政策 URL,改了不重新发布就会是旧的。
on:
push:
branches: [master]
paths:
- 'website/**'
- 'public/screenshots/**'
- 'screenshots/**'
- 'public/qrcode.png'
- 'src-tauri/icons/icon.png'
- 'docs/privacy.html'
Expand All @@ -25,7 +25,7 @@ on:
pull_request:
paths:
- 'website/**'
- 'public/screenshots/**'
- 'screenshots/**'
- 'public/qrcode.png'
- 'src-tauri/icons/icon.png'
- 'docs/privacy.html'
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ website/assets/
# Copied from docs/privacy.html so the Microsoft Store's privacy-policy URL
# (…github.io/quant-desktop/privacy.html) survives the switch to Actions.
website/privacy.html
# sitemap.xml 由 scripts/site-assets.mjs 按实际页面生成(含 git 取到的 lastmod),
# 手维护的话加了页面必然忘记登记。
website/sitemap.xml

# Editor directories and files
.vscode/*
Expand Down
17 changes: 13 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,19 @@ App.vue → NConfigProvider + NMessageProvider + NDialogProvider

三处刻意的设计:

- **素材不复制。** `public/screenshots/` 是唯一来源(README 也在用),`website/assets/` 由 [scripts/site-assets.mjs](scripts/site-assets.mjs) 复制生成,并加进 `.gitignore` —— 与 `dist/` 同级,都是产物。之所以要走脚本而不是直接放两份图:两份副本必然漂移,README 换了截图而官网上还是旧的。脚本在 CI 与本地是**同一条命令**(`npm run site:assets`),所以本地过了 CI 就会过。它顺带做一件 CI 才有意义的事:扫描 `index.html` 里每个本地 `src`/`href` 并断言文件存在 —— 截图名写错在浏览器里只是一张破图,评审时看不出来。
- **素材不复制,而且不在 `public/` 下。** 截图住在仓库根的 `screenshots/`(README 也在用),`website/assets/` 由 [scripts/site-assets.mjs](scripts/site-assets.mjs) 复制生成,并加进 `.gitignore` —— 与 `dist/` 同级,都是产物。**位置这件事有个坑**:`public/` 是 Vite 的静态目录,会被整份复制进 `dist/`,而 `tauri.conf.json` 的 `frontendDist` 就是 `../dist` —— 截图原本放在 `public/screenshots/` 时,那 1.9MB 一直在被打进桌面安装包,而应用代码一处都没引用(`StatusBar.vue` 只引用了 `public/qrcode.png`,那是二维码的远程地址与兜底,**不能跟着搬**)。搬到仓库根之后 `dist/` 从约 3.3MB 降到 1.4MB。**改位置时必须同一个提交里改 README** —— 它由 github.com 按仓库相对路径渲染,文件一挪就全变破图。
脚本在 CI 与本地是**同一条命令**(`npm run site:assets`),所以本地过了 CI 就会过。它顺带做几件 CI 才有意义的事:扫描**每个**页面(不只 `index.html`)的 `src`/`href`/`srcset` 并断言文件存在 —— 截图名写错在浏览器里只是一张破图,评审时看不出来;以及**生成 `sitemap.xml`**(见下)。
- **配色与 `variables.css` 同源,且是仓库里唯一一处有意重复。** 站点没有构建步骤,就拿不到 `variables.css`;把调色板搬一份进 `website/styles.css` 是唯一的办法。所以**改那边的 surface / text / accent token 时,这里要跟着改**。站点在其上追加了一组**页面级氛围 token**(`--page-*` / `--grid-line` / `--hairline` / `--sheen` / `--glow-*` / `--btn-accent-*`),那批是站点自己的,应用里没有对应物:风格取向是深色金融终端 —— 有纵深的渐变底 + 蓝图网格、发丝描边 + 顶边 1px 高光、主按钮辉光。
- **主题契约与应用一致**:`<html data-theme>`,`<head>` 里那段内联脚本在首帧前定好主题(否则深色访问者会先看到一帧白闪),`app.js` 只做增强 —— 禁用 JS 时页面依然完整可读,`.reveal` 的隐藏也由内联脚本加的 `.js` 类门控,不是无条件写死。`app.js` 里没有网络请求。

**图片管线(`scripts/optimize-images.mjs`,`npm run site:images`)。** 截图原图是 2057px 的 PNG,一张 200–330KB;站点按 `<picture>` + WebP + `srcset` 三档(600 / 1100 / 2057)出,落地的通常是 44–68KB,降约 75–80%。同时出 favicon 32/180、品牌图标 128(原先 28px 的位置挂着 512px 的 203KB 原图)、以及 1200×630 的社交卡片。产物**提交进仓库**,所以 CI 不需要浏览器 —— 为十几张营销图给每次 `npm ci` 拉一个 `sharp` 那样的原生依赖不划算。代价是脚本本身依赖本机 Chrome(`CHROME_PATH` 可覆盖),**只有换截图时才需要手动跑**。三条实测得到的坑:

- **`canvas.toDataURL` 在 `file://` 下会因 canvas 被 taint 而抛 SecurityError**,必须给 Chrome 加 `--allow-file-access-from-files`。
- **目标宽 > 源宽时必须整个跳过,不能 `Math.min` 后仍按目标宽命名。** `settings-index.png` 只有 1298px 宽,早期实现产出的是 1298px 的图却叫 `-2057.webp`,而 HTML 里写着 `2057w` —— 描述符撒谎会让浏览器算出错误的密度。现在的规则是"文件名永远等于真实宽度",源图比所有目标都小的(行情条那两张 344×59)就不出 WebP,直接用 PNG。脚本每次运行会先清掉上一轮的 `.webp`,否则改了规则会留下没人引用却照样被发布的残留。
- **`background-clip: text` 会静默吃掉溢出元素框的字形**(`scripts/og-card.html` 的字标就是被它截断的):渐变只画在框内,溢出部分没有背景可裁,于是 `color: transparent` 生效、字直接消失。配 `width: max-content` 让框包住文字。

功能图的 `<picture>` 外面套了一层壳,所以给 `picture` 加了 `display: contents` —— 否则它会成为 grid/flex 的子项,把 `.ticker-runway` 的 `place-items` 和功能图栅格的列宽都算错一层。

三条踩过坑的约束,改样式时别破坏:

- **不引 webfont。** 通用设计建议会给 Inter + Google Fonts,但 `fonts.googleapis.com` 在国内基本加载不出来,而这个应用的用户主要在国内 —— 引它等于拿首屏去赌。科技感靠**刻意使用等宽栈**(`--font-mono`,应用本来就有这条栈)传达:区块编号、规格行、文件名、体积、表头。零网络依赖。
Expand All @@ -258,14 +267,14 @@ SEO 与 GEO(生成式引擎优化)都落在 `<head>` 与 `website/` 根下
- **`application/ld+json` 是 GEO 的主要抓手。** 一个 `@graph` 装三个节点:`SoftwareApplication`(14 条 `featureList`、5 张 `screenshot`、`offers` 价格 0、`operatingSystem`、`license`)、`FAQPage`(页面上那四条问答)、`WebSite`。两条纪律:字段写**事实**不写形容词;`FAQPage` 的答案必须与**页面上看得见的正文**逐字对得上(结构化数据描述的内容页面没有,属于违规)。`scripts/` 之外有一个校验脚本会同时检查 JSON 能否解析、以及答案是否真的出现在正文里。
- **有意不写 `softwareVersion`。** 页面上没有任何静态版本号(顶栏那枚徽章是运行时拉 shields.io 的),写死一个数字只会在下次发版后变成假信息。
- **`og:image` 用 `main-light.png` 而不是 `main-dark.png`。** 缩略图尺寸下深色截图糊成一团,亮色的还能看清界面;同时要声明 `og:image:width/height`,宽高对不上会被判为无效图。
- **`robots.txt` 把 AI 检索类爬虫(GPTBot / ClaudeBot / PerplexityBot / Google-Extended …)显式放行。** 不写也不会被默认拦(`User-agent: *` 已放行),显式列出的价值是这份文件同时是一份意图声明,将来要收紧只改这一处。`privacy.html` 带 `noindex`,因此**刻意不写进 sitemap**。
- **`llms.txt`** 是给 AI 检索用的精简事实页(平台、体积、价格、许可、数据来源、功能清单、常见问答)。写它的时候注意把「商业使用需获授权」这条写清楚 —— PolyForm Noncommercial 不是 MIT,答案引擎漏掉这个限制会误导人。
- **`robots.txt` 与 `llms.txt` 都不在本仓库里,唯一一份在 [Leaderxin/leaderxin.github.io](https://github.com/Leaderxin/leaderxin.github.io)。** 原因是协议层面的:robots 规定爬虫只读 `scheme://host/robots.txt`,llms.txt 的约定同理 —— 而项目站挂在 `/quant-desktop/` 下,放在那里的这两份文件**一次都不会被访问**(实测 `https://leaderxin.github.io/robots.txt` 曾是 404)。2026-10 建了用户站仓库把域名根补上,顺带也解决了根域名 404。那个仓库同时放 `index.html`(跳转到项目站,刻意 noindex)与根域的 `404.html`。**所以本站不再有这两份文件** —— 曾经有过,删掉是为了不出现两份必然漂移的副本。改 AI 爬虫放行名单、或改那些事实(平台/体积/价格/许可)时,去那个仓库改。
- **`sitemap.xml` 由 `site-assets.mjs` 生成,不是手写的。** 它扫描 `website/*.html`、排除 `SITEMAP_SKIP`(`404.html` 是错误页、`privacy.html` 带 `noindex` —— 把 noindex 页列进 sitemap 会被 Search Console 报成错误),`lastmod` 取自 `git log` 而不是「现在」:每次部署都变的日期对爬虫是噪音。之所以要生成:手维护的 sitemap 在加页面时必然漏登记,而且**失败是静默的** —— 没有人类链接指向 sitemap 去看它。

改文案时记得回头看这几处:`<title>`、`meta description`、OG/Twitter 三件套、JSON-LD 里的 `description` 与 `featureList`、以及 `llms.txt`。它们不在一个文件里,最容易只改了一处。

注意 **`ticker-dark.png` / `ticker-light.png` 只有 344×59**,必须按原尺寸展示(`.ticker-runway` 就是为此存在的)—— 其余截图是 2000px 级,可自由缩放。

工作流的 `paths` 过滤器里带着 `public/screenshots/**` 与 `docs/privacy.html`:换一张截图、改一次隐私政策,同样应该重新发布,否则站点上还是旧的,而「只改了 public/」看起来与官网无关。
Pages 现在已经跑在 GitHub Actions 上(2026-10 从 `master:/docs` 迁过来的,见下)。工作流的 `paths` 过滤器里带着 `screenshots/**` 与 `docs/privacy.html`:换一张截图、改一次隐私政策,同样应该重新发布,否则站点上还是旧的,而「只改了 screenshots/」看起来与官网无关。

**从 `docs/` 迁过来这件事本身有个坑,别踩。** 迁之前 Pages 的源是 `master:/docs`(`build_type: legacy`),为的是 Microsoft Store 上架需要的隐私政策页(见 `183bba7 docs: 新增隐私政策页面(Microsoft Store 上架用)`)—— 也就是说 `https://leaderxin.github.io/quant-desktop/privacy.html` 是**商店登记过的地址**;同一个源顺带把 `docs/superpowers/` 下 20 份内部规划稿也公开了。而切到 GitHub Actions 后站点根变成 `website/`,那个地址会 404。所以 `scripts/site-assets.mjs` 会把 `docs/privacy.html` 复制到**站点根**(`website/privacy.html`,已 gitignore),与截图同一套「单一来源 + 脚本复制」的做法,两份不可能分家 —— **改隐私政策只改 `docs/privacy.html` 一处**。

Expand Down
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
[![Microsoft Store](https://img.shields.io/badge/Microsoft_Store-%E4%B8%8B%E8%BD%BD-00A4EF?style=for-the-badge)](https://apps.microsoft.com/detail/9P46MZZDTTZ0)
[![加入交流群](https://img.shields.io/badge/%E5%8A%A0%E5%85%A5%E4%BA%A4%E6%B5%81%E7%BE%A4-%E5%BE%AE%E4%BF%A1-07c160?style=for-the-badge)](#-贡献与交流)

<img src="public/screenshots/main-dark.png" width="880" alt="QuantDesktop 主界面(夜间主题)" />
<img src="screenshots/main-dark.png" width="880" alt="QuantDesktop 主界面(夜间主题)" />

<sub><b>主界面 · 夜间主题</b> —— 指数栏 · 涨跌概览 · 自选列表,一屏尽览</sub>

Expand Down Expand Up @@ -57,7 +57,7 @@
- **涨跌速览** —— 上涨 / 下跌 / 平盘、涨停 / 跌停家数常驻主界面,市场情绪一眼看清
- **市场概览面板** —— 两市成交额、全市场涨跌分布直方图、行业与概念板块涨跌排行,热点在哪里一目了然

<img src="public/screenshots/market-overview.png" width="880" alt="市场涨跌概览与板块排名" />
<img src="screenshots/market-overview.png" width="880" alt="市场涨跌概览与板块排名" />

<sub><b>市场概览面板</b> —— 涨跌分布 · 两市成交额 · 行业 / 概念板块排行</sub>

Expand All @@ -75,22 +75,22 @@
<tr>
<td width="50%">

<img src="public/screenshots/detail-minute-depth.png" alt="个股分时图与五档盘口" />
<img src="screenshots/detail-minute-depth.png" alt="个股分时图与五档盘口" />

<sub><b>分时走势 + 五档盘口</b><br/>盘口每 3 秒静默刷新,附带开 / 高 / 低 / 量 / 额 / 换手率概要</sub>

</td>
<td width="50%">

<img src="public/screenshots/detail-kline-sub.png" alt="个股分钟 K 线与副图指标" />
<img src="screenshots/detail-kline-sub.png" alt="个股分钟 K 线与副图指标" />

<sub><b>分钟 K 线 + 副图指标</b><br/>主图 MA / BOLL 切换,副图成交量 / MACD 切换</sub>

</td>
</tr>
</table>

<img src="public/screenshots/index-kline.png" width="880" alt="指数 K 线详情" />
<img src="screenshots/index-kline.png" width="880" alt="指数 K 线详情" />

<sub><b>指数详情</b> —— 上证指数日 K,均线与量能一应俱全</sub>

Expand All @@ -103,7 +103,7 @@
- 置顶 / 上移 / 下移 / 删除,右键菜单快捷操作
- 跨数据源智能回退:一个源搜不到,自动换源再试

<img src="public/screenshots/add-stock.png" width="880" alt="添加自选" />
<img src="screenshots/add-stock.png" width="880" alt="添加自选" />

<sub><b>添加自选</b> —— 代码搜索 + 市场标签,同码指数与个股不再混淆</sub>

Expand All @@ -124,14 +124,14 @@
<tr>
<td align="center" width="50%">

<img src="public/screenshots/ticker-dark.png" alt="悬浮行情条(夜间主题)" />
<img src="screenshots/ticker-dark.png" alt="悬浮行情条(夜间主题)" />

<sub><b>夜间主题</b></sub>

</td>
<td align="center" width="50%">

<img src="public/screenshots/ticker-light.png" alt="悬浮行情条(日间主题)" />
<img src="screenshots/ticker-light.png" alt="悬浮行情条(日间主题)" />

<sub><b>日间主题</b></sub>

Expand Down Expand Up @@ -160,14 +160,14 @@
<tr>
<td width="50%">

<img src="public/screenshots/settings-index.png" alt="设置页 · 指数区" />
<img src="screenshots/settings-index.png" alt="设置页 · 指数区" />

<sub><b>指数区</b><br/>候选池勾选 + 拖拽排序,列表顺序就是顶栏从左到右的顺序</sub>

</td>
<td width="50%">

<img src="public/screenshots/settings-ticker.png" alt="设置页 · 行情条" />
<img src="screenshots/settings-ticker.png" alt="设置页 · 行情条" />

<sub><b>行情条</b><br/>显隐 / 透明背景 / 单次条数 / 轮播范围,改动即时预览</sub>

Expand All @@ -185,14 +185,14 @@
<tr>
<td width="50%">

<img src="public/screenshots/main-dark.png" alt="主界面夜间主题" />
<img src="screenshots/main-dark.png" alt="主界面夜间主题" />

<sub><b>夜间主题</b> —— 默认</sub>

</td>
<td width="50%">

<img src="public/screenshots/main-light.png" alt="主界面日间主题" />
<img src="screenshots/main-light.png" alt="主界面日间主题" />

<sub><b>日间主题</b></sub>

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"test": "vitest run",
"test:watch": "vitest",
"site:assets": "node scripts/site-assets.mjs",
"site:images": "node scripts/optimize-images.mjs",
"tauri": "tauri",
"tauri:build": "node scripts/build.mjs"
},
Expand Down
Binary file added screenshots/add-stock-1100.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/add-stock-2057.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/add-stock-600.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added screenshots/detail-kline-sub-1100.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/detail-kline-sub-2057.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/detail-kline-sub-600.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/detail-minute-depth-1100.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/detail-minute-depth-2057.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/detail-minute-depth-600.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/index-kline-1100.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/index-kline-2057.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/index-kline-600.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added screenshots/main-dark-1100.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/main-dark-2057.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/main-dark-600.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added screenshots/main-light-1100.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/main-light-2057.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/main-light-600.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added screenshots/market-overview-1100.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/market-overview-2057.webp
Binary file added screenshots/market-overview-600.webp
Binary file added screenshots/settings-index-1100.webp
Binary file added screenshots/settings-index-600.webp
File renamed without changes
Binary file added screenshots/settings-ticker-1100.webp
Binary file added screenshots/settings-ticker-600.webp
File renamed without changes
File renamed without changes
Loading
Loading