@v-tilt/browser 1.12.0 → 1.13.1
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.map +1 -1
- package/dist/array.chat.js +2 -0
- package/dist/array.chat.js.map +1 -0
- package/dist/array.chat.no-external.js +2 -0
- package/dist/array.chat.no-external.js.map +1 -0
- package/dist/array.full.chat.js +2 -0
- package/dist/array.full.chat.js.map +1 -0
- package/dist/array.full.chat.no-external.js +2 -0
- package/dist/array.full.chat.no-external.js.map +1 -0
- package/dist/array.full.js +1 -1
- package/dist/array.full.js.map +1 -1
- package/dist/array.full.no-external.js +2 -0
- package/dist/array.full.no-external.js.map +1 -0
- 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/chat.js +1 -1
- package/dist/chat.js.map +1 -1
- package/dist/entrypoints/all-external-dependencies.d.ts +10 -3
- package/dist/entrypoints/array.chat.d.ts +10 -0
- package/dist/entrypoints/array.chat.no-external.d.ts +6 -0
- package/dist/entrypoints/array.full.chat.d.ts +13 -0
- package/dist/entrypoints/array.full.chat.no-external.d.ts +7 -0
- package/dist/entrypoints/array.full.d.ts +5 -9
- package/dist/entrypoints/array.full.no-external.d.ts +12 -0
- package/dist/entrypoints/module.chat.es.d.ts +7 -0
- package/dist/entrypoints/module.full.chat.es.d.ts +12 -0
- package/dist/entrypoints/module.full.es.d.ts +12 -0
- package/dist/entrypoints/module.no-external.es.d.ts +1 -0
- package/dist/extensions/chat/bubble-drag.d.ts +20 -5
- package/dist/extensions/chat/chat-wrapper.d.ts +8 -2
- package/dist/extensions/chat/chat.d.ts +21 -221
- package/dist/extensions/chat/controller/__tests__/fakes/ably-realtime-fake.d.ts +84 -0
- package/dist/extensions/chat/controller/ably-client.d.ts +160 -0
- package/dist/extensions/chat/controller/ably-handlers.d.ts +95 -0
- package/dist/extensions/chat/controller/ably-token.d.ts +67 -0
- package/dist/extensions/chat/controller/chat-controller.d.ts +194 -0
- package/dist/extensions/chat/controller/message-delivery-status.d.ts +6 -0
- package/dist/extensions/chat/controller/message-order.d.ts +12 -0
- package/dist/extensions/chat/controller/message-stream.d.ts +49 -0
- package/dist/extensions/chat/lib/bubble-offset.d.ts +18 -0
- package/dist/extensions/chat/lib/merge-chat-config.d.ts +3 -0
- package/dist/extensions/chat/normalize-send-content.d.ts +2 -0
- package/dist/extensions/chat/outbox/message-delivery.d.ts +17 -0
- package/dist/extensions/chat/outbox/message-outbox.d.ts +57 -0
- package/dist/extensions/chat/store/chat-store.d.ts +122 -0
- package/dist/extensions/chat/types.d.ts +1 -19
- package/dist/extensions/chat/ui/ChannelItem.d.ts +12 -0
- package/dist/extensions/chat/ui/ChannelListView.d.ts +14 -0
- package/dist/extensions/chat/ui/ChatBubble.d.ts +14 -0
- package/dist/extensions/chat/ui/ChatHeader.d.ts +13 -0
- package/dist/extensions/chat/ui/ChatPanel.d.ts +14 -0
- package/dist/extensions/chat/ui/ChatRoot.d.ts +31 -0
- package/dist/extensions/chat/ui/ClosedBanner.d.ts +7 -0
- package/dist/extensions/chat/ui/ConnectionBanner.d.ts +32 -0
- package/dist/extensions/chat/ui/ConversationView.d.ts +14 -0
- package/dist/extensions/chat/ui/MessageBubble.d.ts +25 -0
- package/dist/extensions/chat/ui/MessageInput.d.ts +14 -0
- package/dist/extensions/chat/ui/MessageList.d.ts +19 -0
- package/dist/extensions/chat/ui/NewMessagesPill.d.ts +13 -0
- package/dist/extensions/chat/ui/Skeletons.d.ts +14 -0
- package/dist/extensions/chat/ui/TypingBubble.d.ts +23 -0
- package/dist/extensions/chat/ui/TypingIndicator.d.ts +12 -0
- package/dist/extensions/chat/ui/WidgetSlot.d.ts +20 -0
- package/dist/extensions/chat/ui/hooks/use-auto-mark-read.d.ts +18 -0
- package/dist/extensions/chat/ui/hooks/use-scroll-preservation.d.ts +33 -0
- package/dist/extensions/chat/ui/icons.d.ts +18 -0
- package/dist/extensions/chat/ui/message-render.d.ts +18 -0
- package/dist/extensions/chat/ui/shadow-styles.d.ts +13 -0
- package/dist/extensions/chat/ui/utils/mobile-body-scroll-lock.d.ts +14 -0
- package/dist/extensions/chat/widget-registry.d.ts +21 -5
- package/dist/extensions/chat/widgets/collect-email.d.ts +4 -2
- package/dist/extensions/chat/widgets/escalate-to-human.d.ts +2 -1
- package/dist/external-scripts-loader.js +1 -1
- package/dist/external-scripts-loader.js.map +1 -1
- package/dist/feature.d.ts +1 -0
- package/dist/lib/merge-vtilt-config.d.ts +9 -0
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/module.chat.d.ts +1788 -0
- package/dist/module.chat.js +2 -0
- package/dist/module.chat.js.map +1 -0
- package/dist/module.d.ts +48 -3
- package/dist/module.full.chat.d.ts +1792 -0
- package/dist/module.full.chat.js +2 -0
- package/dist/module.full.chat.js.map +1 -0
- package/dist/module.full.d.ts +1793 -0
- package/dist/module.full.js +2 -0
- package/dist/module.full.js.map +1 -0
- package/dist/module.js +1 -1
- package/dist/module.js.map +1 -1
- package/dist/module.no-external.d.ts +48 -3
- package/dist/module.no-external.js +1 -1
- package/dist/module.no-external.js.map +1 -1
- package/dist/recorder.js.map +1 -1
- package/dist/snippet-stub-methods.d.ts +14 -0
- package/dist/utils/globals.d.ts +53 -27
- package/dist/utils/index.d.ts +8 -0
- package/dist/vtilt.d.ts +6 -1
- package/dist/web-vitals.js.map +1 -1
- package/package.json +4 -1
- package/dist/extensions/chat/chat-styles.d.ts +0 -27
- package/dist/extensions/chat/message-content-styles.d.ts +0 -1
- package/dist/extensions/chat/message-html.d.ts +0 -6
- package/dist/extensions/chat/message-markdown.d.ts +0 -8
- package/dist/extensions/ga4-proxy.d.ts +0 -59
- package/dist/utils/type-utils.d.ts +0 -4
- package/dist/web-vitals.d.ts +0 -81
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Lazy extension entrypoints bundled into `array.full*` / `module.full*` builds.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Registers factories on `window.__VTiltExtensions__` so `loadExternalDependency`
|
|
5
|
+
* is skipped when these bundles are used with `disable_external_dependency_loading`.
|
|
6
|
+
*
|
|
7
|
+
* **Chat is intentionally excluded** — same as PostHog's `all-external-dependencies.ts`
|
|
8
|
+
* (recorder, surveys, web-vitals, … but not `conversations`). Chat is ~60 KB gzip
|
|
9
|
+
* and not every site uses it. Opt in via:
|
|
10
|
+
* - `array.chat.js` / `module.chat.es.js` (core + chat only)
|
|
11
|
+
* - `array.full.chat.js` / `module.full.chat.es.js` (recorder + vitals + chat)
|
|
12
|
+
* - side-effect `import "./chat"` before `vt.init()` (bundler)
|
|
6
13
|
*/
|
|
7
14
|
import "./recorder";
|
|
8
15
|
import "./web-vitals";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core snippet plus chat — equivalent to loading `array.js` and `chat.js`
|
|
3
|
+
* together. Chat registers on `__VTiltExtensions__` at parse time so there is
|
|
4
|
+
* no second network request for `chat.js`.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* <script src="https://cdn.example.com/dist/array.chat.js"></script>
|
|
8
|
+
*/
|
|
9
|
+
import "./chat";
|
|
10
|
+
import "./array";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full IIFE bundle with chat pre-registered on `__VTiltExtensions__`.
|
|
3
|
+
*
|
|
4
|
+
* Use when the chat widget must be ready immediately (hero CTAs, programmatic
|
|
5
|
+
* `vt.sendChatMessage()` on first paint) without a second script request.
|
|
6
|
+
*
|
|
7
|
+
* PostHog equivalent: load `array.js` and side-effect-import `conversations.js`,
|
|
8
|
+
* or use their bundler pattern:
|
|
9
|
+
* import "posthog-js/dist/conversations"
|
|
10
|
+
*/
|
|
11
|
+
import "./all-external-dependencies";
|
|
12
|
+
import "./chat";
|
|
13
|
+
import "./array";
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Full
|
|
3
|
-
*
|
|
4
|
-
* Same as array.ts but includes all external dependencies bundled in.
|
|
5
|
-
* This is useful for users who want a single script tag without lazy loading.
|
|
2
|
+
* Full IIFE bundle — core SDK plus session recording and web vitals
|
|
3
|
+
* pre-registered on `__VTiltExtensions__`.
|
|
6
4
|
*
|
|
7
5
|
* Usage:
|
|
8
6
|
* <script src="https://cdn.example.com/dist/array.full.js"></script>
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* - Session recording (rrweb)
|
|
14
|
-
* - Web Vitals tracking
|
|
8
|
+
* Does **not** include chat (PostHog keeps conversations out of `array.full` too).
|
|
9
|
+
* For chat without a second request, use `array.chat.js` (core + chat) or
|
|
10
|
+
* `array.full.chat.js` (also includes recorder and web vitals).
|
|
15
11
|
*/
|
|
16
12
|
import "./all-external-dependencies";
|
|
17
13
|
import "./array";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full IIFE bundle with external script loading disabled at runtime.
|
|
3
|
+
*
|
|
4
|
+
* Same as `array.full.ts` but intended for use with
|
|
5
|
+
* `disable_external_dependency_loading: true` — extensions are already on
|
|
6
|
+
* `__VTiltExtensions__`. PostHog equivalent: `array.full.no-external.ts`.
|
|
7
|
+
*
|
|
8
|
+
* Does not include chat; use `array.full.chat.no-external.ts` when chat must
|
|
9
|
+
* be pre-registered without a separate network request.
|
|
10
|
+
*/
|
|
11
|
+
import "./all-external-dependencies";
|
|
12
|
+
import "./array.no-external";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full ESM bundle with chat pre-registered — no extra `chat.js` fetch.
|
|
3
|
+
*
|
|
4
|
+
* PostHog equivalent:
|
|
5
|
+
* import "posthog-js/dist/conversations"
|
|
6
|
+
* import posthog from "posthog-js/dist/module.no-external"
|
|
7
|
+
*/
|
|
8
|
+
import "./all-external-dependencies";
|
|
9
|
+
import "./chat";
|
|
10
|
+
import vt from "./module.es";
|
|
11
|
+
export * from "./module.es";
|
|
12
|
+
export default vt;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full ESM bundle — core SDK plus recorder and web-vitals pre-registered on
|
|
3
|
+
* `__VTiltExtensions__`. Same role as PostHog's `module.full.es.ts`.
|
|
4
|
+
*
|
|
5
|
+
* Chat is not included (see `module.full.chat.es.ts` or side-effect-import
|
|
6
|
+
* `./chat` before init). Matches PostHog keeping `conversations` out of the
|
|
7
|
+
* default full bundle because of bundle size.
|
|
8
|
+
*/
|
|
9
|
+
import "./all-external-dependencies";
|
|
10
|
+
import vt from "./module.es";
|
|
11
|
+
export * from "./module.es";
|
|
12
|
+
export default vt;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { VTilt } from "../vtilt";
|
|
2
2
|
export * from "../types";
|
|
3
|
+
export { VTILT_SNIPPET_STUB_METHODS, VTILT_SNIPPET_STUB_METHOD_NAMES, type VtiltSnippetStubMethodName, } from "../snippet-stub-methods";
|
|
3
4
|
export declare const vt: import("../vtilt").VTilt;
|
|
4
5
|
export default vt;
|
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
interface DraggableOptions {
|
|
9
9
|
/** Element that initiates the drag (defaults to the target element itself) */
|
|
10
10
|
handle?: HTMLElement;
|
|
11
|
+
/**
|
|
12
|
+
* Fired after the element settles at a new position: initial restore
|
|
13
|
+
* from localStorage, drag end, and viewport resize re-clamp. The chat
|
|
14
|
+
* widget uses this to recompute the panel's opening direction so the
|
|
15
|
+
* panel always extends into whichever side of the viewport has more
|
|
16
|
+
* room (see `usePanelAnchor` in `ui/ChatRoot.tsx`).
|
|
17
|
+
*/
|
|
18
|
+
onPositionChange?: () => void;
|
|
11
19
|
}
|
|
12
20
|
interface DragHandle {
|
|
13
21
|
destroy: () => void;
|
|
@@ -15,13 +23,20 @@ interface DragHandle {
|
|
|
15
23
|
/**
|
|
16
24
|
* Make a fixed-position element draggable.
|
|
17
25
|
*
|
|
18
|
-
* @param el
|
|
19
|
-
* @param opts.handle
|
|
20
|
-
*
|
|
21
|
-
*
|
|
26
|
+
* @param el The element to reposition via translate3d.
|
|
27
|
+
* @param opts.handle Optional drag handle element (e.g. bubble
|
|
28
|
+
* inside a container). Pointer events listen
|
|
29
|
+
* on the handle; transform applies to `el`.
|
|
30
|
+
* Defaults to `el` itself.
|
|
31
|
+
* @param opts.onPositionChange Called after the element settles —
|
|
32
|
+
* initial restore, drag end, and viewport
|
|
33
|
+
* resize re-clamp. Used by the chat widget
|
|
34
|
+
* to recompute the panel's opening
|
|
35
|
+
* direction.
|
|
22
36
|
*
|
|
23
37
|
* - Restores persisted position from localStorage on attach.
|
|
24
|
-
* - Persists position on drag end
|
|
38
|
+
* - Persists position on drag end (and on resize when the clamp had to
|
|
39
|
+
* correct an out-of-bounds value, so the next page load is consistent).
|
|
25
40
|
* - Distinguishes click from drag via a 5px movement threshold.
|
|
26
41
|
* When dragged, the subsequent `click` event on the handle is suppressed once.
|
|
27
42
|
* - Re-clamps on viewport resize.
|
|
@@ -50,6 +50,7 @@ export declare class ChatWrapper extends LazyFeature<ChatConfig, LazyLoadedChatI
|
|
|
50
50
|
private _connectionCallbacks;
|
|
51
51
|
/** True after vt.showChat() so preloaded widget stays visible after replacing the lightweight bubble. */
|
|
52
52
|
private _bubbleExplicitShow;
|
|
53
|
+
private _unreadPollIntervalId;
|
|
53
54
|
constructor(instance: VTilt, config?: ChatConfig);
|
|
54
55
|
static extractConfig(config: VTiltConfig): ChatConfig;
|
|
55
56
|
get isEnabled(): boolean;
|
|
@@ -79,19 +80,24 @@ export declare class ChatWrapper extends LazyFeature<ChatConfig, LazyLoadedChatI
|
|
|
79
80
|
selectChannel(channelId: string): void;
|
|
80
81
|
createChannel(): void;
|
|
81
82
|
goToChannelList(): void;
|
|
82
|
-
sendMessage(content: SendChatMessageContent, options?: SendChatMessageOptions): void
|
|
83
|
+
sendMessage(content: SendChatMessageContent, options?: SendChatMessageOptions): Promise<void>;
|
|
83
84
|
markAsRead(): void;
|
|
84
85
|
registerWidget(definition: Parameters<NonNullable<LazyLoadedChatInterface["registerWidget"]>>[0]): void;
|
|
85
86
|
onMessage(callback: MessageCallback): Unsubscribe;
|
|
86
87
|
onTyping(callback: TypingCallback): Unsubscribe;
|
|
87
88
|
onConnectionChange(callback: ConnectionCallback): Unsubscribe;
|
|
88
|
-
|
|
89
|
+
private _isBubbleDraggable;
|
|
89
90
|
getMergedConfig(): ChatConfig;
|
|
91
|
+
/** Apply corner + inset to the lightweight bubble and/or loaded shadow host. */
|
|
92
|
+
private _syncBubbleLayout;
|
|
90
93
|
destroy(): void;
|
|
91
94
|
private _fetchServerSettings;
|
|
92
95
|
private _fetchUnreadForBubble;
|
|
93
96
|
private _showBubble;
|
|
94
97
|
private _onVisibilityChange;
|
|
98
|
+
private _shouldPollUnread;
|
|
99
|
+
private _startUnreadPoll;
|
|
100
|
+
private _stopUnreadPoll;
|
|
95
101
|
private _schedulePreload;
|
|
96
102
|
private _lazyLoadAndThen;
|
|
97
103
|
}
|
|
@@ -1,223 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Chat Widget
|
|
2
|
+
* Chat Widget — thin re-export shim.
|
|
3
|
+
*
|
|
4
|
+
* The entire chat implementation moved to a layered architecture as part of
|
|
5
|
+
* the May 2026 Preact rewrite. The previous monolithic ~3800-line class
|
|
6
|
+
* (`LazyLoadedChat`) is now decomposed into:
|
|
7
|
+
*
|
|
8
|
+
* - controller/ (chat-controller, ably-client, ably-handlers, ably-token,
|
|
9
|
+
* message-stream) — orchestration and Ably transport
|
|
10
|
+
* - store/ (chat-store) — signal-based reactive state
|
|
11
|
+
* - ui/ (ChatRoot.tsx + components + hooks)
|
|
12
|
+
* — Preact components rendered in a Shadow DOM
|
|
13
|
+
* - widgets/ (collect-email.tsx, escalate-to-human.tsx)
|
|
14
|
+
* — built-in tool widgets (Preact)
|
|
15
|
+
*
|
|
16
|
+
* See `docs/modules/chat/widget-architecture.md` for the full architecture.
|
|
17
|
+
*
|
|
18
|
+
* This file remains so external callers (`entrypoints/chat.ts`,
|
|
19
|
+
* `ChatWrapper`, tests) can keep importing `LazyLoadedChat` from
|
|
20
|
+
* `extensions/chat/chat` unchanged. New code should import the controller
|
|
21
|
+
* and store directly from their own modules.
|
|
3
22
|
*/
|
|
4
|
-
|
|
5
|
-
import { type ChatConfig, type ChatChannel, type ChatChannelSummary, type ChatWidgetView, type LazyLoadedChatInterface, type SendChatMessageContent, type SendChatMessageOptions } from "../../utils/globals";
|
|
6
|
-
import { type MessageCallback, type TypingCallback, type ConnectionCallback, type Unsubscribe } from "./types";
|
|
7
|
-
import { type WidgetContext } from "./widget-registry";
|
|
8
|
-
export declare class LazyLoadedChat implements LazyLoadedChatInterface {
|
|
9
|
-
private _instance;
|
|
10
|
-
private _config;
|
|
11
|
-
private _state;
|
|
12
|
-
private _container;
|
|
13
|
-
private _widget;
|
|
14
|
-
private _bubble;
|
|
15
|
-
private _bubbleDragHandle;
|
|
16
|
-
private _chatTheme;
|
|
17
|
-
private _widgetResizeCleanup;
|
|
18
|
-
private _ably;
|
|
19
|
-
private _notificationsChannel;
|
|
20
|
-
private _ablyChannel;
|
|
21
|
-
private _typingChannel;
|
|
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;
|
|
28
|
-
private _messageCallbacks;
|
|
29
|
-
private _typingCallbacks;
|
|
30
|
-
private _connectionCallbacks;
|
|
31
|
-
private _typingDebounce;
|
|
32
|
-
private _isUserTyping;
|
|
33
|
-
private _initialUserReadAt;
|
|
34
|
-
private _isMarkingRead;
|
|
35
|
-
private _widgetRegistry;
|
|
36
|
-
/** Tracks whether show() was explicitly called (vs open() temporarily revealing the container). */
|
|
37
|
-
private _bubbleExplicitShow;
|
|
38
|
-
constructor(instance: VTilt, config?: ChatConfig);
|
|
39
|
-
get isOpen(): boolean;
|
|
40
|
-
get isConnected(): boolean;
|
|
41
|
-
get isLoading(): boolean;
|
|
42
|
-
get unreadCount(): number;
|
|
43
|
-
get channel(): ChatChannel | null;
|
|
44
|
-
get channels(): ChatChannelSummary[];
|
|
45
|
-
get currentView(): ChatWidgetView;
|
|
46
|
-
private get _theme();
|
|
47
|
-
private get _distinctId();
|
|
48
|
-
/**
|
|
49
|
-
* Track a chat message event via the SDK capture pipeline.
|
|
50
|
-
* Gated by remote config chatTracking settings.
|
|
51
|
-
*
|
|
52
|
-
* - User messages: only if chatTracking.trackUserMessages is enabled
|
|
53
|
-
* - AI/Agent messages: only if chatTracking.trackAgentMessages is enabled
|
|
54
|
-
*/
|
|
55
|
-
private _trackChatMessage;
|
|
56
|
-
open(): void;
|
|
57
|
-
close(): void;
|
|
58
|
-
toggle(): void;
|
|
59
|
-
show(): void;
|
|
60
|
-
hide(): void;
|
|
61
|
-
getChannels(): Promise<void>;
|
|
62
|
-
selectChannel(channelId: string): Promise<void>;
|
|
63
|
-
createChannel(): Promise<void>;
|
|
64
|
-
goToChannelList(): Promise<void>;
|
|
65
|
-
sendMessage(content: SendChatMessageContent, options?: SendChatMessageOptions): Promise<void>;
|
|
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;
|
|
73
|
-
private _autoMarkAsRead;
|
|
74
|
-
private _isMessageReadByUser;
|
|
75
|
-
/**
|
|
76
|
-
* Calculate unread count based on messages and user_last_read_at
|
|
77
|
-
* (unread agent/AI messages for the user)
|
|
78
|
-
*/
|
|
79
|
-
private _calculateUnreadCount;
|
|
80
|
-
onMessage(callback: MessageCallback): Unsubscribe;
|
|
81
|
-
onTyping(callback: TypingCallback): Unsubscribe;
|
|
82
|
-
onConnectionChange(callback: ConnectionCallback): Unsubscribe;
|
|
83
|
-
destroy(): void;
|
|
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;
|
|
116
|
-
private _handleNewMessage;
|
|
117
|
-
private _handleTypingEvent;
|
|
118
|
-
private _handleReadCursorEvent;
|
|
119
|
-
private _notifyConnectionChange;
|
|
120
|
-
private _createUI;
|
|
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;
|
|
128
|
-
private _handleUserTyping;
|
|
129
|
-
private _sendTypingIndicator;
|
|
130
|
-
private _handleSend;
|
|
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;
|
|
157
|
-
private _updateHeader;
|
|
158
|
-
private _getChannelListHTML;
|
|
159
|
-
private _getChannelItemHTML;
|
|
160
|
-
private _getConversationHTML;
|
|
161
|
-
private _attachChannelListListeners;
|
|
162
|
-
private _attachConversationListeners;
|
|
163
|
-
private _formatRelativeTime;
|
|
164
|
-
private _renderMessages;
|
|
165
|
-
private _getMessageHTML;
|
|
166
|
-
private _isMessageReadByAgent;
|
|
167
|
-
/**
|
|
168
|
-
* Handle streaming AI response
|
|
169
|
-
* Industry standard: Real-time streaming with progressive UI updates
|
|
170
|
-
*/
|
|
171
|
-
private _handleStreamingResponse;
|
|
172
|
-
/**
|
|
173
|
-
* Remove temporary message by ID
|
|
174
|
-
*/
|
|
175
|
-
private _removeTempMessage;
|
|
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;
|
|
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;
|
|
222
|
-
private _formatTime;
|
|
223
|
-
}
|
|
23
|
+
export { ChatController as LazyLoadedChat } from "./controller/chat-controller";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory fake of Ably's `BaseRealtime` surface used by AblyClient tests.
|
|
3
|
+
*
|
|
4
|
+
* Only models the parts the chat layer actually touches:
|
|
5
|
+
* - `connection` with `state` + `.on(event, handler)` events
|
|
6
|
+
* for "connected" | "disconnected" | "suspended" | "failed"
|
|
7
|
+
* - `auth.clientId`, `auth.authorize(token)`
|
|
8
|
+
* - `channels.get(name)` returning a FakeRealtimeChannel with attach/detach,
|
|
9
|
+
* subscribe/unsubscribe/publish, and a `state` machine that can be driven
|
|
10
|
+
* programmatically.
|
|
11
|
+
* - `channels.release(name)`
|
|
12
|
+
* - `connect()` / `close()`
|
|
13
|
+
*
|
|
14
|
+
* The Ably typings are wide and surface lots of unrelated APIs; rather than
|
|
15
|
+
* fight them we expose this fake with its own minimal types and only cast
|
|
16
|
+
* to `BaseRealtime` at the construction site.
|
|
17
|
+
*/
|
|
18
|
+
import type { ChannelState, ChannelStateChange, TokenRequest } from "ably";
|
|
19
|
+
import type { BaseRealtime } from "ably/modular";
|
|
20
|
+
type Handler<T> = (event: T) => void;
|
|
21
|
+
export declare class FakeRealtimeChannel {
|
|
22
|
+
readonly name: string;
|
|
23
|
+
state: ChannelState;
|
|
24
|
+
readonly subscriptions: Map<string, ((msg: {
|
|
25
|
+
data: unknown;
|
|
26
|
+
}) => void)[]>;
|
|
27
|
+
readonly published: Array<{
|
|
28
|
+
event: string;
|
|
29
|
+
data: unknown;
|
|
30
|
+
}>;
|
|
31
|
+
attachCalls: number;
|
|
32
|
+
detachCalls: number;
|
|
33
|
+
private _stateListeners;
|
|
34
|
+
private _attachResolvers;
|
|
35
|
+
constructor(name: string);
|
|
36
|
+
attach(): Promise<void>;
|
|
37
|
+
/** Test helper to resolve a pending attach. */
|
|
38
|
+
resolveAttach(): void;
|
|
39
|
+
/** Test helper to reject a pending attach. */
|
|
40
|
+
rejectAttach(err: unknown): void;
|
|
41
|
+
detach(): Promise<void>;
|
|
42
|
+
setState(next: ChannelState): void;
|
|
43
|
+
on(handler: Handler<ChannelStateChange>): void;
|
|
44
|
+
off(handler: Handler<ChannelStateChange>): void;
|
|
45
|
+
subscribe(event: string, callback: (msg: {
|
|
46
|
+
data: unknown;
|
|
47
|
+
}) => void): void;
|
|
48
|
+
unsubscribe(): void;
|
|
49
|
+
publish(event: string, data: unknown): Promise<void>;
|
|
50
|
+
/** Drive an inbound message for tests. */
|
|
51
|
+
emit(event: string, data: unknown): void;
|
|
52
|
+
}
|
|
53
|
+
export declare class FakeAuth {
|
|
54
|
+
clientId: string | null;
|
|
55
|
+
authorizeCalls: number;
|
|
56
|
+
shouldFailAuthorize: boolean;
|
|
57
|
+
authorize(token: TokenRequest): Promise<void>;
|
|
58
|
+
}
|
|
59
|
+
export type ConnectionState = "initialized" | "connecting" | "connected" | "disconnected" | "suspended" | "closing" | "closed" | "failed";
|
|
60
|
+
export declare class FakeConnection {
|
|
61
|
+
state: ConnectionState;
|
|
62
|
+
private _listeners;
|
|
63
|
+
on(event: string, handler: Handler<unknown>): void;
|
|
64
|
+
emit(event: ConnectionState): void;
|
|
65
|
+
}
|
|
66
|
+
export declare class FakeChannels {
|
|
67
|
+
releaseCalls: string[];
|
|
68
|
+
private _registry;
|
|
69
|
+
get(name: string): FakeRealtimeChannel;
|
|
70
|
+
release(name: string): void;
|
|
71
|
+
}
|
|
72
|
+
export declare class FakeBaseRealtime {
|
|
73
|
+
connectCalls: number;
|
|
74
|
+
closeCalls: number;
|
|
75
|
+
readonly connection: FakeConnection;
|
|
76
|
+
readonly auth: FakeAuth;
|
|
77
|
+
readonly channels: FakeChannels;
|
|
78
|
+
constructor();
|
|
79
|
+
connect(): void;
|
|
80
|
+
close(): void;
|
|
81
|
+
/** Cast helper to satisfy AblyClient's constructor signature. */
|
|
82
|
+
asBaseRealtime(): BaseRealtime;
|
|
83
|
+
}
|
|
84
|
+
export {};
|