dsh-sidecard-ask 2.1.2 → 2.1.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/CHANGELOG.md +24 -0
- package/README.md +23 -5
- package/client.js +75 -6
- package/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.1.3
|
|
4
|
+
|
|
5
|
+
**主题:折叠态胶囊不再压在聊天输入框上(2026-10-25 实拍报告)。**
|
|
6
|
+
|
|
7
|
+
### 修复
|
|
8
|
+
|
|
9
|
+
- **胶囊盖住输入框**。浮层固定在 `bottom:16px`,而输入框就在会话列底部,折叠态胶囊
|
|
10
|
+
正好落在输入框上。新增纯函数
|
|
11
|
+
`floatBottom({ frameHeight, composerTop, fallback, limit })`:测量 `[data-composer-card]`
|
|
12
|
+
的顶边,把浮层抬到输入框上方 8px(CSS 变量 `--dsa-gap-bottom`)。两道保险——输入框
|
|
13
|
+
顶边落在窗口上半部(空会话的居中输入框)时不抬;结果上限 240px,避免超高输入框把
|
|
14
|
+
浮层推到会话正文上。
|
|
15
|
+
- **胶囊改为按内容收缩**。浮层本就贴着右边缘(`right:8px`),「再往右」没有余量,所以
|
|
16
|
+
改为缩窄:内容自适应、上限 240px、容器右对齐,左边缘随之右退。内边距 8/12 → 6/10、
|
|
17
|
+
行高 18 → 16,问题文本由 `flex:1` 改为 `flex:0 1 auto`(只在真放不下时才截断)。
|
|
18
|
+
取舍:抬到输入框上方后它会压住最后一行消息,所以必须足够窄小。
|
|
19
|
+
- 新增 `node.dataset.layerBottom`(与 `layerWidth` 并列),用户反馈可以直接报出浮层
|
|
20
|
+
离底多少。
|
|
21
|
+
|
|
22
|
+
### 验证
|
|
23
|
+
|
|
24
|
+
`test/float-placement.mjs` 新增 `floatBottom` 数值用例与源码钩子;四套脚本
|
|
25
|
+
100 / 1427 / 57 / 271 全绿。
|
|
26
|
+
|
|
3
27
|
## 2.1.2
|
|
4
28
|
|
|
5
29
|
**主题:按 DSH 0.2.1-rc.3 的实测契约修复——原生右栏卡片、语言跟随、只读工具收窄、可信主机围栏。
|
package/README.md
CHANGED
|
@@ -196,7 +196,7 @@ plugin_manager(action="install_bundle", target="D:\\Users\\34332\\AI\\dsh-sideca
|
|
|
196
196
|
|
|
197
197
|
非法值会被拒绝(设置页报错、接口返回 `invalid-config`),并把被忽略的项列在 `/state` 的 `problems` 里——不会静默吞掉。
|
|
198
198
|
|
|
199
|
-
### 4.1 内置浮层的位置规则(`floatPlacement`)
|
|
199
|
+
### 4.1 内置浮层的位置规则(`floatPlacement` / `floatBottom`)
|
|
200
200
|
|
|
201
201
|
用户实测报告过一个问题:浮层是 `position:fixed; right:16px; width:min(400px,92vw)` 的实心卡片。
|
|
202
202
|
实测那一次(1400px 宽的窗口)对话区右边界在 x≈971,而浮层左边界在 x≈858,**压住正文约 113px**,
|
|
@@ -218,7 +218,23 @@ bleeds = 对话区右边界 - left > 1 // 只有带宽 < 240px 时才
|
|
|
218
218
|
- 浮层**只使用对话区右侧的预留带**(右栏也在这条带里),窗口越窄它越窄,而不是越靠左。
|
|
219
219
|
- `capped: true` 表示「上限没用满,是被可用带宽压住的」——这是正常的自适应,不是错误。
|
|
220
220
|
- 挂载时、窗口 resize 时、右栏开合时(`ResizeObserver`)都会重算;流式刷新只重渲染,不动几何。
|
|
221
|
-
- 浮层根节点带 `data-layer-width
|
|
221
|
+
- 浮层根节点带 `data-layer-width`(实际宽度)、`data-layer-bottom`(距窗口底的距离)与
|
|
222
|
+
`data-dsa-float-mode`:报告问题时可直接读这三个值。
|
|
223
|
+
|
|
224
|
+
**垂直位置(2.1.3)**:宽度不是唯一的问题——浮层原本固定在 `bottom:16px`,而输入框就在会话列底部,
|
|
225
|
+
折叠态胶囊必然压在输入框上(2026-10-25 实拍报告「挡住聊天框了」)。垂直位置由纯函数 `floatBottom` 决定:
|
|
226
|
+
|
|
227
|
+
```js
|
|
228
|
+
// floatBottom({ frameHeight, composerTop = null, fallback = 16, limit = 240 })
|
|
229
|
+
if (composerTop 未知 || 帧高为 0) return 16 // 测不到就保持旧锚点
|
|
230
|
+
if (composerTop < 帧高 / 2) return 16 // 居中输入框(空会话)不是底栏,不抬
|
|
231
|
+
bottom = clamp(帧高 - composerTop + 8, 16, 240) // 抬到输入框上方 8px,上限 240px
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`composerTop` 取自 DSH 的 `[data-composer-card]`(输入框那张卡片的顶边),该节点也进了
|
|
235
|
+
`ResizeObserver`——输入多行时卡片会往上长,浮层跟着重算,不会重新沉回输入框上。
|
|
236
|
+
取舍要讲清楚:抬起来之后它**改压最后一两行消息**,所以胶囊同时改成按内容收缩(≤240px、右对齐),
|
|
237
|
+
尽量把覆盖面积压到最小。
|
|
222
238
|
|
|
223
239
|
### 4.2 右侧栏打开时「占满它」(`dockRail`,1.8.3)
|
|
224
240
|
|
|
@@ -782,10 +798,12 @@ node tools/compat-probe.mjs --json tools/.cache/matrix.json
|
|
|
782
798
|
展开后标题栏右侧有**收起为胶囊(⇥)**与**删除这条追问对话(×)**——× 会把该卡片从浮层与追问历史里
|
|
783
799
|
一并移除,**无法撤销**(有内容时会先弹插件内确认层;点浮层底部的「关闭」则只把它摘离浮层、历史仍保留在
|
|
784
800
|
设置页的历史管理器里)。2.0.0 起不再有「隐藏整个浮层 / 只留一个计数圆点」这套形态。
|
|
785
|
-
用 F12 选中浮层根节点(`[data-dsa-root]
|
|
801
|
+
用 F12 选中浮层根节点(`[data-dsa-root]`)读三个值自检:
|
|
786
802
|
- `data-dsa-float-mode` 应为 `capsule` / `full`(铺满右栏时为 `dock`);
|
|
787
|
-
- `data-layer-width` 应等于 `min(floatMaxWidth, 对话区右边界到帧右边缘的可用带宽)
|
|
788
|
-
|
|
803
|
+
- `data-layer-width` 应等于 `min(floatMaxWidth, 对话区右边界到帧右边缘的可用带宽)`;
|
|
804
|
+
- `data-layer-bottom` 应等于 `帧高 - 输入框(`[data-composer-card]`)顶边 + 8`(空会话/测不到时为 16)。
|
|
805
|
+
**关键判据:胶囊/卡片的左边缘不得越过对话区(`[data-slot^="conversation"]`)的右边界,
|
|
806
|
+
下边缘不得落在输入框上**。
|
|
789
807
|
把窗口从 1920px 拖到 900px、再开关一次右侧栏,浮层应随之变窄而不是往左压住正文;
|
|
790
808
|
只有窗口窄到可用带宽不足 240px 时才允许压入(此时是设计内的降级,不是 bug)。
|
|
791
809
|
9. **占满右侧栏(1.8.3,`dockRail`)**:先把右侧栏打开(任意面板,例如「团队任务板」),再追问一次 →
|
package/client.js
CHANGED
|
@@ -117,7 +117,7 @@ window.__ModuleLoader__.load({
|
|
|
117
117
|
* the Host and the module's `api.version` both read it, so a report can
|
|
118
118
|
* never claim a generation the browser is not actually running.
|
|
119
119
|
*/
|
|
120
|
-
const CLIENT_VERSION = '2.1.
|
|
120
|
+
const CLIENT_VERSION = '2.1.3'
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
123
|
* Tab kind served by the DSH native right rail (also its implementation id).
|
|
@@ -364,6 +364,17 @@ window.__ModuleLoader__.load({
|
|
|
364
364
|
const FLOAT_GAP_SIDEBAR = 10
|
|
365
365
|
const FLOAT_MIN_WIDTH = 240
|
|
366
366
|
const FLOAT_MAX_WIDTH_LIMIT = 560
|
|
367
|
+
/**
|
|
368
|
+
* Vertical clearance (2.1.3). The layer used to be pinned `bottom:16px`,
|
|
369
|
+
* i.e. straight on top of the composer — which is what the 2026-10-25
|
|
370
|
+
* report ("挡住聊天框了") caught: the capsule covered the input box. The
|
|
371
|
+
* layer now measures the composer and sits just above it.
|
|
372
|
+
*/
|
|
373
|
+
const FLOAT_GAP_BOTTOM = 16
|
|
374
|
+
const FLOAT_GAP_COMPOSER = 8
|
|
375
|
+
const FLOAT_BOTTOM_LIMIT = 240
|
|
376
|
+
/** The capsule is a status pill, not a card: it sizes to its content. */
|
|
377
|
+
const CAPSULE_MAX_WIDTH = 240
|
|
367
378
|
|
|
368
379
|
/**
|
|
369
380
|
* The floating layer's width for one frame.
|
|
@@ -394,6 +405,40 @@ window.__ModuleLoader__.load({
|
|
|
394
405
|
return { width, avail, capped: width < cap, bleeds: lane - left > 1 }
|
|
395
406
|
}
|
|
396
407
|
|
|
408
|
+
/**
|
|
409
|
+
* How far above the frame's bottom edge the floating layer sits (2.1.3).
|
|
410
|
+
*
|
|
411
|
+
* Pure, like {@link floatPlacement}, and mirrored by the tests.
|
|
412
|
+
* `composerTop` is the composer card's top edge, in frame coordinates. The
|
|
413
|
+
* composer is part of the conversation column, so the old fixed
|
|
414
|
+
* `bottom:16px` parked the layer straight on top of the input box; the
|
|
415
|
+
* layer is lifted until its own bottom edge clears the composer by
|
|
416
|
+
* `FLOAT_GAP_COMPOSER`.
|
|
417
|
+
*
|
|
418
|
+
* Two guards keep that lift honest: a composer whose top sits in the frame's
|
|
419
|
+
* upper half is not a bottom bar (the empty-session composer is vertically
|
|
420
|
+
* centred) and is ignored, and the result is clamped to `limit` so an
|
|
421
|
+
* unusually tall composer cannot push the layer over the conversation.
|
|
422
|
+
*
|
|
423
|
+
* @param {object} input - `frameHeight`, `composerTop`, `fallback`, `limit`.
|
|
424
|
+
* @returns {number} Bottom offset in px, never below `fallback`.
|
|
425
|
+
*/
|
|
426
|
+
function floatBottom({
|
|
427
|
+
frameHeight,
|
|
428
|
+
composerTop = null,
|
|
429
|
+
fallback = FLOAT_GAP_BOTTOM,
|
|
430
|
+
limit = FLOAT_BOTTOM_LIMIT,
|
|
431
|
+
}) {
|
|
432
|
+
const base = Number.isFinite(fallback) ? Math.max(0, Math.round(fallback)) : FLOAT_GAP_BOTTOM
|
|
433
|
+
const frame = Number.isFinite(frameHeight) ? frameHeight : 0
|
|
434
|
+
if (frame <= 0 || !Number.isFinite(composerTop)) return base
|
|
435
|
+
// A centred composer is not a bottom bar; leave the layer where it was.
|
|
436
|
+
if (composerTop < frame / 2) return base
|
|
437
|
+
const clear = Math.round(frame - composerTop + FLOAT_GAP_COMPOSER)
|
|
438
|
+
const cap = Number.isFinite(limit) ? Math.max(base, limit) : FLOAT_BOTTOM_LIMIT
|
|
439
|
+
return Math.max(base, Math.min(clear, cap))
|
|
440
|
+
}
|
|
441
|
+
|
|
397
442
|
const DOCK_MIN_WIDTH = 280
|
|
398
443
|
const DOCK_MIN_HEIGHT = 240
|
|
399
444
|
|
|
@@ -2654,9 +2699,15 @@ body[data-ds-dark-theme] .dsa-seg button[data-on="true"]:hover{
|
|
|
2654
2699
|
\`floatPlacement\` through --dsa-float-w, so it grows left only as far as the
|
|
2655
2700
|
conversation column allows. pointer-events stays off on the container so the
|
|
2656
2701
|
empty area around the pill never eats a click meant for the conversation. */
|
|
2657
|
-
.dsa-float{pointer-events:none;position:fixed;right:var(--dsa-gap-right, 8px);bottom:16px;
|
|
2702
|
+
.dsa-float{pointer-events:none;position:fixed;right:var(--dsa-gap-right, 8px);bottom:var(--dsa-gap-bottom, 16px);
|
|
2658
2703
|
display:flex;flex-direction:column;gap:8px;width:var(--dsa-float-w, 340px);max-width:92vw}
|
|
2659
2704
|
.dsa-float > *{pointer-events:auto}
|
|
2705
|
+
/* Capsule mode (2.1.3): the layer is a status pill, not a card. It sizes to its
|
|
2706
|
+
own content and stays right-aligned, so shrinking it is what pulls its left
|
|
2707
|
+
edge back over the conversation — the layer is already flush with the frame's
|
|
2708
|
+
right edge, so it has nowhere further right to go. */
|
|
2709
|
+
.dsa-float[data-dsa-float-mode="capsule"]{width:auto;align-items:flex-end;
|
|
2710
|
+
max-width:min(var(--dsa-float-w, 340px), var(--dsa-capsule-max, 240px), 92vw)}
|
|
2660
2711
|
/* Docked presentation (1.8.3): the same container, but pinned to the right
|
|
2661
2712
|
rail's measured rectangle (inline left/top/width/height) instead of the
|
|
2662
2713
|
frame's bottom-right corner. Right/bottom must be released, otherwise the
|
|
@@ -2671,8 +2722,8 @@ body[data-ds-dark-theme] .dsa-seg button[data-on="true"]:hover{
|
|
|
2671
2722
|
box-shadow:none;background:transparent;padding:10px 12px 8px}
|
|
2672
2723
|
.dsa-float[data-dsa-float-mode="dock"] .dsa-answer{max-height:none;flex:1 1 auto;min-height:0;overflow:auto}
|
|
2673
2724
|
.dsa-stack{display:flex;flex-direction:column;gap:8px;max-height:52vh;overflow:auto}
|
|
2674
|
-
.dsa-capsule{display:flex;align-items:center;gap:
|
|
2675
|
-
padding:
|
|
2725
|
+
.dsa-capsule{display:flex;align-items:center;gap:6px;width:auto;max-width:100%;box-sizing:border-box;text-align:left;
|
|
2726
|
+
padding:6px 10px;border-radius:999px;cursor:pointer;font:inherit;font-size:12px;line-height:16px;
|
|
2676
2727
|
border:1px solid var(--dsw-alias-border-l2, rgba(127,127,127,.35));
|
|
2677
2728
|
background:var(--dsw-alias-bg-overlay, #fff);color:var(--dsw-alias-label-primary, #111);
|
|
2678
2729
|
box-shadow:0 2px 10px rgba(0,0,0,.14)}
|
|
@@ -2685,7 +2736,7 @@ body[data-ds-dark-theme] .dsa-seg button[data-on="true"]:hover{
|
|
|
2685
2736
|
.dsa-capsule-dot[data-status="error"]{background:var(--dsw-alias-state-error-primary, #c0392b)}
|
|
2686
2737
|
.dsa-capsule-dot[data-status="stopped"]{background:var(--dsw-alias-state-warn-primary, #b8860b)}
|
|
2687
2738
|
.dsa-capsule-label{font-weight:600;white-space:nowrap}
|
|
2688
|
-
.dsa-capsule-q{min-width:0;flex:1;color:var(--dsw-alias-label-secondary, #666);
|
|
2739
|
+
.dsa-capsule-q{min-width:0;flex:0 1 auto;color:var(--dsw-alias-label-secondary, #666);
|
|
2689
2740
|
white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
2690
2741
|
.dsa-capsule-chev{flex:none;color:var(--dsw-alias-label-secondary, #888)}
|
|
2691
2742
|
/* Delete confirmation (2.0.0). Fixed, not inline: a card docked into the right
|
|
@@ -4707,7 +4758,10 @@ body[data-ds-dark-theme] .dsa-seg button[data-on="true"]:hover{
|
|
|
4707
4758
|
// Slot subtrees only: `.rightbar` and `[data-dsh-panel-host]` were
|
|
4708
4759
|
// dropped in 2.1.2 — no supported build renders either marker, so they
|
|
4709
4760
|
// were two dead branches in a selector that runs on every resize.
|
|
4710
|
-
|
|
4761
|
+
// 2.1.3: the composer card joins the list so a multi-line draft (which
|
|
4762
|
+
// grows the box upward) re-measures the layer's vertical clearance
|
|
4763
|
+
// instead of letting the pill sink back onto the input.
|
|
4764
|
+
const selector = '[data-slot^="conversation"], [data-slot^="sidebar.right"], [data-composer-card]'
|
|
4711
4765
|
const observed = new Set()
|
|
4712
4766
|
const all = () => {
|
|
4713
4767
|
try {
|
|
@@ -4743,11 +4797,25 @@ body[data-ds-dark-theme] .dsa-seg button[data-on="true"]:hover{
|
|
|
4743
4797
|
const railBox = rail === null ? null : rail.box
|
|
4744
4798
|
const laneRight = laneRect === null ? rootWidth : laneRect.right
|
|
4745
4799
|
const railLeft = railBox === null ? null : railBox.left
|
|
4800
|
+
// 2.1.3: the composer occupies the bottom of the conversation column,
|
|
4801
|
+
// so the layer's old fixed `bottom:16px` landed straight on top of the
|
|
4802
|
+
// input box. Measure the composer card (the box the user types in) and
|
|
4803
|
+
// lift the layer clear of it; `floatBottom` owns the guards.
|
|
4804
|
+
const composerEl = one('[data-slot^="conversation"] [data-composer-card]')
|
|
4805
|
+
?? one('[data-composer-card]')
|
|
4806
|
+
const composerRect = measure(composerEl)
|
|
4807
|
+
const gapBottom = floatBottom({
|
|
4808
|
+
frameHeight: rootHeight,
|
|
4809
|
+
composerTop: composerRect === null ? null : composerRect.top,
|
|
4810
|
+
})
|
|
4811
|
+
node.style.setProperty('--dsa-gap-bottom', `${gapBottom}px`)
|
|
4746
4812
|
const placement = floatPlacement({ frameWidth: rootWidth, laneRight, railLeft, maxWidth })
|
|
4747
4813
|
node.style.setProperty('--dsa-float-w', `${placement.width}px`)
|
|
4748
4814
|
node.style.setProperty('--dsa-gap-right', `${FLOAT_GAP_RIGHT}px`)
|
|
4815
|
+
node.style.setProperty('--dsa-capsule-max', `${CAPSULE_MAX_WIDTH}px`)
|
|
4749
4816
|
// Readable placement, so a user report can say WHERE the layer sat.
|
|
4750
4817
|
node.dataset.layerWidth = String(placement.width)
|
|
4818
|
+
node.dataset.layerBottom = String(gapBottom)
|
|
4751
4819
|
|
|
4752
4820
|
// Docked presentation: the card fills the rail page's body instead of
|
|
4753
4821
|
// floating over it. Only measured in `full` mode — a capsule/launcher
|
|
@@ -7429,6 +7497,7 @@ body[data-ds-dark-theme] .dsa-seg button[data-on="true"]:hover{
|
|
|
7429
7497
|
renderRichText,
|
|
7430
7498
|
pickSurface,
|
|
7431
7499
|
floatPlacement,
|
|
7500
|
+
floatBottom,
|
|
7432
7501
|
railDock,
|
|
7433
7502
|
compareVersions,
|
|
7434
7503
|
electFamilyLeader,
|
package/index.js
CHANGED
|
@@ -45,7 +45,7 @@ export const name = 'dsh-sidecard-ask'
|
|
|
45
45
|
export const inject = ['webServer']
|
|
46
46
|
|
|
47
47
|
/** Version of this plugin (kept in step with package.json by test/verify.mjs). */
|
|
48
|
-
export const PLUGIN_VERSION = '2.1.
|
|
48
|
+
export const PLUGIN_VERSION = '2.1.3'
|
|
49
49
|
|
|
50
50
|
/** Route prefix of the plugin's own API. */
|
|
51
51
|
export const ROUTE_PREFIX = '/sidecard-ask/api'
|