@alliance-droid/chat-widget 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/README.md +102 -0
- package/dist/client/connection.d.ts +39 -0
- package/dist/client/connection.js +198 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +6 -0
- package/dist/components/ChatInput.svelte +73 -0
- package/dist/components/ChatInput.svelte.d.ts +9 -0
- package/dist/components/ChatPanel.svelte +117 -0
- package/dist/components/ChatPanel.svelte.d.ts +20 -0
- package/dist/components/ChatWidget.svelte +160 -0
- package/dist/components/ChatWidget.svelte.d.ts +4 -0
- package/dist/components/MessageBubble.svelte +60 -0
- package/dist/components/MessageBubble.svelte.d.ts +8 -0
- package/dist/components/MessageList.svelte +58 -0
- package/dist/components/MessageList.svelte.d.ts +10 -0
- package/dist/components/SupportChat.svelte +133 -0
- package/dist/components/SupportChat.svelte.d.ts +4 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +17 -0
- package/dist/server/ai/anthropic.d.ts +16 -0
- package/dist/server/ai/anthropic.js +109 -0
- package/dist/server/ai/openai.d.ts +16 -0
- package/dist/server/ai/openai.js +112 -0
- package/dist/server/ai/provider.d.ts +21 -0
- package/dist/server/ai/provider.js +6 -0
- package/dist/server/ai/xai.d.ts +16 -0
- package/dist/server/ai/xai.js +113 -0
- package/dist/server/escalation.d.ts +18 -0
- package/dist/server/escalation.js +61 -0
- package/dist/server/handler.d.ts +8 -0
- package/dist/server/handler.js +235 -0
- package/dist/server/index.d.ts +11 -0
- package/dist/server/index.js +10 -0
- package/dist/server/session.d.ts +52 -0
- package/dist/server/session.js +115 -0
- package/dist/stores/chat.d.ts +18 -0
- package/dist/stores/chat.js +40 -0
- package/dist/stores/connection.d.ts +20 -0
- package/dist/stores/connection.js +52 -0
- package/dist/types.d.ts +79 -0
- package/dist/types.js +4 -0
- package/package.json +73 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Manager
|
|
3
|
+
*
|
|
4
|
+
* In-memory session storage (Redis adapter TODO)
|
|
5
|
+
*/
|
|
6
|
+
export class SessionManager {
|
|
7
|
+
sessions = new Map();
|
|
8
|
+
ttl;
|
|
9
|
+
cleanupInterval;
|
|
10
|
+
constructor(ttlMs = 24 * 60 * 60 * 1000) {
|
|
11
|
+
this.ttl = ttlMs;
|
|
12
|
+
// Cleanup expired sessions every hour
|
|
13
|
+
this.cleanupInterval = setInterval(() => this.cleanup(), 60 * 60 * 1000);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a new session
|
|
17
|
+
*/
|
|
18
|
+
create(userId, userMeta) {
|
|
19
|
+
const id = this.generateId();
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
const session = {
|
|
22
|
+
id,
|
|
23
|
+
status: 'ai',
|
|
24
|
+
messages: [],
|
|
25
|
+
userId,
|
|
26
|
+
userMeta,
|
|
27
|
+
createdAt: now,
|
|
28
|
+
updatedAt: now
|
|
29
|
+
};
|
|
30
|
+
this.sessions.set(id, session);
|
|
31
|
+
return session;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get a session by ID
|
|
35
|
+
*/
|
|
36
|
+
get(id) {
|
|
37
|
+
const session = this.sessions.get(id);
|
|
38
|
+
if (session && this.isExpired(session)) {
|
|
39
|
+
this.sessions.delete(id);
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return session;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Add a message to a session
|
|
46
|
+
*/
|
|
47
|
+
addMessage(sessionId, message) {
|
|
48
|
+
const session = this.get(sessionId);
|
|
49
|
+
if (!session)
|
|
50
|
+
return;
|
|
51
|
+
session.messages.push(message);
|
|
52
|
+
session.updatedAt = Date.now();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Update session status
|
|
56
|
+
*/
|
|
57
|
+
setStatus(sessionId, status) {
|
|
58
|
+
const session = this.get(sessionId);
|
|
59
|
+
if (!session)
|
|
60
|
+
return;
|
|
61
|
+
session.status = status;
|
|
62
|
+
session.updatedAt = Date.now();
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Assign a support agent to a session
|
|
66
|
+
*/
|
|
67
|
+
assignSupport(sessionId, agentId) {
|
|
68
|
+
const session = this.get(sessionId);
|
|
69
|
+
if (!session)
|
|
70
|
+
return false;
|
|
71
|
+
// Don't reassign if already assigned
|
|
72
|
+
if (session.supportAgentId)
|
|
73
|
+
return false;
|
|
74
|
+
session.supportAgentId = agentId;
|
|
75
|
+
session.status = 'human';
|
|
76
|
+
session.updatedAt = Date.now();
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Delete a session
|
|
81
|
+
*/
|
|
82
|
+
delete(id) {
|
|
83
|
+
this.sessions.delete(id);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Cleanup expired sessions
|
|
87
|
+
*/
|
|
88
|
+
cleanup() {
|
|
89
|
+
const now = Date.now();
|
|
90
|
+
for (const [id, session] of this.sessions) {
|
|
91
|
+
if (this.isExpired(session)) {
|
|
92
|
+
this.sessions.delete(id);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Check if session is expired
|
|
98
|
+
*/
|
|
99
|
+
isExpired(session) {
|
|
100
|
+
return Date.now() - session.updatedAt > this.ttl;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Generate unique session ID
|
|
104
|
+
*/
|
|
105
|
+
generateId() {
|
|
106
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}`;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Cleanup on shutdown
|
|
110
|
+
*/
|
|
111
|
+
destroy() {
|
|
112
|
+
clearInterval(this.cleanupInterval);
|
|
113
|
+
this.sessions.clear();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat Store
|
|
3
|
+
*
|
|
4
|
+
* Manages chat messages, status, and typing indicators
|
|
5
|
+
*/
|
|
6
|
+
import type { Message, ChatStatus } from '../types.js';
|
|
7
|
+
export declare const chatStore: {
|
|
8
|
+
messages: import("svelte/store").Writable<Message[]>;
|
|
9
|
+
status: import("svelte/store").Writable<ChatStatus>;
|
|
10
|
+
isTyping: import("svelte/store").Writable<boolean>;
|
|
11
|
+
typingSender: import("svelte/store").Writable<"ai" | "human" | null>;
|
|
12
|
+
addMessage(message: Message): void;
|
|
13
|
+
updateMessage(id: string, updates: Partial<Message>): void;
|
|
14
|
+
setStatus(newStatus: ChatStatus): void;
|
|
15
|
+
setTyping(typing: boolean, sender?: "ai" | "human" | null): void;
|
|
16
|
+
clear(): void;
|
|
17
|
+
messageCount: import("svelte/store").Readable<number>;
|
|
18
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat Store
|
|
3
|
+
*
|
|
4
|
+
* Manages chat messages, status, and typing indicators
|
|
5
|
+
*/
|
|
6
|
+
import { writable, derived } from 'svelte/store';
|
|
7
|
+
function createChatStore() {
|
|
8
|
+
const messages = writable([]);
|
|
9
|
+
const status = writable('idle');
|
|
10
|
+
const isTyping = writable(false);
|
|
11
|
+
const typingSender = writable(null);
|
|
12
|
+
return {
|
|
13
|
+
messages,
|
|
14
|
+
status,
|
|
15
|
+
isTyping,
|
|
16
|
+
typingSender,
|
|
17
|
+
addMessage(message) {
|
|
18
|
+
messages.update(msgs => [...msgs, message]);
|
|
19
|
+
},
|
|
20
|
+
updateMessage(id, updates) {
|
|
21
|
+
messages.update(msgs => msgs.map(msg => (msg.id === id ? { ...msg, ...updates } : msg)));
|
|
22
|
+
},
|
|
23
|
+
setStatus(newStatus) {
|
|
24
|
+
status.set(newStatus);
|
|
25
|
+
},
|
|
26
|
+
setTyping(typing, sender = null) {
|
|
27
|
+
isTyping.set(typing);
|
|
28
|
+
typingSender.set(sender);
|
|
29
|
+
},
|
|
30
|
+
clear() {
|
|
31
|
+
messages.set([]);
|
|
32
|
+
status.set('idle');
|
|
33
|
+
isTyping.set(false);
|
|
34
|
+
typingSender.set(null);
|
|
35
|
+
},
|
|
36
|
+
// Derived store for message count
|
|
37
|
+
messageCount: derived(messages, $messages => $messages.length)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export const chatStore = createChatStore();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection Store
|
|
3
|
+
*
|
|
4
|
+
* Manages WebSocket connection state
|
|
5
|
+
*/
|
|
6
|
+
import type { ConnectionState } from '../types.js';
|
|
7
|
+
export declare const connectionStore: {
|
|
8
|
+
state: import("svelte/store").Writable<ConnectionState>;
|
|
9
|
+
sessionId: import("svelte/store").Writable<string | null>;
|
|
10
|
+
error: import("svelte/store").Writable<string | null>;
|
|
11
|
+
reconnectAttempts: import("svelte/store").Writable<number>;
|
|
12
|
+
setConnected(newSessionId: string): void;
|
|
13
|
+
setConnecting(): void;
|
|
14
|
+
setReconnecting(): void;
|
|
15
|
+
setDisconnected(errorMessage?: string): void;
|
|
16
|
+
setError(errorMessage: string): void;
|
|
17
|
+
clearError(): void;
|
|
18
|
+
reset(): void;
|
|
19
|
+
isConnected: import("svelte/store").Readable<boolean>;
|
|
20
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection Store
|
|
3
|
+
*
|
|
4
|
+
* Manages WebSocket connection state
|
|
5
|
+
*/
|
|
6
|
+
import { writable, derived } from 'svelte/store';
|
|
7
|
+
function createConnectionStore() {
|
|
8
|
+
const state = writable('disconnected');
|
|
9
|
+
const sessionId = writable(null);
|
|
10
|
+
const error = writable(null);
|
|
11
|
+
const reconnectAttempts = writable(0);
|
|
12
|
+
return {
|
|
13
|
+
state,
|
|
14
|
+
sessionId,
|
|
15
|
+
error,
|
|
16
|
+
reconnectAttempts,
|
|
17
|
+
setConnected(newSessionId) {
|
|
18
|
+
state.set('connected');
|
|
19
|
+
sessionId.set(newSessionId);
|
|
20
|
+
error.set(null);
|
|
21
|
+
reconnectAttempts.set(0);
|
|
22
|
+
},
|
|
23
|
+
setConnecting() {
|
|
24
|
+
state.set('connecting');
|
|
25
|
+
},
|
|
26
|
+
setReconnecting() {
|
|
27
|
+
state.set('reconnecting');
|
|
28
|
+
reconnectAttempts.update(n => n + 1);
|
|
29
|
+
},
|
|
30
|
+
setDisconnected(errorMessage) {
|
|
31
|
+
state.set('disconnected');
|
|
32
|
+
if (errorMessage) {
|
|
33
|
+
error.set(errorMessage);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
setError(errorMessage) {
|
|
37
|
+
error.set(errorMessage);
|
|
38
|
+
},
|
|
39
|
+
clearError() {
|
|
40
|
+
error.set(null);
|
|
41
|
+
},
|
|
42
|
+
reset() {
|
|
43
|
+
state.set('disconnected');
|
|
44
|
+
sessionId.set(null);
|
|
45
|
+
error.set(null);
|
|
46
|
+
reconnectAttempts.set(0);
|
|
47
|
+
},
|
|
48
|
+
// Derived store for connection status
|
|
49
|
+
isConnected: derived(state, $state => $state === 'connected')
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export const connectionStore = createConnectionStore();
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat Widget Types
|
|
3
|
+
*/
|
|
4
|
+
export interface Message {
|
|
5
|
+
id: string;
|
|
6
|
+
content: string;
|
|
7
|
+
sender: 'user' | 'ai' | 'human' | 'system';
|
|
8
|
+
timestamp: number;
|
|
9
|
+
streaming?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface ClientMessage {
|
|
12
|
+
type: 'message' | 'escalate' | 'typing' | 'read' | 'join';
|
|
13
|
+
sessionId?: string;
|
|
14
|
+
content?: string;
|
|
15
|
+
metadata?: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
export interface ServerMessage {
|
|
18
|
+
type: 'message' | 'typing' | 'status' | 'error' | 'escalated' | 'connected' | 'auth_success' | 'auth_error';
|
|
19
|
+
id?: string;
|
|
20
|
+
content?: string;
|
|
21
|
+
sender?: 'user' | 'ai' | 'human' | 'system';
|
|
22
|
+
timestamp?: number;
|
|
23
|
+
streaming?: boolean;
|
|
24
|
+
sessionId?: string;
|
|
25
|
+
error?: string;
|
|
26
|
+
}
|
|
27
|
+
export type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';
|
|
28
|
+
export type ChatStatus = 'idle' | 'ai' | 'escalating' | 'human' | 'closed';
|
|
29
|
+
export interface ChatWidgetProps {
|
|
30
|
+
serverUrl: string;
|
|
31
|
+
apiKey?: string;
|
|
32
|
+
reconnect?: boolean;
|
|
33
|
+
reconnectInterval?: number;
|
|
34
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
35
|
+
position?: 'bottom-right' | 'bottom-left';
|
|
36
|
+
greeting?: string;
|
|
37
|
+
placeholder?: string;
|
|
38
|
+
fabIcon?: string;
|
|
39
|
+
enableSounds?: boolean;
|
|
40
|
+
enableEscalation?: boolean;
|
|
41
|
+
escalationLabel?: string;
|
|
42
|
+
userId?: string;
|
|
43
|
+
userMeta?: Record<string, unknown>;
|
|
44
|
+
class?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface SupportChatProps {
|
|
47
|
+
serverUrl: string;
|
|
48
|
+
sessionId: string;
|
|
49
|
+
class?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface ChatServerConfig {
|
|
52
|
+
ai: AIProviderConfig;
|
|
53
|
+
escalation?: EscalationConfig;
|
|
54
|
+
storage?: 'memory' | 'redis';
|
|
55
|
+
sessionTTL?: number;
|
|
56
|
+
}
|
|
57
|
+
export interface AIProviderConfig {
|
|
58
|
+
provider: 'xai' | 'openai' | 'anthropic';
|
|
59
|
+
apiKey: string;
|
|
60
|
+
model?: string;
|
|
61
|
+
systemPrompt?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface EscalationConfig {
|
|
64
|
+
telegram: {
|
|
65
|
+
botToken: string;
|
|
66
|
+
chatId: string;
|
|
67
|
+
};
|
|
68
|
+
joinUrl: (sessionId: string) => string;
|
|
69
|
+
}
|
|
70
|
+
export interface ChatSession {
|
|
71
|
+
id: string;
|
|
72
|
+
status: ChatStatus;
|
|
73
|
+
messages: Message[];
|
|
74
|
+
userId?: string;
|
|
75
|
+
userMeta?: Record<string, unknown>;
|
|
76
|
+
supportAgentId?: string;
|
|
77
|
+
createdAt: number;
|
|
78
|
+
updatedAt: number;
|
|
79
|
+
}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alliance-droid/chat-widget",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Svelte chat widget with AI support and human escalation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite dev",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"build:package": "svelte-kit sync && svelte-package && publint",
|
|
10
|
+
"preview": "vite preview",
|
|
11
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
12
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"test:watch": "vitest",
|
|
15
|
+
"prepublishOnly": "npm run build:package"
|
|
16
|
+
},
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"svelte": "./dist/index.js",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./server": {
|
|
24
|
+
"types": "./dist/server/index.d.ts",
|
|
25
|
+
"default": "./dist/server/index.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"svelte": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"svelte": "^5.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@sveltejs/adapter-auto": "^4.0.0",
|
|
38
|
+
"@sveltejs/kit": "^2.16.0",
|
|
39
|
+
"@sveltejs/package": "^2.3.0",
|
|
40
|
+
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
|
41
|
+
"@types/node": "^22.0.0",
|
|
42
|
+
"@types/ws": "^8.5.0",
|
|
43
|
+
"autoprefixer": "^10.4.0",
|
|
44
|
+
"postcss": "^8.4.0",
|
|
45
|
+
"publint": "^0.3.0",
|
|
46
|
+
"svelte": "^5.0.0",
|
|
47
|
+
"svelte-check": "^4.0.0",
|
|
48
|
+
"tailwindcss": "^3.4.0",
|
|
49
|
+
"typescript": "^5.0.0",
|
|
50
|
+
"vite": "^6.0.0",
|
|
51
|
+
"vitest": "^2.0.0"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@alliance-droid/svelte-component-library": "^0.1.0",
|
|
55
|
+
"ws": "^8.18.0"
|
|
56
|
+
},
|
|
57
|
+
"keywords": [
|
|
58
|
+
"svelte",
|
|
59
|
+
"chat",
|
|
60
|
+
"widget",
|
|
61
|
+
"ai",
|
|
62
|
+
"support",
|
|
63
|
+
"customer-service"
|
|
64
|
+
],
|
|
65
|
+
"license": "MIT",
|
|
66
|
+
"publishConfig": {
|
|
67
|
+
"access": "restricted"
|
|
68
|
+
},
|
|
69
|
+
"repository": {
|
|
70
|
+
"type": "git",
|
|
71
|
+
"url": "https://github.com/alliance-droid/chat-widget"
|
|
72
|
+
}
|
|
73
|
+
}
|