Skip to content

Manifest Reference ​

Every extension has a typora-extension.json file at the top of its folder. Typora validates it when the extension is installed and every time it loads.

Validation is forgiving. If one contribution is malformed, Typora drops that contribution, keeps the rest, and lists the problem in the extension's row in Settings ▸ Extensions. Only a missing or invalid id, name or version disables the whole extension.

Example ​

json
{
  "id": "someone.plantuml",
  "name": "PlantUML",
  "version": "1.2.0",
  "engines": { "typora": ">=2.0.0" },
  "description": "PlantUML diagrams drawn by a Kroki server.",
  "author": "Someone",
  "repository": "https://github.com/someone/typora-plantuml",
  "permissions": {
    "network": ["https://kroki.io"]
  },
  "contributes": {
    "fenceRenderers": [{ "lang": ["plantuml", "puml"], "kroki": "plantuml" }],
    "configuration": {
      "properties": {
        "server": { "type": "string", "default": "https://kroki.io", "title": "Server" }
      }
    }
  }
}

Top-level fields ​

FieldTypeRequiredDescription
idstringyesUnique, stable id shaped like author.name: at most 64 characters, lowercase letters, digits and hyphens, at least two dot-separated parts (^[a-z0-9][a-z0-9-]*(\.[a-z0-9][a-z0-9-]*)+$). The install folder must have the same name.
namestringyesDisplay name. Also used as the group name for the extension's items in menus and palettes.
versionstringyesSemantic version: 1.2.3, optionally with a -pre or +build suffix.
descriptionstringOne sentence shown in Settings and in the store.
authorstringShown in Settings and in the store.
repositorystringURL of the source repository.
enginesobject{ "typora": ">=2.0.0" } — the Typora versions the extension targets.
osstring[]The platforms it works on: darwin (macOS), win32 (Windows), linux. Leave it out for all. Elsewhere Typora lists the extension as Only on macOS (etc.) and doesn't run it; the store marks it the same way.
l10nstringFolder of <locale>.json message catalogs, read by typora.l10n.t().
keywordsstring[]Search terms for the store.
tagsstring[]Store filters, from: Language Pack, Markdown Extension, Diagram, Math, Theme, Writing, Template, AI, Media, Image Uploader, Export, Publish, Data Sync, Productivity, File Viewer, View, Developer.
unlistedbooleanOptional; defaults to false. Set to true to hide the extension from store homepage cards and website/app search while keeping its direct page URL and installation available. See Unlisted extensions.
permissionsobjectWhat the extension may access. See below and Permissions and Security.
binariesobjectPrograms in the package, one file per platform and CPU. See below.
contributesobjectWhat the extension adds. See below.
mainstringPath to an ES module (.js or .mjs) run in a Worker, once per window. See Worker API.
backgroundstringPath to an ES module run in a Worker once for the whole app. See Worker API.
activationEventsstring[]When to start main. Usually derived — see Activation.

All paths in the manifest (main, entry, folders) are relative to the extension folder, use /, and may not contain .. or start with / or a URL scheme.

permissions ​

KeyValueGrants
networkarray of originsfetch from pages and scripts, and Kroki rendering, to these origins. Each origin is https://host or http://host, optionally with a port, and may start with *. for subdomains: "https://*.example.com". No paths. An entry "{config.<key>}" stands for the server the user enters in that setting.
document"read" or "write"Read the active document; "write" also allows editing it.
workspace"read"List and read files inside the folder open in the window.
clipboardtrueRead and write clipboard text.
aitrueSend text to the model the user set up in Settings ▸ Intelligence (typora.ai).
process"*" or array of command namesRun programs as the user (typora.process, view.process): "*" — anything, including the user's shell; a list — these commands, by name on the user's login-shell PATH or at a full path to a program with that name. Any form also allows the package's binaries.

Any permission makes Typora ask the user before the extension runs.

binaries ​

Programs you ship, one build per <os>-<arch>:

json
"permissions": { "process": [] },
"binaries": {
  "helper": {
    "darwin-arm64": "bin/darwin-arm64/helper",
    "darwin-x64": "bin/darwin-x64/helper",
    "linux-x64": "bin/linux-x64/helper",
    "linux-arm64": "bin/linux-arm64/helper",
    "win32-x64": "bin/win32-x64/helper.exe",
    "win32-arm64": "bin/win32-arm64/helper.exe"
  }
}

Targets: darwin-arm64, darwin-x64, darwin-universal (a fat binary), linux-arm64, linux-x64, win32-arm64, win32-x64. Start one with typora.process.spawn({ binary: "helper" }, args); Typora picks the build for the machine's CPU, falling back where the OS translates: an Apple-silicon Mac tries darwin-arm64, darwin-universal, then darwin-x64 (Rosetta); Windows on Arm tries win32-arm64, then win32-x64. Without a build for the machine, spawn rejects with "helper has no build for linux-arm64."

You don't need to keep the executable bit (zips drop it; Typora sets it before running), but macOS binaries for Apple silicon must be signed — ad hoc is enough (codesign -s - helper; Apple's linker signs ad hoc by default).

binaries needs permissions.process. See Permissions and Security.

contributes ​

KeyTypeTierPage
themesfolder pathsL0Writing Themes
templatesfolder pathL0Templates, snippets, formats, keys
snippetsfolder pathL0Templates, snippets, formats, keys
customFormatsarrayL0Templates, snippets, formats, keys
keyBindingsarrayL0Templates, snippets, formats, keys
texMacrosobjectL0TeX Macros
mathjaxobjectMathJax componentsMathJax Fonts and TeX Packages
localizationsarrayL0Language Packs
dictionariesarrayL0Language Packs
inputRulesarrayL0Input Rules
inlineRulesarrayL0Inline Rules
fenceRenderersarrayL0 (Kroki) / L1 / L2Fence Renderers
detailViewsarrayL0 / L1 / L2Detail Views
fileViewersarrayL1 / L2File Viewers
commandsarrayL2Commands and Menus
imageUploadersarrayL2Image Uploaders
menusobjectL2Commands and Menus
configurationobjectL0Configuration

Local ids inside contributes (commands[].id, detailViews[].id, fileViewers[].id, inlineRules[].id, fenceRenderers[].id, imageUploaders[].id) are scoped to your extension. They may contain letters, digits, ., _ and -, up to 64 characters.

Not yet available

grammars is reserved for upcoming syntax-highlighting contributions. Typora currently ignores it.

Activation ​

main is not started until it is needed. Typora derives the activation events from contributes when activationEvents is omitted:

EventFired when
onCommand:<id>One of your commands is run.
onFileViewer:<id>A file opens in one of your fileViewers that has no entry.
onDetailView:<id>One of your detailViews with "provider": "main" is shown.
onFence:<id>A fence for one of your fenceRenderers with "resolver": true is rendered.
onImageUploader:<id>One of your imageUploaders uploads images.
onStartupFinishedThe window has finished loading.

If main has nothing to wait for — for example it only registers formatters or watches the document — it starts with onStartupFinished. You can also list "activationEvents": ["onStartupFinished"] explicitly, as the Writing Stats sample does, to run at startup regardless. Prefer lazy events: every window pays for an eagerly started Worker.

background is not driven by activation events: while the extension is enabled, its background Worker runs in Typora's background host for as long as the app runs.

Checks across fields ​

Typora also reports these combinations:

  • A fence renderer with "kroki" needs a network permission.
  • A "{config.<key>}" network entry needs a string setting <key> whose default is empty or one of the listed origins.
  • A fence renderer with "resolver": true, or a detail view with "provider": "main", needs main.
  • A file viewer needs an entry or a main that resolves it.

Theme adapted from Nimbus (MIT).