### 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; } ```