@haklex/rich-ext-chat 0.4.1 → 0.5.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.
@@ -0,0 +1,421 @@
1
+ import { A as variantPill, C as participantLabel, D as rail, E as participantRow, M as variantPillHint, N as variantPillName, O as sectionLabel, P as variantStack, S as participantInput, T as participantPillUser, _ as modalBody, a as buttonGhost, b as pane, c as editLabel, d as messageActions, f as messageCard, g as modal, h as messageTextarea, i as button, j as variantPillActive, k as semanticClassNames, l as editOverlay, m as messageSelect, n as addMessage, o as buttonPrimary, p as messageHead, r as addMessageButton, s as editContainer, t as ChatRenderer, u as editorDialogPopup, v as modalFooter, w as participantPill, x as participantCard, y as modalHeader } from "./ChatRenderer-DYZpPFed.js";
2
+ import { i as _defineProperty, n as $isChatNode, o as createMessageId, r as ChatNode } from "./ChatNode-DIZ2_6jG.js";
3
+ import { $getNodeByKey, $insertNodes } from "lexical";
4
+ import { ArrowDown, ArrowUp, MessageSquare, Pencil, Trash2 } from "lucide-react";
5
+ import { createContext, createElement, useCallback, useContext, useEffect, useRef, useState } from "react";
6
+ import { useColorScheme } from "@haklex/rich-editor";
7
+ import { presentDialog } from "@haklex/rich-editor-ui";
8
+ import { usePortalTheme } from "@haklex/rich-style-token";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ //#region ../../node_modules/.pnpm/@lexical+react@0.44.0_react-dom@19.2.5_react@19.2.5__react@19.2.5_yjs@13.6.29/node_modules/@lexical/react/LexicalComposerContext.prod.mjs
11
+ /**
12
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
13
+ *
14
+ * This source code is licensed under the MIT license found in the
15
+ * LICENSE file in the root directory of this source tree.
16
+ *
17
+ */
18
+ var r = createContext(null);
19
+ function o() {
20
+ const n = useContext(r);
21
+ return n ?? function(n, ...e) {
22
+ const r = new URL("https://lexical.dev/docs/error"), t = new URLSearchParams();
23
+ t.append("code", n);
24
+ for (const n of e) t.append("v", n);
25
+ throw r.search = t.toString(), Error(`Minified Lexical error #${n}; visit ${r.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`);
26
+ }(8), n;
27
+ }
28
+ //#endregion
29
+ //#region src/variant-reducer.ts
30
+ function switchVariant(next, participants) {
31
+ return participants.map((p, idx) => {
32
+ if (idx !== 1) return p;
33
+ if (next === "user-user") return {
34
+ ...p,
35
+ kind: "user"
36
+ };
37
+ return {
38
+ ...p,
39
+ kind: "agent"
40
+ };
41
+ });
42
+ }
43
+ //#endregion
44
+ //#region src/ChatEditorModal.tsx
45
+ var VARIANT_LABELS = {
46
+ "user-agent": {
47
+ name: "user · agent",
48
+ hint: "Bubble + article"
49
+ },
50
+ "user-user": {
51
+ name: "user · user",
52
+ hint: "Both bubbles"
53
+ }
54
+ };
55
+ function defaultName(p) {
56
+ return p.name ?? (p.kind === "agent" ? "Assistant" : "User");
57
+ }
58
+ var ChatEditorModal = ({ initial, dismiss, onCommit, onCancel }) => {
59
+ const [variant, setVariant] = useState(initial.variant);
60
+ const [participants, setParticipants] = useState(initial.participants);
61
+ const [messages, setMessages] = useState(initial.messages);
62
+ const handleVariantChange = useCallback((next) => {
63
+ setVariant(next);
64
+ setParticipants((current) => switchVariant(next, current));
65
+ }, []);
66
+ const updateParticipant = useCallback((id, patch) => {
67
+ setParticipants((prev) => prev.map((p) => p.id === id ? {
68
+ ...p,
69
+ ...patch
70
+ } : p));
71
+ }, []);
72
+ const addMessage$1 = useCallback(() => {
73
+ setMessages((prev) => {
74
+ const recent = prev.at(-1);
75
+ const fallback = participants[0]?.id ?? "";
76
+ const participantId = recent ? recent.participantId : fallback;
77
+ return [...prev, {
78
+ id: createMessageId(),
79
+ participantId,
80
+ content: ""
81
+ }];
82
+ });
83
+ }, [participants]);
84
+ const updateMessage = useCallback((id, patch) => {
85
+ setMessages((prev) => prev.map((m) => m.id === id ? {
86
+ ...m,
87
+ ...patch
88
+ } : m));
89
+ }, []);
90
+ const moveMessage = useCallback((id, direction) => {
91
+ setMessages((prev) => {
92
+ const idx = prev.findIndex((m) => m.id === id);
93
+ if (idx < 0) return prev;
94
+ const target = idx + direction;
95
+ if (target < 0 || target >= prev.length) return prev;
96
+ const next = [...prev];
97
+ [next[idx], next[target]] = [next[target], next[idx]];
98
+ return next;
99
+ });
100
+ }, []);
101
+ const deleteMessage = useCallback((id) => {
102
+ setMessages((prev) => prev.filter((m) => m.id !== id));
103
+ }, []);
104
+ const handleDone = useCallback(() => {
105
+ onCommit?.({
106
+ variant,
107
+ participants,
108
+ messages
109
+ });
110
+ dismiss();
111
+ }, [
112
+ onCommit,
113
+ variant,
114
+ participants,
115
+ messages,
116
+ dismiss
117
+ ]);
118
+ const handleCancel = useCallback(() => {
119
+ onCancel?.();
120
+ dismiss();
121
+ }, [onCancel, dismiss]);
122
+ return /* @__PURE__ */ jsxs("div", {
123
+ className: modal,
124
+ children: [
125
+ /* @__PURE__ */ jsx("div", {
126
+ className: modalHeader,
127
+ children: "Edit chat"
128
+ }),
129
+ /* @__PURE__ */ jsxs("div", {
130
+ className: modalBody,
131
+ children: [/* @__PURE__ */ jsxs("aside", {
132
+ className: rail,
133
+ children: [
134
+ /* @__PURE__ */ jsx("div", {
135
+ className: sectionLabel,
136
+ children: "Variant"
137
+ }),
138
+ /* @__PURE__ */ jsx("div", {
139
+ className: variantStack,
140
+ children: Object.keys(VARIANT_LABELS).map((key) => /* @__PURE__ */ jsxs("button", {
141
+ className: `${variantPill} ${variant === key ? variantPillActive : ""}`,
142
+ type: "button",
143
+ onClick: () => handleVariantChange(key),
144
+ children: [/* @__PURE__ */ jsx("div", {
145
+ className: variantPillName,
146
+ children: VARIANT_LABELS[key].name
147
+ }), /* @__PURE__ */ jsx("div", {
148
+ className: variantPillHint,
149
+ children: VARIANT_LABELS[key].hint
150
+ })]
151
+ }, key))
152
+ }),
153
+ /* @__PURE__ */ jsx("div", {
154
+ className: sectionLabel,
155
+ children: "Participants"
156
+ }),
157
+ participants.map((p) => /* @__PURE__ */ jsxs("div", {
158
+ className: participantCard,
159
+ children: [/* @__PURE__ */ jsxs("div", {
160
+ className: participantRow,
161
+ children: [/* @__PURE__ */ jsx("span", {
162
+ className: `${participantPill} ${p.kind === "user" ? participantPillUser : ""}`,
163
+ children: p.kind
164
+ }), /* @__PURE__ */ jsx("input", {
165
+ className: participantInput,
166
+ placeholder: "Display name",
167
+ type: "text",
168
+ value: p.name ?? "",
169
+ onChange: (e) => updateParticipant(p.id, { name: e.target.value || void 0 })
170
+ })]
171
+ }), /* @__PURE__ */ jsxs("div", {
172
+ className: participantRow,
173
+ children: [/* @__PURE__ */ jsx("span", {
174
+ className: participantLabel,
175
+ children: "Avatar"
176
+ }), /* @__PURE__ */ jsx("input", {
177
+ className: participantInput,
178
+ placeholder: "URL (optional)",
179
+ type: "text",
180
+ value: p.avatar ?? "",
181
+ onChange: (e) => updateParticipant(p.id, { avatar: e.target.value || void 0 })
182
+ })]
183
+ })]
184
+ }, p.id))
185
+ ]
186
+ }), /* @__PURE__ */ jsxs("main", {
187
+ className: pane,
188
+ children: [
189
+ /* @__PURE__ */ jsxs("div", {
190
+ className: sectionLabel,
191
+ children: ["Messages — ", messages.length]
192
+ }),
193
+ messages.map((m) => /* @__PURE__ */ jsxs("div", {
194
+ className: messageCard,
195
+ children: [/* @__PURE__ */ jsxs("div", {
196
+ className: messageHead,
197
+ children: [/* @__PURE__ */ jsx("select", {
198
+ className: messageSelect,
199
+ value: m.participantId,
200
+ onChange: (e) => updateMessage(m.id, { participantId: e.target.value }),
201
+ children: participants.map((p) => /* @__PURE__ */ jsxs("option", {
202
+ value: p.id,
203
+ children: [
204
+ defaultName(p),
205
+ " (",
206
+ p.kind,
207
+ ")"
208
+ ]
209
+ }, p.id))
210
+ }), /* @__PURE__ */ jsxs("div", {
211
+ className: messageActions,
212
+ children: [
213
+ /* @__PURE__ */ jsx("button", {
214
+ "aria-label": "Move up",
215
+ className: `${button} ${buttonGhost}`,
216
+ type: "button",
217
+ onClick: () => moveMessage(m.id, -1),
218
+ children: /* @__PURE__ */ jsx(ArrowUp, { size: 12 })
219
+ }),
220
+ /* @__PURE__ */ jsx("button", {
221
+ "aria-label": "Move down",
222
+ className: `${button} ${buttonGhost}`,
223
+ type: "button",
224
+ onClick: () => moveMessage(m.id, 1),
225
+ children: /* @__PURE__ */ jsx(ArrowDown, { size: 12 })
226
+ }),
227
+ /* @__PURE__ */ jsx("button", {
228
+ "aria-label": "Delete",
229
+ className: `${button} ${buttonGhost}`,
230
+ type: "button",
231
+ onClick: () => deleteMessage(m.id),
232
+ children: /* @__PURE__ */ jsx(Trash2, { size: 12 })
233
+ })
234
+ ]
235
+ })]
236
+ }), /* @__PURE__ */ jsx("textarea", {
237
+ className: messageTextarea,
238
+ value: m.content,
239
+ onChange: (e) => updateMessage(m.id, { content: e.target.value })
240
+ })]
241
+ }, m.id)),
242
+ /* @__PURE__ */ jsx("div", {
243
+ className: addMessage,
244
+ children: /* @__PURE__ */ jsx("button", {
245
+ className: addMessageButton,
246
+ type: "button",
247
+ onClick: addMessage$1,
248
+ children: "+ Add message"
249
+ })
250
+ })
251
+ ]
252
+ })]
253
+ }),
254
+ /* @__PURE__ */ jsxs("div", {
255
+ className: modalFooter,
256
+ children: [/* @__PURE__ */ jsx("button", {
257
+ className: button,
258
+ type: "button",
259
+ onClick: handleCancel,
260
+ children: "Cancel"
261
+ }), /* @__PURE__ */ jsx("button", {
262
+ className: `${button} ${buttonPrimary}`,
263
+ type: "button",
264
+ onClick: handleDone,
265
+ children: "Done"
266
+ })]
267
+ })
268
+ ]
269
+ });
270
+ };
271
+ //#endregion
272
+ //#region src/ChatEditRenderer.tsx
273
+ var ChatEditRenderer = ({ variant, participants, messages, onChange, onCancel, registerOpenTrigger }) => {
274
+ const { className: portalClassName } = usePortalTheme();
275
+ const colorScheme = useColorScheme();
276
+ const openEditor = useCallback(() => {
277
+ presentDialog({
278
+ content: ({ dismiss }) => /* @__PURE__ */ jsx(ChatEditorModal, {
279
+ dismiss,
280
+ initial: {
281
+ variant,
282
+ participants,
283
+ messages
284
+ },
285
+ onCancel,
286
+ onCommit: onChange
287
+ }),
288
+ className: editorDialogPopup,
289
+ portalClassName,
290
+ theme: colorScheme,
291
+ showCloseButton: false,
292
+ clickOutsideToDismiss: false
293
+ });
294
+ }, [
295
+ variant,
296
+ participants,
297
+ messages,
298
+ onChange,
299
+ onCancel,
300
+ portalClassName,
301
+ colorScheme
302
+ ]);
303
+ useEffect(() => {
304
+ registerOpenTrigger?.(openEditor);
305
+ }, [openEditor, registerOpenTrigger]);
306
+ return /* @__PURE__ */ jsxs("div", {
307
+ className: `${editContainer} ${semanticClassNames.editContainer}`,
308
+ children: [/* @__PURE__ */ jsx(ChatRenderer, {
309
+ messages,
310
+ participants,
311
+ variant
312
+ }), /* @__PURE__ */ jsx("button", {
313
+ "aria-label": "Edit chat",
314
+ className: `${editOverlay} ${semanticClassNames.editOverlay}`,
315
+ type: "button",
316
+ onClick: openEditor,
317
+ children: /* @__PURE__ */ jsxs("span", {
318
+ className: `${editLabel} ${semanticClassNames.editLabel}`,
319
+ children: [/* @__PURE__ */ jsx(Pencil, { size: 14 }), " Edit"]
320
+ })
321
+ })]
322
+ });
323
+ };
324
+ //#endregion
325
+ //#region src/ChatEditDecorator.tsx
326
+ var ChatEditDecorator = ({ nodeKey, variant, participants, messages }) => {
327
+ const [editor] = o();
328
+ const hasOpenedRef = useRef(false);
329
+ const openTriggerRef = useRef(null);
330
+ const messagesLengthRef = useRef(messages.length);
331
+ const onChange = useCallback((next) => {
332
+ editor.update(() => {
333
+ const node = $getNodeByKey(nodeKey);
334
+ if ($isChatNode(node)) {
335
+ node.setVariant(next.variant);
336
+ node.setParticipants(next.participants);
337
+ node.setMessages(next.messages);
338
+ }
339
+ });
340
+ }, [editor, nodeKey]);
341
+ const onCancel = useCallback(() => {
342
+ editor.update(() => {
343
+ const node = $getNodeByKey(nodeKey);
344
+ if ($isChatNode(node) && node.getMessages().length === 0) node.remove();
345
+ });
346
+ }, [editor, nodeKey]);
347
+ useEffect(() => {
348
+ if (hasOpenedRef.current) return;
349
+ if (messagesLengthRef.current > 0) return;
350
+ const trigger = openTriggerRef.current;
351
+ if (!trigger) return;
352
+ hasOpenedRef.current = true;
353
+ trigger();
354
+ }, []);
355
+ return /* @__PURE__ */ jsx(ChatEditRenderer, {
356
+ messages,
357
+ participants,
358
+ variant,
359
+ registerOpenTrigger: (open) => {
360
+ openTriggerRef.current = open;
361
+ },
362
+ onCancel,
363
+ onChange
364
+ });
365
+ };
366
+ //#endregion
367
+ //#region src/nodes/ChatEditNode.ts
368
+ var ChatEditNode = class ChatEditNode extends ChatNode {
369
+ static clone(node) {
370
+ return new ChatEditNode({
371
+ variant: node.__variant,
372
+ participants: node.__participants,
373
+ messages: node.__messages
374
+ }, node.__key);
375
+ }
376
+ static importJSON(serializedNode) {
377
+ return new ChatEditNode({
378
+ variant: serializedNode.variant,
379
+ participants: serializedNode.participants,
380
+ messages: serializedNode.messages
381
+ });
382
+ }
383
+ decorate(_editor, _config) {
384
+ return createElement(ChatEditDecorator, {
385
+ nodeKey: this.__key,
386
+ variant: this.__variant,
387
+ participants: this.__participants,
388
+ messages: this.__messages
389
+ });
390
+ }
391
+ };
392
+ _defineProperty(ChatEditNode, "commandItems", [{
393
+ title: "Chat",
394
+ icon: createElement(MessageSquare, { size: 20 }),
395
+ description: "Embed a conversation snapshot",
396
+ keywords: [
397
+ "chat",
398
+ "conversation",
399
+ "dialog",
400
+ "agent"
401
+ ],
402
+ section: "MEDIA",
403
+ placement: ["slash", "toolbar"],
404
+ group: "insert",
405
+ onSelect: (editor) => {
406
+ editor.update(() => {
407
+ $insertNodes([$createChatEditNode({ variant: "user-agent" })]);
408
+ });
409
+ }
410
+ }]);
411
+ function $createChatEditNode(payload) {
412
+ return new ChatEditNode(payload);
413
+ }
414
+ function $isChatEditNode(node) {
415
+ return node instanceof ChatEditNode;
416
+ }
417
+ //#endregion
418
+ //#region src/edit.ts
419
+ var chatEditNodes = [ChatEditNode];
420
+ //#endregion
421
+ export { ChatEditDecorator as a, ChatEditNode as i, $createChatEditNode as n, ChatEditRenderer as o, $isChatEditNode as r, ChatEditorModal as s, chatEditNodes as t };
package/dist/edit.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { Klass, LexicalNode } from 'lexical';
2
+ export type { ChatEditDecoratorProps } from './ChatEditDecorator';
3
+ export { ChatEditDecorator } from './ChatEditDecorator';
4
+ export type { ChatEditorModalProps } from './ChatEditorModal';
5
+ export { ChatEditorModal } from './ChatEditorModal';
6
+ export type { ChatEditRendererProps } from './ChatEditRenderer';
7
+ export { ChatEditRenderer } from './ChatEditRenderer';
8
+ export { $createChatEditNode, $isChatEditNode, ChatEditNode } from './nodes/ChatEditNode';
9
+ export declare const chatEditNodes: Array<Klass<LexicalNode>>;
10
+ //# sourceMappingURL=edit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../src/edit.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAIlD,YAAY,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAE1F,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAkB,CAAC"}
package/dist/edit.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { a as ChatEditDecorator, i as ChatEditNode, n as $createChatEditNode, o as ChatEditRenderer, r as $isChatEditNode, s as ChatEditorModal, t as chatEditNodes } from "./edit-C2JFybx6.js";
2
+ export { $createChatEditNode, $isChatEditNode, ChatEditDecorator, ChatEditNode, ChatEditRenderer, ChatEditorModal, chatEditNodes };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,4 @@
1
- export type { ChatEditDecoratorProps } from './ChatEditDecorator';
2
- export { ChatEditDecorator } from './ChatEditDecorator';
3
- export type { ChatEditorModalProps } from './ChatEditorModal';
4
- export { ChatEditorModal } from './ChatEditorModal';
5
- export type { ChatEditRendererProps } from './ChatEditRenderer';
6
- export { ChatEditRenderer } from './ChatEditRenderer';
7
- export { ChatRenderer } from './ChatRenderer';
8
- export { chatEditNodes, chatNodes } from './nodes';
9
- export { $createChatEditNode, $isChatEditNode, ChatEditNode } from './nodes/ChatEditNode';
10
- export { $createChatNode, $isChatNode, ChatNode } from './nodes/ChatNode';
11
- export type { ChatMessage, ChatParticipant, ChatParticipantKind, ChatRendererProps, ChatVariant, SerializedChatNode, } from './types';
1
+ export * from './edit';
2
+ export * from './node';
3
+ export * from './renderer';
12
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AAEtB,YAAY,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC1E,YAAY,EACV,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,WAAW,EACX,kBAAkB,GACnB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC"}