Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions QuickMD/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to QuickMD will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- **Reload** (⌘R, View ▸ Reload, or the ↻ button in the top-right cluster): re-reads the document from disk now and restarts the file watcher, for the times auto-reload does not notice a change (some network volumes and editors' save strategies). A toast says whether anything changed; a file that is gone shows the "File no longer exists" banner. Off while Source Edit shows, where the editor already follows the disk.

## [1.12.0] - 2026-10-05

Source Edit: QuickMD stays a viewer first, but you can now fix the Markdown you are reading without leaving the window. Asked for by [@KillSwitchSIG](https://github.com/KillSwitchSIG) (#31) and [@vinaymve](https://github.com/vinaymve) (#34).
Expand Down
9 changes: 9 additions & 0 deletions QuickMD/QuickMD/MarkdownExport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ struct FocusedOpenInExternalEditorKey: FocusedValueKey {
typealias Value = () -> Void
}

/// View ▸ Reload (⌘R) and the reload pill.
struct FocusedReloadDocumentKey: FocusedValueKey {
typealias Value = () -> Void
}

struct FocusedZoomActionKey: FocusedValueKey {
typealias Value = (MarkdownZoom) -> Void
}
Expand Down Expand Up @@ -147,6 +152,10 @@ extension FocusedValues {
get { self[FocusedOpenInExternalEditorKey.self] }
set { self[FocusedOpenInExternalEditorKey.self] = newValue }
}
var reloadDocumentAction: (() -> Void)? {
get { self[FocusedReloadDocumentKey.self] }
set { self[FocusedReloadDocumentKey.self] = newValue }
}
var zoomAction: ((MarkdownZoom) -> Void)? {
get { self[FocusedZoomActionKey.self] }
set { self[FocusedZoomActionKey.self] = newValue }
Expand Down
20 changes: 20 additions & 0 deletions QuickMD/QuickMD/MarkdownView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,9 @@ struct MarkdownView: View {
.transition(.opacity)
}
if documentURL != nil {
ReloadDocumentButton(theme: theme) {
reloadManually()
}
EditSourceButton(theme: theme) {
toggleSourceEdit()
}
Expand Down Expand Up @@ -526,6 +529,7 @@ struct MarkdownView: View {
})
.focusedSceneValue(\.copyDocumentAction, { copyToClipboard(currentText) })
.focusedSceneValue(\.openInExternalEditorAction, { openInExternalEditor() })
.focusedSceneValue(\.reloadDocumentAction, { reloadManually() })
.focusedSceneValue(\.toggleDocumentListAction, {
guard !isReadingMode else { return }
withAnimation(.easeInOut(duration: 0.2)) { isDocumentListVisible.toggle() }
Expand Down Expand Up @@ -844,6 +848,22 @@ struct MarkdownView: View {
}
}

/// ⌘R / the reload pill: re-read the file now, and restart the watcher in
/// case it lost track of the file (some volumes and save strategies never
/// deliver an event). Not while Source Edit shows — the session owns the
/// disk then.
private func reloadManually() {
guard !editSession.isActive, let url = documentURL else { return }
guard FileManager.default.fileExists(atPath: url.path) else {
withAnimation(.easeInOut(duration: 0.2)) { fileMissing = true }
return
}
let before = currentText
reloadFromDisk()
startWatching(url)
showToast(currentText == before ? "Reloaded \u{00B7} no changes" : "Reloaded")
}

/// ⌘E — hand the document off to the user's configured editor, at the line
/// the reader is at when that editor documents a line link (v1.11 E-D1).
private func openInExternalEditor() {
Expand Down
18 changes: 18 additions & 0 deletions QuickMD/QuickMD/QuickMDApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ struct QuickMDApp: App {
CopyMarkdownCommand()
}
CommandGroup(after: .toolbar) {
ReloadDocumentCommand()
Divider()
ZoomCommands()
ToggleToCCommand()
ToggleDocumentListCommand()
Expand Down Expand Up @@ -209,6 +211,22 @@ struct CopyMarkdownCommand: View {
}
}

/// View ▸ Reload (⌘R) — re-reads the frontmost document from disk, for the
/// times auto-reload did not notice a change (network volumes, some editors'
/// save strategies). Off while Source Edit shows: the session owns the disk.
struct ReloadDocumentCommand: View {
@FocusedValue(\.reloadDocumentAction) var reloadDocumentAction
@FocusedValue(\.sourceEditState) var sourceEditState

var body: some View {
Button("Reload") {
reloadDocumentAction?()
}
.keyboardShortcut("r", modifiers: .command)
.disabled(reloadDocumentAction == nil || sourceEditState?.isEditing == true)
}
}

struct OpenInExternalEditorCommand: View {
@FocusedValue(\.openInExternalEditorAction) var openInExternalEditorAction
@FocusedValue(\.sourceEditState) var sourceEditState
Expand Down
16 changes: 16 additions & 0 deletions QuickMD/QuickMD/Views/ChromeButtons.swift
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,22 @@ struct CopySourceButton: View {
}
}

// MARK: - Reload Button

/// Top-right button: re-read the document from disk (⌘R).
struct ReloadDocumentButton: View {
let theme: MarkdownTheme
let action: () -> Void

var body: some View {
ChromePill(theme: theme, title: "Reload", help: "Reload from disk (⌘R)", action: action) {
Image(systemName: "arrow.clockwise")
.font(.system(size: 11))
}
.accessibilityIdentifier("reload-document")
}
}

// MARK: - Edit Source Button

/// Top-right pencil button — fixes the source in place (Source Edit, ⌥⌘E).
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Perfect for developers, writers, students, and anyone who works with Markdown da

### Works with Your Editor
- **Auto-reload** — the document refreshes the moment another app saves it. Turn on auto-save in VS Code, Cursor or Zed, or let an AI agent write the file, and QuickMD is a live preview
- **Reload (`⌘R`)** — re-read the file on demand, for the rare change auto-reload misses (network volumes, unusual save strategies)
- **Open in External Editor (`⌘E`)** — for longer writing sessions: hands the file to your editor (auto-detected, configurable in Settings). VS Code, BBEdit, TextMate and Nova open at the line you are reading

### Complete Markdown Support
Expand Down
Loading