@signalcore/ui 1.0.0-beta.2 → 1.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +126 -21
- package/dist/AgentAbout.js +6 -2
- package/dist/AgentAbout.js.map +1 -1
- package/dist/AgentChat.d.ts +25 -3
- package/dist/AgentChat.js +302 -28
- package/dist/AgentChat.js.map +1 -1
- package/dist/AgentSpace.d.ts +25 -4
- package/dist/AgentSpace.js +55 -11
- package/dist/AgentSpace.js.map +1 -1
- package/dist/Threads.d.ts +73 -0
- package/dist/Threads.js +140 -0
- package/dist/Threads.js.map +1 -0
- package/dist/View.d.ts +48 -0
- package/dist/View.js +101 -0
- package/dist/View.js.map +1 -0
- package/dist/automation/marks.js +7 -3
- package/dist/automation/marks.js.map +1 -1
- package/dist/automation/messages.d.ts +4 -0
- package/dist/automation/messages.js +5 -1
- package/dist/automation/messages.js.map +1 -1
- package/dist/automation/when.js +8 -0
- package/dist/automation/when.js.map +1 -1
- package/dist/blocks.d.ts +83 -2
- package/dist/client.d.ts +23 -5
- package/dist/client.js +8 -2
- package/dist/client.js.map +1 -1
- package/dist/contract.d.ts +9 -4
- package/dist/es.d.ts +2 -0
- package/dist/es.js +34 -3
- package/dist/es.js.map +1 -1
- package/dist/frames.d.ts +7 -1
- package/dist/frames.js.map +1 -1
- package/dist/index.d.ts +14 -7
- package/dist/index.js +11 -4
- package/dist/kit/Agenda.d.ts +25 -0
- package/dist/kit/Agenda.js +119 -0
- package/dist/kit/Agenda.js.map +1 -0
- package/dist/kit/Combobox.js +1 -1
- package/dist/kit/DateField.js +1 -1
- package/dist/kit/Kanban.d.ts +17 -0
- package/dist/kit/Kanban.js +157 -0
- package/dist/kit/Kanban.js.map +1 -0
- package/dist/kit/TimeField.js +1 -1
- package/dist/kit/Timeline.d.ts +14 -0
- package/dist/kit/Timeline.js +71 -0
- package/dist/kit/Timeline.js.map +1 -0
- package/dist/kit/context.d.ts +1 -1
- package/dist/kit/context.js.map +1 -1
- package/dist/kit/messages.d.ts +22 -0
- package/dist/kit/messages.js +10 -0
- package/dist/kit/messages.js.map +1 -1
- package/dist/surface/Form.js +1 -1
- package/dist/surface/Placed.d.ts +20 -0
- package/dist/surface/Placed.js +70 -0
- package/dist/surface/Placed.js.map +1 -0
- package/dist/surface/Surface.d.ts +4 -2
- package/dist/surface/Surface.js +78 -2
- package/dist/surface/Surface.js.map +1 -1
- package/dist/surface/places.d.ts +16 -0
- package/dist/surface/places.js +15 -0
- package/dist/surface/places.js.map +1 -0
- package/dist/surface/scope.d.ts +17 -1
- package/dist/surface/scope.js +1 -0
- package/dist/surface/scope.js.map +1 -1
- package/dist/types.d.ts +23 -0
- package/dist/types.js.map +1 -1
- package/dist/useConversation.d.ts +2 -0
- package/dist/useConversation.js +16 -0
- package/dist/useConversation.js.map +1 -1
- package/dist/widget.js +17 -16
- package/package.json +1 -1
- package/src/AgentAbout.tsx +4 -0
- package/src/AgentChat.tsx +417 -34
- package/src/AgentSpace.tsx +106 -14
- package/src/Threads.tsx +218 -0
- package/src/View.tsx +117 -0
- package/src/automation/marks.tsx +7 -0
- package/src/automation/messages.ts +8 -0
- package/src/automation/when.ts +4 -0
- package/src/blocks.ts +100 -2
- package/src/client.ts +42 -10
- package/src/contract.ts +404 -29
- package/src/es.ts +30 -0
- package/src/frames.ts +3 -1
- package/src/index.ts +36 -3
- package/src/kit/Agenda.tsx +161 -0
- package/src/kit/Kanban.tsx +182 -0
- package/src/kit/Timeline.tsx +58 -0
- package/src/kit/context.tsx +45 -1
- package/src/kit/messages.ts +31 -0
- package/src/styles.css +825 -66
- package/src/surface/Placed.tsx +98 -0
- package/src/surface/Surface.tsx +92 -1
- package/src/surface/places.ts +19 -0
- package/src/surface/scope.ts +24 -1
- package/src/types.ts +24 -0
- package/src/useConversation.ts +10 -0
- package/src/widget/element.tsx +54 -7
- package/src/widget/widget.css +13 -0
package/dist/kit/messages.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","names":[],"sources":["../../src/kit/messages.ts"],"sourcesContent":["import type { DayPickerLocale } from \"react-day-picker\";\n\nimport { AUTOMATION_MESSAGES, type AutomationMessages } from \"../automation/messages\";\n\n/**\n * The language the kit's dates are read in: a locale from `@signalcore/ui/locale` (the\n * calendar's, with its words), whose `code` also formats days, times, numbers and lists.\n */\nexport type KitLocale = DayPickerLocale;\n\n/** The words the kit's controls say; a host gives its own, in its language. */\nexport interface KitMessages {\n /** A field left empty that must be filled. */\n required: string;\n /** A day, a time, both, an option, a rating still to pick; also an empty picker's invitation. */\n chooseDate: string;\n chooseTime: string;\n chooseMoment: string;\n chooseOption: string;\n chooseRating: string;\n /** A box that must be ticked to go on. */\n check: string;\n invalidEmail: string;\n invalidPhone: string;\n invalidUrl: string;\n invalidNumber: string;\n /** A time typed in a shape that is not one. */\n invalidTime: string;\n /** A number under its least, or over its most, given formatted. */\n atLeast: (min: string) => string;\n atMost: (max: string) => string;\n /** A day or a time that is not offered. */\n unavailableDate: string;\n unavailableTime: string;\n /** Beside the label of a field that may stay empty. */\n optional: string;\n /** In a long list's box while nothing is picked. */\n pick: string;\n /** In that box while the person types. */\n search: string;\n noResults: string;\n /** The button that shows or hides a secret as it is typed. */\n show: string;\n hide: string;\n /** A number's steppers, for screen readers. */\n increase: string;\n decrease: string;\n /** A picked tag's remove button, for screen readers. */\n remove: (label: string) => string;\n /** A box ticked or not, as an answer reads. */\n yes: string;\n no: string;\n /** A rating, as an answer reads. */\n rated: (value: number, max: number) => string;\n /** One star of a rating, for screen readers. */\n stars: (count: number) => string;\n /** A choice's last row, to answer in one's own words, and its box while empty. */\n other: string;\n otherPlaceholder: string;\n /** A form's submit when the agent gave it no words. */\n send: string;\n /** Where a form's submit was, once it is sent. */\n sent: string;\n /** A file field left empty that must be filled. */\n chooseFile: string;\n /** A file field's drop zone: what it asks, and how. */\n upload: (multiple: boolean) => string;\n drop: string;\n /**\n * Under them, what it takes: the kinds (named and joined, \"\" when anything) and the most a\n * file may weigh, formatted.\n */\n limits: (kinds: string, size: string) => string;\n /** A family of kinds as the limits name it: \"image\" for any image, \"video\", \"audio\", \"text\". */\n anyOf: (family: string) => string;\n /** A file on its way, beside how much has gone. */\n uploading: string;\n /** A file that was not sent: heavier than the most (formatted), a kind not taken, one too many. */\n tooBig: (size: string) => string;\n wrongType: string;\n tooMany: (max: number) => string;\n /** A file whose upload failed, and the button that tries it again. */\n uploadFailed: string;\n retry: string;\n /** A form sent while a file is still on its way, or one failed. */\n stillUploading: string;\n fixFiles: string;\n /** A file's chip, opened on a click, for screen readers. */\n open: (name: string) => string;\n /** A file whose name is not known yet. */\n file: string;\n /** An audio player's button, and its way through, for screen readers. */\n play: string;\n pause: string;\n position: string;\n /** An automation and its runs: when it runs, how it stands, what each run did. */\n automation: AutomationMessages;\n /** An app for the person to connect, as its connection stands. */\n connect: ConnectMessages;\n}\n\n/** The words of an app's card: its button, and what it says where the button isn't. */\nexport interface ConnectMessages {\n /** The button that opens the app's consent page, given the app's name. */\n authorize: (app: string) => string;\n /** It works. */\n connected: string;\n /** It failed, or the account was lost: the agent asks for it again. */\n failed: string;\n /** Before the platform has the consent page. */\n preparing: string;\n}\n\nconst FAMILIES: Readonly<Record<string, string>> = {\n image: \"images\",\n video: \"videos\",\n audio: \"audio\",\n text: \"text\",\n};\n\nexport const KIT_MESSAGES: KitMessages = {\n required: \"This field is required\",\n chooseDate: \"Choose a day\",\n chooseTime: \"Choose a time\",\n chooseMoment: \"Choose a day and a time\",\n chooseOption: \"Choose an option\",\n other: \"Another answer\",\n otherPlaceholder: \"Write your answer\",\n chooseRating: \"Choose a rating\",\n check: \"Tick the box to go on\",\n invalidEmail: \"Enter a valid email\",\n invalidPhone: \"Enter a valid phone number\",\n invalidUrl: \"Enter a valid address\",\n invalidNumber: \"Enter a number\",\n invalidTime: \"Write a time like 9:30\",\n atLeast: (min) => `At least ${min}`,\n atMost: (max) => `At most ${max}`,\n unavailableDate: \"That day is not available\",\n unavailableTime: \"That time is not available\",\n optional: \"Optional\",\n pick: \"Choose…\",\n search: \"Search…\",\n noResults: \"No results\",\n show: \"Show\",\n hide: \"Hide\",\n increase: \"Increase\",\n decrease: \"Decrease\",\n remove: (label) => `Remove ${label}`,\n yes: \"Yes\",\n no: \"No\",\n rated: (value, max) => `${value} of ${max}`,\n stars: (count) => (count === 1 ? \"1 star\" : `${count} stars`),\n send: \"Send\",\n sent: \"Sent\",\n chooseFile: \"Add a file\",\n upload: (multiple) => (multiple ? \"Upload files\" : \"Upload a file\"),\n drop: \"Drop it here or choose\",\n limits: (kinds, size) => (kinds ? `${kinds}, up to ${size}` : `Up to ${size}`),\n anyOf: (family) => FAMILIES[family] ?? family,\n uploading: \"Uploading\",\n tooBig: (size) => `Over ${size}`,\n wrongType: \"This kind of file isn't taken here\",\n tooMany: (max) => `At most ${max} files`,\n uploadFailed: \"Couldn't upload it\",\n retry: \"Retry\",\n stillUploading: \"Wait for the upload to finish\",\n fixFiles: \"Remove or retry the files that failed\",\n open: (name) => `Open ${name}`,\n file: \"File\",\n play: \"Play\",\n pause: \"Pause\",\n position: \"Position\",\n automation: AUTOMATION_MESSAGES,\n connect: {\n authorize: (app) => `Authorize in ${app}`,\n connected: \"Connected\",\n failed: \"It couldn't be connected\",\n preparing: \"Getting it ready\",\n },\n};\n"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"messages.js","names":[],"sources":["../../src/kit/messages.ts"],"sourcesContent":["import type { DayPickerLocale } from \"react-day-picker\";\n\nimport { AUTOMATION_MESSAGES, type AutomationMessages } from \"../automation/messages\";\n\n/**\n * The language the kit's dates are read in: a locale from `@signalcore/ui/locale` (the\n * calendar's, with its words), whose `code` also formats days, times, numbers and lists.\n */\nexport type KitLocale = DayPickerLocale;\n\n/** The words the kit's controls say; a host gives its own, in its language. */\nexport interface KitMessages {\n /** A field left empty that must be filled. */\n required: string;\n /** A day, a time, both, an option, a rating still to pick; also an empty picker's invitation. */\n chooseDate: string;\n chooseTime: string;\n chooseMoment: string;\n chooseOption: string;\n chooseRating: string;\n /** A box that must be ticked to go on. */\n check: string;\n invalidEmail: string;\n invalidPhone: string;\n invalidUrl: string;\n invalidNumber: string;\n /** A time typed in a shape that is not one. */\n invalidTime: string;\n /** A number under its least, or over its most, given formatted. */\n atLeast: (min: string) => string;\n atMost: (max: string) => string;\n /** A day or a time that is not offered. */\n unavailableDate: string;\n unavailableTime: string;\n /** Beside the label of a field that may stay empty. */\n optional: string;\n /** In a long list's box while nothing is picked. */\n pick: string;\n /** In that box while the person types. */\n search: string;\n noResults: string;\n /** The button that shows or hides a secret as it is typed. */\n show: string;\n hide: string;\n /** A number's steppers, for screen readers. */\n increase: string;\n decrease: string;\n /** A picked tag's remove button, for screen readers. */\n remove: (label: string) => string;\n /** A box ticked or not, as an answer reads. */\n yes: string;\n no: string;\n /** A rating, as an answer reads. */\n rated: (value: number, max: number) => string;\n /** One star of a rating, for screen readers. */\n stars: (count: number) => string;\n /** A choice's last row, to answer in one's own words, and its box while empty. */\n other: string;\n otherPlaceholder: string;\n /** A form's submit when the agent gave it no words. */\n send: string;\n /** Where a form's submit was, once it is sent. */\n sent: string;\n /** A file field left empty that must be filled. */\n chooseFile: string;\n /** A file field's drop zone: what it asks, and how. */\n upload: (multiple: boolean) => string;\n drop: string;\n /**\n * Under them, what it takes: the kinds (named and joined, \"\" when anything) and the most a\n * file may weigh, formatted.\n */\n limits: (kinds: string, size: string) => string;\n /** A family of kinds as the limits name it: \"image\" for any image, \"video\", \"audio\", \"text\". */\n anyOf: (family: string) => string;\n /** A file on its way, beside how much has gone. */\n uploading: string;\n /** A file that was not sent: heavier than the most (formatted), a kind not taken, one too many. */\n tooBig: (size: string) => string;\n wrongType: string;\n tooMany: (max: number) => string;\n /** A file whose upload failed, and the button that tries it again. */\n uploadFailed: string;\n retry: string;\n /** A form sent while a file is still on its way, or one failed. */\n stillUploading: string;\n fixFiles: string;\n /** A file's chip, opened on a click, for screen readers. */\n open: (name: string) => string;\n /** A file whose name is not known yet. */\n file: string;\n /** An audio player's button, and its way through, for screen readers. */\n play: string;\n pause: string;\n position: string;\n /** An automation and its runs: when it runs, how it stands, what each run did. */\n automation: AutomationMessages;\n /** An app for the person to connect, as its connection stands. */\n connect: ConnectMessages;\n /** A calendar's words. */\n calendar: CalendarMessages;\n /** A board's words. */\n board: BoardMessages;\n}\n\n/** What a calendar says besides its events. */\nexport interface CalendarMessages {\n /** An event that takes the whole day, where its hours would be. */\n allDay: string;\n /** A day with nothing on it. */\n empty: string;\n}\n\n/** What a board says besides its cards. */\nexport interface BoardMessages {\n /** A card's button that moves it to another column, given the card, for screen readers. */\n move: (card: string) => string;\n /** Over the columns it may go to. */\n moveTo: string;\n /** A card moved, as the person's answer reads. */\n moved: (card: string, column: string) => string;\n /** A column with no cards. */\n empty: string;\n}\n\n/** The words of an app's card: its button, and what it says where the button isn't. */\nexport interface ConnectMessages {\n /** The button that opens the app's consent page, given the app's name. */\n authorize: (app: string) => string;\n /** It works. */\n connected: string;\n /** It failed, or the account was lost: the agent asks for it again. */\n failed: string;\n /** Before the platform has the consent page. */\n preparing: string;\n}\n\nconst FAMILIES: Readonly<Record<string, string>> = {\n image: \"images\",\n video: \"videos\",\n audio: \"audio\",\n text: \"text\",\n};\n\nexport const KIT_MESSAGES: KitMessages = {\n required: \"This field is required\",\n chooseDate: \"Choose a day\",\n chooseTime: \"Choose a time\",\n chooseMoment: \"Choose a day and a time\",\n chooseOption: \"Choose an option\",\n other: \"Another answer\",\n otherPlaceholder: \"Write your answer\",\n chooseRating: \"Choose a rating\",\n check: \"Tick the box to go on\",\n invalidEmail: \"Enter a valid email\",\n invalidPhone: \"Enter a valid phone number\",\n invalidUrl: \"Enter a valid address\",\n invalidNumber: \"Enter a number\",\n invalidTime: \"Write a time like 9:30\",\n atLeast: (min) => `At least ${min}`,\n atMost: (max) => `At most ${max}`,\n unavailableDate: \"That day is not available\",\n unavailableTime: \"That time is not available\",\n optional: \"Optional\",\n pick: \"Choose…\",\n search: \"Search…\",\n noResults: \"No results\",\n show: \"Show\",\n hide: \"Hide\",\n increase: \"Increase\",\n decrease: \"Decrease\",\n remove: (label) => `Remove ${label}`,\n yes: \"Yes\",\n no: \"No\",\n rated: (value, max) => `${value} of ${max}`,\n stars: (count) => (count === 1 ? \"1 star\" : `${count} stars`),\n send: \"Send\",\n sent: \"Sent\",\n chooseFile: \"Add a file\",\n upload: (multiple) => (multiple ? \"Upload files\" : \"Upload a file\"),\n drop: \"Drop it here or choose\",\n limits: (kinds, size) => (kinds ? `${kinds}, up to ${size}` : `Up to ${size}`),\n anyOf: (family) => FAMILIES[family] ?? family,\n uploading: \"Uploading\",\n tooBig: (size) => `Over ${size}`,\n wrongType: \"This kind of file isn't taken here\",\n tooMany: (max) => `At most ${max} files`,\n uploadFailed: \"Couldn't upload it\",\n retry: \"Retry\",\n stillUploading: \"Wait for the upload to finish\",\n fixFiles: \"Remove or retry the files that failed\",\n open: (name) => `Open ${name}`,\n file: \"File\",\n play: \"Play\",\n pause: \"Pause\",\n position: \"Position\",\n automation: AUTOMATION_MESSAGES,\n connect: {\n authorize: (app) => `Authorize in ${app}`,\n connected: \"Connected\",\n failed: \"It couldn't be connected\",\n preparing: \"Getting it ready\",\n },\n calendar: { allDay: \"All day\", empty: \"Nothing planned\" },\n board: {\n move: (card) => `Move ${card}`,\n moveTo: \"Move to\",\n moved: (card, column) => `${card} to ${column}`,\n empty: \"Nothing here\",\n },\n};\n"],"mappings":";;AAyIA,MAAM,WAA6C;CACjD,OAAO;CACP,OAAO;CACP,OAAO;CACP,MAAM;AACR;AAEA,MAAa,eAA4B;CACvC,UAAU;CACV,YAAY;CACZ,YAAY;CACZ,cAAc;CACd,cAAc;CACd,OAAO;CACP,kBAAkB;CAClB,cAAc;CACd,OAAO;CACP,cAAc;CACd,cAAc;CACd,YAAY;CACZ,eAAe;CACf,aAAa;CACb,UAAU,QAAQ,YAAY;CAC9B,SAAS,QAAQ,WAAW;CAC5B,iBAAiB;CACjB,iBAAiB;CACjB,UAAU;CACV,MAAM;CACN,QAAQ;CACR,WAAW;CACX,MAAM;CACN,MAAM;CACN,UAAU;CACV,UAAU;CACV,SAAS,UAAU,UAAU;CAC7B,KAAK;CACL,IAAI;CACJ,QAAQ,OAAO,QAAQ,GAAG,MAAM,MAAM;CACtC,QAAQ,UAAW,UAAU,IAAI,WAAW,GAAG,MAAM;CACrD,MAAM;CACN,MAAM;CACN,YAAY;CACZ,SAAS,aAAc,WAAW,iBAAiB;CACnD,MAAM;CACN,SAAS,OAAO,SAAU,QAAQ,GAAG,MAAM,UAAU,SAAS,SAAS;CACvE,QAAQ,WAAW,SAAS,WAAW;CACvC,WAAW;CACX,SAAS,SAAS,QAAQ;CAC1B,WAAW;CACX,UAAU,QAAQ,WAAW,IAAI;CACjC,cAAc;CACd,OAAO;CACP,gBAAgB;CAChB,UAAU;CACV,OAAO,SAAS,QAAQ;CACxB,MAAM;CACN,MAAM;CACN,OAAO;CACP,UAAU;CACV,YAAY;CACZ,SAAS;EACP,YAAY,QAAQ,gBAAgB;EACpC,WAAW;EACX,QAAQ;EACR,WAAW;CACb;CACA,UAAU;EAAE,QAAQ;EAAW,OAAO;CAAkB;CACxD,OAAO;EACL,OAAO,SAAS,QAAQ;EACxB,QAAQ;EACR,QAAQ,MAAM,WAAW,GAAG,KAAK,MAAM;EACvC,OAAO;CACT;AACF"}
|
package/dist/surface/Form.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { useKit } from "../kit/context.js";
|
|
2
2
|
import { Button } from "../kit/Button.js";
|
|
3
3
|
import { Field } from "../kit/Field.js";
|
|
4
|
+
import { Checkbox, NumberInput, SecretInput, TextArea, TextInput } from "../kit/Inputs.js";
|
|
4
5
|
import { FileField } from "../kit/FileField.js";
|
|
5
6
|
import { Chips, Choices } from "../kit/Choices.js";
|
|
6
7
|
import { Combobox } from "../kit/Combobox.js";
|
|
7
8
|
import { DateField } from "../kit/DateField.js";
|
|
8
|
-
import { Checkbox, NumberInput, SecretInput, TextArea, TextInput } from "../kit/Inputs.js";
|
|
9
9
|
import { RangeField } from "../kit/RangeField.js";
|
|
10
10
|
import { RatingField } from "../kit/RatingField.js";
|
|
11
11
|
import { DateTimeField, TimeField } from "../kit/TimeField.js";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/surface/Placed.d.ts
|
|
3
|
+
/** The parts of a surface shown apart from the conversation, by the class each carries. */
|
|
4
|
+
export type PlacedPart = "sc-placed" | "sc-placed--panel" | "sc-placed--modal" | "sc-placed__head" | "sc-placed__title" | "sc-placed__body";
|
|
5
|
+
/**
|
|
6
|
+
* A surface the agent put apart from the conversation, tied to it all the same: in a `panel` that
|
|
7
|
+
* stands beside the conversation (over it where there is no room), or in a `modal` over the page,
|
|
8
|
+
* whose backdrop, Escape or cross close it. Its title heads it; closed, the conversation's line
|
|
9
|
+
* opens it again.
|
|
10
|
+
*/
|
|
11
|
+
export declare function Placed({ where, title, closeLabel, onClose, classNames, children }: {
|
|
12
|
+
where: "panel" | "modal";
|
|
13
|
+
title: string;
|
|
14
|
+
closeLabel: string;
|
|
15
|
+
onClose: () => void;
|
|
16
|
+
classNames: Partial<Record<PlacedPart, string>> | undefined;
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
}): import("react").JSX.Element;
|
|
19
|
+
//#endregion
|
|
20
|
+
//# sourceMappingURL=Placed.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { classes } from "../classes.js";
|
|
2
|
+
import { Button } from "../kit/Button.js";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { XIcon } from "@phosphor-icons/react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/surface/Placed.tsx
|
|
7
|
+
/**
|
|
8
|
+
* A surface the agent put apart from the conversation, tied to it all the same: in a `panel` that
|
|
9
|
+
* stands beside the conversation (over it where there is no room), or in a `modal` over the page,
|
|
10
|
+
* whose backdrop, Escape or cross close it. Its title heads it; closed, the conversation's line
|
|
11
|
+
* opens it again.
|
|
12
|
+
*/
|
|
13
|
+
function Placed({ where, title, closeLabel, onClose, classNames, children }) {
|
|
14
|
+
const dialog = useRef(null);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const opened = dialog.current;
|
|
17
|
+
if (opened === null) return void 0;
|
|
18
|
+
if (typeof opened.showModal === "function") opened.showModal();
|
|
19
|
+
else opened.setAttribute("open", "");
|
|
20
|
+
return () => {
|
|
21
|
+
if (typeof opened.close === "function") opened.close();
|
|
22
|
+
};
|
|
23
|
+
}, []);
|
|
24
|
+
const head = /* @__PURE__ */ jsxs("header", {
|
|
25
|
+
className: classes(classNames, "sc-placed__head"),
|
|
26
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
27
|
+
className: classes(classNames, "sc-placed__title"),
|
|
28
|
+
children: title
|
|
29
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
30
|
+
tone: "ghost",
|
|
31
|
+
size: "sm",
|
|
32
|
+
icon: XIcon,
|
|
33
|
+
"aria-label": closeLabel,
|
|
34
|
+
title: closeLabel,
|
|
35
|
+
onClick: onClose
|
|
36
|
+
})]
|
|
37
|
+
});
|
|
38
|
+
const onKeyDown = (event) => {
|
|
39
|
+
if (event.key !== "Escape") return;
|
|
40
|
+
event.stopPropagation();
|
|
41
|
+
if (where === "panel") onClose();
|
|
42
|
+
};
|
|
43
|
+
const body = /* @__PURE__ */ jsx("div", {
|
|
44
|
+
className: classes(classNames, "sc-placed__body"),
|
|
45
|
+
children
|
|
46
|
+
});
|
|
47
|
+
if (where === "panel") return /* @__PURE__ */ jsxs("aside", {
|
|
48
|
+
part: "placed panel",
|
|
49
|
+
"aria-label": title,
|
|
50
|
+
onKeyDown,
|
|
51
|
+
className: classes(classNames, "sc-placed", "sc-placed--panel"),
|
|
52
|
+
children: [head, body]
|
|
53
|
+
});
|
|
54
|
+
return /* @__PURE__ */ jsxs("dialog", {
|
|
55
|
+
ref: dialog,
|
|
56
|
+
part: "placed modal",
|
|
57
|
+
"aria-label": title,
|
|
58
|
+
onKeyDown,
|
|
59
|
+
onClose,
|
|
60
|
+
onClick: (event) => {
|
|
61
|
+
if (event.target === event.currentTarget) onClose();
|
|
62
|
+
},
|
|
63
|
+
className: `sc ${classes(classNames, "sc-placed", "sc-placed--modal")}`,
|
|
64
|
+
children: [head, body]
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
export { Placed };
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=Placed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Placed.js","names":[],"sources":["../../src/surface/Placed.tsx"],"sourcesContent":["import { XIcon } from \"@phosphor-icons/react\";\nimport { useEffect, useRef, type KeyboardEvent, type ReactNode } from \"react\";\n\nimport { classes } from \"../classes\";\nimport { Button } from \"../kit/Button\";\n\n/** The parts of a surface shown apart from the conversation, by the class each carries. */\nexport type PlacedPart =\n | \"sc-placed\"\n | \"sc-placed--panel\"\n | \"sc-placed--modal\"\n | \"sc-placed__head\"\n | \"sc-placed__title\"\n | \"sc-placed__body\";\n\n/**\n * A surface the agent put apart from the conversation, tied to it all the same: in a `panel` that\n * stands beside the conversation (over it where there is no room), or in a `modal` over the page,\n * whose backdrop, Escape or cross close it. Its title heads it; closed, the conversation's line\n * opens it again.\n */\nexport function Placed({\n where,\n title,\n closeLabel,\n onClose,\n classNames,\n children,\n}: {\n where: \"panel\" | \"modal\";\n title: string;\n closeLabel: string;\n onClose: () => void;\n classNames: Partial<Record<PlacedPart, string>> | undefined;\n children: ReactNode;\n}) {\n const dialog = useRef<HTMLDialogElement>(null);\n useEffect(() => {\n const opened = dialog.current;\n if (opened === null) return undefined;\n // jsdom has no top layer: a dialog there is only shown.\n if (typeof opened.showModal === \"function\") opened.showModal();\n else opened.setAttribute(\"open\", \"\");\n return () => {\n if (typeof opened.close === \"function\") opened.close();\n };\n }, []);\n const head = (\n <header className={classes(classNames, \"sc-placed__head\")}>\n <h2 className={classes(classNames, \"sc-placed__title\")}>{title}</h2>\n <Button\n tone=\"ghost\"\n size=\"sm\"\n icon={XIcon}\n aria-label={closeLabel}\n title={closeLabel}\n onClick={onClose}\n />\n </header>\n );\n // Escape closes this, not the reply the conversation is writing.\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n event.stopPropagation();\n if (where === \"panel\") onClose();\n };\n const body = <div className={classes(classNames, \"sc-placed__body\")}>{children}</div>;\n if (where === \"panel\") {\n return (\n <aside\n part=\"placed panel\"\n aria-label={title}\n onKeyDown={onKeyDown}\n className={classes(classNames, \"sc-placed\", \"sc-placed--panel\")}\n >\n {head}\n {body}\n </aside>\n );\n }\n return (\n <dialog\n ref={dialog}\n part=\"placed modal\"\n aria-label={title}\n onKeyDown={onKeyDown}\n onClose={onClose}\n // A press on the backdrop (the dialog itself, outside its box) closes it.\n onClick={(event) => {\n if (event.target === event.currentTarget) onClose();\n }}\n className={`sc ${classes(classNames, \"sc-placed\", \"sc-placed--modal\")}`}\n >\n {head}\n {body}\n </dialog>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAqBA,SAAgB,OAAO,EACrB,OACA,OACA,YACA,SACA,YACA,YAQC;CACD,MAAM,SAAS,OAA0B,IAAI;CAC7C,gBAAgB;EACd,MAAM,SAAS,OAAO;EACtB,IAAI,WAAW,MAAM,OAAO,KAAA;EAE5B,IAAI,OAAO,OAAO,cAAc,YAAY,OAAO,UAAU;OACxD,OAAO,aAAa,QAAQ,EAAE;EACnC,aAAa;GACX,IAAI,OAAO,OAAO,UAAU,YAAY,OAAO,MAAM;EACvD;CACF,GAAG,CAAC,CAAC;CACL,MAAM,OACJ,qBAAC,UAAD;EAAQ,WAAW,QAAQ,YAAY,iBAAiB;EAAxD,UAAA,CACE,oBAAC,MAAD;GAAI,WAAW,QAAQ,YAAY,kBAAkB;GAAI,UAAA;EAAU,CAAA,GACnE,oBAAC,QAAD;GACE,MAAK;GACL,MAAK;GACL,MAAM;GACN,cAAY;GACZ,OAAO;GACP,SAAS;EACV,CAAA,CACK;;CAGV,MAAM,aAAa,UAAyB;EAC1C,IAAI,MAAM,QAAQ,UAAU;EAC5B,MAAM,gBAAgB;EACtB,IAAI,UAAU,SAAS,QAAQ;CACjC;CACA,MAAM,OAAO,oBAAC,OAAD;EAAK,WAAW,QAAQ,YAAY,iBAAiB;EAAI;CAAc,CAAA;CACpF,IAAI,UAAU,SACZ,OACE,qBAAC,SAAD;EACE,MAAK;EACL,cAAY;EACD;EACX,WAAW,QAAQ,YAAY,aAAa,kBAAkB;EAJhE,UAAA,CAMG,MACA,IACI;;CAGX,OACE,qBAAC,UAAD;EACE,KAAK;EACL,MAAK;EACL,cAAY;EACD;EACF;EAET,UAAU,UAAU;GAClB,IAAI,MAAM,WAAW,MAAM,eAAe,QAAQ;EACpD;EACA,WAAW,MAAM,QAAQ,YAAY,aAAa,kBAAkB;EAVtE,UAAA,CAYG,MACA,IACK;;AAEZ"}
|
|
@@ -2,7 +2,7 @@ import { Surface as Surface$1 } from "../blocks.js";
|
|
|
2
2
|
import { KitLocale, KitMessages } from "../kit/messages.js";
|
|
3
3
|
import { FileHost } from "../kit/uploads.js";
|
|
4
4
|
import { SurfaceAction } from "./values.js";
|
|
5
|
-
import { SurfaceClassNames, SurfaceComponents } from "./scope.js";
|
|
5
|
+
import { HostBlocks, SurfaceClassNames, SurfaceComponents } from "./scope.js";
|
|
6
6
|
//#region src/surface/Surface.d.ts
|
|
7
7
|
export interface SurfaceProps {
|
|
8
8
|
surface: Surface$1;
|
|
@@ -12,6 +12,8 @@ export interface SurfaceProps {
|
|
|
12
12
|
/** What the person already did on it: the option picked, the button pressed, the form sent. */
|
|
13
13
|
answered?: SurfaceAction | undefined;
|
|
14
14
|
components?: SurfaceComponents | undefined;
|
|
15
|
+
/** The host's own blocks, by the name each was declared with. */
|
|
16
|
+
blocks?: HostBlocks | undefined;
|
|
15
17
|
classNames?: SurfaceClassNames;
|
|
16
18
|
/** The words its controls say, in the host's language; English by default. */
|
|
17
19
|
messages?: Partial<KitMessages> | undefined;
|
|
@@ -30,6 +32,6 @@ export interface SurfaceProps {
|
|
|
30
32
|
* is half-drawn. Each block's action comes back through `onAction`; the host wraps it with the
|
|
31
33
|
* surface's id.
|
|
32
34
|
*/
|
|
33
|
-
export declare function Surface({ surface, onAction, active, answered, components, classNames, messages, locale, files }: SurfaceProps): import("react").JSX.Element;
|
|
35
|
+
export declare function Surface({ surface, onAction, active, answered, components, blocks, classNames, messages, locale, files }: SurfaceProps): import("react").JSX.Element;
|
|
34
36
|
//#endregion
|
|
35
37
|
//# sourceMappingURL=Surface.d.ts.map
|
package/dist/surface/Surface.js
CHANGED
|
@@ -11,8 +11,11 @@ import { baseName } from "../files.js";
|
|
|
11
11
|
import { FileMark } from "../kit/FileMark.js";
|
|
12
12
|
import { AudioPlayer } from "../kit/AudioPlayer.js";
|
|
13
13
|
import { Markdown } from "./Markdown.js";
|
|
14
|
+
import { Agenda } from "../kit/Agenda.js";
|
|
15
|
+
import { Kanban } from "../kit/Kanban.js";
|
|
14
16
|
import { Card } from "../kit/Card.js";
|
|
15
17
|
import { Chart } from "../kit/Chart.js";
|
|
18
|
+
import { Timeline } from "../kit/Timeline.js";
|
|
16
19
|
import { ScopeContext, useScope, useSurfaceParts } from "./scope.js";
|
|
17
20
|
import { FIELD_KINDS, answeredIn, chosen, picked, pressed } from "./values.js";
|
|
18
21
|
import { FormView } from "./Form.js";
|
|
@@ -37,7 +40,11 @@ const BLOCKS = /* @__PURE__ */ new Set([
|
|
|
37
40
|
"connect",
|
|
38
41
|
"button",
|
|
39
42
|
"choice",
|
|
40
|
-
"form"
|
|
43
|
+
"form",
|
|
44
|
+
"calendar",
|
|
45
|
+
"timeline",
|
|
46
|
+
"board",
|
|
47
|
+
"host"
|
|
41
48
|
]);
|
|
42
49
|
/** Whether every block down the tree, and every field of its forms, is one this renderer draws. */
|
|
43
50
|
function known(blocks) {
|
|
@@ -62,7 +69,7 @@ function framesItself(surface) {
|
|
|
62
69
|
* is half-drawn. Each block's action comes back through `onAction`; the host wraps it with the
|
|
63
70
|
* surface's id.
|
|
64
71
|
*/
|
|
65
|
-
function Surface({ surface, onAction, active = true, answered, components, classNames, messages, locale, files }) {
|
|
72
|
+
function Surface({ surface, onAction, active = true, answered, components, blocks, classNames, messages, locale, files }) {
|
|
66
73
|
return /* @__PURE__ */ jsx(KitProvider, {
|
|
67
74
|
classNames,
|
|
68
75
|
messages,
|
|
@@ -73,6 +80,7 @@ function Surface({ surface, onAction, active = true, answered, components, class
|
|
|
73
80
|
answered,
|
|
74
81
|
onAction,
|
|
75
82
|
components,
|
|
83
|
+
blocks,
|
|
76
84
|
classNames,
|
|
77
85
|
files
|
|
78
86
|
},
|
|
@@ -159,8 +167,76 @@ function Draw({ block }) {
|
|
|
159
167
|
case "button": return /* @__PURE__ */ jsx(Pressable, { block });
|
|
160
168
|
case "choice": return /* @__PURE__ */ jsx(Choice, { block });
|
|
161
169
|
case "form": return /* @__PURE__ */ jsx(FormView, { block });
|
|
170
|
+
case "calendar": return /* @__PURE__ */ jsx(CalendarShown, { block });
|
|
171
|
+
case "timeline": return /* @__PURE__ */ jsx(Timeline, { items: block.items });
|
|
172
|
+
case "board": return /* @__PURE__ */ jsx(BoardShown, { block });
|
|
173
|
+
case "host": return /* @__PURE__ */ jsx(Hosted, { block });
|
|
162
174
|
}
|
|
163
175
|
}
|
|
176
|
+
/** A calendar; with its action, pressing an event sends it (`{event}`). */
|
|
177
|
+
function CalendarShown({ block }) {
|
|
178
|
+
const { active, onAction } = useScope();
|
|
179
|
+
const { action } = block;
|
|
180
|
+
return /* @__PURE__ */ jsx(Agenda, {
|
|
181
|
+
span: block.span,
|
|
182
|
+
start: block.start,
|
|
183
|
+
events: block.events,
|
|
184
|
+
disabled: !active,
|
|
185
|
+
onEvent: action === void 0 ? void 0 : (event) => {
|
|
186
|
+
onAction({
|
|
187
|
+
name: action,
|
|
188
|
+
data: { event: event.id },
|
|
189
|
+
label: event.title
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/** A board; with its action, a card moved to another column sends it (`{card, column}`). */
|
|
195
|
+
function BoardShown({ block }) {
|
|
196
|
+
const { active, onAction } = useScope();
|
|
197
|
+
const { messages } = useKit();
|
|
198
|
+
const { action } = block;
|
|
199
|
+
return /* @__PURE__ */ jsx(Kanban, {
|
|
200
|
+
columns: block.columns,
|
|
201
|
+
disabled: !active,
|
|
202
|
+
onMove: action === void 0 ? void 0 : (card, column) => {
|
|
203
|
+
onAction({
|
|
204
|
+
name: action,
|
|
205
|
+
data: {
|
|
206
|
+
card: card.id,
|
|
207
|
+
column: column.id
|
|
208
|
+
},
|
|
209
|
+
label: messages.board.moved(card.title, column.title)
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* One of the host's own blocks, drawn by the component it registered under the block's name; a
|
|
216
|
+
* name it gave none for, or a component that fails as it draws, leaves the block's words in a
|
|
217
|
+
* quiet card in its place, and the rest of the surface draws.
|
|
218
|
+
*/
|
|
219
|
+
function Hosted({ block }) {
|
|
220
|
+
const { blocks, active, onAction } = useScope();
|
|
221
|
+
const part = useSurfaceParts();
|
|
222
|
+
const twin = /* @__PURE__ */ jsx("div", {
|
|
223
|
+
part: "host",
|
|
224
|
+
className: part("sc-surface__host"),
|
|
225
|
+
"data-block": block.name,
|
|
226
|
+
children: /* @__PURE__ */ jsx(Markdown, { text: block.text })
|
|
227
|
+
});
|
|
228
|
+
const Own = blocks?.[block.name];
|
|
229
|
+
if (Own === void 0) return twin;
|
|
230
|
+
return /* @__PURE__ */ jsx(Whole, {
|
|
231
|
+
twin,
|
|
232
|
+
children: /* @__PURE__ */ jsx(Own, {
|
|
233
|
+
props: block.props,
|
|
234
|
+
text: block.text,
|
|
235
|
+
active,
|
|
236
|
+
onAction
|
|
237
|
+
})
|
|
238
|
+
});
|
|
239
|
+
}
|
|
164
240
|
/**
|
|
165
241
|
* A file the agent put in front of the person, compact: its kind's mark, its title (its name
|
|
166
242
|
* under it when the title is another), its size at the right; pressed, it opens whole in the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Surface.js","names":["Host"],"sources":["../../src/surface/Surface.tsx"],"sourcesContent":["import { ArrowUpIcon, CheckIcon, PencilSimpleIcon } from \"@phosphor-icons/react\";\nimport { Component, useEffect, useRef, useState, type ComponentType, type ReactNode } from \"react\";\n\nimport { Automation } from \"../automation/Automation\";\nimport type {\n AutomationBlock,\n Block,\n ButtonBlock,\n ChoiceBlock,\n ConnectBlock,\n FieldsBlock,\n ImageBlock,\n ListBlock,\n Surface as Shown,\n TableBlock,\n} from \"../blocks\";\nimport { classes } from \"../classes\";\nimport { Button } from \"../kit/Button\";\nimport { formatSize } from \"../about\";\nimport { baseName } from \"../files\";\nimport { Card } from \"../kit/Card\";\nimport { Chart } from \"../kit/Chart\";\nimport { KitProvider, useKit, useParts } from \"../kit/context\";\nimport { FileMark } from \"../kit/FileMark\";\nimport { List, Row } from \"../kit/List\";\nimport { Mark } from \"../kit/Mark\";\nimport type { KitLocale, KitMessages } from \"../kit/messages\";\nimport type { FileHost } from \"../kit/uploads\";\nimport { AudioPlayer } from \"../kit/AudioPlayer\";\nimport { Note } from \"../kit/Note\";\nimport { Stat } from \"../kit/Stat\";\nimport type { Attachment, FileBlock } from \"../types\";\nimport { FormView } from \"./Form\";\nimport { Markdown } from \"./Markdown\";\nimport {\n ScopeContext,\n useScope,\n useSurfaceParts,\n type Scope,\n type SurfaceClassNames,\n type SurfaceComponents,\n} from \"./scope\";\nimport { answeredIn, chosen, FIELD_KINDS, picked, pressed, type SurfaceAction } from \"./values\";\n\nexport interface SurfaceProps {\n surface: Shown;\n onAction: (action: SurfaceAction) => void;\n /** False draws the inputs disabled: a surface already answered, or one from an older turn. */\n active?: boolean | undefined;\n /** What the person already did on it: the option picked, the button pressed, the form sent. */\n answered?: SurfaceAction | undefined;\n components?: SurfaceComponents | undefined;\n classNames?: SurfaceClassNames;\n /** The words its controls say, in the host's language; English by default. */\n messages?: Partial<KitMessages> | undefined;\n /** The language of its calendar, days, times and numbers; English by default. */\n locale?: KitLocale | undefined;\n /**\n * Where its file fields' files go (uploaded as they are picked) and how the ones sent are found\n * and opened: the conversation's. None: a file field takes no files.\n */\n files?: FileHost | undefined;\n}\n\nconst BLOCKS: ReadonlySet<string> = new Set([\n \"card\",\n \"stack\",\n \"divider\",\n \"text\",\n \"fields\",\n \"table\",\n \"list\",\n \"stat\",\n \"chart\",\n \"note\",\n \"image\",\n \"automation\",\n \"file\",\n \"connect\",\n \"button\",\n \"choice\",\n \"form\",\n]);\n\n/** Whether every block down the tree, and every field of its forms, is one this renderer draws. */\nexport function known(blocks: Block[]): boolean {\n return blocks.every((block) => {\n if (!BLOCKS.has(block.type)) return false;\n if (block.type === \"form\") return block.fields.every((field) => FIELD_KINDS.has(field.kind));\n return \"children\" in block ? known(block.children) : true;\n });\n}\n\n/**\n * Whether a screen draws its own edge: one that is a single card (or an automation, or an app\n * to connect, each a card of its own) needs no panel around it where it is shown.\n */\nexport function framesItself(surface: Shown): boolean {\n const [only, ...more] = surface.blocks;\n return (\n more.length === 0 &&\n (only?.type === \"card\" || only?.type === \"automation\" || only?.type === \"connect\")\n );\n}\n\n/**\n * What the agent showed, drawn block by block with the kit's primitives (a `card` is a `Card`, a\n * `list` a `List` of `Row`s, a form's fields its controls); a block or a field kind the API has\n * grown since this renderer was written leaves the whole surface to its text twin, so nothing\n * is half-drawn. Each block's action comes back through `onAction`; the host wraps it with the\n * surface's id.\n */\nexport function Surface({\n surface,\n onAction,\n active = true,\n answered,\n components,\n classNames,\n messages,\n locale,\n files,\n}: SurfaceProps) {\n const scope: Scope = { active, answered, onAction, components, classNames, files };\n return (\n <KitProvider classNames={classNames} messages={messages} locale={locale}>\n <ScopeContext value={scope}>\n <div className={`sc ${classes(classNames, \"sc-surface\")}`}>\n <Whole twin={<p className={classes(classNames, \"sc-surface__text\")}>{surface.text}</p>}>\n {known(surface.blocks)\n ? surface.blocks.map((block, index) => <Draw key={index} block={block} />)\n : null}\n </Whole>\n </div>\n </ScopeContext>\n </KitProvider>\n );\n}\n\n/**\n * The surface drawn whole or not at all: what it can't draw (blocks it doesn't know, or one that\n * fails as it draws) leaves its text twin in its place, never a broken page.\n */\nclass Whole extends Component<{ twin: ReactNode; children: ReactNode }, { failed: boolean }> {\n override state = { failed: false };\n\n static getDerivedStateFromError(): { failed: boolean } {\n return { failed: true };\n }\n\n override render() {\n return this.state.failed || this.props.children === null\n ? this.props.twin\n : this.props.children;\n }\n}\n\n/** One block: the host's own component for its type when it gave one, else the kit's. */\nfunction Draw({ block }: { block: Block }) {\n const { active, onAction, components } = useScope();\n const part = useSurfaceParts();\n const children =\n \"children\" in block\n ? block.children.map((child, index) => <Draw key={index} block={child} />)\n : undefined;\n const Own = components?.[block.type];\n if (Own) {\n // Found by the block's own type, so its fields are the right props; the union is too wide\n // for the compiler to see that pairing.\n const Host = Own as ComponentType<Record<string, unknown>>;\n return <Host {...block} active={active} onAction={onAction} children={children} />;\n }\n switch (block.type) {\n case \"card\":\n return (\n <Card title={block.title} subtitle={block.subtitle} image={block.image}>\n {children}\n </Card>\n );\n case \"stack\":\n return (\n <div\n part=\"stack\"\n className={part(\n \"sc-surface__stack\",\n ...(block.direction === \"row\" ? ([\"sc-surface__stack--row\"] as const) : []),\n )}\n >\n {children}\n </div>\n );\n case \"divider\":\n return <hr part=\"divider\" className={part(\"sc-surface__divider\")} />;\n case \"text\":\n return (\n <div part=\"text\" className={part(\"sc-surface__text\")}>\n <Markdown text={block.text} />\n </div>\n );\n case \"fields\":\n return <Fields block={block} />;\n case \"table\":\n return <Table block={block} />;\n case \"list\":\n return <Listing block={block} />;\n case \"stat\":\n return <Stat label={block.label} value={block.value} hint={block.hint} tone={block.tone} />;\n case \"chart\":\n return <Chart kind={block.kind} points={block.points} />;\n case \"note\":\n return (\n <Note tone={block.tone}>\n <Markdown text={block.text} />\n </Note>\n );\n case \"image\":\n return <Picture block={block} />;\n case \"automation\":\n return <AutomationShown block={block} />;\n case \"file\":\n return <FileShown block={block} />;\n case \"connect\":\n return <Connect block={block} />;\n case \"button\":\n return <Pressable block={block} />;\n case \"choice\":\n return <Choice block={block} />;\n case \"form\":\n return <FormView block={block} />;\n }\n}\n\n/**\n * A file the agent put in front of the person, compact: its kind's mark, its title (its name\n * under it when the title is another), its size at the right; pressed, it opens whole in the\n * host's viewer (`files.open`), whatever its kind. A picture, a video or a sound plays where it\n * is instead, when the host can play it (`files.play`). Until the API has put it in the\n * conversation it is only named, quietly.\n */\nfunction FileShown({ block }: { block: FileBlock }) {\n const { files } = useScope();\n const { code } = useKit();\n const { file } = block;\n const name = file?.name ?? baseName(block.path);\n const title = block.title ?? name;\n const open = file !== null ? files?.open : undefined;\n const played = file !== null ? playedAs(file.media_type) : null;\n const src = usePlayed(file, played !== null ? files?.play : undefined);\n if (file !== null && played !== null && src !== null) {\n return (\n <Played\n kind={played}\n src={src.url}\n name={name}\n caption={block.title}\n onOpen={open ? () => open(file) : undefined}\n onLost={src.again}\n />\n );\n }\n return (\n <List>\n <Row\n mark={<FileMark name={name} type={file?.media_type} />}\n title={title}\n subtitle={title === name ? undefined : name}\n meta={file === null ? undefined : formatSize(file.size, code)}\n quiet={file === null}\n onPress={open && file !== null ? () => open(file) : undefined}\n />\n </List>\n );\n}\n\ntype PlayedKind = \"image\" | \"video\" | \"audio\";\n\n/** How a file plays in the page: a picture, a video, a sound; null for any other kind. */\nfunction playedAs(mediaType: string): PlayedKind | null {\n const family = mediaType.split(\"/\")[0];\n return family === \"image\" || family === \"video\" || family === \"audio\" ? family : null;\n}\n\n/**\n * Where `file` plays from, asked of the host once (`play`): null until it answers, and when it\n * can't. `again` asks for a new address, for a player whose address ran out.\n */\nexport function usePlayed(\n file: Attachment | null,\n play: ((file: Attachment) => Promise<string>) | undefined,\n): { url: string; again: () => void } | null {\n const [url, setUrl] = useState<string | null>(null);\n const [asked, setAsked] = useState(0);\n // The file and the host's way to play it as they are now: a block drawn again carries the\n // same file anew, and the host its functions; what is asked for changes with the file's id.\n const latest = useRef({ file, play });\n latest.current = { file, play };\n const id = file?.id;\n const playable = play !== undefined;\n useEffect(() => {\n const { file: now, play: player } = latest.current;\n if (now === null || player === undefined) return;\n let current = true;\n player(now).then(\n (address) => current && setUrl(address),\n () => current && setUrl(null),\n );\n return () => {\n current = false;\n };\n }, [id, playable, asked]);\n return url === null ? null : { url, again: () => setAsked((times) => times + 1) };\n}\n\n/**\n * A picture, a video or a sound the agent put in front of the person, where it is: the\n * browser's own player, streaming from the address it plays from; a picture pressed opens whole\n * in the host's viewer. A player whose address ran out asks for a new one, once.\n */\nfunction Played({\n kind,\n src,\n name,\n caption,\n onOpen,\n onLost,\n}: {\n kind: PlayedKind;\n src: string;\n name: string;\n caption: string | undefined;\n onOpen: (() => void) | undefined;\n onLost: () => void;\n}) {\n const part = useSurfaceParts();\n const lost = useRef(false);\n const once = () => {\n if (lost.current) return;\n lost.current = true;\n onLost();\n };\n return (\n <figure part=\"media\" className={part(\"sc-surface__media\")} data-kind={kind}>\n {kind === \"image\" ? (\n onOpen ? (\n <button type=\"button\" onClick={onOpen} aria-label={name}>\n <img src={src} alt={name} loading=\"lazy\" onError={once} />\n </button>\n ) : (\n <img src={src} alt={name} loading=\"lazy\" onError={once} />\n )\n ) : kind === \"video\" ? (\n <video src={src} controls playsInline preload=\"metadata\" aria-label={name} onError={once} />\n ) : (\n <AudioPlayer src={src} label={name} onError={once} />\n )}\n {caption !== undefined && <figcaption>{caption}</figcaption>}\n </figure>\n );\n}\n\n/** Labels and their values, a row each. */\nfunction Fields({ block }: { block: FieldsBlock }) {\n const part = useSurfaceParts();\n return (\n <dl part=\"fields\" className={part(\"sc-surface__fields\")}>\n {block.items.map((item, index) => (\n <div key={index}>\n <dt>{item.label}</dt>\n <dd>{item.value}</dd>\n </div>\n ))}\n </dl>\n );\n}\n\n/** Rows under named columns, scrolling across inside their own box when they are wide. */\nfunction Table({ block }: { block: TableBlock }) {\n const part = useSurfaceParts();\n return (\n <div part=\"table\" className={part(\"sc-surface__table\")}>\n <table>\n <thead>\n <tr>\n {block.columns.map((column, index) => (\n <th key={index} scope=\"col\">\n {column}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {block.rows.map((row, index) => (\n <tr key={index}>\n {block.columns.map((_, at) => (\n <td key={at}>{row[at] ?? \"\"}</td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\n/** A list block as the kit's `List`: a `Row` each; with an action, rows to pick, the picked checked. */\nfunction Listing({ block }: { block: ListBlock }) {\n const { active, answered, onAction } = useScope();\n const { action } = block;\n const pick = action !== undefined && answered?.name === action ? answered.data.item : undefined;\n return (\n <List>\n {block.items.map((entry) => (\n <Row\n key={entry.id}\n mark={\n entry.image !== undefined && https(entry.image) ? (\n <Mark src={entry.image} photo size={40} />\n ) : undefined\n }\n title={entry.title}\n subtitle={entry.subtitle}\n meta={entry.meta}\n pressed={entry.id === pick}\n disabled={!active}\n onPress={\n action === undefined ? undefined : () => onAction(chosen({ ...block, action }, entry))\n }\n />\n ))}\n </List>\n );\n}\n\n/** A picture from an https address, never sending the page's address along, with its caption. */\nfunction Picture({ block }: { block: ImageBlock }) {\n const part = useSurfaceParts();\n if (!https(block.url)) return null;\n return (\n <figure part=\"image\" className={part(\"sc-surface__image\")}>\n <img src={block.url} alt={block.alt} loading=\"lazy\" referrerPolicy=\"no-referrer\" />\n {block.caption !== undefined && <figcaption>{block.caption}</figcaption>}\n </figure>\n );\n}\n\n/**\n * An automation, read only: the agent's page is where it is switched and its runs opened. One\n * that is not there (removed, renamed since) says so quietly.\n */\nfunction AutomationShown({ block }: { block: AutomationBlock }) {\n const part = useParts();\n const { messages } = useKit();\n // `null`: it is gone; missing: not filled in by the API, which a newer server always does.\n if (block.view == null) {\n return (\n <p className={part(\"sc-automation\", \"sc-automation--gone\")}>{messages.automation.gone}</p>\n );\n }\n return <Automation automation={block.view} runs={block.view.runs} />;\n}\n\n/**\n * An app for the person to connect, as its connection stands: its logo and name over the line\n * the agent wrote of what it will do with it; while it waits for them, the button that opens\n * its consent page apart; once it works, or if it failed, a quiet word in its place. Before\n * the API has asked for it, only the app as the agent named it.\n */\nfunction Connect({ block }: { block: ConnectBlock }) {\n const part = useSurfaceParts();\n const { messages } = useKit();\n const { connection } = block;\n const title = connection?.title ?? block.app;\n const pending = connection?.status === \"pending\" ? connection.url : null;\n const url = pending !== null && https(pending) ? pending : null;\n const working = connection?.status === \"active\";\n const failed = connection !== null && !working && connection.status !== \"pending\";\n return (\n <Card title={title} subtitle={block.purpose} image={connection?.icon ?? undefined}>\n {url !== null ? (\n <Button tone=\"primary\" href={url}>\n {messages.connect.authorize(title)}\n </Button>\n ) : (\n <p\n className={part(\n \"sc-surface__connect\",\n ...(working ? ([\"sc-surface__connect--done\"] as const) : []),\n )}\n >\n {working && <CheckIcon aria-hidden weight=\"bold\" />}\n {connectWords(messages.connect, working, failed)}\n </p>\n )}\n </Card>\n );\n}\n\n/** The quiet word an app's card says where its button isn't: working, failed, or on its way. */\nfunction connectWords(words: KitMessages[\"connect\"], working: boolean, failed: boolean): string {\n if (working) return words.connected;\n return failed ? words.failed : words.preparing;\n}\n\n/**\n * A button that sends its action, marked once it was the one pressed; or a link that opens its\n * page apart (it stays usable on an older turn: it changes nothing here).\n */\nfunction Pressable({ block }: { block: ButtonBlock }) {\n const { active, answered, onAction } = useScope();\n const { action, url, tone, label } = block;\n if (url !== undefined) {\n return https(url) ? (\n <Button tone={tone} href={url}>\n {label}\n </Button>\n ) : null;\n }\n if (action === undefined) return null;\n const done = answered?.name === action && answered.label === label;\n return (\n <Button\n tone={tone}\n icon={done ? CheckIcon : undefined}\n pressed={done}\n disabled={!active}\n onClick={() => onAction(pressed({ ...block, action }))}\n >\n {label}\n </Button>\n );\n}\n\n/** A few options to pick one of at once; the one picked keeps a ring and a check. */\nfunction Choice({ block }: { block: ChoiceBlock }) {\n const { active, answered, onAction } = useScope();\n const part = useSurfaceParts();\n const mine = answered?.name === block.action ? answered.data : undefined;\n const chosenId = mine?.option;\n const listed =\n block.other === true ||\n block.options.length > SHORT_CHOICE ||\n block.options.some((option) => option.hint !== undefined || option.label.length > SHORT_LABEL);\n return (\n <div\n role=\"group\"\n part=\"choice\"\n className={part(\n \"sc-surface__choice\",\n ...(listed ? ([\"sc-surface__choice--list\"] as const) : []),\n )}\n >\n {block.options.map((option) => (\n <button\n key={option.id}\n type=\"button\"\n disabled={!active}\n aria-pressed={option.id === chosenId || undefined}\n onClick={() => onAction(picked(block, option))}\n part=\"option\"\n className={part(\"sc-surface__option\")}\n >\n <span>\n {option.label}\n {option.hint !== undefined && <small>{option.hint}</small>}\n </span>\n {option.id === chosenId && <CheckIcon aria-hidden weight=\"bold\" />}\n </button>\n ))}\n {block.other === true && (\n <Other\n active={active}\n said={typeof mine?.text === \"string\" ? mine.text : undefined}\n onSay={(text) => onAction(answeredIn(block, text))}\n />\n )}\n </div>\n );\n}\n\n/** Pills fit options this few and this short; more, longer or with a hint, they go one a row. */\nconst SHORT_CHOICE = 4;\nconst SHORT_LABEL = 28;\n\n/**\n * A choice's last row, to answer in one's own words: pressed, it turns into a box with its send\n * (Enter sends, Escape folds it back). Once said, the words stand in it, chosen.\n */\nfunction Other({\n active,\n said,\n onSay,\n}: {\n active: boolean;\n said: string | undefined;\n onSay: (text: string) => void;\n}) {\n const part = useSurfaceParts();\n const { messages } = useKit();\n const [writing, setWriting] = useState(false);\n const [text, setText] = useState(\"\");\n if (said !== undefined || !writing) {\n return (\n <button\n type=\"button\"\n disabled={!active}\n aria-pressed={said !== undefined || undefined}\n onClick={() => setWriting(true)}\n part=\"option other\"\n className={part(\"sc-surface__option\", \"sc-surface__option--other\")}\n >\n {said === undefined && <PencilSimpleIcon aria-hidden />}\n <span>{said ?? messages.other}</span>\n {said !== undefined && <CheckIcon aria-hidden weight=\"bold\" />}\n </button>\n );\n }\n const send = () => {\n const words = text.trim();\n if (words !== \"\") onSay(words);\n };\n return (\n <form\n part=\"other\"\n className={part(\"sc-surface__other\")}\n onSubmit={(event) => {\n event.preventDefault();\n send();\n }}\n >\n <input\n // A person who pressed to write wants to write.\n autoFocus\n value={text}\n aria-label={messages.other}\n placeholder={messages.otherPlaceholder}\n onChange={(event) => setText(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n setWriting(false);\n }\n }}\n />\n <button type=\"submit\" aria-label={messages.send} disabled={text.trim() === \"\"}>\n <ArrowUpIcon aria-hidden weight=\"bold\" />\n </button>\n </form>\n );\n}\n\n/** Only an https address is loaded or opened: never a script, never a local file. */\nfunction https(url: string): boolean {\n return /^https:\\/\\//i.test(url);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAgEA,MAAM,yBAA8B,IAAI,IAAI;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;AAGD,SAAgB,MAAM,QAA0B;CAC9C,OAAO,OAAO,OAAO,UAAU;EAC7B,IAAI,CAAC,OAAO,IAAI,MAAM,IAAI,GAAG,OAAO;EACpC,IAAI,MAAM,SAAS,QAAQ,OAAO,MAAM,OAAO,OAAO,UAAU,YAAY,IAAI,MAAM,IAAI,CAAC;EAC3F,OAAO,cAAc,QAAQ,MAAM,MAAM,QAAQ,IAAI;CACvD,CAAC;AACH;;;;;AAMA,SAAgB,aAAa,SAAyB;CACpD,MAAM,CAAC,MAAM,GAAG,QAAQ,QAAQ;CAChC,OACE,KAAK,WAAW,MACf,MAAM,SAAS,UAAU,MAAM,SAAS,gBAAgB,MAAM,SAAS;AAE5E;;;;;;;;AASA,SAAgB,QAAQ,EACtB,SACA,UACA,SAAS,MACT,UACA,YACA,YACA,UACA,QACA,SACe;CAEf,OACE,oBAAC,aAAD;EAAyB;EAAsB;EAAkB;EAC/D,UAAA,oBAAC,cAAD;GAAc,OAAO;IAHF;IAAQ;IAAU;IAAU;IAAY;IAAY;GAG9C;GACvB,UAAA,oBAAC,OAAD;IAAK,WAAW,MAAM,QAAQ,YAAY,YAAY;IACpD,UAAA,oBAAC,OAAD;KAAO,MAAM,oBAAC,KAAD;MAAG,WAAW,QAAQ,YAAY,kBAAkB;MAAI,UAAA,QAAQ;KAAQ,CAAA;KAClF,UAAA,MAAM,QAAQ,MAAM,IACjB,QAAQ,OAAO,KAAK,OAAO,UAAU,oBAAC,MAAD,EAAyB,MAAQ,GAAtB,KAAsB,CAAC,IACvE;IACC,CAAA;GACJ,CAAA;EACO,CAAA;CACH,CAAA;AAEjB;;;;;AAMA,IAAM,QAAN,cAAoB,UAAyE;CAC3F,QAAiB,EAAE,QAAQ,MAAM;CAEjC,OAAO,2BAAgD;EACrD,OAAO,EAAE,QAAQ,KAAK;CACxB;CAEA,SAAkB;EAChB,OAAO,KAAK,MAAM,UAAU,KAAK,MAAM,aAAa,OAChD,KAAK,MAAM,OACX,KAAK,MAAM;CACjB;AACF;;AAGA,SAAS,KAAK,EAAE,SAA2B;CACzC,MAAM,EAAE,QAAQ,UAAU,eAAe,SAAS;CAClD,MAAM,OAAO,gBAAgB;CAC7B,MAAM,WACJ,cAAc,QACV,MAAM,SAAS,KAAK,OAAO,UAAU,oBAAC,MAAD,EAAkB,OAAO,MAAQ,GAAtB,KAAsB,CAAC,IACvE,KAAA;CACN,MAAM,MAAM,aAAa,MAAM;CAC/B,IAAI,KAIF,OAAO,oBAACA,KAAD;EAAM,GAAI;EAAe;EAAkB;EAAoB;CAAW,CAAA;CAEnF,QAAQ,MAAM,MAAd;EACE,KAAK,QACH,OACE,oBAAC,MAAD;GAAM,OAAO,MAAM;GAAO,UAAU,MAAM;GAAU,OAAO,MAAM;GAC9D;EACG,CAAA;EAEV,KAAK,SACH,OACE,oBAAC,OAAD;GACE,MAAK;GACL,WAAW,KACT,qBACA,GAAI,MAAM,cAAc,QAAS,CAAC,wBAAwB,IAAc,CAAC,CAC3E;GAEC;EACE,CAAA;EAET,KAAK,WACH,OAAO,oBAAC,MAAD;GAAI,MAAK;GAAU,WAAW,KAAK,qBAAqB;EAAI,CAAA;EACrE,KAAK,QACH,OACE,oBAAC,OAAD;GAAK,MAAK;GAAO,WAAW,KAAK,kBAAkB;GACjD,UAAA,oBAAC,UAAD,EAAU,MAAM,MAAM,KAAO,CAAA;EAC1B,CAAA;EAET,KAAK,UACH,OAAO,oBAAC,QAAD,EAAe,MAAQ,CAAA;EAChC,KAAK,SACH,OAAO,oBAAC,OAAD,EAAc,MAAQ,CAAA;EAC/B,KAAK,QACH,OAAO,oBAAC,SAAD,EAAgB,MAAQ,CAAA;EACjC,KAAK,QACH,OAAO,oBAAC,MAAD;GAAM,OAAO,MAAM;GAAO,OAAO,MAAM;GAAO,MAAM,MAAM;GAAM,MAAM,MAAM;EAAO,CAAA;EAC5F,KAAK,SACH,OAAO,oBAAC,OAAD;GAAO,MAAM,MAAM;GAAM,QAAQ,MAAM;EAAS,CAAA;EACzD,KAAK,QACH,OACE,oBAAC,MAAD;GAAM,MAAM,MAAM;GAChB,UAAA,oBAAC,UAAD,EAAU,MAAM,MAAM,KAAO,CAAA;EACzB,CAAA;EAEV,KAAK,SACH,OAAO,oBAAC,SAAD,EAAgB,MAAQ,CAAA;EACjC,KAAK,cACH,OAAO,oBAAC,iBAAD,EAAwB,MAAQ,CAAA;EACzC,KAAK,QACH,OAAO,oBAAC,WAAD,EAAkB,MAAQ,CAAA;EACnC,KAAK,WACH,OAAO,oBAAC,SAAD,EAAgB,MAAQ,CAAA;EACjC,KAAK,UACH,OAAO,oBAAC,WAAD,EAAkB,MAAQ,CAAA;EACnC,KAAK,UACH,OAAO,oBAAC,QAAD,EAAe,MAAQ,CAAA;EAChC,KAAK,QACH,OAAO,oBAAC,UAAD,EAAiB,MAAQ,CAAA;CACpC;AACF;;;;;;;;AASA,SAAS,UAAU,EAAE,SAA+B;CAClD,MAAM,EAAE,UAAU,SAAS;CAC3B,MAAM,EAAE,SAAS,OAAO;CACxB,MAAM,EAAE,SAAS;CACjB,MAAM,OAAO,MAAM,QAAQ,SAAS,MAAM,IAAI;CAC9C,MAAM,QAAQ,MAAM,SAAS;CAC7B,MAAM,OAAO,SAAS,OAAO,OAAO,OAAO,KAAA;CAC3C,MAAM,SAAS,SAAS,OAAO,SAAS,KAAK,UAAU,IAAI;CAC3D,MAAM,MAAM,UAAU,MAAM,WAAW,OAAO,OAAO,OAAO,KAAA,CAAS;CACrE,IAAI,SAAS,QAAQ,WAAW,QAAQ,QAAQ,MAC9C,OACE,oBAAC,QAAD;EACE,MAAM;EACN,KAAK,IAAI;EACH;EACN,SAAS,MAAM;EACf,QAAQ,aAAa,KAAK,IAAI,IAAI,KAAA;EAClC,QAAQ,IAAI;CACb,CAAA;CAGL,OACE,oBAAC,MAAD,EAAA,UACE,oBAAC,KAAD;EACE,MAAM,oBAAC,UAAD;GAAgB;GAAM,MAAM,MAAM;EAAa,CAAA;EAC9C;EACP,UAAU,UAAU,OAAO,KAAA,IAAY;EACvC,MAAM,SAAS,OAAO,KAAA,IAAY,WAAW,KAAK,MAAM,IAAI;EAC5D,OAAO,SAAS;EAChB,SAAS,QAAQ,SAAS,aAAa,KAAK,IAAI,IAAI,KAAA;CACrD,CAAA,EACG,CAAA;AAEV;;AAKA,SAAS,SAAS,WAAsC;CACtD,MAAM,SAAS,UAAU,MAAM,GAAG,CAAC,CAAC;CACpC,OAAO,WAAW,WAAW,WAAW,WAAW,WAAW,UAAU,SAAS;AACnF;;;;;AAMA,SAAgB,UACd,MACA,MAC2C;CAC3C,MAAM,CAAC,KAAK,UAAU,SAAwB,IAAI;CAClD,MAAM,CAAC,OAAO,YAAY,SAAS,CAAC;CAGpC,MAAM,SAAS,OAAO;EAAE;EAAM;CAAK,CAAC;CACpC,OAAO,UAAU;EAAE;EAAM;CAAK;CAC9B,MAAM,KAAK,MAAM;CAEjB,gBAAgB;EACd,MAAM,EAAE,MAAM,KAAK,MAAM,WAAW,OAAO;EAC3C,IAAI,QAAQ,QAAQ,WAAW,KAAA,GAAW;EAC1C,IAAI,UAAU;EACd,OAAO,GAAG,CAAC,CAAC,MACT,YAAY,WAAW,OAAO,OAAO,SAChC,WAAW,OAAO,IAAI,CAC9B;EACA,aAAa;GACX,UAAU;EACZ;CACF,GAAG;EAAC;EAZa,SAAS,KAAA;EAYR;CAAK,CAAC;CACxB,OAAO,QAAQ,OAAO,OAAO;EAAE;EAAK,aAAa,UAAU,UAAU,QAAQ,CAAC;CAAE;AAClF;;;;;;AAOA,SAAS,OAAO,EACd,MACA,KACA,MACA,SACA,QACA,UAQC;CACD,MAAM,OAAO,gBAAgB;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,aAAa;EACjB,IAAI,KAAK,SAAS;EAClB,KAAK,UAAU;EACf,OAAO;CACT;CACA,OACE,qBAAC,UAAD;EAAQ,MAAK;EAAQ,WAAW,KAAK,mBAAmB;EAAG,aAAW;EAAtE,UAAA,CACG,SAAS,UACR,SACE,oBAAC,UAAD;GAAQ,MAAK;GAAS,SAAS;GAAQ,cAAY;GACjD,UAAA,oBAAC,OAAD;IAAU;IAAK,KAAK;IAAM,SAAQ;IAAO,SAAS;GAAO,CAAA;EACnD,CAAA,IAER,oBAAC,OAAD;GAAU;GAAK,KAAK;GAAM,SAAQ;GAAO,SAAS;EAAO,CAAA,IAEzD,SAAS,UACX,oBAAC,SAAD;GAAY;GAAK,UAAA;GAAS,aAAA;GAAY,SAAQ;GAAW,cAAY;GAAM,SAAS;EAAO,CAAA,IAE3F,oBAAC,aAAD;GAAkB;GAAK,OAAO;GAAM,SAAS;EAAO,CAAA,GAErD,YAAY,KAAA,KAAa,oBAAC,cAAD,EAAA,UAAa,QAAoB,CAAA,CACrD;;AAEZ;;AAGA,SAAS,OAAO,EAAE,SAAiC;CACjD,MAAM,OAAO,gBAAgB;CAC7B,OACE,oBAAC,MAAD;EAAI,MAAK;EAAS,WAAW,KAAK,oBAAoB;EACnD,UAAA,MAAM,MAAM,KAAK,MAAM,UACtB,qBAAC,OAAD,EAAA,UAAA,CACE,oBAAC,MAAD,EAAA,UAAK,KAAK,MAAU,CAAA,GACpB,oBAAC,MAAD,EAAA,UAAK,KAAK,MAAU,CAAA,CACjB,EAAA,GAHK,KAGL,CACN;CACC,CAAA;AAER;;AAGA,SAAS,MAAM,EAAE,SAAgC;CAC/C,MAAM,OAAO,gBAAgB;CAC7B,OACE,oBAAC,OAAD;EAAK,MAAK;EAAQ,WAAW,KAAK,mBAAmB;EACnD,UAAA,qBAAC,SAAD,EAAA,UAAA,CACE,oBAAC,SAAD,EAAA,UACE,oBAAC,MAAD,EAAA,UACG,MAAM,QAAQ,KAAK,QAAQ,UAC1B,oBAAC,MAAD;GAAgB,OAAM;GACnB,UAAA;EACC,GAFK,KAEL,CACL,EACC,CAAA,EACC,CAAA,GACP,oBAAC,SAAD,EAAA,UACG,MAAM,KAAK,KAAK,KAAK,UACpB,oBAAC,MAAD,EAAA,UACG,MAAM,QAAQ,KAAK,GAAG,OACrB,oBAAC,MAAD,EAAA,UAAc,IAAI,OAAO,GAAO,GAAvB,EAAuB,CACjC,EACC,GAJK,KAIL,CACL,EACI,CAAA,CACF,EAAA,CAAA;CACJ,CAAA;AAET;;AAGA,SAAS,QAAQ,EAAE,SAA+B;CAChD,MAAM,EAAE,QAAQ,UAAU,aAAa,SAAS;CAChD,MAAM,EAAE,WAAW;CACnB,MAAM,OAAO,WAAW,KAAA,KAAa,UAAU,SAAS,SAAS,SAAS,KAAK,OAAO,KAAA;CACtF,OACE,oBAAC,MAAD,EAAA,UACG,MAAM,MAAM,KAAK,UAChB,oBAAC,KAAD;EAEE,MACE,MAAM,UAAU,KAAA,KAAa,MAAM,MAAM,KAAK,IAC5C,oBAAC,MAAD;GAAM,KAAK,MAAM;GAAO,OAAA;GAAM,MAAM;EAAK,CAAA,IACvC,KAAA;EAEN,OAAO,MAAM;EACb,UAAU,MAAM;EAChB,MAAM,MAAM;EACZ,SAAS,MAAM,OAAO;EACtB,UAAU,CAAC;EACX,SACE,WAAW,KAAA,IAAY,KAAA,UAAkB,SAAS,OAAO;GAAE,GAAG;GAAO;EAAO,GAAG,KAAK,CAAC;CAExF,GAdM,MAAM,EAcZ,CACF,EACG,CAAA;AAEV;;AAGA,SAAS,QAAQ,EAAE,SAAgC;CACjD,MAAM,OAAO,gBAAgB;CAC7B,IAAI,CAAC,MAAM,MAAM,GAAG,GAAG,OAAO;CAC9B,OACE,qBAAC,UAAD;EAAQ,MAAK;EAAQ,WAAW,KAAK,mBAAmB;EAAxD,UAAA,CACE,oBAAC,OAAD;GAAK,KAAK,MAAM;GAAK,KAAK,MAAM;GAAK,SAAQ;GAAO,gBAAe;EAAe,CAAA,GACjF,MAAM,YAAY,KAAA,KAAa,oBAAC,cAAD,EAAA,UAAa,MAAM,QAAoB,CAAA,CACjE;;AAEZ;;;;;AAMA,SAAS,gBAAgB,EAAE,SAAqC;CAC9D,MAAM,OAAO,SAAS;CACtB,MAAM,EAAE,aAAa,OAAO;CAE5B,IAAI,MAAM,QAAQ,MAChB,OACE,oBAAC,KAAD;EAAG,WAAW,KAAK,iBAAiB,qBAAqB;EAAI,UAAA,SAAS,WAAW;CAAQ,CAAA;CAG7F,OAAO,oBAAC,YAAD;EAAY,YAAY,MAAM;EAAM,MAAM,MAAM,KAAK;CAAO,CAAA;AACrE;;;;;;;AAQA,SAAS,QAAQ,EAAE,SAAkC;CACnD,MAAM,OAAO,gBAAgB;CAC7B,MAAM,EAAE,aAAa,OAAO;CAC5B,MAAM,EAAE,eAAe;CACvB,MAAM,QAAQ,YAAY,SAAS,MAAM;CACzC,MAAM,UAAU,YAAY,WAAW,YAAY,WAAW,MAAM;CACpE,MAAM,MAAM,YAAY,QAAQ,MAAM,OAAO,IAAI,UAAU;CAC3D,MAAM,UAAU,YAAY,WAAW;CACvC,MAAM,SAAS,eAAe,QAAQ,CAAC,WAAW,WAAW,WAAW;CACxE,OACE,oBAAC,MAAD;EAAa;EAAO,UAAU,MAAM;EAAS,OAAO,YAAY,QAAQ,KAAA;EACrE,UAAA,QAAQ,OACP,oBAAC,QAAD;GAAQ,MAAK;GAAU,MAAM;GAC1B,UAAA,SAAS,QAAQ,UAAU,KAAK;EAC3B,CAAA,IAER,qBAAC,KAAD;GACE,WAAW,KACT,uBACA,GAAI,UAAW,CAAC,2BAA2B,IAAc,CAAC,CAC5D;GAJF,UAAA,CAMG,WAAW,oBAAC,WAAD;IAAW,eAAA;IAAY,QAAO;GAAQ,CAAA,GACjD,aAAa,SAAS,SAAS,SAAS,MAAM,CAC9C;;CAED,CAAA;AAEV;;AAGA,SAAS,aAAa,OAA+B,SAAkB,QAAyB;CAC9F,IAAI,SAAS,OAAO,MAAM;CAC1B,OAAO,SAAS,MAAM,SAAS,MAAM;AACvC;;;;;AAMA,SAAS,UAAU,EAAE,SAAiC;CACpD,MAAM,EAAE,QAAQ,UAAU,aAAa,SAAS;CAChD,MAAM,EAAE,QAAQ,KAAK,MAAM,UAAU;CACrC,IAAI,QAAQ,KAAA,GACV,OAAO,MAAM,GAAG,IACd,oBAAC,QAAD;EAAc;EAAM,MAAM;EACvB,UAAA;CACK,CAAA,IACN;CAEN,IAAI,WAAW,KAAA,GAAW,OAAO;CACjC,MAAM,OAAO,UAAU,SAAS,UAAU,SAAS,UAAU;CAC7D,OACE,oBAAC,QAAD;EACQ;EACN,MAAM,OAAO,YAAY,KAAA;EACzB,SAAS;EACT,UAAU,CAAC;EACX,eAAe,SAAS,QAAQ;GAAE,GAAG;GAAO;EAAO,CAAC,CAAC;EAEpD,UAAA;CACK,CAAA;AAEZ;;AAGA,SAAS,OAAO,EAAE,SAAiC;CACjD,MAAM,EAAE,QAAQ,UAAU,aAAa,SAAS;CAChD,MAAM,OAAO,gBAAgB;CAC7B,MAAM,OAAO,UAAU,SAAS,MAAM,SAAS,SAAS,OAAO,KAAA;CAC/D,MAAM,WAAW,MAAM;CACvB,MAAM,SACJ,MAAM,UAAU,QAChB,MAAM,QAAQ,SAAS,gBACvB,MAAM,QAAQ,MAAM,WAAW,OAAO,SAAS,KAAA,KAAa,OAAO,MAAM,SAAS,WAAW;CAC/F,OACE,qBAAC,OAAD;EACE,MAAK;EACL,MAAK;EACL,WAAW,KACT,sBACA,GAAI,SAAU,CAAC,0BAA0B,IAAc,CAAC,CAC1D;EANF,UAAA,CAQG,MAAM,QAAQ,KAAK,WAClB,qBAAC,UAAD;GAEE,MAAK;GACL,UAAU,CAAC;GACX,gBAAc,OAAO,OAAO,YAAY,KAAA;GACxC,eAAe,SAAS,OAAO,OAAO,MAAM,CAAC;GAC7C,MAAK;GACL,WAAW,KAAK,oBAAoB;GAPtC,UAAA,CASE,qBAAC,QAAD,EAAA,UAAA,CACG,OAAO,OACP,OAAO,SAAS,KAAA,KAAa,oBAAC,SAAD,EAAA,UAAQ,OAAO,KAAY,CAAA,CACrD,EAAA,CAAA,GACL,OAAO,OAAO,YAAY,oBAAC,WAAD;IAAW,eAAA;IAAY,QAAO;GAAQ,CAAA,CAC3D;EAbD,GAAA,OAAO,EAaN,CACT,GACA,MAAM,UAAU,QACf,oBAAC,OAAD;GACU;GACR,MAAM,OAAO,MAAM,SAAS,WAAW,KAAK,OAAO,KAAA;GACnD,QAAQ,SAAS,SAAS,WAAW,OAAO,IAAI,CAAC;EAClD,CAAA,CAEA;;AAET;;AAGA,MAAM,eAAe;AACrB,MAAM,cAAc;;;;;AAMpB,SAAS,MAAM,EACb,QACA,MACA,SAKC;CACD,MAAM,OAAO,gBAAgB;CAC7B,MAAM,EAAE,aAAa,OAAO;CAC5B,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,CAAC,MAAM,WAAW,SAAS,EAAE;CACnC,IAAI,SAAS,KAAA,KAAa,CAAC,SACzB,OACE,qBAAC,UAAD;EACE,MAAK;EACL,UAAU,CAAC;EACX,gBAAc,SAAS,KAAA,KAAa,KAAA;EACpC,eAAe,WAAW,IAAI;EAC9B,MAAK;EACL,WAAW,KAAK,sBAAsB,2BAA2B;EANnE,UAAA;GAQG,SAAS,KAAA,KAAa,oBAAC,kBAAD,EAAkB,eAAA,KAAa,CAAA;GACtD,oBAAC,QAAD,EAAA,UAAO,QAAQ,SAAS,MAAY,CAAA;GACnC,SAAS,KAAA,KAAa,oBAAC,WAAD;IAAW,eAAA;IAAY,QAAO;GAAQ,CAAA;EACvD;;CAGZ,MAAM,aAAa;EACjB,MAAM,QAAQ,KAAK,KAAK;EACxB,IAAI,UAAU,IAAI,MAAM,KAAK;CAC/B;CACA,OACE,qBAAC,QAAD;EACE,MAAK;EACL,WAAW,KAAK,mBAAmB;EACnC,WAAW,UAAU;GACnB,MAAM,eAAe;GACrB,KAAK;EACP;EANF,UAAA,CAQE,oBAAC,SAAD;GAEE,WAAA;GACA,OAAO;GACP,cAAY,SAAS;GACrB,aAAa,SAAS;GACtB,WAAW,UAAU,QAAQ,MAAM,OAAO,KAAK;GAC/C,YAAY,UAAU;IACpB,IAAI,MAAM,QAAQ,UAAU;KAC1B,MAAM,gBAAgB;KACtB,WAAW,KAAK;IAClB;GACF;EACD,CAAA,GACD,oBAAC,UAAD;GAAQ,MAAK;GAAS,cAAY,SAAS;GAAM,UAAU,KAAK,KAAK,MAAM;GACzE,UAAA,oBAAC,aAAD;IAAa,eAAA;IAAY,QAAO;GAAQ,CAAA;EAClC,CAAA,CACJ;;AAEV;;AAGA,SAAS,MAAM,KAAsB;CACnC,OAAO,eAAe,KAAK,GAAG;AAChC"}
|
|
1
|
+
{"version":3,"file":"Surface.js","names":["Host"],"sources":["../../src/surface/Surface.tsx"],"sourcesContent":["import { ArrowUpIcon, CheckIcon, PencilSimpleIcon } from \"@phosphor-icons/react\";\nimport { Component, useEffect, useRef, useState, type ComponentType, type ReactNode } from \"react\";\n\nimport { Automation } from \"../automation/Automation\";\nimport type {\n AutomationBlock,\n Block,\n BoardBlock,\n ButtonBlock,\n CalendarBlock,\n ChoiceBlock,\n ConnectBlock,\n FieldsBlock,\n HostBlock,\n ImageBlock,\n ListBlock,\n Surface as Shown,\n TableBlock,\n} from \"../blocks\";\nimport { classes } from \"../classes\";\nimport { Button } from \"../kit/Button\";\nimport { formatSize } from \"../about\";\nimport { baseName } from \"../files\";\nimport { Agenda } from \"../kit/Agenda\";\nimport { Kanban } from \"../kit/Kanban\";\nimport { Card } from \"../kit/Card\";\nimport { Chart } from \"../kit/Chart\";\nimport { KitProvider, useKit, useParts } from \"../kit/context\";\nimport { FileMark } from \"../kit/FileMark\";\nimport { List, Row } from \"../kit/List\";\nimport { Mark } from \"../kit/Mark\";\nimport type { KitLocale, KitMessages } from \"../kit/messages\";\nimport type { FileHost } from \"../kit/uploads\";\nimport { AudioPlayer } from \"../kit/AudioPlayer\";\nimport { Note } from \"../kit/Note\";\nimport { Stat } from \"../kit/Stat\";\nimport { Timeline } from \"../kit/Timeline\";\nimport type { Attachment, FileBlock } from \"../types\";\nimport { FormView } from \"./Form\";\nimport { Markdown } from \"./Markdown\";\nimport {\n ScopeContext,\n useScope,\n useSurfaceParts,\n type HostBlockProps,\n type HostBlocks,\n type Scope,\n type SurfaceClassNames,\n type SurfaceComponents,\n} from \"./scope\";\nimport { answeredIn, chosen, FIELD_KINDS, picked, pressed, type SurfaceAction } from \"./values\";\n\nexport interface SurfaceProps {\n surface: Shown;\n onAction: (action: SurfaceAction) => void;\n /** False draws the inputs disabled: a surface already answered, or one from an older turn. */\n active?: boolean | undefined;\n /** What the person already did on it: the option picked, the button pressed, the form sent. */\n answered?: SurfaceAction | undefined;\n components?: SurfaceComponents | undefined;\n /** The host's own blocks, by the name each was declared with. */\n blocks?: HostBlocks | undefined;\n classNames?: SurfaceClassNames;\n /** The words its controls say, in the host's language; English by default. */\n messages?: Partial<KitMessages> | undefined;\n /** The language of its calendar, days, times and numbers; English by default. */\n locale?: KitLocale | undefined;\n /**\n * Where its file fields' files go (uploaded as they are picked) and how the ones sent are found\n * and opened: the conversation's. None: a file field takes no files.\n */\n files?: FileHost | undefined;\n}\n\nconst BLOCKS: ReadonlySet<string> = new Set([\n \"card\",\n \"stack\",\n \"divider\",\n \"text\",\n \"fields\",\n \"table\",\n \"list\",\n \"stat\",\n \"chart\",\n \"note\",\n \"image\",\n \"automation\",\n \"file\",\n \"connect\",\n \"button\",\n \"choice\",\n \"form\",\n \"calendar\",\n \"timeline\",\n \"board\",\n \"host\",\n]);\n\n/** Whether every block down the tree, and every field of its forms, is one this renderer draws. */\nexport function known(blocks: Block[]): boolean {\n return blocks.every((block) => {\n if (!BLOCKS.has(block.type)) return false;\n if (block.type === \"form\") return block.fields.every((field) => FIELD_KINDS.has(field.kind));\n return \"children\" in block ? known(block.children) : true;\n });\n}\n\n/**\n * Whether a screen draws its own edge: one that is a single card (or an automation, or an app\n * to connect, each a card of its own) needs no panel around it where it is shown.\n */\nexport function framesItself(surface: Shown): boolean {\n const [only, ...more] = surface.blocks;\n return (\n more.length === 0 &&\n (only?.type === \"card\" || only?.type === \"automation\" || only?.type === \"connect\")\n );\n}\n\n/**\n * What the agent showed, drawn block by block with the kit's primitives (a `card` is a `Card`, a\n * `list` a `List` of `Row`s, a form's fields its controls); a block or a field kind the API has\n * grown since this renderer was written leaves the whole surface to its text twin, so nothing\n * is half-drawn. Each block's action comes back through `onAction`; the host wraps it with the\n * surface's id.\n */\nexport function Surface({\n surface,\n onAction,\n active = true,\n answered,\n components,\n blocks,\n classNames,\n messages,\n locale,\n files,\n}: SurfaceProps) {\n const scope: Scope = { active, answered, onAction, components, blocks, classNames, files };\n return (\n <KitProvider classNames={classNames} messages={messages} locale={locale}>\n <ScopeContext value={scope}>\n <div className={`sc ${classes(classNames, \"sc-surface\")}`}>\n <Whole twin={<p className={classes(classNames, \"sc-surface__text\")}>{surface.text}</p>}>\n {known(surface.blocks)\n ? surface.blocks.map((block, index) => <Draw key={index} block={block} />)\n : null}\n </Whole>\n </div>\n </ScopeContext>\n </KitProvider>\n );\n}\n\n/**\n * The surface drawn whole or not at all: what it can't draw (blocks it doesn't know, or one that\n * fails as it draws) leaves its text twin in its place, never a broken page.\n */\nclass Whole extends Component<{ twin: ReactNode; children: ReactNode }, { failed: boolean }> {\n override state = { failed: false };\n\n static getDerivedStateFromError(): { failed: boolean } {\n return { failed: true };\n }\n\n override render() {\n return this.state.failed || this.props.children === null\n ? this.props.twin\n : this.props.children;\n }\n}\n\n/** One block: the host's own component for its type when it gave one, else the kit's. */\nfunction Draw({ block }: { block: Block }) {\n const { active, onAction, components } = useScope();\n const part = useSurfaceParts();\n const children =\n \"children\" in block\n ? block.children.map((child, index) => <Draw key={index} block={child} />)\n : undefined;\n const Own = components?.[block.type];\n if (Own) {\n // Found by the block's own type, so its fields are the right props; the union is too wide\n // for the compiler to see that pairing.\n const Host = Own as ComponentType<Record<string, unknown>>;\n return <Host {...block} active={active} onAction={onAction} children={children} />;\n }\n switch (block.type) {\n case \"card\":\n return (\n <Card title={block.title} subtitle={block.subtitle} image={block.image}>\n {children}\n </Card>\n );\n case \"stack\":\n return (\n <div\n part=\"stack\"\n className={part(\n \"sc-surface__stack\",\n ...(block.direction === \"row\" ? ([\"sc-surface__stack--row\"] as const) : []),\n )}\n >\n {children}\n </div>\n );\n case \"divider\":\n return <hr part=\"divider\" className={part(\"sc-surface__divider\")} />;\n case \"text\":\n return (\n <div part=\"text\" className={part(\"sc-surface__text\")}>\n <Markdown text={block.text} />\n </div>\n );\n case \"fields\":\n return <Fields block={block} />;\n case \"table\":\n return <Table block={block} />;\n case \"list\":\n return <Listing block={block} />;\n case \"stat\":\n return <Stat label={block.label} value={block.value} hint={block.hint} tone={block.tone} />;\n case \"chart\":\n return <Chart kind={block.kind} points={block.points} />;\n case \"note\":\n return (\n <Note tone={block.tone}>\n <Markdown text={block.text} />\n </Note>\n );\n case \"image\":\n return <Picture block={block} />;\n case \"automation\":\n return <AutomationShown block={block} />;\n case \"file\":\n return <FileShown block={block} />;\n case \"connect\":\n return <Connect block={block} />;\n case \"button\":\n return <Pressable block={block} />;\n case \"choice\":\n return <Choice block={block} />;\n case \"form\":\n return <FormView block={block} />;\n case \"calendar\":\n return <CalendarShown block={block} />;\n case \"timeline\":\n return <Timeline items={block.items} />;\n case \"board\":\n return <BoardShown block={block} />;\n case \"host\":\n return <Hosted block={block} />;\n }\n}\n\n/** A calendar; with its action, pressing an event sends it (`{event}`). */\nfunction CalendarShown({ block }: { block: CalendarBlock }) {\n const { active, onAction } = useScope();\n const { action } = block;\n return (\n <Agenda\n span={block.span}\n start={block.start}\n events={block.events}\n disabled={!active}\n onEvent={\n action === undefined\n ? undefined\n : (event) => {\n onAction({ name: action, data: { event: event.id }, label: event.title });\n }\n }\n />\n );\n}\n\n/** A board; with its action, a card moved to another column sends it (`{card, column}`). */\nfunction BoardShown({ block }: { block: BoardBlock }) {\n const { active, onAction } = useScope();\n const { messages } = useKit();\n const { action } = block;\n return (\n <Kanban\n columns={block.columns}\n disabled={!active}\n onMove={\n action === undefined\n ? undefined\n : (card, column) => {\n onAction({\n name: action,\n data: { card: card.id, column: column.id },\n label: messages.board.moved(card.title, column.title),\n });\n }\n }\n />\n );\n}\n\n/**\n * One of the host's own blocks, drawn by the component it registered under the block's name; a\n * name it gave none for, or a component that fails as it draws, leaves the block's words in a\n * quiet card in its place, and the rest of the surface draws.\n */\nfunction Hosted({ block }: { block: HostBlock }) {\n const { blocks, active, onAction } = useScope();\n const part = useSurfaceParts();\n const twin = (\n <div part=\"host\" className={part(\"sc-surface__host\")} data-block={block.name}>\n <Markdown text={block.text} />\n </div>\n );\n // Registered under this name, for these props: the API checked them against its schema.\n const Own = blocks?.[block.name] as ComponentType<HostBlockProps> | undefined;\n if (Own === undefined) return twin;\n return (\n <Whole twin={twin}>\n <Own props={block.props} text={block.text} active={active} onAction={onAction} />\n </Whole>\n );\n}\n\n/**\n * A file the agent put in front of the person, compact: its kind's mark, its title (its name\n * under it when the title is another), its size at the right; pressed, it opens whole in the\n * host's viewer (`files.open`), whatever its kind. A picture, a video or a sound plays where it\n * is instead, when the host can play it (`files.play`). Until the API has put it in the\n * conversation it is only named, quietly.\n */\nfunction FileShown({ block }: { block: FileBlock }) {\n const { files } = useScope();\n const { code } = useKit();\n const { file } = block;\n const name = file?.name ?? baseName(block.path);\n const title = block.title ?? name;\n const open = file !== null ? files?.open : undefined;\n const played = file !== null ? playedAs(file.media_type) : null;\n const src = usePlayed(file, played !== null ? files?.play : undefined);\n if (file !== null && played !== null && src !== null) {\n return (\n <Played\n kind={played}\n src={src.url}\n name={name}\n caption={block.title}\n onOpen={open ? () => open(file) : undefined}\n onLost={src.again}\n />\n );\n }\n return (\n <List>\n <Row\n mark={<FileMark name={name} type={file?.media_type} />}\n title={title}\n subtitle={title === name ? undefined : name}\n meta={file === null ? undefined : formatSize(file.size, code)}\n quiet={file === null}\n onPress={open && file !== null ? () => open(file) : undefined}\n />\n </List>\n );\n}\n\ntype PlayedKind = \"image\" | \"video\" | \"audio\";\n\n/** How a file plays in the page: a picture, a video, a sound; null for any other kind. */\nfunction playedAs(mediaType: string): PlayedKind | null {\n const family = mediaType.split(\"/\")[0];\n return family === \"image\" || family === \"video\" || family === \"audio\" ? family : null;\n}\n\n/**\n * Where `file` plays from, asked of the host once (`play`): null until it answers, and when it\n * can't. `again` asks for a new address, for a player whose address ran out.\n */\nexport function usePlayed(\n file: Attachment | null,\n play: ((file: Attachment) => Promise<string>) | undefined,\n): { url: string; again: () => void } | null {\n const [url, setUrl] = useState<string | null>(null);\n const [asked, setAsked] = useState(0);\n // The file and the host's way to play it as they are now: a block drawn again carries the\n // same file anew, and the host its functions; what is asked for changes with the file's id.\n const latest = useRef({ file, play });\n latest.current = { file, play };\n const id = file?.id;\n const playable = play !== undefined;\n useEffect(() => {\n const { file: now, play: player } = latest.current;\n if (now === null || player === undefined) return;\n let current = true;\n player(now).then(\n (address) => current && setUrl(address),\n () => current && setUrl(null),\n );\n return () => {\n current = false;\n };\n }, [id, playable, asked]);\n return url === null ? null : { url, again: () => setAsked((times) => times + 1) };\n}\n\n/**\n * A picture, a video or a sound the agent put in front of the person, where it is: the\n * browser's own player, streaming from the address it plays from; a picture pressed opens whole\n * in the host's viewer. A player whose address ran out asks for a new one, once.\n */\nfunction Played({\n kind,\n src,\n name,\n caption,\n onOpen,\n onLost,\n}: {\n kind: PlayedKind;\n src: string;\n name: string;\n caption: string | undefined;\n onOpen: (() => void) | undefined;\n onLost: () => void;\n}) {\n const part = useSurfaceParts();\n const lost = useRef(false);\n const once = () => {\n if (lost.current) return;\n lost.current = true;\n onLost();\n };\n return (\n <figure part=\"media\" className={part(\"sc-surface__media\")} data-kind={kind}>\n {kind === \"image\" ? (\n onOpen ? (\n <button type=\"button\" onClick={onOpen} aria-label={name}>\n <img src={src} alt={name} loading=\"lazy\" onError={once} />\n </button>\n ) : (\n <img src={src} alt={name} loading=\"lazy\" onError={once} />\n )\n ) : kind === \"video\" ? (\n <video src={src} controls playsInline preload=\"metadata\" aria-label={name} onError={once} />\n ) : (\n <AudioPlayer src={src} label={name} onError={once} />\n )}\n {caption !== undefined && <figcaption>{caption}</figcaption>}\n </figure>\n );\n}\n\n/** Labels and their values, a row each. */\nfunction Fields({ block }: { block: FieldsBlock }) {\n const part = useSurfaceParts();\n return (\n <dl part=\"fields\" className={part(\"sc-surface__fields\")}>\n {block.items.map((item, index) => (\n <div key={index}>\n <dt>{item.label}</dt>\n <dd>{item.value}</dd>\n </div>\n ))}\n </dl>\n );\n}\n\n/** Rows under named columns, scrolling across inside their own box when they are wide. */\nfunction Table({ block }: { block: TableBlock }) {\n const part = useSurfaceParts();\n return (\n <div part=\"table\" className={part(\"sc-surface__table\")}>\n <table>\n <thead>\n <tr>\n {block.columns.map((column, index) => (\n <th key={index} scope=\"col\">\n {column}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {block.rows.map((row, index) => (\n <tr key={index}>\n {block.columns.map((_, at) => (\n <td key={at}>{row[at] ?? \"\"}</td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\n/** A list block as the kit's `List`: a `Row` each; with an action, rows to pick, the picked checked. */\nfunction Listing({ block }: { block: ListBlock }) {\n const { active, answered, onAction } = useScope();\n const { action } = block;\n const pick = action !== undefined && answered?.name === action ? answered.data.item : undefined;\n return (\n <List>\n {block.items.map((entry) => (\n <Row\n key={entry.id}\n mark={\n entry.image !== undefined && https(entry.image) ? (\n <Mark src={entry.image} photo size={40} />\n ) : undefined\n }\n title={entry.title}\n subtitle={entry.subtitle}\n meta={entry.meta}\n pressed={entry.id === pick}\n disabled={!active}\n onPress={\n action === undefined ? undefined : () => onAction(chosen({ ...block, action }, entry))\n }\n />\n ))}\n </List>\n );\n}\n\n/** A picture from an https address, never sending the page's address along, with its caption. */\nfunction Picture({ block }: { block: ImageBlock }) {\n const part = useSurfaceParts();\n if (!https(block.url)) return null;\n return (\n <figure part=\"image\" className={part(\"sc-surface__image\")}>\n <img src={block.url} alt={block.alt} loading=\"lazy\" referrerPolicy=\"no-referrer\" />\n {block.caption !== undefined && <figcaption>{block.caption}</figcaption>}\n </figure>\n );\n}\n\n/**\n * An automation, read only: the agent's page is where it is switched and its runs opened. One\n * that is not there (removed, renamed since) says so quietly.\n */\nfunction AutomationShown({ block }: { block: AutomationBlock }) {\n const part = useParts();\n const { messages } = useKit();\n // `null`: it is gone; missing: not filled in by the API, which a newer server always does.\n if (block.view == null) {\n return (\n <p className={part(\"sc-automation\", \"sc-automation--gone\")}>{messages.automation.gone}</p>\n );\n }\n return <Automation automation={block.view} runs={block.view.runs} />;\n}\n\n/**\n * An app for the person to connect, as its connection stands: its logo and name over the line\n * the agent wrote of what it will do with it; while it waits for them, the button that opens\n * its consent page apart; once it works, or if it failed, a quiet word in its place. Before\n * the API has asked for it, only the app as the agent named it.\n */\nfunction Connect({ block }: { block: ConnectBlock }) {\n const part = useSurfaceParts();\n const { messages } = useKit();\n const { connection } = block;\n const title = connection?.title ?? block.app;\n const pending = connection?.status === \"pending\" ? connection.url : null;\n const url = pending !== null && https(pending) ? pending : null;\n const working = connection?.status === \"active\";\n const failed = connection !== null && !working && connection.status !== \"pending\";\n return (\n <Card title={title} subtitle={block.purpose} image={connection?.icon ?? undefined}>\n {url !== null ? (\n <Button tone=\"primary\" href={url}>\n {messages.connect.authorize(title)}\n </Button>\n ) : (\n <p\n className={part(\n \"sc-surface__connect\",\n ...(working ? ([\"sc-surface__connect--done\"] as const) : []),\n )}\n >\n {working && <CheckIcon aria-hidden weight=\"bold\" />}\n {connectWords(messages.connect, working, failed)}\n </p>\n )}\n </Card>\n );\n}\n\n/** The quiet word an app's card says where its button isn't: working, failed, or on its way. */\nfunction connectWords(words: KitMessages[\"connect\"], working: boolean, failed: boolean): string {\n if (working) return words.connected;\n return failed ? words.failed : words.preparing;\n}\n\n/**\n * A button that sends its action, marked once it was the one pressed; or a link that opens its\n * page apart (it stays usable on an older turn: it changes nothing here).\n */\nfunction Pressable({ block }: { block: ButtonBlock }) {\n const { active, answered, onAction } = useScope();\n const { action, url, tone, label } = block;\n if (url !== undefined) {\n return https(url) ? (\n <Button tone={tone} href={url}>\n {label}\n </Button>\n ) : null;\n }\n if (action === undefined) return null;\n const done = answered?.name === action && answered.label === label;\n return (\n <Button\n tone={tone}\n icon={done ? CheckIcon : undefined}\n pressed={done}\n disabled={!active}\n onClick={() => onAction(pressed({ ...block, action }))}\n >\n {label}\n </Button>\n );\n}\n\n/** A few options to pick one of at once; the one picked keeps a ring and a check. */\nfunction Choice({ block }: { block: ChoiceBlock }) {\n const { active, answered, onAction } = useScope();\n const part = useSurfaceParts();\n const mine = answered?.name === block.action ? answered.data : undefined;\n const chosenId = mine?.option;\n const listed =\n block.other === true ||\n block.options.length > SHORT_CHOICE ||\n block.options.some((option) => option.hint !== undefined || option.label.length > SHORT_LABEL);\n return (\n <div\n role=\"group\"\n part=\"choice\"\n className={part(\n \"sc-surface__choice\",\n ...(listed ? ([\"sc-surface__choice--list\"] as const) : []),\n )}\n >\n {block.options.map((option) => (\n <button\n key={option.id}\n type=\"button\"\n disabled={!active}\n aria-pressed={option.id === chosenId || undefined}\n onClick={() => onAction(picked(block, option))}\n part=\"option\"\n className={part(\"sc-surface__option\")}\n >\n <span>\n {option.label}\n {option.hint !== undefined && <small>{option.hint}</small>}\n </span>\n {option.id === chosenId && <CheckIcon aria-hidden weight=\"bold\" />}\n </button>\n ))}\n {block.other === true && (\n <Other\n active={active}\n said={typeof mine?.text === \"string\" ? mine.text : undefined}\n onSay={(text) => onAction(answeredIn(block, text))}\n />\n )}\n </div>\n );\n}\n\n/** Pills fit options this few and this short; more, longer or with a hint, they go one a row. */\nconst SHORT_CHOICE = 4;\nconst SHORT_LABEL = 28;\n\n/**\n * A choice's last row, to answer in one's own words: pressed, it turns into a box with its send\n * (Enter sends, Escape folds it back). Once said, the words stand in it, chosen.\n */\nfunction Other({\n active,\n said,\n onSay,\n}: {\n active: boolean;\n said: string | undefined;\n onSay: (text: string) => void;\n}) {\n const part = useSurfaceParts();\n const { messages } = useKit();\n const [writing, setWriting] = useState(false);\n const [text, setText] = useState(\"\");\n if (said !== undefined || !writing) {\n return (\n <button\n type=\"button\"\n disabled={!active}\n aria-pressed={said !== undefined || undefined}\n onClick={() => setWriting(true)}\n part=\"option other\"\n className={part(\"sc-surface__option\", \"sc-surface__option--other\")}\n >\n {said === undefined && <PencilSimpleIcon aria-hidden />}\n <span>{said ?? messages.other}</span>\n {said !== undefined && <CheckIcon aria-hidden weight=\"bold\" />}\n </button>\n );\n }\n const send = () => {\n const words = text.trim();\n if (words !== \"\") onSay(words);\n };\n return (\n <form\n part=\"other\"\n className={part(\"sc-surface__other\")}\n onSubmit={(event) => {\n event.preventDefault();\n send();\n }}\n >\n <input\n // A person who pressed to write wants to write.\n autoFocus\n value={text}\n aria-label={messages.other}\n placeholder={messages.otherPlaceholder}\n onChange={(event) => setText(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n setWriting(false);\n }\n }}\n />\n <button type=\"submit\" aria-label={messages.send} disabled={text.trim() === \"\"}>\n <ArrowUpIcon aria-hidden weight=\"bold\" />\n </button>\n </form>\n );\n}\n\n/** Only an https address is loaded or opened: never a script, never a local file. */\nfunction https(url: string): boolean {\n return /^https:\\/\\//i.test(url);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Place, Surface } from "../blocks.js";
|
|
2
|
+
//#region src/surface/places.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Where a host lets the agent put what it shows besides the conversation: a panel beside it, a
|
|
5
|
+
* modal over the page. A place it doesn't allow shows in the conversation.
|
|
6
|
+
*/
|
|
7
|
+
export interface SurfacePlaces {
|
|
8
|
+
panel?: boolean | undefined;
|
|
9
|
+
modal?: boolean | undefined;
|
|
10
|
+
}
|
|
11
|
+
/** Both, unless the host says otherwise. */
|
|
12
|
+
export declare const EVERY_PLACE: SurfacePlaces;
|
|
13
|
+
/** Where a surface shows in this page: the place the agent chose when the host allows it. */
|
|
14
|
+
export declare function placeOf(surface: Surface, places?: SurfacePlaces): Place;
|
|
15
|
+
//#endregion
|
|
16
|
+
//# sourceMappingURL=places.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/surface/places.ts
|
|
2
|
+
/** Both, unless the host says otherwise. */
|
|
3
|
+
const EVERY_PLACE = {
|
|
4
|
+
panel: true,
|
|
5
|
+
modal: true
|
|
6
|
+
};
|
|
7
|
+
/** Where a surface shows in this page: the place the agent chose when the host allows it. */
|
|
8
|
+
function placeOf(surface, places = EVERY_PLACE) {
|
|
9
|
+
const where = surface.where ?? "chat";
|
|
10
|
+
return where !== "chat" && places[where] === true ? where : "chat";
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { EVERY_PLACE, placeOf };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=places.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"places.js","names":[],"sources":["../../src/surface/places.ts"],"sourcesContent":["import type { Place, Surface } from \"../blocks\";\n\n/**\n * Where a host lets the agent put what it shows besides the conversation: a panel beside it, a\n * modal over the page. A place it doesn't allow shows in the conversation.\n */\nexport interface SurfacePlaces {\n panel?: boolean | undefined;\n modal?: boolean | undefined;\n}\n\n/** Both, unless the host says otherwise. */\nexport const EVERY_PLACE: SurfacePlaces = { panel: true, modal: true };\n\n/** Where a surface shows in this page: the place the agent chose when the host allows it. */\nexport function placeOf(surface: Surface, places: SurfacePlaces = EVERY_PLACE): Place {\n const where = surface.where ?? \"chat\";\n return where !== \"chat\" && places[where] === true ? where : \"chat\";\n}\n"],"mappings":";;AAYA,MAAa,cAA6B;CAAE,OAAO;CAAM,OAAO;AAAK;;AAGrE,SAAgB,QAAQ,SAAkB,SAAwB,aAAoB;CACpF,MAAM,QAAQ,QAAQ,SAAS;CAC/B,OAAO,UAAU,UAAU,OAAO,WAAW,OAAO,QAAQ;AAC9D"}
|
package/dist/surface/scope.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { ComponentType, ReactNode } from "react";
|
|
|
8
8
|
* The surface's own parts, by the class each carries: the blocks that are not a kit primitive,
|
|
9
9
|
* and the form's layout. `classNames` takes these and the kit's (`KitPart`) under the same keys.
|
|
10
10
|
*/
|
|
11
|
-
export type SurfacePart = "sc-surface" | "sc-surface__stack" | "sc-surface__stack--row" | "sc-surface__divider" | "sc-surface__text" | "sc-surface__fields" | "sc-surface__table" | "sc-surface__image" | "sc-surface__media" | "sc-surface__choice" | "sc-surface__choice--list" | "sc-surface__option" | "sc-surface__option--other" | "sc-surface__other" | "sc-surface__form" | "sc-surface__submit" | "sc-surface__foot" | "sc-surface__sent" | "sc-surface__connect" | "sc-surface__connect--done";
|
|
11
|
+
export type SurfacePart = "sc-surface" | "sc-surface__stack" | "sc-surface__stack--row" | "sc-surface__divider" | "sc-surface__text" | "sc-surface__fields" | "sc-surface__table" | "sc-surface__image" | "sc-surface__media" | "sc-surface__choice" | "sc-surface__choice--list" | "sc-surface__option" | "sc-surface__option--other" | "sc-surface__other" | "sc-surface__form" | "sc-surface__submit" | "sc-surface__foot" | "sc-surface__sent" | "sc-surface__connect" | "sc-surface__connect--done" | "sc-surface__host";
|
|
12
12
|
export type SurfaceClassNames = Partial<Record<SurfacePart | KitPart, string>> | undefined;
|
|
13
13
|
/**
|
|
14
14
|
* What a host's component for a block type receives: the block itself, whether the surface still
|
|
@@ -28,5 +28,21 @@ export type BlockProps<Type extends Block["type"]> = Omit<Extract<Block, {
|
|
|
28
28
|
};
|
|
29
29
|
/** The host's own drawing of a block type, in place of the package's: `{ table: TheirTable }`. */
|
|
30
30
|
export type SurfaceComponents = { [Type in Block["type"]]?: ComponentType<BlockProps<Type>>; };
|
|
31
|
+
/**
|
|
32
|
+
* What a host's own block receives (`HostBlocks`): its `props`, as the API checked them against
|
|
33
|
+
* the schema the host declared; `text`, the same in words; whether the surface still takes
|
|
34
|
+
* input, and `onAction` to send what the person did with it, as any block does.
|
|
35
|
+
*/
|
|
36
|
+
export interface HostBlockProps<Props = Record<string, unknown>> {
|
|
37
|
+
props: Props;
|
|
38
|
+
text: string;
|
|
39
|
+
active: boolean;
|
|
40
|
+
onAction: (action: SurfaceAction) => void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The host's own blocks, by the name it declared each with (`PUT /v1/blocks/{name}`):
|
|
44
|
+
* `{ "demoprep.deal-card": DealCard }`. A block of a name it doesn't give shows its words.
|
|
45
|
+
*/
|
|
46
|
+
export type HostBlocks = Readonly<Record<string, ComponentType<HostBlockProps<never>>>>;
|
|
31
47
|
//#endregion
|
|
32
48
|
//# sourceMappingURL=scope.d.ts.map
|
package/dist/surface/scope.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope.js","names":[],"sources":["../../src/surface/scope.ts"],"sourcesContent":["import { createContext, useContext, type ComponentType, type ReactNode } from \"react\";\n\nimport type { Block } from \"../blocks\";\nimport { classes } from \"../classes\";\nimport type { KitPart } from \"../kit/context\";\nimport type { FileHost } from \"../kit/uploads\";\nimport type { SurfaceAction } from \"./values\";\n\n/**\n * The surface's own parts, by the class each carries: the blocks that are not a kit primitive,\n * and the form's layout. `classNames` takes these and the kit's (`KitPart`) under the same keys.\n */\nexport type SurfacePart =\n | \"sc-surface\"\n | \"sc-surface__stack\"\n | \"sc-surface__stack--row\"\n | \"sc-surface__divider\"\n | \"sc-surface__text\"\n | \"sc-surface__fields\"\n | \"sc-surface__table\"\n | \"sc-surface__image\"\n | \"sc-surface__media\"\n | \"sc-surface__choice\"\n | \"sc-surface__choice--list\"\n | \"sc-surface__option\"\n | \"sc-surface__option--other\"\n | \"sc-surface__other\"\n | \"sc-surface__form\"\n | \"sc-surface__submit\"\n | \"sc-surface__foot\"\n | \"sc-surface__sent\"\n | \"sc-surface__connect\"\n | \"sc-surface__connect--done\";\n\nexport type SurfaceClassNames = Partial<Record<SurfacePart | KitPart, string>> | undefined;\n\n/**\n * What a host's component for a block type receives: the block itself, whether the surface still\n * takes input, and `onAction` to send what the person did; a container's `children` come drawn.\n */\nexport type BlockProps<Type extends Block[\"type\"]> = Omit<\n Extract<Block, { type: Type }>,\n \"children\"\n> &\n (Extract<Block, { type: Type }> extends { children: Block[] }\n ? { children: ReactNode }\n : unknown) & {\n active: boolean;\n onAction: (action: SurfaceAction) => void;\n };\n\n/** The host's own drawing of a block type, in place of the package's: `{ table: TheirTable }`. */\nexport type SurfaceComponents = { [Type in Block[\"type\"]]?: ComponentType<BlockProps<Type>> };\n\n/** What every block of one surface shares: whether it takes input, what was done on it. */\nexport interface Scope {\n active: boolean;\n /** What the person already did on the surface. */\n answered: SurfaceAction | undefined;\n onAction: (action: SurfaceAction) => void;\n components: SurfaceComponents | undefined;\n classNames: SurfaceClassNames;\n /** Where its file fields' files go and are found; none: they take none. */\n files: FileHost | undefined;\n}\n\nexport const ScopeContext = createContext<Scope>({\n active: false,\n answered: undefined,\n onAction: () => undefined,\n components: undefined,\n classNames: undefined,\n files: undefined,\n});\n\nexport function useScope(): Scope {\n return useContext(ScopeContext);\n}\n\n/** The classes of the surface's own parts, with the host's own under each. */\nexport function useSurfaceParts(): (...parts: SurfacePart[]) => string {\n const { classNames } = useScope();\n return (...parts) => classes(classNames, ...parts);\n}\n"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"scope.js","names":[],"sources":["../../src/surface/scope.ts"],"sourcesContent":["import { createContext, useContext, type ComponentType, type ReactNode } from \"react\";\n\nimport type { Block } from \"../blocks\";\nimport { classes } from \"../classes\";\nimport type { KitPart } from \"../kit/context\";\nimport type { FileHost } from \"../kit/uploads\";\nimport type { SurfaceAction } from \"./values\";\n\n/**\n * The surface's own parts, by the class each carries: the blocks that are not a kit primitive,\n * and the form's layout. `classNames` takes these and the kit's (`KitPart`) under the same keys.\n */\nexport type SurfacePart =\n | \"sc-surface\"\n | \"sc-surface__stack\"\n | \"sc-surface__stack--row\"\n | \"sc-surface__divider\"\n | \"sc-surface__text\"\n | \"sc-surface__fields\"\n | \"sc-surface__table\"\n | \"sc-surface__image\"\n | \"sc-surface__media\"\n | \"sc-surface__choice\"\n | \"sc-surface__choice--list\"\n | \"sc-surface__option\"\n | \"sc-surface__option--other\"\n | \"sc-surface__other\"\n | \"sc-surface__form\"\n | \"sc-surface__submit\"\n | \"sc-surface__foot\"\n | \"sc-surface__sent\"\n | \"sc-surface__connect\"\n | \"sc-surface__connect--done\"\n | \"sc-surface__host\";\n\nexport type SurfaceClassNames = Partial<Record<SurfacePart | KitPart, string>> | undefined;\n\n/**\n * What a host's component for a block type receives: the block itself, whether the surface still\n * takes input, and `onAction` to send what the person did; a container's `children` come drawn.\n */\nexport type BlockProps<Type extends Block[\"type\"]> = Omit<\n Extract<Block, { type: Type }>,\n \"children\"\n> &\n (Extract<Block, { type: Type }> extends { children: Block[] }\n ? { children: ReactNode }\n : unknown) & {\n active: boolean;\n onAction: (action: SurfaceAction) => void;\n };\n\n/** The host's own drawing of a block type, in place of the package's: `{ table: TheirTable }`. */\nexport type SurfaceComponents = { [Type in Block[\"type\"]]?: ComponentType<BlockProps<Type>> };\n\n/**\n * What a host's own block receives (`HostBlocks`): its `props`, as the API checked them against\n * the schema the host declared; `text`, the same in words; whether the surface still takes\n * input, and `onAction` to send what the person did with it, as any block does.\n */\nexport interface HostBlockProps<Props = Record<string, unknown>> {\n props: Props;\n text: string;\n active: boolean;\n onAction: (action: SurfaceAction) => void;\n}\n\n/**\n * The host's own blocks, by the name it declared each with (`PUT /v1/blocks/{name}`):\n * `{ \"demoprep.deal-card\": DealCard }`. A block of a name it doesn't give shows its words.\n */\n// `never`: a component typed for its own props (`HostBlockProps<Deal>`) takes its place.\nexport type HostBlocks = Readonly<Record<string, ComponentType<HostBlockProps<never>>>>;\n\n/** What every block of one surface shares: whether it takes input, what was done on it. */\nexport interface Scope {\n active: boolean;\n /** What the person already did on the surface. */\n answered: SurfaceAction | undefined;\n onAction: (action: SurfaceAction) => void;\n components: SurfaceComponents | undefined;\n /** The host's own blocks, by name. */\n blocks: HostBlocks | undefined;\n classNames: SurfaceClassNames;\n /** Where its file fields' files go and are found; none: they take none. */\n files: FileHost | undefined;\n}\n\nexport const ScopeContext = createContext<Scope>({\n active: false,\n answered: undefined,\n onAction: () => undefined,\n components: undefined,\n blocks: undefined,\n classNames: undefined,\n files: undefined,\n});\n\nexport function useScope(): Scope {\n return useContext(ScopeContext);\n}\n\n/** The classes of the surface's own parts, with the host's own under each. */\nexport function useSurfaceParts(): (...parts: SurfacePart[]) => string {\n const { classNames } = useScope();\n return (...parts) => classes(classNames, ...parts);\n}\n"],"mappings":";;;AAwFA,MAAa,eAAe,cAAqB;CAC/C,QAAQ;CACR,UAAU,KAAA;CACV,gBAAgB,KAAA;CAChB,YAAY,KAAA;CACZ,QAAQ,KAAA;CACR,YAAY,KAAA;CACZ,OAAO,KAAA;AACT,CAAC;AAED,SAAgB,WAAkB;CAChC,OAAO,WAAW,YAAY;AAChC;;AAGA,SAAgB,kBAAuD;CACrE,MAAM,EAAE,eAAe,SAAS;CAChC,QAAQ,GAAG,UAAU,QAAQ,YAAY,GAAG,KAAK;AACnD"}
|