diff --git a/frontend/realtime/src/components/features/ToolbarTopLeft/index.tsx b/frontend/realtime/src/components/features/ToolbarTopLeft/index.tsx
index 576acad090..05f00de41d 100644
--- a/frontend/realtime/src/components/features/ToolbarTopLeft/index.tsx
+++ b/frontend/realtime/src/components/features/ToolbarTopLeft/index.tsx
@@ -1,17 +1,43 @@
import { twMerge } from "tailwind-merge";
+import { TabSelector, TabItem } from "~/components/ui/TabSelector";
+import { useComputed } from "@preact/signals-react";
+import { appMode, dispatchUiEvents } from "~/signals";
// import { paperWrapperStyles } from "~/components/styles";
// import { faPlus, faQuestion } from "@fortawesome/pro-solid-svg-icons";
// import { ToolbarButton } from "~/components/features/ToolbarButton";
export const ToolbarTopLeft = () => {
+ // Use the appMode signal directly with useComputed for reactivity - active tab sets the app mode
+ const activeTab = useComputed(() => appMode.value);
+
+ const tabs: TabItem[] = [
+ { id: "image", label: "Image" },
+ { id: "edit", label: "Edit" },
+ // { id: "video", label: "Video" },
+ ];
+
+ const handleTabChange = (tabId: string) => {
+ // Update the global signal
+ dispatchUiEvents.changeAppMode(tabId as "image" | "edit");
+ };
+
+ console.log(appMode.value);
+
return (
-
+

+
+
+
{/*
New Board
diff --git a/frontend/realtime/src/components/ui/TabSelector.tsx b/frontend/realtime/src/components/ui/TabSelector.tsx
new file mode 100644
index 0000000000..6bda0a8ed5
--- /dev/null
+++ b/frontend/realtime/src/components/ui/TabSelector.tsx
@@ -0,0 +1,82 @@
+import React, { useRef, useEffect, useState } from "react";
+import { Tab, TabGroup, TabList } from "@headlessui/react";
+import { twMerge } from "tailwind-merge";
+
+export interface TabItem {
+ id: string;
+ label: string;
+}
+
+interface TabSelectorProps {
+ tabs: TabItem[];
+ activeTab: string;
+ onTabChange: (tabId: string) => void;
+ className?: string;
+}
+
+export const TabSelector: React.FC
= ({
+ tabs,
+ activeTab,
+ onTabChange,
+ className,
+}) => {
+ // Find the index of the active tab
+ const selectedIndex = tabs.findIndex((tab) => tab.id === activeTab);
+ const tabsRef = useRef<(HTMLElement | null)[]>([]);
+ const [indicatorStyle, setIndicatorStyle] = useState({
+ left: 0,
+ width: 0,
+ });
+
+ // Handle tab change
+ const handleTabChange = (index: number) => {
+ onTabChange(tabs[index].id);
+ };
+
+ // Update the indicator position when the selected tab changes
+ useEffect(() => {
+ if (selectedIndex >= 0 && tabsRef.current[selectedIndex]) {
+ const currentTab = tabsRef.current[selectedIndex];
+ if (currentTab) {
+ setIndicatorStyle({
+ left: currentTab.offsetLeft,
+ width: currentTab.offsetWidth,
+ });
+ }
+ }
+ }, [selectedIndex, tabs]);
+
+ return (
+
+
+ {/* Animated indicator */}
+
+
+ {tabs.map((tab, index) => (
+ (tabsRef.current[index] = el)}
+ className={({ selected }) =>
+ twMerge(
+ "relative z-20 mx-0.5 min-w-max rounded-md border-2 border-transparent px-4 py-1 text-center font-semibold transition-all duration-300 ease-in-out",
+ selected ? "text-white" : "text-gray-300 hover:text-white",
+ )
+ }
+ >
+ {tab.label}
+
+ ))}
+
+
+ );
+};
diff --git a/frontend/realtime/src/components/ui/index.ts b/frontend/realtime/src/components/ui/index.ts
index 25897f25c1..788a004ad8 100644
--- a/frontend/realtime/src/components/ui/index.ts
+++ b/frontend/realtime/src/components/ui/index.ts
@@ -8,6 +8,7 @@ export * from "./PanelRight";
export * from "./Slider";
export * from "./Spinner";
export * from "./Switches";
+export * from "./TabSelector";
export * from "./TextArea";
export * from "./TextEditor";
export * from "./Tooltip";
diff --git a/frontend/realtime/src/signals/uiEvents/appMode.ts b/frontend/realtime/src/signals/uiEvents/appMode.ts
new file mode 100644
index 0000000000..e2b721e6e1
--- /dev/null
+++ b/frontend/realtime/src/signals/uiEvents/appMode.ts
@@ -0,0 +1,38 @@
+import { signal, effect } from "@preact/signals-core";
+
+// this is to control the app mode - BFlat
+
+// Define the app mode types
+export type AppModeType = "image" | "edit";
+
+// Create a signal for the current app mode
+export const appMode = signal("image");
+
+// Create a function to change the app mode
+export const changeAppMode = (mode: AppModeType) => {
+ appMode.value = mode;
+};
+
+// Store callbacks for mode changes
+const callbacks: ((mode: AppModeType) => void)[] = [];
+
+// Set up an effect to notify callbacks when the mode changes
+effect(() => {
+ const currentMode = appMode.value;
+ callbacks.forEach((callback) => callback(currentMode));
+});
+
+// Export an event handler for components to use
+export const appModeEvents = {
+ onChange: (callback: (mode: AppModeType) => void) => {
+ callbacks.push(callback);
+
+ // Return a function to unsubscribe
+ return () => {
+ const index = callbacks.indexOf(callback);
+ if (index !== -1) {
+ callbacks.splice(index, 1);
+ }
+ };
+ },
+};
diff --git a/frontend/realtime/src/signals/uiEvents/index.ts b/frontend/realtime/src/signals/uiEvents/index.ts
index a698c9c1f3..f54c5ff6f5 100644
--- a/frontend/realtime/src/signals/uiEvents/index.ts
+++ b/frontend/realtime/src/signals/uiEvents/index.ts
@@ -36,6 +36,7 @@ import {
dispatchers as modelSelectionDispatchers,
events as modelSelectionEvents,
} from "./modelSelection";
+import { appMode, changeAppMode, appModeEvents } from "./appMode";
export const uiEvents = {
...addMediaEvents,
@@ -49,6 +50,7 @@ export const uiEvents = {
promptEvents: promptEvents,
loadingIndicator: loadingIndicatorEvents,
modelSelection: modelSelectionEvents,
+ appMode: appModeEvents,
};
export const dispatchUiEvents = {
...addMediaDispatchers,
@@ -62,4 +64,7 @@ export const dispatchUiEvents = {
promptSettings: promptDispatchers,
loadingIndicator: loadingIndicatorDispatchers,
modelSelection: modelSelectionDispatchers,
+ changeAppMode,
};
+
+export { appMode };