@happyvertical/smrt-messages 0.34.4 → 0.34.6

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 (32) hide show
  1. package/dist/manifest.json +2 -2
  2. package/dist/smrt-knowledge.json +4 -4
  3. package/dist/svelte/components/AccountCard.svelte +22 -13
  4. package/dist/svelte/components/AttachmentChip.svelte +26 -7
  5. package/dist/svelte/components/AttachmentChip.svelte.d.ts +0 -3
  6. package/dist/svelte/components/AttachmentChip.svelte.d.ts.map +1 -1
  7. package/dist/svelte/components/AttachmentUpload.svelte +12 -5
  8. package/dist/svelte/components/AttachmentUpload.svelte.d.ts.map +1 -1
  9. package/dist/svelte/components/ComposeForm.svelte +24 -45
  10. package/dist/svelte/components/ComposeForm.svelte.d.ts.map +1 -1
  11. package/dist/svelte/components/EmailAccountManager.svelte +57 -78
  12. package/dist/svelte/components/EmailAccountManager.svelte.d.ts.map +1 -1
  13. package/dist/svelte/components/EmailFilterManager.svelte +75 -95
  14. package/dist/svelte/components/EmailFilterManager.svelte.d.ts.map +1 -1
  15. package/dist/svelte/components/FolderNav.svelte +22 -15
  16. package/dist/svelte/components/FolderNav.svelte.d.ts.map +1 -1
  17. package/dist/svelte/components/ForwardForm.svelte +13 -29
  18. package/dist/svelte/components/ForwardForm.svelte.d.ts.map +1 -1
  19. package/dist/svelte/components/MessageCard.svelte +13 -6
  20. package/dist/svelte/components/MessageCard.svelte.d.ts.map +1 -1
  21. package/dist/svelte/components/MessageDetail.svelte +19 -12
  22. package/dist/svelte/components/MessageFilters.svelte +18 -14
  23. package/dist/svelte/components/MessageFilters.svelte.d.ts.map +1 -1
  24. package/dist/svelte/components/MessageToolbar.svelte +28 -20
  25. package/dist/svelte/components/MessageToolbar.svelte.d.ts.map +1 -1
  26. package/dist/svelte/components/RecipientInput.svelte +15 -6
  27. package/dist/svelte/components/RecipientInput.svelte.d.ts.map +1 -1
  28. package/dist/svelte/components/ReplyForm.svelte +13 -29
  29. package/dist/svelte/components/ReplyForm.svelte.d.ts.map +1 -1
  30. package/dist/svelte/components/ThreadView.svelte +19 -12
  31. package/dist/svelte/components/ThreadView.svelte.d.ts.map +1 -1
  32. package/package.json +8 -7
@@ -6,6 +6,7 @@
6
6
  * Works with any backend via callback props.
7
7
  */
8
8
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
9
+ import { Button } from '@happyvertical/smrt-ui/ui';
9
10
  import { M } from '../i18n.js';
10
11
  import type { BlacklistEntry, WhitelistEntry } from '../types.js';
11
12
 
@@ -158,33 +159,36 @@ function getPatternPlaceholder(type: string): string {
158
159
 
159
160
  <div class="email-filter-manager">
160
161
  <div class="section-toggle">
161
- <button
162
+ <Button
163
+ variant={activeSection === 'whitelist' ? 'primary' : 'ghost'}
164
+ size="sm"
162
165
  class="section-btn"
163
- class:active={activeSection === 'whitelist'}
164
166
  onclick={() => activeSection = 'whitelist'}
165
167
  >
166
168
  Whitelist
167
169
  <span class="count">{whitelist.length}</span>
168
- </button>
169
- <button
170
+ </Button>
171
+ <Button
172
+ variant={activeSection === 'blacklist' ? 'primary' : 'ghost'}
173
+ size="sm"
170
174
  class="section-btn"
171
- class:active={activeSection === 'blacklist'}
172
175
  onclick={() => activeSection = 'blacklist'}
173
176
  >
174
177
  Blacklist
175
178
  <span class="count">{blacklist.length}</span>
176
- </button>
179
+ </Button>
177
180
  </div>
178
181
 
179
182
  {#if filterError}
180
183
  <div class="filter-error" role="alert" aria-live="assertive">
181
184
  {filterError}
182
- <button
183
- type="button"
185
+ <Button
186
+ variant="ghost"
187
+ size="sm"
184
188
  class="dismiss-btn"
185
189
  aria-label={t(M['messages.email_filter_manager.dismiss_error'])}
186
190
  onclick={() => filterError = null}
187
- >&times;</button>
191
+ >&times;</Button>
188
192
  </div>
189
193
  {/if}
190
194
 
@@ -196,10 +200,12 @@ function getPatternPlaceholder(type: string): string {
196
200
  {t(M['messages.email_filter_manager.whitelist_description'])}
197
201
  </div>
198
202
  {#if !isReadonly && onaddwhitelist}
199
- <button
203
+ <Button
204
+ variant="secondary"
205
+ size="sm"
200
206
  class="add-btn"
201
207
  onclick={() => { resetWhitelistForm(); showWhitelistForm = true; }}
202
- >+ Add</button>
208
+ >+ Add</Button>
203
209
  {/if}
204
210
  </div>
205
211
 
@@ -249,10 +255,10 @@ function getPatternPlaceholder(type: string): string {
249
255
  </div>
250
256
  </div>
251
257
  <div class="form-actions">
252
- <button class="cancel-btn" onclick={resetWhitelistForm} disabled={savingWhitelist}>Cancel</button>
253
- <button class="save-btn" onclick={saveWhitelistEntry} disabled={savingWhitelist || !wlPattern.trim()}>
258
+ <Button variant="secondary" size="sm" class="cancel-btn" onclick={resetWhitelistForm} disabled={savingWhitelist}>Cancel</Button>
259
+ <Button variant="primary" size="sm" class="save-btn" onclick={saveWhitelistEntry} disabled={savingWhitelist || !wlPattern.trim()}>
254
260
  {savingWhitelist ? 'Saving...' : 'Add'}
255
- </button>
261
+ </Button>
256
262
  </div>
257
263
  </div>
258
264
  {/if}
@@ -276,11 +282,13 @@ function getPatternPlaceholder(type: string): string {
276
282
  {/if}
277
283
  </div>
278
284
  {#if !isReadonly && onremovewhitelist}
279
- <button
285
+ <Button
286
+ variant="ghost"
287
+ size="sm"
280
288
  class="delete-btn"
281
289
  onclick={() => removeWhitelistEntry(entry)}
282
290
  title={t(M['messages.email_filter_manager.whitelist_remove'])}
283
- >&times;</button>
291
+ >&times;</Button>
284
292
  {/if}
285
293
  </div>
286
294
  {/each}
@@ -296,10 +304,12 @@ function getPatternPlaceholder(type: string): string {
296
304
  {t(M['messages.email_filter_manager.blacklist_description'])}
297
305
  </div>
298
306
  {#if !isReadonly && onaddblacklist}
299
- <button
307
+ <Button
308
+ variant="secondary"
309
+ size="sm"
300
310
  class="add-btn"
301
311
  onclick={() => { resetBlacklistForm(); showBlacklistForm = true; }}
302
- >+ Add</button>
312
+ >+ Add</Button>
303
313
  {/if}
304
314
  </div>
305
315
 
@@ -345,10 +355,10 @@ function getPatternPlaceholder(type: string): string {
345
355
  </div>
346
356
  </div>
347
357
  <div class="form-actions">
348
- <button class="cancel-btn" onclick={resetBlacklistForm} disabled={savingBlacklist}>Cancel</button>
349
- <button class="save-btn" onclick={saveBlacklistEntry} disabled={savingBlacklist || !blPattern.trim()}>
358
+ <Button variant="secondary" size="sm" class="cancel-btn" onclick={resetBlacklistForm} disabled={savingBlacklist}>Cancel</Button>
359
+ <Button variant="primary" size="sm" class="save-btn" onclick={saveBlacklistEntry} disabled={savingBlacklist || !blPattern.trim()}>
350
360
  {savingBlacklist ? 'Saving...' : 'Add'}
351
- </button>
361
+ </Button>
352
362
  </div>
353
363
  </div>
354
364
  {/if}
@@ -372,11 +382,13 @@ function getPatternPlaceholder(type: string): string {
372
382
  {/if}
373
383
  </div>
374
384
  {#if !isReadonly && onremoveblacklist}
375
- <button
385
+ <Button
386
+ variant="ghost"
387
+ size="sm"
376
388
  class="delete-btn"
377
389
  onclick={() => removeBlacklistEntry(entry)}
378
390
  title={t(M['messages.email_filter_manager.blacklist_remove'])}
379
- >&times;</button>
391
+ >&times;</Button>
380
392
  {/if}
381
393
  </div>
382
394
  {/each}
@@ -401,28 +413,19 @@ function getPatternPlaceholder(type: string): string {
401
413
  border-radius: var(--smrt-radius-md, 8px);
402
414
  }
403
415
 
404
- .section-btn {
416
+ /*
417
+ * The whitelist/blacklist toggle now renders through smrt-ui's <Button>,
418
+ * driven by `variant={active ? 'primary' : 'ghost'}` (a plain class:active view
419
+ * toggle, not an ARIA tablist). The variants own background/color/hover;
420
+ * `.section-toggle :global(.section-btn)` only re-asserts the row layout
421
+ * (flex/gap/radius) inside the pierced Button child scope (issue #1589).
422
+ */
423
+ .section-toggle :global(.section-btn) {
405
424
  display: flex;
406
425
  align-items: center;
407
426
  gap: 0.375rem;
408
- padding: 0.375rem 0.75rem;
409
- border: none;
410
427
  border-radius: var(--smrt-radius-md, 8px);
411
- background: transparent;
412
- color: var(--smrt-color-on-surface-variant, #43474e);
413
428
  font-size: var(--smrt-typography-label-large-size, 0.8125rem);
414
- font-family: inherit;
415
- cursor: pointer;
416
- transition: all 150ms ease;
417
- }
418
-
419
- .section-btn.active {
420
- background: var(--smrt-color-primary-container, #d8e2ff);
421
- color: var(--smrt-color-primary, #005ac1);
422
- }
423
-
424
- .section-btn:hover:not(.active) {
425
- background: var(--smrt-color-surface-container-high, #e6e7ef);
426
429
  }
427
430
 
428
431
  .count {
@@ -450,25 +453,16 @@ function getPatternPlaceholder(type: string): string {
450
453
  color: var(--smrt-color-on-surface-variant, #43474e);
451
454
  }
452
455
 
453
- .add-btn {
454
- padding: 0.375rem 0.75rem;
456
+ /*
457
+ * The add button now renders through smrt-ui's <Button variant="secondary">.
458
+ * The variant owns the outlined-primary look + hover; `.section-header-row
459
+ * :global(.add-btn)` only re-asserts radius and flex-shrink (issue #1589).
460
+ */
461
+ .section-header-row :global(.add-btn) {
455
462
  border-radius: var(--smrt-radius-md, 8px);
456
- border: 1px solid var(--smrt-color-primary, #005ac1);
457
- background: transparent;
458
- color: var(--smrt-color-primary, #005ac1);
459
- cursor: pointer;
460
- font-size: var(--smrt-typography-label-large-size, 0.8125rem);
461
- font-family: inherit;
462
- font-weight: var(--smrt-typography-weight-medium, 500);
463
- transition: all 150ms ease;
464
463
  flex-shrink: 0;
465
464
  }
466
465
 
467
- .add-btn:hover {
468
- background: var(--smrt-color-primary, #005ac1);
469
- color: var(--smrt-color-on-primary, #fff);
470
- }
471
-
472
466
  .entry-form {
473
467
  background: var(--smrt-color-surface-container, #f0f1f9);
474
468
  border: 1px solid var(--smrt-color-primary, #005ac1);
@@ -557,42 +551,20 @@ function getPatternPlaceholder(type: string): string {
557
551
  padding-top: 0.25rem;
558
552
  }
559
553
 
560
- .cancel-btn {
561
- padding: 0.375rem 0.75rem;
562
- border-radius: var(--smrt-radius-md, 8px);
563
- border: 1px solid var(--smrt-color-outline-variant, #c2c7cf);
564
- background: transparent;
565
- color: var(--smrt-color-on-surface-variant, #43474e);
566
- cursor: pointer;
567
- font-size: var(--smrt-typography-label-large-size, 0.8125rem);
568
- font-family: inherit;
569
- transition: all 150ms ease;
570
- }
571
-
572
- .cancel-btn:hover {
573
- background: var(--smrt-color-surface-container-high, #e6e7ef);
574
- }
575
-
576
- .save-btn {
577
- padding: 0.375rem 0.75rem;
554
+ /*
555
+ * Cancel/Save now use Button's secondary/primary variants (dead bespoke CSS
556
+ * removed). `.form-actions :global(...)` only re-asserts the rounded radius;
557
+ * Cancel's neutral outline override keeps it from picking up secondary's
558
+ * primary border color (issue #1589).
559
+ */
560
+ .form-actions :global(.cancel-btn),
561
+ .form-actions :global(.save-btn) {
578
562
  border-radius: var(--smrt-radius-md, 8px);
579
- border: 1px solid var(--smrt-color-primary, #005ac1);
580
- background: var(--smrt-color-primary, #005ac1);
581
- color: var(--smrt-color-on-primary, #fff);
582
- cursor: pointer;
583
- font-size: var(--smrt-typography-label-large-size, 0.8125rem);
584
- font-family: inherit;
585
- font-weight: var(--smrt-typography-weight-medium, 500);
586
- transition: all 150ms ease;
587
- }
588
-
589
- .save-btn:hover:not(:disabled) {
590
- opacity: 0.9;
591
563
  }
592
564
 
593
- .save-btn:disabled {
594
- opacity: 0.5;
595
- cursor: not-allowed;
565
+ .form-actions :global(.cancel-btn) {
566
+ border-color: var(--smrt-color-outline-variant, #c2c7cf);
567
+ color: var(--smrt-color-on-surface-variant, #43474e);
596
568
  }
597
569
 
598
570
  .entries-list {
@@ -684,7 +656,13 @@ function getPatternPlaceholder(type: string): string {
684
656
  flex-shrink: 0;
685
657
  }
686
658
 
687
- .delete-btn {
659
+ /*
660
+ * The remove button now renders through smrt-ui's <Button variant="ghost">.
661
+ * `.entry-card :global(.delete-btn)` anchors on the real `.entry-card` element
662
+ * and pierces the Button child scope to keep the round icon button and its
663
+ * red-on-hover affordance (issue #1589).
664
+ */
665
+ .entry-card :global(.delete-btn) {
688
666
  font-size: var(--smrt-typography-body-large-size, 1rem);
689
667
  line-height: 1;
690
668
  padding: 0.125rem 0.5rem;
@@ -692,13 +670,10 @@ function getPatternPlaceholder(type: string): string {
692
670
  border: 1px solid transparent;
693
671
  background: transparent;
694
672
  color: var(--smrt-color-on-surface-variant, #43474e);
695
- cursor: pointer;
696
- font-family: inherit;
697
- transition: all 150ms ease;
698
673
  flex-shrink: 0;
699
674
  }
700
675
 
701
- .delete-btn:hover {
676
+ .entry-card :global(.delete-btn):hover {
702
677
  background: var(--smrt-color-error-container, #fce4ec);
703
678
  color: var(--smrt-color-error, #ba1a1a);
704
679
  border-color: var(--smrt-color-error, #ba1a1a);
@@ -724,11 +699,16 @@ function getPatternPlaceholder(type: string): string {
724
699
  font-size: var(--smrt-typography-body-medium-size, 0.8125rem);
725
700
  }
726
701
 
727
- .dismiss-btn {
702
+ /*
703
+ * The dismiss button now renders through smrt-ui's <Button variant="ghost">.
704
+ * `.filter-error :global(.dismiss-btn)` anchors on the real `.filter-error`
705
+ * element and pierces the Button child scope. `color: inherit` keeps the icon
706
+ * matching the error banner color (issue #1589).
707
+ */
708
+ .filter-error :global(.dismiss-btn) {
728
709
  background: transparent;
729
710
  border: none;
730
711
  font-size: var(--smrt-typography-body-large-size, 1rem);
731
- cursor: pointer;
732
712
  color: inherit;
733
713
  padding: 0 0.25rem;
734
714
  }
@@ -1 +1 @@
1
- {"version":3,"file":"EmailFilterManager.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/EmailFilterManager.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlE,MAAM,WAAW,KAAK;IACpB,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9D;AAmUD,QAAA,MAAM,kBAAkB,2CAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"EmailFilterManager.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/EmailFilterManager.svelte.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlE,MAAM,WAAW,KAAK;IACpB,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9D;AAoUD,QAAA,MAAM,kBAAkB,2CAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
@@ -3,6 +3,7 @@
3
3
  * FolderNav - Folder/label navigation sidebar
4
4
  */
5
5
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
6
+ import { Button } from '@happyvertical/smrt-ui/ui';
6
7
  import { M } from '../i18n.messages.js';
7
8
  import type { FolderData } from '../types.js';
8
9
 
@@ -50,10 +51,10 @@ function getFolderIcon(specialUse?: string): string {
50
51
  <ul class="folder-list" role="list">
51
52
  {#each _systemFolders as folder (folder.id)}
52
53
  <li>
53
- <button
54
- class="folder-item"
55
- class:folder-item--active={folder.id === activeFolderId}
56
- type="button"
54
+ <Button
55
+ variant="ghost"
56
+ fullWidth
57
+ class={folder.id === activeFolderId ? 'folder-item folder-item--active' : 'folder-item'}
57
58
  onclick={() => onfolderclick?.(folder)}
58
59
  aria-current={folder.id === activeFolderId ? 'true' : undefined}
59
60
  >
@@ -62,7 +63,7 @@ function getFolderIcon(specialUse?: string): string {
62
63
  {#if showCounts && folder.unreadCount > 0}
63
64
  <span class="unread-badge">{folder.unreadCount}</span>
64
65
  {/if}
65
- </button>
66
+ </Button>
66
67
  </li>
67
68
  {/each}
68
69
  </ul>
@@ -75,10 +76,10 @@ function getFolderIcon(specialUse?: string): string {
75
76
  <ul class="folder-list" role="list">
76
77
  {#each _userFolders as folder (folder.id)}
77
78
  <li>
78
- <button
79
- class="folder-item"
80
- class:folder-item--active={folder.id === activeFolderId}
81
- type="button"
79
+ <Button
80
+ variant="ghost"
81
+ fullWidth
82
+ class={folder.id === activeFolderId ? 'folder-item folder-item--active' : 'folder-item'}
82
83
  onclick={() => onfolderclick?.(folder)}
83
84
  aria-current={folder.id === activeFolderId ? 'true' : undefined}
84
85
  >
@@ -87,7 +88,7 @@ function getFolderIcon(specialUse?: string): string {
87
88
  {#if showCounts && folder.unreadCount > 0}
88
89
  <span class="unread-badge">{folder.unreadCount}</span>
89
90
  {/if}
90
- </button>
91
+ </Button>
91
92
  </li>
92
93
  {/each}
93
94
  </ul>
@@ -107,7 +108,14 @@ function getFolderIcon(specialUse?: string): string {
107
108
  padding: 0;
108
109
  }
109
110
 
110
- .folder-item {
111
+ /*
112
+ * Folder rows now render through smrt-ui's <Button variant="ghost" fullWidth>.
113
+ * The <button> is emitted inside the Button child, so `.folder-list :global(.folder-item)`
114
+ * anchors on the real `.folder-list` element and pierces the child scope to keep
115
+ * the nav-row layout, neutral color, hover and active styling (issue #1589). The
116
+ * active modifier is `folder-item--active`, NOT a Button variant class.
117
+ */
118
+ .folder-list :global(.folder-item) {
111
119
  display: flex;
112
120
  align-items: center;
113
121
  gap: 0.5rem;
@@ -115,7 +123,6 @@ function getFolderIcon(specialUse?: string): string {
115
123
  padding: 0.5rem 0.75rem;
116
124
  border: none;
117
125
  background: none;
118
- cursor: pointer;
119
126
  font: var(--smrt-typography-body-medium-font, 0.875rem / 1.25 sans-serif);
120
127
  color: var(--smrt-color-on-surface, #1a1c1e);
121
128
  border-radius: var(--smrt-radius-small, 0.25rem);
@@ -123,16 +130,16 @@ function getFolderIcon(specialUse?: string): string {
123
130
  text-align: left;
124
131
  }
125
132
 
126
- .folder-item:hover {
133
+ .folder-list :global(.folder-item):hover {
127
134
  background: var(--smrt-color-surface-variant, #e1e2ec);
128
135
  }
129
136
 
130
- .folder-item--active {
137
+ .folder-list :global(.folder-item--active) {
131
138
  background: var(--smrt-color-secondary-container, #d7e3f7);
132
139
  font-weight: var(--smrt-typography-weight-medium, 500);
133
140
  }
134
141
 
135
- .folder-item:focus-visible {
142
+ .folder-list :global(.folder-item):focus-visible {
136
143
  outline: 2px solid var(--smrt-color-primary, #005ac1);
137
144
  outline-offset: -2px;
138
145
  }
@@ -1 +1 @@
1
- {"version":3,"file":"FolderNav.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/FolderNav.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;CAC9C;AAiFD,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"FolderNav.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/FolderNav.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;CAC9C;AAkFD,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -10,6 +10,7 @@ export interface Props {
10
10
 
11
11
  <script lang="ts">
12
12
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
13
+ import { Button } from '@happyvertical/smrt-ui/ui';
13
14
  import { M } from '../i18n.js';
14
15
  import RecipientInput from './RecipientInput.svelte';
15
16
 
@@ -85,17 +86,16 @@ export interface Props {
85
86
  {/if}
86
87
 
87
88
  <div class="actions">
88
- <button
89
- type="button"
90
- class="btn-primary"
89
+ <Button
90
+ variant="primary"
91
91
  disabled={isSending || to.length === 0}
92
92
  onclick={handleSend}
93
93
  >
94
94
  {isSending ? 'Sending...' : 'Forward'}
95
- </button>
96
- <button type="button" class="btn-text" onclick={() => oncancel?.()}>
95
+ </Button>
96
+ <Button variant="ghost" class="btn-text" onclick={() => oncancel?.()}>
97
97
  Cancel
98
- </button>
98
+ </Button>
99
99
  </div>
100
100
  </div>
101
101
 
@@ -162,29 +162,13 @@ export interface Props {
162
162
  gap: var(--smrt-spacing-2, 8px);
163
163
  }
164
164
 
165
- .btn-primary {
166
- padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-6, 24px);
167
- border-radius: var(--smrt-radius-full, 20px);
168
- border: none;
169
- background: var(--smrt-color-primary, #6750a4);
170
- color: var(--smrt-color-on-primary, #fff);
171
- font-family: var(--smrt-font-family, system-ui);
172
- font-size: var(--smrt-typography-label-large-size, 14px);
173
- cursor: pointer;
174
- }
175
-
176
- .btn-primary:disabled {
177
- opacity: 0.6;
178
- cursor: not-allowed;
179
- }
180
-
181
- .btn-text {
182
- padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-4, 16px);
183
- border: none;
184
- background: transparent;
165
+ /*
166
+ * Forward now uses Button's primary variant directly (dead .btn-primary CSS
167
+ * removed). The Cancel button keeps its neutral on-surface-variant text via
168
+ * `.actions :global(.btn-text)` (Button's ghost uses the primary color) —
169
+ * issue #1589.
170
+ */
171
+ .actions :global(.btn-text) {
185
172
  color: var(--smrt-color-on-surface-variant, #49454f);
186
- font-family: var(--smrt-font-family, system-ui);
187
- font-size: var(--smrt-typography-label-large-size, 14px);
188
- cursor: pointer;
189
173
  }
190
174
  </style>
@@ -1 +1 @@
1
- {"version":3,"file":"ForwardForm.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ForwardForm.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,WAAW,KAAK;IACpB,eAAe,EAAE,WAAW,CAAC;IAC7B,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AA0FD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"ForwardForm.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ForwardForm.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,WAAW,KAAK;IACpB,eAAe,EAAE,WAAW,CAAC;IAC7B,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AA4FD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
7
+ import { Button } from '@happyvertical/smrt-ui/ui';
7
8
  import type { Snippet } from 'svelte';
8
9
  import { M } from '../i18n.messages.js';
9
10
  import type { AccountData, MessageData } from '../types.js';
@@ -106,6 +107,7 @@ const _slackMeta = $derived.by(() => {
106
107
  </div>
107
108
  {/if}
108
109
 
110
+ <!-- raw-primitive-allow: large pressable message row wrapping rich header/subject/preview content (a structural selection surface with descendant compact-mode layout rules), not a standard action button -->
109
111
  <button
110
112
  class="message-content"
111
113
  type="button"
@@ -162,15 +164,15 @@ const _slackMeta = $derived.by(() => {
162
164
  </button>
163
165
 
164
166
  {#if onflag}
165
- <button
167
+ <Button
168
+ variant="ghost"
166
169
  class="flag-btn"
167
- type="button"
168
170
  onclick={() => onflag?.(message)}
169
171
  title={message.isFlagged ? 'Unflag' : 'Flag'}
170
172
  aria-label={message.isFlagged ? 'Unflag' : 'Flag'}
171
173
  >
172
174
  {message.isFlagged ? '⚑' : '⚐'}
173
- </button>
175
+ </Button>
174
176
  {/if}
175
177
  </div>
176
178
 
@@ -308,21 +310,26 @@ const _slackMeta = $derived.by(() => {
308
310
  color: var(--smrt-color-on-surface-variant, #43474e);
309
311
  }
310
312
 
311
- .flag-btn {
313
+ /*
314
+ * The flag toggle now renders through smrt-ui's <Button variant="ghost">.
315
+ * `.message-card :global(.flag-btn)` anchors on the real `.message-card`
316
+ * element and pierces the Button child scope to keep the dimmed icon-toggle
317
+ * styling (issue #1589).
318
+ */
319
+ .message-card :global(.flag-btn) {
312
320
  display: flex;
313
321
  align-items: center;
314
322
  justify-content: center;
315
323
  width: 2rem;
316
324
  border: none;
317
325
  background: none;
318
- cursor: pointer;
319
326
  font-size: var(--smrt-typography-body-large-size, 1rem);
320
327
  color: var(--smrt-color-on-surface-variant, #43474e);
321
328
  opacity: 0.5;
322
329
  transition: opacity var(--smrt-duration-short2, 150ms);
323
330
  }
324
331
 
325
- .flag-btn:hover {
332
+ .message-card :global(.flag-btn):hover {
326
333
  opacity: 1;
327
334
  }
328
335
 
@@ -1 +1 @@
1
- {"version":3,"file":"MessageCard.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/MessageCard.svelte.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK5D,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAC1C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC,CAAC;CACnD;AA6ID,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"MessageCard.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/MessageCard.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK5D,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAC1C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC,CAAC;CACnD;AA+ID,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -3,7 +3,7 @@
3
3
  * MessageDetail - Full message view with type-adaptive sections
4
4
  */
5
5
 
6
- import { Card } from '@happyvertical/smrt-ui/ui';
6
+ import { Button, Card } from '@happyvertical/smrt-ui/ui';
7
7
  import type { Snippet } from 'svelte';
8
8
  import type { AccountData, AttachmentData, MessageData } from '../types.js';
9
9
  import AttachmentChip from './AttachmentChip.svelte';
@@ -162,19 +162,19 @@ const _displayBody = $derived.by(() => {
162
162
  {#if onreply || onforward || ondelete}
163
163
  <div class="actions">
164
164
  {#if onreply}
165
- <button class="action-btn" type="button" onclick={() => onreply?.(message)}>
165
+ <Button variant="ghost" class="action-btn" onclick={() => onreply?.(message)}>
166
166
  ↩ Reply
167
- </button>
167
+ </Button>
168
168
  {/if}
169
169
  {#if onforward}
170
- <button class="action-btn" type="button" onclick={() => onforward?.(message)}>
170
+ <Button variant="ghost" class="action-btn" onclick={() => onforward?.(message)}>
171
171
  ↪ Forward
172
- </button>
172
+ </Button>
173
173
  {/if}
174
174
  {#if ondelete}
175
- <button class="action-btn action-btn--danger" type="button" onclick={() => ondelete?.(message)}>
175
+ <Button variant="ghost" class="action-btn action-btn--danger" onclick={() => ondelete?.(message)}>
176
176
  🗑 Delete
177
- </button>
177
+ </Button>
178
178
  {/if}
179
179
  </div>
180
180
  {/if}
@@ -280,27 +280,34 @@ const _displayBody = $derived.by(() => {
280
280
  border-top: 1px solid var(--smrt-color-outline-variant, #c4c6d0);
281
281
  }
282
282
 
283
- .action-btn {
283
+ /*
284
+ * Action buttons now render through smrt-ui's <Button variant="ghost">. The
285
+ * <button> is emitted inside the Button child, so `.actions :global(.action-btn)`
286
+ * anchors on the real `.actions` element and pierces the child scope to keep
287
+ * the original outlined-surface styling (issue #1589). The destructive button's
288
+ * modifier is `action-btn--danger`, NOT `danger`, to avoid colliding with
289
+ * Button's own `.danger` variant class.
290
+ */
291
+ .actions :global(.action-btn) {
284
292
  padding: 0.5rem 1rem;
285
293
  border: 1px solid var(--smrt-color-outline, #72787e);
286
294
  border-radius: var(--smrt-radius-small, 0.25rem);
287
295
  background: var(--smrt-color-surface, #fefbff);
288
296
  color: var(--smrt-color-on-surface, #1a1c1e);
289
297
  font: var(--smrt-typography-label-large-font, 500 0.875rem / 1.25 sans-serif);
290
- cursor: pointer;
291
298
  transition: background var(--smrt-duration-short2, 150ms);
292
299
  }
293
300
 
294
- .action-btn:hover {
301
+ .actions :global(.action-btn):hover {
295
302
  background: var(--smrt-color-surface-variant, #e1e2ec);
296
303
  }
297
304
 
298
- .action-btn--danger {
305
+ .actions :global(.action-btn--danger) {
299
306
  color: var(--smrt-color-error, #ba1a1a);
300
307
  border-color: var(--smrt-color-error, #ba1a1a);
301
308
  }
302
309
 
303
- .action-btn--danger:hover {
310
+ .actions :global(.action-btn--danger):hover {
304
311
  background: var(--smrt-color-error-container, #ffdad6);
305
312
  }
306
313
  </style>