An opinionated journaling plugin for Obsidian that follows Journey's approach to journaling: one note per day, with date, timezone, location, photos, and a few lines of text per entry.
The plugin adds three custom Bases views (Entries, Calendar, Memories), commands for stamping coordinates from device GPS or from Immich photos, and integrates with Obsidian Immich Memories (getPhotosForDate public API) to resolve thumbnails.
These views require Obsidian's Bases core plugin to be enabled. They are designed to be used as views on a Base that filters down to your journal entries (e.g. by folder or tag).
Vertical, paginated feed of journal entries sorted by journalDate descending (newest first) with infinite scroll (IntersectionObserver + 7-item pages).
Each card shows:
- Date header — formatted as
MMM D, YYYY | ddddfromjournalDatefrontmatter. - Title — extracted from the filename by subtracting the leading date. Convention is
YYYY-MM-DD My Title→My Title. Optional configured prefix (defaultJournal) is stripped first for backwards compatibility. If the filename is date-only, no title is shown. - Body preview — markdown-rendered note body (frontmatter stripped, up to 200 lines, overflow hidden) via
MarkdownRenderer. - Thumbnail — first Immich photo for that entry's date/timezone (
getPhotosForFile→getPhotosForDate), rendered as a square background image. Cards without photos getno-imagestyling and expanded text.
Clicking a card opens the underlying file.
Source: src/views/entries.ts, src/views/shared.ts:entryTitle, src/views/shared.ts:renderEntryTextBlock, src/immich.ts:getPhotosForFile
Month grid with one cell per day:
- Days with an entry are tinted (
has-entry); days that also have an Immich photo use the photo as cell background (has-image). - Today is outlined, out-of-month days are muted.
- Clicking a day with an entry opens that entry.
- Toolbar with prev/next month chevrons and a Jump to date modal (month dropdown + year input).
- Viewed month is persisted per view via
this.config(viewedMonth→YYYY-MM).
Source: src/views/calendar.ts
Horizontal carousel of past anniversaries that have an entry on the same calendar day:
- Candidates:
30 days ago,1 year ago,2 years ago, … up to the oldest entry's year. - Each card shows the period label (e.g.
2 years ago) and the first Immich photo as background, or an empty dashed placeholder. - Tapping a card opens a full-screen, swipeable slideshow modal (
MemoriesModal):- Each slide is either an Immich photo (prefers
previewUrl, falls back tofullsizeUrl/thumbnailUrl) or text-only fallback when no photos exist. - Gestures: swipe left/right to advance, swipe up to close, click media to advance, close button.
- Bottom panel shows truncated text preview (
renderEntryTextBlock) and Open note › CTA that opens the underlying file. - Photos are fetched per anniversary date via
getPhotosForDateString(dateStr, timeZone)where timezone is read from frontmatter using Immich Memories' configuredtimezoneField.
- Each slide is either an Immich photo (prefers
Source: src/views/memories.ts
All thumbnail and slideshow photos come from the Immich Memories plugin:
- Plugin ID:
obsidian-immich-memories. - Public API
getPhotosForDate(dateStr, timeZone)is accessed viaapp.plugins.plugins['obsidian-immich-memories'].api. - Date and timezone are read from frontmatter using the field names configured in Settings → Immich Memories (defaults:
dateandtimezone). The journal plugin reads those fields with case-insensitive fallback and supportstimeZone/timezone/timeZonevariants. getPhotosForFileandgetPhotosForDateStringinsrc/immich.tswrap the public API withUTCfallback.- EXIF lat/lng for the Set coordinates from Immich memories command is not part of the public API, so the plugin calls Immich directly (
GET /api/assets/{id}) usingimmichServerUrl/immichApiKeyfrom Immich Memories settings.
Source: src/immich.ts
- Set coordinates from device location (
set-coordinates-from-device-gps) — writes the device's current GPS latitude/longitude into the active note'scoordinatesfrontmatter as"lat, lon". RespectsjournalEntriesFoldersetting when deciding if a file is a journal file (used for listeners, but command checks active file). ShowsNoticeon success/failure. - Set coordinates from Immich memories (
set-coordinates-from-immich-images) — fetches photos for the active file's date viagetPhotosForFile, then looks up EXIF lat/lon via Immich asset API and writes the first found coordinates intocoordinates.
Both commands are registered in src/main.ts and src/frontmatter.ts:updateCoordinates.
On any metadataCache.on("changed") event inside the configured journal folder:
- Extracts
journalTime(orYYYY-MM-DDprefix) from frontmatter. - If it differs from current
journalDateproperty (setting:journalDateProperty, defaultjournalDate), updates that frontmatter field. - Renames the file if basename starts with a date:
YYYY-MM-DD*→ new date + rest of name, preserving title. Example:2024-01-01 My Day.md→2024-02-03 My Day.mdwhenjournalTimechanges to2024-02-03.
Source: src/main.ts:handleJournalTimeChange, extractDate
entryTitle(file, prefix?) in src/views/shared.ts:
// Spec: filename minus "YYYY-MM-DD "
"2025-08-21 My awesome day" → "My awesome day"
"2025-08-21" → "" (no title)If a journalPrefixProperty (e.g. Journal ) is configured and the basename starts with it, that prefix is stripped before date parsing. The Entries view shows this title as journal-entry-card-title (bold, ellipsis truncated).
Configurable via Settings → Community plugins → Journal:
- Journal entries folder — vault-relative folder containing entries. Listeners (
handleJournalTimeChange) and folder checks only act on files inside this folder. Leave empty to act on all files. Has folder autocomplete viaFolderSuggest. - Entry date property — frontmatter property name that holds the canonical entry date for sorting/grouping in Bases views. Default:
journalDate. Note: Immich photo lookup uses Immich Memories'dateField/timezoneField, not this property. - Journal entry prefix — string prefix at the beginning of a journal entry filename that is ignored when deriving the title. Default:
Journal. Example:Journal 2025-08-21 Trip→ titleTrip.
Defaults in src/settings.ts:DEFAULT_SETTINGS.
Expected filename format:
YYYY-MM-DD Optional title.md
- Date prefix is used for display fallback and auto-rename.
- Title is everything after
YYYY-MM-DD(space). The Entries view card extracts and shows this title. - If you use a prefix like
Journal, configure it in settings so title extraction strips it first.
journal-entry-template.md is a Templater template:
---
createdTime: <% tp.file.creation_date() %>
journalDate: <% tp.date.now() %>
journalTime: <% tp.date.now("YYYY-MM-DDTHH:mm:ss") %>
timeZone: <% Intl.DateTimeFormat().resolvedOptions().timeZone %>
coordinates:
sentiment:
isFavorite:
tags:
- "#journal"
cssclasses:
- journal
- immichBanner
---
<%_ await tp.file.rename(tp.date.now("YYYY-MM-DD")) %>
<%_ await app.commands.executeCommandById("obsidian-journal:set-coordinates-from-device-gps") %>It sets up frontmatter (journalDate, journalTime, timeZone, coordinates, sentiment, isFavorite, #journal, journal + immichBanner cssclasses), renames the file to current date, and triggers device GPS stamping.
Modify immichBanner class in your CSS/snippets as desired for photo banners.
npm install
npm run dev # watch build via esbuild
npm run build # production build: tsc -noEmit -skipLibCheck + esbuild
npm run lint # eslint with obsidianmd pluginBuild output is bundled to main.js at the repo root alongside manifest.json and styles.css (required release artifacts). Source lives in src/:
src/
main.ts # Plugin lifecycle, commands, journalTime watcher, Bases view registration
settings.ts # Settings interface, defaults, FolderSuggest, settings tab
frontmatter.ts # updateCoordinates helper
immich.ts # Public API wrapper for Immich Memories, lat/lng fetch
views/
shared.ts # parseEntryDate, readTimezoneForEntry, entryTitle, renderEntryTextBlock, openEntry, photos helpers
entries.ts # EntriesBasesView — paginated feed with title + body + thumbnail
calendar.ts # CalendarBasesView — month grid + jump modal
memories.ts # MemoriesBasesView — anniversary carousel + full-screen slideshow modal
Symlink or copy main.js, manifest.json, and styles.css into <Vault>/.obsidian/plugins/obsidian-journal/, then enable the plugin in Settings → Community plugins and enable Bases core plugin.
The migration/ directory contains one-off scripts that convert a Journey export (JSON per entry) into Obsidian-compatible Markdown notes:
src/journey-to-obsidian.ts— converts each JSON toYYYY-MM-DD.mdwith frontmatter (journalDate,journalTime,createdTime,modifiedTime,timeZone,coordinates,sentiment,isFavorite, tags, cssclasses) and body via Turndown.src/journey-to-obsidian-title-as-filename.ts— same, but extracts leading<h1>from Journey HTML text as title:YYYY-MM-DD Title.md. Drops the<h1>from body, sanitizes colons (1:1→1-1,:→)), skips files with invalid filename chars (*"<>\/:|?) and reports them.
Both upload? Original version had Immich upload logic removed — current scripts preserve structure only. See migration/package.json for tsx runner and migration/.env.example for setup. Output dir will contain converted Markdown files.