Refine workspace reading and mobile layout
This commit is contained in:
parent
07eba5c402
commit
369c74483c
|
|
@ -5,6 +5,10 @@
|
|||
> 所以不是每个版本号都有条目。条目格式 `## <版本> — <日期>`,新条目加在最上面。
|
||||
> 工程口径的完整记录见 `PROGRESS.md` / git log。
|
||||
|
||||
## 0.60.9 — 2026-07-29
|
||||
|
||||
- 网页工作台改用更温和的暖白配色,任务与文件列表的分隔更加清楚;助手回复按完整轮次形成统一阅读块,手机端输入区也改为更紧凑的图标操作布局。
|
||||
|
||||
## 0.60.8 — 2026-07-29
|
||||
|
||||
- 网页工作台完成一轮整体视觉优化:任务、对话和文件三栏层级更清晰,长回复阅读更舒适,按钮、输入框、弹窗、状态反馈及手机端触控尺寸也更加统一。
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。
|
||||
|
||||
最后更新:2026-07-29(Web 工作台 Visual Polish v1,bump 0.60.8)
|
||||
最后更新:2026-07-29(Web 工作台视觉复核修正,bump 0.60.9)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
### 2026-07-29
|
||||
|
||||
- **07-29 / 0.60.9 / Web 工作台视觉复核修正(桌面 + 手机实图)**:据上线截图把冷蓝灰改为暖白/浅米灰;任务与文件行恢复明确分隔,助手消息收进居中 52rem 文档型回答块,工具与进度对齐同一阅读轨道;同时修掉 `task_progress`-only assistant 被过滤工具后仍留下空“助手”壳。手机端 composer 改为独立状态行 + 3 个 44px SVG 图标键 + 发送键,缩短 placeholder、加入安全区并压缩操作/模型区;录音与润色状态改只更新 label/ARIA,不再覆盖 SVG DOM。相关 Python 21 项 + Node 5 项及 JS 语法、HTML/SVG 结构、CSS 括号检查通过;无 API、schema、migration 或依赖变化。
|
||||
- **07-29 / 0.60.8 / Web 工作台 Visual Polish v1**:新增零构建 `web/static/ui.css` 作为内部轻量视觉基础层,集中颜色、间距、圆角、阴影与 focus/disabled 等控件状态;收敛三栏层级、任务/资源导航、对话正文与工具过程块、输入区、文件栏、modal/toast 及 ≤640px 触控尺寸,不改 DOM 契约、API 或业务交互。新增 CSS 文件接入、MIME 与 `no-cache` 回归测试;相关 Python 20 项 + Node 5 项通过,浏览器连接不可用故截图级视觉验收留真实页面复核。
|
||||
- **07-29 / 0.60.7 / 组合框内直接新建工作目录**:新对话目录 combobox 增加 allow-create 语义:输入时继续过滤已有目录,无完全同名项时在同一下拉内提供“创建工作目录「名称」”,明确选择后才进入新建状态,失焦搜索词不会误提交;删除外部新建按钮与二级输入框。纯前端改动,无 API、schema、migration 或目录绑定语义变化;全量 387 测试通过(17 项按环境跳过)。
|
||||
- **07-29 / 0.60.6 / 新对话目录选择改可搜索组合框**:将上一版独立搜索框 + 原生 select 合并为单一 combobox,同一输入区完成已选目录展示、即时过滤和下拉选择,并补方向键、Enter、Esc、失焦恢复与 ARIA 状态;最近目录置顶、明确选择 working_dir 和新建目录入口均保持不变。纯前端改动,无依赖、API、schema 或 migration;全量 387 测试通过(17 项按环境跳过)。
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。
|
||||
# 改版本只动这一行。
|
||||
__version__ = "0.60.8"
|
||||
__version__ = "0.60.9"
|
||||
|
|
|
|||
|
|
@ -53,6 +53,20 @@ class StaticVendorTests(unittest.TestCase):
|
|||
self.assertIn(":focus-visible", css)
|
||||
self.assertIn("@media (max-width: 640px)", css)
|
||||
|
||||
def test_ui_polish_keeps_mobile_composer_and_reading_rail_contracts(self) -> None:
|
||||
html = DEV_HTML.read_text(encoding="utf-8")
|
||||
css = UI_CSS.read_text(encoding="utf-8")
|
||||
frontend = _frontend_source()
|
||||
|
||||
self.assertIn("--list-divider:", css)
|
||||
self.assertIn(".msg.assistant", css)
|
||||
self.assertIn(".composer-tool .composer-label", css)
|
||||
self.assertIn('aria-label="添加附件"', html)
|
||||
self.assertIn('aria-label="语音输入"', html)
|
||||
self.assertIn('aria-label="润色提示词"', html)
|
||||
self.assertIn("hasVisibleAssistantContent", frontend)
|
||||
self.assertIn('input.placeholder = mqPhone.matches', frontend)
|
||||
|
||||
def test_frontend_surfaces_context_and_cache_stats(self) -> None:
|
||||
src = _frontend_source()
|
||||
|
||||
|
|
|
|||
|
|
@ -1841,10 +1841,31 @@
|
|||
<span id="chat-attach"></span>
|
||||
<div class="row">
|
||||
<span class="hint" id="chat-hint">就绪</span>
|
||||
<span style="flex:1;"></span>
|
||||
<button type="button" class="small" id="chat-attach-btn" title="添加附件:手机可拍照 / 选照片,电脑直接开文件选择器(也可粘贴 / 拖拽)">📎 附件</button>
|
||||
<button type="button" class="small" id="chat-mic" title="语音输入:点击开始录音,再点结束并转写填入输入框(最长 60 秒)">🎙 语音</button>
|
||||
<button type="button" class="small" id="chat-optimize" disabled title="用当前对话模型润色草稿(参考已选生图模型偏好)— 替换为更清晰可执行的 prompt,Ctrl+Z 可撤销">✨ 润色</button>
|
||||
<span class="composer-spacer"></span>
|
||||
<button type="button" class="small composer-tool" id="chat-attach-btn"
|
||||
aria-label="添加附件" title="添加附件:手机可拍照 / 选照片,电脑直接开文件选择器(也可粘贴 / 拖拽)">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="m21.4 11.6-8.9 8.9a6 6 0 0 1-8.5-8.5l9.6-9.6a4 4 0 0 1 5.7 5.7l-9.6 9.6a2 2 0 1 1-2.8-2.8l8.9-8.9"/>
|
||||
</svg>
|
||||
<span class="composer-label">附件</span>
|
||||
</button>
|
||||
<button type="button" class="small composer-tool" id="chat-mic"
|
||||
aria-label="语音输入" title="语音输入:点击开始录音,再点结束并转写填入输入框(最长 60 秒)">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<rect x="9" y="2" width="6" height="12" rx="3"/><path d="M5 10a7 7 0 0 0 14 0M12 17v5M8 22h8"/>
|
||||
</svg>
|
||||
<span class="composer-label">语音</span>
|
||||
</button>
|
||||
<button type="button" class="small composer-tool" id="chat-optimize" disabled
|
||||
aria-label="润色提示词" title="用当前对话模型润色草稿(参考已选生图模型偏好)— 替换为更清晰可执行的 prompt,Ctrl+Z 可撤销">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="m15 4 5 5L8 21H3v-5L15 4Z"/><path d="m13 6 5 5M5 3v4M3 5h4M19 16v5M16.5 18.5h5"/>
|
||||
</svg>
|
||||
<span class="composer-label">润色</span>
|
||||
</button>
|
||||
<button type="submit" class="primary" id="chat-action">发送</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -620,7 +620,7 @@ export async function showNewConversationDraft() {
|
|||
$("chat-form").style.display = "flex";
|
||||
$("chat-input").readOnly = false;
|
||||
$("chat-input").classList.remove("readonly-locked");
|
||||
$("chat-input").placeholder = "输入消息…(Enter 发送,Shift+Enter 换行)";
|
||||
setComposerPlaceholder(null);
|
||||
$("chat-attach-btn").disabled = true; // task_dir 尚不存在,首发建 task 后开放
|
||||
$("chat-mic").disabled = false;
|
||||
$("btn-done").disabled = true;
|
||||
|
|
@ -1275,7 +1275,7 @@ function finishRunningTool(run) {
|
|||
function createLiveAssistantCard(run) {
|
||||
const card = document.createElement("div");
|
||||
card.className = "msg assistant live-run";
|
||||
card.innerHTML = `<div class="role">助手</div>`;
|
||||
card.innerHTML = `<div class="role">科研助手</div>`;
|
||||
run.card = card;
|
||||
run.curSeg = null;
|
||||
if (run.acc) {
|
||||
|
|
@ -1509,18 +1509,21 @@ function renderMessages(msgs, { stickBottom = true } = {}) {
|
|||
const card = document.createElement("div");
|
||||
card.className = "msg " + role;
|
||||
card.dataset.idx = m.idx;
|
||||
const roleLabel = { user: "我", assistant: "助手", error: "错误" }[role] || role;
|
||||
const roleLabel = { user: "我", assistant: "科研助手", error: "错误" }[role] || role;
|
||||
let html = `<div class="role">${roleLabel}</div>`;
|
||||
let hasVisibleAssistantContent = false;
|
||||
// 思考过程(thinking 模型持久化的 reasoning_content):默认折叠,与直播态同构
|
||||
if (role === "assistant") {
|
||||
const rc = (typeof p.reasoning_content === "string" && p.reasoning_content)
|
||||
|| ((p.provider_specific_fields || {}).reasoning_content || "");
|
||||
if (rc) {
|
||||
html += `<details class="reasoning"><summary>思考过程</summary><div class="reason-body">${escapeHtml(rc)}</div></details>`;
|
||||
hasVisibleAssistantContent = true;
|
||||
}
|
||||
}
|
||||
if (typeof p.content === "string" && p.content) {
|
||||
html += `<div class="body">${renderMd(p.content)}</div>`;
|
||||
if (role === "assistant") hasVisibleAssistantContent = true;
|
||||
// assistant 正文里 echo 的 <wd>/... 路径**永远**挂 chip(绕开 seenRels —— 上面
|
||||
// tool 结果可能 inline 过同图,但 chip 是小按钮无视觉污染,助手回复里有可
|
||||
// 点的"产物锚点"比没有好);强制 allowInlineMedia=false 防止大图被重复 inline。
|
||||
|
|
@ -1555,6 +1558,7 @@ function renderMessages(msgs, { stickBottom = true } = {}) {
|
|||
}
|
||||
}
|
||||
html += buildAskUserCard(argsObj, { interactive: !answered, chosenLabel: chosen }).outerHTML;
|
||||
hasVisibleAssistantContent = true;
|
||||
continue;
|
||||
}
|
||||
const label = toolActivityLabel(fn, argsObj);
|
||||
|
|
@ -1564,6 +1568,7 @@ function renderMessages(msgs, { stickBottom = true } = {}) {
|
|||
<details class="tool-call"><summary>${escapeHtml(label)}</summary><pre>${escapeHtml(args)}</pre></details>
|
||||
${renderArtifactBarHtml(rels, isProducer)}
|
||||
`;
|
||||
hasVisibleAssistantContent = true;
|
||||
}
|
||||
// 进度不再内联进消息卡 —— 累积值在循环末统一喂顶部 dock(见 setTaskProgress)
|
||||
}
|
||||
|
|
@ -1571,6 +1576,9 @@ function renderMessages(msgs, { stickBottom = true } = {}) {
|
|||
const hasCopy = role === "user"
|
||||
|| (role === "assistant" && typeof p.content === "string" && p.content.trim());
|
||||
if (hasCopy) html += msgActionsHtml();
|
||||
// task_progress-only assistant rows feed the persistent progress dock.
|
||||
// Once that tool call is filtered, do not leave an empty assistant shell.
|
||||
if (role === "assistant" && !hasVisibleAssistantContent) continue;
|
||||
card.innerHTML = html;
|
||||
highlightIn(card);
|
||||
wrap.appendChild(card);
|
||||
|
|
@ -1828,9 +1836,7 @@ function applyChannelComposerLock(meta) {
|
|||
const cfg = meta && channelCfg(meta.channel); // 微信 / 企业微信镜像 → 只读
|
||||
input.readOnly = !!cfg;
|
||||
input.classList.toggle("readonly-locked", !!cfg);
|
||||
input.placeholder = cfg
|
||||
? `${cfg.label}对话请在${cfg.label}里进行 — web 端为只读镜像,可查看历史`
|
||||
: "输入消息…(Enter 发送,Shift+Enter 换行,可粘贴 / 拖拽文件)";
|
||||
setComposerPlaceholder(cfg);
|
||||
if (cfg) {
|
||||
const opt = $("chat-optimize");
|
||||
if (opt) opt.disabled = true;
|
||||
|
|
@ -1841,6 +1847,24 @@ function applyChannelComposerLock(meta) {
|
|||
if (attach) attach.disabled = !!cfg; // 附件按钮同口径(与拖拽/粘贴的 _composerLocked 一致)
|
||||
}
|
||||
|
||||
function setComposerPlaceholder(cfg) {
|
||||
const input = $("chat-input");
|
||||
if (!input) return;
|
||||
if (cfg) {
|
||||
input.placeholder = mqPhone.matches
|
||||
? `${cfg.label}对话请在${cfg.label}中进行`
|
||||
: `${cfg.label}对话请在${cfg.label}里进行 — web 端为只读镜像,可查看历史`;
|
||||
return;
|
||||
}
|
||||
input.placeholder = mqPhone.matches
|
||||
? "输入消息…"
|
||||
: "输入消息…(Enter 发送,Shift+Enter 换行,可粘贴 / 拖拽文件)";
|
||||
}
|
||||
|
||||
mqPhone.addEventListener("change", () => {
|
||||
setComposerPlaceholder(channelCfg(state.taskMeta && state.taskMeta.channel));
|
||||
});
|
||||
|
||||
// 润色:同步调后端,把 textarea 内容替成优化后文本。用 execCommand('insertText')
|
||||
// 接 textarea 原生 undo 栈 — Ctrl+Z 一次回到原文。streaming 期间允许并行(后端
|
||||
// 不与主对话 run 互斥,各跑各的 LLM)。
|
||||
|
|
@ -1861,8 +1885,11 @@ async function optimizePrompt() {
|
|||
const btn = $("chat-optimize");
|
||||
state.optimizing = true;
|
||||
btn.disabled = true;
|
||||
const oldLabel = btn.textContent;
|
||||
btn.textContent = "润色中…";
|
||||
const label = btn.querySelector(".composer-label");
|
||||
const oldLabel = label ? label.textContent : "润色";
|
||||
const oldAriaLabel = btn.getAttribute("aria-label") || "润色提示词";
|
||||
if (label) label.textContent = "润色中";
|
||||
btn.setAttribute("aria-label", "正在润色");
|
||||
const oldHint = $("chat-hint").textContent;
|
||||
$("chat-hint").textContent = "润色中…";
|
||||
try {
|
||||
|
|
@ -1891,7 +1918,8 @@ async function optimizePrompt() {
|
|||
$("chat-hint").textContent = `润色失败:${e.message}`;
|
||||
} finally {
|
||||
state.optimizing = false;
|
||||
btn.textContent = oldLabel;
|
||||
if (label) label.textContent = oldLabel;
|
||||
btn.setAttribute("aria-label", oldAriaLabel);
|
||||
syncOptimizeBtn();
|
||||
}
|
||||
}
|
||||
|
|
@ -2064,7 +2092,11 @@ async function startVoiceInput(mode, startY) {
|
|||
cv.height = Math.max(1, cv.clientHeight * dpr);
|
||||
_vrHint(mode === "hold" ? "松开转写 · 上滑取消" : "说完按 Enter 结束 · Esc 取消");
|
||||
btn.classList.add("recording");
|
||||
if (mode === "click") btn.textContent = "■ 停止";
|
||||
if (mode === "click") {
|
||||
const label = btn.querySelector(".composer-label");
|
||||
if (label) label.textContent = "停止";
|
||||
btn.setAttribute("aria-label", "停止录音");
|
||||
}
|
||||
const tick = () => {
|
||||
if (!_voice) return;
|
||||
const sec = Math.floor((Date.now() - _voice.t0) / 1000);
|
||||
|
|
@ -2080,7 +2112,9 @@ async function startVoiceInput(mode, startY) {
|
|||
if (mode === "hold" && _holdReleased) {
|
||||
if (Date.now() - _pressT0 < HOLD_TOGGLE_MS && !_voice.cancelArmed) {
|
||||
_voice.mode = "click";
|
||||
btn.textContent = "■ 停止";
|
||||
const label = btn.querySelector(".composer-label");
|
||||
if (label) label.textContent = "停止";
|
||||
btn.setAttribute("aria-label", "停止录音");
|
||||
_vrHint("说完按 Enter 结束 · Esc 取消");
|
||||
} else {
|
||||
stopVoiceInput(_voice.cancelArmed);
|
||||
|
|
@ -2096,7 +2130,9 @@ async function stopVoiceInput(discard) {
|
|||
cancelAnimationFrame(v.raf);
|
||||
const btn = $("chat-mic");
|
||||
btn.classList.remove("recording");
|
||||
btn.textContent = "🎙 语音";
|
||||
const label = btn.querySelector(".composer-label");
|
||||
if (label) label.textContent = "语音";
|
||||
btn.setAttribute("aria-label", "语音输入");
|
||||
$("chat-form").classList.remove("recording");
|
||||
$("voice-rec-panel").classList.remove("cancel-armed");
|
||||
$("vr-time").textContent = "0:00";
|
||||
|
|
@ -2382,7 +2418,7 @@ async function sendMessage(overrideText) {
|
|||
// assistant 流式占位卡
|
||||
const asstCard = document.createElement("div");
|
||||
asstCard.className = "msg assistant live-run";
|
||||
asstCard.innerHTML = `<div class="role">助手</div><div class="body streaming"></div>`;
|
||||
asstCard.innerHTML = `<div class="role">科研助手</div><div class="body streaming"></div>`;
|
||||
wrap.appendChild(asstCard);
|
||||
wrap.scrollTop = wrap.scrollHeight;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,15 +11,16 @@
|
|||
color-scheme: light;
|
||||
|
||||
/* Neutral workspace palette */
|
||||
--bg: #f3f5f7;
|
||||
--bg: #f6f4f1;
|
||||
--panel: #ffffff;
|
||||
--panel-subtle: #f8f9fb;
|
||||
--panel-muted: #f4f6f8;
|
||||
--border: #dde2e7;
|
||||
--border-soft: #e9edf1;
|
||||
--text: #20242a;
|
||||
--text-strong: #15181d;
|
||||
--muted: #6f7782;
|
||||
--panel-subtle: #fbfaf8;
|
||||
--panel-muted: #f7f4f0;
|
||||
--border: #e2ddd7;
|
||||
--border-soft: #ede8e2;
|
||||
--list-divider: #ddd7d0;
|
||||
--text: #292623;
|
||||
--text-strong: #1c1a18;
|
||||
--muted: #756f69;
|
||||
|
||||
/* Institute red is deliberately reserved for primary emphasis. */
|
||||
--accent: #b83a31;
|
||||
|
|
@ -27,9 +28,9 @@
|
|||
--accent-soft: #fbecea;
|
||||
--accent-ring: rgba(184, 58, 49, 0.16);
|
||||
|
||||
--hover: #f0f3f5;
|
||||
--code-bg: #f3f5f7;
|
||||
--user-bg: #f1f5fa;
|
||||
--hover: #f5f1ed;
|
||||
--code-bg: #f6f3ef;
|
||||
--user-bg: #f5f1eb;
|
||||
--asst-bg: #ffffff;
|
||||
|
||||
--c-green: #238653;
|
||||
|
|
@ -97,7 +98,7 @@ button.small {
|
|||
}
|
||||
|
||||
button:hover:not(:disabled) {
|
||||
border-color: #cbd2d9;
|
||||
border-color: #cdc5bd;
|
||||
}
|
||||
|
||||
button.primary {
|
||||
|
|
@ -136,7 +137,7 @@ select {
|
|||
input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):hover,
|
||||
textarea:hover,
|
||||
select:hover {
|
||||
border-color: #c7ced5;
|
||||
border-color: #c9c1b9;
|
||||
}
|
||||
|
||||
input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):focus,
|
||||
|
|
@ -148,7 +149,7 @@ select:focus {
|
|||
}
|
||||
|
||||
::placeholder {
|
||||
color: #9aa1aa;
|
||||
color: #9b958f;
|
||||
}
|
||||
|
||||
/* Application shell */
|
||||
|
|
@ -202,7 +203,7 @@ header .who {
|
|||
}
|
||||
|
||||
.splitter::before {
|
||||
background: #d7dde3;
|
||||
background: #d9d3cc;
|
||||
}
|
||||
|
||||
.pane-head {
|
||||
|
|
@ -224,7 +225,7 @@ header .who {
|
|||
|
||||
#task-scroll,
|
||||
#file-list {
|
||||
scrollbar-color: #cbd1d7 transparent;
|
||||
scrollbar-color: #cec7bf transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
|
|
@ -240,27 +241,28 @@ header .who {
|
|||
}
|
||||
|
||||
#task-list {
|
||||
padding: 4px 8px 8px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.task-row {
|
||||
margin: 3px 0;
|
||||
padding: 9px 10px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--r-md);
|
||||
margin: 0;
|
||||
padding: 10px 12px 10px 9px;
|
||||
border: 0;
|
||||
border-left: 3px solid transparent;
|
||||
border-bottom: 1px solid var(--list-divider);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.task-row:hover {
|
||||
background: var(--panel);
|
||||
border-color: var(--border-soft);
|
||||
box-shadow: 0 1px 3px rgba(24, 32, 41, 0.04);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.task-row.active {
|
||||
padding-left: 8px;
|
||||
padding-left: 9px;
|
||||
background: var(--accent-soft);
|
||||
border-color: #f0d2cf;
|
||||
border-left: 3px solid var(--accent);
|
||||
border-bottom-color: #e8c6c3;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
|
|
@ -335,7 +337,8 @@ header .who {
|
|||
line-height: 1.7;
|
||||
}
|
||||
|
||||
/* Conversation: assistant content reads like a document, not a bubble stack. */
|
||||
/* Conversation: one centred reading rail, with each assistant turn grouped as
|
||||
a document block rather than a floating chat label. */
|
||||
|
||||
#chat-meta {
|
||||
min-height: 40px;
|
||||
|
|
@ -345,7 +348,7 @@ header .who {
|
|||
}
|
||||
|
||||
#chat-stream {
|
||||
gap: 14px;
|
||||
gap: 16px;
|
||||
padding: 20px clamp(16px, 3vw, 36px) 24px;
|
||||
background: var(--panel);
|
||||
scrollbar-color: #cbd1d7 transparent;
|
||||
|
|
@ -364,14 +367,21 @@ header .who {
|
|||
}
|
||||
|
||||
.msg.assistant {
|
||||
padding: 8px 0;
|
||||
border-color: transparent;
|
||||
border-radius: 0;
|
||||
width: min(100%, 52rem);
|
||||
max-width: min(100%, 52rem);
|
||||
align-self: center;
|
||||
padding: 16px 18px;
|
||||
border: 1px solid var(--border-soft);
|
||||
border-left: 3px solid #cbc3bb;
|
||||
border-radius: var(--r-lg);
|
||||
background: #fffdfa;
|
||||
}
|
||||
|
||||
.msg.assistant.live-run {
|
||||
padding: 12px 14px;
|
||||
padding: 16px 18px;
|
||||
border-color: rgba(184, 58, 49, 0.2);
|
||||
border-left-color: var(--accent);
|
||||
border-left-width: 3px;
|
||||
border-radius: var(--r-lg);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(184, 58, 49, 0.05),
|
||||
|
|
@ -387,6 +397,42 @@ header .who {
|
|||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.msg.assistant > .role {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
color: #6d665f;
|
||||
}
|
||||
|
||||
.msg.assistant > .role::before {
|
||||
content: "";
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
flex: none;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
box-shadow: 0 0 0 3px var(--accent-soft);
|
||||
}
|
||||
|
||||
.msg.tool {
|
||||
width: min(100%, 52rem);
|
||||
max-width: min(100%, 52rem);
|
||||
align-self: center;
|
||||
padding: 0 18px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.msg.tool > .role {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#task-progress-dock .task-progress {
|
||||
width: min(100%, 52rem);
|
||||
max-width: 52rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.msg .body {
|
||||
color: var(--text);
|
||||
font-size: 15px;
|
||||
|
|
@ -410,7 +456,7 @@ header .who {
|
|||
|
||||
.tool-call,
|
||||
.reasoning {
|
||||
color: #4f5863;
|
||||
color: #5f5851;
|
||||
}
|
||||
|
||||
.tool-call summary,
|
||||
|
|
@ -444,13 +490,30 @@ header .who {
|
|||
padding: 10px 12px;
|
||||
line-height: 1.6;
|
||||
border-radius: var(--r-lg);
|
||||
background: #fcfcfd;
|
||||
background: #fffdfa;
|
||||
}
|
||||
|
||||
#chat-form .row {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.composer-spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.composer-tool {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.composer-tool svg {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
#chat-action {
|
||||
min-width: 64px;
|
||||
}
|
||||
|
|
@ -467,7 +530,7 @@ header .who {
|
|||
.file-row {
|
||||
min-height: 38px;
|
||||
padding: 7px 12px;
|
||||
border-bottom-color: var(--border-soft);
|
||||
border-bottom: 1px solid var(--list-divider);
|
||||
}
|
||||
|
||||
.file-row:hover {
|
||||
|
|
@ -475,7 +538,7 @@ header .who {
|
|||
}
|
||||
|
||||
.file-row .name {
|
||||
color: #30363d;
|
||||
color: #332f2b;
|
||||
}
|
||||
|
||||
.storage-foot {
|
||||
|
|
@ -486,7 +549,7 @@ header .who {
|
|||
|
||||
.storage-foot .bar {
|
||||
height: 5px;
|
||||
background: #e5e9ed;
|
||||
background: #e8e3de;
|
||||
}
|
||||
|
||||
/* Dialogs and notifications */
|
||||
|
|
@ -557,13 +620,69 @@ header .who {
|
|||
min-height: 42px;
|
||||
}
|
||||
|
||||
.mobile-tabs {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.mobile-tabs button {
|
||||
min-height: 38px;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
#pane-mid > .pane-head {
|
||||
min-height: 38px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
#pane-mid > .pane-head > button {
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
#chat-meta {
|
||||
min-height: auto;
|
||||
padding: 7px 10px;
|
||||
column-gap: 7px;
|
||||
row-gap: 7px;
|
||||
}
|
||||
|
||||
#chat-meta > .spacer {
|
||||
flex: 1 0 100%;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
#chat-meta .mdl-wrap {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#chat-meta select {
|
||||
width: 100% !important;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
#task-progress-dock {
|
||||
padding: 6px 10px;
|
||||
}
|
||||
|
||||
#chat-stream {
|
||||
gap: 12px;
|
||||
gap: 14px;
|
||||
padding: 14px 12px 18px;
|
||||
}
|
||||
|
||||
.msg {
|
||||
max-width: 97%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.msg.assistant {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 13px 14px;
|
||||
}
|
||||
|
||||
.msg.tool {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 0 14px;
|
||||
}
|
||||
|
||||
.msg .body {
|
||||
|
|
@ -573,14 +692,60 @@ header .who {
|
|||
|
||||
#chat-form {
|
||||
padding: 10px;
|
||||
padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
|
||||
}
|
||||
|
||||
#chat-input {
|
||||
min-height: 64px;
|
||||
}
|
||||
|
||||
#chat-form .row button {
|
||||
min-height: 36px;
|
||||
#chat-form .row {
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
#chat-hint {
|
||||
order: -1;
|
||||
flex: 1 0 100%;
|
||||
min-height: 18px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.composer-spacer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#chat-form .row .composer-tool {
|
||||
width: 44px;
|
||||
min-width: 44px;
|
||||
height: 44px;
|
||||
min-height: 44px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.composer-tool svg {
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
}
|
||||
|
||||
.composer-tool .composer-label {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#chat-action {
|
||||
width: 72px;
|
||||
min-width: 72px;
|
||||
height: 44px;
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.modal {
|
||||
|
|
|
|||
Loading…
Reference in New Issue