diff --git a/README.md b/README.md index 2a1598b..3a2e7ef 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,35 @@ -# Anchor Deck +
+
+
让每一页,重新可编辑。
-[在线体验](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)    -AI 很会生成漂亮的 HTML slides,但后续改字、换图、挪布局通常很痛苦。Anchor Deck 解决的就是这一步:上传、适配、下载,然后直接在浏览器里继续编辑。 +AI 很会生成漂亮的 HTML slides,但后续改字、换图、挪布局通常很痛苦。帆页 Anchor Deck 解决的就是这一步:上传、适配、编辑和保存,让一次性 HTML 可以继续修改。 -Anchor Deck 提供网页转换和 MCP 工作区两种用法。两者共享同一个浏览器编辑器,但入口、AI 来源和配置彼此独立。 +帆页围绕同一套 HTML 检测、转换和浏览器编辑器,提供在线工作台、网页转换器和 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 默认不长期保存。
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] || "";