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
54 changes: 39 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
# Anchor Deck
<p align="center">
<img src="docs/brand/anchor-deck-icon.svg" alt="帆页 Anchor Deck 图标" width="112" height="112">
</p>

**把“能看”的 HTML 演示稿,变成“还能继续改”的本地 deck。**
<h1 align="center">帆页 Anchor Deck</h1>

Turn AI-generated HTML presentations into editable local decks.
<p align="center"><strong>让每一页,重新可编辑。</strong></p>

[在线体验](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 工作区 |
| --- | --- |
| <img src="https://cdn.jsdelivr.net/gh/wengzige/html-deck-editor@main/docs/images/readme/web-converter.jpg" alt="网页转换截图" width="100%" loading="lazy" decoding="async"> | <img src="https://cdn.jsdelivr.net/gh/wengzige/html-deck-editor@main/docs/images/readme/codex-workspace.jpg" alt="MCP 工作区截图" width="100%" loading="lazy" decoding="async"> |
| 上传 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 客户端

<a id="anchor-deck-mcp"></a>

Expand Down Expand Up @@ -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 / 文件夹。
Expand All @@ -229,7 +242,7 @@ AI 智能适配主要完成:
3. AI 调用 `prepare_ai_adaptation` 读取摘要,生成计划,再调用 `apply_ai_adaptation` 写入本地 HTML。
4. 打开工作区复核页数、顺序和可编辑区域。

### 网页模式 AI API 配置
### 网页转换器 AI API 配置

网页模式采用 BYOK(Bring Your Own Key):

Expand All @@ -242,12 +255,13 @@ AI 智能适配主要完成:

## 其他功能亮点

- **本地优先**:文件默认只在浏览器里读取、改写和打包。
- **网页转换本地优先**:文件默认只在浏览器里读取、改写和打包。
- **一键转可编辑**:上传 HTML / ZIP / 文件夹,下载新的可编辑 ZIP。
- **AI 智能适配**:复杂 HTML 可以先用 AI 识别页面、文本、图片和视觉块,再生成更适合编辑器的结构。
- **在线账户工作区**:在线版支持账户项目、跨浏览器读取最近一次同步版本、平台 AI 额度或自有 API,并继续复用同一套编辑器。
- **保留原资源**:尽量保留原始 CSS、JS、图片、字体和相对路径。
- **静态文件输出**:生成结果仍然是普通 HTML,可保存、转发、托管。
- **BYOK AI / MCP 智能体**:网页模式使用你自己的 API KeyMCP 模式由当前 AI 客户端生成适配计划。
- **灵活的 AI 来源**:在线版可选平台额度或自有 API,网页转换器使用自己的 API KeyMCP 则由当前 AI 客户端生成适配计划。
- **for-ai.md 交接**:在编辑器里给元素写批注,一键导出给外部 AI 的修改上下文。
- **字体库与字体导入**:内置常用中文字体栈,可选联网开源字体,也可导入 WOFF2 / WOFF / TTF / OTF。
- **PDF / 图片导出**:自由勾选页面,导出 PDF、PNG 或 JPG;多页图片自动打包 ZIP。
Expand Down Expand Up @@ -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 工作区。

### 网页转换器

打开:

Expand Down Expand Up @@ -368,8 +390,10 @@ Anchor Deck 适合这些场景:

## 隐私与安全

- 文件默认只在浏览器本地处理。
- 使用网页转换器时,文件默认只在浏览器本地处理。
- 使用在线工作台时,登录、账户项目、平台 AI 和主动保存需要由服务器处理;未登录项目只保存在当前浏览器。兼容性改进的数据范围和开关会在在线版设置中明确显示。
- 项目不会内置作者 API Key。
- 在线工作台可以使用平台 AI,也可以把自己的 API 配置加密保存到账户;页面不会返回密钥明文。
- 网页模式的 AI 适配只在用户配置 API 后才会调用用户选择的服务商或代理。
- MCP 模式不要求网页 API Key;当前 AI 客户端读取结构摘要、生成计划,MCP 在本机应用计划。
- API Key 默认不长期保存。
Expand Down
78 changes: 39 additions & 39 deletions src/runtime/html-deck-editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}

Expand All @@ -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 {
Expand Down Expand Up @@ -355,7 +355,7 @@
right: 0;
height: 3px;
border-radius: 2px;
background: #1f2be0;
background: #17624a;
pointer-events: none;
}

Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -1083,36 +1083,36 @@
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 {
min-height: 164px;
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] {
Expand All @@ -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;
}
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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;
}

Expand All @@ -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"] {
Expand Down Expand Up @@ -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),
Expand All @@ -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),
Expand Down Expand Up @@ -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) {
Expand Down
Loading
Loading