dsh-sidecard-ask 1.8.3 → 1.9.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/CHANGELOG.md +132 -0
- package/README.md +134 -4
- package/client.js +538 -9
- package/index.js +772 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,137 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.1
|
|
4
|
+
|
|
5
|
+
**主题:反馈入口重做——从「设置页里一整块常驻表单」改成「顶部一行横幅 + 居中弹窗」。**
|
|
6
|
+
|
|
7
|
+
用户看过 1.9.0 的界面后原话是「啥也没改啊,反馈部分全部重新做」。复盘:1.9.0 虽然把横幅改成了「默认收起」,
|
|
8
|
+
但收起态仍是一条**整宽品牌色描边大卡片**,展开态把类型/标题/描述/邮箱/SMTP 六行表单全塞进设置页,视觉上跟改之前
|
|
9
|
+
没区别,真正的设置项还是被挤到首屏之外。所以 1.9.1 不再做增量修饰,**整块重写**:
|
|
10
|
+
|
|
11
|
+
- **`dsa-feedback-bar` 一行横幅**替代原来的 `dsa-feedback-hero` 卡片:左侧「反馈」标题 + 一句说明,
|
|
12
|
+
右侧一个主按钮;高度只有一行,设置页第一眼看到的就是真正的设置项。
|
|
13
|
+
- **表单搬进居中对话框**(`FeedbackDialog`,`role="dialog"` / `aria-modal="true"`,`Esc`、点背景、`×` 三种关闭),
|
|
14
|
+
复用插件已有的 `.dsa-backdrop` / `.dsa-dialog` 弹窗样式,不再往页面流里插任何表单节点。
|
|
15
|
+
- **删掉设置页里的「设置邮件发送(SMTP)」区块**(host/端口/SSL/账号/密码/From 六个输入框 + 折叠按钮):
|
|
16
|
+
发信凭证属于部署方/维护者,改用**环境变量或用户层 `config.json`**,填报人看不到也不需要这些概念。
|
|
17
|
+
配置键本身保留,host 侧行为不变。
|
|
18
|
+
- **`/state` 反馈就绪状态**改成在弹窗里用一行状态条说明(已就绪 / 有收件人缺 SMTP / 未配置),不再占设置页版面。
|
|
19
|
+
- 保留 1.9.0 已定的语义:收件人打包默认 `b64:` 混淆值且环境变量优先、填报人「你的邮箱」写进 `Reply-To:`、
|
|
20
|
+
三条出口(发送 / 用邮件客户端发送 / 复制内容)、host 侧限速与打码日志。
|
|
21
|
+
- 测试:`test/contract-test.mjs` 里原先针对内联表单的 61 行断言整块替换为 126 行(横幅只有一行、点开才渲染表单、
|
|
22
|
+
弹窗可独立渲染、关闭路径三种、SMTP 输入框**不存在**、字典键齐全),contract 1209/13 → **1239/0**。
|
|
23
|
+
- README §4.4 与配置表同步重写;不再出现「在设置页填 SMTP」这类指引。
|
|
24
|
+
|
|
25
|
+
## 1.9.0
|
|
26
|
+
|
|
27
|
+
**主题:设置页多了一个「反馈」入口——把问题与建议直接用邮件发出去,而仓库里不存任何邮箱或密码。**
|
|
28
|
+
|
|
29
|
+
起因是用户要求:在插件设置页新增「反馈」按钮,让用户把问题和建议通过邮件发到指定邮箱,并明确要求
|
|
30
|
+
「邮箱地址与服务凭证通过配置项或环境变量注入,不得硬编码或写入仓库」「不引入非必要第三方依赖」。
|
|
31
|
+
|
|
32
|
+
- **一条可折叠的反馈横幅**(设置页**最顶部**、**默认收起**、品牌色描边卡片 `data-dsa-feedback-section`):收起时只有
|
|
33
|
+
一行(标题 + 一句话说明 + 主按钮),点开才是表单——类型(问题 / 建议 / 疑问 / 其他)、标题(4–120 码点)、
|
|
34
|
+
详细描述(10–4000 码点)、可选的**你的邮箱**;收件人不占界面位置(见下)。
|
|
35
|
+
标题拒绝换行(头注入的第二道防线);计数按**码点**,一段 120 个 emoji 的标题不会被当成 240 字拒绝。
|
|
36
|
+
- **`From:` 仍是维护者自己的 SMTP 账号,填报人的邮箱进 `Reply-To:`**。QQ/163 会拒收不属于本账号的
|
|
37
|
+
`From:`(553),所以发件身份不变;填报人在「你的邮箱」里填了地址,Host 校验为邮箱后写成 `Reply-To:`,
|
|
38
|
+
维护者按「回复」直接回到填报人(该值不含 CR/LF,无法注入头字段);留空则整行不写。`mailto:` 回退路径
|
|
39
|
+
没有 `Reply-To` 可用,同一地址同时留在正文 `Contact:` 行。(早期版本一度完全删掉 `Reply-To`、只留正文行;
|
|
40
|
+
在用户要求「跟 recycle-bin 的效果一样」之后改回现在的形态。)
|
|
41
|
+
- **收件人完全不在页面上出现**:`/state` 只给 `hasRecipient` / `toMasked` 这类摘要,`config` 里也删掉了
|
|
42
|
+
`feedbackAddress`;设置页既没有掩码行,也没有「更换收件人」入口与地址输入框。收件人优先级是**环境变量
|
|
43
|
+
(`DSH_SIDECARD_FEEDBACK_TO` 或共用的 `DSH_CRB_FEEDBACK_TO`)> 用户层/打包配置 `feedbackAddress`**。
|
|
44
|
+
「用邮件客户端发送」向 Host 要一条拼好的 `mailto:` URL 再跳转。
|
|
45
|
+
就绪状态分三种说(没地址 / 有地址缺 SMTP / 齐全),不再把「能用邮件客户端发送」说成「未配置」。
|
|
46
|
+
这条经历**两轮返工**:第一版给了 `type="password"` + 「显示收件人」开关,用户截图指出「这不是直接暴露
|
|
47
|
+
我的邮箱吗」——一键看明文本身就是暴露面;第二版改成只渲染掩码 + 只写更换入口,用户又指出这两块本来
|
|
48
|
+
就不该存在,于是全部删除。
|
|
49
|
+
- **三条出口**:`发送`(收件邮箱 + SMTP 凭证齐备时可用,成功后清空草稿)/ `用邮件客户端发送`(只要收件邮箱,
|
|
50
|
+
拼 `mailto:`,**零凭证**)/ `复制内容`(总是可用,纯文本进剪贴板)。未配置时提交按钮**不置灰**——
|
|
51
|
+
点下去会拿到 503 与原因,比一个死按钮更容易自查。
|
|
52
|
+
- **凭证优先从环境变量来**(后来补上设置页,见下一条):`DSH_SIDECARD_SMTP_URL`(`smtps://user%40example.com:pass@smtp.example.com`;
|
|
53
|
+
也接受共用的 `DSH_CRB_SMTP_URL`)或分开的 `DSH_SIDECARD_SMTP_{HOST,PORT,SECURE,USER,PASS,FROM}`。
|
|
54
|
+
收件邮箱走 `DSH_SIDECARD_FEEDBACK_TO` / `DSH_CRB_FEEDBACK_TO`(**优先**)或配置项 `feedbackAddress`。
|
|
55
|
+
`?from=` / `DSH_SIDECARD_SMTP_FROM` / `USER` 取到的发件地址必须像邮箱,否则整条 SMTP 路径判为未配置。
|
|
56
|
+
- **SMTP 也能直接在设置页填**(用户报「无法发送邮件」之后补的):本机 DSH 进程里没有任何 `DSH_SIDECARD_*`
|
|
57
|
+
变量——普通用户不会给 DSH 设环境变量,所以「凭证只能来自环境变量」等于「发送按钮永远 503」。于是新增
|
|
58
|
+
`feedbackSmtpHost/Port/Secure/User/Pass/From` 六个配置项,折叠在「设置邮件发送(SMTP)」里,保存在
|
|
59
|
+
用户层 `config.json`(仓库外,不进 npm 包)。**密码只写不读**:`/state` 删掉该键、页面永远拿到空串、
|
|
60
|
+
空串保存=保留原值、`/state` 的 `config` 里也没有它。默认端口 465 配隐式 TLS;587 走 STARTTLS。
|
|
61
|
+
顺带修掉一个真 bug:**STARTTLS 现在只在 `EHLO` 回复声明了它时才升级**,否则会给没开 STARTTLS 的中继
|
|
62
|
+
发一个必被拒的动词,把一个正常的明文中继判成「发信失败」。
|
|
63
|
+
- **没有新依赖**:`node:net` + `node:tls` 手写最小 SMTP 客户端(`sendSmtpMessage`)——多行 `250-` 应答汇总、
|
|
64
|
+
`EHLO` → `STARTTLS`(非隐式 TLS 时)→ `AUTH PLAIN`(被拒回退 `AUTH LOGIN`)→ `MAIL FROM` → `RCPT TO` →
|
|
65
|
+
`DATA`(dot-stuffing + `\r\n.\r\n`)→ `QUIT`,20 秒超时,finally 清 socket/timer。
|
|
66
|
+
- **邮件本身**:`text/plain; charset=UTF-8` + base64 正文,主题 RFC 2047 编码(中文标题不乱码),
|
|
67
|
+
正文附插件版本 / DSH 版本 / Node 版本 / 平台 / 时间;**不含主机名、用户名、路径**。
|
|
68
|
+
- **限速与日志**:Host 侧 30 秒 1 封、1 小时 5 封,失败返回 `feedback-too-fast` / `feedback-too-many` 并带
|
|
69
|
+
`retry-after`;只有真正投递成功才计入配额。日志只记 kind 与打码收件人(`o***@example.com`),**从不记正文**。
|
|
70
|
+
- **收件邮箱以 `b64:` 混淆打包**:默认值是维护者收件箱,存成 `b64:<base64>`,所以明文 `name@domain` 不出现在
|
|
71
|
+
仓库与 npm 包里,挡的是正则抓取。**这是混淆不是加密**——`decodeAddress()` 就在同包里,拿到包的人总能还原;
|
|
72
|
+
地址也仍可由用户层配置或环境变量整体接管。早期版本默认空字符串、要求每个部署方自己配;用户要求
|
|
73
|
+
「跟 recycle-bin 的效果一样」之后改成现在这样,代价是维护者邮箱随包公开(可接受:反馈本来就该寄给它)。
|
|
74
|
+
- 错误码契约:`feedback-invalid`(400,边界/头注入)、`feedback-unconfigured`(503,缺收件人或缺 SMTP)、
|
|
75
|
+
`feedback-too-fast` / `feedback-too-many`(429)、`feedback-send-failed`(502,附服务器原文)。
|
|
76
|
+
`/state` 的 `capabilities.feedback` 给出 `{transport, configured, toMasked, reason, limits}`,设置页据此显示就绪状态。
|
|
77
|
+
- 顺手把深色主题下的主按钮字色改成深墨(`.dsa-btn-primary`):这是全站一直存在的 2.79:1,不是本次引入,
|
|
78
|
+
但新卡片里的「发送」正好是主按钮,一并修掉。
|
|
79
|
+
- 修复了一处顺带发现的真问题:`writeJson` 用 `res.writeHead(status, {...})` 会**丢弃**此前 `setHeader` 的值,
|
|
80
|
+
429 的 `retry-after` 因此一直发不出去;`writeJson/writeError` 现在接受 `extraHeaders`。
|
|
81
|
+
- 测试:`contract` 新增 **232** 项(纯函数边界、`parseSmtpUrl`/`readSmtpFromEnv` 两种写法(含 `DSH_CRB_SMTP_URL`
|
|
82
|
+
别名)、`b64:` 收件人解码、「环境变量优先于配置项」的优先级回归、`Reply-To:` 组头、组信的 base64 与 RFC 2047、
|
|
83
|
+
限速闸门,以及一条**真 socket** 的 `/feedback` 端到端——本地 SMTP 假中继逐条断言 `AUTH PLAIN`、信封、`DATA`、
|
|
84
|
+
编码后的主题与正文、限速 429 与 `retry-after`,并断言正文**没有进日志**)。
|
|
85
|
+
总项数 1359 → **1582**(verify 57 + contract 1221 + float 78 + smoke 226)。
|
|
86
|
+
- **同日二次配置(用户要求「跟 dsh-conversation-recycle-bin 的效果一样」)**,四处对齐:
|
|
87
|
+
1. 反馈横幅**默认收起**成一行,主按钮点开才展开表单(原来默认展开,把真正的设置项挤到屏幕外);
|
|
88
|
+
2. 「联系方式」改成**你的邮箱**:`type="email"` + 客户端格式预校验 + Host 侧再校验,填了作为 `Reply-To:`;
|
|
89
|
+
3. 收件人默认值改为**打包的 `b64:` 混淆地址**,并让**环境变量优先**于它(原来配置优先,一旦默认非空,
|
|
90
|
+
环境变量就永久失效);`feedbackAddress` 也接受 `b64:` 形式并在解码后校验;
|
|
91
|
+
4. 直接复用兄弟插件的 `DSH_CRB_SMTP_URL` / `DSH_CRB_FEEDBACK_TO`:本机只配一次,两个插件都能发信。
|
|
92
|
+
实测(本机用户级 `DSH_CRB_SMTP_URL` 注入后):`describeFeedback()` → `configured:true, transport:'smtp'`。
|
|
93
|
+
- **如实标注**:
|
|
94
|
+
1. 只对**本地假中继**验证过协议,没有连过真实服务商(Gmail / QQ / 163 的应用专用密码、白名单、
|
|
95
|
+
`MAIL FROM` 与 `USER` 同域等要求都未实测);
|
|
96
|
+
2. 设置页的**展开交互**仍无法在合成 DOM 里断言——`test/harness.mjs` 的 `useState` 不触发重渲染;测试覆盖的是
|
|
97
|
+
「默认收起(`style.display:none`)、按钮存在、`aria-expanded='false'`、字段齐全」,展开后的输入与提交需在页面上手测;
|
|
98
|
+
3. 「用邮件客户端发送」只验证了 URL 的编码,没有真的拉起系统邮件应用;
|
|
99
|
+
4. 只有纯文本,没有附件/截图上传(见交付说明里的待确认项)。
|
|
100
|
+
|
|
101
|
+
## 1.8.4
|
|
102
|
+
|
|
103
|
+
**主题:选区旁的动作条从「六枚等价胶囊排成一排」改成一块面板加一个主操作——并顺手堵掉右侧被窗口裁掉的那条路。**
|
|
104
|
+
|
|
105
|
+
起因是用户截图(574×42):「💬 追问选中内容」后面紧接 `解释 / 翻译 / 总结 / 润色 / 纠错` 五枚带边框胶囊,
|
|
106
|
+
全部挤在一行,右端被窗口边缘切断;用户的意见是「全部排成一排不好看」。
|
|
107
|
+
|
|
108
|
+
- **诊断(不是「一行」的问题,是层级与预算两个问题)**:
|
|
109
|
+
1. 六枚胶囊同色同粗、各自带边框与阴影,读起来像六个并列功能,没有主操作;
|
|
110
|
+
2. 定位宽度是**估算**出来的(`estimateLabelWidth(label) + 24` 相加),它只用于夹取 `left`,
|
|
111
|
+
一旦估小或选区靠右,整条就会顶出右边缘——`max-width:min(620px,92vw)` 也挡不住,
|
|
112
|
+
因为它是相对视口的,不是相对面板左边剩下的空间。
|
|
113
|
+
- **单一面板**:`.dsa-quickbar` 从「无底色的 flex 行」改成一块控件——`padding:3px`、`border-radius:12px`、
|
|
114
|
+
中灰描边 `rgba(127,127,127,.5)`、`0 6px 20px rgba(0,0,0,.16)` 投影、`background:bg-overlay`。
|
|
115
|
+
- **一个主操作**:`.dsa-quickbar .dsa-trigger-embedded` 改成品牌实底 + 白字 + 600 字重;
|
|
116
|
+
深色主题改深墨字 `#0a1428`(本机 uiskin 深色品牌 `#5b9cf5` 上白字实测 **2.79:1**,低于 4.5:1 正文下限;
|
|
117
|
+
深墨 **6.57:1**),与 1.8.2 的分段芯片同一处理。
|
|
118
|
+
- **一条细分隔线**:`.dsa-quickbar-sep`(1px × 15px,`border-l1`)分开「做那一件事」与「或从现成问题里挑一个」,
|
|
119
|
+
只在有快捷动作时渲染。
|
|
120
|
+
- **五个动作降级为幽灵胶囊**:`.dsa-quickbar .dsa-chip` 去掉边框/底色/阴影,字色 `label-secondary`,
|
|
121
|
+
hover 才浮出 `bg-layer-2` 底与 `label-primary` 字色。弹层里的 chip 与右键菜单不受影响。
|
|
122
|
+
- **宽度改成算出来的**:`TriggerBar` 读 `window.innerWidth`,给面板加
|
|
123
|
+
`max-width: min(620, 视口宽 − 面板左边 − 8)`,并用 `${Math.min(620, room)}px` 落到内联样式;
|
|
124
|
+
放不下就换行(`flex-wrap:wrap`),不再被窗口切掉。`window` 不存在时(测试/SSR)取 620。
|
|
125
|
+
- **行为零变化**:键盘漫游(←/→/Home/End)、`Alt+1…5`、右键菜单、点芯片直接发问都照旧;
|
|
126
|
+
不新增配置项——这是版式修正,不需要开关。
|
|
127
|
+
- 测试:`contract` 新增 **9** 项(渲染侧 3 条:恰好一个 `.dsa-quickbar-sep`、chips 自成一组、
|
|
128
|
+
主按钮是 `dsa-trigger-embedded` 变体;样式侧 6 条:单一面板、品牌实底主按钮、chips 去边框、
|
|
129
|
+
`.dsa-quickbar-sep` 规则存在、深色墨字覆盖、面板自带 `maxWidth` 上限)。总项数 1350 → **1359**。
|
|
130
|
+
- **如实标注**:交付时给的四张图(`design-preview/10-quickbar-*.png`)是**离屏渲染**——
|
|
131
|
+
真 CSS 从 `client.js` 抽、主题值取自本机 uiskin 皮肤、无头 Edge 截图,不是运行中页面的截图;
|
|
132
|
+
hover / focus 态与真实选区定位没有截图证明,需要在页面上手测(刷新即生效,`link:` 安装)。
|
|
133
|
+
- 顺手把 README §4.3 补成这一条的外观说明(含深色原因、换行行为与「不写配置项」的结论)。
|
|
134
|
+
|
|
3
135
|
## 1.8.3
|
|
4
136
|
|
|
5
137
|
**主题:右侧栏打开时,答案卡直接铺满它、读成栏内的一页——而不是 340px 的浮层压在右栏正文上。**
|
package/README.md
CHANGED
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
聊天记录、任务详情、右侧栏内容……只要是能选中的文本都能用;**没装侧边卡片插件也能跑**(自动回退到内置浮层卡片)。
|
|
10
10
|
|
|
11
|
+
> **1.8.4 增强一览**(详见 CHANGELOG):选区旁的动作条不再「六枚等价胶囊排成一排」——改成**一块面板 + 一个主操作**:
|
|
12
|
+
> 品牌实底按钮「追问选中内容」、一条细分隔线、五个无边框文字胶囊(hover 才浮出底色),深色主题把主按钮
|
|
13
|
+
> 的字改成深墨色(白字在深色品牌上只有 2.79:1);宽度也改成按「视口宽 − 面板左边 − 8」算,
|
|
14
|
+
> 放不下就换行,不再被窗口右边缘切掉。见 §4.3。
|
|
15
|
+
|
|
11
16
|
> **1.8.0 增强一览**(P2,详见 CHANGELOG):**回答风格档位**(`answerStyle` 精简/标准/详细)
|
|
12
17
|
> 与 ≤500 字的自定义指令;改写类答案可**写回主对话输入框**(先做了真实页面探针,走官方
|
|
13
18
|
> `inputActions.insertText`,DOM 路径退化为 `beforeinput` 追加);卡片与历史条目可**查看原文**
|
|
@@ -176,6 +181,13 @@ plugin_manager(action="install_bundle", target="D:\\Users\\34332\\AI\\dsh-sideca
|
|
|
176
181
|
| `answerStyle` | `concise` \| `standard` \| `detailed` | `standard` | **回答风格档位**:只给最小完整答案 / 现状 / 必要时展开并给出依据。指令追加在人格之后,**不会**覆盖"用提问的语言作答"与"选中文本是数据"这两条 | 下一次提问 |
|
|
177
182
|
| `customPersona` | 字符串,≤500 码点 | `''` | **自定义指令**:追加在风格档位之后,以 `Additional user instruction:` 标注随每次提问发送(例如"尽量给可验证来源") | 下一次提问 |
|
|
178
183
|
| `followUpSuggestions` | 布尔 | `false` | **模型生成的建议追问**:让模型在答案末尾用 `<followups>问1 \| 问2</followups>` 给出 2–3 个可点的下一步问题(客户端剥离该块并渲染为芯片,**不额外发请求**) | 下一次提问 |
|
|
184
|
+
| `feedbackAddress` | 字符串(邮箱或 `b64:` 编码)\| `''` | 维护者收件箱(`b64:` 混淆存储) | **反馈收件邮箱**:设置页「反馈」弹窗把问题与建议发到这里。默认值指向维护者自己的邮箱,以 `b64:<base64>` 形式打包——**这是混淆不是加密**,`decodeAddress()` 就在同包里,只能挡住正则抓取;环境变量 `DSH_SIDECARD_FEEDBACK_TO` / `DSH_CRB_FEEDBACK_TO` **优先于**本项,置空则只留「用邮件客户端发送」回退路径 | 立即 |
|
|
185
|
+
| `feedbackSmtpHost` | 字符串,≤255 | `''` | **SMTP 服务器**。1.9.1 起只从**环境变量或用户层配置**读。**环境变量优先于配置**(`DSH_SIDECARD_SMTP_URL` / `DSH_CRB_SMTP_URL` 等能凑出完整端点时,配置里的整段不生效);只有用户层配置会存它,**不进仓库** | 立即 |
|
|
186
|
+
| `feedbackSmtpPort` | 数字,1–65535 | `465` | SMTP 端口。465 配隐式 TLS,587 配 STARTTLS(服务器没声明 STARTTLS 时不会硬升级) | 立即 |
|
|
187
|
+
| `feedbackSmtpSecure` | 布尔 | `true` | 隐式 TLS(465)。关掉则按 STARTTLS 处理 | 立即 |
|
|
188
|
+
| `feedbackSmtpUser` | 字符串,≤254 | `''` | SMTP 登录账号。部分中继给的是裸账号名而非邮箱,故不强制邮箱格式 | 立即 |
|
|
189
|
+
| `feedbackSmtpPass` | 字符串,≤512 | `''` | **密码 / 授权码**(QQ、163 必须用授权码)。**只写不读**:`/state` 会删掉它、页面永远拿到空串;留空保存=保留原值 | 立即 |
|
|
190
|
+
| `feedbackSmtpFrom` | 字符串(邮箱)\| `''` | `''` | 发信账号显式指定的 `From:`,留空等于 `feedbackSmtpUser`。必须是 SMTP 服务商允许的地址 | 立即 |
|
|
179
191
|
|
|
180
192
|
非法值会被拒绝(设置页报错、接口返回 `invalid-config`),并把被忽略的项列在 `/state` 的 `problems` 里——不会静默吞掉。
|
|
181
193
|
|
|
@@ -232,6 +244,115 @@ bleeds = 对话区右边界 - left > 1 // 只有带宽 < 240px 时才
|
|
|
232
244
|
| `off` | 设置里的 `dockRail` 被关掉了 |
|
|
233
245
|
| `mode` | 当前不是完整卡片形态(胶囊 / 隐藏),本来就不该铺满 |
|
|
234
246
|
|
|
247
|
+
### 4.3 选区旁的动作条(外观)
|
|
248
|
+
|
|
249
|
+
选中文本后浮出的那条东西以前是**六枚等价的胶囊排成一排**(`追问选中内容` 与 `解释/翻译/总结/润色/纠错`),
|
|
250
|
+
彼此同色同粗、各带边框和阴影:既没有主次,也没有分组,窗口窄一点或选区靠右时整条还会顶出右边缘被裁掉。
|
|
251
|
+
现在它是**一块面板 + 一个主操作**:
|
|
252
|
+
|
|
253
|
+
- 面板 `.dsa-quickbar`:`padding:3px`、`border-radius:12px`、中灰描边、`0 6px 20px` 投影,整条读作一个控件;
|
|
254
|
+
- 主按钮(`.dsa-trigger-embedded`):品牌实底 + 白字 + 600 字重(深色主题改为深墨字,原因同 §九 A 的分段芯片:
|
|
255
|
+
uiskin 深色品牌 `#5b9cf5` 上白字只有 2.79:1);
|
|
256
|
+
- 细分隔线 `.dsa-quickbar-sep`(1px × 15px,`border-l1`)把「做那一件事」和「或从这些现成问题里挑一个」分开;
|
|
257
|
+
- 五个快捷动作降级为**无边框文字胶囊**(`.dsa-quickbar .dsa-chip`):透明底 + `label-secondary` 字色,
|
|
258
|
+
hover 才浮出 `bg-layer-2` 底与 `label-primary` 字色——层级低于主按钮,但仍然一眼可点;
|
|
259
|
+
- **宽度不再靠估算**:组件给面板加 `max-width: min(620, 视口宽 - 面板左边 - 8)`,放不下就换行,
|
|
260
|
+
不会被窗口边缘切掉;分隔线随主按钮留在第一行,换行的胶囊继续从左边排。
|
|
261
|
+
|
|
262
|
+
外观之外没有行为变化:键盘漫游、`Alt+1…5`、右键菜单、点击后直接发问都照旧。这条也不写配置项——
|
|
263
|
+
它是版式修正,不需要开关。
|
|
264
|
+
|
|
265
|
+
### 4.4 设置页的「反馈」入口(邮件)
|
|
266
|
+
|
|
267
|
+
设置页**最顶部**是一条品牌色描边的**反馈横幅**(`dsa-feedback-bar`):一行文字 + 右侧主按钮,**点开才是表单**,
|
|
268
|
+
表单放在一个居中对话框(`role="dialog"`,`Esc` / 点背景 / 右上角 `×` 都能关)。1.9.1 之前是「顶部常驻内联表单」,
|
|
269
|
+
它把真正的设置项全挤到屏幕外,被否掉后重做成现在这种「一条横幅 + 一个弹窗」。表单字段:类型(问题 / 建议 / 疑问 / 其他)、
|
|
270
|
+
标题、详细描述、可选的**你的邮箱**(填了就写进邮件的 `Reply-To:`,维护者按「回复」能直接回给你;留空即匿名)。
|
|
271
|
+
**设置页上没有任何 SMTP 输入框**:发信凭据属于部署方/维护者,走环境变量或用户层配置(见下),填报人不用管。
|
|
272
|
+
三条出口,从首选到兜底:
|
|
273
|
+
|
|
274
|
+
| 出口 | 何时可用 | 说明 |
|
|
275
|
+
|---|---|---|
|
|
276
|
+
| **发送**(推荐) | 收件邮箱已配置 **且** SMTP 凭证可用(环境变量或用户层配置) | 插件直接把邮件投给 SMTP 服务器;成功后清空草稿 |
|
|
277
|
+
| **用邮件客户端发送** | 只要有收件邮箱 | 拼一个 `mailto:` 交给系统邮件应用,**不需要任何凭证** |
|
|
278
|
+
| **复制内容** | 总是 | 把同样的纯文本(含版本与环境)放进剪贴板,用户自己粘到任意渠道 |
|
|
279
|
+
|
|
280
|
+
**收件邮箱默认可用,也可被环境变量接管。** 打包默认值是维护者邮箱(`b64:` 混淆存储,见上表);
|
|
281
|
+
环境变量 `DSH_SIDECARD_FEEDBACK_TO` 或 `DSH_CRB_FEEDBACK_TO`(与 dsh-conversation-recycle-bin 共用)
|
|
282
|
+
**优先**于它。用户层配置落在 `$DSH_HOME/sidecard-ask/config.json`
|
|
283
|
+
(Windows 默认 `C:\Users\<你>\.dsh\sidecard-ask\config.json`)——**不在插件目录里,因此不会被提交、
|
|
284
|
+
不会进 npm 包**。
|
|
285
|
+
|
|
286
|
+
**SMTP 凭证有两种给法**,都落在部署方/维护者这一侧(1.9.1 起设置页不再提供填写框):
|
|
287
|
+
|
|
288
|
+
1. **用户层配置**(`$DSH_HOME/sidecard-ask/config.json`,Windows 默认
|
|
289
|
+
`C:\Users\<你>\.dsh\sidecard-ask\config.json`,或 profile 的 `cordis.patch.yml` 里给该行写 `config`):
|
|
290
|
+
`feedbackSmtpHost` / `feedbackSmtpPort` / `feedbackSmtpSecure` / `feedbackSmtpUser` / `feedbackSmtpPass` /
|
|
291
|
+
`feedbackSmtpFrom`。**密码只写不读**:`/state` 删除该键、页面永远拿到空串,留空保存表示保留原值。
|
|
292
|
+
QQ 邮箱请填 `smtp.qq.com` / 465 / `secure: true` / 你的 QQ 邮箱 / **授权码**
|
|
293
|
+
(不是 QQ 密码,要在 QQ 邮箱后台「设置 → 账户 → POP3/SMTP 服务」里生成)。
|
|
294
|
+
2. **环境变量**(推荐,**优先级高于用户层配置**),有两种写法:
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
# 写法一:一个 URL(smtps = 隐式 TLS,端口默认 465;smtp = STARTTLS,端口默认 587)
|
|
298
|
+
DSH_SIDECARD_SMTP_URL="smtps://user%40example.com:app-password@smtp.example.com"
|
|
299
|
+
# 也可以直接复用 dsh-conversation-recycle-bin 的那一个:
|
|
300
|
+
DSH_CRB_SMTP_URL="smtps://user%40example.com:app-password@smtp.example.com"
|
|
301
|
+
|
|
302
|
+
# 写法二:分开写(密码里带 @ 或 : 时不用手动转义)
|
|
303
|
+
DSH_SIDECARD_SMTP_HOST="smtp.example.com"
|
|
304
|
+
DSH_SIDECARD_SMTP_PORT="465"
|
|
305
|
+
DSH_SIDECARD_SMTP_SECURE="1" # 1/true/yes = 隐式 TLS
|
|
306
|
+
DSH_SIDECARD_SMTP_USER="user@example.com"
|
|
307
|
+
DSH_SIDECARD_SMTP_PASS="app-password"
|
|
308
|
+
DSH_SIDECARD_SMTP_FROM="user@example.com" # 可选,默认等于 USER
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
补充约定(都会体现在 `/state` 的 `capabilities.feedback` 里,反馈弹窗据此显示「已就绪 / 未配置」):
|
|
312
|
+
|
|
313
|
+
- **环境变量优先于用户层配置**,而且只认「完整的那一套」:环境里能凑出主机+账号+密码就用环境(`smtp.source = 'env'`),
|
|
314
|
+
否则才看用户层 `config.json` 填的;两边都不完整就报「还没填全」。这条顺序是有原因的——本机曾出现「环境变量是好的、
|
|
315
|
+
配置里留着半套(填了主机没填密码)」,旧实现先看配置,于是一套能用的凭证被半套配置挡住,
|
|
316
|
+
反馈永远发不出去。1.9.1 起设置页不自带 SMTP 表单,这条优先级只作用于「环境变量 vs 用户层配置」。
|
|
317
|
+
|
|
318
|
+
- **发件人必须像邮箱**:`?from=` / `DSH_SIDECARD_SMTP_FROM` / `USER` 三者取到的地址必须通过邮箱格式检查,
|
|
319
|
+
否则整条 SMTP 路径判为未配置——`MAIL FROM` 里塞不进一个合法地址就该早点说清楚。
|
|
320
|
+
- **STARTTLS 只在服务器声明时才升级**:先看 `EHLO` 的多行回复里有没有 `STARTTLS`,没有就直接明文对话;
|
|
321
|
+
否则一个没开 STARTTLS 的内网中继会被自己的升级请求判成「发信失败」。
|
|
322
|
+
- 本地/内网中继可以用 `smtp://user:pass@host:25?starttls=0` 关掉升级;**不要**对公网服务器这么写。
|
|
323
|
+
- 邮件正文是 `text/plain; charset=UTF-8` + base64,主题用 RFC 2047 编码(中文标题不会变乱码);
|
|
324
|
+
正文里附插件版本、DSH 版本、Node 版本、平台与时间,**不含主机名、用户名或任何路径**。
|
|
325
|
+
- 限速在 Host 侧:30 秒内最多一封、1 小时内最多 5 封;失败返回 `feedback-too-fast` /
|
|
326
|
+
`feedback-too-many` 并带 `retry-after` 头。只有**真正投递成功**才计入配额,中继故障不烧配额。
|
|
327
|
+
- 日志只记 kind 与**打码后的**收件人(`o***@example.com`),**从不记录正文**。
|
|
328
|
+
- **收件人从页面里彻底消失**(两轮返工的终点:先删掉「显示收件人」开关,再按用户要求删掉掩码行与
|
|
329
|
+
「更换收件人」按钮):
|
|
330
|
+
- `/state` 的 `capabilities.feedback` 只给 `hasRecipient` / `toMasked` 这类摘要,**没有** `to`;
|
|
331
|
+
- `/state` 的 `config` 里也**删掉了** `feedbackAddress`,页面拿不到它;
|
|
332
|
+
- 设置页不显示收件人的任何形式——没有掩码行、没有更换入口、没有地址输入框。收件人只由用户层配置
|
|
333
|
+
`feedbackAddress` 或环境变量 `DSH_SIDECARD_FEEDBACK_TO` 提供,那是部署方/维护者的事,填写者不用管;
|
|
334
|
+
- 「用邮件客户端发送」点击时向 Host 要一条**拼好的** `mailto:` URL(`POST /feedback-mailto`),
|
|
335
|
+
浏览器只负责跳转,不自己拼地址。
|
|
336
|
+
- **`From:` 仍是维护者自己的 SMTP 账号,但填报人的邮箱会写进 `Reply-To:`**:QQ/163 会拒收不属于本账号的
|
|
337
|
+
`From:`(553),所以发件身份不变;填报人在「你的邮箱」里填了地址,Host 就把它作为 `Reply-To:` 写进邮件头,
|
|
338
|
+
维护者按「回复」直接回到填报人。该值在 Host 侧先过邮箱格式校验且不含 CR/LF,因此无法注入头字段;
|
|
339
|
+
留空则整行不写(匿名反馈)。`mailto:` 回退路径里没有 `Reply-To` 可用,同一个地址同时留在正文 `Contact:` 行。
|
|
340
|
+
|
|
341
|
+
- 三种就绪状态在界面上分开说,不混成一个「未配置」:没有收件人 → 提示配置;有收件人但没有 SMTP →
|
|
342
|
+
提示「缺的只是发信凭据,现在可用『用邮件客户端发送』」;齐全 → 什么都不提示。
|
|
343
|
+
- **关于「加密」的实话**:这里的邮箱只是一次 SMTP 的收件地址,插件必须能解出明文才能发信,任何随插件
|
|
344
|
+
分发的「加密」都等于把钥匙一起发出去,只能算混淆——打包默认值用的 `b64:` 就是这种混淆,只挡正则抓取。
|
|
345
|
+
真正有效的保护是**只存本机配置 + 明文不进浏览器 + 日志打码 + 可被环境变量整体接管**——这四条都已实现。
|
|
346
|
+
仍然避免不了的两处:`mailto:` URL 本身必然含明文(由 Host 组好直接交给系统邮件应用,不经页面渲染),
|
|
347
|
+
以及**已经提交进 git 历史的作者邮箱**(见下文)。
|
|
348
|
+
|
|
349
|
+
失败排查顺序:`/state` 的 `capabilities.feedback.reason`(它会直接说「账号或密码还没填全」这类话)→
|
|
350
|
+
用户层配置是否整行都在(服务器/端口/账号/密码;**端口必须是数字**,字符串会被判非法)→ 环境变量是否
|
|
351
|
+
被 DSH 进程继承(改完要重启 DSH)→ SMTP 服务商是否要求**应用专用密码**(Gmail / QQ 邮箱 / 163 都要求,
|
|
352
|
+
登录密码会被拒)→ 端口与 TLS 写法是否匹配(465 用隐式 TLS;587 让服务器自己声明 STARTTLS)→
|
|
353
|
+
中继是否在白名单里要求 `FROM` 与 `USER` 同域。
|
|
354
|
+
`feedback-send-failed` 的 message 是服务器原文,直接读它比猜快。
|
|
355
|
+
|
|
235
356
|
---
|
|
236
357
|
|
|
237
358
|
## 五、侧边承载面与"侧边卡片插件"适配
|
|
@@ -641,13 +762,22 @@ node tools/compat-probe.mjs --json tools/.cache/matrix.json
|
|
|
641
762
|
cd D:\Users\34332\AI\dsh-sidecard-ask
|
|
642
763
|
node test/verify.mjs # 静态:清单/补丁/导出面/i18n/零依赖
|
|
643
764
|
node test/contract-test.mjs # 契约:常量、信封、SSE 逐帧、纯函数、槽位注册与渲染
|
|
644
|
-
node test/float-placement.mjs #
|
|
645
|
-
node test/smoke-test.mjs #
|
|
765
|
+
node test/float-placement.mjs # 浮层定位矩阵
|
|
766
|
+
node test/smoke-test.mjs # 冒烟:装载、槽位、渲染、错误路径
|
|
646
767
|
```
|
|
647
768
|
|
|
769
|
+
当前规模(本机实测,四项均 exit 0):verify **57** 项 + contract **1239** 项 + float-placement **78** 项 +
|
|
770
|
+
smoke **226** 项 = **1600 项全部通过**(较 1.8.4 的 1359 多 241 项;contract 从 989 涨到 1239,新增大多来自
|
|
771
|
+
反馈功能:
|
|
772
|
+
纯函数边界、`parseSmtpUrl` / `readSmtpFromEnv` 两种写法(含复用 `DSH_CRB_SMTP_URL`)、`b64:` 收件人解码、
|
|
773
|
+
「环境变量优先于用户层配置」的优先级回归、`Reply-To:` 组头、限速闸门,以及一条**真 socket** 的 `/feedback`
|
|
774
|
+
端到端——本地 SMTP 假中继逐条断言 `AUTH PLAIN`、信封、`DATA`、编码后的主题与正文、429 与 `retry-after`,
|
|
775
|
+
并断言正文没有进日志;1.9.1 又把内联表单的断言整块换成「横幅一行 + 弹窗表单」的断言)。
|
|
776
|
+
|
|
648
777
|
四个脚本都以 `process.exitCode` 反映结果,失败会列出具体条目;测试会把 `DSH_HOME` 指向临时目录,不会污染真实配置。
|
|
649
|
-
|
|
650
|
-
|
|
778
|
+
1.8.4 为 1359 项:contract 989 → 998,新增 6 条钉住选区动作条的版式(单一面板、品牌实底主按钮、
|
|
779
|
+
chips 去边框、分隔线规则、深色墨字覆盖、面板自带 `maxWidth` 上限)。
|
|
780
|
+
1.8.3 为 1350 项:contract 972 → 989,新增 8 项钉住设置页的「占满
|
|
651
781
|
右侧栏」复选框、3 个新 i18n 键,以及承载面 `auto` 的新顺序;float-placement 38 → 78,新增 40 项覆盖 `railDock` 的夹取/最小尺寸/
|
|
652
782
|
`clipped` 语义、真机上 dock 没生效那次发现的三个缺陷(残留的 `rail` 变量、右栏开合不触发重算、
|
|
653
783
|
右栏锚点必须优先用插件自己的 `[data-dsa-rail-host]` 节点),以及「自动链里不出现原生右侧栏」这条顺序断言)。更早的 1.8.1 为 1277 项,其中 contract
|