@signalcore/ui 1.0.0-beta.1 → 1.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/dist/AgentAbout.js +7 -4
- package/dist/AgentAbout.js.map +1 -1
- package/dist/AgentTasks.js +2 -1
- package/dist/AgentTasks.js.map +1 -1
- package/dist/VoiceCall.js +1 -1
- package/dist/VoiceCall.js.map +1 -1
- package/dist/about.d.ts +34 -144
- package/dist/about.js +6 -8
- package/dist/about.js.map +1 -1
- package/dist/automation/cron.d.ts +1 -1
- package/dist/automation/cron.js.map +1 -1
- package/dist/automation/marks.js +7 -3
- package/dist/automation/marks.js.map +1 -1
- package/dist/automation/messages.d.ts +2 -0
- package/dist/automation/messages.js +2 -0
- package/dist/automation/messages.js.map +1 -1
- package/dist/automation/types.d.ts +25 -128
- package/dist/automation/types.js +36 -0
- package/dist/automation/types.js.map +1 -0
- package/dist/automation/when.d.ts +1 -1
- package/dist/automation/when.js +5 -1
- package/dist/automation/when.js.map +1 -1
- package/dist/blocks.d.ts +23 -1
- package/dist/browser.js +2 -2
- package/dist/browser.js.map +1 -1
- package/dist/call.d.ts +1 -1
- package/dist/call.js.map +1 -1
- package/dist/contract.d.ts +262 -0
- package/dist/credentials.js.map +1 -1
- package/dist/dev.js +1 -1
- package/dist/dev.js.map +1 -1
- package/dist/es.js +9 -1
- package/dist/es.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/kit/AudioPlayer.js +1 -1
- package/dist/kit/AudioPlayer.js.map +1 -1
- package/dist/kit/Choices.js +1 -1
- package/dist/kit/Choices.js.map +1 -1
- package/dist/kit/FileField.js +1 -1
- package/dist/kit/FileField.js.map +1 -1
- package/dist/kit/Popover.js.map +1 -1
- package/dist/kit/Tile.d.ts +1 -1
- package/dist/kit/Tile.js +1 -1
- package/dist/kit/Tile.js.map +1 -1
- package/dist/kit/messages.d.ts +13 -0
- package/dist/kit/messages.js +7 -1
- package/dist/kit/messages.js.map +1 -1
- package/dist/kit/uploads.d.ts +2 -1
- package/dist/kit/uploads.js +1 -1
- package/dist/kit/uploads.js.map +1 -1
- package/dist/kit/waveform.js.map +1 -1
- package/dist/markdown.js.map +1 -1
- package/dist/marks.js +2 -3
- package/dist/marks.js.map +1 -1
- package/dist/surface/Markdown.js +1 -1
- package/dist/surface/Markdown.js.map +1 -1
- package/dist/surface/Surface.js +43 -4
- package/dist/surface/Surface.js.map +1 -1
- package/dist/surface/scope.d.ts +1 -1
- package/dist/surface/scope.js.map +1 -1
- package/dist/surface/values.js +1 -1
- package/dist/surface/values.js.map +1 -1
- package/dist/tint.d.ts +1 -1
- package/dist/tint.js +1 -1
- package/dist/tint.js.map +1 -1
- package/dist/usePolled.js +1 -1
- package/dist/usePolled.js.map +1 -1
- package/dist/voice/webgl.js +1 -1
- package/dist/voice/webgl.js.map +1 -1
- package/dist/widget.js +14 -14
- package/package.json +1 -1
- package/src/AgentAbout.tsx +10 -3
- package/src/AgentTasks.tsx +2 -2
- package/src/VoiceCall.tsx +1 -1
- package/src/about.ts +42 -144
- package/src/automation/cron.ts +1 -1
- package/src/automation/marks.tsx +6 -2
- package/src/automation/messages.ts +4 -0
- package/src/automation/types.ts +58 -118
- package/src/automation/when.ts +3 -1
- package/src/blocks.ts +25 -0
- package/src/browser.ts +4 -4
- package/src/call.ts +1 -1
- package/src/contract.ts +1720 -0
- package/src/credentials.ts +2 -2
- package/src/dev.ts +1 -1
- package/src/es.ts +8 -0
- package/src/index.ts +1 -0
- package/src/kit/AudioPlayer.tsx +1 -1
- package/src/kit/Choices.tsx +1 -1
- package/src/kit/FileField.tsx +1 -1
- package/src/kit/Popover.tsx +1 -1
- package/src/kit/Tile.tsx +1 -1
- package/src/kit/messages.ts +20 -0
- package/src/kit/uploads.ts +2 -2
- package/src/kit/waveform.ts +1 -1
- package/src/markdown.ts +1 -1
- package/src/marks.tsx +3 -123
- package/src/styles.css +15 -0
- package/src/surface/Markdown.tsx +1 -1
- package/src/surface/Surface.tsx +53 -4
- package/src/surface/scope.ts +3 -1
- package/src/surface/values.ts +5 -5
- package/src/tint.ts +1 -1
- package/src/usePolled.ts +2 -2
- package/src/voice/webgl.ts +1 -1
package/README.md
CHANGED
|
@@ -696,7 +696,14 @@ it is instead (`part="media"`, `sc-surface__media`), in the browser's own player
|
|
|
696
696
|
address the host gives (`files.play`; the conversation's asks `client.conversations.play`, a link
|
|
697
697
|
the API signs for an hour that needs no credential and answers `Range`); a picture pressed opens
|
|
698
698
|
in the viewer, a player whose address ran out asks for a new one once, and a host that can't play
|
|
699
|
-
it keeps the row. Until the API has filled in `file` it is only named, quietly.
|
|
699
|
+
it keeps the row. Until the API has filled in `file` it is only named, quietly. A `connect`
|
|
700
|
+
block (`ConnectBlock`) is an app for the person to connect, drawn by the platform alone (the
|
|
701
|
+
agent's `connect_app`, never composed): a card with the app's logo and name over the line the
|
|
702
|
+
agent wrote of what it will do with it, and, while the connection waits for them, a primary
|
|
703
|
+
"Autorizar en <app>" (`messages.connect.authorize`) that opens its consent page apart; once it
|
|
704
|
+
works, a check and "Conectada" in its place (`sc-surface__connect--done`), and a quiet word when
|
|
705
|
+
it failed. It changes as the connection does, from the `ui` frames the API sends again on the
|
|
706
|
+
same id; before the API has asked for it (`connection` null), only the app as the agent named it.
|
|
700
707
|
|
|
701
708
|
## AgentAbout
|
|
702
709
|
|
package/dist/AgentAbout.js
CHANGED
|
@@ -44,7 +44,8 @@ const ABOUT_MESSAGES = {
|
|
|
44
44
|
kinds: {
|
|
45
45
|
schedule: "At a time",
|
|
46
46
|
webhook: "When called",
|
|
47
|
-
event: "On an event"
|
|
47
|
+
event: "On an event",
|
|
48
|
+
platform: "On a message"
|
|
48
49
|
},
|
|
49
50
|
topics: {
|
|
50
51
|
apps: "Apps",
|
|
@@ -569,11 +570,13 @@ function Gone({ classNames, children }) {
|
|
|
569
570
|
children
|
|
570
571
|
});
|
|
571
572
|
}
|
|
572
|
-
/** An automation's mark says its kind: a time, a call from another system, an event
|
|
573
|
+
/** An automation's mark says its kind: a time, a call from another system, an event, a
|
|
574
|
+
* message. */
|
|
573
575
|
const KIND_ICONS = {
|
|
574
576
|
schedule: "clock",
|
|
575
577
|
webhook: "code",
|
|
576
|
-
event: "bell"
|
|
578
|
+
event: "bell",
|
|
579
|
+
platform: "message"
|
|
577
580
|
};
|
|
578
581
|
/** An app's mark: the host's logo when it draws one, else the provider's, bare. */
|
|
579
582
|
function appMark(app, apps, size) {
|
|
@@ -678,7 +681,7 @@ function Automations({ told, live, words, classNames, tools, controls, apps, onO
|
|
|
678
681
|
const now = standing.get(automation.name);
|
|
679
682
|
const going = now?.last_run?.status === "running";
|
|
680
683
|
return /* @__PURE__ */ jsx(Row, {
|
|
681
|
-
mark: automation.provider === void 0 ? /* @__PURE__ */ jsx(Mark, { icon: GLYPHS[KIND_ICONS[automation.kind]] }) : appMark({
|
|
684
|
+
mark: automation.provider === void 0 || automation.provider === null ? /* @__PURE__ */ jsx(Mark, { icon: GLYPHS[KIND_ICONS[automation.kind]] }) : appMark({
|
|
682
685
|
provider: automation.provider,
|
|
683
686
|
name: automation.app_title ?? toolName(automation.app ?? automation.provider),
|
|
684
687
|
icon: automation.icon
|
package/dist/AgentAbout.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentAbout.js","names":[],"sources":["../src/AgentAbout.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\n\nimport {\n BooksIcon,\n BroadcastIcon,\n KeyIcon,\n LightbulbIcon,\n PuzzlePieceIcon,\n WrenchIcon,\n type Icon as Glyph,\n} from \"@phosphor-icons/react\";\nimport {\n aboutState,\n appTitle,\n automationsOf,\n countOf,\n formatSize,\n toneOf,\n toolName,\n toolTitle,\n type AboutOut,\n type ActionFact,\n type AutomationFact,\n type AutomationKind,\n type ChannelFact,\n type ChannelKind,\n type ConnectionFact,\n type DataFact,\n type Facts,\n type FileFact,\n type Icon,\n type MemoryFact,\n type Provider,\n type StatusTone,\n type Told,\n type Topic,\n type VaultFact,\n} from \"./about\";\nimport { Automation } from \"./automation/Automation\";\nimport { RunView } from \"./automation/RunView\";\nimport type { Automation as Standing, Run } from \"./automation/types\";\nimport { agentGlyph } from \"./agentIcons\";\nimport { classes } from \"./classes\";\nimport { ApiError, type Client } from \"./client\";\nimport { baseName } from \"./files\";\nimport { GLYPHS } from \"./icons\";\nimport { KitProvider, useKit, type KitClassNames, type KitPart } from \"./kit/context\";\nimport { FileMark } from \"./kit/FileMark\";\nimport { List, Row } from \"./kit/List\";\nimport { Mark } from \"./kit/Mark\";\nimport { MarkStack } from \"./kit/MarkStack\";\nimport { Tag } from \"./kit/Tag\";\nimport { KIT_MESSAGES, type KitLocale, type KitMessages } from \"./kit/messages\";\nimport { actionMark, channelMark, deviceMark, providerMark } from \"./marks\";\n\n/** What each part of what it works with is, at a glance: bare, beside its name. */\nconst TOPIC_GLYPHS: Readonly<Record<Topic, Glyph>> = {\n apps: PuzzlePieceIcon,\n tools: WrenchIcon,\n knowledge: BooksIcon,\n learning: LightbulbIcon,\n channels: BroadcastIcon,\n keys: KeyIcon,\n};\nimport type { Channel, ChannelStatus } from \"./types\";\nimport { agentTint } from \"./tint\";\nimport { usePolled } from \"./usePolled\";\n\n/** The words Qué puede says; a host gives its own, in its language. */\nexport interface AboutMessages {\n /** While the words are read, before anything is known. */\n loading: string;\n /** While the agent writes what it does the first time: a model call, a few seconds. */\n writing: (name: string) => string;\n /** Under the words while the agent writes them again, because it changed since. */\n updating: string;\n /** The button that tries again when the words could not be read or written. */\n refresh: string;\n /** Over what it asks the person's leave for. */\n asks: string;\n /** Over the tools that come with every agent, when it has its own too. */\n builtIn: string;\n /** The words could not be read or written; the server's own sentence follows when it gave one. */\n failed: string;\n /** Over what it does on its own. */\n does: string;\n /** In their place when it does nothing on its own yet. */\n noAutomations: string;\n /** On an automation that is off. */\n paused: string;\n /** What an automation waits for, when the agent hasn't said when yet. */\n kinds: Readonly<Record<AutomationKind, string>>;\n /** Each topic's name, on its tile and over it when open. */\n topics: Readonly<Record<Topic, string>>;\n /** A topic with nothing: on its row, and as its one line when open. */\n empty: Readonly<Record<Topic, string>>;\n /** Under an open topic's name: what it is, in a line. */\n hints: Readonly<Record<Topic, string>>;\n /** Over what is the agent's own, inside what it knows and its keys. */\n theirs: (name: string) => string;\n /** Over what is the organization's, shared with every agent: said once, by this heading. */\n organization: string;\n /** Over the tables, inside what it knows. */\n tables: string;\n /** Under a file `search_files` does not read: it is only kept. */\n kept: string;\n /** Beside a tool that is off. */\n off: string;\n /** What it may do in a table, beside it. */\n can: Readonly<Record<string, string>>;\n /** Over the words, and on the way back from a topic: who this is about. */\n title: (name: string) => string;\n /** Over the topics. */\n facts: string;\n /** In place of a run that is no longer there. */\n runGone: string;\n /** What the kit's pieces say in it (an automation, a run): their words, in the host's language. */\n kit: Partial<KitMessages>;\n /** A shipped action's name in the owner's words, by its code name; the rest keep the code name. */\n actions: Readonly<Record<string, string>>;\n /** A file's size, as the host's people read it. */\n size: (bytes: number) => string;\n /** When a memory was kept, beside it. */\n ago: (when: Date) => string;\n /** A channel's name, by its kind: its row's title. */\n channelNames: Readonly<Record<ChannelKind, string>>;\n /** How a channel stands, beside its dot. */\n channelStates: Readonly<Record<ChannelStatus, string>>;\n /** The provider's word on a channel's status (English, as the API keeps it), in the host's. */\n channelDetail: (detail: string) => string;\n}\n\nexport const ABOUT_MESSAGES: AboutMessages = {\n loading: \"Loading…\",\n writing: (name) => `${name} is writing down what it does…`,\n updating: \"Updating…\",\n refresh: \"Try again\",\n asks: \"It asks you before\",\n builtIn: \"Built in\",\n failed: \"What it can do could not be read.\",\n does: \"What it does on its own\",\n noAutomations: \"Nothing on its own yet: it answers when someone writes to it.\",\n paused: \"Paused\",\n kinds: { schedule: \"At a time\", webhook: \"When called\", event: \"On an event\" },\n topics: {\n apps: \"Apps\",\n tools: \"Tools\",\n knowledge: \"Knowledge\",\n learning: \"Learning\",\n channels: \"Channels\",\n keys: \"Keys\",\n },\n empty: {\n apps: \"None yet\",\n tools: \"None on\",\n knowledge: \"Nothing yet\",\n learning: \"Nothing yet\",\n channels: \"None yet\",\n keys: \"None\",\n },\n hints: {\n apps: \"The business's apps it uses, and what it may do in each.\",\n tools: \"What it can do while it talks with someone.\",\n knowledge: \"The documents and tables it reads to answer.\",\n learning: \"What it has learned from its conversations.\",\n channels: \"Where it works: where people write to it, and the devices it uses.\",\n keys: \"Passwords and keys it uses, kept sealed.\",\n },\n theirs: (name) => `${name}'s own`,\n organization: \"The organization's\",\n tables: \"Tables\",\n kept: \"Only kept\",\n off: \"off\",\n can: { find: \"looks up\", add: \"adds\", update: \"changes\" },\n title: (name) => `About ${name}`,\n facts: \"What it works with\",\n runGone: \"This run is no longer there.\",\n kit: KIT_MESSAGES,\n actions: {},\n size: (bytes) => formatSize(bytes),\n ago: (when) => when.toLocaleDateString(),\n channelNames: {\n whatsapp: \"WhatsApp\",\n instagram: \"Instagram\",\n sms: \"SMS\",\n email: \"Email\",\n web: \"Web chat\",\n phone: \"Phone\",\n },\n channelStates: {\n active: \"Active\",\n pending: \"Pending\",\n error: \"Error\",\n disconnected: \"Disconnected\",\n },\n channelDetail: (detail) => detail,\n};\n\n/** The parts, by the class each carries; `classNames` adds a host's own under the same keys. */\nexport type AboutPart =\n | \"sc-about\"\n | \"sc-about__pane\"\n | \"sc-about__intro\"\n | \"sc-about__heading\"\n | \"sc-about__name\"\n | \"sc-about__topics\"\n | \"sc-about__topic\"\n | \"sc-about__topic-name\"\n | \"sc-about__topic-none\"\n | \"sc-about__topic-count\"\n | \"sc-about__topic-go\"\n | \"sc-about__lead\"\n | \"sc-about__updated\"\n | \"sc-about__updating\"\n | \"sc-about__section\"\n | \"sc-about__label\"\n | \"sc-about__asks\"\n | \"sc-about__menu\"\n | \"sc-about__memory\"\n | \"sc-about__back\"\n | \"sc-about__bar\"\n | \"sc-about__bar-end\"\n | \"sc-about__automation\"\n | \"sc-about__run\"\n | \"sc-about__group\"\n | \"sc-about__title\"\n | \"sc-about__subtitle\"\n | \"sc-about__file\"\n | \"sc-about__detail\"\n | \"sc-about__hint\"\n | \"sc-about__count\"\n | \"sc-about__tools\"\n | \"sc-about__dot\"\n | \"sc-about__state\"\n | \"sc-about__empty\"\n | \"sc-about__writing\";\n\n/** The panel's own parts and the kit's it is drawn with: a host styles both under one prop. */\ntype ClassNames = Partial<Record<AboutPart | KitPart, string>> | undefined;\n\n/** What a host's control is given, besides the thing it stands beside. */\nexport interface AboutTools {\n /**\n * Reads the facts again, and has the agent write its words anew when the change made them\n * stale; the open topic stays. Call it after a change.\n */\n refresh: () => Promise<void>;\n}\n\ntype Control<T> = ((thing: T, tools: AboutTools) => ReactNode) | undefined;\n\n/**\n * The host's own controls: the package only reads, a host that lets its people change things\n * draws them here. Each one is drawn at the right end of what it stands beside.\n */\nexport interface AboutControls {\n /** Over what it does on its own (a new automation). */\n newAutomation?: ((tools: AboutTools) => ReactNode) | undefined;\n /** On each automation (switch it off, delete it). */\n automation?: Control<AutomationFact>;\n /** Under its automations: what else it does on its own (the tasks it runs for contacts). */\n automationsNote?: ((tools: AboutTools) => ReactNode) | undefined;\n /** Over an open topic (add an app, upload a file, add a key). */\n topic?: Partial<Record<Topic, (tools: AboutTools) => ReactNode>> | undefined;\n app?: Control<ConnectionFact>;\n tool?: Control<ActionFact>;\n file?: Control<FileFact>;\n table?: Control<DataFact>;\n memory?: Control<MemoryFact>;\n key?: Control<VaultFact>;\n /** On each channel the agent answers (its settings, a check, disconnecting it). */\n channel?: Control<Channel>;\n}\n\n/**\n * How a host draws an app's mark, with its own brands (the package carries none): v2's\n * `AppDirectory`. `provider` says the catalog and the app (`arcade:gmail`); `icon` is the one the\n * provider declares, when it does.\n */\nexport interface AppDirectory {\n logo: (app: {\n provider: string;\n name: string;\n icon?: string | null | undefined;\n size: number;\n }) => ReactNode;\n}\n\nexport interface AgentAboutProps {\n client: Client;\n agentId: string;\n /** Its mark (`AGENT_ICONS`), drawn beside its name in its tint; a sparkle until it has one. */\n icon?: string | null | undefined;\n /** The agent's name, for the words that address it. */\n name: string;\n messages?: Partial<AboutMessages> | undefined;\n classNames?: ClassNames;\n controls?: AboutControls | undefined;\n /** The host's logos for apps; without them an app shows the mark its provider gives. */\n apps?: AppDirectory | undefined;\n /**\n * Opens a file the owner presses in what it knows (in the host's viewer: `FileView` in a\n * dialog); without it the files are only listed.\n */\n onOpenFile?: ((file: FileFact) => void) | undefined;\n /**\n * What is open in it: its home, a topic, an automation, a run of one. Given, the host keeps it\n * (in its address, say) and hears each move through `onViewChange`; without it the panel keeps\n * its own.\n */\n view?: AboutView | undefined;\n onViewChange?: ((view: AboutView) => void) | undefined;\n /** Opens a conversation a run opened or was asked in; without it, none opens. */\n onOpenConversation?: ((id: string) => void) | undefined;\n /** The language its dates and times are said in. */\n locale?: KitLocale | undefined;\n}\n\n/**\n * What is open in the panel, one level under another: its home; a topic or an automation from\n * it; a run from its automation.\n */\nexport type AboutView =\n | { kind: \"home\" }\n | { kind: \"topic\"; topic: Topic }\n | { kind: \"automation\"; name: string }\n | { kind: \"run\"; name: string; run: string };\n\nexport const ABOUT_HOME: AboutView = { kind: \"home\" };\n\n/** How far a view is from the home: what says whether a move goes in or back out. */\nconst depthOf = (view: AboutView): number =>\n view.kind === \"home\" ? 0 : view.kind === \"run\" ? 2 : 1;\n\n/** A view as one string: what tells two apart. */\nconst keyOf = (view: AboutView): string =>\n view.kind === \"home\"\n ? \"home\"\n : view.kind === \"topic\"\n ? `topic:${view.topic}`\n : view.kind === \"automation\"\n ? `automation:${view.name}`\n : `run:${view.name}:${view.run}`;\n\n/** The view a step back from `view` goes to. */\nexport function aboutBack(view: AboutView): AboutView {\n return view.kind === \"run\" ? { kind: \"automation\", name: view.name } : ABOUT_HOME;\n}\n\n/**\n * Qué puede, made to sit beside the conversation: who it is about, in its own words (a line or\n * two); what it does on its own, one row per automation (its kind's mark, or its app's logo),\n * what it does and when, as the agent tells it; what it asks leave for; then what it works with,\n * as a grid of tiles, one per topic (its apps, tools, what it knows and has learned, where it\n * works, its keys), each with its count and a glimpse of what it holds, opening it in the same\n * place. Every list is the kit's `List` and `Row`, and every tile its `Tile`, the ones an agent's\n * screens use, so a host styles them once. The words are written by the agent the first time and\n * again whenever it changed since, by themselves. The component only reads; a host draws its own\n * controls through `controls` and calls `tools.refresh()` after a change, and opens a file\n * through `onOpenFile`. An automation opens in the panel itself, one level in (what it does, its\n * steps, its runs), and a run one more, each with its way back; `view` lets the host keep where\n * it is. Another agent starts afresh.\n */\nexport function AgentAbout(props: AgentAboutProps) {\n return (\n <KitProvider\n classNames={props.classNames as KitClassNames}\n messages={props.messages?.kit}\n locale={props.locale}\n >\n <View key={props.agentId} {...props} />\n </KitProvider>\n );\n}\n\nfunction View({\n client,\n agentId,\n icon,\n name,\n messages,\n classNames,\n controls,\n apps,\n onOpenFile,\n view: given,\n onViewChange,\n onOpenConversation,\n}: AgentAboutProps) {\n const words = { ...ABOUT_MESSAGES, ...messages };\n const [data, setData] = useState<AboutOut | null>(null);\n const [live, setLive] = useState<readonly Standing[] | null>(null);\n const [writing, setWriting] = useState(false);\n const [problem, setProblem] = useState<unknown>(null);\n // Every change the host makes reads the open automation or run again, too.\n const [version, setVersion] = useState(0);\n const [own, setOwn] = useState<AboutView>(ABOUT_HOME);\n const view = given ?? own;\n const motion = useMotion(view);\n const root = useRef<HTMLDivElement>(null);\n const scrolled = useRef(new Map<string, number>());\n const intro = useId();\n\n /** Opens `next`, keeping where the panel was scrolled to on the way in, for the way back. */\n const go = (next: AboutView) => {\n const scroller = scrollerOf(root.current);\n if (scroller !== null && depthOf(next) > depthOf(view)) {\n scrolled.current.set(keyOf(view), scroller.scrollTop);\n }\n (onViewChange ?? setOwn)(next);\n };\n\n // A view opens at its top, or back where it was left.\n const shown = keyOf(view);\n useLayoutEffect(() => {\n const scroller = scrollerOf(root.current);\n if (scroller !== null) scroller.scrollTop = scrolled.current.get(shown) ?? 0;\n }, [shown]);\n\n // Each call takes `still`, whether its answer is still wanted: a mount undone before the answer\n // (StrictMode runs effects twice) keeps nothing, so one writing runs, not two.\n\n /** Has the agent write about itself anew; what it answers is the words and the facts both. */\n const write = useCallback(\n async (still: () => boolean) => {\n setProblem(null);\n setWriting(true);\n try {\n const written = await client.agents.describe(agentId);\n if (still()) setData(written);\n } catch (error) {\n if (still()) setProblem(error);\n } finally {\n if (still()) setWriting(false);\n }\n },\n [client, agentId],\n );\n\n /**\n * Reads the words and the facts, and how each automation stands (a server without them leaves\n * the rows as they were told); when the words are missing or stale, has the agent write them.\n */\n const read = useCallback(\n async (still: () => boolean) => {\n setProblem(null);\n try {\n const [first, standing] = await Promise.all([\n client.agents.about(agentId),\n client.automations.list(agentId).catch(() => null),\n ]);\n if (!still()) return;\n setData(first);\n setLive(standing);\n // Words never written, or written before the agent last changed: it writes them again\n // by itself, so what it says stays true without anyone asking.\n if (first.about === null || first.about_at === null) await write(still);\n } catch (error) {\n if (still()) setProblem(error);\n }\n },\n [client, agentId, write],\n );\n\n useEffect(() => {\n let gone = false;\n void read(() => !gone);\n return () => {\n gone = true;\n };\n }, [read]);\n\n const tools = useMemo<AboutTools>(\n () => ({\n refresh: async () => {\n setVersion((count) => count + 1);\n await read(() => true);\n },\n }),\n [read],\n );\n // What to try again after a failure: the reading when nothing was read, the writing otherwise.\n const retry = () => (data === null ? read(() => true) : write(() => true));\n // A tool by the owner's words for it; one of its own code, by its name made words.\n const label = (action: ActionFact) =>\n action.source === \"own\" ? toolName(action.name) : (words.actions[action.name] ?? action.name);\n const pane = (bar: ReactNode, children: ReactNode) => (\n <div ref={root} className={`sc ${classes(classNames, \"sc-about\")}`}>\n <div key={shown} data-motion={motion} className={classes(classNames, \"sc-about__pane\")}>\n {bar}\n {children}\n </div>\n </div>\n );\n const home = words.title(name);\n\n if (view.kind === \"topic\" && data !== null) {\n return pane(\n <Bar back={home} onBack={() => go(ABOUT_HOME)} classNames={classNames} />,\n <TopicView\n client={client}\n agentId={agentId}\n name={name}\n version={version}\n topic={view.topic}\n facts={data.facts}\n apps={apps}\n label={label}\n words={words}\n classNames={classNames}\n tools={tools}\n controls={controls}\n onOpenFile={onOpenFile}\n />,\n );\n }\n\n if (view.kind === \"automation\") {\n const fact = data?.facts.automations.find((each) => each.name === view.name);\n return pane(\n <Bar\n back={home}\n onBack={() => go(ABOUT_HOME)}\n end={fact !== undefined ? controls?.automation?.(fact, tools) : undefined}\n classNames={classNames}\n />,\n <AutomationPane\n key={version}\n client={client}\n agentId={agentId}\n name={view.name}\n apps={apps}\n words={words}\n classNames={classNames}\n onOpenRun={(run) => go({ kind: \"run\", name: view.name, run: run.id })}\n />,\n );\n }\n\n if (view.kind === \"run\") {\n return pane(\n <Bar\n back={live?.find((each) => each.name === view.name)?.title ?? home}\n onBack={() => go({ kind: \"automation\", name: view.name })}\n classNames={classNames}\n />,\n <RunPane\n key={version}\n client={client}\n agentId={agentId}\n name={view.name}\n run={view.run}\n apps={apps}\n words={words}\n classNames={classNames}\n onOpenConversation={onOpenConversation}\n />,\n );\n }\n\n const state = data === null ? null : aboutState(data.about, data.about_at, writing);\n return pane(\n null,\n <>\n <section aria-labelledby={intro} className={classes(classNames, \"sc-about__intro\")}>\n <AgentName\n id={intro}\n agentId={agentId}\n icon={icon}\n name={name}\n label={home}\n classNames={classNames}\n />\n {problem !== null ? (\n <Problem words={words} classNames={classNames} onRetry={retry}>\n {words.failed}\n {problem instanceof ApiError && ` ${problem.detail}`}\n </Problem>\n ) : data === null || data.about === null ? (\n <Writing\n text={data === null ? words.loading : words.writing(name)}\n classNames={classNames}\n />\n ) : (\n <p className={classes(classNames, \"sc-about__lead\")}>{data.about.summary}</p>\n )}\n {state === \"writing\" && data?.about != null && (\n <p role=\"status\" className={classes(classNames, \"sc-about__updating\")}>\n {words.updating}\n </p>\n )}\n </section>\n {data !== null && (\n <>\n <Automations\n apps={apps}\n told={automationsOf(data.about, data.facts)}\n live={live}\n words={words}\n classNames={classNames}\n tools={tools}\n controls={controls}\n onOpen={(automation) => go({ kind: \"automation\", name: automation.name })}\n />\n {data.about !== null && data.about.asks.length > 0 && (\n <Asks asks={data.about.asks} words={words} classNames={classNames} />\n )}\n <nav aria-label={words.facts} className={classes(classNames, \"sc-about__menu\")}>\n <p className={classes(classNames, \"sc-about__label\")}>{words.facts}</p>\n <div className={classes(classNames, \"sc-about__topics\")}>\n {HOME_TOPICS.map((topic) => (\n <TopicRow\n key={topic}\n topic={topic}\n facts={data.facts}\n apps={apps}\n words={words}\n classNames={classNames}\n onOpen={() => go({ kind: \"topic\", topic })}\n />\n ))}\n </div>\n </nav>\n </>\n )}\n </>,\n );\n}\n\n/**\n * What the panel's home shows the agent works with, in the order the owner looks for it: where\n * people reach it, the apps it uses, what it knows, what it learned, its keys. Its tools are\n * the platform's business: they show in the technical view, not here.\n */\nconst HOME_TOPICS: readonly Topic[] = [\"channels\", \"apps\", \"knowledge\", \"learning\", \"keys\"];\n\n/** How many icons a topic's row draws before \"+N\". */\nconst MOST_MARKS = 5;\n\n/**\n * A topic on the panel's home, as icons: its name, then what it holds as their logos or marks\n * (the channels', the apps', the files') and how many; a topic with nothing says so, quietly.\n * Pressing it opens the topic.\n */\nfunction TopicRow({\n topic,\n facts,\n apps,\n words,\n classNames,\n onOpen,\n}: {\n topic: Topic;\n facts: Facts;\n apps: AppDirectory | undefined;\n words: AboutMessages;\n classNames: ClassNames;\n onOpen: () => void;\n}) {\n const count = countOf(topic, facts);\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n data-empty={count === 0 || undefined}\n className={classes(classNames, \"sc-about__topic\")}\n >\n <span className={classes(classNames, \"sc-about__topic-name\")}>{words.topics[topic]}</span>\n {count === 0 ? (\n <span className={classes(classNames, \"sc-about__topic-none\")}>{words.empty[topic]}</span>\n ) : (\n <>\n <TopicMarks topic={topic} facts={facts} apps={apps} words={words} count={count} />\n <span className={classes(classNames, \"sc-about__topic-count\")}>{count}</span>\n </>\n )}\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden\n className={classes(classNames, \"sc-about__topic-go\")}\n >\n <path\n d=\"M6 3.5 10.5 8 6 12.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n );\n}\n\n/** What a topic holds, as icons: each channel's and app's logo, each file's mark; else its glyph. */\nfunction TopicMarks({\n topic,\n facts,\n apps,\n words,\n count,\n}: {\n topic: Topic;\n facts: Facts;\n apps: AppDirectory | undefined;\n words: AboutMessages;\n count: number;\n}) {\n const marks: ReactNode[] =\n topic === \"channels\"\n ? facts.channels.map((channel) => (\n <span key={`${channel.kind}:${channel.name}`}>\n {channelLogo(channel.kind, apps, words, 20)}\n </span>\n ))\n : topic === \"apps\"\n ? facts.connections.map((connection) => (\n <span key={connection.name}>{appMark(connectionApp(connection), apps, 20)}</span>\n ))\n : topic === \"knowledge\"\n ? facts.files.map((file) => <FileMark key={file.path} name={file.path} size={20} />)\n : [<Mark key={topic} icon={TOPIC_GLYPHS[topic]} size={20} />];\n const shown = marks.slice(0, MOST_MARKS);\n const drawn = topic === \"learning\" || topic === \"keys\" ? 0 : count;\n return <MarkStack more={Math.max(0, drawn - shown.length)}>{shown}</MarkStack>;\n}\n\n/**\n * Who the panel is about, at its top: the agent's mark in its tint, bare, beside its name; the\n * panel's title (\"About Ana\") stays for screen readers.\n */\nfunction AgentName({\n id,\n agentId,\n icon,\n name,\n label,\n classNames,\n}: {\n id: string;\n agentId: string;\n icon: string | null | undefined;\n name: string;\n label: string;\n classNames: ClassNames;\n}) {\n const Glyph = agentGlyph(icon);\n return (\n <h2\n id={id}\n aria-label={label}\n data-tint={agentTint(agentId)}\n className={classes(classNames, \"sc-about__name\")}\n >\n <Glyph aria-hidden weight=\"duotone\" />\n <span>{name}</span>\n </h2>\n );\n}\n\n/**\n * Which way the panel moved to show `view`: in (one level deeper), back out, or nowhere (the first\n * view). Kept per view, so a view's own updates never replay it.\n */\nfunction useMotion(view: AboutView): \"in\" | \"out\" | undefined {\n const key = keyOf(view);\n const depth = depthOf(view);\n const [moved, setMoved] = useState<{\n key: string;\n depth: number;\n motion: \"in\" | \"out\" | undefined;\n }>({ key, depth, motion: undefined });\n if (moved.key !== key) {\n setMoved({ key, depth, motion: depth >= moved.depth ? \"in\" : \"out\" });\n }\n return moved.motion;\n}\n\n/** The nearest box around `element` that scrolls: where the panel's place is kept. */\nfunction scrollerOf(element: HTMLElement | null): HTMLElement | null {\n for (let at = element?.parentElement ?? null; at !== null; at = at.parentElement) {\n const overflow = getComputedStyle(at).overflowY;\n if (overflow === \"auto\" || overflow === \"scroll\") return at;\n }\n return null;\n}\n\n/**\n * The bar over a view opened from another: the way back to it, named, at its start; the host's\n * controls at its end. It stays at the top as the view scrolls.\n */\nfunction Bar({\n back,\n onBack,\n end,\n classNames,\n}: {\n back: string;\n onBack: () => void;\n end?: ReactNode;\n classNames: ClassNames;\n}) {\n return (\n <div className={classes(classNames, \"sc-about__bar\")}>\n <button type=\"button\" onClick={onBack} className={classes(classNames, \"sc-about__back\")}>\n <svg viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden>\n <path\n d=\"M10 3.5 5.5 8l4.5 4.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n <span>{back}</span>\n </button>\n {end != null && <span className={classes(classNames, \"sc-about__bar-end\")}>{end}</span>}\n </div>\n );\n}\n\n/**\n * An automation, open in the panel: what starts it and when it runs next, the owner's sentence,\n * its steps, the table it moves, its runs (each opening its record). Read again every few\n * seconds while a run of it is under way.\n */\nfunction AutomationPane({\n client,\n agentId,\n name,\n apps,\n words,\n classNames,\n onOpenRun,\n}: Drawn & {\n client: Client;\n agentId: string;\n name: string;\n apps: AppDirectory | undefined;\n onOpenRun: (run: Run) => void;\n}) {\n const { messages } = useKit();\n const read = usePolled(\n () => client.automations.get(agentId, name),\n `${agentId}:${name}`,\n (automation) => automation.runs.some((run) => run.status === \"running\"),\n );\n if (read.gone) return <Gone classNames={classNames}>{messages.automation.gone}</Gone>;\n if (read.value === null) {\n return read.problem === null ? (\n <Loading words={words} classNames={classNames} />\n ) : (\n <Gone classNames={classNames}>{words.failed}</Gone>\n );\n }\n return (\n <Automation\n className={classes(classNames, \"sc-about__automation\")}\n automation={read.value}\n runs={read.value.runs}\n apps={apps}\n onOpenRun={onOpenRun}\n />\n );\n}\n\n/**\n * A run, open in the panel: how it went, step by step in words, what it opened. Read with its\n * automation (its steps' order and its apps' names), again while it is under way.\n */\nfunction RunPane({\n client,\n agentId,\n name,\n run,\n apps,\n words,\n classNames,\n onOpenConversation,\n}: Drawn & {\n client: Client;\n agentId: string;\n name: string;\n run: string;\n apps: AppDirectory | undefined;\n onOpenConversation: ((id: string) => void) | undefined;\n}) {\n const read = usePolled(\n async () => {\n const [record, automation] = await Promise.all([\n client.runs.get(run),\n client.automations.get(agentId, name).catch(() => null),\n ]);\n return { record, automation };\n },\n run,\n ({ record }) => record.status === \"running\",\n );\n if (read.gone) return <Gone classNames={classNames}>{words.runGone}</Gone>;\n if (read.value === null) {\n return read.problem === null ? (\n <Loading words={words} classNames={classNames} />\n ) : (\n <Gone classNames={classNames}>{words.failed}</Gone>\n );\n }\n return (\n <RunView\n className={classes(classNames, \"sc-about__run\")}\n run={read.value.record}\n automation={read.value.automation}\n apps={apps}\n onOpenConversation={onOpenConversation}\n />\n );\n}\n\n/** A view whose thing is being read: lines with a light sweeping them. */\nfunction Loading({ words, classNames }: Drawn) {\n return <Writing text={words.loading} classNames={classNames} />;\n}\n\n/** In place of what is no longer there, or could not be read. */\nfunction Gone({ classNames, children }: { classNames: ClassNames; children: ReactNode }) {\n return <p className={classes(classNames, \"sc-about__empty\")}>{children}</p>;\n}\n\n/** An automation's mark says its kind: a time, a call from another system, an event. */\nconst KIND_ICONS: Readonly<Record<AutomationKind, Icon>> = {\n schedule: \"clock\",\n webhook: \"code\",\n event: \"bell\",\n};\n\n/** An app's mark: the host's logo when it draws one, else the provider's, bare. */\nfunction appMark(\n app: { provider: string; name: string; icon?: string | null | undefined },\n apps: AppDirectory | undefined,\n size: number,\n) {\n if (apps !== undefined) return apps.logo({ ...app, size });\n return (\n <Mark logo size={size}>\n {providerMark(app.provider.split(\":\")[0] as Provider, app.icon ?? undefined)}\n </Mark>\n );\n}\n\nconst connectionApp = (connection: ConnectionFact) => ({\n provider: `${connection.provider}:${connection.toolkit ?? connection.name}`,\n name: appTitle(connection),\n icon: connection.icon,\n});\n\n/**\n * A channel's mark: the host's logo for its brand when it draws apps (WhatsApp, Instagram, the\n * plain mark of a text message), else the package's glyph, bare.\n */\nfunction channelLogo(\n kind: ChannelKind,\n apps: AppDirectory | undefined,\n words: AboutMessages,\n size: number,\n) {\n if (apps !== undefined)\n return apps.logo({ provider: kind, name: words.channelNames[kind], size });\n return <Mark size={size}>{channelMark(kind)}</Mark>;\n}\n\ninterface Drawn {\n words: AboutMessages;\n classNames: ClassNames;\n}\n\n/** What could not be read or written, in the warning tone, and the button to try again. */\nfunction Problem({\n words,\n classNames,\n onRetry,\n children,\n}: Drawn & { onRetry: () => Promise<void>; children: ReactNode }) {\n return (\n <p role=\"alert\" className={classes(classNames, \"sc-about__updated\")}>\n <span>{children}</span>\n <button type=\"button\" onClick={() => void onRetry()}>\n {words.refresh}\n </button>\n </p>\n );\n}\n\n/** The agent writing: the lead's lines with a light sweeping them, and the sentence saying so. */\nfunction Writing({ text, classNames }: { text: string; classNames: ClassNames }) {\n return (\n <div role=\"status\" className={classes(classNames, \"sc-about__writing\")}>\n <div aria-hidden>\n <i />\n <i />\n </div>\n <p>{text}</p>\n </div>\n );\n}\n\n/** What it asks leave for: its label, then the things, a short list. */\nfunction Asks({ asks, words, classNames }: Drawn & { asks: string[] }) {\n const id = useId();\n return (\n <section\n aria-labelledby={id}\n className={classes(classNames, \"sc-about__section\", \"sc-about__asks\")}\n >\n <div className={classes(classNames, \"sc-about__label\")}>\n <h3 id={id}>{words.asks}</h3>\n </div>\n <ul>\n {asks.map((ask, index) => (\n <li key={index}>{ask}</li>\n ))}\n </ul>\n </section>\n );\n}\n\n/**\n * What it does on its own: one row per automation, its kind's mark (or, on an app's event, the\n * app's logo), what it does then over when, as the agent tells it (its title and its kind until\n * it has), a paused one quieter; how it stands now when that needs a look (a run under way, the\n * last one failed); the host's control at each row's end, and over them the way to a new one. A\n * row opens its automation.\n */\nfunction Automations({\n told,\n live,\n words,\n classNames,\n tools,\n controls,\n apps,\n onOpen,\n}: Drawn & {\n told: Told[];\n live: readonly Standing[] | null;\n tools: AboutTools;\n controls: AboutControls | undefined;\n apps: AppDirectory | undefined;\n onOpen: (automation: AutomationFact) => void;\n}) {\n const id = useId();\n const { messages } = useKit();\n const standing = new Map(live?.map((automation) => [automation.name, automation]));\n return (\n <section aria-labelledby={id} className={classes(classNames, \"sc-about__section\")}>\n <div className={classes(classNames, \"sc-about__label\")}>\n <h3 id={id}>{words.does}</h3>\n {controls?.newAutomation !== undefined && (\n <span className={classes(classNames, \"sc-about__tools\")}>\n {controls.newAutomation(tools)}\n </span>\n )}\n </div>\n {told.length === 0 ? (\n <p className={classes(classNames, \"sc-about__empty\")}>{words.noAutomations}</p>\n ) : (\n <List>\n {told.map(({ automation, line }) => {\n const when = line?.when ?? words.kinds[automation.kind];\n const now = standing.get(automation.name);\n const going = now?.last_run?.status === \"running\";\n return (\n <Row\n key={automation.id}\n mark={\n automation.provider === undefined ? (\n <Mark icon={GLYPHS[KIND_ICONS[automation.kind]]} />\n ) : (\n appMark(\n {\n provider: automation.provider,\n name:\n automation.app_title ?? toolName(automation.app ?? automation.provider),\n icon: automation.icon,\n },\n apps,\n 28,\n )\n )\n }\n title={line?.text ?? automation.title}\n subtitle={when}\n error={now?.state === \"failing\" ? messages.automation.states.failing : undefined}\n meta={\n going ? (\n <Tag tone=\"info\">\n <i aria-hidden className={classes(classNames, \"sc-run__live\")} />\n {messages.automation.statuses.running}\n </Tag>\n ) : automation.on ? undefined : (\n <Tag>{words.paused}</Tag>\n )\n }\n quiet={!automation.on}\n onPress={() => onOpen(automation)}\n trailing={controls?.automation?.(automation, tools)}\n />\n );\n })}\n </List>\n )}\n {controls?.automationsNote?.(tools)}\n </section>\n );\n}\n\n/**\n * One topic, open: its name with its count and the host's control, a line saying what it is, then\n * its rows (what it knows as its files and its tables; what it learned; the rest with a mark and\n * a note), or its one line when it holds nothing.\n */\nfunction TopicView({\n client,\n agentId,\n name,\n version,\n topic,\n facts,\n apps,\n label,\n words,\n classNames,\n tools,\n controls,\n onOpenFile,\n}: Drawn & {\n client: Client;\n agentId: string;\n /** The agent's name, over what is its own. */\n name: string;\n /** Moves with every change the host makes: what the topic reads itself, it reads again. */\n version: number;\n topic: Topic;\n facts: Facts;\n apps: AppDirectory | undefined;\n label: (action: ActionFact) => string;\n tools: AboutTools;\n controls: AboutControls | undefined;\n onOpenFile: ((file: FileFact) => void) | undefined;\n}) {\n const id = useId();\n const tool = controls?.topic?.[topic];\n const count = countOf(topic, facts);\n // The tools are all listed, the ones that are off too: they are what the owner turns on.\n const shown = topic === \"tools\" ? facts.actions.length : count;\n return (\n <section aria-labelledby={id} className={classes(classNames, \"sc-about__group\")}>\n <h3 id={id} className={classes(classNames, \"sc-about__title\")}>\n <span>{words.topics[topic]}</span>\n {count > 0 && <b className={classes(classNames, \"sc-about__count\")}>{count}</b>}\n {tool !== undefined && (\n <span className={classes(classNames, \"sc-about__tools\")}>{tool(tools)}</span>\n )}\n </h3>\n <p className={classes(classNames, \"sc-about__hint\")}>{words.hints[topic]}</p>\n {topic === \"channels\" ? (\n <Channels\n client={client}\n agentId={agentId}\n version={version}\n facts={facts}\n apps={apps}\n words={words}\n classNames={classNames}\n tools={tools}\n controls={controls}\n />\n ) : shown === 0 ? (\n <p className={classes(classNames, \"sc-about__empty\")}>{words.empty[topic]}</p>\n ) : topic === \"knowledge\" ? (\n <Knowledge\n name={name}\n facts={facts}\n words={words}\n classNames={classNames}\n tools={tools}\n controls={controls}\n onOpenFile={onOpenFile}\n />\n ) : topic === \"tools\" ? (\n <Tools\n name={name}\n facts={facts}\n label={label}\n words={words}\n classNames={classNames}\n tools={tools}\n controls={controls}\n />\n ) : topic === \"keys\" ? (\n <Keys\n name={name}\n facts={facts}\n words={words}\n classNames={classNames}\n tools={tools}\n controls={controls}\n />\n ) : topic === \"learning\" ? (\n <List>\n {facts.memories.map((memory) => (\n <Row\n key={memory.id}\n className={classes(classNames, \"sc-about__memory\")}\n title={memory.text}\n subtitle={words.ago(new Date(memory.created_at))}\n trailing={controls?.memory?.(memory, tools)}\n />\n ))}\n </List>\n ) : (\n <Rows items={appItems(facts, tools, controls, apps)} classNames={classNames} />\n )}\n </section>\n );\n}\n\n/** How each state of a channel reads beside its dot: working, waiting, broken, or off. */\nconst CHANNEL_TONES: Readonly<Record<ChannelStatus, StatusTone | \"wait\">> = {\n active: \"on\",\n pending: \"wait\",\n error: \"off\",\n disconnected: \"quiet\",\n};\n\nconst isStatus = (status: string): status is ChannelStatus => status in CHANNEL_TONES;\n\n/** The order channels are listed in: by kind, as people reach a business most; disconnected last. */\nconst KIND_ORDER: readonly ChannelKind[] = [\n \"whatsapp\",\n \"instagram\",\n \"sms\",\n \"email\",\n \"web\",\n \"phone\",\n];\n\nconst inOrder = (channels: readonly Channel[]): Channel[] =>\n [...channels].sort(\n (a, b) =>\n Number(a.status === \"disconnected\") - Number(b.status === \"disconnected\") ||\n KIND_ORDER.indexOf(a.kind) - KIND_ORDER.indexOf(b.kind),\n );\n\n/**\n * Where it works: each channel it answers, as the API lists them (WhatsApp, Instagram, SMS; the\n * disconnected ones last, quieter), by its brand's mark and name, its number or handle, how it stands with the\n * provider's word on it, and the host's control at its end; then the devices it uses. Read\n * when the topic opens and after each change the host makes; until then, or on a server without\n * the list, the facts' rows.\n */\nfunction Channels({\n client,\n agentId,\n version,\n facts,\n apps,\n words,\n classNames,\n tools,\n controls,\n}: Drawn & {\n client: Client;\n agentId: string;\n version: number;\n facts: Facts;\n apps: AppDirectory | undefined;\n tools: AboutTools;\n controls: AboutControls | undefined;\n}) {\n const [listed, setListed] = useState<readonly Channel[] | null>(null);\n useEffect(() => {\n let gone = false;\n client.channels.list(agentId).then(\n (channels) => {\n if (!gone) setListed(channels);\n },\n () => undefined,\n );\n return () => {\n gone = true;\n };\n }, [client, agentId, version]);\n\n const state = (status: string) => {\n const known = isStatus(status);\n return (\n <span\n data-tone={known ? CHANNEL_TONES[status] : toneOf(status)}\n className={classes(classNames, \"sc-about__state\")}\n >\n <i aria-hidden className={classes(classNames, \"sc-about__dot\")} />\n {known ? words.channelStates[status] : status}\n </span>\n );\n };\n // Its number or handle, and under it the provider's word on it; on an error, that word is why.\n const told = (channel: Channel) => {\n const detail = channel.detail === null ? null : words.channelDetail(channel.detail);\n const error = channel.status === \"error\";\n return {\n subtitle: (\n <>\n {channel.name}\n {!error && detail !== null && detail !== \"\" && (\n <span className={classes(classNames, \"sc-about__detail\")}>{detail}</span>\n )}\n </>\n ),\n error: error ? (detail ?? undefined) : undefined,\n };\n };\n const rows =\n listed === null\n ? facts.channels.map((channel: ChannelFact) => (\n <Row\n key={`${channel.kind}:${channel.name}`}\n mark={channelLogo(channel.kind, apps, words, 28)}\n title={words.channelNames[channel.kind]}\n subtitle={channel.name}\n meta={state(channel.status)}\n />\n ))\n : inOrder(listed).map((channel) => (\n <Row\n key={channel.id}\n mark={channelLogo(channel.kind, apps, words, 28)}\n title={words.channelNames[channel.kind]}\n {...told(channel)}\n meta={state(channel.status)}\n quiet={channel.status === \"disconnected\"}\n trailing={controls?.channel?.(channel, tools) ?? undefined}\n />\n ));\n const devices = facts.devices.map((device) => (\n <Row\n key={`device:${device.kind}:${device.name}`}\n mark={<Mark>{deviceMark(device.kind)}</Mark>}\n title={device.name}\n meta={\n <i\n aria-hidden\n data-tone={toneOf(device.status)}\n className={classes(classNames, \"sc-about__dot\")}\n />\n }\n />\n ));\n if (rows.length + devices.length === 0) {\n return <p className={classes(classNames, \"sc-about__empty\")}>{words.empty.channels}</p>;\n }\n return (\n <List>\n {rows}\n {devices}\n </List>\n );\n}\n\n/**\n * One row of a topic: a mark, a name, a quieter line (what an app may do there, a note), a dot\n * for a status, and the host's control at its end.\n */\ninterface Item {\n key: string;\n mark: ReactNode;\n label: string;\n note?: string | undefined;\n status?: StatusTone | undefined;\n off?: boolean | undefined;\n control?: ReactNode;\n}\n\n/** Its apps: each by its logo and name, what it may do there under it, how it stands at its end. */\nfunction appItems(\n facts: Facts,\n tools: AboutTools,\n controls: AboutControls | undefined,\n apps: AppDirectory | undefined,\n): Item[] {\n return facts.connections.map((connection) => ({\n key: `${connection.provider}:${connection.name}`,\n mark: appMark(connectionApp(connection), apps, 28),\n label: appTitle(connection),\n note: connection.actions.map((action) => toolTitle(connection, action)).join(\", \"),\n status: connection.status === undefined ? undefined : toneOf(connection.status),\n control: controls?.app?.(connection, tools),\n }));\n}\n\n/**\n * What it can do while it talks: its own code's actions, then the ones every agent comes with\n * (each under its heading when it has both); in each, the ones it has first, then the ones the\n * owner can turn on, quieter, saying so.\n */\nfunction Tools({\n name,\n facts,\n label,\n words,\n classNames,\n tools,\n controls,\n}: Drawn & {\n name: string;\n facts: Facts;\n label: (action: ActionFact) => string;\n tools: AboutTools;\n controls: AboutControls | undefined;\n}) {\n const groups = [\n { key: \"own\", title: words.theirs(name), actions: facts.actions.filter(isOwn) },\n { key: \"built\", title: words.builtIn, actions: facts.actions.filter((a) => !isOwn(a)) },\n ].filter((group) => group.actions.length > 0);\n const rows = (actions: readonly ActionFact[]) => (\n <Rows\n items={[...actions.filter((a) => a.on), ...actions.filter((a) => !a.on)].map((action) => ({\n key: `${action.source}:${action.name}`,\n mark: <Mark>{actionMark(action.name, action.source)}</Mark>,\n label: label(action),\n note: action.on ? undefined : words.off,\n off: !action.on,\n control: controls?.tool?.(action, tools),\n }))}\n classNames={classNames}\n />\n );\n if (groups.length === 1) return rows(groups[0]?.actions ?? []);\n return (\n <>\n {groups.map((group) => (\n <Group key={group.key} title={group.title} classNames={classNames}>\n {rows(group.actions)}\n </Group>\n ))}\n </>\n );\n}\n\nconst isOwn = (action: ActionFact): boolean => action.source === \"own\";\n\n/** A part of a topic under its small heading: whose things they are, or what kind. */\nfunction Group({\n title,\n classNames,\n children,\n}: {\n title: string;\n classNames: ClassNames;\n children: ReactNode;\n}) {\n return (\n <>\n <h4 className={classes(classNames, \"sc-about__subtitle\")}>{title}</h4>\n {children}\n </>\n );\n}\n\n/** What is the agent's own, then the organization's: the heading says whose, once. */\nfunction byOwner<T extends { shared?: boolean | undefined }>(\n things: readonly T[],\n name: string,\n words: AboutMessages,\n): { key: string; title: string; things: T[] }[] {\n return [\n { key: \"own\", title: words.theirs(name), things: things.filter((thing) => !thing.shared) },\n {\n key: \"shared\",\n title: words.organization,\n things: things.filter((thing) => thing.shared === true),\n },\n ].filter((group) => group.things.length > 0);\n}\n\n/**\n * What it knows, by whose it is: its own files, then the organization's (shared with every\n * agent: the heading says so, once), then the tables it reads or writes. A file is its kind's\n * mark, its name on one line, its folder under it (and that it is only kept, when `search_files`\n * does not read it), its size at the right; pressed, the host opens it; the host's controls at\n * its end.\n */\nfunction Knowledge({\n name,\n facts,\n words,\n classNames,\n tools,\n controls,\n onOpenFile,\n}: Drawn & {\n name: string;\n facts: Facts;\n tools: AboutTools;\n controls: AboutControls | undefined;\n onOpenFile: ((file: FileFact) => void) | undefined;\n}) {\n return (\n <>\n {byOwner(facts.files, name, words).map((group) => (\n <Group key={group.key} title={group.title} classNames={classNames}>\n <List>\n {group.things.map((file) => (\n <Row\n key={file.path}\n className={classes(classNames, \"sc-about__file\")}\n mark={<FileMark name={file.path} />}\n title={<span title={file.path}>{baseName(file.path)}</span>}\n subtitle={fileLine(file, words)}\n meta={words.size(file.size)}\n onPress={onOpenFile && (() => onOpenFile(file))}\n trailing={controls?.file?.(file, tools) ?? undefined}\n />\n ))}\n </List>\n </Group>\n ))}\n {facts.data.length > 0 && (\n <Group title={words.tables} classNames={classNames}>\n <Rows\n items={facts.data.map((table) => ({\n key: table.table,\n mark: <Mark icon={GLYPHS.table} />,\n label: table.title ?? table.table,\n note: table.can.map((can) => words.can[can] ?? can).join(\", \"),\n control: controls?.table?.(table, tools),\n }))}\n classNames={classNames}\n />\n </Group>\n )}\n </>\n );\n}\n\n/** Under a file's name: its folder, and that it is only kept when nothing reads it at a search. */\nfunction fileLine(file: FileFact, words: AboutMessages): string | undefined {\n const folder = folderOf(file.path);\n if (file.searchable) return folder === \"\" ? undefined : folder;\n return folder === \"\" ? words.kept : `${folder} · ${words.kept}`;\n}\n\n/** Its keys, its own then the organization's, each with where it may be sent. */\nfunction Keys({\n name,\n facts,\n words,\n classNames,\n tools,\n controls,\n}: Drawn & {\n name: string;\n facts: Facts;\n tools: AboutTools;\n controls: AboutControls | undefined;\n}) {\n return (\n <>\n {byOwner(facts.vault, name, words).map((group) => (\n <Group key={group.key} title={group.title} classNames={classNames}>\n <Rows\n items={group.things.map((secret) => ({\n key: `${group.key}:${secret.name}`,\n mark: <Mark icon={GLYPHS.lock} />,\n label: secret.name,\n note: secret.hosts?.join(\", \"),\n control: controls?.key?.(secret, tools),\n }))}\n classNames={classNames}\n />\n </Group>\n ))}\n </>\n );\n}\n\n/** The folders a file sits in: \"\" for one at the top. */\nconst folderOf = (path: string): string => path.slice(0, Math.max(0, path.lastIndexOf(\"/\")));\n\nfunction Rows({ items, classNames }: { items: Item[]; classNames: ClassNames }) {\n return (\n <List>\n {items.map((item) => (\n <Row\n key={item.key}\n mark={item.mark}\n title={item.label}\n subtitle={item.note === undefined || item.note === \"\" ? undefined : item.note}\n meta={\n item.status === undefined ? undefined : (\n <i\n aria-hidden\n data-tone={item.status}\n className={classes(classNames, \"sc-about__dot\")}\n />\n )\n }\n quiet={item.off}\n trailing={item.control ?? undefined}\n />\n ))}\n </List>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentAbout.js","names":[],"sources":["../src/AgentAbout.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\n\nimport {\n BooksIcon,\n BroadcastIcon,\n KeyIcon,\n LightbulbIcon,\n PuzzlePieceIcon,\n WrenchIcon,\n type Icon as Glyph,\n} from \"@phosphor-icons/react\";\nimport {\n aboutState,\n appTitle,\n automationsOf,\n countOf,\n formatSize,\n toneOf,\n toolName,\n toolTitle,\n type AboutOut,\n type ActionFact,\n type AutomationFact,\n type AutomationKind,\n type ChannelFact,\n type ChannelKind,\n type ConnectionFact,\n type DataFact,\n type Facts,\n type FileFact,\n type Icon,\n type MemoryFact,\n type Provider,\n type StatusTone,\n type Told,\n type Topic,\n type VaultFact,\n} from \"./about\";\nimport { Automation } from \"./automation/Automation\";\nimport { RunView } from \"./automation/RunView\";\nimport type { Automation as Standing, Run } from \"./automation/types\";\nimport { agentGlyph } from \"./agentIcons\";\nimport { classes } from \"./classes\";\nimport { ApiError, type Client } from \"./client\";\nimport { baseName } from \"./files\";\nimport { GLYPHS } from \"./icons\";\nimport { KitProvider, useKit, type KitClassNames, type KitPart } from \"./kit/context\";\nimport { FileMark } from \"./kit/FileMark\";\nimport { List, Row } from \"./kit/List\";\nimport { Mark } from \"./kit/Mark\";\nimport { MarkStack } from \"./kit/MarkStack\";\nimport { Tag } from \"./kit/Tag\";\nimport { KIT_MESSAGES, type KitLocale, type KitMessages } from \"./kit/messages\";\nimport { actionMark, channelMark, deviceMark, providerMark } from \"./marks\";\n\n/** What each part of what it works with is, at a glance: bare, beside its name. */\nconst TOPIC_GLYPHS: Readonly<Record<Topic, Glyph>> = {\n apps: PuzzlePieceIcon,\n tools: WrenchIcon,\n knowledge: BooksIcon,\n learning: LightbulbIcon,\n channels: BroadcastIcon,\n keys: KeyIcon,\n};\nimport type { Channel, ChannelStatus } from \"./types\";\nimport { agentTint } from \"./tint\";\nimport { usePolled } from \"./usePolled\";\n\n/** The words Qué puede says; a host gives its own, in its language. */\nexport interface AboutMessages {\n /** While the words are read, before anything is known. */\n loading: string;\n /** While the agent writes what it does the first time: a model call, a few seconds. */\n writing: (name: string) => string;\n /** Under the words while the agent writes them again, because it changed since. */\n updating: string;\n /** The button that tries again when the words could not be read or written. */\n refresh: string;\n /** Over what it asks the person's leave for. */\n asks: string;\n /** Over the tools that come with every agent, when it has its own too. */\n builtIn: string;\n /** The words could not be read or written; the server's own sentence follows when it gave one. */\n failed: string;\n /** Over what it does on its own. */\n does: string;\n /** In their place when it does nothing on its own yet. */\n noAutomations: string;\n /** On an automation that is off. */\n paused: string;\n /** What an automation waits for, when the agent hasn't said when yet. */\n kinds: Readonly<Record<AutomationKind, string>>;\n /** Each topic's name, on its tile and over it when open. */\n topics: Readonly<Record<Topic, string>>;\n /** A topic with nothing: on its row, and as its one line when open. */\n empty: Readonly<Record<Topic, string>>;\n /** Under an open topic's name: what it is, in a line. */\n hints: Readonly<Record<Topic, string>>;\n /** Over what is the agent's own, inside what it knows and its keys. */\n theirs: (name: string) => string;\n /** Over what is the organization's, shared with every agent: said once, by this heading. */\n organization: string;\n /** Over the tables, inside what it knows. */\n tables: string;\n /** Under a file `search_files` does not read: it is only kept. */\n kept: string;\n /** Beside a tool that is off. */\n off: string;\n /** What it may do in a table, beside it. */\n can: Readonly<Record<string, string>>;\n /** Over the words, and on the way back from a topic: who this is about. */\n title: (name: string) => string;\n /** Over the topics. */\n facts: string;\n /** In place of a run that is no longer there. */\n runGone: string;\n /** What the kit's pieces say in it (an automation, a run): their words, in the host's language. */\n kit: Partial<KitMessages>;\n /** A shipped action's name in the owner's words, by its code name; the rest keep the code name. */\n actions: Readonly<Record<string, string>>;\n /** A file's size, as the host's people read it. */\n size: (bytes: number) => string;\n /** When a memory was kept, beside it. */\n ago: (when: Date) => string;\n /** A channel's name, by its kind: its row's title. */\n channelNames: Readonly<Record<ChannelKind, string>>;\n /** How a channel stands, beside its dot. */\n channelStates: Readonly<Record<ChannelStatus, string>>;\n /** The provider's word on a channel's status (English, as the API keeps it), in the host's. */\n channelDetail: (detail: string) => string;\n}\n\nexport const ABOUT_MESSAGES: AboutMessages = {\n loading: \"Loading…\",\n writing: (name) => `${name} is writing down what it does…`,\n updating: \"Updating…\",\n refresh: \"Try again\",\n asks: \"It asks you before\",\n builtIn: \"Built in\",\n failed: \"What it can do could not be read.\",\n does: \"What it does on its own\",\n noAutomations: \"Nothing on its own yet: it answers when someone writes to it.\",\n paused: \"Paused\",\n kinds: {\n schedule: \"At a time\",\n webhook: \"When called\",\n event: \"On an event\",\n platform: \"On a message\",\n },\n topics: {\n apps: \"Apps\",\n tools: \"Tools\",\n knowledge: \"Knowledge\",\n learning: \"Learning\",\n channels: \"Channels\",\n keys: \"Keys\",\n },\n empty: {\n apps: \"None yet\",\n tools: \"None on\",\n knowledge: \"Nothing yet\",\n learning: \"Nothing yet\",\n channels: \"None yet\",\n keys: \"None\",\n },\n hints: {\n apps: \"The business's apps it uses, and what it may do in each.\",\n tools: \"What it can do while it talks with someone.\",\n knowledge: \"The documents and tables it reads to answer.\",\n learning: \"What it has learned from its conversations.\",\n channels: \"Where it works: where people write to it, and the devices it uses.\",\n keys: \"Passwords and keys it uses, kept sealed.\",\n },\n theirs: (name) => `${name}'s own`,\n organization: \"The organization's\",\n tables: \"Tables\",\n kept: \"Only kept\",\n off: \"off\",\n can: { find: \"looks up\", add: \"adds\", update: \"changes\" },\n title: (name) => `About ${name}`,\n facts: \"What it works with\",\n runGone: \"This run is no longer there.\",\n kit: KIT_MESSAGES,\n actions: {},\n size: (bytes) => formatSize(bytes),\n ago: (when) => when.toLocaleDateString(),\n channelNames: {\n whatsapp: \"WhatsApp\",\n instagram: \"Instagram\",\n sms: \"SMS\",\n email: \"Email\",\n web: \"Web chat\",\n phone: \"Phone\",\n },\n channelStates: {\n active: \"Active\",\n pending: \"Pending\",\n error: \"Error\",\n disconnected: \"Disconnected\",\n },\n channelDetail: (detail) => detail,\n};\n\n/** The parts, by the class each carries; `classNames` adds a host's own under the same keys. */\nexport type AboutPart =\n | \"sc-about\"\n | \"sc-about__pane\"\n | \"sc-about__intro\"\n | \"sc-about__heading\"\n | \"sc-about__name\"\n | \"sc-about__topics\"\n | \"sc-about__topic\"\n | \"sc-about__topic-name\"\n | \"sc-about__topic-none\"\n | \"sc-about__topic-count\"\n | \"sc-about__topic-go\"\n | \"sc-about__lead\"\n | \"sc-about__updated\"\n | \"sc-about__updating\"\n | \"sc-about__section\"\n | \"sc-about__label\"\n | \"sc-about__asks\"\n | \"sc-about__menu\"\n | \"sc-about__memory\"\n | \"sc-about__back\"\n | \"sc-about__bar\"\n | \"sc-about__bar-end\"\n | \"sc-about__automation\"\n | \"sc-about__run\"\n | \"sc-about__group\"\n | \"sc-about__title\"\n | \"sc-about__subtitle\"\n | \"sc-about__file\"\n | \"sc-about__detail\"\n | \"sc-about__hint\"\n | \"sc-about__count\"\n | \"sc-about__tools\"\n | \"sc-about__dot\"\n | \"sc-about__state\"\n | \"sc-about__empty\"\n | \"sc-about__writing\";\n\n/** The panel's own parts and the kit's it is drawn with: a host styles both under one prop. */\ntype ClassNames = Partial<Record<AboutPart | KitPart, string>> | undefined;\n\n/** What a host's control is given, besides the thing it stands beside. */\nexport interface AboutTools {\n /**\n * Reads the facts again, and has the agent write its words anew when the change made them\n * stale; the open topic stays. Call it after a change.\n */\n refresh: () => Promise<void>;\n}\n\ntype Control<T> = ((thing: T, tools: AboutTools) => ReactNode) | undefined;\n\n/**\n * The host's own controls: the package only reads, a host that lets its people change things\n * draws them here. Each one is drawn at the right end of what it stands beside.\n */\nexport interface AboutControls {\n /** Over what it does on its own (a new automation). */\n newAutomation?: ((tools: AboutTools) => ReactNode) | undefined;\n /** On each automation (switch it off, delete it). */\n automation?: Control<AutomationFact>;\n /** Under its automations: what else it does on its own (the tasks it runs for contacts). */\n automationsNote?: ((tools: AboutTools) => ReactNode) | undefined;\n /** Over an open topic (add an app, upload a file, add a key). */\n topic?: Partial<Record<Topic, (tools: AboutTools) => ReactNode>> | undefined;\n app?: Control<ConnectionFact>;\n tool?: Control<ActionFact>;\n file?: Control<FileFact>;\n table?: Control<DataFact>;\n memory?: Control<MemoryFact>;\n key?: Control<VaultFact>;\n /** On each channel the agent answers (its settings, a check, disconnecting it). */\n channel?: Control<Channel>;\n}\n\n/**\n * How a host draws an app's mark, with its own brands (the package carries none): v2's\n * `AppDirectory`. `provider` says the catalog and the app (`arcade:gmail`); `icon` is the one the\n * provider declares, when it does.\n */\nexport interface AppDirectory {\n logo: (app: {\n provider: string;\n name: string;\n icon?: string | null | undefined;\n size: number;\n }) => ReactNode;\n}\n\nexport interface AgentAboutProps {\n client: Client;\n agentId: string;\n /** Its mark (`AGENT_ICONS`), drawn beside its name in its tint; a sparkle until it has one. */\n icon?: string | null | undefined;\n /** The agent's name, for the words that address it. */\n name: string;\n messages?: Partial<AboutMessages> | undefined;\n classNames?: ClassNames;\n controls?: AboutControls | undefined;\n /** The host's logos for apps; without them an app shows the mark its provider gives. */\n apps?: AppDirectory | undefined;\n /**\n * Opens a file the owner presses in what it knows (in the host's viewer: `FileView` in a\n * dialog); without it the files are only listed.\n */\n onOpenFile?: ((file: FileFact) => void) | undefined;\n /**\n * What is open in it: its home, a topic, an automation, a run of one. Given, the host keeps it\n * (in its address, say) and hears each move through `onViewChange`; without it the panel keeps\n * its own.\n */\n view?: AboutView | undefined;\n onViewChange?: ((view: AboutView) => void) | undefined;\n /** Opens a conversation a run opened or was asked in; without it, none opens. */\n onOpenConversation?: ((id: string) => void) | undefined;\n /** The language its dates and times are said in. */\n locale?: KitLocale | undefined;\n}\n\n/**\n * What is open in the panel, one level under another: its home; a topic or an automation from\n * it; a run from its automation.\n */\nexport type AboutView =\n | { kind: \"home\" }\n | { kind: \"topic\"; topic: Topic }\n | { kind: \"automation\"; name: string }\n | { kind: \"run\"; name: string; run: string };\n\nexport const ABOUT_HOME: AboutView = { kind: \"home\" };\n\n/** How far a view is from the home: what says whether a move goes in or back out. */\nconst depthOf = (view: AboutView): number =>\n view.kind === \"home\" ? 0 : view.kind === \"run\" ? 2 : 1;\n\n/** A view as one string: what tells two apart. */\nconst keyOf = (view: AboutView): string =>\n view.kind === \"home\"\n ? \"home\"\n : view.kind === \"topic\"\n ? `topic:${view.topic}`\n : view.kind === \"automation\"\n ? `automation:${view.name}`\n : `run:${view.name}:${view.run}`;\n\n/** The view a step back from `view` goes to. */\nexport function aboutBack(view: AboutView): AboutView {\n return view.kind === \"run\" ? { kind: \"automation\", name: view.name } : ABOUT_HOME;\n}\n\n/**\n * Qué puede, made to sit beside the conversation: who it is about, in its own words (a line or\n * two); what it does on its own, one row per automation (its kind's mark, or its app's logo),\n * what it does and when, as the agent tells it; what it asks leave for; then what it works with,\n * as a grid of tiles, one per topic (its apps, tools, what it knows and has learned, where it\n * works, its keys), each with its count and a glimpse of what it holds, opening it in the same\n * place. Every list is the kit's `List` and `Row`, and every tile its `Tile`, the ones an agent's\n * screens use, so a host styles them once. The words are written by the agent the first time and\n * again whenever it changed since, by themselves. The component only reads; a host draws its own\n * controls through `controls` and calls `tools.refresh()` after a change, and opens a file\n * through `onOpenFile`. An automation opens in the panel itself, one level in (what it does, its\n * steps, its runs), and a run one more, each with its way back; `view` lets the host keep where\n * it is. Another agent starts afresh.\n */\nexport function AgentAbout(props: AgentAboutProps) {\n return (\n <KitProvider\n classNames={props.classNames as KitClassNames}\n messages={props.messages?.kit}\n locale={props.locale}\n >\n <View key={props.agentId} {...props} />\n </KitProvider>\n );\n}\n\nfunction View({\n client,\n agentId,\n icon,\n name,\n messages,\n classNames,\n controls,\n apps,\n onOpenFile,\n view: given,\n onViewChange,\n onOpenConversation,\n}: AgentAboutProps) {\n const words = { ...ABOUT_MESSAGES, ...messages };\n const [data, setData] = useState<AboutOut | null>(null);\n const [live, setLive] = useState<readonly Standing[] | null>(null);\n const [writing, setWriting] = useState(false);\n const [problem, setProblem] = useState<unknown>(null);\n // Every change the host makes reads the open automation or run again, too.\n const [version, setVersion] = useState(0);\n const [own, setOwn] = useState<AboutView>(ABOUT_HOME);\n const view = given ?? own;\n const motion = useMotion(view);\n const root = useRef<HTMLDivElement>(null);\n const scrolled = useRef(new Map<string, number>());\n const intro = useId();\n\n /** Opens `next`, keeping where the panel was scrolled to on the way in, for the way back. */\n const go = (next: AboutView) => {\n const scroller = scrollerOf(root.current);\n if (scroller !== null && depthOf(next) > depthOf(view)) {\n scrolled.current.set(keyOf(view), scroller.scrollTop);\n }\n (onViewChange ?? setOwn)(next);\n };\n\n // A view opens at its top, or back where it was left.\n const shown = keyOf(view);\n useLayoutEffect(() => {\n const scroller = scrollerOf(root.current);\n if (scroller !== null) scroller.scrollTop = scrolled.current.get(shown) ?? 0;\n }, [shown]);\n\n // Each call takes `still`, whether its answer is still wanted: a mount undone before the answer\n // (StrictMode runs effects twice) keeps nothing, so one writing runs, not two.\n\n /** Has the agent write about itself anew; what it answers is the words and the facts both. */\n const write = useCallback(\n async (still: () => boolean) => {\n setProblem(null);\n setWriting(true);\n try {\n const written = await client.agents.describe(agentId);\n if (still()) setData(written);\n } catch (error) {\n if (still()) setProblem(error);\n } finally {\n if (still()) setWriting(false);\n }\n },\n [client, agentId],\n );\n\n /**\n * Reads the words and the facts, and how each automation stands (a server without them leaves\n * the rows as they were told); when the words are missing or stale, has the agent write them.\n */\n const read = useCallback(\n async (still: () => boolean) => {\n setProblem(null);\n try {\n const [first, standing] = await Promise.all([\n client.agents.about(agentId),\n client.automations.list(agentId).catch(() => null),\n ]);\n if (!still()) return;\n setData(first);\n setLive(standing);\n // Words never written, or written before the agent last changed: it writes them again\n // by itself, so what it says stays true without anyone asking.\n if (first.about === null || first.about_at === null) await write(still);\n } catch (error) {\n if (still()) setProblem(error);\n }\n },\n [client, agentId, write],\n );\n\n useEffect(() => {\n let gone = false;\n void read(() => !gone);\n return () => {\n gone = true;\n };\n }, [read]);\n\n const tools = useMemo<AboutTools>(\n () => ({\n refresh: async () => {\n setVersion((count) => count + 1);\n await read(() => true);\n },\n }),\n [read],\n );\n // What to try again after a failure: the reading when nothing was read, the writing otherwise.\n const retry = () => (data === null ? read(() => true) : write(() => true));\n // A tool by the owner's words for it; one of its own code, by its name made words.\n const label = (action: ActionFact) =>\n action.source === \"own\" ? toolName(action.name) : (words.actions[action.name] ?? action.name);\n const pane = (bar: ReactNode, children: ReactNode) => (\n <div ref={root} className={`sc ${classes(classNames, \"sc-about\")}`}>\n <div key={shown} data-motion={motion} className={classes(classNames, \"sc-about__pane\")}>\n {bar}\n {children}\n </div>\n </div>\n );\n const home = words.title(name);\n\n if (view.kind === \"topic\" && data !== null) {\n return pane(\n <Bar back={home} onBack={() => go(ABOUT_HOME)} classNames={classNames} />,\n <TopicView\n client={client}\n agentId={agentId}\n name={name}\n version={version}\n topic={view.topic}\n facts={data.facts}\n apps={apps}\n label={label}\n words={words}\n classNames={classNames}\n tools={tools}\n controls={controls}\n onOpenFile={onOpenFile}\n />,\n );\n }\n\n if (view.kind === \"automation\") {\n const fact = data?.facts.automations.find((each) => each.name === view.name);\n return pane(\n <Bar\n back={home}\n onBack={() => go(ABOUT_HOME)}\n end={fact !== undefined ? controls?.automation?.(fact, tools) : undefined}\n classNames={classNames}\n />,\n <AutomationPane\n key={version}\n client={client}\n agentId={agentId}\n name={view.name}\n apps={apps}\n words={words}\n classNames={classNames}\n onOpenRun={(run) => go({ kind: \"run\", name: view.name, run: run.id })}\n />,\n );\n }\n\n if (view.kind === \"run\") {\n return pane(\n <Bar\n back={live?.find((each) => each.name === view.name)?.title ?? home}\n onBack={() => go({ kind: \"automation\", name: view.name })}\n classNames={classNames}\n />,\n <RunPane\n key={version}\n client={client}\n agentId={agentId}\n name={view.name}\n run={view.run}\n apps={apps}\n words={words}\n classNames={classNames}\n onOpenConversation={onOpenConversation}\n />,\n );\n }\n\n const state = data === null ? null : aboutState(data.about, data.about_at, writing);\n return pane(\n null,\n <>\n <section aria-labelledby={intro} className={classes(classNames, \"sc-about__intro\")}>\n <AgentName\n id={intro}\n agentId={agentId}\n icon={icon}\n name={name}\n label={home}\n classNames={classNames}\n />\n {problem !== null ? (\n <Problem words={words} classNames={classNames} onRetry={retry}>\n {words.failed}\n {problem instanceof ApiError && ` ${problem.detail}`}\n </Problem>\n ) : data === null || data.about === null ? (\n <Writing\n text={data === null ? words.loading : words.writing(name)}\n classNames={classNames}\n />\n ) : (\n <p className={classes(classNames, \"sc-about__lead\")}>{data.about.summary}</p>\n )}\n {state === \"writing\" && data?.about != null && (\n <p role=\"status\" className={classes(classNames, \"sc-about__updating\")}>\n {words.updating}\n </p>\n )}\n </section>\n {data !== null && (\n <>\n <Automations\n apps={apps}\n told={automationsOf(data.about, data.facts)}\n live={live}\n words={words}\n classNames={classNames}\n tools={tools}\n controls={controls}\n onOpen={(automation) => go({ kind: \"automation\", name: automation.name })}\n />\n {data.about !== null && data.about.asks.length > 0 && (\n <Asks asks={data.about.asks} words={words} classNames={classNames} />\n )}\n <nav aria-label={words.facts} className={classes(classNames, \"sc-about__menu\")}>\n <p className={classes(classNames, \"sc-about__label\")}>{words.facts}</p>\n <div className={classes(classNames, \"sc-about__topics\")}>\n {HOME_TOPICS.map((topic) => (\n <TopicRow\n key={topic}\n topic={topic}\n facts={data.facts}\n apps={apps}\n words={words}\n classNames={classNames}\n onOpen={() => go({ kind: \"topic\", topic })}\n />\n ))}\n </div>\n </nav>\n </>\n )}\n </>,\n );\n}\n\n/**\n * What the panel's home shows the agent works with, in the order the owner looks for it: where\n * people reach it, the apps it uses, what it knows, what it learned, its keys. Its tools are\n * the platform's business: they show in the technical view, not here.\n */\nconst HOME_TOPICS: readonly Topic[] = [\"channels\", \"apps\", \"knowledge\", \"learning\", \"keys\"];\n\n/** How many icons a topic's row draws before \"+N\". */\nconst MOST_MARKS = 5;\n\n/**\n * A topic on the panel's home, as icons: its name, then what it holds as their logos or marks\n * (the channels', the apps', the files') and how many; a topic with nothing says so, quietly.\n * Pressing it opens the topic.\n */\nfunction TopicRow({\n topic,\n facts,\n apps,\n words,\n classNames,\n onOpen,\n}: {\n topic: Topic;\n facts: Facts;\n apps: AppDirectory | undefined;\n words: AboutMessages;\n classNames: ClassNames;\n onOpen: () => void;\n}) {\n const count = countOf(topic, facts);\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n data-empty={count === 0 || undefined}\n className={classes(classNames, \"sc-about__topic\")}\n >\n <span className={classes(classNames, \"sc-about__topic-name\")}>{words.topics[topic]}</span>\n {count === 0 ? (\n <span className={classes(classNames, \"sc-about__topic-none\")}>{words.empty[topic]}</span>\n ) : (\n <>\n <TopicMarks topic={topic} facts={facts} apps={apps} words={words} count={count} />\n <span className={classes(classNames, \"sc-about__topic-count\")}>{count}</span>\n </>\n )}\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden\n className={classes(classNames, \"sc-about__topic-go\")}\n >\n <path\n d=\"M6 3.5 10.5 8 6 12.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n );\n}\n\n/** What a topic holds, as icons: each channel's and app's logo, each file's mark; else its glyph. */\nfunction TopicMarks({\n topic,\n facts,\n apps,\n words,\n count,\n}: {\n topic: Topic;\n facts: Facts;\n apps: AppDirectory | undefined;\n words: AboutMessages;\n count: number;\n}) {\n const marks: ReactNode[] =\n topic === \"channels\"\n ? facts.channels.map((channel) => (\n <span key={`${channel.kind}:${channel.name}`}>\n {channelLogo(channel.kind, apps, words, 20)}\n </span>\n ))\n : topic === \"apps\"\n ? facts.connections.map((connection) => (\n <span key={connection.name}>{appMark(connectionApp(connection), apps, 20)}</span>\n ))\n : topic === \"knowledge\"\n ? facts.files.map((file) => <FileMark key={file.path} name={file.path} size={20} />)\n : [<Mark key={topic} icon={TOPIC_GLYPHS[topic]} size={20} />];\n const shown = marks.slice(0, MOST_MARKS);\n const drawn = topic === \"learning\" || topic === \"keys\" ? 0 : count;\n return <MarkStack more={Math.max(0, drawn - shown.length)}>{shown}</MarkStack>;\n}\n\n/**\n * Who the panel is about, at its top: the agent's mark in its tint, bare, beside its name; the\n * panel's title (\"About Ana\") stays for screen readers.\n */\nfunction AgentName({\n id,\n agentId,\n icon,\n name,\n label,\n classNames,\n}: {\n id: string;\n agentId: string;\n icon: string | null | undefined;\n name: string;\n label: string;\n classNames: ClassNames;\n}) {\n const Glyph = agentGlyph(icon);\n return (\n <h2\n id={id}\n aria-label={label}\n data-tint={agentTint(agentId)}\n className={classes(classNames, \"sc-about__name\")}\n >\n <Glyph aria-hidden weight=\"duotone\" />\n <span>{name}</span>\n </h2>\n );\n}\n\n/**\n * Which way the panel moved to show `view`: in (one level deeper), back out, or nowhere (the first\n * view). Kept per view, so a view's own updates never replay it.\n */\nfunction useMotion(view: AboutView): \"in\" | \"out\" | undefined {\n const key = keyOf(view);\n const depth = depthOf(view);\n const [moved, setMoved] = useState<{\n key: string;\n depth: number;\n motion: \"in\" | \"out\" | undefined;\n }>({ key, depth, motion: undefined });\n if (moved.key !== key) {\n setMoved({ key, depth, motion: depth >= moved.depth ? \"in\" : \"out\" });\n }\n return moved.motion;\n}\n\n/** The nearest box around `element` that scrolls: where the panel's place is kept. */\nfunction scrollerOf(element: HTMLElement | null): HTMLElement | null {\n for (let at = element?.parentElement ?? null; at !== null; at = at.parentElement) {\n const overflow = getComputedStyle(at).overflowY;\n if (overflow === \"auto\" || overflow === \"scroll\") return at;\n }\n return null;\n}\n\n/**\n * The bar over a view opened from another: the way back to it, named, at its start; the host's\n * controls at its end. It stays at the top as the view scrolls.\n */\nfunction Bar({\n back,\n onBack,\n end,\n classNames,\n}: {\n back: string;\n onBack: () => void;\n end?: ReactNode;\n classNames: ClassNames;\n}) {\n return (\n <div className={classes(classNames, \"sc-about__bar\")}>\n <button type=\"button\" onClick={onBack} className={classes(classNames, \"sc-about__back\")}>\n <svg viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden>\n <path\n d=\"M10 3.5 5.5 8l4.5 4.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n <span>{back}</span>\n </button>\n {end != null && <span className={classes(classNames, \"sc-about__bar-end\")}>{end}</span>}\n </div>\n );\n}\n\n/**\n * An automation, open in the panel: what starts it and when it runs next, the owner's sentence,\n * its steps, the table it moves, its runs (each opening its record). Read again every few\n * seconds while a run of it is under way.\n */\nfunction AutomationPane({\n client,\n agentId,\n name,\n apps,\n words,\n classNames,\n onOpenRun,\n}: Drawn & {\n client: Client;\n agentId: string;\n name: string;\n apps: AppDirectory | undefined;\n onOpenRun: (run: Run) => void;\n}) {\n const { messages } = useKit();\n const read = usePolled(\n () => client.automations.get(agentId, name),\n `${agentId}:${name}`,\n (automation) => automation.runs.some((run) => run.status === \"running\"),\n );\n if (read.gone) return <Gone classNames={classNames}>{messages.automation.gone}</Gone>;\n if (read.value === null) {\n return read.problem === null ? (\n <Loading words={words} classNames={classNames} />\n ) : (\n <Gone classNames={classNames}>{words.failed}</Gone>\n );\n }\n return (\n <Automation\n className={classes(classNames, \"sc-about__automation\")}\n automation={read.value}\n runs={read.value.runs}\n apps={apps}\n onOpenRun={onOpenRun}\n />\n );\n}\n\n/**\n * A run, open in the panel: how it went, step by step in words, what it opened. Read with its\n * automation (its steps' order and its apps' names), again while it is under way.\n */\nfunction RunPane({\n client,\n agentId,\n name,\n run,\n apps,\n words,\n classNames,\n onOpenConversation,\n}: Drawn & {\n client: Client;\n agentId: string;\n name: string;\n run: string;\n apps: AppDirectory | undefined;\n onOpenConversation: ((id: string) => void) | undefined;\n}) {\n const read = usePolled(\n async () => {\n const [record, automation] = await Promise.all([\n client.runs.get(run),\n client.automations.get(agentId, name).catch(() => null),\n ]);\n return { record, automation };\n },\n run,\n ({ record }) => record.status === \"running\",\n );\n if (read.gone) return <Gone classNames={classNames}>{words.runGone}</Gone>;\n if (read.value === null) {\n return read.problem === null ? (\n <Loading words={words} classNames={classNames} />\n ) : (\n <Gone classNames={classNames}>{words.failed}</Gone>\n );\n }\n return (\n <RunView\n className={classes(classNames, \"sc-about__run\")}\n run={read.value.record}\n automation={read.value.automation}\n apps={apps}\n onOpenConversation={onOpenConversation}\n />\n );\n}\n\n/** A view whose thing is being read: lines with a light sweeping them. */\nfunction Loading({ words, classNames }: Drawn) {\n return <Writing text={words.loading} classNames={classNames} />;\n}\n\n/** In place of what is no longer there, or could not be read. */\nfunction Gone({ classNames, children }: { classNames: ClassNames; children: ReactNode }) {\n return <p className={classes(classNames, \"sc-about__empty\")}>{children}</p>;\n}\n\n/** An automation's mark says its kind: a time, a call from another system, an event, a\n * message. */\nconst KIND_ICONS: Readonly<Record<AutomationKind, Icon>> = {\n schedule: \"clock\",\n webhook: \"code\",\n event: \"bell\",\n platform: \"message\",\n};\n\n/** An app's mark: the host's logo when it draws one, else the provider's, bare. */\nfunction appMark(\n app: { provider: string; name: string; icon?: string | null | undefined },\n apps: AppDirectory | undefined,\n size: number,\n) {\n if (apps !== undefined) return apps.logo({ ...app, size });\n return (\n <Mark logo size={size}>\n {providerMark(app.provider.split(\":\")[0] as Provider, app.icon ?? undefined)}\n </Mark>\n );\n}\n\nconst connectionApp = (connection: ConnectionFact) => ({\n provider: `${connection.provider}:${connection.toolkit ?? connection.name}`,\n name: appTitle(connection),\n icon: connection.icon,\n});\n\n/**\n * A channel's mark: the host's logo for its brand when it draws apps (WhatsApp, Instagram, the\n * plain mark of a text message), else the package's glyph, bare.\n */\nfunction channelLogo(\n kind: ChannelKind,\n apps: AppDirectory | undefined,\n words: AboutMessages,\n size: number,\n) {\n if (apps !== undefined)\n return apps.logo({ provider: kind, name: words.channelNames[kind], size });\n return <Mark size={size}>{channelMark(kind)}</Mark>;\n}\n\ninterface Drawn {\n words: AboutMessages;\n classNames: ClassNames;\n}\n\n/** What could not be read or written, in the warning tone, and the button to try again. */\nfunction Problem({\n words,\n classNames,\n onRetry,\n children,\n}: Drawn & { onRetry: () => Promise<void>; children: ReactNode }) {\n return (\n <p role=\"alert\" className={classes(classNames, \"sc-about__updated\")}>\n <span>{children}</span>\n <button type=\"button\" onClick={() => void onRetry()}>\n {words.refresh}\n </button>\n </p>\n );\n}\n\n/** The agent writing: the lead's lines with a light sweeping them, and the sentence saying so. */\nfunction Writing({ text, classNames }: { text: string; classNames: ClassNames }) {\n return (\n <div role=\"status\" className={classes(classNames, \"sc-about__writing\")}>\n <div aria-hidden>\n <i />\n <i />\n </div>\n <p>{text}</p>\n </div>\n );\n}\n\n/** What it asks leave for: its label, then the things, a short list. */\nfunction Asks({ asks, words, classNames }: Drawn & { asks: string[] }) {\n const id = useId();\n return (\n <section\n aria-labelledby={id}\n className={classes(classNames, \"sc-about__section\", \"sc-about__asks\")}\n >\n <div className={classes(classNames, \"sc-about__label\")}>\n <h3 id={id}>{words.asks}</h3>\n </div>\n <ul>\n {asks.map((ask, index) => (\n <li key={index}>{ask}</li>\n ))}\n </ul>\n </section>\n );\n}\n\n/**\n * What it does on its own: one row per automation, its kind's mark (or, on an app's event, the\n * app's logo), what it does then over when, as the agent tells it (its title and its kind until\n * it has), a paused one quieter; how it stands now when that needs a look (a run under way, the\n * last one failed); the host's control at each row's end, and over them the way to a new one. A\n * row opens its automation.\n */\nfunction Automations({\n told,\n live,\n words,\n classNames,\n tools,\n controls,\n apps,\n onOpen,\n}: Drawn & {\n told: Told[];\n live: readonly Standing[] | null;\n tools: AboutTools;\n controls: AboutControls | undefined;\n apps: AppDirectory | undefined;\n onOpen: (automation: AutomationFact) => void;\n}) {\n const id = useId();\n const { messages } = useKit();\n const standing = new Map(live?.map((automation) => [automation.name, automation]));\n return (\n <section aria-labelledby={id} className={classes(classNames, \"sc-about__section\")}>\n <div className={classes(classNames, \"sc-about__label\")}>\n <h3 id={id}>{words.does}</h3>\n {controls?.newAutomation !== undefined && (\n <span className={classes(classNames, \"sc-about__tools\")}>\n {controls.newAutomation(tools)}\n </span>\n )}\n </div>\n {told.length === 0 ? (\n <p className={classes(classNames, \"sc-about__empty\")}>{words.noAutomations}</p>\n ) : (\n <List>\n {told.map(({ automation, line }) => {\n const when = line?.when ?? words.kinds[automation.kind];\n const now = standing.get(automation.name);\n const going = now?.last_run?.status === \"running\";\n return (\n <Row\n key={automation.id}\n mark={\n automation.provider === undefined || automation.provider === null ? (\n <Mark icon={GLYPHS[KIND_ICONS[automation.kind]]} />\n ) : (\n appMark(\n {\n provider: automation.provider,\n name:\n automation.app_title ?? toolName(automation.app ?? automation.provider),\n icon: automation.icon,\n },\n apps,\n 28,\n )\n )\n }\n title={line?.text ?? automation.title}\n subtitle={when}\n error={now?.state === \"failing\" ? messages.automation.states.failing : undefined}\n meta={\n going ? (\n <Tag tone=\"info\">\n <i aria-hidden className={classes(classNames, \"sc-run__live\")} />\n {messages.automation.statuses.running}\n </Tag>\n ) : automation.on ? undefined : (\n <Tag>{words.paused}</Tag>\n )\n }\n quiet={!automation.on}\n onPress={() => onOpen(automation)}\n trailing={controls?.automation?.(automation, tools)}\n />\n );\n })}\n </List>\n )}\n {controls?.automationsNote?.(tools)}\n </section>\n );\n}\n\n/**\n * One topic, open: its name with its count and the host's control, a line saying what it is, then\n * its rows (what it knows as its files and its tables; what it learned; the rest with a mark and\n * a note), or its one line when it holds nothing.\n */\nfunction TopicView({\n client,\n agentId,\n name,\n version,\n topic,\n facts,\n apps,\n label,\n words,\n classNames,\n tools,\n controls,\n onOpenFile,\n}: Drawn & {\n client: Client;\n agentId: string;\n /** The agent's name, over what is its own. */\n name: string;\n /** Moves with every change the host makes: what the topic reads itself, it reads again. */\n version: number;\n topic: Topic;\n facts: Facts;\n apps: AppDirectory | undefined;\n label: (action: ActionFact) => string;\n tools: AboutTools;\n controls: AboutControls | undefined;\n onOpenFile: ((file: FileFact) => void) | undefined;\n}) {\n const id = useId();\n const tool = controls?.topic?.[topic];\n const count = countOf(topic, facts);\n // The tools are all listed, the ones that are off too: they are what the owner turns on.\n const shown = topic === \"tools\" ? facts.actions.length : count;\n return (\n <section aria-labelledby={id} className={classes(classNames, \"sc-about__group\")}>\n <h3 id={id} className={classes(classNames, \"sc-about__title\")}>\n <span>{words.topics[topic]}</span>\n {count > 0 && <b className={classes(classNames, \"sc-about__count\")}>{count}</b>}\n {tool !== undefined && (\n <span className={classes(classNames, \"sc-about__tools\")}>{tool(tools)}</span>\n )}\n </h3>\n <p className={classes(classNames, \"sc-about__hint\")}>{words.hints[topic]}</p>\n {topic === \"channels\" ? (\n <Channels\n client={client}\n agentId={agentId}\n version={version}\n facts={facts}\n apps={apps}\n words={words}\n classNames={classNames}\n tools={tools}\n controls={controls}\n />\n ) : shown === 0 ? (\n <p className={classes(classNames, \"sc-about__empty\")}>{words.empty[topic]}</p>\n ) : topic === \"knowledge\" ? (\n <Knowledge\n name={name}\n facts={facts}\n words={words}\n classNames={classNames}\n tools={tools}\n controls={controls}\n onOpenFile={onOpenFile}\n />\n ) : topic === \"tools\" ? (\n <Tools\n name={name}\n facts={facts}\n label={label}\n words={words}\n classNames={classNames}\n tools={tools}\n controls={controls}\n />\n ) : topic === \"keys\" ? (\n <Keys\n name={name}\n facts={facts}\n words={words}\n classNames={classNames}\n tools={tools}\n controls={controls}\n />\n ) : topic === \"learning\" ? (\n <List>\n {facts.memories.map((memory) => (\n <Row\n key={memory.id}\n className={classes(classNames, \"sc-about__memory\")}\n title={memory.text}\n subtitle={words.ago(new Date(memory.created_at))}\n trailing={controls?.memory?.(memory, tools)}\n />\n ))}\n </List>\n ) : (\n <Rows items={appItems(facts, tools, controls, apps)} classNames={classNames} />\n )}\n </section>\n );\n}\n\n/** How each state of a channel reads beside its dot: working, waiting, broken, or off. */\nconst CHANNEL_TONES: Readonly<Record<ChannelStatus, StatusTone | \"wait\">> = {\n active: \"on\",\n pending: \"wait\",\n error: \"off\",\n disconnected: \"quiet\",\n};\n\nconst isStatus = (status: string): status is ChannelStatus => status in CHANNEL_TONES;\n\n/** The order channels are listed in: by kind, as people reach a business most; disconnected last. */\nconst KIND_ORDER: readonly ChannelKind[] = [\n \"whatsapp\",\n \"instagram\",\n \"sms\",\n \"email\",\n \"web\",\n \"phone\",\n];\n\nconst inOrder = (channels: readonly Channel[]): Channel[] =>\n [...channels].sort(\n (a, b) =>\n Number(a.status === \"disconnected\") - Number(b.status === \"disconnected\") ||\n KIND_ORDER.indexOf(a.kind) - KIND_ORDER.indexOf(b.kind),\n );\n\n/**\n * Where it works: each channel it answers, as the API lists them (WhatsApp, Instagram, SMS; the\n * disconnected ones last, quieter), by its brand's mark and name, its number or handle, how it stands with the\n * provider's word on it, and the host's control at its end; then the devices it uses. Read\n * when the topic opens and after each change the host makes; until then, or on a server without\n * the list, the facts' rows.\n */\nfunction Channels({\n client,\n agentId,\n version,\n facts,\n apps,\n words,\n classNames,\n tools,\n controls,\n}: Drawn & {\n client: Client;\n agentId: string;\n version: number;\n facts: Facts;\n apps: AppDirectory | undefined;\n tools: AboutTools;\n controls: AboutControls | undefined;\n}) {\n const [listed, setListed] = useState<readonly Channel[] | null>(null);\n useEffect(() => {\n let gone = false;\n client.channels.list(agentId).then(\n (channels) => {\n if (!gone) setListed(channels);\n },\n () => undefined,\n );\n return () => {\n gone = true;\n };\n }, [client, agentId, version]);\n\n const state = (status: string) => {\n const known = isStatus(status);\n return (\n <span\n data-tone={known ? CHANNEL_TONES[status] : toneOf(status)}\n className={classes(classNames, \"sc-about__state\")}\n >\n <i aria-hidden className={classes(classNames, \"sc-about__dot\")} />\n {known ? words.channelStates[status] : status}\n </span>\n );\n };\n // Its number or handle, and under it the provider's word on it; on an error, that word is why.\n const told = (channel: Channel) => {\n const detail = channel.detail === null ? null : words.channelDetail(channel.detail);\n const error = channel.status === \"error\";\n return {\n subtitle: (\n <>\n {channel.name}\n {!error && detail !== null && detail !== \"\" && (\n <span className={classes(classNames, \"sc-about__detail\")}>{detail}</span>\n )}\n </>\n ),\n error: error ? (detail ?? undefined) : undefined,\n };\n };\n const rows =\n listed === null\n ? facts.channels.map((channel: ChannelFact) => (\n <Row\n key={`${channel.kind}:${channel.name}`}\n mark={channelLogo(channel.kind, apps, words, 28)}\n title={words.channelNames[channel.kind]}\n subtitle={channel.name}\n meta={state(channel.status)}\n />\n ))\n : inOrder(listed).map((channel) => (\n <Row\n key={channel.id}\n mark={channelLogo(channel.kind, apps, words, 28)}\n title={words.channelNames[channel.kind]}\n {...told(channel)}\n meta={state(channel.status)}\n quiet={channel.status === \"disconnected\"}\n trailing={controls?.channel?.(channel, tools) ?? undefined}\n />\n ));\n const devices = facts.devices.map((device) => (\n <Row\n key={`device:${device.kind}:${device.name}`}\n mark={<Mark>{deviceMark(device.kind)}</Mark>}\n title={device.name}\n meta={\n <i\n aria-hidden\n data-tone={toneOf(device.status)}\n className={classes(classNames, \"sc-about__dot\")}\n />\n }\n />\n ));\n if (rows.length + devices.length === 0) {\n return <p className={classes(classNames, \"sc-about__empty\")}>{words.empty.channels}</p>;\n }\n return (\n <List>\n {rows}\n {devices}\n </List>\n );\n}\n\n/**\n * One row of a topic: a mark, a name, a quieter line (what an app may do there, a note), a dot\n * for a status, and the host's control at its end.\n */\ninterface Item {\n key: string;\n mark: ReactNode;\n label: string;\n note?: string | undefined;\n status?: StatusTone | undefined;\n off?: boolean | undefined;\n control?: ReactNode;\n}\n\n/** Its apps: each by its logo and name, what it may do there under it, how it stands at its end. */\nfunction appItems(\n facts: Facts,\n tools: AboutTools,\n controls: AboutControls | undefined,\n apps: AppDirectory | undefined,\n): Item[] {\n return facts.connections.map((connection) => ({\n key: `${connection.provider}:${connection.name}`,\n mark: appMark(connectionApp(connection), apps, 28),\n label: appTitle(connection),\n note: connection.actions.map((action) => toolTitle(connection, action)).join(\", \"),\n status: connection.status === undefined ? undefined : toneOf(connection.status),\n control: controls?.app?.(connection, tools),\n }));\n}\n\n/**\n * What it can do while it talks: its own code's actions, then the ones every agent comes with\n * (each under its heading when it has both); in each, the ones it has first, then the ones the\n * owner can turn on, quieter, saying so.\n */\nfunction Tools({\n name,\n facts,\n label,\n words,\n classNames,\n tools,\n controls,\n}: Drawn & {\n name: string;\n facts: Facts;\n label: (action: ActionFact) => string;\n tools: AboutTools;\n controls: AboutControls | undefined;\n}) {\n const groups = [\n { key: \"own\", title: words.theirs(name), actions: facts.actions.filter(isOwn) },\n { key: \"built\", title: words.builtIn, actions: facts.actions.filter((a) => !isOwn(a)) },\n ].filter((group) => group.actions.length > 0);\n const rows = (actions: readonly ActionFact[]) => (\n <Rows\n items={[...actions.filter((a) => a.on), ...actions.filter((a) => !a.on)].map((action) => ({\n key: `${action.source}:${action.name}`,\n mark: <Mark>{actionMark(action.name, action.source)}</Mark>,\n label: label(action),\n note: action.on ? undefined : words.off,\n off: !action.on,\n control: controls?.tool?.(action, tools),\n }))}\n classNames={classNames}\n />\n );\n if (groups.length === 1) return rows(groups[0]?.actions ?? []);\n return (\n <>\n {groups.map((group) => (\n <Group key={group.key} title={group.title} classNames={classNames}>\n {rows(group.actions)}\n </Group>\n ))}\n </>\n );\n}\n\nconst isOwn = (action: ActionFact): boolean => action.source === \"own\";\n\n/** A part of a topic under its small heading: whose things they are, or what kind. */\nfunction Group({\n title,\n classNames,\n children,\n}: {\n title: string;\n classNames: ClassNames;\n children: ReactNode;\n}) {\n return (\n <>\n <h4 className={classes(classNames, \"sc-about__subtitle\")}>{title}</h4>\n {children}\n </>\n );\n}\n\n/** What is the agent's own, then the organization's: the heading says whose, once. */\nfunction byOwner<T extends { shared?: boolean | undefined }>(\n things: readonly T[],\n name: string,\n words: AboutMessages,\n): { key: string; title: string; things: T[] }[] {\n return [\n { key: \"own\", title: words.theirs(name), things: things.filter((thing) => !thing.shared) },\n {\n key: \"shared\",\n title: words.organization,\n things: things.filter((thing) => thing.shared === true),\n },\n ].filter((group) => group.things.length > 0);\n}\n\n/**\n * What it knows, by whose it is: its own files, then the organization's (shared with every\n * agent: the heading says so, once), then the tables it reads or writes. A file is its kind's\n * mark, its name on one line, its folder under it (and that it is only kept, when `search_files`\n * does not read it), its size at the right; pressed, the host opens it; the host's controls at\n * its end.\n */\nfunction Knowledge({\n name,\n facts,\n words,\n classNames,\n tools,\n controls,\n onOpenFile,\n}: Drawn & {\n name: string;\n facts: Facts;\n tools: AboutTools;\n controls: AboutControls | undefined;\n onOpenFile: ((file: FileFact) => void) | undefined;\n}) {\n return (\n <>\n {byOwner(facts.files, name, words).map((group) => (\n <Group key={group.key} title={group.title} classNames={classNames}>\n <List>\n {group.things.map((file) => (\n <Row\n key={file.path}\n className={classes(classNames, \"sc-about__file\")}\n mark={<FileMark name={file.path} />}\n title={<span title={file.path}>{baseName(file.path)}</span>}\n subtitle={fileLine(file, words)}\n meta={words.size(file.size)}\n onPress={onOpenFile && (() => onOpenFile(file))}\n trailing={controls?.file?.(file, tools) ?? undefined}\n />\n ))}\n </List>\n </Group>\n ))}\n {facts.data.length > 0 && (\n <Group title={words.tables} classNames={classNames}>\n <Rows\n items={facts.data.map((table) => ({\n key: table.table,\n mark: <Mark icon={GLYPHS.table} />,\n label: table.title ?? table.table,\n note: table.can.map((can) => words.can[can] ?? can).join(\", \"),\n control: controls?.table?.(table, tools),\n }))}\n classNames={classNames}\n />\n </Group>\n )}\n </>\n );\n}\n\n/** Under a file's name: its folder, and that it is only kept when nothing reads it at a search. */\nfunction fileLine(file: FileFact, words: AboutMessages): string | undefined {\n const folder = folderOf(file.path);\n if (file.searchable) return folder === \"\" ? undefined : folder;\n return folder === \"\" ? words.kept : `${folder} · ${words.kept}`;\n}\n\n/** Its keys, its own then the organization's, each with where it may be sent. */\nfunction Keys({\n name,\n facts,\n words,\n classNames,\n tools,\n controls,\n}: Drawn & {\n name: string;\n facts: Facts;\n tools: AboutTools;\n controls: AboutControls | undefined;\n}) {\n return (\n <>\n {byOwner(facts.vault, name, words).map((group) => (\n <Group key={group.key} title={group.title} classNames={classNames}>\n <Rows\n items={group.things.map((secret) => ({\n key: `${group.key}:${secret.name}`,\n mark: <Mark icon={GLYPHS.lock} />,\n label: secret.name,\n note: secret.hosts?.join(\", \"),\n control: controls?.key?.(secret, tools),\n }))}\n classNames={classNames}\n />\n </Group>\n ))}\n </>\n );\n}\n\n/** The folders a file sits in: \"\" for one at the top. */\nconst folderOf = (path: string): string => path.slice(0, Math.max(0, path.lastIndexOf(\"/\")));\n\nfunction Rows({ items, classNames }: { items: Item[]; classNames: ClassNames }) {\n return (\n <List>\n {items.map((item) => (\n <Row\n key={item.key}\n mark={item.mark}\n title={item.label}\n subtitle={item.note === undefined || item.note === \"\" ? undefined : item.note}\n meta={\n item.status === undefined ? undefined : (\n <i\n aria-hidden\n data-tone={item.status}\n className={classes(classNames, \"sc-about__dot\")}\n />\n )\n }\n quiet={item.off}\n trailing={item.control ?? undefined}\n />\n ))}\n </List>\n );\n}\n"],"mappings":"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"}
|
package/dist/AgentTasks.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { classes } from "./classes.js";
|
|
2
2
|
import { Automation } from "./automation/Automation.js";
|
|
3
3
|
import { Button } from "./kit/Button.js";
|
|
4
|
+
import { taskCard } from "./automation/types.js";
|
|
4
5
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
5
6
|
import { ArrowLeftIcon, TrashIcon } from "@phosphor-icons/react";
|
|
6
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -96,7 +97,7 @@ function AgentTasks({ client, agentId, tasks, onChange, onBack, words, className
|
|
|
96
97
|
}) : /* @__PURE__ */ jsx("div", {
|
|
97
98
|
className: classes(classNames, "sc-tasks__list"),
|
|
98
99
|
children: tasks.map((task) => /* @__PURE__ */ jsx(Automation, {
|
|
99
|
-
automation: task,
|
|
100
|
+
automation: taskCard(task),
|
|
100
101
|
runs: task.runs,
|
|
101
102
|
switching: busy === task.id,
|
|
102
103
|
onSwitch: (on) => void change(task, () => client.tasks.switch(agentId, task.id, on)),
|
package/dist/AgentTasks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentTasks.js","names":[],"sources":["../src/AgentTasks.tsx"],"sourcesContent":["import { ArrowLeftIcon, TrashIcon } from \"@phosphor-icons/react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { Automation } from \"./automation/Automation\";\nimport type
|
|
1
|
+
{"version":3,"file":"AgentTasks.js","names":[],"sources":["../src/AgentTasks.tsx"],"sourcesContent":["import { ArrowLeftIcon, TrashIcon } from \"@phosphor-icons/react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { Automation } from \"./automation/Automation\";\nimport { taskCard, type Task } from \"./automation/types\";\nimport { classes } from \"./classes\";\nimport type { Client } from \"./client\";\nimport { Button } from \"./kit/Button\";\n\n/** What the person's tasks say, in the space; a host gives its own, in its language. */\nexport interface TaskMessages {\n /** Over the chat, when the agent runs any for them: how many, to open them. */\n open: (count: number) => string;\n /** Over the list. */\n title: string;\n back: string;\n /** Taking one away. */\n remove: string;\n /** None left. */\n empty: string;\n /** A change that didn't go through. */\n failed: string;\n}\n\nexport const TASK_MESSAGES: TaskMessages = {\n open: (count) => (count === 1 ? \"1 task set up for you\" : `${count} tasks set up for you`),\n title: \"Your tasks\",\n back: \"Back to the conversation\",\n remove: \"Remove\",\n empty: \"Nothing set up. Ask the agent to remind you of something, or to check back later.\",\n failed: \"That didn't go through. Try again.\",\n};\n\nexport type TaskPart =\n | \"sc-tasks\"\n | \"sc-tasks__head\"\n | \"sc-tasks__title\"\n | \"sc-tasks__list\"\n | \"sc-tasks__empty\"\n | \"sc-tasks__problem\";\n\n/** How often the person's tasks are read again while the space is open. */\nexport const TASKS_POLL_MS = 20_000;\n\n/**\n * The tasks the agent runs for the person the client speaks for, read when the space opens,\n * again every `TASKS_POLL_MS` and on `refresh`. Null until read, and whenever it can't be (an API\n * without tasks, the business's own client): the space then shows none.\n */\nexport function useTasks(\n client: Client,\n agentId: string,\n enabled: boolean,\n): { tasks: Task[] | null; refresh: () => Promise<void> } {\n const [tasks, setTasks] = useState<Task[] | null>(null);\n const live = useRef(true);\n const refresh = useCallback(async () => {\n try {\n const read = await client.tasks.list(agentId);\n if (live.current) setTasks(read);\n } catch {\n if (live.current) setTasks(null);\n }\n }, [client, agentId]);\n useEffect(() => {\n live.current = true;\n if (!enabled) return;\n void refresh();\n const timer = setInterval(() => void refresh(), TASKS_POLL_MS);\n return () => {\n live.current = false;\n clearInterval(timer);\n };\n }, [enabled, refresh]);\n return { tasks: enabled ? tasks : null, refresh };\n}\n\nexport interface AgentTasksProps {\n client: Client;\n agentId: string;\n tasks: readonly Task[];\n /** Read them again, after a change. */\n onChange: () => Promise<void>;\n onBack: () => void;\n words: TaskMessages;\n classNames?: Partial<Record<TaskPart, string>> | undefined;\n}\n\n/**\n * The person's tasks, as they asked the agent for them: each as an automation reads (when, its\n * words, its last runs and how each went), with their switch to pause it and the way to take it\n * away; the way back to the conversation over them.\n */\nexport function AgentTasks({\n client,\n agentId,\n tasks,\n onChange,\n onBack,\n words,\n classNames,\n}: AgentTasksProps) {\n const [busy, setBusy] = useState<string | null>(null);\n const [failed, setFailed] = useState(false);\n const change = async (task: Task, doing: () => Promise<unknown>) => {\n setBusy(task.id);\n setFailed(false);\n try {\n await doing();\n await onChange();\n } catch {\n setFailed(true);\n } finally {\n setBusy(null);\n }\n };\n return (\n <section aria-label={words.title} part=\"tasks\" className={classes(classNames, \"sc-tasks\")}>\n <header className={classes(classNames, \"sc-tasks__head\")}>\n <Button\n tone=\"ghost\"\n size=\"sm\"\n icon={ArrowLeftIcon}\n aria-label={words.back}\n title={words.back}\n onClick={onBack}\n />\n <h3 className={classes(classNames, \"sc-tasks__title\")}>{words.title}</h3>\n </header>\n {failed && (\n <p role=\"alert\" className={classes(classNames, \"sc-tasks__problem\")}>\n {words.failed}\n </p>\n )}\n {tasks.length === 0 ? (\n <p className={classes(classNames, \"sc-tasks__empty\")}>{words.empty}</p>\n ) : (\n <div className={classes(classNames, \"sc-tasks__list\")}>\n {tasks.map((task) => (\n <Automation\n key={task.id}\n automation={taskCard(task)}\n runs={task.runs}\n switching={busy === task.id}\n onSwitch={(on) => void change(task, () => client.tasks.switch(agentId, task.id, on))}\n actions={\n <Button\n tone=\"ghost\"\n size=\"sm\"\n icon={TrashIcon}\n aria-label={words.remove}\n title={words.remove}\n disabled={busy === task.id}\n onClick={() => void change(task, () => client.tasks.remove(agentId, task.id))}\n />\n }\n />\n ))}\n </div>\n )}\n </section>\n );\n}\n"],"mappings":";;;;;;;;AAwBA,MAAa,gBAA8B;CACzC,OAAO,UAAW,UAAU,IAAI,0BAA0B,GAAG,MAAM;CACnE,OAAO;CACP,MAAM;CACN,QAAQ;CACR,OAAO;CACP,QAAQ;AACV;;AAWA,MAAa,gBAAgB;;;;;;AAO7B,SAAgB,SACd,QACA,SACA,SACwD;CACxD,MAAM,CAAC,OAAO,YAAY,SAAwB,IAAI;CACtD,MAAM,OAAO,OAAO,IAAI;CACxB,MAAM,UAAU,YAAY,YAAY;EACtC,IAAI;GACF,MAAM,OAAO,MAAM,OAAO,MAAM,KAAK,OAAO;GAC5C,IAAI,KAAK,SAAS,SAAS,IAAI;EACjC,QAAQ;GACN,IAAI,KAAK,SAAS,SAAS,IAAI;EACjC;CACF,GAAG,CAAC,QAAQ,OAAO,CAAC;CACpB,gBAAgB;EACd,KAAK,UAAU;EACf,IAAI,CAAC,SAAS;EACd,QAAa;EACb,MAAM,QAAQ,kBAAkB,KAAK,QAAQ,GAAG,aAAa;EAC7D,aAAa;GACX,KAAK,UAAU;GACf,cAAc,KAAK;EACrB;CACF,GAAG,CAAC,SAAS,OAAO,CAAC;CACrB,OAAO;EAAE,OAAO,UAAU,QAAQ;EAAM;CAAQ;AAClD;;;;;;AAkBA,SAAgB,WAAW,EACzB,QACA,SACA,OACA,UACA,QACA,OACA,cACkB;CAClB,MAAM,CAAC,MAAM,WAAW,SAAwB,IAAI;CACpD,MAAM,CAAC,QAAQ,aAAa,SAAS,KAAK;CAC1C,MAAM,SAAS,OAAO,MAAY,UAAkC;EAClE,QAAQ,KAAK,EAAE;EACf,UAAU,KAAK;EACf,IAAI;GACF,MAAM,MAAM;GACZ,MAAM,SAAS;EACjB,QAAQ;GACN,UAAU,IAAI;EAChB,UAAU;GACR,QAAQ,IAAI;EACd;CACF;CACA,OACE,qBAAC,WAAD;EAAS,cAAY,MAAM;EAAO,MAAK;EAAQ,WAAW,QAAQ,YAAY,UAAU;EAAxF,UAAA;GACE,qBAAC,UAAD;IAAQ,WAAW,QAAQ,YAAY,gBAAgB;IAAvD,UAAA,CACE,oBAAC,QAAD;KACE,MAAK;KACL,MAAK;KACL,MAAM;KACN,cAAY,MAAM;KAClB,OAAO,MAAM;KACb,SAAS;IACV,CAAA,GACD,oBAAC,MAAD;KAAI,WAAW,QAAQ,YAAY,iBAAiB;KAAI,UAAA,MAAM;IAAU,CAAA,CAClE;;GACP,UACC,oBAAC,KAAD;IAAG,MAAK;IAAQ,WAAW,QAAQ,YAAY,mBAAmB;IAC/D,UAAA,MAAM;GACN,CAAA;GAEJ,MAAM,WAAW,IAChB,oBAAC,KAAD;IAAG,WAAW,QAAQ,YAAY,iBAAiB;IAAI,UAAA,MAAM;GAAS,CAAA,IAEtE,oBAAC,OAAD;IAAK,WAAW,QAAQ,YAAY,gBAAgB;IACjD,UAAA,MAAM,KAAK,SACV,oBAAC,YAAD;KAEE,YAAY,SAAS,IAAI;KACzB,MAAM,KAAK;KACX,WAAW,SAAS,KAAK;KACzB,WAAW,OAAO,KAAK,OAAO,YAAY,OAAO,MAAM,OAAO,SAAS,KAAK,IAAI,EAAE,CAAC;KACnF,SACE,oBAAC,QAAD;MACE,MAAK;MACL,MAAK;MACL,MAAM;MACN,cAAY,MAAM;MAClB,OAAO,MAAM;MACb,UAAU,SAAS,KAAK;MACxB,eAAe,KAAK,OAAO,YAAY,OAAO,MAAM,OAAO,SAAS,KAAK,EAAE,CAAC;KAC7E,CAAA;IAEJ,GAhBM,KAAK,EAgBX,CACF;GACE,CAAA;EAEA;;AAEb"}
|
package/dist/VoiceCall.js
CHANGED
|
@@ -52,7 +52,7 @@ const VOICE_CALL_MESSAGES = {
|
|
|
52
52
|
},
|
|
53
53
|
builder: {
|
|
54
54
|
title: (name) => `${name} doesn't take calls`,
|
|
55
|
-
help: "
|
|
55
|
+
help: "The builder only writes. Call one of the agents."
|
|
56
56
|
},
|
|
57
57
|
refused: {
|
|
58
58
|
title: (name) => `${name} can't take this call`,
|
package/dist/VoiceCall.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoiceCall.js","names":[],"sources":["../src/VoiceCall.tsx"],"sourcesContent":["import {\n MicrophoneIcon,\n MicrophoneSlashIcon,\n PhoneDisconnectIcon,\n SpeakerHighIcon,\n} from \"@phosphor-icons/react\";\nimport { useEffect, useRef, useState, type ReactNode, type RefObject } from \"react\";\n\nimport {\n durationOf,\n keyBelongsElsewhere,\n lookOf,\n orbOf,\n type CallEndedBy,\n type CallLook,\n type CallProblem,\n type CallState,\n} from \"./call\";\nimport { classes } from \"./classes\";\nimport { Button } from \"./kit/Button\";\nimport { KitProvider, type KitClassNames, type KitPart } from \"./kit/context\";\nimport type { AgentTint } from \"./tint\";\nimport { useCall, type CallJoin } from \"./useCall\";\nimport { Orb } from \"./voice/Orb\";\nimport type { OrbStyle } from \"./voice/orbs\";\n\n/** How big the orb is drawn: large, in the middle, with room for it to grow when the voice is loud. */\nconst ORB_SIZE = 280;\n\n/** The orb's level where nobody it follows is speaking. */\nconst SILENT = (): number => 0;\n\n/** The words a call says; a host gives its own, in its language. */\nexport interface VoiceCallMessages {\n /** The call's name, for those who can't see it. */\n call: (name: string) => string;\n /** What the call is doing, in a word or two. */\n states: Readonly<Record<Exclude<CallLook, \"problem\">, string>>;\n /** Under the state while the call connects. */\n calling: (name: string) => string;\n /** Why the call ended, before how long it lasted. */\n ended: Readonly<Record<CallEndedBy, (name: string) => string>>;\n /** Why the call never began, and what the person can do about it. */\n problems: Readonly<Record<CallProblem, { title: (name: string) => string; help: string }>>;\n mute: string;\n hangUp: string;\n /** Under the controls, where there is a keyboard. */\n keys: string;\n /** Under the controls while the microphone is off. */\n muted: string;\n /** The browser held the agent's voice until a press: the button that lets it play. */\n hear: (name: string) => string;\n}\n\nexport const VOICE_CALL_MESSAGES: VoiceCallMessages = {\n call: (name) => `Call with ${name}`,\n states: {\n connecting: \"Connecting…\",\n listening: \"Listening\",\n thinking: \"Thinking\",\n speaking: \"Speaking\",\n reconnecting: \"Reconnecting…\",\n ended: \"Call ended\",\n },\n calling: (name) => `Calling ${name}`,\n ended: {\n person: () => \"You hung up\",\n agent: (name) => `${name} hung up`,\n limit: () => \"It reached its longest time\",\n dropped: () => \"The line dropped\",\n },\n problems: {\n denied: {\n title: () => \"The microphone isn't allowed\",\n help: \"Allow the microphone for this page (the icon beside the address) and call again.\",\n },\n nomic: {\n title: () => \"No microphone\",\n help: \"Plug one in, or pick another in your system's sound settings, and call again.\",\n },\n inuse: {\n title: () => \"The microphone is busy\",\n help: \"Another app is using it. Close it and call again.\",\n },\n unsupported: {\n title: () => \"This browser can't call\",\n help: \"Open this page over https in a recent Chrome, Edge, Safari or Firefox.\",\n },\n off: {\n title: () => \"Calls aren't set up\",\n help: \"Calls are turned on where your agents work. Ask whoever set it up.\",\n },\n builder: {\n title: (name) => `${name} doesn't take calls`,\n help: \"A squad's builder only writes. Call one of the squad's agents.\",\n },\n refused: {\n title: (name) => `${name} can't take this call`,\n help: \"Try again in a moment.\",\n },\n failed: {\n title: (name) => `Couldn't call ${name}`,\n help: \"Check your connection and try again.\",\n },\n unanswered: {\n title: (name) => `${name} isn't answering`,\n help: \"Calls aren't being taken right now. Try again in a moment.\",\n },\n },\n mute: \"Mute\",\n hangUp: \"Hang up\",\n keys: \"Space mutes and unmutes\",\n muted: \"Your microphone is off\",\n hear: (name) => `Hear ${name}`,\n};\n\n/** The call's parts, by the class each carries; `classNames` adds a host's own under the same keys. */\nexport type VoiceCallPart =\n | \"sc-call\"\n | `sc-call--${CallLook}`\n | `sc-call--${AgentTint}`\n | \"sc-call--on\"\n | \"sc-call--muted\"\n | \"sc-call__stage\"\n | \"sc-call__orb\"\n | \"sc-call__state\"\n | \"sc-call__line\"\n | \"sc-call__sound\"\n | \"sc-call__controls\"\n | \"sc-call__control\"\n | \"sc-call__mute\"\n | \"sc-call__hangup\"\n | \"sc-call__hint\"\n | \"sc-call__captions\"\n | \"sc-call__after\";\n\nexport interface VoiceCallProps {\n /**\n * Where to join: LiveKit's address and a token for the call's room (our API's\n * `client.conversations.call(agentId)` answers both, `client.agents.call(id)` for the owner's\n * test). Called once, when the call starts, after the microphone is open.\n */\n connect: () => Promise<CallJoin>;\n /** The agent's name, for the words that say it. */\n name: string;\n /**\n * The agent's own tint (`agentTint(id)`), worn by the call's chrome: the orb's shader paints its\n * own colours, and the still orb that stands in where there is no WebGL wears it.\n */\n tint?: AgentTint | undefined;\n /** How the orb is drawn: `\"wave\"` (the default) or `\"fluid-dots\"`. */\n orb?: OrbStyle | undefined;\n /** The orb's diameter in pixels: 280 by default, less for a call in a small panel. */\n orbSize?: number | undefined;\n /**\n * Why the call ended as the API keeps it, once the host has read it (`endedByReason` of its\n * conversation's call): what the end says when the agent's side closed the room (its goodbye,\n * or its longest time), which the room doesn't tell. Until then the end says how long it was.\n */\n endedBy?: CallEndedBy | null | undefined;\n messages?: Partial<VoiceCallMessages> | undefined;\n /** The host's classes for the call's parts and the kit's (the orb's `sc-orb…`, the buttons'). */\n classNames?: Partial<Record<VoiceCallPart | KitPart, string>> | undefined;\n /**\n * The call is over, once: it ended (`answered`: the agent took it; `problem` null), or it\n * couldn't begin (`problem`: why, which the call shows until the host takes it away).\n */\n onEnd?: ((answered: boolean, problem: CallProblem | null) => void) | undefined;\n /** The agent took the call: its time counts from now. Once. */\n onAnswer?: (() => void) | undefined;\n /** What the host offers once the call is over, in place of the controls: call again, write. */\n after?: ReactNode;\n /** What is being said, drawn by the host under the orb while the call is on (`sc-call__captions`). */\n captions?: ReactNode;\n}\n\n/**\n * A voice call with an agent, the one a customer has on the phone: the call starts when it\n * mounts and ends when it unmounts. The orb, large in the middle, rests while the line is found,\n * moves with whoever speaks (the agent's voice while it speaks, the person's while it listens),\n * calms while the agent thinks, and fades once the call is over; under it what the call\n * is doing in a word, its time, and the two controls a phone has, mute and hang up. A microphone\n * that can't be used, or a call that can't be made (the API's refusal among them), says why in\n * words and what to do; an end says who ended it (the room's own word, or the API's: `endedBy`)\n * and how long it lasted.\n *\n * Space mutes and unmutes from anywhere that isn't a field or a control of the page's own.\n * Escape never hangs up: it is the key that closes things, pressed by reflex, and a call ended\n * by accident can't be taken back, so ending one is the red button's alone. The orb stands\n * still under reduced motion, one frame per state: the words say the rest.\n */\nexport function VoiceCall(props: VoiceCallProps) {\n return (\n <KitProvider classNames={props.classNames as KitClassNames}>\n <Call {...props} />\n </KitProvider>\n );\n}\n\nfunction Call({\n connect,\n name,\n tint = \"sky\",\n orb: style,\n orbSize = ORB_SIZE,\n endedBy,\n messages,\n classNames,\n onEnd,\n onAnswer,\n after,\n captions,\n}: VoiceCallProps) {\n const words = { ...VOICE_CALL_MESSAGES, ...messages };\n const root = useRef<HTMLElement>(null);\n const sink = useRef<HTMLDivElement>(null);\n const call = useCall(connect, { sink });\n const { state } = call;\n const look = lookOf(state);\n const orb = orbOf(look);\n const over = state.stage === \"over\";\n const seconds = useSeconds(state);\n\n useSpace(root, !over, () => {\n call.mute();\n });\n // The call takes the keyboard as it opens, so Space is its from the start; and again once over,\n // when the control that had it is gone, so the host's offers are the next stop.\n useEffect(() => {\n root.current?.focus({ preventScroll: true });\n }, [over]);\n // The host's ears as it last gave them.\n const told = useRef({ onEnd, onAnswer });\n useEffect(() => {\n told.current = { onEnd, onAnswer };\n });\n const answered = state.answeredAt !== null;\n useEffect(() => {\n if (answered) told.current.onAnswer?.();\n }, [answered]);\n useEffect(() => {\n if (over) told.current.onEnd?.(answered, state.problem);\n }, [over]);\n\n const { headline, line } = wordsOf(state, look, seconds, words, name, endedBy ?? null);\n const modifiers: VoiceCallPart[] = [`sc-call--${look}`, `sc-call--${tint}`];\n if (!over) modifiers.push(\"sc-call--on\");\n if (state.muted && !over) modifiers.push(\"sc-call--muted\");\n\n return (\n <section\n ref={root}\n tabIndex={-1}\n aria-label={words.call(name)}\n part=\"call\"\n className={`sc ${classes(classNames, \"sc-call\", ...modifiers)}`}\n >\n <div className={classes(classNames, \"sc-call__stage\")}>\n <Orb\n state={orb.state}\n level={orb.follows === null ? SILENT : call.meter[orb.follows]}\n size={orbSize}\n style={style}\n label={headline}\n className={classes(classNames, \"sc-call__orb\")}\n />\n <p role=\"status\" part=\"call-state\" className={classes(classNames, \"sc-call__state\")}>\n <span key={headline}>{headline}</span>\n </p>\n <p part=\"call-line\" className={classes(classNames, \"sc-call__line\")}>\n {line}\n </p>\n </div>\n {captions !== undefined && !over && (\n <div part=\"captions\" className={classes(classNames, \"sc-call__captions\")}>\n {captions}\n </div>\n )}\n {state.silenced && !over && (\n <Button\n size=\"sm\"\n icon={SpeakerHighIcon}\n onClick={call.hear}\n className={classes(classNames, \"sc-call__sound\")}\n >\n {words.hear(name)}\n </Button>\n )}\n {over ? (\n after !== undefined && (\n <div part=\"call-after\" className={classes(classNames, \"sc-call__after\")}>\n {after}\n </div>\n )\n ) : (\n <>\n <div part=\"call-controls\" className={classes(classNames, \"sc-call__controls\")}>\n <div className={classes(classNames, \"sc-call__control\")}>\n <Button\n icon={state.muted ? MicrophoneSlashIcon : MicrophoneIcon}\n aria-label={words.mute}\n aria-pressed={state.muted}\n disabled={state.stage === \"opening\"}\n onClick={() => {\n call.mute();\n }}\n part=\"mute\"\n className={classes(classNames, \"sc-call__mute\")}\n />\n <span aria-hidden>{words.mute}</span>\n </div>\n <div className={classes(classNames, \"sc-call__control\")}>\n <Button\n tone=\"danger\"\n icon={PhoneDisconnectIcon}\n aria-label={words.hangUp}\n onClick={call.hangUp}\n part=\"hang-up\"\n className={classes(classNames, \"sc-call__hangup\")}\n />\n <span aria-hidden>{words.hangUp}</span>\n </div>\n </div>\n <p\n data-keys={state.muted ? undefined : \"\"}\n className={classes(classNames, \"sc-call__hint\")}\n >\n {state.muted ? words.muted : words.keys}\n </p>\n </>\n )}\n {/* The agent's voice plays from here. */}\n <div ref={sink} hidden />\n </section>\n );\n}\n\n/**\n * What the call says: what it is doing (or why it couldn't begin), and under it who is being\n * called, the time, how it ended (as the room knows it, else as the API said: `endedBy`) and how\n * long it lasted, or what to do.\n */\nfunction wordsOf(\n state: CallState,\n look: CallLook,\n seconds: number,\n words: VoiceCallMessages,\n name: string,\n endedBy: CallEndedBy | null,\n): { headline: string; line: string } {\n if (look === \"problem\" && state.problem !== null) {\n const problem = words.problems[state.problem];\n return { headline: problem.title(name), line: problem.help };\n }\n const headline = look === \"problem\" ? \"\" : words.states[look];\n if (state.end !== null) {\n const by = state.end.by ?? endedBy;\n const lasted = durationOf(state.end.seconds);\n // Why it ended says it ended; how long it lasted, under it.\n return { headline: by === null ? headline : words.ended[by](name), line: lasted };\n }\n return {\n headline,\n line: state.answeredAt === null ? words.calling(name) : durationOf(seconds),\n };\n}\n\n/** The call's time in whole seconds: counting while it is on, held once it is over. */\nfunction useSeconds(state: CallState): number {\n const [now, setNow] = useState(() => Date.now());\n const counting = state.answeredAt !== null && state.stage !== \"over\";\n useEffect(() => {\n if (!counting) return undefined;\n const tick = setInterval(() => {\n setNow(Date.now());\n }, 500);\n return () => {\n clearInterval(tick);\n };\n }, [counting]);\n if (state.end !== null) return state.end.seconds;\n if (state.answeredAt === null) return 0;\n return Math.max(0, Math.floor((now - state.answeredAt) / 1000));\n}\n\n/**\n * Space mutes and unmutes while `active`, wherever it doesn't belong to something else (a field,\n * a control of the page's own). Taken whole, down and up: the page doesn't scroll, and a control\n * of the call that has the focus is not pressed by it.\n */\nfunction useSpace(root: RefObject<HTMLElement | null>, active: boolean, toggle: () => void) {\n const latest = useRef(toggle);\n useEffect(() => {\n latest.current = toggle;\n });\n useEffect(() => {\n if (!active) return undefined;\n const onKey = (event: KeyboardEvent) => {\n if (event.key !== \" \" || event.ctrlKey || event.metaKey || event.altKey) return;\n if (event.defaultPrevented || keyBelongsElsewhere(event.target, root.current)) return;\n event.preventDefault();\n if (event.type === \"keydown\" && !event.repeat) latest.current();\n };\n window.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"keyup\", onKey);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"keyup\", onKey);\n };\n }, [active, root]);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VoiceCall.js","names":[],"sources":["../src/VoiceCall.tsx"],"sourcesContent":["import {\n MicrophoneIcon,\n MicrophoneSlashIcon,\n PhoneDisconnectIcon,\n SpeakerHighIcon,\n} from \"@phosphor-icons/react\";\nimport { useEffect, useRef, useState, type ReactNode, type RefObject } from \"react\";\n\nimport {\n durationOf,\n keyBelongsElsewhere,\n lookOf,\n orbOf,\n type CallEndedBy,\n type CallLook,\n type CallProblem,\n type CallState,\n} from \"./call\";\nimport { classes } from \"./classes\";\nimport { Button } from \"./kit/Button\";\nimport { KitProvider, type KitClassNames, type KitPart } from \"./kit/context\";\nimport type { AgentTint } from \"./tint\";\nimport { useCall, type CallJoin } from \"./useCall\";\nimport { Orb } from \"./voice/Orb\";\nimport type { OrbStyle } from \"./voice/orbs\";\n\n/** How big the orb is drawn: large, in the middle, with room for it to grow when the voice is loud. */\nconst ORB_SIZE = 280;\n\n/** The orb's level where nobody it follows is speaking. */\nconst SILENT = (): number => 0;\n\n/** The words a call says; a host gives its own, in its language. */\nexport interface VoiceCallMessages {\n /** The call's name, for those who can't see it. */\n call: (name: string) => string;\n /** What the call is doing, in a word or two. */\n states: Readonly<Record<Exclude<CallLook, \"problem\">, string>>;\n /** Under the state while the call connects. */\n calling: (name: string) => string;\n /** Why the call ended, before how long it lasted. */\n ended: Readonly<Record<CallEndedBy, (name: string) => string>>;\n /** Why the call never began, and what the person can do about it. */\n problems: Readonly<Record<CallProblem, { title: (name: string) => string; help: string }>>;\n mute: string;\n hangUp: string;\n /** Under the controls, where there is a keyboard. */\n keys: string;\n /** Under the controls while the microphone is off. */\n muted: string;\n /** The browser held the agent's voice until a press: the button that lets it play. */\n hear: (name: string) => string;\n}\n\nexport const VOICE_CALL_MESSAGES: VoiceCallMessages = {\n call: (name) => `Call with ${name}`,\n states: {\n connecting: \"Connecting…\",\n listening: \"Listening\",\n thinking: \"Thinking\",\n speaking: \"Speaking\",\n reconnecting: \"Reconnecting…\",\n ended: \"Call ended\",\n },\n calling: (name) => `Calling ${name}`,\n ended: {\n person: () => \"You hung up\",\n agent: (name) => `${name} hung up`,\n limit: () => \"It reached its longest time\",\n dropped: () => \"The line dropped\",\n },\n problems: {\n denied: {\n title: () => \"The microphone isn't allowed\",\n help: \"Allow the microphone for this page (the icon beside the address) and call again.\",\n },\n nomic: {\n title: () => \"No microphone\",\n help: \"Plug one in, or pick another in your system's sound settings, and call again.\",\n },\n inuse: {\n title: () => \"The microphone is busy\",\n help: \"Another app is using it. Close it and call again.\",\n },\n unsupported: {\n title: () => \"This browser can't call\",\n help: \"Open this page over https in a recent Chrome, Edge, Safari or Firefox.\",\n },\n off: {\n title: () => \"Calls aren't set up\",\n help: \"Calls are turned on where your agents work. Ask whoever set it up.\",\n },\n builder: {\n title: (name) => `${name} doesn't take calls`,\n help: \"The builder only writes. Call one of the agents.\",\n },\n refused: {\n title: (name) => `${name} can't take this call`,\n help: \"Try again in a moment.\",\n },\n failed: {\n title: (name) => `Couldn't call ${name}`,\n help: \"Check your connection and try again.\",\n },\n unanswered: {\n title: (name) => `${name} isn't answering`,\n help: \"Calls aren't being taken right now. Try again in a moment.\",\n },\n },\n mute: \"Mute\",\n hangUp: \"Hang up\",\n keys: \"Space mutes and unmutes\",\n muted: \"Your microphone is off\",\n hear: (name) => `Hear ${name}`,\n};\n\n/** The call's parts, by the class each carries; `classNames` adds a host's own under the same keys. */\nexport type VoiceCallPart =\n | \"sc-call\"\n | `sc-call--${CallLook}`\n | `sc-call--${AgentTint}`\n | \"sc-call--on\"\n | \"sc-call--muted\"\n | \"sc-call__stage\"\n | \"sc-call__orb\"\n | \"sc-call__state\"\n | \"sc-call__line\"\n | \"sc-call__sound\"\n | \"sc-call__controls\"\n | \"sc-call__control\"\n | \"sc-call__mute\"\n | \"sc-call__hangup\"\n | \"sc-call__hint\"\n | \"sc-call__captions\"\n | \"sc-call__after\";\n\nexport interface VoiceCallProps {\n /**\n * Where to join: LiveKit's address and a token for the call's room (our API's\n * `client.conversations.call(agentId)` answers both, `client.agents.call(id)` for the owner's\n * test). Called once, when the call starts, after the microphone is open.\n */\n connect: () => Promise<CallJoin>;\n /** The agent's name, for the words that say it. */\n name: string;\n /**\n * The agent's own tint (`agentTint(id)`), worn by the call's chrome: the orb's shader paints its\n * own colours, and the still orb that stands in where there is no WebGL wears it.\n */\n tint?: AgentTint | undefined;\n /** How the orb is drawn: `\"wave\"` (the default) or `\"fluid-dots\"`. */\n orb?: OrbStyle | undefined;\n /** The orb's diameter in pixels: 280 by default, less for a call in a small panel. */\n orbSize?: number | undefined;\n /**\n * Why the call ended as the API keeps it, once the host has read it (`endedByReason` of its\n * conversation's call): what the end says when the agent's side closed the room (its goodbye,\n * or its longest time), which the room doesn't tell. Until then the end says how long it was.\n */\n endedBy?: CallEndedBy | null | undefined;\n messages?: Partial<VoiceCallMessages> | undefined;\n /** The host's classes for the call's parts and the kit's (the orb's `sc-orb…`, the buttons'). */\n classNames?: Partial<Record<VoiceCallPart | KitPart, string>> | undefined;\n /**\n * The call is over, once: it ended (`answered`: the agent took it; `problem` null), or it\n * couldn't begin (`problem`: why, which the call shows until the host takes it away).\n */\n onEnd?: ((answered: boolean, problem: CallProblem | null) => void) | undefined;\n /** The agent took the call: its time counts from now. Once. */\n onAnswer?: (() => void) | undefined;\n /** What the host offers once the call is over, in place of the controls: call again, write. */\n after?: ReactNode;\n /** What is being said, drawn by the host under the orb while the call is on (`sc-call__captions`). */\n captions?: ReactNode;\n}\n\n/**\n * A voice call with an agent, the one a customer has on the phone: the call starts when it\n * mounts and ends when it unmounts. The orb, large in the middle, rests while the line is found,\n * moves with whoever speaks (the agent's voice while it speaks, the person's while it listens),\n * calms while the agent thinks, and fades once the call is over; under it what the call\n * is doing in a word, its time, and the two controls a phone has, mute and hang up. A microphone\n * that can't be used, or a call that can't be made (the API's refusal among them), says why in\n * words and what to do; an end says who ended it (the room's own word, or the API's: `endedBy`)\n * and how long it lasted.\n *\n * Space mutes and unmutes from anywhere that isn't a field or a control of the page's own.\n * Escape never hangs up: it is the key that closes things, pressed by reflex, and a call ended\n * by accident can't be taken back, so ending one is the red button's alone. The orb stands\n * still under reduced motion, one frame per state: the words say the rest.\n */\nexport function VoiceCall(props: VoiceCallProps) {\n return (\n <KitProvider classNames={props.classNames as KitClassNames}>\n <Call {...props} />\n </KitProvider>\n );\n}\n\nfunction Call({\n connect,\n name,\n tint = \"sky\",\n orb: style,\n orbSize = ORB_SIZE,\n endedBy,\n messages,\n classNames,\n onEnd,\n onAnswer,\n after,\n captions,\n}: VoiceCallProps) {\n const words = { ...VOICE_CALL_MESSAGES, ...messages };\n const root = useRef<HTMLElement>(null);\n const sink = useRef<HTMLDivElement>(null);\n const call = useCall(connect, { sink });\n const { state } = call;\n const look = lookOf(state);\n const orb = orbOf(look);\n const over = state.stage === \"over\";\n const seconds = useSeconds(state);\n\n useSpace(root, !over, () => {\n call.mute();\n });\n // The call takes the keyboard as it opens, so Space is its from the start; and again once over,\n // when the control that had it is gone, so the host's offers are the next stop.\n useEffect(() => {\n root.current?.focus({ preventScroll: true });\n }, [over]);\n // The host's ears as it last gave them.\n const told = useRef({ onEnd, onAnswer });\n useEffect(() => {\n told.current = { onEnd, onAnswer };\n });\n const answered = state.answeredAt !== null;\n useEffect(() => {\n if (answered) told.current.onAnswer?.();\n }, [answered]);\n useEffect(() => {\n if (over) told.current.onEnd?.(answered, state.problem);\n }, [over]);\n\n const { headline, line } = wordsOf(state, look, seconds, words, name, endedBy ?? null);\n const modifiers: VoiceCallPart[] = [`sc-call--${look}`, `sc-call--${tint}`];\n if (!over) modifiers.push(\"sc-call--on\");\n if (state.muted && !over) modifiers.push(\"sc-call--muted\");\n\n return (\n <section\n ref={root}\n tabIndex={-1}\n aria-label={words.call(name)}\n part=\"call\"\n className={`sc ${classes(classNames, \"sc-call\", ...modifiers)}`}\n >\n <div className={classes(classNames, \"sc-call__stage\")}>\n <Orb\n state={orb.state}\n level={orb.follows === null ? SILENT : call.meter[orb.follows]}\n size={orbSize}\n style={style}\n label={headline}\n className={classes(classNames, \"sc-call__orb\")}\n />\n <p role=\"status\" part=\"call-state\" className={classes(classNames, \"sc-call__state\")}>\n <span key={headline}>{headline}</span>\n </p>\n <p part=\"call-line\" className={classes(classNames, \"sc-call__line\")}>\n {line}\n </p>\n </div>\n {captions !== undefined && !over && (\n <div part=\"captions\" className={classes(classNames, \"sc-call__captions\")}>\n {captions}\n </div>\n )}\n {state.silenced && !over && (\n <Button\n size=\"sm\"\n icon={SpeakerHighIcon}\n onClick={call.hear}\n className={classes(classNames, \"sc-call__sound\")}\n >\n {words.hear(name)}\n </Button>\n )}\n {over ? (\n after !== undefined && (\n <div part=\"call-after\" className={classes(classNames, \"sc-call__after\")}>\n {after}\n </div>\n )\n ) : (\n <>\n <div part=\"call-controls\" className={classes(classNames, \"sc-call__controls\")}>\n <div className={classes(classNames, \"sc-call__control\")}>\n <Button\n icon={state.muted ? MicrophoneSlashIcon : MicrophoneIcon}\n aria-label={words.mute}\n aria-pressed={state.muted}\n disabled={state.stage === \"opening\"}\n onClick={() => {\n call.mute();\n }}\n part=\"mute\"\n className={classes(classNames, \"sc-call__mute\")}\n />\n <span aria-hidden>{words.mute}</span>\n </div>\n <div className={classes(classNames, \"sc-call__control\")}>\n <Button\n tone=\"danger\"\n icon={PhoneDisconnectIcon}\n aria-label={words.hangUp}\n onClick={call.hangUp}\n part=\"hang-up\"\n className={classes(classNames, \"sc-call__hangup\")}\n />\n <span aria-hidden>{words.hangUp}</span>\n </div>\n </div>\n <p\n data-keys={state.muted ? undefined : \"\"}\n className={classes(classNames, \"sc-call__hint\")}\n >\n {state.muted ? words.muted : words.keys}\n </p>\n </>\n )}\n {/* The agent's voice plays from here. */}\n <div ref={sink} hidden />\n </section>\n );\n}\n\n/**\n * What the call says: what it is doing (or why it couldn't begin), and under it who is being\n * called, the time, how it ended (as the room knows it, else as the API said: `endedBy`) and how\n * long it lasted, or what to do.\n */\nfunction wordsOf(\n state: CallState,\n look: CallLook,\n seconds: number,\n words: VoiceCallMessages,\n name: string,\n endedBy: CallEndedBy | null,\n): { headline: string; line: string } {\n if (look === \"problem\" && state.problem !== null) {\n const problem = words.problems[state.problem];\n return { headline: problem.title(name), line: problem.help };\n }\n const headline = look === \"problem\" ? \"\" : words.states[look];\n if (state.end !== null) {\n const by = state.end.by ?? endedBy;\n const lasted = durationOf(state.end.seconds);\n // Why it ended says it ended; how long it lasted, under it.\n return { headline: by === null ? headline : words.ended[by](name), line: lasted };\n }\n return {\n headline,\n line: state.answeredAt === null ? words.calling(name) : durationOf(seconds),\n };\n}\n\n/** The call's time in whole seconds: counting while it is on, held once it is over. */\nfunction useSeconds(state: CallState): number {\n const [now, setNow] = useState(() => Date.now());\n const counting = state.answeredAt !== null && state.stage !== \"over\";\n useEffect(() => {\n if (!counting) return undefined;\n const tick = setInterval(() => {\n setNow(Date.now());\n }, 500);\n return () => {\n clearInterval(tick);\n };\n }, [counting]);\n if (state.end !== null) return state.end.seconds;\n if (state.answeredAt === null) return 0;\n return Math.max(0, Math.floor((now - state.answeredAt) / 1000));\n}\n\n/**\n * Space mutes and unmutes while `active`, wherever it doesn't belong to something else (a field,\n * a control of the page's own). Taken whole, down and up: the page doesn't scroll, and a control\n * of the call that has the focus is not pressed by it.\n */\nfunction useSpace(root: RefObject<HTMLElement | null>, active: boolean, toggle: () => void) {\n const latest = useRef(toggle);\n useEffect(() => {\n latest.current = toggle;\n });\n useEffect(() => {\n if (!active) return undefined;\n const onKey = (event: KeyboardEvent) => {\n if (event.key !== \" \" || event.ctrlKey || event.metaKey || event.altKey) return;\n if (event.defaultPrevented || keyBelongsElsewhere(event.target, root.current)) return;\n event.preventDefault();\n if (event.type === \"keydown\" && !event.repeat) latest.current();\n };\n window.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"keyup\", onKey);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"keyup\", onKey);\n };\n }, [active, root]);\n}\n"],"mappings":"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"}
|