@signalcore/ui 1.0.0-beta.2 → 1.0.0-beta.4
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/README.md +126 -21
- package/dist/AgentAbout.js +6 -2
- package/dist/AgentAbout.js.map +1 -1
- package/dist/AgentChat.d.ts +25 -3
- package/dist/AgentChat.js +302 -28
- package/dist/AgentChat.js.map +1 -1
- package/dist/AgentSpace.d.ts +25 -4
- package/dist/AgentSpace.js +55 -11
- package/dist/AgentSpace.js.map +1 -1
- package/dist/Threads.d.ts +73 -0
- package/dist/Threads.js +140 -0
- package/dist/Threads.js.map +1 -0
- package/dist/View.d.ts +48 -0
- package/dist/View.js +101 -0
- package/dist/View.js.map +1 -0
- package/dist/automation/marks.js +7 -3
- package/dist/automation/marks.js.map +1 -1
- package/dist/automation/messages.d.ts +4 -0
- package/dist/automation/messages.js +5 -1
- package/dist/automation/messages.js.map +1 -1
- package/dist/automation/when.js +8 -0
- package/dist/automation/when.js.map +1 -1
- package/dist/blocks.d.ts +83 -2
- package/dist/client.d.ts +23 -5
- package/dist/client.js +8 -2
- package/dist/client.js.map +1 -1
- package/dist/contract.d.ts +9 -4
- package/dist/es.d.ts +2 -0
- package/dist/es.js +34 -3
- package/dist/es.js.map +1 -1
- package/dist/frames.d.ts +7 -1
- package/dist/frames.js.map +1 -1
- package/dist/index.d.ts +14 -7
- package/dist/index.js +11 -4
- package/dist/kit/Agenda.d.ts +25 -0
- package/dist/kit/Agenda.js +119 -0
- package/dist/kit/Agenda.js.map +1 -0
- package/dist/kit/Combobox.js +1 -1
- package/dist/kit/DateField.js +1 -1
- package/dist/kit/Kanban.d.ts +17 -0
- package/dist/kit/Kanban.js +157 -0
- package/dist/kit/Kanban.js.map +1 -0
- package/dist/kit/TimeField.js +1 -1
- package/dist/kit/Timeline.d.ts +14 -0
- package/dist/kit/Timeline.js +71 -0
- package/dist/kit/Timeline.js.map +1 -0
- package/dist/kit/context.d.ts +1 -1
- package/dist/kit/context.js.map +1 -1
- package/dist/kit/messages.d.ts +22 -0
- package/dist/kit/messages.js +10 -0
- package/dist/kit/messages.js.map +1 -1
- package/dist/surface/Form.js +1 -1
- package/dist/surface/Placed.d.ts +20 -0
- package/dist/surface/Placed.js +70 -0
- package/dist/surface/Placed.js.map +1 -0
- package/dist/surface/Surface.d.ts +4 -2
- package/dist/surface/Surface.js +78 -2
- package/dist/surface/Surface.js.map +1 -1
- package/dist/surface/places.d.ts +16 -0
- package/dist/surface/places.js +15 -0
- package/dist/surface/places.js.map +1 -0
- package/dist/surface/scope.d.ts +17 -1
- package/dist/surface/scope.js +1 -0
- package/dist/surface/scope.js.map +1 -1
- package/dist/types.d.ts +23 -0
- package/dist/types.js.map +1 -1
- package/dist/useConversation.d.ts +2 -0
- package/dist/useConversation.js +16 -0
- package/dist/useConversation.js.map +1 -1
- package/dist/widget.js +17 -16
- package/package.json +1 -1
- package/src/AgentAbout.tsx +4 -0
- package/src/AgentChat.tsx +417 -34
- package/src/AgentSpace.tsx +106 -14
- package/src/Threads.tsx +218 -0
- package/src/View.tsx +117 -0
- package/src/automation/marks.tsx +7 -0
- package/src/automation/messages.ts +8 -0
- package/src/automation/when.ts +4 -0
- package/src/blocks.ts +100 -2
- package/src/client.ts +42 -10
- package/src/contract.ts +404 -29
- package/src/es.ts +30 -0
- package/src/frames.ts +3 -1
- package/src/index.ts +36 -3
- package/src/kit/Agenda.tsx +161 -0
- package/src/kit/Kanban.tsx +182 -0
- package/src/kit/Timeline.tsx +58 -0
- package/src/kit/context.tsx +45 -1
- package/src/kit/messages.ts +31 -0
- package/src/styles.css +825 -66
- package/src/surface/Placed.tsx +98 -0
- package/src/surface/Surface.tsx +92 -1
- package/src/surface/places.ts +19 -0
- package/src/surface/scope.ts +24 -1
- package/src/types.ts +24 -0
- package/src/useConversation.ts +10 -0
- package/src/widget/element.tsx +54 -7
- package/src/widget/widget.css +13 -0
package/dist/AgentChat.js
CHANGED
|
@@ -2,6 +2,7 @@ import { actionWords } from "./about.js";
|
|
|
2
2
|
import { classes } from "./classes.js";
|
|
3
3
|
import { KIT_MESSAGES } from "./kit/messages.js";
|
|
4
4
|
import { KitProvider } from "./kit/context.js";
|
|
5
|
+
import { Button } from "./kit/Button.js";
|
|
5
6
|
import { actionMark } from "./marks.js";
|
|
6
7
|
import { BROWSER_MESSAGES, BrowserView } from "./BrowserView.js";
|
|
7
8
|
import { channelParts } from "./channelParts.js";
|
|
@@ -10,13 +11,17 @@ import { useReveal } from "./reveal.js";
|
|
|
10
11
|
import { useConversationFiles } from "./conversationFiles.js";
|
|
11
12
|
import "./frames.js";
|
|
12
13
|
import { AudioPlayer } from "./kit/AudioPlayer.js";
|
|
14
|
+
import { Field } from "./kit/Field.js";
|
|
15
|
+
import { Checkbox, NumberInput, TextArea, TextInput } from "./kit/Inputs.js";
|
|
13
16
|
import { FileChips, UploadChips, carriesFiles, useUploads } from "./kit/FileField.js";
|
|
14
17
|
import { stepGlyph } from "./stepGlyphs.js";
|
|
15
18
|
import { Markdown } from "./surface/Markdown.js";
|
|
19
|
+
import { Placed } from "./surface/Placed.js";
|
|
20
|
+
import { placeOf } from "./surface/places.js";
|
|
16
21
|
import { Surface, framesItself, usePlayed } from "./surface/Surface.js";
|
|
17
22
|
import { NO_TOOL, useConversation } from "./useConversation.js";
|
|
18
23
|
import { Fragment, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
19
|
-
import { ArrowUpIcon, CaretRightIcon, CheckIcon, ChecksIcon, ClockIcon, MicrophoneIcon, PaperclipIcon, ShieldWarningIcon, StopIcon, UploadSimpleIcon, WarningCircleIcon } from "@phosphor-icons/react";
|
|
24
|
+
import { ArrowUpIcon, ArrowsOutSimpleIcon, CaretRightIcon, CheckIcon, ChecksIcon, ClockIcon, MicrophoneIcon, PaperclipIcon, ShieldWarningIcon, SidebarSimpleIcon, StopIcon, UploadSimpleIcon, WarningCircleIcon } from "@phosphor-icons/react";
|
|
20
25
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
21
26
|
//#region src/AgentChat.tsx
|
|
22
27
|
const CHAT_MESSAGES = {
|
|
@@ -54,8 +59,14 @@ const CHAT_MESSAGES = {
|
|
|
54
59
|
inputs: {},
|
|
55
60
|
allow: "Allow",
|
|
56
61
|
decline: "Decline",
|
|
62
|
+
approvals: (count) => `${count} actions need your approval`,
|
|
63
|
+
approveAll: "Approve all",
|
|
64
|
+
approveSome: (count) => `Approve ${count}`,
|
|
65
|
+
declineAll: "Decline all",
|
|
57
66
|
confirming: "Waiting for the business to confirm",
|
|
58
67
|
handedOff: "Handed to a person",
|
|
68
|
+
opened: (name, title) => `${name} opened ${title}`,
|
|
69
|
+
closePlaced: "Close",
|
|
59
70
|
handover: (holder, by, name) => {
|
|
60
71
|
const automation = by.kind === "automation" ? ` by “${by.title}”` : "";
|
|
61
72
|
switch (holder) {
|
|
@@ -86,6 +97,70 @@ const CHAT_MESSAGES = {
|
|
|
86
97
|
kit: KIT_MESSAGES,
|
|
87
98
|
browser: BROWSER_MESSAGES
|
|
88
99
|
};
|
|
100
|
+
/** Where nothing is put apart: a conversation on a channel. */
|
|
101
|
+
const NO_PLACES = {};
|
|
102
|
+
/**
|
|
103
|
+
* What the person can still do on the conversation's surfaces: what they did on each, by its id
|
|
104
|
+
* (each draws its answer), and whether one takes input. A surface takes input while the turn
|
|
105
|
+
* waits on it or, once nothing runs or waits, when the agent showed it since the person last
|
|
106
|
+
* spoke; older ones are read, not used. On a channel the person answered there: here it reads.
|
|
107
|
+
*/
|
|
108
|
+
function surfacesOf(live, onChannel) {
|
|
109
|
+
const { messages, pending, awaiting } = live;
|
|
110
|
+
const idle = !onChannel && !pending && awaiting === null;
|
|
111
|
+
const lastSaid = messages.findLastIndex((message) => message.role === "user");
|
|
112
|
+
const answers = /* @__PURE__ */ new Map();
|
|
113
|
+
for (const message of messages) if (message.action) answers.set(message.action.surface, message.action);
|
|
114
|
+
const current = (index) => idle && index > lastSaid;
|
|
115
|
+
return {
|
|
116
|
+
answers,
|
|
117
|
+
current,
|
|
118
|
+
takes: (id, index) => !onChannel && id === awaiting || current(index)
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/** A step that drew a surface, by its id: the surface, and the index of its reply. */
|
|
122
|
+
function placedStep(messages, id) {
|
|
123
|
+
for (const [index, message] of messages.entries()) {
|
|
124
|
+
const step = message.steps.find((each) => each.id === id);
|
|
125
|
+
if (step?.surface) return {
|
|
126
|
+
step,
|
|
127
|
+
surface: step.surface,
|
|
128
|
+
index
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The surface open in a panel or a modal, by its step's id: each one the agent puts apart opens
|
|
135
|
+
* as it comes, and a conversation that opens waiting on one opens it; one read with the history
|
|
136
|
+
* waits for its line. Closing it, or opening another, is the person's.
|
|
137
|
+
*/
|
|
138
|
+
function useOpenedPlace(live, places) {
|
|
139
|
+
const [opened, setOpened] = useState(null);
|
|
140
|
+
const ids = live.messages.flatMap((message) => message.steps).filter((step) => step.surface && placeOf(step.surface, places) !== "chat").map((step) => step.id);
|
|
141
|
+
const seen = useRef(null);
|
|
142
|
+
const key = ids.join(" ");
|
|
143
|
+
const { loading, awaiting } = live;
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
if (loading) return;
|
|
146
|
+
const known = seen.current;
|
|
147
|
+
const placed = key === "" ? [] : key.split(" ");
|
|
148
|
+
if (known === null) {
|
|
149
|
+
seen.current = new Set(placed);
|
|
150
|
+
if (awaiting !== null && placed.includes(awaiting)) setOpened(awaiting);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const fresh = placed.filter((id) => !known.has(id));
|
|
154
|
+
for (const id of fresh) known.add(id);
|
|
155
|
+
const newest = fresh.at(-1);
|
|
156
|
+
if (newest !== void 0) setOpened(newest);
|
|
157
|
+
}, [
|
|
158
|
+
key,
|
|
159
|
+
loading,
|
|
160
|
+
awaiting
|
|
161
|
+
]);
|
|
162
|
+
return [opened, setOpened];
|
|
163
|
+
}
|
|
89
164
|
/** How close to the foot, in pixels, still counts as being at it (v2's). */
|
|
90
165
|
const NEAR_FOOT = 80;
|
|
91
166
|
/**
|
|
@@ -112,7 +187,7 @@ function AgentChat(props) {
|
|
|
112
187
|
}, props.conversationId)
|
|
113
188
|
});
|
|
114
189
|
}
|
|
115
|
-
function Chat({ client, conversationId, name, messages, classNames, components, locale, onError, actions, suggestions, compose, look = "agent", steps = client.who === "person" ? "hidden" : "shown", attachments = true, onOpenFile, dictate, write, handovers = false, onHolder, onStep, handover, onTool, taken }) {
|
|
190
|
+
function Chat({ client, conversationId, name, messages, classNames, components, blocks, surfaces, locale, onError, actions, suggestions, compose, look = "agent", steps = client.who === "person" ? "hidden" : "shown", attachments = true, onOpenFile, dictate, write, handovers = false, onHolder, onStep, handover, onTool, taken }) {
|
|
116
191
|
const words = {
|
|
117
192
|
...CHAT_MESSAGES,
|
|
118
193
|
...messages
|
|
@@ -125,6 +200,7 @@ function Chat({ client, conversationId, name, messages, classNames, components,
|
|
|
125
200
|
onTool: onTool ?? (() => Promise.reject(new Error(words.noTool)))
|
|
126
201
|
});
|
|
127
202
|
const files = useConversationFiles(client, conversationId, live.messages, onOpenFile);
|
|
203
|
+
useReadAsShown(client, conversationId, live.messages.at(-1)?.id);
|
|
128
204
|
const uploads = useUploads(attachments ? files : void 0, { max: 10 });
|
|
129
205
|
const attaching = attachments && (write !== void 0 || live.awaiting === null);
|
|
130
206
|
const [over, setOver] = useState(false);
|
|
@@ -134,6 +210,9 @@ function Chat({ client, conversationId, name, messages, classNames, components,
|
|
|
134
210
|
const builder = look !== "agent";
|
|
135
211
|
const { pending } = live;
|
|
136
212
|
const onChannel = live.messages.some((message) => message.author != null);
|
|
213
|
+
const places = onChannel ? NO_PLACES : surfaces;
|
|
214
|
+
const [opened, setOpened] = useOpenedPlace(live, places);
|
|
215
|
+
const open = opened === null ? null : placedStep(live.messages, opened);
|
|
137
216
|
const stop = useRef(live.stop);
|
|
138
217
|
useEffect(() => {
|
|
139
218
|
stop.current = live.stop;
|
|
@@ -150,6 +229,7 @@ function Chat({ client, conversationId, name, messages, classNames, components,
|
|
|
150
229
|
}, [builder, pending]);
|
|
151
230
|
const looks = builder ? ["sc-chat--builder"] : [];
|
|
152
231
|
if (look === "console") looks.push("sc-chat--console");
|
|
232
|
+
if (open !== null && placeOf(open.surface, places) === "panel") looks.push("sc-chat--placed");
|
|
153
233
|
const stepsShown = steps === "shown";
|
|
154
234
|
return /* @__PURE__ */ jsxs("div", {
|
|
155
235
|
part: "chat",
|
|
@@ -203,6 +283,10 @@ function Chat({ client, conversationId, name, messages, classNames, components,
|
|
|
203
283
|
words,
|
|
204
284
|
classNames,
|
|
205
285
|
components,
|
|
286
|
+
blocks,
|
|
287
|
+
places,
|
|
288
|
+
opened,
|
|
289
|
+
onOpen: setOpened,
|
|
206
290
|
locale,
|
|
207
291
|
following,
|
|
208
292
|
took,
|
|
@@ -213,6 +297,33 @@ function Chat({ client, conversationId, name, messages, classNames, components,
|
|
|
213
297
|
who: client.who,
|
|
214
298
|
steps: stepsShown
|
|
215
299
|
}),
|
|
300
|
+
open !== null && /* @__PURE__ */ jsx(Placed, {
|
|
301
|
+
where: placeOf(open.surface, places) === "modal" ? "modal" : "panel",
|
|
302
|
+
title: open.surface.title ?? "",
|
|
303
|
+
closeLabel: words.closePlaced,
|
|
304
|
+
onClose: () => {
|
|
305
|
+
setOpened(null);
|
|
306
|
+
},
|
|
307
|
+
classNames,
|
|
308
|
+
children: /* @__PURE__ */ jsx(Surface, {
|
|
309
|
+
surface: open.surface,
|
|
310
|
+
active: surfacesOf(live, onChannel).takes(open.step.id, open.index),
|
|
311
|
+
answered: surfacesOf(live, onChannel).answers.get(open.step.id),
|
|
312
|
+
components,
|
|
313
|
+
blocks,
|
|
314
|
+
classNames,
|
|
315
|
+
messages: words.kit,
|
|
316
|
+
locale,
|
|
317
|
+
files,
|
|
318
|
+
onAction: (action) => {
|
|
319
|
+
live.act({
|
|
320
|
+
surface: open.step.id,
|
|
321
|
+
...action
|
|
322
|
+
});
|
|
323
|
+
if (placeOf(open.surface, places) === "modal") setOpened(null);
|
|
324
|
+
}
|
|
325
|
+
})
|
|
326
|
+
}, open.step.id),
|
|
216
327
|
/* @__PURE__ */ jsx(BrowserView, {
|
|
217
328
|
live,
|
|
218
329
|
name,
|
|
@@ -348,7 +459,7 @@ function whenSaid(at, now, code) {
|
|
|
348
459
|
* one quiet line across the log says so, and under a person's message the agent let pass, that
|
|
349
460
|
* it did. Screen readers hear a reply once, when it is stored.
|
|
350
461
|
*/
|
|
351
|
-
function Log({ live, name, words, classNames, components, locale, following, took, builder, onChannel, files, handovers, who, steps }) {
|
|
462
|
+
function Log({ live, name, words, classNames, components, locale, following, took, builder, onChannel, files, handovers, who, steps, blocks, places, opened, onOpen }) {
|
|
352
463
|
const scroller = useRef(null);
|
|
353
464
|
const said = useRef(null);
|
|
354
465
|
const { messages, pending, draft, awaiting } = live;
|
|
@@ -388,10 +499,7 @@ function Log({ live, name, words, classNames, components, locale, following, too
|
|
|
388
499
|
observer.disconnect();
|
|
389
500
|
};
|
|
390
501
|
}, [following]);
|
|
391
|
-
const
|
|
392
|
-
const lastSaid = messages.findLastIndex((message) => message.role === "user");
|
|
393
|
-
const answers = /* @__PURE__ */ new Map();
|
|
394
|
-
for (const message of messages) if (message.action) answers.set(message.action.surface, message.action);
|
|
502
|
+
const { answers, current } = surfacesOf(live, onChannel);
|
|
395
503
|
const handed = handovers === void 0 ? handoffs(messages) : NO_HANDOFFS;
|
|
396
504
|
const placed = handoversAt(messages, handovers ?? []);
|
|
397
505
|
const lines = (index) => placed.get(index)?.map((handover) => /* @__PURE__ */ jsx(HandoverLine, {
|
|
@@ -441,14 +549,19 @@ function Log({ live, name, words, classNames, components, locale, following, too
|
|
|
441
549
|
message,
|
|
442
550
|
from: carried.current?.id === message.id ? carried.current.from : void 0,
|
|
443
551
|
awaiting: onChannel ? null : awaiting,
|
|
444
|
-
current:
|
|
552
|
+
current: current(index),
|
|
445
553
|
answers,
|
|
446
554
|
name,
|
|
447
555
|
words,
|
|
448
556
|
classNames,
|
|
449
557
|
components,
|
|
558
|
+
blocks,
|
|
559
|
+
places,
|
|
560
|
+
opened,
|
|
561
|
+
onOpen,
|
|
450
562
|
locale,
|
|
451
563
|
onAction: live.act,
|
|
564
|
+
onActAll: live.actAll,
|
|
452
565
|
builder,
|
|
453
566
|
files,
|
|
454
567
|
who,
|
|
@@ -670,8 +783,9 @@ function DeliveryMark({ message, words, classNames }) {
|
|
|
670
783
|
* for (on a person's page, one the business allows is a quiet line instead); on a channel, where
|
|
671
784
|
* it got to.
|
|
672
785
|
*/
|
|
673
|
-
function Reply({ message, from, awaiting, current, answers, name, words, classNames, components, locale, onAction, builder, files, who, steps, onChannel }) {
|
|
786
|
+
function Reply({ message, from, awaiting, current, answers, name, words, classNames, components, locale, onAction, builder, files, who, steps, onChannel, onActAll, blocks, places, opened, onOpen }) {
|
|
674
787
|
const text = useReveal(message.text, from ?? message.text.length);
|
|
788
|
+
const asked = message.steps.filter((step) => step.request !== null && !(step.request.decides === "business" && who === "person"));
|
|
675
789
|
const { shown: said, typing } = onChannel ? channelParts(message, text) : {
|
|
676
790
|
shown: [text],
|
|
677
791
|
typing: false
|
|
@@ -717,27 +831,52 @@ function Reply({ message, from, awaiting, current, answers, name, words, classNa
|
|
|
717
831
|
] })
|
|
718
832
|
}, step.id))
|
|
719
833
|
}),
|
|
720
|
-
message.steps.map((step) =>
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
834
|
+
message.steps.map((step) => {
|
|
835
|
+
if (!step.surface) return null;
|
|
836
|
+
const place = placeOf(step.surface, places);
|
|
837
|
+
if (place !== "chat") {
|
|
838
|
+
const Glyph = place === "panel" ? SidebarSimpleIcon : ArrowsOutSimpleIcon;
|
|
839
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
840
|
+
type: "button",
|
|
841
|
+
part: "placed",
|
|
842
|
+
"aria-expanded": opened === step.id,
|
|
843
|
+
onClick: () => {
|
|
844
|
+
onOpen(step.id);
|
|
845
|
+
},
|
|
846
|
+
className: classes(classNames, "sc-chat__placed", ...opened === step.id ? ["sc-chat__placed--open"] : []),
|
|
847
|
+
children: [/* @__PURE__ */ jsx(Glyph, { "aria-hidden": true }), /* @__PURE__ */ jsx("span", { children: words.opened(name, step.surface.title ?? "") })]
|
|
848
|
+
}, step.id);
|
|
849
|
+
}
|
|
850
|
+
return /* @__PURE__ */ jsx("div", {
|
|
851
|
+
part: "surface",
|
|
852
|
+
className: classes(classNames, "sc-chat__screen", ...framesItself(step.surface) ? ["sc-chat__screen--bare"] : []),
|
|
853
|
+
children: /* @__PURE__ */ jsx(Surface, {
|
|
854
|
+
surface: step.surface,
|
|
855
|
+
active: step.id === awaiting || current,
|
|
856
|
+
answered: answers.get(step.id),
|
|
857
|
+
components,
|
|
858
|
+
blocks,
|
|
859
|
+
classNames,
|
|
860
|
+
messages: words.kit,
|
|
861
|
+
locale,
|
|
862
|
+
files,
|
|
863
|
+
onAction: (action) => onAction({
|
|
864
|
+
surface: step.id,
|
|
865
|
+
...action
|
|
866
|
+
})
|
|
735
867
|
})
|
|
736
|
-
})
|
|
737
|
-
}
|
|
868
|
+
}, step.id);
|
|
869
|
+
}),
|
|
870
|
+
asked.length > 1 && /* @__PURE__ */ jsx(Approvals, {
|
|
871
|
+
steps: asked,
|
|
872
|
+
name,
|
|
873
|
+
words,
|
|
874
|
+
classNames,
|
|
875
|
+
onActAll
|
|
876
|
+
}, asked.map((step) => step.id).join()),
|
|
738
877
|
message.steps.map((step) => {
|
|
739
878
|
const { request } = step;
|
|
740
|
-
if (request === null) return null;
|
|
879
|
+
if (request === null || asked.length > 1 && asked.some((one) => one.id === step.id)) return null;
|
|
741
880
|
if (request.decides === "business" && who === "person") return /* @__PURE__ */ jsxs("p", {
|
|
742
881
|
part: "confirming",
|
|
743
882
|
className: classes(classNames, "sc-chat__confirming"),
|
|
@@ -938,6 +1077,141 @@ function Approval({ request, name, words, classNames, components, locale, onActi
|
|
|
938
1077
|
});
|
|
939
1078
|
}
|
|
940
1079
|
/**
|
|
1080
|
+
* Several actions a reply waits on the person's leave for, in one card: a box to tick for each
|
|
1081
|
+
* (all ticked to begin with), what each would take under it, editable while ticked, and one
|
|
1082
|
+
* button that sends a yes for each ticked (with what was changed) and a no for the rest, at once.
|
|
1083
|
+
*/
|
|
1084
|
+
function Approvals({ steps, name, words, classNames, onActAll }) {
|
|
1085
|
+
const [chosen, setChosen] = useState(() => new Set(steps.map((step) => step.id)));
|
|
1086
|
+
const [edits, setEdits] = useState({});
|
|
1087
|
+
const [sent, setSent] = useState(false);
|
|
1088
|
+
const inputOf = (step) => ({
|
|
1089
|
+
...step.request.input,
|
|
1090
|
+
...edits[step.id]
|
|
1091
|
+
});
|
|
1092
|
+
const send = () => {
|
|
1093
|
+
setSent(true);
|
|
1094
|
+
onActAll(steps.map((step) => chosen.has(step.id) ? {
|
|
1095
|
+
surface: step.id,
|
|
1096
|
+
name: "approve",
|
|
1097
|
+
data: step.id in edits ? { input: inputOf(step) } : {},
|
|
1098
|
+
label: words.allow
|
|
1099
|
+
} : {
|
|
1100
|
+
surface: step.id,
|
|
1101
|
+
name: "decline",
|
|
1102
|
+
data: {},
|
|
1103
|
+
label: words.decline
|
|
1104
|
+
}));
|
|
1105
|
+
};
|
|
1106
|
+
const label = chosen.size === steps.length ? words.approveAll : chosen.size === 0 ? words.declineAll : words.approveSome(chosen.size);
|
|
1107
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1108
|
+
part: "approval",
|
|
1109
|
+
className: classes(classNames, "sc-chat__request", "sc-chat__requests"),
|
|
1110
|
+
children: [
|
|
1111
|
+
/* @__PURE__ */ jsxs("header", { children: [/* @__PURE__ */ jsx(ShieldWarningIcon, { "aria-hidden": true }), words.approvals(steps.length)] }),
|
|
1112
|
+
/* @__PURE__ */ jsx("ul", { children: steps.map((step) => {
|
|
1113
|
+
const ticked = chosen.has(step.id);
|
|
1114
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
1115
|
+
className: classes(classNames, "sc-chat__ask"),
|
|
1116
|
+
children: [/* @__PURE__ */ jsx(Checkbox, {
|
|
1117
|
+
value: ticked,
|
|
1118
|
+
disabled: sent,
|
|
1119
|
+
onChange: (on) => {
|
|
1120
|
+
setChosen((last) => {
|
|
1121
|
+
const next = new Set(last);
|
|
1122
|
+
if (on) next.add(step.id);
|
|
1123
|
+
else next.delete(step.id);
|
|
1124
|
+
return next;
|
|
1125
|
+
});
|
|
1126
|
+
},
|
|
1127
|
+
children: words.request(name, step.request.action)
|
|
1128
|
+
}), ticked && /* @__PURE__ */ jsx("div", {
|
|
1129
|
+
className: classes(classNames, "sc-chat__ask-fields"),
|
|
1130
|
+
children: Object.entries(inputOf(step)).map(([field, value]) => /* @__PURE__ */ jsx(InputValue, {
|
|
1131
|
+
label: words.inputs[field] ?? field,
|
|
1132
|
+
value,
|
|
1133
|
+
disabled: sent,
|
|
1134
|
+
onChange: (changed) => {
|
|
1135
|
+
setEdits((last) => ({
|
|
1136
|
+
...last,
|
|
1137
|
+
[step.id]: {
|
|
1138
|
+
...last[step.id],
|
|
1139
|
+
[field]: changed
|
|
1140
|
+
}
|
|
1141
|
+
}));
|
|
1142
|
+
}
|
|
1143
|
+
}, field))
|
|
1144
|
+
})]
|
|
1145
|
+
}, step.id);
|
|
1146
|
+
}) }),
|
|
1147
|
+
/* @__PURE__ */ jsx("footer", { children: /* @__PURE__ */ jsx(Button, {
|
|
1148
|
+
tone: "primary",
|
|
1149
|
+
disabled: sent,
|
|
1150
|
+
onClick: send,
|
|
1151
|
+
children: label
|
|
1152
|
+
}) })
|
|
1153
|
+
]
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* One field of an action's input, editable as what it holds: words, a number, a yes or no;
|
|
1158
|
+
* anything else (a list, an object) shown as it is.
|
|
1159
|
+
*/
|
|
1160
|
+
function InputValue({ label, value, disabled, onChange }) {
|
|
1161
|
+
if (typeof value === "boolean") return /* @__PURE__ */ jsx(Checkbox, {
|
|
1162
|
+
value,
|
|
1163
|
+
disabled,
|
|
1164
|
+
onChange,
|
|
1165
|
+
children: label
|
|
1166
|
+
});
|
|
1167
|
+
return /* @__PURE__ */ jsx(Field, {
|
|
1168
|
+
label,
|
|
1169
|
+
children: (ids) => typeof value === "number" ? /* @__PURE__ */ jsx(NumberInput, {
|
|
1170
|
+
...ids,
|
|
1171
|
+
value,
|
|
1172
|
+
disabled,
|
|
1173
|
+
onChange
|
|
1174
|
+
}) : typeof value === "string" ? value.length > 80 || value.includes("\n") ? /* @__PURE__ */ jsx(TextArea, {
|
|
1175
|
+
...ids,
|
|
1176
|
+
value,
|
|
1177
|
+
disabled,
|
|
1178
|
+
onChange
|
|
1179
|
+
}) : /* @__PURE__ */ jsx(TextInput, {
|
|
1180
|
+
...ids,
|
|
1181
|
+
value,
|
|
1182
|
+
disabled,
|
|
1183
|
+
onChange
|
|
1184
|
+
}) : /* @__PURE__ */ jsx(TextInput, {
|
|
1185
|
+
...ids,
|
|
1186
|
+
value: JSON.stringify(value),
|
|
1187
|
+
disabled: true,
|
|
1188
|
+
onChange: () => void 0
|
|
1189
|
+
})
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* On a person's page, what came is read as the chat shows it: each newest message while the page
|
|
1194
|
+
* is in sight, and once it comes back into sight. Their threads' `unread` follows.
|
|
1195
|
+
*/
|
|
1196
|
+
function useReadAsShown(client, conversationId, newest) {
|
|
1197
|
+
useEffect(() => {
|
|
1198
|
+
if (client.who !== "person" || newest === void 0) return void 0;
|
|
1199
|
+
const read = () => {
|
|
1200
|
+
if (document.visibilityState === "hidden") return;
|
|
1201
|
+
client.conversations.read(conversationId).catch(() => void 0);
|
|
1202
|
+
};
|
|
1203
|
+
read();
|
|
1204
|
+
document.addEventListener("visibilitychange", read);
|
|
1205
|
+
return () => {
|
|
1206
|
+
document.removeEventListener("visibilitychange", read);
|
|
1207
|
+
};
|
|
1208
|
+
}, [
|
|
1209
|
+
client,
|
|
1210
|
+
conversationId,
|
|
1211
|
+
newest
|
|
1212
|
+
]);
|
|
1213
|
+
}
|
|
1214
|
+
/**
|
|
941
1215
|
* Where the person writes: a box that grows a few lines with the text. Enter sends, Shift+Enter
|
|
942
1216
|
* breaks the line; nothing goes while a reply is on its way or the socket is closed, but the
|
|
943
1217
|
* person can keep writing. While the turn waits for them to act above, the box is closed and
|
|
@@ -1122,6 +1396,6 @@ function Composer({ live, name, words, classNames, actions, compose, taken, fres
|
|
|
1122
1396
|
});
|
|
1123
1397
|
}
|
|
1124
1398
|
//#endregion
|
|
1125
|
-
export { AgentChat, CHAT_MESSAGES, decisionLine, handoffs, handoverBy, handoverLine, handoversAt, heardTail, shownState, whenSaid, workingOn };
|
|
1399
|
+
export { AgentChat, CHAT_MESSAGES, decisionLine, handoffs, handoverBy, handoverLine, handoversAt, heardTail, shownState, surfacesOf, whenSaid, workingOn };
|
|
1126
1400
|
|
|
1127
1401
|
//# sourceMappingURL=AgentChat.js.map
|