@sybilion/uilib 1.3.33 → 1.3.36

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.
Files changed (54) hide show
  1. package/dist/esm/components/ui/Chat/ChatChrome/ChatChrome.js +2 -2
  2. package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.helpers.js +18 -4
  3. package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.js +39 -52
  4. package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.styl.js +2 -2
  5. package/dist/esm/components/ui/Chat/ChatPrompt/ChatPromptComposer.js +25 -0
  6. package/dist/esm/components/ui/Chat/ChatPrompt/chatPromptDoc.js +17 -0
  7. package/dist/esm/components/ui/Chat/ChatPrompt/useChatPromptEditor.js +163 -0
  8. package/dist/esm/components/ui/Tooltip/Tooltip.styl.js +1 -1
  9. package/dist/esm/index.js +2 -0
  10. package/dist/esm/tiptap/slash-mention/SlashSuggestionList.js +53 -0
  11. package/dist/esm/tiptap/slash-mention/SlashSuggestionList.styl.js +7 -0
  12. package/dist/esm/tiptap/slash-mention/createSlashMentionExtension.js +151 -0
  13. package/dist/esm/tiptap/slash-mention/defaultChatSlashItems.js +12 -0
  14. package/dist/esm/types/src/components/ui/Chat/Chat.types.d.ts +3 -0
  15. package/dist/esm/types/src/components/ui/Chat/ChatChrome/ChatChrome.d.ts +1 -1
  16. package/dist/esm/types/src/components/ui/Chat/ChatChrome/ChatChrome.types.d.ts +5 -0
  17. package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPrompt.d.ts +1 -1
  18. package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPrompt.helpers.d.ts +6 -0
  19. package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPromptComposer.d.ts +13 -0
  20. package/dist/esm/types/src/components/ui/Chat/ChatPrompt/chatPromptDoc.d.ts +3 -0
  21. package/dist/esm/types/src/components/ui/Chat/ChatPrompt/useChatPromptEditor.d.ts +20 -0
  22. package/dist/esm/types/src/components/ui/Chat/index.d.ts +1 -0
  23. package/dist/esm/types/src/components/ui/Input/Input.d.ts +1 -1
  24. package/dist/esm/types/src/docs/pages/ChatSlashCommandsPage.d.ts +1 -0
  25. package/dist/esm/types/src/index.d.ts +1 -0
  26. package/dist/esm/types/src/tiptap/slash-mention/SlashSuggestionList.d.ts +12 -0
  27. package/dist/esm/types/src/tiptap/slash-mention/createSlashMentionExtension.d.ts +21 -0
  28. package/dist/esm/types/src/tiptap/slash-mention/defaultChatSlashItems.d.ts +4 -0
  29. package/dist/esm/types/src/tiptap/slash-mention/index.d.ts +5 -0
  30. package/dist/esm/types/src/tiptap/slash-mention/types.d.ts +25 -0
  31. package/package.json +15 -1
  32. package/src/components/ui/Chat/Chat.types.ts +4 -0
  33. package/src/components/ui/Chat/ChatChrome/ChatChrome.tsx +4 -0
  34. package/src/components/ui/Chat/ChatChrome/ChatChrome.types.ts +5 -0
  35. package/src/components/ui/Chat/ChatPrompt/ChatPrompt.helpers.ts +43 -0
  36. package/src/components/ui/Chat/ChatPrompt/ChatPrompt.styl +33 -5
  37. package/src/components/ui/Chat/ChatPrompt/ChatPrompt.styl.d.ts +2 -1
  38. package/src/components/ui/Chat/ChatPrompt/ChatPrompt.tsx +50 -106
  39. package/src/components/ui/Chat/ChatPrompt/ChatPromptComposer.tsx +93 -0
  40. package/src/components/ui/Chat/ChatPrompt/chatPromptDoc.ts +18 -0
  41. package/src/components/ui/Chat/ChatPrompt/useChatPromptEditor.ts +214 -0
  42. package/src/components/ui/Chat/index.ts +1 -0
  43. package/src/components/ui/Tooltip/Tooltip.styl +1 -0
  44. package/src/docs/pages/ChatSlashCommandsPage.tsx +139 -0
  45. package/src/docs/pages/TooltipPage.tsx +1 -1
  46. package/src/docs/registry.ts +6 -0
  47. package/src/index.ts +1 -0
  48. package/src/tiptap/slash-mention/SlashSuggestionList.styl +48 -0
  49. package/src/tiptap/slash-mention/SlashSuggestionList.styl.d.ts +11 -0
  50. package/src/tiptap/slash-mention/SlashSuggestionList.tsx +109 -0
  51. package/src/tiptap/slash-mention/createSlashMentionExtension.ts +217 -0
  52. package/src/tiptap/slash-mention/defaultChatSlashItems.ts +18 -0
  53. package/src/tiptap/slash-mention/index.ts +16 -0
  54. package/src/tiptap/slash-mention/types.ts +29 -0
@@ -0,0 +1,217 @@
1
+ import type { MutableRefObject } from 'react';
2
+
3
+ import { type Editor, mergeAttributes } from '@tiptap/core';
4
+ import Mention from '@tiptap/extension-mention';
5
+ import type { EditorState } from '@tiptap/pm/state';
6
+ import { ReactRenderer } from '@tiptap/react';
7
+ import type { SuggestionProps } from '@tiptap/suggestion';
8
+
9
+ import {
10
+ SlashSuggestionList,
11
+ type SlashSuggestionListHandle,
12
+ } from './SlashSuggestionList';
13
+ import { filterSlashItems } from './defaultChatSlashItems';
14
+ import type {
15
+ CreateSlashMentionExtensionOptions,
16
+ SlashCommandItem,
17
+ } from './types';
18
+
19
+ export function slashMentionSuggestionRender(
20
+ uiRef: MutableRefObject<SlashSuggestionListHandle | null>,
21
+ ): {
22
+ onStart?: (
23
+ props: SuggestionProps<SlashCommandItem, SlashCommandItem>,
24
+ ) => void;
25
+ onUpdate?: (
26
+ props: SuggestionProps<SlashCommandItem, SlashCommandItem>,
27
+ ) => void;
28
+ onExit?: (props: SuggestionProps<SlashCommandItem, SlashCommandItem>) => void;
29
+ onKeyDown?: (p: {
30
+ view: unknown;
31
+ event: KeyboardEvent;
32
+ range: { from: number; to: number };
33
+ }) => boolean;
34
+ } {
35
+ let popup: ReactRenderer<{
36
+ items: SlashCommandItem[];
37
+ command: (item: SlashCommandItem) => void;
38
+ listHandleRef: MutableRefObject<SlashSuggestionListHandle | null>;
39
+ }> | null = null;
40
+
41
+ const place = (
42
+ props: Pick<
43
+ SuggestionProps<SlashCommandItem, SlashCommandItem>,
44
+ 'clientRect'
45
+ >,
46
+ ) => {
47
+ if (!popup?.element) return;
48
+ const rect = props.clientRect?.();
49
+ if (!rect) return;
50
+ const el = popup.element;
51
+ el.style.left = `${rect.left}px`;
52
+ el.style.top = `${rect.bottom + 4}px`;
53
+ };
54
+
55
+ return {
56
+ onStart: props => {
57
+ uiRef.current = null;
58
+ popup?.destroy?.();
59
+ popup = new ReactRenderer(SlashSuggestionList, {
60
+ editor: props.editor,
61
+ props: {
62
+ items: props.items,
63
+ command: props.command,
64
+ listHandleRef: uiRef,
65
+ },
66
+ });
67
+ popup.element.style.position = 'fixed';
68
+ popup.element.style.margin = '0';
69
+ popup.element.style.zIndex = '10002';
70
+ document.body.append(popup.element);
71
+ place(props);
72
+ },
73
+ onUpdate: props => {
74
+ if (!popup) return;
75
+ popup.updateProps({
76
+ items: props.items,
77
+ command: props.command,
78
+ listHandleRef: uiRef,
79
+ });
80
+ place(props);
81
+ },
82
+ onExit: () => {
83
+ popup?.destroy();
84
+ popup?.element?.remove?.();
85
+ popup = null;
86
+ uiRef.current = null;
87
+ },
88
+ onKeyDown: ({ event }) =>
89
+ uiRef.current?.onKeyboardEvent(event as KeyboardEvent) ?? false,
90
+ };
91
+ }
92
+
93
+ function insertDefaultMention(
94
+ editor: Editor,
95
+ range: { from: number; to: number },
96
+ props: SlashCommandItem,
97
+ slashChar: string,
98
+ ): void {
99
+ const nodeAfter = editor.view.state.selection.$to.nodeAfter;
100
+ const extend = nodeAfter?.text?.startsWith(' ') ? 1 : 0;
101
+ const adjusted = extend ? { ...range, to: range.to + extend } : range;
102
+
103
+ editor
104
+ .chain()
105
+ .focus()
106
+ .insertContentAt(adjusted, [
107
+ {
108
+ type: 'mention',
109
+ attrs: {
110
+ id: props.id,
111
+ label: props.label,
112
+ mentionSuggestionChar: slashChar,
113
+ },
114
+ },
115
+ { type: 'text', text: ' ' },
116
+ ])
117
+ .run();
118
+
119
+ queueMicrotask(() => {
120
+ editor.view.dom.ownerDocument?.defaultView
121
+ ?.getSelection?.()
122
+ ?.collapseToEnd();
123
+ });
124
+ }
125
+
126
+ function allowSlashTrigger({
127
+ state,
128
+ range,
129
+ }: {
130
+ editor: Editor;
131
+ state: EditorState;
132
+ range: { from: number; to: number };
133
+ }) {
134
+ const type = state.schema.nodes['mention'];
135
+ const $from = state.doc.resolve(range.from);
136
+ if (!type || !$from.parent.type.contentMatch.matchType(type)) return false;
137
+ if ($from.parentOffset === 0) return true;
138
+ const before = state.doc.textBetween(range.from - 1, range.from);
139
+ return /\s/.test(before);
140
+ }
141
+
142
+ export type CreateSlashMentionExtensionConfiguredOptions =
143
+ CreateSlashMentionExtensionOptions & {
144
+ onSuggestionUiActiveChange?: (active: boolean) => void;
145
+ };
146
+
147
+ export function createSlashMentionExtension({
148
+ items: resolvedItems,
149
+ slashChar = '/',
150
+ pluginKey,
151
+ onItemCommand,
152
+ onSuggestionUiActiveChange,
153
+ }: CreateSlashMentionExtensionConfiguredOptions) {
154
+ const uiRef: MutableRefObject<SlashSuggestionListHandle | null> = {
155
+ current: null,
156
+ };
157
+
158
+ return Mention.configure({
159
+ renderText({ node }) {
160
+ return `/${node.attrs.id as string}`;
161
+ },
162
+ renderHTML({ options, node, suggestion }) {
163
+ const suggestionChar = suggestion?.char ?? slashChar;
164
+ const id =
165
+ typeof node.attrs.id === 'string'
166
+ ? node.attrs.id
167
+ : String(node.attrs.id ?? '');
168
+ return [
169
+ 'span',
170
+ mergeAttributes(
171
+ {
172
+ 'data-type': 'mention',
173
+ 'data-slash-command': id,
174
+ class: 'slash-mention',
175
+ },
176
+ options.HTMLAttributes,
177
+ ),
178
+ `${suggestionChar}${id}`,
179
+ ];
180
+ },
181
+ suggestion: {
182
+ char: slashChar,
183
+ pluginKey,
184
+ allowedPrefixes: [' ', '\n', '\t', '\r'],
185
+ allow: props => allowSlashTrigger(props),
186
+ items: ({ query }) =>
187
+ Promise.resolve(filterSlashItems(resolvedItems, query)),
188
+ command: ({ editor, range, props }) => {
189
+ const item = props as SlashCommandItem;
190
+ if (onItemCommand?.({ editor, range, item }) === true) {
191
+ queueMicrotask(() => {
192
+ editor.view.dom.ownerDocument?.defaultView
193
+ ?.getSelection?.()
194
+ ?.collapseToEnd();
195
+ });
196
+ return null;
197
+ }
198
+ insertDefaultMention(editor, range, item, slashChar);
199
+ return null;
200
+ },
201
+ render: () => {
202
+ const menu = slashMentionSuggestionRender(uiRef);
203
+ return {
204
+ ...menu,
205
+ onStart: props => {
206
+ onSuggestionUiActiveChange?.(true);
207
+ menu.onStart?.(props);
208
+ },
209
+ onExit: props => {
210
+ menu.onExit?.(props);
211
+ onSuggestionUiActiveChange?.(false);
212
+ },
213
+ };
214
+ },
215
+ },
216
+ });
217
+ }
@@ -0,0 +1,18 @@
1
+ import type { SlashCommandItem } from './types';
2
+
3
+ /** Empty default: pass `slashCommandItems` from the app to enable `/` palette. */
4
+ export const DEFAULT_CHAT_SLASH_ITEMS: SlashCommandItem[] = [];
5
+
6
+ export function filterSlashItems(
7
+ items: SlashCommandItem[],
8
+ query: string,
9
+ ): SlashCommandItem[] {
10
+ const q = query.trim().toLowerCase();
11
+ if (!q) return items;
12
+ return items.filter(
13
+ item =>
14
+ item.id.toLowerCase().includes(q) ||
15
+ item.label.toLowerCase().includes(q) ||
16
+ (item.description?.toLowerCase().includes(q) ?? false),
17
+ );
18
+ }
@@ -0,0 +1,16 @@
1
+ export type {
2
+ SlashCommandItem,
3
+ SlashOnItemCommand,
4
+ SlashItemCommandContext,
5
+ CreateSlashMentionExtensionOptions,
6
+ } from './types';
7
+ export {
8
+ DEFAULT_CHAT_SLASH_ITEMS,
9
+ filterSlashItems,
10
+ } from './defaultChatSlashItems';
11
+ export {
12
+ createSlashMentionExtension,
13
+ slashMentionSuggestionRender,
14
+ } from './createSlashMentionExtension';
15
+ export type { CreateSlashMentionExtensionConfiguredOptions } from './createSlashMentionExtension';
16
+ export type { SlashSuggestionListHandle } from './SlashSuggestionList';
@@ -0,0 +1,29 @@
1
+ import type { Editor, Range } from '@tiptap/core';
2
+
3
+ export type SlashCommandItem = {
4
+ id: string;
5
+ label: string;
6
+ description?: string;
7
+ };
8
+
9
+ export type SlashItemCommandContext = {
10
+ editor: Editor;
11
+ range: Range;
12
+ item: SlashCommandItem;
13
+ };
14
+
15
+ /**
16
+ * If provided, run before default mention insertion. Return true to skip inserting a mention node.
17
+ */
18
+ export type SlashOnItemCommand = (ctx: SlashItemCommandContext) => boolean;
19
+
20
+ export type CreateSlashMentionExtensionOptions = {
21
+ /** Items shown in the slash menu (filtered by query after `/`). */
22
+ items: SlashCommandItem[];
23
+ /** Trigger character; use `/` for slash commands. */
24
+ slashChar?: string;
25
+ /** Optional custom plugin key when multiple slash editors exist. */
26
+ pluginKey?: import('@tiptap/pm/state').PluginKey;
27
+ /** Custom handler (e.g. insert a block node instead of a mention). */
28
+ onItemCommand?: SlashOnItemCommand;
29
+ };