mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-05-03 11:51:36 +00:00
fixes as fequestd
This commit is contained in:
parent
07435fc255
commit
23d253e7f8
@ -5,6 +5,12 @@ import { classNames } from '~/utils/classNames';
|
||||
import { profileStore } from '~/lib/stores/profile';
|
||||
import type { TabType, Profile } from './types';
|
||||
|
||||
const BetaLabel = () => (
|
||||
<span className="px-1.5 py-0.5 rounded-full bg-purple-500/10 dark:bg-purple-500/20 text-[10px] font-medium text-purple-600 dark:text-purple-400 ml-2">
|
||||
BETA
|
||||
</span>
|
||||
);
|
||||
|
||||
interface AvatarDropdownProps {
|
||||
onSelectTab: (tab: TabType) => void;
|
||||
}
|
||||
@ -15,55 +21,52 @@ export const AvatarDropdown = ({ onSelectTab }: AvatarDropdownProps) => {
|
||||
return (
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<motion.button
|
||||
className="group flex items-center justify-center"
|
||||
whileHover={{ scale: 1.02 }}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
'w-10 h-10',
|
||||
'rounded-full overflow-hidden',
|
||||
'bg-gray-100/50 dark:bg-gray-800/50',
|
||||
'flex items-center justify-center',
|
||||
'ring-1 ring-gray-200/50 dark:ring-gray-700/50',
|
||||
'group-hover:ring-purple-500/50 dark:group-hover:ring-purple-500/50',
|
||||
'group-hover:bg-purple-500/10 dark:group-hover:bg-purple-500/10',
|
||||
'transition-all duration-200',
|
||||
'relative',
|
||||
)}
|
||||
>
|
||||
{profile?.avatar ? (
|
||||
<div className="w-full h-full">
|
||||
<img
|
||||
src={profile.avatar}
|
||||
alt={profile?.username || 'Profile'}
|
||||
className={classNames(
|
||||
'w-full h-full',
|
||||
'object-cover',
|
||||
'transform-gpu',
|
||||
'image-rendering-crisp',
|
||||
'group-hover:brightness-110',
|
||||
'group-hover:scale-105',
|
||||
'transition-all duration-200',
|
||||
)}
|
||||
loading="eager"
|
||||
decoding="sync"
|
||||
/>
|
||||
<div
|
||||
className={classNames(
|
||||
'absolute inset-0',
|
||||
'ring-1 ring-inset ring-black/5 dark:ring-white/5',
|
||||
'group-hover:ring-purple-500/20 dark:group-hover:ring-purple-500/20',
|
||||
'group-hover:bg-purple-500/5 dark:group-hover:bg-purple-500/5',
|
||||
'transition-colors duration-200',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="i-ph:robot-fill w-6 h-6 text-gray-400 dark:text-gray-400 group-hover:text-purple-500 dark:group-hover:text-purple-400 transition-colors" />
|
||||
)}
|
||||
</div>
|
||||
<motion.button className="group outline-none relative" whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }}>
|
||||
{profile?.avatar ? (
|
||||
<div
|
||||
className={classNames(
|
||||
'w-10 h-10',
|
||||
'rounded-full overflow-hidden',
|
||||
'bg-white dark:bg-gray-800',
|
||||
'transition-all duration-200',
|
||||
'relative',
|
||||
'shadow-sm',
|
||||
)}
|
||||
>
|
||||
<img
|
||||
src={profile.avatar}
|
||||
alt={profile?.username || 'Profile'}
|
||||
className={classNames(
|
||||
'w-full h-full',
|
||||
'object-cover',
|
||||
'transform-gpu',
|
||||
'image-rendering-crisp',
|
||||
'group-hover:brightness-110',
|
||||
'group-hover:scale-105',
|
||||
'transition-all duration-200',
|
||||
)}
|
||||
loading="eager"
|
||||
decoding="sync"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={classNames(
|
||||
'w-10 h-10',
|
||||
'rounded-full',
|
||||
'flex items-center justify-center',
|
||||
'bg-white dark:bg-gray-800',
|
||||
'text-gray-400 dark:text-gray-500',
|
||||
'group-hover:text-purple-500 dark:group-hover:text-purple-400',
|
||||
'transition-all duration-200',
|
||||
'shadow-sm',
|
||||
)}
|
||||
>
|
||||
<div className="i-ph:question w-6 h-6" />
|
||||
</div>
|
||||
)}
|
||||
{/* Add subtle circular highlight effect */}
|
||||
<div className="absolute inset-0 rounded-full bg-gradient-to-b from-white/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</motion.button>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
@ -86,7 +89,7 @@ export const AvatarDropdown = ({ onSelectTab }: AvatarDropdownProps) => {
|
||||
'border-b border-gray-200/50 dark:border-gray-800/50',
|
||||
)}
|
||||
>
|
||||
<div className="w-10 h-10 rounded-full overflow-hidden bg-gray-100/50 dark:bg-gray-800/50 flex-shrink-0">
|
||||
<div className="w-10 h-10 rounded-full overflow-hidden flex-shrink-0 bg-white dark:bg-gray-800 shadow-sm">
|
||||
{profile?.avatar ? (
|
||||
<img
|
||||
src={profile.avatar}
|
||||
@ -96,8 +99,8 @@ export const AvatarDropdown = ({ onSelectTab }: AvatarDropdownProps) => {
|
||||
decoding="sync"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<div className="i-ph:robot-fill w-6 h-6 text-gray-400 dark:text-gray-400 group-hover:text-purple-500 dark:group-hover:text-purple-400 transition-colors" />
|
||||
<div className="w-full h-full flex items-center justify-center text-gray-400 dark:text-gray-500 font-medium text-lg">
|
||||
<span className="relative -top-0.5">?</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@ -121,7 +124,7 @@ export const AvatarDropdown = ({ onSelectTab }: AvatarDropdownProps) => {
|
||||
)}
|
||||
onClick={() => onSelectTab('profile')}
|
||||
>
|
||||
<div className="i-ph:robot-fill w-4 h-4 text-gray-400 group-hover:text-purple-500 dark:group-hover:text-purple-400 transition-colors" />
|
||||
<div className="i-ph:user-circle w-4 h-4 text-gray-400 group-hover:text-purple-500 dark:group-hover:text-purple-400 transition-colors" />
|
||||
Edit Profile
|
||||
</DropdownMenu.Item>
|
||||
|
||||
@ -137,7 +140,7 @@ export const AvatarDropdown = ({ onSelectTab }: AvatarDropdownProps) => {
|
||||
)}
|
||||
onClick={() => onSelectTab('settings')}
|
||||
>
|
||||
<div className="i-ph:gear-six-fill w-4 h-4 text-gray-400 group-hover:text-purple-500 dark:group-hover:text-purple-400 transition-colors" />
|
||||
<div className="i-ph:gear-six w-4 h-4 text-gray-400 group-hover:text-purple-500 dark:group-hover:text-purple-400 transition-colors" />
|
||||
Settings
|
||||
</DropdownMenu.Item>
|
||||
|
||||
@ -155,8 +158,9 @@ export const AvatarDropdown = ({ onSelectTab }: AvatarDropdownProps) => {
|
||||
)}
|
||||
onClick={() => onSelectTab('task-manager')}
|
||||
>
|
||||
<div className="i-ph:activity-fill w-4 h-4 text-gray-400 group-hover:text-purple-500 dark:group-hover:text-purple-400 transition-colors" />
|
||||
<div className="i-ph:activity w-4 h-4 text-gray-400 group-hover:text-purple-500 dark:group-hover:text-purple-400 transition-colors" />
|
||||
Task Manager
|
||||
<BetaLabel />
|
||||
</DropdownMenu.Item>
|
||||
|
||||
<DropdownMenu.Item
|
||||
@ -171,8 +175,9 @@ export const AvatarDropdown = ({ onSelectTab }: AvatarDropdownProps) => {
|
||||
)}
|
||||
onClick={() => onSelectTab('service-status')}
|
||||
>
|
||||
<div className="i-ph:heartbeat-fill w-4 h-4 text-gray-400 group-hover:text-purple-500 dark:group-hover:text-purple-400 transition-colors" />
|
||||
<div className="i-ph:heartbeat w-4 h-4 text-gray-400 group-hover:text-purple-500 dark:group-hover:text-purple-400 transition-colors" />
|
||||
Service Status
|
||||
<BetaLabel />
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
|
@ -82,6 +82,15 @@ const TAB_DESCRIPTIONS: Record<TabType, string> = {
|
||||
'tab-management': 'Configure visible tabs and their order',
|
||||
};
|
||||
|
||||
// Beta status for experimental features
|
||||
const BETA_TABS = new Set<TabType>(['task-manager', 'service-status']);
|
||||
|
||||
const BetaLabel = () => (
|
||||
<div className="absolute top-2 right-2 px-1.5 py-0.5 rounded-full bg-purple-500/10 dark:bg-purple-500/20">
|
||||
<span className="text-[10px] font-medium text-purple-600 dark:text-purple-400">BETA</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
const AnimatedSwitch = ({ checked, onCheckedChange, id, label }: AnimatedSwitchProps) => {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
@ -108,11 +117,12 @@ const AnimatedSwitch = ({ checked, onCheckedChange, id, label }: AnimatedSwitchP
|
||||
'group-hover:shadow-md group-active:shadow-sm',
|
||||
'group-hover:scale-95 group-active:scale-90',
|
||||
)}
|
||||
layout
|
||||
initial={false}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
stiffness: 500,
|
||||
damping: 30,
|
||||
duration: 0.2,
|
||||
}}
|
||||
animate={{
|
||||
x: checked ? '1.25rem' : '0rem',
|
||||
@ -414,29 +424,13 @@ export const ControlPanel = ({ open, onClose }: ControlPanelProps) => {
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center space-x-4">
|
||||
{activeTab || showTabManagement ? (
|
||||
{(activeTab || showTabManagement) && (
|
||||
<button
|
||||
onClick={handleBack}
|
||||
className="flex items-center justify-center w-8 h-8 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-purple-500/10 dark:hover:bg-purple-500/20 group transition-all duration-200"
|
||||
className="flex items-center justify-center w-8 h-8 rounded-full bg-transparent hover:bg-purple-500/10 dark:hover:bg-purple-500/20 group transition-all duration-200"
|
||||
>
|
||||
<div className="i-ph:arrow-left w-4 h-4 text-gray-500 dark:text-gray-400 group-hover:text-purple-500 transition-colors" />
|
||||
</button>
|
||||
) : (
|
||||
<motion.div
|
||||
className="w-7 h-7"
|
||||
initial={{ rotate: -5 }}
|
||||
animate={{ rotate: 5 }}
|
||||
transition={{
|
||||
repeat: Infinity,
|
||||
repeatType: 'reverse',
|
||||
duration: 2,
|
||||
ease: 'easeInOut',
|
||||
}}
|
||||
>
|
||||
<div className="w-full h-full flex items-center justify-center bg-gray-100/50 dark:bg-gray-800/50 rounded-full">
|
||||
<div className="i-ph:lightning-fill w-5 h-5 text-purple-500 dark:text-purple-400 transition-colors" />
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
<DialogTitle className="text-xl font-semibold text-gray-900 dark:text-white">
|
||||
{showTabManagement ? 'Tab Management' : activeTab ? TAB_LABELS[activeTab] : 'Control Panel'}
|
||||
@ -462,7 +456,7 @@ export const ControlPanel = ({ open, onClose }: ControlPanelProps) => {
|
||||
{/* Close Button */}
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="flex items-center justify-center w-8 h-8 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-purple-500/10 dark:hover:bg-purple-500/20 group transition-all duration-200"
|
||||
className="flex items-center justify-center w-8 h-8 rounded-full bg-transparent hover:bg-purple-500/10 dark:hover:bg-purple-500/20 group transition-all duration-200"
|
||||
>
|
||||
<div className="i-ph:x w-4 h-4 text-gray-500 dark:text-gray-400 group-hover:text-purple-500 transition-colors" />
|
||||
</button>
|
||||
@ -513,8 +507,10 @@ export const ControlPanel = ({ open, onClose }: ControlPanelProps) => {
|
||||
statusMessage={getStatusMessage(tab.id)}
|
||||
description={TAB_DESCRIPTIONS[tab.id]}
|
||||
isLoading={loadingTab === tab.id}
|
||||
className="h-full"
|
||||
/>
|
||||
className="h-full relative"
|
||||
>
|
||||
{BETA_TABS.has(tab.id) && <BetaLabel />}
|
||||
</TabTile>
|
||||
</motion.div>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
|
@ -13,9 +13,10 @@ interface TabTileProps {
|
||||
description?: string;
|
||||
isLoading?: boolean;
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const TabTile = ({
|
||||
export const TabTile: React.FC<TabTileProps> = ({
|
||||
tab,
|
||||
onClick,
|
||||
isActive,
|
||||
@ -24,6 +25,7 @@ export const TabTile = ({
|
||||
description,
|
||||
isLoading,
|
||||
className,
|
||||
children,
|
||||
}: TabTileProps) => {
|
||||
return (
|
||||
<Tooltip.Provider delayDuration={200}>
|
||||
@ -100,62 +102,33 @@ export const TabTile = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Status Indicator */}
|
||||
{/* Update Indicator with Tooltip */}
|
||||
{hasUpdate && (
|
||||
<motion.div
|
||||
className={classNames(
|
||||
'absolute top-3 right-3',
|
||||
'w-2.5 h-2.5 rounded-full',
|
||||
'bg-purple-500',
|
||||
'ring-4 ring-purple-500',
|
||||
)}
|
||||
initial={{ scale: 0 }}
|
||||
animate={{ scale: 1 }}
|
||||
transition={{ type: 'spring', bounce: 0.5 }}
|
||||
/>
|
||||
<>
|
||||
<div className="absolute top-4 right-4 w-2 h-2 rounded-full bg-purple-500 dark:bg-purple-400 animate-pulse" />
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content
|
||||
className={classNames(
|
||||
'px-3 py-1.5 rounded-lg',
|
||||
'bg-[#18181B] text-white',
|
||||
'text-sm font-medium',
|
||||
'select-none',
|
||||
'z-[100]',
|
||||
)}
|
||||
side="top"
|
||||
sideOffset={5}
|
||||
>
|
||||
{statusMessage}
|
||||
<Tooltip.Arrow className="fill-[#18181B]" />
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Loading Overlay */}
|
||||
{isLoading && (
|
||||
<motion.div
|
||||
className={classNames(
|
||||
'absolute inset-0 rounded-xl z-10',
|
||||
'bg-white dark:bg-black',
|
||||
'flex items-center justify-center',
|
||||
)}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<motion.div
|
||||
className={classNames('w-8 h-8 rounded-full', 'border-2 border-purple-500', 'border-t-purple-500')}
|
||||
animate={{ rotate: 360 }}
|
||||
transition={{
|
||||
duration: 1,
|
||||
repeat: Infinity,
|
||||
ease: 'linear',
|
||||
}}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
{/* Children (e.g. Beta Label) */}
|
||||
{children}
|
||||
</motion.div>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content
|
||||
className={classNames(
|
||||
'px-3 py-1.5 rounded-lg',
|
||||
'bg-[#18181B] text-white',
|
||||
'text-sm font-medium',
|
||||
'select-none',
|
||||
'z-[100]',
|
||||
)}
|
||||
side="top"
|
||||
sideOffset={5}
|
||||
>
|
||||
{statusMessage || TAB_LABELS[tab.id]}
|
||||
<Tooltip.Arrow className="fill-[#18181B]" />
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
</Tooltip.Root>
|
||||
</Tooltip.Provider>
|
||||
);
|
||||
|
@ -121,19 +121,13 @@ export default function FeaturesTab() {
|
||||
|
||||
// Enable features by default on first load
|
||||
React.useEffect(() => {
|
||||
// Only enable if they haven't been explicitly set before
|
||||
if (isLatestBranch === undefined) {
|
||||
enableLatestBranch(true);
|
||||
}
|
||||
|
||||
if (contextOptimizationEnabled === undefined) {
|
||||
enableContextOptimization(true);
|
||||
}
|
||||
|
||||
if (autoSelectTemplate === undefined) {
|
||||
setAutoSelectTemplate(true);
|
||||
}
|
||||
// Force enable these features by default
|
||||
enableLatestBranch(true);
|
||||
enableContextOptimization(true);
|
||||
setAutoSelectTemplate(true);
|
||||
setPromptId('optimized');
|
||||
|
||||
// Only enable event logs if not explicitly set before
|
||||
if (eventLogs === undefined) {
|
||||
setEventLogs(true);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user