@rtif-sdk/web 1.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 +21 -0
- package/README.md +67 -0
- package/dist/block-drag-handler.d.ts +189 -0
- package/dist/block-drag-handler.d.ts.map +1 -0
- package/dist/block-drag-handler.js +745 -0
- package/dist/block-drag-handler.js.map +1 -0
- package/dist/block-renderer.d.ts +402 -0
- package/dist/block-renderer.d.ts.map +1 -0
- package/dist/block-renderer.js +424 -0
- package/dist/block-renderer.js.map +1 -0
- package/dist/clipboard.d.ts +178 -0
- package/dist/clipboard.d.ts.map +1 -0
- package/dist/clipboard.js +432 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/command-bus.d.ts +113 -0
- package/dist/command-bus.d.ts.map +1 -0
- package/dist/command-bus.js +70 -0
- package/dist/command-bus.js.map +1 -0
- package/dist/composition.d.ts +220 -0
- package/dist/composition.d.ts.map +1 -0
- package/dist/composition.js +271 -0
- package/dist/composition.js.map +1 -0
- package/dist/content-extraction.d.ts +69 -0
- package/dist/content-extraction.d.ts.map +1 -0
- package/dist/content-extraction.js +228 -0
- package/dist/content-extraction.js.map +1 -0
- package/dist/content-handler-file.d.ts +40 -0
- package/dist/content-handler-file.d.ts.map +1 -0
- package/dist/content-handler-file.js +91 -0
- package/dist/content-handler-file.js.map +1 -0
- package/dist/content-handler-image.d.ts +82 -0
- package/dist/content-handler-image.d.ts.map +1 -0
- package/dist/content-handler-image.js +120 -0
- package/dist/content-handler-image.js.map +1 -0
- package/dist/content-handler-url.d.ts +129 -0
- package/dist/content-handler-url.d.ts.map +1 -0
- package/dist/content-handler-url.js +244 -0
- package/dist/content-handler-url.js.map +1 -0
- package/dist/content-handlers.d.ts +67 -0
- package/dist/content-handlers.d.ts.map +1 -0
- package/dist/content-handlers.js +263 -0
- package/dist/content-handlers.js.map +1 -0
- package/dist/content-pipeline.d.ts +383 -0
- package/dist/content-pipeline.d.ts.map +1 -0
- package/dist/content-pipeline.js +232 -0
- package/dist/content-pipeline.js.map +1 -0
- package/dist/cursor-nav.d.ts +149 -0
- package/dist/cursor-nav.d.ts.map +1 -0
- package/dist/cursor-nav.js +230 -0
- package/dist/cursor-nav.js.map +1 -0
- package/dist/cursor-rect.d.ts +65 -0
- package/dist/cursor-rect.d.ts.map +1 -0
- package/dist/cursor-rect.js +98 -0
- package/dist/cursor-rect.js.map +1 -0
- package/dist/drop-indicator.d.ts +108 -0
- package/dist/drop-indicator.d.ts.map +1 -0
- package/dist/drop-indicator.js +236 -0
- package/dist/drop-indicator.js.map +1 -0
- package/dist/editor.d.ts +41 -0
- package/dist/editor.d.ts.map +1 -0
- package/dist/editor.js +710 -0
- package/dist/editor.js.map +1 -0
- package/dist/floating-toolbar.d.ts +93 -0
- package/dist/floating-toolbar.d.ts.map +1 -0
- package/dist/floating-toolbar.js +159 -0
- package/dist/floating-toolbar.js.map +1 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +119 -0
- package/dist/index.js.map +1 -0
- package/dist/input-bridge.d.ts +273 -0
- package/dist/input-bridge.d.ts.map +1 -0
- package/dist/input-bridge.js +884 -0
- package/dist/input-bridge.js.map +1 -0
- package/dist/link-popover.d.ts +38 -0
- package/dist/link-popover.d.ts.map +1 -0
- package/dist/link-popover.js +278 -0
- package/dist/link-popover.js.map +1 -0
- package/dist/mark-renderer.d.ts +275 -0
- package/dist/mark-renderer.d.ts.map +1 -0
- package/dist/mark-renderer.js +210 -0
- package/dist/mark-renderer.js.map +1 -0
- package/dist/perf.d.ts +145 -0
- package/dist/perf.d.ts.map +1 -0
- package/dist/perf.js +260 -0
- package/dist/perf.js.map +1 -0
- package/dist/plugin-kit.d.ts +265 -0
- package/dist/plugin-kit.d.ts.map +1 -0
- package/dist/plugin-kit.js +234 -0
- package/dist/plugin-kit.js.map +1 -0
- package/dist/plugins/alignment-plugin.d.ts +68 -0
- package/dist/plugins/alignment-plugin.d.ts.map +1 -0
- package/dist/plugins/alignment-plugin.js +98 -0
- package/dist/plugins/alignment-plugin.js.map +1 -0
- package/dist/plugins/block-utils.d.ts +113 -0
- package/dist/plugins/block-utils.d.ts.map +1 -0
- package/dist/plugins/block-utils.js +191 -0
- package/dist/plugins/block-utils.js.map +1 -0
- package/dist/plugins/blockquote-plugin.d.ts +39 -0
- package/dist/plugins/blockquote-plugin.d.ts.map +1 -0
- package/dist/plugins/blockquote-plugin.js +88 -0
- package/dist/plugins/blockquote-plugin.js.map +1 -0
- package/dist/plugins/bold-plugin.d.ts +37 -0
- package/dist/plugins/bold-plugin.d.ts.map +1 -0
- package/dist/plugins/bold-plugin.js +48 -0
- package/dist/plugins/bold-plugin.js.map +1 -0
- package/dist/plugins/callout-plugin.d.ts +100 -0
- package/dist/plugins/callout-plugin.d.ts.map +1 -0
- package/dist/plugins/callout-plugin.js +200 -0
- package/dist/plugins/callout-plugin.js.map +1 -0
- package/dist/plugins/code-block-plugin.d.ts +62 -0
- package/dist/plugins/code-block-plugin.d.ts.map +1 -0
- package/dist/plugins/code-block-plugin.js +176 -0
- package/dist/plugins/code-block-plugin.js.map +1 -0
- package/dist/plugins/code-plugin.d.ts +37 -0
- package/dist/plugins/code-plugin.d.ts.map +1 -0
- package/dist/plugins/code-plugin.js +48 -0
- package/dist/plugins/code-plugin.js.map +1 -0
- package/dist/plugins/embed-plugin.d.ts +90 -0
- package/dist/plugins/embed-plugin.d.ts.map +1 -0
- package/dist/plugins/embed-plugin.js +147 -0
- package/dist/plugins/embed-plugin.js.map +1 -0
- package/dist/plugins/font-family-plugin.d.ts +58 -0
- package/dist/plugins/font-family-plugin.d.ts.map +1 -0
- package/dist/plugins/font-family-plugin.js +57 -0
- package/dist/plugins/font-family-plugin.js.map +1 -0
- package/dist/plugins/font-size-plugin.d.ts +57 -0
- package/dist/plugins/font-size-plugin.d.ts.map +1 -0
- package/dist/plugins/font-size-plugin.js +56 -0
- package/dist/plugins/font-size-plugin.js.map +1 -0
- package/dist/plugins/heading-plugin.d.ts +52 -0
- package/dist/plugins/heading-plugin.d.ts.map +1 -0
- package/dist/plugins/heading-plugin.js +114 -0
- package/dist/plugins/heading-plugin.js.map +1 -0
- package/dist/plugins/hr-plugin.d.ts +33 -0
- package/dist/plugins/hr-plugin.d.ts.map +1 -0
- package/dist/plugins/hr-plugin.js +75 -0
- package/dist/plugins/hr-plugin.js.map +1 -0
- package/dist/plugins/image-plugin.d.ts +115 -0
- package/dist/plugins/image-plugin.d.ts.map +1 -0
- package/dist/plugins/image-plugin.js +199 -0
- package/dist/plugins/image-plugin.js.map +1 -0
- package/dist/plugins/indent-plugin.d.ts +62 -0
- package/dist/plugins/indent-plugin.d.ts.map +1 -0
- package/dist/plugins/indent-plugin.js +128 -0
- package/dist/plugins/indent-plugin.js.map +1 -0
- package/dist/plugins/index.d.ts +45 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +42 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/italic-plugin.d.ts +37 -0
- package/dist/plugins/italic-plugin.d.ts.map +1 -0
- package/dist/plugins/italic-plugin.js +48 -0
- package/dist/plugins/italic-plugin.js.map +1 -0
- package/dist/plugins/link-plugin.d.ts +129 -0
- package/dist/plugins/link-plugin.d.ts.map +1 -0
- package/dist/plugins/link-plugin.js +212 -0
- package/dist/plugins/link-plugin.js.map +1 -0
- package/dist/plugins/list-plugin.d.ts +53 -0
- package/dist/plugins/list-plugin.d.ts.map +1 -0
- package/dist/plugins/list-plugin.js +309 -0
- package/dist/plugins/list-plugin.js.map +1 -0
- package/dist/plugins/mark-utils.d.ts +173 -0
- package/dist/plugins/mark-utils.d.ts.map +1 -0
- package/dist/plugins/mark-utils.js +425 -0
- package/dist/plugins/mark-utils.js.map +1 -0
- package/dist/plugins/mention-plugin.d.ts +191 -0
- package/dist/plugins/mention-plugin.d.ts.map +1 -0
- package/dist/plugins/mention-plugin.js +295 -0
- package/dist/plugins/mention-plugin.js.map +1 -0
- package/dist/plugins/strikethrough-plugin.d.ts +37 -0
- package/dist/plugins/strikethrough-plugin.d.ts.map +1 -0
- package/dist/plugins/strikethrough-plugin.js +48 -0
- package/dist/plugins/strikethrough-plugin.js.map +1 -0
- package/dist/plugins/text-color-plugin.d.ts +57 -0
- package/dist/plugins/text-color-plugin.d.ts.map +1 -0
- package/dist/plugins/text-color-plugin.js +56 -0
- package/dist/plugins/text-color-plugin.js.map +1 -0
- package/dist/plugins/underline-plugin.d.ts +37 -0
- package/dist/plugins/underline-plugin.d.ts.map +1 -0
- package/dist/plugins/underline-plugin.js +48 -0
- package/dist/plugins/underline-plugin.js.map +1 -0
- package/dist/presets.d.ts +95 -0
- package/dist/presets.d.ts.map +1 -0
- package/dist/presets.js +159 -0
- package/dist/presets.js.map +1 -0
- package/dist/renderer.d.ts +125 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +415 -0
- package/dist/renderer.js.map +1 -0
- package/dist/scroll-to-cursor.d.ts +25 -0
- package/dist/scroll-to-cursor.d.ts.map +1 -0
- package/dist/scroll-to-cursor.js +59 -0
- package/dist/scroll-to-cursor.js.map +1 -0
- package/dist/selection-sync.d.ts +159 -0
- package/dist/selection-sync.d.ts.map +1 -0
- package/dist/selection-sync.js +527 -0
- package/dist/selection-sync.js.map +1 -0
- package/dist/shortcut-handler.d.ts +98 -0
- package/dist/shortcut-handler.d.ts.map +1 -0
- package/dist/shortcut-handler.js +155 -0
- package/dist/shortcut-handler.js.map +1 -0
- package/dist/toolbar.d.ts +103 -0
- package/dist/toolbar.d.ts.map +1 -0
- package/dist/toolbar.js +134 -0
- package/dist/toolbar.js.map +1 -0
- package/dist/trigger-manager.d.ts +205 -0
- package/dist/trigger-manager.d.ts.map +1 -0
- package/dist/trigger-manager.js +466 -0
- package/dist/trigger-manager.js.map +1 -0
- package/dist/types.d.ts +216 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +30 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyboard shortcut handler for the RTIF web editor.
|
|
3
|
+
*
|
|
4
|
+
* Reads shortcut bindings from the engine and intercepts `keydown` events
|
|
5
|
+
* to dispatch the corresponding commands through the {@link CommandBus}.
|
|
6
|
+
* Uses platform-aware modifier key detection (Cmd on Mac, Ctrl on Win/Linux).
|
|
7
|
+
*
|
|
8
|
+
* The shortcut handler fires BEFORE the cursor-nav handler's keydown listener,
|
|
9
|
+
* so plugin-registered shortcuts take priority over built-in navigation.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import { isMac } from './cursor-nav.js';
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// ShortcutHandler
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
/**
|
|
18
|
+
* Intercepts `keydown` events and matches them against engine-registered
|
|
19
|
+
* keyboard shortcuts.
|
|
20
|
+
*
|
|
21
|
+
* When a match is found, the corresponding command is executed via the
|
|
22
|
+
* command bus and the event is prevented/stopped.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const handler = new ShortcutHandler(root, {
|
|
27
|
+
* getShortcuts: () => engine.getShortcuts(),
|
|
28
|
+
* commandBus: bus,
|
|
29
|
+
* isComposing: () => compositionHandler.isComposing(),
|
|
30
|
+
* });
|
|
31
|
+
* handler.attach();
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export class ShortcutHandler {
|
|
35
|
+
_root;
|
|
36
|
+
_deps;
|
|
37
|
+
_attached = false;
|
|
38
|
+
_boundHandler;
|
|
39
|
+
constructor(root, deps) {
|
|
40
|
+
this._root = root;
|
|
41
|
+
this._deps = deps;
|
|
42
|
+
this._boundHandler = (e) => this._handleKeyDown(e);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Attach the `keydown` event listener to the root element.
|
|
46
|
+
*
|
|
47
|
+
* Uses `capture: true` so shortcuts fire before other keydown handlers.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* handler.attach();
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
attach() {
|
|
55
|
+
if (this._attached)
|
|
56
|
+
return;
|
|
57
|
+
this._root.addEventListener('keydown', this._boundHandler, true);
|
|
58
|
+
this._attached = true;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Remove the `keydown` event listener.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* handler.detach();
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
detach() {
|
|
69
|
+
if (!this._attached)
|
|
70
|
+
return;
|
|
71
|
+
this._root.removeEventListener('keydown', this._boundHandler, true);
|
|
72
|
+
this._attached = false;
|
|
73
|
+
}
|
|
74
|
+
_handleKeyDown(e) {
|
|
75
|
+
// Suppress shortcuts during IME composition
|
|
76
|
+
if (this._deps.isComposing())
|
|
77
|
+
return;
|
|
78
|
+
const shortcuts = this._deps.getShortcuts();
|
|
79
|
+
if (shortcuts.length === 0)
|
|
80
|
+
return;
|
|
81
|
+
for (const binding of shortcuts) {
|
|
82
|
+
if (matchesShortcut(e, binding)) {
|
|
83
|
+
// Check canExecute before consuming the event — if the command
|
|
84
|
+
// cannot execute (e.g., Tab outside a list), let the event pass
|
|
85
|
+
// through to the browser.
|
|
86
|
+
let canExec = true;
|
|
87
|
+
try {
|
|
88
|
+
canExec = this._deps.commandBus.canExecute(binding.command, binding.payload);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
/* default to allowing */
|
|
92
|
+
}
|
|
93
|
+
if (!canExec)
|
|
94
|
+
continue;
|
|
95
|
+
e.preventDefault();
|
|
96
|
+
e.stopPropagation();
|
|
97
|
+
this._deps.commandBus.execute(binding.command, binding.payload);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Matching
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
/**
|
|
107
|
+
* Check whether a keyboard event matches a shortcut binding.
|
|
108
|
+
*
|
|
109
|
+
* Platform-aware: `mod` matches `metaKey` on Mac, `ctrlKey` on other platforms.
|
|
110
|
+
*
|
|
111
|
+
* @param e - The keyboard event
|
|
112
|
+
* @param binding - The shortcut binding to match against
|
|
113
|
+
* @returns Whether the event matches the binding
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```ts
|
|
117
|
+
* const matches = matchesShortcut(event, {
|
|
118
|
+
* descriptor: { key: 'b', mod: true },
|
|
119
|
+
* command: 'toggleBold',
|
|
120
|
+
* });
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export function matchesShortcut(e, binding) {
|
|
124
|
+
const desc = binding.descriptor;
|
|
125
|
+
// Key comparison (case-insensitive for single characters)
|
|
126
|
+
if (e.key.toLowerCase() !== desc.key.toLowerCase())
|
|
127
|
+
return false;
|
|
128
|
+
// Platform modifier: Cmd on Mac, Ctrl on others
|
|
129
|
+
const mac = isMac();
|
|
130
|
+
const modRequired = desc.mod ?? false;
|
|
131
|
+
const modPressed = mac ? e.metaKey : e.ctrlKey;
|
|
132
|
+
if (modRequired !== modPressed)
|
|
133
|
+
return false;
|
|
134
|
+
// On Mac, if mod is required (metaKey), we don't also require ctrlKey to be false,
|
|
135
|
+
// but we do check the other modifiers
|
|
136
|
+
const shiftRequired = desc.shift ?? false;
|
|
137
|
+
if (shiftRequired !== e.shiftKey)
|
|
138
|
+
return false;
|
|
139
|
+
const altRequired = desc.alt ?? false;
|
|
140
|
+
if (altRequired !== e.altKey)
|
|
141
|
+
return false;
|
|
142
|
+
// Reject if extra modifier keys are held that aren't required
|
|
143
|
+
// On Mac: ctrl shouldn't be pressed (unless it's not a mod shortcut)
|
|
144
|
+
// On Win/Linux: meta shouldn't be pressed
|
|
145
|
+
if (mac && !modRequired && e.metaKey)
|
|
146
|
+
return false;
|
|
147
|
+
if (!mac && !modRequired && e.ctrlKey)
|
|
148
|
+
return false;
|
|
149
|
+
if (mac && e.ctrlKey && modRequired)
|
|
150
|
+
return false; // Ctrl+Cmd combo not expected
|
|
151
|
+
if (!mac && e.metaKey)
|
|
152
|
+
return false;
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=shortcut-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shortcut-handler.js","sourceRoot":"","sources":["../src/shortcut-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AA6BxC,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,eAAe;IACT,KAAK,CAAc;IACnB,KAAK,CAAsB;IACpC,SAAS,GAAG,KAAK,CAAC;IACT,aAAa,CAA6B;IAE3D,YAAY,IAAiB,EAAE,IAAyB;QACtD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,aAAa,GAAG,CAAC,CAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IACpE,CAAC;IAED;;;;;;;;;OASG;IACH,MAAM;QACJ,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QACjE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACxB,CAAC;IAED;;;;;;;OAOG;IACH,MAAM;QACJ,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO;QAC5B,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QACpE,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IACzB,CAAC;IAEO,cAAc,CAAC,CAAgB;QACrC,4CAA4C;QAC5C,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;YAAE,OAAO;QAErC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;QAC5C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEnC,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;YAChC,IAAI,eAAe,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC;gBAChC,+DAA+D;gBAC/D,gEAAgE;gBAChE,0BAA0B;gBAC1B,IAAI,OAAO,GAAG,IAAI,CAAC;gBACnB,IAAI,CAAC;oBACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;gBAC/E,CAAC;gBAAC,MAAM,CAAC;oBACP,yBAAyB;gBAC3B,CAAC;gBACD,IAAI,CAAC,OAAO;oBAAE,SAAS;gBAEvB,CAAC,CAAC,cAAc,EAAE,CAAC;gBACnB,CAAC,CAAC,eAAe,EAAE,CAAC;gBACpB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;gBAChE,OAAO;YACT,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED,8EAA8E;AAC9E,WAAW;AACX,8EAA8E;AAE9E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,eAAe,CAAC,CAAgB,EAAE,OAAwB;IACxE,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC;IAEhC,0DAA0D;IAC1D,IAAI,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE;QAAE,OAAO,KAAK,CAAC;IAEjE,gDAAgD;IAChD,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;IACpB,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,IAAI,KAAK,CAAC;IACtC,MAAM,UAAU,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAC/C,IAAI,WAAW,KAAK,UAAU;QAAE,OAAO,KAAK,CAAC;IAE7C,mFAAmF;IACnF,sCAAsC;IACtC,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC;IAC1C,IAAI,aAAa,KAAK,CAAC,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAE/C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,IAAI,KAAK,CAAC;IACtC,IAAI,WAAW,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAE3C,8DAA8D;IAC9D,qEAAqE;IACrE,0CAA0C;IAC1C,IAAI,GAAG,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IACnD,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IACpD,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,IAAI,WAAW;QAAE,OAAO,KAAK,CAAC,CAAC,8BAA8B;IACjF,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAEpC,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixed toolbar primitive for the RTIF web editor.
|
|
3
|
+
*
|
|
4
|
+
* Renders a horizontal toolbar with buttons that execute editor commands.
|
|
5
|
+
* Button state (active, disabled) updates live via the {@link CommandBus}.
|
|
6
|
+
* Uses `mousedown` + `preventDefault()` to avoid stealing focus from the editor.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import type { CommandBus } from './command-bus.js';
|
|
11
|
+
/**
|
|
12
|
+
* Type-safe constants for common toolbar group names.
|
|
13
|
+
*
|
|
14
|
+
* Use these when building `ToolbarButtonConfig` arrays to ensure consistent
|
|
15
|
+
* grouping across your toolbar.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const buttons: ToolbarButtonConfig[] = [
|
|
20
|
+
* { command: 'undo', icon: '↩', label: 'Undo', group: ToolbarGroup.HISTORY },
|
|
21
|
+
* { command: 'toggleMark:bold', icon: 'B', label: 'Bold', group: ToolbarGroup.MARKS },
|
|
22
|
+
* ];
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare const ToolbarGroup: {
|
|
26
|
+
readonly HISTORY: "history";
|
|
27
|
+
readonly MARKS: "marks";
|
|
28
|
+
readonly BLOCKS: "blocks";
|
|
29
|
+
readonly INSERT: "insert";
|
|
30
|
+
readonly ALIGN: "align";
|
|
31
|
+
readonly INDENT: "indent";
|
|
32
|
+
readonly STYLE: "style";
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Configuration for a single toolbar button.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* const boldButton: ToolbarButtonConfig = {
|
|
40
|
+
* command: 'toggleMark:bold',
|
|
41
|
+
* icon: 'B',
|
|
42
|
+
* label: 'Bold',
|
|
43
|
+
* group: 'format',
|
|
44
|
+
* };
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export interface ToolbarButtonConfig {
|
|
48
|
+
/** The command to execute when the button is clicked */
|
|
49
|
+
readonly command: string;
|
|
50
|
+
/** Optional payload to pass to the command handler */
|
|
51
|
+
readonly payload?: unknown;
|
|
52
|
+
/** Icon text or HTML to display in the button (e.g., "B", "<svg>...") */
|
|
53
|
+
readonly icon: string;
|
|
54
|
+
/** Accessible label for the button */
|
|
55
|
+
readonly label: string;
|
|
56
|
+
/** Optional group name for visual grouping (rendered as a separator between groups) */
|
|
57
|
+
readonly group?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Handle returned by `createToolbar()` for lifecycle management.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* const toolbar = createToolbar(container, bus, buttons);
|
|
65
|
+
* // ... later:
|
|
66
|
+
* toolbar.destroy();
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export interface ToolbarHandle {
|
|
70
|
+
/** The toolbar's root DOM element */
|
|
71
|
+
readonly element: HTMLElement;
|
|
72
|
+
/** Remove all event listeners and subscriptions. Idempotent. */
|
|
73
|
+
destroy(): void;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Create a fixed toolbar with command-driven buttons.
|
|
77
|
+
*
|
|
78
|
+
* Buttons reflect live command state:
|
|
79
|
+
* - `aria-pressed="true"` when the command's `isActive` returns true
|
|
80
|
+
* - `aria-disabled="true"` when the command's `canExecute` returns false
|
|
81
|
+
*
|
|
82
|
+
* The toolbar subscribes to the CommandBus for automatic state updates.
|
|
83
|
+
*
|
|
84
|
+
* @param container - The DOM element to render the toolbar into
|
|
85
|
+
* @param bus - The CommandBus to execute commands and query state
|
|
86
|
+
* @param buttons - Array of button configurations
|
|
87
|
+
* @returns A ToolbarHandle for lifecycle management
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* const toolbar = createToolbar(
|
|
92
|
+
* document.getElementById('toolbar')!,
|
|
93
|
+
* bus,
|
|
94
|
+
* [
|
|
95
|
+
* { command: 'toggleMark:bold', icon: 'B', label: 'Bold', group: 'format' },
|
|
96
|
+
* { command: 'toggleMark:italic', icon: 'I', label: 'Italic', group: 'format' },
|
|
97
|
+
* { command: 'undo', icon: '↩', label: 'Undo', group: 'history' },
|
|
98
|
+
* ],
|
|
99
|
+
* );
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export declare function createToolbar(container: HTMLElement, bus: CommandBus, buttons: ReadonlyArray<ToolbarButtonConfig>): ToolbarHandle;
|
|
103
|
+
//# sourceMappingURL=toolbar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolbar.d.ts","sourceRoot":"","sources":["../src/toolbar.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAMnD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,YAAY;;;;;;;;CAQf,CAAC;AAMX;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uFAAuF;IACvF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,gEAAgE;IAChE,OAAO,IAAI,IAAI,CAAC;CACjB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,WAAW,EACtB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,aAAa,CAAC,mBAAmB,CAAC,GAC1C,aAAa,CA+Ef"}
|
package/dist/toolbar.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixed toolbar primitive for the RTIF web editor.
|
|
3
|
+
*
|
|
4
|
+
* Renders a horizontal toolbar with buttons that execute editor commands.
|
|
5
|
+
* Button state (active, disabled) updates live via the {@link CommandBus}.
|
|
6
|
+
* Uses `mousedown` + `preventDefault()` to avoid stealing focus from the editor.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Toolbar group constants
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
/**
|
|
14
|
+
* Type-safe constants for common toolbar group names.
|
|
15
|
+
*
|
|
16
|
+
* Use these when building `ToolbarButtonConfig` arrays to ensure consistent
|
|
17
|
+
* grouping across your toolbar.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const buttons: ToolbarButtonConfig[] = [
|
|
22
|
+
* { command: 'undo', icon: '↩', label: 'Undo', group: ToolbarGroup.HISTORY },
|
|
23
|
+
* { command: 'toggleMark:bold', icon: 'B', label: 'Bold', group: ToolbarGroup.MARKS },
|
|
24
|
+
* ];
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export const ToolbarGroup = {
|
|
28
|
+
HISTORY: 'history',
|
|
29
|
+
MARKS: 'marks',
|
|
30
|
+
BLOCKS: 'blocks',
|
|
31
|
+
INSERT: 'insert',
|
|
32
|
+
ALIGN: 'align',
|
|
33
|
+
INDENT: 'indent',
|
|
34
|
+
STYLE: 'style',
|
|
35
|
+
};
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Factory
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
/**
|
|
40
|
+
* Create a fixed toolbar with command-driven buttons.
|
|
41
|
+
*
|
|
42
|
+
* Buttons reflect live command state:
|
|
43
|
+
* - `aria-pressed="true"` when the command's `isActive` returns true
|
|
44
|
+
* - `aria-disabled="true"` when the command's `canExecute` returns false
|
|
45
|
+
*
|
|
46
|
+
* The toolbar subscribes to the CommandBus for automatic state updates.
|
|
47
|
+
*
|
|
48
|
+
* @param container - The DOM element to render the toolbar into
|
|
49
|
+
* @param bus - The CommandBus to execute commands and query state
|
|
50
|
+
* @param buttons - Array of button configurations
|
|
51
|
+
* @returns A ToolbarHandle for lifecycle management
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const toolbar = createToolbar(
|
|
56
|
+
* document.getElementById('toolbar')!,
|
|
57
|
+
* bus,
|
|
58
|
+
* [
|
|
59
|
+
* { command: 'toggleMark:bold', icon: 'B', label: 'Bold', group: 'format' },
|
|
60
|
+
* { command: 'toggleMark:italic', icon: 'I', label: 'Italic', group: 'format' },
|
|
61
|
+
* { command: 'undo', icon: '↩', label: 'Undo', group: 'history' },
|
|
62
|
+
* ],
|
|
63
|
+
* );
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export function createToolbar(container, bus, buttons) {
|
|
67
|
+
// Create the toolbar element
|
|
68
|
+
const toolbar = container.ownerDocument.createElement('div');
|
|
69
|
+
toolbar.setAttribute('role', 'toolbar');
|
|
70
|
+
toolbar.setAttribute('aria-label', 'Editor toolbar');
|
|
71
|
+
// Track button elements for state updates
|
|
72
|
+
const buttonElements = [];
|
|
73
|
+
let lastGroup;
|
|
74
|
+
for (const config of buttons) {
|
|
75
|
+
// Add separator between groups
|
|
76
|
+
if (config.group !== undefined && config.group !== lastGroup && lastGroup !== undefined) {
|
|
77
|
+
const separator = container.ownerDocument.createElement('span');
|
|
78
|
+
separator.setAttribute('role', 'separator');
|
|
79
|
+
separator.setAttribute('aria-orientation', 'vertical');
|
|
80
|
+
separator.className = 'rtif-toolbar-separator';
|
|
81
|
+
toolbar.appendChild(separator);
|
|
82
|
+
}
|
|
83
|
+
lastGroup = config.group;
|
|
84
|
+
const btn = container.ownerDocument.createElement('button');
|
|
85
|
+
btn.type = 'button';
|
|
86
|
+
btn.setAttribute('aria-label', config.label);
|
|
87
|
+
btn.setAttribute('data-command', config.command);
|
|
88
|
+
btn.className = 'rtif-toolbar-button';
|
|
89
|
+
btn.innerHTML = config.icon;
|
|
90
|
+
// Prevent focus steal on mousedown
|
|
91
|
+
btn.addEventListener('mousedown', (e) => {
|
|
92
|
+
e.preventDefault();
|
|
93
|
+
});
|
|
94
|
+
// Execute command on click
|
|
95
|
+
btn.addEventListener('click', () => {
|
|
96
|
+
bus.execute(config.command, config.payload);
|
|
97
|
+
});
|
|
98
|
+
toolbar.appendChild(btn);
|
|
99
|
+
buttonElements.push({ el: btn, config });
|
|
100
|
+
}
|
|
101
|
+
container.appendChild(toolbar);
|
|
102
|
+
// Update button states
|
|
103
|
+
function updateStates() {
|
|
104
|
+
for (const { el, config } of buttonElements) {
|
|
105
|
+
const active = bus.isActive(config.command, config.payload);
|
|
106
|
+
const canExec = bus.canExecute(config.command, config.payload);
|
|
107
|
+
el.setAttribute('aria-pressed', String(active));
|
|
108
|
+
el.setAttribute('aria-disabled', String(!canExec));
|
|
109
|
+
el.disabled = !canExec;
|
|
110
|
+
if (active) {
|
|
111
|
+
el.classList.add('rtif-toolbar-active');
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
el.classList.remove('rtif-toolbar-active');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Initial state update
|
|
119
|
+
updateStates();
|
|
120
|
+
// Subscribe to state changes for live updates
|
|
121
|
+
const unsubscribe = bus.subscribe(() => updateStates());
|
|
122
|
+
let destroyed = false;
|
|
123
|
+
return {
|
|
124
|
+
element: toolbar,
|
|
125
|
+
destroy() {
|
|
126
|
+
if (destroyed)
|
|
127
|
+
return;
|
|
128
|
+
destroyed = true;
|
|
129
|
+
unsubscribe();
|
|
130
|
+
toolbar.remove();
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=toolbar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolbar.js","sourceRoot":"","sources":["../src/toolbar.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic trigger infrastructure for character-triggered interactions.
|
|
3
|
+
*
|
|
4
|
+
* Detects trigger characters (e.g., `@`, `#`, `/`), manages TriggerSession
|
|
5
|
+
* lifecycle, handles keyboard routing and dismissal. Fully reusable for
|
|
6
|
+
* @mentions, #hashtags, /slash-commands, etc.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import type { IEditorEngine } from '@rtif-sdk/engine';
|
|
11
|
+
import type { CursorRectAPI } from './cursor-rect.js';
|
|
12
|
+
import type { Disposable } from './types.js';
|
|
13
|
+
/** Reason a trigger session was deactivated. */
|
|
14
|
+
export type TriggerDeactivateReason = 'accepted' | 'dismissed' | 'cursor-moved' | 'deleted';
|
|
15
|
+
/** Position constraint for trigger activation. */
|
|
16
|
+
export type TriggerPosition = 'start' | 'after-whitespace' | 'anywhere';
|
|
17
|
+
/**
|
|
18
|
+
* Configuration for registering a trigger character.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const config: TriggerConfig = {
|
|
23
|
+
* id: 'mention',
|
|
24
|
+
* char: '@',
|
|
25
|
+
* onActivate(session) { showDropdown(session.query); },
|
|
26
|
+
* onChange(session) { filterDropdown(session.query); },
|
|
27
|
+
* onDeactivate(reason) { hideDropdown(); },
|
|
28
|
+
* };
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export interface TriggerConfig {
|
|
32
|
+
/** Unique identifier for this trigger. */
|
|
33
|
+
readonly id: string;
|
|
34
|
+
/** The trigger character (e.g., "@", "#", "/"). */
|
|
35
|
+
readonly char: string;
|
|
36
|
+
/**
|
|
37
|
+
* Position constraint for when the trigger activates.
|
|
38
|
+
* - `'start'` — Only at the start of a block.
|
|
39
|
+
* - `'after-whitespace'` — After whitespace or at block start.
|
|
40
|
+
* - `'anywhere'` — At any position.
|
|
41
|
+
*
|
|
42
|
+
* Default: `'after-whitespace'`.
|
|
43
|
+
*/
|
|
44
|
+
readonly position?: TriggerPosition;
|
|
45
|
+
/** Called when a trigger session activates. */
|
|
46
|
+
readonly onActivate: (session: TriggerSession) => void;
|
|
47
|
+
/** Called when a trigger session deactivates. */
|
|
48
|
+
readonly onDeactivate?: (reason: TriggerDeactivateReason) => void;
|
|
49
|
+
/** Called when the query text changes during an active session. */
|
|
50
|
+
readonly onChange?: (session: TriggerSession) => void;
|
|
51
|
+
/**
|
|
52
|
+
* Called on keydown during an active session.
|
|
53
|
+
* Return `true` to stop event propagation (e.g., ArrowDown in dropdown).
|
|
54
|
+
* Return `false` to let the event propagate to the editor.
|
|
55
|
+
*/
|
|
56
|
+
readonly onKeyDown?: (session: TriggerSession, event: KeyboardEvent) => boolean;
|
|
57
|
+
/**
|
|
58
|
+
* If true, the session is deactivated when the query contains a space.
|
|
59
|
+
* Default: false.
|
|
60
|
+
*/
|
|
61
|
+
readonly cancelOnSpace?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Called when the trigger position may have changed due to scroll or resize.
|
|
64
|
+
* Consumers should re-query `getTriggerRect()` / `getCursorRect()` and
|
|
65
|
+
* reposition their UI.
|
|
66
|
+
*
|
|
67
|
+
* Only fires while a session for this trigger is active.
|
|
68
|
+
*/
|
|
69
|
+
readonly onPositionChange?: (session: TriggerSession) => void;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Represents an active trigger session.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* function handleActivate(session: TriggerSession) {
|
|
77
|
+
* console.log('Trigger:', session.trigger.char, 'Query:', session.query);
|
|
78
|
+
* const rect = session.getTriggerRect();
|
|
79
|
+
* if (rect) positionDropdown(rect);
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export interface TriggerSession {
|
|
84
|
+
/** The trigger configuration that activated this session. */
|
|
85
|
+
readonly trigger: Readonly<TriggerConfig>;
|
|
86
|
+
/** Absolute offset of the trigger character in the document. */
|
|
87
|
+
readonly triggerOffset: number;
|
|
88
|
+
/** Text between the trigger character and the cursor. */
|
|
89
|
+
readonly query: string;
|
|
90
|
+
/** Get the screen rect of the trigger character. */
|
|
91
|
+
getTriggerRect(): DOMRect | null;
|
|
92
|
+
/** Get the screen rect of the current cursor position. */
|
|
93
|
+
getCursorRect(): DOMRect | null;
|
|
94
|
+
/**
|
|
95
|
+
* Accept a result — replaces trigger+query with the result text/marks.
|
|
96
|
+
*
|
|
97
|
+
* @param result - The text and optional marks to insert
|
|
98
|
+
*/
|
|
99
|
+
accept(result: TriggerAcceptResult): void;
|
|
100
|
+
/** Dismiss the session — leaves text as-is, closes session. */
|
|
101
|
+
dismiss(): void;
|
|
102
|
+
/** Returns true if this session is still active. */
|
|
103
|
+
isActive(): boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The result passed to `session.accept()`.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```ts
|
|
110
|
+
* session.accept({
|
|
111
|
+
* text: '@Alice Johnson',
|
|
112
|
+
* marks: { mention: { id: 'user-123', displayName: 'Alice Johnson' } },
|
|
113
|
+
* appendSpace: true,
|
|
114
|
+
* });
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export interface TriggerAcceptResult {
|
|
118
|
+
/** The text to insert in place of trigger+query. */
|
|
119
|
+
readonly text: string;
|
|
120
|
+
/** Optional marks to apply to the inserted text. */
|
|
121
|
+
readonly marks?: Record<string, unknown>;
|
|
122
|
+
/**
|
|
123
|
+
* Whether to append a space after the inserted text.
|
|
124
|
+
* Default: true.
|
|
125
|
+
*/
|
|
126
|
+
readonly appendSpace?: boolean;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The TriggerManager interface.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* const manager = createTriggerManager(deps);
|
|
134
|
+
* const disposable = manager.register(mentionTriggerConfig);
|
|
135
|
+
* // later:
|
|
136
|
+
* disposable.dispose();
|
|
137
|
+
* manager.destroy();
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
export interface TriggerManager {
|
|
141
|
+
/**
|
|
142
|
+
* Register a trigger character configuration.
|
|
143
|
+
*
|
|
144
|
+
* @param config - The trigger configuration
|
|
145
|
+
* @returns A disposable to unregister the trigger
|
|
146
|
+
*/
|
|
147
|
+
register(config: TriggerConfig): Disposable;
|
|
148
|
+
/**
|
|
149
|
+
* Get the currently active trigger session, or null if none.
|
|
150
|
+
*
|
|
151
|
+
* @returns The active session, or null
|
|
152
|
+
*/
|
|
153
|
+
getActiveSession(): TriggerSession | null;
|
|
154
|
+
/**
|
|
155
|
+
* Returns true if any trigger session is currently active.
|
|
156
|
+
*
|
|
157
|
+
* @returns Whether a session is active
|
|
158
|
+
*/
|
|
159
|
+
isActive(): boolean;
|
|
160
|
+
/** Tear down the TriggerManager — removes all listeners. */
|
|
161
|
+
destroy(): void;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Dependencies injected into the TriggerManager.
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```ts
|
|
168
|
+
* const deps: TriggerManagerDeps = {
|
|
169
|
+
* root,
|
|
170
|
+
* engine,
|
|
171
|
+
* cursorRect,
|
|
172
|
+
* isComposing: () => compositionHandler.isComposing(),
|
|
173
|
+
* };
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
export interface TriggerManagerDeps {
|
|
177
|
+
/** The editor root element. */
|
|
178
|
+
readonly root: HTMLElement;
|
|
179
|
+
/** The RTIF engine instance. */
|
|
180
|
+
readonly engine: IEditorEngine;
|
|
181
|
+
/** The cursor rect API for positioning. */
|
|
182
|
+
readonly cursorRect: CursorRectAPI;
|
|
183
|
+
/** Whether an IME composition is in progress. */
|
|
184
|
+
readonly isComposing: () => boolean;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Create a TriggerManager instance.
|
|
188
|
+
*
|
|
189
|
+
* The trigger manager detects trigger characters, manages sessions,
|
|
190
|
+
* and handles keyboard routing. Attach its keydown listener BEFORE
|
|
191
|
+
* the shortcut handler for correct priority.
|
|
192
|
+
*
|
|
193
|
+
* @param deps - Dependencies for the trigger manager
|
|
194
|
+
* @returns A TriggerManager instance
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* const triggerManager = createTriggerManager({
|
|
199
|
+
* root, engine, cursorRect,
|
|
200
|
+
* isComposing: () => compositionHandler.isComposing(),
|
|
201
|
+
* });
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
export declare function createTriggerManager(deps: TriggerManagerDeps): TriggerManager;
|
|
205
|
+
//# sourceMappingURL=trigger-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trigger-manager.d.ts","sourceRoot":"","sources":["../src/trigger-manager.ts"],"names":[],"mappings":"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"}
|