@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.
Files changed (71) hide show
  1. package/README.md +79 -0
  2. package/dist/AgentChat.d.ts +19 -3
  3. package/dist/AgentChat.js +149 -27
  4. package/dist/AgentChat.js.map +1 -1
  5. package/dist/AgentSpace.d.ts +14 -2
  6. package/dist/AgentSpace.js +3 -1
  7. package/dist/AgentSpace.js.map +1 -1
  8. package/dist/View.d.ts +48 -0
  9. package/dist/View.js +101 -0
  10. package/dist/View.js.map +1 -0
  11. package/dist/blocks.d.ts +83 -2
  12. package/dist/client.d.ts +9 -1
  13. package/dist/client.js +1 -0
  14. package/dist/client.js.map +1 -1
  15. package/dist/es.js +12 -0
  16. package/dist/es.js.map +1 -1
  17. package/dist/index.d.ts +11 -5
  18. package/dist/index.js +7 -1
  19. package/dist/kit/Agenda.d.ts +25 -0
  20. package/dist/kit/Agenda.js +119 -0
  21. package/dist/kit/Agenda.js.map +1 -0
  22. package/dist/kit/Combobox.js +1 -1
  23. package/dist/kit/DateField.js +1 -1
  24. package/dist/kit/Kanban.d.ts +17 -0
  25. package/dist/kit/Kanban.js +157 -0
  26. package/dist/kit/Kanban.js.map +1 -0
  27. package/dist/kit/TimeField.js +1 -1
  28. package/dist/kit/Timeline.d.ts +14 -0
  29. package/dist/kit/Timeline.js +71 -0
  30. package/dist/kit/Timeline.js.map +1 -0
  31. package/dist/kit/context.d.ts +1 -1
  32. package/dist/kit/context.js.map +1 -1
  33. package/dist/kit/messages.d.ts +22 -0
  34. package/dist/kit/messages.js +10 -0
  35. package/dist/kit/messages.js.map +1 -1
  36. package/dist/surface/Placed.d.ts +20 -0
  37. package/dist/surface/Placed.js +70 -0
  38. package/dist/surface/Placed.js.map +1 -0
  39. package/dist/surface/Surface.d.ts +4 -2
  40. package/dist/surface/Surface.js +78 -2
  41. package/dist/surface/Surface.js.map +1 -1
  42. package/dist/surface/places.d.ts +16 -0
  43. package/dist/surface/places.js +15 -0
  44. package/dist/surface/places.js.map +1 -0
  45. package/dist/surface/scope.d.ts +17 -1
  46. package/dist/surface/scope.js +1 -0
  47. package/dist/surface/scope.js.map +1 -1
  48. package/dist/types.d.ts +12 -0
  49. package/dist/types.js.map +1 -1
  50. package/dist/widget.js +17 -17
  51. package/package.json +1 -1
  52. package/src/AgentChat.tsx +215 -34
  53. package/src/AgentSpace.tsx +17 -1
  54. package/src/View.tsx +117 -0
  55. package/src/blocks.ts +100 -2
  56. package/src/client.ts +16 -0
  57. package/src/contract.ts +166 -5
  58. package/src/es.ts +9 -0
  59. package/src/index.ts +27 -2
  60. package/src/kit/Agenda.tsx +161 -0
  61. package/src/kit/Kanban.tsx +182 -0
  62. package/src/kit/Timeline.tsx +58 -0
  63. package/src/kit/context.tsx +45 -1
  64. package/src/kit/messages.ts +31 -0
  65. package/src/styles.css +568 -0
  66. package/src/surface/Placed.tsx +98 -0
  67. package/src/surface/Surface.tsx +92 -1
  68. package/src/surface/places.ts +19 -0
  69. package/src/surface/scope.ts +24 -1
  70. package/src/types.ts +13 -0
  71. package/src/widget/element.tsx +41 -3
@@ -1 +1 @@
1
- {"version":3,"file":"AgentSpace.js","names":[],"sources":["../src/AgentSpace.tsx"],"sourcesContent":["import { PhoneIcon, XIcon } from \"@phosphor-icons/react\";\nimport { lazy, Suspense, useEffect, useRef, useState } from \"react\";\n\nimport { AgentChat, type ChatMessages, type ChatPart, type ChatSteps } from \"./AgentChat\";\nimport { agentGlyph } from \"./agentIcons\";\nimport {\n AgentTasks,\n TASK_MESSAGES,\n useTasks,\n type TaskMessages,\n type TaskPart,\n} from \"./AgentTasks\";\nimport type { BrowserPart } from \"./BrowserView\";\nimport { classes } from \"./classes\";\nimport { ApiError, type Client } from \"./client\";\nimport { Button } from \"./kit/Button\";\nimport { KitProvider, type KitPart } from \"./kit/context\";\nimport type { KitLocale } from \"./kit/messages\";\nimport { Modes, type ModeMessages, type ModePart, type SpaceMode } from \"./Modes\";\nimport type { SurfaceComponents, SurfacePart } from \"./surface/scope\";\nimport { agentTint, type AgentTint } from \"./tint\";\nimport {\n THREAD_MESSAGES,\n Threads,\n useUnread,\n type ThreadMessages,\n type ThreadPart,\n} from \"./Threads\";\nimport { useAgentFace } from \"./useAgentFace\";\nimport { useConversation, type LiveConversation, type ToolHandler } from \"./useConversation\";\nimport type { VoiceCallMessages, VoiceCallPart } from \"./VoiceCall\";\nimport type { OrbStyle } from \"./voice/orbs\";\n\n// The call, and LiveKit under it, load the first time someone calls.\nconst VoiceCall = lazy(() =>\n import(\"./VoiceCall\").then((module) => ({ default: module.VoiceCall })),\n);\n\n/** The words the space says around the chat and the call; a host gives its own, in its language. */\nexport interface SpaceMessages {\n /** The two modes beside the composer's paperclip: the group's name, and each mode's. */\n modes: ModeMessages;\n /** After a call that couldn't begin: try it again. */\n again: string;\n /** The button that closes the space, where the host gives `onClose`. */\n close: string;\n /** The agent's name when its face can't be read. */\n agent: string;\n /** While the conversation is found, or started. */\n opening: string;\n /** The conversation could not be opened; and the button that tries again. */\n failed: string;\n retry: string;\n /** What the chat says (`AgentChat`'s words). */\n chat: Partial<ChatMessages>;\n /** What a call says (`VoiceCall`'s words). */\n voice: Partial<VoiceCallMessages>;\n /** What the person's tasks say (`AgentTasks`'s words). */\n tasks: TaskMessages;\n /** What the person's threads say (`Threads`'s words). */\n threads: ThreadMessages;\n}\n\nexport const SPACE_MESSAGES: SpaceMessages = {\n modes: { label: \"Mode\", text: \"Text\", voice: \"Voice\" },\n again: \"Call again\",\n close: \"Close\",\n agent: \"Agent\",\n opening: \"Opening the conversation…\",\n failed: \"The conversation couldn't be opened.\",\n retry: \"Try again\",\n chat: {},\n voice: {},\n tasks: TASK_MESSAGES,\n threads: THREAD_MESSAGES,\n};\n\n/** The space's parts, by the class each carries; `classNames` adds a host's own under the same keys. */\nexport type SpacePart =\n | \"sc-space\"\n | \"sc-space--compact\"\n | \"sc-space--full\"\n | \"sc-space--voice\"\n | \"sc-space__face\"\n | \"sc-space__mark\"\n | `sc-space__mark--${AgentTint}`\n | \"sc-space__who\"\n | \"sc-space__name\"\n | \"sc-space__role\"\n | \"sc-space__close\"\n | \"sc-space__body\"\n | \"sc-space__voice\"\n | \"sc-space__foot\"\n | \"sc-space__caption\"\n | \"sc-space__caption--person\"\n | \"sc-space__caption--agent\"\n | \"sc-space__status\"\n | \"sc-space__tasks\"\n | \"sc-space__bar\"\n | \"sc-space__threads\"\n | \"sc-space__dot\"\n | ModePart;\n\n/**\n * How the space sits: `compact` in a small panel (a widget's, a side of the host's page); `full`\n * as a page's main column, the conversation at a reading measure.\n */\nexport type SpaceLayout = \"compact\" | \"full\";\n\n/** How big a call's orb is drawn in each layout: a small panel keeps room for what is said. */\nconst ORB_SIZES: Readonly<Record<SpaceLayout, number>> = { compact: 200, full: 280 };\n\nexport interface AgentSpaceProps {\n client: Client;\n /** The agent the person talks to (its face is read with `client`). One per mount: key on it. */\n agentId: string;\n /**\n * The person's conversation to go on with, read when the space opens. Without one, or once it\n * is gone (not theirs, not there), a new one starts, told through `onConversation`.\n */\n conversationId?: string | undefined;\n /** Hears the conversation the space started, for the host to keep and give back next time. */\n onConversation?: ((id: string) => void) | undefined;\n layout?: SpaceLayout | undefined;\n /**\n * Draws the agent's face at the top: its mark in its tint, its name and role (and the close).\n * Off by default: the host's page says who it is, or draws its own with `useAgentFace`.\n */\n header?: boolean | undefined;\n /**\n * Whether the chat shows the agent's steps (`AgentChat`'s `steps`): by default hidden for a\n * person or a visitor, shown for the business.\n */\n steps?: ChatSteps | undefined;\n messages?: Partial<SpaceMessages> | undefined;\n classNames?:\n | Partial<\n Record<\n | SpacePart\n | ChatPart\n | SurfacePart\n | KitPart\n | BrowserPart\n | VoiceCallPart\n | TaskPart\n | ThreadPart,\n string\n >\n >\n | undefined;\n /** The host's own drawing of a block type, for every surface the agent shows. */\n components?: SurfaceComponents | undefined;\n /** The language of the surfaces' calendars, days, times and numbers; English by default. */\n locale?: KitLocale | undefined;\n /** A few things the person might say first, offered while nothing has been said. */\n suggestions?: readonly string[] | undefined;\n /** Runs the host's own tools the agent calls in the page (`AgentChat`'s `onTool`). */\n onTool?: ToolHandler | undefined;\n /** An error's code, from the chat's socket or `load`; the host words it. */\n onError?: ((code: string) => void) | undefined;\n /** How a call's orb is drawn: `\"wave\"` (the default) or `\"fluid-dots\"`. */\n orb?: OrbStyle | undefined;\n /** Draws a close button (in the face, or at the top's end without it), for a space in a panel. */\n onClose?: (() => void) | undefined;\n /**\n * Whether a person sees the tasks the agent runs for them (`AgentTasks`): a line over the chat\n * while there are any, which opens them. On by default; the business's own space never has it.\n */\n tasks?: boolean | undefined;\n /**\n * Whether a person sees their threads with the agent (`Threads`): a line over the chat, a dot on\n * it while another waits on them, which lists them by title with what is unread, to go to one\n * or start another. On by default; the business's own space never has it.\n */\n threads?: boolean | undefined;\n}\n\n/**\n * An agent in a host's page, for the people it serves: the conversation, and when the agent has a\n * voice, two modes beside the composer's paperclip, text and voice. Voice turns the space into\n * the call (`VoiceCall`, with the orb and what is being said), in the same place and size, with\n * the modes at its foot to go back; a call is a conversation of its own (the API makes one with\n * each), and once the agent took it the space goes on in it, where what was said shows, told\n * through `onConversation`. Ending the call, or choosing text, comes back to writing; a call that\n * couldn't begin stays to say why, and offers to try again. While the agent runs tasks for the\n * person (what they asked it to do later), a line over the chat opens them, with their runs, to\n * pause or take away. The conversation is the one given, or\n * one the space starts. The agent's face over it all is the host's to turn on (`header`). Styled\n * as `AgentChat` is: the tokens, `messages`, `classNames` and `components`. One agent per mount:\n * key it on the agent's id.\n */\nexport function AgentSpace({\n client,\n agentId,\n conversationId,\n onConversation,\n layout = \"compact\",\n header = false,\n steps,\n messages,\n classNames,\n components,\n locale,\n suggestions,\n onTool,\n onError,\n orb,\n onClose,\n tasks: showsTasks = true,\n threads: showsThreads = true,\n}: AgentSpaceProps) {\n const words = { ...SPACE_MESSAGES, ...messages };\n const theirs = useTasks(client, agentId, showsTasks && client.who === \"person\");\n const [listing, setListing] = useState(false);\n const threads = useUnread(client, agentId, showsThreads && client.who === \"person\");\n const [browsing, setBrowsing] = useState(false);\n const { face, loading } = useAgentFace(client, agentId);\n const opened = useOpened(client, agentId, conversationId, onConversation);\n const [mode, setMode] = useState<SpaceMode>(\"text\");\n // Each call made is a new attempt, which keys a new `VoiceCall`.\n const [attempt, setAttempt] = useState(0);\n // The conversation the call being made is (each call is one of its own, made with it), and\n // whether the agent took it: once it did, the space goes on in it.\n const [called, setCalled] = useState<string | null>(null);\n const answered = useRef(false);\n // Whether the modes drawn after a switch take the keyboard: when it was in the space (asked of\n // the space itself, which a shadow root's focus answers too).\n const [refocus, setRefocus] = useState(false);\n const root = useRef<HTMLElement>(null);\n const tint = agentTint(agentId);\n const Glyph = agentGlyph(face?.icon);\n const name = face?.name ?? (loading ? \"\" : words.agent);\n const id = opened.id;\n const voice = face?.voice.enabled === true;\n const parts: SpacePart[] = [`sc-space--${layout}`];\n if (mode === \"voice\") parts.push(\"sc-space--voice\");\n\n /** Back to writing: in the call's conversation once the agent took the call. */\n const write = () => {\n if (answered.current && called !== null) opened.go(called);\n setMode(\"text\");\n };\n const choose = (next: SpaceMode) => {\n setRefocus(root.current?.matches(\":focus-within\") ?? false);\n if (next === \"text\") {\n write();\n return;\n }\n answered.current = false;\n setCalled(null);\n setAttempt((count) => count + 1);\n setMode(\"voice\");\n };\n const modes = (current: SpaceMode) => (\n <Modes\n mode={current}\n onChange={choose}\n words={words.modes}\n classNames={classNames}\n focus={refocus}\n />\n );\n\n const body = () => {\n if (opened.failed) {\n return (\n <div className={classes(classNames, \"sc-space__status\")}>\n <p>{words.failed}</p>\n <Button size=\"sm\" onClick={opened.retry}>\n {words.retry}\n </Button>\n </div>\n );\n }\n if (id === null || loading) {\n return (\n <p role=\"status\" className={classes(classNames, \"sc-space__status\")}>\n {words.opening}\n </p>\n );\n }\n if (browsing && threads.threads !== null) {\n const went = (next: string) => {\n opened.go(next);\n setBrowsing(false);\n void threads.refresh();\n };\n return (\n <Threads\n threads={threads.threads}\n current={id}\n onSelect={went}\n onNew={() => {\n void client.conversations.start(agentId).then((started) => {\n went(started.id);\n });\n }}\n onBack={() => {\n setBrowsing(false);\n }}\n messages={words.threads}\n classNames={classNames}\n />\n );\n }\n if (listing && theirs.tasks !== null) {\n return (\n <AgentTasks\n client={client}\n agentId={agentId}\n tasks={theirs.tasks}\n onChange={theirs.refresh}\n onBack={() => {\n setListing(false);\n }}\n words={words.tasks}\n classNames={classNames}\n />\n );\n }\n if (mode === \"voice\" && voice) {\n return (\n <div part=\"voice\" className={classes(classNames, \"sc-space__voice\")}>\n <Suspense fallback={null}>\n <VoiceCall\n key={attempt}\n connect={async () => {\n const join = await client.conversations.call(agentId);\n setCalled(join.conversation_id);\n return join;\n }}\n onAnswer={() => {\n answered.current = true;\n }}\n // Over, the call gives the space back to writing; one that couldn't begin stays to\n // say why, with the way to try again and the modes to go back.\n onEnd={(_, problem) => {\n if (problem !== null) return;\n setRefocus(root.current?.matches(\":focus-within\") ?? false);\n write();\n }}\n name={name}\n tint={tint}\n orb={orb}\n orbSize={ORB_SIZES[layout]}\n messages={words.voice}\n classNames={classNames}\n captions={\n called !== null && (\n <Captions\n client={client}\n conversationId={called}\n onTool={onTool}\n classNames={classNames}\n />\n )\n }\n after={\n <Button\n tone=\"primary\"\n icon={PhoneIcon}\n onClick={() => {\n choose(\"voice\");\n }}\n >\n {words.again}\n </Button>\n }\n />\n </Suspense>\n <div className={classes(classNames, \"sc-space__foot\")}>{modes(\"voice\")}</div>\n </div>\n );\n }\n const count = theirs.tasks?.length ?? 0;\n // Another thread with something for them: the line to the threads carries a dot.\n const news = (threads.threads ?? []).some(\n (thread) =>\n thread.id !== id && ((thread.unread ?? 0) > 0 || thread.waiting_on_person === true),\n );\n return (\n <>\n {(threads.threads !== null || count > 0) && (\n <div className={classes(classNames, \"sc-space__bar\")}>\n {threads.threads !== null && (\n <button\n type=\"button\"\n part=\"threads-open\"\n className={classes(classNames, \"sc-space__threads\")}\n onClick={() => {\n setBrowsing(true);\n void threads.refresh();\n }}\n >\n {words.threads.open}\n {news && <span aria-hidden className={classes(classNames, \"sc-space__dot\")} />}\n </button>\n )}\n {count > 0 && (\n <button\n type=\"button\"\n part=\"tasks-open\"\n className={classes(classNames, \"sc-space__tasks\")}\n onClick={() => {\n setListing(true);\n void theirs.refresh();\n }}\n >\n {words.tasks.open(count)}\n </button>\n )}\n </div>\n )}\n <AgentChat\n client={client}\n conversationId={id}\n name={name}\n messages={words.chat}\n classNames={classNames}\n components={components}\n locale={locale}\n suggestions={suggestions}\n steps={steps}\n actions={voice ? modes(\"text\") : undefined}\n onTool={onTool}\n onError={onError}\n />\n </>\n );\n };\n\n const close = onClose && (\n <Button\n tone=\"ghost\"\n size=\"sm\"\n icon={XIcon}\n aria-label={words.close}\n title={words.close}\n onClick={onClose}\n part=\"close\"\n className={classes(classNames, \"sc-space__close\")}\n />\n );\n\n return (\n <KitProvider classNames={classNames} messages={words.chat.kit} locale={locale}>\n <section\n ref={root}\n aria-label={name || undefined}\n part=\"space\"\n className={`sc ${classes(classNames, \"sc-space\", ...parts)}`}\n >\n {header ? (\n <header part=\"header\" className={classes(classNames, \"sc-space__face\")}>\n <span\n aria-hidden\n className={classes(classNames, \"sc-space__mark\", `sc-space__mark--${tint}`)}\n >\n <Glyph weight=\"duotone\" />\n </span>\n <div className={classes(classNames, \"sc-space__who\")}>\n <h2 className={classes(classNames, \"sc-space__name\")}>{name}</h2>\n {face?.role && <p className={classes(classNames, \"sc-space__role\")}>{face.role}</p>}\n </div>\n {close}\n </header>\n ) : (\n close\n )}\n <div className={classes(classNames, \"sc-space__body\")}>{body()}</div>\n </section>\n </KitProvider>\n );\n}\n\n/**\n * What is being said on the call, under the orb: the person's last words, and the agent's answer\n * as it comes, read live from the call's own conversation (where the platform keeps them). The\n * host's tools the agent calls meanwhile run here too, as in the chat.\n */\nfunction Captions({\n client,\n conversationId,\n onTool,\n classNames,\n}: {\n client: Client;\n conversationId: string;\n onTool: ToolHandler | undefined;\n classNames: Partial<Record<SpacePart, string>> | undefined;\n}) {\n const live = useConversation(client, conversationId, { onTool });\n const { person, agent } = captionsOf(live);\n return (\n <>\n {person !== \"\" && (\n <p className={classes(classNames, \"sc-space__caption\", \"sc-space__caption--person\")}>\n {person}\n </p>\n )}\n {agent !== \"\" && (\n <p className={classes(classNames, \"sc-space__caption\", \"sc-space__caption--agent\")}>\n {agent}\n </p>\n )}\n </>\n );\n}\n\n/**\n * The last exchange of a call, as its captions say it: what the person last said, and the agent's\n * words after it (as they stream, else the last it said; its greeting before anything was said).\n */\nexport function captionsOf({\n messages,\n pending,\n draft,\n}: Pick<LiveConversation, \"messages\" | \"pending\" | \"draft\">): {\n person: string;\n agent: string;\n} {\n const lastSaid = messages.findLastIndex((message) => message.role === \"user\");\n const replied = messages\n .slice(lastSaid + 1)\n .findLast(\n (message) =>\n message.role === \"assistant\" && message.author !== \"operator\" && message.text !== \"\",\n );\n return {\n person: messages[lastSaid]?.text ?? \"\",\n agent: pending && draft !== \"\" ? draft : (replied?.text ?? \"\"),\n };\n}\n\n/** Whether a conversation asked for is gone for this person: not there, or not theirs. */\nconst isGone = (error: unknown): boolean =>\n error instanceof ApiError && (error.status === 404 || error.status === 403);\n\n/**\n * The conversation the space shows: the one given while it is still the person's, else a new one\n * with the agent, told to `onConversation`. Asked once per attempt (a second effect of the same\n * attempt, as React's strict mode runs, waits on the first rather than starting another); after\n * that, the conversation it opened is the one it goes on with, until it goes on in another (`go`:\n * a call's), told the same way.\n */\nfunction useOpened(\n client: Client,\n agentId: string,\n given: string | undefined,\n onConversation: ((id: string) => void) | undefined,\n): { id: string | null; failed: boolean; retry: () => void; go: (id: string) => void } {\n const kept = useRef(given);\n const asking = useRef<{ client: Client; attempt: number; id: Promise<string> } | null>(null);\n const [id, setId] = useState<string | null>(null);\n const [failed, setFailed] = useState(false);\n const [attempt, setAttempt] = useState(0);\n // The host's ear as it last gave it, told from the effect's answer and from a call's end alike.\n const told = useRef(onConversation);\n useEffect(() => {\n told.current = onConversation;\n });\n const started = (conversation: string) => {\n told.current?.(conversation);\n };\n\n useEffect(() => {\n let current = true;\n const start = () =>\n client.conversations.start(agentId).then((conversation) => {\n started(conversation.id);\n return conversation.id;\n });\n const open = (): Promise<string> => {\n const known = kept.current;\n if (known === undefined) return start();\n return client.conversations.get(known).then(\n (conversation) => conversation.id,\n (error: unknown) => (isGone(error) ? start() : Promise.reject(error)),\n );\n };\n const asked = asking.current;\n const ask =\n asked?.client === client && asked.attempt === attempt\n ? asked\n : { client, attempt, id: open() };\n asking.current = ask;\n ask.id.then(\n (opened) => {\n if (!current) return;\n kept.current = opened;\n setId(opened);\n },\n () => {\n if (current) setFailed(true);\n },\n );\n return () => {\n current = false;\n };\n }, [client, agentId, attempt]);\n\n return {\n id,\n failed,\n retry: () => {\n setFailed(false);\n setAttempt((count) => count + 1);\n },\n go: (conversation) => {\n if (conversation === kept.current) return;\n kept.current = conversation;\n setId(conversation);\n started(conversation);\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AgentSpace.js","names":[],"sources":["../src/AgentSpace.tsx"],"sourcesContent":["import { PhoneIcon, XIcon } from \"@phosphor-icons/react\";\nimport { lazy, Suspense, useEffect, useRef, useState } from \"react\";\n\nimport { AgentChat, type ChatMessages, type ChatPart, type ChatSteps } from \"./AgentChat\";\nimport { agentGlyph } from \"./agentIcons\";\nimport {\n AgentTasks,\n TASK_MESSAGES,\n useTasks,\n type TaskMessages,\n type TaskPart,\n} from \"./AgentTasks\";\nimport type { BrowserPart } from \"./BrowserView\";\nimport { classes } from \"./classes\";\nimport { ApiError, type Client } from \"./client\";\nimport { Button } from \"./kit/Button\";\nimport { KitProvider, type KitPart } from \"./kit/context\";\nimport type { KitLocale } from \"./kit/messages\";\nimport { Modes, type ModeMessages, type ModePart, type SpaceMode } from \"./Modes\";\nimport type { SurfacePlaces } from \"./surface/places\";\nimport type { HostBlocks, SurfaceComponents, SurfacePart } from \"./surface/scope\";\nimport { agentTint, type AgentTint } from \"./tint\";\nimport {\n THREAD_MESSAGES,\n Threads,\n useUnread,\n type ThreadMessages,\n type ThreadPart,\n} from \"./Threads\";\nimport { useAgentFace } from \"./useAgentFace\";\nimport { useConversation, type LiveConversation, type ToolHandler } from \"./useConversation\";\nimport type { VoiceCallMessages, VoiceCallPart } from \"./VoiceCall\";\nimport type { OrbStyle } from \"./voice/orbs\";\n\n// The call, and LiveKit under it, load the first time someone calls.\nconst VoiceCall = lazy(() =>\n import(\"./VoiceCall\").then((module) => ({ default: module.VoiceCall })),\n);\n\n/** The words the space says around the chat and the call; a host gives its own, in its language. */\nexport interface SpaceMessages {\n /** The two modes beside the composer's paperclip: the group's name, and each mode's. */\n modes: ModeMessages;\n /** After a call that couldn't begin: try it again. */\n again: string;\n /** The button that closes the space, where the host gives `onClose`. */\n close: string;\n /** The agent's name when its face can't be read. */\n agent: string;\n /** While the conversation is found, or started. */\n opening: string;\n /** The conversation could not be opened; and the button that tries again. */\n failed: string;\n retry: string;\n /** What the chat says (`AgentChat`'s words). */\n chat: Partial<ChatMessages>;\n /** What a call says (`VoiceCall`'s words). */\n voice: Partial<VoiceCallMessages>;\n /** What the person's tasks say (`AgentTasks`'s words). */\n tasks: TaskMessages;\n /** What the person's threads say (`Threads`'s words). */\n threads: ThreadMessages;\n}\n\nexport const SPACE_MESSAGES: SpaceMessages = {\n modes: { label: \"Mode\", text: \"Text\", voice: \"Voice\" },\n again: \"Call again\",\n close: \"Close\",\n agent: \"Agent\",\n opening: \"Opening the conversation…\",\n failed: \"The conversation couldn't be opened.\",\n retry: \"Try again\",\n chat: {},\n voice: {},\n tasks: TASK_MESSAGES,\n threads: THREAD_MESSAGES,\n};\n\n/** The space's parts, by the class each carries; `classNames` adds a host's own under the same keys. */\nexport type SpacePart =\n | \"sc-space\"\n | \"sc-space--compact\"\n | \"sc-space--full\"\n | \"sc-space--voice\"\n | \"sc-space__face\"\n | \"sc-space__mark\"\n | `sc-space__mark--${AgentTint}`\n | \"sc-space__who\"\n | \"sc-space__name\"\n | \"sc-space__role\"\n | \"sc-space__close\"\n | \"sc-space__body\"\n | \"sc-space__voice\"\n | \"sc-space__foot\"\n | \"sc-space__caption\"\n | \"sc-space__caption--person\"\n | \"sc-space__caption--agent\"\n | \"sc-space__status\"\n | \"sc-space__tasks\"\n | \"sc-space__bar\"\n | \"sc-space__threads\"\n | \"sc-space__dot\"\n | ModePart;\n\n/**\n * How the space sits: `compact` in a small panel (a widget's, a side of the host's page); `full`\n * as a page's main column, the conversation at a reading measure.\n */\nexport type SpaceLayout = \"compact\" | \"full\";\n\n/** How big a call's orb is drawn in each layout: a small panel keeps room for what is said. */\nconst ORB_SIZES: Readonly<Record<SpaceLayout, number>> = { compact: 200, full: 280 };\n\nexport interface AgentSpaceProps {\n client: Client;\n /** The agent the person talks to (its face is read with `client`). One per mount: key on it. */\n agentId: string;\n /**\n * The person's conversation to go on with, read when the space opens. Without one, or once it\n * is gone (not theirs, not there), a new one starts, told through `onConversation`.\n */\n conversationId?: string | undefined;\n /** Hears the conversation the space started, for the host to keep and give back next time. */\n onConversation?: ((id: string) => void) | undefined;\n layout?: SpaceLayout | undefined;\n /**\n * Draws the agent's face at the top: its mark in its tint, its name and role (and the close).\n * Off by default: the host's page says who it is, or draws its own with `useAgentFace`.\n */\n header?: boolean | undefined;\n /**\n * Whether the chat shows the agent's steps (`AgentChat`'s `steps`): by default hidden for a\n * person or a visitor, shown for the business.\n */\n steps?: ChatSteps | undefined;\n messages?: Partial<SpaceMessages> | undefined;\n classNames?:\n | Partial<\n Record<\n | SpacePart\n | ChatPart\n | SurfacePart\n | KitPart\n | BrowserPart\n | VoiceCallPart\n | TaskPart\n | ThreadPart,\n string\n >\n >\n | undefined;\n /** The host's own drawing of a block type, for every surface the agent shows. */\n components?: SurfaceComponents | undefined;\n /**\n * The host's own blocks, by the name each was declared with (`PUT /v1/blocks/{name}`):\n * `{ \"demoprep.deal-card\": DealCard }`. One it doesn't give shows its words.\n */\n blocks?: HostBlocks | undefined;\n /**\n * Where the agent may put what it shows besides the conversation: `panel`, beside it (over it\n * in a narrow space), and `modal`, over the page; both by default. A place left out shows in\n * the conversation: `{ panel: true, modal: false }`.\n */\n surfaces?: SurfacePlaces | undefined;\n /** The language of the surfaces' calendars, days, times and numbers; English by default. */\n locale?: KitLocale | undefined;\n /** A few things the person might say first, offered while nothing has been said. */\n suggestions?: readonly string[] | undefined;\n /** Runs the host's own tools the agent calls in the page (`AgentChat`'s `onTool`). */\n onTool?: ToolHandler | undefined;\n /** An error's code, from the chat's socket or `load`; the host words it. */\n onError?: ((code: string) => void) | undefined;\n /** How a call's orb is drawn: `\"wave\"` (the default) or `\"fluid-dots\"`. */\n orb?: OrbStyle | undefined;\n /** Draws a close button (in the face, or at the top's end without it), for a space in a panel. */\n onClose?: (() => void) | undefined;\n /**\n * Whether a person sees the tasks the agent runs for them (`AgentTasks`): a line over the chat\n * while there are any, which opens them. On by default; the business's own space never has it.\n */\n tasks?: boolean | undefined;\n /**\n * Whether a person sees their threads with the agent (`Threads`): a line over the chat, a dot on\n * it while another waits on them, which lists them by title with what is unread, to go to one\n * or start another. On by default; the business's own space never has it.\n */\n threads?: boolean | undefined;\n}\n\n/**\n * An agent in a host's page, for the people it serves: the conversation, and when the agent has a\n * voice, two modes beside the composer's paperclip, text and voice. Voice turns the space into\n * the call (`VoiceCall`, with the orb and what is being said), in the same place and size, with\n * the modes at its foot to go back; a call is a conversation of its own (the API makes one with\n * each), and once the agent took it the space goes on in it, where what was said shows, told\n * through `onConversation`. Ending the call, or choosing text, comes back to writing; a call that\n * couldn't begin stays to say why, and offers to try again. While the agent runs tasks for the\n * person (what they asked it to do later), a line over the chat opens them, with their runs, to\n * pause or take away. The conversation is the one given, or\n * one the space starts. The agent's face over it all is the host's to turn on (`header`). Styled\n * as `AgentChat` is: the tokens, `messages`, `classNames` and `components`. One agent per mount:\n * key it on the agent's id.\n */\nexport function AgentSpace({\n client,\n agentId,\n conversationId,\n onConversation,\n layout = \"compact\",\n header = false,\n steps,\n messages,\n classNames,\n components,\n blocks,\n surfaces,\n locale,\n suggestions,\n onTool,\n onError,\n orb,\n onClose,\n tasks: showsTasks = true,\n threads: showsThreads = true,\n}: AgentSpaceProps) {\n const words = { ...SPACE_MESSAGES, ...messages };\n const theirs = useTasks(client, agentId, showsTasks && client.who === \"person\");\n const [listing, setListing] = useState(false);\n const threads = useUnread(client, agentId, showsThreads && client.who === \"person\");\n const [browsing, setBrowsing] = useState(false);\n const { face, loading } = useAgentFace(client, agentId);\n const opened = useOpened(client, agentId, conversationId, onConversation);\n const [mode, setMode] = useState<SpaceMode>(\"text\");\n // Each call made is a new attempt, which keys a new `VoiceCall`.\n const [attempt, setAttempt] = useState(0);\n // The conversation the call being made is (each call is one of its own, made with it), and\n // whether the agent took it: once it did, the space goes on in it.\n const [called, setCalled] = useState<string | null>(null);\n const answered = useRef(false);\n // Whether the modes drawn after a switch take the keyboard: when it was in the space (asked of\n // the space itself, which a shadow root's focus answers too).\n const [refocus, setRefocus] = useState(false);\n const root = useRef<HTMLElement>(null);\n const tint = agentTint(agentId);\n const Glyph = agentGlyph(face?.icon);\n const name = face?.name ?? (loading ? \"\" : words.agent);\n const id = opened.id;\n const voice = face?.voice.enabled === true;\n const parts: SpacePart[] = [`sc-space--${layout}`];\n if (mode === \"voice\") parts.push(\"sc-space--voice\");\n\n /** Back to writing: in the call's conversation once the agent took the call. */\n const write = () => {\n if (answered.current && called !== null) opened.go(called);\n setMode(\"text\");\n };\n const choose = (next: SpaceMode) => {\n setRefocus(root.current?.matches(\":focus-within\") ?? false);\n if (next === \"text\") {\n write();\n return;\n }\n answered.current = false;\n setCalled(null);\n setAttempt((count) => count + 1);\n setMode(\"voice\");\n };\n const modes = (current: SpaceMode) => (\n <Modes\n mode={current}\n onChange={choose}\n words={words.modes}\n classNames={classNames}\n focus={refocus}\n />\n );\n\n const body = () => {\n if (opened.failed) {\n return (\n <div className={classes(classNames, \"sc-space__status\")}>\n <p>{words.failed}</p>\n <Button size=\"sm\" onClick={opened.retry}>\n {words.retry}\n </Button>\n </div>\n );\n }\n if (id === null || loading) {\n return (\n <p role=\"status\" className={classes(classNames, \"sc-space__status\")}>\n {words.opening}\n </p>\n );\n }\n if (browsing && threads.threads !== null) {\n const went = (next: string) => {\n opened.go(next);\n setBrowsing(false);\n void threads.refresh();\n };\n return (\n <Threads\n threads={threads.threads}\n current={id}\n onSelect={went}\n onNew={() => {\n void client.conversations.start(agentId).then((started) => {\n went(started.id);\n });\n }}\n onBack={() => {\n setBrowsing(false);\n }}\n messages={words.threads}\n classNames={classNames}\n />\n );\n }\n if (listing && theirs.tasks !== null) {\n return (\n <AgentTasks\n client={client}\n agentId={agentId}\n tasks={theirs.tasks}\n onChange={theirs.refresh}\n onBack={() => {\n setListing(false);\n }}\n words={words.tasks}\n classNames={classNames}\n />\n );\n }\n if (mode === \"voice\" && voice) {\n return (\n <div part=\"voice\" className={classes(classNames, \"sc-space__voice\")}>\n <Suspense fallback={null}>\n <VoiceCall\n key={attempt}\n connect={async () => {\n const join = await client.conversations.call(agentId);\n setCalled(join.conversation_id);\n return join;\n }}\n onAnswer={() => {\n answered.current = true;\n }}\n // Over, the call gives the space back to writing; one that couldn't begin stays to\n // say why, with the way to try again and the modes to go back.\n onEnd={(_, problem) => {\n if (problem !== null) return;\n setRefocus(root.current?.matches(\":focus-within\") ?? false);\n write();\n }}\n name={name}\n tint={tint}\n orb={orb}\n orbSize={ORB_SIZES[layout]}\n messages={words.voice}\n classNames={classNames}\n captions={\n called !== null && (\n <Captions\n client={client}\n conversationId={called}\n onTool={onTool}\n classNames={classNames}\n />\n )\n }\n after={\n <Button\n tone=\"primary\"\n icon={PhoneIcon}\n onClick={() => {\n choose(\"voice\");\n }}\n >\n {words.again}\n </Button>\n }\n />\n </Suspense>\n <div className={classes(classNames, \"sc-space__foot\")}>{modes(\"voice\")}</div>\n </div>\n );\n }\n const count = theirs.tasks?.length ?? 0;\n // Another thread with something for them: the line to the threads carries a dot.\n const news = (threads.threads ?? []).some(\n (thread) =>\n thread.id !== id && ((thread.unread ?? 0) > 0 || thread.waiting_on_person === true),\n );\n return (\n <>\n {(threads.threads !== null || count > 0) && (\n <div className={classes(classNames, \"sc-space__bar\")}>\n {threads.threads !== null && (\n <button\n type=\"button\"\n part=\"threads-open\"\n className={classes(classNames, \"sc-space__threads\")}\n onClick={() => {\n setBrowsing(true);\n void threads.refresh();\n }}\n >\n {words.threads.open}\n {news && <span aria-hidden className={classes(classNames, \"sc-space__dot\")} />}\n </button>\n )}\n {count > 0 && (\n <button\n type=\"button\"\n part=\"tasks-open\"\n className={classes(classNames, \"sc-space__tasks\")}\n onClick={() => {\n setListing(true);\n void theirs.refresh();\n }}\n >\n {words.tasks.open(count)}\n </button>\n )}\n </div>\n )}\n <AgentChat\n client={client}\n conversationId={id}\n name={name}\n messages={words.chat}\n classNames={classNames}\n components={components}\n blocks={blocks}\n surfaces={surfaces}\n locale={locale}\n suggestions={suggestions}\n steps={steps}\n actions={voice ? modes(\"text\") : undefined}\n onTool={onTool}\n onError={onError}\n />\n </>\n );\n };\n\n const close = onClose && (\n <Button\n tone=\"ghost\"\n size=\"sm\"\n icon={XIcon}\n aria-label={words.close}\n title={words.close}\n onClick={onClose}\n part=\"close\"\n className={classes(classNames, \"sc-space__close\")}\n />\n );\n\n return (\n <KitProvider classNames={classNames} messages={words.chat.kit} locale={locale}>\n <section\n ref={root}\n aria-label={name || undefined}\n part=\"space\"\n className={`sc ${classes(classNames, \"sc-space\", ...parts)}`}\n >\n {header ? (\n <header part=\"header\" className={classes(classNames, \"sc-space__face\")}>\n <span\n aria-hidden\n className={classes(classNames, \"sc-space__mark\", `sc-space__mark--${tint}`)}\n >\n <Glyph weight=\"duotone\" />\n </span>\n <div className={classes(classNames, \"sc-space__who\")}>\n <h2 className={classes(classNames, \"sc-space__name\")}>{name}</h2>\n {face?.role && <p className={classes(classNames, \"sc-space__role\")}>{face.role}</p>}\n </div>\n {close}\n </header>\n ) : (\n close\n )}\n <div className={classes(classNames, \"sc-space__body\")}>{body()}</div>\n </section>\n </KitProvider>\n );\n}\n\n/**\n * What is being said on the call, under the orb: the person's last words, and the agent's answer\n * as it comes, read live from the call's own conversation (where the platform keeps them). The\n * host's tools the agent calls meanwhile run here too, as in the chat.\n */\nfunction Captions({\n client,\n conversationId,\n onTool,\n classNames,\n}: {\n client: Client;\n conversationId: string;\n onTool: ToolHandler | undefined;\n classNames: Partial<Record<SpacePart, string>> | undefined;\n}) {\n const live = useConversation(client, conversationId, { onTool });\n const { person, agent } = captionsOf(live);\n return (\n <>\n {person !== \"\" && (\n <p className={classes(classNames, \"sc-space__caption\", \"sc-space__caption--person\")}>\n {person}\n </p>\n )}\n {agent !== \"\" && (\n <p className={classes(classNames, \"sc-space__caption\", \"sc-space__caption--agent\")}>\n {agent}\n </p>\n )}\n </>\n );\n}\n\n/**\n * The last exchange of a call, as its captions say it: what the person last said, and the agent's\n * words after it (as they stream, else the last it said; its greeting before anything was said).\n */\nexport function captionsOf({\n messages,\n pending,\n draft,\n}: Pick<LiveConversation, \"messages\" | \"pending\" | \"draft\">): {\n person: string;\n agent: string;\n} {\n const lastSaid = messages.findLastIndex((message) => message.role === \"user\");\n const replied = messages\n .slice(lastSaid + 1)\n .findLast(\n (message) =>\n message.role === \"assistant\" && message.author !== \"operator\" && message.text !== \"\",\n );\n return {\n person: messages[lastSaid]?.text ?? \"\",\n agent: pending && draft !== \"\" ? draft : (replied?.text ?? \"\"),\n };\n}\n\n/** Whether a conversation asked for is gone for this person: not there, or not theirs. */\nconst isGone = (error: unknown): boolean =>\n error instanceof ApiError && (error.status === 404 || error.status === 403);\n\n/**\n * The conversation the space shows: the one given while it is still the person's, else a new one\n * with the agent, told to `onConversation`. Asked once per attempt (a second effect of the same\n * attempt, as React's strict mode runs, waits on the first rather than starting another); after\n * that, the conversation it opened is the one it goes on with, until it goes on in another (`go`:\n * a call's), told the same way.\n */\nfunction useOpened(\n client: Client,\n agentId: string,\n given: string | undefined,\n onConversation: ((id: string) => void) | undefined,\n): { id: string | null; failed: boolean; retry: () => void; go: (id: string) => void } {\n const kept = useRef(given);\n const asking = useRef<{ client: Client; attempt: number; id: Promise<string> } | null>(null);\n const [id, setId] = useState<string | null>(null);\n const [failed, setFailed] = useState(false);\n const [attempt, setAttempt] = useState(0);\n // The host's ear as it last gave it, told from the effect's answer and from a call's end alike.\n const told = useRef(onConversation);\n useEffect(() => {\n told.current = onConversation;\n });\n const started = (conversation: string) => {\n told.current?.(conversation);\n };\n\n useEffect(() => {\n let current = true;\n const start = () =>\n client.conversations.start(agentId).then((conversation) => {\n started(conversation.id);\n return conversation.id;\n });\n const open = (): Promise<string> => {\n const known = kept.current;\n if (known === undefined) return start();\n return client.conversations.get(known).then(\n (conversation) => conversation.id,\n (error: unknown) => (isGone(error) ? start() : Promise.reject(error)),\n );\n };\n const asked = asking.current;\n const ask =\n asked?.client === client && asked.attempt === attempt\n ? asked\n : { client, attempt, id: open() };\n asking.current = ask;\n ask.id.then(\n (opened) => {\n if (!current) return;\n kept.current = opened;\n setId(opened);\n },\n () => {\n if (current) setFailed(true);\n },\n );\n return () => {\n current = false;\n };\n }, [client, agentId, attempt]);\n\n return {\n id,\n failed,\n retry: () => {\n setFailed(false);\n setAttempt((count) => count + 1);\n },\n go: (conversation) => {\n if (conversation === kept.current) return;\n kept.current = conversation;\n setId(conversation);\n started(conversation);\n },\n };\n}\n"],"mappings":"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"}
package/dist/View.d.ts ADDED
@@ -0,0 +1,48 @@
1
+ import { ViewRendered } from "./types.js";
2
+ import { Client } from "./client.js";
3
+ import { KitLocale, KitMessages } from "./kit/messages.js";
4
+ import { SurfaceAction } from "./surface/values.js";
5
+ import { HostBlocks, SurfaceClassNames, SurfaceComponents } from "./surface/scope.js";
6
+ //#region src/View.d.ts
7
+ /** How often a view is read again while it is shown: the figures move as the rows do. */
8
+ export declare const VIEW_POLL_MS = 30000;
9
+ /** A view as it was last read: the view once it came, why it failed, or that it isn't there. */
10
+ export interface ViewRead {
11
+ view: ViewRendered | null;
12
+ problem: unknown;
13
+ /** No such view, or not one for this person (404). */
14
+ gone: boolean;
15
+ }
16
+ /**
17
+ * An agent's view (`name`), rendered for the person the page speaks for (their token: their
18
+ * own) or, for the business, for the contact it names: read when shown and again every
19
+ * `VIEW_POLL_MS`, what was read staying while the next read is on its way.
20
+ */
21
+ export declare function useView(client: Client, name: string, contact?: string): ViewRead;
22
+ export interface ViewProps {
23
+ client: Client;
24
+ /** The view's name, as its agent or the business wrote it (`proxima_reunion`). */
25
+ name: string;
26
+ /** For the business: the contact it is rendered for (their id). A person's token: their own. */
27
+ contact?: string | undefined;
28
+ /** The host's own blocks, by the name each was declared with. */
29
+ blocks?: HostBlocks | undefined;
30
+ /** The host's own drawing of a block type. */
31
+ components?: SurfaceComponents | undefined;
32
+ classNames?: SurfaceClassNames;
33
+ /** The words of its blocks, in the host's language; English by default. */
34
+ messages?: Partial<KitMessages> | undefined;
35
+ /** The language of its days, times and numbers; English by default. */
36
+ locale?: KitLocale | undefined;
37
+ /** What the person does on a host's block that sends something; without it, they only read. */
38
+ onAction?: ((action: SurfaceAction) => void) | undefined;
39
+ }
40
+ /**
41
+ * An agent's view where the host's page puts it (a rep's next meeting on its home, their week
42
+ * beside a record), drawn with the kit's blocks in the host's style, live: read again every half
43
+ * minute, so it shows what the rows hold now, never a copy. While it is first read, an empty
44
+ * box holds its place; a view that isn't there (or isn't the person's) draws nothing.
45
+ */
46
+ export declare function View({ client, name, contact, blocks, components, classNames, messages, locale, onAction }: ViewProps): import("react").JSX.Element;
47
+ //#endregion
48
+ //# sourceMappingURL=View.d.ts.map
package/dist/View.js ADDED
@@ -0,0 +1,101 @@
1
+ import { classes } from "./classes.js";
2
+ import { ApiError } from "./client.js";
3
+ import { Surface } from "./surface/Surface.js";
4
+ import { useEffect, useRef, useState } from "react";
5
+ import { jsx } from "react/jsx-runtime";
6
+ //#region src/View.tsx
7
+ /** How often a view is read again while it is shown: the figures move as the rows do. */
8
+ const VIEW_POLL_MS = 3e4;
9
+ /**
10
+ * An agent's view (`name`), rendered for the person the page speaks for (their token: their
11
+ * own) or, for the business, for the contact it names: read when shown and again every
12
+ * `VIEW_POLL_MS`, what was read staying while the next read is on its way.
13
+ */
14
+ function useView(client, name, contact) {
15
+ const key = `${name}\n${contact ?? ""}`;
16
+ const [read, setRead] = useState({
17
+ key,
18
+ view: null,
19
+ problem: null,
20
+ gone: false
21
+ });
22
+ const latest = useRef({
23
+ client,
24
+ name,
25
+ contact
26
+ });
27
+ useEffect(() => {
28
+ latest.current = {
29
+ client,
30
+ name,
31
+ contact
32
+ };
33
+ });
34
+ useEffect(() => {
35
+ let stopped = false;
36
+ let timer;
37
+ const load = async () => {
38
+ const now = latest.current;
39
+ try {
40
+ const view = await now.client.views.render(now.name, now.contact);
41
+ if (stopped) return;
42
+ setRead({
43
+ key,
44
+ view,
45
+ problem: null,
46
+ gone: false
47
+ });
48
+ } catch (error) {
49
+ if (stopped) return;
50
+ const gone = error instanceof ApiError && error.status === 404;
51
+ setRead((last) => ({
52
+ ...last,
53
+ key,
54
+ problem: gone ? null : error,
55
+ gone
56
+ }));
57
+ }
58
+ if (!stopped) timer = setTimeout(() => void load(), VIEW_POLL_MS);
59
+ };
60
+ load();
61
+ return () => {
62
+ stopped = true;
63
+ clearTimeout(timer);
64
+ };
65
+ }, [client, key]);
66
+ return read.key === key ? read : {
67
+ view: null,
68
+ problem: null,
69
+ gone: false
70
+ };
71
+ }
72
+ /**
73
+ * An agent's view where the host's page puts it (a rep's next meeting on its home, their week
74
+ * beside a record), drawn with the kit's blocks in the host's style, live: read again every half
75
+ * minute, so it shows what the rows hold now, never a copy. While it is first read, an empty
76
+ * box holds its place; a view that isn't there (or isn't the person's) draws nothing.
77
+ */
78
+ function View({ client, name, contact, blocks, components, classNames, messages, locale, onAction }) {
79
+ const { view } = useView(client, name, contact);
80
+ if (view === null) return /* @__PURE__ */ jsx("div", {
81
+ "aria-busy": "true",
82
+ className: `sc ${classes(classNames, "sc-surface")}`
83
+ });
84
+ return /* @__PURE__ */ jsx(Surface, {
85
+ surface: {
86
+ blocks: view.blocks,
87
+ text: view.text
88
+ },
89
+ active: onAction !== void 0,
90
+ onAction: onAction ?? (() => void 0),
91
+ blocks,
92
+ components,
93
+ classNames,
94
+ messages,
95
+ locale
96
+ });
97
+ }
98
+ //#endregion
99
+ export { VIEW_POLL_MS, View, useView };
100
+
101
+ //# sourceMappingURL=View.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"View.js","names":[],"sources":["../src/View.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\n\nimport { classes } from \"./classes\";\nimport { ApiError, type Client } from \"./client\";\nimport type { KitLocale, KitMessages } from \"./kit/messages\";\nimport type { HostBlocks, SurfaceClassNames, SurfaceComponents } from \"./surface/scope\";\nimport { Surface } from \"./surface/Surface\";\nimport type { SurfaceAction } from \"./surface/values\";\nimport type { ViewRendered } from \"./types\";\n\n/** How often a view is read again while it is shown: the figures move as the rows do. */\nexport const VIEW_POLL_MS = 30_000;\n\n/** A view as it was last read: the view once it came, why it failed, or that it isn't there. */\nexport interface ViewRead {\n view: ViewRendered | null;\n problem: unknown;\n /** No such view, or not one for this person (404). */\n gone: boolean;\n}\n\n/**\n * An agent's view (`name`), rendered for the person the page speaks for (their token: their\n * own) or, for the business, for the contact it names: read when shown and again every\n * `VIEW_POLL_MS`, what was read staying while the next read is on its way.\n */\nexport function useView(client: Client, name: string, contact?: string): ViewRead {\n const key = `${name}\\n${contact ?? \"\"}`;\n const [read, setRead] = useState<ViewRead & { key: string }>({\n key,\n view: null,\n problem: null,\n gone: false,\n });\n const latest = useRef({ client, name, contact });\n useEffect(() => {\n latest.current = { client, name, contact };\n });\n useEffect(() => {\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const load = async () => {\n const now = latest.current;\n try {\n const view = await now.client.views.render(now.name, now.contact);\n if (stopped) return;\n setRead({ key, view, problem: null, gone: false });\n } catch (error) {\n if (stopped) return;\n const gone = error instanceof ApiError && error.status === 404;\n setRead((last) => ({ ...last, key, problem: gone ? null : error, gone }));\n }\n if (!stopped) timer = setTimeout(() => void load(), VIEW_POLL_MS);\n };\n void load();\n return () => {\n stopped = true;\n clearTimeout(timer);\n };\n }, [client, key]);\n // Another view's read is not this one's: until its own comes, nothing is shown.\n return read.key === key ? read : { view: null, problem: null, gone: false };\n}\n\nexport interface ViewProps {\n client: Client;\n /** The view's name, as its agent or the business wrote it (`proxima_reunion`). */\n name: string;\n /** For the business: the contact it is rendered for (their id). A person's token: their own. */\n contact?: string | undefined;\n /** The host's own blocks, by the name each was declared with. */\n blocks?: HostBlocks | undefined;\n /** The host's own drawing of a block type. */\n components?: SurfaceComponents | undefined;\n classNames?: SurfaceClassNames;\n /** The words of its blocks, in the host's language; English by default. */\n messages?: Partial<KitMessages> | undefined;\n /** The language of its days, times and numbers; English by default. */\n locale?: KitLocale | undefined;\n /** What the person does on a host's block that sends something; without it, they only read. */\n onAction?: ((action: SurfaceAction) => void) | undefined;\n}\n\n/**\n * An agent's view where the host's page puts it (a rep's next meeting on its home, their week\n * beside a record), drawn with the kit's blocks in the host's style, live: read again every half\n * minute, so it shows what the rows hold now, never a copy. While it is first read, an empty\n * box holds its place; a view that isn't there (or isn't the person's) draws nothing.\n */\nexport function View({\n client,\n name,\n contact,\n blocks,\n components,\n classNames,\n messages,\n locale,\n onAction,\n}: ViewProps) {\n const { view } = useView(client, name, contact);\n if (view === null) {\n return <div aria-busy=\"true\" className={`sc ${classes(classNames, \"sc-surface\")}`} />;\n }\n return (\n <Surface\n surface={{ blocks: view.blocks, text: view.text }}\n active={onAction !== undefined}\n onAction={onAction ?? (() => undefined)}\n blocks={blocks}\n components={components}\n classNames={classNames}\n messages={messages}\n locale={locale}\n />\n );\n}\n"],"mappings":";;;;;;;AAWA,MAAa,eAAe;;;;;;AAe5B,SAAgB,QAAQ,QAAgB,MAAc,SAA4B;CAChF,MAAM,MAAM,GAAG,KAAK,IAAI,WAAW;CACnC,MAAM,CAAC,MAAM,WAAW,SAAqC;EAC3D;EACA,MAAM;EACN,SAAS;EACT,MAAM;CACR,CAAC;CACD,MAAM,SAAS,OAAO;EAAE;EAAQ;EAAM;CAAQ,CAAC;CAC/C,gBAAgB;EACd,OAAO,UAAU;GAAE;GAAQ;GAAM;EAAQ;CAC3C,CAAC;CACD,gBAAgB;EACd,IAAI,UAAU;EACd,IAAI;EACJ,MAAM,OAAO,YAAY;GACvB,MAAM,MAAM,OAAO;GACnB,IAAI;IACF,MAAM,OAAO,MAAM,IAAI,OAAO,MAAM,OAAO,IAAI,MAAM,IAAI,OAAO;IAChE,IAAI,SAAS;IACb,QAAQ;KAAE;KAAK;KAAM,SAAS;KAAM,MAAM;IAAM,CAAC;GACnD,SAAS,OAAO;IACd,IAAI,SAAS;IACb,MAAM,OAAO,iBAAiB,YAAY,MAAM,WAAW;IAC3D,SAAS,UAAU;KAAE,GAAG;KAAM;KAAK,SAAS,OAAO,OAAO;KAAO;IAAK,EAAE;GAC1E;GACA,IAAI,CAAC,SAAS,QAAQ,iBAAiB,KAAK,KAAK,GAAG,YAAY;EAClE;EACA,KAAU;EACV,aAAa;GACX,UAAU;GACV,aAAa,KAAK;EACpB;CACF,GAAG,CAAC,QAAQ,GAAG,CAAC;CAEhB,OAAO,KAAK,QAAQ,MAAM,OAAO;EAAE,MAAM;EAAM,SAAS;EAAM,MAAM;CAAM;AAC5E;;;;;;;AA2BA,SAAgB,KAAK,EACnB,QACA,MACA,SACA,QACA,YACA,YACA,UACA,QACA,YACY;CACZ,MAAM,EAAE,SAAS,QAAQ,QAAQ,MAAM,OAAO;CAC9C,IAAI,SAAS,MACX,OAAO,oBAAC,OAAD;EAAK,aAAU;EAAO,WAAW,MAAM,QAAQ,YAAY,YAAY;CAAM,CAAA;CAEtF,OACE,oBAAC,SAAD;EACE,SAAS;GAAE,QAAQ,KAAK;GAAQ,MAAM,KAAK;EAAK;EAChD,QAAQ,aAAa,KAAA;EACrB,UAAU,mBAAmB,KAAA;EACrB;EACI;EACA;EACF;EACF;CACT,CAAA;AAEL"}
package/dist/blocks.d.ts CHANGED
@@ -103,6 +103,76 @@ export interface ConnectBlock {
103
103
  purpose: string;
104
104
  connection: ConnectView | null;
105
105
  }
106
+ /**
107
+ * One thing on a calendar: its title at its time ("YYYY-MM-DDTHH:MM" in the person's time; a day
108
+ * alone, "YYYY-MM-DD", is all of it), until `end`, with a line of detail and a page to open.
109
+ */
110
+ export interface CalendarEvent {
111
+ id: string;
112
+ title: string;
113
+ start: string;
114
+ end?: string;
115
+ detail?: string;
116
+ /** https only. */
117
+ url?: string;
118
+ }
119
+ /**
120
+ * A day's events, or a week's from `start` ("YYYY-MM-DD"), in their order. With `action`,
121
+ * pressing one sends it with `{event: id}`; one with `url` opens its page instead.
122
+ */
123
+ export interface CalendarBlock {
124
+ type: "calendar";
125
+ span: "day" | "week";
126
+ start: string;
127
+ events: CalendarEvent[];
128
+ action?: string;
129
+ }
130
+ /** One thing on a timeline: when, what, a line about it, where it stands in a word, its mark. */
131
+ export interface TimelineItem {
132
+ id?: string;
133
+ at: string;
134
+ title: string;
135
+ text?: string;
136
+ status?: string;
137
+ tone?: Tone;
138
+ }
139
+ /** Dated things, one under another, the newest first. */
140
+ export interface TimelineBlock {
141
+ type: "timeline";
142
+ items: TimelineItem[];
143
+ }
144
+ export interface BoardCard {
145
+ id: string;
146
+ title: string;
147
+ subtitle?: string;
148
+ /** At its foot: a price, a day, who. */
149
+ meta?: string;
150
+ }
151
+ export interface BoardColumn {
152
+ id: string;
153
+ title: string;
154
+ cards: BoardCard[];
155
+ }
156
+ /**
157
+ * Cards in columns, a column per stage. With `action`, the person moves a card to another column,
158
+ * which sends `action` with `{card, column}`.
159
+ */
160
+ export interface BoardBlock {
161
+ type: "board";
162
+ columns: BoardColumn[];
163
+ action?: string;
164
+ }
165
+ /**
166
+ * One of the host's own blocks, by the name it declared (`PUT /v1/blocks/{name}`): its `props`,
167
+ * checked by the API against the schema the host declared, and `text`, the same in words, which a
168
+ * page that doesn't draw it shows instead.
169
+ */
170
+ export interface HostBlock {
171
+ type: "host";
172
+ name: string;
173
+ props: Record<string, unknown>;
174
+ text: string;
175
+ }
106
176
  /** One thing the person can do: pressing it sends `action`, or, with `url`, opens that page. */
107
177
  export interface ButtonBlock {
108
178
  type: "button";
@@ -197,11 +267,22 @@ export interface StackBlock {
197
267
  direction: "column" | "row";
198
268
  children: Block[];
199
269
  }
200
- export type Block = CardBlock | StackBlock | DividerBlock | TextBlock | FieldsBlock | TableBlock | ListBlock | StatBlock | ChartBlock | NoteBlock | ImageBlock | AutomationBlock | FileBlock | ConnectBlock | ButtonBlock | ChoiceBlock | FormBlock;
201
- /** What the agent shows: blocks, and the words a channel without a screen gets instead. */
270
+ export type Block = CardBlock | StackBlock | DividerBlock | TextBlock | FieldsBlock | TableBlock | ListBlock | StatBlock | ChartBlock | NoteBlock | ImageBlock | AutomationBlock | FileBlock | ConnectBlock | ButtonBlock | ChoiceBlock | FormBlock | CalendarBlock | TimelineBlock | BoardBlock | HostBlock;
271
+ /**
272
+ * Where a surface shows in a page: in the conversation, in a panel beside it, or in a modal over
273
+ * it. A page that doesn't allow the place shows it in the conversation.
274
+ */
275
+ export type Place = "chat" | "panel" | "modal";
276
+ /**
277
+ * What the agent shows: blocks, and the words a channel without a screen gets instead; where it
278
+ * shows and, in a panel or a modal, its title, which the conversation's line to open it says.
279
+ */
202
280
  export interface Surface {
203
281
  blocks: Block[];
204
282
  text: string;
283
+ /** None: the conversation. */
284
+ where?: Place;
285
+ title?: string;
205
286
  }
206
287
  //#endregion
207
288
  //# sourceMappingURL=blocks.d.ts.map
package/dist/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { AboutOut, MemoryFact } from "./about.js";
2
2
  import { Automation, AutomationView, RunRecord, Task } from "./automation/types.js";
3
3
  import { Credentials, Speaker } from "./credentials.js";
4
- import { AgentFace, CallToken, Channel, Conversation, ConversationDetail, ConversationFile, FileAt, FilePreview, StoredFile, WidgetConfig } from "./types.js";
4
+ import { AgentFace, CallToken, Channel, Conversation, ConversationDetail, ConversationFile, FileAt, FilePreview, StoredFile, ViewRendered, WidgetConfig } from "./types.js";
5
5
  //#region src/client.d.ts
6
6
  /**
7
7
  * What the API answered when it was not a success: its status, its sentence (`detail`, written
@@ -100,6 +100,14 @@ export interface Client {
100
100
  /** One run of an automation with its record: every call it made, step by step. */
101
101
  get: (id: string) => Promise<RunRecord>;
102
102
  };
103
+ /** What an agent keeps on show for each person it serves (`compose_view`, `/agents/{id}/views`). */
104
+ views: {
105
+ /**
106
+ * A view rendered from the rows now: for a person, their own; for the business, for the
107
+ * contact it names (`contact`, their id).
108
+ */
109
+ render: (name: string, contact?: string) => Promise<ViewRendered>;
110
+ };
103
111
  /**
104
112
  * The tasks an agent runs for contacts, each with its newest runs: a person's own only (what
105
113
  * they asked the agent for in their conversations); the business's, every one.
package/dist/client.js CHANGED
@@ -175,6 +175,7 @@ function createClient(options) {
175
175
  get: (agentId, name) => json("GET", `/agents/${seg(agentId)}/automations/${seg(name)}`)
176
176
  },
177
177
  runs: { get: (id) => json("GET", `/runs/${seg(id)}`) },
178
+ views: { render: (name, contact) => json("GET", `/views/${seg(name)}/render${contact === void 0 ? "" : `?contact=${seg(contact)}`}`) },
178
179
  tasks: {
179
180
  list: (agentId) => json("GET", `/agents/${seg(agentId)}/tasks`),
180
181
  switch: (agentId, taskId, on) => json("PATCH", `/agents/${seg(agentId)}/tasks/${seg(taskId)}`, body({ on })),
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type { AboutOut, MemoryFact } from \"./about\";\nimport type { Automation, AutomationView, RunRecord, Task } from \"./automation/types\";\nimport { signerOf, type Credentials, type Speaker } from \"./credentials\";\nimport type {\n AgentFace,\n WidgetConfig,\n CallToken,\n Channel,\n Conversation,\n ConversationDetail,\n ConversationFile,\n FileAt,\n FileLink,\n FilePreview,\n StoredFile,\n} from \"./types\";\n\n/**\n * What the API answered when it was not a success: its status, its sentence (`detail`, written\n * to be shown) and its `code`, for a host to word in its own language.\n */\nexport class ApiError extends Error {\n readonly status: number;\n readonly detail: string;\n /** What went wrong, by name (`not_found`, `no_calls`); null when the answer named nothing. */\n readonly code: string | null;\n\n constructor(status: number, detail: string, code: string | null = null) {\n super(detail);\n this.name = \"ApiError\";\n this.status = status;\n this.detail = detail;\n this.code = code;\n }\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst isIssue = (value: unknown): value is { msg: string } =>\n isRecord(value) && typeof value.msg === \"string\";\n\n/**\n * Reads an error body: `{code, detail: \"message\"}`, or FastAPI's 422 `{detail: [{msg, …}, …]}`\n * (the first issue speaks for all). A body with neither is named by its status alone.\n */\nexport function parseError(status: number, body: unknown): ApiError {\n const detail = isRecord(body) ? body.detail : undefined;\n const code = isRecord(body) && typeof body.code === \"string\" ? body.code : null;\n if (typeof detail === \"string\" && detail !== \"\") return new ApiError(status, detail, code);\n if (Array.isArray(detail)) {\n const issue = detail.find(isIssue);\n if (issue) return new ApiError(status, issue.msg);\n }\n return new ApiError(status, `HTTP ${status}`);\n}\n\n/** A body read as JSON, or null when it is not. */\nfunction readJson(text: string): unknown {\n try {\n return JSON.parse(text) as unknown;\n } catch {\n return null;\n }\n}\n\n/**\n * Where the API is, and who calls it (`Credentials`: the cookie, a person's token, a token asked\n * of the host's backend, or a publishable key and its visitor).\n */\nexport type ClientOptions = Credentials & {\n /**\n * The API's origin and prefix: `/api` in our own app (the cookie's, on the page's origin), the\n * public `https://…/v1` from a host's page.\n */\n baseUrl: string;\n /** A host's own `fetch`, when the global one is not the right one. */\n fetch?: typeof fetch | undefined;\n};\n\n/** The files under one owner: the organization's, or an agent's own. */\nexport interface Files {\n /** Every file, by path. */\n list: () => Promise<StoredFile[]>;\n /**\n * Stores the bytes under the path (replacing a file of the same name), with the blob's type\n * as the file's; `searchable` makes it knowledge `search_files` reads (text, PDF).\n */\n put: (path: string, blob: Blob, searchable: boolean) => Promise<StoredFile>;\n delete: (path: string) => Promise<void>;\n}\n\n/** How an upload goes: how much of it has gone, and how to let it go. */\nexport interface UploadOptions {\n /** Hears the share sent so far, from 0 to 1, as it goes. */\n onProgress?: ((share: number) => void) | undefined;\n /** Aborting it stops the upload, and the promise rejects with an `AbortError`. */\n signal?: AbortSignal | undefined;\n}\n\n/** The API, as far as this package reaches it. */\nexport interface Client {\n /**\n * The widget's agent and look, by the publishable key it speaks with (a web channel's key).\n */\n widget: () => Promise<WidgetConfig>;\n /**\n * Who the page speaks for, by its credentials: the business (our own app's cookie) or a person\n * it serves (a token, a publishable key's visitor). What waits for the business's yes is\n * answered by the business alone.\n */\n who: Speaker;\n agents: {\n /** What a person may know of the agent: its name, role, mark, and whether it takes calls. */\n face: (id: string) => Promise<AgentFace>;\n /** What the agent says about itself (null until it has written it) and the facts it rests on. */\n about: (id: string) => Promise<AboutOut>;\n /** Has the agent write about itself anew (a model call, a few seconds); answers like `about`. */\n describe: (id: string) => Promise<AboutOut>;\n /** The agent's own files, by path. */\n files: (id: string) => Promise<StoredFile[]>;\n /** Stores a file of the agent's own; see `Files.put`. */\n putFile: (id: string, path: string, blob: Blob, searchable: boolean) => Promise<StoredFile>;\n deleteFile: (id: string, path: string) => Promise<void>;\n /** What the agent remembered, newest first. */\n memories: (id: string) => Promise<MemoryFact[]>;\n /** Takes one memory from it. */\n forget: (id: string, memoryId: string) => Promise<void>;\n /**\n * The owner's call with the agent, from our own app (v3/VOICE.md): the call and its\n * conversation (a test) made now, and where to join it (`VoiceCall`'s `connect`). App only:\n * it is `/api`'s, signed in by the owner's cookie, and 404s on `/v1`; a host's page calls with\n * `conversations.call(agentId)`.\n */\n call: (id: string) => Promise<CallToken>;\n };\n /**\n * The organization's files, shared by every agent; and the preview of any file the caller may\n * read, the organization's, an agent's own or a conversation's (`preview`).\n */\n files: Files & {\n /** What a viewer draws of the file, by its `kind` (v3/FILES.md, Preview). */\n preview: (at: FileAt) => Promise<FilePreview>;\n };\n /** What an agent does on its own (v3/AUTOMATIONS.md). */\n automations: {\n /** The agent's automations, each as it stands now with its newest run. */\n list: (agentId: string) => Promise<Automation[]>;\n /** One of them by its name, with its newest runs. */\n get: (agentId: string, name: string) => Promise<AutomationView>;\n };\n runs: {\n /** One run of an automation with its record: every call it made, step by step. */\n get: (id: string) => Promise<RunRecord>;\n };\n /**\n * The tasks an agent runs for contacts, each with its newest runs: a person's own only (what\n * they asked the agent for in their conversations); the business's, every one.\n */\n tasks: {\n list: (agentId: string) => Promise<Task[]>;\n /** Pauses it, or turns it on again; answers it as it stands. */\n switch: (agentId: string, taskId: string, on: boolean) => Promise<Task>;\n remove: (agentId: string, taskId: string) => Promise<void>;\n };\n /** Where people write to the organization's agents (v3/CHANNELS.md). */\n channels: {\n /** The organization's channels, or one agent's: the disconnected ones too. */\n list: (agentId?: string) => Promise<Channel[]>;\n };\n conversations: {\n /** One conversation with its messages. */\n get: (id: string) => Promise<ConversationDetail>;\n /** The agent's conversations, newest first: a person's own only, each with what is unread. */\n list: (agentId: string) => Promise<Conversation[]>;\n /** A new conversation with an agent, titled when given. */\n start: (agentId: string, options?: { title?: string | undefined }) => Promise<Conversation>;\n /** How far the person has read it: all of it, now. */\n read: (id: string) => Promise<Conversation>;\n /** Its title, as the person gives it. */\n rename: (id: string, title: string) => Promise<Conversation>;\n /** The files that arrived in a conversation: attached, sent through a form. */\n files: (id: string) => Promise<ConversationFile[]>;\n /**\n * Stores a file in the conversation as the person picks it, to say it or send it through a\n * form by its id: its bytes as they are, under its type and name.\n */\n upload: (id: string, file: File, options?: UploadOptions) => Promise<ConversationFile>;\n /**\n * Where a picture, a video or a sound of the conversation plays from in the page's own\n * player: an address that needs no credential, for an hour, streamed as the player seeks.\n */\n play: (id: string, fileId: string) => Promise<string>;\n /**\n * A call with the agent from the browser, for whoever may talk to it (a person, as theirs):\n * the call and its own conversation made now, where what is said lands, and where to join it\n * (`VoiceCall`'s `connect`).\n */\n call: (agentId: string) => Promise<CallToken>;\n };\n /**\n * The conversation's socket address, signed in: `ws:` beside `http:`, `wss:` beside `https:`,\n * with the token (and the visitor) in its query, since a browser's socket takes no header.\n * `fresh` asks the host's backend for a new token first (`getToken`), for a socket that could\n * not open with the last one.\n */\n liveUrl: (conversationId: string, options?: { fresh?: boolean | undefined }) => Promise<string>;\n}\n\nconst seg = encodeURIComponent;\n\n/** A request's JSON body. */\nconst body = (value: unknown): Blob =>\n new Blob([JSON.stringify(value)], { type: \"application/json\" });\n\n/** A file's path in a URL: each name of it encoded, the slashes between them kept. */\nconst filePath = (path: string): string => path.split(\"/\").map(seg).join(\"/\");\n\n/** Where a file's preview is read: under its conversation, its agent or the organization. */\nconst previewPath = (at: FileAt): string => {\n if (\"conversationId\" in at) {\n return `/conversations/${seg(at.conversationId)}/files/${seg(at.fileId)}/preview`;\n }\n const owner = \"agentId\" in at ? `/agents/${seg(at.agentId)}` : \"\";\n return `${owner}/files/${filePath(at.path)}/preview`;\n};\n\ntype Method = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\n/**\n * The API under `baseUrl`: JSON in and out, an `ApiError` for anything but a success, signed in\n * the one way `options` gives. A token asked of the host's backend (`getToken`) that the API\n * refuses (401: it expired) is asked for once more, and the request sent again with it.\n */\nexport function createClient(options: ClientOptions): Client {\n const { baseUrl, fetch: call = fetch } = options;\n const signer = signerOf(options);\n\n /** One request with the cookie or the credential's headers; a body goes as it is, typed. */\n async function attempt(method: Method, path: string, body: Blob | undefined) {\n const signed = await signer.sign();\n const response = await call(`${baseUrl}${path}`, {\n method,\n credentials: signer.credentials,\n headers:\n body === undefined ? signed.headers : { ...signed.headers, \"Content-Type\": body.type },\n ...(body === undefined ? {} : { body }),\n });\n return { response, signed };\n }\n\n async function send(method: Method, path: string, body?: Blob): Promise<Response> {\n let { response, signed } = await attempt(method, path, body);\n if (response.status === 401 && signed.renew !== null) {\n signed.renew();\n ({ response } = await attempt(method, path, body));\n }\n if (!response.ok) {\n const answer: unknown = await response.json().catch(() => null);\n throw parseError(response.status, answer);\n }\n return response;\n }\n\n async function json<T>(method: Method, path: string, body?: Blob): Promise<T> {\n return (await (await send(method, path, body)).json()) as T;\n }\n\n /** A request whose answer has no body (204). */\n async function run(method: Method, path: string): Promise<void> {\n await send(method, path);\n }\n\n /** The four of `Files` under a prefix: `/files`, or `/agents/{id}/files`. */\n const filesUnder = (prefix: string): Files => ({\n list: () => json(\"GET\", prefix),\n put: (path, blob, searchable) =>\n json(\"PUT\", `${prefix}/${filePath(path)}?searchable=${searchable}`, blob),\n delete: (path) => run(\"DELETE\", `${prefix}/${filePath(path)}`),\n });\n\n const own = (id: string) => filesUnder(`/agents/${seg(id)}/files`);\n\n /**\n * A file sent by XMLHttpRequest, the one way a browser tells how much of a body has gone; the\n * cookie goes as with `fetch` on the same origin, any other credential as its headers. Refused\n * with a token asked of the host's backend, it is sent once more with a new one.\n */\n async function sendFile(\n path: string,\n file: File,\n options: UploadOptions = {},\n ): Promise<ConversationFile> {\n const signed = await signer.sign();\n try {\n return await post(path, file, signed.headers, options);\n } catch (error) {\n const expired = error instanceof ApiError && error.status === 401;\n if (!expired || signed.renew === null) throw error;\n signed.renew();\n return post(path, file, (await signer.sign()).headers, options);\n }\n }\n\n function post(\n path: string,\n file: File,\n headers: Readonly<Record<string, string>>,\n { onProgress, signal }: UploadOptions,\n ): Promise<ConversationFile> {\n return new Promise((resolve, reject) => {\n const aborted = () => new DOMException(\"The upload was aborted\", \"AbortError\");\n if (signal?.aborted) {\n reject(aborted());\n return;\n }\n const request = new XMLHttpRequest();\n const abort = () => {\n request.abort();\n };\n request.open(\"POST\", `${baseUrl}${path}`);\n for (const [name, value] of Object.entries(headers)) request.setRequestHeader(name, value);\n request.setRequestHeader(\"Content-Type\", file.type || \"application/octet-stream\");\n request.setRequestHeader(\"X-File-Name\", encodeURIComponent(file.name));\n request.upload.addEventListener(\"progress\", (event) => {\n if (event.lengthComputable && event.total > 0) onProgress?.(event.loaded / event.total);\n });\n request.addEventListener(\"loadend\", () => {\n signal?.removeEventListener(\"abort\", abort);\n });\n request.addEventListener(\"load\", () => {\n const body = readJson(request.responseText);\n if (request.status >= 200 && request.status < 300) resolve(body as ConversationFile);\n else reject(parseError(request.status, body));\n });\n request.addEventListener(\"error\", () => {\n reject(new ApiError(0, \"The upload could not reach the server\"));\n });\n request.addEventListener(\"abort\", () => {\n reject(aborted());\n });\n signal?.addEventListener(\"abort\", abort, { once: true });\n request.send(file);\n });\n }\n\n return {\n widget: () => json(\"GET\", \"/channels/web/widget\"),\n who: signer.who,\n agents: {\n face: (id) => json(\"GET\", `/agents/${seg(id)}/face`),\n about: (id) => json(\"GET\", `/agents/${seg(id)}/about`),\n describe: (id) => json(\"POST\", `/agents/${seg(id)}/describe`),\n files: (id) => own(id).list(),\n putFile: (id, path, blob, searchable) => own(id).put(path, blob, searchable),\n deleteFile: (id, path) => own(id).delete(path),\n memories: (id) => json(\"GET\", `/agents/${seg(id)}/memories`),\n forget: (id, memoryId) => run(\"DELETE\", `/agents/${seg(id)}/memories/${seg(memoryId)}`),\n call: (id) => json(\"POST\", `/agents/${seg(id)}/voice/token`),\n },\n files: { ...filesUnder(\"/files\"), preview: (at) => json(\"GET\", previewPath(at)) },\n automations: {\n list: (agentId) => json(\"GET\", `/agents/${seg(agentId)}/automations`),\n get: (agentId, name) => json(\"GET\", `/agents/${seg(agentId)}/automations/${seg(name)}`),\n },\n runs: {\n get: (id) => json(\"GET\", `/runs/${seg(id)}`),\n },\n tasks: {\n list: (agentId) => json(\"GET\", `/agents/${seg(agentId)}/tasks`),\n switch: (agentId, taskId, on) =>\n json(\"PATCH\", `/agents/${seg(agentId)}/tasks/${seg(taskId)}`, body({ on })),\n remove: (agentId, taskId) => run(\"DELETE\", `/agents/${seg(agentId)}/tasks/${seg(taskId)}`),\n },\n channels: {\n list: (agentId) =>\n json(\"GET\", agentId === undefined ? \"/channels\" : `/channels?agent=${seg(agentId)}`),\n },\n conversations: {\n get: (id) => json(\"GET\", `/conversations/${seg(id)}`),\n list: (agentId) => json(\"GET\", `/agents/${seg(agentId)}/conversations`),\n start: (agentId, { title } = {}) =>\n json(\n \"POST\",\n `/agents/${seg(agentId)}/conversations`,\n title === undefined ? undefined : body({ title }),\n ),\n read: (id) => json(\"POST\", `/conversations/${seg(id)}/read`, body({})),\n rename: (id, title) => json(\"PATCH\", `/conversations/${seg(id)}`, body({ title })),\n files: (id) => json(\"GET\", `/conversations/${seg(id)}/files`),\n upload: (id, file, options) => sendFile(`/conversations/${seg(id)}/files`, file, options),\n play: async (id, fileId) => {\n const link = await json<FileLink>(\n \"GET\",\n `/conversations/${seg(id)}/files/${seg(fileId)}/link`,\n );\n return new URL(`${baseUrl}${link.path}`, window.location.href).href;\n },\n call: (agentId) => json(\"POST\", `/agents/${seg(agentId)}/calls`),\n },\n liveUrl: async (conversationId, { fresh = false } = {}) => {\n const url = new URL(\n `${baseUrl}/conversations/${seg(conversationId)}/live`,\n window.location.href,\n );\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n for (const [name, value] of Object.entries(await signer.query(fresh))) {\n url.searchParams.set(name, value);\n }\n return url.href;\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type { AboutOut, MemoryFact } from \"./about\";\nimport type { Automation, AutomationView, RunRecord, Task } from \"./automation/types\";\nimport { signerOf, type Credentials, type Speaker } from \"./credentials\";\nimport type {\n AgentFace,\n WidgetConfig,\n CallToken,\n Channel,\n Conversation,\n ConversationDetail,\n ConversationFile,\n FileAt,\n FileLink,\n FilePreview,\n StoredFile,\n ViewRendered,\n} from \"./types\";\n\n/**\n * What the API answered when it was not a success: its status, its sentence (`detail`, written\n * to be shown) and its `code`, for a host to word in its own language.\n */\nexport class ApiError extends Error {\n readonly status: number;\n readonly detail: string;\n /** What went wrong, by name (`not_found`, `no_calls`); null when the answer named nothing. */\n readonly code: string | null;\n\n constructor(status: number, detail: string, code: string | null = null) {\n super(detail);\n this.name = \"ApiError\";\n this.status = status;\n this.detail = detail;\n this.code = code;\n }\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst isIssue = (value: unknown): value is { msg: string } =>\n isRecord(value) && typeof value.msg === \"string\";\n\n/**\n * Reads an error body: `{code, detail: \"message\"}`, or FastAPI's 422 `{detail: [{msg, …}, …]}`\n * (the first issue speaks for all). A body with neither is named by its status alone.\n */\nexport function parseError(status: number, body: unknown): ApiError {\n const detail = isRecord(body) ? body.detail : undefined;\n const code = isRecord(body) && typeof body.code === \"string\" ? body.code : null;\n if (typeof detail === \"string\" && detail !== \"\") return new ApiError(status, detail, code);\n if (Array.isArray(detail)) {\n const issue = detail.find(isIssue);\n if (issue) return new ApiError(status, issue.msg);\n }\n return new ApiError(status, `HTTP ${status}`);\n}\n\n/** A body read as JSON, or null when it is not. */\nfunction readJson(text: string): unknown {\n try {\n return JSON.parse(text) as unknown;\n } catch {\n return null;\n }\n}\n\n/**\n * Where the API is, and who calls it (`Credentials`: the cookie, a person's token, a token asked\n * of the host's backend, or a publishable key and its visitor).\n */\nexport type ClientOptions = Credentials & {\n /**\n * The API's origin and prefix: `/api` in our own app (the cookie's, on the page's origin), the\n * public `https://…/v1` from a host's page.\n */\n baseUrl: string;\n /** A host's own `fetch`, when the global one is not the right one. */\n fetch?: typeof fetch | undefined;\n};\n\n/** The files under one owner: the organization's, or an agent's own. */\nexport interface Files {\n /** Every file, by path. */\n list: () => Promise<StoredFile[]>;\n /**\n * Stores the bytes under the path (replacing a file of the same name), with the blob's type\n * as the file's; `searchable` makes it knowledge `search_files` reads (text, PDF).\n */\n put: (path: string, blob: Blob, searchable: boolean) => Promise<StoredFile>;\n delete: (path: string) => Promise<void>;\n}\n\n/** How an upload goes: how much of it has gone, and how to let it go. */\nexport interface UploadOptions {\n /** Hears the share sent so far, from 0 to 1, as it goes. */\n onProgress?: ((share: number) => void) | undefined;\n /** Aborting it stops the upload, and the promise rejects with an `AbortError`. */\n signal?: AbortSignal | undefined;\n}\n\n/** The API, as far as this package reaches it. */\nexport interface Client {\n /**\n * The widget's agent and look, by the publishable key it speaks with (a web channel's key).\n */\n widget: () => Promise<WidgetConfig>;\n /**\n * Who the page speaks for, by its credentials: the business (our own app's cookie) or a person\n * it serves (a token, a publishable key's visitor). What waits for the business's yes is\n * answered by the business alone.\n */\n who: Speaker;\n agents: {\n /** What a person may know of the agent: its name, role, mark, and whether it takes calls. */\n face: (id: string) => Promise<AgentFace>;\n /** What the agent says about itself (null until it has written it) and the facts it rests on. */\n about: (id: string) => Promise<AboutOut>;\n /** Has the agent write about itself anew (a model call, a few seconds); answers like `about`. */\n describe: (id: string) => Promise<AboutOut>;\n /** The agent's own files, by path. */\n files: (id: string) => Promise<StoredFile[]>;\n /** Stores a file of the agent's own; see `Files.put`. */\n putFile: (id: string, path: string, blob: Blob, searchable: boolean) => Promise<StoredFile>;\n deleteFile: (id: string, path: string) => Promise<void>;\n /** What the agent remembered, newest first. */\n memories: (id: string) => Promise<MemoryFact[]>;\n /** Takes one memory from it. */\n forget: (id: string, memoryId: string) => Promise<void>;\n /**\n * The owner's call with the agent, from our own app (v3/VOICE.md): the call and its\n * conversation (a test) made now, and where to join it (`VoiceCall`'s `connect`). App only:\n * it is `/api`'s, signed in by the owner's cookie, and 404s on `/v1`; a host's page calls with\n * `conversations.call(agentId)`.\n */\n call: (id: string) => Promise<CallToken>;\n };\n /**\n * The organization's files, shared by every agent; and the preview of any file the caller may\n * read, the organization's, an agent's own or a conversation's (`preview`).\n */\n files: Files & {\n /** What a viewer draws of the file, by its `kind` (v3/FILES.md, Preview). */\n preview: (at: FileAt) => Promise<FilePreview>;\n };\n /** What an agent does on its own (v3/AUTOMATIONS.md). */\n automations: {\n /** The agent's automations, each as it stands now with its newest run. */\n list: (agentId: string) => Promise<Automation[]>;\n /** One of them by its name, with its newest runs. */\n get: (agentId: string, name: string) => Promise<AutomationView>;\n };\n runs: {\n /** One run of an automation with its record: every call it made, step by step. */\n get: (id: string) => Promise<RunRecord>;\n };\n /** What an agent keeps on show for each person it serves (`compose_view`, `/agents/{id}/views`). */\n views: {\n /**\n * A view rendered from the rows now: for a person, their own; for the business, for the\n * contact it names (`contact`, their id).\n */\n render: (name: string, contact?: string) => Promise<ViewRendered>;\n };\n /**\n * The tasks an agent runs for contacts, each with its newest runs: a person's own only (what\n * they asked the agent for in their conversations); the business's, every one.\n */\n tasks: {\n list: (agentId: string) => Promise<Task[]>;\n /** Pauses it, or turns it on again; answers it as it stands. */\n switch: (agentId: string, taskId: string, on: boolean) => Promise<Task>;\n remove: (agentId: string, taskId: string) => Promise<void>;\n };\n /** Where people write to the organization's agents (v3/CHANNELS.md). */\n channels: {\n /** The organization's channels, or one agent's: the disconnected ones too. */\n list: (agentId?: string) => Promise<Channel[]>;\n };\n conversations: {\n /** One conversation with its messages. */\n get: (id: string) => Promise<ConversationDetail>;\n /** The agent's conversations, newest first: a person's own only, each with what is unread. */\n list: (agentId: string) => Promise<Conversation[]>;\n /** A new conversation with an agent, titled when given. */\n start: (agentId: string, options?: { title?: string | undefined }) => Promise<Conversation>;\n /** How far the person has read it: all of it, now. */\n read: (id: string) => Promise<Conversation>;\n /** Its title, as the person gives it. */\n rename: (id: string, title: string) => Promise<Conversation>;\n /** The files that arrived in a conversation: attached, sent through a form. */\n files: (id: string) => Promise<ConversationFile[]>;\n /**\n * Stores a file in the conversation as the person picks it, to say it or send it through a\n * form by its id: its bytes as they are, under its type and name.\n */\n upload: (id: string, file: File, options?: UploadOptions) => Promise<ConversationFile>;\n /**\n * Where a picture, a video or a sound of the conversation plays from in the page's own\n * player: an address that needs no credential, for an hour, streamed as the player seeks.\n */\n play: (id: string, fileId: string) => Promise<string>;\n /**\n * A call with the agent from the browser, for whoever may talk to it (a person, as theirs):\n * the call and its own conversation made now, where what is said lands, and where to join it\n * (`VoiceCall`'s `connect`).\n */\n call: (agentId: string) => Promise<CallToken>;\n };\n /**\n * The conversation's socket address, signed in: `ws:` beside `http:`, `wss:` beside `https:`,\n * with the token (and the visitor) in its query, since a browser's socket takes no header.\n * `fresh` asks the host's backend for a new token first (`getToken`), for a socket that could\n * not open with the last one.\n */\n liveUrl: (conversationId: string, options?: { fresh?: boolean | undefined }) => Promise<string>;\n}\n\nconst seg = encodeURIComponent;\n\n/** A request's JSON body. */\nconst body = (value: unknown): Blob =>\n new Blob([JSON.stringify(value)], { type: \"application/json\" });\n\n/** A file's path in a URL: each name of it encoded, the slashes between them kept. */\nconst filePath = (path: string): string => path.split(\"/\").map(seg).join(\"/\");\n\n/** Where a file's preview is read: under its conversation, its agent or the organization. */\nconst previewPath = (at: FileAt): string => {\n if (\"conversationId\" in at) {\n return `/conversations/${seg(at.conversationId)}/files/${seg(at.fileId)}/preview`;\n }\n const owner = \"agentId\" in at ? `/agents/${seg(at.agentId)}` : \"\";\n return `${owner}/files/${filePath(at.path)}/preview`;\n};\n\ntype Method = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\n/**\n * The API under `baseUrl`: JSON in and out, an `ApiError` for anything but a success, signed in\n * the one way `options` gives. A token asked of the host's backend (`getToken`) that the API\n * refuses (401: it expired) is asked for once more, and the request sent again with it.\n */\nexport function createClient(options: ClientOptions): Client {\n const { baseUrl, fetch: call = fetch } = options;\n const signer = signerOf(options);\n\n /** One request with the cookie or the credential's headers; a body goes as it is, typed. */\n async function attempt(method: Method, path: string, body: Blob | undefined) {\n const signed = await signer.sign();\n const response = await call(`${baseUrl}${path}`, {\n method,\n credentials: signer.credentials,\n headers:\n body === undefined ? signed.headers : { ...signed.headers, \"Content-Type\": body.type },\n ...(body === undefined ? {} : { body }),\n });\n return { response, signed };\n }\n\n async function send(method: Method, path: string, body?: Blob): Promise<Response> {\n let { response, signed } = await attempt(method, path, body);\n if (response.status === 401 && signed.renew !== null) {\n signed.renew();\n ({ response } = await attempt(method, path, body));\n }\n if (!response.ok) {\n const answer: unknown = await response.json().catch(() => null);\n throw parseError(response.status, answer);\n }\n return response;\n }\n\n async function json<T>(method: Method, path: string, body?: Blob): Promise<T> {\n return (await (await send(method, path, body)).json()) as T;\n }\n\n /** A request whose answer has no body (204). */\n async function run(method: Method, path: string): Promise<void> {\n await send(method, path);\n }\n\n /** The four of `Files` under a prefix: `/files`, or `/agents/{id}/files`. */\n const filesUnder = (prefix: string): Files => ({\n list: () => json(\"GET\", prefix),\n put: (path, blob, searchable) =>\n json(\"PUT\", `${prefix}/${filePath(path)}?searchable=${searchable}`, blob),\n delete: (path) => run(\"DELETE\", `${prefix}/${filePath(path)}`),\n });\n\n const own = (id: string) => filesUnder(`/agents/${seg(id)}/files`);\n\n /**\n * A file sent by XMLHttpRequest, the one way a browser tells how much of a body has gone; the\n * cookie goes as with `fetch` on the same origin, any other credential as its headers. Refused\n * with a token asked of the host's backend, it is sent once more with a new one.\n */\n async function sendFile(\n path: string,\n file: File,\n options: UploadOptions = {},\n ): Promise<ConversationFile> {\n const signed = await signer.sign();\n try {\n return await post(path, file, signed.headers, options);\n } catch (error) {\n const expired = error instanceof ApiError && error.status === 401;\n if (!expired || signed.renew === null) throw error;\n signed.renew();\n return post(path, file, (await signer.sign()).headers, options);\n }\n }\n\n function post(\n path: string,\n file: File,\n headers: Readonly<Record<string, string>>,\n { onProgress, signal }: UploadOptions,\n ): Promise<ConversationFile> {\n return new Promise((resolve, reject) => {\n const aborted = () => new DOMException(\"The upload was aborted\", \"AbortError\");\n if (signal?.aborted) {\n reject(aborted());\n return;\n }\n const request = new XMLHttpRequest();\n const abort = () => {\n request.abort();\n };\n request.open(\"POST\", `${baseUrl}${path}`);\n for (const [name, value] of Object.entries(headers)) request.setRequestHeader(name, value);\n request.setRequestHeader(\"Content-Type\", file.type || \"application/octet-stream\");\n request.setRequestHeader(\"X-File-Name\", encodeURIComponent(file.name));\n request.upload.addEventListener(\"progress\", (event) => {\n if (event.lengthComputable && event.total > 0) onProgress?.(event.loaded / event.total);\n });\n request.addEventListener(\"loadend\", () => {\n signal?.removeEventListener(\"abort\", abort);\n });\n request.addEventListener(\"load\", () => {\n const body = readJson(request.responseText);\n if (request.status >= 200 && request.status < 300) resolve(body as ConversationFile);\n else reject(parseError(request.status, body));\n });\n request.addEventListener(\"error\", () => {\n reject(new ApiError(0, \"The upload could not reach the server\"));\n });\n request.addEventListener(\"abort\", () => {\n reject(aborted());\n });\n signal?.addEventListener(\"abort\", abort, { once: true });\n request.send(file);\n });\n }\n\n return {\n widget: () => json(\"GET\", \"/channels/web/widget\"),\n who: signer.who,\n agents: {\n face: (id) => json(\"GET\", `/agents/${seg(id)}/face`),\n about: (id) => json(\"GET\", `/agents/${seg(id)}/about`),\n describe: (id) => json(\"POST\", `/agents/${seg(id)}/describe`),\n files: (id) => own(id).list(),\n putFile: (id, path, blob, searchable) => own(id).put(path, blob, searchable),\n deleteFile: (id, path) => own(id).delete(path),\n memories: (id) => json(\"GET\", `/agents/${seg(id)}/memories`),\n forget: (id, memoryId) => run(\"DELETE\", `/agents/${seg(id)}/memories/${seg(memoryId)}`),\n call: (id) => json(\"POST\", `/agents/${seg(id)}/voice/token`),\n },\n files: { ...filesUnder(\"/files\"), preview: (at) => json(\"GET\", previewPath(at)) },\n automations: {\n list: (agentId) => json(\"GET\", `/agents/${seg(agentId)}/automations`),\n get: (agentId, name) => json(\"GET\", `/agents/${seg(agentId)}/automations/${seg(name)}`),\n },\n runs: {\n get: (id) => json(\"GET\", `/runs/${seg(id)}`),\n },\n views: {\n render: (name, contact) =>\n json(\n \"GET\",\n `/views/${seg(name)}/render${contact === undefined ? \"\" : `?contact=${seg(contact)}`}`,\n ),\n },\n tasks: {\n list: (agentId) => json(\"GET\", `/agents/${seg(agentId)}/tasks`),\n switch: (agentId, taskId, on) =>\n json(\"PATCH\", `/agents/${seg(agentId)}/tasks/${seg(taskId)}`, body({ on })),\n remove: (agentId, taskId) => run(\"DELETE\", `/agents/${seg(agentId)}/tasks/${seg(taskId)}`),\n },\n channels: {\n list: (agentId) =>\n json(\"GET\", agentId === undefined ? \"/channels\" : `/channels?agent=${seg(agentId)}`),\n },\n conversations: {\n get: (id) => json(\"GET\", `/conversations/${seg(id)}`),\n list: (agentId) => json(\"GET\", `/agents/${seg(agentId)}/conversations`),\n start: (agentId, { title } = {}) =>\n json(\n \"POST\",\n `/agents/${seg(agentId)}/conversations`,\n title === undefined ? undefined : body({ title }),\n ),\n read: (id) => json(\"POST\", `/conversations/${seg(id)}/read`, body({})),\n rename: (id, title) => json(\"PATCH\", `/conversations/${seg(id)}`, body({ title })),\n files: (id) => json(\"GET\", `/conversations/${seg(id)}/files`),\n upload: (id, file, options) => sendFile(`/conversations/${seg(id)}/files`, file, options),\n play: async (id, fileId) => {\n const link = await json<FileLink>(\n \"GET\",\n `/conversations/${seg(id)}/files/${seg(fileId)}/link`,\n );\n return new URL(`${baseUrl}${link.path}`, window.location.href).href;\n },\n call: (agentId) => json(\"POST\", `/agents/${seg(agentId)}/calls`),\n },\n liveUrl: async (conversationId, { fresh = false } = {}) => {\n const url = new URL(\n `${baseUrl}/conversations/${seg(conversationId)}/live`,\n window.location.href,\n );\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n for (const [name, value] of Object.entries(await signer.query(fresh))) {\n url.searchParams.set(name, value);\n }\n return url.href;\n },\n };\n}\n"],"mappings":"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"}
package/dist/es.js CHANGED
@@ -191,6 +191,16 @@ const KIT_MESSAGES_ES = {
191
191
  connected: "Conectada",
192
192
  failed: "No se ha podido conectar",
193
193
  preparing: "Preparando la conexión"
194
+ },
195
+ calendar: {
196
+ allDay: "Todo el día",
197
+ empty: "Nada previsto"
198
+ },
199
+ board: {
200
+ move: (card) => `Mover ${card}`,
201
+ moveTo: "Mover a",
202
+ moved: (card, column) => `${card} a ${column}`,
203
+ empty: "Nada aquí"
194
204
  }
195
205
  };
196
206
  const BROWSER_MESSAGES_ES = {
@@ -277,6 +287,8 @@ const CHAT_MESSAGES_ES = {
277
287
  declineAll: "Rechazar todo",
278
288
  confirming: "Esperando la confirmación del negocio",
279
289
  handedOff: "Pasada a una persona",
290
+ opened: (name, title) => `${name} abrió ${title}`,
291
+ closePlaced: "Cerrar",
280
292
  handover: (holder, by, name) => {
281
293
  const automation = by.kind === "automation" ? ` por «${by.title}»` : "";
282
294
  switch (holder) {