@happyvertical/smrt-messages 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 (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 +19 -7
  8. package/dist/svelte/components/AttachmentUpload.svelte.d.ts.map +1 -1
  9. package/dist/svelte/components/ComposeForm.svelte +66 -75
  10. package/dist/svelte/components/ComposeForm.svelte.d.ts.map +1 -1
  11. package/dist/svelte/components/EmailAccountManager.svelte +81 -115
  12. package/dist/svelte/components/EmailAccountManager.svelte.d.ts.map +1 -1
  13. package/dist/svelte/components/EmailFilterManager.svelte +94 -127
  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 +16 -48
  18. package/dist/svelte/components/ForwardForm.svelte.d.ts.map +1 -1
  19. package/dist/svelte/components/MessageCard.svelte +14 -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 +47 -43
  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 +32 -9
  27. package/dist/svelte/components/RecipientInput.svelte.d.ts.map +1 -1
  28. package/dist/svelte/components/ReplyForm.svelte +16 -48
  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
@@ -5,7 +5,9 @@
5
5
  * Reusable component for managing email allow/block lists.
6
6
  * Works with any backend via callback props.
7
7
  */
8
+ import { Input, Select } from '@happyvertical/smrt-ui/forms';
8
9
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
10
+ import { Button } from '@happyvertical/smrt-ui/ui';
9
11
  import { M } from '../i18n.js';
10
12
  import type { BlacklistEntry, WhitelistEntry } from '../types.js';
11
13
 
@@ -158,33 +160,36 @@ function getPatternPlaceholder(type: string): string {
158
160
 
159
161
  <div class="email-filter-manager">
160
162
  <div class="section-toggle">
161
- <button
163
+ <Button
164
+ variant={activeSection === 'whitelist' ? 'primary' : 'ghost'}
165
+ size="sm"
162
166
  class="section-btn"
163
- class:active={activeSection === 'whitelist'}
164
167
  onclick={() => activeSection = 'whitelist'}
165
168
  >
166
169
  Whitelist
167
170
  <span class="count">{whitelist.length}</span>
168
- </button>
169
- <button
171
+ </Button>
172
+ <Button
173
+ variant={activeSection === 'blacklist' ? 'primary' : 'ghost'}
174
+ size="sm"
170
175
  class="section-btn"
171
- class:active={activeSection === 'blacklist'}
172
176
  onclick={() => activeSection = 'blacklist'}
173
177
  >
174
178
  Blacklist
175
179
  <span class="count">{blacklist.length}</span>
176
- </button>
180
+ </Button>
177
181
  </div>
178
182
 
179
183
  {#if filterError}
180
184
  <div class="filter-error" role="alert" aria-live="assertive">
181
185
  {filterError}
182
- <button
183
- type="button"
186
+ <Button
187
+ variant="ghost"
188
+ size="sm"
184
189
  class="dismiss-btn"
185
190
  aria-label={t(M['messages.email_filter_manager.dismiss_error'])}
186
191
  onclick={() => filterError = null}
187
- >&times;</button>
192
+ >&times;</Button>
188
193
  </div>
189
194
  {/if}
190
195
 
@@ -196,10 +201,12 @@ function getPatternPlaceholder(type: string): string {
196
201
  {t(M['messages.email_filter_manager.whitelist_description'])}
197
202
  </div>
198
203
  {#if !isReadonly && onaddwhitelist}
199
- <button
204
+ <Button
205
+ variant="secondary"
206
+ size="sm"
200
207
  class="add-btn"
201
208
  onclick={() => { resetWhitelistForm(); showWhitelistForm = true; }}
202
- >+ Add</button>
209
+ >+ Add</Button>
203
210
  {/if}
204
211
  </div>
205
212
 
@@ -209,15 +216,15 @@ function getPatternPlaceholder(type: string): string {
209
216
  <div class="form-row">
210
217
  <div class="form-field" style="flex: 0 0 120px;">
211
218
  <label class="form-label" for="wl-type">Type</label>
212
- <select id="wl-type" class="form-select" bind:value={wlType}>
219
+ <Select id="wl-type" class="form-select" bind:value={wlType}>
213
220
  <option value="email">Email</option>
214
221
  <option value="domain">Domain</option>
215
222
  <option value="regex">Regex</option>
216
- </select>
223
+ </Select>
217
224
  </div>
218
225
  <div class="form-field" style="flex: 1;">
219
226
  <label class="form-label" for="wl-pattern">Pattern</label>
220
- <input
227
+ <Input
221
228
  id="wl-pattern"
222
229
  class="form-input"
223
230
  type="text"
@@ -229,7 +236,7 @@ function getPatternPlaceholder(type: string): string {
229
236
  <div class="form-row">
230
237
  <div class="form-field" style="flex: 1;">
231
238
  <label class="form-label" for="wl-category">Category <span class="optional">(optional)</span></label>
232
- <input
239
+ <Input
233
240
  id="wl-category"
234
241
  class="form-input"
235
242
  type="text"
@@ -239,7 +246,7 @@ function getPatternPlaceholder(type: string): string {
239
246
  </div>
240
247
  <div class="form-field" style="flex: 2;">
241
248
  <label class="form-label" for="wl-desc">Description</label>
242
- <input
249
+ <Input
243
250
  id="wl-desc"
244
251
  class="form-input"
245
252
  type="text"
@@ -249,10 +256,10 @@ function getPatternPlaceholder(type: string): string {
249
256
  </div>
250
257
  </div>
251
258
  <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()}>
259
+ <Button variant="secondary" size="sm" class="cancel-btn" onclick={resetWhitelistForm} disabled={savingWhitelist}>Cancel</Button>
260
+ <Button variant="primary" size="sm" class="save-btn" onclick={saveWhitelistEntry} disabled={savingWhitelist || !wlPattern.trim()}>
254
261
  {savingWhitelist ? 'Saving...' : 'Add'}
255
- </button>
262
+ </Button>
256
263
  </div>
257
264
  </div>
258
265
  {/if}
@@ -276,11 +283,13 @@ function getPatternPlaceholder(type: string): string {
276
283
  {/if}
277
284
  </div>
278
285
  {#if !isReadonly && onremovewhitelist}
279
- <button
286
+ <Button
287
+ variant="ghost"
288
+ size="sm"
280
289
  class="delete-btn"
281
290
  onclick={() => removeWhitelistEntry(entry)}
282
291
  title={t(M['messages.email_filter_manager.whitelist_remove'])}
283
- >&times;</button>
292
+ >&times;</Button>
284
293
  {/if}
285
294
  </div>
286
295
  {/each}
@@ -296,10 +305,12 @@ function getPatternPlaceholder(type: string): string {
296
305
  {t(M['messages.email_filter_manager.blacklist_description'])}
297
306
  </div>
298
307
  {#if !isReadonly && onaddblacklist}
299
- <button
308
+ <Button
309
+ variant="secondary"
310
+ size="sm"
300
311
  class="add-btn"
301
312
  onclick={() => { resetBlacklistForm(); showBlacklistForm = true; }}
302
- >+ Add</button>
313
+ >+ Add</Button>
303
314
  {/if}
304
315
  </div>
305
316
 
@@ -309,15 +320,15 @@ function getPatternPlaceholder(type: string): string {
309
320
  <div class="form-row">
310
321
  <div class="form-field" style="flex: 0 0 120px;">
311
322
  <label class="form-label" for="bl-type">Type</label>
312
- <select id="bl-type" class="form-select" bind:value={blType}>
323
+ <Select id="bl-type" class="form-select" bind:value={blType}>
313
324
  <option value="email">Email</option>
314
325
  <option value="domain">Domain</option>
315
326
  <option value="regex">Regex</option>
316
- </select>
327
+ </Select>
317
328
  </div>
318
329
  <div class="form-field" style="flex: 1;">
319
330
  <label class="form-label" for="bl-pattern">Pattern</label>
320
- <input
331
+ <Input
321
332
  id="bl-pattern"
322
333
  class="form-input"
323
334
  type="text"
@@ -329,7 +340,7 @@ function getPatternPlaceholder(type: string): string {
329
340
  <div class="form-row">
330
341
  <div class="form-field" style="flex: 1;">
331
342
  <label class="form-label" for="bl-reason">Reason</label>
332
- <input
343
+ <Input
333
344
  id="bl-reason"
334
345
  class="form-input"
335
346
  type="text"
@@ -339,16 +350,17 @@ function getPatternPlaceholder(type: string): string {
339
350
  </div>
340
351
  <div class="form-field checkbox-field">
341
352
  <label class="form-label checkbox-label">
353
+ <!-- raw-primitive-allow: native checkbox; no Provider-free checkbox primitive (Toggle is a switch with different semantics, CheckboxInput requires a Provider) -->
342
354
  <input type="checkbox" bind:checked={blAutoArchive} />
343
355
  Auto-archive
344
356
  </label>
345
357
  </div>
346
358
  </div>
347
359
  <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()}>
360
+ <Button variant="secondary" size="sm" class="cancel-btn" onclick={resetBlacklistForm} disabled={savingBlacklist}>Cancel</Button>
361
+ <Button variant="primary" size="sm" class="save-btn" onclick={saveBlacklistEntry} disabled={savingBlacklist || !blPattern.trim()}>
350
362
  {savingBlacklist ? 'Saving...' : 'Add'}
351
- </button>
363
+ </Button>
352
364
  </div>
353
365
  </div>
354
366
  {/if}
@@ -372,11 +384,13 @@ function getPatternPlaceholder(type: string): string {
372
384
  {/if}
373
385
  </div>
374
386
  {#if !isReadonly && onremoveblacklist}
375
- <button
387
+ <Button
388
+ variant="ghost"
389
+ size="sm"
376
390
  class="delete-btn"
377
391
  onclick={() => removeBlacklistEntry(entry)}
378
392
  title={t(M['messages.email_filter_manager.blacklist_remove'])}
379
- >&times;</button>
393
+ >&times;</Button>
380
394
  {/if}
381
395
  </div>
382
396
  {/each}
@@ -401,28 +415,19 @@ function getPatternPlaceholder(type: string): string {
401
415
  border-radius: var(--smrt-radius-md, 8px);
402
416
  }
403
417
 
404
- .section-btn {
418
+ /*
419
+ * The whitelist/blacklist toggle now renders through smrt-ui's <Button>,
420
+ * driven by `variant={active ? 'primary' : 'ghost'}` (a plain class:active view
421
+ * toggle, not an ARIA tablist). The variants own background/color/hover;
422
+ * `.section-toggle :global(.section-btn)` only re-asserts the row layout
423
+ * (flex/gap/radius) inside the pierced Button child scope (issue #1589).
424
+ */
425
+ .section-toggle :global(.section-btn) {
405
426
  display: flex;
406
427
  align-items: center;
407
428
  gap: 0.375rem;
408
- padding: 0.375rem 0.75rem;
409
- border: none;
410
429
  border-radius: var(--smrt-radius-md, 8px);
411
- background: transparent;
412
- color: var(--smrt-color-on-surface-variant, #43474e);
413
430
  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
431
  }
427
432
 
428
433
  .count {
@@ -450,25 +455,16 @@ function getPatternPlaceholder(type: string): string {
450
455
  color: var(--smrt-color-on-surface-variant, #43474e);
451
456
  }
452
457
 
453
- .add-btn {
454
- padding: 0.375rem 0.75rem;
458
+ /*
459
+ * The add button now renders through smrt-ui's <Button variant="secondary">.
460
+ * The variant owns the outlined-primary look + hover; `.section-header-row
461
+ * :global(.add-btn)` only re-asserts radius and flex-shrink (issue #1589).
462
+ */
463
+ .section-header-row :global(.add-btn) {
455
464
  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
465
  flex-shrink: 0;
465
466
  }
466
467
 
467
- .add-btn:hover {
468
- background: var(--smrt-color-primary, #005ac1);
469
- color: var(--smrt-color-on-primary, #fff);
470
- }
471
-
472
468
  .entry-form {
473
469
  background: var(--smrt-color-surface-container, #f0f1f9);
474
470
  border: 1px solid var(--smrt-color-primary, #005ac1);
@@ -508,29 +504,14 @@ function getPatternPlaceholder(type: string): string {
508
504
  opacity: 0.7;
509
505
  }
510
506
 
511
- .form-input,
512
- .form-select {
513
- padding: 0.5rem 0.625rem;
514
- border-radius: var(--smrt-radius-md, 8px);
515
- border: 1px solid var(--smrt-color-outline-variant, #c2c7cf);
516
- background: var(--smrt-color-surface, #fefbff);
517
- color: var(--smrt-color-on-surface, #1a1c1e);
518
- font-size: var(--smrt-typography-body-medium-size, 0.8125rem);
519
- font-family: inherit;
520
- transition: border-color 150ms ease;
521
- }
522
-
523
- .form-input:focus,
524
- .form-select:focus {
525
- outline: none;
526
- border-color: var(--smrt-color-primary, #005ac1);
527
- }
528
-
529
- .form-input::placeholder {
530
- color: var(--smrt-color-on-surface-variant, #43474e);
531
- opacity: 0.5;
532
- }
533
-
507
+ /*
508
+ * The form fields now render through smrt-ui's <Input> / <Select>, which bring
509
+ * their own tokenised border / background / focus / placeholder styling and
510
+ * honor prefers-reduced-motion themselves. The old `.form-input` / `.form-select`
511
+ * rules (and their :focus / ::placeholder overrides) are dropped as dead — they
512
+ * targeted the raw elements that now live inside the primitive child scopes
513
+ * (issue #1589).
514
+ */
534
515
  .checkbox-field {
535
516
  justify-content: flex-end;
536
517
  padding-bottom: 0.5rem;
@@ -557,42 +538,20 @@ function getPatternPlaceholder(type: string): string {
557
538
  padding-top: 0.25rem;
558
539
  }
559
540
 
560
- .cancel-btn {
561
- padding: 0.375rem 0.75rem;
541
+ /*
542
+ * Cancel/Save now use Button's secondary/primary variants (dead bespoke CSS
543
+ * removed). `.form-actions :global(...)` only re-asserts the rounded radius;
544
+ * Cancel's neutral outline override keeps it from picking up secondary's
545
+ * primary border color (issue #1589).
546
+ */
547
+ .form-actions :global(.cancel-btn),
548
+ .form-actions :global(.save-btn) {
562
549
  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
550
  }
575
551
 
576
- .save-btn {
577
- padding: 0.375rem 0.75rem;
578
- 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
- }
592
-
593
- .save-btn:disabled {
594
- opacity: 0.5;
595
- cursor: not-allowed;
552
+ .form-actions :global(.cancel-btn) {
553
+ border-color: var(--smrt-color-outline-variant, #c2c7cf);
554
+ color: var(--smrt-color-on-surface-variant, #43474e);
596
555
  }
597
556
 
598
557
  .entries-list {
@@ -684,7 +643,13 @@ function getPatternPlaceholder(type: string): string {
684
643
  flex-shrink: 0;
685
644
  }
686
645
 
687
- .delete-btn {
646
+ /*
647
+ * The remove button now renders through smrt-ui's <Button variant="ghost">.
648
+ * `.entry-card :global(.delete-btn)` anchors on the real `.entry-card` element
649
+ * and pierces the Button child scope to keep the round icon button and its
650
+ * red-on-hover affordance (issue #1589).
651
+ */
652
+ .entry-card :global(.delete-btn) {
688
653
  font-size: var(--smrt-typography-body-large-size, 1rem);
689
654
  line-height: 1;
690
655
  padding: 0.125rem 0.5rem;
@@ -692,13 +657,10 @@ function getPatternPlaceholder(type: string): string {
692
657
  border: 1px solid transparent;
693
658
  background: transparent;
694
659
  color: var(--smrt-color-on-surface-variant, #43474e);
695
- cursor: pointer;
696
- font-family: inherit;
697
- transition: all 150ms ease;
698
660
  flex-shrink: 0;
699
661
  }
700
662
 
701
- .delete-btn:hover {
663
+ .entry-card :global(.delete-btn):hover {
702
664
  background: var(--smrt-color-error-container, #fce4ec);
703
665
  color: var(--smrt-color-error, #ba1a1a);
704
666
  border-color: var(--smrt-color-error, #ba1a1a);
@@ -724,11 +686,16 @@ function getPatternPlaceholder(type: string): string {
724
686
  font-size: var(--smrt-typography-body-medium-size, 0.8125rem);
725
687
  }
726
688
 
727
- .dismiss-btn {
689
+ /*
690
+ * The dismiss button now renders through smrt-ui's <Button variant="ghost">.
691
+ * `.filter-error :global(.dismiss-btn)` anchors on the real `.filter-error`
692
+ * element and pierces the Button child scope. `color: inherit` keeps the icon
693
+ * matching the error banner color (issue #1589).
694
+ */
695
+ .filter-error :global(.dismiss-btn) {
728
696
  background: transparent;
729
697
  border: none;
730
698
  font-size: var(--smrt-typography-body-large-size, 1rem);
731
- cursor: pointer;
732
699
  color: inherit;
733
700
  padding: 0 0.25rem;
734
701
  }
@@ -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":"AAaA,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;AAsUD,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,8 @@ export interface Props {
10
10
 
11
11
  <script lang="ts">
12
12
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
13
+ import { Textarea } from '@happyvertical/smrt-ui/forms';
14
+ import { Button } from '@happyvertical/smrt-ui/ui';
13
15
  import { M } from '../i18n.js';
14
16
  import RecipientInput from './RecipientInput.svelte';
15
17
 
@@ -67,12 +69,11 @@ export interface Props {
67
69
  onchange={(r) => { to = r; }}
68
70
  />
69
71
 
70
- <textarea
71
- class="forward-body"
72
+ <Textarea
72
73
  bind:value={body}
73
74
  placeholder={t(M['messages.forward_form.note_placeholder'])}
74
75
  rows={3}
75
- ></textarea>
76
+ />
76
77
 
77
78
  <div class="forwarded-original">
78
79
  <pre class="forwarded-text">{forwardedBlock}</pre>
@@ -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
 
@@ -117,22 +117,6 @@ export interface Props {
117
117
  font-weight: var(--smrt-typography-weight-medium, 500);
118
118
  }
119
119
 
120
- .forward-body {
121
- width: 100%;
122
- border: 1px solid var(--smrt-color-outline-variant, #cac4d0);
123
- border-radius: var(--smrt-radius-sm, 8px);
124
- padding: var(--smrt-spacing-2, 8px);
125
- font-family: var(--smrt-font-family, system-ui);
126
- font-size: var(--smrt-typography-body-medium-size, 14px);
127
- resize: vertical;
128
- box-sizing: border-box;
129
- }
130
-
131
- .forward-body:focus {
132
- outline: 2px solid var(--smrt-color-primary, #6750a4);
133
- outline-offset: -1px;
134
- }
135
-
136
120
  .forwarded-original {
137
121
  padding: var(--smrt-spacing-2, 8px);
138
122
  background: var(--smrt-color-surface-variant, #e7e0ec);
@@ -162,29 +146,13 @@ export interface Props {
162
146
  gap: var(--smrt-spacing-2, 8px);
163
147
  }
164
148
 
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;
149
+ /*
150
+ * Forward now uses Button's primary variant directly (dead .btn-primary CSS
151
+ * removed). The Cancel button keeps its neutral on-surface-variant text via
152
+ * `.actions :global(.btn-text)` (Button's ghost uses the primary color) —
153
+ * issue #1589.
154
+ */
155
+ .actions :global(.btn-text) {
185
156
  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
157
  }
190
158
  </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;AA8FD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}