mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-22 18:45:19 +02:00
add show in finder
This commit is contained in:
parent
41f783d504
commit
f9ddc6549a
5 changed files with 46 additions and 1 deletions
|
|
@ -679,6 +679,11 @@ export function setupIpcHandlers() {
|
||||||
const error = await shell.openPath(filePath);
|
const error = await shell.openPath(filePath);
|
||||||
return { error: error || undefined };
|
return { error: error || undefined };
|
||||||
},
|
},
|
||||||
|
'shell:showItemInFolder': async (_event, args) => {
|
||||||
|
const filePath = resolveShellPath(args.path);
|
||||||
|
shell.showItemInFolder(filePath);
|
||||||
|
return { success: true };
|
||||||
|
},
|
||||||
'shell:readFileBase64': async (_event, args) => {
|
'shell:readFileBase64': async (_event, args) => {
|
||||||
const filePath = resolveShellPath(args.path);
|
const filePath = resolveShellPath(args.path);
|
||||||
const stat = await fs.stat(filePath);
|
const stat = await fs.stat(filePath);
|
||||||
|
|
|
||||||
|
|
@ -3966,6 +3966,12 @@ function App() {
|
||||||
document.body.removeChild(textarea)
|
document.body.removeChild(textarea)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
revealInFileManager: (path: string, isDir: boolean) => {
|
||||||
|
const channel = isDir ? 'shell:openPath' : 'shell:showItemInFolder'
|
||||||
|
void window.ipc.invoke(channel, { path }).catch((err) => {
|
||||||
|
console.error('Failed to open in file manager:', err)
|
||||||
|
})
|
||||||
|
},
|
||||||
onOpenInNewTab: (path: string) => {
|
onOpenInNewTab: (path: string) => {
|
||||||
openFileInNewTab(path)
|
openFileInNewTab(path)
|
||||||
},
|
},
|
||||||
|
|
@ -4769,6 +4775,7 @@ function App() {
|
||||||
rename: knowledgeActions.rename,
|
rename: knowledgeActions.rename,
|
||||||
remove: knowledgeActions.remove,
|
remove: knowledgeActions.remove,
|
||||||
copyPath: knowledgeActions.copyPath,
|
copyPath: knowledgeActions.copyPath,
|
||||||
|
revealInFileManager: knowledgeActions.revealInFileManager,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useEffect, useState, useMemo, useCallback, useRef } from 'react'
|
import { useEffect, useState, useMemo, useCallback, useRef } from 'react'
|
||||||
import { ArrowDown, ArrowUp, ChevronLeft, ChevronRight, X, Check, ListFilter, Filter, Search, Save, Copy, Pencil, Trash2 } from 'lucide-react'
|
import { ArrowDown, ArrowUp, ChevronLeft, ChevronRight, X, Check, ListFilter, Filter, Search, Save, Copy, FolderOpen, Pencil, Trash2 } from 'lucide-react'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover'
|
import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover'
|
||||||
import { Command, CommandInput, CommandList, CommandItem, CommandEmpty, CommandGroup } from '@/components/ui/command'
|
import { Command, CommandInput, CommandList, CommandItem, CommandEmpty, CommandGroup } from '@/components/ui/command'
|
||||||
|
|
@ -103,9 +103,18 @@ type BasesViewProps = {
|
||||||
rename: (oldPath: string, newName: string, isDir: boolean) => Promise<void>
|
rename: (oldPath: string, newName: string, isDir: boolean) => Promise<void>
|
||||||
remove: (path: string) => Promise<void>
|
remove: (path: string) => Promise<void>
|
||||||
copyPath: (path: string) => void
|
copyPath: (path: string) => void
|
||||||
|
revealInFileManager: (path: string, isDir: boolean) => void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFileManagerName(): string {
|
||||||
|
if (typeof navigator === 'undefined') return 'File Manager'
|
||||||
|
const platform = navigator.platform.toLowerCase()
|
||||||
|
if (platform.includes('mac')) return 'Finder'
|
||||||
|
if (platform.includes('win')) return 'Explorer'
|
||||||
|
return 'File Manager'
|
||||||
|
}
|
||||||
|
|
||||||
function collectFiles(nodes: TreeNode[]): { path: string; name: string; mtimeMs: number }[] {
|
function collectFiles(nodes: TreeNode[]): { path: string; name: string; mtimeMs: number }[] {
|
||||||
return nodes.flatMap((n) =>
|
return nodes.flatMap((n) =>
|
||||||
n.kind === 'file' && n.name.endsWith('.md')
|
n.kind === 'file' && n.name.endsWith('.md')
|
||||||
|
|
@ -919,6 +928,10 @@ function NoteRow({
|
||||||
<Copy className="mr-2 size-4" />
|
<Copy className="mr-2 size-4" />
|
||||||
Copy Path
|
Copy Path
|
||||||
</ContextMenuItem>
|
</ContextMenuItem>
|
||||||
|
<ContextMenuItem onClick={() => actions?.revealInFileManager(note.path, false)}>
|
||||||
|
<FolderOpen className="mr-2 size-4" />
|
||||||
|
Open in {getFileManagerName()}
|
||||||
|
</ContextMenuItem>
|
||||||
<ContextMenuSeparator />
|
<ContextMenuSeparator />
|
||||||
<ContextMenuItem onClick={() => { setNewName(baseName); isSubmittingRef.current = false; setIsRenaming(true) }}>
|
<ContextMenuItem onClick={() => { setNewName(baseName); isSubmittingRef.current = false; setIsRenaming(true) }}>
|
||||||
<Pencil className="mr-2 size-4" />
|
<Pencil className="mr-2 size-4" />
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import {
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
FilePlus,
|
FilePlus,
|
||||||
Folder,
|
Folder,
|
||||||
|
FolderOpen,
|
||||||
FolderPlus,
|
FolderPlus,
|
||||||
Globe,
|
Globe,
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
|
|
@ -119,9 +120,18 @@ type KnowledgeActions = {
|
||||||
rename: (path: string, newName: string, isDir: boolean) => Promise<void>
|
rename: (path: string, newName: string, isDir: boolean) => Promise<void>
|
||||||
remove: (path: string) => Promise<void>
|
remove: (path: string) => Promise<void>
|
||||||
copyPath: (path: string) => void
|
copyPath: (path: string) => void
|
||||||
|
revealInFileManager: (path: string, isDir: boolean) => void
|
||||||
onOpenInNewTab?: (path: string) => void
|
onOpenInNewTab?: (path: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFileManagerName(): string {
|
||||||
|
if (typeof navigator === 'undefined') return 'File Manager'
|
||||||
|
const platform = navigator.platform.toLowerCase()
|
||||||
|
if (platform.includes('mac')) return 'Finder'
|
||||||
|
if (platform.includes('win')) return 'Explorer'
|
||||||
|
return 'File Manager'
|
||||||
|
}
|
||||||
|
|
||||||
type RunListItem = {
|
type RunListItem = {
|
||||||
id: string
|
id: string
|
||||||
title?: string
|
title?: string
|
||||||
|
|
@ -1177,11 +1187,13 @@ function KnowledgeSection({
|
||||||
},
|
},
|
||||||
}), [actions, deriveParent])
|
}), [actions, deriveParent])
|
||||||
|
|
||||||
|
const fileManagerName = getFileManagerName()
|
||||||
const quickActions = [
|
const quickActions = [
|
||||||
{ icon: FilePlus, label: "New Note", action: () => wrappedActions.createNote() },
|
{ icon: FilePlus, label: "New Note", action: () => wrappedActions.createNote() },
|
||||||
{ icon: FolderPlus, label: "New Folder", action: () => void wrappedActions.createFolder() },
|
{ icon: FolderPlus, label: "New Folder", action: () => void wrappedActions.createFolder() },
|
||||||
{ icon: Network, label: "Graph View", action: () => actions.openGraph() },
|
{ icon: Network, label: "Graph View", action: () => actions.openGraph() },
|
||||||
{ icon: Table2, label: "Bases", action: () => actions.openBases() },
|
{ icon: Table2, label: "Bases", action: () => actions.openBases() },
|
||||||
|
{ icon: FolderOpen, label: `Open in ${fileManagerName}`, action: () => actions.revealInFileManager('knowledge', true) },
|
||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -1389,6 +1401,10 @@ function Tree({
|
||||||
<Copy className="mr-2 size-4" />
|
<Copy className="mr-2 size-4" />
|
||||||
Copy Path
|
Copy Path
|
||||||
</ContextMenuItem>
|
</ContextMenuItem>
|
||||||
|
<ContextMenuItem onClick={() => actions.revealInFileManager(item.path, isDir)}>
|
||||||
|
<FolderOpen className="mr-2 size-4" />
|
||||||
|
Open in {getFileManagerName()}
|
||||||
|
</ContextMenuItem>
|
||||||
<ContextMenuSeparator />
|
<ContextMenuSeparator />
|
||||||
<ContextMenuItem onClick={() => { setNewName(baseName); isSubmittingRef.current = false; setIsRenaming(true) }}>
|
<ContextMenuItem onClick={() => { setNewName(baseName); isSubmittingRef.current = false; setIsRenaming(true) }}>
|
||||||
<Pencil className="mr-2 size-4" />
|
<Pencil className="mr-2 size-4" />
|
||||||
|
|
|
||||||
|
|
@ -489,6 +489,10 @@ const ipcSchemas = {
|
||||||
req: z.object({ path: z.string() }),
|
req: z.object({ path: z.string() }),
|
||||||
res: z.object({ error: z.string().optional() }),
|
res: z.object({ error: z.string().optional() }),
|
||||||
},
|
},
|
||||||
|
'shell:showItemInFolder': {
|
||||||
|
req: z.object({ path: z.string() }),
|
||||||
|
res: z.object({ success: z.literal(true) }),
|
||||||
|
},
|
||||||
'shell:readFileBase64': {
|
'shell:readFileBase64': {
|
||||||
req: z.object({ path: z.string() }),
|
req: z.object({ path: z.string() }),
|
||||||
res: z.object({ data: z.string(), mimeType: z.string(), size: z.number() }),
|
res: z.object({ data: z.string(), mimeType: z.string(), size: z.number() }),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue