diff --git a/apps/web/src/components/settings/SystemInfoPanel.test.mjs b/apps/web/src/components/settings/SystemInfoPanel.test.mjs index 7ac7ff98..003c0e8d 100644 --- a/apps/web/src/components/settings/SystemInfoPanel.test.mjs +++ b/apps/web/src/components/settings/SystemInfoPanel.test.mjs @@ -54,3 +54,13 @@ describe("system information diagnostic fields", () => { expect(source).toContain("formatLastSuccessfulSync"); }); }); + +describe("system information copy feedback", () => { + test("shows visible success and failure feedback", () => { + expect(source).toContain('useState<"idle" | "copied" | "error">'); + expect(source).toContain(" { const { t, i18n } = useTranslation(); - const [copied, setCopied] = useState(false); + const [copyState, setCopyState] = useState<"idle" | "copied" | "error">("idle"); + const copyResetTimeoutRef = useRef(null); const [desktopUpdateChecked, setDesktopUpdateChecked] = useState(false); const [viewportRevision, setViewportRevision] = useState(0); const queryClient = useQueryClient(); @@ -308,6 +310,9 @@ export const SystemInfoPanel = ({ active = true }: { active?: boolean }) => { window.addEventListener("resize", onResize); return () => window.removeEventListener("resize", onResize); }, [active]); + useEffect(() => () => { + if (copyResetTimeoutRef.current !== null) window.clearTimeout(copyResetTimeoutRef.current); + }, []); const desktopUpdateCheckMutation = useMutation({ mutationFn: () => desktopBridge!.checkUpdate(), onSuccess: (status) => { @@ -391,9 +396,13 @@ export const SystemInfoPanel = ({ active = true }: { active?: boolean }) => { const text = infoGroups .map((group) => [group.title, ...group.items.map((item) => `${item.label}: ${item.value}`)].join("\n")) .join("\n\n"); - if (!(await copyTextToClipboard(text))) return; - setCopied(true); - window.setTimeout(() => setCopied(false), 1600); + const copied = await copyTextToClipboard(text); + setCopyState(copied ? "copied" : "error"); + if (copyResetTimeoutRef.current !== null) window.clearTimeout(copyResetTimeoutRef.current); + copyResetTimeoutRef.current = window.setTimeout(() => { + setCopyState("idle"); + copyResetTimeoutRef.current = null; + }, copied ? 2200 : 3000); }; const clientAheadOfInstance = isClientAheadOfInstance( @@ -431,13 +440,25 @@ export const SystemInfoPanel = ({ active = true }: { active?: boolean }) => { @@ -563,6 +584,11 @@ export const SystemInfoPanel = ({ active = true }: { active?: boolean }) => { ); })} + {copyState !== "idle" ? ( + + {t(copyState === "copied" ? "systemInfo.copySucceeded" : "systemInfo.copyFailed")} + + ) : null} ); }; diff --git a/packages/shared/src/i18n/en-US.ts b/packages/shared/src/i18n/en-US.ts index ef7d0da7..717eb7ae 100644 --- a/packages/shared/src/i18n/en-US.ts +++ b/packages/shared/src/i18n/en-US.ts @@ -1225,6 +1225,8 @@ export const enUS = { browserMode: "Browser", unknown: "Unknown", copy: "Copy info", + copySucceeded: "System information copied to the clipboard.", + copyFailed: "Copy failed. Check clipboard permissions and try again.", clientAheadOfInstanceByPlatform: { cloudflare: "This client is newer than the connected cloud instance. You can wait for the daily automatic instance update, or run the Update deployed EdgeEver workflow.", docker: "This client is newer than the connected cloud instance. You can wait for the daily automatic instance update, or run ./update.sh in the install directory (default ~/edgeever).", diff --git a/packages/shared/src/i18n/ja.ts b/packages/shared/src/i18n/ja.ts index 3053a13f..a7dbcecc 100644 --- a/packages/shared/src/i18n/ja.ts +++ b/packages/shared/src/i18n/ja.ts @@ -1225,6 +1225,8 @@ export const ja = { browserMode: "ブラウザ", unknown: "不明", copy: "情報をコピー", + copySucceeded: "システム情報をクリップボードにコピーしました。", + copyFailed: "コピーに失敗しました。クリップボードの権限を確認して、もう一度お試しください。", clientAheadOfInstanceByPlatform: { cloudflare: "このクライアントは接続中のクラウドインスタンスより新しいです。毎日の自動インスタンス更新を待つか、Update deployed EdgeEver ワークフローを実行できます。", docker: "このクライアントは接続中のクラウドインスタンスより新しいです。毎日の自動インスタンス更新を待つか、インストールディレクトリで ./update.sh を実行できます(既定は ~/edgeever)。", diff --git a/packages/shared/src/i18n/zh-CN.ts b/packages/shared/src/i18n/zh-CN.ts index 22fdc3f2..613bf79c 100644 --- a/packages/shared/src/i18n/zh-CN.ts +++ b/packages/shared/src/i18n/zh-CN.ts @@ -1225,6 +1225,8 @@ export const zhCN = { browserMode: "浏览器", unknown: "未知", copy: "复制信息", + copySucceeded: "系统信息已复制到剪贴板。", + copyFailed: "复制失败,请检查剪贴板权限后重试。", clientAheadOfInstanceByPlatform: { cloudflare: "当前客户端版本高于云端实例。可等待每天自动更新,或手动运行 Update deployed EdgeEver 工作流。", docker: "当前客户端版本高于云端实例。可等待每天自动更新,或在安装目录执行 ./update.sh(默认 ~/edgeever)。",