@v-tilt/browser 1.10.10 → 1.12.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/all-external-dependencies.js +1 -1
- package/dist/all-external-dependencies.js.map +1 -1
- package/dist/array.full.js +1 -1
- package/dist/array.full.js.map +1 -1
- package/dist/array.js +1 -1
- package/dist/array.js.map +1 -1
- package/dist/array.no-external.js +1 -1
- package/dist/array.no-external.js.map +1 -1
- package/dist/autocapture-types.d.ts +17 -0
- package/dist/autocapture-utils.d.ts +24 -1
- package/dist/autocapture.d.ts +94 -5
- package/dist/chat.js +1 -1
- package/dist/chat.js.map +1 -1
- package/dist/config.d.ts +8 -1
- package/dist/constants.d.ts +19 -13
- package/dist/core/capture.d.ts +15 -5
- package/dist/core/config-utils.d.ts +15 -0
- package/dist/core/consent.d.ts +62 -0
- package/dist/core/event-buffer.d.ts +60 -0
- package/dist/core/fb-cookies.d.ts +32 -0
- package/dist/core/feature-manager.d.ts +61 -69
- package/dist/core/fifo-queue.d.ts +23 -0
- package/dist/core/identity.d.ts +23 -33
- package/dist/core/index.d.ts +7 -1
- package/dist/core/page-lifecycle.d.ts +41 -0
- package/dist/core/remote-config.d.ts +14 -17
- package/dist/extensions/chat/bubble-drag.d.ts +30 -0
- package/dist/extensions/chat/chat-api.d.ts +15 -0
- package/dist/extensions/chat/chat-styles.d.ts +27 -0
- package/dist/extensions/chat/chat-wrapper.d.ts +20 -145
- package/dist/extensions/chat/chat.d.ts +129 -12
- package/dist/extensions/chat/message-content-styles.d.ts +1 -0
- package/dist/extensions/chat/message-html.d.ts +6 -0
- package/dist/extensions/chat/message-markdown.d.ts +8 -0
- package/dist/extensions/chat/normalize-send-content.d.ts +24 -0
- package/dist/extensions/chat/types.d.ts +19 -57
- package/dist/extensions/chat/widget-registry.d.ts +53 -0
- package/dist/extensions/chat/widgets/collect-email.d.ts +6 -0
- package/dist/extensions/chat/widgets/escalate-to-human.d.ts +6 -0
- package/dist/extensions/ga4-proxy.d.ts +59 -0
- package/dist/extensions/google-tag-gateway/consent-bridge.d.ts +27 -0
- package/dist/extensions/google-tag-gateway/enhanced-conversions.d.ts +35 -0
- package/dist/extensions/google-tag-gateway/event-bridge.d.ts +74 -0
- package/dist/extensions/google-tag-gateway/google-tag-gateway.d.ts +95 -0
- package/dist/extensions/google-tag-gateway/gtag-loader.d.ts +85 -0
- package/dist/extensions/google-tag-gateway/index.d.ts +7 -0
- package/dist/extensions/google-tag-gateway/normalize.d.ts +28 -0
- package/dist/extensions/google-tag-gateway/public-api.d.ts +23 -0
- package/dist/extensions/history-autocapture.d.ts +2 -2
- package/dist/extensions/replay/index.d.ts +1 -1
- package/dist/extensions/replay/session-recording-utils.d.ts +13 -43
- package/dist/extensions/replay/session-recording-wrapper.d.ts +10 -66
- package/dist/extensions/replay/session-recording.d.ts +53 -1
- package/dist/extensions/replay/types.d.ts +6 -1
- package/dist/extensions/web-vitals/web-vitals-manager.d.ts +14 -43
- package/dist/external-scripts-loader.js +1 -1
- package/dist/external-scripts-loader.js.map +1 -1
- package/dist/feature.d.ts +54 -172
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/module.d.ts +728 -753
- package/dist/module.js +1 -1
- package/dist/module.js.map +1 -1
- package/dist/module.no-external.d.ts +728 -753
- package/dist/module.no-external.js +1 -1
- package/dist/module.no-external.js.map +1 -1
- package/dist/rate-limiter.d.ts +0 -1
- package/dist/recorder.js +1 -1
- package/dist/recorder.js.map +1 -1
- package/dist/request.d.ts +34 -20
- package/dist/scroll-depth-tracker.d.ts +42 -0
- package/dist/server.d.ts +114 -0
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/session.d.ts +12 -0
- package/dist/types.d.ts +204 -9
- package/dist/user-manager.d.ts +26 -52
- package/dist/utils/base64.d.ts +30 -0
- package/dist/utils/bot-detection.d.ts +28 -0
- package/dist/utils/endpoint-url.d.ts +36 -0
- package/dist/utils/event-emitter.d.ts +1 -0
- package/dist/utils/globals.d.ts +71 -2
- package/dist/utils/index.d.ts +20 -5
- package/dist/utils/logger.d.ts +66 -0
- package/dist/utils/request-utils.d.ts +5 -0
- package/dist/utils/safewrap.d.ts +6 -1
- package/dist/utils/transport-health.d.ts +55 -0
- package/dist/utils/type-guards.d.ts +1 -1
- package/dist/vtilt.d.ts +85 -25
- package/dist/web-vitals.js.map +1 -1
- package/package.json +9 -4
|
@@ -3,48 +3,45 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles loading, fetching, caching, and applying remote configuration
|
|
5
5
|
* from the /decide endpoint. Supports bootstrap data for instant loading.
|
|
6
|
+
*
|
|
7
|
+
* Config merge precedence: client init > remote > defaults
|
|
8
|
+
* Uses _initConfig snapshot to distinguish user-set values from cache-applied values.
|
|
9
|
+
*
|
|
10
|
+
* @see docs/patterns/tracker-feature-lifecycle.md
|
|
6
11
|
*/
|
|
7
12
|
import { VTiltConfig, RemoteConfig } from "../types";
|
|
8
|
-
|
|
9
|
-
* Interface for the VTilt instance methods needed by RemoteConfigManager.
|
|
10
|
-
* Using a minimal interface avoids circular dependency.
|
|
11
|
-
*/
|
|
13
|
+
import type { FeatureManager } from "./feature-manager";
|
|
12
14
|
export interface RemoteConfigHost {
|
|
13
15
|
getConfig(): VTiltConfig;
|
|
14
16
|
updateConfig(config: Partial<VTiltConfig>): void;
|
|
17
|
+
buildEndpointUrl(path: string): string;
|
|
15
18
|
__loaded: boolean;
|
|
16
|
-
|
|
17
|
-
startAutocapture?(): void;
|
|
18
|
-
_initChat?(): void;
|
|
19
|
+
featureManager: FeatureManager;
|
|
19
20
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Callback when remote config is loaded/applied
|
|
22
|
-
*/
|
|
23
21
|
export type RemoteConfigCallback = (config: RemoteConfig) => void;
|
|
24
22
|
export declare class RemoteConfigManager {
|
|
25
23
|
private _host;
|
|
26
24
|
private _remoteConfig;
|
|
27
25
|
private _onApply?;
|
|
28
26
|
private _onLoad?;
|
|
27
|
+
private _initConfig;
|
|
29
28
|
constructor(host: RemoteConfigHost);
|
|
30
|
-
/** Get the current remote config (may be null if not loaded) */
|
|
31
29
|
get config(): RemoteConfig | null;
|
|
32
|
-
/** Set callback for when config is applied */
|
|
33
30
|
onApply(callback: RemoteConfigCallback): void;
|
|
34
|
-
/** Set callback for when fresh config is loaded */
|
|
35
31
|
onLoad(callback: RemoteConfigCallback): void;
|
|
36
32
|
/**
|
|
37
33
|
* Load remote config from cache and fetch fresh in background.
|
|
38
34
|
* Priority: 1) Bootstrap config 2) Window bootstrap 3) localStorage cache
|
|
39
35
|
*/
|
|
40
36
|
load(): void;
|
|
41
|
-
/**
|
|
42
|
-
* Fetch remote config from /decide endpoint.
|
|
43
|
-
*/
|
|
44
37
|
private _fetch;
|
|
45
38
|
/**
|
|
46
39
|
* Apply remote config to current config.
|
|
47
|
-
*
|
|
40
|
+
* Uses _initConfig snapshot: only values NOT set by the user in vt.init() are applied.
|
|
41
|
+
* Feature config is mapped via FeatureManager descriptors; non-feature config is handled inline.
|
|
42
|
+
*
|
|
43
|
+
* @param fromCache When true, analytics toggles are skipped to avoid acting on stale
|
|
44
|
+
* localStorage data before the fresh /decide fetch resolves.
|
|
48
45
|
*/
|
|
49
46
|
private _apply;
|
|
50
47
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Draggable bubble utility.
|
|
3
|
+
*
|
|
4
|
+
* Attaches pointer-based drag behavior to a fixed-position bubble element.
|
|
5
|
+
* Persists the drag offset in localStorage so the position survives page
|
|
6
|
+
* navigations and transfers between the lightweight and full bubbles.
|
|
7
|
+
*/
|
|
8
|
+
interface DraggableOptions {
|
|
9
|
+
/** Element that initiates the drag (defaults to the target element itself) */
|
|
10
|
+
handle?: HTMLElement;
|
|
11
|
+
}
|
|
12
|
+
interface DragHandle {
|
|
13
|
+
destroy: () => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Make a fixed-position element draggable.
|
|
17
|
+
*
|
|
18
|
+
* @param el The element to reposition via translate3d.
|
|
19
|
+
* @param opts.handle Optional drag handle element (e.g. bubble inside a container).
|
|
20
|
+
* Pointer events listen on the handle; transform applies to `el`.
|
|
21
|
+
* Defaults to `el` itself.
|
|
22
|
+
*
|
|
23
|
+
* - Restores persisted position from localStorage on attach.
|
|
24
|
+
* - Persists position on drag end.
|
|
25
|
+
* - Distinguishes click from drag via a 5px movement threshold.
|
|
26
|
+
* When dragged, the subsequent `click` event on the handle is suppressed once.
|
|
27
|
+
* - Re-clamps on viewport resize.
|
|
28
|
+
*/
|
|
29
|
+
export declare function makeDraggable(el: HTMLElement, opts?: DraggableOptions): DragHandle;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat widget API – HTTP helpers and endpoint constants.
|
|
3
|
+
*/
|
|
4
|
+
import type { VTiltConfig } from "../../types";
|
|
5
|
+
export declare const API: {
|
|
6
|
+
readonly channels: "/api/chat/widget/channels";
|
|
7
|
+
readonly messages: (channelId: string) => string;
|
|
8
|
+
readonly read: (channelId: string) => string;
|
|
9
|
+
readonly ablyToken: "/api/chat/token/widget";
|
|
10
|
+
};
|
|
11
|
+
/** Minimal host surface for HTTP helpers (lazy chat bundle must not rely on `VTilt` prototype methods). */
|
|
12
|
+
export interface ApiRequestInstance {
|
|
13
|
+
getConfig(): Pick<VTiltConfig, "api_host" | "token">;
|
|
14
|
+
}
|
|
15
|
+
export declare function apiRequest<T>(instance: ApiRequestInstance, endpoint: string, options?: RequestInit): Promise<T | null>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat widget style and HTML string builders.
|
|
3
|
+
* Keeps all style/logic in TS (no separate CSS file), aligned with Intercom/PostHog.
|
|
4
|
+
*/
|
|
5
|
+
export type ChatWidgetPosition = "bottom-right" | "bottom-left";
|
|
6
|
+
export interface ChatTheme {
|
|
7
|
+
primaryColor: string;
|
|
8
|
+
fontFamily: string;
|
|
9
|
+
borderRadius?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function getContainerStyles(theme: ChatTheme, position?: ChatWidgetPosition): string;
|
|
12
|
+
export declare function getBubbleStyles(theme: ChatTheme): string;
|
|
13
|
+
export declare function getBubbleHTML(): string;
|
|
14
|
+
export declare function getWidgetStyles(): string;
|
|
15
|
+
/**
|
|
16
|
+
* Container styles when the widget is fullscreen on mobile.
|
|
17
|
+
* The container (direct child of body) becomes the fullscreen overlay;
|
|
18
|
+
* any drag transform is cleared so position:fixed works correctly.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getMobileContainerStyles(theme: ChatTheme): string;
|
|
21
|
+
/**
|
|
22
|
+
* Widget styles when fullscreen on mobile.
|
|
23
|
+
* Widget fills the container with absolute positioning.
|
|
24
|
+
* Display is omitted — set by _updateUI based on open state.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getMobileWidgetStyles(): string;
|
|
27
|
+
export declare function getWidgetHTML(theme: ChatTheme): string;
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Chat Wrapper
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Extends LazyFeature to handle lazy-loading the chat widget script.
|
|
5
5
|
* The actual chat logic is in LazyLoadedChat which is loaded on demand.
|
|
6
6
|
*
|
|
7
|
-
* Implements the Feature interface for consistent lifecycle management.
|
|
8
|
-
* This follows the same pattern as SessionRecordingWrapper.
|
|
9
|
-
*
|
|
10
7
|
* ## Initialization Modes (Intercom-like flexibility)
|
|
11
8
|
*
|
|
12
9
|
* 1. **Snippet-only (dashboard configured)**:
|
|
@@ -30,193 +27,71 @@
|
|
|
30
27
|
* ```
|
|
31
28
|
*
|
|
32
29
|
* Code config always takes precedence over dashboard settings.
|
|
30
|
+
*
|
|
31
|
+
* @see docs/patterns/tracker-feature-lifecycle.md
|
|
33
32
|
*/
|
|
34
33
|
import type { VTilt } from "../../vtilt";
|
|
35
|
-
import type { VTiltConfig
|
|
36
|
-
import
|
|
37
|
-
import { type ChatConfig, type ChatChannelSummary, type ChatWidgetView } from "../../utils/globals";
|
|
34
|
+
import type { VTiltConfig } from "../../types";
|
|
35
|
+
import { LazyFeature } from "../../feature";
|
|
36
|
+
import { type ChatConfig, type ChatChannelSummary, type ChatWidgetView, type LazyLoadedChatInterface, type SendChatMessageContent, type SendChatMessageOptions } from "../../utils/globals";
|
|
38
37
|
import type { MessageCallback, TypingCallback, ConnectionCallback, Unsubscribe } from "./types";
|
|
39
|
-
/** Status when lazy loading is in progress */
|
|
40
38
|
export declare const CHAT_LOADING: "loading";
|
|
41
|
-
|
|
42
|
-
* Chat Wrapper
|
|
43
|
-
*
|
|
44
|
-
* This is the lightweight class that lives in the main bundle.
|
|
45
|
-
* Implements Feature for consistent lifecycle management.
|
|
46
|
-
*
|
|
47
|
-
* It handles:
|
|
48
|
-
* - Auto-fetching settings from dashboard (Intercom-like)
|
|
49
|
-
* - Merging server settings with code config
|
|
50
|
-
* - Deciding if chat should be enabled
|
|
51
|
-
* - Lazy loading the actual chat widget code
|
|
52
|
-
* - Delegating to LazyLoadedChat
|
|
53
|
-
* - Queuing messages/callbacks before widget loads
|
|
54
|
-
*/
|
|
55
|
-
export declare class ChatWrapper implements Feature {
|
|
56
|
-
private readonly _instance;
|
|
39
|
+
export declare class ChatWrapper extends LazyFeature<ChatConfig, LazyLoadedChatInterface> {
|
|
57
40
|
readonly name = "Chat";
|
|
58
|
-
|
|
59
|
-
private _config;
|
|
41
|
+
readonly scriptName = "chat";
|
|
60
42
|
private _serverConfig;
|
|
61
43
|
private _configFetched;
|
|
62
|
-
private _isLoading;
|
|
63
|
-
private _isStarted;
|
|
64
44
|
private _loadError;
|
|
45
|
+
private _bubbleDragHandle;
|
|
65
46
|
private _pendingMessages;
|
|
66
47
|
private _pendingCallbacks;
|
|
67
48
|
private _messageCallbacks;
|
|
68
49
|
private _typingCallbacks;
|
|
69
50
|
private _connectionCallbacks;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
* Self-contained - vtilt.ts doesn't need to know config details.
|
|
74
|
-
*/
|
|
51
|
+
/** True after vt.showChat() so preloaded widget stays visible after replacing the lightweight bubble. */
|
|
52
|
+
private _bubbleExplicitShow;
|
|
53
|
+
constructor(instance: VTilt, config?: ChatConfig);
|
|
75
54
|
static extractConfig(config: VTiltConfig): ChatConfig;
|
|
76
55
|
get isEnabled(): boolean;
|
|
77
|
-
get isStarted(): boolean;
|
|
78
56
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
57
|
+
* Chat has a custom start flow: fetch server settings, show bubble,
|
|
58
|
+
* lazy-load full widget on user interaction.
|
|
81
59
|
*/
|
|
82
60
|
startIfEnabled(): void;
|
|
83
|
-
/**
|
|
84
|
-
* Stop the chat widget (Feature interface).
|
|
85
|
-
*/
|
|
86
61
|
stop(): void;
|
|
87
|
-
/**
|
|
88
|
-
* Handle config updates (Feature interface).
|
|
89
|
-
*/
|
|
90
62
|
onConfigUpdate(config: VTiltConfig): void;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
*/
|
|
94
|
-
onRemoteConfig(remoteConfig: RemoteConfig): void;
|
|
95
|
-
/**
|
|
96
|
-
* Async start implementation
|
|
97
|
-
*/
|
|
63
|
+
protected _createLoaded(): LazyLoadedChatInterface;
|
|
64
|
+
protected _onLoaded(loaded: LazyLoadedChatInterface): void;
|
|
98
65
|
private _startAsync;
|
|
99
|
-
/**
|
|
100
|
-
* Whether the chat widget is open
|
|
101
|
-
*/
|
|
102
66
|
get isOpen(): boolean;
|
|
103
|
-
/**
|
|
104
|
-
* Whether connected to realtime service
|
|
105
|
-
*/
|
|
106
67
|
get isConnected(): boolean;
|
|
107
|
-
/**
|
|
108
|
-
* Whether the widget is loading
|
|
109
|
-
*/
|
|
110
68
|
get isLoading(): boolean;
|
|
111
|
-
/**
|
|
112
|
-
* Number of unread messages
|
|
113
|
-
*/
|
|
114
69
|
get unreadCount(): number;
|
|
115
|
-
/**
|
|
116
|
-
* Current channel (if any)
|
|
117
|
-
*/
|
|
118
70
|
get channel(): import("./types").ChatChannel | null;
|
|
119
|
-
/**
|
|
120
|
-
* List of user's channels (multi-channel support)
|
|
121
|
-
*/
|
|
122
71
|
get channels(): ChatChannelSummary[];
|
|
123
|
-
/**
|
|
124
|
-
* Current view state ('list' or 'conversation')
|
|
125
|
-
*/
|
|
126
72
|
get currentView(): ChatWidgetView;
|
|
127
|
-
/**
|
|
128
|
-
* Open the chat widget
|
|
129
|
-
*/
|
|
130
73
|
open(): void;
|
|
131
|
-
/**
|
|
132
|
-
* Close the chat widget
|
|
133
|
-
*/
|
|
134
74
|
close(): void;
|
|
135
|
-
/**
|
|
136
|
-
* Toggle the chat widget open/closed
|
|
137
|
-
*/
|
|
138
75
|
toggle(): void;
|
|
139
|
-
/**
|
|
140
|
-
* Show the chat widget (make visible but not necessarily open)
|
|
141
|
-
*/
|
|
142
76
|
show(): void;
|
|
143
|
-
/**
|
|
144
|
-
* Hide the chat widget
|
|
145
|
-
*/
|
|
146
77
|
hide(): void;
|
|
147
|
-
/**
|
|
148
|
-
* Fetch/refresh the list of user's channels
|
|
149
|
-
*/
|
|
150
78
|
getChannels(): void;
|
|
151
|
-
/**
|
|
152
|
-
* Select a channel and load its messages
|
|
153
|
-
*/
|
|
154
79
|
selectChannel(channelId: string): void;
|
|
155
|
-
/**
|
|
156
|
-
* Create a new channel and enter it
|
|
157
|
-
*/
|
|
158
80
|
createChannel(): void;
|
|
159
|
-
/**
|
|
160
|
-
* Go back to channel list from conversation view
|
|
161
|
-
*/
|
|
162
81
|
goToChannelList(): void;
|
|
163
|
-
|
|
164
|
-
* Send a message
|
|
165
|
-
*/
|
|
166
|
-
sendMessage(content: string): void;
|
|
167
|
-
/**
|
|
168
|
-
* Mark messages as read
|
|
169
|
-
*/
|
|
82
|
+
sendMessage(content: SendChatMessageContent, options?: SendChatMessageOptions): void;
|
|
170
83
|
markAsRead(): void;
|
|
171
|
-
|
|
172
|
-
* Subscribe to new messages
|
|
173
|
-
*/
|
|
84
|
+
registerWidget(definition: Parameters<NonNullable<LazyLoadedChatInterface["registerWidget"]>>[0]): void;
|
|
174
85
|
onMessage(callback: MessageCallback): Unsubscribe;
|
|
175
|
-
/**
|
|
176
|
-
* Subscribe to typing indicators
|
|
177
|
-
*/
|
|
178
86
|
onTyping(callback: TypingCallback): Unsubscribe;
|
|
179
|
-
/**
|
|
180
|
-
* Subscribe to connection changes
|
|
181
|
-
*/
|
|
182
87
|
onConnectionChange(callback: ConnectionCallback): Unsubscribe;
|
|
183
|
-
/**
|
|
184
|
-
* Update configuration
|
|
185
|
-
*/
|
|
186
88
|
updateConfig(config: Partial<ChatConfig>): void;
|
|
187
|
-
/**
|
|
188
|
-
* Get the merged configuration (server + code, code takes precedence)
|
|
189
|
-
*/
|
|
190
89
|
getMergedConfig(): ChatConfig;
|
|
191
|
-
/**
|
|
192
|
-
* Destroy the chat widget
|
|
193
|
-
*/
|
|
194
90
|
destroy(): void;
|
|
195
|
-
/**
|
|
196
|
-
* Fetch chat settings from the server
|
|
197
|
-
* This enables "snippet-only" installation where widget configures from dashboard
|
|
198
|
-
*/
|
|
199
91
|
private _fetchServerSettings;
|
|
200
|
-
|
|
201
|
-
* Show the chat bubble (launcher button) without fully loading the widget
|
|
202
|
-
* This creates a lightweight bubble that loads the full widget on click
|
|
203
|
-
*/
|
|
92
|
+
private _fetchUnreadForBubble;
|
|
204
93
|
private _showBubble;
|
|
205
|
-
private
|
|
206
|
-
/**
|
|
207
|
-
* Schedule preload on idle
|
|
208
|
-
*/
|
|
94
|
+
private _onVisibilityChange;
|
|
209
95
|
private _schedulePreload;
|
|
210
|
-
/**
|
|
211
|
-
* Lazy load and then execute callback
|
|
212
|
-
*/
|
|
213
96
|
private _lazyLoadAndThen;
|
|
214
|
-
/**
|
|
215
|
-
* Lazy load the chat script
|
|
216
|
-
*/
|
|
217
|
-
private _lazyLoad;
|
|
218
|
-
/**
|
|
219
|
-
* Called after the chat script is loaded
|
|
220
|
-
*/
|
|
221
|
-
private _onScriptLoaded;
|
|
222
97
|
}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* Chat Widget - Lazy loaded chat implementation using Ably for real-time messaging.
|
|
3
3
|
*/
|
|
4
4
|
import type { VTilt } from "../../vtilt";
|
|
5
|
-
import { type ChatConfig, type ChatChannel, type ChatChannelSummary, type ChatWidgetView, type LazyLoadedChatInterface } from "../../utils/globals";
|
|
5
|
+
import { type ChatConfig, type ChatChannel, type ChatChannelSummary, type ChatWidgetView, type LazyLoadedChatInterface, type SendChatMessageContent, type SendChatMessageOptions } from "../../utils/globals";
|
|
6
6
|
import { type MessageCallback, type TypingCallback, type ConnectionCallback, type Unsubscribe } from "./types";
|
|
7
|
+
import { type WidgetContext } from "./widget-registry";
|
|
7
8
|
export declare class LazyLoadedChat implements LazyLoadedChatInterface {
|
|
8
9
|
private _instance;
|
|
9
10
|
private _config;
|
|
@@ -11,10 +12,19 @@ export declare class LazyLoadedChat implements LazyLoadedChatInterface {
|
|
|
11
12
|
private _container;
|
|
12
13
|
private _widget;
|
|
13
14
|
private _bubble;
|
|
15
|
+
private _bubbleDragHandle;
|
|
16
|
+
private _chatTheme;
|
|
17
|
+
private _widgetResizeCleanup;
|
|
14
18
|
private _ably;
|
|
19
|
+
private _notificationsChannel;
|
|
15
20
|
private _ablyChannel;
|
|
16
21
|
private _typingChannel;
|
|
17
22
|
private _connectionState;
|
|
23
|
+
private _ablyProjectId;
|
|
24
|
+
/** Channel id included in the last per-channel Ably token authorize. */
|
|
25
|
+
private _realtimeChannelId;
|
|
26
|
+
/** Channel id we are connecting to (select/create in flight). */
|
|
27
|
+
private _pendingRealtimeChannelId;
|
|
18
28
|
private _messageCallbacks;
|
|
19
29
|
private _typingCallbacks;
|
|
20
30
|
private _connectionCallbacks;
|
|
@@ -22,6 +32,9 @@ export declare class LazyLoadedChat implements LazyLoadedChatInterface {
|
|
|
22
32
|
private _isUserTyping;
|
|
23
33
|
private _initialUserReadAt;
|
|
24
34
|
private _isMarkingRead;
|
|
35
|
+
private _widgetRegistry;
|
|
36
|
+
/** Tracks whether show() was explicitly called (vs open() temporarily revealing the container). */
|
|
37
|
+
private _bubbleExplicitShow;
|
|
25
38
|
constructor(instance: VTilt, config?: ChatConfig);
|
|
26
39
|
get isOpen(): boolean;
|
|
27
40
|
get isConnected(): boolean;
|
|
@@ -48,9 +61,15 @@ export declare class LazyLoadedChat implements LazyLoadedChatInterface {
|
|
|
48
61
|
getChannels(): Promise<void>;
|
|
49
62
|
selectChannel(channelId: string): Promise<void>;
|
|
50
63
|
createChannel(): Promise<void>;
|
|
51
|
-
goToChannelList(): void
|
|
52
|
-
sendMessage(content:
|
|
64
|
+
goToChannelList(): Promise<void>;
|
|
65
|
+
sendMessage(content: SendChatMessageContent, options?: SendChatMessageOptions): Promise<void>;
|
|
53
66
|
markAsRead(): void;
|
|
67
|
+
/**
|
|
68
|
+
* Send a silent trigger to the messages API after a widget action completes.
|
|
69
|
+
* This makes the AI respond to the action (e.g., acknowledge email collection)
|
|
70
|
+
* without creating a visible user message in the chat.
|
|
71
|
+
*/
|
|
72
|
+
private _triggerAIAfterWidgetAction;
|
|
54
73
|
private _autoMarkAsRead;
|
|
55
74
|
private _isMessageReadByUser;
|
|
56
75
|
/**
|
|
@@ -62,32 +81,87 @@ export declare class LazyLoadedChat implements LazyLoadedChatInterface {
|
|
|
62
81
|
onTyping(callback: TypingCallback): Unsubscribe;
|
|
63
82
|
onConnectionChange(callback: ConnectionCallback): Unsubscribe;
|
|
64
83
|
destroy(): void;
|
|
65
|
-
|
|
66
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Ensure Ably client is connected and subscribed to the project notifications channel.
|
|
86
|
+
* Called when the widget opens. Safe to call multiple times.
|
|
87
|
+
*/
|
|
88
|
+
private _ensureAblyConnected;
|
|
89
|
+
/**
|
|
90
|
+
* Request an Ably token for the current connection scope.
|
|
91
|
+
* Pass channelId when subscribing to a conversation; omit on channel list.
|
|
92
|
+
*/
|
|
93
|
+
private _refreshAblyToken;
|
|
94
|
+
/**
|
|
95
|
+
* Subscribe to per-channel messages and typing for a specific conversation.
|
|
96
|
+
*/
|
|
97
|
+
private _connectChannel;
|
|
98
|
+
/**
|
|
99
|
+
* Unsubscribe, detach, and release per-channel Ably channels.
|
|
100
|
+
* Ably only allows release() when the channel is initialized, detached, or
|
|
101
|
+
* failed — not when attached. So we detach first, then release.
|
|
102
|
+
* Releasing removes the channel from the Ably internal registry so a
|
|
103
|
+
* subsequent auth.authorize() with narrower capabilities won't try to
|
|
104
|
+
* reattach it and fail with "Channel denied access."
|
|
105
|
+
*/
|
|
106
|
+
private _disconnectChannel;
|
|
107
|
+
/**
|
|
108
|
+
* Fully disconnect from Ably — unsubscribe everything and close the connection.
|
|
109
|
+
*/
|
|
110
|
+
private _disconnectAll;
|
|
111
|
+
/**
|
|
112
|
+
* Handle project-level notifications (new channels, updates, closes).
|
|
113
|
+
* Updates channel list and badge count in real time.
|
|
114
|
+
*/
|
|
115
|
+
private _handleNotification;
|
|
67
116
|
private _handleNewMessage;
|
|
68
117
|
private _handleTypingEvent;
|
|
69
118
|
private _handleReadCursorEvent;
|
|
70
119
|
private _notifyConnectionChange;
|
|
71
120
|
private _createUI;
|
|
72
121
|
private _attachEventListeners;
|
|
122
|
+
/**
|
|
123
|
+
* Mark a widget as submitted in local message metadata.
|
|
124
|
+
* Called after a successful widget action — _renderWidgets() uses this
|
|
125
|
+
* to show the confirmation UI instead of the input form.
|
|
126
|
+
*/
|
|
127
|
+
private _markWidgetSubmitted;
|
|
73
128
|
private _handleUserTyping;
|
|
74
129
|
private _sendTypingIndicator;
|
|
75
130
|
private _handleSend;
|
|
76
131
|
private _updateUI;
|
|
132
|
+
private _isMobile;
|
|
133
|
+
private _containerBaseStyle;
|
|
134
|
+
private _savedTransform;
|
|
135
|
+
private _isMobileFullscreen;
|
|
136
|
+
/**
|
|
137
|
+
* Mobile fullscreen: make the *container* fullscreen instead of the widget.
|
|
138
|
+
* Using position:fixed on the widget is unreliable because the container
|
|
139
|
+
* may have a CSS transform (from drag) which creates a new containing block.
|
|
140
|
+
* The container is a direct child of <body> so position:fixed is always safe.
|
|
141
|
+
*
|
|
142
|
+
* Both elements get their full style replaced atomically via setAttribute.
|
|
143
|
+
* Display is set by _updateUI after this call.
|
|
144
|
+
*/
|
|
145
|
+
private _applyMobileFullscreen;
|
|
146
|
+
private _removeMobileFullscreen;
|
|
147
|
+
private _scrollY;
|
|
148
|
+
private _lockBodyScroll;
|
|
149
|
+
/**
|
|
150
|
+
* Ensure the opened widget:
|
|
151
|
+
* 1. Covers the bubble (anchored at container bottom-right) for UI consistency.
|
|
152
|
+
* 2. Stays fully inside the viewport by shifting its position when near edges (no size change).
|
|
153
|
+
*
|
|
154
|
+
* On mobile the CSS media query handles fullscreen positioning, so this is a no-op.
|
|
155
|
+
*/
|
|
156
|
+
private _constrainWidgetToViewport;
|
|
77
157
|
private _updateHeader;
|
|
78
158
|
private _getChannelListHTML;
|
|
79
159
|
private _getChannelItemHTML;
|
|
80
160
|
private _getConversationHTML;
|
|
81
161
|
private _attachChannelListListeners;
|
|
82
162
|
private _attachConversationListeners;
|
|
83
|
-
private _escapeHtml;
|
|
84
163
|
private _formatRelativeTime;
|
|
85
164
|
private _renderMessages;
|
|
86
|
-
private _getContainerStyles;
|
|
87
|
-
private _getBubbleStyles;
|
|
88
|
-
private _getBubbleHTML;
|
|
89
|
-
private _getWidgetStyles;
|
|
90
|
-
private _getWidgetHTML;
|
|
91
165
|
private _getMessageHTML;
|
|
92
166
|
private _isMessageReadByAgent;
|
|
93
167
|
/**
|
|
@@ -99,8 +173,51 @@ export declare class LazyLoadedChat implements LazyLoadedChatInterface {
|
|
|
99
173
|
* Remove temporary message by ID
|
|
100
174
|
*/
|
|
101
175
|
private _removeTempMessage;
|
|
102
|
-
private _apiRequest;
|
|
103
176
|
private _getTimeOpen;
|
|
177
|
+
/**
|
|
178
|
+
* Bubble body for any message — plain escaped text or sanitized rich HTML,
|
|
179
|
+
* using the same rules for user (html/markdown) and agent/AI messages.
|
|
180
|
+
*/
|
|
181
|
+
private _renderMessageContent;
|
|
182
|
+
private _renderMessageHtml;
|
|
183
|
+
/**
|
|
184
|
+
* Render widgets for a message. Widget data sources (checked in order):
|
|
185
|
+
* 1. metadata._widgets -- set during streaming (temp messages)
|
|
186
|
+
* 2. metadata.widgets -- persisted in DB (loaded on refresh, received via Ably)
|
|
187
|
+
* 3. Content markers -- fallback for legacy messages with <!--vtilt:widget:...--> in content
|
|
188
|
+
*
|
|
189
|
+
* If a widget has submitted=true in metadata, render the confirmation instead of the input.
|
|
190
|
+
*/
|
|
191
|
+
private _renderWidgets;
|
|
192
|
+
/**
|
|
193
|
+
* Build WidgetContext for widget renderers.
|
|
194
|
+
*/
|
|
195
|
+
private _getWidgetContext;
|
|
196
|
+
/**
|
|
197
|
+
* Extract widget blocks from streamed text, strip them, and return parsed widgets.
|
|
198
|
+
*/
|
|
199
|
+
private _parseWidgetBlocks;
|
|
200
|
+
/**
|
|
201
|
+
* Public method for SDK consumers to register custom widgets.
|
|
202
|
+
*/
|
|
203
|
+
registerWidget(definition: {
|
|
204
|
+
type: string;
|
|
205
|
+
toolDescription: string;
|
|
206
|
+
parameters: Record<string, {
|
|
207
|
+
type: string;
|
|
208
|
+
description: string;
|
|
209
|
+
}>;
|
|
210
|
+
render: (params: Record<string, unknown>, context: WidgetContext) => string;
|
|
211
|
+
onAction: (action: string, data: Record<string, unknown>, context: WidgetContext) => Promise<{
|
|
212
|
+
replaceHTML?: string;
|
|
213
|
+
sendMessage?: string;
|
|
214
|
+
}>;
|
|
215
|
+
}): void;
|
|
104
216
|
private _escapeHTML;
|
|
217
|
+
/**
|
|
218
|
+
* Get a human-readable preview for a message (for channel list).
|
|
219
|
+
* Falls back to widget labels if content is empty (tool-only messages).
|
|
220
|
+
*/
|
|
221
|
+
private _getMessagePreview;
|
|
105
222
|
private _formatTime;
|
|
106
223
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const CHAT_MESSAGE_CONTENT_CSS = "\n #vtilt-chat-widget .vtilt-md {\n max-width: 100%;\n overflow-wrap: anywhere;\n word-break: break-word;\n font-size: inherit;\n line-height: 1.45;\n }\n #vtilt-chat-widget .vtilt-md * {\n box-sizing: border-box;\n }\n #vtilt-chat-widget .vtilt-md :is(p, h1, h2, h3, h4, h5, h6, ul, ol, blockquote, pre) {\n margin: 0;\n }\n #vtilt-chat-widget .vtilt-md p { margin: 0 0 0.5em 0; }\n #vtilt-chat-widget .vtilt-md p:last-child { margin-bottom: 0; }\n #vtilt-chat-widget .vtilt-md :is(b, strong) { font-weight: 600; }\n #vtilt-chat-widget .vtilt-md :is(i, em) { font-style: italic; }\n #vtilt-chat-widget .vtilt-md u { text-decoration: underline; }\n #vtilt-chat-widget .vtilt-md :is(h1, h2, h3, h4, h5, h6) {\n font-size: 1em;\n font-weight: 600;\n margin: 0.65em 0 0.35em 0;\n line-height: 1.3;\n }\n #vtilt-chat-widget .vtilt-md :is(h1, h2, h3):first-child { margin-top: 0.15em; }\n\n /* Lists \u2014 flex rows + ::before bullets (beats host list-item / ::marker resets) */\n #vtilt-chat-widget .vtilt-md :is(ul, ol) {\n list-style: none !important;\n margin: 0.5em 0 !important;\n padding: 0 0 0 0.35em !important;\n }\n #vtilt-chat-widget .vtilt-md li {\n display: flex !important;\n flex-direction: row !important;\n flex-wrap: wrap !important;\n align-items: flex-start !important;\n gap: 0.35em !important;\n margin: 0.2em 0 !important;\n padding: 0 !important;\n line-height: 1.45;\n list-style: none !important;\n list-style-type: none !important;\n }\n #vtilt-chat-widget .vtilt-md li::marker {\n content: \"\" !important;\n font-size: 0 !important;\n color: transparent !important;\n }\n #vtilt-chat-widget .vtilt-md li::-webkit-list-marker {\n display: none !important;\n }\n #vtilt-chat-widget .vtilt-md ul > li::before {\n content: \"\u2022\" !important;\n display: inline-block !important;\n position: static !important;\n flex: 0 0 auto;\n line-height: 1.45;\n }\n #vtilt-chat-widget .vtilt-md ul ul > li::before {\n content: \"\u25E6\" !important;\n }\n #vtilt-chat-widget .vtilt-md ol { counter-reset: vtilt-ol; }\n #vtilt-chat-widget .vtilt-md ol > li { counter-increment: vtilt-ol; }\n #vtilt-chat-widget .vtilt-md ol > li::before {\n content: counter(vtilt-ol) \".\" !important;\n display: inline-block !important;\n position: static !important;\n flex: 0 0 auto;\n min-width: 1.1em;\n line-height: 1.45;\n }\n #vtilt-chat-widget .vtilt-md ol ol { counter-reset: vtilt-ol; }\n #vtilt-chat-widget .vtilt-md li > :is(ul, ol) {\n flex: 1 0 100% !important;\n width: 100% !important;\n margin: 0.25em 0 0 0 !important;\n padding-left: 0.75em !important;\n }\n\n #vtilt-chat-widget .vtilt-md code {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-size: 0.9em;\n background: rgba(0, 0, 0, 0.06);\n padding: 0.1em 0.35em;\n border-radius: 4px;\n }\n #vtilt-chat-widget .vtilt-md pre {\n margin: 0.5em 0;\n padding: 0.5em 0.65em;\n border-radius: 6px;\n background: rgba(0, 0, 0, 0.06);\n overflow-x: auto;\n white-space: pre-wrap;\n }\n #vtilt-chat-widget .vtilt-md pre code { background: none; padding: 0; }\n #vtilt-chat-widget .vtilt-md blockquote {\n margin: 0.5em 0;\n padding-left: 0.75em;\n border-left: 3px solid rgba(0, 0, 0, 0.15);\n }\n #vtilt-chat-widget .vtilt-md a { color: inherit; text-decoration: underline; }\n #vtilt-chat-widget .vtilt-md br { line-height: inherit; }\n\n /* Widget visitor bubble (brand color) */\n #vtilt-chat-widget .vtilt-user-md code { background: rgba(255, 255, 255, 0.2); }\n #vtilt-chat-widget .vtilt-user-md pre { background: rgba(255, 255, 255, 0.15); }\n #vtilt-chat-widget .vtilt-user-md pre code { background: none; }\n #vtilt-chat-widget .vtilt-user-md blockquote { border-left-color: rgba(255, 255, 255, 0.35); }\n";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert stored message content to safe HTML for bubble rendering.
|
|
3
|
+
*/
|
|
4
|
+
export declare function formatMessageBodyHtml(text: string, contentType: "text" | "html" | "attachment", doc: Document | null): string;
|
|
5
|
+
/** Plain-text preview for channel list (strips widgets and HTML). */
|
|
6
|
+
export declare function plainTextMessagePreview(content: string, maxLength?: number): string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Detect content that already contains HTML block-level tags. */
|
|
2
|
+
export declare const HTML_BLOCK_TAG_RE: RegExp;
|
|
3
|
+
/**
|
|
4
|
+
* Best-effort markdown → HTML. Returns input unchanged when it already looks like HTML.
|
|
5
|
+
*/
|
|
6
|
+
export declare function markdownToHtml(src: string): string;
|
|
7
|
+
/** Whether markdown conversion should run before sanitization. */
|
|
8
|
+
export declare function shouldUseMarkdownPipeline(contentType: "text" | "html" | "attachment", cleanText: string): boolean;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { SendChatMessageContent, SendChatMessageOptions } from "../../utils/globals";
|
|
2
|
+
import type { ContentType } from "./types";
|
|
3
|
+
/** Metadata key for markdown stored as `content_type: text`. Must match dashboard `message-rendering.ts`. */
|
|
4
|
+
export declare const RICH_FORMAT_METADATA_KEY = "rich_format";
|
|
5
|
+
export declare const RICH_FORMAT_MARKDOWN = "markdown";
|
|
6
|
+
export interface NormalizedOutgoingMessage {
|
|
7
|
+
content: string;
|
|
8
|
+
content_type: ContentType;
|
|
9
|
+
metadata: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
export declare function isRichUserMessage(message: {
|
|
12
|
+
content_type: string;
|
|
13
|
+
metadata?: Record<string, unknown>;
|
|
14
|
+
}): boolean;
|
|
15
|
+
/** Whether a stored message should use the rich HTML renderer in the widget. */
|
|
16
|
+
export declare function messageUsesRichRendering(message: {
|
|
17
|
+
sender_type: string;
|
|
18
|
+
content_type: string;
|
|
19
|
+
metadata?: Record<string, unknown>;
|
|
20
|
+
}): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Normalize integrator input into stored message fields.
|
|
23
|
+
*/
|
|
24
|
+
export declare function normalizeSendChatMessageContent(input: SendChatMessageContent, options?: SendChatMessageOptions): NormalizedOutgoingMessage | null;
|