@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
  * SearchMessages - Message search interface with results list
4
4
  * Provides a search input and displays matching messages
5
5
  */
6
+ import { Form, Input } 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.js';
8
10
  import type { ChatMessageData } from '../../types.js';
9
11
 
@@ -25,11 +27,16 @@ export interface Props {
25
27
  let { isOpen, onclose, onsearch, results, onselectresult }: Props = $props();
26
28
 
27
29
  let query = $state('');
28
- let searchInput = $state<HTMLInputElement | null>(null);
29
30
 
30
31
  const hasResults = $derived(results.length > 0);
31
32
  const hasQuery = $derived(query.trim().length > 0);
32
33
 
34
+ // The Input primitive doesn't expose its inner element, so resolve it by id.
35
+ function focusSearchInput() {
36
+ if (typeof document === 'undefined') return;
37
+ document.getElementById('search-messages-input')?.focus();
38
+ }
39
+
33
40
  function handleSubmit() {
34
41
  const trimmed = query.trim();
35
42
  if (trimmed.length > 0) {
@@ -94,8 +101,10 @@ $effect(() => {
94
101
  });
95
102
 
96
103
  $effect(() => {
97
- if (isOpen && searchInput) {
98
- searchInput.focus();
104
+ if (isOpen) {
105
+ // Defer a frame so the search input is mounted before focusing.
106
+ const raf = requestAnimationFrame(focusSearchInput);
107
+ return () => cancelAnimationFrame(raf);
99
108
  }
100
109
  });
101
110
  </script>
@@ -108,7 +117,9 @@ $effect(() => {
108
117
  >
109
118
  <div class="search-panel__header">
110
119
  <h2 class="search-panel__title">{t(M['chat.search_messages.title'])}</h2>
111
- <button
120
+ <Button
121
+ variant="ghost"
122
+ size="sm"
112
123
  class="close-btn"
113
124
  type="button"
114
125
  onclick={handleClose}
@@ -118,10 +129,10 @@ $effect(() => {
118
129
  <path d="M18 6 6 18" />
119
130
  <path d="m6 6 12 12" />
120
131
  </svg>
121
- </button>
132
+ </Button>
122
133
  </div>
123
134
 
124
- <form
135
+ <Form
125
136
  class="search-form"
126
137
  onsubmit={(e) => { e.preventDefault(); handleSubmit(); }}
127
138
  >
@@ -130,9 +141,9 @@ $effect(() => {
130
141
  <circle cx="11" cy="11" r="8" />
131
142
  <path d="m21 21-4.35-4.35" />
132
143
  </svg>
133
- <input
134
- bind:this={searchInput}
144
+ <Input
135
145
  bind:value={query}
146
+ id="search-messages-input"
136
147
  type="search"
137
148
  class="search-input"
138
149
  placeholder={t(M['chat.search_messages.input_placeholder'])}
@@ -140,20 +151,22 @@ $effect(() => {
140
151
  aria-label={t(M['chat.search_messages.query'])}
141
152
  />
142
153
  {#if hasQuery}
143
- <button
154
+ <Button
155
+ variant="ghost"
156
+ size="sm"
144
157
  class="clear-btn"
145
158
  type="button"
146
- onclick={() => { query = ''; searchInput?.focus(); }}
159
+ onclick={() => { query = ''; focusSearchInput(); }}
147
160
  aria-label={t(M['chat.search_messages.clear'])}
148
161
  >
149
162
  <svg class="clear-btn__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
150
163
  <path d="M18 6 6 18" />
151
164
  <path d="m6 6 12 12" />
152
165
  </svg>
153
- </button>
166
+ </Button>
154
167
  {/if}
155
168
  </div>
156
- </form>
169
+ </Form>
157
170
 
158
171
  <div class="search-results" aria-label={t(M['chat.search_messages.results'])}>
159
172
  {#if hasResults}
@@ -163,6 +176,7 @@ $effect(() => {
163
176
  <ul class="results-list" aria-label={t(M['chat.search_messages.results'])}>
164
177
  {#each results as message (message.id)}
165
178
  <li class="results-list__item">
179
+ <!-- raw-primitive-allow: large pressable search-result card wrapping rich content (avatar, sender, date, multi-line highlighted message excerpt); no Button primitive owns this structural selection-card pattern -->
166
180
  <button
167
181
  class="result-item"
168
182
  type="button"
@@ -231,35 +245,28 @@ $effect(() => {
231
245
  color: var(--smrt-color-on-surface, #1a1c1e);
232
246
  }
233
247
 
234
- .close-btn {
235
- display: inline-flex;
236
- align-items: center;
237
- justify-content: center;
248
+ /* :global() pierces into the Button child's rendered <button> (see #1589). */
249
+ .search-panel__header :global(.close-btn) {
238
250
  width: 1.75rem;
239
251
  height: 1.75rem;
240
- border: none;
252
+ padding: 0;
241
253
  background: none;
242
254
  border-radius: var(--smrt-radius-medium, 0.5rem);
243
255
  color: var(--smrt-color-on-surface-variant, #43474e);
244
- cursor: pointer;
245
256
  transition: background var(--smrt-duration-short2, 150ms);
246
257
  }
247
258
 
248
- .close-btn:hover {
259
+ .search-panel__header :global(.close-btn:hover) {
249
260
  background: var(--smrt-color-surface-variant, #e1e2ec);
250
261
  }
251
262
 
252
- .close-btn:focus-visible {
253
- outline: 2px solid var(--smrt-color-primary, #005ac1);
254
- outline-offset: -2px;
255
- }
256
-
257
263
  .close-btn__icon {
258
264
  width: 1rem;
259
265
  height: 1rem;
260
266
  }
261
267
 
262
- .search-form {
268
+ /* :global() targets the Form primitive's rendered <form> (see #1589 scoping trap). */
269
+ :global(.search-form) {
263
270
  padding: 0.75rem 1rem;
264
271
  border-bottom: 1px solid var(--smrt-color-outline-variant, #c4c6d0);
265
272
  }
@@ -287,34 +294,39 @@ $effect(() => {
287
294
  color: var(--smrt-color-on-surface-variant, #43474e);
288
295
  }
289
296
 
290
- .search-input {
297
+ /* The Input primitive sits inside the bordered .search-input-wrap, which owns
298
+ the border + focus-within ring — so neutralize the primitive's own chrome
299
+ (border/background/padding/box-shadow) and let it render borderless. */
300
+ :global(.search-input) {
291
301
  flex: 1;
292
302
  border: none;
293
303
  outline: none;
304
+ padding: 0;
294
305
  background: transparent;
306
+ box-shadow: none;
295
307
  font: var(--smrt-typography-body-medium-font, 0.875rem / 1.25 sans-serif);
296
308
  color: var(--smrt-color-on-surface, #1a1c1e);
297
309
  }
298
310
 
299
- .search-input::placeholder {
300
- color: var(--smrt-color-on-surface-variant, #43474e);
311
+ :global(.search-input:focus) {
312
+ border: none;
313
+ outline: none;
314
+ box-shadow: none;
301
315
  }
302
316
 
303
- .clear-btn {
317
+ /* :global() pierces into the Button child's rendered <button> (see #1589). */
318
+ .search-input-wrap :global(.clear-btn) {
304
319
  flex-shrink: 0;
305
- display: inline-flex;
306
- align-items: center;
307
- justify-content: center;
308
320
  width: 1.25rem;
309
321
  height: 1.25rem;
310
- border: none;
322
+ padding: 0;
311
323
  background: none;
312
324
  border-radius: var(--smrt-radius-full, 9999px);
313
325
  color: var(--smrt-color-on-surface-variant, #43474e);
314
- cursor: pointer;
315
326
  }
316
327
 
317
- .clear-btn:hover {
328
+ .search-input-wrap :global(.clear-btn:hover) {
329
+ background: none;
318
330
  color: var(--smrt-color-on-surface, #1a1c1e);
319
331
  }
320
332
 
@@ -448,10 +460,13 @@ $effect(() => {
448
460
  }
449
461
 
450
462
  @media (prefers-reduced-motion: reduce) {
451
- .close-btn,
452
463
  .result-item,
453
464
  .search-input-wrap {
454
465
  transition: none;
455
466
  }
467
+
468
+ .search-panel__header :global(.close-btn) {
469
+ transition: none;
470
+ }
456
471
  }
457
472
  </style>
@@ -1 +1 @@
1
- {"version":3,"file":"SearchMessages.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/dialogs/SearchMessages.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGtD,MAAM,WAAW,KAAK;IACpB,uCAAuC;IACvC,MAAM,EAAE,OAAO,CAAC;IAChB,yCAAyC;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gDAAgD;IAChD,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,qBAAqB;IACrB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,yCAAyC;IACzC,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAyKD,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"SearchMessages.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/dialogs/SearchMessages.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGtD,MAAM,WAAW,KAAK;IACpB,uCAAuC;IACvC,MAAM,EAAE,OAAO,CAAC;IAChB,yCAAyC;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gDAAgD;IAChD,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,qBAAqB;IACrB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,yCAAyC;IACzC,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAmLD,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -72,6 +72,7 @@ function handlePointerUp() {
72
72
  />
73
73
  </aside>
74
74
 
75
+ <!-- raw-primitive-allow: custom drag-to-resize splitter with pointer capture (pointerdown/move/up) and ArrowLeft/ArrowRight keyboard resizing; a press-and-drag control no Button primitive owns -->
75
76
  <button
76
77
  type="button"
77
78
  class="chat-layout__resize-handle"
@@ -1 +1 @@
1
- {"version":3,"file":"ChatLayout.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/layout/ChatLayout.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAInD,MAAM,WAAW,KAAK;IACpB,2BAA2B;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,iCAAiC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,uCAAuC;IACvC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,wBAAwB;IACxB,QAAQ,EAAE,OAAO,CAAC;CACnB;AA+DD,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"ChatLayout.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/layout/ChatLayout.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAInD,MAAM,WAAW,KAAK;IACpB,2BAA2B;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,iCAAiC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,uCAAuC;IACvC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,wBAAwB;IACxB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAgED,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -4,6 +4,7 @@
4
4
  * Lists participants grouped by online status with role badges
5
5
  */
6
6
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
7
+ import { Button } from '@happyvertical/smrt-ui/ui';
7
8
  import { M } from '../../i18n.messages.js';
8
9
  import type { ChatParticipantData } from '../../types.js';
9
10
 
@@ -74,7 +75,9 @@ function getRoleBadgeLabel(role: string): string {
74
75
  <div class="member-list__header">
75
76
  <h2 class="member-list__title">Members</h2>
76
77
  <span class="member-list__count">{participants.length}</span>
77
- <button
78
+ <Button
79
+ variant="ghost"
80
+ size="sm"
78
81
  class="close-btn"
79
82
  type="button"
80
83
  onclick={onclose}
@@ -84,7 +87,7 @@ function getRoleBadgeLabel(role: string): string {
84
87
  <path d="M18 6 6 18" />
85
88
  <path d="m6 6 12 12" />
86
89
  </svg>
87
- </button>
90
+ </Button>
88
91
  </div>
89
92
 
90
93
  <div class="member-list__content">
@@ -235,30 +238,22 @@ function getRoleBadgeLabel(role: string): string {
235
238
  color: var(--smrt-color-on-surface-variant, #43474e);
236
239
  }
237
240
 
238
- .close-btn {
241
+ /* :global() pierces into the Button child's rendered <button> (see #1589). */
242
+ .member-list__header :global(.close-btn) {
239
243
  margin-left: auto;
240
- display: inline-flex;
241
- align-items: center;
242
- justify-content: center;
243
244
  width: 1.75rem;
244
245
  height: 1.75rem;
245
- border: none;
246
+ padding: 0;
246
247
  background: none;
247
248
  border-radius: var(--smrt-radius-medium, 0.5rem);
248
249
  color: var(--smrt-color-on-surface-variant, #43474e);
249
- cursor: pointer;
250
250
  transition: background var(--smrt-duration-short2, 150ms);
251
251
  }
252
252
 
253
- .close-btn:hover {
253
+ .member-list__header :global(.close-btn:hover) {
254
254
  background: var(--smrt-color-surface-variant, #e1e2ec);
255
255
  }
256
256
 
257
- .close-btn:focus-visible {
258
- outline: 2px solid var(--smrt-color-primary, #005ac1);
259
- outline-offset: -2px;
260
- }
261
-
262
257
  .close-btn__icon {
263
258
  width: 1rem;
264
259
  height: 1rem;
@@ -384,7 +379,7 @@ function getRoleBadgeLabel(role: string): string {
384
379
  }
385
380
 
386
381
  @media (prefers-reduced-motion: reduce) {
387
- .close-btn {
382
+ .member-list__header :global(.close-btn) {
388
383
  transition: none;
389
384
  }
390
385
  }
@@ -1 +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;AA0LD,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"MemberList.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/layout/MemberList.svelte.ts"],"names":[],"mappings":"AAUA,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;AA2LD,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -4,6 +4,7 @@
4
4
  * Displays room info and provides access to members and search
5
5
  */
6
6
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
7
+ import { Button } from '@happyvertical/smrt-ui/ui';
7
8
  import { M } from '../../i18n.messages.js';
8
9
  import type { ChatRoomData } from '../../types.js';
9
10
 
@@ -78,7 +79,9 @@ const roomTypeLabel = $derived.by(() => {
78
79
 
79
80
  <div class="room-header__actions">
80
81
  {#if onshowmembers}
81
- <button
82
+ <Button
83
+ variant="ghost"
84
+ size="sm"
82
85
  class="header-btn"
83
86
  type="button"
84
87
  onclick={onshowmembers}
@@ -92,11 +95,13 @@ const roomTypeLabel = $derived.by(() => {
92
95
  <path d="M16 3.13a4 4 0 0 1 0 7.75" />
93
96
  </svg>
94
97
  <span class="header-btn__count">{participantCount}</span>
95
- </button>
98
+ </Button>
96
99
  {/if}
97
100
 
98
101
  {#if onshowsearch}
99
- <button
102
+ <Button
103
+ variant="ghost"
104
+ size="sm"
100
105
  class="header-btn"
101
106
  type="button"
102
107
  onclick={onshowsearch}
@@ -107,7 +112,7 @@ const roomTypeLabel = $derived.by(() => {
107
112
  <circle cx="11" cy="11" r="8" />
108
113
  <path d="m21 21-4.35-4.35" />
109
114
  </svg>
110
- </button>
115
+ </Button>
111
116
  {/if}
112
117
  </div>
113
118
  </header>
@@ -201,28 +206,20 @@ const roomTypeLabel = $derived.by(() => {
201
206
  gap: 0.25rem;
202
207
  }
203
208
 
204
- .header-btn {
205
- display: inline-flex;
206
- align-items: center;
209
+ /* :global() pierces into each Button's rendered <button> (see #1589). */
210
+ .room-header__actions :global(.header-btn) {
207
211
  gap: 0.25rem;
208
212
  padding: 0.375rem 0.5rem;
209
- border: none;
210
213
  background: none;
211
214
  border-radius: var(--smrt-radius-medium, 0.5rem);
212
215
  color: var(--smrt-color-on-surface-variant, #43474e);
213
- cursor: pointer;
214
216
  transition: background var(--smrt-duration-short2, 150ms);
215
217
  }
216
218
 
217
- .header-btn:hover {
219
+ .room-header__actions :global(.header-btn:hover) {
218
220
  background: var(--smrt-color-surface-variant, #e1e2ec);
219
221
  }
220
222
 
221
- .header-btn:focus-visible {
222
- outline: 2px solid var(--smrt-color-primary, #005ac1);
223
- outline-offset: -2px;
224
- }
225
-
226
223
  .header-btn__icon {
227
224
  width: 1.25rem;
228
225
  height: 1.25rem;
@@ -234,7 +231,7 @@ const roomTypeLabel = $derived.by(() => {
234
231
  }
235
232
 
236
233
  @media (prefers-reduced-motion: reduce) {
237
- .header-btn {
234
+ .room-header__actions :global(.header-btn) {
238
235
  transition: none;
239
236
  }
240
237
  }
@@ -1 +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"}
1
+ {"version":3,"file":"RoomHeader.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/layout/RoomHeader.svelte.ts"],"names":[],"mappings":"AAUA,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;AA+FD,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -4,6 +4,7 @@
4
4
  * Groups rooms by type: channels, DMs, and agent conversations
5
5
  */
6
6
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
7
+ import { Button } from '@happyvertical/smrt-ui/ui';
7
8
  import { M } from '../../i18n.messages.js';
8
9
  import type { ChatRoomData } from '../../types.js';
9
10
 
@@ -71,19 +72,22 @@ function formatTimestamp(date?: string | Date | null): string {
71
72
  <nav class="room-list" aria-label={t(M['chat.room_list.rooms_label'])}>
72
73
  {#if oncreateroom}
73
74
  <div class="room-list__header">
74
- <button
75
+ <Button
76
+ variant="ghost"
77
+ fullWidth
75
78
  class="create-room-btn"
76
79
  type="button"
77
80
  onclick={oncreateroom}
78
81
  aria-label={t(M['chat.room_list.create_new_room'])}
79
82
  >
80
83
  {t(M['chat.room_list.new_room'])}
81
- </button>
84
+ </Button>
82
85
  </div>
83
86
  {/if}
84
87
 
85
88
  {#if channels.length > 0}
86
89
  <section class="room-group">
90
+ <!-- raw-primitive-allow: section disclosure trigger with aria-expanded toggling a collapsible room list, wrapping rich content (rotating chevron, group label, room count); structural accordion header no Button primitive owns -->
87
91
  <button
88
92
  class="room-group__header"
89
93
  type="button"
@@ -101,6 +105,7 @@ function formatTimestamp(date?: string | Date | null): string {
101
105
  <ul class="room-group__list" role="list">
102
106
  {#each channels as room (room.id)}
103
107
  <li>
108
+ <!-- raw-primitive-allow: pressable room-selection row with active and aria-current state, wrapping rich content (type icon, name, unread badge); no Button primitive owns this structural nav-selection pattern -->
104
109
  <button
105
110
  class="room-item"
106
111
  class:room-item--active={room.id === currentRoomId}
@@ -126,6 +131,7 @@ function formatTimestamp(date?: string | Date | null): string {
126
131
 
127
132
  {#if directMessages.length > 0}
128
133
  <section class="room-group">
134
+ <!-- raw-primitive-allow: section disclosure trigger with aria-expanded toggling a collapsible room list, wrapping rich content (rotating chevron, group label, room count); structural accordion header no Button primitive owns -->
129
135
  <button
130
136
  class="room-group__header"
131
137
  type="button"
@@ -143,6 +149,7 @@ function formatTimestamp(date?: string | Date | null): string {
143
149
  <ul class="room-group__list" role="list">
144
150
  {#each directMessages as room (room.id)}
145
151
  <li>
152
+ <!-- raw-primitive-allow: pressable room-selection row with active and aria-current state, wrapping rich content (avatar, name, last-message preview, timestamp, unread badge); no Button primitive owns this structural nav-selection pattern -->
146
153
  <button
147
154
  class="room-item"
148
155
  class:room-item--active={room.id === currentRoomId}
@@ -184,6 +191,7 @@ function formatTimestamp(date?: string | Date | null): string {
184
191
 
185
192
  {#if agentRooms.length > 0}
186
193
  <section class="room-group">
194
+ <!-- raw-primitive-allow: section disclosure trigger with aria-expanded toggling a collapsible room list, wrapping rich content (rotating chevron, group label, room count); structural accordion header no Button primitive owns -->
187
195
  <button
188
196
  class="room-group__header"
189
197
  type="button"
@@ -201,6 +209,7 @@ function formatTimestamp(date?: string | Date | null): string {
201
209
  <ul class="room-group__list" role="list">
202
210
  {#each agentRooms as room (room.id)}
203
211
  <li>
212
+ <!-- raw-primitive-allow: pressable room-selection row with active and aria-current state, wrapping rich content (agent icon, name, unread badge); no Button primitive owns this structural nav-selection pattern -->
204
213
  <button
205
214
  class="room-item"
206
215
  class:room-item--active={room.id === currentRoomId}
@@ -227,9 +236,9 @@ function formatTimestamp(date?: string | Date | null): string {
227
236
  <div class="room-list__empty">
228
237
  <p>{t(M['chat.room_list.no_rooms'])}</p>
229
238
  {#if oncreateroom}
230
- <button class="create-link" type="button" onclick={oncreateroom}>
239
+ <Button variant="ghost" size="sm" class="create-link" type="button" onclick={oncreateroom}>
231
240
  {t(M['chat.room_list.create_first_room'])}
232
- </button>
241
+ </Button>
233
242
  {/if}
234
243
  </div>
235
244
  {/if}
@@ -247,27 +256,21 @@ function formatTimestamp(date?: string | Date | null): string {
247
256
  padding: 0.5rem 0.75rem;
248
257
  }
249
258
 
250
- .create-room-btn {
251
- width: 100%;
259
+ /* :global() pierces into the Button child's rendered <button> (see #1589). */
260
+ .room-list__header :global(.create-room-btn) {
252
261
  padding: 0.5rem 0.75rem;
253
262
  border: 1px dashed var(--smrt-color-outline, #74777f);
254
263
  border-radius: var(--smrt-radius-medium, 0.5rem);
255
264
  background: transparent;
256
265
  color: var(--smrt-color-primary, #005ac1);
257
266
  font: var(--smrt-typography-label-large-font, 500 0.875rem / 1.25 sans-serif);
258
- cursor: pointer;
259
267
  transition: background var(--smrt-duration-short2, 150ms);
260
268
  }
261
269
 
262
- .create-room-btn:hover {
270
+ .room-list__header :global(.create-room-btn:hover) {
263
271
  background: var(--smrt-color-primary-container, #d6e3ff);
264
272
  }
265
273
 
266
- .create-room-btn:focus-visible {
267
- outline: 2px solid var(--smrt-color-primary, #005ac1);
268
- outline-offset: -2px;
269
- }
270
-
271
274
  .room-group {
272
275
  margin-top: 0.25rem;
273
276
  }
@@ -452,20 +455,26 @@ function formatTimestamp(date?: string | Date | null): string {
452
455
  font: var(--smrt-typography-body-medium-font, 0.875rem / 1.25 sans-serif);
453
456
  }
454
457
 
455
- .create-link {
456
- border: none;
458
+ /* :global() pierces into the Button child's rendered <button> (see #1589). */
459
+ .room-list__empty :global(.create-link) {
457
460
  background: none;
458
461
  color: var(--smrt-color-primary, #005ac1);
459
462
  font: var(--smrt-typography-label-large-font, 500 0.875rem / 1.25 sans-serif);
460
- cursor: pointer;
461
463
  text-decoration: underline;
462
464
  }
463
465
 
466
+ .room-list__empty :global(.create-link:hover) {
467
+ background: none;
468
+ }
469
+
464
470
  @media (prefers-reduced-motion: reduce) {
465
471
  .room-item,
466
- .create-room-btn,
467
472
  .room-group__chevron {
468
473
  transition: none;
469
474
  }
475
+
476
+ .room-list__header :global(.create-room-btn) {
477
+ transition: none;
478
+ }
470
479
  }
471
480
  </style>
@@ -1 +1 @@
1
- {"version":3,"file":"RoomList.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/layout/RoomList.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,MAAM,WAAW,KAAK;IACpB,2BAA2B;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,iCAAiC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AA6LD,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"RoomList.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/layout/RoomList.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,MAAM,WAAW,KAAK;IACpB,2BAA2B;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,iCAAiC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAoMD,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}