Appearance
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.mdtypora-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
- Open Settings ▸ Extensions.
- Click Install from File… and pick the
yourname.hellofolder. - 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.errorin a Worker andtypora.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,nameorversiondisables the whole extension.
Next steps
- Browse the contribution points for what else a manifest can add.
- Read the Worker API and View SDK references.
- Copy from the samples.