WYSIWYG Markdown editor web component with inline AI directives. Built with Lit and Tiptap.
npm install md-compleatmd-compleat requires the following peer dependencies:
npm install lit @tiptap/core @tiptap/starter-kit @tiptap/pm @tiptap/extension-image @tiptap/extension-link @tiptap/extension-table @tiptap/extension-table-row @tiptap/extension-table-header @tiptap/extension-table-cell tiptap-markdown<md-compleat content="# Hello World"></md-compleat>import 'md-compleat';
const editor = document.querySelector('md-compleat');
// Get the current markdown content
const markdown = editor.getMarkdown();A standalone build is available that bundles all dependencies (Lit, Tiptap) into a single file — no peer dependencies needed:
<script type="module">
import 'md-compleat/standalone';
</script>
<md-compleat content="# Hello World"></md-compleat>Or via CDN:
<script type="module" src="https://cdn.jsdelivr.net/npm/md-compleat/dist/md-compleat.standalone.js"></script>
<md-compleat content="# Hello from CDN"></md-compleat>The standalone build is larger (~213KB gzipped) since it includes all dependencies. Use the standard ESM import if your bundler already provides Lit and Tiptap.
| Attribute | Property | Type | Default | Description |
|---|---|---|---|---|
content |
content |
String |
'' |
Initial markdown content |
ai-shortcut |
aiShortcut |
String |
'' |
Keyboard shortcut to trigger AI directive insertion |
ai-execute-shortcut |
aiExecuteShortcut |
String |
'' |
Keyboard shortcut to execute an AI directive |
ai-provider |
aiProviderName |
String |
'' |
Provider name: "proxy" or "cli" |
ai-endpoint |
aiEndpoint |
String |
'' |
Proxy endpoint URL for the proxy provider |
ai-cli-command |
aiCliCommand |
String |
'' |
CLI command template for the CLI provider |
ai-proxy-headers |
aiProxyHeaders |
String |
'' |
JSON string of additional headers for the proxy provider |
Send AI requests through an HTTP proxy endpoint:
<md-compleat
ai-provider="proxy"
ai-endpoint="https://your-api.example.com/ai"
ai-proxy-headers='{"Authorization": "Bearer token123"}'
content="# My Document"
></md-compleat>Execute AI requests via a local CLI command:
<md-compleat
ai-provider="cli"
ai-cli-command="my-ai-tool --prompt"
content="# My Document"
></md-compleat>For full control, set the aiProvider property directly or use the createProvider() factory:
import { createProvider } from 'md-compleat';
const editor = document.querySelector('md-compleat');
// Option 1: Set a custom provider object
editor.aiProvider = {
async execute(document, signal) {
const response = await fetch('/my-api', {
method: 'POST',
body: JSON.stringify({ document }),
signal,
});
return response.text();
}
};
// Option 2: Use the factory for built-in providers
const provider = createProvider({
provider: 'proxy',
endpoint: 'https://api.example.com/ai',
});
editor.aiProvider = provider;AI directives are special inline blocks that hold an instruction for the AI provider. They are stored in markdown as <ai instruction="..." /> (self-closing) or <ai>...</ai> (block).
There are three ways to insert an AI directive in the editor:
-
Slash command (with space) — type
/ai(followed by a space) on an empty line. The text is replaced by a directive chip and the editor enters edit mode so you can type the instruction. -
Slash command (dropdown) — type
/aiand select the suggestion from the dropdown (Enter or click). Same result as above. -
Keyboard shortcut — press the shortcut configured via the
ai-shortcutattribute (e.g.Ctrl-Shift-a). This inserts a directive at the current cursor position.
Note: Typing
<ai instruction="..." />literally in the editor will not create a directive node — it will be treated as plain text and the angle brackets will be escaped on save. Always use one of the methods above.
Click the directive chip to enter inline edit mode. Self-closing directives show a single-line input; block directives show a textarea. Press Escape or click outside to confirm.
Click the play button (▶) on a directive chip, or place the cursor on a directive and press the shortcut configured via ai-execute-shortcut. The editor sends the full document context and the directive instruction to the active AI provider.
When editing markdown files outside the editor, use these formats:
<!-- Self-closing (inline instruction) -->
<ai instruction="summarize the paragraph above" />
<!-- Block (multi-line instruction) -->
<ai>
rewrite this section to be more concise
and add examples
</ai>| Method | Returns | Description |
|---|---|---|
getMarkdown() |
string |
Returns the current editor content as markdown |
getActiveProvider() |
AiProvider |
Returns the currently active AI provider |
| Property | Type | Description |
|---|---|---|
aiProvider |
AiProvider | null |
Get or set a custom AI provider instance |
The component adapts to light and dark modes automatically using light-dark(). All color properties can be overridden.
| Property | Default | Description |
|---|---|---|
--md-compleat-font-family |
system-ui, -apple-system, sans-serif |
Main font family |
--md-compleat-font-mono |
ui-monospace, 'SFMono-Regular', ... |
Monospace font family |
--md-compleat-max-width |
65ch |
Maximum width of the editor |
--md-compleat-max-height |
none |
Maximum height of the editor |
--md-compleat-focus-outline |
2px solid highlight |
Focus outline style |
--md-compleat-code-bg |
adapts to light/dark | Code block background color |
--md-compleat-blockquote-border |
adapts to light/dark | Blockquote left border |
--md-compleat-hr-color |
adapts to light/dark | Horizontal rule color |
--md-compleat-table-border |
adapts to light/dark | Table border style |
--md-compleat-link-color |
adapts to light/dark | Link color |
--md-compleat-ai-highlight |
adapts to light/dark | AI highlight background |
--md-compleat-ai-chip-bg |
adapts to light/dark | AI directive chip background |
--md-compleat-ai-chip-border |
#7c3aed |
AI directive chip border color |
| Event | Detail | Description |
|---|---|---|
content-changed |
{ content: string } |
Fired when the editor content changes |
ISC