Skip to content
Open
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
147 changes: 61 additions & 86 deletions website/src/repl/components/ReplEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,56 @@ import { Code } from '@src/repl/components/Code';
import UserFacingErrorMessage from '@src/repl/components/UserFacingErrorMessage';
import { Header } from './Header';
import { useSettings, settingsMap } from '@src/settings.mjs';
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
import { useCallback } from 'react';
import { useRef } from 'react';

// Resize handle component with visual feedback
function ResizeHandle({ direction = 'horizontal' }) {
// Resize handle component
function ResizeHandle({ direction, containerRef, settingKey }) {
const isHorizontal = direction === 'horizontal';
const isDragging = useRef(false);

const handlePointerDown = (e) => {
isDragging.current = true;
e.target.setPointerCapture(e.pointerId);
};

const handlePointerMove = (e) => {
if (!isDragging.current || !containerRef.current) return;

const container = containerRef.current;
const size = isHorizontal ? container.offsetHeight : container.offsetWidth;
const movement = isHorizontal ? -e.movementY : -e.movementX;
const deltaPercent = (movement / size) * 100;

const currentSize = Number(settingsMap.get()[settingKey]) || (isHorizontal ? 35 : 30);
const newSize = Math.max(20, Math.min(50, currentSize + deltaPercent));
settingsMap.setKey(settingKey, newSize);
};

const handlePointerUp = (e) => {
isDragging.current = false;
e.target.releasePointerCapture(e.pointerId);
};

return (
<PanelResizeHandle
<div
className={`
group relative flex items-center justify-center
${isHorizontal ? 'h-2 cursor-row-resize' : 'w-2 cursor-col-resize'}
bg-transparent hover:bg-foreground/10 active:bg-foreground/20
transition-colors duration-150
group flex items-center justify-center shrink-0
${isHorizontal ? 'h-2 cursor-row-resize w-full' : 'w-2 cursor-col-resize h-full'}
hover:bg-foreground/10 active:bg-foreground/20
`}
style={{ touchAction: 'none' }}
onPointerDown={handlePointerDown}
onPointerMove={handlePointerMove}
onPointerUp={handlePointerUp}
>
{/* Visual indicator */}
<div
className={`
${isHorizontal ? 'w-12 h-1' : 'w-1 h-12'}
bg-foreground/20 group-hover:bg-foreground/40 group-active:bg-foreground/60
rounded-full transition-colors duration-150
bg-foreground/20 group-hover:bg-foreground/40
rounded-full
`}
/>
</PanelResizeHandle>
</div>
);
}

Expand All @@ -37,86 +63,35 @@ export default function ReplEditor(Props) {
const settings = useSettings();
const { panelPosition, isZen, isPanelOpen, panelSizeBottom, panelSizeRight } = settings;

// Handler for saving panel sizes to localStorage
const handleLayoutChange = useCallback((sizes) => {
if (panelPosition === 'bottom' && sizes.length === 2) {
// sizes[1] is the bottom panel size in percentage
settingsMap.setKey('panelSizeBottom', sizes[1]);
} else if (panelPosition === 'right' && sizes.length === 2) {
// sizes[1] is the right panel size in percentage
settingsMap.setKey('panelSizeRight', sizes[1]);
}
}, [panelPosition]);
const containerElRef = useRef(null);

// Zen mode - no panels
if (isZen) {
return (
<div className="h-full flex flex-col relative" {...editorProps}>
<Loader active={pending} />
<Header context={context} />
<div className="grow flex relative overflow-hidden">
<Code containerRef={containerRef} editorRef={editorRef} init={init} />
</div>
<UserFacingErrorMessage error={error} />
</div>
);
}
const showRightPanel = !isZen && panelPosition === 'right';
const showBottomPanel = !isZen && panelPosition === 'bottom';

// Right panel layout
if (panelPosition === 'right') {
return (
<div className="h-full flex flex-col relative" {...editorProps}>
<Loader active={pending} />
<Header context={context} />
<PanelGroup
direction="horizontal"
onLayout={handleLayoutChange}
className="grow overflow-hidden"
>
<Panel defaultSize={100 - (isPanelOpen ? panelSizeRight : 3)} minSize={30}>
<Code containerRef={containerRef} editorRef={editorRef} init={init} />
</Panel>
{isPanelOpen && <ResizeHandle direction="vertical" />}
<Panel
defaultSize={isPanelOpen ? panelSizeRight : 3}
minSize={isPanelOpen ? 15 : 3}
maxSize={isPanelOpen ? 60 : 3}
collapsible={true}
>
<VerticalPanel context={context} />
</Panel>
</PanelGroup>
<UserFacingErrorMessage error={error} />
</div>
);
}

// Bottom panel layout
return (
<div className="h-full flex flex-col relative" {...editorProps}>
<div ref={containerElRef} className="h-full flex flex-col relative" {...editorProps}>
<Loader active={pending} />
<Header context={context} />
<PanelGroup
direction="vertical"
onLayout={handleLayoutChange}
className="grow overflow-hidden"
>
<Panel defaultSize={100 - (isPanelOpen ? panelSizeBottom : 5)} minSize={30}>
<div className="h-full flex relative overflow-hidden">
<Code containerRef={containerRef} editorRef={editorRef} init={init} />
<div className="grow flex relative overflow-hidden">
<Code containerRef={containerRef} editorRef={editorRef} init={init} />
{showRightPanel && isPanelOpen && (
<ResizeHandle direction="vertical" containerRef={containerElRef} settingKey="panelSizeRight" />
)}
{showRightPanel && (
<div className="shrink-0 overflow-hidden h-full" style={{ width: isPanelOpen ? `${panelSizeRight}%` : '48px' }}>
<VerticalPanel context={context} />
</div>
</Panel>
{isPanelOpen && <ResizeHandle direction="horizontal" />}
<Panel
defaultSize={isPanelOpen ? panelSizeBottom : 5}
minSize={isPanelOpen ? 15 : 5}
maxSize={isPanelOpen ? 70 : 5}
collapsible={true}
>
<HorizontalPanel context={context} />
</Panel>
</PanelGroup>
)}
</div>
<UserFacingErrorMessage error={error} />
{showBottomPanel && isPanelOpen && (
<ResizeHandle direction="horizontal" containerRef={containerElRef} settingKey="panelSizeBottom" />
)}
{showBottomPanel && (
<div className="shrink-0 overflow-hidden" style={{ height: isPanelOpen ? `${panelSizeBottom}%` : '48px' }}>
<HorizontalPanel context={context} />
</div>
)}
</div>
);
}
Loading