diff --git a/frontend/src/components/CsvFileBrowser.jsx b/frontend/src/components/CsvFileBrowser.jsx
index 9ad74f2..112ef6e 100644
--- a/frontend/src/components/CsvFileBrowser.jsx
+++ b/frontend/src/components/CsvFileBrowser.jsx
@@ -59,7 +59,8 @@ import {
FaCalendar,
FaDatabase,
FaSync,
- FaCog
+ FaCog,
+ FaCopy
} from 'react-icons/fa'
import * as api from '../services/api'
@@ -348,6 +349,15 @@ function FileTree({ tree, selectedFiles, onFileToggle, expandedItems, onToggleEx
onClick={() => onFileToggle(fileNode, 'excel')}
/>
+
+ }
+ colorScheme="purple"
+ variant="outline"
+ onClick={() => onFileToggle(fileNode, 'copy')}
+ />
+
@@ -457,6 +467,40 @@ export default function CsvFileBrowser() {
duration: 3000
})
}
+ } else if (action === 'copy') {
+ // Copy path to clipboard
+ try {
+ await navigator.clipboard.writeText(fileNode.path)
+ toast({
+ title: '📋 Path copied',
+ description: `File path copied to clipboard`,
+ status: 'success',
+ duration: 2000
+ })
+ } catch (error) {
+ // Fallback for older browsers
+ try {
+ const textArea = document.createElement('textarea')
+ textArea.value = fileNode.path
+ document.body.appendChild(textArea)
+ textArea.select()
+ document.execCommand('copy')
+ document.body.removeChild(textArea)
+ toast({
+ title: '📋 Path copied',
+ description: `File path copied to clipboard`,
+ status: 'success',
+ duration: 2000
+ })
+ } catch (fallbackError) {
+ toast({
+ title: '❌ Failed to copy path',
+ description: 'Clipboard access not available',
+ status: 'error',
+ duration: 3000
+ })
+ }
+ }
} else {
// Toggle selection
const newSelected = new Set(selectedFiles)
@@ -469,6 +513,56 @@ export default function CsvFileBrowser() {
}
}
+ // Copy selected file paths
+ const copySelectedPaths = async () => {
+ if (selectedFiles.size === 0) {
+ toast({
+ title: '⚠️ No files selected',
+ description: 'Please select files to copy their paths',
+ status: 'warning',
+ duration: 2000
+ })
+ return
+ }
+
+ try {
+ const filePaths = Array.from(selectedFiles)
+ const pathsText = filePaths.join('\n')
+ await navigator.clipboard.writeText(pathsText)
+ toast({
+ title: '📋 Paths copied',
+ description: `${filePaths.length} file path(s) copied to clipboard`,
+ status: 'success',
+ duration: 2000
+ })
+ } catch (error) {
+ // Fallback for older browsers
+ try {
+ const filePaths = Array.from(selectedFiles)
+ const pathsText = filePaths.join('\n')
+ const textArea = document.createElement('textarea')
+ textArea.value = pathsText
+ document.body.appendChild(textArea)
+ textArea.select()
+ document.execCommand('copy')
+ document.body.removeChild(textArea)
+ toast({
+ title: '📋 Paths copied',
+ description: `${filePaths.length} file path(s) copied to clipboard`,
+ status: 'success',
+ duration: 2000
+ })
+ } catch (fallbackError) {
+ toast({
+ title: '❌ Failed to copy paths',
+ description: 'Clipboard access not available',
+ status: 'error',
+ duration: 3000
+ })
+ }
+ }
+ }
+
// Launch PlotJuggler with selected files
const launchSelectedFiles = async () => {
if (selectedFiles.size === 0) {
@@ -692,6 +786,14 @@ export default function CsvFileBrowser() {
>
Open in PlotJuggler
+ }
+ colorScheme="purple"
+ variant="outline"
+ onClick={copySelectedPaths}
+ >
+ Copy Paths
+