This commit is contained in:
Timothy Jaeryang Baek
2026-03-01 02:08:41 -06:00
parent ae28e7d245
commit 234306ff57
3 changed files with 90 additions and 2 deletions
+51 -2
View File
@@ -19,6 +19,7 @@
type FileEntry
} from '$lib/apis/terminal';
import Folder from '../icons/Folder.svelte';
import Document from '../icons/Document.svelte';
import Spinner from '../common/Spinner.svelte';
import ConfirmDialog from '../common/ConfirmDialog.svelte';
@@ -50,6 +51,9 @@
let creatingFolder = false;
let newFolderName = '';
let newFolderInput: HTMLInputElement;
let creatingFile = false;
let newFileName = '';
let newFileInput: HTMLInputElement;
// ── Delete confirmation ──────────────────────────────────────────────
let deleteTarget: { path: string; name: string } | null = null;
@@ -257,6 +261,31 @@
await loadDir(currentPath);
};
// ── File creation ────────────────────────────────────────────────────
const startNewFile = async () => {
creatingFile = true;
newFileName = '';
await tick();
newFileInput?.focus();
};
const submitNewFile = async () => {
const name = newFileName.trim();
creatingFile = false;
newFileName = '';
if (!name) return;
const terminal = selectedTerminal;
if (!terminal) return;
const emptyFile = new File([''], name, { type: 'application/octet-stream' });
const result = await uploadToTerminal(terminal.url, terminal.key, currentPath, emptyFile);
toast[result ? 'success' : 'error'](
$i18n.t(result ? 'File created' : 'Failed to create file')
);
await loadDir(currentPath);
};
// ── Delete ───────────────────────────────────────────────────────────
const handleDelete = async (path: string, name: string) => {
const terminal = selectedTerminal;
@@ -397,6 +426,7 @@
onNavigate={loadDir}
onRefresh={() => loadDir(currentPath)}
onNewFolder={startNewFolder}
onNewFile={startNewFile}
onUploadFiles={handleUploadFiles}
/>
@@ -422,7 +452,7 @@
<div class="flex justify-center pt-8"><Spinner className="size-4" /></div>
{:else if error}
<div class="p-4 text-xs">{error}</div>
{:else if entries.length === 0 && !creatingFolder}
{:else if entries.length === 0 && !creatingFolder && !creatingFile}
<div class="flex flex-col items-center justify-center gap-1.5 py-12 text-center">
<Folder className="size-6 text-gray-200 dark:text-gray-700" />
<div class="text-xs text-gray-400 dark:text-gray-500">
@@ -454,8 +484,27 @@
/>
</div>
{/if}
{#if creatingFile}
<div class="flex items-center gap-2 px-3 py-1.5">
<Document className="size-4 shrink-0 text-gray-400 dark:text-gray-500" />
<input
bind:this={newFileInput}
bind:value={newFileName}
class="flex-1 text-xs bg-transparent border border-gray-200 dark:border-gray-700 rounded px-1.5 py-0.5 outline-none focus:border-blue-400 dark:focus:border-blue-500"
placeholder={$i18n.t('File name')}
on:keydown={(e) => {
if (e.key === 'Enter') submitNewFile();
if (e.key === 'Escape') {
creatingFile = false;
newFileName = '';
}
}}
on:blur={submitNewFile}
/>
</div>
{/if}
{#if entries.length > 0 || creatingFolder}
{#if entries.length > 0 || creatingFolder || creatingFile}
<ul>
{#each entries as entry}
<FileEntryRow
@@ -3,6 +3,7 @@
import { tick } from 'svelte';
import Folder from '../../icons/Folder.svelte';
import NewFolderAlt from '../../icons/NewFolderAlt.svelte';
import FilePlusAlt from '../../icons/FilePlusAlt.svelte';
import Spinner from '../../common/Spinner.svelte';
import Tooltip from '../../common/Tooltip.svelte';
@@ -15,6 +16,7 @@
export let onNavigate: (path: string) => void = () => {};
export let onRefresh: () => void = () => {};
export let onNewFolder: () => void = () => {};
export let onNewFile: () => void = () => {};
export let onUploadFiles: (files: File[]) => void = () => {};
let uploadInput: HTMLInputElement;
@@ -83,6 +85,15 @@
<NewFolderAlt className="size-3.5" />
</button>
</Tooltip>
<Tooltip content={$i18n.t('New File')}>
<button
class="shrink-0 p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-400"
on:click={onNewFile}
aria-label={$i18n.t('New File')}
>
<FilePlusAlt className="size-3.5" />
</button>
</Tooltip>
<Tooltip content={$i18n.t('Upload')}>
<button
class="shrink-0 p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-400"
@@ -0,0 +1,28 @@
<script lang="ts">
export let className = 'size-4';
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
class={className}
>
<path
d="M4 12V2.6C4 2.26863 4.26863 2 4.6 2H16.2515C16.4106 2 16.5632 2.06321 16.6757 2.17574L19.8243 5.32426C19.9368 5.43679 20 5.5894 20 5.74853V21.4C20 21.7314 19.7314 22 19.4 22H11"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M16 2V5.4C16 5.73137 16.2686 6 16.6 6H20"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M1.99219 19H4.99219M7.99219 19H4.99219M4.99219 19V16M4.99219 19V22"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>