> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qomicex.top/llms.txt
> Use this file to discover all available pages before exploring further.

# MarkdownLib Library plugin

## MarkdownLib Library plugin

> MarkdownLib is a library that wraps Markdown parsing as a Library plugin (Library plugin), which any plugin can call through `callPlugin` to render Markdown text as HTML.

* **Plugin ID**: `top.qomicex.markdown`
* **Version**: `1.2.1`
* **Dependency**: none
* **Bundling**: `marked v15.0.12` is embedded, with no external CDN dependency (not affected by the Tauri CSP)

## Why you need it

QML plugins run inside a Tauri WebView, and after packaging the CSP blocks all external requests and external `<script src>` loads. If a plugin references marked straight from a CDN in its HTML:

```html theme={null}
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
```

the CSP blocks it in packaged builds, `marked` is undefined, and Markdown cannot be rendered.

Once marked lives in its own Library plugin:

* the marked code is embedded in the MarkdownLib package, so **no external CDN is needed** and CSP is bypassed naturally;
* multiple plugins can **share a single rendering implementation** instead of embedding duplicate code;
* plugin dependencies manage it, so **versions are explicit and upgradeable**.

## Installation

After you install the MarkdownLib Library plugin, other plugins declare the dependency in `manifest.json`:

```json theme={null}
{
  "dependencies": [
    { "id": "top.qomicex.markdown", "version": ">=1.0.0" }
  ]
}
```

Dependency fields:

| Field | Required | Notes |
| - | - | - |
| `id` | Yes | manifest id of the depended-on plugin |
| `version` | No | Version range; missing means any version |
| `optional` | No | `true` for an optional Library plugin, `false` or missing for a required one |

Supported version syntax: `>=1.2.0`, `<=1.2.0`, `>1.0`, `<2.0`, `=1.0.1`, exact `1.2.0`, and multiple conditions separated by spaces `">=1.0 <2.0"`.

## Interface

MarkdownLib exposes the following methods through `registerMethod`:

### `renderMarkdown(md)`

Renders a full Markdown text as an HTML string.

| Item | Notes |
| - | - |
| Parameter | `md: string` — Markdown text |
| Returns | `Promise<string>` — HTML string |
| Use case | Rendering paragraphs, headings, bold text, lists, code blocks, tables, links, and so on |

### `renderMarkdownInline(md)`

Renders **inline** Markdown as HTML (no block-level elements).

| Item | Notes |
| - | - |
| Parameter | `md: string` — inline Markdown |
| Returns | `Promise<string>` — HTML string |
| Use case | Rendering `**bold**`, `` `code` ``, `[link](url)` and so on within a single line of text |

### `stripHtml(html)`

Removes HTML tags and returns plain text.

| Item | Notes |
| - | - |
| Parameter | `html: string` — HTML string |
| Returns | `Promise<string>` — plain text |
| Use case | Extracting text content, preview summaries, and so on |

## Calling methods

Call through `__PLUGIN_API__.callPlugin`:

```js theme={null}
await __PLUGIN_API__.callPlugin(pluginId, methodName, ...args)
```

```js theme={null}
const html = await __PLUGIN_API__.callPlugin(
  'top.qomicex.markdown',  // plugin ID
  'renderMarkdown',        // method name
  '# Heading\n\n**bold** and `code`'
)
```

## Examples

### Basic rendering

```html theme={null}
<div id="out"></div>
<script>
  async function render() {
    try {
      const html = await __PLUGIN_API__.callPlugin(
        'top.qomicex.markdown',
        'renderMarkdown',
        '## Heading\n\n**bold**、*italic*、`inline code`'
      )
      document.getElementById('out').innerHTML = html
    } catch (e) {
      document.getElementById('out').textContent = 'render failed: ' + e.message
    }
  }
  render()
</script>
```

### Inline rendering + stripping tags

```js theme={null}
const inline = await __PLUGIN_API__.callPlugin('top.qomicex.markdown', 'renderMarkdownInline', '**Hello** world')
// → '<strong>Hello</strong> world'

const text = await __PLUGIN_API__.callPlugin('top.qomicex.markdown', 'stripHtml', '<p>Hello <b>World</b></p>')
// → 'Hello World'
```

### Using it in an overlay window

Overlay windows expose `callPlugin` on `__PLUGIN_API__` too, with the same usage.

### Handling a missing dependency

```js theme={null}
if (await __PLUGIN_API__.callPlugin('top.qomicex.markdown', 'renderMarkdown', '**test**')) {
  // renders normally
} else {
  // dependency missing or inactive; fall back
}
```

## Implementation approach

### Provider: register methods

Once activated, MarkdownLib's `dist/index.html` exposes marked's capabilities to the plugin system through `registerMethod`:

```html theme={null}
<script>
  __PLUGIN_API__.registerMethod('renderMarkdown', function (md) {
    return marked.parse(md || '')
  })
</script>
```

Key points:

* `registerMethod(methodName, fn)`: `fn` may return a plain value or a Promise (async methods are supported too);
* a plugin can register multiple methods;
* all methods are unregistered automatically when the plugin is disabled;
* name methods meaningfully so they do not clash with other plugins.

### Consumer: call methods

Dependent plugins call through `callPlugin`:

```js theme={null}
await __PLUGIN_API__.callPlugin('top.qomicex.markdown', 'renderMarkdown', md)
```

Call flow:

```text theme={null}
consumer plugin ──callPlugin──▶ main window __pluginRegistry relays the call
                              │
                              ▼
                         MarkdownLib (activated)run renderMarkdown
                              │
                              ▼
                    returns the rendered result (Promise value)
```

### Why embed instead of using a CDN

MarkdownLib **embeds** `marked.min.js` in the plugin package instead of referencing it through `<script src="CDN">`:

* **CSP-safe after packaging**: external scripts are blocked, embedded ones are not;
* **works offline**: no network needed;
* **locked version**: bundled into the plugin, so the version is deterministic and reproducible.

## How it is defined

### Plugin dependency mechanism

Dependencies are declared in the `dependencies` array of `manifest.json` and validated by the backend at install and startup:

```text theme={null}
install time: check required dependencies are installed with matching versions, otherwise refuse the install (PLUGIN_MISSING_DEPENDENCY)
startup: activate in dependency topological order (dependencies activate before dependents)
at runtime: a missing optional dependency does not block enabling; it only errors when you call its methods
```

Dependent plugins register methods with `registerMethod` on `__PLUGIN_API__`, and the methods are stored in the main window's `__pluginRegistry`. Disabling a plugin removes its methods automatically.

### How method registration and calls work

```text theme={null}
registerMethod(name, fn)
  ──▶ main window __pluginRegistry[name] = fn

callPlugin(pluginId, name, ...args)
  ──▶ look up pluginId registered as name 
       ├── found → run fn(...args)，return the result (value or Promise)
       └── not found → reject (plugin missing, not activated, or method not registered)
```

### Error handling

`callPlugin` rejects on failure, so always wrap it in try/catch on the caller side:

| Scenario | Error message (runtime string, shown as the launcher returns it) |
| - | - |
| Target plugin not installed | `Plugin xxx does not provide method yyy (it may not be installed or activated)` |
| Target plugin not active | Same as above |
| Method not registered | Same as above |
| Error thrown inside the method | `Plugin xxx method yyy failed: <specific error>` |

## Revision history

| Date | Version | Changes | Author |
| - | - | - | - |
| 2026-08-02 | v1.0 | Initial version | AI Agent |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.