-
-
setViewport(value)}
- >
-
-
-
-
-
-
-
- Full Width
-
-
-
-
-
- Desktop
-
-
-
-
-
- Mobile
-
-
-
-
-
setProjectPreviewPath(e.target.value)}
- className="w-full sm:w-[120px]"
- placeholder="Path (e.g. /)"
- />
-
-
-
-
-
-
-
-
- window.open(projectPreviewUrl, '_blank')}
- >
-
-
-
-
-
- This preview URL is temporary and will change when the project
- is rebooted
-
-
-
-
+
+
+
+
+
setViewport(value)}
+ >
+
+
+
+
+
+
+
+ Full Width
+
+
+
+
+
+ Desktop
+
+
+
+
+
+ Mobile
+
+
+
+
+
setProjectPreviewPath(e.target.value)}
+ className="w-full sm:w-[120px]"
+ placeholder="Path (e.g. /)"
+ />
+
+
+
+
+
+
+
+
+ window.open(projectPreviewUrl, '_blank')}
+ >
+
+
+
+
+
+ This preview URL is temporary and will change when the project
+ is rebooted
+
+
+
+
+
diff --git a/frontend/src/app/chats/components/RightPanel.js b/frontend/src/app/chats/components/RightPanel.js
index ae7ffd8..4a48eaf 100644
--- a/frontend/src/app/chats/components/RightPanel.js
+++ b/frontend/src/app/chats/components/RightPanel.js
@@ -2,14 +2,11 @@
import { useState } from 'react';
import { Button } from '@/components/ui/button';
-import { X as XIcon, PanelRight as PanelRightIcon } from 'lucide-react';
import { PreviewTab } from './PreviewTab';
import { FilesTab } from './FilesTab';
import { ProjectTab } from './ProjectTab';
export function RightPanel({
- isOpen,
- onClose,
projectPreviewUrl,
projectPreviewHash,
projectFileTree,
@@ -22,73 +19,47 @@ export function RightPanel({
const [selectedTab, setSelectedTab] = useState('preview');
return (
- <>
- {isOpen && (
-
-
onClose()}>
-
+
+
+
+ setSelectedTab('preview')}
+ >
+ Preview
+
+ setSelectedTab('editor')}
+ >
+ Files
+
+ setSelectedTab('info')}
+ >
+ Project
- )}
-
-
-
-
- setSelectedTab('preview')}
- >
- Preview
-
- setSelectedTab('editor')}
- >
- Files
-
- setSelectedTab('info')}
- >
- Project
-
-
-
-
-
-
-
-
-
-
- {selectedTab === 'preview' ? (
-
- ) : selectedTab === 'editor' ? (
-
- ) : (
-
- )}
-
-
- >
+
+ {selectedTab === 'preview' ? (
+
+ ) : selectedTab === 'editor' ? (
+
+ ) : (
+
+ )}
+
+
);
-}
+}
\ No newline at end of file
diff --git a/frontend/src/app/chats/page.js b/frontend/src/app/chats/page.js
index f52d350..dd4ce6b 100644
--- a/frontend/src/app/chats/page.js
+++ b/frontend/src/app/chats/page.js
@@ -9,6 +9,7 @@ import { api } from '@/lib/api';
import { Chat } from './components/Chat';
import { RightPanel } from './components/RightPanel';
import { useToast } from '@/hooks/use-toast';
+import Splitter from '@/components/ui/splitter';
export default function WorkspacePage({ chatId }) {
const { addChat, team, projects, refreshProjects } = useUser();
@@ -294,31 +295,33 @@ export default function WorkspacePage({ chatId }) {
)}
-
- setIsPreviewOpen(false)}
- projectPreviewUrl={projectPreviewUrl}
- projectPreviewPath={projectPreviewPath}
- setProjectPreviewPath={setProjectPreviewPath}
- projectPreviewHash={previewHash}
- projectFileTree={projectFileTree}
- project={projects.find((p) => +p.id === +projectId)}
- chatId={chatId}
- status={status}
- />
+
+
+ setIsPreviewOpen(false)}
+ projectPreviewUrl={projectPreviewUrl}
+ projectPreviewPath={projectPreviewPath}
+ setProjectPreviewPath={setProjectPreviewPath}
+ projectPreviewHash={previewHash}
+ projectFileTree={projectFileTree}
+ project={projects.find((p) => +p.id === +projectId)}
+ chatId={chatId}
+ status={status}
+ />
+
);
diff --git a/frontend/src/components/ui/splitter.jsx b/frontend/src/components/ui/splitter.jsx
new file mode 100644
index 0000000..6b4a78e
--- /dev/null
+++ b/frontend/src/components/ui/splitter.jsx
@@ -0,0 +1,81 @@
+import React, { useState, useRef, useEffect } from 'react';
+
+const Splitter = ({
+ children,
+ className = '',
+ minLeftWidth = 300,
+ minRightWidth = 50,
+ defaultLeftWidth = '70%'
+}) => {
+ const [isDragging, setIsDragging] = useState(false);
+ const [leftWidth, setLeftWidth] = useState(defaultLeftWidth);
+ const containerRef = useRef(null);
+ const leftPaneRef = useRef(null);
+ const rightPaneRef = useRef(null);
+
+ const handleMouseDown = (e) => {
+ e.preventDefault();
+ setIsDragging(true);
+ };
+
+ useEffect(() => {
+ const handleMouseMove = (e) => {
+ if (!isDragging || !containerRef.current) return;
+
+ const container = containerRef.current;
+ const containerRect = container.getBoundingClientRect();
+ const newLeftWidth = e.clientX - containerRect.left;
+
+ // Calculate the right width based on the new left width
+ const rightWidth = containerRect.width - newLeftWidth;
+
+ // Only update if both panes meet minimum width requirements
+ if (newLeftWidth >= minLeftWidth && rightWidth >= minRightWidth) {
+ setLeftWidth(`${(newLeftWidth / containerRect.width) * 100}%`);
+ }
+ };
+
+ const handleMouseUp = () => {
+ setIsDragging(false);
+ };
+
+ if (isDragging) {
+ document.addEventListener('mousemove', handleMouseMove);
+ document.addEventListener('mouseup', handleMouseUp);
+ }
+
+ return () => {
+ document.removeEventListener('mousemove', handleMouseMove);
+ document.removeEventListener('mouseup', handleMouseUp);
+ };
+ }, [isDragging, minLeftWidth, minRightWidth]);
+
+ const [leftChild, rightChild] = React.Children.toArray(children);
+
+ return (
+
+
+ {leftChild}
+
+
+
+
+
+ {rightChild}
+
+
+ );
+};
+
+export default Splitter;
\ No newline at end of file