### Install notectl core
Source: https://github.com/samyssmile/notectl/blob/main/README.md
Install the core package via npm.
```bash
npm install @notectl/core
```
--------------------------------
### Install Core Package with npm, pnpm, yarn, or bun
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/getting-started/installation.md
Install the core notectl package using your preferred package manager.
```bash
# npm
npm install @notectl/core
```
```bash
# pnpm
pnpm add @notectl/core
```
```bash
# yarn
yarn add @notectl/core
```
```bash
# bun
bun add @notectl/core
```
--------------------------------
### Start Angular Development Server
Source: https://github.com/samyssmile/notectl/blob/main/examples/angular/README.md
Run this command to start a local development server. The application will automatically reload on source file changes.
```bash
ng serve
```
--------------------------------
### Custom HighlightPlugin Colors Example
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/highlight.md
Example of initializing the HighlightPlugin with a custom palette for specific study-mode use cases.
```typescript
// Study-mode highlighter colors
new HighlightPlugin({
colors: [
'#fff176', // Yellow — key definitions
'#aed581', // Green — important facts
'#4dd0e1', // Cyan — questions
'#f48fb1', // Pink — action items
'#ffab91', // Orange — examples
],
})
```
--------------------------------
### Execute List Commands
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/list.md
Examples of executing list-related commands on the editor instance.
```ts
// Create a bullet list
editor.executeCommand('toggleList:bullet');
// Indent a list item
editor.executeCommand('indentListItem');
// Toggle a checkbox
editor.executeCommand('toggleChecklistItem');
```
--------------------------------
### Import Built-in Themes
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/theme.md
Examples of importing the default light and dark themes.
```ts
import { LIGHT_THEME } from '@notectl/core';
// background: '#ffffff', primary: '#4a90d9', ...
```
```ts
import { DARK_THEME } from '@notectl/core';
// background: '#1e1e2e', primary: '#89b4fa', ...
```
--------------------------------
### Example: Get Link Href
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/commands.md
An example demonstrating how to extract the 'href' attribute from a 'link' mark at the current selection.
```typescript
const href = getMarkAttrAtSelection(state, markType('link'), (m) => m.attrs.href);
```
--------------------------------
### Full Toolbar Example with All Plugins
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/toolbar.mdx
A comprehensive toolbar configuration including all built-in plugins, organized into logical groups for text formatting, block types, elements, and typography.
```typescript
import { createEditor } from '@notectl/core';
import { TextFormattingPlugin } from '@notectl/core/plugins/text-formatting';
import { HeadingPlugin } from '@notectl/core/plugins/heading';
import { BlockquotePlugin } from '@notectl/core/plugins/blockquote';
import { LinkPlugin } from '@notectl/core/plugins/link';
import { ListPlugin } from '@notectl/core/plugins/list';
import { TablePlugin } from '@notectl/core/plugins/table';
import { CodeBlockPlugin } from '@notectl/core/plugins/code-block';
import { HorizontalRulePlugin } from '@notectl/core/plugins/horizontal-rule';
import { StrikethroughPlugin } from '@notectl/core/plugins/strikethrough';
import { SuperSubPlugin } from '@notectl/core/plugins/super-sub';
import { HighlightPlugin } from '@notectl/core/plugins/highlight';
import { TextColorPlugin } from '@notectl/core/plugins/text-color';
import { AlignmentPlugin } from '@notectl/core/plugins/alignment';
import { FontPlugin } from '@notectl/core/plugins/font';
import { FontSizePlugin } from '@notectl/core/plugins/font-size';
import { ImagePlugin } from '@notectl/core/plugins/image';
import { HardBreakPlugin } from '@notectl/core/plugins/hard-break';
import { STARTER_FONTS } from '@notectl/core/fonts';
const editor = await createEditor({
toolbar: [
// Text formatting: Bold, Italic, Underline
[new TextFormattingPlugin({ bold: true, italic: true, underline: true })],
// Block types: Paragraph / Heading selector
[new HeadingPlugin()],
// Block elements
[new BlockquotePlugin(), new LinkPlugin()],
// Lists: Ordered and Unordered
[new ListPlugin()],
// Tables and Images
[new TablePlugin(), new ImagePlugin()],
// Code blocks
[new CodeBlockPlugin()],
// Additional inline formatting
[
new HorizontalRulePlugin(),
new StrikethroughPlugin(),
new SuperSubPlugin(),
new HighlightPlugin(),
new TextColorPlugin(),
],
// Typography: Alignment, Font, Size
[
new AlignmentPlugin(),
new FontPlugin({ fonts: [...STARTER_FONTS] }),
new FontSizePlugin({ sizes: [12, 16, 24, 32, 48], defaultSize: 16 }),
],
],
plugins: [new HardBreakPlugin()], // No toolbar UI needed
placeholder: 'Start typing...',
autofocus: true,
});
```
--------------------------------
### Basic Toolbar Setup
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/toolbar.mdx
Configure the toolbar with basic text formatting, heading, and list plugins. Each array in the `toolbar` option represents a visual group of buttons.
```typescript
import { createEditor } from '@notectl/core';
import { TextFormattingPlugin } from '@notectl/core/plugins/text-formatting';
import { HeadingPlugin } from '@notectl/core/plugins/heading';
import { ListPlugin } from '@notectl/core/plugins/list';
const editor = await createEditor({
toolbar: [
[new TextFormattingPlugin()], // Group 1: B, I, U
[new HeadingPlugin()], // Group 2: Heading dropdown
[new ListPlugin()], // Group 3: List buttons
],
});
```
--------------------------------
### Install Notectl Angular Package
Source: https://github.com/samyssmile/notectl/blob/main/README.md
Install the core and Angular-specific packages for Notectl using npm.
```bash
npm install @notectl/core @notectl/angular
```
--------------------------------
### Full-Featured Notectl Editor Setup
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/angular.mdx
Demonstrates a production-ready editor configuration with all available plugins. Ensure all necessary plugins are imported and configured within the toolbar array.
```typescript
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import {
NotectlEditorComponent,
TextFormattingPlugin,
HeadingPlugin,
ListPlugin,
LinkPlugin,
BlockquotePlugin,
CodeBlockPlugin,
TablePlugin,
HorizontalRulePlugin,
StrikethroughPlugin,
HighlightPlugin,
TextColorPlugin,
FontPlugin,
FontSizePlugin,
AlignmentPlugin,
SuperSubPlugin,
STARTER_FONTS,
ThemePreset,
} from '@notectl/angular';
import type { Plugin } from '@notectl/angular';
@Component({
selector: 'app-full-editor',
imports: [NotectlEditorComponent],
template: `
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FullEditorComponent {
protected readonly theme = signal(ThemePreset.Light);
protected readonly toolbar: ReadonlyArray> = [
[new TextFormattingPlugin({ bold: true, italic: true, underline: true })],
[new HeadingPlugin()],
[new BlockquotePlugin(), new LinkPlugin()],
[new ListPlugin()],
[new TablePlugin()],
[new HorizontalRulePlugin(), new StrikethroughPlugin(), new TextColorPlugin()],
[new HighlightPlugin(), new SuperSubPlugin()],
[new CodeBlockPlugin()],
[
new AlignmentPlugin(),
new FontPlugin({ fonts: [...STARTER_FONTS] }),
new FontSizePlugin({ sizes: [12, 16, 24, 32, 48], defaultSize: 16 }),
],
];
}
```
--------------------------------
### Toolbar Plugin - Manual Setup
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/toolbar.md
For advanced use cases, you can create and configure the ToolbarPlugin manually.
```APIDOC
## Manual Setup
For advanced use cases, you can create the ToolbarPlugin manually:
```ts
import { ToolbarPlugin } from '@notectl/core/plugins/toolbar';
const toolbar = new ToolbarPlugin({
groups: [['text-formatting'], ['heading']],
});
const editor = await createEditor({
plugins: [
new TextFormattingPlugin(),
new HeadingPlugin(),
toolbar,
],
});
```
```
--------------------------------
### Create Full Editor Preset
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/presets.md
Use `createFullPreset` to get all standard plugins for a production-ready editor. Configure theme and placeholder as needed.
```typescript
import { createEditor, ThemePreset } from '@notectl/core';
import { createFullPreset } from '@notectl/core/presets';
const editor = await createEditor({
...createFullPreset(),
theme: ThemePreset.Light,
placeholder: 'Start typing...',
});
document.getElementById('app').appendChild(editor);
```
--------------------------------
### Initialize notectl editor
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/index.mdx
Demonstrates various ways to initialize and interact with the notectl editor, including minimal setup, full presets, custom toolbars, and content management.
```ts
import { createEditor } from '@notectl/core';
const editor = await createEditor({
placeholder: 'Start typing...',
});
document.getElementById('editor')!.appendChild(editor);
```
```ts
import { createEditor, ThemePreset } from '@notectl/core';
import { createFullPreset } from '@notectl/core/presets';
const editor = await createEditor({
...createFullPreset(),
theme: ThemePreset.Light,
placeholder: 'Start typing...',
});
document.getElementById('editor')!.appendChild(editor);
```
```ts
import { createEditor } from '@notectl/core';
import { TextFormattingPlugin } from '@notectl/core/plugins/text-formatting';
import { HeadingPlugin } from '@notectl/core/plugins/heading';
import { ListPlugin } from '@notectl/core/plugins/list';
import { LinkPlugin } from '@notectl/core/plugins/link';
import { TablePlugin } from '@notectl/core/plugins/table';
const editor = await createEditor({
toolbar: [
[new TextFormattingPlugin()],
[new HeadingPlugin()],
[new ListPlugin()],
[new LinkPlugin(), new TablePlugin()],
],
});
document.getElementById('editor')!.appendChild(editor);
```
```ts
// Get content as JSON, HTML, or plain text
const json = editor.getJSON();
const html = await editor.getContentHTML();
const text = editor.getText();
// Set content programmatically
await editor.setContentHTML('Hello
World
');
editor.setJSON(savedDocument);
// Listen for changes
editor.on('stateChange', ({ newState }) => {
save(newState.doc);
});
```
--------------------------------
### Use EventBus
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/utilities.md
Example of subscribing to and emitting events using the EventBus.
```typescript
import { EventBus } from '@notectl/core';
const bus = new EventBus();
const unsub = bus.on(SearchChanged, (payload) => {
console.log(payload.query); // type-safe
});
bus.emit(SearchChanged, { query: 'hello' });
unsub();
```
--------------------------------
### Toolbar Priority Migration Example
Source: https://github.com/samyssmile/notectl/blob/main/CHANGELOG.md
Illustrates the migration from priority-based toolbar item ordering in v1.x to registration order and layout configuration in v2.0. Use `ToolbarLayoutConfig` on the editor to control group ordering and separators.
```diff
```diff
- // v1.x — priority-based ordering
- registerToolbarItem({ id: 'bold', priority: 10, separatorAfter: true, ... });
+ // v2.0 — registration order + layout config
+ registerToolbarItem({ id: 'bold', group: 'format', ... });
```
```
--------------------------------
### Notectl Development Commands
Source: https://github.com/samyssmile/notectl/blob/main/README.md
Common commands for developing Notectl, including installation, building, testing, linting, and type checking.
```bash
pnpm install
pnpm build
pnpm test
pnpm test:e2e
pnpm lint
pnpm typecheck
```
--------------------------------
### Use Notectl from CDN
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/getting-started/installation.md
Integrate notectl directly from a CDN using a script tag for prototyping. This example demonstrates creating an editor instance and appending it to a div.
```html
```
--------------------------------
### Headless Mode Editor Setup
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/toolbar.mdx
Create an editor without a toolbar by providing plugins directly to the `plugins` array. UI elements are not rendered, and commands must be executed programmatically.
```typescript
const editor = createEditor({
plugins: [
new TextFormattingPlugin(),
new HeadingPlugin(),
new ListPlugin(),
],
});
```
--------------------------------
### Notectl Transaction System - Step Examples
Source: https://github.com/samyssmile/notectl/blob/main/CLAUDE.md
Changes in Notectl are expressed as Steps, such as `InsertTextStep`, `DeleteTextStep`, `SplitBlockStep`, etc. Each step is invertible for undo functionality. Transactions are built using a builder pattern.
```text
Changes are expressed as **Steps** (`InsertTextStep`, `DeleteTextStep`, `SplitBlockStep`, `MergeBlocksStep`, `AddMarkStep`, `RemoveMarkStep`, `SetBlockTypeStep`, etc.). Every step stores enough data to be inverted for undo. Built via `state.transaction('input').insertText(...).build()`.
```
--------------------------------
### Create CSP-Compliant Editor
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/content-security-policy.mdx
Initialize the notectl editor. This configuration works out-of-the-box for modern browsers with CSP compliance, requiring no extra setup.
```typescript
import { createEditor } from '@notectl/core';
// CSP-compliant out of the box — no extra config needed
const editor = await createEditor({
placeholder: 'Start typing...',
});
document.getElementById('editor')!.appendChild(editor);
```
--------------------------------
### Setup Plugin Tests with pluginHarness()
Source: https://github.com/samyssmile/notectl/blob/main/CLAUDE.md
Use pluginHarness to initialize a plugin test environment. Access plugin specifications, commands, and state through the returned harness object.
```typescript
import { pluginHarness, stateBuilder } from '../../test/TestUtils.js';
const state = stateBuilder().paragraph('hello', 'b1').cursor('b1', 0).build();
const h = await pluginHarness(new MyPlugin(), state);
h.getMarkSpec('bold'); // MarkSpec | undefined
h.getNodeSpec('heading'); // NodeSpec | undefined
h.getToolbarItem('bold'); // ToolbarItem | undefined
h.executeCommand('toggleBold'); // boolean
h.getKeymaps(); // KeymapEntry[]
h.getInputRules(); // InputRule[]
h.getState(); // current EditorState
h.dispatch; // vi.fn() spy
```
--------------------------------
### Initialize Editor with Plugins
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/writing-plugins.md
Demonstrates how to include the custom plugin within the editor configuration.
```ts
const editor = await createEditor({
toolbar: [
[new TextFormattingPlugin()],
[new HighlightPlugin()],
],
});
```
--------------------------------
### Initialize Full-Featured Editor
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/getting-started/quick-start.mdx
Uses createFullPreset to include all standard plugins and theme options. Requires STARTER_FONTS for the font picker to display items.
```ts
import { createEditor, ThemePreset } from '@notectl/core';
import { createFullPreset } from '@notectl/core/presets';
import { STARTER_FONTS } from '@notectl/core/fonts';
const editor = await createEditor({
...createFullPreset({
font: { fonts: STARTER_FONTS },
}),
theme: ThemePreset.Light,
placeholder: 'Start typing...',
autofocus: true,
});
document.getElementById('app').appendChild(editor);
```
```ts
const editor = await createEditor({
...createFullPreset({
font: { fonts: STARTER_FONTS },
list: { interactiveCheckboxes: true },
heading: { levels: [1, 2, 3] },
}),
theme: ThemePreset.Dark,
placeholder: 'Start typing...',
});
```
--------------------------------
### Initialize Starlight Project
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/README.md
Use this command to scaffold a new Astro project with the Starlight template.
```bash
pnpm create astro@latest -- --template starlight
```
--------------------------------
### Font Face CSS Example
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/custom-fonts.mdx
Example of CSS generated by the FontPlugin when a font has fontFaces. This is automatically cleaned up when the editor is destroyed.
```css
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-Variable.woff2') format('woff2');
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
```
--------------------------------
### Style Mode Migration Example
Source: https://github.com/samyssmile/notectl/blob/main/CHANGELOG.md
Demonstrates the change in style mode configuration, moving from an explicit `styleMode: 'inline'` option to a strict, token-based stylesheet system where strict mode is the only mode. Elements outside a registered style root fall back to inline styles.
```diff
```diff
const editor = document.createElement('notectl-editor');
- editor.config = { styleMode: 'inline' };
+ // No styleMode needed — strict mode is the only mode
editor.config = {};
```
```
--------------------------------
### Initialize editor with presets
Source: https://github.com/samyssmile/notectl/blob/main/README.md
Create and mount the editor using minimal or full presets.
```ts
import { createEditor } from '@notectl/core';
import { createMinimalPreset } from '@notectl/core/presets/minimal';
const editor = await createEditor({
...createMinimalPreset(),
placeholder: 'Start typing...',
autofocus: true,
});
document.getElementById('app')!.appendChild(editor);
```
```ts
import { ThemePreset, createEditor } from '@notectl/core';
import { STARTER_FONTS } from '@notectl/core/fonts';
import { ToolbarOverflowBehavior } from '@notectl/core/plugins/toolbar';
import { createFullPreset } from '@notectl/core/presets/full';
const preset = createFullPreset({
font: { fonts: STARTER_FONTS },
});
const editor = await createEditor({
...preset,
toolbar: {
groups: preset.toolbar,
overflow: ToolbarOverflowBehavior.Flow,
},
theme: ThemePreset.Light,
placeholder: 'Start typing...',
autofocus: true,
});
document.getElementById('app')!.appendChild(editor);
```
--------------------------------
### Image Plugin Usage
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/image.md
Demonstrates how to import and instantiate the ImagePlugin, with and without custom configuration.
```APIDOC
## Image Plugin Usage
```ts
import { ImagePlugin } from '@notectl/core/plugins/image';
// Basic usage
new ImagePlugin()
// With custom configuration
new ImagePlugin({
maxFileSize: 10 * 1024 * 1024, // 10 MB
acceptedTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp', 'image/svg+xml'],
maxWidth: 800,
resizable: true,
})
```
```
--------------------------------
### Notectl Data Structure Example
Source: https://github.com/samyssmile/notectl/blob/main/packages/core/stats.html
An example JSON structure representing the hierarchical data used by Notectl, including file names, UIDs, and nested children.
```json
const data = {"version":2,"tree":{"name":"root","children":[{"name":"notectl-core.mjs","children":[{"name":"src/index.ts","uid":"0e13f235-1"}]},{"name":"full.mjs","children":[{"name":"src/full.ts","uid":"0e13f235-3"}]},{"name":"html.mjs","children":[{"name":"src/html.ts","uid":"0e13f235-5"}]},{"name":"presets.mjs","children":[{"name":"src/presets.ts","uid":"0e13f235-7"}]},{"name":"plugins/heading.mjs","children":[{"name":"src/plugins/heading/index.ts","uid":"0e13f235-9"}]},{"name":"presets/minimal.mjs","children":[{"name":"src/presets","children":[{"uid":"0e13f235-11","name":"MinimalPreset.ts"},{"uid":"0e13f235-13","name":"minimal.ts"}]}]},{"name":"presets/full.mjs","children":[{"name":"src/presets","children":[{"uid":"0e13f235-15","name":"FullPreset.ts"},{"uid":"0e13f235-17","name":"full.ts"}]}]},{"name":"fonts.mjs","children":[{"name":"src","children":[{"name":"plugins/font/StarterFonts.ts","uid":"0e13f235-19"},{"uid":"0e13f235-21","name":"fonts.ts"}]}]},{"name":"plugins/shared.mjs","children":[{"name":"src/plugins/shared","children":[{"uid":"0e13f235-23","name":"PopupKeyboardPatterns.ts"},{"uid":"0e13f235-25","name":"index.ts"}]}]},{"name":"plugins/table.mjs","children":[{"name":"src","children":[{"name":"editor/styles/table.ts","uid":"0e13f235-27"},{"name":"plugins/table","children":[{"uid":"0e13f235-29","name":"TableHelpers.ts"},{"uid":"0e13f235-31","name":"TableLocale.ts"},{"uid":"0e13f235-33","name":"TableBorderColor.ts"},{"uid":"0e13f235-35","name":"TableCommands.ts"},{"uid":"0e13f235-37","name":"TableContextMenu.ts"},{"uid":"0e13f235-39","name":"TableNavigation.ts"},{"uid":"0e13f235-41","name":"TableControlsDOM.ts"},{"uid":"0e13f235-43","name":"TableControlsLayout.ts"},{"uid":"0e13f235-45","name":"TableControls.ts"},{"uid":"0e13f235-47","name":"TableNodeViews.ts"},{"uid":"0e13f235-49","name":"TableSelection.ts"},{"uid":"0e13f235-51","name":"TablePlugin.ts"},{"uid":"0e13f235-53","name":"index.ts"}]}]},{"name":"plugins/image.mjs","children":[{"name":"src","children":[{"name":"editor/styles/image.ts","uid":"0e13f235-55"},{"name":"plugins/image","children":[{"uid":"0e13f235-57","name":"ImageCommands.ts"},{"uid":"0e13f235-59","name":"ImageLocale.ts"},{"uid":"0e13f235-61","name":"ImageNodeView.ts"},{"uid":"0e13f235-63","name":"ImageUpload.ts"},{"uid":"0e13f235-65","name":"ImagePlugin.ts"},{"uid":"0e13f235-67","name":"index.ts"}]}]},{"name":"plugins/code-block.mjs","children":[{"name":"src","children":[{"name":"editor/styles/code-block.ts","uid":"0e13f235-69"},{"name":"plugins/code-block","children":[{"uid":"0e13f235-71","name":"CodeBlockKeyboardHandlers.ts"},{"uid":"0e13f235-73","name":"CodeBlockCommands.ts"},{"uid":"0e13f235-75","name":"CodeBlockLocale.ts"},{"uid":"0e13f235-77","name":"CodeBlockNodeView.ts"},{"uid":"0e13f235-79","name":"CodeBlockTypes.ts"},{"uid":"0e13f235-81","name":"CodeBlockService.ts"},{"uid":"0e13f235-83","name":"CodeBlockPlugin.ts"},{"uid":"0e13f235-85","name":"index.ts"}]}]},{"name":"plugins/link.mjs","children":[{"name":"src/plugins/link","children":[{"uid":"0e13f235-87","name":"LinkLocale.ts"},{"uid":"0e13f235-89","name":"LinkPlugin.ts"},{"uid":"0e13f235-91","name":"index.ts"}]}]},{"name":"plugins/list.mjs","children":[{"name":"src","children":[{"name":"editor/styles/list.ts","uid":"0e13f235-93"},{"name":"plugins/list","children":[{"uid":"0e13f235-95","name":"ListLocale.ts"},{"uid":"0e13f235-97","name":"ListPlugin.ts"},{"uid":"0e13f235-99","name":"index.ts"}]}]},{"name":"plugins/blockquote.mjs","children":[{"name":"src/plugins/blockquote","children":[{"uid":"0e13f235-101","name":"BlockquoteKeyboardHandlers.ts"},{"uid":"0e13f235-103","name":"BlockquoteLocale.ts"},{"uid":"0e13f235-105","name":"BlockquoteStyles.ts"},{"uid":"0e13f235-107","name":"BlockquotePlugin.ts"},{"uid":"0e13f235-109","name":"index.ts"}]}]},{"name":"plugins/strikethrough.mjs","children":[{"name":"src/plugins/strikethrough","children":[{"uid":"0e13f235-111","name":"StrikethroughLocale.ts"},{"uid":"0e13f235-113","n
```
--------------------------------
### Move Cursor to Block Start
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/transaction.md
Use `moveToBlockStart` to create a transaction that moves the cursor to the beginning of the current block (offset 0). Returns `null` if the cursor is already at the start or if it's a node/gap cursor.
```typescript
import { moveToBlockStart } from '@notectl/core';
const tr = moveToBlockStart(state);
```
--------------------------------
### Print Plugin Usage
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/print.md
Demonstrates how to import and initialize the PrintPlugin, with and without custom configuration.
```APIDOC
## Print Plugin Usage
### Description
Initialize the `PrintPlugin` to add print functionality to the editor. It can be used with default settings or custom configurations.
### Code Example
```ts
import { PrintPlugin } from '@notectl/core/plugins/print';
// Initialize with default configuration
new PrintPlugin()
// Initialize with custom configuration
new PrintPlugin({
defaults: { title: 'My Document', margin: '2cm' },
keyBinding: 'Mod-Shift-P',
showToolbarItem: true,
})
```
```
--------------------------------
### LinkPlugin Configuration and Usage
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/link.md
How to initialize the LinkPlugin and configure its behavior.
```APIDOC
## LinkPlugin Initialization
### Description
Initializes the LinkPlugin for the editor. Supports configuration for link behavior.
### Configuration
- **openInNewTab** (boolean) - Optional - Whether links open in a new tab (adds target="_blank"). Default: true.
- **locale** (LinkLocale) - Optional - Custom locale for toolbar labels and popup strings.
### Request Example
```ts
new LinkPlugin({ openInNewTab: true });
```
```
--------------------------------
### Custom Font Definition Example
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/font.md
Define a custom font, 'Inter', including its name, CSS family, category, and @font-face descriptors for variable font loading. This example demonstrates how to integrate custom fonts with the FontPlugin.
```ts
import { FontPlugin } from '@notectl/core/plugins/font';
import { STARTER_FONTS } from '@notectl/core/fonts';
import type { FontDefinition } from '@notectl/core/plugins/font';
const INTER: FontDefinition = {
name: 'Inter',
family: "'Inter', sans-serif",
category: 'sans-serif',
fontFaces: [
{
src: "url('/fonts/Inter-Variable.woff2') format('woff2')",
weight: '100 900',
style: 'normal',
},
],
};
new FontPlugin({
fonts: [...STARTER_FONTS, INTER],
defaultFont: 'Fira Sans',
})
```
--------------------------------
### Initialize a minimal editor
Source: https://context7.com/samyssmile/notectl/llms.txt
Sets up a lightweight editor instance with basic text formatting and navigation plugins.
```typescript
import { createEditor } from '@notectl/core';
import { createMinimalPreset } from '@notectl/core/presets/minimal';
const editor = await createEditor({
...createMinimalPreset(),
placeholder: 'Write something...',
autofocus: true,
});
document.getElementById('app')!.appendChild(editor);
```
--------------------------------
### Configure Nesting Depth
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/list.md
Example of setting a custom maximum nesting level.
```ts
new ListPlugin({ maxIndent: 8 })
```
--------------------------------
### Registering and Consuming Services
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/writing-plugins.md
Demonstrates how to expose typed services for other plugins to consume and how to register them.
```APIDOC
## Services
Expose typed services for other plugins:
```ts
import { ServiceKey } from '@notectl/core';
interface MyService {
doSomething(): void;
}
const MyServiceKey = new ServiceKey('my-service');
// Register
context.registerService(MyServiceKey, {
doSomething() { /* ... */ },
});
// Consume (from another plugin)
const service = context.getService(MyServiceKey);
service?.doSomething();
```
```
--------------------------------
### Configure List Types
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/list.md
Example of restricting the plugin to only bullet and ordered lists.
```ts
new ListPlugin({ types: ['bullet', 'ordered'] })
```
--------------------------------
### Variable Fonts Configuration
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/custom-fonts.mdx
Example of configuring variable fonts with weight ranges.
```APIDOC
## Variable Fonts
### Description
This section illustrates how to define variable fonts using weight ranges in the `fontFaces` descriptor.
### Method
Configuration Example
### Endpoint
N/A
### Parameters
#### Request Body
- **name** (string) - Required - Display name of the font.
- **family** (string) - Required - CSS `font-family` value.
- **category** (string) - Optional - Font category.
- **fontFaces** (FontFaceDescriptor[]) - Required - Array of `FontFaceDescriptor` objects, including weight ranges for variable fonts.
### Request Example
```typescript
const INTER_FONT = {
name: 'Inter',
family: "'Inter', sans-serif",
category: 'sans-serif',
fontFaces: [
{
src: "url('/fonts/Inter-Variable.woff2') format('woff2')",
weight: '100 900', // Variable weight range
style: 'normal',
},
{
src: "url('/fonts/Inter-Italic-Variable.woff2') format('woff2')",
weight: '100 900',
style: 'italic',
},
],
};
```
### Response
N/A
```
--------------------------------
### CodeBlockPlugin Usage
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/code-block.md
Demonstrates how to import and instantiate the CodeBlockPlugin with default and custom theme configurations.
```APIDOC
## CodeBlockPlugin Usage
### Description
Instantiate the `CodeBlockPlugin` to enable fenced code blocks with syntax highlighting, a copy button, and customizable theming.
### Method
```ts
new CodeBlockPlugin(config?: CodeBlockConfig)
```
### Parameters
#### Request Body
- **config** (CodeBlockConfig) - Optional - Configuration object for the plugin.
### Request Example
```ts
// Default (dark theme)
import { CodeBlockPlugin } from '@notectl/core/plugins/code-block';
new CodeBlockPlugin()
// Light theme
new CodeBlockPlugin({
background: '#f8f9fa',
headerBackground: '#e9ecef',
textColor: '#212529',
headerColor: '#868e96',
})
```
```
--------------------------------
### Block Boundary Movement
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/transaction.md
State-level functions for moving the cursor to the start or end of a block.
```APIDOC
## Block Boundary Movement
State-level functions for moving the cursor to block boundaries.
### `moveToBlockStart(state)`
Moves the cursor to offset 0 of the current block. Returns `null` if already at the start or if the selection is a node/gap cursor:
```ts
import { moveToBlockStart } from '@notectl/core';
const tr = moveToBlockStart(state);
```
### `moveToBlockEnd(state)`
Moves the cursor to the end of the current block. Returns `null` if already at the end:
```ts
import { moveToBlockEnd } from '@notectl/core';
const tr = moveToBlockEnd(state);
```
```
--------------------------------
### Use Starter Fonts
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/custom-fonts.mdx
Utilize pre-bundled fonts that do not require external file hosting.
```ts
import { STARTER_FONTS, FIRA_CODE, FIRA_SANS } from '@notectl/core/fonts';
// Use both starter fonts
new FontPlugin({ fonts: [...STARTER_FONTS] })
// Or pick individually
new FontPlugin({ fonts: [FIRA_SANS, FIRA_CODE] })
```
--------------------------------
### GET /services/TableSelectionService
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/table.md
Access the TableSelectionService programmatically to manage multi-cell selections within the editor.
```APIDOC
## GET /services/TableSelectionService
### Description
Access the selection service programmatically to interact with table cell selections.
### Method
GET
### Request Example
```ts
import { TableSelectionServiceKey } from '@notectl/core/plugins/table';
const service = editor.getService(TableSelectionServiceKey);
```
```
--------------------------------
### Calculate Next Grapheme Size
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/utilities.md
Calculate the size of the grapheme cluster starting at a given offset.
```typescript
nextGraphemeSize('Hello', 0); // 1 (H)
nextGraphemeSize('👨👩👧', 0); // 8 (family emoji)
```
--------------------------------
### Blockquote Plugin Usage
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/blockquote.md
Demonstrates how to import and instantiate the BlockquotePlugin.
```APIDOC
## Usage
```ts
import { BlockquotePlugin } from '@notectl/core/plugins/blockquote';
new BlockquotePlugin()
```
```
--------------------------------
### Toolbar Runtime API
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/toolbar.md
Runtime methods for interacting with the ToolbarPlugin, such as getting and setting overflow behavior.
```APIDOC
## Runtime API
### `getOverflowBehavior(): ToolbarOverflowBehavior`
Returns the current overflow behavior mode.
### `setOverflowBehavior(behavior: ToolbarOverflowBehavior): void`
Switches the overflow behavior at runtime. Triggers an immediate re-layout.
```ts
import { ToolbarOverflowBehavior } from '@notectl/core/plugins/toolbar';
// Switch to flow mode at runtime
toolbarPlugin.setOverflowBehavior(ToolbarOverflowBehavior.Flow);
```
```
--------------------------------
### Get Plain Text Content
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/content.md
Retrieve the content as plain text, with blocks joined by newlines.
```typescript
const text = editor.getText();
// "Hello World\nSome text here."
```
--------------------------------
### Initialize SuperSubPlugin
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/super-sub.md
Instantiate the plugin with default settings or specific mark configurations.
```ts
import { SuperSubPlugin } from '@notectl/core/plugins/super-sub';
new SuperSubPlugin()
// or enable only one:
new SuperSubPlugin({ superscript: true, subscript: false })
```
--------------------------------
### Access Popup Service
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/popup-framework.md
Import and access the PopupServiceKey to get the popup service instance from the context.
```typescript
import { PopupServiceKey } from '@notectl/core/plugins/shared';
const popups = context.getService(PopupServiceKey);
```
--------------------------------
### Initialize a full-featured editor
Source: https://context7.com/samyssmile/notectl/llms.txt
Creates an editor instance using the full preset with custom font and list configurations.
```typescript
import { createEditor, ThemePreset } from '@notectl/core';
import { createFullPreset } from '@notectl/core/presets/full';
import { STARTER_FONTS } from '@notectl/core/fonts';
import { ToolbarOverflowBehavior } from '@notectl/core/plugins/toolbar';
// Full-featured editor with all plugins
const preset = createFullPreset({
font: { fonts: STARTER_FONTS },
list: { interactiveCheckboxes: true },
});
const editor = await createEditor({
...preset,
toolbar: {
groups: preset.toolbar,
overflow: ToolbarOverflowBehavior.Flow,
},
theme: ThemePreset.Light,
placeholder: 'Start typing...',
autofocus: true,
locale: Locale.EN,
});
document.getElementById('app')!.appendChild(editor);
```
--------------------------------
### Use Type Guards
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/utilities.md
Example of narrowing a block node to a specific type to access attributes safely.
```typescript
if (isNodeOfType(block, 'heading')) {
block.attrs.level; // number — type-safe
}
```
--------------------------------
### Retrieve Editor Content as HTML
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/editor.md
Examples of generating HTML output with different formatting and CSS modes.
```typescript
// Default — returns inline-styled HTML string
const html = await editor.getContentHTML();
// Pretty-printed — returns indented HTML string
const pretty = await editor.getContentHTML({ pretty: true });
// Class-based CSS mode — returns { html, css, styleMap } object
const { html, css } = await editor.getContentHTML({ cssMode: 'classes' });
const { html, css } = await editor.getContentHTML({ cssMode: 'classes', pretty: true });
```
--------------------------------
### Initialize and Use LocaleService
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/utilities.md
Demonstrates initializing the locale service and accessing it within a plugin context.
```ts
import { LocaleService, LocaleServiceKey } from '@notectl/core';
const service = new LocaleService('en');
service.getLocale(); // 'en'
```
```ts
const locale = context.getService(LocaleServiceKey);
if (locale) {
const lang = locale.getLocale();
}
```
--------------------------------
### Get Pretty HTML Content
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/content.md
Generate indented, human-readable HTML output by passing the `pretty` option.
```typescript
const pretty = await editor.getContentHTML({ pretty: true });
```
--------------------------------
### Initialize KeymapRegistry
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/input.md
Create a new instance of the KeymapRegistry.
```typescript
import { KeymapRegistry } from '@notectl/core';
const registry = new KeymapRegistry();
```
--------------------------------
### Content API
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/editor.md
APIs for getting and setting editor content, including options for CSS class-based serialization.
```APIDOC
## GET /api/content/html
### Description
Retrieves the editor's content as HTML. Supports `cssMode` option for class-based serialization.
### Method
GET
### Endpoint
/api/content/html
### Query Parameters
- **cssMode** (string) - Optional - If set to 'classes', dynamic marks are serialized as CSS class names.
### Response
#### Success Response (200)
- **html** (string) - The editor content in HTML format.
- **css** (string) - CSS rules generated when `cssMode` is 'classes'.
### Response Example
```json
{
"html": "Hello
",
"css": ".notectl-s0 { color: #ff0000; }\n.notectl-align-center { text-align: center; }"
}
```
## POST /api/content/html
### Description
Parses HTML and sets it as the editor content.
### Method
POST
### Endpoint
/api/content/html
### Request Body
- **html** (string) - Required - The HTML content to set.
- **options** (object) - Optional - Options for setting content.
- **cssMode** (string) - Optional - If set to 'classes', dynamic marks are serialized as CSS class names.
### Response
#### Success Response (200)
- void
## GET /api/content/text
### Description
Returns plain text content of the editor, with blocks joined by newline characters.
### Method
GET
### Endpoint
/api/content/text
### Response
#### Success Response (200)
- **text** (string) - The plain text content.
### Response Example
```json
{
"text": "Hello\nWorld"
}
```
## GET /api/content/isEmpty
### Description
Returns `true` if the editor contains only a single empty paragraph, `false` otherwise.
### Method
GET
### Endpoint
/api/content/isEmpty
### Response
#### Success Response (200)
- **isEmpty** (boolean) - `true` if the editor is empty, `false` otherwise.
### Response Example
```json
{
"isEmpty": true
}
```
```
--------------------------------
### Import Starter Fonts
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/font.md
Import built-in starter fonts like Fira Code and Fira Sans from the '@notectl/core/fonts' module. These fonts come with embedded WOFF2 data, eliminating the need for external URLs.
```ts
import { STARTER_FONTS, FIRA_CODE, FIRA_SANS } from '@notectl/core/fonts';
```
--------------------------------
### PopupHandle Interface
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/popup-framework.md
Represents a handle to an open popup, providing methods to close it or get its DOM element.
```typescript
interface PopupHandle {
/** Closes this popup and any child popups. */
close(options?: PopupCloseOptions): void;
/** Returns the popup DOM element. */
getElement(): HTMLElement;
}
```
--------------------------------
### Create Editor with Preset Theme
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/styling.md
Initialize the editor using a predefined theme preset like Dark. Ensure the necessary imports are included.
```typescript
import { createEditor, ThemePreset } from '@notectl/core';
const editor = await createEditor({
theme: ThemePreset.Dark,
});
```
--------------------------------
### Configure Heading Plugin for H1-H3
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/heading.md
Example of initializing the HeadingPlugin to only support heading levels 1, 2, and 3.
```typescript
new HeadingPlugin({ levels: [1, 2, 3] })
```
--------------------------------
### Generate Tick Formatter
Source: https://github.com/samyssmile/notectl/blob/main/packages/core/stats.html
Creates a formatter function for scale ticks based on the start, stop, and count parameters.
```javascript
function tickFormat(start, stop, count, specifier) {
var step = tickStep(start, stop, count), precision;
specifier = formatSpecifier(specifier == null ? ",f" : specifier);
switch (specifier.type) {
case "s": {
var value = Math.max(Math.abs(start), Math.abs(stop));
if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
return formatPrefix(specifier, value);
}
case "": case "e": case "g": case "p": case "r": {
if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
break;
}
case "f": case "%": {
if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
break;
}
}
return format(specifier);
}
```
--------------------------------
### At Symbol Handling for Extended Globs
Source: https://github.com/samyssmile/notectl/blob/main/packages/core/stats.html
Identifies the '@' symbol as the start of an extended glob pattern when followed by an opening parenthesis.
```javascript
if (value === '@') {
if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
push({ type: 'at', extglob: true, value, output: '' });
co
```
--------------------------------
### Initialize NodeViewRegistry
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/view-utilities.md
Demonstrates how to create a new instance of `NodeViewRegistry` to manage custom node view factories.
```typescript
import { NodeViewRegistry } from '@notectl/core';
const registry = new NodeViewRegistry();
```
--------------------------------
### Disable Justify Alignment
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/alignment.md
Configure the AlignmentPlugin to exclude the 'justify' option, allowing only 'start', 'center', and 'end' alignments.
```typescript
new AlignmentPlugin({
alignments: ['start', 'center', 'end'],
})
```
--------------------------------
### Integrate notectl with Plain HTML
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/getting-started/quick-start.mdx
Initialize the editor directly in a standard HTML page using a module script.
```html
```
--------------------------------
### Get JSON Document Model
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/content.md
Retrieve the canonical structured tree of blocks, text nodes, and marks from the editor.
```typescript
const doc = editor.getJSON();
```
--------------------------------
### Get Human-Readable Block Type Labels
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/utilities.md
Retrieves labels for block types, useful for screen reader output.
```ts
import { getBlockTypeLabel } from '@notectl/core';
getBlockTypeLabel('paragraph'); // 'Paragraph'
getBlockTypeLabel('heading'); // 'Heading'
getBlockTypeLabel('heading', { level: 2 }); // 'Heading 2'
getBlockTypeLabel('code_block'); // 'Code Block'
getBlockTypeLabel('unknown_type'); // 'unknown_type' (fallback)
```
```ts
function getBlockTypeLabel(typeName: string, attrs?: Record): string
```
--------------------------------
### Initialize ListPlugin
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/list.md
Basic instantiation of the ListPlugin with default or custom configuration.
```ts
import { ListPlugin } from '@notectl/core/plugins/list';
new ListPlugin()
// or with custom config:
new ListPlugin({ types: ['bullet', 'ordered'], maxIndent: 3 })
```
--------------------------------
### Toolbar Plugin - Automatic Setup
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/toolbar.md
The ToolbarPlugin is automatically created and registered when the `toolbar` configuration option is used in `createEditor`.
```APIDOC
## Automatic Setup
The `ToolbarPlugin` renders the editor toolbar UI. It is **automatically created** when you use the `toolbar` configuration option — you don't need to instantiate it manually.
```ts
const editor = await createEditor({
toolbar: [
[new TextFormattingPlugin()],
[new HeadingPlugin()],
],
});
// ToolbarPlugin is automatically created and registered
```
Each inner array becomes a visual **toolbar group** separated by dividers.
```
--------------------------------
### Initialize CodeBlockPlugin
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/plugins/code-block.md
Instantiate the plugin with default settings or custom theme colors.
```ts
import { CodeBlockPlugin } from '@notectl/core/plugins/code-block';
// Default (dark theme)
new CodeBlockPlugin()
// Light theme
new CodeBlockPlugin({
background: '#f8f9fa',
headerBackground: '#e9ecef',
textColor: '#212529',
headerColor: '#868e96',
})
```
--------------------------------
### Get Pretty HTML with CSS Classes
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/guides/content.md
Combine the `pretty` option with `cssMode: 'classes'` for indented, CSP-compliant HTML output.
```typescript
const { html, css } = await editor.getContentHTML({ cssMode: 'classes', pretty: true });
```
--------------------------------
### Immutability Example
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/editor-state.md
Demonstrates the immutable nature of EditorState. Applying a transaction creates a new state instance, leaving the original unchanged.
```typescript
const state1 = editor.getState();
const tr = state1.transaction('command')
.insertText(blockId, 0, 'hello', [])
.build();
const state2 = state1.apply(tr);
// state1 is unchanged
assert(state1 !== state2);
assert(state1.doc !== state2.doc);
```
--------------------------------
### Exclamation Mark Handling
Source: https://github.com/samyssmile/notectl/blob/main/packages/core/stats.html
Handles exclamation marks for negated extended globs when followed by an opening parenthesis, or for negation at the start of the input.
```javascript
if (value === '!') {
if (opts.noextglob !== true && peek() === '(') {
if (peek(2) !== '?' || !/\!\=<:>\]/.test(peek(3))) {
extglobOpen('negate', value);
continue;
}
}
if (opts.nonegate !== true && state.index === 0) {
negate();
continue;
}
}
```
--------------------------------
### Creating an Editor
Source: https://github.com/samyssmile/notectl/blob/main/docs-site/src/content/docs/api/editor.md
Demonstrates two methods for creating an instance of the NotectlEditor: using a factory function or manual construction.
```APIDOC
## Creating an Editor
### Factory Function (Recommended)
```ts
import { createEditor } from '@notectl/core';
const editor = await createEditor({
placeholder: 'Start typing...',
autofocus: true,
});
document.body.appendChild(editor);
```
### Manual Construction
```ts
const editor = document.createElement('notectl-editor') as NotectlEditor;
document.body.appendChild(editor);
await editor.init({
placeholder: 'Start typing...'
});
```
```
--------------------------------
### Node Leaves Function
Source: https://github.com/samyssmile/notectl/blob/main/packages/core/stats.html
Returns an array of all leaf nodes in the hierarchy, starting from the current node. A leaf node is a node with no children.
```javascript
function node_leaves() {
var leaves = [];
this.eachBefore(function(node) {
if (!node.children) {
leaves.push(node);
}
});
return leaves;
}
```