@fluentui-copilot/chat-input-plugins 0.0.4 → 0.0.6

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/CHANGELOG.json CHANGED
@@ -2,7 +2,55 @@
2
2
  "name": "@fluentui-copilot/chat-input-plugins",
3
3
  "entries": [
4
4
  {
5
- "date": "Tue, 30 Apr 2024 20:54:47 GMT",
5
+ "date": "Wed, 15 May 2024 23:57:19 GMT",
6
+ "tag": "@fluentui-copilot/chat-input-plugins_v0.0.6",
7
+ "version": "0.0.6",
8
+ "comments": {
9
+ "patch": [
10
+ {
11
+ "author": "tristan.watanabe@gmail.com",
12
+ "package": "@fluentui-copilot/chat-input-plugins",
13
+ "commit": "64698dbd45e8203c4db973e866db42f4e74ebeef",
14
+ "comment": "feat: add transform callback to getInputText to enable custom string representation for each node."
15
+ }
16
+ ]
17
+ }
18
+ },
19
+ {
20
+ "date": "Wed, 08 May 2024 22:10:20 GMT",
21
+ "tag": "@fluentui-copilot/chat-input-plugins_v0.0.5",
22
+ "version": "0.0.5",
23
+ "comments": {
24
+ "patch": [
25
+ {
26
+ "author": "owcampbe@microsoft.com",
27
+ "package": "@fluentui-copilot/chat-input-plugins",
28
+ "commit": "2bcbb68c4751536bb0ee7b4b19ded4f21be9f956",
29
+ "comment": "fix: Strip out sentinel node characters from ImperativeControlPlugin."
30
+ },
31
+ {
32
+ "author": "owcampbe@microsoft.com",
33
+ "package": "@fluentui-copilot/chat-input-plugins",
34
+ "commit": "da7a5eaa6af7c4eacef58d70f51acfb30977a38c",
35
+ "comment": "fix: insertTextAtCursor should append if no selection exists."
36
+ },
37
+ {
38
+ "author": "owcampbe@microsoft.com",
39
+ "package": "@fluentui-copilot/chat-input-plugins",
40
+ "commit": "8b3fa1f2bc2732776130769a904a98c314207fef",
41
+ "comment": "fix: Improve navigation around GhostText."
42
+ },
43
+ {
44
+ "author": "owcampbe@microsoft.com",
45
+ "package": "@fluentui-copilot/chat-input-plugins",
46
+ "commit": "8758b361c9586e347ec3c8c16038532598c31e77",
47
+ "comment": "fix: Get fresh reference to lexical node for update closure."
48
+ }
49
+ ]
50
+ }
51
+ },
52
+ {
53
+ "date": "Tue, 30 Apr 2024 20:55:42 GMT",
6
54
  "tag": "@fluentui-copilot/chat-input-plugins_v0.0.4",
7
55
  "version": "0.0.4",
8
56
  "comments": {
package/CHANGELOG.md CHANGED
@@ -1,12 +1,33 @@
1
1
  # Change Log - @fluentui-copilot/chat-input-plugins
2
2
 
3
- This log was last generated on Tue, 30 Apr 2024 20:54:47 GMT and should not be manually modified.
3
+ This log was last generated on Wed, 15 May 2024 23:57:19 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## [0.0.6](https://github.com/microsoft/fluentai/tree/@fluentui-copilot/chat-input-plugins_v0.0.6)
8
+
9
+ Wed, 15 May 2024 23:57:19 GMT
10
+ [Compare changes](https://github.com/microsoft/fluentai/compare/@fluentui-copilot/chat-input-plugins_v0.0.5..@fluentui-copilot/chat-input-plugins_v0.0.6)
11
+
12
+ ### Patches
13
+
14
+ - feat: add transform callback to getInputText to enable custom string representation for each node. ([PR #1626](https://github.com/microsoft/fluentai/pull/1626) by tristan.watanabe@gmail.com)
15
+
16
+ ## [0.0.5](https://github.com/microsoft/fluentai/tree/@fluentui-copilot/chat-input-plugins_v0.0.5)
17
+
18
+ Wed, 08 May 2024 22:10:20 GMT
19
+ [Compare changes](https://github.com/microsoft/fluentai/compare/@fluentui-copilot/chat-input-plugins_v0.0.4..@fluentui-copilot/chat-input-plugins_v0.0.5)
20
+
21
+ ### Patches
22
+
23
+ - fix: Strip out sentinel node characters from ImperativeControlPlugin. ([PR #1612](https://github.com/microsoft/fluentai/pull/1612) by owcampbe@microsoft.com)
24
+ - fix: insertTextAtCursor should append if no selection exists. ([PR #1590](https://github.com/microsoft/fluentai/pull/1590) by owcampbe@microsoft.com)
25
+ - fix: Improve navigation around GhostText. ([PR #1611](https://github.com/microsoft/fluentai/pull/1611) by owcampbe@microsoft.com)
26
+ - fix: Get fresh reference to lexical node for update closure. ([PR #1613](https://github.com/microsoft/fluentai/pull/1613) by owcampbe@microsoft.com)
27
+
7
28
  ## [0.0.4](https://github.com/microsoft/fluentai/tree/@fluentui-copilot/chat-input-plugins_v0.0.4)
8
29
 
9
- Tue, 30 Apr 2024 20:54:47 GMT
30
+ Tue, 30 Apr 2024 20:55:42 GMT
10
31
  [Compare changes](https://github.com/microsoft/fluentai/compare/@fluentui-copilot/chat-input-plugins_v0.0.3..@fluentui-copilot/chat-input-plugins_v0.0.4)
11
32
 
12
33
  ### Patches
package/dist/index.d.ts CHANGED
@@ -113,7 +113,10 @@ export declare interface IImperativeControlBase {
113
113
  appendText: (text: string) => void;
114
114
  prependText: (text: string) => void;
115
115
  insertTextAtCursor: (text: string) => void;
116
- getInputText: () => string;
116
+ /**
117
+ * @param transform will be called for each Lexical node in the input. This enables custom string representation for each node.
118
+ */
119
+ getInputText: (transform?: (node: LexicalNode) => string) => string;
117
120
  scrollToBottom: () => void;
118
121
  moveCursor: (location: number) => void;
119
122
  }
@@ -133,7 +136,7 @@ export declare class ImperativeControlBase implements IImperativeControlBase {
133
136
  appendText(text: string): void;
134
137
  prependText(text: string): void;
135
138
  insertTextAtCursor(text: string): void;
136
- getInputText(): string;
139
+ getInputText(transform?: (node: LexicalNode) => string): string;
137
140
  scrollToBottom(): void;
138
141
  }
139
142
 
@@ -150,6 +153,8 @@ export declare class ManualGhostTextBase<ComponentPropsType> {
150
153
  cancelGhostText(): void;
151
154
  }
152
155
 
156
+ export declare const SENTINEL_VALUE = "\u200B\u200C";
157
+
153
158
  export declare class SentinelNode extends TextNode {
154
159
  constructor(key?: NodeKey);
155
160
  static getType(): string;
@@ -1,6 +1,6 @@
1
1
  import { _ as _define_property } from "@swc/helpers/_/_define_property";
2
- import { $createTextNode, $getLeafNodes, $getRoot, $getSelection, $insertNodes, $isRangeSelection, $normalizeSelection__EXPERIMENTAL, $setSelection, COMMAND_PRIORITY_CRITICAL, INSERT_PARAGRAPH_COMMAND, KEY_ENTER_COMMAND, PASTE_COMMAND, mergeRegister } from '@fluentui-copilot/text-editor';
3
- import { $createSentinelNode, $isSentinelNode, SentinelNode } from './SentinelNode';
2
+ import { $createTextNode, $getLeafNodes, $getNodeByKey, $getRoot, $getSelection, $insertNodes, $isRangeSelection, $normalizeSelection__EXPERIMENTAL, $setSelection, COMMAND_PRIORITY_CRITICAL, INSERT_PARAGRAPH_COMMAND, KEY_ENTER_COMMAND, PASTE_COMMAND, mergeRegister } from '@fluentui-copilot/text-editor';
3
+ import { $createSentinelNode, $isSentinelNode, SENTINEL_VALUE, SentinelNode } from './SentinelNode';
4
4
  export class BasicFunctionalityBase {
5
5
  __enterHandler(event) {
6
6
  const selection = $getSelection();
@@ -28,7 +28,7 @@ export class BasicFunctionalityBase {
28
28
  this.deactivateContentCallbacks();
29
29
  this.__contentChangeCleanup = this.__editor.registerTextContentListener(text => {
30
30
  // Remove the sentinel node
31
- const processed = text.replace('\u200b\u200c', '');
31
+ const processed = text.replace(SENTINEL_VALUE, '');
32
32
  onContentChange === null || onContentChange === void 0 ? void 0 : onContentChange(processed);
33
33
  onCountChanged === null || onCountChanged === void 0 ? void 0 : onCountChanged(processed.length);
34
34
  });
@@ -107,19 +107,23 @@ export class BasicFunctionalityBase {
107
107
  if (leaves.length === 0) {
108
108
  return;
109
109
  }
110
- const sentinel = leaves[leaves.length - 1];
110
+ const lastNode = leaves[leaves.length - 1];
111
+ const lastNodeKey = lastNode.getKey();
111
112
  // If the last node isn't a sentinel, add one
112
- if (!$isSentinelNode(sentinel)) {
113
+ if (!$isSentinelNode(lastNode)) {
113
114
  this.__editor.update(() => {
114
- sentinel.insertAfter($createSentinelNode());
115
+ var
116
+ // We find the node by its key again in case the node was removed before this update runs
117
+ _$getNodeByKey;
118
+ (_$getNodeByKey = $getNodeByKey(lastNodeKey)) === null || _$getNodeByKey === void 0 ? void 0 : _$getNodeByKey.insertAfter($createSentinelNode());
115
119
  }, {
116
120
  discrete: true
117
121
  });
118
122
  return;
119
123
  }
120
124
  // If the sentinel node is not selected, we're done
121
- const previous = sentinel.getPreviousSibling();
122
- if (!previous || !sentinel.isSelected()) {
125
+ const previous = lastNode.getPreviousSibling();
126
+ if (!previous || !lastNode.isSelected()) {
123
127
  return;
124
128
  }
125
129
  const selection = $getSelection();
@@ -131,7 +135,8 @@ export class BasicFunctionalityBase {
131
135
  // where selection is ill-defined.
132
136
  if (selection.isCollapsed() && selection.anchor.offset > 0) {
133
137
  this.__editor.update(() => {
134
- sentinel.selectStart();
138
+ var _$getNodeByKey;
139
+ (_$getNodeByKey = $getNodeByKey(lastNodeKey)) === null || _$getNodeByKey === void 0 ? void 0 : _$getNodeByKey.selectStart();
135
140
  }, {
136
141
  discrete: true
137
142
  });
@@ -141,17 +146,19 @@ export class BasicFunctionalityBase {
141
146
  if (!selection.isCollapsed()) {
142
147
  let selectionChanged = false;
143
148
  const newSelection = selection.clone();
144
- if (newSelection.anchor.getNode() === sentinel && newSelection.anchor.offset > 0) {
145
- newSelection.anchor.set(sentinel.getKey(), 0, 'text');
149
+ if (newSelection.anchor.getNode() === lastNode && newSelection.anchor.offset > 0) {
150
+ newSelection.anchor.set(lastNodeKey, 0, 'text');
146
151
  selectionChanged = true;
147
152
  }
148
- if (newSelection.focus.getNode() === sentinel && newSelection.focus.offset > 0) {
149
- newSelection.focus.set(sentinel.getKey(), 0, 'text');
153
+ if (newSelection.focus.getNode() === lastNode && newSelection.focus.offset > 0) {
154
+ newSelection.focus.set(lastNodeKey, 0, 'text');
150
155
  selectionChanged = true;
151
156
  }
152
157
  if (selectionChanged) {
153
158
  this.__editor.update(() => {
154
- $setSelection($normalizeSelection__EXPERIMENTAL(newSelection));
159
+ if ($getNodeByKey(lastNodeKey) !== null) {
160
+ $setSelection($normalizeSelection__EXPERIMENTAL(newSelection));
161
+ }
155
162
  }, {
156
163
  discrete: true
157
164
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["BasicFunctionality.base.ts"],"sourcesContent":["import type { LexicalEditor } from '@fluentui-copilot/text-editor';\nimport {\n $createTextNode,\n $getLeafNodes,\n $getRoot,\n $getSelection,\n $insertNodes,\n $isRangeSelection,\n $normalizeSelection__EXPERIMENTAL,\n $setSelection,\n COMMAND_PRIORITY_CRITICAL,\n INSERT_PARAGRAPH_COMMAND,\n KEY_ENTER_COMMAND,\n PASTE_COMMAND,\n mergeRegister,\n} from '@fluentui-copilot/text-editor';\nimport { $createSentinelNode, $isSentinelNode, SentinelNode } from './SentinelNode';\n\nexport interface IBasicFunctionalityBase {\n insertDefaultValue: (defaultValue: string) => void;\n setIsDisabled: (isDisabled: boolean) => void;\n activateContentCallbacks(onContentChange?: (value: string) => void, onCountChanged?: (count: number) => void): void;\n deactivateContentCallbacks(): void;\n activateTrimWhitespace(): void;\n deactivateTrimWhitespace(): void;\n activatePasteCallback(onPaste: (event: ClipboardEvent) => void): void;\n deactivatePasteCallback(): void;\n cleanup(): void;\n}\n\nexport class BasicFunctionalityBase implements IBasicFunctionalityBase {\n private __editor: LexicalEditor;\n private __contentChangeCleanup?: () => void;\n private __trimWhitespaceCleanup?: () => void;\n private __baseHandlersCleanup?: () => void;\n private __pasteHandlerCleanup?: () => void;\n\n private __enterHandler(event: KeyboardEvent) {\n const selection = $getSelection();\n if (!$isRangeSelection(selection)) {\n return false;\n }\n\n if (event === null) {\n return false;\n }\n\n event.preventDefault();\n\n if (event.shiftKey) {\n return this.__editor.dispatchCommand(INSERT_PARAGRAPH_COMMAND, undefined);\n }\n\n // Mark event handled to override default behavior\n return true;\n }\n\n constructor(editor: LexicalEditor) {\n this.__editor = editor;\n\n this.__baseHandlersCleanup = mergeRegister(\n this.__editor.registerCommand(KEY_ENTER_COMMAND, this.__enterHandler.bind(this), COMMAND_PRIORITY_CRITICAL),\n\n // Add a sentinel node at the end of the input when there is content.\n // This sentinel node fixes a number of issues.\n // In Safari, Lexical's behaviour of adding <br /> tags to the end of the input when it ends\n // in a decorator node causes cursor location issues: https://github.com/facebook/lexical/issues/4487\n // Otherwise, when a decorator node is the last node in the input, the cursor can't move past it.\n // Adding an invisible text node that doesn't contribute to the content and can't be selected to the end of the input\n // mitigates these issues.\n this.__editor.registerUpdateListener(({ editorState }) => {\n editorState.read(() => {\n const leaves = $getLeafNodes($getRoot());\n if (leaves.length === 0) {\n return;\n }\n\n const sentinel = leaves[leaves.length - 1];\n\n // If the last node isn't a sentinel, add one\n if (!$isSentinelNode(sentinel)) {\n this.__editor.update(\n () => {\n sentinel.insertAfter($createSentinelNode());\n },\n { discrete: true },\n );\n return;\n }\n\n // If the sentinel node is not selected, we're done\n const previous = sentinel.getPreviousSibling();\n if (!previous || !sentinel.isSelected()) {\n return;\n }\n\n const selection = $getSelection();\n if (!$isRangeSelection(selection)) {\n return;\n }\n\n // If the cursor is inside the sentinel node, move it out (next to the beginning)\n // We allow selection on the boundary of the sentinel in case the adjacent node is a decorator node\n // where selection is ill-defined.\n if (selection.isCollapsed() && selection.anchor.offset > 0) {\n this.__editor.update(\n () => {\n sentinel.selectStart();\n },\n { discrete: true },\n );\n return;\n }\n\n // If the selection is a range which includes the sentinel, modify the range to exclude it\n if (!selection.isCollapsed()) {\n let selectionChanged = false;\n const newSelection = selection.clone();\n\n if (newSelection.anchor.getNode() === sentinel && newSelection.anchor.offset > 0) {\n newSelection.anchor.set(sentinel.getKey(), 0, 'text');\n selectionChanged = true;\n }\n if (newSelection.focus.getNode() === sentinel && newSelection.focus.offset > 0) {\n newSelection.focus.set(sentinel.getKey(), 0, 'text');\n selectionChanged = true;\n }\n\n if (selectionChanged) {\n this.__editor.update(\n () => {\n $setSelection($normalizeSelection__EXPERIMENTAL(newSelection));\n },\n { discrete: true },\n );\n }\n }\n });\n }),\n this.__editor.registerNodeTransform(SentinelNode, node => {\n if (!node.getPreviousSibling() || node.getNextSibling()) {\n node.remove();\n return;\n }\n }),\n );\n }\n\n insertDefaultValue(defaultValue: string) {\n if (defaultValue) {\n this.__editor.update(() => {\n $insertNodes([$createTextNode(defaultValue)]);\n });\n }\n }\n\n activateContentCallbacks(\n onContentChange?: ((value: string) => void) | undefined,\n onCountChanged?: ((count: number) => void) | undefined,\n ) {\n this.deactivateContentCallbacks();\n this.__contentChangeCleanup = this.__editor.registerTextContentListener(text => {\n // Remove the sentinel node\n const processed = text.replace('\\u200b\\u200c', '');\n onContentChange?.(processed);\n onCountChanged?.(processed.length);\n });\n }\n\n deactivateContentCallbacks() {\n this.__contentChangeCleanup?.();\n this.__contentChangeCleanup = undefined;\n }\n\n activatePasteCallback(onPaste: (event: ClipboardEvent) => void) {\n this.__pasteHandlerCleanup = this.__editor.registerCommand(\n PASTE_COMMAND,\n (event: ClipboardEvent) => {\n onPaste(event);\n\n if (event.defaultPrevented) {\n return true;\n }\n\n return false;\n },\n COMMAND_PRIORITY_CRITICAL,\n );\n }\n\n deactivatePasteCallback() {\n this.__pasteHandlerCleanup?.();\n this.__pasteHandlerCleanup = undefined;\n }\n\n activateTrimWhitespace() {\n this.deactivateTrimWhitespace();\n this.__trimWhitespaceCleanup = this.__editor.registerTextContentListener(text => {\n if (text.trim() === '') {\n this.__editor.update(\n () => {\n $getRoot()\n .getAllTextNodes()\n .forEach(node => {\n node.remove();\n });\n },\n // Don't allow undoing this action\n { tag: 'historic' },\n );\n }\n });\n }\n\n deactivateTrimWhitespace() {\n this.__trimWhitespaceCleanup?.();\n this.__trimWhitespaceCleanup = undefined;\n }\n\n setIsDisabled(isDisabled: boolean) {\n this.__editor.setEditable(!isDisabled);\n }\n\n cleanup() {\n this.deactivateContentCallbacks();\n this.deactivateTrimWhitespace();\n this.deactivatePasteCallback();\n this.__baseHandlersCleanup?.();\n this.__baseHandlersCleanup = undefined;\n }\n}\n"],"names":["$createTextNode","$getLeafNodes","$getRoot","$getSelection","$insertNodes","$isRangeSelection","$normalizeSelection__EXPERIMENTAL","$setSelection","COMMAND_PRIORITY_CRITICAL","INSERT_PARAGRAPH_COMMAND","KEY_ENTER_COMMAND","PASTE_COMMAND","mergeRegister","$createSentinelNode","$isSentinelNode","SentinelNode","BasicFunctionalityBase","__enterHandler","event","selection","preventDefault","shiftKey","__editor","dispatchCommand","undefined","insertDefaultValue","defaultValue","update","activateContentCallbacks","onContentChange","onCountChanged","deactivateContentCallbacks","__contentChangeCleanup","registerTextContentListener","text","processed","replace","length","activatePasteCallback","onPaste","__pasteHandlerCleanup","registerCommand","defaultPrevented","deactivatePasteCallback","activateTrimWhitespace","deactivateTrimWhitespace","__trimWhitespaceCleanup","trim","getAllTextNodes","forEach","node","remove","tag","setIsDisabled","isDisabled","setEditable","cleanup","__baseHandlersCleanup","constructor","editor","bind","registerUpdateListener","editorState","read","leaves","sentinel","insertAfter","discrete","previous","getPreviousSibling","isSelected","isCollapsed","anchor","offset","selectStart","selectionChanged","newSelection","clone","getNode","set","getKey","focus","registerNodeTransform","getNextSibling"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
1
+ {"version":3,"sources":["BasicFunctionality.base.ts"],"sourcesContent":["import type { LexicalEditor } from '@fluentui-copilot/text-editor';\nimport {\n $createTextNode,\n $getLeafNodes,\n $getNodeByKey,\n $getRoot,\n $getSelection,\n $insertNodes,\n $isRangeSelection,\n $normalizeSelection__EXPERIMENTAL,\n $setSelection,\n COMMAND_PRIORITY_CRITICAL,\n INSERT_PARAGRAPH_COMMAND,\n KEY_ENTER_COMMAND,\n PASTE_COMMAND,\n mergeRegister,\n} from '@fluentui-copilot/text-editor';\nimport { $createSentinelNode, $isSentinelNode, SENTINEL_VALUE, SentinelNode } from './SentinelNode';\n\nexport interface IBasicFunctionalityBase {\n insertDefaultValue: (defaultValue: string) => void;\n setIsDisabled: (isDisabled: boolean) => void;\n activateContentCallbacks(onContentChange?: (value: string) => void, onCountChanged?: (count: number) => void): void;\n deactivateContentCallbacks(): void;\n activateTrimWhitespace(): void;\n deactivateTrimWhitespace(): void;\n activatePasteCallback(onPaste: (event: ClipboardEvent) => void): void;\n deactivatePasteCallback(): void;\n cleanup(): void;\n}\n\nexport class BasicFunctionalityBase implements IBasicFunctionalityBase {\n private __editor: LexicalEditor;\n private __contentChangeCleanup?: () => void;\n private __trimWhitespaceCleanup?: () => void;\n private __baseHandlersCleanup?: () => void;\n private __pasteHandlerCleanup?: () => void;\n\n private __enterHandler(event: KeyboardEvent) {\n const selection = $getSelection();\n if (!$isRangeSelection(selection)) {\n return false;\n }\n\n if (event === null) {\n return false;\n }\n\n event.preventDefault();\n\n if (event.shiftKey) {\n return this.__editor.dispatchCommand(INSERT_PARAGRAPH_COMMAND, undefined);\n }\n\n // Mark event handled to override default behavior\n return true;\n }\n\n constructor(editor: LexicalEditor) {\n this.__editor = editor;\n\n this.__baseHandlersCleanup = mergeRegister(\n this.__editor.registerCommand(KEY_ENTER_COMMAND, this.__enterHandler.bind(this), COMMAND_PRIORITY_CRITICAL),\n\n // Add a sentinel node at the end of the input when there is content.\n // This sentinel node fixes a number of issues.\n // In Safari, Lexical's behaviour of adding <br /> tags to the end of the input when it ends\n // in a decorator node causes cursor location issues: https://github.com/facebook/lexical/issues/4487\n // Otherwise, when a decorator node is the last node in the input, the cursor can't move past it.\n // Adding an invisible text node that doesn't contribute to the content and can't be selected to the end of the input\n // mitigates these issues.\n this.__editor.registerUpdateListener(({ editorState }) => {\n editorState.read(() => {\n const leaves = $getLeafNodes($getRoot());\n if (leaves.length === 0) {\n return;\n }\n\n const lastNode = leaves[leaves.length - 1];\n const lastNodeKey = lastNode.getKey();\n\n // If the last node isn't a sentinel, add one\n if (!$isSentinelNode(lastNode)) {\n this.__editor.update(\n () => {\n // We find the node by its key again in case the node was removed before this update runs\n $getNodeByKey(lastNodeKey)?.insertAfter($createSentinelNode());\n },\n { discrete: true },\n );\n return;\n }\n\n // If the sentinel node is not selected, we're done\n const previous = lastNode.getPreviousSibling();\n if (!previous || !lastNode.isSelected()) {\n return;\n }\n\n const selection = $getSelection();\n if (!$isRangeSelection(selection)) {\n return;\n }\n\n // If the cursor is inside the sentinel node, move it out (next to the beginning)\n // We allow selection on the boundary of the sentinel in case the adjacent node is a decorator node\n // where selection is ill-defined.\n if (selection.isCollapsed() && selection.anchor.offset > 0) {\n this.__editor.update(\n () => {\n $getNodeByKey(lastNodeKey)?.selectStart();\n },\n { discrete: true },\n );\n return;\n }\n\n // If the selection is a range which includes the sentinel, modify the range to exclude it\n if (!selection.isCollapsed()) {\n let selectionChanged = false;\n const newSelection = selection.clone();\n\n if (newSelection.anchor.getNode() === lastNode && newSelection.anchor.offset > 0) {\n newSelection.anchor.set(lastNodeKey, 0, 'text');\n selectionChanged = true;\n }\n if (newSelection.focus.getNode() === lastNode && newSelection.focus.offset > 0) {\n newSelection.focus.set(lastNodeKey, 0, 'text');\n selectionChanged = true;\n }\n\n if (selectionChanged) {\n this.__editor.update(\n () => {\n if ($getNodeByKey(lastNodeKey) !== null) {\n $setSelection($normalizeSelection__EXPERIMENTAL(newSelection));\n }\n },\n { discrete: true },\n );\n }\n }\n });\n }),\n this.__editor.registerNodeTransform(SentinelNode, node => {\n if (!node.getPreviousSibling() || node.getNextSibling()) {\n node.remove();\n return;\n }\n }),\n );\n }\n\n insertDefaultValue(defaultValue: string) {\n if (defaultValue) {\n this.__editor.update(() => {\n $insertNodes([$createTextNode(defaultValue)]);\n });\n }\n }\n\n activateContentCallbacks(\n onContentChange?: ((value: string) => void) | undefined,\n onCountChanged?: ((count: number) => void) | undefined,\n ) {\n this.deactivateContentCallbacks();\n this.__contentChangeCleanup = this.__editor.registerTextContentListener(text => {\n // Remove the sentinel node\n const processed = text.replace(SENTINEL_VALUE, '');\n onContentChange?.(processed);\n onCountChanged?.(processed.length);\n });\n }\n\n deactivateContentCallbacks() {\n this.__contentChangeCleanup?.();\n this.__contentChangeCleanup = undefined;\n }\n\n activatePasteCallback(onPaste: (event: ClipboardEvent) => void) {\n this.__pasteHandlerCleanup = this.__editor.registerCommand(\n PASTE_COMMAND,\n (event: ClipboardEvent) => {\n onPaste(event);\n\n if (event.defaultPrevented) {\n return true;\n }\n\n return false;\n },\n COMMAND_PRIORITY_CRITICAL,\n );\n }\n\n deactivatePasteCallback() {\n this.__pasteHandlerCleanup?.();\n this.__pasteHandlerCleanup = undefined;\n }\n\n activateTrimWhitespace() {\n this.deactivateTrimWhitespace();\n this.__trimWhitespaceCleanup = this.__editor.registerTextContentListener(text => {\n if (text.trim() === '') {\n this.__editor.update(\n () => {\n $getRoot()\n .getAllTextNodes()\n .forEach(node => {\n node.remove();\n });\n },\n // Don't allow undoing this action\n { tag: 'historic' },\n );\n }\n });\n }\n\n deactivateTrimWhitespace() {\n this.__trimWhitespaceCleanup?.();\n this.__trimWhitespaceCleanup = undefined;\n }\n\n setIsDisabled(isDisabled: boolean) {\n this.__editor.setEditable(!isDisabled);\n }\n\n cleanup() {\n this.deactivateContentCallbacks();\n this.deactivateTrimWhitespace();\n this.deactivatePasteCallback();\n this.__baseHandlersCleanup?.();\n this.__baseHandlersCleanup = undefined;\n }\n}\n"],"names":["$createTextNode","$getLeafNodes","$getNodeByKey","$getRoot","$getSelection","$insertNodes","$isRangeSelection","$normalizeSelection__EXPERIMENTAL","$setSelection","COMMAND_PRIORITY_CRITICAL","INSERT_PARAGRAPH_COMMAND","KEY_ENTER_COMMAND","PASTE_COMMAND","mergeRegister","$createSentinelNode","$isSentinelNode","SENTINEL_VALUE","SentinelNode","BasicFunctionalityBase","__enterHandler","event","selection","preventDefault","shiftKey","__editor","dispatchCommand","undefined","insertDefaultValue","defaultValue","update","activateContentCallbacks","onContentChange","onCountChanged","deactivateContentCallbacks","__contentChangeCleanup","registerTextContentListener","text","processed","replace","length","activatePasteCallback","onPaste","__pasteHandlerCleanup","registerCommand","defaultPrevented","deactivatePasteCallback","activateTrimWhitespace","deactivateTrimWhitespace","__trimWhitespaceCleanup","trim","getAllTextNodes","forEach","node","remove","tag","setIsDisabled","isDisabled","setEditable","cleanup","__baseHandlersCleanup","constructor","editor","bind","registerUpdateListener","editorState","read","leaves","lastNode","lastNodeKey","getKey","insertAfter","discrete","previous","getPreviousSibling","isSelected","isCollapsed","anchor","offset","selectStart","selectionChanged","newSelection","clone","getNode","set","focus","registerNodeTransform","getNextSibling"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { TextNode } from '@fluentui-copilot/text-editor';
2
+ export const SENTINEL_VALUE = '\u200b\u200c';
2
3
  export class SentinelNode extends TextNode {
3
4
  static getType() {
4
5
  return 'sentinel';
@@ -12,7 +13,7 @@ export class SentinelNode extends TextNode {
12
13
  constructor(key) {
13
14
  // 2 zero width characters will not be visible in the editor.
14
15
  // These also happen to be markers for CIQ to ignore the following content.
15
- super('\u200b\u200c', key);
16
+ super(SENTINEL_VALUE, key);
16
17
  }
17
18
  }
18
19
  export function $createSentinelNode(key) {
@@ -1 +1 @@
1
- {"version":3,"sources":["SentinelNode.ts"],"sourcesContent":["import type { LexicalNode, NodeKey } from '@fluentui-copilot/text-editor';\nimport { TextNode } from '@fluentui-copilot/text-editor';\n\nexport class SentinelNode extends TextNode {\n constructor(key?: NodeKey) {\n // 2 zero width characters will not be visible in the editor.\n // These also happen to be markers for CIQ to ignore the following content.\n super('\\u200b\\u200c', key);\n }\n\n static getType() {\n return 'sentinel';\n }\n static clone(node: SentinelNode) {\n return new SentinelNode(node.__key);\n }\n\n isToken() {\n return true;\n }\n}\n\nexport function $createSentinelNode(key?: NodeKey) {\n return new SentinelNode(key);\n}\n\nexport function $isSentinelNode(node: LexicalNode | null): node is SentinelNode {\n return node instanceof SentinelNode;\n}\n"],"names":["TextNode","SentinelNode","getType","clone","node","__key","isToken","constructor","key","$createSentinelNode","$isSentinelNode"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;","mappings":"AACA,SAASA,QAAQ,QAAQ,gCAAgC;AAEzD,OAAO,MAAMC,qBAAqBD;IAOhC,OAAOE,UAAU;QACf,OAAO;IACT;IACA,OAAOC,MAAMC,IAAkB,EAAE;QAC/B,OAAO,IAAIH,aAAaG,KAAKC,KAAK;IACpC;IAEAC,UAAU;QACR,OAAO;IACT;IAfAC,YAAYC,GAAa,CAAE;QACzB,6DAA6D;QAC7D,2EAA2E;QAC3E,KAAK,CAAC,gBAAgBA;IACxB;AAYF;AAEA,OAAO,SAASC,oBAAoBD,GAAa;IAC/C,OAAO,IAAIP,aAAaO;AAC1B;AAEA,OAAO,SAASE,gBAAgBN,IAAwB;IACtD,OAAOA,gBAAgBH;AACzB"}
1
+ {"version":3,"sources":["SentinelNode.ts"],"sourcesContent":["import type { LexicalNode, NodeKey } from '@fluentui-copilot/text-editor';\nimport { TextNode } from '@fluentui-copilot/text-editor';\n\nexport const SENTINEL_VALUE = '\\u200b\\u200c';\n\nexport class SentinelNode extends TextNode {\n constructor(key?: NodeKey) {\n // 2 zero width characters will not be visible in the editor.\n // These also happen to be markers for CIQ to ignore the following content.\n super(SENTINEL_VALUE, key);\n }\n\n static getType() {\n return 'sentinel';\n }\n static clone(node: SentinelNode) {\n return new SentinelNode(node.__key);\n }\n\n isToken() {\n return true;\n }\n}\n\nexport function $createSentinelNode(key?: NodeKey) {\n return new SentinelNode(key);\n}\n\nexport function $isSentinelNode(node: LexicalNode | null): node is SentinelNode {\n return node instanceof SentinelNode;\n}\n"],"names":["TextNode","SENTINEL_VALUE","SentinelNode","getType","clone","node","__key","isToken","constructor","key","$createSentinelNode","$isSentinelNode"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;","mappings":"AACA,SAASA,QAAQ,QAAQ,gCAAgC;AAEzD,OAAO,MAAMC,iBAAiB,eAAe;AAE7C,OAAO,MAAMC,qBAAqBF;IAOhC,OAAOG,UAAU;QACf,OAAO;IACT;IACA,OAAOC,MAAMC,IAAkB,EAAE;QAC/B,OAAO,IAAIH,aAAaG,KAAKC,KAAK;IACpC;IAEAC,UAAU;QACR,OAAO;IACT;IAfAC,YAAYC,GAAa,CAAE;QACzB,6DAA6D;QAC7D,2EAA2E;QAC3E,KAAK,CAACR,gBAAgBQ;IACxB;AAYF;AAEA,OAAO,SAASC,oBAAoBD,GAAa;IAC/C,OAAO,IAAIP,aAAaO;AAC1B;AAEA,OAAO,SAASE,gBAAgBN,IAAwB;IACtD,OAAOA,gBAAgBH;AACzB"}
@@ -1,5 +1,6 @@
1
1
  import { _ as _define_property } from "@swc/helpers/_/_define_property";
2
- import { $createTextNode, $getNodeByKey, $getSelection, $setSelection, COMMAND_PRIORITY_LOW, KEY_TAB_COMMAND, mergeRegister } from '@fluentui-copilot/text-editor';
2
+ import { $createTextNode, $getNodeByKey, $getSelection, $isRangeSelection, $isTextNode, $setSelection, COMMAND_PRIORITY_LOW, KEY_TAB_COMMAND, mergeRegister } from '@fluentui-copilot/text-editor';
3
+ import { $isSentinelNode } from '../BasicFunctionality';
3
4
  export class GhostTextPluginBase {
4
5
  cleanup() {
5
6
  var _this___cleanup, _this;
@@ -34,12 +35,14 @@ export class GhostTextPluginBase {
34
35
  let ghostTextNodeKey = null;
35
36
  let lastText = undefined;
36
37
  let justRemovedGhostText = false;
38
+ let justAddedGhostText = false;
37
39
  function $clearGhostText() {
38
40
  const ghostTextNode = ghostTextNodeKey !== null ? $getNodeByKey(ghostTextNodeKey) : null;
39
41
  ghostTextNodeKey = null;
40
42
  lastText = undefined;
41
43
  if (ghostTextNode && ghostTextNode.isAttached()) {
42
44
  ghostTextNode.remove();
45
+ justRemovedGhostText = true;
43
46
  }
44
47
  }
45
48
  function handleGhostTextNodeTransform(node) {
@@ -58,7 +61,6 @@ export class GhostTextPluginBase {
58
61
  $clearGhostText();
59
62
  const selection = $getSelection();
60
63
  if (!text || !selection) {
61
- justRemovedGhostText = true;
62
64
  return;
63
65
  }
64
66
  const selectionCopy = selection.clone();
@@ -67,6 +69,8 @@ export class GhostTextPluginBase {
67
69
  lastText = text;
68
70
  selection.insertNodes([node]);
69
71
  $setSelection(selectionCopy);
72
+ justAddedGhostText = true;
73
+ justRemovedGhostText = false;
70
74
  }, {
71
75
  tag: 'historic'
72
76
  });
@@ -76,12 +80,50 @@ export class GhostTextPluginBase {
76
80
  editorState,
77
81
  prevEditorState
78
82
  } = props;
79
- // If this update was caused by deleting the previous ghost text, don't create a new one until a subsequent update
80
- if (justRemovedGhostText) {
83
+ // If this update was caused by adding or deleting ghost text, don't recheck the ghost text function until a subsequent update
84
+ if (justRemovedGhostText || justAddedGhostText) {
81
85
  justRemovedGhostText = false;
86
+ justAddedGhostText = false;
82
87
  return;
83
88
  }
84
89
  editorState.read(() => {
90
+ // We only update the ghost text if the user selection is inside the input
91
+ const selection = $getSelection();
92
+ if (!$getSelection()) {
93
+ return;
94
+ }
95
+ if ($isRangeSelection(selection) && selection.isCollapsed()) {
96
+ var _selectedNode_getPreviousSibling;
97
+ var _selection_getNodes_at;
98
+ // All the `$isXNode` functions prefer `null` over `undefined`
99
+ const selectedNode = (_selection_getNodes_at = selection.getNodes().at(0)) !== null && _selection_getNodes_at !== void 0 ? _selection_getNodes_at : null;
100
+ const previousNodeKey = selectedNode === null || selectedNode === void 0 ? void 0 : (_selectedNode_getPreviousSibling = selectedNode.getPreviousSibling()) === null || _selectedNode_getPreviousSibling === void 0 ? void 0 : _selectedNode_getPreviousSibling.getKey();
101
+ const previousNodeIsGhostText = previousNodeKey === ghostTextNodeKey;
102
+ // If the ghost text is active and we're navigating past it, act as if the ghost text is not there and move 1 extra character
103
+ if (previousNodeIsGhostText && !$isSentinelNode(selectedNode) && $isTextNode(selectedNode) && selection.anchor.offset === 0) {
104
+ editor.update(() => {
105
+ const selection = $getSelection();
106
+ if ($isRangeSelection(selection)) {
107
+ selection.modify('move', false, 'character');
108
+ }
109
+ $clearGhostText();
110
+ }, {
111
+ tag: 'historic'
112
+ });
113
+ // Defer checking the ghost text until after this update has finished
114
+ return;
115
+ }
116
+ // If the ghost text is the last node before the sentinel, we shouldn't let selection get past it
117
+ if (previousNodeIsGhostText && $isSentinelNode(selectedNode)) {
118
+ editor.update(() => {
119
+ var _$getNodeByKey_getPreviousSibling, _$getNodeByKey;
120
+ (_$getNodeByKey = $getNodeByKey(previousNodeKey)) === null || _$getNodeByKey === void 0 ? void 0 : (_$getNodeByKey_getPreviousSibling = _$getNodeByKey.getPreviousSibling()) === null || _$getNodeByKey_getPreviousSibling === void 0 ? void 0 : _$getNodeByKey_getPreviousSibling.selectEnd();
121
+ }, {
122
+ tag: 'historic'
123
+ });
124
+ return;
125
+ }
126
+ }
85
127
  const promise = this.__$getGhostText(editor, editorState, prevEditorState);
86
128
  promise.then(handleGhostTextResponse).catch(e => console.error(e));
87
129
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["GhostText.base.ts"],"sourcesContent":["import type { EditorState, Klass, LexicalEditor, LexicalNode, UpdateListener } from '@fluentui-copilot/text-editor';\nimport {\n $createTextNode,\n $getNodeByKey,\n $getSelection,\n $setSelection,\n COMMAND_PRIORITY_LOW,\n KEY_TAB_COMMAND,\n mergeRegister,\n} from '@fluentui-copilot/text-editor';\n\nexport type GetGhostTextFunction = (\n editor: LexicalEditor,\n editorState: EditorState,\n prevEditorState: EditorState,\n) => Promise<string | undefined>;\n\nexport interface IGhostTextNode<ComponentPropsType> extends LexicalNode {\n __content: string;\n __id: string;\n __allowCommitting?: boolean;\n __componentProps?: ComponentPropsType;\n __exposeText?: boolean;\n}\n\nexport class GhostTextPluginBase<ComponentPropsType> {\n private __id: string;\n private __$getGhostText: GetGhostTextFunction;\n private __componentProps?: ComponentPropsType;\n private __exposeText?: boolean;\n private __allowCompletion?: boolean;\n\n private __cleanup?: () => void;\n\n cleanup() {\n this.__cleanup?.();\n }\n\n constructor(\n editor: LexicalEditor,\n id: string,\n $getGhostText: GetGhostTextFunction,\n nodeClass: Klass<IGhostTextNode<ComponentPropsType>>,\n createNode: (\n id: string,\n content: string,\n exposeText?: boolean,\n componentProps?: ComponentPropsType,\n ) => IGhostTextNode<ComponentPropsType>,\n componentProps?: ComponentPropsType,\n // Whether or not the ghost text should count as text inside the input for submitting and character count\n exposeText?: boolean,\n allowCompletion?: boolean,\n ) {\n this.__id = id;\n this.__$getGhostText = $getGhostText;\n this.__componentProps = componentProps;\n this.__exposeText = exposeText;\n this.__allowCompletion = allowCompletion;\n\n let ghostTextNodeKey: string | null = null;\n let lastText: string | undefined = undefined;\n let justRemovedGhostText = false;\n\n function $clearGhostText() {\n const ghostTextNode = ghostTextNodeKey !== null ? $getNodeByKey(ghostTextNodeKey) : null;\n ghostTextNodeKey = null;\n lastText = undefined;\n if (ghostTextNode && ghostTextNode.isAttached()) {\n ghostTextNode.remove();\n }\n }\n function handleGhostTextNodeTransform(node: IGhostTextNode<ComponentPropsType>) {\n const key = node.getKey();\n\n if (node.__id === id && key !== ghostTextNodeKey) {\n // Only one ghost text\n node.remove();\n $clearGhostText();\n }\n }\n\n const handleGhostTextResponse = (text?: string) => {\n if (text === lastText) {\n return;\n }\n\n editor.update(\n () => {\n $clearGhostText();\n\n const selection = $getSelection();\n if (!text || !selection) {\n justRemovedGhostText = true;\n return;\n }\n\n const selectionCopy = selection.clone();\n\n const node = createNode(this.__id, text, this.__exposeText, this.__componentProps);\n ghostTextNodeKey = node.getKey();\n lastText = text;\n\n selection.insertNodes([node]);\n $setSelection(selectionCopy);\n },\n { tag: 'historic' },\n );\n };\n\n const handleUpdate: UpdateListener = props => {\n const { editorState, prevEditorState } = props;\n // If this update was caused by deleting the previous ghost text, don't create a new one until a subsequent update\n if (justRemovedGhostText) {\n justRemovedGhostText = false;\n return;\n }\n editorState.read(() => {\n const promise = this.__$getGhostText(editor, editorState, prevEditorState);\n promise.then(handleGhostTextResponse).catch(e => console.error(e));\n });\n };\n\n function unmountGhostText() {\n if (ghostTextNodeKey) {\n editor.update(\n () => {\n $clearGhostText();\n },\n { tag: 'historic' },\n );\n }\n }\n\n function $handleTabCommand(e: KeyboardEvent) {\n if (ghostTextNodeKey === null || lastText === null) {\n return false;\n }\n\n const ghostTextNode = $getNodeByKey(ghostTextNodeKey);\n if (!ghostTextNode) {\n return false;\n }\n\n e.preventDefault();\n\n const textNode = $createTextNode(lastText);\n ghostTextNode.replace(textNode);\n textNode.selectEnd();\n $clearGhostText();\n return true;\n }\n\n this.__cleanup = mergeRegister(\n editor.registerNodeTransform(nodeClass, handleGhostTextNodeTransform),\n editor.registerUpdateListener(handleUpdate),\n this.__allowCompletion ? editor.registerCommand(KEY_TAB_COMMAND, $handleTabCommand, COMMAND_PRIORITY_LOW) : noop,\n unmountGhostText,\n );\n }\n\n setExposeText(exposeText?: boolean) {\n this.__exposeText = exposeText;\n }\n\n setComponentProps(componentProps?: ComponentPropsType) {\n this.__componentProps = componentProps;\n }\n\n setGetGhostText($getGhostText: GetGhostTextFunction) {\n this.__$getGhostText = $getGhostText;\n }\n\n setAllowCompletion(allowCompletion?: boolean) {\n this.__allowCompletion = allowCompletion;\n }\n}\nfunction noop(): void {\n return;\n}\n"],"names":["$createTextNode","$getNodeByKey","$getSelection","$setSelection","COMMAND_PRIORITY_LOW","KEY_TAB_COMMAND","mergeRegister","GhostTextPluginBase","cleanup","__cleanup","setExposeText","exposeText","__exposeText","setComponentProps","componentProps","__componentProps","setGetGhostText","$getGhostText","__$getGhostText","setAllowCompletion","allowCompletion","__allowCompletion","constructor","editor","id","nodeClass","createNode","__id","ghostTextNodeKey","lastText","undefined","justRemovedGhostText","$clearGhostText","ghostTextNode","isAttached","remove","handleGhostTextNodeTransform","node","key","getKey","handleGhostTextResponse","text","update","selection","selectionCopy","clone","insertNodes","tag","handleUpdate","props","editorState","prevEditorState","read","promise","then","catch","e","console","error","unmountGhostText","$handleTabCommand","preventDefault","textNode","replace","selectEnd","registerNodeTransform","registerUpdateListener","registerCommand","noop"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
1
+ {"version":3,"sources":["GhostText.base.ts"],"sourcesContent":["import type { EditorState, Klass, LexicalEditor, LexicalNode, UpdateListener } from '@fluentui-copilot/text-editor';\nimport {\n $createTextNode,\n $getNodeByKey,\n $getSelection,\n $isRangeSelection,\n $isTextNode,\n $setSelection,\n COMMAND_PRIORITY_LOW,\n KEY_TAB_COMMAND,\n mergeRegister,\n} from '@fluentui-copilot/text-editor';\nimport { $isSentinelNode } from '../BasicFunctionality';\n\nexport type GetGhostTextFunction = (\n editor: LexicalEditor,\n editorState: EditorState,\n prevEditorState: EditorState,\n) => Promise<string | undefined>;\n\nexport interface IGhostTextNode<ComponentPropsType> extends LexicalNode {\n __content: string;\n __id: string;\n __allowCommitting?: boolean;\n __componentProps?: ComponentPropsType;\n __exposeText?: boolean;\n}\n\nexport class GhostTextPluginBase<ComponentPropsType> {\n private __id: string;\n private __$getGhostText: GetGhostTextFunction;\n private __componentProps?: ComponentPropsType;\n private __exposeText?: boolean;\n private __allowCompletion?: boolean;\n\n private __cleanup?: () => void;\n\n cleanup() {\n this.__cleanup?.();\n }\n\n constructor(\n editor: LexicalEditor,\n id: string,\n $getGhostText: GetGhostTextFunction,\n nodeClass: Klass<IGhostTextNode<ComponentPropsType>>,\n createNode: (\n id: string,\n content: string,\n exposeText?: boolean,\n componentProps?: ComponentPropsType,\n ) => IGhostTextNode<ComponentPropsType>,\n componentProps?: ComponentPropsType,\n // Whether or not the ghost text should count as text inside the input for submitting and character count\n exposeText?: boolean,\n allowCompletion?: boolean,\n ) {\n this.__id = id;\n this.__$getGhostText = $getGhostText;\n this.__componentProps = componentProps;\n this.__exposeText = exposeText;\n this.__allowCompletion = allowCompletion;\n\n let ghostTextNodeKey: string | null = null;\n let lastText: string | undefined = undefined;\n let justRemovedGhostText = false;\n let justAddedGhostText = false;\n\n function $clearGhostText() {\n const ghostTextNode = ghostTextNodeKey !== null ? $getNodeByKey(ghostTextNodeKey) : null;\n ghostTextNodeKey = null;\n lastText = undefined;\n if (ghostTextNode && ghostTextNode.isAttached()) {\n ghostTextNode.remove();\n justRemovedGhostText = true;\n }\n }\n function handleGhostTextNodeTransform(node: IGhostTextNode<ComponentPropsType>) {\n const key = node.getKey();\n\n if (node.__id === id && key !== ghostTextNodeKey) {\n // Only one ghost text\n node.remove();\n $clearGhostText();\n }\n }\n\n const handleGhostTextResponse = (text?: string) => {\n if (text === lastText) {\n return;\n }\n\n editor.update(\n () => {\n $clearGhostText();\n\n const selection = $getSelection();\n if (!text || !selection) {\n return;\n }\n\n const selectionCopy = selection.clone();\n\n const node = createNode(this.__id, text, this.__exposeText, this.__componentProps);\n ghostTextNodeKey = node.getKey();\n lastText = text;\n\n selection.insertNodes([node]);\n $setSelection(selectionCopy);\n justAddedGhostText = true;\n justRemovedGhostText = false;\n },\n { tag: 'historic' },\n );\n };\n\n const handleUpdate: UpdateListener = props => {\n const { editorState, prevEditorState } = props;\n\n // If this update was caused by adding or deleting ghost text, don't recheck the ghost text function until a subsequent update\n if (justRemovedGhostText || justAddedGhostText) {\n justRemovedGhostText = false;\n justAddedGhostText = false;\n return;\n }\n\n editorState.read(() => {\n // We only update the ghost text if the user selection is inside the input\n const selection = $getSelection();\n if (!$getSelection()) {\n return;\n }\n\n if ($isRangeSelection(selection) && selection.isCollapsed()) {\n // All the `$isXNode` functions prefer `null` over `undefined`\n const selectedNode = selection.getNodes().at(0) ?? null;\n const previousNodeKey = selectedNode?.getPreviousSibling()?.getKey();\n const previousNodeIsGhostText = previousNodeKey === ghostTextNodeKey;\n // If the ghost text is active and we're navigating past it, act as if the ghost text is not there and move 1 extra character\n if (\n previousNodeIsGhostText &&\n !$isSentinelNode(selectedNode) &&\n $isTextNode(selectedNode) &&\n selection.anchor.offset === 0\n ) {\n editor.update(\n () => {\n const selection = $getSelection();\n if ($isRangeSelection(selection)) {\n selection.modify('move', false, 'character');\n }\n $clearGhostText();\n },\n { tag: 'historic' },\n );\n // Defer checking the ghost text until after this update has finished\n return;\n }\n\n // If the ghost text is the last node before the sentinel, we shouldn't let selection get past it\n if (previousNodeIsGhostText && $isSentinelNode(selectedNode)) {\n editor.update(\n () => {\n $getNodeByKey(previousNodeKey)?.getPreviousSibling()?.selectEnd();\n },\n { tag: 'historic' },\n );\n return;\n }\n }\n\n const promise = this.__$getGhostText(editor, editorState, prevEditorState);\n promise.then(handleGhostTextResponse).catch(e => console.error(e));\n });\n };\n\n function unmountGhostText() {\n if (ghostTextNodeKey) {\n editor.update(\n () => {\n $clearGhostText();\n },\n { tag: 'historic' },\n );\n }\n }\n\n function $handleTabCommand(e: KeyboardEvent) {\n if (ghostTextNodeKey === null || lastText === null) {\n return false;\n }\n\n const ghostTextNode = $getNodeByKey(ghostTextNodeKey);\n if (!ghostTextNode) {\n return false;\n }\n\n e.preventDefault();\n\n const textNode = $createTextNode(lastText);\n ghostTextNode.replace(textNode);\n textNode.selectEnd();\n $clearGhostText();\n return true;\n }\n\n this.__cleanup = mergeRegister(\n editor.registerNodeTransform(nodeClass, handleGhostTextNodeTransform),\n editor.registerUpdateListener(handleUpdate),\n this.__allowCompletion ? editor.registerCommand(KEY_TAB_COMMAND, $handleTabCommand, COMMAND_PRIORITY_LOW) : noop,\n unmountGhostText,\n );\n }\n\n setExposeText(exposeText?: boolean) {\n this.__exposeText = exposeText;\n }\n\n setComponentProps(componentProps?: ComponentPropsType) {\n this.__componentProps = componentProps;\n }\n\n setGetGhostText($getGhostText: GetGhostTextFunction) {\n this.__$getGhostText = $getGhostText;\n }\n\n setAllowCompletion(allowCompletion?: boolean) {\n this.__allowCompletion = allowCompletion;\n }\n}\nfunction noop(): void {\n return;\n}\n"],"names":["$createTextNode","$getNodeByKey","$getSelection","$isRangeSelection","$isTextNode","$setSelection","COMMAND_PRIORITY_LOW","KEY_TAB_COMMAND","mergeRegister","$isSentinelNode","GhostTextPluginBase","cleanup","__cleanup","setExposeText","exposeText","__exposeText","setComponentProps","componentProps","__componentProps","setGetGhostText","$getGhostText","__$getGhostText","setAllowCompletion","allowCompletion","__allowCompletion","constructor","editor","id","nodeClass","createNode","__id","ghostTextNodeKey","lastText","undefined","justRemovedGhostText","justAddedGhostText","$clearGhostText","ghostTextNode","isAttached","remove","handleGhostTextNodeTransform","node","key","getKey","handleGhostTextResponse","text","update","selection","selectionCopy","clone","insertNodes","tag","handleUpdate","props","editorState","prevEditorState","read","isCollapsed","selectedNode","getNodes","at","previousNodeKey","getPreviousSibling","previousNodeIsGhostText","anchor","offset","modify","selectEnd","promise","then","catch","e","console","error","unmountGhostText","$handleTabCommand","preventDefault","textNode","replace","registerNodeTransform","registerUpdateListener","registerCommand","noop"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { _ as _define_property } from "@swc/helpers/_/_define_property";
2
2
  import { $createParagraphNode, $createRangeSelection, $createTextNode, $getLeafNodes, $getRoot, $getSelection, $isTextNode, $normalizeSelection__EXPERIMENTAL, $setSelection } from '@fluentui-copilot/text-editor';
3
+ import { SENTINEL_VALUE, $isSentinelNode } from '../BasicFunctionality';
3
4
  export class ImperativeControlBase {
4
5
  moveCursor(location) {
5
6
  this.__editor.update(() => {
@@ -52,12 +53,23 @@ export class ImperativeControlBase {
52
53
  insertTextAtCursor(text) {
53
54
  this.__editor.update(() => {
54
55
  var _$getSelection;
55
- (_$getSelection = $getSelection()) === null || _$getSelection === void 0 ? void 0 : _$getSelection.insertText(text);
56
+ const selection = (_$getSelection = $getSelection()) !== null && _$getSelection !== void 0 ? _$getSelection : $getRoot().selectEnd();
57
+ selection.insertText(text);
56
58
  });
57
59
  }
58
- getInputText() {
60
+ getInputText(transform) {
59
61
  return this.__editor.getEditorState().read(() => {
60
- return $getRoot().getTextContent();
62
+ if (!transform) {
63
+ return $getRoot().getTextContent().replace(SENTINEL_VALUE, '');
64
+ }
65
+ const children = $getLeafNodes($getRoot());
66
+ const transformedNodeTexts = [];
67
+ for (const currentNode of children) {
68
+ if (!$isSentinelNode(currentNode)) {
69
+ transformedNodeTexts.push(transform(currentNode));
70
+ }
71
+ }
72
+ return transformedNodeTexts.join('');
61
73
  });
62
74
  }
63
75
  scrollToBottom() {
@@ -1 +1 @@
1
- {"version":3,"sources":["ImperativeControl.base.ts"],"sourcesContent":["import type { LexicalEditor } from '@fluentui-copilot/text-editor';\nimport {\n $createParagraphNode,\n $createRangeSelection,\n $createTextNode,\n $getLeafNodes,\n $getRoot,\n $getSelection,\n $isTextNode,\n $normalizeSelection__EXPERIMENTAL,\n $setSelection,\n} from '@fluentui-copilot/text-editor';\n\nexport interface IImperativeControlBase {\n setInputText: (inputText: string) => void;\n appendText: (text: string) => void;\n prependText: (text: string) => void;\n insertTextAtCursor: (text: string) => void;\n getInputText: () => string;\n scrollToBottom: () => void;\n moveCursor: (location: number) => void;\n}\n\nexport class ImperativeControlBase implements IImperativeControlBase {\n private __editor: LexicalEditor;\n\n constructor(editor: LexicalEditor) {\n this.__editor = editor;\n }\n\n moveCursor(location: number): void {\n this.__editor.update(() => {\n const children = $getLeafNodes($getRoot());\n\n let baseOffset = 0;\n let currentNode = children.shift();\n while (baseOffset < location && currentNode) {\n const nodeLength =\n $isTextNode(currentNode) && !currentNode.isToken()\n ? currentNode.getTextContent().length\n : // Token text nodes and non-text nodes are considered to be a single entry in the input\n 1;\n\n if (baseOffset + nodeLength >= location) {\n const elementType = $isTextNode(currentNode) ? 'text' : 'element';\n const localOffset = location - baseOffset;\n const nodeKey = currentNode.getKey();\n\n const selection = $createRangeSelection();\n selection.anchor.set(nodeKey, localOffset, elementType);\n selection.focus.set(nodeKey, localOffset, elementType);\n\n $setSelection($normalizeSelection__EXPERIMENTAL(selection));\n return;\n }\n\n baseOffset += nodeLength;\n currentNode = children.shift();\n }\n\n if (location > baseOffset) {\n $getRoot().selectEnd();\n }\n });\n }\n setInputText(inputText: string) {\n this.__editor.update(() => {\n const root = $getRoot();\n root.clear();\n if (inputText !== '') {\n const newParagraph = $createParagraphNode();\n const newText = $createTextNode(inputText);\n\n newParagraph.append(newText);\n root.append(newParagraph);\n root.selectEnd();\n }\n });\n }\n appendText(text: string) {\n this.__editor.update(() => {\n $getRoot().selectEnd().insertText(text);\n });\n }\n prependText(text: string) {\n this.__editor.update(() => {\n $getRoot().selectStart().insertText(text);\n });\n }\n insertTextAtCursor(text: string) {\n this.__editor.update(() => {\n $getSelection()?.insertText(text);\n });\n }\n getInputText() {\n return this.__editor.getEditorState().read(() => {\n return $getRoot().getTextContent();\n });\n }\n scrollToBottom() {\n this.__editor.getRootElement()?.scrollIntoView({ behavior: 'smooth', block: 'end' });\n return;\n }\n}\n"],"names":["$createParagraphNode","$createRangeSelection","$createTextNode","$getLeafNodes","$getRoot","$getSelection","$isTextNode","$normalizeSelection__EXPERIMENTAL","$setSelection","ImperativeControlBase","moveCursor","location","__editor","update","children","baseOffset","currentNode","shift","nodeLength","isToken","getTextContent","length","elementType","localOffset","nodeKey","getKey","selection","anchor","set","focus","selectEnd","setInputText","inputText","root","clear","newParagraph","newText","append","appendText","text","insertText","prependText","selectStart","insertTextAtCursor","getInputText","getEditorState","read","scrollToBottom","getRootElement","scrollIntoView","behavior","block","constructor","editor"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":";AACA,SACEA,oBAAoB,EACpBC,qBAAqB,EACrBC,eAAe,EACfC,aAAa,EACbC,QAAQ,EACRC,aAAa,EACbC,WAAW,EACXC,iCAAiC,EACjCC,aAAa,QACR,gCAAgC;AAYvC,OAAO,MAAMC;IAOXC,WAAWC,QAAgB,EAAQ;QACjC,IAAI,CAACC,QAAQ,CAACC,MAAM,CAAC;YACnB,MAAMC,WAAWX,cAAcC;YAE/B,IAAIW,aAAa;YACjB,IAAIC,cAAcF,SAASG,KAAK;YAChC,MAAOF,aAAaJ,YAAYK,YAAa;gBAC3C,MAAME,aACJZ,YAAYU,gBAAgB,CAACA,YAAYG,OAAO,KAC5CH,YAAYI,cAAc,GAAGC,MAAM,GAEnC;gBAEN,IAAIN,aAAaG,cAAcP,UAAU;oBACvC,MAAMW,cAAchB,YAAYU,eAAe,SAAS;oBACxD,MAAMO,cAAcZ,WAAWI;oBAC/B,MAAMS,UAAUR,YAAYS,MAAM;oBAElC,MAAMC,YAAYzB;oBAClByB,UAAUC,MAAM,CAACC,GAAG,CAACJ,SAASD,aAAaD;oBAC3CI,UAAUG,KAAK,CAACD,GAAG,CAACJ,SAASD,aAAaD;oBAE1Cd,cAAcD,kCAAkCmB;oBAChD;gBACF;gBAEAX,cAAcG;gBACdF,cAAcF,SAASG,KAAK;YAC9B;YAEA,IAAIN,WAAWI,YAAY;gBACzBX,WAAW0B,SAAS;YACtB;QACF;IACF;IACAC,aAAaC,SAAiB,EAAE;QAC9B,IAAI,CAACpB,QAAQ,CAACC,MAAM,CAAC;YACnB,MAAMoB,OAAO7B;YACb6B,KAAKC,KAAK;YACV,IAAIF,cAAc,IAAI;gBACpB,MAAMG,eAAenC;gBACrB,MAAMoC,UAAUlC,gBAAgB8B;gBAEhCG,aAAaE,MAAM,CAACD;gBACpBH,KAAKI,MAAM,CAACF;gBACZF,KAAKH,SAAS;YAChB;QACF;IACF;IACAQ,WAAWC,IAAY,EAAE;QACvB,IAAI,CAAC3B,QAAQ,CAACC,MAAM,CAAC;YACnBT,WAAW0B,SAAS,GAAGU,UAAU,CAACD;QACpC;IACF;IACAE,YAAYF,IAAY,EAAE;QACxB,IAAI,CAAC3B,QAAQ,CAACC,MAAM,CAAC;YACnBT,WAAWsC,WAAW,GAAGF,UAAU,CAACD;QACtC;IACF;IACAI,mBAAmBJ,IAAY,EAAE;QAC/B,IAAI,CAAC3B,QAAQ,CAACC,MAAM,CAAC;gBACnBR;aAAAA,iBAAAA,6BAAAA,qCAAAA,eAAiBmC,UAAU,CAACD;QAC9B;IACF;IACAK,eAAe;QACb,OAAO,IAAI,CAAChC,QAAQ,CAACiC,cAAc,GAAGC,IAAI,CAAC;YACzC,OAAO1C,WAAWgB,cAAc;QAClC;IACF;IACA2B,iBAAiB;YACf;SAAA,gCAAA,IAAI,CAACnC,QAAQ,CAACoC,cAAc,gBAA5B,oDAAA,8BAAgCC,cAAc,CAAC;YAAEC,UAAU;YAAUC,OAAO;QAAM;QAClF;IACF;IA5EAC,YAAYC,MAAqB,CAAE;QAFnC,uBAAQzC,YAAR,KAAA;QAGE,IAAI,CAACA,QAAQ,GAAGyC;IAClB;AA2EF"}
1
+ {"version":3,"sources":["ImperativeControl.base.ts"],"sourcesContent":["import type { LexicalEditor, LexicalNode } from '@fluentui-copilot/text-editor';\nimport {\n $createParagraphNode,\n $createRangeSelection,\n $createTextNode,\n $getLeafNodes,\n $getRoot,\n $getSelection,\n $isTextNode,\n $normalizeSelection__EXPERIMENTAL,\n $setSelection,\n} from '@fluentui-copilot/text-editor';\nimport { SENTINEL_VALUE, $isSentinelNode } from '../BasicFunctionality';\n\nexport interface IImperativeControlBase {\n setInputText: (inputText: string) => void;\n appendText: (text: string) => void;\n prependText: (text: string) => void;\n insertTextAtCursor: (text: string) => void;\n /**\n * @param transform will be called for each Lexical node in the input. This enables custom string representation for each node.\n */\n getInputText: (transform?: (node: LexicalNode) => string) => string;\n scrollToBottom: () => void;\n moveCursor: (location: number) => void;\n}\n\nexport class ImperativeControlBase implements IImperativeControlBase {\n private __editor: LexicalEditor;\n\n constructor(editor: LexicalEditor) {\n this.__editor = editor;\n }\n\n moveCursor(location: number): void {\n this.__editor.update(() => {\n const children = $getLeafNodes($getRoot());\n\n let baseOffset = 0;\n let currentNode = children.shift();\n while (baseOffset < location && currentNode) {\n const nodeLength =\n $isTextNode(currentNode) && !currentNode.isToken()\n ? currentNode.getTextContent().length\n : // Token text nodes and non-text nodes are considered to be a single entry in the input\n 1;\n\n if (baseOffset + nodeLength >= location) {\n const elementType = $isTextNode(currentNode) ? 'text' : 'element';\n const localOffset = location - baseOffset;\n const nodeKey = currentNode.getKey();\n\n const selection = $createRangeSelection();\n selection.anchor.set(nodeKey, localOffset, elementType);\n selection.focus.set(nodeKey, localOffset, elementType);\n\n $setSelection($normalizeSelection__EXPERIMENTAL(selection));\n return;\n }\n\n baseOffset += nodeLength;\n currentNode = children.shift();\n }\n\n if (location > baseOffset) {\n $getRoot().selectEnd();\n }\n });\n }\n setInputText(inputText: string) {\n this.__editor.update(() => {\n const root = $getRoot();\n root.clear();\n if (inputText !== '') {\n const newParagraph = $createParagraphNode();\n const newText = $createTextNode(inputText);\n\n newParagraph.append(newText);\n root.append(newParagraph);\n root.selectEnd();\n }\n });\n }\n appendText(text: string) {\n this.__editor.update(() => {\n $getRoot().selectEnd().insertText(text);\n });\n }\n prependText(text: string) {\n this.__editor.update(() => {\n $getRoot().selectStart().insertText(text);\n });\n }\n insertTextAtCursor(text: string) {\n this.__editor.update(() => {\n const selection = $getSelection() ?? $getRoot().selectEnd();\n selection.insertText(text);\n });\n }\n getInputText(transform?: (node: LexicalNode) => string) {\n return this.__editor.getEditorState().read(() => {\n if (!transform) {\n return $getRoot().getTextContent().replace(SENTINEL_VALUE, '');\n }\n const children = $getLeafNodes($getRoot());\n const transformedNodeTexts: string[] = [];\n\n for (const currentNode of children) {\n if (!$isSentinelNode(currentNode)) {\n transformedNodeTexts.push(transform(currentNode));\n }\n }\n\n return transformedNodeTexts.join('');\n });\n }\n scrollToBottom() {\n this.__editor.getRootElement()?.scrollIntoView({ behavior: 'smooth', block: 'end' });\n return;\n }\n}\n"],"names":["$createParagraphNode","$createRangeSelection","$createTextNode","$getLeafNodes","$getRoot","$getSelection","$isTextNode","$normalizeSelection__EXPERIMENTAL","$setSelection","SENTINEL_VALUE","$isSentinelNode","ImperativeControlBase","moveCursor","location","__editor","update","children","baseOffset","currentNode","shift","nodeLength","isToken","getTextContent","length","elementType","localOffset","nodeKey","getKey","selection","anchor","set","focus","selectEnd","setInputText","inputText","root","clear","newParagraph","newText","append","appendText","text","insertText","prependText","selectStart","insertTextAtCursor","getInputText","transform","getEditorState","read","replace","transformedNodeTexts","push","join","scrollToBottom","getRootElement","scrollIntoView","behavior","block","constructor","editor"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { BasicFunctionalityBase, SentinelNode, $createSentinelNode, $isSentinelNode } from './BasicFunctionality';
1
+ export { BasicFunctionalityBase, SentinelNode, $createSentinelNode, $isSentinelNode, SENTINEL_VALUE } from './BasicFunctionality';
2
2
  export { ChatInputEntityPluginBase } from './ChatInputEntity';
3
3
  export { GhostTextPluginBase } from './GhostText';
4
4
  export { ImperativeControlBase } from './ImperativeControl';
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["index.ts"],"sourcesContent":["export { BasicFunctionalityBase, SentinelNode, $createSentinelNode, $isSentinelNode } from './BasicFunctionality';\nexport type { IBasicFunctionalityBase } from './BasicFunctionality';\n\nexport { ChatInputEntityPluginBase } from './ChatInputEntity';\nexport type {\n ChatInputEntityData,\n ChatInputEntityPluginProps,\n IChatInputEntityPluginBase,\n IEntityNode,\n} from './ChatInputEntity';\n\nexport { GhostTextPluginBase } from './GhostText';\nexport type { GetGhostTextFunction, IGhostTextNode } from './GhostText';\n\nexport { ImperativeControlBase } from './ImperativeControl';\nexport type { IImperativeControlBase } from './ImperativeControl';\n\nexport { ManualGhostTextBase } from './ManualGhostText';\nexport type { IManualGhostTextBase } from './ManualGhostText';\n"],"names":["BasicFunctionalityBase","SentinelNode","$createSentinelNode","$isSentinelNode","ChatInputEntityPluginBase","GhostTextPluginBase","ImperativeControlBase","ManualGhostTextBase"],"rangeMappings":";;;;","mappings":"AAAA,SAASA,sBAAsB,EAAEC,YAAY,EAAEC,mBAAmB,EAAEC,eAAe,QAAQ,uBAAuB;AAGlH,SAASC,yBAAyB,QAAQ,oBAAoB;AAQ9D,SAASC,mBAAmB,QAAQ,cAAc;AAGlD,SAASC,qBAAqB,QAAQ,sBAAsB;AAG5D,SAASC,mBAAmB,QAAQ,oBAAoB"}
1
+ {"version":3,"sources":["index.ts"],"sourcesContent":["export {\n BasicFunctionalityBase,\n SentinelNode,\n $createSentinelNode,\n $isSentinelNode,\n SENTINEL_VALUE,\n} from './BasicFunctionality';\nexport type { IBasicFunctionalityBase } from './BasicFunctionality';\n\nexport { ChatInputEntityPluginBase } from './ChatInputEntity';\nexport type {\n ChatInputEntityData,\n ChatInputEntityPluginProps,\n IChatInputEntityPluginBase,\n IEntityNode,\n} from './ChatInputEntity';\n\nexport { GhostTextPluginBase } from './GhostText';\nexport type { GetGhostTextFunction, IGhostTextNode } from './GhostText';\n\nexport { ImperativeControlBase } from './ImperativeControl';\nexport type { IImperativeControlBase } from './ImperativeControl';\n\nexport { ManualGhostTextBase } from './ManualGhostText';\nexport type { IManualGhostTextBase } from './ManualGhostText';\n"],"names":["BasicFunctionalityBase","SentinelNode","$createSentinelNode","$isSentinelNode","SENTINEL_VALUE","ChatInputEntityPluginBase","GhostTextPluginBase","ImperativeControlBase","ManualGhostTextBase"],"rangeMappings":";;;;","mappings":"AAAA,SACEA,sBAAsB,EACtBC,YAAY,EACZC,mBAAmB,EACnBC,eAAe,EACfC,cAAc,QACT,uBAAuB;AAG9B,SAASC,yBAAyB,QAAQ,oBAAoB;AAQ9D,SAASC,mBAAmB,QAAQ,cAAc;AAGlD,SAASC,qBAAqB,QAAQ,sBAAsB;AAG5D,SAASC,mBAAmB,QAAQ,oBAAoB"}
@@ -40,7 +40,7 @@ class BasicFunctionalityBase {
40
40
  this.deactivateContentCallbacks();
41
41
  this.__contentChangeCleanup = this.__editor.registerTextContentListener((text)=>{
42
42
  // Remove the sentinel node
43
- const processed = text.replace('\u200b\u200c', '');
43
+ const processed = text.replace(_SentinelNode.SENTINEL_VALUE, '');
44
44
  onContentChange === null || onContentChange === void 0 ? void 0 : onContentChange(processed);
45
45
  onCountChanged === null || onCountChanged === void 0 ? void 0 : onCountChanged(processed.length);
46
46
  });
@@ -115,19 +115,22 @@ class BasicFunctionalityBase {
115
115
  if (leaves.length === 0) {
116
116
  return;
117
117
  }
118
- const sentinel = leaves[leaves.length - 1];
118
+ const lastNode = leaves[leaves.length - 1];
119
+ const lastNodeKey = lastNode.getKey();
119
120
  // If the last node isn't a sentinel, add one
120
- if (!(0, _SentinelNode.$isSentinelNode)(sentinel)) {
121
+ if (!(0, _SentinelNode.$isSentinelNode)(lastNode)) {
121
122
  this.__editor.update(()=>{
122
- sentinel.insertAfter((0, _SentinelNode.$createSentinelNode)());
123
+ var // We find the node by its key again in case the node was removed before this update runs
124
+ _$getNodeByKey;
125
+ (_$getNodeByKey = (0, _texteditor.$getNodeByKey)(lastNodeKey)) === null || _$getNodeByKey === void 0 ? void 0 : _$getNodeByKey.insertAfter((0, _SentinelNode.$createSentinelNode)());
123
126
  }, {
124
127
  discrete: true
125
128
  });
126
129
  return;
127
130
  }
128
131
  // If the sentinel node is not selected, we're done
129
- const previous = sentinel.getPreviousSibling();
130
- if (!previous || !sentinel.isSelected()) {
132
+ const previous = lastNode.getPreviousSibling();
133
+ if (!previous || !lastNode.isSelected()) {
131
134
  return;
132
135
  }
133
136
  const selection = (0, _texteditor.$getSelection)();
@@ -139,7 +142,8 @@ class BasicFunctionalityBase {
139
142
  // where selection is ill-defined.
140
143
  if (selection.isCollapsed() && selection.anchor.offset > 0) {
141
144
  this.__editor.update(()=>{
142
- sentinel.selectStart();
145
+ var _$getNodeByKey;
146
+ (_$getNodeByKey = (0, _texteditor.$getNodeByKey)(lastNodeKey)) === null || _$getNodeByKey === void 0 ? void 0 : _$getNodeByKey.selectStart();
143
147
  }, {
144
148
  discrete: true
145
149
  });
@@ -149,17 +153,19 @@ class BasicFunctionalityBase {
149
153
  if (!selection.isCollapsed()) {
150
154
  let selectionChanged = false;
151
155
  const newSelection = selection.clone();
152
- if (newSelection.anchor.getNode() === sentinel && newSelection.anchor.offset > 0) {
153
- newSelection.anchor.set(sentinel.getKey(), 0, 'text');
156
+ if (newSelection.anchor.getNode() === lastNode && newSelection.anchor.offset > 0) {
157
+ newSelection.anchor.set(lastNodeKey, 0, 'text');
154
158
  selectionChanged = true;
155
159
  }
156
- if (newSelection.focus.getNode() === sentinel && newSelection.focus.offset > 0) {
157
- newSelection.focus.set(sentinel.getKey(), 0, 'text');
160
+ if (newSelection.focus.getNode() === lastNode && newSelection.focus.offset > 0) {
161
+ newSelection.focus.set(lastNodeKey, 0, 'text');
158
162
  selectionChanged = true;
159
163
  }
160
164
  if (selectionChanged) {
161
165
  this.__editor.update(()=>{
162
- (0, _texteditor.$setSelection)((0, _texteditor.$normalizeSelection__EXPERIMENTAL)(newSelection));
166
+ if ((0, _texteditor.$getNodeByKey)(lastNodeKey) !== null) {
167
+ (0, _texteditor.$setSelection)((0, _texteditor.$normalizeSelection__EXPERIMENTAL)(newSelection));
168
+ }
163
169
  }, {
164
170
  discrete: true
165
171
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["BasicFunctionality.base.ts"],"sourcesContent":["import type { LexicalEditor } from '@fluentui-copilot/text-editor';\nimport {\n $createTextNode,\n $getLeafNodes,\n $getRoot,\n $getSelection,\n $insertNodes,\n $isRangeSelection,\n $normalizeSelection__EXPERIMENTAL,\n $setSelection,\n COMMAND_PRIORITY_CRITICAL,\n INSERT_PARAGRAPH_COMMAND,\n KEY_ENTER_COMMAND,\n PASTE_COMMAND,\n mergeRegister,\n} from '@fluentui-copilot/text-editor';\nimport { $createSentinelNode, $isSentinelNode, SentinelNode } from './SentinelNode';\n\nexport interface IBasicFunctionalityBase {\n insertDefaultValue: (defaultValue: string) => void;\n setIsDisabled: (isDisabled: boolean) => void;\n activateContentCallbacks(onContentChange?: (value: string) => void, onCountChanged?: (count: number) => void): void;\n deactivateContentCallbacks(): void;\n activateTrimWhitespace(): void;\n deactivateTrimWhitespace(): void;\n activatePasteCallback(onPaste: (event: ClipboardEvent) => void): void;\n deactivatePasteCallback(): void;\n cleanup(): void;\n}\n\nexport class BasicFunctionalityBase implements IBasicFunctionalityBase {\n private __editor: LexicalEditor;\n private __contentChangeCleanup?: () => void;\n private __trimWhitespaceCleanup?: () => void;\n private __baseHandlersCleanup?: () => void;\n private __pasteHandlerCleanup?: () => void;\n\n private __enterHandler(event: KeyboardEvent) {\n const selection = $getSelection();\n if (!$isRangeSelection(selection)) {\n return false;\n }\n\n if (event === null) {\n return false;\n }\n\n event.preventDefault();\n\n if (event.shiftKey) {\n return this.__editor.dispatchCommand(INSERT_PARAGRAPH_COMMAND, undefined);\n }\n\n // Mark event handled to override default behavior\n return true;\n }\n\n constructor(editor: LexicalEditor) {\n this.__editor = editor;\n\n this.__baseHandlersCleanup = mergeRegister(\n this.__editor.registerCommand(KEY_ENTER_COMMAND, this.__enterHandler.bind(this), COMMAND_PRIORITY_CRITICAL),\n\n // Add a sentinel node at the end of the input when there is content.\n // This sentinel node fixes a number of issues.\n // In Safari, Lexical's behaviour of adding <br /> tags to the end of the input when it ends\n // in a decorator node causes cursor location issues: https://github.com/facebook/lexical/issues/4487\n // Otherwise, when a decorator node is the last node in the input, the cursor can't move past it.\n // Adding an invisible text node that doesn't contribute to the content and can't be selected to the end of the input\n // mitigates these issues.\n this.__editor.registerUpdateListener(({ editorState }) => {\n editorState.read(() => {\n const leaves = $getLeafNodes($getRoot());\n if (leaves.length === 0) {\n return;\n }\n\n const sentinel = leaves[leaves.length - 1];\n\n // If the last node isn't a sentinel, add one\n if (!$isSentinelNode(sentinel)) {\n this.__editor.update(\n () => {\n sentinel.insertAfter($createSentinelNode());\n },\n { discrete: true },\n );\n return;\n }\n\n // If the sentinel node is not selected, we're done\n const previous = sentinel.getPreviousSibling();\n if (!previous || !sentinel.isSelected()) {\n return;\n }\n\n const selection = $getSelection();\n if (!$isRangeSelection(selection)) {\n return;\n }\n\n // If the cursor is inside the sentinel node, move it out (next to the beginning)\n // We allow selection on the boundary of the sentinel in case the adjacent node is a decorator node\n // where selection is ill-defined.\n if (selection.isCollapsed() && selection.anchor.offset > 0) {\n this.__editor.update(\n () => {\n sentinel.selectStart();\n },\n { discrete: true },\n );\n return;\n }\n\n // If the selection is a range which includes the sentinel, modify the range to exclude it\n if (!selection.isCollapsed()) {\n let selectionChanged = false;\n const newSelection = selection.clone();\n\n if (newSelection.anchor.getNode() === sentinel && newSelection.anchor.offset > 0) {\n newSelection.anchor.set(sentinel.getKey(), 0, 'text');\n selectionChanged = true;\n }\n if (newSelection.focus.getNode() === sentinel && newSelection.focus.offset > 0) {\n newSelection.focus.set(sentinel.getKey(), 0, 'text');\n selectionChanged = true;\n }\n\n if (selectionChanged) {\n this.__editor.update(\n () => {\n $setSelection($normalizeSelection__EXPERIMENTAL(newSelection));\n },\n { discrete: true },\n );\n }\n }\n });\n }),\n this.__editor.registerNodeTransform(SentinelNode, node => {\n if (!node.getPreviousSibling() || node.getNextSibling()) {\n node.remove();\n return;\n }\n }),\n );\n }\n\n insertDefaultValue(defaultValue: string) {\n if (defaultValue) {\n this.__editor.update(() => {\n $insertNodes([$createTextNode(defaultValue)]);\n });\n }\n }\n\n activateContentCallbacks(\n onContentChange?: ((value: string) => void) | undefined,\n onCountChanged?: ((count: number) => void) | undefined,\n ) {\n this.deactivateContentCallbacks();\n this.__contentChangeCleanup = this.__editor.registerTextContentListener(text => {\n // Remove the sentinel node\n const processed = text.replace('\\u200b\\u200c', '');\n onContentChange?.(processed);\n onCountChanged?.(processed.length);\n });\n }\n\n deactivateContentCallbacks() {\n this.__contentChangeCleanup?.();\n this.__contentChangeCleanup = undefined;\n }\n\n activatePasteCallback(onPaste: (event: ClipboardEvent) => void) {\n this.__pasteHandlerCleanup = this.__editor.registerCommand(\n PASTE_COMMAND,\n (event: ClipboardEvent) => {\n onPaste(event);\n\n if (event.defaultPrevented) {\n return true;\n }\n\n return false;\n },\n COMMAND_PRIORITY_CRITICAL,\n );\n }\n\n deactivatePasteCallback() {\n this.__pasteHandlerCleanup?.();\n this.__pasteHandlerCleanup = undefined;\n }\n\n activateTrimWhitespace() {\n this.deactivateTrimWhitespace();\n this.__trimWhitespaceCleanup = this.__editor.registerTextContentListener(text => {\n if (text.trim() === '') {\n this.__editor.update(\n () => {\n $getRoot()\n .getAllTextNodes()\n .forEach(node => {\n node.remove();\n });\n },\n // Don't allow undoing this action\n { tag: 'historic' },\n );\n }\n });\n }\n\n deactivateTrimWhitespace() {\n this.__trimWhitespaceCleanup?.();\n this.__trimWhitespaceCleanup = undefined;\n }\n\n setIsDisabled(isDisabled: boolean) {\n this.__editor.setEditable(!isDisabled);\n }\n\n cleanup() {\n this.deactivateContentCallbacks();\n this.deactivateTrimWhitespace();\n this.deactivatePasteCallback();\n this.__baseHandlersCleanup?.();\n this.__baseHandlersCleanup = undefined;\n }\n}\n"],"names":["BasicFunctionalityBase","__enterHandler","event","selection","$getSelection","$isRangeSelection","preventDefault","shiftKey","__editor","dispatchCommand","INSERT_PARAGRAPH_COMMAND","insertDefaultValue","defaultValue","update","$insertNodes","onContentChange","onCountChanged","deactivateContentCallbacks","activateContentCallbacks","text","processed","__contentChangeCleanup","registerTextContentListener","length","_this___contentChangeCleanup","_this","call","undefined","__pasteHandlerCleanup","registerCommand","PASTE_COMMAND","activatePasteCallback","onPaste","COMMAND_PRIORITY_CRITICAL","_this___pasteHandlerCleanup","deactivatePasteCallback","deactivateTrimWhitespace","activateTrimWhitespace","__trimWhitespaceCleanup","trim","forEach","node","remove","_this___trimWhitespaceCleanup","isDisabled","setIsDisabled","cleanup","__baseHandlersCleanup","_this___baseHandlersCleanup","editor","constructor","registerUpdateListener","editorState","read","leaves","$getLeafNodes","$getRoot","$isSentinelNode","sentinel","insertAfter","discrete","getPreviousSibling","isSelected","selectStart","anchor","offset","isCollapsed","newSelection","set","selectionChanged","getKey","focus","$setSelection","SentinelNode","getNextSibling"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
1
+ {"version":3,"sources":["BasicFunctionality.base.ts"],"sourcesContent":["import type { LexicalEditor } from '@fluentui-copilot/text-editor';\nimport {\n $createTextNode,\n $getLeafNodes,\n $getNodeByKey,\n $getRoot,\n $getSelection,\n $insertNodes,\n $isRangeSelection,\n $normalizeSelection__EXPERIMENTAL,\n $setSelection,\n COMMAND_PRIORITY_CRITICAL,\n INSERT_PARAGRAPH_COMMAND,\n KEY_ENTER_COMMAND,\n PASTE_COMMAND,\n mergeRegister,\n} from '@fluentui-copilot/text-editor';\nimport { $createSentinelNode, $isSentinelNode, SENTINEL_VALUE, SentinelNode } from './SentinelNode';\n\nexport interface IBasicFunctionalityBase {\n insertDefaultValue: (defaultValue: string) => void;\n setIsDisabled: (isDisabled: boolean) => void;\n activateContentCallbacks(onContentChange?: (value: string) => void, onCountChanged?: (count: number) => void): void;\n deactivateContentCallbacks(): void;\n activateTrimWhitespace(): void;\n deactivateTrimWhitespace(): void;\n activatePasteCallback(onPaste: (event: ClipboardEvent) => void): void;\n deactivatePasteCallback(): void;\n cleanup(): void;\n}\n\nexport class BasicFunctionalityBase implements IBasicFunctionalityBase {\n private __editor: LexicalEditor;\n private __contentChangeCleanup?: () => void;\n private __trimWhitespaceCleanup?: () => void;\n private __baseHandlersCleanup?: () => void;\n private __pasteHandlerCleanup?: () => void;\n\n private __enterHandler(event: KeyboardEvent) {\n const selection = $getSelection();\n if (!$isRangeSelection(selection)) {\n return false;\n }\n\n if (event === null) {\n return false;\n }\n\n event.preventDefault();\n\n if (event.shiftKey) {\n return this.__editor.dispatchCommand(INSERT_PARAGRAPH_COMMAND, undefined);\n }\n\n // Mark event handled to override default behavior\n return true;\n }\n\n constructor(editor: LexicalEditor) {\n this.__editor = editor;\n\n this.__baseHandlersCleanup = mergeRegister(\n this.__editor.registerCommand(KEY_ENTER_COMMAND, this.__enterHandler.bind(this), COMMAND_PRIORITY_CRITICAL),\n\n // Add a sentinel node at the end of the input when there is content.\n // This sentinel node fixes a number of issues.\n // In Safari, Lexical's behaviour of adding <br /> tags to the end of the input when it ends\n // in a decorator node causes cursor location issues: https://github.com/facebook/lexical/issues/4487\n // Otherwise, when a decorator node is the last node in the input, the cursor can't move past it.\n // Adding an invisible text node that doesn't contribute to the content and can't be selected to the end of the input\n // mitigates these issues.\n this.__editor.registerUpdateListener(({ editorState }) => {\n editorState.read(() => {\n const leaves = $getLeafNodes($getRoot());\n if (leaves.length === 0) {\n return;\n }\n\n const lastNode = leaves[leaves.length - 1];\n const lastNodeKey = lastNode.getKey();\n\n // If the last node isn't a sentinel, add one\n if (!$isSentinelNode(lastNode)) {\n this.__editor.update(\n () => {\n // We find the node by its key again in case the node was removed before this update runs\n $getNodeByKey(lastNodeKey)?.insertAfter($createSentinelNode());\n },\n { discrete: true },\n );\n return;\n }\n\n // If the sentinel node is not selected, we're done\n const previous = lastNode.getPreviousSibling();\n if (!previous || !lastNode.isSelected()) {\n return;\n }\n\n const selection = $getSelection();\n if (!$isRangeSelection(selection)) {\n return;\n }\n\n // If the cursor is inside the sentinel node, move it out (next to the beginning)\n // We allow selection on the boundary of the sentinel in case the adjacent node is a decorator node\n // where selection is ill-defined.\n if (selection.isCollapsed() && selection.anchor.offset > 0) {\n this.__editor.update(\n () => {\n $getNodeByKey(lastNodeKey)?.selectStart();\n },\n { discrete: true },\n );\n return;\n }\n\n // If the selection is a range which includes the sentinel, modify the range to exclude it\n if (!selection.isCollapsed()) {\n let selectionChanged = false;\n const newSelection = selection.clone();\n\n if (newSelection.anchor.getNode() === lastNode && newSelection.anchor.offset > 0) {\n newSelection.anchor.set(lastNodeKey, 0, 'text');\n selectionChanged = true;\n }\n if (newSelection.focus.getNode() === lastNode && newSelection.focus.offset > 0) {\n newSelection.focus.set(lastNodeKey, 0, 'text');\n selectionChanged = true;\n }\n\n if (selectionChanged) {\n this.__editor.update(\n () => {\n if ($getNodeByKey(lastNodeKey) !== null) {\n $setSelection($normalizeSelection__EXPERIMENTAL(newSelection));\n }\n },\n { discrete: true },\n );\n }\n }\n });\n }),\n this.__editor.registerNodeTransform(SentinelNode, node => {\n if (!node.getPreviousSibling() || node.getNextSibling()) {\n node.remove();\n return;\n }\n }),\n );\n }\n\n insertDefaultValue(defaultValue: string) {\n if (defaultValue) {\n this.__editor.update(() => {\n $insertNodes([$createTextNode(defaultValue)]);\n });\n }\n }\n\n activateContentCallbacks(\n onContentChange?: ((value: string) => void) | undefined,\n onCountChanged?: ((count: number) => void) | undefined,\n ) {\n this.deactivateContentCallbacks();\n this.__contentChangeCleanup = this.__editor.registerTextContentListener(text => {\n // Remove the sentinel node\n const processed = text.replace(SENTINEL_VALUE, '');\n onContentChange?.(processed);\n onCountChanged?.(processed.length);\n });\n }\n\n deactivateContentCallbacks() {\n this.__contentChangeCleanup?.();\n this.__contentChangeCleanup = undefined;\n }\n\n activatePasteCallback(onPaste: (event: ClipboardEvent) => void) {\n this.__pasteHandlerCleanup = this.__editor.registerCommand(\n PASTE_COMMAND,\n (event: ClipboardEvent) => {\n onPaste(event);\n\n if (event.defaultPrevented) {\n return true;\n }\n\n return false;\n },\n COMMAND_PRIORITY_CRITICAL,\n );\n }\n\n deactivatePasteCallback() {\n this.__pasteHandlerCleanup?.();\n this.__pasteHandlerCleanup = undefined;\n }\n\n activateTrimWhitespace() {\n this.deactivateTrimWhitespace();\n this.__trimWhitespaceCleanup = this.__editor.registerTextContentListener(text => {\n if (text.trim() === '') {\n this.__editor.update(\n () => {\n $getRoot()\n .getAllTextNodes()\n .forEach(node => {\n node.remove();\n });\n },\n // Don't allow undoing this action\n { tag: 'historic' },\n );\n }\n });\n }\n\n deactivateTrimWhitespace() {\n this.__trimWhitespaceCleanup?.();\n this.__trimWhitespaceCleanup = undefined;\n }\n\n setIsDisabled(isDisabled: boolean) {\n this.__editor.setEditable(!isDisabled);\n }\n\n cleanup() {\n this.deactivateContentCallbacks();\n this.deactivateTrimWhitespace();\n this.deactivatePasteCallback();\n this.__baseHandlersCleanup?.();\n this.__baseHandlersCleanup = undefined;\n }\n}\n"],"names":["BasicFunctionalityBase","__enterHandler","event","selection","$getSelection","$isRangeSelection","preventDefault","shiftKey","__editor","dispatchCommand","INSERT_PARAGRAPH_COMMAND","insertDefaultValue","defaultValue","update","$insertNodes","onContentChange","onCountChanged","deactivateContentCallbacks","activateContentCallbacks","text","processed","__contentChangeCleanup","SENTINEL_VALUE","registerTextContentListener","length","_this___contentChangeCleanup","_this","call","undefined","__pasteHandlerCleanup","registerCommand","PASTE_COMMAND","activatePasteCallback","onPaste","COMMAND_PRIORITY_CRITICAL","_this___pasteHandlerCleanup","deactivatePasteCallback","deactivateTrimWhitespace","activateTrimWhitespace","__trimWhitespaceCleanup","trim","forEach","node","remove","_this___trimWhitespaceCleanup","isDisabled","setIsDisabled","cleanup","__baseHandlersCleanup","_this___baseHandlersCleanup","editor","constructor","registerUpdateListener","editorState","read","leaves","$getLeafNodes","$getRoot","$isSentinelNode","lastNode","$getNodeByKey","lastNodeKey","_$getNodeByKey","insertAfter","$createSentinelNode","getPreviousSibling","selectStart","isCollapsed","newSelection","anchor","selectionChanged","getNode","offset","focus","$setSelection","discrete","registerNodeTransform","SentinelNode","getNextSibling"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
@@ -15,11 +15,15 @@ _export(exports, {
15
15
  $isSentinelNode: function() {
16
16
  return $isSentinelNode;
17
17
  },
18
+ SENTINEL_VALUE: function() {
19
+ return SENTINEL_VALUE;
20
+ },
18
21
  SentinelNode: function() {
19
22
  return SentinelNode;
20
23
  }
21
24
  });
22
25
  const _texteditor = require("@fluentui-copilot/text-editor");
26
+ const SENTINEL_VALUE = '\u200b\u200c';
23
27
  class SentinelNode extends _texteditor.TextNode {
24
28
  static getType() {
25
29
  return 'sentinel';
@@ -33,7 +37,7 @@ class SentinelNode extends _texteditor.TextNode {
33
37
  constructor(key){
34
38
  // 2 zero width characters will not be visible in the editor.
35
39
  // These also happen to be markers for CIQ to ignore the following content.
36
- super('\u200b\u200c', key);
40
+ super(SENTINEL_VALUE, key);
37
41
  }
38
42
  }
39
43
  function $createSentinelNode(key) {
@@ -1 +1 @@
1
- {"version":3,"sources":["SentinelNode.ts"],"sourcesContent":["import type { LexicalNode, NodeKey } from '@fluentui-copilot/text-editor';\nimport { TextNode } from '@fluentui-copilot/text-editor';\n\nexport class SentinelNode extends TextNode {\n constructor(key?: NodeKey) {\n // 2 zero width characters will not be visible in the editor.\n // These also happen to be markers for CIQ to ignore the following content.\n super('\\u200b\\u200c', key);\n }\n\n static getType() {\n return 'sentinel';\n }\n static clone(node: SentinelNode) {\n return new SentinelNode(node.__key);\n }\n\n isToken() {\n return true;\n }\n}\n\nexport function $createSentinelNode(key?: NodeKey) {\n return new SentinelNode(key);\n}\n\nexport function $isSentinelNode(node: LexicalNode | null): node is SentinelNode {\n return node instanceof SentinelNode;\n}\n"],"names":["$createSentinelNode","$isSentinelNode","SentinelNode","TextNode","getType","clone","node","__key","isToken","constructor","key"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":";;;;;;;;;;;IAsBgBA,mBAAAA;eAAAA;;IAIAC,eAAAA;eAAAA;;IAvBHC,YAAAA;eAAAA;;;4BAFY;AAElB,MAAMA,qBAAqBC,oBAAAA;WAOhCC,UAAOA;eACL;;WAEFC,MAAOA,IAAMC,EAAkB;eAC7B,IAAOJ,aAAIA,KAAaI,KAAKC;;cAG/BC;eACE;;gBAdFC,GAAYC,CAAa;qEACvB;mFACA;aACA,CAAA,gBAAMA;;AAaV;AAEO,SAASV,oBAAoBU,GAAa;WAC/C,IAAOR,aAAIA;AACb;AAEO,SAASD,gBAAgBK,IAAwB;WACtDA,gBAAOA;AACT"}
1
+ {"version":3,"sources":["SentinelNode.ts"],"sourcesContent":["import type { LexicalNode, NodeKey } from '@fluentui-copilot/text-editor';\nimport { TextNode } from '@fluentui-copilot/text-editor';\n\nexport const SENTINEL_VALUE = '\\u200b\\u200c';\n\nexport class SentinelNode extends TextNode {\n constructor(key?: NodeKey) {\n // 2 zero width characters will not be visible in the editor.\n // These also happen to be markers for CIQ to ignore the following content.\n super(SENTINEL_VALUE, key);\n }\n\n static getType() {\n return 'sentinel';\n }\n static clone(node: SentinelNode) {\n return new SentinelNode(node.__key);\n }\n\n isToken() {\n return true;\n }\n}\n\nexport function $createSentinelNode(key?: NodeKey) {\n return new SentinelNode(key);\n}\n\nexport function $isSentinelNode(node: LexicalNode | null): node is SentinelNode {\n return node instanceof SentinelNode;\n}\n"],"names":["$createSentinelNode","$isSentinelNode","SENTINEL_VALUE","SentinelNode","TextNode","getType","clone","node","__key","isToken","constructor","key"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":";;;;;;;;;;;IAwBgBA,mBAAAA;eAAAA;;IAIAC,eAAAA;eAAAA;;IAzBHC,cAAAA;eAAAA;;IAEAC,YAAAA;eAAAA;;;4BAJY;AAElB,MAAMD,iBAAiB;AAEvB,MAAMC,qBAAqBC,oBAAAA;WAOhCC,UAAOA;eACL;;WAEFC,MAAOA,IAAMC,EAAkB;eAC7B,IAAOJ,aAAIA,KAAaI,KAAKC;;cAG/BC;eACE;;gBAdFC,GAAYC,CAAa;qEACvB;mFACA;aACA,CAAAT,gBAAMA;;AAaV;AAEO,SAASF,oBAAoBW,GAAa;WAC/C,IAAOR,aAAIA;AACb;AAEO,SAASF,gBAAgBM,IAAwB;WACtDA,gBAAOA;AACT"}
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "GhostTextPluginBase", {
10
10
  });
11
11
  const _define_property = require("@swc/helpers/_/_define_property");
12
12
  const _texteditor = require("@fluentui-copilot/text-editor");
13
+ const _BasicFunctionality = require("../BasicFunctionality");
13
14
  class GhostTextPluginBase {
14
15
  cleanup() {
15
16
  var _this___cleanup, _this;
@@ -43,12 +44,14 @@ class GhostTextPluginBase {
43
44
  let ghostTextNodeKey = null;
44
45
  let lastText = undefined;
45
46
  let justRemovedGhostText = false;
47
+ let justAddedGhostText = false;
46
48
  function $clearGhostText() {
47
49
  const ghostTextNode = ghostTextNodeKey !== null ? (0, _texteditor.$getNodeByKey)(ghostTextNodeKey) : null;
48
50
  ghostTextNodeKey = null;
49
51
  lastText = undefined;
50
52
  if (ghostTextNode && ghostTextNode.isAttached()) {
51
53
  ghostTextNode.remove();
54
+ justRemovedGhostText = true;
52
55
  }
53
56
  }
54
57
  function handleGhostTextNodeTransform(node) {
@@ -67,7 +70,6 @@ class GhostTextPluginBase {
67
70
  $clearGhostText();
68
71
  const selection = (0, _texteditor.$getSelection)();
69
72
  if (!text || !selection) {
70
- justRemovedGhostText = true;
71
73
  return;
72
74
  }
73
75
  const selectionCopy = selection.clone();
@@ -78,18 +80,58 @@ class GhostTextPluginBase {
78
80
  node
79
81
  ]);
80
82
  (0, _texteditor.$setSelection)(selectionCopy);
83
+ justAddedGhostText = true;
84
+ justRemovedGhostText = false;
81
85
  }, {
82
86
  tag: 'historic'
83
87
  });
84
88
  };
85
89
  const handleUpdate = (props)=>{
86
90
  const { editorState, prevEditorState } = props;
87
- // If this update was caused by deleting the previous ghost text, don't create a new one until a subsequent update
88
- if (justRemovedGhostText) {
91
+ // If this update was caused by adding or deleting ghost text, don't recheck the ghost text function until a subsequent update
92
+ if (justRemovedGhostText || justAddedGhostText) {
89
93
  justRemovedGhostText = false;
94
+ justAddedGhostText = false;
90
95
  return;
91
96
  }
92
97
  editorState.read(()=>{
98
+ // We only update the ghost text if the user selection is inside the input
99
+ const selection = (0, _texteditor.$getSelection)();
100
+ if (!(0, _texteditor.$getSelection)()) {
101
+ return;
102
+ }
103
+ if ((0, _texteditor.$isRangeSelection)(selection) && selection.isCollapsed()) {
104
+ var _selectedNode_getPreviousSibling;
105
+ var _selection_getNodes_at;
106
+ // All the `$isXNode` functions prefer `null` over `undefined`
107
+ const selectedNode = (_selection_getNodes_at = selection.getNodes().at(0)) !== null && _selection_getNodes_at !== void 0 ? _selection_getNodes_at : null;
108
+ const previousNodeKey = selectedNode === null || selectedNode === void 0 ? void 0 : (_selectedNode_getPreviousSibling = selectedNode.getPreviousSibling()) === null || _selectedNode_getPreviousSibling === void 0 ? void 0 : _selectedNode_getPreviousSibling.getKey();
109
+ const previousNodeIsGhostText = previousNodeKey === ghostTextNodeKey;
110
+ // If the ghost text is active and we're navigating past it, act as if the ghost text is not there and move 1 extra character
111
+ if (previousNodeIsGhostText && !(0, _BasicFunctionality.$isSentinelNode)(selectedNode) && (0, _texteditor.$isTextNode)(selectedNode) && selection.anchor.offset === 0) {
112
+ editor.update(()=>{
113
+ const selection = (0, _texteditor.$getSelection)();
114
+ if ((0, _texteditor.$isRangeSelection)(selection)) {
115
+ selection.modify('move', false, 'character');
116
+ }
117
+ $clearGhostText();
118
+ }, {
119
+ tag: 'historic'
120
+ });
121
+ // Defer checking the ghost text until after this update has finished
122
+ return;
123
+ }
124
+ // If the ghost text is the last node before the sentinel, we shouldn't let selection get past it
125
+ if (previousNodeIsGhostText && (0, _BasicFunctionality.$isSentinelNode)(selectedNode)) {
126
+ editor.update(()=>{
127
+ var _$getNodeByKey_getPreviousSibling, _$getNodeByKey;
128
+ (_$getNodeByKey = (0, _texteditor.$getNodeByKey)(previousNodeKey)) === null || _$getNodeByKey === void 0 ? void 0 : (_$getNodeByKey_getPreviousSibling = _$getNodeByKey.getPreviousSibling()) === null || _$getNodeByKey_getPreviousSibling === void 0 ? void 0 : _$getNodeByKey_getPreviousSibling.selectEnd();
129
+ }, {
130
+ tag: 'historic'
131
+ });
132
+ return;
133
+ }
134
+ }
93
135
  const promise = this.__$getGhostText(editor, editorState, prevEditorState);
94
136
  promise.then(handleGhostTextResponse).catch((e)=>console.error(e));
95
137
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["GhostText.base.ts"],"sourcesContent":["import type { EditorState, Klass, LexicalEditor, LexicalNode, UpdateListener } from '@fluentui-copilot/text-editor';\nimport {\n $createTextNode,\n $getNodeByKey,\n $getSelection,\n $setSelection,\n COMMAND_PRIORITY_LOW,\n KEY_TAB_COMMAND,\n mergeRegister,\n} from '@fluentui-copilot/text-editor';\n\nexport type GetGhostTextFunction = (\n editor: LexicalEditor,\n editorState: EditorState,\n prevEditorState: EditorState,\n) => Promise<string | undefined>;\n\nexport interface IGhostTextNode<ComponentPropsType> extends LexicalNode {\n __content: string;\n __id: string;\n __allowCommitting?: boolean;\n __componentProps?: ComponentPropsType;\n __exposeText?: boolean;\n}\n\nexport class GhostTextPluginBase<ComponentPropsType> {\n private __id: string;\n private __$getGhostText: GetGhostTextFunction;\n private __componentProps?: ComponentPropsType;\n private __exposeText?: boolean;\n private __allowCompletion?: boolean;\n\n private __cleanup?: () => void;\n\n cleanup() {\n this.__cleanup?.();\n }\n\n constructor(\n editor: LexicalEditor,\n id: string,\n $getGhostText: GetGhostTextFunction,\n nodeClass: Klass<IGhostTextNode<ComponentPropsType>>,\n createNode: (\n id: string,\n content: string,\n exposeText?: boolean,\n componentProps?: ComponentPropsType,\n ) => IGhostTextNode<ComponentPropsType>,\n componentProps?: ComponentPropsType,\n // Whether or not the ghost text should count as text inside the input for submitting and character count\n exposeText?: boolean,\n allowCompletion?: boolean,\n ) {\n this.__id = id;\n this.__$getGhostText = $getGhostText;\n this.__componentProps = componentProps;\n this.__exposeText = exposeText;\n this.__allowCompletion = allowCompletion;\n\n let ghostTextNodeKey: string | null = null;\n let lastText: string | undefined = undefined;\n let justRemovedGhostText = false;\n\n function $clearGhostText() {\n const ghostTextNode = ghostTextNodeKey !== null ? $getNodeByKey(ghostTextNodeKey) : null;\n ghostTextNodeKey = null;\n lastText = undefined;\n if (ghostTextNode && ghostTextNode.isAttached()) {\n ghostTextNode.remove();\n }\n }\n function handleGhostTextNodeTransform(node: IGhostTextNode<ComponentPropsType>) {\n const key = node.getKey();\n\n if (node.__id === id && key !== ghostTextNodeKey) {\n // Only one ghost text\n node.remove();\n $clearGhostText();\n }\n }\n\n const handleGhostTextResponse = (text?: string) => {\n if (text === lastText) {\n return;\n }\n\n editor.update(\n () => {\n $clearGhostText();\n\n const selection = $getSelection();\n if (!text || !selection) {\n justRemovedGhostText = true;\n return;\n }\n\n const selectionCopy = selection.clone();\n\n const node = createNode(this.__id, text, this.__exposeText, this.__componentProps);\n ghostTextNodeKey = node.getKey();\n lastText = text;\n\n selection.insertNodes([node]);\n $setSelection(selectionCopy);\n },\n { tag: 'historic' },\n );\n };\n\n const handleUpdate: UpdateListener = props => {\n const { editorState, prevEditorState } = props;\n // If this update was caused by deleting the previous ghost text, don't create a new one until a subsequent update\n if (justRemovedGhostText) {\n justRemovedGhostText = false;\n return;\n }\n editorState.read(() => {\n const promise = this.__$getGhostText(editor, editorState, prevEditorState);\n promise.then(handleGhostTextResponse).catch(e => console.error(e));\n });\n };\n\n function unmountGhostText() {\n if (ghostTextNodeKey) {\n editor.update(\n () => {\n $clearGhostText();\n },\n { tag: 'historic' },\n );\n }\n }\n\n function $handleTabCommand(e: KeyboardEvent) {\n if (ghostTextNodeKey === null || lastText === null) {\n return false;\n }\n\n const ghostTextNode = $getNodeByKey(ghostTextNodeKey);\n if (!ghostTextNode) {\n return false;\n }\n\n e.preventDefault();\n\n const textNode = $createTextNode(lastText);\n ghostTextNode.replace(textNode);\n textNode.selectEnd();\n $clearGhostText();\n return true;\n }\n\n this.__cleanup = mergeRegister(\n editor.registerNodeTransform(nodeClass, handleGhostTextNodeTransform),\n editor.registerUpdateListener(handleUpdate),\n this.__allowCompletion ? editor.registerCommand(KEY_TAB_COMMAND, $handleTabCommand, COMMAND_PRIORITY_LOW) : noop,\n unmountGhostText,\n );\n }\n\n setExposeText(exposeText?: boolean) {\n this.__exposeText = exposeText;\n }\n\n setComponentProps(componentProps?: ComponentPropsType) {\n this.__componentProps = componentProps;\n }\n\n setGetGhostText($getGhostText: GetGhostTextFunction) {\n this.__$getGhostText = $getGhostText;\n }\n\n setAllowCompletion(allowCompletion?: boolean) {\n this.__allowCompletion = allowCompletion;\n }\n}\nfunction noop(): void {\n return;\n}\n"],"names":["GhostTextPluginBase","cleanup","_this","__cleanup","_this___cleanup","call","setExposeText","exposeText","__exposeText","setComponentProps","componentProps","__componentProps","setGetGhostText","$getGhostText","__$getGhostText","setAllowCompletion","allowCompletion","__allowCompletion","constructor","editor","id","nodeClass","createNode","__id","ghostTextNodeKey","undefined","lastText","justRemovedGhostText","$clearGhostText","$getNodeByKey","ghostTextNode","isAttached","remove","handleGhostTextNodeTransform","node","key","text","selection","clone","getKey","selectionCopy","handleUpdate","props","promise","editorState","prevEditorState","then","handleGhostTextResponse","catch","e","console","error","preventDefault","textNode","selectEnd","mergeRegister","registerNodeTransform","registerUpdateListener","registerCommand","KEY_TAB_COMMAND","$handleTabCommand","COMMAND_PRIORITY_LOW","noop","unmountGhostText"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":";;;;+BAyBaA;;;eAAAA;;;;4BAhBN;AAgBA,MAAMA;cASXC;6BACEC;2BAAA,AAAAA,CAAAA,QAAA,IAAA,EAAAC,SAAKA,MAAS,QAAAC,oBAAd,KAAA,IAAA,KAAA,IAAAA,gBAAAC,IAAA,CAAAH;;kBA8HFI,UAAcC,EAAoB;YAChC,CAAAC,YAAKA,GAAAA;;sBAGPC,cAAkBC,EAAmC;YACnD,CAAAC,gBAAKA,GAAAA;;oBAGPC,aAAgBC,EAAmC;YACjD,CAAAC,eAAKA,GAAAA;;uBAGPC,eAAmBC,EAAyB;YAC1C,CAAAC,iBAAKA,GAAAA;;gBAxIPC,MACEC,EAAqBC,EACrBA,EAAUP,aACVA,EAAmCQ,SACnCA,EAAoDC,UACpDA,EAKuCZ,cACvCA,2GAIA;cA3BF,EAAAM,eAAA,CAAQO;8BACR,EAAA,IAAA,EAAA,QAAQT,KAAAA;8BACR,EAAA,IAAA,EAAA,mBAAQH,KAAR;8BACA,EAAA,IAAA,EAAA,oBAAA,KAAA;8BACA,EAAA,IAAA,EAAA,gBAAQM,KAAAA;8BAER,EAAA,IAAA,EAAA,qBAAA,KAAA;8BAsBcG,EAAAA,IAAAA,EAAAA,aAAAA,KAAAA;YACZ,CAAAG,IAAKT,GAAAA;YACL,CAAAA,eAAKH,GAAAA;YACL,CAAAA,gBAAiB,GAAGJ;YACpB,CAAAC,YAAKS,GAAAA;YAEL,CAAAA,iBAAIO,GAAAA;YACJA,mBAAmCC;YACnCC,WAAIC;YAEJA,uBAASC;iBACPA;kBACAJ,gBAAAA,qBAAmB,OAAAK,IAAAA,yBAAA,EAAAL,oBAAA;+BACRC;uBACPK;iCACFA,cAAoBC,UAAA,IAAA;8BACtBC,MAAA;;;iBAGAC,6BAAuBC,IAAA;kBAEvBC,MAAID,KAAKX,MAAI;yBACX,KAAAH,MAAAe,QAAsBX,kBAAA;sCACX;2BACXI;;;;wCAKqBQ,CAAAA;yBACrBV,UAAA;;;yBAKEE,CAAAA;;kCAGKQ,IAAAA,yBAASC;6BACZV,CAAAA,WAAAA;2CACA;;;sCAKWL,UAAWgB,KAAKf;6BAC7BC,WAAAA,IAAmBU,CAAAA,IAAKK,EAAAA,MAAM,IAAA,CAAA/B,YAAA,EAAA,IAAA,CAAAG,gBAAA;mCACnByB,KAAAA,MAAAA;2BAEXC;qCAAuBH,CAAAA;oBAAAA;iBAAAA;6CAAK,EAAAM;;qBAE9B;;;cAGJC,eAAAC,CAAAA;kBAEA,aACQ,iBACN;8HAEyB;sCACvB;uCACF;;;wBAGEC,IAAAA,CAAAA;sBACFA,UAAA,IAAA,CAAA7B,eAAA,CAAAK,QAAAyB,aAAAC;gBACFF,QAAAG,IAAA,CAAAC,yBAAAC,KAAA,CAAAC,CAAAA,IAAAC,QAAAC,KAAA,CAAAF;;;;kCAMQrB;6BAEF,CAAA;;;yBAEJ;gBACF;;;mCAIWqB,CAAA;qCACT,QAAAvB,aAAA,MAAA;uBAEA;;kCAESG,IAAAA,yBAAA,EAAAL;gCACT;uBAEE4B;;4BAGFtB;kBACAuB,WAASC,IAAAA,2BAAS,EAAA5B;0BAClBE,OAAAA,CAAAA;qBACA0B,SAAO;;mBAGJnD;QAMP;QAiBF,IAAA,CAAAA,SAAA,GAAAoD,IAAAA,yBAAA,EAAApC,OAAAqC,qBAAA,CAAAnC,WAAAY,+BAAAd,OAAAsC,sBAAA,CAAAhB,eAAA,IAAA,CAAAxB,iBAAA,GAAAE,OAAAuC,eAAA,CAAAC,2BAAA,EAAAC,mBAAAC,gCAAA,IAAAC,MAAAC;IACA;;AAEA,SAAAD"}
1
+ {"version":3,"sources":["GhostText.base.ts"],"sourcesContent":["import type { EditorState, Klass, LexicalEditor, LexicalNode, UpdateListener } from '@fluentui-copilot/text-editor';\nimport {\n $createTextNode,\n $getNodeByKey,\n $getSelection,\n $isRangeSelection,\n $isTextNode,\n $setSelection,\n COMMAND_PRIORITY_LOW,\n KEY_TAB_COMMAND,\n mergeRegister,\n} from '@fluentui-copilot/text-editor';\nimport { $isSentinelNode } from '../BasicFunctionality';\n\nexport type GetGhostTextFunction = (\n editor: LexicalEditor,\n editorState: EditorState,\n prevEditorState: EditorState,\n) => Promise<string | undefined>;\n\nexport interface IGhostTextNode<ComponentPropsType> extends LexicalNode {\n __content: string;\n __id: string;\n __allowCommitting?: boolean;\n __componentProps?: ComponentPropsType;\n __exposeText?: boolean;\n}\n\nexport class GhostTextPluginBase<ComponentPropsType> {\n private __id: string;\n private __$getGhostText: GetGhostTextFunction;\n private __componentProps?: ComponentPropsType;\n private __exposeText?: boolean;\n private __allowCompletion?: boolean;\n\n private __cleanup?: () => void;\n\n cleanup() {\n this.__cleanup?.();\n }\n\n constructor(\n editor: LexicalEditor,\n id: string,\n $getGhostText: GetGhostTextFunction,\n nodeClass: Klass<IGhostTextNode<ComponentPropsType>>,\n createNode: (\n id: string,\n content: string,\n exposeText?: boolean,\n componentProps?: ComponentPropsType,\n ) => IGhostTextNode<ComponentPropsType>,\n componentProps?: ComponentPropsType,\n // Whether or not the ghost text should count as text inside the input for submitting and character count\n exposeText?: boolean,\n allowCompletion?: boolean,\n ) {\n this.__id = id;\n this.__$getGhostText = $getGhostText;\n this.__componentProps = componentProps;\n this.__exposeText = exposeText;\n this.__allowCompletion = allowCompletion;\n\n let ghostTextNodeKey: string | null = null;\n let lastText: string | undefined = undefined;\n let justRemovedGhostText = false;\n let justAddedGhostText = false;\n\n function $clearGhostText() {\n const ghostTextNode = ghostTextNodeKey !== null ? $getNodeByKey(ghostTextNodeKey) : null;\n ghostTextNodeKey = null;\n lastText = undefined;\n if (ghostTextNode && ghostTextNode.isAttached()) {\n ghostTextNode.remove();\n justRemovedGhostText = true;\n }\n }\n function handleGhostTextNodeTransform(node: IGhostTextNode<ComponentPropsType>) {\n const key = node.getKey();\n\n if (node.__id === id && key !== ghostTextNodeKey) {\n // Only one ghost text\n node.remove();\n $clearGhostText();\n }\n }\n\n const handleGhostTextResponse = (text?: string) => {\n if (text === lastText) {\n return;\n }\n\n editor.update(\n () => {\n $clearGhostText();\n\n const selection = $getSelection();\n if (!text || !selection) {\n return;\n }\n\n const selectionCopy = selection.clone();\n\n const node = createNode(this.__id, text, this.__exposeText, this.__componentProps);\n ghostTextNodeKey = node.getKey();\n lastText = text;\n\n selection.insertNodes([node]);\n $setSelection(selectionCopy);\n justAddedGhostText = true;\n justRemovedGhostText = false;\n },\n { tag: 'historic' },\n );\n };\n\n const handleUpdate: UpdateListener = props => {\n const { editorState, prevEditorState } = props;\n\n // If this update was caused by adding or deleting ghost text, don't recheck the ghost text function until a subsequent update\n if (justRemovedGhostText || justAddedGhostText) {\n justRemovedGhostText = false;\n justAddedGhostText = false;\n return;\n }\n\n editorState.read(() => {\n // We only update the ghost text if the user selection is inside the input\n const selection = $getSelection();\n if (!$getSelection()) {\n return;\n }\n\n if ($isRangeSelection(selection) && selection.isCollapsed()) {\n // All the `$isXNode` functions prefer `null` over `undefined`\n const selectedNode = selection.getNodes().at(0) ?? null;\n const previousNodeKey = selectedNode?.getPreviousSibling()?.getKey();\n const previousNodeIsGhostText = previousNodeKey === ghostTextNodeKey;\n // If the ghost text is active and we're navigating past it, act as if the ghost text is not there and move 1 extra character\n if (\n previousNodeIsGhostText &&\n !$isSentinelNode(selectedNode) &&\n $isTextNode(selectedNode) &&\n selection.anchor.offset === 0\n ) {\n editor.update(\n () => {\n const selection = $getSelection();\n if ($isRangeSelection(selection)) {\n selection.modify('move', false, 'character');\n }\n $clearGhostText();\n },\n { tag: 'historic' },\n );\n // Defer checking the ghost text until after this update has finished\n return;\n }\n\n // If the ghost text is the last node before the sentinel, we shouldn't let selection get past it\n if (previousNodeIsGhostText && $isSentinelNode(selectedNode)) {\n editor.update(\n () => {\n $getNodeByKey(previousNodeKey)?.getPreviousSibling()?.selectEnd();\n },\n { tag: 'historic' },\n );\n return;\n }\n }\n\n const promise = this.__$getGhostText(editor, editorState, prevEditorState);\n promise.then(handleGhostTextResponse).catch(e => console.error(e));\n });\n };\n\n function unmountGhostText() {\n if (ghostTextNodeKey) {\n editor.update(\n () => {\n $clearGhostText();\n },\n { tag: 'historic' },\n );\n }\n }\n\n function $handleTabCommand(e: KeyboardEvent) {\n if (ghostTextNodeKey === null || lastText === null) {\n return false;\n }\n\n const ghostTextNode = $getNodeByKey(ghostTextNodeKey);\n if (!ghostTextNode) {\n return false;\n }\n\n e.preventDefault();\n\n const textNode = $createTextNode(lastText);\n ghostTextNode.replace(textNode);\n textNode.selectEnd();\n $clearGhostText();\n return true;\n }\n\n this.__cleanup = mergeRegister(\n editor.registerNodeTransform(nodeClass, handleGhostTextNodeTransform),\n editor.registerUpdateListener(handleUpdate),\n this.__allowCompletion ? editor.registerCommand(KEY_TAB_COMMAND, $handleTabCommand, COMMAND_PRIORITY_LOW) : noop,\n unmountGhostText,\n );\n }\n\n setExposeText(exposeText?: boolean) {\n this.__exposeText = exposeText;\n }\n\n setComponentProps(componentProps?: ComponentPropsType) {\n this.__componentProps = componentProps;\n }\n\n setGetGhostText($getGhostText: GetGhostTextFunction) {\n this.__$getGhostText = $getGhostText;\n }\n\n setAllowCompletion(allowCompletion?: boolean) {\n this.__allowCompletion = allowCompletion;\n }\n}\nfunction noop(): void {\n return;\n}\n"],"names":["GhostTextPluginBase","cleanup","_this","__cleanup","_this___cleanup","call","setExposeText","exposeText","__exposeText","setComponentProps","componentProps","__componentProps","setGetGhostText","$getGhostText","__$getGhostText","setAllowCompletion","allowCompletion","__allowCompletion","constructor","editor","id","nodeClass","createNode","__id","ghostTextNodeKey","undefined","lastText","justRemovedGhostText","justAddedGhostText","$clearGhostText","$getNodeByKey","ghostTextNode","isAttached","handleGhostTextNodeTransform","node","key","text","selection","clone","getKey","selectionCopy","$setSelection","handleUpdate","props","read","$getSelection","selectedNode","previousNodeIsGhostText","previousNodeKey","_selectedNode_getPreviousSibling","getPreviousSibling","$isSentinelNode","modify","_$getNodeByKey","tag","_$getNodeByKey_getPreviousSibling","selectEnd","promise","editorState","prevEditorState","then","handleGhostTextResponse","catch","e","console","error","preventDefault","textNode","mergeRegister","registerNodeTransform","registerUpdateListener","registerCommand","KEY_TAB_COMMAND","$handleTabCommand","COMMAND_PRIORITY_LOW","noop","unmountGhostText"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "ImperativeControlBase", {
10
10
  });
11
11
  const _define_property = require("@swc/helpers/_/_define_property");
12
12
  const _texteditor = require("@fluentui-copilot/text-editor");
13
+ const _BasicFunctionality = require("../BasicFunctionality");
13
14
  class ImperativeControlBase {
14
15
  moveCursor(location) {
15
16
  this.__editor.update(()=>{
@@ -62,12 +63,23 @@ class ImperativeControlBase {
62
63
  insertTextAtCursor(text) {
63
64
  this.__editor.update(()=>{
64
65
  var _$getSelection;
65
- (_$getSelection = (0, _texteditor.$getSelection)()) === null || _$getSelection === void 0 ? void 0 : _$getSelection.insertText(text);
66
+ const selection = (_$getSelection = (0, _texteditor.$getSelection)()) !== null && _$getSelection !== void 0 ? _$getSelection : (0, _texteditor.$getRoot)().selectEnd();
67
+ selection.insertText(text);
66
68
  });
67
69
  }
68
- getInputText() {
70
+ getInputText(transform) {
69
71
  return this.__editor.getEditorState().read(()=>{
70
- return (0, _texteditor.$getRoot)().getTextContent();
72
+ if (!transform) {
73
+ return (0, _texteditor.$getRoot)().getTextContent().replace(_BasicFunctionality.SENTINEL_VALUE, '');
74
+ }
75
+ const children = (0, _texteditor.$getLeafNodes)((0, _texteditor.$getRoot)());
76
+ const transformedNodeTexts = [];
77
+ for (const currentNode of children){
78
+ if (!(0, _BasicFunctionality.$isSentinelNode)(currentNode)) {
79
+ transformedNodeTexts.push(transform(currentNode));
80
+ }
81
+ }
82
+ return transformedNodeTexts.join('');
71
83
  });
72
84
  }
73
85
  scrollToBottom() {
@@ -1 +1 @@
1
- {"version":3,"sources":["ImperativeControl.base.ts"],"sourcesContent":["import type { LexicalEditor } from '@fluentui-copilot/text-editor';\nimport {\n $createParagraphNode,\n $createRangeSelection,\n $createTextNode,\n $getLeafNodes,\n $getRoot,\n $getSelection,\n $isTextNode,\n $normalizeSelection__EXPERIMENTAL,\n $setSelection,\n} from '@fluentui-copilot/text-editor';\n\nexport interface IImperativeControlBase {\n setInputText: (inputText: string) => void;\n appendText: (text: string) => void;\n prependText: (text: string) => void;\n insertTextAtCursor: (text: string) => void;\n getInputText: () => string;\n scrollToBottom: () => void;\n moveCursor: (location: number) => void;\n}\n\nexport class ImperativeControlBase implements IImperativeControlBase {\n private __editor: LexicalEditor;\n\n constructor(editor: LexicalEditor) {\n this.__editor = editor;\n }\n\n moveCursor(location: number): void {\n this.__editor.update(() => {\n const children = $getLeafNodes($getRoot());\n\n let baseOffset = 0;\n let currentNode = children.shift();\n while (baseOffset < location && currentNode) {\n const nodeLength =\n $isTextNode(currentNode) && !currentNode.isToken()\n ? currentNode.getTextContent().length\n : // Token text nodes and non-text nodes are considered to be a single entry in the input\n 1;\n\n if (baseOffset + nodeLength >= location) {\n const elementType = $isTextNode(currentNode) ? 'text' : 'element';\n const localOffset = location - baseOffset;\n const nodeKey = currentNode.getKey();\n\n const selection = $createRangeSelection();\n selection.anchor.set(nodeKey, localOffset, elementType);\n selection.focus.set(nodeKey, localOffset, elementType);\n\n $setSelection($normalizeSelection__EXPERIMENTAL(selection));\n return;\n }\n\n baseOffset += nodeLength;\n currentNode = children.shift();\n }\n\n if (location > baseOffset) {\n $getRoot().selectEnd();\n }\n });\n }\n setInputText(inputText: string) {\n this.__editor.update(() => {\n const root = $getRoot();\n root.clear();\n if (inputText !== '') {\n const newParagraph = $createParagraphNode();\n const newText = $createTextNode(inputText);\n\n newParagraph.append(newText);\n root.append(newParagraph);\n root.selectEnd();\n }\n });\n }\n appendText(text: string) {\n this.__editor.update(() => {\n $getRoot().selectEnd().insertText(text);\n });\n }\n prependText(text: string) {\n this.__editor.update(() => {\n $getRoot().selectStart().insertText(text);\n });\n }\n insertTextAtCursor(text: string) {\n this.__editor.update(() => {\n $getSelection()?.insertText(text);\n });\n }\n getInputText() {\n return this.__editor.getEditorState().read(() => {\n return $getRoot().getTextContent();\n });\n }\n scrollToBottom() {\n this.__editor.getRootElement()?.scrollIntoView({ behavior: 'smooth', block: 'end' });\n return;\n }\n}\n"],"names":["ImperativeControlBase","moveCursor","location","__editor","update","children","$getLeafNodes","$getRoot","baseOffset","currentNode","nodeLength","$isTextNode","isToken","getTextContent","length","elementType","localOffset","nodeKey","selection","$createRangeSelection","anchor","focus","$setSelection","$normalizeSelection__EXPERIMENTAL","selectEnd","setInputText","inputText","root","newParagraph","$createParagraphNode","newText","$createTextNode","appendText","text","insertText","prependText","selectStart","insertTextAtCursor","$getSelection","getInputText","getEditorState","read","scrollToBottom","getRootElement","_this___editor_getRootElement","scrollIntoView","behavior","block","constructor","editor"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":";;;;+BAuBaA;;;eAAAA;;;;4BAZN;AAYA,MAAMA;eAOXC,QAAWC,EAAgB;YACzB,CAAAC,QAAKA,CAAAA,MAASC,CAAAA;kBACZC,WAAMA,IAAAA,yBAAWC,EAAAA,IAAAA,oBAAcC;6BAE3BC;8BACAC,SAAcJ,KAAAA;kBAClBG,aAAOA,YAAaN,YAAYO;mCACxBC,IAAAA,uBACJC,EAAAA,gBAAYF,CAAAA,YAAiBA,OAAAA,KAAYG,YACrCH,cAAYI,GAAAA,MAAc,GAAGC;iCAI/BN,cAAaE,UAAcR;wCACvBa,IAAAA,uBAAcJ,EAAAA,eAAYF,SAAAA;wCAC1BO,WAAcd;oCACde,YAAUR,MAAAA;sCAEVS,IAAAA,iCAAYC;8BAClBD,MAAAA,CAAAA,GAAUE,CAAAA,SAAUJ,aAAUA;8BAC9BE,KAAAA,CAAAA,GAAAA,CAAUG,SAASL,aAAUA;iDAE7BM,EAAAA,IAAAA,6CAAcC,EAAAA;;;8BAIhBf;8BACAC,SAAcJ,KAAAA;;2BAGZH,YAAWM;wCACbD,IAAAA,SAAWiB;;;;iBAIjBC,SAAaC,EAAiB;YAC5B,CAAAvB,QAAKA,CAAAA,MAASC,CAAAA;kBACZuB,OAAMA,IAAAA,oBAAOpB;sBACboB;8BACID,IAAAA;qCACIE,IAAAA,gCAAeC;gCACfC,IAAAA,2BAAUC,EAAAA;6BAEhBH,MAAAA,CAAAA;2BACAD,CAAAA;8BACKH;;;;eAIXQ,IAAWC,EAAY;YACrB,CAAA9B,QAAKA,CAAAA,MAASC,CAAAA;oCACZG,IAAAA,SAAWiB,GAAAA,UAAYU,CAAAA;;;gBAG3BC,IAAYF,EAAY;YACtB,CAAA9B,QAAKA,CAAAA,MAASC,CAAAA;oCACZG,IAAAA,WAAW6B,GAAAA,UAAcF,CAAAA;;;uBAG7BG,IAAmBJ,EAAY;YAC7B,CAAA9B,QAAKA,CAAAA,MAASC,CAAAA;;8BACZkC,IAAAA,yBAAAA,GAAAA,MAAAA,QAAAA,mBAAAA,KAAAA,IAAAA,KAAAA,IAAAA,eAAAA,UAAiBJ,CAAAA;;;mBAGrBK;eACE,IAAO,CAAApC,QAAKA,CAAAA,cAASqC,GAAAA,IAAiBC,CAAAA;mBACpClC,IAAAA,oBAAOA,IAAAA,cAAWM;;;qBAGtB6B;;yCACE,IAAA,CAAAvC,QAAKA,CAAAA,cAASwC,EAAAA,MAAc,QAAAC,kCAA5B,KAAA,IAAA,KAAA,IAAAA,8BAAAC,cAAgCA,CAAAA;sBAAiBC;mBAAoBC;;;;gBA1EvEC,MAAYC,CAAqB;8BAFjC,EAAA,IAAA,EAAA,YAAQ9C,KAAR;YAGE,CAAAA,QAAKA,GAAAA;;AA4ET"}
1
+ {"version":3,"sources":["ImperativeControl.base.ts"],"sourcesContent":["import type { LexicalEditor, LexicalNode } from '@fluentui-copilot/text-editor';\nimport {\n $createParagraphNode,\n $createRangeSelection,\n $createTextNode,\n $getLeafNodes,\n $getRoot,\n $getSelection,\n $isTextNode,\n $normalizeSelection__EXPERIMENTAL,\n $setSelection,\n} from '@fluentui-copilot/text-editor';\nimport { SENTINEL_VALUE, $isSentinelNode } from '../BasicFunctionality';\n\nexport interface IImperativeControlBase {\n setInputText: (inputText: string) => void;\n appendText: (text: string) => void;\n prependText: (text: string) => void;\n insertTextAtCursor: (text: string) => void;\n /**\n * @param transform will be called for each Lexical node in the input. This enables custom string representation for each node.\n */\n getInputText: (transform?: (node: LexicalNode) => string) => string;\n scrollToBottom: () => void;\n moveCursor: (location: number) => void;\n}\n\nexport class ImperativeControlBase implements IImperativeControlBase {\n private __editor: LexicalEditor;\n\n constructor(editor: LexicalEditor) {\n this.__editor = editor;\n }\n\n moveCursor(location: number): void {\n this.__editor.update(() => {\n const children = $getLeafNodes($getRoot());\n\n let baseOffset = 0;\n let currentNode = children.shift();\n while (baseOffset < location && currentNode) {\n const nodeLength =\n $isTextNode(currentNode) && !currentNode.isToken()\n ? currentNode.getTextContent().length\n : // Token text nodes and non-text nodes are considered to be a single entry in the input\n 1;\n\n if (baseOffset + nodeLength >= location) {\n const elementType = $isTextNode(currentNode) ? 'text' : 'element';\n const localOffset = location - baseOffset;\n const nodeKey = currentNode.getKey();\n\n const selection = $createRangeSelection();\n selection.anchor.set(nodeKey, localOffset, elementType);\n selection.focus.set(nodeKey, localOffset, elementType);\n\n $setSelection($normalizeSelection__EXPERIMENTAL(selection));\n return;\n }\n\n baseOffset += nodeLength;\n currentNode = children.shift();\n }\n\n if (location > baseOffset) {\n $getRoot().selectEnd();\n }\n });\n }\n setInputText(inputText: string) {\n this.__editor.update(() => {\n const root = $getRoot();\n root.clear();\n if (inputText !== '') {\n const newParagraph = $createParagraphNode();\n const newText = $createTextNode(inputText);\n\n newParagraph.append(newText);\n root.append(newParagraph);\n root.selectEnd();\n }\n });\n }\n appendText(text: string) {\n this.__editor.update(() => {\n $getRoot().selectEnd().insertText(text);\n });\n }\n prependText(text: string) {\n this.__editor.update(() => {\n $getRoot().selectStart().insertText(text);\n });\n }\n insertTextAtCursor(text: string) {\n this.__editor.update(() => {\n const selection = $getSelection() ?? $getRoot().selectEnd();\n selection.insertText(text);\n });\n }\n getInputText(transform?: (node: LexicalNode) => string) {\n return this.__editor.getEditorState().read(() => {\n if (!transform) {\n return $getRoot().getTextContent().replace(SENTINEL_VALUE, '');\n }\n const children = $getLeafNodes($getRoot());\n const transformedNodeTexts: string[] = [];\n\n for (const currentNode of children) {\n if (!$isSentinelNode(currentNode)) {\n transformedNodeTexts.push(transform(currentNode));\n }\n }\n\n return transformedNodeTexts.join('');\n });\n }\n scrollToBottom() {\n this.__editor.getRootElement()?.scrollIntoView({ behavior: 'smooth', block: 'end' });\n return;\n }\n}\n"],"names":["ImperativeControlBase","moveCursor","location","__editor","update","children","$getLeafNodes","$getRoot","baseOffset","currentNode","nodeLength","$isTextNode","isToken","getTextContent","length","elementType","localOffset","nodeKey","selection","$createRangeSelection","anchor","focus","$setSelection","$normalizeSelection__EXPERIMENTAL","selectEnd","setInputText","inputText","root","newParagraph","$createParagraphNode","newText","$createTextNode","appendText","text","insertText","prependText","selectStart","insertTextAtCursor","$getSelection","getInputText","transform","getEditorState","read","replace","SENTINEL_VALUE","transformedNodeTexts","$isSentinelNode","scrollToBottom","getRootElement","_this___editor_getRootElement","scrollIntoView","behavior","block","constructor","editor"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":";;;;+BA2BaA;;;eAAAA;;;;4BAhBN;oCACyC;AAezC,MAAMA;eAOXC,QAAWC,EAAgB;YACzB,CAAAC,QAAKA,CAAAA,MAASC,CAAAA;kBACZC,WAAMA,IAAAA,yBAAWC,EAAAA,IAAAA,oBAAcC;6BAE3BC;8BACAC,SAAcJ,KAAAA;kBAClBG,aAAOA,YAAaN,YAAYO;mCACxBC,IAAAA,uBACJC,EAAAA,gBAAYF,CAAAA,YAAiBA,OAAAA,KAAYG,YACrCH,cAAYI,GAAAA,MAAc,GAAGC;iCAI/BN,cAAaE,UAAcR;wCACvBa,IAAAA,uBAAcJ,EAAAA,eAAYF,SAAAA;wCAC1BO,WAAcd;oCACde,YAAUR,MAAAA;sCAEVS,IAAAA,iCAAYC;8BAClBD,MAAAA,CAAAA,GAAUE,CAAAA,SAAUJ,aAAUA;8BAC9BE,KAAAA,CAAAA,GAAAA,CAAUG,SAASL,aAAUA;iDAE7BM,EAAAA,IAAAA,6CAAcC,EAAAA;;;8BAIhBf;8BACAC,SAAcJ,KAAAA;;2BAGZH,YAAWM;wCACbD,IAAAA,SAAWiB;;;;iBAIjBC,SAAaC,EAAiB;YAC5B,CAAAvB,QAAKA,CAAAA,MAASC,CAAAA;kBACZuB,OAAMA,IAAAA,oBAAOpB;sBACboB;8BACID,IAAAA;qCACIE,IAAAA,gCAAeC;gCACfC,IAAAA,2BAAUC,EAAAA;6BAEhBH,MAAAA,CAAAA;2BACAD,CAAAA;8BACKH;;;;eAIXQ,IAAWC,EAAY;YACrB,CAAA9B,QAAKA,CAAAA,MAASC,CAAAA;oCACZG,IAAAA,SAAWiB,GAAAA,UAAYU,CAAAA;;;gBAG3BC,IAAYF,EAAY;YACtB,CAAA9B,QAAKA,CAAAA,MAASC,CAAAA;oCACZG,IAAAA,WAAW6B,GAAAA,UAAcF,CAAAA;;;uBAG7BG,IAAmBJ,EAAY;YAC7B,CAAA9B,QAAKA,CAAAA,MAASC,CAAAA;;kBACZc,YAAMA,CAAAA,iBAAYoB,IAAAA,yBAAAA,GAAAA,MAAAA,QAAAA,mBAAAA,KAAAA,IAAAA,iBAAAA,IAAAA,oBAAmB/B,IAAAA,SAAWiB;sBAChDN,UAAUgB,CAAAA;;;iBAGdK,SAAaC,EAAyC;eACpD,IAAO,CAAArC,QAAKA,CAAAA,cAASsC,GAAAA,IAAiBC,CAAAA;4BAC/BF;+CACIjC,IAAAA,cAAWM,GAAAA,OAAc,CAAG8B,kCAAQC,EAAAA;;kBAE7CvC,WAAMA,IAAAA,yBAAWC,EAAAA,IAAAA,oBAAcC;kBAC/BsC,uBAAMA,EAAAA;uBAEDpC,eAAMA,SAAeJ;4DACnByC,EAAAA,cAAgBrC;yCACnBoC,IAAAA,CAAAA,UAA0BL;;;mBAI9BK,qBAAOA,IAAAA,CAAAA;;;qBAGXE;;yCACE,IAAA,CAAA5C,QAAKA,CAAAA,cAAS6C,EAAAA,MAAc,QAAAC,kCAA5B,KAAA,IAAA,KAAA,IAAAA,8BAAAC,cAAgCA,CAAAA;sBAAiBC;mBAAoBC;;;;gBAvFvEC,MAAYC,CAAqB;8BAFjC,EAAA,IAAA,EAAA,YAAQnD,KAAR;YAGE,CAAAA,QAAKA,GAAAA;;AAyFT"}
@@ -30,6 +30,9 @@ _export(exports, {
30
30
  ManualGhostTextBase: function() {
31
31
  return _ManualGhostText.ManualGhostTextBase;
32
32
  },
33
+ SENTINEL_VALUE: function() {
34
+ return _BasicFunctionality.SENTINEL_VALUE;
35
+ },
33
36
  SentinelNode: function() {
34
37
  return _BasicFunctionality.SentinelNode;
35
38
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["index.ts"],"sourcesContent":["export { BasicFunctionalityBase, SentinelNode, $createSentinelNode, $isSentinelNode } from './BasicFunctionality';\nexport type { IBasicFunctionalityBase } from './BasicFunctionality';\n\nexport { ChatInputEntityPluginBase } from './ChatInputEntity';\nexport type {\n ChatInputEntityData,\n ChatInputEntityPluginProps,\n IChatInputEntityPluginBase,\n IEntityNode,\n} from './ChatInputEntity';\n\nexport { GhostTextPluginBase } from './GhostText';\nexport type { GetGhostTextFunction, IGhostTextNode } from './GhostText';\n\nexport { ImperativeControlBase } from './ImperativeControl';\nexport type { IImperativeControlBase } from './ImperativeControl';\n\nexport { ManualGhostTextBase } from './ManualGhostText';\nexport type { IManualGhostTextBase } from './ManualGhostText';\n"],"names":["$createSentinelNode","$isSentinelNode","BasicFunctionalityBase","ChatInputEntityPluginBase","GhostTextPluginBase","ImperativeControlBase","ManualGhostTextBase","SentinelNode"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":";;;;;;;;;;;IAA+CA,mBAAmB;eAAnBA,uCAAmB;;IAAEC,eAAe;eAAfA,mCAAe;;IAA1EC,sBAAsB;eAAtBA,0CAAsB;;IAGtBC,yBAAyB;eAAzBA,0CAAyB;;IAQzBC,mBAAmB;eAAnBA,8BAAmB;;IAGnBC,qBAAqB;eAArBA,wCAAqB;;IAGrBC,mBAAmB;eAAnBA,oCAAmB;;IAjBKC,YAAY;eAAZA,gCAAY;;;oCAA8C;iCAGjD;2BAQN;mCAGE;iCAGF"}
1
+ {"version":3,"sources":["index.ts"],"sourcesContent":["export {\n BasicFunctionalityBase,\n SentinelNode,\n $createSentinelNode,\n $isSentinelNode,\n SENTINEL_VALUE,\n} from './BasicFunctionality';\nexport type { IBasicFunctionalityBase } from './BasicFunctionality';\n\nexport { ChatInputEntityPluginBase } from './ChatInputEntity';\nexport type {\n ChatInputEntityData,\n ChatInputEntityPluginProps,\n IChatInputEntityPluginBase,\n IEntityNode,\n} from './ChatInputEntity';\n\nexport { GhostTextPluginBase } from './GhostText';\nexport type { GetGhostTextFunction, IGhostTextNode } from './GhostText';\n\nexport { ImperativeControlBase } from './ImperativeControl';\nexport type { IImperativeControlBase } from './ImperativeControl';\n\nexport { ManualGhostTextBase } from './ManualGhostText';\nexport type { IManualGhostTextBase } from './ManualGhostText';\n"],"names":["$createSentinelNode","$isSentinelNode","BasicFunctionalityBase","ChatInputEntityPluginBase","GhostTextPluginBase","ImperativeControlBase","ManualGhostTextBase","SENTINEL_VALUE","SentinelNode"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":";;;;;;;;;;;IAGEA,mBAAmB;eAAnBA,uCAAmB;;IACnBC,eAAe;eAAfA,mCAAe;;IAHfC,sBAAsB;eAAtBA,0CAAsB;;IAQfC,yBAAyB;eAAzBA,0CAAyB;;IAQzBC,mBAAmB;eAAnBA,8BAAmB;;IAGnBC,qBAAqB;eAArBA,wCAAqB;;IAGrBC,mBAAmB;eAAnBA,oCAAmB;;IAlB1BC,cAAc;eAAdA,kCAAc;;IAHdC,YAAY;eAAZA,gCAAY;;;oCAIP;iCAGmC;2BAQN;mCAGE;iCAGF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluentui-copilot/chat-input-plugins",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "A Fluent AI package for non-react specific chat input plugins.",
5
5
  "main": "lib-commonjs/index.js",
6
6
  "module": "lib/index.js",