💄 create file/folder in root

This commit is contained in:
2026-03-03 19:48:33 +10:00
parent 4ab1dc74c9
commit 694482ba26
4 changed files with 141 additions and 9 deletions

View File

@@ -196,6 +196,24 @@
color: var(--accent); 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 { .annotation-tooltip-body {
color: var(--text-primary); color: var(--text-primary);
word-break: break-word; word-break: break-word;

View File

@@ -217,6 +217,14 @@ const annotationHoverTooltip = hoverTooltip(
label.className = 'annotation-tooltip-label' label.className = 'annotation-tooltip-label'
label.textContent = 'Your comment' label.textContent = 'Your comment'
dom.appendChild(label) 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') const divider = document.createElement('div')
divider.className = 'annotation-tooltip-divider' divider.className = 'annotation-tooltip-divider'
dom.appendChild(divider) dom.appendChild(divider)
@@ -241,6 +249,15 @@ const annotationHoverTooltip = hoverTooltip(
label.textContent = ann.type.replace(/_/g, ' ') label.textContent = ann.type.replace(/_/g, ' ')
dom.appendChild(label) 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') const divider = document.createElement('div')
divider.className = 'annotation-tooltip-divider' divider.className = 'annotation-tooltip-divider'
dom.appendChild(divider) dom.appendChild(divider)

View File

@@ -18,6 +18,29 @@
text-transform: uppercase; 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 { .file-tree-bible-btn {
background: none; background: none;
border: none; border: none;

View File

@@ -1,10 +1,43 @@
import { useState } from 'react'
import { useEditorStore } from '../../store/editorStore' import { useEditorStore } from '../../store/editorStore'
import { currentEditorView } from '../Editor/MarkdownEditor' import { currentEditorView } from '../Editor/MarkdownEditor'
import { FileTreeNode } from './FileTreeNode' import { FileTreeNode } from './FileTreeNode'
import { ContextMenu } from './ContextMenu'
import type { MenuItem } from './ContextMenu'
import './FileTree.css' import './FileTree.css'
export function FileTree(): JSX.Element { 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> => { const handleOpenStoryBible = async (): Promise<void> => {
try { try {
@@ -31,6 +64,21 @@ export function FileTree(): JSX.Element {
<nav className="file-tree"> <nav className="file-tree">
<div className="file-tree-header"> <div className="file-tree-header">
<span>HOHOFF</span> <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 <button
className="file-tree-bible-btn" className="file-tree-bible-btn"
onClick={handleOpenStoryBible} onClick={handleOpenStoryBible}
@@ -39,7 +87,25 @@ export function FileTree(): JSX.Element {
📖 📖
</button> </button>
</div> </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) => ( {fileTree.map((node) => (
<FileTreeNode <FileTreeNode
key={node.path} key={node.path}
@@ -50,6 +116,14 @@ export function FileTree(): JSX.Element {
/> />
))} ))}
</div> </div>
{rootMenuPos && (
<ContextMenu
x={rootMenuPos.x}
y={rootMenuPos.y}
items={rootMenuItems}
onClose={() => setRootMenuPos(null)}
/>
)}
</nav> </nav>
) )
} }