From a4cd6abb3a20d165933a01b00be9ee1c089720c6 Mon Sep 17 00:00:00 2001 From: Gagancreates Date: Fri, 8 May 2026 02:11:06 +0530 Subject: [PATCH] feat: render audio files with native player --- apps/x/apps/renderer/src/App.tsx | 7 ++- .../src/components/audio-file-viewer.tsx | 60 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 apps/x/apps/renderer/src/components/audio-file-viewer.tsx diff --git a/apps/x/apps/renderer/src/App.tsx b/apps/x/apps/renderer/src/App.tsx index 8984fcb7..915e3516 100644 --- a/apps/x/apps/renderer/src/App.tsx +++ b/apps/x/apps/renderer/src/App.tsx @@ -17,6 +17,7 @@ import { HtmlFileViewer } from '@/components/html-file-viewer'; import { ImageFileViewer } from '@/components/image-file-viewer'; import { VideoFileViewer } from '@/components/video-file-viewer'; import { PdfFileViewer } from '@/components/pdf-file-viewer'; +import { AudioFileViewer } from '@/components/audio-file-viewer'; import { useDebounce } from './hooks/use-debounce'; import { SidebarContentPanel } from '@/components/sidebar-content'; import { SuggestedTopicsView } from '@/components/suggested-topics-view'; @@ -1431,7 +1432,7 @@ function App() { // Media viewers (HTML, image, video, PDF) self-load via app:// protocol. // Skip the generic UTF-8 loader so we don't trash fileContent with binary // bytes or double-fetch large files. - if (/\.(html?|png|jpe?g|webp|gif|svg|avif|bmp|ico|mp4|mov|webm|m4v|pdf)$/i.test(pathToLoad)) { + if (/\.(html?|png|jpe?g|webp|gif|svg|avif|bmp|ico|mp4|mov|webm|m4v|pdf|mp3|wav|m4a|ogg|flac|aac)$/i.test(pathToLoad)) { setFileContent('') return } @@ -4846,6 +4847,10 @@ function App() {
+ ) : selectedPath && /\.(mp3|wav|m4a|ogg|flac|aac)$/i.test(selectedPath) ? ( +
+ +
) : (
diff --git a/apps/x/apps/renderer/src/components/audio-file-viewer.tsx b/apps/x/apps/renderer/src/components/audio-file-viewer.tsx
new file mode 100644
index 00000000..54d1035d
--- /dev/null
+++ b/apps/x/apps/renderer/src/components/audio-file-viewer.tsx
@@ -0,0 +1,60 @@
+import { useEffect, useState } from 'react'
+import { ExternalLinkIcon, FileAudioIcon } from 'lucide-react'
+
+interface AudioFileViewerProps {
+  path: string
+}
+
+type State = 'loading' | 'ready' | 'error'
+
+function basename(path: string): string {
+  const idx = path.lastIndexOf('/')
+  return idx >= 0 ? path.slice(idx + 1) : path
+}
+
+export function AudioFileViewer({ path }: AudioFileViewerProps) {
+  const [state, setState] = useState('loading')
+
+  useEffect(() => {
+    setState('loading')
+  }, [path])
+
+  const src = `app://workspace/${path.split('/').map(encodeURIComponent).join('/')}`
+
+  if (state === 'error') {
+    return (
+      
+ +

Cannot play this audio file

+

The codec or container format isn't supported.

+ +
+ ) + } + + return ( +
+ +

+ {basename(path)} +

+
+ ) +}