@bittersmilezzz/dsh-model-selector 0.1.20 → 0.2.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.
@@ -26,8 +26,10 @@ interface EffortSliderProps {
26
26
  state: DirectoryState;
27
27
  select: (selection: ModelSelection) => Promise<boolean>;
28
28
  t: TranslateNS<'modelSelector'>;
29
+ /** 提交失败(超时/拒绝)时回调:让菜单把错误条归到「加载失败」之外(见 renderErrorStrip)。 */
30
+ onSelectFailure: () => void;
29
31
  }
30
- export declare const EffortSlider: react.MemoExoticComponent<({ state, select, t }: EffortSliderProps) => react.JSX.Element | null>;
32
+ export declare const EffortSlider: react.MemoExoticComponent<({ state, select, t, onSelectFailure }: EffortSliderProps) => react.JSX.Element | null>;
31
33
  interface ModelSelectProps {
32
34
  locked: boolean;
33
35
  available: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"ModelSelect.d.ts","sourceRoot":"","sources":["../../../src/client/ModelSelect.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,cAAc,CAAA;AASzD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;AAGzB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mDAAmD,CAAA;AAC5F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAclE,OAAO,KAAK,EAAE,cAAc,EAAsB,MAAM,+CAA+C,CAAA;AAEvG,mEAAmE;AACnE,KAAK,cAAc,GAAG,mBAAmB,CAAA;AAoBzC,UAAU,iBAAiB;IACzB,KAAK,EAAE,cAAc,CAAA;IACrB,MAAM,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACvD,CAAC,EAAE,WAAW,CAAC,eAAe,CAAC,CAAA;CAChC;AA4BD,eAAO,MAAM,YAAY,mDAA0D,iBAAiB,8BAmQlG,CAAC;AAoFH,UAAU,gBAAgB;IACxB,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,OAAO,CAAA;IAClB,SAAS,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAA;IAC7C,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,MAAM,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACvD,CAAC,EAAE,WAAW,CAAC,eAAe,CAAC,CAAA;CAChC;AACD,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,gBAAgB,4BAqnB9F"}
1
+ {"version":3,"file":"ModelSelect.d.ts","sourceRoot":"","sources":["../../../src/client/ModelSelect.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,cAAc,CAAA;AASzD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;AAGzB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mDAAmD,CAAA;AAC5F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAoBlE,OAAO,KAAK,EAAE,cAAc,EAAsB,MAAM,+CAA+C,CAAA;AAEvG,mEAAmE;AACnE,KAAK,cAAc,GAAG,mBAAmB,CAAA;AAEzC,UAAU,iBAAiB;IACzB,KAAK,EAAE,cAAc,CAAA;IACrB,MAAM,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACvD,CAAC,EAAE,WAAW,CAAC,eAAe,CAAC,CAAA;IAC/B,4DAA4D;IAC5D,eAAe,EAAE,MAAM,IAAI,CAAA;CAC5B;AA4BD,eAAO,MAAM,YAAY,oEAA2E,iBAAiB,8BA6RnH,CAAC;AA2FH,UAAU,gBAAgB;IACxB,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,OAAO,CAAA;IAClB,SAAS,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAA;IAC7C,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,MAAM,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACvD,CAAC,EAAE,WAAW,CAAC,eAAe,CAAC,CAAA;CAChC;AACD,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,gBAAgB,4BAisB9F"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * dsh-model-selector — trigger 文案回退链的纯函数(node 可测)。
3
+ *
4
+ * 从 ModelSelect.tsx 原样搬出:四条近乎互相复制的表达式(可见文本 / title /
5
+ * aria)含三层嵌套三目,是本组件里 drift 风险最高的一段,抽出来用假 t 直接测。
6
+ */
7
+ import type { ModelSelection } from '@deepseek-ai/dsh-api-session-controller/types';
8
+ import type { Translate } from '@deepseek-ai/dsh-client-ui-slots';
9
+ /**
10
+ * 本模块用到的字典键。用具体联合而不是 string:调用处传进来的
11
+ * `TranslateNS<'modelSelector'>` 参数逆变后要求这些键必须真的在字典里 ——
12
+ * 字典改名会在编译期报错,而不是运行时渲染出原始 key。
13
+ */
14
+ export type TriggerKey = 'trigger.fallback' | 'trigger.loading' | 'trigger.selectAria' | 'trigger.aria' | 'trigger.ariaEffort';
15
+ /** 文案输入:目录命中的名字优先,命中不到时回退到原始 id。 */
16
+ export interface TriggerCopyInput {
17
+ current: ModelSelection | null;
18
+ /** 当前选中项在目录里的名称;不在目录中时为 undefined。 */
19
+ modelName: string | undefined;
20
+ /** 当前选中项所属供应商名;不在目录中时为 undefined。 */
21
+ providerName: string | undefined;
22
+ /** 当前生效的推理档位名;模型无推理或档位未解析时为 undefined。 */
23
+ effortLabel: string | undefined;
24
+ /** current 为 null 且目录仍在加载。 */
25
+ waiting: boolean;
26
+ }
27
+ /** trigger 的四路文案。 */
28
+ export interface TriggerCopy {
29
+ modelLabel: string;
30
+ providerLabel: string | undefined;
31
+ /** 可见文本(`·` 拼接 effort)。 */
32
+ label: string;
33
+ /** title:供应商 · 可见文本。 */
34
+ title: string;
35
+ /** aria-label:读屏可听到模型 + 档位。 */
36
+ aria: string;
37
+ }
38
+ /**
39
+ * trigger 文案。目录成员资格只是参考(routable 契约):current 匹配不到任何
40
+ * group 不代表没有选择 —— 此时显示 provider/model 原始 id,而不是「选择模型」。
41
+ */
42
+ export declare function dmsTriggerCopy(input: TriggerCopyInput, t: Translate<TriggerKey>): TriggerCopy;
43
+ //# sourceMappingURL=copy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy.d.ts","sourceRoot":"","sources":["../../../src/client/copy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+CAA+C,CAAA;AACnF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAA;AAEjE;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAClB,kBAAkB,GAClB,iBAAiB,GACjB,oBAAoB,GACpB,cAAc,GACd,oBAAoB,CAAA;AAExB,oCAAoC;AACpC,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;IAC9B,sCAAsC;IACtC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,qCAAqC;IACrC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,0CAA0C;IAC1C,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,8BAA8B;IAC9B,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,qBAAqB;AACrB,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAA;CACb;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,EAAE,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,GAAG,WAAW,CAqB7F"}
@@ -25,6 +25,28 @@ export declare const EFFORT_RANK: {
25
25
  };
26
26
  /** The strongest thinking level a model offers, or undefined for none. */
27
27
  export declare function maxEffortOf(reasoning: ModelReasoning): string | undefined;
28
+ /**
29
+ * 已知档位的秩;适配器自造的非规范 id(如 'turbo')返回 undefined —— 与
30
+ * 「已知但秩为 0」(off)区分开。maxEffortOf 对并列的未知档取首个,把它当
31
+ * 「最强档」提交等于随机挑一个档、还可能被宿主拒,故调用方必须先过这一关。
32
+ */
33
+ export declare function dmsEffortRank(id: string): number | undefined;
34
+ /** 选中模型时的自动档位决策。 */
35
+ export interface ChoosePlan {
36
+ /** 随选择提交的档位 id;undefined = 不提交 reasoningEffort(交给宿主用模型默认档)。 */
37
+ effort: string | undefined;
38
+ /** 是否播报「已自动选到最强思考档」:只有确实替用户做了决定(提交档 ≠ 模型默认档)才播。 */
39
+ autoRaised: boolean;
40
+ }
41
+ /**
42
+ * 选择模型时自动落到最强档的决策(从 choose() 搬出并修正):
43
+ * - 非推理模型 / 无档位 → 不提交 effort;
44
+ * - 最强档是 'off'(模型只提供 off)→ 不提交;
45
+ * - 最强档是**非规范 id**(rank 未知)→ 不提交:maxEffortOf 对并列未知档取
46
+ * 首个,把它当「最强」提交会挑错档甚至被宿主拒(用户看到「切换失败」);
47
+ * - 否则提交已知最强档;只有它不等于模型声明的默认档时才播报落点。
48
+ */
49
+ export declare function dmsChoosePlan(reasoning: ModelReasoning | undefined): ChoosePlan;
28
50
  export declare function dmsEffortIndex(levels: readonly EffortLevel[], id: string | undefined): number;
29
51
  /** 索引钳位:档位数为 0 时返回 0,四舍五入后落在 [0, count-1]。 */
30
52
  export declare function dmsClampIndex(value: number, count: number): number;
@@ -50,11 +72,14 @@ export declare function dmsEffortBusy(committing: boolean, status: DirectoryStat
50
72
  * 只有当自回滚以来没有任何新的提交改写 committedRef(仍等于回滚前的档位)、
51
73
  * 提交纪元未推进(epoch === epochAtCommit,即期间没有发起过新提交——提交失败
52
74
  * 回滚后 committedRef 会回到与上一轮回滚相同的值,值比较无法区分「无新提交」
53
- * 与「新提交失败回滚」,纪元才能区分)、且当前无拖动/提交在途时,迟到结果才
54
- * 仍对应当前唯一的意图链——此时 UI 处于「回滚但后端已生效」的错位态,应补
55
- * 一次同步而不是保持回滚。否则以新操作链为准。
75
+ * 与「新提交失败回滚」,纪元才能区分)、当前无拖动/提交在途,**且生效模型仍是
76
+ * 发起提交时的那个**(sameModel)时,迟到结果才仍对应当前唯一的意图链——此时
77
+ * UI 处于「回滚但后端已生效」的错位态,应补一次同步而不是保持回滚。
78
+ * 否则以新操作链为准:模型被外部改写(另一入口切换 / 宿主推送新投影 / 重连后
79
+ * 同步出别的选择)时,next/index 是在旧模型的档位表上算出来的,写回 UI 会显示
80
+ * 一个后端并未生效的档位。
56
81
  */
57
- export declare function dmsShouldAdoptLateSuccess(committed: string, previous: string, dragging: boolean, committing: boolean, epoch: number, epochAtCommit: number): boolean;
82
+ export declare function dmsShouldAdoptLateSuccess(committed: string, previous: string, dragging: boolean, committing: boolean, epoch: number, epochAtCommit: number, sameModel: boolean): boolean;
58
83
  /**
59
84
  * 拖动终止事件是否对应当前活动拖动:pointerActive 为假(拖动已结束/从未开始)
60
85
  * 或事件 pointerId 与活动指针不一致(迟到/重复事件——典型是 pointercancel 终态
@@ -1 +1 @@
1
- {"version":3,"file":"effort.d.ts","sourceRoot":"","sources":["../../../src/client/effort.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mDAAmD,CAAA;AAC5F,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,+CAA+C,CAAA;AAEvG,2DAA2D;AAC3D,MAAM,MAAM,WAAW,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAA;AAC3D,mEAAmE;AACnE,MAAM,MAAM,cAAc,GAAG,mBAAmB,CAAA;AAChD,oEAAoE;AACpE,MAAM,MAAM,UAAU,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;AAE7D,+EAA+E;AAC/E,eAAO,MAAM,WAAW;;;;;;;;CAQvB,CAAA;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,SAAS,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAUzE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE7F;AAED,8CAA8C;AAC9C,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGlE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAGvH;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,UAAU,GAAG,SAAS,CAM7E;AAED,mCAAmC;AACnC,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAOrG;AAED,+BAA+B;AAC/B,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,WAAW,EAAE,CAG7E;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG,OAAO,CAE5F;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAEpK;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAI9H"}
1
+ {"version":3,"file":"effort.d.ts","sourceRoot":"","sources":["../../../src/client/effort.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mDAAmD,CAAA;AAC5F,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,+CAA+C,CAAA;AAEvG,2DAA2D;AAC3D,MAAM,MAAM,WAAW,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAA;AAC3D,mEAAmE;AACnE,MAAM,MAAM,cAAc,GAAG,mBAAmB,CAAA;AAChD,oEAAoE;AACpE,MAAM,MAAM,UAAU,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;AAE7D,+EAA+E;AAC/E,eAAO,MAAM,WAAW;;;;;;;;CAQvB,CAAA;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,SAAS,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAUzE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI5D;AAED,oBAAoB;AACpB,MAAM,WAAW,UAAU;IACzB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,mDAAmD;IACnD,UAAU,EAAE,OAAO,CAAA;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,cAAc,GAAG,SAAS,GAAG,UAAU,CAK/E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE7F;AAED,8CAA8C;AAC9C,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGlE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAGvH;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,UAAU,GAAG,SAAS,CAM7E;AAED,mCAAmC;AACnC,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAOrG;AAED,+BAA+B;AAC/B,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,WAAW,EAAE,CAG7E;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG,OAAO,CAE5F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAExL;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAI9H"}
@@ -9,6 +9,14 @@
9
9
  * (default 0) becomes the rendered winner.
10
10
  */
11
11
  import type { Context as ClientContext } from '@deepseek-ai/cordis';
12
+ /**
13
+ * 座位优先级。ui-conversation 把 `conversation.input.model` 声明为 kind 'single'
14
+ * (单槽按 priority 升序取最低者渲染),官方占用者
15
+ * (@deepseek-ai/dsh-client-ui-model-selection)注册时不传 priority(即默认 0),
16
+ * 故 -1 恒胜出。**改这个值等于静默换回官方 UI** —— test/plugin-contract.test.mjs
17
+ * 直接断言本常量(而不是扫源码文本,那样一行注释就能骗过它),注册处也不许再写裸字面量。
18
+ */
19
+ export declare const SEAT_PRIORITY = -1;
12
20
  /** Required services: the registry, session lookup, locale, the slot seat, and the model directory's Remote faces. */
13
21
  export declare const inject: string[];
14
22
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAoBnE,sHAAsH;AACtH,eAAO,MAAM,MAAM,UAOlB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAsD9C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAmBnE;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,KAAK,CAAA;AAE/B,sHAAsH;AACtH,eAAO,MAAM,MAAM,UAOlB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAsD9C"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * dsh-model-selector — 菜单键盘状态机的纯决策逻辑(node 可测)。
3
+ *
4
+ * 从 ModelSelect.tsx 的 onRootKeyDown 原样搬出(无 JSX、无 DOM、无模块级
5
+ * 副作用):Escape 分层、搜索框内方向键进列表、Home/End 跳首末、Enter 选
6
+ * 首个命中,都是分支密集且易被「顺手改坏」的用户可见逻辑,留在 .tsx 里
7
+ * 只能靠浏览器手测。事件处理器只做 dispatch,分支判定全在这里。
8
+ */
9
+ /** 键盘事件归一化后的输入面。 */
10
+ export interface MenuKeyInput {
11
+ /** event.key(按原样比较,不做大小写归一)。 */
12
+ key: string;
13
+ /**
14
+ * IME 组合输入中。调用方除 isComposing 外还要折算 Safari 的 keyCode 229:
15
+ * WebKit 在 compositionend 之后补发的那次 Enter 带 isComposing === false,
16
+ * 只有 keyCode 229 能识别 —— 否则中文候选上屏会直接选中首个命中、切换模型。
17
+ */
18
+ composing: boolean;
19
+ /** 菜单是否打开。 */
20
+ open: boolean;
21
+ /** 焦点是否在搜索框内。 */
22
+ fromSearch: boolean;
23
+ /** 焦点是否在菜单容器内(搜索框/行/滑杆;trigger 不算)。 */
24
+ inMenu: boolean;
25
+ /** 焦点是否不在任何 input 上(range 滑杆也是 input,方向键归它调档)。 */
26
+ onNonInput: boolean;
27
+ /** 搜索词是否非空。 */
28
+ hasQuery: boolean;
29
+ /** 当前可聚焦的行数(搜索态 = 命中数,分组态 = 组头 + 可见模型行)。 */
30
+ rowCount: number;
31
+ /** 搜索命中总数(Enter 选中首个命中的判据)。 */
32
+ hitCount: number;
33
+ }
34
+ /** 决策结果:调用方据此执行副作用(preventDefault 由调用方按需决定)。 */
35
+ export type MenuKeyAction = {
36
+ type: 'none';
37
+ } | {
38
+ type: 'clearQuery';
39
+ focusSearch: boolean;
40
+ } | {
41
+ type: 'close';
42
+ } | {
43
+ type: 'moveFocus';
44
+ offset: number;
45
+ } | {
46
+ type: 'focusEdge';
47
+ last: boolean;
48
+ } | {
49
+ type: 'chooseFirstHit';
50
+ };
51
+ /**
52
+ * 按键 → 动作。无行可去时对方向键/Home/End 返回 none 而不是吞键:列表为空
53
+ * (加载中/无命中/全折叠)时搜索框里的上下键仍应有移光标语义,preventDefault
54
+ * 由调用方在真正执行动作时才发。
55
+ */
56
+ export declare function dmsMenuKeyAction(input: MenuKeyInput): MenuKeyAction;
57
+ /**
58
+ * roving 环绕下标:焦点不在任何行上(activeIndex < 0)时向下从首行进、向上从
59
+ * 末行进;已在某行上时按 offset 循环。length <= 0 返回 -1(无可去之处)。
60
+ */
61
+ export declare function dmsNextRowIndex(activeIndex: number, offset: number, length: number): number;
62
+ //# sourceMappingURL=keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../src/client/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,oBAAoB;AACpB,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,GAAG,EAAE,MAAM,CAAA;IACX;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAA;IAClB,cAAc;IACd,IAAI,EAAE,OAAO,CAAA;IACb,iBAAiB;IACjB,UAAU,EAAE,OAAO,CAAA;IACnB,uCAAuC;IACvC,MAAM,EAAE,OAAO,CAAA;IACf,kDAAkD;IAClD,UAAU,EAAE,OAAO,CAAA;IACnB,eAAe;IACf,QAAQ,EAAE,OAAO,CAAA;IACjB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAA;IAChB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,gDAAgD;AAChD,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,CAAA;AAE9B;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,CAyBnE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAI3F"}
@@ -26,6 +26,11 @@ export declare function dmsBelowMaxHeight(triggerBottom: number, viewportHeight:
26
26
  * 拖出屏外)时,返回钳到视口内的 left 值,调用方改用 left 锚定。
27
27
  * 返回 undefined 表示保持默认右锚定。
28
28
  * menuWidth 传菜单实际渲染宽度(offsetWidth),与 CSS `min(280px, 100vw-32px)` 解耦。
29
+ *
30
+ * **坐标系**:入参与返回值都是**视口坐标**。菜单是 .dms-root(position:
31
+ * relative)内的 absolute 元素,left 的参考系是 root 的 padding box —— 调用方
32
+ * 必须减去 root 的左缘再写进 style.left,否则窄窗口下菜单整体右移、横向溢出
33
+ * 时整幅画到屏外。
29
34
  */
30
35
  export declare function dmsMenuLeft(rootRight: number, menuWidth: number, viewportWidth: number, margin: number): number | undefined;
31
36
  //# sourceMappingURL=menuFit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"menuFit.d.ts","sourceRoot":"","sources":["../../../src/client/menuFit.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;uCACuC;AACvC,eAAO,MAAM,eAAe,MAAM,CAAC;AACnC;8CAC8C;AAC9C,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAEvG;AAED,2DAA2D;AAC3D,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpG;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAM3H"}
1
+ {"version":3,"file":"menuFit.d.ts","sourceRoot":"","sources":["../../../src/client/menuFit.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;uCACuC;AACvC,eAAO,MAAM,eAAe,MAAM,CAAC;AACnC;8CAC8C;AAC9C,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAEvG;AAED,2DAA2D;AAC3D,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAM3H"}
@@ -6,6 +6,14 @@
6
6
  * 其余行 tabindex=-1、仅方向键可达。本模块只负责一件事:给定当前渲染的
7
7
  * 行集合与选中行,算出 Tab 从搜索框进入列表时应停靠的默认行键。
8
8
  */
9
+ /**
10
+ * 模型行键的**唯一构造点**:菜单 DOM 的 `data-row-key`、roving 决策与行焦点
11
+ * 回调必须同源。此前这三处各自拼模板串,一次局部改名就能让 dmsDefaultRowKey
12
+ * 返回的键匹配不到任何行(Tab 落点静默失效),且测试拿自制副本验证不到。
13
+ */
14
+ export declare function dmsRowKey(providerId: string, modelId: string): string;
15
+ /** 组头行键(分组视图里组头也参与方向键导航)。 */
16
+ export declare function dmsHeaderKey(providerId: string): string;
9
17
  export interface RovingRowSet {
10
18
  /** 搜索命中行键(渲染顺序);null = 非搜索态(分组视图)。 */
11
19
  hitKeys: readonly string[] | null;
@@ -1 +1 @@
1
- {"version":3,"file":"roving.d.ts","sourceRoot":"","sources":["../../../src/client/roving.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,sCAAsC;IACtC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAA;IACjC,+BAA+B;IAC/B,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;IAC5B,iCAAiC;IACjC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IAC7B,0BAA0B;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CASjE"}
1
+ {"version":3,"file":"roving.d.ts","sourceRoot":"","sources":["../../../src/client/roving.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,6BAA6B;AAC7B,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,MAAM,WAAW,YAAY;IAC3B,sCAAsC;IACtC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAA;IACjC,+BAA+B;IAC/B,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;IAC5B,iCAAiC;IACjC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IAC7B,0BAA0B;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CASjE"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * dsh-model-selector — 搜索匹配、高亮区间与列表空态的纯函数(node 可测)。
3
+ *
4
+ * 从 ModelSelect.tsx 原样搬出:归一化、haystack 构造、命中窗口截断、名称高亮
5
+ * 区间与「无模型 / 无命中」的优先级都是用户可见且曾经出过错的分支,留在 .tsx
6
+ * 里只能靠浏览器手测。
7
+ */
8
+ import type { ModelProviderGroup, ModelSelection } from '@deepseek-ai/dsh-api-session-controller/types';
9
+ /** 搜索索引条目:模型 + 折叠后的 haystack + 选中载荷。 */
10
+ export interface SearchChoice {
11
+ group: ModelProviderGroup;
12
+ model: ModelProviderGroup['models'][number];
13
+ /** dmsFold 之后的搜索文本(名称/描述/供应商名/id)。 */
14
+ haystack: string;
15
+ selection: ModelSelection;
16
+ }
17
+ /** 搜索命中的渲染条目。 */
18
+ export interface SearchHit {
19
+ group: ModelProviderGroup;
20
+ model: ModelProviderGroup['models'][number];
21
+ /** 名称内的命中区间(原串坐标);null = 命中落在描述/供应商/id 上,或无法安全定位。 */
22
+ nameHit: {
23
+ start: number;
24
+ end: number;
25
+ } | null;
26
+ }
27
+ /** 搜索结果:items 为窗口内条目(渲染用),total 为命中总数(播报/截断提示用)。 */
28
+ export interface SearchResult {
29
+ items: SearchHit[];
30
+ total: number;
31
+ }
32
+ /**
33
+ * 搜索归一化:NFKD 分解 → 去组合标记 → 小写。
34
+ *
35
+ * 比裸 `toLowerCase()` 宽容:`'İstanbul'` 能被 `'istanbul'` 命中(裸 toLowerCase
36
+ * 把 U+0130 折成 `'i̇'` 两个码位,与 ASCII `'i'` 反而不匹配 → 漏检;`'café'` 同理)。
37
+ * 注意它**可能改变码位长度**,所以不能用它的下标去切原串 —— 高亮区间另走
38
+ * {@link dmsNameHit},只在长度不变时给区间。
39
+ */
40
+ export declare function dmsFold(text: string): string;
41
+ /** 搜索词归一化(去首尾空白后折叠)。空串 = 非搜索态。 */
42
+ export declare function dmsNormalizeQuery(raw: string): string;
43
+ /** 模型条目的搜索文本(haystack)。与 choices 构建处同源,避免两处漂移。 */
44
+ export declare function dmsHaystack(group: ModelProviderGroup, model: ModelProviderGroup['models'][number]): string;
45
+ /**
46
+ * 名称内的命中区间:折叠后长度不变时用下标定位;长度变化(含会改变码位数的
47
+ * 大小写/变音符映射)时返回 null —— 宁可不高亮,也不能用错位的区间去切原串
48
+ * (那会把高亮画到别的字符上,甚至越界)。
49
+ */
50
+ export declare function dmsNameHit(name: string, normalizedQuery: string): {
51
+ start: number;
52
+ end: number;
53
+ } | null;
54
+ /**
55
+ * 搜索:遍历 choices 计数全部命中,只构造窗口内(前 limit 条)的渲染条目 ——
56
+ * 宽泛关键词(单字母)命中数百条时,每次击键省掉数百次对象分配与名称匹配;
57
+ * total 单独累计,播报与「仅显示前 N 条」提示仍然准确。
58
+ */
59
+ export declare function dmsSearchHits(choices: readonly SearchChoice[], normalizedQuery: string, limit: number): SearchResult | null;
60
+ /** 列表区空态:'noModels' = 目录里没有模型,'noHits' = 搜索无命中,null = 正常渲染列表。 */
61
+ export type MenuEmptyState = 'noModels' | 'noHits' | null;
62
+ /**
63
+ * 空态判定:**无模型优先于无命中** —— 目录为空而用户又输了关键词时,真相是
64
+ * 「没有可用的模型」(该引导用户去配置/加载目录),不是「没有匹配关键词的模型」
65
+ * (那会引导用户去改关键词,而无论改什么都不会有结果)。
66
+ */
67
+ export declare function dmsMenuEmptyState(input: {
68
+ status: string;
69
+ choiceCount: number;
70
+ searching: boolean;
71
+ hitCount: number;
72
+ }): MenuEmptyState;
73
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/client/search.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,+CAA+C,CAAA;AAEvG,wCAAwC;AACxC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,kBAAkB,CAAA;IACzB,KAAK,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;IAC3C,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,cAAc,CAAA;CAC1B;AAED,iBAAiB;AACjB,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,kBAAkB,CAAA;IACzB,KAAK,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;IAC3C,qDAAqD;IACrD,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CAC/C;AAED,oDAAoD;AACpD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,kCAAkC;AAClC,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,kDAAkD;AAClD,wBAAgB,WAAW,CAAC,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,MAAM,CAE1G;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAMvG;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,MAAM,GACZ,YAAY,GAAG,IAAI,CAWrB;AAED,iEAAiE;AACjE,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,QAAQ,GAAG,IAAI,CAAA;AAEzD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB,GAAG,cAAc,CAIjB"}
@@ -5,6 +5,10 @@
5
5
  * apply and removed again on unload. Class names are prefixed `dms-` so they
6
6
  * cannot collide with CSS-module hashes from other plugins. Colors come only
7
7
  * from `--dsw-*` theme tokens, matching the shipped Menu material.
8
+ *
9
+ * 显式 `: string` 注解:不加注解时 tsc 会把整份 CSS 推断成字面量类型并写进
10
+ * lib/types/client/styles.d.ts(19KB 的 d.ts 只为声明一个常量),发布包里
11
+ * 白白多出十几 KB 且每次改样式都全量重写。
8
12
  */
9
- export declare const CSS = "\n.dms-root {\n position: relative;\n min-width: 0;\n}\n\n.dms-trigger {\n display: flex;\n align-items: center;\n gap: 4px;\n min-width: 0;\n max-width: min(420px, calc(100vw - 48px));\n height: 28px;\n padding: 0 4px 0 8px;\n border: none;\n border-radius: 24px;\n outline: none;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 13px;\n line-height: 20px;\n font-weight: 500;\n cursor: pointer;\n}\n.dms-trigger:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }\n.dms-trigger:focus-visible { box-shadow: 0 0 0 2px var(--dsw-alias-border-l3); }\n.dms-trigger:disabled { color: var(--dsw-alias-label-dimmed); cursor: default; }\n\n.dms-triggerLabel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n line-height: 20px;\n}\n.dms-triggerEffort {\n flex: 0 0 auto;\n line-height: 20px;\n color: var(--dsw-alias-label-caption);\n}\n.dms-triggerProvider {\n flex: 0 0 auto;\n max-width: 88px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--dsw-alias-label-caption);\n font-size: 11px;\n line-height: 20px;\n font-weight: 400;\n}\n\n.dms-chevron,\n.dms-groupChevron {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 auto;\n line-height: 0;\n}\n.dms-chevron {\n color: var(--dsw-alias-label-caption);\n transition: transform 120ms ease;\n}\n.dms-chevronOpen { transform: rotate(180deg); }\n.dms-groupChevron {\n color: var(--dsw-alias-label-tertiary);\n transition: transform 120ms ease;\n}\n.dms-groupChevronClosed { transform: rotate(-90deg); }\n.dms-model-check svg { display: block; }\n\n.dms-menu {\n position: absolute;\n right: 0;\n bottom: calc(100% + 8px);\n z-index: 20;\n display: flex;\n flex-direction: column;\n width: min(280px, calc(100vw - 32px));\n overflow: hidden;\n padding: 4px 0 4px 4px;\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-inverted);\n border-radius: 12px;\n background: var(--dsw-specific-menu);\n box-shadow: var(--dsw-shadow-lv3);\n color: var(--dsw-alias-label-primary);\n animation: dms-menu-in 150ms cubic-bezier(.22,1,.36,1);\n --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);\n --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);\n}\n.dms-menuModel { border-right: none; }\n.dms-menuBelow {\n bottom: auto;\n top: calc(100% + 8px);\n animation-name: dms-menu-in-below;\n}\n\n.dms-status,\n.dms-empty {\n flex: 0 0 auto;\n padding: 10px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.dms-more {\n flex: 0 0 auto;\n padding: 8px 12px;\n border-top: 1px solid var(--dsw-alias-border-l1, transparent);\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n/* Provider-load failure strip: capped and scrollable so a long list of failed\n groups can never squeeze the model list out of the menu (E4). */\n.dms-failures {\n flex: 0 0 auto;\n max-height: 96px;\n min-height: 0;\n overflow-y: auto;\n}\n\n.dms-error,\n.dms-warning {\n flex: 0 0 auto;\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 8px;\n margin-bottom: 4px;\n padding: 7px 8px;\n border-radius: 8px;\n background: var(--dsw-alias-interactive-bg-hover-danger);\n color: var(--dsw-alias-state-error-primary);\n font-size: 12px;\n line-height: 18px;\n}\n.dms-warning {\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-state-warn-label);\n}\n.dms-retry {\n flex: 0 0 auto;\n padding: 0;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: inherit;\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n}\n.dms-retry:focus-visible { box-shadow: 0 0 0 2px var(--dsw-alias-border-l3); }\n\n/* Search box pinned above the scrollable list. */\n.dms-search {\n position: relative;\n margin-bottom: 4px;\n margin-right: 4px;\n flex: 0 0 auto;\n}\n.dms-searchInput {\n box-sizing: border-box;\n width: 100%;\n height: 30px;\n padding: 0 30px 0 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n outline: none;\n background: var(--dsw-alias-bg-input);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n line-height: 18px;\n}\n.dms-searchInput::placeholder { color: var(--dsw-alias-label-tertiary); }\n.dms-searchInput:focus-visible { box-shadow: 0 0 0 2px var(--dsw-alias-border-l3); }\n.dms-searchClear {\n position: absolute;\n top: 0;\n right: 0;\n display: grid;\n place-items: center;\n width: 30px;\n height: 30px;\n padding: 0;\n border: none;\n background: transparent;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n}\n.dms-searchClear:hover { color: var(--dsw-alias-label-primary); }\n/* \u952E\u76D8\u7126\u70B9\u6B64\u524D\u5728\u8FD9\u4E09\u4E2A\u53EF\u805A\u7126\u63A7\u4EF6\u4E0A\u5B8C\u5168\u9690\u8EAB\uFF08\u4E0A\u8F6E\u53EA\u8865\u4E86\u6A21\u578B\u884C\uFF09\u3002 */\n.dms-searchClear:focus-visible {\n border-radius: 8px;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n/* \u56FE\u6807\u7EC4\u4EF6\u53EA\u6536 size/className\uFF0C\u88C5\u9970\u6027 aria-hidden \u53EA\u80FD\u5305\u4E00\u5C42 span\uFF1Bcontents \u8BA9\u5B83\u4E0D\u5360\u5E03\u5C40\u3002 */\n.dms-icon-slot { display: contents; }\n\n.dms-groups { flex: 1 1 auto; min-height: 0; overflow-y: auto; contain: content; }\n/* \u7A7A\u6001\u5360\u4F4D\uFF1A\u7A7A\u6001\u64AD\u62A5\u8282\u70B9\u5728 role=menu \u5BB9\u5668\u5916\uFF0C\u7528\u4E0E .dms-groups \u540C\u6B3E\u5F39\u6027\u5E03\u5C40\n \u8865\u56DE\u5217\u8868\u533A\u672C\u6765\u7684\u4F4D\u7F6E\uFF08\u4E0D\u6324\u538B\u5176\u4F59\u680F\uFF0C\u6D88\u606F\u4E5F\u4E0D\u4F1A\u8D34\u5230\u83DC\u5355\u9876\u90E8\uFF09\u3002 */\n.dms-groupsFill { flex: 1 1 auto; min-height: 0; overflow-y: auto; contain: content; }\n.dms-group + .dms-group { margin-top: 4px; }\n\n/* Collapsible provider header: a full-width toggle button. */\n.dms-groupHeader {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n gap: 4px;\n width: 100%;\n padding: 5px 8px 3px;\n border: none;\n outline: none;\n background: var(--dsw-specific-menu);\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n text-align: left;\n cursor: pointer;\n}\n.dms-groupHeader:hover { color: var(--dsw-alias-label-primary); }\n.dms-groupHeader:focus-visible { box-shadow: inset 0 0 0 2px var(--dsw-alias-border-l3); }\n.dms-groupName {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dms-groupCount { flex: 0 0 auto; color: var(--dsw-alias-label-dimmed); font-weight: 400; }\n\n.dms-nameRow {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n.dms-badge {\n flex: 0 0 auto;\n padding: 0 6px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n color: var(--dsw-alias-label-caption);\n font-size: 10px;\n line-height: 16px;\n font-weight: 500;\n}\n.dms-notice {\n flex: 0 0 auto;\n margin: 4px 0 0;\n padding: 6px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n line-height: 18px;\n}\n.dms-effort {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n width: 100%;\n min-width: 0;\n min-height: 32px;\n color: var(--dsw-alias-label-secondary);\n user-select: none;\n box-sizing: border-box;\n}\n/* \u6863\u4F4D\u8BFB\u6570\uFF1A\u62D6\u52A8\u65F6\u5B9E\u65F6\u8DDF\u968F preview\uFF0C\u66FF\u4EE3\u6B64\u524D\u53EA\u6709\u8BFB\u5C4F\u80FD\u770B\u5230\u7684 aria-valuetext\u3002 */\n.dms-effort-value {\n flex: 0 0 auto;\n margin-left: 8px;\n min-width: 3.2em;\n color: var(--dsw-alias-label-primary);\n font-size: 11px;\n line-height: 16px;\n text-align: right;\n}\n/* \u5B98\u65B9\u5951\u7EA6\u7684 efforts[].description\uFF0C\u6574\u884C\u5C55\u793A\u5728\u6ED1\u6746\u4E0B\u65B9\u3002 */\n.dms-effort-desc {\n flex: 1 0 100%;\n margin-top: 2px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n line-height: 14px;\n}\n.dms-effort-slider {\n --dms-progress: 50%;\n position: relative;\n width: 100%;\n height: 30px;\n flex: 1 1 0;\n min-width: 0;\n border-radius: 999px;\n isolation: isolate;\n transition: filter 180ms ease;\n}\n.dms-effort-track {\n position: absolute;\n inset: 0;\n overflow: hidden;\n border-radius: inherit;\n background: linear-gradient(100deg, #03040a 0%, #071126 22%, #101d4c 45%, #302262 70%, #5d35a0 100%);\n box-shadow:\n inset 0 1px 0 rgba(189, 199, 255, .15),\n inset 0 -1px 0 rgba(0, 0, 0, .55),\n 0 3px 10px rgba(12, 17, 55, .34);\n}\n.dms-effort-track::after {\n content: \"\";\n position: absolute;\n inset: 0;\n background:\n radial-gradient(circle at 18% 45%, rgba(82, 130, 255, .12), transparent 24%),\n linear-gradient(90deg, rgba(0, 0, 0, .28), transparent 42%, rgba(168, 113, 255, .12));\n pointer-events: none;\n}\n.dms-effort-fx {\n position: absolute;\n z-index: 1;\n inset: 0;\n overflow: hidden;\n border-radius: inherit;\n pointer-events: none;\n}\n.dms-effort-canvas {\n position: absolute;\n z-index: 2;\n inset: 0;\n width: 100%;\n height: 100%;\n opacity: 1;\n image-rendering: pixelated;\n mix-blend-mode: screen;\n transition: filter 140ms ease;\n}\n.dms-effort-flare {\n position: absolute;\n z-index: 3;\n transition: left 70ms linear, filter 140ms ease;\n top: 50%;\n left: var(--dms-progress);\n width: 78px;\n height: 46px;\n border-radius: 50%;\n background: radial-gradient(ellipse at 100% 50%, rgba(255,255,255,.96) 0 4%, rgba(188,189,255,.8) 11%, rgba(106,87,255,.5) 28%, rgba(105,31,255,.2) 49%, transparent 74%);\n filter: blur(2px) saturate(1.25);\n mix-blend-mode: screen;\n transform: translate(-100%, -50%);\n pointer-events: none;\n}\n.dms-effort-flare::before,\n.dms-effort-flare::after {\n content: \"\";\n position: absolute;\n inset: 50% auto auto 100%;\n border-radius: 999px;\n transform: translate(-50%, -50%);\n}\n.dms-effort-flare::before {\n width: 52px;\n height: 1px;\n background: linear-gradient(90deg, transparent, rgba(100,160,255,.42), #f1ecff, rgba(193,82,255,.65), transparent);\n box-shadow: 0 0 7px #9b7cff, 0 0 13px rgba(72,132,255,.64);\n}\n.dms-effort-flare::after {\n width: 1px;\n height: 20px;\n background: linear-gradient(180deg, transparent, rgba(196,190,255,.84), transparent);\n box-shadow: 0 0 7px #9c7cff;\n}\n.dms-effort-knob {\n position: absolute;\n z-index: 4;\n top: 50%;\n left: clamp(14px, var(--dms-progress), calc(100% - 14px));\n width: 28px;\n height: 28px;\n border: 1px solid rgba(255,255,255,.94);\n border-radius: 50%;\n background: #fff;\n box-shadow:\n 0 0 0 2px rgba(92,105,255,.12),\n 0 0 14px rgba(121,82,255,.48),\n 0 2px 7px rgba(0,0,0,.3);\n transform: translate(-50%, -50%);\n transition: left 240ms cubic-bezier(.22,1,.36,1), transform 160ms ease, box-shadow 180ms ease;\n pointer-events: none;\n}\n.dms-effort-input {\n position: absolute;\n z-index: 5;\n inset: -5px 0;\n width: 100%;\n height: calc(100% + 10px);\n margin: 0;\n opacity: 0;\n cursor: grab;\n touch-action: none;\n}\n.dms-effort-input:active { cursor: grabbing; }\n.dms-effort-input:focus-visible + .dms-effort-knob {\n outline: 2px solid var(--dsw-static-blue-400);\n outline-offset: 2px;\n}\n.dms-effort.is-dragging .dms-effort-canvas {\n filter: saturate(1.45) brightness(1.28) contrast(1.06);\n}\n.dms-effort.is-dragging .dms-effort-flare {\n filter: blur(1.5px) saturate(1.6) brightness(1.42);\n transition: none;\n}\n.dms-effort.is-dragging .dms-effort-knob {\n transform: translate(-50%, -50%) scale(1.07);\n transition: none;\n box-shadow:\n 0 0 0 3px rgba(113,115,255,.25),\n 0 0 20px rgba(74,145,255,.86),\n 0 0 31px rgba(171,53,255,.66),\n 0 3px 8px rgba(0,0,0,.32);\n}\n.dms-effort.is-error .dms-effort-slider {\n outline: 1px solid var(--dsw-alias-state-error-secondary);\n outline-offset: 2px;\n}\n.dms-effort.is-busy { opacity: .72; }\n.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; }\n/* \u89C6\u89C9\u9690\u85CF\u5DE5\u5177\u7C7B\uFF08SR \u64AD\u62A5/\u9519\u8BEF\u539F\u6587\uFF1A\u4E0D\u5360\u5E03\u5C40\u4F46\u53EF\u88AB\u8BFB\u5C4F\u8BFB\u5230\uFF09\u3002 */\n.dms-sr {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n.dms-model-option {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 20px;\n align-items: center;\n gap: 8px;\n width: 100%;\n min-height: 38px;\n padding: 7px 9px;\n border: 0;\n border-radius: 9px;\n color: inherit;\n background: transparent;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n.dms-model-option:hover { background: var(--dsw-alias-fill-tertiary, rgba(120,125,140,.09)); }\n.dms-model-option-copy { min-width: 0; }\n.dms-model-option-name { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }\n/* \u641C\u7D22\u547D\u4E2D\u7247\u6BB5\uFF1A\u540D\u79F0\u5185\u6807\u4E0B\u5212\u7EBF\uFF08\u547D\u4E2D\u63CF\u8FF0/\u4F9B\u5E94\u5546\u65F6\u4E0D\u6807\uFF0C\u89C1 nameHit\uFF09\u3002 */\n.dms-hit {\n text-decoration: underline;\n text-decoration-color: var(--dsw-static-blue-400, #4d70ff);\n text-decoration-thickness: 2px;\n text-underline-offset: 3px;\n}\n.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; }\n.dms-model-check { color: var(--dsw-static-deepseek-500, #4d70ff); font-size: 15px; text-align: center; }\n/* \u65B9\u5411\u952E\u5BFC\u822A\u628A\u7126\u70B9\u843D\u5728\u9009\u9879\u4E0A\uFF0C\u6B64\u524D\u6CA1\u6709\u4EFB\u4F55\u7126\u70B9\u6307\u793A\u3002 */\n.dms-model-option:focus-visible { background: var(--dsw-alias-interactive-bg-hover); }\n/* \u5F53\u524D\u9009\u4E2D\u884C\uFF1A\u4EC5\u9760\u52FE\u53F7\u4E0D\u8DB3\u4EE5\u5728\u5BC6\u96C6\u5217\u8868\u91CC\u5B9A\u4F4D\u3002 */\n.dms-model-optionSelected { background: var(--dsw-alias-interactive-bg-hover); }\n.dms-model-optionSelected .dms-model-option-name { font-weight: 600; }\n/* select \u8FDB\u884C\u4E2D\uFF1A\u5B98\u65B9\u4EE5 disabled \u53D8\u7070\u8868\u8FBE\uFF0C\u8FD9\u91CC\u7528 aria-busy \u9A71\u52A8\u540C\u6837\u7684\u89C6\u89C9\u3001\n \u4E0D\u593A\u952E\u76D8\u7126\u70B9\uFF08aria-disabled \u4FDD\u8FDE\u7EED\u6027\uFF0C\u89C1 ModelOption\uFF09\u3002 */\n.dms-menu[aria-busy=\"true\"] .dms-model-option { color: var(--dsw-alias-label-dimmed); }\n/* \u641C\u7D22\u7ED3\u679C\u91CC\u7684\u4F9B\u5E94\u5546\u6807\u662F\u533A\u5206\u8DE8\u4F9B\u5E94\u5546\u540C\u540D\u6A21\u578B\u7684\u552F\u4E00\u7EBF\u7D22\uFF0C\u4E4B\u524D\u65E0\u4EFB\u4F55\u6837\u5F0F\u3002 */\n.dms-model-option-provider {\n display: block;\n margin-top: 2px;\n overflow: hidden;\n color: var(--dsw-alias-label-dimmed, #9296a0);\n font-size: 10px;\n line-height: 14px;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n@keyframes dms-menu-in {\n from { opacity: 0; transform: translateY(5px) scale(.98); }\n to { opacity: 1; transform: translateY(0) scale(1); }\n}\n@keyframes dms-menu-in-below {\n from { opacity: 0; transform: translateY(-5px) scale(.98); }\n to { opacity: 1; transform: translateY(0) scale(1); }\n}\nbody:not([data-ds-dark-theme]) .dms-effort-slider {\n filter: none;\n}\nbody:not([data-ds-dark-theme]) .dms-effort-track {\n background: var(--dsw-static-blue-75, #e5f0ff);\n box-shadow:\n inset 0 1px 0 rgba(255,255,255,.9),\n inset 0 0 0 1px rgba(80,133,194,.14),\n 0 3px 10px rgba(48,101,165,.13);\n}\nbody:not([data-ds-dark-theme]) .dms-effort-track::before {\n content: \"\";\n position: absolute;\n z-index: 0;\n inset: 0 auto 0 0;\n width: var(--dms-progress);\n border-radius: inherit;\n background: linear-gradient(90deg, #fff 0%, #e2f0ff 20%, #a8d0fb 57%, #438fdf 100%);\n transition: width 240ms cubic-bezier(.22,1,.36,1);\n}\nbody:not([data-ds-dark-theme]) .dms-effort.is-dragging .dms-effort-track::before {\n transition: none;\n}\nbody:not([data-ds-dark-theme]) .dms-effort-track::after {\n z-index: 1;\n background: linear-gradient(90deg, rgba(255,255,255,.48), transparent 34%, rgba(23,101,201,.07));\n}\nbody:not([data-ds-dark-theme]) .dms-effort-canvas {\n opacity: .78;\n mix-blend-mode: multiply;\n}\nbody:not([data-ds-dark-theme]) .dms-effort-flare {\n background: radial-gradient(ellipse at 100% 50%, rgba(255,255,255,.98) 0 5%, rgba(204,231,255,.88) 13%, rgba(91,162,241,.48) 31%, rgba(37,111,207,.16) 53%, transparent 75%);\n filter: blur(2px) saturate(1.12);\n}\nbody:not([data-ds-dark-theme]) .dms-effort-flare::before {\n background: linear-gradient(90deg, transparent, rgba(116,177,244,.34), #fff, rgba(66,139,225,.58), transparent);\n box-shadow: 0 0 7px rgba(58,133,222,.5), 0 0 13px rgba(104,176,255,.38);\n}\nbody:not([data-ds-dark-theme]) .dms-effort-flare::after {\n background: linear-gradient(180deg, transparent, rgba(255,255,255,.94), transparent);\n box-shadow: 0 0 7px rgba(64,137,224,.44);\n}\nbody:not([data-ds-dark-theme]) .dms-effort-knob {\n border-color: rgba(126,160,197,.32);\n box-shadow:\n 0 0 0 2px rgba(58,124,207,.09),\n 0 0 13px rgba(48,118,207,.3),\n 0 3px 8px rgba(39,77,119,.18);\n}\nbody:not([data-ds-dark-theme]) .dms-effort.is-dragging .dms-effort-knob {\n box-shadow:\n 0 0 0 3px rgba(36,105,192,.15),\n 0 0 20px rgba(25,100,201,.45),\n 0 3px 8px rgba(39,77,119,.18);\n}\n@media (prefers-reduced-motion: reduce) {\n .dms-chevron,\n .dms-groupChevron,\n .dms-effort-slider,\n .dms-effort-canvas,\n .dms-effort-knob,\n .dms-effort-flare,\n body:not([data-ds-dark-theme]) .dms-effort-track::before { transition: none; }\n .dms-menu { animation: none; }\n}\n\n/* Inline effort footer pinned below the scrollable model list (single-pane\n menu: switching models and adjusting effort both live in one surface). */\n.dms-effortFooter {\n flex: 0 0 auto;\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 4px 4px 0 0;\n padding: 6px 10px;\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\n.dms-effortFooterLabel {\n flex: 0 0 auto;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n line-height: 18px;\n white-space: nowrap;\n}\n.dms-effortFooter .dms-effort { flex: 1 1 auto; min-width: 0; }\n\n";
13
+ export declare const CSS: string;
10
14
  //# sourceMappingURL=styles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/client/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;EAOE;AACF,eAAO,MAAM,GAAG,24kBA8kBf,CAAC"}
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/client/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;EAWE;AACF,eAAO,MAAM,GAAG,EAAE,MA2lBjB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bittersmilezzz/dsh-model-selector",
3
- "version": "0.1.20",
3
+ "version": "0.2.0",
4
4
  "description": "DeepSeek Harness 的增强模型选择器:单层菜单(搜索 + 分组)+ 底部内联推理强度(Effort)滑杆。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -18,7 +18,11 @@
18
18
  "./package.json": "./package.json"
19
19
  },
20
20
  "files": [
21
- "lib",
21
+ "lib/client.js",
22
+ "lib/client.js.map",
23
+ "lib/index.js",
24
+ "lib/index.js.map",
25
+ "lib/types",
22
26
  "cordis.patch.yml",
23
27
  "README.md",
24
28
  "LICENSE",
@@ -29,7 +33,8 @@
29
33
  "bundle": "tsdown",
30
34
  "watch": "tsdown --watch",
31
35
  "typecheck": "tsc -p tsconfig.host.json --noEmit && tsc -p tsconfig.client.json --noEmit",
32
- "test": "node --test test/*.test.mjs"
36
+ "test": "node --test test/*.test.mjs",
37
+ "validate:registry": "node --test test/*.test.mjs"
33
38
  },
34
39
  "license": "MIT",
35
40
  "keywords": [