diff --git a/app/components/Button.tsx b/app/components/Button.tsx index 75bd8c00aa..0aa6b15580 100644 --- a/app/components/Button.tsx +++ b/app/components/Button.tsx @@ -16,12 +16,8 @@ export const Button = ({ disabled, ...rest }: ButtonPropsI) => { - const variant = disabled ? "disabled" : propsVariant; function getVariantClassNames(variant: string) { switch (variant) { - case "disabled": { - return "bg-brand-secondary-500 text-brand-secondary-300"; - } case "secondary": { return " bg-brand-secondary hover:bg-brand-secondary-900 text-white focus-visible:outline-brand-secondary"; } @@ -30,20 +26,25 @@ export const Button = ({ } case "primary": default: { - return " bg-brand-primary hover:bg-brand-primary-400 text-white focus-visible:outline-brand-primary-600"; + return "bg-brand-primary hover:bg-brand-primary-400 text-white focus-visible:outline-brand-primary-600"; } } } + const disabledClass = twMerge( + disabled ? "opacity-40 pointer-events-none" : "", + ); + const className = twMerge( "text-sm font-medium rounded-lg px-3 py-2 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 transition-all duration-150 flex gap-2 items-center justify-center", - getVariantClassNames(variant), + getVariantClassNames(propsVariant), propsClassName, + disabledClass, ); return ( ); diff --git a/app/components/WaveformPlayer.tsx b/app/components/WaveformPlayer.tsx index 1603b8277f..b4b40d7afe 100644 --- a/app/components/WaveformPlayer.tsx +++ b/app/components/WaveformPlayer.tsx @@ -1,54 +1,55 @@ -import { useCallback, useEffect, useState, useRef } from 'react'; -import WaveSurfer from 'wavesurfer.js'; -import { faCirclePlay, faCirclePause, } from "@fortawesome/pro-solid-svg-icons" -import { ButtonIcon } from '~/components'; +import { useCallback, useEffect, useState, useRef } from "react"; +import WaveSurfer from "wavesurfer.js"; +import { faCirclePlay, faCirclePause } from "@fortawesome/pro-solid-svg-icons"; +import { ButtonIcon } from "~/components"; -export const WaveformPlayer = ({ - audio -}:{ - audio: string -}) => { +export const WaveformPlayer = ({ audio }: { audio: string }) => { const waveSurferRef = useRef(undefined); const [isPlaying, toggleIsPlaying] = useState(false); - const containerRef = useCallback((node:HTMLDivElement)=>{ - if(node){ + const containerRef = useCallback((node: HTMLDivElement) => { + if (node) { const waveSurfer = WaveSurfer.create({ container: node, barWidth: 2, - height: 30, + height: 24, cursorWidth: 0, + waveColor: "#D7C8C8", + progressColor: "#FB8381", }); waveSurfer.load(audio); - waveSurfer.on('ready', () => { + waveSurfer.on("ready", () => { waveSurferRef.current = waveSurfer; }); - waveSurfer.on('play', () => { + waveSurfer.on("play", () => { toggleIsPlaying(true); }); - waveSurfer.on('pause', () => { + waveSurfer.on("pause", () => { toggleIsPlaying(false); }); + waveSurfer.on("finish", () => { + waveSurfer.seekTo(0); + }); } }, []); - useEffect(()=>{ - return()=>{ + useEffect(() => { + return () => { console.log("OK?"); waveSurferRef.current?.destroy(); - } - },[]); + }; + }, []); return ( -
+
{ waveSurferRef.current?.playPause(); }} /> -
+
); }; diff --git a/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/audioItemElement.tsx b/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/audioItemElement.tsx index d5528f7e64..a2aeda42a9 100644 --- a/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/audioItemElement.tsx +++ b/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/audioItemElement.tsx @@ -11,7 +11,7 @@ import { initPosition, } from "~/pages/PageEnigma/store"; -import { H4, H6, Pill } from "~/components"; +import { H5, H6, Pill } from "~/components"; function getGcsUrl(bucketRelativePath: string | undefined | null): string { let bucket = "vocodes-public"; @@ -79,20 +79,22 @@ export const AudioItemElement = ({ item }: Props) => { return (
-
-
+ onPointerDown={onPointerDown}> +
+
Audio
- - {item.publicBucketPath && } - -

{item.name}

- {item.description &&
{item.description}
} + {item.publicBucketPath && ( + + )} + +
{item.name}
+ {item.description && ( +
+ {item.description} +
+ )}
); diff --git a/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/audioItemElements.tsx b/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/audioItemElements.tsx index 428bb244f9..2dfeaa1144 100644 --- a/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/audioItemElements.tsx +++ b/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/audioItemElements.tsx @@ -7,7 +7,6 @@ interface Props { } export const AudioItemElements = ({ items, assetFilter }: Props) => { - const displayItems = items.filter((item) => { if (assetFilter === AssetFilterOption.ALL) { return true; @@ -19,7 +18,7 @@ export const AudioItemElements = ({ items, assetFilter }: Props) => { }); return ( -
+
{displayItems.map((item) => ( ))} diff --git a/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/pageLibrary.tsx b/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/pageLibrary.tsx index 79aab7fb6f..43c897163c 100644 --- a/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/pageLibrary.tsx +++ b/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/pageLibrary.tsx @@ -10,25 +10,24 @@ import { AudioItemElements } from "./audioItemElements"; import { AudioTabPages } from "./types"; export const PageLibrary = ({ - changePage -}:{ - changePage: (newPage:AudioTabPages) => void -})=>{ + changePage, +}: { + changePage: (newPage: AudioTabPages) => void; +}) => { useSignals(); const allAudioItems = [...audioItems.value, ...audioItemsFromServer.value]; return ( <>
-
+
@@ -62,13 +59,12 @@ export const PageLibrary = ({ icon={faCirclePlus} variant="action" className="w-full py-3 text-sm font-medium" - onClick={()=>changePage(AudioTabPages.TTS)} - > + onClick={() => changePage(AudioTabPages.TTS)}> Generate Audio
-
+
); -} \ No newline at end of file +}; diff --git a/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/pageTTS.tsx b/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/pageTTS.tsx index cbf214443c..5d21338254 100644 --- a/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/pageTTS.tsx +++ b/app/pages/PageEnigma/comps/SidePanelTabs/AudioTab/pageTTS.tsx @@ -4,22 +4,31 @@ import { v4 as uuidv4 } from "uuid"; import { faChevronLeft, - faBrainCircuit, + faVolumeHigh, } from "@fortawesome/pro-solid-svg-icons"; -import { - Button, ButtonIcon, H2, Label, +import { + Button, + ButtonIcon, + H2, + Label, LoadingDotsTyping, ListSearchDropdown, - Textarea + Textarea, } from "~/components"; import { JobState } from "~/hooks/useInferenceJobManager/useInferenceJobManager"; -import { TtsModelListItem, GenerateTtsAudioResponse } from "~/pages/PageEnigma/models/tts"; +import { + TtsModelListItem, + GenerateTtsAudioResponse, +} from "~/pages/PageEnigma/models/tts"; -import { addInferenceJob, inferenceJobs} from "~/pages/PageEnigma/store/inferenceJobs"; +import { addInferenceJob, inferenceJobs } from "~/pages/PageEnigma/store/inferenceJobs"; -import { GenerateTtsAudio, GetMediaFileByToken } from "./utilities"; +import { + GenerateTtsAudio, + GetMediaFileByToken, +} from "./utilities"; import { AudioTabPages } from "./types"; import { AudioItemElement } from "./audioItemElement"; import { MediaItem, AssetType } from "~/pages/PageEnigma/models"; @@ -27,22 +36,21 @@ import { MediaItem, AssetType } from "~/pages/PageEnigma/models"; type TtsState = { voice: TtsModelListItem | undefined; text: string; - hasEnqueued :boolean; + hasEnqueued: boolean; inferenceToken?: string; inferenceJobType?: string; hasTtsResult: boolean; -} -const initialState:TtsState = - { - voice:undefined, - text:"", - hasEnqueued:false, - inferenceJobType: undefined, - inferenceToken: undefined, - hasTtsResult:false, - }; +}; +const initialState: TtsState = { + voice: undefined, + text: "", + hasEnqueued: false, + inferenceJobType: undefined, + inferenceToken: undefined, + hasTtsResult: false, +}; -export const PageTTS =({ +export const PageTTS = ({ changePage, sessionToken, voiceModels @@ -53,9 +61,9 @@ export const PageTTS =({ })=>{ const [ttsState, setTtsState] = useState(initialState); - const [ttsResultFile, setTtsResultFile] = useState(); + const [ttsResultFile, setTtsResultFile] = useState(); - useSignalEffect(()=>{ + useSignalEffect(() => { console.log(inferenceJobs.value); if(ttsState.hasEnqueued && ttsState.inferenceToken){ const found = inferenceJobs.value.find((job)=>job.job_id===ttsState.inferenceToken); @@ -65,25 +73,26 @@ export const PageTTS =({ setTtsState((curr)=>({ ...curr, hasTtsResult: true, - })) - GetMediaFileByToken(found.result.entity_token, sessionToken) - .then(res=>{ - console.log(res); - const morphedItem:MediaItem = { - version: 1, - type: AssetType.AUDIO, - media_id: res.media_file.token, - object_uuid: res.media_file.token, - name: ttsState.voice?.title || '', - description: ttsState.text, - publicBucketPath: res.media_file.public_bucket_path, - length: 25, - thumbnail: "/resources/placeholders/audio_placeholder.png", - isMine: true, - // isBookmarked?: boolean; - } - setTtsResultFile(morphedItem); - }) + })); + GetMediaFileByToken(found.result.entity_token, sessionToken).then( + (res) => { + console.log(res); + const morphedItem: MediaItem = { + version: 1, + type: AssetType.AUDIO, + media_id: res.media_file.token, + object_uuid: res.media_file.token, + name: ttsState.voice?.title || "", + description: ttsState.text, + publicBucketPath: res.media_file.public_bucket_path, + length: 25, + thumbnail: "/resources/placeholders/audio_placeholder.png", + isMine: true, + // isBookmarked?: boolean; + }; + setTtsResultFile(morphedItem); + }, + ); } } }); @@ -92,52 +101,56 @@ export const PageTTS =({ if(ttsState.hasEnqueued && ttsState.inferenceToken && ttsState.inferenceJobType){ console.log(`tts has Enqueued`); addInferenceJob({ - version:1, + version: 1, job_id: ttsState.inferenceToken, job_type: ttsState.inferenceJobType, job_status: JobState.PENDING, - }) - } - },[ttsState]); - - const requestTts = useCallback( (sessionToken:string)=>{ - const modelToken = ttsState.voice ? ttsState.voice.model_token : undefined; - - if(modelToken){ - setTtsState((curr)=>({ - ...curr, - hasEnqueued: true, - inferenceToken: undefined, - inferenceJobType: undefined, - hasAudio: false, - result: undefined, - })); - - const request = { - uuid_idempotency_token: uuidv4(), - tts_model_token: modelToken, - inference_text: ttsState.text, - }; - - GenerateTtsAudio(request, sessionToken).then(( - res:GenerateTtsAudioResponse)=>{ - if(res && res.inference_job_token){ - setTtsState((curr)=>({ - ...curr, - inferenceToken: res.inference_job_token, - inferenceJobType: res.inference_job_token_type - })); - } }); - }else{ - console.log("no voice model selected"); } - },[ttsState]); + }, [ttsState]); - const handleTextInput = ( - e: React.ChangeEvent - )=>{ - setTtsState((curr)=>({ + const requestTts = useCallback( + (sessionToken: string) => { + const modelToken = ttsState.voice + ? ttsState.voice.model_token + : undefined; + + if (modelToken) { + setTtsState((curr) => ({ + ...curr, + hasEnqueued: true, + inferenceToken: undefined, + inferenceJobType: undefined, + hasAudio: false, + result: undefined, + })); + + const request = { + uuid_idempotency_token: uuidv4(), + tts_model_token: modelToken, + inference_text: ttsState.text, + }; + + GenerateTtsAudio(request, sessionToken).then( + (res: GenerateTtsAudioResponse) => { + if (res && res.inference_job_token) { + setTtsState((curr) => ({ + ...curr, + inferenceToken: res.inference_job_token, + inferenceJobType: res.inference_job_token_type, + })); + } + }, + ); + } else { + console.log("no voice model selected"); + } + }, + [ttsState], + ); + + const handleTextInput = (e: React.ChangeEvent) => { + setTtsState((curr) => ({ ...curr, text: e.target.value, })); @@ -151,16 +164,17 @@ export const PageTTS =({ ...curr, voice: currVoiceModel, })); - } + }; - return( + return (
-
+
changePage(AudioTabPages.LIBRARY)} + onClick={() => changePage(AudioTabPages.LIBRARY)} /> -

Generate TTS

+

Generate TTS

{voiceModels.length > 0 && } -
+
-
-
+