@pinkpixel/marzipan 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +133 -0
- package/dist/favicon.png +0 -0
- package/dist/index.js +3212 -0
- package/dist/index.js.map +1 -0
- package/dist/logo.png +0 -0
- package/docs/README.md +33 -0
- package/docs/TABLE_OF_CONTENTS.md +19 -0
- package/docs/api.md +749 -0
- package/docs/plugins.md +57 -0
- package/docs/quick-start.md +90 -0
- package/docs/types.d.ts +231 -0
- package/package.json +66 -0
package/docs/plugins.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Plugin Catalogue
|
|
2
|
+
|
|
3
|
+
Marzipan ships first-party plugins from the `src/plugins` directory. Each plugin exports a factory from `@pinkpixel/marzipan/plugins/<name>` so you can opt into only the code you need.
|
|
4
|
+
|
|
5
|
+
## Using a plugin
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { Marzipan } from '@pinkpixel/marzipan';
|
|
9
|
+
import { tablePlugin } from '@pinkpixel/marzipan/plugins/tablePlugin';
|
|
10
|
+
|
|
11
|
+
new Marzipan('#editor', {
|
|
12
|
+
toolbar: true,
|
|
13
|
+
plugins: [tablePlugin({
|
|
14
|
+
defaultColumns: 3,
|
|
15
|
+
defaultRows: 4,
|
|
16
|
+
})],
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Every factory returns an object that Marzipan consumes internally. You can mix and match plugins freely.
|
|
21
|
+
|
|
22
|
+
## Available plugins
|
|
23
|
+
|
|
24
|
+
| Plugin | Import Path | Description |
|
|
25
|
+
|--------|-------------|-------------|
|
|
26
|
+
| `accentSwatchPlugin` | `@pinkpixel/marzipan/plugins/accentSwatchPlugin` | Adds a palette picker for accent colours and syncs with the toolbar + stats bar. |
|
|
27
|
+
| `imageManagerPlugin` | `@pinkpixel/marzipan/plugins/imageManagerPlugin` | Dropzone and gallery UI for inserting images and managing uploads. |
|
|
28
|
+
| `imagePickerPlugin` | `@pinkpixel/marzipan/plugins/imagePickerPlugin` | Toolbar button for inserting images via URL or optional uploader callback. |
|
|
29
|
+
| `mermaidPlugin` | `@pinkpixel/marzipan/plugins/mermaidPlugin` | Lazy-loads Mermaid from npm/ESM and renders diagrams inline. |
|
|
30
|
+
| `mermaidExternalPlugin` | `@pinkpixel/marzipan/plugins/mermaidExternal` | Mermaid integration that targets a CDN script tag—perfect for sandboxed playgrounds. |
|
|
31
|
+
| `tablePlugin` | `@pinkpixel/marzipan/plugins/tablePlugin` | Toolbar-driven table generator with inline editing controls. |
|
|
32
|
+
| `tableGridPlugin` | `@pinkpixel/marzipan/plugins/tableGridPlugin` | Grid overlay for rapid column/row creation (exports `tableGridStyles`). |
|
|
33
|
+
| `tableGeneratorPlugin` | `@pinkpixel/marzipan/plugins/tableGenerator` | Quick GFM table inserter with prompt-driven sizing. |
|
|
34
|
+
| `tinyHighlightPlugin` | `@pinkpixel/marzipan/plugins/tinyHighlight` | Zero-runtime syntax highlighting for fenced code blocks (`tinyHighlightStyles` helper available). |
|
|
35
|
+
|
|
36
|
+
> 📝 The plugin names map 1:1 to files in `src/plugins`. Inspect those files for advanced configuration options.
|
|
37
|
+
|
|
38
|
+
## Configuration tips
|
|
39
|
+
|
|
40
|
+
- **Tree shaking** – Import plugins individually; bundlers remove unused exports automatically.
|
|
41
|
+
- **Styling** – Some plugins inject their own CSS. Bakeshop demonstrates how to mirror the styling in your app.
|
|
42
|
+
- **Events** – Many plugins accept callbacks (e.g., image handlers). Pass your own upload or analytics hooks through the factory options.
|
|
43
|
+
- **Server-side rendering** – When using SSR, guard plugin usage behind `typeof window !== 'undefined'` if they rely on browser-only APIs.
|
|
44
|
+
|
|
45
|
+
## Demo coverage
|
|
46
|
+
|
|
47
|
+
The `bakeshop-demo` application renders every plugin in the “Plugin Gallery” panel. Launch it with:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
cd bakeshop-demo
|
|
51
|
+
npm install
|
|
52
|
+
npm run dev
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Use the panel toggles to see plugin behaviour before integrating it into your own project.
|
|
56
|
+
|
|
57
|
+
For change history and new additions, see the [CHANGELOG](../CHANGELOG.md).
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Marzipan Quick Start Guide
|
|
2
|
+
|
|
3
|
+
Spin up the editor, wire up the bundled actions, and opt into plugins in just a few steps. 🎉
|
|
4
|
+
|
|
5
|
+
## 1. Install the package
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @pinkpixel/marzipan
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The package ships ESM output and TypeScript definitions. No extra formatting dependencies are required.
|
|
12
|
+
|
|
13
|
+
## 2. Render your first editor
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { Marzipan } from '@pinkpixel/marzipan';
|
|
17
|
+
|
|
18
|
+
const [editor] = new Marzipan('#my-editor', {
|
|
19
|
+
placeholder: 'Start writing…',
|
|
20
|
+
toolbar: true,
|
|
21
|
+
theme: 'solar',
|
|
22
|
+
smartLists: true,
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
You can pass a selector string, DOM element, `NodeList`, or array of elements. The constructor always returns an array of instances.
|
|
27
|
+
|
|
28
|
+
## 3. Trigger formatting with bundled actions
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { actions } from '@pinkpixel/marzipan';
|
|
32
|
+
|
|
33
|
+
const textarea = document.querySelector<HTMLTextAreaElement>('#my-editor textarea');
|
|
34
|
+
if (textarea) {
|
|
35
|
+
actions.toggleBold(textarea);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
All actions accept the target `HTMLTextAreaElement`. They’re the same utilities used internally by the toolbar and keyboard shortcuts.
|
|
40
|
+
|
|
41
|
+
## 4. Enable a plugin
|
|
42
|
+
|
|
43
|
+
Every plugin in `src/plugins` publishes under `@pinkpixel/marzipan/plugins/<name>`.
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { tablePlugin } from '@pinkpixel/marzipan/plugins/tablePlugin';
|
|
47
|
+
import { tinyHighlight } from '@pinkpixel/marzipan/plugins/tinyHighlight';
|
|
48
|
+
|
|
49
|
+
new Marzipan('#with-plugins', {
|
|
50
|
+
plugins: [tablePlugin(), tinyHighlight()],
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Each plugin exports a factory so you can configure behavior before adding it to the editor instance.
|
|
55
|
+
|
|
56
|
+
## 5. React usage (example)
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { useEffect, useRef } from 'react';
|
|
60
|
+
import { Marzipan } from '@pinkpixel/marzipan';
|
|
61
|
+
import { actions } from '@pinkpixel/marzipan';
|
|
62
|
+
|
|
63
|
+
export function Editor() {
|
|
64
|
+
const hostRef = useRef<HTMLDivElement>(null);
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (!hostRef.current) return;
|
|
68
|
+
const [instance] = new Marzipan(hostRef.current, { toolbar: true });
|
|
69
|
+
return () => instance.destroy?.();
|
|
70
|
+
}, []);
|
|
71
|
+
|
|
72
|
+
return <div ref={hostRef} />;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 6. Explore the Bakeshop demo
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
cd bakeshop-demo
|
|
80
|
+
npm install
|
|
81
|
+
npm run dev
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Open `http://localhost:5173` to experiment with every option, plugin, and action in a guided playground.
|
|
85
|
+
|
|
86
|
+
## Next steps
|
|
87
|
+
|
|
88
|
+
- Review the [API reference](./api.md) for events, helpers, and configuration details.
|
|
89
|
+
- Browse the [Plugin Catalogue](./plugins.md) to discover tables, Mermaid, image helpers, and more.
|
|
90
|
+
- Check [../CHANGELOG.md](../CHANGELOG.md) for the latest updates.
|
package/docs/types.d.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript definitions for Marzipan markdown editor
|
|
3
|
+
* @version 1.0.0
|
|
4
|
+
* @author Pink Pixel
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface MarzipanTheme {
|
|
8
|
+
name: string;
|
|
9
|
+
colors: ThemeColors;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ThemeColors {
|
|
13
|
+
// Base colors
|
|
14
|
+
background: string;
|
|
15
|
+
text: string;
|
|
16
|
+
textMuted?: string;
|
|
17
|
+
|
|
18
|
+
// Syntax highlighting
|
|
19
|
+
comment: string;
|
|
20
|
+
keyword: string;
|
|
21
|
+
string: string;
|
|
22
|
+
number: string;
|
|
23
|
+
punctuation?: string;
|
|
24
|
+
|
|
25
|
+
// UI elements
|
|
26
|
+
selection?: string;
|
|
27
|
+
border?: string;
|
|
28
|
+
toolbar?: string;
|
|
29
|
+
|
|
30
|
+
// Interactive elements
|
|
31
|
+
linkHover?: string;
|
|
32
|
+
buttonActive?: string;
|
|
33
|
+
|
|
34
|
+
// Additional colors for custom themes
|
|
35
|
+
[key: string]: string | undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface MarzipanMobileOptions {
|
|
39
|
+
fontSize: string;
|
|
40
|
+
padding: string;
|
|
41
|
+
lineHeight: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface StatsData {
|
|
45
|
+
chars: number;
|
|
46
|
+
words: number;
|
|
47
|
+
lines: number;
|
|
48
|
+
line: number;
|
|
49
|
+
column: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type StatsFormatter = (stats: StatsData) => string;
|
|
53
|
+
|
|
54
|
+
export type ChangeHandler = (value: string, instance: Marzipan) => void;
|
|
55
|
+
|
|
56
|
+
export type KeydownHandler = (event: KeyboardEvent, instance: Marzipan) => void;
|
|
57
|
+
|
|
58
|
+
export interface ToolbarConfig {
|
|
59
|
+
buttons: Array<ToolbarButton | '|'>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type ToolbarButton =
|
|
63
|
+
| 'bold'
|
|
64
|
+
| 'italic'
|
|
65
|
+
| 'strikethrough'
|
|
66
|
+
| 'code'
|
|
67
|
+
| 'link'
|
|
68
|
+
| 'image'
|
|
69
|
+
| 'quote'
|
|
70
|
+
| 'ul'
|
|
71
|
+
| 'ol'
|
|
72
|
+
| 'hr'
|
|
73
|
+
| 'toggle-plain';
|
|
74
|
+
|
|
75
|
+
export interface TextareaProps {
|
|
76
|
+
[key: string]: any;
|
|
77
|
+
className?: string;
|
|
78
|
+
class?: string;
|
|
79
|
+
style?: Partial<CSSStyleDeclaration> | string;
|
|
80
|
+
'aria-label'?: string;
|
|
81
|
+
'data-testid'?: string;
|
|
82
|
+
maxlength?: number;
|
|
83
|
+
rows?: number;
|
|
84
|
+
cols?: number;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface MarzipanOptions {
|
|
88
|
+
// Typography
|
|
89
|
+
fontSize?: string;
|
|
90
|
+
lineHeight?: number;
|
|
91
|
+
fontFamily?: string;
|
|
92
|
+
padding?: string;
|
|
93
|
+
|
|
94
|
+
// Mobile-specific styles
|
|
95
|
+
mobile?: Partial<MarzipanMobileOptions>;
|
|
96
|
+
|
|
97
|
+
// Textarea properties
|
|
98
|
+
textareaProps?: TextareaProps;
|
|
99
|
+
|
|
100
|
+
// Behavior
|
|
101
|
+
autofocus?: boolean;
|
|
102
|
+
autoResize?: boolean;
|
|
103
|
+
minHeight?: string;
|
|
104
|
+
maxHeight?: string | null;
|
|
105
|
+
placeholder?: string;
|
|
106
|
+
value?: string;
|
|
107
|
+
|
|
108
|
+
// Callbacks
|
|
109
|
+
onChange?: ChangeHandler;
|
|
110
|
+
onKeydown?: KeydownHandler;
|
|
111
|
+
|
|
112
|
+
// Features
|
|
113
|
+
showActiveLineRaw?: boolean;
|
|
114
|
+
showStats?: boolean;
|
|
115
|
+
toolbar?: boolean | ToolbarConfig;
|
|
116
|
+
statsFormatter?: StatsFormatter;
|
|
117
|
+
smartLists?: boolean;
|
|
118
|
+
|
|
119
|
+
// Themes (per-instance)
|
|
120
|
+
theme?: string | MarzipanTheme;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface RenderOptions {
|
|
124
|
+
cleanHTML?: boolean;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export type TargetElement = string | Element | NodeList | Array<Element>;
|
|
128
|
+
|
|
129
|
+
export declare class Marzipan {
|
|
130
|
+
// Static properties
|
|
131
|
+
static instances: WeakMap<Element, Marzipan>;
|
|
132
|
+
static stylesInjected: boolean;
|
|
133
|
+
static globalListenersInitialized: boolean;
|
|
134
|
+
static instanceCount: number;
|
|
135
|
+
static currentTheme: MarzipanTheme;
|
|
136
|
+
|
|
137
|
+
// Static attached utilities
|
|
138
|
+
static MarkdownParser: any;
|
|
139
|
+
static ShortcutsManager: any;
|
|
140
|
+
static themes: { [key: string]: MarzipanTheme };
|
|
141
|
+
static getTheme: (name: string) => MarzipanTheme | null;
|
|
142
|
+
|
|
143
|
+
// Instance properties
|
|
144
|
+
element: Element;
|
|
145
|
+
container: HTMLElement;
|
|
146
|
+
wrapper: HTMLElement;
|
|
147
|
+
textarea: HTMLTextAreaElement;
|
|
148
|
+
preview: HTMLElement;
|
|
149
|
+
statsBar?: HTMLElement;
|
|
150
|
+
toolbar?: any;
|
|
151
|
+
shortcuts: any;
|
|
152
|
+
linkTooltip: any;
|
|
153
|
+
|
|
154
|
+
options: Required<MarzipanOptions>;
|
|
155
|
+
instanceId: number;
|
|
156
|
+
initialized: boolean;
|
|
157
|
+
instanceTheme: string | MarzipanTheme | null;
|
|
158
|
+
|
|
159
|
+
// Constructor
|
|
160
|
+
constructor(target: TargetElement, options?: MarzipanOptions);
|
|
161
|
+
|
|
162
|
+
// Instance methods - Content Management
|
|
163
|
+
getValue(): string;
|
|
164
|
+
setValue(value: string): void;
|
|
165
|
+
getRenderedHTML(options?: RenderOptions): string;
|
|
166
|
+
getPreviewHTML(): string;
|
|
167
|
+
getCleanHTML(): string;
|
|
168
|
+
|
|
169
|
+
// Instance methods - Focus Management
|
|
170
|
+
focus(): void;
|
|
171
|
+
blur(): void;
|
|
172
|
+
|
|
173
|
+
// Instance methods - Display Modes
|
|
174
|
+
showPlainTextarea(show: boolean): boolean;
|
|
175
|
+
showPreviewMode(show: boolean): boolean;
|
|
176
|
+
showStats(show: boolean): void;
|
|
177
|
+
|
|
178
|
+
// Instance methods - Editor State
|
|
179
|
+
updatePreview(): void;
|
|
180
|
+
isInitialized(): boolean;
|
|
181
|
+
reinit(options?: Partial<MarzipanOptions>): void;
|
|
182
|
+
destroy(): void;
|
|
183
|
+
|
|
184
|
+
// Instance methods - Event Handlers (typically internal)
|
|
185
|
+
handleInput(event: Event): void;
|
|
186
|
+
handleKeydown(event: KeyboardEvent): void;
|
|
187
|
+
handleScroll(event: Event): void;
|
|
188
|
+
handleSmartListContinuation(): boolean;
|
|
189
|
+
|
|
190
|
+
// Static methods
|
|
191
|
+
static init(target: TargetElement, options?: MarzipanOptions): Array<Marzipan>;
|
|
192
|
+
static getInstance(element: Element): Marzipan | null;
|
|
193
|
+
static destroyAll(): void;
|
|
194
|
+
static setTheme(theme: string | MarzipanTheme, customColors?: Partial<ThemeColors>): void;
|
|
195
|
+
static injectStyles(force?: boolean): void;
|
|
196
|
+
static initGlobalListeners(): void;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Default export
|
|
200
|
+
export default Marzipan;
|
|
201
|
+
|
|
202
|
+
// Module declaration for environments that need it
|
|
203
|
+
declare module '@pinkpixel/marzipan' {
|
|
204
|
+
export default Marzipan;
|
|
205
|
+
export {
|
|
206
|
+
Marzipan,
|
|
207
|
+
MarzipanOptions,
|
|
208
|
+
MarzipanTheme,
|
|
209
|
+
ThemeColors,
|
|
210
|
+
StatsData,
|
|
211
|
+
StatsFormatter,
|
|
212
|
+
ChangeHandler,
|
|
213
|
+
KeydownHandler,
|
|
214
|
+
ToolbarConfig,
|
|
215
|
+
ToolbarButton,
|
|
216
|
+
TextareaProps,
|
|
217
|
+
RenderOptions,
|
|
218
|
+
TargetElement
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Global declaration for browser environments
|
|
223
|
+
declare global {
|
|
224
|
+
interface Window {
|
|
225
|
+
Marzipan: typeof Marzipan;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
interface Element {
|
|
229
|
+
MarzipanInstance?: Marzipan;
|
|
230
|
+
}
|
|
231
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pinkpixel/marzipan",
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "Pure TypeScript markdown editor library with overlay preview",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"author": "Pink Pixel",
|
|
8
|
+
"email": "admin@pinkpixel.dev",
|
|
9
|
+
"homepage": "https://marzipan.pinkpixel.dev",
|
|
10
|
+
"repository": "https://github.com/pinkpixel-dev/marzipan",
|
|
11
|
+
"issues": "https://github.com/pinkpixel-dev/marzipan/issues",
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=20"
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"module": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"import": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts"
|
|
22
|
+
},
|
|
23
|
+
"./plugins/*": {
|
|
24
|
+
"import": "./dist/plugins/*.js",
|
|
25
|
+
"types": "./dist/plugins/*.d.ts"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md",
|
|
31
|
+
"docs"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"dev": "vite build --watch",
|
|
35
|
+
"build": "tsc && vite build",
|
|
36
|
+
"lint": "eslint .",
|
|
37
|
+
"prettier": "prettier --write .",
|
|
38
|
+
"typecheck": "tsc --noEmit",
|
|
39
|
+
"prepublishOnly": "npm run build",
|
|
40
|
+
"prepack": "npm run build"
|
|
41
|
+
},
|
|
42
|
+
"keywords": [
|
|
43
|
+
"markdown",
|
|
44
|
+
"editor",
|
|
45
|
+
"typescript",
|
|
46
|
+
"library",
|
|
47
|
+
"lightweight",
|
|
48
|
+
"textarea",
|
|
49
|
+
"overlay",
|
|
50
|
+
"preview",
|
|
51
|
+
"wysiwyg"
|
|
52
|
+
],
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@eslint/js": "^9.36.0",
|
|
55
|
+
"@types/node": "^24.5.2",
|
|
56
|
+
"@typescript-eslint/eslint-plugin": "8.44.1",
|
|
57
|
+
"@typescript-eslint/parser": "8.44.1",
|
|
58
|
+
"eslint": "^9.36.0",
|
|
59
|
+
"globals": "16.4.0",
|
|
60
|
+
"mermaid": "^11.12.0",
|
|
61
|
+
"prettier": "^3.6.2",
|
|
62
|
+
"typescript": "^5.9.2",
|
|
63
|
+
"typescript-eslint": "^8.44.1",
|
|
64
|
+
"vite": "^7.1.7"
|
|
65
|
+
}
|
|
66
|
+
}
|