Files
dsh-plugin-model-enhancer/lib/client.js
T

1291 lines
44 KiB
JavaScript

window.__ModuleLoader__.load({
id: "dsh-plugin-model-enhancer",
factory: (require) => {
var module = { exports: {} };
var exports = module.exports;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name2 in all)
__defProp(target, name2, { get: all[name2], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/client/index.ts
var index_exports = {};
__export(index_exports, {
apply: () => apply,
inject: () => inject,
name: () => name
});
module.exports = __toCommonJS(index_exports);
// src/client/styles.ts
var CODEX_LEVELS_ZH = [
{ id: "off", label: "\u5173", short: "\u5173" },
{ id: "minimal", label: "\u6781\u4F4E", short: "\u6781\u4F4E" },
{ id: "low", label: "\u4F4E", short: "\u4F4E" },
{ id: "medium", label: "\u4E2D", short: "\u4E2D" },
{ id: "high", label: "\u9AD8", short: "\u9AD8" },
{ id: "xhigh", label: "\u8D85\u9AD8", short: "\u8D85\u9AD8" },
{ id: "max", label: "\u6700\u5927", short: "\u6700\u5927" }
];
var CODEX_LEVELS_EN = [
{ id: "off", label: "Off", short: "Off" },
{ id: "minimal", label: "Minimal", short: "Minimal" },
{ id: "low", label: "Low", short: "Low" },
{ id: "medium", label: "Medium", short: "Medium" },
{ id: "high", label: "High", short: "High" },
{ id: "xhigh", label: "X-High", short: "X-High" },
{ id: "max", label: "Max", short: "Max" }
];
var ENHANCER_CSS = `
/* Hide default trigger when Codex trigger is active */
div[class*="_7KE1Ra_root"], div[class*="ModelSelect_module_css_root"] {
display: none !important;
}
.me-codex-trigger {
box-sizing: border-box;
height: 28px;
padding: 0 8px;
border-radius: 14px;
border: 1px solid transparent;
background: transparent;
color: var(--dsw-alias-label-secondary, #61666b);
font-size: 13px;
font-weight: 500;
display: inline-flex;
align-items: center;
gap: 4px;
cursor: pointer;
outline: none;
transition: all 120ms ease;
user-select: none;
}
.me-codex-trigger:hover:not(:disabled),
.me-codex-trigger.active-open {
border-color: var(--dsw-alias-border-l2, rgba(0, 0, 0, 0.15));
color: var(--dsw-alias-label-primary, #0f1115);
}
body[data-ds-dark-theme] .me-codex-trigger:hover:not(:disabled),
body[data-ds-dark-theme] .me-codex-trigger.active-open {
border-color: var(--dsw-alias-border-l3, rgba(255, 255, 255, 0.2));
color: var(--dsw-alias-label-primary, #fff);
}
.me-codex-trigger-chevron {
transition: transform 140ms ease;
opacity: 0.7;
}
.me-codex-trigger-chevron.open {
transform: rotate(180deg);
}
.me-codex-popup {
position: absolute;
bottom: calc(100% + 8px);
right: 0;
width: 280px;
background: var(--dsw-specific-menu, #ffffff);
border: 1px solid var(--dsw-alias-border-l2, rgba(0, 0, 0, 0.08));
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);
border-radius: 14px;
padding: 10px 12px 12px;
z-index: 1200;
display: flex;
flex-direction: column;
gap: 8px;
}
body[data-ds-dark-theme] .me-codex-popup {
background: var(--dsw-specific-menu, #1b1d22);
border-color: var(--dsw-alias-border-l3, rgba(255, 255, 255, 0.1));
box-shadow: 0 10px 36px rgba(0, 0, 0, 0.5), 0 2px 10px rgba(0, 0, 0, 0.3);
}
.me-codex-topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.me-codex-icon-btn {
box-sizing: border-box;
width: 26px;
height: 26px;
border-radius: 6px;
border: none;
background: transparent;
color: var(--dsw-alias-label-secondary, #64748b);
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
padding: 0;
}
.me-codex-icon-btn:hover {
background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, 0.05));
color: var(--dsw-alias-label-primary, #0f172a);
}
body[data-ds-dark-theme] .me-codex-icon-btn {
color: var(--dsw-alias-label-secondary, #94a3b8);
}
body[data-ds-dark-theme] .me-codex-icon-btn:hover {
background: var(--dsw-alias-surface-l2, rgba(255, 255, 255, 0.08));
color: #fff;
}
.me-codex-center {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
user-select: none;
}
.me-codex-effort-title {
font-size: 13px;
font-weight: 600;
color: var(--dsw-alias-label-primary, #0f172a);
display: flex;
align-items: center;
gap: 3px;
}
body[data-ds-dark-theme] .me-codex-effort-title {
color: #fff;
}
.me-codex-model-name {
font-size: 11px;
color: var(--dsw-alias-label-tertiary, #64748b);
white-space: nowrap;
max-width: 170px;
overflow: hidden;
text-overflow: ellipsis;
}
.me-codex-model-name:hover {
color: #2563eb;
}
body[data-ds-dark-theme] .me-codex-model-name {
color: var(--dsw-alias-label-tertiary, #94a3b8);
}
body[data-ds-dark-theme] .me-codex-model-name:hover {
color: #60a5fa;
}
.me-slider-track-wrap {
box-sizing: border-box;
padding: 4px 0 2px;
cursor: pointer;
user-select: none;
}
.me-slider-track {
position: relative;
height: 8px;
background: var(--dsw-alias-border-l2, rgba(0, 0, 0, 0.08));
border-radius: 4px;
display: flex;
align-items: center;
}
body[data-ds-dark-theme] .me-slider-track {
background: var(--dsw-alias-border-l3, rgba(255, 255, 255, 0.12));
}
.me-slider-active-fill {
position: absolute;
left: 0;
height: 100%;
border-radius: 4px;
background: #3b82f6;
pointer-events: none;
transition: width 120ms ease;
}
.me-slider-dots-layer {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 14px;
pointer-events: none;
}
.me-slider-dot {
width: 4px;
height: 4px;
border-radius: 50%;
background: var(--dsw-alias-border-l3, rgba(0, 0, 0, 0.2));
}
body[data-ds-dark-theme] .me-slider-dot {
background: rgba(255, 255, 255, 0.25);
}
.me-slider-dot.lit {
background: #fff;
}
.me-slider-thumb {
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
width: 16px;
height: 16px;
border-radius: 50%;
background: #ffffff;
border: 2px solid #3b82f6;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
pointer-events: none;
transition: left 120ms ease;
}
body[data-ds-dark-theme] .me-slider-thumb {
background: #1e293b;
border-color: #60a5fa;
}
.me-model-picker-list {
max-height: 210px;
overflow-y: auto;
padding-right: 2px;
}
.me-model-group-section {
margin-top: 8px;
padding-top: 6px;
border-top: 1px solid var(--dsw-alias-border-l2, rgba(0, 0, 0, 0.08));
}
.me-model-group-section:first-child {
margin-top: 0;
padding-top: 0;
border-top: none;
}
.me-model-picker-group {
font-size: 11px;
color: #2563eb;
font-weight: 600;
padding: 3px 6px;
text-transform: uppercase;
letter-spacing: 0.05em;
display: inline-flex;
align-items: center;
gap: 4px;
background: rgba(37, 99, 235, 0.08);
border-radius: 4px;
margin-bottom: 4px;
}
body[data-ds-dark-theme] .me-model-picker-group {
color: #60a5fa;
background: rgba(59, 130, 246, 0.08);
}
.me-model-picker-item {
box-sizing: border-box;
width: 100%;
padding: 6px 8px;
border-radius: 6px;
background: transparent;
border: none;
color: var(--dsw-alias-label-primary, #0f1115);
font-size: 13px;
text-align: left;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
transition: all 120ms ease;
}
.me-model-picker-item:hover {
background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, 0.05));
}
.me-model-picker-item.active {
background: rgba(37, 99, 235, 0.1);
color: #2563eb;
font-weight: 500;
}
body[data-ds-dark-theme] .me-model-picker-item {
color: var(--dsw-alias-label-primary, #e2e8f0);
}
body[data-ds-dark-theme] .me-model-picker-item:hover {
background: var(--dsw-alias-surface-l2, rgba(255, 255, 255, 0.06));
}
body[data-ds-dark-theme] .me-model-picker-item.active {
background: rgba(56, 189, 248, 0.12);
color: #38bdf8;
}
/* Footer Section */
.me-footer-card {
box-sizing: border-box;
margin-top: 16px;
padding: 16px;
border-radius: 12px;
border: 1px solid var(--dsw-alias-border-l2, rgba(0, 0, 0, 0.08));
background: var(--dsw-alias-bg-module-platform, rgba(0, 0, 0, 0.02));
display: flex;
flex-direction: column;
gap: 12px;
}
body[data-ds-dark-theme] .me-footer-card {
border-color: var(--dsw-alias-border-l3, rgba(255, 255, 255, 0.1));
background: var(--dsw-alias-surface-l1, rgba(255, 255, 255, 0.02));
}
.me-footer-title {
font-size: 15px;
font-weight: 600;
color: var(--dsw-alias-label-primary, #0f1115);
display: flex;
align-items: center;
gap: 8px;
}
body[data-ds-dark-theme] .me-footer-title {
color: #fff;
}
.me-footer-desc {
font-size: 13px;
color: var(--dsw-alias-label-secondary, #61666b);
line-height: 20px;
}
.me-diff-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.me-diff-table th, .me-diff-table td {
padding: 6px 8px;
border: 1px solid var(--dsw-alias-border-l2, rgba(0, 0, 0, 0.08));
}
body[data-ds-dark-theme] .me-diff-table th, body[data-ds-dark-theme] .me-diff-table td {
border-color: var(--dsw-alias-border-l3, rgba(255, 255, 255, 0.1));
}
.me-val-new {
color: var(--dsw-alias-state-success-primary, #10b981);
font-weight: 500;
}
`;
// src/client/CodexModelSelect.tsx
var import_react = __toESM(require("react"), 1);
// src/client/i18n.ts
var I18N_DICT = {
zh: {
// ModelListEditor & Badges
batchBtn: "\u4E00\u952E\u8865\u5168",
batchBtnBusy: "\u8865\u5168\u4E2D...",
batchBtnTitle: "\u6839\u636E\u6A21\u578B ID \u4ECE models.dev \u81EA\u52A8\u5339\u914D\u5E76\u9884\u586B\u5145\u7F3A\u5931\u53C2\u6570",
singleBtn: "\u667A\u80FD\u8865\u5168",
singleBtnBusy: "\u8865\u5168\u4E2D",
singleBtnTitle: "\u4ECE models.dev \u83B7\u53D6\u8BE5\u6A21\u578B\u63A8\u8350\u53C2\u6570",
badgeVision: "\u56FE\u50CF",
badgeVisionTitle: "\u652F\u6301\u56FE\u50CF\u8F93\u5165 (Vision)",
badgeThinking: "\u601D\u8003",
badgeThinkingTitle: "\u601D\u8003\u6863\u4F4D: {levels}",
defaultEffort: "\u9ED8\u8BA4",
modalitiesTitle: "\u8F93\u5165\u6A21\u6001\u652F\u6301 (Input Modalities)",
modalityText: "\u6587\u672C\u8F93\u5165 (Text)",
modalityVision: "\u56FE\u50CF\u8F93\u5165 (Vision)",
reasoningTitle: "\u63A8\u7406\u601D\u8003\u6863\u4F4D (Reasoning Efforts)",
enableReasoning: "\u542F\u7528\u6DF1\u5EA6\u601D\u8003",
saveBlockedHint: "\u4FDD\u5B58\u53D7\u963B\u63D0\u793A\uFF1A\u6A21\u578B {index} {reason}",
fillNoIdError: "\u5217\u8868\u4E2D\u6CA1\u6709\u586B\u5199\u6A21\u578B ID \u7684\u6709\u6548\u6A21\u578B",
fillSuccessNotice: "\u4E00\u952E\u8865\u5168\u5B8C\u6210\uFF1A\u5DF2\u5339\u914D\u5E76\u66F4\u65B0 {count}/{total} \u4E2A\u6A21\u578B",
fillFailedNotice: "\u4E00\u952E\u8865\u5168\u5931\u8D25: {message}",
singleFillRequireId: "\u8BF7\u5148\u586B\u5199\u6A21\u578B ID",
singleFillNotFound: '\u672A\u5728 models.dev \u4E2D\u627E\u5230\u4E0E "{id}" \u5339\u914D\u7684\u6A21\u578B\u4FE1\u606F',
singleFillFetchFailed: "\u83B7\u53D6 models.dev \u5931\u8D25: {message}",
singleFillApplied: '\u5DF2\u9884\u586B\u5145 "{id}" \u63A8\u8350\u53C2\u6570',
// PrefillModal & Footer Settings
enhancerTitle: "Model Enhancer (\u591A\u6A21\u6001\u4E0E\u63A8\u7406\u53C2\u6570\u589E\u5F3A)",
enhancerIntro: "\u914D\u7F6E models.dev \u9884\u586B\u5145\u3001\u8F93\u5165\u6A21\u6001\u4E0E\u63A8\u7406\u601D\u8003\u6863\u4F4D\uFF0C\u5E76\u53EF\u9009\u62E9\u76EE\u6807 Provider \u4E00\u952E\u5E94\u7528\u589E\u5F3A\u914D\u7F6E\u3002",
targetProvider: "\u76EE\u6807 Provider\uFF1A",
refreshSettings: "\u5237\u65B0\u914D\u7F6E",
activeEnhancerNotice: "\u5F53\u524D\u6B63\u5728\u5BF9\u300C{provider}\u300D\u751F\u6548\u589E\u5F3A\u529F\u80FD\u3002",
modalTitle: "models.dev \u9884\u586B\u5145\u786E\u8BA4\u4E0E\u9009\u62E9",
matchedModelLabel: "\u68C0\u7D22\u5230\u5339\u914D\u6A21\u578B\uFF1A",
thSelect: "\u9009\u7528",
thField: "\u914D\u7F6E\u9879",
thCurrent: "\u5F53\u524D\u8BBE\u7F6E",
thRecommended: "models.dev \u63A8\u8350\u503C",
fieldName: "\u663E\u793A\u540D\u79F0",
fieldContext: "\u4E0A\u4E0B\u6587\u957F\u5EA6",
fieldMaxTokens: "\u6700\u5927\u8F93\u51FA",
fieldInput: "\u56FE\u50CF\u8F93\u5165",
fieldReasoning: "\u63A8\u7406\u601D\u8003\u7B49\u7EA7",
fieldUnset: "(\u672A\u8BBE\u7F6E)",
fieldDefault: "(\u9ED8\u8BA4)",
visionSupported: "\u652F\u6301\u56FE\u50CF",
visionOnlyText: "\u4EC5\u6587\u672C",
thinkingOff: "\u5173\u95ED\u601D\u8003",
thinkingOn: "\u5F00\u542F",
cancel: "\u53D6\u6D88",
applySelected: "\u5E94\u7528\u5DF2\u9009\u63A8\u8350\u9879",
// Codex Slider
effortOff: "\u5173",
effortMinimal: "\u6781\u4F4E",
effortLow: "\u4F4E",
effortMedium: "\u4E2D",
effortHigh: "\u9AD8",
effortXHigh: "\u8D85\u9AD8",
effortMax: "\u6700\u5927",
effortReasoningTitle: "\u63A8\u7406\u5F3A\u5EA6",
effortSelectModelTip: "\u70B9\u51FB\u5207\u6362\u6A21\u578B",
effortResetTip: "\u6062\u590D\u9ED8\u8BA4\u6863\u4F4D",
effortChooseModel: "\u9009\u62E9\u6A21\u578B",
commandsTitle: "\u547D\u4EE4\u83DC\u5355 (/)"
},
en: {
// ModelListEditor & Badges
batchBtn: "Auto-Fill All",
batchBtnBusy: "Filling\u2026",
batchBtnTitle: "Auto-match and prefill missing parameters from models.dev",
singleBtn: "Auto-Fill",
singleBtnBusy: "Filling\u2026",
singleBtnTitle: "Fetch recommended parameters for this model from models.dev",
badgeVision: "Vision",
badgeVisionTitle: "Supports Image/Vision input",
badgeThinking: "Reasoning",
badgeThinkingTitle: "Reasoning efforts: {levels}",
defaultEffort: "Default",
modalitiesTitle: "Input Modalities",
modalityText: "Text Input",
modalityVision: "Vision Input",
reasoningTitle: "Reasoning Efforts",
enableReasoning: "Enable Deep Reasoning",
saveBlockedHint: "Save blocked: Model {index} {reason}",
fillNoIdError: "No valid models with a Model ID found in the list",
fillSuccessNotice: "Auto-fill complete: matched & updated {count}/{total} models",
fillFailedNotice: "Auto-fill failed: {message}",
singleFillRequireId: "Please enter a Model ID first",
singleFillNotFound: 'No matching model found on models.dev for "{id}"',
singleFillFetchFailed: "Failed to fetch models.dev: {message}",
singleFillApplied: 'Prefilled recommended parameters for "{id}"',
// PrefillModal & Footer Settings
enhancerTitle: "Model Enhancer Settings",
enhancerIntro: "Configure models.dev auto-fill, input modalities and reasoning effort levels across providers.",
targetProvider: "Target Provider:",
refreshSettings: "Refresh",
activeEnhancerNotice: 'Enhancer active on "{provider}".',
modalTitle: "models.dev Prefill Confirmation",
matchedModelLabel: "Matched Model: ",
thSelect: "Apply",
thField: "Property",
thCurrent: "Current Setting",
thRecommended: "models.dev Value",
fieldName: "Display Name",
fieldContext: "Context Window",
fieldMaxTokens: "Max Output Tokens",
fieldInput: "Vision Input",
fieldReasoning: "Reasoning Efforts",
fieldUnset: "(Unset)",
fieldDefault: "(Default)",
visionSupported: "Vision Supported",
visionOnlyText: "Text Only",
thinkingOff: "Disabled",
thinkingOn: "Enabled",
cancel: "Cancel",
applySelected: "Apply Selected",
// Codex Slider
effortOff: "Off",
effortMinimal: "Minimal",
effortLow: "Low",
effortMedium: "Medium",
effortHigh: "High",
effortXHigh: "X-High",
effortMax: "Max",
effortReasoningTitle: "Reasoning Effort",
effortSelectModelTip: "Click to switch model",
effortResetTip: "Reset to default effort",
effortChooseModel: "Select model",
commandsTitle: "Commands (/)"
}
};
function isZhLocale() {
if (typeof navigator !== "undefined") {
const lang = (navigator.language || "").toLowerCase();
if (lang.startsWith("zh")) return true;
}
return false;
}
function getTranslation(key, isZh = isZhLocale(), params) {
const dict = isZh ? I18N_DICT.zh : I18N_DICT.en;
let str = dict[key] || I18N_DICT.en[key] || key;
if (params) {
for (const [k, v] of Object.entries(params)) {
str = str.replace(new RegExp(`\\{${k}\\}`, "g"), String(v));
}
}
return str;
}
// src/client/CodexModelSelect.tsx
function CodexModelSelect(props) {
const { directory, load, select, available, locked } = props;
const state = (0, import_react.useSyncExternalStore)(directory.subscribe, directory.getSnapshot);
const [open, setOpen] = (0, import_react.useState)(false);
const [showModelPicker, setShowModelPicker] = (0, import_react.useState)(false);
const rootRef = (0, import_react.useRef)(null);
const isZh = isZhLocale();
const codexLevels = isZh ? CODEX_LEVELS_ZH : CODEX_LEVELS_EN;
(0, import_react.useEffect)(() => {
if (available) load();
}, [available]);
(0, import_react.useEffect)(() => {
if (!open) return;
const onMouseDown = (e) => {
if (!rootRef.current?.contains(e.target)) {
setOpen(false);
setShowModelPicker(false);
}
};
document.addEventListener("mousedown", onMouseDown);
return () => document.removeEventListener("mousedown", onMouseDown);
}, [open]);
const choices = (0, import_react.useMemo)(() => (state.groups || []).flatMap((g) => (g.models || []).map((m) => ({
group: g,
model: m
}))), [state.groups]);
const currentChoice = choices.find((c) => c.group.id === state.current?.provider && c.model.id === state.current?.model);
const currentModel = currentChoice?.model;
const reasoning = currentModel?.reasoning;
const availableEfforts = (0, import_react.useMemo)(() => {
if (!reasoning || !reasoning.efforts || reasoning.efforts.length === 0) return [];
return reasoning.efforts.map((e) => {
const found = codexLevels.find((l) => l.id === e.id);
return {
id: e.id,
label: found?.label || e.name || e.id,
short: found?.short || e.name || e.id
};
});
}, [reasoning, isZh]);
const effectiveEffort = state.current?.reasoningEffort ?? reasoning?.defaultEffort;
const currentEffortIndex = (0, import_react.useMemo)(() => {
if (availableEfforts.length === 0) return 0;
const idx = availableEfforts.findIndex((e) => e.id === effectiveEffort);
return idx === -1 ? 0 : idx;
}, [availableEfforts, effectiveEffort]);
const currentEffortObj = availableEfforts[currentEffortIndex];
const effortDisplayLabel = currentEffortObj ? currentEffortObj.label : isZh ? "\u4E2D" : "Medium";
const triggerModelName = currentModel?.name || (isZh ? "\u9009\u62E9\u6A21\u578B" : "Select model");
const triggerEffortLabel = reasoning ? currentEffortObj ? currentEffortObj.short : isZh ? "\u9ED8\u8BA4" : "Default" : null;
const onSelectEffortByIndex = (idx) => {
if (!currentChoice || !availableEfforts[idx]) return;
const effort = availableEfforts[idx].id;
select({
provider: currentChoice.group.id,
model: currentChoice.model.id,
reasoningEffort: effort
});
};
const onResetDefault = () => {
if (!currentChoice) return;
select({
provider: currentChoice.group.id,
model: currentChoice.model.id,
reasoningEffort: reasoning?.defaultEffort
});
};
const onTrackClick = (e) => {
if (availableEfforts.length <= 1) return;
const rect = e.currentTarget.getBoundingClientRect();
const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
const idx = Math.round(ratio * (availableEfforts.length - 1));
onSelectEffortByIndex(idx);
};
if (!available) return null;
return import_react.default.createElement(
"div",
{
ref: rootRef,
style: { position: "relative", display: "inline-flex", alignItems: "center" }
},
import_react.default.createElement(
"button",
{
type: "button",
className: `me-codex-trigger ${open ? "active-open" : ""}`,
disabled: locked,
onClick: () => setOpen(!open)
},
import_react.default.createElement("span", null, triggerModelName),
triggerEffortLabel ? import_react.default.createElement("span", {
style: { color: "#3b82f6", fontWeight: 600 }
}, "\xB7 ", triggerEffortLabel) : null,
import_react.default.createElement("svg", {
className: `me-codex-trigger-chevron ${open ? "open" : ""}`,
width: "12",
height: "12",
viewBox: "0 0 16 16",
fill: "none",
stroke: "currentColor",
strokeWidth: "1.8"
}, import_react.default.createElement("path", { d: "M4 6l4 4 4-4" }))
),
open ? import_react.default.createElement(
"div",
{
className: "me-codex-popup"
},
import_react.default.createElement(
"div",
{
className: "me-codex-topbar"
},
import_react.default.createElement(
"div",
{
className: "me-codex-icon-btn",
title: isZh ? "\u63A8\u7406\u5F3A\u5EA6" : "Reasoning Effort"
},
import_react.default.createElement("svg", {
width: "16",
height: "16",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: "2"
}, import_react.default.createElement("path", { d: "M13 2L3 14h9l-1 8 10-12h-9l1-8z" }))
),
import_react.default.createElement(
"div",
{
className: "me-codex-center",
onClick: () => setShowModelPicker(!showModelPicker),
title: isZh ? "\u70B9\u51FB\u5207\u6362\u6A21\u578B" : "Click to switch model"
},
import_react.default.createElement(
"div",
{
className: "me-codex-effort-title"
},
effortDisplayLabel,
import_react.default.createElement("svg", {
width: "12",
height: "12",
viewBox: "0 0 16 16",
fill: "none",
stroke: "currentColor",
strokeWidth: "2",
style: { transform: showModelPicker ? "rotate(90deg)" : void 0, transition: "transform 140ms ease" }
}, import_react.default.createElement("path", { d: "M6 3.5L10.5 8L6 12.5" }))
),
import_react.default.createElement("div", {
className: "me-codex-model-name"
}, currentModel?.name || (isZh ? "\u9009\u62E9\u6A21\u578B" : "Select model"))
),
import_react.default.createElement(
"button",
{
type: "button",
className: "me-codex-icon-btn",
title: isZh ? "\u6062\u590D\u9ED8\u8BA4\u6863\u4F4D" : "Reset to default effort",
onClick: onResetDefault
},
import_react.default.createElement("svg", {
width: "16",
height: "16",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: "2"
}, import_react.default.createElement("path", { d: "M3 12a9 9 0 109-9 9.75 9.75 0 00-6.74 2.74L3 8m0 0V3m0 5h5" }))
)
),
showModelPicker ? import_react.default.createElement(
"div",
{
className: "me-model-picker-list"
},
(state.groups || []).map((group) => import_react.default.createElement(
"div",
{
key: group.id,
className: "me-model-group-section"
},
import_react.default.createElement(
"div",
{
className: "me-model-picker-group"
},
import_react.default.createElement("svg", {
width: "12",
height: "12",
viewBox: "0 0 16 16",
fill: "none",
stroke: "currentColor",
strokeWidth: "1.8"
}, import_react.default.createElement("path", { d: "M2 4h12M2 8h12M2 12h8" })),
group.name
),
(group.models || []).map((m) => {
const active = state.current?.provider === group.id && state.current?.model === m.id;
return import_react.default.createElement(
"button",
{
key: m.id,
type: "button",
className: `me-model-picker-item ${active ? "active" : ""}`,
onClick: () => {
select({
provider: group.id,
model: m.id,
reasoningEffort: m.reasoning?.defaultEffort
});
setShowModelPicker(false);
}
},
import_react.default.createElement("span", null, m.name),
active ? import_react.default.createElement("svg", {
width: "14",
height: "14",
viewBox: "0 0 16 16",
fill: "none",
stroke: "#38bdf8",
strokeWidth: "2"
}, import_react.default.createElement("path", { d: "M3.5 8.5l3 3 6-6" })) : null
);
})
))
) : import_react.default.createElement(
"div",
{
className: "me-slider-track-wrap",
onClick: onTrackClick
},
import_react.default.createElement(
"div",
{
className: "me-slider-track"
},
import_react.default.createElement("div", {
className: "me-slider-active-fill",
style: {
width: availableEfforts.length <= 1 ? "100%" : `calc(16px + (100% - 32px) * ${currentEffortIndex / (availableEfforts.length - 1)})`
}
}),
import_react.default.createElement(
"div",
{
className: "me-slider-dots-layer"
},
availableEfforts.map((eff, i) => import_react.default.createElement("div", {
key: eff.id,
className: `me-slider-dot ${i <= currentEffortIndex ? "lit" : ""}`
}))
),
import_react.default.createElement("div", {
className: "me-slider-thumb",
style: {
left: availableEfforts.length <= 1 ? "calc(100% - 16px)" : `calc(16px + (100% - 32px) * ${currentEffortIndex / (availableEfforts.length - 1)})`
}
})
)
)
) : null
);
}
// src/client/ModelsFooterSettings.tsx
var import_react2 = __toESM(require("react"), 1);
// src/client/modelsDev.ts
var THINKING_LEVELS_ALL = ["minimal", "low", "medium", "high", "xhigh", "max"];
var modelsDevGlobalCache = null;
var modelsDevGlobalPromise = null;
async function fetchAllModelsDev() {
if (modelsDevGlobalCache) return modelsDevGlobalCache;
if (modelsDevGlobalPromise) return modelsDevGlobalPromise;
modelsDevGlobalPromise = (async () => {
const endpoints = ["/api/models-dev/models", "https://models.dev/api.json"];
let lastError = null;
for (const url of endpoints) {
try {
const res = await fetch(url);
if (res.ok) {
const json = await res.json();
const list = [];
for (const [pId, p] of Object.entries(json)) {
if (p && p.models) {
for (const [mId, m] of Object.entries(p.models)) {
list.push({
providerKey: pId,
providerName: p.name || pId,
id: m.id || mId,
rawKey: mId,
...m
});
}
}
}
modelsDevGlobalCache = list;
return list;
}
} catch (e) {
lastError = e;
}
}
throw lastError || new Error("Failed to load models.dev");
})();
try {
return await modelsDevGlobalPromise;
} finally {
modelsDevGlobalPromise = null;
}
}
function normModelKey(key) {
if (!key || typeof key !== "string") return "";
return key.toLowerCase().replace(/^[a-z0-9_-]+\//, "").replace(/[-_.:/]/g, "").replace(/\d{8}$/, "");
}
function findDevModel(queryId, allList) {
if (!queryId || !allList) return null;
const q = queryId.trim().toLowerCase();
const qNorm = normModelKey(q);
let m = allList.find((x) => x.id?.toLowerCase() === q || x.rawKey?.toLowerCase() === q);
if (m) return m;
m = allList.find((x) => x.id?.split("/").pop()?.toLowerCase() === q);
if (m) return m;
m = allList.find((x) => normModelKey(x.id) === qNorm || normModelKey(x.name || "") === qNorm);
if (m) return m;
m = allList.find((x) => {
const n = normModelKey(x.id);
return n.length >= 3 && (n.includes(qNorm) || qNorm.includes(n));
});
return m || null;
}
// src/client/ModelsFooterSettings.tsx
function ModelsFooterSettings({ ctx }) {
const isZh = isZhLocale();
const [loading, setLoading] = (0, import_react2.useState)(false);
const [providerList, setProviderList] = (0, import_react2.useState)([]);
const [selectedProvider, setSelectedProvider] = (0, import_react2.useState)("");
const [statusMsg, setStatusMsg] = (0, import_react2.useState)(null);
const [batching, setBatching] = (0, import_react2.useState)(false);
const loadProviders = async () => {
try {
setLoading(true);
const res = await ctx.remote?.llm?.listConfigurableProviders?.();
if (res?.ok && Array.isArray(res.value)) {
setProviderList(res.value);
if (res.value.length > 0 && !selectedProvider) {
setSelectedProvider(res.value[0].provider);
}
}
} catch (e) {
console.error("[model-enhancer] Failed to list providers:", e);
} finally {
setLoading(false);
}
};
(0, import_react2.useEffect)(() => {
loadProviders();
}, []);
const handleBatchDevMatch = async () => {
if (!selectedProvider) return;
setBatching(true);
setStatusMsg(null);
try {
const descRes = await ctx.remote?.settings?.describe?.();
const namespaces = descRes?.ok ? descRes.value.namespaces : [];
const piAiNs = namespaces.find((n) => n.ns === "llm-pi-ai");
if (!piAiNs) {
setStatusMsg({ type: "err", text: "llm-pi-ai settings namespace not found." });
return;
}
const provConfig = piAiNs.value?.providers?.[selectedProvider] || {};
const models = Array.isArray(provConfig.models) ? provConfig.models : [];
if (models.length === 0) {
setStatusMsg({ type: "err", text: getTranslation("fillNoIdError", isZh) });
return;
}
const allDevModels = await fetchAllModelsDev();
let count = 0;
const nextModels = models.map((m) => {
if (!m || !m.id) return m;
const matched = findDevModel(m.id, allDevModels);
if (!matched) return m;
count++;
const hasImage = Boolean(matched.modalities?.input?.includes("image"));
const hasReasoning = Boolean(matched.reasoning);
let reasoningLevels = void 0;
if (hasReasoning) {
reasoningLevels = { off: "none" };
let added = false;
if (Array.isArray(matched.reasoning_options)) {
for (const opt of matched.reasoning_options) {
if (opt.type === "effort" && Array.isArray(opt.values)) {
for (const val of opt.values) {
if (val === "none" || val === "off") reasoningLevels.off = "none";
else if (THINKING_LEVELS_ALL.includes(val)) {
reasoningLevels[val] = val;
added = true;
}
}
}
}
}
if (!added) {
reasoningLevels.low = "low";
reasoningLevels.medium = "medium";
reasoningLevels.high = "high";
}
}
return {
...m,
name: m.name || matched.name,
contextWindow: m.contextWindow || matched.limit?.context,
maxTokens: m.maxTokens || matched.limit?.output,
input: m.input || (hasImage ? ["text", "image"] : ["text"]),
reasoningEfforts: m.reasoningEfforts !== void 0 ? m.reasoningEfforts : hasReasoning ? reasoningLevels : false
};
});
const ops = [{
op: "set",
path: ["providers", selectedProvider, "models"],
value: nextModels
}];
const mutateRes = await ctx.remote.settings.mutate("llm-pi-ai", ops, piAiNs.revision);
if (mutateRes?.ok) {
setStatusMsg({
type: "ok",
text: getTranslation("fillSuccessNotice", isZh, { count, total: models.length })
});
} else {
setStatusMsg({
type: "err",
text: mutateRes?.error?.message || "Failed to mutate settings"
});
}
} catch (err) {
setStatusMsg({
type: "err",
text: err.message || String(err)
});
} finally {
setBatching(false);
}
};
return import_react2.default.createElement(
"div",
{
className: "me-footer-card"
},
import_react2.default.createElement(
"div",
{
className: "me-footer-title"
},
import_react2.default.createElement("svg", {
width: "18",
height: "18",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: "2"
}, import_react2.default.createElement("path", { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" })),
import_react2.default.createElement("span", null, getTranslation("enhancerTitle", isZh))
),
import_react2.default.createElement("div", {
className: "me-footer-desc"
}, getTranslation("enhancerIntro", isZh)),
import_react2.default.createElement(
"div",
{
style: { display: "flex", alignItems: "center", gap: "12px", flexWrap: "wrap" }
},
import_react2.default.createElement(
"label",
{
style: { display: "inline-flex", alignItems: "center", gap: "6px", fontSize: "13px", fontWeight: 500 }
},
import_react2.default.createElement("span", null, getTranslation("targetProvider", isZh)),
import_react2.default.createElement(
"select",
{
value: selectedProvider,
onChange: (e) => setSelectedProvider(e.target.value),
style: {
height: "32px",
borderRadius: "6px",
border: "1px solid var(--dsw-alias-border-l2, #ccc)",
background: "var(--dsw-alias-bg-module-platform, #fff)",
color: "var(--dsw-alias-label-primary, #000)",
padding: "0 8px",
fontSize: "13px"
}
},
providerList.map((p) => import_react2.default.createElement("option", { key: p.provider, value: p.provider }, `${p.displayName || p.provider} (${p.settingsNs || "generic"})`))
)
),
import_react2.default.createElement(
"button",
{
type: "button",
disabled: batching || !selectedProvider,
onClick: handleBatchDevMatch,
style: {
height: "32px",
borderRadius: "16px",
border: "none",
background: "#2563eb",
color: "#fff",
padding: "0 14px",
fontSize: "13px",
fontWeight: 500,
cursor: batching || !selectedProvider ? "not-allowed" : "pointer",
opacity: batching || !selectedProvider ? 0.6 : 1,
display: "inline-flex",
alignItems: "center",
gap: "6px"
}
},
batching ? getTranslation("batchBtnBusy", isZh) : getTranslation("batchBtn", isZh)
),
import_react2.default.createElement("button", {
type: "button",
disabled: loading,
onClick: loadProviders,
style: {
height: "32px",
borderRadius: "16px",
border: "1px solid var(--dsw-alias-border-l2, #ccc)",
background: "transparent",
color: "var(--dsw-alias-label-secondary, #666)",
padding: "0 12px",
fontSize: "13px",
cursor: "pointer"
}
}, getTranslation("refreshSettings", isZh))
),
statusMsg ? import_react2.default.createElement("div", {
style: {
fontSize: "12px",
padding: "6px 10px",
borderRadius: "6px",
background: statusMsg.type === "ok" ? "rgba(16, 185, 129, 0.1)" : "rgba(239, 68, 68, 0.1)",
color: statusMsg.type === "ok" ? "#10b981" : "#ef4444"
}
}, statusMsg.text) : null
);
}
// src/client/ProviderCardExtras.tsx
var import_react3 = __toESM(require("react"), 1);
function ProviderCardExtras({ provider, configured, keyConfigured, ctx }) {
const isZh = isZhLocale();
const [matching, setMatching] = (0, import_react3.useState)(false);
const [notice, setNotice] = (0, import_react3.useState)(null);
if (provider.settingsNs !== "llm-pi-ai") {
return null;
}
const handleAutoFillProvider = async () => {
setMatching(true);
setNotice(null);
try {
const descRes = await ctx.remote?.settings?.describe?.();
const namespaces = descRes?.ok ? descRes.value.namespaces : [];
const piAiNs = namespaces.find((n) => n.ns === "llm-pi-ai");
if (!piAiNs) {
setNotice({ type: "err", text: "llm-pi-ai namespace missing." });
return;
}
const provKey = provider.provider;
const provConfig = piAiNs.value?.providers?.[provKey] || {};
const models = Array.isArray(provConfig.models) ? provConfig.models : [];
if (models.length === 0) {
setNotice({ type: "err", text: getTranslation("fillNoIdError", isZh) });
return;
}
const allDevModels = await fetchAllModelsDev();
let count = 0;
const nextModels = models.map((m) => {
if (!m || !m.id) return m;
const matched = findDevModel(m.id, allDevModels);
if (!matched) return m;
count++;
const hasImage = Boolean(matched.modalities?.input?.includes("image"));
const hasReasoning = Boolean(matched.reasoning);
let reasoningLevels = void 0;
if (hasReasoning) {
reasoningLevels = { off: "none" };
let added = false;
if (Array.isArray(matched.reasoning_options)) {
for (const opt of matched.reasoning_options) {
if (opt.type === "effort" && Array.isArray(opt.values)) {
for (const val of opt.values) {
if (val === "none" || val === "off") reasoningLevels.off = "none";
else if (THINKING_LEVELS_ALL.includes(val)) {
reasoningLevels[val] = val;
added = true;
}
}
}
}
}
if (!added) {
reasoningLevels.low = "low";
reasoningLevels.medium = "medium";
reasoningLevels.high = "high";
}
}
return {
...m,
name: m.name || matched.name,
contextWindow: m.contextWindow || matched.limit?.context,
maxTokens: m.maxTokens || matched.limit?.output,
input: m.input || (hasImage ? ["text", "image"] : ["text"]),
reasoningEfforts: m.reasoningEfforts !== void 0 ? m.reasoningEfforts : hasReasoning ? reasoningLevels : false
};
});
const ops = [{
op: "set",
path: ["providers", provKey, "models"],
value: nextModels
}];
const mutateRes = await ctx.remote.settings.mutate("llm-pi-ai", ops, piAiNs.revision);
if (mutateRes?.ok) {
setNotice({
type: "ok",
text: getTranslation("fillSuccessNotice", isZh, { count, total: models.length })
});
} else {
setNotice({
type: "err",
text: mutateRes?.error?.message || "Mutation rejected"
});
}
} catch (e) {
setNotice({ type: "err", text: e.message || String(e) });
} finally {
setMatching(false);
}
};
return import_react3.default.createElement(
"div",
{
style: {
padding: "8px 12px",
marginTop: "4px",
background: "var(--dsw-alias-bg-module-platform, rgba(0, 0, 0, 0.02))",
borderRadius: "8px",
border: "1px dashed var(--dsw-alias-border-l2, rgba(0, 0, 0, 0.1))",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
gap: "8px"
}
},
import_react3.default.createElement("span", {
style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #666)" }
}, getTranslation("activeEnhancerNotice", isZh, { provider: provider.displayName })),
import_react3.default.createElement("button", {
type: "button",
disabled: matching,
onClick: handleAutoFillProvider,
style: {
height: "26px",
padding: "0 10px",
borderRadius: "13px",
border: "none",
background: "#2563eb",
color: "#fff",
fontSize: "12px",
fontWeight: 500,
cursor: matching ? "not-allowed" : "pointer",
opacity: matching ? 0.7 : 1
}
}, matching ? getTranslation("singleBtnBusy", isZh) : getTranslation("batchBtn", isZh)),
notice ? import_react3.default.createElement("span", {
style: {
fontSize: "11px",
color: notice.type === "ok" ? "#10b981" : "#ef4444",
marginLeft: "8px"
}
}, notice.text) : null
);
}
// src/client/index.ts
var name = "dsh-plugin-model-enhancer";
var inject = [
"slots",
"remote",
"remote.settings",
"remote.llm",
"modelDirectories",
"sessions"
];
function apply(ctx) {
if (typeof document !== "undefined" && !document.querySelector("style[data-plugin='dsh-model-enhancer']")) {
const tag = document.createElement("style");
tag.dataset.plugin = "dsh-model-enhancer";
tag.textContent = ENHANCER_CSS;
document.head.appendChild(tag);
}
ctx.inject(["slots"], (scope) => {
const sessions = ctx.get("sessions");
scope.slots.inject("conversation.input.right", () => scope.slots.register({
name: "conversation.input.right",
id: "me-codex-model-selector",
order: 100,
inject: (sessionId) => {
const dirSvc = ctx.get("modelDirectories");
const directory = dirSvc ? dirSvc.directoryFor(sessionId) : null;
const available = sessions ? sessions.subagentAddress(sessionId) === void 0 : true;
return {
available: available && Boolean(directory),
directory: directory ? directory.store : {
subscribe: () => () => {
},
getSnapshot: () => ({ status: "idle", groups: [], current: null })
},
load: () => {
if (available && directory) directory.load().catch(() => {
});
},
select: (selection) => available && directory ? directory.select(selection).then(() => true, () => false) : Promise.resolve(false)
};
}
}, CodexModelSelect));
});
ctx.inject(["slots"], (scope) => {
scope.slots.inject("settings.models.footer", () => scope.slots.register({
name: "settings.models.footer",
id: "me-models-footer",
order: 50,
inject: () => ({ ctx })
}, ModelsFooterSettings));
});
ctx.inject(["slots"], (scope) => {
scope.slots.inject("settings.models.provider-card", () => scope.slots.register({
name: "settings.models.provider-card",
key: "llm-pi-ai",
inject: (ownerProps) => ({
...ownerProps,
ctx
})
}, ProviderCardExtras));
});
}
return module.exports;
}
});