@puwenhui/dsh-email 0.2.2 → 0.2.3

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/lib/client.js CHANGED
@@ -74,7 +74,7 @@ window.__ModuleLoader__.load({ id: "@puwenhui/dsh-email", factory: (require) =>
74
74
  key: "folders",
75
75
  label: "同步文件夹",
76
76
  type: "array",
77
- placeholder: "INBOX, 已发送"
77
+ placeholder: "INBOX, 已发送, 垃圾邮件"
78
78
  },
79
79
  {
80
80
  key: "pollSeconds",
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["useSyncExternalStore","useState","useRef","IconChevronDownOutline14","h","useSyncExternalStore"],"sources":["../client-src/settings-card.tsx","../client-src/index.tsx"],"sourcesContent":["/**\n * 设置 → 插件 →「邮件」配置卡:邮箱连接与重要邮件提醒规则。\n * 交互与外观对齐宿主 PluginCard 模式:点击头部展开/收起、保存成功自动收起、\n * 未保存标记;颜色全部走宿主主题变量(--dsw-alias-*,跟随「通用设置」外观)。\n * 读写经宿主 settingsScope(revision fence);「已覆盖」标记与恢复默认基于\n * user 层字段存在性;密码字段不回显,输入新值才写入。\n */\nimport { useEffect, useRef, useState, useSyncExternalStore } from 'react'\nimport { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'\n\n/** 结构类型:宿主 settingsScope.bind() 的返回(避免跨插件值导入) */\ninterface ScopeLike {\n getSnapshot(): { status: string; value: unknown; user: unknown; revision?: number }\n subscribe(listener: () => void): () => void\n mutate(ops: ReadonlyArray<{ op: 'set' | 'unset'; path: readonly string[]; value?: unknown }>, expectedRevision?: number): Promise<void>\n}\n\ninterface FieldDef {\n key: string\n label: string\n type: 'string' | 'number' | 'boolean' | 'array' | 'secret'\n placeholder?: string\n hint?: string\n}\n\n/** 字段清单(与宿主半边 MailSettings schema 保持同步) */\nconst FIELDS: FieldDef[] = [\n { key: 'user', label: '邮箱账号', type: 'string', placeholder: 'name@corp.com' },\n { key: 'pass', label: '邮箱密码', type: 'secret', hint: '留空表示不修改;未设置时使用环境变量' },\n { key: 'host', label: 'IMAP 收件服务器', type: 'string', placeholder: 'imap.263.net' },\n { key: 'port', label: 'IMAP 端口', type: 'number' },\n { key: 'tlsMode', label: '加密方式', type: 'string', placeholder: 'tls(可选 starttls / none)' },\n { key: 'smtpHost', label: 'SMTP 发件服务器', type: 'string', placeholder: 'smtp.263.net' },\n { key: 'smtpPort', label: 'SMTP 端口', type: 'number', hint: '263 为 25(无 SSL);465 自动 SSL' },\n { key: 'smtpUser', label: 'SMTP 账号', type: 'string', placeholder: '默认同邮箱账号' },\n { key: 'smtpPass', label: 'SMTP 密码', type: 'secret', hint: '默认同邮箱密码;留空表示不修改' },\n { key: 'folders', label: '同步文件夹', type: 'array', placeholder: 'INBOX, 已发送' },\n { key: 'pollSeconds', label: '同步间隔(秒)', type: 'number' },\n { key: 'backfillDays', label: '首次回填天数', type: 'number' },\n { key: 'notifyEnabled', label: '开启重要新邮件提醒', type: 'boolean' },\n { key: 'notifyAll', label: '所有新邮件都提醒', type: 'boolean', hint: '慎开,容易骚扰' },\n { key: 'notifyFrom', label: '重要发件人白名单', type: 'array', placeholder: 'leader@corp.com, 客户A' },\n { key: 'notifyKeywords', label: '主题关键词', type: 'array', placeholder: '审批, 紧急' },\n]\n\nexport function SettingsCard({ scope }: { scope: ScopeLike }): JSX.Element | null {\n // 裸传方法引用会丢 this(scope.store 变 undefined),必须绑定\n const subscribe = useSync(scope, s => s.subscribe.bind(s))\n const getSnapshot = useSync(scope, s => s.getSnapshot.bind(s))\n const snap = useSyncExternalStore(subscribe, getSnapshot)\n\n const [open, setOpen] = useState(false)\n const [draft, setDraft] = useState<Record<string, unknown> | null>(null)\n const [saving, setSaving] = useState(false)\n const [message, setMessage] = useState<string | null>(null)\n const [failed, setFailed] = useState(false)\n const saveStarted = useRef(false)\n\n const current = (snap.value ?? {}) as Record<string, unknown>\n const userLayer = (snap.user ?? {}) as Record<string, unknown>\n const shown = draft ?? current\n const dirty = draft !== null\n\n // 宿主 PluginCard 同款:保存成功(无失败、无未存编辑)后自动收起\n useEffect(() => {\n if (saving) { saveStarted.current = true; return }\n if (!saveStarted.current) return\n saveStarted.current = false\n if (!dirty && !failed) setOpen(false)\n }, [dirty, failed, saving])\n\n const edit = (key: string, value: unknown): void => {\n setMessage(null)\n setFailed(false)\n setDraft(prev => ({ ...(prev ?? { ...current }), [key]: value }))\n }\n\n const asText = (f: FieldDef): string => {\n const v = shown[f.key]\n if (f.type === 'array') return Array.isArray(v) ? v.join(', ') : String(v ?? '')\n return String(v ?? '')\n }\n\n const save = async (): Promise<void> => {\n if (draft === null) return\n setSaving(true)\n try {\n const ops: Array<{ op: 'set' | 'unset'; path: readonly string[]; value?: unknown }> = []\n for (const f of FIELDS) {\n const after = draft[f.key]\n if (after === undefined) continue // 未改动\n if (f.type === 'secret' && String(after) === '') continue // 密码留空 = 不修改\n const before = current[f.key]\n if (JSON.stringify(after) === JSON.stringify(before)) continue\n ops.push({ op: 'set', path: [f.key], value: after })\n }\n if (ops.length > 0) await scope.mutate(ops, snap.revision)\n setDraft(null)\n setFailed(false)\n setMessage(ops.length > 0 ? '已保存,即时生效' : '无更改')\n } catch (e) {\n setFailed(true)\n setMessage(`保存失败:${String((e as Error).message)}`)\n } finally {\n setSaving(false)\n }\n }\n\n const resetField = async (f: FieldDef): Promise<void> => {\n setSaving(true)\n try {\n await scope.mutate([{ op: 'unset', path: [f.key] }], snap.revision)\n if (draft !== null) {\n const next = { ...draft }\n delete next[f.key]\n setDraft(next)\n }\n setMessage(`已恢复默认:${f.label}`)\n } catch (e) {\n setFailed(true)\n setMessage(`恢复失败:${String((e as Error).message)}`)\n } finally {\n setSaving(false)\n }\n }\n\n if (snap.status !== 'ready') {\n if (snap.status === 'unavailable') return null // 宿主未暴露该命名空间时不留痕迹\n return <div style={{ ...cardStyle, padding: '12px 16px', color: 'var(--dsw-alias-label-tertiary)' }}>邮件配置加载中…</div>\n }\n\n return (\n <div style={cardStyle}>\n {/* 头部:点击展开/收起(宿主 PluginCard 同款交互) */}\n <button\n type=\"button\"\n aria-expanded={open}\n onClick={() => { setOpen(!open) }}\n style={{\n display: 'flex', alignItems: 'center', gap: '10px', width: '100%',\n background: 'transparent', border: 'none', padding: '2px 0',\n color: 'inherit', font: 'inherit', cursor: 'pointer', textAlign: 'left',\n }}\n >\n <span style={{ display: 'flex', flexDirection: 'column', gap: '2px', flex: '1 1 auto' }}>\n <span style={{ fontWeight: 600, fontSize: '14px', color: 'var(--dsw-alias-label-primary)' }}>邮件</span>\n <span style={{ fontSize: '12px', color: 'var(--dsw-alias-label-tertiary)' }}>邮箱连接(IMAP/SMTP)与重要新邮件提醒</span>\n </span>\n {dirty && <span style={{ fontSize: '11px', color: 'var(--dsw-alias-label-secondary)', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: '4px', padding: '1px 6px' }}>未保存</span>}\n <IconChevronDownOutline14 style={{ transform: open ? 'rotate(180deg)' : 'none', transition: 'transform 0.15s', flex: '0 0 auto' }} />\n </button>\n\n {open && (\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px 14px', marginTop: '12px', paddingTop: '12px', borderTop: '1px solid var(--dsw-alias-border-l2)' }}>\n {FIELDS.map((f) => {\n const overridden = Object.hasOwn(userLayer, f.key)\n return (\n <label key={f.key} style={{ display: 'flex', flexDirection: 'column', gap: '4px', fontSize: '13px' }}>\n <span style={{ color: 'var(--dsw-alias-label-secondary)' }}>\n {f.label}\n {f.type === 'secret'\n ? (overridden && <span style={{ marginLeft: '6px', color: 'var(--dsw-alias-brand-primary)' }}>·已设置</span>)\n : (overridden && <span style={{ marginLeft: '6px', color: 'var(--dsw-alias-brand-primary)' }} title=\"用户已覆盖默认值\">·已覆盖</span>)}\n {overridden && (\n <button\n type=\"button\"\n disabled={saving}\n onClick={() => { void resetField(f) }}\n style={{ marginLeft: '8px', padding: '0 6px', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: '4px', background: 'transparent', font: 'inherit', fontSize: '11px', color: 'var(--dsw-alias-label-secondary)', cursor: 'pointer' }}\n >恢复默认</button>\n )}\n </span>\n {f.type === 'boolean'\n ? <input\n type=\"checkbox\"\n checked={shown[f.key] === true}\n onChange={(e) => { edit(f.key, e.target.checked) }}\n style={{ width: '16px', height: '16px' }}\n />\n : <input\n type={f.type === 'secret' ? 'password' : 'text'}\n value={f.type === 'secret' ? String(draft?.[f.key] ?? '') : asText(f)}\n placeholder={f.placeholder ?? (f.type === 'secret' ? '输入新值(不回显)' : '')}\n onChange={(e) => {\n const raw = e.target.value\n if (f.type === 'number') edit(f.key, raw === '' ? undefined : Number(raw))\n else if (f.type === 'array') edit(f.key, raw === '' ? [] : raw.split(/[,,]/).map(s => s.trim()).filter(s => s !== ''))\n else edit(f.key, raw)\n }}\n style={{\n width: '100%', padding: '6px 10px', font: 'inherit', boxSizing: 'border-box',\n border: '1px solid var(--dsw-alias-border-l2)', borderRadius: '6px',\n background: 'var(--dsw-alias-bg-layer-2)', color: 'var(--dsw-alias-label-primary)',\n }}\n />}\n {f.hint !== undefined && <span style={{ fontSize: '11px', color: 'var(--dsw-alias-label-tertiary)' }}>{f.hint}</span>}\n </label>\n )\n })}\n <div style={{ gridColumn: '1 / -1', display: 'flex', alignItems: 'center', gap: '10px', marginTop: '4px' }}>\n <button\n type=\"button\"\n disabled={saving || !dirty}\n onClick={() => { void save() }}\n style={{\n padding: '6px 16px', borderRadius: '6px', font: 'inherit',\n border: '1px solid var(--dsw-alias-brand-primary)', background: 'var(--dsw-alias-brand-primary)',\n color: 'var(--dsw-alias-label-primary-foreground)', cursor: dirty ? 'pointer' : 'default',\n opacity: dirty ? 1 : 0.6,\n }}\n >{saving ? '保存中…' : '保存'}</button>\n {dirty && (\n <button\n type=\"button\"\n disabled={saving}\n onClick={() => { setDraft(null); setMessage(null); setFailed(false) }}\n style={{ padding: '6px 16px', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: '6px', background: 'transparent', font: 'inherit', color: 'var(--dsw-alias-label-secondary)', cursor: 'pointer' }}\n >放弃更改</button>\n )}\n {message !== null && <span style={{ fontSize: '12px', color: failed ? 'var(--dsw-alias-label-error)' : 'var(--dsw-alias-label-secondary)' }}>{message}</span>}\n </div>\n </div>\n )}\n </div>\n )\n}\n\n/** 从 scope 派生稳定回调(绑定 this) */\nfunction useSync<T>(scope: ScopeLike, pick: (s: ScopeLike) => T): T {\n const [fn] = useState(() => pick(scope))\n return fn\n}\n\nconst cardStyle: React.CSSProperties = {\n border: '1px solid var(--dsw-alias-border-l2)',\n borderRadius: '10px',\n padding: '12px 16px',\n background: 'var(--dsw-alias-bg-layer-3)',\n font: '13px/1.5 system-ui, \"Segoe UI\", \"Microsoft YaHei\", sans-serif',\n color: 'var(--dsw-alias-label-primary)',\n}\n","/**\n * 浏览器半边:\n * 1) 设置 → 插件 →「邮件」配置卡(settings.plugin.item keyed 槽,读写经 settingsScope)\n * 2) 重要新邮件提醒:宿主 SSE(/dsh-email/notify)→ 桌面通知 + 页内右上横幅兜底\n */\nimport { createElement as h, useSyncExternalStore } from 'react'\nimport { SettingsCard } from './settings-card.tsx'\n\nexport const name = 'dsh-email'\nexport const inject = ['slots', 'locale', 'theme']\n\ninterface ImportantMail {\n subject: string\n from: string\n snippet: string\n tag: string\n}\n\ninterface Banner extends ImportantMail {\n key: number\n}\n\n// ── 模块级状态(横幅队列 + 通知权限快照),渲染器经 useSyncExternalStore 消费 ──\nlet banners: Banner[] = []\nlet permission: NotificationPermission | 'unsupported' =\n typeof Notification === 'undefined' ? 'unsupported' : Notification.permission\nconst listeners = new Set<() => void>()\n\nfunction emit(): void {\n for (const cb of listeners) cb()\n}\n\nconst store = {\n subscribe(cb: () => void): () => void {\n listeners.add(cb)\n return () => { listeners.delete(cb) }\n },\n getBanners(): Banner[] {\n return banners\n },\n getPermission(): NotificationPermission | 'unsupported' {\n return permission\n },\n}\n\nlet bannerSeq = 0\nfunction pushBanner(mail: ImportantMail): void {\n bannerSeq += 1\n const banner: Banner = { ...mail, key: bannerSeq }\n banners = [...banners, banner].slice(-4) // 最多同时 4 条\n emit()\n setTimeout(() => {\n banners = banners.filter(b => b.key !== banner.key)\n emit()\n }, 8000)\n}\n\nfunction showNotification(mail: ImportantMail): void {\n if (typeof Notification === 'undefined' || Notification.permission !== 'granted') return\n try {\n const n = new Notification(`新邮件:${mail.subject}`, {\n body: `${mail.from}\\n${mail.snippet}`,\n tag: mail.tag, // 同一封邮件只弹一次\n })\n n.onclick = () => { window.focus(); n.close() }\n } catch { /* 通知失败不影响横幅 */ }\n}\n\nfunction onImportantMails(mails: ImportantMail[]): void {\n for (const mail of mails) {\n showNotification(mail)\n pushBanner(mail)\n }\n}\n\n/** SSE 地址相对 document.baseURI 解析(反向代理子路径部署安全) */\nfunction notifyUrl(): string {\n return new URL('dsh-email/notify', document.baseURI).toString()\n}\n\nfunction connectSse(): () => void {\n if (typeof document === 'undefined') return () => {}\n const es = new EventSource(notifyUrl())\n es.addEventListener('important-mail', (ev) => {\n try { onImportantMails(JSON.parse((ev as MessageEvent).data as string) as ImportantMail[]) } catch { /* 忽略坏包 */ }\n })\n return () => es.close()\n}\n\nasync function requestNotifyPermission(): Promise<void> {\n if (typeof Notification === 'undefined') return\n permission = await Notification.requestPermission()\n emit()\n}\n\nexport function apply(ctx: {\n slots: {\n inject: (slot: string, register: () => () => void) => () => void\n register: (options: Record<string, unknown>, component: (props?: unknown) => unknown) => () => void\n }\n inject: (services: string[], callback: (scoped: unknown) => void) => () => void\n effect: (fn: () => unknown, label?: string) => () => void\n}): void {\n // 1) 配置卡:设置 → 插件 →「邮件」。\n // dshmarket 同款:在 settingsScope 服务可用的上下文里注册 keyed 卡\n // (不带 locale 字段——未注册词典的 locale 命名空间会让注册失败)。\n ctx.inject(['settingsScope'], (scopedCtx) => {\n const scoped = scopedCtx as {\n slots: typeof ctx.slots\n effect: typeof ctx.effect\n settingsScope: { bind(spec: { namespace: string }): unknown }\n }\n const scope = scoped.settingsScope.bind({ namespace: 'dsh-email' })\n scoped.effect(() => scoped.slots.inject('settings.plugin.item', () =>\n scoped.slots.register(\n { name: 'settings.plugin.item', key: 'dsh-email' },\n () => h(SettingsCard, { scope: scope as never }),\n )), 'dsh-email: settings card')\n })\n\n // 2) 重要邮件提醒:SSE → 桌面通知 + 页内横幅\n ctx.effect(() => {\n const disconnect = connectSse()\n const offOverlay = ctx.slots.inject('shell.overlay', () =>\n ctx.slots.register(\n { name: 'shell.overlay', id: 'dsh-email-notify', label: () => 'dsh-email' },\n () => h(NotifyLayer),\n ))\n return () => { disconnect(); offOverlay() }\n }, 'dsh-email: notify')\n}\n\nfunction NotifyLayer(): JSX.Element | null {\n const list = useSyncExternalStore(store.subscribe, store.getBanners)\n const perm = useSyncExternalStore(store.subscribe, store.getPermission)\n const showGuide = perm === 'default'\n if (list.length === 0 && !showGuide) return null\n return h('div', {\n style: {\n position: 'fixed', top: '14px', right: '14px', zIndex: 9999,\n display: 'flex', flexDirection: 'column', gap: '8px', maxWidth: '360px',\n pointerEvents: 'none',\n } satisfies React.CSSProperties,\n },\n showGuide && h('div', {\n style: {\n pointerEvents: 'auto', padding: '8px 12px', borderRadius: '8px',\n background: 'rgba(29,29,31,0.86)', color: '#fff',\n display: 'flex', alignItems: 'center', gap: '8px',\n font: '13px/1.4 system-ui, \"Segoe UI\", \"Microsoft YaHei\", sans-serif',\n } satisfies React.CSSProperties,\n },\n h('span', null, '邮件提醒'),\n h('button', {\n type: 'button',\n onClick: () => { void requestNotifyPermission() },\n style: {\n padding: '3px 10px', borderRadius: '6px', border: 'none',\n background: '#2563eb', color: '#fff', font: 'inherit', cursor: 'pointer',\n } satisfies React.CSSProperties,\n }, '开启桌面通知'),\n ),\n ...list.map(b => h('div', {\n key: b.key,\n style: {\n pointerEvents: 'auto', padding: '10px 14px', borderRadius: '10px',\n background: 'rgba(29,29,31,0.92)', color: '#fff',\n boxShadow: '0 4px 16px rgba(0,0,0,0.2)',\n font: '13px/1.45 system-ui, \"Segoe UI\", \"Microsoft YaHei\", sans-serif',\n } satisfies React.CSSProperties,\n },\n h('div', { style: { fontWeight: 600, marginBottom: '2px' } }, `✉️ ${b.subject}`),\n h('div', { style: { opacity: 0.85 } }, b.from),\n b.snippet !== '' && h('div', { style: { opacity: 0.65, marginTop: '2px' } }, b.snippet),\n )),\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["useSyncExternalStore","useState","useRef","IconChevronDownOutline14","h","useSyncExternalStore"],"sources":["../client-src/settings-card.tsx","../client-src/index.tsx"],"sourcesContent":["/**\n * 设置 → 插件 →「邮件」配置卡:邮箱连接与重要邮件提醒规则。\n * 交互与外观对齐宿主 PluginCard 模式:点击头部展开/收起、保存成功自动收起、\n * 未保存标记;颜色全部走宿主主题变量(--dsw-alias-*,跟随「通用设置」外观)。\n * 读写经宿主 settingsScope(revision fence);「已覆盖」标记与恢复默认基于\n * user 层字段存在性;密码字段不回显,输入新值才写入。\n */\nimport { useEffect, useRef, useState, useSyncExternalStore } from 'react'\nimport { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'\n\n/** 结构类型:宿主 settingsScope.bind() 的返回(避免跨插件值导入) */\ninterface ScopeLike {\n getSnapshot(): { status: string; value: unknown; user: unknown; revision?: number }\n subscribe(listener: () => void): () => void\n mutate(ops: ReadonlyArray<{ op: 'set' | 'unset'; path: readonly string[]; value?: unknown }>, expectedRevision?: number): Promise<void>\n}\n\ninterface FieldDef {\n key: string\n label: string\n type: 'string' | 'number' | 'boolean' | 'array' | 'secret'\n placeholder?: string\n hint?: string\n}\n\n/** 字段清单(与宿主半边 MailSettings schema 保持同步) */\nconst FIELDS: FieldDef[] = [\n { key: 'user', label: '邮箱账号', type: 'string', placeholder: 'name@corp.com' },\n { key: 'pass', label: '邮箱密码', type: 'secret', hint: '留空表示不修改;未设置时使用环境变量' },\n { key: 'host', label: 'IMAP 收件服务器', type: 'string', placeholder: 'imap.263.net' },\n { key: 'port', label: 'IMAP 端口', type: 'number' },\n { key: 'tlsMode', label: '加密方式', type: 'string', placeholder: 'tls(可选 starttls / none)' },\n { key: 'smtpHost', label: 'SMTP 发件服务器', type: 'string', placeholder: 'smtp.263.net' },\n { key: 'smtpPort', label: 'SMTP 端口', type: 'number', hint: '263 为 25(无 SSL);465 自动 SSL' },\n { key: 'smtpUser', label: 'SMTP 账号', type: 'string', placeholder: '默认同邮箱账号' },\n { key: 'smtpPass', label: 'SMTP 密码', type: 'secret', hint: '默认同邮箱密码;留空表示不修改' },\n { key: 'folders', label: '同步文件夹', type: 'array', placeholder: 'INBOX, 已发送, 垃圾邮件' },\n { key: 'pollSeconds', label: '同步间隔(秒)', type: 'number' },\n { key: 'backfillDays', label: '首次回填天数', type: 'number' },\n { key: 'notifyEnabled', label: '开启重要新邮件提醒', type: 'boolean' },\n { key: 'notifyAll', label: '所有新邮件都提醒', type: 'boolean', hint: '慎开,容易骚扰' },\n { key: 'notifyFrom', label: '重要发件人白名单', type: 'array', placeholder: 'leader@corp.com, 客户A' },\n { key: 'notifyKeywords', label: '主题关键词', type: 'array', placeholder: '审批, 紧急' },\n]\n\nexport function SettingsCard({ scope }: { scope: ScopeLike }): JSX.Element | null {\n // 裸传方法引用会丢 this(scope.store 变 undefined),必须绑定\n const subscribe = useSync(scope, s => s.subscribe.bind(s))\n const getSnapshot = useSync(scope, s => s.getSnapshot.bind(s))\n const snap = useSyncExternalStore(subscribe, getSnapshot)\n\n const [open, setOpen] = useState(false)\n const [draft, setDraft] = useState<Record<string, unknown> | null>(null)\n const [saving, setSaving] = useState(false)\n const [message, setMessage] = useState<string | null>(null)\n const [failed, setFailed] = useState(false)\n const saveStarted = useRef(false)\n\n const current = (snap.value ?? {}) as Record<string, unknown>\n const userLayer = (snap.user ?? {}) as Record<string, unknown>\n const shown = draft ?? current\n const dirty = draft !== null\n\n // 宿主 PluginCard 同款:保存成功(无失败、无未存编辑)后自动收起\n useEffect(() => {\n if (saving) { saveStarted.current = true; return }\n if (!saveStarted.current) return\n saveStarted.current = false\n if (!dirty && !failed) setOpen(false)\n }, [dirty, failed, saving])\n\n const edit = (key: string, value: unknown): void => {\n setMessage(null)\n setFailed(false)\n setDraft(prev => ({ ...(prev ?? { ...current }), [key]: value }))\n }\n\n const asText = (f: FieldDef): string => {\n const v = shown[f.key]\n if (f.type === 'array') return Array.isArray(v) ? v.join(', ') : String(v ?? '')\n return String(v ?? '')\n }\n\n const save = async (): Promise<void> => {\n if (draft === null) return\n setSaving(true)\n try {\n const ops: Array<{ op: 'set' | 'unset'; path: readonly string[]; value?: unknown }> = []\n for (const f of FIELDS) {\n const after = draft[f.key]\n if (after === undefined) continue // 未改动\n if (f.type === 'secret' && String(after) === '') continue // 密码留空 = 不修改\n const before = current[f.key]\n if (JSON.stringify(after) === JSON.stringify(before)) continue\n ops.push({ op: 'set', path: [f.key], value: after })\n }\n if (ops.length > 0) await scope.mutate(ops, snap.revision)\n setDraft(null)\n setFailed(false)\n setMessage(ops.length > 0 ? '已保存,即时生效' : '无更改')\n } catch (e) {\n setFailed(true)\n setMessage(`保存失败:${String((e as Error).message)}`)\n } finally {\n setSaving(false)\n }\n }\n\n const resetField = async (f: FieldDef): Promise<void> => {\n setSaving(true)\n try {\n await scope.mutate([{ op: 'unset', path: [f.key] }], snap.revision)\n if (draft !== null) {\n const next = { ...draft }\n delete next[f.key]\n setDraft(next)\n }\n setMessage(`已恢复默认:${f.label}`)\n } catch (e) {\n setFailed(true)\n setMessage(`恢复失败:${String((e as Error).message)}`)\n } finally {\n setSaving(false)\n }\n }\n\n if (snap.status !== 'ready') {\n if (snap.status === 'unavailable') return null // 宿主未暴露该命名空间时不留痕迹\n return <div style={{ ...cardStyle, padding: '12px 16px', color: 'var(--dsw-alias-label-tertiary)' }}>邮件配置加载中…</div>\n }\n\n return (\n <div style={cardStyle}>\n {/* 头部:点击展开/收起(宿主 PluginCard 同款交互) */}\n <button\n type=\"button\"\n aria-expanded={open}\n onClick={() => { setOpen(!open) }}\n style={{\n display: 'flex', alignItems: 'center', gap: '10px', width: '100%',\n background: 'transparent', border: 'none', padding: '2px 0',\n color: 'inherit', font: 'inherit', cursor: 'pointer', textAlign: 'left',\n }}\n >\n <span style={{ display: 'flex', flexDirection: 'column', gap: '2px', flex: '1 1 auto' }}>\n <span style={{ fontWeight: 600, fontSize: '14px', color: 'var(--dsw-alias-label-primary)' }}>邮件</span>\n <span style={{ fontSize: '12px', color: 'var(--dsw-alias-label-tertiary)' }}>邮箱连接(IMAP/SMTP)与重要新邮件提醒</span>\n </span>\n {dirty && <span style={{ fontSize: '11px', color: 'var(--dsw-alias-label-secondary)', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: '4px', padding: '1px 6px' }}>未保存</span>}\n <IconChevronDownOutline14 style={{ transform: open ? 'rotate(180deg)' : 'none', transition: 'transform 0.15s', flex: '0 0 auto' }} />\n </button>\n\n {open && (\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px 14px', marginTop: '12px', paddingTop: '12px', borderTop: '1px solid var(--dsw-alias-border-l2)' }}>\n {FIELDS.map((f) => {\n const overridden = Object.hasOwn(userLayer, f.key)\n return (\n <label key={f.key} style={{ display: 'flex', flexDirection: 'column', gap: '4px', fontSize: '13px' }}>\n <span style={{ color: 'var(--dsw-alias-label-secondary)' }}>\n {f.label}\n {f.type === 'secret'\n ? (overridden && <span style={{ marginLeft: '6px', color: 'var(--dsw-alias-brand-primary)' }}>·已设置</span>)\n : (overridden && <span style={{ marginLeft: '6px', color: 'var(--dsw-alias-brand-primary)' }} title=\"用户已覆盖默认值\">·已覆盖</span>)}\n {overridden && (\n <button\n type=\"button\"\n disabled={saving}\n onClick={() => { void resetField(f) }}\n style={{ marginLeft: '8px', padding: '0 6px', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: '4px', background: 'transparent', font: 'inherit', fontSize: '11px', color: 'var(--dsw-alias-label-secondary)', cursor: 'pointer' }}\n >恢复默认</button>\n )}\n </span>\n {f.type === 'boolean'\n ? <input\n type=\"checkbox\"\n checked={shown[f.key] === true}\n onChange={(e) => { edit(f.key, e.target.checked) }}\n style={{ width: '16px', height: '16px' }}\n />\n : <input\n type={f.type === 'secret' ? 'password' : 'text'}\n value={f.type === 'secret' ? String(draft?.[f.key] ?? '') : asText(f)}\n placeholder={f.placeholder ?? (f.type === 'secret' ? '输入新值(不回显)' : '')}\n onChange={(e) => {\n const raw = e.target.value\n if (f.type === 'number') edit(f.key, raw === '' ? undefined : Number(raw))\n else if (f.type === 'array') edit(f.key, raw === '' ? [] : raw.split(/[,,]/).map(s => s.trim()).filter(s => s !== ''))\n else edit(f.key, raw)\n }}\n style={{\n width: '100%', padding: '6px 10px', font: 'inherit', boxSizing: 'border-box',\n border: '1px solid var(--dsw-alias-border-l2)', borderRadius: '6px',\n background: 'var(--dsw-alias-bg-layer-2)', color: 'var(--dsw-alias-label-primary)',\n }}\n />}\n {f.hint !== undefined && <span style={{ fontSize: '11px', color: 'var(--dsw-alias-label-tertiary)' }}>{f.hint}</span>}\n </label>\n )\n })}\n <div style={{ gridColumn: '1 / -1', display: 'flex', alignItems: 'center', gap: '10px', marginTop: '4px' }}>\n <button\n type=\"button\"\n disabled={saving || !dirty}\n onClick={() => { void save() }}\n style={{\n padding: '6px 16px', borderRadius: '6px', font: 'inherit',\n border: '1px solid var(--dsw-alias-brand-primary)', background: 'var(--dsw-alias-brand-primary)',\n color: 'var(--dsw-alias-label-primary-foreground)', cursor: dirty ? 'pointer' : 'default',\n opacity: dirty ? 1 : 0.6,\n }}\n >{saving ? '保存中…' : '保存'}</button>\n {dirty && (\n <button\n type=\"button\"\n disabled={saving}\n onClick={() => { setDraft(null); setMessage(null); setFailed(false) }}\n style={{ padding: '6px 16px', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: '6px', background: 'transparent', font: 'inherit', color: 'var(--dsw-alias-label-secondary)', cursor: 'pointer' }}\n >放弃更改</button>\n )}\n {message !== null && <span style={{ fontSize: '12px', color: failed ? 'var(--dsw-alias-label-error)' : 'var(--dsw-alias-label-secondary)' }}>{message}</span>}\n </div>\n </div>\n )}\n </div>\n )\n}\n\n/** 从 scope 派生稳定回调(绑定 this) */\nfunction useSync<T>(scope: ScopeLike, pick: (s: ScopeLike) => T): T {\n const [fn] = useState(() => pick(scope))\n return fn\n}\n\nconst cardStyle: React.CSSProperties = {\n border: '1px solid var(--dsw-alias-border-l2)',\n borderRadius: '10px',\n padding: '12px 16px',\n background: 'var(--dsw-alias-bg-layer-3)',\n font: '13px/1.5 system-ui, \"Segoe UI\", \"Microsoft YaHei\", sans-serif',\n color: 'var(--dsw-alias-label-primary)',\n}\n","/**\n * 浏览器半边:\n * 1) 设置 → 插件 →「邮件」配置卡(settings.plugin.item keyed 槽,读写经 settingsScope)\n * 2) 重要新邮件提醒:宿主 SSE(/dsh-email/notify)→ 桌面通知 + 页内右上横幅兜底\n */\nimport { createElement as h, useSyncExternalStore } from 'react'\nimport { SettingsCard } from './settings-card.tsx'\n\nexport const name = 'dsh-email'\nexport const inject = ['slots', 'locale', 'theme']\n\ninterface ImportantMail {\n subject: string\n from: string\n snippet: string\n tag: string\n}\n\ninterface Banner extends ImportantMail {\n key: number\n}\n\n// ── 模块级状态(横幅队列 + 通知权限快照),渲染器经 useSyncExternalStore 消费 ──\nlet banners: Banner[] = []\nlet permission: NotificationPermission | 'unsupported' =\n typeof Notification === 'undefined' ? 'unsupported' : Notification.permission\nconst listeners = new Set<() => void>()\n\nfunction emit(): void {\n for (const cb of listeners) cb()\n}\n\nconst store = {\n subscribe(cb: () => void): () => void {\n listeners.add(cb)\n return () => { listeners.delete(cb) }\n },\n getBanners(): Banner[] {\n return banners\n },\n getPermission(): NotificationPermission | 'unsupported' {\n return permission\n },\n}\n\nlet bannerSeq = 0\nfunction pushBanner(mail: ImportantMail): void {\n bannerSeq += 1\n const banner: Banner = { ...mail, key: bannerSeq }\n banners = [...banners, banner].slice(-4) // 最多同时 4 条\n emit()\n setTimeout(() => {\n banners = banners.filter(b => b.key !== banner.key)\n emit()\n }, 8000)\n}\n\nfunction showNotification(mail: ImportantMail): void {\n if (typeof Notification === 'undefined' || Notification.permission !== 'granted') return\n try {\n const n = new Notification(`新邮件:${mail.subject}`, {\n body: `${mail.from}\\n${mail.snippet}`,\n tag: mail.tag, // 同一封邮件只弹一次\n })\n n.onclick = () => { window.focus(); n.close() }\n } catch { /* 通知失败不影响横幅 */ }\n}\n\nfunction onImportantMails(mails: ImportantMail[]): void {\n for (const mail of mails) {\n showNotification(mail)\n pushBanner(mail)\n }\n}\n\n/** SSE 地址相对 document.baseURI 解析(反向代理子路径部署安全) */\nfunction notifyUrl(): string {\n return new URL('dsh-email/notify', document.baseURI).toString()\n}\n\nfunction connectSse(): () => void {\n if (typeof document === 'undefined') return () => {}\n const es = new EventSource(notifyUrl())\n es.addEventListener('important-mail', (ev) => {\n try { onImportantMails(JSON.parse((ev as MessageEvent).data as string) as ImportantMail[]) } catch { /* 忽略坏包 */ }\n })\n return () => es.close()\n}\n\nasync function requestNotifyPermission(): Promise<void> {\n if (typeof Notification === 'undefined') return\n permission = await Notification.requestPermission()\n emit()\n}\n\nexport function apply(ctx: {\n slots: {\n inject: (slot: string, register: () => () => void) => () => void\n register: (options: Record<string, unknown>, component: (props?: unknown) => unknown) => () => void\n }\n inject: (services: string[], callback: (scoped: unknown) => void) => () => void\n effect: (fn: () => unknown, label?: string) => () => void\n}): void {\n // 1) 配置卡:设置 → 插件 →「邮件」。\n // dshmarket 同款:在 settingsScope 服务可用的上下文里注册 keyed 卡\n // (不带 locale 字段——未注册词典的 locale 命名空间会让注册失败)。\n ctx.inject(['settingsScope'], (scopedCtx) => {\n const scoped = scopedCtx as {\n slots: typeof ctx.slots\n effect: typeof ctx.effect\n settingsScope: { bind(spec: { namespace: string }): unknown }\n }\n const scope = scoped.settingsScope.bind({ namespace: 'dsh-email' })\n scoped.effect(() => scoped.slots.inject('settings.plugin.item', () =>\n scoped.slots.register(\n { name: 'settings.plugin.item', key: 'dsh-email' },\n () => h(SettingsCard, { scope: scope as never }),\n )), 'dsh-email: settings card')\n })\n\n // 2) 重要邮件提醒:SSE → 桌面通知 + 页内横幅\n ctx.effect(() => {\n const disconnect = connectSse()\n const offOverlay = ctx.slots.inject('shell.overlay', () =>\n ctx.slots.register(\n { name: 'shell.overlay', id: 'dsh-email-notify', label: () => 'dsh-email' },\n () => h(NotifyLayer),\n ))\n return () => { disconnect(); offOverlay() }\n }, 'dsh-email: notify')\n}\n\nfunction NotifyLayer(): JSX.Element | null {\n const list = useSyncExternalStore(store.subscribe, store.getBanners)\n const perm = useSyncExternalStore(store.subscribe, store.getPermission)\n const showGuide = perm === 'default'\n if (list.length === 0 && !showGuide) return null\n return h('div', {\n style: {\n position: 'fixed', top: '14px', right: '14px', zIndex: 9999,\n display: 'flex', flexDirection: 'column', gap: '8px', maxWidth: '360px',\n pointerEvents: 'none',\n } satisfies React.CSSProperties,\n },\n showGuide && h('div', {\n style: {\n pointerEvents: 'auto', padding: '8px 12px', borderRadius: '8px',\n background: 'rgba(29,29,31,0.86)', color: '#fff',\n display: 'flex', alignItems: 'center', gap: '8px',\n font: '13px/1.4 system-ui, \"Segoe UI\", \"Microsoft YaHei\", sans-serif',\n } satisfies React.CSSProperties,\n },\n h('span', null, '邮件提醒'),\n h('button', {\n type: 'button',\n onClick: () => { void requestNotifyPermission() },\n style: {\n padding: '3px 10px', borderRadius: '6px', border: 'none',\n background: '#2563eb', color: '#fff', font: 'inherit', cursor: 'pointer',\n } satisfies React.CSSProperties,\n }, '开启桌面通知'),\n ),\n ...list.map(b => h('div', {\n key: b.key,\n style: {\n pointerEvents: 'auto', padding: '10px 14px', borderRadius: '10px',\n background: 'rgba(29,29,31,0.92)', color: '#fff',\n boxShadow: '0 4px 16px rgba(0,0,0,0.2)',\n font: '13px/1.45 system-ui, \"Segoe UI\", \"Microsoft YaHei\", sans-serif',\n } satisfies React.CSSProperties,\n },\n h('div', { style: { fontWeight: 600, marginBottom: '2px' } }, `✉️ ${b.subject}`),\n h('div', { style: { opacity: 0.85 } }, b.from),\n b.snippet !== '' && h('div', { style: { opacity: 0.65, marginTop: '2px' } }, b.snippet),\n )),\n )\n}\n"],"mappings":"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"}
package/lib/plugin.mjs CHANGED
@@ -26,7 +26,7 @@ function resolveConfig(overrides = {}) {
26
26
  user,
27
27
  pass,
28
28
  dbPath: pick(overrides.dbPath, env.EMAIL_DB_PATH, local.dbPath, "mail.db"),
29
- folders: pick(overrides.folders, local.folders, ["INBOX", "\u5DF2\u53D1\u9001"]),
29
+ folders: pick(overrides.folders, local.folders, ["INBOX", "\u5DF2\u53D1\u9001", "\u5783\u573E\u90AE\u4EF6"]),
30
30
  backfillDays: Number(pick(overrides.backfillDays, env.EMAIL_BACKFILL_DAYS, local.backfillDays, 90)),
31
31
  pollSeconds: Number(pick(overrides.pollSeconds, env.EMAIL_POLL_SECONDS, 60)),
32
32
  maxSourceBytes: Number(pick(overrides.maxSourceBytes, env.EMAIL_MAX_SOURCE_BYTES, local.maxSourceBytes, 10 * 1024 * 1024)),
@@ -659,7 +659,7 @@ var EmailService = class {
659
659
  const kwList = Array.isArray(cfg.notifyKeywords) ? cfg.notifyKeywords : [];
660
660
  const important = [];
661
661
  for (const folder of this.cfg.folders) {
662
- if (folder === "已发送") continue;
662
+ if (folder === "已发送" || folder === "垃圾邮件") continue;
663
663
  const last = before[folder] ?? 0;
664
664
  const rows = this.store.db.prepare("SELECT mailbox, uid, subject, from_name, from_addr, snippet FROM messages WHERE mailbox=? AND uid>? ORDER BY uid").all(folder, last);
665
665
  for (const r of rows) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@puwenhui/dsh-email",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "DSH 邮件助手插件:IMAP 同步 + 全文索引 + 会话归并 + 10 个模型工具 + 设置配置卡 + 重要新邮件桌面提醒",
5
5
  "repository": {
6
6
  "type": "git",