Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export default function PublishDropdown({
<Button
variant="ghost"
size="md"
className="!px-2.5 font-normal"
className="!px-2.5 font-normal !bg-white !text-black hover:!bg-gray-100"
data-testid="publish-button"
>
Share
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import ForwardedIconComponent from "@/components/common/genericIconComponent";
import ShadTooltip from "@/components/common/shadTooltipComponent";
import { PlaygroundButtonSliding } from "@/components/core/playgroundComponent/sliding-container/components";
import { PLAYGROUND_BUTTON_NAME } from "@/constants/constants";
import { CustomIOModal } from "@/customization/components/custom-new-modal";
// Will be reverted before merging this PR - IOModal commented out to test sliding container
// import { CustomIOModal } from "@/customization/components/custom-new-modal";
import { ENABLE_PUBLISH } from "@/customization/feature-flags";

interface PlaygroundButtonProps {
Expand Down Expand Up @@ -49,22 +51,26 @@ const PlaygroundButton = ({
setOpen,
canvasOpen,
}: PlaygroundButtonProps) => {
return hasIO ? (
<CustomIOModal
open={open}
setOpen={setOpen}
disable={!hasIO}
canvasOpen={canvasOpen}
>
<ActiveButton />
</CustomIOModal>
) : (
<ShadTooltip content="Add a Chat Input or Chat Output to use the playground">
<div>
<DisabledButton />
</div>
</ShadTooltip>
);
// Will be reverted before merging this PR - IOModal commented out to test sliding container
// return hasIO ? (
// <CustomIOModal
// open={open}
// setOpen={setOpen}
// disable={!hasIO}
// canvasOpen={canvasOpen}
// >
// <ActiveButton />
// </CustomIOModal>
// ) : (
// <ShadTooltip content="Add a Chat Input or Chat Output to use the playground">
// <div>
// <DisabledButton />
// </div>
// </ShadTooltip>
// );

// New sliding container implementation for testing
return <PlaygroundButtonSliding hasIO={hasIO} />;
};

export default PlaygroundButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useSlidingContainerStore } from "../stores/sliding-container-store";

// Placeholder content for sliding container
// This will be replaced with ChatHeader in the next PR
function SlidingContainerPlaceholder() {
return (
<div className="h-full w-full bg-background border-l border-transparent shadow-lg flex items-center justify-center">
<p className="text-muted-foreground">
Sliding container - content coming soon
</p>
</div>
);
}

export function FlowPageSlidingContainerContent() {
return <SlidingContainerPlaceholder />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { FlowPageSlidingContainerContent } from "./flow-page-sliding-container";
export { PlaygroundButtonSliding } from "./playground-button-sliding";
export { SlidingContainer } from "./sliding-container";
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import ForwardedIconComponent from "@/components/common/genericIconComponent";
import ShadTooltip from "@/components/common/shadTooltipComponent";
import { PLAYGROUND_BUTTON_NAME } from "@/constants/constants";
import { ENABLE_PUBLISH } from "@/customization/feature-flags";
import { useSlidingContainerStore } from "../stores/sliding-container-store";

interface PlaygroundButtonSlidingProps {
hasIO: boolean;
}

const STROKE_WIDTH = ENABLE_PUBLISH ? 2 : 1.5;
const ICON_CLASS = "h-4 w-4 transition-all flex-shrink-0";

export function PlaygroundButtonSliding({
hasIO,
}: PlaygroundButtonSlidingProps) {
const toggle = useSlidingContainerStore((state) => state.toggle);
const isOpen = useSlidingContainerStore((state) => state.isOpen);
const width = useSlidingContainerStore((state) => state.width);
const iconName = isOpen ? "PanelRightClose" : "Play";

// Show label when slide container is closed (width === 0px or not open)
const showLabel = !isOpen || width === 0;

if (!hasIO) {
// For disabled state, always show label since container can't be opened
return (
<ShadTooltip content="Add a Chat Input or Chat Output to use the playground">
<div
className="playground-btn-flow-toolbar cursor-not-allowed text-muted-foreground duration-150"
data-testid="playground-btn-flow-sliding"
>
<ForwardedIconComponent
name="Play"
className={ICON_CLASS}
strokeWidth={STROKE_WIDTH}
/>
<span className="hidden md:block">{PLAYGROUND_BUTTON_NAME}</span>
</div>
</ShadTooltip>
);
}

return (
<div
onClick={toggle}
data-testid="playground-btn-flow-io-sliding"
className="playground-btn-flow-toolbar hover:bg-accent cursor-pointer"
>
<ForwardedIconComponent
name={iconName}
className={ICON_CLASS}
strokeWidth={STROKE_WIDTH}
/>
{showLabel && (
<span className="hidden md:block">{PLAYGROUND_BUTTON_NAME}</span>
)}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { cn } from "@/utils/utils";

const DEFAULT_WIDTH = 400;
const DEFAULT_DURATION = 300;

interface SlidingContainerProps {
isOpen: boolean;
children: React.ReactNode;
className?: string;
width?: number;
onWidthChange?: (width: number) => void;
resizable?: boolean;
duration?: number;
isFullscreen?: boolean;
}

export function SlidingContainer({
isOpen,
children,
className,
width = DEFAULT_WIDTH,
onWidthChange,
resizable = false,
duration = DEFAULT_DURATION,
isFullscreen = false,
}: SlidingContainerProps) {
const [isResizing, setIsResizing] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);

const handleMouseDown = useCallback(
(e: React.MouseEvent) => {
if (!resizable || !isOpen) return;
e.preventDefault();
setIsResizing(true);
},
[resizable, isOpen],
);

useEffect(() => {
if (!isResizing) return;

const handleMouseMove = (e: MouseEvent) => {
if (!onWidthChange) return;
const newWidth = window.innerWidth - e.clientX;
onWidthChange(newWidth);
};

const handleMouseUp = () => {
setIsResizing(false);
};

document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);

return () => {
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp);
};
}, [isResizing, onWidthChange]);

const actualWidth = isFullscreen ? "100%" : isOpen ? `${width}px` : "0px";

return (
<div
ref={containerRef}
className={cn(
"relative h-full overflow-hidden transition-all ease-in-out",
isResizing && "select-none",
className,
)}
style={{
width: actualWidth,
minWidth: actualWidth,
transitionDuration: isResizing ? "0ms" : `${duration}ms`,
}}
>
{resizable && isOpen && !isFullscreen && (
<div
onMouseDown={handleMouseDown}
className={cn(
"absolute left-0 top-0 z-10 h-full w-1 cursor-col-resize bg-transparent hover:bg-primary/20 transition-colors",
isResizing && "bg-primary/30",
)}
style={{ touchAction: "none" }}
aria-label="Resize panel"
/>
)}
<div className="h-full w-full">{children}</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type { SlidingContainerStoreType } from "./sliding-container-store";
export { useSlidingContainerStore } from "./sliding-container-store";
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { create } from "zustand";

const MIN_WIDTH = 300;
const MAX_WIDTH_RATIO = 0.8;
const DEFAULT_WIDTH = 400;

export interface SlidingContainerStoreType {
isOpen: boolean;
setIsOpen: (isOpen: boolean) => void;
toggle: () => void;
width: number;
setWidth: (width: number) => void;
isFullscreen: boolean;
setIsFullscreen: (isFullscreen: boolean) => void;
toggleFullscreen: () => void;
}

export const useSlidingContainerStore = create<SlidingContainerStoreType>(
(set) => ({
isOpen: false,
setIsOpen: (isOpen: boolean) => set({ isOpen }),
toggle: () => set((state) => ({ isOpen: !state.isOpen })),
width: DEFAULT_WIDTH,
setWidth: (width: number) => {
const maxWidth = window.innerWidth * MAX_WIDTH_RATIO;
set({ width: Math.max(MIN_WIDTH, Math.min(maxWidth, width)) });
},
isFullscreen: false,
setIsFullscreen: (isFullscreen: boolean) => set({ isFullscreen }),
toggleFullscreen: () =>
set((state) => ({ isFullscreen: !state.isFullscreen })),
}),
);
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ export const useUpdateMessage: useMutationFunctionType<
message.files = JSON.parse(message.files);
}
if (isPlayground && flowId) {
const messages = JSON.parse(sessionStorage.getItem(flowId) || "");
const storageData = sessionStorage.getItem(flowId) || "[]";
const messages = JSON.parse(storageData);
const messageIndex = messages.findIndex(
(m: Message) => m.id === message.id,
);
Expand All @@ -46,10 +47,8 @@ export const useUpdateMessage: useMutationFunctionType<
}
};

const mutation: UseMutationResult<Message, any, UpdateMessageParams> = mutate(
["useUpdateMessages"],
updateMessageApi,
{
const mutation: UseMutationResult<Message, unknown, UpdateMessageParams> =
mutate(["useUpdateMessages"], updateMessageApi, {
...options,
onSettled: (_, __, params, ___) => {
//@ts-ignore
Expand All @@ -60,8 +59,7 @@ export const useUpdateMessage: useMutationFunctionType<
});
}
},
},
);
});

return mutation;
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export const useUpdateSessionName: useMutationFunctionType<
const isPlayground = useFlowStore.getState().playgroundPage;
// if we are in playground we will edit the local storage instead of the API
if (isPlayground && flowId) {
const messages = JSON.parse(sessionStorage.getItem(flowId) || "");
const storageData = sessionStorage.getItem(flowId) || "[]";
const messages = JSON.parse(storageData);
const messagesWithNewSessionId = messages.map((message: Message) => {
if (message.session_id === data.old_session_id) {
message.session_id = data.new_session_id;
Expand All @@ -47,7 +48,7 @@ export const useUpdateSessionName: useMutationFunctionType<
}
};

const mutation: UseMutationResult<Message[], any, UpdateSessionParams> =
const mutation: UseMutationResult<Message[], unknown, UpdateSessionParams> =
mutate(["useUpdateSessionName"], updateSessionApi, {
...options,
onSettled: () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -742,10 +742,16 @@ export default function Page({
};

return (
<div className="h-full w-full bg-canvas" ref={reactFlowWrapper}>
<div
className="h-full w-full bg-canvas rounded-[8px]"
ref={reactFlowWrapper}
>
{showCanvas ? (
<>
<div id="react-flow-id" className="h-full w-full bg-canvas relative">
<div
id="react-flow-id"
className="h-full w-full bg-canvas relative rounded-[8px]"
>
{!view && (
<>
<MemoizedLogCanvasControls />
Expand Down
Loading