Skip to content

Customization

Ying Zhong edited this page Jan 5, 2025 · 55 revisions

This page is for those looking to customize the appearance and behavior of MarkEdit (as a user). To make changes to MarkEdit (as a developer), refer to Development Guide.

Entries

MarkEdit relies on these files to customize some behaviors, including the editor and some 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/settings.json

You can add your own style sheets and JavaScript code to override appearance and behavior.

These files live in the Documents folder of MarkEdit, you can also open the folder from the main menu:

Open Documents Folder

Note they only affect the main app, the appearance and behavior of the Quick Look extension is not customizable.

After editing these files, restart the app to make them take effect. To debug editor customizations, right click the editor, select Inspect Element to show the web inspector (or press Option-Command-I).

Editor Appearance and Behavior

To change the appearance, just open editor.css and put your custom style sheets there.

For example, this style sheet enables "readable width" for the editor, it looks nicer when you prefer to edit files in full screen.

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

If you like more margins when line numbers are off, try this one:

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

Similarly, editor.js is for customizing the behavior. There's window.editor to interact with, to check all available interfaces, refer to index.ts.

To leverage both files, here is also an advanced example showing us how to add a width guide to the editor: #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

Put your .css and .js files in there and you're done. Please note that the order of code injection is not guaranteed.

MarkEdit-api

Inspired by #627, MarkEdit now supports programmable interfaces as MarkEdit-api.

There is a global object called MarkEdit that offers many interfaces, here are some of them:

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 take control of the editor by leveraging CodeMirror extensions.

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

Advanced Settings

As mentioned, we would like to keep settings clean, but we have noticed an increasing demand for more settings.

The ~/Library/Containers/app.cyan.markedit/Data/Documents/settings.json file was introduced for that purpose, these settings are for Pro users and are not visible in the Settings panel. For example:

{
  "editor.autoCharacterPairs" : true,
  "editor.closeAlwaysConfirmsChanges" : false,
  "editor.indentBehavior" : "never",
  "editor.writingToolsBehavior" : "limited",
  "editor.headerFontSizeDiffs" : [5, 3, 1],
  "general.checksForUpdates" : true,
  "general.defaultOpenDirectory" : "~/Downloads",
  "general.defaultSaveDirectory" : "~/Desktop",
  "general.disableCorsRestrictions" : false,
  "general.mainWindowHotKey" : {
    "key" : "M",
    "modifiers" : [
      "Shift",
      "Command",
      "Option"
    ]
  }
}

The file must be a valid JSON format and follow the spec strictly.

Key values are all optional, but if they exist, they must follow the specification. Here are two examples of invalid key values in settings.json:

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

We don't like error-tolerant design, invalid key values will cause the entire settings.json file to be invalid.

editor.autoCharacterPairs

Whether to automatically wrap selections with characters like [], **, etc.

If not provided, the default behavior is to wrap.

editor.closeAlwaysConfirmsChanges

Basically "Ask to keep changes when closing documents" in system settings, except that this is for MarkEdit only.

If not provided, the default behavior respects the system decision.

editor.indentBehavior

Whether to indent paragraphs or lines, similar to list items.

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

If not provided, the default behavior is not to do that.

editor.writingToolsBehavior

For macOS Sequoia and higher, controls the Writing Tools behavior.

Possible values are (letter case matters): complete for an inline experience, limited for a panel experience, and none to opt it out.

If not provided, the default behavior respects the system decision.

editor.headerFontSizeDiffs

An array of numbers to dynamically increase the font size of headers (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.

general.checksForUpdates

Whether to check for updates, set to false to disable automatic updates.

If not provided, the app checks for updates automatically.

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 open files from ~/Desktop.

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

general.disableCorsRestrictions

Whether to disable CORS restrictions and allow Fetch API to work with any URL.

If not provided, CORS restrictions will be enforced.

general.mainWindowHotKey

Define a hotkey to toggle the visibility of the main window. It must be in the following format (letter case matters):

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

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

For non-printable characters, user their names. For example, Tab, Return, F1 etc. (Check here for a list of all 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 front-end development, we recommend documentations from Mozilla: CSS reference and JavaScript reference.

To have the best experience in both light mode and dark mode, we recommend taking a look Dark Mode in CSS and Dark Mode Support in WebKit.

Since we use a CodeMirror editor, you can check out the styling guide to learn selectors used in CodeMirror.

Also, we enabled classHighlighter to have stable classes for tokens, prefixed with tok-. Please check out the documentation to learn 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
URL cm-md-url
Link cm-md-link, cm-md-linkMark
Code cm-md-inlineCode, cm-md-codeBlock
Table cm-md-table
Horizontal Rule cm-md-horizontalRule

Note, you often need to use the !important syntax to make sure your style sheets have higher priority than built-in ones (not always the case).

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

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

Theming Mechanisms

Please note that this is not intended to replace the theming system or extension system, and there are situations that this approach cannot help with.

To keep the app simple, we are not going to add more themes or theming mechanisms, check out #486 to learn more.

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

Leveraging Symbolic Links

The customization files mentioned above can be symbolic linked. 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

In that case, these files can be synced across devices via iCloud. After creating the symbolic link, you will need to grant access to the folder, because MarkEdit is sandboxed and only has access to user selected folders:

Grant Folder Access

Select the folder that contains the original file and restart the app to take effect. In our example, it should be the folder in the iCloud container.

To revoke granted folder access, open the Terminal app and run:

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

For more details, please check out #537.

Note, you will have to use ln -s, creating an alias in Finder would not work the same way.