@happyvertical/smrt-chat 0.30.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 (169) hide show
  1. package/AGENTS.md +35 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/README.md +163 -0
  5. package/dist/__smrt-register__.d.ts +2 -0
  6. package/dist/__smrt-register__.d.ts.map +1 -0
  7. package/dist/chunks/ChatService-Dpzc1Pa5.js +2044 -0
  8. package/dist/chunks/ChatService-Dpzc1Pa5.js.map +1 -0
  9. package/dist/collections/AgentSessionCollection.d.ts +57 -0
  10. package/dist/collections/AgentSessionCollection.d.ts.map +1 -0
  11. package/dist/collections/ChatMessageCollection.d.ts +79 -0
  12. package/dist/collections/ChatMessageCollection.d.ts.map +1 -0
  13. package/dist/collections/ChatParticipantCollection.d.ts +26 -0
  14. package/dist/collections/ChatParticipantCollection.d.ts.map +1 -0
  15. package/dist/collections/ChatReactionCollection.d.ts +23 -0
  16. package/dist/collections/ChatReactionCollection.d.ts.map +1 -0
  17. package/dist/collections/ChatRoomCollection.d.ts +43 -0
  18. package/dist/collections/ChatRoomCollection.d.ts.map +1 -0
  19. package/dist/collections/ChatThreadCollection.d.ts +9 -0
  20. package/dist/collections/ChatThreadCollection.d.ts.map +1 -0
  21. package/dist/index.d.ts +5 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +18 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/internal/agent-runtime.d.ts +16 -0
  26. package/dist/internal/agent-runtime.d.ts.map +1 -0
  27. package/dist/internal/agent-runtime.js +5 -0
  28. package/dist/internal/agent-runtime.js.map +1 -0
  29. package/dist/manifest.json +2811 -0
  30. package/dist/models/AgentSession.d.ts +70 -0
  31. package/dist/models/AgentSession.d.ts.map +1 -0
  32. package/dist/models/ChatMessage.d.ts +55 -0
  33. package/dist/models/ChatMessage.d.ts.map +1 -0
  34. package/dist/models/ChatParticipant.d.ts +32 -0
  35. package/dist/models/ChatParticipant.d.ts.map +1 -0
  36. package/dist/models/ChatReaction.d.ts +19 -0
  37. package/dist/models/ChatReaction.d.ts.map +1 -0
  38. package/dist/models/ChatRoom.d.ts +44 -0
  39. package/dist/models/ChatRoom.d.ts.map +1 -0
  40. package/dist/models/ChatThread.d.ts +24 -0
  41. package/dist/models/ChatThread.d.ts.map +1 -0
  42. package/dist/models/index.d.ts +7 -0
  43. package/dist/models/index.d.ts.map +1 -0
  44. package/dist/playground.d.ts +2 -0
  45. package/dist/playground.d.ts.map +1 -0
  46. package/dist/playground.js +166 -0
  47. package/dist/playground.js.map +1 -0
  48. package/dist/services/ChatService.d.ts +390 -0
  49. package/dist/services/ChatService.d.ts.map +1 -0
  50. package/dist/services/index.d.ts +2 -0
  51. package/dist/services/index.d.ts.map +1 -0
  52. package/dist/smrt-knowledge.json +1507 -0
  53. package/dist/svelte/components/agent/AgentChat.svelte +542 -0
  54. package/dist/svelte/components/agent/AgentChat.svelte.d.ts +21 -0
  55. package/dist/svelte/components/agent/AgentChat.svelte.d.ts.map +1 -0
  56. package/dist/svelte/components/agent/AgentSelector.svelte +175 -0
  57. package/dist/svelte/components/agent/AgentSelector.svelte.d.ts +11 -0
  58. package/dist/svelte/components/agent/AgentSelector.svelte.d.ts.map +1 -0
  59. package/dist/svelte/components/agent/AgentSessionPanel.svelte +322 -0
  60. package/dist/svelte/components/agent/AgentSessionPanel.svelte.d.ts +15 -0
  61. package/dist/svelte/components/agent/AgentSessionPanel.svelte.d.ts.map +1 -0
  62. package/dist/svelte/components/agent/ToolCallDisplay.svelte +335 -0
  63. package/dist/svelte/components/agent/ToolCallDisplay.svelte.d.ts +9 -0
  64. package/dist/svelte/components/agent/ToolCallDisplay.svelte.d.ts.map +1 -0
  65. package/dist/svelte/components/agent/message-blocks.d.ts +12 -0
  66. package/dist/svelte/components/agent/message-blocks.d.ts.map +1 -0
  67. package/dist/svelte/components/agent/message-blocks.js +41 -0
  68. package/dist/svelte/components/agent/message-blocks.test.js +31 -0
  69. package/dist/svelte/components/dialogs/RoomCreateDialog.svelte +403 -0
  70. package/dist/svelte/components/dialogs/RoomCreateDialog.svelte.d.ts +16 -0
  71. package/dist/svelte/components/dialogs/RoomCreateDialog.svelte.d.ts.map +1 -0
  72. package/dist/svelte/components/dialogs/SearchMessages.svelte +457 -0
  73. package/dist/svelte/components/dialogs/SearchMessages.svelte.d.ts +17 -0
  74. package/dist/svelte/components/dialogs/SearchMessages.svelte.d.ts.map +1 -0
  75. package/dist/svelte/components/layout/ChatLayout.svelte +150 -0
  76. package/dist/svelte/components/layout/ChatLayout.svelte.d.ts +18 -0
  77. package/dist/svelte/components/layout/ChatLayout.svelte.d.ts.map +1 -0
  78. package/dist/svelte/components/layout/MemberList.svelte +389 -0
  79. package/dist/svelte/components/layout/MemberList.svelte.d.ts +11 -0
  80. package/dist/svelte/components/layout/MemberList.svelte.d.ts.map +1 -0
  81. package/dist/svelte/components/layout/RoomHeader.svelte +241 -0
  82. package/dist/svelte/components/layout/RoomHeader.svelte.d.ts +15 -0
  83. package/dist/svelte/components/layout/RoomHeader.svelte.d.ts.map +1 -0
  84. package/dist/svelte/components/layout/RoomList.svelte +471 -0
  85. package/dist/svelte/components/layout/RoomList.svelte.d.ts +15 -0
  86. package/dist/svelte/components/layout/RoomList.svelte.d.ts.map +1 -0
  87. package/dist/svelte/components/messages/MessageInput.svelte +232 -0
  88. package/dist/svelte/components/messages/MessageInput.svelte.d.ts +20 -0
  89. package/dist/svelte/components/messages/MessageInput.svelte.d.ts.map +1 -0
  90. package/dist/svelte/components/messages/MessageItem.svelte +431 -0
  91. package/dist/svelte/components/messages/MessageItem.svelte.d.ts +19 -0
  92. package/dist/svelte/components/messages/MessageItem.svelte.d.ts.map +1 -0
  93. package/dist/svelte/components/messages/MessageList.svelte +129 -0
  94. package/dist/svelte/components/messages/MessageList.svelte.d.ts +17 -0
  95. package/dist/svelte/components/messages/MessageList.svelte.d.ts.map +1 -0
  96. package/dist/svelte/components/messages/ThreadPanel.svelte +156 -0
  97. package/dist/svelte/components/messages/ThreadPanel.svelte.d.ts +17 -0
  98. package/dist/svelte/components/messages/ThreadPanel.svelte.d.ts.map +1 -0
  99. package/dist/svelte/components/messages/__tests__/MessageInput.test.js +38 -0
  100. package/dist/svelte/components/shared/Avatar.svelte +30 -0
  101. package/dist/svelte/components/shared/Avatar.svelte.d.ts +14 -0
  102. package/dist/svelte/components/shared/Avatar.svelte.d.ts.map +1 -0
  103. package/dist/svelte/components/shared/FileUpload.svelte +382 -0
  104. package/dist/svelte/components/shared/FileUpload.svelte.d.ts +14 -0
  105. package/dist/svelte/components/shared/FileUpload.svelte.d.ts.map +1 -0
  106. package/dist/svelte/components/shared/LinkPreview.svelte +108 -0
  107. package/dist/svelte/components/shared/LinkPreview.svelte.d.ts +18 -0
  108. package/dist/svelte/components/shared/LinkPreview.svelte.d.ts.map +1 -0
  109. package/dist/svelte/components/shared/MentionAutocomplete.svelte +168 -0
  110. package/dist/svelte/components/shared/MentionAutocomplete.svelte.d.ts +18 -0
  111. package/dist/svelte/components/shared/MentionAutocomplete.svelte.d.ts.map +1 -0
  112. package/dist/svelte/components/shared/MessageBubble.svelte +81 -0
  113. package/dist/svelte/components/shared/MessageBubble.svelte.d.ts +16 -0
  114. package/dist/svelte/components/shared/MessageBubble.svelte.d.ts.map +1 -0
  115. package/dist/svelte/components/shared/ReactionPicker.svelte +103 -0
  116. package/dist/svelte/components/shared/ReactionPicker.svelte.d.ts +10 -0
  117. package/dist/svelte/components/shared/ReactionPicker.svelte.d.ts.map +1 -0
  118. package/dist/svelte/components/shared/ReadReceipts.svelte +127 -0
  119. package/dist/svelte/components/shared/ReadReceipts.svelte.d.ts +13 -0
  120. package/dist/svelte/components/shared/ReadReceipts.svelte.d.ts.map +1 -0
  121. package/dist/svelte/components/shared/TypingIndicator.svelte +90 -0
  122. package/dist/svelte/components/shared/TypingIndicator.svelte.d.ts +12 -0
  123. package/dist/svelte/components/shared/TypingIndicator.svelte.d.ts.map +1 -0
  124. package/dist/svelte/components/shared/UserPresence.svelte +65 -0
  125. package/dist/svelte/components/shared/UserPresence.svelte.d.ts +13 -0
  126. package/dist/svelte/components/shared/UserPresence.svelte.d.ts.map +1 -0
  127. package/dist/svelte/components/shared/__tests__/Avatar.test.js +20 -0
  128. package/dist/svelte/components/shared/__tests__/LinkPreview.test.js +29 -0
  129. package/dist/svelte/components/shared/__tests__/MessageBubble.test.js +21 -0
  130. package/dist/svelte/components/shared/__tests__/ReactionPicker.test.js +35 -0
  131. package/dist/svelte/components/shared/__tests__/ReadReceipts.test.js +28 -0
  132. package/dist/svelte/components/shared/__tests__/TypingIndicator.test.js +27 -0
  133. package/dist/svelte/components/shared/__tests__/UserPresence.test.js +23 -0
  134. package/dist/svelte/components/tabs/ChatTab.svelte +240 -0
  135. package/dist/svelte/components/tabs/ChatTab.svelte.d.ts +21 -0
  136. package/dist/svelte/components/tabs/ChatTab.svelte.d.ts.map +1 -0
  137. package/dist/svelte/components/tabs/ChatTabList.svelte +158 -0
  138. package/dist/svelte/components/tabs/ChatTabList.svelte.d.ts +13 -0
  139. package/dist/svelte/components/tabs/ChatTabList.svelte.d.ts.map +1 -0
  140. package/dist/svelte/components/tabs/ChatTabs.svelte +88 -0
  141. package/dist/svelte/components/tabs/ChatTabs.svelte.d.ts +21 -0
  142. package/dist/svelte/components/tabs/ChatTabs.svelte.d.ts.map +1 -0
  143. package/dist/svelte/components/tabs/MiniChat.svelte +253 -0
  144. package/dist/svelte/components/tabs/MiniChat.svelte.d.ts +15 -0
  145. package/dist/svelte/components/tabs/MiniChat.svelte.d.ts.map +1 -0
  146. package/dist/svelte/i18n.d.ts +51 -0
  147. package/dist/svelte/i18n.d.ts.map +1 -0
  148. package/dist/svelte/i18n.js +72 -0
  149. package/dist/svelte/i18n.messages.d.ts +50 -0
  150. package/dist/svelte/i18n.messages.d.ts.map +1 -0
  151. package/dist/svelte/i18n.messages.js +69 -0
  152. package/dist/svelte/index.d.ts +48 -0
  153. package/dist/svelte/index.d.ts.map +1 -0
  154. package/dist/svelte/index.js +117 -0
  155. package/dist/svelte/playground.d.ts +171 -0
  156. package/dist/svelte/playground.d.ts.map +1 -0
  157. package/dist/svelte/playground.js +161 -0
  158. package/dist/svelte/types.d.ts +116 -0
  159. package/dist/svelte/types.d.ts.map +1 -0
  160. package/dist/svelte/types.js +1 -0
  161. package/dist/types.d.ts +99 -0
  162. package/dist/types.d.ts.map +1 -0
  163. package/dist/types.js +2 -0
  164. package/dist/types.js.map +1 -0
  165. package/dist/ui.d.ts +4 -0
  166. package/dist/ui.d.ts.map +1 -0
  167. package/dist/ui.js +92 -0
  168. package/dist/ui.js.map +1 -0
  169. package/package.json +95 -0
@@ -0,0 +1,389 @@
1
+ <script lang="ts">
2
+ /**
3
+ * MemberList - Room member sidebar panel
4
+ * Lists participants grouped by online status with role badges
5
+ */
6
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
7
+ import { M } from '../../i18n.messages.js';
8
+ import type { ChatParticipantData } from '../../types.js';
9
+
10
+ const { t } = useI18n();
11
+
12
+ export interface Props {
13
+ /** Room participants */
14
+ participants: ChatParticipantData[];
15
+ /** Callback to close the member panel */
16
+ onclose: () => void;
17
+ }
18
+
19
+ let { participants, onclose }: Props = $props();
20
+
21
+ const onlineMembers = $derived(
22
+ participants.filter((p) => p.onlineStatus === 'online'),
23
+ );
24
+ const awayMembers = $derived(
25
+ participants.filter(
26
+ (p) => p.onlineStatus === 'away' || p.onlineStatus === 'dnd',
27
+ ),
28
+ );
29
+ const offlineMembers = $derived(
30
+ participants.filter((p) => p.onlineStatus === 'offline'),
31
+ );
32
+
33
+ function getStatusColor(status: string): string {
34
+ switch (status) {
35
+ case 'online':
36
+ return 'var(--smrt-color-tertiary, #006c4f)';
37
+ case 'away':
38
+ return 'var(--smrt-color-warning, #f0a000)';
39
+ case 'dnd':
40
+ return 'var(--smrt-color-error, #ba1a1a)';
41
+ default:
42
+ return 'var(--smrt-color-outline, #74777f)';
43
+ }
44
+ }
45
+
46
+ function getStatusLabel(status: string): string {
47
+ switch (status) {
48
+ case 'online':
49
+ return 'Online';
50
+ case 'away':
51
+ return 'Away';
52
+ case 'dnd':
53
+ return 'Do not disturb';
54
+ default:
55
+ return 'Offline';
56
+ }
57
+ }
58
+
59
+ function getRoleBadgeLabel(role: string): string {
60
+ switch (role) {
61
+ case 'owner':
62
+ return 'Owner';
63
+ case 'admin':
64
+ return 'Admin';
65
+ default:
66
+ return '';
67
+ }
68
+ }
69
+ </script>
70
+
71
+ <aside class="member-list" aria-label={t(M['chat.member_list.room_members'])}>
72
+ <div class="member-list__header">
73
+ <h2 class="member-list__title">Members</h2>
74
+ <span class="member-list__count">{participants.length}</span>
75
+ <button
76
+ class="close-btn"
77
+ type="button"
78
+ onclick={onclose}
79
+ aria-label={t(M['chat.member_list.close'])}
80
+ >
81
+ <svg class="close-btn__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
82
+ <path d="M18 6 6 18" />
83
+ <path d="m6 6 12 12" />
84
+ </svg>
85
+ </button>
86
+ </div>
87
+
88
+ <div class="member-list__content">
89
+ {#if onlineMembers.length > 0}
90
+ <section class="member-group">
91
+ <h3 class="member-group__header">
92
+ Online
93
+ <span class="member-group__count">{onlineMembers.length}</span>
94
+ </h3>
95
+ <ul class="member-group__list" role="list">
96
+ {#each onlineMembers as member (member.id)}
97
+ <li class="member-item">
98
+ <div class="member-item__avatar-wrap">
99
+ {#if member.avatarUrl}
100
+ <img class="member-item__avatar" src={member.avatarUrl} alt="" />
101
+ {:else}
102
+ <span class="member-item__avatar-placeholder">
103
+ {(member.nickname || member.name).charAt(0).toUpperCase()}
104
+ </span>
105
+ {/if}
106
+ <span
107
+ class="member-item__status-dot"
108
+ style:background={getStatusColor(member.onlineStatus)}
109
+ aria-label={getStatusLabel(member.onlineStatus)}
110
+ ></span>
111
+ </div>
112
+ <span class="member-item__info">
113
+ <span class="member-item__name">{member.nickname || member.name}</span>
114
+ {#if getRoleBadgeLabel(member.role)}
115
+ <span class="role-badge role-badge--{member.role}">
116
+ {getRoleBadgeLabel(member.role)}
117
+ </span>
118
+ {/if}
119
+ </span>
120
+ </li>
121
+ {/each}
122
+ </ul>
123
+ </section>
124
+ {/if}
125
+
126
+ {#if awayMembers.length > 0}
127
+ <section class="member-group">
128
+ <h3 class="member-group__header">
129
+ Away
130
+ <span class="member-group__count">{awayMembers.length}</span>
131
+ </h3>
132
+ <ul class="member-group__list" role="list">
133
+ {#each awayMembers as member (member.id)}
134
+ <li class="member-item">
135
+ <div class="member-item__avatar-wrap">
136
+ {#if member.avatarUrl}
137
+ <img class="member-item__avatar" src={member.avatarUrl} alt="" />
138
+ {:else}
139
+ <span class="member-item__avatar-placeholder">
140
+ {(member.nickname || member.name).charAt(0).toUpperCase()}
141
+ </span>
142
+ {/if}
143
+ <span
144
+ class="member-item__status-dot"
145
+ style:background={getStatusColor(member.onlineStatus)}
146
+ aria-label={getStatusLabel(member.onlineStatus)}
147
+ ></span>
148
+ </div>
149
+ <span class="member-item__info">
150
+ <span class="member-item__name">{member.nickname || member.name}</span>
151
+ {#if getRoleBadgeLabel(member.role)}
152
+ <span class="role-badge role-badge--{member.role}">
153
+ {getRoleBadgeLabel(member.role)}
154
+ </span>
155
+ {/if}
156
+ </span>
157
+ </li>
158
+ {/each}
159
+ </ul>
160
+ </section>
161
+ {/if}
162
+
163
+ {#if offlineMembers.length > 0}
164
+ <section class="member-group">
165
+ <h3 class="member-group__header">
166
+ Offline
167
+ <span class="member-group__count">{offlineMembers.length}</span>
168
+ </h3>
169
+ <ul class="member-group__list" role="list">
170
+ {#each offlineMembers as member (member.id)}
171
+ <li class="member-item member-item--offline">
172
+ <div class="member-item__avatar-wrap">
173
+ {#if member.avatarUrl}
174
+ <img class="member-item__avatar" src={member.avatarUrl} alt="" />
175
+ {:else}
176
+ <span class="member-item__avatar-placeholder">
177
+ {(member.nickname || member.name).charAt(0).toUpperCase()}
178
+ </span>
179
+ {/if}
180
+ <span
181
+ class="member-item__status-dot"
182
+ style:background={getStatusColor(member.onlineStatus)}
183
+ aria-label={getStatusLabel(member.onlineStatus)}
184
+ ></span>
185
+ </div>
186
+ <span class="member-item__info">
187
+ <span class="member-item__name">{member.nickname || member.name}</span>
188
+ {#if getRoleBadgeLabel(member.role)}
189
+ <span class="role-badge role-badge--{member.role}">
190
+ {getRoleBadgeLabel(member.role)}
191
+ </span>
192
+ {/if}
193
+ </span>
194
+ </li>
195
+ {/each}
196
+ </ul>
197
+ </section>
198
+ {/if}
199
+
200
+ {#if participants.length === 0}
201
+ <p class="member-list__empty">{t(M['chat.member_list.empty'])}</p>
202
+ {/if}
203
+ </div>
204
+ </aside>
205
+
206
+ <style>
207
+ .member-list {
208
+ display: flex;
209
+ flex-direction: column;
210
+ width: 240px;
211
+ height: 100%;
212
+ background: var(--smrt-color-surface-container, #f0f0f4);
213
+ border-left: 1px solid var(--smrt-color-outline-variant, #c4c6d0);
214
+ overflow: hidden;
215
+ }
216
+
217
+ .member-list__header {
218
+ display: flex;
219
+ align-items: center;
220
+ gap: 0.375rem;
221
+ padding: 0.75rem 1rem;
222
+ border-bottom: 1px solid var(--smrt-color-outline-variant, #c4c6d0);
223
+ }
224
+
225
+ .member-list__title {
226
+ margin: 0;
227
+ font: var(--smrt-typography-title-small-font, 600 0.875rem / 1.25 sans-serif);
228
+ color: var(--smrt-color-on-surface, #1a1c1e);
229
+ }
230
+
231
+ .member-list__count {
232
+ font: var(--smrt-typography-label-small-font, 500 0.6875rem / 1 sans-serif);
233
+ color: var(--smrt-color-on-surface-variant, #43474e);
234
+ }
235
+
236
+ .close-btn {
237
+ margin-left: auto;
238
+ display: inline-flex;
239
+ align-items: center;
240
+ justify-content: center;
241
+ width: 1.75rem;
242
+ height: 1.75rem;
243
+ border: none;
244
+ background: none;
245
+ border-radius: var(--smrt-radius-medium, 0.5rem);
246
+ color: var(--smrt-color-on-surface-variant, #43474e);
247
+ cursor: pointer;
248
+ transition: background var(--smrt-duration-short2, 150ms);
249
+ }
250
+
251
+ .close-btn:hover {
252
+ background: var(--smrt-color-surface-variant, #e1e2ec);
253
+ }
254
+
255
+ .close-btn:focus-visible {
256
+ outline: 2px solid var(--smrt-color-primary, #005ac1);
257
+ outline-offset: -2px;
258
+ }
259
+
260
+ .close-btn__icon {
261
+ width: 1rem;
262
+ height: 1rem;
263
+ }
264
+
265
+ .member-list__content {
266
+ flex: 1;
267
+ overflow-y: auto;
268
+ padding: 0.5rem 0;
269
+ }
270
+
271
+ .member-group {
272
+ margin-bottom: 0.5rem;
273
+ }
274
+
275
+ .member-group__header {
276
+ display: flex;
277
+ align-items: center;
278
+ gap: 0.375rem;
279
+ margin: 0;
280
+ padding: 0.375rem 1rem;
281
+ font: var(--smrt-typography-label-small-font, 500 0.6875rem / 1 sans-serif);
282
+ color: var(--smrt-color-on-surface-variant, #43474e);
283
+ text-transform: uppercase;
284
+ letter-spacing: 0.05em;
285
+ }
286
+
287
+ .member-group__count {
288
+ color: var(--smrt-color-outline, #74777f);
289
+ }
290
+
291
+ .member-group__list {
292
+ list-style: none;
293
+ margin: 0;
294
+ padding: 0;
295
+ }
296
+
297
+ .member-item {
298
+ display: flex;
299
+ align-items: center;
300
+ gap: 0.5rem;
301
+ padding: 0.375rem 1rem;
302
+ cursor: default;
303
+ }
304
+
305
+ .member-item--offline {
306
+ opacity: 0.5;
307
+ }
308
+
309
+ .member-item__avatar-wrap {
310
+ position: relative;
311
+ flex-shrink: 0;
312
+ }
313
+
314
+ .member-item__avatar {
315
+ width: 2rem;
316
+ height: 2rem;
317
+ border-radius: var(--smrt-radius-full, 9999px);
318
+ object-fit: cover;
319
+ }
320
+
321
+ .member-item__avatar-placeholder {
322
+ width: 2rem;
323
+ height: 2rem;
324
+ border-radius: var(--smrt-radius-full, 9999px);
325
+ background: var(--smrt-color-primary-container, #d6e3ff);
326
+ color: var(--smrt-color-on-primary-container, #001a41);
327
+ display: inline-flex;
328
+ align-items: center;
329
+ justify-content: center;
330
+ font: var(--smrt-typography-label-medium-font, 500 0.75rem / 1 sans-serif);
331
+ }
332
+
333
+ .member-item__status-dot {
334
+ position: absolute;
335
+ bottom: 0;
336
+ right: 0;
337
+ width: 0.625rem;
338
+ height: 0.625rem;
339
+ border-radius: var(--smrt-radius-full, 9999px);
340
+ border: 2px solid var(--smrt-color-surface-container, #f0f0f4);
341
+ }
342
+
343
+ .member-item__info {
344
+ flex: 1;
345
+ min-width: 0;
346
+ display: flex;
347
+ align-items: center;
348
+ gap: 0.375rem;
349
+ }
350
+
351
+ .member-item__name {
352
+ font: var(--smrt-typography-body-medium-font, 0.875rem / 1.25 sans-serif);
353
+ color: var(--smrt-color-on-surface, #1a1c1e);
354
+ overflow: hidden;
355
+ text-overflow: ellipsis;
356
+ white-space: nowrap;
357
+ }
358
+
359
+ .role-badge {
360
+ flex-shrink: 0;
361
+ padding: 0.0625rem 0.375rem;
362
+ border-radius: var(--smrt-radius-full, 9999px);
363
+ font: var(--smrt-typography-label-small-font, 500 0.6875rem / 1 sans-serif);
364
+ }
365
+
366
+ .role-badge--owner {
367
+ background: var(--smrt-color-tertiary-container, #c2f0dd);
368
+ color: var(--smrt-color-on-tertiary-container, #002114);
369
+ }
370
+
371
+ .role-badge--admin {
372
+ background: var(--smrt-color-secondary-container, #d7e3f7);
373
+ color: var(--smrt-color-on-secondary-container, #101c2b);
374
+ }
375
+
376
+ .member-list__empty {
377
+ text-align: center;
378
+ padding: 2rem 1rem;
379
+ margin: 0;
380
+ color: var(--smrt-color-on-surface-variant, #43474e);
381
+ font: var(--smrt-typography-body-medium-font, 0.875rem / 1.25 sans-serif);
382
+ }
383
+
384
+ @media (prefers-reduced-motion: reduce) {
385
+ .close-btn {
386
+ transition: none;
387
+ }
388
+ }
389
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { ChatParticipantData } from '../../types.js';
2
+ export interface Props {
3
+ /** Room participants */
4
+ participants: ChatParticipantData[];
5
+ /** Callback to close the member panel */
6
+ onclose: () => void;
7
+ }
8
+ declare const MemberList: import("svelte").Component<Props, {}, "">;
9
+ type MemberList = ReturnType<typeof MemberList>;
10
+ export default MemberList;
11
+ //# sourceMappingURL=MemberList.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MemberList.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/layout/MemberList.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAG1D,MAAM,WAAW,KAAK;IACpB,wBAAwB;IACxB,YAAY,EAAE,mBAAmB,EAAE,CAAC;IACpC,yCAAyC;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAwLD,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -0,0 +1,241 @@
1
+ <script lang="ts">
2
+ /**
3
+ * RoomHeader - Room header bar with name, topic, and action buttons
4
+ * Displays room info and provides access to members and search
5
+ */
6
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
7
+ import { M } from '../../i18n.messages.js';
8
+ import type { ChatRoomData } from '../../types.js';
9
+
10
+ const { t } = useI18n();
11
+
12
+ export interface Props {
13
+ /** Current room data */
14
+ room: ChatRoomData;
15
+ /** Number of participants in the room */
16
+ participantCount: number;
17
+ /** Callback to show member list panel */
18
+ onshowmembers?: () => void;
19
+ /** Callback to show message search */
20
+ onshowsearch?: () => void;
21
+ }
22
+
23
+ let { room, participantCount, onshowmembers, onshowsearch }: Props = $props();
24
+
25
+ const roomIcon = $derived.by(() => {
26
+ switch (room.roomType) {
27
+ case 'public':
28
+ return '#';
29
+ case 'private':
30
+ return '\u{1F512}';
31
+ case 'dm':
32
+ return '';
33
+ case 'agent':
34
+ return '\u{2699}';
35
+ default:
36
+ return '#';
37
+ }
38
+ });
39
+
40
+ const roomTypeLabel = $derived.by(() => {
41
+ switch (room.roomType) {
42
+ case 'public':
43
+ return 'Public channel';
44
+ case 'private':
45
+ return 'Private channel';
46
+ case 'dm':
47
+ return 'Direct message';
48
+ case 'agent':
49
+ return 'Agent conversation';
50
+ default:
51
+ return 'Channel';
52
+ }
53
+ });
54
+ </script>
55
+
56
+ <header class="room-header" aria-label={t(M['chat.room_header.room_label'], { name: room.name })}>
57
+ <div class="room-header__info">
58
+ <div class="room-header__title-row">
59
+ {#if room.roomType !== 'dm'}
60
+ <span class="room-header__icon" aria-hidden="true">{roomIcon}</span>
61
+ {:else if room.avatarUrl}
62
+ <img class="room-header__avatar" src={room.avatarUrl} alt="" />
63
+ {:else}
64
+ <span class="room-header__avatar-placeholder">
65
+ {room.name.charAt(0).toUpperCase()}
66
+ </span>
67
+ {/if}
68
+ <h1 class="room-header__name">{room.name}</h1>
69
+ <span class="room-header__type-badge">{roomTypeLabel}</span>
70
+ </div>
71
+
72
+ {#if room.topic}
73
+ <p class="room-header__topic" title={room.topic}>
74
+ {room.topic}
75
+ </p>
76
+ {/if}
77
+ </div>
78
+
79
+ <div class="room-header__actions">
80
+ {#if onshowmembers}
81
+ <button
82
+ class="header-btn"
83
+ type="button"
84
+ onclick={onshowmembers}
85
+ aria-label={t(M['chat.room_header.show_members'], { count: participantCount })}
86
+ title={t(M['chat.room_header.members'])}
87
+ >
88
+ <svg class="header-btn__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
89
+ <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
90
+ <circle cx="9" cy="7" r="4" />
91
+ <path d="M23 21v-2a4 4 0 0 0-3-3.87" />
92
+ <path d="M16 3.13a4 4 0 0 1 0 7.75" />
93
+ </svg>
94
+ <span class="header-btn__count">{participantCount}</span>
95
+ </button>
96
+ {/if}
97
+
98
+ {#if onshowsearch}
99
+ <button
100
+ class="header-btn"
101
+ type="button"
102
+ onclick={onshowsearch}
103
+ aria-label={t(M['chat.room_header.search_messages'])}
104
+ title={t(M['chat.room_header.search'])}
105
+ >
106
+ <svg class="header-btn__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
107
+ <circle cx="11" cy="11" r="8" />
108
+ <path d="m21 21-4.35-4.35" />
109
+ </svg>
110
+ </button>
111
+ {/if}
112
+ </div>
113
+ </header>
114
+
115
+ <style>
116
+ .room-header {
117
+ display: flex;
118
+ align-items: center;
119
+ justify-content: space-between;
120
+ gap: 1rem;
121
+ padding: 0.75rem 1rem;
122
+ background: var(--smrt-color-surface, #fefbff);
123
+ border-bottom: 1px solid var(--smrt-color-outline-variant, #c4c6d0);
124
+ min-height: 3.5rem;
125
+ }
126
+
127
+ .room-header__info {
128
+ flex: 1;
129
+ min-width: 0;
130
+ display: flex;
131
+ flex-direction: column;
132
+ gap: 0.125rem;
133
+ }
134
+
135
+ .room-header__title-row {
136
+ display: flex;
137
+ align-items: center;
138
+ gap: 0.5rem;
139
+ }
140
+
141
+ .room-header__icon {
142
+ flex-shrink: 0;
143
+ font-size: var(--smrt-typography-title-medium-size, 1.125rem);
144
+ font-weight: var(--smrt-typography-weight-bold, 700);
145
+ color: var(--smrt-color-on-surface-variant, #43474e);
146
+ }
147
+
148
+ .room-header__avatar {
149
+ flex-shrink: 0;
150
+ width: 1.75rem;
151
+ height: 1.75rem;
152
+ border-radius: var(--smrt-radius-full, 9999px);
153
+ object-fit: cover;
154
+ }
155
+
156
+ .room-header__avatar-placeholder {
157
+ flex-shrink: 0;
158
+ width: 1.75rem;
159
+ height: 1.75rem;
160
+ border-radius: var(--smrt-radius-full, 9999px);
161
+ background: var(--smrt-color-primary-container, #d6e3ff);
162
+ color: var(--smrt-color-on-primary-container, #001a41);
163
+ display: inline-flex;
164
+ align-items: center;
165
+ justify-content: center;
166
+ font: var(--smrt-typography-label-medium-font, 500 0.75rem / 1 sans-serif);
167
+ }
168
+
169
+ .room-header__name {
170
+ margin: 0;
171
+ font: var(--smrt-typography-title-medium-font, 600 1rem / 1.5 sans-serif);
172
+ color: var(--smrt-color-on-surface, #1a1c1e);
173
+ overflow: hidden;
174
+ text-overflow: ellipsis;
175
+ white-space: nowrap;
176
+ }
177
+
178
+ .room-header__type-badge {
179
+ flex-shrink: 0;
180
+ padding: 0.125rem 0.5rem;
181
+ border-radius: var(--smrt-radius-full, 9999px);
182
+ background: var(--smrt-color-surface-container, #f0f0f4);
183
+ color: var(--smrt-color-on-surface-variant, #43474e);
184
+ font: var(--smrt-typography-label-small-font, 500 0.6875rem / 1 sans-serif);
185
+ }
186
+
187
+ .room-header__topic {
188
+ margin: 0;
189
+ font: var(--smrt-typography-body-small-font, 0.75rem / 1.25 sans-serif);
190
+ color: var(--smrt-color-on-surface-variant, #43474e);
191
+ overflow: hidden;
192
+ text-overflow: ellipsis;
193
+ white-space: nowrap;
194
+ padding-left: 1.75rem;
195
+ }
196
+
197
+ .room-header__actions {
198
+ flex-shrink: 0;
199
+ display: flex;
200
+ align-items: center;
201
+ gap: 0.25rem;
202
+ }
203
+
204
+ .header-btn {
205
+ display: inline-flex;
206
+ align-items: center;
207
+ gap: 0.25rem;
208
+ padding: 0.375rem 0.5rem;
209
+ border: none;
210
+ background: none;
211
+ border-radius: var(--smrt-radius-medium, 0.5rem);
212
+ color: var(--smrt-color-on-surface-variant, #43474e);
213
+ cursor: pointer;
214
+ transition: background var(--smrt-duration-short2, 150ms);
215
+ }
216
+
217
+ .header-btn:hover {
218
+ background: var(--smrt-color-surface-variant, #e1e2ec);
219
+ }
220
+
221
+ .header-btn:focus-visible {
222
+ outline: 2px solid var(--smrt-color-primary, #005ac1);
223
+ outline-offset: -2px;
224
+ }
225
+
226
+ .header-btn__icon {
227
+ width: 1.25rem;
228
+ height: 1.25rem;
229
+ }
230
+
231
+ .header-btn__count {
232
+ font: var(--smrt-typography-label-medium-font, 500 0.75rem / 1 sans-serif);
233
+ color: var(--smrt-color-on-surface-variant, #43474e);
234
+ }
235
+
236
+ @media (prefers-reduced-motion: reduce) {
237
+ .header-btn {
238
+ transition: none;
239
+ }
240
+ }
241
+ </style>
@@ -0,0 +1,15 @@
1
+ import type { ChatRoomData } from '../../types.js';
2
+ export interface Props {
3
+ /** Current room data */
4
+ room: ChatRoomData;
5
+ /** Number of participants in the room */
6
+ participantCount: number;
7
+ /** Callback to show member list panel */
8
+ onshowmembers?: () => void;
9
+ /** Callback to show message search */
10
+ onshowsearch?: () => void;
11
+ }
12
+ declare const RoomHeader: import("svelte").Component<Props, {}, "">;
13
+ type RoomHeader = ReturnType<typeof RoomHeader>;
14
+ export default RoomHeader;
15
+ //# sourceMappingURL=RoomHeader.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RoomHeader.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/layout/RoomHeader.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,MAAM,WAAW,KAAK;IACpB,wBAAwB;IACxB,IAAI,EAAE,YAAY,CAAC;IACnB,yCAAyC;IACzC,gBAAgB,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,sCAAsC;IACtC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AA8FD,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}