@happyvertical/smrt-chat 0.34.5 → 0.34.7

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 (52) hide show
  1. package/dist/manifest.json +2 -2
  2. package/dist/smrt-knowledge.json +4 -4
  3. package/dist/svelte/components/agent/AgentChat.svelte +34 -49
  4. package/dist/svelte/components/agent/AgentChat.svelte.d.ts.map +1 -1
  5. package/dist/svelte/components/agent/AgentSelector.svelte +1 -0
  6. package/dist/svelte/components/agent/AgentSelector.svelte.d.ts.map +1 -1
  7. package/dist/svelte/components/agent/AgentSessionPanel.svelte +15 -16
  8. package/dist/svelte/components/agent/AgentSessionPanel.svelte.d.ts.map +1 -1
  9. package/dist/svelte/components/agent/ToolCallDisplay.svelte +1 -0
  10. package/dist/svelte/components/agent/ToolCallDisplay.svelte.d.ts.map +1 -1
  11. package/dist/svelte/components/dialogs/RoomCreateDialog.svelte +29 -94
  12. package/dist/svelte/components/dialogs/RoomCreateDialog.svelte.d.ts.map +1 -1
  13. package/dist/svelte/components/dialogs/SearchMessages.svelte +51 -36
  14. package/dist/svelte/components/dialogs/SearchMessages.svelte.d.ts.map +1 -1
  15. package/dist/svelte/components/layout/ChatLayout.svelte +1 -0
  16. package/dist/svelte/components/layout/ChatLayout.svelte.d.ts.map +1 -1
  17. package/dist/svelte/components/layout/MemberList.svelte +10 -15
  18. package/dist/svelte/components/layout/MemberList.svelte.d.ts.map +1 -1
  19. package/dist/svelte/components/layout/RoomHeader.svelte +13 -16
  20. package/dist/svelte/components/layout/RoomHeader.svelte.d.ts.map +1 -1
  21. package/dist/svelte/components/layout/RoomList.svelte +26 -17
  22. package/dist/svelte/components/layout/RoomList.svelte.d.ts.map +1 -1
  23. package/dist/svelte/components/messages/MessageInput.svelte +34 -48
  24. package/dist/svelte/components/messages/MessageInput.svelte.d.ts.map +1 -1
  25. package/dist/svelte/components/messages/MessageItem.svelte +52 -43
  26. package/dist/svelte/components/messages/MessageItem.svelte.d.ts.map +1 -1
  27. package/dist/svelte/components/messages/ThreadPanel.svelte +9 -9
  28. package/dist/svelte/components/messages/ThreadPanel.svelte.d.ts.map +1 -1
  29. package/dist/svelte/components/shared/FileUpload.svelte +23 -54
  30. package/dist/svelte/components/shared/FileUpload.svelte.d.ts.map +1 -1
  31. package/dist/svelte/components/shared/MentionAutocomplete.svelte +1 -0
  32. package/dist/svelte/components/shared/MentionAutocomplete.svelte.d.ts.map +1 -1
  33. package/dist/svelte/components/shared/ReactionPicker.svelte +15 -66
  34. package/dist/svelte/components/shared/ReactionPicker.svelte.d.ts.map +1 -1
  35. package/dist/svelte/components/tabs/ChatTab.svelte +20 -18
  36. package/dist/svelte/components/tabs/ChatTab.svelte.d.ts.map +1 -1
  37. package/dist/svelte/components/tabs/ChatTabList.svelte +13 -10
  38. package/dist/svelte/components/tabs/ChatTabList.svelte.d.ts.map +1 -1
  39. package/dist/svelte/components/tabs/MiniChat.svelte +21 -37
  40. package/dist/svelte/components/tabs/MiniChat.svelte.d.ts.map +1 -1
  41. package/dist/svelte/index.d.ts +1 -2
  42. package/dist/svelte/index.d.ts.map +1 -1
  43. package/dist/svelte/index.js +5 -4
  44. package/package.json +9 -8
  45. package/dist/svelte/components/shared/MessageBubble.svelte +0 -81
  46. package/dist/svelte/components/shared/MessageBubble.svelte.d.ts +0 -16
  47. package/dist/svelte/components/shared/MessageBubble.svelte.d.ts.map +0 -1
  48. package/dist/svelte/components/shared/TypingIndicator.svelte +0 -90
  49. package/dist/svelte/components/shared/TypingIndicator.svelte.d.ts +0 -12
  50. package/dist/svelte/components/shared/TypingIndicator.svelte.d.ts.map +0 -1
  51. package/dist/svelte/components/shared/__tests__/MessageBubble.test.js +0 -21
  52. package/dist/svelte/components/shared/__tests__/TypingIndicator.test.js +0 -27
@@ -3,7 +3,9 @@
3
3
  * MessageInput - Chat message input bar
4
4
  * Text area with send button. Shows reply preview when replying.
5
5
  */
6
+ import { Textarea } from '@happyvertical/smrt-ui/forms';
6
7
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
8
+ import { Button } from '@happyvertical/smrt-ui/ui';
7
9
  import { M } from '../../i18n.messages.js';
8
10
 
9
11
  const { t } = useI18n();
@@ -30,7 +32,9 @@ const {
30
32
  }: Props = $props();
31
33
 
32
34
  let content = $state('');
33
- let textareaEl: HTMLTextAreaElement | undefined = $state();
35
+ // Captured from the textarea's input event so auto-resize works without binding
36
+ // to the Textarea primitive's inner DOM node (the primitive doesn't expose it).
37
+ let textareaEl: HTMLTextAreaElement | undefined;
34
38
 
35
39
  function handleSend() {
36
40
  const trimmed = content.trim();
@@ -49,10 +53,11 @@ function handleKeydown(event: KeyboardEvent) {
49
53
  }
50
54
  }
51
55
 
52
- function handleInput() {
53
- if (!textareaEl) return;
54
- textareaEl.style.height = 'auto';
55
- textareaEl.style.height = `${Math.min(textareaEl.scrollHeight, 120)}px`;
56
+ function handleInput(event: Event) {
57
+ const el = event.currentTarget as HTMLTextAreaElement;
58
+ textareaEl = el;
59
+ el.style.height = 'auto';
60
+ el.style.height = `${Math.min(el.scrollHeight, 120)}px`;
56
61
  }
57
62
  </script>
58
63
 
@@ -64,31 +69,33 @@ function handleInput() {
64
69
  <span class="message-input__reply-text">{replyTo.content}</span>
65
70
  </div>
66
71
  {#if oncancelreply}
67
- <button
72
+ <Button
73
+ variant="ghost"
74
+ size="sm"
68
75
  class="message-input__reply-close"
69
76
  type="button"
70
77
  onclick={oncancelreply}
71
78
  aria-label={t(M['chat.message_input.cancel_reply'])}
72
79
  >
73
80
  <svg viewBox="0 0 16 16" width="14" height="14"><path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" /></svg>
74
- </button>
81
+ </Button>
75
82
  {/if}
76
83
  </div>
77
84
  {/if}
78
85
 
79
86
  <div class="message-input__bar">
80
- <textarea
81
- bind:this={textareaEl}
87
+ <Textarea
82
88
  bind:value={content}
83
89
  class="message-input__textarea"
84
90
  {placeholder}
85
91
  {disabled}
86
- rows="1"
92
+ rows={1}
87
93
  onkeydown={handleKeydown}
88
94
  oninput={handleInput}
89
95
  aria-label={t(M['chat.message_input.input_label'])}
90
- ></textarea>
91
- <button
96
+ />
97
+ <Button
98
+ variant="ghost"
92
99
  class="message-input__send"
93
100
  type="button"
94
101
  onclick={handleSend}
@@ -105,7 +112,7 @@ function handleInput() {
105
112
  stroke-linejoin="round"
106
113
  />
107
114
  </svg>
108
- </button>
115
+ </Button>
109
116
  </div>
110
117
  </div>
111
118
 
@@ -155,21 +162,18 @@ function handleInput() {
155
162
  text-overflow: ellipsis;
156
163
  }
157
164
 
158
- .message-input__reply-close {
159
- display: flex;
160
- align-items: center;
161
- justify-content: center;
165
+ /* :global() pierces into the Button child's rendered <button> (see #1589). */
166
+ .message-input__reply :global(.message-input__reply-close) {
162
167
  width: 24px;
163
168
  height: 24px;
164
- border: none;
169
+ padding: 0;
165
170
  border-radius: var(--smrt-radius-small, 0.25rem);
166
171
  background: transparent;
167
172
  color: var(--smrt-color-on-surface-variant, #43474e);
168
- cursor: pointer;
169
173
  flex-shrink: 0;
170
174
  }
171
175
 
172
- .message-input__reply-close:hover {
176
+ .message-input__reply :global(.message-input__reply-close:hover) {
173
177
  background: var(--smrt-color-surface-container-high, #e1e3e8);
174
178
  }
175
179
 
@@ -180,53 +184,35 @@ function handleInput() {
180
184
  padding: var(--smrt-spacing-3, 0.75rem) var(--smrt-spacing-4, 1rem);
181
185
  }
182
186
 
183
- .message-input__textarea {
187
+ /* Layout/sizing for the Textarea primitive's rendered <textarea>; tokenised
188
+ border/focus/placeholder styling comes from the primitive. The height is
189
+ JS-driven (auto-resize), so resize is disabled and capped at max-height. */
190
+ :global(.message-input__textarea) {
184
191
  flex: 1;
185
192
  resize: none;
186
- border: 1px solid var(--smrt-color-outline-variant, #c4c6cf);
187
- border-radius: var(--smrt-radius-medium, 0.5rem);
188
- padding: var(--smrt-spacing-2, 0.375rem) var(--smrt-spacing-3, 0.75rem);
189
- font-size: var(--smrt-typography-body-medium-size, 0.875rem);
190
- font-family: inherit;
191
- line-height: 1.4;
192
- background: var(--smrt-color-surface-container-low, #f9fafb);
193
- color: var(--smrt-color-on-surface, #1b1b1f);
193
+ min-height: auto;
194
194
  max-height: 120px;
195
195
  overflow-y: auto;
196
196
  }
197
197
 
198
- .message-input__textarea:focus {
199
- outline: 2px solid var(--smrt-color-primary, #005ac1);
200
- outline-offset: -1px;
201
- border-color: transparent;
202
- }
203
-
204
- .message-input__textarea::placeholder {
205
- color: var(--smrt-color-outline, #74777f);
206
- }
207
-
208
- .message-input__send {
209
- display: flex;
210
- align-items: center;
211
- justify-content: center;
198
+ /* :global() pierces into the Button child's rendered <button> (see #1589). */
199
+ .message-input__bar :global(.message-input__send) {
212
200
  width: 36px;
213
201
  height: 36px;
214
- border: none;
202
+ padding: 0;
215
203
  border-radius: var(--smrt-radius-full, 9999px);
216
204
  background: var(--smrt-color-primary, #005ac1);
217
205
  color: var(--smrt-color-on-primary, #ffffff);
218
- cursor: pointer;
219
206
  flex-shrink: 0;
220
207
  transition: background var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease);
221
208
  }
222
209
 
223
- .message-input__send:hover:not(:disabled) {
210
+ .message-input__bar :global(.message-input__send:hover:not(:disabled)) {
224
211
  background: color-mix(in srgb, var(--smrt-color-primary, #005ac1) 85%, var(--smrt-color-shadow, #000));
225
212
  }
226
213
 
227
- .message-input__send:disabled {
214
+ .message-input__bar :global(.message-input__send:disabled) {
228
215
  background: var(--smrt-color-surface-container-high, #e1e3e8);
229
216
  color: var(--smrt-color-outline, #74777f);
230
- cursor: not-allowed;
231
217
  }
232
218
  </style>
@@ -1 +1 @@
1
- {"version":3,"file":"MessageInput.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/messages/MessageInput.svelte.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,KAAK;IACpB,sCAAsC;IACtC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wBAAwB;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6CAA6C;IAC7C,OAAO,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACrE,4BAA4B;IAC5B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AAyED,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"MessageInput.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/messages/MessageInput.svelte.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,KAAK;IACpB,sCAAsC;IACtC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wBAAwB;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6CAA6C;IAC7C,OAAO,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACrE,4BAA4B;IAC5B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AA8ED,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -4,11 +4,12 @@
4
4
  * Displays avatar, message bubble, reactions, reply preview, and tool call data.
5
5
  */
6
6
 
7
+ import { MessageBubble } from '@happyvertical/smrt-ui/chat';
7
8
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
9
+ import { Button } from '@happyvertical/smrt-ui/ui';
8
10
  import { M } from '../../i18n.messages.js';
9
11
  import type { ChatMessageData } from '../../types.js';
10
12
  import Avatar from '../shared/Avatar.svelte';
11
- import MessageBubble from '../shared/MessageBubble.svelte';
12
13
  import ReactionPicker from '../shared/ReactionPicker.svelte';
13
14
 
14
15
  const { t } = useI18n();
@@ -110,7 +111,7 @@ function handleEscape(event: KeyboardEvent) {
110
111
 
111
112
  {#if message.messageType === 'system' || message.messageType === 'action'}
112
113
  <div class="message-item message-item--system">
113
- <MessageBubble content={message.content} isOwn={false} variant="system" />
114
+ <MessageBubble content={message.content} own={false} variant="system" />
114
115
  </div>
115
116
  {:else}
116
117
  <div
@@ -146,7 +147,7 @@ function handleEscape(event: KeyboardEvent) {
146
147
 
147
148
  <MessageBubble
148
149
  content={message.content}
149
- {isOwn}
150
+ own={isOwn}
150
151
  variant={bubbleVariant}
151
152
  />
152
153
 
@@ -172,16 +173,17 @@ function handleEscape(event: KeyboardEvent) {
172
173
  {#if message.reactions.length > 0}
173
174
  <div class="message-item__reactions">
174
175
  {#each message.reactions as reaction}
175
- <button
176
- class="message-item__reaction"
177
- class:message-item__reaction--active={reaction.reacted}
176
+ <Button
177
+ variant="ghost"
178
+ size="sm"
179
+ class="message-item__reaction{reaction.reacted ? ' message-item__reaction--active' : ''}"
178
180
  type="button"
179
181
  onclick={() => onreact?.(message.id, reaction.emoji)}
180
182
  aria-label="{reaction.emoji} {reaction.count}"
181
183
  >
182
184
  <span class="message-item__reaction-emoji">{reaction.emoji}</span>
183
185
  <span class="message-item__reaction-count">{reaction.count}</span>
184
- </button>
186
+ </Button>
185
187
  {/each}
186
188
  </div>
187
189
  {/if}
@@ -195,7 +197,9 @@ function handleEscape(event: KeyboardEvent) {
195
197
  </div>
196
198
 
197
199
  {#if hasActions}
198
- <button
200
+ <Button
201
+ variant="ghost"
202
+ size="sm"
199
203
  class="message-item__more-btn"
200
204
  type="button"
201
205
  onclick={openActions}
@@ -205,7 +209,7 @@ function handleEscape(event: KeyboardEvent) {
205
209
  aria-controls={actionsId}
206
210
  >
207
211
  <svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"><circle cx="3" cy="8" r="1.3" fill="currentColor" /><circle cx="8" cy="8" r="1.3" fill="currentColor" /><circle cx="13" cy="8" r="1.3" fill="currentColor" /></svg>
208
- </button>
212
+ </Button>
209
213
  {/if}
210
214
 
211
215
  {#if showActions}
@@ -216,44 +220,52 @@ function handleEscape(event: KeyboardEvent) {
216
220
  aria-label={t(M['chat.message_item.more_actions'])}
217
221
  >
218
222
  {#if onreact}
219
- <button
223
+ <Button
224
+ variant="ghost"
225
+ size="sm"
220
226
  class="message-item__action-btn"
221
227
  type="button"
222
228
  onclick={toggleReactionPicker}
223
229
  aria-label={t(M['chat.message_item.add_reaction'])}
224
230
  >
225
231
  <svg viewBox="0 0 16 16" width="14" height="14"><circle cx="8" cy="8" r="7" fill="none" stroke="currentColor" stroke-width="1.2" /><circle cx="5.5" cy="6.5" r="0.8" fill="currentColor" /><circle cx="10.5" cy="6.5" r="0.8" fill="currentColor" /><path d="M5.5 9.5c.5 1.2 1.5 1.8 2.5 1.8s2-.6 2.5-1.8" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" /></svg>
226
- </button>
232
+ </Button>
227
233
  {/if}
228
234
  {#if onreply}
229
- <button
235
+ <Button
236
+ variant="ghost"
237
+ size="sm"
230
238
  class="message-item__action-btn"
231
239
  type="button"
232
240
  onclick={() => onreply?.(message.id)}
233
241
  aria-label={t(M['chat.message_item.reply'])}
234
242
  >
235
243
  <svg viewBox="0 0 16 16" width="14" height="14"><path d="M6 3L2 7l4 4" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" /><path d="M2 7h8c2.2 0 4 1.8 4 4v1" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" /></svg>
236
- </button>
244
+ </Button>
237
245
  {/if}
238
246
  {#if isOwn && onedit}
239
- <button
247
+ <Button
248
+ variant="ghost"
249
+ size="sm"
240
250
  class="message-item__action-btn"
241
251
  type="button"
242
252
  onclick={() => onedit?.(message.id)}
243
253
  aria-label={t(M['chat.message_item.edit'])}
244
254
  >
245
255
  <svg viewBox="0 0 16 16" width="14" height="14"><path d="M11.5 1.5l3 3L5 14H2v-3z" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" /></svg>
246
- </button>
256
+ </Button>
247
257
  {/if}
248
258
  {#if isOwn && ondelete}
249
- <button
259
+ <Button
260
+ variant="ghost"
261
+ size="sm"
250
262
  class="message-item__action-btn"
251
263
  type="button"
252
264
  onclick={() => ondelete?.(message.id)}
253
265
  aria-label={t(M['chat.message_item.delete'])}
254
266
  >
255
267
  <svg viewBox="0 0 16 16" width="14" height="14"><path d="M2 4h12M5.3 4V2.7A.7.7 0 016 2h4a.7.7 0 01.7.7V4m1.6 0v9.3a1.4 1.4 0 01-1.4 1.4H5.1a1.4 1.4 0 01-1.4-1.4V4" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" /></svg>
256
- </button>
268
+ </Button>
257
269
  {/if}
258
270
  </div>
259
271
 
@@ -391,24 +403,22 @@ function handleEscape(event: KeyboardEvent) {
391
403
  gap: var(--smrt-spacing-1, 0.25rem);
392
404
  }
393
405
 
394
- .message-item__reaction {
395
- display: inline-flex;
396
- align-items: center;
406
+ /* :global() pierces into each Button's rendered <button> (see #1589). */
407
+ .message-item__reactions :global(.message-item__reaction) {
397
408
  gap: var(--smrt-spacing-1, 4px);
398
409
  padding: var(--smrt-spacing-1, 4px) var(--smrt-spacing-2, 8px);
399
410
  border: 1px solid var(--smrt-color-outline-variant, #c4c6cf);
400
411
  border-radius: var(--smrt-radius-full, 9999px);
401
412
  background: var(--smrt-color-surface, #ffffff);
402
- cursor: pointer;
403
413
  font-size: var(--smrt-typography-label-medium-size, 0.75rem);
404
414
  transition: background var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease);
405
415
  }
406
416
 
407
- .message-item__reaction:hover {
417
+ .message-item__reactions :global(.message-item__reaction:hover) {
408
418
  background: var(--smrt-color-surface-container-high, #e1e3e8);
409
419
  }
410
420
 
411
- .message-item__reaction--active {
421
+ .message-item__reactions :global(.message-item__reaction--active) {
412
422
  border-color: var(--smrt-color-primary, #005ac1);
413
423
  background: var(--smrt-color-primary-container, #d6e3ff);
414
424
  }
@@ -441,12 +451,11 @@ function handleEscape(event: KeyboardEvent) {
441
451
  font-style: italic;
442
452
  }
443
453
 
444
- .message-item__more-btn {
445
- display: flex;
446
- align-items: center;
447
- justify-content: center;
454
+ /* :global() pierces into the Button child's rendered <button> (see #1589). */
455
+ .message-item :global(.message-item__more-btn) {
448
456
  width: 26px;
449
457
  height: 26px;
458
+ padding: 0;
450
459
  position: absolute;
451
460
  top: 0;
452
461
  right: var(--smrt-spacing-4, 1rem);
@@ -454,12 +463,11 @@ function handleEscape(event: KeyboardEvent) {
454
463
  border-radius: var(--smrt-radius-small, 0.25rem);
455
464
  background: var(--smrt-color-surface, #ffffff);
456
465
  color: var(--smrt-color-on-surface-variant, #43474e);
457
- cursor: pointer;
458
466
  opacity: 0;
459
467
  transition: opacity var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease);
460
468
  }
461
469
 
462
- .message-item--own .message-item__more-btn {
470
+ .message-item--own :global(.message-item__more-btn) {
463
471
  right: auto;
464
472
  left: var(--smrt-spacing-4, 1rem);
465
473
  }
@@ -467,19 +475,19 @@ function handleEscape(event: KeyboardEvent) {
467
475
  /* Reveal the keyboard/touch affordance on hover OR when focus is inside the
468
476
  row, so it is never strictly hover-only (a11y blocker, T2 #1391). Always
469
477
  visible to coarse pointers (touch) which cannot hover. */
470
- .message-item:hover .message-item__more-btn,
471
- .message-item:focus-within .message-item__more-btn {
478
+ .message-item:hover :global(.message-item__more-btn),
479
+ .message-item:focus-within :global(.message-item__more-btn) {
472
480
  opacity: 1;
473
481
  }
474
482
 
475
- .message-item__more-btn:focus-visible {
483
+ .message-item :global(.message-item__more-btn:focus-visible) {
476
484
  opacity: 1;
477
485
  outline: 2px solid var(--smrt-color-primary, #005ac1);
478
486
  outline-offset: 1px;
479
487
  }
480
488
 
481
489
  @media (hover: none) {
482
- .message-item__more-btn {
490
+ .message-item :global(.message-item__more-btn) {
483
491
  opacity: 1;
484
492
  }
485
493
  }
@@ -504,21 +512,18 @@ function handleEscape(event: KeyboardEvent) {
504
512
  left: var(--smrt-spacing-4, 1rem);
505
513
  }
506
514
 
507
- .message-item__action-btn {
508
- display: flex;
509
- align-items: center;
510
- justify-content: center;
515
+ /* :global() pierces into each Button's rendered <button> (see #1589). */
516
+ .message-item__actions :global(.message-item__action-btn) {
511
517
  width: 26px;
512
518
  height: 26px;
513
- border: none;
519
+ padding: 0;
514
520
  border-radius: var(--smrt-radius-small, 0.25rem);
515
521
  background: transparent;
516
522
  color: var(--smrt-color-on-surface-variant, #43474e);
517
- cursor: pointer;
518
523
  transition: background var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease);
519
524
  }
520
525
 
521
- .message-item__action-btn:hover {
526
+ .message-item__actions :global(.message-item__action-btn:hover) {
522
527
  background: var(--smrt-color-surface-container-high, #e1e3e8);
523
528
  }
524
529
 
@@ -528,6 +533,10 @@ function handleEscape(event: KeyboardEvent) {
528
533
  right: var(--smrt-spacing-4, 1rem);
529
534
  z-index: 10;
530
535
  transform: translateY(-100%);
536
+ /* The picker primitive supplies its own surface + radius; the popover
537
+ wrapper lifts it off the conversation with matching elevation. */
538
+ border-radius: var(--smrt-radius-large, 0.75rem);
539
+ box-shadow: var(--smrt-elevation-2, 0 2px 6px rgba(0, 0, 0, 0.15));
531
540
  }
532
541
 
533
542
  .message-item--own .message-item__picker-popover {
@@ -536,9 +545,9 @@ function handleEscape(event: KeyboardEvent) {
536
545
  }
537
546
 
538
547
  @media (prefers-reduced-motion: reduce) {
539
- .message-item__more-btn,
540
- .message-item__action-btn,
541
- .message-item__reaction {
548
+ .message-item :global(.message-item__more-btn),
549
+ .message-item__actions :global(.message-item__action-btn),
550
+ .message-item__reactions :global(.message-item__reaction) {
542
551
  transition: none;
543
552
  }
544
553
  }
@@ -1 +1 @@
1
- {"version":3,"file":"MessageItem.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/messages/MessageItem.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAMtD,MAAM,WAAW,KAAK;IACpB,6BAA6B;IAC7B,OAAO,EAAE,eAAe,CAAC;IACzB,wDAAwD;IACxD,KAAK,EAAE,OAAO,CAAC;IACf,qBAAqB;IACrB,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,qBAAqB;IACrB,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,oBAAoB;IACpB,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,sBAAsB;IACtB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAuMD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"MessageItem.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/messages/MessageItem.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAKtD,MAAM,WAAW,KAAK;IACpB,6BAA6B;IAC7B,OAAO,EAAE,eAAe,CAAC;IACzB,wDAAwD;IACxD,KAAK,EAAE,OAAO,CAAC;IACf,qBAAqB;IACrB,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,qBAAqB;IACrB,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,oBAAoB;IACpB,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,sBAAsB;IACtB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAwMD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
8
+ import { Button } from '@happyvertical/smrt-ui/ui';
8
9
  import { M } from '../../i18n.messages.js';
9
10
  import type { ChatMessageData, ChatThreadData } from '../../types.js';
10
11
  import MessageInput from './MessageInput.svelte';
@@ -47,7 +48,9 @@ const replyCount = $derived(
47
48
  {#if thread.isResolved}
48
49
  <span class="thread-panel__resolved">Resolved</span>
49
50
  {/if}
50
- <button
51
+ <Button
52
+ variant="ghost"
53
+ size="sm"
51
54
  class="thread-panel__close"
52
55
  type="button"
53
56
  onclick={onclose}
@@ -56,7 +59,7 @@ const replyCount = $derived(
56
59
  <svg viewBox="0 0 16 16" width="16" height="16">
57
60
  <path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
58
61
  </svg>
59
- </button>
62
+ </Button>
60
63
  </header>
61
64
 
62
65
  <div class="thread-panel__messages">
@@ -124,22 +127,19 @@ const replyCount = $derived(
124
127
  flex-shrink: 0;
125
128
  }
126
129
 
127
- .thread-panel__close {
128
- display: flex;
129
- align-items: center;
130
- justify-content: center;
130
+ /* :global() pierces into the Button child's rendered <button> (see #1589). */
131
+ .thread-panel__header :global(.thread-panel__close) {
131
132
  width: 32px;
132
133
  height: 32px;
133
- border: none;
134
+ padding: 0;
134
135
  border-radius: var(--smrt-radius-small, 0.25rem);
135
136
  background: transparent;
136
137
  color: var(--smrt-color-on-surface-variant, #43474e);
137
- cursor: pointer;
138
138
  flex-shrink: 0;
139
139
  transition: background var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease);
140
140
  }
141
141
 
142
- .thread-panel__close:hover {
142
+ .thread-panel__header :global(.thread-panel__close:hover) {
143
143
  background: var(--smrt-color-surface-container-high, #e1e3e8);
144
144
  }
145
145
 
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadPanel.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/messages/ThreadPanel.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKtE,MAAM,WAAW,KAAK;IACpB,sBAAsB;IACtB,MAAM,EAAE,cAAc,CAAC;IACvB,6BAA6B;IAC7B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,gCAAgC;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,iCAAiC;IACjC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,6BAA6B;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAsDD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"ThreadPanel.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/messages/ThreadPanel.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKtE,MAAM,WAAW,KAAK;IACpB,sBAAsB;IACtB,MAAM,EAAE,cAAc,CAAC;IACvB,6BAA6B;IAC7B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,gCAAgC;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,iCAAiC;IACjC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,6BAA6B;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAuDD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -5,6 +5,7 @@
5
5
  * Supports file type filtering and max size constraints.
6
6
  */
7
7
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
8
+ import { Button } from '@happyvertical/smrt-ui/ui';
8
9
  import { M } from '../../i18n.js';
9
10
 
10
11
  const { t } = useI18n();
@@ -116,7 +117,9 @@ function getFileIcon(type: string): string {
116
117
  <span class="file-upload__file-name">{file.name}</span>
117
118
  <span class="file-upload__file-size">{formatSize(file.size)}</span>
118
119
  </div>
119
- <button
120
+ <Button
121
+ variant="ghost"
122
+ size="sm"
120
123
  class="file-upload__file-remove"
121
124
  type="button"
122
125
  onclick={() => removePending(i)}
@@ -125,25 +128,27 @@ function getFileIcon(type: string): string {
125
128
  <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
126
129
  <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
127
130
  </svg>
128
- </button>
131
+ </Button>
129
132
  </div>
130
133
  {/each}
131
134
 
132
135
  <div class="file-upload__preview-actions">
133
- <button
134
- class="file-upload__confirm-btn"
136
+ <Button
137
+ variant="primary"
138
+ size="sm"
135
139
  type="button"
136
140
  onclick={confirmUpload}
137
141
  >
138
142
  {t(M['chat.file_upload.upload_files'], { count: pendingFiles.length, plural: pendingFiles.length !== 1 ? 's' : '' })}
139
- </button>
140
- <button
141
- class="file-upload__clear-btn"
143
+ </Button>
144
+ <Button
145
+ variant="secondary"
146
+ size="sm"
142
147
  type="button"
143
148
  onclick={() => { pendingFiles = []; sizeError = ''; }}
144
149
  >
145
150
  Clear
146
- </button>
151
+ </Button>
147
152
  </div>
148
153
  </div>
149
154
  {/if}
@@ -164,6 +169,7 @@ function getFileIcon(type: string): string {
164
169
  ondragleave={handleDragLeave}
165
170
  >
166
171
  <label class="file-upload__label">
172
+ <!-- raw-primitive-allow: visually-hidden native file input that backs a custom drag-and-drop zone; the base Input primitive renders a visible bordered control and cannot be the hidden picker behind this drop zone -->
167
173
  <input
168
174
  type="file"
169
175
  class="file-upload__input"
@@ -302,22 +308,19 @@ function getFileIcon(type: string): string {
302
308
  color: var(--smrt-color-outline, #74777f);
303
309
  }
304
310
 
305
- .file-upload__file-remove {
306
- display: inline-flex;
307
- align-items: center;
308
- justify-content: center;
311
+ /* :global() pierces into the Button child's rendered <button> (see #1589). */
312
+ .file-upload__file :global(.file-upload__file-remove) {
309
313
  width: 24px;
310
314
  height: 24px;
311
- border: none;
315
+ padding: 0;
312
316
  background: none;
313
317
  color: var(--smrt-color-on-surface-variant, #43474e);
314
- cursor: pointer;
315
318
  border-radius: var(--smrt-radius-full, 9999px);
316
319
  flex-shrink: 0;
317
320
  transition: background var(--smrt-duration-short2, 150ms);
318
321
  }
319
322
 
320
- .file-upload__file-remove:hover {
323
+ .file-upload__file :global(.file-upload__file-remove:hover) {
321
324
  background: var(--smrt-color-error-container, #ffdad6);
322
325
  color: var(--smrt-color-error, #ba1a1a);
323
326
  }
@@ -328,41 +331,6 @@ function getFileIcon(type: string): string {
328
331
  padding-top: var(--smrt-spacing-1, 4px);
329
332
  }
330
333
 
331
- .file-upload__confirm-btn {
332
- padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-4, 16px);
333
- border: none;
334
- border-radius: var(--smrt-radius-full, 9999px);
335
- background: var(--smrt-color-primary, #005ac1);
336
- color: var(--smrt-color-on-primary, #ffffff);
337
- font: var(--smrt-typography-label-large-font, 500 0.875rem/1.25 sans-serif);
338
- cursor: pointer;
339
- transition: opacity var(--smrt-duration-short2, 150ms);
340
- }
341
-
342
- .file-upload__confirm-btn:hover {
343
- opacity: 0.85;
344
- }
345
-
346
- .file-upload__confirm-btn:focus-visible {
347
- outline: 2px solid var(--smrt-color-primary, #005ac1);
348
- outline-offset: 2px;
349
- }
350
-
351
- .file-upload__clear-btn {
352
- padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-4, 16px);
353
- border: 1px solid var(--smrt-color-outline, #74777f);
354
- border-radius: var(--smrt-radius-full, 9999px);
355
- background: transparent;
356
- color: var(--smrt-color-on-surface-variant, #43474e);
357
- font: var(--smrt-typography-label-large-font, 500 0.875rem/1.25 sans-serif);
358
- cursor: pointer;
359
- transition: background var(--smrt-duration-short2, 150ms);
360
- }
361
-
362
- .file-upload__clear-btn:hover {
363
- background: var(--smrt-color-surface-variant, #e1e2ec);
364
- }
365
-
366
334
  .file-upload__error {
367
335
  padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-3, 12px);
368
336
  border-radius: var(--smrt-radius-small, 4px);
@@ -372,10 +340,11 @@ function getFileIcon(type: string): string {
372
340
  }
373
341
 
374
342
  @media (prefers-reduced-motion: reduce) {
375
- .file-upload__drop-zone,
376
- .file-upload__file-remove,
377
- .file-upload__confirm-btn,
378
- .file-upload__clear-btn {
343
+ .file-upload__drop-zone {
344
+ transition: none;
345
+ }
346
+
347
+ .file-upload__file :global(.file-upload__file-remove) {
379
348
  transition: none;
380
349
  }
381
350
  }
@@ -1 +1 @@
1
- {"version":3,"file":"FileUpload.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/shared/FileUpload.svelte.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,KAAK;IACpB,uCAAuC;IACvC,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAyJD,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"FileUpload.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/shared/FileUpload.svelte.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,KAAK;IACpB,uCAAuC;IACvC,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA2JD,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}