Compare commits

..

5 Commits

Author SHA1 Message Date
70d2bceb45 feat(chat): 编辑摘要卡片文件列表限高 5 行 + 内部滚动
原先全量渲染无高度限制,编辑文件多时卡片撑爆消息流。现 160px 封顶
(32px x 5 行),超出内部滚动,滚动条隐藏(同 QuickDock 惯例)。
2026-08-12 11:24:12 +08:00
dd08845bbc fix(quickdock): 子智能体/后台指令/文件行选中态由近黑改为 hover 色
is-active 原先使用 --surface-soft,dark 主题下为 #0f0f0f 近黑色,违反
大面积色块禁近黑规范;改为 var(--hover-bg),点击选中与 hover 视觉一致。
2026-08-12 11:23:57 +08:00
4b78fd6d40 fix(input): 输入栏 + 按钮改 SVG 图标,修复视觉重心偏下
原为裸文本 + 字形(font-size 20px),flex 居中的是行框而非字形光学
中心,hover 背景块下可见偏下。改用项目既有 SVG 加号(16x16,stroke 2),
与 WorkspaceSwitcher 同路径。
2026-08-12 11:23:41 +08:00
011f9e14bc fix(file-manager): direct 执行环境下文件工具不再误拦授权范围外路径
run_command 是否走沙箱只看执行环境,而 read_file/write_file/edit_file 是
进程内文件操作不走 OS 沙箱,唯一防线是 FileManager._ensure_host_access
的授权路径检查,但它不知道执行环境,导致完全访问权限(direct)下仍按
授权列表拦截。现 FileManager 同步 host_execution_mode,direct 时与
run_command 语义对齐直接放行,sandbox 模式检查保持不变。
2026-08-12 11:23:27 +08:00
8da4a596eb fix(frontend): 空对话与显式新建路由下 has_images/has_videos 不应用残留状态
status 中的值来自 terminal 残留的旧对话上下文,空对话语义上无图片/视频,
直接置 false,否则误拦 handleModelSelect 的图片/视频能力检查。
2026-08-12 11:23:06 +08:00
9 changed files with 58 additions and 12 deletions

View File

@ -259,6 +259,8 @@ class MainTerminal(MainTerminalCommandMixin, MainTerminalContextMixin, MainTermi
self.terminal_manager.set_host_execution_mode(mode)
if getattr(self, "sub_agent_manager", None):
self.sub_agent_manager.set_host_execution_mode(mode)
if getattr(self, "file_manager", None):
self.file_manager.set_host_execution_mode(mode)
def _init_host_network_permission(self):
default_permission = str(HOST_SANDBOX_NETWORK_PERMISSION or "restricted").strip().lower()

View File

@ -54,8 +54,15 @@ class FileManagerBase:
self.container_session: Optional["ContainerHandle"] = None
self._container_proxy: Optional[ContainerFileProxy] = None
self._data_dir: Optional[str] = data_dir
# 宿主机执行环境sandbox / direct由主终端同步
# direct 时与 run_command 对齐:不走路径授权检查(不套沙箱语义)。
self.host_execution_mode: str = "sandbox"
self.set_container_session(container_session)
def set_host_execution_mode(self, mode: str) -> None:
normalized = str(mode or "").strip().lower()
self.host_execution_mode = "direct" if normalized == "direct" else "sandbox"
def _load_personalization_config(self) -> Optional[Dict]:
"""加载个性化配置"""
if not self._data_dir:

View File

@ -175,6 +175,11 @@ class PathMixin:
def _ensure_host_access(self, full_path: Path, access: str) -> Tuple[bool, str]:
if not self._is_host_mode():
return True, ""
# 执行环境为 direct完全访问权限run_command 不套沙箱、可读写任意路径;
# read_file/write_file/edit_file 为进程内文件操作,本就不走 OS 沙箱,此处与
# run_command 语义对齐直接放行。sandbox 模式下保持授权范围检查不变。
if getattr(self, "host_execution_mode", "sandbox") == "direct":
return True, ""
check_target = full_path
if access == "write" and not full_path.exists():
check_target = full_path.parent.resolve()

View File

@ -107,6 +107,7 @@ export const stateMethods = {
this.imageEntries = [];
this.imageLoading = false;
this.conversationHasImages = false;
this.conversationHasVideos = false;
this.toolSetSettingsLoading(false);
this.toolSetSettings([]);
this.pendingToolApprovals = [];

View File

@ -189,11 +189,20 @@ export const resourceMethods = {
} else {
this.pendingExecutionMode = '';
}
if (status && typeof status.has_images !== 'undefined') {
this.conversationHasImages = !!status.has_images;
}
if (status && typeof status.has_videos !== 'undefined') {
this.conversationHasVideos = !!status.has_videos;
// has_images/has_videos 遵循与上面模型/运行模式相同的权威规则:
// 有打开对话时才从 status 同步;空对话/显式新建路由(/new上 status 里的值
// 是 terminal 残留的旧对话上下文,不能应用——空对话语义上无图片/视频,
// 直接置 false否则会误拦 handleModelSelect 的图片/视频能力检查。
if (hasConversation && !onExplicitNewRoute) {
if (status && typeof status.has_images !== 'undefined') {
this.conversationHasImages = !!status.has_images;
}
if (status && typeof status.has_videos !== 'undefined') {
this.conversationHasVideos = !!status.has_videos;
}
} else {
this.conversationHasImages = false;
this.conversationHasVideos = false;
}
const compression = status?.conversation?.compression;
if (compression && typeof compression === 'object') {

View File

@ -389,6 +389,18 @@ function lineNumber(line: any): string {
color: var(--state-danger);
}
/* 列表最多显示 5 行32px/行),超出内部滚动;滚动条隐藏(同 QuickDock 惯例) */
.edit-summary-card__list {
max-height: 160px;
overflow-y: auto;
scrollbar-width: none;
-ms-overflow-style: none;
}
.edit-summary-card__list::-webkit-scrollbar {
display: none;
}
.edit-summary-card__row {
display: flex;
align-items: center;

View File

@ -311,7 +311,7 @@
}
.qd-run-item.is-active {
background: var(--surface-soft);
background: var(--hover-bg);
}
.qd-run-status {
@ -418,7 +418,7 @@
}
.qd-file-item.is-active {
background: var(--surface-soft);
background: var(--hover-bg);
}
.qd-file-name {

View File

@ -308,8 +308,22 @@
data-tutorial="quick-menu-open"
@click.stop="$emit('toggle-quick-menu')"
:disabled="!isConnected"
aria-label="快捷菜单"
>
+
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M5 12h14" /><path d="M12 5v14" />
</svg>
</button>
<div class="agent-type-switcher" :class="{ 'is-disabled': agentTypeLocked }">
<button

View File

@ -886,10 +886,6 @@ body[data-theme='light'] {
background: var(--hover-bg);
}
.add-btn {
font-size: 20px;
}
.stadium-btn.send-btn {
background: var(--accent);
color: var(--on-accent);