@happyvertical/smrt-messages 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/AGENTS.md +31 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/README.md +103 -0
  5. package/dist/__smrt-register__.d.ts +2 -0
  6. package/dist/__smrt-register__.d.ts.map +1 -0
  7. package/dist/collections/AccountCollection.d.ts +42 -0
  8. package/dist/collections/AccountCollection.d.ts.map +1 -0
  9. package/dist/collections/AttachmentCollection.d.ts +67 -0
  10. package/dist/collections/AttachmentCollection.d.ts.map +1 -0
  11. package/dist/collections/BlacklistCollection.d.ts +14 -0
  12. package/dist/collections/BlacklistCollection.d.ts.map +1 -0
  13. package/dist/collections/EmailAccountCollection.d.ts +74 -0
  14. package/dist/collections/EmailAccountCollection.d.ts.map +1 -0
  15. package/dist/collections/EmailAttachmentCollection.d.ts +38 -0
  16. package/dist/collections/EmailAttachmentCollection.d.ts.map +1 -0
  17. package/dist/collections/EmailCollection.d.ts +81 -0
  18. package/dist/collections/EmailCollection.d.ts.map +1 -0
  19. package/dist/collections/EmailFolderCollection.d.ts +85 -0
  20. package/dist/collections/EmailFolderCollection.d.ts.map +1 -0
  21. package/dist/collections/MessageCollection.d.ts +74 -0
  22. package/dist/collections/MessageCollection.d.ts.map +1 -0
  23. package/dist/collections/WhitelistCollection.d.ts +18 -0
  24. package/dist/collections/WhitelistCollection.d.ts.map +1 -0
  25. package/dist/index.d.ts +27 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +3068 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/manifest.json +10576 -0
  30. package/dist/models/Account.d.ts +47 -0
  31. package/dist/models/Account.d.ts.map +1 -0
  32. package/dist/models/Attachment.d.ts +48 -0
  33. package/dist/models/Attachment.d.ts.map +1 -0
  34. package/dist/models/Blacklist.d.ts +21 -0
  35. package/dist/models/Blacklist.d.ts.map +1 -0
  36. package/dist/models/Email.d.ts +98 -0
  37. package/dist/models/Email.d.ts.map +1 -0
  38. package/dist/models/EmailAccount.d.ts +65 -0
  39. package/dist/models/EmailAccount.d.ts.map +1 -0
  40. package/dist/models/EmailAttachment.d.ts +19 -0
  41. package/dist/models/EmailAttachment.d.ts.map +1 -0
  42. package/dist/models/EmailFolder.d.ts +65 -0
  43. package/dist/models/EmailFolder.d.ts.map +1 -0
  44. package/dist/models/Message.d.ts +105 -0
  45. package/dist/models/Message.d.ts.map +1 -0
  46. package/dist/models/SlackAccount.d.ts +13 -0
  47. package/dist/models/SlackAccount.d.ts.map +1 -0
  48. package/dist/models/SlackMessage.d.ts +34 -0
  49. package/dist/models/SlackMessage.d.ts.map +1 -0
  50. package/dist/models/Tweet.d.ts +31 -0
  51. package/dist/models/Tweet.d.ts.map +1 -0
  52. package/dist/models/TwitterAccount.d.ts +12 -0
  53. package/dist/models/TwitterAccount.d.ts.map +1 -0
  54. package/dist/models/Whitelist.d.ts +21 -0
  55. package/dist/models/Whitelist.d.ts.map +1 -0
  56. package/dist/playground.d.ts +2 -0
  57. package/dist/playground.d.ts.map +1 -0
  58. package/dist/playground.js +176 -0
  59. package/dist/playground.js.map +1 -0
  60. package/dist/senders/EmailSender.d.ts +13 -0
  61. package/dist/senders/EmailSender.d.ts.map +1 -0
  62. package/dist/senders/SlackSender.d.ts +11 -0
  63. package/dist/senders/SlackSender.d.ts.map +1 -0
  64. package/dist/senders/TweetSender.d.ts +11 -0
  65. package/dist/senders/TweetSender.d.ts.map +1 -0
  66. package/dist/smrt-knowledge.json +4234 -0
  67. package/dist/svelte/components/AccountAvatar.svelte +107 -0
  68. package/dist/svelte/components/AccountAvatar.svelte.d.ts +12 -0
  69. package/dist/svelte/components/AccountAvatar.svelte.d.ts.map +1 -0
  70. package/dist/svelte/components/AccountCard.svelte +173 -0
  71. package/dist/svelte/components/AccountCard.svelte.d.ts +12 -0
  72. package/dist/svelte/components/AccountCard.svelte.d.ts.map +1 -0
  73. package/dist/svelte/components/AccountList.svelte +90 -0
  74. package/dist/svelte/components/AccountList.svelte.d.ts +12 -0
  75. package/dist/svelte/components/AccountList.svelte.d.ts.map +1 -0
  76. package/dist/svelte/components/AttachmentChip.svelte +99 -0
  77. package/dist/svelte/components/AttachmentChip.svelte.d.ts +12 -0
  78. package/dist/svelte/components/AttachmentChip.svelte.d.ts.map +1 -0
  79. package/dist/svelte/components/AttachmentUpload.svelte +160 -0
  80. package/dist/svelte/components/AttachmentUpload.svelte.d.ts +11 -0
  81. package/dist/svelte/components/AttachmentUpload.svelte.d.ts.map +1 -0
  82. package/dist/svelte/components/ComposeForm.svelte +387 -0
  83. package/dist/svelte/components/ComposeForm.svelte.d.ts +13 -0
  84. package/dist/svelte/components/ComposeForm.svelte.d.ts.map +1 -0
  85. package/dist/svelte/components/EmailAccountManager.svelte +690 -0
  86. package/dist/svelte/components/EmailAccountManager.svelte.d.ts +15 -0
  87. package/dist/svelte/components/EmailAccountManager.svelte.d.ts.map +1 -0
  88. package/dist/svelte/components/EmailFilterManager.svelte +687 -0
  89. package/dist/svelte/components/EmailFilterManager.svelte.d.ts +14 -0
  90. package/dist/svelte/components/EmailFilterManager.svelte.d.ts.map +1 -0
  91. package/dist/svelte/components/FolderNav.svelte +171 -0
  92. package/dist/svelte/components/FolderNav.svelte.d.ts +11 -0
  93. package/dist/svelte/components/FolderNav.svelte.d.ts.map +1 -0
  94. package/dist/svelte/components/ForwardForm.svelte +166 -0
  95. package/dist/svelte/components/ForwardForm.svelte.d.ts +10 -0
  96. package/dist/svelte/components/ForwardForm.svelte.d.ts.map +1 -0
  97. package/dist/svelte/components/MessageCard.svelte +336 -0
  98. package/dist/svelte/components/MessageCard.svelte.d.ts +20 -0
  99. package/dist/svelte/components/MessageCard.svelte.d.ts.map +1 -0
  100. package/dist/svelte/components/MessageDetail.svelte +309 -0
  101. package/dist/svelte/components/MessageDetail.svelte.d.ts +18 -0
  102. package/dist/svelte/components/MessageDetail.svelte.d.ts.map +1 -0
  103. package/dist/svelte/components/MessageFilters.svelte +228 -0
  104. package/dist/svelte/components/MessageFilters.svelte.d.ts +13 -0
  105. package/dist/svelte/components/MessageFilters.svelte.d.ts.map +1 -0
  106. package/dist/svelte/components/MessageList.svelte +101 -0
  107. package/dist/svelte/components/MessageList.svelte.d.ts +23 -0
  108. package/dist/svelte/components/MessageList.svelte.d.ts.map +1 -0
  109. package/dist/svelte/components/MessageStatusIndicator.svelte +82 -0
  110. package/dist/svelte/components/MessageStatusIndicator.svelte.d.ts +11 -0
  111. package/dist/svelte/components/MessageStatusIndicator.svelte.d.ts.map +1 -0
  112. package/dist/svelte/components/MessageToolbar.svelte +131 -0
  113. package/dist/svelte/components/MessageToolbar.svelte.d.ts +14 -0
  114. package/dist/svelte/components/MessageToolbar.svelte.d.ts.map +1 -0
  115. package/dist/svelte/components/MessageTypeBadge.svelte +59 -0
  116. package/dist/svelte/components/MessageTypeBadge.svelte.d.ts +9 -0
  117. package/dist/svelte/components/MessageTypeBadge.svelte.d.ts.map +1 -0
  118. package/dist/svelte/components/RecipientInput.svelte +150 -0
  119. package/dist/svelte/components/RecipientInput.svelte.d.ts +11 -0
  120. package/dist/svelte/components/RecipientInput.svelte.d.ts.map +1 -0
  121. package/dist/svelte/components/ReplyForm.svelte +159 -0
  122. package/dist/svelte/components/ReplyForm.svelte.d.ts +11 -0
  123. package/dist/svelte/components/ReplyForm.svelte.d.ts.map +1 -0
  124. package/dist/svelte/components/SendStatusBadge.svelte +64 -0
  125. package/dist/svelte/components/SendStatusBadge.svelte.d.ts +8 -0
  126. package/dist/svelte/components/SendStatusBadge.svelte.d.ts.map +1 -0
  127. package/dist/svelte/components/ThreadView.svelte +240 -0
  128. package/dist/svelte/components/ThreadView.svelte.d.ts +12 -0
  129. package/dist/svelte/components/ThreadView.svelte.d.ts.map +1 -0
  130. package/dist/svelte/i18n.d.ts +42 -0
  131. package/dist/svelte/i18n.d.ts.map +1 -0
  132. package/dist/svelte/i18n.js +60 -0
  133. package/dist/svelte/i18n.messages.d.ts +32 -0
  134. package/dist/svelte/i18n.messages.d.ts.map +1 -0
  135. package/dist/svelte/i18n.messages.js +46 -0
  136. package/dist/svelte/index.d.ts +54 -0
  137. package/dist/svelte/index.d.ts.map +1 -0
  138. package/dist/svelte/index.js +44 -0
  139. package/dist/svelte/playground.d.ts +341 -0
  140. package/dist/svelte/playground.d.ts.map +1 -0
  141. package/dist/svelte/playground.js +171 -0
  142. package/dist/svelte/types.d.ts +195 -0
  143. package/dist/svelte/types.d.ts.map +1 -0
  144. package/dist/svelte/types.js +6 -0
  145. package/dist/types.d.ts +316 -0
  146. package/dist/types.d.ts.map +1 -0
  147. package/dist/types.js +2 -0
  148. package/dist/types.js.map +1 -0
  149. package/dist/ui.d.ts +4 -0
  150. package/dist/ui.d.ts.map +1 -0
  151. package/dist/ui.js +103 -0
  152. package/dist/ui.js.map +1 -0
  153. package/package.json +104 -0
@@ -0,0 +1,687 @@
1
+ <script lang="ts">
2
+ /**
3
+ * EmailFilterManager - Combined whitelist/blacklist management
4
+ *
5
+ * Reusable component for managing email allow/block lists.
6
+ * Works with any backend via callback props.
7
+ */
8
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
9
+ import { M } from '../i18n.js';
10
+ import type { BlacklistEntry, WhitelistEntry } from '../types.js';
11
+
12
+ const { t } = useI18n();
13
+
14
+ export interface Props {
15
+ whitelist: WhitelistEntry[];
16
+ blacklist: BlacklistEntry[];
17
+ readonly?: boolean;
18
+ onaddwhitelist?: (data: Omit<WhitelistEntry, 'id'>) => Promise<void>;
19
+ onremovewhitelist?: (entry: WhitelistEntry) => Promise<void>;
20
+ onaddblacklist?: (data: Omit<BlacklistEntry, 'id'>) => Promise<void>;
21
+ onremoveblacklist?: (entry: BlacklistEntry) => Promise<void>;
22
+ }
23
+
24
+ const {
25
+ whitelist,
26
+ blacklist,
27
+ readonly: isReadonly = false,
28
+ onaddwhitelist,
29
+ onremovewhitelist,
30
+ onaddblacklist,
31
+ onremoveblacklist,
32
+ }: Props = $props();
33
+
34
+ type ActiveSection = 'whitelist' | 'blacklist';
35
+ let activeSection = $state<ActiveSection>('whitelist');
36
+
37
+ // Whitelist form state
38
+ let showWhitelistForm = $state(false);
39
+ let savingWhitelist = $state(false);
40
+ let wlPattern = $state('');
41
+ let wlType = $state<'email' | 'domain' | 'regex'>('email');
42
+ let wlCategory = $state('');
43
+ let wlDescription = $state('');
44
+
45
+ // Blacklist form state
46
+ let showBlacklistForm = $state(false);
47
+ let savingBlacklist = $state(false);
48
+ let blPattern = $state('');
49
+ let blType = $state<'email' | 'domain' | 'regex'>('email');
50
+ let blReason = $state('');
51
+ let blAutoArchive = $state(true);
52
+
53
+ function resetWhitelistForm() {
54
+ wlPattern = '';
55
+ wlType = 'email';
56
+ wlCategory = '';
57
+ wlDescription = '';
58
+ showWhitelistForm = false;
59
+ }
60
+
61
+ function resetBlacklistForm() {
62
+ blPattern = '';
63
+ blType = 'email';
64
+ blReason = '';
65
+ blAutoArchive = true;
66
+ showBlacklistForm = false;
67
+ }
68
+
69
+ async function saveWhitelistEntry() {
70
+ if (!wlPattern.trim() || !onaddwhitelist) return;
71
+ try {
72
+ savingWhitelist = true;
73
+ await onaddwhitelist({
74
+ pattern: wlPattern.trim(),
75
+ type: wlType,
76
+ category: wlCategory.trim() || null,
77
+ description: wlDescription.trim(),
78
+ });
79
+ resetWhitelistForm();
80
+ } catch (e) {
81
+ } finally {
82
+ savingWhitelist = false;
83
+ }
84
+ }
85
+
86
+ async function removeWhitelistEntry(entry: WhitelistEntry) {
87
+ if (isReadonly || !onremovewhitelist) return;
88
+ try {
89
+ await onremovewhitelist(entry);
90
+ } catch (e) {}
91
+ }
92
+
93
+ async function saveBlacklistEntry() {
94
+ if (!blPattern.trim() || !onaddblacklist) return;
95
+ try {
96
+ savingBlacklist = true;
97
+ await onaddblacklist({
98
+ pattern: blPattern.trim(),
99
+ type: blType,
100
+ reason: blReason.trim(),
101
+ autoArchive: blAutoArchive,
102
+ });
103
+ resetBlacklistForm();
104
+ } catch (e) {
105
+ } finally {
106
+ savingBlacklist = false;
107
+ }
108
+ }
109
+
110
+ async function removeBlacklistEntry(entry: BlacklistEntry) {
111
+ if (isReadonly || !onremoveblacklist) return;
112
+ try {
113
+ await onremoveblacklist(entry);
114
+ } catch (e) {}
115
+ }
116
+
117
+ function getTypeIcon(type: string): string {
118
+ switch (type) {
119
+ case 'email':
120
+ return '@';
121
+ case 'domain':
122
+ return '*.';
123
+ case 'regex':
124
+ return '/r/';
125
+ default:
126
+ return '?';
127
+ }
128
+ }
129
+
130
+ function getPatternPlaceholder(type: string): string {
131
+ switch (type) {
132
+ case 'email':
133
+ return 'user@example.com';
134
+ case 'domain':
135
+ return 'example.com';
136
+ case 'regex':
137
+ return '.*@example\\.com';
138
+ default:
139
+ return '';
140
+ }
141
+ }
142
+ </script>
143
+
144
+ <div class="email-filter-manager">
145
+ <div class="section-toggle">
146
+ <button
147
+ class="section-btn"
148
+ class:active={activeSection === 'whitelist'}
149
+ onclick={() => activeSection = 'whitelist'}
150
+ >
151
+ Whitelist
152
+ <span class="count">{whitelist.length}</span>
153
+ </button>
154
+ <button
155
+ class="section-btn"
156
+ class:active={activeSection === 'blacklist'}
157
+ onclick={() => activeSection = 'blacklist'}
158
+ >
159
+ Blacklist
160
+ <span class="count">{blacklist.length}</span>
161
+ </button>
162
+ </div>
163
+
164
+ <!-- Whitelist Section -->
165
+ {#if activeSection === 'whitelist'}
166
+ <div class="section-content">
167
+ <div class="section-header-row">
168
+ <div class="section-description">
169
+ {t(M['messages.email_filter_manager.whitelist_description'])}
170
+ </div>
171
+ {#if !isReadonly && onaddwhitelist}
172
+ <button
173
+ class="add-btn"
174
+ onclick={() => { resetWhitelistForm(); showWhitelistForm = true; }}
175
+ >+ Add</button>
176
+ {/if}
177
+ </div>
178
+
179
+ {#if showWhitelistForm}
180
+ <div class="entry-form">
181
+ <div class="form-title">{t(M['messages.email_filter_manager.add_whitelist_entry'])}</div>
182
+ <div class="form-row">
183
+ <div class="form-field" style="flex: 0 0 120px;">
184
+ <label class="form-label" for="wl-type">Type</label>
185
+ <select id="wl-type" class="form-select" bind:value={wlType}>
186
+ <option value="email">Email</option>
187
+ <option value="domain">Domain</option>
188
+ <option value="regex">Regex</option>
189
+ </select>
190
+ </div>
191
+ <div class="form-field" style="flex: 1;">
192
+ <label class="form-label" for="wl-pattern">Pattern</label>
193
+ <input
194
+ id="wl-pattern"
195
+ class="form-input"
196
+ type="text"
197
+ bind:value={wlPattern}
198
+ placeholder={getPatternPlaceholder(wlType)}
199
+ />
200
+ </div>
201
+ </div>
202
+ <div class="form-row">
203
+ <div class="form-field" style="flex: 1;">
204
+ <label class="form-label" for="wl-category">Category <span class="optional">(optional)</span></label>
205
+ <input
206
+ id="wl-category"
207
+ class="form-input"
208
+ type="text"
209
+ bind:value={wlCategory}
210
+ placeholder={t(M['messages.email_filter_manager.category_placeholder'])}
211
+ />
212
+ </div>
213
+ <div class="form-field" style="flex: 2;">
214
+ <label class="form-label" for="wl-desc">Description</label>
215
+ <input
216
+ id="wl-desc"
217
+ class="form-input"
218
+ type="text"
219
+ bind:value={wlDescription}
220
+ placeholder={t(M['messages.email_filter_manager.whitelist_description_placeholder'])}
221
+ />
222
+ </div>
223
+ </div>
224
+ <div class="form-actions">
225
+ <button class="cancel-btn" onclick={resetWhitelistForm} disabled={savingWhitelist}>Cancel</button>
226
+ <button class="save-btn" onclick={saveWhitelistEntry} disabled={savingWhitelist || !wlPattern.trim()}>
227
+ {savingWhitelist ? 'Saving...' : 'Add'}
228
+ </button>
229
+ </div>
230
+ </div>
231
+ {/if}
232
+
233
+ {#if whitelist.length === 0 && !showWhitelistForm}
234
+ <p class="placeholder">{t(M['messages.email_filter_manager.no_whitelist_entries'])}</p>
235
+ {:else}
236
+ <div class="entries-list">
237
+ {#each whitelist as entry}
238
+ <div class="entry-card allow">
239
+ <div class="entry-main">
240
+ <span class="type-badge" title={entry.type}>{getTypeIcon(entry.type)}</span>
241
+ <div class="entry-info">
242
+ <span class="entry-pattern">{entry.pattern}</span>
243
+ {#if entry.description}
244
+ <span class="entry-description">{entry.description}</span>
245
+ {/if}
246
+ </div>
247
+ {#if entry.category}
248
+ <span class="category-tag">{entry.category}</span>
249
+ {/if}
250
+ </div>
251
+ {#if !isReadonly && onremovewhitelist}
252
+ <button
253
+ class="delete-btn"
254
+ onclick={() => removeWhitelistEntry(entry)}
255
+ title={t(M['messages.email_filter_manager.whitelist_remove'])}
256
+ >&times;</button>
257
+ {/if}
258
+ </div>
259
+ {/each}
260
+ </div>
261
+ {/if}
262
+ </div>
263
+
264
+ <!-- Blacklist Section -->
265
+ {:else}
266
+ <div class="section-content">
267
+ <div class="section-header-row">
268
+ <div class="section-description">
269
+ {t(M['messages.email_filter_manager.blacklist_description'])}
270
+ </div>
271
+ {#if !isReadonly && onaddblacklist}
272
+ <button
273
+ class="add-btn"
274
+ onclick={() => { resetBlacklistForm(); showBlacklistForm = true; }}
275
+ >+ Add</button>
276
+ {/if}
277
+ </div>
278
+
279
+ {#if showBlacklistForm}
280
+ <div class="entry-form">
281
+ <div class="form-title">{t(M['messages.email_filter_manager.add_blacklist_entry'])}</div>
282
+ <div class="form-row">
283
+ <div class="form-field" style="flex: 0 0 120px;">
284
+ <label class="form-label" for="bl-type">Type</label>
285
+ <select id="bl-type" class="form-select" bind:value={blType}>
286
+ <option value="email">Email</option>
287
+ <option value="domain">Domain</option>
288
+ <option value="regex">Regex</option>
289
+ </select>
290
+ </div>
291
+ <div class="form-field" style="flex: 1;">
292
+ <label class="form-label" for="bl-pattern">Pattern</label>
293
+ <input
294
+ id="bl-pattern"
295
+ class="form-input"
296
+ type="text"
297
+ bind:value={blPattern}
298
+ placeholder={getPatternPlaceholder(blType)}
299
+ />
300
+ </div>
301
+ </div>
302
+ <div class="form-row">
303
+ <div class="form-field" style="flex: 1;">
304
+ <label class="form-label" for="bl-reason">Reason</label>
305
+ <input
306
+ id="bl-reason"
307
+ class="form-input"
308
+ type="text"
309
+ bind:value={blReason}
310
+ placeholder={t(M['messages.email_filter_manager.reason_placeholder'])}
311
+ />
312
+ </div>
313
+ <div class="form-field checkbox-field">
314
+ <label class="form-label checkbox-label">
315
+ <input type="checkbox" bind:checked={blAutoArchive} />
316
+ Auto-archive
317
+ </label>
318
+ </div>
319
+ </div>
320
+ <div class="form-actions">
321
+ <button class="cancel-btn" onclick={resetBlacklistForm} disabled={savingBlacklist}>Cancel</button>
322
+ <button class="save-btn" onclick={saveBlacklistEntry} disabled={savingBlacklist || !blPattern.trim()}>
323
+ {savingBlacklist ? 'Saving...' : 'Add'}
324
+ </button>
325
+ </div>
326
+ </div>
327
+ {/if}
328
+
329
+ {#if blacklist.length === 0 && !showBlacklistForm}
330
+ <p class="placeholder">{t(M['messages.email_filter_manager.no_blacklist_entries'])}</p>
331
+ {:else}
332
+ <div class="entries-list">
333
+ {#each blacklist as entry}
334
+ <div class="entry-card block">
335
+ <div class="entry-main">
336
+ <span class="type-badge" title={entry.type}>{getTypeIcon(entry.type)}</span>
337
+ <div class="entry-info">
338
+ <span class="entry-pattern">{entry.pattern}</span>
339
+ {#if entry.reason}
340
+ <span class="entry-description">{entry.reason}</span>
341
+ {/if}
342
+ </div>
343
+ {#if entry.autoArchive}
344
+ <span class="auto-archive-tag">auto-archive</span>
345
+ {/if}
346
+ </div>
347
+ {#if !isReadonly && onremoveblacklist}
348
+ <button
349
+ class="delete-btn"
350
+ onclick={() => removeBlacklistEntry(entry)}
351
+ title={t(M['messages.email_filter_manager.blacklist_remove'])}
352
+ >&times;</button>
353
+ {/if}
354
+ </div>
355
+ {/each}
356
+ </div>
357
+ {/if}
358
+ </div>
359
+ {/if}
360
+ </div>
361
+
362
+ <style>
363
+ .email-filter-manager {
364
+ display: flex;
365
+ flex-direction: column;
366
+ gap: 0.75rem;
367
+ }
368
+
369
+ .section-toggle {
370
+ display: flex;
371
+ gap: 0.25rem;
372
+ background: var(--smrt-color-surface-container, #f0f1f9);
373
+ padding: 0.25rem;
374
+ border-radius: var(--smrt-radius-md, 8px);
375
+ }
376
+
377
+ .section-btn {
378
+ display: flex;
379
+ align-items: center;
380
+ gap: 0.375rem;
381
+ padding: 0.375rem 0.75rem;
382
+ border: none;
383
+ border-radius: var(--smrt-radius-md, 8px);
384
+ background: transparent;
385
+ color: var(--smrt-color-on-surface-variant, #43474e);
386
+ font-size: var(--smrt-typography-label-large-size, 0.8125rem);
387
+ font-family: inherit;
388
+ cursor: pointer;
389
+ transition: all 150ms ease;
390
+ }
391
+
392
+ .section-btn.active {
393
+ background: var(--smrt-color-primary-container, #d8e2ff);
394
+ color: var(--smrt-color-primary, #005ac1);
395
+ }
396
+
397
+ .section-btn:hover:not(.active) {
398
+ background: var(--smrt-color-surface-container-high, #e6e7ef);
399
+ }
400
+
401
+ .count {
402
+ font-size: var(--smrt-typography-label-small-size, 0.6875rem);
403
+ background: var(--smrt-color-outline-variant, #c2c7cf);
404
+ padding: 0.0625rem 0.375rem;
405
+ border-radius: var(--smrt-radius-full, 9999px);
406
+ }
407
+
408
+ .section-content {
409
+ display: flex;
410
+ flex-direction: column;
411
+ gap: 0.75rem;
412
+ }
413
+
414
+ .section-header-row {
415
+ display: flex;
416
+ align-items: center;
417
+ justify-content: space-between;
418
+ gap: 1rem;
419
+ }
420
+
421
+ .section-description {
422
+ font-size: var(--smrt-typography-body-medium-size, 0.8125rem);
423
+ color: var(--smrt-color-on-surface-variant, #43474e);
424
+ }
425
+
426
+ .add-btn {
427
+ padding: 0.375rem 0.75rem;
428
+ border-radius: var(--smrt-radius-md, 8px);
429
+ border: 1px solid var(--smrt-color-primary, #005ac1);
430
+ background: transparent;
431
+ color: var(--smrt-color-primary, #005ac1);
432
+ cursor: pointer;
433
+ font-size: var(--smrt-typography-label-large-size, 0.8125rem);
434
+ font-family: inherit;
435
+ font-weight: var(--smrt-typography-weight-medium, 500);
436
+ transition: all 150ms ease;
437
+ flex-shrink: 0;
438
+ }
439
+
440
+ .add-btn:hover {
441
+ background: var(--smrt-color-primary, #005ac1);
442
+ color: var(--smrt-color-on-primary, #fff);
443
+ }
444
+
445
+ .entry-form {
446
+ background: var(--smrt-color-surface-container, #f0f1f9);
447
+ border: 1px solid var(--smrt-color-primary, #005ac1);
448
+ border-radius: var(--smrt-radius-md, 8px);
449
+ padding: 1rem;
450
+ display: flex;
451
+ flex-direction: column;
452
+ gap: 0.75rem;
453
+ }
454
+
455
+ .form-title {
456
+ font-size: var(--smrt-typography-title-small-size, 0.875rem);
457
+ font-weight: var(--smrt-typography-weight-semibold, 600);
458
+ color: var(--smrt-color-primary, #005ac1);
459
+ }
460
+
461
+ .form-row {
462
+ display: flex;
463
+ gap: 0.75rem;
464
+ align-items: flex-end;
465
+ }
466
+
467
+ .form-field {
468
+ display: flex;
469
+ flex-direction: column;
470
+ gap: 0.25rem;
471
+ }
472
+
473
+ .form-label {
474
+ font-size: var(--smrt-typography-label-medium-size, 0.75rem);
475
+ font-weight: var(--smrt-typography-weight-medium, 500);
476
+ color: var(--smrt-color-on-surface-variant, #43474e);
477
+ }
478
+
479
+ .optional {
480
+ font-weight: var(--smrt-typography-weight-normal, 400);
481
+ opacity: 0.7;
482
+ }
483
+
484
+ .form-input,
485
+ .form-select {
486
+ padding: 0.5rem 0.625rem;
487
+ border-radius: var(--smrt-radius-md, 8px);
488
+ border: 1px solid var(--smrt-color-outline-variant, #c2c7cf);
489
+ background: var(--smrt-color-surface, #fefbff);
490
+ color: var(--smrt-color-on-surface, #1a1c1e);
491
+ font-size: var(--smrt-typography-body-medium-size, 0.8125rem);
492
+ font-family: inherit;
493
+ transition: border-color 150ms ease;
494
+ }
495
+
496
+ .form-input:focus,
497
+ .form-select:focus {
498
+ outline: none;
499
+ border-color: var(--smrt-color-primary, #005ac1);
500
+ }
501
+
502
+ .form-input::placeholder {
503
+ color: var(--smrt-color-on-surface-variant, #43474e);
504
+ opacity: 0.5;
505
+ }
506
+
507
+ .checkbox-field {
508
+ justify-content: flex-end;
509
+ padding-bottom: 0.5rem;
510
+ }
511
+
512
+ .checkbox-label {
513
+ display: flex;
514
+ align-items: center;
515
+ gap: 0.5rem;
516
+ cursor: pointer;
517
+ font-size: var(--smrt-typography-body-medium-size, 0.8125rem);
518
+ color: var(--smrt-color-on-surface, #1a1c1e);
519
+ white-space: nowrap;
520
+ }
521
+
522
+ .checkbox-label input[type="checkbox"] {
523
+ accent-color: var(--smrt-color-primary, #005ac1);
524
+ }
525
+
526
+ .form-actions {
527
+ display: flex;
528
+ justify-content: flex-end;
529
+ gap: 0.5rem;
530
+ padding-top: 0.25rem;
531
+ }
532
+
533
+ .cancel-btn {
534
+ padding: 0.375rem 0.75rem;
535
+ border-radius: var(--smrt-radius-md, 8px);
536
+ border: 1px solid var(--smrt-color-outline-variant, #c2c7cf);
537
+ background: transparent;
538
+ color: var(--smrt-color-on-surface-variant, #43474e);
539
+ cursor: pointer;
540
+ font-size: var(--smrt-typography-label-large-size, 0.8125rem);
541
+ font-family: inherit;
542
+ transition: all 150ms ease;
543
+ }
544
+
545
+ .cancel-btn:hover {
546
+ background: var(--smrt-color-surface-container-high, #e6e7ef);
547
+ }
548
+
549
+ .save-btn {
550
+ padding: 0.375rem 0.75rem;
551
+ border-radius: var(--smrt-radius-md, 8px);
552
+ border: 1px solid var(--smrt-color-primary, #005ac1);
553
+ background: var(--smrt-color-primary, #005ac1);
554
+ color: var(--smrt-color-on-primary, #fff);
555
+ cursor: pointer;
556
+ font-size: var(--smrt-typography-label-large-size, 0.8125rem);
557
+ font-family: inherit;
558
+ font-weight: var(--smrt-typography-weight-medium, 500);
559
+ transition: all 150ms ease;
560
+ }
561
+
562
+ .save-btn:hover:not(:disabled) {
563
+ opacity: 0.9;
564
+ }
565
+
566
+ .save-btn:disabled {
567
+ opacity: 0.5;
568
+ cursor: not-allowed;
569
+ }
570
+
571
+ .entries-list {
572
+ display: flex;
573
+ flex-direction: column;
574
+ gap: 0.5rem;
575
+ }
576
+
577
+ .entry-card {
578
+ display: flex;
579
+ align-items: center;
580
+ justify-content: space-between;
581
+ background: var(--smrt-color-surface-container, #f0f1f9);
582
+ border: 1px solid var(--smrt-color-outline-variant, #c2c7cf);
583
+ border-radius: var(--smrt-radius-md, 8px);
584
+ padding: 0.75rem 1rem;
585
+ transition: background 150ms ease, border-color 150ms ease;
586
+ }
587
+
588
+ .entry-card:hover {
589
+ background: var(--smrt-color-surface-container-high, #e6e7ef);
590
+ }
591
+
592
+ .entry-card.allow:hover {
593
+ border-color: var(--smrt-color-success, #16a34a);
594
+ }
595
+
596
+ .entry-card.block:hover {
597
+ border-color: var(--smrt-color-error, #ba1a1a);
598
+ }
599
+
600
+ .entry-main {
601
+ display: flex;
602
+ align-items: center;
603
+ gap: 0.75rem;
604
+ flex: 1;
605
+ min-width: 0;
606
+ }
607
+
608
+ .type-badge {
609
+ font-size: var(--smrt-typography-label-small-size, 0.6875rem);
610
+ font-weight: var(--smrt-typography-weight-semibold, 600);
611
+ font-family: var(--smrt-font-family-mono, monospace);
612
+ background: var(--smrt-color-surface, #fefbff);
613
+ border: 1px solid var(--smrt-color-outline-variant, #c2c7cf);
614
+ padding: 0.25rem 0.5rem;
615
+ border-radius: var(--smrt-radius-sm, 4px);
616
+ flex-shrink: 0;
617
+ min-width: 2rem;
618
+ text-align: center;
619
+ }
620
+
621
+ .entry-info {
622
+ display: flex;
623
+ flex-direction: column;
624
+ gap: 0.125rem;
625
+ flex: 1;
626
+ min-width: 0;
627
+ }
628
+
629
+ .entry-pattern {
630
+ font-weight: var(--smrt-typography-weight-medium, 500);
631
+ font-size: var(--smrt-typography-title-small-size, 0.875rem);
632
+ overflow: hidden;
633
+ text-overflow: ellipsis;
634
+ white-space: nowrap;
635
+ }
636
+
637
+ .entry-description {
638
+ font-size: var(--smrt-typography-body-small-size, 0.75rem);
639
+ color: var(--smrt-color-on-surface-variant, #43474e);
640
+ }
641
+
642
+ .category-tag {
643
+ font-size: var(--smrt-typography-label-small-size, 0.6875rem);
644
+ color: var(--smrt-color-tertiary, #6b5778);
645
+ background: var(--smrt-color-tertiary-container, #f2daff);
646
+ padding: 0.125rem 0.5rem;
647
+ border-radius: var(--smrt-radius-full, 9999px);
648
+ flex-shrink: 0;
649
+ }
650
+
651
+ .auto-archive-tag {
652
+ font-size: var(--smrt-typography-label-small-size, 0.6875rem);
653
+ color: var(--smrt-color-warning, #ca8a04);
654
+ background: var(--smrt-color-warning-container, #fef9c3);
655
+ padding: 0.125rem 0.5rem;
656
+ border-radius: var(--smrt-radius-full, 9999px);
657
+ flex-shrink: 0;
658
+ }
659
+
660
+ .delete-btn {
661
+ font-size: var(--smrt-typography-body-large-size, 1rem);
662
+ line-height: 1;
663
+ padding: 0.125rem 0.5rem;
664
+ border-radius: var(--smrt-radius-full, 9999px);
665
+ border: 1px solid transparent;
666
+ background: transparent;
667
+ color: var(--smrt-color-on-surface-variant, #43474e);
668
+ cursor: pointer;
669
+ font-family: inherit;
670
+ transition: all 150ms ease;
671
+ flex-shrink: 0;
672
+ }
673
+
674
+ .delete-btn:hover {
675
+ background: var(--smrt-color-error-container, #fce4ec);
676
+ color: var(--smrt-color-error, #ba1a1a);
677
+ border-color: var(--smrt-color-error, #ba1a1a);
678
+ }
679
+
680
+ .placeholder {
681
+ padding: 2rem;
682
+ text-align: center;
683
+ color: var(--smrt-color-on-surface-variant, #43474e);
684
+ background: var(--smrt-color-surface-container, #f0f1f9);
685
+ border-radius: var(--smrt-radius-md, 8px);
686
+ }
687
+ </style>
@@ -0,0 +1,14 @@
1
+ import type { BlacklistEntry, WhitelistEntry } from '../types.js';
2
+ export interface Props {
3
+ whitelist: WhitelistEntry[];
4
+ blacklist: BlacklistEntry[];
5
+ readonly?: boolean;
6
+ onaddwhitelist?: (data: Omit<WhitelistEntry, 'id'>) => Promise<void>;
7
+ onremovewhitelist?: (entry: WhitelistEntry) => Promise<void>;
8
+ onaddblacklist?: (data: Omit<BlacklistEntry, 'id'>) => Promise<void>;
9
+ onremoveblacklist?: (entry: BlacklistEntry) => Promise<void>;
10
+ }
11
+ declare const EmailFilterManager: import("svelte").Component<Props, {}, "">;
12
+ type EmailFilterManager = ReturnType<typeof EmailFilterManager>;
13
+ export default EmailFilterManager;
14
+ //# sourceMappingURL=EmailFilterManager.svelte.d.ts.map
@@ -0,0 +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;AA6SD,QAAA,MAAM,kBAAkB,2CAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}