💄 create file/folder in root
This commit is contained in:
@@ -196,6 +196,24 @@
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.annotation-tooltip-dismiss {
|
||||
float: right;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
padding: 0 0 0 8px;
|
||||
cursor: pointer;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.annotation-tooltip-dismiss:hover {
|
||||
opacity: 1;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.annotation-tooltip-body {
|
||||
color: var(--text-primary);
|
||||
word-break: break-word;
|
||||
|
||||
@@ -217,6 +217,14 @@ const annotationHoverTooltip = hoverTooltip(
|
||||
label.className = 'annotation-tooltip-label'
|
||||
label.textContent = 'Your comment'
|
||||
dom.appendChild(label)
|
||||
const dismissBtn = document.createElement('button')
|
||||
dismissBtn.className = 'annotation-tooltip-dismiss'
|
||||
dismissBtn.title = 'Dismiss'
|
||||
dismissBtn.textContent = '×'
|
||||
dismissBtn.addEventListener('click', () => {
|
||||
useEditorStore.getState().removeAnnotation(ann.id)
|
||||
})
|
||||
dom.appendChild(dismissBtn)
|
||||
const divider = document.createElement('div')
|
||||
divider.className = 'annotation-tooltip-divider'
|
||||
dom.appendChild(divider)
|
||||
@@ -241,6 +249,15 @@ const annotationHoverTooltip = hoverTooltip(
|
||||
label.textContent = ann.type.replace(/_/g, ' ')
|
||||
dom.appendChild(label)
|
||||
|
||||
const dismissBtn = document.createElement('button')
|
||||
dismissBtn.className = 'annotation-tooltip-dismiss'
|
||||
dismissBtn.title = 'Dismiss'
|
||||
dismissBtn.textContent = '×'
|
||||
dismissBtn.addEventListener('click', () => {
|
||||
useEditorStore.getState().removeAnnotation(ann.id)
|
||||
})
|
||||
dom.appendChild(dismissBtn)
|
||||
|
||||
const divider = document.createElement('div')
|
||||
divider.className = 'annotation-tooltip-divider'
|
||||
dom.appendChild(divider)
|
||||
|
||||
@@ -18,6 +18,29 @@
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.file-tree-header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.file-tree-action-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
line-height: 1;
|
||||
padding: 0 3px;
|
||||
opacity: 0.55;
|
||||
transition: opacity 0.15s;
|
||||
flex-shrink: 0;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.file-tree-action-btn:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.file-tree-bible-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
@@ -1,10 +1,43 @@
|
||||
import { useState } from 'react'
|
||||
import { useEditorStore } from '../../store/editorStore'
|
||||
import { currentEditorView } from '../Editor/MarkdownEditor'
|
||||
import { FileTreeNode } from './FileTreeNode'
|
||||
import { ContextMenu } from './ContextMenu'
|
||||
import type { MenuItem } from './ContextMenu'
|
||||
import './FileTree.css'
|
||||
|
||||
export function FileTree(): JSX.Element {
|
||||
const { fileTree, activeFilePath, setActiveFile, markSaved } = useEditorStore()
|
||||
const { fileTree, activeFilePath, setActiveFile, markSaved, setFileTree } = useEditorStore()
|
||||
const [creatingRoot, setCreatingRoot] = useState<'file' | 'dir' | null>(null)
|
||||
const [createRootValue, setCreateRootValue] = useState('')
|
||||
const [rootMenuPos, setRootMenuPos] = useState<{ x: number; y: number } | null>(null)
|
||||
|
||||
const rootMenuItems: MenuItem[] = [
|
||||
{ label: 'New File', action: () => { setCreatingRoot('file'); setCreateRootValue('') } },
|
||||
{ label: 'New Folder', action: () => { setCreatingRoot('dir'); setCreateRootValue('') } },
|
||||
]
|
||||
|
||||
async function refreshTree(): Promise<void> {
|
||||
const tree = await window.api.listFiles()
|
||||
setFileTree(tree)
|
||||
}
|
||||
|
||||
async function submitCreateRoot(): Promise<void> {
|
||||
const trimmed = createRootValue.trim()
|
||||
setCreatingRoot(null)
|
||||
setCreateRootValue('')
|
||||
if (!trimmed) return
|
||||
try {
|
||||
if (creatingRoot === 'file') {
|
||||
await window.api.createFile('__root__', trimmed)
|
||||
} else {
|
||||
await window.api.createDir('__root__', trimmed)
|
||||
}
|
||||
await refreshTree()
|
||||
} catch (err) {
|
||||
console.error('Create at root failed:', err)
|
||||
}
|
||||
}
|
||||
|
||||
const handleOpenStoryBible = async (): Promise<void> => {
|
||||
try {
|
||||
@@ -31,6 +64,21 @@ export function FileTree(): JSX.Element {
|
||||
<nav className="file-tree">
|
||||
<div className="file-tree-header">
|
||||
<span>HOHOFF</span>
|
||||
<div className="file-tree-header-actions">
|
||||
<button
|
||||
className="file-tree-action-btn"
|
||||
onClick={() => { setCreatingRoot('file'); setCreateRootValue('') }}
|
||||
title="New File"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
<button
|
||||
className="file-tree-action-btn"
|
||||
onClick={() => { setCreatingRoot('dir'); setCreateRootValue('') }}
|
||||
title="New Folder"
|
||||
>
|
||||
⊕
|
||||
</button>
|
||||
<button
|
||||
className="file-tree-bible-btn"
|
||||
onClick={handleOpenStoryBible}
|
||||
@@ -39,7 +87,25 @@ export function FileTree(): JSX.Element {
|
||||
📖
|
||||
</button>
|
||||
</div>
|
||||
<div className="file-tree-list">
|
||||
</div>
|
||||
<div
|
||||
className="file-tree-list"
|
||||
onContextMenu={(e) => { e.preventDefault(); setRootMenuPos({ x: e.clientX, y: e.clientY }) }}
|
||||
>
|
||||
{creatingRoot && (
|
||||
<input
|
||||
className="tree-rename-input"
|
||||
value={createRootValue}
|
||||
onChange={(e) => setCreateRootValue(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') void submitCreateRoot()
|
||||
if (e.key === 'Escape') { setCreatingRoot(null); setCreateRootValue('') }
|
||||
}}
|
||||
onBlur={() => { setCreatingRoot(null); setCreateRootValue('') }}
|
||||
placeholder={creatingRoot === 'file' ? 'file name' : 'folder name'}
|
||||
autoFocus
|
||||
/>
|
||||
)}
|
||||
{fileTree.map((node) => (
|
||||
<FileTreeNode
|
||||
key={node.path}
|
||||
@@ -50,6 +116,14 @@ export function FileTree(): JSX.Element {
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{rootMenuPos && (
|
||||
<ContextMenu
|
||||
x={rootMenuPos.x}
|
||||
y={rootMenuPos.y}
|
||||
items={rootMenuItems}
|
||||
onClose={() => setRootMenuPos(null)}
|
||||
/>
|
||||
)}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user