/**
* The provider's own controls (rendered inside the header provider-settings gear popover,
* `ProviderGear`) — capability-driven from the provider's `settings.js`, each with an info tooltip.
* What's NOT here: the BYOK API key lives in the header `ApiKeyField`; the provider label/tier is
* shown by the gear popover header; and the negative prompt is edited on the composer via its
* Prompt/Negative switch (so it's filtered out of these controls).
* @module gui/components/ProviderBox
*/
import { useEffect } from "react";
import { useIntl, defineMessages } from "react-intl";
import { Text, Num, Toggle, Select } from "./Field.jsx";
import { getProvider } from "../lib/providers/index.js";
import { useProviderSettings } from "../lib/useProvider.js";
import { infoFor } from "../../../shared/_shared/fieldInfo.js";
import { jsxDEV as _jsxDEV, Fragment as _Fragment } from "react/jsx-dev-runtime";
const msgs = defineMessages({
noSettings: {
id: "providerBox.noSettings",
defaultMessage: "This provider has no extra settings."
},
loading: {
id: "providerBox.loading",
defaultMessage: "Loading…"
}
});
/**
* A small info "i" with a tooltip.
* @param {object} props `{ text }`.
* @returns {(JSX.Element|null)}
*/
function InfoTip({
text
}) {
if (!text) return null;
return /*#__PURE__*/_jsxDEV("span", {
className: "info-tip",
title: text,
"aria-label": text,
role: "img",
children: "i"
}, void 0, false);
}
/**
* Render one capability-driven provider field (with its tooltip).
* @param {object} props `{ f, params, setParam, optionData }`.
* @returns {JSX.Element}
*/
function ProviderField({
f,
params,
setParam,
optionData
}) {
const v = params[f.key];
const label = /*#__PURE__*/_jsxDEV(_Fragment, {
children: [f.label, /*#__PURE__*/_jsxDEV(InfoTip, {
text: infoFor(f)
}, void 0, false)]
}, void 0, true);
if (f.type === "checkbox") return /*#__PURE__*/_jsxDEV(Toggle, {
label: label,
value: v,
onChange: x => setParam(f.key, x)
}, void 0, false);
if (f.type === "select") {
const options = f.options || optionData[f.optionsFrom] || [];
return /*#__PURE__*/_jsxDEV(Select, {
label: label,
value: v ?? "",
options: options,
onChange: x => setParam(f.key, x)
}, void 0, false);
}
if (f.type === "number") {
return /*#__PURE__*/_jsxDEV(Num, {
label: label,
value: v,
min: f.min,
max: f.max,
step: f.step,
onChange: x => setParam(f.key, x)
}, void 0, false);
}
return /*#__PURE__*/_jsxDEV(Text, {
label: label,
value: v,
onChange: x => setParam(f.key, x)
}, void 0, false);
}
/**
* The provider's own controls (rendered inside the header gear popover).
* @param {object} props
* @param {object} props.settings The current settings.
* @param {Function} props.setSettings Update the settings.
* @param {string} [props.providerId] Which provider's controls to render. Defaults to the active
* image provider (`settings.provider`); the gear accordion passes the text / upscale provider id
* so one box can drive any provider row.
* @returns {(JSX.Element|null)}
*/
export default function ProviderBox({
settings,
setSettings,
providerId
}) {
const intl = useIntl();
const provider = getProvider(providerId || settings.provider);
const pid = provider?.id;
const {
schema,
options
} = useProviderSettings(pid);
const params = settings.providerParams?.[pid] || {};
const setParam = (key, value) => setSettings(s => ({
...s,
providerParams: {
...s.providerParams,
[pid]: {
...s.providerParams?.[pid],
[key]: value
}
}
}));
// Seed missing param defaults when the provider's schema loads (without clobbering saved values).
useEffect(() => {
if (!schema?.defaults || !pid) return;
setSettings(s => {
const cur = s.providerParams?.[pid] || {};
let changed = false;
const next = {
...cur
};
for (const [k, val] of Object.entries(schema.defaults)) {
if (next[k] === undefined) {
next[k] = val;
changed = true;
}
}
return changed ? {
...s,
providerParams: {
...s.providerParams,
[pid]: next
}
} : s;
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [schema, pid]);
if (!provider) return null;
// What's edited elsewhere and so filtered out of this popover:
// • negativePrompt — on the composer (Prompt/Negative switch).
// • the common image knobs (batch + size/ratio/width/height) — inline at the bottom-left of the
// prompt box (`InlineImageControls`), for the in-app image (api-tier) providers.
const inlineKeys = ["batchSize", "size", "imageSize", "aspectRatio", "ar", "imageWidth", "imageHeight"];
const fields = (schema?.fields || []).filter(f => f.key !== "negativePrompt" && !inlineKeys.includes(f.key));
if (!fields.length) return /*#__PURE__*/_jsxDEV("p", {
className: "hint provider-controls-empty",
children: intl.formatMessage(schema ? msgs.noSettings : msgs.loading)
}, void 0, false);
return /*#__PURE__*/_jsxDEV("div", {
className: "provider-controls group-body",
children: fields.map(f => /*#__PURE__*/_jsxDEV(ProviderField, {
f: f,
params: params,
setParam: setParam,
optionData: options
}, f.key, false))
}, void 0, false);
}