Skip to content
Merged
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
8 changes: 4 additions & 4 deletions apps/desktop/src/renderer/components/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ const preloadLanesPage = lanesRoute.preload;
const filesRoute = createPreloadableRoute<{ active?: boolean }>(() =>
import("../files/FilesTab").then((m) => ({ default: m.FilesTab }))
);
const FilesPage = filesRoute.Component;
const preloadFilesPage = filesRoute.preload;
const FilesTab = filesRoute.Component;
const preloadFilesTab = filesRoute.preload;
const workRoute = createPreloadableRoute<{ active?: boolean }>(() =>
import("../terminals/TerminalsPage").then((m) => ({ default: m.TerminalsPage }))
);
Expand Down Expand Up @@ -425,7 +425,7 @@ function ProjectRouteContent({ active, route }: { active: boolean; route: string
<Route path="/glossary" element={<PageErrorBoundary><GlossaryPage /></PageErrorBoundary>} />
<Route path="/files" element={
<PageErrorBoundary>
<React.Suspense fallback={LazyFallback}>{React.createElement(FilesPage as React.ComponentType<{ active?: boolean }>, routeProps)}</React.Suspense>
<React.Suspense fallback={LazyFallback}>{React.createElement(FilesTab as React.ComponentType<{ active?: boolean }>, routeProps)}</React.Suspense>
</PageErrorBoundary>
} />
<Route path="/graph" element={
Expand Down Expand Up @@ -614,7 +614,7 @@ function ProjectTabHost() {
const preload = () => {
void preloadTerminalsPage().catch(() => undefined);
void preloadLanesPage().catch(() => undefined);
void preloadFilesPage().catch(() => undefined);
void preloadFilesTab().catch(() => undefined);
void preloadCtoPage().catch(() => undefined);
};
const idleWindow = window as Window & {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,23 +117,6 @@ vi.mock("../terminals/TerminalsPage", async () => {
};
});

vi.mock("../files/FilesPage", async () => {
const Router = await vi.importActual("react-router-dom") as typeof RouterNamespace;

return {
FilesPage: () => {
const navigate = Router.useNavigate();
return (
<div data-testid="files-page">
<button type="button" onClick={() => navigate("/work")}>
Open work
</button>
</div>
);
},
};
});

vi.mock("../files/FilesTab", async () => {
const Router = await vi.importActual("react-router-dom") as typeof RouterNamespace;

Expand Down
294 changes: 206 additions & 88 deletions apps/desktop/src/renderer/components/app/LinearIssueBrowser.tsx

Large diffs are not rendered by default.

14 changes: 13 additions & 1 deletion apps/desktop/src/renderer/components/app/LinearPaneModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { createPortal } from "react-dom";
import { CircleNotch, X } from "@phosphor-icons/react";

import type { CtoLinearQuickView } from "../../../shared/types";
import {
ADE_BROWSER_VIEW_OCCLUSION_END_EVENT,
ADE_BROWSER_VIEW_OCCLUSION_START_EVENT,
} from "../../lib/workSidebarBrowserResize";
import { LinearMark, LINEAR_BRAND } from "../lanes/linearBrand";

/**
Expand Down Expand Up @@ -34,6 +38,14 @@ export function LinearPaneModal({
}) {
const popoverRef = useRef<HTMLDivElement | null>(null);

useEffect(() => {
if (!open || typeof window === "undefined") return undefined;
window.dispatchEvent(new Event(ADE_BROWSER_VIEW_OCCLUSION_START_EVENT));
return () => {
window.dispatchEvent(new Event(ADE_BROWSER_VIEW_OCCLUSION_END_EVENT));
};
}, [open]);

useEffect(() => {
if (!open) return;
const onDown = (event: MouseEvent) => {
Expand Down Expand Up @@ -73,7 +85,7 @@ export function LinearPaneModal({
role="dialog"
aria-modal="true"
aria-label={ariaLabel}
className="fixed left-1/2 top-1/2 z-[9999] flex h-[min(900px,calc(100dvh-28px))] w-[min(1380px,calc(100vw-28px))] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl border bg-[color:var(--ade-shell-surface,#121019)] text-fg shadow-2xl shadow-black/50"
className="fixed left-1/2 top-1/2 z-[9999] flex h-[min(940px,calc(100dvh-28px))] w-[min(1760px,calc(100vw-28px))] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl border bg-[color:var(--ade-shell-surface,#121019)] text-fg shadow-2xl shadow-black/50"
style={{
borderColor: "rgba(123, 138, 240, 0.55)",
boxShadow: "0 24px 70px rgba(0, 0, 0, 0.58), 0 0 0 1px rgba(123, 138, 240, 0.18)",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import {
subscribeLinearIssueQuickViewRequests,
type LinearIssueQuickViewRequest,
} from "../../lib/linearIssueQuickViewNavigation";
import {
ADE_BROWSER_VIEW_OCCLUSION_END_EVENT,
ADE_BROWSER_VIEW_OCCLUSION_START_EVENT,
} from "../../lib/workSidebarBrowserResize";
import { cn } from "../ui/cn";
import { linearIssueLaneName } from "../../../shared/linearIssueBranch";
import { LinearMark, LINEAR_BRAND } from "../lanes/linearBrand";
Expand Down Expand Up @@ -123,6 +127,7 @@ export function LinearQuickViewButton({
const buttonRef = useRef<HTMLButtonElement | null>(null);
const popoverRef = useRef<HTMLDivElement | null>(null);
const cachedQuickViewRef = useRef<CtoLinearQuickView | null>(null);
const occludesNativeBrowser = open || batchModalOpen;
// Remembers each issue's chosen config so "Retry failed" reuses the same model.
const batchConfigByIssueRef = useRef<Map<string, BatchLaunchIssueConfig>>(new Map());

Expand Down Expand Up @@ -167,6 +172,14 @@ export function LinearQuickViewButton({
return subscribeLinearIssueQuickViewRequests(handleQuickViewRequest);
}, [handleQuickViewRequest, variant]);

useEffect(() => {
if (!occludesNativeBrowser || typeof window === "undefined") return undefined;
window.dispatchEvent(new Event(ADE_BROWSER_VIEW_OCCLUSION_START_EVENT));
return () => {
window.dispatchEvent(new Event(ADE_BROWSER_VIEW_OCCLUSION_END_EVENT));
};
}, [occludesNativeBrowser]);

useEffect(() => {
if (variant !== "icon") return;
let cancelled = false;
Expand Down Expand Up @@ -627,7 +640,7 @@ export function LinearQuickViewButton({
role="dialog"
aria-modal="true"
aria-label="Linear quick view"
className="fixed left-1/2 top-1/2 z-[9999] flex h-[min(900px,calc(100dvh-28px))] w-[min(1380px,calc(100vw-28px))] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl border bg-[color:var(--ade-shell-surface,#121019)] text-fg shadow-2xl shadow-black/50"
className="fixed left-1/2 top-1/2 z-[9999] flex h-[min(940px,calc(100dvh-28px))] w-[min(1760px,calc(100vw-28px))] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl border bg-[color:var(--ade-shell-surface,#121019)] text-fg shadow-2xl shadow-black/50"
style={{
borderColor: "rgba(123, 138, 240, 0.55)",
boxShadow: "0 24px 70px rgba(0, 0, 0, 0.58), 0 0 0 1px rgba(123, 138, 240, 0.18)",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/re
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { ChatBuiltInBrowserPanel } from "./ChatBuiltInBrowserPanel";
import {
ADE_BROWSER_VIEW_OCCLUSION_END_EVENT,
ADE_BROWSER_VIEW_OCCLUSION_START_EVENT,
ADE_WORK_SIDEBAR_BROWSER_RESIZE_END_EVENT,
ADE_WORK_SIDEBAR_BROWSER_RESIZE_START_EVENT,
} from "../../lib/workSidebarBrowserResize";
Expand Down Expand Up @@ -185,6 +187,49 @@ describe("ChatBuiltInBrowserPanel", () => {
});
});

it("keeps the native browser hidden while overlay occlusions are active", async () => {
const { api } = installBrowserApi();

render(<ChatBuiltInBrowserPanel sessionId="chat-1" />);

await waitFor(() => {
expect(api.setBounds).toHaveBeenCalledWith(expect.objectContaining({
width: 640,
height: 360,
visible: true,
}));
});

window.dispatchEvent(new Event(ADE_BROWSER_VIEW_OCCLUSION_START_EVENT));

await waitFor(() => {
expect(api.setBounds).toHaveBeenLastCalledWith(expect.objectContaining({
width: 640,
height: 360,
visible: false,
}));
});

window.dispatchEvent(new Event(ADE_BROWSER_VIEW_OCCLUSION_START_EVENT));
window.dispatchEvent(new Event(ADE_BROWSER_VIEW_OCCLUSION_END_EVENT));

expect(api.setBounds).toHaveBeenLastCalledWith(expect.objectContaining({
width: 640,
height: 360,
visible: false,
}));

window.dispatchEvent(new Event(ADE_BROWSER_VIEW_OCCLUSION_END_EVENT));

await waitFor(() => {
expect(api.setBounds).toHaveBeenLastCalledWith(expect.objectContaining({
width: 640,
height: 360,
visible: true,
}));
});
});

it("starts and cancels screenshot crop mode when chat context is available", async () => {
const { api } = installBrowserApi();
api.captureScreenshot.mockResolvedValue({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import type { BuiltInBrowserTab } from "../../../shared/types/builtInBrowser";
import { consumePendingBuiltInBrowserNavigation } from "../../lib/openExternal";
import { useAppStore } from "../../state/appStore";
import {
ADE_BROWSER_VIEW_OCCLUSION_END_EVENT,
ADE_BROWSER_VIEW_OCCLUSION_START_EVENT,
ADE_WORK_SIDEBAR_BROWSER_RESIZE_END_EVENT,
ADE_WORK_SIDEBAR_BROWSER_RESIZE_START_EVENT,
} from "../../lib/workSidebarBrowserResize";
Expand Down Expand Up @@ -634,6 +636,7 @@ export function ChatBuiltInBrowserPanel({
const selectedItemRef = useRef<BuiltInBrowserContextItem | null>(null);
const captureModeRef = useRef(false);
const browserInputSuppressedRef = useRef(false);
const browserViewSuppressionCountRef = useRef(0);
const autoAttachedContextIdsRef = useRef(new Set<string>());
const editingUrlRef = useRef(false);
const apiAvailable = Boolean(getBrowserApi());
Expand Down Expand Up @@ -815,11 +818,14 @@ export function ChatBuiltInBrowserPanel({
};
const suppressInput = () => {
cancelRestoreFrame();
browserViewSuppressionCountRef.current += 1;
browserInputSuppressedRef.current = true;
setBrowserInputSuppressed(true);
reportBounds(false);
};
const restoreInput = () => {
browserViewSuppressionCountRef.current = Math.max(0, browserViewSuppressionCountRef.current - 1);
if (browserViewSuppressionCountRef.current > 0) return;
browserInputSuppressedRef.current = false;
setBrowserInputSuppressed(false);
cancelRestoreFrame();
Expand All @@ -830,9 +836,13 @@ export function ChatBuiltInBrowserPanel({
};
window.addEventListener(ADE_WORK_SIDEBAR_BROWSER_RESIZE_START_EVENT, suppressInput);
window.addEventListener(ADE_WORK_SIDEBAR_BROWSER_RESIZE_END_EVENT, restoreInput);
window.addEventListener(ADE_BROWSER_VIEW_OCCLUSION_START_EVENT, suppressInput);
window.addEventListener(ADE_BROWSER_VIEW_OCCLUSION_END_EVENT, restoreInput);
return () => {
window.removeEventListener(ADE_WORK_SIDEBAR_BROWSER_RESIZE_START_EVENT, suppressInput);
window.removeEventListener(ADE_WORK_SIDEBAR_BROWSER_RESIZE_END_EVENT, restoreInput);
window.removeEventListener(ADE_BROWSER_VIEW_OCCLUSION_START_EVENT, suppressInput);
window.removeEventListener(ADE_BROWSER_VIEW_OCCLUSION_END_EVENT, restoreInput);
cancelRestoreFrame();
};
}, [reportBounds]);
Expand Down
Loading
Loading