| import { useEffect, useState } from 'react'; |
| import { getSettings, SettingsType, DEFAULT_SETTINGS, getBackgroundImage } from './lib/db'; |
| import { useFileStore } from './stores/fileStore'; |
| import TreeView from './components/Sidebar/TreeView'; |
| import EditorCore from './components/Editor/EditorCore'; |
| import SettingsModal from './components/Settings/SettingsModal'; |
| import { |
| Settings01Icon, Download01Icon, BorderAll01Icon |
| } from 'hugeicons-react'; |
|
|
| function App() { |
| const [settings, setSettings] = useState<SettingsType>(DEFAULT_SETTINGS); |
| const [bgImage, setBgImage] = useState<string | null>(null); |
| const [showSettings, setShowSettings] = useState(false); |
| const [showSidebar, setShowSidebar] = useState(true); |
|
|
| const { loadFiles } = useFileStore(); |
|
|
| useEffect(() => { |
| |
| loadFiles(); |
| |
| |
| getSettings().then(setSettings); |
| |
| getBackgroundImage().then(blob => { |
| if (blob) { |
| if (blob instanceof Blob) { |
| setBgImage(URL.createObjectURL(blob)); |
| } else { |
| setBgImage(blob); |
| } |
| } |
| }); |
|
|
| |
| const handleKeyDown = (e: KeyboardEvent) => { |
| if (e.key === 'Escape') { |
| setShowSettings(false); |
| } |
| }; |
| window.addEventListener('keydown', handleKeyDown); |
| return () => window.removeEventListener('keydown', handleKeyDown); |
| }, []); |
|
|
| return ( |
| <div |
| className={`app-container font-${settings.typography.replace(/\s+/g, '-').toLowerCase()}`} |
| style={{ '--bg-opacity': settings.bgOpacity, '--fg-opacity': settings.fgOpacity } as any} |
| > |
| {bgImage && ( |
| <div |
| className="app-background" |
| style={{ backgroundImage: `url(${bgImage})` }} |
| /> |
| )} |
| |
| {showSettings && <SettingsModal onClose={() => setShowSettings(false)} />} |
| |
| <aside className="mini-sidebar"> |
| <button |
| className={`btn-icon ${showSidebar ? 'active' : ''}`} |
| onClick={() => setShowSidebar(!showSidebar)} |
| title="Toggle Sidebar" |
| style={{ padding: 8, background: showSidebar ? 'rgba(0,0,0,0.03)' : 'transparent' }} |
| > |
| <BorderAll01Icon size={20} color={showSidebar ? 'var(--text-primary)' : 'var(--text-secondary)'} /> |
| </button> |
| |
| <div style={{ flex: 1 }} /> |
| <button className="btn-icon" onClick={() => setShowSettings(true)} title="Settings" style={{ padding: 8 }}> |
| <Settings01Icon size={20} color="var(--text-secondary)" /> |
| </button> |
| <button |
| className="btn-icon" |
| onClick={async () => { |
| const { exportFullWorkspace } = await import('./lib/db'); |
| const data = await exportFullWorkspace(); |
| const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); |
| const url = URL.createObjectURL(blob); |
| const a = document.createElement('a'); |
| a.href = url; |
| a.download = `hybrid-workspace-${new Date().toISOString().split('T')[0]}.json`; |
| a.click(); |
| }} |
| title="Download Workspace" |
| style={{ padding: 8 }} |
| > |
| <Download01Icon size={20} color="var(--text-secondary)" /> |
| </button> |
| </aside> |
| |
| {showSidebar && ( |
| <aside className="sidebar-panel"> |
| <div className="sidebar-header" style={{ padding: '4px 16px', display: 'flex', alignItems: 'center', height: '48px', borderBottom: '1px solid var(--border-color)' }}> |
| <span style={{ letterSpacing: '0.05em', textTransform: 'uppercase', fontSize: '10px', fontWeight: 700, opacity: 0.4 }}>WORKSPACE</span> |
| </div> |
| <TreeView /> |
| </aside> |
| )} |
| |
| <main className="editor-main"> |
| <EditorCore /> |
| </main> |
| </div> |
| ); |
| } |
|
|
| export default App; |
|
|