Add Copy file path to file preview tab menus (#1473)
* feat(app): add Copy file path to file preview tab menus * fix(app): restore copyAgentId translations accidentally replaced by placeholder
This commit is contained in:
@@ -86,6 +86,7 @@ interface SplitContainerProps {
|
||||
onCloseTab: (tabId: string) => Promise<void> | void;
|
||||
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
|
||||
onCopyAgentId: (agentId: string) => Promise<void> | void;
|
||||
onCopyFilePath: (path: string) => Promise<void> | void;
|
||||
onReloadAgent: (agentId: string) => Promise<void> | void;
|
||||
onRenameTab: (tab: WorkspaceTabDescriptor) => void;
|
||||
onCloseTabsToLeft: (tabId: string, paneTabs: WorkspaceTabDescriptor[]) => Promise<void> | void;
|
||||
@@ -364,6 +365,7 @@ export function SplitContainer({
|
||||
onCloseTab,
|
||||
onCopyResumeCommand,
|
||||
onCopyAgentId,
|
||||
onCopyFilePath,
|
||||
onReloadAgent,
|
||||
onRenameTab,
|
||||
onCloseTabsToLeft,
|
||||
@@ -580,6 +582,7 @@ export function SplitContainer({
|
||||
onCloseTab={onCloseTab}
|
||||
onCopyResumeCommand={onCopyResumeCommand}
|
||||
onCopyAgentId={onCopyAgentId}
|
||||
onCopyFilePath={onCopyFilePath}
|
||||
onReloadAgent={onReloadAgent}
|
||||
onRenameTab={onRenameTab}
|
||||
onCloseTabsToLeft={onCloseTabsToLeft}
|
||||
@@ -722,6 +725,7 @@ function SplitNodeView({
|
||||
onCloseTab,
|
||||
onCopyResumeCommand,
|
||||
onCopyAgentId,
|
||||
onCopyFilePath,
|
||||
onReloadAgent,
|
||||
onRenameTab,
|
||||
onCloseTabsToLeft,
|
||||
@@ -774,6 +778,7 @@ function SplitNodeView({
|
||||
onCloseTab={onCloseTab}
|
||||
onCopyResumeCommand={onCopyResumeCommand}
|
||||
onCopyAgentId={onCopyAgentId}
|
||||
onCopyFilePath={onCopyFilePath}
|
||||
onReloadAgent={onReloadAgent}
|
||||
onRenameTab={onRenameTab}
|
||||
onCloseTabsToLeft={onCloseTabsToLeft}
|
||||
@@ -819,6 +824,7 @@ function SplitNodeView({
|
||||
onCloseTab={onCloseTab}
|
||||
onCopyResumeCommand={onCopyResumeCommand}
|
||||
onCopyAgentId={onCopyAgentId}
|
||||
onCopyFilePath={onCopyFilePath}
|
||||
onReloadAgent={onReloadAgent}
|
||||
onRenameTab={onRenameTab}
|
||||
onCloseTabsToLeft={onCloseTabsToLeft}
|
||||
@@ -870,6 +876,7 @@ function SplitPaneView({
|
||||
onCloseTab,
|
||||
onCopyResumeCommand,
|
||||
onCopyAgentId,
|
||||
onCopyFilePath,
|
||||
onReloadAgent,
|
||||
onRenameTab,
|
||||
onCloseTabsToLeft,
|
||||
@@ -1011,6 +1018,7 @@ function SplitPaneView({
|
||||
onCloseTab={onCloseTab}
|
||||
onCopyResumeCommand={onCopyResumeCommand}
|
||||
onCopyAgentId={onCopyAgentId}
|
||||
onCopyFilePath={onCopyFilePath}
|
||||
onReloadAgent={onReloadAgent}
|
||||
onRenameTab={onRenameTab}
|
||||
onCloseTabsToLeft={handleCloseTabsToLeft}
|
||||
|
||||
@@ -433,6 +433,7 @@ export const ar: TranslationResources = {
|
||||
openFor: "فتح القائمة لـ{{label}}",
|
||||
copyResumeCommand: "نسخ أمر السيرة الذاتية",
|
||||
copyAgentId: "نسخ معرف الوكيل",
|
||||
copyFilePath: "Copy file path",
|
||||
rename: "إعادة تسمية",
|
||||
closeAbove: "إغلاق علامات التبويب أعلاه",
|
||||
closeBelow: "إغلاق علامات التبويب أدناه",
|
||||
@@ -465,6 +466,7 @@ export const ar: TranslationResources = {
|
||||
copyFailed: "فشل النسخ",
|
||||
agentIdCopiedLabel: "AgentID",
|
||||
resumeCommandCopiedLabel: "أمر الاستئناف",
|
||||
filePathCopiedLabel: "File path",
|
||||
resumeIdUnavailable: "السيرة الذاتية ID غير متوفرة",
|
||||
resumeCommandUnavailable: "أمر الاستئناف غير متوفر",
|
||||
reloadingAgent: "وكيل إعادة التحميل...",
|
||||
|
||||
@@ -432,6 +432,7 @@ export const en = {
|
||||
openFor: "Open menu for {{label}}",
|
||||
copyResumeCommand: "Copy resume command",
|
||||
copyAgentId: "Copy agent id",
|
||||
copyFilePath: "Copy file path",
|
||||
rename: "Rename",
|
||||
closeAbove: "Close tabs above",
|
||||
closeBelow: "Close tabs below",
|
||||
@@ -464,6 +465,7 @@ export const en = {
|
||||
copyFailed: "Copy failed",
|
||||
agentIdCopiedLabel: "Agent ID",
|
||||
resumeCommandCopiedLabel: "resume command",
|
||||
filePathCopiedLabel: "File path",
|
||||
resumeIdUnavailable: "Resume ID not available",
|
||||
resumeCommandUnavailable: "Resume command not available",
|
||||
reloadingAgent: "Reloading agent...",
|
||||
|
||||
@@ -436,6 +436,7 @@ export const es: TranslationResources = {
|
||||
openFor: "Menú abierto para{{label}}",
|
||||
copyResumeCommand: "Copiar comando de reanudación",
|
||||
copyAgentId: "Copiar ID del agente",
|
||||
copyFilePath: "Copy file path",
|
||||
rename: "Rebautizar",
|
||||
closeAbove: "Cerrar pestañas arriba",
|
||||
closeBelow: "Cerrar pestañas a continuación",
|
||||
@@ -469,6 +470,7 @@ export const es: TranslationResources = {
|
||||
copyFailed: "Copia fallida",
|
||||
agentIdCopiedLabel: "AgentID",
|
||||
resumeCommandCopiedLabel: "reanudar el comando",
|
||||
filePathCopiedLabel: "File path",
|
||||
resumeIdUnavailable: "ReanudarIDno disponible",
|
||||
resumeCommandUnavailable: "Comando de reanudación no disponible",
|
||||
reloadingAgent: "Agente de recarga...",
|
||||
|
||||
@@ -436,6 +436,7 @@ export const fr: TranslationResources = {
|
||||
openFor: "Ouvrir le menu pour{{label}}",
|
||||
copyResumeCommand: "Copier la commande de reprise",
|
||||
copyAgentId: "Copier l'identifiant de l'agent",
|
||||
copyFilePath: "Copy file path",
|
||||
rename: "Rebaptiser",
|
||||
closeAbove: "Fermer les onglets ci-dessus",
|
||||
closeBelow: "Fermer les onglets ci-dessous",
|
||||
@@ -469,6 +470,7 @@ export const fr: TranslationResources = {
|
||||
copyFailed: "Échec de la copie",
|
||||
agentIdCopiedLabel: "AgentID",
|
||||
resumeCommandCopiedLabel: "reprendre la commande",
|
||||
filePathCopiedLabel: "File path",
|
||||
resumeIdUnavailable: "ReprendreIDnon disponible",
|
||||
resumeCommandUnavailable: "Commande de reprise non disponible",
|
||||
reloadingAgent: "Agent de rechargement...",
|
||||
|
||||
@@ -436,6 +436,7 @@ export const ru: TranslationResources = {
|
||||
openFor: "Открыть меню для{{label}}",
|
||||
copyResumeCommand: "Копировать команду возобновления",
|
||||
copyAgentId: "Скопировать идентификатор агента",
|
||||
copyFilePath: "Copy file path",
|
||||
rename: "Переименовать",
|
||||
closeAbove: "Закрыть вкладки выше",
|
||||
closeBelow: "Закройте вкладки ниже",
|
||||
@@ -468,6 +469,7 @@ export const ru: TranslationResources = {
|
||||
copyFailed: "Не удалось скопировать",
|
||||
agentIdCopiedLabel: "AgentID",
|
||||
resumeCommandCopiedLabel: "команда возобновления",
|
||||
filePathCopiedLabel: "File path",
|
||||
resumeIdUnavailable: "Резюме ID недоступно",
|
||||
resumeCommandUnavailable: "Команда возобновления недоступна",
|
||||
reloadingAgent: "Перезагрузка агента...",
|
||||
|
||||
@@ -433,6 +433,7 @@ export const zhCN: TranslationResources = {
|
||||
openFor: "打开 {{label}} 的菜单",
|
||||
copyResumeCommand: "复制恢复命令",
|
||||
copyAgentId: "复制 Agent ID",
|
||||
copyFilePath: "Copy file path",
|
||||
rename: "重命名",
|
||||
closeAbove: "关闭上方标签",
|
||||
closeBelow: "关闭下方标签",
|
||||
@@ -465,6 +466,7 @@ export const zhCN: TranslationResources = {
|
||||
copyFailed: "复制失败",
|
||||
agentIdCopiedLabel: "Agent ID",
|
||||
resumeCommandCopiedLabel: "恢复命令",
|
||||
filePathCopiedLabel: "File path",
|
||||
resumeIdUnavailable: "恢复 ID 不可用",
|
||||
resumeCommandUnavailable: "恢复命令不可用",
|
||||
reloadingAgent: "正在重新加载 Agent...",
|
||||
|
||||
@@ -205,6 +205,7 @@ interface WorkspaceDesktopTabsRowProps {
|
||||
onCloseTab: (tabId: string) => Promise<void> | void;
|
||||
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
|
||||
onCopyAgentId: (agentId: string) => Promise<void> | void;
|
||||
onCopyFilePath: (path: string) => Promise<void> | void;
|
||||
onReloadAgent: (agentId: string) => Promise<void> | void;
|
||||
onRenameTab: (tab: WorkspaceTabDescriptor) => void;
|
||||
onCloseTabsToLeft: (tabId: string) => Promise<void> | void;
|
||||
@@ -519,6 +520,7 @@ export function WorkspaceDesktopTabsRow({
|
||||
onCloseTab,
|
||||
onCopyResumeCommand,
|
||||
onCopyAgentId,
|
||||
onCopyFilePath,
|
||||
onReloadAgent,
|
||||
onRenameTab,
|
||||
onCloseTabsToLeft,
|
||||
@@ -590,6 +592,7 @@ export function WorkspaceDesktopTabsRow({
|
||||
() => ({
|
||||
copyResumeCommand: t("workspace.tabs.menu.copyResumeCommand"),
|
||||
copyAgentId: t("workspace.tabs.menu.copyAgentId"),
|
||||
copyFilePath: t("workspace.tabs.menu.copyFilePath"),
|
||||
rename: t("workspace.tabs.menu.rename"),
|
||||
closeAbove: t("workspace.tabs.menu.closeAbove"),
|
||||
closeBelow: t("workspace.tabs.menu.closeBelow"),
|
||||
@@ -684,6 +687,7 @@ export function WorkspaceDesktopTabsRow({
|
||||
normalizedWorkspaceId={normalizedWorkspaceId}
|
||||
onCopyResumeCommand={onCopyResumeCommand}
|
||||
onCopyAgentId={onCopyAgentId}
|
||||
onCopyFilePath={onCopyFilePath}
|
||||
onReloadAgent={onReloadAgent}
|
||||
onRenameTab={onRenameTab}
|
||||
onCloseTabsToLeft={onCloseTabsToLeft}
|
||||
@@ -714,6 +718,7 @@ export function WorkspaceDesktopTabsRow({
|
||||
onCloseTabsToLeft,
|
||||
onCloseTabsToRight,
|
||||
onCopyAgentId,
|
||||
onCopyFilePath,
|
||||
onCopyResumeCommand,
|
||||
onNavigateTab,
|
||||
onReloadAgent,
|
||||
@@ -894,6 +899,7 @@ function ResolvedDesktopTabChip({
|
||||
normalizedWorkspaceId,
|
||||
onCopyResumeCommand,
|
||||
onCopyAgentId,
|
||||
onCopyFilePath,
|
||||
onReloadAgent,
|
||||
onRenameTab,
|
||||
onCloseTabsToLeft,
|
||||
@@ -919,6 +925,7 @@ function ResolvedDesktopTabChip({
|
||||
normalizedWorkspaceId: string;
|
||||
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
|
||||
onCopyAgentId: (agentId: string) => Promise<void> | void;
|
||||
onCopyFilePath: (path: string) => Promise<void> | void;
|
||||
onReloadAgent: (agentId: string) => Promise<void> | void;
|
||||
onRenameTab: (tab: WorkspaceTabDescriptor) => void;
|
||||
onCloseTabsToLeft: (tabId: string) => Promise<void> | void;
|
||||
@@ -944,6 +951,7 @@ function ResolvedDesktopTabChip({
|
||||
tabCount,
|
||||
onCopyResumeCommand,
|
||||
onCopyAgentId,
|
||||
onCopyFilePath,
|
||||
onReloadAgent,
|
||||
onRenameTab,
|
||||
onCloseTab,
|
||||
@@ -960,6 +968,7 @@ function ResolvedDesktopTabChip({
|
||||
onCloseTabsToLeft,
|
||||
onCloseTabsToRight,
|
||||
onCopyAgentId,
|
||||
onCopyFilePath,
|
||||
onCopyResumeCommand,
|
||||
labels,
|
||||
onReloadAgent,
|
||||
|
||||
@@ -365,6 +365,7 @@ interface MobileWorkspaceTabSwitcherProps {
|
||||
onSelectSwitcherTab: (key: string) => void;
|
||||
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
|
||||
onCopyAgentId: (agentId: string) => Promise<void> | void;
|
||||
onCopyFilePath: (path: string) => Promise<void> | void;
|
||||
onReloadAgent: (agentId: string) => Promise<void> | void;
|
||||
onRenameTab: (tab: WorkspaceTabDescriptor) => void;
|
||||
onCloseTab: (tabId: string) => Promise<void> | void;
|
||||
@@ -552,6 +553,7 @@ function MobileWorkspaceTabOption({
|
||||
onPress,
|
||||
onCopyResumeCommand,
|
||||
onCopyAgentId,
|
||||
onCopyFilePath,
|
||||
onReloadAgent,
|
||||
onRenameTab,
|
||||
onCloseTab,
|
||||
@@ -569,6 +571,7 @@ function MobileWorkspaceTabOption({
|
||||
onPress: () => void;
|
||||
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
|
||||
onCopyAgentId: (agentId: string) => Promise<void> | void;
|
||||
onCopyFilePath: (path: string) => Promise<void> | void;
|
||||
onReloadAgent: (agentId: string) => Promise<void> | void;
|
||||
onRenameTab: (tab: WorkspaceTabDescriptor) => void;
|
||||
onCloseTab: (tabId: string) => Promise<void> | void;
|
||||
@@ -581,6 +584,7 @@ function MobileWorkspaceTabOption({
|
||||
() => ({
|
||||
copyResumeCommand: t("workspace.tabs.menu.copyResumeCommand"),
|
||||
copyAgentId: t("workspace.tabs.menu.copyAgentId"),
|
||||
copyFilePath: t("workspace.tabs.menu.copyFilePath"),
|
||||
rename: t("workspace.tabs.menu.rename"),
|
||||
closeAbove: t("workspace.tabs.menu.closeAbove"),
|
||||
closeBelow: t("workspace.tabs.menu.closeBelow"),
|
||||
@@ -602,6 +606,7 @@ function MobileWorkspaceTabOption({
|
||||
menuTestIDBase,
|
||||
onCopyResumeCommand,
|
||||
onCopyAgentId,
|
||||
onCopyFilePath,
|
||||
onReloadAgent,
|
||||
onRenameTab,
|
||||
onCloseTab,
|
||||
@@ -668,6 +673,7 @@ const MobileWorkspaceTabSwitcher = memo(function MobileWorkspaceTabSwitcher({
|
||||
onSelectSwitcherTab,
|
||||
onCopyResumeCommand,
|
||||
onCopyAgentId,
|
||||
onCopyFilePath,
|
||||
onReloadAgent,
|
||||
onRenameTab,
|
||||
onCloseTab,
|
||||
@@ -723,6 +729,7 @@ const MobileWorkspaceTabSwitcher = memo(function MobileWorkspaceTabSwitcher({
|
||||
onPress={onPress}
|
||||
onCopyResumeCommand={onCopyResumeCommand}
|
||||
onCopyAgentId={onCopyAgentId}
|
||||
onCopyFilePath={onCopyFilePath}
|
||||
onReloadAgent={onReloadAgent}
|
||||
onRenameTab={onRenameTab}
|
||||
onCloseTab={onCloseTab}
|
||||
@@ -740,6 +747,7 @@ const MobileWorkspaceTabSwitcher = memo(function MobileWorkspaceTabSwitcher({
|
||||
normalizedWorkspaceId,
|
||||
onCopyResumeCommand,
|
||||
onCopyAgentId,
|
||||
onCopyFilePath,
|
||||
onReloadAgent,
|
||||
onRenameTab,
|
||||
onCloseTab,
|
||||
@@ -2600,6 +2608,19 @@ function WorkspaceScreenContent({
|
||||
[toast, t],
|
||||
);
|
||||
|
||||
const handleCopyFilePath = useCallback(
|
||||
async (path: string) => {
|
||||
if (!path) return;
|
||||
try {
|
||||
await Clipboard.setStringAsync(path);
|
||||
toast.copied(t("workspace.tabs.toasts.filePathCopiedLabel"));
|
||||
} catch {
|
||||
toast.error(t("workspace.tabs.toasts.copyFailed"));
|
||||
}
|
||||
},
|
||||
[toast, t],
|
||||
);
|
||||
|
||||
const handleCopyResumeCommand = useCallback(
|
||||
async (agentId: string) => {
|
||||
if (!agentId) return;
|
||||
@@ -3422,6 +3443,7 @@ function WorkspaceScreenContent({
|
||||
onCloseTab={handleCloseTabById}
|
||||
onCopyResumeCommand={handleCopyResumeCommand}
|
||||
onCopyAgentId={handleCopyAgentId}
|
||||
onCopyFilePath={handleCopyFilePath}
|
||||
onReloadAgent={handleReloadAgent}
|
||||
onRenameTab={handleRenameTab}
|
||||
onCloseTabsToLeft={handleCloseTabsToLeftInPane}
|
||||
@@ -3456,6 +3478,7 @@ function WorkspaceScreenContent({
|
||||
handleCloseTabById,
|
||||
handleCopyResumeCommand,
|
||||
handleCopyAgentId,
|
||||
handleCopyFilePath,
|
||||
handleReloadAgent,
|
||||
handleRenameTab,
|
||||
handleCloseTabsToLeftInPane,
|
||||
@@ -3535,6 +3558,7 @@ function WorkspaceScreenContent({
|
||||
onSelectSwitcherTab={handleSelectSwitcherTab}
|
||||
onCopyResumeCommand={handleCopyResumeCommand}
|
||||
onCopyAgentId={handleCopyAgentId}
|
||||
onCopyFilePath={handleCopyFilePath}
|
||||
onReloadAgent={handleReloadAgent}
|
||||
onRenameTab={handleRenameTab}
|
||||
onCloseTab={handleCloseTabById}
|
||||
@@ -3556,6 +3580,7 @@ function WorkspaceScreenContent({
|
||||
onCloseTab={handleCloseTabById}
|
||||
onCopyResumeCommand={handleCopyResumeCommand}
|
||||
onCopyAgentId={handleCopyAgentId}
|
||||
onCopyFilePath={handleCopyFilePath}
|
||||
onReloadAgent={handleReloadAgent}
|
||||
onRenameTab={handleRenameTab}
|
||||
onCloseTabsToLeft={handleCloseTabsToLeft}
|
||||
|
||||
@@ -15,6 +15,7 @@ describe("buildWorkspaceTabMenuEntries", () => {
|
||||
it("uses desktop tab ordering labels for desktop menus", () => {
|
||||
const onCopyResumeCommand = vi.fn();
|
||||
const onCopyAgentId = vi.fn();
|
||||
const onCopyFilePath = vi.fn();
|
||||
const onReloadAgent = vi.fn();
|
||||
const onRenameTab = vi.fn();
|
||||
const onCloseTab = vi.fn();
|
||||
@@ -30,6 +31,7 @@ describe("buildWorkspaceTabMenuEntries", () => {
|
||||
menuTestIDBase: "workspace-tab-context-agent_123",
|
||||
onCopyResumeCommand,
|
||||
onCopyAgentId,
|
||||
onCopyFilePath,
|
||||
onReloadAgent,
|
||||
onRenameTab,
|
||||
onCloseTab,
|
||||
@@ -59,6 +61,7 @@ describe("buildWorkspaceTabMenuEntries", () => {
|
||||
menuTestIDBase: "workspace-tab-menu-agent_123",
|
||||
onCopyResumeCommand: vi.fn(),
|
||||
onCopyAgentId: vi.fn(),
|
||||
onCopyFilePath: vi.fn(),
|
||||
onReloadAgent: vi.fn(),
|
||||
onRenameTab: vi.fn(),
|
||||
onCloseTab: vi.fn(),
|
||||
@@ -93,6 +96,7 @@ describe("buildWorkspaceTabMenuEntries", () => {
|
||||
menuTestIDBase: "workspace-tab-menu-draft_123",
|
||||
onCopyResumeCommand: vi.fn(),
|
||||
onCopyAgentId: vi.fn(),
|
||||
onCopyFilePath: vi.fn(),
|
||||
onReloadAgent: vi.fn(),
|
||||
onRenameTab: vi.fn(),
|
||||
onCloseTab: vi.fn(),
|
||||
@@ -120,6 +124,7 @@ describe("buildWorkspaceTabMenuEntries", () => {
|
||||
menuTestIDBase: "workspace-tab-context-agent_123",
|
||||
onCopyResumeCommand: vi.fn(),
|
||||
onCopyAgentId: vi.fn(),
|
||||
onCopyFilePath: vi.fn(),
|
||||
onReloadAgent: vi.fn(),
|
||||
onRenameTab: vi.fn(),
|
||||
onCloseTab: vi.fn(),
|
||||
@@ -148,6 +153,7 @@ describe("buildWorkspaceTabMenuEntries", () => {
|
||||
menuTestIDBase: "workspace-tab-context-agent_123",
|
||||
onCopyResumeCommand: vi.fn(),
|
||||
onCopyAgentId: vi.fn(),
|
||||
onCopyFilePath: vi.fn(),
|
||||
onReloadAgent: vi.fn(),
|
||||
onRenameTab,
|
||||
onCloseTab: vi.fn(),
|
||||
@@ -181,6 +187,7 @@ describe("buildWorkspaceTabMenuEntries", () => {
|
||||
menuTestIDBase: "workspace-tab-context-terminal_abc",
|
||||
onCopyResumeCommand: vi.fn(),
|
||||
onCopyAgentId: vi.fn(),
|
||||
onCopyFilePath: vi.fn(),
|
||||
onReloadAgent: vi.fn(),
|
||||
onRenameTab,
|
||||
onCloseTab: vi.fn(),
|
||||
@@ -193,6 +200,7 @@ describe("buildWorkspaceTabMenuEntries", () => {
|
||||
expect(labels[0]).toBe("Rename");
|
||||
expect(labels).not.toContain("Copy resume command");
|
||||
expect(labels).not.toContain("Copy agent id");
|
||||
expect(labels).not.toContain("Copy file path");
|
||||
expect(labels).not.toContain("Reload agent");
|
||||
|
||||
const renameEntry = entries.find((entry) => entry.kind === "item" && entry.label === "Rename");
|
||||
@@ -203,6 +211,48 @@ describe("buildWorkspaceTabMenuEntries", () => {
|
||||
expect(onRenameTab).toHaveBeenCalledWith(terminalTab);
|
||||
});
|
||||
|
||||
it("includes copy file path for file tabs", () => {
|
||||
const onCopyFilePath = vi.fn();
|
||||
const fileTab: WorkspaceTabDescriptor = {
|
||||
key: "file_abc",
|
||||
tabId: "file_abc",
|
||||
kind: "file",
|
||||
target: { kind: "file", path: "/some/path.ts", lineStart: 1, lineEnd: 10 },
|
||||
};
|
||||
const entries = buildWorkspaceTabMenuEntries({
|
||||
surface: "desktop",
|
||||
tab: fileTab,
|
||||
index: 0,
|
||||
tabCount: 1,
|
||||
menuTestIDBase: "workspace-tab-context-file_abc",
|
||||
onCopyResumeCommand: vi.fn(),
|
||||
onCopyAgentId: vi.fn(),
|
||||
onCopyFilePath,
|
||||
onReloadAgent: vi.fn(),
|
||||
onRenameTab: vi.fn(),
|
||||
onCloseTab: vi.fn(),
|
||||
onCloseTabsBefore: vi.fn(),
|
||||
onCloseTabsAfter: vi.fn(),
|
||||
onCloseOtherTabs: vi.fn(),
|
||||
});
|
||||
|
||||
const labels = entries.filter((entry) => entry.kind === "item").map((entry) => entry.label);
|
||||
expect(labels[0]).toBe("Copy file path");
|
||||
expect(labels).not.toContain("Copy resume command");
|
||||
expect(labels).not.toContain("Copy agent id");
|
||||
expect(labels).not.toContain("Rename");
|
||||
expect(labels).not.toContain("Reload agent");
|
||||
|
||||
const copyFilePathEntry = entries.find(
|
||||
(entry) => entry.kind === "item" && entry.key === "copy-file-path",
|
||||
);
|
||||
if (!copyFilePathEntry || copyFilePathEntry.kind !== "item") {
|
||||
throw new Error("Copy file path entry missing");
|
||||
}
|
||||
copyFilePathEntry.onSelect();
|
||||
expect(onCopyFilePath).toHaveBeenCalledWith("/some/path.ts");
|
||||
});
|
||||
|
||||
it("uses the same rename entry shape for agent and terminal tabs", () => {
|
||||
const terminalTab: WorkspaceTabDescriptor = {
|
||||
key: "terminal_abc",
|
||||
@@ -218,6 +268,7 @@ describe("buildWorkspaceTabMenuEntries", () => {
|
||||
menuTestIDBase,
|
||||
onCopyResumeCommand: vi.fn(),
|
||||
onCopyAgentId: vi.fn(),
|
||||
onCopyFilePath: vi.fn(),
|
||||
onReloadAgent: vi.fn(),
|
||||
onRenameTab: vi.fn(),
|
||||
onCloseTab: vi.fn(),
|
||||
|
||||
@@ -8,6 +8,7 @@ export type WorkspaceTabMenuSurface = "desktop" | "mobile";
|
||||
export interface WorkspaceTabMenuLabels {
|
||||
copyResumeCommand: string;
|
||||
copyAgentId: string;
|
||||
copyFilePath: string;
|
||||
rename: string;
|
||||
closeAbove: string;
|
||||
closeBelow: string;
|
||||
@@ -22,6 +23,7 @@ export interface WorkspaceTabMenuLabels {
|
||||
export const DEFAULT_WORKSPACE_TAB_MENU_LABELS: WorkspaceTabMenuLabels = {
|
||||
copyResumeCommand: i18n.t("workspace.tabs.menu.copyResumeCommand"),
|
||||
copyAgentId: i18n.t("workspace.tabs.menu.copyAgentId"),
|
||||
copyFilePath: i18n.t("workspace.tabs.menu.copyFilePath"),
|
||||
rename: i18n.t("workspace.tabs.menu.rename"),
|
||||
closeAbove: i18n.t("workspace.tabs.menu.closeAbove"),
|
||||
closeBelow: i18n.t("workspace.tabs.menu.closeBelow"),
|
||||
@@ -66,6 +68,7 @@ interface BuildWorkspaceTabMenuEntriesInput {
|
||||
menuTestIDBase: string;
|
||||
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
|
||||
onCopyAgentId: (agentId: string) => Promise<void> | void;
|
||||
onCopyFilePath: (path: string) => Promise<void> | void;
|
||||
onReloadAgent: (agentId: string) => Promise<void> | void;
|
||||
onRenameTab: (tab: WorkspaceTabDescriptor) => void;
|
||||
onCloseTab: (tabId: string) => Promise<void> | void;
|
||||
@@ -81,6 +84,7 @@ interface BuildWorkspaceDesktopTabActionsInput {
|
||||
tabCount: number;
|
||||
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
|
||||
onCopyAgentId: (agentId: string) => Promise<void> | void;
|
||||
onCopyFilePath: (path: string) => Promise<void> | void;
|
||||
onReloadAgent: (agentId: string) => Promise<void> | void;
|
||||
onRenameTab: (tab: WorkspaceTabDescriptor) => void;
|
||||
onCloseTab: (tabId: string) => Promise<void> | void;
|
||||
@@ -148,6 +152,7 @@ export function buildWorkspaceTabMenuEntries(
|
||||
menuTestIDBase,
|
||||
onCopyResumeCommand,
|
||||
onCopyAgentId,
|
||||
onCopyFilePath,
|
||||
onReloadAgent,
|
||||
onRenameTab,
|
||||
onCloseTab,
|
||||
@@ -186,6 +191,20 @@ export function buildWorkspaceTabMenuEntries(
|
||||
});
|
||||
}
|
||||
|
||||
if (tab.target.kind === "file") {
|
||||
const filePath = tab.target.path;
|
||||
entries.push({
|
||||
kind: "item",
|
||||
key: "copy-file-path",
|
||||
label: labels.copyFilePath,
|
||||
icon: "copy",
|
||||
testID: `${menuTestIDBase}-copy-file-path`,
|
||||
onSelect: () => {
|
||||
void onCopyFilePath(filePath);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (tab.target.kind === "agent" || tab.target.kind === "terminal") {
|
||||
entries.push({
|
||||
kind: "item",
|
||||
@@ -278,6 +297,7 @@ export function buildWorkspaceDesktopTabActions(
|
||||
menuTestIDBase: contextMenuTestId,
|
||||
onCopyResumeCommand: input.onCopyResumeCommand,
|
||||
onCopyAgentId: input.onCopyAgentId,
|
||||
onCopyFilePath: input.onCopyFilePath,
|
||||
onReloadAgent: input.onReloadAgent,
|
||||
onRenameTab: input.onRenameTab,
|
||||
onCloseTab: input.onCloseTab,
|
||||
|
||||
Reference in New Issue
Block a user