diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..232c894 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,41 @@ +name: 发布网站 + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - 'docs/**' + - '.github/workflows/pages.yml' + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + deploy: + name: 部署 GitHub Pages + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - uses: actions/configure-pages@v5 + + - name: 上传网站 + uses: actions/upload-pages-artifact@v3 + with: + path: docs + + - name: 发布网站 + id: deployment + uses: actions/deploy-pages@v4 diff --git a/site/.nojekyll b/docs/.nojekyll similarity index 100% rename from site/.nojekyll rename to docs/.nojekyll diff --git a/docs/404.html b/docs/404.html new file mode 100644 index 0000000..70a3f39 --- /dev/null +++ b/docs/404.html @@ -0,0 +1,23 @@ + + + + + + + + 页面不存在 · PeopleLib + + + + +
+
+ +

404

+

这个页面不存在

+

地址可能已经改变,返回首页继续了解 PeopleLib。

+ 返回首页 +
+
+ + diff --git a/site/CNAME b/docs/CNAME similarity index 100% rename from site/CNAME rename to docs/CNAME diff --git a/site/assets/icon-light-256.png b/docs/assets/icon.png similarity index 100% rename from site/assets/icon-light-256.png rename to docs/assets/icon.png diff --git a/site/assets/og-cover.jpg b/docs/assets/og-cover.jpg similarity index 100% rename from site/assets/og-cover.jpg rename to docs/assets/og-cover.jpg diff --git a/site/assets/shot-annotations.jpg b/docs/assets/shot-annotations.jpg similarity index 100% rename from site/assets/shot-annotations.jpg rename to docs/assets/shot-annotations.jpg diff --git a/site/assets/shot-reader.jpg b/docs/assets/shot-reader.jpg similarity index 100% rename from site/assets/shot-reader.jpg rename to docs/assets/shot-reader.jpg diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..e6d0434 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,202 @@ + + + + + + + + + + + + + PeopleLib · 文献检索与本地阅读 + + + + + + + +
+
+
+

查文献、管书库、做笔记

+

PeopleLib 是一款桌面阅读工具,把开放文献检索、本地书库、PDF 与电子书阅读、批注和 AI 助手放在一个应用里。

+ +

支持 Windows x64、macOS Apple 芯片和 Linux

+
+ +
+ +
+
+ + PeopleLib +
+ PeopleLib 本地书库界面 +
+ +
+
+

主要功能

+

从搜索、下载到阅读和记录,常用操作不用切换软件。

+
+
+
+

多源检索

+

统一检索 16 个文献与图书数据源,可指定单个来源,也可聚合搜索。

+
+
+

本地书库

+

管理下载和导入的文件,用书架、标签和阅读状态整理资料。

+
+
+

内置阅读器

+

阅读 PDF、EPUB、MOBI、AZW、TXT 与 Markdown,支持进度、书签、选文和批注。

+
+
+

AI 助手

+

使用自己的 API Key,可针对选文、当前页、全文或框选区域提问。

+
+
+
+ +
+
+

阅读器

+

目录、书签、批注、笔记和 AI 助手都在阅读窗口中,PDF 与电子书无需调用外部程序。

+
    +
  • PDF 页面批注与画布笔记
  • +
  • 可重排电子书与阅读进度
  • +
  • 文本、页面和图像上下文提问
  • +
+
+
+ PeopleLib 阅读器与 AI 助手界面 +
+
+ +
+
+

笔记与批注

+

支持富文本读书笔记、画布笔记和 PDF 页面批注,内容与书籍关联并保存在本地。

+
+ +
+
+ 读书笔记 +

整理摘录与文字

+

选中的段落可以直接摘录进笔记,富文本内容适合继续归纳、整理和导出。

+
+
+ 画布笔记 +

在页面上自由圈画

+

把 PDF 页面作为底版,使用画笔、高亮、形状和文字记录阅读时的想法。

+
+
+
+ +
+
+

支持格式

+

部分格式可以入库管理,但需要调用系统关联程序阅读。

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PeopleLib 格式支持对照
格式入库与管理内置阅读说明
PDF支持支持页面批注、书签、选文与笔记,可调渲染画质
EPUB支持支持目录、重排、书签、选文与笔记
MOBI / AZW / AZW3支持支持解析无 DRM 的 MOBI、KF7 与 KF8 内容
TXT / Markdown支持支持自动识别编码,Markdown 支持常用排版结构
DJVU / FB2 / CBZ / CBR支持系统打开可入库整理,阅读时调用系统关联应用
+
+

DRM 保护或损坏的文件不会尝试绕过保护,应用会提示改用系统关联程序打开。

+
+ +
+
+

数据保存在本机

+
+

书库、笔记、批注与阅读进度都写入本地文件。应用没有账号和同步服务,AI 内容只在确认后发送。

+
+ +
+ +

下载 PeopleLib

+

安装包由 GitHub Releases 发布。

+ 查看最新版本 +
+
+ + + + diff --git a/site/robots.txt b/docs/robots.txt similarity index 100% rename from site/robots.txt rename to docs/robots.txt diff --git a/docs/sitemap.xml b/docs/sitemap.xml new file mode 100644 index 0000000..cb3b599 --- /dev/null +++ b/docs/sitemap.xml @@ -0,0 +1,8 @@ + + + + https://reader.mesalogo.com/ + monthly + 1.0 + + diff --git a/docs/styles.css b/docs/styles.css new file mode 100644 index 0000000..d89c223 --- /dev/null +++ b/docs/styles.css @@ -0,0 +1,691 @@ +:root { + color-scheme: light; + --ink: #17233a; + --muted: #637089; + --line: #dfe5ef; + --surface: #ffffff; + --soft: #f3f6fb; + --blue: #3467eb; + --blue-dark: #2854c7; + --gold: #e6b95e; + font-family: Inter, "PingFang SC", "Microsoft YaHei", system-ui, sans-serif; + font-synthesis: none; +} + +* { + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + margin: 0; + color: var(--ink); + background: #f7f8fb; + line-height: 1.6; +} + +.error-page { + display: grid; + min-height: 100vh; + place-items: center; +} + +.error-content { + display: flex; + flex-direction: column; + align-items: center; + padding: 60px 20px; + text-align: center; +} + +.error-content img { + margin-bottom: 28px; + border-radius: 18px; + box-shadow: 0 18px 40px rgba(36, 54, 91, 0.18); +} + +.error-content h1 { + font-size: clamp(42px, 7vw, 72px); +} + +.error-code { + margin: 0 0 10px; + color: var(--blue); + font-size: 14px; + font-weight: 700; +} + +.error-content > p:not(.error-code) { + margin: 18px 0 30px; + color: var(--muted); +} + +a { + color: inherit; + text-decoration: none; +} + +img { + display: block; + max-width: 100%; +} + +.site-header { + position: sticky; + z-index: 20; + top: 0; + display: flex; + align-items: center; + width: min(1180px, calc(100% - 40px)); + height: 76px; + margin: 0 auto; + border-bottom: 1px solid rgba(223, 229, 239, 0.85); + background: #f7f8fb; +} + +.brand { + display: inline-flex; + align-items: center; + gap: 10px; + font-size: 18px; + font-weight: 750; + letter-spacing: -0.02em; +} + +.brand img { + border-radius: 11px; + box-shadow: 0 6px 18px rgba(31, 44, 74, 0.12); +} + +nav { + display: flex; + gap: 34px; + margin-left: auto; + color: var(--muted); + font-size: 14px; +} + +nav a, +footer a { + transition: color 160ms ease; +} + +nav a:hover, +footer a:hover { + color: var(--blue); +} + +.header-download { + margin-left: 34px; + padding: 8px 18px; + border: 1px solid var(--line); + border-radius: 7px; + background: var(--surface); + font-size: 14px; + font-weight: 650; + transition: border-color 160ms ease, transform 160ms ease; +} + +.header-download:hover { + border-color: #b9c6dc; + transform: translateY(-1px); +} + +main, +footer { + width: min(1180px, calc(100% - 40px)); + margin: 0 auto; +} + +.hero { + display: grid; + grid-template-columns: minmax(0, 1.35fr) minmax(280px, 0.65fr); + align-items: center; + min-height: 560px; + padding: 64px 7% 72px; +} + +h1, +h2, +h3, +p { + overflow-wrap: break-word; +} + +h1 { + max-width: 700px; + margin: 0; + font-size: clamp(44px, 5.4vw, 68px); + line-height: 1.12; + letter-spacing: -0.045em; +} + +.lead { + max-width: 650px; + margin: 28px 0 0; + color: var(--muted); + font-size: clamp(17px, 2vw, 20px); +} + +.hero-actions { + display: flex; + flex-wrap: wrap; + gap: 12px; + margin-top: 36px; +} + +.button { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 50px; + padding: 0 24px; + border: 1px solid transparent; + border-radius: 7px; + font-size: 15px; + font-weight: 700; + transition: background 160ms ease, border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease; +} + +.button:hover { + transform: translateY(-2px); +} + +.button-primary { + color: #fff; + background: var(--blue); + box-shadow: 0 6px 14px rgba(52, 103, 235, 0.18); +} + +.button-primary:hover { + background: var(--blue-dark); + box-shadow: 0 8px 18px rgba(52, 103, 235, 0.22); +} + +.button-secondary { + border-color: var(--line); + background: rgba(255, 255, 255, 0.72); +} + +.button-secondary:hover { + border-color: #b9c6dc; + background: #fff; +} + +.platforms { + margin: 18px 0 0; + color: #8490a4; + font-size: 13px; +} + +.hero-art { + display: grid; + place-items: center; + min-height: 300px; +} + +.app-icon { + border: 1px solid rgba(255, 255, 255, 0.8); + border-radius: 22%; + box-shadow: 0 18px 35px rgba(36, 54, 91, 0.18); +} + +.product-shot { + overflow: hidden; + border: 1px solid #d6dce8; + border-radius: 10px; + background: var(--surface); + box-shadow: 0 18px 45px rgba(42, 53, 78, 0.1); +} + +.window-bar { + display: flex; + align-items: center; + gap: 7px; + height: 42px; + padding: 0 15px; + border-bottom: 1px solid #e6eaf1; + background: #fbfcfe; +} + +.window-bar span { + width: 9px; + height: 9px; + border-radius: 50%; + background: #d6dce8; +} + +.window-bar strong { + margin: 0 auto 0 -33px; + color: #8b95a7; + font-size: 12px; + font-weight: 600; +} + +.section { + padding: 120px 0; +} + +.section-heading { + max-width: 720px; + margin: 0 0 46px; +} + +h2 { + margin: 0; + font-size: clamp(32px, 3.8vw, 46px); + line-height: 1.18; + letter-spacing: -0.045em; +} + +.section-heading > p:last-child, +.preview-copy > p { + margin: 18px 0 0; + color: var(--muted); + font-size: 17px; +} + +.feature-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 14px; +} + +.feature-grid article { + min-height: 220px; + padding: 28px; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--surface); + transition: border-color 180ms ease; +} + +.feature-grid article:hover { + border-color: #c5d0e3; +} + +h3 { + margin: 0; + font-size: 21px; + letter-spacing: -0.025em; +} + +.feature-grid p { + margin: 18px 0 0; + color: var(--muted); + font-size: 14px; +} + +.preview { + display: grid; + grid-template-columns: minmax(280px, 0.72fr) minmax(0, 1.28fr); + align-items: center; + gap: 7%; +} + +.preview-copy ul { + display: grid; + gap: 12px; + margin: 30px 0 0; + padding: 0; + color: var(--ink); + list-style: none; +} + +.preview-copy li { + position: relative; + padding-left: 25px; +} + +.preview-copy li::before { + position: absolute; + top: 0.72em; + left: 0; + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--gold); + content: ""; + transform: translateY(-50%); +} + +.preview-image { + overflow: hidden; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--surface); + box-shadow: 0 16px 40px rgba(42, 53, 78, 0.1); +} + +.note-gallery { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 18px; +} + +.note-gallery figure { + overflow: hidden; + margin: 0; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--surface); + box-shadow: 0 12px 32px rgba(42, 53, 78, 0.08); +} + +.note-gallery img { + width: 100%; + aspect-ratio: 1.51; + object-fit: cover; +} + +.note-gallery figcaption { + padding: 15px 18px; + color: var(--muted); + font-size: 13px; +} + +.note-types { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 18px; + margin-top: 18px; +} + +.note-types article { + padding: 28px; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--surface); +} + +.note-types span { + display: block; + margin-bottom: 18px; + color: var(--blue); + font-size: 12px; + font-weight: 750; + letter-spacing: 0.1em; +} + +.note-types p { + margin: 10px 0 0; + color: var(--muted); + font-size: 14px; +} + +.format-table-wrap { + overflow-x: auto; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--surface); +} + +table { + width: 100%; + min-width: 720px; + border-collapse: collapse; +} + +caption { + position: absolute; + overflow: hidden; + width: 1px; + height: 1px; + clip: rect(0 0 0 0); + white-space: nowrap; +} + +th, +td { + padding: 20px 22px; + border-bottom: 1px solid var(--line); + text-align: left; + vertical-align: middle; +} + +thead th { + color: var(--muted); + background: var(--soft); + font-size: 12px; + font-weight: 700; + letter-spacing: 0.05em; +} + +tbody th { + font-size: 14px; + white-space: nowrap; +} + +tbody td { + color: var(--muted); + font-size: 14px; +} + +tbody tr:last-child th, +tbody tr:last-child td { + border-bottom: 0; +} + +.support-yes, +.support-no { + display: inline-flex; + align-items: center; + min-height: 26px; + padding: 0 10px; + border-radius: 999px; + font-size: 12px; + font-weight: 700; + white-space: nowrap; +} + +.support-yes { + color: #28724a; + background: #e7f5ec; +} + +.support-no { + color: #6e7789; + background: #edf0f5; +} + +.format-hint { + margin: 18px 4px 0; + color: var(--muted); + font-size: 13px; +} + +.privacy { + display: grid; + grid-template-columns: 1fr 0.75fr; + align-items: end; + gap: 9%; + padding: 76px; + border-radius: 8px; + color: #f8faff; + background: #18233a; +} + +.privacy > p { + margin: 0; + color: #b9c3d5; + font-size: 17px; +} + +.cta { + display: flex; + flex-direction: column; + align-items: center; + padding: 150px 20px; + text-align: center; +} + +.cta img { + margin-bottom: 28px; + border-radius: 18px; + box-shadow: 0 12px 28px rgba(36, 54, 91, 0.14); +} + +.cta p { + margin: 16px 0 30px; + color: var(--muted); +} + +footer { + display: grid; + grid-template-columns: 1fr auto 1fr; + align-items: center; + padding: 28px 0 40px; + border-top: 1px solid var(--line); + color: var(--muted); + font-size: 13px; +} + +.footer-brand { + font-size: 15px; +} + +footer p { + margin: 0; +} + +footer p:last-child { + justify-self: end; +} + +@media (max-width: 900px) { + .hero { + grid-template-columns: 1fr; + min-height: auto; + padding: 90px 4% 70px; + text-align: center; + } + + .hero-copy { + z-index: 2; + } + + .lead { + margin-right: auto; + margin-left: auto; + } + + .hero-actions { + justify-content: center; + } + + .hero-art { + min-height: 300px; + } + + .feature-grid { + grid-template-columns: repeat(2, 1fr); + } + + .preview { + grid-template-columns: 1fr; + gap: 48px; + } + + .note-gallery { + grid-template-columns: 1fr; + } + + .preview-copy { + max-width: 650px; + } + + .privacy { + grid-template-columns: 1fr; + align-items: start; + gap: 30px; + } +} + +@media (max-width: 640px) { + .site-header, + main, + footer { + width: min(100% - 28px, 1180px); + } + + .site-header { + height: 66px; + } + + nav { + display: none; + } + + .header-download { + margin-left: auto; + } + + .hero { + padding: 72px 0 55px; + } + + h1 { + font-size: clamp(43px, 14vw, 62px); + } + + .hero-art { + min-height: 250px; + } + + .app-icon { + width: 140px; + height: 140px; + } + + .product-shot { + border-radius: 12px; + } + + .section { + padding: 100px 0; + } + + .feature-grid { + grid-template-columns: 1fr; + } + + .note-types { + grid-template-columns: 1fr; + } + + .feature-grid article { + min-height: auto; + } + + .privacy { + padding: 42px 28px; + } + + .cta { + padding: 110px 10px; + } + + footer { + grid-template-columns: 1fr auto; + gap: 10px; + } + + footer > p:nth-child(2) { + display: none; + } +} + +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } + + *, + *::before, + *::after { + transition-duration: 0.01ms !important; + } +} diff --git a/site/404.html b/site/404.html deleted file mode 100644 index dae86a4..0000000 --- a/site/404.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - -页面不存在 - PeopleLib - - - - - - - - - - - - - -
-
-
- 404 -

这个页面不存在

-

地址可能拼错了,或者页面已经移动。

-

- 回到首页 - 下载页 -

-
-
-
- - - - - diff --git a/site/DEPLOY.md b/site/DEPLOY.md deleted file mode 100644 index 9202525..0000000 --- a/site/DEPLOY.md +++ /dev/null @@ -1,357 +0,0 @@ -# 站点部署说明 - -`reader.mesalogo.com` 的 GitHub Pages 部署步骤。站点是纯静态的,没有构建步骤、没有 npm 依赖、没有 CDN 外链。 - -本文里凡是标注「查证」的,出处是 GitHub 官方文档(已抓取正文核对);标注「推断」的是我根据文档与本仓库现状的判断,需要你实际操作时确认。 - ---- - -## 一、文件清单 - -站点全部文件在 `site/`,目录本身就是站点根。 - -| 路径 | 作用 | -|---|---| -| `site/index.html` | 首页。价值主张、核心能力、隐私要点、真实截图、格式对照表 | -| `site/features.html` | 功能页。按真实功能清单展开,末尾有「不包含的功能」一节 | -| `site/privacy.html` | 隐私与安全。本地优先、自带密钥、确认后外发、进程边界、不绕 DRM、联网范围表 | -| `site/download.html` | 下载与安装。Windows 免安装步骤、macOS DMG 与首次打开的处理、首启建议 | -| `site/faq.html` | 常见问题。费用、数据位置、格式、macOS 拦截、离线、更新、超大 PDF 等 | -| `site/404.html` | 自定义 404。用根绝对路径引用资源(见下文注意事项) | -| `site/assets/style.css` | 唯一样式表。深浅两套配色随 `prefers-color-scheme` 切换 | -| `site/assets/shot-*.jpg` | 产品截图,由 `docs/screenshots/` 原图缩放转码而来 | -| `site/assets/icon-{light,dark}-{32,256,512}.png` | favicon 与品牌标记,从 `icons/dist/` 复制 | -| `site/assets/og-cover.jpg` | Open Graph 分享封面,1200x630 | -| `site/assets/build-assets.ps1` | 一次性资产生成脚本。换截图时手动重跑,站点本身不依赖它。只写 `site/assets/`,只读 `docs/screenshots/` 与 `icons/dist/` | -| `site/CNAME` | 自定义域名声明,内容是单行 `reader.mesalogo.com` | -| `site/.nojekyll` | 空文件,跳过 Jekyll 处理 | -| `site/robots.txt` | 允许全部抓取,指向 sitemap | -| `site/sitemap.xml` | 五个页面的站点地图 | - -没有 JavaScript。折叠式常见问题用原生 `
` 实现,不需要脚本。 - -### 已做的校验 - -- 六个页面在移动宽度(390px)下 `scrollWidth == clientWidth`,无横向溢出。 -- 全部内部链接与资源路径存在,全部页内锚点有效。 -- 页面只引用本地资源。外部地址只出现在 ``(指向 github.com)与 ``。 -- 浏览器控制台无报错、无警告。 -- 深浅两套配色的正文、次要文字、按钮、链接、页脚、表头对比度全部达到 WCAG AA,最低一项 4.75:1。 -- 每页一个 `h1`,`lang="zh-CN"`,全部 `` 有 `alt`,装饰性图标用空 `alt`。 -- 全部文件 UTF-8 无 BOM,无 emoji,无破折号。 - ---- - -## 二、部署方案 - -### 问题背景 - -本仓库有两个远端,用途不同: - -- `github`(`git@github.com:lofyer/peoplelib.git`,公开):源码与 CI,历史与本地无共同祖先。 -- `origin`(私有 Gitea):完整源码,日常开发推这里。 - -GitHub Pages 只能托管在公开仓库或付费计划的私有仓库上。站点要走公开仓库,所以核心问题是:**怎么让站点有自己独立的一条历史,改站点不牵动源码分支。** - -顺带提醒一句(查证):Pages 站点在互联网上始终是公开的,即使仓库是私有的。所以不要指望靠仓库权限藏住站点内容。 - -### 三种可选方式 - -GitHub 文档明确的发布源只有两类(查证):从某个分支发布,源目录只能是该分支的根 `/` 或 `/docs`;或者用自定义 GitHub Actions 工作流发布。 - -| 方式 | 是否可行 | 评价 | -|---|---|---| -| 公开仓库 `main` 分支的 `/docs` | 可行 | 但公开仓库的 `docs/screenshots/` 已被 README 引用,站点文件混进同一目录后,`docs/` 既是文档目录又是站点根,语义混乱。而且 README 与站点共用一次提交,改站点会污染 README 的历史 | -| 公开仓库独立 `gh-pages` 分支,根目录就是站点 | **推荐** | 分支里只有站点文件,改站点不牵动 `main` 的源码历史,两条线互不干扰 | -| GitHub Actions 工作流 | 不推荐 | 站点零构建,Actions 唯一的价值是自动化,收益抵不上多出来的运行时依赖与调试面。`main` 上的工作流只管应用的构建与发布 | - -### 结论:公开仓库的 `gh-pages` orphan 分支,源目录 `/` - -三个问题的答案: - -1. **部署方式**:从公开仓库的 `gh-pages` 分支发布,源目录选根 `/`。不用 `/docs`,不用 Actions。 -2. **CNAME 位置**:放在**发布分支的根目录**。因为 `site/` 的内容会成为 `gh-pages` 的根,所以 `site/CNAME` 推上去之后自然就在 `gh-pages` 根上,位置正确,不需要移动。(查证:从分支发布时,在 Settings 里保存自定义域名会自动在源分支根目录提交一个 `CNAME` 文件;反过来,你自己先放好这个文件也一样生效。用 Actions 发布则不会创建 `CNAME`,已有的也会被忽略。) -3. **`site/` 目录名会不会冲突**:不冲突,但**前提是用上面这个方案**。Pages 认不了名为 `site` 的源目录,它只认分支根或 `/docs`。而在本方案里 `site/` 只是私有仓库里的源目录,推送时把它的**内容**摊到 `gh-pages` 的根,Pages 看到的是根目录,所以目录叫什么都无所谓。**不需要改名,也不需要动仓库里其他任何地方。** - ---- - -## 三、发布操作 - -### 3.1 首次发布 - -用一个临时 worktree 挂 orphan 分支,避免污染主工作区。以下命令在 PowerShell 下逐条执行,`$repo` 换成你的实际路径。 - -```powershell -$repo = "D:\my_git\peoplelib" -$wt = "D:\my_git\peoplelib-pages" # 临时工作树,放在仓库外面 - -cd $repo - -# 1. 建一个挂着 orphan 分支的工作树。orphan 意味着无父提交,天然与源码历史无共同祖先 -git worktree add --orphan -b gh-pages $wt - -# 2. 把站点内容摊到工作树根目录。注意是 site\ 的内容,不是 site 这个目录本身 -Copy-Item -Path "$repo\site\*" -Destination $wt -Recurse -Force -Copy-Item -Path "$repo\site\.nojekyll" -Destination $wt -Force # 点开头的文件通配符可能漏掉,单独补一次 - -# 3. 确认工作树里只有站点文件,没有任何源码 -cd $wt -git status --porcelain -Get-ChildItem -Force | Select-Object Name - -# 4. 提交 -git add -A -git commit -m "站点:发布 reader.mesalogo.com 首版" - -# 5. 推到公开远端 -git push github gh-pages -``` - -`git worktree add --orphan` 需要 Git 2.42 或更高。本机是 2.55.0,可用。 - -发布脚本里不要 `git add` 那个 `assets/build-assets.ps1`,其实带上也无妨,它只是个纯文本工具脚本,不含源码逻辑,留着方便日后换图。你若不想让它进公开仓库,第 3 步之后删掉即可: - -```powershell -Remove-Item "$wt\assets\build-assets.ps1" -``` - -### 3.2 后续更新 - -工作树保留着的话,更新只要重复复制加提交: - -```powershell -$repo = "D:\my_git\peoplelib" -$wt = "D:\my_git\peoplelib-pages" - -cd $wt -# 先清干净,避免删掉的文件残留在分支上(保留 .git) -Get-ChildItem -Force | Where-Object { $_.Name -ne '.git' } | Remove-Item -Recurse -Force - -Copy-Item -Path "$repo\site\*" -Destination $wt -Recurse -Force -Copy-Item -Path "$repo\site\.nojekyll" -Destination $wt -Force - -git add -A -git commit -m "站点:更新下载说明" -git push github gh-pages -``` - -不再需要工作树时清理: - -```powershell -cd $repo -git worktree remove ..\peoplelib-pages -``` - -### 3.3 需要你决定的两件事 - -这两件都在 `site/` 之外,我没有动,交给你: - -1. **`.gitignore`**:`site/` 目前**没有**被忽略,`git status` 能看到它。如果你希望站点源文件跟着私有仓库一起走版本管理(推荐,这样才有历史可查),就什么都不用改。如果你不想让它进私有仓库,自己在 `.gitignore` 里加 `/site/`,注意要带前导斜杠,否则会连带匹配其他层级的同名目录,这和仓库里 `/dist/` 那条规则是同一个坑。 - -2. **不要加 GitHub Actions workflow**。上面已经论证过不推荐。如果你后来改主意要走 Actions,记住一条(查证):用 Actions 发布时 `CNAME` 文件不会被创建,已存在的也会被忽略且不是必需的,域名完全由 Settings 里的配置决定,那时 `site/CNAME` 就成了无用文件。 - ---- - -## 四、GitHub 仓库设置 - -在 `https://github.com/lofyer/peoplelib` 上操作。 - -1. 进 **Settings**,左侧 **Code and automation** 分组里点 **Pages**。 -2. **Build and deployment** 的 Source 选 **Deploy from a branch**。 -3. Branch 选 **`gh-pages`**,Folder 选 **`/ (root)`**,点 **Save**。 -4. **Custom domain** 填 `reader.mesalogo.com`,点 **Save**。 - -第 4 步之后 GitHub 会自动跑一次 DNS 检查。因为 `site/CNAME` 已经在分支根上且内容正确,这一步通常不会再产生额外提交;如果 GitHub 仍然自己提交了一次 `CNAME`,那是正常行为(查证:从分支发布时保存自定义域名会在源分支根目录提交 `CNAME`),下次更新前先 `git pull github gh-pages` 同步一下即可。 - -### 关于域名验证 - -GitHub 文档建议(查证):**先验证自定义域名,再把它加到仓库里**,以提升安全性、避免域名被抢占。验证入口在个人或组织的 **Settings → Pages → Add a domain**,它会要求你加一条 `_github-pages-challenge-.mesalogo.com` 的 TXT 记录。 - -这一步不是必须的,但如果你以后停用了 Pages 而 DNS 记录还留着,未验证的域名可能被别人拿去托管他们自己的站点。建议做。(查证:文档明确说明未验证且站点停用时存在被接管的风险。) - ---- - -## 五、DNS 配置 - -`reader.mesalogo.com` 是**子域名**,不是 apex 域名。这个区别决定了记录类型。 - -### 需要加的记录 - -一条,就一条(查证): - -| 类型 | 名称 | 值 | TTL | -|---|---|---|---| -| `CNAME` | `reader`(即 `reader.mesalogo.com`) | `lofyer.github.io` | 自动 / 默认 | - -要点: - -- 值是 `lofyer.github.io`,**不带仓库名**。文档原文是 CNAME 记录应始终指向 `.github.io` 或 `.github.io`,排除仓库名。(查证) -- 值不带 `https://`,不带结尾斜杠。有些 DNS 面板要求结尾带点写成 `lofyer.github.io.`,按面板的格式来。 -- **不要**加 A 记录或 AAAA 记录。那四个 `185.199.10x.153` 与对应的 IPv6 地址是给 **apex 域名**(`example.com` 这种)用的。子域名只需要 CNAME。(查证) -- **不要**用通配符记录如 `*.mesalogo.com`。文档强烈反对,因为即使验证了域名,通配符覆盖下的更深层子域名仍可能被接管。(查证) - -`site/CNAME` 文件与 DNS 里的 CNAME 记录是两件不同的东西,名字撞车而已:文件告诉 GitHub「这个站点用哪个域名」,DNS 记录告诉全世界「这个域名指向哪台服务器」。两边都要配。 - -### 当前状态 - -我查过(`Resolve-DnsName`): - -- `reader.mesalogo.com` 目前**不存在**,返回「DNS 名称不存在」。所以是全新添加,不会覆盖已有记录。 -- `mesalogo.com` 的权威 NS 是 `serena.ns.cloudflare.com` 与 `aarav.ns.cloudflare.com`,也就是**域名托管在 Cloudflare**。 - -### Cloudflare 特有注意事项(推断,非 GitHub 文档内容) - -在 Cloudflare 面板加这条 CNAME 时,右侧有个 Proxy status 开关: - -- 建议先设成 **DNS only**(灰色云朵)。橙色云朵代表 Cloudflare 代理,此时 Cloudflare 会自己终止 TLS,GitHub 那边的 DNS 检查可能拿不到期望的应答,导致证书签发卡住或反复失败。 -- 等 GitHub 侧证书签发成功、`https://reader.mesalogo.com` 能正常打开之后,如果你确实想用 Cloudflare 的 CDN,再切成橙色云朵,并把 SSL/TLS 模式设为 **Full (strict)**。不要用 Flexible,那会造成 Cloudflare 到 GitHub 之间走明文。 -- Cloudflare 默认 TTL 是 Auto,不用改。 - -这一节是我的操作建议,GitHub 文档不涉及具体 DNS 服务商。请以实际结果为准,卡住了先把云朵切灰。 - -### 验证 DNS 是否生效 - -Windows 没有 `dig`(查证:文档明确提到这一点并推荐 `Resolve-DnsName`): - -```powershell -Resolve-DnsName reader.mesalogo.com -Type CNAME -``` - -期望看到 `NameHost` 是 `lofyer.github.io`。 - -DNS 变更最多需要 24 小时传播(查证)。多数情况几分钟就好,但如果刚改完查不到,先等,别急着反复改。 - ---- - -## 六、HTTPS 证书 - -顺序很重要:**先 DNS 生效,再等证书,最后勾 Enforce HTTPS。** - -流程(查证): - -1. 你在 Settings → Pages 里保存或修改自定义域名后,GitHub 自动开始一次 DNS 检查。 -2. 检查通过后,GitHub 排队向 Let's Encrypt 申请 TLS 证书,拿到后自动部署到负责 Pages TLS 终止的服务器上。 -3. 全流程成功后,Settings → Pages 的自定义域名旁边会出现一个**对勾**。 -4. 这时再勾选 **Enforce HTTPS**,所有 HTTP 请求会被透明重定向到 HTTPS。 - -排障(查证):如果点了 Save 之后**几分钟**还没完成,出现「Certificate not yet created」,就点域名旁边的 **Remove**,重新输入域名再 **Save**,这会取消并重启签发流程。 - -关于 Enforce HTTPS 的勾选时机:文档说所有 Pages 站点包括正确配置了自定义域名的站点都支持 HTTPS 与 HTTPS 强制。**推断**:在证书还没签发出来(域名旁边没有对勾)时,这个复选框通常是灰的点不动,或者勾上会导致站点短时间打不开。所以按上面的顺序走,看到对勾再勾它。 - -混合内容(查证):如果页面里有 `http://` 开头的图片、CSS 或 JS,站点会被判定为混合内容。本站点不存在这个问题,所有资源都是相对路径引用的本地文件,已经校验过。 - ---- - -## 七、注意事项与已知坑 - -### 404 页面用的是根绝对路径 - -`site/404.html` 里的资源引用写成 `/assets/style.css` 这样的根绝对路径,因为 GitHub 会拿这个页面响应任意深度的错误路径,相对路径在 `/a/b/c` 这种地址下会解析错。 - -**代价**:在自定义域名生效之前,站点临时地址是 `https://lofyer.github.io/peoplelib/`,此时 404 页面的样式与图标会加载失败(它去找 `lofyer.github.io/assets/...` 而不是 `/peoplelib/assets/...`)。域名生效后站点在根路径上,一切正常。其他五个页面全部用相对路径,两种地址下都正常。 - -如果你想在临时地址下也让 404 页面完整,把该文件里的 `/assets/` 与 `/features.html` 等改成相对路径,代价是深层路径下样式丢失。二者不能同时满足,建议维持现状,等域名生效。 - -### `.nojekyll` - -空文件,作用是跳过 Jekyll 处理。当前站点里没有下划线开头的文件或目录,严格说不加也能正常发布;加上是防御性的,避免以后新增 `_something` 之类的路径时被 Jekyll 悄悄吞掉。(Jekyll 忽略下划线前缀路径这一行为属于既有共识,本次未逐字查证 GitHub 文档;`.nojekyll` 本身在发布源文档里被提到是外部 CI 部署的常见做法。) - -复制文件时留意:PowerShell 的 `Copy-Item site\*` 通配符可能不匹配点开头的文件,所以上面的命令里单独补了一次 `.nojekyll`。这个坑很容易漏,漏了不会报错,只是文件没过去。 - -### 版本号会过期 - -页脚、首页与下载页都写了 **2.0.0**,取自 `package.json` 的 `version` 字段。发新版时记得改,一共出现在这几处: - -```powershell -Select-String -Path D:\my_git\peoplelib\site\*.html -Pattern '1\.3\.0' -``` - -### 站点不引用 `docs/screenshots/` 原图 - -`site/assets/shot-*.jpg` 是压缩转码后的副本,和 `docs/screenshots/` 的原始 PNG 相互独立。换截图时替换 `docs/screenshots/` 里的原图,然后重跑: - -```powershell -pwsh -NoProfile -File D:\my_git\peoplelib\site\assets\build-assets.ps1 -``` - -脚本只写 `site/assets/`,只读 `docs/screenshots/` 与 `icons/dist/`。 - -### 仓库根目录的诊断图没有被使用 - -`font-missing-diagnostic.png`、`probe*.json` 之类的诊断产物一个都没进站点。 - ---- - -## 八、需要你补的资产 - -### 现在用的是真实截图,不是占位图 - -站点已经用上了 `docs/screenshots/` 里的六张真实截图,缩放到 1600px 宽、JPEG 质量 88: - -| 站点文件 | 来源 | 用在哪 | -|---|---|---| -| `shot-library.jpg` | `PeopleLib_bAecy2Izab.png` | 首页主视觉、OG 封面 | -| `shot-search.jpg` | `PeopleLib_34BtvLsqDE.png` | 首页画廊、功能页检索节 | -| `shot-reader.jpg` | `PeopleLib_ScQMUA2D24.png` | 首页画廊、功能页阅读节 | -| `shot-ai.jpg` | `PeopleLib_2N6zVpCFBA.png` | 首页画廊、功能页 AI 节 | -| `shot-ai-confirm.jpg` | `ai-send-confirmation.png` | 首页画廊、隐私页确认框一节 | -| `shot-annotations.jpg` | `pdf-annotations.png` | 功能页批注节 | - -所以站点**不缺图也能直接上线**。下面是可以让它更好的补充项,都是可选的。 - -### 建议补拍的截图 - -| 优先级 | 内容 | 用在哪 | 建议尺寸 | 说明 | -|---|---|---:|---|---| -| 高 | **AI 设置页**,展示接口地址、协议选择与 Key 输入框 | 隐私页「AI 密钥」一节 | 宽 ≥ 1600px,16:10 左右 | 目前这一节全是文字。给出真实界面能显著提升「自带密钥」这个卖点的说服力。**截图前务必清空或涂掉真实 Key** | -| 高 | **画布笔记**,最好是以 PDF 页面作底版的那种 | 功能页「笔记与批注」节 | 宽 ≥ 1600px | 画布笔记是差异化功能,现在只有文字描述 | -| 中 | **书架与标签的批量整理**,多选状态下的操作栏 | 功能页「本地书库」节 | 宽 ≥ 1600px | 那一节现在是定义列表,没有配图 | -| 中 | **macOS 首次打开的系统提示框**,以及右键菜单里的「打开」 | 下载页 macOS 节、常见问题 | 宽 800 至 1200px 即可 | 这是最容易让用户误判为病毒的环节,配图比文字管用得多。需要在 macOS 上截 | -| 中 | **EPUB 阅读界面**,带目录侧栏 | 功能页 EPUB 卡片 | 宽 ≥ 1600px | 现有截图全是 PDF,看不出 EPUB 的重排效果 | -| 低 | **浅色主题下的书库界面** | 首页主视觉的浅色替换 | 宽 ≥ 1600px | 现有截图都是深色界面,浅色站点配色下略显突兀。若要做,需要给 `` 加 `prefers-color-scheme` 分支,改动量不大但需要我再写一次 | -| 低 | **代理设置界面** | 功能页「代理与镜像容错」节 | 宽 ≥ 1600px | 截图前遮掉真实代理地址 | - -补图流程:把新 PNG 放进 `docs/screenshots/`,在 `site/assets/build-assets.ps1` 的 `$map` 数组里加一行映射,重跑脚本,然后在对应 HTML 里插 `
`。 - -### 图标 - -已经复用 `icons/dist/` 的应用图标,浅色深色两套按 `prefers-color-scheme` 切换,不需要新画。 - -唯一缺的是 **`favicon.ico`**。当前只提供 32px PNG favicon,现代浏览器都认,但少数老浏览器和某些抓取器会去根目录硬找 `/favicon.ico`。要补的话把 `icons/dist/book-ai-light.ico` 复制成 `site/favicon.ico` 即可,属于可选项。 - -### Open Graph 图 - -`site/assets/og-cover.jpg` 已生成,1200x630,81 KB,内容是深色底加应用图标、产品名、两行说明与域名,右侧放书库截图。由 `build-assets.ps1` 用 GDI+ 绘制,字体是「Microsoft YaHei」。 - -**推断**:如果这张图要在正式场合露脸,建议你用设计工具重做一版。脚本生成的版式够用但排版粗糙,尤其是文字与截图之间的留白比例。替换时直接覆盖 `site/assets/og-cover.jpg`,保持 1200x630 与文件名不变,HTML 里的 `og:image` 尺寸声明就不用动。 - ---- - -## 九、上线后的自查清单 - -```powershell -# DNS -Resolve-DnsName reader.mesalogo.com -Type CNAME - -# 首页可达且是 200 -curl.exe -sSI https://reader.mesalogo.com/ | Select-Object -First 1 - -# HTTP 是否重定向到 HTTPS(勾了 Enforce HTTPS 之后应该是 301) -curl.exe -sSI http://reader.mesalogo.com/ | Select-Object -First 3 - -# 404 页面生效(应返回 404 且内容是中文自定义页) -curl.exe -sS -o NUL -w "%{http_code}`n" https://reader.mesalogo.com/nonexistent - -# 五个页面全部可达 -'', 'features.html', 'privacy.html', 'download.html', 'faq.html' | ForEach-Object { - $u = "https://reader.mesalogo.com/$_" - "{0} {1}" -f (curl.exe -sS -o NUL -w "%{http_code}" $u), $u -} -``` - -浏览器里再手工看四件事: - -1. 切换系统深浅色主题,页面配色跟着变。 -2. 手机上打开,导航与卡片不溢出。 -3. 把首页链接贴进任意支持 Open Graph 预览的地方,标题、描述与封面图正常显示。 -4. 键盘按 Tab,焦点框可见,第一次 Tab 出现「跳到主要内容」。 diff --git a/site/assets/build-assets.ps1 b/site/assets/build-assets.ps1 deleted file mode 100644 index 775a052..0000000 --- a/site/assets/build-assets.ps1 +++ /dev/null @@ -1,102 +0,0 @@ -# 一次性资产生成脚本:把 docs/screenshots 与 icons/dist 的原图缩放并转码到 site/assets。 -# 站点本身零构建,这个脚本只在替换截图时手动重跑。 -param( - [string]$Repo = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path -) - -Add-Type -AssemblyName System.Drawing - -$outDir = $PSScriptRoot -$srcShots = Join-Path $Repo 'docs\screenshots' -$srcIcons = Join-Path $Repo 'icons\dist' - -function Save-Jpeg { - param([System.Drawing.Bitmap]$Bitmap, [string]$Path, [int]$Quality = 88) - $codec = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | Where-Object { $_.MimeType -eq 'image/jpeg' } - $params = New-Object System.Drawing.Imaging.EncoderParameters 1 - $params.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter ([System.Drawing.Imaging.Encoder]::Quality), ([int]$Quality) - $Bitmap.Save($Path, $codec, $params) - $params.Dispose() -} - -function Resize-Shot { - param([string]$In, [string]$Out, [int]$MaxWidth = 1600, [int]$Quality = 88) - $src = [System.Drawing.Image]::FromFile($In) - try { - $scale = [Math]::Min(1.0, $MaxWidth / $src.Width) - $w = [int][Math]::Round($src.Width * $scale) - $h = [int][Math]::Round($src.Height * $scale) - $bmp = New-Object System.Drawing.Bitmap $w, $h - try { - $g = [System.Drawing.Graphics]::FromImage($bmp) - $g.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic - $g.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality - $g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality - $g.DrawImage($src, 0, 0, $w, $h) - $g.Dispose() - Save-Jpeg -Bitmap $bmp -Path $Out -Quality $Quality - "{0}`t{1}x{2}`t{3} KB" -f (Split-Path $Out -Leaf), $w, $h, [Math]::Round((Get-Item $Out).Length / 1KB) - } finally { $bmp.Dispose() } - } finally { $src.Dispose() } -} - -$map = @( - @{ In = 'PeopleLib_bAecy2Izab.png'; Out = 'shot-library.jpg' } - @{ In = 'PeopleLib_34BtvLsqDE.png'; Out = 'shot-search.jpg' } - @{ In = 'PeopleLib_ScQMUA2D24.png'; Out = 'shot-reader.jpg' } - @{ In = 'PeopleLib_2N6zVpCFBA.png'; Out = 'shot-ai.jpg' } - @{ In = 'ai-send-confirmation.png'; Out = 'shot-ai-confirm.jpg' } - @{ In = 'pdf-annotations.png'; Out = 'shot-annotations.jpg' } -) - -foreach ($m in $map) { - $in = Join-Path $srcShots $m.In - if (Test-Path $in) { Resize-Shot -In $in -Out (Join-Path $outDir $m.Out) } - else { Write-Warning "缺少源图 $in" } -} - -foreach ($v in @('light', 'dark')) { - foreach ($size in @(32, 256, 512)) { - $in = Join-Path $srcIcons "$v\icon-$size.png" - if (Test-Path $in) { Copy-Item $in (Join-Path $outDir "icon-$v-$size.png") -Force } - } -} - -# Open Graph 封面:深色底 + 应用图标 + 书库截图,1200x630。 -$og = New-Object System.Drawing.Bitmap 1200, 630 -try { - $g = [System.Drawing.Graphics]::FromImage($og) - $g.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic - $g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality - $g.TextRenderingHint = [System.Drawing.Text.TextRenderingHint]::ClearTypeGridFit - $g.Clear([System.Drawing.ColorTranslator]::FromHtml('#12141a')) - - $shot = Join-Path $outDir 'shot-library.jpg' - if (Test-Path $shot) { - $img = [System.Drawing.Image]::FromFile($shot) - try { - $targetW = 660 - $scale = $targetW / $img.Width - $h = [int][Math]::Round($img.Height * $scale) - $g.DrawImage($img, 500, [int]((630 - $h) / 2), $targetW, $h) - } finally { $img.Dispose() } - } - - $iconPath = Join-Path $outDir 'icon-dark-256.png' - if (Test-Path $iconPath) { - $icon = [System.Drawing.Image]::FromFile($iconPath) - try { $g.DrawImage($icon, 72, 132, 112, 112) } finally { $icon.Dispose() } - } - - $white = New-Object System.Drawing.SolidBrush ([System.Drawing.ColorTranslator]::FromHtml('#f4f5f7')) - $muted = New-Object System.Drawing.SolidBrush ([System.Drawing.ColorTranslator]::FromHtml('#a8aeba')) - $fTitle = New-Object System.Drawing.Font 'Microsoft YaHei', 44, ([System.Drawing.FontStyle]::Bold) - $fSub = New-Object System.Drawing.Font 'Microsoft YaHei', 19 - $fFoot = New-Object System.Drawing.Font 'Microsoft YaHei', 15 - $g.DrawString('PeopleLib', $fTitle, $white, 68, 268) - $g.DrawString("多源文献检索`n本地书库与内置阅读器", $fSub, $muted, 72, 348) - $g.DrawString('reader.mesalogo.com', $fFoot, $muted, 72, 470) - $g.Dispose() - Save-Jpeg -Bitmap $og -Path (Join-Path $outDir 'og-cover.jpg') -Quality 86 - "og-cover.jpg`t1200x630`t{0} KB" -f [Math]::Round((Get-Item (Join-Path $outDir 'og-cover.jpg')).Length / 1KB) -} finally { $og.Dispose() } diff --git a/site/assets/icon-dark-256.png b/site/assets/icon-dark-256.png deleted file mode 100644 index b169074..0000000 Binary files a/site/assets/icon-dark-256.png and /dev/null differ diff --git a/site/assets/icon-dark-32.png b/site/assets/icon-dark-32.png deleted file mode 100644 index 7ed43e8..0000000 Binary files a/site/assets/icon-dark-32.png and /dev/null differ diff --git a/site/assets/icon-dark-512.png b/site/assets/icon-dark-512.png deleted file mode 100644 index 856c528..0000000 Binary files a/site/assets/icon-dark-512.png and /dev/null differ diff --git a/site/assets/icon-light-32.png b/site/assets/icon-light-32.png deleted file mode 100644 index 674c831..0000000 Binary files a/site/assets/icon-light-32.png and /dev/null differ diff --git a/site/assets/icon-light-512.png b/site/assets/icon-light-512.png deleted file mode 100644 index dec1aef..0000000 Binary files a/site/assets/icon-light-512.png and /dev/null differ diff --git a/site/assets/shot-ai-confirm.jpg b/site/assets/shot-ai-confirm.jpg deleted file mode 100644 index eebb189..0000000 Binary files a/site/assets/shot-ai-confirm.jpg and /dev/null differ diff --git a/site/assets/shot-ai.jpg b/site/assets/shot-ai.jpg deleted file mode 100644 index b15b0a7..0000000 Binary files a/site/assets/shot-ai.jpg and /dev/null differ diff --git a/site/assets/shot-library.jpg b/site/assets/shot-library.jpg deleted file mode 100644 index 92bdf81..0000000 Binary files a/site/assets/shot-library.jpg and /dev/null differ diff --git a/site/assets/shot-search.jpg b/site/assets/shot-search.jpg deleted file mode 100644 index 18fc522..0000000 Binary files a/site/assets/shot-search.jpg and /dev/null differ diff --git a/site/assets/style.css b/site/assets/style.css deleted file mode 100644 index 3ba769b..0000000 --- a/site/assets/style.css +++ /dev/null @@ -1,722 +0,0 @@ -:root { - color-scheme: light dark; - - --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", - "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", "Noto Sans CJK SC", - sans-serif; - --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", - "DejaVu Sans Mono", monospace; - - --bg: #fbfaf7; - --bg-elevated: #ffffff; - --bg-subtle: #f2f0eb; - --bg-code: #f0eee8; - --text: #1b1c20; - --text-muted: #55585f; - --text-faint: #6d7078; - --border: #ded9cf; - --border-strong: #c6c0b3; - --accent: #8a6110; - --accent-hover: #6d4b0b; - --accent-contrast: #ffffff; - --accent-soft: #f6eeda; - --focus: #1b5ea8; - --shadow: 0 1px 2px rgba(27, 28, 32, .05), 0 8px 24px rgba(27, 28, 32, .07); - --radius: 12px; - --measure: 68ch; -} - -@media (prefers-color-scheme: dark) { - :root { - --bg: #12141a; - --bg-elevated: #1a1d24; - --bg-subtle: #1f232b; - --bg-code: #22262f; - --text: #f0f1f4; - --text-muted: #b3b8c4; - --text-faint: #9aa0ad; - --border: #2e333d; - --border-strong: #414753; - --accent: #e0b256; - --accent-hover: #f0c672; - --accent-contrast: #17181c; - --accent-soft: #2a2519; - --focus: #7ab6f0; - --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 10px 28px rgba(0, 0, 0, .34); - } -} - -*, -*::before, -*::after { - box-sizing: border-box; -} - -html { - -webkit-text-size-adjust: 100%; - scroll-behavior: smooth; -} - -@media (prefers-reduced-motion: reduce) { - html { scroll-behavior: auto; } - * { animation-duration: .01ms !important; transition-duration: .01ms !important; } -} - -body { - margin: 0; - background: var(--bg); - color: var(--text); - font-family: var(--font-sans); - font-size: 17px; - line-height: 1.75; - text-rendering: optimizeLegibility; -} - -h1, h2, h3, h4 { - line-height: 1.3; - font-weight: 650; - letter-spacing: .01em; - margin: 0 0 .6em; - text-wrap: balance; -} - -h1 { font-size: clamp(1.9rem, 1.3rem + 2.4vw, 3rem); } -h2 { font-size: clamp(1.45rem, 1.2rem + 1.1vw, 1.95rem); } -h3 { font-size: 1.16rem; } - -p, ul, ol, dl, table, figure, pre { - margin: 0 0 1.1em; -} - -a { - color: var(--accent); - text-decoration-thickness: 1px; - text-underline-offset: 3px; -} - -a:hover { color: var(--accent-hover); } - -:focus-visible { - outline: 3px solid var(--focus); - outline-offset: 2px; - border-radius: 4px; -} - -code, kbd, samp, pre { - font-family: var(--font-mono); - font-size: .92em; -} - -code:not(pre code) { - background: var(--bg-code); - border: 1px solid var(--border); - border-radius: 5px; - padding: .1em .38em; - word-break: break-word; -} - -pre { - background: var(--bg-code); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: .95rem 1.05rem; - overflow-x: auto; - line-height: 1.6; -} - -kbd { - background: var(--bg-elevated); - border: 1px solid var(--border-strong); - border-bottom-width: 2px; - border-radius: 5px; - padding: .05em .4em; -} - -img { max-width: 100%; height: auto; } - -hr { - border: 0; - border-top: 1px solid var(--border); - margin: 2.5rem 0; -} - -.skip-link { - position: absolute; - left: -9999px; - top: 0; - z-index: 100; - background: var(--bg-elevated); - color: var(--text); - border: 1px solid var(--border-strong); - padding: .6rem 1rem; - border-radius: 0 0 var(--radius) 0; -} - -.skip-link:focus { - left: 0; -} - -.wrap { - width: 100%; - max-width: 1080px; - margin-inline: auto; - padding-inline: clamp(1rem, 4vw, 2rem); -} - -.site-header { - position: sticky; - top: 0; - z-index: 30; - background: var(--bg); - background: color-mix(in srgb, var(--bg) 88%, transparent); - backdrop-filter: saturate(1.4) blur(10px); - border-bottom: 1px solid var(--border); -} - -@supports not (backdrop-filter: blur(2px)) { - .site-header { background: var(--bg); } -} - -.site-header .wrap { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: .5rem 1.5rem; - padding-block: .7rem; -} - -.brand { - display: inline-flex; - align-items: center; - gap: .55rem; - font-weight: 700; - font-size: 1.06rem; - color: var(--text); - text-decoration: none; - letter-spacing: .01em; -} - -.brand img { - width: 30px; - height: 30px; - border-radius: 7px; -} - -.brand .brand-light { display: block; } -.brand .brand-dark { display: none; } - -@media (prefers-color-scheme: dark) { - .brand .brand-light { display: none; } - .brand .brand-dark { display: block; } -} - -.site-nav { - margin-inline-start: auto; -} - -.site-nav ul { - display: flex; - flex-wrap: wrap; - gap: .2rem .35rem; - list-style: none; - margin: 0; - padding: 0; -} - -.site-nav a { - display: block; - padding: .35rem .68rem; - border-radius: 8px; - color: var(--text-muted); - text-decoration: none; - font-size: .95rem; -} - -.site-nav a:hover { - background: var(--bg-subtle); - color: var(--text); -} - -.site-nav a[aria-current="page"] { - color: var(--text); - background: var(--bg-subtle); - font-weight: 600; -} - -main { display: block; } - -section { - padding-block: clamp(2.6rem, 5vw, 4.4rem); -} - -section + section { - border-top: 1px solid var(--border); -} - -.section-head { - max-width: var(--measure); - margin-bottom: 2rem; -} - -.section-head p { - color: var(--text-muted); - margin-bottom: 0; -} - -.eyebrow { - display: block; - font-size: .8rem; - font-weight: 700; - letter-spacing: .16em; - text-transform: uppercase; - color: var(--text-faint); - margin-bottom: .7rem; -} - -.hero { - padding-block: clamp(2.6rem, 6vw, 5rem); -} - -.hero-grid { - display: grid; - gap: clamp(2rem, 4vw, 3.2rem); - align-items: center; - grid-template-columns: minmax(0, 1fr); -} - -@media (min-width: 900px) { - .hero-grid { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); } -} - -.hero p.lede { - font-size: 1.12rem; - color: var(--text-muted); - max-width: 46ch; -} - -.hero h1 { margin-bottom: .5em; } - -.tagline { - display: inline-flex; - align-items: center; - gap: .5rem; - background: var(--accent-soft); - color: var(--accent); - border: 1px solid var(--border-strong); - border-color: color-mix(in srgb, var(--accent) 32%, transparent); - border-radius: 999px; - padding: .22rem .8rem; - font-size: .86rem; - font-weight: 600; - margin-bottom: 1.4rem; -} - -.btn-row { - display: flex; - flex-wrap: wrap; - gap: .7rem; - margin-bottom: 1rem; -} - -.btn { - display: inline-flex; - align-items: center; - justify-content: center; - gap: .45rem; - padding: .62rem 1.25rem; - border-radius: 10px; - border: 1px solid transparent; - font-size: 1rem; - font-weight: 600; - text-decoration: none; - transition: background-color .15s ease, border-color .15s ease, color .15s ease; -} - -.btn-primary { - background: var(--accent); - color: var(--accent-contrast); -} - -.btn-primary:hover { - background: var(--accent-hover); - color: var(--accent-contrast); -} - -.btn-secondary { - background: var(--bg-elevated); - border-color: var(--border-strong); - color: var(--text); -} - -.btn-secondary:hover { - background: var(--bg-subtle); - color: var(--text); - border-color: var(--text-faint); -} - -.btn-row-center { justify-content: center; } - -.hint { - color: var(--text-faint); - font-size: .9rem; - margin: 0; -} - -.shot { - margin: 0; - border: 1px solid var(--border); - border-radius: var(--radius); - background: var(--bg-elevated); - box-shadow: var(--shadow); - overflow: hidden; -} - -.shot img { - display: block; - width: 100%; - background: var(--bg-subtle); -} - -.shot figcaption { - padding: .65rem .9rem; - font-size: .88rem; - color: var(--text-faint); - border-top: 1px solid var(--border); -} - -.shot-placeholder { - display: flex; - align-items: center; - justify-content: center; - min-height: 220px; - padding: 1.5rem; - text-align: center; - color: var(--text-faint); - font-size: .92rem; - background: - repeating-linear-gradient(135deg, - var(--bg-subtle) 0 12px, - var(--bg-elevated) 12px 24px); -} - -.grid { - display: grid; - gap: 1rem; - grid-template-columns: repeat(auto-fit, minmax(min(100%, 270px), 1fr)); -} - -.card { - background: var(--bg-elevated); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 1.25rem 1.35rem; -} - -.card h3 { - margin-bottom: .45em; -} - -.card p:last-child, -.card ul:last-child { - margin-bottom: 0; -} - -.card ul { - padding-inline-start: 1.15rem; - color: var(--text-muted); -} - -.card .card-meta { - display: block; - font-size: .82rem; - letter-spacing: .1em; - text-transform: uppercase; - color: var(--text-faint); - margin-bottom: .5rem; - font-weight: 700; -} - -.prose { - max-width: var(--measure); -} - -.prose ul, .prose ol { - padding-inline-start: 1.3rem; -} - -.prose li + li { - margin-top: .35em; -} - -.prose li > strong:first-child { - color: var(--text); -} - -.callout { - border: 1px solid var(--border-strong); - border-inline-start: 4px solid var(--accent); - border-radius: 0 var(--radius) var(--radius) 0; - background: var(--bg-elevated); - padding: 1rem 1.2rem; - margin: 0 0 1.4rem; -} - -.callout > :last-child { margin-bottom: 0; } - -.callout h3 { - font-size: 1.02rem; - margin-bottom: .35em; -} - -.table-scroll { - overflow-x: auto; - border: 1px solid var(--border); - border-radius: var(--radius); - background: var(--bg-elevated); -} - -table { - width: 100%; - border-collapse: collapse; - margin: 0; - font-size: .96rem; -} - -caption { - caption-side: top; - text-align: start; - padding: .8rem 1rem 0; - color: var(--text-faint); - font-size: .9rem; -} - -th, td { - text-align: start; - padding: .62rem .9rem; - border-bottom: 1px solid var(--border); - vertical-align: top; -} - -thead th { - background: var(--bg-subtle); - font-size: .88rem; - letter-spacing: .04em; - color: var(--text-muted); - white-space: nowrap; -} - -tbody tr:last-child td { - border-bottom: 0; -} - -.yes { color: var(--accent); font-weight: 700; } -.no { color: var(--text-faint); } - -.steps { - counter-reset: step; - list-style: none; - padding: 0; - max-width: var(--measure); -} - -.steps > li { - counter-increment: step; - position: relative; - padding-inline-start: 2.6rem; - margin-bottom: 1.1rem; -} - -.steps > li::before { - content: counter(step); - position: absolute; - inset-inline-start: 0; - top: .18em; - width: 1.8rem; - height: 1.8rem; - display: grid; - place-items: center; - border-radius: 50%; - background: var(--accent-soft); - border: 1px solid var(--border-strong); - border-color: color-mix(in srgb, var(--accent) 35%, transparent); - color: var(--accent); - font-size: .9rem; - font-weight: 700; - font-variant-numeric: tabular-nums; -} - -.steps > li > :last-child { margin-bottom: 0; } - -.faq { - max-width: var(--measure); -} - -.faq > h2 { - font-size: .84rem; - letter-spacing: .14em; - text-transform: uppercase; - color: var(--text-faint); - margin: 2rem 0 .8rem; - padding-bottom: .4rem; - border-bottom: 1px solid var(--border); -} - -.faq > h2:first-child { margin-top: 0; } - -.faq details { - border: 1px solid var(--border); - border-radius: var(--radius); - background: var(--bg-elevated); - padding: 0 1.1rem; - margin-bottom: .7rem; -} - -.faq summary { - cursor: pointer; - font-weight: 620; - padding: .85rem 0; - list-style: none; - display: flex; - gap: .6rem; - align-items: baseline; -} - -.faq summary::-webkit-details-marker { display: none; } - -.faq summary::before { - content: "+"; - color: var(--accent); - font-weight: 700; - flex: none; -} - -.faq details[open] summary::before { content: "-"; } - -.faq details[open] summary { - border-bottom: 1px solid var(--border); -} - -.faq .faq-body { - padding: .9rem 0 .3rem 1.5rem; -} - -.faq .faq-body > :last-child { margin-bottom: 0; } - -.gallery { - display: grid; - gap: 1.1rem; - grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr)); -} - -.kv { - display: grid; - gap: .1rem 1.2rem; - margin: 0 0 1.2rem; -} - -@media (min-width: 620px) { - .kv { grid-template-columns: max-content minmax(0, 1fr); } -} - -.kv dt { - font-weight: 650; - color: var(--text); -} - -.kv dd { - margin: 0 0 .55rem; - color: var(--text-muted); -} - -.pill-list { - display: flex; - flex-wrap: wrap; - gap: .45rem; - list-style: none; - padding: 0; - margin: 0 0 1.2rem; -} - -.pill-list li { - border: 1px solid var(--border); - background: var(--bg-elevated); - border-radius: 999px; - padding: .2rem .78rem; - font-size: .9rem; - color: var(--text-muted); -} - -.cta { - background: var(--bg-subtle); -} - -.cta .wrap { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - gap: 1.2rem; -} - -.cta h2 { margin-bottom: .25em; } -.cta p { margin-bottom: 0; color: var(--text-muted); } -.cta .btn-row { margin-bottom: 0; } - -.site-footer { - border-top: 1px solid var(--border); - background: var(--bg); - padding-block: 2.2rem; - color: var(--text-faint); - font-size: .92rem; -} - -.site-footer .wrap { - display: grid; - gap: 1.4rem; - grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)); -} - -.site-footer h2 { - font-size: .82rem; - letter-spacing: .14em; - text-transform: uppercase; - color: var(--text-faint); - margin-bottom: .6rem; -} - -.site-footer ul { - list-style: none; - padding: 0; - margin: 0; -} - -.site-footer li { margin-bottom: .3rem; } - -.site-footer .legal { - grid-column: 1 / -1; - border-top: 1px solid var(--border); - padding-top: 1.2rem; - margin: 0; - max-width: var(--measure); -} - -.visually-hidden { - position: absolute; - width: 1px; - height: 1px; - margin: -1px; - padding: 0; - overflow: hidden; - clip: rect(0 0 0 0); - white-space: nowrap; - border: 0; -} - -.center { text-align: center; } - -.notfound { - min-height: 52vh; - display: grid; - place-content: center; - text-align: center; -} diff --git a/site/download.html b/site/download.html deleted file mode 100644 index 53b5882..0000000 --- a/site/download.html +++ /dev/null @@ -1,237 +0,0 @@ - - - - - -下载与安装 - PeopleLib - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
- 下载 -

获取 PeopleLib

-

- 所有正式版本都通过项目的 GitHub Releases 发布,不通过 npm 或应用商店分发。 - 当前版本 2.0.0,MIT 许可,免费。 -

-
-

- 前往最新发布页 - 查看全部版本 -

-

请只从上面的 GitHub Releases 页面下载。本站不直接托管安装包。

-
-
- -
-
-
- Windows -

Windows x64 免安装版

-

没有安装程序,不写注册表,解压就能用。

-
-
    -
  1. -

    在 Releases 页面下载 Windows x64 的压缩包。

    -
  2. -
  3. -

    完整解压到一个固定目录。不要在压缩包里直接双击运行,那样程序找不到自己的资源文件。

    -
  4. -
  5. -

    双击目录中的 PeopleLib.exe 启动。

    -
  6. -
  7. -

    保留整个程序目录,不要只把 exe 移到别处。用户数据保存在程序同级的 data/ 目录里。

    -
  8. -
-
-
-

更新到新版本

-

- 先完全退出正在运行的 PeopleLib.exe,再用新版本的文件覆盖程序目录。 - data/ 目录不要替换,书库、笔记与批注都在里面。想换目录的话,把整个目录连 data/ 一起搬走。 -

-
-

- Windows 可能对下载来的可执行文件提示来源不明,这是未购买代码签名证书的常见表现。 - 如果你不确定,可以先在 Releases 页面核对文件来源。 -

-
-
-
- -
-
-
- macOS -

macOS arm64 DMG

-

适用于 Apple Silicon 机型。安装包为 ad-hoc 签名,没有经过 Apple 公证。

-
-
-
-

首次打开会被系统拦下,这是正常的

-

- 应用用的是 ad-hoc 签名,没有付费开发者账号,也没有走 Apple 的公证流程。 - 所以 macOS 首次会提示无法验证开发者,或者说文件已损坏。这不是病毒,而是未公证应用的标准提示, - 按下面的步骤打开一次之后就不会再出现。 -

-
-
-
    -
  1. -

    在 Releases 页面下载 macOS arm64 的 .dmg 文件。

    -
  2. -
  3. -

    双击挂载 DMG,把 PeopleLib 拖进「应用程序」文件夹。

    -
  4. -
  5. -

    - 在「应用程序」里 按住 Control 点击(或右键)PeopleLib 图标,选择「打开」, - 然后在弹出的对话框里再点一次「打开」。直接双击是不行的,必须走右键菜单这条路径, - 因为只有它会给出确认打开的选项。 -

    -
  6. -
  7. -

    - 如果仍然被拦,打开「系统设置」,进入「隐私与安全性」,在页面下方找到刚被阻止的 PeopleLib, - 点击「仍要打开」。 -

    -
  8. -
-
-

- macOS 版的用户数据在 ~/Library/Application Support/PeopleLib,不在应用包里。 - 升级时直接覆盖「应用程序」里的旧版本,数据不受影响。 -

-

Intel 机型目前没有提供构建版本。

-
-
-
- -
-
-
- 上手 -

第一次启动之后做什么

-
-
-
    -
  1. 需要代理就先配代理。设置里配一次,对所有数据源与封面请求同时生效。
  2. -
  3. 试一次检索。可以先只选一个数据源,确认网络通畅,再切到聚合搜索。
  4. -
  5. 导入已有的书。本地已有的 PDF、EPUB 等文件可以直接导入书库,不必来自检索结果。
  6. -
  7. 想用 AI 就填自己的接口。填接口地址与 API Key,选择协议类型。不填也能正常阅读,AI 是可选的。
  8. -
-
-
-
- -
-
-
- 开发者 -

从源码运行

-
-
-

- 源码运行、打包发布、代理与账号配置、数据位置、项目结构与测试说明都在源码仓库的 BUILD.md 里。 - 公开仓库只发布二进制与使用说明。 -

-

运行环境要求 Node.js 22.19.0 或更高版本。

-
-
-
- -
-
-
-

装完遇到问题

-

常见问题页覆盖了 macOS 拦截、数据位置与格式支持。

-
-

- 查看常见问题 - 提交问题反馈 -

-
-
- -
- -
-
-
-

站点

- -
-
-

项目

- -
-
-

版本

-
    -
  • PeopleLib 2.0.0
  • -
  • MIT 许可
  • -
-
- -
-
- - - diff --git a/site/faq.html b/site/faq.html deleted file mode 100644 index 2a0f3a4..0000000 --- a/site/faq.html +++ /dev/null @@ -1,330 +0,0 @@ - - - - - -常见问题 - PeopleLib - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
- 常见问题 -

常见问题

-

点击问题展开答案。没有覆盖到的情况可以到仓库提 issue。

-
- -
- -

费用与账号

- -
- PeopleLib 本身要钱吗 -
-

不要。项目以 MIT 许可开源,没有付费版、订阅或功能解锁。

-
-
- -
- AI 助手要不要花钱 -
-

- 取决于你自己接的服务。应用不内置任何厂商的 AI 服务,也不代收任何费用。 - 你在设置里填自己的接口地址与 API Key,费用由你和那家服务商结算,账单也在他们那边看。 -

-

- 如果你接的是本地跑的模型,或者某个免费额度内的接口,那就不花钱。 - 不填 AI 配置的话,检索、书库与阅读功能都能正常使用,AI 是可选的。 -

-
-
- -
- 需要注册账号吗 -
-

- PeopleLib 自己没有账号体系,不需要注册也不需要登录。 - 个别数据源(例如需要登录才能取下载直链的站点)要用你自己在那个站点的凭据,凭据保存在本地。 -

-
-
- -

数据与隐私

- -
- 我的数据存在哪里 -
-

全部在本机。书库条目、下载的文件、封面缓存、笔记、批注与阅读进度都写在本地数据目录里,没有云端副本。

-
-
Windows
-
程序目录同级的 data/
-
macOS
-
~/Library/Application Support/PeopleLib
-
-

备份就是复制这个目录。彻底删除就是删掉它,不需要在任何地方注销。

-
-
- -
- 我的书和笔记会被上传吗 -
-

- 不会。应用没有服务端,也没有遥测上报。唯一会把正文内容发出去的情况是你主动使用 AI 助手并确认发送, - 目标是你自己填写的那个接口地址。详见隐私与安全。 -

-
-
- -
- AI 会不会把整本书悄悄发出去 -
-

- 不会。上下文范围由你选,选「全文」时无论文档多短都强制弹确认框,并显示确切字数。 - 界面上显示的字数就是真正外发的字数,应用不在本地偷偷截断,也不会在你没确认时发送。 -

-
-
- -
- 我的 API Key 安全吗 -
-

- Key 经操作系统提供的 safeStorage 加密后落盘,不是明文配置文件,也不会进入界面进程, - 日志与错误信息里也不会出现。请求由主进程直接发往你填写的地址,中间没有第三方转发。 -

-
-
- -
- 能完全离线使用吗 -
-

- 可以。已经下载进书库的文件,阅读、批注与笔记都不需要网络。封面已经缓存在本地,离线也能正常显示。 - 需要联网的只有检索、下载、AI 请求与检查更新这四件事。 -

-
-
- -

格式与阅读

- -
- 支持哪些格式 -
-

内置阅读支持四类:

-
    -
  • PDF:页面批注、书签、选文与笔记,渲染画质可调
  • -
  • EPUB:目录、重排、书签、选文与笔记
  • -
  • MOBI / AZW / AZW3:无 DRM 的 MOBI、KF7 与 KF8 内容
  • -
  • TXT / Markdown:自动识别编码,Markdown 渲染标题、列表、代码块与表格
  • -
-

- DJVU、FB2、CBZ、CBR 可以入库与整理,但没有内置阅读器,阅读要调用系统关联应用。 - 完整对照表在首页。 -

-
-
- -
- Kindle 买的书能读吗 -
-

- 带 DRM 的文件不能。PeopleLib 不解除 DRM,遇到受保护的 MOBI、AZW、AZW3、KFX 或 Topaz 文件会明确拒绝, - 并提示改用系统关联的应用打开。无 DRM 的 MOBI、KF7 与 KF8 可以正常阅读。 -

-
-
- -
- 几百兆的 PDF 会不会卡死 -
-

- 超大 PDF 走分段读取,不会把整个文件读进内存,所以打开一本很大的书不会一次性吃掉几百兆内存。 - 如果渲染感觉吃力,可以在底栏把画质档位调低。 -

-
-
- -
- TXT 打开是乱码怎么办 -
-

- 应用会自动探测 UTF-8、带 BOM 与不带 BOM 的 UTF-16,以及 GB18030。 - 如果仍然乱码,说明文件用了这几种之外的编码,可以先用文本编辑器另存为 UTF-8 再导入。 -

-
-
- -
- 笔记能导出吗 -
-

可以。摘录与笔记支持导出,方便带到别的工具里继续整理。

-
-
- -

安装与运行

- -
- macOS 提示无法验证开发者,或者说文件已损坏 -
-

- 这是未公证应用的标准提示,不是文件损坏,也不是病毒。macOS 版用的是 ad-hoc 签名, - 没有付费开发者账号,也没有走 Apple 的公证流程,所以系统首次会拦下来。 -

-
    -
  1. 先把 PeopleLib 拖进「应用程序」文件夹。
  2. -
  3. 按住 Control 点击(或右键)应用图标,选择「打开」,在弹出的对话框里再点一次「打开」。直接双击不行,必须走右键菜单,只有这条路径会给出确认选项。
  4. -
  5. 如果仍被拦,打开「系统设置」,进入「隐私与安全性」,在页面下方找到被阻止的 PeopleLib,点击「仍要打开」。
  6. -
-

成功打开一次之后系统会记住这个选择,之后正常双击即可。

-
-
- -
- Windows 版怎么安装 -
-

- 没有安装程序。下载压缩包后完整解压到一个固定目录,双击里面的 PeopleLib.exe 就能用。 - 不要在压缩包里直接双击运行,也不要把 exe 单独移出来,程序需要同目录下的资源文件。 -

-
-
- -
- 更新版本会不会丢数据 -
-

- 不会,但要注意操作顺序。Windows 上先完全退出正在运行的 PeopleLib.exe,再用新文件覆盖程序目录, - data/ 目录保持原样不要替换。macOS 上数据在 ~/Library/Application Support/PeopleLib, - 直接覆盖「应用程序」里的旧版本即可。 -

-
-
- -
- 能换程序目录或者搬到别的电脑吗 -
-

- Windows 版是便携布局,把整个程序目录连同 data/ 一起复制走就行, - 不要只搬 exe,也不要把 data/ 落在旧目录里。 -

-
-
- -
- 搜不到结果或者下载失败 -
-

- 数据源的可用性取决于站点自身状态。有的站点启用了人机验证,有的接口只提供最新列表而不支持关键词搜索, - 有的下载需要该站点的账号。应用遇到这些情况会给出明确提示。 -

-

如果所有源都不通,先检查设置里的代理配置,它对所有数据源与封面请求统一生效。

-
-
- -
- 有 Linux 版、移动端或浏览器插件吗 -
-

- 目前发布的是 Windows x64 免安装版与 macOS arm64 DMG。没有移动端应用,也没有浏览器插件。 - 项目源码开放,其他平台可自行尝试从源码运行。 -

-
-
- -
-
-
- -
-
-
-

问题没解决

-

到仓库开一个 issue,说明系统版本与复现步骤。

-
-

- 提交问题反馈 - 回到下载页 -

-
-
- -
- -
-
-
-

站点

- -
-
-

项目

- -
-
-

版本

-
    -
  • PeopleLib 2.0.0
  • -
  • MIT 许可
  • -
-
- -
-
- - - diff --git a/site/features.html b/site/features.html deleted file mode 100644 index eb927bc..0000000 --- a/site/features.html +++ /dev/null @@ -1,321 +0,0 @@ - - - - - -功能 - PeopleLib - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
- 功能 -

已经实现的部分

-

这一页只写应用里真实存在的能力。没有列出的功能就是没有做。

-
- -
-
- - - -
-
-
- 归档 -

本地书库

-
-
-
-
收藏与下载
-
检索到的条目可以先收藏,再决定是否下载文件。下载完成后自动登记进书库。
-
封面缓存
-
封面下载后缓存在本地,离线也能正常显示网格视图。
-
书架与标签
-
用书架做粗分类,用标签做交叉归类,同一本书可以属于多个标签。
-
批量整理
-
支持多选,批量移动书架、批量打标签、批量删除。
-
阅读状态
-
记录每本书的阅读状态与进度,重新打开时回到上次的位置。
-
本地导入
-
已有的文件可以直接导入书库,不必来自检索结果。
-
-
-
-
- -
-
-
- 阅读 -

内置阅读器

-

四类格式在应用内直接打开,不需要外部程序。

-
-
-
-

PDF

-
    -
  • 页面批注、书签、选文与笔记
  • -
  • 渲染画质分三档可调并记住选择
  • -
  • 超大文件分段读取,不整文件载入内存
  • -
-
-
-

EPUB

-
    -
  • 目录导航与文字重排
  • -
  • 书签、选文与笔记
  • -
  • 按章节与偏移量记录阅读位置
  • -
-
-
-

MOBI / AZW / AZW3

-
    -
  • 使用 Foliate 解析无 DRM 的 MOBI、KF7 与 KF8
  • -
  • DRM 保护的文件明确拒绝,不尝试绕过
  • -
-
-
-

TXT / Markdown

-
    -
  • 自动识别 UTF-8、UTF-16 与 GB18030 编码
  • -
  • Markdown 渲染标题、列表、代码块与表格
  • -
  • 与 EPUB 同一套重排与定位机制
  • -
-
-
-
- 内置 PDF 阅读器,页面上带有高亮批注,右侧是笔记面板 -
PDF 阅读与批注
-
-
-
- -
-
-
- 批注 -

笔记与批注

-
-
-

- 批注直接落在页面上,和书目条目绑定。笔记分两种形态:读书笔记是文字为主的富文本, - 画布笔记可以自由绘制,并且能把 PDF 的某一页当作底版,在上面圈画标记。 -

-

选中的段落可以摘录进笔记,笔记与摘录支持导出,方便带到别的工具里继续用。

-
-
- PDF 页面上的批注工具与已有的高亮标记 -
批注工具
-
-
-
- -
-
-
- AI -

自带密钥的 AI 助手

-

应用不内置任何厂商服务,接口地址与密钥都由你填。

-
-
-

支持三类协议:Anthropic 的 /messages、OpenAI Responses 的 /responses,以及兼容 Chat 的 /chat/completions。任何符合这三类协议的服务都能接。

-

五种上下文范围

-
    -
  • 选中文本:只发你选中的那一段,需要先选中。
  • -
  • 当前页:发当前页的正文,不依赖选区。
  • -
  • 全文:发整篇正文,不依赖选区,发送前强制确认。
  • -
  • 页面图像:把当前页渲染成图片发送,适合公式与图表。
  • -
  • 框选区域:在页面上拉一个框,只发框内的图像。
  • -
-

关于字数

-

- 正文完整发送,本地不做截断。界面上显示的字数就是真正外发的字数,两者一致。 - 如果超出模型的上下文限制,应用会把接口返回的报错转成中文提示,而不是悄悄少发一部分内容。 -

-

对话支持多轮追问,上下文范围在会话中可以随时切换。

-
-
- AI 助手侧栏,展示上下文范围选择与对话内容 -
AI 助手侧栏与上下文范围选择
-
-
-
- -
-
-
- 网络 -

代理与镜像容错

-
-
-
-
全局代理
-
在设置里配一次,对所有数据源请求与封面请求同时生效,不需要逐个源单独设置。
-
镜像故障转移
-
某个镜像不可用时自动切到下一个,原镜像恢复后自动重新启用,不需要手动重置。
-
失败提示
-
站点侧的人机验证、登录过期与超时都会转成明确的中文提示。
-
-
-
-
- -
-
-
- 维护 -

版本更新

-
-
-

- 可以在设置里手动检查更新,也可以打开启动时自动检查。检查的对象是项目的 GitHub Releases, - 发现新版本后应用会打开对应的下载页。更新前退出旧版本再覆盖程序文件,用户数据目录不需要替换。 -

-
-
-
- -
-
-
- 边界 -

不包含的功能

-

写清楚没有什么,比含糊其辞更省你的时间。

-
-
-
    -
  • 没有云同步,也没有账号体系。
  • -
  • 没有移动端应用,也没有浏览器插件。
  • -
  • 没有多人协作或共享书库。
  • -
  • DJVU、FB2、CBZ、CBR 只能入库与整理,阅读要调用系统关联应用。
  • -
  • 不解除 DRM。受保护的文件会被明确拒绝并提示改用系统关联应用。
  • -
-
-
-
- -
-
-
-

看完想试试

-

下载页有 Windows 与 macOS 的完整安装说明。

-
-

- 前往下载 - 先看隐私说明 -

-
-
- -
- -
-
-
-

站点

- -
-
-

项目

- -
-
-

版本

-
    -
  • PeopleLib 2.0.0
  • -
  • MIT 许可
  • -
-
- -
-
- - - diff --git a/site/index.html b/site/index.html deleted file mode 100644 index b2f131b..0000000 --- a/site/index.html +++ /dev/null @@ -1,280 +0,0 @@ - - - - - -PeopleLib:多源文献检索、本地书库与内置阅读器 - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-

Windows 与 macOS 桌面应用

-

检索、收藏、阅读,都在自己的电脑里完成

-

- PeopleLib 把 12 个公开文献源的检索、详情与下载收进同一个界面,下载的文件直接进本地书库, - 用内置阅读器打开并做批注。书库、笔记与阅读进度都留在你的磁盘上,不经过任何服务器。 -

-

- 下载 PeopleLib - 查看功能 -

-

当前版本 2.0.0,MIT 许可。Windows x64 免安装,macOS arm64 提供 DMG。

-
-
- PeopleLib 书库界面,左侧是书架与标签,右侧网格展示带封面的图书条目 -
本地书库:书架、标签、封面缓存与阅读状态
-
-
-
- -
-
-
- 核心能力 -

一条完整的链路,从找到文献到读完做笔记

-

没有账号,没有云端,没有订阅。每一步都在本机完成。

-
-
-
- 检索 -

多源统一检索

-

12 个数据源共用同一套搜索、详情与下载流程,可指定单个源,也可聚合全部。数据源可用性取决于站点自身状态,程序会给出明确提示而不是静默失败。

-
-
- 归档 -

本地书库

-

收藏条目、下载文件、缓存封面,用书架与标签整理,支持多选批量操作,并记录每本书的阅读状态。

-
-
- 阅读 -

内置阅读器

-

PDF、EPUB、无 DRM 的 MOBI/AZW/AZW3 与 TXT/Markdown 都在应用内打开,保留进度、书签与选文。超大 PDF 分段读取,不把整个文件读进内存。

-
-
- 批注 -

笔记与批注

-

读书笔记与画布笔记两种形态,画布笔记可以拿 PDF 页面当底版自由标注,摘录与笔记可导出。

-
-
- AI -

自带密钥的 AI 助手

-

填自己的接口地址与 API Key,支持选中文本、当前页、全文、页面图像与框选区域五种上下文范围,可多轮追问。

-
-
- 网络 -

代理与镜像容错

-

代理一处配置对所有数据源与封面请求生效。镜像失效自动切换,恢复后自动重新启用。

-
-
-
-
- -
-
-
- 设计取舍 -

本地优先不是宣传语,是架构约束

-

这些限制写在代码的边界里,不是可以在设置里关掉的开关。

-
-
-
-

数据不出本机

-

书库、笔记、批注与阅读进度都存在本地文件里。应用没有账号体系,也没有同步服务端。

-
-
-

AI 密钥由系统加密保管

-

没有内置任何厂商服务。API Key 经操作系统的 safeStorage 加密后落盘,不进渲染进程,不写日志。

-
-
-

正文不会自动外发

-

只有你确认后才发送。选择「全文」范围时无论文档多短都强制弹确认框,并显示确切字数。

-
-
-

渲染层拿不到任意路径

-

界面进程不能直接读盘,所有文件访问都要经主进程白名单校验,只放行书库中真实登记的条目。

-
-
-

阅读完整的隐私与安全说明

-
-
- -
-
-
- 界面预览 -

真实截图

-

以下画面来自 Windows 版本,界面为中文。

-
- -
-
- -
-
-
- 格式 -

能读什么,能管什么

-

入库管理与内置阅读是两件事,下表分开列出,不夸大支持范围。

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
格式支持对照
格式入库与管理内置阅读说明
PDF支持支持页面批注、书签、选文与笔记,可调渲染画质
EPUB支持支持目录、重排、书签、选文与笔记
MOBI / AZW / AZW3支持支持使用 Foliate 解析无 DRM 的 MOBI、KF7 与 KF8 内容
TXT / Markdown支持支持自动识别编码,Markdown 渲染标题、列表、代码块与表格
DJVU / FB2 / CBZ / CBR支持不支持可入库整理,阅读需调用系统关联应用
-
-

DRM 保护的 MOBI/AZW/AZW3、KFX、Topaz 以及损坏或不兼容的文件不会尝试绕过保护,应用会明确提示改用系统关联应用打开。

-
-
- -
-
-
-

准备好开始了

-

Windows 免安装解压即用,macOS 提供 arm64 DMG。

-
-

- 前往下载 - GitHub Releases -

-
-
- -
- -
-
-
-

站点

- -
-
-

项目

- -
-
-

版本

-
    -
  • PeopleLib 2.0.0
  • -
  • MIT 许可
  • -
-
- -
-
- - - diff --git a/site/privacy.html b/site/privacy.html deleted file mode 100644 index 5200e48..0000000 --- a/site/privacy.html +++ /dev/null @@ -1,292 +0,0 @@ - - - - - -隐私与安全 - PeopleLib - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
- 隐私与安全 -

你的书库只属于你

-

- 下面这些不是隐私政策的模板文字,而是应用实际的架构约束。它们决定了某些事情在这个程序里做不到, - 哪怕以后想加也要先推翻现有设计。 -

-
-
-
-

一句话总结

-

- PeopleLib 没有服务端。它唯一会主动联网的场合是你按下搜索或下载,以及你启用检查更新。 - AI 请求发往你自己填的接口地址。除此之外没有任何数据外发。 -

-
-
-
-
- -
-
-
- 本地优先 -

数据存在哪里

-
-
-

书库条目、下载的文件、封面缓存、读书笔记、画布笔记、批注与阅读进度,全部写在本机的数据目录里。没有账号,没有同步服务端,没有遥测上报。

-

AI 对话记录同样只存在本机,按会话分别存成文件,随书籍删除一并清理。发给视觉模型的图像也留在本地,供你回看历史时显示,后续追问不会重复上传。

-
-
Windows
-
程序目录同级的 data/,便携布局。整个程序目录可以整体搬走或备份。
-
macOS
-
~/Library/Application Support/PeopleLib。应用包在 DMG 里是只读的,数据不能放里面,升级覆盖也不会碰到这个目录。
-
-

想彻底删除数据,删掉上面对应的目录即可,不需要在任何地方注销账号。

-
-
-
- -
-
-
- AI 密钥 -

完全自带密钥,没有中间人

-
-
-

- 应用没有内置任何厂商服务,也没有代理转发层。你在设置里填自己的接口地址与 API Key, - 请求从你的机器直接发到你填的那个地址。项目方看不到你的用量,也收不到你的费用。 -

-
    -
  • 密钥加密落盘:API Key 经操作系统提供的 safeStorage 加密后保存,不是明文配置文件。
  • -
  • 密钥不进渲染进程:界面进程拿不到密钥原文,实际请求由主进程发出。
  • -
  • 密钥不写日志:日志与错误信息里不会出现密钥或 token。
  • -
  • Responses 协议不留存:走 OpenAI Responses 协议时请求带 store: false,不让服务端留存对话记录。
  • -
-

注意:你选择的模型提供方会按它自己的政策处理收到的内容。PeopleLib 能控制的是不多发、不留副本、不经第三方转发。

-
-
-
- -
-
-
- 明确确认 -

正文不会自动外发

-
-
-

- 打开一本书不会触发任何 AI 请求。只有你选定上下文范围并点击发送,内容才会离开本机。 -

-
    -
  • 选择「全文」范围时,无论文档多短都会强制弹出确认框,并提示可能超出模型的上下文限制。
  • -
  • 确认框里显示的字数就是真正外发的字数。应用不做本地截断,界面数字与实际请求一致。
  • -
  • 如果内容超出模型限制,应用把接口返回的报错转成中文提示,而不是悄悄少发一部分。
  • -
-

- 这条规则有过教训。早期版本按固定字数上限挖掉正文中间部分,结果 40 页的文档实际只发出 8 页, - 而界面仍显示全文字数。那属于静默的数据丢失,用户还会把答非所问归因于模型,所以该行为已经移除。 -

-
-
- 发送前的确认对话框,显示即将外发的字数与超出上下文限制的提示 -
发送确认框
-
-
-
- -
-
-
- 进程边界 -

界面进程读不到任意文件

-
-
-

- Electron 应用最常见的风险是渲染进程能拿着任意路径去读盘。PeopleLib 把这条路堵住了: - 渲染层不能传任意路径,所有文件访问都要经主进程的白名单校验,只放行书库中真实登记的条目。 -

-
    -
  • 路径白名单:不在书库里的路径一律拒绝,无论渲染层怎么构造参数。
  • -
  • 会话与发送方绑定:超大 PDF 的分段读取会话与请求方绑定,句柄不透明,界面窗口销毁后立即回收。
  • -
  • Markdown 双重净化:Markdown 渲染禁用裸 HTML,再过一层 DOMPurify,防止文件内容里的脚本被执行。
  • -
-
-
-
- -
-
-
- 合规 -

不绕过 DRM

-
-
-

- 带 DRM 的 MOBI、AZW、AZW3、KFX 与 Topaz 文件,以及损坏或不兼容的文件,应用不会尝试解除保护。 - 遇到这类文件会直接给出提示,建议改用系统关联的应用打开。 -

-

- 同样地,PeopleLib 只是对公开网络接口的客户端封装,不托管也不分发任何内容。 - 各数据源所提供作品的版权状态因司法辖区而异,使用者需自行确保用法符合当地法律与各站点的服务条款。 -

-
-
-
- -
-
-
- 联网范围 -

什么时候会联网

-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
应用发起网络请求的全部场合
场合目标触发方式
检索与下载你选择的数据源站点你点击搜索或下载
封面加载条目对应的封面地址展示书库或检索结果
AI 请求你自己填写的接口地址你确认发送
检查更新项目的 GitHub Releases手动检查,或你启用了启动时检查
-
-

所有这些请求都走你在设置里配置的全局代理,包括封面请求。

-
-
-
- -
-
-
- 本站点 -

这个网站本身

-
-
-

- 本站是托管在 GitHub Pages 上的静态页面,没有分析脚本,没有 Cookie,没有第三方字体或 CDN 资源, - 页面用的是你系统里已有的字体。作为托管方,GitHub 会按其自身政策记录访问日志,这一点本站无法控制。 -

-
-
-
- -
-
-
-

还有疑问

-

常见问题里覆盖了费用、数据位置、格式支持与 macOS 首次打开。

-
-

- 查看常见问题 - 下载与安装 -

-
-
- -
- -
-
-
-

站点

- -
-
-

项目

- -
-
-

版本

-
    -
  • PeopleLib 2.0.0
  • -
  • MIT 许可
  • -
-
- -
-
- - - diff --git a/site/sitemap.xml b/site/sitemap.xml deleted file mode 100644 index 25b1180..0000000 --- a/site/sitemap.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - https://reader.mesalogo.com/ - monthly - 1.0 - - - https://reader.mesalogo.com/features.html - monthly - 0.8 - - - https://reader.mesalogo.com/privacy.html - monthly - 0.8 - - - https://reader.mesalogo.com/download.html - monthly - 0.9 - - - https://reader.mesalogo.com/faq.html - monthly - 0.7 - -