A native Markdown editing component for macOS, built with SwiftUI and CodeMirror 6. MarkdownEditor delivers a premium editing experience with live syntax highlighting, rich widget rendering, and seamless Swift integration.
- Features
- Requirements
- Installation
- Quick Start
- Configuration
- App Sandbox Setup
- Architecture
- Contributing
- Bug Reports and Issues
- License
- Native macOS Aesthetics: Xcode-inspired light and dark themes that automatically follow system appearance
- Two-Way SwiftUI Binding: Seamless integration with SwiftUI state via
Binding<String> - Live Syntax Highlighting: Full Markdown syntax highlighting powered by CodeMirror 6
- Keyboard Shortcuts: Standard formatting shortcuts (Cmd+B for bold, Cmd+I for italic, etc.)
- Mermaid Diagrams: Live rendering of flowcharts, sequence diagrams, and other Mermaid diagram types with resize handles
- KaTeX Math: Full LaTeX math support for inline (
$...$) and block ($$...$$) formulas - Inline Images: Render and resize images directly within the editor
- Premium Code Blocks: Syntax-highlighted code blocks with language badges and distinct backgrounds
- Syntax Hiding: Obsidian-style interaction that hides Markdown markers on inactive lines for a cleaner writing experience
- Command Palette: Built-in command palette triggered by
/for quick insertions, formatting, and navigation - Smart Calculator: Inline math evaluation inside
$...$blocks (e.g., typing$2+2=displays4) - Typesafe Configuration: Full Swift API for configuring fonts, themes, line numbers, and feature toggles
- Lazy Loading: Mermaid and KaTeX libraries are loaded on-demand, reducing initial bundle size by 22%
- Widget Caching: LRU cache prevents redundant widget re-creation
- Smart Debouncing: Uses
requestIdleCallbackfor non-critical updates - Theme-Aware Widgets: Math and diagram widgets properly update when switching between light and dark themes
- macOS 14.0 or later
- Xcode 15 or later
- Swift 5.9 or later
Add MarkdownEditor to your project's Package.swift:
dependencies: [
.package(url: "https://github.com/Pallepadehat/MarkdownEditor.git", from: "1.0.0")
]Then add MarkdownEditor to your target's dependencies:
.target(
name: "YourApp",
dependencies: ["MarkdownEditor"]
)Alternatively, in Xcode:
- Go to File > Add Package Dependencies
- Enter the repository URL:
https://github.com/Pallepadehat/MarkdownEditor.git - Select version 1.0.0 or later
- Add to your target
import SwiftUI
import MarkdownEditor
struct ContentView: View {
@State private var content = "# Hello World"
var body: some View {
EditorWebView(text: $content)
.frame(minWidth: 400, minHeight: 300)
}
}The package provides a convenience type alias for discoverable naming:
import MarkdownEditor
struct ContentView: View {
@State private var markdown = ""
var body: some View {
MarkdownEditorView(text: $markdown)
}
}let config = EditorConfiguration(
fontSize: 16,
fontFamily: "Menlo",
lineHeight: 1.6,
showLineNumbers: true,
wrapLines: true,
renderMermaid: true,
renderMath: true,
renderImages: true,
hideSyntax: true
)
EditorWebView(
text: $content,
configuration: config,
onReady: {
print("Editor is ready for interaction")
}
)The EditorConfiguration struct provides type-safe configuration for all editor settings:
| Option | Type | Default | Description |
|---|---|---|---|
fontSize |
CGFloat |
15 |
Font size in points |
fontFamily |
String |
System monospace | CSS font-family string |
lineHeight |
CGFloat |
1.6 |
Line height multiplier |
showLineNumbers |
Bool |
true |
Display line numbers in the gutter |
wrapLines |
Bool |
true |
Wrap long lines instead of horizontal scroll |
renderMermaid |
Bool |
true |
Enable live Mermaid diagram rendering |
renderMath |
Bool |
true |
Enable KaTeX math formula rendering |
renderImages |
Bool |
true |
Enable inline image rendering |
hideSyntax |
Bool |
true |
Hide Markdown syntax on inactive lines |
// Uses all default values
EditorWebView(text: $content, configuration: .default)Configuration changes are automatically applied when the SwiftUI view updates:
struct EditorView: View {
@State private var content = ""
@State private var showLineNumbers = true
var body: some View {
EditorWebView(
text: $content,
configuration: EditorConfiguration(
showLineNumbers: showLineNumbers
)
)
Toggle("Show Line Numbers", isOn: $showLineNumbers)
}
}MarkdownEditor uses WKWebView internally, which requires specific entitlements when running in the macOS App Sandbox.
In Xcode (Signing and Capabilities > App Sandbox):
- Outgoing Connections (Client): Enable this setting. Required for WKWebView XPC communication.
- Incoming Connections (Server): Leave disabled. Not required and may cause App Store submission issues.
If using Hardened Runtime (Signing and Capabilities > Hardened Runtime):
- Allow Execution of JIT-compiled Code: Enable this setting if you encounter JavaScript execution issues.
Sandbox-related console logs: Messages like XPC_ERROR_CONNECTION_INVALID or Sandbox restriction are typically harmless WebKit noise. To minimize:
- Run in Release mode rather than Debug
- Verify Outgoing Connections is enabled
- Clean Build Folder (Cmd+Shift+K)
MarkdownEditor uses a hybrid architecture combining Swift and TypeScript:
MarkdownEditor/
├── Sources/MarkdownEditor/ # Swift package
│ ├── EditorWebView.swift # Main SwiftUI view
│ ├── EditorBridge.swift # Swift-JS communication
│ ├── MarkdownEditor.swift # Package exports
│ └── Resources/ # Bundled editor HTML
│
├── CoreEditor/ # TypeScript source
│ └── src/
│ ├── index.ts # Entry point
│ ├── core/ # Editor initialization and state
│ ├── bridge/ # Swift-JS message protocol
│ ├── extensions/ # CodeMirror extensions
│ │ ├── formatting.ts # Bold, italic, lists, etc.
│ │ ├── keymaps.ts # Keyboard shortcuts
│ │ └── calc.ts # Smart calculator
│ ├── widgets/ # Rich content widgets
│ │ ├── mermaid.ts # Diagram rendering
│ │ ├── math.ts # KaTeX rendering
│ │ ├── images.ts # Inline images
│ │ └── syntax-hiding.ts # Obsidian-style hiding
│ ├── ui/ # Command palette and themes
│ └── utils/ # Debounce and DOM helpers
│
└── Package.swift # Swift package manifest
- EditorWebView: The main SwiftUI view that hosts the WKWebView
- EditorBridge: Handles bidirectional communication between Swift and JavaScript
- EditorConfiguration: Type-safe configuration struct
- CoreEditor: CodeMirror 6 editor implementation in TypeScript
Contributions are welcome. This project uses a hybrid Swift/TypeScript architecture, so contributions may involve one or both languages.
- Xcode 15 or later
- Bun (for TypeScript development):
curl -fsSL https://bun.sh/install | bash
-
Clone the repository:
git clone https://github.com/Pallepadehat/MarkdownEditor.git cd MarkdownEditor -
Install CoreEditor dependencies:
cd CoreEditor bun install -
Start the development server with hot reload:
bun run dev
-
Build for production:
bun run build
-
Open
Package.swiftin Xcode to work on the Swift code
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Make your changes
- Run the build to ensure everything compiles:
bun run buildin CoreEditor - Test your changes in a sample macOS app
- Commit with clear, descriptive messages
- Push to your fork and open a pull request
- Swift: Follow Swift API Design Guidelines
- TypeScript: Follow the existing TypeScript style patterns used in
CoreEditor/src
Found a bug or have a feature request? Please open an issue on GitHub.
- Check existing issues to avoid duplicates
- Use the latest version of MarkdownEditor
- Include reproduction steps if reporting a bug
When reporting bugs, please include:
- Description: Clear description of the issue
- Steps to Reproduce: Minimal steps to reproduce the behavior
- Expected Behavior: What you expected to happen
- Actual Behavior: What actually happened
- Environment: macOS version, Xcode version, MarkdownEditor version
- Code Sample: If applicable, minimal code that demonstrates the issue
For feature requests, describe:
- The use case or problem you are trying to solve
- Your proposed solution (if any)
- Any alternatives you have considered
Open an issue at: https://github.com/Pallepadehat/MarkdownEditor/issues