@happyvertical/smrt-messages 0.31.0 → 0.31.1
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.
- package/dist/index.js +49 -7
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +2 -2
- package/dist/models/Message.d.ts +10 -0
- package/dist/models/Message.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +4 -4
- package/dist/svelte/components/AccountAvatar.svelte +4 -4
- package/dist/svelte/components/AccountList.svelte +12 -3
- package/dist/svelte/components/AccountList.svelte.d.ts.map +1 -1
- package/dist/svelte/components/ComposeForm.svelte +27 -2
- package/dist/svelte/components/ComposeForm.svelte.d.ts.map +1 -1
- package/dist/svelte/components/EmailAccountManager.svelte +25 -2
- package/dist/svelte/components/EmailAccountManager.svelte.d.ts.map +1 -1
- package/dist/svelte/components/EmailFilterManager.svelte +50 -2
- package/dist/svelte/components/EmailFilterManager.svelte.d.ts.map +1 -1
- package/dist/svelte/components/ForwardForm.svelte +26 -2
- package/dist/svelte/components/ForwardForm.svelte.d.ts.map +1 -1
- package/dist/svelte/components/MessageDetail.svelte +11 -14
- package/dist/svelte/components/MessageDetail.svelte.d.ts.map +1 -1
- package/dist/svelte/components/ReplyForm.svelte +26 -2
- package/dist/svelte/components/ReplyForm.svelte.d.ts.map +1 -1
- package/dist/svelte/components/SendStatusBadge.svelte +1 -1
- package/dist/svelte/components/ThreadView.svelte +10 -3
- package/dist/svelte/components/ThreadView.svelte.d.ts.map +1 -1
- package/dist/svelte/i18n.d.ts +2 -0
- package/dist/svelte/i18n.d.ts.map +1 -1
- package/dist/svelte/i18n.js +2 -0
- package/package.json +7 -7
package/dist/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "1.0.0",
|
|
3
|
-
"timestamp":
|
|
3
|
+
"timestamp": 1782192569825,
|
|
4
4
|
"packageName": "@happyvertical/smrt-messages",
|
|
5
|
-
"packageVersion": "0.31.
|
|
5
|
+
"packageVersion": "0.31.1",
|
|
6
6
|
"objects": {
|
|
7
7
|
"@happyvertical/smrt-messages:AccountCollection": {
|
|
8
8
|
"name": "accountcollection",
|
package/dist/models/Message.d.ts
CHANGED
|
@@ -87,6 +87,16 @@ export declare class Message extends SmrtObject {
|
|
|
87
87
|
* Retry sending a failed message
|
|
88
88
|
*/
|
|
89
89
|
retrySend(options?: SendMessageOptions): Promise<MessageSendResult>;
|
|
90
|
+
/**
|
|
91
|
+
* Options for a derived draft (reply/forward) built from this message. Carries
|
|
92
|
+
* the DB connection + tenant context from `this.options`, but strips this
|
|
93
|
+
* message's own identity fields. When this message was hydrated from the DB,
|
|
94
|
+
* `this.options` holds the row's `id`/`slug`/`context`/`_skipLoad`; spreading
|
|
95
|
+
* those into a new draft would make `draft.save()` upsert onto the natural-key
|
|
96
|
+
* conflict columns (`slug`/`context`/`_meta_type`) and overwrite the ORIGINAL
|
|
97
|
+
* message instead of inserting a new row. See EmailAccount.childOptions().
|
|
98
|
+
*/
|
|
99
|
+
protected draftOptions(): Record<string, unknown>;
|
|
90
100
|
/**
|
|
91
101
|
* Create a reply to this message (returns unsaved draft)
|
|
92
102
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../src/models/Message.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAc,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,UAAU,EACX,MAAM,UAAU,CAAC;AAElB,qBAOa,OAAQ,SAAQ,UAAU;IAErC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAG/B,SAAS,SAAM;IACf,QAAQ,SAAM;IACd,OAAO,SAAM;IACb,IAAI,SAAM;IACV,WAAW,SAAM;IACjB,QAAQ,SAAM;IACd,WAAW,SAAM;IACjB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAQ;IACzB,MAAM,UAAS;IACf,SAAS,UAAS;IAClB,cAAc,UAAS;IACvB,IAAI,SAAK;IACT,QAAQ,SAAM;IAGd,UAAU,EAAE,UAAU,CAAW;IACjC,MAAM,EAAE,IAAI,GAAG,IAAI,CAAQ;IAC3B,SAAS,SAAM;IACf,UAAU,SAAK;IACf,UAAU,SAAK;IACf,eAAe,EAAE,IAAI,GAAG,IAAI,CAAQ;IAEpC,kBAAkB,SAAM;IAGxB,SAAS,OAAc;IACvB,SAAS,OAAc;gBAEX,OAAO,GAAE,cAAmB;IAiCxC;;OAEG;IACH,cAAc,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAS3D;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;IAI1E;;OAEG;IACH,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IASlC;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAI5C;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAM/B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAMpC;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,UAAU,CAAC,SAAS,SAAM,GAAG,MAAM;IAMnC;;OAEG;IACG,UAAU;IAWhB;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAW7C;;OAEG;IACG,cAAc;IAapB;;OAEG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../src/models/Message.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAc,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,UAAU,EACX,MAAM,UAAU,CAAC;AAElB,qBAOa,OAAQ,SAAQ,UAAU;IAErC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAG/B,SAAS,SAAM;IACf,QAAQ,SAAM;IACd,OAAO,SAAM;IACb,IAAI,SAAM;IACV,WAAW,SAAM;IACjB,QAAQ,SAAM;IACd,WAAW,SAAM;IACjB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAQ;IACzB,MAAM,UAAS;IACf,SAAS,UAAS;IAClB,cAAc,UAAS;IACvB,IAAI,SAAK;IACT,QAAQ,SAAM;IAGd,UAAU,EAAE,UAAU,CAAW;IACjC,MAAM,EAAE,IAAI,GAAG,IAAI,CAAQ;IAC3B,SAAS,SAAM;IACf,UAAU,SAAK;IACf,UAAU,SAAK;IACf,eAAe,EAAE,IAAI,GAAG,IAAI,CAAQ;IAEpC,kBAAkB,SAAM;IAGxB,SAAS,OAAc;IACvB,SAAS,OAAc;gBAEX,OAAO,GAAE,cAAmB;IAiCxC;;OAEG;IACH,cAAc,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAS3D;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;IAI1E;;OAEG;IACH,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IASlC;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAI5C;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAM/B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAMpC;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,UAAU,CAAC,SAAS,SAAM,GAAG,MAAM;IAMnC;;OAEG;IACG,UAAU;IAWhB;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAW7C;;OAEG;IACG,cAAc;IAapB;;OAEG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAwFpE;;OAEG;IACG,SAAS,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAwBzE;;;;;;;;OAQG;IACH,SAAS,CAAC,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IASjD;;OAEG;IACH,WAAW,CAAC,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO;IA0BvD;;OAEG;IACH,aAAa,IAAI,OAAO;IAyBxB;;OAEG;IACH,SAAS,CAAC,eAAe,IAAI,MAAM;CAapC"}
|
package/dist/smrt-knowledge.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-06-
|
|
3
|
+
"generatedAt": "2026-06-23T05:29:30.369Z",
|
|
4
4
|
"packageName": "@happyvertical/smrt-messages",
|
|
5
|
-
"packageVersion": "0.31.
|
|
5
|
+
"packageVersion": "0.31.1",
|
|
6
6
|
"sourceManifestPath": "dist/manifest.json",
|
|
7
7
|
"agentDocPath": "AGENTS.md",
|
|
8
8
|
"sourceHashes": {
|
|
9
|
-
"manifest": "
|
|
10
|
-
"packageJson": "
|
|
9
|
+
"manifest": "1f6a120e0283bad9c9fdc24c484859aed4a96ad5f95251652726be0b89efd677",
|
|
10
|
+
"packageJson": "0472121595e3074e12c90af955583d88bde78684290e5af4851d10259a0b3804",
|
|
11
11
|
"agents": "32563a30ceeb21bd8732042f894e2a148fd83727caffc1eec7c97540005c6fdc"
|
|
12
12
|
},
|
|
13
13
|
"exports": [
|
|
@@ -87,13 +87,13 @@ const _icon = $derived.by(() => {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
.avatar--slack {
|
|
90
|
-
background: #e8def8;
|
|
91
|
-
color: #4a1175;
|
|
90
|
+
background: var(--smrt-color-tertiary-container, #e8def8);
|
|
91
|
+
color: var(--smrt-color-on-tertiary-container, #4a1175);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
.avatar--twitter {
|
|
95
|
-
background: #d3e8fd;
|
|
96
|
-
color: #0c4a6e;
|
|
95
|
+
background: var(--smrt-color-secondary-container, #d3e8fd);
|
|
96
|
+
color: var(--smrt-color-on-secondary-container, #0c4a6e);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
.icon {
|
|
@@ -26,7 +26,16 @@ const {
|
|
|
26
26
|
onremove,
|
|
27
27
|
}: Props = $props();
|
|
28
28
|
|
|
29
|
-
function handleAccountClick(account: AccountData) {
|
|
29
|
+
function handleAccountClick(event: MouseEvent, account: AccountData) {
|
|
30
|
+
// Action buttons (Sync/Activate/Deactivate/Remove) render inside the
|
|
31
|
+
// AccountCard, which sits inside this role="button" wrapper. A click on one
|
|
32
|
+
// of them bubbles up here and would also fire onaccountclick. Ignore clicks
|
|
33
|
+
// that originate inside an interactive control so only the card surface
|
|
34
|
+
// itself opens the account.
|
|
35
|
+
const target = event.target as HTMLElement | null;
|
|
36
|
+
if (target?.closest('button, a, input, select, textarea')) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
30
39
|
onaccountclick?.(account);
|
|
31
40
|
}
|
|
32
41
|
|
|
@@ -37,7 +46,7 @@ function handleAccountKeydown(event: KeyboardEvent, account: AccountData) {
|
|
|
37
46
|
|
|
38
47
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
39
48
|
event.preventDefault();
|
|
40
|
-
|
|
49
|
+
onaccountclick?.(account);
|
|
41
50
|
}
|
|
42
51
|
}
|
|
43
52
|
</script>
|
|
@@ -59,7 +68,7 @@ function handleAccountKeydown(event: KeyboardEvent, account: AccountData) {
|
|
|
59
68
|
<div
|
|
60
69
|
role="button"
|
|
61
70
|
tabindex="0"
|
|
62
|
-
onclick={() => handleAccountClick(account)}
|
|
71
|
+
onclick={(event) => handleAccountClick(event, account)}
|
|
63
72
|
onkeydown={(event) => handleAccountKeydown(event, account)}
|
|
64
73
|
>
|
|
65
74
|
<AccountCard {account} {onsync} {onremove} />
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountList.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/AccountList.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAChD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;CAC3C;
|
|
1
|
+
{"version":3,"file":"AccountList.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/AccountList.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAChD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;CAC3C;AA6ED,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -61,6 +61,7 @@ export interface Props {
|
|
|
61
61
|
let channelId = $state('');
|
|
62
62
|
let isDirty = $state(false);
|
|
63
63
|
let isSending = $state(false);
|
|
64
|
+
let sendError = $state<string | null>(null);
|
|
64
65
|
let showCc = $state(false);
|
|
65
66
|
let showBcc = $state(false);
|
|
66
67
|
let appliedInitialState: Partial<ComposeState> | undefined;
|
|
@@ -88,6 +89,7 @@ export interface Props {
|
|
|
88
89
|
channelId = nextState.channelId ?? '';
|
|
89
90
|
isDirty = nextState.isDirty;
|
|
90
91
|
isSending = nextState.isSending;
|
|
92
|
+
sendError = null;
|
|
91
93
|
showCc = ccRecipients.length > 0;
|
|
92
94
|
showBcc = bccRecipients.length > 0;
|
|
93
95
|
});
|
|
@@ -114,11 +116,20 @@ export interface Props {
|
|
|
114
116
|
isDirty = true;
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
function handleSend() {
|
|
119
|
+
async function handleSend() {
|
|
118
120
|
if (isSending) return;
|
|
119
121
|
|
|
120
122
|
isSending = true;
|
|
121
|
-
|
|
123
|
+
sendError = null;
|
|
124
|
+
try {
|
|
125
|
+
// onsend may be sync or async; awaiting handles both a thrown error and a
|
|
126
|
+
// rejected promise so the form never latches in the "Sending…" state.
|
|
127
|
+
await onsend?.(getCurrentState());
|
|
128
|
+
} catch (e) {
|
|
129
|
+
sendError = e instanceof Error ? e.message : String(e);
|
|
130
|
+
} finally {
|
|
131
|
+
isSending = false;
|
|
132
|
+
}
|
|
122
133
|
}
|
|
123
134
|
|
|
124
135
|
function handleSaveDraft() {
|
|
@@ -241,6 +252,12 @@ export interface Props {
|
|
|
241
252
|
/>
|
|
242
253
|
{/if}
|
|
243
254
|
|
|
255
|
+
{#if sendError}
|
|
256
|
+
<div class="send-error" role="alert" aria-live="assertive">
|
|
257
|
+
{sendError}
|
|
258
|
+
</div>
|
|
259
|
+
{/if}
|
|
260
|
+
|
|
244
261
|
<div class="actions">
|
|
245
262
|
<button
|
|
246
263
|
type="submit"
|
|
@@ -342,6 +359,14 @@ export interface Props {
|
|
|
342
359
|
font-weight: var(--smrt-typography-weight-semibold, 600);
|
|
343
360
|
}
|
|
344
361
|
|
|
362
|
+
.send-error {
|
|
363
|
+
padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-3, 12px);
|
|
364
|
+
border-radius: var(--smrt-radius-sm, 8px);
|
|
365
|
+
background: var(--smrt-color-error-container, #ffdad6);
|
|
366
|
+
color: var(--smrt-color-on-error-container, #410002);
|
|
367
|
+
font-size: var(--smrt-typography-body-small-size, 12px);
|
|
368
|
+
}
|
|
369
|
+
|
|
345
370
|
.actions {
|
|
346
371
|
display: flex;
|
|
347
372
|
gap: var(--smrt-spacing-2, 8px);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComposeForm.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ComposeForm.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,WAAW,EAEX,YAAY,EAEb,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,KAAK;IACpB,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IACnC,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"ComposeForm.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ComposeForm.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,WAAW,EAEX,YAAY,EAEb,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,KAAK;IACpB,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IACnC,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAoOD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -34,6 +34,7 @@ let editingId = $state<string | null>(null);
|
|
|
34
34
|
let saving = $state(false);
|
|
35
35
|
let testingId = $state<string | null>(null);
|
|
36
36
|
let testResult = $state<{ success: boolean; error?: string } | null>(null);
|
|
37
|
+
let actionError = $state<string | null>(null);
|
|
37
38
|
|
|
38
39
|
// Form state
|
|
39
40
|
let maName = $state('');
|
|
@@ -65,6 +66,7 @@ function resetForm() {
|
|
|
65
66
|
editingId = null;
|
|
66
67
|
showForm = false;
|
|
67
68
|
testResult = null;
|
|
69
|
+
actionError = null;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
function startEdit(acct: EmailAccountData) {
|
|
@@ -80,16 +82,21 @@ function startEdit(acct: EmailAccountData) {
|
|
|
80
82
|
maSmtpPort = acct.smtpPort ?? 465;
|
|
81
83
|
maSmtpSecurity = acct.smtpSecurity ?? 'ssl';
|
|
82
84
|
maUsername = acct.username ?? '';
|
|
83
|
-
|
|
85
|
+
// Never echo the stored secret into the DOM. Leave the password blank on edit
|
|
86
|
+
// ('(unchanged)' placeholder); save() only writes a new password when one is
|
|
87
|
+
// typed (see the `!editingId || maPassword` guard).
|
|
88
|
+
maPassword = '';
|
|
84
89
|
editingId = acct.id;
|
|
85
90
|
showForm = true;
|
|
86
91
|
testResult = null;
|
|
92
|
+
actionError = null;
|
|
87
93
|
}
|
|
88
94
|
|
|
89
95
|
async function save() {
|
|
90
96
|
if (!maName.trim() || !maEmail.trim() || !onsave) return;
|
|
91
97
|
try {
|
|
92
98
|
saving = true;
|
|
99
|
+
actionError = null;
|
|
93
100
|
const data: Partial<EmailAccountData> = {
|
|
94
101
|
name: maName.trim(),
|
|
95
102
|
email: maEmail.trim(),
|
|
@@ -109,6 +116,7 @@ async function save() {
|
|
|
109
116
|
await onsave(data, editingId ?? undefined);
|
|
110
117
|
resetForm();
|
|
111
118
|
} catch (e) {
|
|
119
|
+
actionError = e instanceof Error ? e.message : String(e);
|
|
112
120
|
} finally {
|
|
113
121
|
saving = false;
|
|
114
122
|
}
|
|
@@ -117,9 +125,12 @@ async function save() {
|
|
|
117
125
|
async function remove(acct: EmailAccountData) {
|
|
118
126
|
if (isReadonly || !ondelete) return;
|
|
119
127
|
try {
|
|
128
|
+
actionError = null;
|
|
120
129
|
await ondelete(acct);
|
|
121
130
|
if (editingId === acct.id) resetForm();
|
|
122
|
-
} catch (e) {
|
|
131
|
+
} catch (e) {
|
|
132
|
+
actionError = e instanceof Error ? e.message : String(e);
|
|
133
|
+
}
|
|
123
134
|
}
|
|
124
135
|
|
|
125
136
|
async function testConnection(acct: EmailAccountData) {
|
|
@@ -258,6 +269,18 @@ function getProviderLabel(type: string): string {
|
|
|
258
269
|
</div>
|
|
259
270
|
{/if}
|
|
260
271
|
|
|
272
|
+
{#if actionError}
|
|
273
|
+
<div class="test-result failure" role="alert" aria-live="assertive">
|
|
274
|
+
{actionError}
|
|
275
|
+
<button
|
|
276
|
+
type="button"
|
|
277
|
+
class="dismiss-btn"
|
|
278
|
+
aria-label={t(M['messages.email_account_manager.dismiss_error'])}
|
|
279
|
+
onclick={() => actionError = null}
|
|
280
|
+
>×</button>
|
|
281
|
+
</div>
|
|
282
|
+
{/if}
|
|
283
|
+
|
|
261
284
|
{#if testResult}
|
|
262
285
|
<div class="test-result" class:success={testResult.success} class:failure={!testResult.success}>
|
|
263
286
|
{#if testResult.success}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmailAccountManager.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/EmailAccountManager.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,gBAAgB,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,EAAE,CACP,OAAO,EAAE,gBAAgB,KACtB,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;
|
|
1
|
+
{"version":3,"file":"EmailAccountManager.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/EmailAccountManager.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,gBAAgB,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,EAAE,CACP,OAAO,EAAE,gBAAgB,KACtB,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;AA+TD,QAAA,MAAM,mBAAmB,2CAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
|
|
@@ -33,6 +33,11 @@ const {
|
|
|
33
33
|
|
|
34
34
|
type ActiveSection = 'whitelist' | 'blacklist';
|
|
35
35
|
let activeSection = $state<ActiveSection>('whitelist');
|
|
36
|
+
let filterError = $state<string | null>(null);
|
|
37
|
+
|
|
38
|
+
function toErrorMessage(e: unknown): string {
|
|
39
|
+
return e instanceof Error ? e.message : String(e);
|
|
40
|
+
}
|
|
36
41
|
|
|
37
42
|
// Whitelist form state
|
|
38
43
|
let showWhitelistForm = $state(false);
|
|
@@ -70,6 +75,7 @@ async function saveWhitelistEntry() {
|
|
|
70
75
|
if (!wlPattern.trim() || !onaddwhitelist) return;
|
|
71
76
|
try {
|
|
72
77
|
savingWhitelist = true;
|
|
78
|
+
filterError = null;
|
|
73
79
|
await onaddwhitelist({
|
|
74
80
|
pattern: wlPattern.trim(),
|
|
75
81
|
type: wlType,
|
|
@@ -78,6 +84,7 @@ async function saveWhitelistEntry() {
|
|
|
78
84
|
});
|
|
79
85
|
resetWhitelistForm();
|
|
80
86
|
} catch (e) {
|
|
87
|
+
filterError = toErrorMessage(e);
|
|
81
88
|
} finally {
|
|
82
89
|
savingWhitelist = false;
|
|
83
90
|
}
|
|
@@ -86,14 +93,18 @@ async function saveWhitelistEntry() {
|
|
|
86
93
|
async function removeWhitelistEntry(entry: WhitelistEntry) {
|
|
87
94
|
if (isReadonly || !onremovewhitelist) return;
|
|
88
95
|
try {
|
|
96
|
+
filterError = null;
|
|
89
97
|
await onremovewhitelist(entry);
|
|
90
|
-
} catch (e) {
|
|
98
|
+
} catch (e) {
|
|
99
|
+
filterError = toErrorMessage(e);
|
|
100
|
+
}
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
async function saveBlacklistEntry() {
|
|
94
104
|
if (!blPattern.trim() || !onaddblacklist) return;
|
|
95
105
|
try {
|
|
96
106
|
savingBlacklist = true;
|
|
107
|
+
filterError = null;
|
|
97
108
|
await onaddblacklist({
|
|
98
109
|
pattern: blPattern.trim(),
|
|
99
110
|
type: blType,
|
|
@@ -102,6 +113,7 @@ async function saveBlacklistEntry() {
|
|
|
102
113
|
});
|
|
103
114
|
resetBlacklistForm();
|
|
104
115
|
} catch (e) {
|
|
116
|
+
filterError = toErrorMessage(e);
|
|
105
117
|
} finally {
|
|
106
118
|
savingBlacklist = false;
|
|
107
119
|
}
|
|
@@ -110,8 +122,11 @@ async function saveBlacklistEntry() {
|
|
|
110
122
|
async function removeBlacklistEntry(entry: BlacklistEntry) {
|
|
111
123
|
if (isReadonly || !onremoveblacklist) return;
|
|
112
124
|
try {
|
|
125
|
+
filterError = null;
|
|
113
126
|
await onremoveblacklist(entry);
|
|
114
|
-
} catch (e) {
|
|
127
|
+
} catch (e) {
|
|
128
|
+
filterError = toErrorMessage(e);
|
|
129
|
+
}
|
|
115
130
|
}
|
|
116
131
|
|
|
117
132
|
function getTypeIcon(type: string): string {
|
|
@@ -161,6 +176,18 @@ function getPatternPlaceholder(type: string): string {
|
|
|
161
176
|
</button>
|
|
162
177
|
</div>
|
|
163
178
|
|
|
179
|
+
{#if filterError}
|
|
180
|
+
<div class="filter-error" role="alert" aria-live="assertive">
|
|
181
|
+
{filterError}
|
|
182
|
+
<button
|
|
183
|
+
type="button"
|
|
184
|
+
class="dismiss-btn"
|
|
185
|
+
aria-label={t(M['messages.email_filter_manager.dismiss_error'])}
|
|
186
|
+
onclick={() => filterError = null}
|
|
187
|
+
>×</button>
|
|
188
|
+
</div>
|
|
189
|
+
{/if}
|
|
190
|
+
|
|
164
191
|
<!-- Whitelist Section -->
|
|
165
192
|
{#if activeSection === 'whitelist'}
|
|
166
193
|
<div class="section-content">
|
|
@@ -684,4 +711,25 @@ function getPatternPlaceholder(type: string): string {
|
|
|
684
711
|
background: var(--smrt-color-surface-container, #f0f1f9);
|
|
685
712
|
border-radius: var(--smrt-radius-md, 8px);
|
|
686
713
|
}
|
|
714
|
+
|
|
715
|
+
.filter-error {
|
|
716
|
+
display: flex;
|
|
717
|
+
align-items: center;
|
|
718
|
+
justify-content: space-between;
|
|
719
|
+
gap: 0.5rem;
|
|
720
|
+
padding: 0.5rem 0.75rem;
|
|
721
|
+
border-radius: var(--smrt-radius-md, 8px);
|
|
722
|
+
background: var(--smrt-color-error-container, #fce4ec);
|
|
723
|
+
color: var(--smrt-color-error, #ba1a1a);
|
|
724
|
+
font-size: var(--smrt-typography-body-medium-size, 0.8125rem);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
.dismiss-btn {
|
|
728
|
+
background: transparent;
|
|
729
|
+
border: none;
|
|
730
|
+
font-size: var(--smrt-typography-body-large-size, 1rem);
|
|
731
|
+
cursor: pointer;
|
|
732
|
+
color: inherit;
|
|
733
|
+
padding: 0 0.25rem;
|
|
734
|
+
}
|
|
687
735
|
</style>
|
|
@@ -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;
|
|
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"}
|
|
@@ -20,6 +20,7 @@ export interface Props {
|
|
|
20
20
|
let to: RecipientEntry[] = $state([]);
|
|
21
21
|
let body = $state('');
|
|
22
22
|
let isSending = $state(false);
|
|
23
|
+
let sendError = $state<string | null>(null);
|
|
23
24
|
|
|
24
25
|
const forwardedBlock = $derived.by(() => {
|
|
25
26
|
const dateStr = originalMessage.date
|
|
@@ -41,10 +42,19 @@ export interface Props {
|
|
|
41
42
|
].join('\n');
|
|
42
43
|
});
|
|
43
44
|
|
|
44
|
-
function handleSend() {
|
|
45
|
+
async function handleSend() {
|
|
45
46
|
if (isSending || to.length === 0) return;
|
|
46
47
|
isSending = true;
|
|
47
|
-
|
|
48
|
+
sendError = null;
|
|
49
|
+
try {
|
|
50
|
+
// onsend may be sync or async; awaiting handles both so the button never
|
|
51
|
+
// latches in the "Sending…" state on failure.
|
|
52
|
+
await onsend?.(to, body);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
sendError = e instanceof Error ? e.message : String(e);
|
|
55
|
+
} finally {
|
|
56
|
+
isSending = false;
|
|
57
|
+
}
|
|
48
58
|
}
|
|
49
59
|
</script>
|
|
50
60
|
|
|
@@ -68,6 +78,12 @@ export interface Props {
|
|
|
68
78
|
<pre class="forwarded-text">{forwardedBlock}</pre>
|
|
69
79
|
</div>
|
|
70
80
|
|
|
81
|
+
{#if sendError}
|
|
82
|
+
<div class="send-error" role="alert" aria-live="assertive">
|
|
83
|
+
{sendError}
|
|
84
|
+
</div>
|
|
85
|
+
{/if}
|
|
86
|
+
|
|
71
87
|
<div class="actions">
|
|
72
88
|
<button
|
|
73
89
|
type="button"
|
|
@@ -133,6 +149,14 @@ export interface Props {
|
|
|
133
149
|
font-family: var(--smrt-font-family, system-ui);
|
|
134
150
|
}
|
|
135
151
|
|
|
152
|
+
.send-error {
|
|
153
|
+
padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-3, 12px);
|
|
154
|
+
border-radius: var(--smrt-radius-sm, 8px);
|
|
155
|
+
background: var(--smrt-color-error-container, #ffdad6);
|
|
156
|
+
color: var(--smrt-color-on-error-container, #410002);
|
|
157
|
+
font-size: var(--smrt-typography-body-small-size, 12px);
|
|
158
|
+
}
|
|
159
|
+
|
|
136
160
|
.actions {
|
|
137
161
|
display: flex;
|
|
138
162
|
gap: var(--smrt-spacing-2, 8px);
|
|
@@ -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;
|
|
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"}
|
|
@@ -69,15 +69,16 @@ const _slackMeta = $derived.by(() => {
|
|
|
69
69
|
return message.meta || {};
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.replace(/<
|
|
80
|
-
|
|
72
|
+
// Body is always rendered as untrusted plain text — we never inject provider
|
|
73
|
+
// HTML via {@html}. A regex "sanitizer" is bypassable (e.g. an unclosed
|
|
74
|
+
// <script> survives), so when the caller opts into the HTML body we down-render
|
|
75
|
+
// it to text by stripping tags. Real rich-HTML rendering, if ever needed, must
|
|
76
|
+
// go through a vetted sanitizer or a sandboxed iframe (out of scope here).
|
|
77
|
+
const _displayBody = $derived.by(() => {
|
|
78
|
+
if (showHtml && message.htmlBody) {
|
|
79
|
+
return message.htmlBody.replace(/<[^>]*>/g, '');
|
|
80
|
+
}
|
|
81
|
+
return message.body;
|
|
81
82
|
});
|
|
82
83
|
</script>
|
|
83
84
|
|
|
@@ -144,11 +145,7 @@ const _sanitizedHtml = $derived.by(() => {
|
|
|
144
145
|
{/if}
|
|
145
146
|
|
|
146
147
|
<div class="body">
|
|
147
|
-
{
|
|
148
|
-
{@html _sanitizedHtml}
|
|
149
|
-
{:else}
|
|
150
|
-
<pre class="body-text">{message.body}</pre>
|
|
151
|
-
{/if}
|
|
148
|
+
<pre class="body-text">{_displayBody}</pre>
|
|
152
149
|
</div>
|
|
153
150
|
|
|
154
151
|
{#if attachments.length > 0}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageDetail.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/MessageDetail.svelte.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM5E,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;IAC/B,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACzC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAC3C,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAC1C,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC,CAAC;CAClD;
|
|
1
|
+
{"version":3,"file":"MessageDetail.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/MessageDetail.svelte.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM5E,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;IAC/B,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACzC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAC3C,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAC1C,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC,CAAC;CAClD;AA4KD,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -24,6 +24,7 @@ export interface Props {
|
|
|
24
24
|
|
|
25
25
|
let body = $state('');
|
|
26
26
|
let isSending = $state(false);
|
|
27
|
+
let sendError = $state<string | null>(null);
|
|
27
28
|
|
|
28
29
|
const quotedBody = $derived.by(() => {
|
|
29
30
|
const dateStr = originalMessage.date
|
|
@@ -37,10 +38,19 @@ export interface Props {
|
|
|
37
38
|
return `On ${dateStr}, ${from} wrote:\n${lines}`;
|
|
38
39
|
});
|
|
39
40
|
|
|
40
|
-
function handleSend() {
|
|
41
|
+
async function handleSend() {
|
|
41
42
|
if (isSending) return;
|
|
42
43
|
isSending = true;
|
|
43
|
-
|
|
44
|
+
sendError = null;
|
|
45
|
+
try {
|
|
46
|
+
// onsend may be sync or async; awaiting handles both so the button never
|
|
47
|
+
// latches in the "Sending…" state on failure.
|
|
48
|
+
await onsend?.(body);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
sendError = e instanceof Error ? e.message : String(e);
|
|
51
|
+
} finally {
|
|
52
|
+
isSending = false;
|
|
53
|
+
}
|
|
44
54
|
}
|
|
45
55
|
</script>
|
|
46
56
|
|
|
@@ -60,6 +70,12 @@ export interface Props {
|
|
|
60
70
|
<pre class="quoted-text">{quotedBody}</pre>
|
|
61
71
|
</div>
|
|
62
72
|
|
|
73
|
+
{#if sendError}
|
|
74
|
+
<div class="send-error" role="alert" aria-live="assertive">
|
|
75
|
+
{sendError}
|
|
76
|
+
</div>
|
|
77
|
+
{/if}
|
|
78
|
+
|
|
63
79
|
<div class="actions">
|
|
64
80
|
<button
|
|
65
81
|
type="button"
|
|
@@ -126,6 +142,14 @@ export interface Props {
|
|
|
126
142
|
font-family: var(--smrt-font-family, system-ui);
|
|
127
143
|
}
|
|
128
144
|
|
|
145
|
+
.send-error {
|
|
146
|
+
padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-3, 12px);
|
|
147
|
+
border-radius: var(--smrt-radius-sm, 8px);
|
|
148
|
+
background: var(--smrt-color-error-container, #ffdad6);
|
|
149
|
+
color: var(--smrt-color-on-error-container, #410002);
|
|
150
|
+
font-size: var(--smrt-typography-body-small-size, 12px);
|
|
151
|
+
}
|
|
152
|
+
|
|
129
153
|
.actions {
|
|
130
154
|
display: flex;
|
|
131
155
|
gap: var(--smrt-spacing-2, 8px);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReplyForm.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ReplyForm.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,WAAW,KAAK;IACpB,eAAe,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;
|
|
1
|
+
{"version":3,"file":"ReplyForm.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ReplyForm.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,WAAW,KAAK;IACpB,eAAe,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAoFD,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|
|
@@ -17,7 +17,7 @@ export interface Props {
|
|
|
17
17
|
case 'sending':
|
|
18
18
|
return { label: 'Sending', color: 'var(--smrt-color-primary, #6750a4)', icon: '↑' };
|
|
19
19
|
case 'sent':
|
|
20
|
-
return { label: 'Sent', color: 'var(--smrt-color-
|
|
20
|
+
return { label: 'Sent', color: 'var(--smrt-color-success, #006d3b)', icon: '✓' };
|
|
21
21
|
case 'failed':
|
|
22
22
|
return { label: 'Failed', color: 'var(--smrt-color-error, #ba1a1a)', icon: '✕' };
|
|
23
23
|
case 'scheduled':
|
|
@@ -46,7 +46,14 @@ $effect(() => {
|
|
|
46
46
|
collapsed = new Set(initialCollapsed);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
function handleMessageClick(message: MessageData) {
|
|
49
|
+
function handleMessageClick(event: MouseEvent, message: MessageData) {
|
|
50
|
+
// MessageDetail renders its own Reply/Forward/Delete buttons inside this
|
|
51
|
+
// role="button" wrapper. Ignore clicks that originate inside an interactive
|
|
52
|
+
// control so only the message surface itself fires onmessageclick.
|
|
53
|
+
const target = event.target as HTMLElement | null;
|
|
54
|
+
if (target?.closest('button, a, input, select, textarea')) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
50
57
|
onmessageclick?.(message);
|
|
51
58
|
}
|
|
52
59
|
|
|
@@ -57,7 +64,7 @@ function handleMessageKeydown(event: KeyboardEvent, message: MessageData) {
|
|
|
57
64
|
|
|
58
65
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
59
66
|
event.preventDefault();
|
|
60
|
-
|
|
67
|
+
onmessageclick?.(message);
|
|
61
68
|
}
|
|
62
69
|
}
|
|
63
70
|
|
|
@@ -128,7 +135,7 @@ const _sortedMessages = $derived(
|
|
|
128
135
|
<div
|
|
129
136
|
role="button"
|
|
130
137
|
tabindex="0"
|
|
131
|
-
onclick={() => handleMessageClick(message)}
|
|
138
|
+
onclick={(event) => handleMessageClick(event, message)}
|
|
132
139
|
onkeydown={(event) => handleMessageKeydown(event, message)}
|
|
133
140
|
>
|
|
134
141
|
<MessageDetail
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThreadView.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ThreadView.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;CAC1C;
|
|
1
|
+
{"version":3,"file":"ThreadView.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ThreadView.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;CAC1C;AAqID,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
package/dist/svelte/i18n.d.ts
CHANGED
|
@@ -24,12 +24,14 @@ export declare const M: {
|
|
|
24
24
|
readonly 'messages.email_account_manager.imap_host_title': "messages.email_account_manager.imap_host_title";
|
|
25
25
|
readonly 'messages.email_account_manager.test_connection': "messages.email_account_manager.test_connection";
|
|
26
26
|
readonly 'messages.email_account_manager.remove': "messages.email_account_manager.remove";
|
|
27
|
+
readonly 'messages.email_account_manager.dismiss_error': "messages.email_account_manager.dismiss_error";
|
|
27
28
|
readonly 'messages.email_filter_manager.whitelist_description': "messages.email_filter_manager.whitelist_description";
|
|
28
29
|
readonly 'messages.email_filter_manager.add_whitelist_entry': "messages.email_filter_manager.add_whitelist_entry";
|
|
29
30
|
readonly 'messages.email_filter_manager.category_placeholder': "messages.email_filter_manager.category_placeholder";
|
|
30
31
|
readonly 'messages.email_filter_manager.whitelist_description_placeholder': "messages.email_filter_manager.whitelist_description_placeholder";
|
|
31
32
|
readonly 'messages.email_filter_manager.no_whitelist_entries': "messages.email_filter_manager.no_whitelist_entries";
|
|
32
33
|
readonly 'messages.email_filter_manager.whitelist_remove': "messages.email_filter_manager.whitelist_remove";
|
|
34
|
+
readonly 'messages.email_filter_manager.dismiss_error': "messages.email_filter_manager.dismiss_error";
|
|
33
35
|
readonly 'messages.email_filter_manager.blacklist_description': "messages.email_filter_manager.blacklist_description";
|
|
34
36
|
readonly 'messages.email_filter_manager.add_blacklist_entry': "messages.email_filter_manager.add_blacklist_entry";
|
|
35
37
|
readonly 'messages.email_filter_manager.reason_placeholder': "messages.email_filter_manager.reason_placeholder";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/svelte/i18n.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,CAAC
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/svelte/i18n.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgEZ,CAAC"}
|