2026-09-09 00:52:52 +08:00
|
|
|
window.__ModuleLoader__.load({
|
2026-09-11 13:58:29 +08:00
|
|
|
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 ENHANCER_CSS = `
|
|
|
|
|
.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;
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
|
2026-09-11 21:45:40 +08:00
|
|
|
// src/client/ModelsFooterSettings.tsx
|
2026-09-11 13:58:29 +08:00
|
|
|
var import_react = __toESM(require("react"), 1);
|
|
|
|
|
|
2026-09-11 21:45:40 +08:00
|
|
|
// 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 sanitizePositiveInt(val) {
|
|
|
|
|
if (typeof val === "number" && Number.isFinite(val) && Math.floor(val) === val && val >= 1) {
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
if (typeof val === "string" && /^\d+$/.test(val.trim())) {
|
|
|
|
|
const parsed = parseInt(val.trim(), 10);
|
|
|
|
|
if (Number.isFinite(parsed) && parsed >= 1) {
|
|
|
|
|
return parsed;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return void 0;
|
|
|
|
|
}
|
|
|
|
|
function mergeModelCapabilities(m, matched) {
|
|
|
|
|
if (!m) return m;
|
|
|
|
|
if (!matched) {
|
|
|
|
|
const result2 = { ...m };
|
|
|
|
|
const validExistingContext = sanitizePositiveInt(m.contextWindow);
|
|
|
|
|
if (validExistingContext !== void 0) {
|
|
|
|
|
result2.contextWindow = validExistingContext;
|
|
|
|
|
} else {
|
|
|
|
|
delete result2.contextWindow;
|
|
|
|
|
}
|
|
|
|
|
const validExistingMaxTokens = sanitizePositiveInt(m.maxTokens);
|
|
|
|
|
if (validExistingMaxTokens !== void 0) {
|
|
|
|
|
result2.maxTokens = validExistingMaxTokens;
|
|
|
|
|
} else {
|
|
|
|
|
delete result2.maxTokens;
|
|
|
|
|
}
|
|
|
|
|
return result2;
|
|
|
|
|
}
|
|
|
|
|
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";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const result = {
|
|
|
|
|
...m,
|
|
|
|
|
name: m.name || matched.name,
|
|
|
|
|
input: m.input || (hasImage ? ["text", "image"] : ["text"]),
|
|
|
|
|
reasoningEfforts: m.reasoningEfforts !== void 0 ? m.reasoningEfforts : hasReasoning ? reasoningLevels : false
|
|
|
|
|
};
|
|
|
|
|
const resolvedContext = sanitizePositiveInt(m.contextWindow) ?? sanitizePositiveInt(matched.limit?.context);
|
|
|
|
|
if (resolvedContext !== void 0) {
|
|
|
|
|
result.contextWindow = resolvedContext;
|
|
|
|
|
} else {
|
|
|
|
|
delete result.contextWindow;
|
|
|
|
|
}
|
|
|
|
|
const resolvedMaxTokens = sanitizePositiveInt(m.maxTokens) ?? sanitizePositiveInt(matched.limit?.output);
|
|
|
|
|
if (resolvedMaxTokens !== void 0) {
|
|
|
|
|
result.maxTokens = resolvedMaxTokens;
|
|
|
|
|
} else {
|
|
|
|
|
delete result.maxTokens;
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-11 13:58:29 +08:00
|
|
|
// 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",
|
2026-09-11 21:45:40 +08:00
|
|
|
applySelected: "\u5E94\u7528\u5DF2\u9009\u63A8\u8350\u9879"
|
2026-09-11 13:58:29 +08:00
|
|
|
},
|
|
|
|
|
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",
|
2026-09-11 21:45:40 +08:00
|
|
|
applySelected: "Apply Selected"
|
2026-09-11 13:58:29 +08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
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/ModelsFooterSettings.tsx
|
|
|
|
|
function ModelsFooterSettings({ ctx }) {
|
|
|
|
|
const isZh = isZhLocale();
|
2026-09-11 21:45:40 +08:00
|
|
|
const [loading, setLoading] = (0, import_react.useState)(false);
|
|
|
|
|
const [providerList, setProviderList] = (0, import_react.useState)([]);
|
|
|
|
|
const [selectedProvider, setSelectedProvider] = (0, import_react.useState)("");
|
|
|
|
|
const [statusMsg, setStatusMsg] = (0, import_react.useState)(null);
|
|
|
|
|
const [batching, setBatching] = (0, import_react.useState)(false);
|
2026-09-11 13:58:29 +08:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
};
|
2026-09-11 21:45:40 +08:00
|
|
|
(0, import_react.useEffect)(() => {
|
2026-09-11 13:58:29 +08:00
|
|
|
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);
|
2026-09-11 21:45:40 +08:00
|
|
|
if (matched) count++;
|
|
|
|
|
return mergeModelCapabilities(m, matched);
|
2026-09-11 13:58:29 +08:00
|
|
|
});
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
};
|
2026-09-11 21:45:40 +08:00
|
|
|
return import_react.default.createElement(
|
2026-09-11 13:58:29 +08:00
|
|
|
"div",
|
|
|
|
|
{
|
|
|
|
|
className: "me-footer-card"
|
|
|
|
|
},
|
2026-09-11 21:45:40 +08:00
|
|
|
import_react.default.createElement(
|
2026-09-11 13:58:29 +08:00
|
|
|
"div",
|
|
|
|
|
{
|
|
|
|
|
className: "me-footer-title"
|
|
|
|
|
},
|
2026-09-11 21:45:40 +08:00
|
|
|
import_react.default.createElement("svg", {
|
2026-09-11 13:58:29 +08:00
|
|
|
width: "18",
|
|
|
|
|
height: "18",
|
|
|
|
|
viewBox: "0 0 24 24",
|
|
|
|
|
fill: "none",
|
|
|
|
|
stroke: "currentColor",
|
|
|
|
|
strokeWidth: "2"
|
2026-09-11 21:45:40 +08:00
|
|
|
}, import_react.default.createElement("path", { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" })),
|
|
|
|
|
import_react.default.createElement("span", null, getTranslation("enhancerTitle", isZh))
|
2026-09-11 13:58:29 +08:00
|
|
|
),
|
2026-09-11 21:45:40 +08:00
|
|
|
import_react.default.createElement("div", {
|
2026-09-11 13:58:29 +08:00
|
|
|
className: "me-footer-desc"
|
|
|
|
|
}, getTranslation("enhancerIntro", isZh)),
|
2026-09-11 21:45:40 +08:00
|
|
|
import_react.default.createElement(
|
2026-09-11 13:58:29 +08:00
|
|
|
"div",
|
|
|
|
|
{
|
|
|
|
|
style: { display: "flex", alignItems: "center", gap: "12px", flexWrap: "wrap" }
|
|
|
|
|
},
|
2026-09-11 21:45:40 +08:00
|
|
|
import_react.default.createElement(
|
2026-09-11 13:58:29 +08:00
|
|
|
"label",
|
|
|
|
|
{
|
|
|
|
|
style: { display: "inline-flex", alignItems: "center", gap: "6px", fontSize: "13px", fontWeight: 500 }
|
|
|
|
|
},
|
2026-09-11 21:45:40 +08:00
|
|
|
import_react.default.createElement("span", null, getTranslation("targetProvider", isZh)),
|
|
|
|
|
import_react.default.createElement(
|
2026-09-11 13:58:29 +08:00
|
|
|
"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"
|
|
|
|
|
}
|
|
|
|
|
},
|
2026-09-11 21:45:40 +08:00
|
|
|
providerList.map((p) => import_react.default.createElement("option", { key: p.provider, value: p.provider }, `${p.displayName || p.provider} (${p.settingsNs || "generic"})`))
|
2026-09-11 13:58:29 +08:00
|
|
|
)
|
|
|
|
|
),
|
2026-09-11 21:45:40 +08:00
|
|
|
import_react.default.createElement(
|
2026-09-11 13:58:29 +08:00
|
|
|
"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)
|
|
|
|
|
),
|
2026-09-11 21:45:40 +08:00
|
|
|
import_react.default.createElement("button", {
|
2026-09-11 13:58:29 +08:00
|
|
|
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))
|
|
|
|
|
),
|
2026-09-11 21:45:40 +08:00
|
|
|
statusMsg ? import_react.default.createElement("div", {
|
2026-09-11 13:58:29 +08:00
|
|
|
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
|
2026-09-11 21:45:40 +08:00
|
|
|
var import_react2 = __toESM(require("react"), 1);
|
2026-09-11 13:58:29 +08:00
|
|
|
function ProviderCardExtras({ provider, configured, keyConfigured, ctx }) {
|
|
|
|
|
const isZh = isZhLocale();
|
2026-09-11 21:45:40 +08:00
|
|
|
const [matching, setMatching] = (0, import_react2.useState)(false);
|
|
|
|
|
const [notice, setNotice] = (0, import_react2.useState)(null);
|
2026-09-11 13:58:29 +08:00
|
|
|
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);
|
2026-09-11 21:45:40 +08:00
|
|
|
if (matched) count++;
|
|
|
|
|
return mergeModelCapabilities(m, matched);
|
2026-09-11 13:58:29 +08:00
|
|
|
});
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
};
|
2026-09-11 21:45:40 +08:00
|
|
|
return import_react2.default.createElement(
|
2026-09-11 13:58:29 +08:00
|
|
|
"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"
|
|
|
|
|
}
|
|
|
|
|
},
|
2026-09-11 21:45:40 +08:00
|
|
|
import_react2.default.createElement("span", {
|
2026-09-11 13:58:29 +08:00
|
|
|
style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #666)" }
|
|
|
|
|
}, getTranslation("activeEnhancerNotice", isZh, { provider: provider.displayName })),
|
2026-09-11 21:45:40 +08:00
|
|
|
import_react2.default.createElement("button", {
|
2026-09-11 13:58:29 +08:00
|
|
|
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)),
|
2026-09-11 21:45:40 +08:00
|
|
|
notice ? import_react2.default.createElement("span", {
|
2026-09-11 13:58:29 +08:00
|
|
|
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",
|
2026-09-11 21:45:40 +08:00
|
|
|
"remote.llm"
|
2026-09-11 13:58:29 +08:00
|
|
|
];
|
2026-09-09 00:52:52 +08:00
|
|
|
function apply(ctx) {
|
2026-09-11 13:58:29 +08:00
|
|
|
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) => {
|
|
|
|
|
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;
|
|
|
|
|
}
|
2026-09-09 00:52:52 +08:00
|
|
|
});
|