Skip to content

Commit

Permalink
Merge pull request #5 from stephanj/issue-3
Browse files Browse the repository at this point in the history
Feat 3: Introduced splitter between the two panels
  • Loading branch information
sshh12 authored Jan 4, 2025
2 parents b0a9642 + 38626f9 commit b4b007e
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 173 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
env.*
env.*
.idea/*
168 changes: 90 additions & 78 deletions frontend/src/app/chats/components/PreviewTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,98 +78,110 @@ export function PreviewTab({

if (!projectPreviewUrl) {
return (
<div className="flex items-center justify-center h-full text-muted-foreground">
No preview available
<div className="w-full h-full flex items-center justify-center bg-muted/10">
<span className="text-muted-foreground">No preview available</span>
</div>
);
}

return (
<div className="flex flex-col h-full">
<div className="p-2 border-b flex flex-col sm:flex-row sm:items-center justify-between gap-2">
<div className="flex flex-wrap items-center gap-2">
<Select
value={viewport}
onValueChange={(value) => setViewport(value)}
>
<SelectTrigger className="w-[140px] sm:w-[180px]">
<SelectValue placeholder="Select viewport" />
</SelectTrigger>
<SelectContent>
<SelectItem value="full">
<div className="flex items-center gap-2">
<Maximize2 className="h-4 w-4" />
<span>Full Width</span>
</div>
</SelectItem>
<SelectItem value="desktop">
<div className="flex items-center gap-2">
<Monitor className="h-4 w-4" />
<span>Desktop</span>
</div>
</SelectItem>
<SelectItem value="mobile">
<div className="flex items-center gap-2">
<Smartphone className="h-4 w-4" />
<span>Mobile</span>
</div>
</SelectItem>
</SelectContent>
</Select>
<Input
value={projectPreviewPath}
onChange={(e) => setProjectPreviewPath(e.target.value)}
className="w-full sm:w-[120px]"
placeholder="Path (e.g. /)"
/>
</div>
<div className="flex gap-2 self-end sm:self-auto">
<Button variant="ghost" size="icon" onClick={handleRefresh}>
<RotateCw className="h-4 w-4" />
</Button>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => window.open(projectPreviewUrl, '_blank')}
>
<ExternalLink className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>
This preview URL is temporary and will change when the project
is rebooted
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<div className="w-full h-full flex flex-col">
<div className="flex-none border-b bg-background">
<div className="p-2 flex flex-col sm:flex-row sm:items-center justify-between gap-2">
<div className="flex flex-wrap items-center gap-2">
<Select
value={viewport}
onValueChange={(value) => setViewport(value)}
>
<SelectTrigger className="w-[140px] sm:w-[180px]">
<SelectValue placeholder="Select viewport" />
</SelectTrigger>
<SelectContent>
<SelectItem value="full">
<div className="flex items-center gap-2">
<Maximize2 className="h-4 w-4" />
<span>Full Width</span>
</div>
</SelectItem>
<SelectItem value="desktop">
<div className="flex items-center gap-2">
<Monitor className="h-4 w-4" />
<span>Desktop</span>
</div>
</SelectItem>
<SelectItem value="mobile">
<div className="flex items-center gap-2">
<Smartphone className="h-4 w-4" />
<span>Mobile</span>
</div>
</SelectItem>
</SelectContent>
</Select>
<Input
value={projectPreviewPath}
onChange={(e) => setProjectPreviewPath(e.target.value)}
className="w-full sm:w-[120px]"
placeholder="Path (e.g. /)"
/>
</div>
<div className="flex gap-2 self-end sm:self-auto">
<Button variant="ghost" size="icon" onClick={handleRefresh}>
<RotateCw className="h-4 w-4" />
</Button>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => window.open(projectPreviewUrl, '_blank')}
>
<ExternalLink className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>
This preview URL is temporary and will change when the project
is rebooted
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
</div>
</div>
<div
className="flex-1 flex items-start justify-center p-4 bg-gray-50"
className="flex-1 w-full h-full bg-muted/10 overflow-auto"
ref={containerRef}
>
<div
className="w-full h-full flex items-start justify-center p-4"
style={{
width: viewport === 'full' ? '100%' : 'auto',
height: viewport === 'full' ? '100%' : 'auto',
transform: viewport !== 'full' ? `scale(${scale})` : 'none',
transformOrigin: 'top center',
display: 'flex',
alignItems: 'flex-start',
justifyContent: 'center',
marginTop: '20px',
minHeight: viewport === 'full' ? '100%' : 'auto'
}}
>
<iframe
src={`${projectPreviewUrl}${debouncedPath}`}
style={viewportStyles[viewport]}
className="border shadow-sm bg-white"
title="Project Preview"
/>
<div
style={{
width: viewport === 'full' ? '100%' : 'auto',
height: viewport === 'full' ? '100%' : 'auto',
transform: viewport !== 'full' ? `scale(${scale})` : 'none',
transformOrigin: 'top center',
display: 'flex',
alignItems: 'flex-start',
justifyContent: 'center',
marginTop: '20px',
}}
>
<iframe
src={`${projectPreviewUrl}${debouncedPath}`}
style={{
...viewportStyles[viewport],
height: viewport === 'full' ? '100%' : viewportStyles[viewport].height
}}
className="border shadow-sm bg-white"
title="Project Preview"
/>
</div>
</div>
</div>
</div>
Expand Down
109 changes: 40 additions & 69 deletions frontend/src/app/chats/components/RightPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -22,73 +19,47 @@ export function RightPanel({
const [selectedTab, setSelectedTab] = useState('preview');

return (
<>
{isOpen && (
<div className="md:hidden fixed top-4 right-4 z-40">
<Button variant="outline" size="sm" onClick={() => onClose()}>
<PanelRightIcon className="h-4 w-4" />
<div className="flex flex-col w-full h-full">
<div className="border-b bg-background">
<div className="flex items-center space-x-4 px-4 py-2">
<Button
variant={selectedTab === 'preview' ? 'default' : 'ghost'}
size="sm"
onClick={() => setSelectedTab('preview')}
>
Preview
</Button>
<Button
variant={selectedTab === 'editor' ? 'default' : 'ghost'}
size="sm"
onClick={() => setSelectedTab('editor')}
>
Files
</Button>
<Button
variant={selectedTab === 'info' ? 'default' : 'ghost'}
size="sm"
onClick={() => setSelectedTab('info')}
>
Project
</Button>
</div>
)}

<div
className={`${
isOpen ? 'translate-x-0' : 'translate-x-full'
} md:translate-x-0 fixed md:static right-0 top-0 h-screen w-full md:w-[600px] border-l bg-background transition-transform duration-200 ease-in-out z-30`}
>
<div className="p-4 pl-16 md:pl-4 border-b flex items-center justify-between">
<div className="flex items-center space-x-4">
<Button
variant={selectedTab === 'preview' ? 'default' : 'ghost'}
size="sm"
onClick={() => setSelectedTab('preview')}
>
Preview
</Button>
<Button
variant={selectedTab === 'editor' ? 'default' : 'ghost'}
size="sm"
onClick={() => setSelectedTab('editor')}
>
Files
</Button>
<Button
variant={selectedTab === 'info' ? 'default' : 'ghost'}
size="sm"
onClick={() => setSelectedTab('info')}
>
Project
</Button>
</div>
<div className="flex items-center space-x-2">
<Button
variant="ghost"
size="sm"
className="md:hidden"
onClick={onClose}
>
<XIcon className="h-4 w-4" />
</Button>
</div>
</div>
<div className="p-4">
<div className="rounded-lg border bg-muted/40 h-[calc(100vh-8rem)]">
{selectedTab === 'preview' ? (
<PreviewTab
projectPreviewUrl={projectPreviewUrl}
projectPreviewHash={projectPreviewHash}
projectPreviewPath={projectPreviewPath}
setProjectPreviewPath={setProjectPreviewPath}
status={status}
/>
) : selectedTab === 'editor' ? (
<FilesTab projectFileTree={projectFileTree} project={project} />
) : (
<ProjectTab project={project} onSendMessage={onSendMessage} />
)}
</div>
</div>
</div>
</>
<div className="flex-1 overflow-hidden">
{selectedTab === 'preview' ? (
<PreviewTab
projectPreviewUrl={projectPreviewUrl}
projectPreviewHash={projectPreviewHash}
projectPreviewPath={projectPreviewPath}
setProjectPreviewPath={setProjectPreviewPath}
status={status}
/>
) : selectedTab === 'editor' ? (
<FilesTab projectFileTree={projectFileTree} project={project} />
) : (
<ProjectTab project={project} onSendMessage={onSendMessage} />
)}
</div>
</div>
);
}
}
53 changes: 28 additions & 25 deletions frontend/src/app/chats/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -294,31 +295,33 @@ export default function WorkspacePage({ chatId }) {
</Button>
</div>
)}
<Chat
connected={!!webSocketRef.current}
messages={messages}
onSendMessage={handleSendMessage}
projectTitle={chatTitle}
status={status}
onProjectSelect={handleProjectSelect}
onStackSelect={handleStackPackSelect}
showStackPacks={chatId === 'new'}
suggestedFollowUps={suggestedFollowUps}
onReconnect={handleReconnect}
/>
<RightPanel
onSendMessage={handleSendMessage}
isOpen={isPreviewOpen}
onClose={() => setIsPreviewOpen(false)}
projectPreviewUrl={projectPreviewUrl}
projectPreviewPath={projectPreviewPath}
setProjectPreviewPath={setProjectPreviewPath}
projectPreviewHash={previewHash}
projectFileTree={projectFileTree}
project={projects.find((p) => +p.id === +projectId)}
chatId={chatId}
status={status}
/>
<Splitter defaultLeftWidth="60%" minLeftWidth={400} minRightWidth={400} className="h-full">
<Chat
connected={!!webSocketRef.current}
messages={messages}
onSendMessage={handleSendMessage}
projectTitle={chatTitle}
status={status}
onProjectSelect={handleProjectSelect}
onStackSelect={handleStackPackSelect}
showStackPacks={chatId === 'new'}
suggestedFollowUps={suggestedFollowUps}
onReconnect={handleReconnect}
/>
<RightPanel
onSendMessage={handleSendMessage}
isOpen={isPreviewOpen}
onClose={() => setIsPreviewOpen(false)}
projectPreviewUrl={projectPreviewUrl}
projectPreviewPath={projectPreviewPath}
setProjectPreviewPath={setProjectPreviewPath}
projectPreviewHash={previewHash}
projectFileTree={projectFileTree}
project={projects.find((p) => +p.id === +projectId)}
chatId={chatId}
status={status}
/>
</Splitter>
</div>
</div>
);
Expand Down
Loading

0 comments on commit b4b007e

Please sign in to comment.