From acdbbdfa55c2da53c51d880a9b591bd078a73ef8 Mon Sep 17 00:00:00 2001 From: mirivlad Date: Tue, 16 Jun 2026 03:00:24 +0800 Subject: [PATCH] refactor(frontend): extract FilesTab safely MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New component: frontend/src/lib/components/files/FilesTab.svelte - Owns all file tab state: loadingFiles, currentFolderId, folderStack, fileItems, preview*, clipboard, selectedIds, dragIds, importing, importSummary, etc. - Public API via bind:this: resetToNode(), addFile(), loadFolder(), openFileById(), focusItem(), handleFilesKeydown(), resetState() - Events: openNote, refreshParent, error - Inline modals: rename, confirm, import dialog, file preview - App.svelte changes: - Removed all file-specific state variables and functions - Preview state renamed to trashPreview* (for trash preview only) - Files tab inline markup replaced with component - Overview 'Add file' button delegates to filesTabRef.addFile() - openInboxArtifact, navigateToFile, openActivityTarget, openSearchResult delegate to filesTabRef for file operations - Node rename (tree context menu) uses separate openNodeRename/submitNodeRename functions (file rename is now in FilesTab) - closeTrashPreview for trash file preview modal - Build: npm run build ✅, go test ./... ✅, build.sh gui ✅ --- frontend/src/App.svelte | 729 +-- frontend/src/App.svelte.bak | 4794 +++++++++++++++++ .../src/lib/components/files/FilesTab.svelte | 734 +++ 3 files changed, 5622 insertions(+), 635 deletions(-) create mode 100644 frontend/src/App.svelte.bak create mode 100644 frontend/src/lib/components/files/FilesTab.svelte diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 5934ef0..1115143 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -18,6 +18,7 @@ import { t } from './lib/i18n' import NoteEditorPanel from './lib/components/notes/NoteEditorPanel.svelte' import InternalLinkPicker from './lib/components/notes/InternalLinkPicker.svelte' + import FilesTab from './lib/components/files/FilesTab.svelte' // ===== Wails v2 API call helper ===== function wailsCall(method, ...args) { @@ -79,6 +80,7 @@ let notes = [] let noteEditor = null let noteEditorPanel = undefined; // bind:this ref for NoteEditorPanel + let filesTabRef = undefined; // bind:this ref for FilesTab let noteViewMode = 'edit' let showLinkModal = false let linkModalLabel = '' @@ -158,26 +160,14 @@ { id: 'launch_app', label: t('action.launchApp') }, ] let loading = true - let importing = false - let importSummary = null - let showImportDialog = false - let pendingImportPath = '' - let pendingImportParent = '' - let pendingImportMode = 'copy' let treeItems = [] let expanded = {} let childrenMap = {} - let loadingFiles = false - let currentFolderId = null - let folderStack = [] - let fileItems = [] - let previewItem = null - let previewContent = '' - let previewLoading = false - let previewError = '' - let clipboard = { items: [], mode: 'copy' } - let selectedIds = [] - let dragIds = [] + + let trashPreviewItem = null + let trashPreviewContent = '' + let trashPreviewLoading = false + let trashPreviewError = '' let dropRootValid = false let inboxDropValid = false let captureDropActive = false @@ -302,12 +292,11 @@ function closeTopModalForBack() { if (showConfirm) { closeConfirm(); return true } if (showSettings) { closeSettings(); return true } - if (previewItem) { closePreview(); return true } + if (trashPreviewItem) { closeTrashPreview(); return true } if (assignInboxItem) { closeAssignInbox(); return true } if (editingLink) { closeEditLink(); return true } if (showRename) { showRename = false; return true } if (showWorklogModal) { closeWorklogModal(); return true } - if (showImportDialog) { cancelImport(); return true } if (showCreateAction) { cancelCreateAction(); return true } if (showCreateNote) { cancelCreateNote(); return true } if (showCreateNode) { cancelCreateNode(); return true } @@ -326,8 +315,8 @@ } await selectNode(node) activeTab = snapshot.tab || 'overview' - if (activeTab === 'files' && fileItems.length === 0) { - await loadFolder(node.id) + if (activeTab === 'files') { + filesTabRef?.resetToNode?.(node.id) } return true } else if (snapshot.section) { @@ -373,7 +362,7 @@ if (activeTab === tabId) return rememberNavigation() activeTab = tabId - if (tabId === 'files' && selectedNode && fileItems.length === 0 && !currentFolderId) loadFolder(selectedNode.id) + if (tabId === 'files' && selectedNode) filesTabRef?.resetToNode?.(selectedNode.id) } // ===== Lifecycle ===== @@ -521,13 +510,6 @@ actions = [] worklog = [] suggestions = [] - fileItems = [] - folderStack = [] - currentFolderId = null - previewItem = null - previewContent = '' - selectedIds = [] - dragIds = [] resetTrashBrowser() noteEditor = null showCreateNode = false @@ -552,292 +534,63 @@ } async function loadTree(nodeID) { - loadingFiles = true try { treeItems = await wailsCall('ListItems', nodeID) || [] } catch (e) { treeItems = [] } - loadingFiles = false } - // ===== Folder navigation ===== + // ===== Node rename (for tree context menu) ===== + function openNodeRename(id, currentName) { + renameId = id + renameValue = currentName + renameError = '' + showRename = true + } - async function loadFolder(folderId) { - loadingFiles = true + async function submitNodeRename() { + const name = renameValue.trim() + if (!name) { renameError = t('rename.emptyError'); return } try { - let items = await wailsCall('ListItems', folderId) || [] - items.sort((a, b) => { - if (a.type !== b.type) return a.type === 'folder' ? -1 : 1 - return (a.name || '').localeCompare(b.name || '') - }) - fileItems = items + await wailsCall('ValidateName', name) } catch (e) { - fileItems = [] + renameError = t('rename.invalidError') + return } - loadingFiles = false - } - - async function navigateToFolder(folderId) { - if (!selectedNode) return - rememberNavigation() + showRename = false + const id = renameId + renameId = '' try { - const node = await wailsCall('GetNodeDetail', folderId) - if (node) { - folderStack = [...folderStack, { id: folderId, name: node.title }] + await wailsCall('RenameNode', id, name) + if (selectedNode && selectedNode.id === id) { + selectedNode = { ...selectedNode, title: name } } - } catch (e) { - folderStack = [...folderStack, { id: folderId, name: '...' }] - } - currentFolderId = folderId - expanded = { ...expanded, [folderId]: true } - const children = await wailsCall('ListWorkspaceChildren', folderId) || [] - setNodeChildren(workspaceTree, folderId, children) - workspaceTree = [...workspaceTree] - await loadFolder(folderId) - } - - function navigateBack() { - rememberNavigation() - if (folderStack.length < 2) { - // Go back to root - folderStack = [] - currentFolderId = null - loadFolder(selectedNode.id) - } else { - const target = folderStack[folderStack.length - 2] - folderStack = folderStack.slice(0, -1) - currentFolderId = target.id - loadFolder(target.id) - } - } - - function navigateToBreadcrumb(index) { - const target = folderStack[index] - folderStack = folderStack.slice(0, index + 1) - currentFolderId = target.id - loadFolder(target.id) - } - - // ===== File preview ===== - - async function openPreview(item) { - // For .md files: check if linked to a note, open note editor instead of preview modal - if (item && item.fileId && isMarkdownFile(item)) { - try { - const action = await wailsCall('CheckFileAction', item.fileId) - if (action.action === 'note') { - await openNote({ id: action.noteId, title: action.noteTitle }) - return - } - if (action.action === 'external') { - await wailsCall('OpenFile', item.fileId) - return - } - // 'preview' → fall through to normal preview - } catch (e) { - console.warn('CheckFileAction failed, falling back to preview:', e) - } - } - - previewItem = item - previewContent = '' - previewError = '' - previewLoading = true - - try { - if (needsBase64Preview(item)) { - previewContent = await wailsCall('GetFileBase64', item.fileId) || '' - } else if (needsTextPreview(item)) { - previewContent = await wailsCall('ReadFileText', item.fileId) || '' - } - } catch (e) { - previewError = String(e) - } - previewLoading = false - } - - function closePreview() { - previewItem = null - previewContent = '' - previewError = '' - } - - // ===== File operations ===== - - async function createFile() { - const name = prompt(t('file.namePrompt')) - if (!name || !name.trim()) return - try { - const parentId = currentFolderId || selectedNode.id - await wailsCall('CreateEmptyFile', parentId, name.trim()) - await loadFolder(parentId) - await refreshParentNode(parentId) + await reloadTreePreservingExpanded() } catch (e) { error = String(e) } } - async function duplicateItem(id) { - try { - await wailsCall('DuplicateNode', id) - const parentId = currentFolderId || selectedNode.id - await loadFolder(parentId) - await refreshParentNode(parentId) - } catch (e) { - error = String(e) - } + function cancelNodeRename() { + showRename = false + renameId = '' + renameValue = '' + renameError = '' } - function renameItem(id) { - const item = fileItems.find(x => x.id === id) - if (item) openRename(item.id, item.name) + function onNodeRenameKeydown(e) { + if (e.key === 'Enter') submitNodeRename() + else renameError = '' } - function cutItem(id) { - clipboard = { items: [id], mode: 'cut' } + // ===== Trash preview ===== + function closeTrashPreview() { + trashPreviewItem = null + trashPreviewContent = '' + trashPreviewError = '' } - function copyItem(id) { - clipboard = { items: [id], mode: 'copy' } - } - - async function pasteItem() { - if (clipboard.items.length === 0) return - const targetId = currentFolderId || selectedNode.id - try { - if (clipboard.mode === 'copy') { - for (const id of clipboard.items) { - await wailsCall('DuplicateNode', id) - } - } else { - for (const id of clipboard.items) { - await wailsCall('MoveNode', id, targetId) - } - } - clipboard = { items: [], mode: 'copy' } - await loadFolder(targetId) - } catch (e) { - error = String(e) - } - } - - // ===== Selection ===== - - function toggleSelection(id) { - if (selectedIds.includes(id)) { - selectedIds = selectedIds.filter(x => x !== id) - } else { - selectedIds = [...selectedIds, id] - } - } - - function selectOne(id) { - selectedIds = [id] - } - - function selectAll() { - selectedIds = fileItems.map(x => x.id) - } - - function rangeSelect(id) { - if (fileItems.length === 0) return - const lastId = selectedIds.length > 0 ? selectedIds[selectedIds.length - 1] : fileItems[0].id - const lastIdx = fileItems.findIndex(x => x.id === lastId) - const curIdx = fileItems.findIndex(x => x.id === id) - if (lastIdx === -1 || curIdx === -1) return - const start = Math.min(lastIdx, curIdx) - const end = Math.max(lastIdx, curIdx) - const range = fileItems.slice(start, end + 1).map(x => x.id) - const set = new Set(selectedIds) - range.forEach(x => set.add(x)) - selectedIds = [...set] - } - - function clearSelection() { - selectedIds = [] - } - - function getTargetIds(ids) { - return ids.length > 0 ? ids : fileItems.map(x => x.id) - } - - async function deleteSelected() { - const ids = getTargetIds(selectedIds) - const item = fileItems.find(x => x.id === ids[0]) - let label - if (ids.length === 1 && item?.type === 'folder') { - label = t('delete.folder') - } else if (ids.length === 1) { - label = t('delete.file') - } else { - label = t('delete.files', { count: ids.length }) - } - openConfirm({ - title: t('delete.confirmTitle'), - message: t('delete.confirmMessage') + ' ' + label + '?', - confirmText: t('common.delete'), - danger: true, - onConfirm: async () => { - for (const id of ids) { - try { - await wailsCall('DeleteFileOrFolder', id) - } catch (e) { error = String(e) } - } - selectedIds = [] - const reloadId = currentFolderId || selectedNode.id - await loadFolder(reloadId) - } - }) - } - - function cutSelected() { - const ids = getTargetIds(selectedIds) - clipboard = { items: ids, mode: 'cut' } - selectedIds = [] - } - - function copySelected() { - const ids = getTargetIds(selectedIds) - clipboard = { items: ids, mode: 'copy' } - selectedIds = [] - } - - // ===== Drag-and-drop ===== - - function onDragStart(e, id) { - e.stopPropagation() - const ids = selectedIds.includes(id) ? selectedIds : [id] - dragIds = ids - e.dataTransfer.effectAllowed = 'move' - e.dataTransfer.setData('text/plain', ids.join(',')) - } - - function onDragOver(e, folderId) { - const item = fileItems.find(x => x.id === folderId) - if (item && item.type === 'folder') { - e.preventDefault() - e.stopPropagation() - e.dataTransfer.dropEffect = 'move' - } - } - - async function onDrop(e, folderId) { - e.preventDefault() - e.stopPropagation() - if (dragIds.length === 0) return - for (const id of dragIds) { - try { - await wailsCall('MoveNode', id, folderId) - } catch (e) { error = String(e) } - } - dragIds = [] - selectedIds = [] - await loadFolder(currentFolderId || selectedNode.id) - } - - // ===== Keyboard ===== - function isEditableTarget(target) { if (!target || !(target instanceof Element)) return false return !!target.closest('input, textarea, select, [contenteditable="true"], [contenteditable=""]') @@ -857,109 +610,14 @@ return } - if (activeTab !== 'files') return - - if (e.ctrlKey || e.metaKey) { - if (e.key === 'c' || e.key === 'C') { e.preventDefault(); copySelected() } - else if (e.key === 'x' || e.key === 'X') { e.preventDefault(); cutSelected() } - else if (e.key === 'v' || e.key === 'V') { e.preventDefault(); pasteItem() } - else if (e.key === 'a' || e.key === 'A') { e.preventDefault(); selectAll() } - else if (e.key === 'o' || e.key === 'O') { e.preventDefault(); openSelectedExternal() } - else if (e.key === 'Enter') { e.preventDefault(); openSelected() } - } else if (e.key === 'Enter') { - e.preventDefault() - openSelected() - } else if (e.key === 'Delete') { - if (previewItem) { e.preventDefault(); closePreview(); return } - if (selectedIds.length > 0) { e.preventDefault(); deleteSelected(); return } - } else if (e.key === 'Escape') { - if (previewItem) { closePreview(); return } - if (selectedIds.length > 0) { clearSelection(); return } - } else if (e.key === 'F2') { - e.preventDefault() - openRenameForSelection() + if (activeTab === 'files' && filesTabRef) { + return filesTabRef.handleFilesKeydown(e) } } - function openSelected() { - if (selectedIds.length === 1) { - const item = fileItems.find(x => x.id === selectedIds[0]) - if (item) { - if (item.type === 'folder') { - navigateToFolder(item.id) - } else { - openPreview(item) - } - } - } - } + // ===== Rename modal ===== - function openSelectedExternal() { - if (selectedIds.length === 1) { - const item = fileItems.find(x => x.id === selectedIds[0]) - if (item && item.fileId) { - wailsCall('OpenFile', item.fileId) - } - } - } - - // ===== Rename modal ===== - - function openRename(id, currentName) { - renameId = id - renameValue = currentName - renameError = '' - showRename = true - } - - function openRenameForSelection() { - if (selectedIds.length === 1) { - const item = fileItems.find(x => x.id === selectedIds[0]) - if (item) { - openRename(item.id, item.name) - } - } - } - - async function submitRename() { - const name = renameValue.trim() - if (!name) { renameError = t('rename.emptyError'); return } - try { - await wailsCall('ValidateName', name) - } catch (e) { - renameError = t('rename.invalidError') - return - } - showRename = false - const id = renameId - renameId = '' - try { - await wailsCall('RenameNode', id, name) - if (selectedNode && selectedNode.id === id) { - selectedNode = { ...selectedNode, title: name } - } - await reloadTreePreservingExpanded() - if (currentFolderId) { - await loadFolder(currentFolderId) - } - } catch (e) { - error = String(e) - } - } - - function cancelRename() { - showRename = false - renameId = '' - renameValue = '' - renameError = '' - } - - function onRenameKeydown(e) { - if (e.key === 'Enter') submitRename() - else renameError = '' - } - - // ===== Confirm modal ===== + // ===== Confirm modal ===== function openConfirm(opts) { confirmTitle = opts.title || t('common.confirm') @@ -1111,7 +769,7 @@ // ===== Node operations from context menu ===== function openRenameForNode(node) { - openRename(node.id, node.title) + openNodeRename(node.id, node.title) closeContextMenu() } @@ -1184,12 +842,8 @@ selectedSection = '' selectedNode = item activeTab = 'files' - folderStack = [] - currentFolderId = null - selectedIds = [] - previewItem = null await loadTabData(item.id) - await loadFolder(item.id) + filesTabRef?.resetToNode?.(item.id) return } @@ -1199,7 +853,8 @@ if (!record) throw new Error('file record not found') const preview = fileRecordToPreviewItem(item, record) if (canPreviewFile(preview)) { - await openPreview(preview) + activeTab = 'files' + filesTabRef?.openFileById?.(item.id) } else { await wailsCall('OpenFile', preview.fileId) } @@ -1508,14 +1163,8 @@ await selectNode(parent) } setActiveTab('files') - await loadFolder(parentId) - // Find the file in the loaded fileItems and open preview - const fileItem = fileItems.find(f => f.id === id) - if (fileItem) { - await openPreview(fileItem) - } else { - showVerstakToastMessage(t('note.internal.fileFound', { title: node.title })) - } + filesTabRef?.resetToNode?.(parentId) + filesTabRef?.openFileById?.(id) } else { showVerstakToastMessage(t('note.internal.fileFound', { title: node.title })) } @@ -1685,22 +1334,22 @@ } async function openTrashFilePreview(node) { - previewItem = { name: node.title, type: 'file', mime: 'text/plain', size: 0, fileId: node.id } - previewContent = '' - previewError = '' - previewLoading = true + trashPreviewItem = { name: node.title, type: 'file', mime: 'text/plain', size: 0, fileId: node.id } + trashPreviewContent = '' + trashPreviewError = '' + trashPreviewLoading = true try { if (node.trashFsPath) { - previewContent = await wailsCall('ReadTrashFile', node.trashFsPath) || '' + trashPreviewContent = await wailsCall('ReadTrashFile', node.trashFsPath) || '' } else { - previewContent = await wailsCall('ReadTrashFileContent', node.id) || '' + trashPreviewContent = await wailsCall('ReadTrashFileContent', node.id) || '' } const ext = (node.title || '').split('.').pop().toLowerCase() if (['png','jpg','jpeg','gif','webp','bmp','svg'].includes(ext)) { - previewContent = 'data:image/' + (ext === 'svg' ? 'svg+xml' : ext) + ';base64,' + btoa(previewContent) + trashPreviewContent = 'data:image/' + (ext === 'svg' ? 'svg+xml' : ext) + ';base64,' + btoa(previewContent) } - } catch (e) { previewError = String(e) } - previewLoading = false + } catch (e) { trashPreviewError = String(e) } + trashPreviewLoading = false } function toggleTrashSelection(id) { @@ -1980,88 +1629,7 @@ } // ===== Files ===== - async function addFile() { - const path = await wailsCall('PickFile') - if (!path) return - const parentId = currentFolderId || selectedNode.id - await startImport(parentId, path) - } - - async function addFolder() { - const path = await wailsCall('PickDirectory') - if (!path) return - const parentId = currentFolderId || selectedNode.id - await startImport(parentId, path) - } - - async function startImport(parentID, sourcePath) { - importing = true - try { - const summary = await wailsCall('PreviewImport', sourcePath) - importSummary = summary - pendingImportPath = sourcePath - pendingImportParent = parentID - showImportDialog = true - } catch (e) { - error = String(e) - } - importing = false - } - - async function confirmImport(mode) { - try { - const parentId = pendingImportParent || selectedNode.id - const result = mode === 'copy' - ? await wailsCall('AddPathCopy', parentId, pendingImportPath) - : await wailsCall('AddPathLink', parentId, pendingImportPath) - showImportDialog = false - importSummary = null - folderStack = [] - currentFolderId = null - await Promise.all([ - loadTabData(parentId), - loadFolder(parentId), - refreshParentNode(parentId), - ]) - } catch (e) { - error = String(e) - } - } - - function cancelImport() { - showImportDialog = false - importSummary = null - } - - async function deleteFile({ id, type }) { - const label = type === 'folder' ? t('delete.folder') : t('delete.file') - openConfirm({ - title: t('delete.confirmTitle'), - message: t('delete.confirmMessage') + ' ' + label + '?', - confirmText: t('common.delete'), - danger: true, - onConfirm: async () => { - try { - await wailsCall('DeleteFileOrFolder', id) - files = files.filter(f => f.nodeId !== id) - const reloadId = currentFolderId || selectedNode.id - await loadFolder(reloadId) - } catch (e) { - error = String(e) - } - } - }) - } - - async function openSelectedFile(fileID) { - try { - await wailsCall('OpenFile', fileID) - } catch (e) { - error = String(e) - } - } - - // ===== Drag-and-drop ===== + // ===== Drag-and-drop ===== async function onFilesDropped(paths) { try { if (!paths || paths.length === 0) return @@ -2342,7 +1910,6 @@ } } function hasExternalCaptureData(dataTransfer) { - if (dragIds.length > 0) return false const types = Array.from(dataTransfer?.types || []) return types.includes('Files') || types.includes('text/uri-list') || @@ -2669,20 +2236,17 @@ try { const detail = await wailsCall('GetNodeDetail', target.targetId) if (detail && detail.parent_id) { - await loadFolder(detail.parent_id) - const fileItem = fileItems.find(f => f.id === target.targetId) - if (fileItem && fileItem.type === 'file' && canPreviewFile(fileItem)) { - setTimeout(() => openPreview(fileItem), 150) - } + filesTabRef?.resetToNode?.(detail.parent_id) + filesTabRef?.openFileById?.(target.targetId) } else { // No parent — item sits at the root level - await loadFolder(targetNode) + filesTabRef?.resetToNode?.(targetNode) } } catch(e) { - await loadFolder(targetNode) + filesTabRef?.resetToNode?.(targetNode) } } else { - await loadFolder(targetNode) + filesTabRef?.resetToNode?.(targetNode) } } } catch (e) { @@ -2733,18 +2297,15 @@ if (parent) { await selectNode(parent) setActiveTab('files') - await loadFolder(parent.id) - const fileItem = fileItems.find(item => item.id === detail.id) - if (fileItem && canPreviewFile(fileItem)) { - await openPreview(fileItem) - } + filesTabRef?.resetToNode?.(parent.id) + filesTabRef?.openFileById?.(detail.id) } return } if (result.type === 'folder') { await selectNode(detail) setActiveTab('files') - await loadFolder(detail.id) + filesTabRef?.resetToNode?.(detail.id) return } await selectNode(detail) @@ -2839,6 +2400,8 @@ } + + {#if showFirstRun} {:else if showRecovery} @@ -3016,7 +2579,7 @@ {t('overview.newNote')} - @@ -3089,87 +2652,14 @@ {:else if activeTab === 'files'} - -
-
- - - - {#if clipboard.items.length > 0} - - {/if} -
- - {#if loadingFiles} -
-

{t('common.loading')}

-
- {:else} - {#if folderStack.length > 0} - { - const i = e.detail - if (i === 0) { - folderStack = [] - currentFolderId = null - loadFolder(selectedNode.id) - } else { - navigateToBreadcrumb(i - 1) - } - }}/> - - {:else} - - {/if} - - {#if fileItems.length === 0} -
-
- - - - -
-

{folderStack.length > 0 ? t('file.noFiles') : t('file.noFilesCase')}

-

{t('file.hint')}

-
- - -
-
- {:else} -
- {#each fileItems as item (item.id)} - navigateToFolder(e.detail)} - on:preview={(e) => openPreview(e.detail)} - on:openExternal={(e) => wailsCall('OpenFile', e.detail)} - on:showInFolder={(e) => wailsCall('OpenFolder', e.detail)} - on:delete={(e) => deleteFile(e.detail)} - on:rename={(e) => renameItem(e.detail.id)} - on:duplicate={(e) => duplicateItem(e.detail)} - on:cut={(e) => cutItem(e.detail)} - on:copy={(e) => copyItem(e.detail)} - on:selectOne={(e) => selectOne(e.detail)} - on:toggleSelect={(e) => toggleSelection(e.detail)} - on:rangeSelect={(e) => rangeSelect(e.detail)} - /> - {/each} -
- {/if} - {/if} - - {#if importing && !showImportDialog} -

{t('file.scanning')}

- {/if} -
+ openNote(e.detail)} + on:refreshParent={(e) => refreshParentNode(e.detail.nodeId)} + on:error={(e) => error = e.detail.message} + /> {:else if activeTab === 'inbox'}
@@ -4035,46 +3525,22 @@
{/if} - {#if showImportDialog && importSummary} - - {/if} - {#if showRename} - {/if} + + + +{#if showLinkModal} + +{/if} + + +{#if showInternalLinkPicker} + +{/if} + + +{#if renamingNoteId && renamingNoteId !== noteEditor?.id} + +{/if} + + +{#if showVerstakToast} + +{/if} + diff --git a/frontend/src/lib/components/files/FilesTab.svelte b/frontend/src/lib/components/files/FilesTab.svelte new file mode 100644 index 0000000..0b78b87 --- /dev/null +++ b/frontend/src/lib/components/files/FilesTab.svelte @@ -0,0 +1,734 @@ + + + +
+
+ + + + {#if clipboard.items.length > 0} + + {/if} +
+ + {#if loadingFiles} +
+

{t('common.loading')}

+
+ {:else} + {#if folderStack.length > 0} + { + const i = e.detail + if (i === 0) { + folderStack = [] + currentFolderId = null + if (selectedNode) _loadFolder(selectedNode.id) + } else { + navigateToBreadcrumb(i - 1) + } + }}/> + + {:else} + + {/if} + + {#if fileItems.length === 0} +
+
+ + + + +
+

{folderStack.length > 0 ? t('file.noFiles') : t('file.noFilesCase')}

+

{t('file.hint')}

+
+ + +
+
+ {:else} +
+ {#each fileItems as item (item.id)} + navigateToFolder(e.detail)} + on:preview={(e) => _openPreview(e.detail)} + on:openExternal={(e) => wailsCall('OpenFile', e.detail)} + on:showInFolder={(e) => wailsCall('OpenFolder', e.detail)} + on:delete={(e) => deleteFile(e.detail)} + on:rename={(e) => renameItem(e.detail.id)} + on:duplicate={(e) => duplicateItem(e.detail)} + on:cut={(e) => cutItem(e.detail)} + on:copy={(e) => copyItem(e.detail)} + on:selectOne={(e) => selectOne(e.detail)} + on:toggleSelect={(e) => toggleSelection(e.detail)} + on:rangeSelect={(e) => rangeSelect(e.detail)} + /> + {/each} +
+ {/if} + {/if} + + {#if importing && !showImportDialog} +

{t('file.scanning')}

+ {/if} +
+ + +{#if showRename} + +{/if} + + +{#if showConfirm} + +{/if} + + +{#if showImportDialog && importSummary} + +{/if} + + +{#if previewItem} + wailsCall('OpenFile', e.detail)} + /> +{/if} + +