mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-31 07:45:55 -04:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ebd202928 | ||
|
|
df7b45f62d | ||
|
|
6190416514 | ||
|
|
5fe4520f62 | ||
|
|
05fbd2ff03 | ||
|
|
10caf36d2f |
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"default":{"identifier":"default","description":"enables the default permissions","local":true,"windows":["main"],"permissions":["core:default","log:default","upload:allow-download","upload:default",{"identifier":"http:default","allow":[{"url":"https://*.storyteller.ai"},{"url":"https://*.fakeyou.com"}],"deny":[{"url":"https://private.tauri.app"}]}]}}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -45,10 +45,6 @@ export const TopBar = ({ pageName, appTabIdSignal, setAppTabId }: Props) => {
|
||||
const [activeLibraryTab, setActiveLibraryTab] = useState("my-media");
|
||||
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
|
||||
|
||||
const handleClick = () => {
|
||||
window.location.href = "https://storyteller-2d.netlify.app/";
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className="fixed left-0 top-0 z-[60] w-full border-b border-white/5 bg-ui-background">
|
||||
@@ -76,16 +72,6 @@ export const TopBar = ({ pageName, appTabIdSignal, setAppTabId }: Props) => {
|
||||
disabled={false}
|
||||
onTabChange={(tabId) => setAppTabId(tabId)}
|
||||
/>
|
||||
{/* <Button
|
||||
variant="secondary"
|
||||
icon={faChevronRight}
|
||||
iconClassName="text-xs"
|
||||
iconFlip={true}
|
||||
onClick={handleClick}
|
||||
className="bg-transparent p-0 text-sm text-white/80 hover:bg-transparent hover:text-white hover:underline hover:underline-offset-2"
|
||||
>
|
||||
Go to 2D Canvas
|
||||
</Button> */}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-center gap-2 font-medium">
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import { MouseEventHandler, useState, useEffect } from "react";
|
||||
import { Transition } from "@headlessui/react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { uiAccess } from "./signals/uiAccess";
|
||||
import { dispatchUiEvents } from "./signals/uiEvents";
|
||||
|
||||
import { ToolbarNode } from "./components/features/ToolbarNode";
|
||||
import { ToolbarNodeButtonNames } from "./components/features/ToolbarNode/enums";
|
||||
import { transitionTimingStyles } from "./components/styles";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
|
||||
export const ContextualToolbarNode = () => {
|
||||
const [winSize, setWinSize] = useState<{ width: number; height: number }>({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
const { isShowing, position, ...rest } = uiAccess.toolbarNode.signal.value;
|
||||
const buttonsProps = Object.values(ToolbarNodeButtonNames).reduce(
|
||||
(acc, buttonName) => {
|
||||
acc[buttonName] = {
|
||||
onClick: dispatchUiEvents.toolbarNode[buttonName],
|
||||
disabled:
|
||||
uiAccess.toolbarNode.signal.value.buttonStates[buttonName].disabled,
|
||||
hidden:
|
||||
uiAccess.toolbarNode.signal.value.buttonStates[buttonName].hidden,
|
||||
active:
|
||||
uiAccess.toolbarNode.signal.value.buttonStates[buttonName].active,
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{} as {
|
||||
[key in ToolbarNodeButtonNames]: {
|
||||
onClick: MouseEventHandler<HTMLButtonElement>;
|
||||
disabled: boolean;
|
||||
hidden: boolean;
|
||||
active: boolean;
|
||||
};
|
||||
},
|
||||
);
|
||||
useEffect(() => {
|
||||
function resizeHandler() {
|
||||
setWinSize({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
}
|
||||
window.addEventListener("resize", resizeHandler);
|
||||
return () => {
|
||||
window.removeEventListener("resize", resizeHandler);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleColorChange = (color: string) => {
|
||||
// Don't do anything if no node is known to be bound
|
||||
if (uiAccess.toolbarNode.signal.value.knodeIds.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the UI component
|
||||
uiAccess.toolbarNode.setColor(color);
|
||||
|
||||
// Signal the engine with the node ID (just grab the first -and expected only- node)
|
||||
const nodeID = uiAccess.toolbarNode.signal.value.knodeIds[0];
|
||||
dispatchUiEvents.toolbarNode.setSelectedColor({ kNodeId: nodeID, color });
|
||||
}
|
||||
|
||||
useSignals();
|
||||
const color = uiAccess.toolbarNode.signal.value.color;
|
||||
|
||||
const boundedTopPosition = Math.min(position.y, winSize.height - 164);
|
||||
return (
|
||||
<Transition
|
||||
as="div"
|
||||
show={isShowing}
|
||||
className={twMerge(
|
||||
transitionTimingStyles,
|
||||
"fixed -translate-x-1/2 translate-y-5 data-[closed]:opacity-0",
|
||||
)}
|
||||
style={{
|
||||
top: boundedTopPosition,
|
||||
left: position.x,
|
||||
}}
|
||||
>
|
||||
<ToolbarNode
|
||||
{...rest}
|
||||
buttonsProps={buttonsProps}
|
||||
locked={uiAccess.toolbarNode.signal.value.locked}
|
||||
onLockClicked={(e) => {
|
||||
dispatchUiEvents.toolbarNode.lock(e);
|
||||
}}
|
||||
color={color}
|
||||
onColorChange={handleColorChange}
|
||||
/>
|
||||
</Transition>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import { forwardRef, HTMLAttributes } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { useRenderCounter } from "~/hooks/useRenderCounter";
|
||||
|
||||
export const KonvaCanvasContainer = forwardRef<
|
||||
HTMLDivElement,
|
||||
HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
useRenderCounter("KonvaCanvasContainer");
|
||||
|
||||
const classes = twMerge("pegboard -z-10", className);
|
||||
return <div ref={ref} className={classes} {...props} />;
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
|
||||
// Components
|
||||
import { KonvaCanvasContainer } from "./KonvaCanvasContainer";
|
||||
import { ContextualToolbarNode } from "./ContextualToolbarNode";
|
||||
// import { SignaledCanvasDragDropFiles } from "./SignaledCanvasDragDropFiles";
|
||||
import { SignaledDialogs } from "./SignaledDialogs";
|
||||
import { SignaledToolbarMain } from "./SignaledToolbarMain";
|
||||
|
||||
// import { PromptBox } from "~/components/PromptBox";
|
||||
// The KonvaApp is the root of the Konva stage
|
||||
// and only entry point for anything in Konva JS
|
||||
import { EngineType } from "~/KonvaApp";
|
||||
import { KonvaApp } from "~/KonvaApp";
|
||||
|
||||
// all the signal-contexts are wrapped in hooks
|
||||
import { useAppUiContext } from "./contextSignals/appUi"; not needed for 2d.
|
||||
import { useLayoutContext } from "./contextSignals/layout";
|
||||
|
||||
// common hooks
|
||||
import { useRenderCounter } from "~/hooks/useRenderCounter";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
// TODO REFACTOR THIS INTO THE COMPONENT LIBRARY its used in 3d and 2d
|
||||
import { UndoRedo } from "./components/reusable/UndoRedo/UndoRedo";
|
||||
|
||||
export const KonvaRootComponent = ({
|
||||
className,
|
||||
sceneToken,
|
||||
}: {
|
||||
className: string;
|
||||
sceneToken?: string;
|
||||
}) => {
|
||||
// This is a hook that will log the number of times the component has rerendered
|
||||
// Let's make sure we only log once
|
||||
useRenderCounter("KonvaRootComponent");
|
||||
const navigate = useNavigate();
|
||||
// const appUiContext = useAppUiContext();
|
||||
const layoutContext = useLayoutContext();
|
||||
|
||||
// Add state to track engine initialization
|
||||
const [isEngineReady, setIsEngineReady] = useState(false);
|
||||
const engineRef = useRef<EngineType | null>(null);
|
||||
|
||||
const konvaContainerCallbackRef = useCallback((node: HTMLDivElement) => {
|
||||
// Only initialize if we have a node and haven't initialized yet
|
||||
if (node !== null && !isEngineReady && engineRef.current === null) {
|
||||
try {
|
||||
const options = {
|
||||
navigate: navigate,
|
||||
sceneToken: sceneToken,
|
||||
};
|
||||
engineRef.current = KonvaApp(node, options);
|
||||
// Only set ready if initialization succeeded
|
||||
if (engineRef.current) {
|
||||
setIsEngineReady(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize KonvaApp:", error);
|
||||
engineRef.current = null;
|
||||
setIsEngineReady(false);
|
||||
}
|
||||
}
|
||||
}, [navigate, sceneToken, isEngineReady]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Always render the canvas container */}
|
||||
<KonvaCanvasContainer
|
||||
ref={konvaContainerCallbackRef}
|
||||
className={className}
|
||||
/>
|
||||
|
||||
{/* Conditionally render the loading overlay */}
|
||||
{!isEngineReady && (
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="text-gray-500">Initializing canvas...</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Conditionally render the UI components that need the engine */}
|
||||
{isEngineReady && engineRef.current && (
|
||||
<UndoRedo engine={engineRef.current}>
|
||||
|
||||
{/* <SignaledCanvasDragDropFiles
|
||||
openAddImage={appUiContext.openAddImage}
|
||||
openAddVideo={appUiContext.openAddVideo}
|
||||
/> */}
|
||||
{/* /<PromptBox /> */}
|
||||
<SignaledToolbarMain
|
||||
layoutSignal={layoutContext.signal}
|
||||
appUiContext={appUiContext}
|
||||
/>
|
||||
<SignaledDialogs
|
||||
appUiSignal={appUiContext.signal}
|
||||
resetAll={appUiContext.resetAll}
|
||||
/>
|
||||
<ContextualToolbarNode />
|
||||
</UndoRedo>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
import { useCallback } from "react";
|
||||
import { Signal } from "@preact/signals-react";
|
||||
import { dispatchUiEvents } from "./signals/uiEvents";
|
||||
import {
|
||||
DialogAddImage,
|
||||
DialogEditText,
|
||||
DialogError,
|
||||
} from "~/components/features";
|
||||
|
||||
import { AppUiSignalType } from "./contextSignals/appUi";
|
||||
import { dialogError } from "~/signals/uiAccess/dialogError";
|
||||
|
||||
|
||||
export const SignaledDialogs = ({
|
||||
appUiSignal,
|
||||
resetAll,
|
||||
}: {
|
||||
appUiSignal: Signal<AppUiSignalType>;
|
||||
resetAll: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
{/* Dialogs that are opened from within Reactland*/}
|
||||
<DialogAddImage
|
||||
isOpen={appUiSignal.value.isAddImageOpen ?? false}
|
||||
stagedImage={appUiSignal.value.stagedImage}
|
||||
closeCallback={resetAll}
|
||||
onAddImage={(file) => {
|
||||
dispatchUiEvents.addImageToEngine(file);
|
||||
}}
|
||||
/>
|
||||
<DialogEditText
|
||||
isOpen={appUiSignal.value.isEditTextOpen ?? false}
|
||||
onDoneEditText={(data) => {
|
||||
dispatchUiEvents.addTextToEngine(data);
|
||||
}}
|
||||
closeCallback={resetAll}
|
||||
/>
|
||||
{/* Dialogs that are opened from the Engine*/}
|
||||
<SignaledDialogError />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const SignaledDialogError = () => {
|
||||
const props = dialogError.signal.value;
|
||||
const { isShowing, title, message } = props;
|
||||
const onClose = useCallback(() => {
|
||||
dialogError.hide();
|
||||
}, []);
|
||||
return (
|
||||
<DialogError
|
||||
isShowing={isShowing}
|
||||
title={title}
|
||||
message={message}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export const paperWrapperStyles =
|
||||
"rounded-xl border border-ui-border p-1.5 shadow-lg bg-ui-panel";
|
||||
|
||||
export const transitionTimingStyles = "transition duration-150 ease-in-out";
|
||||
|
||||
export const toolTipStyles = {
|
||||
base: twMerge(
|
||||
"relative",
|
||||
"after:absolute after:z-40",
|
||||
"after:content-[attr(data-tooltip)] after:text-black after:text-nowrap after:text-sm after:font-medium after:text-white/80 after:glass",
|
||||
"after:rounded-lg after:border after:border-ui-border after:bg-ui-panel after:px-2.5 after:py-1 after:shadow-xl",
|
||||
"after:hidden hover:after:block",
|
||||
),
|
||||
top: twMerge(
|
||||
"after:left-1/2 after:bottom-full after:-translate-x-1/2 after:mb-3.5",
|
||||
),
|
||||
bottom: twMerge(
|
||||
"after:left-1/2 after:top-full after:-translate-x-1/2 after:mt-3.5",
|
||||
),
|
||||
left: twMerge(
|
||||
"after:top-1/2 after:right-full after:-translate-y-1/2 after:mr-3.5",
|
||||
),
|
||||
right: twMerge(
|
||||
"after:top-1/2 after:left-full after:-translate-y-1/2 after:ml-3.5",
|
||||
),
|
||||
};
|
||||
|
||||
export const dialogPanelStyles = twMerge(
|
||||
"flex max-w-5xl flex-col justify-between gap-4 p-6 bg-[#1C1C20]",
|
||||
);
|
||||
@@ -0,0 +1,52 @@
|
||||
import { computed, signal } from "@preact/signals-react";
|
||||
import { UserInfo } from "@ApiManager/models/Users";
|
||||
import { ActiveSubscriptions } from "@ApiManager/models/Billing";
|
||||
import { LoyaltyProgram } from "@ApiManager/enums/Billing";
|
||||
import { USER_FEATURE_FLAGS } from "@ApiManager/enums/UserFeatures";
|
||||
|
||||
import { AUTH_STATUS } from "./enums";
|
||||
|
||||
const status = signal<AUTH_STATUS>(AUTH_STATUS.INIT);
|
||||
const userInfo = signal<UserInfo | undefined>(undefined);
|
||||
const activeSubs = signal<ActiveSubscriptions | undefined>(undefined);
|
||||
|
||||
const canUpload3D = computed(() => {
|
||||
if (!userInfo.value || !userInfo.value.maybe_feature_flags) {
|
||||
return undefined;
|
||||
}
|
||||
return userInfo.value.maybe_feature_flags.includes(
|
||||
USER_FEATURE_FLAGS.UPLOAD_3D,
|
||||
);
|
||||
});
|
||||
|
||||
const hasAccess = computed(() => {
|
||||
if (
|
||||
userInfo.value === undefined ||
|
||||
userInfo.value.can_access_studio === undefined
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
return userInfo.value.can_access_studio;
|
||||
});
|
||||
|
||||
const hasPremium = computed(() => {
|
||||
if (activeSubs.value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
activeSubs.value.active_subscriptions.length > 0 ||
|
||||
activeSubs.value.maybe_loyalty_program === LoyaltyProgram.CONTRIBUTOR
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
export const signals = {
|
||||
status,
|
||||
userInfo,
|
||||
activeSubs,
|
||||
canUpload3D,
|
||||
hasAccess,
|
||||
hasPremium,
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
export enum AUTH_STATUS {
|
||||
INIT = "INIT",
|
||||
NO_ACCESS = "no_access",
|
||||
LOGGED_IN = "logged_in",
|
||||
LOGGING = "logging",
|
||||
GET_USER_INFO = "get_user_info",
|
||||
LOGGED_OUT = "logged_out",
|
||||
}
|
||||
|
||||
export enum AUTH_ERROR_FALLBACKS {
|
||||
CreateSessionError = "Unknown Error during Create Session",
|
||||
DestorySessionError = "Unknown Error during Destroy Session",
|
||||
GetSessionError = "Unknown Error During Get Session",
|
||||
Unauthorized = "User Unauthorized",
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import { signals } from "./auth_signals";
|
||||
import { UsersApi } from "@storyteller/api";
|
||||
import { BillingApi } from "@storyteller/api";
|
||||
|
||||
import {
|
||||
updateActiveSubscriptions,
|
||||
updateAuthStatus,
|
||||
updateUserInfo,
|
||||
setLogoutStates,
|
||||
} from "./utilities";
|
||||
import { AUTH_STATUS } from "./enums";
|
||||
|
||||
export const logout = async (
|
||||
failureCallback?: (errorMessage: string) => void,
|
||||
) => {
|
||||
const usersApi = new UsersApi();
|
||||
const logoutResponse = await usersApi.Logout();
|
||||
if (!logoutResponse.success && failureCallback) {
|
||||
failureCallback(
|
||||
logoutResponse.errorMessage || "Unknown Error during Destroy Session",
|
||||
);
|
||||
}
|
||||
// if success, nothing
|
||||
// regarldess of success/fail, clear the state and localstorage
|
||||
setLogoutStates();
|
||||
};
|
||||
|
||||
export const login = async ({
|
||||
usernameOrEmail,
|
||||
password,
|
||||
}: {
|
||||
usernameOrEmail: string;
|
||||
password: string;
|
||||
failureCallback?: () => void;
|
||||
}) => {
|
||||
updateAuthStatus(AUTH_STATUS.LOGGING);
|
||||
|
||||
const usersApi = new UsersApi();
|
||||
const loginResponse = await usersApi.Login({ usernameOrEmail, password });
|
||||
if (!loginResponse.success || !loginResponse.data) {
|
||||
setLogoutStates();
|
||||
return;
|
||||
}
|
||||
|
||||
// technically user is login with the system now, HOWEVER,
|
||||
// in storyteller studio, only having a sesison is not enough,
|
||||
// we need session info and active subscription info as well
|
||||
getUserInfoAndSubcriptions();
|
||||
};
|
||||
|
||||
export const signUp = async ({
|
||||
username,
|
||||
email,
|
||||
password,
|
||||
passwordConfirmation,
|
||||
}: {
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
passwordConfirmation: string;
|
||||
}) => {
|
||||
updateAuthStatus(AUTH_STATUS.LOGGING);
|
||||
|
||||
const usersApi = new UsersApi();
|
||||
const response = await usersApi.Signup({
|
||||
email,
|
||||
password,
|
||||
passwordConfirmation,
|
||||
username,
|
||||
});
|
||||
console.log(response);
|
||||
if (!response.success || !response.data) {
|
||||
setLogoutStates();
|
||||
return response.errorMessage ?? "Unknown error";
|
||||
}
|
||||
|
||||
// technically user is login with the system now, HOWEVER,
|
||||
// in storyteller studio, only having a sesison is not enough,
|
||||
// we need session info and active subscription info as well
|
||||
getUserInfoAndSubcriptions();
|
||||
return "";
|
||||
};
|
||||
|
||||
export const persistLogin = async () => {
|
||||
//Only run First Load, return if not
|
||||
if (signals.status.value !== AUTH_STATUS.INIT) {
|
||||
return;
|
||||
}
|
||||
getUserInfoAndSubcriptions();
|
||||
};
|
||||
|
||||
async function getUserInfoAndSubcriptions() {
|
||||
updateAuthStatus(AUTH_STATUS.GET_USER_INFO);
|
||||
const usersApi = new UsersApi();
|
||||
const sessionResponse = await usersApi.GetSession();
|
||||
if (
|
||||
!sessionResponse.success ||
|
||||
!sessionResponse.data ||
|
||||
!sessionResponse.data.user
|
||||
) {
|
||||
setLogoutStates();
|
||||
return;
|
||||
}
|
||||
|
||||
if (sessionResponse.data && !sessionResponse.data.user.can_access_studio) {
|
||||
updateAuthStatus(AUTH_STATUS.NO_ACCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
const billingApi = new BillingApi();
|
||||
const subscriptionsResponse = await billingApi.ListActiveSubscriptions();
|
||||
if (
|
||||
!subscriptionsResponse.success ||
|
||||
!subscriptionsResponse.data ||
|
||||
!subscriptionsResponse.data.active_subscriptions
|
||||
) {
|
||||
setLogoutStates();
|
||||
return;
|
||||
}
|
||||
|
||||
updateUserInfo(sessionResponse.data.user);
|
||||
updateActiveSubscriptions({
|
||||
maybe_loyalty_program: subscriptionsResponse.data.maybe_loyalty_program,
|
||||
active_subscriptions: subscriptionsResponse.data.active_subscriptions || [],
|
||||
});
|
||||
updateAuthStatus(AUTH_STATUS.LOGGED_IN);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { signals } from "./auth_signals";
|
||||
import * as enums from "./enums";
|
||||
import * as fetchers from "./fetchers";
|
||||
export const authentication = {
|
||||
signals,
|
||||
fetchers,
|
||||
enums,
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import { AUTH_STATUS } from "./enums";
|
||||
import { UserInfo } from "~/Classes/ApiManager/models/Users";
|
||||
import { ActiveSubscriptions } from "@ApiManager/models/Billing";
|
||||
|
||||
import { signals } from "./auth_signals";
|
||||
|
||||
export const updateAuthStatus = (newStatus: AUTH_STATUS) => {
|
||||
signals.status.value = newStatus;
|
||||
};
|
||||
|
||||
export const updateUserInfo = (
|
||||
newInfo: UserInfo | undefined,
|
||||
flush?: boolean,
|
||||
) => {
|
||||
if (newInfo && !flush) {
|
||||
//case of updating UserInfo partly
|
||||
signals.userInfo.value = {
|
||||
...signals.userInfo.value,
|
||||
...newInfo,
|
||||
};
|
||||
} else {
|
||||
//case of setting a new User
|
||||
//case of deleting userInfo
|
||||
signals.userInfo.value = newInfo;
|
||||
}
|
||||
};
|
||||
export const updateActiveSubscriptions = (
|
||||
activeSubs: ActiveSubscriptions | undefined,
|
||||
) => {
|
||||
signals.activeSubs.value = activeSubs;
|
||||
};
|
||||
|
||||
// this function is not exposed, only the logout function is
|
||||
export const setLogoutStates = () => {
|
||||
updateAuthStatus(AUTH_STATUS.LOGGED_OUT);
|
||||
updateUserInfo(undefined);
|
||||
updateActiveSubscriptions(undefined);
|
||||
// flushAllBackgroundLoadedMedia();
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
import { effect, signal } from "@preact/signals-react";
|
||||
|
||||
export enum GenerationLoadingState {
|
||||
INIT,
|
||||
GENERATING,
|
||||
GENERATED,
|
||||
}
|
||||
|
||||
const initValue = { loadingState: GenerationLoadingState.INIT, prompt: "" };
|
||||
|
||||
export const generationSignal = signal<{
|
||||
loadingState: GenerationLoadingState;
|
||||
prompt: string;
|
||||
imageB64?: string;
|
||||
}>(initValue);
|
||||
|
||||
export const startGeneration = (prompt: string) => {
|
||||
generationSignal.value = {
|
||||
loadingState: GenerationLoadingState.GENERATING,
|
||||
prompt,
|
||||
imageB64: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
export const onStartGeneration = (callback: (prompt: string) => void) => {
|
||||
effect(() => {
|
||||
// If value just changed to GENERATING, notify the listeners
|
||||
if (
|
||||
generationSignal.value.loadingState === GenerationLoadingState.GENERATING
|
||||
) {
|
||||
callback(generationSignal.value.prompt);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const finishGeneration = (imageB64: string) => {
|
||||
generationSignal.value = {
|
||||
loadingState: GenerationLoadingState.GENERATED,
|
||||
prompt: generationSignal.value.prompt,
|
||||
imageB64,
|
||||
};
|
||||
};
|
||||
|
||||
export const cancelGeneration = () => {
|
||||
if (
|
||||
generationSignal.value.loadingState === GenerationLoadingState.GENERATING
|
||||
) {
|
||||
generationSignal.value = initValue;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./generationSignals";
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from "./authentication";
|
||||
export * from "./uiAccess";
|
||||
export * from "./uiEvents";
|
||||
export * from "./generation";
|
||||
@@ -0,0 +1,50 @@
|
||||
import { signal } from "@preact/signals-react";
|
||||
import { ContextualButtonRetryProps } from "./type";
|
||||
const buttonRetrySignal = signal<ContextualButtonRetryProps>({
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
isShowing: false,
|
||||
disabled: false,
|
||||
});
|
||||
|
||||
export const buttonRetry = {
|
||||
signal: buttonRetrySignal,
|
||||
disable: () => {
|
||||
buttonRetrySignal.value = { ...buttonRetrySignal.value, disabled: true };
|
||||
},
|
||||
updatePosition(position: { x: number; y: number }) {
|
||||
buttonRetrySignal.value = {
|
||||
...buttonRetrySignal.value,
|
||||
position,
|
||||
};
|
||||
},
|
||||
enable: () => {
|
||||
buttonRetrySignal.value = {
|
||||
...buttonRetrySignal.value,
|
||||
disabled: false,
|
||||
};
|
||||
},
|
||||
show(props: Omit<ContextualButtonRetryProps, "isShowing">) {
|
||||
if (buttonRetrySignal.value.isShowing) {
|
||||
if (import.meta.env.DEV) {
|
||||
console.warn(
|
||||
"Loading bar is already showing",
|
||||
"use the `updatePosition` methods instead",
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
buttonRetrySignal.value = {
|
||||
...props,
|
||||
isShowing: true,
|
||||
};
|
||||
},
|
||||
hide() {
|
||||
buttonRetrySignal.value = {
|
||||
...buttonRetrySignal.value,
|
||||
isShowing: false,
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import { signal } from "@preact/signals-react";
|
||||
const buttonTestSignal = signal<{ disabled: boolean; active: boolean }>({
|
||||
disabled: false,
|
||||
active: false,
|
||||
});
|
||||
export const buttonTest = {
|
||||
signal: buttonTestSignal,
|
||||
disable: () => {
|
||||
buttonTestSignal.value = { ...buttonTestSignal.value, disabled: true };
|
||||
},
|
||||
enable: () => {
|
||||
buttonTestSignal.value = {
|
||||
...buttonTestSignal.value,
|
||||
disabled: false,
|
||||
};
|
||||
},
|
||||
setActive: () => {
|
||||
buttonTestSignal.value = {
|
||||
...buttonTestSignal.value,
|
||||
active: true,
|
||||
};
|
||||
},
|
||||
setInactive: () => {
|
||||
buttonTestSignal.value = {
|
||||
...buttonTestSignal.value,
|
||||
active: false,
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import { signal } from "@preact/signals-react";
|
||||
import { ChromakeyProps } from "~/components/features/DialogChromakey/type";
|
||||
|
||||
const initialState = {
|
||||
isShowing: false,
|
||||
chromakeyProps: {
|
||||
isChromakeyEnabled: false,
|
||||
},
|
||||
};
|
||||
const dialogChromaSignal = signal<{
|
||||
isShowing: boolean;
|
||||
chromakeyProps: ChromakeyProps;
|
||||
}>(initialState);
|
||||
|
||||
export const dialogChromakey = {
|
||||
signal: dialogChromaSignal,
|
||||
|
||||
show(chromakeyProps: ChromakeyProps) {
|
||||
dialogChromaSignal.value = {
|
||||
...dialogChromaSignal.value,
|
||||
isShowing: true,
|
||||
chromakeyProps,
|
||||
};
|
||||
},
|
||||
hide() {
|
||||
dialogChromaSignal.value = initialState;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
import { signal } from "@preact/signals-react";
|
||||
|
||||
const initialState = {
|
||||
isShowing: false,
|
||||
title: "",
|
||||
message: "",
|
||||
};
|
||||
const dialogErrorSignal = signal(initialState);
|
||||
|
||||
export const dialogError = {
|
||||
signal: dialogErrorSignal,
|
||||
|
||||
show({ title, message }: { title?: string; message?: string }) {
|
||||
dialogErrorSignal.value = {
|
||||
...dialogErrorSignal.value,
|
||||
title: title ?? "Error",
|
||||
message: message ?? "An unknownerror occurred",
|
||||
isShowing: true,
|
||||
};
|
||||
},
|
||||
hide() {
|
||||
dialogErrorSignal.value = initialState;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import { ContextualLoadingBarProps } from "./type";
|
||||
|
||||
type uiAccessType = {
|
||||
loadingBar: ContextualLoadingBarProps;
|
||||
};
|
||||
|
||||
export type { uiAccessType };
|
||||
|
||||
import { buttonRetry } from "./buttonRetry";
|
||||
import { buttonTest } from "./buttonTest";
|
||||
import { dialogChromakey } from "./dialogChromakey";
|
||||
import { dialogError } from "./dialogError";
|
||||
import { loadingBar } from "./loadingBar";
|
||||
import { magicBox } from "./magicBox";
|
||||
import { toolbarMain } from "./toolbarMain";
|
||||
import { toolbarNode } from "./toolbarNode";
|
||||
import { toolbarVideoExtraction } from "./toolbarVideoExtraction";
|
||||
|
||||
export const uiAccess = {
|
||||
buttonRetry,
|
||||
buttonTest,
|
||||
dialogChromakey,
|
||||
dialogError,
|
||||
loadingBar,
|
||||
magicBox,
|
||||
toolbarMain,
|
||||
toolbarNode,
|
||||
toolbarVideoExtraction,
|
||||
};
|
||||
@@ -0,0 +1,86 @@
|
||||
import { signal } from "@preact/signals-react";
|
||||
import { ContextualLoadingBarProps } from "./type";
|
||||
import { LoadingBarStatus } from "~/components/ui/LoadingBar";
|
||||
|
||||
const loadingBarSignal = signal<ContextualLoadingBarProps>({
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
width: 300,
|
||||
isShowing: false,
|
||||
progress: 0,
|
||||
status: LoadingBarStatus.IDLE,
|
||||
message: undefined,
|
||||
onRetry: undefined,
|
||||
});
|
||||
|
||||
export const loadingBar = {
|
||||
signal: loadingBarSignal,
|
||||
isShowing() {
|
||||
return loadingBarSignal.value.isShowing;
|
||||
},
|
||||
update(props: Partial<Omit<ContextualLoadingBarProps, "isShowing">>) {
|
||||
loadingBarSignal.value = {
|
||||
...loadingBarSignal.value,
|
||||
// isShowing: true,
|
||||
...props,
|
||||
};
|
||||
},
|
||||
updateMessage(message: string) {
|
||||
loadingBarSignal.value = {
|
||||
...loadingBarSignal.value,
|
||||
message,
|
||||
};
|
||||
},
|
||||
updateWidth(width: number) {
|
||||
loadingBarSignal.value = {
|
||||
...loadingBarSignal.value,
|
||||
width,
|
||||
};
|
||||
},
|
||||
updatePosition(position: { x: number; y: number }) {
|
||||
loadingBarSignal.value = {
|
||||
...loadingBarSignal.value,
|
||||
position,
|
||||
};
|
||||
},
|
||||
updateProgress(progress: number) {
|
||||
loadingBarSignal.value = {
|
||||
...loadingBarSignal.value,
|
||||
progress,
|
||||
};
|
||||
},
|
||||
updateStatus(status: LoadingBarStatus) {
|
||||
loadingBarSignal.value = {
|
||||
...loadingBarSignal.value,
|
||||
status,
|
||||
};
|
||||
},
|
||||
show(props?: Partial<Omit<ContextualLoadingBarProps, "isShowing">>) {
|
||||
if (loadingBarSignal.value.isShowing) {
|
||||
if (import.meta.env.DEV) {
|
||||
console.warn(
|
||||
"Loading bar is already showing",
|
||||
"use the `update` methods instead",
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const mergedProps = props
|
||||
? { ...props, ...loadingBarSignal.value }
|
||||
: loadingBarSignal.value;
|
||||
loadingBarSignal.value = {
|
||||
...mergedProps,
|
||||
isShowing: true,
|
||||
};
|
||||
},
|
||||
hide() {
|
||||
if (loadingBarSignal.value.isShowing !== false) {
|
||||
loadingBarSignal.value = {
|
||||
...loadingBarSignal.value,
|
||||
isShowing: false,
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import { signal } from "@preact/signals-react";
|
||||
type MagicBoxSignalType = {
|
||||
isShowing: boolean;
|
||||
orientation: "vertical" | "horizontal";
|
||||
scale: number;
|
||||
};
|
||||
const magicBoxSignal = signal<MagicBoxSignalType>({
|
||||
isShowing: false,
|
||||
orientation: "vertical",
|
||||
scale: 1,
|
||||
});
|
||||
export const magicBox = {
|
||||
signal: magicBoxSignal,
|
||||
isShowing: () => {
|
||||
return magicBoxSignal.value.isShowing;
|
||||
},
|
||||
hide: () => {
|
||||
magicBoxSignal.value = { ...magicBoxSignal.value, isShowing: false };
|
||||
},
|
||||
show: () => {
|
||||
magicBoxSignal.value = { ...magicBoxSignal.value, isShowing: true };
|
||||
},
|
||||
setOrientation: (orientation: "vertical" | "horizontal") => {
|
||||
magicBoxSignal.value = { ...magicBoxSignal.value, orientation };
|
||||
},
|
||||
setScale: (scale: number) => {
|
||||
magicBoxSignal.value = { ...magicBoxSignal.value, scale };
|
||||
},
|
||||
update: (updatedVals: Partial<MagicBoxSignalType>) => {
|
||||
magicBoxSignal.value = { ...magicBoxSignal.value, ...updatedVals };
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { toolbarMain as toolbarMainOnly } from "./toolbarMain";
|
||||
import { loadingBar } from "./loadingBar";
|
||||
|
||||
export const toolbarMain = {
|
||||
...toolbarMainOnly,
|
||||
loadingBar,
|
||||
};
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import { signal } from "@preact/signals-react";
|
||||
import { LoadingBarStatus } from "~/components/ui";
|
||||
import { LoadingBarInterface } from "../type";
|
||||
|
||||
const loadingBarSignal = signal<LoadingBarInterface>({
|
||||
isShowing: false,
|
||||
progress: 0,
|
||||
status: LoadingBarStatus.IDLE,
|
||||
message: undefined,
|
||||
});
|
||||
|
||||
export const loadingBar = {
|
||||
signal: loadingBarSignal,
|
||||
update: (props: Partial<LoadingBarInterface>) => {
|
||||
loadingBarSignal.value = { ...loadingBarSignal.value, ...props };
|
||||
},
|
||||
updateMessage(message: string | undefined) {
|
||||
loadingBarSignal.value = {
|
||||
...loadingBarSignal.value,
|
||||
message,
|
||||
};
|
||||
},
|
||||
updateProgress(progress: number) {
|
||||
loadingBarSignal.value = {
|
||||
...loadingBarSignal.value,
|
||||
progress,
|
||||
};
|
||||
},
|
||||
updateStatus(status: LoadingBarStatus) {
|
||||
loadingBarSignal.value = {
|
||||
...loadingBarSignal.value,
|
||||
status,
|
||||
};
|
||||
},
|
||||
show: (props?: Omit<LoadingBarInterface, "isShowing">) => {
|
||||
const mergedProps = props
|
||||
? { ...props, ...loadingBarSignal.value }
|
||||
: loadingBarSignal.value;
|
||||
loadingBarSignal.value = {
|
||||
...mergedProps,
|
||||
isShowing: true,
|
||||
};
|
||||
},
|
||||
hide: () => {
|
||||
loadingBarSignal.value = { ...loadingBarSignal.value, isShowing: false };
|
||||
},
|
||||
};
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
import { signal } from "@preact/signals-react";
|
||||
import { ToolbarMainButtonNames } from "~/components/features/ToolbarMain/enum";
|
||||
import { isToolbarMode } from "~/signals/uiEvents/toolbarMain/toolbarMain";
|
||||
|
||||
interface ToolbarMainSignalInterface {
|
||||
disabled: boolean;
|
||||
buttonStates: {
|
||||
[key in ToolbarMainButtonNames]: { disabled: boolean; active: boolean };
|
||||
};
|
||||
}
|
||||
const toolbarMainSignal = signal<ToolbarMainSignalInterface>({
|
||||
disabled: false,
|
||||
buttonStates: initButtonStates(),
|
||||
});
|
||||
|
||||
export const toolbarMain = {
|
||||
signal: toolbarMainSignal,
|
||||
enable() {
|
||||
toolbarMainSignal.value = {
|
||||
...toolbarMainSignal.value,
|
||||
disabled: false,
|
||||
};
|
||||
},
|
||||
disable() {
|
||||
toolbarMainSignal.value = {
|
||||
...toolbarMainSignal.value,
|
||||
disabled: true,
|
||||
};
|
||||
},
|
||||
|
||||
changeButtonState(
|
||||
buttonName: ToolbarMainButtonNames,
|
||||
{ disabled, active }: { disabled?: boolean; active?: boolean },
|
||||
) {
|
||||
toolbarMainSignal.value = {
|
||||
...toolbarMainSignal.value,
|
||||
buttonStates: {
|
||||
...toolbarMainSignal.value.buttonStates,
|
||||
[buttonName]: {
|
||||
disabled: active ? false : (disabled ?? false),
|
||||
active: active ?? false,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
setMode(mode: ToolbarMainButtonNames) {
|
||||
if (!isToolbarMode(mode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.changeButtonState(ToolbarMainButtonNames.SELECT, { active: false });
|
||||
this.changeButtonState(ToolbarMainButtonNames.ERASER, { active: false });
|
||||
this.changeButtonState(ToolbarMainButtonNames.PAINT, { active: false });
|
||||
this.changeButtonState(mode, { active: true });
|
||||
}
|
||||
};
|
||||
|
||||
function initButtonStates() {
|
||||
const ret: { [key: string]: { disabled: boolean; active: boolean } } = {};
|
||||
Object.values(ToolbarMainButtonNames).forEach((buttonName) => {
|
||||
ret[buttonName] = {
|
||||
disabled: false,
|
||||
active: false,
|
||||
};
|
||||
});
|
||||
return ret as ToolbarMainSignalInterface["buttonStates"];
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
import { signal } from "@preact/signals-react";
|
||||
import { ContextualUi } from "./type";
|
||||
import { ToolbarNodeButtonNames as ButtonNames } from "~/components/features/ToolbarNode/enums";
|
||||
import { DEFAULT_PAINT_COLOR } from "../uiEvents/toolbarMain/paintMode";
|
||||
|
||||
type ButtonStates = {
|
||||
[key in ButtonNames]: {
|
||||
disabled: boolean;
|
||||
active: boolean;
|
||||
hidden: boolean;
|
||||
};
|
||||
};
|
||||
export interface ContextualToolbarProps extends ContextualUi {
|
||||
knodeIds: string[];
|
||||
disabled: boolean;
|
||||
downloadUrl?: string;
|
||||
locked: boolean | "unknown";
|
||||
lockDisabled: boolean;
|
||||
buttonStates: ButtonStates;
|
||||
color: string;
|
||||
position: { x: number, y: number };
|
||||
}
|
||||
interface PartialContextualToolbarProps
|
||||
extends Partial<Omit<ContextualToolbarProps, "buttonStates">> {
|
||||
buttonStates?: Partial<ButtonStates>;
|
||||
}
|
||||
const toolbarNodeSignal = signal<ContextualToolbarProps>({
|
||||
knodeIds: [],
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
isShowing: false,
|
||||
disabled: false,
|
||||
locked: false,
|
||||
lockDisabled: false,
|
||||
buttonStates: initButtonStates(),
|
||||
color: DEFAULT_PAINT_COLOR,
|
||||
});
|
||||
|
||||
export const toolbarNode = {
|
||||
signal: toolbarNodeSignal,
|
||||
isShowing() {
|
||||
return toolbarNodeSignal.value.isShowing;
|
||||
},
|
||||
setup(props: ContextualToolbarProps) {
|
||||
toolbarNodeSignal.value = props;
|
||||
},
|
||||
update(props: Partial<ContextualToolbarProps>) {
|
||||
toolbarNodeSignal.value = {
|
||||
...toolbarNodeSignal.value,
|
||||
...props,
|
||||
};
|
||||
},
|
||||
setPosition(position: ContextualToolbarProps["position"]) {
|
||||
toolbarNodeSignal.value = {
|
||||
...toolbarNodeSignal.value,
|
||||
position,
|
||||
};
|
||||
},
|
||||
isLocked() {
|
||||
return toolbarNodeSignal.value.locked;
|
||||
},
|
||||
isLockDisabled() {
|
||||
return toolbarNodeSignal.value.lockDisabled;
|
||||
},
|
||||
disableLock() {
|
||||
toolbarNodeSignal.value.lockDisabled = true;
|
||||
},
|
||||
enableLock() {
|
||||
toolbarNodeSignal.value.lockDisabled = false;
|
||||
},
|
||||
setLocked(locked: boolean) {
|
||||
const { buttonStates } = toolbarNodeSignal.value;
|
||||
toolbarNodeSignal.value = {
|
||||
...toolbarNodeSignal.value,
|
||||
buttonStates: {
|
||||
...buttonStates,
|
||||
[ButtonNames.TRANSFORM]: {
|
||||
disabled: locked,
|
||||
hidden: false,
|
||||
active: buttonStates[ButtonNames.TRANSFORM].active,
|
||||
},
|
||||
[ButtonNames.COLOR]: {
|
||||
disabled: locked,
|
||||
hidden: false,
|
||||
active: buttonStates[ButtonNames.COLOR].active,
|
||||
},
|
||||
},
|
||||
locked: locked,
|
||||
};
|
||||
},
|
||||
show(
|
||||
values: PartialContextualToolbarProps = {
|
||||
locked: false,
|
||||
buttonStates: {},
|
||||
},
|
||||
) {
|
||||
const { locked, buttonStates, ...rest } = values;
|
||||
toolbarNodeSignal.value = {
|
||||
...toolbarNodeSignal.value,
|
||||
...rest,
|
||||
buttonStates: {
|
||||
...initButtonStates({
|
||||
locked,
|
||||
}),
|
||||
...buttonStates,
|
||||
},
|
||||
isShowing: true,
|
||||
locked: locked ?? false,
|
||||
};
|
||||
},
|
||||
hide() {
|
||||
toolbarNodeSignal.value = {
|
||||
...toolbarNodeSignal.value,
|
||||
isShowing: false,
|
||||
};
|
||||
},
|
||||
enable() {
|
||||
toolbarNodeSignal.value = {
|
||||
...toolbarNodeSignal.value,
|
||||
disabled: false,
|
||||
};
|
||||
},
|
||||
disable() {
|
||||
toolbarNodeSignal.value = {
|
||||
...toolbarNodeSignal.value,
|
||||
disabled: true,
|
||||
};
|
||||
},
|
||||
changeButtonState(
|
||||
buttonName: ButtonNames,
|
||||
{ disabled, active }: { disabled?: boolean; active?: boolean },
|
||||
) {
|
||||
const prevButtonState = toolbarNodeSignal.value.buttonStates[buttonName];
|
||||
toolbarNodeSignal.value = {
|
||||
...toolbarNodeSignal.value,
|
||||
buttonStates: {
|
||||
...toolbarNodeSignal.value.buttonStates,
|
||||
[buttonName]: {
|
||||
disabled: disabled ?? prevButtonState.disabled,
|
||||
active: active ?? prevButtonState.active,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
setColor(color: string) {
|
||||
toolbarNodeSignal.value = {
|
||||
...toolbarNodeSignal.value,
|
||||
color,
|
||||
}
|
||||
},
|
||||
batchButtonStates({ buttonStates }: { buttonStates: Partial<ButtonStates> }) {
|
||||
const prevButtonStates = toolbarNodeSignal.value.buttonStates;
|
||||
toolbarNodeSignal.value = {
|
||||
...toolbarNodeSignal.value,
|
||||
buttonStates: {
|
||||
...prevButtonStates,
|
||||
...buttonStates,
|
||||
},
|
||||
};
|
||||
},
|
||||
resetAllButtonStates() {
|
||||
toolbarNodeSignal.value = {
|
||||
...toolbarNodeSignal.value,
|
||||
buttonStates: initButtonStates(),
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
function initButtonStates(props: { locked?: boolean | "unknown" } = {}) {
|
||||
return Object.values(ButtonNames).reduce((buttonStates, buttonName) => {
|
||||
if (props.locked !== undefined && buttonName === ButtonNames.TRANSFORM) {
|
||||
buttonStates[buttonName] = {
|
||||
disabled: props.locked === "unknown" || props.locked === true,
|
||||
hidden: false,
|
||||
active: true,
|
||||
};
|
||||
} else {
|
||||
buttonStates[buttonName] = {
|
||||
disabled: false,
|
||||
hidden: false,
|
||||
active: false,
|
||||
};
|
||||
}
|
||||
return buttonStates;
|
||||
}, {} as ButtonStates);
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
import { signal } from "@preact/signals-react";
|
||||
import { ButtonState, LoadingBarState } from "./type";
|
||||
import { ToolbarVideoExtractionButtonNames as ButtonNames } from "~/components/features/ToolbarVideoExtraction/enums";
|
||||
import { VideoExtractionEvents } from "~/KonvaApp/types/events";
|
||||
|
||||
type ButtonStates = {
|
||||
[key in ButtonNames]: ButtonState;
|
||||
};
|
||||
|
||||
export interface ToolbarVideoExtractionProps {
|
||||
isShowing: boolean;
|
||||
disabled: boolean;
|
||||
mode: "inclusion" | "exclusion";
|
||||
ready: boolean;
|
||||
buttonStates: ButtonStates;
|
||||
loadingBarState: LoadingBarState;
|
||||
}
|
||||
|
||||
const toolbarVideoExtractionSignal = signal<ToolbarVideoExtractionProps>({
|
||||
ready: false,
|
||||
mode: "inclusion",
|
||||
isShowing: false,
|
||||
disabled: false,
|
||||
buttonStates: initButtonStates(),
|
||||
loadingBarState: {
|
||||
progress: 0,
|
||||
status: VideoExtractionEvents.SESSION_CLOSED,
|
||||
message: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
export const toolbarVideoExtraction = {
|
||||
signal: toolbarVideoExtractionSignal,
|
||||
update(updateProps: Partial<ToolbarVideoExtractionProps>) {
|
||||
toolbarVideoExtractionSignal.value = {
|
||||
...toolbarVideoExtractionSignal.value,
|
||||
...updateProps,
|
||||
};
|
||||
},
|
||||
setReady(ready: boolean) {
|
||||
toolbarVideoExtractionSignal.value = {
|
||||
...toolbarVideoExtractionSignal.value,
|
||||
ready,
|
||||
};
|
||||
},
|
||||
setMode(mode: "inclusion" | "exclusion") {
|
||||
toolbarVideoExtractionSignal.value = {
|
||||
...toolbarVideoExtractionSignal.value,
|
||||
mode,
|
||||
};
|
||||
},
|
||||
updateProgress(newLoadingBarState: LoadingBarState) {
|
||||
toolbarVideoExtractionSignal.value = {
|
||||
...toolbarVideoExtractionSignal.value,
|
||||
loadingBarState: {
|
||||
...toolbarVideoExtractionSignal.value.loadingBarState,
|
||||
...newLoadingBarState,
|
||||
},
|
||||
};
|
||||
},
|
||||
show() {
|
||||
toolbarVideoExtractionSignal.value = {
|
||||
...toolbarVideoExtractionSignal.value,
|
||||
isShowing: true,
|
||||
};
|
||||
},
|
||||
hide() {
|
||||
toolbarVideoExtractionSignal.value = {
|
||||
...toolbarVideoExtractionSignal.value,
|
||||
isShowing: true,
|
||||
};
|
||||
},
|
||||
enable() {
|
||||
toolbarVideoExtractionSignal.value = {
|
||||
...toolbarVideoExtractionSignal.value,
|
||||
disabled: false,
|
||||
};
|
||||
},
|
||||
disable() {
|
||||
toolbarVideoExtractionSignal.value = {
|
||||
...toolbarVideoExtractionSignal.value,
|
||||
disabled: true,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
function initButtonStates() {
|
||||
return Object.values(ButtonNames).reduce((buttonStates, buttonName) => {
|
||||
buttonStates[buttonName] = {
|
||||
disabled: false,
|
||||
hidden: false,
|
||||
active: false,
|
||||
};
|
||||
return buttonStates;
|
||||
}, {} as ButtonStates);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { LoadingBarProps, LoadingBarStatus } from "~/components/ui";
|
||||
|
||||
export type LoadingBarState = {
|
||||
progress: number;
|
||||
status: string;
|
||||
message: string | undefined;
|
||||
};
|
||||
export type ButtonState = {
|
||||
disabled: boolean;
|
||||
active: boolean;
|
||||
hidden: boolean;
|
||||
};
|
||||
export interface ContextualUi {
|
||||
position: {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
isShowing: boolean;
|
||||
}
|
||||
|
||||
export interface ContextualLoadingBarProps
|
||||
extends ContextualUi,
|
||||
Omit<LoadingBarProps, "position"> {
|
||||
width: number;
|
||||
}
|
||||
|
||||
export interface ContextualButtonRetryProps extends ContextualUi {
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
export interface LoadingBarInterface {
|
||||
isShowing: boolean;
|
||||
progress: number;
|
||||
status: LoadingBarStatus;
|
||||
message?: string;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { signal, effect } from "@preact/signals-react";
|
||||
import { ShapeNodeData, TextNodeData } from "~/KonvaApp/types";
|
||||
|
||||
// ADDING IMAGES
|
||||
const stagedImage = signal<File | null>(null);
|
||||
const addImageToEngine = (image: File) => {
|
||||
stagedImage.value = image;
|
||||
};
|
||||
|
||||
const onGetStagedImage = (callback: (file: File) => void) => {
|
||||
effect(() => {
|
||||
if (stagedImage.value) {
|
||||
callback(stagedImage.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// ADDING VIDEOS
|
||||
type VideoSignal = {
|
||||
mediaFileToken: string;
|
||||
mediaFileUrl: string;
|
||||
videoWidth: number;
|
||||
videoHeight: number;
|
||||
};
|
||||
const stagedVideo = signal<VideoSignal | null>(null);
|
||||
|
||||
const addVideoToEngine = (videoData: VideoSignal) => {
|
||||
stagedVideo.value = videoData;
|
||||
};
|
||||
|
||||
const onGetStagedVideo = (callback: (videoData: VideoSignal) => void) => {
|
||||
effect(() => {
|
||||
if (stagedVideo.value) {
|
||||
callback(stagedVideo.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// ADD and EDIT TEXT
|
||||
const stagedText = signal<TextNodeData | null>(null);
|
||||
const addTextToEngine = (data: TextNodeData) => {
|
||||
stagedText.value = data;
|
||||
};
|
||||
const onAddTextToEngine = (callback: (data: TextNodeData) => void) => {
|
||||
effect(() => {
|
||||
if (stagedText.value) {
|
||||
callback(stagedText.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Add shapes
|
||||
const stagedShape = signal<ShapeNodeData | null>(null);
|
||||
const addShapeToEngine = (data: ShapeNodeData) => {
|
||||
stagedShape.value = data;
|
||||
}
|
||||
const onAddShapeToEngine = (callback: (data: ShapeNodeData) => void) => {
|
||||
effect(() => {
|
||||
if (stagedShape.value) {
|
||||
callback(stagedShape.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//EXPORTS
|
||||
export const dispatchers = {
|
||||
addImageToEngine,
|
||||
addVideoToEngine,
|
||||
addTextToEngine,
|
||||
addShapeToEngine,
|
||||
};
|
||||
|
||||
export const events = {
|
||||
onGetStagedImage,
|
||||
onGetStagedVideo,
|
||||
onAddTextToEngine,
|
||||
onAddShapeToEngine,
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
import { signal, effect } from "@preact/signals-react";
|
||||
import {
|
||||
RandomTextsNegative,
|
||||
RandomTextsPositive,
|
||||
} from "~/components/features/DialogAiStylize/data/randomTextList";
|
||||
|
||||
import { ArtStyleNames } from "~/components/features/DialogAiStylize/enums";
|
||||
|
||||
interface AiStylizeSingalType {
|
||||
artstyle: ArtStyleNames;
|
||||
positivePrompt: string;
|
||||
negativePrompt: string;
|
||||
cinematic: boolean;
|
||||
enginePreProcessing: boolean;
|
||||
faceDetail: boolean;
|
||||
lipSync: boolean;
|
||||
upscale: boolean;
|
||||
styleStrength: number;
|
||||
}
|
||||
const initialValues = {
|
||||
artstyle: ArtStyleNames.Anime2DFlat,
|
||||
positivePrompt: RandomTextsPositive[ArtStyleNames.Anime2DFlat][0],
|
||||
negativePrompt: RandomTextsNegative[ArtStyleNames.Anime2DFlat][0],
|
||||
cinematic: false,
|
||||
enginePreProcessing: false,
|
||||
faceDetail: false,
|
||||
lipSync: false,
|
||||
upscale: false,
|
||||
styleStrength: 0.8,
|
||||
};
|
||||
const stagedAiStylizeRequest = signal<AiStylizeSingalType>();
|
||||
|
||||
const dispatchRequest = (data: AiStylizeSingalType) => {
|
||||
stagedAiStylizeRequest.value = data;
|
||||
};
|
||||
let onRequestEffectCleanup: (() => void) | undefined;
|
||||
const onRequest = (callback: (data: AiStylizeSingalType) => void) => {
|
||||
if (onRequestEffectCleanup) {
|
||||
onRequestEffectCleanup();
|
||||
}
|
||||
onRequestEffectCleanup = effect(() => {
|
||||
if (stagedAiStylizeRequest.value) {
|
||||
callback(stagedAiStylizeRequest.value);
|
||||
stagedAiStylizeRequest.value = undefined;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getCurrentValues = stagedAiStylizeRequest.value;
|
||||
const getInitialValues = initialValues;
|
||||
|
||||
export const aiStylizeDispatchers = {
|
||||
dispatchRequest,
|
||||
};
|
||||
export const aiStylizeEvents = {
|
||||
onRequest,
|
||||
getCurrentValues,
|
||||
getInitialValues,
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
import { signal, effect } from "@preact/signals-core";
|
||||
|
||||
// this is to control the app mode - BFlat
|
||||
|
||||
// Define the app mode types
|
||||
export type AppModeType = "realtime" | "edit" | "generate" | "gallery";
|
||||
|
||||
// Create a signal for the current app mode
|
||||
export const appMode = signal<AppModeType>("realtime");
|
||||
|
||||
// Create a function to change the app mode
|
||||
export const changeAppMode = (mode: AppModeType) => {
|
||||
appMode.value = mode;
|
||||
};
|
||||
|
||||
// Store callbacks for mode changes
|
||||
const callbacks: ((mode: AppModeType) => void)[] = [];
|
||||
|
||||
// Set up an effect to notify callbacks when the mode changes
|
||||
effect(() => {
|
||||
const currentMode = appMode.value;
|
||||
callbacks.forEach((callback) => callback(currentMode));
|
||||
});
|
||||
|
||||
// Export an event handler for components to use
|
||||
export const appModeEvents = {
|
||||
onChange: (callback: (mode: AppModeType) => void) => {
|
||||
callbacks.push(callback);
|
||||
|
||||
// Return a function to unsubscribe
|
||||
return () => {
|
||||
const index = callbacks.indexOf(callback);
|
||||
if (index !== -1) {
|
||||
callbacks.splice(index, 1);
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import { MouseEventHandler } from "react";
|
||||
import { signal, effect } from "@preact/signals-react";
|
||||
|
||||
const event = signal<
|
||||
React.MouseEvent<HTMLButtonElement, MouseEvent> | undefined
|
||||
>(undefined);
|
||||
|
||||
let effectCleanup: (() => void) | undefined = undefined;
|
||||
|
||||
export const eventsHandler = {
|
||||
onClick: (callback: MouseEventHandler<HTMLButtonElement>) => {
|
||||
if (effectCleanup) {
|
||||
effectCleanup();
|
||||
}
|
||||
effectCleanup = effect(() => {
|
||||
if (event.value) {
|
||||
callback(event.value);
|
||||
event.value = undefined;
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const dispatcher = {
|
||||
onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
console.log("Button Retry dispatcher");
|
||||
event.value = e;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
// import { MouseEventHandler } from "react";
|
||||
import { signal, effect } from "@preact/signals-react";
|
||||
|
||||
const event = signal<number | undefined>();
|
||||
|
||||
export const eventsHandler = {
|
||||
onClick: (callback: () => void) => {
|
||||
effect(() => {
|
||||
if (event.value) {
|
||||
console.log("Button Test effect event handler", event.value);
|
||||
callback();
|
||||
return () => {
|
||||
console.log("Button Test effect event handler cleanup");
|
||||
event.value = undefined;
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const dispatcher = {
|
||||
onClick: () => {
|
||||
console.log("Button Test dispatcher");
|
||||
event.value = new Date().getTime();
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
import {
|
||||
dispatchers as addMediaDispatchers,
|
||||
events as addMediaEvents,
|
||||
} from "./addMedia";
|
||||
import { aiStylizeDispatchers, aiStylizeEvents } from "./aiStylize";
|
||||
import {
|
||||
dispatcher as buttonTestDispatcher,
|
||||
eventsHandler as buttonTestEvent,
|
||||
} from "./buttonTest";
|
||||
import {
|
||||
dispatcher as buttonRetryDispatcher,
|
||||
eventsHandler as buttonRetryEvent,
|
||||
} from "./buttonRetry";
|
||||
import { dispatchChromakeyRequest, onChromakeyRequest } from "./chromakey";
|
||||
import {
|
||||
dispatchers as toolbarMainDispatchers,
|
||||
eventsHandlers as toolbarMainEvents,
|
||||
} from "./toolbarMain";
|
||||
import {
|
||||
dispatchers as toolbarNodeDispatchers,
|
||||
eventsHandlers as toolbarNodeEvents,
|
||||
} from "./toolbarNode";
|
||||
import {
|
||||
dispatchers as toolbarVideoExtractionDispatchers,
|
||||
eventsHandlers as toolbarVideoExtractionEvents,
|
||||
} from "./toolbarVideoExtraction";
|
||||
import {
|
||||
dispatchers as promptDispatchers,
|
||||
events as promptEvents,
|
||||
} from "./promptSettings";
|
||||
import {
|
||||
dispatchers as loadingIndicatorDispatchers,
|
||||
events as loadingIndicatorEvents,
|
||||
} from "./loadingIndicator";
|
||||
import {
|
||||
dispatchers as modelSelectionDispatchers,
|
||||
events as modelSelectionEvents,
|
||||
} from "./modelSelection";
|
||||
import { appMode, changeAppMode, appModeEvents } from "./appMode";
|
||||
|
||||
export const uiEvents = {
|
||||
...addMediaEvents,
|
||||
onChromakeyRequest,
|
||||
aiStylize: aiStylizeEvents,
|
||||
buttonRetry: buttonRetryEvent,
|
||||
buttonTest: buttonTestEvent,
|
||||
toolbarMain: toolbarMainEvents,
|
||||
toolbarNode: toolbarNodeEvents,
|
||||
toolbarVideoExtraction: toolbarVideoExtractionEvents,
|
||||
promptEvents: promptEvents,
|
||||
loadingIndicator: loadingIndicatorEvents,
|
||||
modelSelection: modelSelectionEvents,
|
||||
appMode: appModeEvents,
|
||||
};
|
||||
export const dispatchUiEvents = {
|
||||
...addMediaDispatchers,
|
||||
dispatchChromakeyRequest,
|
||||
aiStylize: aiStylizeDispatchers,
|
||||
buttonRetry: buttonRetryDispatcher,
|
||||
buttonTest: buttonTestDispatcher,
|
||||
toolbarMain: toolbarMainDispatchers,
|
||||
toolbarNode: toolbarNodeDispatchers,
|
||||
toolbarVideoExtraction: toolbarVideoExtractionDispatchers,
|
||||
promptSettings: promptDispatchers,
|
||||
loadingIndicator: loadingIndicatorDispatchers,
|
||||
modelSelection: modelSelectionDispatchers,
|
||||
changeAppMode,
|
||||
};
|
||||
|
||||
export { appMode };
|
||||
@@ -0,0 +1,55 @@
|
||||
import { effect, signal } from "@preact/signals-react";
|
||||
|
||||
// Model loading settings
|
||||
// - Is loading visible
|
||||
// - Loading progress percentage
|
||||
export const DEFAULT_IS_LOADING_VISIBLE = false;
|
||||
export const isLoadingVisible = signal<boolean>(DEFAULT_IS_LOADING_VISIBLE);
|
||||
const setIsLoadingVisible = (visible: boolean) => {
|
||||
isLoadingVisible.value = visible;
|
||||
};
|
||||
|
||||
const onIsLoadingVisibleChanged = (callback: (visible: boolean) => void) => {
|
||||
effect(() => {
|
||||
callback(isLoadingVisible.value);
|
||||
});
|
||||
};
|
||||
|
||||
export const DEFAULT_LOADING_PROGRESS = 0;
|
||||
export const loadingProgress = signal<number>(DEFAULT_LOADING_PROGRESS);
|
||||
const setLoadingProgress = (progress: number) => {
|
||||
// Round to integer, then clamp between 0 and 100
|
||||
loadingProgress.value = Math.max(0, Math.min(100, Math.round(progress)));
|
||||
};
|
||||
|
||||
const onLoadingProgressChanged = (callback: (progress: number) => void) => {
|
||||
effect(() => {
|
||||
callback(loadingProgress.value);
|
||||
});
|
||||
};
|
||||
|
||||
// Loading text signal
|
||||
export const DEFAULT_LOADING_TEXT = "Downloading";
|
||||
export const loadingText = signal<string>(DEFAULT_LOADING_TEXT);
|
||||
const setLoadingText = (text: string) => {
|
||||
loadingText.value = text;
|
||||
};
|
||||
|
||||
const onLoadingTextChanged = (callback: (text: string) => void) => {
|
||||
effect(() => {
|
||||
callback(loadingText.value);
|
||||
});
|
||||
};
|
||||
|
||||
// EXPORTS
|
||||
export const dispatchers = {
|
||||
setIsLoadingVisible,
|
||||
setLoadingProgress,
|
||||
setLoadingText,
|
||||
};
|
||||
|
||||
export const events = {
|
||||
onIsLoadingVisibleChanged,
|
||||
onLoadingProgressChanged,
|
||||
onLoadingTextChanged,
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
import { effect, signal } from "@preact/signals-react";
|
||||
import { models } from "~/data/models";
|
||||
|
||||
// Define the signals for selected models and download states
|
||||
export const selectedModel = signal<string | null>(
|
||||
models.length > 0 ? models[0].id : null,
|
||||
);
|
||||
export const selectedLora = signal<string | null>(null);
|
||||
|
||||
// Replace single download tracking with a map to track multiple downloads
|
||||
export interface DownloadInfo {
|
||||
progress: number;
|
||||
type: "model" | "lora";
|
||||
}
|
||||
|
||||
// Map of modelId/loraId to download progress
|
||||
export const downloadingItems = signal<Record<string, DownloadInfo>>({});
|
||||
|
||||
// Dispatchers
|
||||
const setSelectedModel = (modelId: string | null) => {
|
||||
selectedModel.value = modelId;
|
||||
};
|
||||
|
||||
const setSelectedLora = (loraId: string | null) => {
|
||||
selectedLora.value = loraId;
|
||||
};
|
||||
|
||||
// Start downloading a model or lora
|
||||
const startDownload = (id: string, type: "model" | "lora") => {
|
||||
const newDownloads = { ...downloadingItems.value };
|
||||
newDownloads[id] = { progress: 0, type };
|
||||
downloadingItems.value = newDownloads;
|
||||
};
|
||||
|
||||
// Update the progress of a downloading item
|
||||
const updateDownloadProgress = (id: string, progress: number) => {
|
||||
if (!downloadingItems.value[id]) return;
|
||||
|
||||
const newDownloads = { ...downloadingItems.value };
|
||||
newDownloads[id] = {
|
||||
...newDownloads[id],
|
||||
progress: Math.max(0, Math.min(100, Math.round(progress))),
|
||||
};
|
||||
downloadingItems.value = newDownloads;
|
||||
};
|
||||
|
||||
// Complete or cancel a download
|
||||
const finishDownload = (id: string) => {
|
||||
const newDownloads = { ...downloadingItems.value };
|
||||
delete newDownloads[id];
|
||||
downloadingItems.value = newDownloads;
|
||||
};
|
||||
|
||||
// Check if an item is currently downloading
|
||||
const isDownloading = (id: string): boolean => {
|
||||
return !!downloadingItems.value[id];
|
||||
};
|
||||
|
||||
// Get the progress of a downloading item
|
||||
const getDownloadProgress = (id: string): number => {
|
||||
return downloadingItems.value[id]?.progress || 0;
|
||||
};
|
||||
|
||||
// Event listeners
|
||||
const onSelectedModelChanged = (callback: (modelId: string | null) => void) => {
|
||||
effect(() => {
|
||||
callback(selectedModel.value);
|
||||
});
|
||||
};
|
||||
|
||||
const onSelectedLoraChanged = (callback: (loraId: string | null) => void) => {
|
||||
effect(() => {
|
||||
callback(selectedLora.value);
|
||||
});
|
||||
};
|
||||
|
||||
const onDownloadsChanged = (
|
||||
callback: (downloads: Record<string, DownloadInfo>) => void,
|
||||
) => {
|
||||
effect(() => {
|
||||
callback(downloadingItems.value);
|
||||
});
|
||||
};
|
||||
|
||||
// EXPORTS
|
||||
export const dispatchers = {
|
||||
setSelectedModel,
|
||||
setSelectedLora,
|
||||
startDownload,
|
||||
updateDownloadProgress,
|
||||
finishDownload,
|
||||
isDownloading,
|
||||
getDownloadProgress,
|
||||
};
|
||||
|
||||
export const events = {
|
||||
onSelectedModelChanged,
|
||||
onSelectedLoraChanged,
|
||||
onDownloadsChanged,
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
import { effect, signal } from "@preact/signals-react";
|
||||
|
||||
// Prompt settings
|
||||
// - Prompt text
|
||||
// - Prompt strength
|
||||
export const DEFAULT_PROMPT_TEXT: string = "";
|
||||
export const promptText = signal<string>(DEFAULT_PROMPT_TEXT);
|
||||
const setPrompt = (data: string) => {
|
||||
promptText.value = data;
|
||||
};
|
||||
|
||||
const onPromptTextChanged = (callback: (data: string) => void) => {
|
||||
effect(() => {
|
||||
if (promptText.value) {
|
||||
callback(promptText.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const DEFAULT_PROMPT_STRENGTH = 100;
|
||||
export const promptStrength = signal<number>(DEFAULT_PROMPT_STRENGTH);
|
||||
const setPromptStrength = (data: number) => {
|
||||
// Round to integer, then clamp between 0 and 100
|
||||
promptStrength.value = Math.max(0, Math.min(100, Math.round(data)));
|
||||
};
|
||||
|
||||
const onPromptStrengthChanged = (callback: (data: number) => void) => {
|
||||
effect(() => {
|
||||
if (promptStrength.value) {
|
||||
callback(promptStrength.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//EXPORTS
|
||||
export const dispatchers = {
|
||||
setPrompt,
|
||||
setPromptStrength,
|
||||
};
|
||||
|
||||
export const events = {
|
||||
onPromptTextChanged,
|
||||
onPromptStrengthChanged,
|
||||
};
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { signal, effect } from "@preact/signals-react";
|
||||
|
||||
export const DEFAULT_BG_COLOR: string = "#ffffff";
|
||||
export const bgColor = signal<string>(DEFAULT_BG_COLOR);
|
||||
export const setBgColor = (data: string) => {
|
||||
bgColor.value = data;
|
||||
};
|
||||
|
||||
export const onBgColorChanged = (callback: (data: string) => void) => {
|
||||
effect(() => {
|
||||
if (bgColor.value) {
|
||||
callback(bgColor.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import { effect, signal } from "@preact/signals-react";
|
||||
import { clamp, normalize } from "~/utilities";
|
||||
import { BRUSH_MAX_SIZE, BRUSH_MIN_SIZE, DEFAULT_PAINT_BRUSH_SIZE, paintBrushSize } from "./paintMode";
|
||||
|
||||
export const eraseBrushSize = signal<number>(DEFAULT_PAINT_BRUSH_SIZE);
|
||||
export const setEraseBrushSize = (brushSize: number) => {
|
||||
eraseBrushSize.value = clamp(brushSize, BRUSH_MIN_SIZE, BRUSH_MAX_SIZE);
|
||||
}
|
||||
|
||||
export const onEraseBrushSizeChanged = (callback: (normalizedSize: number) => void) => {
|
||||
effect(() => {
|
||||
callback(normalize(paintBrushSize.value, BRUSH_MIN_SIZE, BRUSH_MAX_SIZE));
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import { buttonDispatchers, buttonEventsHandlers } from "./toolbarMain";
|
||||
import {
|
||||
loadingBarRetryDispatch,
|
||||
loadingBarRetryEventHandler,
|
||||
} from "./loadingBar";
|
||||
import { onEraseBrushSizeChanged, setEraseBrushSize } from "./eraseMode";
|
||||
import { onPaintColorChanged, setPaintBrushSize, setPaintColor } from "./paintMode";
|
||||
import { onBgColorChanged, setBgColor } from "./backgroundMenu";
|
||||
export const dispatchers = {
|
||||
...buttonDispatchers,
|
||||
loadingBarRetry: loadingBarRetryDispatch,
|
||||
setPaintColor,
|
||||
setPaintBrushSize,
|
||||
setEraseBrushSize,
|
||||
setBgColor
|
||||
};
|
||||
export const eventsHandlers = {
|
||||
...buttonEventsHandlers,
|
||||
loadingBarRetry: loadingBarRetryEventHandler,
|
||||
onPaintColorChanged,
|
||||
onEraseBrushSizeChanged,
|
||||
onBgColorChanged
|
||||
};
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import { MouseEventHandler } from "react";
|
||||
import { signal, effect } from "@preact/signals-react";
|
||||
|
||||
const loadingBarRetryEvent = signal<
|
||||
React.MouseEvent<HTMLButtonElement> | undefined
|
||||
>();
|
||||
|
||||
let loadingBarRetryEffectCleanup: (() => void) | undefined = undefined;
|
||||
export const loadingBarRetryEventHandler = {
|
||||
onClick: (callback: MouseEventHandler<HTMLButtonElement>) => {
|
||||
if (loadingBarRetryEffectCleanup) {
|
||||
loadingBarRetryEffectCleanup();
|
||||
}
|
||||
loadingBarRetryEffectCleanup = effect(() => {
|
||||
if (loadingBarRetryEvent.value) {
|
||||
callback(loadingBarRetryEvent.value);
|
||||
loadingBarRetryEvent.value = undefined;
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const loadingBarRetryDispatch = (
|
||||
e: React.MouseEvent<HTMLButtonElement>,
|
||||
) => {
|
||||
console.log("toolbarMain > loadingBar > retry : onClick dispatched", e);
|
||||
loadingBarRetryEvent.value = e;
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import { effect, signal } from "@preact/signals-react";
|
||||
import { clamp, normalize } from "~/utilities";
|
||||
|
||||
export const DEFAULT_PAINT_COLOR: string = "#ff0000";
|
||||
export const paintColor = signal<string>(DEFAULT_PAINT_COLOR);
|
||||
export const setPaintColor = (data: string) => {
|
||||
paintColor.value = data;
|
||||
};
|
||||
|
||||
export const BRUSH_MIN_SIZE: number = 0;
|
||||
export const BRUSH_MAX_SIZE: number = 100;
|
||||
// NOTE: Must be between MIN and MAX sizes - there's no runtime checks
|
||||
export const DEFAULT_PAINT_BRUSH_SIZE: number = 50;
|
||||
export const paintBrushSize = signal<number>(DEFAULT_PAINT_BRUSH_SIZE);
|
||||
export const setPaintBrushSize = (brushSize: number) => {
|
||||
paintBrushSize.value = clamp(brushSize, BRUSH_MIN_SIZE, BRUSH_MAX_SIZE);
|
||||
console.debug(`Paint brush size set to ${paintBrushSize.value}`);
|
||||
}
|
||||
|
||||
export const onPaintColorChanged = (callback: (data: string) => void) => {
|
||||
effect(() => {
|
||||
if (paintColor.value) {
|
||||
callback(paintColor.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const onPaintBrushSizeChanged = (callback: (normalizedSize: number) => void) => {
|
||||
effect(() => {
|
||||
callback(normalize(paintBrushSize.value, BRUSH_MIN_SIZE, BRUSH_MAX_SIZE));
|
||||
});
|
||||
};
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
import { MouseEventHandler } from "react";
|
||||
import { signal, effect, Signal } from "@preact/signals-react";
|
||||
|
||||
import { ToolbarMainButtonNames } from "~/components/features/ToolbarMain/enum";
|
||||
import { AppModes } from "~/KonvaApp/constants";
|
||||
|
||||
const events = Object.values(ToolbarMainButtonNames).reduce(
|
||||
(acc, buttonName) => {
|
||||
acc[buttonName] = signal<
|
||||
React.MouseEvent<HTMLButtonElement, MouseEvent> | undefined
|
||||
>();
|
||||
return acc;
|
||||
},
|
||||
{} as {
|
||||
[key in ToolbarMainButtonNames]: Signal<
|
||||
(React.MouseEvent<HTMLButtonElement, MouseEvent> | undefined) | undefined
|
||||
>;
|
||||
},
|
||||
);
|
||||
const effectsCleanups = Object.values(ToolbarMainButtonNames).reduce(
|
||||
(acc, buttonName) => {
|
||||
acc[buttonName] = undefined;
|
||||
return acc;
|
||||
},
|
||||
{} as {
|
||||
[key in ToolbarMainButtonNames]: (() => void) | undefined;
|
||||
},
|
||||
);
|
||||
|
||||
export const isToolbarMode = (buttonName: ToolbarMainButtonNames) => {
|
||||
return appModeMap[buttonName] !== undefined;
|
||||
}
|
||||
|
||||
export const appModeMap: Record<string, AppModes> = {
|
||||
[ToolbarMainButtonNames.SELECT]: AppModes.SELECT,
|
||||
[ToolbarMainButtonNames.ERASER]: AppModes.ERASER,
|
||||
[ToolbarMainButtonNames.PAINT]: AppModes.PAINT,
|
||||
}
|
||||
|
||||
const defaultHandler = (buttonName: ToolbarMainButtonNames, callback: MouseEventHandler<HTMLButtonElement>) => {
|
||||
if (effectsCleanups[buttonName]) {
|
||||
effectsCleanups[buttonName]();
|
||||
}
|
||||
effectsCleanups[buttonName] = effect(() => {
|
||||
if (events[buttonName].value) {
|
||||
callback(events[buttonName].value);
|
||||
events[buttonName].value = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const buttonEventsHandlers = Object.values(
|
||||
ToolbarMainButtonNames,
|
||||
).reduce(
|
||||
(acc, buttonName) => {
|
||||
acc[buttonName] = {
|
||||
onClick: (callback: MouseEventHandler<HTMLButtonElement>) => { defaultHandler(buttonName, callback) },
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{} as {
|
||||
[key in ToolbarMainButtonNames]: {
|
||||
onClick: (callback: MouseEventHandler<HTMLButtonElement>) => void;
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export const buttonDispatchers = Object.values(ToolbarMainButtonNames).reduce(
|
||||
(acc, buttonName) => {
|
||||
acc[buttonName] = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
events[buttonName].value = e;
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{} as {
|
||||
[key in ToolbarMainButtonNames]: MouseEventHandler<HTMLButtonElement>;
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,153 @@
|
||||
import { MouseEventHandler } from "react";
|
||||
import { signal, effect, Signal } from "@preact/signals-react";
|
||||
|
||||
import { ToolbarNodeButtonNames as ButtonNames } from "./components/features/ToolbarNode/enums";
|
||||
const lockEvent = signal<React.MouseEvent<HTMLButtonElement> | undefined>();
|
||||
let lockEffectCleanup: (() => void) | undefined;
|
||||
const lockEventHandler = (callback: MouseEventHandler<HTMLButtonElement>) => {
|
||||
if (lockEffectCleanup) {
|
||||
lockEffectCleanup();
|
||||
}
|
||||
lockEffectCleanup = effect(() => {
|
||||
if (lockEvent.value) {
|
||||
callback(lockEvent.value);
|
||||
lockEvent.value = undefined;
|
||||
}
|
||||
});
|
||||
};
|
||||
const lockDispatcher = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
lockEvent.value = e;
|
||||
};
|
||||
|
||||
const buttonEventsRecords = Object.values(ButtonNames).reduce(
|
||||
(acc, buttonName) => {
|
||||
acc[buttonName] = {
|
||||
eventSignal: signal<
|
||||
React.MouseEvent<HTMLButtonElement, MouseEvent> | undefined
|
||||
>(),
|
||||
effectCleanup: undefined,
|
||||
lastEventTimestamp: undefined,
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{} as {
|
||||
[key in ButtonNames]: {
|
||||
eventSignal: Signal<
|
||||
| (React.MouseEvent<HTMLButtonElement, MouseEvent> | undefined)
|
||||
| undefined
|
||||
>;
|
||||
effectCleanup: (() => void) | undefined;
|
||||
lastEventTimestamp: number | undefined;
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const buttonEventsHandlers = Object.values(ButtonNames).reduce(
|
||||
(acc, buttonName) => {
|
||||
// We define custom events/handlers for color changes
|
||||
if (buttonName === ButtonNames.COLOR) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
acc[buttonName] = {
|
||||
onClick: (callback: MouseEventHandler<HTMLButtonElement>) => {
|
||||
const { eventSignal, effectCleanup, lastEventTimestamp } =
|
||||
buttonEventsRecords[buttonName];
|
||||
if (effectCleanup !== undefined) {
|
||||
// this clears the effect listener to rebind a new one for the onClick
|
||||
effectCleanup();
|
||||
}
|
||||
buttonEventsRecords[buttonName].effectCleanup = effect(() => {
|
||||
if (
|
||||
eventSignal.value &&
|
||||
lastEventTimestamp !== eventSignal.value.timeStamp
|
||||
) {
|
||||
buttonEventsRecords[buttonName].lastEventTimestamp =
|
||||
eventSignal.value.timeStamp;
|
||||
// console.log(buttonName, "EFFECT is triggered");
|
||||
callback(eventSignal.value);
|
||||
buttonEventsRecords[buttonName].eventSignal.value = undefined;
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{} as {
|
||||
[key in ButtonNames]: {
|
||||
onClick: (callback: MouseEventHandler<HTMLButtonElement>) => void;
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const buttonDispatchers = Object.values(ButtonNames).reduce(
|
||||
(acc, buttonName) => {
|
||||
acc[buttonName] = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
// console.log(buttonName + " DISPATCHED", e);
|
||||
buttonEventsRecords[buttonName].eventSignal.value = e;
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{} as {
|
||||
[key in ButtonNames]: MouseEventHandler<HTMLButtonElement>;
|
||||
},
|
||||
);
|
||||
|
||||
const retryEvent = signal<React.MouseEvent<HTMLButtonElement> | undefined>();
|
||||
let lastRetryEventTimeStamp: number | undefined = undefined;
|
||||
let retryEffectCleanup: (() => void) | undefined;
|
||||
const retryEventHandler = (callback: MouseEventHandler<HTMLButtonElement>) => {
|
||||
if (retryEffectCleanup) {
|
||||
retryEffectCleanup();
|
||||
}
|
||||
retryEffectCleanup = effect(() => {
|
||||
if (retryEvent.value) {
|
||||
if (
|
||||
lastRetryEventTimeStamp === undefined ||
|
||||
lastRetryEventTimeStamp !== retryEvent.value.timeStamp
|
||||
) {
|
||||
lastRetryEventTimeStamp = retryEvent.value.timeStamp;
|
||||
callback(retryEvent.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const retryDispatcher = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
retryEvent.value = e;
|
||||
};
|
||||
|
||||
export type NodeColor = {
|
||||
kNodeId: string;
|
||||
color: string;
|
||||
}
|
||||
const selectedColorEvent = signal<NodeColor | null>();
|
||||
const setSelectedColor = (nodeColor: NodeColor) => {
|
||||
selectedColorEvent.value = nodeColor;
|
||||
}
|
||||
const onSelectedColorChanged = (callback: (color: NodeColor) => void) => {
|
||||
effect(() => {
|
||||
if (selectedColorEvent.value) {
|
||||
callback(selectedColorEvent.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const dispatchers = {
|
||||
lock: lockDispatcher,
|
||||
retry: retryDispatcher,
|
||||
setSelectedColor,
|
||||
...buttonDispatchers,
|
||||
};
|
||||
export const eventsHandlers = {
|
||||
lock: {
|
||||
onClick: lockEventHandler,
|
||||
},
|
||||
retry: {
|
||||
onClick: retryEventHandler,
|
||||
},
|
||||
color: {
|
||||
onConfirmChanged: onSelectedColorChanged,
|
||||
},
|
||||
...buttonEventsHandlers,
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useRef } from "react";
|
||||
|
||||
export const useRenderCounter = (componentName: string) => {
|
||||
const renderCount = useRef(0);
|
||||
renderCount.current++;
|
||||
if (renderCount.current === 1) {
|
||||
if (import.meta.env.DEV) {
|
||||
console.log(`${componentName} rerendered ${renderCount.current} times`);
|
||||
}
|
||||
} else {
|
||||
console.warn(`${componentName} rerendered ${renderCount.current} times`);
|
||||
}
|
||||
return renderCount.current;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from "react";
|
||||
import React, { useContext, useEffect } from "react";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { LoadingDots, TopBar } from "~/components";
|
||||
@@ -16,22 +16,35 @@ import {
|
||||
editorLoader,
|
||||
cameraAspectRatio,
|
||||
outlinerIsShowing,
|
||||
disableHotkeyInput,
|
||||
enableHotkeyInput,
|
||||
gridVisibility,
|
||||
setGridVisibility,
|
||||
} from "~/pages/PageEnigma/signals";
|
||||
import { EditorCanvas } from "./comps/EngineCanvases";
|
||||
import { SceneContainer } from "./comps/SceneContainer";
|
||||
import { Outliner } from "./comps/Outliner";
|
||||
import { CameraAspectRatio } from "./enums";
|
||||
import { PromptBox } from "./comps/PromptBox";
|
||||
import { PromptBox3D } from "@storyteller/ui-promptbox";
|
||||
import { PopoverItem } from "@storyteller/ui-popover";
|
||||
import { OnboardingHelper } from "./comps/OnboardingHelper";
|
||||
import { IsDesktopApp } from "@storyteller/tauri-utils";
|
||||
import { FocalLengthDisplay } from "./comps/FocalLengthDisplay/FocalLengthDisplay";
|
||||
import { DemoModal } from "@storyteller/ui-demo-modal";
|
||||
import { appTabId, setAppTabId } from "~/signals/appTab";
|
||||
import { KonvaCanvasContainer } from "../Page2d/KonvaCanvasContainer";
|
||||
import { KonvaRootComponent } from "../Page2d/KonvaRootComponent";
|
||||
import { addCamera, cameras, deleteCamera, focalLengthDragging, selectedCameraId, updateCamera } from "./signals/camera";
|
||||
import { isPromptBoxFocused } from "./signals/promptBox";
|
||||
import { uploadImage } from "~/components/reusable/UploadModalMedia/uploadImage";
|
||||
import { EngineContext } from "./contexts/EngineContext";
|
||||
import Queue, { QueueNames } from "./Queue";
|
||||
import { toEngineActions } from "./Queue/toEngineActions";
|
||||
import { UnionedDataTypes } from "./Queue/Queue";
|
||||
|
||||
export const PageEditor = () => {
|
||||
useSignals();
|
||||
//console.log(api());
|
||||
//To prevent the click event from propagating to the canvas: TODO: HANDLE THIS BETTER?
|
||||
|
||||
const handleOverlayClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
event.stopPropagation();
|
||||
};
|
||||
@@ -74,14 +87,164 @@ export const PageEditor = () => {
|
||||
return scaleHeight;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-screen">
|
||||
<TopBar
|
||||
pageName="Edit Scene"
|
||||
appTabIdSignal={appTabId}
|
||||
setAppTabId={setAppTabId}
|
||||
/>
|
||||
<OnboardingHelper />
|
||||
// These are callbacks required by promptbox
|
||||
const editorEngine = useContext(EngineContext);
|
||||
const handleCameraSelect = (selectedItem: PopoverItem) => {
|
||||
const selectedCamera = cameras.value.find(
|
||||
(cam) => cam.label === selectedItem.label,
|
||||
);
|
||||
if (selectedCamera && editorEngine) {
|
||||
selectedCameraId.value = selectedCamera.id;
|
||||
|
||||
// Show focal length display temporarily
|
||||
// TODO: Rename dragging to visible - BFlat
|
||||
focalLengthDragging.value = {
|
||||
isDragging: true,
|
||||
focalLength: selectedCamera.focalLength,
|
||||
};
|
||||
setTimeout(() => {
|
||||
focalLengthDragging.value = {
|
||||
isDragging: false,
|
||||
focalLength: selectedCamera.focalLength,
|
||||
};
|
||||
}, 1500);
|
||||
|
||||
// Update the main camera to match the selected camera's properties
|
||||
if (editorEngine.camera) {
|
||||
// First update position and lookAt
|
||||
editorEngine.camera.position.set(
|
||||
selectedCamera.position.x,
|
||||
selectedCamera.position.y,
|
||||
selectedCamera.position.z,
|
||||
);
|
||||
editorEngine.camera.lookAt(
|
||||
selectedCamera.lookAt.x,
|
||||
selectedCamera.lookAt.y,
|
||||
selectedCamera.lookAt.z,
|
||||
);
|
||||
|
||||
// Update FOV
|
||||
editorEngine.camera.fov = editorEngine.focalLengthToFov(
|
||||
selectedCamera.focalLength,
|
||||
);
|
||||
editorEngine.camera.updateProjectionMatrix();
|
||||
|
||||
// Reset and update camera controls
|
||||
if (editorEngine.cameraViewControls) {
|
||||
editorEngine.cameraViewControls.reset();
|
||||
editorEngine.cameraViewControls.update(0);
|
||||
}
|
||||
|
||||
// Force a render to update the view
|
||||
editorEngine.renderScene();
|
||||
|
||||
// Queue an update to ensure the engine processes the camera change
|
||||
// Queue.publish({
|
||||
// queueName: QueueNames.TO_ENGINE,
|
||||
// action: toEngineActions.CAMERA_CHANGED,
|
||||
// data: selectedCamera,
|
||||
// });
|
||||
}
|
||||
|
||||
// Force update camera properties in the state
|
||||
updateCamera(selectedCamera.id, {
|
||||
focalLength: selectedCamera.focalLength,
|
||||
position: selectedCamera.position,
|
||||
rotation: selectedCamera.rotation,
|
||||
lookAt: selectedCamera.lookAt,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddCamera = () => {
|
||||
// Check if we've reached the maximum number of cameras
|
||||
if (cameras.value.length >= 6) {
|
||||
console.warn("Maximum number of cameras (6) reached");
|
||||
return;
|
||||
}
|
||||
|
||||
const newIndex = cameras.value.length + 1;
|
||||
const newId = `cam${newIndex}`;
|
||||
|
||||
// This is for generating random orbital position for the new camera using spherical coordinates
|
||||
const radius = Math.random() * 5 + 7; // Distance from center: 7 to 12 units
|
||||
const theta = Math.random() * Math.PI * 2; // Azimuthal angle: 0 to 2π
|
||||
const phi = Math.PI / 3 + (Math.random() * Math.PI) / 6; // Polar angle: π/3 to π/2 (60° to 90° from horizontal)
|
||||
|
||||
// Convert spherical coordinates to Cartesian coordinates
|
||||
const randomX = radius * Math.sin(phi) * Math.cos(theta);
|
||||
const randomY = Math.abs(radius * Math.cos(phi)) + 2; // Ensure Y is positive and at least 2 units up
|
||||
const randomZ = radius * Math.sin(phi) * Math.sin(theta);
|
||||
|
||||
addCamera({
|
||||
id: newId,
|
||||
label: `Camera ${newIndex}`,
|
||||
focalLength: 24,
|
||||
position: {
|
||||
x: randomX,
|
||||
y: randomY,
|
||||
z: randomZ,
|
||||
},
|
||||
rotation: { x: 0, y: 0, z: 0 },
|
||||
lookAt: { x: 0, y: 0, z: 0 },
|
||||
});
|
||||
|
||||
// Switch to the newly created camera
|
||||
selectedCameraId.value = newId;
|
||||
|
||||
// Update the engine camera to match the new camera's properties
|
||||
if (editorEngine && editorEngine.camera) {
|
||||
editorEngine.camera.position.set(randomX, randomY, randomZ);
|
||||
editorEngine.camera.lookAt(0, 0, 0);
|
||||
editorEngine.camera.fov = editorEngine.focalLengthToFov(24);
|
||||
editorEngine.camera.updateProjectionMatrix();
|
||||
|
||||
// Reset and update camera controls
|
||||
if (editorEngine.cameraViewControls) {
|
||||
editorEngine.cameraViewControls.reset();
|
||||
editorEngine.cameraViewControls.update(0);
|
||||
}
|
||||
|
||||
// Force a render to update the view
|
||||
editorEngine.renderScene();
|
||||
|
||||
// Queue.publish({
|
||||
// queueName: QueueNames.TO_ENGINE,
|
||||
// action: toEngineActions.CAMERA_CHANGED,
|
||||
// data: {
|
||||
// id: newId,
|
||||
// label: `Camera ${newIndex}`,
|
||||
// focalLength: 24,
|
||||
// position: { x: randomX, y: randomY, z: randomZ },
|
||||
// rotation: { x: 0, y: 0, z: 0 },
|
||||
// lookAt: { x: 0, y: 0, z: 0 },
|
||||
// },
|
||||
// });
|
||||
}
|
||||
};
|
||||
|
||||
const handleCameraNameChange = (id: string, newName: string) => {
|
||||
updateCamera(id, { label: newName });
|
||||
};
|
||||
|
||||
const handleCameraFocalLengthChange = (id: string, value: number) => {
|
||||
const camera = cameras.value.find((cam) => cam.id === id);
|
||||
if (camera) {
|
||||
updateCamera(id, { focalLength: value });
|
||||
}
|
||||
};
|
||||
|
||||
const onAspectRatioSelect = (newRatio: UnionedDataTypes) => {
|
||||
// Publish the change to the engine
|
||||
Queue.publish({
|
||||
queueName: QueueNames.TO_ENGINE,
|
||||
action: toEngineActions.CHANGE_CAMERA_ASPECT_RATIO,
|
||||
data: newRatio,
|
||||
});
|
||||
}
|
||||
|
||||
const ContentFor3D = () => {
|
||||
return <> <OnboardingHelper />
|
||||
<div
|
||||
className="relative flex w-screen"
|
||||
style={{ height: "calc(100vh - 68px)" }}
|
||||
@@ -136,7 +299,34 @@ export const PageEditor = () => {
|
||||
<ControlPanelSceneObject />
|
||||
</div>
|
||||
|
||||
<PromptBox />
|
||||
<PromptBox3D
|
||||
cameras={cameras}
|
||||
cameraAspectRatio={cameraAspectRatio}
|
||||
disableHotkeyInput={disableHotkeyInput}
|
||||
enableHotkeyInput={enableHotkeyInput}
|
||||
gridVisibility={gridVisibility}
|
||||
setGridVisibility={setGridVisibility}
|
||||
selectedCameraId={selectedCameraId}
|
||||
deleteCamera={deleteCamera}
|
||||
focalLengthDragging={focalLengthDragging}
|
||||
isPromptBoxFocused={isPromptBoxFocused}
|
||||
uploadImage={uploadImage}
|
||||
handleCameraSelect={handleCameraSelect}
|
||||
handleAddCamera={handleAddCamera}
|
||||
handleCameraNameChange={handleCameraNameChange}
|
||||
handleCameraFocalLengthChange={handleCameraFocalLengthChange}
|
||||
onAspectRatioSelect={onAspectRatioSelect}
|
||||
setEnginePrompt={(prompt) => {
|
||||
if (!editorEngine) {
|
||||
return;
|
||||
}
|
||||
|
||||
editorEngine!.positive_prompt = prompt
|
||||
}}
|
||||
snapshotCurrentFrame={
|
||||
editorEngine?.snapShotOfCurrentFrame
|
||||
}
|
||||
/>
|
||||
|
||||
<LoadingDots
|
||||
className="absolute left-0 top-0 z-50"
|
||||
@@ -146,18 +336,7 @@ export const PageEditor = () => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Side panel */}
|
||||
{/* <div onClick={handleOverlayClick}>
|
||||
<SidePanel />
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
{/* Timeline */}
|
||||
{/* <div onClick={handleOverlayClick}>
|
||||
<Timeline />
|
||||
</div> */}
|
||||
|
||||
<DemoModal
|
||||
title="Welcome to ArtCraft 3D"
|
||||
subTitle="Your 3D editor for digital art and design"
|
||||
@@ -172,6 +351,23 @@ export const PageEditor = () => {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
|
||||
const ContentFor2D = () => {
|
||||
return <KonvaCanvasContainer>
|
||||
<KonvaRootComponent className="w-full h-full" />
|
||||
</KonvaCanvasContainer>;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-screen">
|
||||
<TopBar
|
||||
pageName="Edit Scene"
|
||||
appTabIdSignal={appTabId}
|
||||
setAppTabId={setAppTabId}
|
||||
/>
|
||||
{appTabId.value === "3D" ? <ContentFor3D /> : <ContentFor2D />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
-2394
File diff suppressed because it is too large
Load Diff
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"name": "proxy",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "src/proxy.ts",
|
||||
"type": "module",
|
||||
"types": "./types",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "node --import=tsx src/proxy.ts"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"canvas": "^2.11.2",
|
||||
"common": "file:../common",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
"ffmpeg-static": "^5.2.0",
|
||||
"fluent-ffmpeg": "^2.1.3",
|
||||
"http-proxy-middleware": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/ffmpeg-static": "^3.0.3",
|
||||
"@types/fluent-ffmpeg": "^2.1.24",
|
||||
"tsx": "^4.11.2"
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
ffmpeg -i ./$1/videos/step1.mp4 \
|
||||
-f lavfi \
|
||||
-i anullsrc \
|
||||
-max_muxing_queue_size 999999 \
|
||||
-vf select='gte(n\,1)',scale=1024:576 \
|
||||
-c:v libx264 \
|
||||
-c:a aac \
|
||||
-shortest \
|
||||
-pix_fmt yuv420p \
|
||||
-f mp4 ./$1/videos/tmp0.mp4;
|
||||
@@ -1,75 +0,0 @@
|
||||
import express from "express";
|
||||
import { createProxyMiddleware } from "http-proxy-middleware";
|
||||
import cors from "cors";
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(cors());
|
||||
|
||||
app.use(
|
||||
"/v1",
|
||||
createProxyMiddleware({
|
||||
target: "https://api.storyteller.ai/v1",
|
||||
changeOrigin: true,
|
||||
secure: true,
|
||||
logger: console,
|
||||
}),
|
||||
);
|
||||
|
||||
app.use(
|
||||
"/google",
|
||||
createProxyMiddleware({
|
||||
target: "https://storage.googleapis.com",
|
||||
changeOrigin: true,
|
||||
secure: true,
|
||||
// logger: console,
|
||||
}),
|
||||
);
|
||||
|
||||
app.use(
|
||||
"/funnel",
|
||||
createProxyMiddleware({
|
||||
// target: "https://funnel.tailce84f.ts.net",
|
||||
target: "https://style.storyteller.ai",
|
||||
changeOrigin: true,
|
||||
secure: true,
|
||||
// logger: console,
|
||||
}),
|
||||
);
|
||||
|
||||
app.use(
|
||||
"/cdn",
|
||||
createProxyMiddleware({
|
||||
target: "https://cdn.storyteller.ai",
|
||||
changeOrigin: true,
|
||||
secure: true,
|
||||
// logger: console,
|
||||
}),
|
||||
);
|
||||
|
||||
app.use(
|
||||
"/vocodes",
|
||||
createProxyMiddleware({
|
||||
target: "https://storage.googleapis.com/vocodes-public",
|
||||
changeOrigin: true,
|
||||
secure: true,
|
||||
// logger: console,
|
||||
}),
|
||||
);
|
||||
|
||||
app.use(
|
||||
"/gravatar",
|
||||
createProxyMiddleware({
|
||||
target: "https://www.gravatar.com",
|
||||
changeOrigin: true,
|
||||
secure: true,
|
||||
logger: console,
|
||||
}),
|
||||
);
|
||||
|
||||
// app.use(express.json({ limit: "150mb" }));
|
||||
// app.post("/upload-video", uploadVideo);
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log("Listening on port 3000");
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"include": [
|
||||
"env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"netlify-edge-plugin.ts",
|
||||
"src/proxy.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"target": "ES2022",
|
||||
"strict": true,
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": "./src",
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# @storyteller/enums
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test @storyteller/enums` to execute the unit tests via [Vitest](https://vitest.dev/).
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@storyteller/enums",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"development": "./src/index.ts",
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from "./lib/EditorState";
|
||||
export * from "./lib/Queue";
|
||||
export * from "./lib/UploaderStates";
|
||||
export * from "./lib/Hotkeys";
|
||||
export * from "./lib/Toaster";
|
||||
@@ -0,0 +1,13 @@
|
||||
export enum EditorStates {
|
||||
EDIT,
|
||||
CAMERA_VIEW,
|
||||
PREVIEW,
|
||||
}
|
||||
|
||||
export enum CameraAspectRatio {
|
||||
HORIZONTAL_16_9 = "horizontal_16_9",
|
||||
VERTICAL_9_16 = "vertical_9_16",
|
||||
SQUARE_1_1 = "SQUARE_1_1",
|
||||
HORIZONTAL_3_2 = "horizontal_3_2",
|
||||
VERTICAL_2_3 = "vertical_2_3",
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export enum DomLevels {
|
||||
NONE,
|
||||
INPUT,
|
||||
PANEL,
|
||||
DIALOGUE,
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export enum AssetType {
|
||||
ANIMATION = "animation",
|
||||
AUDIO = "audio",
|
||||
CAMERA = "camera",
|
||||
CHARACTER = "character",
|
||||
EXPRESSION = "expression",
|
||||
OBJECT = "object",
|
||||
SKYBOX = "skybox",
|
||||
SHAPE = "shape",
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export enum ClipGroup {
|
||||
CHARACTER = "character",
|
||||
CAMERA = "camera",
|
||||
GLOBAL_AUDIO = "global_audio",
|
||||
OBJECT = "object",
|
||||
PROMPT_TRAVEL = "prompt",
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export enum ClipType {
|
||||
TRANSFORM = "transform",
|
||||
AUDIO = "audio",
|
||||
ANIMATION = "animation",
|
||||
EXPRESSION = "expression",
|
||||
FAKE = "fake",
|
||||
PROMPT_TRAVEL = "prompt",
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
export enum fromEngineActions {
|
||||
ADD_OBJECT = "add_object",
|
||||
CAMERA_ASPECT_RATIO_CHANGED = "camera_aspect_ratio_changed",
|
||||
// data = CameraAspectRatio
|
||||
DELETE_OBJECT = "delete_object",
|
||||
DESELECT_OBJECT = "deselect_object",
|
||||
RESET_TIMELINE = "reset_timeline",
|
||||
SELECT_OBJECT = "select_object",
|
||||
UPDATE_CHARACTER_ID = "update_character_id",
|
||||
UPDATE_TIME = "update_time",
|
||||
UPDATE_TIME_LINE = "update_timeline",
|
||||
POP_A_TOAST = "pop_a_toast",
|
||||
// data={currentTime: number}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export enum QueueNames {
|
||||
FROM_ENGINE = "FROM_ENGINE",
|
||||
TO_ENGINE = "TO_ENGINE",
|
||||
TO_TIMELINE = "TO_TIMELINE",
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
export enum toEngineActions {
|
||||
ADD_CLIP = "add_clip",
|
||||
// data=QueueClip
|
||||
ADD_KEYFRAME = "add_keyframe",
|
||||
|
||||
ADD_CHARACTER = "add_character",
|
||||
|
||||
ADD_OBJECT = "add_object",
|
||||
|
||||
ADD_SHAPE = "add_shape",
|
||||
|
||||
// data= QueueKeyframe
|
||||
DELETE_CLIP = "delete_clip",
|
||||
// data=QueueClip
|
||||
DELETE_KEYFRAME = "delete_keyframe",
|
||||
// data=QueueKeyframe
|
||||
ENTER_EDIT_STATE = "enter_edit",
|
||||
// data=null
|
||||
ENTER_PREVIEW_STATE = "enter_preview",
|
||||
// data=null
|
||||
GENERATE_VIDEO = "generate_video",
|
||||
// data=null
|
||||
MUTE = "mute",
|
||||
// data={version, type, group, object_uuid} if lipsync
|
||||
// data={version, type, group} if global_audio
|
||||
REFRESH_PREVIEW = "refresh_preview",
|
||||
// data=QueueClip
|
||||
TOGGLE_CAMERA_STATE = "toggle_camera",
|
||||
// data=null
|
||||
TOGGLE_REPEATING = "toggle_repeating",
|
||||
// data=null
|
||||
|
||||
CHANGE_CAMERA_ASPECT_RATIO = "change_camera_aspect_ratio",
|
||||
// data=CamperaAspectRatio
|
||||
|
||||
CAMERA_CHANGED = "camera_changed",
|
||||
|
||||
UNMUTE = "unmute",
|
||||
// data={version, type, group, object_uuid} if lipsync
|
||||
// data={version, type, group} if global_audio
|
||||
UPDATE_CLIP = "update_clip",
|
||||
// data=QueueClip
|
||||
UPDATE_KEYFRAME = "update_keyframe",
|
||||
// data=QueueKeyframe
|
||||
UPDATE_TIME = "update_time",
|
||||
// data={currentTime: number}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export enum toTimelineActions {
|
||||
ADD_KEYFRAME = "add_keyframe",
|
||||
GENERATE_PROGRESS = "generate_progress",
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export * from "./AssetType";
|
||||
export * from "./QueueNames";
|
||||
export * from "./FromEngineActions";
|
||||
export * from "./ToEngineActions";
|
||||
export * from "./ToTimelineActions";
|
||||
export * from "./ClipType";
|
||||
export * from "./ClipGroup";
|
||||
@@ -0,0 +1,5 @@
|
||||
export enum ToastTypes {
|
||||
SUCCESS,
|
||||
WARNING,
|
||||
ERROR,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./ToastTypes";
|
||||
@@ -0,0 +1,12 @@
|
||||
export enum UploaderStates {
|
||||
ready,
|
||||
uploadingAsset,
|
||||
uploadingImage,
|
||||
uploadingCover,
|
||||
settingCover,
|
||||
success,
|
||||
assetError,
|
||||
coverCreateError,
|
||||
coverSetError,
|
||||
imageCreateError,
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../../tsconfig.base.json"
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts",
|
||||
"vite/client"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"jsx": "react-jsx",
|
||||
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo"
|
||||
},
|
||||
"exclude": [
|
||||
"out-tsc",
|
||||
"dist",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx",
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.js",
|
||||
"src/**/*.jsx",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/vitest",
|
||||
"types": [
|
||||
"vitest/globals",
|
||||
"vitest/importMeta",
|
||||
"vite/client",
|
||||
"node",
|
||||
"vitest"
|
||||
],
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import * as path from 'path';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
root: __dirname,
|
||||
cacheDir: '../../../node_modules/.vite/libs/common/enums',
|
||||
plugins: [react(), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })],
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
// Configuration for building your library.
|
||||
// See: https://vitejs.dev/guide/build.html#library-mode
|
||||
build: {
|
||||
outDir: './dist',
|
||||
emptyOutDir: true,
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
lib: {
|
||||
// Could also be a dictionary or array of multiple entry points.
|
||||
entry: 'src/index.ts',
|
||||
name: '@storyteller/enums',
|
||||
fileName: 'index',
|
||||
// Change this to the formats you want to support.
|
||||
// Don't forget to update your package.json as well.
|
||||
formats: ['es' as const]
|
||||
},
|
||||
rollupOptions: {
|
||||
// External packages that should not be bundled into your library.
|
||||
external: ['react','react-dom','react/jsx-runtime']
|
||||
},
|
||||
},
|
||||
test: {
|
||||
watch: false,
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: './test-output/vitest/coverage',
|
||||
provider: 'v8' as const,
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# @storyteller/interfaces
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test @storyteller/interfaces` to execute the unit tests via [Vitest](https://vitest.dev/).
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@storyteller/interfaces",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"development": "./src/index.ts",
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export * from "./lib/Camera";
|
||||
export * from "./lib/UploadImage";
|
||||
export * from "./lib/IGenerationOptions";
|
||||
export * from "./lib/UpdateTime";
|
||||
export * from "./lib/Queue";
|
||||
export * from "./lib/MediaItem";
|
||||
export * from "./lib/UploadImage";
|
||||
export * from "./lib/UploadedState";
|
||||
@@ -0,0 +1,13 @@
|
||||
export interface Camera {
|
||||
id: string;
|
||||
label: string;
|
||||
focalLength: number;
|
||||
position: { x: number; y: number; z: number };
|
||||
rotation: { x: number; y: number; z: number };
|
||||
lookAt: { x: number; y: number; z: number };
|
||||
}
|
||||
|
||||
export interface FocalLengthDragging {
|
||||
isDragging: boolean;
|
||||
focalLength: number;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export interface IGenerationOptions {
|
||||
upscale: boolean;
|
||||
faceDetail: boolean;
|
||||
styleStrength: number;
|
||||
lipSync: boolean;
|
||||
cinematic: boolean;
|
||||
globalIpAdapterImageMediaToken: string | null;
|
||||
enginePreProcessing: boolean;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { AssetType } from "@storyteller/enums";
|
||||
|
||||
export interface MediaItem {
|
||||
version: number;
|
||||
type: AssetType;
|
||||
maybe_animation_type?: string;
|
||||
media_type?: string;
|
||||
media_id: string;
|
||||
object_uuid?: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
publicBucketPath?: string;
|
||||
length?: number;
|
||||
thumbnail?: string;
|
||||
isMine?: boolean;
|
||||
isBookmarked?: boolean;
|
||||
imageIndex?: number;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { ClipGroup, ClipType } from "@storyteller/enums";
|
||||
import { XYZ } from "@storyteller/types";
|
||||
|
||||
export interface QueueClip {
|
||||
version: number;
|
||||
type: ClipType;
|
||||
group: ClipGroup;
|
||||
object_uuid?: string;
|
||||
clip_uuid?: string;
|
||||
media_id?: string;
|
||||
name?: string;
|
||||
offset?: number;
|
||||
length?: number;
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
export interface QueueKeyframe {
|
||||
version: number;
|
||||
keyframe_uuid?: string;
|
||||
group: ClipGroup;
|
||||
object_uuid: string;
|
||||
object_name?: string;
|
||||
offset?: number;
|
||||
position: XYZ;
|
||||
rotation: XYZ;
|
||||
scale: XYZ;
|
||||
selected?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface UpdateTime {
|
||||
currentTime: number;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { UploaderState } from "./UploadedState";
|
||||
|
||||
export interface UploadImageArgs {
|
||||
title: string;
|
||||
assetFile: File;
|
||||
progressCallback: (newState: UploaderState) => void;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { UploaderStates } from "@storyteller/enums";
|
||||
|
||||
export interface UploaderState {
|
||||
status: UploaderStates;
|
||||
errorMessage?: string;
|
||||
data?: string;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../../tsconfig.base.json"
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts",
|
||||
"vite/client"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"jsx": "react-jsx",
|
||||
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo"
|
||||
},
|
||||
"exclude": [
|
||||
"out-tsc",
|
||||
"dist",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx",
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.js",
|
||||
"src/**/*.jsx",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/vitest",
|
||||
"types": [
|
||||
"vitest/globals",
|
||||
"vitest/importMeta",
|
||||
"vite/client",
|
||||
"node",
|
||||
"vitest"
|
||||
],
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import * as path from 'path';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
root: __dirname,
|
||||
cacheDir: '../../../node_modules/.vite/libs/common/interfaces',
|
||||
plugins: [react(), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })],
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
// Configuration for building your library.
|
||||
// See: https://vitejs.dev/guide/build.html#library-mode
|
||||
build: {
|
||||
outDir: './dist',
|
||||
emptyOutDir: true,
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
lib: {
|
||||
// Could also be a dictionary or array of multiple entry points.
|
||||
entry: 'src/index.ts',
|
||||
name: '@storyteller/interfaces',
|
||||
fileName: 'index',
|
||||
// Change this to the formats you want to support.
|
||||
// Don't forget to update your package.json as well.
|
||||
formats: ['es' as const]
|
||||
},
|
||||
rollupOptions: {
|
||||
// External packages that should not be bundled into your library.
|
||||
external: ['react','react-dom','react/jsx-runtime']
|
||||
},
|
||||
},
|
||||
test: {
|
||||
watch: false,
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: './test-output/vitest/coverage',
|
||||
provider: 'v8' as const,
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# @storyteller/types
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test @storyteller/types` to execute the unit tests via [Vitest](https://vitest.dev/).
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@storyteller/types",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"development": "./src/index.ts",
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './lib';
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './queue';
|
||||
export * from './toast';
|
||||
export * from './xyz';
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user