@bittersmilezzz/dsh-model-selector 0.1.19 → 0.1.21

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 CHANGED
@@ -56,6 +56,8 @@ dsh plugin --profile <profile> add <path-to-repo>
56
56
 
57
57
  - 通过 slot 优先级(priority: -1)**shadow 官方 seat**:若官方后续改动该 seat 的注入面(`ModelSelectInjected`),需同步适配;这是官方认可的 slot 叠加机制,并非禁用官方 entry。
58
58
  - 与其它也 shadow `conversation.input.model` 的插件同时安装时,优先级决定渲染赢家,可能互相覆盖(本插件优先级 -1 最低,默认胜出)。
59
+ - 遮蔽的**依赖侧**已由 `test/official-seat-shadow.test.mjs` 钉住:官方当前注册该座位时**不声明** `priority`(即默认 0),遮蔽才成立;官方一旦开始声明(尤其 `<= -1`)或把该座位从 single 改成 chain,测试先变红,提示重新评估策略。
60
+ - 若把这个插件提交到第三方商城(如 DSH-Store),其「不动官方组件」条款是最需要解释的一点:本插件走的是官方 slot 的 shadowing 语义(`priority: -1`),**没有**禁用/替换官方 entry,也没有改动任何 `@deepseek-ai/*` 组件本体。
59
61
 
60
62
  ## 开发
61
63
 
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["IconChevronDownOutline14","IconCheckOutline14","IconCloseFill14","useAnchoredMaxHeight","Toast","IconWarningOutline16","zhDict","enDict"],"sources":["../src/client/locales.ts","../src/client/effort.ts","../src/client/effortCanvas.ts","../src/client/effortDrag.ts","../src/client/menuFit.ts","../src/client/roving.ts","../src/client/ModelSelect.tsx","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/**\n * `modelSelector` namespace dictionaries for the enhanced model seat.\n *\n * Simplified Chinese is the key-set source of truth; the English dictionary is\n * checked complete against it. Product copy is Chinese-first per repo style.\n */\nexport const zh = {\n\t\"trigger.fallback\": \"选择模型\",\n\t\"trigger.loading\": \"正在加载模型…\",\n\t\"trigger.selectAria\": \"选择模型\",\n\t\"trigger.aria\": \"选择模型,当前 {model}\",\n\t\"trigger.ariaEffort\": \"选择模型,当前 {model},推理等级 {effort}\",\n\t\"menu.aria\": \"模型列表\",\n\t\"menu.effort\": \"推理等级\",\n\t\"search.placeholder\": \"搜索模型\",\n\t\"search.clearAria\": \"清除搜索\",\n\t\"search.noMatch\": \"没有匹配“{query}”的模型。\",\n\t\"search.status\": \"{count} 个结果\",\n\t\"search.more\": \"仅显示前 {shown} 条(共 {total} 条命中),请细化关键词。\",\n\t\"group.toggleAria\": \"{name}({count} 个模型)\",\n\t\"effort.providerDefault\": \"Default\",\n\t\"status.loading\": \"正在刷新模型列表…\",\n\t\"error.action\": \"模型操作失败:{message}\",\n\t\"action.reload\": \"重新加载\",\n\t\"warning.groupLoad\": \"{name} 加载失败:{message}\",\n\t\"empty.models\": \"没有可用的模型。\",\n\t\"empty.efforts\": \"当前模型未提供推理等级。\",\n\t\"effort.failed\": \"档位切换未生效(后端未接受),已恢复原档位\",\n\t\"effort.timeout\": \"档位切换超时,已恢复原档位\",\n\t\"notice.already\": \"已是当前模型,无需切换\",\n\t\"notice.selectFailed\": \"切换失败,请重试\",\n\t\"toast.effortAuto\": \"已自动选到最强思考档:{effort}\",\n\t\"badge.reasoning\": \"推理\",\n\t\"badge.reasoningHint\": \"支持思考等级,切换后自动选到最大思考强度\"\n};\n\n\nexport const en = {\n\t\"trigger.fallback\": \"Select model\",\n\t\"trigger.loading\": \"Loading models…\",\n\t\"trigger.selectAria\": \"Select model\",\n\t\"trigger.aria\": \"Select model, current {model}\",\n\t\"trigger.ariaEffort\": \"Select model, current {model}, reasoning effort {effort}\",\n\t\"menu.aria\": \"Model list\",\n\t\"menu.effort\": \"Effort\",\n\t\"search.placeholder\": \"Search models\",\n\t\"search.clearAria\": \"Clear search\",\n\t\"search.noMatch\": \"No models match “{query}”.\",\n\t\"search.status\": \"{count} results\",\n\t\"search.more\": \"Showing first {shown} of {total} matches; refine your query.\",\n\t\"group.toggleAria\": \"{name} ({count} models)\",\n\t\"effort.providerDefault\": \"Default\",\n\t\"status.loading\": \"Refreshing model list…\",\n\t\"error.action\": \"Model operation failed: {message}\",\n\t\"action.reload\": \"Reload\",\n\t\"warning.groupLoad\": \"{name} failed to load: {message}\",\n\t\"empty.models\": \"No models available.\",\n\t\"empty.efforts\": \"This model provides no reasoning effort levels.\",\n\t\"effort.failed\": \"Failed to switch effort (backend rejected); restored\",\n\t\"effort.timeout\": \"Timed out switching effort; restored\",\n\t\"notice.already\": \"Already the current model\",\n\t\"notice.selectFailed\": \"Switch failed, please try again\",\n\t\"toast.effortAuto\": \"Auto-selected the strongest reasoning level: {effort}\",\n\t\"badge.reasoning\": \"Reasoning\",\n\t\"badge.reasoningHint\": \"Supports reasoning levels; switches land on the strongest\"\n};\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** The enhanced model seat copy. */\n modelSelector: keyof typeof zh\n }\n}\n","/**\n * dsh-model-selector — 推理强度档位的纯函数。\n *\n * 从 ModelSelect.tsx 原样搬出(无 JSX、无 DOM、无模块级副作用),以便用\n * node --test 直接跑源码:滑块钳位、档位映射与「取模型最强档」都是易错且\n * 用户可见的逻辑,留在 .tsx 里则无法脱离 React 测试。\n */\nimport type { ModelDirectoryState } from '@deepseek-ai/dsh-client-ui-model-selection/client'\nimport type { ModelProviderGroup, ModelReasoning } from '@deepseek-ai/dsh-api-session-controller/types'\n\n/** One model effort level as advertised by the adapter. */\nexport type EffortLevel = ModelReasoning['efforts'][number]\n/** Per-session model directory snapshot (official state shape). */\nexport type DirectoryState = ModelDirectoryState\n/** The selected model entry, or undefined when nothing resolves. */\nexport type ModelEntry = ModelProviderGroup['models'][number]\n\n/** Canonical strength order of pi-ai thinking levels (strongest = highest). */\nexport const EFFORT_RANK = {\n off: 0,\n minimal: 1,\n low: 2,\n medium: 3,\n high: 4,\n xhigh: 5,\n max: 6,\n}\n\n/** The strongest thinking level a model offers, or undefined for none. */\nexport function maxEffortOf(reasoning: ModelReasoning): string | undefined {\n let best\n for (const effort of reasoning.efforts) {\n const rank = EFFORT_RANK[effort.id as keyof typeof EFFORT_RANK] ?? 0\n if (best === void 0 || rank > best.rank) best = {\n id: effort.id,\n rank,\n }\n }\n return best?.id\n}\n\nexport function dmsEffortIndex(levels: readonly EffortLevel[], id: string | undefined): number {\n return levels.findIndex((level) => level.id === id)\n}\n\n/** 索引钳位:档位数为 0 时返回 0,四舍五入后落在 [0, count-1]。 */\nexport function dmsClampIndex(value: number, count: number): number {\n if (count <= 0) return 0\n return Math.max(0, Math.min(count - 1, Math.round(value)))\n}\n\n/**\n * 指针水平位置 → 档位原始值(未四舍五入,拖动中途可落在两档之间;落点由\n * 提交方 clamp)。输入条退化(宽度 ≤ 0 或档位数 < 2)时保持当前值不动。\n */\nexport function dmsPointerRaw(clientX: number, left: number, width: number, levelCount: number, current: number): number {\n if (width <= 0 || levelCount < 2) return current\n return Math.max(0, Math.min(levelCount - 1, (clientX - left) / width * (levelCount - 1)))\n}\n\nexport function dmsCurrentModel(state: DirectoryState): ModelEntry | undefined {\n if (state.current === null) return void 0\n const current = state.current\n const group = state.groups.find((g) => g.id === current.provider)\n const model = group?.models.find((m) => m.id === current.model)\n return model ?? void 0\n}\n\n/** 当前生效档位:用户已选优先,其次模型默认,最后取中间档。 */\nexport function dmsEffectiveEffortIndex(levels: readonly EffortLevel[], state: DirectoryState): number {\n const reasoning = dmsCurrentModel(state)?.reasoning\n const current = dmsEffortIndex(levels, state.current?.reasoningEffort)\n if (current >= 0) return current\n const fallback = dmsEffortIndex(levels, reasoning?.defaultEffort)\n if (fallback >= 0) return fallback\n return Math.floor((levels.length - 1) / 2)\n}\n\n/** 滑块档位:少于两档(无法构成滑杆)时返回空数组。 */\nexport function dmsSliderLevels(state: DirectoryState): readonly EffortLevel[] {\n const efforts = dmsCurrentModel(state)?.reasoning?.efforts\n return efforts !== void 0 && efforts.length >= 2 ? efforts : []\n}\n\n/**\n * 滑杆是否处于忙态:自身提交中,或共享目录上有任一 select 在途\n * (目录 select 是 last-writer-wins,模型切换在途时再发 effort RPC 会与\n * 它交错——effort 打到旧模型上,与模型切换互相覆盖;choose() 按同一状态\n * 拒绝了模型点击,滑杆必须同规则拒绝交互)。\n */\nexport function dmsEffortBusy(committing: boolean, status: DirectoryState['status']): boolean {\n return committing || status === 'selecting'\n}\n\n/**\n * 超时回滚后 select 迟到成功是否应采纳并同步 UI:\n * 只有当自回滚以来没有任何新的提交改写 committedRef(仍等于回滚前的档位)、\n * 提交纪元未推进(epoch === epochAtCommit,即期间没有发起过新提交——提交失败\n * 回滚后 committedRef 会回到与上一轮回滚相同的值,值比较无法区分「无新提交」\n * 与「新提交失败回滚」,纪元才能区分)、且当前无拖动/提交在途时,迟到结果才\n * 仍对应当前唯一的意图链——此时 UI 处于「回滚但后端已生效」的错位态,应补\n * 一次同步而不是保持回滚。否则以新操作链为准。\n */\nexport function dmsShouldAdoptLateSuccess(committed: string, previous: string, dragging: boolean, committing: boolean, epoch: number, epochAtCommit: number): boolean {\n return committed === previous && epoch === epochAtCommit && !dragging && !committing\n}\n\n/**\n * 拖动终止事件是否对应当前活动拖动:pointerActive 为假(拖动已结束/从未开始)\n * 或事件 pointerId 与活动指针不一致(迟到/重复事件——典型是 pointercancel 终态\n * 后平台补发的 pointerup)时返回 false,调用方应幂等跳过终止动作:不清状态、\n * 不触发提交/回滚回调。pointerId 缺省(blur 兜底提交,无事件对象)时只要求\n * 拖动仍在进行中。\n */\nexport function dmsIsActiveDrag(pointerActive: boolean, activePointerId: number | null, pointerId: number | undefined): boolean {\n if (!pointerActive) return false\n if (pointerId !== void 0 && activePointerId !== pointerId) return false\n return true\n}\n","/**\n * dsh-model-selector — EffortSlider 的 canvas 辐射特效模块。\n *\n * 从 ModelSelect.tsx 原样搬出(原 EffortSlider 组件内的 330 行状态机里,\n * 绘制与动画循环约占一半且与 React 状态解耦):\n * - dmsDrawRadiation:纯绘制函数,state(progress/dragging)与主题都由参数\n * 传入,不读组件闭包——同一帧可被 pointer 事件、rAF 循环、主题变化各路径复用。\n * - useEffortCanvas:canvas 生命周期(尺寸/DPR、ResizeObserver、主题\n * MutationObserver、visibilitychange、prefers-reduced-motion)与相位缓动\n * 动画循环。外部通过 radiationRef 更新 target/dragging 驱动重绘,hook\n * 返回的 redraw 注册到组件侧 ref(preview 变化时组件直接调它触发一帧)。\n */\nimport * as react from 'react'\n\n/** 绘制状态的最小契约:进度(0..1)+ 是否拖拽(拖拽时加速/改粒子参数)。 */\nexport interface RadiationState {\n progress: number\n dragging: boolean\n /** 目标进度;undefined 表示尚未初始化(首帧同步起跳,不做缓动)。 */\n target?: number\n}\n\n/** 拖动时辐射推挤的缓动系数(越小越跟手),非拖拽时回弹系数。 */\nconst DRAG_EASE = 0.55\nconst IDLE_EASE = 0.12\nconst SETTLE_EPSILON = 0.002\n\n/**\n * 纯绘制:把滑块进度画成拖尾辐射(列能量 + 径向光晕 + 粒子流 + 热核光斑)。\n * 全部可变量(尺寸、相位、进度、拖拽态、深浅色)都来自参数——无模块级状态。\n */\nexport function dmsDrawRadiation(context: CanvasRenderingContext2D, width: number, height: number, time: number, state: RadiationState, isDark: boolean): void {\n const origin = state.progress * width;\n const cell = 4;\n const speed = state.dragging ? 2.8 : 1;\n context.clearRect(0, 0, width, height);\n if (origin <= 0) return;\n context.save();\n context.beginPath();\n context.rect(0, 0, origin, height);\n context.clip();\n for (let x = 0; x < origin; x += cell) {\n const delta = x + cell * 0.5 - origin;\n const distance = Math.abs(delta);\n const phaseA = distance / 10 - time * 74e-4 * speed;\n const phaseB = distance / 23 - time * 41e-4 * speed + 1.7;\n const phaseC = distance / 40 - time * 22e-4 * speed + 3.4;\n const sinA = Math.max(0, Math.sin(phaseA));\n const sinB = Math.max(0, Math.sin(phaseB));\n const sinC = Math.max(0, Math.sin(phaseC));\n const waveA = Math.pow(sinA, 2.6);\n const waveB = Math.pow(sinB, 3.2);\n const waveC = Math.pow(sinC, 4);\n const crest = Math.pow(sinA, 15) + Math.pow(sinB, 18) * 0.78;\n const wave = Math.min(1, waveA * 0.76 + waveB * 0.58 + waveC * 0.32);\n const trail = 0.38 + 0.62 * Math.exp(-distance / 90);\n const pillar = Math.pow(Math.max(0, Math.sin(x / 20 + time * 16e-4)), 3) * 0.27;\n const columnEnergy = trail * (wave * 1.04 + pillar + crest * 0.32);\n if (columnEnergy > 0.012) {\n const nearness = Math.max(0, 1 - distance / 140);\n const red = isDark ? Math.round(42 + 124 * nearness + 75 * wave) : Math.round(28 + 58 * nearness + 15 * wave);\n const green = isDark ? Math.round(56 + 58 * nearness + 44 * crest) : Math.round(88 + 72 * nearness + 30 * crest);\n const blue = isDark ? Math.round(175 + 72 * nearness + 8 * wave) : Math.round(182 + 62 * nearness);\n const alpha = isDark ? Math.min(0.88, columnEnergy * 0.72) : Math.min(0.62, columnEnergy * 0.54);\n context.fillStyle = `rgba(${red}, ${green}, ${blue}, ${alpha})`;\n context.fillRect(x, 0, cell - 1, height);\n }\n for (let y = 0; y < height; y += cell) {\n const deltaY = y + cell * 0.5 - height * 0.5;\n const radial = Math.hypot(delta / 38, deltaY / 11);\n const halo = Math.exp(-radial * 0.96) * 1.08;\n const verticalShape = 0.58 + 0.42 * Math.cos(deltaY / height * Math.PI);\n const grain = 0.72 + 0.28 * Math.sin(x * 0.73 + y * 1.31 + time * 6e-3);\n const alpha = Math.min(0.96, (columnEnergy * 0.88 + halo + crest * 0.19) * verticalShape * grain);\n if (alpha < 0.035) continue;\n const hot = Math.max(0, 1 - radial / 2.4);\n const red = isDark ? Math.round(54 + 148 * hot + 42 * wave + 35 * crest) : Math.round(25 + 72 * hot + 12 * wave);\n const green = isDark ? Math.round(68 + 78 * hot + 46 * crest) : Math.round(98 + 72 * hot + 24 * crest);\n const blue = isDark ? Math.round(186 + 64 * hot) : Math.round(194 + 56 * hot);\n context.fillStyle = `rgba(${red}, ${green}, ${blue}, ${isDark ? alpha : alpha * 0.72})`;\n context.fillRect(x, y, cell - 1, cell - 1);\n }\n }\n for (let i = 0; i < 14; i += 1) {\n const travel = (time * (state.dragging ? 0.16 : 0.065) * (0.78 + i % 5 * 0.09) + i * 23) % Math.max(30, origin + 64);\n const particleX = origin - travel;\n if (particleX < -24 || particleX > width + 16) continue;\n const particleY = 3 + (i * 13 + Math.sin(time * 3e-3 + i) * 5) % Math.max(7, height - 6);\n const length = 4 + i % 4 * 4 + (state.dragging ? 6 : 0);\n const alpha = 0.28 + i % 5 * 0.1;\n const streak = context.createLinearGradient(particleX, 0, particleX + length, 0);\n streak.addColorStop(0, isDark ? \"rgba(72,118,255,0)\" : \"rgba(24,94,184,0)\");\n streak.addColorStop(0.68, isDark ? `rgba(112,135,255,${alpha})` : `rgba(36,108,202,${alpha * 0.72})`);\n streak.addColorStop(1, isDark ? `rgba(236,222,255,${Math.min(1, alpha + 0.26)})` : `rgba(103,175,248,${Math.min(0.82, alpha + 0.18)})`);\n context.fillStyle = streak;\n context.fillRect(particleX, particleY, length, i % 3 === 0 ? 2 : 1);\n }\n const glow = context.createRadialGradient(origin, height / 2, 0, origin, height / 2, 24);\n glow.addColorStop(0, isDark ? \"rgba(255,255,255,.82)\" : \"rgba(255,255,255,.86)\");\n glow.addColorStop(0.14, isDark ? \"rgba(183,190,255,.54)\" : \"rgba(162,210,255,.48)\");\n glow.addColorStop(0.44, isDark ? \"rgba(103,74,255,.28)\" : \"rgba(37,112,207,.22)\");\n glow.addColorStop(1, isDark ? \"rgba(86,31,210,0)\" : \"rgba(25,91,181,0)\");\n context.fillStyle = glow;\n context.fillRect(origin - 26, 0, 52, height);\n context.restore();\n}\n\n/**\n * 辐射画布生命周期 hook:尺寸/DPR 自适应、主题变化监听、动画循环与相位时钟。\n *\n * 外部契约:\n * - radiationRef.current.target / .dragging 由组件在 preview/忙态变化时更新,\n * hook 的动画循环按 target 做指数缓动(拖动中每帧逼近 target,回弹时渐近);\n * - 返回的 redraw 应注册到组件侧 ref,供事件驱动的重绘(拖动一帧、主题翻转)\n * 即时调用;挂载时画首帧并把 progress 直接对齐 target(打开菜单不闪中间态)。\n *\n * 动画成本:拖拽时只画事件驱动帧(pointer 移动触发的单帧),循环空闲时\n * 一帧即停——画布不常驻 rAF,CPU 占用只在缓动回弹窗口内存在。\n */\nexport function useEffortCanvas(canvasRef: react.RefObject<HTMLCanvasElement | null>, radiationRef: react.MutableRefObject<RadiationState>): () => void {\n const redrawRef = react.useRef<(() => void) | null>(null);\n react.useEffect(() => {\n const canvas = canvasRef.current;\n if (canvas === null) return;\n const context = canvas.getContext(\"2d\");\n if (context === null) return;\n const reducedMotion = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n let width = 1;\n let height = 1;\n let frame = 0;\n // Phase clock advances only while the loop runs, so a frozen (idle)\n // effect resumes at the exact wave phase it paused at — no jump.\n let phase = 0;\n let lastFrameAt = 0;\n const resize = () => {\n const bounds = canvas.getBoundingClientRect();\n const ratio = Math.min(window.devicePixelRatio || 1, 2);\n width = Math.max(1, bounds.width);\n height = Math.max(1, bounds.height);\n canvas.width = Math.max(1, Math.round(width * ratio));\n canvas.height = Math.max(1, Math.round(height * ratio));\n context.setTransform(ratio, 0, 0, ratio, 0, 0);\n };\n // Draw one frame at the current phase; returns true while the effect\n // should keep animating (still easing toward its target).\n // Idle = one static frame, zero further CPU — the loop only runs while\n // the glow is settling, instead of forever.\n // While dragging, stop after a single frame: every pointer move bumps\n // `preview` and redraw() draws one event-driven frame, so the glow\n // follows the knob without a back-to-back repaint stream. Continuously\n // repainting this mix-blend canvas is what flickered the whole menu.\n const draw = (): boolean => {\n const r = radiationRef.current;\n if (r.target !== void 0) {\n const k = r.dragging ? DRAG_EASE : IDLE_EASE;\n r.progress += (r.target - r.progress) * k;\n if (Math.abs(r.target - r.progress) < SETTLE_EPSILON) r.progress = r.target;\n }\n // 主题在这里(每次 draw 时)读取一次:dmsDrawRadiation 保持参数输入、\n // 无 DOM 依赖可测;主题翻转由 themeObserver → draw() 即时取到新值。\n const isDark = document.body.hasAttribute(\"data-ds-dark-theme\");\n dmsDrawRadiation(context, width, height, phase, r, isDark);\n if (r.dragging) return false;\n return r.target !== void 0 && Math.abs(r.target - r.progress) > SETTLE_EPSILON;\n };\n const loop = (time: number): void => {\n if (lastFrameAt !== 0) phase += time - lastFrameAt;\n lastFrameAt = time;\n if (draw() && !document.hidden) {\n frame = window.requestAnimationFrame(loop);\n } else {\n frame = 0;\n lastFrameAt = 0;\n }\n };\n const redraw = () => {\n if (reducedMotion.matches) {\n draw();\n return;\n }\n // Restart the loop on demand (pointer/theme/preview changes);\n // a running loop already draws every frame, so no extra draw here.\n if (frame === 0 && !document.hidden) {\n lastFrameAt = 0;\n frame = window.requestAnimationFrame(loop);\n }\n };\n const resizeObserver = new ResizeObserver(() => {\n resize();\n draw();\n });\n const themeObserver = new MutationObserver(() => draw());\n const onVisibility = () => {\n if (!document.hidden) redraw();\n };\n // 跨屏(窗口拖到另一台显示器)时 devicePixelRatio 变化:canvas 的 CSS\n // 尺寸不变,ResizeObserver 不会触发,位图分辨率停留在旧 DPR → 模糊。\n // 按当前 DPR 构造 resolution 媒体查询,DPR 一变旧查询即失配 → change。\n const resolutionQuery = window.matchMedia(`(resolution: ${window.devicePixelRatio || 1}dppx)`);\n const onResolutionChange = () => {\n resize();\n draw();\n };\n if (resolutionQuery.addEventListener !== void 0) {\n resolutionQuery.addEventListener(\"change\", onResolutionChange);\n } else {\n resolutionQuery.addListener(onResolutionChange); // 旧版 WebKit\n }\n resizeObserver.observe(canvas);\n themeObserver.observe(document.body, { attributes: true, attributeFilter: [\"data-ds-dark-theme\"] });\n document.addEventListener(\"visibilitychange\", onVisibility);\n redrawRef.current = redraw;\n resize();\n // 首帧直接把进度对齐 target(若已初始化):菜单打开瞬间不展示中间进度。\n const r = radiationRef.current;\n if (r.target !== void 0) r.progress = r.target;\n draw();\n if (!reducedMotion.matches && !document.hidden) frame = window.requestAnimationFrame(loop);\n return () => {\n window.cancelAnimationFrame(frame);\n resizeObserver.disconnect();\n themeObserver.disconnect();\n if (resolutionQuery.removeEventListener !== void 0) {\n resolutionQuery.removeEventListener(\"change\", onResolutionChange);\n } else {\n resolutionQuery.removeListener(onResolutionChange);\n }\n document.removeEventListener(\"visibilitychange\", onVisibility);\n redrawRef.current = null;\n };\n }, [canvasRef, radiationRef]);\n return react.useCallback(() => redrawRef.current?.(), []);\n}\n","/**\n * dsh-model-selector — EffortSlider 的指针拖动状态机。\n *\n * 从 ModelSelect.tsx 原样搬出(原 EffortSlider 的 8 个 ref 指针生命周期):\n * 拖动只认发起时记录的活动 pointerId,window 捕获阶段的 move/up/cancel 兜底\n * 指针拖出输入条/菜单外的场景;取消(pointercancel / 触控失控)走回滚回调,\n * 不提交。几何换算(clientX → 档位原始值)与忙态判定都经由回调外接,\n * hook 本身不读组件状态,拖动中途组件重渲染(preview setState)不会中断拖动。\n */\nimport * as react from 'react'\nimport { dmsIsActiveDrag, dmsPointerRaw } from './effort.ts'\n\nexport interface EffortDragHandlers {\n onPointerDown: (event: react.PointerEvent<HTMLInputElement>) => void\n onPointerMove: (event: react.PointerEvent<HTMLInputElement>) => void\n onPointerUp: (event: react.PointerEvent<HTMLInputElement>) => void\n onPointerCancel: (event: react.PointerEvent<HTMLInputElement>) => void\n onBlur: () => void\n}\n\nexport interface UseEffortDragOptions {\n /** 当前档位数(levels.length),拖动态期间变化时以最新值为准。 */\n levelCount: () => number\n /** 忙态(提交中/目录 select 在途)时拒绝新开拖动。 */\n canStart: () => boolean\n /** 拖动中预览(组件侧乐观 setState);返回值供组件更新其当前 raw ref。 */\n onPreview: (raw: number) => void\n /** 拖动结束(pointerup / blur 兜底)时提交;拖出后归位也会提交同一档。 */\n onCommit: (raw: number) => void\n /** 取消(pointercancel / 触控失控)时回滚到已提交档,不提交。 */\n onRollback: () => void\n inputRef: react.RefObject<HTMLInputElement | null>\n /** 拖动活状态(true = 指针按下未结束)。组件与 hook 共享同一 ref:\n * 组件侧用它守卫「拖动态不被打断」(commit 迟到采纳判定、目录同步 effect)。 */\n draggingRef: react.MutableRefObject<boolean>\n}\n\n/**\n * 拖动中由 pointerId 校验的几何换算:raw = (clientX - left) / width 映射到档位区间。\n * 输入条退化(宽度 ≤ 0 / 档位数 < 2)时返回 fallback(当前值,不跳档)。\n */\nfunction rawFromPointer(input: HTMLInputElement, clientX: number, levelCount: number, fallback: number): number {\n const bounds = input.getBoundingClientRect();\n return dmsPointerRaw(clientX, bounds.left, bounds.width, levelCount, fallback);\n}\n\nexport function useEffortDrag({ levelCount, canStart, onPreview, onCommit, onRollback, inputRef, draggingRef }: UseEffortDragOptions) {\n const [dragging, setDragging] = react.useState(false);\n const pointerActiveRef = react.useRef(false);\n const activePointerIdRef = react.useRef<number | null>(null);\n // 拖动中最近一次预览的 raw:blur 兜底提交(无 clientX)时用它。\n const lastRawRef = react.useRef(0);\n // 回调经 ref 转发,hook 内部的事件处理器始终持有最新闭包(组件重渲染不重建监听)。\n const callbacksRef = react.useRef({ levelCount, canStart, onPreview, onCommit, onRollback });\n callbacksRef.current = { levelCount, canStart, onPreview, onCommit, onRollback };\n\n const showPointerPreview = react.useCallback((input: HTMLInputElement, clientX: number): void => {\n const raw = rawFromPointer(input, clientX, callbacksRef.current.levelCount(), lastRawRef.current);\n lastRawRef.current = raw;\n callbacksRef.current.onPreview(raw);\n }, []);\n\n const beginDragging = react.useCallback((input: HTMLInputElement, pointerId: number, clientX: number): void => {\n pointerActiveRef.current = true;\n activePointerIdRef.current = pointerId;\n draggingRef.current = true;\n setDragging(true);\n showPointerPreview(input, clientX);\n try {\n if (!input.hasPointerCapture(pointerId)) input.setPointerCapture(pointerId);\n } catch (cause) {\n // 预期内失败:setPointerCapture 在元素已卸载 / 指针已失效等场景会抛\n // NotFoundError/InvalidStateError。影响仅限「指针拖出输入条后由元素继续\n // 接收事件」这条优化路径——window 捕获阶段的 move/up 兜底监听仍然生效,\n // 拖动不会因此中断,无需恢复;留 debug 痕迹便于排查真实异常。\n console.debug(\"[dms-effort] setPointerCapture failed\", cause);\n }\n }, [showPointerPreview]);\n\n const moveDragging = react.useCallback((input: HTMLInputElement, pointerId: number, clientX: number): void => {\n if (!pointerActiveRef.current || activePointerIdRef.current !== pointerId) return;\n showPointerPreview(input, clientX);\n }, [showPointerPreview]);\n\n const stopDragging = react.useCallback((input: HTMLInputElement, pointerId?: number, clientX?: number): void => {\n if (!dmsIsActiveDrag(pointerActiveRef.current, activePointerIdRef.current, pointerId)) return;\n const raw = clientX === void 0 ? lastRawRef.current : rawFromPointer(input, clientX, callbacksRef.current.levelCount(), lastRawRef.current);\n lastRawRef.current = raw;\n pointerActiveRef.current = false;\n activePointerIdRef.current = null;\n draggingRef.current = false;\n setDragging(false);\n if (pointerId !== void 0 && input.hasPointerCapture(pointerId)) {\n input.releasePointerCapture(pointerId);\n }\n callbacksRef.current.onCommit(raw);\n }, []);\n\n // 取消路径的公共清理:pointercancel 是终态事件(规范上不再有该指针的后续事件),\n // 但部分平台/浏览器在 cancel 后仍会补发 pointerup——不清干净会把「取消」误当成\n // 正常提交。此函数经 dmsIsActiveDrag 校验活动指针、清空全部拖动状态、回滚不提交,\n // 幂等(第二次调用因指针已清空而直接返回)。blur 兜底共用 stopDragging 的同一\n // 校验,双触发路径不会产生重复 commit。\n const cancelDragging = (pointerId: number): void => {\n if (!dmsIsActiveDrag(pointerActiveRef.current, activePointerIdRef.current, pointerId)) return;\n pointerActiveRef.current = false;\n activePointerIdRef.current = null;\n draggingRef.current = false;\n setDragging(false);\n callbacksRef.current.onRollback();\n };\n\n // 全局兜底监听:拖动中指针移出输入条/菜单仍留在窗口内时,move/up 由 window 捕获。\n // 只在拖动进行中挂载(pointerdown 命中 → dragging=true),up/cancel/blur 结束\n // 拖动(dragging=false)即卸载——菜单开着但不拖动时窗口上不挂任何监听。pointercancel\n // 是终态,平台随后补发的 pointerup 要么因监听已卸载而收不到、要么走 stopDragging\n // 的 dmsIsActiveDrag 守卫幂等跳过,与常驻挂载时语义一致。挂载缝隙(pointerdown\n // 到 effect 落地之间)内指针仍在输入条上,由输入条自身的 JSX 事件兜住,无事件丢失。\n const globalMoveRef = react.useRef<(event: PointerEvent) => void>(() => {});\n const globalEndRef = react.useRef<(event: PointerEvent) => void>(() => {});\n const globalCancelRef = react.useRef<(event: PointerEvent) => void>(() => {});\n globalMoveRef.current = (event) => {\n const input = inputRef.current;\n if (input !== null) moveDragging(input, event.pointerId, event.clientX);\n };\n globalEndRef.current = (event) => {\n const input = inputRef.current;\n if (input !== null) stopDragging(input, event.pointerId, event.clientX);\n };\n globalCancelRef.current = (event: PointerEvent): void => cancelDragging(event.pointerId);\n react.useEffect(() => {\n if (!dragging) return;\n const move = (event: PointerEvent) => globalMoveRef.current(event);\n const end = (event: PointerEvent) => globalEndRef.current(event);\n const cancel = (event: PointerEvent) => globalCancelRef.current(event);\n window.addEventListener(\"pointermove\", move, true);\n window.addEventListener(\"pointerup\", end, true);\n window.addEventListener(\"pointercancel\", cancel, true);\n return () => {\n window.removeEventListener(\"pointermove\", move, true);\n window.removeEventListener(\"pointerup\", end, true);\n window.removeEventListener(\"pointercancel\", cancel, true);\n };\n }, [dragging]);\n\n const handlers: EffortDragHandlers = {\n onPointerDown: (event: react.PointerEvent<HTMLInputElement>) => {\n // 忙态(模型切换/提交在途)不开新拖拽:目录 select 是\n // last-writer-wins,交错的 effort RPC 会打到旧模型上。\n if (!callbacksRef.current.canStart()) return;\n event.preventDefault();\n event.currentTarget.focus();\n beginDragging(event.currentTarget, event.pointerId, event.clientX);\n },\n onPointerMove: (event: react.PointerEvent<HTMLInputElement>) => moveDragging(event.currentTarget, event.pointerId, event.clientX),\n onPointerUp: (event: react.PointerEvent<HTMLInputElement>) => stopDragging(event.currentTarget, event.pointerId, event.clientX),\n onPointerCancel: (event: react.PointerEvent<HTMLInputElement>) => {\n if (event.currentTarget.hasPointerCapture(event.pointerId)) {\n event.currentTarget.releasePointerCapture(event.pointerId);\n }\n cancelDragging(event.pointerId);\n },\n onBlur: () => {\n const input = inputRef.current;\n if (input !== null) stopDragging(input);\n },\n };\n\n return {\n dragging,\n draggingRef,\n handlers,\n };\n}\n","/**\n * dsh-model-selector — 菜单弹出方向与高度钳位的纯函数。\n *\n * 从 ModelSelect.tsx 原样搬出(无 JSX、无 DOM、无模块级副作用),以便用\n * node --test 直接跑源码:方向决策(向上 vs 向下弹)和向下弹的高度钳位都是\n * 用户可见且曾在矮窗口/滚动场景出过 bug 的逻辑,留在 .tsx 里则无法脱离\n * 浏览器测试。\n */\n\n/** 菜单设计最大高度(px);实际由 useAnchoredMaxHeight 按视口可用空间钳位。\n * 调法:调大允许更高菜单(矮视口下仍会被钳到视口内),调小菜单更矮。 */\nexport const MENU_MAX_HEIGHT = 420;\n/** 与视口边缘保留的距离;对齐 primitives 里未导出的 MARGIN。\n * 改动同时影响向上/向下弹与水平钳位的边距,需与官方 MARGIN 意图保持一致。 */\nexport const MENU_VIEWPORT_MARGIN = 12;\n\n/**\n * 向上弹当且仅当 trigger 上方可用空间不小于下方可用空间:\n * 取上下两侧中空间更大的一侧,避免矮窗口把面板挤到视口下沿外面。\n */\nexport function dmsMenuAbove(triggerTop: number, triggerBottom: number, viewportHeight: number): boolean {\n return triggerTop >= viewportHeight - triggerBottom;\n}\n\n/** 向下弹时按 trigger 下方空间钳位菜单高度(top-anchored,不能复用向上弹的 fit)。 */\nexport function dmsBelowMaxHeight(triggerBottom: number, viewportHeight: number, cap: number): number {\n return Math.max(0, Math.min(cap, viewportHeight - triggerBottom - MENU_VIEWPORT_MARGIN));\n}\n\n/**\n * 菜单水平钳位:默认右锚定(right:0,与 seat 右缘对齐);seat 右缘左侧放不下\n * 整幅菜单(seat 靠输入区左下 + 窄窗口)、或 seat 右缘本身已越出视口右缘\n * (页面横向溢出/缩放时 rect.right 可大于 innerWidth,右锚定会把整幅菜单\n * 拖出屏外)时,返回钳到视口内的 left 值,调用方改用 left 锚定。\n * 返回 undefined 表示保持默认右锚定。\n * menuWidth 传菜单实际渲染宽度(offsetWidth),与 CSS `min(280px, 100vw-32px)` 解耦。\n */\nexport function dmsMenuLeft(rootRight: number, menuWidth: number, viewportWidth: number, margin: number): number | undefined {\n if (rootRight - menuWidth >= margin && rootRight <= viewportWidth - margin) return void 0;\n return Math.min(\n Math.max(margin, rootRight - menuWidth),\n Math.max(margin, viewportWidth - margin - menuWidth),\n );\n}\n","/**\n * dsh-model-selector — 菜单 roving tabindex 的纯决策逻辑(node 可测)。\n *\n * 模型列表可达数百行,若每行都是 Tab 停靠点,Tab 键会陷入行海;roving\n * tabindex(WAI-ARIA menu 模式的通行做法)只让「活动行」留在 Tab 序里,\n * 其余行 tabindex=-1、仅方向键可达。本模块只负责一件事:给定当前渲染的\n * 行集合与选中行,算出 Tab 从搜索框进入列表时应停靠的默认行键。\n */\nexport interface RovingRowSet {\n /** 搜索命中行键(渲染顺序);null = 非搜索态(分组视图)。 */\n hitKeys: readonly string[] | null\n /** 分组视图中可见(未折叠)的模型行键(渲染顺序)。 */\n modelKeys: readonly string[]\n /** 分组视图中全部组头键(组头始终渲染,即使组被折叠)。 */\n headerKeys: readonly string[]\n /** 当前选中行键;null = 尚无选中。 */\n selectedKey: string | null\n}\n\n/**\n * Tab 默认落点:搜索态取「选中行若在命中集内,否则首个命中」;分组态取\n * 「选中行若可见,否则首个可见模型行」;全部折叠(无可见模型行)时回退到\n * 首个组头。返回 null 表示当前没有任何可聚焦行(空态由菜单外的 role=status\n * 播报,列表容器不渲染)。\n */\nexport function dmsDefaultRowKey(set: RovingRowSet): string | null {\n if (set.hitKeys !== null) {\n if (set.hitKeys.length === 0) return null\n return set.selectedKey !== null && set.hitKeys.includes(set.selectedKey) ? set.selectedKey : set.hitKeys[0]!\n }\n if (set.modelKeys.length > 0) {\n return set.selectedKey !== null && set.modelKeys.includes(set.selectedKey) ? set.selectedKey : set.modelKeys[0]!\n }\n return set.headerKeys[0] ?? null\n}\n","/**\n * ModelSelect: the enhanced composer model seat (`conversation.input.model`).\n *\n * A single-pane menu (simpler than the shipped two-level root menu): opening\n * the seat lands directly on the model list — search box + collapsible\n * provider groups — with the current model's effort slider pinned inline at\n * the bottom of the menu, so switching models and tuning reasoning effort\n * both happen in one surface without an intermediate page.\n *\n * Data and submission ride the SAME per-session directory the /model popup\n * shares (via `modelDirectories`), so a switch made here is what the popup\n * shows next and vice versa. Icons, Toast and the menu fit/dismiss hooks are\n * reused from @deepseek-ai/dsh-client-ui-primitives at runtime; colors\n * come from `--dsw-*` tokens in the injected stylesheet.\n */\nimport * as react from 'react'\nimport { zh as zhDict, en as enDict } from './locales.ts'\n// Runtime reuse of the official primitives (already an external in\n// tsdown.config.ts and a platform module in the web loader): Toast is the\n// sanctioned surface for a rejected selection, and the two hooks fit/dismiss\n// the open menu. The list itself stays hand-rolled — primitives' Menu renders\n// every entry (footer included) as a <button role=\"menuitem\">, which cannot\n// host the effort range input or the search field.\nimport { Toast, IconWarningOutline16, IconChevronDownOutline14, IconCheckOutline14, IconCloseFill14, useAnchoredMaxHeight, useDismissOnOutsidePointer } from '@deepseek-ai/dsh-client-ui-primitives'\n\nexport { zhDict, enDict }\n// Type-only: official model-selection directory types (the enhanced seat's\n// data contract — same shared per-session directory the /model popup reads).\nimport type { ModelDirectoryState } from '@deepseek-ai/dsh-client-ui-model-selection/client'\nimport type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { SnapshotStore } from '@deepseek-ai/dsh-client-store'\n// Effort helpers live in effort.ts (pure, no JSX/DOM) so node --test can cover them.\nimport { dmsClampIndex, dmsEffortIndex, dmsEffectiveEffortIndex, dmsEffortBusy, dmsShouldAdoptLateSuccess, dmsSliderLevels, maxEffortOf } from './effort.ts'\n// 辐射画布(绘制纯函数 + 动画循环 hook)与指针拖动状态机:从 EffortSlider\n// 组件内拆分独立成模块,组件本体只保留档位/提交/渲染三件事。\nimport { useEffortCanvas } from './effortCanvas.ts'\nimport { useEffortDrag } from './effortDrag.ts'\n// Menu direction/clamp helpers likewise (pure — the direction flip and the\n// below-clamp are user-visible and were previously only browser-testable).\nimport { MENU_MAX_HEIGHT, MENU_VIEWPORT_MARGIN, dmsMenuAbove, dmsBelowMaxHeight, dmsMenuLeft } from './menuFit.ts'\n// roving tabindex 的默认落点决策(纯函数,node 可测——行键集合与选中行的\n// 组合分支不值得只在浏览器里验证)。\nimport { dmsDefaultRowKey } from './roving.ts'\n// Type-only: the model catalog carrier types (moved here in dsh alpha.2).\nimport type { ModelSelection, ModelProviderGroup } from '@deepseek-ai/dsh-api-session-controller/types'\n\n/** Per-session model directory snapshot (official state shape). */\ntype DirectoryState = ModelDirectoryState\n/** The enhanced seat's injected business face. */\n/** 搜索索引条目:模型 + 其搜索 haystack + 选中载荷。 */\ntype ModelChoice = {\n\tgroup: ModelProviderGroup\n\tmodel: ModelProviderGroup[\"models\"][number]\n\thaystack: string\n\tselection: ModelSelection\n}\n\n/** 搜索命中的渲染条目:只装渲染窗口内的命中(窗口外只计数,见 hits memo)。 */\ntype SearchHit = {\n\tgroup: ModelProviderGroup\n\tmodel: ModelProviderGroup[\"models\"][number]\n\tnameHit: { start: number; end: number } | null\n}\n\n/** 搜索命中结果:items 为窗口内条目(渲染用),total 为命中总数(播报/截断提示用)。 */\ntype SearchResult = { items: SearchHit[]; total: number }\n\ninterface EffortSliderProps {\n state: DirectoryState\n select: (selection: ModelSelection) => Promise<boolean>\n t: TranslateNS<'modelSelector'>\n}\n\n\n// 图标直接用 primitives 的组件(此前本仓自带一份逐字节相同的 SVG 副本);\n// 图标组件只接受 size/className,aria-hidden 由调用处的包装元素承载。\nconst IconChevronDown = <IconChevronDownOutline14 />;\nconst IconCheck = <IconCheckOutline14 />;\nconst IconClear = <IconCloseFill14 />;\n/**\n* How long a successfully loaded directory snapshot is trusted before the\n* menu re-fetches it over RPC. The snapshot lives in the per-session store,\n* so reopening the menu within this window costs zero RPC and zero re-render.\n* 调法:调大 → 打开更省(RPC/重渲染都免),但目录侧改动要等窗口过期才可见;\n* 调小 → 目录更新更实时。目录内容由官方 modelDirectories 推送,本值只影响\n* 打开瞬间是否信任快照。\n*/\nconst DIRECTORY_STALE_MS = 3e4;\n/** 搜索命中渲染上限:宽泛关键词(如单字母)命中数百条时避免 DOM 爆炸。\n * 调法:调大 → 宽泛搜索看到更多命中(渲染与搜索成本线性上升);调小 → 更省。 */\nconst MAX_VISIBLE_HITS = 100;\n/** effort select RPC 的超时护栏:官方 select 无超时契约,RPC 永久挂起时\n * 必须释放滑杆的 committing 锁并回滚,否则滑杆被锁死到菜单关闭。\n * 调法:后端/网络慢时调大避免误回滚(滑杆锁死时间随之变长);调小更早释放交互。 */\nconst EFFORT_COMMIT_TIMEOUT_MS = 12e3;\n// ── 推理强度滑块(移植自 dsh-reasoning-effort:辐射特效 + 档位随模型自动适配)──\n// 绘制与动画循环见 ./effortCanvas.ts(dmsDrawRadiation / useEffortCanvas),\n// 指针拖动状态机见 ./effortDrag.ts(useEffortDrag);本组件只做档位状态、\n// 提交/回滚与渲染。\nexport const EffortSlider = react.memo(function EffortSlider({ state, select, t }: EffortSliderProps) {\n\tconst levels = dmsSliderLevels(state);\n\t// 惰性初始化到当前生效档:useState(\"\")/0 会让菜单打开的第一帧档位名为空、\n\t// 进度闪 0%,随后才被同步 effect 纠正(首帧即正确,无闪烁)。\n\tconst [effort, setEffort] = react.useState(() => levels[dmsEffectiveEffortIndex(levels, state)]?.id ?? \"\");\n\tconst [preview, setPreview] = react.useState(() => dmsEffectiveEffortIndex(levels, state));\n\tconst [committing, setCommitting] = react.useState(false);\n\tconst [localError, setLocalError] = react.useState<string | null>(null);\n\tconst canvasRef = react.useRef<HTMLCanvasElement | null>(null);\n\tconst inputRef = react.useRef<HTMLInputElement | null>(null);\n\tconst committedRef = react.useRef(\"\");\n\tconst committingRef = react.useRef(false);\n\tconst previewRef = react.useRef(0);\n\tconst draggingRef = react.useRef(false);\n\t// 提交超时定时器句柄 + 挂载标志:菜单关闭(EffortSlider 卸载)时由 cleanup\n\t// clearTimeout 并置 unmounted——迟到成功采纳/超时回滚的 setState 只在挂载期\n\t// 跑,卸载后不再触碰 React 状态,定时器也不必在卸载后继续空转。\n\tconst commitTimerRef = react.useRef<number | null>(null);\n\tconst mountedRef = react.useRef(false);\n\t// 提交纪元:单调递增,迟到采纳判据靠它区分「无新提交」与「新提交失败回滚」。\n\tconst commitEpochRef = react.useRef(0);\n\t// 辐射状态由组件持有:preview 变化/拖动时更新 target/dragging 驱动重绘;\n\t// progress 惰性对齐当前档位(打开菜单首帧即正确,无中间态缓动)。useRef\n\t// 初始化表达式每次渲染都会求值但仅首帧生效,这里取首次渲染的档位作起点。\n\tconst radiationRef = react.useRef<{ progress: number; dragging: boolean; target?: number }>({\n\t\tprogress: levels.length >= 2 ? dmsEffectiveEffortIndex(levels, state) / (levels.length - 1) : 0.5,\n\t\tdragging: false,\n\t});\n\tconst redrawRef = react.useRef<(() => void) | null>(null);\n\tconst available = state.current !== null && levels.length >= 2;\n\tconst busy = dmsEffortBusy(committing, state.status);\n\tconst error = localError;\n\treact.useEffect(() => {\n\t\tif (!available || committingRef.current || draggingRef.current) return;\n\t\tconst index = dmsEffectiveEffortIndex(levels, state);\n\t\tconst next = levels[index]?.id ?? \"\";\n\t\tcommittedRef.current = next;\n\t\tpreviewRef.current = index;\n\t\tsetEffort(next);\n\t\tsetPreview(index);\n\t\tsetLocalError(null);\n\t}, [available, levels.length, state.current?.provider, state.current?.model, state.current?.reasoningEffort]);\n\treact.useEffect(() => {\n\t\tpreviewRef.current = preview;\n\t\tradiationRef.current.target = levels.length >= 2 ? preview / (levels.length - 1) : 0.5;\n\t\tredrawRef.current?.();\n\t}, [preview, levels.length]);\n\tconst redraw = useEffortCanvas(canvasRef, radiationRef);\n\treact.useEffect(() => {\n\t\tredrawRef.current = redraw;\n\t\treturn () => { redrawRef.current = null; };\n\t}, [redraw]);\n\t// 卸载清理:迟到的 select settle 不再 setState(迟到成功采纳/超时回滚的\n\t// setState 都经 mountedRef 守卫);未决的超时定时器直接清除——组件已卸载,\n\t// 回滚/采纳都无人可见,定时器没有存在的意义。\n\treact.useEffect(() => {\n\t\tmountedRef.current = true;\n\t\treturn () => {\n\t\t\tmountedRef.current = false;\n\t\t\tif (commitTimerRef.current !== null) {\n\t\t\t\twindow.clearTimeout(commitTimerRef.current);\n\t\t\t\tcommitTimerRef.current = null;\n\t\t\t}\n\t\t};\n\t}, []);\n\tconst showPointerPreview = react.useCallback((raw: number): void => {\n\t\tpreviewRef.current = raw;\n\t\tsetPreview(raw);\n\t\tsetEffort(levels[dmsClampIndex(raw, levels.length)]?.id ?? \"\");\n\t}, [levels]);\n\tconst rollback = react.useCallback(() => {\n\t\tconst previous = committedRef.current;\n\t\tpreviewRef.current = Math.max(0, dmsEffortIndex(levels, previous));\n\t\tsetEffort(previous);\n\t\tsetPreview(Math.max(0, dmsEffortIndex(levels, previous)));\n\t}, [levels]);\n\tconst commit = react.useCallback(async (raw: number): Promise<void> => {\n\t\tif (dmsEffortBusy(committingRef.current, state.status)) return;\n\t\t// 先按钳位后的档位判断是否无操作:落回已提交的同一档(含拖动归位、键盘\n\t\t// 重复按当前档)时,不发多余 RPC,也不闪本地 busy 灰——原实现把这次早退\n\t\t// 放在 setCommitting(true)/乐观 setState 之后,同一档提交仍会闪一次。\n\t\t// levels 非空时 clamp 保证 index ∈ [0, count-1],next 不可能为 undefined。\n\t\tconst index = dmsClampIndex(raw, levels.length);\n\t\tconst next = levels[index]?.id;\n\t\tif (next === void 0 || next === committedRef.current) return;\n\t\tcommittingRef.current = true;\n\t\tconst previous = committedRef.current;\n\t\t// 提交纪元:每次发起新提交单调递增。迟到采纳判据用它区分「无新提交」与\n\t\t// 「新提交失败回滚」——两者 committedRef 都会回到 previous,值比较无法\n\t\t// 区分,纪元未变才说明迟到结果仍属当前唯一意图链。\n\t\tcommitEpochRef.current += 1;\n\t\tconst epochAtCommit = commitEpochRef.current;\n\t\t// 注意:这里不重置 dragging——commit 的唯二入口是拖动结束(stopDragging\n\t\t// 已先 setDragging(false))与键盘(本就非拖拽),无需防御性复位。\n\t\tsetCommitting(true);\n\t\tsetLocalError(null);\n\t\tpreviewRef.current = index;\n\t\tsetPreview(index);\n\t\tsetEffort(next);\n\t\ttry {\n\t\t\tconst current = state.current;\n\t\t\tif (current === null) throw new Error(t(\"empty.efforts\"));\n\t\t\t// 超时护栏:官方 select 无超时契约,把它包进带 12s 定时的 Promise——\n\t\t\t// 超时即视为失败(回滚 + 播报),并释放 committing 锁。select 迟到的\n\t\t\t// resolve 不直接丢弃:若它确认了新档位且自回滚以来没有新提交改写\n\t\t\t// committedRef(回滚态仍对应本次提交链),把 UI/committedRef 同步回\n\t\t\t// 实际已生效的档位;迟到的 reject 保持回滚态。timer 在 select 先完成\n\t\t\t// 时清除,不泄漏空转定时器。\n\t\t\tconst ok = await new Promise<boolean>((resolve, reject) => {\n\t\t\t\tlet settled = false;\n\t\t\t\tconst timer = window.setTimeout(() => {\n\t\t\t\t\tif (settled) return;\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tcommitTimerRef.current = null;\n\t\t\t\t\treject(new Error(t(\"effort.timeout\")));\n\t\t\t\t}, EFFORT_COMMIT_TIMEOUT_MS);\n\t\t\t\t// 定时器句柄进 ref:菜单关闭(本组件卸载)时由 cleanup 直接\n\t\t\t\t// clearTimeout,不再依赖 Promise 闭包把定时器活到 select 迟到 settle。\n\t\t\t\tcommitTimerRef.current = timer;\n\t\t\t\tselect({ provider: current.provider, model: current.model, reasoningEffort: next }).then(\n\t\t\t\t\t(accepted) => {\n\t\t\t\t\t\tif (settled) {\n\t\t\t\t\t\t\t// 迟到成功且实际生效:committedRef 还是回滚前的 previous\n\t\t\t\t\t\t\t// 且提交纪元未推进(期间没有新的提交/拖动改写它),说明\n\t\t\t\t\t\t\t// 回滚态对应用户当前唯一意图链,后端已确认新档位——补一次\n\t\t\t\t\t\t\t// UI 同步,消除「UI 回滚、后端已改档」的错位;否则以新\n\t\t\t\t\t\t\t// 操作链为准不覆盖(新提交失败回滚后 committedRef 值与\n\t\t\t\t\t\t\t// 上一轮相同,纪元未推进才说明没有新提交)。\n\t\t\t\t\t\t\t// 判定收敛到 dmsShouldAdoptLateSuccess(effort.ts,node 可测)。\n\t\t\t\t\t\t\tif (accepted && mountedRef.current && dmsShouldAdoptLateSuccess(committedRef.current, previous, draggingRef.current, committingRef.current, commitEpochRef.current, epochAtCommit)) {\n\t\t\t\t\t\t\t\tcommittedRef.current = next;\n\t\t\t\t\t\t\t\tpreviewRef.current = index;\n\t\t\t\t\t\t\t\tsetEffort(next);\n\t\t\t\t\t\t\t\tsetPreview(index);\n\t\t\t\t\t\t\t\tsetLocalError(null);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\twindow.clearTimeout(timer);\n\t\t\t\t\t\tcommitTimerRef.current = null;\n\t\t\t\t\t\tresolve(accepted);\n\t\t\t\t\t},\n\t\t\t\t\t(cause: unknown) => {\n\t\t\t\t\t\tif (settled) return;\n\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\twindow.clearTimeout(timer);\n\t\t\t\t\t\tcommitTimerRef.current = null;\n\t\t\t\t\t\treject(cause);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t});\n\t\t\tif (!ok) throw new Error(t(\"effort.failed\"));\n\t\t\tif (mountedRef.current) {\n\t\t\t\tcommittedRef.current = next;\n\t\t\t\tpreviewRef.current = index;\n\t\t\t\tsetEffort(next);\n\t\t\t\tsetPreview(index);\n\t\t\t}\n\t\t} catch (cause) {\n\t\t\tconst restore = Math.max(0, dmsEffortIndex(levels, previous));\n\t\t\tcommittedRef.current = previous;\n\t\t\tpreviewRef.current = restore;\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetEffort(previous);\n\t\t\t\tsetPreview(restore);\n\t\t\t\tsetLocalError(cause instanceof Error ? cause.message : String(cause));\n\t\t\t}\n\t\t} finally {\n\t\t\tcommittingRef.current = false;\n\t\t\tif (mountedRef.current) setCommitting(false);\n\t\t}\n\t}, [levels, select, state, t]);\n\t// 指针拖动状态机(pointer 生命周期 + window 兜底监听)在 effortDrag.ts;\n\t// commit/rollback/showPointerPreview 作回调外接——hook 经 callbacksRef 持有\n\t// 最新闭包,事件触发时取到当前渲染的函数。\n\tconst drag = useEffortDrag({\n\t\tlevelCount: () => levels.length,\n\t\tcanStart: () => !busy,\n\t\tonPreview: showPointerPreview,\n\t\tonCommit: (raw) => void commit(raw),\n\t\tonRollback: rollback,\n\t\tinputRef,\n\t\tdraggingRef,\n\t});\n\tconst { dragging } = drag;\n\t// 拖动态同步到辐射状态与画布(preview effect 已处理 target,这里只切 dragging 速度)。\n\treact.useEffect(() => {\n\t\tradiationRef.current.dragging = dragging;\n\t\tredrawRef.current?.();\n\t}, [dragging]);\n\tconst onKeyDown = (event: react.KeyboardEvent<HTMLInputElement>): void => {\n\t\tconst current = dmsClampIndex(Number(event.currentTarget.value), levels.length);\n\t\tlet target;\n\t\tif (event.key === \"ArrowLeft\" || event.key === \"ArrowDown\" || event.key === \"PageDown\") {\n\t\t\ttarget = Math.max(0, current - 1);\n\t\t} else if (event.key === \"ArrowRight\" || event.key === \"ArrowUp\" || event.key === \"PageUp\") {\n\t\t\ttarget = Math.min(levels.length - 1, current + 1);\n\t\t} else if (event.key === \"Home\") {\n\t\t\ttarget = 0;\n\t\t} else if (event.key === \"End\") {\n\t\t\ttarget = levels.length - 1;\n\t\t}\n\t\tif (target === void 0) return;\n\t\t// 忙态(自身提交中/共享目录 select 在途)吞掉按键:必须先 preventDefault,\n\t\t// 否则 range input 的原生步进会改 DOM value,与受控值漂移。\n\t\tevent.preventDefault();\n\t\tif (busy) return;\n\t\tvoid commit(target);\n\t};\n\tif (!available) return null;\n\tconst count = levels.length;\n\tconst effortName = levels[dmsEffortIndex(levels, effort)]?.name ?? effort;\n\tconst effortDesc = levels[dmsEffortIndex(levels, effort)]?.description;\n\tconst progress = preview / (count - 1) * 100;\n\tconst style = { \"--dms-progress\": `${progress}%` } as react.CSSProperties;\n\treturn (\n\t\t<div\n\t\t\tclassName={`dms-effort${dragging ? \" is-dragging\" : \"\"}${busy ? \" is-busy\" : \"\"}${error === null ? \"\" : \" is-error\"}`}\n\t\t>\n\t\t\t<div\n\t\t\t\tclassName=\"dms-effort-slider\"\n\t\t\t\tstyle={style}\n\t\t\t>\n\t\t\t\t<div className=\"dms-effort-track\" aria-hidden=\"true\" />\n\t\t\t\t<div className=\"dms-effort-fx\" aria-hidden=\"true\">\n\t\t\t\t\t<canvas ref={canvasRef} className=\"dms-effort-canvas\" />\n\t\t\t\t\t<span className=\"dms-effort-flare\" />\n\t\t\t\t</div>\n\t\t\t\t<input\n\t\t\t\t\tref={inputRef}\n\t\t\t\t\tclassName=\"dms-effort-input\"\n\t\t\t\t\ttype=\"range\"\n\t\t\t\t\tmin=\"0\"\n\t\t\t\t\tmax={count - 1}\n\t\t\t\t\tstep=\"0.01\"\n\t\t\t\t\tvalue={preview}\n\t\t\t\t\taria-disabled={busy}\n\t\t\t\t\taria-label={t(\"menu.effort\")}\n\t\t\t\t\taria-valuetext={effortName}\n\t\t\t\t\tonChange={(event: react.ChangeEvent<HTMLInputElement>) => {\n\t\t\t\t\t\tconst raw = Number(event.currentTarget.value);\n\t\t\t\t\t\tshowPointerPreview(raw);\n\t\t\t\t\t}}\n\t\t\t\t\tonPointerDown={drag.handlers.onPointerDown}\n\t\t\t\t\tonPointerMove={drag.handlers.onPointerMove}\n\t\t\t\t\tonPointerUp={drag.handlers.onPointerUp}\n\t\t\t\t\tonPointerCancel={drag.handlers.onPointerCancel}\n\t\t\t\t\tonBlur={drag.handlers.onBlur}\n\t\t\t\t\tonKeyDown={onKeyDown}\n\t\t\t\t/>\n\t\t\t\t<span className=\"dms-effort-knob\" aria-hidden=\"true\" />\n\t\t\t</div>\n\t\t\t<span className=\"dms-effort-value\">{effortName}</span>\n\t\t\t{effortDesc === undefined ? null : <span className=\"dms-effort-desc\">{effortDesc}</span>}\n\t\t\t{error === null ? null : <span className=\"dms-sr\" role=\"status\">{error}</span>}\n\t\t\t{error === null ? null : <div className=\"dms-effort-error\">{error}</div>}\n\t\t</div>\n\t);\n});\n\ninterface ModelOptionProps {\n\tgroup: ModelProviderGroup\n\tmodel: ModelProviderGroup['models'][number]\n\tshowProvider: boolean\n\tselected: boolean\n\tbusy: boolean\n\t/** 键盘导航用的稳定行标识:菜单内 `[data-row-key]` 查询即按 DOM 顺序聚焦。 */\n\trowKey: string\n\t/** roving tabindex:true = 当前活动行(唯一可 Tab 到的模型行,其余 -1)。 */\n\tactive: boolean\n\t/** 行聚焦回调(箭头导航/点击/程序化 focus 都经它同步活动行键)。 */\n\tonRowFocus: (key: string) => void\n\t/** 搜索命中且命中落在名称内时的片段区间(高亮);undefined/null = 不标。 */\n\tnameHit?: { start: number; end: number } | null\n\tt: TranslateNS<'modelSelector'>\n\tonChoose: (selection: ModelSelection) => void\n}\n/**\n * 菜单里的一行模型。独立成 memo 组件:目录可能数百行,搜索输入每击键都会\n * 重建菜单内容,行 props(group/model 引用、selected/busy/rowKey、稳定的\n * onChoose)稳定时 React 直接跳过 reconcile,只重渲染真正变化的那行。\n *\n * 自定义比较器(默认浅比较的补充):nameHit 是每次击键新建的对象(引用必变),\n * 但 100 条命中里绝大多数命中区间不变——按值比较 start/end,值相同即跳过\n * 重渲染,搜索才不卡顿。\n */\nconst ModelOption = react.memo(function ModelOption({ group, model, showProvider, selected, busy, rowKey, active, onRowFocus, nameHit, t, onChoose }: ModelOptionProps) {\n\tconst hit = nameHit === undefined || nameHit === null || nameHit.start === nameHit.end\n\t\t? null\n\t\t: [\n\t\t\tmodel.name.slice(0, nameHit.start),\n\t\t\tmodel.name.slice(nameHit.start, nameHit.end),\n\t\t\tmodel.name.slice(nameHit.end),\n\t\t];\n\treturn (\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\trole=\"menuitemradio\"\n\t\t\taria-checked={selected}\n\t\t\tdata-row-key={rowKey}\n\t\t\ttabIndex={active ? 0 : -1}\n\t\t\tclassName={`dms-model-option${selected ? \" dms-model-optionSelected\" : \"\"}`}\n\t\t\ttitle={model.description === undefined ? model.name : `${model.name} — ${model.description}`}\n\t\t\taria-disabled={busy}\n\t\t\tonFocus={() => onRowFocus(rowKey)}\n\t\t\tonClick={() => onChoose({ provider: group.id, model: model.id })}\n\t\t>\n\t\t\t<span className=\"dms-model-option-copy\">\n\t\t\t\t<span className=\"dms-nameRow\">\n\t\t\t\t\t<span className=\"dms-model-option-name\">\n\t\t\t\t\t\t{hit === null ? model.name : <>\n\t\t\t\t\t\t\t{hit[0]}<span className=\"dms-hit\">{hit[1]}</span>{hit[2]}\n\t\t\t\t\t\t</>}\n\t\t\t\t\t</span>\n\t\t\t\t\t{model.reasoning !== undefined && <span className=\"dms-badge\" title={t(\"badge.reasoningHint\")}>{t(\"badge.reasoning\")}</span>}\n\t\t\t\t</span>\n\t\t\t\t{model.description !== undefined && <span className=\"dms-model-option-desc\">{model.description}</span>}\n\t\t\t\t{showProvider && <span className=\"dms-model-option-provider\">{group.name}</span>}\n\t\t\t</span>\n\t\t\t<span className=\"dms-model-check\" aria-hidden=\"true\">\n\t\t\t\t{selected ? IconCheck : null}\n\t\t\t</span>\n\t\t</button>\n\t);\n}, (prev, next) => {\n\tif (prev.group !== next.group) return false;\n\tif (prev.model !== next.model) return false;\n\tif (prev.showProvider !== next.showProvider) return false;\n\tif (prev.selected !== next.selected) return false;\n\tif (prev.busy !== next.busy) return false;\n\tif (prev.rowKey !== next.rowKey) return false;\n\tif (prev.active !== next.active) return false;\n\tif (prev.onRowFocus !== next.onRowFocus) return false;\n\tif (prev.t !== next.t) return false;\n\tif (prev.onChoose !== next.onChoose) return false;\n\t// 引用相同(含都是 null/undefined)即视为未变化;否则按值比较命中区间。\n\tif (prev.nameHit === next.nameHit) return true;\n\tif (prev.nameHit === null || prev.nameHit === void 0) return false;\n\tif (next.nameHit === null || next.nameHit === void 0) return false;\n\treturn prev.nameHit.start === next.nameHit.start && prev.nameHit.end === next.nameHit.end;\n});\n\ninterface ModelSelectProps {\n locked: boolean\n available: boolean\n directory: SnapshotStore<ModelDirectoryState>\n load: () => void\n select: (selection: ModelSelection) => Promise<boolean>\n t: TranslateNS<'modelSelector'>\n}\nexport function ModelSelect({ locked, available, directory, load, select, t }: ModelSelectProps) {\n\tconst state = react.useSyncExternalStore(directory.subscribe, directory.getSnapshot);\n\tconst [open, setOpen] = react.useState(false);\n\t// 弹出方向自适应:菜单向上弹出(bottom 定位),但 trigger 距视口顶部\n\t// 空间不足时(新会话/页面中部)改为向下弹出,避免搜索框与选项溢出视口。\n\tconst [menuAbove, setMenuAbove] = react.useState(true);\n\tconst [query, setQuery] = react.useState(\"\");\n\tconst [collapsed, setCollapsed] = react.useState<Set<string>>(() => new Set());\n\tconst [notice, setNotice] = react.useState<string | null>(null);\n\t// roving tabindex 的活动行键:全列表只有这一行 tabindex=0(其余 -1,仅方向键\n\t// 可达),Tab 从搜索框进列表落在它上面。行焦点移动经 onRowFocus 更新;行集合\n\t// 变化(打开/搜索/折叠/选中迁移)时由下方 effect 重置到默认落点。\n\tconst [activeRowKey, setActiveRowKey] = react.useState<string | null>(null);\n\tconst onRowFocus = react.useCallback((key: string): void => {\n\t\tsetActiveRowKey(key);\n\t}, []);\n\t// 菜单内 notice 会随 close() 一起清掉,失败原因必须落在菜单外的瞬时横幅上\n\t// (官方 seat 正是用 Toast 播报 select 拒绝)。seq 递增让同一段文案可重播。\n\tconst [toast, setToast] = react.useState<{ seq: number; text: string; failed: boolean } | null>(null);\n\tconst toastSeqRef = react.useRef(0);\n\tconst showToast = react.useCallback((text: string, failed = true): void => {\n\t\ttoastSeqRef.current += 1;\n\t\tsetToast({ seq: toastSeqRef.current, text, failed });\n\t}, []);\n\tconst lastActionRef = react.useRef<\"load\" | \"select\">(\"load\");\n\tconst rootRef = react.useRef<HTMLDivElement | null>(null);\n\tconst triggerRef = react.useRef<HTMLButtonElement | null>(null);\n\tconst searchRef = react.useRef<HTMLInputElement | null>(null);\n\tconst lastLoadRef = react.useRef(0);\n\tconst id = react.useId();\n\t// 模型目录稳定时,haystack 索引不随 select/状态抖动重建:仅在 groups 引用\n\t// 真正变化(新 load 结果)时重建 400+ 条搜索索引。useMemo 保证 group 引用\n\t// 未变时 choices 引用稳定,currentChoice/hits 的 useMemo 依赖它也不会抖动。\n\tconst choices = react.useMemo<readonly ModelChoice[]>(\n\t\t() => state.groups.flatMap((group) => group.models.map((model) => ({\n\t\t\tgroup,\n\t\t\tmodel,\n\t\t\thaystack: `${model.name}\\n${model.description ?? \"\"}\\n${group.name}\\n${model.id}\\n${group.id}`.toLowerCase(),\n\t\t\tselection: {\n\t\t\t\tprovider: group.id,\n\t\t\t\tmodel: model.id,\n\t\t\t\t...model.reasoning?.defaultEffort === void 0 ? {} : { reasoningEffort: model.reasoning.defaultEffort }\n\t\t\t}\n\t\t}))),\n\t\t[state.groups],\n\t);\n\tconst currentChoice = react.useMemo(() => {\n\t\tconst current = state.current;\n\t\tif (current === null) return void 0;\n\t\treturn choices.find((c) => c.selection.provider === current.provider && c.selection.model === current.model);\n\t}, [choices, state.current]);\n\tconst reasoning = currentChoice?.model.reasoning;\n\tconst effectiveEffort = state.current?.reasoningEffort ?? reasoning?.defaultEffort;\n\tconst effortLabel = reasoning === void 0 ? void 0 : effectiveEffort === void 0 ? t(\"effort.providerDefault\") : reasoning.efforts.find((level) => level.id === effectiveEffort)?.name ?? effectiveEffort;\n\tconst busy = state.status === \"selecting\";\n\tconst normalized = query.trim().toLowerCase();\n\tconst hits = react.useMemo<SearchResult | null>(() => {\n\t\tif (normalized === \"\") return null;\n\t\tconst items: SearchHit[] = [];\n\t\tlet total = 0;\n\t\tfor (const choice of choices) {\n\t\t\tif (!choice.haystack.includes(normalized)) continue;\n\t\t\ttotal += 1;\n\t\t\t// 渲染窗口已满:只计数不构造对象——宽泛关键词(单字母)命中数百条时,\n\t\t\t// 每次击键省掉数百次 `{group, model, nameHit}` 分配;nameHit 的 indexOf\n\t\t\t// 也只在窗口内做(高亮只消费窗口内条目)。总数单独累计,播报与\n\t\t\t// 「仅显示前 N 条」提示仍然准确。\n\t\t\tif (items.length >= MAX_VISIBLE_HITS) continue;\n\t\t\tlet nameHit: { start: number; end: number } | null = null;\n\t\t\tconst at = choice.model.name.toLowerCase().indexOf(normalized);\n\t\t\tif (at >= 0) nameHit = { start: at, end: at + normalized.length };\n\t\t\titems.push({ group: choice.group, model: choice.model, nameHit });\n\t\t}\n\t\treturn { items, total };\n\t}, [choices, normalized]);\n\t// roving tabindex 的默认落点(Tab 从搜索框进列表的停靠行):选中行优先、\n\t// 其次首个可见模型行、全折叠时回退首个组头。纯决策在 roving.ts(node 可测)。\n\tconst defaultRowKey = react.useMemo(() => dmsDefaultRowKey({\n\t\thitKeys: hits === null ? null : hits.items.map((h) => `${h.group.id}\\u0000${h.model.id}`),\n\t\tmodelKeys: state.groups.flatMap((g) => (collapsed.has(g.id) ? [] : g.models.map((m) => `${g.id}\\u0000${m.id}`))),\n\t\theaderKeys: state.groups.map((g) => `header:${g.id}`),\n\t\tselectedKey: state.current === null ? null : `${state.current.provider}\\u0000${state.current.model}`,\n\t}), [hits, state.groups, collapsed, state.current]);\n\t// 行集合/选中变化时把 tabindex=0 的默认行同步到当前渲染集;菜单关闭清空。\n\t// 焦点已在行上(onRowFocus 改写过 activeRowKey)时,只要默认落点没变本\n\t// effect 不重跑,活动行保持用户最后聚焦的那行。\n\treact.useEffect(() => {\n\t\tif (!open) {\n\t\t\tsetActiveRowKey(null);\n\t\t\treturn;\n\t\t}\n\t\tsetActiveRowKey(defaultRowKey);\n\t}, [open, defaultRowKey]);\n\tconst reload = react.useCallback(() => {\n\t\tlastActionRef.current = \"load\";\n\t\tlastLoadRef.current = Date.now();\n\t\tload();\n\t}, [load]);\n\treact.useEffect(() => {\n\t\tif (available) {\n\t\t\t// 目录新鲜度守卫:inject 重跑会重建 load 引用触发本 effect,30s 内已有\n\t\t\t// 有效目录时不重复全量 load(show() 打开路径已有同款守卫)。\n\t\t\tif (Date.now() - lastLoadRef.current < DIRECTORY_STALE_MS && state.status === \"ready\" && state.groups.length > 0) return;\n\t\t\tlastActionRef.current = \"load\";\n\t\t\tlastLoadRef.current = Date.now();\n\t\t\tload();\n\t\t}\n\t}, [available, load]);\n\t// 菜单高度按视口实测钳位(原先 CSS 写死 min(420px, 100vh - 96px)),\n\t// composer 变高/窗口变小时自动收,不再溢出。\n\tconst menuRef = react.useRef<HTMLDivElement | null>(null);\n\t// useAnchoredMaxHeight 只适配「底边固定、向上生长」的底部锚定浮层:fit 读的是\n\t// 浮层自身底缘与视口顶缘的距离。向下弹(menuAbove=false)时面板改为顶边固定,\n\t// hook 度量的底缘是向下弹出后的位置,返回值在渲染处(maxHeight 三目)被忽略——\n\t// 继续挂监听只白付测量:每次 scroll/resize 都未节流 getBoundingClientRect +\n\t// setState 重渲染。向下弹期间把被测 ref 换成恒 null 的空 ref,hook 的 layout\n\t// effect 因 el === null 整体跳过(不注册监听、不测量);方向翻回向上时 ref 换回\n\t// menuRef,effect 因 ref 身份变化重跑并立即 fit——翻向后的钳位是新鲜测量\n\t// (原实现 signal 不变不重跑,翻回时用的还是向下弹期间留下的旧值)。\n\tconst inactiveMenuRef = react.useRef<HTMLDivElement | null>(null);\n\tconst menuMaxHeight = useAnchoredMaxHeight(menuAbove ? menuRef : inactiveMenuRef, MENU_MAX_HEIGHT, open);\n\tconst [belowMaxHeight, setBelowMaxHeight] = react.useState(MENU_MAX_HEIGHT);\n\t// 水平钳位:seat 右缘放不下整幅菜单(窄窗口)时改为 left 锚定,undefined = 默认右锚定。\n\tconst [menuLeft, setMenuLeft] = react.useState<number | undefined>(undefined);\n\t// 布局生效前测量(对齐 useAnchoredMaxHeight 的 useLayoutEffect 模式):\n\t// 用 useEffect 会在首帧绘制后才翻转方向/水平锚定,向下弹或左夹取场景\n\t// 菜单先按默认(向上弹 + 右锚定)画一帧再跳位,肉眼可见闪错位。\n\treact.useLayoutEffect(() => {\n\t\tif (!open) return;\n\t\tconst measure = () => {\n\t\t\tconst trigger = triggerRef.current;\n\t\t\tif (trigger === null) return;\n\t\t\tconst rect = trigger.getBoundingClientRect();\n\t\t\t// 取上下两侧中空间更大的一侧:只有上方放不下时才向下弹。\n\t\t\t// 原先要求上方空出满高(MENU_MAX_HEIGHT + 20),窗口一矮就反而把面板\n\t\t\t// 挤到视口下沿外面。\n\t\t\tsetMenuAbove(dmsMenuAbove(rect.top, rect.bottom, window.innerHeight));\n\t\t\t// Downward the panel is top-anchored, so the hook's own fit would feed\n\t\t\t// back on itself; clamp against the space below the trigger instead.\n\t\t\tsetBelowMaxHeight(dmsBelowMaxHeight(rect.bottom, window.innerHeight, MENU_MAX_HEIGHT));\n\t\t\t// 水平:菜单实际渲染宽度为准(offsetWidth),右锚定放不下时钳到视口内。\n\t\t\tconst menuWidth = menuRef.current?.offsetWidth ?? 0;\n\t\t\tsetMenuLeft(dmsMenuLeft(rect.right, menuWidth, window.innerWidth, MENU_VIEWPORT_MARGIN));\n\t\t};\n\t\tmeasure();\n\t\tlet raf = 0;\n\t\t// scroll 捕获阶段每帧触发,resize 也高频;rAF 合并同帧多次触发,\n\t\t// passive 声明让浏览器不必等我们作 preventDefault。\n\t\tconst onViewportChange = () => {\n\t\t\tif (raf !== 0) return;\n\t\t\traf = requestAnimationFrame(() => {\n\t\t\t\traf = 0;\n\t\t\t\tmeasure();\n\t\t\t});\n\t\t};\n\t\twindow.addEventListener(\"resize\", onViewportChange, { passive: true });\n\t\twindow.addEventListener(\"scroll\", onViewportChange, { capture: true, passive: true });\n\t\treturn () => {\n\t\t\tif (raf !== 0) cancelAnimationFrame(raf);\n\t\t\twindow.removeEventListener(\"resize\", onViewportChange);\n\t\t\twindow.removeEventListener(\"scroll\", onViewportChange, { capture: true });\n\t\t};\n\t}, [open]);\n\treact.useEffect(() => {\n\t\tif (!open) return;\n\t\tsearchRef.current?.focus();\n\t}, [open]);\n\t// 打开即把当前选中行滚进可视区:选中模型在长列表深处时不用手动翻找。\n\t// 手动改 scrollTop 而不用 scrollIntoView,避免连带滚动页面/其它祖先容器。\n\t// 吸顶分组头(sticky top:0)会盖住滚到容器顶缘的行:偏移量按所在组的\n\t// 头部实际高度让位,否则「滚到了」却看不见。\n\t// 依赖补全:目录异步加载完成(state.groups 引用变化)后重新定位——原实现\n\t// 只依赖 [open],加载完成晚于开合的那次渲染时选中行永远不会滚进可视区。\n\t// useLayoutEffect(而非 queueMicrotask):DOM 已在提交阶段就绪,同步滚动\n\t// 不产生首帧跳动;且此时行/头部尺寸可测。\n\tconst scrollSelectedIntoView = (): void => {\n\t\tconst list = menuRef.current?.querySelector(\".dms-groups\");\n\t\tif (list === null || list === void 0) return;\n\t\t// 折叠/展开/目录刷新改变内容高度后,scrollTop 可能超出新的可滚上限\n\t\t// (浏览器不自动钳回,滚动条下方露出空白),先钳回合法区间再定位。\n\t\tconst maxScroll = list.scrollHeight - list.clientHeight;\n\t\tif (list.scrollTop > maxScroll) list.scrollTop = Math.max(0, maxScroll);\n\t\t// 选中行不在 DOM(选中组被折叠 / 搜索把选中模型滤掉)时,退而滚动到\n\t\t// 选中项所在分组(含折叠态,让用户看到「当前模型在那个组」)。\n\t\tconst selected = state.current;\n\t\tconst row = list.querySelector('[role=\"menuitemradio\"][aria-checked=\"true\"]');\n\t\tif (row === null) {\n\t\t\tif (selected === null) return;\n\t\t\tconst section = list.querySelector<HTMLElement>(`section[data-group-id=\"${globalThis.CSS.escape(selected.provider)}\"]`);\n\t\t\tif (section === null) return;\n\t\t\tconst sectionRect = section.getBoundingClientRect();\n\t\t\tconst listRect = list.getBoundingClientRect();\n\t\t\tif (sectionRect.top < listRect.top) list.scrollTop += sectionRect.top - listRect.top;\n\t\t\telse if (sectionRect.bottom > listRect.bottom) list.scrollTop += sectionRect.bottom - listRect.bottom;\n\t\t\treturn;\n\t\t}\n\t\tconst rowRect = row.getBoundingClientRect();\n\t\tconst listRect = list.getBoundingClientRect();\n\t\tconst header = row.closest(\"section\")?.querySelector(\".dms-groupHeader\");\n\t\tconst headerHeight = header === null || header === undefined ? 0 : header.getBoundingClientRect().height;\n\t\tif (rowRect.top < listRect.top + headerHeight) list.scrollTop += rowRect.top - (listRect.top + headerHeight);\n\t\telse if (rowRect.bottom > listRect.bottom) list.scrollTop += rowRect.bottom - listRect.bottom;\n\t};\n\t// 打开瞬间自动展开选中组(若被折叠)。用 ref 记录「本次打开周期已自动展开过\n\t// 哪个 provider」:layout effect 依赖含 collapsed,若不拦截,用户手动折叠\n\t// 选中组时 effect 会立刻把它重新展开——折叠功能对选中组形同失效。\n\t// 只在 provider 首次解析到(含异步 load 完成后)且处于折叠态时展开一次,\n\t// 之后同 provider 的手动折叠不再被抢。\n\tconst expandedProviderRef = react.useRef<string | null>(null);\n\treact.useLayoutEffect(() => {\n\t\tif (!open) {\n\t\t\texpandedProviderRef.current = null;\n\t\t\treturn;\n\t\t}\n\t\t// 搜索命中渲染期间不滚动/不自动展开:命中列表替换了分组 DOM,滚动测量\n\t\t// 没有意义;且每次击键 hits 引用都变化——提前返回避免每次击键重跑\n\t\t// querySelector×2 + getBoundingClientRect×3 的定位测量(当前模型命中时\n\t\t// 还会写 scrollTop)。查询清空(hits 回 null)后本 effect 因 deps 变化\n\t\t// 重跑,滚动/展开行为与首次打开、异步加载完成场景保持一致。\n\t\tif (hits !== null) return;\n\t\tconst provider = state.current?.provider ?? null;\n\t\tif (provider !== null && provider !== expandedProviderRef.current && collapsed.has(provider)) {\n\t\t\texpandedProviderRef.current = provider;\n\t\t\tsetCollapsed((prev) => {\n\t\t\t\tif (!prev.has(provider)) return prev;\n\t\t\t\tconst next = new Set(prev);\n\t\t\t\tnext.delete(provider);\n\t\t\t\treturn next;\n\t\t\t});\n\t\t}\n\t\tscrollSelectedIntoView();\n\t}, [open, state.groups, state.current, collapsed, hits]);\n\t// show/close/choose/toggleCollapse 四个 useCallback 必须全部位于下方\n\t// `if (!available) return null` 早退之前:hooks 数量不得随渲染分支变化\n\t// (React 会直接抛 \"Rendered more hooks than during the previous render\")。\n\tconst show = react.useCallback(() => {\n\t\tsetOpen(true);\n\t\tif (state.status === \"error\" || state.groups.length === 0 || Date.now() - lastLoadRef.current > DIRECTORY_STALE_MS) reload();\n\t}, [state.status, state.groups.length, reload]);\n\tconst close = react.useCallback((restoreFocus = false) => {\n\t\tsetOpen(false);\n\t\tsetNotice(null);\n\t\tsetQuery(\"\");\n\t\tif (restoreFocus) queueMicrotask(() => {\n\t\t\ttriggerRef.current?.focus();\n\t\t});\n\t}, []);\n\t// 外部 pointerdown 关闭与 Escape 一致走 close(true):搜索框随菜单卸载后\n\t// 焦点落回 trigger(此前焦点直接掉 body)。hook 只以 false 调用 setter,\n\t// 忽略参数即可。\n\tconst dismissOnOutsidePointer = react.useCallback(() => {\n\t\tclose(true);\n\t}, [close]);\n\tuseDismissOnOutsidePointer(rootRef, open, dismissOnOutsidePointer);\n\t// locked 翻转(会话移除/失活/页面 inert 等)时若菜单还开着直接关闭:\n\t// trigger 已禁用,挂着的菜单没有可交互入口;close() 顺带清掉搜索词与\n\t// notice。close 引用稳定([] deps),effect 只在 locked/open 变化时重跑。\n\treact.useEffect(() => {\n\t\tif (locked && open) close();\n\t}, [locked, open, close]);\n\tconst choose = react.useCallback((selection: ModelSelection): void => {\n\t\tif (busy) return;\n\t\tif (state.current?.provider === selection.provider && state.current.model === selection.model) {\n\t\t\tsetNotice(t(\"notice.already\"));\n\t\t\treturn;\n\t\t}\n\t\tconst target = choices.find((c) => c.selection.provider === selection.provider && c.selection.model === selection.model);\n\t\tconst max = target?.model.reasoning === void 0 ? void 0 : maxEffortOf(target.model.reasoning);\n\t\tconst effort = max === \"off\" ? void 0 : max;\n\t\tconst full = {\n\t\t\tprovider: selection.provider,\n\t\t\tmodel: selection.model,\n\t\t\t...effort === void 0 ? {} : { reasoningEffort: effort }\n\t\t};\n\t\tlastActionRef.current = \"select\";\n\t\t// 自动拉档只有「高于模型自己声明的默认档」时才算替用户做了决定,此时播报落点。\n\t\tconst autoRaised = effort !== void 0 && effort !== target?.model.reasoning?.defaultEffort;\n\t\tconst autoName = target?.model.reasoning?.efforts.find((level) => level.id === effort)?.name ?? effort ?? \"\";\n\t\t// 超时护栏与 EffortSlider.commit() 同款:目录 select 无超时契约,RPC 永久\n\t\t// 挂起时若不加护栏,status='selecting' 由 store 持有、本插件无法复位,交互\n\t\t// 永久锁死。超时/拒绝都按切换失败播报 Toast(复用既有文案,不新增 key);\n\t\t// 不强行复位 store——目录 settle 后状态自然回到 ready,与 effort commit 一致。\n\t\t// 迟到的 settle 一律忽略:成功与否都会经共享目录写回,UI 无需再动作。\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tconst accepted = await new Promise<boolean>((resolve, reject) => {\n\t\t\t\t\tlet settled = false;\n\t\t\t\t\tconst timer = window.setTimeout(() => {\n\t\t\t\t\t\tif (settled) return;\n\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\treject(new Error(t(\"notice.selectFailed\")));\n\t\t\t\t\t}, EFFORT_COMMIT_TIMEOUT_MS);\n\t\t\t\t\tselect(full).then(\n\t\t\t\t\t\t(ok) => {\n\t\t\t\t\t\t\tif (settled) return;\n\t\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\t\twindow.clearTimeout(timer);\n\t\t\t\t\t\t\tresolve(ok);\n\t\t\t\t\t\t},\n\t\t\t\t\t\t(cause: unknown) => {\n\t\t\t\t\t\t\tif (settled) return;\n\t\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\t\twindow.clearTimeout(timer);\n\t\t\t\t\t\t\treject(cause);\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t\tif (!accepted) {\n\t\t\t\t\tconst message = directory.getSnapshot().error;\n\t\t\t\t\tshowToast(message !== null ? t(\"error.action\", { message }) : t(\"notice.selectFailed\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (rootRef.current !== null) {\n\t\t\t\t\tclose(true);\n\t\t\t\t\tif (autoRaised) showToast(t(\"toast.effortAuto\", { effort: autoName }), false);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// 超时 / select 拒绝:都按切换失败播报(防御性 catch 同时杜绝\n\t\t\t\t// unhandled rejection——未来注入面变更也不会让它漏出去)。\n\t\t\t\tshowToast(t(\"notice.selectFailed\"));\n\t\t\t}\n\t\t})();\n\t}, [busy, state.current, choices, select, t, directory, close, showToast]);\n\tconst toggleCollapse = react.useCallback((groupId: string): void => {\n\t\tsetCollapsed((prev) => {\n\t\t\tconst next = new Set(prev);\n\t\t\tif (next.has(groupId)) next.delete(groupId);\n\t\t\telse next.add(groupId);\n\t\t\treturn next;\n\t\t});\n\t}, []);\n\tif (!available) return null;\n\t// 键盘导航:按 DOM 顺序查询菜单内全部行(data-row-key),不再依赖渲染期\n\t// 收集的 ref 数组(那既阻止行级 memo,又把副作用塞进 render)。\n\tconst moveFocus = (offset: number): void => {\n\t\tconst menu = menuRef.current;\n\t\tif (menu === null) return;\n\t\tconst items = [...menu.querySelectorAll<HTMLButtonElement>('[data-row-key]')];\n\t\tif (items.length === 0) return;\n\t\tconst active = items.findIndex((item) => item === document.activeElement);\n\t\t// 焦点不在任何行上(在搜索框等菜单内控件)时:向下从首行进、向上从末行进(环绕);\n\t\t// 已在某行上时按 offset 循环。\n\t\tconst start = active < 0 ? (offset > 0 ? -1 : 0) : active;\n\t\titems[(start + offset + items.length) % items.length]?.focus();\n\t};\n\t// Home/End 跳首/末行(WAI-ARIA menu 标准键;搜索框内不劫持——那是移光标键)。\n\tconst focusEdge = (last: boolean): void => {\n\t\tconst items = menuRef.current?.querySelectorAll<HTMLButtonElement>('[data-row-key]');\n\t\tif (items === undefined || items.length === 0) return;\n\t\t(last ? items[items.length - 1] : items[0])?.focus();\n\t};\n\tconst onRootKeyDown = (event: react.KeyboardEvent<HTMLDivElement>): void => {\n\t\t// IME 组合输入期间不劫持按键:Enter 是候选上屏确认、方向键在候选窗翻页、\n\t\t// Escape 是取消组合——此时关菜单/选首个命中/移焦点都是抢用户的输入。\n\t\tif (event.nativeEvent.isComposing) return;\n\t\tconst target = event.target;\n\t\tif (event.key === \"Escape\" && open) {\n\t\t\t// Escape 分层:查询非空且焦点在菜单内时,第一按只清词(搜索框内清词\n\t\t\t// 焦点留在原处;列表内清词会让命中行卸载,焦点还回搜索框,避免掉\n\t\t\t// body),再按一次才关菜单。IME 组合输入在函数入口已被 isComposing\n\t\t\t// 挡掉,这里不需要重复守卫。\n\t\t\tif (query !== \"\" && rootRef.current?.contains(document.activeElement) === true) {\n\t\t\t\tconst fromSearch = target instanceof HTMLInputElement && target === searchRef.current;\n\t\t\t\tsetNotice(null);\n\t\t\t\tsetQuery(\"\");\n\t\t\t\tif (!fromSearch) searchRef.current?.focus();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tevent.preventDefault();\n\t\t\tclose(true);\n\t\t\treturn;\n\t\t}\n\t\tif (!open) return;\n\t\tconst fromSearch = target instanceof HTMLInputElement && target === searchRef.current;\n\t\tif ((event.key === \"ArrowDown\" || event.key === \"ArrowUp\") && (!(target instanceof HTMLInputElement) || fromSearch)) {\n\t\t\t// 搜索框自动聚焦后箭头原本只在输入框内移光标(键盘导航死路);现在\n\t\t\t// 搜索框内方向键也进入结果列表(effort 滑杆的 range input 不受影响)。\n\t\t\tevent.preventDefault();\n\t\t\tmoveFocus(event.key === \"ArrowDown\" ? 1 : -1);\n\t\t\treturn;\n\t\t}\n\t\tif ((event.key === \"Home\" || event.key === \"End\") && !(target instanceof HTMLInputElement)) {\n\t\t\tevent.preventDefault();\n\t\t\tfocusEdge(event.key === \"End\");\n\t\t\treturn;\n\t\t}\n\t\t// 只有搜索框内的 Enter 才选中第一个命中:焦点落在 effort 滑杆(range\n\t\t// input)等其它控件时,Enter 不应把用户的选择抢走。\n\t\tif (event.key === \"Enter\" && fromSearch) {\n\t\t\tif (hits !== null && hits.items.length > 0) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tconst first = hits.items[0]!;\n\t\t\t\tchoose({ provider: first.group.id, model: first.model.id });\n\t\t\t}\n\t\t}\n\t};\n\tconst onBlur = (event: react.FocusEvent<HTMLDivElement>): void => {\n\t\tif (busy) return;\n\t\tconst related = event.relatedTarget;\n\t\tif (related instanceof Node) {\n\t\t\tif (rootRef.current?.contains(related)) return;\n\t\t\tclose();\n\t\t\treturn;\n\t\t}\n\t\t// relatedTarget 为 null(窗口失焦 alt-tab / 焦点落到不可聚焦区域)也收起菜单。\n\t\tclose();\n\t};\n\t// 标签兜底对齐官方:目录成员资格只是参考(routable 契约),current 匹配不到\n\t// group 不代表没有选择——此时显示 provider/model 原始 id,而不是「选择模型」。\n\tconst waiting = state.current === null && state.status === \"loading\";\n\tconst modelLabel = currentChoice?.model.name\n\t\t?? (waiting\n\t\t\t? t(\"trigger.loading\")\n\t\t\t: state.current === null ? t(\"trigger.fallback\") : `${state.current.provider}/${state.current.model}`);\n\tconst providerLabel = currentChoice?.group.name;\n\tconst triggerLabel = effortLabel === void 0 ? modelLabel : `${modelLabel} · ${effortLabel}`;\n\tconst triggerTitle = providerLabel === void 0 ? triggerLabel : `${providerLabel} · ${triggerLabel}`;\n\tconst triggerAria = waiting ? t(\"trigger.loading\") : currentChoice === void 0 ? (state.current === null ? t(\"trigger.selectAria\") : t(\"trigger.aria\", { model: `${state.current.provider}/${state.current.model}` })) : effortLabel === void 0 ? t(\"trigger.aria\", { model: providerLabel === void 0 ? modelLabel : `${providerLabel} ${modelLabel}` }) : t(\"trigger.ariaEffort\", {\n\t\tmodel: providerLabel === void 0 ? modelLabel : `${providerLabel} ${modelLabel}`,\n\t\teffort: effortLabel\n\t});\n\tconst renderErrorStrip = () => {\n\t\t// Load failures only: a rejected select is announced by the Toast, which\n\t\t// also survives closing the menu (see choose()).\n\t\tif (state.error !== null && lastActionRef.current === \"load\") {\n\t\t\treturn (\n\t\t\t\t<div className=\"dms-error\">\n\t\t\t\t\t<span>{t(\"error.action\", { message: state.error })}</span>\n\t\t\t\t\t<button type=\"button\" className=\"dms-retry\" onClick={reload}>{t(\"action.reload\")}</button>\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t};\n\tconst renderFailures = (failures: DirectoryState['failures']) => failures.map((failure) => (\n\t\t<div key={failure.id} className=\"dms-warning\">\n\t\t\t<span>{t(\"warning.groupLoad\", { name: failure.name, message: failure.message })}</span>\n\t\t\t<button type=\"button\" className=\"dms-retry\" onClick={reload}>{t(\"action.reload\")}</button>\n\t\t</div>\n\t));\n\t// 列表区的两个空态:空态播报节点必须放在 role=menu 容器外(menu 内容模型\n\t// 只允许菜单节点),视觉位置由 .dms-groupsFill 的弹性占位补回(与 .dms-groups\n\t// 同款 flex 布局),空态时列表容器本身不渲染。\n\tconst noHits = hits !== null && hits.items.length === 0;\n\tconst noModels = hits === null && state.status === 'ready' && choices.length === 0;\n\treturn (\n\t\t<div ref={rootRef} className=\"dms-root\" onKeyDown={onRootKeyDown} onBlur={onBlur}>\n\t\t\t<button\n\t\t\t\tref={triggerRef}\n\t\t\t\ttype=\"button\"\n\t\t\t\tclassName=\"dms-trigger\"\n\t\t\t\taria-label={triggerAria}\n\t\t\t\taria-haspopup=\"menu\"\n\t\t\t\taria-expanded={open}\n\t\t\t\taria-controls={open ? `${id}-menu` : undefined}\n\t\t\t\ttitle={triggerTitle}\n\t\t\t\tdisabled={locked}\n\t\t\t\tonClick={() => (open ? close() : show())}\n\t\t\t>\n\t\t\t\t<span className=\"dms-triggerLabel\">{modelLabel}</span>\n\t\t\t\t{providerLabel !== undefined && <span className=\"dms-triggerProvider\">{providerLabel}</span>}\n\t\t\t\t{effortLabel !== undefined && <span className=\"dms-triggerEffort\">{effortLabel}</span>}\n\t\t\t\t<span className={`dms-chevron${open ? \" dms-chevronOpen\" : \"\"}`} aria-hidden=\"true\">\n\t\t\t\t\t{IconChevronDown}\n\t\t\t\t</span>\n\t\t\t</button>\n\t\t\t{open ? (\n\t\t\t\t<div\n\t\t\t\t\tid={`${id}-menu`}\n\t\t\t\t\tref={menuRef}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tmaxHeight: menuAbove ? menuMaxHeight : belowMaxHeight,\n\t\t\t\t\t\t...(menuLeft === undefined ? null : { left: menuLeft, right: \"auto\" }),\n\t\t\t\t\t}}\n\t\t\t\t\tclassName={'dms-menu dms-menuModel' + (menuAbove ? '' : ' dms-menuBelow')}\n\t\t\t\t\taria-busy={state.status === 'loading' || busy}\n\t\t\t\t>\n\t\t\t\t\t{state.status === 'loading' && <div className=\"dms-status\">{t('status.loading')}</div>}\n\t\t\t\t\t{renderErrorStrip()}\n\t\t\t\t\t{state.failures.length > 0 && <div className=\"dms-failures\">{renderFailures(state.failures)}</div>}\n\t\t\t\t\t<div className=\"dms-search\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tref={searchRef}\n\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\tclassName=\"dms-searchInput\"\n\t\t\t\t\t\t\tvalue={query}\n\t\t\t\t\t\t\tplaceholder={t('search.placeholder')}\n\t\t\t\t\t\t\taria-label={t('search.placeholder')}\n\t\t\t\t\t\t\taria-controls={`${id}-groups`}\n\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\tenterKeyHint=\"search\"\n\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\tonChange={(event: react.ChangeEvent<HTMLInputElement>) => {\n\t\t\t\t\t\t\t\tsetNotice(null);\n\t\t\t\t\t\t\t\tsetQuery(event.target.value);\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{query !== '' && (\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclassName=\"dms-searchClear\"\n\t\t\t\t\t\t\t\taria-label={t('search.clearAria')}\n\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\tsetNotice(null);\n\t\t\t\t\t\t\t\t\tsetQuery('');\n\t\t\t\t\t\t\t\t\tsearchRef.current?.focus();\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span aria-hidden=\"true\" className=\"dms-icon-slot\">{IconClear}</span>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t{/* 搜索结果的屏幕阅读器播报:命中总数变化时由 role=status 播报,\n\t\t\t\t\t 视觉隐藏(.dms-sr);放在 role=menu 容器外,不污染菜单内容模型。 */}\n\t\t\t\t\t{hits !== null && (\n\t\t\t\t\t\t<span className=\"dms-sr\" role=\"status\">{t('search.status', { count: String(hits.total) })}</span>\n\t\t\t\t\t)}\n\t\t\t\t\t{/* 空态(无命中/无模型)与「仅显示前 N 条」note 全部在 role=menu\n\t\t\t\t\t 容器外:menu 的直接内容模型只允许 menuitem/group 等菜单节点,\n\t\t\t\t\t live region 会被菜单导航跳过、静态说明不该冒充菜单项。视觉\n\t\t\t\t\t 位置由 .dms-groupsFill(空态)与容器后的 .dms-more 保持。 */}\n\t\t\t\t\t{noHits && (\n\t\t\t\t\t\t<div className=\"dms-empty dms-groupsFill\" role=\"status\">{t('search.noMatch', { query: query.trim() })}</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{noModels && (\n\t\t\t\t\t\t<div className=\"dms-empty dms-groupsFill\" role=\"status\">{t('empty.models')}</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{!noHits && !noModels && (\n\t\t\t\t\t<div className=\"dms-groups\" id={`${id}-groups`} role=\"menu\" aria-label={t(\"menu.aria\")}>\n\t\t\t\t\t\t{hits !== null\n\t\t\t\t\t\t\t? hits.items.map((hit) => {\n\t\t\t\t\t\t\t\tconst rowKey = `${hit.group.id}\\u0000${hit.model.id}`;\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<ModelOption\n\t\t\t\t\t\t\t\t\t\tkey={rowKey}\n\t\t\t\t\t\t\t\t\t\tgroup={hit.group}\n\t\t\t\t\t\t\t\t\t\tmodel={hit.model}\n\t\t\t\t\t\t\t\t\t\tshowProvider\n\t\t\t\t\t\t\t\t\t\tselected={state.current?.provider === hit.group.id && state.current.model === hit.model.id}\n\t\t\t\t\t\t\t\t\t\tbusy={busy}\n\t\t\t\t\t\t\t\t\t\trowKey={rowKey}\n\t\t\t\t\t\t\t\t\t\tactive={rowKey === activeRowKey}\n\t\t\t\t\t\t\t\t\t\tonRowFocus={onRowFocus}\n\t\t\t\t\t\t\t\t\t\tnameHit={hit.nameHit}\n\t\t\t\t\t\t\t\t\t\tt={t}\n\t\t\t\t\t\t\t\t\t\tonChoose={choose}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t: state.groups.map((group) => {\n\t\t\t\t\t\t\t\tconst headingId = `${id}-${group.id}`;\n\t\t\t\t\t\t\t\tconst isCollapsed = collapsed.has(group.id);\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<section key={group.id} data-group-id={group.id} role=\"group\" aria-labelledby={headingId} className=\"dms-group\">\n\t\t\t\t\t\t\t\t\t\t{/* 组头参与 roving 方向键导航(data-row-key)但不进 Tab 序\n\t\t\t\t\t\t\t\t\t\t (tabIndex=-1):折叠/展开对键盘可达,数百行却不会\n\t\t\t\t\t\t\t\t\t\t 撑爆 Tab 停靠点。role=menuitem + aria-expanded 是\n\t\t\t\t\t\t\t\t\t\t ARIA menu 模式里「可展开菜单项」的规范语义。 */}\n<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\t\t\t\t\tdata-row-key={`header:${group.id}`}\n\t\t\t\t\t\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"dms-groupHeader\"\n\t\t\t\t\t\t\t\t\t\t\taria-expanded={!isCollapsed}\n\t\t\t\t\t\t\t\t\t\t\taria-label={t('group.toggleAria', { name: group.name, count: String(group.models.length) })}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => toggleCollapse(group.id)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span className={`dms-groupChevron${isCollapsed ? ' dms-groupChevronClosed' : ''}`} aria-hidden=\"true\">\n\t\t\t\t\t\t\t\t\t\t\t\t{IconChevronDown}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t<span id={headingId} className=\"dms-groupName\">{group.name}</span>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"dms-groupCount\">{group.models.length}</span>\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t{!isCollapsed && group.models.map((model) => {\n\t\t\t\t\t\t\t\t\t\t\tconst rowKey = `${group.id}\\u0000${model.id}`;\n\t\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t\t<ModelOption\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey={rowKey}\n\t\t\t\t\t\t\t\t\t\t\t\t\tgroup={group}\n\t\t\t\t\t\t\t\t\t\t\t\t\tmodel={model}\n\t\t\t\t\t\t\t\t\t\t\t\t\tshowProvider={false}\n\t\t\t\t\t\t\t\t\t\t\t\t\tselected={state.current?.provider === group.id && state.current.model === model.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\tbusy={busy}\n\t\t\t\t\t\t\t\t\t\t\t\t\trowKey={rowKey}\n\t\t\t\t\t\t\t\t\t\t\t\t\tactive={rowKey === activeRowKey}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonRowFocus={onRowFocus}\n\t\t\t\t\t\t\t\t\t\t\t\t\tt={t}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonChoose={choose}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t</section>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{hits !== null && hits.total > MAX_VISIBLE_HITS && (\n\t\t\t\t\t\t// role=note:静态辅助说明,从菜单项语义里退出来。\n\t\t\t\t\t\t<div className=\"dms-more\" role=\"note\">{t('search.more', { shown: String(MAX_VISIBLE_HITS), total: String(hits.total) })}</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{state.current !== null && dmsSliderLevels(state).length >= 2 && (\n\t\t\t\t\t\t<div className=\"dms-effortFooter\">\n\t\t\t\t\t\t\t<span className=\"dms-effortFooterLabel\">{t('menu.effort')}</span>\n\t\t\t\t\t\t\t<EffortSlider state={state} select={select} t={t} />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{notice !== null && <div className=\"dms-notice\" role=\"status\">{notice}</div>}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t{toast !== null && (\n\t\t\t\t<Toast\n\t\t\t\t\tkey={toast.seq}\n\t\t\t\t\ttext={toast.text}\n\t\t\t\t\ticon={toast.failed ? <IconWarningOutline16 /> : undefined}\n\t\t\t\t\tanchor={rootRef.current?.closest<HTMLElement>(\"[data-composer-card]\") ?? null}\n\t\t\t\t\tonDone={() => { setToast(null); }}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</div>\n\t);\n}","/**\n* Stylesheet for the enhanced model seat.\n*\n* Injected as one `<style data-plugin=\"@bittersmilezzz/dsh-model-selector\">` tag by the client\n* apply and removed again on unload. Class names are prefixed `dms-` so they\n* cannot collide with CSS-module hashes from other plugins. Colors come only\n* from `--dsw-*` theme tokens, matching the shipped Menu material.\n*/\nexport 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/* 键盘焦点此前在这三个可聚焦控件上完全隐身(上轮只补了模型行)。 */\n.dms-searchClear:focus-visible {\n border-radius: 8px;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n/* 图标组件只收 size/className,装饰性 aria-hidden 只能包一层 span;contents 让它不占布局。 */\n.dms-icon-slot { display: contents; }\n\n.dms-groups { flex: 1 1 auto; min-height: 0; overflow-y: auto; contain: content; }\n/* 空态占位:空态播报节点在 role=menu 容器外,用与 .dms-groups 同款弹性布局\n 补回列表区本来的位置(不挤压其余栏,消息也不会贴到菜单顶部)。 */\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/* 档位读数:拖动时实时跟随 preview,替代此前只有读屏能看到的 aria-valuetext。 */\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/* 官方契约的 efforts[].description,整行展示在滑杆下方。 */\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/* 视觉隐藏工具类(SR 播报/错误原文:不占布局但可被读屏读到)。 */\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/* 搜索命中片段:名称内标下划线(命中描述/供应商时不标,见 nameHit)。 */\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/* 方向键导航把焦点落在选项上,此前没有任何焦点指示。 */\n.dms-model-option:focus-visible { background: var(--dsw-alias-interactive-bg-hover); }\n/* 当前选中行:仅靠勾号不足以在密集列表里定位。 */\n.dms-model-optionSelected { background: var(--dsw-alias-interactive-bg-hover); }\n.dms-model-optionSelected .dms-model-option-name { font-weight: 600; }\n/* select 进行中:官方以 disabled 变灰表达,这里用 aria-busy 驱动同样的视觉、\n 不夺键盘焦点(aria-disabled 保连续性,见 ModelOption)。 */\n.dms-menu[aria-busy=\"true\"] .dms-model-option { color: var(--dsw-alias-label-dimmed); }\n/* 搜索结果里的供应商标是区分跨供应商同名模型的唯一线索,之前无任何样式。 */\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`;\n","/**\n * dsh-model-selector — browser half.\n *\n * Replaces the shipped `conversation.input.model` seat with an enhanced one\n * (provider-group collapse + name search) while keeping the exact shared\n * per-session directory (`ctx.modelDirectories`) the /model popup reads, so\n * both entries stay in sync. Shadowing is the sanctioned seam: the slot is\n * `single`, so a registration at a LOWER priority than the shipped occupant\n * (default 0) becomes the rendered winner.\n */\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type { SessionId } from '@deepseek-ai/dsh-session/types'\n// Type-only: pulls the ui-renderer Context merge (ctx.slots), moved here in dsh alpha.2.\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\n// Type-only: pulls the session controller Context merge (ctx.sessions) in alpha.2.\nimport type {} from '@deepseek-ai/dsh-api-session-controller/client'\n// Type-only: pulls the ui-conversation SlotMap merge (the input model seat).\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\n// Type-only: pulls the locale plugin's Context merge (ctx.locale).\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\n// Type-only: pulls the model-selection plugin's Context merge (ctx.modelDirectories)\n// plus the injected face type the seat registration hands to the component.\nimport type { ModelSelectInjected } from '@deepseek-ai/dsh-client-ui-model-selection/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-model-selection/client'\nimport { ModelSelect, zhDict, enDict } from './ModelSelect.tsx'\nimport { CSS } from './styles.ts'\n\n/** Dictionary namespace owned by this plugin. */\nconst NS = 'modelSelector'\n\n/** Required services: the registry, session lookup, locale, the slot seat, and the model directory's Remote faces. */\nexport const inject = [\n 'slots',\n 'sessions',\n 'locale',\n 'modelDirectories',\n 'remote',\n 'remote.session',\n]\n\n/**\n * Client plugin body: register the dictionaries and stylesheet, then take the\n * model seat over the shared directory once `modelDirectories` appears.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, {\n zh: zhDict,\n en: enDict,\n }), 'dsh-model-selector: dictionaries')\n ctx.effect(() => {\n const tag = document.createElement('style')\n tag.dataset.plugin = '@bittersmilezzz/dsh-model-selector'\n tag.textContent = CSS\n document.head.appendChild(tag)\n return () => {\n tag.remove()\n }\n }, 'dsh-model-selector: styles')\n ctx.inject(['modelDirectories'], (scope) => {\n const models = scope.modelDirectories\n const sessions = scope.sessions\n scope.slots.inject('conversation.input.model', () => scope.slots.register({\n name: 'conversation.input.model',\n locale: NS,\n priority: -1,\n inject: (sessionId: string): ModelSelectInjected => {\n const sid = sessionId as SessionId\n const directory = models.directoryFor(sid)\n const available = sessions.subagentAddress(sid) === undefined\n return {\n available,\n directory: directory.store,\n load: () => {\n if (available) directory.load().catch((error: unknown) => { console.warn('[dsh-model-selector] directory.load failed:', error) })\n },\n // 保留 boolean 契约(调用方按 false 走 notice),但把真实拒绝原因留在控制台,\n // 否则用户只看到「切换失败」、线上无从定位。\n select: (selection) => available\n ? directory.select(selection).then(\n () => true,\n (error: unknown) => {\n console.warn('[dsh-model-selector] select failed:', error)\n return false\n },\n )\n : Promise.resolve(false),\n }\n },\n }, ModelSelect))\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["IconChevronDownOutline14","IconCheckOutline14","IconCloseFill14","useAnchoredMaxHeight","Toast","IconWarningOutline16","zhDict","enDict"],"sources":["../src/client/locales.ts","../src/client/effort.ts","../src/client/effortCanvas.ts","../src/client/effortDrag.ts","../src/client/menuFit.ts","../src/client/roving.ts","../src/client/ModelSelect.tsx","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/**\n * `modelSelector` namespace dictionaries for the enhanced model seat.\n *\n * Simplified Chinese is the key-set source of truth; the English dictionary is\n * checked complete against it. Product copy is Chinese-first per repo style.\n */\nexport const zh = {\n\t\"trigger.fallback\": \"选择模型\",\n\t\"trigger.loading\": \"正在加载模型…\",\n\t\"trigger.selectAria\": \"选择模型\",\n\t\"trigger.aria\": \"选择模型,当前 {model}\",\n\t\"trigger.ariaEffort\": \"选择模型,当前 {model},推理等级 {effort}\",\n\t\"menu.aria\": \"模型列表\",\n\t\"menu.effort\": \"推理等级\",\n\t\"search.placeholder\": \"搜索模型\",\n\t\"search.clearAria\": \"清除搜索\",\n\t\"search.noMatch\": \"没有匹配“{query}”的模型。\",\n\t\"search.status\": \"{count} 个结果\",\n\t\"search.more\": \"仅显示前 {shown} 条(共 {total} 条命中),请细化关键词。\",\n\t\"group.toggleAria\": \"{name}({count} 个模型)\",\n\t\"effort.providerDefault\": \"Default\",\n\t\"status.loading\": \"正在刷新模型列表…\",\n\t\"error.action\": \"模型操作失败:{message}\",\n\t\"action.reload\": \"重新加载\",\n\t\"warning.groupLoad\": \"{name} 加载失败:{message}\",\n\t\"empty.models\": \"没有可用的模型。\",\n\t\"empty.efforts\": \"当前模型未提供推理等级。\",\n\t\"effort.failed\": \"档位切换未生效(后端未接受),已恢复原档位\",\n\t\"effort.timeout\": \"档位切换超时,已恢复原档位\",\n\t\"notice.already\": \"已是当前模型,无需切换\",\n\t\"notice.selectFailed\": \"切换失败,请重试\",\n\t\"toast.effortAuto\": \"已自动选到最强思考档:{effort}\",\n\t\"badge.reasoning\": \"推理\",\n\t\"badge.reasoningHint\": \"支持思考等级,切换后自动选到最大思考强度\"\n};\n\n\nexport const en = {\n\t\"trigger.fallback\": \"Select model\",\n\t\"trigger.loading\": \"Loading models…\",\n\t\"trigger.selectAria\": \"Select model\",\n\t\"trigger.aria\": \"Select model, current {model}\",\n\t\"trigger.ariaEffort\": \"Select model, current {model}, reasoning effort {effort}\",\n\t\"menu.aria\": \"Model list\",\n\t\"menu.effort\": \"Effort\",\n\t\"search.placeholder\": \"Search models\",\n\t\"search.clearAria\": \"Clear search\",\n\t\"search.noMatch\": \"No models match “{query}”.\",\n\t\"search.status\": \"{count} results\",\n\t\"search.more\": \"Showing first {shown} of {total} matches; refine your query.\",\n\t\"group.toggleAria\": \"{name} ({count} models)\",\n\t\"effort.providerDefault\": \"Default\",\n\t\"status.loading\": \"Refreshing model list…\",\n\t\"error.action\": \"Model operation failed: {message}\",\n\t\"action.reload\": \"Reload\",\n\t\"warning.groupLoad\": \"{name} failed to load: {message}\",\n\t\"empty.models\": \"No models available.\",\n\t\"empty.efforts\": \"This model provides no reasoning effort levels.\",\n\t\"effort.failed\": \"Failed to switch effort (backend rejected); restored\",\n\t\"effort.timeout\": \"Timed out switching effort; restored\",\n\t\"notice.already\": \"Already the current model\",\n\t\"notice.selectFailed\": \"Switch failed, please try again\",\n\t\"toast.effortAuto\": \"Auto-selected the strongest reasoning level: {effort}\",\n\t\"badge.reasoning\": \"Reasoning\",\n\t\"badge.reasoningHint\": \"Supports reasoning levels; switches land on the strongest\"\n};\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** The enhanced model seat copy. */\n modelSelector: keyof typeof zh\n }\n}\n","/**\n * dsh-model-selector — 推理强度档位的纯函数。\n *\n * 从 ModelSelect.tsx 原样搬出(无 JSX、无 DOM、无模块级副作用),以便用\n * node --test 直接跑源码:滑块钳位、档位映射与「取模型最强档」都是易错且\n * 用户可见的逻辑,留在 .tsx 里则无法脱离 React 测试。\n */\nimport type { ModelDirectoryState } from '@deepseek-ai/dsh-client-ui-model-selection/client'\nimport type { ModelProviderGroup, ModelReasoning } from '@deepseek-ai/dsh-api-session-controller/types'\n\n/** One model effort level as advertised by the adapter. */\nexport type EffortLevel = ModelReasoning['efforts'][number]\n/** Per-session model directory snapshot (official state shape). */\nexport type DirectoryState = ModelDirectoryState\n/** The selected model entry, or undefined when nothing resolves. */\nexport type ModelEntry = ModelProviderGroup['models'][number]\n\n/** Canonical strength order of pi-ai thinking levels (strongest = highest). */\nexport const EFFORT_RANK = {\n off: 0,\n minimal: 1,\n low: 2,\n medium: 3,\n high: 4,\n xhigh: 5,\n max: 6,\n}\n\n/** The strongest thinking level a model offers, or undefined for none. */\nexport function maxEffortOf(reasoning: ModelReasoning): string | undefined {\n let best\n for (const effort of reasoning.efforts) {\n const rank = EFFORT_RANK[effort.id as keyof typeof EFFORT_RANK] ?? 0\n if (best === void 0 || rank > best.rank) best = {\n id: effort.id,\n rank,\n }\n }\n return best?.id\n}\n\nexport function dmsEffortIndex(levels: readonly EffortLevel[], id: string | undefined): number {\n return levels.findIndex((level) => level.id === id)\n}\n\n/** 索引钳位:档位数为 0 时返回 0,四舍五入后落在 [0, count-1]。 */\nexport function dmsClampIndex(value: number, count: number): number {\n if (count <= 0) return 0\n return Math.max(0, Math.min(count - 1, Math.round(value)))\n}\n\n/**\n * 指针水平位置 → 档位原始值(未四舍五入,拖动中途可落在两档之间;落点由\n * 提交方 clamp)。输入条退化(宽度 ≤ 0 或档位数 < 2)时保持当前值不动。\n */\nexport function dmsPointerRaw(clientX: number, left: number, width: number, levelCount: number, current: number): number {\n if (width <= 0 || levelCount < 2) return current\n return Math.max(0, Math.min(levelCount - 1, (clientX - left) / width * (levelCount - 1)))\n}\n\nexport function dmsCurrentModel(state: DirectoryState): ModelEntry | undefined {\n if (state.current === null) return void 0\n const current = state.current\n const group = state.groups.find((g) => g.id === current.provider)\n const model = group?.models.find((m) => m.id === current.model)\n return model ?? void 0\n}\n\n/** 当前生效档位:用户已选优先,其次模型默认,最后取中间档。 */\nexport function dmsEffectiveEffortIndex(levels: readonly EffortLevel[], state: DirectoryState): number {\n const reasoning = dmsCurrentModel(state)?.reasoning\n const current = dmsEffortIndex(levels, state.current?.reasoningEffort)\n if (current >= 0) return current\n const fallback = dmsEffortIndex(levels, reasoning?.defaultEffort)\n if (fallback >= 0) return fallback\n return Math.floor((levels.length - 1) / 2)\n}\n\n/** 滑块档位:少于两档(无法构成滑杆)时返回空数组。 */\nexport function dmsSliderLevels(state: DirectoryState): readonly EffortLevel[] {\n const efforts = dmsCurrentModel(state)?.reasoning?.efforts\n return efforts !== void 0 && efforts.length >= 2 ? efforts : []\n}\n\n/**\n * 滑杆是否处于忙态:自身提交中,或共享目录上有任一 select 在途\n * (目录 select 是 last-writer-wins,模型切换在途时再发 effort RPC 会与\n * 它交错——effort 打到旧模型上,与模型切换互相覆盖;choose() 按同一状态\n * 拒绝了模型点击,滑杆必须同规则拒绝交互)。\n */\nexport function dmsEffortBusy(committing: boolean, status: DirectoryState['status']): boolean {\n return committing || status === 'selecting'\n}\n\n/**\n * 超时回滚后 select 迟到成功是否应采纳并同步 UI:\n * 只有当自回滚以来没有任何新的提交改写 committedRef(仍等于回滚前的档位)、\n * 提交纪元未推进(epoch === epochAtCommit,即期间没有发起过新提交——提交失败\n * 回滚后 committedRef 会回到与上一轮回滚相同的值,值比较无法区分「无新提交」\n * 与「新提交失败回滚」,纪元才能区分)、且当前无拖动/提交在途时,迟到结果才\n * 仍对应当前唯一的意图链——此时 UI 处于「回滚但后端已生效」的错位态,应补\n * 一次同步而不是保持回滚。否则以新操作链为准。\n */\nexport function dmsShouldAdoptLateSuccess(committed: string, previous: string, dragging: boolean, committing: boolean, epoch: number, epochAtCommit: number): boolean {\n return committed === previous && epoch === epochAtCommit && !dragging && !committing\n}\n\n/**\n * 拖动终止事件是否对应当前活动拖动:pointerActive 为假(拖动已结束/从未开始)\n * 或事件 pointerId 与活动指针不一致(迟到/重复事件——典型是 pointercancel 终态\n * 后平台补发的 pointerup)时返回 false,调用方应幂等跳过终止动作:不清状态、\n * 不触发提交/回滚回调。pointerId 缺省(blur 兜底提交,无事件对象)时只要求\n * 拖动仍在进行中。\n */\nexport function dmsIsActiveDrag(pointerActive: boolean, activePointerId: number | null, pointerId: number | undefined): boolean {\n if (!pointerActive) return false\n if (pointerId !== void 0 && activePointerId !== pointerId) return false\n return true\n}\n","/**\n * dsh-model-selector — EffortSlider 的 canvas 辐射特效模块。\n *\n * 从 ModelSelect.tsx 原样搬出(原 EffortSlider 组件内的 330 行状态机里,\n * 绘制与动画循环约占一半且与 React 状态解耦):\n * - dmsDrawRadiation:纯绘制函数,state(progress/dragging)与主题都由参数\n * 传入,不读组件闭包——同一帧可被 pointer 事件、rAF 循环、主题变化各路径复用。\n * - useEffortCanvas:canvas 生命周期(尺寸/DPR、ResizeObserver、主题\n * MutationObserver、visibilitychange、prefers-reduced-motion)与相位缓动\n * 动画循环。外部通过 radiationRef 更新 target/dragging 驱动重绘,hook\n * 返回的 redraw 注册到组件侧 ref(preview 变化时组件直接调它触发一帧)。\n */\nimport * as react from 'react'\n\n/** 绘制状态的最小契约:进度(0..1)+ 是否拖拽(拖拽时加速/改粒子参数)。 */\nexport interface RadiationState {\n progress: number\n dragging: boolean\n /** 目标进度;undefined 表示尚未初始化(首帧同步起跳,不做缓动)。 */\n target?: number\n}\n\n/** 拖动时辐射推挤的缓动系数(越小越跟手),非拖拽时回弹系数。 */\nconst DRAG_EASE = 0.55\nconst IDLE_EASE = 0.12\nconst SETTLE_EPSILON = 0.002\n\n/**\n * 纯绘制:把滑块进度画成拖尾辐射(列能量 + 径向光晕 + 粒子流 + 热核光斑)。\n * 全部可变量(尺寸、相位、进度、拖拽态、深浅色)都来自参数——无模块级状态。\n */\nexport function dmsDrawRadiation(context: CanvasRenderingContext2D, width: number, height: number, time: number, state: RadiationState, isDark: boolean): void {\n const origin = state.progress * width;\n const cell = 4;\n const speed = state.dragging ? 2.8 : 1;\n context.clearRect(0, 0, width, height);\n if (origin <= 0) return;\n context.save();\n context.beginPath();\n context.rect(0, 0, origin, height);\n context.clip();\n for (let x = 0; x < origin; x += cell) {\n const delta = x + cell * 0.5 - origin;\n const distance = Math.abs(delta);\n const phaseA = distance / 10 - time * 74e-4 * speed;\n const phaseB = distance / 23 - time * 41e-4 * speed + 1.7;\n const phaseC = distance / 40 - time * 22e-4 * speed + 3.4;\n const sinA = Math.max(0, Math.sin(phaseA));\n const sinB = Math.max(0, Math.sin(phaseB));\n const sinC = Math.max(0, Math.sin(phaseC));\n const waveA = Math.pow(sinA, 2.6);\n const waveB = Math.pow(sinB, 3.2);\n const waveC = Math.pow(sinC, 4);\n const crest = Math.pow(sinA, 15) + Math.pow(sinB, 18) * 0.78;\n const wave = Math.min(1, waveA * 0.76 + waveB * 0.58 + waveC * 0.32);\n const trail = 0.38 + 0.62 * Math.exp(-distance / 90);\n const pillar = Math.pow(Math.max(0, Math.sin(x / 20 + time * 16e-4)), 3) * 0.27;\n const columnEnergy = trail * (wave * 1.04 + pillar + crest * 0.32);\n if (columnEnergy > 0.012) {\n const nearness = Math.max(0, 1 - distance / 140);\n const red = isDark ? Math.round(42 + 124 * nearness + 75 * wave) : Math.round(28 + 58 * nearness + 15 * wave);\n const green = isDark ? Math.round(56 + 58 * nearness + 44 * crest) : Math.round(88 + 72 * nearness + 30 * crest);\n const blue = isDark ? Math.round(175 + 72 * nearness + 8 * wave) : Math.round(182 + 62 * nearness);\n const alpha = isDark ? Math.min(0.88, columnEnergy * 0.72) : Math.min(0.62, columnEnergy * 0.54);\n context.fillStyle = `rgba(${red}, ${green}, ${blue}, ${alpha})`;\n context.fillRect(x, 0, cell - 1, height);\n }\n for (let y = 0; y < height; y += cell) {\n const deltaY = y + cell * 0.5 - height * 0.5;\n const radial = Math.hypot(delta / 38, deltaY / 11);\n const halo = Math.exp(-radial * 0.96) * 1.08;\n const verticalShape = 0.58 + 0.42 * Math.cos(deltaY / height * Math.PI);\n const grain = 0.72 + 0.28 * Math.sin(x * 0.73 + y * 1.31 + time * 6e-3);\n const alpha = Math.min(0.96, (columnEnergy * 0.88 + halo + crest * 0.19) * verticalShape * grain);\n if (alpha < 0.035) continue;\n const hot = Math.max(0, 1 - radial / 2.4);\n const red = isDark ? Math.round(54 + 148 * hot + 42 * wave + 35 * crest) : Math.round(25 + 72 * hot + 12 * wave);\n const green = isDark ? Math.round(68 + 78 * hot + 46 * crest) : Math.round(98 + 72 * hot + 24 * crest);\n const blue = isDark ? Math.round(186 + 64 * hot) : Math.round(194 + 56 * hot);\n context.fillStyle = `rgba(${red}, ${green}, ${blue}, ${isDark ? alpha : alpha * 0.72})`;\n context.fillRect(x, y, cell - 1, cell - 1);\n }\n }\n for (let i = 0; i < 14; i += 1) {\n const travel = (time * (state.dragging ? 0.16 : 0.065) * (0.78 + i % 5 * 0.09) + i * 23) % Math.max(30, origin + 64);\n const particleX = origin - travel;\n if (particleX < -24 || particleX > width + 16) continue;\n const particleY = 3 + (i * 13 + Math.sin(time * 3e-3 + i) * 5) % Math.max(7, height - 6);\n const length = 4 + i % 4 * 4 + (state.dragging ? 6 : 0);\n const alpha = 0.28 + i % 5 * 0.1;\n const streak = context.createLinearGradient(particleX, 0, particleX + length, 0);\n streak.addColorStop(0, isDark ? \"rgba(72,118,255,0)\" : \"rgba(24,94,184,0)\");\n streak.addColorStop(0.68, isDark ? `rgba(112,135,255,${alpha})` : `rgba(36,108,202,${alpha * 0.72})`);\n streak.addColorStop(1, isDark ? `rgba(236,222,255,${Math.min(1, alpha + 0.26)})` : `rgba(103,175,248,${Math.min(0.82, alpha + 0.18)})`);\n context.fillStyle = streak;\n context.fillRect(particleX, particleY, length, i % 3 === 0 ? 2 : 1);\n }\n const glow = context.createRadialGradient(origin, height / 2, 0, origin, height / 2, 24);\n glow.addColorStop(0, isDark ? \"rgba(255,255,255,.82)\" : \"rgba(255,255,255,.86)\");\n glow.addColorStop(0.14, isDark ? \"rgba(183,190,255,.54)\" : \"rgba(162,210,255,.48)\");\n glow.addColorStop(0.44, isDark ? \"rgba(103,74,255,.28)\" : \"rgba(37,112,207,.22)\");\n glow.addColorStop(1, isDark ? \"rgba(86,31,210,0)\" : \"rgba(25,91,181,0)\");\n context.fillStyle = glow;\n context.fillRect(origin - 26, 0, 52, height);\n context.restore();\n}\n\n/**\n * 辐射画布生命周期 hook:尺寸/DPR 自适应、主题变化监听、动画循环与相位时钟。\n *\n * 外部契约:\n * - radiationRef.current.target / .dragging 由组件在 preview/忙态变化时更新,\n * hook 的动画循环按 target 做指数缓动(拖动中每帧逼近 target,回弹时渐近);\n * - 返回的 redraw 应注册到组件侧 ref,供事件驱动的重绘(拖动一帧、主题翻转)\n * 即时调用;挂载时画首帧并把 progress 直接对齐 target(打开菜单不闪中间态)。\n *\n * 动画成本:拖拽时只画事件驱动帧(pointer 移动触发的单帧),循环空闲时\n * 一帧即停——画布不常驻 rAF,CPU 占用只在缓动回弹窗口内存在。\n */\nexport function useEffortCanvas(canvasRef: react.RefObject<HTMLCanvasElement | null>, radiationRef: react.MutableRefObject<RadiationState>): () => void {\n const redrawRef = react.useRef<(() => void) | null>(null);\n react.useEffect(() => {\n const canvas = canvasRef.current;\n if (canvas === null) return;\n const context = canvas.getContext(\"2d\");\n if (context === null) return;\n const reducedMotion = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n let width = 1;\n let height = 1;\n let frame = 0;\n // Phase clock advances only while the loop runs, so a frozen (idle)\n // effect resumes at the exact wave phase it paused at — no jump.\n let phase = 0;\n let lastFrameAt = 0;\n const resize = () => {\n const bounds = canvas.getBoundingClientRect();\n const ratio = Math.min(window.devicePixelRatio || 1, 2);\n width = Math.max(1, bounds.width);\n height = Math.max(1, bounds.height);\n canvas.width = Math.max(1, Math.round(width * ratio));\n canvas.height = Math.max(1, Math.round(height * ratio));\n context.setTransform(ratio, 0, 0, ratio, 0, 0);\n };\n // Draw one frame at the current phase; returns true while the effect\n // should keep animating (still easing toward its target).\n // Idle = one static frame, zero further CPU — the loop only runs while\n // the glow is settling, instead of forever.\n // While dragging, stop after a single frame: every pointer move bumps\n // `preview` and redraw() draws one event-driven frame, so the glow\n // follows the knob without a back-to-back repaint stream. Continuously\n // repainting this mix-blend canvas is what flickered the whole menu.\n const draw = (): boolean => {\n const r = radiationRef.current;\n if (r.target !== void 0) {\n const k = r.dragging ? DRAG_EASE : IDLE_EASE;\n r.progress += (r.target - r.progress) * k;\n if (Math.abs(r.target - r.progress) < SETTLE_EPSILON) r.progress = r.target;\n }\n // 主题在这里(每次 draw 时)读取一次:dmsDrawRadiation 保持参数输入、\n // 无 DOM 依赖可测;主题翻转由 themeObserver → draw() 即时取到新值。\n const isDark = document.body.hasAttribute(\"data-ds-dark-theme\");\n dmsDrawRadiation(context, width, height, phase, r, isDark);\n if (r.dragging) return false;\n return r.target !== void 0 && Math.abs(r.target - r.progress) > SETTLE_EPSILON;\n };\n const loop = (time: number): void => {\n if (lastFrameAt !== 0) phase += time - lastFrameAt;\n lastFrameAt = time;\n if (draw() && !document.hidden) {\n frame = window.requestAnimationFrame(loop);\n } else {\n frame = 0;\n lastFrameAt = 0;\n }\n };\n const redraw = () => {\n if (reducedMotion.matches) {\n draw();\n return;\n }\n // Restart the loop on demand (pointer/theme/preview changes);\n // a running loop already draws every frame, so no extra draw here.\n if (frame === 0 && !document.hidden) {\n lastFrameAt = 0;\n frame = window.requestAnimationFrame(loop);\n }\n };\n const resizeObserver = new ResizeObserver(() => {\n resize();\n draw();\n });\n const themeObserver = new MutationObserver(() => draw());\n const onVisibility = () => {\n if (!document.hidden) redraw();\n };\n // 跨屏(窗口拖到另一台显示器)时 devicePixelRatio 变化:canvas 的 CSS\n // 尺寸不变,ResizeObserver 不会触发,位图分辨率停留在旧 DPR → 模糊。\n // 按当前 DPR 构造 resolution 媒体查询,DPR 一变旧查询即失配 → change。\n const resolutionQuery = window.matchMedia(`(resolution: ${window.devicePixelRatio || 1}dppx)`);\n const onResolutionChange = () => {\n resize();\n draw();\n };\n if (resolutionQuery.addEventListener !== void 0) {\n resolutionQuery.addEventListener(\"change\", onResolutionChange);\n } else {\n resolutionQuery.addListener(onResolutionChange); // 旧版 WebKit\n }\n resizeObserver.observe(canvas);\n themeObserver.observe(document.body, { attributes: true, attributeFilter: [\"data-ds-dark-theme\"] });\n document.addEventListener(\"visibilitychange\", onVisibility);\n redrawRef.current = redraw;\n resize();\n // 首帧直接把进度对齐 target(若已初始化):菜单打开瞬间不展示中间进度。\n const r = radiationRef.current;\n if (r.target !== void 0) r.progress = r.target;\n draw();\n if (!reducedMotion.matches && !document.hidden) frame = window.requestAnimationFrame(loop);\n return () => {\n window.cancelAnimationFrame(frame);\n resizeObserver.disconnect();\n themeObserver.disconnect();\n if (resolutionQuery.removeEventListener !== void 0) {\n resolutionQuery.removeEventListener(\"change\", onResolutionChange);\n } else {\n resolutionQuery.removeListener(onResolutionChange);\n }\n document.removeEventListener(\"visibilitychange\", onVisibility);\n redrawRef.current = null;\n };\n }, [canvasRef, radiationRef]);\n return react.useCallback(() => redrawRef.current?.(), []);\n}\n","/**\n * dsh-model-selector — EffortSlider 的指针拖动状态机。\n *\n * 从 ModelSelect.tsx 原样搬出(原 EffortSlider 的 8 个 ref 指针生命周期):\n * 拖动只认发起时记录的活动 pointerId,window 捕获阶段的 move/up/cancel 兜底\n * 指针拖出输入条/菜单外的场景;取消(pointercancel / 触控失控)走回滚回调,\n * 不提交。几何换算(clientX → 档位原始值)与忙态判定都经由回调外接,\n * hook 本身不读组件状态,拖动中途组件重渲染(preview setState)不会中断拖动。\n */\nimport * as react from 'react'\nimport { dmsIsActiveDrag, dmsPointerRaw } from './effort.ts'\n\nexport interface EffortDragHandlers {\n onPointerDown: (event: react.PointerEvent<HTMLInputElement>) => void\n onPointerMove: (event: react.PointerEvent<HTMLInputElement>) => void\n onPointerUp: (event: react.PointerEvent<HTMLInputElement>) => void\n onPointerCancel: (event: react.PointerEvent<HTMLInputElement>) => void\n onBlur: () => void\n}\n\nexport interface UseEffortDragOptions {\n /** 当前档位数(levels.length),拖动态期间变化时以最新值为准。 */\n levelCount: () => number\n /** 忙态(提交中/目录 select 在途)时拒绝新开拖动。 */\n canStart: () => boolean\n /** 拖动中预览(组件侧乐观 setState);返回值供组件更新其当前 raw ref。 */\n onPreview: (raw: number) => void\n /** 拖动结束(pointerup / blur 兜底)时提交;拖出后归位也会提交同一档。 */\n onCommit: (raw: number) => void\n /** 取消(pointercancel / 触控失控)时回滚到已提交档,不提交。 */\n onRollback: () => void\n inputRef: react.RefObject<HTMLInputElement | null>\n /** 拖动活状态(true = 指针按下未结束)。组件与 hook 共享同一 ref:\n * 组件侧用它守卫「拖动态不被打断」(commit 迟到采纳判定、目录同步 effect)。 */\n draggingRef: react.MutableRefObject<boolean>\n}\n\n/**\n * 拖动中由 pointerId 校验的几何换算:raw = (clientX - left) / width 映射到档位区间。\n * 输入条退化(宽度 ≤ 0 / 档位数 < 2)时返回 fallback(当前值,不跳档)。\n */\nfunction rawFromPointer(input: HTMLInputElement, clientX: number, levelCount: number, fallback: number): number {\n const bounds = input.getBoundingClientRect();\n return dmsPointerRaw(clientX, bounds.left, bounds.width, levelCount, fallback);\n}\n\nexport function useEffortDrag({ levelCount, canStart, onPreview, onCommit, onRollback, inputRef, draggingRef }: UseEffortDragOptions) {\n const [dragging, setDragging] = react.useState(false);\n const pointerActiveRef = react.useRef(false);\n const activePointerIdRef = react.useRef<number | null>(null);\n // 拖动中最近一次预览的 raw:blur 兜底提交(无 clientX)时用它。\n const lastRawRef = react.useRef(0);\n // 回调经 ref 转发,hook 内部的事件处理器始终持有最新闭包(组件重渲染不重建监听)。\n const callbacksRef = react.useRef({ levelCount, canStart, onPreview, onCommit, onRollback });\n callbacksRef.current = { levelCount, canStart, onPreview, onCommit, onRollback };\n\n const showPointerPreview = react.useCallback((input: HTMLInputElement, clientX: number): void => {\n const raw = rawFromPointer(input, clientX, callbacksRef.current.levelCount(), lastRawRef.current);\n lastRawRef.current = raw;\n callbacksRef.current.onPreview(raw);\n }, []);\n\n const beginDragging = react.useCallback((input: HTMLInputElement, pointerId: number, clientX: number): void => {\n pointerActiveRef.current = true;\n activePointerIdRef.current = pointerId;\n draggingRef.current = true;\n setDragging(true);\n showPointerPreview(input, clientX);\n try {\n if (!input.hasPointerCapture(pointerId)) input.setPointerCapture(pointerId);\n } catch (cause) {\n // 预期内失败:setPointerCapture 在元素已卸载 / 指针已失效等场景会抛\n // NotFoundError/InvalidStateError。影响仅限「指针拖出输入条后由元素继续\n // 接收事件」这条优化路径——window 捕获阶段的 move/up 兜底监听仍然生效,\n // 拖动不会因此中断,无需恢复;留 debug 痕迹便于排查真实异常。\n console.debug(\"[dms-effort] setPointerCapture failed\", cause);\n }\n }, [showPointerPreview]);\n\n const moveDragging = react.useCallback((input: HTMLInputElement, pointerId: number, clientX: number): void => {\n if (!pointerActiveRef.current || activePointerIdRef.current !== pointerId) return;\n showPointerPreview(input, clientX);\n }, [showPointerPreview]);\n\n const stopDragging = react.useCallback((input: HTMLInputElement, pointerId?: number, clientX?: number): void => {\n if (!dmsIsActiveDrag(pointerActiveRef.current, activePointerIdRef.current, pointerId)) return;\n const raw = clientX === void 0 ? lastRawRef.current : rawFromPointer(input, clientX, callbacksRef.current.levelCount(), lastRawRef.current);\n lastRawRef.current = raw;\n pointerActiveRef.current = false;\n activePointerIdRef.current = null;\n draggingRef.current = false;\n setDragging(false);\n if (pointerId !== void 0 && input.hasPointerCapture(pointerId)) {\n input.releasePointerCapture(pointerId);\n }\n callbacksRef.current.onCommit(raw);\n }, []);\n\n // 取消路径的公共清理:pointercancel 是终态事件(规范上不再有该指针的后续事件),\n // 但部分平台/浏览器在 cancel 后仍会补发 pointerup——不清干净会把「取消」误当成\n // 正常提交。此函数经 dmsIsActiveDrag 校验活动指针、清空全部拖动状态、回滚不提交,\n // 幂等(第二次调用因指针已清空而直接返回)。blur 兜底共用 stopDragging 的同一\n // 校验,双触发路径不会产生重复 commit。\n const cancelDragging = (pointerId: number): void => {\n if (!dmsIsActiveDrag(pointerActiveRef.current, activePointerIdRef.current, pointerId)) return;\n pointerActiveRef.current = false;\n activePointerIdRef.current = null;\n draggingRef.current = false;\n setDragging(false);\n callbacksRef.current.onRollback();\n };\n\n // 全局兜底监听:拖动中指针移出输入条/菜单仍留在窗口内时,move/up 由 window 捕获。\n // 只在拖动进行中挂载(pointerdown 命中 → dragging=true),up/cancel/blur 结束\n // 拖动(dragging=false)即卸载——菜单开着但不拖动时窗口上不挂任何监听。pointercancel\n // 是终态,平台随后补发的 pointerup 要么因监听已卸载而收不到、要么走 stopDragging\n // 的 dmsIsActiveDrag 守卫幂等跳过,与常驻挂载时语义一致。挂载缝隙(pointerdown\n // 到 effect 落地之间)内指针仍在输入条上,由输入条自身的 JSX 事件兜住,无事件丢失。\n const globalMoveRef = react.useRef<(event: PointerEvent) => void>(() => {});\n const globalEndRef = react.useRef<(event: PointerEvent) => void>(() => {});\n const globalCancelRef = react.useRef<(event: PointerEvent) => void>(() => {});\n globalMoveRef.current = (event) => {\n const input = inputRef.current;\n if (input !== null) moveDragging(input, event.pointerId, event.clientX);\n };\n globalEndRef.current = (event) => {\n const input = inputRef.current;\n if (input !== null) stopDragging(input, event.pointerId, event.clientX);\n };\n globalCancelRef.current = (event: PointerEvent): void => cancelDragging(event.pointerId);\n react.useEffect(() => {\n if (!dragging) return;\n const move = (event: PointerEvent) => globalMoveRef.current(event);\n const end = (event: PointerEvent) => globalEndRef.current(event);\n const cancel = (event: PointerEvent) => globalCancelRef.current(event);\n window.addEventListener(\"pointermove\", move, true);\n window.addEventListener(\"pointerup\", end, true);\n window.addEventListener(\"pointercancel\", cancel, true);\n return () => {\n window.removeEventListener(\"pointermove\", move, true);\n window.removeEventListener(\"pointerup\", end, true);\n window.removeEventListener(\"pointercancel\", cancel, true);\n };\n }, [dragging]);\n\n const handlers: EffortDragHandlers = {\n onPointerDown: (event: react.PointerEvent<HTMLInputElement>) => {\n // 忙态(模型切换/提交在途)不开新拖拽:目录 select 是\n // last-writer-wins,交错的 effort RPC 会打到旧模型上。\n if (!callbacksRef.current.canStart()) return;\n event.preventDefault();\n event.currentTarget.focus();\n beginDragging(event.currentTarget, event.pointerId, event.clientX);\n },\n onPointerMove: (event: react.PointerEvent<HTMLInputElement>) => moveDragging(event.currentTarget, event.pointerId, event.clientX),\n onPointerUp: (event: react.PointerEvent<HTMLInputElement>) => stopDragging(event.currentTarget, event.pointerId, event.clientX),\n onPointerCancel: (event: react.PointerEvent<HTMLInputElement>) => {\n if (event.currentTarget.hasPointerCapture(event.pointerId)) {\n event.currentTarget.releasePointerCapture(event.pointerId);\n }\n cancelDragging(event.pointerId);\n },\n onBlur: () => {\n const input = inputRef.current;\n if (input !== null) stopDragging(input);\n },\n };\n\n return {\n dragging,\n draggingRef,\n handlers,\n };\n}\n","/**\n * dsh-model-selector — 菜单弹出方向与高度钳位的纯函数。\n *\n * 从 ModelSelect.tsx 原样搬出(无 JSX、无 DOM、无模块级副作用),以便用\n * node --test 直接跑源码:方向决策(向上 vs 向下弹)和向下弹的高度钳位都是\n * 用户可见且曾在矮窗口/滚动场景出过 bug 的逻辑,留在 .tsx 里则无法脱离\n * 浏览器测试。\n */\n\n/** 菜单设计最大高度(px);实际由 useAnchoredMaxHeight 按视口可用空间钳位。\n * 调法:调大允许更高菜单(矮视口下仍会被钳到视口内),调小菜单更矮。 */\nexport const MENU_MAX_HEIGHT = 420;\n/** 与视口边缘保留的距离;对齐 primitives 里未导出的 MARGIN。\n * 改动同时影响向上/向下弹与水平钳位的边距,需与官方 MARGIN 意图保持一致。 */\nexport const MENU_VIEWPORT_MARGIN = 12;\n\n/**\n * 向上弹当且仅当 trigger 上方可用空间不小于下方可用空间:\n * 取上下两侧中空间更大的一侧,避免矮窗口把面板挤到视口下沿外面。\n */\nexport function dmsMenuAbove(triggerTop: number, triggerBottom: number, viewportHeight: number): boolean {\n return triggerTop >= viewportHeight - triggerBottom;\n}\n\n/** 向下弹时按 trigger 下方空间钳位菜单高度(top-anchored,不能复用向上弹的 fit)。 */\nexport function dmsBelowMaxHeight(triggerBottom: number, viewportHeight: number, cap: number): number {\n return Math.max(0, Math.min(cap, viewportHeight - triggerBottom - MENU_VIEWPORT_MARGIN));\n}\n\n/**\n * 菜单水平钳位:默认右锚定(right:0,与 seat 右缘对齐);seat 右缘左侧放不下\n * 整幅菜单(seat 靠输入区左下 + 窄窗口)、或 seat 右缘本身已越出视口右缘\n * (页面横向溢出/缩放时 rect.right 可大于 innerWidth,右锚定会把整幅菜单\n * 拖出屏外)时,返回钳到视口内的 left 值,调用方改用 left 锚定。\n * 返回 undefined 表示保持默认右锚定。\n * menuWidth 传菜单实际渲染宽度(offsetWidth),与 CSS `min(280px, 100vw-32px)` 解耦。\n */\nexport function dmsMenuLeft(rootRight: number, menuWidth: number, viewportWidth: number, margin: number): number | undefined {\n if (rootRight - menuWidth >= margin && rootRight <= viewportWidth - margin) return void 0;\n return Math.min(\n Math.max(margin, rootRight - menuWidth),\n Math.max(margin, viewportWidth - margin - menuWidth),\n );\n}\n","/**\n * dsh-model-selector — 菜单 roving tabindex 的纯决策逻辑(node 可测)。\n *\n * 模型列表可达数百行,若每行都是 Tab 停靠点,Tab 键会陷入行海;roving\n * tabindex(WAI-ARIA menu 模式的通行做法)只让「活动行」留在 Tab 序里,\n * 其余行 tabindex=-1、仅方向键可达。本模块只负责一件事:给定当前渲染的\n * 行集合与选中行,算出 Tab 从搜索框进入列表时应停靠的默认行键。\n */\nexport interface RovingRowSet {\n /** 搜索命中行键(渲染顺序);null = 非搜索态(分组视图)。 */\n hitKeys: readonly string[] | null\n /** 分组视图中可见(未折叠)的模型行键(渲染顺序)。 */\n modelKeys: readonly string[]\n /** 分组视图中全部组头键(组头始终渲染,即使组被折叠)。 */\n headerKeys: readonly string[]\n /** 当前选中行键;null = 尚无选中。 */\n selectedKey: string | null\n}\n\n/**\n * Tab 默认落点:搜索态取「选中行若在命中集内,否则首个命中」;分组态取\n * 「选中行若可见,否则首个可见模型行」;全部折叠(无可见模型行)时回退到\n * 首个组头。返回 null 表示当前没有任何可聚焦行(空态由菜单外的 role=status\n * 播报,列表容器不渲染)。\n */\nexport function dmsDefaultRowKey(set: RovingRowSet): string | null {\n if (set.hitKeys !== null) {\n if (set.hitKeys.length === 0) return null\n return set.selectedKey !== null && set.hitKeys.includes(set.selectedKey) ? set.selectedKey : set.hitKeys[0]!\n }\n if (set.modelKeys.length > 0) {\n return set.selectedKey !== null && set.modelKeys.includes(set.selectedKey) ? set.selectedKey : set.modelKeys[0]!\n }\n return set.headerKeys[0] ?? null\n}\n","/**\n * ModelSelect: the enhanced composer model seat (`conversation.input.model`).\n *\n * A single-pane menu (simpler than the shipped two-level root menu): opening\n * the seat lands directly on the model list — search box + collapsible\n * provider groups — with the current model's effort slider pinned inline at\n * the bottom of the menu, so switching models and tuning reasoning effort\n * both happen in one surface without an intermediate page.\n *\n * Data and submission ride the SAME per-session directory the /model popup\n * shares (via `modelDirectories`), so a switch made here is what the popup\n * shows next and vice versa. Icons, Toast and the menu fit/dismiss hooks are\n * reused from @deepseek-ai/dsh-client-ui-primitives at runtime; colors\n * come from `--dsw-*` tokens in the injected stylesheet.\n */\nimport * as react from 'react'\nimport { zh as zhDict, en as enDict } from './locales.ts'\n// Runtime reuse of the official primitives (already an external in\n// tsdown.config.ts and a platform module in the web loader): Toast is the\n// sanctioned surface for a rejected selection, and the two hooks fit/dismiss\n// the open menu. The list itself stays hand-rolled — primitives' Menu renders\n// every entry (footer included) as a <button role=\"menuitem\">, which cannot\n// host the effort range input or the search field.\nimport { Toast, IconWarningOutline16, IconChevronDownOutline14, IconCheckOutline14, IconCloseFill14, useAnchoredMaxHeight, useDismissOnOutsidePointer } from '@deepseek-ai/dsh-client-ui-primitives'\n\nexport { zhDict, enDict }\n// Type-only: official model-selection directory types (the enhanced seat's\n// data contract — same shared per-session directory the /model popup reads).\nimport type { ModelDirectoryState } from '@deepseek-ai/dsh-client-ui-model-selection/client'\nimport type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { SnapshotStore } from '@deepseek-ai/dsh-client-store'\n// Effort helpers live in effort.ts (pure, no JSX/DOM) so node --test can cover them.\nimport { dmsClampIndex, dmsEffortIndex, dmsEffectiveEffortIndex, dmsEffortBusy, dmsShouldAdoptLateSuccess, dmsSliderLevels, maxEffortOf } from './effort.ts'\n// 辐射画布(绘制纯函数 + 动画循环 hook)与指针拖动状态机:从 EffortSlider\n// 组件内拆分独立成模块,组件本体只保留档位/提交/渲染三件事。\nimport { useEffortCanvas } from './effortCanvas.ts'\nimport { useEffortDrag } from './effortDrag.ts'\n// Menu direction/clamp helpers likewise (pure — the direction flip and the\n// below-clamp are user-visible and were previously only browser-testable).\nimport { MENU_MAX_HEIGHT, MENU_VIEWPORT_MARGIN, dmsMenuAbove, dmsBelowMaxHeight, dmsMenuLeft } from './menuFit.ts'\n// roving tabindex 的默认落点决策(纯函数,node 可测——行键集合与选中行的\n// 组合分支不值得只在浏览器里验证)。\nimport { dmsDefaultRowKey } from './roving.ts'\n// Type-only: the model catalog carrier types (moved here in dsh alpha.2).\nimport type { ModelSelection, ModelProviderGroup } from '@deepseek-ai/dsh-api-session-controller/types'\n\n/** Per-session model directory snapshot (official state shape). */\ntype DirectoryState = ModelDirectoryState\n/** The enhanced seat's injected business face. */\n/** 搜索索引条目:模型 + 其搜索 haystack + 选中载荷。 */\ntype ModelChoice = {\n\tgroup: ModelProviderGroup\n\tmodel: ModelProviderGroup[\"models\"][number]\n\thaystack: string\n\tselection: ModelSelection\n}\n\n/** 搜索命中的渲染条目:只装渲染窗口内的命中(窗口外只计数,见 hits memo)。 */\ntype SearchHit = {\n\tgroup: ModelProviderGroup\n\tmodel: ModelProviderGroup[\"models\"][number]\n\tnameHit: { start: number; end: number } | null\n}\n\n/** 搜索命中结果:items 为窗口内条目(渲染用),total 为命中总数(播报/截断提示用)。 */\ntype SearchResult = { items: SearchHit[]; total: number }\n\ninterface EffortSliderProps {\n state: DirectoryState\n select: (selection: ModelSelection) => Promise<boolean>\n t: TranslateNS<'modelSelector'>\n}\n\n\n// 图标直接用 primitives 的组件(此前本仓自带一份逐字节相同的 SVG 副本);\n// 图标组件只接受 size/className,aria-hidden 由调用处的包装元素承载。\nconst IconChevronDown = <IconChevronDownOutline14 />;\nconst IconCheck = <IconCheckOutline14 />;\nconst IconClear = <IconCloseFill14 />;\n/**\n* How long a successfully loaded directory snapshot is trusted before the\n* menu re-fetches it over RPC. The snapshot lives in the per-session store,\n* so reopening the menu within this window costs zero RPC and zero re-render.\n* 调法:调大 → 打开更省(RPC/重渲染都免),但目录侧改动要等窗口过期才可见;\n* 调小 → 目录更新更实时。目录内容由官方 modelDirectories 推送,本值只影响\n* 打开瞬间是否信任快照。\n*/\nconst DIRECTORY_STALE_MS = 3e4;\n/** 搜索命中渲染上限:宽泛关键词(如单字母)命中数百条时避免 DOM 爆炸。\n * 调法:调大 → 宽泛搜索看到更多命中(渲染与搜索成本线性上升);调小 → 更省。 */\nconst MAX_VISIBLE_HITS = 100;\n/** effort select RPC 的超时护栏:官方 select 无超时契约,RPC 永久挂起时\n * 必须释放滑杆的 committing 锁并回滚,否则滑杆被锁死到菜单关闭。\n * 调法:后端/网络慢时调大避免误回滚(滑杆锁死时间随之变长);调小更早释放交互。 */\nconst EFFORT_COMMIT_TIMEOUT_MS = 12e3;\n// ── 推理强度滑块(移植自 dsh-reasoning-effort:辐射特效 + 档位随模型自动适配)──\n// 绘制与动画循环见 ./effortCanvas.ts(dmsDrawRadiation / useEffortCanvas),\n// 指针拖动状态机见 ./effortDrag.ts(useEffortDrag);本组件只做档位状态、\n// 提交/回滚与渲染。\nexport const EffortSlider = react.memo(function EffortSlider({ state, select, t }: EffortSliderProps) {\n\tconst levels = dmsSliderLevels(state);\n\t// 惰性初始化到当前生效档:useState(\"\")/0 会让菜单打开的第一帧档位名为空、\n\t// 进度闪 0%,随后才被同步 effect 纠正(首帧即正确,无闪烁)。\n\tconst [effort, setEffort] = react.useState(() => levels[dmsEffectiveEffortIndex(levels, state)]?.id ?? \"\");\n\tconst [preview, setPreview] = react.useState(() => dmsEffectiveEffortIndex(levels, state));\n\tconst [committing, setCommitting] = react.useState(false);\n\tconst [localError, setLocalError] = react.useState<string | null>(null);\n\tconst canvasRef = react.useRef<HTMLCanvasElement | null>(null);\n\tconst inputRef = react.useRef<HTMLInputElement | null>(null);\n\tconst committedRef = react.useRef(\"\");\n\tconst committingRef = react.useRef(false);\n\tconst previewRef = react.useRef(0);\n\tconst draggingRef = react.useRef(false);\n\t// 提交超时定时器句柄 + 挂载标志:菜单关闭(EffortSlider 卸载)时由 cleanup\n\t// clearTimeout 并置 unmounted——迟到成功采纳/超时回滚的 setState 只在挂载期\n\t// 跑,卸载后不再触碰 React 状态,定时器也不必在卸载后继续空转。\n\tconst commitTimerRef = react.useRef<number | null>(null);\n\tconst mountedRef = react.useRef(false);\n\t// 提交纪元:单调递增,迟到采纳判据靠它区分「无新提交」与「新提交失败回滚」。\n\tconst commitEpochRef = react.useRef(0);\n\t// 辐射状态由组件持有:preview 变化/拖动时更新 target/dragging 驱动重绘;\n\t// progress 惰性对齐当前档位(打开菜单首帧即正确,无中间态缓动)。useRef\n\t// 初始化表达式每次渲染都会求值但仅首帧生效,这里取首次渲染的档位作起点。\n\tconst radiationRef = react.useRef<{ progress: number; dragging: boolean; target?: number }>({\n\t\tprogress: levels.length >= 2 ? dmsEffectiveEffortIndex(levels, state) / (levels.length - 1) : 0.5,\n\t\tdragging: false,\n\t});\n\tconst redrawRef = react.useRef<(() => void) | null>(null);\n\tconst available = state.current !== null && levels.length >= 2;\n\tconst busy = dmsEffortBusy(committing, state.status);\n\tconst error = localError;\n\treact.useEffect(() => {\n\t\tif (!available || committingRef.current || draggingRef.current) return;\n\t\tconst index = dmsEffectiveEffortIndex(levels, state);\n\t\tconst next = levels[index]?.id ?? \"\";\n\t\tcommittedRef.current = next;\n\t\tpreviewRef.current = index;\n\t\tsetEffort(next);\n\t\tsetPreview(index);\n\t\tsetLocalError(null);\n\t}, [available, levels.length, state.current?.provider, state.current?.model, state.current?.reasoningEffort]);\n\treact.useEffect(() => {\n\t\tpreviewRef.current = preview;\n\t\tradiationRef.current.target = levels.length >= 2 ? preview / (levels.length - 1) : 0.5;\n\t\tredrawRef.current?.();\n\t}, [preview, levels.length]);\n\tconst redraw = useEffortCanvas(canvasRef, radiationRef);\n\treact.useEffect(() => {\n\t\tredrawRef.current = redraw;\n\t\treturn () => { redrawRef.current = null; };\n\t}, [redraw]);\n\t// 卸载清理:迟到的 select settle 不再 setState(迟到成功采纳/超时回滚的\n\t// setState 都经 mountedRef 守卫);未决的超时定时器直接清除——组件已卸载,\n\t// 回滚/采纳都无人可见,定时器没有存在的意义。\n\treact.useEffect(() => {\n\t\tmountedRef.current = true;\n\t\treturn () => {\n\t\t\tmountedRef.current = false;\n\t\t\tif (commitTimerRef.current !== null) {\n\t\t\t\twindow.clearTimeout(commitTimerRef.current);\n\t\t\t\tcommitTimerRef.current = null;\n\t\t\t}\n\t\t};\n\t}, []);\n\tconst showPointerPreview = react.useCallback((raw: number): void => {\n\t\tpreviewRef.current = raw;\n\t\tsetPreview(raw);\n\t\tsetEffort(levels[dmsClampIndex(raw, levels.length)]?.id ?? \"\");\n\t}, [levels]);\n\tconst rollback = react.useCallback(() => {\n\t\tconst previous = committedRef.current;\n\t\tpreviewRef.current = Math.max(0, dmsEffortIndex(levels, previous));\n\t\tsetEffort(previous);\n\t\tsetPreview(Math.max(0, dmsEffortIndex(levels, previous)));\n\t}, [levels]);\n\tconst commit = react.useCallback(async (raw: number): Promise<void> => {\n\t\tif (dmsEffortBusy(committingRef.current, state.status)) return;\n\t\t// 先按钳位后的档位判断是否无操作:落回已提交的同一档(含拖动归位、键盘\n\t\t// 重复按当前档)时,不发多余 RPC,也不闪本地 busy 灰——原实现把这次早退\n\t\t// 放在 setCommitting(true)/乐观 setState 之后,同一档提交仍会闪一次。\n\t\t// levels 非空时 clamp 保证 index ∈ [0, count-1],next 不可能为 undefined。\n\t\tconst index = dmsClampIndex(raw, levels.length);\n\t\tconst next = levels[index]?.id;\n\t\tif (next === void 0 || next === committedRef.current) return;\n\t\tcommittingRef.current = true;\n\t\tconst previous = committedRef.current;\n\t\t// 提交纪元:每次发起新提交单调递增。迟到采纳判据用它区分「无新提交」与\n\t\t// 「新提交失败回滚」——两者 committedRef 都会回到 previous,值比较无法\n\t\t// 区分,纪元未变才说明迟到结果仍属当前唯一意图链。\n\t\tcommitEpochRef.current += 1;\n\t\tconst epochAtCommit = commitEpochRef.current;\n\t\t// 注意:这里不重置 dragging——commit 的唯二入口是拖动结束(stopDragging\n\t\t// 已先 setDragging(false))与键盘(本就非拖拽),无需防御性复位。\n\t\tsetCommitting(true);\n\t\tsetLocalError(null);\n\t\tpreviewRef.current = index;\n\t\tsetPreview(index);\n\t\tsetEffort(next);\n\t\ttry {\n\t\t\tconst current = state.current;\n\t\t\tif (current === null) throw new Error(t(\"empty.efforts\"));\n\t\t\t// 超时护栏:官方 select 无超时契约,把它包进带 12s 定时的 Promise——\n\t\t\t// 超时即视为失败(回滚 + 播报),并释放 committing 锁。select 迟到的\n\t\t\t// resolve 不直接丢弃:若它确认了新档位且自回滚以来没有新提交改写\n\t\t\t// committedRef(回滚态仍对应本次提交链),把 UI/committedRef 同步回\n\t\t\t// 实际已生效的档位;迟到的 reject 保持回滚态。timer 在 select 先完成\n\t\t\t// 时清除,不泄漏空转定时器。\n\t\t\tconst ok = await new Promise<boolean>((resolve, reject) => {\n\t\t\t\tlet settled = false;\n\t\t\t\tconst timer = window.setTimeout(() => {\n\t\t\t\t\tif (settled) return;\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tcommitTimerRef.current = null;\n\t\t\t\t\treject(new Error(t(\"effort.timeout\")));\n\t\t\t\t}, EFFORT_COMMIT_TIMEOUT_MS);\n\t\t\t\t// 定时器句柄进 ref:菜单关闭(本组件卸载)时由 cleanup 直接\n\t\t\t\t// clearTimeout,不再依赖 Promise 闭包把定时器活到 select 迟到 settle。\n\t\t\t\tcommitTimerRef.current = timer;\n\t\t\t\tselect({ provider: current.provider, model: current.model, reasoningEffort: next }).then(\n\t\t\t\t\t(accepted) => {\n\t\t\t\t\t\tif (settled) {\n\t\t\t\t\t\t\t// 迟到成功且实际生效:committedRef 还是回滚前的 previous\n\t\t\t\t\t\t\t// 且提交纪元未推进(期间没有新的提交/拖动改写它),说明\n\t\t\t\t\t\t\t// 回滚态对应用户当前唯一意图链,后端已确认新档位——补一次\n\t\t\t\t\t\t\t// UI 同步,消除「UI 回滚、后端已改档」的错位;否则以新\n\t\t\t\t\t\t\t// 操作链为准不覆盖(新提交失败回滚后 committedRef 值与\n\t\t\t\t\t\t\t// 上一轮相同,纪元未推进才说明没有新提交)。\n\t\t\t\t\t\t\t// 判定收敛到 dmsShouldAdoptLateSuccess(effort.ts,node 可测)。\n\t\t\t\t\t\t\tif (accepted && mountedRef.current && dmsShouldAdoptLateSuccess(committedRef.current, previous, draggingRef.current, committingRef.current, commitEpochRef.current, epochAtCommit)) {\n\t\t\t\t\t\t\t\tcommittedRef.current = next;\n\t\t\t\t\t\t\t\tpreviewRef.current = index;\n\t\t\t\t\t\t\t\tsetEffort(next);\n\t\t\t\t\t\t\t\tsetPreview(index);\n\t\t\t\t\t\t\t\tsetLocalError(null);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\twindow.clearTimeout(timer);\n\t\t\t\t\t\tcommitTimerRef.current = null;\n\t\t\t\t\t\tresolve(accepted);\n\t\t\t\t\t},\n\t\t\t\t\t(cause: unknown) => {\n\t\t\t\t\t\tif (settled) return;\n\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\twindow.clearTimeout(timer);\n\t\t\t\t\t\tcommitTimerRef.current = null;\n\t\t\t\t\t\treject(cause);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t});\n\t\t\tif (!ok) throw new Error(t(\"effort.failed\"));\n\t\t\tif (mountedRef.current) {\n\t\t\t\tcommittedRef.current = next;\n\t\t\t\tpreviewRef.current = index;\n\t\t\t\tsetEffort(next);\n\t\t\t\tsetPreview(index);\n\t\t\t}\n\t\t} catch (cause) {\n\t\t\tconst restore = Math.max(0, dmsEffortIndex(levels, previous));\n\t\t\tcommittedRef.current = previous;\n\t\t\tpreviewRef.current = restore;\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetEffort(previous);\n\t\t\t\tsetPreview(restore);\n\t\t\t\tsetLocalError(cause instanceof Error ? cause.message : String(cause));\n\t\t\t}\n\t\t} finally {\n\t\t\tcommittingRef.current = false;\n\t\t\tif (mountedRef.current) setCommitting(false);\n\t\t}\n\t}, [levels, select, state, t]);\n\t// 指针拖动状态机(pointer 生命周期 + window 兜底监听)在 effortDrag.ts;\n\t// commit/rollback/showPointerPreview 作回调外接——hook 经 callbacksRef 持有\n\t// 最新闭包,事件触发时取到当前渲染的函数。\n\tconst drag = useEffortDrag({\n\t\tlevelCount: () => levels.length,\n\t\tcanStart: () => !busy,\n\t\tonPreview: showPointerPreview,\n\t\tonCommit: (raw) => void commit(raw),\n\t\tonRollback: rollback,\n\t\tinputRef,\n\t\tdraggingRef,\n\t});\n\tconst { dragging } = drag;\n\t// 拖动态同步到辐射状态与画布(preview effect 已处理 target,这里只切 dragging 速度)。\n\treact.useEffect(() => {\n\t\tradiationRef.current.dragging = dragging;\n\t\tredrawRef.current?.();\n\t}, [dragging]);\n\tconst onKeyDown = (event: react.KeyboardEvent<HTMLInputElement>): void => {\n\t\tconst current = dmsClampIndex(Number(event.currentTarget.value), levels.length);\n\t\tlet target;\n\t\tif (event.key === \"ArrowLeft\" || event.key === \"ArrowDown\" || event.key === \"PageDown\") {\n\t\t\ttarget = Math.max(0, current - 1);\n\t\t} else if (event.key === \"ArrowRight\" || event.key === \"ArrowUp\" || event.key === \"PageUp\") {\n\t\t\ttarget = Math.min(levels.length - 1, current + 1);\n\t\t} else if (event.key === \"Home\") {\n\t\t\ttarget = 0;\n\t\t} else if (event.key === \"End\") {\n\t\t\ttarget = levels.length - 1;\n\t\t}\n\t\tif (target === void 0) return;\n\t\t// 忙态(自身提交中/共享目录 select 在途)吞掉按键:必须先 preventDefault,\n\t\t// 否则 range input 的原生步进会改 DOM value,与受控值漂移。\n\t\tevent.preventDefault();\n\t\tif (busy) return;\n\t\tvoid commit(target);\n\t};\n\tif (!available) return null;\n\tconst count = levels.length;\n\tconst effortName = levels[dmsEffortIndex(levels, effort)]?.name ?? effort;\n\tconst effortDesc = levels[dmsEffortIndex(levels, effort)]?.description;\n\tconst progress = preview / (count - 1) * 100;\n\tconst style = { \"--dms-progress\": `${progress}%` } as react.CSSProperties;\n\treturn (\n\t\t<div\n\t\t\tclassName={`dms-effort${dragging ? \" is-dragging\" : \"\"}${busy ? \" is-busy\" : \"\"}${error === null ? \"\" : \" is-error\"}`}\n\t\t>\n\t\t\t<div\n\t\t\t\tclassName=\"dms-effort-slider\"\n\t\t\t\tstyle={style}\n\t\t\t>\n\t\t\t\t<div className=\"dms-effort-track\" aria-hidden=\"true\" />\n\t\t\t\t<div className=\"dms-effort-fx\" aria-hidden=\"true\">\n\t\t\t\t\t<canvas ref={canvasRef} className=\"dms-effort-canvas\" />\n\t\t\t\t\t<span className=\"dms-effort-flare\" />\n\t\t\t\t</div>\n\t\t\t\t<input\n\t\t\t\t\tref={inputRef}\n\t\t\t\t\tclassName=\"dms-effort-input\"\n\t\t\t\t\ttype=\"range\"\n\t\t\t\t\tmin=\"0\"\n\t\t\t\t\tmax={count - 1}\n\t\t\t\t\tstep=\"0.01\"\n\t\t\t\t\tvalue={preview}\n\t\t\t\t\taria-disabled={busy}\n\t\t\t\t\taria-label={t(\"menu.effort\")}\n\t\t\t\t\taria-valuetext={effortName}\n\t\t\t\t\tonChange={(event: react.ChangeEvent<HTMLInputElement>) => {\n\t\t\t\t\t\tconst raw = Number(event.currentTarget.value);\n\t\t\t\t\t\tshowPointerPreview(raw);\n\t\t\t\t\t}}\n\t\t\t\t\tonPointerDown={drag.handlers.onPointerDown}\n\t\t\t\t\tonPointerMove={drag.handlers.onPointerMove}\n\t\t\t\t\tonPointerUp={drag.handlers.onPointerUp}\n\t\t\t\t\tonPointerCancel={drag.handlers.onPointerCancel}\n\t\t\t\t\tonBlur={drag.handlers.onBlur}\n\t\t\t\t\tonKeyDown={onKeyDown}\n\t\t\t\t/>\n\t\t\t\t<span className=\"dms-effort-knob\" aria-hidden=\"true\" />\n\t\t\t</div>\n\t\t\t<span className=\"dms-effort-value\">{effortName}</span>\n\t\t\t{effortDesc === undefined ? null : <span className=\"dms-effort-desc\">{effortDesc}</span>}\n\t\t\t{error === null ? null : <span className=\"dms-sr\" role=\"status\">{error}</span>}\n\t\t\t{error === null ? null : <div className=\"dms-effort-error\">{error}</div>}\n\t\t</div>\n\t);\n});\n\ninterface ModelOptionProps {\n\tgroup: ModelProviderGroup\n\tmodel: ModelProviderGroup['models'][number]\n\tshowProvider: boolean\n\tselected: boolean\n\tbusy: boolean\n\t/** 键盘导航用的稳定行标识:菜单内 `[data-row-key]` 查询即按 DOM 顺序聚焦。 */\n\trowKey: string\n\t/** roving tabindex:true = 当前活动行(唯一可 Tab 到的模型行,其余 -1)。 */\n\tactive: boolean\n\t/** 行聚焦回调(箭头导航/点击/程序化 focus 都经它同步活动行键)。 */\n\tonRowFocus: (key: string) => void\n\t/** 搜索命中且命中落在名称内时的片段区间(高亮);undefined/null = 不标。 */\n\tnameHit?: { start: number; end: number } | null\n\tt: TranslateNS<'modelSelector'>\n\tonChoose: (selection: ModelSelection) => void\n}\n/**\n * 菜单里的一行模型。独立成 memo 组件:目录可能数百行,搜索输入每击键都会\n * 重建菜单内容,行 props(group/model 引用、selected/busy/rowKey、稳定的\n * onChoose)稳定时 React 直接跳过 reconcile,只重渲染真正变化的那行。\n *\n * 自定义比较器(默认浅比较的补充):nameHit 是每次击键新建的对象(引用必变),\n * 但 100 条命中里绝大多数命中区间不变——按值比较 start/end,值相同即跳过\n * 重渲染,搜索才不卡顿。\n */\nconst ModelOption = react.memo(function ModelOption({ group, model, showProvider, selected, busy, rowKey, active, onRowFocus, nameHit, t, onChoose }: ModelOptionProps) {\n\tconst hit = nameHit === undefined || nameHit === null || nameHit.start === nameHit.end\n\t\t? null\n\t\t: [\n\t\t\tmodel.name.slice(0, nameHit.start),\n\t\t\tmodel.name.slice(nameHit.start, nameHit.end),\n\t\t\tmodel.name.slice(nameHit.end),\n\t\t];\n\treturn (\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\trole=\"menuitemradio\"\n\t\t\taria-checked={selected}\n\t\t\tdata-row-key={rowKey}\n\t\t\ttabIndex={active ? 0 : -1}\n\t\t\tclassName={`dms-model-option${selected ? \" dms-model-optionSelected\" : \"\"}`}\n\t\t\ttitle={model.description === undefined ? model.name : `${model.name} — ${model.description}`}\n\t\t\taria-disabled={busy}\n\t\t\tonFocus={() => onRowFocus(rowKey)}\n\t\t\tonClick={() => onChoose({ provider: group.id, model: model.id })}\n\t\t>\n\t\t\t<span className=\"dms-model-option-copy\">\n\t\t\t\t<span className=\"dms-nameRow\">\n\t\t\t\t\t<span className=\"dms-model-option-name\">\n\t\t\t\t\t\t{hit === null ? model.name : <>\n\t\t\t\t\t\t\t{hit[0]}<span className=\"dms-hit\">{hit[1]}</span>{hit[2]}\n\t\t\t\t\t\t</>}\n\t\t\t\t\t</span>\n\t\t\t\t\t{model.reasoning !== undefined && <span className=\"dms-badge\" title={t(\"badge.reasoningHint\")}>{t(\"badge.reasoning\")}</span>}\n\t\t\t\t</span>\n\t\t\t\t{model.description !== undefined && <span className=\"dms-model-option-desc\">{model.description}</span>}\n\t\t\t\t{showProvider && <span className=\"dms-model-option-provider\">{group.name}</span>}\n\t\t\t</span>\n\t\t\t<span className=\"dms-model-check\" aria-hidden=\"true\">\n\t\t\t\t{selected ? IconCheck : null}\n\t\t\t</span>\n\t\t</button>\n\t);\n}, (prev, next) => {\n\tif (prev.group !== next.group) return false;\n\tif (prev.model !== next.model) return false;\n\tif (prev.showProvider !== next.showProvider) return false;\n\tif (prev.selected !== next.selected) return false;\n\tif (prev.busy !== next.busy) return false;\n\tif (prev.rowKey !== next.rowKey) return false;\n\tif (prev.active !== next.active) return false;\n\tif (prev.onRowFocus !== next.onRowFocus) return false;\n\tif (prev.t !== next.t) return false;\n\tif (prev.onChoose !== next.onChoose) return false;\n\t// 引用相同(含都是 null/undefined)即视为未变化;否则按值比较命中区间。\n\tif (prev.nameHit === next.nameHit) return true;\n\tif (prev.nameHit === null || prev.nameHit === void 0) return false;\n\tif (next.nameHit === null || next.nameHit === void 0) return false;\n\treturn prev.nameHit.start === next.nameHit.start && prev.nameHit.end === next.nameHit.end;\n});\n\ninterface ModelSelectProps {\n locked: boolean\n available: boolean\n directory: SnapshotStore<ModelDirectoryState>\n load: () => void\n select: (selection: ModelSelection) => Promise<boolean>\n t: TranslateNS<'modelSelector'>\n}\nexport function ModelSelect({ locked, available, directory, load, select, t }: ModelSelectProps) {\n\tconst state = react.useSyncExternalStore(directory.subscribe, directory.getSnapshot);\n\tconst [open, setOpen] = react.useState(false);\n\t// 弹出方向自适应:菜单向上弹出(bottom 定位),但 trigger 距视口顶部\n\t// 空间不足时(新会话/页面中部)改为向下弹出,避免搜索框与选项溢出视口。\n\tconst [menuAbove, setMenuAbove] = react.useState(true);\n\tconst [query, setQuery] = react.useState(\"\");\n\tconst [collapsed, setCollapsed] = react.useState<Set<string>>(() => new Set());\n\tconst [notice, setNotice] = react.useState<string | null>(null);\n\t// roving tabindex 的活动行键:全列表只有这一行 tabindex=0(其余 -1,仅方向键\n\t// 可达),Tab 从搜索框进列表落在它上面。行焦点移动经 onRowFocus 更新;行集合\n\t// 变化(打开/搜索/折叠/选中迁移)时由下方 effect 重置到默认落点。\n\tconst [activeRowKey, setActiveRowKey] = react.useState<string | null>(null);\n\tconst onRowFocus = react.useCallback((key: string): void => {\n\t\tsetActiveRowKey(key);\n\t}, []);\n\t// 菜单内 notice 会随 close() 一起清掉,失败原因必须落在菜单外的瞬时横幅上\n\t// (官方 seat 正是用 Toast 播报 select 拒绝)。seq 递增让同一段文案可重播。\n\tconst [toast, setToast] = react.useState<{ seq: number; text: string; failed: boolean } | null>(null);\n\tconst toastSeqRef = react.useRef(0);\n\tconst showToast = react.useCallback((text: string, failed = true): void => {\n\t\ttoastSeqRef.current += 1;\n\t\tsetToast({ seq: toastSeqRef.current, text, failed });\n\t}, []);\n\tconst lastActionRef = react.useRef<\"load\" | \"select\">(\"load\");\n\tconst rootRef = react.useRef<HTMLDivElement | null>(null);\n\tconst triggerRef = react.useRef<HTMLButtonElement | null>(null);\n\tconst searchRef = react.useRef<HTMLInputElement | null>(null);\n\tconst lastLoadRef = react.useRef(0);\n\tconst id = react.useId();\n\t// 模型目录稳定时,haystack 索引不随 select/状态抖动重建:仅在 groups 引用\n\t// 真正变化(新 load 结果)时重建 400+ 条搜索索引。useMemo 保证 group 引用\n\t// 未变时 choices 引用稳定,currentChoice/hits 的 useMemo 依赖它也不会抖动。\n\tconst choices = react.useMemo<readonly ModelChoice[]>(\n\t\t() => state.groups.flatMap((group) => group.models.map((model) => ({\n\t\t\tgroup,\n\t\t\tmodel,\n\t\t\thaystack: `${model.name}\\n${model.description ?? \"\"}\\n${group.name}\\n${model.id}\\n${group.id}`.toLowerCase(),\n\t\t\tselection: {\n\t\t\t\tprovider: group.id,\n\t\t\t\tmodel: model.id,\n\t\t\t\t...model.reasoning?.defaultEffort === void 0 ? {} : { reasoningEffort: model.reasoning.defaultEffort }\n\t\t\t}\n\t\t}))),\n\t\t[state.groups],\n\t);\n\tconst currentChoice = react.useMemo(() => {\n\t\tconst current = state.current;\n\t\tif (current === null) return void 0;\n\t\treturn choices.find((c) => c.selection.provider === current.provider && c.selection.model === current.model);\n\t}, [choices, state.current]);\n\tconst reasoning = currentChoice?.model.reasoning;\n\tconst effectiveEffort = state.current?.reasoningEffort ?? reasoning?.defaultEffort;\n\tconst effortLabel = reasoning === void 0 ? void 0 : effectiveEffort === void 0 ? t(\"effort.providerDefault\") : reasoning.efforts.find((level) => level.id === effectiveEffort)?.name ?? effectiveEffort;\n\tconst busy = state.status === \"selecting\";\n\tconst normalized = query.trim().toLowerCase();\n\tconst hits = react.useMemo<SearchResult | null>(() => {\n\t\tif (normalized === \"\") return null;\n\t\tconst items: SearchHit[] = [];\n\t\tlet total = 0;\n\t\tfor (const choice of choices) {\n\t\t\tif (!choice.haystack.includes(normalized)) continue;\n\t\t\ttotal += 1;\n\t\t\t// 渲染窗口已满:只计数不构造对象——宽泛关键词(单字母)命中数百条时,\n\t\t\t// 每次击键省掉数百次 `{group, model, nameHit}` 分配;nameHit 的 indexOf\n\t\t\t// 也只在窗口内做(高亮只消费窗口内条目)。总数单独累计,播报与\n\t\t\t// 「仅显示前 N 条」提示仍然准确。\n\t\t\tif (items.length >= MAX_VISIBLE_HITS) continue;\n\t\t\tlet nameHit: { start: number; end: number } | null = null;\n\t\t\tconst at = choice.model.name.toLowerCase().indexOf(normalized);\n\t\t\tif (at >= 0) nameHit = { start: at, end: at + normalized.length };\n\t\t\titems.push({ group: choice.group, model: choice.model, nameHit });\n\t\t}\n\t\treturn { items, total };\n\t}, [choices, normalized]);\n\t// roving tabindex 的默认落点(Tab 从搜索框进列表的停靠行):选中行优先、\n\t// 其次首个可见模型行、全折叠时回退首个组头。纯决策在 roving.ts(node 可测)。\n\tconst defaultRowKey = react.useMemo(() => dmsDefaultRowKey({\n\t\thitKeys: hits === null ? null : hits.items.map((h) => `${h.group.id}\\u0000${h.model.id}`),\n\t\tmodelKeys: state.groups.flatMap((g) => (collapsed.has(g.id) ? [] : g.models.map((m) => `${g.id}\\u0000${m.id}`))),\n\t\theaderKeys: state.groups.map((g) => `header:${g.id}`),\n\t\tselectedKey: state.current === null ? null : `${state.current.provider}\\u0000${state.current.model}`,\n\t}), [hits, state.groups, collapsed, state.current]);\n\t// 行集合/选中变化时把 tabindex=0 的默认行同步到当前渲染集;菜单关闭清空。\n\t// 焦点已在行上(onRowFocus 改写过 activeRowKey)时,只要默认落点没变本\n\t// effect 不重跑,活动行保持用户最后聚焦的那行。\n\treact.useEffect(() => {\n\t\tif (!open) {\n\t\t\tsetActiveRowKey(null);\n\t\t\treturn;\n\t\t}\n\t\tsetActiveRowKey(defaultRowKey);\n\t}, [open, defaultRowKey]);\n\tconst reload = react.useCallback(() => {\n\t\tlastActionRef.current = \"load\";\n\t\tlastLoadRef.current = Date.now();\n\t\tload();\n\t}, [load]);\n\treact.useEffect(() => {\n\t\tif (available) {\n\t\t\t// 目录新鲜度守卫:inject 重跑会重建 load 引用触发本 effect,30s 内已有\n\t\t\t// 有效目录时不重复全量 load(show() 打开路径已有同款守卫)。\n\t\t\tif (Date.now() - lastLoadRef.current < DIRECTORY_STALE_MS && state.status === \"ready\" && state.groups.length > 0) return;\n\t\t\tlastActionRef.current = \"load\";\n\t\t\tlastLoadRef.current = Date.now();\n\t\t\tload();\n\t\t}\n\t}, [available, load]);\n\t// 菜单高度按视口实测钳位(原先 CSS 写死 min(420px, 100vh - 96px)),\n\t// composer 变高/窗口变小时自动收,不再溢出。\n\tconst menuRef = react.useRef<HTMLDivElement | null>(null);\n\t// useAnchoredMaxHeight 只适配「底边固定、向上生长」的底部锚定浮层:fit 读的是\n\t// 浮层自身底缘与视口顶缘的距离。向下弹(menuAbove=false)时面板改为顶边固定,\n\t// hook 度量的底缘是向下弹出后的位置,返回值在渲染处(maxHeight 三目)被忽略——\n\t// 继续挂监听只白付测量:每次 scroll/resize 都未节流 getBoundingClientRect +\n\t// setState 重渲染。向下弹期间把被测 ref 换成恒 null 的空 ref,hook 的 layout\n\t// effect 因 el === null 整体跳过(不注册监听、不测量);方向翻回向上时 ref 换回\n\t// menuRef,effect 因 ref 身份变化重跑并立即 fit——翻向后的钳位是新鲜测量\n\t// (原实现 signal 不变不重跑,翻回时用的还是向下弹期间留下的旧值)。\n\tconst inactiveMenuRef = react.useRef<HTMLDivElement | null>(null);\n\tconst menuMaxHeight = useAnchoredMaxHeight(menuAbove ? menuRef : inactiveMenuRef, MENU_MAX_HEIGHT, open);\n\tconst [belowMaxHeight, setBelowMaxHeight] = react.useState(MENU_MAX_HEIGHT);\n\t// 为何不采纳官方 useAnchoredPosition(与文件头「不用官方 Menu」并列的第二个不采纳决定):\n\t// 该 hook 的水平起点固定是 anchor 左缘(left = rect.left,仅越界时钳进视口),没有\n\t// 「右缘对齐 anchor 右缘」这种模式;而本菜单默认就是右锚定(.dms-menu 的 right:0,与\n\t// seat 右缘对齐),只有右侧放不下时才钳到视口内改用 left。方向也不由 hook 决定:\n\t// side 是调用方写死的 'top' | 'bottom',而这里要按 trigger 上下空间自动选边\n\t// (dmsMenuAbove);且 hook 返回的是 fixed 定位坐标,本菜单是 .dms-root 内的\n\t// absolute + bottom/right 锚定,采纳它等于同时改默认对齐、方向策略与定位模型——\n\t// 属用户可见的运行时行为变更,故保留自研测量。\n\t// 水平钳位:seat 右缘放不下整幅菜单(窄窗口)时改为 left 锚定,undefined = 默认右锚定。\n\tconst [menuLeft, setMenuLeft] = react.useState<number | undefined>(undefined);\n\t// 布局生效前测量(对齐 useAnchoredMaxHeight 的 useLayoutEffect 模式):\n\t// 用 useEffect 会在首帧绘制后才翻转方向/水平锚定,向下弹或左夹取场景\n\t// 菜单先按默认(向上弹 + 右锚定)画一帧再跳位,肉眼可见闪错位。\n\treact.useLayoutEffect(() => {\n\t\tif (!open) return;\n\t\tconst measure = () => {\n\t\t\tconst trigger = triggerRef.current;\n\t\t\tif (trigger === null) return;\n\t\t\tconst rect = trigger.getBoundingClientRect();\n\t\t\t// 取上下两侧中空间更大的一侧:只有上方放不下时才向下弹。\n\t\t\t// 原先要求上方空出满高(MENU_MAX_HEIGHT + 20),窗口一矮就反而把面板\n\t\t\t// 挤到视口下沿外面。\n\t\t\tsetMenuAbove(dmsMenuAbove(rect.top, rect.bottom, window.innerHeight));\n\t\t\t// Downward the panel is top-anchored, so the hook's own fit would feed\n\t\t\t// back on itself; clamp against the space below the trigger instead.\n\t\t\tsetBelowMaxHeight(dmsBelowMaxHeight(rect.bottom, window.innerHeight, MENU_MAX_HEIGHT));\n\t\t\t// 水平:菜单实际渲染宽度为准(offsetWidth),右锚定放不下时钳到视口内。\n\t\t\tconst menuWidth = menuRef.current?.offsetWidth ?? 0;\n\t\t\tsetMenuLeft(dmsMenuLeft(rect.right, menuWidth, window.innerWidth, MENU_VIEWPORT_MARGIN));\n\t\t};\n\t\tmeasure();\n\t\tlet raf = 0;\n\t\t// scroll 捕获阶段每帧触发,resize 也高频;rAF 合并同帧多次触发,\n\t\t// passive 声明让浏览器不必等我们作 preventDefault。\n\t\tconst onViewportChange = () => {\n\t\t\tif (raf !== 0) return;\n\t\t\traf = requestAnimationFrame(() => {\n\t\t\t\traf = 0;\n\t\t\t\tmeasure();\n\t\t\t});\n\t\t};\n\t\twindow.addEventListener(\"resize\", onViewportChange, { passive: true });\n\t\twindow.addEventListener(\"scroll\", onViewportChange, { capture: true, passive: true });\n\t\treturn () => {\n\t\t\tif (raf !== 0) cancelAnimationFrame(raf);\n\t\t\twindow.removeEventListener(\"resize\", onViewportChange);\n\t\t\twindow.removeEventListener(\"scroll\", onViewportChange, { capture: true });\n\t\t};\n\t}, [open]);\n\treact.useEffect(() => {\n\t\tif (!open) return;\n\t\tsearchRef.current?.focus();\n\t}, [open]);\n\t// 打开即把当前选中行滚进可视区:选中模型在长列表深处时不用手动翻找。\n\t// 手动改 scrollTop 而不用 scrollIntoView,避免连带滚动页面/其它祖先容器。\n\t// 吸顶分组头(sticky top:0)会盖住滚到容器顶缘的行:偏移量按所在组的\n\t// 头部实际高度让位,否则「滚到了」却看不见。\n\t// 依赖补全:目录异步加载完成(state.groups 引用变化)后重新定位——原实现\n\t// 只依赖 [open],加载完成晚于开合的那次渲染时选中行永远不会滚进可视区。\n\t// useLayoutEffect(而非 queueMicrotask):DOM 已在提交阶段就绪,同步滚动\n\t// 不产生首帧跳动;且此时行/头部尺寸可测。\n\tconst scrollSelectedIntoView = (): void => {\n\t\tconst list = menuRef.current?.querySelector(\".dms-groups\");\n\t\tif (list === null || list === void 0) return;\n\t\t// 折叠/展开/目录刷新改变内容高度后,scrollTop 可能超出新的可滚上限\n\t\t// (浏览器不自动钳回,滚动条下方露出空白),先钳回合法区间再定位。\n\t\tconst maxScroll = list.scrollHeight - list.clientHeight;\n\t\tif (list.scrollTop > maxScroll) list.scrollTop = Math.max(0, maxScroll);\n\t\t// 选中行不在 DOM(选中组被折叠 / 搜索把选中模型滤掉)时,退而滚动到\n\t\t// 选中项所在分组(含折叠态,让用户看到「当前模型在那个组」)。\n\t\tconst selected = state.current;\n\t\tconst row = list.querySelector('[role=\"menuitemradio\"][aria-checked=\"true\"]');\n\t\tif (row === null) {\n\t\t\tif (selected === null) return;\n\t\t\tconst section = list.querySelector<HTMLElement>(`section[data-group-id=\"${globalThis.CSS.escape(selected.provider)}\"]`);\n\t\t\tif (section === null) return;\n\t\t\tconst sectionRect = section.getBoundingClientRect();\n\t\t\tconst listRect = list.getBoundingClientRect();\n\t\t\tif (sectionRect.top < listRect.top) list.scrollTop += sectionRect.top - listRect.top;\n\t\t\telse if (sectionRect.bottom > listRect.bottom) list.scrollTop += sectionRect.bottom - listRect.bottom;\n\t\t\treturn;\n\t\t}\n\t\tconst rowRect = row.getBoundingClientRect();\n\t\tconst listRect = list.getBoundingClientRect();\n\t\tconst header = row.closest(\"section\")?.querySelector(\".dms-groupHeader\");\n\t\tconst headerHeight = header === null || header === undefined ? 0 : header.getBoundingClientRect().height;\n\t\tif (rowRect.top < listRect.top + headerHeight) list.scrollTop += rowRect.top - (listRect.top + headerHeight);\n\t\telse if (rowRect.bottom > listRect.bottom) list.scrollTop += rowRect.bottom - listRect.bottom;\n\t};\n\t// 打开瞬间自动展开选中组(若被折叠)。用 ref 记录「本次打开周期已自动展开过\n\t// 哪个 provider」:layout effect 依赖含 collapsed,若不拦截,用户手动折叠\n\t// 选中组时 effect 会立刻把它重新展开——折叠功能对选中组形同失效。\n\t// 只在 provider 首次解析到(含异步 load 完成后)且处于折叠态时展开一次,\n\t// 之后同 provider 的手动折叠不再被抢。\n\tconst expandedProviderRef = react.useRef<string | null>(null);\n\treact.useLayoutEffect(() => {\n\t\tif (!open) {\n\t\t\texpandedProviderRef.current = null;\n\t\t\treturn;\n\t\t}\n\t\t// 搜索命中渲染期间不滚动/不自动展开:命中列表替换了分组 DOM,滚动测量\n\t\t// 没有意义;且每次击键 hits 引用都变化——提前返回避免每次击键重跑\n\t\t// querySelector×2 + getBoundingClientRect×3 的定位测量(当前模型命中时\n\t\t// 还会写 scrollTop)。查询清空(hits 回 null)后本 effect 因 deps 变化\n\t\t// 重跑,滚动/展开行为与首次打开、异步加载完成场景保持一致。\n\t\tif (hits !== null) return;\n\t\tconst provider = state.current?.provider ?? null;\n\t\tif (provider !== null && provider !== expandedProviderRef.current && collapsed.has(provider)) {\n\t\t\texpandedProviderRef.current = provider;\n\t\t\tsetCollapsed((prev) => {\n\t\t\t\tif (!prev.has(provider)) return prev;\n\t\t\t\tconst next = new Set(prev);\n\t\t\t\tnext.delete(provider);\n\t\t\t\treturn next;\n\t\t\t});\n\t\t}\n\t\tscrollSelectedIntoView();\n\t}, [open, state.groups, state.current, collapsed, hits]);\n\t// show/close/choose/toggleCollapse 四个 useCallback 必须全部位于下方\n\t// `if (!available) return null` 早退之前:hooks 数量不得随渲染分支变化\n\t// (React 会直接抛 \"Rendered more hooks than during the previous render\")。\n\tconst show = react.useCallback(() => {\n\t\tsetOpen(true);\n\t\tif (state.status === \"error\" || state.groups.length === 0 || Date.now() - lastLoadRef.current > DIRECTORY_STALE_MS) reload();\n\t}, [state.status, state.groups.length, reload]);\n\tconst close = react.useCallback((restoreFocus = false) => {\n\t\tsetOpen(false);\n\t\tsetNotice(null);\n\t\tsetQuery(\"\");\n\t\tif (restoreFocus) queueMicrotask(() => {\n\t\t\ttriggerRef.current?.focus();\n\t\t});\n\t}, []);\n\t// 外部 pointerdown 关闭与 Escape 一致走 close(true):搜索框随菜单卸载后\n\t// 焦点落回 trigger(此前焦点直接掉 body)。hook 只以 false 调用 setter,\n\t// 忽略参数即可。\n\tconst dismissOnOutsidePointer = react.useCallback(() => {\n\t\tclose(true);\n\t}, [close]);\n\tuseDismissOnOutsidePointer(rootRef, open, dismissOnOutsidePointer);\n\t// locked 翻转(会话移除/失活/页面 inert 等)时若菜单还开着直接关闭:\n\t// trigger 已禁用,挂着的菜单没有可交互入口;close() 顺带清掉搜索词与\n\t// notice。close 引用稳定([] deps),effect 只在 locked/open 变化时重跑。\n\treact.useEffect(() => {\n\t\tif (locked && open) close();\n\t}, [locked, open, close]);\n\tconst choose = react.useCallback((selection: ModelSelection): void => {\n\t\tif (busy) return;\n\t\tif (state.current?.provider === selection.provider && state.current.model === selection.model) {\n\t\t\tsetNotice(t(\"notice.already\"));\n\t\t\treturn;\n\t\t}\n\t\tconst target = choices.find((c) => c.selection.provider === selection.provider && c.selection.model === selection.model);\n\t\tconst max = target?.model.reasoning === void 0 ? void 0 : maxEffortOf(target.model.reasoning);\n\t\tconst effort = max === \"off\" ? void 0 : max;\n\t\tconst full = {\n\t\t\tprovider: selection.provider,\n\t\t\tmodel: selection.model,\n\t\t\t...effort === void 0 ? {} : { reasoningEffort: effort }\n\t\t};\n\t\tlastActionRef.current = \"select\";\n\t\t// 自动拉档只有「高于模型自己声明的默认档」时才算替用户做了决定,此时播报落点。\n\t\tconst autoRaised = effort !== void 0 && effort !== target?.model.reasoning?.defaultEffort;\n\t\tconst autoName = target?.model.reasoning?.efforts.find((level) => level.id === effort)?.name ?? effort ?? \"\";\n\t\t// 超时护栏与 EffortSlider.commit() 同款:目录 select 无超时契约,RPC 永久\n\t\t// 挂起时若不加护栏,status='selecting' 由 store 持有、本插件无法复位,交互\n\t\t// 永久锁死。超时/拒绝都按切换失败播报 Toast(复用既有文案,不新增 key);\n\t\t// 不强行复位 store——目录 settle 后状态自然回到 ready,与 effort commit 一致。\n\t\t// 迟到的 settle 一律忽略:成功与否都会经共享目录写回,UI 无需再动作。\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tconst accepted = await new Promise<boolean>((resolve, reject) => {\n\t\t\t\t\tlet settled = false;\n\t\t\t\t\tconst timer = window.setTimeout(() => {\n\t\t\t\t\t\tif (settled) return;\n\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\treject(new Error(t(\"notice.selectFailed\")));\n\t\t\t\t\t}, EFFORT_COMMIT_TIMEOUT_MS);\n\t\t\t\t\tselect(full).then(\n\t\t\t\t\t\t(ok) => {\n\t\t\t\t\t\t\tif (settled) return;\n\t\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\t\twindow.clearTimeout(timer);\n\t\t\t\t\t\t\tresolve(ok);\n\t\t\t\t\t\t},\n\t\t\t\t\t\t(cause: unknown) => {\n\t\t\t\t\t\t\tif (settled) return;\n\t\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\t\twindow.clearTimeout(timer);\n\t\t\t\t\t\t\treject(cause);\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t\tif (!accepted) {\n\t\t\t\t\tconst message = directory.getSnapshot().error;\n\t\t\t\t\tshowToast(message !== null ? t(\"error.action\", { message }) : t(\"notice.selectFailed\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (rootRef.current !== null) {\n\t\t\t\t\tclose(true);\n\t\t\t\t\tif (autoRaised) showToast(t(\"toast.effortAuto\", { effort: autoName }), false);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// 超时 / select 拒绝:都按切换失败播报(防御性 catch 同时杜绝\n\t\t\t\t// unhandled rejection——未来注入面变更也不会让它漏出去)。\n\t\t\t\tshowToast(t(\"notice.selectFailed\"));\n\t\t\t}\n\t\t})();\n\t}, [busy, state.current, choices, select, t, directory, close, showToast]);\n\tconst toggleCollapse = react.useCallback((groupId: string): void => {\n\t\tsetCollapsed((prev) => {\n\t\t\tconst next = new Set(prev);\n\t\t\tif (next.has(groupId)) next.delete(groupId);\n\t\t\telse next.add(groupId);\n\t\t\treturn next;\n\t\t});\n\t}, []);\n\tif (!available) return null;\n\t// 键盘导航:按 DOM 顺序查询菜单内全部行(data-row-key),不再依赖渲染期\n\t// 收集的 ref 数组(那既阻止行级 memo,又把副作用塞进 render)。\n\tconst moveFocus = (offset: number): void => {\n\t\tconst menu = menuRef.current;\n\t\tif (menu === null) return;\n\t\tconst items = [...menu.querySelectorAll<HTMLButtonElement>('[data-row-key]')];\n\t\tif (items.length === 0) return;\n\t\tconst active = items.findIndex((item) => item === document.activeElement);\n\t\t// 焦点不在任何行上(在搜索框等菜单内控件)时:向下从首行进、向上从末行进(环绕);\n\t\t// 已在某行上时按 offset 循环。\n\t\tconst start = active < 0 ? (offset > 0 ? -1 : 0) : active;\n\t\titems[(start + offset + items.length) % items.length]?.focus();\n\t};\n\t// Home/End 跳首/末行(WAI-ARIA menu 标准键;搜索框内不劫持——那是移光标键)。\n\tconst focusEdge = (last: boolean): void => {\n\t\tconst items = menuRef.current?.querySelectorAll<HTMLButtonElement>('[data-row-key]');\n\t\tif (items === undefined || items.length === 0) return;\n\t\t(last ? items[items.length - 1] : items[0])?.focus();\n\t};\n\tconst onRootKeyDown = (event: react.KeyboardEvent<HTMLDivElement>): void => {\n\t\t// IME 组合输入期间不劫持按键:Enter 是候选上屏确认、方向键在候选窗翻页、\n\t\t// Escape 是取消组合——此时关菜单/选首个命中/移焦点都是抢用户的输入。\n\t\tif (event.nativeEvent.isComposing) return;\n\t\tconst target = event.target;\n\t\tif (event.key === \"Escape\" && open) {\n\t\t\t// Escape 分层:查询非空且焦点在菜单内时,第一按只清词(搜索框内清词\n\t\t\t// 焦点留在原处;列表内清词会让命中行卸载,焦点还回搜索框,避免掉\n\t\t\t// body),再按一次才关菜单。IME 组合输入在函数入口已被 isComposing\n\t\t\t// 挡掉,这里不需要重复守卫。\n\t\t\tif (query !== \"\" && rootRef.current?.contains(document.activeElement) === true) {\n\t\t\t\tconst fromSearch = target instanceof HTMLInputElement && target === searchRef.current;\n\t\t\t\tsetNotice(null);\n\t\t\t\tsetQuery(\"\");\n\t\t\t\tif (!fromSearch) searchRef.current?.focus();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tevent.preventDefault();\n\t\t\tclose(true);\n\t\t\treturn;\n\t\t}\n\t\tif (!open) return;\n\t\tconst fromSearch = target instanceof HTMLInputElement && target === searchRef.current;\n\t\tif ((event.key === \"ArrowDown\" || event.key === \"ArrowUp\") && (!(target instanceof HTMLInputElement) || fromSearch)) {\n\t\t\t// 搜索框自动聚焦后箭头原本只在输入框内移光标(键盘导航死路);现在\n\t\t\t// 搜索框内方向键也进入结果列表(effort 滑杆的 range input 不受影响)。\n\t\t\tevent.preventDefault();\n\t\t\tmoveFocus(event.key === \"ArrowDown\" ? 1 : -1);\n\t\t\treturn;\n\t\t}\n\t\tif ((event.key === \"Home\" || event.key === \"End\") && !(target instanceof HTMLInputElement)) {\n\t\t\tevent.preventDefault();\n\t\t\tfocusEdge(event.key === \"End\");\n\t\t\treturn;\n\t\t}\n\t\t// 只有搜索框内的 Enter 才选中第一个命中:焦点落在 effort 滑杆(range\n\t\t// input)等其它控件时,Enter 不应把用户的选择抢走。\n\t\tif (event.key === \"Enter\" && fromSearch) {\n\t\t\tif (hits !== null && hits.items.length > 0) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tconst first = hits.items[0]!;\n\t\t\t\tchoose({ provider: first.group.id, model: first.model.id });\n\t\t\t}\n\t\t}\n\t};\n\tconst onBlur = (event: react.FocusEvent<HTMLDivElement>): void => {\n\t\tif (busy) return;\n\t\tconst related = event.relatedTarget;\n\t\tif (related instanceof Node) {\n\t\t\tif (rootRef.current?.contains(related)) return;\n\t\t\tclose();\n\t\t\treturn;\n\t\t}\n\t\t// relatedTarget 为 null(窗口失焦 alt-tab / 焦点落到不可聚焦区域)也收起菜单。\n\t\tclose();\n\t};\n\t// 标签兜底对齐官方:目录成员资格只是参考(routable 契约),current 匹配不到\n\t// group 不代表没有选择——此时显示 provider/model 原始 id,而不是「选择模型」。\n\tconst waiting = state.current === null && state.status === \"loading\";\n\tconst modelLabel = currentChoice?.model.name\n\t\t?? (waiting\n\t\t\t? t(\"trigger.loading\")\n\t\t\t: state.current === null ? t(\"trigger.fallback\") : `${state.current.provider}/${state.current.model}`);\n\tconst providerLabel = currentChoice?.group.name;\n\tconst triggerLabel = effortLabel === void 0 ? modelLabel : `${modelLabel} · ${effortLabel}`;\n\tconst triggerTitle = providerLabel === void 0 ? triggerLabel : `${providerLabel} · ${triggerLabel}`;\n\tconst triggerAria = waiting ? t(\"trigger.loading\") : currentChoice === void 0 ? (state.current === null ? t(\"trigger.selectAria\") : t(\"trigger.aria\", { model: `${state.current.provider}/${state.current.model}` })) : effortLabel === void 0 ? t(\"trigger.aria\", { model: providerLabel === void 0 ? modelLabel : `${providerLabel} ${modelLabel}` }) : t(\"trigger.ariaEffort\", {\n\t\tmodel: providerLabel === void 0 ? modelLabel : `${providerLabel} ${modelLabel}`,\n\t\teffort: effortLabel\n\t});\n\tconst renderErrorStrip = () => {\n\t\t// Load failures only: a rejected select is announced by the Toast, which\n\t\t// also survives closing the menu (see choose()).\n\t\tif (state.error !== null && lastActionRef.current === \"load\") {\n\t\t\treturn (\n\t\t\t\t<div className=\"dms-error\">\n\t\t\t\t\t<span>{t(\"error.action\", { message: state.error })}</span>\n\t\t\t\t\t<button type=\"button\" className=\"dms-retry\" onClick={reload}>{t(\"action.reload\")}</button>\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t};\n\tconst renderFailures = (failures: DirectoryState['failures']) => failures.map((failure) => (\n\t\t<div key={failure.id} className=\"dms-warning\">\n\t\t\t<span>{t(\"warning.groupLoad\", { name: failure.name, message: failure.message })}</span>\n\t\t\t<button type=\"button\" className=\"dms-retry\" onClick={reload}>{t(\"action.reload\")}</button>\n\t\t</div>\n\t));\n\t// 列表区的两个空态:空态播报节点必须放在 role=menu 容器外(menu 内容模型\n\t// 只允许菜单节点),视觉位置由 .dms-groupsFill 的弹性占位补回(与 .dms-groups\n\t// 同款 flex 布局),空态时列表容器本身不渲染。\n\tconst noHits = hits !== null && hits.items.length === 0;\n\tconst noModels = hits === null && state.status === 'ready' && choices.length === 0;\n\treturn (\n\t\t<div ref={rootRef} className=\"dms-root\" onKeyDown={onRootKeyDown} onBlur={onBlur}>\n\t\t\t<button\n\t\t\t\tref={triggerRef}\n\t\t\t\ttype=\"button\"\n\t\t\t\tclassName=\"dms-trigger\"\n\t\t\t\taria-label={triggerAria}\n\t\t\t\taria-haspopup=\"menu\"\n\t\t\t\taria-expanded={open}\n\t\t\t\taria-controls={open ? `${id}-menu` : undefined}\n\t\t\t\ttitle={triggerTitle}\n\t\t\t\tdisabled={locked}\n\t\t\t\tonClick={() => (open ? close() : show())}\n\t\t\t>\n\t\t\t\t<span className=\"dms-triggerLabel\">{modelLabel}</span>\n\t\t\t\t{providerLabel !== undefined && <span className=\"dms-triggerProvider\">{providerLabel}</span>}\n\t\t\t\t{effortLabel !== undefined && <span className=\"dms-triggerEffort\">{effortLabel}</span>}\n\t\t\t\t<span className={`dms-chevron${open ? \" dms-chevronOpen\" : \"\"}`} aria-hidden=\"true\">\n\t\t\t\t\t{IconChevronDown}\n\t\t\t\t</span>\n\t\t\t</button>\n\t\t\t{open ? (\n\t\t\t\t<div\n\t\t\t\t\tid={`${id}-menu`}\n\t\t\t\t\tref={menuRef}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tmaxHeight: menuAbove ? menuMaxHeight : belowMaxHeight,\n\t\t\t\t\t\t...(menuLeft === undefined ? null : { left: menuLeft, right: \"auto\" }),\n\t\t\t\t\t}}\n\t\t\t\t\tclassName={'dms-menu dms-menuModel' + (menuAbove ? '' : ' dms-menuBelow')}\n\t\t\t\t\taria-busy={state.status === 'loading' || busy}\n\t\t\t\t>\n\t\t\t\t\t{state.status === 'loading' && <div className=\"dms-status\">{t('status.loading')}</div>}\n\t\t\t\t\t{renderErrorStrip()}\n\t\t\t\t\t{state.failures.length > 0 && <div className=\"dms-failures\">{renderFailures(state.failures)}</div>}\n\t\t\t\t\t<div className=\"dms-search\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tref={searchRef}\n\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\tclassName=\"dms-searchInput\"\n\t\t\t\t\t\t\tvalue={query}\n\t\t\t\t\t\t\tplaceholder={t('search.placeholder')}\n\t\t\t\t\t\t\taria-label={t('search.placeholder')}\n\t\t\t\t\t\t\taria-controls={`${id}-groups`}\n\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\tenterKeyHint=\"search\"\n\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\tonChange={(event: react.ChangeEvent<HTMLInputElement>) => {\n\t\t\t\t\t\t\t\tsetNotice(null);\n\t\t\t\t\t\t\t\tsetQuery(event.target.value);\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{query !== '' && (\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclassName=\"dms-searchClear\"\n\t\t\t\t\t\t\t\taria-label={t('search.clearAria')}\n\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\tsetNotice(null);\n\t\t\t\t\t\t\t\t\tsetQuery('');\n\t\t\t\t\t\t\t\t\tsearchRef.current?.focus();\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span aria-hidden=\"true\" className=\"dms-icon-slot\">{IconClear}</span>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t{/* 搜索结果的屏幕阅读器播报:命中总数变化时由 role=status 播报,\n\t\t\t\t\t 视觉隐藏(.dms-sr);放在 role=menu 容器外,不污染菜单内容模型。 */}\n\t\t\t\t\t{hits !== null && (\n\t\t\t\t\t\t<span className=\"dms-sr\" role=\"status\">{t('search.status', { count: String(hits.total) })}</span>\n\t\t\t\t\t)}\n\t\t\t\t\t{/* 空态(无命中/无模型)与「仅显示前 N 条」note 全部在 role=menu\n\t\t\t\t\t 容器外:menu 的直接内容模型只允许 menuitem/group 等菜单节点,\n\t\t\t\t\t live region 会被菜单导航跳过、静态说明不该冒充菜单项。视觉\n\t\t\t\t\t 位置由 .dms-groupsFill(空态)与容器后的 .dms-more 保持。 */}\n\t\t\t\t\t{noHits && (\n\t\t\t\t\t\t<div className=\"dms-empty dms-groupsFill\" role=\"status\">{t('search.noMatch', { query: query.trim() })}</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{noModels && (\n\t\t\t\t\t\t<div className=\"dms-empty dms-groupsFill\" role=\"status\">{t('empty.models')}</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{!noHits && !noModels && (\n\t\t\t\t\t<div className=\"dms-groups\" id={`${id}-groups`} role=\"menu\" aria-label={t(\"menu.aria\")}>\n\t\t\t\t\t\t{hits !== null\n\t\t\t\t\t\t\t? hits.items.map((hit) => {\n\t\t\t\t\t\t\t\tconst rowKey = `${hit.group.id}\\u0000${hit.model.id}`;\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<ModelOption\n\t\t\t\t\t\t\t\t\t\tkey={rowKey}\n\t\t\t\t\t\t\t\t\t\tgroup={hit.group}\n\t\t\t\t\t\t\t\t\t\tmodel={hit.model}\n\t\t\t\t\t\t\t\t\t\tshowProvider\n\t\t\t\t\t\t\t\t\t\tselected={state.current?.provider === hit.group.id && state.current.model === hit.model.id}\n\t\t\t\t\t\t\t\t\t\tbusy={busy}\n\t\t\t\t\t\t\t\t\t\trowKey={rowKey}\n\t\t\t\t\t\t\t\t\t\tactive={rowKey === activeRowKey}\n\t\t\t\t\t\t\t\t\t\tonRowFocus={onRowFocus}\n\t\t\t\t\t\t\t\t\t\tnameHit={hit.nameHit}\n\t\t\t\t\t\t\t\t\t\tt={t}\n\t\t\t\t\t\t\t\t\t\tonChoose={choose}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t: state.groups.map((group) => {\n\t\t\t\t\t\t\t\tconst headingId = `${id}-${group.id}`;\n\t\t\t\t\t\t\t\tconst isCollapsed = collapsed.has(group.id);\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<section key={group.id} data-group-id={group.id} role=\"group\" aria-labelledby={headingId} className=\"dms-group\">\n\t\t\t\t\t\t\t\t\t\t{/* 组头参与 roving 方向键导航(data-row-key)但不进 Tab 序\n\t\t\t\t\t\t\t\t\t\t (tabIndex=-1):折叠/展开对键盘可达,数百行却不会\n\t\t\t\t\t\t\t\t\t\t 撑爆 Tab 停靠点。role=menuitem + aria-expanded 是\n\t\t\t\t\t\t\t\t\t\t ARIA menu 模式里「可展开菜单项」的规范语义。 */}\n<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\t\t\t\t\tdata-row-key={`header:${group.id}`}\n\t\t\t\t\t\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"dms-groupHeader\"\n\t\t\t\t\t\t\t\t\t\t\taria-expanded={!isCollapsed}\n\t\t\t\t\t\t\t\t\t\t\taria-label={t('group.toggleAria', { name: group.name, count: String(group.models.length) })}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => toggleCollapse(group.id)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span className={`dms-groupChevron${isCollapsed ? ' dms-groupChevronClosed' : ''}`} aria-hidden=\"true\">\n\t\t\t\t\t\t\t\t\t\t\t\t{IconChevronDown}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t<span id={headingId} className=\"dms-groupName\">{group.name}</span>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"dms-groupCount\">{group.models.length}</span>\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t{!isCollapsed && group.models.map((model) => {\n\t\t\t\t\t\t\t\t\t\t\tconst rowKey = `${group.id}\\u0000${model.id}`;\n\t\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t\t<ModelOption\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey={rowKey}\n\t\t\t\t\t\t\t\t\t\t\t\t\tgroup={group}\n\t\t\t\t\t\t\t\t\t\t\t\t\tmodel={model}\n\t\t\t\t\t\t\t\t\t\t\t\t\tshowProvider={false}\n\t\t\t\t\t\t\t\t\t\t\t\t\tselected={state.current?.provider === group.id && state.current.model === model.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\tbusy={busy}\n\t\t\t\t\t\t\t\t\t\t\t\t\trowKey={rowKey}\n\t\t\t\t\t\t\t\t\t\t\t\t\tactive={rowKey === activeRowKey}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonRowFocus={onRowFocus}\n\t\t\t\t\t\t\t\t\t\t\t\t\tt={t}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonChoose={choose}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t</section>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{hits !== null && hits.total > MAX_VISIBLE_HITS && (\n\t\t\t\t\t\t// role=note:静态辅助说明,从菜单项语义里退出来。\n\t\t\t\t\t\t<div className=\"dms-more\" role=\"note\">{t('search.more', { shown: String(MAX_VISIBLE_HITS), total: String(hits.total) })}</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{state.current !== null && dmsSliderLevels(state).length >= 2 && (\n\t\t\t\t\t\t<div className=\"dms-effortFooter\">\n\t\t\t\t\t\t\t<span className=\"dms-effortFooterLabel\">{t('menu.effort')}</span>\n\t\t\t\t\t\t\t<EffortSlider state={state} select={select} t={t} />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{notice !== null && <div className=\"dms-notice\" role=\"status\">{notice}</div>}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t{toast !== null && (\n\t\t\t\t<Toast\n\t\t\t\t\tkey={toast.seq}\n\t\t\t\t\ttext={toast.text}\n\t\t\t\t\ticon={toast.failed ? <IconWarningOutline16 /> : undefined}\n\t\t\t\t\tanchor={rootRef.current?.closest<HTMLElement>(\"[data-composer-card]\") ?? null}\n\t\t\t\t\tonDone={() => { setToast(null); }}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</div>\n\t);\n}","/**\n* Stylesheet for the enhanced model seat.\n*\n* Injected as one `<style data-plugin=\"@bittersmilezzz/dsh-model-selector\">` tag by the client\n* apply and removed again on unload. Class names are prefixed `dms-` so they\n* cannot collide with CSS-module hashes from other plugins. Colors come only\n* from `--dsw-*` theme tokens, matching the shipped Menu material.\n*/\nexport 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/* 键盘焦点此前在这三个可聚焦控件上完全隐身(上轮只补了模型行)。 */\n.dms-searchClear:focus-visible {\n border-radius: 8px;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n/* 图标组件只收 size/className,装饰性 aria-hidden 只能包一层 span;contents 让它不占布局。 */\n.dms-icon-slot { display: contents; }\n\n.dms-groups { flex: 1 1 auto; min-height: 0; overflow-y: auto; contain: content; }\n/* 空态占位:空态播报节点在 role=menu 容器外,用与 .dms-groups 同款弹性布局\n 补回列表区本来的位置(不挤压其余栏,消息也不会贴到菜单顶部)。 */\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/* 档位读数:拖动时实时跟随 preview,替代此前只有读屏能看到的 aria-valuetext。 */\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/* 官方契约的 efforts[].description,整行展示在滑杆下方。 */\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/* 视觉隐藏工具类(SR 播报/错误原文:不占布局但可被读屏读到)。 */\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/* 搜索命中片段:名称内标下划线(命中描述/供应商时不标,见 nameHit)。 */\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/* 方向键导航把焦点落在选项上,此前没有任何焦点指示。 */\n.dms-model-option:focus-visible { background: var(--dsw-alias-interactive-bg-hover); }\n/* 当前选中行:仅靠勾号不足以在密集列表里定位。 */\n.dms-model-optionSelected { background: var(--dsw-alias-interactive-bg-hover); }\n.dms-model-optionSelected .dms-model-option-name { font-weight: 600; }\n/* select 进行中:官方以 disabled 变灰表达,这里用 aria-busy 驱动同样的视觉、\n 不夺键盘焦点(aria-disabled 保连续性,见 ModelOption)。 */\n.dms-menu[aria-busy=\"true\"] .dms-model-option { color: var(--dsw-alias-label-dimmed); }\n/* 搜索结果里的供应商标是区分跨供应商同名模型的唯一线索,之前无任何样式。 */\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`;\n","/**\n * dsh-model-selector — browser half.\n *\n * Replaces the shipped `conversation.input.model` seat with an enhanced one\n * (provider-group collapse + name search) while keeping the exact shared\n * per-session directory (`ctx.modelDirectories`) the /model popup reads, so\n * both entries stay in sync. Shadowing is the sanctioned seam: the slot is\n * `single`, so a registration at a LOWER priority than the shipped occupant\n * (default 0) becomes the rendered winner.\n */\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type { SessionId } from '@deepseek-ai/dsh-session/types'\n// Type-only: pulls the ui-renderer Context merge (ctx.slots), moved here in dsh alpha.2.\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\n// Type-only: pulls the session controller Context merge (ctx.sessions) in alpha.2.\nimport type {} from '@deepseek-ai/dsh-api-session-controller/client'\n// Type-only: pulls the ui-conversation SlotMap merge (the input model seat).\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\n// Type-only: pulls the locale plugin's Context merge (ctx.locale).\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\n// Type-only: pulls the model-selection plugin's Context merge (ctx.modelDirectories)\n// plus the injected face type the seat registration hands to the component.\nimport type { ModelSelectInjected } from '@deepseek-ai/dsh-client-ui-model-selection/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-model-selection/client'\nimport { ModelSelect, zhDict, enDict } from './ModelSelect.tsx'\nimport { CSS } from './styles.ts'\n\n/** Dictionary namespace owned by this plugin. */\nconst NS = 'modelSelector'\n\n/** Required services: the registry, session lookup, locale, the slot seat, and the model directory's Remote faces. */\nexport const inject = [\n 'slots',\n 'sessions',\n 'locale',\n 'modelDirectories',\n 'remote',\n 'remote.session',\n]\n\n/**\n * Client plugin body: register the dictionaries and stylesheet, then take the\n * model seat over the shared directory once `modelDirectories` appears.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, {\n zh: zhDict,\n en: enDict,\n }), 'dsh-model-selector: dictionaries')\n ctx.effect(() => {\n const tag = document.createElement('style')\n tag.dataset.plugin = '@bittersmilezzz/dsh-model-selector'\n tag.textContent = CSS\n document.head.appendChild(tag)\n return () => {\n tag.remove()\n }\n }, 'dsh-model-selector: styles')\n ctx.inject(['modelDirectories'], (scope) => {\n const models = scope.modelDirectories\n const sessions = scope.sessions\n // 有意的座位层重叠,勿当 bug「修掉」:官方 @deepseek-ai/dsh-client-ui-model-selection\n // 自带同名座位(packages/client/ui-model-selection/src/client/index.ts:177,\n // 未传 priority → 默认 0),本插件靠 priority: -1 遮蔽它。ui-conversation 把该座位\n // 声明为 single/session(kind: 'single'; scope: 'session'),single 槽「priority 升序\n // 取最低者渲染」,故 -1 恒胜出。两者共用同一份 ctx.modelDirectories 会话目录,\n // 任一入口的切换就是另一入口的显示——重叠是设计取舍,不是冲突。\n // 重新评估触发条件:官方若改动该座位的默认优先级、或把它从 single 改成 chain\n // (chain 每项都参与渲染,遮蔽不生效),此遮蔽即失效,不能默认仍然成立。\n scope.slots.inject('conversation.input.model', () => scope.slots.register({\n name: 'conversation.input.model',\n locale: NS,\n priority: -1,\n inject: (sessionId: string): ModelSelectInjected => {\n const sid = sessionId as SessionId\n const directory = models.directoryFor(sid)\n const available = sessions.subagentAddress(sid) === undefined\n return {\n available,\n directory: directory.store,\n load: () => {\n if (available) directory.load().catch((error: unknown) => { console.warn('[dsh-model-selector] directory.load failed:', error) })\n },\n // 保留 boolean 契约(调用方按 false 走 notice),但把真实拒绝原因留在控制台,\n // 否则用户只看到「切换失败」、线上无从定位。\n select: (selection) => available\n ? directory.select(selection).then(\n () => true,\n (error: unknown) => {\n console.warn('[dsh-model-selector] select failed:', error)\n return false\n },\n )\n : Promise.resolve(false),\n }\n },\n }, ModelSelect))\n })\n}\n"],"mappings":"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"}
@@ -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,4BA6mB9F"}
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 +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,CA8C9C"}
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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bittersmilezzz/dsh-model-selector",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "DeepSeek Harness 的增强模型选择器:单层菜单(搜索 + 分组)+ 底部内联推理强度(Effort)滑杆。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",