Initial commit

This commit is contained in:
xggz
2026-03-06 22:56:13 +08:00
commit 54d1097b41
273 changed files with 92457 additions and 0 deletions

View File

@@ -0,0 +1,181 @@
"use client"
import { useState, useEffect } from "react"
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
} from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Plus, Trash2 } from "lucide-react"
import type { FolderCommand } from "@/lib/types"
import {
createFolderCommand,
updateFolderCommand,
deleteFolderCommand,
} from "@/lib/tauri"
interface CommandDraft {
id: number | null
name: string
command: string
deleted: boolean
}
interface CommandManageDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
folderId: number
commands: FolderCommand[]
onSaved: () => void
}
export function CommandManageDialog({
open,
onOpenChange,
folderId,
commands,
onSaved,
}: CommandManageDialogProps) {
const [drafts, setDrafts] = useState<CommandDraft[]>([])
const [saving, setSaving] = useState(false)
useEffect(() => {
if (open) {
setDrafts(
commands.map((c) => ({
id: c.id,
name: c.name,
command: c.command,
deleted: false,
}))
)
}
}, [open, commands])
const addDraft = () => {
setDrafts((prev) => [
...prev,
{ id: null, name: "", command: "", deleted: false },
])
}
const updateDraft = (
index: number,
field: "name" | "command",
value: string
) => {
setDrafts((prev) =>
prev.map((d, i) => (i === index ? { ...d, [field]: value } : d))
)
}
const removeDraft = (index: number) => {
setDrafts((prev) =>
prev.map((d, i) => (i === index ? { ...d, deleted: true } : d))
)
}
const handleSave = async () => {
setSaving(true)
try {
for (const draft of drafts) {
if (draft.deleted && draft.id != null) {
await deleteFolderCommand(draft.id)
} else if (draft.deleted) {
continue
} else if (draft.id == null && draft.name && draft.command) {
await createFolderCommand(folderId, draft.name, draft.command)
} else if (draft.id != null) {
const orig = commands.find((c) => c.id === draft.id)
if (
orig &&
(orig.name !== draft.name || orig.command !== draft.command)
) {
await updateFolderCommand(draft.id, draft.name, draft.command)
}
}
}
onSaved()
onOpenChange(false)
} catch (err) {
console.error("Failed to save commands:", err)
} finally {
setSaving(false)
}
}
const visibleDrafts = drafts.filter((d) => !d.deleted)
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-lg">
<DialogHeader>
<DialogTitle>Manage Commands</DialogTitle>
</DialogHeader>
<ScrollArea className="max-h-72">
<div className="space-y-2 pr-2">
{visibleDrafts.length === 0 && (
<p className="text-sm text-muted-foreground text-center py-4">
No commands yet
</p>
)}
{drafts.map(
(draft, index) =>
!draft.deleted && (
<div key={index} className="flex items-center gap-2">
<Input
placeholder="Name"
value={draft.name}
onChange={(e) =>
updateDraft(index, "name", e.target.value)
}
className="h-8 text-sm flex-1"
/>
<Input
placeholder="Command"
value={draft.command}
onChange={(e) =>
updateDraft(index, "command", e.target.value)
}
className="h-8 text-sm font-mono flex-[2]"
/>
<Button
variant="ghost"
size="icon"
className="h-8 w-8 shrink-0"
onClick={() => removeDraft(index)}
>
<Trash2 className="h-3.5 w-3.5 text-destructive" />
</Button>
</div>
)
)}
</div>
</ScrollArea>
<DialogFooter className="flex items-center justify-between sm:justify-between">
<Button variant="outline" size="sm" onClick={addDraft}>
<Plus className="h-3.5 w-3.5 mr-1" />
Add
</Button>
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={() => onOpenChange(false)}
>
Cancel
</Button>
<Button size="sm" onClick={handleSave} disabled={saving}>
{saving ? "Saving..." : "Save"}
</Button>
</div>
</DialogFooter>
</DialogContent>
</Dialog>
)
}