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:
mirivlad 2026-06-03 04:46:42 +08:00
parent 3c9b9edf8c
commit cc3500c14f
6 changed files with 32 additions and 16 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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',