[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled

This commit is contained in:
2026-09-13 18:24:01 +08:00
commit 3c72efcb7f
3255 changed files with 907009 additions and 0 deletions

View File

@@ -0,0 +1,114 @@
import React from 'react';
import { Folder, Loader2 } from 'lucide-react';
import { Button } from '../ui/button';
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '../ui/dialog';
import { Input } from '../ui/input';
import { cn } from '../../lib/utils';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type SftpMoveToDialogProps = Record<string, any>;
export const SftpMoveToDialog: React.FC<SftpMoveToDialogProps> = ({
showMoveToDialog, setShowMoveToDialog, setMoveToPath, setMoveToError, setMoveToSuggestions,
setMoveToSuggestionIndex, t, moveToInputRef, moveToPath, fetchMoveToSuggestions,
moveToSuggestions, moveToSuggestionIndex, moveToError, isMoving, handleMoveToSubmit,
}) => (
<Dialog open={showMoveToDialog} onOpenChange={(open) => {
if (!open) {
setShowMoveToDialog(false);
setMoveToSuggestions([]);
setMoveToSuggestionIndex(-1);
}
}}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>{t('sftp.moveTo.title')}</DialogTitle>
</DialogHeader>
<div className="relative">
<Input
ref={moveToInputRef}
value={moveToPath}
onChange={(e) => {
const val = e.target.value;
setMoveToPath(val);
setMoveToError(null);
setMoveToSuggestionIndex(-1);
fetchMoveToSuggestions(val);
}}
onKeyDown={(e) => {
if (e.key === 'ArrowDown' && moveToSuggestions.length > 0) {
e.preventDefault();
setMoveToSuggestionIndex((i) => i < moveToSuggestions.length - 1 ? i + 1 : 0);
} else if (e.key === 'ArrowUp' && moveToSuggestions.length > 0) {
e.preventDefault();
setMoveToSuggestionIndex((i) => i > 0 ? i - 1 : moveToSuggestions.length - 1);
} else if (e.key === 'Tab' && moveToSuggestionIndex >= 0) {
e.preventDefault();
const selected = moveToSuggestions[moveToSuggestionIndex];
setMoveToPath(selected);
setMoveToError(null);
fetchMoveToSuggestions(selected);
} else if (e.key === 'Enter') {
e.preventDefault();
if (moveToSuggestionIndex >= 0 && moveToSuggestions[moveToSuggestionIndex]) {
const selected = moveToSuggestions[moveToSuggestionIndex];
setMoveToPath(selected);
setMoveToSuggestionIndex(-1);
setMoveToSuggestions([]);
setMoveToError(null);
} else {
void handleMoveToSubmit();
}
} else if (e.key === 'Escape') {
if (moveToSuggestions.length > 0) {
e.preventDefault();
e.stopPropagation();
setMoveToSuggestions([]);
setMoveToSuggestionIndex(-1);
}
// When no suggestions, let the Dialog handle ESC to close itself
}
}}
placeholder={t('sftp.moveTo.placeholder')}
autoFocus
className={moveToError ? 'border-destructive' : undefined}
/>
{moveToSuggestions.length > 0 && (
<div className="absolute left-0 right-0 top-full mt-1 z-50 rounded-md border bg-popover shadow-md max-h-48 overflow-y-auto">
{moveToSuggestions.map((suggestion, i) => (
<div
key={suggestion}
className={cn(
'px-3 py-1.5 text-sm cursor-pointer truncate',
i === moveToSuggestionIndex ? 'bg-accent text-accent-foreground' : 'hover:bg-accent/50',
)}
onMouseDown={(e) => {
e.preventDefault();
setMoveToPath(suggestion);
setMoveToSuggestions([]);
setMoveToSuggestionIndex(-1);
setMoveToError(null);
}}
>
<Folder size={12} className="inline mr-2 text-yellow-500" />
{suggestion}
</div>
))}
</div>
)}
</div>
{moveToError && (
<p className="text-xs text-destructive">{moveToError}</p>
)}
<DialogFooter>
<Button variant="outline" size="sm" onClick={() => setShowMoveToDialog(false)}>
{t('common.cancel')}
</Button>
<Button size="sm" disabled={!moveToPath.trim() || isMoving} onClick={() => void handleMoveToSubmit()}>
{isMoving && <Loader2 size={14} className="mr-2 animate-spin" />}
{t('sftp.moveTo.confirm')}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);