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)
This commit is contained in:
CaIon
2026-04-29 18:09:11 +08:00
parent 3b592895c6
commit b44faec66b
15 changed files with 5658 additions and 892 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+25 -10
View File
@@ -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> = {
+29
View File
@@ -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)
+9
View File
@@ -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[]
}
// ============================================================================