Files

686 lines
24 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 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;
}
`;
// src/client/ModelsFooterSettings.tsx
var import_react = __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 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;
}
// 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"
},
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"
}
};
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();
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);
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_react.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) count++;
return mergeModelCapabilities(m, matched);
});
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_react.default.createElement(
"div",
{
className: "me-footer-card"
},
import_react.default.createElement(
"div",
{
className: "me-footer-title"
},
import_react.default.createElement("svg", {
width: "18",
height: "18",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: "2"
}, 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))
),
import_react.default.createElement("div", {
className: "me-footer-desc"
}, getTranslation("enhancerIntro", isZh)),
import_react.default.createElement(
"div",
{
style: { display: "flex", alignItems: "center", gap: "12px", flexWrap: "wrap" }
},
import_react.default.createElement(
"label",
{
style: { display: "inline-flex", alignItems: "center", gap: "6px", fontSize: "13px", fontWeight: 500 }
},
import_react.default.createElement("span", null, getTranslation("targetProvider", isZh)),
import_react.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_react.default.createElement("option", { key: p.provider, value: p.provider }, `${p.displayName || p.provider} (${p.settingsNs || "generic"})`))
)
),
import_react.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_react.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_react.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_react2 = __toESM(require("react"), 1);
function ProviderCardExtras({ provider, configured, keyConfigured, ctx }) {
const isZh = isZhLocale();
const [matching, setMatching] = (0, import_react2.useState)(false);
const [notice, setNotice] = (0, import_react2.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) count++;
return mergeModelCapabilities(m, matched);
});
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_react2.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_react2.default.createElement("span", {
style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #666)" }
}, getTranslation("activeEnhancerNotice", isZh, { provider: provider.displayName })),
import_react2.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_react2.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"
];
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) => {
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;
}
});