Skip to content

Detail Views ​

The detail panel (the right-hand panel with Outline, History and References) can host extension tabs. A view is one of three kinds:

KindManifestTier
Rule-basedqueryL0
PageentryL1
Fed by main"provider": "main"L2

Common fields:

FieldRequiredDescription
idyesLocal id of the view.
titleyesTab title.
iconIcon name for the tab: list-checks, hash, network, tags, chart, calendar, terminal, image, image-play or puzzle (the default).
sfSymbolSF Symbol name for the view's item in the macOS menus (View and the tab strip's right-click menu), e.g. checklist. Defaults to info.circle.text.page.

Rule-based views ​

A rule-based view lists blocks or inline elements that match a query. Typora re-checks only the blocks an edit touched, so these views stay fast on long documents. Clicking an item selects it in the editor.

json
"detailViews": [
  {
    "id": "open-tasks",
    "title": "Open Tasks",
    "icon": "list-checks",
    "sfSymbol": "checklist",
    "query": { "block": ["list_item"], "where": { "checked": false } },
    "item": { "label": "{text}", "groupBy": "heading" }
  },
  {
    "id": "todo",
    "title": "TODO",
    "icon": "hash",
    "query": { "block": ["paragraph", "heading"], "text": "\\b(TODO|FIXME|XXX)\\b[::]?\\s*(.*)" },
    "item": { "label": "{$1}: {$2}", "groupBy": "heading" }
  },
  {
    "id": "links",
    "title": "Links",
    "icon": "network",
    "query": { "inline": ["link", "autolink", "url", "wikilink"] },
    "item": { "label": "{text}" }
  }
]

query ​

FieldDescription
blockBlock types to match (list below).
inlineInline types to match inside every block (list below). Each match is its own item.
whereBlock attributes that must be equal, e.g. { "level": 2 } for headings, { "checked": false } for open tasks, { "lang": "python" } for code fences.
textRegular expression the block's (or inline's) source must match. Capture groups are available to the label.
flagsExtra regex flags: i, m, s (u is always on).

Block types: paragraph, heading, blockquote, custom_container, fences, hr, page_break, def_link, def_footnote, table, meta_block, math_block, html_block, list, toc, list_item, table_row, table_cell.

Inline types (common ones): strong, em, code, del, underline, highlight, link, reflink, autolink, url, wikilink, wikiembed, image, refimg, footnote, citation, emoji, inline_math, superscript, subscript, html_inline.

item ​

FieldDescription
labelTemplate for the item text. Placeholders: {text} (first line of the block, heading marks removed, or the inline's text), {match} (the whole regex match), {$1}…{$9} (capture groups), {level}, {lang}, {type}. Default {text}.
groupBy"heading" groups items under the heading they appear in.

Page views ​

A page view is an HTML page shown in the tab. With the document permission it receives the document's blocks, and again after every edit (debounced), and can select ranges in the editor.

json
{
  "permissions": { "document": "read" },
  "contributes": {
    "detailViews": [{ "id": "mindmap", "title": "Mind Map", "icon": "network", "entry": "views/mindmap.html" }]
  }
}
js
const view = window.typora.view;

view.onDocument(({ blocks }) => {
  // blocks: [{ id, type, text, attrs, depth, parent }] in document order
  const headings = blocks.filter((b) => b.type === "heading");
  draw(headings, (heading) => view.reveal({ blockId: heading.id }));
});

With document: "write" the page can also insert at the caret with view.insertMarkdown(md) — the Openverse Images sample inserts the image you click. Without the document permission the page gets no document and view.reveal is refused. A page can still use settings, storage and (with network) view.fetch — for example the Writing Stats sample shows numbers its background script stores. See the View SDK.

Views fed by main ​

For lists that need logic, declare "provider": "main" and register a checker in main. A checker is a pure function of one block; Typora sends only the blocks an edit touched to the Worker and keeps the results for the rest.

json
{
  "contributes": {
    "detailViews": [{ "id": "long-paragraphs", "title": "Long Paragraphs", "provider": "main" }]
  },
  "main": "main.js"
}
js
export function activate(ctx) {
  ctx.subscriptions.push(
    typora.iteration.register("long-paragraphs", (block) => {
      if (block.type !== "paragraph") return [];
      const words = block.text.split(/\s+/).filter(Boolean).length;
      return words > 150 ? [{ label: `${words} words: ${block.text.slice(0, 40)}…`, group: "Too long" }] : [];
    }),
  );
}

The checker receives { id, type, text, attrs, depth, parent } and returns a list of items:

FieldDescription
labelItem text.
from, toOptional offsets into the block's text to select on click. Omit to select the whole block.
groupOptional group heading.

main is started the first time the view is shown (onDetailView:<id>).

Theme adapted from Nimbus (MIT).