@v-office/website-chat 1.0.0 → 1.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 +8 -3
- package/dist/{catalog-RXKmEmMW.js → catalog-y3xQ2jXW.js} +16 -9
- package/dist/catalog.d.ts +19 -0
- package/dist/catalog.js +2 -2
- package/dist/{icons-B5PAY3aU.js → icons-B4hI9ipR.js} +98 -5
- package/dist/index.d.ts +18 -4
- package/dist/index.js +211 -76
- package/dist/{panel-D8pKi4RZ.js → panel-hLRBoh8t.js} +200 -115
- package/instructions/CHANGELOG.md +2 -0
- package/instructions/MIGRATION.md +2 -0
- package/instructions/versions/1.0.1/CHANGELOG.md +14 -0
- package/instructions/versions/1.0.1/MIGRATION.md +9 -0
- package/instructions/versions/1.1.0/CHANGELOG.md +52 -0
- package/instructions/versions/1.1.0/MIGRATION.md +22 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -28,16 +28,21 @@ Render `WebsiteChat` once in the layout as a client island; see `instructions/ve
|
|
|
28
28
|
| `locale` | yes | `"de"` or `"en"` |
|
|
29
29
|
| `searchPath` | yes | the site's search, for a rental without a page and when the chat is down |
|
|
30
30
|
| `contactPath` | yes | the site's contact page |
|
|
31
|
-
| `theme` | no | `classic` (default), `soft` or `minimal
|
|
32
|
-
| `colors` | no | `{ primary, secondary }`, any CSS colour
|
|
31
|
+
| `theme` | no | `classic` (default), `soft` or `minimal`: shadows or borders, how round |
|
|
32
|
+
| `colors` | no | `{ primary, secondary, accent }`, any CSS colour, over the site's tokens |
|
|
33
33
|
| `position` | no | the entry pill and the panel on the `right` (default) or `left` |
|
|
34
34
|
| `copy` | no | overrides of single fields of `DEFAULT_COPY` (strings, `suggestions`) |
|
|
35
35
|
|
|
36
36
|
The chat stays hidden until an agent switches it on for the website in AI Studio.
|
|
37
37
|
|
|
38
|
+
It takes its colours and corners from the site's shadcn tokens (`--primary`,
|
|
39
|
+
`--primary-foreground`, `--background`, `--foreground`, `--muted`, `--muted-foreground`, `--border`,
|
|
40
|
+
`--radius`), dark mode included, and its own values where a site has none. A site whose corners
|
|
41
|
+
are square (`--radius: 0`) gets square buttons too.
|
|
42
|
+
|
|
38
43
|
A site with a sticky header sets `--chat-top` (its height) on any element around the widget, such as
|
|
39
44
|
`:root`: the dimmed page and the panel start below it. `--chat-width` widens the panel on wide screens
|
|
40
|
-
(default `
|
|
45
|
+
(default `700px`).
|
|
41
46
|
|
|
42
47
|
The service places these components in replies: `RentalCard` (photo gallery, rating, the wishes the
|
|
43
48
|
search that found it applied), `RentalCarousel`, `Compare`, `StayQuote`, `PriceBreakdown`,
|
|
@@ -137,16 +137,23 @@ const versionOf = (version) => {
|
|
|
137
137
|
Number(match[3])
|
|
138
138
|
] : null;
|
|
139
139
|
};
|
|
140
|
-
/**
|
|
141
|
-
|
|
140
|
+
/** Whether a widget of `clientVersion` is `since` or newer within the same major; an unreadable
|
|
141
|
+
* version is neither. */
|
|
142
|
+
const isAtLeast = (clientVersion, since) => {
|
|
142
143
|
const client = versionOf(clientVersion);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (since[0] !== client[0]) return false;
|
|
147
|
-
return since[1] < client[1] || since[1] === client[1] && since[2] <= client[2];
|
|
148
|
-
});
|
|
144
|
+
const from = versionOf(since);
|
|
145
|
+
if (!client || from[0] !== client[0]) return false;
|
|
146
|
+
return from[1] < client[1] || from[1] === client[1] && from[2] <= client[2];
|
|
149
147
|
};
|
|
148
|
+
/** See {@link ComponentsFor}. */
|
|
149
|
+
const componentsFor = (clientVersion) => PLACEABLE_COMPONENTS.filter((name) => isAtLeast(clientVersion, COMPONENT_SINCE[name]));
|
|
150
|
+
/** Whether the widget shows the assistant's `**bold**` as bold text (1.1.0 and later); an older
|
|
151
|
+
* one would show the asterisks, so the service asks for none there. */
|
|
152
|
+
const rendersBold = (clientVersion) => isAtLeast(clientVersion, "1.1.0");
|
|
153
|
+
/** Whether the widget's booking button opens the rental's booking page with the rate and extras the
|
|
154
|
+
* visitor chose in the chat (1.1.0 and later); an older one opens the rental page and loses them, so
|
|
155
|
+
* the service puts no stay together there. */
|
|
156
|
+
const handsOverStays = (clientVersion) => isAtLeast(clientVersion, "1.1.0");
|
|
150
157
|
/** See {@link ParseItem}. */
|
|
151
158
|
const parseItem = (item, allowed) => {
|
|
152
159
|
if (typeof item !== "object" || item === null) return null;
|
|
@@ -180,4 +187,4 @@ const chatSpec = (items, state) => {
|
|
|
180
187
|
};
|
|
181
188
|
};
|
|
182
189
|
//#endregion
|
|
183
|
-
export { componentsFor as a,
|
|
190
|
+
export { componentsFor as a, rendersBold as c, chatSpec as i, stateKey as l, PLACEABLE_COMPONENTS as n, handsOverStays as o, chatCatalog as r, parseItem as s, COMPONENT_SINCE as t, statePath as u };
|
package/dist/catalog.d.ts
CHANGED
|
@@ -51,6 +51,15 @@ interface ChatStay {
|
|
|
51
51
|
readonly label: string;
|
|
52
52
|
readonly amount: string;
|
|
53
53
|
}[];
|
|
54
|
+
/** The rate the visitor chose in the chat (`?policy=` on the way to booking); absent for the
|
|
55
|
+
* PMS's pre-selection. */
|
|
56
|
+
readonly policyId?: string | null;
|
|
57
|
+
/** The extras the visitor chose in the chat, by the PMS's service id, as the quote priced them
|
|
58
|
+
* (`?service=<id>:<count>` on the way to booking). */
|
|
59
|
+
readonly services?: readonly {
|
|
60
|
+
readonly id: string;
|
|
61
|
+
readonly count: number;
|
|
62
|
+
}[];
|
|
54
63
|
}
|
|
55
64
|
/** A day of a rental's calendar: bookable, taken, or outside what can be booked (past, closed). */
|
|
56
65
|
type ChatDayStatus = "free" | "booked" | "closed";
|
|
@@ -109,6 +118,9 @@ interface ChatState {
|
|
|
109
118
|
readonly stays: Readonly<Record<string, ChatStay>>;
|
|
110
119
|
/** A rental's page on this website in the visitor's language; absent without one. */
|
|
111
120
|
readonly pages: Readonly<Record<string, string>>;
|
|
121
|
+
/** A rental's booking page on this website, where a stay is booked with everything the chat put
|
|
122
|
+
* together; absent without one (the stay then goes to the rental page). */
|
|
123
|
+
readonly bookingPages?: Readonly<Record<string, string>>;
|
|
112
124
|
/** A rental's calendar, by rental id; only for rentals whose calendar was asked for. */
|
|
113
125
|
readonly availability: Readonly<Record<string, ChatCalendar>>;
|
|
114
126
|
/** A rental's price calendar, by rental id (PriceCalendar). */
|
|
@@ -295,6 +307,13 @@ type ParseItem = (item: unknown, allowed: readonly PlaceableComponent[]) => Chat
|
|
|
295
307
|
type ChatSpec = (items: readonly ChatItem[], state: ChatState) => Spec;
|
|
296
308
|
/** See {@link ComponentsFor}. */
|
|
297
309
|
export declare const componentsFor: ComponentsFor;
|
|
310
|
+
/** Whether the widget shows the assistant's `**bold**` as bold text (1.1.0 and later); an older
|
|
311
|
+
* one would show the asterisks, so the service asks for none there. */
|
|
312
|
+
export declare const rendersBold: (clientVersion: string) => boolean;
|
|
313
|
+
/** Whether the widget's booking button opens the rental's booking page with the rate and extras the
|
|
314
|
+
* visitor chose in the chat (1.1.0 and later); an older one opens the rental page and loses them, so
|
|
315
|
+
* the service puts no stay together there. */
|
|
316
|
+
export declare const handsOverStays: (clientVersion: string) => boolean;
|
|
298
317
|
/** See {@link ParseItem}. */
|
|
299
318
|
export declare const parseItem: ParseItem;
|
|
300
319
|
/** See {@link ChatSpec}. */
|
package/dist/catalog.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { i as SESSION_TTL_S, n as CHAT_PATH, r as MAX_MESSAGE_CHARS, t as CHAT_LOCALES } from "./protocol-BuNqZAMt.js";
|
|
2
|
-
import { a as componentsFor, c as
|
|
3
|
-
export { CHAT_LOCALES, CHAT_PATH, COMPONENT_SINCE, MAX_MESSAGE_CHARS, PLACEABLE_COMPONENTS, SESSION_TTL_S, chatCatalog, chatSpec, componentsFor, parseItem, stateKey, statePath };
|
|
2
|
+
import { a as componentsFor, c as rendersBold, i as chatSpec, l as stateKey, n as PLACEABLE_COMPONENTS, o as handsOverStays, r as chatCatalog, s as parseItem, t as COMPONENT_SINCE, u as statePath } from "./catalog-y3xQ2jXW.js";
|
|
3
|
+
export { CHAT_LOCALES, CHAT_PATH, COMPONENT_SINCE, MAX_MESSAGE_CHARS, PLACEABLE_COMPONENTS, SESSION_TTL_S, chatCatalog, chatSpec, componentsFor, handsOverStays, parseItem, rendersBold, stateKey, statePath };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as CHAT_PATH } from "./protocol-BuNqZAMt.js";
|
|
2
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
//#region src/client.ts
|
|
4
4
|
const url = (endpoint) => `${endpoint.replace(/\/+$/, "")}${CHAT_PATH}`;
|
|
5
5
|
/** See {@link ChatAvailable}. */
|
|
@@ -130,16 +130,109 @@ const Svg = ({ children, size = 16 }) => /* @__PURE__ */ jsx("svg", {
|
|
|
130
130
|
focusable: "false",
|
|
131
131
|
children
|
|
132
132
|
});
|
|
133
|
-
/** The chat's mark, in the
|
|
133
|
+
/** The chat's mark, two sparkles in the accent colour's circle. */
|
|
134
134
|
const Spark = () => /* @__PURE__ */ jsx("span", {
|
|
135
135
|
className: "spark",
|
|
136
|
-
children: /* @__PURE__ */
|
|
136
|
+
children: /* @__PURE__ */ jsxs(Svg, {
|
|
137
137
|
size: 18,
|
|
138
|
-
children: /* @__PURE__ */ jsx("path", { d: "
|
|
138
|
+
children: [/* @__PURE__ */ jsx("path", { d: "M10 4c.5 4 2 5.5 6 6-4 .5-5.5 2-6 6-.5-4-2-5.5-6-6 4-.5 5.5-2 6-6Z" }), /* @__PURE__ */ jsx("path", { d: "M18 14c.3 2 1 2.7 3 3-2 .3-2.7 1-3 3-.3-2-1-2.7-3-3 2-.3 2.7-1 3-3Z" })]
|
|
139
139
|
})
|
|
140
140
|
});
|
|
141
141
|
const Arrow = () => /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "M5 12h14M13 6l6 6-6 6" }) });
|
|
142
142
|
const Restart = () => /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 1 0 3-6.7L3 8M3 3v5h5" }) });
|
|
143
143
|
const Cross = () => /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "M6 6l12 12M18 6L6 18" }) });
|
|
144
|
+
const Chevron = ({ side }) => /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: side === "prev" ? "M15 18l-6-6 6-6" : "M9 6l6 6-6 6" }) });
|
|
145
|
+
const Check = () => /* @__PURE__ */ jsx(Svg, {
|
|
146
|
+
size: 14,
|
|
147
|
+
children: /* @__PURE__ */ jsx("path", { d: "M5 12.5l4.5 4.5L19 7.5" })
|
|
148
|
+
});
|
|
149
|
+
/** What a suggestion is about, read from its words (German and English), for its icon. */
|
|
150
|
+
const TOPICS = [
|
|
151
|
+
{
|
|
152
|
+
topic: "date",
|
|
153
|
+
words: /\bwann\b|termin|datum|anreise|\bwhen\b|\bdates?\b/
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
topic: "book",
|
|
157
|
+
words: /buch|\bbook/
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
topic: "price",
|
|
161
|
+
words: /preis|kost|günstig|teuer|budget|€|price|cost|cheap/
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
topic: "pet",
|
|
165
|
+
words: /hund|katze|haustier|\b(?:dogs?|cats?|pets?)\b/
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
topic: "people",
|
|
169
|
+
words: /famili|kinder|zu zweit|personen|erwachsene|gruppe|family|kids|people|adults|couple/
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
topic: "date",
|
|
173
|
+
words: /wann|termin|datum|woche|nächte|nacht|ferien|silvester|ostern|sommer|herbst|winter|frühling|januar|februar|märz|april|mai|juni|juli|august|september|oktober|november|dezember|when|date|week|night|holiday/
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
topic: "place",
|
|
177
|
+
words: /strand|lage|meer|see|wo |nähe|entfern|beach|where|sea|location/
|
|
178
|
+
}
|
|
179
|
+
];
|
|
180
|
+
const TOPIC_PATHS = {
|
|
181
|
+
book: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("rect", {
|
|
182
|
+
x: "5",
|
|
183
|
+
y: "10.5",
|
|
184
|
+
width: "14",
|
|
185
|
+
height: "10",
|
|
186
|
+
rx: "2"
|
|
187
|
+
}), /* @__PURE__ */ jsx("path", { d: "M8.5 10.5V7.5a3.5 3.5 0 0 1 7 0v3" })] }),
|
|
188
|
+
price: /* @__PURE__ */ jsx("path", { d: "M17 6.5A6.5 6.5 0 1 0 17 17.5M4 10.5h9M4 13.5h9" }),
|
|
189
|
+
pet: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
190
|
+
/* @__PURE__ */ jsx("circle", {
|
|
191
|
+
cx: "6",
|
|
192
|
+
cy: "10",
|
|
193
|
+
r: "1.6"
|
|
194
|
+
}),
|
|
195
|
+
/* @__PURE__ */ jsx("circle", {
|
|
196
|
+
cx: "10",
|
|
197
|
+
cy: "6",
|
|
198
|
+
r: "1.6"
|
|
199
|
+
}),
|
|
200
|
+
/* @__PURE__ */ jsx("circle", {
|
|
201
|
+
cx: "14",
|
|
202
|
+
cy: "6",
|
|
203
|
+
r: "1.6"
|
|
204
|
+
}),
|
|
205
|
+
/* @__PURE__ */ jsx("circle", {
|
|
206
|
+
cx: "18",
|
|
207
|
+
cy: "10",
|
|
208
|
+
r: "1.6"
|
|
209
|
+
}),
|
|
210
|
+
/* @__PURE__ */ jsx("path", { d: "M12 12c-3 0-5 3-5 5.2 0 1.4 1.3 2 2.6 1.6 1.5-.5 3.3-.5 4.8 0 1.3.4 2.6-.2 2.6-1.6C17 15 15 12 12 12Z" })
|
|
211
|
+
] }),
|
|
212
|
+
people: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("circle", {
|
|
213
|
+
cx: "9",
|
|
214
|
+
cy: "8",
|
|
215
|
+
r: "3"
|
|
216
|
+
}), /* @__PURE__ */ jsx("path", { d: "M3.5 19c.6-3 2.8-5 5.5-5s4.9 2 5.5 5M15.5 5.5a3 3 0 0 1 0 5.6M17.5 14.3c1.6.7 2.7 2.4 3 4.7" })] }),
|
|
217
|
+
date: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("rect", {
|
|
218
|
+
x: "4",
|
|
219
|
+
y: "5.5",
|
|
220
|
+
width: "16",
|
|
221
|
+
height: "14.5",
|
|
222
|
+
rx: "2"
|
|
223
|
+
}), /* @__PURE__ */ jsx("path", { d: "M4 10h16M8.5 3.5v4M15.5 3.5v4" })] }),
|
|
224
|
+
place: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "M12 21s-6.5-5.6-6.5-11a6.5 6.5 0 0 1 13 0c0 5.4-6.5 11-6.5 11Z" }), /* @__PURE__ */ jsx("circle", {
|
|
225
|
+
cx: "12",
|
|
226
|
+
cy: "10",
|
|
227
|
+
r: "2.3"
|
|
228
|
+
})] }),
|
|
229
|
+
other: /* @__PURE__ */ jsx("path", { d: "M5 5.5h14a1.5 1.5 0 0 1 1.5 1.5v8a1.5 1.5 0 0 1-1.5 1.5h-7l-4.5 3.5v-3.5H5A1.5 1.5 0 0 1 3.5 15V7A1.5 1.5 0 0 1 5 5.5Z" })
|
|
230
|
+
};
|
|
231
|
+
/** A suggestion's icon: the first topic its words name, else a speech bubble. */
|
|
232
|
+
const TopicIcon = ({ text }) => {
|
|
233
|
+
const words = text.toLowerCase();
|
|
234
|
+
const topic = TOPICS.find((candidate) => candidate.words.test(words))?.topic ?? "other";
|
|
235
|
+
return /* @__PURE__ */ jsx(Svg, { children: TOPIC_PATHS[topic] });
|
|
236
|
+
};
|
|
144
237
|
//#endregion
|
|
145
|
-
export {
|
|
238
|
+
export { Restart as a, clearConversation as c, chatAvailable as d, sendMessage as f, Cross as i, loadConversation as l, Check as n, Spark as o, Chevron as r, TopicIcon as s, Arrow as t, saveConversation as u };
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,15 @@ interface ChatCopy {
|
|
|
30
30
|
readonly contactLink: string;
|
|
31
31
|
readonly bookStay: string;
|
|
32
32
|
readonly viewRental: string;
|
|
33
|
+
/** A card's short button: book its stay, or see the rental without one. */
|
|
34
|
+
readonly book: string;
|
|
35
|
+
readonly view: string;
|
|
36
|
+
/** On a card's photo, when the rental has the visitor's wishes. */
|
|
37
|
+
readonly matchBadge: string;
|
|
38
|
+
/** A stay's length on a card; `{n}` is the count. */
|
|
39
|
+
readonly nights: Readonly<Partial<Record<Intl.LDMLPluralRule, string>>> & {
|
|
40
|
+
readonly other: string;
|
|
41
|
+
};
|
|
33
42
|
readonly total: string;
|
|
34
43
|
readonly notBookable: string;
|
|
35
44
|
readonly photo: string;
|
|
@@ -76,15 +85,20 @@ interface ChatCopy {
|
|
|
76
85
|
export declare const DEFAULT_COPY: Readonly<Record<ChatLocale, ChatCopy>>;
|
|
77
86
|
//#endregion
|
|
78
87
|
//#region src/theme.d.ts
|
|
79
|
-
/** The curated looks over one layout
|
|
80
|
-
* lighter shadows), `minimal` (borders instead of shadows,
|
|
88
|
+
/** The curated looks over one layout, each relative to the site's corner radius: `classic` (soft
|
|
89
|
+
* shadows), `soft` (rounder corners, lighter shadows), `minimal` (borders instead of shadows, tighter
|
|
90
|
+
* corners). */
|
|
81
91
|
export declare const CHAT_THEMES: readonly ["classic", "soft", "minimal"];
|
|
82
92
|
type ChatTheme = (typeof CHAT_THEMES)[number];
|
|
83
|
-
/**
|
|
84
|
-
*
|
|
93
|
+
/** Colours for the chat, any CSS colour, over the ones it takes from the site's tokens. `primary`
|
|
94
|
+
* fills buttons, the send button and the chat's mark, and runs around the entry pill; `secondary`
|
|
95
|
+
* tints the visitor's messages and the suggestions. */
|
|
85
96
|
interface ChatColors {
|
|
86
97
|
readonly primary?: string;
|
|
87
98
|
readonly secondary?: string;
|
|
99
|
+
/** The small marks: the chat's name and sparkle, a card's place, the icons, the ring round the
|
|
100
|
+
* entry pill. The primary when unset. */
|
|
101
|
+
readonly accent?: string;
|
|
88
102
|
}
|
|
89
103
|
//#endregion
|
|
90
104
|
//#region src/WebsiteChat.d.ts
|