Advertisement

← vibesdk

src/routes/chat/components/view-mode-switch.tsx

5.1 KB Download file
import { cn } from '@cloudflare/kumo';
import { AnimatePresence, motion } from 'framer-motion';
import { Eye, Code, FileText, Presentation, Database, GitBranch } from 'lucide-react';
import { featureRegistry } from '@/features';
import type { ProjectType } from '@/api-types';

// Map icon names to components
const ICON_MAP: Record<string, React.ComponentType<{ className?: string }>> = {
	Eye,
	Presentation,
};

export function ViewModeSwitch({
	view,
	onChange,
	previewAvailable = false,
	showTooltip = false,
	hasDocumentation = false,
	previewUrl,
	projectType,
	databaseAvailable = false,
	repoAvailable = false,
}: {
	view: 'preview' | 'editor' | 'docs' | 'blueprint' | 'presentation' | 'database' | 'repo'
	onChange: (mode: 'preview' | 'editor' | 'docs' | 'blueprint' | 'presentation' | 'database' | 'repo') => void;
	previewAvailable: boolean;
	showTooltip: boolean;
	hasDocumentation: boolean;
	previewUrl?: string;
	projectType?: ProjectType;
	/** When true, the read-only DB viewer tab is shown. Wired only for think-behavior apps. */
	databaseAvailable?: boolean;
	/** When true, the read-only Artifacts repo viewer tab is shown. Wired only for think-behavior apps. */
	repoAvailable?: boolean;
}) {
	// Get feature definition to determine icon and label
	const featureDefinition = projectType ? featureRegistry.getDefinition(projectType) : null;

	// Get the preview view definition to find the icon
	const featureModule = projectType ? featureRegistry.getModule(projectType) : null;
	const views = featureModule?.getViews() ?? [];
	const previewView = views.find(v => v.id === 'preview');
	const iconName = previewView?.iconName;
	const PreviewIcon = (iconName && ICON_MAP[iconName]) || Eye;

	if (!previewAvailable) {
		return null;
	}

	return (
		<div className="flex items-center gap-1 bg-bg-1 rounded-md p-0.5 relative">
			<AnimatePresence>
				{showTooltip && (
					<motion.div
						initial={{ opacity: 0, scale: 0.4 }}
						animate={{ opacity: 1, scale: 1 }}
						exit={{ opacity: 0, scale: 0 }}
						className="absolute z-50 top-10 left-0 bg-kumo-elevated text-text-primary text-xs px-2 py-1 rounded whitespace-nowrap animate-fade-in"
					>
						You can view code anytime from here
					</motion.div>
				)}
			</AnimatePresence>

			{/* Preview button - show when app has preview URL */}
			{previewUrl && (
				<button
					onClick={() => onChange('preview')}
					className={cn(
						'p-1 flex items-center justify-between h-full rounded-md transition-colors',
						view === 'preview' || view === 'presentation'
							? 'bg-bg-4 text-text-primary'
							: 'text-text-50/70 hover:text-text-primary hover:bg-brand',
					)}
					title={featureDefinition?.name ?? 'Preview'}
				>
					<PreviewIcon className="size-4" />
				</button>
			)}

			<button
				onClick={() => onChange('editor')}
				className={cn(
					'p-1 flex items-center justify-between h-full rounded-md transition-colors',
					view === 'editor'
						? 'bg-bg-4 text-text-primary'
						: 'text-text-50/70 hover:text-text-primary hover:bg-brand',
				)}
				title="Code"
			>
				<Code className="size-4" />
			</button>

			{/* Docs button - show when documentation exists */}
			{hasDocumentation && (
				<button
					onClick={() => onChange('docs')}
					className={cn(
						'p-1 flex items-center justify-between h-full rounded-md transition-colors',
						view === 'docs'
							? 'bg-bg-4 text-text-primary'
							: 'text-text-50/70 hover:text-text-primary hover:bg-brand',
					)}
					title="Docs"
				>
					<FileText className="size-4" />
				</button>
			)}
			{/* Database button - think-behavior apps get a singleton env.DB
			    backed by a SpaceDO Facet. Read-only viewer. */}
			{databaseAvailable && (
				<button
					onClick={() => onChange('database')}
					className={cn(
						'p-1 flex items-center justify-between h-full rounded-md transition-colors',
						view === 'database'
							? 'bg-bg-4 text-text-primary'
							: 'text-text-50/70 hover:text-text-primary hover:bg-brand',
					)}
					title="Database (read-only)"
				>
					<Database className="size-4" />
				</button>
			)}
			{/* Repo button - read-only viewer for the app's Cloudflare
			    Artifacts repository (durable git history). */}
			{repoAvailable && (
				<button
					onClick={() => onChange('repo')}
					className={cn(
						'p-1 flex items-center justify-between h-full rounded-md transition-colors',
						view === 'repo'
							? 'bg-bg-4 text-text-primary'
							: 'text-text-50/70 hover:text-text-primary hover:bg-brand',
					)}
					title="Repository (read-only)"
				>
					<GitBranch className="size-4" />
				</button>
			)}
			{/* {terminalAvailable && (
				<button
					onClick={() => onChange('terminal')}
					className={cn(
						'p-1 flex items-center justify-between h-full rounded-md transition-colors',
						view === 'terminal'
							? 'bg-bg-4 text-text-primary'
							: 'text-text-50/70 hover:text-text-primary hover:bg-brand',
					)}
					title="Terminal"
				>
					<Terminal className="size-4" />
				</button>
			)} */}
		</div>
	);
}