mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-14 13:25:21 +00:00
Merge ca0fd3ec4c into 4899c9fbc7
This commit is contained in:
commit
5443acdccd
7 changed files with 712 additions and 604 deletions
|
|
@ -40,7 +40,8 @@ export const CodePreviewPanel = async ({ path, repoName, revisionName }: CodePre
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-row py-1 px-2 items-center justify-between">
|
<div className="flex flex-row py-1 px-2 items-center justify-between ">
|
||||||
|
<div className="w-2/3">
|
||||||
<PathHeader
|
<PathHeader
|
||||||
path={path}
|
path={path}
|
||||||
repo={{
|
repo={{
|
||||||
|
|
@ -51,24 +52,27 @@ export const CodePreviewPanel = async ({ path, repoName, revisionName }: CodePre
|
||||||
}}
|
}}
|
||||||
branchDisplayName={revisionName}
|
branchDisplayName={revisionName}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-1/3 flex justify-end">
|
||||||
{(fileWebUrl && codeHostInfo) && (
|
{(fileWebUrl && codeHostInfo) && (
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href={fileWebUrl}
|
href={fileWebUrl}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="flex flex-row items-center gap-2 px-2 py-0.5 rounded-md flex-shrink-0"
|
className="flex flex-row items-center gap-4 px-2 py-0.5 rounded-md"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src={codeHostInfo.icon}
|
src={codeHostInfo.icon}
|
||||||
alt={codeHostInfo.codeHostName}
|
alt={codeHostInfo.codeHostName}
|
||||||
className={cn('w-4 h-4 flex-shrink-0', codeHostInfo.iconClassName)}
|
className={cn('w-4 h-4 flex-shrink-0', codeHostInfo.iconClassName)}
|
||||||
/>
|
/>
|
||||||
<span className="text-sm font-medium">Open in {codeHostInfo.codeHostName}</span>
|
<span className="text-xs font-medium">Open in {codeHostInfo.codeHostName}</span>
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
<PureCodePreviewPanel
|
<PureCodePreviewPanel
|
||||||
source={fileSourceResponse.source}
|
source={fileSourceResponse.source}
|
||||||
|
|
@ -77,6 +81,7 @@ export const CodePreviewPanel = async ({ path, repoName, revisionName }: CodePre
|
||||||
path={path}
|
path={path}
|
||||||
revisionName={revisionName ?? 'HEAD'}
|
revisionName={revisionName ?? 'HEAD'}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -11,10 +11,12 @@ export interface BrowseState {
|
||||||
language: string;
|
language: string;
|
||||||
}
|
}
|
||||||
isBottomPanelCollapsed: boolean;
|
isBottomPanelCollapsed: boolean;
|
||||||
|
isChatPanelCollapsed: boolean;
|
||||||
isFileTreePanelCollapsed: boolean;
|
isFileTreePanelCollapsed: boolean;
|
||||||
isFileSearchOpen: boolean;
|
isFileSearchOpen: boolean;
|
||||||
activeExploreMenuTab: "references" | "definitions";
|
activeExploreMenuTab: "references" | "definitions";
|
||||||
bottomPanelSize: number;
|
bottomPanelSize: number;
|
||||||
|
chatPanelSize: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultState: BrowseState = {
|
const defaultState: BrowseState = {
|
||||||
|
|
@ -24,6 +26,8 @@ const defaultState: BrowseState = {
|
||||||
isFileSearchOpen: false,
|
isFileSearchOpen: false,
|
||||||
activeExploreMenuTab: "references",
|
activeExploreMenuTab: "references",
|
||||||
bottomPanelSize: 35,
|
bottomPanelSize: 35,
|
||||||
|
isChatPanelCollapsed: true,
|
||||||
|
chatPanelSize: 20,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SET_BROWSE_STATE_QUERY_PARAM = "setBrowseState";
|
export const SET_BROWSE_STATE_QUERY_PARAM = "setBrowseState";
|
||||||
|
|
@ -33,7 +37,7 @@ export const BrowseStateContext = createContext<{
|
||||||
updateBrowseState: (state: Partial<BrowseState>) => void;
|
updateBrowseState: (state: Partial<BrowseState>) => void;
|
||||||
}>({
|
}>({
|
||||||
state: defaultState,
|
state: defaultState,
|
||||||
updateBrowseState: () => {},
|
updateBrowseState: () => { },
|
||||||
});
|
});
|
||||||
|
|
||||||
interface BrowseStateProviderProps {
|
interface BrowseStateProviderProps {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
import { KeyboardShortcutHint } from "@/app/components/keyboardShortcutHint"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { RiRobot3Line } from "react-icons/ri";
|
||||||
|
import { useBrowseState } from "../hooks/useBrowseState";
|
||||||
|
import { ImperativePanelHandle } from "react-resizable-panels";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
import { useHotkeys } from "react-hotkeys-hook";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import { ResizablePanel } from "@/components/ui/resizable";
|
||||||
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
|
|
||||||
|
export const CHAT_PANEL_MIN_SIZE = 5;
|
||||||
|
export const CHAT_PANEL_MAX_SIZE = 50;
|
||||||
|
|
||||||
|
interface ChatPanelProps {
|
||||||
|
order: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ChatPanel = ({ order }: ChatPanelProps) => {
|
||||||
|
const panelRef = useRef<ImperativePanelHandle>(null);
|
||||||
|
const {
|
||||||
|
state: { isChatPanelCollapsed, chatPanelSize },
|
||||||
|
updateBrowseState
|
||||||
|
} = useBrowseState();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isChatPanelCollapsed) {
|
||||||
|
panelRef.current?.collapse();
|
||||||
|
} else {
|
||||||
|
panelRef.current?.expand();
|
||||||
|
}
|
||||||
|
}, [isChatPanelCollapsed]);
|
||||||
|
|
||||||
|
useHotkeys("shift+mod+o", (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
updateBrowseState({ isChatPanelCollapsed: !isChatPanelCollapsed });
|
||||||
|
}, {
|
||||||
|
enableOnFormTags: true,
|
||||||
|
enableOnContentEditable: true,
|
||||||
|
description: "Open Chat Panel"
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ResizablePanel
|
||||||
|
minSize={CHAT_PANEL_MIN_SIZE}
|
||||||
|
maxSize={CHAT_PANEL_MAX_SIZE}
|
||||||
|
collapsible={true}
|
||||||
|
ref={panelRef}
|
||||||
|
defaultSize={isChatPanelCollapsed ? 0 : chatPanelSize}
|
||||||
|
onCollapse={() => updateBrowseState({ isChatPanelCollapsed: true })}
|
||||||
|
onExpand={() => updateBrowseState({ isChatPanelCollapsed: false })}
|
||||||
|
onResize={(size) => {
|
||||||
|
if (!isChatPanelCollapsed) {
|
||||||
|
updateBrowseState({ chatPanelSize: size });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
order={order}
|
||||||
|
id={"chat-panel"}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-center justify-center h-full text-muted-foreground gap-2 p-4">
|
||||||
|
<p className="text-sm">Chat goes here</p>
|
||||||
|
</div>
|
||||||
|
</ResizablePanel>
|
||||||
|
{isChatPanelCollapsed && (
|
||||||
|
<div className="flex flex-col items-center h-full p-2">
|
||||||
|
<Tooltip
|
||||||
|
delayDuration={100}
|
||||||
|
>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="h-8 w-8"
|
||||||
|
onClick={() => {
|
||||||
|
panelRef.current?.expand();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RiRobot3Line className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="bottom" className="flex flex-row items-center gap-2">
|
||||||
|
<KeyboardShortcutHint shortcut="⇧ ⌘ O" />
|
||||||
|
<Separator orientation="vertical" className="h-4" />
|
||||||
|
<span>Open AI Chat</span>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import { ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable";
|
import { ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable";
|
||||||
import { BottomPanel } from "./components/bottomPanel";
|
import { BottomPanel } from "./components/bottomPanel";
|
||||||
|
import { ChatPanel } from "./components/chatPanel";
|
||||||
import { AnimatedResizableHandle } from "@/components/ui/animatedResizableHandle";
|
import { AnimatedResizableHandle } from "@/components/ui/animatedResizableHandle";
|
||||||
import { BrowseStateProvider } from "./browseStateProvider";
|
import { BrowseStateProvider } from "./browseStateProvider";
|
||||||
import { FileTreePanel } from "@/features/fileTree/components/fileTreePanel";
|
import { FileTreePanel } from "@/features/fileTree/components/fileTreePanel";
|
||||||
|
|
@ -42,8 +43,8 @@ export default function Layout({
|
||||||
|
|
||||||
<ResizablePanel
|
<ResizablePanel
|
||||||
order={2}
|
order={2}
|
||||||
minSize={10}
|
minSize={20}
|
||||||
defaultSize={80}
|
defaultSize={60}
|
||||||
id="code-preview-panel-container"
|
id="code-preview-panel-container"
|
||||||
>
|
>
|
||||||
<ResizablePanelGroup
|
<ResizablePanelGroup
|
||||||
|
|
@ -61,6 +62,10 @@ export default function Layout({
|
||||||
/>
|
/>
|
||||||
</ResizablePanelGroup>
|
</ResizablePanelGroup>
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
|
|
||||||
|
<AnimatedResizableHandle />
|
||||||
|
|
||||||
|
<ChatPanel order={3} />
|
||||||
</ResizablePanelGroup>
|
</ResizablePanelGroup>
|
||||||
</div>
|
</div>
|
||||||
<FileSearchCommandDialog />
|
<FileSearchCommandDialog />
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue