@signalcore/ui 1.0.0-beta.3 → 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 +79 -0
- package/dist/AgentChat.d.ts +19 -3
- package/dist/AgentChat.js +149 -27
- package/dist/AgentChat.js.map +1 -1
- package/dist/AgentSpace.d.ts +14 -2
- package/dist/AgentSpace.js +3 -1
- package/dist/AgentSpace.js.map +1 -1
- package/dist/View.d.ts +48 -0
- package/dist/View.js +101 -0
- package/dist/View.js.map +1 -0
- package/dist/blocks.d.ts +83 -2
- package/dist/client.d.ts +9 -1
- package/dist/client.js +1 -0
- package/dist/client.js.map +1 -1
- package/dist/es.js +12 -0
- package/dist/es.js.map +1 -1
- package/dist/index.d.ts +11 -5
- package/dist/index.js +7 -1
- 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/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 +12 -0
- package/dist/types.js.map +1 -1
- package/dist/widget.js +17 -17
- package/package.json +1 -1
- package/src/AgentChat.tsx +215 -34
- package/src/AgentSpace.tsx +17 -1
- package/src/View.tsx +117 -0
- package/src/blocks.ts +100 -2
- package/src/client.ts +16 -0
- package/src/contract.ts +166 -5
- package/src/es.ts +9 -0
- package/src/index.ts +27 -2
- 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 +568 -0
- 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 +13 -0
- package/src/widget/element.tsx +41 -3
package/README.md
CHANGED
|
@@ -185,6 +185,39 @@ couldn't begin again), `close`, `agent` (its name when the face can't be read),
|
|
|
185
185
|
(`--text`, `--voice`; `aria-checked` on the one on), `sc-space__caption` (`--person`, `--agent`),
|
|
186
186
|
`sc-space__status`.
|
|
187
187
|
|
|
188
|
+
### Panels, modals and your own blocks
|
|
189
|
+
|
|
190
|
+
What the agent shows can stand apart from the conversation (`show` and `ask_person` with
|
|
191
|
+
`where`, v3/UI.md, Surfaces): in a **panel** beside the chat (over it, where the space is narrow:
|
|
192
|
+
the rep's week while they talk), or in a **modal** over the page (something to answer now; once
|
|
193
|
+
used it closes and the chat goes on). Each opens as it comes, and a conversation that opens
|
|
194
|
+
waiting on one opens it; the reply keeps a quiet line, "Ray abrió tu semana" (`opened`), that
|
|
195
|
+
opens it again. Your page decides what it allows: `surfaces={{ panel: true, modal: false }}`
|
|
196
|
+
(both by default); a place left out shows in the conversation, as on a channel. `AgentChat`
|
|
197
|
+
takes the same `surfaces`.
|
|
198
|
+
|
|
199
|
+
`blocks` registers your own blocks by the name each was declared with (`PUT /v1/blocks/{name}`):
|
|
200
|
+
`blocks={{ "demoprep.deal-card": DealCard }}`. The agent gives only props, which the API checked
|
|
201
|
+
against your schema; your component gets `{props, text, active, onAction}` (`HostBlockProps`),
|
|
202
|
+
and a block it has no component for shows its words in a quiet card (`sc-surface__host`).
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
function DealCard({ props }: HostBlockProps<{ title: string; amount: number }>) {
|
|
206
|
+
return <YourDealCard title={props.title} amount={props.amount} />;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
<AgentSpace
|
|
210
|
+
client={client}
|
|
211
|
+
agentId="agt_…"
|
|
212
|
+
surfaces={{ panel: true, modal: true }}
|
|
213
|
+
blocks={{ "demoprep.deal-card": DealCard }}
|
|
214
|
+
/>;
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Words: `opened(name, title)`, `closePlaced`. Parts: `sc-chat__placed` (`--open`), `sc-chat--placed`
|
|
218
|
+
(a panel is open), `sc-placed` (`--panel`, `--modal`, the latter a `<dialog>`),
|
|
219
|
+
`sc-placed__head`, `sc-placed__title`, `sc-placed__body`.
|
|
220
|
+
|
|
188
221
|
### Threads, unread, and several yeses at once
|
|
189
222
|
|
|
190
223
|
A person keeps several conversations (threads) with the agent, each with its title: one your
|
|
@@ -257,6 +290,10 @@ flow at the element's size (560px tall unless your CSS says). A visitor's conver
|
|
|
257
290
|
| `stylesheet` | your own CSS for the widget: the CSS itself (anything with a `{`) or its address; adopted inside the shadow root, after ours. Also the element's `stylesheet` property |
|
|
258
291
|
| `unstyled` | our look's defaults drop away (grounds, lines, shadows, corners, every ink but the page's text), the layout stays: for a host that styles everything |
|
|
259
292
|
| `shadow` | `none`: no shadow root; the widget draws in the page (our stylesheet goes first in `<head>`, so your CSS wins), for a host whose global CSS should apply directly. Read once, as the element connects |
|
|
293
|
+
| `surfaces` | where the agent may put what it shows besides the conversation: `panel`, `modal`, both (`"panel modal"`, the default) or none (`""`) |
|
|
294
|
+
|
|
295
|
+
Your own blocks go as the element's `blocks` property, React components the widget's React
|
|
296
|
+
draws: `document.querySelector("signalcore-chat").blocks = { "demoprep.deal-card": DealCard }`.
|
|
260
297
|
|
|
261
298
|
By default the widget takes your page's type and ink: the font, the base size and the text colour
|
|
262
299
|
the element inherits come through the shadow root and stand in for our defaults (read as it draws,
|
|
@@ -731,6 +768,48 @@ works, a check and "Conectada" in its place (`sc-surface__connect--done`), and a
|
|
|
731
768
|
it failed. It changes as the connection does, from the `ui` frames the API sends again on the
|
|
732
769
|
same id; before the API has asked for it (`connection` null), only the app as the agent named it.
|
|
733
770
|
|
|
771
|
+
The kit's blocks of time and work: a `calendar` (`Agenda`: a day's events, or a week's from its
|
|
772
|
+
`start`, at their hours; one with a `url` opens its page, with the calendar's `action` the rest
|
|
773
|
+
send `{event}`; narrow, the days go one under another, an empty one a quiet line; 680px and up,
|
|
774
|
+
the week stands as seven columns), a `timeline` (`Timeline`: dated things the newest first, each
|
|
775
|
+
a dot on a hairline, in its `tone` when it has one, its `status` a quiet word), a `board`
|
|
776
|
+
(`Kanban`: cards in columns; with its `action`, a card dragged to another column, or moved from
|
|
777
|
+
its button's list of the others, goes there at once and sends `{card, column}`, labelled
|
|
778
|
+
`board.moved`). A `host` block is one of yours, drawn by the component `blocks` registers under
|
|
779
|
+
its `name` (a component that fails as it draws leaves its words, and the rest draws). Words:
|
|
780
|
+
`calendar` (`allDay`, `empty`), `board` (`move`, `moveTo`, `moved`, `empty`). Parts:
|
|
781
|
+
`sc-agenda` (`--day`), `sc-agenda__days`, `sc-agenda__day` (`--today`, `--empty`),
|
|
782
|
+
`sc-agenda__date`, `sc-agenda__weekday`, `sc-agenda__number`, `sc-agenda__events`,
|
|
783
|
+
`sc-agenda__event`, `sc-agenda__time`, `sc-agenda__what`, `sc-agenda__title`,
|
|
784
|
+
`sc-agenda__detail`, `sc-agenda__empty`; `sc-timeline`, `sc-timeline__item` (`--info`,
|
|
785
|
+
`--success`, `--warning`, `--danger`), `sc-timeline__dot`, `sc-timeline__body`,
|
|
786
|
+
`sc-timeline__head`, `sc-timeline__title`, `sc-timeline__status`, `sc-timeline__when`,
|
|
787
|
+
`sc-timeline__text`; `sc-board`, `sc-board__column` (`--over`), `sc-board__head`,
|
|
788
|
+
`sc-board__title`, `sc-board__count`, `sc-board__cards`, `sc-board__card` (`--dragging`),
|
|
789
|
+
`sc-board__card-title`, `sc-board__card-subtitle`, `sc-board__card-meta`, `sc-board__move`,
|
|
790
|
+
`sc-board__menu`, `sc-board__empty`.
|
|
791
|
+
|
|
792
|
+
## View
|
|
793
|
+
|
|
794
|
+
`<View client name contact? blocks? components? classNames? messages? locale? onAction? />` draws
|
|
795
|
+
an agent's view where your page puts it: what the agent keeps on show for each person (their
|
|
796
|
+
next meeting, their week), a dashboard's spec rendered for one contact with their own rows
|
|
797
|
+
(`GET /views/{name}/render`). With a person's token it is theirs; for the business, `contact`
|
|
798
|
+
names whom (their id). It is read when shown and again every `VIEW_POLL_MS` (30 s), so it shows
|
|
799
|
+
what the rows hold now; while it is first read an empty box holds its place, and a view that
|
|
800
|
+
isn't there (or isn't the person's) draws nothing. `useView(client, name, contact?)` gives
|
|
801
|
+
`{view, problem, gone}` for your own layout (`view.title`, `view.blocks`, `view.text`).
|
|
802
|
+
|
|
803
|
+
```tsx
|
|
804
|
+
<View
|
|
805
|
+
client={client}
|
|
806
|
+
name="proxima_reunion"
|
|
807
|
+
blocks={{ "demoprep.deal-card": DealCard }}
|
|
808
|
+
messages={KIT_MESSAGES_ES}
|
|
809
|
+
locale={es}
|
|
810
|
+
/>
|
|
811
|
+
```
|
|
812
|
+
|
|
734
813
|
## AgentAbout
|
|
735
814
|
|
|
736
815
|
`<AgentAbout client agentId name messages? classNames? controls? apps? locale? view? onViewChange? onOpenFile? onOpenConversation? />`: Qué puede, made to sit
|
package/dist/AgentChat.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ import { KitPart } from "./kit/context.js";
|
|
|
5
5
|
import { ToolHandler } from "./useConversation.js";
|
|
6
6
|
import { BrowserMessages, BrowserPart } from "./BrowserView.js";
|
|
7
7
|
import { Dictate } from "./useDictation.js";
|
|
8
|
-
import {
|
|
8
|
+
import { PlacedPart } from "./surface/Placed.js";
|
|
9
|
+
import { SurfacePlaces } from "./surface/places.js";
|
|
10
|
+
import { HostBlocks, SurfaceComponents, SurfacePart } from "./surface/scope.js";
|
|
9
11
|
import { ReactNode } from "react";
|
|
10
12
|
//#region src/AgentChat.d.ts
|
|
11
13
|
/** The words the chat says; a host gives its own, in its language. */
|
|
@@ -74,6 +76,13 @@ export interface ChatMessages {
|
|
|
74
76
|
confirming: string;
|
|
75
77
|
/** The line across the log where the agent handed the conversation to a person. */
|
|
76
78
|
handedOff: string;
|
|
79
|
+
/**
|
|
80
|
+
* The line where the agent put what it showed in a panel or a modal, which opens it again:
|
|
81
|
+
* the agent's name and the surface's title ("Ray opened your week").
|
|
82
|
+
*/
|
|
83
|
+
opened: (name: string, title: string) => string;
|
|
84
|
+
/** The button that closes a panel or a modal. */
|
|
85
|
+
closePlaced: string;
|
|
77
86
|
/**
|
|
78
87
|
* The line across the log where who answers the conversation changed (`handovers`), before
|
|
79
88
|
* why: to whom (`holder`), and who changed it (`by`).
|
|
@@ -108,8 +117,8 @@ export interface ChatMessages {
|
|
|
108
117
|
}
|
|
109
118
|
export declare const CHAT_MESSAGES: ChatMessages;
|
|
110
119
|
/** The chat's parts, by the class each carries; `classNames` adds a host's own under the same keys. */
|
|
111
|
-
export type ChatPart = "sc-chat" | "sc-chat--builder" | "sc-chat--console" | "sc-chat__log" | "sc-chat__log--channel" | "sc-chat__message" | "sc-chat__message--person" | "sc-chat__message--agent" | "sc-chat__message--action" | "sc-chat__message--business" | "sc-chat__said" | "sc-chat__business" | "sc-chat__meta" | "sc-chat__delivery" | "sc-chat__text" | "sc-chat__steps" | "sc-chat__step" | "sc-chat__mark" | "sc-chat__step--running" | "sc-chat__step--queued" | "sc-chat__step--waiting" | "sc-chat__step--done" | "sc-chat__step--failed" | "sc-chat__output" | "sc-chat__request" | "sc-chat__requests" | "sc-chat__ask" | "sc-chat__ask-fields" | "sc-chat__confirming" | "sc-chat__screen" | "sc-chat__screen--bare" | "sc-chat__handoff" | "sc-chat__handover" | "sc-chat__decision" | "sc-chat__message--heard" | "sc-chat__message--voice" | "sc-chat__transcript" | "sc-chat__cut" | "sc-chat__reply" | "sc-chat__status" | "sc-chat__done" | "sc-chat__empty" | "sc-chat__suggestion" | "sc-chat__composer" | "sc-chat__files" | "sc-chat__attach" | "sc-chat__dictate" | "sc-chat__typing" | "sc-chat__dictation" | "sc-chat__drop" | "sc-chat__input" | "sc-chat__actions" | "sc-chat__send" | "sc-chat__stop";
|
|
112
|
-
type ClassNames = Partial<Record<ChatPart | SurfacePart | KitPart | BrowserPart, string>> | undefined;
|
|
120
|
+
export type ChatPart = "sc-chat" | "sc-chat--builder" | "sc-chat--console" | "sc-chat--placed" | "sc-chat__log" | "sc-chat__log--channel" | "sc-chat__message" | "sc-chat__message--person" | "sc-chat__message--agent" | "sc-chat__message--action" | "sc-chat__message--business" | "sc-chat__said" | "sc-chat__business" | "sc-chat__meta" | "sc-chat__delivery" | "sc-chat__text" | "sc-chat__steps" | "sc-chat__step" | "sc-chat__mark" | "sc-chat__step--running" | "sc-chat__step--queued" | "sc-chat__step--waiting" | "sc-chat__step--done" | "sc-chat__step--failed" | "sc-chat__output" | "sc-chat__request" | "sc-chat__requests" | "sc-chat__ask" | "sc-chat__ask-fields" | "sc-chat__confirming" | "sc-chat__screen" | "sc-chat__screen--bare" | "sc-chat__handoff" | "sc-chat__placed" | "sc-chat__placed--open" | "sc-chat__handover" | "sc-chat__decision" | "sc-chat__message--heard" | "sc-chat__message--voice" | "sc-chat__transcript" | "sc-chat__cut" | "sc-chat__reply" | "sc-chat__status" | "sc-chat__done" | "sc-chat__empty" | "sc-chat__suggestion" | "sc-chat__composer" | "sc-chat__files" | "sc-chat__attach" | "sc-chat__dictate" | "sc-chat__typing" | "sc-chat__dictation" | "sc-chat__drop" | "sc-chat__input" | "sc-chat__actions" | "sc-chat__send" | "sc-chat__stop";
|
|
121
|
+
type ClassNames = Partial<Record<ChatPart | SurfacePart | KitPart | BrowserPart | PlacedPart, string>> | undefined;
|
|
113
122
|
/**
|
|
114
123
|
* How the chat looks. `agent` is an agent's conversation. `builder` is the builder's, v2's: the
|
|
115
124
|
* working line says what it does under the brand's sweeping light, the steps open on what they
|
|
@@ -152,6 +161,13 @@ export interface AgentChatProps {
|
|
|
152
161
|
classNames?: ClassNames;
|
|
153
162
|
/** The host's own drawing of a block type, for every surface the agent shows. */
|
|
154
163
|
components?: SurfaceComponents | undefined;
|
|
164
|
+
/** The host's own blocks, by the name each was declared with (`PUT /v1/blocks/{name}`). */
|
|
165
|
+
blocks?: HostBlocks | undefined;
|
|
166
|
+
/**
|
|
167
|
+
* Where the agent may put what it shows besides the conversation: a panel beside it, a modal
|
|
168
|
+
* over the page (both by default). One the host doesn't allow shows in the conversation.
|
|
169
|
+
*/
|
|
170
|
+
surfaces?: SurfacePlaces | undefined;
|
|
155
171
|
/** The language of the surfaces' calendars, days, times and numbers; English by default. */
|
|
156
172
|
locale?: KitLocale | undefined;
|
|
157
173
|
/** An error's code, from the socket or `load` for a history that could not be read; the host words it. */
|
package/dist/AgentChat.js
CHANGED
|
@@ -16,10 +16,12 @@ import { Checkbox, NumberInput, TextArea, TextInput } from "./kit/Inputs.js";
|
|
|
16
16
|
import { FileChips, UploadChips, carriesFiles, useUploads } from "./kit/FileField.js";
|
|
17
17
|
import { stepGlyph } from "./stepGlyphs.js";
|
|
18
18
|
import { Markdown } from "./surface/Markdown.js";
|
|
19
|
+
import { Placed } from "./surface/Placed.js";
|
|
20
|
+
import { placeOf } from "./surface/places.js";
|
|
19
21
|
import { Surface, framesItself, usePlayed } from "./surface/Surface.js";
|
|
20
22
|
import { NO_TOOL, useConversation } from "./useConversation.js";
|
|
21
23
|
import { Fragment, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
22
|
-
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";
|
|
23
25
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
24
26
|
//#region src/AgentChat.tsx
|
|
25
27
|
const CHAT_MESSAGES = {
|
|
@@ -63,6 +65,8 @@ const CHAT_MESSAGES = {
|
|
|
63
65
|
declineAll: "Decline all",
|
|
64
66
|
confirming: "Waiting for the business to confirm",
|
|
65
67
|
handedOff: "Handed to a person",
|
|
68
|
+
opened: (name, title) => `${name} opened ${title}`,
|
|
69
|
+
closePlaced: "Close",
|
|
66
70
|
handover: (holder, by, name) => {
|
|
67
71
|
const automation = by.kind === "automation" ? ` by “${by.title}”` : "";
|
|
68
72
|
switch (holder) {
|
|
@@ -93,6 +97,70 @@ const CHAT_MESSAGES = {
|
|
|
93
97
|
kit: KIT_MESSAGES,
|
|
94
98
|
browser: BROWSER_MESSAGES
|
|
95
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
|
+
}
|
|
96
164
|
/** How close to the foot, in pixels, still counts as being at it (v2's). */
|
|
97
165
|
const NEAR_FOOT = 80;
|
|
98
166
|
/**
|
|
@@ -119,7 +187,7 @@ function AgentChat(props) {
|
|
|
119
187
|
}, props.conversationId)
|
|
120
188
|
});
|
|
121
189
|
}
|
|
122
|
-
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 }) {
|
|
123
191
|
const words = {
|
|
124
192
|
...CHAT_MESSAGES,
|
|
125
193
|
...messages
|
|
@@ -142,6 +210,9 @@ function Chat({ client, conversationId, name, messages, classNames, components,
|
|
|
142
210
|
const builder = look !== "agent";
|
|
143
211
|
const { pending } = live;
|
|
144
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);
|
|
145
216
|
const stop = useRef(live.stop);
|
|
146
217
|
useEffect(() => {
|
|
147
218
|
stop.current = live.stop;
|
|
@@ -158,6 +229,7 @@ function Chat({ client, conversationId, name, messages, classNames, components,
|
|
|
158
229
|
}, [builder, pending]);
|
|
159
230
|
const looks = builder ? ["sc-chat--builder"] : [];
|
|
160
231
|
if (look === "console") looks.push("sc-chat--console");
|
|
232
|
+
if (open !== null && placeOf(open.surface, places) === "panel") looks.push("sc-chat--placed");
|
|
161
233
|
const stepsShown = steps === "shown";
|
|
162
234
|
return /* @__PURE__ */ jsxs("div", {
|
|
163
235
|
part: "chat",
|
|
@@ -211,6 +283,10 @@ function Chat({ client, conversationId, name, messages, classNames, components,
|
|
|
211
283
|
words,
|
|
212
284
|
classNames,
|
|
213
285
|
components,
|
|
286
|
+
blocks,
|
|
287
|
+
places,
|
|
288
|
+
opened,
|
|
289
|
+
onOpen: setOpened,
|
|
214
290
|
locale,
|
|
215
291
|
following,
|
|
216
292
|
took,
|
|
@@ -221,6 +297,33 @@ function Chat({ client, conversationId, name, messages, classNames, components,
|
|
|
221
297
|
who: client.who,
|
|
222
298
|
steps: stepsShown
|
|
223
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),
|
|
224
327
|
/* @__PURE__ */ jsx(BrowserView, {
|
|
225
328
|
live,
|
|
226
329
|
name,
|
|
@@ -356,7 +459,7 @@ function whenSaid(at, now, code) {
|
|
|
356
459
|
* one quiet line across the log says so, and under a person's message the agent let pass, that
|
|
357
460
|
* it did. Screen readers hear a reply once, when it is stored.
|
|
358
461
|
*/
|
|
359
|
-
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 }) {
|
|
360
463
|
const scroller = useRef(null);
|
|
361
464
|
const said = useRef(null);
|
|
362
465
|
const { messages, pending, draft, awaiting } = live;
|
|
@@ -396,10 +499,7 @@ function Log({ live, name, words, classNames, components, locale, following, too
|
|
|
396
499
|
observer.disconnect();
|
|
397
500
|
};
|
|
398
501
|
}, [following]);
|
|
399
|
-
const
|
|
400
|
-
const lastSaid = messages.findLastIndex((message) => message.role === "user");
|
|
401
|
-
const answers = /* @__PURE__ */ new Map();
|
|
402
|
-
for (const message of messages) if (message.action) answers.set(message.action.surface, message.action);
|
|
502
|
+
const { answers, current } = surfacesOf(live, onChannel);
|
|
403
503
|
const handed = handovers === void 0 ? handoffs(messages) : NO_HANDOFFS;
|
|
404
504
|
const placed = handoversAt(messages, handovers ?? []);
|
|
405
505
|
const lines = (index) => placed.get(index)?.map((handover) => /* @__PURE__ */ jsx(HandoverLine, {
|
|
@@ -449,12 +549,16 @@ function Log({ live, name, words, classNames, components, locale, following, too
|
|
|
449
549
|
message,
|
|
450
550
|
from: carried.current?.id === message.id ? carried.current.from : void 0,
|
|
451
551
|
awaiting: onChannel ? null : awaiting,
|
|
452
|
-
current:
|
|
552
|
+
current: current(index),
|
|
453
553
|
answers,
|
|
454
554
|
name,
|
|
455
555
|
words,
|
|
456
556
|
classNames,
|
|
457
557
|
components,
|
|
558
|
+
blocks,
|
|
559
|
+
places,
|
|
560
|
+
opened,
|
|
561
|
+
onOpen,
|
|
458
562
|
locale,
|
|
459
563
|
onAction: live.act,
|
|
460
564
|
onActAll: live.actAll,
|
|
@@ -679,7 +783,7 @@ function DeliveryMark({ message, words, classNames }) {
|
|
|
679
783
|
* for (on a person's page, one the business allows is a quiet line instead); on a channel, where
|
|
680
784
|
* it got to.
|
|
681
785
|
*/
|
|
682
|
-
function Reply({ message, from, awaiting, current, answers, name, words, classNames, components, locale, onAction, builder, files, who, steps, onChannel, onActAll }) {
|
|
786
|
+
function Reply({ message, from, awaiting, current, answers, name, words, classNames, components, locale, onAction, builder, files, who, steps, onChannel, onActAll, blocks, places, opened, onOpen }) {
|
|
683
787
|
const text = useReveal(message.text, from ?? message.text.length);
|
|
684
788
|
const asked = message.steps.filter((step) => step.request !== null && !(step.request.decides === "business" && who === "person"));
|
|
685
789
|
const { shown: said, typing } = onChannel ? channelParts(message, text) : {
|
|
@@ -727,24 +831,42 @@ function Reply({ message, from, awaiting, current, answers, name, words, classNa
|
|
|
727
831
|
] })
|
|
728
832
|
}, step.id))
|
|
729
833
|
}),
|
|
730
|
-
message.steps.map((step) =>
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
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
|
+
})
|
|
745
867
|
})
|
|
746
|
-
})
|
|
747
|
-
}
|
|
868
|
+
}, step.id);
|
|
869
|
+
}),
|
|
748
870
|
asked.length > 1 && /* @__PURE__ */ jsx(Approvals, {
|
|
749
871
|
steps: asked,
|
|
750
872
|
name,
|
|
@@ -1274,6 +1396,6 @@ function Composer({ live, name, words, classNames, actions, compose, taken, fres
|
|
|
1274
1396
|
});
|
|
1275
1397
|
}
|
|
1276
1398
|
//#endregion
|
|
1277
|
-
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 };
|
|
1278
1400
|
|
|
1279
1401
|
//# sourceMappingURL=AgentChat.js.map
|