Feat/web UI fixes v2 (#2152)

* fix(web): 修复复制按钮和插件安装对话框UI问题

- 新增 clipboard.ts 工具函数支持 Clipboard API 降级
- 修复添加机器人页面 Webhook URL 复制按钮未生效
- 修复 API 集成对话框 API Key 复制按钮未生效
- 修复 Bot 会话监控用户 ID 复制按钮未生效
- 修复插件安装进度状态框横向溢出和小屏缩放问题

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(web): improve clipboard copy with Selection API fallback

Replace navigator.clipboard.writeText with Selection API + execCommand
for reliable copying in non-secure contexts. Remove duplicate dialog.
Fix scanProviderModels type signature to accept rerank model type.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(web): revert package-lock.json to match upstream

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(web): fix prettier formatting errors

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(web): unify all clipboard copy to use copyToClipboard utility

- Fix embed code copy button not working in non-secure contexts
- Add copy animation (check icon) to embed code button via EmbedCodeField component
- Replace raw navigator.clipboard calls in plugins/page.tsx and BotLogCard.tsx
- Remove duplicated inline fallback implementations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Junyan Qin <rockchinq@gmail.com>
This commit is contained in:
sheetung
2026-04-26 01:57:54 +08:00
committed by GitHub
parent 69b87a0d8a
commit c1168745b7
8 changed files with 171 additions and 155 deletions
@@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
import { Check, ChevronDown, ChevronRight, Copy } from 'lucide-react';
import { toast } from 'sonner';
import { cn } from '@/lib/utils';
import { copyToClipboard } from '@/app/utils/clipboard';
const LEVEL_STYLES: Record<string, string> = {
error: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400',
@@ -31,36 +32,19 @@ export function BotLogCard({
function copySessionId() {
const text = botLog.message_session_id;
if (navigator.clipboard?.writeText) {
navigator.clipboard
.writeText(text)
.then(() => {
copyToClipboard(text)
.then((ok) => {
if (ok) {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
toast.success(t('common.copySuccess'));
})
.catch(() => fallbackCopy(text));
} else {
fallbackCopy(text);
}
}
function fallbackCopy(text: string) {
const ta = document.createElement('textarea');
ta.value = text;
ta.style.cssText = 'position:fixed;left:-9999px;top:-9999px';
document.body.appendChild(ta);
ta.focus();
ta.select();
try {
document.execCommand('copy');
setCopied(true);
setTimeout(() => setCopied(false), 2000);
toast.success(t('common.copySuccess'));
} catch {
toast.error(t('common.copyFailed'));
}
document.body.removeChild(ta);
} else {
toast.error(t('common.copyFailed'));
}
})
.catch(() => {
toast.error(t('common.copyFailed'));
});
}
function formatTime(timestamp: number) {
@@ -19,6 +19,7 @@ import {
ThumbsUp,
ThumbsDown,
} from 'lucide-react';
import { copyToClipboard } from '@/app/utils/clipboard';
import {
MessageChainComponent,
Plain,
@@ -108,10 +109,9 @@ const BotSessionMonitor = forwardRef<
};
const copyUserId = (userId: string) => {
navigator.clipboard.writeText(userId).then(() => {
setCopiedUserId(true);
setTimeout(() => setCopiedUserId(false), 2000);
});
copyToClipboard(userId).catch(() => {});
setCopiedUserId(true);
setTimeout(() => setCopiedUserId(false), 2000);
};
const loadSessions = useCallback(async () => {