From 366f69a46e562df56009a02351391ea63b29978f Mon Sep 17 00:00:00 2001 From: duyua9 Date: Thu, 23 Apr 2026 02:31:59 +0800 Subject: [PATCH] fix: handle clipboard copy failures gracefully --- src/locales/en.json | 2 ++ src/locales/zh-Hans.json | 2 ++ src/locales/zh-Hant.json | 2 ++ src/prompt.ts | 17 +++++++++++++-- test/copy-action-fallback.test.mjs | 35 ++++++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 test/copy-action-fallback.test.mjs diff --git a/src/locales/en.json b/src/locales/en.json index 0bd42784..9fdedeae 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -45,6 +45,8 @@ "Cancel": "Cancel", "Exit the program": "Exit the program", "Running": "Running", + "Clipboard unavailable. Copy the script manually:": "Clipboard unavailable. Copy the script manually:", + "Clipboard copy failed; script printed for manual copy.": "Clipboard copy failed; script printed for manual copy.", "Copied to clipboard!": "Copied to clipboard!", "Your new script": "Your new script", "Invalid config property": "Invalid config property", diff --git a/src/locales/zh-Hans.json b/src/locales/zh-Hans.json index 46307188..3bb59cf7 100644 --- a/src/locales/zh-Hans.json +++ b/src/locales/zh-Hans.json @@ -45,6 +45,8 @@ "Cancel": "取消", "Exit the program": "退出程序", "Running": "正在运行", + "Clipboard unavailable. Copy the script manually:": "剪贴板不可用,请手动复制脚本:", + "Clipboard copy failed; script printed for manual copy.": "复制到剪贴板失败,已打印脚本供手动复制。", "Copied to clipboard!": "已复制到剪贴板!", "Your new script": "您的新脚本", "Invalid config property": "无效的配置属性", diff --git a/src/locales/zh-Hant.json b/src/locales/zh-Hant.json index 490437fd..087bf188 100644 --- a/src/locales/zh-Hant.json +++ b/src/locales/zh-Hant.json @@ -45,6 +45,8 @@ "Cancel": "取消", "Exit the program": "退出程序", "Running": "正在運行", + "Clipboard unavailable. Copy the script manually:": "剪貼簿不可用,請手動複製腳本:", + "Clipboard copy failed; script printed for manual copy.": "複製到剪貼簿失敗,已列印腳本供手動複製。", "Copied to clipboard!": "已複製到剪貼簿!", "Your new script": "您的新腳本", "Invalid config property": "無效的配置屬性", diff --git a/src/prompt.ts b/src/prompt.ts index 145c07b3..a201b362 100644 --- a/src/prompt.ts +++ b/src/prompt.ts @@ -36,6 +36,20 @@ const sample = (arr: T[]): T | undefined => { return len ? arr[Math.floor(Math.random() * len)] : undefined; }; +export async function copyScriptWithFallback( + script: string, + writeClipboard: (value: string) => Promise = clipboardy.write, + ui: { note: typeof p.note; outro: typeof p.outro } = { note: p.note, outro: p.outro } +) { + try { + await writeClipboard(script); + ui.outro(i18n.t('Copied to clipboard!')); + } catch (_error) { + ui.note(script, i18n.t('Clipboard unavailable. Copy the script manually:')); + ui.outro(i18n.t('Clipboard copy failed; script printed for manual copy.')); + } +} + async function runScript(script: string) { p.outro(`${i18n.t('Running')}: ${script}`); console.log(''); @@ -198,8 +212,7 @@ async function runOrReviseFlow( label: '📋 ' + i18n.t('Copy'), hint: i18n.t('Copy the generated script to your clipboard'), value: async () => { - await clipboardy.write(script); - p.outro(i18n.t('Copied to clipboard!')); + await copyScriptWithFallback(script); }, }, { diff --git a/test/copy-action-fallback.test.mjs b/test/copy-action-fallback.test.mjs new file mode 100644 index 00000000..5b038c54 --- /dev/null +++ b/test/copy-action-fallback.test.mjs @@ -0,0 +1,35 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import jitiFactory from 'jiti'; + +const jiti = jitiFactory(import.meta.url, { interopDefault: true }); +const { copyScriptWithFallback } = await jiti('../src/prompt.ts'); + +test('copy fallback prints the script when clipboard write fails', async () => { + const notes = []; + const outros = []; + await copyScriptWithFallback( + 'echo hello', + async () => { + throw new Error('clipboard backend unavailable'); + }, + { + note: (...args) => notes.push(args), + outro: (...args) => outros.push(args), + } + ); + assert.equal(notes.length, 1); + assert.deepEqual(notes[0], ['echo hello', 'Clipboard unavailable. Copy the script manually:']); + assert.equal(outros.at(-1)?.[0], 'Clipboard copy failed; script printed for manual copy.'); +}); + +test('copy success still reports clipboard success', async () => { + const notes = []; + const outros = []; + await copyScriptWithFallback('echo hello', async () => {}, { + note: (...args) => notes.push(args), + outro: (...args) => outros.push(args), + }); + assert.equal(notes.length, 0); + assert.equal(outros.at(-1)?.[0], 'Copied to clipboard!'); +});