11import { createEffect , createSignal , type ComponentProps } from "solid-js" ;
22import { cx } from "cva" ;
3-
4- import { commands , events } from "~/utils/tauri" ;
3+ import { type as ostype } from "@tauri-apps/plugin-os" ;
54import { createTimer } from "@solid-primitives/timer" ;
65import { createMutation } from "@tanstack/solid-query" ;
6+ import { createStore , produce } from "solid-js/store" ;
7+ import { getCurrentWindow } from "@tauri-apps/api/window" ;
8+
9+ import { commands , events } from "~/utils/tauri" ;
710import {
811 createOptionsQuery ,
912 createCurrentRecordingQuery ,
1013} from "~/utils/queries" ;
11- import { createStore , produce } from "solid-js/store" ;
1214
13- const audioLevelStore = {
14- level : 0 ,
15- initialized : false ,
16- init ( ) {
17- if ( this . initialized ) return ;
18-
19- events . audioInputLevelChange . listen ( ( dbs ) => {
20- const DB_MIN = - 60 ;
21- const DB_MAX = 0 ;
22-
23- const dbValue = dbs . payload ?? DB_MIN ;
24- const normalizedLevel = Math . max (
25- 0 ,
26- Math . min ( 1 , ( dbValue - DB_MIN ) / ( DB_MAX - DB_MIN ) )
27- ) ;
28- this . level = normalizedLevel ;
29-
30- window . dispatchEvent (
31- new CustomEvent ( "audioLevelChange" , { detail : normalizedLevel } )
32- ) ;
33- } ) ;
34-
35- this . initialized = true ;
36- } ,
37- cleanup ( ) {
38- this . initialized = false ;
39- this . level = 0 ;
40- } ,
41- } ;
15+ type State = "recording" | "paused" | "stopped" ;
4216
4317export default function ( ) {
4418 const start = Date . now ( ) ;
4519 const [ time , setTime ] = createSignal ( Date . now ( ) ) ;
46- const [ isPaused , setIsPaused ] = createSignal ( false ) ;
47- const [ stopped , setStopped ] = createSignal ( false ) ;
48- const [ audioLevel , setAudioLevel ] = createSignal < number > ( 0 ) ;
20+ const [ state , setState ] = createSignal < State > ( "recording" ) ;
4921 const currentRecording = createCurrentRecordingQuery ( ) ;
5022 const { options } = createOptionsQuery ( ) ;
5123
24+ const audioLevel = createAudioInputLevel ( ) ;
25+
5226 const [ pauseResumes , setPauseResumes ] = createStore <
5327 | [ ]
5428 | [
@@ -57,66 +31,42 @@ export default function () {
5731 ]
5832 > ( [ ] ) ;
5933
60- const isAudioEnabled = ( ) => {
61- return options . data ?. micName != null ;
62- } ;
63-
6434 createTimer (
6535 ( ) => {
66- if ( stopped ( ) || isPaused ( ) ) return ;
36+ if ( state ( ) !== "recording" ) return ;
6737 setTime ( Date . now ( ) ) ;
6838 } ,
6939 100 ,
7040 setInterval
7141 ) ;
7242
7343 createEffect ( ( ) => {
74- setTime ( Date . now ( ) ) ;
75- } ) ;
76-
77- // Single effect to handle audio initialization and cleanup
78- createEffect ( ( ) => {
79- if ( ! isAudioEnabled ( ) ) {
80- audioLevelStore . cleanup ( ) ;
81- setAudioLevel ( 0 ) ;
82- return ;
83- }
84-
85- audioLevelStore . init ( ) ;
86- setAudioLevel ( audioLevelStore . level ) ;
87-
88- const handler = ( e : CustomEvent ) => {
89- setAudioLevel ( e . detail ) ;
90- } ;
91-
92- window . addEventListener ( "audioLevelChange" , handler as EventListener ) ;
93- return ( ) => {
94- window . removeEventListener ( "audioLevelChange" , handler as EventListener ) ;
95- } ;
44+ if ( ! currentRecording . isPending && currentRecording . data === undefined )
45+ getCurrentWindow ( ) . close ( ) ;
9646 } ) ;
9747
9848 const stopRecording = createMutation ( ( ) => ( {
9949 mutationFn : async ( ) => {
100- setStopped ( true ) ;
50+ setState ( "stopped" ) ;
10151 await commands . stopRecording ( ) ;
10252 } ,
10353 } ) ) ;
10454
10555 const togglePause = createMutation ( ( ) => ( {
10656 mutationFn : async ( ) => {
107- if ( isPaused ( ) ) {
57+ if ( state ( ) === "paused" ) {
10858 await commands . resumeRecording ( ) ;
10959 setPauseResumes (
11060 produce ( ( a ) => {
11161 if ( a . length === 0 ) return a ;
11262 a [ a . length - 1 ] . resume = Date . now ( ) ;
11363 } )
11464 ) ;
115- setIsPaused ( false ) ;
65+ setState ( "recording" ) ;
11666 } else {
11767 await commands . pauseRecording ( ) ;
11868 setPauseResumes ( ( a ) => [ ...a , { pause : Date . now ( ) } ] ) ;
119- setIsPaused ( true ) ;
69+ setState ( "paused" ) ;
12070 }
12171 setTime ( Date . now ( ) ) ;
12272 } ,
@@ -125,8 +75,7 @@ export default function () {
12575 const restartRecording = createMutation ( ( ) => ( {
12676 mutationFn : async ( ) => {
12777 await events . requestRestartRecording . emit ( ) ;
128- setStopped ( false ) ;
129- setIsPaused ( false ) ;
78+ setState ( "recording" ) ;
13079 setTime ( Date . now ( ) ) ;
13180 } ,
13281 } ) ) ;
@@ -149,14 +98,14 @@ export default function () {
14998 onClick = { ( ) => stopRecording . mutate ( ) }
15099 >
151100 < IconCapStopCircle />
152- < span class = "font-[500] text-[0.875rem]" >
101+ < span class = "font-[500] text-[0.875rem] tabular-nums " >
153102 { formatTime ( adjustedTime ( ) / 1000 ) }
154103 </ span >
155104 </ button >
156105
157106 < div class = "flex items-center gap-1" >
158107 < div class = "relative h-8 w-8 flex items-center justify-center" >
159- { isAudioEnabled ( ) ? (
108+ { options . data ?. micName != null ? (
160109 < >
161110 < IconCapMicrophone class = "size-5 text-gray-400" />
162111 < div class = "absolute bottom-1 left-1 right-1 h-0.5 bg-gray-400 overflow-hidden rounded-full" >
@@ -176,12 +125,19 @@ export default function () {
176125 ) }
177126 </ div >
178127
179- < ActionButton
180- disabled = { togglePause . isPending }
181- onClick = { ( ) => togglePause . mutate ( ) }
182- >
183- { isPaused ( ) ? < IconCapPlayCircle /> : < IconCapPauseCircle /> }
184- </ ActionButton >
128+ { ( currentRecording . data ?. type === "studio" ||
129+ ostype ( ) === "macos" ) && (
130+ < ActionButton
131+ disabled = { togglePause . isPending }
132+ onClick = { ( ) => togglePause . mutate ( ) }
133+ >
134+ { state ( ) === "paused" ? (
135+ < IconCapPlayCircle />
136+ ) : (
137+ < IconCapPauseCircle />
138+ ) }
139+ </ ActionButton >
140+ ) }
185141
186142 < ActionButton
187143 disabled = { restartRecording . isPending }
@@ -218,7 +174,25 @@ function ActionButton(props: ComponentProps<"button">) {
218174
219175function formatTime ( secs : number ) {
220176 const minutes = Math . floor ( secs / 60 ) ;
221- const seconds = Math . round ( secs % 60 ) ;
177+ const seconds = Math . floor ( secs % 60 ) ;
222178
223179 return `${ minutes } :${ seconds . toString ( ) . padStart ( 2 , "0" ) } ` ;
224180}
181+
182+ function createAudioInputLevel ( ) {
183+ const [ level , setLevel ] = createSignal ( 0 ) ;
184+
185+ events . audioInputLevelChange . listen ( ( dbs ) => {
186+ const DB_MIN = - 60 ;
187+ const DB_MAX = 0 ;
188+
189+ const dbValue = dbs . payload ?? DB_MIN ;
190+ const normalizedLevel = Math . max (
191+ 0 ,
192+ Math . min ( 1 , ( dbValue - DB_MIN ) / ( DB_MAX - DB_MIN ) )
193+ ) ;
194+ setLevel ( normalizedLevel ) ;
195+ } ) ;
196+
197+ return level ;
198+ }
0 commit comments