@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.
Files changed (99) hide show
  1. package/README.md +126 -21
  2. package/dist/AgentAbout.js +6 -2
  3. package/dist/AgentAbout.js.map +1 -1
  4. package/dist/AgentChat.d.ts +25 -3
  5. package/dist/AgentChat.js +302 -28
  6. package/dist/AgentChat.js.map +1 -1
  7. package/dist/AgentSpace.d.ts +25 -4
  8. package/dist/AgentSpace.js +55 -11
  9. package/dist/AgentSpace.js.map +1 -1
  10. package/dist/Threads.d.ts +73 -0
  11. package/dist/Threads.js +140 -0
  12. package/dist/Threads.js.map +1 -0
  13. package/dist/View.d.ts +48 -0
  14. package/dist/View.js +101 -0
  15. package/dist/View.js.map +1 -0
  16. package/dist/automation/marks.js +7 -3
  17. package/dist/automation/marks.js.map +1 -1
  18. package/dist/automation/messages.d.ts +4 -0
  19. package/dist/automation/messages.js +5 -1
  20. package/dist/automation/messages.js.map +1 -1
  21. package/dist/automation/when.js +8 -0
  22. package/dist/automation/when.js.map +1 -1
  23. package/dist/blocks.d.ts +83 -2
  24. package/dist/client.d.ts +23 -5
  25. package/dist/client.js +8 -2
  26. package/dist/client.js.map +1 -1
  27. package/dist/contract.d.ts +9 -4
  28. package/dist/es.d.ts +2 -0
  29. package/dist/es.js +34 -3
  30. package/dist/es.js.map +1 -1
  31. package/dist/frames.d.ts +7 -1
  32. package/dist/frames.js.map +1 -1
  33. package/dist/index.d.ts +14 -7
  34. package/dist/index.js +11 -4
  35. package/dist/kit/Agenda.d.ts +25 -0
  36. package/dist/kit/Agenda.js +119 -0
  37. package/dist/kit/Agenda.js.map +1 -0
  38. package/dist/kit/Combobox.js +1 -1
  39. package/dist/kit/DateField.js +1 -1
  40. package/dist/kit/Kanban.d.ts +17 -0
  41. package/dist/kit/Kanban.js +157 -0
  42. package/dist/kit/Kanban.js.map +1 -0
  43. package/dist/kit/TimeField.js +1 -1
  44. package/dist/kit/Timeline.d.ts +14 -0
  45. package/dist/kit/Timeline.js +71 -0
  46. package/dist/kit/Timeline.js.map +1 -0
  47. package/dist/kit/context.d.ts +1 -1
  48. package/dist/kit/context.js.map +1 -1
  49. package/dist/kit/messages.d.ts +22 -0
  50. package/dist/kit/messages.js +10 -0
  51. package/dist/kit/messages.js.map +1 -1
  52. package/dist/surface/Form.js +1 -1
  53. package/dist/surface/Placed.d.ts +20 -0
  54. package/dist/surface/Placed.js +70 -0
  55. package/dist/surface/Placed.js.map +1 -0
  56. package/dist/surface/Surface.d.ts +4 -2
  57. package/dist/surface/Surface.js +78 -2
  58. package/dist/surface/Surface.js.map +1 -1
  59. package/dist/surface/places.d.ts +16 -0
  60. package/dist/surface/places.js +15 -0
  61. package/dist/surface/places.js.map +1 -0
  62. package/dist/surface/scope.d.ts +17 -1
  63. package/dist/surface/scope.js +1 -0
  64. package/dist/surface/scope.js.map +1 -1
  65. package/dist/types.d.ts +23 -0
  66. package/dist/types.js.map +1 -1
  67. package/dist/useConversation.d.ts +2 -0
  68. package/dist/useConversation.js +16 -0
  69. package/dist/useConversation.js.map +1 -1
  70. package/dist/widget.js +17 -16
  71. package/package.json +1 -1
  72. package/src/AgentAbout.tsx +4 -0
  73. package/src/AgentChat.tsx +417 -34
  74. package/src/AgentSpace.tsx +106 -14
  75. package/src/Threads.tsx +218 -0
  76. package/src/View.tsx +117 -0
  77. package/src/automation/marks.tsx +7 -0
  78. package/src/automation/messages.ts +8 -0
  79. package/src/automation/when.ts +4 -0
  80. package/src/blocks.ts +100 -2
  81. package/src/client.ts +42 -10
  82. package/src/contract.ts +404 -29
  83. package/src/es.ts +30 -0
  84. package/src/frames.ts +3 -1
  85. package/src/index.ts +36 -3
  86. package/src/kit/Agenda.tsx +161 -0
  87. package/src/kit/Kanban.tsx +182 -0
  88. package/src/kit/Timeline.tsx +58 -0
  89. package/src/kit/context.tsx +45 -1
  90. package/src/kit/messages.ts +31 -0
  91. package/src/styles.css +825 -66
  92. package/src/surface/Placed.tsx +98 -0
  93. package/src/surface/Surface.tsx +92 -1
  94. package/src/surface/places.ts +19 -0
  95. package/src/surface/scope.ts +24 -1
  96. package/src/types.ts +24 -0
  97. package/src/useConversation.ts +10 -0
  98. package/src/widget/element.tsx +54 -7
  99. 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 idle = !onChannel && !pending && awaiting === null;
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: idle && index > lastSaid,
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) => step.surface && /* @__PURE__ */ jsx("div", {
721
- part: "surface",
722
- className: classes(classNames, "sc-chat__screen", ...framesItself(step.surface) ? ["sc-chat__screen--bare"] : []),
723
- children: /* @__PURE__ */ jsx(Surface, {
724
- surface: step.surface,
725
- active: step.id === awaiting || current,
726
- answered: answers.get(step.id),
727
- components,
728
- classNames,
729
- messages: words.kit,
730
- locale,
731
- files,
732
- onAction: (action) => onAction({
733
- surface: step.id,
734
- ...action
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
- }, step.id)),
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