From 87b84b9c9c5514b23c4b49a193f19fa0beed9785 Mon Sep 17 00:00:00 2001 From: maomaochong-ai Date: Sun, 6 Sep 2026 20:29:38 +0800 Subject: [PATCH] =?UTF-8?q?feat(create):=20=E5=88=9B=E4=BD=9C=E9=A6=96?= =?UTF-8?q?=E9=A1=B5=E4=BC=9A=E8=AF=9D=E5=8C=96=20-=20=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=80=81=E6=8C=87=E7=A4=BA=E5=99=A8/=E8=AE=A1=E5=88=92?= =?UTF-8?q?=E8=B7=AF=E7=BA=BF=E5=88=86=E7=A6=BB/=E5=8D=95=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D=E6=89=BF=E8=BD=BD=E5=88=86=E9=95=9C/=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E4=B8=AD=E5=BF=83=E4=B8=8E=E7=A9=BA=E6=80=81=20hero/?= =?UTF-8?q?=E5=8E=86=E5=8F=B2=E6=8A=BD=E5=B1=89=E5=88=86=E7=BB=84/?= =?UTF-8?q?=E8=A1=8C=E6=93=8D=E4=BD=9C=20hover=20=E8=8F=9C=E5=8D=95?= =?UTF-8?q?=E4=B8=8E=E9=87=8D=E5=91=BD=E5=90=8D/=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E6=8C=89=E9=92=AE=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/service/provider.go | 8 + backend/internal/service/provider_test.go | 8 + docs/plans/boardui-absorption-plan.mdx | 3 +- docs/plans/home-chat-optimization-plan.mdx | 99 ++ web/src/components/ai/working-indicator.tsx | 92 ++ .../canvas/canvas-agent-chat-ui.tsx | 44 +- .../canvas/canvas-assistant-panel.tsx | 17 +- web/src/styles/globals.css | 1274 ++++------------- 8 files changed, 542 insertions(+), 1003 deletions(-) create mode 100644 docs/plans/home-chat-optimization-plan.mdx create mode 100644 web/src/components/ai/working-indicator.tsx diff --git a/backend/internal/service/provider.go b/backend/internal/service/provider.go index 2517ea811..8a8556e76 100644 --- a/backend/internal/service/provider.go +++ b/backend/internal/service/provider.go @@ -295,6 +295,14 @@ func providerPayloadErrorCategory(raw string) (string, bool) { return "模型服务额度不足,请检查渠道余额或配额", true case strings.Contains(normalized, "model") && (strings.Contains(normalized, "not found") || strings.Contains(normalized, "permission") || strings.Contains(normalized, "access")): return "模型不存在或当前渠道未获得模型权限", true + // 推理/思考模式模型通常禁止强制指定工具调用:DeepSeek 思考模式返回 + // "Thinking mode does not support this tool_choice",其他 OpenAI 兼容 + // 供应商措辞类似。归为固定可行动原因,画布智能体据此把首步的 + // tool_choice=required 降级为 auto 重试一次。排在通用参数类目之前, + // 避免这类稳定标识落回笼统的"请检查模型和参数"。 + case (strings.Contains(normalized, "thinking") || strings.Contains(normalized, "reasoning")) && strings.Contains(normalized, "tool_choice"), + strings.Contains(normalized, "tool_choice") && (strings.Contains(normalized, "not support") || strings.Contains(normalized, "unsupported")): + return "当前模型为思考/推理模式,不支持强制工具调用(tool_choice=required),请改用自动工具选择或更换非思考模式模型", true case strings.Contains(normalized, "invalid"), strings.Contains(normalized, "parameter"), strings.Contains(normalized, "argument"): return "模型服务拒绝了请求,请检查模型和参数", true } diff --git a/backend/internal/service/provider_test.go b/backend/internal/service/provider_test.go index 05d80d236..7ac7dc782 100644 --- a/backend/internal/service/provider_test.go +++ b/backend/internal/service/provider_test.go @@ -608,6 +608,8 @@ func TestProviderPayloadErrorMessageUsesSafeActionableCategories(t *testing.T) { {name: "moderation", raw: "request blocked by content policy: prompt=private", want: "安全审核"}, {name: "quota", raw: "insufficient quota for api-key=secret", want: "额度不足"}, {name: "model access", raw: "model not found for tenant secret-id", want: "模型不存在"}, + {name: "thinking mode rejects forced tool choice", raw: `{"error":{"message":"Thinking mode does not support this tool_choice","request_id":"secret-trace"}}`, want: "不支持强制工具调用"}, + {name: "reasoning mode rejects forced tool choice", raw: `{"error":{"message":"tool_choice=required is not supported in reasoning mode"}}`, want: "不支持强制工具调用"}, {name: "unknown", raw: "trace_id=private internal stack", want: "模型服务返回失败"}, } for _, tt := range tests { @@ -682,6 +684,12 @@ func TestProviderUserFacingErrorMessageClassifiesRejectedRequestBodies(t *testin body: "", want: "请检查模型和参数", }, + { + name: "thinking mode rejects forced tool choice", + statusCode: http.StatusBadRequest, + body: `{"error":{"message":"Thinking mode does not support this tool_choice","request_id":"secret"}}`, + want: "不支持强制工具调用", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/docs/plans/boardui-absorption-plan.mdx b/docs/plans/boardui-absorption-plan.mdx index cb3e8c29b..109da3da2 100644 --- a/docs/plans/boardui-absorption-plan.mdx +++ b/docs/plans/boardui-absorption-plan.mdx @@ -28,12 +28,13 @@ BoardUI 是面向 AI/agent 界面的 React 组件库(62+ 源码组件、400+ | `hooks/use-count-up.ts`(数字滚动) | 抄写至 `web/src/hooks/use-count-up.ts`,保留 easeOutCubic/取消动画语义;适配项目规范:尊重 `prefers-reduced-motion`、暴露 `from` 参数 | 后台运营健康卡(活跃用户 / 生成任务 / 服务质量)数值滚动 | | agent-thinking(wave 点阵指示器形态) | 形态吸收至 `canvas-agent-chat-ui.tsx`:3×3 对角波光点阵 + `role="status"` + 秒数计时 + reduced-motion 静态降级(不引 UI 资产与 motion 依赖) | Canvas AI 助手工作状态 `AgentWorkingMessage` | | composer-loader(发送态点亮语义) | 语义吸收:发送中胶囊加主题色呼吸光环(1.6s 周期,reduced-motion 关闭) | Canvas AI 助手输入胶囊 | +| agent-log(WorkingRow:工作行与计划分离、真实事件驱动、树线连接) | 语义吸收至创建页分镜 pending:胶囊改纯计划路线(去伪当前步高亮),新增独立活动行 `storyboard-workbench-activity`(真实状态文案 + WorkingDots + `role="status"`,复用共享组件) | 创建页 Storyboard 镜头生成中卡片 | ### 2.2 待吸收(按 rollout 计划后续执行) | BoardUI 资产 | 内容 | 本项目落点 | |---|---|---| -| agent-log | Agent 执行过程日志/事件流渲染 | Canvas 任务运行面板(TaskLog 升级) | +| agent-log 的 reveal/连接线形态(等第二消费点:画布 TaskLog 升级时抽共享组件,参考 WorkingDots 路径) | 形态参考 | Canvas 任务运行面板(TaskLog 升级,进行中) | | notification-center | 通知中心(含 agent 完成/失败分组) | 全局通知浮层 / 后台 | | styles/typography.css | text-* 排版复合类体系 | 对照 `ui-design-system.mdx` 融入 globals.css(rollout §6.2 缺口) | | utils/cx.ts(tailwind-merge) | 类名合并 | 与项目 `lib/utils.ts` 对齐补 cn() | diff --git a/docs/plans/home-chat-optimization-plan.mdx b/docs/plans/home-chat-optimization-plan.mdx new file mode 100644 index 000000000..8ac95fe9e --- /dev/null +++ b/docs/plans/home-chat-optimization-plan.mdx @@ -0,0 +1,99 @@ +# 首页对话区优化计划(BoardUI 参照自研) + +> 状态:已确认执行顺序(2026-09-06)——Phase A 先行、随后 Phase B;代码高亮启用 `@streamdown/code` +> 关联:`docs/plans/boardui-absorption-plan.mdx`(图标/工具栏/空态已完成并落版)、`docs/plans/ui-kit-rollout-plan.mdx` +> 范围文件:`web/src/pages/create/index.tsx`(1897 行)、`web/src/styles/globals.css`(15940 行)、`web/src/components/ai/ai-message-markdown.tsx` + +## 1. 背景与目标 + +首页(`/` = 创作页)对话区仍有多处可优化,用户点名两大方向,缺一不可: + +1. **历史会话弹窗**:按钮入口 → 弹层内容 → 列表项 → 行内操作,整体打磨。 +2. **消息正文富内容**:AI 回复中渲染的文本、**代码块、表格、图片**等内容形态的呈现质量。 + +工作方式:以 BoardUI 仓库(`../boardui`)为视觉与交互参照**自研**落地;自研过程中如发现本站代码不合理(硬编码、重复、缺无障碍、结构性膨胀),同步改进——但不引入 BoardUI 的运行时依赖(无 `react-aria-components`、无 `motion`,沿用 AntD + Tailwind + 自研 token)。 + +### 调研结论速览 + +**BoardUI 侧**(`components/application/agent-chat/*`): +- 对话外壳为「右侧会话 rail + 主消息区 + 底部输入条」三段式卡片(rail 260px 卡片、主区 `rounded-3xl bg-secondary`、输入条同宽贴底)。 +- 会话 rail 交互最值得吸收:行 hover 与选中同底色(hover 即预览选中);行尾时间徽标 hover 时淡出让位给「⋯」菜单(190px 浮层:重命名/标未读/删除,删除项红色);**行内重命名**(自聚焦全选、Enter 提交、Esc 还原、blur 提交);未读蓝点 6px;新建会话置顶按钮行;分组小标。 +- 消息行操作簇:hover 显露、`focus-within` 保留、流式中禁用隐藏(28px 圆形、`rounded-md`、tertiary 图标色、hover 底色)。 +- 用户消息为右侧气泡:`max-w-[75%] rounded-2xl px-3 py-[11px] bg-background-primary shadow-card`;assistant 通栏无气泡,与消息簇同宽(`max-w-3xl`)排版。 +- 输入条为 52px 胶囊 + 36px 圆形控制钮;发送中变「停止」;下方一条 26px 状态行如实显示 provider/消息数。 +- 空态:标题 + 次级说明 + 胶囊建议钮;首 token 到达前以「思考中」占位。 +- **注意**:BoardUI 的 agent-chat 是纯文本 starter,全库无 markdown/代码块/表格/图片渲染组件(已 grep 证实:无 react-markdown/shiki/prism/`language-` 类)。故**富内容方向以本站自研管线为主干**,BoardUI 仅提供容器语汇(chip/复制钮/滚动容器/间距节奏)。 + +**本站侧现状**(引用为现状快照,实施前以最新行号为准): +- 消息渲染:assistant 消息头含镜头徽标/模式/模型/时间/状态与「生成同款」,推理过程可折叠(`MessageReasoning`),正文经 `AIMessageMarkdown`(`web/src/components/ai/ai-message-markdown.tsx`,streamdown 流式渲染,`parseIncompleteMarkdown`,逐词淡入)。 +- 富内容现状(样式见 `globals.css` 15206–15297): + - 代码块:仅有衬底 pre(`rounded r-sm`、`foreground 7%` 底),**无语言标签条、无复制按钮、无行号**(`lineNumbers={false}`)、无语法高亮。依赖 `@streamdown/code@1.1.1` 已安装但**从未被 import**(沉睡资产,即流式高亮的官方配套)。 + - 表格:滚动容器 + 单元格仅下边框 + 表头 5% 底,较朴素;无边框网格/斑马纹/圆角容器。 + - 图片:markdown 内 `![...]` 无自定义渲染(无圆角/尺寸约束/点击预览);会话内生成结果以媒体卡片网格呈现并有预览 Modal(含缩放拖拽),此路已通。 + - 引用/列表/分割线/行内代码:已有 token 化样式(`color-mix(in srgb, currentColor …)` 自适应明暗)。 +- 历史弹窗现状(`index.tsx` `CreationHistoryDrawer` 段):AntD Drawer;含标题计数、搜索框、新建会话卡、时间桶分组(今天/昨天/近 7 天/更早)、行=整行按钮 + 直接删除按钮 + 模式 tag + 摘要截断、空态、`aria-current`。已不粗糙,但与 BoardUI rail 相比缺少:**hover 菜单(重命名/删除并入)、行内重命名、未读标记、时间徽标让位动画、行间 hover 反馈一致性**;「删除」为单点直达按钮存在误删风险(删除虽走确认流程)。 +- 输入条:已完成 R2 幽灵按钮统一(32px 透明按钮族),与消息区为同一 820 居中列。 + +## 2. 待优化清单(分阶段) + +### Phase A — 历史会话弹窗升级(借鉴 BoardUI rail 交互) + +目标:让「历史」弹窗的列表行达到与 BoardUI rail 同级的信息密度与操作安全度,并补齐行内编辑。 + +- [ ] A1 行操作重构:删除从「常驻小按钮」改为 hover「⋯」菜单(菜单项:重命名 / 删除[红]);hover 态即选中预览底色;键盘可达(按钮 `aria-expanded`、菜单 Esc 关闭、焦点回退触发器)。 +- [ ] A2 行内重命名:行标题双击或菜单「重命名」进入编辑态——输入自聚焦全选,Enter 提交 / Esc 还原 / blur 提交;提交走既有改名逻辑并乐观更新。 +- [ ] A3 时间徽标:展示相对时间(刚刚/n 分钟/n 小时/n 天前),hover 行时让位给操作菜单(淡出/淡入,`prefers-reduced-motion` 下直接切换)。 +- [ ] A4 行信息补全与排版:摘要截断行 + 消息数/时间粒度统一;选中态、hover 态、焦点环与页面其他按钮族同源(沿用 R2 幽灵钮规范)。 +- [ ] A5 空态与加载态核对:无历史时按钮/面板空态文案与插图风格对齐首页空态;打开时骨架或已有加载态统一。 +- [ ] A6 删除确认保留既有确认流;确认框按钮规格对齐全局。 + +验收:悬停/键盘可完成重命名与删除;重命名即时生效;删除需确认;明暗两主题下 hover/选中/焦点可辨;`bunx tsc --noEmit` 通过;提交后浏览器人工验收。 + +### Phase B — 消息正文富内容打磨(文本/代码块/表格/图片) + +目标:AI 回复中的结构化内容达到「可读、可复制、可引用、好看」的水准。以 `AIMessageMarkdown` 的 components 映射为单一扩展点,不另起渲染体系。 + +- [ ] B1 代码块外壳:语言标签条(语言名取自 `className="language-*"`,未知语言不显示或显 `code`)+ 复制按钮(`aria-label`、成功态反馈)+ 统一圆角/边框/底色 token;横向滚动保留;顶部标签条在流式渲染中稳定不跳动。 +- [ ] B2 代码高亮:评估启用 `@streamdown/code`(流式友好,已装未用)。决策点见 §4。若启用需处理:未知语言降级、流式增量着色节流、深色主题 token 化(不引入散落色值)。 +- [ ] B3 行号(可选):streamdown 已支持 `lineNumbers`(当前显式关闭)。默认关闭(对话场景非编辑器),提供设置/偏好开关的可行性标注即可,不强推。 +- [ ] B4 表格增强:网格边框(弱化)+ 表头底色 + hover 行、粘性表头容器?——对话表格通常短小,做「圆角容器 + 细网格 + 表头 5–8% 底」即可,不引入复杂表格库;列多时滚动容器保留并补滚动提示(可选)。 +- [ ] B5 行内图片:markdown `![alt](url)` 渲染为受限尺寸圆角卡片(`max-height` 约束、`object-fit: cover/contain` 由比例定)、点击进入既有预览 Modal(复用生成结果预览组件);加载失败占位(alt 文案 + 破损图标);懒加载。流式期间图片未收尾时的处理与正文一致(不闪跳)。 +- [ ] B6 消息级操作簇(如可行且不喧宾夺主):assistant 消息 hover 出现「复制全文/朗读(如已有能力)」等,参照 BoardUI 显示规则(hover 显露、focus-within 保留、流式隐藏);与现有消息头操作区不重复。 +- [ ] B7 渲染一致性小修:标题层级字号用 em 但整体应随正文 1em 缩放核验;行内代码与代码块在流式未闭合时的处理(streamdown 已处理,人工验收确认);引用块与代码块间距节奏(`0.8em` 段落间距)核对。 + +验收:向模型提问触发含代码块/表格/列表/引用内容,肉眼核对明暗主题;代码块复制可用且键盘可达;图片点击预览;流式中无布局跳动;`tsc` 通过;浏览器人工验收。 + +### Phase C — 代码质量改进(自研中发现的不合理点) + +伴随 A/B 落地顺手治理,不单独立项大改: + +- [ ] C1 消息正文样式(15206–15297)与代码块新样式统一收口到创作页样式节或提升为共享组件类;新组件(代码块/图片/历史行菜单)优先页面级组件,出现第二处使用时再提升(遵循 AGENTS.md 组件归属规则)。 +- [ ] C2 清理内联字面样式(如 `index.tsx` 内 `style={{display:"inline-flex",gap:8}}` 之类散落写法)→ 具名 CSS 类;磁贴/图标尺寸继续走 R3 统一族。 +- [ ] C3 时间格式化工具核对:消息时间/会话分组时间/相对时间若存在多套近似实现,合并为一处(验证引用后再删,不臆测)。 +- [ ] C4 无障碍核对(随 A/B 每项必查):新增按钮均有 `aria-label`/`aria-expanded`、焦点环、Esc/Enter 语义;`prefers-reduced-motion` 下逐词淡入与让位动画降级为直接切换(若 streamdown 动画类无降级则补 CSS)。 +- [ ] C5 `index.tsx` 1897 行:本轮**不拆文件**(页面私有组件合法),但 A/B 新增块保持独立组件定义清晰;若后续页面文件再涨且出现跨页复用再议拆分。 + +## 3. BoardUI 关键规格速查(实施时对照) + +| 能力 | BoardUI 配方 | 本站落点建议 | +| --- | --- | --- | +| rail 行圆角/hover | `rounded-2lg`,hover 与选中同 fill | 历史行 `--r-md`~`--r-lg` 家族,hover 即预览选中 | +| 未读点 | 6px `bg-button-primary` | 会话级未读(如可用 data 支持)否则标注「数据不支持」 | +| 行菜单 | 190px、`p-2`、删除 `text-text-error-primary` | 自制浮层或 AntD Dropdown 定制样式 | +| 时间徽标让位 | 相对时间 hover 淡出让位菜单 | CSS transition + reduced-motion 降级 | +| 用户气泡 | `max-w-[75%] px-3 py-[11px] rounded-2xl` | 已右对齐气泡,核对圆角/内边距/阴影一致性 | +| 消息 hover 操作 | 28px `rounded-md`、流式禁用隐藏 | 消息头已有操作区,评估是否需要消息尾簇 | +| 输入条 | 52px 胶囊 + 36px 圆钮 + 26px 状态行 | 现有 820 幽灵钮族已统一,仅核对与消息区衔接 | +| 空态 | 标题/说明/胶囊建议钮、思考占位 | 已对齐(R2/R3),微调即可 | + +## 4. 已确认决策 + +1. **执行顺序**:Phase A(历史弹窗)先行,随后 Phase B(富内容)——用户已确认(2026-09-06)。 +2. **代码高亮方案**:启用 `@streamdown/code`(官方流式配套、已装零新增依赖)——用户已确认(2026-09-06)。实施时:未知语言降级为无高亮;增量着色节流;颜色走主题 token。 +3. 行号默认关闭、图片预览复用既有 Modal——按正文推荐默认执行,无异议。 + +## 5. 落地纪律 + +- 每个 Phase 独立提交(`feat/fix/refactor` 前缀按仓库规范),提交前 `bunx tsc --noEmit`,改动后同步 `docs/content/docs/progress/pending-test.mdx` 验收段落(含本次删除任务通知已记录的条目)。 +- 所有改动保持工作树干净;本计划文档随首轮实施一并提交。 +- BoardUI 只读参照;若其视觉与本站 token(明暗自适应)冲突,以本站 token 体系为准。 diff --git a/web/src/components/ai/working-indicator.tsx b/web/src/components/ai/working-indicator.tsx new file mode 100644 index 000000000..fc624ce0b --- /dev/null +++ b/web/src/components/ai/working-indicator.tsx @@ -0,0 +1,92 @@ +import { useEffect, useState } from "react"; +import { motion, useReducedMotion } from "motion/react"; + +/** + * WorkingDots: diagonal wave dot-matrix "busy" indicator. + * + * Form absorbed from boardui agent-thinking (wave variant) — no UI assets or + * extra dependencies; reduced-motion renders a static frame. The grid is drawn + * with `bg-current`, so it inherits whatever text color the caller provides + * (canvas theme accents, CSS vars, etc.). + */ +export function WorkingDots({ + cols = 3, + rows = 3, + dotSize = 4, + gap = 2, + tickMs = 90, + minOpacity = 0.14, + trail = 0.32, + className, +}: { + cols?: number; + rows?: number; + dotSize?: number; + gap?: number; + tickMs?: number; + minOpacity?: number; + trail?: number; + className?: string; +}) { + const reducedMotion = useReducedMotion(); + const [phase, setPhase] = useState(0); + useEffect(() => { + if (reducedMotion) return; + const timer = window.setInterval(() => setPhase((value) => (value + 1 / 8) % 1), tickMs); + return () => window.clearInterval(timer); + }, [reducedMotion, tickMs]); + const count = cols * rows; + return ( + + {Array.from({ length: count }, (_, index) => { + const col = index % cols; + const row = Math.floor(index / cols); + // Normalized diagonal position, scaled so the wave front turns + // back before reaching the far corner (3/4 for a 3x3 grid, + // matching the original canvas indicator pixel-for-pixel). + const n = Math.max(cols, rows); + const normalized = (col / Math.max(1, cols - 1) + row / Math.max(1, rows - 1)) / 2; + const scalar = normalized * (n / (n + 1)); + const behind = (phase - scalar + 1) % 1; + const lit = Math.max(0, 1 - behind / trail) ** 1.5; + const opacity = minOpacity + (1 - minOpacity) * lit; + return ; + })} + + ); +} + +/** + * WorkingGlow: soft breathing halo rendered over a "busy" control (send + * button, composer capsule). Color accepts a hex value or a CSS variable + * (e.g. `var(--creation-text)`); the blurred layer uses color-mix so both + * work. Disabled under prefers-reduced-motion. Caller positions it: wrap the + * control in a `relative` container and pass the matching border radius. + */ +export function WorkingGlow({ + active, + color, + radius = 0, + className, +}: { + active: boolean; + color: string; + radius?: number | string; + className?: string; +}) { + const reducedMotion = useReducedMotion(); + if (!active || reducedMotion) return null; + const soft = `color-mix(in srgb, ${color} 55%, transparent)`; + return ( + + ); +} diff --git a/web/src/components/canvas/canvas-agent-chat-ui.tsx b/web/src/components/canvas/canvas-agent-chat-ui.tsx index 6e940c819..0fd610a33 100644 --- a/web/src/components/canvas/canvas-agent-chat-ui.tsx +++ b/web/src/components/canvas/canvas-agent-chat-ui.tsx @@ -9,6 +9,7 @@ import { canvasThemes } from "@/lib/canvas-theme"; import type { CanvasAgentOperationImpact } from "@/lib/canvas/canvas-agent-ops"; import type { LocalUser } from "@/stores/use-user-store"; import { AIMessageMarkdown } from "@/components/ai/ai-message-markdown"; +import { WorkingDots, WorkingGlow } from "@/components/ai/working-indicator"; import { CanvasResourceMentionTextarea } from "./canvas-resource-mention-textarea"; import type { CanvasResourceReference } from "@/lib/canvas/canvas-resource-references"; import type { Skill } from "@/services/api/skills"; @@ -50,37 +51,6 @@ export function extractCanvasAgentQuickActions(text: string): CanvasAgentQuickAc const WORKING_TEXT = "正在推演..."; -// Agent 工作指示器:3×3 点阵对角波光。吸收自 boardui agent-thinking 的 -// wave 形态(不引 UI 资产,仅点阵节奏与降级规范);reduced-motion 下保持静态帧。 -const WORKING_DOTS = 3; -const WORKING_DOTS_TICK_MS = 90; -const WORKING_DOTS_MIN_OPACITY = 0.14; -const WORKING_DOTS_PHASE_STEP = 1 / 8; -const WORKING_DOTS_TRAIL = 0.32; - -function AgentWorkingDots() { - const reducedMotion = useReducedMotion(); - const [phase, setPhase] = useState(0); - useEffect(() => { - if (reducedMotion) return; - const timer = window.setInterval(() => setPhase((value) => (value + WORKING_DOTS_PHASE_STEP) % 1), WORKING_DOTS_TICK_MS); - return () => window.clearInterval(timer); - }, [reducedMotion]); - return ( - - {Array.from({ length: WORKING_DOTS * WORKING_DOTS }, (_, index) => { - const col = index % WORKING_DOTS; - const row = Math.floor(index / WORKING_DOTS); - // 对角波相位 0..<1:波前扫过点阵后折返,读作光在格内往返。 - const scalar = ((col + row) / (2 * (WORKING_DOTS - 1))) * (WORKING_DOTS / (WORKING_DOTS + 1)); - const behind = (phase - scalar + 1) % 1; - const lit = Math.max(0, 1 - behind / WORKING_DOTS_TRAIL) ** 1.5; - const opacity = WORKING_DOTS_MIN_OPACITY + (1 - WORKING_DOTS_MIN_OPACITY) * lit; - return ; - })} - - ); -} function AgentWorkingElapsed() { const [elapsed, setElapsed] = useState(0); @@ -304,7 +274,7 @@ export function AgentToolCard({ title, text, detail, theme }: { title: string; t export function AgentWorkingMessage({ theme, label = WORKING_TEXT }: { theme: (typeof canvasThemes)[keyof typeof canvasThemes]; label?: string }) { return (
- + {label}
@@ -417,15 +387,7 @@ export function AgentChatComposer({ boxShadow: `0 16px 40px ${theme.spatial.shadow}, inset 0 1px 0 rgba(255,255,255,0.045)`, }} > - {sending && !reducedMotion ? ( - - ) : null} + {sending && !reducedMotion ? : null} {attachments.length ? (
{attachments.map((item) => ( diff --git a/web/src/components/canvas/canvas-assistant-panel.tsx b/web/src/components/canvas/canvas-assistant-panel.tsx index 68e2c0ffd..975bae7ab 100644 --- a/web/src/components/canvas/canvas-assistant-panel.tsx +++ b/web/src/components/canvas/canvas-assistant-panel.tsx @@ -1651,8 +1651,23 @@ function toolCallToResponseInput(call: ResponseToolCall): ResponseInputMessage { return { type: "function_call", call_id: call.id, name: call.function.name, arguments: call.function.arguments, ...(call.thoughtSignature ? { thoughtSignature: call.thoughtSignature } : {}) }; } +// 上游兼容兜底:DeepSeek 等 OpenAI 兼容渠道的思考/推理模式不允许 +// tool_choice=required(后端归类文案见 providerPayloadErrorCategory)。 +// required 只用于对话首步把智能体拖入工具循环;遇到该类拒绝时降级为 auto +// 重试一次——auto 在思考模式下可用,对话与工具调用均不受影响。 +// 语义与 image.ts 对非 auto tool_choice 的兼容降级策略一致。 +const THINKING_MODE_TOOL_CHOICE_REJECTION = "不支持强制工具调用"; + async function requestOnlineAgentModel(config: AiConfig, messages: ResponseInputMessage[], toolChoice: "auto" | "required", prompt: string, onDelta: (text: string) => void) { - return runBackendToolGenerationTask({ prompt, config, messages, tools: ONLINE_AGENT_TOOLS, toolChoice, onDelta }); + const submit = (choice: "auto" | "required") => runBackendToolGenerationTask({ prompt, config, messages, tools: ONLINE_AGENT_TOOLS, toolChoice: choice, onDelta }); + try { + return await submit(toolChoice); + } catch (error) { + if (toolChoice !== "required" || !(error instanceof Error) || !error.message.includes(THINKING_MODE_TOOL_CHOICE_REJECTION)) { + throw error; + } + return submit("auto"); + } } function summarizeToolCalls(calls: ResponseToolCall[]) { diff --git a/web/src/styles/globals.css b/web/src/styles/globals.css index 22bf15275..745d66bf9 100644 --- a/web/src/styles/globals.css +++ b/web/src/styles/globals.css @@ -2574,7 +2574,7 @@ flex: 0 0 auto; place-items: center; border: 1px solid var(--creation-border); - border-radius: 9px; + border-radius: var(--r-md); background: color-mix(in srgb, var(--creation-surface-muted) 68%, transparent); color: var(--creation-muted); transition: border-color 160ms ease, background-color 160ms ease, color 160ms ease, transform 120ms ease; @@ -12883,28 +12883,26 @@ html:not(.dark) .creation-custom-value { background: rgba(28, 32, 37, .055) !imp right: 28px; display: flex; align-items: center; - gap: 6px; + gap: var(--space-1); } .creation-top-action { - display: grid; - width: 36px; - height: 36px; - place-items: center; - border: 0; - border-radius: 8px; - background: rgba(255, 255, 255, .06); + display: inline-flex; + min-height: var(--space-8); + align-items: center; + justify-content: center; + border: 1px solid transparent; + border-radius: var(--r-md); + background: transparent; color: var(--creation-muted); - transition: background-color 150ms ease, color 150ms ease, transform 150ms var(--motion-ease-out); + padding: 0 var(--space-2); + transition: background-color var(--motion-state) ease, color var(--motion-state) ease; } -.creation-top-action svg { width: 16px; height: 16px; } +.creation-top-action svg { width: var(--space-4); height: var(--space-4); } .creation-top-action:hover, -.creation-top-action[aria-expanded="true"] { background: rgba(255, 255, 255, .12); color: var(--creation-text); transform: translateY(-1px); } -.creation-top-action:focus-visible { outline: 2px solid var(--control-focus-ring); outline-offset: 2px; } -html:not(.dark) .creation-top-action { background: rgba(28, 32, 37, .055); } -html:not(.dark) .creation-top-action:hover, -html:not(.dark) .creation-top-action[aria-expanded="true"] { background: rgba(28, 32, 37, .11); } +.creation-top-action[aria-expanded="true"] { background: var(--creation-surface-hover); color: var(--creation-text); } +.creation-top-action:focus-visible { outline: 2px solid var(--creation-accent); outline-offset: 1px; } .creation-history-drawer, .creation-history-drawer-root { @@ -12953,19 +12951,44 @@ html:not(.dark) .creation-history-drawer-root { .creation-history-list { display: grid; gap: var(--space-1); margin: 0; padding: 0; list-style: none; } .creation-history-list > li { position: relative; min-width: 0; } -.creation-history-item-main { display: flex; width: 100%; min-height: 78px; flex-direction: column; align-items: flex-start; justify-content: center; gap: 5px; border: 1px solid transparent; border-radius: var(--r-sm); background: transparent; padding: 12px 50px 13px 10px; color: var(--creation-history-text); text-align: left; transition: background-color 150ms ease, border-color 150ms ease; } -.creation-history-item-main:hover { background: var(--creation-history-hover); } -.creation-history-item-main:focus-visible { outline: 2px solid color-mix(in srgb, var(--creation-history-accent) 68%, transparent); outline-offset: -2px; } -.creation-history-list > li.is-active .creation-history-item-main { border-color: color-mix(in srgb, var(--creation-history-accent) 28%, transparent); background: color-mix(in srgb, var(--creation-history-accent) 7%, var(--creation-history-surface)); } -.creation-history-delete { position: absolute; z-index: 1; top: 50%; right: 9px; display: grid; width: 32px; height: 32px; place-items: center; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--creation-history-muted); transform: translateY(-50%); transition: background-color 150ms ease, color 150ms ease; } -.creation-history-delete svg { width: 16px; height: 16px; } -.creation-history-delete:hover { background: color-mix(in srgb, var(--destructive) 12%, transparent); color: var(--destructive); } -.creation-history-delete:focus-visible { outline: 2px solid color-mix(in srgb, var(--destructive) 60%, transparent); outline-offset: 1px; } -.creation-history-time { display: flex; width: 100%; align-items: center; justify-content: space-between; color: var(--creation-history-muted); font-size: var(--fs-micro); font-variant-numeric: tabular-nums; line-height: 1.3; } -.creation-history-time em { border: 1px solid var(--creation-history-line); border-radius: var(--r-xs); padding: 2px 5px; color: var(--creation-history-muted); font-size: var(--fs-micro); font-style: normal; } -.creation-history-item-heading { width: 100%; min-width: 0; overflow: hidden; font-size: var(--fs-label); font-weight: 650; line-height: 1.35; text-overflow: ellipsis; white-space: nowrap; } -.creation-history-snippet { display: block; width: 100%; overflow: hidden; color: color-mix(in srgb, var(--creation-history-muted) 86%, transparent); font-size: var(--fs-micro); line-height: 1.35; text-overflow: ellipsis; white-space: nowrap; } +.creation-history-row { position: relative; display: flex; min-width: 0; align-items: center; border-radius: var(--r-sm); transition: background-color 150ms ease; } +.creation-history-row:hover { background: var(--creation-history-hover); } +.creation-history-row .creation-history-item-main { display: flex; min-width: 0; flex: 1; flex-direction: column; align-items: flex-start; justify-content: center; gap: 3px; border: 0; border-radius: var(--r-sm); background: transparent; padding: 8px 62px 8px 12px; color: var(--creation-history-text); text-align: left; } +.creation-history-row .creation-history-item-main:hover { background: transparent; } +.creation-history-row .creation-history-item-main:focus-visible { outline: 2px solid color-mix(in srgb, var(--creation-history-accent) 68%, transparent); outline-offset: -2px; } +.creation-history-list > li.is-active .creation-history-row { background: color-mix(in srgb, var(--creation-history-accent) 9%, var(--creation-history-surface)); } +.creation-history-list > li.is-active .creation-history-row:hover { background: color-mix(in srgb, var(--creation-history-accent) 14%, var(--creation-history-surface)); } +.creation-history-item-heading { width: 100%; overflow: hidden; font-size: var(--fs-label); font-weight: 650; line-height: 1.35; text-overflow: ellipsis; white-space: nowrap; } +.creation-history-snippet { display: flex; width: 100%; min-width: 0; align-items: baseline; gap: 6px; overflow: hidden; color: color-mix(in srgb, var(--creation-history-muted) 88%, transparent); font-size: var(--fs-micro); line-height: 1.45; } +.creation-history-snippet em { flex: 0 0 auto; color: color-mix(in srgb, var(--creation-history-muted) 80%, transparent); font-size: var(--fs-micro); font-style: normal; font-weight: 600; } +.creation-history-snippet span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.creation-history-time-slot { position: absolute; z-index: 1; top: 50%; right: 8px; display: flex; height: 26px; align-items: center; color: var(--creation-history-muted); font-size: var(--fs-micro); font-variant-numeric: tabular-nums; line-height: 1.3; white-space: nowrap; pointer-events: none; transform: translateY(-50%); transition: opacity 150ms ease; } +.creation-history-more { position: absolute; z-index: 2; top: 50%; right: 8px; display: grid; width: 26px; height: 26px; place-items: center; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--creation-history-muted); opacity: 0; transform: translateY(-50%); cursor: pointer; transition: background-color 150ms ease, color 150ms ease, opacity 150ms ease; } +.creation-history-row:hover .creation-history-time-slot { opacity: 0; } +.creation-history-row:hover .creation-history-more, +.creation-history-row:focus-within .creation-history-more, +.creation-history-more.is-open, +.creation-history-more:focus-visible { opacity: 1; } +.creation-history-more:hover, +.creation-history-more.is-open { background: var(--creation-history-hover); color: var(--creation-history-text); } +.creation-history-more:focus-visible { outline: 2px solid color-mix(in srgb, var(--creation-history-accent) 66%, transparent); outline-offset: 1px; } +.creation-history-more svg { width: 16px; height: 16px; } +.creation-history-rename { display: flex; min-height: 52px; align-items: center; border-radius: var(--r-sm); background: var(--creation-history-hover); padding: 0 8px; } +.creation-history-rename-input { width: 100%; border: 0; border-radius: var(--r-sm); background: transparent; padding: 7px 8px; color: var(--creation-history-text); font: inherit; font-size: var(--fs-label); font-weight: 600; } +.creation-history-rename-input::selection { background: color-mix(in srgb, var(--creation-history-accent) 32%, transparent); } +.creation-history-rename-input:focus-visible { outline: 2px solid color-mix(in srgb, var(--creation-history-accent) 60%, transparent); outline-offset: 0; } .creation-history-empty { display: grid; min-height: 180px; place-items: center; color: var(--creation-history-muted); font-size: var(--fs-label); } +.creation-history-menu-overlay .ant-dropdown-menu { border-radius: var(--r-md); padding: 4px; min-width: 168px; } +.creation-history-menu-overlay .ant-dropdown-menu .ant-dropdown-menu-item { border-radius: 6px; } +@media (pointer: coarse) { + .creation-history-more { opacity: 1; } + .creation-history-row:hover .creation-history-time-slot { opacity: 1; } +} +@media (prefers-reduced-motion: reduce) { + .creation-history-row, + .creation-history-more, + .creation-history-time-slot { transition: none; } +} @media (max-width: 640px) { .creation-top-actions { top: 14px; right: 14px; } @@ -14684,829 +14707,6 @@ html:not(.dark) .creation-history-drawer-root { color: color-mix(in srgb, var(--foreground) 45%, transparent); } -/* ===== 创作页 · 分镜工作台(方案 B · 分镜台) ===== */ -.creation-home .storyboard-workbench { - display: flex; - flex: 1 1 auto; - min-height: 0; - flex-direction: column; -} - -/* 分镜台 · 顶部工具条(镜头图标轨道 + 新建创作/历史对话同排) */ -.creation-home .storyboard-workbench-bar { - display: flex; - flex: 0 0 auto; - min-height: 0; - align-items: center; - justify-content: space-between; - gap: 12px; - padding: 12px 28px; -} - -.creation-home .storyboard-workbench-rail { - position: relative; - display: flex; - flex: 0 0 auto; - align-items: center; -} - -/* 单一「镜头时间线」图标按钮 */ -.creation-home .storyboard-workbench-rail-button { - position: relative; - display: grid; - width: 36px; - height: 36px; - place-items: center; - border: 1px solid var(--creation-border); - border-radius: 10px; - background: var(--creation-surface); - color: var(--creation-muted); - cursor: pointer; - transition: border-color 150ms ease, background-color 150ms ease, color 150ms ease, transform 150ms var(--motion-ease-out); -} -.creation-home .storyboard-workbench-rail-button svg { width: 17px; height: 17px; } -.creation-home .storyboard-workbench-rail-button:hover { border-color: var(--creation-border-strong); background: var(--creation-surface-hover); color: var(--creation-text); transform: translateY(-1px); } -.creation-home .storyboard-workbench-rail-button:focus-visible { outline: 2px solid var(--creation-accent); outline-offset: 2px; } -.creation-home .storyboard-workbench-rail-button.is-open { border-color: var(--creation-text); background: var(--creation-text); color: var(--creation-bg); } -.creation-home .storyboard-workbench-rail-button.is-draft { border-style: dashed; border-color: var(--creation-border-strong); color: var(--creation-text); } - -.creation-home .storyboard-workbench-rail-badge { - position: absolute; - top: -5px; - right: -5px; - display: grid; - min-width: 16px; - height: 16px; - place-items: center; - border: 1px solid var(--creation-surface); - border-radius: 999px; - background: var(--creation-accent); - color: var(--creation-surface); - padding: 0 4px; - font-size: var(--fs-micro); - font-weight: 600; - font-variant-numeric: tabular-nums; - line-height: 1; -} - -/* 展开镜头列表弹层 */ -.creation-home .storyboard-workbench-rail-pop { - position: absolute; - z-index: 40; - top: calc(100% + 8px); - left: 0; - width: min(400px, calc(100vw - 32px)); - border: 1px solid var(--creation-border); - border-radius: 14px; - background: var(--creation-surface); - box-shadow: var(--elevation-card); - padding: 8px; -} -.creation-home .storyboard-workbench-rail-pop-head { - display: flex; - align-items: center; - justify-content: space-between; - padding: 4px 4px 8px; - color: var(--creation-text); - font-size: var(--fs-caption); - font-weight: 560; -} -.creation-home .storyboard-workbench-rail-pop-title { display: inline-flex; align-items: center; gap: 6px; } -.creation-home .storyboard-workbench-rail-pop-title svg { width: 15px; height: 15px; color: var(--creation-muted); } -.creation-home .storyboard-workbench-rail-pop-head small { color: var(--creation-faint); font-size: var(--fs-micro); font-weight: 400; letter-spacing: .06em; margin-left: 4px; } -.creation-home .storyboard-workbench-rail-pop-close { - display: grid; - width: 24px; - height: 24px; - place-items: center; - border: 0; - border-radius: 6px; - background: transparent; - color: var(--creation-muted); - cursor: pointer; - transition: background-color 150ms ease, color 150ms ease; -} -.creation-home .storyboard-workbench-rail-pop-close svg { width: 14px; height: 14px; } -.creation-home .storyboard-workbench-rail-pop-close:hover { background: var(--creation-surface-hover); color: var(--creation-text); } -.creation-home .storyboard-workbench-rail-pop-close:focus-visible { outline: 2px solid var(--creation-accent); outline-offset: 1px; } -.creation-home .storyboard-workbench-rail-pop ul { display: flex; flex-direction: column; gap: 3px; max-height: 300px; overflow-y: auto; padding: 0; margin: 0; list-style: none; } - -.creation-home .storyboard-workbench-rail-row { - display: flex; - width: 100%; - align-items: center; - gap: 10px; - border: 0; - border-radius: 10px; - background: transparent; - color: var(--creation-text); - padding: 6px; - text-align: left; - cursor: pointer; - transition: background-color 150ms ease, border-color 150ms ease; -} -.creation-home .storyboard-workbench-rail-row:hover { background: var(--creation-surface-hover); } -.creation-home .storyboard-workbench-rail-row:focus-visible { outline: 2px solid var(--creation-accent); outline-offset: -2px; } -.creation-home .storyboard-workbench-rail-row.is-active { background: var(--creation-surface-selected); } -.creation-home .storyboard-workbench-rail-row.is-draft { border: 1px dashed var(--creation-border-strong); color: var(--creation-muted); } - -.creation-home .storyboard-workbench-rail-thumb { - display: grid; - width: 84px; - height: 48px; - flex: 0 0 auto; - place-items: center; - overflow: hidden; - border: 1px solid var(--creation-border); - border-radius: 8px; - background: var(--creation-surface-hover); -} -.creation-home .storyboard-workbench-rail-thumb img, -.creation-home .storyboard-workbench-rail-thumb video { - width: 100%; - height: 100%; - object-fit: cover; -} -.creation-home .storyboard-workbench-rail-thumb-ph { - display: grid; - width: 100%; - height: 100%; - place-items: center; - gap: 2px; -} -.creation-home .storyboard-workbench-rail-thumb-ph svg { width: 16px; height: 16px; color: var(--creation-faint); } -.creation-home .storyboard-workbench-rail-thumb-ph em { color: var(--creation-faint); font-size: var(--fs-micro); font-style: normal; font-variant-numeric: tabular-nums; letter-spacing: .04em; } - -.creation-home .storyboard-workbench-rail-info { - display: flex; - min-width: 0; - flex: 1 1 auto; - flex-direction: column; - gap: 3px; -} -.creation-home .storyboard-workbench-rail-head { - display: flex; - align-items: center; - gap: 6px; -} -.creation-home .storyboard-workbench-rail-row-shot { color: var(--creation-text); font-size: var(--fs-caption); font-variant-numeric: tabular-nums; font-weight: 560; } -.creation-home .storyboard-workbench-rail-row-state { border-radius: 999px; background: rgba(148, 163, 184, .18); color: var(--creation-muted); padding: 1px 7px; font-size: var(--fs-micro); letter-spacing: .04em; white-space: nowrap; } -.creation-home .storyboard-workbench-rail-row-state.is-pending { background: rgba(148, 163, 184, .22); } -.creation-home .storyboard-workbench-rail-row-state.is-error { background: color-mix(in srgb, var(--palette-status-error) 14%, transparent); color: var(--palette-status-error); } -.creation-home .storyboard-workbench-rail-row-state.is-done { background: rgba(148, 163, 184, .28); color: var(--creation-text); } -.creation-home .storyboard-workbench-rail-row-title { overflow: hidden; color: var(--creation-text); font-size: var(--fs-body); text-overflow: ellipsis; white-space: nowrap; } -.creation-home .storyboard-workbench-rail-row.is-draft .storyboard-workbench-rail-row-title { color: inherit; } -.creation-home .storyboard-workbench-rail-row time { margin-left: auto; color: var(--creation-faint); font-size: var(--fs-micro); font-variant-numeric: tabular-nums; white-space: nowrap; } - -.creation-home .storyboard-workbench-rail-pop-add { - display: flex; - width: 100%; - align-items: center; - justify-content: center; - gap: 6px; - border: 1px dashed var(--creation-border); - border-radius: 8px; - background: transparent; - color: var(--creation-muted); - margin-top: 6px; - padding: 7px 8px; - font-size: var(--fs-caption); - cursor: pointer; - transition: border-color 150ms ease, background-color 150ms ease, color 150ms ease; -} -.creation-home .storyboard-workbench-rail-pop-add svg { width: 13px; height: 13px; } -.creation-home .storyboard-workbench-rail-pop-add:hover { border-color: var(--creation-border-strong); background: var(--creation-surface-hover); color: var(--creation-text); } -.creation-home .storyboard-workbench-rail-pop-add:focus-visible { outline: 2px solid var(--creation-accent); outline-offset: 2px; } - -.creation-home .storyboard-workbench-bar-actions { - display: flex; - flex: 0 0 auto; - align-items: center; - gap: 6px; -} - -.creation-home .storyboard-workbench-bar-action { - display: grid; - width: 36px; - height: 36px; - place-items: center; - border: 0; - border-radius: 8px; - background: rgba(255, 255, 255, .06); - color: var(--creation-muted); - cursor: pointer; - transition: background-color 150ms ease, color 150ms ease, transform 150ms var(--motion-ease-out); -} -.creation-home .storyboard-workbench-bar-action svg { width: 16px; height: 16px; } -.creation-home .storyboard-workbench-bar-action:hover { background: rgba(255, 255, 255, .12); color: var(--creation-text); transform: translateY(-1px); } -.creation-home .storyboard-workbench-bar-action:focus-visible { outline: 2px solid var(--creation-accent); outline-offset: 2px; } -html:not(.dark) .creation-home .storyboard-workbench-bar-action { background: rgba(28, 32, 37, .055); } -html:not(.dark) .creation-home .storyboard-workbench-bar-action:hover { background: rgba(28, 32, 37, .11); } - -/* 分镜卡舞台 */ -.creation-home .storyboard-workbench-stage { - flex: 1 1 auto; - min-height: 0; - overflow-y: auto; - overscroll-behavior: contain; - padding: 0 28px 18px; -} - -.creation-home .storyboard-workbench-stage-inner { - width: min(1120px, 100%); - margin: 0 auto; - animation: creation-composer-arrive var(--motion-dur-slow-calc) var(--motion-ease-out) both; -} - -.creation-home .storyboard-workbench-card { - overflow: hidden; - border: 0; - border-radius: var(--r-lg); - background: var(--creation-surface); - box-shadow: none; -} - -.creation-home .storyboard-workbench-card.is-error { background: color-mix(in srgb, var(--palette-status-error) 6%, var(--creation-surface)); } - -.creation-home .storyboard-workbench-card-head { - display: flex; - min-width: 0; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - gap: 10px 14px; - padding: 14px 18px; -} - -.creation-home .storyboard-workbench-card-heading { - display: flex; - min-width: 0; - flex-wrap: wrap; - align-items: center; - gap: 8px; -} - -.creation-home .storyboard-workbench-card-shot { - display: inline-flex; - align-items: center; - gap: 7px; - color: var(--creation-text); - font-size: var(--fs-body); - font-weight: 600; -} - -.creation-home .storyboard-workbench-card-shot-index { - display: grid; - min-width: 26px; - height: 22px; - place-items: center; - border-radius: 6px; - background: color-mix(in srgb, var(--creation-text) 9%, transparent); - color: var(--creation-text); - font-size: var(--fs-tiny); - font-variant-numeric: tabular-nums; -} - -.creation-home .storyboard-workbench-card-mode { - display: inline-flex; - align-items: center; - gap: 5px; - border: 0; - border-radius: 999px; - background: var(--creation-surface-muted); - color: var(--creation-muted); - padding: 3px 9px; - font-size: var(--fs-tiny); - white-space: nowrap; -} - -.creation-home .storyboard-workbench-card-mode svg { width: 12px; height: 12px; } -.creation-home .storyboard-workbench-card-model { min-width: 0; overflow: hidden; color: var(--creation-muted); font-size: var(--fs-caption); text-overflow: ellipsis; white-space: nowrap; } - -.creation-home .storyboard-workbench-card-state { - display: inline-flex; - align-items: center; - gap: 5px; - border-radius: 999px; - color: var(--creation-muted); - padding: 3px 9px; - font-size: var(--fs-tiny); - white-space: nowrap; -} - -.creation-home .storyboard-workbench-card-state svg { width: 12px; height: 12px; } -.creation-home .storyboard-workbench-card-state.is-pending { background: color-mix(in srgb, var(--creation-accent) 12%, transparent); color: var(--creation-accent); } -.creation-home .storyboard-workbench-card-state.is-error { background: color-mix(in srgb, var(--palette-status-error) 12%, transparent); color: var(--palette-status-error); } -.creation-home .storyboard-workbench-card-state.is-done { background: color-mix(in srgb, var(--creation-text) 8%, transparent); color: var(--creation-muted); } - -.creation-home .storyboard-workbench-card-actions { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 6px; -} - -.creation-home .storyboard-workbench-card-actions button, -.creation-home .storyboard-workbench-card-actions a { - display: inline-flex; - align-items: center; - gap: 5px; - border: 0; - border-radius: 8px; - background: var(--creation-surface-muted); - color: var(--creation-muted); - padding: 6px 10px; - font-size: var(--fs-caption); - text-decoration: none; - cursor: pointer; - transition: color 140ms ease, border-color 140ms ease, background-color 140ms ease; -} - -.creation-home .storyboard-workbench-card-actions button:hover, -.creation-home .storyboard-workbench-card-actions a:hover { background: var(--creation-surface-hover); color: var(--creation-text); } -.creation-home .storyboard-workbench-card-actions button:disabled { cursor: not-allowed; opacity: .5; } -.creation-home .storyboard-workbench-card-actions button:focus-visible, -.creation-home .storyboard-workbench-card-actions a:focus-visible { outline: 2px solid var(--creation-accent); outline-offset: 2px; } -.creation-home .storyboard-workbench-card-actions svg { width: 13px; height: 13px; } - -.creation-home .storyboard-workbench-card-body { padding: 18px; } - -/* 镜头脚本区 */ -.creation-home .storyboard-workbench-brief { - margin-bottom: 18px; - border: 0; - border-radius: 12px; - background: var(--creation-surface-muted); - padding: 13px 15px; -} - -.creation-home .storyboard-workbench-brief-label { - display: flex; - align-items: center; - gap: 6px; - margin: 0 0 8px; - color: var(--creation-faint); - font-size: var(--fs-tiny); - font-weight: 560; - letter-spacing: .08em; -} - -.creation-home .storyboard-workbench-brief-label svg { width: 12px; height: 12px; } -.creation-home .storyboard-workbench-brief-text { margin: 0; color: var(--creation-text); font-size: var(--fs-body); line-height: 1.75; white-space: pre-wrap; } -.creation-home .storyboard-workbench-brief .creation-user-message-references { margin-top: 10px; } -.creation-home .storyboard-workbench-brief-attachments { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; } - -/* 结果区 */ -.creation-home .storyboard-workbench-pending, -.creation-home .storyboard-workbench-empty { - display: flex; - min-height: 120px; - align-items: center; - justify-content: center; - gap: 8px; - border: 0; - border-radius: 12px; - background: var(--creation-surface-muted); - color: var(--creation-muted); - font-size: var(--fs-caption); -} - -.creation-home .storyboard-workbench-pending svg { width: 16px; height: 16px; color: var(--creation-accent); } -.creation-home .storyboard-workbench-empty svg { width: 16px; height: 16px; } -.creation-home .storyboard-workbench-empty button { - border: 0; - background: transparent; - color: var(--creation-text); - font-size: var(--fs-caption); - text-decoration: underline; - cursor: pointer; -} - -.creation-home .storyboard-workbench-error { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 10px; - border: 0; - border-radius: 12px; - background: color-mix(in srgb, var(--palette-status-error) 7%, transparent); - padding: 13px 15px; - color: var(--palette-status-error); - font-size: var(--fs-caption); - line-height: 1.6; -} - -.creation-home .storyboard-workbench-error button { - display: inline-flex; - height: 28px; - align-items: center; - gap: 5px; - border: 0; - border-radius: 6px; - background: color-mix(in srgb, var(--palette-status-error) 14%, transparent); - color: var(--palette-status-error); - padding: 0 9px; - font-size: var(--fs-label); - cursor: pointer; -} - -.creation-home .storyboard-workbench-error button:hover { background: color-mix(in srgb, var(--palette-status-error) 24%, transparent); } -.creation-home .storyboard-workbench-error svg { width: 13px; height: 13px; } -.creation-home .storyboard-workbench-text { max-width: none; padding: 0; } - -.creation-home .storyboard-workbench-media-meta { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 10px; - margin-top: 12px; - color: var(--creation-faint); - font-size: var(--fs-label); -} - -.creation-home .storyboard-workbench-media-meta > span { margin-right: 2px; } -.creation-home .storyboard-workbench-media-meta button, -.creation-home .storyboard-workbench-media-meta a { - display: inline-flex; - align-items: center; - gap: 4px; - border: 0; - background: transparent; - color: var(--creation-muted); - font-size: var(--fs-label); - text-decoration: none; - cursor: pointer; -} - -.creation-home .storyboard-workbench-media-meta button:hover, -.creation-home .storyboard-workbench-media-meta a:hover { color: var(--creation-text); } -.creation-home .storyboard-workbench-media-meta svg { width: 13px; height: 13px; } - -/* 撰写区 */ -.creation-home .storyboard-workbench-composer { - flex: 0 0 auto; - padding: 8px 28px 22px; -} - -.creation-home .storyboard-workbench-composer .creation-chat-composer { - width: min(1120px, 100%); - margin: 0 auto; -} - -@media (max-width: 800px) { - .creation-home .storyboard-workbench-bar { padding: 10px 14px; } - .creation-home .storyboard-workbench-rail-pop { width: min(340px, calc(100vw - 24px)); } - .creation-home .storyboard-workbench-stage { padding: 0 14px 14px; } - .creation-home .storyboard-workbench-composer { padding-right: 12px; padding-left: 12px; } - .creation-home .storyboard-workbench-card-head { padding: 12px 14px; } - .creation-home .storyboard-workbench-card-body { padding: 14px; } -} - -/* 空态分镜台预览:轨道幽灵镜头位 + 空分镜卡 */ -.creation-home .storyboard-workbench .storyboard-workbench-shot.is-ghost { - border-style: dashed; - border-color: var(--creation-border); - background: transparent; - box-shadow: none; - cursor: default; - opacity: .62; - pointer-events: none; -} - -.creation-home .storyboard-workbench .storyboard-workbench-shot.is-ghost .storyboard-workbench-shot-thumb { - background: color-mix(in srgb, var(--creation-text) 4%, transparent); -} - -.creation-home .storyboard-workbench .storyboard-workbench-shot.is-ghost:hover { - border-color: var(--creation-border); - background: transparent; - transform: none; -} - -.creation-home .storyboard-workbench .storyboard-workbench-card.is-empty { - border-style: solid; -} - -.creation-home .storyboard-workbench-empty-hero { - padding: clamp(22px, 5vh, 48px) 8px clamp(18px, 3.5vh, 32px); - text-align: center; -} - -.creation-home .storyboard-workbench-empty-kicker { - display: inline-flex; - align-items: center; - gap: 6px; - color: var(--creation-faint); - font-size: var(--fs-label); - letter-spacing: .12em; -} - -.creation-home .storyboard-workbench-empty-hero h1 { - margin: 12px 0 0; - color: var(--creation-text); - font-size: var(--fs-title); - font-weight: 620; - letter-spacing: 0; - line-height: 1.4; -} - -.creation-home .storyboard-workbench-empty-hero p { - max-width: 52ch; - margin: 12px auto 0; - color: var(--creation-muted); - font-size: var(--fs-body); - line-height: 1.8; -} - -.creation-home .storyboard-workbench-empty-steps { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: center; - gap: 8px; - margin-top: 20px; -} - -.creation-home .storyboard-workbench-empty-steps span { - display: inline-flex; - align-items: center; - gap: 7px; - border: 1px solid var(--creation-border); - border-radius: 999px; - background: var(--creation-surface-muted); - color: var(--creation-muted); - padding: 6px 13px; - font-size: var(--fs-caption); - white-space: nowrap; -} - -.creation-home .storyboard-workbench-empty-steps em { - color: var(--creation-text); - font-size: var(--fs-micro); - font-style: normal; - font-variant-numeric: tabular-nums; - font-weight: 600; - letter-spacing: .02em; -} - -.creation-home .storyboard-workbench-empty-preview { - display: flex; - align-items: center; - gap: 13px; - border: 1px dashed var(--creation-border); - border-radius: 12px; - background: var(--creation-surface-muted); - padding: 15px 17px; -} - -.creation-home .storyboard-workbench-empty-preview-icon { - display: grid; - width: 40px; - height: 40px; - flex: 0 0 auto; - place-items: center; - border-radius: 10px; - background: color-mix(in srgb, var(--creation-text) 7%, transparent); - color: var(--creation-faint); -} - -.creation-home .storyboard-workbench-empty-preview-icon svg { width: 17px; height: 17px; } - -.creation-home .storyboard-workbench-empty-preview-copy strong { - display: block; - color: var(--creation-text); - font-size: var(--fs-caption); - font-weight: 560; -} - -.creation-home .storyboard-workbench-empty-preview-copy span { - display: block; - margin-top: 3px; - color: var(--creation-faint); - font-size: var(--fs-label); - line-height: 1.6; -} - -@media (max-width: 800px) { - .creation-home .storyboard-workbench-empty-hero { padding-top: 24px; padding-bottom: 20px; } - .creation-home .storyboard-workbench-empty-preview { padding: 13px 14px; } - .creation-home .storyboard-workbench-empty-steps span { padding: 5px 10px; } -} - -/* 对话时间线:你 → 影策 AI */ -.creation-home .storyboard-workbench-thread { - display: flex; - flex-direction: column; - gap: 6px; -} - -.creation-home .storyboard-workbench-turn { - display: flex; - min-width: 0; -} - -.creation-home .storyboard-workbench-turn.is-user { justify-content: flex-end; } -.creation-home .storyboard-workbench-turn.is-ai { align-items: flex-start; gap: 10px; } - -.creation-home .storyboard-workbench-turn-copy { - display: flex; - min-width: 0; - flex-direction: column; -} - -.creation-home .storyboard-workbench-turn.is-user .storyboard-workbench-turn-copy { max-width: min(82%, 680px); align-items: flex-end; } -.creation-home .storyboard-workbench-turn.is-ai .storyboard-workbench-turn-copy { flex: 1 1 auto; } - -.creation-home .storyboard-workbench-turn-meta { - display: flex; - align-items: center; - gap: 8px; - margin: 0 2px 6px; -} - -.creation-home .storyboard-workbench-turn.is-user .storyboard-workbench-turn-meta { justify-content: flex-end; } - -.creation-home .storyboard-workbench-turn-role { - color: var(--creation-faint); - font-size: var(--fs-label); - font-weight: 600; - letter-spacing: .04em; -} - -.creation-home .storyboard-workbench-turn-role.is-ai { - display: inline-flex; - align-items: center; - gap: 5px; - color: var(--creation-text); -} - -.creation-home .storyboard-workbench-turn-role.is-ai svg { width: 12px; height: 12px; color: var(--creation-accent); } -.creation-home .storyboard-workbench-turn-model { color: var(--creation-faint); font-size: var(--fs-micro); } -.creation-home .storyboard-workbench-turn-time { color: var(--creation-faint); font-size: var(--fs-micro); font-variant-numeric: tabular-nums; } - -/* 用户气泡 */ -.creation-home .storyboard-workbench-turn.is-user .storyboard-workbench-turn-bubble { - min-width: 0; - border: 0; - border-radius: var(--r-lg) var(--r-sm) var(--r-lg) var(--r-lg); - background: var(--creation-surface-muted); - padding: 11px 14px; -} - -.creation-home .storyboard-workbench-turn-text { margin: 0; color: var(--creation-text); font-size: var(--fs-body); line-height: 1.7; white-space: pre-wrap; overflow-wrap: anywhere; } - -/* 移交标记 */ -.creation-home .storyboard-workbench-handoff { - display: flex; - align-items: center; - gap: 12px; - justify-content: center; - margin: 8px 0; -} - -.creation-home .storyboard-workbench-handoff-rail { display: none; } - -.creation-home .storyboard-workbench-handoff-badge { - display: inline-flex; - align-items: center; - gap: 5px; - color: var(--creation-faint); - font-size: var(--fs-micro); - letter-spacing: .08em; - white-space: nowrap; -} - -.creation-home .storyboard-workbench-handoff-badge svg { width: 12px; height: 12px; } - -/* 影策 AI 头像 */ -.creation-home .storyboard-workbench-ai-avatar { - display: grid; - width: 30px; - height: 30px; - flex: 0 0 auto; - place-items: center; - border-radius: 9px; - background: var(--creation-text); - color: var(--creation-bg); - box-shadow: 0 0 0 1px color-mix(in srgb, var(--creation-text) 18%, transparent); -} - -.creation-home .storyboard-workbench-ai-avatar svg { width: 15px; height: 15px; } -.creation-home .storyboard-workbench-ai-avatar.is-large { width: 42px; height: 42px; border-radius: 12px; } -.creation-home .storyboard-workbench-ai-avatar.is-large svg { width: 19px; height: 19px; } - -/* 生成中思考管线 */ -.creation-home .storyboard-workbench-pending { - display: flex; - min-height: 0; - align-items: stretch; - justify-content: flex-start; - border: 0; - border-radius: 12px; - background: var(--creation-surface-muted); -} - -.creation-home .storyboard-workbench-thinking { display: flex; width: 100%; flex-direction: column; gap: 14px; padding: 14px 16px; } -.creation-home .storyboard-workbench-thinking-copy { display: flex; flex-direction: column; gap: 4px; } -.creation-home .storyboard-workbench-thinking-copy strong { color: var(--creation-text); font-size: var(--fs-body); font-weight: 600; } -.creation-home .storyboard-workbench-thinking-copy span { color: var(--creation-muted); font-size: var(--fs-label); line-height: 1.7; } - -.creation-home .storyboard-workbench-pipeline { display: flex; flex-wrap: wrap; gap: 8px; } - -.creation-home .storyboard-workbench-pipeline em { - display: inline-flex; - align-items: center; - gap: 7px; - border: 0; - border-radius: 999px; - background: var(--creation-surface); - color: var(--creation-faint); - padding: 5px 12px; - font-size: var(--fs-caption); - font-style: normal; - opacity: 0; - animation: creation-pipeline-step 480ms ease forwards; - animation-delay: calc(var(--step) * 520ms + 200ms); -} - -.creation-home .storyboard-workbench-pipeline em i { color: var(--creation-text); font-size: var(--fs-micro); font-style: normal; font-variant-numeric: tabular-nums; font-weight: 600; } -.creation-home .storyboard-workbench-pipeline em:last-child { background: color-mix(in srgb, var(--creation-accent) 10%, var(--creation-surface)); color: var(--creation-text); } - -@keyframes creation-pipeline-step { - 0% { opacity: 0; transform: translateY(4px); } - 100% { opacity: 1; transform: translateY(0); } -} - -/* 导演手记 */ -.creation-home .storyboard-workbench-director-note { - display: flex; - align-items: flex-start; - gap: 9px; - margin-top: 12px; - border: 0; - border-radius: 10px; - background: color-mix(in srgb, var(--creation-text) 4%, transparent); - padding: 10px 13px; - color: var(--creation-muted); - font-size: var(--fs-label); - line-height: 1.7; -} - -.creation-home .storyboard-workbench-director-note span { flex: 0 0 auto; color: var(--creation-text); font-weight: 600; } - -/* 空态影策开场 */ -.creation-home .storyboard-workbench-empty-opener { - display: flex; - align-items: flex-start; - gap: 14px; - padding: 8px 2px 2px; -} - -.creation-home .storyboard-workbench-empty-opener-copy { min-width: 0; } - -.creation-home .storyboard-workbench-empty-opener-name { - display: inline-flex; - align-items: center; - gap: 5px; - color: var(--creation-faint); - font-size: var(--fs-label); - font-weight: 600; -} - -.creation-home .storyboard-workbench-empty-opener-name svg { width: 12px; height: 12px; color: var(--creation-accent); } -.creation-home .storyboard-workbench-empty-opener-copy h1 { margin: 9px 0 0; color: var(--creation-text); font-size: var(--fs-title); font-weight: 620; line-height: 1.4; } -.creation-home .storyboard-workbench-empty-opener-copy p { max-width: 58ch; margin: 9px 0 0; color: var(--creation-muted); font-size: var(--fs-caption); line-height: 1.8; } - -.creation-home .storyboard-workbench-empty-starters { margin-top: 22px; } -.creation-home .storyboard-workbench-empty-starters-label { display: block; margin-bottom: 10px; color: var(--creation-faint); font-size: var(--fs-label); } -.creation-home .storyboard-workbench-empty-starters-list { display: grid; gap: 8px; } - -.creation-home .storyboard-workbench-empty-starters-list button { - display: flex; - align-items: flex-start; - gap: 9px; - border: 1px dashed var(--creation-border); - border-radius: 10px; - background: transparent; - color: var(--creation-muted); - padding: 9px 12px; - font-size: var(--fs-caption); - text-align: left; - line-height: 1.65; - cursor: pointer; - transition: border-color 140ms ease, background-color 140ms ease, color 140ms ease; -} - -.creation-home .storyboard-workbench-empty-starters-list button:hover { border-color: var(--creation-border-strong); background: var(--creation-surface-hover); color: var(--creation-text); } -.creation-home .storyboard-workbench-empty-starters-list button:focus-visible { outline: 2px solid var(--creation-accent); outline-offset: 2px; } -.creation-home .storyboard-workbench-empty-starters-list button svg { width: 13px; height: 13px; flex: 0 0 auto; margin-top: 3px; color: var(--creation-accent); } - -@media (max-width: 800px) { - .creation-home .storyboard-workbench-turn.is-user .storyboard-workbench-turn-copy { max-width: 88%; } - .creation-home .storyboard-workbench-pipeline { gap: 6px; } - .creation-home .storyboard-workbench-pipeline em { padding: 4px 10px; } - .creation-home .storyboard-workbench-empty-opener { gap: 11px; } -} /* 空态:banner + 对话区(控态时展示,发送后切换分镜台) */ .creation-home .creation-empty-workspace { @@ -15527,65 +14727,16 @@ html:not(.dark) .creation-home .storyboard-workbench-bar-action:hover { backgrou .creation-empty-workspace .creation-empty-composer { width: 100%; } } -/* 舞台:下一镜草稿引导卡 */ -.creation-home .storyboard-workbench-card.is-next { - background: var(--creation-surface); - box-shadow: none; -} - -.creation-home .storyboard-workbench-next-panel { - display: flex; - align-items: flex-start; - gap: 14px; - border: 0; - border-radius: 12px; - background: var(--creation-surface-muted); - padding: 16px 18px; -} - -.creation-home .storyboard-workbench-next-panel-icon { - display: grid; - width: 44px; - height: 44px; - flex: 0 0 auto; - place-items: center; - border-radius: 12px; - background: color-mix(in srgb, var(--creation-text) 7%, transparent); - color: var(--creation-faint); -} - -.creation-home .storyboard-workbench-next-panel-icon svg { width: 19px; height: 19px; } - -.creation-home .storyboard-workbench-next-panel-copy strong { - display: block; - color: var(--creation-text); - font-size: var(--fs-body); - font-weight: 600; - line-height: 1.5; -} - -.creation-home .storyboard-workbench-next-panel-copy span { - display: block; - max-width: 58ch; - margin-top: 6px; - color: var(--creation-muted); - font-size: var(--fs-caption); - line-height: 1.8; -} - -@media (max-width: 800px) { - .creation-home .storyboard-workbench-next-panel { padding: 13px 14px; } - .creation-home .storyboard-workbench-next-panel-icon { width: 38px; height: 38px; } -} .creation-home .creation-empty-suggest { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 10px; + display: flex; + flex-direction: column; + gap: 14px; width: min(820px, calc(100% - 28px)); - margin: 22px auto 0; + margin: 24px auto 0; } + .creation-home .creation-empty-suggest .suggest-card { display: flex; align-items: flex-start; @@ -15612,13 +14763,17 @@ html:not(.dark) .creation-home .storyboard-workbench-bar-action:hover { backgrou } .creation-home .creation-empty-suggest .suggest-icon { - width: 36px; - height: 36px; - border-radius: var(--r-sm); - color: color-mix(in srgb, var(--creation-text) 82%, transparent); + width: 34px; + height: 34px; + border: 1px solid var(--creation-border); + border-radius: var(--r-md); + background: color-mix(in srgb, var(--creation-surface-muted) 68%, transparent); + color: var(--creation-muted); } - -.creation-home .creation-empty-suggest .suggest-icon svg { width: 15px; height: 15px; stroke-width: 2; } +.creation-home .creation-empty-suggest .suggest-icon svg { width: 16px; height: 16px; stroke-width: 2; } +.creation-home .creation-empty-suggest .suggest-card:hover .suggest-icon, +.creation-home .creation-hero-prompt:hover .suggest-icon { border-color: var(--creation-border-strong); background: var(--creation-surface-hover); color: var(--creation-text); } +.dark .creation-home .creation-empty-suggest .suggest-icon { border-color: rgba(255, 255, 255, .07); background: rgba(38, 41, 48, .44); } .creation-home .creation-empty-suggest .suggest-copy strong { display: block; @@ -15636,10 +14791,6 @@ html:not(.dark) .creation-home .storyboard-workbench-bar-action:hover { backgrou line-height: 1.55; } -@media (max-width: 800px) { - .creation-home .creation-empty-suggest { grid-template-columns: 1fr; } -} - /* 空态布局收敛:保持图片区、文案和输入区的文档流顺序,内容超高时由主区滚动承载。 */ .creation-home .creation-empty-workspace { justify-content: flex-start; @@ -15651,7 +14802,6 @@ html:not(.dark) .creation-home .storyboard-workbench-bar-action:hover { backgrou .creation-home .creation-empty-workspace > .creation-empty-suggest { flex: 0 0 auto; } - .creation-empty-workspace > .creation-empty-art { width: min(820px, calc(100% - 28px)); height: 200px; @@ -16182,11 +15332,146 @@ html:not(.dark) .creation-home .storyboard-workbench-bar-action:hover { backgrou .creation-home .creation-thread-toolbar { display: flex; flex: 0 0 auto; + width: min(900px, calc(100% - 56px)); align-items: center; - justify-content: flex-end; + justify-content: space-between; gap: var(--space-2); - padding: var(--space-3) var(--space-6); + padding: var(--space-3) 0; + margin: 0 auto; } +.creation-home .creation-toolbar-actions { display: inline-flex; align-items: center; gap: var(--space-1); } +.creation-home .creation-toolbar-action { + display: inline-flex; + min-height: var(--space-8); + align-items: center; + justify-content: center; + gap: var(--space-1); + border: 1px solid transparent; + border-radius: var(--r-md); + background: transparent; + color: var(--creation-muted); + padding: 0 var(--space-2); + cursor: pointer; + transition: background-color var(--motion-state) ease, color var(--motion-state) ease; +} +.creation-home .creation-toolbar-action:hover { background: var(--creation-surface-hover); color: var(--creation-text); } +.creation-home .creation-toolbar-action:focus-visible { outline: 2px solid var(--creation-accent); outline-offset: 1px; } +.creation-home .creation-toolbar-action svg { width: var(--space-4); height: var(--space-4); } +.creation-home .creation-toolbar-shots { position: relative; display: inline-flex; } +.creation-home .creation-rail-trigger { + display: inline-flex; + min-height: var(--space-8); + align-items: center; + gap: var(--space-1); + border: 1px solid transparent; + border-radius: var(--r-md); + background: transparent; + color: var(--creation-muted); + padding: 0 var(--space-2); + font-size: var(--fs-caption); + font-weight: 560; + cursor: pointer; + transition: background-color var(--motion-state) ease, color var(--motion-state) ease; +} +.creation-home .creation-rail-trigger:hover, +.creation-home .creation-rail-trigger[aria-expanded="true"] { background: var(--creation-surface-hover); color: var(--creation-text); } +.creation-home .creation-rail-trigger:focus-visible { outline: 2px solid var(--creation-accent); outline-offset: 1px; } +.creation-home .creation-rail-trigger svg { width: var(--space-4); height: var(--space-4); } +.creation-home .creation-rail-count { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 16px; + height: 16px; + border-radius: 99px; + background: color-mix(in srgb, var(--creation-accent) 14%, transparent); + color: var(--creation-accent); + padding: 0 var(--space-1); + font-size: var(--fs-micro); + font-style: normal; + font-weight: 620; + font-variant-numeric: tabular-nums; + line-height: 1; +} +.creation-home .creation-rail-pop { + position: absolute; + z-index: 30; + top: calc(100% + var(--space-2)); + left: 0; + width: min(360px, calc(100vw - 48px)); + border: 1px solid var(--creation-border); + border-radius: var(--r-lg); + background: var(--creation-surface); + box-shadow: var(--shadow-popover); + padding: var(--space-2); +} +.creation-home .creation-rail-pop-head { display: flex; align-items: center; justify-content: space-between; padding: var(--space-1) var(--space-2) var(--space-2); } +.creation-home .creation-rail-pop-title { display: flex; align-items: baseline; gap: var(--space-2); color: var(--creation-text); font-size: var(--fs-body); font-weight: 620; } +.creation-home .creation-rail-pop-title small { color: var(--creation-muted); font-size: var(--fs-micro); font-weight: 480; } +.creation-home .creation-rail-pop-close { + display: inline-flex; + align-items: center; + justify-content: center; + border: 0; + border-radius: var(--r-sm); + background: transparent; + color: var(--creation-muted); + padding: var(--space-1); + cursor: pointer; +} +.creation-home .creation-rail-pop-close:hover { background: var(--creation-surface-hover); color: var(--creation-text); } +.creation-home .creation-rail-pop-close svg { width: var(--space-4); height: var(--space-4); } +.creation-home .creation-rail-list { list-style: none; margin: 0; padding: 0; max-height: min(46vh, 320px); overflow-y: auto; } +.creation-home .creation-rail-row { + display: grid; + width: 100%; + grid-template-columns: auto minmax(0, 1fr) auto; + align-items: center; + gap: var(--space-2); + border: 0; + border-radius: var(--r-md); + background: transparent; + color: var(--creation-text); + padding: var(--space-2); + text-align: left; + cursor: pointer; + transition: background-color var(--motion-state) ease; +} +.creation-home .creation-rail-row:hover { background: var(--creation-surface-hover); } +.creation-home .creation-rail-row:focus-visible { outline: 2px solid var(--creation-accent); outline-offset: -2px; } +.creation-home .creation-rail-row-shot { + display: inline-flex; + min-width: 44px; + align-items: center; + justify-content: center; + border-radius: var(--r-sm); + background: color-mix(in srgb, var(--creation-accent) 10%, transparent); + color: var(--creation-accent); + padding: var(--space-1) var(--space-2); + font-size: var(--fs-micro); + font-weight: 620; + font-variant-numeric: tabular-nums; +} +.creation-home .creation-rail-row-prompt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--creation-text); font-size: var(--fs-caption); } +.creation-home .creation-rail-row-state { color: var(--creation-muted); font-size: var(--fs-micro); white-space: nowrap; } +.creation-home .creation-rail-row-state.is-done { color: var(--palette-status-success); } +.creation-home .creation-rail-row-state.is-error { color: var(--palette-status-error); } +.creation-home .creation-rail-row-state.is-pending { color: var(--creation-accent); } +.creation-home .creation-rail-empty { margin: 0; color: var(--creation-muted); padding: var(--space-3) var(--space-2); font-size: var(--fs-caption); } +.creation-home .creation-shot-badge { + display: inline-flex; + align-items: center; + border: 1px solid color-mix(in srgb, var(--creation-accent) 40%, transparent); + border-radius: var(--r-sm); + background: color-mix(in srgb, var(--creation-accent) 10%, transparent); + color: var(--creation-accent); + padding: 0 var(--space-2); + font-size: var(--fs-micro); + font-weight: 620; + font-variant-numeric: tabular-nums; + line-height: 1.6; +} +.creation-home .creation-thread-message { scroll-margin-top: 92px; } .creation-home .creation-thread-scroll { flex: 1 1 auto; @@ -16195,43 +15480,6 @@ html:not(.dark) .creation-home .storyboard-workbench-bar-action:hover { backgrou overscroll-behavior: contain; } -.creation-home .creation-view-switch { - display: inline-flex; - align-items: center; - gap: var(--space-1); - border-radius: var(--r-md); - background: var(--creation-surface-muted); - padding: var(--space-1); -} - -.creation-home .creation-view-switch button { - display: inline-flex; - min-height: var(--space-8); - align-items: center; - gap: var(--space-1); - border: 0; - border-radius: var(--r-sm); - background: transparent; - color: var(--creation-muted); - padding: 0 var(--space-3); - font-size: var(--fs-caption); - cursor: pointer; - transition: background-color var(--motion-state) ease, color var(--motion-state) ease; -} - -.creation-home .creation-view-switch button[aria-pressed="true"] { - background: var(--creation-surface-selected); - color: var(--creation-text); -} - -.creation-home .creation-view-switch button:focus-visible { - outline: 2px solid var(--creation-accent); - outline-offset: 1px; -} - -.creation-home .creation-view-switch svg { width: var(--space-4); height: var(--space-4); } - -.creation-home .storyboard-workbench-bar-actions .creation-view-switch { margin-right: var(--space-2); } .creation-home .creation-thread-stage { width: min(900px, calc(100% - 56px)); @@ -16252,12 +15500,12 @@ html:not(.dark) .creation-home .storyboard-workbench-bar-action:hover { backgrou .creation-home .creation-user-message { display: flex; - width: min(680px, 86%); + width: min(560px, 86%); max-width: none; flex-direction: column; - align-items: flex-start; + align-items: flex-end; gap: var(--space-2); - margin: 0 0 var(--space-10); + margin: 0 0 var(--space-10) auto; border-radius: 0; background: transparent; padding: 0; @@ -16304,7 +15552,11 @@ html:not(.dark) .creation-home .creation-user-message { background: transparent; .creation-home .creation-user-message-copy svg { width: var(--space-3); height: var(--space-3); } .creation-home .creation-user-message-copy-wrap { + width: fit-content; max-width: 100%; + border-radius: var(--r-lg) var(--r-lg) 4px var(--r-lg); + background: color-mix(in srgb, var(--creation-accent) 10%, var(--creation-surface)); + padding: 10px 14px; } .creation-home .creation-user-message-copy-wrap p { @@ -16496,7 +15748,6 @@ html:not(.dark) .creation-home .creation-user-message { background: transparent; @media (max-width: 800px) { .creation-home .creation-thread-toolbar { align-items: flex-end; padding: var(--space-3); } - .creation-home .creation-view-switch button { padding: 0 var(--space-2); } .creation-home .creation-thread-stage, .creation-home .creation-thread-composer { width: calc(100% - 28px); } .creation-home .creation-thread-stage { padding-top: var(--space-5); } @@ -16608,4 +15859,107 @@ html:not(.dark) .creation-home .creation-user-message { background: transparent; .ra-pop-in { animation: none; } -} \ No newline at end of file + + +} + +/* —— 创建页空态推荐主卡(吸收 boardui pro-prompt 光晕卡形态)—— */ +.creation-home .creation-hero-prompt { + position: relative; + display: flex; + width: 100%; + align-items: center; + gap: var(--space-3); + overflow: hidden; + border: 1px solid var(--creation-border); + border-radius: var(--r-lg); + background: var(--creation-surface); + padding: var(--space-4) var(--space-5); + text-align: left; + cursor: pointer; + transition: border-color var(--motion-state) ease, transform var(--motion-state) ease, box-shadow var(--motion-state) ease; +} +.creation-home .creation-hero-prompt:hover { border-color: var(--creation-border-strong); box-shadow: 0 10px 30px -16px color-mix(in srgb, var(--creation-accent) 46%, transparent); transform: translateY(-1px); } +.creation-home .creation-hero-prompt:focus-visible { outline: 2px solid var(--creation-accent); outline-offset: 2px; } +.creation-home .creation-hero-prompt-ray { + position: absolute; + inset: -60% -22% auto auto; + width: 72%; + aspect-ratio: 1; + background: radial-gradient(closest-side, color-mix(in srgb, var(--creation-accent) 17%, transparent), transparent 72%); + filter: blur(2px); + pointer-events: none; +} +.creation-home .creation-hero-prompt-copy { position: relative; display: flex; min-width: 0; flex-direction: column; gap: 2px; } +.creation-home .creation-hero-prompt-copy strong { color: var(--creation-text); font-size: var(--fs-body); font-weight: 640; } +.creation-home .creation-hero-prompt-hint { color: var(--creation-muted); font-size: var(--fs-caption); } +.creation-home .creation-hero-prompt-text { + overflow: hidden; + margin-top: var(--space-1); + color: color-mix(in srgb, var(--creation-text) 78%, var(--creation-accent)); + font-size: var(--fs-caption); + font-style: normal; + line-height: 1.6; + text-overflow: ellipsis; + white-space: nowrap; +} +.creation-home .creation-hero-prompt .suggest-icon { position: relative; flex: 0 0 auto; } +.creation-home .creation-hero-prompt-spark { + position: relative; + flex: 0 0 auto; + margin-left: auto; + color: color-mix(in srgb, var(--creation-accent) 80%, var(--creation-muted)); + width: var(--space-4); + height: var(--space-4); + opacity: .85; +} +.creation-home .creation-empty-suggest-grid { display: grid; width: 100%; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); } +@media (max-width: 760px) { + .creation-home .creation-empty-suggest-grid { grid-template-columns: 1fr; } +} + + +/* —— 创建页历史抽屉:新建置顶卡 + 时间分组(吸收 boardui agent-chat-history)—— */ +.creation-home .creation-history-new { + display: flex; + width: calc(100% - 4px); + align-items: center; + gap: 11px; + margin: 0 2px 13px; + border: 1px dashed color-mix(in srgb, var(--creation-history-accent) 46%, var(--creation-history-line)); + border-radius: var(--r-md); + background: color-mix(in srgb, var(--creation-history-accent) 6%, transparent); + padding: 11px 13px; + color: var(--creation-history-text); + text-align: left; + cursor: pointer; + transition: background-color 150ms ease, border-color 150ms ease, transform 150ms ease; +} +.creation-home .creation-history-new:hover { border-color: color-mix(in srgb, var(--creation-history-accent) 74%, var(--creation-history-line)); background: color-mix(in srgb, var(--creation-history-accent) 10%, transparent); } +.creation-home .creation-history-new:focus-visible { outline: 2px solid color-mix(in srgb, var(--creation-history-accent) 70%, transparent); outline-offset: 1px; } +.creation-home .creation-history-new-icon { + display: grid; + width: 34px; + height: 34px; + flex: 0 0 auto; + place-items: center; + border-radius: 99px; + background: color-mix(in srgb, var(--creation-history-accent) 16%, transparent); + color: var(--creation-history-accent); +} +.creation-home .creation-history-new-icon svg { width: 16px; height: 16px; } +.creation-home .creation-history-new-copy { display: flex; min-width: 0; flex-direction: column; gap: 1px; } +.creation-home .creation-history-new-copy strong { font-size: var(--fs-label); font-weight: 650; } +.creation-home .creation-history-new-copy small { overflow: hidden; color: var(--creation-history-muted); font-size: var(--fs-micro); text-overflow: ellipsis; white-space: nowrap; } +.creation-home .creation-history-group-head { position: sticky; z-index: 1; top: 0; margin-top: 4px; padding: 10px 4px 5px; background: linear-gradient(to bottom, var(--creation-history-bg) 78%, transparent); } +.creation-home .creation-history-group-head h4 { + display: flex; + align-items: baseline; + gap: 6px; + margin: 0; + color: var(--creation-history-muted); + font-size: var(--fs-micro); + font-weight: 640; + letter-spacing: .04em; +} +.creation-home .creation-history-group-head h4::after { content: ""; flex: 1; height: 1px; background: var(--creation-history-line); transform: translateY(-2px); }