auto-save 2026-05-27 17:51 (~4)
This commit is contained in:
@@ -1,12 +1,5 @@
|
|||||||
{
|
{
|
||||||
"entries": [
|
"entries": [
|
||||||
{
|
|
||||||
"files_changed": 3,
|
|
||||||
"hash": "3e7c165",
|
|
||||||
"message": "fix: snap workbench scale to common sizes",
|
|
||||||
"ts": "2026-05-20T19:47:26+08:00",
|
|
||||||
"type": "commit"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"files_changed": 1,
|
"files_changed": 1,
|
||||||
"hash": "b4f5612",
|
"hash": "b4f5612",
|
||||||
@@ -3197,6 +3190,13 @@
|
|||||||
"message": "auto-save 2026-05-27 17:24 (~4)",
|
"message": "auto-save 2026-05-27 17:24 (~4)",
|
||||||
"hash": "fb939b8",
|
"hash": "fb939b8",
|
||||||
"files_changed": 4
|
"files_changed": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ts": "2026-05-27T17:29:45+08:00",
|
||||||
|
"type": "commit",
|
||||||
|
"message": "auto-save 2026-05-27 17:29 (~3)",
|
||||||
|
"hash": "6ac548a",
|
||||||
|
"files_changed": 3
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
<!-- Model selector | 模型选择 -->
|
<!-- Model selector | 模型选择 -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-xs text-[var(--text-secondary)]">模型</span>
|
<span class="text-xs text-[var(--text-secondary)]">模型</span>
|
||||||
<n-dropdown :options="modelOptions" @select="handleModelSelect">
|
<n-dropdown trigger="click" :options="modelOptions" @select="handleModelSelect">
|
||||||
<button class="flex items-center gap-1 text-sm text-[var(--text-primary)] hover:text-[var(--accent-color)]">
|
<button class="flex items-center gap-1 text-sm text-[var(--text-primary)] hover:text-[var(--accent-color)]">
|
||||||
{{ displayModelName }}
|
{{ displayModelName }}
|
||||||
<n-icon :size="12"><ChevronDownOutline /></n-icon>
|
<n-icon :size="12"><ChevronDownOutline /></n-icon>
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
<!-- Aspect ratio selector | 宽高比选择 -->
|
<!-- Aspect ratio selector | 宽高比选择 -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-xs text-[var(--text-secondary)]">比例</span>
|
<span class="text-xs text-[var(--text-secondary)]">比例</span>
|
||||||
<n-dropdown :options="ratioOptions" @select="handleRatioSelect">
|
<n-dropdown trigger="click" :options="ratioOptions" @select="handleRatioSelect">
|
||||||
<button class="flex items-center gap-1 text-sm text-[var(--text-primary)] hover:text-[var(--accent-color)]">
|
<button class="flex items-center gap-1 text-sm text-[var(--text-primary)] hover:text-[var(--accent-color)]">
|
||||||
{{ localRatio }}
|
{{ localRatio }}
|
||||||
<n-icon :size="12">
|
<n-icon :size="12">
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
<!-- Duration selector | 时长选择 -->
|
<!-- Duration selector | 时长选择 -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-xs text-[var(--text-secondary)]">时长</span>
|
<span class="text-xs text-[var(--text-secondary)]">时长</span>
|
||||||
<n-dropdown :options="durationOptions" @select="handleDurationSelect">
|
<n-dropdown trigger="click" :options="durationOptions" @select="handleDurationSelect">
|
||||||
<button class="flex items-center gap-1 text-sm text-[var(--text-primary)] hover:text-[var(--accent-color)]">
|
<button class="flex items-center gap-1 text-sm text-[var(--text-primary)] hover:text-[var(--accent-color)]">
|
||||||
{{ localDuration }}s
|
{{ localDuration }}s
|
||||||
<n-icon :size="12">
|
<n-icon :size="12">
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export const getModelQualityOptions = (modelKey) => {
|
|||||||
* Returns options based on model's ratios array
|
* Returns options based on model's ratios array
|
||||||
*/
|
*/
|
||||||
export const getModelRatioOptions = (modelKey) => {
|
export const getModelRatioOptions = (modelKey) => {
|
||||||
const model = VIDEO_MODELS.find(m => m.key === modelKey)
|
const model = getModelConfig(modelKey) || VIDEO_MODELS.find(m => m.key === modelKey)
|
||||||
if (!model?.ratios) return VIDEO_RATIO_OPTIONS
|
if (!model?.ratios) return VIDEO_RATIO_OPTIONS
|
||||||
|
|
||||||
// Convert ratios array to dropdown options | 转换 ratios 数组为下拉选项
|
// Convert ratios array to dropdown options | 转换 ratios 数组为下拉选项
|
||||||
@@ -137,7 +137,7 @@ export const getModelRatioOptions = (modelKey) => {
|
|||||||
* Returns options based on model's durs array
|
* Returns options based on model's durs array
|
||||||
*/
|
*/
|
||||||
export const getModelDurationOptions = (modelKey) => {
|
export const getModelDurationOptions = (modelKey) => {
|
||||||
const model = VIDEO_MODELS.find(m => m.key === modelKey)
|
const model = getModelConfig(modelKey) || VIDEO_MODELS.find(m => m.key === modelKey)
|
||||||
if (!model?.durs) return VIDEO_DURATION_OPTIONS
|
if (!model?.durs) return VIDEO_DURATION_OPTIONS
|
||||||
|
|
||||||
// durs is already in { label, key } format | durs 已经是 { label, key } 格式
|
// durs is already in { label, key } format | durs 已经是 { label, key } 格式
|
||||||
|
|||||||
@@ -139,6 +139,45 @@ const normalizeRuntimeImageModel = (item) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const normalizeRuntimeDurationOptions = (items = []) => {
|
||||||
|
if (!Array.isArray(items)) return []
|
||||||
|
return items
|
||||||
|
.map(item => {
|
||||||
|
const key = typeof item === 'object' ? item?.value || item?.key || item?.id : item
|
||||||
|
if (!key) return null
|
||||||
|
return {
|
||||||
|
label: typeof item === 'object' ? item.label || `${key} 秒` : `${key} 秒`,
|
||||||
|
key
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeRuntimeVideoModel = (item) => {
|
||||||
|
const key = item?.id || item?.model
|
||||||
|
if (!key) return null
|
||||||
|
const sizeOptions = normalizeRuntimeSizeOptions(item.size_options)
|
||||||
|
const durationOptions = normalizeRuntimeDurationOptions(item.duration_options)
|
||||||
|
return {
|
||||||
|
label: item.label || item.model || key,
|
||||||
|
key,
|
||||||
|
provider: ['chatfire'],
|
||||||
|
type: 't2v+i2v',
|
||||||
|
model: item.model,
|
||||||
|
ratios: sizeOptions.map(option => option.key),
|
||||||
|
durs: durationOptions,
|
||||||
|
resolutions: ['720p'],
|
||||||
|
defaultResolution: '720p',
|
||||||
|
defaultParams: {
|
||||||
|
ratio: sizeOptions[0]?.key || '720x1280',
|
||||||
|
duration: durationOptions[0]?.key || 5,
|
||||||
|
resolution: '720p'
|
||||||
|
},
|
||||||
|
available: item.available !== false,
|
||||||
|
isRuntime: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const mergeModels = (builtInModels, runtimeModels) => {
|
const mergeModels = (builtInModels, runtimeModels) => {
|
||||||
const byKey = new Map()
|
const byKey = new Map()
|
||||||
builtInModels.forEach(model => byKey.set(model.key, { ...model, isCustom: false }))
|
builtInModels.forEach(model => byKey.set(model.key, { ...model, isCustom: false }))
|
||||||
@@ -206,6 +245,7 @@ export const useModelStore = defineStore('model', () => {
|
|||||||
const customImageModelsByProvider = ref(getStoredJson(STORAGE_KEYS.CUSTOM_IMAGE_MODELS_BY_PROVIDER, {}))
|
const customImageModelsByProvider = ref(getStoredJson(STORAGE_KEYS.CUSTOM_IMAGE_MODELS_BY_PROVIDER, {}))
|
||||||
const customVideoModelsByProvider = ref(getStoredJson(STORAGE_KEYS.CUSTOM_VIDEO_MODELS_BY_PROVIDER, {}))
|
const customVideoModelsByProvider = ref(getStoredJson(STORAGE_KEYS.CUSTOM_VIDEO_MODELS_BY_PROVIDER, {}))
|
||||||
const runtimeImageModels = ref([])
|
const runtimeImageModels = ref([])
|
||||||
|
const runtimeVideoModels = ref([])
|
||||||
|
|
||||||
// 选中的模型
|
// 选中的模型
|
||||||
const selectedChatModel = ref(getStored(STORAGE_KEYS.SELECTED_CHAT_MODEL, DEFAULT_CHAT_MODEL))
|
const selectedChatModel = ref(getStored(STORAGE_KEYS.SELECTED_CHAT_MODEL, DEFAULT_CHAT_MODEL))
|
||||||
@@ -259,7 +299,7 @@ export const useModelStore = defineStore('model', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const allVideoModels = computed(() =>
|
const allVideoModels = computed(() =>
|
||||||
VIDEO_MODELS.map(m => ({ ...m, isCustom: false }))
|
mergeModels(VIDEO_MODELS, runtimeVideoModels.value)
|
||||||
)
|
)
|
||||||
|
|
||||||
// ============ Computed: Available Models (filtered by provider) ============
|
// ============ Computed: Available Models (filtered by provider) ============
|
||||||
@@ -274,7 +314,7 @@ export const useModelStore = defineStore('model', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const availableVideoModels = computed(() =>
|
const availableVideoModels = computed(() =>
|
||||||
allVideoModels.value.filter(m => isModelSupported(m, currentProvider.value))
|
allVideoModels.value.filter(m => isModelSupported(m, currentProvider.value) && m.available !== false)
|
||||||
)
|
)
|
||||||
|
|
||||||
// ============ Computed: Model Options for UI (all models, not filtered by provider) ============
|
// ============ Computed: Model Options for UI (all models, not filtered by provider) ============
|
||||||
@@ -291,7 +331,8 @@ export const useModelStore = defineStore('model', () => {
|
|||||||
const allVideoModelOptions = computed(() =>
|
const allVideoModelOptions = computed(() =>
|
||||||
allVideoModels.value.map(m => ({
|
allVideoModels.value.map(m => ({
|
||||||
label: m.label,
|
label: m.label,
|
||||||
key: m.key
|
key: m.key,
|
||||||
|
disabled: false
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -399,6 +440,15 @@ export const useModelStore = defineStore('model', () => {
|
|||||||
.filter(item => item?.id && item.id !== 'auto')
|
.filter(item => item?.id && item.id !== 'auto')
|
||||||
.map(normalizeRuntimeImageModel)
|
.map(normalizeRuntimeImageModel)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
const videoOptions = data?.models?.video_options || []
|
||||||
|
runtimeVideoModels.value = videoOptions
|
||||||
|
.filter(item => {
|
||||||
|
const id = String(item?.id || '').toLowerCase()
|
||||||
|
const model = String(item?.model || '').toLowerCase()
|
||||||
|
return id.includes('seedance') || model.includes('seedance')
|
||||||
|
})
|
||||||
|
.map(normalizeRuntimeVideoModel)
|
||||||
|
.filter(Boolean)
|
||||||
return true
|
return true
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('[model store] runtime model load failed', err)
|
console.warn('[model store] runtime model load failed', err)
|
||||||
@@ -564,6 +614,7 @@ export const useModelStore = defineStore('model', () => {
|
|||||||
allImageModels,
|
allImageModels,
|
||||||
allVideoModels,
|
allVideoModels,
|
||||||
runtimeImageModels,
|
runtimeImageModels,
|
||||||
|
runtimeVideoModels,
|
||||||
|
|
||||||
// Available models filtered by provider
|
// Available models filtered by provider
|
||||||
availableChatModels,
|
availableChatModels,
|
||||||
|
|||||||
Reference in New Issue
Block a user