@jupyter/chat 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/input/chat-input.js +1 -1
- package/lib/components/input/use-chat-commands.js +11 -3
- package/lib/input-model.js +30 -3
- package/lib/types.d.ts +7 -2
- package/lib/utils.js +8 -6
- package/package.json +1 -1
- package/src/components/input/chat-input.tsx +1 -1
- package/src/components/input/use-chat-commands.tsx +11 -3
- package/src/input-model.ts +33 -4
- package/src/types.ts +7 -2
- package/src/utils.ts +8 -6
|
@@ -118,7 +118,7 @@ export function ChatInput(props) {
|
|
|
118
118
|
(!sendWithShiftEnter && !event.shiftKey)) {
|
|
119
119
|
// Run all command providers
|
|
120
120
|
await ((_a = props.chatCommandRegistry) === null || _a === void 0 ? void 0 : _a.onSubmit(model));
|
|
121
|
-
model.send(
|
|
121
|
+
model.send(model.value);
|
|
122
122
|
event.stopPropagation();
|
|
123
123
|
event.preventDefault();
|
|
124
124
|
}
|
|
@@ -14,7 +14,9 @@ import React, { useEffect, useState } from 'react';
|
|
|
14
14
|
export function useChatCommands(inputModel, chatCommandRegistry) {
|
|
15
15
|
// whether an option is highlighted in the chat commands menu
|
|
16
16
|
const [highlighted, setHighlighted] = useState(false);
|
|
17
|
-
// whether the chat commands menu is open
|
|
17
|
+
// whether the chat commands menu is open.
|
|
18
|
+
// NOTE: every `setOpen(false)` call should be followed by a
|
|
19
|
+
// `setHighlighted(false)` call.
|
|
18
20
|
const [open, setOpen] = useState(false);
|
|
19
21
|
// current list of chat commands matched by the current word.
|
|
20
22
|
// the current word is the space-separated word at the user's cursor.
|
|
@@ -55,7 +57,13 @@ export function useChatCommands(inputModel, chatCommandRegistry) {
|
|
|
55
57
|
}
|
|
56
58
|
// Otherwise, open/close the menu based on the presence of command
|
|
57
59
|
// completions and set the menu entries.
|
|
58
|
-
|
|
60
|
+
if (commandCompletions.length) {
|
|
61
|
+
setOpen(true);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
setOpen(false);
|
|
65
|
+
setHighlighted(false);
|
|
66
|
+
}
|
|
59
67
|
setCommands(commandCompletions);
|
|
60
68
|
}
|
|
61
69
|
inputModel.currentWordChanged.connect(getCommands);
|
|
@@ -91,7 +99,7 @@ export function useChatCommands(inputModel, chatCommandRegistry) {
|
|
|
91
99
|
autocompleteProps: {
|
|
92
100
|
open,
|
|
93
101
|
options: commands,
|
|
94
|
-
getOptionLabel: (command) => command.name,
|
|
102
|
+
getOptionLabel: (command) => typeof command === 'string' ? '' : command.name,
|
|
95
103
|
renderOption: (defaultProps, command, __, ___) => {
|
|
96
104
|
const { key, ...listItemProps } = defaultProps;
|
|
97
105
|
const commandIcon = React.isValidElement(command.icon) ? (command.icon) : (React.createElement("span", null, command.icon instanceof LabIcon ? (React.createElement(command.icon.react, null)) : (command.icon)));
|
package/lib/input-model.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
import { Signal } from '@lumino/signaling';
|
|
6
|
-
const WHITESPACE = new Set([' ', '\n', '\t']);
|
|
7
6
|
/**
|
|
8
7
|
* The input model.
|
|
9
8
|
*/
|
|
@@ -254,14 +253,42 @@ export class InputModel {
|
|
|
254
253
|
}
|
|
255
254
|
var Private;
|
|
256
255
|
(function (Private) {
|
|
256
|
+
const WHITESPACE = new Set([' ', '\n', '\t']);
|
|
257
|
+
/**
|
|
258
|
+
* Returns the start index (inclusive) & end index (exclusive) that contain
|
|
259
|
+
* the current word. The start & end index can be passed to `String.slice()`
|
|
260
|
+
* to extract the current word. The returned range never includes any
|
|
261
|
+
* whitespace character unless it is escaped by a backslash `\`.
|
|
262
|
+
*
|
|
263
|
+
* NOTE: the escape sequence handling here is naive and non-recursive. This
|
|
264
|
+
* function considers the space in "`\\ `" as escaped, even though "`\\ `"
|
|
265
|
+
* defines a backslash followed by an _unescaped_ space in most languages.
|
|
266
|
+
*/
|
|
257
267
|
function getCurrentWordBoundaries(input, cursorIndex) {
|
|
258
268
|
let start = cursorIndex;
|
|
259
269
|
let end = cursorIndex;
|
|
260
270
|
const n = input.length;
|
|
261
|
-
while (
|
|
271
|
+
while (
|
|
272
|
+
// terminate when `input[start - 1]` is whitespace
|
|
273
|
+
// i.e. `input[start]` is never whitespace after exiting
|
|
274
|
+
(start - 1 >= 0 && !WHITESPACE.has(input[start - 1])) ||
|
|
275
|
+
// unless it is preceded by a backslash
|
|
276
|
+
(start - 2 >= 0 &&
|
|
277
|
+
input[start - 2] === '\\' &&
|
|
278
|
+
WHITESPACE.has(input[start - 1]))) {
|
|
262
279
|
start--;
|
|
263
280
|
}
|
|
264
|
-
|
|
281
|
+
// `end` is an exclusive index unlike `start`, hence the different `while`
|
|
282
|
+
// condition here
|
|
283
|
+
while (
|
|
284
|
+
// terminate when `input[end]` is whitespace
|
|
285
|
+
// i.e. `input[end]` may be whitespace after exiting
|
|
286
|
+
(end < n && !WHITESPACE.has(input[end])) ||
|
|
287
|
+
// unless it is preceded by a backslash
|
|
288
|
+
(end < n &&
|
|
289
|
+
end - 1 >= 0 &&
|
|
290
|
+
input[end - 1] === '\\' &&
|
|
291
|
+
WHITESPACE.has(input[end]))) {
|
|
265
292
|
end++;
|
|
266
293
|
}
|
|
267
294
|
return [start, end];
|
package/lib/types.d.ts
CHANGED
|
@@ -9,9 +9,14 @@ export interface IUser {
|
|
|
9
9
|
color?: string;
|
|
10
10
|
avatar_url?: string;
|
|
11
11
|
/**
|
|
12
|
-
* The string to use to mention a user in the chat.
|
|
12
|
+
* The string to use to mention a user in the chat. This is computed via the
|
|
13
|
+
* following procedure:
|
|
14
|
+
*
|
|
15
|
+
* 1. Let `mention_name = user.display_name || user.name || user.username`.
|
|
16
|
+
*
|
|
17
|
+
* 2. Replace each ' ' character with '-' in `mention_name`.
|
|
13
18
|
*/
|
|
14
|
-
mention_name
|
|
19
|
+
mention_name: string;
|
|
15
20
|
/**
|
|
16
21
|
* Boolean identifying if user is a bot.
|
|
17
22
|
*/
|
package/lib/utils.js
CHANGED
|
@@ -46,9 +46,10 @@ export function replaceMentionToSpan(content, user) {
|
|
|
46
46
|
if (!user.mention_name) {
|
|
47
47
|
return content;
|
|
48
48
|
}
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
|
|
49
|
+
const mention = '@' + user.mention_name;
|
|
50
|
+
const regex = new RegExp(mention, 'g');
|
|
51
|
+
const mentionEl = `<span class="${MENTION_CLASS}">${mention}</span>`;
|
|
52
|
+
return content.replace(regex, mentionEl);
|
|
52
53
|
}
|
|
53
54
|
/**
|
|
54
55
|
* Replace a span to a mentioned to user string (@someone).
|
|
@@ -60,7 +61,8 @@ export function replaceSpanToMention(content, user) {
|
|
|
60
61
|
if (!user.mention_name) {
|
|
61
62
|
return content;
|
|
62
63
|
}
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
|
|
64
|
+
const mention = '@' + user.mention_name;
|
|
65
|
+
const mentionEl = `<span class="${MENTION_CLASS}">${mention}</span>`;
|
|
66
|
+
const regex = new RegExp(mentionEl, 'g');
|
|
67
|
+
return content.replace(regex, mention);
|
|
66
68
|
}
|
package/package.json
CHANGED
|
@@ -159,7 +159,7 @@ export function ChatInput(props: ChatInput.IProps): JSX.Element {
|
|
|
159
159
|
) {
|
|
160
160
|
// Run all command providers
|
|
161
161
|
await props.chatCommandRegistry?.onSubmit(model);
|
|
162
|
-
model.send(
|
|
162
|
+
model.send(model.value);
|
|
163
163
|
event.stopPropagation();
|
|
164
164
|
event.preventDefault();
|
|
165
165
|
}
|
|
@@ -37,7 +37,9 @@ export function useChatCommands(
|
|
|
37
37
|
// whether an option is highlighted in the chat commands menu
|
|
38
38
|
const [highlighted, setHighlighted] = useState(false);
|
|
39
39
|
|
|
40
|
-
// whether the chat commands menu is open
|
|
40
|
+
// whether the chat commands menu is open.
|
|
41
|
+
// NOTE: every `setOpen(false)` call should be followed by a
|
|
42
|
+
// `setHighlighted(false)` call.
|
|
41
43
|
const [open, setOpen] = useState(false);
|
|
42
44
|
|
|
43
45
|
// current list of chat commands matched by the current word.
|
|
@@ -90,7 +92,12 @@ export function useChatCommands(
|
|
|
90
92
|
|
|
91
93
|
// Otherwise, open/close the menu based on the presence of command
|
|
92
94
|
// completions and set the menu entries.
|
|
93
|
-
|
|
95
|
+
if (commandCompletions.length) {
|
|
96
|
+
setOpen(true);
|
|
97
|
+
} else {
|
|
98
|
+
setOpen(false);
|
|
99
|
+
setHighlighted(false);
|
|
100
|
+
}
|
|
94
101
|
setCommands(commandCompletions);
|
|
95
102
|
}
|
|
96
103
|
|
|
@@ -138,7 +145,8 @@ export function useChatCommands(
|
|
|
138
145
|
autocompleteProps: {
|
|
139
146
|
open,
|
|
140
147
|
options: commands,
|
|
141
|
-
getOptionLabel: (command: ChatCommand) =>
|
|
148
|
+
getOptionLabel: (command: ChatCommand | string) =>
|
|
149
|
+
typeof command === 'string' ? '' : command.name,
|
|
142
150
|
renderOption: (
|
|
143
151
|
defaultProps,
|
|
144
152
|
command: ChatCommand,
|
package/src/input-model.ts
CHANGED
|
@@ -11,8 +11,6 @@ import { ISelectionWatcher } from './selection-watcher';
|
|
|
11
11
|
import { IChatContext } from './model';
|
|
12
12
|
import { IAttachment, IUser } from './types';
|
|
13
13
|
|
|
14
|
-
const WHITESPACE = new Set([' ', '\n', '\t']);
|
|
15
|
-
|
|
16
14
|
/**
|
|
17
15
|
* The chat input interface.
|
|
18
16
|
*/
|
|
@@ -525,6 +523,18 @@ export namespace InputModel {
|
|
|
525
523
|
}
|
|
526
524
|
|
|
527
525
|
namespace Private {
|
|
526
|
+
const WHITESPACE = new Set([' ', '\n', '\t']);
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Returns the start index (inclusive) & end index (exclusive) that contain
|
|
530
|
+
* the current word. The start & end index can be passed to `String.slice()`
|
|
531
|
+
* to extract the current word. The returned range never includes any
|
|
532
|
+
* whitespace character unless it is escaped by a backslash `\`.
|
|
533
|
+
*
|
|
534
|
+
* NOTE: the escape sequence handling here is naive and non-recursive. This
|
|
535
|
+
* function considers the space in "`\\ `" as escaped, even though "`\\ `"
|
|
536
|
+
* defines a backslash followed by an _unescaped_ space in most languages.
|
|
537
|
+
*/
|
|
528
538
|
export function getCurrentWordBoundaries(
|
|
529
539
|
input: string,
|
|
530
540
|
cursorIndex: number
|
|
@@ -533,11 +543,30 @@ namespace Private {
|
|
|
533
543
|
let end = cursorIndex;
|
|
534
544
|
const n = input.length;
|
|
535
545
|
|
|
536
|
-
while (
|
|
546
|
+
while (
|
|
547
|
+
// terminate when `input[start - 1]` is whitespace
|
|
548
|
+
// i.e. `input[start]` is never whitespace after exiting
|
|
549
|
+
(start - 1 >= 0 && !WHITESPACE.has(input[start - 1])) ||
|
|
550
|
+
// unless it is preceded by a backslash
|
|
551
|
+
(start - 2 >= 0 &&
|
|
552
|
+
input[start - 2] === '\\' &&
|
|
553
|
+
WHITESPACE.has(input[start - 1]))
|
|
554
|
+
) {
|
|
537
555
|
start--;
|
|
538
556
|
}
|
|
539
557
|
|
|
540
|
-
|
|
558
|
+
// `end` is an exclusive index unlike `start`, hence the different `while`
|
|
559
|
+
// condition here
|
|
560
|
+
while (
|
|
561
|
+
// terminate when `input[end]` is whitespace
|
|
562
|
+
// i.e. `input[end]` may be whitespace after exiting
|
|
563
|
+
(end < n && !WHITESPACE.has(input[end])) ||
|
|
564
|
+
// unless it is preceded by a backslash
|
|
565
|
+
(end < n &&
|
|
566
|
+
end - 1 >= 0 &&
|
|
567
|
+
input[end - 1] === '\\' &&
|
|
568
|
+
WHITESPACE.has(input[end]))
|
|
569
|
+
) {
|
|
541
570
|
end++;
|
|
542
571
|
}
|
|
543
572
|
|
package/src/types.ts
CHANGED
|
@@ -14,9 +14,14 @@ export interface IUser {
|
|
|
14
14
|
color?: string;
|
|
15
15
|
avatar_url?: string;
|
|
16
16
|
/**
|
|
17
|
-
* The string to use to mention a user in the chat.
|
|
17
|
+
* The string to use to mention a user in the chat. This is computed via the
|
|
18
|
+
* following procedure:
|
|
19
|
+
*
|
|
20
|
+
* 1. Let `mention_name = user.display_name || user.name || user.username`.
|
|
21
|
+
*
|
|
22
|
+
* 2. Replace each ' ' character with '-' in `mention_name`.
|
|
18
23
|
*/
|
|
19
|
-
mention_name
|
|
24
|
+
mention_name: string;
|
|
20
25
|
/**
|
|
21
26
|
* Boolean identifying if user is a bot.
|
|
22
27
|
*/
|
package/src/utils.ts
CHANGED
|
@@ -60,9 +60,10 @@ export function replaceMentionToSpan(content: string, user: IUser): string {
|
|
|
60
60
|
if (!user.mention_name) {
|
|
61
61
|
return content;
|
|
62
62
|
}
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
|
|
63
|
+
const mention = '@' + user.mention_name;
|
|
64
|
+
const regex = new RegExp(mention, 'g');
|
|
65
|
+
const mentionEl = `<span class="${MENTION_CLASS}">${mention}</span>`;
|
|
66
|
+
return content.replace(regex, mentionEl);
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
/**
|
|
@@ -75,7 +76,8 @@ export function replaceSpanToMention(content: string, user: IUser): string {
|
|
|
75
76
|
if (!user.mention_name) {
|
|
76
77
|
return content;
|
|
77
78
|
}
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
|
|
79
|
+
const mention = '@' + user.mention_name;
|
|
80
|
+
const mentionEl = `<span class="${MENTION_CLASS}">${mention}</span>`;
|
|
81
|
+
const regex = new RegExp(mentionEl, 'g');
|
|
82
|
+
return content.replace(regex, mention);
|
|
81
83
|
}
|