# qjclaw 聊天页与设置页 UI/交互整改方案

  ## Summary

  基于项目现状，问题集中在三处：发送反馈弱、核心聊天交互缺失、设置页信息层级混乱。已确认的项目事实是：聊天输入当前仅支持 Ctrl/Cmd+Enter 发送；发送按钮只有静态箭头；消息气泡无复制入口；窗口为 frame: false 且只在局部头部区域可拖动；设置
  页工作目录当前可编辑可保存；设置保存后只有顶部短暂提示，没有未保存提醒。

  整改目标是把聊天主链路做成“可感知、可键盘操作、可恢复”，把设置页做成“只暴露真实可操作项、减少冗余说明、强化保存状态反馈”。

  ## 修改范围

  - 前端主改动
      - apps/ui/src/App.tsx
      - apps/ui/src/styles.css
  - 可能涉及的桌面壳配合确认
      - apps/desktop/src/main/create-window.ts
  - 不在本次范围
      - IPC 协议与 SaveConfigInput 结构
      - 后端配置存储逻辑
      - 消息渲染内容格式化能力
      - 插件页、专家页业务流程本身

  ## Key Changes

  - 聊天发送区
      - 将输入区改为标准 <form onSubmit> 提交。
      - 输入规则改为 Enter 发送，Shift+Enter 换行，替代现有 Ctrl/Cmd+Enter。
      - 重写发送按钮状态：
          - 空闲态改成双蓝色火箭 >> 意象图标。
          - 发送中显示橙色旋转圆环，不再只是灰掉。
          - 禁用态保留轮廓和可读状态，不做纯灰块。
      - 更新占位文案与提示语，避免继续显示旧的快捷键规则。
  - 聊天消息区
      - 在用户和助手消息气泡下方新增稳定工具栏，提供“复制”按钮。
      - 点击复制后按钮短暂反馈“已复制”，随后恢复。
      - 仅复制消息正文文本；思考中空消息、trace、状态卡、附件不纳入复制。
      - 工具栏默认占位，避免 hover 出现造成消息跳动。
  - 窗口拖拽
      - 保留 frame: false，但补充稳定 drag 热区。
      - 非对话页维持 page-topbar 可拖动，同时确保存在真实空白拖拽区域。
      - 对话页在 conversation-panel-head 层增加更明确的 drag strip 或可拖区域。
      - 所有按钮、输入框、状态芯片继续标记 no-drag，避免拖拽吞掉点击。
  - 设置页
      - 工作目录改为只读展示卡，不再提供编辑输入框和“保存工作区设置”按钮。
      - 模型配置卡片精简文案，只保留卡片标题、状态、必要字段。
      - api_key 等字段标签强化层级，提升与输入框的颜色和字重区分。
      - 引入“未保存变更”脏状态：
          - 任一 key 字段被修改后显示非阻断提示条“当前修改尚未保存，保存后生效”。
          - 切换页面、离开设置页或关闭窗口时，如存在脏状态则弹确认。
          - 保存成功后改为明确提示“已保存，新的配置将在后续执行中生效”。
  - 视觉规范
      - 延续当前浅色桌面工具基调，但降低装饰性，提升功能优先级。
      - 重点强化三组对比：字段标签 vs 输入框、可编辑项 vs 只读项、空闲态按钮 vs 处理中按钮。
      - 所有新增交互保留可见 focus 状态，满足键盘可达性。

  ## 风险点

  - 输入行为变更风险
      - 现有用户已习惯 Ctrl/Cmd+Enter，改为 Enter 直发后，可能误发送多行内容。
      - 需要通过占位文案、辅助提示和 Shift+Enter 明示降低迁移成本。
      - 如果 drag 区覆盖不准，会出现“按钮点不动”或“窗口仍拖不动”。
      - 消息卡新增工具栏后，长消息、流式消息、trace 展开态可能出现间距不协调。
      - 需要验证不同消息态下不会破坏对齐关系。
  - 设置页脏状态风险
      - 草稿值、已保存值、服务端返回值之间如果比较策略不一致，容易出现误报“未保存”或保存后仍然脏。
      - 需统一以当前草稿与最新保存配置做精确比较。
  - 工作目录只读化风险
      - 当前 saveConfig() 仍会带上 workspacePath；前端隐藏编辑后要避免误导实现者继续保留无意义保存入口。
      - 本次应只调整 UI 暴露，不触碰底层兼容字段。
  - 提示反馈冲突风险
      - 现有 infoText/errorText 顶部提示、发送态提示、设置未保存提示可能同时出现。
      - 需要明确优先级和位置，避免提示堆叠。

  ## Public Interfaces / Behavior

  - 输入行为从“Ctrl/Cmd+Enter 发送”调整为“Enter 发送，Shift+Enter 换行”。
  - 消息项新增复制能力，但不修改消息数据协议；复制反馈优先由 UI 本地状态按 message.id 管理。
  - 设置页新增未保存变更判断，但不新增后端字段，不修改 SaveConfigInput。
  - 工作目录从可编辑配置项降级为只读展示项，前端不再提供对应保存入口。

  ## Test Plan

  - 手工交互测试
      - 聊天输入框按 Enter 可发送，按 Shift+Enter 仅换行。
      - 发送中按钮显示橙色 spinner，且不可重复提交。
      - 按钮空闲、禁用、发送中三种视觉状态清晰可分。
      - 用户消息和助手消息的复制按钮都可用，复制结果与界面正文一致。
      - 复制后按钮反馈正确，2 秒左右恢复。
      - 长文本、多行文本、流式完成后的文本都能正确复制。
      - 聊天页、设置页、插件页顶部都能稳定拖动窗口。
      - 顶部按钮、会话按钮、输入框不会被拖拽区影响点击。
      - 设置页任一 key 字段修改后出现未保存提示。
      - 设置页保存成功后提示更新，未保存提示消失。
      - 存在未保存变更时，切页、离开设置页、关闭窗口会触发确认。
      - 工作目录区域仅展示，不可编辑，不再出现“保存工作区设置”。
  - 样式与布局测试
      - 验证 375px、768px、1024px、1440px 下聊天区和设置页不溢出。
      - 验证消息气泡新增工具栏后，用户/助手左右对齐仍正确。
      - 验证设置卡片字段标签对比度明显提升，标签与输入框层级可快速分辨。
      - 验证 toast、错误提示、未保存提示不会互相遮挡。
  - 可访问性测试
      - Tab 顺序符合视觉顺序。
      - 新增复制按钮、发送按钮、设置按钮都有可见 focus ring。
      - aria-label 与按钮状态文案在发送中、复制后都正确。
      - 颜色不是唯一状态指示，发送中和未保存状态同时有图形/文案辅助。
  - 验证命令
      - 运行 corepack pnpm typecheck
      - 如改动影响桌面壳交互，补跑 corepack pnpm dev 进行窗口拖拽与聊天链路手测

  ## Assumptions

  - 采用已确认默认方案：工作目录用只读展示，不使用只读输入框。
  - “需要保存生效”采用非阻断提示为主，离开当前上下文时再弹确认。
  - 复制按钮先做纯文本复制，不覆盖 trace、状态卡和附件。
  - 主要改动集中在 apps/ui/src/App.tsx 与 apps/ui/src/styles.css，桌面壳仅做必要配合确认。