fix: restore default channel connection paste

This commit is contained in:
CaIon
2026-07-08 12:39:23 +08:00
parent 6ce7305cd3
commit 57865fc1f8
10 changed files with 243 additions and 29 deletions
@@ -24,6 +24,7 @@ import {
Boxes,
CheckCircle2,
Circle,
ClipboardPaste,
HelpCircle,
KeyRound,
Loader2,
@@ -115,6 +116,10 @@ import {
ADMIN_PERMISSION_RESOURCES,
hasPermission,
} from '@/lib/admin-permissions'
import {
parseChannelConnectionInfo,
type ChannelConnectionInfo,
} from '@/lib/channel-connection-info'
import { getLobeIcon } from '@/lib/lobe-icon'
import { ROLE } from '@/lib/roles'
import { cn } from '@/lib/utils'
@@ -626,6 +631,8 @@ export function ChannelMutateDrawer({
const [paramOverrideEditorOpen, setParamOverrideEditorOpen] = useState(false)
const [advancedCustomEditorOpen, setAdvancedCustomEditorOpen] =
useState(false)
const [clipboardConnectionInfo, setClipboardConnectionInfo] =
useState<ChannelConnectionInfo | null>(null)
const isEditing = Boolean(currentRow)
const channelId = currentRow?.id ?? null
@@ -757,6 +764,67 @@ export function ChannelMutateDrawer({
}
}, [open, resetDoubaoApiUnlock])
const applyConnectionInfo = useCallback(
(connectionInfo: ChannelConnectionInfo) => {
form.setValue('key', connectionInfo.key, {
shouldDirty: true,
shouldValidate: true,
})
form.setValue('base_url', connectionInfo.url, {
shouldDirty: true,
shouldValidate: true,
})
setClipboardConnectionInfo(null)
toast.success(t('Connection info filled in'))
},
[form, t]
)
const pasteConnectionInfoFromClipboard = useCallback(async () => {
if (typeof navigator === 'undefined' || !navigator.clipboard?.readText) {
toast.error(t('Unable to read clipboard'))
return
}
try {
const text = await navigator.clipboard.readText()
const parsed = parseChannelConnectionInfo(text)
if (parsed) {
applyConnectionInfo(parsed)
return
}
toast.info(t('No connection info found in clipboard'))
} catch {
toast.error(t('Unable to read clipboard'))
}
}, [applyConnectionInfo, t])
useEffect(() => {
if (!open || isEditing) {
setClipboardConnectionInfo(null)
return
}
if (typeof navigator === 'undefined' || !navigator.clipboard?.readText) {
return
}
let cancelled = false
void navigator.clipboard
.readText()
.then((text) => {
if (cancelled) return
setClipboardConnectionInfo(parseChannelConnectionInfo(text))
})
.catch(() => {
/* Clipboard detection is best-effort on drawer open. */
})
return () => {
cancelled = true
}
}, [isEditing, open])
// Helper computed values
const isBatchMode =
multiKeyMode === 'batch' || multiKeyMode === 'multi_to_single'
@@ -1741,6 +1809,7 @@ export function ChannelMutateDrawer({
setActiveEditorSectionId(CHANNEL_EDITOR_SECTION_IDS.identity)
setExpandedEditorNavItemId(undefined)
setAdvancedSettingsOpen(false)
setClipboardConnectionInfo(null)
}
},
[onOpenChange, form]
@@ -1751,26 +1820,42 @@ export function ChannelMutateDrawer({
<Sheet open={open} onOpenChange={handleOpenChange}>
<SheetContent className={sideDrawerContentClassName('sm:max-w-5xl')}>
<SheetHeader className={sideDrawerHeaderClassName()}>
<SheetTitle className='flex items-center gap-3'>
<span className='bg-muted flex size-9 shrink-0 items-center justify-center rounded-md'>
<ChannelTypeLogo type={currentType} size={22} />
</span>
<span>
{isEditing ? t('Edit Channel') : t('Create Channel')}
<span className='text-muted-foreground ml-2 text-sm font-normal'>
{t(currentTypeLabel)}
</span>
</span>
</SheetTitle>
<SheetDescription>
{isEditing
? t(
"Update channel configuration and click save when you're done."
)
: t(
'Add a new channel by providing the necessary information.'
)}
</SheetDescription>
<div className='flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between'>
<div className='min-w-0'>
<SheetTitle className='flex items-center gap-3'>
<span className='bg-muted flex size-9 shrink-0 items-center justify-center rounded-md'>
<ChannelTypeLogo type={currentType} size={22} />
</span>
<span>
{isEditing ? t('Edit Channel') : t('Create Channel')}
<span className='text-muted-foreground ml-2 text-sm font-normal'>
{t(currentTypeLabel)}
</span>
</span>
</SheetTitle>
<SheetDescription className='mt-1'>
{isEditing
? t(
"Update channel configuration and click save when you're done."
)
: t(
'Add a new channel by providing the necessary information.'
)}
</SheetDescription>
</div>
{!isEditing && (
<Button
type='button'
variant='outline'
size='sm'
className='shrink-0'
onClick={pasteConnectionInfoFromClipboard}
>
<ClipboardPaste className='size-4' />
<span>{t('Paste Connection Info')}</span>
</Button>
)}
</div>
</SheetHeader>
{sensitiveLocked && (
@@ -1786,6 +1871,31 @@ export function ChannelMutateDrawer({
</Alert>
)}
{!isEditing && clipboardConnectionInfo && (
<Alert>
<AlertDescription className='flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between'>
<span>{t('Connection info detected in clipboard')}</span>
<span className='flex shrink-0 gap-2'>
<Button
type='button'
size='sm'
onClick={() => applyConnectionInfo(clipboardConnectionInfo)}
>
{t('Fill in')}
</Button>
<Button
type='button'
variant='ghost'
size='sm'
onClick={() => setClipboardConnectionInfo(null)}
>
{t('Ignore')}
</Button>
</span>
</AlertDescription>
</Alert>
)}
<Form {...form}>
<form
id='channel-form'
@@ -50,6 +50,7 @@ import {
import { useChatPresets } from '@/features/chat/hooks/use-chat-presets'
import { resolveChatUrl, type ChatPreset } from '@/features/chat/lib/chat-links'
import { sendToFluent } from '@/features/chat/lib/send-to-fluent'
import { encodeChannelConnectionInfo } from '@/lib/channel-connection-info'
import { copyToClipboard } from '@/lib/copy-to-clipboard'
import { updateApiKeyStatus } from '../api'
@@ -70,14 +71,6 @@ function getServerAddress(): string {
return window.location.origin
}
function encodeConnectionString(key: string, url: string): string {
return JSON.stringify({
_type: 'newapi_channel_conn',
key,
url,
})
}
type DataTableRowActionsProps<TData> = {
row: Row<TData>
}
@@ -262,7 +255,10 @@ export function DataTableRowActions<TData>({
onClick={async () => {
const realKey = getCachedRealKey()
if (!realKey) return
const connStr = encodeConnectionString(realKey, getServerAddress())
const connStr = encodeChannelConnectionInfo(
realKey,
getServerAddress()
)
const ok = await copyToClipboard(connStr)
if (ok) toast.success(t('Copied'))
}}