mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 09:48:44 +00:00
Merge pull request #141 from storytold/feature/tts-inference
Feature/tts inference
This commit is contained in:
44 files changed
+1724
-323
No files matched your search
+14
@@ -0,0 +1,14 @@
|
||||
export const apiHost = 'https://api.fakeyou.com';
|
||||
|
||||
// Authentications
|
||||
// const createAccount = `${apiHost}}/create_account`;
|
||||
export const login = `${apiHost}/v1/login`;
|
||||
export const getSession = `${apiHost}/v1/session`;
|
||||
export const logout = `${apiHost}/v1/logout`;
|
||||
|
||||
// TTS
|
||||
export const inferTts = `${apiHost}/tts/inference`;
|
||||
export const listTts = `${apiHost}/tts/list`;
|
||||
|
||||
// User Media
|
||||
export const listMediaByUser = (username:string) => `${apiHost}/v1/media_files/list/user/${username}`;
|
||||
@@ -1,28 +1,32 @@
|
||||
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { FontAwesomeIcon, FontAwesomeIconProps } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
interface ButtonIconProps {
|
||||
icon: IconDefinition;
|
||||
fill?: boolean;
|
||||
interface ButtonIconProps extends FontAwesomeIconProps {
|
||||
// icon: IconDefinition;
|
||||
onClick: () => void;
|
||||
className?: string;
|
||||
hoverFill?: boolean;
|
||||
}
|
||||
|
||||
export const ButtonIcon = ({
|
||||
icon,
|
||||
size,
|
||||
onClick,
|
||||
fill = false,
|
||||
className : propsClassName,
|
||||
hoverFill = false,
|
||||
}: ButtonIconProps) => {
|
||||
const className = twMerge(
|
||||
"flex h-8 w-8 items-center justify-center rounded-lg transition-all duration-150",
|
||||
fill
|
||||
hoverFill
|
||||
? "bg-ui-controls-button hover:bg-ui-controls-button/[0.75]"
|
||||
: "bg-transparent hover:bg-ui-panel/[0.4]",
|
||||
propsClassName,
|
||||
);
|
||||
|
||||
return (
|
||||
<button className={className} onClick={onClick}>
|
||||
<FontAwesomeIcon icon={icon} />
|
||||
<FontAwesomeIcon icon={icon} size={size}/>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
import { Fragment, useEffect, useState } from 'react'
|
||||
import { Listbox, Transition } from '@headlessui/react'
|
||||
import { faCheck, faChevronDown } from '@fortawesome/pro-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
|
||||
|
||||
interface ListDropdownProps {
|
||||
list: {[key: string] : string}[];
|
||||
onSelect: (val:string)=>void;
|
||||
}
|
||||
export const ListDropdown = ({
|
||||
list, onSelect
|
||||
}:ListDropdownProps) => {
|
||||
const [selected, setSelected] = useState(list[0]);
|
||||
|
||||
useEffect(()=>{
|
||||
onSelect(Object.values(selected)[0]);
|
||||
}, [selected]);
|
||||
|
||||
return (
|
||||
<Listbox value={selected} onChange={setSelected}>
|
||||
<div className="relative mt-1">
|
||||
<Listbox.Button className="relative w-full h-10 cursor-pointer rounded-md bg-brand-secondary py-2 pl-3 pr-10 text-left outline-none outline-offset-0 transition-all duration-150 ease-in-out focus:outline-brand-primary sm:text-sm">
|
||||
<span className="block truncate">{selected.name}</span>
|
||||
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<FontAwesomeIcon icon={faChevronDown} aria-hidden="true"/>
|
||||
</span>
|
||||
</Listbox.Button>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave="transition ease-in duration-100"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Listbox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-brand-secondary py-1 text-base shadow-lg focus:outline-none sm:text-sm z-10">
|
||||
{list.map((item, itemIdx) => (
|
||||
<Listbox.Option
|
||||
key={itemIdx}
|
||||
className={({ active }) =>
|
||||
`relative cursor-pointer select-none py-2 pl-10 pr-4 text-white ${
|
||||
active ? 'text-white' : 'text-gray-400'
|
||||
}`
|
||||
}
|
||||
value={item}
|
||||
>
|
||||
{({ selected }) => (
|
||||
<>
|
||||
<span
|
||||
className={`block truncate ${
|
||||
selected ? 'font-medium' : 'font-normal'
|
||||
}`}
|
||||
>
|
||||
{Object.values(item)[0]}
|
||||
</span>
|
||||
{selected ? (
|
||||
<span className="absolute inset-y-0 left-0 flex items-center pl-3">
|
||||
<FontAwesomeIcon icon={faCheck} aria-hidden="true"/>
|
||||
</span>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</Listbox.Option>
|
||||
))}
|
||||
</Listbox.Options>
|
||||
</Transition>
|
||||
</div>
|
||||
</Listbox>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import { Fragment, useState, useEffect } from 'react'
|
||||
import { Combobox, Transition } from '@headlessui/react'
|
||||
import { faCheck, faChevronDown } from '@fortawesome/pro-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
|
||||
interface ListDropdownProps {
|
||||
list: {[key: string] : string}[];
|
||||
listDisplayKey?: string;
|
||||
onSelect: (val:string)=>void
|
||||
}
|
||||
export const ListSearchDropdown = ({
|
||||
list, listDisplayKey, onSelect
|
||||
}:ListDropdownProps) => {
|
||||
const [selected, setSelected] = useState(list[0])
|
||||
const [query, setQuery] = useState('')
|
||||
|
||||
const filteredList =
|
||||
query === ''
|
||||
? list
|
||||
: list.filter((item) =>{
|
||||
if(listDisplayKey)
|
||||
return item[listDisplayKey]
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, '')
|
||||
.includes(query.toLowerCase().replace(/\s+/g, ''));
|
||||
|
||||
return Object.values(item)[0]
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, '')
|
||||
.includes(query.toLowerCase().replace(/\s+/g, ''));
|
||||
})
|
||||
|
||||
useEffect(()=>{
|
||||
if (listDisplayKey) onSelect(selected[listDisplayKey]);
|
||||
else onSelect(Object.values(selected)[0]);
|
||||
}, [selected]);
|
||||
|
||||
return (
|
||||
<Combobox value={selected} onChange={setSelected}>
|
||||
<div className="relative mt-1">
|
||||
<div className="relative w-full cursor-default rounded-md text-left shadow-md sm:text-sm">
|
||||
<Combobox.Input
|
||||
className="w-full border-none rounded-md py-2 pl-3 pr-10 text-sm leading-5 bg-brand-secondary text-white outline-none outline-offset-0 transition-all duration-150 ease-in-out focus:outline-brand-primary"
|
||||
displayValue={(item: {[key: string] : string}) => {
|
||||
if(listDisplayKey) return item[listDisplayKey];
|
||||
else return Object.values(item)[0]
|
||||
}}
|
||||
onChange={(event) => setQuery(event.target.value)}
|
||||
/>
|
||||
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<FontAwesomeIcon icon={faChevronDown} aria-hidden="true"/>
|
||||
</Combobox.Button>
|
||||
</div>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave="transition ease-in duration-100"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
afterLeave={() => setQuery('')}
|
||||
>
|
||||
<Combobox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-brand-secondary py-1 text-base shadow-lg focus:outline-none sm:text-sm z-10">
|
||||
{filteredList.length === 0 && query !== '' ? (
|
||||
<div className="relative cursor-default select-none px-4 py-2 text-white">
|
||||
Nothing found.
|
||||
</div>
|
||||
) : (
|
||||
filteredList.map((item, itemIdx) => {
|
||||
if(itemIdx <= 10)
|
||||
return (
|
||||
<Combobox.Option
|
||||
key={itemIdx}
|
||||
className={({ active }) =>
|
||||
`relative cursor-pointer select-none py-2 pl-10 pr-4 ${
|
||||
active ? 'text-white' : 'text-gray-400'
|
||||
}`
|
||||
}
|
||||
value={item}
|
||||
>
|
||||
{({ selected, active }) => (
|
||||
<>
|
||||
<span
|
||||
className={`block truncate ${
|
||||
selected ? 'font-medium' : 'font-normal'
|
||||
}`}
|
||||
>
|
||||
{!listDisplayKey && Object.values(item)[0]}
|
||||
{listDisplayKey && item[listDisplayKey]}
|
||||
</span>
|
||||
{selected ? (
|
||||
<span
|
||||
className={`absolute inset-y-0 left-0 flex items-center pl-3 ${
|
||||
active ? 'text-white' : 'text-teal-600'
|
||||
}`}
|
||||
>
|
||||
<FontAwesomeIcon icon={faCheck} aria-hidden="true"/>
|
||||
</span>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</Combobox.Option>
|
||||
);
|
||||
return null;
|
||||
})
|
||||
)}
|
||||
</Combobox.Options>
|
||||
</Transition>
|
||||
</div>
|
||||
</Combobox>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { Fragment, ReactNode } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
export const TransitionDialogue = ({
|
||||
isOpen, title, onClose, className, children,
|
||||
}:{
|
||||
isOpen: boolean;
|
||||
title?: ReactNode;
|
||||
onClose:()=>void;
|
||||
className?: string;
|
||||
children: ReactNode;
|
||||
|
||||
})=>{
|
||||
return(
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10 " onClose={onClose}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black/40" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className={twMerge("w-full max-w-lg transform rounded-xl border border-ui-panel-border bg-ui-panel p-5 text-left align-middle shadow-xl transition-all", className)}>
|
||||
{title && (
|
||||
<Dialog.Title
|
||||
as="h4"
|
||||
className="mb-4 text-xl font-bold text-white"
|
||||
>
|
||||
{title}
|
||||
</Dialog.Title>
|
||||
)}
|
||||
{children}
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
);
|
||||
};
|
||||
@@ -7,6 +7,9 @@ import { InputVector } from "./InputVector";
|
||||
import { H1, H2, H3, H4, H5, H6, P, Label, Link} from "./Typography";
|
||||
import { ItemPicker } from "./ItemPicker";
|
||||
import { Textarea } from "./Textarea";
|
||||
import { TransitionDialogue } from "./TransitionDialogue";
|
||||
import { ListDropdown } from "./ListDropdown"
|
||||
import { ListSearchDropdown } from "./ListSearchDropdown"
|
||||
import { LoadingBar } from "./LoadingBar";
|
||||
import { LoadingDots, LoadingDotsBricks, LoadingDotsTyping } from "./LoadingDots";
|
||||
|
||||
@@ -18,8 +21,11 @@ export {
|
||||
H1,H2,H3,H4,H5,H6,P,Label,Link,
|
||||
ItemPicker,
|
||||
Textarea,
|
||||
TransitionDialogue,
|
||||
Input,
|
||||
InputVector,
|
||||
ListDropdown,
|
||||
ListSearchDropdown,
|
||||
LoadingBar,
|
||||
LoadingDots,
|
||||
LoadingDotsBricks,
|
||||
|
||||
@@ -32,7 +32,7 @@ export const AuthenticationContext = createContext<{
|
||||
|
||||
export const AuthenticationProvider = ({children}:{children:ReactNode})=>{
|
||||
// console.log('auth provider rerender');
|
||||
const [authCookies, setAuthCookie, removeAuthCookie] = useCookies([STORAGE_KEYS.USER_INFO]);
|
||||
const [, setAuthCookie, removeAuthCookie] = useCookies([STORAGE_KEYS.USER_INFO]);
|
||||
const [authState, setAuthState] = useState<AuthState>({});
|
||||
|
||||
const loginAndGetUserInfo = useCallback((
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
|
||||
import { login, getSession, logout } from '~/api';
|
||||
import {
|
||||
SessionResponse,
|
||||
} from "./types";
|
||||
|
||||
const apiHost = "https://api.fakeyou.com";
|
||||
// const createAccount = `${apiHost}}/create_account`;
|
||||
const login = `${apiHost}/v1/login`;
|
||||
const getSession = `${apiHost}/v1/session`;
|
||||
const logout = `${apiHost}/v1/logout`;
|
||||
|
||||
export const CreateSession = async ({
|
||||
usernameOrEmail, password
|
||||
}:{
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { useInferenceJobManager } from "./useInferenceJobManager";
|
||||
@@ -0,0 +1 @@
|
||||
export { useInferenceJobManager } from './useInferenceJobManager';
|
||||
@@ -0,0 +1,122 @@
|
||||
import { useEffect, useCallback } from "react";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { inferenceJobs, updateInferenceJob } from '~/pages/PageEnigma/store/inferenceJobs';
|
||||
|
||||
export enum JobState {
|
||||
UNKNOWN = "unknown", // Only on frontend.
|
||||
PENDING = "pending",
|
||||
STARTED = "started",
|
||||
COMPLETE_SUCCESS = "complete_success",
|
||||
COMPLETE_FAILURE = "complete_failure",
|
||||
ATTEMPT_FAILED = "attempt_failed",
|
||||
DEAD = "dead",
|
||||
CANCELED_BY_USER = "canceled_by_user",
|
||||
}
|
||||
export interface RequestDetails {
|
||||
inference_category: string,
|
||||
maybe_model_type?: string,
|
||||
maybe_model_token?: string,
|
||||
maybe_model_title?: string,
|
||||
maybe_raw_inference_text?: string,
|
||||
}
|
||||
|
||||
export interface StatusDetails {
|
||||
status: string,
|
||||
maybe_extra_status_description?: string,
|
||||
maybe_failure_category?: string,
|
||||
attempt_count: number,
|
||||
}
|
||||
|
||||
export interface ResultDetails {
|
||||
entity_type: string,
|
||||
entity_token: string,
|
||||
maybe_public_bucket_media_path?: string,
|
||||
}
|
||||
|
||||
export interface JobStatus {
|
||||
job_token: string,
|
||||
request: RequestDetails,
|
||||
status: StatusDetails,
|
||||
maybe_result?: ResultDetails,
|
||||
created_at: Date,
|
||||
updated_at: Date,
|
||||
}
|
||||
|
||||
export interface GetJobStatusResponse {
|
||||
success: boolean;
|
||||
state: JobStatus
|
||||
}
|
||||
type ErrorResponse = {
|
||||
success: boolean;
|
||||
error_reason: string;
|
||||
}
|
||||
export async function GetInferenceJobStatus(jobToken:string) : Promise<GetJobStatusResponse | null> {
|
||||
|
||||
const endpoint = `https://api.fakeyou.com/v1/model_inference/job_status/${jobToken}`;
|
||||
|
||||
return await fetch(endpoint, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
const response : GetJobStatusResponse = res;
|
||||
return response;
|
||||
})
|
||||
.catch(e => {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
function shouldKeepPolling(currStatus:string){
|
||||
if(currStatus === JobState.PENDING
|
||||
|| currStatus === JobState.STARTED
|
||||
) return true;
|
||||
return false
|
||||
}
|
||||
|
||||
export const useInferenceJobManager = () => {
|
||||
useSignals();
|
||||
|
||||
const pollInferenceJobs = useCallback(()=>{
|
||||
if(inferenceJobs.value.length > 0 ){
|
||||
inferenceJobs.value.forEach(job=>{
|
||||
if(shouldKeepPolling(job.job_status)){
|
||||
GetInferenceJobStatus(job.job_id).then((res:GetJobStatusResponse|null)=>{
|
||||
if(res!==null){
|
||||
if(res.state.status.status !== job.job_status){
|
||||
console.log(`${res.state.job_token} has new state: ${res.state.status.status}`)
|
||||
if(res.state.status.status === JobState.COMPLETE_SUCCESS){
|
||||
console.log(res.state);
|
||||
updateInferenceJob({
|
||||
...job,
|
||||
job_status: res.state.status.status,
|
||||
result: res.state.maybe_result
|
||||
});
|
||||
}else{
|
||||
updateInferenceJob({
|
||||
...job,
|
||||
job_status: res.state.status.status
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(()=>{
|
||||
console.log('useInferenceJobManager Mounted');
|
||||
const intervalTimer = setInterval(pollInferenceJobs,2000);
|
||||
return()=>{
|
||||
console.log('useInferenceJobManager Dismounted');
|
||||
clearInterval(intervalTimer);
|
||||
}
|
||||
},[]);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { Fragment, useState } from "react";
|
||||
|
||||
import { useState } from "react";
|
||||
import { TransitionDialogue } from "~/components/TransitionDialogue";
|
||||
import { Button, ButtonPropsI } from "~/components/Button";
|
||||
|
||||
type UnionedButtonProps = { label?: string } & ButtonPropsI;
|
||||
@@ -9,11 +8,15 @@ interface ButtonDialoguePropsI {
|
||||
buttonProps?: UnionedButtonProps;
|
||||
confirmButtonProps?: UnionedButtonProps;
|
||||
closeButtonProps?: UnionedButtonProps;
|
||||
dialogProps?:{
|
||||
className?: string;
|
||||
};
|
||||
title?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const ButtonDialogue = ({
|
||||
dialogProps = {},
|
||||
buttonProps: unionedButtonProps,
|
||||
confirmButtonProps,
|
||||
closeButtonProps: unionedCloseButtonProps,
|
||||
@@ -36,74 +39,41 @@ export const ButtonDialogue = ({
|
||||
{buttonLabel}
|
||||
</Button>
|
||||
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10 " onClose={closeModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
<TransitionDialogue
|
||||
title={title}
|
||||
isOpen={isOpen}
|
||||
onClose={closeModal}
|
||||
className={dialogProps.className}
|
||||
>
|
||||
<div className="mt-2">{children}</div>
|
||||
|
||||
<div className="mt-6 flex justify-end gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
onClick={closeModal}
|
||||
{...closeButtonProps}
|
||||
variant="secondary"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black/40" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-xl border border-ui-panel-border bg-ui-panel p-5 text-left align-middle shadow-xl transition-all">
|
||||
{title && (
|
||||
<Dialog.Title
|
||||
as="h4"
|
||||
className="mb-4 text-xl font-bold text-white"
|
||||
>
|
||||
{title}
|
||||
</Dialog.Title>
|
||||
)}
|
||||
<div className="mt-2">{children}</div>
|
||||
|
||||
<div className="mt-6 flex justify-end gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
onClick={closeModal}
|
||||
{...closeButtonProps}
|
||||
variant="secondary"
|
||||
>
|
||||
{closeButtonLabel}
|
||||
</Button>
|
||||
{confirmButtonProps && (
|
||||
<Button
|
||||
type="button"
|
||||
{...confirmButtonProps}
|
||||
onClick={(e) => {
|
||||
if (confirmButtonProps.onClick) {
|
||||
confirmButtonProps.onClick(e);
|
||||
}
|
||||
closeModal();
|
||||
}}
|
||||
>
|
||||
{confirmButtonProps.label
|
||||
? confirmButtonProps.label
|
||||
: "Confirm"}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
{closeButtonLabel}
|
||||
</Button>
|
||||
{confirmButtonProps && (
|
||||
<Button
|
||||
type="button"
|
||||
{...confirmButtonProps}
|
||||
onClick={(e) => {
|
||||
if (confirmButtonProps.onClick) {
|
||||
confirmButtonProps.onClick(e);
|
||||
}
|
||||
closeModal();
|
||||
}}
|
||||
>
|
||||
{confirmButtonProps.label
|
||||
? confirmButtonProps.label
|
||||
: "Confirm"}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</TransitionDialogue>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,141 @@
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import {
|
||||
faClipboard,
|
||||
faClipboardList,
|
||||
faCircleCheck,
|
||||
faCircleExclamation,
|
||||
faLoader,
|
||||
faSpinner,
|
||||
faSquareXmark,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { deleteInferenceJob, inferenceJobs } from "~/pages/PageEnigma/store/inferenceJobs";
|
||||
import { InferenceJob } from "~/pages/PageEnigma/models";
|
||||
|
||||
import { ButtonDialogue } from "../ButtonDialogue";
|
||||
import { ButtonIcon, H4,H6,P } from "~/components";
|
||||
import { JobState } from "~/hooks/useInferenceJobManager/useInferenceJobManager";
|
||||
|
||||
const printJobType = (jobType : string)=>{
|
||||
switch (jobType){
|
||||
case "generic":
|
||||
return "Text to Speech";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
};
|
||||
|
||||
const printJobStatus = (jobStatus : string)=>{
|
||||
switch (jobStatus){
|
||||
case JobState.PENDING:
|
||||
return "Adding to the queue.";
|
||||
case JobState.STARTED:
|
||||
return "Processing, please wait...";
|
||||
case JobState.COMPLETE_SUCCESS:
|
||||
return "Completed! See Audio Panel for the result. file."
|
||||
default:
|
||||
return "Unknown Status";
|
||||
}
|
||||
};
|
||||
|
||||
const JobStatusIcon = ({jobStatus} : {jobStatus:string})=>{
|
||||
switch (jobStatus){
|
||||
case JobState.PENDING:{
|
||||
return(
|
||||
<FontAwesomeIcon
|
||||
className="col-span-1 animate-spin"
|
||||
icon={faSpinner}
|
||||
size="2xl"
|
||||
/>
|
||||
);
|
||||
}
|
||||
case JobState.STARTED:{
|
||||
return(
|
||||
<FontAwesomeIcon
|
||||
className="col-span-1 animate-spin"
|
||||
icon={faLoader}
|
||||
size="2xl"
|
||||
/>
|
||||
);
|
||||
}
|
||||
case JobState.COMPLETE_SUCCESS:{
|
||||
return(
|
||||
<FontAwesomeIcon
|
||||
className="col-span-1"
|
||||
icon={faCircleCheck}
|
||||
size="2xl"
|
||||
/>
|
||||
);
|
||||
}
|
||||
default:{
|
||||
return(
|
||||
<FontAwesomeIcon
|
||||
className="col-span-1"
|
||||
icon={faCircleExclamation}
|
||||
size="2xl"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const InferenceItem = ({job}:{job:InferenceJob}) => {
|
||||
// console.log(job);
|
||||
return(
|
||||
<li
|
||||
className="grid grid-cols-12 gap-2 items-center bg-brand-secondary rounded-md py-2 px-4"
|
||||
>
|
||||
<JobStatusIcon jobStatus={job.job_status} />
|
||||
<div className="col-span-10 flex flex-col">
|
||||
<H4>{printJobType(job.job_type)}</H4>
|
||||
<H6>{printJobStatus(job.job_status)} </H6>
|
||||
</div>
|
||||
{job.job_status === JobState.COMPLETE_SUCCESS &&
|
||||
<div className="col-span-1 flex justify-end">
|
||||
<ButtonIcon
|
||||
icon={faSquareXmark}
|
||||
size="2xl"
|
||||
onClick={()=>{
|
||||
deleteInferenceJob(job);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
export const DialogueInference = () =>{
|
||||
useSignals();
|
||||
|
||||
const buttonIcon = inferenceJobs.value.length > 0
|
||||
? faClipboardList : faClipboard;
|
||||
return(
|
||||
<ButtonDialogue
|
||||
title="My Jobs"
|
||||
dialogProps={{
|
||||
className:"w-1/2 max-w-full"
|
||||
}}
|
||||
buttonProps={{
|
||||
icon: buttonIcon,
|
||||
variant: "secondary",
|
||||
label: "My Jobs",
|
||||
className:( inferenceJobs.value.length === 0 ? "invisible" : "visible")
|
||||
}}
|
||||
>
|
||||
{inferenceJobs.value.length > 0 &&
|
||||
<ul className="flex flex-col gap-2">
|
||||
{inferenceJobs.value.map(job=>
|
||||
<InferenceItem key={job.job_id} job={job} />
|
||||
)}
|
||||
</ul>
|
||||
}
|
||||
{
|
||||
inferenceJobs.value.length === 0 &&
|
||||
<P>You currently have no running proccesses.</P>
|
||||
}
|
||||
</ButtonDialogue>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { DialogueInference } from './DialogueInference'
|
||||
@@ -1,16 +1,24 @@
|
||||
import { useContext, } from "react";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
|
||||
import { AuthenticationContext } from "~/contexts/Authentication";
|
||||
import { Button, ButtonLink } from "~/components";
|
||||
import { DialogueInference } from "../DialogueInference";
|
||||
import { inferenceJobs } from "~/pages/PageEnigma/store/inferenceJobs";
|
||||
|
||||
export const AuthButtons = ()=>{
|
||||
useSignals();
|
||||
|
||||
const {authState, logout} = useContext(AuthenticationContext);
|
||||
const handleLogout = ()=>{
|
||||
if(logout)logout();
|
||||
}
|
||||
if ( authState && authState.isLoggedIn ) {
|
||||
return(
|
||||
<Button onClick={handleLogout}>Log Out</Button>
|
||||
<>
|
||||
<DialogueInference />
|
||||
<Button onClick={handleLogout}>Log Out</Button>
|
||||
</>
|
||||
);
|
||||
}else{
|
||||
return(
|
||||
|
||||
@@ -4,7 +4,6 @@ import { AuthenticationContext } from "~/contexts/Authentication";
|
||||
|
||||
export const withProtectionRoute = <P extends {}>(Component: ComponentType<P>) => (rest:P) => {
|
||||
const { authState } = useContext(AuthenticationContext);
|
||||
// console.log(authState);
|
||||
if (authState.isLoggedIn === undefined ) {
|
||||
return <>waiting</>;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ import { TrackProvider } from "~/pages/PageEnigma/contexts/TrackContext/TrackPro
|
||||
import { AppUIProvider } from "~/pages/PageEnigma/contexts/AppUiContext";
|
||||
import { EngineProvider } from "~/pages/PageEnigma/contexts/EngineProvider";
|
||||
import { DragComponent } from "~/pages/PageEnigma/comps/DragComponent/DragComponent";
|
||||
import { useInferenceJobManager } from "~/hooks";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import {
|
||||
@@ -14,6 +16,7 @@ import {
|
||||
|
||||
export const PageEnigma = () => {
|
||||
useSignals();
|
||||
useInferenceJobManager();
|
||||
useEffect(() => {
|
||||
function setPage() {
|
||||
// TODO address this issue with zooming
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Controls3D } from "./comps/Controls3D";
|
||||
import { ControlsTopButtons } from "./comps/ControlsTopButtons";
|
||||
import { ControlsVideo } from "./comps/ControlsVideo";
|
||||
import { ControlPanelSceneObject } from "./comps/ControlPanelSceneObject";
|
||||
import { DialogueTTS } from "./comps/DialogueTTS/DialogueTTS";
|
||||
import { PreviewEngineCamera } from "./comps/PreviewEngineCamera";
|
||||
import { ViewSideBySide } from "./comps/ViewSideBySide";
|
||||
import { Timeline } from "./comps/Timeline";
|
||||
@@ -113,6 +114,7 @@ export const PageEnigmaComponent = () => {
|
||||
<PreviewEngineCamera />
|
||||
<ControlsVideo />
|
||||
<ControlPanelSceneObject />
|
||||
<DialogueTTS />
|
||||
</div>
|
||||
</div>
|
||||
{appUiState.viewMode === APPUI_VIEW_MODES.SIDE_BY_SIDE && (
|
||||
|
||||
@@ -1,26 +1,35 @@
|
||||
import { fromEngineActions } from "~/pages/PageEnigma/Queue/fromEngineActions";
|
||||
import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions";
|
||||
|
||||
import {
|
||||
QueueClip,
|
||||
QueueKeyframe,
|
||||
UpdateTime,
|
||||
} from "~/pages/PageEnigma/models";
|
||||
import { toTimelineActions } from "./toTimelineActions";
|
||||
|
||||
type UnionedActionTypes = fromEngineActions | toEngineActions | toTimelineActions;
|
||||
type UnionedDataTypes = QueueClip | UpdateTime | QueueKeyframe | ClipUI[] | MediaItem;
|
||||
|
||||
export type QueueSubscribeType = {
|
||||
action: UnionedActionTypes;
|
||||
data: UnionedDataTypes;
|
||||
}
|
||||
import { ClipUI } from "../datastructures/clips/clip_ui";
|
||||
import { MediaItem } from "~/pages/PageEnigma/models";
|
||||
class Queue {
|
||||
private _queue: Record<
|
||||
string,
|
||||
{
|
||||
action: fromEngineActions | toEngineActions | toTimelineActions;
|
||||
data: QueueClip | UpdateTime | QueueKeyframe | ClipUI[] | MediaItem;
|
||||
action: UnionedActionTypes;
|
||||
data: UnionedDataTypes;
|
||||
}[]
|
||||
> = {};
|
||||
private _subscribers: Record<
|
||||
string,
|
||||
(entry: {
|
||||
action: fromEngineActions | toEngineActions | toTimelineActions;
|
||||
data: QueueClip | UpdateTime | QueueKeyframe | ClipUI[] | MediaItem;
|
||||
action: UnionedActionTypes;
|
||||
data: UnionedDataTypes;
|
||||
}) => void
|
||||
> = {};
|
||||
|
||||
@@ -30,8 +39,8 @@ class Queue {
|
||||
data,
|
||||
}: {
|
||||
queueName: string;
|
||||
action: fromEngineActions | toEngineActions | toTimelineActions;
|
||||
data: QueueClip | UpdateTime | QueueKeyframe | ClipUI[] | MediaItem ;
|
||||
action: UnionedActionTypes;
|
||||
data: UnionedDataTypes ;
|
||||
}) {
|
||||
if (!this._queue[queueName]) {
|
||||
this._queue[queueName] = [];
|
||||
@@ -46,10 +55,7 @@ class Queue {
|
||||
|
||||
public subscribe(
|
||||
queueName: string,
|
||||
onMessage: (entry: {
|
||||
action: fromEngineActions | toEngineActions | toTimelineActions;
|
||||
data: QueueClip | UpdateTime | QueueKeyframe | ClipUI[] | MediaItem;
|
||||
}) => void,
|
||||
onMessage: (entry: QueueSubscribeType) => void,
|
||||
) {
|
||||
this._subscribers[queueName] = onMessage;
|
||||
while (this._queue[queueName]?.length) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export enum QueueNames {
|
||||
FROM_ENGINE = "FROM_ENGINE",
|
||||
TO_ENGINE = "TO_ENGINE",
|
||||
TO_TIMELINE = "TO_TIMELINE"
|
||||
TO_TIMELINE = "TO_TIMELINE",
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export enum toInferenceActions {
|
||||
ADD_TTS_JOB = "add_tts_job"
|
||||
}
|
||||
@@ -66,7 +66,7 @@ export const Controls3D = () => {
|
||||
<div className="flex justify-center">
|
||||
<div className="rounded-b-lg border-x border-b border-ui-panel-border bg-ui-controls p-2 text-white">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<ButtonIcon icon={faPlus} onClick={handlePlus} fill={true} />
|
||||
<ButtonIcon icon={faPlus} onClick={handlePlus} hoverFill={true} />
|
||||
|
||||
<span className="h-4 w-0 border-l border-white/[0.15]" />
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { useContext, useState } from "react";
|
||||
import { Button, Input } from "~/components";
|
||||
import { Input } from "~/components";
|
||||
import { ButtonDialogue } from "~/modules/ButtonDialogue";
|
||||
import { EngineContext } from "../../contexts/EngineContext";
|
||||
import { ToasterContext } from "~/pages/PageEnigma/contexts/ToasterContext";
|
||||
import { APPUI_ACTION_TYPES } from "~/pages/PageEnigma/reducers";
|
||||
// import { APPUI_ACTION_TYPES } from "~/pages/PageEnigma/reducers";
|
||||
|
||||
import { TestFeaturesButtons } from "./TestFeaturesButtons";
|
||||
|
||||
export const ControlsTopButtons = () => {
|
||||
const editorEngine = useContext(EngineContext);
|
||||
|
||||
const [sceneName, setSceneName] = useState<string>("");
|
||||
const [sceneToken, setSceneToken] = useState<string>("");
|
||||
const { addToast } = useContext(ToasterContext);
|
||||
@@ -33,25 +36,6 @@ export const ControlsTopButtons = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleButtonTest = () => {};
|
||||
|
||||
const handleTestButton2 = () => {
|
||||
console.log("Test Button 2");
|
||||
};
|
||||
|
||||
const handleButtonCameraView = () => {
|
||||
editorEngine?.switchCameraView();
|
||||
};
|
||||
// const handleButtonPlayBack = () => {
|
||||
// editorEngine?.startPlayback();
|
||||
// };
|
||||
const handleButtonTakeFrame = () => {
|
||||
// editorEngine?.take_timeline_cam_clip();
|
||||
};
|
||||
const handleButtonSingleFrame = () => {
|
||||
editorEngine?.generateFrame();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2 pl-3 pt-3">
|
||||
<div className="flex gap-2">
|
||||
@@ -128,14 +112,7 @@ export const ControlsTopButtons = () => {
|
||||
<p>Ask Wil about React</p>
|
||||
</ButtonDialogue>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
{/* <Button onClick={handleButtonSingleFrame}>Render Single Frame</Button>
|
||||
<Button onClick={handleButtonTakeFrame}>Take Frame</Button> */}
|
||||
</div>
|
||||
<TestFeaturesButtons debug={false}/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
import { useContext } from "react";
|
||||
import { Button, } from "~/components";
|
||||
import { ClipGroup } from "~/pages/PageEnigma/models/track";
|
||||
import { APPUI_ACTION_TYPES } from "../../reducers";
|
||||
import { AppUiContext } from "../../contexts/AppUiContext";
|
||||
import { EngineContext } from "../../contexts/EngineContext";
|
||||
|
||||
export const TestFeaturesButtons = ({debug}:{debug:boolean})=>{
|
||||
if (!debug) return null;
|
||||
|
||||
const [appUiState, dispatchAppUiState] = useContext(AppUiContext);
|
||||
const editorEngine = useContext(EngineContext);
|
||||
|
||||
const testObjectPanel = () => {
|
||||
if (appUiState?.controlPanel.isShowing) {
|
||||
dispatchAppUiState({
|
||||
type: APPUI_ACTION_TYPES.HIDE_CONTROLPANELS_SCENEOBJECT,
|
||||
});
|
||||
} else {
|
||||
dispatchAppUiState({
|
||||
type: APPUI_ACTION_TYPES.SHOW_CONTROLPANELS_SCENEOBJECT,
|
||||
payload: {
|
||||
group: ClipGroup.OBJECT,
|
||||
object_name: "TEST BUTTON",
|
||||
object_uuid: "",
|
||||
version: "1",
|
||||
objectVectors: {
|
||||
position: { x: 1, y: 2, z: 3 },
|
||||
rotation: { x: 4, y: 5, z: 6 },
|
||||
scale: { x: 7, y: 8, z: 9 },
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const testTTSPanel = () =>{
|
||||
dispatchAppUiState({
|
||||
type: APPUI_ACTION_TYPES.OPEN_DIALOGUE_TTS
|
||||
})
|
||||
};
|
||||
|
||||
const handleButtonCameraView = () => {
|
||||
editorEngine?.switchCameraView();
|
||||
};
|
||||
const testStylizeRequest = () => {
|
||||
// editorEngine?.testStylizeRequest();
|
||||
console.log('editorEnging does not have testStylizeRequest')
|
||||
};
|
||||
const handleButtonRender = () => {
|
||||
editorEngine?.generateVideo();
|
||||
};
|
||||
|
||||
const handleButtonTakeFrame = () => {
|
||||
// editorEngine?.take_timeline_cam_clip();
|
||||
console.log('editorEnging does not have take_timeline_cam_clip')
|
||||
};
|
||||
|
||||
const handleButtonSingleFrame = () => {
|
||||
editorEngine?.generateFrame();
|
||||
};
|
||||
const handleButtonPlayBack = () => {
|
||||
editorEngine?.startPlayback();
|
||||
};
|
||||
|
||||
const smallButtons = "text-xs p-2 h-6 ";
|
||||
const tertiaryColor = "bg-brand-tertiary hover:bg-brand-teriary-400 focus-visible:outline-brand-tertiary ";
|
||||
|
||||
return(
|
||||
<>
|
||||
<div className="flex gap-1">
|
||||
<Button variant="secondary" onClick={handleButtonCameraView} className={smallButtons}>
|
||||
Toggle Camera View
|
||||
</Button>
|
||||
<Button
|
||||
onClick={testObjectPanel}
|
||||
className={smallButtons}
|
||||
>
|
||||
Object Panel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={testTTSPanel}
|
||||
className={tertiaryColor + smallButtons}
|
||||
>
|
||||
TTS Panel
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
<Button onClick={handleButtonSingleFrame} className={smallButtons}>Render Single Frame</Button>
|
||||
<Button onClick={handleButtonTakeFrame} className={smallButtons}>Take Frame</Button>
|
||||
<Button onClick={handleButtonRender} className={smallButtons}>Render</Button>
|
||||
<Button
|
||||
onClick={testStylizeRequest}
|
||||
className={"bg-brand-tertiary hover:bg-brand-teriary-400 focus-visible:outline-brand-tertiary " +smallButtons}
|
||||
style={{ zIndex: 9001 }}
|
||||
>
|
||||
Test Stylize
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
import { useCallback, useContext, useEffect, useState } from "react";
|
||||
import { useSignalEffect } from "@preact/signals-react/runtime";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import {
|
||||
faVolume,
|
||||
faShuffle,
|
||||
faPlay,
|
||||
faBrainCircuit,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { AppUiContext } from "../../contexts/AppUiContext";
|
||||
import { APPUI_ACTION_TYPES } from "../../reducers";
|
||||
import {
|
||||
Button,
|
||||
H2,
|
||||
H5,
|
||||
Label,
|
||||
LoadingDotsTyping,
|
||||
ListSearchDropdown,
|
||||
TransitionDialogue,
|
||||
Textarea
|
||||
} from "~/components";
|
||||
import { ListTtsModels } from "./utilities";
|
||||
import { TtsModelListItem } from "./types";
|
||||
import { GenerateTtsAudio } from "./generateTts";
|
||||
import { addInferenceJob, inferenceJobs} from "../../store/inferenceJobs";
|
||||
import { JobState } from "~/hooks/useInferenceJobManager/useInferenceJobManager";
|
||||
|
||||
type TtsState = {
|
||||
voice: TtsModelListItem | undefined;
|
||||
text: string;
|
||||
hasEnqueued :boolean;
|
||||
inferenceToken?: string;
|
||||
inferenceJobType?: string;
|
||||
hasAudio: boolean;
|
||||
audioFile?: any;
|
||||
}
|
||||
|
||||
export const DialogueTTS = ()=>{
|
||||
|
||||
const [appUiState, dispatchAppUiState] = useContext(AppUiContext);
|
||||
const [ttsState, setTtsState] = useState<TtsState>({
|
||||
voice:undefined,
|
||||
text:"",
|
||||
hasEnqueued:false,
|
||||
hasAudio:false,
|
||||
});
|
||||
|
||||
useSignalEffect(()=>{
|
||||
console.log(inferenceJobs.value);
|
||||
if(ttsState.hasEnqueued && ttsState.inferenceToken){
|
||||
const found = inferenceJobs.value.find((job)=>job.job_id===ttsState.inferenceToken);
|
||||
console.log(`finding: ${ttsState.inferenceToken}`);
|
||||
console.log(found);
|
||||
if(found?.job_status === JobState.COMPLETE_SUCCESS){
|
||||
setTtsState((curr)=>({
|
||||
...curr,
|
||||
hasAudio: true,
|
||||
audioFile: found.result
|
||||
}))
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const [ttsModels, setTtsModels] = useState<Array<TtsModelListItem>>([]);
|
||||
|
||||
const listModels = useCallback(async () => {
|
||||
const ttsModelsLoaded = ttsModels.length > 0;
|
||||
if (ttsModelsLoaded) {
|
||||
return; // Already queried.
|
||||
}
|
||||
const models = await ListTtsModels();
|
||||
if (models) {
|
||||
setTtsModels(models);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
listModels();
|
||||
}, [listModels]);
|
||||
|
||||
useEffect(()=> {
|
||||
if(ttsState.hasEnqueued && ttsState.inferenceToken && ttsState.inferenceJobType){
|
||||
console.log(`tts has Enqueued`);
|
||||
addInferenceJob({
|
||||
version:1,
|
||||
job_id: ttsState.inferenceToken,
|
||||
job_type: ttsState.inferenceJobType,
|
||||
job_status: JobState.PENDING,
|
||||
})
|
||||
}
|
||||
},[ttsState]);
|
||||
|
||||
const requestTts = useCallback( ()=>{
|
||||
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).then(res=>{
|
||||
if(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 handleClose = ()=> {
|
||||
if(ttsState.hasAudio){
|
||||
setTtsState((curr)=>({
|
||||
...curr,
|
||||
hasEnqueued: false,
|
||||
inferenceToken: undefined,
|
||||
inferenceJobType: undefined,
|
||||
hasAudio: false,
|
||||
result: undefined,
|
||||
}));
|
||||
}
|
||||
dispatchAppUiState({
|
||||
type: APPUI_ACTION_TYPES.CLOSE_DIALOGUE_TTS
|
||||
})
|
||||
};
|
||||
|
||||
const handleTextInput = (
|
||||
e: React.ChangeEvent<HTMLTextAreaElement>
|
||||
)=>{
|
||||
setTtsState((curr)=>({
|
||||
...curr,
|
||||
text: e.target.value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleOnSelect = (val:string)=>{
|
||||
const voiceModel = ttsModels.find((item)=>{
|
||||
if (item.title === val) return item
|
||||
})
|
||||
// console.log(val);
|
||||
// console.log(voiceModel);
|
||||
setTtsState((curr)=>({
|
||||
...curr,
|
||||
voice: voiceModel,
|
||||
}));
|
||||
}
|
||||
|
||||
return(
|
||||
<TransitionDialogue
|
||||
title={
|
||||
<>
|
||||
<FontAwesomeIcon icon={faVolume} className="pr-2"/>
|
||||
Generate TTS
|
||||
</>
|
||||
}
|
||||
className="max-w-xl"
|
||||
isOpen={appUiState.diagloueTts.isOpen}
|
||||
onClose={handleClose}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<Label className="mb-1">Select a Voice</Label>
|
||||
<ListSearchDropdown
|
||||
list={ttsModels}
|
||||
listDisplayKey="title"
|
||||
onSelect={handleOnSelect}
|
||||
/>
|
||||
<div className="flex w-full justify-between mt-4">
|
||||
<Label>What would you like to say?</Label>
|
||||
<div className="flex gap-2 items-center">
|
||||
<FontAwesomeIcon className="text-brand-primary" icon={faShuffle}/>
|
||||
<H5 className="text-brand-primary">Randomized Text</H5>
|
||||
</div>
|
||||
</div>
|
||||
<Textarea
|
||||
placeholder="Enter what you want the voice to say here."
|
||||
value={ttsState.text}
|
||||
onChange={handleTextInput}
|
||||
/>
|
||||
<div className="mt-6 flex gap-2">
|
||||
<div className="w-full h-12">
|
||||
{!ttsState.hasAudio && !ttsState.hasEnqueued &&
|
||||
<Button
|
||||
className="w-36 h-full text-xl "
|
||||
variant={ttsState.hasAudio ? "secondary" : "primary" }
|
||||
disabled={ttsState.text === ""}
|
||||
icon={faBrainCircuit}
|
||||
onClick={requestTts}
|
||||
>
|
||||
Generate
|
||||
</Button>
|
||||
}
|
||||
{!ttsState.hasAudio && ttsState.hasEnqueued &&
|
||||
<LoadingDotsTyping className="bg-brand-secondary-500 rounded-lg"/>
|
||||
}
|
||||
{ ttsState.hasAudio &&
|
||||
<div className="bg-success w-full h-full rounded-lg flex items-center justify-center">
|
||||
<H2>Success!! Please Check "My Audio" in the Audio Panel.</H2>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="mt-6 flex justify-between gap-2">
|
||||
{ ttsState.hasEnqueued &&
|
||||
<Button
|
||||
type="button"
|
||||
disabled={!ttsState.hasAudio}
|
||||
onClick={requestTts}
|
||||
icon={faBrainCircuit}
|
||||
>
|
||||
Generate Another
|
||||
</Button>
|
||||
}
|
||||
<span />
|
||||
<Button
|
||||
type="button"
|
||||
onClick={handleClose}
|
||||
variant="secondary"
|
||||
>
|
||||
{ttsState.hasEnqueued ? 'Close':'Cancel'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</TransitionDialogue>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
import { inferTts } from "~/api";
|
||||
import {
|
||||
GenerateTtsAudioRequest,
|
||||
GenerateTtsAudioSuccess,
|
||||
GenerateTtsAudioError,
|
||||
GenerateTtsAudioResponse,
|
||||
GenerateTtsAudioErrorType,
|
||||
EndpointResponse
|
||||
} from './types';
|
||||
|
||||
import {
|
||||
maybeMapError
|
||||
} from './utilities';
|
||||
|
||||
export function GenerateTtsAudioIsError(response: GenerateTtsAudioResponse): response is GenerateTtsAudioError {
|
||||
return !('inference_job_token' in response);
|
||||
}
|
||||
|
||||
export async function GenerateTtsAudio(request: GenerateTtsAudioRequest) : Promise<GenerateTtsAudioResponse | undefined>
|
||||
{
|
||||
return await fetch(inferTts, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(request),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (!('inference_job_token' in res)) {
|
||||
return { error: GenerateTtsAudioErrorType.UnknownError };
|
||||
}
|
||||
const ret:GenerateTtsAudioSuccess = {
|
||||
success: true,
|
||||
inference_job_token: res.inference_job_token,
|
||||
inference_job_token_type: res.inference_job_token_type,
|
||||
}
|
||||
return ret;
|
||||
})
|
||||
.catch(e => {
|
||||
let maybeError = maybeMapError(e);
|
||||
if (maybeError !== undefined) {
|
||||
return { error: maybeError };
|
||||
}
|
||||
return { error: GenerateTtsAudioErrorType.UnknownError };
|
||||
});
|
||||
|
||||
// return await fetch(inferTts, {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// 'Accept': 'application/json',
|
||||
// 'Content-Type': 'application/json',
|
||||
// },
|
||||
// credentials: 'include',
|
||||
// body: JSON.stringify(request),
|
||||
// })
|
||||
// .then(res => res.json().then(data => ({ status: <number>res.status, body: <EndpointResponse>data })))
|
||||
// .then((fullResponse) => {
|
||||
// let maybeError = maybeMapError(fullResponse);
|
||||
// if (maybeError !== undefined) {
|
||||
// return { error: maybeError };
|
||||
// }
|
||||
// if (!fullResponse.body || !fullResponse.body.success) {
|
||||
// return { error: GenerateTtsAudioErrorType.UnknownError };
|
||||
// }
|
||||
|
||||
// if (!('inference_job_token' in fullResponse.body)) {
|
||||
// return { error: GenerateTtsAudioErrorType.UnknownError };
|
||||
// } else {
|
||||
// return <GenerateTtsAudioSuccess> {
|
||||
// inference_job_token: fullResponse.body.inference_job_token,
|
||||
// inference_job_token_type: fullResponse.body.inference_job_token_type,
|
||||
// };
|
||||
// }
|
||||
// })
|
||||
// .catch(e => {
|
||||
// let maybeError = maybeMapError(e);
|
||||
// if (maybeError !== undefined) {
|
||||
// return { error: maybeError };
|
||||
// }
|
||||
// return { error: GenerateTtsAudioErrorType.UnknownError };
|
||||
// });
|
||||
};
|
||||
Whitespace-only changes.
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Types for Listing TTS Models
|
||||
*/
|
||||
|
||||
export interface TtsModelListResponsePayload {
|
||||
success: boolean,
|
||||
models: Array<TtsModelListItem>,
|
||||
}
|
||||
|
||||
export interface UserRatings {
|
||||
positive_count: number,
|
||||
negative_count: number,
|
||||
// Total count does not take into account "neutral" ratings.
|
||||
total_count: number,
|
||||
}
|
||||
|
||||
export interface TtsModelListItem {
|
||||
model_token: string,
|
||||
tts_model_type: string,
|
||||
creator_user_token: string,
|
||||
creator_username: string,
|
||||
creator_display_name: string,
|
||||
creator_gravatar_hash: string,
|
||||
ietf_language_tag: string,
|
||||
ietf_primary_language_subtag: string,
|
||||
updatable_slug: string,
|
||||
title: string,
|
||||
is_front_page_featured: boolean,
|
||||
is_twitch_featured: boolean,
|
||||
user_ratings: UserRatings,
|
||||
category_tokens: string[],
|
||||
created_at: string,
|
||||
updated_at: string,
|
||||
}
|
||||
|
||||
/*
|
||||
* Types for Making a TTS Generation Request
|
||||
*/
|
||||
export interface GenerateTtsAudioRequest {
|
||||
uuid_idempotency_token: string,
|
||||
tts_model_token: string,
|
||||
inference_text: string,
|
||||
// TODO(2022-03): TEMPORARY
|
||||
is_storyteller_demo?: boolean,
|
||||
}
|
||||
|
||||
export interface GenerateTtsAudioSuccess {
|
||||
success: boolean,
|
||||
inference_job_token: string,
|
||||
// Which queue to poll
|
||||
inference_job_token_type?: string,
|
||||
}
|
||||
|
||||
export interface GenerateTtsAudioError {
|
||||
error: GenerateTtsAudioErrorType,
|
||||
}
|
||||
|
||||
export enum GenerateTtsAudioErrorType {
|
||||
BadRequest,
|
||||
NotFound,
|
||||
TooManyRequests,
|
||||
ServerError,
|
||||
UnknownError,
|
||||
}
|
||||
export type GenerateTtsAudioResponse = GenerateTtsAudioSuccess | GenerateTtsAudioError;
|
||||
|
||||
|
||||
|
||||
export interface EndpointSuccessResponse {
|
||||
success: boolean,
|
||||
inference_job_token: string,
|
||||
inference_job_token_type?: string,
|
||||
}
|
||||
|
||||
export interface EndpointErrorResponse {
|
||||
success: boolean,
|
||||
}
|
||||
|
||||
export type EndpointResponse = EndpointSuccessResponse | EndpointErrorResponse;
|
||||
|
||||
export interface StatusLike {
|
||||
status: number,
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { listTts } from '~/api';
|
||||
import {
|
||||
TtsModelListItem,
|
||||
TtsModelListResponsePayload,
|
||||
GenerateTtsAudioErrorType,
|
||||
StatusLike
|
||||
} from './types';
|
||||
|
||||
export async function ListTtsModels() : Promise<Array<TtsModelListItem>| undefined> {
|
||||
return await fetch(listTts, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
const response : TtsModelListResponsePayload = res;
|
||||
if (!response.success) {
|
||||
return;
|
||||
}
|
||||
return response?.models;
|
||||
})
|
||||
.catch(e => {
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
export function maybeMapError(statuslike: StatusLike) : GenerateTtsAudioErrorType | undefined {
|
||||
switch (statuslike.status) {
|
||||
case 400:
|
||||
return GenerateTtsAudioErrorType.BadRequest;
|
||||
case 404:
|
||||
return GenerateTtsAudioErrorType.NotFound;
|
||||
case 429:
|
||||
return GenerateTtsAudioErrorType.TooManyRequests;
|
||||
case 500:
|
||||
return GenerateTtsAudioErrorType.ServerError;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
import { useCallback, useContext, useEffect, useState } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { useSignals, useSignalEffect } from "@preact/signals-react/runtime";
|
||||
import { faCirclePlus, faArrowsRotate } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
import { AppUiContext } from "~/pages/PageEnigma/contexts/AppUiContext";
|
||||
import { AuthenticationContext } from "~/contexts/Authentication";
|
||||
import { APPUI_ACTION_TYPES } from "~/pages/PageEnigma/reducers";
|
||||
import { audioFilter, audioItems } from "~/pages/PageEnigma/store";
|
||||
import { AssetFilterOption } from "~/pages/PageEnigma/models";
|
||||
|
||||
import { Button, ButtonIcon } from "~/components";
|
||||
|
||||
import { MediaItem, AssetType } from "~/pages/PageEnigma/models";
|
||||
import { ItemElements } from "~/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ItemElements";
|
||||
import { audioItemsFromServer } from "~/pages/PageEnigma/store/mediaFromServer";
|
||||
import { ListAudioByUser } from "./listAudioByUser";
|
||||
import { inferenceJobs } from "~/pages/PageEnigma/store/inferenceJobs";
|
||||
import { JobState } from "~/hooks/useInferenceJobManager/useInferenceJobManager";
|
||||
|
||||
export const AudioTabComponent = () => {
|
||||
useSignals();
|
||||
const [ state, setState ] = useState({
|
||||
firstLoad: false,
|
||||
fetchingUserAudio: false,
|
||||
});
|
||||
const { authState } = useContext(AuthenticationContext);
|
||||
const [, dispatchAppUiState] = useContext(AppUiContext);
|
||||
const handleOpenTtsDialogue = ()=>{
|
||||
dispatchAppUiState({
|
||||
type: APPUI_ACTION_TYPES.OPEN_DIALOGUE_TTS
|
||||
})
|
||||
}
|
||||
const allAudioItems = [...audioItems.value, ...audioItemsFromServer.value];
|
||||
|
||||
const handleListAudioByUser = useCallback((username:string)=>{
|
||||
setState((curr)=>({...curr, fetchingUserAudio:true}));
|
||||
ListAudioByUser(username).then((res:any[])=>{
|
||||
console.log(res)
|
||||
setState((curr)=>({...curr, fetchingUserAudio:false}));
|
||||
audioItemsFromServer.value = res.map(item=>{
|
||||
const morphedItem:MediaItem = {
|
||||
version: 1,
|
||||
type: AssetType.AUDIO,
|
||||
media_id: item.token,
|
||||
object_uuid: item.token,
|
||||
name: item.maybe_title || item.origin.maybe_model.title,
|
||||
length: 25,
|
||||
thumbnail: "resources/placeholders/audio_placeholder.png",
|
||||
isMine: true,
|
||||
// isBookmarked?: boolean;
|
||||
}
|
||||
return morphedItem;
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(()=>{
|
||||
if ( authState.userInfo ){
|
||||
if(state.firstLoad === false && audioItemsFromServer.value.length === 0){
|
||||
handleListAudioByUser(authState.userInfo.username);
|
||||
}
|
||||
}
|
||||
}, [authState, state, handleListAudioByUser]);
|
||||
|
||||
useSignalEffect(()=>{
|
||||
// flagging first load is done
|
||||
if (state.firstLoad === false && audioItemsFromServer.value.length > 0){
|
||||
setState(curr=>({...curr, firstLoad: true}));
|
||||
}
|
||||
|
||||
// when inference changes, check if there's a new audio to refresh for
|
||||
if (inferenceJobs.value.length > 0 && authState.userInfo){
|
||||
const found = inferenceJobs.value.find((job)=>{
|
||||
if(job.job_status === JobState.COMPLETE_SUCCESS){
|
||||
console.log(job);
|
||||
|
||||
const foundItemOfJob = audioItemsFromServer.value.find((item)=>{
|
||||
return item.media_id === job.result.entity_token
|
||||
});
|
||||
|
||||
return foundItemOfJob !== undefined;
|
||||
}
|
||||
});
|
||||
if(found === undefined){
|
||||
handleListAudioByUser(authState.userInfo.username);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full overflow-x-auto">
|
||||
<div className="mb-4 mt-4 flex justify-start items-center gap-2 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.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.some((item) => item.isBookmarked)}
|
||||
>
|
||||
Bookmarked
|
||||
</button>
|
||||
<ButtonIcon
|
||||
className={twMerge(
|
||||
"absolute right-0 mr-4",
|
||||
// state.fetchingUserAudio ? "animate-spin" : "",
|
||||
)}
|
||||
icon={faArrowsRotate}
|
||||
onClick={()=>{
|
||||
if(authState.userInfo?.username){
|
||||
handleListAudioByUser(authState.userInfo?.username);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full px-4">
|
||||
<Button
|
||||
icon={faCirclePlus}
|
||||
variant="action"
|
||||
className="w-full py-3 text-sm font-medium"
|
||||
onClick={handleOpenTtsDialogue}
|
||||
>
|
||||
Generate Audio
|
||||
</Button>
|
||||
</div>
|
||||
<div className="h-full w-full overflow-y-auto px-4 pt-4">
|
||||
<ItemElements
|
||||
items={allAudioItems}
|
||||
assetFilter={audioFilter.value}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { AudioTabComponent } from "./AudioTabComponent";
|
||||
@@ -0,0 +1,55 @@
|
||||
import { listMediaByUser } from "~/api";
|
||||
import {
|
||||
MediaFileType,
|
||||
MediaFileClass,
|
||||
MediaFileSubtype,
|
||||
UserDetailsLight,
|
||||
WeightCategory,
|
||||
WeightType
|
||||
} from "./types";
|
||||
|
||||
|
||||
export interface MediaFile {
|
||||
token: string;
|
||||
media_type: MediaFileType;
|
||||
media_class: MediaFileClass | null;
|
||||
maybe_media_subtype: MediaFileSubtype | null;
|
||||
public_bucket_path: string;
|
||||
maybe_engine_extension: string | null;
|
||||
maybe_batch_token: string;
|
||||
maybe_title: string | null;
|
||||
maybe_original_filename: string | null;
|
||||
maybe_creator_user: UserDetailsLight | null;
|
||||
maybe_prompt_token: string | null;
|
||||
creator_set_visibility: string;
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
maybe_model_weight_info: {
|
||||
title: string;
|
||||
weight_token: string;
|
||||
weight_category: WeightCategory;
|
||||
weight_type: WeightType;
|
||||
maybe_weight_creator: UserDetailsLight;
|
||||
maybe_cover_image_public_bucket_path: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const ListAudioByUser = async(username:string) => {
|
||||
return await fetch(listMediaByUser(username),{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
},
|
||||
credentials: 'include'
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if(res.success && res.results){
|
||||
return res.results.filter((item:MediaFile)=>item['media_type']==='audio');
|
||||
}else{
|
||||
Promise.reject();
|
||||
}
|
||||
})
|
||||
.catch(e => ({ success : false }));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
export enum WeightCategory {
|
||||
TTS = "text_to_speech",
|
||||
VOCODER = "vocoder",
|
||||
VC = "voice_conversion",
|
||||
ZS = "zero_shot",
|
||||
SD = "image_generation",
|
||||
WF = "workflow_config",
|
||||
}
|
||||
|
||||
|
||||
export enum MediaFileClass {
|
||||
/// Unknown (default value)
|
||||
/// This will be present until we migrate all old files.
|
||||
Unknown = "unknown",
|
||||
|
||||
/// Audio files: wav, mp3, etc.
|
||||
Audio = "audio",
|
||||
|
||||
/// Image files: png, jpeg, etc.
|
||||
Image = "image",
|
||||
|
||||
/// Video files: mp4, etc.
|
||||
Video = "video",
|
||||
|
||||
/// Engine "animations"
|
||||
Animation = "animation",
|
||||
|
||||
/// Engine "characters"
|
||||
Character = "character",
|
||||
|
||||
/// Engine "prop" items
|
||||
Prop = "prop",
|
||||
|
||||
/// Engine scenes (internal and external scenes)
|
||||
Scene = "scene",
|
||||
}
|
||||
|
||||
|
||||
export enum MediaFileType {
|
||||
Audio = "audio",
|
||||
Video = "video",
|
||||
Image = "image",
|
||||
|
||||
// BVH is a very popular file format for motion capture.
|
||||
// It is compatible with our engine.
|
||||
BVH = "bvh",
|
||||
|
||||
// GLTF is the text-based format of GLB. Unforunately it requires a
|
||||
// second file to include the visual data, so we strongly discourage
|
||||
// the use of this format in favor of GLB.
|
||||
GLTF = "gltf",
|
||||
|
||||
// GLB is the format we convert FBX files into. It's a very popular
|
||||
// open source 3D file format.
|
||||
GLB = "glb",
|
||||
|
||||
// FBX isn't supported by Storyteller Engine, but we can get
|
||||
// uploads in this format. It's a very popular 3D graphics format.
|
||||
FBX = "fbx",
|
||||
|
||||
// Full Storyteller Engine scenes (RON = Rusty Object Notation)
|
||||
// This is a temporary format that will eventually go away.
|
||||
SceneRon = "scene_ron",
|
||||
|
||||
None = "none",
|
||||
}
|
||||
|
||||
|
||||
export enum MediaFileSubtype {
|
||||
/// Animation file from Mixamo
|
||||
/// Primarily used for FBX and GLB.
|
||||
Mixamo = "mixamo",
|
||||
|
||||
/// Animation file from MocapNet
|
||||
/// Primarily used for BVH.
|
||||
MocapNet = "mocap_net",
|
||||
|
||||
/// Generic animation case
|
||||
/// Used for BVH files, but can also pertain to animation-only files of other types.
|
||||
AnimationOnly = "animation_only",
|
||||
|
||||
// TODO(bt,2024-03-08): Migrate records and code, then remove
|
||||
/// DEPRECATED: Use `SceneImport` instead.
|
||||
Scene = "scene",
|
||||
|
||||
/// Generic 3D scene file.
|
||||
/// Can pertain to glTF, glB, FBX, etc.
|
||||
SceneImport = "scene_import",
|
||||
|
||||
/// Native Storyteller scene format.
|
||||
/// Typically stored in a `.scn.ron` file.
|
||||
StorytellerScene = "storyteller_scene",
|
||||
}
|
||||
|
||||
/// A common type returned by several endpoints.
|
||||
/// Basic information to display a user and their avatar.
|
||||
export interface UserDetailsLight {
|
||||
user_token: string,
|
||||
/// Username (lowercase)
|
||||
username: string,
|
||||
/// Username with user-specified capitalization
|
||||
display_name: string,
|
||||
gravatar_hash: string,
|
||||
default_avatar: DefaultAvatarInfo,
|
||||
|
||||
}
|
||||
|
||||
export interface DefaultAvatarInfo {
|
||||
image_index: number,
|
||||
color_index: number,
|
||||
}
|
||||
|
||||
|
||||
export enum WeightType {
|
||||
TT2 = "tt2",
|
||||
HIFIGAN_TT2 = "hifigan_tt2",
|
||||
RVCv2 = "rvc_v2",
|
||||
SD_15 = "sd_1.5",
|
||||
SDXL = "sdxl",
|
||||
SVC = "so_vits_svc",
|
||||
LORA = "loRA",
|
||||
VALL_E = "vall_e",
|
||||
NONE = "none",
|
||||
}
|
||||
@@ -1,69 +1 @@
|
||||
import { audioFilter, audioItems } from "~/pages/PageEnigma/store";
|
||||
import { AssetFilterOption } from "~/pages/PageEnigma/models";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { ItemElements } from "~/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ItemElements";
|
||||
import { Button } from "~/components";
|
||||
import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export const AudioTab = () => {
|
||||
useSignals();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full overflow-x-auto">
|
||||
<div className="mb-4 mt-4 flex justify-start gap-2 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={!audioItems.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={!audioItems.value.some((item) => item.isBookmarked)}
|
||||
>
|
||||
Bookmarked
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full px-4 pb-4">
|
||||
<Button
|
||||
icon={faCirclePlus}
|
||||
variant="action"
|
||||
className="w-full py-3 text-sm font-medium"
|
||||
>
|
||||
Generate Audio
|
||||
</Button>
|
||||
</div>
|
||||
<div className="h-full w-full overflow-y-auto px-4">
|
||||
<ItemElements
|
||||
items={audioItems.value}
|
||||
assetFilter={audioFilter.value}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export { AudioTabComponent as AudioTab } from "../AudioTabComponent";
|
||||
@@ -570,4 +570,4 @@ export class TimeLine {
|
||||
currentTime: this.current_time,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,3 +2,4 @@ export * from "./assets";
|
||||
export * from "./message";
|
||||
export * from "./sidePanel";
|
||||
export * from "./track";
|
||||
export * from "./inferenceJobs";
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface InferenceJob {
|
||||
version: number;
|
||||
job_id: string;
|
||||
job_type: string;
|
||||
job_status: string;
|
||||
result?: any;
|
||||
}
|
||||
@@ -2,8 +2,24 @@ import { State, Action, ACTION_TYPES } from "./types";
|
||||
|
||||
export function reducer(state: State, action: Action): State {
|
||||
switch (action.type) {
|
||||
case ACTION_TYPES.OPEN_DIALOGUE_TTS:{
|
||||
return{
|
||||
...state,
|
||||
diagloueTts:{
|
||||
isOpen: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
case ACTION_TYPES.CLOSE_DIALOGUE_TTS:{
|
||||
return{
|
||||
...state,
|
||||
diagloueTts:{
|
||||
isOpen: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
case ACTION_TYPES.SHOW_CONTROLPANELS_SCENEOBJECT: {
|
||||
console.log("pay", action.payload);
|
||||
// console.log("pay", action.payload);
|
||||
return {
|
||||
...state,
|
||||
controlPanel: {
|
||||
|
||||
@@ -34,6 +34,9 @@ export type State = {
|
||||
useFakeTimer?: number;
|
||||
};
|
||||
controlPanel: ControlPanel;
|
||||
diagloueTts: {
|
||||
isOpen: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export enum ACTION_TYPES {
|
||||
@@ -47,13 +50,14 @@ export enum ACTION_TYPES {
|
||||
SHOW_CONTROLPANELS_SCENEOBJECT = "show_controlpanels_sceneobject",
|
||||
UPDATE_CONTROLPANELS_SCENEOBJECT = "update_controlpanels_sceneobject",
|
||||
HIDE_CONTROLPANELS_SCENEOBJECT = "hide_controlpanels_sceneobject",
|
||||
OPEN_DIALOGUE_TTS = "open_dialogue_tts",
|
||||
CLOSE_DIALOGUE_TTS = "close_dialogue_tts",
|
||||
}
|
||||
|
||||
export type Action =
|
||||
| {
|
||||
// Not sure why we need this but type script won't stop complaning fix wil.
|
||||
type: ACTION_TYPES.HIDE_CONTROLPANELS_SCENEOBJECT;
|
||||
}
|
||||
| { type: ACTION_TYPES.OPEN_DIALOGUE_TTS }
|
||||
| { type: ACTION_TYPES.CLOSE_DIALOGUE_TTS }
|
||||
| { type: ACTION_TYPES.HIDE_CONTROLPANELS_SCENEOBJECT }
|
||||
| {
|
||||
type: ACTION_TYPES.UPDATE_CONTROLPANELS_SCENEOBJECT;
|
||||
payload: SceneObject;
|
||||
|
||||
@@ -27,4 +27,5 @@ export const initialStateValues: State = {
|
||||
},
|
||||
},
|
||||
},
|
||||
diagloueTts:{isOpen:false}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
import { signal } from "@preact/signals-core";
|
||||
|
||||
import { InferenceJob } from "../models";
|
||||
|
||||
export const inferenceJobs = signal<InferenceJob[]>([]);
|
||||
|
||||
export function addInferenceJob(
|
||||
newJob: InferenceJob){
|
||||
const existingInferenceJobs = inferenceJobs.value;
|
||||
const jobExist = existingInferenceJobs.find(job=>job.job_id==newJob.job_id)
|
||||
if(!jobExist){
|
||||
inferenceJobs.value = [...existingInferenceJobs, newJob]
|
||||
};
|
||||
// else do nothing
|
||||
}
|
||||
|
||||
export function updateInferenceJob(
|
||||
updatableJob: InferenceJob){
|
||||
const existingInferenceJobs = inferenceJobs.value;
|
||||
existingInferenceJobs.forEach(job=>{
|
||||
if(job.job_id === updatableJob.job_id){
|
||||
job.job_status = updatableJob.job_status
|
||||
}
|
||||
if(updatableJob.result){
|
||||
job.result = updatableJob.result;
|
||||
}
|
||||
})
|
||||
inferenceJobs.value = [...existingInferenceJobs];
|
||||
}
|
||||
|
||||
export function deleteInferenceJob(
|
||||
deletableJob: InferenceJob){
|
||||
console.log('delete')
|
||||
let newList:InferenceJob[] = []
|
||||
inferenceJobs.value.forEach(job=>{
|
||||
if(job.job_id !== deletableJob.job_id) {
|
||||
newList.push(job);
|
||||
} else {
|
||||
// do nothing
|
||||
}
|
||||
});
|
||||
console.log(newList);
|
||||
inferenceJobs.value = [...newList];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { signal } from "@preact/signals-core";
|
||||
import { MediaItem } from "~/pages/PageEnigma/models";
|
||||
export const audioItemsFromServer = signal<MediaItem[]>([]);
|
||||
@@ -3,6 +3,8 @@ import {
|
||||
Button,
|
||||
ButtonLink,
|
||||
H1,H2,H3,H4,H5,H6,P,Label,
|
||||
ListDropdown,
|
||||
ListSearchDropdown,
|
||||
LoadingBar,
|
||||
LoadingDotsTyping,
|
||||
LoadingDotsBricks,
|
||||
@@ -13,6 +15,15 @@ export default function ComponentLibrary () {
|
||||
const [progress, setProgress] = useState(0);
|
||||
const loopRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
const testdata = [
|
||||
{ name: 'Wade Cooper' },
|
||||
{ name: 'Arlene Mccoy' },
|
||||
{ name: 'Devon Webb' },
|
||||
{ name: 'Tom Cook' },
|
||||
{ name: 'Tanya Fox' },
|
||||
{ name: 'Hellen Schmidt' },
|
||||
]
|
||||
|
||||
const pushProgressBar: ()=>void = useCallback(()=>{
|
||||
loopRef.current = setInterval(function timer(){
|
||||
setProgress((curr)=>{
|
||||
@@ -72,6 +83,12 @@ export default function ComponentLibrary () {
|
||||
<ButtonLink to="/">Back to /</ButtonLink>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2 mb-4 max-w-2xl">
|
||||
<H4>List Dropdowns</H4>
|
||||
<ListDropdown list={testdata}/>
|
||||
<ListSearchDropdown list={testdata}/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2 mb-4">
|
||||
<H4>Loading Dots</H4>
|
||||
<div className="flex gap-2">
|
||||
@@ -84,7 +101,7 @@ export default function ComponentLibrary () {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2 mb-4">
|
||||
<div className="flex flex-col gap-2 mb-4 max-w-2xl">
|
||||
<H4>Loading Bar</H4>
|
||||
<div className="flex gap-2 items-center">
|
||||
<LoadingBar
|
||||
|
||||
Generated
+111
-119
@@ -838,9 +838,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
|
||||
"integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
|
||||
"integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -5264,16 +5264,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.18.3",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz",
|
||||
"integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==",
|
||||
"version": "4.19.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
|
||||
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.2",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.5.0",
|
||||
"cookie": "0.6.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
@@ -5304,14 +5304,6 @@
|
||||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/cookie": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
@@ -9858,9 +9850,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.37",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.37.tgz",
|
||||
"integrity": "sha512-7iB/v/r7Woof0glKLH8b1SPHrsX7uhdO+Geb41QpF/+mWZHU3uxxSlN+UXGVit1PawOYDToO+AbZzhBzWRDwbQ==",
|
||||
"version": "8.4.38",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
|
||||
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
|
||||
"devOptional": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -11888,9 +11880,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tar": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",
|
||||
"integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==",
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
|
||||
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chownr": "^2.0.0",
|
||||
@@ -12708,14 +12700,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.3.tgz",
|
||||
"integrity": "sha512-UfmUD36DKkqhi/F75RrxvPpry+9+tTkrXfMNZD+SboZqBCMsxKtO52XeGzzuh7ioz+Eo/SYDBbdb0Z7vgcDJew==",
|
||||
"version": "5.2.8",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz",
|
||||
"integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.19.3",
|
||||
"postcss": "^8.4.35",
|
||||
"rollup": "^4.2.0"
|
||||
"esbuild": "^0.20.1",
|
||||
"postcss": "^8.4.38",
|
||||
"rollup": "^4.13.0"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
@@ -12804,9 +12796,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/android-arm": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
|
||||
"integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
|
||||
"integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -12820,9 +12812,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
|
||||
"integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -12836,9 +12828,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/android-x64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -12852,9 +12844,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
|
||||
"integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -12868,9 +12860,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -12884,9 +12876,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
|
||||
"integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -12900,9 +12892,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -12916,9 +12908,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
|
||||
"integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
|
||||
"integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -12932,9 +12924,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
|
||||
"integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -12948,9 +12940,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
|
||||
"integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
|
||||
"integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -12964,9 +12956,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
|
||||
"integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
|
||||
"integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -12980,9 +12972,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
|
||||
"integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
|
||||
"integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
@@ -12996,9 +12988,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
|
||||
"integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
|
||||
"integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -13012,9 +13004,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
|
||||
"integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
|
||||
"integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -13028,9 +13020,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
|
||||
"integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
|
||||
"integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -13044,9 +13036,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -13060,9 +13052,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -13076,9 +13068,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -13092,9 +13084,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -13108,9 +13100,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
|
||||
"integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -13124,9 +13116,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
|
||||
"integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
|
||||
"integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -13140,9 +13132,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -13156,9 +13148,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/esbuild": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
|
||||
"integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
|
||||
"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
@@ -13168,29 +13160,29 @@
|
||||
"node": ">=12"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.19.12",
|
||||
"@esbuild/android-arm": "0.19.12",
|
||||
"@esbuild/android-arm64": "0.19.12",
|
||||
"@esbuild/android-x64": "0.19.12",
|
||||
"@esbuild/darwin-arm64": "0.19.12",
|
||||
"@esbuild/darwin-x64": "0.19.12",
|
||||
"@esbuild/freebsd-arm64": "0.19.12",
|
||||
"@esbuild/freebsd-x64": "0.19.12",
|
||||
"@esbuild/linux-arm": "0.19.12",
|
||||
"@esbuild/linux-arm64": "0.19.12",
|
||||
"@esbuild/linux-ia32": "0.19.12",
|
||||
"@esbuild/linux-loong64": "0.19.12",
|
||||
"@esbuild/linux-mips64el": "0.19.12",
|
||||
"@esbuild/linux-ppc64": "0.19.12",
|
||||
"@esbuild/linux-riscv64": "0.19.12",
|
||||
"@esbuild/linux-s390x": "0.19.12",
|
||||
"@esbuild/linux-x64": "0.19.12",
|
||||
"@esbuild/netbsd-x64": "0.19.12",
|
||||
"@esbuild/openbsd-x64": "0.19.12",
|
||||
"@esbuild/sunos-x64": "0.19.12",
|
||||
"@esbuild/win32-arm64": "0.19.12",
|
||||
"@esbuild/win32-ia32": "0.19.12",
|
||||
"@esbuild/win32-x64": "0.19.12"
|
||||
"@esbuild/aix-ppc64": "0.20.2",
|
||||
"@esbuild/android-arm": "0.20.2",
|
||||
"@esbuild/android-arm64": "0.20.2",
|
||||
"@esbuild/android-x64": "0.20.2",
|
||||
"@esbuild/darwin-arm64": "0.20.2",
|
||||
"@esbuild/darwin-x64": "0.20.2",
|
||||
"@esbuild/freebsd-arm64": "0.20.2",
|
||||
"@esbuild/freebsd-x64": "0.20.2",
|
||||
"@esbuild/linux-arm": "0.20.2",
|
||||
"@esbuild/linux-arm64": "0.20.2",
|
||||
"@esbuild/linux-ia32": "0.20.2",
|
||||
"@esbuild/linux-loong64": "0.20.2",
|
||||
"@esbuild/linux-mips64el": "0.20.2",
|
||||
"@esbuild/linux-ppc64": "0.20.2",
|
||||
"@esbuild/linux-riscv64": "0.20.2",
|
||||
"@esbuild/linux-s390x": "0.20.2",
|
||||
"@esbuild/linux-x64": "0.20.2",
|
||||
"@esbuild/netbsd-x64": "0.20.2",
|
||||
"@esbuild/openbsd-x64": "0.20.2",
|
||||
"@esbuild/sunos-x64": "0.20.2",
|
||||
"@esbuild/win32-arm64": "0.20.2",
|
||||
"@esbuild/win32-ia32": "0.20.2",
|
||||
"@esbuild/win32-x64": "0.20.2"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/rollup": {
|
||||
|
||||
Reference in new issue
Block a user