fix: sidebar refresh, context menu position, show-in-explorer for all items
Sidebar refresh: - addFile/addFolder now use currentFolderId || selectedNode.id as parent - startImport stores pendingImportParent; confirmImport uses it - setNodeChildren also updates node.has_children for toggle arrow reactivity - navigateToFolder expands the folder node in sidebar tree Context menu position: - FileTreeRow stores menuX/menuY from contextmenu event coords - Menu uses position:fixed with cursor-relative left/top and window clamp Show in explorer: - Sidebar context menu uses file.showInExplorer label - en.js: add file.showInExplorer key
This commit is contained in:
parent
3c9b9edf8c
commit
cc3500c14f
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -16,8 +16,8 @@
|
||||||
background: #13131f;
|
background: #13131f;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script type="module" crossorigin src="/assets/main-BZ3I06Px.js"></script>
|
<script type="module" crossorigin src="/assets/main-43Kzwqa7.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/main-DsSP02cl.css">
|
<link rel="stylesheet" crossorigin href="/assets/main-D6qvckLy.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@
|
||||||
let importSummary = null
|
let importSummary = null
|
||||||
let showImportDialog = false
|
let showImportDialog = false
|
||||||
let pendingImportPath = ''
|
let pendingImportPath = ''
|
||||||
|
let pendingImportParent = ''
|
||||||
let pendingImportMode = 'copy'
|
let pendingImportMode = 'copy'
|
||||||
let treeItems = []
|
let treeItems = []
|
||||||
let expanded = {}
|
let expanded = {}
|
||||||
|
|
@ -247,7 +248,6 @@
|
||||||
|
|
||||||
async function navigateToFolder(folderId) {
|
async function navigateToFolder(folderId) {
|
||||||
if (!selectedNode) return
|
if (!selectedNode) return
|
||||||
// Get the folder node name for breadcrumbs
|
|
||||||
try {
|
try {
|
||||||
const node = await wailsCall('GetNodeDetail', folderId)
|
const node = await wailsCall('GetNodeDetail', folderId)
|
||||||
if (node) {
|
if (node) {
|
||||||
|
|
@ -257,6 +257,10 @@
|
||||||
folderStack = [...folderStack, { id: folderId, name: '...' }]
|
folderStack = [...folderStack, { id: folderId, name: '...' }]
|
||||||
}
|
}
|
||||||
currentFolderId = folderId
|
currentFolderId = folderId
|
||||||
|
expanded = { ...expanded, [folderId]: true }
|
||||||
|
const children = await wailsCall('ListWorkspaceChildren', folderId) || []
|
||||||
|
setNodeChildren(workspaceTree, folderId, children)
|
||||||
|
workspaceTree = [...workspaceTree]
|
||||||
await loadFolder(folderId)
|
await loadFolder(folderId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -681,6 +685,7 @@
|
||||||
for (const node of tree) {
|
for (const node of tree) {
|
||||||
if (node.id === nodeId) {
|
if (node.id === nodeId) {
|
||||||
node.children = children
|
node.children = children
|
||||||
|
node.has_children = children.length > 0
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (node.children && setNodeChildren(node.children, nodeId, children)) {
|
if (node.children && setNodeChildren(node.children, nodeId, children)) {
|
||||||
|
|
@ -877,21 +882,24 @@
|
||||||
async function addFile() {
|
async function addFile() {
|
||||||
const path = await wailsCall('PickFile')
|
const path = await wailsCall('PickFile')
|
||||||
if (!path) return
|
if (!path) return
|
||||||
await startImport(selectedNode.id, path)
|
const parentId = currentFolderId || selectedNode.id
|
||||||
|
await startImport(parentId, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addFolder() {
|
async function addFolder() {
|
||||||
const path = await wailsCall('PickDirectory')
|
const path = await wailsCall('PickDirectory')
|
||||||
if (!path) return
|
if (!path) return
|
||||||
await startImport(selectedNode.id, path)
|
const parentId = currentFolderId || selectedNode.id
|
||||||
|
await startImport(parentId, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startImport(nodeID, sourcePath) {
|
async function startImport(parentID, sourcePath) {
|
||||||
importing = true
|
importing = true
|
||||||
try {
|
try {
|
||||||
const summary = await wailsCall('PreviewImport', sourcePath)
|
const summary = await wailsCall('PreviewImport', sourcePath)
|
||||||
importSummary = summary
|
importSummary = summary
|
||||||
pendingImportPath = sourcePath
|
pendingImportPath = sourcePath
|
||||||
|
pendingImportParent = parentID
|
||||||
showImportDialog = true
|
showImportDialog = true
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = String(e)
|
error = String(e)
|
||||||
|
|
@ -901,17 +909,18 @@
|
||||||
|
|
||||||
async function confirmImport(mode) {
|
async function confirmImport(mode) {
|
||||||
try {
|
try {
|
||||||
|
const parentId = pendingImportParent || selectedNode.id
|
||||||
const result = mode === 'copy'
|
const result = mode === 'copy'
|
||||||
? await wailsCall('AddPathCopy', selectedNode.id, pendingImportPath)
|
? await wailsCall('AddPathCopy', parentId, pendingImportPath)
|
||||||
: await wailsCall('AddPathLink', selectedNode.id, pendingImportPath)
|
: await wailsCall('AddPathLink', parentId, pendingImportPath)
|
||||||
showImportDialog = false
|
showImportDialog = false
|
||||||
importSummary = null
|
importSummary = null
|
||||||
folderStack = []
|
folderStack = []
|
||||||
currentFolderId = null
|
currentFolderId = null
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
loadTabData(selectedNode.id),
|
loadTabData(parentId),
|
||||||
loadFolder(selectedNode.id),
|
loadFolder(parentId),
|
||||||
refreshParentNode(selectedNode.id),
|
refreshParentNode(parentId),
|
||||||
])
|
])
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = String(e)
|
error = String(e)
|
||||||
|
|
@ -1656,7 +1665,7 @@
|
||||||
{t('common.delete')}
|
{t('common.delete')}
|
||||||
</button>
|
</button>
|
||||||
<button class="context-menu-item" on:click={() => openNodeFolder(contextMenu.node)}>
|
<button class="context-menu-item" on:click={() => openNodeFolder(contextMenu.node)}>
|
||||||
{t('nav.openFolder')}
|
{t('file.showInExplorer')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@
|
||||||
const isFolder = item.type === 'folder'
|
const isFolder = item.type === 'folder'
|
||||||
const fileType = formatFileType(item)
|
const fileType = formatFileType(item)
|
||||||
let menuOpen = false
|
let menuOpen = false
|
||||||
|
let menuX = 0
|
||||||
|
let menuY = 0
|
||||||
let clickTimer = null
|
let clickTimer = null
|
||||||
|
|
||||||
function handleClick(e) {
|
function handleClick(e) {
|
||||||
|
|
@ -107,6 +109,8 @@
|
||||||
|
|
||||||
function oncontextmenu(e) {
|
function oncontextmenu(e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
menuX = Math.min(e.clientX, window.innerWidth - 240)
|
||||||
|
menuY = Math.min(e.clientY, window.innerHeight - 320)
|
||||||
menuOpen = true
|
menuOpen = true
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -189,7 +193,7 @@
|
||||||
{#if menuOpen}
|
{#if menuOpen}
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class="menu-backdrop" on:click|stopPropagation={closeMenu} role="presentation"></div>
|
<div class="menu-backdrop" on:click|stopPropagation={closeMenu} role="presentation"></div>
|
||||||
<div class="menu" on:click|stopPropagation role="menu">
|
<div class="menu" style="left: {menuX}px; top: {menuY}px; position: fixed;" on:click|stopPropagation role="menu">
|
||||||
<button class="menu-item" on:click={handleOpen} role="menuitem">
|
<button class="menu-item" on:click={handleOpen} role="menuitem">
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
||||||
{t('common.open')}
|
{t('common.open')}
|
||||||
|
|
@ -347,9 +351,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
right: 12px;
|
|
||||||
margin-top: 4px;
|
|
||||||
background: #1a1a28;
|
background: #1a1a28;
|
||||||
border: 1px solid #2a2a3c;
|
border: 1px solid #2a2a3c;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ export default {
|
||||||
'file.preview': 'Preview',
|
'file.preview': 'Preview',
|
||||||
'file.openExternal': 'Open in external program',
|
'file.openExternal': 'Open in external program',
|
||||||
'file.openFolder': 'Open folder',
|
'file.openFolder': 'Open folder',
|
||||||
|
'file.showInExplorer': 'Show in explorer',
|
||||||
'file.delete': 'Delete',
|
'file.delete': 'Delete',
|
||||||
'file.pickSingle': 'Select file',
|
'file.pickSingle': 'Select file',
|
||||||
'file.pickDirectory': 'Select folder',
|
'file.pickDirectory': 'Select folder',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue