diff --git a/src/web-ui/src/app/components/panels/base/FlexiblePanel.tsx b/src/web-ui/src/app/components/panels/base/FlexiblePanel.tsx index a046bced26..f668d8d366 100644 --- a/src/web-ui/src/app/components/panels/base/FlexiblePanel.tsx +++ b/src/web-ui/src/app/components/panels/base/FlexiblePanel.tsx @@ -713,6 +713,7 @@ const FlexiblePanel: React.FC = memo(({ key={sessionId} sessionId={sessionId} autoFocus={true} + supportsCopyPaste={false} /> diff --git a/src/web-ui/src/app/scenes/terminal/TerminalScene.tsx b/src/web-ui/src/app/scenes/terminal/TerminalScene.tsx index 87deb411c8..b2f4a0a8df 100644 --- a/src/web-ui/src/app/scenes/terminal/TerminalScene.tsx +++ b/src/web-ui/src/app/scenes/terminal/TerminalScene.tsx @@ -44,6 +44,7 @@ const TerminalScene: React.FC = ({ isActive = true }) => { showStatusBar onExit={handleExit} onClose={handleClose} + supportsCopyPaste={false} /> ) : (
diff --git a/src/web-ui/src/flow_chat/tool-cards/TerminalToolCard.tsx b/src/web-ui/src/flow_chat/tool-cards/TerminalToolCard.tsx index 339958a151..47fcddac0e 100644 --- a/src/web-ui/src/flow_chat/tool-cards/TerminalToolCard.tsx +++ b/src/web-ui/src/flow_chat/tool-cards/TerminalToolCard.tsx @@ -487,6 +487,7 @@ export const TerminalToolCard: React.FC = ({ successMessage={t('toolCards.terminal.commandCopied')} failureMessage={t('toolCards.terminal.copyCommandFailed')} ariaLabel={t('toolCards.terminal.copyCommand')} + showSuccessNotification={false} /> ); diff --git a/src/web-ui/src/flow_chat/tool-cards/ToolCardHeaderActions.tsx b/src/web-ui/src/flow_chat/tool-cards/ToolCardHeaderActions.tsx index 74c17a4f58..3f1561d6c4 100644 --- a/src/web-ui/src/flow_chat/tool-cards/ToolCardHeaderActions.tsx +++ b/src/web-ui/src/flow_chat/tool-cards/ToolCardHeaderActions.tsx @@ -30,6 +30,7 @@ interface ToolCardCopyActionProps { ariaLabel?: string; className?: string; disabled?: boolean; + showSuccessNotification?: boolean; } export const ToolCardCopyAction: React.FC = ({ @@ -41,11 +42,13 @@ export const ToolCardCopyAction: React.FC = ({ ariaLabel, className, disabled, + showSuccessNotification }) => { const { copied, copy } = useCopyTextAction({ getText, successMessage, failureMessage, + showSuccessNotification }); return ( diff --git a/src/web-ui/src/flow_chat/tool-cards/useCopyTextAction.ts b/src/web-ui/src/flow_chat/tool-cards/useCopyTextAction.ts index 235639a30d..d33c2e234b 100644 --- a/src/web-ui/src/flow_chat/tool-cards/useCopyTextAction.ts +++ b/src/web-ui/src/flow_chat/tool-cards/useCopyTextAction.ts @@ -8,6 +8,7 @@ interface UseCopyTextActionOptions { successMessage: string; failureMessage: string; resetMs?: number; + showSuccessNotification?: boolean; } export function useCopyTextAction({ @@ -15,6 +16,7 @@ export function useCopyTextAction({ successMessage, failureMessage, resetMs = 1600, + showSuccessNotification = true, }: UseCopyTextActionOptions) { const [copied, setCopied] = useState(false); const resetTimerRef = useRef(null); @@ -42,7 +44,10 @@ export function useCopyTextAction({ } setCopied(true); - notificationService.success(successMessage, { duration: resetMs }); + if (showSuccessNotification){ + notificationService.success(successMessage, { duration: resetMs }); + } + if (resetTimerRef.current !== null) { window.clearTimeout(resetTimerRef.current); @@ -51,7 +56,7 @@ export function useCopyTextAction({ setCopied(false); resetTimerRef.current = null; }, resetMs); - }, [failureMessage, getText, resetMs, successMessage]); + }, [failureMessage, getText, resetMs, successMessage,showSuccessNotification]); return { copied, copy }; } diff --git a/src/web-ui/src/shared/context-menu-system/core/ContextResolver.ts b/src/web-ui/src/shared/context-menu-system/core/ContextResolver.ts index b543f9be3e..8e6a2639ce 100644 --- a/src/web-ui/src/shared/context-menu-system/core/ContextResolver.ts +++ b/src/web-ui/src/shared/context-menu-system/core/ContextResolver.ts @@ -130,7 +130,7 @@ export class ContextResolver { `terminal-${Date.now()}`; const sessionId = terminalElement.getAttribute('data-session-id') || undefined; const isReadOnly = terminalElement.getAttribute('data-readonly') === 'true'; - + const supportsCopyPaste = terminalElement.getAttribute('data-supports-copy-paste') !== 'false'; const selection = window.getSelection(); const selectedText = selection?.toString() || ''; @@ -143,7 +143,8 @@ export class ContextResolver { sessionId, hasSelection, selectedText: hasSelection ? selectedText : undefined, - isReadOnly + isReadOnly, + supportsCopyPaste }; } diff --git a/src/web-ui/src/shared/context-menu-system/providers/TerminalMenuProvider.ts b/src/web-ui/src/shared/context-menu-system/providers/TerminalMenuProvider.ts index 46e452c441..4d12b9803f 100644 --- a/src/web-ui/src/shared/context-menu-system/providers/TerminalMenuProvider.ts +++ b/src/web-ui/src/shared/context-menu-system/providers/TerminalMenuProvider.ts @@ -19,9 +19,10 @@ export class TerminalMenuProvider implements IMenuProvider { const terminalContext = context as TerminalContext; const items: MenuItem[] = []; const isReadOnly = terminalContext.isReadOnly ?? false; - + const supportsCopyPaste = terminalContext.supportsCopyPaste ?? true; - items.push({ + if(supportsCopyPaste) { + items.push({ id: 'terminal-copy', label: i18nService.t('common:actions.copy'), icon: 'Copy', @@ -51,6 +52,8 @@ export class TerminalMenuProvider implements IMenuProvider { } }); } + } + items.push({ diff --git a/src/web-ui/src/shared/context-menu-system/types/context.types.ts b/src/web-ui/src/shared/context-menu-system/types/context.types.ts index 66adf1cd49..ca4e7b9605 100644 --- a/src/web-ui/src/shared/context-menu-system/types/context.types.ts +++ b/src/web-ui/src/shared/context-menu-system/types/context.types.ts @@ -122,6 +122,8 @@ export interface TerminalContext extends BaseContext { selectedText?: string; isReadOnly?: boolean; + + supportsCopyPaste?: boolean; } diff --git a/src/web-ui/src/tools/terminal/components/ConnectedTerminal.tsx b/src/web-ui/src/tools/terminal/components/ConnectedTerminal.tsx index 511d88fc55..d4c18a45e9 100644 --- a/src/web-ui/src/tools/terminal/components/ConnectedTerminal.tsx +++ b/src/web-ui/src/tools/terminal/components/ConnectedTerminal.tsx @@ -37,6 +37,7 @@ export interface ConnectedTerminalProps { onClose?: () => void; onTitleChange?: (title: string) => void; onExit?: (exitCode?: number) => void; + supportsCopyPaste?: boolean; } const ConnectedTerminal: React.FC = memo(({ @@ -49,6 +50,7 @@ const ConnectedTerminal: React.FC = memo(({ onClose, onTitleChange, onExit, + supportsCopyPaste = true, }) => { const terminalRef = useRef(null); const [title, setTitle] = useState(initialSession?.name || 'Terminal'); @@ -412,6 +414,7 @@ const ConnectedTerminal: React.FC = memo(({ onReady={handleTerminalReady} onPaste={handlePaste} preventShrinkBelowColsRef={preventShrinkBelowColsRef} + supportsCopyPaste={supportsCopyPaste} /> {showStatusBar && session && ( diff --git a/src/web-ui/src/tools/terminal/components/Terminal.tsx b/src/web-ui/src/tools/terminal/components/Terminal.tsx index 9b11e65f71..313fcfcae1 100644 --- a/src/web-ui/src/tools/terminal/components/Terminal.tsx +++ b/src/web-ui/src/tools/terminal/components/Terminal.tsx @@ -130,6 +130,7 @@ export interface TerminalProps { * content. Set back to 0 (or leave unset) to restore normal resize behaviour. */ preventShrinkBelowColsRef?: React.MutableRefObject; + supportsCopyPaste?: boolean; } export interface TerminalRef { @@ -179,6 +180,7 @@ const Terminal = forwardRef(({ onReady, onPaste, preventShrinkBelowColsRef, + supportsCopyPaste = true, }, ref) => { const containerRef = useRef(null); const terminalRef = useRef(null); @@ -684,6 +686,7 @@ const Terminal = forwardRef(({ className={`bitfun-terminal ${className}`} data-terminal-id={terminalId} data-session-id={sessionId} + data-supports-copy-paste={supportsCopyPaste?'true':'false'} >