Skip to content

Commit 33bce2e

Browse files
committed
feat(desktop): add editor back, forward, and zen
Match the note-list header with a chrome row: pop-out, distraction-free, and note history. Zen hides sidebar and list so the editor can take the frame.
1 parent d270f96 commit 33bce2e

15 files changed

Lines changed: 414 additions & 65 deletions

File tree

apps/desktop/src/renderer/App.tsx

Lines changed: 90 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ import { useAppearanceSettings } from './hooks/useAppearanceSettings';
3939
import { useOfficialThemes } from './hooks/useOfficialThemes';
4040
import { useResizableLayout } from './hooks/useResizableLayout';
4141
import { useSyncStore } from './stores/syncStore';
42+
import {
43+
canGoBack,
44+
canGoForward,
45+
emptyNoteHistory,
46+
historyBack,
47+
historyForward,
48+
visitNote,
49+
} from './utils/noteHistory';
4250
import { useDeepLinks } from './hooks/useDeepLinks';
4351
import { useAutoSave } from './hooks/useAutoSave';
4452
import { useNoteActions } from './hooks/useNoteActions';
@@ -75,19 +83,24 @@ function NotesApp() {
7583
sidebarWidth,
7684
notelistWidth,
7785
sidebarCollapsed,
86+
distractionFree,
7887
toggleSidebar,
88+
toggleDistractionFree,
7989
startResizeSidebar,
8090
startResizeNotelist,
8191
} = useResizableLayout();
8292

93+
const hideSidebar = sidebarCollapsed || distractionFree;
94+
const hideNoteList = distractionFree;
95+
8396
useEffect(() => {
8497
const setVisibility = window.dripnex.windows.setButtonVisibility;
8598
if (typeof setVisibility !== 'function') return;
86-
void setVisibility(!sidebarCollapsed);
99+
void setVisibility(!hideSidebar);
87100
return () => {
88101
void setVisibility(true);
89102
};
90-
}, [sidebarCollapsed]);
103+
}, [hideSidebar]);
91104

92105
// Navigation state from Zustand
93106
const navigation = useNavigation();
@@ -129,6 +142,11 @@ function NotesApp() {
129142
const [selectedNote, setSelectedNote] = useState<NoteSnapshot | null>(null);
130143
const selectedNoteRef = useRef<NoteSnapshot | null>(null);
131144
selectedNoteRef.current = selectedNote;
145+
const [noteHistory, setNoteHistory] = useState(emptyNoteHistory);
146+
const setSelectedNoteAndVisit = useCallback((note: NoteSnapshot | null) => {
147+
setSelectedNote(note);
148+
if (note) setNoteHistory(prev => visitNote(prev, note.id));
149+
}, []);
132150
const { appAPI, dataAPI, pluginSlot } = usePluginRuntime(selectedNoteRef);
133151
const { searchQuery, debouncedSearch, handleSearch, clearSearch } = useDebouncedSearch(300);
134152
const [isGraphOpen, setIsGraphOpen] = useState(false);
@@ -250,7 +268,7 @@ function NotesApp() {
250268
handleStatusChange,
251269
} = useNoteActions({
252270
selectedNote,
253-
setSelectedNote,
271+
setSelectedNote: setSelectedNoteAndVisit,
254272
selectedNotebookId,
255273
clearSearch,
256274
appAPI,
@@ -292,9 +310,31 @@ function NotesApp() {
292310
setIsGraphOpen,
293311
searchQuery,
294312
clearSearch,
295-
setSelectedNote,
313+
setSelectedNote: setSelectedNoteAndVisit,
296314
displayedNotes,
297315
onSelectNote: handleSelectNote,
316+
onNoteBack: () => {
317+
const { state, id } = historyBack(noteHistory);
318+
if (!id) return;
319+
setNoteHistory(state);
320+
void window.dripnex.notes.get(id).then(result => {
321+
if (result.ok) setSelectedNote(result.data);
322+
});
323+
},
324+
onNoteForward: () => {
325+
const { state, id } = historyForward(noteHistory);
326+
if (!id) return;
327+
setNoteHistory(state);
328+
void window.dripnex.notes.get(id).then(result => {
329+
if (result.ok) setSelectedNote(result.data);
330+
});
331+
},
332+
onToggleZen: toggleDistractionFree,
333+
onOpenInWindow: () => {
334+
const note = selectedNoteRef.current;
335+
if (!note) return;
336+
void window.dripnex.windows.openNote(note.id, note.title || 'Note');
337+
},
298338
});
299339

300340
// Determine selected quick filter for NoteList header
@@ -371,14 +411,14 @@ function NotesApp() {
371411
<div className="app__layout">
372412
<aside
373413
className="app__sidebar"
374-
data-collapsed={sidebarCollapsed ? 'true' : 'false'}
414+
data-collapsed={hideSidebar ? 'true' : 'false'}
375415
style={{ width: sidebarWidth }}
376-
aria-hidden={sidebarCollapsed}
377-
inert={sidebarCollapsed || undefined}
416+
aria-hidden={hideSidebar}
417+
inert={hideSidebar || undefined}
378418
>
379419
<Sidebar onOpenGraph={() => setIsGraphOpen(true)} />
380420
</aside>
381-
{!sidebarCollapsed ? (
421+
{!hideSidebar ? (
382422
<div
383423
className="resize-handle"
384424
onMouseDown={startResizeSidebar}
@@ -387,7 +427,13 @@ function NotesApp() {
387427
/>
388428
) : null}
389429

390-
<section className="app__notelist" style={{ width: notelistWidth }}>
430+
<section
431+
className="app__notelist"
432+
data-collapsed={hideNoteList ? 'true' : 'false'}
433+
style={{ width: notelistWidth }}
434+
aria-hidden={hideNoteList}
435+
inert={hideNoteList || undefined}
436+
>
391437
<NoteList
392438
notes={displayedNotes}
393439
selectedId={selectedNote?.id ?? null}
@@ -415,12 +461,14 @@ function NotesApp() {
415461
isLoading={isLoading}
416462
/>
417463
</section>
418-
<div
419-
className="resize-handle"
420-
onMouseDown={startResizeNotelist}
421-
role="separator"
422-
aria-orientation="vertical"
423-
/>
464+
{!hideNoteList ? (
465+
<div
466+
className="resize-handle"
467+
onMouseDown={startResizeNotelist}
468+
role="separator"
469+
aria-orientation="vertical"
470+
/>
471+
) : null}
424472

425473
<main className="app__editor">
426474
{isGraphOpen ? (
@@ -462,6 +510,33 @@ function NotesApp() {
462510
onWikilinkClick={handleWikilinkClick}
463511
onNavigateToNote={handleSelectNote}
464512
onNoteUpdate={setSelectedNote}
513+
canBack={canGoBack(noteHistory)}
514+
canForward={canGoForward(noteHistory)}
515+
distractionFree={distractionFree}
516+
onBack={() => {
517+
const { state, id } = historyBack(noteHistory);
518+
if (!id) return;
519+
setNoteHistory(state);
520+
void window.dripnex.notes.get(id).then(result => {
521+
if (result.ok) setSelectedNote(result.data);
522+
});
523+
}}
524+
onForward={() => {
525+
const { state, id } = historyForward(noteHistory);
526+
if (!id) return;
527+
setNoteHistory(state);
528+
void window.dripnex.notes.get(id).then(result => {
529+
if (result.ok) setSelectedNote(result.data);
530+
});
531+
}}
532+
onToggleZen={toggleDistractionFree}
533+
onOpenWindow={() => {
534+
if (!selectedNote) return;
535+
void window.dripnex.windows.openNote(
536+
selectedNote.id,
537+
selectedNote.title || 'Note'
538+
);
539+
}}
465540
/>
466541
)}
467542
</main>

apps/desktop/src/renderer/components/CommandPalette.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ import {
2828
RefreshCw,
2929
Hash,
3030
BookMarked,
31+
ArrowLeft,
32+
ArrowRight,
33+
Maximize2,
34+
SquareArrowOutUpRight,
3135
} from 'lucide-react';
3236
import type { LucideIcon } from 'lucide-react';
3337
import type { CommandCategory } from '@dripnex/command-registry';
@@ -95,6 +99,10 @@ const ICON_MAP: Record<string, LucideIcon> = {
9599
RefreshCw,
96100
Hash,
97101
BookMarked,
102+
ArrowLeft,
103+
ArrowRight,
104+
Maximize2,
105+
SquareArrowOutUpRight,
98106
};
99107

100108
const CATEGORY_ORDER: { category: CommandCategory; label: string }[] = [

apps/desktop/src/renderer/components/NoteEditor.module.css

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,28 @@
6363
animation: editor-spin 0.8s linear infinite;
6464
}
6565

66-
.note-editor-header {
66+
.note-editor-chrome {
6767
display: grid;
6868
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
6969
align-items: center;
70-
column-gap: 12px;
71-
padding: 6px 16px;
70+
column-gap: 8px;
71+
padding: 8px 10px;
72+
min-height: 44px;
73+
border-bottom: 1px solid var(--border-subtle);
74+
-webkit-app-region: drag;
75+
}
76+
77+
.note-editor-chrome-left {
78+
display: flex;
79+
align-items: center;
80+
gap: 2px;
81+
-webkit-app-region: no-drag;
82+
}
83+
84+
.note-editor-header {
85+
display: flex;
86+
align-items: center;
87+
padding: 8px 16px 4px;
7288
border-bottom: none;
7389
-webkit-app-region: drag;
7490
}

0 commit comments

Comments
 (0)