+ {collapsePreview && (
+
+ )}
{workareaTabs.length > 0 ? (
store.setChatActiveTab(value)}
onCloseTab={(id) => store.closeChatTab(id)}
- onOpenArtifact={(url, title) => store.openArtifactTab(url, title)}
+ onOpenArtifact={(url, title) => {
+ store.showWorkarea();
+ store.openArtifactTab(url, title);
+ }}
onSendPrompt={(content) => store.recordSessionPrompt(content)}
onError={(message) => notify(message, "error")}
headerAction={
diff --git a/src/routes/v2/pages/Tangent/components/ResourcesWindowContent.test.tsx b/src/routes/v2/pages/Tangent/components/ResourcesWindowContent.test.tsx
index 5b8c4803f5..60a1ff7b8e 100644
--- a/src/routes/v2/pages/Tangent/components/ResourcesWindowContent.test.tsx
+++ b/src/routes/v2/pages/Tangent/components/ResourcesWindowContent.test.tsx
@@ -10,11 +10,16 @@ import { useRemotePipelineNames } from "@/services/projects/useRemotePipelineNam
import { ResourcesWindowContent } from "./ResourcesWindowContent";
const openWorkareaTarget = vi.fn();
+const showWorkarea = vi.fn();
const deleteResource = vi.fn();
const saveInstructions = vi.fn();
vi.mock("@/routes/v2/pages/Tangent/context/TangentProjectContext", () => ({
- useTangentProject: () => ({ projectId: "project-1", openWorkareaTarget }),
+ useTangentProject: () => ({
+ projectId: "project-1",
+ openWorkareaTarget,
+ showWorkarea,
+ }),
}));
vi.mock("@/services/projects/useProjectResources", () => ({
@@ -194,6 +199,7 @@ describe("ResourcesWindowContent", () => {
await user.click(screen.getByTestId("open-resource-r-2"));
+ expect(showWorkarea).toHaveBeenCalled();
expect(openWorkareaTarget).toHaveBeenCalledWith(
{ type: "document", identity: "id/r-2" },
"Model card",
@@ -220,6 +226,7 @@ describe("ResourcesWindowContent", () => {
await user.click(screen.getByTestId("open-resource-r-5"));
+ expect(showWorkarea).toHaveBeenCalled();
expect(openWorkareaTarget).toHaveBeenCalledWith(
{ type: "document", identity: "id/r-5" },
"Loose note",
@@ -242,6 +249,7 @@ describe("ResourcesWindowContent", () => {
expect(screen.getByText("Hello World")).toBeInTheDocument();
expect(screen.getByText("Pipeline")).toBeInTheDocument();
await userEvent.setup().click(screen.getByTestId("open-resource-r-5"));
+ expect(showWorkarea).toHaveBeenCalled();
expect(openWorkareaTarget).toHaveBeenCalledWith(
{ type: "pipeline", identity: "remote/uuid-1" },
"Hello World",
@@ -263,6 +271,7 @@ describe("ResourcesWindowContent", () => {
screen.queryByText("Make a basic hello world pipeline"),
).not.toBeInTheDocument();
await userEvent.setup().click(screen.getByTestId("open-resource-r-5"));
+ expect(showWorkarea).toHaveBeenCalled();
expect(openWorkareaTarget).toHaveBeenCalledWith(
{ type: "pipeline", identity: "remote/uuid-1" },
"Workspace secret hello world",
@@ -309,6 +318,7 @@ describe("ResourcesWindowContent", () => {
await user.click(screen.getByTestId("open-resource-r-7"));
+ expect(showWorkarea).toHaveBeenCalled();
expect(openWorkareaTarget).toHaveBeenCalledWith(
{ type: "pipeline", identity: "name/Churn model" },
"Churn model",
diff --git a/src/routes/v2/pages/Tangent/components/ResourcesWindowContent.tsx b/src/routes/v2/pages/Tangent/components/ResourcesWindowContent.tsx
index 3d09873561..84bdbea220 100644
--- a/src/routes/v2/pages/Tangent/components/ResourcesWindowContent.tsx
+++ b/src/routes/v2/pages/Tangent/components/ResourcesWindowContent.tsx
@@ -176,6 +176,7 @@ export function ResourcesWindowContent() {
async function handleOpenResource(resource: ProjectResourceItem) {
if (!resource.target) return;
try {
+ store.showWorkarea();
await store.openWorkareaTarget(resource.target, resource.name);
} catch (error) {
notify(getErrorMessage(error), "error");
diff --git a/src/routes/v2/pages/Tangent/components/RunsWindowContent.test.tsx b/src/routes/v2/pages/Tangent/components/RunsWindowContent.test.tsx
index 0883f07689..5373a2a97a 100644
--- a/src/routes/v2/pages/Tangent/components/RunsWindowContent.test.tsx
+++ b/src/routes/v2/pages/Tangent/components/RunsWindowContent.test.tsx
@@ -11,9 +11,14 @@ import {
import { RunsWindowContent } from "./RunsWindowContent";
const openWorkareaTarget = vi.fn();
+const showWorkarea = vi.fn();
vi.mock("@/routes/v2/pages/Tangent/context/TangentProjectContext", () => ({
- useTangentProject: () => ({ projectId: "project-1", openWorkareaTarget }),
+ useTangentProject: () => ({
+ projectId: "project-1",
+ openWorkareaTarget,
+ showWorkarea,
+ }),
}));
vi.mock("@/services/projects/useProjectRuns", () => ({
@@ -79,6 +84,7 @@ describe("RunsWindowContent", () => {
render();
await user.click(screen.getByTestId("open-run-run-7"));
+ expect(showWorkarea).toHaveBeenCalled();
expect(openWorkareaTarget).toHaveBeenCalledWith(
{ type: "run", identity: "id/run-7" },
"Churn model",
@@ -101,6 +107,7 @@ describe("RunsWindowContent", () => {
expect(window.open).toHaveBeenCalledWith("/runs/run-7", "_blank");
expect(openWorkareaTarget).not.toHaveBeenCalled();
+ expect(showWorkarea).not.toHaveBeenCalled();
},
);
@@ -111,6 +118,7 @@ describe("RunsWindowContent", () => {
render();
await user.click(screen.getByTestId("open-run-run-8"));
+ expect(showWorkarea).toHaveBeenCalled();
expect(openWorkareaTarget).toHaveBeenCalledWith(
{ type: "run", identity: "id/run-8" },
"Unnamed pipeline",
diff --git a/src/routes/v2/pages/Tangent/components/RunsWindowContent.tsx b/src/routes/v2/pages/Tangent/components/RunsWindowContent.tsx
index 74f947315a..13849439b2 100644
--- a/src/routes/v2/pages/Tangent/components/RunsWindowContent.tsx
+++ b/src/routes/v2/pages/Tangent/components/RunsWindowContent.tsx
@@ -38,6 +38,7 @@ export function RunsWindowContent() {
}
const title = run.pipelineName ?? UNNAMED_PIPELINE;
try {
+ store.showWorkarea();
await store.openWorkareaTarget(
{ type: "run", identity: idIdentity(run.id) },
title,
diff --git a/src/routes/v2/pages/Tangent/components/TangentProjectWorkspace.tsx b/src/routes/v2/pages/Tangent/components/TangentProjectWorkspace.tsx
index e7d4617603..2717d765dd 100644
--- a/src/routes/v2/pages/Tangent/components/TangentProjectWorkspace.tsx
+++ b/src/routes/v2/pages/Tangent/components/TangentProjectWorkspace.tsx
@@ -1,41 +1,44 @@
import { observer } from "mobx-react-lite";
+import { useState } from "react";
import { InlineStack } from "@/components/ui/layout";
-import { VerticalResizeHandle } from "@/components/ui/resize-handle";
+import { cn } from "@/lib/utils";
import { useTangentProject } from "@/routes/v2/pages/Tangent/context/TangentProjectContext";
import { useChatPaneWidth } from "@/routes/v2/pages/Tangent/hooks/useChatPaneWidth";
import { useNarrowLayoutDock } from "@/routes/v2/pages/Tangent/hooks/useNarrowLayoutDock";
import { useTangentProjectWindows } from "@/routes/v2/pages/Tangent/hooks/useTangentProjectWindows";
import { useTangentSessionParam } from "@/routes/v2/pages/Tangent/hooks/useTangentSessionParam";
-import { MIN_CHAT_WIDTH } from "@/routes/v2/pages/Tangent/layout";
import { useSharedStores } from "@/routes/v2/shared/store/SharedStoreContext";
import { DockArea } from "@/routes/v2/shared/windows/DockArea";
import { COLLAPSED_DOCK_AREA_WIDTH } from "@/routes/v2/shared/windows/types";
import { WindowContainer } from "@/routes/v2/shared/windows/WindowContainer";
import { useWindowPersistence } from "@/routes/v2/shared/windows/windowPersistence";
+import { CollapsedWorkareaRail } from "./CollapsedWorkareaRail";
import { DynamicWorkarea } from "./DynamicWorkarea";
import { ProjectChatArea } from "./ProjectChatArea";
import { ProjectHeader } from "./ProjectHeader";
import { TangentProjectAgentProvider } from "./TangentProjectAgentProvider";
+import { WorkareaDivider } from "./WorkareaDivider";
export const TangentProjectWorkspace = observer(
function TangentProjectWorkspace() {
useWindowPersistence("tangent-project");
useTangentProjectWindows();
- useNarrowLayoutDock();
const store = useTangentProject();
+ useNarrowLayoutDock(store.workareaCollapsed);
useTangentSessionParam(store);
const { windows } = useSharedStores();
const dock = windows.getDockAreaConfig("left");
+ const dockWidth = dock.collapsed ? COLLAPSED_DOCK_AREA_WIDTH : dock.width;
const {
rowRef,
+ rowWidth,
width: chatWidth,
maxWidth: maxChatWidth,
requestWidth,
- } = useChatPaneWidth(
- dock.collapsed ? COLLAPSED_DOCK_AREA_WIDTH : dock.width,
- );
+ } = useChatPaneWidth(dockWidth);
+ const [collapsePreview, setCollapsePreview] = useState(false);
return (
@@ -43,25 +46,31 @@ export const TangentProjectWorkspace = observer(
-
+ {!store.workareaCollapsed && (
+
0}
+ maxChatWidth={maxChatWidth}
+ onResize={requestWidth}
+ onCollapsePreview={setCollapsePreview}
+ />
+ )}
-
+
+ {store.workareaCollapsed && }
diff --git a/src/routes/v2/pages/Tangent/components/WorkareaDivider.test.tsx b/src/routes/v2/pages/Tangent/components/WorkareaDivider.test.tsx
new file mode 100644
index 0000000000..bc8bf8c2f5
--- /dev/null
+++ b/src/routes/v2/pages/Tangent/components/WorkareaDivider.test.tsx
@@ -0,0 +1,64 @@
+import { fireEvent, render, screen } from "@testing-library/react";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+
+import { TangentProjectStore } from "@/routes/v2/pages/Tangent/store/TangentProjectStore";
+
+import { WorkareaDivider } from "./WorkareaDivider";
+
+let store: TangentProjectStore;
+const resize = vi.fn();
+
+vi.mock("@/routes/v2/pages/Tangent/context/TangentProjectContext", () => ({
+ useTangentProject: () => store,
+}));
+
+const MAX_CHAT = 760;
+
+function renderDivider() {
+ render(
+