diff --git a/src/app/globals.css b/src/app/globals.css
index 50052b5..f4a1c4a 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -461,4 +461,88 @@ html {
.how-it-works-popup::-webkit-scrollbar-thumb {
background: var(--border-secondary);
border-radius: 2px;
+}
+
+/* ===== SLIDE-UP DRAWER ANIMATIONS ===== */
+
+@keyframes fadeIn {
+ 0% {
+ opacity: 0;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+}
+
+@keyframes slideUp {
+ 0% {
+ opacity: 0;
+ transform: translateY(100%);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes slideDown {
+ 0% {
+ opacity: 1;
+ transform: translateY(0);
+ }
+
+ 100% {
+ opacity: 0;
+ transform: translateY(100%);
+ }
+}
+
+@keyframes fadeOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
+
+@keyframes tooltipSlideUp {
+ 0% {
+ opacity: 0;
+ transform: translateY(8px);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+/* ===== COMMAND BAR SCROLLBAR ===== */
+
+.command-scroll {
+ scrollbar-width: thin;
+ scrollbar-color: var(--text-muted) var(--bg-hover);
+ padding-bottom: 10px;
+}
+
+.command-scroll::-webkit-scrollbar {
+ height: 6px;
+}
+
+.command-scroll::-webkit-scrollbar-track {
+ background: var(--bg-hover);
+ border-radius: 6px;
+}
+
+.command-scroll::-webkit-scrollbar-thumb {
+ background: var(--text-muted);
+ border-radius: 6px;
+}
+
+.command-scroll::-webkit-scrollbar-thumb:hover {
+ background: var(--text-primary);
}
\ No newline at end of file
diff --git a/src/app/page.tsx b/src/app/page.tsx
index fe5825f..b73dd38 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -2,7 +2,7 @@
import { useState, useEffect, useMemo, useCallback, useRef, useLayoutEffect } from 'react';
import { createPortal } from 'react-dom';
-import { Check, Copy, ChevronDown, ChevronRight, X, Download, HelpCircle, Github, Heart } from 'lucide-react';
+import { Check, Copy, ChevronDown, ChevronRight, ChevronUp, X, Download, HelpCircle, Github, Heart } from 'lucide-react';
import { useLinuxInit } from '@/hooks/useLinuxInit';
import { distros, categories, getAppsByCategory, type DistroId, type AppData, type Category } from '@/lib/data';
import { generateInstallScript } from '@/lib/generateInstallScript';
@@ -664,6 +664,26 @@ function CommandFooter({
const [copied, setCopied] = useState(false);
const [showCopyTooltip, setShowCopyTooltip] = useState(false);
const [showDownloadTooltip, setShowDownloadTooltip] = useState(false);
+ const [drawerOpen, setDrawerOpen] = useState(false);
+ const [drawerClosing, setDrawerClosing] = useState(false);
+
+ const closeDrawer = useCallback(() => {
+ setDrawerClosing(true);
+ setTimeout(() => {
+ setDrawerOpen(false);
+ setDrawerClosing(false);
+ }, 250);
+ }, []);
+
+ // Close drawer on Escape key
+ useEffect(() => {
+ if (!drawerOpen) return;
+ const handleEscape = (e: KeyboardEvent) => {
+ if (e.key === 'Escape') closeDrawer();
+ };
+ document.addEventListener('keydown', handleEscape);
+ return () => document.removeEventListener('keydown', handleEscape);
+ }, [drawerOpen, closeDrawer]);
const handleCopy = async () => {
if (selectedCount === 0) return;
@@ -764,23 +784,37 @@ function CommandFooter({
- {/* Command Bar */}
+ {/* Command Bar - Compact */}
{selectedCount}
-
-
0 ? 'text-[var(--text-secondary)]' : 'text-[var(--text-muted)]'} style={{ transition: 'color 0.5s' }}>{command}
+
selectedCount > 0 && setDrawerOpen(true)}
+ >
+
+
+ 0 ? 'text-[var(--text-secondary)]' : 'text-[var(--text-muted)]'}`} style={{ transition: 'color 0.5s' }}>{command}
+
+ {selectedCount > 0 && (
+
+
+
+ )}
+
{/* Download Button with Tooltip */}
-
selectedCount > 0 && setShowDownloadTooltip(true)}
onMouseLeave={() => setShowDownloadTooltip(false)}
>
{showDownloadTooltip && (
{/* Copy Button with Tooltip */}
-
+
+
+ {/* Slide-up Drawer */}
+ {drawerOpen && (
+ <>
+ {/* Backdrop */}
+
+ {/* Drawer - Mobile: bottom sheet, Desktop: centered modal */}
+
+ {/* Drawer Handle - mobile only */}
+
+
+
+
+ {/* Drawer Header */}
+
+
+
+ $
+
+
+
Terminal Command
+
{selectedCount} app{selectedCount !== 1 ? 's' : ''} • Press Esc to close
+
+
+
+
+
+ {/* Command Content - Terminal style */}
+
+
+ {/* Terminal header with action buttons on desktop */}
+
+
+ {/* Desktop inline actions */}
+
+
+
+
+
+ {/* Terminal content */}
+
+
+ $
+
+ {command}
+
+
+
+
+
+
+ {/* Drawer Actions - mobile only (stacked) */}
+
+
+
+
+
+ >
+ )}
);
}
diff --git a/src/components/ui/theme-toggle.tsx b/src/components/ui/theme-toggle.tsx
index 44d7c2f..2db756f 100644
--- a/src/components/ui/theme-toggle.tsx
+++ b/src/components/ui/theme-toggle.tsx
@@ -1,6 +1,6 @@
"use client"
-import { useState } from "react"
+import { useState, useEffect } from "react"
import { Moon, Sun } from "lucide-react"
import { cn } from "@/lib/utils"
import { useTheme } from "@/hooks/use-theme"
@@ -12,12 +12,27 @@ interface ThemeToggleProps {
export function ThemeToggle({ className }: ThemeToggleProps) {
const { theme, toggle } = useTheme()
+ const [mounted, setMounted] = useState(false)
+
+ // Prevent hydration mismatch by only rendering after mount
+ useEffect(() => {
+ setMounted(true)
+ }, [])
+
const isDark = theme === "dark"
- // next-themes
- // const { resolvedTheme, setTheme } = useTheme()
- // const isDark = resolvedTheme === "dark"
- // onClick={() => setTheme(isDark ? "light" : "dark")}
+ // Render placeholder with same dimensions during SSR
+ if (!mounted) {
+ return (
+
+ )
+ }
return (