File manager: selection, keyboard shortcuts, rename modal, security, localization

- Selection model: click=select, dblclick=open, Ctrl+click toggle,
  Shift+click range select, Ctrl+A, Esc to clear
- Keyboard shortcuts: Enter, Ctrl+Enter, F2, Backspace (navigate up),
  Delete/Backspace (delete with confirm)
- Rename modal replaces prompt() with validation via backend ValidateName
- Context menu: Open, Open External, Show in Folder, Rename, Duplicate,
  Copy, Cut, Delete — all with SVG icons and Russian labels
- Backend security: vaultPath/absPathSafe helpers prevent path traversal,
  validateName rejects .. / \ null bytes empty overlong names
- MoveNode auto-renames on name conflict (copy style)
- Duplicate uses (copy) (copy 2) suffix pattern
- Russian localization: all file type labels, preview messages, tooltips
- FilePreviewModal: fixed broken {/if} tag
This commit is contained in:
2026-06-01 01:35:45 +08:00
parent 2487d3bbaa
commit a4ae22c445
9 changed files with 446 additions and 125 deletions
+7 -7
View File
@@ -43,7 +43,7 @@
</div>
<div class="preview-meta">{formatFileSize(item.size)} · {formatMimeType(item.mime)}</div>
<div class="preview-actions">
<button class="action-btn" on:click={handleOpenExternal} title="Open in external program" aria-label="Open externally">
<button class="action-btn" on:click={handleOpenExternal} title="Открыть во внешней программе" aria-label="Открыть внешне">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
<polyline points="15 3 21 3 21 9"/>
@@ -60,11 +60,11 @@
</header>
<div class="preview-body">
{#if loading}
<div class="preview-status"><p>Loading preview...</p></div>
<div class="preview-status"><p>Загрузка...</p></div>
{:else if error}
<div class="preview-status">
<p>{error}</p>
<button class="btn btn-sm" on:click={handleOpenExternal}>Open in external program</button>
<button class="btn btn-sm" on:click={handleOpenExternal}>Открыть во внешней программе</button>
</div>
{:else if showImage && content}
<div class="preview-image-container">
@@ -79,14 +79,14 @@
</div>
{:else}
<div class="preview-status">
<p>PDF preview is not available in this environment.</p>
<button class="btn btn-sm" on:click={handleOpenExternal}>Open in external program</button>
<p>Предпросмотр PDF недоступен.</p>
<button class="btn btn-sm" on:click={handleOpenExternal}>Открыть во внешней программе</button>
</div>
{/if}
{:else}
<div class="preview-status">
<p>Preview is not available for this file type.</p>
<button class="btn btn-sm" on:click={handleOpenExternal}>Open in external program</button>
<p>Предпросмотр недоступен для этого типа файлов.</p>
<button class="btn btn-sm" on:click={handleOpenExternal}>Открыть во внешней программе</button>
</div>
{/if}
</div>