11// Copyright 2025, Command Line Inc.
22// SPDX-License-Identifier: Apache-2.0
33
4+ import { WaveAIModel } from "@/app/aipanel/waveai-model" ;
5+ import { ContextMenuModel } from "@/app/store/contextmenu" ;
46import { BuilderBuildPanelModel } from "@/builder/store/builder-buildpanel-model" ;
57import { useAtomValue } from "jotai" ;
6- import { memo , useEffect , useRef } from "react" ;
8+ import { memo , useCallback , useEffect , useRef } from "react" ;
9+ import { debounce } from "throttle-debounce" ;
10+
11+ function handleBuildPanelContextMenu ( e : React . MouseEvent , selectedText : string ) : void {
12+ e . preventDefault ( ) ;
13+ e . stopPropagation ( ) ;
14+
15+ if ( ! selectedText ) {
16+ return ;
17+ }
18+
19+ const menu : ContextMenuItem [ ] = [
20+ { role : "copy" } ,
21+ { type : "separator" } ,
22+ {
23+ label : "Add to Context" ,
24+ click : ( ) => {
25+ const model = WaveAIModel . getInstance ( ) ;
26+ const formattedText = `from builder output:\n\`\`\`\n${ selectedText } \n\`\`\`` ;
27+ model . appendText ( formattedText , true ) ;
28+ model . focusInput ( ) ;
29+ } ,
30+ } ,
31+ ] ;
32+ ContextMenuModel . showContextMenu ( menu , e ) ;
33+ }
734
835const BuilderBuildPanel = memo ( ( ) => {
936 const model = BuilderBuildPanelModel . getInstance ( ) ;
1037 const outputLines = useAtomValue ( model . outputLines ) ;
1138 const scrollRef = useRef < HTMLDivElement > ( null ) ;
39+ const preRef = useRef < HTMLPreElement > ( null ) ;
1240
1341 useEffect ( ( ) => {
1442 model . initialize ( ) ;
@@ -23,13 +51,38 @@ const BuilderBuildPanel = memo(() => {
2351 }
2452 } , [ outputLines ] ) ;
2553
54+ const debouncedCopyOnSelect = useCallback (
55+ debounce ( 50 , ( ) => {
56+ const selection = window . getSelection ( ) ;
57+ if ( selection && selection . toString ( ) . length > 0 ) {
58+ navigator . clipboard . writeText ( selection . toString ( ) ) ;
59+ }
60+ } ) ,
61+ [ ]
62+ ) ;
63+
64+ const handleMouseUp = useCallback ( ( ) => {
65+ debouncedCopyOnSelect ( ) ;
66+ } , [ debouncedCopyOnSelect ] ) ;
67+
68+ const handleContextMenu = useCallback ( ( e : React . MouseEvent ) => {
69+ const selection = window . getSelection ( ) ;
70+ const selectedText = selection ? selection . toString ( ) : "" ;
71+ handleBuildPanelContextMenu ( e , selectedText ) ;
72+ } , [ ] ) ;
73+
2674 return (
2775 < div className = "w-full h-full flex flex-col bg-black" >
2876 < div className = "flex-shrink-0 px-3 py-2 border-b border-gray-700" >
2977 < span className = "text-sm font-semibold text-gray-300" > Build Output</ span >
3078 </ div >
3179 < div ref = { scrollRef } className = "flex-1 overflow-y-auto overflow-x-auto p-2" >
32- < pre className = "font-mono text-xs text-gray-100 whitespace-pre" >
80+ < pre
81+ ref = { preRef }
82+ className = "font-mono text-xs text-gray-100 whitespace-pre"
83+ onMouseUp = { handleMouseUp }
84+ onContextMenu = { handleContextMenu }
85+ >
3386 { outputLines . length === 0 ? (
3487 < span className = "text-secondary" > Waiting for output...</ span >
3588 ) : (
0 commit comments