Skip to content

Add editable untitled Markdown previews - #244

Open
whrit wants to merge 3 commits into
pluk-inc:mainfrom
whrit:feat/untitled-md-preview
Open

Add editable untitled Markdown previews#244
whrit wants to merge 3 commits into
pluk-inc:mainfrom
whrit:feat/untitled-md-preview

Conversation

@whrit

@whrit whrit commented Jul 28, 2026

Copy link
Copy Markdown

Summary

Adds workflow for previewing raw Markdown without creating a file first.

Now opens an editable untitled document when launched without a file. Users can paste Markdown directly from clipboard, switch to the existing full rendered preview, return to the source editor without losing content, and optionally save the draft as a Markdown file.

Reuses app’s existing editor and renderer. Does not add a second parser, rendering surface, temporary-file layer, or separate Quick Look implementation.

Motivation

Previously, the renderer was effectively gated behind a local file URL. This was inconvenient when Markdown already existed in the clipboard ie content copied from GPT, Claude, mail, messages, notes, docs, or terminal.

Reviewing that Markdown required manually creating a file, saving it, and reopening it in Markdown Preview.

The renderer itself was already capable of rendering an in-memory String; the missing piece was an untitled document lifecycle around it.

Verification

Automated

xcodebuild \
-project md-preview.xcodeproj \
-scheme md-preview \
-configuration Debug \
CODE_SIGNING_ALLOWED=NO \
build

Result:

BUILD SUCCEEDED
swift test --package-path tests/swift-tests

Result:

Executed 94 tests, with 0 failures
git diff --check

* Design untitled Markdown preview workflow

* Plan untitled Markdown preview implementation

* Ignore local implementation worktrees

* Add editable untitled Markdown previews

* Keep folder opens out of the untitled placeholder editor

* Stop folder windows from becoming untitled editors

* Save untitled Markdown documents

* Preserve edits across untitled saves

Co-authored-by: Cursor <cursoragent@cursor.com>

* Open untitled Markdown drafts by default

Co-authored-by: Cursor <cursoragent@cursor.com>

* Preserve explicit Open during launch

Co-authored-by: Cursor <cursoragent@cursor.com>

* Allow creating untitled Markdown documents

* Harden untitled Markdown workflows

* remove(docs): delete outdated untitled markdown preview plans and specs

- Removed `2026-07-27-untitled-markdown-preview.md` due to completion and integration
- Removed `2026-07-27-untitled-markdown-preview-design.md` as it's no longer needed
- Ensured all related tasks and goals are reflected in current documentation

---------

Co-authored-by: Beckett_quik <beckett@quikturn.io>
Co-authored-by: Cursor <cursoragent@cursor.com>
Copilot AI review requested due to automatic review settings July 28, 2026 06:45
@whrit

whrit commented Jul 28, 2026

Copy link
Copy Markdown
Author

Think it's almost there- a good starting point

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an “untitled, editable” document lifecycle so Markdown Preview can launch without a file and still provide a source editor + rendered preview workflow, including saving drafts to disk, while ensuring folder-navigation windows don’t accidentally become editors.

Changes:

  • Create and manage URL-less (“Untitled”) documents that can enter edit mode, preserve draft contents across mode switches, and later adopt a chosen save location.
  • Harden edit-mode transitions and window/menu behaviors (focus editor on reveal, prevent stacked save panels, and handle “folder-only” navigator windows distinctly).
  • Update app/document metadata and misc housekeeping (document role, gitignore entries).

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
md-preview/SidebarViewController.swift Avoids unmounting an explicitly opened folder when displaying an untitled (URL-less) document.
md-preview/MarkdownDocument.swift Allows replacing markdown without requiring a file URL (supports untitled drafts).
md-preview/MainSplitViewController.swift Improves edit overlay lifecycle (focus on reveal; guard for early exit before fade-in completes).
md-preview/DocumentWindowController.swift Core untitled draft + Save/Save As workflow, save-panel gating, and folder-window vs draft-window behavior.
md-preview/AppDelegate.swift Schedules creation of a new untitled document on launch/reopen instead of prompting for a file.
Info.plist Marks the document type role as Editor to reflect new edit capability.
.gitignore Ignores local design/tooling directories.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread md-preview/DocumentWindowController.swift Outdated
@mfauzaan

Copy link
Copy Markdown
Member

Thanks man, this is a good start
I'll add little bit of UI changes and refine on top of this before merging the PR :)

…for markdown files

- Modified `allowedContentTypes` to include additional markdown file extensions from `Self.markdownFileExtensions`
- Ensures flexibility in handling various markdown file types
- Maintains consistency with existing functionality while enhancing extensibility
Copilot AI review requested due to automatic review settings July 28, 2026 15:15

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated 1 comment.

Comment on lines 269 to 271
func windowWillReturnUndoManager(_ window: NSWindow) -> UndoManager? {
isEditing ? nil : tableUndoManager
tableUndoManager
}
Edit ▸ Undo and Redo were dead while editing: the nib items resolved
through the responder chain to the window's undo manager, which knows
nothing about CodeMirror's history, so ⌘Z did nothing (or, worse,
reached the preview's table undo stack).

The editor now reports its history depth across the WebKit bridge, and
the menu items are retargeted to route to whichever undo client actually
owns the keystroke:

- `undoWindow` resolves the owning window as `keyWindow ?? mainWindow`,
  then prefers an `attachedSheet`, so Undo can never reach past a sheet
  into the document blocked behind it.
- `focusedEditorForUndo` claims Undo for CodeMirror while editing unless
  a field editor (`NSTextView`) is first responder, which keeps the
  toolbar search field's own undo stack intact.
- Otherwise the window's own responder chain answers, exactly as AppKit
  would have — that path also supplies the per-action menu titles.

Asking the responder chain directly means there is no focus state to
mirror between JavaScript and Swift and no notification to keep in sync.

Known tradeoff: the retargeted items no longer forward `undo:` into the
out-of-process save panel, so ⌘Z does not undo typing in its filename
field. That is system UI with no document risk, and the item correctly
reports itself disabled there.

Verified on an instrumented build: editor focused routes to CodeMirror
(one step per invocation); ⌘F with or without typing routes to the field
editor and leaves the document untouched; a Save As sheet routes to the
panel, not the document. 94 Swift helper tests and 67 editor-bundle
checks pass.
Copilot AI review requested due to automatic review settings July 28, 2026 19:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 11 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (3)

md-preview/DocumentWindowController.swift:1661

  • EditedMarkdownSaveResult was changed to case saved(rerendered: Bool), but this switch still matches case .saved: which will not compile. Update the pattern to ignore/bind the associated value.

This issue also appears in the following locations of the same file:

  • line 1715
  • line 1821
            guard let self else { return }
            switch result {
            case .saved:
                self.currentMarkdown = updated

md-preview/DocumentWindowController.swift:1719

  • This switch still uses case .saved: even though EditedMarkdownSaveResult.saved now has an associated value (rerendered). This is a compile-time error; match .saved(rerendered: _) (or bind the bool if you need it).
            guard let self else { return }
            switch result {
            case .saved:
                self.currentMarkdown = updated
                if let url = self.currentFileURL {

md-preview/DocumentWindowController.swift:1824

  • This switch result still includes a case .saved: branch earlier in the same switch, but EditedMarkdownSaveResult.saved now has an associated value (rerendered). That branch needs to match .saved(rerendered: _) (or bind the bool), otherwise the file won’t compile.
                    fileURL: fileURL
                )
                self.renderCurrentDocument(text: externalMarkdown, fileURL: fileURL)
            case let .failed(error):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants