Skip to content
On this page

Plugin API Reference

Complete API reference for Marzipan plugin development.

MarzipanInstance

The editor instance passed to plugins.

Properties

container

ts
container: HTMLElement

The main editor container element.

Example:

ts
function myPlugin() {
  return (editor) => {
    editor.container.classList.add('custom-editor');
  };
}

textarea

ts
textarea: HTMLTextAreaElement

The markdown input textarea element.

Example:

ts
function myPlugin() {
  return (editor) => {
    editor.textarea.placeholder = 'Start writing...';
  };
}

Methods

updatePreview()

ts
updatePreview(): void

Triggers an update of the preview pane.

Example:

ts
function myPlugin() {
  return (editor) => {
    editor.textarea.value = '# Hello World';
    editor.updatePreview();
  };
}

getValue()

ts
getValue(): string

Returns the current markdown content.

Example:

ts
function myPlugin() {
  return (editor) => {
    const content = editor.getValue();
    console.log('Current content:', content);
  };
}

setValue()

ts
setValue(value: string): void

Sets the markdown content and updates the preview.

Example:

ts
function myPlugin() {
  return (editor) => {
    editor.setValue('# New Content\n\nHello!');
  };
}

Plugin Types

MarzipanPlugin

ts
type MarzipanPlugin = (editor: MarzipanInstance) => void;

A function that receives the editor instance and modifies it.

PluginFactory

ts
type PluginFactory<T = any> = (options?: T) => MarzipanPlugin;

A function that accepts options and returns a plugin function.

Example:

ts
interface MyPluginOptions {
  label: string;
}

const myPlugin: PluginFactory<MyPluginOptions> = (opts) => {
  return (editor) => {
    console.log(opts.label);
  };
};

DOM Selectors

Toolbar

ts
const toolbar = editor.container.querySelector('.marzipan-toolbar');

Preview

ts
const preview = editor.container.querySelector('.marzipan-preview');

Textarea

ts
const textarea = editor.textarea;
// or
const textarea = editor.container.querySelector('.marzipan-textarea');

Common Patterns

Insert Text at Cursor

ts
function insertText(editor: MarzipanInstance, text: string) {
  const { textarea, updatePreview } = editor;
  const start = textarea.selectionStart ?? 0;
  const end = textarea.selectionEnd ?? 0;
  
  textarea.setRangeText(text, start, end, 'end');
  updatePreview();
  textarea.focus();
}

Wrap Selection

ts
function wrapText(
  editor: MarzipanInstance,
  prefix: string,
  suffix: string
) {
  const { textarea, updatePreview } = editor;
  const start = textarea.selectionStart ?? 0;
  const end = textarea.selectionEnd ?? 0;
  const selection = textarea.value.substring(start, end);
  
  textarea.setRangeText(
    `${prefix}${selection}${suffix}`,
    start,
    end,
    'end'
  );
  
  updatePreview();
  textarea.focus();
}

Get Current Line

ts
function getCurrentLine(editor: MarzipanInstance): string {
  const { textarea } = editor;
  const pos = textarea.selectionStart ?? 0;
  const text = textarea.value;
  
  const lineStart = text.lastIndexOf('\n', pos - 1) + 1;
  const lineEnd = text.indexOf('\n', pos);
  
  return text.substring(
    lineStart,
    lineEnd === -1 ? text.length : lineEnd
  );
}

Replace Current Line

ts
function replaceCurrentLine(
  editor: MarzipanInstance,
  newLine: string
) {
  const { textarea, updatePreview } = editor;
  const pos = textarea.selectionStart ?? 0;
  const text = textarea.value;
  
  const lineStart = text.lastIndexOf('\n', pos - 1) + 1;
  const lineEnd = text.indexOf('\n', pos);
  const actualEnd = lineEnd === -1 ? text.length : lineEnd;
  
  textarea.setRangeText(newLine, lineStart, actualEnd, 'end');
  updatePreview();
  textarea.focus();
}

CSS Classes

Editor Container

.marzipan-container

Toolbar

.marzipan-toolbar

Toolbar Button

.mz-btn

Textarea

.marzipan-textarea

Preview

.marzipan-preview

Popover

.mz-pop

CSS Variables

Colors

css
--mz-bg          /* Background */
--mz-fg          /* Foreground text */
--mz-border      /* Border color */
--mz-accent      /* Accent color */

Toolbar

css
--mz-toolbar-bg  /* Toolbar background */
--mz-btn-bg      /* Button background */
--mz-btn-fg      /* Button text */
--mz-btn-hover   /* Button hover state */

Preview

css
--mz-preview-bg  /* Preview background */
--mz-preview-fg  /* Preview text */

Events

Textarea Events

input

Fired when content changes.

ts
function myPlugin() {
  return (editor) => {
    editor.textarea.addEventListener('input', () => {
      console.log('Content changed');
    });
  };
}

keydown

Fired on key press.

ts
function myPlugin() {
  return (editor) => {
    editor.textarea.addEventListener('keydown', (e) => {
      if (e.key === 'Tab' && !e.shiftKey) {
        e.preventDefault();
        // Handle tab
      }
    });
  };
}

Custom Events

marzipan:accent

Fired when accent color changes.

ts
function myPlugin() {
  return (editor) => {
    editor.container.addEventListener('marzipan:accent', (e) => {
      console.log('New accent:', e.detail.color);
    });
  };
}

Helper Utilities

Debounce

ts
function debounce<T extends (...args: any[]) => any>(
  fn: T,
  delay: number
): (...args: Parameters<T>) => void {
  let timeoutId: ReturnType<typeof setTimeout>;
  
  return (...args: Parameters<T>) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn(...args), delay);
  };
}

// Usage
function myPlugin() {
  return (editor) => {
    const debouncedUpdate = debounce(() => {
      console.log('Content updated');
    }, 300);
    
    editor.textarea.addEventListener('input', debouncedUpdate);
  };
}

Throttle

ts
function throttle<T extends (...args: any[]) => any>(
  fn: T,
  delay: number
): (...args: Parameters<T>) => void {
  let lastCall = 0;
  
  return (...args: Parameters<T>) => {
    const now = Date.now();
    if (now - lastCall >= delay) {
      lastCall = now;
      fn(...args);
    }
  };
}

Complete Examples

Toolbar Button Plugin

ts
import type { MarzipanInstance } from '@pinkpixel/marzipan';

interface ButtonPluginOptions {
  label?: string;
  title?: string;
  action: (editor: MarzipanInstance) => void;
}

export function buttonPlugin(opts: ButtonPluginOptions) {
  const { label = '🔧', title = 'Action', action } = opts;
  
  return (editor: MarzipanInstance) => {
    const toolbar = editor.container.querySelector('.marzipan-toolbar');
    if (!toolbar) return;
    
    const button = document.createElement('button');
    button.type = 'button';
    button.className = 'mz-btn';
    button.textContent = label;
    button.title = title;
    button.onclick = () => action(editor);
    
    toolbar.appendChild(button);
  };
}

// Usage
buttonPlugin({
  label: '📝',
  title: 'Insert template',
  action: (editor) => {
    editor.setValue('# Template\n\nContent here...');
  }
})

Preview Enhancement Plugin

ts
export function enhancePreview() {
  return (editor: MarzipanInstance) => {
    const { container, updatePreview } = editor;
    
    const originalUpdate = updatePreview.bind(editor);
    editor.updatePreview = function() {
      originalUpdate();
      
      const preview = container.querySelector('.marzipan-preview');
      if (!preview) return;
      
      // Add syntax highlighting
      preview.querySelectorAll('pre code').forEach(block => {
        // Highlight code
      });
      
      // Add copy buttons
      preview.querySelectorAll('pre').forEach(pre => {
        if (pre.querySelector('.copy-button')) return;
        
        const button = document.createElement('button');
        button.textContent = 'Copy';
        button.className = 'copy-button';
        button.onclick = () => {
          const code = pre.textContent || '';
          navigator.clipboard.writeText(code);
        };
        
        pre.appendChild(button);
      });
    };
    
    updatePreview.call(editor);
  };
}

Keyboard Shortcut Plugin

ts
interface ShortcutOptions {
  key: string;
  ctrl?: boolean;
  alt?: boolean;
  shift?: boolean;
  action: (editor: MarzipanInstance) => void;
}

export function shortcutPlugin(opts: ShortcutOptions) {
  return (editor: MarzipanInstance) => {
    editor.textarea.addEventListener('keydown', (e) => {
      const matches = 
        e.key === opts.key &&
        e.ctrlKey === (opts.ctrl ?? false) &&
        e.altKey === (opts.alt ?? false) &&
        e.shiftKey === (opts.shift ?? false);
      
      if (matches) {
        e.preventDefault();
        opts.action(editor);
      }
    });
  };
}

// Usage: Ctrl+Shift+B for bold
shortcutPlugin({
  key: 'b',
  ctrl: true,
  shift: true,
  action: (editor) => {
    const { textarea, updatePreview } = editor;
    const start = textarea.selectionStart ?? 0;
    const end = textarea.selectionEnd ?? 0;
    const selection = textarea.value.substring(start, end);
    
    textarea.setRangeText(`**${selection}**`, start, end, 'end');
    updatePreview();
  }
})

TypeScript Definitions

Full type definitions:

ts
interface MarzipanInstance {
  container: HTMLElement;
  textarea: HTMLTextAreaElement;
  updatePreview: () => void;
  getValue: () => string;
  setValue: (value: string) => void;
}

type MarzipanPlugin = (editor: MarzipanInstance) => void;

type PluginFactory<T = any> = (options?: T) => MarzipanPlugin;

interface MarzipanOptions {
  toolbar?: boolean;
  theme?: 'solar' | 'cave' | 'forest';
  value?: string;
  placeholder?: string;
  minHeight?: string;
  plugins?: MarzipanPlugin[];
  onChange?: (value: string, instance: MarzipanInstance) => void;
  onReady?: (instance: MarzipanInstance) => void;
}

See Also

Released under the Apache 2.0 License