@bittersmilezzz/dsh-model-selector 0.3.7 → 0.3.9
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/README.md +2 -1
- package/lib/client.js +26 -39
- package/lib/client.js.map +1 -1
- package/lib/types/client/ModelSelect.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/menuFit.d.ts +12 -21
- package/lib/types/client/menuFit.d.ts.map +1 -1
- package/lib/types/client/styles.d.ts.map +1 -1
- package/package.json +21 -21
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
- 替换官方输入区的模型选择 seat(`conversation.input.model`,shadow 方式叠加,不禁用官方组件)
|
|
18
18
|
- 单层模型菜单:名称搜索过滤(命中片段高亮)+ 提供商分组折叠,选中行与供应商标可见高亮,打开自动滚到当前选中行
|
|
19
19
|
- 搜索宽容:大小写、变音符与 `İ`/`café` 这类 Unicode 折叠差异都能命中(NFKD 归一化;高亮只在码位长度不变时标注,不画错位置)
|
|
20
|
-
-
|
|
20
|
+
- 菜单定位走官方 `useAnchoredPosition`(右缘对齐 seat + 视口钳位 + 跟随浮层尺寸变化),弹出方向按上下空间自适应,高度按可用空间收紧,矮窗口不溢出
|
|
21
21
|
- 键盘可完整操作:方向键在结果间移动、搜索框内 Enter 选中首个命中、Escape 先清搜索词再关闭并还原焦点;IME 组合输入的确认键不会被误当成「选中首个命中」
|
|
22
22
|
- 无障碍:搜索框是 combobox(结果列表 listbox / 分组视图 menu)、roving tabindex 只留一个 Tab 停靠点(全折叠时落在组头)、焦点指示与选中态可辨、状态变化经常驻 live region 播报
|
|
23
23
|
- 选中模型后可展开底部内联**推理强度滑杆**,实时显示当前档位名与该档位说明
|
|
@@ -57,6 +57,7 @@ dsh plugin --profile <profile> add <path-to-repo>
|
|
|
57
57
|
## 已知风险
|
|
58
58
|
|
|
59
59
|
- 通过 slot 优先级(priority: -1)**shadow 官方 seat**:若官方后续改动该 seat 的注入面(`ModelSelectInjected`),需同步适配;这是官方认可的 slot 叠加机制,并非禁用官方 entry。
|
|
60
|
+
- **已知限制(0.2.1+)**:官方 `conversation.input.model.label` 内联标签子座位由官方占用者声明,registry 不允许同一 child key 二次声明(实测会抛 `already declared` 并让插件 apply 失败)。因此本插件接管期间,第三方注入该标签座位的贡献不会被渲染;只有切回官方 seat 时才生效。这不是可以靠声明 children 解决的问题,属 shadow 语义的固有限制。
|
|
60
61
|
- 与其它也 shadow `conversation.input.model` 的插件同时安装时,优先级决定渲染赢家,可能互相覆盖(本插件优先级 -1 最低,默认胜出)。
|
|
61
62
|
- 遮蔽的**依赖侧**已由 `test/official-seat-shadow.test.mjs` 钉住:官方当前注册该座位时**不声明** `priority`(即默认 0),遮蔽才成立;官方一旦开始声明(尤其 `<= -1`)或把该座位从 single 改成 chain,测试先变红,提示重新评估策略。
|
|
62
63
|
- 若把这个插件提交到第三方商城(如 DSH-Store),其「不动官方组件」条款是最需要解释的一点:本插件走的是官方 slot 的 shadowing 语义(`priority: -1`),**没有**禁用/替换官方 entry,也没有改动任何 `@deepseek-ai/*` 组件本体。
|
package/lib/client.js
CHANGED
|
@@ -564,27 +564,14 @@ window.__ModuleLoader__.load({
|
|
|
564
564
|
function dmsMenuAbove(triggerTop, triggerBottom, viewportHeight) {
|
|
565
565
|
return triggerTop >= viewportHeight - triggerBottom;
|
|
566
566
|
}
|
|
567
|
-
/**
|
|
567
|
+
/** 向上弹时按 trigger 上方空间钳位菜单高度(bottom-anchored,不能复用向下弹的公式)。 */
|
|
568
|
+
function dmsAboveMaxHeight(triggerTop, cap) {
|
|
569
|
+
return Math.max(0, Math.min(cap, triggerTop - 12));
|
|
570
|
+
}
|
|
571
|
+
/** 向下弹时按 trigger 下方空间钳位菜单高度(top-anchored,不能复用向上弹的公式)。 */
|
|
568
572
|
function dmsBelowMaxHeight(triggerBottom, viewportHeight, cap) {
|
|
569
573
|
return Math.max(0, Math.min(cap, viewportHeight - triggerBottom - 12));
|
|
570
574
|
}
|
|
571
|
-
/**
|
|
572
|
-
* 菜单水平钳位:默认右锚定(right:0,与 seat 右缘对齐);seat 右缘左侧放不下
|
|
573
|
-
* 整幅菜单(seat 靠输入区左下 + 窄窗口)、或 seat 右缘本身已越出视口右缘
|
|
574
|
-
* (页面横向溢出/缩放时 rect.right 可大于 innerWidth,右锚定会把整幅菜单
|
|
575
|
-
* 拖出屏外)时,返回钳到视口内的 left 值,调用方改用 left 锚定。
|
|
576
|
-
* 返回 undefined 表示保持默认右锚定。
|
|
577
|
-
* menuWidth 传菜单实际渲染宽度(offsetWidth),与 CSS `min(280px, 100vw-32px)` 解耦。
|
|
578
|
-
*
|
|
579
|
-
* **坐标系**:入参与返回值都是**视口坐标**。菜单是 .dms-root(position:
|
|
580
|
-
* relative)内的 absolute 元素,left 的参考系是 root 的 padding box —— 调用方
|
|
581
|
-
* 必须减去 root 的左缘再写进 style.left,否则窄窗口下菜单整体右移、横向溢出
|
|
582
|
-
* 时整幅画到屏外。
|
|
583
|
-
*/
|
|
584
|
-
function dmsMenuLeft(rootRight, menuWidth, viewportWidth, margin) {
|
|
585
|
-
if (rootRight - menuWidth >= margin && rootRight <= viewportWidth - margin) return void 0;
|
|
586
|
-
return Math.min(Math.max(margin, rootRight - menuWidth), Math.max(margin, viewportWidth - margin - menuWidth));
|
|
587
|
-
}
|
|
588
575
|
//#endregion
|
|
589
576
|
//#region src/client/roving.ts
|
|
590
577
|
/**
|
|
@@ -1251,22 +1238,16 @@ window.__ModuleLoader__.load({
|
|
|
1251
1238
|
}
|
|
1252
1239
|
}, [available, load]);
|
|
1253
1240
|
const menuRef = react.useRef(null);
|
|
1254
|
-
const
|
|
1255
|
-
const menuMaxHeight = (0, _deepseek_ai_dsh_client_ui_primitives.useAnchoredMaxHeight)(menuAbove ? menuRef : inactiveMenuRef, 420, open, 12);
|
|
1256
|
-
const [belowMaxHeight, setBelowMaxHeight] = react.useState(420);
|
|
1257
|
-
const [menuLeft, setMenuLeft] = react.useState(void 0);
|
|
1241
|
+
const [menuMaxHeight, setMenuMaxHeight] = react.useState(420);
|
|
1258
1242
|
react.useLayoutEffect(() => {
|
|
1259
1243
|
if (!open) return;
|
|
1260
1244
|
const measure = () => {
|
|
1261
1245
|
const trigger = triggerRef.current;
|
|
1262
1246
|
if (trigger === null) return;
|
|
1263
1247
|
const rect = trigger.getBoundingClientRect();
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
const left = dmsMenuLeft(rect.right, menuWidth, window.innerWidth, 12);
|
|
1268
|
-
const rootLeft = rootRef.current?.getBoundingClientRect().left ?? 0;
|
|
1269
|
-
setMenuLeft(left === void 0 ? void 0 : left - rootLeft);
|
|
1248
|
+
const above = dmsMenuAbove(rect.top, rect.bottom, window.innerHeight);
|
|
1249
|
+
setMenuAbove(above);
|
|
1250
|
+
setMenuMaxHeight(above ? dmsAboveMaxHeight(rect.top, 420) : dmsBelowMaxHeight(rect.bottom, window.innerHeight, 420));
|
|
1270
1251
|
};
|
|
1271
1252
|
measure();
|
|
1272
1253
|
let raf = 0;
|
|
@@ -1288,6 +1269,15 @@ window.__ModuleLoader__.load({
|
|
|
1288
1269
|
window.removeEventListener("scroll", onViewportChange, { capture: true });
|
|
1289
1270
|
};
|
|
1290
1271
|
}, [open]);
|
|
1272
|
+
const menuPos = (0, _deepseek_ai_dsh_client_ui_primitives.useAnchoredPosition)({
|
|
1273
|
+
open,
|
|
1274
|
+
anchorRef: triggerRef,
|
|
1275
|
+
panelRef: menuRef,
|
|
1276
|
+
side: menuAbove ? "top" : "bottom",
|
|
1277
|
+
align: "end",
|
|
1278
|
+
gap: 8,
|
|
1279
|
+
margin: 12
|
|
1280
|
+
});
|
|
1291
1281
|
react.useEffect(() => {
|
|
1292
1282
|
if (!open) return;
|
|
1293
1283
|
searchRef.current?.focus();
|
|
@@ -1584,11 +1574,10 @@ window.__ModuleLoader__.load({
|
|
|
1584
1574
|
id: `${id}-menu`,
|
|
1585
1575
|
ref: menuRef,
|
|
1586
1576
|
style: {
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
}
|
|
1577
|
+
left: menuPos?.left ?? 0,
|
|
1578
|
+
top: menuPos?.top ?? 0,
|
|
1579
|
+
maxHeight: menuMaxHeight,
|
|
1580
|
+
visibility: menuPos === null ? "hidden" : void 0
|
|
1592
1581
|
},
|
|
1593
1582
|
className: "dms-menu dms-menuModel" + (menuAbove ? "" : " dms-menuBelow"),
|
|
1594
1583
|
children: [
|
|
@@ -1865,9 +1854,9 @@ window.__ModuleLoader__.load({
|
|
|
1865
1854
|
.dms-model-check svg { display: block; }
|
|
1866
1855
|
|
|
1867
1856
|
.dms-menu {
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1857
|
+
/* 官方 useAnchoredPosition 返回视口坐标:fixed 定位让它不再依赖 .dms-root
|
|
1858
|
+
的 padding box,免去 root 左缘减法那一层坐标系转换。 */
|
|
1859
|
+
position: fixed;
|
|
1871
1860
|
z-index: 20;
|
|
1872
1861
|
display: flex;
|
|
1873
1862
|
flex-direction: column;
|
|
@@ -1886,8 +1875,6 @@ window.__ModuleLoader__.load({
|
|
|
1886
1875
|
}
|
|
1887
1876
|
.dms-menuModel { border-right: none; }
|
|
1888
1877
|
.dms-menuBelow {
|
|
1889
|
-
bottom: auto;
|
|
1890
|
-
top: calc(100% + 8px);
|
|
1891
1878
|
animation-name: dms-menu-in-below;
|
|
1892
1879
|
}
|
|
1893
1880
|
|
|
@@ -2484,7 +2471,7 @@ body:not([data-ds-dark-theme]) .dms-effort.is-dragging .dms-effort-knob {
|
|
|
2484
2471
|
exports.apply = apply;
|
|
2485
2472
|
exports.inject = inject;
|
|
2486
2473
|
return module.exports;
|
|
2487
|
-
/* __DMS_SRC_HASH__ = "
|
|
2474
|
+
/* __DMS_SRC_HASH__ = "69e9b8c5f261eaa89fb6024b0775233655a48fb4edc152ddd52a1a8acf904d1f" */ }
|
|
2488
2475
|
});
|
|
2489
2476
|
|
|
2490
2477
|
//# sourceMappingURL=client.js.map
|