@notectl/angular 1.7.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 notectl contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,207 @@
1
+ <div align="center">
2
+
3
+ <br />
4
+
5
+ # notectl
6
+
7
+ ### Drop one tag. Get a full editor.
8
+
9
+ `<notectl-editor>` — the rich text editor that works everywhere.<br />
10
+ React, Vue, Angular, Svelte, or plain HTML. Zero config, full power.
11
+
12
+ <br />
13
+
14
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
15
+ [![Web Component](https://img.shields.io/badge/Web_Component-%3Cnotectl--editor%3E-purple)](https://developer.mozilla.org/en-US/docs/Web/API/Web_components)
16
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
17
+ [![npm](https://img.shields.io/npm/v/@notectl/core)](https://www.npmjs.com/package/@notectl/core)
18
+ [![Bundle Size](https://img.shields.io/badge/gzip-~40kb-orange)](https://www.npmjs.com/package/@notectl/core)
19
+
20
+ <br />
21
+
22
+ <img src="e2e/demo.gif" alt="notectl editor demo" width="720" />
23
+
24
+ <br />
25
+
26
+ [**Try the Playground**](https://samyssmile.github.io/notectl/playground/) &nbsp;&middot;&nbsp; [Documentation](https://samyssmile.github.io/notectl/) &nbsp;&middot;&nbsp; [npm](https://www.npmjs.com/package/@notectl/core)
27
+
28
+ </div>
29
+
30
+ <br />
31
+
32
+ ## Quick Start
33
+
34
+ ```bash
35
+ npm install @notectl/core
36
+ ```
37
+
38
+ ### Preset — full editor in 5 lines
39
+
40
+ ```ts
41
+ import { createEditor, createFullPreset, ThemePreset } from '@notectl/core';
42
+
43
+ const editor = await createEditor({
44
+ ...createFullPreset(),
45
+ theme: ThemePreset.Light,
46
+ placeholder: 'Start typing...',
47
+ });
48
+
49
+ document.body.appendChild(editor);
50
+ ```
51
+
52
+ All 19 plugins, toolbar groups, and keyboard shortcuts — ready to go.
53
+
54
+ ### Custom — pick exactly what you need
55
+
56
+ ```ts
57
+ import {
58
+ createEditor,
59
+ ThemePreset,
60
+ TextFormattingPlugin,
61
+ HeadingPlugin,
62
+ ListPlugin,
63
+ LinkPlugin,
64
+ TablePlugin,
65
+ } from '@notectl/core';
66
+
67
+ const editor = await createEditor({
68
+ theme: ThemePreset.Light,
69
+ toolbar: [
70
+ [new TextFormattingPlugin({ bold: true, italic: true, underline: true })],
71
+ [new HeadingPlugin()],
72
+ [new ListPlugin()],
73
+ [new LinkPlugin(), new TablePlugin()],
74
+ ],
75
+ placeholder: 'Start typing...',
76
+ autofocus: true,
77
+ });
78
+
79
+ document.body.appendChild(editor);
80
+ ```
81
+
82
+ <br />
83
+
84
+ ## Why notectl
85
+
86
+ <table>
87
+ <tr>
88
+ <td width="50%">
89
+
90
+ **CSP-compliant — zero inline styles**
91
+
92
+ The only editor with a built-in CSP-safe rendering pipeline. All styles go through `adoptedStyleSheets` with reference-counted token management. Works with `style-src 'self'` — no `unsafe-inline` needed. ProseMirror, TipTap, Slate, Lexical, Quill all write inline styles.
93
+
94
+ </td>
95
+ <td width="50%">
96
+
97
+ **One dependency**
98
+
99
+ The entire editor — state engine, reconciler, plugin system, toolbar, undo/redo, selection sync — is built from scratch with a single production dependency (DOMPurify for HTML sanitization). Tree-shakeable plugin architecture: bundle only what you use.
100
+
101
+ </td>
102
+ </tr>
103
+ <tr>
104
+ <td>
105
+
106
+ **True Web Component**
107
+
108
+ Shadow DOM encapsulation, reactive attributes, framework-agnostic by design. One `<notectl-editor>` tag works in React, Vue, Angular, Svelte, or plain HTML. No wrappers, no adapters, no version lock-in.
109
+
110
+ </td>
111
+ <td>
112
+
113
+ **Plugin system with full lifecycle**
114
+
115
+ Dependency-resolved initialization (topological sort), per-plugin teardown tracking, type-safe inter-plugin services via `ServiceKey<T>`, priority-ordered middleware, error isolation. Plugins can't crash the editor or leak memory.
116
+
117
+ </td>
118
+ </tr>
119
+ </table>
120
+
121
+ <br />
122
+
123
+ ## Plugin Ecosystem
124
+
125
+ Every capability is a plugin. Compose exactly the editor you need.
126
+
127
+ | Plugin | What you get |
128
+ |---|---|
129
+ | **TextFormattingPlugin** | Bold, italic, underline |
130
+ | **StrikethroughPlugin** | ~~Strikethrough~~ text |
131
+ | **SuperSubPlugin** | Superscript and subscript |
132
+ | **HeadingPlugin** | H1 – H6 headings with block type picker |
133
+ | **BlockquotePlugin** | Block quotes |
134
+ | **ListPlugin** | Bullet, ordered, and checklists |
135
+ | **LinkPlugin** | Hyperlink insertion and editing |
136
+ | **TablePlugin** | Full table support with row/column controls |
137
+ | **CodeBlockPlugin** | Code blocks with syntax highlighting |
138
+ | **ImagePlugin** | Image upload, resize, and drag-and-drop |
139
+ | **TextColorPlugin** | Text color picker |
140
+ | **HighlightPlugin** | Text highlighting / background color |
141
+ | **AlignmentPlugin** | Left, center, right, justify |
142
+ | **FontPlugin** | Font family selection with custom web fonts |
143
+ | **FontSizePlugin** | Configurable font sizes |
144
+ | **HorizontalRulePlugin** | Horizontal dividers |
145
+ | **PrintPlugin** | Print editor content with configurable paper sizes |
146
+
147
+ See the [plugin documentation](https://samyssmile.github.io/notectl/plugins/overview/) for configuration and examples.
148
+
149
+ <br />
150
+
151
+ ## Built-in Features
152
+
153
+ - **Themes** — Dark and Light presets, or create fully custom themes
154
+ - **i18n** — 8 languages: English, German, Spanish, French, Chinese, Russian, Arabic, Hindi + auto-detect via `Locale.BROWSER`
155
+ - **Paper sizes** — DIN A4, DIN A5, US Letter, US Legal for WYSIWYG page layout
156
+ - **CSP-compliant** — Style delivery via `adoptedStyleSheets`, no inline styles required
157
+ - **Markdown shortcuts** — `#` → H1, `##` → H2, `-` → bullet list, `1.` → ordered list, `>` → blockquote
158
+ - **Syntax highlighting** — Pluggable highlighter for code blocks
159
+
160
+ <br />
161
+
162
+ ## Content API
163
+
164
+ Read and write content in any format:
165
+
166
+ ```ts
167
+ await editor.getContentHTML(); // export as HTML
168
+ editor.setContentHTML('<p>Hello <strong>world</strong></p>'); // import HTML
169
+ editor.getJSON(); // structured JSON
170
+ editor.setJSON(doc); // import JSON
171
+ editor.getText(); // plain text
172
+ editor.isEmpty(); // check if empty
173
+ ```
174
+
175
+ <br />
176
+
177
+ ## Works with your stack
178
+
179
+ | | Framework | How |
180
+ |---|---|---|
181
+ | **Any** | Vanilla JS, React, Vue, Svelte | `<notectl-editor>` Web Component |
182
+ | **Angular** | Angular 21+ | [`@notectl/angular`](https://www.npmjs.com/package/@notectl/angular) native integration |
183
+
184
+ See [`examples/vanillajs`](examples/vanillajs) and [`examples/angular`](examples/angular) for full working demos.
185
+
186
+ <br />
187
+
188
+ ## Contributing
189
+
190
+ ```bash
191
+ git clone https://github.com/Samyssmile/notectl.git
192
+ cd notectl && pnpm install
193
+ pnpm build # build all packages
194
+ pnpm test # run unit tests
195
+ pnpm test:e2e # run e2e tests
196
+ pnpm lint # lint
197
+ ```
198
+
199
+ <br />
200
+
201
+ <div align="center">
202
+
203
+ **[Get started](https://samyssmile.github.io/notectl/)** &nbsp;&middot;&nbsp; **[Open the playground](https://samyssmile.github.io/notectl/playground/)** &nbsp;&middot;&nbsp; **[View on npm](https://www.npmjs.com/package/@notectl/core)**
204
+
205
+ MIT License
206
+
207
+ </div>
@@ -34,12 +34,12 @@ class NotectlTestHarness {
34
34
  return result;
35
35
  }
36
36
  /** Sets HTML content and triggers change detection. */
37
- setContentHTML(html) {
38
- this.component.setContentHTML(html);
37
+ async setContentHTML(html) {
38
+ await this.component.setContentHTML(html);
39
39
  this.fixture.detectChanges();
40
40
  }
41
41
  /** Returns the current HTML content. */
42
- getContentHTML() {
42
+ async getContentHTML() {
43
43
  return this.component.getContentHTML();
44
44
  }
45
45
  /** Returns the current plain text content. */
@@ -1 +1 @@
1
- {"version":3,"file":"notectl-angular-testing.mjs","sources":["../../testing/src/test-harness.ts","../../testing/src/notectl-angular-testing.ts"],"sourcesContent":["import type { ComponentFixture } from '@angular/core/testing';\nimport type { NotectlEditorComponent } from '@notectl/angular';\n\n/**\n * Test harness for `NotectlEditorComponent` in consumer tests.\n *\n * Wraps a `ComponentFixture` and provides convenience methods\n * for common test operations.\n *\n * @example\n * ```typescript\n * const fixture = TestBed.createComponent(NotectlEditorComponent);\n * const harness = new NotectlTestHarness(fixture);\n * await harness.whenReady();\n * harness.setContentHTML('<p>Hello</p>');\n * expect(harness.getText()).toBe('Hello');\n * ```\n */\nexport class NotectlTestHarness {\n\tconstructor(private readonly fixture: ComponentFixture<NotectlEditorComponent>) {}\n\n\t/** Returns the component instance. */\n\tget component(): NotectlEditorComponent {\n\t\treturn this.fixture.componentInstance;\n\t}\n\n\t/** Waits for the editor to be ready and triggers change detection. */\n\tasync whenReady(): Promise<void> {\n\t\tawait this.component.whenReady();\n\t\tthis.fixture.detectChanges();\n\t}\n\n\t/** Executes a command and triggers change detection. */\n\texecuteCommand(name: string): boolean {\n\t\tconst result: boolean = this.component.executeCommand(name);\n\t\tthis.fixture.detectChanges();\n\t\treturn result;\n\t}\n\n\t/** Sets HTML content and triggers change detection. */\n\tsetContentHTML(html: string): void {\n\t\tthis.component.setContentHTML(html);\n\t\tthis.fixture.detectChanges();\n\t}\n\n\t/** Returns the current HTML content. */\n\tgetContentHTML(): string {\n\t\treturn this.component.getContentHTML();\n\t}\n\n\t/** Returns the current plain text content. */\n\tgetText(): string {\n\t\treturn this.component.getText();\n\t}\n\n\t/** Returns whether the editor is empty. */\n\tget isEmpty(): boolean {\n\t\treturn this.component.isEmpty();\n\t}\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;AAcG;MACU,kBAAkB,CAAA;AACD,IAAA,OAAA;AAA7B,IAAA,WAAA,CAA6B,OAAiD,EAAA;QAAjD,IAAA,CAAA,OAAO,GAAP,OAAO;IAA6C;;AAGjF,IAAA,IAAI,SAAS,GAAA;AACZ,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,iBAAiB;IACtC;;AAGA,IAAA,MAAM,SAAS,GAAA;AACd,QAAA,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE;AAChC,QAAA,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE;IAC7B;;AAGA,IAAA,cAAc,CAAC,IAAY,EAAA;QAC1B,MAAM,MAAM,GAAY,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC;AAC3D,QAAA,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE;AAC5B,QAAA,OAAO,MAAM;IACd;;AAGA,IAAA,cAAc,CAAC,IAAY,EAAA;AAC1B,QAAA,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC;AACnC,QAAA,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE;IAC7B;;IAGA,cAAc,GAAA;AACb,QAAA,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE;IACvC;;IAGA,OAAO,GAAA;AACN,QAAA,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE;IAChC;;AAGA,IAAA,IAAI,OAAO,GAAA;AACV,QAAA,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE;IAChC;AACA;;AC3DD;;AAEG;;;;"}
1
+ {"version":3,"file":"notectl-angular-testing.mjs","sources":["../../testing/src/test-harness.ts","../../testing/src/notectl-angular-testing.ts"],"sourcesContent":["import type { ComponentFixture } from '@angular/core/testing';\nimport type { NotectlEditorComponent } from '@notectl/angular';\n\n/**\n * Test harness for `NotectlEditorComponent` in consumer tests.\n *\n * Wraps a `ComponentFixture` and provides convenience methods\n * for common test operations.\n *\n * @example\n * ```typescript\n * const fixture = TestBed.createComponent(NotectlEditorComponent);\n * const harness = new NotectlTestHarness(fixture);\n * await harness.whenReady();\n * harness.setContentHTML('<p>Hello</p>');\n * expect(harness.getText()).toBe('Hello');\n * ```\n */\nexport class NotectlTestHarness {\n\tconstructor(private readonly fixture: ComponentFixture<NotectlEditorComponent>) {}\n\n\t/** Returns the component instance. */\n\tget component(): NotectlEditorComponent {\n\t\treturn this.fixture.componentInstance;\n\t}\n\n\t/** Waits for the editor to be ready and triggers change detection. */\n\tasync whenReady(): Promise<void> {\n\t\tawait this.component.whenReady();\n\t\tthis.fixture.detectChanges();\n\t}\n\n\t/** Executes a command and triggers change detection. */\n\texecuteCommand(name: string): boolean {\n\t\tconst result: boolean = this.component.executeCommand(name);\n\t\tthis.fixture.detectChanges();\n\t\treturn result;\n\t}\n\n\t/** Sets HTML content and triggers change detection. */\n\tasync setContentHTML(html: string): Promise<void> {\n\t\tawait this.component.setContentHTML(html);\n\t\tthis.fixture.detectChanges();\n\t}\n\n\t/** Returns the current HTML content. */\n\tasync getContentHTML(): Promise<string> {\n\t\treturn this.component.getContentHTML();\n\t}\n\n\t/** Returns the current plain text content. */\n\tgetText(): string {\n\t\treturn this.component.getText();\n\t}\n\n\t/** Returns whether the editor is empty. */\n\tget isEmpty(): boolean {\n\t\treturn this.component.isEmpty();\n\t}\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;AAcG;MACU,kBAAkB,CAAA;AACD,IAAA,OAAA;AAA7B,IAAA,WAAA,CAA6B,OAAiD,EAAA;QAAjD,IAAA,CAAA,OAAO,GAAP,OAAO;IAA6C;;AAGjF,IAAA,IAAI,SAAS,GAAA;AACZ,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,iBAAiB;IACtC;;AAGA,IAAA,MAAM,SAAS,GAAA;AACd,QAAA,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE;AAChC,QAAA,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE;IAC7B;;AAGA,IAAA,cAAc,CAAC,IAAY,EAAA;QAC1B,MAAM,MAAM,GAAY,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC;AAC3D,QAAA,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE;AAC5B,QAAA,OAAO,MAAM;IACd;;IAGA,MAAM,cAAc,CAAC,IAAY,EAAA;QAChC,MAAM,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC;AACzC,QAAA,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE;IAC7B;;AAGA,IAAA,MAAM,cAAc,GAAA;AACnB,QAAA,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE;IACvC;;IAGA,OAAO,GAAA;AACN,QAAA,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE;IAChC;;AAGA,IAAA,IAAI,OAAO,GAAA;AACV,QAAA,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE;IAChC;AACA;;AC3DD;;AAEG;;;;"}
@@ -1,10 +1,29 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { InjectionToken, makeEnvironmentProviders, inject, DestroyRef, input, model, output, signal, computed, viewChild, afterNextRender, effect, ChangeDetectionStrategy, Component, forwardRef, Directive, Injectable } from '@angular/core';
3
3
  import { ThemePreset, NotectlEditor } from '@notectl/core';
4
- export { AlignmentPlugin, BlockquotePlugin, CodeBlockPlugin, DARK_THEME, FontPlugin, FontSizePlugin, HardBreakPlugin, HeadingPlugin, HighlightPlugin, HorizontalRulePlugin, ImagePlugin, LIGHT_THEME, LinkPlugin, ListPlugin, StrikethroughPlugin, SuperSubPlugin, TablePlugin, TextColorPlugin, TextFormattingPlugin, ThemePreset, ToolbarPlugin, createTheme } from '@notectl/core';
4
+ export { DARK_THEME, LIGHT_THEME, ThemePreset, createTheme } from '@notectl/core';
5
5
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
6
6
  import { Subject } from 'rxjs';
7
7
  export { STARTER_FONTS } from '@notectl/core/fonts';
8
+ export { TextFormattingPlugin } from '@notectl/core/plugins/text-formatting';
9
+ export { HeadingPlugin } from '@notectl/core/plugins/heading';
10
+ export { ListPlugin } from '@notectl/core/plugins/list';
11
+ export { LinkPlugin } from '@notectl/core/plugins/link';
12
+ export { BlockquotePlugin } from '@notectl/core/plugins/blockquote';
13
+ export { CodeBlockPlugin } from '@notectl/core/plugins/code-block';
14
+ export { TablePlugin } from '@notectl/core/plugins/table';
15
+ export { ImagePlugin } from '@notectl/core/plugins/image';
16
+ export { HorizontalRulePlugin } from '@notectl/core/plugins/horizontal-rule';
17
+ export { HardBreakPlugin } from '@notectl/core/plugins/hard-break';
18
+ export { StrikethroughPlugin } from '@notectl/core/plugins/strikethrough';
19
+ export { HighlightPlugin } from '@notectl/core/plugins/highlight';
20
+ export { TextColorPlugin } from '@notectl/core/plugins/text-color';
21
+ export { FontPlugin } from '@notectl/core/plugins/font';
22
+ export { FontSizePlugin } from '@notectl/core/plugins/font-size';
23
+ export { AlignmentPlugin } from '@notectl/core/plugins/alignment';
24
+ export { TextDirectionPlugin } from '@notectl/core/plugins/text-direction';
25
+ export { SuperSubPlugin } from '@notectl/core/plugins/super-sub';
26
+ export { ToolbarPlugin } from '@notectl/core/plugins/toolbar';
8
27
 
9
28
  /**
10
29
  * Default configuration applied to all `<notectl-editor>` instances within
@@ -168,12 +187,12 @@ class NotectlEditorComponent {
168
187
  this.requireEditor().setJSON(doc);
169
188
  }
170
189
  /** Returns sanitized HTML representation of the document. */
171
- getContentHTML() {
190
+ async getContentHTML() {
172
191
  return this.requireEditor().getContentHTML();
173
192
  }
174
193
  /** Sets content from HTML (sanitized). */
175
- setContentHTML(html) {
176
- this.requireEditor().setContentHTML(html);
194
+ async setContentHTML(html) {
195
+ return this.requireEditor().setContentHTML(html);
177
196
  }
178
197
  /** Returns plain text content. */
179
198
  getText() {
@@ -345,8 +364,9 @@ class NotectlValueAccessorDirective {
345
364
  stateChangeSub = this.editor.stateChange.subscribe((_event) => {
346
365
  if (this.suppressEmit)
347
366
  return;
348
- const value = this.readValue();
349
- this.onChange(value);
367
+ void this.readValue().then((value) => {
368
+ this.onChange(value);
369
+ });
350
370
  });
351
371
  blurSub = this.editor.editorBlur.subscribe(() => {
352
372
  this.onTouched();
@@ -362,16 +382,16 @@ class NotectlValueAccessorDirective {
362
382
  return;
363
383
  try {
364
384
  this.editor.getState();
365
- this.writeValueToEditor(value);
385
+ void this.writeValueToEditor(value);
366
386
  return;
367
387
  }
368
388
  catch {
369
389
  // Editor not ready yet — defer
370
390
  }
371
391
  this.pendingValue = value;
372
- this.editor.whenReady().then(() => {
392
+ void this.editor.whenReady().then(async () => {
373
393
  if (this.pendingValue !== null) {
374
- this.writeValueToEditor(this.pendingValue);
394
+ await this.writeValueToEditor(this.pendingValue);
375
395
  this.pendingValue = null;
376
396
  }
377
397
  });
@@ -385,20 +405,20 @@ class NotectlValueAccessorDirective {
385
405
  setDisabledState(isDisabled) {
386
406
  this.editor.setReadonly(isDisabled);
387
407
  }
388
- writeValueToEditor(value) {
408
+ async writeValueToEditor(value) {
389
409
  this.suppressEmit = true;
390
410
  try {
391
411
  if (this.format === 'json' && typeof value === 'object') {
392
412
  this.editor.setJSON(value);
393
413
  }
394
414
  else if (this.format === 'html' && typeof value === 'string') {
395
- this.editor.setContentHTML(value);
415
+ await this.editor.setContentHTML(value);
396
416
  }
397
417
  else if (this.format === 'text' && typeof value === 'string') {
398
- this.editor.setContentHTML(`<p>${escapeHtml(value)}</p>`);
418
+ await this.editor.setContentHTML(`<p>${escapeHtml(value)}</p>`);
399
419
  }
400
420
  else if (typeof value === 'string') {
401
- this.editor.setContentHTML(value);
421
+ await this.editor.setContentHTML(value);
402
422
  }
403
423
  else {
404
424
  this.editor.setJSON(value);
@@ -408,11 +428,11 @@ class NotectlValueAccessorDirective {
408
428
  this.suppressEmit = false;
409
429
  }
410
430
  }
411
- readValue() {
431
+ async readValue() {
412
432
  try {
413
433
  switch (this.format) {
414
434
  case 'html':
415
- return this.editor.getContentHTML();
435
+ return await this.editor.getContentHTML();
416
436
  case 'text':
417
437
  return this.editor.getText();
418
438
  default:
@@ -1 +1 @@
1
- {"version":3,"file":"notectl-angular.mjs","sources":["../../src/lib/tokens.ts","../../src/lib/notectl-editor.component.ts","../../src/lib/value-accessor.directive.ts","../../src/lib/notectl-editor.service.ts","../../src/public-api.ts","../../src/notectl-angular.ts"],"sourcesContent":["import { type EnvironmentProviders, InjectionToken, makeEnvironmentProviders } from '@angular/core';\nimport type { NotectlEditorConfig } from '@notectl/core';\n\n/**\n * Default configuration applied to all `<notectl-editor>` instances within\n * the injector scope. Component-level inputs override these defaults.\n */\nexport const NOTECTL_DEFAULT_CONFIG: InjectionToken<Partial<NotectlEditorConfig>> =\n\tnew InjectionToken<Partial<NotectlEditorConfig>>('notectl-default-config');\n\n/**\n * Content format used by the `ControlValueAccessor` for forms integration.\n * Determines how editor content is serialized when reading/writing form values.\n *\n * - `'json'` — `Document` objects (default)\n * - `'html'` — sanitized HTML strings\n * - `'text'` — plain text strings\n */\nexport const NOTECTL_CONTENT_FORMAT: InjectionToken<ContentFormat> =\n\tnew InjectionToken<ContentFormat>('notectl-content-format');\n\n/** Supported content serialization formats for forms integration. */\nexport type ContentFormat = 'json' | 'html' | 'text';\n\n/** Options for `provideNotectl()`. */\nexport interface NotectlProviderOptions {\n\t/** Default editor configuration applied to all instances. */\n\treadonly config?: Partial<NotectlEditorConfig>;\n\t/** Content serialization format for forms integration. Defaults to `'json'`. */\n\treadonly contentFormat?: ContentFormat;\n}\n\n/**\n * Configures the notectl editor at the environment injector level.\n *\n * @example\n * ```typescript\n * bootstrapApplication(App, {\n * providers: [\n * provideNotectl({\n * config: { theme: ThemePreset.Light, placeholder: 'Start typing...' },\n * contentFormat: 'json',\n * }),\n * ],\n * });\n * ```\n */\nexport function provideNotectl(options: NotectlProviderOptions = {}): EnvironmentProviders {\n\tconst providers = [];\n\n\tif (options.config) {\n\t\tproviders.push({ provide: NOTECTL_DEFAULT_CONFIG, useValue: options.config });\n\t}\n\n\tif (options.contentFormat) {\n\t\tproviders.push({ provide: NOTECTL_CONTENT_FORMAT, useValue: options.contentFormat });\n\t}\n\n\treturn makeEnvironmentProviders(providers);\n}\n","import {\n\tChangeDetectionStrategy,\n\tComponent,\n\tDestroyRef,\n\ttype ElementRef,\n\ttype ModelSignal,\n\tafterNextRender,\n\tcomputed,\n\teffect,\n\tinject,\n\tinput,\n\tmodel,\n\toutput,\n\tsignal,\n\tviewChild,\n} from '@angular/core';\nimport type {\n\tDocument,\n\tEditorSelection,\n\tEditorState,\n\tNotectlEditorConfig,\n\tPlugin,\n\tPluginConfig,\n\tStateChangeEvent,\n\tTextFormattingConfig,\n\tTheme,\n\tTransaction,\n} from '@notectl/core';\nimport { NotectlEditor, ThemePreset } from '@notectl/core';\n\nimport { NOTECTL_DEFAULT_CONFIG } from './tokens';\nimport type { SelectionChangeEvent } from './types';\n\n/**\n * Angular standalone component wrapping the `<ntl-editor>` Web Component.\n *\n * Uses `afterNextRender()` for SSR-safe initialization and `effect()` for\n * reactive input tracking — no lifecycle interfaces needed.\n *\n * @example\n * ```html\n * <!-- Basic usage -->\n * <ntl-editor [toolbar]=\"toolbar\" [plugins]=\"plugins\" />\n *\n * <!-- Two-way content binding -->\n * <ntl-editor [(content)]=\"myDocument\" [toolbar]=\"toolbar\" />\n *\n * <!-- Reactive forms -->\n * <ntl-editor [formControl]=\"editorControl\" [toolbar]=\"toolbar\" />\n * ```\n */\n@Component({\n\tselector: 'ntl-editor',\n\ttemplate: '<div #host></div>',\n\tstyles: ':host { display: block; }',\n\tchangeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class NotectlEditorComponent {\n\t// --- Injected dependencies ---\n\n\tprivate readonly destroyRef: DestroyRef = inject(DestroyRef);\n\tprivate readonly defaultConfig: Partial<NotectlEditorConfig> | null = inject(\n\t\tNOTECTL_DEFAULT_CONFIG,\n\t\t{ optional: true },\n\t);\n\n\t// --- Signal Inputs (1:1 with Web Component config) ---\n\n\treadonly plugins = input<Plugin[]>([]);\n\treadonly toolbar = input<ReadonlyArray<ReadonlyArray<Plugin>>>();\n\treadonly features = input<Partial<TextFormattingConfig>>();\n\treadonly placeholder = input<string>('Start typing...');\n\treadonly readonlyMode = input<boolean>(false);\n\treadonly autofocus = input<boolean>(false);\n\treadonly maxHistoryDepth = input<number>();\n\treadonly theme = input<ThemePreset | Theme>(ThemePreset.Light);\n\n\t// --- Two-way content binding via model() ---\n\n\t/**\n\t * Two-way bindable document content.\n\t *\n\t * `undefined` means no external content was provided — the editor manages\n\t * its own state. Once the editor emits changes, the model updates to the\n\t * current `Document`.\n\t *\n\t * @example\n\t * ```html\n\t * <ntl-editor [(content)]=\"myDocument\" />\n\t * ```\n\t */\n\treadonly content: ModelSignal<Document | undefined> = model<Document>();\n\n\t// --- Signal Outputs (events bridged from Web Component) ---\n\n\treadonly stateChange = output<StateChangeEvent>();\n\treadonly selectionChange = output<SelectionChangeEvent>();\n\treadonly editorFocus = output<void>();\n\treadonly editorBlur = output<void>();\n\treadonly ready = output<void>();\n\n\t// --- Reactive State ---\n\n\treadonly editorState = signal<EditorState | null>(null);\n\n\treadonly isEmpty = computed<boolean>(() => {\n\t\tconst state: EditorState | null = this.editorState();\n\t\tif (!state) return true;\n\t\tconst doc: Document = state.doc;\n\t\tif (doc.children.length === 0) return true;\n\t\tif (doc.children.length > 1) return false;\n\t\tconst block = doc.children[0];\n\t\tif (!block) return true;\n\t\treturn block.type === 'paragraph' && block.children.length === 0;\n\t});\n\n\t// --- Internal state ---\n\n\tprivate readonly hostRef = viewChild.required<ElementRef<HTMLDivElement>>('host');\n\tprivate editorRef: NotectlEditor | null = null;\n\tprivate readyResolve: (() => void) | null = null;\n\tprivate readonly readyPromise: Promise<void> = new Promise<void>((resolve) => {\n\t\tthis.readyResolve = resolve;\n\t});\n\tprivate readonly initialized = signal(false);\n\n\t/** Tracks the last document set from within the editor to prevent feedback loops. */\n\tprivate lastEditorDoc: Document | null = null;\n\n\tconstructor() {\n\t\t// SSR-safe: only runs in the browser after first render\n\t\tafterNextRender(() => {\n\t\t\tthis.initEditor();\n\t\t});\n\n\t\t// React to input changes via effect() — replaces ngOnChanges\n\t\teffect(() => {\n\t\t\tconst currentTheme: ThemePreset | Theme = this.theme();\n\t\t\tconst currentPlaceholder: string = this.placeholder();\n\t\t\tconst currentReadonly: boolean = this.readonlyMode();\n\n\t\t\tconst editor: NotectlEditor | null = this.editorRef;\n\t\t\tif (!this.initialized() || !editor) return;\n\n\t\t\teditor.setTheme(currentTheme);\n\t\t\teditor.configure({\n\t\t\t\tplaceholder: currentPlaceholder,\n\t\t\t\treadonly: currentReadonly,\n\t\t\t});\n\t\t});\n\n\t\t// Sync external content model changes into the editor.\n\t\t// Skips when the document was set by the editor itself (feedback loop prevention).\n\t\teffect(() => {\n\t\t\tconst doc: Document | undefined = this.content();\n\t\t\tconst editor: NotectlEditor | null = this.editorRef;\n\t\t\tif (!this.initialized() || !editor || !doc) return;\n\n\t\t\t// Skip if this document originated from the editor's own state change\n\t\t\tif (doc === this.lastEditorDoc) return;\n\n\t\t\teditor.setJSON(doc);\n\t\t});\n\n\t\tthis.destroyRef.onDestroy(() => {\n\t\t\tthis.destroyEditor();\n\t\t});\n\t}\n\n\t// --- Public API (proxy to Web Component) ---\n\n\t/** Returns the document as JSON. */\n\tgetJSON(): Document {\n\t\treturn this.requireEditor().getJSON();\n\t}\n\n\t/** Sets the document from JSON. */\n\tsetJSON(doc: Document): void {\n\t\tthis.requireEditor().setJSON(doc);\n\t}\n\n\t/** Returns sanitized HTML representation of the document. */\n\tgetContentHTML(): string {\n\t\treturn this.requireEditor().getContentHTML();\n\t}\n\n\t/** Sets content from HTML (sanitized). */\n\tsetContentHTML(html: string): void {\n\t\tthis.requireEditor().setContentHTML(html);\n\t}\n\n\t/** Returns plain text content. */\n\tgetText(): string {\n\t\treturn this.requireEditor().getText();\n\t}\n\n\t/** Proxy to the Web Component's `commands` object. */\n\tget commands(): NotectlEditor['commands'] {\n\t\treturn this.requireEditor().commands;\n\t}\n\n\t/** Proxy to the Web Component's `can()` capability checker. */\n\tcan(): ReturnType<NotectlEditor['can']> {\n\t\treturn this.requireEditor().can();\n\t}\n\n\t/** Executes a named command registered by a plugin. */\n\texecuteCommand(name: string): boolean {\n\t\tif (!this.editorRef) return false;\n\t\treturn this.editorRef.executeCommand(name);\n\t}\n\n\t/** Configures a plugin at runtime. */\n\tconfigurePlugin(pluginId: string, config: PluginConfig): void {\n\t\tthis.editorRef?.configurePlugin(pluginId, config);\n\t}\n\n\t/** Dispatches a transaction. */\n\tdispatch(tr: Transaction): void {\n\t\tthis.editorRef?.dispatch(tr);\n\t}\n\n\t/** Returns the current editor state. */\n\tgetState(): EditorState {\n\t\treturn this.requireEditor().getState();\n\t}\n\n\t/** Changes the theme at runtime. */\n\tsetTheme(theme: ThemePreset | Theme): void {\n\t\tthis.editorRef?.setTheme(theme);\n\t}\n\n\t/** Returns the current theme setting. */\n\tgetTheme(): ThemePreset | Theme {\n\t\treturn this.editorRef?.getTheme() ?? this.theme();\n\t}\n\n\t/** Sets the readonly state programmatically (used by ControlValueAccessor). */\n\tsetReadonly(readonlyState: boolean): void {\n\t\tthis.editorRef?.configure({ readonly: readonlyState });\n\t}\n\n\t/** Resolves when the editor is ready. */\n\twhenReady(): Promise<void> {\n\t\treturn this.readyPromise;\n\t}\n\n\t// --- Private ---\n\n\tprivate initEditor(): void {\n\t\tconst hostElement: HTMLDivElement = this.hostRef().nativeElement;\n\t\tconst config: NotectlEditorConfig = this.buildConfig();\n\n\t\tconst editor: NotectlEditor = new NotectlEditor();\n\t\tthis.editorRef = editor;\n\n\t\t// Register event listeners BEFORE appending to DOM, because\n\t\t// appendChild triggers connectedCallback → init() synchronously.\n\t\teditor.on('stateChange', (event: StateChangeEvent) => {\n\t\t\tthis.editorState.set(event.newState);\n\t\t\tthis.syncContentModel(event.newState.doc);\n\t\t\tthis.stateChange.emit(event);\n\t\t});\n\n\t\teditor.on('selectionChange', (event: { selection: EditorSelection }) => {\n\t\t\tthis.selectionChange.emit(event);\n\t\t});\n\n\t\teditor.on('focus', () => {\n\t\t\tthis.editorFocus.emit();\n\t\t});\n\n\t\teditor.on('blur', () => {\n\t\t\tthis.editorBlur.emit();\n\t\t});\n\n\t\teditor.on('ready', () => {\n\t\t\tthis.initialized.set(true);\n\t\t\tconst state: EditorState = editor.getState();\n\t\t\tthis.editorState.set(state);\n\n\t\t\t// Apply initial content model if set before editor was ready\n\t\t\tconst initialContent: Document | undefined = this.content();\n\t\t\tif (initialContent) {\n\t\t\t\teditor.setJSON(initialContent);\n\t\t\t}\n\n\t\t\tthis.readyResolve?.();\n\t\t\tthis.ready.emit();\n\t\t});\n\n\t\t// Init with config BEFORE appending to DOM. appendChild triggers\n\t\t// connectedCallback which calls init() without config — by calling\n\t\t// init(config) first, the editor initializes with the full config\n\t\t// and connectedCallback's init() becomes a no-op (already initialized).\n\t\teditor.init(config);\n\t\thostElement.appendChild(editor);\n\t}\n\n\tprivate buildConfig(): NotectlEditorConfig {\n\t\tconst defaults: Partial<NotectlEditorConfig> = this.defaultConfig ?? {};\n\t\tconst toolbar: ReadonlyArray<ReadonlyArray<Plugin>> | undefined = this.toolbar();\n\t\tconst features: Partial<TextFormattingConfig> | undefined = this.features();\n\t\tconst maxHistory: number | undefined = this.maxHistoryDepth();\n\n\t\tconst config: NotectlEditorConfig = {\n\t\t\t...defaults,\n\t\t\tplugins: this.plugins(),\n\t\t\tplaceholder: this.placeholder(),\n\t\t\treadonly: this.readonlyMode(),\n\t\t\tautofocus: this.autofocus(),\n\t\t\ttheme: this.theme(),\n\t\t};\n\n\t\tif (toolbar !== undefined) {\n\t\t\tconfig.toolbar = toolbar;\n\t\t}\n\t\tif (features !== undefined) {\n\t\t\tconfig.features = features;\n\t\t}\n\t\tif (maxHistory !== undefined) {\n\t\t\tconfig.maxHistoryDepth = maxHistory;\n\t\t}\n\n\t\treturn config;\n\t}\n\n\t/** Syncs editor document changes to the content model without feedback loops. */\n\tprivate syncContentModel(doc: Document): void {\n\t\tthis.lastEditorDoc = doc;\n\t\tthis.content.set(doc);\n\t}\n\n\tprivate destroyEditor(): void {\n\t\tif (this.editorRef) {\n\t\t\tthis.editorRef.destroy();\n\t\t\tthis.editorRef = null;\n\t\t}\n\t\tthis.initialized.set(false);\n\t}\n\n\tprivate requireEditor(): NotectlEditor {\n\t\tconst editor: NotectlEditor | null = this.editorRef;\n\t\tif (!editor) {\n\t\t\tthrow new Error('NotectlEditor is not initialized. Await whenReady() first.');\n\t\t}\n\t\treturn editor;\n\t}\n}\n","import { DestroyRef, Directive, forwardRef, inject } from '@angular/core';\nimport { type ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport type { Document, StateChangeEvent } from '@notectl/core';\n\nimport { NotectlEditorComponent } from './notectl-editor.component';\nimport { type ContentFormat, NOTECTL_CONTENT_FORMAT } from './tokens';\n\ntype OnChangeFn = (value: Document | string | null) => void;\ntype OnTouchedFn = () => void;\n\n/** Escapes HTML special characters to prevent XSS when inserting plain text. */\nfunction escapeHtml(text: string): string {\n\treturn text\n\t\t.replace(/&/g, '&amp;')\n\t\t.replace(/</g, '&lt;')\n\t\t.replace(/>/g, '&gt;')\n\t\t.replace(/\"/g, '&quot;')\n\t\t.replace(/'/g, '&#39;');\n}\n\n/**\n * `ControlValueAccessor` directive for Angular forms integration.\n *\n * Supports Reactive Forms (`formControl`, `formControlName`) and\n * template-driven forms (`ngModel`).\n *\n * The content format is configurable via `provideNotectl({ contentFormat })` or\n * the `NOTECTL_CONTENT_FORMAT` injection token:\n * - `'json'` (default) — form value is a `Document` object\n * - `'html'` — form value is a sanitized HTML string\n * - `'text'` — form value is a plain text string\n */\n@Directive({\n\tselector: 'ntl-editor[formControl], ntl-editor[formControlName], ntl-editor[ngModel]',\n\tproviders: [\n\t\t{\n\t\t\tprovide: NG_VALUE_ACCESSOR,\n\t\t\tuseExisting: forwardRef(() => NotectlValueAccessorDirective),\n\t\t\tmulti: true,\n\t\t},\n\t],\n})\nexport class NotectlValueAccessorDirective implements ControlValueAccessor {\n\tprivate readonly editor: NotectlEditorComponent = inject(NotectlEditorComponent);\n\tprivate readonly destroyRef: DestroyRef = inject(DestroyRef);\n\tprivate readonly format: ContentFormat =\n\t\tinject(NOTECTL_CONTENT_FORMAT, { optional: true }) ?? 'json';\n\n\tprivate onChange: OnChangeFn = () => {};\n\tprivate onTouched: OnTouchedFn = () => {};\n\tprivate pendingValue: Document | string | null = null;\n\tprivate suppressEmit = false;\n\n\tprivate readonly stateChangeSub = this.editor.stateChange.subscribe(\n\t\t(_event: StateChangeEvent) => {\n\t\t\tif (this.suppressEmit) return;\n\t\t\tconst value: Document | string | null = this.readValue();\n\t\t\tthis.onChange(value);\n\t\t},\n\t);\n\n\tprivate readonly blurSub = this.editor.editorBlur.subscribe(() => {\n\t\tthis.onTouched();\n\t});\n\n\tconstructor() {\n\t\tthis.destroyRef.onDestroy(() => {\n\t\t\tthis.stateChangeSub.unsubscribe();\n\t\t\tthis.blurSub.unsubscribe();\n\t\t});\n\t}\n\n\twriteValue(value: Document | string | null): void {\n\t\tif (!value) return;\n\n\t\ttry {\n\t\t\tthis.editor.getState();\n\t\t\tthis.writeValueToEditor(value);\n\t\t\treturn;\n\t\t} catch {\n\t\t\t// Editor not ready yet — defer\n\t\t}\n\n\t\tthis.pendingValue = value;\n\t\tthis.editor.whenReady().then(() => {\n\t\t\tif (this.pendingValue !== null) {\n\t\t\t\tthis.writeValueToEditor(this.pendingValue);\n\t\t\t\tthis.pendingValue = null;\n\t\t\t}\n\t\t});\n\t}\n\n\tregisterOnChange(fn: OnChangeFn): void {\n\t\tthis.onChange = fn;\n\t}\n\n\tregisterOnTouched(fn: OnTouchedFn): void {\n\t\tthis.onTouched = fn;\n\t}\n\n\tsetDisabledState(isDisabled: boolean): void {\n\t\tthis.editor.setReadonly(isDisabled);\n\t}\n\n\tprivate writeValueToEditor(value: Document | string): void {\n\t\tthis.suppressEmit = true;\n\t\ttry {\n\t\t\tif (this.format === 'json' && typeof value === 'object') {\n\t\t\t\tthis.editor.setJSON(value as Document);\n\t\t\t} else if (this.format === 'html' && typeof value === 'string') {\n\t\t\t\tthis.editor.setContentHTML(value);\n\t\t\t} else if (this.format === 'text' && typeof value === 'string') {\n\t\t\t\tthis.editor.setContentHTML(`<p>${escapeHtml(value)}</p>`);\n\t\t\t} else if (typeof value === 'string') {\n\t\t\t\tthis.editor.setContentHTML(value);\n\t\t\t} else {\n\t\t\t\tthis.editor.setJSON(value as Document);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.suppressEmit = false;\n\t\t}\n\t}\n\n\tprivate readValue(): Document | string | null {\n\t\ttry {\n\t\t\tswitch (this.format) {\n\t\t\t\tcase 'html':\n\t\t\t\t\treturn this.editor.getContentHTML();\n\t\t\t\tcase 'text':\n\t\t\t\t\treturn this.editor.getText();\n\t\t\t\tdefault:\n\t\t\t\t\treturn this.editor.getJSON();\n\t\t\t}\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n}\n","import { DestroyRef, Injectable, computed, inject, signal } from '@angular/core';\nimport type { EditorState, StateChangeEvent, Transaction } from '@notectl/core';\nimport { type Observable, Subject } from 'rxjs';\n\nimport type { NotectlEditorComponent } from './notectl-editor.component';\n\n/**\n * Optional injectable service for programmatic editor access via DI.\n *\n * Useful when a toolbar or other component needs to interact with\n * the editor without a direct template reference.\n *\n * The service must be provided at a shared injector level and\n * connected to the editor component via `register()`.\n *\n * @example\n * ```typescript\n * @Component({\n * providers: [NotectlEditorService],\n * template: `\n * <app-toolbar />\n * <ntl-editor #editor [plugins]=\"plugins\" />\n * `,\n * })\n * export class EditorPage {\n * private readonly editor = viewChild.required<NotectlEditorComponent>('editor');\n * private readonly service = inject(NotectlEditorService);\n *\n * constructor() {\n * afterNextRender(() => {\n * this.service.register(this.editor());\n * });\n * }\n * }\n * ```\n */\n@Injectable()\nexport class NotectlEditorService {\n\tprivate readonly destroyRef: DestroyRef = inject(DestroyRef);\n\tprivate readonly editorRef = signal<NotectlEditorComponent | null>(null);\n\tprivate readonly stateChangeSubject = new Subject<StateChangeEvent>();\n\n\t/** Whether an editor is currently registered with this service. */\n\treadonly hasEditor = computed<boolean>(() => this.editorRef() !== null);\n\n\t/** Observable stream of state change events from the editor. */\n\treadonly stateChanges$: Observable<StateChangeEvent> = this.stateChangeSubject.asObservable();\n\n\tprivate stateChangeSub: { unsubscribe(): void } | null = null;\n\n\tconstructor() {\n\t\tthis.destroyRef.onDestroy(() => {\n\t\t\tthis.unregister();\n\t\t\tthis.stateChangeSubject.complete();\n\t\t});\n\t}\n\n\t/** Registers an editor component with this service. */\n\tregister(editor: NotectlEditorComponent): void {\n\t\tthis.unregister();\n\t\tthis.editorRef.set(editor);\n\n\t\tthis.stateChangeSub = editor.stateChange.subscribe((event: StateChangeEvent) => {\n\t\t\tthis.stateChangeSubject.next(event);\n\t\t});\n\t}\n\n\t/** Unregisters the current editor from this service. */\n\tunregister(): void {\n\t\tthis.stateChangeSub?.unsubscribe();\n\t\tthis.stateChangeSub = null;\n\t\tthis.editorRef.set(null);\n\t}\n\n\t/** Executes a named command on the registered editor. */\n\texecuteCommand(name: string): boolean {\n\t\tconst editor: NotectlEditorComponent | null = this.editorRef();\n\t\tif (!editor) return false;\n\t\treturn editor.executeCommand(name);\n\t}\n\n\t/** Returns the current editor state, or `null` if no editor is registered. */\n\tgetState(): EditorState | null {\n\t\tconst editor: NotectlEditorComponent | null = this.editorRef();\n\t\tif (!editor) return null;\n\t\ttry {\n\t\t\treturn editor.getState();\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/** Dispatches a transaction on the registered editor. */\n\tdispatch(tr: Transaction): void {\n\t\tthis.editorRef()?.dispatch(tr);\n\t}\n}\n","/**\n * @notectl/angular — Angular integration for the notectl rich text editor.\n * @packageDocumentation\n */\n\n// --- Angular Bindings ---\nexport { NotectlEditorComponent } from './lib/notectl-editor.component';\nexport { NotectlValueAccessorDirective } from './lib/value-accessor.directive';\nexport { NotectlEditorService } from './lib/notectl-editor.service';\n\n// --- Provider Function ---\nexport {\n\tprovideNotectl,\n\ttype NotectlProviderOptions,\n} from './lib/tokens';\n\n// --- Injection Tokens ---\nexport {\n\tNOTECTL_DEFAULT_CONFIG,\n\tNOTECTL_CONTENT_FORMAT,\n\ttype ContentFormat,\n} from './lib/tokens';\n\n// --- Angular-specific Types ---\nexport type { SelectionChangeEvent } from './lib/types';\n\n// --- Re-exports from @notectl/core (convenience) ---\n\n// Model types\nexport type {\n\tDocument,\n\tBlockNode,\n\tTextNode,\n\tInlineNode,\n\tMark,\n\tBlockAttrs,\n} from '@notectl/core';\n\n// Selection types\nexport type { EditorSelection, Position, Selection } from '@notectl/core';\n\n// State types\nexport type {\n\tEditorState,\n\tTransaction,\n\tTransactionMetadata,\n\tStateChangeEvent,\n} from '@notectl/core';\n\n// Plugin types\nexport type { Plugin, PluginConfig, PluginContext } from '@notectl/core';\n\n// Theme types\nexport type { Theme, PartialTheme, ThemePrimitives } from '@notectl/core';\nexport { ThemePreset, LIGHT_THEME, DARK_THEME, createTheme } from '@notectl/core';\n\n// Editor config\nexport type {\n\tNotectlEditorConfig,\n\tTextFormattingConfig,\n} from '@notectl/core';\n\n// Plugin config types\nexport type { FontDefinition } from '@notectl/core';\n\n// Starter fonts\n/** @deprecated Import from '@notectl/core/fonts' instead. */\nexport { STARTER_FONTS } from '@notectl/core/fonts';\n\n// Plugins (tree-shakable re-exports)\nexport {\n\tTextFormattingPlugin,\n\tHeadingPlugin,\n\tListPlugin,\n\tLinkPlugin,\n\tBlockquotePlugin,\n\tCodeBlockPlugin,\n\tTablePlugin,\n\tImagePlugin,\n\tHorizontalRulePlugin,\n\tHardBreakPlugin,\n\tStrikethroughPlugin,\n\tHighlightPlugin,\n\tTextColorPlugin,\n\tFontPlugin,\n\tFontSizePlugin,\n\tAlignmentPlugin,\n\tSuperSubPlugin,\n\tToolbarPlugin,\n} from '@notectl/core';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;;AAGA;;;AAGG;MACU,sBAAsB,GAClC,IAAI,cAAc,CAA+B,wBAAwB;AAE1E;;;;;;;AAOG;MACU,sBAAsB,GAClC,IAAI,cAAc,CAAgB,wBAAwB;AAa3D;;;;;;;;;;;;;;AAcG;AACG,SAAU,cAAc,CAAC,OAAA,GAAkC,EAAE,EAAA;IAClE,MAAM,SAAS,GAAG,EAAE;AAEpB,IAAA,IAAI,OAAO,CAAC,MAAM,EAAE;AACnB,QAAA,SAAS,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,sBAAsB,EAAE,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IAC9E;AAEA,IAAA,IAAI,OAAO,CAAC,aAAa,EAAE;AAC1B,QAAA,SAAS,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,sBAAsB,EAAE,QAAQ,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;IACrF;AAEA,IAAA,OAAO,wBAAwB,CAAC,SAAS,CAAC;AAC3C;;AC1BA;;;;;;;;;;;;;;;;;AAiBG;MAOU,sBAAsB,CAAA;;AAGjB,IAAA,UAAU,GAAe,MAAM,CAAC,UAAU,CAAC;IAC3C,aAAa,GAAwC,MAAM,CAC3E,sBAAsB,EACtB,EAAE,QAAQ,EAAE,IAAI,EAAE,CAClB;;AAIQ,IAAA,OAAO,GAAG,KAAK,CAAW,EAAE,mDAAC;IAC7B,OAAO,GAAG,KAAK,CAAA,IAAA,SAAA,GAAA,CAAA,SAAA,EAAA,EAAA,SAAA,EAAA,SAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAwC;IACvD,QAAQ,GAAG,KAAK,CAAA,IAAA,SAAA,GAAA,CAAA,SAAA,EAAA,EAAA,SAAA,EAAA,UAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAiC;AACjD,IAAA,WAAW,GAAG,KAAK,CAAS,iBAAiB,uDAAC;AAC9C,IAAA,YAAY,GAAG,KAAK,CAAU,KAAK,wDAAC;AACpC,IAAA,SAAS,GAAG,KAAK,CAAU,KAAK,qDAAC;IACjC,eAAe,GAAG,KAAK,CAAA,IAAA,SAAA,GAAA,CAAA,SAAA,EAAA,EAAA,SAAA,EAAA,iBAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAU;AACjC,IAAA,KAAK,GAAG,KAAK,CAAsB,WAAW,CAAC,KAAK,iDAAC;;AAI9D;;;;;;;;;;;AAWG;IACM,OAAO,GAAsC,KAAK,CAAA,IAAA,SAAA,GAAA,CAAA,SAAA,EAAA,EAAA,SAAA,EAAA,SAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAY;;IAI9D,WAAW,GAAG,MAAM,EAAoB;IACxC,eAAe,GAAG,MAAM,EAAwB;IAChD,WAAW,GAAG,MAAM,EAAQ;IAC5B,UAAU,GAAG,MAAM,EAAQ;IAC3B,KAAK,GAAG,MAAM,EAAQ;;AAItB,IAAA,WAAW,GAAG,MAAM,CAAqB,IAAI,uDAAC;AAE9C,IAAA,OAAO,GAAG,QAAQ,CAAU,MAAK;AACzC,QAAA,MAAM,KAAK,GAAuB,IAAI,CAAC,WAAW,EAAE;AACpD,QAAA,IAAI,CAAC,KAAK;AAAE,YAAA,OAAO,IAAI;AACvB,QAAA,MAAM,GAAG,GAAa,KAAK,CAAC,GAAG;AAC/B,QAAA,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;AAAE,YAAA,OAAO,IAAI;AAC1C,QAAA,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;AAAE,YAAA,OAAO,KAAK;QACzC,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC7B,QAAA,IAAI,CAAC,KAAK;AAAE,YAAA,OAAO,IAAI;AACvB,QAAA,OAAO,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;AACjE,IAAA,CAAC,mDAAC;;AAIe,IAAA,OAAO,GAAG,SAAS,CAAC,QAAQ,CAA6B,MAAM,CAAC;IACzE,SAAS,GAAyB,IAAI;IACtC,YAAY,GAAwB,IAAI;AAC/B,IAAA,YAAY,GAAkB,IAAI,OAAO,CAAO,CAAC,OAAO,KAAI;AAC5E,QAAA,IAAI,CAAC,YAAY,GAAG,OAAO;AAC5B,IAAA,CAAC,CAAC;AACe,IAAA,WAAW,GAAG,MAAM,CAAC,KAAK,uDAAC;;IAGpC,aAAa,GAAoB,IAAI;AAE7C,IAAA,WAAA,GAAA;;QAEC,eAAe,CAAC,MAAK;YACpB,IAAI,CAAC,UAAU,EAAE;AAClB,QAAA,CAAC,CAAC;;QAGF,MAAM,CAAC,MAAK;AACX,YAAA,MAAM,YAAY,GAAwB,IAAI,CAAC,KAAK,EAAE;AACtD,YAAA,MAAM,kBAAkB,GAAW,IAAI,CAAC,WAAW,EAAE;AACrD,YAAA,MAAM,eAAe,GAAY,IAAI,CAAC,YAAY,EAAE;AAEpD,YAAA,MAAM,MAAM,GAAyB,IAAI,CAAC,SAAS;AACnD,YAAA,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM;gBAAE;AAEpC,YAAA,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC;YAC7B,MAAM,CAAC,SAAS,CAAC;AAChB,gBAAA,WAAW,EAAE,kBAAkB;AAC/B,gBAAA,QAAQ,EAAE,eAAe;AACzB,aAAA,CAAC;AACH,QAAA,CAAC,CAAC;;;QAIF,MAAM,CAAC,MAAK;AACX,YAAA,MAAM,GAAG,GAAyB,IAAI,CAAC,OAAO,EAAE;AAChD,YAAA,MAAM,MAAM,GAAyB,IAAI,CAAC,SAAS;YACnD,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG;gBAAE;;AAG5C,YAAA,IAAI,GAAG,KAAK,IAAI,CAAC,aAAa;gBAAE;AAEhC,YAAA,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC;AACpB,QAAA,CAAC,CAAC;AAEF,QAAA,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,MAAK;YAC9B,IAAI,CAAC,aAAa,EAAE;AACrB,QAAA,CAAC,CAAC;IACH;;;IAKA,OAAO,GAAA;AACN,QAAA,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,EAAE;IACtC;;AAGA,IAAA,OAAO,CAAC,GAAa,EAAA;QACpB,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;IAClC;;IAGA,cAAc,GAAA;AACb,QAAA,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC,cAAc,EAAE;IAC7C;;AAGA,IAAA,cAAc,CAAC,IAAY,EAAA;QAC1B,IAAI,CAAC,aAAa,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC;IAC1C;;IAGA,OAAO,GAAA;AACN,QAAA,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,EAAE;IACtC;;AAGA,IAAA,IAAI,QAAQ,GAAA;AACX,QAAA,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC,QAAQ;IACrC;;IAGA,GAAG,GAAA;AACF,QAAA,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC,GAAG,EAAE;IAClC;;AAGA,IAAA,cAAc,CAAC,IAAY,EAAA;QAC1B,IAAI,CAAC,IAAI,CAAC,SAAS;AAAE,YAAA,OAAO,KAAK;QACjC,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC;IAC3C;;IAGA,eAAe,CAAC,QAAgB,EAAE,MAAoB,EAAA;QACrD,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC;IAClD;;AAGA,IAAA,QAAQ,CAAC,EAAe,EAAA;AACvB,QAAA,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,CAAC;IAC7B;;IAGA,QAAQ,GAAA;AACP,QAAA,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC,QAAQ,EAAE;IACvC;;AAGA,IAAA,QAAQ,CAAC,KAA0B,EAAA;AAClC,QAAA,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC;IAChC;;IAGA,QAAQ,GAAA;QACP,OAAO,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,IAAI,CAAC,KAAK,EAAE;IAClD;;AAGA,IAAA,WAAW,CAAC,aAAsB,EAAA;QACjC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;IACvD;;IAGA,SAAS,GAAA;QACR,OAAO,IAAI,CAAC,YAAY;IACzB;;IAIQ,UAAU,GAAA;QACjB,MAAM,WAAW,GAAmB,IAAI,CAAC,OAAO,EAAE,CAAC,aAAa;AAChE,QAAA,MAAM,MAAM,GAAwB,IAAI,CAAC,WAAW,EAAE;AAEtD,QAAA,MAAM,MAAM,GAAkB,IAAI,aAAa,EAAE;AACjD,QAAA,IAAI,CAAC,SAAS,GAAG,MAAM;;;QAIvB,MAAM,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC,KAAuB,KAAI;YACpD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC;YACpC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;AACzC,YAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;AAC7B,QAAA,CAAC,CAAC;QAEF,MAAM,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC,KAAqC,KAAI;AACtE,YAAA,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC;AACjC,QAAA,CAAC,CAAC;AAEF,QAAA,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAK;AACvB,YAAA,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE;AACxB,QAAA,CAAC,CAAC;AAEF,QAAA,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAK;AACtB,YAAA,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE;AACvB,QAAA,CAAC,CAAC;AAEF,QAAA,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAK;AACvB,YAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;AAC1B,YAAA,MAAM,KAAK,GAAgB,MAAM,CAAC,QAAQ,EAAE;AAC5C,YAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;;AAG3B,YAAA,MAAM,cAAc,GAAyB,IAAI,CAAC,OAAO,EAAE;YAC3D,IAAI,cAAc,EAAE;AACnB,gBAAA,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC;YAC/B;AAEA,YAAA,IAAI,CAAC,YAAY,IAAI;AACrB,YAAA,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;AAClB,QAAA,CAAC,CAAC;;;;;AAMF,QAAA,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;AACnB,QAAA,WAAW,CAAC,WAAW,CAAC,MAAM,CAAC;IAChC;IAEQ,WAAW,GAAA;AAClB,QAAA,MAAM,QAAQ,GAAiC,IAAI,CAAC,aAAa,IAAI,EAAE;AACvE,QAAA,MAAM,OAAO,GAAqD,IAAI,CAAC,OAAO,EAAE;AAChF,QAAA,MAAM,QAAQ,GAA8C,IAAI,CAAC,QAAQ,EAAE;AAC3E,QAAA,MAAM,UAAU,GAAuB,IAAI,CAAC,eAAe,EAAE;AAE7D,QAAA,MAAM,MAAM,GAAwB;AACnC,YAAA,GAAG,QAAQ;AACX,YAAA,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;AACvB,YAAA,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE;AAC/B,YAAA,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE;AAC7B,YAAA,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE;AAC3B,YAAA,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;SACnB;AAED,QAAA,IAAI,OAAO,KAAK,SAAS,EAAE;AAC1B,YAAA,MAAM,CAAC,OAAO,GAAG,OAAO;QACzB;AACA,QAAA,IAAI,QAAQ,KAAK,SAAS,EAAE;AAC3B,YAAA,MAAM,CAAC,QAAQ,GAAG,QAAQ;QAC3B;AACA,QAAA,IAAI,UAAU,KAAK,SAAS,EAAE;AAC7B,YAAA,MAAM,CAAC,eAAe,GAAG,UAAU;QACpC;AAEA,QAAA,OAAO,MAAM;IACd;;AAGQ,IAAA,gBAAgB,CAAC,GAAa,EAAA;AACrC,QAAA,IAAI,CAAC,aAAa,GAAG,GAAG;AACxB,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;IACtB;IAEQ,aAAa,GAAA;AACpB,QAAA,IAAI,IAAI,CAAC,SAAS,EAAE;AACnB,YAAA,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE;AACxB,YAAA,IAAI,CAAC,SAAS,GAAG,IAAI;QACtB;AACA,QAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;IAC5B;IAEQ,aAAa,GAAA;AACpB,QAAA,MAAM,MAAM,GAAyB,IAAI,CAAC,SAAS;QACnD,IAAI,CAAC,MAAM,EAAE;AACZ,YAAA,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC;QAC9E;AACA,QAAA,OAAO,MAAM;IACd;uGAlSY,sBAAsB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAtB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,sBAAsB,ohDAJxB,mBAAmB,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,wBAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAIjB,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBANlC,SAAS;AACC,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,YAAY,EAAA,QAAA,EACZ,mBAAmB,EAAA,eAAA,EAEZ,uBAAuB,CAAC,MAAM,EAAA,MAAA,EAAA,CAAA,wBAAA,CAAA,EAAA;gyCA+D2B,MAAM,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AC5GjF;AACA,SAAS,UAAU,CAAC,IAAY,EAAA;AAC/B,IAAA,OAAO;AACL,SAAA,OAAO,CAAC,IAAI,EAAE,OAAO;AACrB,SAAA,OAAO,CAAC,IAAI,EAAE,MAAM;AACpB,SAAA,OAAO,CAAC,IAAI,EAAE,MAAM;AACpB,SAAA,OAAO,CAAC,IAAI,EAAE,QAAQ;AACtB,SAAA,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;AACzB;AAEA;;;;;;;;;;;AAWG;MAWU,6BAA6B,CAAA;AACxB,IAAA,MAAM,GAA2B,MAAM,CAAC,sBAAsB,CAAC;AAC/D,IAAA,UAAU,GAAe,MAAM,CAAC,UAAU,CAAC;AAC3C,IAAA,MAAM,GACtB,MAAM,CAAC,sBAAsB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,IAAI,MAAM;AAErD,IAAA,QAAQ,GAAe,MAAK,EAAE,CAAC;AAC/B,IAAA,SAAS,GAAgB,MAAK,EAAE,CAAC;IACjC,YAAY,GAA6B,IAAI;IAC7C,YAAY,GAAG,KAAK;AAEX,IAAA,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,CAClE,CAAC,MAAwB,KAAI;QAC5B,IAAI,IAAI,CAAC,YAAY;YAAE;AACvB,QAAA,MAAM,KAAK,GAA6B,IAAI,CAAC,SAAS,EAAE;AACxD,QAAA,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;AACrB,IAAA,CAAC,CACD;IAEgB,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,MAAK;QAChE,IAAI,CAAC,SAAS,EAAE;AACjB,IAAA,CAAC,CAAC;AAEF,IAAA,WAAA,GAAA;AACC,QAAA,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,MAAK;AAC9B,YAAA,IAAI,CAAC,cAAc,CAAC,WAAW,EAAE;AACjC,YAAA,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE;AAC3B,QAAA,CAAC,CAAC;IACH;AAEA,IAAA,UAAU,CAAC,KAA+B,EAAA;AACzC,QAAA,IAAI,CAAC,KAAK;YAAE;AAEZ,QAAA,IAAI;AACH,YAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE;AACtB,YAAA,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC;YAC9B;QACD;AAAE,QAAA,MAAM;;QAER;AAEA,QAAA,IAAI,CAAC,YAAY,GAAG,KAAK;QACzB,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,MAAK;AACjC,YAAA,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,EAAE;AAC/B,gBAAA,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,YAAY,CAAC;AAC1C,gBAAA,IAAI,CAAC,YAAY,GAAG,IAAI;YACzB;AACD,QAAA,CAAC,CAAC;IACH;AAEA,IAAA,gBAAgB,CAAC,EAAc,EAAA;AAC9B,QAAA,IAAI,CAAC,QAAQ,GAAG,EAAE;IACnB;AAEA,IAAA,iBAAiB,CAAC,EAAe,EAAA;AAChC,QAAA,IAAI,CAAC,SAAS,GAAG,EAAE;IACpB;AAEA,IAAA,gBAAgB,CAAC,UAAmB,EAAA;AACnC,QAAA,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC;IACpC;AAEQ,IAAA,kBAAkB,CAAC,KAAwB,EAAA;AAClD,QAAA,IAAI,CAAC,YAAY,GAAG,IAAI;AACxB,QAAA,IAAI;YACH,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AACxD,gBAAA,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAiB,CAAC;YACvC;iBAAO,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AAC/D,gBAAA,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC;YAClC;iBAAO,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AAC/D,gBAAA,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAA,GAAA,EAAM,UAAU,CAAC,KAAK,CAAC,CAAA,IAAA,CAAM,CAAC;YAC1D;AAAO,iBAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AACrC,gBAAA,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC;YAClC;iBAAO;AACN,gBAAA,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAiB,CAAC;YACvC;QACD;gBAAU;AACT,YAAA,IAAI,CAAC,YAAY,GAAG,KAAK;QAC1B;IACD;IAEQ,SAAS,GAAA;AAChB,QAAA,IAAI;AACH,YAAA,QAAQ,IAAI,CAAC,MAAM;AAClB,gBAAA,KAAK,MAAM;AACV,oBAAA,OAAO,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE;AACpC,gBAAA,KAAK,MAAM;AACV,oBAAA,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;AAC7B,gBAAA;AACC,oBAAA,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;;QAE/B;AAAE,QAAA,MAAM;AACP,YAAA,OAAO,IAAI;QACZ;IACD;uGA9FY,6BAA6B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA7B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,6BAA6B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,2EAAA,EAAA,SAAA,EAR9B;AACV,YAAA;AACC,gBAAA,OAAO,EAAE,iBAAiB;AAC1B,gBAAA,WAAW,EAAE,UAAU,CAAC,MAAM,6BAA6B,CAAC;AAC5D,gBAAA,KAAK,EAAE,IAAI;AACX,aAAA;AACD,SAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAEW,6BAA6B,EAAA,UAAA,EAAA,CAAA;kBAVzC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACV,oBAAA,QAAQ,EAAE,2EAA2E;AACrF,oBAAA,SAAS,EAAE;AACV,wBAAA;AACC,4BAAA,OAAO,EAAE,iBAAiB;AAC1B,4BAAA,WAAW,EAAE,UAAU,CAAC,mCAAmC,CAAC;AAC5D,4BAAA,KAAK,EAAE,IAAI;AACX,yBAAA;AACD,qBAAA;AACD,iBAAA;;;ACnCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BG;MAEU,oBAAoB,CAAA;AACf,IAAA,UAAU,GAAe,MAAM,CAAC,UAAU,CAAC;AAC3C,IAAA,SAAS,GAAG,MAAM,CAAgC,IAAI,qDAAC;AACvD,IAAA,kBAAkB,GAAG,IAAI,OAAO,EAAoB;;AAG5D,IAAA,SAAS,GAAG,QAAQ,CAAU,MAAM,IAAI,CAAC,SAAS,EAAE,KAAK,IAAI,qDAAC;;AAG9D,IAAA,aAAa,GAAiC,IAAI,CAAC,kBAAkB,CAAC,YAAY,EAAE;IAErF,cAAc,GAAmC,IAAI;AAE7D,IAAA,WAAA,GAAA;AACC,QAAA,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,MAAK;YAC9B,IAAI,CAAC,UAAU,EAAE;AACjB,YAAA,IAAI,CAAC,kBAAkB,CAAC,QAAQ,EAAE;AACnC,QAAA,CAAC,CAAC;IACH;;AAGA,IAAA,QAAQ,CAAC,MAA8B,EAAA;QACtC,IAAI,CAAC,UAAU,EAAE;AACjB,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC;AAE1B,QAAA,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,KAAuB,KAAI;AAC9E,YAAA,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC;AACpC,QAAA,CAAC,CAAC;IACH;;IAGA,UAAU,GAAA;AACT,QAAA,IAAI,CAAC,cAAc,EAAE,WAAW,EAAE;AAClC,QAAA,IAAI,CAAC,cAAc,GAAG,IAAI;AAC1B,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;IACzB;;AAGA,IAAA,cAAc,CAAC,IAAY,EAAA;AAC1B,QAAA,MAAM,MAAM,GAAkC,IAAI,CAAC,SAAS,EAAE;AAC9D,QAAA,IAAI,CAAC,MAAM;AAAE,YAAA,OAAO,KAAK;AACzB,QAAA,OAAO,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC;IACnC;;IAGA,QAAQ,GAAA;AACP,QAAA,MAAM,MAAM,GAAkC,IAAI,CAAC,SAAS,EAAE;AAC9D,QAAA,IAAI,CAAC,MAAM;AAAE,YAAA,OAAO,IAAI;AACxB,QAAA,IAAI;AACH,YAAA,OAAO,MAAM,CAAC,QAAQ,EAAE;QACzB;AAAE,QAAA,MAAM;AACP,YAAA,OAAO,IAAI;QACZ;IACD;;AAGA,IAAA,QAAQ,CAAC,EAAe,EAAA;QACvB,IAAI,CAAC,SAAS,EAAE,EAAE,QAAQ,CAAC,EAAE,CAAC;IAC/B;uGA1DY,oBAAoB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;2GAApB,oBAAoB,EAAA,CAAA;;2FAApB,oBAAoB,EAAA,UAAA,EAAA,CAAA;kBADhC;;;ACpCD;;;AAGG;AAEH;;ACLA;;AAEG;;;;"}
1
+ {"version":3,"file":"notectl-angular.mjs","sources":["../../src/lib/tokens.ts","../../src/lib/notectl-editor.component.ts","../../src/lib/value-accessor.directive.ts","../../src/lib/notectl-editor.service.ts","../../src/public-api.ts","../../src/notectl-angular.ts"],"sourcesContent":["import { type EnvironmentProviders, InjectionToken, makeEnvironmentProviders } from '@angular/core';\nimport type { NotectlEditorConfig } from '@notectl/core';\n\n/**\n * Default configuration applied to all `<notectl-editor>` instances within\n * the injector scope. Component-level inputs override these defaults.\n */\nexport const NOTECTL_DEFAULT_CONFIG: InjectionToken<Partial<NotectlEditorConfig>> =\n\tnew InjectionToken<Partial<NotectlEditorConfig>>('notectl-default-config');\n\n/**\n * Content format used by the `ControlValueAccessor` for forms integration.\n * Determines how editor content is serialized when reading/writing form values.\n *\n * - `'json'` — `Document` objects (default)\n * - `'html'` — sanitized HTML strings\n * - `'text'` — plain text strings\n */\nexport const NOTECTL_CONTENT_FORMAT: InjectionToken<ContentFormat> =\n\tnew InjectionToken<ContentFormat>('notectl-content-format');\n\n/** Supported content serialization formats for forms integration. */\nexport type ContentFormat = 'json' | 'html' | 'text';\n\n/** Options for `provideNotectl()`. */\nexport interface NotectlProviderOptions {\n\t/** Default editor configuration applied to all instances. */\n\treadonly config?: Partial<NotectlEditorConfig>;\n\t/** Content serialization format for forms integration. Defaults to `'json'`. */\n\treadonly contentFormat?: ContentFormat;\n}\n\n/**\n * Configures the notectl editor at the environment injector level.\n *\n * @example\n * ```typescript\n * bootstrapApplication(App, {\n * providers: [\n * provideNotectl({\n * config: { theme: ThemePreset.Light, placeholder: 'Start typing...' },\n * contentFormat: 'json',\n * }),\n * ],\n * });\n * ```\n */\nexport function provideNotectl(options: NotectlProviderOptions = {}): EnvironmentProviders {\n\tconst providers = [];\n\n\tif (options.config) {\n\t\tproviders.push({ provide: NOTECTL_DEFAULT_CONFIG, useValue: options.config });\n\t}\n\n\tif (options.contentFormat) {\n\t\tproviders.push({ provide: NOTECTL_CONTENT_FORMAT, useValue: options.contentFormat });\n\t}\n\n\treturn makeEnvironmentProviders(providers);\n}\n","import {\n\tChangeDetectionStrategy,\n\tComponent,\n\tDestroyRef,\n\ttype ElementRef,\n\ttype ModelSignal,\n\tafterNextRender,\n\tcomputed,\n\teffect,\n\tinject,\n\tinput,\n\tmodel,\n\toutput,\n\tsignal,\n\tviewChild,\n} from '@angular/core';\nimport type {\n\tDocument,\n\tEditorSelection,\n\tEditorState,\n\tNotectlEditorConfig,\n\tPlugin,\n\tPluginConfig,\n\tStateChangeEvent,\n\tTheme,\n\tTransaction,\n} from '@notectl/core';\nimport { NotectlEditor, ThemePreset } from '@notectl/core';\nimport type { TextFormattingConfig } from '@notectl/core/plugins/text-formatting';\n\nimport { NOTECTL_DEFAULT_CONFIG } from './tokens';\nimport type { SelectionChangeEvent } from './types';\n\n/**\n * Angular standalone component wrapping the `<ntl-editor>` Web Component.\n *\n * Uses `afterNextRender()` for SSR-safe initialization and `effect()` for\n * reactive input tracking — no lifecycle interfaces needed.\n *\n * @example\n * ```html\n * <!-- Basic usage -->\n * <ntl-editor [toolbar]=\"toolbar\" [plugins]=\"plugins\" />\n *\n * <!-- Two-way content binding -->\n * <ntl-editor [(content)]=\"myDocument\" [toolbar]=\"toolbar\" />\n *\n * <!-- Reactive forms -->\n * <ntl-editor [formControl]=\"editorControl\" [toolbar]=\"toolbar\" />\n * ```\n */\n@Component({\n\tselector: 'ntl-editor',\n\ttemplate: '<div #host></div>',\n\tstyles: ':host { display: block; }',\n\tchangeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class NotectlEditorComponent {\n\t// --- Injected dependencies ---\n\n\tprivate readonly destroyRef: DestroyRef = inject(DestroyRef);\n\tprivate readonly defaultConfig: Partial<NotectlEditorConfig> | null = inject(\n\t\tNOTECTL_DEFAULT_CONFIG,\n\t\t{ optional: true },\n\t);\n\n\t// --- Signal Inputs (1:1 with Web Component config) ---\n\n\treadonly plugins = input<Plugin[]>([]);\n\treadonly toolbar = input<ReadonlyArray<ReadonlyArray<Plugin>>>();\n\treadonly features = input<Partial<TextFormattingConfig>>();\n\treadonly placeholder = input<string>('Start typing...');\n\treadonly readonlyMode = input<boolean>(false);\n\treadonly autofocus = input<boolean>(false);\n\treadonly maxHistoryDepth = input<number>();\n\treadonly theme = input<ThemePreset | Theme>(ThemePreset.Light);\n\n\t// --- Two-way content binding via model() ---\n\n\t/**\n\t * Two-way bindable document content.\n\t *\n\t * `undefined` means no external content was provided — the editor manages\n\t * its own state. Once the editor emits changes, the model updates to the\n\t * current `Document`.\n\t *\n\t * @example\n\t * ```html\n\t * <ntl-editor [(content)]=\"myDocument\" />\n\t * ```\n\t */\n\treadonly content: ModelSignal<Document | undefined> = model<Document>();\n\n\t// --- Signal Outputs (events bridged from Web Component) ---\n\n\treadonly stateChange = output<StateChangeEvent>();\n\treadonly selectionChange = output<SelectionChangeEvent>();\n\treadonly editorFocus = output<void>();\n\treadonly editorBlur = output<void>();\n\treadonly ready = output<void>();\n\n\t// --- Reactive State ---\n\n\treadonly editorState = signal<EditorState | null>(null);\n\n\treadonly isEmpty = computed<boolean>(() => {\n\t\tconst state: EditorState | null = this.editorState();\n\t\tif (!state) return true;\n\t\tconst doc: Document = state.doc;\n\t\tif (doc.children.length === 0) return true;\n\t\tif (doc.children.length > 1) return false;\n\t\tconst block = doc.children[0];\n\t\tif (!block) return true;\n\t\treturn block.type === 'paragraph' && block.children.length === 0;\n\t});\n\n\t// --- Internal state ---\n\n\tprivate readonly hostRef = viewChild.required<ElementRef<HTMLDivElement>>('host');\n\tprivate editorRef: NotectlEditor | null = null;\n\tprivate readyResolve: (() => void) | null = null;\n\tprivate readonly readyPromise: Promise<void> = new Promise<void>((resolve) => {\n\t\tthis.readyResolve = resolve;\n\t});\n\tprivate readonly initialized = signal(false);\n\n\t/** Tracks the last document set from within the editor to prevent feedback loops. */\n\tprivate lastEditorDoc: Document | null = null;\n\n\tconstructor() {\n\t\t// SSR-safe: only runs in the browser after first render\n\t\tafterNextRender(() => {\n\t\t\tthis.initEditor();\n\t\t});\n\n\t\t// React to input changes via effect() — replaces ngOnChanges\n\t\teffect(() => {\n\t\t\tconst currentTheme: ThemePreset | Theme = this.theme();\n\t\t\tconst currentPlaceholder: string = this.placeholder();\n\t\t\tconst currentReadonly: boolean = this.readonlyMode();\n\n\t\t\tconst editor: NotectlEditor | null = this.editorRef;\n\t\t\tif (!this.initialized() || !editor) return;\n\n\t\t\teditor.setTheme(currentTheme);\n\t\t\teditor.configure({\n\t\t\t\tplaceholder: currentPlaceholder,\n\t\t\t\treadonly: currentReadonly,\n\t\t\t});\n\t\t});\n\n\t\t// Sync external content model changes into the editor.\n\t\t// Skips when the document was set by the editor itself (feedback loop prevention).\n\t\teffect(() => {\n\t\t\tconst doc: Document | undefined = this.content();\n\t\t\tconst editor: NotectlEditor | null = this.editorRef;\n\t\t\tif (!this.initialized() || !editor || !doc) return;\n\n\t\t\t// Skip if this document originated from the editor's own state change\n\t\t\tif (doc === this.lastEditorDoc) return;\n\n\t\t\teditor.setJSON(doc);\n\t\t});\n\n\t\tthis.destroyRef.onDestroy(() => {\n\t\t\tthis.destroyEditor();\n\t\t});\n\t}\n\n\t// --- Public API (proxy to Web Component) ---\n\n\t/** Returns the document as JSON. */\n\tgetJSON(): Document {\n\t\treturn this.requireEditor().getJSON();\n\t}\n\n\t/** Sets the document from JSON. */\n\tsetJSON(doc: Document): void {\n\t\tthis.requireEditor().setJSON(doc);\n\t}\n\n\t/** Returns sanitized HTML representation of the document. */\n\tasync getContentHTML(): Promise<string> {\n\t\treturn this.requireEditor().getContentHTML();\n\t}\n\n\t/** Sets content from HTML (sanitized). */\n\tasync setContentHTML(html: string): Promise<void> {\n\t\treturn this.requireEditor().setContentHTML(html);\n\t}\n\n\t/** Returns plain text content. */\n\tgetText(): string {\n\t\treturn this.requireEditor().getText();\n\t}\n\n\t/** Proxy to the Web Component's `commands` object. */\n\tget commands(): NotectlEditor['commands'] {\n\t\treturn this.requireEditor().commands;\n\t}\n\n\t/** Proxy to the Web Component's `can()` capability checker. */\n\tcan(): ReturnType<NotectlEditor['can']> {\n\t\treturn this.requireEditor().can();\n\t}\n\n\t/** Executes a named command registered by a plugin. */\n\texecuteCommand(name: string): boolean {\n\t\tif (!this.editorRef) return false;\n\t\treturn this.editorRef.executeCommand(name);\n\t}\n\n\t/** Configures a plugin at runtime. */\n\tconfigurePlugin(pluginId: string, config: PluginConfig): void {\n\t\tthis.editorRef?.configurePlugin(pluginId, config);\n\t}\n\n\t/** Dispatches a transaction. */\n\tdispatch(tr: Transaction): void {\n\t\tthis.editorRef?.dispatch(tr);\n\t}\n\n\t/** Returns the current editor state. */\n\tgetState(): EditorState {\n\t\treturn this.requireEditor().getState();\n\t}\n\n\t/** Changes the theme at runtime. */\n\tsetTheme(theme: ThemePreset | Theme): void {\n\t\tthis.editorRef?.setTheme(theme);\n\t}\n\n\t/** Returns the current theme setting. */\n\tgetTheme(): ThemePreset | Theme {\n\t\treturn this.editorRef?.getTheme() ?? this.theme();\n\t}\n\n\t/** Sets the readonly state programmatically (used by ControlValueAccessor). */\n\tsetReadonly(readonlyState: boolean): void {\n\t\tthis.editorRef?.configure({ readonly: readonlyState });\n\t}\n\n\t/** Resolves when the editor is ready. */\n\twhenReady(): Promise<void> {\n\t\treturn this.readyPromise;\n\t}\n\n\t// --- Private ---\n\n\tprivate initEditor(): void {\n\t\tconst hostElement: HTMLDivElement = this.hostRef().nativeElement;\n\t\tconst config: NotectlEditorConfig = this.buildConfig();\n\n\t\tconst editor: NotectlEditor = new NotectlEditor();\n\t\tthis.editorRef = editor;\n\n\t\t// Register event listeners BEFORE appending to DOM, because\n\t\t// appendChild triggers connectedCallback → init() synchronously.\n\t\teditor.on('stateChange', (event: StateChangeEvent) => {\n\t\t\tthis.editorState.set(event.newState);\n\t\t\tthis.syncContentModel(event.newState.doc);\n\t\t\tthis.stateChange.emit(event);\n\t\t});\n\n\t\teditor.on('selectionChange', (event: { selection: EditorSelection }) => {\n\t\t\tthis.selectionChange.emit(event);\n\t\t});\n\n\t\teditor.on('focus', () => {\n\t\t\tthis.editorFocus.emit();\n\t\t});\n\n\t\teditor.on('blur', () => {\n\t\t\tthis.editorBlur.emit();\n\t\t});\n\n\t\teditor.on('ready', () => {\n\t\t\tthis.initialized.set(true);\n\t\t\tconst state: EditorState = editor.getState();\n\t\t\tthis.editorState.set(state);\n\n\t\t\t// Apply initial content model if set before editor was ready\n\t\t\tconst initialContent: Document | undefined = this.content();\n\t\t\tif (initialContent) {\n\t\t\t\teditor.setJSON(initialContent);\n\t\t\t}\n\n\t\t\tthis.readyResolve?.();\n\t\t\tthis.ready.emit();\n\t\t});\n\n\t\t// Init with config BEFORE appending to DOM. appendChild triggers\n\t\t// connectedCallback which calls init() without config — by calling\n\t\t// init(config) first, the editor initializes with the full config\n\t\t// and connectedCallback's init() becomes a no-op (already initialized).\n\t\teditor.init(config);\n\t\thostElement.appendChild(editor);\n\t}\n\n\tprivate buildConfig(): NotectlEditorConfig {\n\t\tconst defaults: Partial<NotectlEditorConfig> = this.defaultConfig ?? {};\n\t\tconst toolbar: ReadonlyArray<ReadonlyArray<Plugin>> | undefined = this.toolbar();\n\t\tconst features: Partial<TextFormattingConfig> | undefined = this.features();\n\t\tconst maxHistory: number | undefined = this.maxHistoryDepth();\n\n\t\tconst config: NotectlEditorConfig = {\n\t\t\t...defaults,\n\t\t\tplugins: this.plugins(),\n\t\t\tplaceholder: this.placeholder(),\n\t\t\treadonly: this.readonlyMode(),\n\t\t\tautofocus: this.autofocus(),\n\t\t\ttheme: this.theme(),\n\t\t};\n\n\t\tif (toolbar !== undefined) {\n\t\t\tconfig.toolbar = toolbar;\n\t\t}\n\t\tif (features !== undefined) {\n\t\t\tconfig.features = features;\n\t\t}\n\t\tif (maxHistory !== undefined) {\n\t\t\tconfig.maxHistoryDepth = maxHistory;\n\t\t}\n\n\t\treturn config;\n\t}\n\n\t/** Syncs editor document changes to the content model without feedback loops. */\n\tprivate syncContentModel(doc: Document): void {\n\t\tthis.lastEditorDoc = doc;\n\t\tthis.content.set(doc);\n\t}\n\n\tprivate destroyEditor(): void {\n\t\tif (this.editorRef) {\n\t\t\tthis.editorRef.destroy();\n\t\t\tthis.editorRef = null;\n\t\t}\n\t\tthis.initialized.set(false);\n\t}\n\n\tprivate requireEditor(): NotectlEditor {\n\t\tconst editor: NotectlEditor | null = this.editorRef;\n\t\tif (!editor) {\n\t\t\tthrow new Error('NotectlEditor is not initialized. Await whenReady() first.');\n\t\t}\n\t\treturn editor;\n\t}\n}\n","import { DestroyRef, Directive, forwardRef, inject } from '@angular/core';\nimport { type ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport type { Document, StateChangeEvent } from '@notectl/core';\n\nimport { NotectlEditorComponent } from './notectl-editor.component';\nimport { type ContentFormat, NOTECTL_CONTENT_FORMAT } from './tokens';\n\ntype OnChangeFn = (value: Document | string | null) => void;\ntype OnTouchedFn = () => void;\n\n/** Escapes HTML special characters to prevent XSS when inserting plain text. */\nfunction escapeHtml(text: string): string {\n\treturn text\n\t\t.replace(/&/g, '&amp;')\n\t\t.replace(/</g, '&lt;')\n\t\t.replace(/>/g, '&gt;')\n\t\t.replace(/\"/g, '&quot;')\n\t\t.replace(/'/g, '&#39;');\n}\n\n/**\n * `ControlValueAccessor` directive for Angular forms integration.\n *\n * Supports Reactive Forms (`formControl`, `formControlName`) and\n * template-driven forms (`ngModel`).\n *\n * The content format is configurable via `provideNotectl({ contentFormat })` or\n * the `NOTECTL_CONTENT_FORMAT` injection token:\n * - `'json'` (default) — form value is a `Document` object\n * - `'html'` — form value is a sanitized HTML string\n * - `'text'` — form value is a plain text string\n */\n@Directive({\n\tselector: 'ntl-editor[formControl], ntl-editor[formControlName], ntl-editor[ngModel]',\n\tproviders: [\n\t\t{\n\t\t\tprovide: NG_VALUE_ACCESSOR,\n\t\t\tuseExisting: forwardRef(() => NotectlValueAccessorDirective),\n\t\t\tmulti: true,\n\t\t},\n\t],\n})\nexport class NotectlValueAccessorDirective implements ControlValueAccessor {\n\tprivate readonly editor: NotectlEditorComponent = inject(NotectlEditorComponent);\n\tprivate readonly destroyRef: DestroyRef = inject(DestroyRef);\n\tprivate readonly format: ContentFormat =\n\t\tinject(NOTECTL_CONTENT_FORMAT, { optional: true }) ?? 'json';\n\n\tprivate onChange: OnChangeFn = () => {};\n\tprivate onTouched: OnTouchedFn = () => {};\n\tprivate pendingValue: Document | string | null = null;\n\tprivate suppressEmit = false;\n\n\tprivate readonly stateChangeSub = this.editor.stateChange.subscribe(\n\t\t(_event: StateChangeEvent) => {\n\t\t\tif (this.suppressEmit) return;\n\t\t\tvoid this.readValue().then((value: Document | string | null) => {\n\t\t\t\tthis.onChange(value);\n\t\t\t});\n\t\t},\n\t);\n\n\tprivate readonly blurSub = this.editor.editorBlur.subscribe(() => {\n\t\tthis.onTouched();\n\t});\n\n\tconstructor() {\n\t\tthis.destroyRef.onDestroy(() => {\n\t\t\tthis.stateChangeSub.unsubscribe();\n\t\t\tthis.blurSub.unsubscribe();\n\t\t});\n\t}\n\n\twriteValue(value: Document | string | null): void {\n\t\tif (!value) return;\n\n\t\ttry {\n\t\t\tthis.editor.getState();\n\t\t\tvoid this.writeValueToEditor(value);\n\t\t\treturn;\n\t\t} catch {\n\t\t\t// Editor not ready yet — defer\n\t\t}\n\n\t\tthis.pendingValue = value;\n\t\tvoid this.editor.whenReady().then(async () => {\n\t\t\tif (this.pendingValue !== null) {\n\t\t\t\tawait this.writeValueToEditor(this.pendingValue);\n\t\t\t\tthis.pendingValue = null;\n\t\t\t}\n\t\t});\n\t}\n\n\tregisterOnChange(fn: OnChangeFn): void {\n\t\tthis.onChange = fn;\n\t}\n\n\tregisterOnTouched(fn: OnTouchedFn): void {\n\t\tthis.onTouched = fn;\n\t}\n\n\tsetDisabledState(isDisabled: boolean): void {\n\t\tthis.editor.setReadonly(isDisabled);\n\t}\n\n\tprivate async writeValueToEditor(value: Document | string): Promise<void> {\n\t\tthis.suppressEmit = true;\n\t\ttry {\n\t\t\tif (this.format === 'json' && typeof value === 'object') {\n\t\t\t\tthis.editor.setJSON(value as Document);\n\t\t\t} else if (this.format === 'html' && typeof value === 'string') {\n\t\t\t\tawait this.editor.setContentHTML(value);\n\t\t\t} else if (this.format === 'text' && typeof value === 'string') {\n\t\t\t\tawait this.editor.setContentHTML(`<p>${escapeHtml(value)}</p>`);\n\t\t\t} else if (typeof value === 'string') {\n\t\t\t\tawait this.editor.setContentHTML(value);\n\t\t\t} else {\n\t\t\t\tthis.editor.setJSON(value as Document);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.suppressEmit = false;\n\t\t}\n\t}\n\n\tprivate async readValue(): Promise<Document | string | null> {\n\t\ttry {\n\t\t\tswitch (this.format) {\n\t\t\t\tcase 'html':\n\t\t\t\t\treturn await this.editor.getContentHTML();\n\t\t\t\tcase 'text':\n\t\t\t\t\treturn this.editor.getText();\n\t\t\t\tdefault:\n\t\t\t\t\treturn this.editor.getJSON();\n\t\t\t}\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n}\n","import { DestroyRef, Injectable, computed, inject, signal } from '@angular/core';\nimport type { EditorState, StateChangeEvent, Transaction } from '@notectl/core';\nimport { type Observable, Subject } from 'rxjs';\n\nimport type { NotectlEditorComponent } from './notectl-editor.component';\n\n/**\n * Optional injectable service for programmatic editor access via DI.\n *\n * Useful when a toolbar or other component needs to interact with\n * the editor without a direct template reference.\n *\n * The service must be provided at a shared injector level and\n * connected to the editor component via `register()`.\n *\n * @example\n * ```typescript\n * @Component({\n * providers: [NotectlEditorService],\n * template: `\n * <app-toolbar />\n * <ntl-editor #editor [plugins]=\"plugins\" />\n * `,\n * })\n * export class EditorPage {\n * private readonly editor = viewChild.required<NotectlEditorComponent>('editor');\n * private readonly service = inject(NotectlEditorService);\n *\n * constructor() {\n * afterNextRender(() => {\n * this.service.register(this.editor());\n * });\n * }\n * }\n * ```\n */\n@Injectable()\nexport class NotectlEditorService {\n\tprivate readonly destroyRef: DestroyRef = inject(DestroyRef);\n\tprivate readonly editorRef = signal<NotectlEditorComponent | null>(null);\n\tprivate readonly stateChangeSubject = new Subject<StateChangeEvent>();\n\n\t/** Whether an editor is currently registered with this service. */\n\treadonly hasEditor = computed<boolean>(() => this.editorRef() !== null);\n\n\t/** Observable stream of state change events from the editor. */\n\treadonly stateChanges$: Observable<StateChangeEvent> = this.stateChangeSubject.asObservable();\n\n\tprivate stateChangeSub: { unsubscribe(): void } | null = null;\n\n\tconstructor() {\n\t\tthis.destroyRef.onDestroy(() => {\n\t\t\tthis.unregister();\n\t\t\tthis.stateChangeSubject.complete();\n\t\t});\n\t}\n\n\t/** Registers an editor component with this service. */\n\tregister(editor: NotectlEditorComponent): void {\n\t\tthis.unregister();\n\t\tthis.editorRef.set(editor);\n\n\t\tthis.stateChangeSub = editor.stateChange.subscribe((event: StateChangeEvent) => {\n\t\t\tthis.stateChangeSubject.next(event);\n\t\t});\n\t}\n\n\t/** Unregisters the current editor from this service. */\n\tunregister(): void {\n\t\tthis.stateChangeSub?.unsubscribe();\n\t\tthis.stateChangeSub = null;\n\t\tthis.editorRef.set(null);\n\t}\n\n\t/** Executes a named command on the registered editor. */\n\texecuteCommand(name: string): boolean {\n\t\tconst editor: NotectlEditorComponent | null = this.editorRef();\n\t\tif (!editor) return false;\n\t\treturn editor.executeCommand(name);\n\t}\n\n\t/** Returns the current editor state, or `null` if no editor is registered. */\n\tgetState(): EditorState | null {\n\t\tconst editor: NotectlEditorComponent | null = this.editorRef();\n\t\tif (!editor) return null;\n\t\ttry {\n\t\t\treturn editor.getState();\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/** Dispatches a transaction on the registered editor. */\n\tdispatch(tr: Transaction): void {\n\t\tthis.editorRef()?.dispatch(tr);\n\t}\n}\n","/**\n * @notectl/angular — Angular integration for the notectl rich text editor.\n * @packageDocumentation\n */\n\n// --- Angular Bindings ---\nexport { NotectlEditorComponent } from './lib/notectl-editor.component';\nexport { NotectlValueAccessorDirective } from './lib/value-accessor.directive';\nexport { NotectlEditorService } from './lib/notectl-editor.service';\n\n// --- Provider Function ---\nexport {\n\tprovideNotectl,\n\ttype NotectlProviderOptions,\n} from './lib/tokens';\n\n// --- Injection Tokens ---\nexport {\n\tNOTECTL_DEFAULT_CONFIG,\n\tNOTECTL_CONTENT_FORMAT,\n\ttype ContentFormat,\n} from './lib/tokens';\n\n// --- Angular-specific Types ---\nexport type { SelectionChangeEvent } from './lib/types';\n\n// --- Re-exports from @notectl/core (convenience) ---\n\n// Model types\nexport type {\n\tDocument,\n\tBlockNode,\n\tTextNode,\n\tInlineNode,\n\tMark,\n\tBlockAttrs,\n} from '@notectl/core';\n\n// Selection types\nexport type { EditorSelection, Position, Selection } from '@notectl/core';\n\n// State types\nexport type {\n\tEditorState,\n\tTransaction,\n\tTransactionMetadata,\n\tStateChangeEvent,\n} from '@notectl/core';\n\n// Plugin types\nexport type { Plugin, PluginConfig, PluginContext } from '@notectl/core';\n\n// Theme types\nexport type { Theme, PartialTheme, ThemePrimitives } from '@notectl/core';\nexport { ThemePreset, LIGHT_THEME, DARK_THEME, createTheme } from '@notectl/core';\n\n// Editor config\nexport type { NotectlEditorConfig } from '@notectl/core';\n\n// Plugin config types (from sub-path exports)\nexport type { TextFormattingConfig } from '@notectl/core/plugins/text-formatting';\nexport type { FontDefinition } from '@notectl/core/plugins/font';\n\n// Starter fonts\n/** @deprecated Import from '@notectl/core/fonts' instead. */\nexport { STARTER_FONTS } from '@notectl/core/fonts';\n\n// Plugins (tree-shakable re-exports from sub-paths)\nexport { TextFormattingPlugin } from '@notectl/core/plugins/text-formatting';\nexport { HeadingPlugin } from '@notectl/core/plugins/heading';\nexport { ListPlugin } from '@notectl/core/plugins/list';\nexport { LinkPlugin } from '@notectl/core/plugins/link';\nexport { BlockquotePlugin } from '@notectl/core/plugins/blockquote';\nexport { CodeBlockPlugin } from '@notectl/core/plugins/code-block';\nexport { TablePlugin } from '@notectl/core/plugins/table';\nexport { ImagePlugin } from '@notectl/core/plugins/image';\nexport { HorizontalRulePlugin } from '@notectl/core/plugins/horizontal-rule';\nexport { HardBreakPlugin } from '@notectl/core/plugins/hard-break';\nexport { StrikethroughPlugin } from '@notectl/core/plugins/strikethrough';\nexport { HighlightPlugin } from '@notectl/core/plugins/highlight';\nexport { TextColorPlugin } from '@notectl/core/plugins/text-color';\nexport { FontPlugin } from '@notectl/core/plugins/font';\nexport { FontSizePlugin } from '@notectl/core/plugins/font-size';\nexport { AlignmentPlugin } from '@notectl/core/plugins/alignment';\nexport { TextDirectionPlugin } from '@notectl/core/plugins/text-direction';\nexport { SuperSubPlugin } from '@notectl/core/plugins/super-sub';\nexport { ToolbarPlugin } from '@notectl/core/plugins/toolbar';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -26,9 +26,9 @@ declare class NotectlTestHarness {
26
26
  /** Executes a command and triggers change detection. */
27
27
  executeCommand(name: string): boolean;
28
28
  /** Sets HTML content and triggers change detection. */
29
- setContentHTML(html: string): void;
29
+ setContentHTML(html: string): Promise<void>;
30
30
  /** Returns the current HTML content. */
31
- getContentHTML(): string;
31
+ getContentHTML(): Promise<string>;
32
32
  /** Returns the current plain text content. */
33
33
  getText(): string;
34
34
  /** Returns whether the editor is empty. */
@@ -1,10 +1,30 @@
1
1
  import * as _angular_core from '@angular/core';
2
2
  import { ModelSignal, InjectionToken, EnvironmentProviders } from '@angular/core';
3
- import { EditorSelection, Plugin, TextFormattingConfig, ThemePreset, Theme, Document, StateChangeEvent, EditorState, NotectlEditor, PluginConfig, Transaction, NotectlEditorConfig } from '@notectl/core';
4
- export { AlignmentPlugin, BlockAttrs, BlockNode, BlockquotePlugin, CodeBlockPlugin, DARK_THEME, Document, EditorSelection, EditorState, FontDefinition, FontPlugin, FontSizePlugin, HardBreakPlugin, HeadingPlugin, HighlightPlugin, HorizontalRulePlugin, ImagePlugin, InlineNode, LIGHT_THEME, LinkPlugin, ListPlugin, Mark, NotectlEditorConfig, PartialTheme, Plugin, PluginConfig, PluginContext, Position, Selection, StateChangeEvent, StrikethroughPlugin, SuperSubPlugin, TablePlugin, TextColorPlugin, TextFormattingConfig, TextFormattingPlugin, TextNode, Theme, ThemePreset, ThemePrimitives, ToolbarPlugin, Transaction, TransactionMetadata, createTheme } from '@notectl/core';
3
+ import { EditorSelection, Plugin, ThemePreset, Theme, Document, StateChangeEvent, EditorState, NotectlEditor, PluginConfig, Transaction, NotectlEditorConfig } from '@notectl/core';
4
+ export { BlockAttrs, BlockNode, DARK_THEME, Document, EditorSelection, EditorState, InlineNode, LIGHT_THEME, Mark, NotectlEditorConfig, PartialTheme, Plugin, PluginConfig, PluginContext, Position, Selection, StateChangeEvent, TextNode, Theme, ThemePreset, ThemePrimitives, Transaction, TransactionMetadata, createTheme } from '@notectl/core';
5
+ import { TextFormattingConfig } from '@notectl/core/plugins/text-formatting';
6
+ export { TextFormattingConfig, TextFormattingPlugin } from '@notectl/core/plugins/text-formatting';
5
7
  import { ControlValueAccessor } from '@angular/forms';
6
8
  import { Observable } from 'rxjs';
9
+ export { FontDefinition, FontPlugin } from '@notectl/core/plugins/font';
7
10
  export { STARTER_FONTS } from '@notectl/core/fonts';
11
+ export { HeadingPlugin } from '@notectl/core/plugins/heading';
12
+ export { ListPlugin } from '@notectl/core/plugins/list';
13
+ export { LinkPlugin } from '@notectl/core/plugins/link';
14
+ export { BlockquotePlugin } from '@notectl/core/plugins/blockquote';
15
+ export { CodeBlockPlugin } from '@notectl/core/plugins/code-block';
16
+ export { TablePlugin } from '@notectl/core/plugins/table';
17
+ export { ImagePlugin } from '@notectl/core/plugins/image';
18
+ export { HorizontalRulePlugin } from '@notectl/core/plugins/horizontal-rule';
19
+ export { HardBreakPlugin } from '@notectl/core/plugins/hard-break';
20
+ export { StrikethroughPlugin } from '@notectl/core/plugins/strikethrough';
21
+ export { HighlightPlugin } from '@notectl/core/plugins/highlight';
22
+ export { TextColorPlugin } from '@notectl/core/plugins/text-color';
23
+ export { FontSizePlugin } from '@notectl/core/plugins/font-size';
24
+ export { AlignmentPlugin } from '@notectl/core/plugins/alignment';
25
+ export { TextDirectionPlugin } from '@notectl/core/plugins/text-direction';
26
+ export { SuperSubPlugin } from '@notectl/core/plugins/super-sub';
27
+ export { ToolbarPlugin } from '@notectl/core/plugins/toolbar';
8
28
 
9
29
  /** Event payload emitted on selection changes. */
10
30
  interface SelectionChangeEvent {
@@ -73,9 +93,9 @@ declare class NotectlEditorComponent {
73
93
  /** Sets the document from JSON. */
74
94
  setJSON(doc: Document): void;
75
95
  /** Returns sanitized HTML representation of the document. */
76
- getContentHTML(): string;
96
+ getContentHTML(): Promise<string>;
77
97
  /** Sets content from HTML (sanitized). */
78
- setContentHTML(html: string): void;
98
+ setContentHTML(html: string): Promise<void>;
79
99
  /** Returns plain text content. */
80
100
  getText(): string;
81
101
  /** Proxy to the Web Component's `commands` object. */
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@notectl/angular",
3
- "version": "1.7.2",
3
+ "version": "2.0.0",
4
4
  "description": "Angular integration for the notectl rich text editor. Provides a standalone component, reactive forms support, and DI service.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "module": "./dist/fesm2022/notectl-angular.mjs",
8
8
  "typings": "./dist/types/notectl-angular.d.ts",
9
9
  "sideEffects": false,
10
- "files": ["dist", "README.md"],
10
+ "files": ["dist", "README.md", "LICENSE"],
11
11
  "keywords": [
12
12
  "angular",
13
13
  "rich-text-editor",
@@ -29,6 +29,8 @@
29
29
  },
30
30
  "scripts": {
31
31
  "build": "ng-packagr -p ng-package.json",
32
+ "prepack": "cp ../../README.md ./README.md && cp ../../LICENSE ./LICENSE",
33
+ "postpack": "rm -f ./README.md ./LICENSE",
32
34
  "test": "vitest run",
33
35
  "test:watch": "vitest",
34
36
  "typecheck": "tsc --noEmit"
@@ -39,7 +41,7 @@
39
41
  "peerDependencies": {
40
42
  "@angular/core": ">=21.0.0",
41
43
  "@angular/forms": ">=21.0.0",
42
- "@notectl/core": "^1.0.0",
44
+ "@notectl/core": "^2.0.0",
43
45
  "rxjs": ">=7.0.0"
44
46
  },
45
47
  "devDependencies": {