Compare commits
5 Commits
plugin_mig
...
f2718282c7
| Author | SHA1 | Date | |
|---|---|---|---|
| f2718282c7 | |||
| 86e30e6ec3 | |||
| b8f26496a0 | |||
| a761b81dd1 | |||
| c1948182ec |
@ -18,16 +18,16 @@
|
|||||||
"filename": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\audio-service\\recordings\\audio_capture_20260220_193822.wav",
|
"filename": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\audio-service\\recordings\\audio_capture_20260220_193822.wav",
|
||||||
"name": "Pee pee\npoo poo",
|
"name": "Pee pee\npoo poo",
|
||||||
"playbackType": "playStop",
|
"playbackType": "playStop",
|
||||||
"startTime": 27.756510985786615,
|
"startTime": 27.587412587412587,
|
||||||
"volume": 1
|
"volume": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"endTime": 28.597210828548004,
|
"endTime": 27.516843118383072,
|
||||||
"filename": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\audio-service\\recordings\\audio_capture_20260220_200442.wav",
|
"filename": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\audio-service\\recordings\\audio_capture_20260220_200442.wav",
|
||||||
"name": "Clip 20260220_200442",
|
"name": "Clip 20260220_200442",
|
||||||
"playbackType": "playStop",
|
"playbackType": "playOverlap",
|
||||||
"startTime": 26.1853978671042,
|
"startTime": 25.120307988450435,
|
||||||
"volume": 1
|
"volume": 0.64
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@ -28,9 +28,9 @@ class AudioRecorder:
|
|||||||
self.channels = 2
|
self.channels = 2
|
||||||
self.buffer = np.zeros((int(self.duration * self.sample_rate), self.channels), dtype=np.float32)
|
self.buffer = np.zeros((int(self.duration * self.sample_rate), self.channels), dtype=np.float32)
|
||||||
self.recordings_dir = "recordings"
|
self.recordings_dir = "recordings"
|
||||||
|
|
||||||
self.stream = sd.InputStream(
|
self.stream = sd.InputStream(
|
||||||
samplerate=self.sample_rate,
|
samplerate=self.sample_rate,
|
||||||
channels=self.channels,
|
|
||||||
callback=self.record_callback
|
callback=self.record_callback
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,10 +43,9 @@ class AudioRecorder:
|
|||||||
self.stream.stop()
|
self.stream.stop()
|
||||||
|
|
||||||
self.buffer = np.zeros((int(self.duration * self.sample_rate), self.channels), dtype=np.float32)
|
self.buffer = np.zeros((int(self.duration * self.sample_rate), self.channels), dtype=np.float32)
|
||||||
|
print(f"AudioRecorder initialized with duration={self.duration}s, sample_rate={self.sample_rate}Hz, channels={self.channels}")
|
||||||
self.stream = sd.InputStream(
|
self.stream = sd.InputStream(
|
||||||
samplerate=self.sample_rate,
|
samplerate=self.sample_rate,
|
||||||
channels=self.channels,
|
|
||||||
callback=self.record_callback
|
callback=self.record_callback
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@ -2,6 +2,8 @@ import { MemoryRouter as Router, Routes, Route } from 'react-router-dom';
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import Dialog from '@mui/material/Dialog';
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
|
||||||
|
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||||
import DialogTitle from '@mui/material/DialogTitle';
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
import DialogContent from '@mui/material/DialogContent';
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
import DialogActions from '@mui/material/DialogActions';
|
import DialogActions from '@mui/material/DialogActions';
|
||||||
@ -10,6 +12,8 @@ import './App.css';
|
|||||||
import ClipList from './components/ClipList';
|
import ClipList from './components/ClipList';
|
||||||
import { useAppDispatch, useAppSelector } from './hooks';
|
import { useAppDispatch, useAppSelector } from './hooks';
|
||||||
import { store } from '../redux/main';
|
import { store } from '../redux/main';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import SettingsPage from './Settings';
|
||||||
|
|
||||||
function MainPage() {
|
function MainPage() {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@ -21,6 +25,7 @@ function MainPage() {
|
|||||||
);
|
);
|
||||||
const [newCollectionOpen, setNewCollectionOpen] = useState(false);
|
const [newCollectionOpen, setNewCollectionOpen] = useState(false);
|
||||||
const [newCollectionName, setNewCollectionName] = useState<string>('');
|
const [newCollectionName, setNewCollectionName] = useState<string>('');
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchMetadata = async () => {
|
const fetchMetadata = async () => {
|
||||||
@ -137,6 +142,22 @@ function MainPage() {
|
|||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
{/* Settings Button at Bottom Left */}
|
||||||
|
<div className="mt-auto mb-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="w-full rounded px-4 py-2 bg-neutral-800 text-offwhite hover:bg-plumDark text-left"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 16,
|
||||||
|
left: 8,
|
||||||
|
width: 'calc(100% - 16px)',
|
||||||
|
}}
|
||||||
|
onClick={() => navigate('/settings')}
|
||||||
|
>
|
||||||
|
⚙️ Settings
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<div
|
<div
|
||||||
@ -150,13 +171,39 @@ function MainPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
|
const theme = createTheme({
|
||||||
|
colorSchemes: {
|
||||||
|
light: false,
|
||||||
|
dark: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#6e44ba', // plum
|
||||||
|
dark: '#6e44ba', // plum
|
||||||
|
contrastText: '#ffffff',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#4f3186', // plumDark
|
||||||
|
dark: '#4f3186', // plumDark
|
||||||
|
contrastText: '#ffffff',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// colorSchemes: {
|
||||||
|
// light: false,
|
||||||
|
// dark: true,
|
||||||
|
// },
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Router>
|
<ThemeProvider theme={theme}>
|
||||||
<Routes>
|
<Router>
|
||||||
<Route path="/" element={<MainPage />} />
|
<Routes>
|
||||||
</Routes>
|
<Route path="/" element={<MainPage />} />
|
||||||
</Router>
|
<Route path="/settings" element={<SettingsPage />} />
|
||||||
|
</Routes>
|
||||||
|
</Router>
|
||||||
|
</ThemeProvider>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
196
electron-ui/src/renderer/Settings.tsx
Normal file
196
electron-ui/src/renderer/Settings.tsx
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import './App.css';
|
||||||
|
import TextField from '@mui/material/TextField';
|
||||||
|
import Select from '@mui/material/Select';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
|
||||||
|
type AudioDevice = {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Settings = {
|
||||||
|
httpPort: string;
|
||||||
|
inputDevice: AudioDevice;
|
||||||
|
outputDevice: AudioDevice;
|
||||||
|
recordingLength: number;
|
||||||
|
outputFolder: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultSettings: Settings = {
|
||||||
|
httpPort: '',
|
||||||
|
inputDevice: { id: '', label: '' },
|
||||||
|
outputDevice: { id: '', label: '' },
|
||||||
|
recordingLength: 0,
|
||||||
|
outputFolder: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchAudioDevices = async (): Promise<AudioDevice[]> => {
|
||||||
|
// Replace with actual backend call
|
||||||
|
// Example: return window.api.getAudioDevices();
|
||||||
|
return [
|
||||||
|
{ id: 'default-in', label: 'Default Input' },
|
||||||
|
{ id: 'mic-1', label: 'Microphone 1' },
|
||||||
|
{ id: 'default-out', label: 'Default Output' },
|
||||||
|
{ id: 'spk-1', label: 'Speakers 1' },
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendSettingsToBackend = (settings: Settings) => {
|
||||||
|
// Replace with actual backend call
|
||||||
|
// Example: window.api.updateSettings(settings);
|
||||||
|
console.log('Settings updated:', settings);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function SettingsPage() {
|
||||||
|
const [settings, setSettings] = useState<Settings>(defaultSettings);
|
||||||
|
const [inputDevices, setInputDevices] = useState<AudioDevice[]>([]);
|
||||||
|
const [outputDevices, setOutputDevices] = useState<AudioDevice[]>([]);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchAudioDevices()
|
||||||
|
.then((devices) => {
|
||||||
|
// For demo, split devices by id
|
||||||
|
setInputDevices(devices.filter((d) => d.id.includes('in')));
|
||||||
|
setOutputDevices(devices.filter((d) => d.id.includes('out')));
|
||||||
|
return devices;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Error fetching audio devices:', error);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
sendSettingsToBackend(settings);
|
||||||
|
}, [settings]);
|
||||||
|
|
||||||
|
const handleChange = () => {
|
||||||
|
// const { name, value } = e.target;
|
||||||
|
// setSettings((prev) => ({
|
||||||
|
// ...prev,
|
||||||
|
// [name]: value,
|
||||||
|
// }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFolderChange = async () => {
|
||||||
|
// Replace with actual folder picker
|
||||||
|
// Example: const folder = await window.api.selectFolder();
|
||||||
|
const folder = window.prompt(
|
||||||
|
'Enter output folder path:',
|
||||||
|
settings.outputFolder,
|
||||||
|
);
|
||||||
|
if (folder !== null) {
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
outputFolder: folder,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-w-screen min-h-screen bg-midnight text-offwhite flex items-center justify-center relative">
|
||||||
|
<div className="w-3/4 min-w-[600px] max-w-[800px] self-start flex flex-col font-sans bg-midnight text-offwhite p-6 rounded-lg relative">
|
||||||
|
{/* X Close Button */}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="absolute top-6 right-6 text-3xl font-bold text-offwhite bg-transparent hover:text-plumDark"
|
||||||
|
aria-label="Close settings"
|
||||||
|
onClick={() => navigate('/')}
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
<span className="text-2xl font-bold mb-4">Settings</span>
|
||||||
|
<div className="mb-4 flex justify-between">
|
||||||
|
<span>HTTP Port:</span>
|
||||||
|
<TextField
|
||||||
|
variant="standard"
|
||||||
|
type="text"
|
||||||
|
name="httpPort"
|
||||||
|
value={settings.httpPort}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSettings((prev) => ({ ...prev, httpPort: e.target.value }));
|
||||||
|
}}
|
||||||
|
className="ml-2 text-white w-[150px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-4 flex justify-between">
|
||||||
|
<span>Input Audio Device:</span>
|
||||||
|
<Select
|
||||||
|
variant="standard"
|
||||||
|
name="inputDevice"
|
||||||
|
value={settings.inputDevice}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSettings((prev) => ({ ...prev, inputDevice: e.target.value }));
|
||||||
|
}}
|
||||||
|
className="ml-2 w-64"
|
||||||
|
>
|
||||||
|
{inputDevices.map((dev) => (
|
||||||
|
<MenuItem key={dev.id} value={dev.id}>
|
||||||
|
{dev.label}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="mb-4 flex justify-between">
|
||||||
|
<span>Output Audio Device:</span>
|
||||||
|
<Select
|
||||||
|
variant="standard"
|
||||||
|
name="outputDevice"
|
||||||
|
value={settings.outputDevice}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
outputDevice: e.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
className="ml-2 w-64"
|
||||||
|
>
|
||||||
|
{outputDevices.map((dev) => (
|
||||||
|
<MenuItem key={dev.id} value={dev.id}>
|
||||||
|
{dev.label}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="mb-4 flex justify-between">
|
||||||
|
<span>Recording Length (seconds):</span>
|
||||||
|
<TextField
|
||||||
|
variant="standard"
|
||||||
|
type="text"
|
||||||
|
name="recordingLength"
|
||||||
|
value={settings.recordingLength}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
recordingLength: e.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
className="ml-2 w-[150px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-4 flex justify-between">
|
||||||
|
<span>Clip Output Folder:</span>
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<TextField
|
||||||
|
variant="standard"
|
||||||
|
type="text"
|
||||||
|
name="outputFolder"
|
||||||
|
value={settings.outputFolder}
|
||||||
|
className="ml-2 w-[300px]"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleFolderChange}
|
||||||
|
className="ml-2 px-3 py-1 rounded bg-plumDark text-offwhite hover:bg-plum"
|
||||||
|
>
|
||||||
|
...
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -9,6 +9,9 @@ import Dialog from '@mui/material/Dialog';
|
|||||||
import DialogTitle from '@mui/material/DialogTitle';
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
import DialogContent from '@mui/material/DialogContent';
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
import DialogActions from '@mui/material/DialogActions';
|
import DialogActions from '@mui/material/DialogActions';
|
||||||
|
import Slider from '@mui/material/Slider';
|
||||||
|
import ToggleButton from '@mui/material/ToggleButton';
|
||||||
|
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
|
||||||
import { useWavesurfer } from '@wavesurfer/react';
|
import { useWavesurfer } from '@wavesurfer/react';
|
||||||
import RegionsPlugin from 'wavesurfer.js/dist/plugins/regions.esm.js';
|
import RegionsPlugin from 'wavesurfer.js/dist/plugins/regions.esm.js';
|
||||||
import ZoomPlugin from 'wavesurfer.js/dist/plugins/zoom.esm.js';
|
import ZoomPlugin from 'wavesurfer.js/dist/plugins/zoom.esm.js';
|
||||||
@ -19,8 +22,10 @@ import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
|
|||||||
import DeleteIcon from '@mui/icons-material/Delete';
|
import DeleteIcon from '@mui/icons-material/Delete';
|
||||||
import { useSortable } from '@dnd-kit/sortable';
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
import { CSS } from '@dnd-kit/utilities';
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
import { ClipMetadata } from '../../redux/types';
|
import { ClipMetadata, PlaybackType } from '../../redux/types';
|
||||||
import { useAppSelector } from '../hooks';
|
import { useAppSelector } from '../hooks';
|
||||||
|
import PlayStopIcon from './playStopIcon';
|
||||||
|
import PlayOverlapIcon from './playOverlapIcon';
|
||||||
|
|
||||||
export interface AudioTrimmerProps {
|
export interface AudioTrimmerProps {
|
||||||
metadata: ClipMetadata;
|
metadata: ClipMetadata;
|
||||||
@ -43,6 +48,7 @@ export default function AudioTrimmer({
|
|||||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||||
const [nameInput, setNameInput] = useState<string>(metadata.name);
|
const [nameInput, setNameInput] = useState<string>(metadata.name);
|
||||||
|
const [volumeInput, setVolumeInput] = useState<number>(metadata.volume ?? 1);
|
||||||
const collectionNames = useAppSelector((state) =>
|
const collectionNames = useAppSelector((state) =>
|
||||||
state.collections.map((col) => col.name),
|
state.collections.map((col) => col.name),
|
||||||
);
|
);
|
||||||
@ -223,6 +229,7 @@ export default function AudioTrimmer({
|
|||||||
} else {
|
} else {
|
||||||
const allRegions = (plugins[0] as RegionsPlugin).getRegions();
|
const allRegions = (plugins[0] as RegionsPlugin).getRegions();
|
||||||
if (allRegions.length > 0) {
|
if (allRegions.length > 0) {
|
||||||
|
wavesurfer.setVolume(metadata.volume ?? 1);
|
||||||
wavesurfer.play(allRegions[0].start, allRegions[0].end);
|
wavesurfer.play(allRegions[0].start, allRegions[0].end);
|
||||||
} else {
|
} else {
|
||||||
wavesurfer.play();
|
wavesurfer.play();
|
||||||
@ -401,12 +408,74 @@ export default function AudioTrimmer({
|
|||||||
<div className="m-1 wavesurfer-scroll-container">
|
<div className="m-1 wavesurfer-scroll-container">
|
||||||
<div ref={containerRef} className="wavesurfer-inner" />
|
<div ref={containerRef} className="wavesurfer-inner" />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid justify-items-stretch grid-cols-2 text-neutral-500">
|
<div className="flex justify-between mt-2">
|
||||||
<div className="m-1 flex justify-start">
|
<span className="w-1/5 flex-none text-sm text-neutral-500 self-center">
|
||||||
<text className="text-sm ">
|
Clip: {formatTime(metadata.startTime ?? 0)} -{' '}
|
||||||
Clip: {formatTime(metadata.startTime ?? 0)} -{' '}
|
{formatTime(metadata.endTime ?? 0)}
|
||||||
{formatTime(metadata.endTime ?? 0)}
|
</span>
|
||||||
</text>
|
<div className="w-3/5 flex-1 flex justify-center items-center">
|
||||||
|
<Slider
|
||||||
|
value={volumeInput}
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
onChange={(e, newValue) => setVolumeInput(newValue as number)}
|
||||||
|
onChangeCommitted={(e, newValue) => {
|
||||||
|
const newVolume = newValue as number;
|
||||||
|
console.log('Volume change:', newVolume);
|
||||||
|
if (onSave) onSave({ ...metadata, volume: newVolume });
|
||||||
|
}}
|
||||||
|
color="secondary"
|
||||||
|
className="p-0 m-0"
|
||||||
|
/>
|
||||||
|
{/* <input
|
||||||
|
type="range"
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
value={volumeInput}
|
||||||
|
onChange={(e) => {
|
||||||
|
const newVolume = parseFloat(e.target.value);
|
||||||
|
setVolumeInput(newVolume);
|
||||||
|
}}
|
||||||
|
onDragEnd={(e) => {
|
||||||
|
console.log('Volume change:');
|
||||||
|
// const newVolume = parseFloat(e.target.value);
|
||||||
|
// if (onSave) onSave({ ...metadata, volume: newVolume });
|
||||||
|
}}
|
||||||
|
className="mx-2 w-full accent-plum"
|
||||||
|
aria-label="Volume slider"
|
||||||
|
/> */}
|
||||||
|
</div>
|
||||||
|
<div className="w-1/5 flex justify-end text-sm text-neutral-500">
|
||||||
|
<ToggleButtonGroup value={metadata.playbackType}>
|
||||||
|
<ToggleButton
|
||||||
|
value="playStop"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
if (onSave)
|
||||||
|
onSave({
|
||||||
|
...metadata,
|
||||||
|
playbackType: PlaybackType.PlayStop,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlayStopIcon />
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton
|
||||||
|
value="playOverlap"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
if (onSave)
|
||||||
|
onSave({
|
||||||
|
...metadata,
|
||||||
|
playbackType: PlaybackType.PlayOverlap,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlayOverlapIcon />
|
||||||
|
</ToggleButton>
|
||||||
|
</ToggleButtonGroup>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -31,6 +31,33 @@ export default function ClipList({ collection }: ClipListProps) {
|
|||||||
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
|
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
console.log('Files dropped:', event.dataTransfer.files);
|
||||||
|
const files = Array.from(event.dataTransfer.files).filter((file) =>
|
||||||
|
file.type.startsWith('audio/'),
|
||||||
|
);
|
||||||
|
if (files.length > 0) {
|
||||||
|
const formData = new FormData();
|
||||||
|
files.forEach((file) => formData.append('files', file));
|
||||||
|
|
||||||
|
// todo send the file to the backend and add to the collection
|
||||||
|
|
||||||
|
// fetch('http://localhost:5010/file/upload', {
|
||||||
|
// method: 'POST',
|
||||||
|
// body: formData,
|
||||||
|
// })
|
||||||
|
// .then((res) => res.json())
|
||||||
|
// .catch((err) => console.error('Error uploading files:', err));
|
||||||
|
// Implement your onDrop logic here
|
||||||
|
// onDrop(files, selectedCollection);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
async function handleDragEnd(event: any) {
|
async function handleDragEnd(event: any) {
|
||||||
const { active, over } = event;
|
const { active, over } = event;
|
||||||
if (active.id !== over?.id) {
|
if (active.id !== over?.id) {
|
||||||
@ -145,7 +172,11 @@ export default function ClipList({ collection }: ClipListProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-full flex flex-col justify-start bg-midnight text-offwhite">
|
<div
|
||||||
|
className="min-h-full flex flex-col justify-start bg-midnight text-offwhite"
|
||||||
|
onDrop={handleDrop}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
>
|
||||||
<DndContext
|
<DndContext
|
||||||
sensors={sensors}
|
sensors={sensors}
|
||||||
collisionDetection={closestCenter}
|
collisionDetection={closestCenter}
|
||||||
|
|||||||
29
electron-ui/src/renderer/components/playOverlapIcon.tsx
Normal file
29
electron-ui/src/renderer/components/playOverlapIcon.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function PlayOverlapIcon({
|
||||||
|
size = 24,
|
||||||
|
color = 'currentColor',
|
||||||
|
}: {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 32 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
{/* Filled play arrow */}
|
||||||
|
<polygon points="4,4 4,20 16,12" fill={color} />
|
||||||
|
{/* Outlined play arrow (underneath and to the right) */}
|
||||||
|
<polygon
|
||||||
|
points="12,4 12,20 24,12"
|
||||||
|
fill="none"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth={1}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
electron-ui/src/renderer/components/playStopIcon.tsx
Normal file
23
electron-ui/src/renderer/components/playStopIcon.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export default function PlayStopIcon({
|
||||||
|
size = 24,
|
||||||
|
color = 'currentColor',
|
||||||
|
}: {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 48 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
aria-label="Play/Stop Icon"
|
||||||
|
>
|
||||||
|
{/* Play Arrow */}
|
||||||
|
<polygon points="4,4 20,12 4,20" fill={color} />
|
||||||
|
{/* Stop Square */}
|
||||||
|
<rect x="28" y="4" width="16" height="16" rx="2" fill={color} />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
3
stream_deck_plugin/.gitignore
vendored
3
stream_deck_plugin/.gitignore
vendored
@ -4,4 +4,5 @@ packages/
|
|||||||
ClipTrimDotNet/bin/
|
ClipTrimDotNet/bin/
|
||||||
ClipTrimDotNet/obj/
|
ClipTrimDotNet/obj/
|
||||||
ClipTrimDotNet/dist/
|
ClipTrimDotNet/dist/
|
||||||
ClipTrimDotNet/node_modules/
|
ClipTrimDotNet/node_modules/
|
||||||
|
.vs/
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -1,96 +0,0 @@
|
|||||||
{
|
|
||||||
"Version": 1,
|
|
||||||
"WorkspaceRootPath": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\",
|
|
||||||
"Documents": [
|
|
||||||
{
|
|
||||||
"AbsoluteMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|c:\\users\\mickl\\desktop\\cliptrim-ui\\cliptrimapp\\stream_deck_plugin\\cliptrimdotnet\\client\\cliptrimclient.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|solutionrelative:cliptrimdotnet\\client\\cliptrimclient.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"AbsoluteMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|c:\\users\\mickl\\desktop\\cliptrim-ui\\cliptrimapp\\stream_deck_plugin\\cliptrimdotnet\\player.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|solutionrelative:cliptrimdotnet\\player.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"AbsoluteMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|c:\\users\\mickl\\desktop\\cliptrim-ui\\cliptrimapp\\stream_deck_plugin\\cliptrimdotnet\\profileswitcher.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|solutionrelative:cliptrimdotnet\\profileswitcher.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"AbsoluteMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|c:\\users\\mickl\\desktop\\cliptrim-ui\\cliptrimapp\\stream_deck_plugin\\cliptrimdotnet\\client\\collectionmetadata.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|solutionrelative:cliptrimdotnet\\client\\collectionmetadata.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"DocumentGroupContainers": [
|
|
||||||
{
|
|
||||||
"Orientation": 0,
|
|
||||||
"VerticalTabListWidth": 256,
|
|
||||||
"DocumentGroups": [
|
|
||||||
{
|
|
||||||
"DockedWidth": 297,
|
|
||||||
"SelectedChildIndex": 2,
|
|
||||||
"Children": [
|
|
||||||
{
|
|
||||||
"$type": "Bookmark",
|
|
||||||
"Name": "ST:0:0:{57d563b6-44a5-47df-85be-f4199ad6b651}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 2,
|
|
||||||
"Title": "ProfileSwitcher.cs",
|
|
||||||
"DocumentMoniker": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\ProfileSwitcher.cs",
|
|
||||||
"RelativeDocumentMoniker": "ClipTrimDotNet\\ProfileSwitcher.cs",
|
|
||||||
"ToolTip": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\ProfileSwitcher.cs",
|
|
||||||
"RelativeToolTip": "ClipTrimDotNet\\ProfileSwitcher.cs",
|
|
||||||
"ViewState": "AgIAAFkAAAAAAAAAAAAlwG8AAABKAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-02-21T15:06:24.045Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 0,
|
|
||||||
"Title": "ClipTrimClient.cs",
|
|
||||||
"DocumentMoniker": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\Client\\ClipTrimClient.cs",
|
|
||||||
"RelativeDocumentMoniker": "ClipTrimDotNet\\Client\\ClipTrimClient.cs",
|
|
||||||
"ToolTip": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\Client\\ClipTrimClient.cs",
|
|
||||||
"RelativeToolTip": "ClipTrimDotNet\\Client\\ClipTrimClient.cs",
|
|
||||||
"ViewState": "AgIAAEgAAAAAAAAAAAAuwGMAAAAJAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-02-21T15:03:49.814Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 3,
|
|
||||||
"Title": "CollectionMetaData.cs",
|
|
||||||
"DocumentMoniker": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\Client\\CollectionMetaData.cs",
|
|
||||||
"RelativeDocumentMoniker": "ClipTrimDotNet\\Client\\CollectionMetaData.cs",
|
|
||||||
"ToolTip": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\Client\\CollectionMetaData.cs",
|
|
||||||
"RelativeToolTip": "ClipTrimDotNet\\Client\\CollectionMetaData.cs",
|
|
||||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-02-21T15:03:47.862Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 1,
|
|
||||||
"Title": "Player.cs",
|
|
||||||
"DocumentMoniker": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\Player.cs",
|
|
||||||
"RelativeDocumentMoniker": "ClipTrimDotNet\\Player.cs",
|
|
||||||
"ToolTip": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\Player.cs*",
|
|
||||||
"RelativeToolTip": "ClipTrimDotNet\\Player.cs*",
|
|
||||||
"ViewState": "AgIAAHIAAAAAAAAAAAA3wIYAAABMAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-02-21T15:00:23.762Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Bookmark",
|
|
||||||
"Name": "ST:0:0:{cce594b6-0c39-4442-ba28-10c64ac7e89f}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,113 +0,0 @@
|
|||||||
{
|
|
||||||
"Version": 1,
|
|
||||||
"WorkspaceRootPath": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\",
|
|
||||||
"Documents": [
|
|
||||||
{
|
|
||||||
"AbsoluteMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|c:\\users\\mickl\\desktop\\cliptrim-ui\\cliptrimapp\\stream_deck_plugin\\cliptrimdotnet\\wavplayer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|solutionrelative:cliptrimdotnet\\wavplayer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"AbsoluteMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|c:\\users\\mickl\\desktop\\cliptrim-ui\\cliptrimapp\\stream_deck_plugin\\cliptrimdotnet\\player.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|solutionrelative:cliptrimdotnet\\player.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"AbsoluteMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|c:\\users\\mickl\\desktop\\cliptrim-ui\\cliptrimapp\\stream_deck_plugin\\cliptrimdotnet\\profileswitcher.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|solutionrelative:cliptrimdotnet\\profileswitcher.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"AbsoluteMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|c:\\users\\mickl\\desktop\\cliptrim-ui\\cliptrimapp\\stream_deck_plugin\\cliptrimdotnet\\client\\cliptrimclient.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|solutionrelative:cliptrimdotnet\\client\\cliptrimclient.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"AbsoluteMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|c:\\users\\mickl\\desktop\\cliptrim-ui\\cliptrimapp\\stream_deck_plugin\\cliptrimdotnet\\client\\collectionmetadata.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{4635D874-69C0-4010-BE46-77EF92EB1553}|ClipTrimDotNet\\ClipTrimDotNet.csproj|solutionrelative:cliptrimdotnet\\client\\collectionmetadata.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"DocumentGroupContainers": [
|
|
||||||
{
|
|
||||||
"Orientation": 0,
|
|
||||||
"VerticalTabListWidth": 256,
|
|
||||||
"DocumentGroups": [
|
|
||||||
{
|
|
||||||
"DockedWidth": 297,
|
|
||||||
"SelectedChildIndex": 1,
|
|
||||||
"Children": [
|
|
||||||
{
|
|
||||||
"$type": "Bookmark",
|
|
||||||
"Name": "ST:0:0:{57d563b6-44a5-47df-85be-f4199ad6b651}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 0,
|
|
||||||
"Title": "WavPlayer.cs",
|
|
||||||
"DocumentMoniker": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\WavPlayer.cs",
|
|
||||||
"RelativeDocumentMoniker": "ClipTrimDotNet\\WavPlayer.cs",
|
|
||||||
"ToolTip": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\WavPlayer.cs",
|
|
||||||
"RelativeToolTip": "ClipTrimDotNet\\WavPlayer.cs",
|
|
||||||
"ViewState": "AgIAALYAAAAAAAAAAAAAALsAAAANAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-02-21T15:16:26.477Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 2,
|
|
||||||
"Title": "ProfileSwitcher.cs",
|
|
||||||
"DocumentMoniker": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\ProfileSwitcher.cs",
|
|
||||||
"RelativeDocumentMoniker": "ClipTrimDotNet\\ProfileSwitcher.cs",
|
|
||||||
"ToolTip": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\ProfileSwitcher.cs",
|
|
||||||
"RelativeToolTip": "ClipTrimDotNet\\ProfileSwitcher.cs",
|
|
||||||
"ViewState": "AgIAAG8AAAAAAAAAAAAWwG8AAABKAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-02-21T15:06:24.045Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 3,
|
|
||||||
"Title": "ClipTrimClient.cs",
|
|
||||||
"DocumentMoniker": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\Client\\ClipTrimClient.cs",
|
|
||||||
"RelativeDocumentMoniker": "ClipTrimDotNet\\Client\\ClipTrimClient.cs",
|
|
||||||
"ToolTip": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\Client\\ClipTrimClient.cs",
|
|
||||||
"RelativeToolTip": "ClipTrimDotNet\\Client\\ClipTrimClient.cs",
|
|
||||||
"ViewState": "AgIAAEgAAAAAAAAAAAAuwGIAAAApAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-02-21T15:03:49.814Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 4,
|
|
||||||
"Title": "CollectionMetaData.cs",
|
|
||||||
"DocumentMoniker": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\Client\\CollectionMetaData.cs",
|
|
||||||
"RelativeDocumentMoniker": "ClipTrimDotNet\\Client\\CollectionMetaData.cs",
|
|
||||||
"ToolTip": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\Client\\CollectionMetaData.cs",
|
|
||||||
"RelativeToolTip": "ClipTrimDotNet\\Client\\CollectionMetaData.cs",
|
|
||||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-02-21T15:03:47.862Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 1,
|
|
||||||
"Title": "Player.cs",
|
|
||||||
"DocumentMoniker": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\Player.cs",
|
|
||||||
"RelativeDocumentMoniker": "ClipTrimDotNet\\Player.cs",
|
|
||||||
"ToolTip": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\stream_deck_plugin\\ClipTrimDotNet\\Player.cs",
|
|
||||||
"RelativeToolTip": "ClipTrimDotNet\\Player.cs",
|
|
||||||
"ViewState": "AgIAAHoAAAAAAAAAAAAswIwAAAAbAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-02-21T15:00:23.762Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Bookmark",
|
|
||||||
"Name": "ST:0:0:{cce594b6-0c39-4442-ba28-10c64ac7e89f}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user