@@ -4,7 +4,7 @@ import type { PromptItemWithVariables, EditableElement, Category } from "@/utils
44import { getPromptSelectorStyles } from "../utils/styles" ;
55import { extractVariables } from "../utils/variableParser" ;
66import { showVariableInput } from "./VariableInput" ;
7- import { isDarkMode } from "@/utils/tools" ;
7+ import { isDarkMode , getCopyShortcutText } from "@/utils/tools" ;
88import { getCategories } from "@/utils/categoryUtils" ;
99import { getGlobalSetting } from "@/utils/globalSettings" ;
1010import { t } from "@/utils/i18n" ;
@@ -28,6 +28,7 @@ const PromptSelector: React.FC<PromptSelectorProps> = ({
2828 const [ selectedCategoryId , setSelectedCategoryId ] = useState < string | null > ( null ) ;
2929 const [ categoriesMap , setCategoriesMap ] = useState < Record < string , Category > > ( { } ) ;
3030 const [ closeOnOutsideClick , setCloseOnOutsideClick ] = useState ( true ) ;
31+ const [ copiedId , setCopiedId ] = useState < string | null > ( null ) ;
3132 const searchInputRef = useRef < HTMLInputElement > ( null ) ;
3233 const listRef = useRef < HTMLDivElement > ( null ) ;
3334 const modalRef = useRef < HTMLDivElement > ( null ) ;
@@ -149,6 +150,20 @@ const PromptSelector: React.FC<PromptSelectorProps> = ({
149150 setSelectedCategoryId ( allOptions [ nextIndex ] ) ;
150151 } ;
151152
153+ // 复制提示词内容
154+ const copyPrompt = async ( e : React . MouseEvent , prompt : PromptItemWithVariables ) => {
155+ e . stopPropagation ( ) ; // 阻止事件冒泡,避免触发选择提示词
156+ try {
157+ await navigator . clipboard . writeText ( prompt . content ) ;
158+ setCopiedId ( prompt . id ) ;
159+ setTimeout ( ( ) => {
160+ setCopiedId ( null ) ;
161+ } , 2000 ) ; // 2秒后清除复制状态
162+ } catch ( err ) {
163+ console . error ( t ( 'copyFailed' ) , err ) ;
164+ }
165+ } ;
166+
152167 // 键盘导航
153168 useEffect ( ( ) => {
154169 const handleKeyDown = ( e : KeyboardEvent ) => {
@@ -181,6 +196,18 @@ const PromptSelector: React.FC<PromptSelectorProps> = ({
181196 // Tab键循环切换分类
182197 cycleCategorySelection ( e . shiftKey ? 'prev' : 'next' ) ;
183198 break ;
199+ case "c" :
200+ // Ctrl+C (Windows) 或 Command+C (Mac) 复制当前选中的提示词
201+ if ( ( e . ctrlKey || e . metaKey ) && filteredPrompts [ selectedIndex ] ) {
202+ e . preventDefault ( ) ;
203+ navigator . clipboard . writeText ( filteredPrompts [ selectedIndex ] . content )
204+ . then ( ( ) => {
205+ setCopiedId ( filteredPrompts [ selectedIndex ] . id ) ;
206+ setTimeout ( ( ) => setCopiedId ( null ) , 2000 ) ;
207+ } )
208+ . catch ( err => console . error ( t ( 'copyFailed' ) , err ) ) ;
209+ }
210+ break ;
184211 }
185212 } ;
186213
@@ -482,7 +509,25 @@ const PromptSelector: React.FC<PromptSelectorProps> = ({
482509 onClick = { ( ) => applyPrompt ( prompt ) }
483510 onMouseEnter = { ( ) => ! isKeyboardNav && setSelectedIndex ( index ) }
484511 >
485- < div className = "qp-prompt-title" > { prompt . title } </ div >
512+ < div className = "qp-flex qp-justify-between qp-items-center" >
513+ < div className = "qp-prompt-title" > { prompt . title } </ div >
514+ < button
515+ className = { `qp-copy-button ${ copiedId === prompt . id ? 'qp-copied' : '' } ` }
516+ onClick = { ( e ) => copyPrompt ( e , prompt ) }
517+ title = { t ( 'copyPrompt' ) }
518+ >
519+ { copiedId === prompt . id ? (
520+ < svg className = "qp-copy-icon" viewBox = "0 0 24 24" fill = "none" xmlns = "http://www.w3.org/2000/svg" >
521+ < path d = "M20 6L9 17L4 12" stroke = "currentColor" strokeWidth = "2" strokeLinecap = "round" strokeLinejoin = "round" />
522+ </ svg >
523+ ) : (
524+ < svg className = "qp-copy-icon" viewBox = "0 0 24 24" fill = "none" xmlns = "http://www.w3.org/2000/svg" >
525+ < path d = "M8 4v12a2 2 0 002 2h8a2 2 0 002-2V7.242a2 2 0 00-.602-1.43L16.083 2.57A2 2 0 0014.685 2H10a2 2 0 00-2 2z" stroke = "currentColor" strokeWidth = "2" strokeLinecap = "round" strokeLinejoin = "round" />
526+ < path d = "M16 2v3a2 2 0 002 2h3M4 8v12a2 2 0 002 2h8" stroke = "currentColor" strokeWidth = "2" strokeLinecap = "round" strokeLinejoin = "round" />
527+ </ svg >
528+ ) }
529+ </ button >
530+ </ div >
486531 < div className = "qp-prompt-preview" > { prompt . content } </ div >
487532 < div className = "qp-prompt-meta" >
488533 { category && (
@@ -543,7 +588,7 @@ const PromptSelector: React.FC<PromptSelectorProps> = ({
543588
544589 < div className = "qp-modal-footer" >
545590 < span > { t ( 'totalPrompts2' , [ filteredPrompts . length . toString ( ) ] ) } </ span >
546- < span > { t ( 'navigationHelp' ) } </ span >
591+ < span > { t ( 'pressCtrlCToCopy' , [ getCopyShortcutText ( ) ] ) } • { t ( ' navigationHelp') } </ span >
547592 </ div >
548593 </ div >
549594 </ div >
0 commit comments