Files
tutti/packages/agent/gui/agent-gui/agentGuiNode/agentMentionAgentGeneratedFilesPresentation.ts
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

161 lines
4.8 KiB
TypeScript

import { translate } from "../../i18n/index";
import type { AgentContextMentionItem } from "./agentRichText/agentFileMentionExtension";
import type { AgentMentionFileItem } from "./agentRichText/agentFileMentionExtension";
const AGENT_GENERATED_FOLDER_MIN_FILES = 2;
export function presentAgentGeneratedFileMentionItems(input: {
files: readonly AgentContextMentionItem[];
browsePath: string | null;
query: string;
}): AgentContextMentionItem[] {
const fileItems = input.files.filter(
(item): item is AgentMentionFileItem => item.kind === "file"
);
if (input.query.trim()) {
return fileItems;
}
if (input.browsePath) {
return presentAgentGeneratedBrowseFolder(fileItems, input.browsePath);
}
return presentAgentGeneratedRootFolders(fileItems);
}
function presentAgentGeneratedRootFolders(
files: readonly AgentMentionFileItem[]
): AgentMentionFileItem[] {
const filesByDirectory = new Map<string, AgentMentionFileItem[]>();
for (const file of files) {
const directoryPath = resolveAgentGeneratedFileDirectoryPath(file);
const group = filesByDirectory.get(directoryPath);
if (group) {
group.push(file);
continue;
}
filesByDirectory.set(directoryPath, [file]);
}
const presented: AgentMentionFileItem[] = [];
for (const [directoryPath, group] of filesByDirectory) {
if (group.length >= AGENT_GENERATED_FOLDER_MIN_FILES) {
presented.push(createAgentGeneratedFolderItem(directoryPath, group));
continue;
}
presented.push(...group);
}
return sortAgentGeneratedMentionItems(presented);
}
function presentAgentGeneratedBrowseFolder(
files: readonly AgentMentionFileItem[],
browsePath: string
): AgentMentionFileItem[] {
const normalizedBrowsePath = normalizeAgentGeneratedDirectoryPath(browsePath);
const children = files.filter(
(file) =>
resolveAgentGeneratedFileDirectoryPath(file) === normalizedBrowsePath
);
return [
createAgentGeneratedFolderBackItem(normalizedBrowsePath),
...sortAgentGeneratedMentionItems(children)
];
}
function createAgentGeneratedFolderItem(
directoryPath: string,
files: readonly AgentMentionFileItem[]
): AgentMentionFileItem {
const normalizedPath = normalizeAgentGeneratedDirectoryPath(directoryPath);
const name =
normalizedPath.split("/").filter(Boolean).at(-1) ?? normalizedPath;
const href =
normalizedPath === "/" || normalizedPath.endsWith("/")
? normalizedPath
: `${normalizedPath}/`;
return {
kind: "file",
href,
path: normalizedPath,
name,
entryKind: "directory",
directoryPath: parentAgentGeneratedDirectoryPath(normalizedPath),
mentionNavigation: "agent-generated-folder",
childCount: files.length
};
}
function createAgentGeneratedFolderBackItem(
browsePath: string
): AgentMentionFileItem {
return {
kind: "file",
href: "",
path: browsePath,
name: translate("agentHost.agentGui.mentionFolderBack"),
entryKind: "unknown",
directoryPath: parentAgentGeneratedDirectoryPath(browsePath),
mentionNavigation: "agent-generated-folder-back"
};
}
function resolveAgentGeneratedFileDirectoryPath(
file: AgentMentionFileItem
): string {
const directoryPath = file.directoryPath?.trim();
if (directoryPath) {
return normalizeAgentGeneratedDirectoryPath(directoryPath);
}
return parentAgentGeneratedDirectoryPath(file.path);
}
function parentAgentGeneratedDirectoryPath(path: string): string {
const normalized = normalizeAgentGeneratedDirectoryPath(path);
if (normalized === "/" || /^[A-Za-z]:\/$/.test(normalized)) {
return "/";
}
const index = normalized.lastIndexOf("/");
if (index <= 0) {
return "/";
}
const parent = normalized.slice(0, index);
return /^[A-Za-z]:$/.test(parent) ? `${parent}/` : parent;
}
function normalizeAgentGeneratedDirectoryPath(path: string): string {
const normalized = path.trim().replace(/\\/g, "/");
const withoutTrailingSeparators = normalized.replace(/\/+$/, "");
// Keep POSIX root as `/`. Removing its only separator turns the synthetic
// folder label into an empty string.
if (!withoutTrailingSeparators) {
return normalized.startsWith("/") ? "/" : "";
}
if (
/^[A-Za-z]:$/.test(withoutTrailingSeparators) &&
/^[A-Za-z]:\/+/.test(normalized)
) {
return `${withoutTrailingSeparators}/`;
}
return withoutTrailingSeparators;
}
function sortAgentGeneratedMentionItems(
items: readonly AgentMentionFileItem[]
): AgentMentionFileItem[] {
return [...items].sort((left, right) =>
left.name.localeCompare(right.name, undefined, { sensitivity: "base" })
);
}
export function agentGeneratedMentionItemKey(
item: AgentContextMentionItem
): string {
if (item.kind !== "file") {
return item.targetId;
}
if (item.mentionNavigation) {
return `${item.mentionNavigation}:${item.path}`;
}
return item.path;
}