Skip to content

Commit d4fec55

Browse files
committed
Add fullscreen expand to plan preview
1 parent 20a7ee1 commit d4fec55

1 file changed

Lines changed: 90 additions & 6 deletions

File tree

apps/code/src/renderer/components/permissions/PlanContent.tsx

Lines changed: 90 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import { Box } from "@radix-ui/themes";
2-
import { useEffect, useRef } from "react";
1+
import { ArrowsIn, ArrowsOut, ListChecks, X } from "@phosphor-icons/react";
2+
import { Box, Flex, IconButton, Text } from "@radix-ui/themes";
3+
import { useEffect, useRef, useState } from "react";
4+
import { createPortal } from "react-dom";
35
import ReactMarkdown from "react-markdown";
46
import remarkGfm from "remark-gfm";
57

@@ -12,6 +14,7 @@ interface PlanContentProps {
1214

1315
export function PlanContent({ id, plan }: PlanContentProps) {
1416
const scrollRef = useRef<HTMLDivElement>(null);
17+
const [isFullscreen, setIsFullscreen] = useState(false);
1518

1619
useEffect(() => {
1720
const el = scrollRef.current;
@@ -33,14 +36,95 @@ export function PlanContent({ id, plan }: PlanContentProps) {
3336
};
3437
}, [id]);
3538

39+
useEffect(() => {
40+
if (!isFullscreen) return;
41+
const handler = (e: KeyboardEvent) => {
42+
if (e.key === "Escape") {
43+
setIsFullscreen(false);
44+
}
45+
};
46+
window.addEventListener("keydown", handler);
47+
return () => window.removeEventListener("keydown", handler);
48+
}, [isFullscreen]);
49+
50+
const markdown = (
51+
<ReactMarkdown remarkPlugins={[remarkGfm]}>{plan}</ReactMarkdown>
52+
);
53+
54+
const portalTarget = document.getElementById("mcp-fullscreen-portal");
55+
56+
if (isFullscreen && portalTarget) {
57+
return (
58+
<>
59+
<Flex justify="end" className="py-0.5">
60+
<IconButton
61+
size="1"
62+
variant="ghost"
63+
color="gray"
64+
onClick={() => setIsFullscreen(false)}
65+
title="Exit fullscreen"
66+
>
67+
<ArrowsIn size={12} />
68+
</IconButton>
69+
</Flex>
70+
71+
{createPortal(
72+
<Box
73+
className="pointer-events-auto absolute inset-0 flex flex-col bg-gray-1"
74+
style={{ transition: "opacity 150ms ease" }}
75+
>
76+
<Flex
77+
align="center"
78+
justify="between"
79+
className="border-gray-6 border-b px-4 py-2"
80+
>
81+
<Flex align="center" gap="2">
82+
<ListChecks size={14} className="text-gray-11" />
83+
<Text size="2" className="text-gray-11">
84+
Plan
85+
</Text>
86+
</Flex>
87+
<IconButton
88+
size="1"
89+
variant="ghost"
90+
color="gray"
91+
onClick={() => setIsFullscreen(false)}
92+
title="Exit fullscreen (Escape)"
93+
>
94+
<X size={14} />
95+
</IconButton>
96+
</Flex>
97+
98+
<Box
99+
ref={scrollRef}
100+
className="plan-markdown flex-1 overflow-y-auto p-6"
101+
>
102+
{markdown}
103+
</Box>
104+
</Box>,
105+
portalTarget,
106+
)}
107+
</>
108+
);
109+
}
110+
36111
return (
37112
<Box
38113
ref={scrollRef}
39-
className="max-h-[50vh] max-w-[750px] overflow-y-auto rounded-lg border-2 border-blue-6 bg-blue-2 p-4"
114+
className="relative max-h-[50vh] max-w-[750px] overflow-y-auto rounded-lg border-2 border-blue-6 bg-blue-2 p-4"
40115
>
41-
<Box className="plan-markdown text-blue-12">
42-
<ReactMarkdown remarkPlugins={[remarkGfm]}>{plan}</ReactMarkdown>
43-
</Box>
116+
<IconButton
117+
size="1"
118+
variant="ghost"
119+
color="gray"
120+
className="sticky top-0 z-10 float-right"
121+
onClick={() => setIsFullscreen(true)}
122+
title="Expand to fullscreen"
123+
>
124+
<ArrowsOut size={12} />
125+
</IconButton>
126+
127+
<Box className="plan-markdown text-blue-12">{markdown}</Box>
44128
</Box>
45129
);
46130
}

0 commit comments

Comments
 (0)