@bittersmilezzz/dsh-model-selector 0.1.21 → 0.3.0
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 +27 -7
- package/lib/client.js +461 -171
- package/lib/client.js.map +1 -1
- package/lib/types/client/ModelSelect.d.ts +6 -5
- package/lib/types/client/ModelSelect.d.ts.map +1 -1
- package/lib/types/client/copy.d.ts +43 -0
- package/lib/types/client/copy.d.ts.map +1 -0
- package/lib/types/client/effort.d.ts +29 -4
- package/lib/types/client/effort.d.ts.map +1 -1
- package/lib/types/client/index.d.ts +8 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/keys.d.ts +62 -0
- package/lib/types/client/keys.d.ts.map +1 -0
- package/lib/types/client/menuFit.d.ts +5 -0
- package/lib/types/client/menuFit.d.ts.map +1 -1
- package/lib/types/client/roving.d.ts +8 -0
- package/lib/types/client/roving.d.ts.map +1 -1
- package/lib/types/client/search.d.ts +73 -0
- package/lib/types/client/search.d.ts.map +1 -0
- package/lib/types/client/styles.d.ts +5 -1
- package/lib/types/client/styles.d.ts.map +1 -1
- package/package.json +28 -23
package/lib/client.js
CHANGED
|
@@ -119,6 +119,37 @@ window.__ModuleLoader__.load({
|
|
|
119
119
|
}
|
|
120
120
|
return best?.id;
|
|
121
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* 已知档位的秩;适配器自造的非规范 id(如 'turbo')返回 undefined —— 与
|
|
124
|
+
* 「已知但秩为 0」(off)区分开。maxEffortOf 对并列的未知档取首个,把它当
|
|
125
|
+
* 「最强档」提交等于随机挑一个档、还可能被宿主拒,故调用方必须先过这一关。
|
|
126
|
+
*/
|
|
127
|
+
function dmsEffortRank(id) {
|
|
128
|
+
return Object.prototype.hasOwnProperty.call(EFFORT_RANK, id) ? EFFORT_RANK[id] : void 0;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 选择模型时自动落到最强档的决策(从 choose() 搬出并修正):
|
|
132
|
+
* - 非推理模型 / 无档位 → 不提交 effort;
|
|
133
|
+
* - 最强档是 'off'(模型只提供 off)→ 不提交;
|
|
134
|
+
* - 最强档是**非规范 id**(rank 未知)→ 不提交:maxEffortOf 对并列未知档取
|
|
135
|
+
* 首个,把它当「最强」提交会挑错档甚至被宿主拒(用户看到「切换失败」);
|
|
136
|
+
* - 否则提交已知最强档;只有它不等于模型声明的默认档时才播报落点。
|
|
137
|
+
*/
|
|
138
|
+
function dmsChoosePlan(reasoning) {
|
|
139
|
+
if (reasoning === void 0) return {
|
|
140
|
+
effort: void 0,
|
|
141
|
+
autoRaised: false
|
|
142
|
+
};
|
|
143
|
+
const best = maxEffortOf(reasoning);
|
|
144
|
+
if (best === void 0 || best === "off" || dmsEffortRank(best) === void 0) return {
|
|
145
|
+
effort: void 0,
|
|
146
|
+
autoRaised: false
|
|
147
|
+
};
|
|
148
|
+
return {
|
|
149
|
+
effort: best,
|
|
150
|
+
autoRaised: best !== reasoning.defaultEffort
|
|
151
|
+
};
|
|
152
|
+
}
|
|
122
153
|
function dmsEffortIndex(levels, id) {
|
|
123
154
|
return levels.findIndex((level) => level.id === id);
|
|
124
155
|
}
|
|
@@ -168,12 +199,15 @@ window.__ModuleLoader__.load({
|
|
|
168
199
|
* 只有当自回滚以来没有任何新的提交改写 committedRef(仍等于回滚前的档位)、
|
|
169
200
|
* 提交纪元未推进(epoch === epochAtCommit,即期间没有发起过新提交——提交失败
|
|
170
201
|
* 回滚后 committedRef 会回到与上一轮回滚相同的值,值比较无法区分「无新提交」
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
202
|
+
* 与「新提交失败回滚」,纪元才能区分)、当前无拖动/提交在途,**且生效模型仍是
|
|
203
|
+
* 发起提交时的那个**(sameModel)时,迟到结果才仍对应当前唯一的意图链——此时
|
|
204
|
+
* UI 处于「回滚但后端已生效」的错位态,应补一次同步而不是保持回滚。
|
|
205
|
+
* 否则以新操作链为准:模型被外部改写(另一入口切换 / 宿主推送新投影 / 重连后
|
|
206
|
+
* 同步出别的选择)时,next/index 是在旧模型的档位表上算出来的,写回 UI 会显示
|
|
207
|
+
* 一个后端并未生效的档位。
|
|
174
208
|
*/
|
|
175
|
-
function dmsShouldAdoptLateSuccess(committed, previous, dragging, committing, epoch, epochAtCommit) {
|
|
176
|
-
return committed === previous && epoch === epochAtCommit && !dragging && !committing;
|
|
209
|
+
function dmsShouldAdoptLateSuccess(committed, previous, dragging, committing, epoch, epochAtCommit, sameModel) {
|
|
210
|
+
return sameModel && committed === previous && epoch === epochAtCommit && !dragging && !committing;
|
|
177
211
|
}
|
|
178
212
|
/**
|
|
179
213
|
* 拖动终止事件是否对应当前活动拖动:pointerActive 为假(拖动已结束/从未开始)
|
|
@@ -525,6 +559,11 @@ window.__ModuleLoader__.load({
|
|
|
525
559
|
* 拖出屏外)时,返回钳到视口内的 left 值,调用方改用 left 锚定。
|
|
526
560
|
* 返回 undefined 表示保持默认右锚定。
|
|
527
561
|
* menuWidth 传菜单实际渲染宽度(offsetWidth),与 CSS `min(280px, 100vw-32px)` 解耦。
|
|
562
|
+
*
|
|
563
|
+
* **坐标系**:入参与返回值都是**视口坐标**。菜单是 .dms-root(position:
|
|
564
|
+
* relative)内的 absolute 元素,left 的参考系是 root 的 padding box —— 调用方
|
|
565
|
+
* 必须减去 root 的左缘再写进 style.left,否则窄窗口下菜单整体右移、横向溢出
|
|
566
|
+
* 时整幅画到屏外。
|
|
528
567
|
*/
|
|
529
568
|
function dmsMenuLeft(rootRight, menuWidth, viewportWidth, margin) {
|
|
530
569
|
if (rootRight - menuWidth >= margin && rootRight <= viewportWidth - margin) return void 0;
|
|
@@ -533,6 +572,26 @@ window.__ModuleLoader__.load({
|
|
|
533
572
|
//#endregion
|
|
534
573
|
//#region src/client/roving.ts
|
|
535
574
|
/**
|
|
575
|
+
* dsh-model-selector — 菜单 roving tabindex 的纯决策逻辑(node 可测)。
|
|
576
|
+
*
|
|
577
|
+
* 模型列表可达数百行,若每行都是 Tab 停靠点,Tab 键会陷入行海;roving
|
|
578
|
+
* tabindex(WAI-ARIA menu 模式的通行做法)只让「活动行」留在 Tab 序里,
|
|
579
|
+
* 其余行 tabindex=-1、仅方向键可达。本模块只负责一件事:给定当前渲染的
|
|
580
|
+
* 行集合与选中行,算出 Tab 从搜索框进入列表时应停靠的默认行键。
|
|
581
|
+
*/
|
|
582
|
+
/**
|
|
583
|
+
* 模型行键的**唯一构造点**:菜单 DOM 的 `data-row-key`、roving 决策与行焦点
|
|
584
|
+
* 回调必须同源。此前这三处各自拼模板串,一次局部改名就能让 dmsDefaultRowKey
|
|
585
|
+
* 返回的键匹配不到任何行(Tab 落点静默失效),且测试拿自制副本验证不到。
|
|
586
|
+
*/
|
|
587
|
+
function dmsRowKey(providerId, modelId) {
|
|
588
|
+
return `${providerId}\u0000${modelId}`;
|
|
589
|
+
}
|
|
590
|
+
/** 组头行键(分组视图里组头也参与方向键导航)。 */
|
|
591
|
+
function dmsHeaderKey(providerId) {
|
|
592
|
+
return `header:${providerId}`;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
536
595
|
* Tab 默认落点:搜索态取「选中行若在命中集内,否则首个命中」;分组态取
|
|
537
596
|
* 「选中行若可见,否则首个可见模型行」;全部折叠(无可见模型行)时回退到
|
|
538
597
|
* 首个组头。返回 null 表示当前没有任何可聚焦行(空态由菜单外的 role=status
|
|
@@ -547,6 +606,144 @@ window.__ModuleLoader__.load({
|
|
|
547
606
|
return set.headerKeys[0] ?? null;
|
|
548
607
|
}
|
|
549
608
|
//#endregion
|
|
609
|
+
//#region src/client/search.ts
|
|
610
|
+
/**
|
|
611
|
+
* 搜索归一化:NFKD 分解 → 去组合标记 → 小写。
|
|
612
|
+
*
|
|
613
|
+
* 比裸 `toLowerCase()` 宽容:`'İstanbul'` 能被 `'istanbul'` 命中(裸 toLowerCase
|
|
614
|
+
* 把 U+0130 折成 `'i̇'` 两个码位,与 ASCII `'i'` 反而不匹配 → 漏检;`'café'` 同理)。
|
|
615
|
+
* 注意它**可能改变码位长度**,所以不能用它的下标去切原串 —— 高亮区间另走
|
|
616
|
+
* {@link dmsNameHit},只在长度不变时给区间。
|
|
617
|
+
*/
|
|
618
|
+
function dmsFold(text) {
|
|
619
|
+
return text.normalize("NFKD").replace(/\p{M}+/gu, "").toLowerCase();
|
|
620
|
+
}
|
|
621
|
+
/** 搜索词归一化(去首尾空白后折叠)。空串 = 非搜索态。 */
|
|
622
|
+
function dmsNormalizeQuery(raw) {
|
|
623
|
+
return dmsFold(raw.trim());
|
|
624
|
+
}
|
|
625
|
+
/** 模型条目的搜索文本(haystack)。与 choices 构建处同源,避免两处漂移。 */
|
|
626
|
+
function dmsHaystack(group, model) {
|
|
627
|
+
return dmsFold(`${model.name}\n${model.description ?? ""}\n${group.name}\n${model.id}\n${group.id}`);
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* 名称内的命中区间:折叠后长度不变时用下标定位;长度变化(含会改变码位数的
|
|
631
|
+
* 大小写/变音符映射)时返回 null —— 宁可不高亮,也不能用错位的区间去切原串
|
|
632
|
+
* (那会把高亮画到别的字符上,甚至越界)。
|
|
633
|
+
*/
|
|
634
|
+
function dmsNameHit(name, normalizedQuery) {
|
|
635
|
+
if (normalizedQuery === "") return null;
|
|
636
|
+
const folded = dmsFold(name);
|
|
637
|
+
if (folded.length !== name.length) return null;
|
|
638
|
+
const at = folded.indexOf(normalizedQuery);
|
|
639
|
+
return at < 0 ? null : {
|
|
640
|
+
start: at,
|
|
641
|
+
end: at + normalizedQuery.length
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* 搜索:遍历 choices 计数全部命中,只构造窗口内(前 limit 条)的渲染条目 ——
|
|
646
|
+
* 宽泛关键词(单字母)命中数百条时,每次击键省掉数百次对象分配与名称匹配;
|
|
647
|
+
* total 单独累计,播报与「仅显示前 N 条」提示仍然准确。
|
|
648
|
+
*/
|
|
649
|
+
function dmsSearchHits(choices, normalizedQuery, limit) {
|
|
650
|
+
if (normalizedQuery === "") return null;
|
|
651
|
+
const items = [];
|
|
652
|
+
let total = 0;
|
|
653
|
+
for (const choice of choices) {
|
|
654
|
+
if (!choice.haystack.includes(normalizedQuery)) continue;
|
|
655
|
+
total += 1;
|
|
656
|
+
if (items.length >= limit) continue;
|
|
657
|
+
items.push({
|
|
658
|
+
group: choice.group,
|
|
659
|
+
model: choice.model,
|
|
660
|
+
nameHit: dmsNameHit(choice.model.name, normalizedQuery)
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
return {
|
|
664
|
+
items,
|
|
665
|
+
total
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* 空态判定:**无模型优先于无命中** —— 目录为空而用户又输了关键词时,真相是
|
|
670
|
+
* 「没有可用的模型」(该引导用户去配置/加载目录),不是「没有匹配关键词的模型」
|
|
671
|
+
* (那会引导用户去改关键词,而无论改什么都不会有结果)。
|
|
672
|
+
*/
|
|
673
|
+
function dmsMenuEmptyState(input) {
|
|
674
|
+
if (input.status === "ready" && input.choiceCount === 0) return "noModels";
|
|
675
|
+
if (input.searching && input.hitCount === 0) return "noHits";
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
//#endregion
|
|
679
|
+
//#region src/client/keys.ts
|
|
680
|
+
/**
|
|
681
|
+
* 按键 → 动作。无行可去时对方向键/Home/End 返回 none 而不是吞键:列表为空
|
|
682
|
+
* (加载中/无命中/全折叠)时搜索框里的上下键仍应有移光标语义,preventDefault
|
|
683
|
+
* 由调用方在真正执行动作时才发。
|
|
684
|
+
*/
|
|
685
|
+
function dmsMenuKeyAction(input) {
|
|
686
|
+
if (input.composing) return { type: "none" };
|
|
687
|
+
const { key, open, fromSearch, inMenu, onNonInput, hasQuery, rowCount, hitCount } = input;
|
|
688
|
+
if (key === "Escape" && open) {
|
|
689
|
+
if (hasQuery && inMenu) return {
|
|
690
|
+
type: "clearQuery",
|
|
691
|
+
focusSearch: !fromSearch
|
|
692
|
+
};
|
|
693
|
+
return { type: "close" };
|
|
694
|
+
}
|
|
695
|
+
if (!open) return { type: "none" };
|
|
696
|
+
if ((key === "ArrowDown" || key === "ArrowUp") && (onNonInput || fromSearch)) {
|
|
697
|
+
if (rowCount === 0) return { type: "none" };
|
|
698
|
+
return {
|
|
699
|
+
type: "moveFocus",
|
|
700
|
+
offset: key === "ArrowDown" ? 1 : -1
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
if ((key === "Home" || key === "End") && onNonInput) {
|
|
704
|
+
if (rowCount === 0) return { type: "none" };
|
|
705
|
+
return {
|
|
706
|
+
type: "focusEdge",
|
|
707
|
+
last: key === "End"
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
if (key === "Enter" && fromSearch && hitCount > 0) return { type: "chooseFirstHit" };
|
|
711
|
+
return { type: "none" };
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* roving 环绕下标:焦点不在任何行上(activeIndex < 0)时向下从首行进、向上从
|
|
715
|
+
* 末行进;已在某行上时按 offset 循环。length <= 0 返回 -1(无可去之处)。
|
|
716
|
+
*/
|
|
717
|
+
function dmsNextRowIndex(activeIndex, offset, length) {
|
|
718
|
+
if (length <= 0) return -1;
|
|
719
|
+
return ((activeIndex < 0 ? offset > 0 ? -1 : 0 : activeIndex) + offset + length) % length;
|
|
720
|
+
}
|
|
721
|
+
//#endregion
|
|
722
|
+
//#region src/client/copy.ts
|
|
723
|
+
/**
|
|
724
|
+
* trigger 文案。目录成员资格只是参考(routable 契约):current 匹配不到任何
|
|
725
|
+
* group 不代表没有选择 —— 此时显示 provider/model 原始 id,而不是「选择模型」。
|
|
726
|
+
*/
|
|
727
|
+
function dmsTriggerCopy(input, t) {
|
|
728
|
+
const { current, modelName, providerName, effortLabel, waiting } = input;
|
|
729
|
+
const modelLabel = modelName ?? (waiting ? t("trigger.loading") : current === null ? t("trigger.fallback") : `${current.provider}/${current.model}`);
|
|
730
|
+
const providerLabel = modelName === void 0 ? void 0 : providerName;
|
|
731
|
+
const label = effortLabel === void 0 ? modelLabel : `${modelLabel} · ${effortLabel}`;
|
|
732
|
+
const title = providerLabel === void 0 ? label : `${providerLabel} · ${label}`;
|
|
733
|
+
const named = providerLabel === void 0 ? modelLabel : `${providerLabel} ${modelLabel}`;
|
|
734
|
+
const ariaModel = modelName === void 0 ? current === null ? null : `${current.provider}/${current.model}` : named;
|
|
735
|
+
return {
|
|
736
|
+
modelLabel,
|
|
737
|
+
providerLabel,
|
|
738
|
+
label,
|
|
739
|
+
title,
|
|
740
|
+
aria: waiting ? t("trigger.loading") : ariaModel === null ? t("trigger.selectAria") : effortLabel === void 0 ? t("trigger.aria", { model: ariaModel }) : t("trigger.ariaEffort", {
|
|
741
|
+
model: ariaModel,
|
|
742
|
+
effort: effortLabel
|
|
743
|
+
})
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
//#endregion
|
|
550
747
|
//#region src/client/ModelSelect.tsx
|
|
551
748
|
/**
|
|
552
749
|
* ModelSelect: the enhanced composer model seat (`conversation.input.model`).
|
|
@@ -563,9 +760,9 @@ window.__ModuleLoader__.load({
|
|
|
563
760
|
* reused from @deepseek-ai/dsh-client-ui-primitives at runtime; colors
|
|
564
761
|
* come from `--dsw-*` tokens in the injected stylesheet.
|
|
565
762
|
*/
|
|
566
|
-
const IconChevronDown = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
567
|
-
const IconCheck = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
568
|
-
const IconClear = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
763
|
+
const IconChevronDown = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { size: 14 });
|
|
764
|
+
const IconCheck = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutlineRegular, { size: 14 });
|
|
765
|
+
const IconClear = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseFillRegular, { size: 14 });
|
|
569
766
|
/**
|
|
570
767
|
* How long a successfully loaded directory snapshot is trusted before the
|
|
571
768
|
* menu re-fetches it over RPC. The snapshot lives in the per-session store,
|
|
@@ -582,12 +779,14 @@ window.__ModuleLoader__.load({
|
|
|
582
779
|
* 必须释放滑杆的 committing 锁并回滚,否则滑杆被锁死到菜单关闭。
|
|
583
780
|
* 调法:后端/网络慢时调大避免误回滚(滑杆锁死时间随之变长);调小更早释放交互。 */
|
|
584
781
|
const EFFORT_COMMIT_TIMEOUT_MS = 12e3;
|
|
585
|
-
const EffortSlider = react.memo(function EffortSlider({ state, select, t }) {
|
|
782
|
+
const EffortSlider = react.memo(function EffortSlider({ state, select, t, onSelectFailure }) {
|
|
586
783
|
const levels = dmsSliderLevels(state);
|
|
587
784
|
const [effort, setEffort] = react.useState(() => levels[dmsEffectiveEffortIndex(levels, state)]?.id ?? "");
|
|
588
785
|
const [preview, setPreview] = react.useState(() => dmsEffectiveEffortIndex(levels, state));
|
|
589
786
|
const [committing, setCommitting] = react.useState(false);
|
|
590
787
|
const [localError, setLocalError] = react.useState(null);
|
|
788
|
+
const stateRef = react.useRef(state);
|
|
789
|
+
stateRef.current = state;
|
|
591
790
|
const canvasRef = react.useRef(null);
|
|
592
791
|
const inputRef = react.useRef(null);
|
|
593
792
|
const committedRef = react.useRef("");
|
|
@@ -616,7 +815,7 @@ window.__ModuleLoader__.load({
|
|
|
616
815
|
setLocalError(null);
|
|
617
816
|
}, [
|
|
618
817
|
available,
|
|
619
|
-
levels
|
|
818
|
+
levels,
|
|
620
819
|
state.current?.provider,
|
|
621
820
|
state.current?.model,
|
|
622
821
|
state.current?.reasoningEffort
|
|
@@ -658,7 +857,13 @@ window.__ModuleLoader__.load({
|
|
|
658
857
|
if (dmsEffortBusy(committingRef.current, state.status)) return;
|
|
659
858
|
const index = dmsClampIndex(raw, levels.length);
|
|
660
859
|
const next = levels[index]?.id;
|
|
661
|
-
if (next === void 0
|
|
860
|
+
if (next === void 0) return;
|
|
861
|
+
if (next === committedRef.current) {
|
|
862
|
+
previewRef.current = index;
|
|
863
|
+
setPreview(index);
|
|
864
|
+
setEffort(next);
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
662
867
|
committingRef.current = true;
|
|
663
868
|
const previous = committedRef.current;
|
|
664
869
|
commitEpochRef.current += 1;
|
|
@@ -671,7 +876,8 @@ window.__ModuleLoader__.load({
|
|
|
671
876
|
try {
|
|
672
877
|
const current = state.current;
|
|
673
878
|
if (current === null) throw new Error(t("empty.efforts"));
|
|
674
|
-
|
|
879
|
+
const modelKeyAtCommit = dmsRowKey(current.provider, current.model);
|
|
880
|
+
const result = await new Promise((resolve, reject) => {
|
|
675
881
|
let settled = false;
|
|
676
882
|
const timer = window.setTimeout(() => {
|
|
677
883
|
if (settled) return;
|
|
@@ -684,9 +890,11 @@ window.__ModuleLoader__.load({
|
|
|
684
890
|
provider: current.provider,
|
|
685
891
|
model: current.model,
|
|
686
892
|
reasoningEffort: next
|
|
687
|
-
}).then((
|
|
893
|
+
}).then((result) => {
|
|
688
894
|
if (settled) {
|
|
689
|
-
|
|
895
|
+
const latest = stateRef.current.current;
|
|
896
|
+
const sameModel = latest !== null && dmsRowKey(latest.provider, latest.model) === modelKeyAtCommit;
|
|
897
|
+
if (result?.ok === true && mountedRef.current && dmsShouldAdoptLateSuccess(committedRef.current, previous, draggingRef.current, committingRef.current, commitEpochRef.current, epochAtCommit, sameModel)) {
|
|
690
898
|
committedRef.current = next;
|
|
691
899
|
previewRef.current = index;
|
|
692
900
|
setEffort(next);
|
|
@@ -698,7 +906,7 @@ window.__ModuleLoader__.load({
|
|
|
698
906
|
settled = true;
|
|
699
907
|
window.clearTimeout(timer);
|
|
700
908
|
commitTimerRef.current = null;
|
|
701
|
-
resolve(
|
|
909
|
+
resolve(result);
|
|
702
910
|
}, (cause) => {
|
|
703
911
|
if (settled) return;
|
|
704
912
|
settled = true;
|
|
@@ -706,7 +914,11 @@ window.__ModuleLoader__.load({
|
|
|
706
914
|
commitTimerRef.current = null;
|
|
707
915
|
reject(cause);
|
|
708
916
|
});
|
|
709
|
-
})
|
|
917
|
+
});
|
|
918
|
+
if (result === void 0 || !result.ok) {
|
|
919
|
+
if (result !== void 0) console.warn("[dsh-model-selector] effort select failed:", result.error);
|
|
920
|
+
throw new Error(t("effort.failed"));
|
|
921
|
+
}
|
|
710
922
|
if (mountedRef.current) {
|
|
711
923
|
committedRef.current = next;
|
|
712
924
|
previewRef.current = index;
|
|
@@ -714,23 +926,29 @@ window.__ModuleLoader__.load({
|
|
|
714
926
|
setPreview(index);
|
|
715
927
|
}
|
|
716
928
|
} catch (cause) {
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
929
|
+
if (commitEpochRef.current === epochAtCommit) {
|
|
930
|
+
onSelectFailure();
|
|
931
|
+
const restore = Math.max(0, dmsEffortIndex(levels, previous));
|
|
932
|
+
committedRef.current = previous;
|
|
933
|
+
previewRef.current = restore;
|
|
934
|
+
if (mountedRef.current) {
|
|
935
|
+
setEffort(previous);
|
|
936
|
+
setPreview(restore);
|
|
937
|
+
setLocalError(cause instanceof Error ? cause.message : String(cause));
|
|
938
|
+
}
|
|
724
939
|
}
|
|
725
940
|
} finally {
|
|
726
|
-
|
|
727
|
-
|
|
941
|
+
if (commitEpochRef.current === epochAtCommit) {
|
|
942
|
+
committingRef.current = false;
|
|
943
|
+
if (mountedRef.current) setCommitting(false);
|
|
944
|
+
}
|
|
728
945
|
}
|
|
729
946
|
}, [
|
|
730
947
|
levels,
|
|
731
948
|
select,
|
|
732
949
|
state,
|
|
733
|
-
t
|
|
950
|
+
t,
|
|
951
|
+
onSelectFailure
|
|
734
952
|
]);
|
|
735
953
|
const drag = useEffortDrag({
|
|
736
954
|
levelCount: () => levels.length,
|
|
@@ -818,14 +1036,14 @@ window.__ModuleLoader__.load({
|
|
|
818
1036
|
className: "dms-effort-desc",
|
|
819
1037
|
children: effortDesc
|
|
820
1038
|
}),
|
|
821
|
-
|
|
1039
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
822
1040
|
className: "dms-sr",
|
|
823
1041
|
role: "status",
|
|
824
|
-
children: error
|
|
1042
|
+
children: error ?? ""
|
|
825
1043
|
}),
|
|
826
|
-
|
|
1044
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
827
1045
|
className: "dms-effort-error",
|
|
828
|
-
children: error
|
|
1046
|
+
children: error ?? ""
|
|
829
1047
|
})
|
|
830
1048
|
]
|
|
831
1049
|
});
|
|
@@ -839,7 +1057,7 @@ window.__ModuleLoader__.load({
|
|
|
839
1057
|
* 但 100 条命中里绝大多数命中区间不变——按值比较 start/end,值相同即跳过
|
|
840
1058
|
* 重渲染,搜索才不卡顿。
|
|
841
1059
|
*/
|
|
842
|
-
const ModelOption = react.memo(function ModelOption({ group, model, showProvider, selected, busy, rowKey, active, onRowFocus, nameHit, t, onChoose }) {
|
|
1060
|
+
const ModelOption = react.memo(function ModelOption({ group, model, showProvider, selected, busy, rowKey, active, searchMode, onRowFocus, nameHit, setsize, posinset, t, onChoose }) {
|
|
843
1061
|
const hit = nameHit === void 0 || nameHit === null || nameHit.start === nameHit.end ? null : [
|
|
844
1062
|
model.name.slice(0, nameHit.start),
|
|
845
1063
|
model.name.slice(nameHit.start, nameHit.end),
|
|
@@ -847,8 +1065,12 @@ window.__ModuleLoader__.load({
|
|
|
847
1065
|
];
|
|
848
1066
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
849
1067
|
type: "button",
|
|
850
|
-
role: "menuitemradio",
|
|
851
|
-
"aria-checked": selected,
|
|
1068
|
+
role: searchMode ? "option" : "menuitemradio",
|
|
1069
|
+
"aria-checked": searchMode ? void 0 : selected,
|
|
1070
|
+
"aria-selected": searchMode ? selected : void 0,
|
|
1071
|
+
id: searchMode ? `dms-opt-${encodeURIComponent(rowKey)}` : void 0,
|
|
1072
|
+
"aria-setsize": searchMode && setsize !== void 0 && setsize > posinset ? setsize : void 0,
|
|
1073
|
+
"aria-posinset": searchMode && posinset !== void 0 ? posinset : void 0,
|
|
852
1074
|
"data-row-key": rowKey,
|
|
853
1075
|
tabIndex: active ? 0 : -1,
|
|
854
1076
|
className: `dms-model-option${selected ? " dms-model-optionSelected" : ""}`,
|
|
@@ -903,9 +1125,12 @@ window.__ModuleLoader__.load({
|
|
|
903
1125
|
if (prev.busy !== next.busy) return false;
|
|
904
1126
|
if (prev.rowKey !== next.rowKey) return false;
|
|
905
1127
|
if (prev.active !== next.active) return false;
|
|
1128
|
+
if (prev.searchMode !== next.searchMode) return false;
|
|
906
1129
|
if (prev.onRowFocus !== next.onRowFocus) return false;
|
|
907
1130
|
if (prev.t !== next.t) return false;
|
|
908
1131
|
if (prev.onChoose !== next.onChoose) return false;
|
|
1132
|
+
if (prev.setsize !== next.setsize) return false;
|
|
1133
|
+
if (prev.posinset !== next.posinset) return false;
|
|
909
1134
|
if (prev.nameHit === next.nameHit) return true;
|
|
910
1135
|
if (prev.nameHit === null || prev.nameHit === void 0) return false;
|
|
911
1136
|
if (next.nameHit === null || next.nameHit === void 0) return false;
|
|
@@ -932,6 +1157,12 @@ window.__ModuleLoader__.load({
|
|
|
932
1157
|
failed
|
|
933
1158
|
});
|
|
934
1159
|
}, []);
|
|
1160
|
+
const markSelectFailure = react.useCallback(() => {
|
|
1161
|
+
lastActionRef.current = "select";
|
|
1162
|
+
}, []);
|
|
1163
|
+
const dismissToast = react.useCallback(() => {
|
|
1164
|
+
setToast(null);
|
|
1165
|
+
}, []);
|
|
935
1166
|
const lastActionRef = react.useRef("load");
|
|
936
1167
|
const rootRef = react.useRef(null);
|
|
937
1168
|
const triggerRef = react.useRef(null);
|
|
@@ -941,7 +1172,7 @@ window.__ModuleLoader__.load({
|
|
|
941
1172
|
const choices = react.useMemo(() => state.groups.flatMap((group) => group.models.map((model) => ({
|
|
942
1173
|
group,
|
|
943
1174
|
model,
|
|
944
|
-
haystack:
|
|
1175
|
+
haystack: dmsHaystack(group, model),
|
|
945
1176
|
selection: {
|
|
946
1177
|
provider: group.id,
|
|
947
1178
|
model: model.id,
|
|
@@ -957,41 +1188,30 @@ window.__ModuleLoader__.load({
|
|
|
957
1188
|
const effectiveEffort = state.current?.reasoningEffort ?? reasoning?.defaultEffort;
|
|
958
1189
|
const effortLabel = reasoning === void 0 ? void 0 : effectiveEffort === void 0 ? t("effort.providerDefault") : reasoning.efforts.find((level) => level.id === effectiveEffort)?.name ?? effectiveEffort;
|
|
959
1190
|
const busy = state.status === "selecting";
|
|
960
|
-
const
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
items.push({
|
|
976
|
-
group: choice.group,
|
|
977
|
-
model: choice.model,
|
|
978
|
-
nameHit
|
|
979
|
-
});
|
|
980
|
-
}
|
|
981
|
-
return {
|
|
982
|
-
items,
|
|
983
|
-
total
|
|
984
|
-
};
|
|
985
|
-
}, [choices, normalized]);
|
|
1191
|
+
const chooseInputRef = react.useRef({
|
|
1192
|
+
busy,
|
|
1193
|
+
current: state.current,
|
|
1194
|
+
choices,
|
|
1195
|
+
open
|
|
1196
|
+
});
|
|
1197
|
+
chooseInputRef.current = {
|
|
1198
|
+
busy,
|
|
1199
|
+
current: state.current,
|
|
1200
|
+
choices,
|
|
1201
|
+
open
|
|
1202
|
+
};
|
|
1203
|
+
const normalized = dmsNormalizeQuery(query);
|
|
1204
|
+
const hits = react.useMemo(() => dmsSearchHits(choices, normalized, MAX_VISIBLE_HITS), [choices, normalized]);
|
|
1205
|
+
const visibleModelKeys = react.useMemo(() => state.groups.flatMap((g) => collapsed.has(g.id) ? [] : g.models.map((m) => dmsRowKey(g.id, m.id))), [state.groups, collapsed]);
|
|
986
1206
|
const defaultRowKey = react.useMemo(() => dmsDefaultRowKey({
|
|
987
|
-
hitKeys: hits === null ? null : hits.items.map((h) =>
|
|
988
|
-
modelKeys:
|
|
989
|
-
headerKeys: state.groups.map((g) =>
|
|
990
|
-
selectedKey: state.current === null ? null :
|
|
1207
|
+
hitKeys: hits === null ? null : hits.items.map((h) => dmsRowKey(h.group.id, h.model.id)),
|
|
1208
|
+
modelKeys: visibleModelKeys,
|
|
1209
|
+
headerKeys: state.groups.map((g) => dmsHeaderKey(g.id)),
|
|
1210
|
+
selectedKey: state.current === null ? null : dmsRowKey(state.current.provider, state.current.model)
|
|
991
1211
|
}), [
|
|
992
1212
|
hits,
|
|
1213
|
+
visibleModelKeys,
|
|
993
1214
|
state.groups,
|
|
994
|
-
collapsed,
|
|
995
1215
|
state.current
|
|
996
1216
|
]);
|
|
997
1217
|
react.useEffect(() => {
|
|
@@ -1028,7 +1248,9 @@ window.__ModuleLoader__.load({
|
|
|
1028
1248
|
setMenuAbove(dmsMenuAbove(rect.top, rect.bottom, window.innerHeight));
|
|
1029
1249
|
setBelowMaxHeight(dmsBelowMaxHeight(rect.bottom, window.innerHeight, 420));
|
|
1030
1250
|
const menuWidth = menuRef.current?.offsetWidth ?? 0;
|
|
1031
|
-
|
|
1251
|
+
const left = dmsMenuLeft(rect.right, menuWidth, window.innerWidth, 12);
|
|
1252
|
+
const rootLeft = rootRef.current?.getBoundingClientRect().left ?? 0;
|
|
1253
|
+
setMenuLeft(left === void 0 ? void 0 : left - rootLeft);
|
|
1032
1254
|
};
|
|
1033
1255
|
measure();
|
|
1034
1256
|
let raf = 0;
|
|
@@ -1111,79 +1333,80 @@ window.__ModuleLoader__.load({
|
|
|
1111
1333
|
state.groups.length,
|
|
1112
1334
|
reload
|
|
1113
1335
|
]);
|
|
1336
|
+
const focusTrigger = () => {
|
|
1337
|
+
const trigger = triggerRef.current;
|
|
1338
|
+
if (trigger !== null && !trigger.disabled) {
|
|
1339
|
+
trigger.focus();
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
rootRef.current?.focus();
|
|
1343
|
+
};
|
|
1114
1344
|
const close = react.useCallback((restoreFocus = false) => {
|
|
1115
1345
|
setOpen(false);
|
|
1116
1346
|
setNotice(null);
|
|
1117
1347
|
setQuery("");
|
|
1118
|
-
if (restoreFocus) queueMicrotask(
|
|
1119
|
-
triggerRef.current?.focus();
|
|
1120
|
-
});
|
|
1348
|
+
if (restoreFocus) queueMicrotask(focusTrigger);
|
|
1121
1349
|
}, []);
|
|
1122
1350
|
const dismissOnOutsidePointer = react.useCallback(() => {
|
|
1123
1351
|
close(true);
|
|
1124
1352
|
}, [close]);
|
|
1125
1353
|
(0, _deepseek_ai_dsh_client_ui_primitives.useDismissOnOutsidePointer)(rootRef, open, dismissOnOutsidePointer);
|
|
1126
1354
|
react.useEffect(() => {
|
|
1127
|
-
if (locked && open) close();
|
|
1355
|
+
if (locked && open) close(true);
|
|
1128
1356
|
}, [
|
|
1129
1357
|
locked,
|
|
1130
1358
|
open,
|
|
1131
1359
|
close
|
|
1132
1360
|
]);
|
|
1133
1361
|
const choose = react.useCallback((selection) => {
|
|
1134
|
-
|
|
1135
|
-
if (
|
|
1362
|
+
const { busy: isBusy, current, choices: catalog } = chooseInputRef.current;
|
|
1363
|
+
if (isBusy) return;
|
|
1364
|
+
if (current?.provider === selection.provider && current.model === selection.model) {
|
|
1136
1365
|
setNotice(t("notice.already"));
|
|
1137
1366
|
return;
|
|
1138
1367
|
}
|
|
1139
|
-
const target =
|
|
1140
|
-
const
|
|
1141
|
-
const effort = max === "off" ? void 0 : max;
|
|
1368
|
+
const target = catalog.find((c) => c.selection.provider === selection.provider && c.selection.model === selection.model);
|
|
1369
|
+
const plan = dmsChoosePlan(target?.model.reasoning);
|
|
1142
1370
|
const full = {
|
|
1143
1371
|
provider: selection.provider,
|
|
1144
1372
|
model: selection.model,
|
|
1145
|
-
...effort === void 0 ? {} : { reasoningEffort: effort }
|
|
1373
|
+
...plan.effort === void 0 ? {} : { reasoningEffort: plan.effort }
|
|
1146
1374
|
};
|
|
1147
1375
|
lastActionRef.current = "select";
|
|
1148
|
-
const
|
|
1149
|
-
const autoName = target?.model.reasoning?.efforts.find((level) => level.id === effort)?.name ?? effort ?? "";
|
|
1376
|
+
const autoName = plan.effort === void 0 ? "" : target?.model.reasoning?.efforts.find((level) => level.id === plan.effort)?.name ?? plan.effort;
|
|
1150
1377
|
(async () => {
|
|
1151
1378
|
try {
|
|
1152
|
-
|
|
1379
|
+
const result = await new Promise((resolve, reject) => {
|
|
1153
1380
|
let settled = false;
|
|
1154
1381
|
const timer = window.setTimeout(() => {
|
|
1155
1382
|
if (settled) return;
|
|
1156
1383
|
settled = true;
|
|
1157
1384
|
reject(new Error(t("notice.selectFailed")));
|
|
1158
1385
|
}, EFFORT_COMMIT_TIMEOUT_MS);
|
|
1159
|
-
select(full).then((
|
|
1386
|
+
select(full).then((settledResult) => {
|
|
1160
1387
|
if (settled) return;
|
|
1161
1388
|
settled = true;
|
|
1162
1389
|
window.clearTimeout(timer);
|
|
1163
|
-
resolve(
|
|
1390
|
+
resolve(settledResult);
|
|
1164
1391
|
}, (cause) => {
|
|
1165
1392
|
if (settled) return;
|
|
1166
1393
|
settled = true;
|
|
1167
1394
|
window.clearTimeout(timer);
|
|
1168
1395
|
reject(cause);
|
|
1169
1396
|
});
|
|
1170
|
-
})
|
|
1171
|
-
|
|
1397
|
+
});
|
|
1398
|
+
if (result === void 0 || !result.ok) {
|
|
1399
|
+
const message = result !== void 0 ? `${result.error.code}: ${result.error.message}` : directory.getSnapshot().error;
|
|
1172
1400
|
showToast(message !== null ? t("error.action", { message }) : t("notice.selectFailed"));
|
|
1173
1401
|
return;
|
|
1174
1402
|
}
|
|
1175
|
-
if (
|
|
1176
|
-
|
|
1177
|
-
if (autoRaised) showToast(t("toast.effortAuto", { effort: autoName }), false);
|
|
1178
|
-
}
|
|
1403
|
+
if (chooseInputRef.current.open) close(true);
|
|
1404
|
+
if (plan.autoRaised) showToast(t("toast.effortAuto", { effort: autoName }), false);
|
|
1179
1405
|
} catch {
|
|
1180
1406
|
showToast(t("notice.selectFailed"));
|
|
1181
1407
|
}
|
|
1182
1408
|
})();
|
|
1183
1409
|
}, [
|
|
1184
|
-
busy,
|
|
1185
|
-
state.current,
|
|
1186
|
-
choices,
|
|
1187
1410
|
select,
|
|
1188
1411
|
t,
|
|
1189
1412
|
directory,
|
|
@@ -1204,8 +1427,7 @@ window.__ModuleLoader__.load({
|
|
|
1204
1427
|
if (menu === null) return;
|
|
1205
1428
|
const items = [...menu.querySelectorAll("[data-row-key]")];
|
|
1206
1429
|
if (items.length === 0) return;
|
|
1207
|
-
|
|
1208
|
-
items[((active < 0 ? offset > 0 ? -1 : 0 : active) + offset + items.length) % items.length]?.focus();
|
|
1430
|
+
items[dmsNextRowIndex(items.findIndex((item) => item === document.activeElement), offset, items.length)]?.focus();
|
|
1209
1431
|
};
|
|
1210
1432
|
const focusEdge = (last) => {
|
|
1211
1433
|
const items = menuRef.current?.querySelectorAll("[data-row-key]");
|
|
@@ -1213,65 +1435,70 @@ window.__ModuleLoader__.load({
|
|
|
1213
1435
|
(last ? items[items.length - 1] : items[0])?.focus();
|
|
1214
1436
|
};
|
|
1215
1437
|
const onRootKeyDown = (event) => {
|
|
1216
|
-
if (event.nativeEvent.isComposing) return;
|
|
1217
1438
|
const target = event.target;
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1439
|
+
const action = dmsMenuKeyAction({
|
|
1440
|
+
key: event.key,
|
|
1441
|
+
composing: event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229,
|
|
1442
|
+
open,
|
|
1443
|
+
fromSearch: target instanceof HTMLInputElement && target === searchRef.current,
|
|
1444
|
+
inMenu: menuRef.current?.contains(document.activeElement) === true,
|
|
1445
|
+
onNonInput: !(target instanceof HTMLInputElement),
|
|
1446
|
+
hasQuery: query !== "",
|
|
1447
|
+
rowCount: menuRef.current?.querySelectorAll("[data-row-key]").length ?? 0,
|
|
1448
|
+
hitCount: hits?.items.length ?? 0
|
|
1449
|
+
});
|
|
1450
|
+
switch (action.type) {
|
|
1451
|
+
case "none": return;
|
|
1452
|
+
case "clearQuery":
|
|
1453
|
+
event.preventDefault();
|
|
1221
1454
|
setNotice(null);
|
|
1222
1455
|
setQuery("");
|
|
1223
|
-
if (
|
|
1456
|
+
if (action.focusSearch) searchRef.current?.focus();
|
|
1224
1457
|
return;
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
return;
|
|
1241
|
-
}
|
|
1242
|
-
if (event.key === "Enter" && fromSearch) {
|
|
1243
|
-
if (hits !== null && hits.items.length > 0) {
|
|
1458
|
+
case "close":
|
|
1459
|
+
event.preventDefault();
|
|
1460
|
+
close(true);
|
|
1461
|
+
return;
|
|
1462
|
+
case "moveFocus":
|
|
1463
|
+
event.preventDefault();
|
|
1464
|
+
moveFocus(action.offset);
|
|
1465
|
+
return;
|
|
1466
|
+
case "focusEdge":
|
|
1467
|
+
event.preventDefault();
|
|
1468
|
+
focusEdge(action.last);
|
|
1469
|
+
return;
|
|
1470
|
+
case "chooseFirstHit": {
|
|
1471
|
+
const first = hits?.items[0];
|
|
1472
|
+
if (first === void 0) return;
|
|
1244
1473
|
event.preventDefault();
|
|
1245
|
-
const first = hits.items[0];
|
|
1246
1474
|
choose({
|
|
1247
1475
|
provider: first.group.id,
|
|
1248
1476
|
model: first.model.id
|
|
1249
1477
|
});
|
|
1478
|
+
return;
|
|
1250
1479
|
}
|
|
1251
1480
|
}
|
|
1252
1481
|
};
|
|
1253
1482
|
const onBlur = (event) => {
|
|
1254
|
-
if (busy) return;
|
|
1255
1483
|
const related = event.relatedTarget;
|
|
1484
|
+
if (related instanceof Node && rootRef.current?.contains(related) === true) return;
|
|
1256
1485
|
if (related instanceof Node) {
|
|
1257
|
-
if (rootRef.current?.contains(related)) return;
|
|
1258
1486
|
close();
|
|
1259
1487
|
return;
|
|
1260
1488
|
}
|
|
1261
|
-
close();
|
|
1489
|
+
if (document.visibilityState === "hidden") close();
|
|
1262
1490
|
};
|
|
1263
|
-
const
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
effort: effortLabel
|
|
1271
|
-
});
|
|
1491
|
+
const trigger = dmsTriggerCopy({
|
|
1492
|
+
current: state.current,
|
|
1493
|
+
modelName: currentChoice?.model.name,
|
|
1494
|
+
providerName: currentChoice?.group.name,
|
|
1495
|
+
effortLabel,
|
|
1496
|
+
waiting: state.current === null && state.status === "loading"
|
|
1497
|
+
}, t);
|
|
1272
1498
|
const renderErrorStrip = () => {
|
|
1273
1499
|
if (state.error !== null && lastActionRef.current === "load") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1274
1500
|
className: "dms-error",
|
|
1501
|
+
role: "alert",
|
|
1275
1502
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("error.action", { message: state.error }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1276
1503
|
type: "button",
|
|
1277
1504
|
className: "dms-retry",
|
|
@@ -1293,11 +1520,16 @@ window.__ModuleLoader__.load({
|
|
|
1293
1520
|
children: t("action.reload")
|
|
1294
1521
|
})]
|
|
1295
1522
|
}, failure.id));
|
|
1296
|
-
const
|
|
1297
|
-
|
|
1523
|
+
const emptyState = dmsMenuEmptyState({
|
|
1524
|
+
status: state.status,
|
|
1525
|
+
choiceCount: choices.length,
|
|
1526
|
+
searching: hits !== null,
|
|
1527
|
+
hitCount: hits?.total ?? 0
|
|
1528
|
+
});
|
|
1298
1529
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1299
1530
|
ref: rootRef,
|
|
1300
1531
|
className: "dms-root",
|
|
1532
|
+
tabIndex: -1,
|
|
1301
1533
|
onKeyDown: onRootKeyDown,
|
|
1302
1534
|
onBlur,
|
|
1303
1535
|
children: [
|
|
@@ -1305,21 +1537,21 @@ window.__ModuleLoader__.load({
|
|
|
1305
1537
|
ref: triggerRef,
|
|
1306
1538
|
type: "button",
|
|
1307
1539
|
className: "dms-trigger",
|
|
1308
|
-
"aria-label":
|
|
1540
|
+
"aria-label": trigger.aria,
|
|
1309
1541
|
"aria-haspopup": "menu",
|
|
1310
1542
|
"aria-expanded": open,
|
|
1311
1543
|
"aria-controls": open ? `${id}-menu` : void 0,
|
|
1312
|
-
title:
|
|
1544
|
+
title: trigger.title,
|
|
1313
1545
|
disabled: locked,
|
|
1314
1546
|
onClick: () => open ? close() : show(),
|
|
1315
1547
|
children: [
|
|
1316
1548
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1317
1549
|
className: "dms-triggerLabel",
|
|
1318
|
-
children: modelLabel
|
|
1550
|
+
children: trigger.modelLabel
|
|
1319
1551
|
}),
|
|
1320
|
-
providerLabel !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1552
|
+
trigger.providerLabel !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1321
1553
|
className: "dms-triggerProvider",
|
|
1322
|
-
children: providerLabel
|
|
1554
|
+
children: trigger.providerLabel
|
|
1323
1555
|
}),
|
|
1324
1556
|
effortLabel !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1325
1557
|
className: "dms-triggerEffort",
|
|
@@ -1343,15 +1575,16 @@ window.__ModuleLoader__.load({
|
|
|
1343
1575
|
}
|
|
1344
1576
|
},
|
|
1345
1577
|
className: "dms-menu dms-menuModel" + (menuAbove ? "" : " dms-menuBelow"),
|
|
1346
|
-
"aria-busy": state.status === "loading" || busy,
|
|
1347
1578
|
children: [
|
|
1348
1579
|
state.status === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1349
1580
|
className: "dms-status",
|
|
1581
|
+
role: "status",
|
|
1350
1582
|
children: t("status.loading")
|
|
1351
1583
|
}),
|
|
1352
1584
|
renderErrorStrip(),
|
|
1353
1585
|
state.failures.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1354
1586
|
className: "dms-failures",
|
|
1587
|
+
role: "alert",
|
|
1355
1588
|
children: renderFailures(state.failures)
|
|
1356
1589
|
}),
|
|
1357
1590
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1363,7 +1596,10 @@ window.__ModuleLoader__.load({
|
|
|
1363
1596
|
value: query,
|
|
1364
1597
|
placeholder: t("search.placeholder"),
|
|
1365
1598
|
"aria-label": t("search.placeholder"),
|
|
1366
|
-
"
|
|
1599
|
+
role: "combobox",
|
|
1600
|
+
"aria-expanded": hits !== null && hits.total > 0,
|
|
1601
|
+
"aria-autocomplete": "list",
|
|
1602
|
+
"aria-controls": emptyState === null ? `${id}-groups` : void 0,
|
|
1367
1603
|
autoComplete: "off",
|
|
1368
1604
|
enterKeyHint: "search",
|
|
1369
1605
|
spellCheck: false,
|
|
@@ -1387,28 +1623,34 @@ window.__ModuleLoader__.load({
|
|
|
1387
1623
|
})
|
|
1388
1624
|
})]
|
|
1389
1625
|
}),
|
|
1390
|
-
|
|
1626
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1391
1627
|
className: "dms-sr",
|
|
1392
1628
|
role: "status",
|
|
1393
|
-
children: t("search.status", { count: String(hits.total) })
|
|
1629
|
+
children: hits !== null && hits.total > 0 ? t("search.status", { count: String(hits.total) }) : ""
|
|
1630
|
+
}),
|
|
1631
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1632
|
+
className: "dms-sr",
|
|
1633
|
+
role: "note",
|
|
1634
|
+
children: t("badge.reasoningHint")
|
|
1394
1635
|
}),
|
|
1395
|
-
noHits && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1636
|
+
emptyState === "noHits" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1396
1637
|
className: "dms-empty dms-groupsFill",
|
|
1397
1638
|
role: "status",
|
|
1398
1639
|
children: t("search.noMatch", { query: query.trim() })
|
|
1399
1640
|
}),
|
|
1400
|
-
noModels && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1641
|
+
emptyState === "noModels" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1401
1642
|
className: "dms-empty dms-groupsFill",
|
|
1402
1643
|
role: "status",
|
|
1403
1644
|
children: t("empty.models")
|
|
1404
1645
|
}),
|
|
1405
|
-
|
|
1646
|
+
emptyState === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1406
1647
|
className: "dms-groups",
|
|
1407
1648
|
id: `${id}-groups`,
|
|
1408
|
-
role: "menu",
|
|
1649
|
+
role: hits !== null ? "listbox" : "menu",
|
|
1650
|
+
"aria-busy": state.status === "loading" || busy,
|
|
1409
1651
|
"aria-label": t("menu.aria"),
|
|
1410
|
-
children: hits !== null ? hits.items.map((hit) => {
|
|
1411
|
-
const rowKey =
|
|
1652
|
+
children: hits !== null ? hits.items.map((hit, index) => {
|
|
1653
|
+
const rowKey = dmsRowKey(hit.group.id, hit.model.id);
|
|
1412
1654
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelOption, {
|
|
1413
1655
|
group: hit.group,
|
|
1414
1656
|
model: hit.model,
|
|
@@ -1417,13 +1659,17 @@ window.__ModuleLoader__.load({
|
|
|
1417
1659
|
busy,
|
|
1418
1660
|
rowKey,
|
|
1419
1661
|
active: rowKey === activeRowKey,
|
|
1662
|
+
searchMode: true,
|
|
1420
1663
|
onRowFocus,
|
|
1421
1664
|
nameHit: hit.nameHit,
|
|
1665
|
+
setsize: hits.total,
|
|
1666
|
+
posinset: index + 1,
|
|
1422
1667
|
t,
|
|
1423
1668
|
onChoose: choose
|
|
1424
1669
|
}, rowKey);
|
|
1425
1670
|
}) : state.groups.map((group) => {
|
|
1426
1671
|
const headingId = `${id}-${group.id}`;
|
|
1672
|
+
const headerKey = dmsHeaderKey(group.id);
|
|
1427
1673
|
const isCollapsed = collapsed.has(group.id);
|
|
1428
1674
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1429
1675
|
"data-group-id": group.id,
|
|
@@ -1433,14 +1679,15 @@ window.__ModuleLoader__.load({
|
|
|
1433
1679
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1434
1680
|
type: "button",
|
|
1435
1681
|
role: "menuitem",
|
|
1436
|
-
"data-row-key":
|
|
1437
|
-
tabIndex: -1,
|
|
1682
|
+
"data-row-key": headerKey,
|
|
1683
|
+
tabIndex: activeRowKey === headerKey ? 0 : -1,
|
|
1438
1684
|
className: "dms-groupHeader",
|
|
1439
1685
|
"aria-expanded": !isCollapsed,
|
|
1440
1686
|
"aria-label": t("group.toggleAria", {
|
|
1441
1687
|
name: group.name,
|
|
1442
1688
|
count: String(group.models.length)
|
|
1443
1689
|
}),
|
|
1690
|
+
onFocus: () => onRowFocus(headerKey),
|
|
1444
1691
|
onClick: () => toggleCollapse(group.id),
|
|
1445
1692
|
children: [
|
|
1446
1693
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -1459,7 +1706,7 @@ window.__ModuleLoader__.load({
|
|
|
1459
1706
|
})
|
|
1460
1707
|
]
|
|
1461
1708
|
}), !isCollapsed && group.models.map((model) => {
|
|
1462
|
-
const rowKey =
|
|
1709
|
+
const rowKey = dmsRowKey(group.id, model.id);
|
|
1463
1710
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelOption, {
|
|
1464
1711
|
group,
|
|
1465
1712
|
model,
|
|
@@ -1468,6 +1715,7 @@ window.__ModuleLoader__.load({
|
|
|
1468
1715
|
busy,
|
|
1469
1716
|
rowKey,
|
|
1470
1717
|
active: rowKey === activeRowKey,
|
|
1718
|
+
searchMode: false,
|
|
1471
1719
|
onRowFocus,
|
|
1472
1720
|
t,
|
|
1473
1721
|
onChoose: choose
|
|
@@ -1492,23 +1740,22 @@ window.__ModuleLoader__.load({
|
|
|
1492
1740
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EffortSlider, {
|
|
1493
1741
|
state,
|
|
1494
1742
|
select,
|
|
1495
|
-
t
|
|
1743
|
+
t,
|
|
1744
|
+
onSelectFailure: markSelectFailure
|
|
1496
1745
|
})]
|
|
1497
1746
|
}),
|
|
1498
|
-
|
|
1747
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1499
1748
|
className: "dms-notice",
|
|
1500
1749
|
role: "status",
|
|
1501
|
-
children: notice
|
|
1750
|
+
children: notice ?? ""
|
|
1502
1751
|
})
|
|
1503
1752
|
]
|
|
1504
1753
|
}) : null,
|
|
1505
1754
|
toast !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
1506
1755
|
text: toast.text,
|
|
1507
|
-
icon: toast.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
1756
|
+
icon: toast.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutlineRegular, { size: 14 }) : void 0,
|
|
1508
1757
|
anchor: rootRef.current?.closest("[data-composer-card]") ?? null,
|
|
1509
|
-
onDone:
|
|
1510
|
-
setToast(null);
|
|
1511
|
-
}
|
|
1758
|
+
onDone: dismissToast
|
|
1512
1759
|
}, toast.seq)
|
|
1513
1760
|
]
|
|
1514
1761
|
});
|
|
@@ -1522,12 +1769,18 @@ window.__ModuleLoader__.load({
|
|
|
1522
1769
|
* apply and removed again on unload. Class names are prefixed `dms-` so they
|
|
1523
1770
|
* cannot collide with CSS-module hashes from other plugins. Colors come only
|
|
1524
1771
|
* from `--dsw-*` theme tokens, matching the shipped Menu material.
|
|
1772
|
+
*
|
|
1773
|
+
* 显式 `: string` 注解:不加注解时 tsc 会把整份 CSS 推断成字面量类型并写进
|
|
1774
|
+
* lib/types/client/styles.d.ts(19KB 的 d.ts 只为声明一个常量),发布包里
|
|
1775
|
+
* 白白多出十几 KB 且每次改样式都全量重写。
|
|
1525
1776
|
*/
|
|
1526
1777
|
const CSS = `
|
|
1527
1778
|
.dms-root {
|
|
1528
1779
|
position: relative;
|
|
1529
1780
|
min-width: 0;
|
|
1530
1781
|
}
|
|
1782
|
+
/* root 只作为焦点落点(tabIndex=-1):不画焦点环,真正的可聚焦控件在它内部。 */
|
|
1783
|
+
.dms-root:focus { outline: none; }
|
|
1531
1784
|
|
|
1532
1785
|
.dms-trigger {
|
|
1533
1786
|
display: flex;
|
|
@@ -1622,9 +1875,20 @@ window.__ModuleLoader__.load({
|
|
|
1622
1875
|
animation-name: dms-menu-in-below;
|
|
1623
1876
|
}
|
|
1624
1877
|
|
|
1878
|
+
/* 菜单头部的「非列表」栏(loading / 空态 / 错误条 / 失败条 / 截断提示):
|
|
1879
|
+
一律允许收缩(flex-shrink 1)且各自限高,让 .dms-groups 始终保住可滚动
|
|
1880
|
+
的最小可视高度。2026-09-21 修复的 P0:向上弹时 useAnchoredMaxHeight 会把
|
|
1881
|
+
maxHeight 钳到「trigger 底缘 - 12px」(底缘固定、向上生长的语义),trigger
|
|
1882
|
+
靠近视口顶部时该值很小;原先这些栏是 flex: 0 0 auto 完全不收缩,头部
|
|
1883
|
+
40-100px 全部占完后 .dms-groups 的可视高度趋近 0,而 .dms-menu 是
|
|
1884
|
+
overflow:hidden —— 用户看到错误条却看不到也滚不到任何模型行。
|
|
1885
|
+
现在给它们统一的上限(相对菜单高度)+ overflow,最坏情况是栏内自己滚动。 */
|
|
1625
1886
|
.dms-status,
|
|
1626
1887
|
.dms-empty {
|
|
1627
|
-
flex: 0
|
|
1888
|
+
flex: 0 1 auto;
|
|
1889
|
+
min-height: 0;
|
|
1890
|
+
max-height: 40%;
|
|
1891
|
+
overflow-y: auto;
|
|
1628
1892
|
padding: 10px;
|
|
1629
1893
|
color: var(--dsw-alias-label-tertiary);
|
|
1630
1894
|
font-size: 13px;
|
|
@@ -1632,7 +1896,10 @@ window.__ModuleLoader__.load({
|
|
|
1632
1896
|
}
|
|
1633
1897
|
|
|
1634
1898
|
.dms-more {
|
|
1635
|
-
flex: 0
|
|
1899
|
+
flex: 0 1 auto;
|
|
1900
|
+
min-height: 0;
|
|
1901
|
+
max-height: 20%;
|
|
1902
|
+
overflow-y: auto;
|
|
1636
1903
|
padding: 8px 12px;
|
|
1637
1904
|
border-top: 1px solid var(--dsw-alias-border-l1, transparent);
|
|
1638
1905
|
color: var(--dsw-alias-label-tertiary);
|
|
@@ -1643,15 +1910,18 @@ window.__ModuleLoader__.load({
|
|
|
1643
1910
|
/* Provider-load failure strip: capped and scrollable so a long list of failed
|
|
1644
1911
|
groups can never squeeze the model list out of the menu (E4). */
|
|
1645
1912
|
.dms-failures {
|
|
1646
|
-
flex: 0
|
|
1647
|
-
max-height: 96px;
|
|
1913
|
+
flex: 0 1 auto;
|
|
1914
|
+
max-height: min(96px, 30%);
|
|
1648
1915
|
min-height: 0;
|
|
1649
1916
|
overflow-y: auto;
|
|
1650
1917
|
}
|
|
1651
1918
|
|
|
1652
1919
|
.dms-error,
|
|
1653
1920
|
.dms-warning {
|
|
1654
|
-
flex: 0
|
|
1921
|
+
flex: 0 1 auto;
|
|
1922
|
+
min-height: 0;
|
|
1923
|
+
max-height: 40%;
|
|
1924
|
+
overflow-y: auto;
|
|
1655
1925
|
display: flex;
|
|
1656
1926
|
align-items: flex-start;
|
|
1657
1927
|
justify-content: space-between;
|
|
@@ -1726,7 +1996,10 @@ window.__ModuleLoader__.load({
|
|
|
1726
1996
|
/* 图标组件只收 size/className,装饰性 aria-hidden 只能包一层 span;contents 让它不占布局。 */
|
|
1727
1997
|
.dms-icon-slot { display: contents; }
|
|
1728
1998
|
|
|
1729
|
-
|
|
1999
|
+
/* 列表区:菜单里唯一的弹性主体。max-height 被 useAnchoredMaxHeight 钳得很小
|
|
2000
|
+
(trigger 靠近视口顶 + 向上弹)时,32px 保底让「至少见到一行模型」始终成立
|
|
2001
|
+
—— 头部各栏此时按自己的 max-height 百分比上限收缩(见 .dms-status 等)。 */
|
|
2002
|
+
.dms-groups { flex: 1 1 auto; min-height: 32px; overflow-y: auto; contain: content; }
|
|
1730
2003
|
/* 空态占位:空态播报节点在 role=menu 容器外,用与 .dms-groups 同款弹性布局
|
|
1731
2004
|
补回列表区本来的位置(不挤压其余栏,消息也不会贴到菜单顶部)。 */
|
|
1732
2005
|
.dms-groupsFill { flex: 1 1 auto; min-height: 0; overflow-y: auto; contain: content; }
|
|
@@ -1789,6 +2062,8 @@ window.__ModuleLoader__.load({
|
|
|
1789
2062
|
font-size: 12px;
|
|
1790
2063
|
line-height: 18px;
|
|
1791
2064
|
}
|
|
2065
|
+
/* 常驻 live region(内容随状态更新):空时收起,不占位。 */
|
|
2066
|
+
.dms-notice:empty { display: none; }
|
|
1792
2067
|
.dms-effort {
|
|
1793
2068
|
display: flex;
|
|
1794
2069
|
align-items: center;
|
|
@@ -1959,6 +2234,8 @@ window.__ModuleLoader__.load({
|
|
|
1959
2234
|
}
|
|
1960
2235
|
.dms-effort.is-busy { opacity: .72; }
|
|
1961
2236
|
.dms-effort-error { flex: 1 0 100%; margin-top: 8px; padding: 6px 10px; border-radius: 8px; color: var(--dsw-alias-state-error-primary, #c83e4d); background: var(--dsw-alias-state-error-tertiary, rgba(220,55,70,.08)); font-size: 11px; line-height: 1.5; }
|
|
2237
|
+
/* 常驻容器(内容随状态更新):空时收起,不占位。 */
|
|
2238
|
+
.dms-effort-error:empty { display: none; }
|
|
1962
2239
|
/* 视觉隐藏工具类(SR 播报/错误原文:不占布局但可被读屏读到)。 */
|
|
1963
2240
|
.dms-sr {
|
|
1964
2241
|
position: absolute;
|
|
@@ -1999,14 +2276,21 @@ window.__ModuleLoader__.load({
|
|
|
1999
2276
|
}
|
|
2000
2277
|
.dms-model-option-desc { display: block; margin-top: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-tertiary, #9296a0); font-size: 10px; }
|
|
2001
2278
|
.dms-model-check { color: var(--dsw-static-deepseek-500, #4d70ff); font-size: 15px; text-align: center; }
|
|
2002
|
-
/*
|
|
2003
|
-
|
|
2279
|
+
/* 方向键导航把焦点落在选项上。选中态是「背景 + 加粗 + 勾号」,悬停是「背景」,
|
|
2280
|
+
两者与焦点指示必须两两可辨 —— 只用同一个 hover 背景 token 会让「焦点落在
|
|
2281
|
+
选中行上」完全不可见(roving 的默认落点恰恰优先选中行)。 */
|
|
2282
|
+
.dms-model-option:focus-visible {
|
|
2283
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
2284
|
+
box-shadow: inset 0 0 0 2px var(--dsw-alias-border-l3);
|
|
2285
|
+
}
|
|
2004
2286
|
/* 当前选中行:仅靠勾号不足以在密集列表里定位。 */
|
|
2005
2287
|
.dms-model-optionSelected { background: var(--dsw-alias-interactive-bg-hover); }
|
|
2006
2288
|
.dms-model-optionSelected .dms-model-option-name { font-weight: 600; }
|
|
2007
2289
|
/* select 进行中:官方以 disabled 变灰表达,这里用 aria-busy 驱动同样的视觉、
|
|
2008
|
-
不夺键盘焦点(aria-disabled 保连续性,见 ModelOption)。
|
|
2009
|
-
|
|
2290
|
+
不夺键盘焦点(aria-disabled 保连续性,见 ModelOption)。aria-busy 挂在
|
|
2291
|
+
role=menu/listbox 容器上(.dms-groups)—— 挂在外层 .dms-menu 会罩住
|
|
2292
|
+
live region、压制其播报。 */
|
|
2293
|
+
.dms-groups[aria-busy="true"] .dms-model-option { color: var(--dsw-alias-label-dimmed); }
|
|
2010
2294
|
/* 搜索结果里的供应商标是区分跨供应商同名模型的唯一线索,之前无任何样式。 */
|
|
2011
2295
|
.dms-model-option-provider {
|
|
2012
2296
|
display: block;
|
|
@@ -2118,6 +2402,14 @@ body:not([data-ds-dark-theme]) .dms-effort.is-dragging .dms-effort-knob {
|
|
|
2118
2402
|
//#region src/client/index.ts
|
|
2119
2403
|
/** Dictionary namespace owned by this plugin. */
|
|
2120
2404
|
const NS = "modelSelector";
|
|
2405
|
+
/**
|
|
2406
|
+
* 座位优先级。ui-conversation 把 `conversation.input.model` 声明为 kind 'single'
|
|
2407
|
+
* (单槽按 priority 升序取最低者渲染),官方占用者
|
|
2408
|
+
* (@deepseek-ai/dsh-client-ui-model-selection)注册时不传 priority(即默认 0),
|
|
2409
|
+
* 故 -1 恒胜出。**改这个值等于静默换回官方 UI** —— test/plugin-contract.test.mjs
|
|
2410
|
+
* 直接断言本常量(而不是扫源码文本,那样一行注释就能骗过它),注册处也不许再写裸字面量。
|
|
2411
|
+
*/
|
|
2412
|
+
const SEAT_PRIORITY = -1;
|
|
2121
2413
|
/** Required services: the registry, session lookup, locale, the slot seat, and the model directory's Remote faces. */
|
|
2122
2414
|
const inject = [
|
|
2123
2415
|
"slots",
|
|
@@ -2165,20 +2457,18 @@ body:not([data-ds-dark-theme]) .dms-effort.is-dragging .dms-effort-knob {
|
|
|
2165
2457
|
console.warn("[dsh-model-selector] directory.load failed:", error);
|
|
2166
2458
|
});
|
|
2167
2459
|
},
|
|
2168
|
-
select: (selection) => available ? directory.select(selection).
|
|
2169
|
-
console.warn("[dsh-model-selector] select failed:", error);
|
|
2170
|
-
return false;
|
|
2171
|
-
}) : Promise.resolve(false)
|
|
2460
|
+
select: (selection) => available ? directory.select(selection) : Promise.resolve(void 0)
|
|
2172
2461
|
};
|
|
2173
2462
|
}
|
|
2174
2463
|
}, ModelSelect));
|
|
2175
2464
|
});
|
|
2176
2465
|
}
|
|
2177
2466
|
//#endregion
|
|
2467
|
+
exports.SEAT_PRIORITY = SEAT_PRIORITY;
|
|
2178
2468
|
exports.apply = apply;
|
|
2179
2469
|
exports.inject = inject;
|
|
2180
2470
|
return module.exports;
|
|
2181
|
-
}
|
|
2471
|
+
/* __DMS_SRC_HASH__ = "86fa33a484beb4717e3ba62f62d7834bd0be9438ebf41565218cd94a6da91e19" */ }
|
|
2182
2472
|
});
|
|
2183
2473
|
|
|
2184
2474
|
//# sourceMappingURL=client.js.map
|