- {/* Quick Start Steps */}
-
-
Quick Start
-
-
-
1
-
Select your distro from the dropdown
-
-
-
2
-
Check the apps you want to install
-
-
-
3
-
Copy the command or download the script
-
-
-
4
-
Paste in terminal (Ctrl+Shift+V) and run
-
+ {/* Content */}
+
+
+ {/* Shortcuts */}
+
+ Keyboard Shortcuts
+
+ {[
+ ['↑↓←→', 'Navigate through apps'],
+ ['hjkl', 'Vim-style navigation'],
+ ['Space', 'Select or deselect app'],
+ ['/', 'Focus search box'],
+ ['y', 'Copy install command'],
+ ['d', 'Download install script'],
+ ['c', 'Clear all selections'],
+ ['t', 'Toggle light/dark theme'],
+ ['Tab', 'Preview current selection'],
+ ['Esc', 'Close this modal'],
+ ['?', 'Show this help'],
+ ['1 / 2', 'Switch AUR helper (yay/paru)'],
+ ].map(([key, desc]) => (
+
+
+ {key}
+
+ {desc}
+
+ ))}
-
+
- {/* Unavailable Apps */}
-
-
App Not Available?
-
-
Greyed-out apps aren't in your distro's repos. Here's what you can do:
-
- -
- •
- Use Flatpak/Snap: Switch to Flatpak or Snap in the distro selector for universal packages
-
- -
- •
- Download from website: Visit the app's official site and grab the
.deb, .rpm, or .AppImage
-
- -
- •
- Hover the ⓘ icon: Some unavailable apps show links to alternative download methods
-
-
-
-
+ {/* Getting Started */}
+
+ Getting Started
+
+ -
+ 1. Pick your distro — Select your Linux distribution from the dropdown at the top. This determines which package manager commands TuxMate generates for you.
+
+ -
+ 2. Select apps — Browse the categories and click on apps to add them to your selection. Selected apps are highlighted. Use keyboard shortcuts to navigate faster.
+
+ -
+ 3. Copy or download — Copy the generated install command to your clipboard, or download a complete shell script. Downloaded scripts include error handling and can install multiple apps at once.
+
+ -
+ 4. Run in terminal — Open your terminal, paste the command (Ctrl+Shift+V), and press Enter. The script will handle the rest.
+
+
+
- {/* Arch & AUR */}
-
-
Arch Linux & AUR
-
- Some Arch packages are in the AUR (Arch User Repository).
- TuxMate uses yay or paru to install these.
- When selecting AUR packages, a popup will ask which helper you have. You can switch between helpers anytime using 1 (yay) or 2 (paru).
-
-
-
- {/* Keyboard Shortcuts */}
-
-
Keyboard Shortcuts
-
-
- ↑↓←→
- Navigate
-
-
- hjkl
- Vim navigation
-
-
- Space
- Toggle selection
-
-
- /
- Search apps
-
-
- y
- Copy command
-
-
- d
- Download script
-
-
- c
- Clear selection
-
-
- t
- Toggle theme
-
-
- Tab
- Open preview
-
-
- Esc
- Close popups
-
-
- ?
- This help
-
-
-
-
- {/* Pro Tips */}
-
-
Pro Tips
-
- -
- 💡
- The download button gives you a full shell script with progress tracking, error handling, and a summary
+ {/* Notes */}
+
+
Good to Know
+
+ -
+ Greyed out apps aren't available in your distro's official repositories. Try switching to Flatpak or Snap in the dropdown, or hover the info icon next to the app for alternative installation methods.
- -
- 💡
-
- Running the script:{' '}
-
chmod +x tuxmate-*.sh && ./tuxmate-*.sh or{' '}
- bash tuxmate-*.sh
-
+ -
+ Arch Linux users — Some packages come from the AUR. TuxMate uses yay or paru as the AUR helper. Press 1 or 2 anytime to switch between them.
- -
- 💡
- Your selections are saved automatically — come back anytime to modify your setup
+
-
+ Auto-save — Your app selections are saved automatically in your browser. Come back anytime and your selections will still be there.
- -
- 💡
- Running
.deb files: sudo dpkg -i file.deb
-
- -
- 💡
- Running
.rpm files: sudo dnf install ./file.rpm
+ -
+ Running scripts — Downloaded scripts are saved as
tuxmate-*.sh. Run them with bash tuxmate-*.sh
-
+
>
diff --git a/src/components/ui/theme-toggle.tsx b/src/components/ui/theme-toggle.tsx
index a70e7f1..571d107 100644
--- a/src/components/ui/theme-toggle.tsx
+++ b/src/components/ui/theme-toggle.tsx
@@ -16,6 +16,7 @@ export function ThemeToggle({ className }: ThemeToggleProps) {
// Prevent hydration mismatch by only rendering after mount
useEffect(() => {
+ // eslint-disable-next-line react-hooks/set-state-in-effect
setMounted(true)
}, [])
diff --git a/src/hooks/useLinuxInit.ts b/src/hooks/useLinuxInit.ts
index 77a5d82..c549382 100644
--- a/src/hooks/useLinuxInit.ts
+++ b/src/hooks/useLinuxInit.ts
@@ -54,6 +54,7 @@ export function useLinuxInit(): UseLinuxInitReturn {
const savedHelper = localStorage.getItem(STORAGE_KEY_HELPER) as 'yay' | 'paru' | null;
if (savedDistro && distros.some(d => d.id === savedDistro)) {
+ // eslint-disable-next-line react-hooks/set-state-in-effect
setSelectedDistroState(savedDistro);
}
@@ -76,7 +77,7 @@ export function useLinuxInit(): UseLinuxInitReturn {
if (savedHelper === 'paru') {
setSelectedHelper('paru');
}
- } catch (e) {
+ } catch {
// Ignore localStorage errors
}
setHydrated(true);
@@ -90,7 +91,7 @@ export function useLinuxInit(): UseLinuxInitReturn {
localStorage.setItem(STORAGE_KEY_APPS, JSON.stringify([...selectedApps]));
localStorage.setItem(STORAGE_KEY_YAY, hasYayInstalled.toString());
localStorage.setItem(STORAGE_KEY_HELPER, selectedHelper);
- } catch (e) {
+ } catch {
// Ignore localStorage errors
}
}, [selectedDistro, selectedApps, hasYayInstalled, selectedHelper, hydrated]);
diff --git a/src/hooks/useTheme.tsx b/src/hooks/useTheme.tsx
index f62c61e..c8a0a69 100644
--- a/src/hooks/useTheme.tsx
+++ b/src/hooks/useTheme.tsx
@@ -22,12 +22,7 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [hydrated, setHydrated] = useState(false)
useEffect(() => {
- // On mount, sync with localStorage and mark as hydrated
- const saved = localStorage.getItem('theme') as Theme | null
- if (saved && saved !== theme) {
- setTheme(saved)
- document.documentElement.classList.toggle('light', saved === 'light')
- }
+ // eslint-disable-next-line react-hooks/set-state-in-effect
setHydrated(true)
}, [])
diff --git a/src/lib/data.ts b/src/lib/data.ts
index 1df6101..6d9928d 100644
--- a/src/lib/data.ts
+++ b/src/lib/data.ts
@@ -153,7 +153,7 @@ export const apps: AppData[] = [
{ id: 'fd', name: 'fd', description: 'Simple, fast alternative to find command', category: 'CLI Tools', iconUrl: mdi('file-search-outline', '#56BE89'), targets: { ubuntu: 'fd-find', debian: 'fd-find', arch: 'fd', fedora: 'fd-find', opensuse: 'fd', nix: 'fd' }, unavailableReason: 'fd is a CLI tool and not available via Flatpak or Snap.' },
{ id: 'tmux', name: 'tmux', description: 'Terminal session manager and multiplexer', category: 'CLI Tools', iconUrl: si('tmux', '#1BB91F'), targets: { ubuntu: 'tmux', debian: 'tmux', arch: 'tmux', fedora: 'tmux', opensuse: 'tmux', nix: 'tmux' }, unavailableReason: 'tmux is a CLI tool and not available via Flatpak or Snap.' },
- { id: 'zellij', name: 'Zellij', description: 'Modern terminal multiplexer with layout system', category: 'Terminal', iconUrl: mdi('view-split-vertical', '#A48CF4'), targets: { ubuntu: 'zellij', arch: 'zellij', fedora: 'zellij', opensuse: 'zellij', nix: 'zellij' }, unavailableReason: 'Not in Debian repos. Install via `cargo install zellij` or see [zellij.dev](https://zellij.dev/documentation/installation.html).' },
+ { id: 'zellij', name: 'Zellij', description: 'Modern terminal multiplexer with layout system', category: 'CLI Tools', iconUrl: mdi('view-split-vertical', '#A48CF4'), targets: { ubuntu: 'zellij', arch: 'zellij', fedora: 'zellij', opensuse: 'zellij', nix: 'zellij' }, unavailableReason: 'Not in Debian repos. Install via `cargo install zellij` or see [zellij.dev](https://zellij.dev/documentation/installation.html).' },
{ id: 'superfile', name: 'Superfile', description: 'Modern terminal file manager with TUI', category: 'CLI Tools', iconUrl: mdi('folder-multiple', '#FFD93D'), targets: { arch: 'superfile', nix: 'superfile' }, unavailableReason: 'Install via `go install` or see [superfile.dev](https://superfile.dev/getting-started/installation/).' },
{ id: 'rsync', name: 'rsync', description: 'Fast incremental file transfer and sync tool', category: 'CLI Tools', iconUrl: mdi('sync', '#2ECC71'), targets: { ubuntu: 'rsync', debian: 'rsync', arch: 'rsync', fedora: 'rsync', opensuse: 'rsync', nix: 'rsync' }, unavailableReason: 'rsync is a CLI tool and not available via Flatpak or Snap.' },
{ id: 'uv', name: 'uv', description: 'Fast Python package manager', category: 'Dev: Languages', iconUrl: si('astral', '#5C4EE5'), targets: { arch: 'uv', nix: 'uv' }, unavailableReason: 'Install via `curl -LsSf https://astral.sh/uv/install.sh | sh`. See [installation guide](https://docs.astral.sh/uv/getting-started/installation/).' },