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
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
type ActiveWorkspaceSelection,
useActiveWorkspaceSelection,
} from "@/stores/navigation-active-workspace-store";
import type { WorkspaceTabTarget } from "@/stores/workspace-tabs-store";
import type { WorkspaceTabTargetInput } from "@/stores/workspace-tabs-store";
import { WorkspaceScreen } from "@/screens/workspace/workspace-screen";
import { useWorkspaceLayoutStoreHydrated } from "@/stores/workspace-layout-store";
import {
Expand All @@ -29,7 +29,7 @@ function getParamValue(value: string | string[] | undefined): string {
return "";
}

function getOpenIntentTarget(openIntent: WorkspaceOpenIntent): WorkspaceTabTarget {
function getOpenIntentTarget(openIntent: WorkspaceOpenIntent): WorkspaceTabTargetInput {
if (openIntent.kind === "agent") {
return { kind: "agent", agentId: openIntent.agentId };
}
Expand Down
11 changes: 7 additions & 4 deletions packages/app/src/components/file-pane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import { persistAttachmentFromBytes } from "@/attachments/service";
import { createPreviewAttachmentId, getFileNameFromPath } from "@/attachments/utils";
import { explorerFileFromReadResult } from "@/file-explorer/read-result";
import { resolveFilePreviewReadTarget } from "@/file-explorer/preview-target";
import type { WorkspaceFileLocation } from "@/workspace/file-open";
import type { WorkspaceFileTabTarget } from "@/workspace/file-open";

interface CodeLineProps {
tokens: HighlightToken[];
Expand All @@ -55,7 +55,7 @@ interface FilePreviewBodyProps {
isLoading: boolean;
showDesktopWebScrollbar: boolean;
isMobile: boolean;
location: WorkspaceFileLocation;
location: WorkspaceFileTabTarget;
imagePreviewUri: string | null;
}

Expand Down Expand Up @@ -508,7 +508,10 @@ function FilePreviewBody({
const markdownParser = useMemo(() => MarkdownIt({ typographer: true, linkify: true }), []);
const markdownRules = useMemo(() => createFilePreviewMarkdownRules(), []);
const isMarkdownFile =
preview?.kind === "text" && isRenderedMarkdownFile(filePath) && !location.lineStart;
preview?.kind === "text" &&
isRenderedMarkdownFile(filePath) &&
!location.lineStart &&
location.renderMode !== "source";

const previewScrollRef = useRef<RNScrollView>(null);
const webScrollbarStyle = useWebScrollbarStyle();
Expand Down Expand Up @@ -702,7 +705,7 @@ export function FilePane({
}: {
serverId: string;
workspaceRoot: string;
location: WorkspaceFileLocation;
location: WorkspaceFileTabTarget;
}) {
const isMobile = useIsCompactFormFactor();
const showDesktopWebScrollbar = isWeb && !isMobile;
Expand Down
8 changes: 8 additions & 0 deletions packages/app/src/components/split-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ interface SplitContainerProps {
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
onCopyAgentId: (agentId: string) => Promise<void> | void;
onReloadAgent: (agentId: string) => Promise<void> | void;
onToggleMarkdownSource: (tab: WorkspaceTabDescriptor) => void;
onRenameTab: (tab: WorkspaceTabDescriptor) => void;
onCloseTabsToLeft: (tabId: string, paneTabs: WorkspaceTabDescriptor[]) => Promise<void> | void;
onCloseTabsToRight: (tabId: string, paneTabs: WorkspaceTabDescriptor[]) => Promise<void> | void;
Expand Down Expand Up @@ -364,6 +365,7 @@ export function SplitContainer({
onCopyResumeCommand,
onCopyAgentId,
onReloadAgent,
onToggleMarkdownSource,
onRenameTab,
onCloseTabsToLeft,
onCloseTabsToRight,
Expand Down Expand Up @@ -580,6 +582,7 @@ export function SplitContainer({
onCopyResumeCommand={onCopyResumeCommand}
onCopyAgentId={onCopyAgentId}
onReloadAgent={onReloadAgent}
onToggleMarkdownSource={onToggleMarkdownSource}
onRenameTab={onRenameTab}
onCloseTabsToLeft={onCloseTabsToLeft}
onCloseTabsToRight={onCloseTabsToRight}
Expand Down Expand Up @@ -720,6 +723,7 @@ function SplitNodeView({
onCopyResumeCommand,
onCopyAgentId,
onReloadAgent,
onToggleMarkdownSource,
onRenameTab,
onCloseTabsToLeft,
onCloseTabsToRight,
Expand Down Expand Up @@ -772,6 +776,7 @@ function SplitNodeView({
onCopyResumeCommand={onCopyResumeCommand}
onCopyAgentId={onCopyAgentId}
onReloadAgent={onReloadAgent}
onToggleMarkdownSource={onToggleMarkdownSource}
onRenameTab={onRenameTab}
onCloseTabsToLeft={onCloseTabsToLeft}
onCloseTabsToRight={onCloseTabsToRight}
Expand Down Expand Up @@ -817,6 +822,7 @@ function SplitNodeView({
onCopyResumeCommand={onCopyResumeCommand}
onCopyAgentId={onCopyAgentId}
onReloadAgent={onReloadAgent}
onToggleMarkdownSource={onToggleMarkdownSource}
onRenameTab={onRenameTab}
onCloseTabsToLeft={onCloseTabsToLeft}
onCloseTabsToRight={onCloseTabsToRight}
Expand Down Expand Up @@ -868,6 +874,7 @@ function SplitPaneView({
onCopyResumeCommand,
onCopyAgentId,
onReloadAgent,
onToggleMarkdownSource,
onRenameTab,
onCloseTabsToLeft,
onCloseTabsToRight,
Expand Down Expand Up @@ -1009,6 +1016,7 @@ function SplitPaneView({
onCopyResumeCommand={onCopyResumeCommand}
onCopyAgentId={onCopyAgentId}
onReloadAgent={onReloadAgent}
onToggleMarkdownSource={onToggleMarkdownSource}
onRenameTab={onRenameTab}
onCloseTabsToLeft={handleCloseTabsToLeft}
onCloseTabsToRight={handleCloseTabsToRight}
Expand Down
5 changes: 4 additions & 1 deletion packages/app/src/panels/file-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import invariant from "tiny-invariant";
import { FilePane } from "@/components/file-pane";
import { usePaneContext } from "@/panels/pane-context";
import type { PanelRegistration } from "@/panels/panel-registry";
import type { WorkspaceTabTarget } from "@/stores/workspace-tabs-store";
import { useWorkspaceExecutionAuthority } from "@/stores/session-store-hooks";

const CENTERED_PADDED_STYLE = {
Expand All @@ -13,7 +14,9 @@ const CENTERED_PADDED_STYLE = {
padding: 16,
} as const;

function useFilePanelDescriptor(target: { kind: "file"; path: string }) {
type FilePanelTarget = Extract<WorkspaceTabTarget, { kind: "file" }>;

function useFilePanelDescriptor(target: FilePanelTarget) {
const fileName = target.path.split("/").findLast(Boolean) ?? target.path;
return {
label: fileName,
Expand Down
14 changes: 10 additions & 4 deletions packages/app/src/screens/workspace/workspace-bulk-close.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function makeFileTab(path: string): WorkspaceTabDescriptor {
key: `file_${path}`,
tabId: `file_${path}`,
kind: "file",
target: { kind: "file", path },
target: { kind: "file", path, renderMode: "preview" },
};
}

Expand All @@ -47,7 +47,7 @@ describe("workspace bulk close helpers", () => {
otherTabs: [
{
tabId: "file_/repo/README.md",
target: { kind: "file", path: "/repo/README.md" },
target: { kind: "file", path: "/repo/README.md", renderMode: "preview" },
},
],
});
Expand Down Expand Up @@ -124,7 +124,10 @@ describe("workspace bulk close helpers", () => {
{ tabId: "agent_a1", target: { kind: "agent", agentId: "a1" } },
{ tabId: "terminal_t1", target: { kind: "terminal", terminalId: "t1" } },
{ tabId: "terminal_t2", target: { kind: "terminal", terminalId: "t2" } },
{ tabId: "file_/repo/README.md", target: { kind: "file", path: "/repo/README.md" } },
{
tabId: "file_/repo/README.md",
target: { kind: "file", path: "/repo/README.md", renderMode: "preview" },
},
]);
});

Expand Down Expand Up @@ -163,7 +166,10 @@ describe("workspace bulk close helpers", () => {
expect(cleanupCalls).toEqual([
{ tabId: "agent_a1", target: { kind: "agent", agentId: "a1" } },
{ tabId: "terminal_t1", target: { kind: "terminal", terminalId: "t1" } },
{ tabId: "file_/repo/README.md", target: { kind: "file", path: "/repo/README.md" } },
{
tabId: "file_/repo/README.md",
target: { kind: "file", path: "/repo/README.md", renderMode: "preview" },
},
]);
});
});
16 changes: 16 additions & 0 deletions packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import {
CopyX,
ArrowLeftToLine,
ArrowRightToLine,
Code2,
Columns2,
Copy,
Eye,
Pencil,
RotateCw,
Rows2,
Expand Down Expand Up @@ -69,6 +71,8 @@ const LOADING_TAB_LABEL_SKELETON_WIDTH = 80;

const ThemedActivityIndicator = withUnistyles(ActivityIndicator);
const ThemedX = withUnistyles(X);
const ThemedCode2 = withUnistyles(Code2);
const ThemedEye = withUnistyles(Eye);
const ThemedCopy = withUnistyles(Copy);
const ThemedRotateCw = withUnistyles(RotateCw);
const ThemedArrowLeftToLine = withUnistyles(ArrowLeftToLine);
Expand Down Expand Up @@ -97,6 +101,10 @@ function TabContextMenuItem({
switch (entry.icon) {
case "copy":
return <ThemedCopy size={16} uniProps={mutedColorMapping} />;
case "code":
return <ThemedCode2 size={16} uniProps={mutedColorMapping} />;
case "eye":
return <ThemedEye size={16} uniProps={mutedColorMapping} />;
case "rotate-cw":
return <ThemedRotateCw size={16} uniProps={mutedColorMapping} />;
case "arrow-left-to-line":
Expand Down Expand Up @@ -155,6 +163,7 @@ interface WorkspaceDesktopTabsRowProps {
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
onCopyAgentId: (agentId: string) => Promise<void> | void;
onReloadAgent: (agentId: string) => Promise<void> | void;
onToggleMarkdownSource: (tab: WorkspaceTabDescriptor) => void;
onRenameTab: (tab: WorkspaceTabDescriptor) => void;
onCloseTabsToLeft: (tabId: string) => Promise<void> | void;
onCloseTabsToRight: (tabId: string) => Promise<void> | void;
Expand Down Expand Up @@ -466,6 +475,7 @@ export function WorkspaceDesktopTabsRow({
onCopyResumeCommand,
onCopyAgentId,
onReloadAgent,
onToggleMarkdownSource,
onRenameTab,
onCloseTabsToLeft,
onCloseTabsToRight,
Expand Down Expand Up @@ -599,6 +609,7 @@ export function WorkspaceDesktopTabsRow({
onCopyResumeCommand={onCopyResumeCommand}
onCopyAgentId={onCopyAgentId}
onReloadAgent={onReloadAgent}
onToggleMarkdownSource={onToggleMarkdownSource}
onRenameTab={onRenameTab}
onCloseTabsToLeft={onCloseTabsToLeft}
onCloseTabsToRight={onCloseTabsToRight}
Expand Down Expand Up @@ -630,6 +641,7 @@ export function WorkspaceDesktopTabsRow({
onCopyResumeCommand,
onNavigateTab,
onReloadAgent,
onToggleMarkdownSource,
onRenameTab,
setHoveredCloseTabKey,
tabDropPreviewIndex,
Expand Down Expand Up @@ -792,6 +804,7 @@ function ResolvedDesktopTabChip({
onCopyResumeCommand,
onCopyAgentId,
onReloadAgent,
onToggleMarkdownSource,
onRenameTab,
onCloseTabsToLeft,
onCloseTabsToRight,
Expand All @@ -816,6 +829,7 @@ function ResolvedDesktopTabChip({
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
onCopyAgentId: (agentId: string) => Promise<void> | void;
onReloadAgent: (agentId: string) => Promise<void> | void;
onToggleMarkdownSource: (tab: WorkspaceTabDescriptor) => void;
onRenameTab: (tab: WorkspaceTabDescriptor) => void;
onCloseTabsToLeft: (tabId: string) => Promise<void> | void;
onCloseTabsToRight: (tabId: string) => Promise<void> | void;
Expand All @@ -839,6 +853,7 @@ function ResolvedDesktopTabChip({
onCopyResumeCommand,
onCopyAgentId,
onReloadAgent,
onToggleMarkdownSource,
onRenameTab,
onCloseTab,
onCloseTabsToLeft,
Expand All @@ -855,6 +870,7 @@ function ResolvedDesktopTabChip({
onCopyAgentId,
onCopyResumeCommand,
onReloadAgent,
onToggleMarkdownSource,
onRenameTab,
tabCount,
],
Expand Down
14 changes: 10 additions & 4 deletions packages/app/src/screens/workspace/workspace-pane-state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
} from "@/screens/workspace/workspace-pane-state";
import type { WorkspaceLayout } from "@/stores/workspace-layout-store";
import type { WorkspaceTab } from "@/stores/workspace-tabs-store";
import { createWorkspaceFileTabTarget } from "@/workspace/file-open";

function createTab(tabId: string, target: WorkspaceTab["target"]): WorkspaceTab {
return {
Expand All @@ -15,11 +16,15 @@ function createTab(tabId: string, target: WorkspaceTab["target"]): WorkspaceTab
};
}

function fileTarget(path: string) {
return createWorkspaceFileTabTarget({ path });
}

describe("workspace-pane-state", () => {
it("selects the focused pane and keeps its tab order", () => {
const tabs: WorkspaceTab[] = [
createTab("agent_agent-a", { kind: "agent", agentId: "agent-a" }),
createTab("file_/repo/README.md", { kind: "file", path: "/repo/README.md" }),
createTab("file_/repo/README.md", fileTarget("/repo/README.md")),
createTab("terminal_term-1", { kind: "terminal", terminalId: "term-1" }),
];
const layout: WorkspaceLayout = {
Expand Down Expand Up @@ -90,7 +95,7 @@ describe("workspace-pane-state", () => {
};
const tabs: WorkspaceTab[] = [
createTab("agent_agent-a", { kind: "agent", agentId: "agent-a" }),
createTab("file_/repo/README.md", { kind: "file", path: "/repo/README.md" }),
createTab("file_/repo/README.md", fileTarget("/repo/README.md")),
];

const state = deriveWorkspacePaneState({
Expand All @@ -103,6 +108,7 @@ describe("workspace-pane-state", () => {
expect(state.activeTab?.descriptor.target).toEqual({
kind: "file",
path: "/repo/README.md",
renderMode: "preview",
});
});

Expand All @@ -118,7 +124,7 @@ describe("workspace-pane-state", () => {
},
focusedPaneId: "main",
};
const tabs = [createTab("file_/repo/README.md", { kind: "file", path: "/repo/README.md" })];
const tabs = [createTab("file_/repo/README.md", fileTarget("/repo/README.md"))];

expect(
resolveSideFileOpenPlacement({
Expand All @@ -142,7 +148,7 @@ describe("workspace-pane-state", () => {
},
focusedPaneId: "main",
};
const tabs = [createTab("file_/repo/README.md", { kind: "file", path: "/repo/README.md" })];
const tabs = [createTab("file_/repo/README.md", fileTarget("/repo/README.md"))];

expect(
resolveSideFileOpenPlacement({
Expand Down
16 changes: 10 additions & 6 deletions packages/app/src/screens/workspace/workspace-pane-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
type SplitPane,
type WorkspaceLayout,
} from "@/stores/workspace-layout-store";
import type { WorkspaceTab, WorkspaceTabTarget } from "@/stores/workspace-tabs-store";
import type { WorkspaceTab, WorkspaceTabTargetInput } from "@/stores/workspace-tabs-store";
import type { WorkspaceTabDescriptor } from "@/screens/workspace/workspace-tabs-types";
import {
buildDeterministicWorkspaceTabId,
Expand Down Expand Up @@ -112,7 +112,7 @@ function getActiveTabId(input: {
tabs: WorkspaceDerivedTab[];
openTabIds: Set<string>;
focusedTabId?: string | null;
preferredTarget?: WorkspaceTabTarget | null;
preferredTarget?: WorkspaceTabTargetInput | null;
}): string | null {
const focusedTabId = trimNonEmpty(input.focusedTabId);
const preferredTarget = normalizeWorkspaceTabTarget(input.preferredTarget ?? null);
Expand Down Expand Up @@ -163,7 +163,7 @@ export function deriveWorkspacePaneState(input: {
paneId?: string | null;
tabs: WorkspaceTab[];
focusedTabId?: string | null;
preferredTarget?: WorkspaceTabTarget | null;
preferredTarget?: WorkspaceTabTargetInput | null;
}): WorkspacePaneState {
const pane = getPane({
layout: input.layout ?? null,
Expand Down Expand Up @@ -207,11 +207,15 @@ export function resolveSideFileOpenPlacement(input: {
layout?: WorkspaceLayout | null;
sourcePaneId?: string | null;
tabs: WorkspaceTab[];
target: WorkspaceTabTarget;
target: WorkspaceTabTargetInput;
}): WorkspaceSideFileOpenPlacement {
const targetTabId = buildDeterministicWorkspaceTabId(input.target);
const target = normalizeWorkspaceTabTarget(input.target);
if (!target) {
return { kind: "split-side-pane", paneId: trimNonEmpty(input.sourcePaneId) ?? "" };
}
const targetTabId = buildDeterministicWorkspaceTabId(target);
const existingTab = input.tabs.find(
(tab) => tab.tabId === targetTabId || workspaceTabTargetsEqual(tab.target, input.target),
(tab) => tab.tabId === targetTabId || workspaceTabTargetsEqual(tab.target, target),
);
if (existingTab) {
return { kind: "open-in-source" };
Expand Down
Loading
Loading