@juspay/svelte-ui-components 2.124.0 → 2.126.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.
Files changed (56) hide show
  1. package/dist/Chat/Chat.svelte +158 -0
  2. package/dist/Chat/Chat.svelte.d.ts +4 -0
  3. package/dist/Chat/controller.svelte.d.ts +24 -0
  4. package/dist/Chat/controller.svelte.js +190 -0
  5. package/dist/Chat/properties.d.ts +51 -0
  6. package/dist/Chat/properties.js +1 -0
  7. package/dist/Chat/roles.d.ts +2 -0
  8. package/dist/Chat/roles.js +3 -0
  9. package/dist/Chat/types.d.ts +45 -0
  10. package/dist/Chat/types.js +1 -0
  11. package/dist/ChatBubble/ChatBubble.svelte +419 -0
  12. package/dist/ChatBubble/ChatBubble.svelte.d.ts +4 -0
  13. package/dist/ChatBubble/properties.d.ts +43 -0
  14. package/dist/ChatBubble/properties.js +1 -0
  15. package/dist/ChatComposer/ChatComposer.svelte +289 -0
  16. package/dist/ChatComposer/ChatComposer.svelte.d.ts +4 -0
  17. package/dist/ChatComposer/properties.d.ts +33 -0
  18. package/dist/ChatComposer/properties.js +1 -0
  19. package/dist/ChatHeader/ChatHeader.svelte +169 -0
  20. package/dist/ChatHeader/ChatHeader.svelte.d.ts +4 -0
  21. package/dist/ChatHeader/properties.d.ts +19 -0
  22. package/dist/ChatHeader/properties.js +1 -0
  23. package/dist/ChatMessage/ChatMessage.svelte +330 -0
  24. package/dist/ChatMessage/ChatMessage.svelte.d.ts +4 -0
  25. package/dist/ChatMessage/properties.d.ts +29 -0
  26. package/dist/ChatMessage/properties.js +1 -0
  27. package/dist/ChatMessageList/ChatMessageList.svelte +181 -0
  28. package/dist/ChatMessageList/ChatMessageList.svelte.d.ts +4 -0
  29. package/dist/ChatMessageList/properties.d.ts +22 -0
  30. package/dist/ChatMessageList/properties.js +1 -0
  31. package/dist/ChatSuggestions/ChatSuggestions.svelte +46 -0
  32. package/dist/ChatSuggestions/ChatSuggestions.svelte.d.ts +4 -0
  33. package/dist/ChatSuggestions/properties.d.ts +16 -0
  34. package/dist/ChatSuggestions/properties.js +1 -0
  35. package/dist/ChatToolStatus/ChatToolStatus.svelte +63 -0
  36. package/dist/ChatToolStatus/ChatToolStatus.svelte.d.ts +4 -0
  37. package/dist/ChatToolStatus/properties.d.ts +10 -0
  38. package/dist/ChatToolStatus/properties.js +1 -0
  39. package/dist/LineChart/LineChart.svelte +36 -15
  40. package/dist/Resizable/Resizable.svelte +304 -0
  41. package/dist/Resizable/Resizable.svelte.d.ts +4 -0
  42. package/dist/Resizable/properties.d.ts +27 -0
  43. package/dist/Resizable/properties.js +1 -0
  44. package/dist/Table/Table.svelte +109 -10
  45. package/dist/Table/properties.d.ts +1 -0
  46. package/dist/assets/attach.svg +3 -0
  47. package/dist/assets/chat.svg +3 -0
  48. package/dist/assets/mic.svg +5 -0
  49. package/dist/assets/retry.svg +4 -0
  50. package/dist/assets/send.svg +4 -0
  51. package/dist/assets/stop.svg +3 -0
  52. package/dist/assets/thumb-down.svg +4 -0
  53. package/dist/assets/thumb-up.svg +4 -0
  54. package/dist/index.d.ts +21 -0
  55. package/dist/index.js +11 -0
  56. package/package.json +1 -1
@@ -0,0 +1,158 @@
1
+ <script lang="ts">
2
+ import ChatHeader from '../ChatHeader/ChatHeader.svelte';
3
+ import ChatMessageList from '../ChatMessageList/ChatMessageList.svelte';
4
+ import ChatComposer from '../ChatComposer/ChatComposer.svelte';
5
+ import ChatSuggestions from '../ChatSuggestions/ChatSuggestions.svelte';
6
+ import ChatToolStatus from '../ChatToolStatus/ChatToolStatus.svelte';
7
+ import type { ChatProperties } from './properties';
8
+
9
+ let {
10
+ messages,
11
+ value = $bindable(''),
12
+ title = '',
13
+ subtitle = '',
14
+ image,
15
+ imageAlt = '',
16
+ placeholder = '',
17
+ disabled = false,
18
+ streaming = false,
19
+ recording = false,
20
+ autoscroll = true,
21
+ toolStatus = null,
22
+ suggestions = [],
23
+ attachments = $bindable([]),
24
+ accept = '',
25
+ multiple = false,
26
+ allowCopy = false,
27
+ closeLabel = 'Close',
28
+ showClose,
29
+ headerAvatar,
30
+ headerActions,
31
+ headerContent,
32
+ message,
33
+ messageAttachments,
34
+ empty,
35
+ composerLeading,
36
+ sendIcon,
37
+ stopIcon,
38
+ voiceIcon,
39
+ attachIcon,
40
+ onsend,
41
+ onsuggestion,
42
+ onclose,
43
+ onstop,
44
+ onvoice,
45
+ onattach,
46
+ onretry,
47
+ onfeedback,
48
+ testId,
49
+ classes
50
+ }: ChatProperties = $props();
51
+
52
+ let showHeader = $derived(
53
+ title.length > 0 ||
54
+ subtitle.length > 0 ||
55
+ (typeof image === 'string' && image.length > 0) ||
56
+ typeof onclose === 'function' ||
57
+ typeof headerAvatar === 'function' ||
58
+ typeof headerActions === 'function'
59
+ );
60
+ let showSuggestions = $derived(suggestions.length > 0 && messages.length === 0);
61
+
62
+ function handleSuggestion(suggestionValue: string, index: number): void {
63
+ if (typeof onsuggestion === 'function') {
64
+ onsuggestion(suggestionValue, index);
65
+ return;
66
+ }
67
+ onsend?.(suggestionValue, []);
68
+ }
69
+ </script>
70
+
71
+ <section
72
+ class="chat {classes ?? ''}"
73
+ data-pw={typeof testId === 'string' ? testId : null}
74
+ testID={typeof testId === 'string' ? testId : null}
75
+ >
76
+ {#if showHeader}
77
+ <ChatHeader
78
+ {title}
79
+ {subtitle}
80
+ {image}
81
+ {imageAlt}
82
+ {closeLabel}
83
+ {showClose}
84
+ {onclose}
85
+ avatar={headerAvatar}
86
+ actions={headerActions}
87
+ children={headerContent}
88
+ />
89
+ {/if}
90
+
91
+ <ChatMessageList
92
+ {messages}
93
+ {autoscroll}
94
+ {message}
95
+ {messageAttachments}
96
+ {empty}
97
+ {allowCopy}
98
+ {onretry}
99
+ {onfeedback}
100
+ />
101
+
102
+ <div class="footer">
103
+ {#if showSuggestions}
104
+ <ChatSuggestions items={suggestions} {disabled} onselect={handleSuggestion} />
105
+ {/if}
106
+ {#if toolStatus !== null}
107
+ <div class="tool-status"><ChatToolStatus label={toolStatus.label} /></div>
108
+ {/if}
109
+ <ChatComposer
110
+ bind:value
111
+ bind:attachments
112
+ {placeholder}
113
+ {disabled}
114
+ {streaming}
115
+ {recording}
116
+ {accept}
117
+ {multiple}
118
+ onsubmit={onsend}
119
+ {onstop}
120
+ {onvoice}
121
+ {onattach}
122
+ leading={composerLeading}
123
+ {sendIcon}
124
+ {stopIcon}
125
+ {voiceIcon}
126
+ {attachIcon}
127
+ />
128
+ </div>
129
+ </section>
130
+
131
+ <style>
132
+ .chat {
133
+ box-sizing: border-box;
134
+ display: flex;
135
+ flex-direction: column;
136
+ height: var(--chat-height, 100%);
137
+ width: var(--chat-width, 100%);
138
+ background: var(--chat-background, #ffffff);
139
+ border: var(--chat-border, none);
140
+ border-radius: var(--chat-border-radius, 0);
141
+ overflow: hidden;
142
+ }
143
+
144
+ .footer {
145
+ box-sizing: border-box;
146
+ display: flex;
147
+ flex-direction: column;
148
+ gap: var(--chat-footer-gap, 10px);
149
+ padding: var(--chat-footer-padding, 12px 1.5rem);
150
+ background: var(--chat-footer-background, transparent);
151
+ border-top: var(--chat-footer-border-top, none);
152
+ }
153
+
154
+ .tool-status {
155
+ display: flex;
156
+ justify-content: var(--chat-tool-status-justify, center);
157
+ }
158
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { ChatProperties } from './properties';
2
+ declare const Chat: import("svelte").Component<ChatProperties, {}, "value" | "attachments">;
3
+ type Chat = ReturnType<typeof Chat>;
4
+ export default Chat;
@@ -0,0 +1,24 @@
1
+ import type { ChatControllerOptions, ChatMessageData, ChatToolStatus } from './types';
2
+ export declare class ChatController {
3
+ messages: ChatMessageData[];
4
+ isStreaming: boolean;
5
+ toolStatus: ChatToolStatus | null;
6
+ private readonly transport;
7
+ private readonly typewriter;
8
+ private readonly generateId;
9
+ private abortController;
10
+ private sessionId;
11
+ private revealBuffer;
12
+ private revealTimer;
13
+ constructor(options: ChatControllerOptions);
14
+ send(prompt: string): Promise<void>;
15
+ retry(): Promise<void>;
16
+ private stream;
17
+ stop(): void;
18
+ reset(): void;
19
+ private feed;
20
+ private tick;
21
+ private drain;
22
+ private stopReveal;
23
+ private patchReply;
24
+ }
@@ -0,0 +1,190 @@
1
+ import { partyOf } from './roles';
2
+ const REVEAL_CHARS_PER_TICK = 2;
3
+ const REVEAL_INTERVAL_MS = 22;
4
+ function prefersReducedMotion() {
5
+ return (typeof window !== 'undefined' &&
6
+ typeof window.matchMedia === 'function' &&
7
+ window.matchMedia('(prefers-reduced-motion: reduce)').matches);
8
+ }
9
+ let idCounter = 0;
10
+ function defaultId() {
11
+ if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
12
+ return crypto.randomUUID();
13
+ }
14
+ idCounter += 1;
15
+ return `msg-${idCounter}`;
16
+ }
17
+ export class ChatController {
18
+ messages = $state([]);
19
+ isStreaming = $state(false);
20
+ toolStatus = $state(null);
21
+ transport;
22
+ typewriter;
23
+ generateId;
24
+ abortController = null;
25
+ sessionId = null;
26
+ revealBuffer = '';
27
+ revealTimer = null;
28
+ constructor(options) {
29
+ this.transport = options.transport;
30
+ this.typewriter = options.typewriter ?? false;
31
+ this.generateId = options.generateId ?? defaultId;
32
+ this.messages = options.initialMessages ?? [];
33
+ }
34
+ async send(prompt) {
35
+ const trimmed = prompt.trim();
36
+ if (trimmed.length === 0 || this.isStreaming) {
37
+ return;
38
+ }
39
+ const history = this.messages.slice();
40
+ this.messages.push({
41
+ id: this.generateId(),
42
+ role: 'sender',
43
+ content: trimmed,
44
+ status: 'sent'
45
+ });
46
+ await this.stream(trimmed, history);
47
+ }
48
+ async retry() {
49
+ if (this.isStreaming) {
50
+ return;
51
+ }
52
+ let senderIndex = -1;
53
+ for (let i = this.messages.length - 1; i >= 0; i -= 1) {
54
+ const role = this.messages.at(i)?.role;
55
+ if (typeof role === 'string' && partyOf(role) === 'sender') {
56
+ senderIndex = i;
57
+ break;
58
+ }
59
+ }
60
+ const senderMessage = this.messages.at(senderIndex);
61
+ if (senderIndex < 0 || typeof senderMessage === 'undefined') {
62
+ return;
63
+ }
64
+ const history = this.messages.slice(0, senderIndex);
65
+ this.messages = this.messages.slice(0, senderIndex + 1);
66
+ await this.stream(senderMessage.content, history);
67
+ }
68
+ async stream(message, history) {
69
+ this.messages.push({
70
+ id: this.generateId(),
71
+ role: 'responder',
72
+ content: '',
73
+ streaming: true
74
+ });
75
+ this.isStreaming = true;
76
+ const controller = new AbortController();
77
+ this.abortController = controller;
78
+ try {
79
+ await this.transport({ message, history, sessionId: this.sessionId, signal: controller.signal }, {
80
+ onText: (chunk) => {
81
+ this.toolStatus = null;
82
+ this.feed(chunk);
83
+ },
84
+ onToolStatus: (status) => {
85
+ this.toolStatus = status;
86
+ },
87
+ onAttachment: (attachment) => {
88
+ this.patchReply((reply) => {
89
+ const next = reply.attachments ?? [];
90
+ next.push(attachment);
91
+ reply.attachments = next;
92
+ });
93
+ },
94
+ onError: (error) => {
95
+ this.patchReply((reply) => {
96
+ reply.status = 'error';
97
+ reply.content =
98
+ reply.content.length > 0 ? `${reply.content}\n\n${error.message}` : error.message;
99
+ });
100
+ },
101
+ onDone: (meta) => {
102
+ if (typeof meta?.sessionId === 'string') {
103
+ this.sessionId = meta.sessionId;
104
+ }
105
+ }
106
+ });
107
+ }
108
+ catch (error) {
109
+ if (!controller.signal.aborted) {
110
+ this.patchReply((reply) => {
111
+ reply.status = 'error';
112
+ reply.content = error instanceof Error ? error.message : 'Something went wrong.';
113
+ });
114
+ }
115
+ }
116
+ await this.drain();
117
+ this.patchReply((reply) => {
118
+ reply.streaming = false;
119
+ });
120
+ if (this.abortController === controller) {
121
+ this.abortController = null;
122
+ }
123
+ this.isStreaming = false;
124
+ this.toolStatus = null;
125
+ }
126
+ stop() {
127
+ this.abortController?.abort();
128
+ this.abortController = null;
129
+ this.stopReveal();
130
+ this.revealBuffer = '';
131
+ this.patchReply((reply) => {
132
+ reply.streaming = false;
133
+ });
134
+ this.isStreaming = false;
135
+ this.toolStatus = null;
136
+ }
137
+ reset() {
138
+ this.stop();
139
+ this.sessionId = null;
140
+ this.messages = [];
141
+ }
142
+ feed(chunk) {
143
+ if (!this.typewriter || prefersReducedMotion()) {
144
+ this.patchReply((reply) => {
145
+ reply.content += chunk;
146
+ });
147
+ return;
148
+ }
149
+ this.revealBuffer += chunk;
150
+ if (this.revealTimer === null) {
151
+ this.revealTimer = setInterval(() => this.tick(), REVEAL_INTERVAL_MS);
152
+ }
153
+ }
154
+ tick() {
155
+ if (this.revealBuffer.length === 0) {
156
+ this.stopReveal();
157
+ return;
158
+ }
159
+ const piece = this.revealBuffer.slice(0, REVEAL_CHARS_PER_TICK);
160
+ this.revealBuffer = this.revealBuffer.slice(piece.length);
161
+ this.patchReply((reply) => {
162
+ reply.content += piece;
163
+ });
164
+ }
165
+ drain() {
166
+ return new Promise((resolve) => {
167
+ const check = () => {
168
+ if (this.revealBuffer.length === 0 && this.revealTimer === null) {
169
+ resolve();
170
+ return;
171
+ }
172
+ setTimeout(check, REVEAL_INTERVAL_MS);
173
+ };
174
+ check();
175
+ });
176
+ }
177
+ stopReveal() {
178
+ if (this.revealTimer !== null) {
179
+ clearInterval(this.revealTimer);
180
+ this.revealTimer = null;
181
+ }
182
+ }
183
+ patchReply(mutate) {
184
+ const last = this.messages.at(-1);
185
+ if (typeof last === 'undefined' || partyOf(last.role) !== 'responder') {
186
+ return;
187
+ }
188
+ mutate(last);
189
+ }
190
+ }
@@ -0,0 +1,51 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ChatMessageData, ChatToolStatus } from './types';
3
+ import type { ChatMessageFeedback } from '../ChatMessage/properties';
4
+ import type { ChatSuggestion } from '../ChatSuggestions/properties';
5
+ export type ChatProperties = OptionalChatProperties & ChatEventProperties & MandatoryChatProperties;
6
+ export type MandatoryChatProperties = {
7
+ messages: ChatMessageData[];
8
+ };
9
+ export type OptionalChatProperties = {
10
+ value?: string;
11
+ title?: string;
12
+ subtitle?: string;
13
+ image?: string;
14
+ imageAlt?: string;
15
+ placeholder?: string;
16
+ disabled?: boolean;
17
+ streaming?: boolean;
18
+ recording?: boolean;
19
+ autoscroll?: boolean;
20
+ toolStatus?: ChatToolStatus | null;
21
+ suggestions?: ChatSuggestion[];
22
+ attachments?: File[];
23
+ accept?: string;
24
+ multiple?: boolean;
25
+ allowCopy?: boolean;
26
+ closeLabel?: string;
27
+ showClose?: boolean;
28
+ headerAvatar?: Snippet;
29
+ headerActions?: Snippet;
30
+ headerContent?: Snippet;
31
+ message?: Snippet<[ChatMessageData]>;
32
+ messageAttachments?: Snippet<[ChatMessageData]>;
33
+ empty?: Snippet;
34
+ composerLeading?: Snippet;
35
+ sendIcon?: Snippet;
36
+ stopIcon?: Snippet;
37
+ voiceIcon?: Snippet;
38
+ attachIcon?: Snippet;
39
+ testId?: string;
40
+ classes?: string;
41
+ };
42
+ export type ChatEventProperties = {
43
+ onsend?: (value: string, attachments: File[]) => void;
44
+ onsuggestion?: (value: string, index: number) => void;
45
+ onclose?: () => void;
46
+ onstop?: () => void;
47
+ onvoice?: () => void;
48
+ onattach?: (files: File[]) => void;
49
+ onretry?: () => void;
50
+ onfeedback?: (value: ChatMessageFeedback, message: ChatMessageData) => void;
51
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { ChatParty, ChatRole } from './types';
2
+ export declare function partyOf(role: ChatRole): ChatParty;
@@ -0,0 +1,3 @@
1
+ export function partyOf(role) {
2
+ return role === 'sender' || role === 'user' ? 'sender' : 'responder';
3
+ }
@@ -0,0 +1,45 @@
1
+ export type ChatParty = 'sender' | 'responder';
2
+ export type ChatRole = ChatParty | 'user' | 'assistant' | 'system' | (string & {});
3
+ export type ChatMessageStatus = 'sending' | 'sent' | 'error';
4
+ export type ChatMessageData = {
5
+ id: string;
6
+ role: ChatRole;
7
+ content: string;
8
+ html?: string;
9
+ streaming?: boolean;
10
+ status?: ChatMessageStatus;
11
+ timestamp?: number;
12
+ attachments?: unknown[];
13
+ };
14
+ export type ChatToolStatus = {
15
+ tool?: string;
16
+ label: string;
17
+ state?: 'calling' | 'done';
18
+ };
19
+ export type ChatError = {
20
+ code?: string;
21
+ message: string;
22
+ };
23
+ export type ChatStreamHandlers = {
24
+ onText: (chunk: string) => void;
25
+ onToolStatus?: (status: ChatToolStatus | null) => void;
26
+ onAttachment?: (attachment: unknown) => void;
27
+ onError?: (error: ChatError) => void;
28
+ onDone?: (meta?: ChatCompletion) => void;
29
+ };
30
+ export type ChatCompletion = {
31
+ sessionId?: string;
32
+ };
33
+ export type ChatTransportInput = {
34
+ message: string;
35
+ history: ChatMessageData[];
36
+ sessionId: string | null;
37
+ signal: AbortSignal;
38
+ };
39
+ export type ChatTransport = (input: ChatTransportInput, handlers: ChatStreamHandlers) => Promise<void>;
40
+ export type ChatControllerOptions = {
41
+ transport: ChatTransport;
42
+ initialMessages?: ChatMessageData[];
43
+ typewriter?: boolean;
44
+ generateId?: () => string;
45
+ };
@@ -0,0 +1 @@
1
+ export {};