Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- 插件工具使用中文等非 ASCII 名称时 LLM 调用失败:主流 API 要求工具名匹配 `^[a-zA-Z0-9_-]{1,64}$`,现自动将非法名称转写为合法拼音名(`pypinyin` 缺失时退回下划线替换),冲突追加 `_2`/`_3` 后缀,并在工具描述前缀 `[原名: …]` 保留原名映射;`config_json.plugins` 配置键与插件内部仍使用原始名称,路由不受影响
- 修复聊天页在"生成中"时于输入框持续打字导致消息列表上下轻微抖动的问题:输入框高度测量改为在离屏克隆节点上进行,不再瞬态改变页面布局
- 修复个性化「通道」面板在页面放大后不出现纵向滚动条、被挤出的通道卡片无法查看的问题:工具栏固定、卡片网格改为内部滚动区(与技能面板一致),移动端仍整页滚动
- 修复聊天页「编辑文件」卡片只展开文件树、不定位到所生成文件的问题:点击后直接打开该轮最近写入文件的编辑标签页,同时保留文件列表标签页,便于继续浏览其他文件

## [0.9.24] - 2026-08-15

Expand Down
9 changes: 7 additions & 2 deletions dashboard/src/pages/Chat/components/AssistantTurnView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from "../utils/messageContent";
import { layoutAssistantTurnHitl } from "../utils/layoutAssistantTurnHitl";
import { useAgent } from "../../../context/AgentContext";
import { lastWriteToolPath } from "../hooks/useChatFileDetection";
import { TodoProgressPanel } from "../../../components/TodoProgressPanel";
import {
collectWriteTodosFromMessages,
Expand All @@ -35,7 +36,7 @@ interface AssistantTurnViewProps {
decisions: Array<{ type: string; message?: string }>,
) => void;
onOpenBrowser?: () => void;
onEditFile?: () => void;
onEditFile?: (path?: string | null) => void;
onRunShellCommand?: (code: string) => void;
shellCommandDisabled?: boolean;
shellCommandDisabledTitle?: string;
Expand Down Expand Up @@ -102,6 +103,10 @@ export default function AssistantTurnView({
const showOpenBrowser = usedBrowser && !!onOpenBrowser;
const usedFileTool = turnUsedFileTool(fullSplit);
const showEditFile = usedFileTool && !!onEditFile;
const lastFilePath = useMemo(
() => lastWriteToolPath(messages, agentId),
[messages, agentId],
);
const hasToolMedia =
toolMedia.images.length > 0 ||
toolMedia.videos.length > 0 ||
Expand Down Expand Up @@ -233,7 +238,7 @@ export default function AssistantTurnView({
className={`${styles.openBrowserPrompt} ${
turnStreaming ? styles.openBrowserPromptActive : ""
}`}
onClick={onEditFile}
onClick={() => onEditFile(lastFilePath)}
aria-label={t("chat.editFileCard", "编辑文件")}
>
<FilePen
Expand Down
4 changes: 2 additions & 2 deletions dashboard/src/pages/Chat/components/MessageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ interface MessageListProps {
decisions: Array<{ type: string; message?: string }>,
) => void;
onOpenBrowser?: () => void;
onEditFile?: () => void;
onEditFile?: (path?: string | null) => void;
onRunShellCommand?: (code: string) => void;
shellCommandDisabled?: boolean;
shellCommandDisabledTitle?: string;
Expand All @@ -128,7 +128,7 @@ interface GroupRenderContext {
decisions: Array<{ type: string; message?: string }>,
) => void;
onOpenBrowser?: () => void;
onEditFile?: () => void;
onEditFile?: (path?: string | null) => void;
onRunShellCommand?: (code: string) => void;
shellCommandDisabled?: boolean;
shellCommandDisabledTitle?: string;
Expand Down
20 changes: 20 additions & 0 deletions dashboard/src/pages/Chat/hooks/useChatDockPanel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,26 @@ describe("useChatDockPanel tabs", () => {
expect(result.current.openTabs.map((t) => t.id)).toEqual([fileId]);
});

it("openFileListAt opens the files tab together with the focused file tab", () => {
const { result } = renderHook(() => useChatDockPanel(false, "main"));
act(() => {
result.current.openFileListAt("/outbound/a.txt");
});
const fileId = dockFileTabId("/outbound/a.txt", "main");
expect(result.current.dockOpen).toBe(true);
expect(result.current.openTabs.map((t) => t.id)).toEqual(["files", fileId]);
expect(result.current.activeTabId).toBe(fileId);
});

it("openFileListAt without a path falls back to the files tab", () => {
const { result } = renderHook(() => useChatDockPanel(false, "main"));
act(() => {
result.current.openFileListAt(null);
});
expect(result.current.openTabs.map((t) => t.id)).toEqual(["files"]);
expect(result.current.activeTabId).toBe("files");
});

it("toggleBrowserPanel opens browser tab then closes dock when active", () => {
const { result } = renderHook(() => useChatDockPanel(false));
act(() => {
Expand Down
13 changes: 13 additions & 0 deletions dashboard/src/pages/Chat/hooks/useChatDockPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,18 @@ export function useChatDockPanel(isMobile: boolean, agentId?: string | null) {
[agentId, openDock, openFileList],
);

/**
* Edit-file card: open the file list tab alongside the generated file tab so
* the tree stays reachable while landing directly on the file itself.
*/
const openFileListAt = useCallback(
(path?: string | null) => {
openFileList();
openFileAt(path);
},
[openFileList, openFileAt],
);

const openBrowserTab = useCallback(() => {
setOpenTabs((prev) => {
if (prev.some((t) => t.id === "browser")) return prev;
Expand Down Expand Up @@ -259,6 +271,7 @@ export function useChatDockPanel(isMobile: boolean, agentId?: string | null) {
handleModeChange,
openFileAt,
openFileList,
openFileListAt,
openBrowserTab,
toggleBrowserPanel,
openTerminalTab,
Expand Down
18 changes: 18 additions & 0 deletions dashboard/src/pages/Chat/hooks/useChatFileDetection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,24 @@ function addPath(
paths.push(normalizeDockFilePath(path!) || key);
}

/**
* Path of the most recent workspace write/edit tool call in this turn, so the
* "edit file" card can jump straight to the generated file (falls back to the
* file list when no listable path is found).
*/
export function lastWriteToolPath(
messages: ChatMessage[],
agentId?: string | null,
): string | null {
for (let i = messages.length - 1; i >= 0; i--) {
const raw = extractWriteToolPath(messages[i]);
if (!raw) continue;
const key = canonicalizeDockFilePath(raw, agentId);
if (key && isDockListablePath(key)) return raw;
}
return null;
}

/**
* Collect workspace file paths from the active thread so the docked file
* panel can switch among written / previewable / attached files together.
Expand Down
3 changes: 2 additions & 1 deletion dashboard/src/pages/Chat/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ function ChatPageInner() {
handleClose: handleDockClose,
handleModeChange: handleDockModeChange,
openFileList,
openFileListAt,
openFileAt,
openBrowserTab,
toggleBrowserPanel,
Expand Down Expand Up @@ -839,7 +840,7 @@ function ChatPageInner() {
}
onEditFile={
!sharedExpertViewer && panelFilePaths.length > 0 && !isMobile
? openFileList
? openFileListAt
: undefined
}
/>
Expand Down
Loading