diff options
| author | 2026-02-25 01:32:51 +0800 | |
|---|---|---|
| committer | 2026-02-25 01:32:51 +0800 | |
| commit | 66668d85d603c5841d755a6023aa1925559fc6d4 (patch) | |
| tree | 485464148c76b0021efb55b7d2afd1c3004ceee0 /packages/ui-new/src/pages/settings-view.tsx.bk | |
| parent | a6773bd092db654360c599ca6b0108ea0e456e8c (diff) | |
| download | DropOut-66668d85d603c5841d755a6023aa1925559fc6d4.tar.gz DropOut-66668d85d603c5841d755a6023aa1925559fc6d4.zip | |
chore(workspace): replace legacy codes
Diffstat (limited to 'packages/ui-new/src/pages/settings-view.tsx.bk')
| -rw-r--r-- | packages/ui-new/src/pages/settings-view.tsx.bk | 1158 |
1 files changed, 0 insertions, 1158 deletions
diff --git a/packages/ui-new/src/pages/settings-view.tsx.bk b/packages/ui-new/src/pages/settings-view.tsx.bk deleted file mode 100644 index ac43d9b..0000000 --- a/packages/ui-new/src/pages/settings-view.tsx.bk +++ /dev/null @@ -1,1158 +0,0 @@ -import { open } from "@tauri-apps/plugin-dialog"; -import { - Coffee, - Download, - FileJson, - Loader2, - RefreshCw, - Upload, -} from "lucide-react"; -import { useEffect, useState } from "react"; -import { toast } from "sonner"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Checkbox } from "@/components/ui/checkbox"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { Separator } from "@/components/ui/separator"; -import { Switch } from "@/components/ui/switch"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { Textarea } from "@/components/ui/textarea"; -import { useSettingsStore } from "../stores/settings-store"; - -const effectOptions = [ - { value: "saturn", label: "Saturn" }, - { value: "constellation", label: "Network (Constellation)" }, -]; - -const logServiceOptions = [ - { value: "paste.rs", label: "paste.rs (Free, No Account)" }, - { value: "pastebin.com", label: "pastebin.com (Requires API Key)" }, -]; - -const llmProviderOptions = [ - { value: "ollama", label: "Ollama (Local)" }, - { value: "openai", label: "OpenAI (Remote)" }, -]; - -const languageOptions = [ - { value: "auto", label: "Auto (Match User)" }, - { value: "English", label: "English" }, - { value: "Chinese", label: "中文" }, - { value: "Japanese", label: "日本語" }, - { value: "Korean", label: "한국어" }, - { value: "Spanish", label: "Español" }, - { value: "French", label: "Français" }, - { value: "German", label: "Deutsch" }, - { value: "Russian", label: "Русский" }, -]; - -const ttsProviderOptions = [ - { value: "disabled", label: "Disabled" }, - { value: "piper", label: "Piper TTS (Local)" }, - { value: "edge", label: "Edge TTS (Online)" }, -]; - -const personas = [ - { - value: "default", - label: "Minecraft Expert (Default)", - prompt: - "You are a helpful Minecraft expert assistant. You help players with game issues, mod installation, performance optimization, and gameplay tips. Analyze any game logs provided and give concise, actionable advice.", - }, - { - value: "technical", - label: "Technical Debugger", - prompt: - "You are a technical support specialist for Minecraft. Focus strictly on analyzing logs, identifying crash causes, and providing technical solutions. Be precise and avoid conversational filler.", - }, - { - value: "concise", - label: "Concise Helper", - prompt: - "You are a direct and concise assistant. Provide answers in as few words as possible while remaining accurate. Use bullet points for lists.", - }, - { - value: "explain", - label: "Teacher / Explainer", - prompt: - "You are a patient teacher. Explain Minecraft concepts, redstone mechanics, and mod features in simple, easy-to-understand terms suitable for beginners.", - }, - { - value: "pirate", - label: "Pirate Captain", - prompt: - "You are a salty Minecraft Pirate Captain! Yarr! Speak like a pirate while helping the crew (the user) with their blocky adventures. Use terms like 'matey', 'landlubber', and 'treasure'.", - }, -]; - -export function SettingsView() { - const { - settings, - backgroundUrl, - javaInstallations, - isDetectingJava, - showJavaDownloadModal, - selectedDownloadSource, - javaCatalog, - isLoadingCatalog, - catalogError, - selectedMajorVersion, - selectedImageType, - showOnlyRecommended, - searchQuery, - isDownloadingJava, - downloadProgress, - javaDownloadStatus, - pendingDownloads, - ollamaModels, - openaiModels, - isLoadingOllamaModels, - isLoadingOpenaiModels, - ollamaModelsError, - openaiModelsError, - showConfigEditor, - rawConfigContent, - configFilePath, - configEditorError, - filteredReleases, - availableMajorVersions, - installStatus, - selectedRelease, - currentModelOptions, - loadSettings, - saveSettings, - detectJava, - selectJava, - openJavaDownloadModal, - closeJavaDownloadModal, - loadJavaCatalog, - refreshCatalog, - loadPendingDownloads, - selectMajorVersion, - downloadJava, - cancelDownload, - resumeDownloads, - openConfigEditor, - closeConfigEditor, - saveRawConfig, - loadOllamaModels, - loadOpenaiModels, - set, - setSetting, - setAssistantSetting, - setFeatureFlag, - } = useSettingsStore(); - - // Mark potentially-unused variables as referenced so TypeScript does not report - // them as unused in this file (they are part of the store API and used elsewhere). - // This is a no-op but satisfies the compiler. - void selectedDownloadSource; - void javaCatalog; - void javaDownloadStatus; - void pendingDownloads; - void ollamaModels; - void openaiModels; - void isLoadingOllamaModels; - void isLoadingOpenaiModels; - void ollamaModelsError; - void openaiModelsError; - void selectedRelease; - void loadJavaCatalog; - void loadPendingDownloads; - void cancelDownload; - void resumeDownloads; - void setFeatureFlag; - const [selectedPersona, setSelectedPersona] = useState("default"); - const [migrating, setMigrating] = useState(false); - const [activeTab, setActiveTab] = useState("appearance"); - - useEffect(() => { - loadSettings(); - detectJava(); - }, [loadSettings, detectJava]); - - useEffect(() => { - if (activeTab === "assistant") { - if (settings.assistant.llmProvider === "ollama") { - loadOllamaModels(); - } else if (settings.assistant.llmProvider === "openai") { - loadOpenaiModels(); - } - } - }, [ - activeTab, - settings.assistant.llmProvider, - loadOllamaModels, - loadOpenaiModels, - ]); - - const handleSelectBackground = async () => { - try { - const selected = await open({ - multiple: false, - filters: [ - { - name: "Images", - extensions: ["png", "jpg", "jpeg", "webp", "gif"], - }, - ], - }); - - if (selected && typeof selected === "string") { - setSetting("customBackgroundPath", selected); - saveSettings(); - } - } catch (e) { - console.error("Failed to select background:", e); - toast.error("Failed to select background"); - } - }; - - const handleClearBackground = () => { - setSetting("customBackgroundPath", null); - saveSettings(); - }; - - const handleApplyPersona = (value: string) => { - const persona = personas.find((p) => p.value === value); - if (persona) { - setAssistantSetting("systemPrompt", persona.prompt); - setSelectedPersona(value); - saveSettings(); - } - }; - - const handleResetSystemPrompt = () => { - const defaultPersona = personas.find((p) => p.value === "default"); - if (defaultPersona) { - setAssistantSetting("systemPrompt", defaultPersona.prompt); - setSelectedPersona("default"); - saveSettings(); - } - }; - - const handleRunMigration = async () => { - if (migrating) return; - setMigrating(true); - try { - await new Promise((resolve) => setTimeout(resolve, 2000)); - toast.success("Migration complete! Files migrated successfully"); - } catch (e) { - console.error("Migration failed:", e); - toast.error(`Migration failed: ${e}`); - } finally { - setMigrating(false); - } - }; - - return ( - <div className="h-full flex flex-col p-6 overflow-hidden"> - <div className="flex items-center justify-between mb-6"> - <h2 className="text-3xl font-black bg-clip-text text-transparent bg-linear-to-r dark:from-white dark:to-white/60 from-gray-900 to-gray-600"> - Settings - </h2> - - <Button - variant="outline" - size="sm" - onClick={openConfigEditor} - className="gap-2" - > - <FileJson className="h-4 w-4" /> - <span className="hidden sm:inline">Open JSON</span> - </Button> - </div> - - <Tabs - value={activeTab} - onValueChange={setActiveTab} - className="flex-1 overflow-hidden" - > - <TabsList className="grid grid-cols-4 mb-6"> - <TabsTrigger value="appearance">Appearance</TabsTrigger> - <TabsTrigger value="java">Java</TabsTrigger> - <TabsTrigger value="advanced">Advanced</TabsTrigger> - <TabsTrigger value="assistant">Assistant</TabsTrigger> - </TabsList> - - <ScrollArea className="flex-1 pr-2"> - <TabsContent value="appearance" className="space-y-6"> - <Card className="border-border"> - <CardHeader> - <CardTitle className="text-lg">Appearance</CardTitle> - </CardHeader> - <CardContent className="space-y-6"> - <div> - <Label className="mb-3">Custom Background Image</Label> - <div className="flex items-center gap-6"> - <div className="w-40 h-24 rounded-xl overflow-hidden bg-secondary border relative group shadow-lg"> - {backgroundUrl ? ( - <img - src={backgroundUrl} - alt="Background Preview" - className="w-full h-full object-cover" - onError={(e) => { - console.error("Failed to load image"); - e.currentTarget.style.display = "none"; - }} - /> - ) : ( - <div className="w-full h-full bg-linear-to-br from-emerald-900 via-zinc-900 to-indigo-950" /> - )} - {!backgroundUrl && ( - <div className="absolute inset-0 flex items-center justify-center text-xs text-white/50 bg-black/20"> - Default Gradient - </div> - )} - </div> - - <div className="flex flex-col gap-2"> - <Button - variant="outline" - onClick={handleSelectBackground} - > - Select Image - </Button> - {backgroundUrl && ( - <Button - variant="ghost" - className="text-red-500" - onClick={handleClearBackground} - > - Reset to Default - </Button> - )} - </div> - </div> - <p className="text-sm text-muted-foreground mt-3"> - Select an image from your computer to replace the default - gradient background. - </p> - </div> - - <Separator /> - - <div className="space-y-4"> - <div className="flex items-center justify-between"> - <div> - <Label className="text-base">Visual Effects</Label> - <p className="text-sm text-muted-foreground"> - Enable particle effects and animated gradients. - </p> - </div> - <Switch - checked={settings.enableVisualEffects} - onCheckedChange={(checked) => { - setSetting("enableVisualEffects", checked); - saveSettings(); - }} - /> - </div> - - {settings.enableVisualEffects && ( - <div className="pl-4 border-l-2 border-border"> - <div className="space-y-2"> - <Label>Theme Effect</Label> - <Select - value={settings.activeEffect} - onValueChange={(value) => { - setSetting("activeEffect", value); - saveSettings(); - }} - > - <SelectTrigger className="w-52"> - <SelectValue /> - </SelectTrigger> - <SelectContent> - {effectOptions.map((option) => ( - <SelectItem - key={option.value} - value={option.value} - > - {option.label} - </SelectItem> - ))} - </SelectContent> - </Select> - <p className="text-sm text-muted-foreground"> - Select the active visual theme. - </p> - </div> - </div> - )} - - <div className="flex items-center justify-between"> - <div> - <Label className="text-base">GPU Acceleration</Label> - <p className="text-sm text-muted-foreground"> - Enable GPU acceleration for the interface. - </p> - </div> - <Switch - checked={settings.enableGpuAcceleration} - onCheckedChange={(checked) => { - setSetting("enableGpuAcceleration", checked); - saveSettings(); - }} - /> - </div> - </div> - </CardContent> - </Card> - </TabsContent> - - <TabsContent value="java" className="space-y-6"> - <Card className="border-border"> - <CardHeader> - <CardTitle className="text-lg">Java Environment</CardTitle> - </CardHeader> - <CardContent className="space-y-6"> - <div> - <Label className="mb-2">Java Path</Label> - <div className="flex gap-2"> - <Input - value={settings.javaPath} - onChange={(e) => setSetting("javaPath", e.target.value)} - className="flex-1" - placeholder="java or full path to java executable" - /> - <Button - variant="outline" - onClick={() => detectJava()} - disabled={isDetectingJava} - > - {isDetectingJava ? ( - <Loader2 className="h-4 w-4 animate-spin" /> - ) : ( - "Detect" - )} - </Button> - </div> - <p className="text-sm text-muted-foreground mt-2"> - Path to Java executable. - </p> - </div> - - <div> - <Label className="mb-2">Memory Settings (MB)</Label> - <div className="grid grid-cols-2 gap-4"> - <div> - <Label htmlFor="min-memory" className="text-sm"> - Minimum Memory - </Label> - <Input - id="min-memory" - type="number" - value={settings.minMemory} - onChange={(e) => - setSetting( - "minMemory", - parseInt(e.target.value, 10) || 1024, - ) - } - min={512} - step={256} - /> - </div> - <div> - <Label htmlFor="max-memory" className="text-sm"> - Maximum Memory - </Label> - <Input - id="max-memory" - type="number" - value={settings.maxMemory} - onChange={(e) => - setSetting( - "maxMemory", - parseInt(e.target.value, 10) || 2048, - ) - } - min={1024} - step={256} - /> - </div> - </div> - <p className="text-sm text-muted-foreground mt-2"> - Memory allocation for Minecraft. - </p> - </div> - - <Separator /> - - <div> - <div className="flex items-center justify-between mb-4"> - <Label className="text-base"> - Detected Java Installations - </Label> - <Button - variant="outline" - size="sm" - onClick={() => detectJava()} - disabled={isDetectingJava} - > - <RefreshCw - className={`h-4 w-4 mr-2 ${isDetectingJava ? "animate-spin" : ""}`} - /> - Rescan - </Button> - </div> - - {javaInstallations.length === 0 ? ( - <div className="text-center py-8 text-muted-foreground border rounded-lg"> - <Coffee className="h-12 w-12 mx-auto mb-4 opacity-30" /> - <p>No Java installations detected</p> - </div> - ) : ( - <div className="space-y-2"> - {javaInstallations.map((installation) => ( - <Card - key={installation.path} - className={`p-3 cursor-pointer transition-colors ${ - settings.javaPath === installation.path - ? "border-primary bg-primary/5" - : "" - }`} - onClick={() => selectJava(installation.path)} - > - <div className="flex items-center justify-between"> - <div> - <div className="font-medium flex items-center gap-2"> - <Coffee className="h-4 w-4" /> - {installation.version} - </div> - <div className="text-sm text-muted-foreground font-mono"> - {installation.path} - </div> - </div> - {settings.javaPath === installation.path && ( - <div className="h-5 w-5 text-primary">✓</div> - )} - </div> - </Card> - ))} - </div> - )} - - <div className="mt-4"> - <Button - variant="default" - className="w-full" - onClick={openJavaDownloadModal} - > - <Download className="h-4 w-4 mr-2" /> - Download Java - </Button> - </div> - </div> - </CardContent> - </Card> - </TabsContent> - - <TabsContent value="advanced" className="space-y-6"> - <Card className="border-border"> - <CardHeader> - <CardTitle className="text-lg">Advanced Settings</CardTitle> - </CardHeader> - <CardContent className="space-y-6"> - <div> - <Label className="mb-2">Download Threads</Label> - <Input - type="number" - value={settings.downloadThreads} - onChange={(e) => - setSetting( - "downloadThreads", - parseInt(e.target.value, 10) || 32, - ) - } - min={1} - max={64} - /> - <p className="text-sm text-muted-foreground mt-2"> - Number of concurrent downloads. - </p> - </div> - - <div> - <Label className="mb-2">Log Upload Service</Label> - <Select - value={settings.logUploadService} - onValueChange={(value) => { - setSetting("logUploadService", value as any); - saveSettings(); - }} - > - <SelectTrigger> - <SelectValue /> - </SelectTrigger> - <SelectContent> - {logServiceOptions.map((option) => ( - <SelectItem key={option.value} value={option.value}> - {option.label} - </SelectItem> - ))} - </SelectContent> - </Select> - </div> - - {settings.logUploadService === "pastebin.com" && ( - <div> - <Label className="mb-2">Pastebin API Key</Label> - <Input - type="password" - value={settings.pastebinApiKey || ""} - onChange={(e) => - setSetting("pastebinApiKey", e.target.value || null) - } - placeholder="Enter your Pastebin API key" - /> - </div> - )} - - <Separator /> - - <div className="space-y-4"> - <div className="flex items-center justify-between"> - <div> - <Label className="text-base">Use Shared Caches</Label> - <p className="text-sm text-muted-foreground"> - Share downloaded assets between instances. - </p> - </div> - <Switch - checked={settings.useSharedCaches} - onCheckedChange={(checked) => { - setSetting("useSharedCaches", checked); - saveSettings(); - }} - /> - </div> - - {!settings.useSharedCaches && ( - <div className="flex items-center justify-between"> - <div> - <Label className="text-base"> - Keep Legacy Per-Instance Storage - </Label> - <p className="text-sm text-muted-foreground"> - Maintain separate cache folders for compatibility. - </p> - </div> - <Switch - checked={settings.keepLegacyPerInstanceStorage} - onCheckedChange={(checked) => { - setSetting("keepLegacyPerInstanceStorage", checked); - saveSettings(); - }} - /> - </div> - )} - - {settings.useSharedCaches && ( - <div className="mt-4"> - <Button - variant="outline" - className="w-full" - onClick={handleRunMigration} - disabled={migrating} - > - {migrating ? ( - <Loader2 className="h-4 w-4 mr-2 animate-spin" /> - ) : ( - <Upload className="h-4 w-4 mr-2" /> - )} - {migrating - ? "Migrating..." - : "Migrate to Shared Caches"} - </Button> - </div> - )} - </div> - </CardContent> - </Card> - </TabsContent> - - <TabsContent value="assistant" className="space-y-6"> - <Card className="border-border"> - <CardHeader> - <CardTitle className="text-lg">AI Assistant</CardTitle> - </CardHeader> - <CardContent className="space-y-6"> - <div className="flex items-center justify-between"> - <div> - <Label className="text-base">Enable Assistant</Label> - <p className="text-sm text-muted-foreground"> - Enable the AI assistant for help with Minecraft issues. - </p> - </div> - <Switch - checked={settings.assistant.enabled} - onCheckedChange={(checked) => { - setAssistantSetting("enabled", checked); - saveSettings(); - }} - /> - </div> - - {settings.assistant.enabled && ( - <> - <div> - <Label className="mb-2">LLM Provider</Label> - <Select - value={settings.assistant.llmProvider} - onValueChange={(value) => { - setAssistantSetting("llmProvider", value as any); - saveSettings(); - }} - > - <SelectTrigger> - <SelectValue /> - </SelectTrigger> - <SelectContent> - {llmProviderOptions.map((option) => ( - <SelectItem key={option.value} value={option.value}> - {option.label} - </SelectItem> - ))} - </SelectContent> - </Select> - </div> - - <div> - <Label className="mb-2">Model</Label> - <Select - value={ - settings.assistant.llmProvider === "ollama" - ? settings.assistant.ollamaModel - : settings.assistant.openaiModel - } - onValueChange={(value) => { - if (settings.assistant.llmProvider === "ollama") { - setAssistantSetting("ollamaModel", value); - } else { - setAssistantSetting("openaiModel", value); - } - saveSettings(); - }} - > - <SelectTrigger> - <SelectValue /> - </SelectTrigger> - <SelectContent> - {currentModelOptions.map((model) => ( - <SelectItem key={model.value} value={model.value}> - {model.label} - </SelectItem> - ))} - </SelectContent> - </Select> - </div> - - {settings.assistant.llmProvider === "ollama" && ( - <div> - <Label className="mb-2">Ollama Endpoint</Label> - <Input - value={settings.assistant.ollamaEndpoint} - onChange={(e) => { - setAssistantSetting( - "ollamaEndpoint", - e.target.value, - ); - saveSettings(); - }} - placeholder="http://localhost:11434" - /> - </div> - )} - - {settings.assistant.llmProvider === "openai" && ( - <> - <div> - <Label className="mb-2">OpenAI API Key</Label> - <Input - type="password" - value={settings.assistant.openaiApiKey || ""} - onChange={(e) => { - setAssistantSetting( - "openaiApiKey", - e.target.value || null, - ); - saveSettings(); - }} - placeholder="Enter your OpenAI API key" - /> - </div> - <div> - <Label className="mb-2">OpenAI Endpoint</Label> - <Input - value={settings.assistant.openaiEndpoint} - onChange={(e) => { - setAssistantSetting( - "openaiEndpoint", - e.target.value, - ); - saveSettings(); - }} - placeholder="https://api.openai.com/v1" - /> - </div> - </> - )} - - <div> - <Label className="mb-2">Response Language</Label> - <Select - value={settings.assistant.responseLanguage} - onValueChange={(value) => { - setAssistantSetting("responseLanguage", value); - saveSettings(); - }} - > - <SelectTrigger> - <SelectValue /> - </SelectTrigger> - <SelectContent> - {languageOptions.map((option) => ( - <SelectItem key={option.value} value={option.value}> - {option.label} - </SelectItem> - ))} - </SelectContent> - </Select> - </div> - - <div> - <Label className="mb-2">Assistant Persona</Label> - <Select - value={selectedPersona} - onValueChange={handleApplyPersona} - > - <SelectTrigger> - <SelectValue /> - </SelectTrigger> - <SelectContent> - {personas.map((persona) => ( - <SelectItem - key={persona.value} - value={persona.value} - > - {persona.label} - </SelectItem> - ))} - </SelectContent> - </Select> - <div className="mt-2"> - <Button - variant="outline" - size="sm" - onClick={handleResetSystemPrompt} - > - Reset to Default - </Button> - </div> - </div> - - <div> - <Label className="mb-2">System Prompt</Label> - - <Textarea - value={settings.assistant.systemPrompt} - onChange={(e) => { - setAssistantSetting("systemPrompt", e.target.value); - saveSettings(); - }} - rows={6} - className="font-mono text-sm" - /> - </div> - - <div> - <Label className="mb-2">Text-to-Speech</Label> - - <Select - value={settings.assistant.ttsProvider} - onValueChange={(value) => { - setAssistantSetting("ttsProvider", value); - saveSettings(); - }} - > - <SelectTrigger> - <SelectValue /> - </SelectTrigger> - - <SelectContent> - {ttsProviderOptions.map((option) => ( - <SelectItem key={option.value} value={option.value}> - {option.label} - </SelectItem> - ))} - </SelectContent> - </Select> - </div> - </> - )} - </CardContent> - </Card> - </TabsContent> - </ScrollArea> - </Tabs> - - {/* Java Download Modal */} - <Dialog - open={showJavaDownloadModal} - onOpenChange={closeJavaDownloadModal} - > - <DialogContent className="max-w-4xl max-h-[80vh] overflow-hidden"> - <DialogHeader> - <DialogTitle>Download Java</DialogTitle> - <DialogDescription> - Download and install Java for Minecraft. - </DialogDescription> - </DialogHeader> - - <div className="grid grid-cols-1 md:grid-cols-3 gap-4"> - <div className="space-y-4"> - <div> - <Label className="mb-2">Java Version</Label> - <Select - value={selectedMajorVersion?.toString() || ""} - onValueChange={(v) => selectMajorVersion(parseInt(v, 10))} - > - <SelectTrigger> - <SelectValue placeholder="Select version" /> - </SelectTrigger> - <SelectContent> - {availableMajorVersions.map((version) => ( - <SelectItem key={version} value={version.toString()}> - Java {version} - </SelectItem> - ))} - </SelectContent> - </Select> - </div> - - <div> - <Label className="mb-2">Type</Label> - <Select - value={selectedImageType} - onValueChange={(v) => set({ selectedImageType: v as any })} - > - <SelectTrigger> - <SelectValue /> - </SelectTrigger> - <SelectContent> - <SelectItem value="jre">JRE (Runtime)</SelectItem> - <SelectItem value="jdk">JDK (Development)</SelectItem> - </SelectContent> - </Select> - </div> - - <div className="flex items-center space-x-2"> - <Checkbox - id="recommended" - checked={showOnlyRecommended} - onCheckedChange={(checked) => - set({ showOnlyRecommended: !!checked }) - } - /> - <Label htmlFor="recommended">Show only LTS/Recommended</Label> - </div> - - <div> - <Label className="mb-2">Search</Label> - <Input - placeholder="Search versions..." - value={searchQuery} - onChange={(e) => set({ searchQuery: e.target.value })} - /> - </div> - - <Button - variant="outline" - size="sm" - onClick={refreshCatalog} - disabled={isLoadingCatalog} - > - <RefreshCw - className={`h-4 w-4 mr-2 ${isLoadingCatalog ? "animate-spin" : ""}`} - /> - Refresh Catalog - </Button> - </div> - - <div className="md:col-span-2"> - <ScrollArea className="h-75 pr-4"> - {isLoadingCatalog ? ( - <div className="flex items-center justify-center h-full"> - <Loader2 className="h-8 w-8 animate-spin" /> - </div> - ) : catalogError ? ( - <div className="text-red-500 p-4">{catalogError}</div> - ) : filteredReleases.length === 0 ? ( - <div className="text-muted-foreground p-4 text-center"> - No Java versions found - </div> - ) : ( - <div className="space-y-2"> - {filteredReleases.map((release) => { - const status = installStatus( - release.majorVersion, - release.imageType, - ); - return ( - <Card - key={`${release.majorVersion}-${release.imageType}`} - className="p-3 cursor-pointer hover:bg-accent" - onClick={() => - selectMajorVersion(release.majorVersion) - } - > - <div className="flex items-center justify-between"> - <div> - <div className="font-medium"> - Java {release.majorVersion}{" "} - {release.imageType.toUpperCase()} - </div> - <div className="text-sm text-muted-foreground"> - {release.releaseName} • {release.architecture}{" "} - {release.architecture} - </div> - </div> - <div className="flex items-center gap-2"> - {release.isLts && ( - <Badge variant="secondary">LTS</Badge> - )} - {status === "installed" && ( - <Badge variant="default">Installed</Badge> - )} - {status === "available" && ( - <Button - variant="ghost" - size="sm" - onClick={(e) => { - e.stopPropagation(); - selectMajorVersion(release.majorVersion); - downloadJava(); - }} - > - <Download className="h-3 w-3 mr-1" /> - Download - </Button> - )} - </div> - </div> - </Card> - ); - })} - </div> - )} - </ScrollArea> - </div> - </div> - - {isDownloadingJava && downloadProgress && ( - <div className="mt-4 p-4 border rounded-lg"> - <div className="flex justify-between items-center mb-2"> - <span className="text-sm font-medium"> - {downloadProgress.fileName} - </span> - <span className="text-sm text-muted-foreground"> - {Math.round(downloadProgress.percentage)}% - </span> - </div> - <div className="w-full bg-secondary h-2 rounded-full overflow-hidden"> - <div - className="bg-primary h-full transition-all duration-300" - style={{ width: `${downloadProgress.percentage}%` }} - /> - </div> - </div> - )} - - <DialogFooter> - <Button - variant="outline" - onClick={closeJavaDownloadModal} - disabled={isDownloadingJava} - > - Cancel - </Button> - {selectedMajorVersion && ( - <Button - onClick={() => downloadJava()} - disabled={isDownloadingJava} - > - {isDownloadingJava ? ( - <> - <Loader2 className="mr-2 h-4 w-4 animate-spin" /> - Downloading... - </> - ) : ( - <> - <Download className="mr-2 h-4 w-4" /> - Download Java {selectedMajorVersion} - </> - )} - </Button> - )} - </DialogFooter> - </DialogContent> - </Dialog> - - {/* Config Editor Modal */} - <Dialog open={showConfigEditor} onOpenChange={closeConfigEditor}> - <DialogContent className="max-w-4xl max-h-[80vh] overflow-hidden"> - <DialogHeader> - <DialogTitle>Edit Configuration</DialogTitle> - <DialogDescription> - Edit the raw JSON configuration file. - </DialogDescription> - </DialogHeader> - - <div className="text-sm text-muted-foreground mb-2"> - File: {configFilePath} - </div> - - {configEditorError && ( - <div className="text-red-500 p-3 bg-red-50 dark:bg-red-950/30 rounded-md"> - {configEditorError} - </div> - )} - - <Textarea - value={rawConfigContent} - onChange={(e) => set({ rawConfigContent: e.target.value })} - className="font-mono text-sm h-100 resize-none" - spellCheck={false} - /> - - <DialogFooter> - <Button variant="outline" onClick={closeConfigEditor}> - Cancel - </Button> - <Button onClick={() => saveRawConfig()}>Save Changes</Button> - </DialogFooter> - </DialogContent> - </Dialog> - </div> - ); -} |