diff --git a/src/main/index.js b/src/main/index.js index 4c88d62..e852007 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -72,6 +72,10 @@ function createWindow() { if (action === 'maximize') win.isMaximized() ? win.unmaximize() : win.maximize() if (action === 'close') win.close() }) + + ipcMain.on('open-explorer', (event, path) => { + shell.showItemInFolder(path) + }) } app.whenReady().then(() => { diff --git a/src/preload/index.js b/src/preload/index.js index 5733b3c..87e2734 100644 --- a/src/preload/index.js +++ b/src/preload/index.js @@ -6,7 +6,8 @@ const api = { startTranscription: (filePath, options) => ipcRenderer.invoke('start-transcription', { filePath, options }), onTranscriptionProgress: (callback) => ipcRenderer.on('transcription-progress', (_, progress) => callback(progress)), onTranscriptionData: (callback) => ipcRenderer.on('transcription-data', (_, data) => callback(data)), - detectHardware: () => ipcRenderer.invoke('detect-hardware') + detectHardware: () => ipcRenderer.invoke('detect-hardware'), + openExplorer: (path) => ipcRenderer.send('open-explorer', path) } if (process.contextIsolated) { diff --git a/src/renderer/src/App.jsx b/src/renderer/src/App.jsx index 4fc3add..efdc817 100644 --- a/src/renderer/src/App.jsx +++ b/src/renderer/src/App.jsx @@ -17,6 +17,7 @@ import { motion, AnimatePresence } from 'framer-motion' import logo from './assets/logo.png' import CustomSelect from './components/CustomSelect' +import ResultModal from './components/ResultModal' const languages = [ { id: 'auto', name: 'AUTO (Recommended)' }, @@ -49,6 +50,7 @@ function App() { model: 'small', format: 'srt' }) + const [showResult, setShowResult] = useState(null) useEffect(() => { if (!window.api) { @@ -109,7 +111,7 @@ function App() { try { const result = await window.api.startTranscription(file.path, config) if (result.success) { - alert('Transcription Complete! Output: ' + result.outputPath) + setShowResult(result.outputPath) } } catch (error) { alert('Error: ' + error.message) @@ -310,6 +312,13 @@ function App() { + + {/* Custom Result Modal */} + setShowResult(null)} + /> ) } diff --git a/src/renderer/src/components/ResultModal.jsx b/src/renderer/src/components/ResultModal.jsx new file mode 100644 index 0000000..e1d8230 --- /dev/null +++ b/src/renderer/src/components/ResultModal.jsx @@ -0,0 +1,55 @@ +import React from 'react' +import { motion, AnimatePresence } from 'framer-motion' +import { CheckCircle2, FolderOpen, X } from 'lucide-react' + +export default function ResultModal({ isOpen, path, onClose }) { + const handleOpenFolder = () => { + if (window.api && window.api.openExplorer) { + window.api.openExplorer(path) + } + } + + return ( + + {isOpen && ( +
+ + + +
+
+
+ +
+ +

Transcription Complete!

+

Your subtitle file is ready for use.

+ +
+
OUTPUT PATH:
+
{path}
+
+ +
+ + +
+
+
+
+ )} +
+ ) +} diff --git a/src/renderer/src/index.css b/src/renderer/src/index.css index 87a8f8a..14fd48c 100644 --- a/src/renderer/src/index.css +++ b/src/renderer/src/index.css @@ -465,3 +465,136 @@ body { transform: translateY(-5px); transition: all 0.3s; } + +/* Result Modal Styles */ +.modal-overlay { + position: absolute; + inset: 0; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(4px); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + padding: 40px; +} + +.result-modal { + width: 100%; + max-width: 500px; + padding: 40px; + position: relative; + text-align: center; + border-color: rgba(0, 242, 255, 0.2); +} + +.modal-close { + position: absolute; + top: 15px; + right: 15px; + background: none; + border: none; + color: var(--text-muted); + cursor: pointer; + transition: color 0.2s; +} + +.modal-close:hover { + color: white; +} + +.success-icon-container { + margin-bottom: 25px; + position: relative; + display: inline-flex; +} + +.success-glow { + position: absolute; + inset: 0; + background: var(--primary-cyan); + filter: blur(30px); + opacity: 0.2; + border-radius: 50%; +} + +.result-modal h2 { + font-size: 24px; + margin-bottom: 8px; + background: linear-gradient(90deg, #fff, var(--primary-cyan)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.modal-subtitle { + color: var(--text-muted); + margin-bottom: 30px; +} + +.path-container { + background: rgba(0, 0, 0, 0.3); + padding: 15px; + border-radius: 10px; + text-align: left; + margin-bottom: 30px; + border: 1px solid rgba(255, 255, 255, 0.05); +} + +.path-label { + font-size: 10px; + color: var(--primary-cyan); + font-weight: 700; + margin-bottom: 5px; + letter-spacing: 1px; +} + +.path-text { + font-size: 13px; + color: #ddd; + word-break: break-all; + font-family: monospace; +} + +.modal-actions { + display: flex; + gap: 15px; +} + +.btn-primary-small { + flex: 1; + padding: 12px; + border-radius: 10px; + border: none; + background: linear-gradient(135deg, var(--primary-cyan), var(--primary-magenta)); + color: #0b0c14; + font-weight: 700; + font-size: 13px; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + cursor: pointer; + transition: all 0.3s; +} + +.btn-primary-small:hover { + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(0, 242, 255, 0.3); +} + +.btn-secondary { + padding: 12px 25px; + border-radius: 10px; + border: 1px solid rgba(255, 255, 255, 0.1); + background: rgba(255, 255, 255, 0.05); + color: white; + font-weight: 600; + font-size: 13px; + cursor: pointer; + transition: all 0.3s; +} + +.btn-secondary:hover { + background: rgba(255, 255, 255, 0.1); + border-color: rgba(255, 255, 255, 0.2); +}