@sonordev/site-kit 7.1.2 → 7.2.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/AGENTS.md +1 -1
- package/CHANGELOG.md +51 -0
- package/README.md +9 -8
- package/agent-manifest.json +12 -4
- package/dist/{AnalyticsProvider-ZB33W6MC.js → AnalyticsProvider-SLFFBRPE.js} +4 -4
- package/dist/{ArticleViewTracker-GMHYLLLZ.js → ArticleViewTracker-TM5AMGN2.js} +3 -3
- package/dist/{BlocksPopup-EA6LYL25.js → BlocksPopup-YJNJIVPO.js} +5 -5
- package/dist/ChatWidget-XKOSH66S.js +17 -0
- package/dist/{FileField-QKA3JB2Q.js → FileField-CI4UL5SH.js} +3 -3
- package/dist/{FormSpotlight-AQTIA5TP.js → FormSpotlight-F2AAEGBN.js} +20 -6
- package/dist/{FormStage-FDTLTP3B.js → FormStage-BI5R5CNC.js} +27 -7
- package/dist/ManagedForm-G6COGHXL.js +16 -0
- package/dist/{ManagedNewsletterForm-J6QSAGGP.js → ManagedNewsletterForm-BO3WCGXX.js} +7 -5
- package/dist/{SignalCore-JZNXOLNT.js → SignalCore-ZA6WHKBR.js} +3 -3
- package/dist/SiteChat-YJADDTKG.js +5 -0
- package/dist/{SiteDesignReporter-W4EJUSTE.js → SiteDesignReporter-5YAW36AQ.js} +5 -5
- package/dist/SitePopups-ST5PMKZE.js +10 -0
- package/dist/SitemapSync-RJTEBINX.js +8 -0
- package/dist/_client/booking-widget.js +5 -5
- package/dist/affiliates/index.js +3 -3
- package/dist/analytics/index.js +4 -4
- package/dist/analytics/send-gate.d.ts +1 -1
- package/dist/articles/index.js +2 -2
- package/dist/articles/server-ui.js +3 -2
- package/dist/articles/server.js +2 -1
- package/dist/{engage → chat}/ChatWidget.d.ts +7 -6
- package/dist/{engage → chat}/EchoUiActions.d.ts +1 -1
- package/dist/chat/SiteChat.d.ts +25 -0
- package/dist/{engage → chat}/brand-color.d.ts +1 -1
- package/dist/{engage → chat}/chat-messages.d.ts +1 -1
- package/dist/{engage → chat}/echo-config.d.ts +1 -1
- package/dist/chat/index.d.ts +10 -6
- package/dist/chat/index.js +14 -8
- package/dist/{engage → chat}/launcher-placement.d.ts +1 -7
- package/dist/{engage → chat}/socket-loader.d.ts +2 -2
- package/dist/chat/types.d.ts +136 -0
- package/dist/chunk-27FISYK4.js +4 -0
- package/dist/{chunk-LUXVWITO.js → chunk-4NTBQNHA.js} +247 -78
- package/dist/{chunk-V3PUVBME.js → chunk-5V2V5C3Y.js} +2 -4
- package/dist/{chunk-Q4W23JYM.js → chunk-5YCEMV2L.js} +1 -1
- package/dist/{chunk-RHNWA34F.js → chunk-64DKGVEI.js} +41 -7
- package/dist/{chunk-GLHQ3LRO.js → chunk-6U3VLV2C.js} +1 -1
- package/dist/{chunk-WWSHFRDL.js → chunk-BC4Y3S3D.js} +1 -1
- package/dist/{chunk-JSZN6LDZ.js → chunk-BF7TZYC3.js} +38 -31
- package/dist/{chunk-M3YEPKP7.js → chunk-BKY2ZNM7.js} +1 -1
- package/dist/{chunk-AQSNPZY4.js → chunk-C2FZBUSS.js} +80 -1
- package/dist/{chunk-NK4CSJNP.js → chunk-CF5QNFYU.js} +2 -2
- package/dist/{chunk-D5EC5KVO.js → chunk-D5DCBD4Y.js} +2 -2
- package/dist/{chunk-3SDOQUUF.js → chunk-D6ZC7XKZ.js} +2 -2
- package/dist/chunk-EJVQJCG5.js +370 -0
- package/dist/{chunk-AH5Q262S.js → chunk-F24FNCTK.js} +1 -1
- package/dist/{chunk-V6FDQLR7.js → chunk-FR6BYFPC.js} +53 -21
- package/dist/{chunk-UXOGCKL6.js → chunk-GEXT7BTE.js} +1 -1
- package/dist/{chunk-JTU2GOM4.js → chunk-GGORNBSC.js} +18 -6
- package/dist/{chunk-ERXXITG4.js → chunk-HGFAEDG3.js} +1 -1
- package/dist/chunk-HODO5BX5.js +28 -0
- package/dist/chunk-HW7B43E5.js +14 -0
- package/dist/{chunk-CSTQDNGK.js → chunk-IAM3D3RL.js} +43 -24
- package/dist/{chunk-PGMVL4AB.js → chunk-JJ3DELUL.js} +2 -2
- package/dist/{chunk-ILU5I6RW.js → chunk-JMLK6MH7.js} +1 -1
- package/dist/{chunk-YEMPUQVU.js → chunk-JWTQS5F5.js} +2 -2
- package/dist/{chunk-VDF5DFWT.js → chunk-K4ZHAURD.js} +1 -1
- package/dist/chunk-NV72HZ4W.js +45 -0
- package/dist/{chunk-4Y5FWDPM.js → chunk-NZ4RXN3G.js} +55 -7
- package/dist/chunk-OWZ7ATLZ.js +187 -0
- package/dist/{chunk-TXBAOEKG.js → chunk-PKGN32AU.js} +5 -4
- package/dist/chunk-Q6E4OPGG.js +40 -0
- package/dist/{chunk-QEDHRBAT.js → chunk-QL6XNPSD.js} +2 -2
- package/dist/{chunk-JBE46QJZ.js → chunk-R46IQIGK.js} +1 -1
- package/dist/{chunk-6WIQCXOO.js → chunk-RPZ7OQGD.js} +1 -1
- package/dist/{chunk-PHYMNUJU.js → chunk-S54ECQGY.js} +1 -1
- package/dist/{chunk-VGH4MDTU.js → chunk-VHKEF5ZB.js} +1 -1
- package/dist/{chunk-HF57LG73.js → chunk-VO3JYRT5.js} +43 -29
- package/dist/chunk-VWWITFWM.js +300 -0
- package/dist/{chunk-CIG5UL2S.js → chunk-ZTLMPUO6.js} +3 -2
- package/dist/client/index.js +3 -3
- package/dist/commerce/index.js +4 -4
- package/dist/contracts/color.d.ts +1 -1
- package/dist/contracts/entries.d.ts +1 -1
- package/dist/contracts/error-page.d.ts +183 -0
- package/dist/contracts/llms.d.ts +1 -0
- package/dist/contracts/proposal-sitemap.d.ts +130 -0
- package/dist/contracts/schema-placeholders.d.ts +87 -0
- package/dist/engage/EngageWidget.d.ts +13 -12
- package/dist/engage/index.d.ts +10 -9
- package/dist/engage/index.js +31 -35
- package/dist/engage/types.d.ts +19 -244
- package/dist/fleet/FleetHeartbeat.d.ts +1 -1
- package/dist/fleet/index.js +4 -4
- package/dist/forms/FormEnhancer.d.ts +25 -12
- package/dist/forms/ServerForm.d.ts +11 -10
- package/dist/forms/StaticForm.d.ts +3 -1
- package/dist/forms/field-autocomplete.d.ts +28 -0
- package/dist/forms/form-dom-values.d.ts +42 -0
- package/dist/forms/index.js +11 -9
- package/dist/forms/server.js +6 -4
- package/dist/forms/static.js +3 -2
- package/dist/forms/submitForm.d.ts +8 -1
- package/dist/forms/types.d.ts +41 -2
- package/dist/forms/useForm.d.ts +21 -4
- package/dist/forms/webmcp.d.ts +38 -0
- package/dist/images/index.js +4 -4
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1 -1
- package/dist/layout/SiteKitClientProviders.d.ts +3 -3
- package/dist/layout/SiteKitLayout.d.ts +4 -4
- package/dist/layout/client.d.ts +3 -3
- package/dist/layout/client.js +7 -7
- package/dist/layout/index.js +8 -8
- package/dist/layout/types.d.ts +5 -4
- package/dist/llms/contract.js +1 -1
- package/dist/llms/index.js +6 -6
- package/dist/maps/index.js +3 -3
- package/dist/mcp/index.js +2 -14
- package/dist/mcp/sonor.js +14 -11
- package/dist/revalidate/index.js +2 -2
- package/dist/seo/client.js +4 -4
- package/dist/seo/index.js +15 -8
- package/dist/seo/llms/contract.js +1 -1
- package/dist/seo/llms.js +6 -6
- package/dist/seo/register-sitemap-cli.js +1 -1
- package/dist/seo/sitemap.js +4 -4
- package/dist/server/index.js +2 -2
- package/dist/shared/dialog.d.ts +27 -0
- package/dist/shared/identity.d.ts +1 -1
- package/dist/shared/layers.d.ts +7 -0
- package/dist/shared/mid-form.d.ts +53 -0
- package/dist/shared/reporting-gate.d.ts +1 -1
- package/dist/shared/version.d.ts +1 -1
- package/dist/shared/visual-viewport-gap.d.ts +1 -1
- package/dist/signal/index.js +2 -2
- package/dist/signal/types.d.ts +1 -1
- package/dist/sitemap/index.js +4 -4
- package/dist/{socket-loader-R24ZSRSQ.js → socket-loader-CGIPEG74.js} +1 -1
- package/dist/sync/index.js +5 -5
- package/dist/types.d.ts +3 -1
- package/dist/{web-vitals.attribution-GD6LGLVF.js → web-vitals.attribution-N2PCCF4Q.js} +1 -1
- package/dist/website/BlocksPopup.d.ts +1 -1
- package/dist/website/PopupBlocks.d.ts +5 -2
- package/dist/website/SitePopups.d.ts +25 -0
- package/dist/website/images.js +4 -4
- package/dist/website/index.js +6 -6
- package/dist/website/popup-rules.d.ts +46 -0
- package/dist/website/popup-types.d.ts +113 -0
- package/dist/website/popups.d.ts +2 -6
- package/dist/website/popups.js +6 -14
- package/dist/{writeLLMsTxt-G3JBNBC5.js → writeLLMsTxt-OV24LQVL.js} +3 -3
- package/docs/MIGRATING-TO-7.md +41 -9
- package/docs.json +2 -1
- package/package.json +2 -2
- package/src/analytics/README.md +2 -2
- package/src/articles/README.md +4 -1
- package/src/{engage → chat}/README.md +63 -62
- package/src/forms/README.md +49 -3
- package/src/layout/README.md +8 -5
- package/src/seo/README.md +3 -1
- package/src/sync/README.md +10 -0
- package/src/website/README.md +133 -0
- package/dist/ChatWidget-UCDWMWCW.js +0 -15
- package/dist/EngageWidget-7ZYSK4GE.js +0 -11
- package/dist/ManagedForm-H7BVAH2W.js +0 -14
- package/dist/SitemapSync-XBANGKHZ.js +0 -8
- package/dist/chunk-NDF4A5JM.js +0 -37
- package/dist/chunk-VLARKWZU.js +0 -100
- package/dist/chunk-YCJT4JJG.js +0 -837
- package/dist/engage/DesignRenderer.d.ts +0 -57
- package/dist/engage/element-rules.d.ts +0 -38
|
@@ -1,58 +1,69 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Website chat — `@sonordev/site-kit/chat`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Echo's chat launcher and conversation on your site, configured in Sonor
|
|
4
|
+
(Messages → Chat settings). AI answers, live handoff to your team, and an
|
|
5
|
+
offline form when nobody's around.
|
|
4
6
|
|
|
5
7
|
## Usage
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
`SiteKitLayout` mounts it for you, after the page goes idle:
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
<SiteKitLayout>{children}</SiteKitLayout> // chat on (default)
|
|
13
|
+
<SiteKitLayout chat={{ offsetBottom: '88px' }}>{children}</SiteKitLayout>
|
|
14
|
+
<SiteKitLayout chat={false}>{children}</SiteKitLayout> // no chat
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
On a site without `SiteKitLayout`, mount `SiteChat` the same way:
|
|
8
18
|
|
|
9
19
|
```tsx
|
|
10
20
|
'use client'
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
export
|
|
14
|
-
return
|
|
15
|
-
<>
|
|
16
|
-
{children}
|
|
17
|
-
<EngageWidget />
|
|
18
|
-
</>
|
|
19
|
-
)
|
|
21
|
+
import { SiteChat } from '@sonordev/site-kit/chat'
|
|
22
|
+
|
|
23
|
+
export function Chat() {
|
|
24
|
+
return <SiteChat position="bottom-right" />
|
|
20
25
|
}
|
|
21
26
|
```
|
|
22
27
|
|
|
28
|
+
`SiteChat` waits for the browser to go idle, skips cross-origin frames and
|
|
29
|
+
localhost (like analytics), and downloads the chat widget only when it
|
|
30
|
+
renders. `ChatWidget` is the widget itself, for a page that resolves those
|
|
31
|
+
choices on its own.
|
|
32
|
+
|
|
23
33
|
## Props
|
|
24
34
|
|
|
25
35
|
```ts
|
|
26
|
-
interface
|
|
27
|
-
apiUrl?: string
|
|
28
|
-
apiKey?: string
|
|
29
|
-
projectId?: string
|
|
36
|
+
interface SiteChatProps {
|
|
37
|
+
apiUrl?: string // Default: from SiteKitLayout, else https://api.sonor.io
|
|
38
|
+
apiKey?: string // Default: from SiteKitLayout
|
|
39
|
+
projectId?: string // For chat routing. Resolved from the key when absent
|
|
30
40
|
position?: 'bottom-right' | 'bottom-left' // Default: 'bottom-right'
|
|
31
41
|
offsetBottom?: string | number // Default: '20px'. See "Launcher placement"
|
|
32
|
-
zIndex?: number
|
|
33
|
-
|
|
34
|
-
|
|
42
|
+
zIndex?: number // Default: 9999. See "Stacking"
|
|
43
|
+
allowInFrame?: boolean // Show inside a cross-origin frame. Default: false
|
|
44
|
+
allowLocalhost?: boolean // Show on localhost. Default: false
|
|
35
45
|
}
|
|
36
46
|
```
|
|
37
47
|
|
|
38
|
-
Through `SiteKitLayout`, the
|
|
48
|
+
Through `SiteKitLayout`, the placement options go in `chat={{ ... }}`. The
|
|
49
|
+
deprecated `engage={{ ... }}` still works through 7.x.
|
|
39
50
|
|
|
40
51
|
## The chat switch
|
|
41
52
|
|
|
42
|
-
Echo follows the project's **
|
|
43
|
-
Chat settings). A project that has never saved chat settings
|
|
44
|
-
Echo is on by default and only an owner who switches it off
|
|
45
|
-
needs the project's **
|
|
46
|
-
answers off, since the chat has nowhere to take a
|
|
53
|
+
Echo follows the project's **Show the chat widget** switch in Sonor
|
|
54
|
+
(Messages → Chat settings). A project that has never saved chat settings
|
|
55
|
+
counts as on, so Echo is on by default and only an owner who switches it off
|
|
56
|
+
hides it. It also needs the project's **Website chat** module on (Project
|
|
57
|
+
Settings); without it Sonor answers off, since the chat has nowhere to take a
|
|
58
|
+
visitor's message.
|
|
47
59
|
|
|
48
60
|
- The launcher appears once `GET /engage/widget/config` answers. Nothing
|
|
49
61
|
renders before that, so a switched-off site never flashes a launcher, and
|
|
50
62
|
it never starts Echo's availability polling.
|
|
51
63
|
- If the config can't be fetched, the launcher stays hidden. The chat can't
|
|
52
64
|
run without the API anyway.
|
|
53
|
-
- `
|
|
54
|
-
|
|
55
|
-
the owner's switch.
|
|
65
|
+
- `chat={false}` in code still turns chat off whatever the switch says. Code
|
|
66
|
+
can turn Echo off; it can't force it on over the owner's switch.
|
|
56
67
|
|
|
57
68
|
## Liquid Glass (6.1.0)
|
|
58
69
|
|
|
@@ -75,7 +86,7 @@ recipe (`src/shared/glass.tsx`), the same material as the mobile CTA bar
|
|
|
75
86
|
panel, and otherwise pulls it toward black (light panel) or white (dark
|
|
76
87
|
panel) just far enough to clear it: `#d4af37` gold becomes `#887023`. Fills
|
|
77
88
|
(launcher, avatar, the visitor's bubbles, buttons) keep the raw brand. One
|
|
78
|
-
helper decides this, `src/
|
|
89
|
+
helper decides this, `src/chat/brand-color.ts`; route any new brand
|
|
79
90
|
foreground through it. `--sk-primary` can be hex, `rgb()` or `hsl()`.
|
|
80
91
|
- **Fields are 16px.** iOS Safari zooms the whole page into any focused field
|
|
81
92
|
under 16px; the chat input and the inline Echo forms were 13.5-14px.
|
|
@@ -104,7 +115,7 @@ it without `!important`. Don't write that override; declare the offset instead.
|
|
|
104
115
|
**A fixed offset, every page:** pass `offsetBottom`.
|
|
105
116
|
|
|
106
117
|
```tsx
|
|
107
|
-
<SiteKitLayout
|
|
118
|
+
<SiteKitLayout chat={{ offsetBottom: '88px' }}>{children}</SiteKitLayout>
|
|
108
119
|
```
|
|
109
120
|
|
|
110
121
|
Any CSS length works (`'5.5rem'`, `'calc(4rem + 8px)'`); a number is pixels.
|
|
@@ -148,13 +159,14 @@ popup maxHeight = 100dvh - launcher bottom - 100px
|
|
|
148
159
|
|
|
149
160
|
### Stacking
|
|
150
161
|
|
|
151
|
-
`zIndex` is the
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
when your own fixed UI (a mobile menu, a cookie
|
|
162
|
+
`zIndex` is the layer the chat launcher sits on, with the chat window one
|
|
163
|
+
layer beneath it. Through `SiteKitLayout` the same number also stacks the
|
|
164
|
+
site's popups, banners and toasts. The default is 9999, above almost anything
|
|
165
|
+
a site draws, so lower it when your own fixed UI (a mobile menu, a cookie
|
|
166
|
+
banner) has to cover the chat:
|
|
155
167
|
|
|
156
168
|
```tsx
|
|
157
|
-
<SiteKitLayout
|
|
169
|
+
<SiteKitLayout chat={{ zIndex: 40 }}>{children}</SiteKitLayout>
|
|
158
170
|
```
|
|
159
171
|
|
|
160
172
|
```
|
|
@@ -162,9 +174,9 @@ launcher z-index = zIndex (default 9999)
|
|
|
162
174
|
popup z-index = zIndex - 1
|
|
163
175
|
```
|
|
164
176
|
|
|
165
|
-
At `zIndex` 0 or below the
|
|
166
|
-
-1 would put it behind the page's own content. The launcher still
|
|
167
|
-
top.
|
|
177
|
+
At `zIndex` 0 or below the chat window shares the launcher's layer instead,
|
|
178
|
+
because -1 would put it behind the page's own content. The launcher still
|
|
179
|
+
paints on top.
|
|
168
180
|
|
|
169
181
|
### Layout vs visual viewport on phones
|
|
170
182
|
|
|
@@ -194,29 +206,9 @@ It's opt-in: while a field has focus the gap grows to the keyboard's height,
|
|
|
194
206
|
so everything that reads it rides above the keyboard. `useVisualViewportGap()`
|
|
195
207
|
is the hook form for an existing client component.
|
|
196
208
|
|
|
197
|
-
##
|
|
198
|
-
|
|
199
|
-
| Type | Description |
|
|
200
|
-
|------|-------------|
|
|
201
|
-
| `popup` | Modal overlay with CTA |
|
|
202
|
-
| `nudge` | Small corner notification |
|
|
203
|
-
| `bar` | Top/bottom sticky bar |
|
|
204
|
-
| `slide-in` | Side panel |
|
|
205
|
-
| `chat` | AI/live chat widget |
|
|
209
|
+
## Chat config
|
|
206
210
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
All configured in the Sonor dashboard — no code changes needed:
|
|
210
|
-
|
|
211
|
-
- **Page targeting** — include/exclude paths with wildcard support
|
|
212
|
-
- **Device targeting** — desktop, mobile, tablet
|
|
213
|
-
- **Visitor targeting** — new vs returning visitors
|
|
214
|
-
- **Triggers** — immediate, delay (seconds), scroll (%), exit-intent, click, custom
|
|
215
|
-
- **Frequency capping** — once, once-per-session, every N days
|
|
216
|
-
|
|
217
|
-
## Chat Widget
|
|
218
|
-
|
|
219
|
-
Supports AI mode (Echo), live mode, and hybrid (AI + human handoff):
|
|
211
|
+
AI mode (Echo), live mode, and hybrid (AI with a human handoff). `ChatWidget` takes these as `config`; Sonor sends the rest from Chat settings:
|
|
220
212
|
|
|
221
213
|
```ts
|
|
222
214
|
interface ChatConfig {
|
|
@@ -236,6 +228,15 @@ interface ChatConfig {
|
|
|
236
228
|
}
|
|
237
229
|
```
|
|
238
230
|
|
|
239
|
-
##
|
|
231
|
+
## Popups
|
|
232
|
+
|
|
233
|
+
Popups, banners and toasts are their own module now:
|
|
234
|
+
[Popups and banners](../website/README.md) (`@sonordev/site-kit/website/popups`).
|
|
235
|
+
|
|
236
|
+
## `@sonordev/site-kit/engage` (deprecated)
|
|
240
237
|
|
|
241
|
-
|
|
238
|
+
Engage was retired in Sonor. Its entry stays through 7.x so old imports keep
|
|
239
|
+
building: `ChatWidget` and the chat types re-export from here, and
|
|
240
|
+
`EngageWidget` draws `SiteChat` and `SitePopups`. Engage Studio's renderer
|
|
241
|
+
(`DesignRenderer`) is gone; popups render from blocks. Import
|
|
242
|
+
`@sonordev/site-kit/chat` and `@sonordev/site-kit/website/popups` instead.
|
package/src/forms/README.md
CHANGED
|
@@ -78,12 +78,12 @@ import { useForm } from '@sonordev/site-kit/forms'
|
|
|
78
78
|
|
|
79
79
|
export function ContactForm() {
|
|
80
80
|
const {
|
|
81
|
-
fields, values, errors, setFieldValue,
|
|
81
|
+
fields, values, errors, setFieldValue, handleSubmit, toolAttributes, isSubmitting,
|
|
82
82
|
step, totalSteps, isMultiStep, nextStep, prevStep, isLastStep,
|
|
83
83
|
} = useForm('contact-form')
|
|
84
84
|
|
|
85
85
|
return (
|
|
86
|
-
<form onSubmit={
|
|
86
|
+
<form {...toolAttributes} onSubmit={handleSubmit}>
|
|
87
87
|
{fields.map(field => (
|
|
88
88
|
<div key={field.slug}>
|
|
89
89
|
<label>{field.label}</label>
|
|
@@ -166,7 +166,12 @@ interface UseFormReturn {
|
|
|
166
166
|
canGoPrev: boolean
|
|
167
167
|
isLastStep: boolean
|
|
168
168
|
validate: () => boolean
|
|
169
|
-
|
|
169
|
+
// Resolves with what happened: { status: 'sent' | 'invalid' | 'failed' | 'busy' | 'next_step', ... }
|
|
170
|
+
submit: (options?: FormSubmitOptions) => Promise<FormSubmitOutcome>
|
|
171
|
+
// A ready-made <form onSubmit>: steps or submits, and answers an AI agent (see below)
|
|
172
|
+
handleSubmit: (event: FormEvent<HTMLFormElement>) => void
|
|
173
|
+
// WebMCP attributes for your <form>; null until the config loads
|
|
174
|
+
toolAttributes: { toolname: string; tooldescription: string } | null
|
|
170
175
|
isSubmitting: boolean
|
|
171
176
|
isComplete: boolean
|
|
172
177
|
reset: () => void
|
|
@@ -212,6 +217,47 @@ it, so Sonor refuses those before anything is written: the visitor sees an
|
|
|
212
217
|
error and you get no lead. Submit through site-kit, and define the form's
|
|
213
218
|
fields in Sonor so both can render and validate them.
|
|
214
219
|
|
|
220
|
+
## Agent-ready forms (7.2.0)
|
|
221
|
+
|
|
222
|
+
People increasingly ask an AI assistant to fill out a form for them: a quote
|
|
223
|
+
request, a booking, a newsletter signup. Assistants act on what the page's
|
|
224
|
+
markup says each control is, so managed forms now say it clearly. There's
|
|
225
|
+
nothing to configure.
|
|
226
|
+
|
|
227
|
+
- **Autocomplete tokens.** Name, email, phone, company, website and address
|
|
228
|
+
fields carry the right `autocomplete` token (`given-name`, `email`, `tel`,
|
|
229
|
+
`organization`, `url`, `postal-code`, ...), read from the field's CRM
|
|
230
|
+
destination in Sonor, then its type, slug and label. Browser autofill and
|
|
231
|
+
password managers use the same tokens.
|
|
232
|
+
- **Accessible wiring.** Error and help text are tied to their control
|
|
233
|
+
(`aria-describedby`), an errored control says so (`aria-invalid`), radio and
|
|
234
|
+
checkbox groups are named by their question, and rating stars by their
|
|
235
|
+
value.
|
|
236
|
+
- **WebMCP.** Every interactive managed form carries the declarative WebMCP
|
|
237
|
+
attributes (`toolname`, `tooldescription`), so a browser agent that supports
|
|
238
|
+
WebMCP can treat it as a tool. It fills the same fields a person would, and
|
|
239
|
+
the submit runs the same validation. There's no `toolautosubmit`: the
|
|
240
|
+
assistant fills the form and the person it's helping presses Send.
|
|
241
|
+
Browsers without WebMCP ignore the attributes.
|
|
242
|
+
- **Agent-sent leads are tagged.** When the browser reports that an agent
|
|
243
|
+
pressed submit (`SubmitEvent.agentInvoked`), the submission carries that
|
|
244
|
+
flag and the lead is tagged in Sonor, so you can see how those leads compare
|
|
245
|
+
over time. It's the browser's word, so it never changes how a submission is
|
|
246
|
+
checked. The agent is handed the outcome of its submit (`respondWith`).
|
|
247
|
+
- **Nothing typed before the form loads is lost.** `ServerForm` renders the
|
|
248
|
+
form in the page HTML and loads the interactive version at idle. Since
|
|
249
|
+
7.2.0, typing into the server-rendered form starts that upgrade at once and
|
|
250
|
+
what was typed carries over, and a Send pressed before the upgrade is held
|
|
251
|
+
and sent the moment it lands. The server-rendered Send button stays disabled
|
|
252
|
+
until the page can catch the click.
|
|
253
|
+
|
|
254
|
+
With `useForm`, spread `toolAttributes` on your `<form>` and use
|
|
255
|
+
`handleSubmit` as its `onSubmit` to get the same behavior.
|
|
256
|
+
|
|
257
|
+
`ServerForm`'s `enhance` prop is ignored since 7.2.0. Only the interactive
|
|
258
|
+
form can send a managed form, so `enhance={false}` only ever produced a form
|
|
259
|
+
that silently went nowhere.
|
|
260
|
+
|
|
215
261
|
## Styles
|
|
216
262
|
|
|
217
263
|
```tsx
|
package/src/layout/README.md
CHANGED
|
@@ -28,9 +28,11 @@ interface SiteKitLayoutProps {
|
|
|
28
28
|
children: React.ReactNode
|
|
29
29
|
apiKey?: string // Defaults to SONOR_API_KEY env var
|
|
30
30
|
apiUrl?: string // Defaults to SONOR_API_URL, then https://api.sonor.io
|
|
31
|
-
projectId?: string // For
|
|
31
|
+
projectId?: string // For chat routing (auto-resolved if omitted)
|
|
32
32
|
analytics?: boolean | AnalyticsConfig // Default: true
|
|
33
|
-
|
|
33
|
+
chat?: boolean | ChatLayoutConfig // Default: true (website chat; config = launcher placement)
|
|
34
|
+
popups?: boolean // Default: true (Website → Popups & Banners)
|
|
35
|
+
engage?: boolean | EngageConfig // Deprecated: false turns chat and popups off; an object configures the launcher
|
|
34
36
|
signal?: boolean | SignalConfig // Default: false
|
|
35
37
|
sitemapSync?: boolean // Default: false (build-time + server reconciler own this)
|
|
36
38
|
fleet?: boolean // Default: true (once-per-session kit version heartbeat)
|
|
@@ -43,7 +45,7 @@ interface SiteKitLayoutProps {
|
|
|
43
45
|
}
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
Module options live with each module: [Analytics](../analytics/README.md) (`trackPageViews`, `excludePaths`, `site`, `allowInFrame`, `allowLocalhost`)
|
|
48
|
+
Module options live with each module: [Analytics](../analytics/README.md) (`trackPageViews`, `excludePaths`, `site`, `allowInFrame`, `allowLocalhost`) [Website chat](../chat/README.md) (`position`, `offsetBottom`, `zIndex`, `allowInFrame`) and [Popups and banners](../website/README.md).
|
|
47
49
|
|
|
48
50
|
## What It Composes
|
|
49
51
|
|
|
@@ -54,12 +56,13 @@ Module options live with each module: [Analytics](../analytics/README.md) (`trac
|
|
|
54
56
|
|
|
55
57
|
**Client-side (lazy-loaded island):**
|
|
56
58
|
- `AnalyticsProvider` — page views, scroll depth, heatmap clicks, Web Vitals
|
|
57
|
-
- `
|
|
59
|
+
- `SitePopups` — popups, banners and toasts
|
|
60
|
+
- `SiteChat` — website chat (Echo)
|
|
58
61
|
- `SignalBridge` — A/B experiments, behavior tracking (opt-in)
|
|
59
62
|
- `SitemapSync` — parses `/sitemap.xml` and syncs to Sonor (opt-in)
|
|
60
63
|
- `FleetHeartbeat` — reports the kit version and enabled modules once per session
|
|
61
64
|
|
|
62
|
-
Since 4.0.0 none of these wrap your page. `{children}` renders first and every module mounts after it as a childless sibling, so `SiteKitLayout` never pushes a route to client rendering. Analytics,
|
|
65
|
+
Since 4.0.0 none of these wrap your page. `{children}` renders first and every module mounts after it as a childless sibling, so `SiteKitLayout` never pushes a route to client rendering. Analytics, chat, popups, SitemapSync and the heartbeat also wait for window load + idle (or the first interaction) unless you pass `defer={false}`. `SignalBridge` isn't deferred, so experiment variants apply early. Visitor and session IDs come from a shared storage singleton rather than a provider.
|
|
63
66
|
|
|
64
67
|
## Note
|
|
65
68
|
|
package/src/seo/README.md
CHANGED
|
@@ -143,6 +143,8 @@ It renders one `application/ld+json` script (an `@graph` when there's more than
|
|
|
143
143
|
- a `BreadcrumbList` built from the path, when there isn't one already and the project has a site URL (skipped on `/`)
|
|
144
144
|
- a speakable `WebPage` or `Article` node, when `speakable`, `pageName` and `pageUrl` are all set
|
|
145
145
|
|
|
146
|
+
Everything Sonor supplies (its schema rows, `managed_schema` and the entity graph) loses any template placeholder first. A node whose values are a template's unfilled slots is dropped: a URL on a reserved example domain (`example.com`), a name like "Example" or "Your Business Name", a placeholder phone such as `+1-000-000-0000`, a slot like `[Resident Name]` or `{plan.name}`, or an object that's only a note about what goes there. Its real siblings and parents stay, so an `FAQPage` keeps its questions when only its publisher was a placeholder, and the `BreadcrumbList` fallback still applies when a placeholder breadcrumb is dropped. Your `additionalSchemas` are never touched. The rule is `@sonordev/contracts/schema-placeholders`.
|
|
147
|
+
|
|
146
148
|
| Prop | Default | Notes |
|
|
147
149
|
|------|---------|-------|
|
|
148
150
|
| `path` | | Required. |
|
|
@@ -157,7 +159,7 @@ It's wrapped in `Suspense`, so the fetch never holds up the rest of the page. Th
|
|
|
157
159
|
|
|
158
160
|
### `<LLMSchema path>`
|
|
159
161
|
|
|
160
|
-
Renders the page's `managed_llm_schema` as a `WebPage` JSON-LD script marked `data-llm-optimized="true"`, linked to the site's `WebSite` node when the project has a site URL. It renders nothing when the page has no LLM schema.
|
|
162
|
+
Renders the page's `managed_llm_schema` as a `WebPage` JSON-LD script marked `data-llm-optimized="true"`, linked to the site's `WebSite` node when the project has a site URL. It renders nothing when the page has no LLM schema, or when that schema is a template placeholder (see `<ManagedSchema>`).
|
|
161
163
|
|
|
162
164
|
### Schema helpers
|
|
163
165
|
|
package/src/sync/README.md
CHANGED
|
@@ -59,6 +59,16 @@ Expired meeting details and time reservations show a recovery action. Guests can
|
|
|
59
59
|
|
|
60
60
|
Navigation is disabled while a reservation or booking request is running. Stale responses cannot reopen a previous step, and holds created after the widget is removed are released.
|
|
61
61
|
|
|
62
|
+
## Accessibility
|
|
63
|
+
|
|
64
|
+
Screen readers and AI agent browsers (which act on elements by their accessible names) can book without guessing:
|
|
65
|
+
|
|
66
|
+
- **Days** are buttons named with the full date, year included: "Wednesday, October 7, 2026". The picked day is pressed (`aria-pressed="true"`), and days that can't be booked are disabled.
|
|
67
|
+
- **Times** are buttons named with the time and the date, "10:30 AM, Wednesday, October 7, 2026", in the booking's time zone. They still show just the time. The picked time is pressed.
|
|
68
|
+
- **Confirm**, the button that reserves the picked time, is named "Confirm 10:30 AM, Wednesday, October 7, 2026".
|
|
69
|
+
|
|
70
|
+
Every name contains what its button shows (a time's and Confirm's start with it), so voice control can still target a button by its visible text (WCAG 2.5.3). None of this changes what the widget sends.
|
|
71
|
+
|
|
62
72
|
## API Functions
|
|
63
73
|
|
|
64
74
|
```ts
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Popups and banners — `@sonordev/site-kit/website/popups`
|
|
2
|
+
|
|
3
|
+
The popups, banners and toasts you publish in Sonor (Website → Popups &
|
|
4
|
+
Banners), shown on your site where and when you set them, in the site's own
|
|
5
|
+
design.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
`SiteKitLayout` mounts it for you, after the page goes idle:
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
<SiteKitLayout>{children}</SiteKitLayout> // popups on (default)
|
|
13
|
+
<SiteKitLayout popups={false}>{children}</SiteKitLayout> // no popups
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
On a site without `SiteKitLayout`, mount `SitePopups` the same way:
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
'use client'
|
|
20
|
+
import { SitePopups } from '@sonordev/site-kit/website/popups'
|
|
21
|
+
|
|
22
|
+
export function Popups() {
|
|
23
|
+
return <SitePopups />
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
A project with nothing published costs one request and draws nothing. The
|
|
28
|
+
renderer downloads only when a popup is about to open.
|
|
29
|
+
|
|
30
|
+
## Props
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
interface SitePopupsProps {
|
|
34
|
+
apiUrl?: string // Default: from SiteKitLayout, else https://api.sonor.io
|
|
35
|
+
apiKey?: string // Default: from SiteKitLayout
|
|
36
|
+
zIndex?: number // The layer popups, banners and toasts sit on. Default: 9999
|
|
37
|
+
allowInFrame?: boolean // Show inside a cross-origin frame. Default: false
|
|
38
|
+
allowLocalhost?: boolean // Show on localhost. Default: false
|
|
39
|
+
debug?: boolean
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Through `SiteKitLayout`, `zIndex` comes from `chat={{ zIndex }}`, so the chat
|
|
44
|
+
launcher and popups share one layer.
|
|
45
|
+
|
|
46
|
+
## What shows
|
|
47
|
+
|
|
48
|
+
Each popup is a set of blocks (heading, formatted text, image, button,
|
|
49
|
+
divider) drawn in your site's design: its colors, fonts, radius and buttons.
|
|
50
|
+
`SiteKitLayout` measures the design from the home page at idle and reports it
|
|
51
|
+
to Sonor, so the popup builder previews popups the way they'll look. Its
|
|
52
|
+
`design` prop declares any part of it yourself
|
|
53
|
+
(`design={{ primary: 'var(--brand)', fontHeading: 'Fraunces, serif' }}`).
|
|
54
|
+
|
|
55
|
+
| Placement | What it is |
|
|
56
|
+
|-----------|------------|
|
|
57
|
+
| Popup | Centred over a dimmed page. One at a time: a second waits until the first closes |
|
|
58
|
+
| Banner | A bar along the top or bottom edge |
|
|
59
|
+
| Toast | A small card in a corner (slide-in) |
|
|
60
|
+
|
|
61
|
+
## Targeting and triggers
|
|
62
|
+
|
|
63
|
+
All set in Sonor, no code changes:
|
|
64
|
+
|
|
65
|
+
- **Pages**: include and exclude paths, with `*` for a prefix (`/services/*`)
|
|
66
|
+
- **Devices**: desktop, mobile, tablet
|
|
67
|
+
- **Schedule**: start and end dates
|
|
68
|
+
- **Trigger**: immediately, after a delay, at a scroll depth, or on exit intent
|
|
69
|
+
- **Frequency**: once, once per session, or every N days. Closing a popup and
|
|
70
|
+
pressing its button both count as seen.
|
|
71
|
+
|
|
72
|
+
A popup targeted at one page leaves when the visitor navigates away from it.
|
|
73
|
+
|
|
74
|
+
### Popups wait for forms
|
|
75
|
+
|
|
76
|
+
A popup or toast that opens on its own first checks whether the visitor is in
|
|
77
|
+
the middle of a form, and waits while they are. Mid-form means:
|
|
78
|
+
|
|
79
|
+
- focus is in a form field, or in a card field embedded in a form;
|
|
80
|
+
- a form has a field they've filled in and haven't sent yet;
|
|
81
|
+
- a site-kit form is sending.
|
|
82
|
+
|
|
83
|
+
It opens a moment after focus leaves the form, or once the form has been sent
|
|
84
|
+
and cleared. It never gives up: while the visitor stays mid-form, it keeps
|
|
85
|
+
waiting. Banners show at once, since they don't take focus or hide the page.
|
|
86
|
+
|
|
87
|
+
Why it matters: a modal popup takes focus and hides the rest of the page from
|
|
88
|
+
assistive technology while it's open. Opening one mid-form throws a person out
|
|
89
|
+
of the field they're typing in, and an AI agent browser filling the form for
|
|
90
|
+
someone (agents act on the accessibility tree) loses every field it was about
|
|
91
|
+
to fill.
|
|
92
|
+
|
|
93
|
+
## Accessibility
|
|
94
|
+
|
|
95
|
+
Popups, toasts and banners are built so people and agent browsers can find
|
|
96
|
+
them and their controls by name:
|
|
97
|
+
|
|
98
|
+
- **A popup** is a modal dialog (`role="dialog"`, `aria-modal="true"`) named
|
|
99
|
+
by its heading. It takes focus when it opens, keeps Tab inside, closes on
|
|
100
|
+
Escape or a click outside it, and puts focus back where it was.
|
|
101
|
+
- **A toast** is a named, non-modal dialog, and **a banner** is a named
|
|
102
|
+
region. Both close on Escape and leave focus where it is.
|
|
103
|
+
- **Close buttons are named "Close"**, whatever they show.
|
|
104
|
+
|
|
105
|
+
## Tracking
|
|
106
|
+
|
|
107
|
+
Each popup's impression (once half of it is on screen) and button presses are
|
|
108
|
+
counted in Sonor, against the same visitor ID (`_sk_vid`) analytics uses.
|
|
109
|
+
|
|
110
|
+
## Drawing one popup yourself
|
|
111
|
+
|
|
112
|
+
`PopupBlocks` is the renderer `SitePopups` uses, for drawing one popup from
|
|
113
|
+
its blocks (a preview, a custom placement):
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
import { PopupBlocks } from '@sonordev/site-kit/website/popups'
|
|
117
|
+
|
|
118
|
+
<PopupBlocks
|
|
119
|
+
blocks={[{ type: 'heading', text: 'Spring cleanups are booking' }]}
|
|
120
|
+
placement="popup"
|
|
121
|
+
onClose={() => setOpen(false)}
|
|
122
|
+
/>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`inline` renders it in place, without the overlay or focus handling.
|
|
126
|
+
|
|
127
|
+
## Engage (retired)
|
|
128
|
+
|
|
129
|
+
Engage was retired in Sonor, and its popups with it. Popups made in Engage
|
|
130
|
+
Studio (a `design_json` without blocks) aren't drawn by site-kit 7.2 or later;
|
|
131
|
+
make them again in Website → Popups & Banners. `@sonordev/site-kit/engage`
|
|
132
|
+
still builds through 7.x: its `EngageWidget` draws `SitePopups` and the
|
|
133
|
+
website chat.
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export { ChatWidget, EchoChat, isChatEnabled } from './chunk-RHNWA34F.js';
|
|
2
|
-
import './chunk-JE4A5S4F.js';
|
|
3
|
-
import './chunk-NDF4A5JM.js';
|
|
4
|
-
import './chunk-6T6CQVNL.js';
|
|
5
|
-
import './chunk-V6FDQLR7.js';
|
|
6
|
-
import './chunk-AQSNPZY4.js';
|
|
7
|
-
import './chunk-5SQK3D53.js';
|
|
8
|
-
import './chunk-NJCTQH2P.js';
|
|
9
|
-
import './chunk-S22FSH7C.js';
|
|
10
|
-
import './chunk-L2V5PUFN.js';
|
|
11
|
-
import './chunk-GJWI74ZZ.js';
|
|
12
|
-
import './chunk-3SDOQUUF.js';
|
|
13
|
-
import './chunk-VDF5DFWT.js';
|
|
14
|
-
import './chunk-AH5Q262S.js';
|
|
15
|
-
import './chunk-PKBMQBKP.js';
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export { EngageWidget } from './chunk-YCJT4JJG.js';
|
|
2
|
-
import './chunk-NDF4A5JM.js';
|
|
3
|
-
import './chunk-6T6CQVNL.js';
|
|
4
|
-
import './chunk-24QZEO3Q.js';
|
|
5
|
-
import './chunk-GJWI74ZZ.js';
|
|
6
|
-
import './chunk-43OCZ3JA.js';
|
|
7
|
-
import './chunk-EKBEOXTH.js';
|
|
8
|
-
import './chunk-3SDOQUUF.js';
|
|
9
|
-
import './chunk-VDF5DFWT.js';
|
|
10
|
-
import './chunk-AH5Q262S.js';
|
|
11
|
-
import './chunk-PKBMQBKP.js';
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export { ManagedForm } from './chunk-CSTQDNGK.js';
|
|
2
|
-
import './chunk-D5EC5KVO.js';
|
|
3
|
-
import './chunk-DXO6SBMD.js';
|
|
4
|
-
import './chunk-LUXVWITO.js';
|
|
5
|
-
import './chunk-V6FDQLR7.js';
|
|
6
|
-
import './chunk-AQSNPZY4.js';
|
|
7
|
-
import './chunk-5SQK3D53.js';
|
|
8
|
-
import './chunk-NJCTQH2P.js';
|
|
9
|
-
import './chunk-L2V5PUFN.js';
|
|
10
|
-
import './chunk-GJWI74ZZ.js';
|
|
11
|
-
import './chunk-3SDOQUUF.js';
|
|
12
|
-
import './chunk-VDF5DFWT.js';
|
|
13
|
-
import './chunk-AH5Q262S.js';
|
|
14
|
-
import './chunk-PKBMQBKP.js';
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export { SitemapSync } from './chunk-UXOGCKL6.js';
|
|
2
|
-
import './chunk-YVKRYQRG.js';
|
|
3
|
-
import './chunk-43OCZ3JA.js';
|
|
4
|
-
import './chunk-EKBEOXTH.js';
|
|
5
|
-
import './chunk-3SDOQUUF.js';
|
|
6
|
-
import './chunk-VDF5DFWT.js';
|
|
7
|
-
import './chunk-AH5Q262S.js';
|
|
8
|
-
import './chunk-PKBMQBKP.js';
|
package/dist/chunk-NDF4A5JM.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { VISUAL_VIEWPORT_GAP_PROPERTY } from './chunk-6T6CQVNL.js';
|
|
2
|
-
|
|
3
|
-
// src/engage/launcher-placement.ts
|
|
4
|
-
var DEFAULT_LAUNCHER_OFFSET_BOTTOM = "20px";
|
|
5
|
-
var DEFAULT_ENGAGE_Z_INDEX = 9999;
|
|
6
|
-
var LAUNCHER_OFFSET_PROPERTY = "--sk-echo-offset-bottom";
|
|
7
|
-
var SIDE_GUTTER = "20px";
|
|
8
|
-
var POPUP_ABOVE_LAUNCHER = "70px";
|
|
9
|
-
var POPUP_VERTICAL_RESERVE = "100px";
|
|
10
|
-
function toCssLength(value) {
|
|
11
|
-
if (typeof value === "number") return Number.isFinite(value) ? `${value}px` : void 0;
|
|
12
|
-
const trimmed = value?.trim();
|
|
13
|
-
return trimmed ? trimmed : void 0;
|
|
14
|
-
}
|
|
15
|
-
function chatLauncherStyles({ position, offsetBottom, zIndex } = {}) {
|
|
16
|
-
const side = position === "bottom-left" ? { left: SIDE_GUTTER } : { right: SIDE_GUTTER };
|
|
17
|
-
const offset = toCssLength(offsetBottom) ?? DEFAULT_LAUNCHER_OFFSET_BOTTOM;
|
|
18
|
-
const layer = typeof zIndex === "number" && Number.isFinite(zIndex) ? zIndex : DEFAULT_ENGAGE_Z_INDEX;
|
|
19
|
-
const clearance = `var(${LAUNCHER_OFFSET_PROPERTY}, ${offset}) + env(safe-area-inset-bottom, 0px) + var(${VISUAL_VIEWPORT_GAP_PROPERTY}, 0px)`;
|
|
20
|
-
return {
|
|
21
|
-
launcher: {
|
|
22
|
-
...side,
|
|
23
|
-
bottom: `calc(${clearance})`
|
|
24
|
-
},
|
|
25
|
-
popup: {
|
|
26
|
-
...side,
|
|
27
|
-
bottom: `calc(${clearance} + ${POPUP_ABOVE_LAUNCHER})`,
|
|
28
|
-
maxHeight: `calc(100dvh - (${clearance}) - ${POPUP_VERTICAL_RESERVE})`
|
|
29
|
-
},
|
|
30
|
-
// One beneath the launcher. At 0 or below, the same layer instead: -1
|
|
31
|
-
// would paint the popup behind the page's own content, and at a shared
|
|
32
|
-
// layer the launcher, which renders after the popup, still paints on top.
|
|
33
|
-
zIndex: { launcher: layer, popup: layer > 0 ? layer - 1 : layer }
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export { DEFAULT_ENGAGE_Z_INDEX, DEFAULT_LAUNCHER_OFFSET_BOTTOM, chatLauncherStyles };
|