@v-office/website-chat 1.0.1 → 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-C0Bb7ln-.js → panel-hLRBoh8t.js} +197 -114
- package/instructions/CHANGELOG.md +1 -0
- package/instructions/MIGRATION.md +1 -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/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as MAX_MESSAGE_CHARS, t as CHAT_LOCALES } from "./protocol-BuNqZAMt.js";
|
|
2
|
-
import {
|
|
2
|
+
import { d as chatAvailable, l as loadConversation, o as Spark, t as Arrow } from "./icons-B4hI9ipR.js";
|
|
3
3
|
import { Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -12,7 +12,7 @@ const DEFAULT_COPY = {
|
|
|
12
12
|
title: "Reiseassistent",
|
|
13
13
|
newConversation: "Neues Gespräch",
|
|
14
14
|
notice: "Sie chatten mit einer KI. Preise und Verfügbarkeit in den Karten kommen direkt aus unserem Buchungssystem.",
|
|
15
|
-
greeting: "Hallo!
|
|
15
|
+
greeting: "Hallo! Erzählen Sie mir einfach, wie Ihr Urlaub aussehen soll: wann, mit wem und was Ihnen wichtig ist. Ich suche passende Unterkünfte, prüfe freie Termine und Preise und zeige Ihnen, was passt.",
|
|
16
16
|
suggestions: [
|
|
17
17
|
"Ein Ferienhaus für 4 Personen in den Sommerferien",
|
|
18
18
|
"Eine Woche mit Hund, gern mit eingezäuntem Garten",
|
|
@@ -31,6 +31,13 @@ const DEFAULT_COPY = {
|
|
|
31
31
|
contactLink: "Kontakt aufnehmen",
|
|
32
32
|
bookStay: "Zur Buchung",
|
|
33
33
|
viewRental: "Zur Unterkunft",
|
|
34
|
+
book: "Buchen",
|
|
35
|
+
view: "Ansehen",
|
|
36
|
+
matchBadge: "Passt genau",
|
|
37
|
+
nights: {
|
|
38
|
+
one: "{n} Nacht",
|
|
39
|
+
other: "{n} Nächte"
|
|
40
|
+
},
|
|
34
41
|
total: "Gesamtpreis",
|
|
35
42
|
notBookable: "Für diesen Zeitraum nicht buchbar",
|
|
36
43
|
photo: "Foto",
|
|
@@ -77,7 +84,7 @@ const DEFAULT_COPY = {
|
|
|
77
84
|
title: "Travel assistant",
|
|
78
85
|
newConversation: "New conversation",
|
|
79
86
|
notice: "You are chatting with an AI. Prices and availability in the cards come straight from our booking system.",
|
|
80
|
-
greeting: "Hello!
|
|
87
|
+
greeting: "Hello! Just tell me what your holiday should look like: when, who with and what matters to you. I find rentals that fit, check free dates and prices and show you what suits.",
|
|
81
88
|
suggestions: [
|
|
82
89
|
"A holiday home for 4 in the summer holidays",
|
|
83
90
|
"A week with our dog, ideally with a fenced garden",
|
|
@@ -96,6 +103,13 @@ const DEFAULT_COPY = {
|
|
|
96
103
|
contactLink: "Contact us",
|
|
97
104
|
bookStay: "Book this stay",
|
|
98
105
|
viewRental: "View the rental",
|
|
106
|
+
book: "Book",
|
|
107
|
+
view: "View",
|
|
108
|
+
matchBadge: "Good match",
|
|
109
|
+
nights: {
|
|
110
|
+
one: "{n} night",
|
|
111
|
+
other: "{n} nights"
|
|
112
|
+
},
|
|
99
113
|
total: "Total",
|
|
100
114
|
notBookable: "Not bookable for these dates",
|
|
101
115
|
photo: "Photo",
|
|
@@ -138,17 +152,88 @@ const DEFAULT_COPY = {
|
|
|
138
152
|
};
|
|
139
153
|
//#endregion
|
|
140
154
|
//#region src/theme.ts
|
|
141
|
-
/** The curated looks over one layout
|
|
142
|
-
* lighter shadows), `minimal` (borders instead of shadows,
|
|
155
|
+
/** The curated looks over one layout, each relative to the site's corner radius: `classic` (soft
|
|
156
|
+
* shadows), `soft` (rounder corners, lighter shadows), `minimal` (borders instead of shadows, tighter
|
|
157
|
+
* corners). */
|
|
143
158
|
const CHAT_THEMES = [
|
|
144
159
|
"classic",
|
|
145
160
|
"soft",
|
|
146
161
|
"minimal"
|
|
147
162
|
];
|
|
148
|
-
/**
|
|
163
|
+
/** The site tokens the chat follows: the shadcn set every AI Studio template defines (and
|
|
164
|
+
* website-components reads), the chat variable each sets, and the chat's own value for a site
|
|
165
|
+
* without it. CSS custom properties reach into the shadow root; `all: initial` leaves them alone.
|
|
166
|
+
* Not `--secondary`: shadcn sets it next to `--muted`, so the visitor's messages would vanish on the
|
|
167
|
+
* message area; the chat's tint is the site's primary, lightened (`--chat-secondary`). */
|
|
168
|
+
const SITE_COLORS = [
|
|
169
|
+
{
|
|
170
|
+
site: "--primary",
|
|
171
|
+
chat: "--chat-primary",
|
|
172
|
+
own: "#1f4f8b"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
site: "--primary-foreground",
|
|
176
|
+
chat: "--chat-on-primary",
|
|
177
|
+
own: "#fff"
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
site: "--background",
|
|
181
|
+
chat: "--chat-surface",
|
|
182
|
+
own: "#fff"
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
site: "--muted",
|
|
186
|
+
chat: "--chat-surface-soft",
|
|
187
|
+
own: "#f5f7fa"
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
site: "--foreground",
|
|
191
|
+
chat: "--chat-text",
|
|
192
|
+
own: "#1b1f24"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
site: "--muted-foreground",
|
|
196
|
+
chat: "--chat-muted",
|
|
197
|
+
own: "#5b6470"
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
site: "--border",
|
|
201
|
+
chat: "--chat-border",
|
|
202
|
+
own: "#d9dee5"
|
|
203
|
+
}
|
|
204
|
+
];
|
|
205
|
+
/** shadcn's base radius; the themes scale it (classic 1.6×, so 16px for shadcn's 10px). */
|
|
206
|
+
const SITE_RADIUS = {
|
|
207
|
+
site: "--radius",
|
|
208
|
+
chat: "--chat-base-radius",
|
|
209
|
+
own: "10px"
|
|
210
|
+
};
|
|
211
|
+
/** See {@link UnusableSiteTokens}. */
|
|
212
|
+
const unusableSiteTokens = (host) => {
|
|
213
|
+
if (typeof CSS === "undefined" || typeof CSS.supports !== "function") return {};
|
|
214
|
+
const style = getComputedStyle(host);
|
|
215
|
+
const unusable = (token, property) => {
|
|
216
|
+
const value = style.getPropertyValue(token.site).trim();
|
|
217
|
+
return value !== "" && !CSS.supports(property, value);
|
|
218
|
+
};
|
|
219
|
+
return Object.fromEntries([...SITE_COLORS.filter((token) => unusable(token, "color")).map((t) => [t.chat, t.own]), ...unusable(SITE_RADIUS, "width") ? [[SITE_RADIUS.chat, SITE_RADIUS.own]] : []]);
|
|
220
|
+
};
|
|
221
|
+
/** See {@link ColorVariables}. An override brings its own text colour, as before the chat took the
|
|
222
|
+
* site's tokens: the site's foregrounds suit the site's colours (a dark theme's light primary has
|
|
223
|
+
* dark text), not the brand colour a site sets here. */
|
|
149
224
|
const colorVariables = (colors) => ({
|
|
150
|
-
...colors?.primary && {
|
|
151
|
-
|
|
225
|
+
...colors?.primary && {
|
|
226
|
+
"--chat-primary": colors.primary,
|
|
227
|
+
"--chat-on-primary": "#fff"
|
|
228
|
+
},
|
|
229
|
+
...colors?.secondary && {
|
|
230
|
+
"--chat-secondary": colors.secondary,
|
|
231
|
+
"--chat-on-secondary": "#1b1f24"
|
|
232
|
+
},
|
|
233
|
+
...colors?.accent && {
|
|
234
|
+
"--chat-accent": colors.accent,
|
|
235
|
+
"--chat-on-accent": "#fff"
|
|
236
|
+
}
|
|
152
237
|
});
|
|
153
238
|
/** The widget's stylesheet, inserted into its shadow root. */
|
|
154
239
|
const CHAT_CSS = `
|
|
@@ -157,15 +242,17 @@ const CHAT_CSS = `
|
|
|
157
242
|
\`:host\` rules, and only important ones reverse that. */
|
|
158
243
|
all: initial !important;
|
|
159
244
|
font-family: inherit !important;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
--chat-secondary:
|
|
163
|
-
--chat-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
--chat-
|
|
167
|
-
--chat-
|
|
168
|
-
--chat-radius:
|
|
245
|
+
${SITE_COLORS.map((t) => ` ${t.chat}: var(${t.site}, ${t.own});`).join("\n")}
|
|
246
|
+
${SITE_RADIUS.chat}: var(${SITE_RADIUS.site}, ${SITE_RADIUS.own});
|
|
247
|
+
--chat-secondary: color-mix(in srgb, var(--chat-primary) 12%, var(--chat-surface));
|
|
248
|
+
--chat-on-secondary: var(--chat-text);
|
|
249
|
+
/* The small marks (the chat's name and sparkle, a card's place, icons, the ring): the primary,
|
|
250
|
+
unless a site sets an accent of its own (\`colors.accent\`). */
|
|
251
|
+
--chat-accent: var(--chat-primary);
|
|
252
|
+
--chat-on-accent: var(--chat-on-primary);
|
|
253
|
+
--chat-radius: calc(var(--chat-base-radius) * 1.6);
|
|
254
|
+
/* Buttons, chips and the pill are pills, unless the site's corners are square (radius 0). */
|
|
255
|
+
--chat-round: calc(var(--chat-base-radius) * 100);
|
|
169
256
|
--chat-shadow: 0 18px 48px rgb(15 23 42 / 0.18);
|
|
170
257
|
--chat-card-shadow: 0 1px 3px rgb(15 23 42 / 0.08), 0 8px 24px rgb(15 23 42 / 0.06);
|
|
171
258
|
/* Not set here, so a site's value reaches the widget: \`--chat-top\` keeps a sticky header uncovered,
|
|
@@ -179,15 +266,16 @@ const CHAT_CSS = `
|
|
|
179
266
|
.entry { position: fixed; right: 20px; bottom: 20px; z-index: 2147483000; }
|
|
180
267
|
[data-position="left"] .entry { right: auto; left: 20px; }
|
|
181
268
|
.pill { position: relative; z-index: 1; display: inline-flex; align-items: center; gap: 12px;
|
|
182
|
-
padding: 8px 20px 8px 8px; border-radius:
|
|
183
|
-
|
|
269
|
+
padding: 8px 20px 8px 8px; border-radius: var(--chat-round);
|
|
270
|
+
border: 1px solid color-mix(in srgb, var(--chat-surface) 70%, transparent);
|
|
271
|
+
background: color-mix(in srgb, var(--chat-surface) 86%, transparent); backdrop-filter: blur(16px) saturate(1.4);
|
|
184
272
|
-webkit-backdrop-filter: blur(16px) saturate(1.4); box-shadow: var(--chat-shadow);
|
|
185
273
|
color: var(--chat-text); font: inherit; text-align: left; cursor: pointer; }
|
|
186
274
|
.spark { flex: none; display: grid; place-items: center; width: 40px; height: 40px; border-radius: 999px;
|
|
187
|
-
background: var(--chat-
|
|
275
|
+
background: var(--chat-accent); color: var(--chat-on-accent); }
|
|
188
276
|
.pill-text { display: flex; flex-direction: column; min-width: 0; flex: 1; line-height: 1.25; }
|
|
189
|
-
.pill-title { font-size: 12px; font-weight: 600; color: var(--chat-
|
|
190
|
-
.pill-prompt { font-size:
|
|
277
|
+
.pill-title { font-size: 12px; font-weight: 600; color: var(--chat-accent); }
|
|
278
|
+
.pill-prompt { font-size: 15px; font-weight: 500; }
|
|
191
279
|
.launcher-field { display: none; }
|
|
192
280
|
.launcher-field input { border: 0; outline: none; background: transparent; padding: 0; margin: 0;
|
|
193
281
|
font: inherit; font-size: 14px; font-weight: 600; color: var(--chat-text); width: 100%; }
|
|
@@ -195,12 +283,14 @@ const CHAT_CSS = `
|
|
|
195
283
|
.launcher-field:focus-within input::placeholder { color: var(--chat-muted); }
|
|
196
284
|
/* The field has no outline of its own: the pill shows keyboard focus, typed text or not. */
|
|
197
285
|
.launcher-field:focus-within { outline: 2px solid var(--chat-primary); outline-offset: 2px; }
|
|
198
|
-
|
|
286
|
+
/* Until the first message the pill glows in the accent colour as well. */
|
|
287
|
+
.entry[data-glow] .pill { box-shadow: var(--chat-shadow), 0 0 28px color-mix(in srgb, var(--chat-accent) 38%, transparent); }
|
|
288
|
+
.glow { position: absolute; inset: -3px; border-radius: var(--chat-round); overflow: hidden; padding: 3px;
|
|
199
289
|
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
|
|
200
290
|
-webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box exclude,
|
|
201
291
|
linear-gradient(#000 0 0); }
|
|
202
292
|
.glow::before { content: ""; position: absolute; inset: -150%;
|
|
203
|
-
background: conic-gradient(from 0deg, transparent 0 62%, var(--chat-
|
|
293
|
+
background: conic-gradient(from 0deg, transparent 0 62%, var(--chat-accent) 82%, transparent 100%); }
|
|
204
294
|
@media (min-width: 1024px) {
|
|
205
295
|
.launcher[data-narrow] { display: none; }
|
|
206
296
|
.launcher-field { display: flex; width: 26rem; padding: 8px; cursor: text; }
|
|
@@ -216,46 +306,54 @@ const CHAT_CSS = `
|
|
|
216
306
|
.scrim[hidden], .panel[hidden] { display: none; }
|
|
217
307
|
@media (min-width: 1024px) {
|
|
218
308
|
.panel { left: auto; right: 20px; bottom: 20px; top: calc(var(--chat-top, 0px) + 20px);
|
|
219
|
-
width: min(var(--chat-width,
|
|
309
|
+
width: min(var(--chat-width, 700px), calc(100vw - 40px)); border-radius: var(--chat-radius); }
|
|
220
310
|
[data-position="left"] .panel { right: auto; left: 20px; }
|
|
221
311
|
}
|
|
222
|
-
.header { display: flex; align-items: center; gap: 8px; padding:
|
|
223
|
-
.title { margin: 0 auto 0 0; font-size:
|
|
224
|
-
text-transform: uppercase; color: var(--chat-
|
|
225
|
-
.icon-button { flex: none; display: grid; place-items: center; width:
|
|
226
|
-
border: 1px solid var(--chat-border); border-radius:
|
|
312
|
+
.header { display: flex; align-items: center; gap: 8px; padding: 20px 24px 4px; }
|
|
313
|
+
.title { margin: 0 auto 0 0; font-size: 13px; font-weight: 600; letter-spacing: 0.16em;
|
|
314
|
+
text-transform: uppercase; color: var(--chat-accent); }
|
|
315
|
+
.icon-button { flex: none; display: grid; place-items: center; width: 40px; height: 40px; padding: 0;
|
|
316
|
+
border: 1px solid var(--chat-border); border-radius: var(--chat-round); background: var(--chat-surface);
|
|
227
317
|
color: var(--chat-text); cursor: pointer; }
|
|
228
318
|
.icon-button:hover { border-color: var(--chat-primary); }
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
.
|
|
319
|
+
/* Closing is the panel's one strong control, filled like the site's buttons. */
|
|
320
|
+
.close { background: var(--chat-primary); border-color: var(--chat-primary); color: var(--chat-on-primary); }
|
|
321
|
+
.close:hover { opacity: 0.9; }
|
|
322
|
+
.notice { margin: 0; padding: 0 24px 14px; font-size: 12px; color: var(--chat-muted); }
|
|
323
|
+
.messages { flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: 24px 24px 40px;
|
|
232
324
|
display: flex; flex-direction: column; gap: 16px; background: var(--chat-surface-soft);
|
|
233
325
|
container-type: inline-size; }
|
|
234
326
|
.turn { display: flex; flex-direction: column; }
|
|
235
327
|
.message { max-width: 88%; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
236
|
-
.message[data-role="visitor"] { align-self: flex-end; margin-left: 48px; padding:
|
|
237
|
-
border-radius: var(--chat-radius); background: var(--chat-secondary);
|
|
238
|
-
|
|
328
|
+
.message[data-role="visitor"] { align-self: flex-end; margin-left: 48px; padding: 12px 16px;
|
|
329
|
+
border-radius: calc(var(--chat-radius) * 0.75); background: var(--chat-secondary);
|
|
330
|
+
color: var(--chat-on-secondary); }
|
|
331
|
+
.message[data-role="assistant"] { align-self: flex-start; max-width: 100%; font-size: 16px; line-height: 1.6; }
|
|
332
|
+
.message strong { font-weight: 600; }
|
|
239
333
|
.ui { align-self: stretch; display: flex; flex-direction: column; gap: 12px; }
|
|
240
334
|
.thinking { display: flex; gap: 5px; padding: 4px 2px; }
|
|
241
|
-
.thinking i { width: 7px; height: 7px; border-radius: 999px; background: var(--chat-
|
|
335
|
+
.thinking i { width: 7px; height: 7px; border-radius: 999px; background: var(--chat-accent); opacity: 0.5; }
|
|
242
336
|
.dock { position: relative; padding: 10px 20px 16px; background: var(--chat-surface); }
|
|
243
337
|
.dock::before { content: ""; position: absolute; left: 0; right: 0; top: -28px; height: 28px;
|
|
244
338
|
pointer-events: none; background: linear-gradient(to top, var(--chat-surface-soft), transparent); }
|
|
245
339
|
.suggestions { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; margin: 0 -20px 10px;
|
|
246
340
|
padding: 0 20px; }
|
|
247
341
|
.suggestions::-webkit-scrollbar { display: none; }
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
342
|
+
/* Suggestions and quick replies: filled pills, an icon for the topic in the accent colour. */
|
|
343
|
+
.suggestion, .chip { flex: none; max-width: 100%; display: inline-flex; align-items: center; gap: 8px;
|
|
344
|
+
border: 1px solid transparent; border-radius: var(--chat-round); background: var(--chat-secondary);
|
|
345
|
+
color: var(--chat-on-secondary); padding: 8px 16px 8px 12px; font: inherit; font-size: 14px;
|
|
346
|
+
font-weight: 600; line-height: 1.35; text-align: left; white-space: nowrap; cursor: pointer; }
|
|
347
|
+
.suggestion svg, .chip svg { flex: none; color: var(--chat-accent); }
|
|
348
|
+
/* Suggestions scroll in one row; a quick reply wraps inside the message's width. */
|
|
349
|
+
.chip { white-space: normal; }
|
|
350
|
+
.suggestion:hover { border-color: var(--chat-primary); background: var(--chat-surface); color: var(--chat-text); }
|
|
253
351
|
@media (min-width: 1024px) {
|
|
254
352
|
.suggestions { flex-wrap: wrap; overflow: visible; margin: 0 0 10px; padding: 0; }
|
|
255
353
|
.suggestion { white-space: normal; }
|
|
256
354
|
}
|
|
257
355
|
.composer { display: flex; align-items: flex-end; gap: 8px; padding: 6px 6px 6px 18px;
|
|
258
|
-
border: 1px solid var(--chat-border); border-radius:
|
|
356
|
+
border: 1px solid var(--chat-border); border-radius: var(--chat-round); background: var(--chat-surface);
|
|
259
357
|
box-shadow: 0 4px 16px rgb(15 23 42 / 0.08); }
|
|
260
358
|
.composer:focus-within { border-color: var(--chat-text); }
|
|
261
359
|
/* 16px keeps iOS from zooming into the field. */
|
|
@@ -263,14 +361,19 @@ const CHAT_CSS = `
|
|
|
263
361
|
outline: none; background: transparent; padding: 6px 0; font: inherit; font-size: 16px;
|
|
264
362
|
line-height: 1.5; color: inherit; field-sizing: content; }
|
|
265
363
|
.send { flex: none; display: grid; place-items: center; width: 36px; height: 36px; padding: 0; border: 0;
|
|
266
|
-
border-radius:
|
|
364
|
+
border-radius: var(--chat-round); background: var(--chat-primary); color: var(--chat-on-primary); cursor: pointer; }
|
|
267
365
|
.send:disabled { opacity: 0.3; cursor: not-allowed; }
|
|
268
|
-
.consent { flex: 1; overflow-y: auto; padding: 24px
|
|
366
|
+
.consent { flex: 1; overflow-y: auto; padding: 24px; display: flex; flex-direction: column; gap: 14px;
|
|
269
367
|
background: var(--chat-surface-soft); }
|
|
270
368
|
.consent p { margin: 0; }
|
|
369
|
+
.consent-card { display: flex; flex-direction: column; align-items: flex-start; gap: 14px; padding: 18px 20px;
|
|
370
|
+
border: 1px solid var(--chat-border); border-radius: var(--chat-radius); background: var(--chat-surface);
|
|
371
|
+
font-size: 14px; color: var(--chat-muted); }
|
|
271
372
|
.pending { margin: 0; align-self: flex-end; max-width: 88%; padding: 10px 14px;
|
|
272
|
-
border-radius: var(--chat-radius); background: var(--chat-secondary);
|
|
273
|
-
|
|
373
|
+
border-radius: var(--chat-radius); background: var(--chat-secondary); color: var(--chat-on-secondary);
|
|
374
|
+
white-space: pre-wrap; }
|
|
375
|
+
/* The bubble's own text colour, softened: the muted grey suits the surface, not a set secondary. */
|
|
376
|
+
.pending figcaption { margin-bottom: 2px; color: inherit; opacity: 0.75; }
|
|
274
377
|
.fallback { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-bottom: 10px; }
|
|
275
378
|
.fallback p { margin: 0; }
|
|
276
379
|
|
|
@@ -282,9 +385,20 @@ const CHAT_CSS = `
|
|
|
282
385
|
.gallery::-webkit-scrollbar, .carousel::-webkit-scrollbar { display: none; }
|
|
283
386
|
.gallery img { flex: 0 0 100%; scroll-snap-align: start; display: block; width: 100%; aspect-ratio: 5 / 3;
|
|
284
387
|
height: auto; object-fit: cover; }
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
388
|
+
/* Dots say which photo shows; arrows come up under a pointer and on keyboard focus (a finger swipes:
|
|
389
|
+
on a touch screen they take no tap, so a tap on a photo's edge does not turn it unseen). */
|
|
390
|
+
.dots { position: absolute; left: 50%; bottom: 10px; translate: -50% 0; display: flex; gap: 4px; }
|
|
391
|
+
.dots i { width: 6px; height: 6px; border-radius: 999px; background: rgb(255 255 255 / 0.6);
|
|
392
|
+
box-shadow: 0 0 2px rgb(0 0 0 / 0.3); }
|
|
393
|
+
.dots i[data-current] { width: 14px; background: #fff; }
|
|
394
|
+
.gallery-arrow { position: absolute; top: 50%; translate: 0 -50%; display: grid; place-items: center;
|
|
395
|
+
width: 32px; height: 32px; padding: 0; border: 0; border-radius: var(--chat-round);
|
|
396
|
+
background: color-mix(in srgb, var(--chat-surface) 92%, transparent); color: var(--chat-text);
|
|
397
|
+
box-shadow: 0 2px 8px rgb(0 0 0 / 0.18); cursor: pointer; opacity: 0; }
|
|
398
|
+
.gallery-arrow[data-side="prev"] { left: 10px; }
|
|
399
|
+
.gallery-arrow[data-side="next"] { right: 10px; }
|
|
400
|
+
.gallery-wrap:hover .gallery-arrow:not(:disabled), .gallery-arrow:focus-visible { opacity: 1; }
|
|
401
|
+
@media (hover: none) { .gallery-arrow:not(:focus-visible) { pointer-events: none; } }
|
|
288
402
|
.carousel { gap: 12px; padding: 2px 2px 6px; }
|
|
289
403
|
.carousel > .card { flex: 0 0 86%; scroll-snap-align: start; }
|
|
290
404
|
/* Room for two: results side by side, all of them, instead of a row to swipe. */
|
|
@@ -294,7 +408,7 @@ const CHAT_CSS = `
|
|
|
294
408
|
}
|
|
295
409
|
.snap-nav { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 6px; }
|
|
296
410
|
.snap-nav button, .calendar-head button { border: 1px solid var(--chat-border); background: var(--chat-surface);
|
|
297
|
-
color: var(--chat-text); border-radius:
|
|
411
|
+
color: var(--chat-text); border-radius: var(--chat-round); width: 28px; height: 28px; cursor: pointer; font: inherit;
|
|
298
412
|
line-height: 1; }
|
|
299
413
|
.snap-nav button:disabled, .calendar-head button:disabled { opacity: 0.35; cursor: default; }
|
|
300
414
|
.rating { color: #b7791f; font-weight: 700; }
|
|
@@ -302,11 +416,9 @@ const CHAT_CSS = `
|
|
|
302
416
|
.breakdown .total { border-top: 1px solid var(--chat-border); padding-top: 6px; margin-top: 2px;
|
|
303
417
|
font-weight: 700; }
|
|
304
418
|
.alternatives, .quick-replies { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
305
|
-
.chip-link { border: 1px solid var(--chat-primary); color: var(--chat-primary); border-radius:
|
|
419
|
+
.chip-link { border: 1px solid var(--chat-primary); color: var(--chat-primary); border-radius: var(--chat-round);
|
|
306
420
|
padding: 5px 10px; text-decoration: none; font-size: 13px; }
|
|
307
|
-
.chip { border:
|
|
308
|
-
border-radius: 999px; padding: 7px 13px; font: inherit; font-size: 13px; cursor: pointer; }
|
|
309
|
-
.chip:hover { border-color: var(--chat-primary); background: var(--chat-surface); }
|
|
421
|
+
.chip:hover { border-color: var(--chat-primary); background: var(--chat-surface); color: var(--chat-text); }
|
|
310
422
|
.calendar { border: 1px solid var(--chat-border); border-radius: var(--chat-radius); padding: 12px;
|
|
311
423
|
background: var(--chat-surface); }
|
|
312
424
|
.calendar-head { display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
@@ -314,13 +426,15 @@ const CHAT_CSS = `
|
|
|
314
426
|
.calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; text-align: center;
|
|
315
427
|
font-size: 12px; }
|
|
316
428
|
.weekday { color: var(--chat-muted); font-weight: 600; }
|
|
317
|
-
.day { padding: 4px 0; border-radius: 6px; }
|
|
429
|
+
.day { padding: 4px 0; border-radius: min(6px, var(--chat-radius)); }
|
|
318
430
|
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
|
|
319
431
|
white-space: nowrap; }
|
|
320
|
-
.day[data-status="free"], .legend i[data-status="free"] {
|
|
321
|
-
|
|
432
|
+
.day[data-status="free"], .legend i[data-status="free"] {
|
|
433
|
+
background: color-mix(in srgb, var(--chat-primary) 14%, var(--chat-surface)); color: var(--chat-text); }
|
|
434
|
+
.day[data-status="booked"], .legend i[data-status="booked"] { background: var(--chat-surface-soft);
|
|
435
|
+
color: var(--chat-muted);
|
|
322
436
|
text-decoration: line-through; }
|
|
323
|
-
.day[data-status="closed"] { color:
|
|
437
|
+
.day[data-status="closed"] { color: var(--chat-muted); opacity: 0.6; }
|
|
324
438
|
.day[data-stay], .legend i[data-stay] { background: var(--chat-primary); color: var(--chat-on-primary);
|
|
325
439
|
text-decoration: none; }
|
|
326
440
|
.legend { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 8px; font-size: 12px; }
|
|
@@ -335,12 +449,26 @@ const CHAT_CSS = `
|
|
|
335
449
|
.compare-col dl { margin: 0; display: flex; flex-direction: column; gap: 2px; flex: 1; }
|
|
336
450
|
.compare-col dt { color: var(--chat-muted); font-size: 11px; margin-top: 4px; }
|
|
337
451
|
.compare-col dd { margin: 0; overflow-wrap: anywhere; }
|
|
338
|
-
.card-body { padding:
|
|
339
|
-
.card-title { margin: 0; font-size:
|
|
452
|
+
.card-body { padding: 16px 18px 12px; display: flex; flex-direction: column; gap: 6px; }
|
|
453
|
+
.card-title { margin: 0; font-size: 18px; font-weight: 500; line-height: 1.3; }
|
|
454
|
+
/* A rental card: place and score, name, facts and wishes; the stay's price and button below a line. */
|
|
455
|
+
.rental-card, .stay-card { display: flex; flex-direction: column; }
|
|
456
|
+
.rental-card .card-body { flex: 1; }
|
|
457
|
+
.card-meta { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 24px; }
|
|
458
|
+
.place { color: var(--chat-accent); font-size: 14px; }
|
|
459
|
+
.score-chip { margin-left: auto; padding: 2px 8px; border-radius: calc(var(--chat-radius) * 0.4);
|
|
460
|
+
background: var(--chat-secondary); color: var(--chat-on-secondary); font-size: 12px; font-weight: 700; }
|
|
461
|
+
.specs { margin: 0; color: var(--chat-muted); font-size: 14px; }
|
|
462
|
+
.card-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px;
|
|
463
|
+
margin: 0 18px; padding: 12px 0 16px; border-top: 1px solid var(--chat-border); }
|
|
464
|
+
.foot-price { display: flex; flex-direction: column; min-width: 0; }
|
|
465
|
+
.foot-price .price { font-size: 18px; line-height: 1.3; }
|
|
466
|
+
.foot-meta { color: var(--chat-muted); font-size: 12px; }
|
|
467
|
+
.button.compact { margin: 0 0 0 auto; padding: 9px 20px; font-size: 14px; white-space: nowrap; }
|
|
340
468
|
.muted { color: var(--chat-muted); font-size: 13px; }
|
|
341
469
|
.price { font-weight: 700; }
|
|
342
470
|
.button { display: inline-block; margin-top: 6px; text-align: center; text-decoration: none;
|
|
343
|
-
padding: 10px 16px; border-radius:
|
|
471
|
+
padding: 10px 16px; border-radius: var(--chat-round); background: var(--chat-primary); color: var(--chat-on-primary);
|
|
344
472
|
border: 0; cursor: pointer; font: inherit; font-weight: 600; }
|
|
345
473
|
.link { color: var(--chat-primary); }
|
|
346
474
|
/* A link out of the chat while a reply still streams (ReplyLink). */
|
|
@@ -348,12 +476,13 @@ const CHAT_CSS = `
|
|
|
348
476
|
|
|
349
477
|
/* Wishes a found stay matches: a badge on the photo, the wishes ticked under the name. */
|
|
350
478
|
.card-media { position: relative; }
|
|
351
|
-
.badge { position: absolute; left:
|
|
352
|
-
background: var(--chat-surface); color: var(--chat-
|
|
479
|
+
.badge { position: absolute; left: 12px; top: 12px; z-index: 1; padding: 5px 12px; border-radius: var(--chat-round);
|
|
480
|
+
background: var(--chat-surface); color: var(--chat-accent); font-size: 12px; font-weight: 600;
|
|
353
481
|
box-shadow: var(--chat-card-shadow); }
|
|
354
|
-
.wishes { list-style: none; margin:
|
|
355
|
-
|
|
356
|
-
|
|
482
|
+
.wishes { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
|
|
483
|
+
.wishes li { display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px 4px 10px;
|
|
484
|
+
border: 1px solid var(--chat-border); border-radius: var(--chat-round); font-size: 13px; font-weight: 600; }
|
|
485
|
+
.wishes svg { color: var(--chat-accent); }
|
|
357
486
|
|
|
358
487
|
/* The price calendar: a column per arrival, the bar's height its total. */
|
|
359
488
|
.price-columns { list-style: none; margin: 10px 0 0; padding: 0; display: grid; grid-auto-flow: column;
|
|
@@ -365,7 +494,8 @@ const CHAT_CSS = `
|
|
|
365
494
|
.price-column[data-free]:not(:disabled):hover { border-color: var(--chat-primary); }
|
|
366
495
|
.price-column[data-asked] { border-color: var(--chat-border); background: var(--chat-surface-soft); }
|
|
367
496
|
.bar-track { display: flex; align-items: flex-end; justify-content: center; width: 100%; height: 72px; }
|
|
368
|
-
.bar { width: 60%; border-radius: 6px 6px
|
|
497
|
+
.bar { width: 60%; border-radius: min(6px, var(--chat-radius)) min(6px, var(--chat-radius)) 0 0;
|
|
498
|
+
background: color-mix(in srgb, var(--chat-primary) 30%, var(--chat-surface)); }
|
|
369
499
|
.price-column[data-cheapest] .bar, .legend i[data-cheapest] { background: var(--chat-primary); }
|
|
370
500
|
.legend i[data-asked] { background: var(--chat-surface-soft); border: 1px solid var(--chat-border); }
|
|
371
501
|
.price-text { font-size: 12px; font-weight: 700; white-space: nowrap; }
|
|
@@ -398,7 +528,7 @@ const CHAT_CSS = `
|
|
|
398
528
|
.map-attribution { margin: 0; padding: 2px 8px; font-size: 10px; color: var(--chat-muted); text-align: right; }
|
|
399
529
|
.map-attribution a { color: inherit; }
|
|
400
530
|
.map-pin { display: block; width: 18px; height: 18px; border-radius: 999px; background: var(--chat-primary);
|
|
401
|
-
border: 3px solid
|
|
531
|
+
border: 3px solid var(--chat-surface); box-shadow: 0 2px 6px rgb(0 0 0 / 0.35); }
|
|
402
532
|
.maplibregl-map { position: relative; overflow: hidden; }
|
|
403
533
|
.maplibregl-canvas-container, .maplibregl-canvas { position: absolute; left: 0; top: 0; }
|
|
404
534
|
/* The canvas takes keyboard focus: a ring for the keyboard, none for a click or a tap. */
|
|
@@ -406,13 +536,13 @@ const CHAT_CSS = `
|
|
|
406
536
|
.maplibregl-canvas:focus-visible { outline: 2px solid var(--chat-primary); outline-offset: -2px; }
|
|
407
537
|
.maplibregl-marker { position: absolute; top: 0; left: 0; will-change: transform; }
|
|
408
538
|
|
|
409
|
-
[data-theme="soft"] { --chat-radius:
|
|
539
|
+
[data-theme="soft"] { --chat-radius: calc(var(--chat-base-radius) * 2.4); --chat-shadow: 0 12px 32px rgb(15 23 42 / 0.12);
|
|
410
540
|
--chat-card-shadow: 0 4px 18px rgb(15 23 42 / 0.07); }
|
|
411
|
-
[data-theme="minimal"] { --chat-radius:
|
|
541
|
+
[data-theme="minimal"] { --chat-radius: calc(var(--chat-base-radius) * 0.6); --chat-shadow: none; --chat-card-shadow: none; }
|
|
412
542
|
[data-theme="minimal"] .panel, [data-theme="minimal"] .pill, [data-theme="minimal"] .card,
|
|
413
543
|
[data-theme="minimal"] .compare-col { border: 1px solid var(--chat-border); }
|
|
414
|
-
|
|
415
|
-
[data-theme="minimal"]
|
|
544
|
+
/* No pills in minimal: every control has its tight corners. */
|
|
545
|
+
[data-theme="minimal"] { --chat-round: var(--chat-radius); }
|
|
416
546
|
|
|
417
547
|
@keyframes chat-spin { to { transform: rotate(1turn); } }
|
|
418
548
|
@keyframes chat-in { from { opacity: 0; transform: translateY(16px); } }
|
|
@@ -436,7 +566,7 @@ const CHAT_CSS = `
|
|
|
436
566
|
`;
|
|
437
567
|
//#endregion
|
|
438
568
|
//#region src/WebsiteChat.tsx
|
|
439
|
-
const Panel = lazy(() => import("./panel-
|
|
569
|
+
const Panel = lazy(() => import("./panel-hLRBoh8t.js"));
|
|
440
570
|
/** See {@link WebsiteChatComponent}. */
|
|
441
571
|
const WebsiteChat = function WebsiteChat(props) {
|
|
442
572
|
const { endpoint, locale, theme = "classic", position = "right" } = props;
|
|
@@ -445,6 +575,7 @@ const WebsiteChat = function WebsiteChat(props) {
|
|
|
445
575
|
const field = useRef(null);
|
|
446
576
|
const wasOpen = useRef(false);
|
|
447
577
|
const [root, setRoot] = useState(null);
|
|
578
|
+
const [replaced, setReplaced] = useState({});
|
|
448
579
|
const [available, setAvailable] = useState(false);
|
|
449
580
|
const [mounted, setMounted] = useState(false);
|
|
450
581
|
const [open, setOpen] = useState(false);
|
|
@@ -483,6 +614,7 @@ const WebsiteChat = function WebsiteChat(props) {
|
|
|
483
614
|
useEffect(() => {
|
|
484
615
|
const element = host.current;
|
|
485
616
|
if (!available || !element) return;
|
|
617
|
+
setReplaced(unusableSiteTokens(element));
|
|
486
618
|
setRoot(element.shadowRoot ?? element.attachShadow({ mode: "open" }));
|
|
487
619
|
}, [available]);
|
|
488
620
|
const submitEntry = (event) => {
|
|
@@ -494,7 +626,10 @@ const WebsiteChat = function WebsiteChat(props) {
|
|
|
494
626
|
};
|
|
495
627
|
return /* @__PURE__ */ jsx("div", {
|
|
496
628
|
ref: host,
|
|
497
|
-
style:
|
|
629
|
+
style: {
|
|
630
|
+
...replaced,
|
|
631
|
+
...colorVariables(props.colors)
|
|
632
|
+
},
|
|
498
633
|
children: root && available && createPortal(/* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("style", { children: CHAT_CSS }), /* @__PURE__ */ jsxs("div", {
|
|
499
634
|
className: "chat",
|
|
500
635
|
"data-theme": theme,
|
|
@@ -503,7 +638,7 @@ const WebsiteChat = function WebsiteChat(props) {
|
|
|
503
638
|
fallback: null,
|
|
504
639
|
children: /* @__PURE__ */ jsx(Panel, {
|
|
505
640
|
endpoint,
|
|
506
|
-
clientVersion: "1.0
|
|
641
|
+
clientVersion: "1.1.0",
|
|
507
642
|
locale,
|
|
508
643
|
copy,
|
|
509
644
|
searchPath: props.searchPath,
|