From a0281ba3445e2edb68b10a774b55de86d57b9107 Mon Sep 17 00:00:00 2001 From: wengzige <260234670+wengzige@users.noreply.github.com> Date: Tue, 14 Jul 2026 08:59:12 +0800 Subject: [PATCH 1/2] docs: integrate online workspace into README --- README.md | 54 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 2a1598b..3a2e7ef 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,35 @@ -# Anchor Deck +

+ 帆页 Anchor Deck 图标 +

-**把“能看”的 HTML 演示稿,变成“还能继续改”的本地 deck。** +

帆页 Anchor Deck

-Turn AI-generated HTML presentations into editable local decks. +

让每一页,重新可编辑。

-[在线体验](https://wengzige.github.io/html-deck-editor/) · [English](README.en.md) · [结构契约](docs/html-deck-contract.md) · [隐私说明](docs/privacy.md) +Turn AI-generated HTML presentations into editable decks. + +[在线工作台](https://anchordeck.dpdns.org/) · [网页转换器](https://wengzige.github.io/html-deck-editor/) · [English](README.en.md) · [品牌规范](docs/brand/README.md) · [结构契约](docs/html-deck-contract.md) · [隐私说明](docs/privacy.md) ![License](https://img.shields.io/badge/license-MIT-0f172a) ![Local First](https://img.shields.io/badge/local--first-browser-2563eb) ![AI Adaptation](https://img.shields.io/badge/AI-smart%20adaptation-14b8a6) -AI 很会生成漂亮的 HTML slides,但后续改字、换图、挪布局通常很痛苦。Anchor Deck 解决的就是这一步:上传、适配、下载,然后直接在浏览器里继续编辑。 +AI 很会生成漂亮的 HTML slides,但后续改字、换图、挪布局通常很痛苦。帆页 Anchor Deck 解决的就是这一步:上传、适配、编辑和保存,让一次性 HTML 可以继续修改。 -Anchor Deck 提供网页转换和 MCP 工作区两种用法。两者共享同一个浏览器编辑器,但入口、AI 来源和配置彼此独立。 +帆页围绕同一套 HTML 检测、转换和浏览器编辑器,提供在线工作台、网页转换器和 MCP 工作区三种入口。在线工作台适合直接续做账户项目;网页转换器和 MCP 则保留本地优先的工作流: | 网页转换 | MCP 工作区 | | --- | --- | | 网页转换截图 | MCP 工作区截图 | | 上传 HTML / ZIP / 文件夹,生成可编辑 ZIP。 | 在 Codex、Claude 或 WorkBuddy 里打开本地工作区,直接让 AI 修改当前 deck。 | -## 两种使用方式 +## 三种使用方式 | 方式 | 适合场景 | AI 来源 | API 配置 | | --- | --- | --- | --- | +| **在线工作台** | 在线打开单个 HTML,在账户项目中继续编辑并按需同步 | 平台 AI 或自己的 API | 平台 AI 可直接使用账户额度;自己的配置加密保存到账户 | | **网页转换** | 上传 HTML / ZIP / 文件夹,生成可编辑 ZIP | 网页调用你选择的模型 | AI 智能适配需要自己的 API Key | | **MCP 工作区** | 让 Codex、Claude Code、Claude Desktop 或 WorkBuddy 直接修改本地 deck | 当前 AI 客户端 | 不需要在网页配置 API | -两条线可以单独使用。网页转换不依赖 MCP;MCP 复用同一套文件检测、转换和浏览器编辑器,并额外提供本地工作区与智能体工具。 +三种入口可以单独使用,但不是三套编辑器。在线工作台在公共编辑核心之上增加账户项目、跨浏览器同步、平台额度和 AI 助手;网页转换器专注浏览器本地转换;MCP 工作区再接入本机文件与当前 AI 客户端。 @@ -206,14 +211,22 @@ AI 智能适配主要完成: 这套流程把 AI 用在“理解复杂 HTML”上,再由本地转换器校验 selector、写入标记、保留资源并完成打包。它能显著提升复杂 HTML 的适配成功率;遇到结构特别混乱或只有整页截图的文件时,仍建议在预览里复核页数、顺序和可编辑区域。 -网页模式和 MCP 模式的 AI 来源不同: +三种入口的 AI 来源不同: | 模式 | 谁分析 HTML | 怎么启动 | | --- | --- | --- | +| 在线工作台 | 平台 AI 或账户中加密保存的自有 API | 从工作区启动 **AI 智能适配**,或在编辑器中打开 **AI 助手** | | 网页 | 你配置的 API | 点击 **AI 智能适配** | | MCP | 当前 AI 客户端(Codex / Claude / WorkBuddy) | 点击 **复制智能适配指令**,粘贴到 AI 输入框并发送 | -### 网页使用步骤 +### 在线工作台使用步骤 + +1. 打开[在线工作台](https://anchordeck.dpdns.org/),登录后导入单个 HTML / HTM。 +2. 在设置中选择平台 AI 或自己的 API;平台 AI 按成功调用扣除账户额度,失败自动退款。 +3. 需要优化复杂结构时启动 **AI 智能适配** 并先核对预览;进入编辑器后也可以用 **AI 助手** 做小范围修改。 +4. 完成一轮修改后点击 **保存到账户** 或 **同步当前修改**;编辑过程不是实时自动保存,重要项目请同时导出本地 HTML。 + +### 网页转换器使用步骤 1. 打开在线页面或本地页面。 2. 上传 HTML / ZIP / 文件夹。 @@ -229,7 +242,7 @@ AI 智能适配主要完成: 3. AI 调用 `prepare_ai_adaptation` 读取摘要,生成计划,再调用 `apply_ai_adaptation` 写入本地 HTML。 4. 打开工作区复核页数、顺序和可编辑区域。 -### 网页模式 AI API 配置 +### 网页转换器 AI API 配置 网页模式采用 BYOK(Bring Your Own Key): @@ -242,12 +255,13 @@ AI 智能适配主要完成: ## 其他功能亮点 -- **本地优先**:文件默认只在浏览器里读取、改写和打包。 +- **网页转换本地优先**:文件默认只在浏览器里读取、改写和打包。 - **一键转可编辑**:上传 HTML / ZIP / 文件夹,下载新的可编辑 ZIP。 - **AI 智能适配**:复杂 HTML 可以先用 AI 识别页面、文本、图片和视觉块,再生成更适合编辑器的结构。 +- **在线账户工作区**:在线版支持账户项目、跨浏览器读取最近一次同步版本、平台 AI 额度或自有 API,并继续复用同一套编辑器。 - **保留原资源**:尽量保留原始 CSS、JS、图片、字体和相对路径。 - **静态文件输出**:生成结果仍然是普通 HTML,可保存、转发、托管。 -- **BYOK AI / MCP 智能体**:网页模式使用你自己的 API Key;MCP 模式由当前 AI 客户端生成适配计划。 +- **灵活的 AI 来源**:在线版可选平台额度或自有 API,网页转换器使用自己的 API Key,MCP 则由当前 AI 客户端生成适配计划。 - **for-ai.md 交接**:在编辑器里给元素写批注,一键导出给外部 AI 的修改上下文。 - **字体库与字体导入**:内置常用中文字体栈,可选联网开源字体,也可导入 WOFF2 / WOFF / TTF / OTF。 - **PDF / 图片导出**:自由勾选页面,导出 PDF、PNG 或 JPG;多页图片自动打包 ZIP。 @@ -284,7 +298,15 @@ Anchor Deck 适合这些场景: - 你想把静态 HTML 演示稿做成可编辑交付物,而不是一次性页面。 - 你正在做 AI PPT / HTML deck 工作流,需要一个“生成后可编辑”的落地点。 -## 在线使用 +## 在线工作台与网页转换器 + +### 在线工作台 + +[https://anchordeck.dpdns.org/](https://anchordeck.dpdns.org/) + +当前在线版面向横屏 PC,支持导入单个 HTML / HTM,完成编辑、批注、智能适配、AI 辅助、账户保存和导出。登录后的账户项目可在不同浏览器间读取最近一次成功同步的版本;当前修改需要主动点击保存,尚不提供实时自动保存、历史版本或分享链接。ZIP 和文件夹请继续使用下面的网页转换器或 MCP 工作区。 + +### 网页转换器 打开: @@ -368,8 +390,10 @@ Anchor Deck 适合这些场景: ## 隐私与安全 -- 文件默认只在浏览器本地处理。 +- 使用网页转换器时,文件默认只在浏览器本地处理。 +- 使用在线工作台时,登录、账户项目、平台 AI 和主动保存需要由服务器处理;未登录项目只保存在当前浏览器。兼容性改进的数据范围和开关会在在线版设置中明确显示。 - 项目不会内置作者 API Key。 +- 在线工作台可以使用平台 AI,也可以把自己的 API 配置加密保存到账户;页面不会返回密钥明文。 - 网页模式的 AI 适配只在用户配置 API 后才会调用用户选择的服务商或代理。 - MCP 模式不要求网页 API Key;当前 AI 客户端读取结构摘要、生成计划,MCP 在本机应用计划。 - API Key 默认不长期保存。 From de71b325bb4dda7c2e3c9042d438af2045731cd9 Mon Sep 17 00:00:00 2001 From: wengzige <260234670+wengzige@users.noreply.github.com> Date: Tue, 14 Jul 2026 09:18:02 +0800 Subject: [PATCH 2/2] style: align injected editor with brand palette --- src/runtime/html-deck-editor.css | 78 ++++++++++++++++---------------- src/test/runtime-css.test.js | 9 +++- 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/src/runtime/html-deck-editor.css b/src/runtime/html-deck-editor.css index 1892479..79ff25d 100644 --- a/src/runtime/html-deck-editor.css +++ b/src/runtime/html-deck-editor.css @@ -18,7 +18,7 @@ width: 42px; height: 42px; border: 1.5px solid var(--ink, #111111); - background: var(--paper, #f8f6ef); + background: var(--paper, #f3f0e7); color: var(--ink, #111111); font: 500 18px/1 var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace); opacity: 0; @@ -120,7 +120,7 @@ pointer-events: auto; border: 1px solid rgba(17, 17, 17, 0.12); border-radius: 8px; - background: rgba(248, 246, 239, 0.97); + background: rgba(243, 240, 231, 0.97); box-shadow: 0 10px 26px rgba(17, 17, 17, 0.10); backdrop-filter: blur(10px); } @@ -213,7 +213,7 @@ .editor-shell[data-html-deck-editor-ui] .toolbar-help-button:focus-visible, .editor-shell[data-html-deck-editor-ui] .frame-delete:focus-visible, .editor-shell[data-html-deck-editor-ui] .shape-choice:focus-visible { - outline: 2px solid rgba(31, 43, 224, 0.34); + outline: 2px solid rgba(23, 98, 74, 0.34); outline-offset: 2px; } @@ -238,9 +238,9 @@ } .editor-shell[data-html-deck-editor-ui] .format-brush-button[aria-pressed="true"] { - border-color: rgba(31, 43, 224, 0.45); - background: rgba(31, 43, 224, 0.08); - color: #1f2be0; + border-color: rgba(23, 98, 74, 0.45); + background: rgba(23, 98, 74, 0.08); + color: #17624a; } .editor-shell[data-html-deck-editor-ui] .shape-picker-wrap { @@ -355,7 +355,7 @@ right: 0; height: 3px; border-radius: 2px; - background: #1f2be0; + background: #17624a; pointer-events: none; } @@ -369,7 +369,7 @@ .editor-shell[data-html-deck-editor-ui] .slide-chip.active { border-color: var(--ink, #111111); - background: rgba(31, 43, 224, 0.08); + background: rgba(23, 98, 74, 0.08); } .editor-shell[data-html-deck-editor-ui] .slide-chip-num { @@ -482,7 +482,7 @@ .editor-shell[data-html-deck-editor-ui] .text-style-button[aria-pressed="true"] { border-color: var(--ink, #111111); - background: rgba(31, 43, 224, 0.1); + background: rgba(23, 98, 74, 0.1); } .editor-shell[data-html-deck-editor-ui] .text-style-italic { @@ -531,7 +531,7 @@ .editor-shell[data-html-deck-editor-ui] .color-preset[aria-checked="true"] { border-color: #111111; - box-shadow: 0 0 0 2px rgba(31, 43, 224, 0.18), inset 0 0 0 1px rgba(255, 255, 255, 0.42); + box-shadow: 0 0 0 2px rgba(23, 98, 74, 0.18), inset 0 0 0 1px rgba(255, 255, 255, 0.42); } .editor-shell[data-html-deck-editor-ui] .background-field { @@ -705,9 +705,9 @@ place-items: center; min-height: 78px; padding: 12px; - border: 1.5px dashed rgba(31, 43, 224, 0.5); + border: 1.5px dashed rgba(23, 98, 74, 0.5); border-radius: 8px; - background: rgba(31, 43, 224, 0.05); + background: rgba(23, 98, 74, 0.05); color: rgba(17, 17, 17, 0.68); font-size: 13px; line-height: 1.38; @@ -716,7 +716,7 @@ .editor-shell[data-html-deck-editor-ui] .drop-zone.dragging { border-color: var(--ink, #111111); - background: rgba(31, 43, 224, 0.13); + background: rgba(23, 98, 74, 0.13); color: var(--ink, #111111); } @@ -766,7 +766,7 @@ .editor-shell[data-html-deck-editor-ui] .comment-card:hover, .editor-shell[data-html-deck-editor-ui] .comment-card:focus-visible { - border-color: rgba(31, 43, 224, 0.34); + border-color: rgba(23, 98, 74, 0.34); outline: none; } @@ -896,11 +896,11 @@ } .editor-shell[data-html-deck-editor-ui] .editor-zoom-reset:hover { - background: #1f2be0; + background: #17624a; } .editor-shell[data-html-deck-editor-ui] .editor-zoom-reset:focus-visible { - outline: 2px solid rgba(31, 43, 224, 0.42); + outline: 2px solid rgba(23, 98, 74, 0.42); outline-offset: 3px; } @@ -1042,8 +1042,8 @@ } .editor-shell[data-html-deck-editor-ui] .editor-export-page:has(input:checked) { - border-color: rgba(31, 43, 224, 0.46); - background: rgba(31, 43, 224, 0.055); + border-color: rgba(23, 98, 74, 0.46); + background: rgba(23, 98, 74, 0.055); } .editor-shell[data-html-deck-editor-ui] .editor-export-page-number { @@ -1083,27 +1083,27 @@ width: max-content; min-height: 34px; padding: 0 13px; - border: 1px solid #1f2be0; + border: 1px solid #17624a; border-radius: 6px; - background: #1f2be0; + background: #17624a; color: #ffffff; font: 700 13px/1 var(--font-body, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif); text-decoration: none; } .editor-shell[data-html-deck-editor-ui] .editor-export-download.secondary { - border-color: rgba(31, 43, 224, 0.24); - background: rgba(31, 43, 224, 0.07); - color: #1f2be0; + border-color: rgba(23, 98, 74, 0.24); + background: rgba(23, 98, 74, 0.07); + color: #17624a; } .editor-shell[data-html-deck-editor-ui] .editor-export-progress-panel { display: grid; gap: 9px; padding: 12px 14px; - border: 1px solid rgba(31, 43, 224, 0.18); + border: 1px solid rgba(23, 98, 74, 0.18); border-radius: 6px; - background: rgba(31, 43, 224, 0.05); + background: rgba(23, 98, 74, 0.05); } .editor-shell[data-html-deck-editor-ui] .editor-export-modal[aria-busy="true"] .editor-export-progress-panel { @@ -1111,8 +1111,8 @@ align-content: center; gap: 12px; padding: 24px; - border-color: rgba(31, 43, 224, 0.24); - background: rgba(31, 43, 224, 0.06); + border-color: rgba(23, 98, 74, 0.24); + background: rgba(23, 98, 74, 0.06); } .editor-shell[data-html-deck-editor-ui] .editor-export-progress-panel[hidden] { @@ -1129,7 +1129,7 @@ } .editor-shell[data-html-deck-editor-ui] .editor-export-progress-kicker { - color: #1f2be0; + color: #17624a; font: 700 12px/1.3 var(--font-body, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif); letter-spacing: 0.04em; } @@ -1146,23 +1146,23 @@ overflow: hidden; border: 0; border-radius: 999px; - background: rgba(31, 43, 224, 0.12); - accent-color: #1f2be0; + background: rgba(23, 98, 74, 0.12); + accent-color: #17624a; } .editor-shell[data-html-deck-editor-ui] .editor-export-progress::-webkit-progress-bar { border-radius: 999px; - background: rgba(31, 43, 224, 0.12); + background: rgba(23, 98, 74, 0.12); } .editor-shell[data-html-deck-editor-ui] .editor-export-progress::-webkit-progress-value { border-radius: 999px; - background: #1f2be0; + background: #17624a; } .editor-shell[data-html-deck-editor-ui] .editor-export-progress::-moz-progress-bar { border-radius: 999px; - background: #1f2be0; + background: #17624a; } .editor-shell[data-html-deck-editor-ui] .editor-export-progress-panel p { @@ -1181,7 +1181,7 @@ .editor-shell[data-html-deck-editor-ui] .editor-export-cancel-running { flex: 0 0 auto; min-width: 88px; - border-color: rgba(31, 43, 224, 0.28); + border-color: rgba(23, 98, 74, 0.28); } .editor-shell[data-html-deck-editor-ui] .editor-export-modal[data-export-cancel="pending"] .editor-export-progress-panel { @@ -1291,7 +1291,7 @@ width: 520px; height: 320px; border: 1.5px solid var(--ink, #111111); - background: var(--paper, #f8f6ef); + background: var(--paper, #f3f0e7); overflow: hidden; } @@ -1306,7 +1306,7 @@ width: 280px; height: 180px; border: 1.5px solid var(--ink, #111111); - background: rgba(31, 43, 224, 0.16); + background: rgba(23, 98, 74, 0.16); } .shape-layer[data-shape="roundRect"] { @@ -1865,7 +1865,7 @@ body.editing:not(.html-deck-editor-exporting) [data-editable], body.editing:not(.html-deck-editor-exporting) [data-editor-kind="text"] { - outline: 1px dashed rgba(31, 43, 224, 0.92); + outline: 1px dashed rgba(23, 98, 74, 0.92); outline-offset: 4px; box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.92), @@ -1886,7 +1886,7 @@ body.editing:not(.html-deck-editor-exporting) [data-editor-kind="media"], body.editing:not(.html-deck-editor-exporting) [data-editor-kind="box"], body.editing:not(.html-deck-editor-exporting) .editor-layer { - outline: 1px dashed rgba(31, 43, 224, 0.92); + outline: 1px dashed rgba(23, 98, 74, 0.92); outline-offset: 4px; box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.92), @@ -1919,7 +1919,7 @@ } body.editing:not(.html-deck-editor-exporting).dragging-file .slide.active { - box-shadow: inset 0 0 0 12px rgba(31, 43, 224, 0.14); + box-shadow: inset 0 0 0 12px rgba(23, 98, 74, 0.14); } @media (max-width: 960px) { diff --git a/src/test/runtime-css.test.js b/src/test/runtime-css.test.js index cb2153e..6808c9d 100644 --- a/src/test/runtime-css.test.js +++ b/src/test/runtime-css.test.js @@ -24,11 +24,18 @@ describe("editor runtime css", () => { it("keeps edit outlines normally but disables every editing-only rule during export", () => { expect(editorCss).toContain("body.editing:not(.html-deck-editor-exporting) [data-editable]"); - expect(editorCss).toContain("outline: 1px dashed rgba(31, 43, 224, 0.92);"); + expect(editorCss).toContain("outline: 1px dashed rgba(23, 98, 74, 0.92);"); const unguardedEditingSelectors = editorCss.match(/body\.editing(?!:not\(\.html-deck-editor-exporting\))/g) || []; expect(unguardedEditingSelectors).toEqual([]); }); + it("uses the shared brand palette for injected editor chrome", () => { + expect(editorCss).toContain("background: var(--paper, #f3f0e7);"); + expect(editorCss).toContain("background: #17624a;"); + expect(editorCss).not.toContain("#1f2be0"); + expect(editorCss).not.toContain("rgba(31, 43, 224"); + }); + it("hides preserved host chrome only while the editor is active", () => { const rule = editorCss.match(/body\.editing:not\(\.html-deck-editor-exporting\) \[data-html-deck-editor-host-chrome\] \{[\s\S]*?\n \}/)?.[0] || "";