Search...Search plugins and themes...
⌘K
Sign in
  • Get started
  • Download
  • Pricing
  • Enterprise
  • Account
  • Obsidian
  • Overview
  • Sync
  • Publish
  • Canvas
  • Mobile
  • Web Clipper
  • CLI
  • Learn
  • Help
  • Developers
  • Changelog
  • About
  • Roadmap
  • Blog
  • Resources
  • System status
  • License overview
  • Terms of service
  • Privacy policy
  • Security
  • Community
  • Plugins
  • Themes
  • Discord
  • Forum / 中文论坛
  • Merch store
  • Brand guidelines
Follow us
DiscordTwitterBlueskyThreadsMastodonYouTubeGitHub
© 2026 Obsidian

Close Reflect

InVerseInVerse37 downloads

Intercept the app quit and show a reflection prompt with an editable title, Markdown body and action buttons before the window closes.

Add to Obsidian
  • Overview
  • Scorecard
  • Updates6

Intercept Obsidian's quit and show a reflection prompt before the window closes — a chance to look at where you are before you leave.

The prompt has an editable title, Markdown body, and as many action buttons as you want. Buttons can cancel the quit, let the app close, open a note, run a command, or open a URL.

The body is real Markdown, so wikilinks, external links, embeds, Meta Bind controls and task checkboxes all work inside it — and embeds are clickable anywhere on their body, which they are not by default. Clicking something that does anything cancels the quit and steps the prompt aside: you are going somewhere, not leaving.

The plugin depends on Obsidian internals that a future release could change — see Limitations.

How the interception works

Obsidian does not make this easy, and the interesting part of this plugin is the three obstacles it works around. All three were established by reading Obsidian 1.13.7's own bundle, and each one is verified by the plugin's own developer log.

1. Obsidian's quit hook erases itself. The workspace quit event is fired from inside Obsidian's own window.onbeforeunload hook, and that hook starts by setting window.onbeforeunload = null so the window.close() it re-issues cannot loop back into it. One session therefore gets exactly one quit event. The plugin captures the hook before its first run and puts it back after a quit it cancelled, which is what lets the second close be intercepted too.

2. Obsidian force-closes any window that survives a close for three seconds. Its main process arms setTimeout(() => { !v.defaultPrevented && g.destroy() }, 3000) on every window close, and destroy() bypasses beforeunload entirely. The catch is that close.defaultPrevented is only ever written by a main-process listener calling preventDefault() — a renderer's beforeunload cancellation travels a different channel (will-prevent-unload) and never marks it. Obsidian's own bootstrap hands the renderer the window through @electron/remote (window.electronWindow), so the plugin joins that same close event and marks it once it has decided to hold — which makes the timer stand down. The mark has to happen after dispatch, not during it: preventing the close outright would stop beforeunload from running at all, and the prompt would never appear.

3. Holding the quit strands Obsidian's "Saving..." screen. That screen lives on a module-private singleton inside Obsidian's bundle and is only taken down by the very window.close() the plugin swallows. There is no way to reach the instance, so the plugin repeats what its hide() does — remove the container and drop the in-progress body class.

Every failure path fails open: an unanswered prompt follows the configured timeout action, and a prompt that could not be shown at all releases the quit, so a broken overlay can never leave the app unclosable.

What counts as leaving

Only a window close raises the prompt on the desktop — clicking the ✕, or anything else that closes the window.

A menu quit, a keyboard shortcut quit, and a restart do not. Those all go through app.quit(), which sets a flag the window close never touches, and the plugin reads that flag (is-quitting) to tell them apart. Intercepting them would mean stopping an update's relaunch to ask a question about the session it is ending anyway.

An in-place reload — Reload app without saving, switching language or vault, toggling restricted mode — never reaches the prompt either, and needs no special handling: Obsidian does those with window.location.reload(), which never touches the window close path, so the quit event is not fired at all.

On mobile

Mobile has no quit to intercept — the quit event is fired from inside Obsidian's Electron hook, which mobile does not have. What it has instead are gestures, and the plugin answers two of them, each behind its own toggle:

Gesture What happens
Back button The prompt follows Obsidian's own "press back again to exit" notice. A press that goes back a note, or collapses a sidebar, raises no notice and is left alone.
Going back to the home screen The prompt is raised as the app is backgrounded, and is waiting when you come back.

Reading Obsidian's notice is what makes the timing right. Capacitor calls listeners in registration order and Obsidian registered its own long before this plugin loaded, so by the time the plugin's listener runs Obsidian has already acted — collapsed a sidebar, gone back a note, or raised the notice. That rules out deciding from the workspace state, because a sidebar Obsidian just collapsed reads exactly like "both sidebars are closed", which is the shape of leaving.

There is a further asymmetry. Capacitor delivers the back button to every listener and offers no way to stop the others, so this runs alongside Obsidian's handler rather than replacing it: pressing back twice in quick succession still sends the app to the background, because that second press is Obsidian's. And backgrounding cannot be cancelled at all — the app is already gone — which is why that second toggle is off by default and why "cancel the quit" on mobile simply means the prompt closes.

The obvious way to take the gesture over, removeAllListeners(), is not used: it drops every other listener Obsidian registered for that plugin too — URI opens, share intents, app state — and leaves the app worse off.

The edit modal

Settings keep the behavioural options. The title, the text and the buttons are edited in a dedicated modal: editor on the left (a real Obsidian Markdown editor, with live preview and [[ completion), live preview on the right, Save and Cancel at the bottom. Everything is edited on a draft, so Cancel really discards.

The modal adapts to the content source:

Inline text Linked note
Editor the overlay text the note's body, frontmatter excluded
Save writes the text into the settings writes the body back into the note, keeping its frontmatter

In linked mode the note is only written when its body actually changed, and only if it was readable in the first place — a note that could not be read is never written over.

The embedded editor borrows Obsidian's own CodeMirror through the module table Obsidian exposes to plugins, so no second copy of CodeMirror is bundled and Obsidian's internal editor state fields keep working. If that ever stops being available the modal falls back to a plain textarea.

Settings

Setting Meaning
Content source Write the text inline, or render it from a note in the vault.
Title, text and buttons Opens the edit modal.
Linked note The note whose contents the overlay shows. Start typing to search.
Remembered copy A copy of the linked note, used when the note cannot be read.
Interceptions before release How many quits to interrupt. Once spent, later quits close without asking.
Release timeout How long to wait for an answer.
When the timeout expires Cancel the quit and stay open, or let the app close. Cancelling is the default.
Write diagnostics to a file Appends one entry per quit attempt inside the vault.
Developer log Echoes diagnostics to the console and writes a step-by-step trace to close-reflect-trace.log in the system temp folder.
Ask on the back button Mobile only.
Ask on going back to the home screen Mobile only, off by default.

Limitations

  • The quit event is documented as "Not guaranteed to actually run." Crashes, an external kill, or a priority shutdown will skip the interception. This is best-effort.
  • Registering a quit task breaks Reload app without saving: the reload command closes the window instead of reloading. Disable the plugin if you need that command.
  • The interception relies on Obsidian internals (@electron/remote, the shape of its quit hook, the progress-screen markup, and on mobile the Capacitor bridge and the shape of Obsidian's own back handling). A future Obsidian release could change any of them; each is guarded and falls back rather than breaking the app.
  • The prompt is a speed bump, not a lock: on desktop it holds the quit, but on mobile Obsidian's own second back press still backgrounds the app.

Data and privacy

  • No network access. No telemetry. No analytics.
  • With Write diagnostics to a file on, the plugin appends one entry per quit attempt to a vault-relative path you choose.
  • With Developer log on, it also writes a step-by-step trace to close-reflect-trace.log in the system temp folder. Both are off by default.

Development

bun install
bun run build      # bundles src/main.ts -> main.js
bun run typecheck  # tsc --noEmit

Copy main.js, manifest.json and styles.css into <vault>/.obsidian/plugins/close-reflect/ and enable the plugin.

Credits

The embedded Markdown editor in src/markdown-editor.ts is ported from EmbeddableMarkdownEditor by Fevol, with the prototype-extraction technique originally from mgmeyers/obsidian-kanban — both MIT licensed. Only that MIT-licensed editor-construction code was carried over.

License

MIT

HealthExcellent
ReviewCaution
About
Intercept Obsidian's quit and show a reflection prompt before the window closes. Edit the title and Markdown body, and add action buttons to cancel the quit, close the app, open a note, run a command, or open a URL.
ThinkingCommands
Details
Current version
1.3.1
Last updated
6 days ago
Created
Last week
Updates
6 releases
Downloads
37
Compatible with
Obsidian 1.5.0+
Platforms
Desktop, Mobile
License
MIT
Report bugRequest featureReport plugin
Author
InVerseInVerselmce72
GitHublmce72
  1. Community
  2. Plugins
  3. Thinking
  4. Close Reflect

Related plugins

Copilot

Run AI agents such as Claude Code, Codex, and OpenCode inside your vault. Turn your second brain into a smart assistant that gets knowledge work done.

ExcaliBrain

An interactive, structured mind-map of your Obsidian vault.

Quick Switcher++

Enhanced Quick Switcher, search open panels, and symbols.

Find orphaned files and broken links

Find files that are not linked anywhere and would otherwise be lost in your vault. In other words: files with no backlinks.

Note Toolbar

Add customizable toolbars to your notes.

Commander

Customize your workspace by adding commands everywhere, create macros and supercharge your mobile toolbar.

Tab Switcher

Switch your tabs with Ctrl + Tab in recently used order like in a browser.

Sort and Permute lines

Sort and Permute lines in whole file or selection.

Lean Terminal

Embedded terminal panel powered by xterm.js and node-pty - no external windows.

Heading Shifter

Easily Shift and Change Markdown headings.