Skip to content

Quick Start ​

This page builds a small extension in three steps: a snippet (no code), a command with a key binding (a main script), and a settings field. It takes about ten minutes.

1. Create the folder and manifest ​

Create a folder named after your extension id. Ids look like author.name: lowercase letters, digits and hyphens, separated by dots.

yourname.hello/
  typora-extension.json
  snippets/
    Greeting.md

typora-extension.json:

json
{
  "id": "yourname.hello",
  "name": "Hello",
  "version": "0.1.0",
  "description": "My first Typora extension.",
  "author": "Your Name",
  "contributes": {
    "snippets": "snippets"
  }
}

snippets/Greeting.md is an ordinary snippet file:

markdown
---
typora-snippet:
  description: A friendly greeting
  prefix: hello
---
Hello, {{1:world}}! Today is {{date:dddd}}.

2. Install it ​

  1. Open Settings ▸ Extensions.
  2. Click Install from File… and pick the yourname.hello folder.
  3. Typora copies the folder into ~/.config/Typora/Extensions/yourname.hello/ and enables it.

Type ;hello in a document — the snippet appears in the list, in a group named Hello.

Developing in place

Install from File… copies your folder. While developing, it is easier to edit the installed copy directly: click Open Folder in Settings ▸ Extensions, work in Extensions/yourname.hello/, and click Reload after each change. Reload restarts every extension from disk, including running scripts.

Sharing a package

To hand the extension to someone without the store, zip the folder's contents and name the file .typora-extension:

sh
cd yourname.hello && zip -r ../yourname.hello.typora-extension .

They install it with Install from File…, or by opening the file with Typora. The store builds the same kind of file for you.

3. Add a command ​

Commands are declared in the manifest — so they show up in the command palette before any code runs — and handled by a main script. Update the manifest:

json
{
  "id": "yourname.hello",
  "name": "Hello",
  "version": "0.2.0",
  "description": "My first Typora extension.",
  "author": "Your Name",
  "contributes": {
    "snippets": "snippets",
    "commands": [{ "id": "greet", "title": "Say Hello", "category": "Hello" }],
    "keyBindings": [{ "command": "greet", "key": "Mod+Alt+H" }],
    "menus": {
      "inlinePalette": [{ "command": "greet" }],
      "editor/context": [{ "command": "greet" }]
    }
  },
  "main": "main.js"
}

Add main.js next to the manifest:

js
// Runs in a sandboxed Worker. `typora` is the Worker API.
export function activate(ctx) {
  ctx.subscriptions.push(
    typora.commands.register("greet", async () => {
      const name = await typora.ui.input({ prompt: "Who should I greet?", value: "world" });
      if (name) await typora.ui.toast(`Hello, ${name}!`);
    }),
  );
}

Click Reload in Settings ▸ Extensions. Now Hello: Say Hello is in the command palette, in the / menu and in the editor's context menu, and Mod+Alt+H runs it (Mod is ⌘ on macOS and Ctrl elsewhere).

The main script starts the first time one of its commands runs. Everything you push to ctx.subscriptions is disposed when the extension is disabled, uninstalled or reloaded.

4. Add a setting and change the document ​

Declare a setting, and ask for permission to edit the document:

json
{
  "permissions": { "document": "write" },
  "contributes": {
    "commands": [{ "id": "greet", "title": "Say Hello", "category": "Hello" }],
    "configuration": {
      "title": "Hello",
      "properties": {
        "greeting": { "type": "string", "default": "Hello", "title": "Greeting" }
      }
    }
  },
  "main": "main.js"
}
js
export function activate(ctx) {
  ctx.subscriptions.push(
    typora.commands.register("greet", async () => {
      const greeting = typora.settings.get("greeting");
      await typora.editor.insertMarkdown(`**${greeting}, world!**`);
    }),
  );
}

After Reload, the extension's row in Settings ▸ Extensions shows a Greeting field. Because the manifest now asks for document: "write", Typora asks the user to allow it before it runs — see Permissions.

Debugging ​

  • Expand your extension's row in Settings ▸ Extensions to see manifest errors, what it adds and its permissions. View Log shows what it logged in every window; a red dot on the button means it logged an error.
  • console.log / console.error in a Worker and typora.view.log() in a page go to that log. Uncaught errors and rejected promises are logged too.
  • A manifest problem disables only the broken contribution and is listed in the row; an invalid id, name or version disables the whole extension.

Next steps ​

Theme adapted from Nimbus (MIT).