M0-M11 狩夜人笔记初始实现:11里程碑+86测试
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useStore } from './store';
|
||||
import { FileTree } from './features/file-tree/FileTree';
|
||||
import { Editor } from './features/editor/Editor';
|
||||
import { Toolbar } from './features/toolbar/Toolbar';
|
||||
import { TitleBar } from './features/shell/TitleBar';
|
||||
import { InputDialog } from './features/dialog/InputDialog';
|
||||
import { NotifyLayer } from './features/dialog/NotifyLayer';
|
||||
import { TabBar } from './features/tabs/TabBar';
|
||||
import { useTabs } from './features/tabs/tab-store';
|
||||
import { GraphView } from './features/graph/GraphView';
|
||||
import { SettingsPanel } from './features/settings/SettingsPanel';
|
||||
import { AppearancePanel } from './features/appearance/AppearancePanel';
|
||||
import { useAppearance } from './features/appearance/appearance-store';
|
||||
import { CheatSheet } from './features/keymap/CheatSheet';
|
||||
import { useGlobalHotkeys } from './features/keymap/use-hotkeys';
|
||||
import { useLayout } from './features/shell/layout-store';
|
||||
import { Resizer } from './features/shell/Resizer';
|
||||
import { EffectLayer } from './features/effects/EffectLayer';
|
||||
import { useSettings } from './features/settings/settings-store';
|
||||
import { SearchBox } from './features/search/SearchBox';
|
||||
import { TagFilter } from './features/search/TagFilter';
|
||||
import { OutlinePanel } from './features/outline/OutlinePanel';
|
||||
import { DocInfoPanel } from './features/doc-info/DocInfoPanel';
|
||||
|
||||
export function App() {
|
||||
const { loadVault, loadTree, openDoc } = useStore();
|
||||
const { restore, tabs, activeId } = useTabs();
|
||||
const [rightTab, setRightTab] = useState<'outline' | 'info'>('outline');
|
||||
const [view, setView] = useState<'editor' | 'graph'>('editor');
|
||||
const [showSettings, setShowSettings] = useState(false);
|
||||
const [showCheat, setShowCheat] = useState(false);
|
||||
const [showAppearance, setShowAppearance] = useState(false);
|
||||
const appearance = useAppearance();
|
||||
const settings = useSettings();
|
||||
const layout = useLayout();
|
||||
|
||||
useEffect(() => { loadVault(); loadTree(); restore(); appearance.preview(); }, []);
|
||||
useEffect(() => { if (activeId && tabs.length > 0) { const t = tabs.find(x => x.id === activeId); if (t) openDoc(t.path).catch(() => {}); } }, []);
|
||||
useGlobalHotkeys({ onNewDoc: () => {}, onSave: () => useStore.getState().saveNow(), onFocusSearch: () => { const el = document.querySelector('input[placeholder*="搜索"]') as HTMLInputElement; el?.focus(); }, onToggleOutline: () => layout.toggleOutline(), onCheat: () => setShowCheat(true), onSettings: () => setShowSettings(true) });
|
||||
|
||||
// 编辑器样式随设置生效(FR-编辑器设置真实生效)
|
||||
useEffect(() => { document.documentElement.style.setProperty('--editor-font-size', settings.fontSize + 'px'); document.documentElement.style.setProperty('--editor-font-family', settings.fontFamily); document.documentElement.style.setProperty('--editor-white-space', settings.wordWrap ? 'pre-wrap' : 'pre'); }, [settings.fontSize, settings.fontFamily, settings.wordWrap]);
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', height: '100vh', background: '#1a1a1a', color: '#ddd' }}>
|
||||
<TitleBar view={view} onToggleView={() => setView(v => v === 'editor' ? 'graph' : 'editor')} onSettings={() => setShowSettings(true)} onAppearance={() => setShowAppearance(true)} onToggleSidebar={() => layout.toggleSidebar()} onToggleMinimal={() => layout.toggleMinimal()} minimal={layout.minimal} />
|
||||
<Toolbar />
|
||||
{view === 'editor' && <TabBar />}
|
||||
<div style={{ flex: 1, display: 'flex', overflow: 'hidden' }}>
|
||||
{layout.minimal ? (<Editor />) : (<>
|
||||
{layout.sidebarVisible && (<>
|
||||
<div style={{ width: layout.sidebarWidth, borderRight: '1px solid #2a2a3a', display: 'flex', flexDirection: 'column', overflow: 'hidden', flexShrink: 0 }}>
|
||||
<SearchBox />
|
||||
<div style={{ flex: 1, overflow: 'auto' }}><FileTree /></div>
|
||||
<TagFilter />
|
||||
</div>
|
||||
<Resizer side="left" onDrag={d => layout.setSidebarWidth(layout.sidebarWidth + d)} />
|
||||
</>)}
|
||||
{view === 'editor' ? <Editor /> : <GraphView />}
|
||||
{view === 'editor' && layout.outlineVisible && (<>
|
||||
<Resizer side="right" onDrag={d => layout.setOutlineWidth(layout.outlineWidth + d)} />
|
||||
<div style={{ width: layout.outlineWidth, borderLeft: '1px solid #2a2a3a', display: 'flex', flexDirection: 'column', overflow: 'hidden', flexShrink: 0 }}>
|
||||
<div style={{ display: 'flex', borderBottom: '1px solid #2a2a3a' }}>
|
||||
<button style={tabBtn(rightTab==='outline')} onClick={() => setRightTab('outline')}>大纲</button>
|
||||
<button style={tabBtn(rightTab==='info')} onClick={() => setRightTab('info')}>信息</button>
|
||||
</div>
|
||||
<div style={{ flex: 1, overflow: 'auto' }}>{rightTab === 'outline' ? <OutlinePanel /> : <DocInfoPanel />}</div>
|
||||
</div>
|
||||
</>)}
|
||||
</>)}
|
||||
</div>
|
||||
<EffectLayer />
|
||||
<InputDialog />
|
||||
<NotifyLayer />
|
||||
{showSettings && <SettingsPanel onClose={() => setShowSettings(false)} />}
|
||||
{showAppearance && <AppearancePanel onClose={() => setShowAppearance(false)} />}
|
||||
{showCheat && <CheatSheet onClose={() => setShowCheat(false)} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function tabBtn(active: boolean): React.CSSProperties {
|
||||
return { flex: 1, padding: '6px', background: active ? '#2a2a3a' : 'transparent', border: 'none', color: active ? '#fff' : '#888', cursor: 'pointer', borderBottom: active ? '2px solid #4a9eff' : '2px solid transparent' };
|
||||
}
|
||||
Reference in New Issue
Block a user