Philo’s journaling system provides a structured daily note-taking experience with powerful editing capabilities and automatic organization.
Daily Notes
Each journal entry is stored as a daily note with a date identifier (YYYY-MM-DD format). Notes are automatically organized and can be accessed by date.
Note Structure
interface DailyNote {
date : string ; // YYYY-MM-DD format
content : string ; // JSON-serialized editor content
city ?: string ; // Optional location data
}
Rich Text Editor
Philo uses Tiptap (ProseMirror-based) for a powerful editing experience with extensive formatting capabilities.
Supported Features
Text Formatting Bold, italic, underline, strikethrough, highlighting, and inline code
Structure Headings (H1-H6), paragraphs, bullet lists, numbered lists, and task lists
Tables Resizable tables with headers and cells
Links & Images Autolink detection, image drag-and-drop, and paste support
Keyboard Shortcuts
Shortcut Action ⌘/Ctrl + ASelect all content ⌘/Ctrl + LToggle task list BackspaceConvert heading to paragraph (at start of line) ⌘/Ctrl + ClickOpen links
Editor Configuration
The editor is configured in src/components/journal/EditableNote.tsx with these extensions:
const editor = useEditor ({
extensions: [
StarterKit . configure ({
heading: { levels: [ 1 , 2 , 3 , 4 , 5 , 6 ] },
listKeymap: false ,
paragraph: false ,
}),
CustomParagraph ,
Image . configure ({ inline: true , allowBase64: false }),
Underline ,
Placeholder ,
Link . configure ({ openOnClick: false , autolink: true }),
TaskList ,
CustomTaskItem . configure ({ nested: true }),
Table . configure ({ resizable: true }),
Highlight ,
HashtagExtension ,
ExcalidrawExtension ,
WidgetExtension ,
FileHandler ,
],
});
Auto-Save
Changes are automatically saved with a 500ms debounce to prevent excessive writes:
const saveDebounced = useDebounceCallback (( jsonStr : string ) => {
const updated = { ... noteRef . current , content: jsonStr };
selfUpdateRef . current = true ;
if ( onSaveRef . current ) {
onSaveRef . current ( updated );
} else {
saveDailyNote ( updated ). catch ( console . error );
}
}, 500 );
The editor stores content in JSON format for reliable serialization and deserialization.
Storage
Notes are persisted using Tauri’s filesystem APIs with JSON serialization:
// Save a daily note
await saveDailyNote ({
date: "2026-03-04" ,
content: JSON . stringify ( editorJSON )
});
// Load a daily note
const note = await loadDailyNote ( "2026-03-04" );
Link Handling
Links can be opened with ⌘/Ctrl + Click:
Link . extend ({
addProseMirrorPlugins () {
return [
new Plugin ({
props: {
handleClick ( _view , _pos , event ) {
if ( ! ( event . metaKey || event . ctrlKey )) return false ;
const anchor = ( event . target as HTMLElement ). closest ( "a" );
if ( anchor && anchor . href ) {
event . preventDefault ();
openUrl ( anchor . href );
return true ;
}
return false ;
},
},
}),
];
},
})
Images use asset URLs resolved through resolveAssetUrl() rather than base64 encoding for better performance.
The editor includes a contextual bubble menu (EditorBubbleMenu) that appears on text selection, providing quick access to formatting options.
Best Practices
Use structured content
Leverage headings, lists, and tables to organize information clearly
Tag with hashtags
Use the HashtagExtension to create searchable tags like #project or #ideas
Embed rich content
Combine text with images, drawings (Excalidraw), and AI-generated widgets
Create task lists
Use task lists for actionable items that can roll over to future days