mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-03 08:57:54 +00:00
styling/sidebar layout fixes (#258)
- broken layout fixes - every single tab is consistent now - audio tab styling updates
This commit is contained in:
1 parent
2fc081e6ef
commit
fd65e65290
21 files changed
+448
-408
No files matched your search
@@ -54,7 +54,7 @@ export const Button = ({
|
||||
disabled={disabled}
|
||||
{...{ ...rest, htmlFor }}>
|
||||
{icon && !iconFlip ? <FontAwesomeIcon icon={icon} /> : null}
|
||||
<div>{children}</div>
|
||||
{children}
|
||||
{icon && iconFlip ? <FontAwesomeIcon icon={icon} /> : null}
|
||||
</ButtonType>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
.fy-file-wrapper {
|
||||
border-color: transparent;
|
||||
border-style: dashed;
|
||||
border-width: 2px;
|
||||
// flex-grow: 1;
|
||||
height: calc(100% - 4px);
|
||||
overflow: scroll;
|
||||
|
||||
.file-picker-button {
|
||||
cursor: pointer;
|
||||
|
||||
+29
-26
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { IconDefinition } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
@@ -15,31 +15,34 @@ interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
icon?: IconDefinition;
|
||||
}
|
||||
|
||||
export const Input = React.forwardRef(({
|
||||
label, icon, className, id, ...rest
|
||||
}: InputProps,
|
||||
ref: React.ForwardedRef<HTMLInputElement>
|
||||
) => {
|
||||
return (
|
||||
<div className={twMerge("flex flex-col", className)}>
|
||||
{label && <Label htmlFor={id ? id : kebabCase(label)}>{label}</Label>}
|
||||
export const Input = React.forwardRef(
|
||||
(
|
||||
{ label, icon, className, id, ...rest }: InputProps,
|
||||
ref: React.ForwardedRef<HTMLInputElement>,
|
||||
) => {
|
||||
return (
|
||||
<div className={twMerge("flex flex-col", className)}>
|
||||
{label && <Label htmlFor={id ? id : kebabCase(label)}>{label}</Label>}
|
||||
|
||||
<div className="relative w-full">
|
||||
{icon && (
|
||||
<FontAwesomeIcon icon={icon} className="absolute h-5 pl-3 pt-2.5" />
|
||||
)}
|
||||
<input
|
||||
ref={ref}
|
||||
id={id ? id : label ? kebabCase(label) : undefined}
|
||||
className={twMerge(
|
||||
"h-10 w-full rounded-md bg-brand-secondary px-3 py-2.5 text-white outline-none outline-offset-0 transition-all duration-150 ease-in-out focus:outline-brand-primary",
|
||||
icon ? "pl-12" : "",
|
||||
<div className="relative w-full">
|
||||
{icon && (
|
||||
<FontAwesomeIcon icon={icon} className="absolute h-5 pl-3 pt-2.5" />
|
||||
)}
|
||||
onFocus={() => disableHotkeyInput(DomLevels.INPUT)}
|
||||
onBlur={() => enableHotkeyInput(DomLevels.INPUT)}
|
||||
{...rest}
|
||||
/>
|
||||
<input
|
||||
ref={ref}
|
||||
id={id ? id : label ? kebabCase(label) : undefined}
|
||||
className={twMerge(
|
||||
"h-10 w-full rounded-md bg-brand-secondary px-3 py-2.5 text-white outline-none outline-offset-0 transition-all duration-150 ease-in-out focus:outline-brand-primary",
|
||||
icon ? "pl-12" : "",
|
||||
)}
|
||||
onFocus={() => disableHotkeyInput(DomLevels.INPUT)}
|
||||
onBlur={() => enableHotkeyInput(DomLevels.INPUT)}
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Input.displayName = "Input";
|
||||
@@ -1,13 +1,15 @@
|
||||
import React, { useState, useRef } from "react";
|
||||
import { Transition } from "@headlessui/react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
interface TooltipProps {
|
||||
children: React.ReactElement;
|
||||
content: React.ReactNode;
|
||||
position: "top" | "bottom" | "left" | "right";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Tooltip = ({ children, content, position }: TooltipProps) => {
|
||||
const Tooltip = ({ children, content, position, className }: TooltipProps) => {
|
||||
const [isShowing, setIsShowing] = useState(false);
|
||||
const triggerRef = useRef<HTMLDivElement>(null);
|
||||
const tooltipRef = useRef<HTMLDivElement>(null);
|
||||
@@ -64,7 +66,10 @@ const Tooltip = ({ children, content, position }: TooltipProps) => {
|
||||
<div
|
||||
ref={tooltipRef}
|
||||
style={getStyleForPosition()}
|
||||
className="absolute z-10 w-max rounded-lg bg-ui-controls px-2.5 py-1.5 text-sm font-medium text-white shadow-lg">
|
||||
className={twMerge(
|
||||
"absolute z-10 w-max rounded-lg bg-ui-controls px-2.5 py-1.5 text-sm font-medium text-white shadow-xl",
|
||||
className ? className : "",
|
||||
)}>
|
||||
{content}
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
@@ -38,7 +38,7 @@ export const Label = ({
|
||||
...rest
|
||||
}: LabelHTMLAttributes<HTMLLabelElement>) => (
|
||||
<label
|
||||
className={twMerge(baseTypo, "mb-2 text-sm font-medium", className)}
|
||||
className={twMerge(baseTypo, "mb-2 text-[15px] font-medium", className)}
|
||||
{...rest}>
|
||||
{children}
|
||||
</label>
|
||||
@@ -51,7 +51,7 @@ export const H5 = ({ className, children }: TypoProps) => (
|
||||
);
|
||||
|
||||
export const H6 = ({ className, children }: TypoProps) => (
|
||||
<h6 className={twMerge(baseTypo, "text-sm font-light", className)}>
|
||||
<h6 className={twMerge(baseTypo, "text-sm font-normal", className)}>
|
||||
{children}
|
||||
</h6>
|
||||
);
|
||||
|
||||
@@ -8,22 +8,24 @@ import {
|
||||
faFileAudio,
|
||||
faSpinnerThird,
|
||||
faTrash,
|
||||
faXmark,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { UploadAudio, UploadAudioIsOk, UploadAudioRequest } from "./utilities";
|
||||
|
||||
import { Button } from "../Button";
|
||||
import { P, WaveformPlayer } from "~/components";
|
||||
import { ButtonIcon, P, WaveformPlayer } from "~/components";
|
||||
import Tooltip from "../Tooltip";
|
||||
|
||||
const FILE_TYPES = ["MP3", "WAV", "FLAC", "OGG"];
|
||||
|
||||
interface Props {
|
||||
sessionToken: string;
|
||||
file?: File;
|
||||
onFileStaged?: (file:File)=>void;
|
||||
onClear?: ()=>void;
|
||||
onFileUploaded: (token:string)=>void
|
||||
onFileStaged?: (file: File) => void;
|
||||
onClear?: () => void;
|
||||
onFileUploaded: (token: string) => void;
|
||||
}
|
||||
|
||||
function UploadComponent({
|
||||
@@ -33,19 +35,18 @@ function UploadComponent({
|
||||
onClear,
|
||||
onFileUploaded,
|
||||
}: Props) {
|
||||
|
||||
const [{file, uploadState, uploadToken}, setState] = useState<{
|
||||
const [{ file, uploadState, uploadToken }, setState] = useState<{
|
||||
file: File | undefined;
|
||||
uploadState: "init"|"none"|"uploading"|"uploaded"|"error";
|
||||
uploadToken ?: string;
|
||||
uploadState: "init" | "none" | "uploading" | "uploaded" | "error";
|
||||
uploadToken?: string;
|
||||
}>({
|
||||
file: propsFile,
|
||||
uploadState: propsFile ? "none" : "init",
|
||||
});
|
||||
const audioUrl = file ? URL.createObjectURL(file) : "";
|
||||
|
||||
const handleChange = (file:File) => {
|
||||
setState((curr)=>({
|
||||
const handleChange = (file: File) => {
|
||||
setState((curr) => ({
|
||||
...curr,
|
||||
file: file,
|
||||
uploadState: "none",
|
||||
@@ -88,9 +89,9 @@ function UploadComponent({
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(()=>{
|
||||
if(file && uploadState === "none" && onFileStaged) onFileStaged(file);
|
||||
if(!file && uploadState === "none" && onClear) onClear();
|
||||
useEffect(() => {
|
||||
if (file && uploadState === "none" && onFileStaged) onFileStaged(file);
|
||||
if (!file && uploadState === "none" && onClear) onClear();
|
||||
}, [file, uploadState]);
|
||||
useEffect(() => {
|
||||
if (uploadState === "uploaded" && uploadToken) onFileUploaded(uploadToken);
|
||||
@@ -109,12 +110,22 @@ function UploadComponent({
|
||||
|
||||
{file && (
|
||||
<>
|
||||
<div className="rounded-lg border border-brand-secondary-700 p-3">
|
||||
<WaveformPlayer audio={audioUrl} />
|
||||
<div className="flex items-center gap-3 rounded-lg bg-brand-secondary p-3">
|
||||
<div className="grow">
|
||||
<WaveformPlayer audio={audioUrl} />
|
||||
</div>
|
||||
<Tooltip content="Remove" position="top">
|
||||
<ButtonIcon
|
||||
icon={faXmark}
|
||||
onClick={handleClear}
|
||||
className="h-auto w-auto bg-transparent p-0 text-xl opacity-60 hover:bg-transparent hover:opacity-90"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="flex justify-evenly gap-3">
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
className="grow"
|
||||
className="w-full py-2.5"
|
||||
variant="action"
|
||||
onClick={handleUploadFile}
|
||||
disabled={uploadState !== "none" && uploadState !== "error"}
|
||||
icon={
|
||||
@@ -133,10 +144,6 @@ function UploadComponent({
|
||||
<FontAwesomeIcon icon={faSpinnerThird} spin />
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<Button className="grow" onClick={handleClear} icon={faTrash}>
|
||||
Clear
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -154,13 +161,13 @@ const DragAndDropZone = ({ file }: { file: any }) => {
|
||||
|
||||
if (!file) {
|
||||
return (
|
||||
<div className="flex cursor-pointer items-center gap-4 rounded-lg border-2 border-dashed border-brand-secondary-700 bg-brand-secondary p-3">
|
||||
<FontAwesomeIcon icon={faFileArrowUp} size="3x" />
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex cursor-pointer items-center gap-3.5 rounded-lg border-2 border-dashed border-ui-controls-button/50 bg-brand-secondary p-3">
|
||||
<FontAwesomeIcon icon={faFileArrowUp} className="text-4xl" />
|
||||
<div className="flex flex-col gap-0">
|
||||
<P className="font-medium">
|
||||
<u>Upload a file</u> or drop it here
|
||||
</P>
|
||||
<P className="opacity-50">
|
||||
<P className="flex items-center gap-2 text-sm font-normal opacity-50">
|
||||
{FILE_TYPES.join(", ").toString()} supported
|
||||
</P>
|
||||
</div>
|
||||
@@ -168,17 +175,17 @@ const DragAndDropZone = ({ file }: { file: any }) => {
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="flex cursor-pointer items-center gap-4 rounded-lg border-2 border-dashed border-brand-secondary-700 bg-brand-secondary p-3">
|
||||
<FontAwesomeIcon icon={faFileAudio} size="3x" />
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex cursor-pointer items-center gap-3.5 rounded-lg border-2 border-dashed border-ui-controls-button/50 bg-brand-secondary p-3">
|
||||
<FontAwesomeIcon icon={faFileAudio} className="text-4xl" />
|
||||
<div className="flex flex-col gap-0">
|
||||
<P className="font-medium">
|
||||
{file.name.slice(0, file.name.lastIndexOf("."))}
|
||||
</P>
|
||||
<P>
|
||||
<P className="flex items-center gap-2 text-sm font-normal">
|
||||
<span className="opacity-50">
|
||||
{`${file.name.split(".").pop().toUpperCase()} file size: ${fileSize} `}
|
||||
</span>
|
||||
<u>Change File</u>
|
||||
<u className="transition-all hover:text-white/80">Change File</u>
|
||||
</P>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,72 +10,82 @@ export const PageAudioGeneration = ({
|
||||
changePage,
|
||||
audioPanelState,
|
||||
setAudioPanelState,
|
||||
}:{
|
||||
}: {
|
||||
sessionToken: string;
|
||||
changePage: (newPage: AudioTabPages)=>void;
|
||||
audioPanelState: AudioPanelState
|
||||
setAudioPanelState: React.Dispatch<React.SetStateAction<AudioPanelState>>
|
||||
}) =>{
|
||||
changePage: (newPage: AudioTabPages) => void;
|
||||
audioPanelState: AudioPanelState;
|
||||
setAudioPanelState: React.Dispatch<React.SetStateAction<AudioPanelState>>;
|
||||
}) => {
|
||||
const subpage = audioPanelState.lastWorkingAudioGeneration;
|
||||
const changeSubpage = (newSubpage: AudioTabPages.TTS | AudioTabPages.V2V) => {
|
||||
setAudioPanelState((curr)=>({
|
||||
setAudioPanelState((curr) => ({
|
||||
...curr,
|
||||
lastWorkingAudioGeneration: newSubpage
|
||||
lastWorkingAudioGeneration: newSubpage,
|
||||
}));
|
||||
};
|
||||
const setTtsState = (newTtsState: TtsState)=>{
|
||||
setAudioPanelState((curr)=>({
|
||||
const setTtsState = (newTtsState: TtsState) => {
|
||||
setAudioPanelState((curr) => ({
|
||||
...curr,
|
||||
ttsState : {...curr.ttsState, ...newTtsState}
|
||||
ttsState: { ...curr.ttsState, ...newTtsState },
|
||||
}));
|
||||
};
|
||||
const setV2VState = (newV2VState: V2VState)=>{
|
||||
setAudioPanelState((curr)=>({
|
||||
const setV2VState = (newV2VState: V2VState) => {
|
||||
setAudioPanelState((curr) => ({
|
||||
...curr,
|
||||
v2vState : {...curr.v2vState, ...newV2VState}
|
||||
v2vState: { ...curr.v2vState, ...newV2VState },
|
||||
}));
|
||||
};
|
||||
return(
|
||||
<div className="flex flex-col px-4 pt-2">
|
||||
<TabTitle title="Generate Audio" onBack={() => changePage(AudioTabPages.LIBRARY)}/>
|
||||
return (
|
||||
<>
|
||||
<TabTitle
|
||||
title="Generate Audio"
|
||||
onBack={() => changePage(AudioTabPages.LIBRARY)}
|
||||
/>
|
||||
<div className="flex flex-col px-4">
|
||||
<div className="mb-4 flex h-10 w-full justify-evenly overflow-hidden rounded-lg">
|
||||
<button
|
||||
className={twMerge(
|
||||
"grow cursor-pointer bg-brand-secondary p-2 text-sm font-medium transition-all",
|
||||
subpage === AudioTabPages.TTS
|
||||
? "bg-ui-controls-button"
|
||||
: "hover:bg-ui-controls-button/50",
|
||||
)}
|
||||
disabled={subpage === AudioTabPages.TTS}
|
||||
onClick={() => changeSubpage(AudioTabPages.TTS)}>
|
||||
Text to Speech
|
||||
</button>
|
||||
<button
|
||||
className={twMerge(
|
||||
"grow cursor-pointer bg-brand-secondary p-2 text-sm font-medium transition-all",
|
||||
subpage === AudioTabPages.V2V
|
||||
? "bg-ui-controls-button"
|
||||
: "hover:bg-ui-controls-button/50",
|
||||
)}
|
||||
disabled={subpage === AudioTabPages.V2V}
|
||||
onClick={() => {
|
||||
changeSubpage(AudioTabPages.V2V);
|
||||
}}>
|
||||
Voice to Voice
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="w-full rounded-lg overflow-hidden flex justify-evenly mb-4">
|
||||
<button
|
||||
className={twMerge(
|
||||
"bg-brand-secondary p-2 grow",
|
||||
subpage === AudioTabPages.TTS ? "bg-brand-secondary-800": "",)}
|
||||
disabled={subpage === AudioTabPages.TTS}
|
||||
onClick={()=>changeSubpage(AudioTabPages.TTS)}
|
||||
>
|
||||
Text to Speech
|
||||
</button>
|
||||
<button
|
||||
className={twMerge(
|
||||
"bg-brand-secondary p-2 grow",
|
||||
subpage === AudioTabPages.V2V ? "bg-brand-secondary-800": "",)}
|
||||
disabled={subpage === AudioTabPages.V2V}
|
||||
onClick={()=>{changeSubpage(AudioTabPages.V2V)}}
|
||||
>
|
||||
Voice to Voice
|
||||
</button>
|
||||
{subpage === AudioTabPages.TTS && (
|
||||
<PageTTS
|
||||
changePage={changePage}
|
||||
sessionToken={sessionToken}
|
||||
ttsState={audioPanelState.ttsState}
|
||||
setTtsState={setTtsState}
|
||||
/>
|
||||
)}
|
||||
{subpage === AudioTabPages.V2V && (
|
||||
<PageVoicetoVoice
|
||||
changePage={changePage}
|
||||
sessionToken={sessionToken}
|
||||
v2vState={audioPanelState.v2vState}
|
||||
setV2VState={setV2VState}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{subpage === AudioTabPages.TTS &&
|
||||
<PageTTS
|
||||
changePage={changePage}
|
||||
sessionToken={sessionToken}
|
||||
ttsState={audioPanelState.ttsState}
|
||||
setTtsState={setTtsState}
|
||||
/>
|
||||
}
|
||||
{subpage === AudioTabPages.V2V &&
|
||||
<PageVoicetoVoice
|
||||
changePage={changePage}
|
||||
sessionToken={sessionToken}
|
||||
v2vState={audioPanelState.v2vState}
|
||||
setV2VState={setV2VState}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -41,38 +41,40 @@ export const PageLibrary = ({
|
||||
return (
|
||||
<>
|
||||
<TabTitle title="Audio" />
|
||||
<div className="w-full h-48 overflow-x-auto px-4 flex items-center justify-start gap-2">
|
||||
<button
|
||||
className={twMerge(
|
||||
"filter-tab",
|
||||
audioFilter.value === AssetFilterOption.ALL ? "active" : "",
|
||||
"disabled",
|
||||
)}
|
||||
onClick={() => (audioFilter.value = AssetFilterOption.ALL)}>
|
||||
All
|
||||
</button>
|
||||
<button
|
||||
className={twMerge(
|
||||
"filter-tab",
|
||||
audioFilter.value === AssetFilterOption.MINE ? "active" : "",
|
||||
"disabled",
|
||||
)}
|
||||
onClick={() => (audioFilter.value = AssetFilterOption.MINE)}
|
||||
disabled={!allAudioItems.value.some((item) => item.isMine)}>
|
||||
My Audios
|
||||
</button>
|
||||
<button
|
||||
className={twMerge(
|
||||
"filter-tab",
|
||||
audioFilter.value === AssetFilterOption.BOOKMARKED
|
||||
? "active"
|
||||
: "",
|
||||
"disabled",
|
||||
)}
|
||||
onClick={() => (audioFilter.value = AssetFilterOption.BOOKMARKED)}
|
||||
disabled={!allAudioItems.value.some((item) => item.isBookmarked)}>
|
||||
Bookmarked
|
||||
</button>
|
||||
<div>
|
||||
<div className="flex gap-2 overflow-x-auto overflow-y-hidden px-4">
|
||||
<button
|
||||
className={twMerge(
|
||||
"filter-tab",
|
||||
audioFilter.value === AssetFilterOption.ALL ? "active" : "",
|
||||
"disabled",
|
||||
)}
|
||||
onClick={() => (audioFilter.value = AssetFilterOption.ALL)}>
|
||||
All
|
||||
</button>
|
||||
<button
|
||||
className={twMerge(
|
||||
"filter-tab",
|
||||
audioFilter.value === AssetFilterOption.MINE ? "active" : "",
|
||||
"disabled",
|
||||
)}
|
||||
onClick={() => (audioFilter.value = AssetFilterOption.MINE)}
|
||||
disabled={!allAudioItems.value.some((item) => item.isMine)}>
|
||||
My Audios
|
||||
</button>
|
||||
<button
|
||||
className={twMerge(
|
||||
"filter-tab",
|
||||
audioFilter.value === AssetFilterOption.BOOKMARKED
|
||||
? "active"
|
||||
: "",
|
||||
"disabled",
|
||||
)}
|
||||
onClick={() => (audioFilter.value = AssetFilterOption.BOOKMARKED)}
|
||||
disabled={!allAudioItems.value.some((item) => item.isBookmarked)}>
|
||||
Bookmarked
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full px-4">
|
||||
@@ -85,7 +87,7 @@ export const PageLibrary = ({
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="grow w-full overflow-y-auto px-4 pb-4">
|
||||
<div className="w-full grow overflow-y-auto px-4 pb-4">
|
||||
{audioInferenceJobs.value.length > 0 && (
|
||||
<div className="mb-4 grid grid-cols-1 gap-2">
|
||||
{audioInferenceJobs.value.map((job) => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useCallback, useState } from "react";
|
||||
import { Input } from "~/components";
|
||||
import { AudioTabPages } from "./types";
|
||||
import { TtsModelListItem } from "~/pages/PageEnigma/models/tts";
|
||||
@@ -9,54 +8,56 @@ import { VoiceModelElement } from "./voiceModelElement";
|
||||
export const PageSelectTtsModel = ({
|
||||
changePage,
|
||||
ttsModels,
|
||||
onSelect
|
||||
}:{
|
||||
changePage: (newPage:AudioTabPages)=>void;
|
||||
onSelect,
|
||||
}: {
|
||||
changePage: (newPage: AudioTabPages) => void;
|
||||
ttsModels: Array<TtsModelListItem>;
|
||||
onSelect: (item:TtsModelListItem)=>void;
|
||||
})=>{
|
||||
const [query, setQuery] = useState('');
|
||||
const filteredListOfModels = query === ''
|
||||
? ttsModels
|
||||
: ttsModels.filter((model) =>{
|
||||
return model.title
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, '')
|
||||
.includes(query.toLowerCase().replace(/\s+/g, ''));
|
||||
})
|
||||
onSelect: (item: TtsModelListItem) => void;
|
||||
}) => {
|
||||
const [query, setQuery] = useState("");
|
||||
const filteredListOfModels =
|
||||
query === ""
|
||||
? ttsModels
|
||||
: ttsModels.filter((model) => {
|
||||
return model.title
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, "")
|
||||
.includes(query.toLowerCase().replace(/\s+/g, ""));
|
||||
});
|
||||
|
||||
const slicedArray = filteredListOfModels.slice(0, 20);
|
||||
|
||||
const refCallback = useCallback((node:HTMLInputElement)=>{
|
||||
if(node) node.focus();
|
||||
//auto focus on the mounting on the input component
|
||||
},[]);
|
||||
|
||||
return(
|
||||
<div className="flex flex-col px-4 pt-2">
|
||||
const refCallback = useCallback((node: HTMLInputElement) => {
|
||||
if (node) node.focus();
|
||||
//auto focus on the mounting on the input component
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabTitle
|
||||
title="Search TTS Voices"
|
||||
onBack={() => changePage(AudioTabPages.GENERATE_AUDIO)}
|
||||
/>
|
||||
<div className="flex flex-col gap-4 overflow-hidden">
|
||||
<Input
|
||||
ref={refCallback}
|
||||
className="mt-1 px-4"
|
||||
placeholder="Search Voice by Name"
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
/>
|
||||
|
||||
<Input
|
||||
ref={refCallback}
|
||||
className="px-4"
|
||||
placeholder="Search Voice by Name"
|
||||
onChange={(e)=>setQuery(e.target.value)}
|
||||
/>
|
||||
|
||||
<div className="grow w-full flex flex-col overflow-y-auto px-4 pb-4 gap-4">
|
||||
{slicedArray.map((item)=>{
|
||||
return(
|
||||
<VoiceModelElement
|
||||
key={item.model_token}
|
||||
model={item}
|
||||
onSelect={(item)=>onSelect(item as TtsModelListItem)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<div className="flex w-full grow flex-col gap-3 overflow-y-auto px-4 pb-4">
|
||||
{slicedArray.map((item) => {
|
||||
return (
|
||||
<VoiceModelElement
|
||||
key={item.model_token}
|
||||
model={item}
|
||||
onSelect={(item) => onSelect(item as TtsModelListItem)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1,40 +1,40 @@
|
||||
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
import { Input } from "~/components";
|
||||
import { AudioTabPages } from "./types";
|
||||
import { TabTitle } from "~/pages/PageEnigma/comps/SidePanelTabs/comps/TabTitle";
|
||||
import { VoiceModelElement } from "./voiceModelElement";
|
||||
import { VoiceConversionModelListItem } from './typesImported';
|
||||
import { VoiceConversionModelListItem } from "./typesImported";
|
||||
|
||||
export const PageSelectV2VModel = ({
|
||||
changePage,
|
||||
v2vModels,
|
||||
onSelect
|
||||
}:{
|
||||
changePage: (newPage:AudioTabPages)=>void;
|
||||
onSelect,
|
||||
}: {
|
||||
changePage: (newPage: AudioTabPages) => void;
|
||||
v2vModels: Array<VoiceConversionModelListItem>;
|
||||
onSelect: (item:VoiceConversionModelListItem)=>void;
|
||||
})=>{
|
||||
const [query, setQuery] = useState('');
|
||||
const filteredListOfModels = query === ''
|
||||
? v2vModels
|
||||
: v2vModels.filter((model) =>{
|
||||
return model.title
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, '')
|
||||
.includes(query.toLowerCase().replace(/\s+/g, ''));
|
||||
})
|
||||
onSelect: (item: VoiceConversionModelListItem) => void;
|
||||
}) => {
|
||||
const [query, setQuery] = useState("");
|
||||
const filteredListOfModels =
|
||||
query === ""
|
||||
? v2vModels
|
||||
: v2vModels.filter((model) => {
|
||||
return model.title
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, "")
|
||||
.includes(query.toLowerCase().replace(/\s+/g, ""));
|
||||
});
|
||||
|
||||
const slicedArray = filteredListOfModels.slice(0, 20);
|
||||
|
||||
const refCallback = useCallback((node:HTMLInputElement)=>{
|
||||
if(node) node.focus();
|
||||
const refCallback = useCallback((node: HTMLInputElement) => {
|
||||
if (node) node.focus();
|
||||
//auto focus on the mounting on the input component
|
||||
},[]);
|
||||
}, []);
|
||||
|
||||
return(
|
||||
<div className="flex flex-col px-4 pt-2">
|
||||
return (
|
||||
<div className="flex flex-col gap-4 overflow-hidden">
|
||||
<TabTitle
|
||||
title="Search Convertible Voices"
|
||||
onBack={() => changePage(AudioTabPages.GENERATE_AUDIO)}
|
||||
@@ -42,21 +42,23 @@ export const PageSelectV2VModel = ({
|
||||
|
||||
<Input
|
||||
ref={refCallback}
|
||||
className="px-4"
|
||||
className="mt-1 px-4"
|
||||
placeholder="Search Voice by Name"
|
||||
onChange={(e)=>setQuery(e.target.value)}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
/>
|
||||
<div className="grow w-full flex flex-col overflow-y-auto px-4 pb-4 gap-4">
|
||||
{slicedArray.map((item)=>{
|
||||
return(
|
||||
<div className="flex w-full grow flex-col gap-3 overflow-y-auto px-4 pb-4">
|
||||
{slicedArray.map((item) => {
|
||||
return (
|
||||
<VoiceModelElement
|
||||
key={item.token}
|
||||
model={item}
|
||||
onSelect={(item)=>onSelect(item as VoiceConversionModelListItem)}
|
||||
onSelect={(item) =>
|
||||
onSelect(item as VoiceConversionModelListItem)
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1,20 +1,14 @@
|
||||
import { useCallback, } from "react";
|
||||
import { useCallback } from "react";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { faVolumeHigh, faChevronRight} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { faVolumeHigh, faChevronRight } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import {
|
||||
H4, H6,
|
||||
Button,
|
||||
Label,
|
||||
Textarea,
|
||||
} from "~/components";
|
||||
import { H4, H6, Button, Label, Textarea } from "~/components";
|
||||
|
||||
import { GenerateTtsAudioResponse, } from "~/pages/PageEnigma/models/tts";
|
||||
import { GenerateTtsAudioResponse } from "~/pages/PageEnigma/models/tts";
|
||||
|
||||
|
||||
import { GenerateTtsAudio, } from "./utilities";
|
||||
import { GenerateTtsAudio } from "./utilities";
|
||||
import { AudioTabPages, TtsState } from "./types";
|
||||
|
||||
export const PageTTS = ({
|
||||
@@ -22,13 +16,12 @@ export const PageTTS = ({
|
||||
sessionToken,
|
||||
ttsState,
|
||||
setTtsState,
|
||||
}:{
|
||||
changePage: (newPage:AudioTabPages) => void;
|
||||
}: {
|
||||
changePage: (newPage: AudioTabPages) => void;
|
||||
sessionToken: string;
|
||||
ttsState: TtsState,
|
||||
setTtsState: (newState:TtsState)=>void,
|
||||
})=>{
|
||||
|
||||
ttsState: TtsState;
|
||||
setTtsState: (newState: TtsState) => void;
|
||||
}) => {
|
||||
const requestTts = useCallback(
|
||||
(sessionToken: string) => {
|
||||
const modelToken = ttsState.voice
|
||||
@@ -36,7 +29,6 @@ export const PageTTS = ({
|
||||
: undefined;
|
||||
|
||||
if (modelToken) {
|
||||
|
||||
const request = {
|
||||
uuid_idempotency_token: uuidv4(),
|
||||
tts_model_token: modelToken,
|
||||
@@ -50,7 +42,7 @@ export const PageTTS = ({
|
||||
...ttsState,
|
||||
inferenceTokens: [
|
||||
...ttsState.inferenceTokens,
|
||||
res.inference_job_token
|
||||
res.inference_job_token,
|
||||
],
|
||||
});
|
||||
|
||||
@@ -74,21 +66,24 @@ export const PageTTS = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<Label className="mb-1">Select a Voice</Label>
|
||||
<div
|
||||
className="p-3 bg-brand-secondary rounded-lg flex justify-between items-center gap-3 cursor-pointer"
|
||||
onClick={()=>changePage(AudioTabPages.SELECT_TTS_MODEL)}
|
||||
>
|
||||
<span className="bg-brand-secondary-600 rounded-lg w-12 h-12"/>
|
||||
<Label>Select a Voice</Label>
|
||||
<button
|
||||
className="flex cursor-pointer items-center justify-between gap-3 rounded-lg bg-brand-secondary p-3 text-start transition-all hover:bg-ui-controls-button/40"
|
||||
onClick={() => changePage(AudioTabPages.SELECT_TTS_MODEL)}>
|
||||
<span className="h-12 w-12 rounded-lg bg-ui-controls-button/100" />
|
||||
<div className="grow">
|
||||
{!ttsState.voice && <H4>None Selected</H4>}
|
||||
{ttsState.voice && <>
|
||||
<H4>{ttsState.voice.title}</H4>
|
||||
<H6>by {ttsState.voice.creator_display_name}</H6>
|
||||
</>}
|
||||
{ttsState.voice && (
|
||||
<>
|
||||
<H4>{ttsState.voice.title}</H4>
|
||||
<H6 className="text-white/70">
|
||||
by {ttsState.voice.creator_display_name}
|
||||
</H6>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<FontAwesomeIcon icon={faChevronRight} size="2x"/>
|
||||
</div>
|
||||
<FontAwesomeIcon icon={faChevronRight} className="text-xl opacity-60" />
|
||||
</button>
|
||||
|
||||
<div className="mt-4 w-full">
|
||||
<Textarea
|
||||
@@ -101,7 +96,7 @@ export const PageTTS = ({
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className="h-11 w-full text-sm mt-4"
|
||||
className="mt-4 h-11 w-full text-sm"
|
||||
variant="primary"
|
||||
disabled={ttsState.text === ""}
|
||||
icon={faVolumeHigh}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback } from "react";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { AudioTabPages, V2VState } from "./types";
|
||||
@@ -9,9 +9,12 @@ import {
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { EnqueueVoiceConversionRequest, EnqueueVoiceConversionResponse } from './typesImported';
|
||||
import { GenerateVoiceConversion } from './utilities';
|
||||
import { H4, H6, Button } from "~/components";
|
||||
import {
|
||||
EnqueueVoiceConversionRequest,
|
||||
EnqueueVoiceConversionResponse,
|
||||
} from "./typesImported";
|
||||
import { GenerateVoiceConversion } from "./utilities";
|
||||
import { H4, H6, Button, Label } from "~/components";
|
||||
|
||||
import UploadComponent from "~/components/UploadComponent";
|
||||
|
||||
@@ -20,74 +23,85 @@ export const PageVoicetoVoice = ({
|
||||
sessionToken,
|
||||
v2vState,
|
||||
setV2VState,
|
||||
}:{
|
||||
changePage: (newPage:AudioTabPages) => void;
|
||||
}: {
|
||||
changePage: (newPage: AudioTabPages) => void;
|
||||
sessionToken: string;
|
||||
v2vState : V2VState;
|
||||
setV2VState : (newState:V2VState)=>void,
|
||||
v2vState: V2VState;
|
||||
setV2VState: (newState: V2VState) => void;
|
||||
}) => {
|
||||
const requestV2V = useCallback((sessionToken: string)=>{
|
||||
const modelToken = v2vState.voice
|
||||
? v2vState.voice.token
|
||||
: undefined;
|
||||
if(modelToken && v2vState.inputFileToken){
|
||||
let request: EnqueueVoiceConversionRequest = {
|
||||
uuid_idempotency_token: uuidv4(),
|
||||
voice_conversion_model_token: modelToken,
|
||||
source_media_upload_token: v2vState.inputFileToken,
|
||||
};
|
||||
const requestV2V = useCallback(
|
||||
(sessionToken: string) => {
|
||||
const modelToken = v2vState.voice ? v2vState.voice.token : undefined;
|
||||
if (modelToken && v2vState.inputFileToken) {
|
||||
let request: EnqueueVoiceConversionRequest = {
|
||||
uuid_idempotency_token: uuidv4(),
|
||||
voice_conversion_model_token: modelToken,
|
||||
source_media_upload_token: v2vState.inputFileToken,
|
||||
};
|
||||
|
||||
GenerateVoiceConversion(request, sessionToken).then(
|
||||
(res:EnqueueVoiceConversionResponse)=>{
|
||||
if (res && res.inference_job_token) {
|
||||
setV2VState({
|
||||
...v2vState,
|
||||
inferenceTokens: [
|
||||
...v2vState.inferenceTokens,
|
||||
res.inference_job_token
|
||||
],
|
||||
});
|
||||
changePage(AudioTabPages.LIBRARY);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}, [v2vState]);
|
||||
GenerateVoiceConversion(request, sessionToken).then(
|
||||
(res: EnqueueVoiceConversionResponse) => {
|
||||
if (res && res.inference_job_token) {
|
||||
setV2VState({
|
||||
...v2vState,
|
||||
inferenceTokens: [
|
||||
...v2vState.inferenceTokens,
|
||||
res.inference_job_token,
|
||||
],
|
||||
});
|
||||
changePage(AudioTabPages.LIBRARY);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
[v2vState],
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<H4 className="mb-1">Select a Voice</H4>
|
||||
<div
|
||||
className="p-3 bg-brand-secondary rounded-lg flex justify-between items-center gap-3 cursor-pointer"
|
||||
onClick={()=>changePage(AudioTabPages.SELECT_V2V_MODEL)}
|
||||
>
|
||||
<span className="bg-brand-secondary-600 rounded-lg w-12 h-12"/>
|
||||
<div className="grow">
|
||||
{!v2vState.voice && <H4>None Selected</H4>}
|
||||
{v2vState.voice && <>
|
||||
<H4>{v2vState.voice.title}</H4>
|
||||
<H6>by {v2vState.voice.creator.display_name}</H6>
|
||||
</>}
|
||||
</div>
|
||||
<FontAwesomeIcon icon={faChevronRight} size="2x"/>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col">
|
||||
<Label>Select a Voice</Label>
|
||||
<button
|
||||
className="flex cursor-pointer items-center justify-between gap-3 rounded-lg bg-brand-secondary p-3 text-start transition-all hover:bg-ui-controls-button/40"
|
||||
onClick={() => changePage(AudioTabPages.SELECT_V2V_MODEL)}>
|
||||
<span className="h-12 w-12 rounded-lg bg-ui-controls-button/100" />
|
||||
<div className="grow">
|
||||
{!v2vState.voice && <H4>None Selected</H4>}
|
||||
{v2vState.voice && (
|
||||
<>
|
||||
<H4>{v2vState.voice.title}</H4>
|
||||
<H6 className="text-white/70">
|
||||
by {v2vState.voice.creator.display_name}
|
||||
</H6>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<FontAwesomeIcon
|
||||
icon={faChevronRight}
|
||||
className="text-xl opacity-60"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<H4 className="mt-4 mb-2">Upload Audio</H4>
|
||||
<UploadComponent
|
||||
sessionToken={sessionToken}
|
||||
file={v2vState.file}
|
||||
onFileStaged={(file:File)=>{
|
||||
setV2VState({...v2vState, file: file});
|
||||
}}
|
||||
onClear={()=>{
|
||||
setV2VState({...v2vState, file: undefined});
|
||||
}}
|
||||
onFileUploaded={(fileToken:string)=>{
|
||||
setV2VState({
|
||||
...v2vState,
|
||||
inputFileToken: fileToken
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<Label>Upload Audio</Label>
|
||||
<UploadComponent
|
||||
sessionToken={sessionToken}
|
||||
file={v2vState.file}
|
||||
onFileStaged={(file: File) => {
|
||||
setV2VState({ ...v2vState, file: file });
|
||||
}}
|
||||
onClear={() => {
|
||||
setV2VState({ ...v2vState, file: undefined });
|
||||
}}
|
||||
onFileUploaded={(fileToken: string) => {
|
||||
setV2VState({
|
||||
...v2vState,
|
||||
inputFileToken: fileToken,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/* <div className="my-4 flex w-full gap-4 justify-between items-center">
|
||||
<span className="bg-brand-secondary grow h-1"/>
|
||||
<H4>OR</H4>
|
||||
@@ -103,13 +117,13 @@ export const PageVoicetoVoice = ({
|
||||
Record Audio
|
||||
</Button> */}
|
||||
<Button
|
||||
className="h-11 w-full text-sm mt-4"
|
||||
className="w-full py-3 text-sm"
|
||||
variant="primary"
|
||||
disabled={!v2vState.voice || !v2vState.inputFileToken}
|
||||
icon={faRightLeft}
|
||||
onClick={()=>requestV2V(sessionToken)}>
|
||||
onClick={() => requestV2V(sessionToken)}>
|
||||
Convert
|
||||
</Button>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -3,31 +3,30 @@ import { H4, H6 } from "~/components";
|
||||
import { VoiceConversionModelListItem } from "./typesImported";
|
||||
export const VoiceModelElement = ({
|
||||
model,
|
||||
onSelect
|
||||
}:{
|
||||
onSelect,
|
||||
}: {
|
||||
model: TtsModelListItem | VoiceConversionModelListItem;
|
||||
onSelect:(item:TtsModelListItem | VoiceConversionModelListItem)=>void
|
||||
})=>{
|
||||
let creatorName: string|undefined;
|
||||
if ("creator_display_name" in model){
|
||||
onSelect: (item: TtsModelListItem | VoiceConversionModelListItem) => void;
|
||||
}) => {
|
||||
let creatorName: string | undefined;
|
||||
if ("creator_display_name" in model) {
|
||||
//case of TTS Models
|
||||
creatorName = model.creator_display_name;
|
||||
}else if("creator" in model){
|
||||
} else if ("creator" in model) {
|
||||
//case of V2V Models
|
||||
creatorName = model.creator.display_name
|
||||
}else {
|
||||
creatorName = model.creator.display_name;
|
||||
} else {
|
||||
creatorName = undefined;
|
||||
}
|
||||
return(
|
||||
<div
|
||||
className="p-3 bg-brand-secondary rounded-lg flex justify-between items-center gap-3 cursor-pointer border-2 border-brand-secondary hover:border-brand-secondary-700"
|
||||
onClick={()=>onSelect(model)}
|
||||
>
|
||||
<span className="bg-brand-secondary-600 rounded-lg w-12 h-12"/>
|
||||
<div className="grow">
|
||||
<H4>{model.title}</H4>
|
||||
{creatorName && <H6>{creatorName}</H6>}
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<button
|
||||
className="flex cursor-pointer items-center justify-between gap-3 rounded-lg border-2 border-transparent bg-brand-secondary p-3 text-start transition-all hover:border-ui-controls-button hover:bg-ui-controls-button/40"
|
||||
onClick={() => onSelect(model)}>
|
||||
<span className="h-12 w-12 rounded-lg bg-ui-controls-button/100" />
|
||||
<div className="grow">
|
||||
<H4>{model.title}</H4>
|
||||
{creatorName && <H6 className="text-white/70">{creatorName}</H6>}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -14,9 +14,10 @@ export const SidePanelTabs = () => {
|
||||
<div
|
||||
key={tab.value}
|
||||
className={
|
||||
tab.value === selectedTab.value?.value ? "h-full flex flex-col gap-4" : "hidden"
|
||||
}
|
||||
>
|
||||
tab.value === selectedTab.value?.value
|
||||
? "flex h-full flex-col gap-3.5 overflow-y-auto"
|
||||
: "hidden"
|
||||
}>
|
||||
{tab.component}
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { faAngleLeft, faClose } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { faChevronLeft, faClose } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { ButtonIcon } from "~/components";
|
||||
import {
|
||||
lastSelectedTab,
|
||||
selectedTab,
|
||||
sidePanelVisible,
|
||||
} from "~/pages/PageEnigma/store";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
@@ -20,12 +19,14 @@ export function TabTitle({ title, onBack }: Props) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between px-4">
|
||||
<div className="flex items-center justify-between px-4 pt-4">
|
||||
{onBack ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<button onClick={onBack}>
|
||||
<FontAwesomeIcon icon={faAngleLeft} />
|
||||
</button>
|
||||
<div className="flex items-center gap-3">
|
||||
<ButtonIcon
|
||||
onClick={onBack}
|
||||
icon={faChevronLeft}
|
||||
className="h-auto w-auto text-xl opacity-50 hover:opacity-90"
|
||||
/>
|
||||
<div className="align-middle text-base font-bold">{title}</div>
|
||||
</div>
|
||||
) : (
|
||||
@@ -34,8 +35,7 @@ export function TabTitle({ title, onBack }: Props) {
|
||||
<ButtonIcon
|
||||
onClick={onClose}
|
||||
icon={faClose}
|
||||
size="lg"
|
||||
className="h-auto w-auto opacity-75 hover:opacity-50"
|
||||
className="h-auto w-auto text-xl opacity-50 hover:opacity-90"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -13,38 +13,42 @@ export const AnimationTab = () => {
|
||||
return (
|
||||
<>
|
||||
<TabTitle title="Animation" />
|
||||
<div className="w-full h-48 overflow-x-auto px-4 flex items-center justify-start gap-2">
|
||||
<button
|
||||
className={twMerge(
|
||||
"filter-tab",
|
||||
animationFilter.value === AssetFilterOption.ALL ? "active" : "",
|
||||
)}
|
||||
onClick={() => (animationFilter.value = AssetFilterOption.ALL)}>
|
||||
All
|
||||
</button>
|
||||
<button
|
||||
className={twMerge(
|
||||
"filter-tab",
|
||||
animationFilter.value === AssetFilterOption.MINE ? "active" : "",
|
||||
)}
|
||||
onClick={() => (animationFilter.value = AssetFilterOption.MINE)}
|
||||
disabled={!animationItems.value.some((item) => item.isMine)}>
|
||||
My Animations
|
||||
</button>
|
||||
<button
|
||||
className={twMerge(
|
||||
"filter-tab",
|
||||
animationFilter.value === AssetFilterOption.BOOKMARKED
|
||||
? "active"
|
||||
: "",
|
||||
)}
|
||||
onClick={() =>
|
||||
(animationFilter.value = AssetFilterOption.BOOKMARKED)
|
||||
}
|
||||
disabled={!animationItems.value.some((item) => item.isBookmarked)}>
|
||||
Bookmarked
|
||||
</button>
|
||||
|
||||
<div>
|
||||
<div className="flex gap-2 overflow-x-auto overflow-y-hidden px-4">
|
||||
<button
|
||||
className={twMerge(
|
||||
"filter-tab",
|
||||
animationFilter.value === AssetFilterOption.ALL ? "active" : "",
|
||||
)}
|
||||
onClick={() => (animationFilter.value = AssetFilterOption.ALL)}>
|
||||
All
|
||||
</button>
|
||||
<button
|
||||
className={twMerge(
|
||||
"filter-tab",
|
||||
animationFilter.value === AssetFilterOption.MINE ? "active" : "",
|
||||
)}
|
||||
onClick={() => (animationFilter.value = AssetFilterOption.MINE)}
|
||||
disabled={!animationItems.value.some((item) => item.isMine)}>
|
||||
My Animations
|
||||
</button>
|
||||
<button
|
||||
className={twMerge(
|
||||
"filter-tab",
|
||||
animationFilter.value === AssetFilterOption.BOOKMARKED
|
||||
? "active"
|
||||
: "",
|
||||
)}
|
||||
onClick={() =>
|
||||
(animationFilter.value = AssetFilterOption.BOOKMARKED)
|
||||
}
|
||||
disabled={!animationItems.value.some((item) => item.isBookmarked)}>
|
||||
Bookmarked
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full px-4">
|
||||
<Button
|
||||
icon={faCirclePlus}
|
||||
@@ -53,7 +57,7 @@ export const AnimationTab = () => {
|
||||
Upload Animation
|
||||
</Button>
|
||||
</div>
|
||||
<div className="grow flex w-full overflow-y-auto px-4 pb-4">
|
||||
<div className="w-full grow overflow-y-auto px-4 pb-4">
|
||||
<ItemElements
|
||||
items={animationItems.value}
|
||||
assetFilter={animationFilter.value}
|
||||
|
||||
@@ -110,9 +110,12 @@ export const ObjectsTab = ({ type }: Props) => {
|
||||
{...{
|
||||
onSuccess: reFetchList,
|
||||
render: ({ parentId }: { parentId: string }) => (
|
||||
<>
|
||||
<div className="w-full overflow-x-auto">
|
||||
<div className="mb-4 mt-4 flex justify-start gap-2 px-4">
|
||||
<div className="flex h-full flex-col gap-3.5">
|
||||
<TabTitle
|
||||
title={`${type === AssetType.CHARACTER ? "Characters" : "Objects"}`}
|
||||
/>
|
||||
<div>
|
||||
<div className="flex gap-2 overflow-x-auto overflow-y-hidden px-4">
|
||||
<button
|
||||
className={twMerge(
|
||||
"filter-tab",
|
||||
@@ -152,10 +155,10 @@ export const ObjectsTab = ({ type }: Props) => {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div {...{ className: "w-full px-4 pb-4" }}>
|
||||
<div {...{ className: "w-full px-4" }}>
|
||||
<Button
|
||||
{...{
|
||||
className: "file-picker-button",
|
||||
className: "file-picker-button py-3",
|
||||
htmlFor: parentId,
|
||||
icon: faCirclePlus,
|
||||
variant: "action",
|
||||
@@ -163,7 +166,7 @@ export const ObjectsTab = ({ type }: Props) => {
|
||||
Upload {type === AssetType.CHARACTER ? "Character" : "Object"}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-full px-4">
|
||||
<div className="w-full grow overflow-y-auto px-4 pb-4">
|
||||
<ItemElements
|
||||
{...{
|
||||
...(type !== AssetType.CHARACTER
|
||||
@@ -178,7 +181,7 @@ export const ObjectsTab = ({ type }: Props) => {
|
||||
assetFilter={assetFilter.value}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -43,7 +43,7 @@ export const Prompts = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3 rounded-t-lg bg-ui-panel py-4">
|
||||
<div className="flex flex-col gap-3 rounded-t-lg bg-ui-panel px-4">
|
||||
<div className="relative w-full">
|
||||
<Textarea
|
||||
label="Enter a Prompt"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button } from "~/components";
|
||||
import { Button, Label } from "~/components";
|
||||
import { faArrowsRotate, faFilm } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { editorState, EditorStates } from "~/pages/PageEnigma/store/engine";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
@@ -36,17 +36,15 @@ export function StyleButtons() {
|
||||
console.log("state", editorState.value);
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col justify-center gap-4 rounded-b-lg bg-ui-panel">
|
||||
<div className="flex w-full flex-col justify-center gap-4 rounded-b-lg bg-ui-panel px-4">
|
||||
<div className="w-full">
|
||||
{editorState.value === EditorStates.EDIT && (
|
||||
<>
|
||||
<div className="mb-2 text-sm font-medium">
|
||||
Render the current camera view with AI
|
||||
</div>
|
||||
<Label>Render the current camera view with AI</Label>
|
||||
<Button
|
||||
icon={faArrowsRotate}
|
||||
variant="primary"
|
||||
className="w-full"
|
||||
className="mt-1.5 w-full"
|
||||
onClick={switchPreview}>
|
||||
Preview Frame
|
||||
</Button>
|
||||
@@ -63,9 +61,9 @@ export function StyleButtons() {
|
||||
)}
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<div className="text-sm font-medium">
|
||||
<Label>
|
||||
When you're done, render your entire animation with AI
|
||||
</div>
|
||||
</Label>
|
||||
<div className="mb-2 text-xs text-white/70">
|
||||
(This may take several minutes)
|
||||
</div>
|
||||
|
||||
@@ -39,7 +39,7 @@ export const StyleSelection = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 rounded-t-lg bg-ui-panel">
|
||||
<div className="flex flex-col gap-4 rounded-t-lg bg-ui-panel px-4">
|
||||
<div className="flex flex-col">
|
||||
<div className="relative">
|
||||
<div
|
||||
|
||||
@@ -10,7 +10,7 @@ export function StylizeTab() {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="overflow-y-auto overflow-x-hidden p-4"
|
||||
className="flex flex-col gap-4"
|
||||
style={{ height: sidePanelHeight.value }}>
|
||||
<TabTitle title="Transform your animation with AI" />
|
||||
<StyleSelection />
|
||||
|
||||
Reference in new issue
Block a user