Skip to content

Customization

Ying Zhong edited this page Aug 29, 2026 · 106 revisions

Note

This page is for users who want to customize MarkEdit's appearance and behavior. Developers who want to modify MarkEdit itself should refer to the Development Guide.

For contributors: please read Philosophy and Why MarkEdit before submitting PRs that introduce behavioral changes.

Entries

MarkEdit uses the following files for editor customizations and advanced settings:

~/Library/Containers/app.cyan.markedit/Data/Documents/editor.css
~/Library/Containers/app.cyan.markedit/Data/Documents/editor.js
~/Library/Containers/app.cyan.markedit/Data/Documents/pandoc.yaml
~/Library/Containers/app.cyan.markedit/Data/Documents/statistics-rules.json
~/Library/Containers/app.cyan.markedit/Data/Documents/settings.json

You can add your own style sheets and JavaScript code to override the app's appearance and behavior.

These files are located in MarkEdit's Documents folder, which you can also open from the main menu:

Open Documents Folder

They affect only the main app. The appearance and behavior of the Quick Look extension cannot be customized.

Tip

After editing these files, restart the app for the changes to take effect. To debug editor customizations, right-click the editor and select Inspect Element to open the web inspector, or press Option-Command-I.

For pandoc.yaml and statistics-rules.json, learn more here.

Editor Appearance and Behavior

To change the editor's appearance, open editor.css and add your custom style sheets.

For example, this style sheet gives the editor a more readable width when editing files in full screen.

.cm-content {
  margin: 0 auto !important;
  max-width: 800px !important;
}

If you prefer wider margins when line numbers are hidden, try this:

.cm-content {
  margin-left: 20px !important;
  margin-right: 20px !important;
}

Use editor.js to customize behavior. You can interact with window.editor; for all available interfaces, refer to index.ts.

For an advanced example that uses both files to add a width guide to the editor, see #416.

If you have lots of customizations and would like to manage them separately, there are now two folders:

~/Library/Containers/app.cyan.markedit/Data/Documents/styles
~/Library/Containers/app.cyan.markedit/Data/Documents/scripts

Place your .css and .js files in these folders. The order in which the code is injected is not guaranteed.

MarkEdit-api

Inspired by #627, MarkEdit now provides programmable interfaces through MarkEdit-api.

The global MarkEdit object provides many interfaces, including:

interface MarkEdit {
  // CodeMirror EditorView instance of the current editor.
  editorView: EditorView;
  // Convenient text editing interfaces.
  editorAPI: TextEditable;
  // Add an extension to MarkEdit.
  addExtension: (extension: Extension) => void;
  // CodeMirror modules used by MarkEdit.
  codemirror: { view, state, language, commands, search };
  // Lezer modules used by MarkEdit.
  lezer: { common, highlight, lr },
  // ...
}

You can control the editor through CodeMirror extensions.

For a complete example, refer to Example (Markdown Table Editor) and MarkEdit Extensions.

Custom Themes

To keep the app simple, we do not plan to add more built-in themes.

If you want to build your own themes, the best way to do it is to rely on the markedit-theming package.

Refer to MarkEdit Themes for examples.

Advanced Settings

As mentioned, we want to keep the settings clean, but we have seen growing demand for more options.

Tip

You can press Shift-Command-Comma to quickly edit this file with your default text editor.

You can add the schema to a JSON editor such as VS Code for type information and validation.

The ~/Library/Containers/app.cyan.markedit/Data/Documents/settings.json file provides these additional options. They are intended for advanced users and do not appear in the Settings panel. For example:

{
  "editor.autoCharacterPairs" : true,
  "editor.autoSaveWhenIdle" : false,
  "editor.closeAlwaysConfirmsChanges" : false,
  "editor.restoreLastSelection" : true,
  "editor.indentBehavior" : "never",
  "editor.writingToolsBehavior" : "limited",
  "editor.undoGroupingInterval" : 300,
  "editor.headerFontSizeDiffs" : [5, 3, 1],
  "editor.visibleWhitespaceCharacter" : "·‌",
  "editor.visibleLineBreakCharacter" : "¬",
  "editor.searchNormalizers" : {
    "[‘’]" : "'",
    "[“”]" : "\""
  },
  "editor.nativeSearchQuerySync" : false,
  "editor.toolbarTranslucency" : "default",
  "editor.customToolbarItems" : [],
  "general.updateBehavior" : "quiet",
  "general.defaultOpenDirectory" : "~/Downloads",
  "general.defaultSaveDirectory" : "~/Desktop",
  "general.disableOpenPanelOptions" : true,
  "general.disableCorsRestrictions" : true,
  "general.disabledWebKitFeatures" : [],
  "general.preferredTerminalApp" : null,
  "general.mainWindowHotKey" : {
    "key" : "M",
    "modifiers" : [
      "Shift",
      "Command",
      "Option"
    ]
  }
}

The file must contain valid JSON and strictly follow the specification.

All keys are optional, but any values you provide must follow the specification. Here are two examples of invalid values in settings.json:

"editor.autoCharacterPairs" : "yes" // The value type is not boolean
"editor.indentBehavior" : "all"     // The value is not recognized

MarkEdit uses strict validation, so any invalid value makes the entire settings.json file invalid.

editor.autoCharacterPairs

Controls whether selections are automatically wrapped with characters such as [] and **.

If not provided, the default behavior is to wrap.

editor.autoSaveWhenIdle

Controls whether changes are saved after you stop typing for a while.

When enabled, the editor does not show an "Edited" label after making changes.

This feature requires editor.closeAlwaysConfirmsChanges to be disabled.

If not provided, changes are not saved automatically.

editor.closeAlwaysConfirmsChanges

This is equivalent to "Ask to keep changes when closing documents" in System Settings, but applies only to MarkEdit.

If not provided, the system setting is used.

editor.restoreLastSelection

Controls whether the selection range from the previous editing session is restored.

If not provided, the default behavior is to restore.

editor.indentBehavior

Controls whether paragraphs or lines are indented like list items.

Possible values are (letter case matters): paragraph to indent all paragraphs, line to indent all lines, and never to disable indentation.

If not provided, indentation is disabled.

editor.writingToolsBehavior

On macOS Sequoia and later, controls the behavior of Writing Tools.

Possible values are (letter case matters): complete for an inline experience, limited for a panel experience, and none to disable Writing Tools.

If not provided, the system setting is used.

editor.undoGroupingInterval

The maximum time, in milliseconds, between edits that are grouped into a single undo step.

If not provided, the default value is 300ms.

editor.headerFontSizeDiffs

An array of numbers that dynamically increase the font size of headings, from heading 1 to heading 6.

If not provided, the default values are [5, 3, 1]. For example, if the body text is 15px, heading 1 will be 20px.

editor.visibleWhitespaceCharacter

The character used to display whitespace. This setting takes effect only when "Render Invisibles" is enabled.

If not provided, · is used. To hide them, use an empty string.

editor.visibleLineBreakCharacter

The character used to display line breaks. This setting takes effect only when "Render Invisibles" is enabled.

If not provided, ¬ is used. To hide them, use an empty string.

editor.searchNormalizers

A key-value map of search-query normalizers. Use a regular expression as the key and the replacement string as the value.

For example, the following configuration ignores the difference between curly and straight quotes:

{
  "[‘’]" : "'",
  "[“”]" : "\""
}

If not provided, the default value is an empty map.

editor.nativeSearchQuerySync

Controls whether search queries are synchronized with native apps. For example, it can automatically populate the search bar with a query entered in TextEdit.

If not provided, search queries from other apps are ignored.

editor.toolbarTranslucency

Controls the toolbar translucency. Available presets are readable, default, and vibrant.

For finer control, use a custom three-number string in the form backdropBlur, tintedOpacity, plainOpacity.

For example, default internally resolves to 8, 0.7, 0.4.

This requires macOS Tahoe or later.

If not provided, the default value is default.

editor.customToolbarItems

Defines custom toolbar items like this:

[
  {
    "title" : "View Mode",
    "icon" : "eye",
    "menuName" : "View Mode"
  }
]

The menuName value must refer to a valid submenu in the main menu. If the menu item represents an action, specify actionName instead.

The icon value must be a valid SF Symbol name. You can browse available symbols using the official SF Symbols app or third-party websites.

After adding these items, relaunch the app and customize the toolbar to use them.

general.updateBehavior

Controls how new app versions are presented. Valid values are never, quiet, notify, and automatic (installs on next launch).

If not provided, the default value is quiet.

general.checksForUpdates

Controls whether the app checks for updates. Set it to false to disable automatic update checks.

Warning

Deprecated. Use general.updateBehavior instead.

general.defaultOpenDirectory

The default directory for opening files. For example, always open files from ~/Downloads.

If not provided, the default behavior is to use the last accessed directory.

general.defaultSaveDirectory

The default directory for saving files. For example, always save files to ~/Desktop.

If not provided, the default behavior is to use the last accessed directory.

general.disableOpenPanelOptions

Controls whether options in the file-open panel are disabled.

If not provided, the default value is true on macOS Tahoe and later because enabling these options can cause performance issues.

general.disableCorsRestrictions

Controls whether CORS restrictions are disabled, allowing the Fetch API to work with any URL.

If not provided, CORS restrictions are not enforced.

general.disabledWebKitFeatures

An array of feature names to disable in WebKit. See UnifiedWebPreferences.yaml for the full list of names.

For example: ["NotificationsEnabled", "PushAPIEnabled"].

If not provided, the default value is an empty array.

general.preferredTerminalApp

The bundle identifier of the preferred terminal app used to run Pandoc commands. For example, "com.apple.Terminal".

If not provided, MarkEdit detects installed popular terminal apps.

general.mainWindowHotKey

Defines a hotkey for toggling the visibility of the main window. Use the following format (letter case matters):

{
  "key" : "M",
  "modifiers" : [
    "Shift",
    "Command",
    "Option"
  ]
}

For printable characters, use their uppercase form. For example, M, F, =, or ..

For non-printable characters, use their names. For example, Tab, Return, or F1. See the list of valid keys.

Valid modifiers are Shift, Control, Command, and Option; the order does not matter.

If not provided, the app will not register a global hotkey.

References

If you are new to frontend development, we recommend Mozilla's CSS reference and JavaScript reference.

For the best experience in both light and dark modes, we recommend reading Dark Mode in CSS and Dark Mode Support in WebKit.

Because MarkEdit uses CodeMirror, you can refer to its styling guide to learn about the available selectors.

We also enable classHighlighter to provide stable token classes prefixed with tok-. Refer to its documentation for details.

Markdown Syntax Classes

Markdown elements in MarkEdit generally have a class name with a cm-md- prefix. Here is a quick reference:

Element Classes
Heading cm-md-header, cm-md-heading1, cm-md-heading2, ...
Bold cm-md-bold
Italic cm-md-italic
Strikethrough cm-md-strikethrough
Blockquote cm-md-quote, cm-md-quoteMark
List Mark cm-md-listMark
Task cm-md-taskMarker, cm-md-taskMarker-checked, -unchecked
URL cm-md-url
Link cm-md-link, cm-md-linkMark
Code cm-md-inlineCode, cm-md-codeBlockWrapper, cm-md-codeBlock
Table cm-md-tableWrapper, cm-md-table
Front Matter cm-md-frontMatterWrapper, cm-md-frontMatter
Horizontal Rule cm-md-horizontalRule

You may need to use the !important syntax to give your style sheets priority over the built-in styles, although it is not always necessary.

For example, to customize the font used for code blocks:

.cm-md-codeBlock, .cm-md-codeBlock * {
  font-family: Menlo !important;
}

Leveraging Symbolic Links

The customization files mentioned above can be linked symbolically. For example, you can create a symbolic link like this:

ln -s ~/Library/Mobile\ Documents/com~apple~CloudDocs/MarkEdit/editor.css ~/Library/Containers/app.cyan.markedit/Data/Documents/editor.css

This allows the files to be synchronized across devices through iCloud. After creating the symbolic link, grant access to the folder containing the original file, then restart the app. In this example, select the folder in the iCloud container.

For more details, please check out #537.

Note

You must use ln -s; an alias created in Finder does not work the same way.

Grant Folder Access

MarkEdit is sandboxed and can access only user-selected folders. Although an exception is applied, it may not work reliably. This step is necessary when MarkEdit cannot access files outside its sandbox.

Grant Folder Access

To revoke access to granted folders, open Terminal and run:

defaults delete app.cyan.markedit general.granted-folder-bookmark

Tip

The entry can also be found from the "File" menu.

Clone this wiki locally