mirror of
https://github.com/basketikun/infinite-canvas.git
synced 2026-08-01 22:21:15 +08:00
feat(canvas): add canvas default image count configuration and update model picker capabilities
This commit is contained in:
@@ -5,7 +5,7 @@ import { useState } from "react";
|
||||
|
||||
import { ModelPicker } from "@/components/model-picker";
|
||||
import { fetchImageModels } from "@/services/api/image";
|
||||
import { useConfigStore, useEffectiveConfig, type AiConfig } from "@/stores/use-config-store";
|
||||
import { filterModelsByCapability, useConfigStore, useEffectiveConfig, type AiConfig } from "@/stores/use-config-store";
|
||||
|
||||
export function AppConfigModal() {
|
||||
const { message } = App.useApp();
|
||||
@@ -41,10 +41,13 @@ export function AppConfigModal() {
|
||||
setLoadingModels(true);
|
||||
try {
|
||||
const models = await fetchImageModels(config);
|
||||
const imageModels = filterModelsByCapability(models, "image");
|
||||
const videoModels = filterModelsByCapability(models, "video");
|
||||
const textModels = filterModelsByCapability(models, "text");
|
||||
updateConfig("models", models);
|
||||
if (models.length && !models.includes(config.imageModel)) updateConfig("imageModel", models[0]);
|
||||
if (models.length && !models.includes(config.videoModel)) updateConfig("videoModel", models[0]);
|
||||
if (models.length && !models.includes(config.textModel)) updateConfig("textModel", models[0]);
|
||||
if (imageModels.length && !imageModels.includes(config.imageModel)) updateConfig("imageModel", imageModels[0]);
|
||||
if (videoModels.length && !videoModels.includes(config.videoModel)) updateConfig("videoModel", videoModels[0]);
|
||||
if (textModels.length && !textModels.includes(config.textModel)) updateConfig("textModel", textModels[0]);
|
||||
message.success("模型列表已更新");
|
||||
} catch (error) {
|
||||
message.error(error instanceof Error ? error.message : "读取模型失败");
|
||||
@@ -115,13 +118,25 @@ export function AppConfigModal() {
|
||||
)}
|
||||
<div className="grid gap-4 md:grid-cols-3">
|
||||
<Form.Item label="默认生图模型" className="mb-4">
|
||||
<ModelPicker config={modelConfig} value={modelConfig.imageModel} onChange={(model) => updateConfig("imageModel", model)} fullWidth />
|
||||
<ModelPicker config={modelConfig} value={modelConfig.imageModel} onChange={(model) => updateConfig("imageModel", model)} capability="image" fullWidth />
|
||||
</Form.Item>
|
||||
<Form.Item label="默认视频模型" className="mb-4">
|
||||
<ModelPicker config={modelConfig} value={modelConfig.videoModel} onChange={(model) => updateConfig("videoModel", model)} fullWidth />
|
||||
<ModelPicker config={modelConfig} value={modelConfig.videoModel} onChange={(model) => updateConfig("videoModel", model)} capability="video" fullWidth />
|
||||
</Form.Item>
|
||||
<Form.Item label="默认文本模型" className="mb-4">
|
||||
<ModelPicker config={modelConfig} value={modelConfig.textModel} onChange={(model) => updateConfig("textModel", model)} fullWidth />
|
||||
<ModelPicker config={modelConfig} value={modelConfig.textModel} onChange={(model) => updateConfig("textModel", model)} capability="text" fullWidth />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-3">
|
||||
<Form.Item label="画布默认生图张数" extra="新建画布生图和配置节点默认使用,单个节点仍可单独覆盖。" className="mb-4">
|
||||
<Input
|
||||
type="number"
|
||||
min={1}
|
||||
max={15}
|
||||
value={config.canvasImageCount}
|
||||
onChange={(event) => updateConfig("canvasImageCount", event.target.value)}
|
||||
onBlur={(event) => updateConfig("canvasImageCount", normalizeImageCount(event.target.value))}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
{effectiveMode === "local" ? (
|
||||
@@ -134,3 +149,7 @@ export function AppConfigModal() {
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeImageCount(value: string) {
|
||||
return String(Math.max(1, Math.min(15, Math.floor(Math.abs(Number(value)) || 3))));
|
||||
}
|
||||
|
||||
@@ -5,22 +5,23 @@ import { Cpu } from "lucide-react";
|
||||
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { AiConfig } from "@/stores/use-config-store";
|
||||
import { filterModelsByCapability, type AiConfig, type ModelCapability } from "@/stores/use-config-store";
|
||||
|
||||
type ModelPickerProps = {
|
||||
config: AiConfig;
|
||||
value?: string;
|
||||
onChange: (model: string) => void;
|
||||
capability?: ModelCapability;
|
||||
className?: string;
|
||||
fullWidth?: boolean;
|
||||
placeholder?: string;
|
||||
onMissingConfig?: () => void;
|
||||
};
|
||||
|
||||
export function ModelPicker({ config, value, onChange, className, fullWidth = false, placeholder = "选择模型", onMissingConfig }: ModelPickerProps) {
|
||||
export function ModelPicker({ config, value, onChange, capability, className, fullWidth = false, placeholder = "选择模型", onMissingConfig }: ModelPickerProps) {
|
||||
const pickerId = useId();
|
||||
const [open, setOpen] = useState(false);
|
||||
const options = useMemo(() => Array.from(new Set([...(config.channelMode === "local" ? [value] : []), ...config.models].filter((model): model is string => Boolean(model)))), [config.channelMode, config.models, value]);
|
||||
const options = useMemo(() => filterModelsByCapability(Array.from(new Set([...(config.channelMode === "local" ? [value] : []), ...config.models].filter((model): model is string => Boolean(model)))), capability), [capability, config.channelMode, config.models, value]);
|
||||
const current = value || "";
|
||||
|
||||
useEffect(() => {
|
||||
@@ -77,7 +78,7 @@ export function ModelPicker({ config, value, onChange, className, fullWidth = fa
|
||||
))
|
||||
) : (
|
||||
<SelectItem value="__empty__" disabled>
|
||||
{config.channelMode === "remote" ? "暂无可用模型" : "请先到配置里拉取模型列表"}
|
||||
{emptyModelLabel(config, capability)}
|
||||
</SelectItem>
|
||||
)}
|
||||
</SelectContent>
|
||||
@@ -85,6 +86,12 @@ export function ModelPicker({ config, value, onChange, className, fullWidth = fa
|
||||
);
|
||||
}
|
||||
|
||||
function emptyModelLabel(config: AiConfig, capability?: ModelCapability) {
|
||||
const label = capability === "image" ? "生图" : capability === "video" ? "视频" : capability === "text" ? "文本" : "";
|
||||
if (config.channelMode === "remote") return `暂无可用${label}模型`;
|
||||
return config.models.length ? `暂无匹配的${label}模型` : "请先到配置里拉取模型列表";
|
||||
}
|
||||
|
||||
function ModelLabel({ model }: { model: string }) {
|
||||
return (
|
||||
<span className="flex min-w-0 items-center gap-2">
|
||||
|
||||
Reference in New Issue
Block a user