Manifest reference
All 19 fields Tintero reads from plugin.json, with the exact rules the installer applies.
On this page
plugin.json sits at the root of your plugin. The authoritative version of this page
is the PluginManifest interface in the type definitions. Type your manifest against
it and a mistake becomes a red squiggle instead of a failed install.
A complete manifest
{
"id": "com.example.word-count",
"name": "Word Count",
"version": "1.0.0",
"description": "A live word count for the document you are writing",
"author": {
"name": "Your Name",
"url": "https://example.com"
},
"license": "MIT",
"type": "sidebar-panel",
"surfaces": [
"sidebar"
],
"main": "plugin.js",
"icon": "icon.svg",
"scopes": [
"ui.sidebar",
"editor.read"
],
"ui": {
"sidebar": {
"label": "Words",
"tooltip": "Live word count",
"width": 280
}
}
} Required fields
Missing any of these and the install fails with a message naming the field.
| Field | Type | Notes |
|---|---|---|
id | string | Reverse-DNS by convention. It is also your plugin's directory name and its storage bucket, which is why the installer is strict about it. |
name | string | Shown to users, in the sidebar and the plugin manager. |
version | string | A free-form string; semver is the convention, and nothing parses it. |
description | string | One line. The install dialog shows it while the user decides. |
author | PluginManifestAuthor | An object. Only "name" is required; "url" is shown as a link. |
type | PluginType | The legacy single-slot field, still required. See surfaces below. |
main | string | Your entry-point JavaScript, relative to the plugin root. |
scopes | PluginScopeString[] | The permissions you request. Everything the API does is gated on these. |
Optional fields
| Field | Type | Notes |
|---|---|---|
license | string | Not required, but its absence is a warning at install. |
minTinteroVersion | string | Advisory. Declare it if you rely on a recently added method. |
surfaces | PluginSurface[] | Contributions model (optional). `surfaces` = where the plugin presents (sidebar/app/background/dialog); `capabilities` = what it contributes (importers/exporters). When omitted, both are derived from `type` so pre-existing single-`type` plugins keep working unchanged. |
capabilities | PluginCapability[] | What your plugin contributes: importers and exporters. |
icon | string | SVG only. See the trap below. |
shortcut | string | Suggested launch shortcut, e.g. "Ctrl+Shift+W" (the user can override it). |
ui | PluginManifestUI | Where your surfaces get their labels and their HTML: sidebar, dialog, settings. |
settings | { schema: Record<string, PluginSettingDefinition>; } | A schema Tintero renders a settings form for, readable via tintero.settings. |
import | PluginManifestImport | Format metadata for importer plugins: extensions and a display name. |
export | PluginManifestExport | Format metadata for exporter plugins: extension, mime type, display name. |
network | PluginManifestNetwork | The host allow-list for tintero.net.fetch. Required if you request net.fetch. |
The ui block
Where each surface gets its label and its markup.
ui.sidebar
| Field | Type | Required |
|---|---|---|
panel | string | no |
label | string | yes |
tooltip | string | no |
width | number | no |
ui
| Field | Type | Required |
|---|---|---|
sidebar | PluginManifestSidebarUI | no |
settings | string | no |
dialog | string | no |
Settings schema
Describe your settings and Tintero draws the form, keeps the values, and hands them back
through tintero.settings. Each field is one of these:
string , number , boolean , select .
| Field | Type | Required |
|---|---|---|
type | PluginSettingType | yes |
label | string | yes |
description | string | no |
required | boolean | no |
secret | boolean | no |
default | any | no |
min | number | no |
max | number | no |
options | string[] | no |
Paths
Every path you write gets stuck onto the end of your own plugin folder, so all of them have
to stay inside it. These are checked when you install:
main , icon , ui.sidebar.panel , ui.settings , ui.dialog .
Rejected outright:
- Empty or whitespace-only paths
- Paths containing a null byte
- Absolute POSIX paths (leading
/) - Paths starting with
~ - Windows drive letters (
C:) - Any
..segment - Any
.segment - Empty segments (
a//b)
Backslashes are turned into / before any of that happens, so Windows paths
are covered by the same rules.
What the installer rejects
These are the installer's own words, straight from validateManifest. If your
plugin will not install, the reason is one of them.
Missing or invalid "id""id" must contain only lowercase letters, numbers, dots and hyphens, and start and end with a letter or numberMissing or invalid "name"Missing or invalid "version"Missing or invalid "description"Missing or invalid "author" (requires at least "name")Missing or invalid "main" entry pointInvalid "type": "…". Must be one of: …Missing or invalid "scopes" arrayInvalid scopes: …Plugins with a "sidebar" surface must include the "ui.sidebar" scopeImporter plugins must include "import" configuration
And the warnings, which do not block an install but the user still sees:
No license specifiedNo icon specified; a default icon will be usedExporter plugins should include "export" configuration
Your plugin id has to match /^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$/:
lowercase letters, numbers, dots and hyphens, starting and ending with a letter or a number.