This commit is contained in:
Stijnus
2025-01-24 01:08:51 +01:00
parent 4e6f18ea1e
commit 27eab591a9
19 changed files with 1759 additions and 592 deletions

View File

@@ -1,12 +1,18 @@
import * as RadixDialog from '@radix-ui/react-dialog';
import { motion } from 'framer-motion';
import { useState, useEffect } from 'react';
import { useState, useEffect, useMemo } from 'react';
import { classNames } from '~/utils/classNames';
import { TabManagement } from './TabManagement';
import { TabTile } from '~/components/settings/shared/TabTile';
import { DialogTitle } from '~/components/ui/Dialog';
import type { TabType, TabVisibilityConfig } from '~/components/settings/settings.types';
import { tabConfigurationStore, updateTabConfiguration } from '~/lib/stores/settings';
import {
tabConfigurationStore,
resetTabConfiguration,
updateTabConfiguration,
developerModeStore,
setDeveloperMode,
} from '~/lib/stores/settings';
import { useStore } from '@nanostores/react';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
@@ -24,6 +30,7 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import CloudProvidersTab from '~/components/settings/providers/CloudProvidersTab';
import LocalProvidersTab from '~/components/settings/providers/LocalProvidersTab';
import TaskManagerTab from '~/components/settings/task-manager/TaskManagerTab';
import { Switch } from '~/components/ui/Switch';
interface DraggableTabTileProps {
tab: TabVisibilityConfig;
@@ -83,8 +90,14 @@ const DraggableTabTile = ({
},
});
const dragDropRef = (node: HTMLDivElement | null) => {
if (node) {
drag(drop(node));
}
};
return (
<div ref={(node) => drag(drop(node))} style={{ opacity: isDragging ? 0.5 : 1 }}>
<div ref={dragDropRef} style={{ opacity: isDragging ? 0.5 : 1 }}>
<TabTile
tab={tab}
onClick={onClick}
@@ -104,15 +117,32 @@ interface DeveloperWindowProps {
}
export const DeveloperWindow = ({ open, onClose }: DeveloperWindowProps) => {
const tabConfiguration = useStore(tabConfigurationStore);
const [activeTab, setActiveTab] = useState<TabType | null>(null);
const [showTabManagement, setShowTabManagement] = useState(false);
const [loadingTab, setLoadingTab] = useState<TabType | null>(null);
const tabConfiguration = useStore(tabConfigurationStore);
const [showTabManagement, setShowTabManagement] = useState(false);
const developerMode = useStore(developerModeStore);
const [profile, setProfile] = useState(() => {
const saved = localStorage.getItem('bolt_user_profile');
return saved ? JSON.parse(saved) : { avatar: null, notifications: true };
});
// Handle developer mode change
const handleDeveloperModeChange = (checked: boolean) => {
setDeveloperMode(checked);
if (!checked) {
onClose();
}
};
// Ensure developer mode is true when window is opened
useEffect(() => {
if (open) {
setDeveloperMode(true);
}
}, [open]);
// Listen for profile changes
useEffect(() => {
const handleStorageChange = (e: StorageEvent) => {
@@ -134,6 +164,38 @@ export const DeveloperWindow = ({ open, onClose }: DeveloperWindowProps) => {
const { hasConnectionIssues, currentIssue, acknowledgeIssue } = useConnectionStatus();
const { hasActiveWarnings, activeIssues, acknowledgeAllIssues } = useDebugStatus();
// Ensure tab configuration is properly initialized
useEffect(() => {
if (!tabConfiguration || !tabConfiguration.userTabs || !tabConfiguration.developerTabs) {
console.warn('Tab configuration is invalid in DeveloperWindow, resetting to defaults');
resetTabConfiguration();
} else {
// Validate tab configuration structure
const isValid =
tabConfiguration.userTabs.every(
(tab) =>
tab &&
typeof tab.id === 'string' &&
typeof tab.visible === 'boolean' &&
typeof tab.window === 'string' &&
typeof tab.order === 'number',
) &&
tabConfiguration.developerTabs.every(
(tab) =>
tab &&
typeof tab.id === 'string' &&
typeof tab.visible === 'boolean' &&
typeof tab.window === 'string' &&
typeof tab.order === 'number',
);
if (!isValid) {
console.warn('Tab configuration is malformed in DeveloperWindow, resetting to defaults');
resetTabConfiguration();
}
}
}, [tabConfiguration]);
const handleBack = () => {
if (showTabManagement) {
setShowTabManagement(false);
@@ -143,21 +205,55 @@ export const DeveloperWindow = ({ open, onClose }: DeveloperWindowProps) => {
};
// Only show tabs that are assigned to the developer window AND are visible
const visibleDeveloperTabs = tabConfiguration.developerTabs
.filter((tab) => {
// Hide notifications tab if notifications are disabled
if (tab.id === 'notifications' && !profile.notifications) {
return false;
}
const visibleDeveloperTabs = useMemo(() => {
console.log('Filtering developer tabs with configuration:', tabConfiguration);
return tab.visible;
})
.sort((a: TabVisibilityConfig, b: TabVisibilityConfig) => (a.order || 0) - (b.order || 0));
if (!tabConfiguration?.developerTabs || !Array.isArray(tabConfiguration.developerTabs)) {
console.warn('Invalid tab configuration, using empty array');
return [];
}
return tabConfiguration.developerTabs
.filter((tab) => {
if (!tab || typeof tab.id !== 'string') {
console.warn('Invalid tab entry:', tab);
return false;
}
// Hide notifications tab if notifications are disabled
if (tab.id === 'notifications' && !profile.notifications) {
console.log('Hiding notifications tab due to disabled notifications');
return false;
}
// Ensure the tab has the required properties
if (typeof tab.visible !== 'boolean' || typeof tab.window !== 'string' || typeof tab.order !== 'number') {
console.warn('Tab missing required properties:', tab);
return false;
}
// Only show tabs that are explicitly visible and assigned to the developer window
const isVisible = tab.visible && tab.window === 'developer';
console.log(`Tab ${tab.id} visibility:`, isVisible);
return isVisible;
})
.sort((a: TabVisibilityConfig, b: TabVisibilityConfig) => {
const orderA = typeof a.order === 'number' ? a.order : 0;
const orderB = typeof b.order === 'number' ? b.order : 0;
return orderA - orderB;
});
}, [tabConfiguration, profile.notifications]);
console.log('Filtered visible developer tabs:', visibleDeveloperTabs);
const moveTab = (dragIndex: number, hoverIndex: number) => {
const draggedTab = visibleDeveloperTabs[dragIndex];
const targetTab = visibleDeveloperTabs[hoverIndex];
console.log('Moving developer tab:', { draggedTab, targetTab });
// Update the order of the dragged and target tabs
const updatedDraggedTab = { ...draggedTab, order: targetTab.order };
const updatedTargetTab = { ...targetTab, order: draggedTab.order };
@@ -278,7 +374,10 @@ export const DeveloperWindow = ({ open, onClose }: DeveloperWindowProps) => {
<DndProvider backend={HTML5Backend}>
<RadixDialog.Root open={open}>
<RadixDialog.Portal>
<div className="fixed inset-0 flex items-center justify-center z-[60]">
<div
className="fixed inset-0 flex items-center justify-center z-[60]"
style={{ opacity: developerMode ? 1 : 0, transition: 'opacity 0.2s ease-in-out' }}
>
<RadixDialog.Overlay className="fixed inset-0">
<motion.div
className="absolute inset-0 bg-black/50 backdrop-blur-sm"
@@ -299,7 +398,7 @@ export const DeveloperWindow = ({ open, onClose }: DeveloperWindowProps) => {
'flex flex-col overflow-hidden',
)}
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
animate={{ opacity: developerMode ? 1 : 0, scale: developerMode ? 1 : 0.95, y: developerMode ? 0 : 20 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }}
transition={{ duration: 0.2 }}
>
@@ -346,6 +445,16 @@ export const DeveloperWindow = ({ open, onClose }: DeveloperWindowProps) => {
</motion.button>
)}
<div className="flex items-center gap-2">
<Switch
checked={developerMode}
onCheckedChange={handleDeveloperModeChange}
className="data-[state=checked]:bg-purple-500"
aria-label="Toggle developer mode"
/>
<label className="text-sm text-gray-500 dark:text-gray-400">Switch to User Mode</label>
</div>
<div className="relative">
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>