@zaofan/dsh-qqbot 1.11.1 → 1.11.2

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.
@@ -693,6 +693,15 @@ window.__ModuleLoader__.load({
693
693
  * dock.members() 群友台账(谁说过话、什么印象分)
694
694
  * 这些走 /api/qqbot-settings/dock-data,scope=当前数据根(每实例一份,天然隔离)。
695
695
  */
696
+ /** 宿主是不是暗色主题(宿主 ThemePresenter 给 body 加 data-ds-dark-theme 属性) */
697
+ function extUiIsDark() {
698
+ try {
699
+ if (document.body && document.body.hasAttribute('data-ds-dark-theme')) return true
700
+ // 兜底:宿主没给属性时看系统偏好
701
+ if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) return true
702
+ } catch (e) { /* ignore */ }
703
+ return false
704
+ }
696
705
  /** 读当前 web 会话 id(宿主的 localStorage,和「当前会话」检测同一个源) */
697
706
  function extUiCurrentSid() {
698
707
  try {
@@ -749,6 +758,35 @@ window.__ModuleLoader__.load({
749
758
  .catch(function () { return null })
750
759
  },
751
760
  toast: function (m) { try { window.alert(String(m)) } catch (e) { /* ignore */ } },
761
+
762
+ /**
763
+ * 主题(2026-10-27 主人问:自定义 UI 能跟随宿主主题吗?—— 能)
764
+ *
765
+ * 两条路,**优先用 CSS 变量**:
766
+ * ① CSS 变量(推荐):插件在 body 上定义了 --qqs-* token,亮/暗两套自动切换。
767
+ * 自定义属性会**穿透 Shadow DOM**,卡片里直接写 var(--qqs-text) 就行。
768
+ * 常用:--qqs-card / --qqs-panel / --qqs-text / --qqs-text2 / --qqs-text3
769
+ * --qqs-border / --qqs-input / --qqs-toolbar / --qqs-primary-fg
770
+ * ② JS 判断(要按主题做逻辑分支时):theme / isDark() / onThemeChange(fn)
771
+ * 宿主切主题时给 body 加 data-ds-dark-theme 属性,这里监听它。
772
+ */
773
+ get theme() { return extUiIsDark() ? 'dark' : 'light' },
774
+ isDark: function () { return extUiIsDark() },
775
+ onThemeChange: function (fn) {
776
+ if (typeof fn !== 'function') return function () {}
777
+ var handler = function () { try { fn(extUiIsDark() ? 'dark' : 'light') } catch (e) { /* ignore */ } }
778
+ try {
779
+ var ob = new MutationObserver(handler)
780
+ ob.observe(document.body, { attributes: true, attributeFilter: ['data-ds-dark-theme', 'class'] })
781
+ var mq = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null
782
+ if (mq && mq.addEventListener) mq.addEventListener('change', handler)
783
+ return function () {
784
+ try { ob.disconnect() } catch (e) { /* ignore */ }
785
+ try { if (mq && mq.removeEventListener) mq.removeEventListener('change', handler) } catch (e) { /* ignore */ }
786
+ }
787
+ } catch (e) { return function () {} }
788
+ },
789
+
752
790
  /**
753
791
  * 能力调用(2026-10-27 主人要求:UI 卡片要和其余扩展对齐能力)
754
792
  *
@@ -109,8 +109,35 @@ qqbotExt.cap('text' | 'markdown', 文本) // 发消息
109
109
 
110
110
  // 自己的后端(见 §7)
111
111
  qqbotExt.api('/你的路由') // → /api/ext/dispatch?id=<你>&p=/你的路由
112
+
113
+ // 主题
114
+ qqbotExt.theme // 'light' | 'dark'
115
+ qqbotExt.isDark()
116
+ qqbotExt.onThemeChange(cb) // 返回取消函数
117
+ ```
118
+
119
+ **让卡片跟着宿主主题变**:插件在 `body` 上定义了一套 `--qqs-*` token,亮/暗两套自动切换,
120
+ 而 **CSS 自定义属性会穿透 Shadow DOM** —— 所以卡片里直接用变量就行,不用自己判断:
121
+
122
+ ```css
123
+ .card {
124
+ background: var(--qqs-card, #fff);
125
+ color: var(--qqs-text, #1f2329);
126
+ border: 1px solid var(--qqs-border, #e6e8ec);
127
+ }
112
128
  ```
113
129
 
130
+ 可用 token:
131
+
132
+ | 类别 | 变量 |
133
+ |---|---|
134
+ | 背景 | `--qqs-card` `--qqs-panel` `--qqs-card-solid` `--qqs-input` `--qqs-toolbar` |
135
+ | 文字 | `--qqs-text` `--qqs-text2` `--qqs-text3` |
136
+ | 边框 | `--qqs-border` `--qqs-border-light` |
137
+ | 主题色 | `--qqs-primary-bg` `--qqs-primary-border` `--qqs-primary-fg` |
138
+
139
+ **一定要带 fallback**(`var(--qqs-text, #1f2329)`)—— 老版本宿主的 token 可能不全。
140
+
114
141
  **写卡片的几条硬边界**(都是设计如此,不是 bug):
115
142
 
116
143
  1. **样式双向隔离**:Shadow DOM 让卡片样式不污染面板,但**面板的 CSS 变量/组件样式也进不来**,得自己写。
@@ -1,55 +1,58 @@
1
- /* 扩展示例 · 两处卡片共用的样式(Shadow DOM 内,不影响面板其他地方) */
2
- :host { display: block; }
3
- .card {
4
- background: #fff; border: 1px solid #e6e8ec; border-radius: 12px;
5
- padding: 10px 12px; font: 13px -apple-system, 'PingFang SC', 'Microsoft YaHei', sans-serif;
6
- color: #1f2329; box-shadow: 0 1px 2px rgba(20,30,60,.05);
7
- }
8
- .hd { display: flex; align-items: center; gap: 6px; font-weight: 700; margin-bottom: 8px; }
9
- .dot { width: 7px; height: 7px; border-radius: 50%; background: #bbb; flex: none; }
10
- .dot.on { background: #16a34a; }
11
- .dot.err { background: #dc2626; }
12
- .row { display: flex; align-items: center; gap: 6px; margin: 6px 0; font-size: 12px; }
13
- .lbl { color: #6b7280; flex: none; }
14
- .val { color: #1f2329; }
15
- .hint { color: #9aa0a8; font-size: 11px; line-height: 1.5; }
16
- .txt {
17
- flex: 1; min-width: 160px; font: inherit; padding: 6px 9px;
18
- border: 1px solid #d0d5dd; border-radius: 8px; outline: none; color: #1f2329;
19
- }
20
- .txt:focus { border-color: #7c6cf0; }
21
- .acts { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
22
- .btn {
23
- font: inherit; font-size: 12px; padding: 5px 12px; border-radius: 8px;
24
- border: 1px solid #d9c6ff; background: #f1ecff; color: #4a3a9f; cursor: pointer;
25
- }
26
- .btn:hover { background: #e9e1ff; }
27
- .btn:disabled { opacity: .5; cursor: default; }
28
- .btn.ghost { background: #fff; border-color: #d0d5dd; color: #4b5563; }
29
-
30
- /* dock 卡片专用:顶部一排子标签 */
31
- .subtabs { display: flex; gap: 4px; border-bottom: 1px solid #eee; margin-bottom: 10px; }
32
- .subtabs button {
33
- font: inherit; font-size: 13px; padding: 6px 14px; border: none; background: none;
34
- cursor: pointer; color: #666; border-bottom: 2px solid transparent;
35
- }
36
- .subtabs button.on { color: #4a3a9f; font-weight: 700; border-bottom-color: #7c6cf0; }
37
- .pane { display: none; }
38
- .pane.on { display: block; }
39
- pre.info {
40
- background: #f6f7f9; border: 1px solid #e6e8ec; border-radius: 8px;
41
- padding: 8px 10px; font-size: 11px; line-height: 1.6; max-height: 180px; overflow: auto;
42
- white-space: pre-wrap; word-break: break-all; margin: 4px 0 0;
43
- }
1
+ /* 颜色全部走 --qqs-* 变量(带 fallback)——
2
+ 宿主的亮/暗 token 定义在 body 上,自定义属性会穿透 Shadow DOM,
3
+ 所以卡片跟着宿主主题自动切换,不用自己判断。 */
4
+ /* 扩展示例 · 两处卡片共用的样式(Shadow DOM 内,不影响面板其他地方) */
5
+ :host { display: block; }
6
+ .card {
7
+ background: var(--qqs-card, #fff); border: 1px solid var(--qqs-border, #e6e8ec); border-radius: 12px;
8
+ padding: 10px 12px; font: 13px -apple-system, 'PingFang SC', 'Microsoft YaHei', sans-serif;
9
+ color: var(--qqs-text, #1f2329); box-shadow: 0 1px 2px rgba(20,30,60,.05);
10
+ }
11
+ .hd { display: flex; align-items: center; gap: 6px; font-weight: 700; margin-bottom: 8px; }
12
+ .dot { width: 7px; height: 7px; border-radius: 50%; background: #bbb; flex: none; }
13
+ .dot.on { background: #16a34a; }
14
+ .dot.err { background: #dc2626; }
15
+ .row { display: flex; align-items: center; gap: 6px; margin: 6px 0; font-size: 12px; }
16
+ .lbl { color: var(--qqs-text3, #6b7280); flex: none; }
17
+ .val { color: var(--qqs-text, #1f2329); }
18
+ .hint { color: var(--qqs-text3, #9aa0a8); font-size: 11px; line-height: 1.5; }
19
+ .txt {
20
+ flex: 1; min-width: 160px; font: inherit; padding: 6px 9px;
21
+ border: 1px solid var(--qqs-border, #d0d5dd); border-radius: 8px; outline: none; color: var(--qqs-text, #1f2329);
22
+ }
23
+ .txt:focus { border-color: #7c6cf0; }
24
+ .acts { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
25
+ .btn {
26
+ font: inherit; font-size: 12px; padding: 5px 12px; border-radius: 8px;
27
+ border: 1px solid var(--qqs-primary-border, #d9c6ff); background: var(--qqs-primary-bg, #f1ecff); color: var(--qqs-primary-fg, #4a3a9f); cursor: pointer;
28
+ }
29
+ .btn:hover { background: #e9e1ff; }
30
+ .btn:disabled { opacity: .5; cursor: default; }
31
+ .btn.ghost { background: var(--qqs-card, #fff); border-color: #d0d5dd; color: #4b5563; }
32
+
33
+ /* dock 卡片专用:顶部一排子标签 */
34
+ .subtabs { display: flex; gap: 4px; border-bottom: 1px solid #eee; margin-bottom: 10px; }
35
+ .subtabs button {
36
+ font: inherit; font-size: 13px; padding: 6px 14px; border: none; background: none;
37
+ cursor: pointer; color: #666; border-bottom: 2px solid transparent;
38
+ }
39
+ .subtabs button.on { color: var(--qqs-primary-fg, #4a3a9f); font-weight: 700; border-bottom-color: #7c6cf0; }
40
+ .pane { display: none; }
41
+ .pane.on { display: block; }
42
+ pre.info {
43
+ background: var(--qqs-card-solid, #f6f7f9); border: 1px solid var(--qqs-border, #e6e8ec); border-radius: 8px;
44
+ padding: 8px 10px; font-size: 11px; line-height: 1.6; max-height: 180px; overflow: auto;
45
+ white-space: pre-wrap; word-break: break-all; margin: 4px 0 0;
46
+ }
44
47
 
45
48
  /* 说明块(2026-10-27):告诉用户这两段文字会出现在 /扩展示例 里 */
46
49
  .tip {
47
- background: #f4f7ff; border: 1px solid #dbe6ff; border-radius: 9px;
48
- padding: 8px 10px; font-size: 11.5px; line-height: 1.65; color: #3a4a6b; margin: 2px 0 10px;
50
+ background: var(--qqs-primary-bg, #f4f7ff); border: 1px solid var(--qqs-primary-border, #dbe6ff); border-radius: 9px;
51
+ padding: 8px 10px; font-size: 11.5px; line-height: 1.65; color: var(--qqs-text2, #3a4a6b); margin: 2px 0 10px;
49
52
  }
50
- .tip b { color: #2b4a8f; }
53
+ .tip b { color: var(--qqs-primary-fg, #2b4a8f); }
51
54
  .code {
52
55
  font-family: ui-monospace, Consolas, monospace; font-size: 11px;
53
- background: #fff; border: 1px dashed #c9d8f5; border-radius: 6px;
54
- padding: 3px 7px; margin: 5px 0; color: #1f3b73;
56
+ background: var(--qqs-card, #fff); border: 1px dashed var(--qqs-primary-border, #c9d8f5); border-radius: 6px;
57
+ padding: 3px 7px; margin: 5px 0; color: var(--qqs-primary-fg, #1f3b73);
55
58
  }
@@ -1,61 +1,64 @@
1
- /* 扩展示例 · 两处卡片共用的样式(Shadow DOM 内,不影响面板其他地方) */
2
- :host { display: block; }
3
- .card {
4
- background: #fff; border: 1px solid #e6e8ec; border-radius: 12px;
5
- padding: 10px 12px; font: 13px -apple-system, 'PingFang SC', 'Microsoft YaHei', sans-serif;
6
- color: #1f2329; box-shadow: 0 1px 2px rgba(20,30,60,.05);
7
- }
8
- .hd { display: flex; align-items: center; gap: 6px; font-weight: 700; margin-bottom: 8px; }
9
- .dot { width: 7px; height: 7px; border-radius: 50%; background: #bbb; flex: none; }
10
- .dot.on { background: #16a34a; }
11
- .dot.err { background: #dc2626; }
12
- .row { display: flex; align-items: center; gap: 6px; margin: 6px 0; font-size: 12px; }
13
- .lbl { color: #6b7280; flex: none; }
14
- .val { color: #1f2329; }
15
- .hint { color: #9aa0a8; font-size: 11px; line-height: 1.5; }
16
- .txt {
17
- flex: 1; min-width: 160px; font: inherit; padding: 6px 9px;
18
- border: 1px solid #d0d5dd; border-radius: 8px; outline: none; color: #1f2329;
19
- }
20
- .txt:focus { border-color: #7c6cf0; }
21
- .acts { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
22
- .btn {
23
- font: inherit; font-size: 12px; padding: 5px 12px; border-radius: 8px;
24
- border: 1px solid #d9c6ff; background: #f1ecff; color: #4a3a9f; cursor: pointer;
25
- }
26
- .btn:hover { background: #e9e1ff; }
27
- .btn:disabled { opacity: .5; cursor: default; }
28
- .btn.ghost { background: #fff; border-color: #d0d5dd; color: #4b5563; }
29
-
30
- /* dock 卡片专用:顶部一排子标签 */
31
- .subtabs { display: flex; gap: 4px; border-bottom: 1px solid #eee; margin-bottom: 10px; }
32
- .subtabs button {
33
- font: inherit; font-size: 13px; padding: 6px 14px; border: none; background: none;
34
- cursor: pointer; color: #666; border-bottom: 2px solid transparent;
35
- }
36
- .subtabs button.on { color: #4a3a9f; font-weight: 700; border-bottom-color: #7c6cf0; }
37
- .pane { display: none; }
38
- .pane.on { display: block; }
39
- pre.info {
40
- background: #f6f7f9; border: 1px solid #e6e8ec; border-radius: 8px;
41
- padding: 8px 10px; font-size: 11px; line-height: 1.6; max-height: 180px; overflow: auto;
42
- white-space: pre-wrap; word-break: break-all; margin: 4px 0 0;
43
- }
1
+ /* 颜色全部走 --qqs-* 变量(带 fallback)——
2
+ 宿主的亮/暗 token 定义在 body 上,自定义属性会穿透 Shadow DOM,
3
+ 所以卡片跟着宿主主题自动切换,不用自己判断。 */
4
+ /* 扩展示例 · 两处卡片共用的样式(Shadow DOM 内,不影响面板其他地方) */
5
+ :host { display: block; }
6
+ .card {
7
+ background: var(--qqs-card, #fff); border: 1px solid var(--qqs-border, #e6e8ec); border-radius: 12px;
8
+ padding: 10px 12px; font: 13px -apple-system, 'PingFang SC', 'Microsoft YaHei', sans-serif;
9
+ color: var(--qqs-text, #1f2329); box-shadow: 0 1px 2px rgba(20,30,60,.05);
10
+ }
11
+ .hd { display: flex; align-items: center; gap: 6px; font-weight: 700; margin-bottom: 8px; }
12
+ .dot { width: 7px; height: 7px; border-radius: 50%; background: #bbb; flex: none; }
13
+ .dot.on { background: #16a34a; }
14
+ .dot.err { background: #dc2626; }
15
+ .row { display: flex; align-items: center; gap: 6px; margin: 6px 0; font-size: 12px; }
16
+ .lbl { color: var(--qqs-text3, #6b7280); flex: none; }
17
+ .val { color: var(--qqs-text, #1f2329); }
18
+ .hint { color: var(--qqs-text3, #9aa0a8); font-size: 11px; line-height: 1.5; }
19
+ .txt {
20
+ flex: 1; min-width: 160px; font: inherit; padding: 6px 9px;
21
+ border: 1px solid var(--qqs-border, #d0d5dd); border-radius: 8px; outline: none; color: var(--qqs-text, #1f2329);
22
+ }
23
+ .txt:focus { border-color: #7c6cf0; }
24
+ .acts { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
25
+ .btn {
26
+ font: inherit; font-size: 12px; padding: 5px 12px; border-radius: 8px;
27
+ border: 1px solid var(--qqs-primary-border, #d9c6ff); background: var(--qqs-primary-bg, #f1ecff); color: var(--qqs-primary-fg, #4a3a9f); cursor: pointer;
28
+ }
29
+ .btn:hover { background: #e9e1ff; }
30
+ .btn:disabled { opacity: .5; cursor: default; }
31
+ .btn.ghost { background: var(--qqs-card, #fff); border-color: #d0d5dd; color: #4b5563; }
32
+
33
+ /* dock 卡片专用:顶部一排子标签 */
34
+ .subtabs { display: flex; gap: 4px; border-bottom: 1px solid #eee; margin-bottom: 10px; }
35
+ .subtabs button {
36
+ font: inherit; font-size: 13px; padding: 6px 14px; border: none; background: none;
37
+ cursor: pointer; color: #666; border-bottom: 2px solid transparent;
38
+ }
39
+ .subtabs button.on { color: var(--qqs-primary-fg, #4a3a9f); font-weight: 700; border-bottom-color: #7c6cf0; }
40
+ .pane { display: none; }
41
+ .pane.on { display: block; }
42
+ pre.info {
43
+ background: var(--qqs-card-solid, #f6f7f9); border: 1px solid var(--qqs-border, #e6e8ec); border-radius: 8px;
44
+ padding: 8px 10px; font-size: 11px; line-height: 1.6; max-height: 180px; overflow: auto;
45
+ white-space: pre-wrap; word-break: break-all; margin: 4px 0 0;
46
+ }
44
47
 
45
48
  /* 说明块(2026-10-27):告诉用户这两段文字会出现在 /扩展示例 里 */
46
49
  .tip {
47
- background: #f4f7ff; border: 1px solid #dbe6ff; border-radius: 9px;
48
- padding: 8px 10px; font-size: 11.5px; line-height: 1.65; color: #3a4a6b; margin: 2px 0 10px;
50
+ background: var(--qqs-primary-bg, #f4f7ff); border: 1px solid var(--qqs-primary-border, #dbe6ff); border-radius: 9px;
51
+ padding: 8px 10px; font-size: 11.5px; line-height: 1.65; color: var(--qqs-text2, #3a4a6b); margin: 2px 0 10px;
49
52
  }
50
- .tip b { color: #2b4a8f; }
53
+ .tip b { color: var(--qqs-primary-fg, #2b4a8f); }
51
54
  .code {
52
55
  font-family: ui-monospace, Consolas, monospace; font-size: 11px;
53
- background: #fff; border: 1px dashed #c9d8f5; border-radius: 6px;
54
- padding: 3px 7px; margin: 5px 0; color: #1f3b73;
56
+ background: var(--qqs-card, #fff); border: 1px dashed var(--qqs-primary-border, #c9d8f5); border-radius: 6px;
57
+ padding: 3px 7px; margin: 5px 0; color: var(--qqs-primary-fg, #1f3b73);
55
58
  }
56
59
 
57
60
  /* 作用域切换(2026-10-27):全局 / 当前账号 */
58
61
  .seg { gap: 14px; flex-wrap: wrap; }
59
- .seg label { display: inline-flex; align-items: center; gap: 5px; cursor: pointer; font-size: 12px; color: #374151; }
62
+ .seg label { display: inline-flex; align-items: center; gap: 5px; cursor: pointer; font-size: 12px; color: var(--qqs-text2, #374151); }
60
63
  .seg input[type="radio"] { margin: 0; cursor: pointer; }
61
- .seg b { color: #2b4a8f; font-weight: 600; }
64
+ .seg b { color: var(--qqs-primary-fg, #2b4a8f); font-weight: 600; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zaofan/dsh-qqbot",
3
- "version": "1.11.1",
3
+ "version": "1.11.2",
4
4
  "description": "QQ Bot IM channel plugin for deepseek-harness (dsh)",
5
5
  "type": "module",
6
6
  "main": "./entry.js",