From 44c12622f99365456554b242e3fa808a98f58c56 Mon Sep 17 00:00:00 2001 From: Learnmore-smart Date: Mon, 8 Jun 2026 06:52:42 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20AI=20=E9=A9=B1=E5=8A=A8=E7=9A=84?= =?UTF-8?q?=E6=8E=92=E7=89=88=E6=B5=B7=E6=8A=A5=E4=B8=8E=E5=8F=A5=E5=AD=90?= =?UTF-8?q?=E4=BA=91=E7=94=9F=E6=88=90=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: traeagent --- .../checklist.md | 26 + .../spec.md | 124 ++ .../tasks.md | 45 + index.html | 1077 +++++++++++++++++ 4 files changed, 1272 insertions(+) create mode 100644 .trae/specs/create-quote-cloud-poster-generator/checklist.md create mode 100644 .trae/specs/create-quote-cloud-poster-generator/spec.md create mode 100644 .trae/specs/create-quote-cloud-poster-generator/tasks.md create mode 100644 index.html diff --git a/.trae/specs/create-quote-cloud-poster-generator/checklist.md b/.trae/specs/create-quote-cloud-poster-generator/checklist.md new file mode 100644 index 0000000..c6bb195 --- /dev/null +++ b/.trae/specs/create-quote-cloud-poster-generator/checklist.md @@ -0,0 +1,26 @@ +# Checklist + +- [x] 单文件 `index.html` 完整可运行(无外部 JS / 框架依赖,仅字体可走 Google Fonts CDN) +- [x] 顶部预留 `OPENROUTER_API_KEY = ''` 占位 +- [x] 内置 15 条初始 Quote 数组与 prompt 中的内容完全一致 +- [x] 使用离屏 `` + `context.measureText()` 测量文本,不使用 `offsetWidth/offsetHeight` +- [x] `measureMultilineText(text, fontSize, fontWeight, maxWidth)` 函数签名与返回值正确 +- [x] 不同 weight 映射不同 fontSize 与 maxWidth(weight 3 最大 + 较宽) +- [x] Quote 按 weight 降序排序,weight 3 居中 +- [x] 阿基米德螺旋线 `x = cx + r*cos(θ), y = cy + r*sin(θ)` 步进 +- [x] AABB 碰撞检测:候选与已放置均不重叠(静态验证:8 种 paper×orient 组合均 0 overlap) +- [x] 所有坐标在内存计算完毕后一次性渲染(`position: absolute; left/top`) +- [x] 画板容器 `aspect-ratio` 与纸张设置一致(A4 portrait ≈ 1:1.414 等) +- [x] 内容包围盒计算正确,`transform: scale()` 居中无裁剪 +- [x] 控制面板:纸张下拉 / 方向切换 / 作者开关 / Print 按钮四要素齐备 +- [x] `@media print` 动态注入 `@page { size: ; margin: 0; }` +- [x] 打印预览:白底、纯黑/深灰文字、无 box-shadow / blur、隐藏控制面板 +- [x] Hover 聚焦:被悬停句子放大高亮,其他句子变暗 + 模糊 +- [x] 作者默认隐藏;全局开关或 Hover 单条时淡入显示 +- [x] OpenRouter 请求使用 `google/gemini-flash-1.5` 模型 +- [x] Prompt 文本与规格一致(要求 weight 为 1/2/3) +- [x] 解析返回 JSON 时过滤 ```json 等 markdown 标记 +- [x] API Key 缺失 / 网络失败时回退到本地启发式评分,页面不崩溃 +- [x] 加载过程中展示 Loading 态 +- [x] 切换纸张 / 方向时画板比例与缩放实时更新 +- [x] 视觉风格:深色系(屏幕态)、高对比(打印态),具备海报感与艺术感 diff --git a/.trae/specs/create-quote-cloud-poster-generator/spec.md b/.trae/specs/create-quote-cloud-poster-generator/spec.md new file mode 100644 index 0000000..ab287d1 --- /dev/null +++ b/.trae/specs/create-quote-cloud-poster-generator/spec.md @@ -0,0 +1,124 @@ +# AI 驱动的排版海报与句子云生成器 (Interactive Quote Cloud & Poster Generator) Spec + +## Why +用户希望在浏览器中以极具艺术感的「句子云」形式呈现一组留言,并能一键导出为高分辨率、可印刷的实体海报(A4 / A3 / Letter 等)。该工具将 AI 文本权重分析、Canvas 文本测量、阿基米德螺旋线排版算法与浏览器原生打印管线整合到一个零依赖的 HTML 文件中。 + +## What Changes +- 新增一个单文件 HTML 应用(`index.html`),包含全部 HTML / CSS / JS。 +- 内置 15 条初始 Quote 数组作为种子数据。 +- 新增 OpenRouter AI 权重请求:调用 `google/gemini-flash-1.5`,为每条 quote 补充 `weight`(1/2/3)。 +- 新增基于离屏 `` + `context.measureText()` 的多行换行测量函数 `measureMultilineText`。 +- 新增阿基米德螺旋线 + AABB 碰撞检测的句子云布局算法。 +- 新增画板容器的纸张比例自适应与等比缩放(`transform: scale(n)`)。 +- 新增控制面板:纸张尺寸、方向、作者显隐、一键打印。 +- 新增 `@media print` 专属样式:动态注入 `@page` 规则,强制白底纯黑、省墨、隐藏控制面板。 +- 新增 Hover 聚焦交互:被悬停句子放大高亮,其他句子变暗并高斯模糊。 +- 新增作者淡入显示逻辑(默认隐藏,全局开关或 Hover 单条时显示)。 + +## Impact +- Affected specs: 新增能力「quote-cloud-rendering」「ai-weight-analysis」「print-pipeline」「focus-mode-interaction」。 +- Affected code: 全新文件 `index.html`(覆盖 `/workspace/index.html`)。 + +## ADDED Requirements + +### Requirement: AI 权重分析 +系统 SHALL 在页面加载时向 OpenRouter 发送请求,使用 `google/gemini-flash-1.5` 模型为每条 quote 计算 `weight`(1/2/3)。 +系统 MUST 在请求失败或 API Key 缺失时回退到本地启发式(基于句长 / 标点 / 关键词的简单评分)以保证页面始终可渲染。 +系统 MUST 解析返回文本中的合法 JSON 数组,过滤 markdown 标记(```json 等)。 + +#### Scenario: API Key 已配置 +- **WHEN** 页面加载且 `OPENROUTER_API_KEY` 不为空 +- **THEN** 调用 OpenRouter,返回带 `weight` 的 quote 数组,触发布局渲染 + +#### Scenario: API Key 为空或请求失败 +- **WHEN** `OPENROUTER_API_KEY` 为空字符串或网络错误 +- **THEN** 启用本地启发式评分,所有 quote 仍能正确渲染,不抛出未捕获错误 + +### Requirement: Canvas 多行文本测量 +系统 SHALL 在内存中创建隐藏的离屏 `` 元素,使用 `context.measureText()` 测量文本尺寸。 +系统 MUST 提供 `measureMultilineText(text, fontSize, fontWeight, maxWidth)` 函数,返回 `[width, height]`。 +系统 MUST 在 Canvas 虚拟环境中模拟 word-wrapping(按词或字符切分),按 `lineHeight = fontSize * 1.2` 累计高度。 +不同 `weight` 应对应不同 `fontSize`(weight 3 最大)和不同 `maxWidth`(大字号对应更宽的 maxWidth)。 + +#### Scenario: 长句子自动换行 +- **WHEN** 句子超过 maxWidth +- **THEN** 自动在空格或词边界换行,返回的 height 等于 `行数 * lineHeight` + +#### Scenario: 性能要求 +- **WHEN** 一次性测量 15 条 quote +- **THEN** 全部测量在 100ms 内完成,且不会触发 DOM 重排 + +### Requirement: 阿基米德螺旋线布局 +系统 SHALL 将 quote 按 `weight` 降序排列,最大权重的 quote 居中放置。 +系统 MUST 从画布中心 `(cx, cy)` 出发,沿阿基米德螺旋线 `x = cx + r*cos(θ), y = cy + r*sin(θ)` 步进(r = a + b*θ,a、b 为常数)。 +在每个候选点上 MUST 进行 AABB 矩形碰撞检测:候选句子的包围盒不能与任何已放置句子的包围盒相交。 +一旦找到第一个不重叠的位置,系统 SHALL 锁定该坐标并继续下一个句子。 +所有句子位置计算完毕后,系统 SHALL 一次性以 `position: absolute; left/top` 渲染到画板。 + +#### Scenario: 紧密穿插无重叠 +- **WHEN** 15 条 quote 全部排版完成 +- **THEN** 任意两条 quote 的包围盒不发生交叉,且整体呈现"见缝插针"的视觉密度 + +#### Scenario: 权重决定字号与优先级 +- **WHEN** weight=3 的金句 +- **THEN** 字号最大,且优先占据中心位置;weight=1 的普通句子字号最小,分布在外围 + +### Requirement: 画板比例与等比缩放 +系统 SHALL 提供一个"画板容器" `.paper-canvas`,其 `aspect-ratio` 与当前纸张设置完全一致(A4 portrait ≈ 1:1.414,A3 portrait ≈ 1:1.414,Letter portrait ≈ 8.5:11,16:9 Screen)。 +系统 MUST 在布局完成后计算所有已放置句子的总体包围盒(minX, minY, maxX, maxY),得出整体宽高。 +系统 MUST 计算缩放系数 `scale = min(canvasW / contentW, canvasH / contentH) * paddingRatio`,使用 `transform: scale(scale)` 将内容居中,确保无裁剪且白边最小。 + +#### Scenario: A4 竖版 +- **WHEN** 选择 A4 + Portrait +- **THEN** 画板容器的宽高比为 1:1.414,句子云内容被等比缩放并居中 + +#### Scenario: 切换纸张 +- **WHEN** 用户从 A4 Portrait 切换到 16:9 Landscape +- **THEN** 画板宽高比实时变化,内容自动重新缩放并居中 + +### Requirement: 控制面板与打印 +系统 SHALL 提供极简控制面板,包含: +- 纸张尺寸下拉:A4 / A3 / Letter / 16:9 Screen +- 方向切换:Portrait / Landscape +- 作者显隐开关 +- 一键打印按钮(调用 `window.print()`) + +点击打印时,系统 SHALL 通过 JS 动态注入 ` + + + +
+
+
+
+
+
+
Composing
+
Quote Cloud · AI Layout
+
+
+
+
+ + + +
Quote Cloud · AI Layout Engine
+ + + + +