@forgebuild/hotal-ui 0.23.40 → 0.23.80
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/dist/hotal-ui.js +17706 -8394
- package/dist/index.d.ts +281 -2
- package/package.json +3 -1
package/dist/index.d.ts
CHANGED
|
@@ -111,9 +111,16 @@ export interface BookingFlowElement extends HTMLElement {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
export interface MenuItem {
|
|
114
|
+
/** Needed to add this item to a cart (see MenuSectionElement.cartEnabled)
|
|
115
|
+
* -- an item missing this never gets a quantity stepper. */
|
|
116
|
+
id?: string;
|
|
114
117
|
name: string;
|
|
115
118
|
description?: string;
|
|
116
119
|
price?: string;
|
|
120
|
+
/** Numeric price for cart math, independent of `price` above (which may
|
|
121
|
+
* be an arbitrary display override a cart can't add up) -- like `id`,
|
|
122
|
+
* required for this item to get a stepper. */
|
|
123
|
+
priceValue?: number;
|
|
117
124
|
tagline?: string;
|
|
118
125
|
group?: string;
|
|
119
126
|
country?: string;
|
|
@@ -125,6 +132,18 @@ export interface MenuCategory {
|
|
|
125
132
|
items: MenuItem[];
|
|
126
133
|
}
|
|
127
134
|
|
|
135
|
+
export interface MenuCartItem {
|
|
136
|
+
id: string;
|
|
137
|
+
name: string;
|
|
138
|
+
price: number;
|
|
139
|
+
quantity: number;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface MenuSectionCartChangeEventDetail {
|
|
143
|
+
items: MenuCartItem[];
|
|
144
|
+
total: number;
|
|
145
|
+
}
|
|
146
|
+
|
|
128
147
|
export interface MenuInfoItem {
|
|
129
148
|
label: string;
|
|
130
149
|
value: string;
|
|
@@ -156,6 +175,16 @@ export interface MenuSectionElement extends HTMLElement {
|
|
|
156
175
|
countryFilter?: boolean;
|
|
157
176
|
groupFilterAllLabel?: string;
|
|
158
177
|
countryFilterAllLabel?: string;
|
|
178
|
+
/** Adds a +/- stepper next to any item with both `id` and `priceValue`
|
|
179
|
+
* (classic variant only, not `panel`) and emits `cart-change` on every
|
|
180
|
+
* change. Off by default -- most hosts use this purely as a read-only
|
|
181
|
+
* display. */
|
|
182
|
+
cartEnabled?: boolean;
|
|
183
|
+
/** Restore cart quantities, e.g. from localStorage: [{ id, quantity }].
|
|
184
|
+
* Reactive, not mount-only -- reassign it (a new array) to re-sync
|
|
185
|
+
* quantities a host changed elsewhere, same as
|
|
186
|
+
* ProshopCatalogueElement's own `initial`. */
|
|
187
|
+
initial?: { id: string; quantity: number }[];
|
|
159
188
|
}
|
|
160
189
|
|
|
161
190
|
export interface EventInquiry {
|
|
@@ -366,6 +395,10 @@ export interface SpaBookingWidgetElement extends HTMLElement {
|
|
|
366
395
|
/** Shown as a "Staff" row in the summary panel once a service is picked.
|
|
367
396
|
* Purely presentational -- there is no staff picker. Omit to hide the row. */
|
|
368
397
|
staffLabel?: string;
|
|
398
|
+
/** Hides every price display (service list, summary rows, total) -- for
|
|
399
|
+
* a free service (e.g. a consultation call) where showing "£0.00"
|
|
400
|
+
* throughout would read oddly. */
|
|
401
|
+
hidePrice?: boolean;
|
|
369
402
|
/** JS `Date#getDay()` values (0 = Sunday) to gray out on the calendar,
|
|
370
403
|
* e.g. `[2]` for a Tuesday closure. Visual hint only -- fetchAvailability
|
|
371
404
|
* is the real source of truth for what's bookable. */
|
|
@@ -698,6 +731,64 @@ export interface LiveDiningOrdersFeedElement extends HTMLElement {
|
|
|
698
731
|
filterBookingId?: string;
|
|
699
732
|
}
|
|
700
733
|
|
|
734
|
+
export interface ShopOrderItem {
|
|
735
|
+
id?: string;
|
|
736
|
+
item_id?: string;
|
|
737
|
+
item_name: string;
|
|
738
|
+
quantity: number;
|
|
739
|
+
unit_price?: string | number;
|
|
740
|
+
total?: string | number;
|
|
741
|
+
/** e.g. size/color ("M / Navy") for apparel-style items. */
|
|
742
|
+
variant?: string | null;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
export interface ShopOrder {
|
|
746
|
+
id: string;
|
|
747
|
+
booking_id?: string | null;
|
|
748
|
+
guest_id?: string | null;
|
|
749
|
+
status: string;
|
|
750
|
+
notes?: string | null;
|
|
751
|
+
fulfillment_method?: 'pickup' | 'shipping' | null;
|
|
752
|
+
total_price: string | number;
|
|
753
|
+
created_at: string;
|
|
754
|
+
items: ShopOrderItem[];
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
export interface ShopOrderBookingRef {
|
|
758
|
+
id: string;
|
|
759
|
+
room_number?: string | null;
|
|
760
|
+
guest_name?: string | null;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
export interface LiveShopOrdersFeedElement extends HTMLElement {
|
|
764
|
+
heading?: string;
|
|
765
|
+
channelName?: string;
|
|
766
|
+
ablyAuthUrl?: string;
|
|
767
|
+
/** Fetches one page of order history, newest-first; Ably only delivers
|
|
768
|
+
* events published after the subscription starts, so history has to
|
|
769
|
+
* come from a REST call. Omit `cursor` for the first page; each
|
|
770
|
+
* subsequent call passes the `created_at` of the oldest loaded order so
|
|
771
|
+
* only the next page is fetched. Return fewer than `limit` items to
|
|
772
|
+
* signal there's nothing older left. */
|
|
773
|
+
fetchOrders?: (params: { cursor?: string; limit: number }) => Promise<ShopOrder[]>;
|
|
774
|
+
/** Resolves booking_id -> room/guest, since the order payload carries
|
|
775
|
+
* only the id. */
|
|
776
|
+
bookings?: ShopOrderBookingRef[];
|
|
777
|
+
maxItems?: number;
|
|
778
|
+
/** Set false to stop fetching further pages once the first one loads --
|
|
779
|
+
* disables the scroll-to-bottom sentinel entirely rather than swapping
|
|
780
|
+
* in a manual button. Default true. */
|
|
781
|
+
autoLoadMore?: boolean;
|
|
782
|
+
emptyLabel?: string;
|
|
783
|
+
/** Symbol prefixed to money amounts ('€', '$', ...); the payload carries
|
|
784
|
+
* no currency of its own. Default empty (bare amount). */
|
|
785
|
+
currency?: string;
|
|
786
|
+
/** Restricts the feed to one booking's orders -- `fetchOrders` should
|
|
787
|
+
* already be scoped server-side; this only filters the live `new-order`
|
|
788
|
+
* events, which otherwise arrive unfiltered off the channel. */
|
|
789
|
+
filterBookingId?: string;
|
|
790
|
+
}
|
|
791
|
+
|
|
701
792
|
export interface VenueCarouselSlide {
|
|
702
793
|
key: string;
|
|
703
794
|
src: string;
|
|
@@ -957,6 +1048,9 @@ export interface BookingCheckoutElement extends HTMLElement {
|
|
|
957
1048
|
locations?: BookingCheckoutLocation[];
|
|
958
1049
|
defaultPhoneCode?: string;
|
|
959
1050
|
allowBookingForOther?: boolean;
|
|
1051
|
+
/** Hides the "Booking for myself/someone else" toggle entirely (not just
|
|
1052
|
+
* the "other" option) -- for checkouts that aren't a booking at all. */
|
|
1053
|
+
showBookingFor?: boolean;
|
|
960
1054
|
termsHref?: string;
|
|
961
1055
|
prefill?: BookingCheckoutPrefill;
|
|
962
1056
|
resolveInitialLocation?: () => Promise<BookingCheckoutResolvedLocation | null>;
|
|
@@ -1161,6 +1255,11 @@ export interface GoogleReviewItem {
|
|
|
1161
1255
|
text: string;
|
|
1162
1256
|
relative_time?: string;
|
|
1163
1257
|
published_at?: string | null;
|
|
1258
|
+
/** Photos the reviewer attached to their review (not the same as
|
|
1259
|
+
* author_photo, which is their profile picture). Only ever populated
|
|
1260
|
+
* when the host's fetchReviews is backed by SerpApi's Google Maps
|
|
1261
|
+
* Reviews API -- Google's own official Places API never exposes these. */
|
|
1262
|
+
photos?: string[] | null;
|
|
1164
1263
|
}
|
|
1165
1264
|
|
|
1166
1265
|
/** The payload <google-reviews> renders -- the shape served by the OTA's
|
|
@@ -1188,17 +1287,194 @@ export interface GoogleReviewsStrings {
|
|
|
1188
1287
|
* `loaded` ({ rating, count }) when the payload arrives. Theme via --gr-*. */
|
|
1189
1288
|
export interface GoogleReviewsElement extends HTMLElement {
|
|
1190
1289
|
/** 'grid' (default) or 'slider' (scroll-snap track with prev/next arrows). */
|
|
1191
|
-
variant?: 'grid' | 'slider';
|
|
1192
|
-
/** Slider only: seconds between automatic advances; 0/unset = off. */
|
|
1290
|
+
variant?: 'grid' | 'slider';
|
|
1291
|
+
/** Slider only: seconds between automatic advances; 0/unset = off. */
|
|
1193
1292
|
autoAdvance?: number;
|
|
1194
1293
|
maxReviews?: number;
|
|
1294
|
+
/** Sweeps a light band across the CTA on hover instead of the default
|
|
1295
|
+
* opacity dip. Off by default. */
|
|
1296
|
+
ctaShimmer?: boolean;
|
|
1195
1297
|
strings?: Partial<GoogleReviewsStrings>;
|
|
1196
1298
|
fetchReviews?: () => Promise<GoogleReviewsPayload>;
|
|
1197
1299
|
}
|
|
1198
1300
|
|
|
1301
|
+
export interface TripadvisorReviewItem {
|
|
1302
|
+
author: string;
|
|
1303
|
+
author_photo?: string | null;
|
|
1304
|
+
rating: number;
|
|
1305
|
+
text: string;
|
|
1306
|
+
relative_time?: string;
|
|
1307
|
+
published_at?: string | null;
|
|
1308
|
+
/** Photos the reviewer attached to their review. */
|
|
1309
|
+
photos?: string[] | null;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
/** The payload <tripadvisor-reviews> renders. Same shape as
|
|
1313
|
+
* GoogleReviewsPayload (maps_url/write_review_url renamed to place_url,
|
|
1314
|
+
* since Tripadvisor has no equivalent one-click write-review deep link). */
|
|
1315
|
+
export interface TripadvisorReviewsPayload {
|
|
1316
|
+
rating: number | null;
|
|
1317
|
+
count: number;
|
|
1318
|
+
place_url?: string | null;
|
|
1319
|
+
write_review_url?: string | null;
|
|
1320
|
+
reviews: TripadvisorReviewItem[];
|
|
1321
|
+
fetched_at?: string;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
export interface TripadvisorReviewsStrings {
|
|
1325
|
+
reviewsLabel: string;
|
|
1326
|
+
ctaLabel: string;
|
|
1327
|
+
seeAllLabel: string;
|
|
1328
|
+
loadingLabel: string;
|
|
1329
|
+
errorLabel: string;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
/** <tripadvisor-reviews>: live Tripadvisor rating, count, and the latest
|
|
1333
|
+
* review cards. Same shape and behaviour as <google-reviews> (see its own
|
|
1334
|
+
* doc comment) -- a separate component rather than a shared one because
|
|
1335
|
+
* each review source gets its own summary doc, seed script, and branding.
|
|
1336
|
+
* Theme via --ta-*. */
|
|
1337
|
+
export interface TripadvisorReviewsElement extends HTMLElement {
|
|
1338
|
+
/** 'grid' (default) or 'slider' (scroll-snap track with prev/next arrows). */
|
|
1339
|
+
variant?: 'grid' | 'slider';
|
|
1340
|
+
/** Slider only: seconds between automatic advances; 0/unset = off. */
|
|
1341
|
+
autoAdvance?: number;
|
|
1342
|
+
maxReviews?: number;
|
|
1343
|
+
/** Sweeps a light band across the CTA on hover instead of the default
|
|
1344
|
+
* opacity dip. Off by default. */
|
|
1345
|
+
ctaShimmer?: boolean;
|
|
1346
|
+
strings?: Partial<TripadvisorReviewsStrings>;
|
|
1347
|
+
fetchReviews?: () => Promise<TripadvisorReviewsPayload>;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
/** <ai-chat-widget>: a floating chat launcher + panel that talks to a
|
|
1351
|
+
* host-provided chat API endpoint (any AI SDK v5+ UI-message-stream route).
|
|
1352
|
+
* Knows nothing about the model, tools, or business data behind `api` --
|
|
1353
|
+
* purely a chat UI. Dispatches `open-booking` ({ service: string | null })
|
|
1354
|
+
* when the model calls a client-side `openBooking` tool, and
|
|
1355
|
+
* `lead-captured` ({ name: string }) once a server-side `captureLead` tool
|
|
1356
|
+
* call completes. Theme via --acw-*. */
|
|
1357
|
+
export interface AiChatWidgetElement extends HTMLElement {
|
|
1358
|
+
/** The chat API endpoint (e.g. "/api/chat"). Required. */
|
|
1359
|
+
api: string;
|
|
1360
|
+
heading?: string;
|
|
1361
|
+
placeholder?: string;
|
|
1362
|
+
/** Shown as the first message in an empty conversation. Omit for none. */
|
|
1363
|
+
greeting?: string;
|
|
1364
|
+
/** Shown in the error message so a visitor can call instead. Omit to leave it out. */
|
|
1365
|
+
phone?: string;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
/** One purchasable option within a variant group -- a real, distinct
|
|
1369
|
+
* catalogue item (own id, own price, own stock on the host's backend), just
|
|
1370
|
+
* presented as a pill under the group's single card instead of a separate
|
|
1371
|
+
* card of its own. */
|
|
1372
|
+
export interface ProshopVariant {
|
|
1373
|
+
id: string;
|
|
1374
|
+
/** e.g. "Blue", "Small" -- shown on the pill. */
|
|
1375
|
+
label: string;
|
|
1376
|
+
price: number | null;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
export interface ProshopItem {
|
|
1380
|
+
id: string;
|
|
1381
|
+
name: string;
|
|
1382
|
+
description?: string;
|
|
1383
|
+
category?: string;
|
|
1384
|
+
image?: string | null;
|
|
1385
|
+
price: number | null;
|
|
1386
|
+
/** When set, the photo + name link here (e.g. a product detail page).
|
|
1387
|
+
* Omit for a catalogue with no such page -- the card stays static. */
|
|
1388
|
+
href?: string | null;
|
|
1389
|
+
/** Renders this card as a variant group: a pill per entry (e.g. colors or
|
|
1390
|
+
* sizes) beneath the description, switching which real item id/price the
|
|
1391
|
+
* qty stepper and cart operate on. `id`/`price` above are only the
|
|
1392
|
+
* initial (first-variant) display values. Each entry is a genuinely
|
|
1393
|
+
* separate catalogue item, so per-variant stock still works with no
|
|
1394
|
+
* other change -- the host just supplies N real items grouped under one
|
|
1395
|
+
* display card instead of N separate cards. */
|
|
1396
|
+
variants?: ProshopVariant[];
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
export interface ProshopCartItem {
|
|
1400
|
+
id: string;
|
|
1401
|
+
name: string;
|
|
1402
|
+
price: number;
|
|
1403
|
+
quantity: number;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
export interface ProshopCatalogueChangeEventDetail {
|
|
1407
|
+
items: ProshopCartItem[];
|
|
1408
|
+
total: number;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
/** <proshop-catalogue>: browse + cart-build a shop/pro-shop catalogue (merch,
|
|
1412
|
+
* equipment, etc.) -- category tabs, a card grid (photo/name/price/qty
|
|
1413
|
+
* stepper), and a sticky cart-total bar. Stops at the cart on purpose:
|
|
1414
|
+
* checkout (contact details + Stripe payment) is <booking-checkout>'s job --
|
|
1415
|
+
* feed this component's `cart-change` total/items into that one rather than
|
|
1416
|
+
* building a second payment flow. Emits `cart-change` on every quantity
|
|
1417
|
+
* change. Theme via --pc-*. */
|
|
1418
|
+
export interface ProshopCatalogueElement extends HTMLElement {
|
|
1419
|
+
items?: ProshopItem[];
|
|
1420
|
+
currency?: string;
|
|
1421
|
+
/** Explicit category display order; categories not listed here are
|
|
1422
|
+
* appended in first-seen order. Omit to use first-seen order only. */
|
|
1423
|
+
categoryOrder?: string[];
|
|
1424
|
+
/** Overrides the auto-generated label for a category key -- otherwise a
|
|
1425
|
+
* category key is shown verbatim. */
|
|
1426
|
+
categoryLabels?: Record<string, string>;
|
|
1427
|
+
/** Restore cart quantities, e.g. from localStorage: [{ id, quantity }].
|
|
1428
|
+
* Reactive, not mount-only -- reassign it (a new array) after mount to
|
|
1429
|
+
* re-sync quantities a host changed elsewhere, e.g. a checkout sidebar. */
|
|
1430
|
+
initial?: { id: string; quantity: number }[];
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
export interface InstagramFeedPost {
|
|
1434
|
+
id: string;
|
|
1435
|
+
image: string;
|
|
1436
|
+
caption?: string | null;
|
|
1437
|
+
permalink?: string | null;
|
|
1438
|
+
likeCount?: number | null;
|
|
1439
|
+
commentCount?: number | null;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
export interface InstagramFeedStrings {
|
|
1443
|
+
followersLabel: string;
|
|
1444
|
+
viewProfileLabel: string;
|
|
1445
|
+
emptyLabel: string;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
/** <instagram-feed>: a curated grid/slider of Instagram posts plus a
|
|
1449
|
+
* follower-count header and "View profile" link. Unlike GoogleReviews/
|
|
1450
|
+
* TripadvisorReviews, `posts` arrives as a plain prop, not a fetchX
|
|
1451
|
+
* callback -- there's no live per-request fetch to make, so no loading/
|
|
1452
|
+
* error state. Same slider mechanics (continuous loop, auto-advance,
|
|
1453
|
+
* pause on hover/focus) as those two for consistency. No heading/eyebrow
|
|
1454
|
+
* of its own -- that's host-page copy, same split as <google-reviews>.
|
|
1455
|
+
* Theme via --if-*. */
|
|
1456
|
+
export interface InstagramFeedElement extends HTMLElement {
|
|
1457
|
+
posts?: InstagramFeedPost[];
|
|
1458
|
+
/** Shown as the header stat, e.g. "4.3K Followers". Omit to hide the stat entirely. */
|
|
1459
|
+
followers?: number;
|
|
1460
|
+
/** The real instagram.com/<handle> URL -- the "View profile" link, and a
|
|
1461
|
+
* post's link fallback when it has no permalink of its own. */
|
|
1462
|
+
profileUrl?: string;
|
|
1463
|
+
/** 'grid' (default) or 'slider' (scroll-snap track with prev/next arrows). */
|
|
1464
|
+
variant?: 'grid' | 'slider';
|
|
1465
|
+
/** Slider only: seconds between automatic advances; 0/unset = off. */
|
|
1466
|
+
autoAdvance?: number;
|
|
1467
|
+
/** Cap on rendered post tiles. Default 12. */
|
|
1468
|
+
maxPosts?: number;
|
|
1469
|
+
strings?: Partial<InstagramFeedStrings>;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1199
1472
|
declare global {
|
|
1200
1473
|
interface HTMLElementTagNameMap {
|
|
1474
|
+
'ai-chat-widget': AiChatWidgetElement;
|
|
1201
1475
|
'google-reviews': GoogleReviewsElement;
|
|
1476
|
+
'tripadvisor-reviews': TripadvisorReviewsElement;
|
|
1477
|
+
'instagram-feed': InstagramFeedElement;
|
|
1202
1478
|
'service-booking-bar': ServiceBookingBarElement;
|
|
1203
1479
|
'reservation-bar': ReservationBarElement;
|
|
1204
1480
|
'booking-flow': BookingFlowElement;
|
|
@@ -1212,6 +1488,7 @@ declare global {
|
|
|
1212
1488
|
'live-spa-bookings-feed': LiveSpaBookingsFeedElement;
|
|
1213
1489
|
'live-golf-bookings-feed': LiveGolfBookingsFeedElement;
|
|
1214
1490
|
'live-dining-orders-feed': LiveDiningOrdersFeedElement;
|
|
1491
|
+
'live-shop-orders-feed': LiveShopOrdersFeedElement;
|
|
1215
1492
|
'venue-carousel': VenueCarouselElement;
|
|
1216
1493
|
'room-grid': RoomGridElement;
|
|
1217
1494
|
'island-map': IslandMapElement;
|
|
@@ -1228,6 +1505,7 @@ declare global {
|
|
|
1228
1505
|
'searchable-select': SearchableSelectElement;
|
|
1229
1506
|
'booking-confirmed': BookingConfirmedElement;
|
|
1230
1507
|
'restaurant-order-form': RestaurantOrderFormElement;
|
|
1508
|
+
'proshop-catalogue': ProshopCatalogueElement;
|
|
1231
1509
|
}
|
|
1232
1510
|
|
|
1233
1511
|
interface HTMLElementEventMap {
|
|
@@ -1248,6 +1526,7 @@ declare global {
|
|
|
1248
1526
|
'checkout-confirmed': CustomEvent<{ ref?: string }>;
|
|
1249
1527
|
ready: CustomEvent<undefined>;
|
|
1250
1528
|
'extras-change': CustomEvent<ExtrasCatalogueChangeEventDetail>;
|
|
1529
|
+
'cart-change': CustomEvent<ProshopCatalogueChangeEventDetail>;
|
|
1251
1530
|
'order-placed': CustomEvent<RestaurantOrderPlacedEventDetail>;
|
|
1252
1531
|
'date-filter-change': CustomEvent<LiveReservationsFeedDateFilterEventDetail>;
|
|
1253
1532
|
// Not declared here: `searchable-select` dispatches a CustomEvent named
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgebuild/hotal-ui",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.80",
|
|
4
4
|
"description": "Reusable Svelte UI components for booking and reservation experiences.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,9 +20,11 @@
|
|
|
20
20
|
"access": "public"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
+
"@ai-sdk/svelte": "^5.0.91",
|
|
23
24
|
"@floating-ui/dom": "^1.8.0",
|
|
24
25
|
"@stripe/stripe-js": "^9.13.0",
|
|
25
26
|
"ably": "^2.27.0",
|
|
27
|
+
"ai": "^7.0.91",
|
|
26
28
|
"gsap": "^3.15.0",
|
|
27
29
|
"libphonenumber-js": "^1.12.41",
|
|
28
30
|
"svelte": "^5.56.8"
|