Skip to content

Commit 637cd13

Browse files
committed
refac
1 parent 1dc647f commit 637cd13

2 files changed

Lines changed: 91 additions & 6 deletions

File tree

src/lib/components/chat/FileNav.svelte

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,25 @@
487487
await loadDir(currentPath);
488488
};
489489
490+
// ── Rename ──────────────────────────────────────────────────────────
491+
const handleRename = async (oldPath: string, newName: string) => {
492+
const terminal = selectedTerminal;
493+
if (!terminal || !newName) return;
494+
495+
const dir = oldPath.substring(0, oldPath.lastIndexOf('/') + 1) || currentPath;
496+
const destination = `${dir}${newName}`;
497+
498+
if (oldPath === destination) return;
499+
500+
const result = await moveEntry(terminal.url, terminal.key, oldPath, destination);
501+
if ('error' in result) {
502+
toast.error(result.error);
503+
} else {
504+
toast.success($i18n.t('Renamed to {{name}}', { name: newName }));
505+
}
506+
await loadDir(currentPath);
507+
};
508+
490509
// ── Lifecycle ────────────────────────────────────────────────────────
491510
onMount(async () => {
492511
const terminal = getTerminal();
@@ -1041,6 +1060,7 @@
10411060
onDownload={downloadFile}
10421061
onDelete={requestDelete}
10431062
onMove={handleMove}
1063+
onRename={handleRename}
10441064
/>
10451065
{/each}
10461066
</ul>

src/lib/components/chat/FileNav/FileEntryRow.svelte

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
<script lang="ts">
2-
import { getContext } from 'svelte';
2+
import { getContext, tick } from 'svelte';
33
import { formatFileSize } from '$lib/utils';
44
import type { FileEntry } from '$lib/apis/terminal';
55
66
import Dropdown from '$lib/components/common/Dropdown.svelte';
77
import Folder from '../../icons/Folder.svelte';
88
import EllipsisHorizontal from '../../icons/EllipsisHorizontal.svelte';
99
import GarbageBin from '../../icons/GarbageBin.svelte';
10+
import Pencil from '../../icons/Pencil.svelte';
1011
1112
const i18n = getContext('i18n');
1213
@@ -19,8 +20,40 @@
1920
export let onDownload: (path: string) => void = () => {};
2021
export let onDelete: (path: string, name: string) => void = () => {};
2122
export let onMove: (source: string, destFolder: string) => void = () => {};
23+
export let onRename: (oldPath: string, newName: string) => void = () => {};
2224
2325
let dragOverFolder = false;
26+
27+
// ── Rename state ─────────────────────────────────────────────────────
28+
let renaming = false;
29+
let renameValue = '';
30+
let renameInput: HTMLInputElement;
31+
32+
const startRename = async () => {
33+
renameValue = entry.name;
34+
renaming = true;
35+
await tick();
36+
renameInput?.focus();
37+
// Select the name without extension for files
38+
if (entry.type === 'file') {
39+
const dotIdx = entry.name.lastIndexOf('.');
40+
renameInput?.setSelectionRange(0, dotIdx > 0 ? dotIdx : entry.name.length);
41+
} else {
42+
renameInput?.select();
43+
}
44+
};
45+
46+
const submitRename = () => {
47+
const newName = renameValue.trim();
48+
renaming = false;
49+
if (!newName || newName === entry.name) return;
50+
onRename(`${currentPath}${entry.name}`, newName);
51+
};
52+
53+
const cancelRename = () => {
54+
renaming = false;
55+
renameValue = '';
56+
};
2457
</script>
2558

2659
<li class="group">
@@ -83,7 +116,12 @@
83116
);
84117
}
85118
}}
86-
on:click={() => onOpen(entry)}
119+
on:click={() => {
120+
if (!renaming) onOpen(entry);
121+
}}
122+
on:dblclick|preventDefault|stopPropagation={() => {
123+
startRename();
124+
}}
87125
>
88126
{#if entry.type === 'directory'}
89127
<Folder className="size-4 shrink-0 text-blue-400 dark:text-blue-300" />
@@ -103,10 +141,25 @@
103141
/>
104142
</svg>
105143
{/if}
106-
<span class="flex-1 text-xs text-gray-800 dark:text-gray-200 truncate">
107-
{entry.name}
108-
</span>
109-
{#if entry.type === 'file' && entry.size !== undefined}
144+
{#if renaming}
145+
<!-- svelte-ignore a11y-click-events-have-key-events -->
146+
<input
147+
bind:this={renameInput}
148+
bind:value={renameValue}
149+
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 text-gray-800 dark:text-gray-200 min-w-0"
150+
on:keydown={(e) => {
151+
if (e.key === 'Enter') { e.preventDefault(); submitRename(); }
152+
if (e.key === 'Escape') { e.preventDefault(); cancelRename(); }
153+
}}
154+
on:blur={submitRename}
155+
on:click|stopPropagation
156+
/>
157+
{:else}
158+
<span class="flex-1 text-xs text-gray-800 dark:text-gray-200 truncate">
159+
{entry.name}
160+
</span>
161+
{/if}
162+
{#if entry.type === 'file' && entry.size !== undefined && !renaming}
110163
<span class="text-xs text-gray-400 shrink-0">{formatFileSize(entry.size)}</span>
111164
{/if}
112165
</button>
@@ -151,6 +204,18 @@
151204
</button>
152205
{/if}
153206

207+
<button
208+
type="button"
209+
class="select-none flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition items-center gap-2 text-sm"
210+
on:click={(e) => {
211+
e.stopPropagation();
212+
startRename();
213+
}}
214+
>
215+
<Pencil className="size-4" strokeWidth="1.5" />
216+
<div class="flex items-center">{$i18n.t('Rename')}</div>
217+
</button>
218+
154219
<button
155220
type="button"
156221
class="select-none flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition items-center gap-2 text-sm"

0 commit comments

Comments
 (0)