Skip to main content
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

ShortcutAction
⌘/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");
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.

Bubble Menu

The editor includes a contextual bubble menu (EditorBubbleMenu) that appears on text selection, providing quick access to formatting options.

Best Practices

1

Use structured content

Leverage headings, lists, and tables to organize information clearly
2

Tag with hashtags

Use the HashtagExtension to create searchable tags like #project or #ideas
3

Embed rich content

Combine text with images, drawings (Excalidraw), and AI-generated widgets
4

Create task lists

Use task lists for actionable items that can roll over to future days

Build docs developers (and LLMs) love