ui: 文件预览弹框让出 chat-form 高度,打开期间仍可点击/打字
#file-preview-modal 加 bottom: var(--preview-bottom-inset, 0),openFilePreview 时 JS 量 chat-form 当前高度写到 inline style,关闭时清掉。card max-height 跟着收缩不溢出, 手机段用 100dvh 同理。无活动任务(chat-form 隐藏)走 0,弹框仍全屏。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
eec7eb156f
commit
0c732590a4
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
### 2026-05-22
|
||||
|
||||
- **dev SPA 文件预览弹框让出 chat-form 高度(打开期间输入区仍可点可打字)**:`web/static/dev.html` 给 `#file-preview-modal` 加 `bottom: var(--preview-bottom-inset, 0)` —— 默认 0 行为不变,`openFilePreview` 时 JS 量 `#chat-form.offsetHeight`(隐藏走 `offsetParent` 判空 → 0,无活动任务恢复全屏)写到弹框元素 inline style 上;`.card` 加 `max-height: calc(100vh - var(...) - 32px)` 让卡片随容器收缩不溢出,手机段同理用 `100dvh`。`closeFilePreview` `removeProperty` 清掉避免下次冗余。弹框遮罩本身物理上不覆盖底部输入区 → chat-form 自然可点击/打字,Esc 与点遮罩关闭逻辑不动。否决:(a) 整窗 `pointer-events: none` + card 收回 —— 遮罩物理还在覆盖,视觉仍遮挡;(b) 弹框抽进 `#pane-mid` 内 absolute —— 弹框来源含 `#pane-right` 文件列表和聊天 chip,挂 mid 内会限制弹框只能在 mid 列,且 `#pane-mid` 多层 flex 嵌套要重排;(c) 硬编一个常量 `bottom: 140px` —— chat-form 高度依据 textarea 用户拖拽变化(min 60 但可拉高),JS 量一次足够准。
|
||||
- **对外路径协议刚性化(system 强约束 + SKILL 简化 + UI 一次性兼容)**:`prompts/system/general_v1.md`「路径」段加规则 —— 助手对外 echo 产物路径必须用 user_root 相对全形式 `<wd_name>/<rel>`(`<wd_name>` = task_dir 末段,如 `生图测试/videos/xxx.mp4` / `基金申报/sections/01-绪论.md` / `公司汇报/slides/deck.pptx`),不简写为 `videos/xxx.mp4` 这种 task 内裸形式(Web UI 按 `<wd_name>/` 前缀挂 chip,简写 → chip 失效用户点不开)。媒体 tool(`seedream` / `seedance`)的 `saved:` 行已是规范全形式可直接照抄,其他场景(ppt / proposal / coding 等 run_python/write 写文件)自己拼。**跨所有产物 skill 统一生效**(不止 imagegen/videogen)。`skills/imagegen/SKILL.md` + `skills/videogen/SKILL.md` 把原有"把 `saved: xxx` 告诉用户"重复教学改成"照抄 saved 行,详见 system「路径」段"(消除 skill 内具体写法 → 协议归一到 system,新产物 skill 不用重复教育)。ppt/proposal/coding 等 SKILL **不动** —— 它们只泛说"告诉用户文件路径"没教错,system 协议升级后助手自然按全形式 echo,加 skill 提醒反而是协议漂移源。`web/static/dev.html::extractArtifactRels` 加一次性兼容兜底:产物目录裸路径 `videos/xxx.<ext>` / `figures/xxx.<ext>`(协议刚性前历史简写)prepend `<wdName>/` 拼成 user_root rel —— 白名单显式枚举两项不扩展、长期老消息归档后整段可删。**术语校准**:前缀叫 `<wd_name>`(working_dir 最后一段)而非 `<task_name>` —— 用户允许 `wd_name ≠ task_name`(`build_agent` wd_raw 走 working_dir 字段独立可指定),`_display` 锚 user_root 出来的是 `<wd_name>`,SKILL/system 早期写 `task_name` 在分叉场景下会误导助手拼错前缀。否决:(a) 后端 `_display` 改 task-relative 让 tool 输出本身就裸 —— `Tool` 基类 + fs/skill_tool/seedream/seedance/agent_builder/smoke 改 8 个文件,且 fs 跨 task 时要分层 fallback(working_dir → user_root → 绝对),复杂度超过收益;(b) 后端补 HEAD 探针让前端验文件存在再挂 chip —— 工程量与开发期需求不匹配;(c) 白名单常驻服务所有简写形式 —— 维护负担+清单可能膨胀,改成"一次性兼容历史消息"角色后边界清晰;(d) 每个写产物的 SKILL 各加一句"按 system 协议" —— 协议漂移源,违反"system 谈通用、SKILL 谈领域"边界。
|
||||
- **dev SPA 手机自适应:两档断点 + tab 单列**:`web/static/dev.html` 加 `@media`,**平板段(641-1024px)**纯 CSS 强制 rail(grid `40px 1fr 260px` + 左 pane 子项 `display:none` + 留 toggle 按钮),不写 localStorage —— 回桌面用户原偏好仍生效。**手机段(≤640px)**单列布局 grid `1fr` + `grid-template-areas:"head" "main"`,三 pane 都 `grid-area: main` 且默认 `display:none`,新加 `body.mv-{left,mid,right}` 控制当前可见 pane;header 加 `.mobile-tabs`(任务/对话/文件)桌面 `display:none`,手机段 `order:99 + flex-basis:100%` 换行铺底;`selectTask` 末加 `if (mqPhone.matches) setMobileView("mv-mid")` 选中任务自动切对话。`applyMobileMode()` 监听 `matchMedia("(max-width: 640px)")`,进手机时清 DOM 上的 `left-collapsed` class(localStorage 不动),回桌面再 `applyLeftCollapsed(读 localStorage)` 恢复。`100vh → 100dvh` 解决 iOS Safari 工具栏挤压;`textarea / input` 强制 ≥16px 防 focus 双指缩放;4 个 modal 卡片宽度从固定 px 改 `min(92vw, …)`,file-preview 改 `100vw × 100dvh` 全屏化。`#pane-toggle-left { display:none !important; }` 手机不允许折叠避免与 tab 切换语义冲突。否决:(a) 抽屉式(要写遮罩+手势 JS,自用工具不划算);(b) 只缩字号不改布局(三列在 360px 屏字段严重截断)。
|
||||
- **豆包 Seedance 2.0 Fast 视频生成接入(文生视频)+ videogen skill**:`config/media/doubao.yaml` 展开 video 段(`seedance_2_fast`:¥37/Mtok 文生 / ¥22/Mtok 图生,实测档位 480p 5s ¥1.86 / 720p 5s ¥4.00 — 由 token 公式 `(in+out)×W×H×fps/1024` 反推校验通过);`tools/seedance.py` 走 ark POST `/contents/generations/tasks` → 5s 间隔轮询 → succeeded 后 download mp4 + .meta.json 落 `<wd>/videos/<ts>-<rand>.mp4`,失败/cancel 不计费;`core/storage/usage.py::record_video_usage` 多态 units snapshot(resolution/duration/ratio/fps/tokens/单价);`build_agent` 加 `video_variant` + `cancel_check` 形参 — cancel_check 必须在 build 阶段传(SeedanceTool ctor 持有用于轮询期间响应停止按钮,改了原"build 后赋 agent.cancel_check"的延迟绑定,web 入口同步迁移);`web/app.py` 加 `_list_video_variants` / `_resolve_video_model` / `GET /v1/video_models` / `MessageRequest.video_model` / `OptimizePromptRequest.video_model`;`dev.html` 顶栏第三下拉 + `state.videoModels/videoModel` + 发消息一起 POST。前端 chip / inline `<video>` / `extractMediaBanner` / `_categorize` 在前期工作里已为 seedance 留好脚手架,几乎不动。`skills/videogen/SKILL.md` 六维诊断把 imagegen 的"光线"换成"运动+镜头"两维(运动必填,否则应该走 seedream 而非 seedance —— 差 18 倍价钱);BLOCKING 门槛比 imagegen 更严(¥4 vs ¥0.22)且要等 30-90s,贴 prompt+参数+预计花费+预计等待四件套等明确确认。`general_v1.md` 加 seedance 触发指引(平行 seedream)。phase 1 仅 t2v,**不支持 i2v**(skill 明示告诉用户)。fast 上限 720p,1080p+ 留给 pro variant(yaml 当前未配)。否决:(a) progress 事件流化(需要给 tool 加 sink 注入,phase 1 用 `run_status=running` 够了);(b) 远端 cgt-task DELETE(Volcengine 无明确 API,best-effort 不动);(c) i2v phase 1 拉进来(要图片转 URL + UI 选已有图,延后)。
|
||||
|
|
|
|||
|
|
@ -483,10 +483,15 @@
|
|||
#new-task-modal .err { color: var(--accent); font-size: 12px; margin-top: 8px; min-height: 1em; }
|
||||
#new-task-modal .actions { margin-top: 14px; display: flex; gap: 8px; justify-content: flex-end; }
|
||||
|
||||
/* ───── file preview modal(略深的遮罩 0.5 + 更重阴影) ───── */
|
||||
#file-preview-modal { background: rgba(0,0,0,0.5); z-index: 90; }
|
||||
/* ───── file preview modal(略深的遮罩 0.5 + 更重阴影) ─────
|
||||
bottom 让出 chat-form 高度(--preview-bottom-inset 由 JS 按需写),输入区不被遮挡,可继续打字 */
|
||||
#file-preview-modal {
|
||||
background: rgba(0,0,0,0.5); z-index: 90;
|
||||
bottom: var(--preview-bottom-inset, 0);
|
||||
}
|
||||
#file-preview-modal .card {
|
||||
width: 90vw; height: 90vh; max-width: 1200px;
|
||||
max-height: calc(100vh - var(--preview-bottom-inset, 0px) - 32px);
|
||||
display: flex; flex-direction: column;
|
||||
box-shadow: 0 12px 32px rgba(0,0,0,.22);
|
||||
}
|
||||
|
|
@ -610,7 +615,8 @@
|
|||
#src-picker-modal .card { width: min(96vw, 560px); max-height: 88dvh; }
|
||||
#file-preview-modal .card {
|
||||
width: 100vw; height: 100dvh;
|
||||
max-width: 100vw; max-height: 100dvh;
|
||||
max-width: 100vw;
|
||||
max-height: calc(100dvh - var(--preview-bottom-inset, 0px));
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -2710,6 +2716,10 @@ async function openFilePreview(rel) {
|
|||
const body = $("fp-body");
|
||||
body.className = "body center";
|
||||
body.innerHTML = `<div class="ph">加载中…</div>`;
|
||||
// 让出聊天输入区高度,弹框不遮挡 chat-form(无活动任务时 cf 隐藏,inset = 0)
|
||||
const cf = $("chat-form");
|
||||
const inset = (cf && cf.offsetParent) ? cf.offsetHeight : 0;
|
||||
$("file-preview-modal").style.setProperty("--preview-bottom-inset", inset + "px");
|
||||
$("file-preview-modal").classList.add("show");
|
||||
|
||||
const cat = _categorize(rel);
|
||||
|
|
@ -2886,6 +2896,7 @@ function _showFallback(msg) {
|
|||
|
||||
function closeFilePreview() {
|
||||
$("file-preview-modal").classList.remove("show");
|
||||
$("file-preview-modal").style.removeProperty("--preview-bottom-inset");
|
||||
$("fp-body").innerHTML = "";
|
||||
_flushBlobUrls();
|
||||
_fpCurrentRel = null;
|
||||
|
|
|
|||
Loading…
Reference in New Issue