diff --git a/AppImage/components/terminal-panel.tsx b/AppImage/components/terminal-panel.tsx index 3426e1b..b0af09e 100644 --- a/AppImage/components/terminal-panel.tsx +++ b/AppImage/components/terminal-panel.tsx @@ -31,7 +31,40 @@ function getWebSocketUrl(): string { } } -const commonCommands = [ +interface CheatSheetResult { + command: string + description: string + examples: string[] +} + +const proxmoxCommands = [ + { cmd: "pvesh get /nodes", desc: "List all Proxmox nodes" }, + { cmd: "pvesh get /nodes/{node}/qemu", desc: "List VMs on a node" }, + { cmd: "pvesh get /nodes/{node}/lxc", desc: "List LXC containers on a node" }, + { cmd: "pvesh get /nodes/{node}/storage", desc: "List storage on a node" }, + { cmd: "pvesh get /nodes/{node}/network", desc: "List network interfaces" }, + { cmd: "qm list", desc: "List all QEMU/KVM virtual machines" }, + { cmd: "qm start ", desc: "Start a virtual machine" }, + { cmd: "qm stop ", desc: "Stop a virtual machine" }, + { cmd: "qm shutdown ", desc: "Shutdown a virtual machine gracefully" }, + { cmd: "qm status ", desc: "Show VM status" }, + { cmd: "qm config ", desc: "Show VM configuration" }, + { cmd: "qm snapshot ", desc: "Create VM snapshot" }, + { cmd: "pct list", desc: "List all LXC containers" }, + { cmd: "pct start ", desc: "Start LXC container" }, + { cmd: "pct stop ", desc: "Stop LXC container" }, + { cmd: "pct enter ", desc: "Enter LXC container console" }, + { cmd: "pct config ", desc: "Show container configuration" }, + { cmd: "pvesm status", desc: "Show storage status" }, + { cmd: "pvesm list ", desc: "List storage content" }, + { cmd: "pveperf", desc: "Test Proxmox system performance" }, + { cmd: "pveversion", desc: "Show Proxmox VE version" }, + { cmd: "systemctl status pve-cluster", desc: "Check cluster status" }, + { cmd: "pvecm status", desc: "Show cluster status" }, + { cmd: "pvecm nodes", desc: "List cluster nodes" }, + { cmd: "zpool status", desc: "Show ZFS pool status" }, + { cmd: "zpool list", desc: "List all ZFS pools" }, + { cmd: "zfs list", desc: "List all ZFS datasets" }, { cmd: "ls -la", desc: "List all files with details" }, { cmd: "cd /path/to/dir", desc: "Change directory" }, { cmd: "mkdir dirname", desc: "Create new directory" }, @@ -83,9 +116,12 @@ export const TerminalPanel: React.FC = ({ websocketUrl, onCl const [isConnected, setIsConnected] = useState(false) const [searchModalOpen, setSearchModalOpen] = useState(false) const [searchQuery, setSearchQuery] = useState("") - const [filteredCommands, setFilteredCommands] = useState(commonCommands) + const [filteredCommands, setFilteredCommands] = useState>(proxmoxCommands) const [lastKeyPressed, setLastKeyPressed] = useState(null) const [isMobile, setIsMobile] = useState(false) + const [isSearching, setIsSearching] = useState(false) + const [searchResults, setSearchResults] = useState([]) + const [useOnline, setUseOnline] = useState(true) useEffect(() => { setIsMobile(window.innerWidth < 768) @@ -96,15 +132,88 @@ export const TerminalPanel: React.FC = ({ websocketUrl, onCl useEffect(() => { if (!searchQuery.trim()) { - setFilteredCommands(commonCommands) + setFilteredCommands(proxmoxCommands) + setSearchResults([]) return } - const query = searchQuery.toLowerCase() - const filtered = commonCommands.filter( - (item) => item.cmd.toLowerCase().includes(query) || item.desc.toLowerCase().includes(query), - ) - setFilteredCommands(filtered) - }, [searchQuery]) + + const query = searchQuery.toLowerCase().trim() + + if (useOnline && query.length > 2) { + setIsSearching(true) + + const searchCheatSh = async () => { + try { + const response = await fetch(`https://cheat.sh/${encodeURIComponent(query)}?T`, { + signal: AbortSignal.timeout(5000), + }) + + if (response.ok) { + const text = await response.text() + + const lines = text.split("\n").filter((line) => line.trim()) + const results: CheatSheetResult[] = [] + + let currentCommand = "" + let currentDesc = "" + let currentExamples: string[] = [] + + for (const line of lines) { + if (line.startsWith("#")) { + if (currentCommand) { + results.push({ + command: currentCommand, + description: currentDesc, + examples: currentExamples, + }) + } + currentCommand = line.replace(/^#\s*/, "").trim() + currentDesc = "" + currentExamples = [] + } else if (line.trim().startsWith("-")) { + currentDesc += line.trim().replace(/^-\s*/, "") + " " + } else if (line.trim() && !line.includes("cheat.sh")) { + currentExamples.push(line.trim()) + } + } + + if (currentCommand) { + results.push({ + command: currentCommand, + description: currentDesc, + examples: currentExamples, + }) + } + + setSearchResults(results) + setFilteredCommands([]) // Clear local results when using online + } else { + throw new Error("API request failed") + } + } catch (error) { + console.log("[v0] Cheat.sh unavailable, using offline mode") + setUseOnline(false) + const filtered = proxmoxCommands.filter( + (item) => item.cmd.toLowerCase().includes(query) || item.desc.toLowerCase().includes(query), + ) + setFilteredCommands(filtered) + setSearchResults([]) + } finally { + setIsSearching(false) + } + } + + const timer = setTimeout(searchCheatSh, 500) + return () => clearTimeout(timer) + } else { + const filtered = proxmoxCommands.filter( + (item) => item.cmd.toLowerCase().includes(query) || item.desc.toLowerCase().includes(query), + ) + setFilteredCommands(filtered) + setSearchResults([]) + setIsSearching(false) + } + }, [searchQuery, useOnline]) useEffect(() => { if (typeof window === "undefined") return @@ -318,6 +427,7 @@ export const TerminalPanel: React.FC = ({ websocketUrl, onCl } setSearchModalOpen(false) setSearchQuery("") + setSearchResults([]) } return ( @@ -451,21 +561,78 @@ export const TerminalPanel: React.FC = ({ websocketUrl, onCl - + - Search Commands + + Search Commands + + {useOnline ? "🌐 Online (cheat.sh)" : "📦 Offline Mode"} + +
setSearchQuery(e.target.value)} - className="w-full" + className="w-full bg-zinc-900 border-zinc-700 focus:border-blue-500 focus:ring-1 focus:ring-blue-500" autoFocus /> + + {isSearching && ( +
+
+

Searching cheat.sh...

+
+ )} +
- {filteredCommands.length > 0 ? ( + {searchResults.length > 0 ? ( + searchResults.map((result, index) => ( +
+
+
+
+ {result.command} + {result.description &&

{result.description}

} +
+
+ {result.examples.length > 0 && ( +
+

Examples:

+ {result.examples.slice(0, 3).map((example, idx) => ( +
{ + e.stopPropagation() + handleSendCommand(example) + }} + className="flex items-center justify-between p-2 rounded bg-zinc-900/50 hover:bg-zinc-900 group" + > + {example} + +
+ ))} +
+ )} +
+
+ )) + ) : filteredCommands.length > 0 ? ( filteredCommands.map((item, index) => (
= ({ websocketUrl, onCl
)) - ) : ( -
No commands found matching "{searchQuery}"
- )} + ) : !isSearching ? ( +
+ {searchQuery ? `No commands found for "${searchQuery}"` : "Type to search commands..."} +
+ ) : null} +
+ +
+ 💡 Tip: Search for any Linux command (tar, grep, docker, etc.) or Proxmox commands (qm, pct, pvesh)