@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,23 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { AccountData, MessageData } from '../types.js';
3
+ export interface Props {
4
+ messages: MessageData[];
5
+ selected?: Set<string>;
6
+ activeMessageId?: string;
7
+ accounts?: AccountData[];
8
+ loading?: boolean;
9
+ emptyMessage?: string;
10
+ onmessageclick?: (message: MessageData) => void;
11
+ onselect?: (message: MessageData) => void;
12
+ onflag?: (message: MessageData) => void;
13
+ showType?: boolean;
14
+ showAccount?: boolean;
15
+ compact?: boolean;
16
+ card?: Snippet<[{
17
+ message: MessageData;
18
+ }]>;
19
+ }
20
+ declare const MessageList: import("svelte").Component<Props, {}, "">;
21
+ type MessageList = ReturnType<typeof MessageList>;
22
+ export default MessageList;
23
+ //# sourceMappingURL=MessageList.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageList.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/MessageList.svelte.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI5D,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAChD,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAC1C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC,CAAC;CAC5C;AAyDD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -0,0 +1,82 @@
1
+ <script lang="ts">
2
+ /**
3
+ * MessageStatusIndicator - Read/flag/attachment indicators
4
+ */
5
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
6
+ import { M } from '../i18n.messages.js';
7
+
8
+ const { t } = useI18n();
9
+
10
+ export interface Props {
11
+ isRead: boolean;
12
+ isFlagged: boolean;
13
+ hasAttachments: boolean;
14
+ isAnswered?: boolean;
15
+ isDraft?: boolean;
16
+ }
17
+
18
+ const {
19
+ isRead,
20
+ isFlagged,
21
+ hasAttachments,
22
+ isAnswered = false,
23
+ isDraft = false,
24
+ }: Props = $props();
25
+ </script>
26
+
27
+ <span class="status-indicators" role="group" aria-label={t(M['messages.message_status_indicator.status'])}>
28
+ {#if !isRead}
29
+ <span class="indicator unread" title={t(M['messages.message_status_indicator.unread'])} aria-label={t(M['messages.message_status_indicator.unread'])}>
30
+ <span class="dot"></span>
31
+ </span>
32
+ {/if}
33
+ {#if isFlagged}
34
+ <span class="indicator flagged" title={t(M['messages.message_status_indicator.flagged'])} aria-label={t(M['messages.message_status_indicator.flagged'])}>⚑</span>
35
+ {/if}
36
+ {#if hasAttachments}
37
+ <span class="indicator attachment" title={t(M['messages.message_status_indicator.has_attachments'])} aria-label={t(M['messages.message_status_indicator.has_attachments'])}>📎</span>
38
+ {/if}
39
+ {#if isAnswered}
40
+ <span class="indicator answered" title={t(M['messages.message_status_indicator.replied'])} aria-label={t(M['messages.message_status_indicator.replied'])}>↩</span>
41
+ {/if}
42
+ {#if isDraft}
43
+ <span class="indicator draft" title={t(M['messages.message_status_indicator.draft'])} aria-label={t(M['messages.message_status_indicator.draft'])}>✎</span>
44
+ {/if}
45
+ </span>
46
+
47
+ <style>
48
+ .status-indicators {
49
+ display: inline-flex;
50
+ align-items: center;
51
+ gap: 0.375rem;
52
+ font-size: var(--smrt-typography-label-large-size, 0.875rem);
53
+ }
54
+
55
+ .indicator {
56
+ display: inline-flex;
57
+ align-items: center;
58
+ }
59
+
60
+ .unread .dot {
61
+ width: 0.5rem;
62
+ height: 0.5rem;
63
+ border-radius: var(--smrt-radius-full, 9999px);
64
+ background: var(--smrt-color-primary, #005ac1);
65
+ }
66
+
67
+ .flagged {
68
+ color: var(--smrt-color-error, #ba1a1a);
69
+ }
70
+
71
+ .attachment {
72
+ color: var(--smrt-color-on-surface-variant, #43474e);
73
+ }
74
+
75
+ .answered {
76
+ color: var(--smrt-color-primary, #005ac1);
77
+ }
78
+
79
+ .draft {
80
+ color: var(--smrt-color-on-surface-variant, #43474e);
81
+ }
82
+ </style>
@@ -0,0 +1,11 @@
1
+ export interface Props {
2
+ isRead: boolean;
3
+ isFlagged: boolean;
4
+ hasAttachments: boolean;
5
+ isAnswered?: boolean;
6
+ isDraft?: boolean;
7
+ }
8
+ declare const MessageStatusIndicator: import("svelte").Component<Props, {}, "">;
9
+ type MessageStatusIndicator = ReturnType<typeof MessageStatusIndicator>;
10
+ export default MessageStatusIndicator;
11
+ //# sourceMappingURL=MessageStatusIndicator.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageStatusIndicator.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/MessageStatusIndicator.svelte.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AA0CD,QAAA,MAAM,sBAAsB,2CAAwC,CAAC;AACrE,KAAK,sBAAsB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACxE,eAAe,sBAAsB,CAAC"}
@@ -0,0 +1,131 @@
1
+ <script lang="ts">
2
+ /**
3
+ * MessageToolbar - Bulk action toolbar
4
+ */
5
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
6
+ import type { Snippet } from 'svelte';
7
+ import { M } from '../i18n.messages.js';
8
+ import type { BulkAction } from '../types.js';
9
+
10
+ const { t } = useI18n();
11
+
12
+ export interface Props {
13
+ selectedCount: number;
14
+ totalCount: number;
15
+ onaction?: (action: BulkAction) => void;
16
+ onselectall?: () => void;
17
+ onclearselection?: () => void;
18
+ extraActions?: Snippet;
19
+ }
20
+
21
+ const {
22
+ selectedCount,
23
+ totalCount,
24
+ onaction,
25
+ onselectall,
26
+ onclearselection,
27
+ extraActions,
28
+ }: Props = $props();
29
+ </script>
30
+
31
+ <div class="toolbar" role="toolbar" aria-label={t(M['messages.message_toolbar.actions_label'])}>
32
+ <div class="selection-info">
33
+ {#if selectedCount > 0}
34
+ <span class="count">{t(M['messages.message_toolbar.count_selected'], { selectedCount, totalCount })}</span>
35
+ {#if onclearselection}
36
+ <button class="link-btn" type="button" onclick={onclearselection}>
37
+ Clear
38
+ </button>
39
+ {/if}
40
+ {:else}
41
+ <span class="count">{totalCount} messages</span>
42
+ {/if}
43
+ {#if onselectall && selectedCount < totalCount}
44
+ <button class="link-btn" type="button" onclick={onselectall}>
45
+ {t(M['messages.message_toolbar.select_all'])}
46
+ </button>
47
+ {/if}
48
+ </div>
49
+
50
+ {#if selectedCount > 0 && onaction}
51
+ <div class="actions">
52
+ <button class="action-btn" type="button" onclick={() => onaction?.('markRead')}>
53
+ {t(M['messages.message_toolbar.mark_read'])}
54
+ </button>
55
+ <button class="action-btn" type="button" onclick={() => onaction?.('markUnread')}>
56
+ {t(M['messages.message_toolbar.mark_unread'])}
57
+ </button>
58
+ <button class="action-btn" type="button" onclick={() => onaction?.('flag')}>
59
+ Flag
60
+ </button>
61
+ <button class="action-btn" type="button" onclick={() => onaction?.('unflag')}>
62
+ Unflag
63
+ </button>
64
+ <button class="action-btn action-btn--danger" type="button" onclick={() => onaction?.('delete')}>
65
+ Delete
66
+ </button>
67
+ {#if extraActions}
68
+ {@render extraActions()}
69
+ {/if}
70
+ </div>
71
+ {/if}
72
+ </div>
73
+
74
+ <style>
75
+ .toolbar {
76
+ display: flex;
77
+ align-items: center;
78
+ justify-content: space-between;
79
+ padding: 0.5rem 0.75rem;
80
+ background: var(--smrt-color-surface-variant, #e1e2ec);
81
+ border-radius: var(--smrt-radius-small, 0.25rem);
82
+ gap: 1rem;
83
+ flex-wrap: wrap;
84
+ }
85
+
86
+ .selection-info {
87
+ display: flex;
88
+ align-items: center;
89
+ gap: 0.5rem;
90
+ }
91
+
92
+ .count {
93
+ font: var(--smrt-typography-body-small-font, 0.75rem / 1.33 sans-serif);
94
+ color: var(--smrt-color-on-surface-variant, #43474e);
95
+ }
96
+
97
+ .link-btn {
98
+ border: none;
99
+ background: none;
100
+ font: var(--smrt-typography-label-medium-font, 500 0.75rem / 1.33 sans-serif);
101
+ color: var(--smrt-color-primary, #005ac1);
102
+ cursor: pointer;
103
+ text-decoration: underline;
104
+ padding: 0;
105
+ }
106
+
107
+ .actions {
108
+ display: flex;
109
+ gap: 0.375rem;
110
+ flex-wrap: wrap;
111
+ }
112
+
113
+ .action-btn {
114
+ padding: 0.25rem 0.625rem;
115
+ border: 1px solid var(--smrt-color-outline, #72787e);
116
+ border-radius: var(--smrt-radius-small, 0.25rem);
117
+ background: var(--smrt-color-surface, #fefbff);
118
+ color: var(--smrt-color-on-surface, #1a1c1e);
119
+ font: var(--smrt-typography-label-small-font, 500 0.6875rem / 1 sans-serif);
120
+ cursor: pointer;
121
+ }
122
+
123
+ .action-btn:hover {
124
+ background: var(--smrt-color-surface-variant, #e1e2ec);
125
+ }
126
+
127
+ .action-btn--danger {
128
+ color: var(--smrt-color-error, #ba1a1a);
129
+ border-color: var(--smrt-color-error, #ba1a1a);
130
+ }
131
+ </style>
@@ -0,0 +1,14 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { BulkAction } from '../types.js';
3
+ export interface Props {
4
+ selectedCount: number;
5
+ totalCount: number;
6
+ onaction?: (action: BulkAction) => void;
7
+ onselectall?: () => void;
8
+ onclearselection?: () => void;
9
+ extraActions?: Snippet;
10
+ }
11
+ declare const MessageToolbar: import("svelte").Component<Props, {}, "">;
12
+ type MessageToolbar = ReturnType<typeof MessageToolbar>;
13
+ export default MessageToolbar;
14
+ //# sourceMappingURL=MessageToolbar.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageToolbar.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/MessageToolbar.svelte.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,WAAW,KAAK;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IACxC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAoED,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -0,0 +1,59 @@
1
+ <script lang="ts">
2
+ /**
3
+ * MessageTypeBadge - Icon + label badge for message types
4
+ */
5
+ import { Badge } from '@happyvertical/smrt-ui/ui';
6
+ import type { MessageType } from '../types.js';
7
+
8
+ export interface Props {
9
+ type: MessageType;
10
+ size?: 'sm' | 'md';
11
+ }
12
+
13
+ const { type, size = 'sm' }: Props = $props();
14
+
15
+ const _label = $derived.by(() => {
16
+ switch (type) {
17
+ case 'email':
18
+ return 'Email';
19
+ case 'tweet':
20
+ return 'Tweet';
21
+ case 'slack':
22
+ return 'Slack';
23
+ default:
24
+ return type;
25
+ }
26
+ });
27
+
28
+ const _variant = $derived.by(() => {
29
+ switch (type) {
30
+ case 'email':
31
+ return 'primary' as const;
32
+ case 'tweet':
33
+ return 'info' as const;
34
+ case 'slack':
35
+ return 'success' as const;
36
+ default:
37
+ return 'default' as const;
38
+ }
39
+ });
40
+ </script>
41
+
42
+ <Badge variant={_variant} {size}>
43
+ <span class="type-badge">
44
+ <span class="type-icon">{type === 'email' ? '✉' : type === 'tweet' ? '𝕏' : type === 'slack' ? '#' : '✉'}</span>
45
+ {_label}
46
+ </span>
47
+ </Badge>
48
+
49
+ <style>
50
+ .type-badge {
51
+ display: inline-flex;
52
+ align-items: center;
53
+ gap: 0.25rem;
54
+ }
55
+
56
+ .type-icon {
57
+ font-size: 0.85em;
58
+ }
59
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { MessageType } from '../types.js';
2
+ export interface Props {
3
+ type: MessageType;
4
+ size?: 'sm' | 'md';
5
+ }
6
+ declare const MessageTypeBadge: import("svelte").Component<Props, {}, "">;
7
+ type MessageTypeBadge = ReturnType<typeof MessageTypeBadge>;
8
+ export default MessageTypeBadge;
9
+ //# sourceMappingURL=MessageTypeBadge.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageTypeBadge.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/MessageTypeBadge.svelte.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACpB;AA+CD,QAAA,MAAM,gBAAgB,2CAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,150 @@
1
+ <script lang="ts" module>
2
+ import type { RecipientEntry } from '../types.js';
3
+
4
+ export interface Props {
5
+ label?: string;
6
+ recipients: RecipientEntry[];
7
+ onchange?: (recipients: RecipientEntry[]) => void;
8
+ placeholder?: string;
9
+ }
10
+ </script>
11
+
12
+ <script lang="ts">
13
+ let {
14
+ label = 'To',
15
+ recipients = [],
16
+ onchange,
17
+ placeholder = 'Add recipient...',
18
+ }: Props = $props();
19
+
20
+ let inputValue = $state('');
21
+ const inputId = $derived(
22
+ `recipient-input-${label.toLowerCase().replace(/[^a-z0-9]+/gi, '-')}`,
23
+ );
24
+
25
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
26
+
27
+ function addRecipient() {
28
+ const trimmed = inputValue.trim();
29
+ if (!trimmed) return;
30
+
31
+ const isValid = emailRegex.test(trimmed);
32
+ const entry: RecipientEntry = {
33
+ address: trimmed,
34
+ isValid,
35
+ };
36
+
37
+ const updated = [...recipients, entry];
38
+ onchange?.(updated);
39
+ inputValue = '';
40
+ }
41
+
42
+ function removeRecipient(index: number) {
43
+ const updated = recipients.filter((_, i) => i !== index);
44
+ onchange?.(updated);
45
+ }
46
+
47
+ function handleKeydown(event: KeyboardEvent) {
48
+ if (event.key === 'Enter' || event.key === ',') {
49
+ event.preventDefault();
50
+ addRecipient();
51
+ }
52
+ if (event.key === 'Backspace' && !inputValue && recipients.length > 0) {
53
+ removeRecipient(recipients.length - 1);
54
+ }
55
+ }
56
+ </script>
57
+
58
+ <div class="recipient-input">
59
+ <label class="label" for={inputId}>{label}</label>
60
+ <div class="chips-container">
61
+ {#each recipients as recipient, i}
62
+ <span class="chip" class:invalid={!recipient.isValid}>
63
+ <span class="chip-text">{recipient.name || recipient.address}</span>
64
+ <button
65
+ class="chip-remove"
66
+ onclick={() => removeRecipient(i)}
67
+ type="button"
68
+ >×</button>
69
+ </span>
70
+ {/each}
71
+ <input
72
+ id={inputId}
73
+ type="text"
74
+ class="input"
75
+ bind:value={inputValue}
76
+ {placeholder}
77
+ onkeydown={handleKeydown}
78
+ onblur={addRecipient}
79
+ />
80
+ </div>
81
+ </div>
82
+
83
+ <style>
84
+ .recipient-input {
85
+ display: flex;
86
+ align-items: flex-start;
87
+ gap: var(--smrt-spacing-2, 8px);
88
+ padding: var(--smrt-spacing-1, 4px) 0;
89
+ border-bottom: 1px solid var(--smrt-color-outline-variant, #cac4d0);
90
+ }
91
+
92
+ .label {
93
+ font-family: var(--smrt-font-family, system-ui);
94
+ font-size: var(--smrt-typography-label-large-size, 14px);
95
+ color: var(--smrt-color-on-surface-variant, #49454f);
96
+ padding-top: var(--smrt-spacing-2, 8px);
97
+ min-width: 32px;
98
+ }
99
+
100
+ .chips-container {
101
+ display: flex;
102
+ flex-wrap: wrap;
103
+ gap: var(--smrt-spacing-1, 4px);
104
+ flex: 1;
105
+ align-items: center;
106
+ }
107
+
108
+ .chip {
109
+ display: inline-flex;
110
+ align-items: center;
111
+ gap: var(--smrt-spacing-1, 4px);
112
+ padding: var(--smrt-spacing-1, 4px) var(--smrt-spacing-1, 4px) var(--smrt-spacing-1, 4px) var(--smrt-spacing-2, 8px);
113
+ border-radius: var(--smrt-radius-sm, 8px);
114
+ background: var(--smrt-color-secondary-container, #e8def8);
115
+ color: var(--smrt-color-on-secondary-container, #1d192b);
116
+ font-size: var(--smrt-typography-label-large-size, 13px);
117
+ font-family: var(--smrt-font-family, system-ui);
118
+ }
119
+
120
+ .chip.invalid {
121
+ background: var(--smrt-color-error-container, #ffdad6);
122
+ color: var(--smrt-color-on-error-container, #410002);
123
+ }
124
+
125
+ .chip-remove {
126
+ background: none;
127
+ border: none;
128
+ cursor: pointer;
129
+ padding: 0 var(--smrt-spacing-1, 4px);
130
+ font-size: var(--smrt-typography-label-large-size, 14px);
131
+ color: inherit;
132
+ opacity: 0.7;
133
+ }
134
+
135
+ .chip-remove:hover {
136
+ opacity: 1;
137
+ }
138
+
139
+ .input {
140
+ flex: 1;
141
+ min-width: 120px;
142
+ border: none;
143
+ outline: none;
144
+ font-family: var(--smrt-font-family, system-ui);
145
+ font-size: var(--smrt-typography-body-medium-size, 14px);
146
+ padding: var(--smrt-spacing-1, 4px) 0;
147
+ background: transparent;
148
+ color: var(--smrt-color-on-surface, #1c1b1f);
149
+ }
150
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { RecipientEntry } from '../types.js';
2
+ export interface Props {
3
+ label?: string;
4
+ recipients: RecipientEntry[];
5
+ onchange?: (recipients: RecipientEntry[]) => void;
6
+ placeholder?: string;
7
+ }
8
+ declare const RecipientInput: import("svelte").Component<Props, {}, "">;
9
+ type RecipientInput = ReturnType<typeof RecipientInput>;
10
+ export default RecipientInput;
11
+ //# sourceMappingURL=RecipientInput.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RecipientInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/RecipientInput.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,KAAK;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,cAAc,EAAE,KAAK,IAAI,CAAC;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAmED,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -0,0 +1,159 @@
1
+ <script lang="ts" module>
2
+ import type { MessageData } from '../types.js';
3
+
4
+ export interface Props {
5
+ originalMessage: MessageData;
6
+ replyAll?: boolean;
7
+ onsend?: (body: string) => void;
8
+ oncancel?: () => void;
9
+ }
10
+ </script>
11
+
12
+ <script lang="ts">
13
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
14
+ import { M } from '../i18n.js';
15
+
16
+ const { t } = useI18n();
17
+
18
+ let {
19
+ originalMessage,
20
+ replyAll = false,
21
+ onsend,
22
+ oncancel,
23
+ }: Props = $props();
24
+
25
+ let body = $state('');
26
+ let isSending = $state(false);
27
+
28
+ const quotedBody = $derived.by(() => {
29
+ const dateStr = originalMessage.date
30
+ ? new Date(originalMessage.date).toLocaleString()
31
+ : 'unknown date';
32
+ const from = originalMessage.senderName || originalMessage.senderAddress;
33
+ const lines = (originalMessage.body || '')
34
+ .split('\n')
35
+ .map((l) => `> ${l}`)
36
+ .join('\n');
37
+ return `On ${dateStr}, ${from} wrote:\n${lines}`;
38
+ });
39
+
40
+ function handleSend() {
41
+ if (isSending) return;
42
+ isSending = true;
43
+ onsend?.(body);
44
+ }
45
+ </script>
46
+
47
+ <div class="reply-form">
48
+ <div class="reply-header">
49
+ {replyAll ? 'Reply All' : 'Reply'} to {originalMessage.senderName || originalMessage.senderAddress}
50
+ </div>
51
+
52
+ <textarea
53
+ class="reply-body"
54
+ bind:value={body}
55
+ placeholder={t(M['messages.reply_form.body_placeholder'])}
56
+ rows={5}
57
+ ></textarea>
58
+
59
+ <div class="quoted-original">
60
+ <pre class="quoted-text">{quotedBody}</pre>
61
+ </div>
62
+
63
+ <div class="actions">
64
+ <button
65
+ type="button"
66
+ class="btn-primary"
67
+ disabled={isSending || !body.trim()}
68
+ onclick={handleSend}
69
+ >
70
+ {isSending ? 'Sending...' : 'Send Reply'}
71
+ </button>
72
+ <button type="button" class="btn-text" onclick={() => oncancel?.()}>
73
+ Cancel
74
+ </button>
75
+ </div>
76
+ </div>
77
+
78
+ <style>
79
+ .reply-form {
80
+ display: flex;
81
+ flex-direction: column;
82
+ gap: var(--smrt-spacing-2, 8px);
83
+ padding: var(--smrt-spacing-3, 12px);
84
+ border: 1px solid var(--smrt-color-outline-variant, #cac4d0);
85
+ border-radius: var(--smrt-radius-md, 12px);
86
+ background: var(--smrt-color-surface, #fffbfe);
87
+ font-family: var(--smrt-font-family, system-ui);
88
+ }
89
+
90
+ .reply-header {
91
+ font-size: var(--smrt-typography-title-small-size, 13px);
92
+ color: var(--smrt-color-on-surface-variant, #49454f);
93
+ font-weight: var(--smrt-typography-weight-medium, 500);
94
+ }
95
+
96
+ .reply-body {
97
+ width: 100%;
98
+ border: 1px solid var(--smrt-color-outline-variant, #cac4d0);
99
+ border-radius: var(--smrt-radius-sm, 8px);
100
+ padding: var(--smrt-spacing-2, 8px);
101
+ font-family: var(--smrt-font-family, system-ui);
102
+ font-size: var(--smrt-typography-body-medium-size, 14px);
103
+ resize: vertical;
104
+ box-sizing: border-box;
105
+ }
106
+
107
+ .reply-body:focus {
108
+ outline: 2px solid var(--smrt-color-primary, #6750a4);
109
+ outline-offset: -1px;
110
+ }
111
+
112
+ .quoted-original {
113
+ padding: var(--smrt-spacing-2, 8px);
114
+ border-left: 3px solid var(--smrt-color-outline-variant, #cac4d0);
115
+ background: var(--smrt-color-surface-variant, #e7e0ec);
116
+ border-radius: 0 var(--smrt-radius-sm, 8px) var(--smrt-radius-sm, 8px) 0;
117
+ max-height: 200px;
118
+ overflow-y: auto;
119
+ }
120
+
121
+ .quoted-text {
122
+ margin: 0;
123
+ font-size: var(--smrt-typography-body-small-size, 12px);
124
+ color: var(--smrt-color-on-surface-variant, #49454f);
125
+ white-space: pre-wrap;
126
+ font-family: var(--smrt-font-family, system-ui);
127
+ }
128
+
129
+ .actions {
130
+ display: flex;
131
+ gap: var(--smrt-spacing-2, 8px);
132
+ }
133
+
134
+ .btn-primary {
135
+ padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-6, 24px);
136
+ border-radius: var(--smrt-radius-full, 20px);
137
+ border: none;
138
+ background: var(--smrt-color-primary, #6750a4);
139
+ color: var(--smrt-color-on-primary, #fff);
140
+ font-family: var(--smrt-font-family, system-ui);
141
+ font-size: var(--smrt-typography-label-large-size, 14px);
142
+ cursor: pointer;
143
+ }
144
+
145
+ .btn-primary:disabled {
146
+ opacity: 0.6;
147
+ cursor: not-allowed;
148
+ }
149
+
150
+ .btn-text {
151
+ padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-4, 16px);
152
+ border: none;
153
+ background: transparent;
154
+ color: var(--smrt-color-on-surface-variant, #49454f);
155
+ font-family: var(--smrt-font-family, system-ui);
156
+ font-size: var(--smrt-typography-label-large-size, 14px);
157
+ cursor: pointer;
158
+ }
159
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { MessageData } from '../types.js';
2
+ export interface Props {
3
+ originalMessage: MessageData;
4
+ replyAll?: boolean;
5
+ onsend?: (body: string) => void;
6
+ oncancel?: () => void;
7
+ }
8
+ declare const ReplyForm: import("svelte").Component<Props, {}, "">;
9
+ type ReplyForm = ReturnType<typeof ReplyForm>;
10
+ export default ReplyForm;
11
+ //# sourceMappingURL=ReplyForm.svelte.d.ts.map