@nethesis/chat-island 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ogg.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ /** Samples at 48 kHz in one Opus packet (RFC 6716, section 3.1). */
2
+ export declare function opusSamples(p: Uint8Array): number;
3
+ /** WebM/Opus bytes to Ogg/Opus bytes; null when the input is not what MediaRecorder writes. */
4
+ export declare function webmOpusToOgg(webm: Uint8Array): Uint8Array<ArrayBuffer> | null;
package/dist/push.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { Config } from './types';
2
+ /** Returns the push node to enable on XMPP, or null when push is unavailable or denied. */
3
+ export declare function registerWebPush(cfg: Config, serviceWorkerUrl: string): Promise<{
4
+ node: string;
5
+ service: string;
6
+ } | null>;
@@ -0,0 +1,2 @@
1
+ declare const table: Record<string, string>;
2
+ export default table;
@@ -0,0 +1,2 @@
1
+ /** Every known :code: outside code becomes its emoji; unknown ones stay as typed. */
2
+ export declare function emojify(text: string): string;
@@ -0,0 +1,2 @@
1
+ /** A short two-note chime made on the fly: nothing to ship, quiet enough for an office. */
2
+ export declare function chime(): void;
@@ -0,0 +1,42 @@
1
+ import type { Contact, Conversation, Message, Reaction, Status } from './types';
2
+ interface State {
3
+ me: string;
4
+ mucHost: string;
5
+ status: Status;
6
+ error?: string;
7
+ contacts: Record<string, Contact>;
8
+ online: Record<string, boolean>;
9
+ mobile: Record<string, boolean>;
10
+ conversations: Record<string, Conversation>;
11
+ order: string[];
12
+ open: string | null;
13
+ picker: boolean;
14
+ push: 'unavailable' | 'ask' | 'enabled' | 'denied';
15
+ enablePush: () => void;
16
+ maxHeads: number;
17
+ newChatButton: boolean;
18
+ setStatus(s: Status, err?: string): void;
19
+ reset(): void;
20
+ setContacts(list: Contact[]): void;
21
+ setOnline(peer: string, online: boolean): void;
22
+ setMobile(users: string[]): void;
23
+ ensure(peer: string): Conversation;
24
+ setGroup(peer: string, name: string, members: string[], avatar?: string, owner?: boolean): void;
25
+ removeConversation(peer: string): void;
26
+ seed(msgs: Message[], me: string): void;
27
+ addMessage(peer: string, m: Message, opts?: {
28
+ live?: boolean;
29
+ }): void;
30
+ prependHistory(peer: string, msgs: Message[], complete: boolean, oldest?: string): void;
31
+ setTyping(peer: string, typing: boolean): void;
32
+ applyReactions(list: Reaction[]): void;
33
+ openChat(peer: string): void;
34
+ closeChat(peer?: string): void;
35
+ removeHead(peer: string): void;
36
+ markRead(peer: string): void;
37
+ setPicker(v: boolean): void;
38
+ totalUnread(): number;
39
+ }
40
+ export declare const peerOf: (m: Message) => string;
41
+ export declare const useStore: import("zustand").UseBoundStore<import("zustand").StoreApi<State>>;
42
+ export {};
@@ -0,0 +1,67 @@
1
+ export type Status = 'connecting' | 'online' | 'offline' | 'error' | 'unauthorized';
2
+ export interface Message {
3
+ id: string;
4
+ from: string;
5
+ to: string;
6
+ body: string;
7
+ ts: number;
8
+ mine: boolean;
9
+ oob?: string;
10
+ pending?: boolean;
11
+ room?: string;
12
+ nick?: string;
13
+ oid?: string;
14
+ }
15
+ /** Someone's reactions to one message (XEP-0444): the whole set, an empty one takes them back. */
16
+ export interface Reaction {
17
+ peer: string;
18
+ target: string;
19
+ user: string;
20
+ emojis: string[];
21
+ }
22
+ export interface Conversation {
23
+ peer: string;
24
+ kind: 'chat' | 'group';
25
+ name?: string;
26
+ avatar?: string;
27
+ owner?: boolean;
28
+ members?: string[];
29
+ messages: Message[];
30
+ unread: number;
31
+ typing: boolean;
32
+ loaded: boolean;
33
+ complete: boolean;
34
+ oldest?: string;
35
+ reactions?: Record<string, Record<string, string[]>>;
36
+ }
37
+ export interface Contact {
38
+ username: string;
39
+ name: string;
40
+ avatar?: string;
41
+ presence?: string;
42
+ number?: string;
43
+ }
44
+ export interface Config {
45
+ host: string;
46
+ username: string;
47
+ token: string;
48
+ }
49
+ /** What the host app gets for its conversation list: one row per colleague or group. */
50
+ export interface ConversationSummary {
51
+ peer: string;
52
+ kind: 'chat' | 'group';
53
+ name: string;
54
+ avatar?: string;
55
+ online: boolean;
56
+ mobile?: boolean;
57
+ owner?: boolean;
58
+ unread: number;
59
+ members?: string[];
60
+ last?: {
61
+ body: string;
62
+ ts: number;
63
+ mine: boolean;
64
+ oob?: string;
65
+ nick?: string;
66
+ };
67
+ }
package/dist/xmpp.d.ts ADDED
@@ -0,0 +1,96 @@
1
+ import type { Element } from '@xmpp/xml';
2
+ import type { Config, Message, Reaction } from './types';
3
+ export interface HistoryPage {
4
+ messages: Message[];
5
+ reactions: Reaction[];
6
+ complete: boolean;
7
+ first?: string;
8
+ }
9
+ export interface GroupInfo {
10
+ peer: string;
11
+ name: string;
12
+ members: string[];
13
+ avatar?: string;
14
+ owner?: boolean;
15
+ }
16
+ type Handlers = {
17
+ message: (m: Message) => void;
18
+ reaction: (r: Reaction) => void;
19
+ presence: (peer: string, online: boolean) => void;
20
+ typing: (peer: string, composing: boolean) => void;
21
+ status: (s: 'connecting' | 'online' | 'offline' | 'error' | 'unauthorized', err?: string) => void;
22
+ groupInvite: (room: string) => void;
23
+ };
24
+ export declare class Chat {
25
+ private xmpp;
26
+ private me;
27
+ private domain;
28
+ readonly mucHost: string;
29
+ private uploadService?;
30
+ private mamQueries;
31
+ private mamReactions;
32
+ private stopped;
33
+ private attempt;
34
+ private retryTimer?;
35
+ private pingTimer?;
36
+ private unauthorized;
37
+ private connected;
38
+ private online;
39
+ private cfg;
40
+ private h;
41
+ constructor(cfg: Config, h: Handlers);
42
+ start(): void;
43
+ stop(): Promise<unknown>;
44
+ /** The network is back, or the tab is looked at again: check now instead of waiting for the timer. */
45
+ private wake;
46
+ private onVisible;
47
+ /** XEP-0199: a server that does not answer means a dead socket; drop it so the retry logic takes over. */
48
+ private ping;
49
+ /** Nobody is known to be online while I am not connected myself. */
50
+ private forgetPresence;
51
+ /** Connect, or reconnect: a failure (network, or a token the server no longer accepts) schedules another try. */
52
+ private connectNow;
53
+ /** 1s, 2s, 4s… up to a minute, with jitter; one timer at a time. */
54
+ private scheduleRetry;
55
+ isGroup(peer: string): boolean;
56
+ /** Send to a colleague (username) or to a group (room address). */
57
+ send(peer: string, body: string, oob?: string): Promise<Message>;
58
+ /** XEP-0444: my whole set of reactions to one message; an empty set takes them back. Archived like a message. */
59
+ react(peer: string, target: string, emojis: string[]): Promise<void>;
60
+ /** Tell these colleagues I am here (directed presence). Those online answer, so both sides see each other. */
61
+ announce(peers: string[]): Promise<void>;
62
+ typing(peer: string, composing: boolean): Promise<void>;
63
+ /** XEP-0357: ask the server to notify `service` on node `node` when I am offline. */
64
+ enablePush(service: string, node: string): Promise<Element>;
65
+ /** Logging out: this browser must not be woken for this account any more. */
66
+ disablePush(service: string, node: string): Promise<Element>;
67
+ /** The newest `max` archived messages with anyone: what the conversation list is built from. */
68
+ recent(max?: number): Promise<HistoryPage>;
69
+ /** History with one colleague or group, newest page first; pass `before` (archive id) to page back. */
70
+ history(peer: string, before?: string, max?: number): Promise<HistoryPage>;
71
+ private mam;
72
+ /** Create a group: a persistent members-only room, everyone subscribed, nobody joined. */
73
+ createGroup(name: string, members: string[], avatar?: string): Promise<string>;
74
+ /** Leave a group. The owner cannot just walk away: the room is destroyed, for everyone. */
75
+ leaveGroup(room: string, owner: boolean): Promise<void>;
76
+ /** The group's picture lives in the room's vCard (XEP-0054), as any XMPP client would expect. */
77
+ setGroupAvatar(room: string, dataUrl: string): Promise<void>;
78
+ private groupAvatar;
79
+ /** The groups I am subscribed to, with their names and members. */
80
+ listGroups(): Promise<GroupInfo[]>;
81
+ /** Whether I am subscribed to a room: an invitation alone proves nothing, anybody can send one. */
82
+ subscribed(room: string): Promise<boolean>;
83
+ groupInfo(room: string): Promise<GroupInfo>;
84
+ /** Upload a file with XEP-0363 and return its public URL. */
85
+ upload(file: File): Promise<string>;
86
+ private findUploadService;
87
+ private waiters;
88
+ private waitFor;
89
+ private onStanza;
90
+ private deliver;
91
+ /** A reactions stanza (XEP-0444), live, carbon, archived or wrapped by MucSub; only from where it may come from. */
92
+ private reactionOf;
93
+ /** Flatten a message stanza; a MucSub wrapper is unwrapped to the group message inside. */
94
+ private toMessage;
95
+ }
96
+ export {};
@@ -0,0 +1,18 @@
1
+ // chat-island service worker: shows chat Web Push notifications, opens the conversation on click.
2
+ self.addEventListener('push', (event) => {
3
+ let data = {}
4
+ try { data = event.data ? event.data.json() : {} } catch { data = { body: event.data && event.data.text() } }
5
+ const title = data.name || data.from || 'New message'
6
+ event.waitUntil(self.registration.showNotification(title, { body: data.body || '', tag: 'chat-' + (data.from || ''), renotify: true, data }))
7
+ })
8
+ self.addEventListener('notificationclick', (event) => {
9
+ event.notification.close()
10
+ const from = event.notification.data && event.notification.data.from
11
+ // The app's own tab, the one under this worker's scope, not any window of the site.
12
+ const scope = self.registration.scope
13
+ event.waitUntil(self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then((list) => {
14
+ const target = list.find((c) => c.url.startsWith(scope)) || list[0]
15
+ if (target) { target.focus(); target.postMessage({ type: 'chat-island-open', username: from }) }
16
+ else self.clients.openWindow(scope)
17
+ }))
18
+ })
@@ -0,0 +1,2 @@
1
+ .chat-island-root,.chat-island-root *,.chat-island-root :before,.chat-island-root :after,.chat-island-root ::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.ci-fixed{position:fixed}.ci-absolute{position:absolute}.ci-relative{position:relative}.ci--left-1{left:-.25rem}.ci--right-1{right:-.25rem}.ci--right-1\.5{right:-.375rem}.ci--top-1{top:-.25rem}.ci-bottom-0{bottom:0}.ci-bottom-1{bottom:.25rem}.ci-bottom-2{bottom:.5rem}.ci-bottom-5{bottom:1.25rem}.ci-bottom-full{bottom:100%}.ci-left-0{left:0}.ci-left-1\/2{left:50%}.ci-left-2{left:.5rem}.ci-left-3{left:.75rem}.ci-left-4{left:1rem}.ci-right-0{right:0}.ci-right-3{right:.75rem}.ci-right-4{right:1rem}.ci-top-16{top:4rem}.ci-z-10{z-index:10}.ci-z-20{z-index:20}.ci-z-30{z-index:30}.ci-z-\[1\]{z-index:1}.ci-z-\[9999\]{z-index:9999}.ci-m-0{margin:0}.ci-my-1{margin-top:.25rem;margin-bottom:.25rem}.ci--mt-1\.5{margin-top:-.375rem}.ci-mb-1{margin-bottom:.25rem}.ci-mb-3{margin-bottom:.75rem}.ci-mt-0\.5{margin-top:.125rem}.ci-mt-1{margin-top:.25rem}.ci-mt-2{margin-top:.5rem}.ci-block{display:block}.ci-flex{display:flex}.ci-grid{display:grid}.ci-hidden{display:none}.ci-h-10{height:2.5rem}.ci-h-12{height:3rem}.ci-h-2\.5{height:.625rem}.ci-h-3{height:.75rem}.ci-h-5{height:1.25rem}.ci-h-6{height:1.5rem}.ci-h-64{height:16rem}.ci-h-7{height:1.75rem}.ci-h-8{height:2rem}.ci-h-9{height:2.25rem}.ci-h-\[30rem\]{height:30rem}.ci-h-full{height:100%}.ci-max-h-32{max-height:8rem}.ci-max-h-60{max-height:15rem}.ci-max-h-64{max-height:16rem}.ci-max-h-\[30rem\]{max-height:30rem}.ci-min-h-0{min-height:0}.ci-w-0{width:0}.ci-w-10{width:2.5rem}.ci-w-12{width:3rem}.ci-w-2\.5{width:.625rem}.ci-w-3{width:.75rem}.ci-w-5{width:1.25rem}.ci-w-7{width:1.75rem}.ci-w-8{width:2rem}.ci-w-9{width:2.25rem}.ci-w-\[21rem\]{width:21rem}.ci-w-\[22rem\]{width:22rem}.ci-w-auto{width:auto}.ci-w-full{width:100%}.ci-min-w-0{min-width:0}.ci-min-w-5{min-width:1.25rem}.ci-max-w-\[80\%\]{max-width:80%}.ci-max-w-full{max-width:100%}.ci-flex-1{flex:1}.ci-shrink-0{flex-shrink:0}.ci--translate-x-1\/2{--tw-translate-x:-50%;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.ci-rotate-45{--tw-rotate:45deg;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.ci-transform{transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes ci-pulse{50%{opacity:.5}}.ci-animate-pulse{animation:2s cubic-bezier(.4,0,.6,1) infinite ci-pulse}.ci-cursor-grab{cursor:grab}.ci-cursor-pointer{cursor:pointer}.ci-cursor-text{cursor:text}.ci-touch-none{touch-action:none}.ci-select-none{-webkit-user-select:none;user-select:none}.ci-select-text{-webkit-user-select:text;user-select:text}.ci-resize-none{resize:none}.ci-list-none{list-style-type:none}.ci-grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.ci-flex-col{flex-direction:column}.ci-flex-col-reverse{flex-direction:column-reverse}.ci-flex-wrap{flex-wrap:wrap}.ci-content-center{align-content:center}.ci-items-start{align-items:flex-start}.ci-items-end{align-items:flex-end}.ci-items-center{align-items:center}.ci-justify-start{justify-content:flex-start}.ci-justify-end{justify-content:flex-end}.ci-justify-center{justify-content:center}.ci-gap-0\.5{gap:.125rem}.ci-gap-1{gap:.25rem}.ci-gap-1\.5{gap:.375rem}.ci-gap-2{gap:.5rem}.ci-gap-3{gap:.75rem}.ci-space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.ci-self-center{align-self:center}.ci-overflow-hidden{overflow:hidden}.ci-overflow-x-auto{overflow-x:auto}.ci-overflow-y-auto{overflow-y:auto}.ci-truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.ci-whitespace-nowrap{white-space:nowrap}.ci-whitespace-pre{white-space:pre}.ci-whitespace-pre-wrap{white-space:pre-wrap}.ci-break-words{overflow-wrap:break-word}.ci-break-all{word-break:break-all}.ci-rounded{border-radius:.25rem}.ci-rounded-2xl{border-radius:1rem}.ci-rounded-3xl{border-radius:1.5rem}.ci-rounded-full{border-radius:9999px}.ci-rounded-lg{border-radius:.5rem}.ci-rounded-xl{border-radius:.75rem}.ci-rounded-b-3xl{border-bottom-right-radius:1.5rem;border-bottom-left-radius:1.5rem}.ci-rounded-t-3xl{border-top-left-radius:1.5rem;border-top-right-radius:1.5rem}.ci-rounded-bl-lg{border-bottom-left-radius:.5rem}.ci-rounded-br-lg{border-bottom-right-radius:.5rem}.ci-border{border-width:1px}.ci-border-0{border-width:0}.ci-border-b{border-bottom-width:1px}.ci-border-b-2{border-bottom-width:2px}.ci-border-t{border-top-width:1px}.ci-border-solid{border-style:solid}.ci-border-emerald-400{--tw-border-opacity:1;border-color:rgb(52 211 153/var(--tw-border-opacity,1))}.ci-border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.ci-border-gray-400{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity,1))}.ci-border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity,1))}.ci-border-iconSecondary{--tw-border-opacity:1;border-color:rgb(67 56 202/var(--tw-border-opacity,1))}.ci-border-transparent{border-color:#0000}.ci-bg-black\/10{background-color:#0000001a}.ci-bg-elevationL2{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.ci-bg-emerald-50{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.ci-bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.ci-bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity,1))}.ci-bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.ci-bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.ci-bg-iconSecondary{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity,1))}.ci-bg-phoneIslandActive{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.ci-bg-phoneIslandCall{--tw-bg-opacity:1;background-color:rgb(21 128 61/var(--tw-bg-opacity,1))}.ci-bg-phoneIslandClose{--tw-bg-opacity:1;background-color:rgb(185 28 28/var(--tw-bg-opacity,1))}.ci-bg-transparent{background-color:#0000}.ci-object-cover{-o-object-fit:cover;object-fit:cover}.ci-p-0{padding:0}.ci-p-1{padding:.25rem}.ci-p-2{padding:.5rem}.ci-px-1{padding-left:.25rem;padding-right:.25rem}.ci-px-1\.5{padding-left:.375rem;padding-right:.375rem}.ci-px-2{padding-left:.5rem;padding-right:.5rem}.ci-px-3{padding-left:.75rem;padding-right:.75rem}.ci-px-3\.5{padding-left:.875rem;padding-right:.875rem}.ci-px-4{padding-left:1rem;padding-right:1rem}.ci-px-5{padding-left:1.25rem;padding-right:1.25rem}.ci-py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.ci-py-1{padding-top:.25rem;padding-bottom:.25rem}.ci-py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.ci-py-2{padding-top:.5rem;padding-bottom:.5rem}.ci-py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.ci-py-3{padding-top:.75rem;padding-bottom:.75rem}.ci-py-6{padding-top:1.5rem;padding-bottom:1.5rem}.ci-pb-1\.5{padding-bottom:.375rem}.ci-pb-2{padding-bottom:.5rem}.ci-pl-11{padding-left:2.75rem}.ci-pl-2{padding-left:.5rem}.ci-pl-2\.5{padding-left:.625rem}.ci-pr-1\.5{padding-right:.375rem}.ci-pr-2{padding-right:.5rem}.ci-pr-4{padding-right:1rem}.ci-pt-2{padding-top:.5rem}.ci-text-left{text-align:left}.ci-text-center{text-align:center}.ci-text-right{text-align:right}.ci-font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.ci-font-sans{font-family:Poppins,Inter,system-ui,sans-serif}.ci-text-\[0\.85em\]{font-size:.85em}.ci-text-\[10px\]{font-size:10px}.ci-text-lg{font-size:1.125rem;line-height:1.75rem}.ci-text-sm{font-size:.875rem;line-height:1.25rem}.ci-text-xs{font-size:.75rem;line-height:1rem}.ci-font-light{font-weight:300}.ci-font-medium{font-weight:500}.ci-italic{font-style:italic}.ci-not-italic{font-style:normal}.ci-leading-4{line-height:1rem}.ci-leading-none{line-height:1}.ci-tracking-wide{letter-spacing:.025em}.ci-text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity,1))}.ci-text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.ci-text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.ci-text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.ci-text-inherit{color:inherit}.ci-text-primaryInvert{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity,1))}.ci-text-secondaryNeutral{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.ci-text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.ci-underline{text-decoration-line:underline}.ci-opacity-0{opacity:0}.ci-opacity-100{opacity:1}.ci-opacity-60{opacity:.6}.ci-opacity-80{opacity:.8}.ci-shadow-2xl{--tw-shadow:0 25px 50px -12px #00000040;--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000), var(--tw-ring-shadow,0 0 #0000), var(--tw-shadow)}.ci-shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000), var(--tw-ring-shadow,0 0 #0000), var(--tw-shadow)}.ci-shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000), var(--tw-ring-shadow,0 0 #0000), var(--tw-shadow)}.ci-shadow-xl{--tw-shadow:0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000001a;--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000), var(--tw-ring-shadow,0 0 #0000), var(--tw-shadow)}.ci-outline-none{outline-offset:2px;outline:2px solid #0000}.ci-ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow,0 0 #0000)}.ci-ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow,0 0 #0000)}.ci-ring-gray-300{--tw-ring-opacity:1;--tw-ring-color:rgb(209 213 219/var(--tw-ring-opacity,1))}.ci-ring-gray-50{--tw-ring-opacity:1;--tw-ring-color:rgb(249 250 251/var(--tw-ring-opacity,1))}.ci-ring-iconSecondary{--tw-ring-opacity:1;--tw-ring-color:rgb(67 56 202/var(--tw-ring-opacity,1))}.ci-transition-all{transition-property:all;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.ci-transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.ci-transition-transform{transition-property:transform;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.ci-duration-200{transition-duration:.2s}.chat-island-root,.chat-island-root *{box-sizing:border-box}.chat-island-root{font-family:Poppins,Inter,system-ui,sans-serif;font-size:14px;line-height:1.4}.chat-island-root button{font:inherit;cursor:pointer}.chat-island-root textarea,.chat-island-root input{font:inherit}.chat-island-root ::-webkit-scrollbar{width:6px}.chat-island-root ::-webkit-scrollbar-thumb{background:#9ca3af80;border-radius:9999px}.placeholder\:ci-text-gray-500::placeholder{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.hover\:ci-scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:ci-scale-125:hover{--tw-scale-x:1.25;--tw-scale-y:1.25;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:ci-bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.hover\:ci-bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.hover\:ci-bg-gray-300\/70:hover{background-color:#d1d5dbb3}.hover\:ci-bg-gray-500:hover{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity,1))}.hover\:ci-bg-phoneIslandCallHover:hover{--tw-bg-opacity:1;background-color:rgb(22 101 52/var(--tw-bg-opacity,1))}.hover\:ci-bg-phoneIslandCloseHover:hover{--tw-bg-opacity:1;background-color:rgb(153 27 27/var(--tw-bg-opacity,1))}.hover\:ci-opacity-100:hover{opacity:1}.focus\:ci-z-20:focus{z-index:20}.focus\:ci-opacity-100:focus{opacity:1}.focus\:ci-ring-0:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow,0 0 #0000)}.focus\:ci-ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow,0 0 #0000)}.focus\:ci-ring-emerald-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(16 185 129/var(--tw-ring-opacity,1))}.focus\:ci-ring-gray-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(156 163 175/var(--tw-ring-opacity,1))}.focus\:ci-ring-green-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(34 197 94/var(--tw-ring-opacity,1))}.focus\:ci-ring-offset-2:focus{--tw-ring-offset-width:2px}.focus\:ci-ring-offset-white:focus{--tw-ring-offset-color:#fff}.active\:ci-cursor-grabbing:active{cursor:grabbing}.enabled\:hover\:ci-border-gray-500:hover:enabled{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity,1))}.enabled\:hover\:ci-bg-gray-300\/70:hover:enabled{background-color:#d1d5dbb3}.enabled\:hover\:ci-bg-gray-500:hover:enabled{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity,1))}.disabled\:ci-cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:ci-opacity-40:disabled{opacity:.4}.ci-group:focus-within .group-focus-within\:ci-flex,.ci-group:hover .group-hover\:ci-flex{display:flex}.ci-group:hover .group-hover\:ci-opacity-100{opacity:1}.dark\:ci-border-emerald-600:is(.ci-dark *){--tw-border-opacity:1;border-color:rgb(5 150 105/var(--tw-border-opacity,1))}.dark\:ci-border-gray-600:is(.ci-dark *){--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity,1))}.dark\:ci-border-gray-700:is(.ci-dark *){--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity,1))}.dark\:ci-border-iconSecondaryDark:is(.ci-dark *){--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity,1))}.dark\:ci-bg-elevationL2Dark:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.dark\:ci-bg-emerald-950:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(2 44 34/var(--tw-bg-opacity,1))}.dark\:ci-bg-gray-300:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.dark\:ci-bg-gray-600:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.dark\:ci-bg-gray-800:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.dark\:ci-bg-gray-900:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.dark\:ci-bg-gray-950:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(3 7 18/var(--tw-bg-opacity,1))}.dark\:ci-bg-iconSecondaryDark:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity,1))}.dark\:ci-bg-phoneIslandActiveDark:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.dark\:ci-bg-phoneIslandCallDark:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.dark\:ci-bg-phoneIslandCloseDark:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.dark\:ci-bg-white\/10:is(.ci-dark *){background-color:#ffffff1a}.dark\:ci-text-gray-200:is(.ci-dark *){--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.dark\:ci-text-gray-400:is(.ci-dark *){--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.dark\:ci-text-gray-900:is(.ci-dark *){--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.dark\:ci-text-gray-950:is(.ci-dark *){--tw-text-opacity:1;color:rgb(3 7 18/var(--tw-text-opacity,1))}.dark\:ci-text-primaryInvertDark:is(.ci-dark *){--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.dark\:ci-text-secondaryNeutralDark:is(.ci-dark *){--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.dark\:ci-text-white:is(.ci-dark *){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.dark\:ci-ring-gray-700:is(.ci-dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(55 65 81/var(--tw-ring-opacity,1))}.dark\:ci-ring-gray-950:is(.ci-dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(3 7 18/var(--tw-ring-opacity,1))}.dark\:ci-ring-iconSecondaryDark:is(.ci-dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity,1))}.dark\:placeholder\:ci-text-gray-400:is(.ci-dark *)::-moz-placeholder{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.dark\:placeholder\:ci-text-gray-400:is(.ci-dark *)::placeholder{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.dark\:hover\:ci-bg-gray-50:hover:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.dark\:hover\:ci-bg-gray-600:hover:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.dark\:hover\:ci-bg-gray-700\/30:hover:is(.ci-dark *){background-color:#3741514d}.dark\:hover\:ci-bg-gray-800:hover:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.dark\:hover\:ci-bg-phoneIslandCallHoverDark:hover:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(134 239 172/var(--tw-bg-opacity,1))}.dark\:hover\:ci-bg-phoneIslandCloseHoverDark:hover:is(.ci-dark *){--tw-bg-opacity:1;background-color:rgb(252 165 165/var(--tw-bg-opacity,1))}.dark\:focus\:ci-ring-emerald-300:focus:is(.ci-dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(110 231 183/var(--tw-ring-opacity,1))}.dark\:focus\:ci-ring-gray-500:focus:is(.ci-dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(107 114 128/var(--tw-ring-opacity,1))}.dark\:focus\:ci-ring-offset-black:focus:is(.ci-dark *){--tw-ring-offset-color:#000}.dark\:enabled\:hover\:ci-bg-gray-700\/30:hover:enabled:is(.ci-dark *){background-color:#3741514d}
2
+ /*$vite$:1*/