Download apps/vscode/webview-ui/src/components/WorkDirModal.tsx from SaylorTwift/kimi-code: direct link, hf CLI and curl.
- Browser
- Download file 4.72 kB
-
https://huggingface.co/SaylorTwift/kimi-code/resolve/main/apps/vscode/webview-ui/src/components/WorkDirModal.tsx
- Command line
-
hf download hf://SaylorTwift/kimi-code/apps/vscode/webview-ui/src/components/WorkDirModal.tsx
-
curl -L -o WorkDirModal.tsx https://huggingface.co/SaylorTwift/kimi-code/resolve/main/apps/vscode/webview-ui/src/components/WorkDirModal.tsx
4.72 kB
| import { useState } from "react"; | |
| import { useQuery } from "@tanstack/react-query"; | |
| import { IconFolder, IconFolderOpen, IconCheck, IconHome } from "@tabler/icons-react"; | |
| import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; | |
| import { Button } from "@/components/ui/button"; | |
| import { useSettingsStore, useChatStore } from "@/stores"; | |
| import { bridge } from "@/services"; | |
| import { cn } from "@/lib/utils"; | |
| export function WorkDirModal() { | |
| const { workDirModalOpen, setWorkDirModalOpen, currentWorkDir, workspaceRoot, setCurrentWorkDir } = useSettingsStore(); | |
| const { startNewConversation } = useChatStore(); | |
| const [loading, setLoading] = useState(false); | |
| const { data: workDirs = [], isPending, isError } = useQuery({ | |
| queryKey: ["registeredWorkDirs"], | |
| queryFn: () => bridge.getRegisteredWorkDirs(), | |
| enabled: workDirModalOpen, | |
| }); | |
| const handleSelect = async (dir: string | null) => { | |
| setLoading(true); | |
| try { | |
| const result = await bridge.setWorkDir(dir); | |
| if (result.ok) { | |
| setCurrentWorkDir(result.workDir === workspaceRoot ? null : result.workDir); | |
| await startNewConversation(); | |
| setWorkDirModalOpen(false); | |
| } | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const handleBrowse = async () => { | |
| setLoading(true); | |
| try { | |
| const result = await bridge.browseWorkDir(); | |
| if (result.ok && result.workDir) { | |
| setCurrentWorkDir(result.workDir === workspaceRoot ? null : result.workDir); | |
| await startNewConversation(); | |
| setWorkDirModalOpen(false); | |
| } | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const displayPath = (fullPath: string) => { | |
| if (!workspaceRoot) return fullPath; | |
| if (fullPath === workspaceRoot) return fullPath.split("/").pop() || fullPath; | |
| return fullPath.replace(workspaceRoot, "."); | |
| }; | |
| const isSelected = (dir: string) => { | |
| if (!currentWorkDir) return dir === workspaceRoot; | |
| return dir === currentWorkDir; | |
| }; | |
| return ( | |
| <Dialog open={workDirModalOpen} onOpenChange={setWorkDirModalOpen}> | |
| <DialogContent className="sm:max-w-md"> | |
| <DialogHeader> | |
| <DialogTitle>Select Working Directory</DialogTitle> | |
| </DialogHeader> | |
| <div className="space-y-1 max-h-64 overflow-y-auto -mx-1 px-1"> | |
| {isPending ? ( | |
| <div className="px-3 py-8 text-center text-xs text-muted-foreground">Loading…</div> | |
| ) : isError ? ( | |
| <div className="px-3 py-8 text-center text-xs text-destructive">Failed to load working directories</div> | |
| ) : ( | |
| workDirs.map((dir) => ( | |
| <button | |
| key={dir} | |
| onClick={() => { | |
| void handleSelect(dir === workspaceRoot ? null : dir); | |
| }} | |
| disabled={loading} | |
| className={cn( | |
| "w-full flex items-center gap-2 px-3 py-2 rounded-md text-left text-sm transition-colors", | |
| isSelected(dir) ? "bg-accent" : "hover:bg-accent/50", | |
| loading && "opacity-50 cursor-not-allowed", | |
| )} | |
| > | |
| {dir === workspaceRoot ? ( | |
| <IconHome className="size-4 text-muted-foreground shrink-0" /> | |
| ) : ( | |
| <IconFolder className="size-4 text-muted-foreground shrink-0" /> | |
| )} | |
| <span className="flex-1 truncate">{displayPath(dir)}</span> | |
| {isSelected(dir) && <IconCheck className="size-4 text-blue-500 shrink-0" />} | |
| {dir === workspaceRoot && <span className="text-xs text-muted-foreground">(root)</span>} | |
| </button> | |
| )) | |
| )} | |
| </div> | |
| <DialogFooter className="sm:justify-between gap-2"> | |
| <div className="flex gap-2"> | |
| <Button | |
| variant="outline" | |
| size="sm" | |
| onClick={() => { | |
| void handleBrowse(); | |
| }} | |
| disabled={loading} | |
| > | |
| <IconFolderOpen className="size-4 mr-1.5" /> | |
| Browse... | |
| </Button> | |
| {currentWorkDir && ( | |
| <Button | |
| variant="outline" | |
| size="sm" | |
| onClick={() => { | |
| void handleSelect(null); | |
| }} | |
| disabled={loading} | |
| > | |
| <IconHome className="size-4 mr-1.5" /> | |
| Reset | |
| </Button> | |
| )} | |
| </div> | |
| <Button variant="ghost" size="sm" onClick={() => setWorkDirModalOpen(false)}> | |
| Cancel | |
| </Button> | |
| </DialogFooter> | |
| </DialogContent> | |
| </Dialog> | |
| ); | |
| } | |