💄 move home button and story bible side-by-side, remove app title in sidebar

This commit is contained in:
TC
2026-06-09 23:09:23 +10:00
parent 46458beb8e
commit c5ab40b438
4 changed files with 52 additions and 91 deletions

View File

@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { FileTree } from './components/FileTree/FileTree' import { FileTree } from './components/FileTree/FileTree'
import { currentEditorView } from './components/Editor/MarkdownEditor'
import { MarkdownEditor } from './components/Editor/MarkdownEditor' import { MarkdownEditor } from './components/Editor/MarkdownEditor'
import { DocumentOutline } from './components/Editor/DocumentOutline' import { DocumentOutline } from './components/Editor/DocumentOutline'
import { ChatPanel } from './components/AIChat/ChatPanel' import { ChatPanel } from './components/AIChat/ChatPanel'
@@ -16,8 +17,24 @@ export default function App(): JSX.Element {
setFileTree, activeFilePath, isDirty, markSaved, activeFileContent, theme, toggleTheme, setFileTree, activeFilePath, isDirty, markSaved, activeFileContent, theme, toggleTheme,
loadSession, revisionPanelOpen, toggleRevisionPanel, fontSize, setFontSize, loadSession, revisionPanelOpen, toggleRevisionPanel, fontSize, setFontSize,
openProjectSearch, clearActiveFile, initPrefs, focusMode, toggleFocusMode, openProjectSearch, clearActiveFile, initPrefs, focusMode, toggleFocusMode,
showHome, goHome showHome, goHome, setActiveFile
} = useEditorStore() } = useEditorStore()
const handleOpenStoryBible = async (): Promise<void> => {
try {
const { path, content } = await window.api.openStoryBible()
setActiveFile(path, content)
if (path === activeFilePath) {
const view = currentEditorView
if (view) {
view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: content } })
markSaved()
}
}
} catch (err) {
console.error('[App] openStoryBible failed:', err)
}
}
const [sidebarOpen, setSidebarOpen] = useState( const [sidebarOpen, setSidebarOpen] = useState(
() => localStorage.getItem('sidebarOpen') !== 'false' () => localStorage.getItem('sidebarOpen') !== 'false'
) )
@@ -175,14 +192,22 @@ export default function App(): JSX.Element {
</div> </div>
</div> </div>
<aside className="sidebar"> <aside className="sidebar">
<div className="sidebar-nav">
<button <button
className={`sidebar-home-btn${showHome ? ' active' : ''}`} className={`sidebar-nav-btn${showHome ? ' active' : ''}`}
onClick={goHome} onClick={goHome}
title="Home" title="Home"
> >
<span className="sidebar-home-icon"></span> <span className="sidebar-nav-icon"></span>
<span className="sidebar-home-label">Home</span>
</button> </button>
<button
className="sidebar-nav-btn"
onClick={handleOpenStoryBible}
title="Open Story Bible"
>
<span className="sidebar-nav-icon">📖</span>
</button>
</div>
<FileTree /> <FileTree />
</aside> </aside>
<main className="editor-area" style={{ position: 'relative' }}> <main className="editor-area" style={{ position: 'relative' }}>

View File

@@ -5,34 +5,6 @@
overflow: hidden; overflow: hidden;
} }
.file-tree-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 12px 10px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.12em;
color: var(--text-muted);
border-bottom: 1px solid var(--border);
text-transform: uppercase;
}
.file-tree-bible-btn {
background: none;
border: none;
cursor: pointer;
font-size: 14px;
line-height: 1;
padding: 0 2px;
opacity: 0.55;
transition: opacity 0.15s;
flex-shrink: 0;
}
.file-tree-bible-btn:hover {
opacity: 1;
}
.file-tree-list { .file-tree-list {
overflow-y: auto; overflow-y: auto;

View File

@@ -1,13 +1,12 @@
import { useState } from 'react' import { useState } from 'react'
import { useEditorStore } from '../../store/editorStore' import { useEditorStore } from '../../store/editorStore'
import { currentEditorView } from '../Editor/MarkdownEditor'
import { FileTreeNode } from './FileTreeNode' import { FileTreeNode } from './FileTreeNode'
import { ContextMenu } from './ContextMenu' import { ContextMenu } from './ContextMenu'
import type { MenuItem } 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, setFileTree } = useEditorStore() const { fileTree, setFileTree } = useEditorStore()
const [creatingRoot, setCreatingRoot] = useState<'file' | 'dir' | null>(null) const [creatingRoot, setCreatingRoot] = useState<'file' | 'dir' | null>(null)
const [createRootValue, setCreateRootValue] = useState('') const [createRootValue, setCreateRootValue] = useState('')
const [rootMenuPos, setRootMenuPos] = useState<{ x: number; y: number } | null>(null) const [rootMenuPos, setRootMenuPos] = useState<{ x: number; y: number } | null>(null)
@@ -39,39 +38,8 @@ export function FileTree(): JSX.Element {
} }
} }
const handleOpenStoryBible = async (): Promise<void> => {
try {
const { path, content } = await window.api.openStoryBible()
setActiveFile(path, content)
// If the path didn't change (already on Story Bible), the MarkdownEditor's
// activeFilePath-keyed effect won't fire. Push content directly, same
// pattern as RevisionPanel.restore().
if (path === activeFilePath) {
const view = currentEditorView
if (view) {
view.dispatch({
changes: { from: 0, to: view.state.doc.length, insert: content }
})
markSaved()
}
}
} catch (err) {
console.error('[FileTree] openStoryBible failed:', err)
}
}
return ( return (
<nav className="file-tree"> <nav className="file-tree">
<div className="file-tree-header">
<span>HOHOFF</span>
<button
className="file-tree-bible-btn"
onClick={handleOpenStoryBible}
title="Open Story Bible"
>
📖
</button>
</div>
<div <div
className="file-tree-list" className="file-tree-list"
onContextMenu={(e) => { e.preventDefault(); setRootMenuPos({ x: e.clientX, y: e.clientY }) }} onContextMenu={(e) => { e.preventDefault(); setRootMenuPos({ x: e.clientX, y: e.clientY }) }}

View File

@@ -194,44 +194,40 @@
flex-direction: column; flex-direction: column;
} }
.sidebar-home-btn { .sidebar-nav {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 7px;
width: 100%;
padding: 8px 12px;
background: none;
border: none;
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
color: var(--text-muted);
font-family: var(--font-sans);
font-size: 11px;
letter-spacing: 0.06em;
text-transform: uppercase;
cursor: pointer;
text-align: left;
transition: color 0.15s, background 0.15s;
flex-shrink: 0; flex-shrink: 0;
} }
.sidebar-home-btn:hover { .sidebar-nav-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 8px 12px;
background: none;
border: none;
color: var(--text-muted);
cursor: pointer;
transition: color 0.15s, background 0.15s;
border-radius: 0;
}
.sidebar-nav-btn:hover {
color: var(--text-primary); color: var(--text-primary);
background: var(--active-bg); background: var(--active-bg);
} }
.sidebar-home-btn.active { .sidebar-nav-btn.active {
color: var(--accent); color: var(--accent);
} }
.sidebar-home-icon { .sidebar-nav-icon {
font-size: 14px; font-size: 14px;
line-height: 1; line-height: 1;
} }
.sidebar-home-label {
line-height: 1;
}
/* ─── Editor area ──────────────────────────────────────────────── */ /* ─── Editor area ──────────────────────────────────────────────── */
.editor-area { .editor-area {
grid-row: 2; grid-row: 2;