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({ ...@@ -103,7 +103,12 @@ export function Combobox({
<ChevronsUpDown className='ml-2 h-4 w-4 shrink-0 opacity-50' /> <ChevronsUpDown className='ml-2 h-4 w-4 shrink-0 opacity-50' />
</Button> </Button>
</PopoverTrigger> </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}> <Command shouldFilter={false}>
<CommandInput <CommandInput
placeholder={searchPlaceholder} 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 = { ...@@ -60,15 +60,30 @@ export const CHANNEL_TYPES = {
57: 'Codex', 57: 'Codex',
} as const } as const
export const CHANNEL_TYPE_OPTIONS = Object.entries(CHANNEL_TYPES) const CHANNEL_TYPE_DISPLAY_ORDER: number[] = [
.filter(([value]) => { 1, 14, 33, 24, 43, 3, 41, 48, 42, 34, 20, 4, 40, 27, 25, 17, 26, 15, 46, 23,
const num = Number(value) 18, 45, 31, 35, 49, 19, 47, 37, 38, 39, 11, 8, 57, 22, 21, 44, 2, 5, 36, 50,
return num !== 0 // Exclude Unknown 51, 52, 53, 54, 55, 56,
}) ]
.map(([value, label]) => ({
value: Number(value), export const CHANNEL_TYPE_OPTIONS: { value: number; label: string }[] = (() => {
label, 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) // Channel Status (label values are i18n keys; use t(config.label) in components)
...@@ -347,7 +362,7 @@ export const FIELD_DESCRIPTIONS = { ...@@ -347,7 +362,7 @@ export const FIELD_DESCRIPTIONS = {
// ============================================================================ // ============================================================================
export const MODEL_FETCHABLE_TYPES = new Set([ 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> = { export const TYPE_TO_KEY_PROMPT: Record<number, string> = {
......
...@@ -59,6 +59,7 @@ export const channelFormSchema = z.object({ ...@@ -59,6 +59,7 @@ export const channelFormSchema = z.object({
// Upstream model update settings (stored in settings JSON) // Upstream model update settings (stored in settings JSON)
upstream_model_update_check_enabled: z.boolean().optional(), upstream_model_update_check_enabled: z.boolean().optional(),
upstream_model_update_auto_sync_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> export type ChannelFormValues = z.infer<typeof channelFormSchema>
...@@ -113,6 +114,9 @@ export const CHANNEL_FORM_DEFAULT_VALUES: ChannelFormValues = { ...@@ -113,6 +114,9 @@ export const CHANNEL_FORM_DEFAULT_VALUES: ChannelFormValues = {
allow_inference_geo: false, allow_inference_geo: false,
allow_speed: false, allow_speed: false,
claude_beta_query: 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( ...@@ -166,6 +170,7 @@ export function transformChannelToFormDefaults(
let claudeBetaQuery = false let claudeBetaQuery = false
let upstreamModelUpdateCheckEnabled = false let upstreamModelUpdateCheckEnabled = false
let upstreamModelUpdateAutoSyncEnabled = false let upstreamModelUpdateAutoSyncEnabled = false
let upstreamModelUpdateIgnoredModels = ''
if (channel.settings) { if (channel.settings) {
try { try {
...@@ -185,6 +190,11 @@ export function transformChannelToFormDefaults( ...@@ -185,6 +190,11 @@ export function transformChannelToFormDefaults(
parsed.upstream_model_update_check_enabled === true parsed.upstream_model_update_check_enabled === true
upstreamModelUpdateAutoSyncEnabled = upstreamModelUpdateAutoSyncEnabled =
parsed.upstream_model_update_auto_sync_enabled === true 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) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error('Failed to parse channel settings:', error) console.error('Failed to parse channel settings:', error)
...@@ -233,6 +243,7 @@ export function transformChannelToFormDefaults( ...@@ -233,6 +243,7 @@ export function transformChannelToFormDefaults(
allow_safety_identifier: allowSafetyIdentifier, allow_safety_identifier: allowSafetyIdentifier,
upstream_model_update_check_enabled: upstreamModelUpdateCheckEnabled, upstream_model_update_check_enabled: upstreamModelUpdateCheckEnabled,
upstream_model_update_auto_sync_enabled: upstreamModelUpdateAutoSyncEnabled, upstream_model_update_auto_sync_enabled: upstreamModelUpdateAutoSyncEnabled,
upstream_model_update_ignored_models: upstreamModelUpdateIgnoredModels,
} }
} }
...@@ -336,7 +347,25 @@ function buildSettingsJSON(formData: ChannelFormValues): string { ...@@ -336,7 +347,25 @@ function buildSettingsJSON(formData: ChannelFormValues): string {
settingsObj.upstream_model_update_check_enabled = settingsObj.upstream_model_update_check_enabled =
formData.upstream_model_update_check_enabled === true formData.upstream_model_update_check_enabled === true
settingsObj.upstream_model_update_auto_sync_enabled = settingsObj.upstream_model_update_auto_sync_enabled =
settingsObj.upstream_model_update_check_enabled === true &&
formData.upstream_model_update_auto_sync_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) return JSON.stringify(settingsObj)
......
...@@ -78,6 +78,15 @@ export interface ChannelOtherSettings { ...@@ -78,6 +78,15 @@ export interface ChannelOtherSettings {
allow_service_tier?: boolean allow_service_tier?: boolean
disable_store?: boolean disable_store?: boolean
allow_safety_identifier?: 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 @@ ...@@ -17,13 +17,13 @@
"file": "ja.json", "file": "ja.json",
"missingCount": 0, "missingCount": 0,
"extrasCount": 0, "extrasCount": 0,
"untranslatedCount": 84 "untranslatedCount": 85
}, },
"ru": { "ru": {
"file": "ru.json", "file": "ru.json",
"missingCount": 0, "missingCount": 0,
"extrasCount": 0, "extrasCount": 0,
"untranslatedCount": 88 "untranslatedCount": 89
}, },
"vi": { "vi": {
"file": "vi.json", "file": "vi.json",
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
"footer.columns.related.links.midjourney": "Midjourney-Proxy", "footer.columns.related.links.midjourney": "Midjourney-Proxy",
"footer.columns.related.links.neko": "neko-api-key-tool", "footer.columns.related.links.neko": "neko-api-key-tool",
"Gemini": "Gemini", "Gemini": "Gemini",
"Gemini Image 4K": "Gemini Image 4K",
"GitHub": "GitHub", "GitHub": "GitHub",
"gpt-3.5-turbo": "gpt-3.5-turbo", "gpt-3.5-turbo": "gpt-3.5-turbo",
"gpt-3.5-turbo-0125": "gpt-3.5-turbo-0125", "gpt-3.5-turbo-0125": "gpt-3.5-turbo-0125",
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
"footer.columns.related.links.midjourney": "Midjourney-Proxy", "footer.columns.related.links.midjourney": "Midjourney-Proxy",
"footer.columns.related.links.neko": "neko-api-key-tool", "footer.columns.related.links.neko": "neko-api-key-tool",
"Gemini": "Gemini", "Gemini": "Gemini",
"Gemini Image 4K": "Gemini Image 4K",
"GitHub": "GitHub", "GitHub": "GitHub",
"gpt-3.5-turbo": "gpt-3.5-turbo", "gpt-3.5-turbo": "gpt-3.5-turbo",
"gpt-3.5-turbo-0125": "gpt-3.5-turbo-0125", "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