Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -483,4 +483,40 @@

.animate-cursor-blink {
animation: cursor-blink 1.5s ease-in-out infinite;
}

/* Responsive container utilities */
@layer utilities {
.container-full {
width: 100%;
max-width: 100%;
}

/* Ensure content doesn't overflow on small screens */
.break-word {
word-break: break-word;
overflow-wrap: break-word;
}
}

/* Mobile-first responsive adjustments */
@media (max-width: 640px) {
/* Smaller padding on mobile */
.terminal-card {
padding: 0.75rem;
}

/* Ensure text doesn't overflow */
code, pre {
word-break: break-all;
white-space: pre-wrap;
}
}

/* Large screen optimizations */
@media (min-width: 1920px) {
/* Even larger grids for very wide screens */
.grid-5xl {
grid-template-columns: repeat(6, minmax(0, 1fr));
}
}
44 changes: 22 additions & 22 deletions src/components/boot-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,55 +18,55 @@ export function BootScreen() {
};

return (
<div className="min-h-screen flex items-center justify-center p-4 scan-lines relative">
<div className="max-w-3xl w-full space-y-8">
<div className="min-h-screen flex flex-col p-4 sm:p-6 lg:p-8 scan-lines relative">
<div className="flex-1 flex flex-col w-full max-w-full space-y-6 sm:space-y-8">
{/* Header */}
<div className="text-center space-y-4">
<div className="flex items-center justify-center gap-3">
<Terminal className="w-12 h-12 terminal-text" />
<h1 className="text-5xl font-bold terminal-text">SudoStart</h1>
<div className="text-left sm:text-center space-y-3 sm:space-y-4">
<div className="flex items-center gap-3">
<Terminal className="w-10 h-10 sm:w-12 sm:h-12 terminal-text" />
<h1 className="text-3xl sm:text-5xl font-bold terminal-text">SudoStart</h1>
</div>
<p className="text-xl text-muted-foreground">
<p className="text-lg sm:text-xl text-muted-foreground">
Zero-to-Code OS Setup Generator
</p>
<div className="flex items-center justify-center gap-2 text-sm text-muted-foreground">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<span className="terminal-text">$</span>
<span>Powered by Root AI</span>
<span className="cursor-blink terminal-text">▊</span>
</div>
</div>

{/* Boot Questions */}
<div className="terminal-card rounded-lg p-8 space-y-8">
<div className="terminal-card rounded-lg p-4 sm:p-6 lg:p-8 space-y-6 sm:space-y-8 flex-1 flex flex-col">
{/* Question 1: OS Selection */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<span className="terminal-text">root@sudostart:~$</span>
<p className="text-lg font-medium">Select your Kernel:</p>
</div>
<div className="grid grid-cols-2 gap-4 pl-8">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4 pl-0 sm:pl-8">
<button
onClick={() => setSelectedOS('macos')}
className={`p-6 rounded-lg border-2 transition-all ${selectedOS === 'macos'
className={`p-4 sm:p-6 rounded-lg border-2 transition-all text-left ${selectedOS === 'macos'
? 'border-primary terminal-glow bg-primary/10'
: 'border-border hover:border-primary/50'
}`}
>
<div className="space-y-2">
<h3 className="text-xl font-bold terminal-text">MacOS</h3>
<p className="text-sm text-muted-foreground">Homebrew</p>
<div className="space-y-1 sm:space-y-2">
<h3 className="text-lg sm:text-xl font-bold terminal-text">MacOS</h3>
<p className="text-xs sm:text-sm text-muted-foreground">Homebrew</p>
</div>
</button>
<button
onClick={() => setSelectedOS('linux')}
className={`p-6 rounded-lg border-2 transition-all ${selectedOS === 'linux'
className={`p-4 sm:p-6 rounded-lg border-2 transition-all text-left ${selectedOS === 'linux'
? 'border-primary terminal-glow bg-primary/10'
: 'border-border hover:border-primary/50'
}`}
>
<div className="space-y-2">
<h3 className="text-xl font-bold terminal-text">Linux</h3>
<p className="text-sm text-muted-foreground">Apt</p>
<div className="space-y-1 sm:space-y-2">
<h3 className="text-lg sm:text-xl font-bold terminal-text">Linux</h3>
<p className="text-xs sm:text-sm text-muted-foreground">Apt</p>
</div>
</button>
</div>
Expand All @@ -75,13 +75,13 @@ export function BootScreen() {


{/* Continue Button */}
<div className="pt-4">
<div className="pt-4 mt-auto">
<button
onClick={handleContinue}
disabled={!selectedOS}
className="w-full py-4 px-6 rounded-lg bg-primary text-primary-foreground font-bold text-lg
className="w-full py-3 sm:py-4 px-4 sm:px-6 rounded-lg bg-primary text-primary-foreground font-bold text-sm sm:text-lg
hover:bg-primary/90 transition-all disabled:opacity-50 disabled:cursor-not-allowed
terminal-glow enabled:hover:shadow-lg"
terminal-glow enabled:hover:shadow-lg break-all"
>
{selectedOS
? `$ sudo start --os=${selectedOS} --shell=bash`
Expand All @@ -91,7 +91,7 @@ export function BootScreen() {
</div>

{/* Footer */}
<div className="text-center text-sm text-muted-foreground">
<div className="text-left sm:text-center text-xs sm:text-sm text-muted-foreground pt-4">
<p>
Build your perfect development environment •{' '}
<span className="terminal-text">Open Source</span>
Expand Down
75 changes: 70 additions & 5 deletions src/components/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useStore } from '@/lib/store';
import { useTheme } from '@/lib/theme-context';
import { MessageSquare, ShoppingCart, Terminal, Search, Layers, Upload, Download as DownloadIcon, Sun, Moon, MoreHorizontal } from 'lucide-react';
import { MessageSquare, ShoppingCart, Terminal, Search, Layers, Upload, Download as DownloadIcon, Sun, Moon, MoreHorizontal, Monitor, Apple, ChevronDown } from 'lucide-react';
import { useState, useRef, useCallback, useEffect } from 'react';
import { BucketModal } from './bucket-modal';
import { SearchBar } from './search-bar';
Expand All @@ -12,18 +12,31 @@ import { useKeyboardShortcuts, getModifierSymbol } from '@/hooks/use-keyboard-sh
import { copyToClipboard } from '@/lib/utils';

export function Navbar() {
const { os, bucket, toggleChat, exportBucket, importBucket, setCurrentStep, generatedScript } = useStore();
const { os, bucket, toggleChat, exportBucket, importBucket, setCurrentStep, generatedScript, setOS } = useStore();
const { theme, toggleTheme } = useTheme();
const { toast } = useToast();
const [isBucketOpen, setIsBucketOpen] = useState(false);
const [isSearchOpen, setIsSearchOpen] = useState(false);
const [isPresetsOpen, setIsPresetsOpen] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isOSSwitcherOpen, setIsOSSwitcherOpen] = useState(false);
const [importError, setImportError] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
const menuRef = useRef<HTMLDivElement>(null);
const osSwitcherRef = useRef<HTMLDivElement>(null);
const modSymbol = getModifierSymbol();

// Handle OS switch
const handleOSSwitch = (newOS: 'macos' | 'linux') => {
if (newOS === os) {
setIsOSSwitcherOpen(false);
return;
}
setOS(newOS);
toast.success(`🖥️ Switched to ${newOS === 'macos' ? 'MacOS' : 'Linux'}`);
setIsOSSwitcherOpen(false);
};

const handleImport = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
Expand Down Expand Up @@ -131,6 +144,9 @@ export function Navbar() {
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
setIsMenuOpen(false);
}
if (osSwitcherRef.current && !osSwitcherRef.current.contains(e.target as Node)) {
setIsOSSwitcherOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
Expand Down Expand Up @@ -166,16 +182,65 @@ export function Navbar() {
</div>
</div>

{/* OS Switcher */}
{os && (
<div className="relative" ref={osSwitcherRef}>
<button
type="button"
onClick={() => setIsOSSwitcherOpen(!isOSSwitcherOpen)}
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-border
hover:border-primary/50 transition-all text-sm focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none"
title="Switch operating system"
aria-label="Switch operating system"
>
{os === 'macos' ? (
<Apple className="w-4 h-4" />
) : (
<Monitor className="w-4 h-4" />
)}
<span className="hidden md:inline capitalize">{os}</span>
<ChevronDown className={`w-3 h-3 transition-transform ${isOSSwitcherOpen ? 'rotate-180' : ''}`} />
</button>

{isOSSwitcherOpen && (
<div className="absolute left-0 top-full mt-2 w-40 py-2 rounded-lg border border-border bg-card shadow-xl z-50">
<button
type="button"
onClick={() => handleOSSwitch('macos')}
className={`w-full flex items-center gap-2 px-4 py-2 text-sm hover:bg-accent transition-colors text-left ${
os === 'macos' ? 'bg-primary/10 text-primary' : ''
}`}
>
<Apple className="w-4 h-4" />
<span>MacOS</span>
{os === 'macos' && <span className="ml-auto text-xs">✓</span>}
</button>
<button
type="button"
onClick={() => handleOSSwitch('linux')}
className={`w-full flex items-center gap-2 px-4 py-2 text-sm hover:bg-accent transition-colors text-left ${
os === 'linux' ? 'bg-primary/10 text-primary' : ''
}`}
>
<Monitor className="w-4 h-4" />
<span>Linux</span>
{os === 'linux' && <span className="ml-auto text-xs">✓</span>}
</button>
</div>
)}
</div>
)}

{/* Search Bar (desktop) */}
<button
type="button"
onClick={() => setIsSearchOpen(true)}
className="hidden md:flex flex-1 max-w-md items-center gap-3 px-4 py-2 rounded-lg bg-muted border border-border hover:border-primary/50 transition-all text-muted-foreground text-sm font-mono focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none"
className="hidden md:flex flex-1 max-w-xl items-center gap-2 px-3 py-1.5 rounded-lg bg-muted border border-border hover:border-primary/50 transition-all text-muted-foreground text-sm font-mono focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none"
aria-keyshortcuts="Meta+K"
>
<Search className="w-4 h-4 shrink-0" />
<Search className="w-3.5 h-3.5 shrink-0" />
<span>Search packages...</span>
<kbd className="ml-auto px-1.5 py-0.5 text-xs rounded border border-border">{modSymbol}K</kbd>
<kbd className="ml-auto px-1 py-0.5 text-xs rounded border border-border">{modSymbol}K</kbd>
</button>

{/* Spacer */}
Expand Down
36 changes: 23 additions & 13 deletions src/components/package-manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export function PackageManager() {
<div className="min-h-screen scan-lines relative">
<Navbar />

<div className="max-w-7xl mx-auto px-6 py-6 space-y-5">
<div className="w-full px-4 sm:px-6 lg:px-8 py-4 sm:py-6 space-y-4 sm:space-y-5">
{/* Header */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div>
Expand Down Expand Up @@ -144,7 +144,7 @@ export function PackageManager() {
<DependencyPanel bucket={bucket} os={os} />

{/* Category filters - compact pills */}
<div className="sticky top-[60px] z-30 py-2 bg-background/90 backdrop-blur-sm -mx-6 px-6
<div className="sticky top-[60px] z-30 py-2 bg-background/90 backdrop-blur-sm -mx-4 sm:-mx-6 lg:-mx-8 px-4 sm:px-6 lg:px-8
flex gap-1.5 flex-wrap border-b border-border/50">
{categories.map((cat, index) => {
const shortcutNumber = index < 9 ? index + 1 : null;
Expand Down Expand Up @@ -174,7 +174,7 @@ export function PackageManager() {
{/* Package Grid */}
<div
ref={packageGridRef}
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-3 sm:gap-4"
role="grid"
aria-label="Package catalog"
>
Expand Down Expand Up @@ -252,23 +252,26 @@ function PackageCard({
const [isLoadingVersions, setIsLoadingVersions] = useState(false);
const [hasFetchedVersions, setHasFetchedVersions] = useState(false);
const [copied, setCopied] = useState(false);
const [showDetails, setShowDetails] = useState(false);
const [showDetails, setShowDetails] = useState(isInBucket);

const dynamicVersionTools = [
// Runtimes
'nodejs', 'python3', 'rust', 'go', 'docker', 'nvm', 'ruby', 'php', 'kotlin', 'java',
'bun', 'deno', 'elixir', 'erlang', 'scala', 'clojure', 'haskell', 'lua', 'perl', 'r',
// Databases
'postgresql', 'redis', 'mongodb', 'mysql', 'mariadb',
'postgresql', 'redis', 'mongodb', 'mysql', 'mariadb', 'sqlite3',
'cockroachdb', 'cassandra', 'neo4j', 'clickhouse', 'timescaledb',
// Mobile
'flutter',
// IDEs
'vscode', 'zed', 'vim',
'vscode', 'zed', 'vim', 'neovim', 'emacs',
// Tools
'terraform', 'ansible', 'github-cli', 'git', 'curl', 'zsh', 'oh-my-zsh', 'jq', 'htop', 'tmux',
'lazygit', 'delta', 'httpie', 'pandoc',
// Containers
'podman', 'kubectl', 'minikube',
'podman', 'kubectl', 'minikube', 'lima', 'multipass', 'vagrant', 'packer', 'buildah', 'skopeo',
// DevOps
'jenkins', 'prometheus', 'docker-compose',
'jenkins', 'prometheus', 'docker-compose', 'pulumi', 'helm', 'kustomize', 'argocd-cli',
// Frameworks
'react', 'vue', 'angular', 'nextjs', 'django', 'flask', 'express',
// Web Servers
Expand All @@ -286,7 +289,17 @@ function PackageCard({
// Data Science
'jupyter', 'tensorflow', 'pandas', 'numpy', 'matplotlib',
// Cloud CLIs
'aws-cli', 'azure-cli',
'aws-cli', 'azure-cli', 'gcloud', 'vercel-cli', 'netlify-cli', 'supabase-cli', 'stripe-cli', 'aws-cdk',
// Security
'bitwarden-cli', '1password-cli', 'gpg', 'openssl', 'wireguard',
// Media
'gimp', 'inkscape', 'krita', 'audacity', 'obs-studio', 'ffmpeg', 'imagemagick',
// VCS
'git-lfs', 'github-desktop', 'sublime-merge', 'fork', 'tower',
// Utilities
'ripgrep', 'fd', 'fzf', 'bat', 'exa', 'dust', 'bottom', 'glances', 'ngrok', 'insomnia',
// Productivity
'notion', 'obsidian', 'logseq', 'todoist', 'taskwarrior', 'timewarrior', 'calcurse', 'newsboat',
];
const supportsDynamic = dynamicVersionTools.includes(pkg.id);

Expand Down Expand Up @@ -359,10 +372,7 @@ function PackageCard({
}
}, [isFocused]);

// Show details when in bucket
useEffect(() => {
setShowDetails(isInBucket);
}, [isInBucket]);


return (
<div
Expand Down
Loading
Loading