Commit b44faec6 by CaIon

feat(ui): overhaul default channel editor with full param override visual editor

- Port classic ParamOverrideEditorModal to default as standalone dialog (~3200 lines)
  with two-panel layout, drag-to-reorder, 23 operation modes, template library,
  visual/JSON dual mode, conditions management, and legacy format support
- Redesign channel drawer layout with clear visual hierarchy (CardHeading vs SubHeading)
  and bordered sub-modules for Field Passthrough and Upstream Model Detection
- Replace header override JsonEditor with plain textarea matching classic behavior
- Add searchable channel type combobox with scroll fix
- Add 100+ i18n keys across all 6 locales (en, zh, fr, ja, ru, vi)
parent 3b592895
......@@ -103,7 +103,12 @@ export function Combobox({
<ChevronsUpDown className='ml-2 h-4 w-4 shrink-0 opacity-50' />
</Button>
</PopoverTrigger>
<PopoverContent className='w-[var(--radix-popover-trigger-width)] p-0'>
<PopoverContent
className='w-[var(--radix-popover-trigger-width)] p-0'
onWheel={(e) => e.stopPropagation()}
onTouchMove={(e) => e.stopPropagation()}
onPointerDown={(e) => e.stopPropagation()}
>
<Command shouldFilter={false}>
<CommandInput
placeholder={searchPlaceholder}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -60,15 +60,30 @@ export const CHANNEL_TYPES = {
57: 'Codex',
} as const
export const CHANNEL_TYPE_OPTIONS = Object.entries(CHANNEL_TYPES)
.filter(([value]) => {
const num = Number(value)
return num !== 0 // Exclude Unknown
})
.map(([value, label]) => ({
value: Number(value),
label,
}))
const CHANNEL_TYPE_DISPLAY_ORDER: number[] = [
1, 14, 33, 24, 43, 3, 41, 48, 42, 34, 20, 4, 40, 27, 25, 17, 26, 15, 46, 23,
18, 45, 31, 35, 49, 19, 47, 37, 38, 39, 11, 8, 57, 22, 21, 44, 2, 5, 36, 50,
51, 52, 53, 54, 55, 56,
]
export const CHANNEL_TYPE_OPTIONS: { value: number; label: string }[] = (() => {
const ordered: { value: number; label: string }[] = []
const seen = new Set<number>()
for (const id of CHANNEL_TYPE_DISPLAY_ORDER) {
const label = CHANNEL_TYPES[id as keyof typeof CHANNEL_TYPES]
if (label) {
ordered.push({ value: id, label })
seen.add(id)
}
}
for (const [key, label] of Object.entries(CHANNEL_TYPES)) {
const id = Number(key)
if (id !== 0 && !seen.has(id)) {
ordered.push({ value: id, label })
}
}
return ordered
})()
// ============================================================================
// Channel Status (label values are i18n keys; use t(config.label) in components)
......@@ -347,7 +362,7 @@ export const FIELD_DESCRIPTIONS = {
// ============================================================================
export const MODEL_FETCHABLE_TYPES = new Set([
1, 4, 14, 17, 20, 23, 24, 25, 26, 31, 34, 35, 40, 42, 43, 47, 48,
1, 4, 14, 17, 20, 23, 24, 25, 26, 27, 31, 34, 35, 40, 42, 43, 47, 48,
])
export const TYPE_TO_KEY_PROMPT: Record<number, string> = {
......
......@@ -59,6 +59,7 @@ export const channelFormSchema = z.object({
// Upstream model update settings (stored in settings JSON)
upstream_model_update_check_enabled: z.boolean().optional(),
upstream_model_update_auto_sync_enabled: z.boolean().optional(),
upstream_model_update_ignored_models: z.string().optional(),
})
export type ChannelFormValues = z.infer<typeof channelFormSchema>
......@@ -113,6 +114,9 @@ export const CHANNEL_FORM_DEFAULT_VALUES: ChannelFormValues = {
allow_inference_geo: false,
allow_speed: false,
claude_beta_query: false,
upstream_model_update_check_enabled: false,
upstream_model_update_auto_sync_enabled: false,
upstream_model_update_ignored_models: '',
}
// ============================================================================
......@@ -166,6 +170,7 @@ export function transformChannelToFormDefaults(
let claudeBetaQuery = false
let upstreamModelUpdateCheckEnabled = false
let upstreamModelUpdateAutoSyncEnabled = false
let upstreamModelUpdateIgnoredModels = ''
if (channel.settings) {
try {
......@@ -185,6 +190,11 @@ export function transformChannelToFormDefaults(
parsed.upstream_model_update_check_enabled === true
upstreamModelUpdateAutoSyncEnabled =
parsed.upstream_model_update_auto_sync_enabled === true
upstreamModelUpdateIgnoredModels = Array.isArray(
parsed.upstream_model_update_ignored_models
)
? parsed.upstream_model_update_ignored_models.join(',')
: ''
} catch (error) {
// eslint-disable-next-line no-console
console.error('Failed to parse channel settings:', error)
......@@ -233,6 +243,7 @@ export function transformChannelToFormDefaults(
allow_safety_identifier: allowSafetyIdentifier,
upstream_model_update_check_enabled: upstreamModelUpdateCheckEnabled,
upstream_model_update_auto_sync_enabled: upstreamModelUpdateAutoSyncEnabled,
upstream_model_update_ignored_models: upstreamModelUpdateIgnoredModels,
}
}
......@@ -336,7 +347,25 @@ function buildSettingsJSON(formData: ChannelFormValues): string {
settingsObj.upstream_model_update_check_enabled =
formData.upstream_model_update_check_enabled === true
settingsObj.upstream_model_update_auto_sync_enabled =
settingsObj.upstream_model_update_check_enabled === true &&
formData.upstream_model_update_auto_sync_enabled === true
settingsObj.upstream_model_update_ignored_models = Array.from(
new Set(
String(formData.upstream_model_update_ignored_models || '')
.split(',')
.map((model) => model.trim())
.filter(Boolean)
)
)
if (
!Array.isArray(settingsObj.upstream_model_update_last_detected_models) ||
settingsObj.upstream_model_update_check_enabled !== true
) {
settingsObj.upstream_model_update_last_detected_models = []
}
if (typeof settingsObj.upstream_model_update_last_check_time !== 'number') {
settingsObj.upstream_model_update_last_check_time = 0
}
}
return JSON.stringify(settingsObj)
......
......@@ -78,6 +78,15 @@ export interface ChannelOtherSettings {
allow_service_tier?: boolean
disable_store?: boolean
allow_safety_identifier?: boolean
allow_include_obfuscation?: boolean
allow_inference_geo?: boolean
allow_speed?: boolean
claude_beta_query?: boolean
upstream_model_update_check_enabled?: boolean
upstream_model_update_auto_sync_enabled?: boolean
upstream_model_update_ignored_models?: string[]
upstream_model_update_last_check_time?: number
upstream_model_update_last_detected_models?: string[]
}
// ============================================================================
......
......@@ -17,13 +17,13 @@
"file": "ja.json",
"missingCount": 0,
"extrasCount": 0,
"untranslatedCount": 84
"untranslatedCount": 85
},
"ru": {
"file": "ru.json",
"missingCount": 0,
"extrasCount": 0,
"untranslatedCount": 88
"untranslatedCount": 89
},
"vi": {
"file": "vi.json",
......
......@@ -21,6 +21,7 @@
"footer.columns.related.links.midjourney": "Midjourney-Proxy",
"footer.columns.related.links.neko": "neko-api-key-tool",
"Gemini": "Gemini",
"Gemini Image 4K": "Gemini Image 4K",
"GitHub": "GitHub",
"gpt-3.5-turbo": "gpt-3.5-turbo",
"gpt-3.5-turbo-0125": "gpt-3.5-turbo-0125",
......
......@@ -25,6 +25,7 @@
"footer.columns.related.links.midjourney": "Midjourney-Proxy",
"footer.columns.related.links.neko": "neko-api-key-tool",
"Gemini": "Gemini",
"Gemini Image 4K": "Gemini Image 4K",
"GitHub": "GitHub",
"gpt-3.5-turbo": "gpt-3.5-turbo",
"gpt-3.5-turbo-0125": "gpt-3.5-turbo-0125",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment