mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-07-08 22:12:07 +08:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
565bb839db | ||
|
|
dbd1f8706d | ||
|
|
3f1d800e90 | ||
|
|
70b30c2494 | ||
|
|
1a1dd720c8 | ||
|
|
2d501d82cf | ||
|
|
7c57592742 | ||
|
|
41105ed803 | ||
|
|
c924b71a5c | ||
|
|
4f0b44de2e | ||
|
|
f6fcbe793c |
@@ -28,6 +28,7 @@ Upgrade the version number of the cloudflare_temp_email project.
|
|||||||
4. Update the `VERSION` constant in `worker/src/constants.ts`.
|
4. Update the `VERSION` constant in `worker/src/constants.ts`.
|
||||||
5. Insert a new version placeholder at the top of `CHANGELOG.md`.
|
5. Insert a new version placeholder at the top of `CHANGELOG.md`.
|
||||||
6. Insert a new version placeholder at the top of `CHANGELOG_EN.md`.
|
6. Insert a new version placeholder at the top of `CHANGELOG_EN.md`.
|
||||||
|
7. Rename the previous version heading in both changelogs from `## v{OLD_VERSION}(main)` to `## v{OLD_VERSION}` so only the new active development version keeps `(main)`.
|
||||||
|
|
||||||
## CHANGELOG format
|
## CHANGELOG format
|
||||||
|
|
||||||
@@ -44,7 +45,14 @@ In `CHANGELOG.md`, insert before the existing `## v{OLD_VERSION}(main)` line (i.
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`CHANGELOG_EN.md` uses the same format.
|
After inserting the new placeholder, update the old heading:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
-## v{OLD_VERSION}(main)
|
||||||
|
+## v{OLD_VERSION}
|
||||||
|
```
|
||||||
|
|
||||||
|
`CHANGELOG_EN.md` uses the same format and must receive the same old-heading update.
|
||||||
|
|
||||||
## Commit message format
|
## Commit message format
|
||||||
|
|
||||||
@@ -53,4 +61,5 @@ feat: upgrade version to v{VERSION}
|
|||||||
|
|
||||||
- Update version number to {VERSION} in all package.json files
|
- Update version number to {VERSION} in all package.json files
|
||||||
- Add v{VERSION} placeholder in CHANGELOG.md
|
- Add v{VERSION} placeholder in CHANGELOG.md
|
||||||
|
- Move (main) marker from v{OLD_VERSION} to v{VERSION}
|
||||||
```
|
```
|
||||||
|
|||||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -6,7 +6,24 @@
|
|||||||
<a href="CHANGELOG_EN.md">English</a>
|
<a href="CHANGELOG_EN.md">English</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## v1.9.0(main)
|
## v1.10.0(main)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- feat: |Frontend| 邮箱新增「邮箱全宽列表视图」开关(在外观设置中控制),开启后默认全宽列表展示邮件标题与正文预览,点击单封邮件再展开为双栏,再次点击同一封邮件可回到列表视图;多选模式下点击邮件会同步切换勾选状态与右侧预览,并禁用同邮件点击收回列表,展开时双栏左侧列表宽度仍遵循「邮箱双栏视图左侧列表宽度占比」配置;默认关闭,保留原有双栏行为
|
||||||
|
- feat: |Frontend| 邮箱全宽列表视图新增「正文预览行数」配置(在外观设置中控制),可设置邮件正文预览的最大行数,默认 2 行,0 表示关闭预览
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- fix: |AI 提取| 强化提示词,要求 AI 保持邮件原始链接域名,避免小模型改写验证链接域名导致错误跳转(issue #1072)
|
||||||
|
- fix: |AI 提取| HTML-only 邮件在发送给 Workers AI 前会先压缩为可读文本,避免样式模板过长导致验证码位于 4000 字截断之后而无法识别
|
||||||
|
- fix: |Frontend| 移动端 Header 增加页头内边距,避免标题、菜单按钮与屏幕边缘过近
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
- feat: |Frontend| 「邮箱双栏视图左侧列表宽度占比」最小值由 0.25 放宽至 0,左侧列表可完全折叠使正文近乎全屏,刻度增加 0 点;收件箱与发件箱的双栏拆分同步生效,并优化外观设置文案以明确该比例控制左侧邮件列表宽度
|
||||||
|
|
||||||
|
## v1.9.0
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,24 @@
|
|||||||
<a href="CHANGELOG_EN.md">English</a>
|
<a href="CHANGELOG_EN.md">English</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## v1.9.0(main)
|
## v1.10.0(main)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- feat: |Frontend| Add a "Full-width mailbox list view" toggle in Appearance settings. When enabled, the mailbox shows a full-width list of subjects and body previews by default; clicking a mail expands it into the two-pane split view, clicking the same mail again returns to the list view; in multi-select mode, clicking a mail updates both its checked state and the right-side preview while disabling same-mail collapse, and the split width still follows the "Left list width in two-column mailbox view" setting. Defaults to off, preserving the original two-pane behavior
|
||||||
|
- feat: |Frontend| Add "Body Preview Lines" in Appearance settings for the full-width mailbox list view, allowing runtime control over the body-preview clamp. It defaults to 2 lines, and 0 disables previews
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- fix: |AI Extract| Strengthen the prompt to keep original link domains from the email, preventing small models from rewriting verification-link domains (issue #1072)
|
||||||
|
- fix: |AI Extract| Convert HTML-only mail bodies into compact readable text before sending them to Workers AI, preventing long templates from pushing verification codes past the 4000-character truncation window
|
||||||
|
- fix: |Frontend| Add mobile Header page padding so the title and menu button no longer sit too close to the screen edge
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
- feat: |Frontend| Lower the "Left list width in two-column mailbox view" minimum from 0.25 to 0 so the left list pane can fully collapse for a near-fullscreen content view, with a 0 mark added; applies to both the inbox and send-box two-pane splits, and clarifies the Appearance setting label so it is clear the value controls the left mail list width
|
||||||
|
|
||||||
|
## v1.9.0
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
|||||||
76
e2e/package-lock.json
generated
76
e2e/package-lock.json
generated
@@ -6,8 +6,8 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "cloudflare-temp-email-e2e",
|
"name": "cloudflare-temp-email-e2e",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"imapflow": "^1.3.1",
|
"imapflow": "^1.4.2",
|
||||||
"nodemailer": "^8.0.5"
|
"nodemailer": "^9.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "1.58.2",
|
"@playwright/test": "1.58.2",
|
||||||
@@ -69,13 +69,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@zone-eu/mailsplit": {
|
"node_modules/@zone-eu/mailsplit": {
|
||||||
"version": "5.4.8",
|
"version": "5.4.12",
|
||||||
"resolved": "https://registry.npmjs.org/@zone-eu/mailsplit/-/mailsplit-5.4.8.tgz",
|
"resolved": "https://registry.npmjs.org/@zone-eu/mailsplit/-/mailsplit-5.4.12.tgz",
|
||||||
"integrity": "sha512-eEyACj4JZ7sjzRvy26QhLgKEMWwQbsw1+QZnlLX+/gihcNH07lVPOcnwf5U6UAL7gkc//J3jVd76o/WS+taUiA==",
|
"integrity": "sha512-w7Gy+NvjZ0MiXm8F6zfjImAqcTONKDImgWVBjDKQVFUXWuz3VFM5levNArkL2M877ajql5+bkS2pDV56injlmg==",
|
||||||
"license": "(MIT OR EUPL-1.1+)",
|
"license": "(MIT OR EUPL-1.1+)",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"libbase64": "1.3.0",
|
"libbase64": "1.3.0",
|
||||||
"libmime": "5.3.7",
|
"libmime": "5.3.8",
|
||||||
"libqp": "2.1.1"
|
"libqp": "2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -129,38 +129,26 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/imapflow": {
|
"node_modules/imapflow": {
|
||||||
"version": "1.3.1",
|
"version": "1.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/imapflow/-/imapflow-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/imapflow/-/imapflow-1.4.2.tgz",
|
||||||
"integrity": "sha512-DKwpMDR1EWXpV5T7adqQAccN7n684AX3poEZ5F3YoPlm2MyGeKavpRgNr3qptdEQaK+x5SlZ9jigT+cMs4geBA==",
|
"integrity": "sha512-73CGfb5+W0FkZ5CY4GfSdsoXyQ+17wdKkpMN2vwJHdLtOOFQWxv0ilG7KYY79XHBYg5njjqxXYB2FPw5Tl81zQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@zone-eu/mailsplit": "5.4.8",
|
"@zone-eu/mailsplit": "5.4.12",
|
||||||
"encoding-japanese": "2.2.0",
|
"encoding-japanese": "2.2.0",
|
||||||
"iconv-lite": "0.7.2",
|
"iconv-lite": "0.7.2",
|
||||||
"libbase64": "1.3.0",
|
"libbase64": "1.3.0",
|
||||||
"libmime": "5.3.8",
|
"libmime": "5.3.8",
|
||||||
"libqp": "2.1.1",
|
"libqp": "2.1.1",
|
||||||
"nodemailer": "8.0.5",
|
"nodemailer": "9.0.1",
|
||||||
"pino": "10.3.1",
|
"pino": "10.3.1",
|
||||||
"socks": "2.8.7"
|
"socks": "2.8.9"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/imapflow/node_modules/libmime": {
|
|
||||||
"version": "5.3.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.8.tgz",
|
|
||||||
"integrity": "sha512-ZrCY+Q66mPvasAfjsQ/IgahzoBvfE1VdtGRpo1hwRB1oK3wJKxhKA3GOcd2a6j7AH5eMFccxK9fBoCpRZTf8ng==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"encoding-japanese": "2.2.0",
|
|
||||||
"iconv-lite": "0.7.2",
|
|
||||||
"libbase64": "1.3.0",
|
|
||||||
"libqp": "2.1.1"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ip-address": {
|
"node_modules/ip-address": {
|
||||||
"version": "10.1.0",
|
"version": "10.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
|
||||||
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
|
"integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12"
|
"node": ">= 12"
|
||||||
@@ -173,29 +161,17 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/libmime": {
|
"node_modules/libmime": {
|
||||||
"version": "5.3.7",
|
"version": "5.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.8.tgz",
|
||||||
"integrity": "sha512-FlDb3Wtha8P01kTL3P9M+ZDNDWPKPmKHWaU/cG/lg5pfuAwdflVpZE+wm9m7pKmC5ww6s+zTxBKS1p6yl3KpSw==",
|
"integrity": "sha512-ZrCY+Q66mPvasAfjsQ/IgahzoBvfE1VdtGRpo1hwRB1oK3wJKxhKA3GOcd2a6j7AH5eMFccxK9fBoCpRZTf8ng==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"encoding-japanese": "2.2.0",
|
"encoding-japanese": "2.2.0",
|
||||||
"iconv-lite": "0.6.3",
|
"iconv-lite": "0.7.2",
|
||||||
"libbase64": "1.3.0",
|
"libbase64": "1.3.0",
|
||||||
"libqp": "2.1.1"
|
"libqp": "2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/libmime/node_modules/iconv-lite": {
|
|
||||||
"version": "0.6.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
|
||||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/libqp": {
|
"node_modules/libqp": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.1.tgz",
|
||||||
@@ -203,9 +179,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/nodemailer": {
|
"node_modules/nodemailer": {
|
||||||
"version": "8.0.5",
|
"version": "9.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-9.0.1.tgz",
|
||||||
"integrity": "sha512-0PF8Yb1yZuQfQbq+5/pZJrtF6WQcjTd5/S4JOHs9PGFxuTqoB/icwuB44pOdURHJbRKX1PPoJZtY7R4VUoCC8w==",
|
"integrity": "sha512-Gwv8SQewT616ZM/URn0H54b8PWo/Wum7md3EW2aWy1lO27+WZCX+Xyak3J+NlmHUjDh5ME+uesJUDRbR3Ye8Bw==",
|
||||||
"license": "MIT-0",
|
"license": "MIT-0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
@@ -346,12 +322,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/socks": {
|
"node_modules/socks": {
|
||||||
"version": "2.8.7",
|
"version": "2.8.9",
|
||||||
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
|
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz",
|
||||||
"integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
|
"integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ip-address": "^10.0.1",
|
"ip-address": "^10.1.1",
|
||||||
"smart-buffer": "^4.2.0"
|
"smart-buffer": "^4.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"imapflow": "^1.3.1",
|
"imapflow": "^1.4.2",
|
||||||
"nodemailer": "^8.0.5"
|
"nodemailer": "^9.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cloudflare_temp_email",
|
"name": "cloudflare_temp_email",
|
||||||
"version": "1.9.0",
|
"version": "1.10.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -28,16 +28,16 @@
|
|||||||
"@vueuse/core": "^14.3.0",
|
"@vueuse/core": "^14.3.0",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
"axios": "^1.16.1",
|
"axios": "^1.18.1",
|
||||||
"dompurify": "^3.4.7",
|
"dompurify": "^3.4.11",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"mail-parser-wasm": "^0.2.2",
|
"mail-parser-wasm": "^0.2.2",
|
||||||
"naive-ui": "^2.44.1",
|
"naive-ui": "^2.44.1",
|
||||||
"postal-mime": "^2.7.4",
|
"postal-mime": "^2.7.4",
|
||||||
"vooks": "^0.2.12",
|
"vooks": "^0.2.12",
|
||||||
"vue": "^3.5.35",
|
"vue": "^3.5.38",
|
||||||
"vue-clipboard3": "^2.0.0",
|
"vue-clipboard3": "^2.0.0",
|
||||||
"vue-i18n": "^11.4.4",
|
"vue-i18n": "^11.4.6",
|
||||||
"vue-router": "^4.6.4"
|
"vue-router": "^4.6.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -50,10 +50,10 @@
|
|||||||
"vite": "^7.3.5",
|
"vite": "^7.3.5",
|
||||||
"vite-plugin-pwa": "^1.3.0",
|
"vite-plugin-pwa": "^1.3.0",
|
||||||
"vite-plugin-wasm": "^3.6.0",
|
"vite-plugin-wasm": "^3.6.0",
|
||||||
"vitest": "^3.2.6",
|
"vitest": "^4.1.9",
|
||||||
"workbox-build": "^7.4.1",
|
"workbox-build": "^7.4.1",
|
||||||
"workbox-window": "^7.4.1",
|
"workbox-window": "^7.4.1",
|
||||||
"wrangler": "^4.96.0"
|
"wrangler": "^4.104.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
||||||
}
|
}
|
||||||
|
|||||||
1500
frontend/pnpm-lock.yaml
generated
1500
frontend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -60,7 +60,7 @@ const props = defineProps({
|
|||||||
const localFilterKeyword = ref('')
|
const localFilterKeyword = ref('')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isDark, mailboxSplitSize, indexTab, loading, useUTCDate,
|
isDark, mailboxSplitSize, mailListView, mailListPreviewLineClamp, indexTab, loading, useUTCDate,
|
||||||
autoRefresh, configAutoRefreshInterval, sendMailModel
|
autoRefresh, configAutoRefreshInterval, sendMailModel
|
||||||
} = useGlobalState()
|
} = useGlobalState()
|
||||||
const autoRefreshInterval = ref(configAutoRefreshInterval.value)
|
const autoRefreshInterval = ref(configAutoRefreshInterval.value)
|
||||||
@@ -71,6 +71,12 @@ const count = ref(0)
|
|||||||
const page = ref(1)
|
const page = ref(1)
|
||||||
const pageSize = ref(20)
|
const pageSize = ref(20)
|
||||||
|
|
||||||
|
const mailListPreviewLineClampValue = computed(() => {
|
||||||
|
const value = Number(mailListPreviewLineClamp.value)
|
||||||
|
if (!Number.isFinite(value)) return 0
|
||||||
|
return Math.min(5, Math.max(0, Math.round(value)))
|
||||||
|
})
|
||||||
|
|
||||||
// Computed property for filtered data (only filter current page)
|
// Computed property for filtered data (only filter current page)
|
||||||
const data = computed(() => {
|
const data = computed(() => {
|
||||||
if (!localFilterKeyword.value || localFilterKeyword.value.trim() === '') {
|
if (!localFilterKeyword.value || localFilterKeyword.value.trim() === '') {
|
||||||
@@ -183,7 +189,7 @@ const refresh = async () => {
|
|||||||
count.value = totalCount;
|
count.value = totalCount;
|
||||||
}
|
}
|
||||||
curMail.value = null;
|
curMail.value = null;
|
||||||
if (!isMobile.value && data.value.length > 0) {
|
if (!isMobile.value && !mailListView.value && data.value.length > 0) {
|
||||||
curMail.value = data.value[0];
|
curMail.value = data.value[0];
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -202,6 +208,11 @@ const backFirstPageAndRefresh = async () => {
|
|||||||
const clickRow = async (row) => {
|
const clickRow = async (row) => {
|
||||||
if (multiActionMode.value) {
|
if (multiActionMode.value) {
|
||||||
row.checked = !row.checked;
|
row.checked = !row.checked;
|
||||||
|
curMail.value = row;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mailListView.value && curMail.value?.id === row.id) {
|
||||||
|
curMail.value = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
curMail.value = row;
|
curMail.value = row;
|
||||||
@@ -375,8 +386,13 @@ onBeforeUnmount(() => {
|
|||||||
clearable />
|
clearable />
|
||||||
</n-space>
|
</n-space>
|
||||||
</div>
|
</div>
|
||||||
<n-split class="left" direction="horizontal" :max="0.75" :min="0.25" :default-size="mailboxSplitSize"
|
<n-split class="left" direction="horizontal" :max="0.75" :min="0" :resize-trigger-size="8"
|
||||||
:on-update:size="onSpiltSizeChange">
|
:default-size="mailboxSplitSize" :on-update:size="onSpiltSizeChange" v-if="!mailListView || curMail">
|
||||||
|
<template #resize-trigger>
|
||||||
|
<div class="split-handle">
|
||||||
|
<div class="split-handle__grip" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<template #1>
|
<template #1>
|
||||||
<div style="overflow: auto; min-height: 60vh; max-height: 100vh;">
|
<div style="overflow: auto; min-height: 60vh; max-height: 100vh;">
|
||||||
<n-list hoverable clickable>
|
<n-list hoverable clickable>
|
||||||
@@ -412,15 +428,25 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
<template #2>
|
<template #2>
|
||||||
<div v-if="curMail" style="margin: 8px;">
|
<div v-if="curMail" style="margin: 8px;">
|
||||||
<n-flex justify="space-between">
|
<n-flex justify="space-between" align="center">
|
||||||
<n-button @click="prevMail" :disabled="!canGoPrevMail" text size="small">
|
<n-space :wrap="false" align="center">
|
||||||
<template #icon>
|
<n-button v-if="mailListView" @click="curMail = null" text size="small">
|
||||||
<n-icon>
|
<template #icon>
|
||||||
<ArrowBackIosNewFilled />
|
<n-icon>
|
||||||
</n-icon>
|
<ArrowBackIosNewFilled />
|
||||||
</template>
|
</n-icon>
|
||||||
{{ t('prevMail') }}
|
</template>
|
||||||
</n-button>
|
{{ t('backToList') }}
|
||||||
|
</n-button>
|
||||||
|
<n-button @click="prevMail" :disabled="!canGoPrevMail" text size="small">
|
||||||
|
<template #icon>
|
||||||
|
<n-icon>
|
||||||
|
<ArrowBackIosNewFilled />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
{{ t('prevMail') }}
|
||||||
|
</n-button>
|
||||||
|
</n-space>
|
||||||
<n-button @click="nextMail" :disabled="!canGoNextMail" text size="small" icon-placement="right">
|
<n-button @click="nextMail" :disabled="!canGoNextMail" text size="small" icon-placement="right">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon>
|
<n-icon>
|
||||||
@@ -446,6 +472,48 @@ onBeforeUnmount(() => {
|
|||||||
</n-card>
|
</n-card>
|
||||||
</template>
|
</template>
|
||||||
</n-split>
|
</n-split>
|
||||||
|
<div v-else class="mail-list-scroll">
|
||||||
|
<n-list hoverable clickable>
|
||||||
|
<n-list-item v-for="row in data" v-bind:key="row.id" @click="() => clickRow(row)"
|
||||||
|
:class="mailItemClass(row)">
|
||||||
|
<template #prefix v-if="multiActionMode">
|
||||||
|
<n-checkbox v-model:checked="row.checked" />
|
||||||
|
</template>
|
||||||
|
<n-thing class="mail-list-thing">
|
||||||
|
<template #header>
|
||||||
|
<n-ellipsis class="mail-list-title">
|
||||||
|
{{ row.subject }}
|
||||||
|
</n-ellipsis>
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
<div class="mail-list-meta">
|
||||||
|
<n-tag type="info">
|
||||||
|
ID: {{ row.id }}
|
||||||
|
</n-tag>
|
||||||
|
<n-tag type="info">
|
||||||
|
{{ utcToLocalDate(row.created_at, useUTCDate) }}
|
||||||
|
</n-tag>
|
||||||
|
<n-tag type="info">
|
||||||
|
<n-ellipsis class="mail-list-meta-text">
|
||||||
|
{{ showEMailTo ? "FROM: " + row.source : row.source }}
|
||||||
|
</n-ellipsis>
|
||||||
|
</n-tag>
|
||||||
|
<n-tag v-if="showEMailTo" type="info">
|
||||||
|
<n-ellipsis class="mail-list-meta-text">
|
||||||
|
TO: {{ row.address }}
|
||||||
|
</n-ellipsis>
|
||||||
|
</n-tag>
|
||||||
|
<AiExtractInfo :metadata="row.metadata" compact />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<n-ellipsis v-if="row.text && mailListPreviewLineClampValue > 0"
|
||||||
|
:line-clamp="mailListPreviewLineClampValue" class="mail-list-preview" :tooltip="false">
|
||||||
|
{{ row.text }}
|
||||||
|
</n-ellipsis>
|
||||||
|
</n-thing>
|
||||||
|
</n-list-item>
|
||||||
|
</n-list>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="left" v-else>
|
<div class="left" v-else>
|
||||||
<n-space justify="space-around" align="center" :wrap="false" style="display: flex; align-items: center;">
|
<n-space justify="space-around" align="center" :wrap="false" style="display: flex; align-items: center;">
|
||||||
@@ -555,8 +623,80 @@ onBeforeUnmount(() => {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mail-list-scroll {
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
min-height: 60vh;
|
||||||
|
max-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mail-list-thing,
|
||||||
|
.mail-list-title,
|
||||||
|
.mail-list-preview {
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mail-list-thing,
|
||||||
|
.mail-list-preview {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mail-list-thing :deep(.n-thing-main),
|
||||||
|
.mail-list-thing :deep(.n-thing-header),
|
||||||
|
.mail-list-thing :deep(.n-thing-header__title),
|
||||||
|
.mail-list-thing :deep(.n-thing-main__description),
|
||||||
|
.mail-list-thing :deep(.n-thing-main__content) {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mail-list-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mail-list-meta :deep(.n-tag) {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mail-list-meta-text {
|
||||||
|
max-width: min(240px, 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mail-list-preview {
|
||||||
|
display: -webkit-box;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mail-list-scroll :deep(.n-list-item__main) {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.split-handle {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-handle__grip {
|
||||||
|
width: 4px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: var(--n-resize-trigger-color);
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-handle:hover .split-handle__grip {
|
||||||
|
background-color: var(--n-resize-trigger-color-hover);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -210,8 +210,13 @@ onMounted(async () => {
|
|||||||
</n-button>
|
</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
</div>
|
</div>
|
||||||
<n-split direction="horizontal" :max="0.75" :min="0.25" :default-size="mailboxSplitSize"
|
<n-split direction="horizontal" :max="0.75" :min="0" :resize-trigger-size="8"
|
||||||
:on-update:size="onSpiltSizeChange">
|
:default-size="mailboxSplitSize" :on-update:size="onSpiltSizeChange">
|
||||||
|
<template #resize-trigger>
|
||||||
|
<div class="split-handle">
|
||||||
|
<div class="split-handle__grip" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<template #1>
|
<template #1>
|
||||||
<div style="overflow: auto; min-height: 60vh; max-height: 100vh;">
|
<div style="overflow: auto; min-height: 60vh; max-height: 100vh;">
|
||||||
<n-list hoverable clickable>
|
<n-list hoverable clickable>
|
||||||
@@ -379,4 +384,23 @@ pre {
|
|||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.split-handle {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-handle__grip {
|
||||||
|
width: 4px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: var(--n-resize-trigger-color);
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-handle:hover .split-handle__grip {
|
||||||
|
background-color: var(--n-resize-trigger-color-hover);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ export const deMessages = {
|
|||||||
"components.AiExtractInfo.authLink": "Authentifizierungslink",
|
"components.AiExtractInfo.authLink": "Authentifizierungslink",
|
||||||
"views.admin.Maintenance.autoCleanup": "Automatische Bereinigung",
|
"views.admin.Maintenance.autoCleanup": "Automatische Bereinigung",
|
||||||
"components.MailBox.autoRefresh": "Automatische Aktualisierung",
|
"components.MailBox.autoRefresh": "Automatische Aktualisierung",
|
||||||
|
"components.MailBox.backToList": "Zurück zur Liste",
|
||||||
"views.common.Appearance.autoRefreshInterval": "Automatisches Aktualisierungsintervall (s)",
|
"views.common.Appearance.autoRefreshInterval": "Automatisches Aktualisierungsintervall (s)",
|
||||||
"views.Index.auto_reply": "Automatische Antwort",
|
"views.Index.auto_reply": "Automatische Antwort",
|
||||||
"views.index.AutoReply.autoReply": "Automatische Antwort",
|
"views.index.AutoReply.autoReply": "Automatische Antwort",
|
||||||
@@ -299,7 +300,10 @@ export const deMessages = {
|
|||||||
"views.index.SimpleIndex.deleteSuccess": "E-Mail erfolgreich gelöscht",
|
"views.index.SimpleIndex.deleteSuccess": "E-Mail erfolgreich gelöscht",
|
||||||
"views.user.UserLogin.cannotForgotPassword": "E-Mail-Verifizierung oder Registrierung ist deaktiviert; das Passwort kann nicht zurückgesetzt werden. Bitte den Administrator kontaktieren.",
|
"views.user.UserLogin.cannotForgotPassword": "E-Mail-Verifizierung oder Registrierung ist deaktiviert; das Passwort kann nicht zurückgesetzt werden. Bitte den Administrator kontaktieren.",
|
||||||
"views.Admin.mailWebhook": "Mail-Webhook",
|
"views.Admin.mailWebhook": "Mail-Webhook",
|
||||||
"views.common.Appearance.mailboxSplitSize": "Größe der Mailbox-Aufteilung",
|
"views.common.Appearance.mailboxSplitSize": "Breite der linken Liste in der zweispaltigen Postfachansicht",
|
||||||
|
"views.common.Appearance.mailListView": "Postfach-Listenansicht in voller Breite",
|
||||||
|
"views.common.Appearance.mailListPreviewLineClamp": "Zeilen der Textvorschau",
|
||||||
|
"views.common.Appearance.off": "Aus",
|
||||||
"views.index.SimpleIndex.refreshSuccess": "E-Mails erfolgreich aktualisiert",
|
"views.index.SimpleIndex.refreshSuccess": "E-Mails erfolgreich aktualisiert",
|
||||||
"views.Admin.unknow": "E-Mails mit unbekanntem Empfänger",
|
"views.Admin.unknow": "E-Mails mit unbekanntem Empfänger",
|
||||||
"views.Admin.maintenance": "Wartung",
|
"views.Admin.maintenance": "Wartung",
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ export const esMessages = {
|
|||||||
"components.AiExtractInfo.authLink": "Enlace de autenticación",
|
"components.AiExtractInfo.authLink": "Enlace de autenticación",
|
||||||
"views.admin.Maintenance.autoCleanup": "Limpieza automática",
|
"views.admin.Maintenance.autoCleanup": "Limpieza automática",
|
||||||
"components.MailBox.autoRefresh": "Actualización automática",
|
"components.MailBox.autoRefresh": "Actualización automática",
|
||||||
|
"components.MailBox.backToList": "Volver a la lista",
|
||||||
"views.common.Appearance.autoRefreshInterval": "Intervalo de actualización automática (s)",
|
"views.common.Appearance.autoRefreshInterval": "Intervalo de actualización automática (s)",
|
||||||
"views.Index.auto_reply": "Respuesta automática",
|
"views.Index.auto_reply": "Respuesta automática",
|
||||||
"views.index.AutoReply.autoReply": "Respuesta automática",
|
"views.index.AutoReply.autoReply": "Respuesta automática",
|
||||||
@@ -299,7 +300,10 @@ export const esMessages = {
|
|||||||
"views.index.SimpleIndex.deleteSuccess": "Correo eliminado correctamente",
|
"views.index.SimpleIndex.deleteSuccess": "Correo eliminado correctamente",
|
||||||
"views.user.UserLogin.cannotForgotPassword": "La verificación por correo o el registro está desactivado; no se puede restablecer la contraseña. Contacta con el administrador.",
|
"views.user.UserLogin.cannotForgotPassword": "La verificación por correo o el registro está desactivado; no se puede restablecer la contraseña. Contacta con el administrador.",
|
||||||
"views.Admin.mailWebhook": "Webhook de correo",
|
"views.Admin.mailWebhook": "Webhook de correo",
|
||||||
"views.common.Appearance.mailboxSplitSize": "Tamaño de división del buzón",
|
"views.common.Appearance.mailboxSplitSize": "Ancho de la lista izquierda en la vista de buzón de dos columnas",
|
||||||
|
"views.common.Appearance.mailListView": "Vista de lista del buzón de ancho completo",
|
||||||
|
"views.common.Appearance.mailListPreviewLineClamp": "Líneas de vista previa del cuerpo",
|
||||||
|
"views.common.Appearance.off": "Desactivado",
|
||||||
"views.index.SimpleIndex.refreshSuccess": "Correos actualizados correctamente",
|
"views.index.SimpleIndex.refreshSuccess": "Correos actualizados correctamente",
|
||||||
"views.Admin.unknow": "Correos con destinatario desconocido",
|
"views.Admin.unknow": "Correos con destinatario desconocido",
|
||||||
"views.Admin.maintenance": "Mantenimiento",
|
"views.Admin.maintenance": "Mantenimiento",
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ export const jaMessages = {
|
|||||||
"components.AiExtractInfo.authLink": "認証リンク",
|
"components.AiExtractInfo.authLink": "認証リンク",
|
||||||
"views.admin.Maintenance.autoCleanup": "自動クリーンアップ",
|
"views.admin.Maintenance.autoCleanup": "自動クリーンアップ",
|
||||||
"components.MailBox.autoRefresh": "自動更新",
|
"components.MailBox.autoRefresh": "自動更新",
|
||||||
|
"components.MailBox.backToList": "リストに戻る",
|
||||||
"views.common.Appearance.autoRefreshInterval": "自動更新間隔 (秒)",
|
"views.common.Appearance.autoRefreshInterval": "自動更新間隔 (秒)",
|
||||||
"views.Index.auto_reply": "自動返信",
|
"views.Index.auto_reply": "自動返信",
|
||||||
"views.index.AutoReply.autoReply": "自動返信",
|
"views.index.AutoReply.autoReply": "自動返信",
|
||||||
@@ -299,7 +300,10 @@ export const jaMessages = {
|
|||||||
"views.index.SimpleIndex.deleteSuccess": "メールを削除しました",
|
"views.index.SimpleIndex.deleteSuccess": "メールを削除しました",
|
||||||
"views.user.UserLogin.cannotForgotPassword": "メール検証または登録が無効のため、パスワードを再設定できません。管理者へ連絡してください。",
|
"views.user.UserLogin.cannotForgotPassword": "メール検証または登録が無効のため、パスワードを再設定できません。管理者へ連絡してください。",
|
||||||
"views.Admin.mailWebhook": "メールWebhook",
|
"views.Admin.mailWebhook": "メールWebhook",
|
||||||
"views.common.Appearance.mailboxSplitSize": "メールボックス分割サイズ",
|
"views.common.Appearance.mailboxSplitSize": "メールボックス2カラム表示の左側リスト幅",
|
||||||
|
"views.common.Appearance.mailListView": "メールボックス全幅リスト表示",
|
||||||
|
"views.common.Appearance.mailListPreviewLineClamp": "本文プレビュー行数",
|
||||||
|
"views.common.Appearance.off": "オフ",
|
||||||
"views.index.SimpleIndex.refreshSuccess": "メールを更新しました",
|
"views.index.SimpleIndex.refreshSuccess": "メールを更新しました",
|
||||||
"views.Admin.unknow": "受信者不明のメール",
|
"views.Admin.unknow": "受信者不明のメール",
|
||||||
"views.Admin.maintenance": "メンテナンス",
|
"views.Admin.maintenance": "メンテナンス",
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ export const ptBRMessages = {
|
|||||||
"components.AiExtractInfo.authLink": "Link de autenticação",
|
"components.AiExtractInfo.authLink": "Link de autenticação",
|
||||||
"views.admin.Maintenance.autoCleanup": "Limpeza automática",
|
"views.admin.Maintenance.autoCleanup": "Limpeza automática",
|
||||||
"components.MailBox.autoRefresh": "Atualização automática",
|
"components.MailBox.autoRefresh": "Atualização automática",
|
||||||
|
"components.MailBox.backToList": "Voltar para a lista",
|
||||||
"views.common.Appearance.autoRefreshInterval": "Intervalo de atualização automática (s)",
|
"views.common.Appearance.autoRefreshInterval": "Intervalo de atualização automática (s)",
|
||||||
"views.Index.auto_reply": "Resposta automática",
|
"views.Index.auto_reply": "Resposta automática",
|
||||||
"views.index.AutoReply.autoReply": "Resposta automática",
|
"views.index.AutoReply.autoReply": "Resposta automática",
|
||||||
@@ -299,7 +300,10 @@ export const ptBRMessages = {
|
|||||||
"views.index.SimpleIndex.deleteSuccess": "E-mail excluído com sucesso",
|
"views.index.SimpleIndex.deleteSuccess": "E-mail excluído com sucesso",
|
||||||
"views.user.UserLogin.cannotForgotPassword": "A verificação por e-mail ou o registro está desativado; não é possível redefinir a senha. Entre em contato com o administrador.",
|
"views.user.UserLogin.cannotForgotPassword": "A verificação por e-mail ou o registro está desativado; não é possível redefinir a senha. Entre em contato com o administrador.",
|
||||||
"views.Admin.mailWebhook": "Webhook de e-mail",
|
"views.Admin.mailWebhook": "Webhook de e-mail",
|
||||||
"views.common.Appearance.mailboxSplitSize": "Tamanho da divisão da caixa de correio",
|
"views.common.Appearance.mailboxSplitSize": "Largura da lista esquerda na visualização de duas colunas da caixa de correio",
|
||||||
|
"views.common.Appearance.mailListView": "Visualização de lista da caixa de correio em largura total",
|
||||||
|
"views.common.Appearance.mailListPreviewLineClamp": "Linhas da prévia do corpo",
|
||||||
|
"views.common.Appearance.off": "Desativado",
|
||||||
"views.index.SimpleIndex.refreshSuccess": "E-mails atualizados com sucesso",
|
"views.index.SimpleIndex.refreshSuccess": "E-mails atualizados com sucesso",
|
||||||
"views.Admin.unknow": "E-mails com destinatário desconhecido",
|
"views.Admin.unknow": "E-mails com destinatário desconhecido",
|
||||||
"views.Admin.maintenance": "Manutenção",
|
"views.Admin.maintenance": "Manutenção",
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ export const MESSAGE_REGISTRY = {
|
|||||||
"en": "Auto Refresh",
|
"en": "Auto Refresh",
|
||||||
"zh": "自动刷新"
|
"zh": "自动刷新"
|
||||||
},
|
},
|
||||||
|
"backToList": {
|
||||||
|
"en": "Back to List",
|
||||||
|
"zh": "返回列表"
|
||||||
|
},
|
||||||
"cancelMultiAction": {
|
"cancelMultiAction": {
|
||||||
"en": "Cancel Multi Action",
|
"en": "Cancel Multi Action",
|
||||||
"zh": "取消多选"
|
"zh": "取消多选"
|
||||||
@@ -2107,8 +2111,20 @@ export const MESSAGE_REGISTRY = {
|
|||||||
"zh": "左侧"
|
"zh": "左侧"
|
||||||
},
|
},
|
||||||
"mailboxSplitSize": {
|
"mailboxSplitSize": {
|
||||||
"en": "Mailbox Split Size",
|
"en": "Left list width in two-column mailbox view",
|
||||||
"zh": "邮箱界面分栏大小"
|
"zh": "邮箱双栏视图左侧列表宽度占比"
|
||||||
|
},
|
||||||
|
"mailListView": {
|
||||||
|
"en": "Full-width mailbox list view",
|
||||||
|
"zh": "邮箱全宽列表视图"
|
||||||
|
},
|
||||||
|
"mailListPreviewLineClamp": {
|
||||||
|
"en": "Body Preview Lines",
|
||||||
|
"zh": "正文预览行数"
|
||||||
|
},
|
||||||
|
"off": {
|
||||||
|
"en": "Off",
|
||||||
|
"zh": "关闭"
|
||||||
},
|
},
|
||||||
"preferShowTextMail": {
|
"preferShowTextMail": {
|
||||||
"en": "Display text Mail by default",
|
"en": "Display text Mail by default",
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ export const useGlobalState = createGlobalState(
|
|||||||
const adminMailTabAddress = ref("");
|
const adminMailTabAddress = ref("");
|
||||||
const adminSendBoxTabAddress = ref("");
|
const adminSendBoxTabAddress = ref("");
|
||||||
const mailboxSplitSize = useStorage('mailboxSplitSize', 0.25);
|
const mailboxSplitSize = useStorage('mailboxSplitSize', 0.25);
|
||||||
|
const mailListView = useStorage('mailListView', false);
|
||||||
|
const mailListPreviewLineClamp = useStorage('mailListPreviewLineClamp', 2);
|
||||||
const useIframeShowMail = useStorage('useIframeShowMail', false);
|
const useIframeShowMail = useStorage('useIframeShowMail', false);
|
||||||
const preferShowTextMail = useStorage('preferShowTextMail', false);
|
const preferShowTextMail = useStorage('preferShowTextMail', false);
|
||||||
const userJwt = useStorage('userJwt', '');
|
const userJwt = useStorage('userJwt', '');
|
||||||
@@ -160,6 +162,8 @@ export const useGlobalState = createGlobalState(
|
|||||||
adminMailTabAddress,
|
adminMailTabAddress,
|
||||||
adminSendBoxTabAddress,
|
adminSendBoxTabAddress,
|
||||||
mailboxSplitSize,
|
mailboxSplitSize,
|
||||||
|
mailListView,
|
||||||
|
mailListPreviewLineClamp,
|
||||||
useIframeShowMail,
|
useIframeShowMail,
|
||||||
preferShowTextMail,
|
preferShowTextMail,
|
||||||
userJwt,
|
userJwt,
|
||||||
|
|||||||
@@ -435,6 +435,10 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
|
:deep(.n-page-header) {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.n-page-header__title) {
|
:deep(.n-page-header__title) {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mailboxSplitSize, useIframeShowMail, preferShowTextMail, configAutoRefreshInterval,
|
mailboxSplitSize, mailListView, mailListPreviewLineClamp, useIframeShowMail, preferShowTextMail, configAutoRefreshInterval,
|
||||||
globalTabplacement, useSideMargin, useUTCDate, useSimpleIndex
|
globalTabplacement, useSideMargin, useUTCDate, useSimpleIndex
|
||||||
} = useGlobalState()
|
} = useGlobalState()
|
||||||
const isMobile = useIsMobile()
|
const isMobile = useIsMobile()
|
||||||
@@ -23,12 +23,26 @@ const { t } = useScopedI18n('views.common.Appearance')
|
|||||||
<div class="center">
|
<div class="center">
|
||||||
<n-card :bordered="false" embedded>
|
<n-card :bordered="false" embedded>
|
||||||
<n-form-item-row v-if="!isMobile" :label="t('mailboxSplitSize')">
|
<n-form-item-row v-if="!isMobile" :label="t('mailboxSplitSize')">
|
||||||
<n-slider v-model:value="mailboxSplitSize" :min="0.25" :max="0.75" :step="0.01" :marks="{
|
<n-slider v-model:value="mailboxSplitSize" :min="0" :max="0.75" :step="0.01" :marks="{
|
||||||
|
0: '0',
|
||||||
0.25: '0.25',
|
0.25: '0.25',
|
||||||
0.5: '0.5',
|
0.5: '0.5',
|
||||||
0.75: '0.75'
|
0.75: '0.75'
|
||||||
}" />
|
}" />
|
||||||
</n-form-item-row>
|
</n-form-item-row>
|
||||||
|
<n-form-item-row v-if="!isMobile" :label="t('mailListView')">
|
||||||
|
<n-switch v-model:value="mailListView" :round="false" />
|
||||||
|
</n-form-item-row>
|
||||||
|
<n-form-item-row v-if="!isMobile" :label="t('mailListPreviewLineClamp')">
|
||||||
|
<n-slider v-model:value="mailListPreviewLineClamp" :min="0" :max="5" :step="1" :marks="{
|
||||||
|
0: t('off'),
|
||||||
|
1: '1',
|
||||||
|
2: '2',
|
||||||
|
3: '3',
|
||||||
|
4: '4',
|
||||||
|
5: '5'
|
||||||
|
}" />
|
||||||
|
</n-form-item-row>
|
||||||
<n-form-item-row :label="t('autoRefreshInterval')">
|
<n-form-item-row :label="t('autoRefreshInterval')">
|
||||||
<n-slider v-model:value="configAutoRefreshInterval" :min="30" :max="300" :step="1" :marks="{
|
<n-slider v-model:value="configAutoRefreshInterval" :min="30" :max="300" :step="1" :marks="{
|
||||||
60: '60', 120: '120', 180: '180', 240: '240'
|
60: '60', 120: '120', 180: '180', 240: '240'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "temp-email-pages",
|
"name": "temp-email-pages",
|
||||||
"version": "1.9.0",
|
"version": "1.10.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"wrangler": "^4.96.0"
|
"wrangler": "^4.104.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
aiosmtpd==1.4.6
|
aiosmtpd==1.4.6
|
||||||
pydantic-settings==2.13.1
|
pydantic-settings==2.14.2
|
||||||
Twisted==25.5.0
|
Twisted==26.4.0
|
||||||
httpx==0.28.1
|
httpx==0.28.1
|
||||||
pyOpenSSL==26.0.0
|
pyOpenSSL==26.0.0
|
||||||
service-identity==24.2.0
|
service-identity==24.2.0
|
||||||
|
|||||||
@@ -48,6 +48,12 @@ No additional env var is required.
|
|||||||
|
|
||||||
Register at `https://resend.com/domains` and add DNS records according to the instructions.
|
Register at `https://resend.com/domains` and add DNS records according to the instructions.
|
||||||
|
|
||||||
|
> [!WARNING] DNS record proxy status on Cloudflare
|
||||||
|
> Resend domain verification CNAME records **must be set to DNS-only** (gray cloud) in
|
||||||
|
> the Cloudflare DNS dashboard. Proxied (orange cloud) records will prevent Resend from
|
||||||
|
> completing verification, and a single failed attempt can take several hours before
|
||||||
|
> Retry becomes available. See [#515](https://github.com/dreamhunter2333/cloudflare_temp_email/issues/515).
|
||||||
|
|
||||||
Create an `api key` on the `API KEYS` page.
|
Create an `api key` on the `API KEYS` page.
|
||||||
|
|
||||||
Then execute the following command to add `RESEND_TOKEN` to secrets:
|
Then execute the following command to add `RESEND_TOKEN` to secrets:
|
||||||
|
|||||||
@@ -48,6 +48,12 @@ send_email = [
|
|||||||
|
|
||||||
注册 `https://resend.com/domains` 根据提示添加 DNS 记录,
|
注册 `https://resend.com/domains` 根据提示添加 DNS 记录,
|
||||||
|
|
||||||
|
> [!WARNING] Cloudflare 上 DNS 记录的代理状态
|
||||||
|
> Resend 域名验证的 CNAME 记录**必须设置为 仅 DNS**(灰云),在
|
||||||
|
> Cloudflare DNS 控制面板中代理(橙云)记录会阻止 Resend
|
||||||
|
> 完成验证,且一次失败的尝试可能需要数小时才能重试。
|
||||||
|
> 参见 [#515](https://github.com/dreamhunter2333/cloudflare_temp_email/issues/515)。
|
||||||
|
|
||||||
`API KEYS` 页面创建 `api key`
|
`API KEYS` 页面创建 `api key`
|
||||||
|
|
||||||
然后执行下面的命令,将 `RESEND_TOKEN` 添加到 secrets 中
|
然后执行下面的命令,将 `RESEND_TOKEN` 添加到 secrets 中
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "temp-mail-docs",
|
"name": "temp-mail-docs",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.9.0",
|
"version": "1.10.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.9.1",
|
"@types/node": "^26.0.0",
|
||||||
"vitepress": "^1.6.4",
|
"vitepress": "^1.6.4",
|
||||||
"wrangler": "^4.96.0"
|
"wrangler": "^4.104.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vitepress dev docs",
|
"dev": "vitepress dev docs",
|
||||||
|
|||||||
968
vitepress-docs/pnpm-lock.yaml
generated
968
vitepress-docs/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cloudflare_temp_email",
|
"name": "cloudflare_temp_email",
|
||||||
"version": "1.9.0",
|
"version": "1.10.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -11,23 +11,23 @@
|
|||||||
"build": "wrangler deploy --dry-run --outdir dist --minify"
|
"build": "wrangler deploy --dry-run --outdir dist --minify"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/workers-types": "^4.20260602.1",
|
"@cloudflare/workers-types": "^4.20260624.1",
|
||||||
"@eslint/js": "9.39.1",
|
"@eslint/js": "9.39.1",
|
||||||
"@types/node": "^25.9.1",
|
"@types/node": "^25.9.4",
|
||||||
"eslint": "9.39.1",
|
"eslint": "9.39.1",
|
||||||
"globals": "^16.5.0",
|
"globals": "^16.5.0",
|
||||||
"typescript-eslint": "^8.60.1",
|
"typescript-eslint": "^8.62.0",
|
||||||
"wrangler": "^4.96.0"
|
"wrangler": "^4.104.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "3.888.0",
|
"@aws-sdk/client-s3": "3.888.0",
|
||||||
"@aws-sdk/s3-request-presigner": "3.888.0",
|
"@aws-sdk/s3-request-presigner": "3.888.0",
|
||||||
"@simplewebauthn/server": "^13.3.1",
|
"@simplewebauthn/server": "^13.3.2",
|
||||||
"hono": "^4.12.23",
|
"hono": "^4.12.27",
|
||||||
"jsonpath-plus": "^10.4.0",
|
"jsonpath-plus": "^10.4.0",
|
||||||
"mimetext": "^3.0.28",
|
"mimetext": "^3.0.28",
|
||||||
"postal-mime": "^2.7.4",
|
"postal-mime": "^2.7.4",
|
||||||
"resend": "^6.12.4",
|
"resend": "^6.14.0",
|
||||||
"telegraf": "4.16.3",
|
"telegraf": "4.16.3",
|
||||||
"worker-mailer": "^1.2.1"
|
"worker-mailer": "^1.2.1"
|
||||||
},
|
},
|
||||||
|
|||||||
1381
worker/pnpm-lock.yaml
generated
1381
worker/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
export const CONSTANTS = {
|
export const CONSTANTS = {
|
||||||
VERSION: 'v' + '1.9.0',
|
VERSION: 'v' + '1.10.0',
|
||||||
|
|
||||||
// DB Version
|
// DB Version
|
||||||
DB_VERSION_KEY: 'db_version',
|
DB_VERSION_KEY: 'db_version',
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ If the extracted content is in markdown link format [text](url):
|
|||||||
2. **Single Selection**: Choose ONLY ONE type based on the highest priority match
|
2. **Single Selection**: Choose ONLY ONE type based on the highest priority match
|
||||||
3. **Real Data Only**: Never invent, guess, or fabricate content
|
3. **Real Data Only**: Never invent, guess, or fabricate content
|
||||||
4. **Complete URLs**: Links must be full, valid URLs as they appear in the email
|
4. **Complete URLs**: Links must be full, valid URLs as they appear in the email
|
||||||
5. **Clean Extraction**: Return only the raw extracted content, no extra text
|
5. **No Domain Modification**: Never modify, rewrite, or substitute URL domains. If the exact URL domain is uncertain, return none
|
||||||
|
6. **Clean Extraction**: Return only the raw extracted content, no extra text
|
||||||
|
|
||||||
# Output Format (JSON only)
|
# Output Format (JSON only)
|
||||||
{
|
{
|
||||||
@@ -166,6 +167,65 @@ async function saveExtractMetadata(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function decodeHtmlEntities(text: string): string {
|
||||||
|
const entities: Record<string, string> = {
|
||||||
|
amp: '&',
|
||||||
|
lt: '<',
|
||||||
|
gt: '>',
|
||||||
|
quot: '"',
|
||||||
|
apos: "'",
|
||||||
|
nbsp: ' ',
|
||||||
|
};
|
||||||
|
|
||||||
|
const decodeCodePoint = (value: number, fallback: string) => {
|
||||||
|
if (!Number.isFinite(value) || value < 0 || value > 0x10ffff) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
return String.fromCodePoint(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return text.replace(/&(#x[0-9a-f]+|#\d+|[a-z][a-z0-9]+);/gi, (match, entity) => {
|
||||||
|
const normalized = entity.toLowerCase();
|
||||||
|
if (normalized.startsWith('#x')) {
|
||||||
|
const value = Number.parseInt(normalized.slice(2), 16);
|
||||||
|
return decodeCodePoint(value, match);
|
||||||
|
}
|
||||||
|
if (normalized.startsWith('#')) {
|
||||||
|
const value = Number.parseInt(normalized.slice(1), 10);
|
||||||
|
return decodeCodePoint(value, match);
|
||||||
|
}
|
||||||
|
return entities[normalized] ?? match;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function htmlToTextForAi(html: string): string {
|
||||||
|
return decodeHtmlEntities(
|
||||||
|
html
|
||||||
|
.replace(/<\s*(script|style|head|svg)[^>]*>[\s\S]*?<\s*\/\s*\1\s*>/gi, ' ')
|
||||||
|
.replace(/<!--[\s\S]*?-->/g, ' ')
|
||||||
|
.replace(/<a\b[^>]*\bhref=(["'])(.*?)\1[^>]*>([\s\S]*?)<\/a>/gi, ' $3 $2 ')
|
||||||
|
.replace(/<\s*br\s*\/?>/gi, '\n')
|
||||||
|
.replace(/<\/\s*(p|div|tr|td|th|li|table|section|article|header|footer|h[1-6])\s*>/gi, '\n')
|
||||||
|
.replace(/<[^>]+>/g, ' ')
|
||||||
|
)
|
||||||
|
.replace(/[ \t\r\f\v]+/g, ' ')
|
||||||
|
.replace(/\n\s+/g, '\n')
|
||||||
|
.replace(/\n{3,}/g, '\n\n')
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEmailContentForExtract(parsedEmail: Awaited<ReturnType<typeof commonParseMail>>): string {
|
||||||
|
if (parsedEmail?.text) {
|
||||||
|
return parsedEmail.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parsedEmail?.html) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return htmlToTextForAi(parsedEmail.html) || parsedEmail.html;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main extraction function
|
* Main extraction function
|
||||||
* Checks if extraction is enabled, processes the email content, and saves to database.
|
* Checks if extraction is enabled, processes the email content, and saves to database.
|
||||||
@@ -219,7 +279,7 @@ export async function extractEmailInfo(
|
|||||||
|
|
||||||
// Parse email to get content (shared by the AI path and the regex fallback)
|
// Parse email to get content (shared by the AI path and the regex fallback)
|
||||||
const parsedEmail = await commonParseMail(parsedEmailContext);
|
const parsedEmail = await commonParseMail(parsedEmailContext);
|
||||||
const emailContent = parsedEmail?.text || parsedEmail?.html || "";
|
const emailContent = getEmailContentForExtract(parsedEmail);
|
||||||
|
|
||||||
if (!emailContent) {
|
if (!emailContent) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user