💄 move home button and story bible side-by-side, remove app title in sidebar
This commit is contained in:
@@ -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">
|
||||||
<button
|
<div className="sidebar-nav">
|
||||||
className={`sidebar-home-btn${showHome ? ' active' : ''}`}
|
<button
|
||||||
onClick={goHome}
|
className={`sidebar-nav-btn${showHome ? ' active' : ''}`}
|
||||||
title="Home"
|
onClick={goHome}
|
||||||
>
|
title="Home"
|
||||||
<span className="sidebar-home-icon">⌂</span>
|
>
|
||||||
<span className="sidebar-home-label">Home</span>
|
<span className="sidebar-nav-icon">⌂</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' }}>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 }) }}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user