@doany-ai/inbox 0.0.0-stage → 0.1.0
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 +175 -2
- package/dist/components/inbox-widget.d.ts +35 -0
- package/dist/components/inbox-widget.js +442 -0
- package/dist/components/parts.d.ts +67 -0
- package/dist/components/parts.js +115 -0
- package/dist/core.d.ts +6 -0
- package/dist/core.js +7 -0
- package/dist/format.d.ts +43 -0
- package/dist/format.js +136 -0
- package/dist/hooks.d.ts +69 -0
- package/dist/hooks.js +112 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +10 -0
- package/dist/labels.d.ts +209 -0
- package/dist/labels.js +210 -0
- package/dist/session.d.ts +204 -0
- package/dist/session.js +613 -0
- package/dist/storage.d.ts +21 -0
- package/dist/storage.js +86 -0
- package/dist/types.d.ts +189 -0
- package/dist/types.js +1 -0
- package/package.json +61 -4
package/README.md
CHANGED
|
@@ -1,3 +1,176 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @doany-ai/inbox
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The chat button on a Doany site. A visitor presses it and asks a question, books, calls, or sends a message to the team, giving an email with the first message. The business answers from its Inbox; its AI receptionist answers too once the business has it on. Everything a client writes lands in the business's Inbox under that client. Nothing here configures the button: the business sets its words, corner, hidden pages and rows on Website › Chat button, and the AI receptionist on Settings › AI Receptionist.
|
|
4
|
+
|
|
5
|
+
```jsx
|
|
6
|
+
import { InboxWidget } from "@doany-ai/inbox";
|
|
7
|
+
|
|
8
|
+
// src/App.jsx: once, inside <Router>, outside <AuthGate>, beside <Toaster />
|
|
9
|
+
<InboxWidget bookHref="/book" />
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The scaffold mounts it. Never build a second chat button, never delete the mount, and never add `InboxWidget` to a site whose `package.json` lacks `@doany-ai/inbox`. When the site gets a booking page, set `bookHref` to it. To turn the button off or change it, the founder uses Website › Chat button. List it in `doany/features.jsonc` as `"inbox": {"package": "@doany-ai/inbox", "entry": "@doany-ai/inbox", "pages": {}}`.
|
|
13
|
+
|
|
14
|
+
Needs `@doany-ai/sdk` 0.5.14 or later (`client.inbox`), `@doany-ai/app-core` 0.2.3 or later, and `react-router-dom` 6.4 or later for the widget.
|
|
15
|
+
|
|
16
|
+
## `InboxWidget`
|
|
17
|
+
|
|
18
|
+
| prop | |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| `bookHref` | the site's booking page (`/book`). **Book online** shows only when the business has something bookable (`paths.book`) and this is set. A path starting with `/` opens through the router; anything else as a link. Setting it tells the platform the chat has the row (`capabilities: ["book_online"]` on every message), so the receptionist sends a client who wants to book there; without it, it passes them to the team |
|
|
21
|
+
| `renderBooking` | `(offer, { onBooked }) => node`, for offer cards only. `offer` is `{ service_setting_id, catalog_item_variant_id, date, timezone }`. Render `BookingFlow` from `@doany-ai/calendar` as the booking page does (its `renderPayment` included), with `serviceSettingId={offer.service_setting_id}`, `catalogItemVariantId={offer.catalog_item_variant_id}`, `initialDate={offer.date}`, `timezone={offer.timezone}`, `layout="stacked"` and `onBooked={onBooked}`. Passing it tells the platform the page can show offers (`capabilities: ["offer_booking"]` on every message); without it the receptionist never makes one |
|
|
22
|
+
| `privacyHref` | the site's privacy page (`/privacy`). When set, the conversation shows "By chatting, you agree to our privacy policy." above the box until the first message, or until the visitor hides it. A path starting with `/` opens through the router; anything else as a link |
|
|
23
|
+
| `labels` | part of `InboxLabels`; the rest keep their defaults |
|
|
24
|
+
| `locale` | for the weekday and time in "Back Mon 9:00", an offer's day and "5 min. ago". Default: the browser's |
|
|
25
|
+
| `className` | on the wrapper |
|
|
26
|
+
|
|
27
|
+
It shows nothing until the business's settings are read, where the business has no button (`enabled` false), and on its hidden paths (`/checkout` hides `/checkout` and `/checkout/paid`, not `/checkouts`).
|
|
28
|
+
|
|
29
|
+
**The button.** A round button in the site's primary colour with a chat icon, in the business's corner (`right` or `left`). The business's words (`label`, else `labels.askUs`) are its accessible name and tooltip. While the panel is open it shows a chevron and closes the panel. A team reply that came while the panel was closed puts a mark on it. Button and panel are `z-40`: the site's dialogs, sign-up and toasts (`z-50`) stay above them.
|
|
30
|
+
|
|
31
|
+
**The panel** sits above the button, about 400 px wide and up to 640 px tall with a 16 px radius; on a screen up to 480 px wide it fills the screen and the button hides under it (every header has a close). It has three views.
|
|
32
|
+
|
|
33
|
+
1. **Home**, where a visitor with no chat starts. A header in the primary colour, fading into the panel: the business's name, close, and the greeting in large type: the business's own (`receptionist.greeting`), else `labels.greeting(name, business)` while the AI answers, else `labels.teamGreeting(business)`. Under it one card per way that is ready, each a single row with an arrow:
|
|
34
|
+
- **Ask a question**, with "{name} and the team can help" (`paths.ask`), or, where the AI does not answer, **Send a message**, with "The team will reply soon" (`paths.message`). One card writes; a visitor who would rather not ask the AI finds **Talk to a person** in the conversation's menu.
|
|
35
|
+
- **Book online** (`paths.book` and `bookHref`).
|
|
36
|
+
- **Call us**, with the number (`paths.call`, a `tel:` link).
|
|
37
|
+
|
|
38
|
+
Under the cards: "Back Mon 9:00" when the business is closed (its next opening, on the business's clock); while it is open, "Usually replies within 15 minutes" once the platform knows how quickly the team answers (`availability.replies_within`); the business's away notice whenever it is set. Opening the panel and reading Home costs nothing and makes no call.
|
|
39
|
+
2. **Messages**: the title, close, and the visitor's conversation as one row (an initial, the receptionist's or the team member's name, the last line, how long ago, a dot when a reply has not been seen), or "No messages yet." Below it a button that opens the conversation: Ask a question, or Send a message.
|
|
40
|
+
3. **The conversation**: a header with back (to Messages), an initial, the receptionist's name over "The team can also help" (or the business's name over "The team will reply soon" where the AI does not answer), a ⋯ menu and close. The menu holds the address the chat started with ("Chatting as sam@example.com"), **Talk to a person** and **Start a new chat**, which forgets the chat in this browser with no call. Talk to a person hands a chat the AI answers to the team with whatever is in the box; before the first message it only makes that message go to the team (where the business takes messages).
|
|
41
|
+
|
|
42
|
+
Home and Messages share a bar of two tabs at the bottom, **Home** and **Messages**; the Messages tab carries a mark while a reply has not been seen.
|
|
43
|
+
|
|
44
|
+
**The thread** opens with the greeting as bubbles from the receptionist. The first is the **one line no prop hides**: while the AI answers (`paths.ask`) or the chat holds an answer from it, the receptionist says it is an AI, "Hi there! You're speaking with {name}, {business}'s AI receptionist. I can make mistakes, and you can ask for the team at any time." `labels.aiNotice` may reword it; an empty result shows the default. The second is the greeting ("How can I help?", or the business's own). Where only the team reads the chat, one bubble: the business's greeting or `labels.teamGreeting(business)`. Then the messages: the client's on the right in the primary colour; the receptionist's and the team's on the left, each group followed by a byline, "Annie · AI receptionist · 5 min. ago" or "Jo · 5 min. ago"; the platform's fixed lines centered in small text, by their code (below). Typing dots while an answer is on its way.
|
|
45
|
+
|
|
46
|
+
**The box** is one bordered box at the bottom with a round send arrow. Until this browser has a chat its first row is an **email** field ("email@example.com") above the message. Nothing is sent without an email in the platform's format: a missing or invalid one shows its error inside the box and takes the keyboard. After the first message the email row is gone. The widget asks for no name and no phone (`start` still takes them). A signed-in account has no email row; if the platform cannot take the account's details the row comes back. Under the box, "Powered by DoAny" on accounts that never paid (`branding`).
|
|
47
|
+
|
|
48
|
+
**Suggested questions.** While the AI answers, and before the visitor's first message, up to four questions sit right-aligned above the box (`suggested_questions`): the business's own from Settings › AI Receptionist, or, when it saved none, the ones the platform builds from what the business has. The platform sends those as codes, and each shows a label:
|
|
49
|
+
|
|
50
|
+
| code | when the business has | label | default |
|
|
51
|
+
| --- | --- | --- | --- |
|
|
52
|
+
| `prices` | something with a price listed | `suggestPrices` | What are your prices? |
|
|
53
|
+
| `book` | something bookable online | `suggestBook` | Can I book a time? |
|
|
54
|
+
| `hours` | opening hours | `suggestHours` | When are you open? |
|
|
55
|
+
| `address` | an address | `suggestAddress` | Where are you? |
|
|
56
|
+
|
|
57
|
+
A code this version does not know is not shown. Pressing a question puts it in the box. With the email row still empty the keyboard goes to the email, and Send sends the question with it, as any first message; with the email typed, or for a signed-in account, it is sent at once. The questions go with the first message, and are not offered once the visitor chose Talk to a person. `inboxSuggestedQuestionLabelKeys` holds the table; `suggestedQuestionTexts(questions, labels)` returns the words.
|
|
58
|
+
|
|
59
|
+
**The privacy line.** On a site that passes `privacyHref`, a small line sits above the box until the first message: "By chatting, you agree to our privacy policy.", where "privacy policy" links to the site's privacy page, and a × that hides it. The link opens through the router, so the page changes without a reload; the panel stays open beside it, except on a phone, where it covers the page and closes. Hiding the line is remembered for the browser session (`sessionStorage`, `doany_inbox_privacy_dismissed`). `labels.privacyNotice` carries `{link}` where `labels.privacyPolicy` goes.
|
|
60
|
+
|
|
61
|
+
The keyboard follows: opening the panel moves it to the panel, opening the conversation to the box (the email row first), and closing the panel back to the button. Escape closes the panel, unless the ⋯ menu is open, which it closes instead. New messages are announced from a polite log.
|
|
62
|
+
|
|
63
|
+
All text renders as plain text. Only `http(s)` links to the site's own live hosts (`link_hosts`) become links; anything else stays text.
|
|
64
|
+
|
|
65
|
+
**Offers.** On a page that passes `renderBooking`, the receptionist may answer with an offer: its words, the day (`Sat, Nov 14`), that day's open times with their timezone, and the site's own booking flow under them, on that day and in that timezone. While the offer is the newest message in the thread the flow is already open, with nothing to press. It stays open when a line follows it (a reply, "You're booked."), and gives way to a card when a newer offer arrives. An older offer is a card with **Book**, which opens its flow. The times are not held, so the flow reads them again and a time taken meanwhile shows its usual refresh. When the flow books, `onBooked` reports the booking to the chat (`client.inbox.bookings`, with the booking's own token), and the chat shows "You're booked." once the platform has noted it. A booking that still waits for its payment (`registration.status` is `pending_payment`) is not booked yet: the chat says nothing, and reports it again alongside its reads (no more often than every 15 seconds, for up to an hour) until the payment confirms it and the line appears. The platform notes only a booking of this chat's client on this site; it never books by itself.
|
|
66
|
+
|
|
67
|
+
### What the visitor is told
|
|
68
|
+
|
|
69
|
+
| when | they see |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| the first message has not reached the business | the message, locked, with "Sending…" or "Not sent. Try again". It is resent unchanged (the SDK keeps it for 15 minutes, across a reload, and resends it once on the next page) |
|
|
72
|
+
| the business turned the button off during a chat | earlier messages stay; the box gives way to "Chat isn't available right now." What was typed is kept for when it is back |
|
|
73
|
+
| a first message the platform refused | the conversation again, with the message and the email back in the box |
|
|
74
|
+
| the client is blocked | "This chat is closed." |
|
|
75
|
+
| the address is a preview | "This site isn't published yet." |
|
|
76
|
+
| no more chats can start with a typed email today | "We can't start new chats today. Please try again tomorrow." with the business's phone and email when it has them |
|
|
77
|
+
| no more new chats right now | the business's phone and email, or "Please try again later." The typed text stays |
|
|
78
|
+
| too many messages | "Too many messages. Wait a moment and try again." |
|
|
79
|
+
| an emailed link that stopped working | "This link has expired." on Home |
|
|
80
|
+
|
|
81
|
+
### Event codes
|
|
82
|
+
|
|
83
|
+
The platform stores codes, never words. Each `payload.code` shows one label, so a translated site shows no English:
|
|
84
|
+
|
|
85
|
+
| code | label | default |
|
|
86
|
+
| --- | --- | --- |
|
|
87
|
+
| `asked_for_person` | `teamWillReply` | The team will get back to you on this. + the wait |
|
|
88
|
+
| `sent_to_team` | `teamWillReply` | The team will get back to you on this. + the wait |
|
|
89
|
+
| `team_has_it` | `teamWillReply` | The team will get back to you on this. + the wait |
|
|
90
|
+
| `cannot_answer` | `cannotAnswer` | I'm not sure about that. I'll pass this to the team. + the wait |
|
|
91
|
+
| `unavailable` | `unavailable`, then `teamWillReply` | I can't answer more right now. The team will get back to you on this. + the wait |
|
|
92
|
+
| `team_replied_by_email` | `teamRepliedByEmail` | The team replied by email. |
|
|
93
|
+
| `safety` | `safety` | If you or someone else is in immediate danger, or you may hurt yourself, please contact your local emergency services now. This chat is not watched live. |
|
|
94
|
+
| `booking_made` | `bookingMade` | You're booked. The confirmation is in your email. |
|
|
95
|
+
| anything else | `unknownEvent` | The team will reply here or by email. |
|
|
96
|
+
|
|
97
|
+
**The wait.** The first five codes leave the client waiting for the team, and each ends with how long that takes (`waitText`):
|
|
98
|
+
|
|
99
|
+
| the business is | the line ends with | label |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| open, and the platform knows how quickly its team answers (`availability.replies_within`) | "Bloom Studio typically replies in 4 hours." | `typicallyReplies(business, time)`, with `replyTime(minutes)` for the time: "15 minutes", "an hour", "4 hours", "a day" |
|
|
102
|
+
| open, and it does not know yet | "They'll reply here or by email." | `replyHereOrEmail` |
|
|
103
|
+
| closed | "Back Mon 9:00" | `backAt(day, time)` |
|
|
104
|
+
|
|
105
|
+
So a visitor who pressed Talk to a person reads "The team will get back to you on this. Bloom Studio typically replies in 4 hours."
|
|
106
|
+
|
|
107
|
+
The wait is said once. When several of these lines follow each other with no reply from the team in between, only the latest carries it (`saysTheWait`): an earlier `cannot_answer` or `unavailable` keeps its own first sentence, and an earlier one of the other three is not drawn. After the team replies, the next handover says it again.
|
|
108
|
+
|
|
109
|
+
`inboxEventLabelKeys` holds the first table; `eventText(code, labels, wait)` returns the words, with `wait` as `{ business, repliesWithin, backAt }`, or `null` for a line a later one repeats.
|
|
110
|
+
|
|
111
|
+
### Reading and polling
|
|
112
|
+
|
|
113
|
+
No call is made before the first message: opening the panel, reading the greeting and typing an email write nothing. With a chat, the thread is read every 2 seconds while an answer is on its way, every 5 seconds with the panel open, and every 30 seconds with it closed. After a failure the wait doubles (up to a minute), or is what the platform asked. Reading stops after 30 idle minutes and starts again when the tab comes back into focus or the panel opens. A team reply while the panel is closed puts a mark on the button and on the Messages tab. The business's settings are read again when the panel opens and at most once a minute alongside the reads. Replies are marked seen only while the panel is open and the page is visible.
|
|
114
|
+
|
|
115
|
+
### The emailed link
|
|
116
|
+
|
|
117
|
+
A reply's email links to `https://{site}/#doany_inbox={token}`. The widget reads the token with `URLSearchParams` from the hash, keeps it in memory, takes only that key off the address through the router (`replace`, keeping the path, the query, the rest of the hash and the history state), opens the panel and calls `client.inbox.resume`. The chat then belongs to this browser, which sees the business's replies from then on.
|
|
118
|
+
|
|
119
|
+
### Where the chat is kept
|
|
120
|
+
|
|
121
|
+
The SDK keeps the chat's token in `localStorage`, one record per account (and one for guests), and a first message whose answer is owed in `sessionStorage`. A chat started as a guest continues after signing in and becomes the account's; signing out takes the account's chat off the browser, so the button then starts a new one. The platform drops a token only with `INBOX_NOT_FOUND`; then the next message starts a new chat. This package keeps only which team reply was last shown (`doany_inbox_read`), so a reload does not mark it unread again, and, for the browser session, that the visitor hid the privacy line.
|
|
122
|
+
|
|
123
|
+
## Hooks
|
|
124
|
+
|
|
125
|
+
`useInbox(options)` has no DOM, so a site can draw its own chat UI on it (import it from `@doany-ai/inbox/core`, which needs no router). Today the platform takes chat calls only from a website's pages; mobile apps come later. Mount it once per page: each instance polls.
|
|
126
|
+
|
|
127
|
+
| option | |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `open` | the panel is open: reads every 5 seconds, marks replies seen. Default `false` |
|
|
130
|
+
| `visible` | the page is visible. Default `true` |
|
|
131
|
+
| `pagePath`, `language` | kept with the first message |
|
|
132
|
+
| `capabilities` | what the page can show beyond text: `"book_online"` (a Book online row), `"offer_booking"` (offer cards) |
|
|
133
|
+
| `actor` | `guest` or `user:<id>`; left out, read from `<AuthProvider>`. A change reads the chat again |
|
|
134
|
+
| `storage` | where the read mark is kept; default `localStorage` |
|
|
135
|
+
| `enabled` | `false` reads nothing |
|
|
136
|
+
|
|
137
|
+
| returns | |
|
|
138
|
+
| --- | --- |
|
|
139
|
+
| `config`, `configLoading`, `available` | the button's settings (`GET /apps/{app_id}/inbox/config`) |
|
|
140
|
+
| `ready`, `hasChat`, `email`, `me`, `signedIn` | whether this browser holds a chat; the address it started with (null for a signed-in account or a chat opened from an emailed link) |
|
|
141
|
+
| `messages` | the thread, oldest first: `{ id, author, author_name, kind, text, payload, occurred_at }` |
|
|
142
|
+
| `replyPending`, `paused`, `off`, `closed`, `unread` | |
|
|
143
|
+
| `pending` | a first message not yet confirmed: `{ text, intent, email, state: "sending" \| "failed" }` |
|
|
144
|
+
| `draft` | a first message refused for good, to put back in the box |
|
|
145
|
+
| `busy`, `problem`, `retryAfter`, `linkExpired` | `problem`: `contact_limit`, `chat_limit`, `rate_limited`, `off`, `closed`, `not_published`, `details_required`, `failed` |
|
|
146
|
+
| `start({ text, intent?, contact? })` | the first message; `intent` `ask` (default) or `person`; `contact` `{ email, name?, phone? }`, required unless signed in. Resolves `true` once stored |
|
|
147
|
+
| `retryPending()` | sends the unconfirmed first message again, unchanged |
|
|
148
|
+
| `send(text)` | a later message. On `false`, keep the words; the same words again keep their id |
|
|
149
|
+
| `askForPerson(text?)` | hands the chat to a person |
|
|
150
|
+
| `resume(linkToken)` | opens an emailed link's token |
|
|
151
|
+
| `reportBooking(result)` | a booking made from an offer card (`BookingFlow`'s `onBooked` result): reported once per booking, with its token; one waiting for its payment is reported again until it is confirmed |
|
|
152
|
+
| `newChat()` | forgets this browser's chat, no call |
|
|
153
|
+
| `wake()`, `markRead()`, `refreshConfig()` | |
|
|
154
|
+
|
|
155
|
+
`InboxSession` is the same chat as a plain class, for code outside React.
|
|
156
|
+
|
|
157
|
+
## Labels
|
|
158
|
+
|
|
159
|
+
`askUs`, `close`, `back`, `unread`, `aiNotice(name, business)`, `greeting(name, business)`, `teamGreeting(business)`, `homeTab`, `messagesTab`, `noMessages`, `ask`, `askSub(name)`, `book`, `call`, `message`, `messageSub`, `teamCanHelp`, `more`, `justNow`, `backAt(day, time)`, `repliesWithin(minutes)`, `replyTime(minutes)`, `emailLabel`, `emailPlaceholder`, `emailRequired`, `emailFormat`, `messageLabel`, `messagePlaceholder`, `send`, `sending`, the suggested questions (`suggestPrices`, `suggestBook`, `suggestHours`, `suggestAddress`, `suggestedQuestions` for the group's name), `privacyNotice`, `privacyPolicy`, `dismiss`, `askForPerson`, `aiReceptionist`, `team`, `typing`, `chattingAs(email)`, `startNewChat`, `notSent`, `tryAgain`, the handover labels above (`teamWillReply`, `typicallyReplies(business, time)`, `replyHereOrEmail`), the event labels (`bookingMade`, `safety`, `cannotAnswer`, `unavailable`, `teamRepliedByEmail`, `unknownEvent`), `linkExpired`, `contactLimit`, `tryLater`, `rateLimited`, `notPublished`, `off`, `closed`, `sendFailed`, `emailUs`, `bookOffer`, `poweredBy`. How long ago a message was written ("5 min. ago") comes from the browser in `locale`, not from a label; under a minute it is `justNow`. Defaults are `defaultInboxLabels`. The business's own words (the button's text, the greeting, the away notice, its name) come from its settings and are not labels.
|
|
160
|
+
|
|
161
|
+
## Customizing
|
|
162
|
+
|
|
163
|
+
1. **Theme**: the site's variables style everything; the package has no colour of its own. `--primary` is the button, Home's header, the visitor's bubbles and the send arrow; `--card`, `--border` and `--muted` the panel, the cards and the business's bubbles.
|
|
164
|
+
2. **Props**: `labels`, `bookHref`, `privacyHref`, `className`.
|
|
165
|
+
3. **Hook**: draw your own chat on `useInbox`, keeping the receptionist's AI line above, the email before the first message, plain-text messages and links only to `config.link_hosts`.
|
|
166
|
+
|
|
167
|
+
The package ships no CSS: the site's Tailwind build scans `node_modules/@doany-ai/inbox/dist`.
|
|
168
|
+
|
|
169
|
+
## Develop
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
npm install
|
|
173
|
+
npm run typecheck # includes tests/sdk-contract.types.ts
|
|
174
|
+
npm test
|
|
175
|
+
npm run build
|
|
176
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type InboxLabels } from "../labels.js";
|
|
2
|
+
import { type RenderBooking } from "./parts.js";
|
|
3
|
+
export interface InboxWidgetProps {
|
|
4
|
+
/**
|
|
5
|
+
* The site's booking page (`/book`). Book online shows only when the
|
|
6
|
+
* business has something bookable and this is set.
|
|
7
|
+
*/
|
|
8
|
+
bookHref?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Draws the site's booking flow for an offer the receptionist made in the
|
|
11
|
+
* chat: `BookingFlow` from `@doany-ai/calendar` on `offer.date` in
|
|
12
|
+
* `offer.timezone`, with `hooks.onBooked` as its `onBooked`. Only offer
|
|
13
|
+
* cards use it. Passing it tells the platform this page can show offers;
|
|
14
|
+
* without it the receptionist never makes one.
|
|
15
|
+
*/
|
|
16
|
+
renderBooking?: RenderBooking;
|
|
17
|
+
/**
|
|
18
|
+
* The site's privacy page (`/privacy`). When set, the conversation shows
|
|
19
|
+
* "By chatting, you agree to our privacy policy." above the box until the
|
|
20
|
+
* first message, or until the visitor hides it.
|
|
21
|
+
*/
|
|
22
|
+
privacyHref?: string;
|
|
23
|
+
/** The words, in part; the rest keep their defaults. */
|
|
24
|
+
labels?: Partial<InboxLabels>;
|
|
25
|
+
/** For the weekday and time in "Back Mon 9:00" and an offer's day. Default: the browser's. */
|
|
26
|
+
locale?: string;
|
|
27
|
+
className?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The chat button and its panel, for a site. Mount it once, inside the
|
|
31
|
+
* router and outside the sign-in gate, beside `<Toaster />`. It reads the
|
|
32
|
+
* business's settings itself and shows nothing where the business has no
|
|
33
|
+
* button, on its hidden paths, or before the settings are read.
|
|
34
|
+
*/
|
|
35
|
+
export declare function InboxWidget({ bookHref, renderBooking, privacyHref, labels: given, locale, className }: InboxWidgetProps): import("react").JSX.Element | null;
|