mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-03 08:57:54 +00:00
Add stylizations page
This commit is contained in:
1 parent
f20dbc88db
commit
75261f4fa0
42 files changed
+519
-216
No files matched your search
@@ -28,7 +28,7 @@ export const ButtonLink = ({
|
||||
}
|
||||
}
|
||||
const baseClassName =
|
||||
"text-sm font-medium rounded-lg px-3 py-2 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 transition-all duration-150";
|
||||
"text-sm font-medium whitespace-nowrap rounded-lg px-3 py-2 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 transition-all duration-150";
|
||||
const variantClassNames = getVariantClassNames(variant);
|
||||
const className = twMerge(baseClassName, variantClassNames, propsClassName);
|
||||
// END TODO
|
||||
@@ -36,7 +36,7 @@ export const ButtonLink = ({
|
||||
return (
|
||||
<Link {...rest}>
|
||||
<button className={className}>
|
||||
{icon && <FontAwesomeIcon className="mr-2" icon={icon} size="sm"/>}
|
||||
{icon && <FontAwesomeIcon className="mr-2" icon={icon} size="sm" />}
|
||||
{children}
|
||||
</button>
|
||||
</Link>
|
||||
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,29 @@
|
||||
import { ReactNode, useEffect, useState } from "react";
|
||||
|
||||
import Editor from "~/pages/PageEnigma/js/editor";
|
||||
|
||||
import { EngineContext } from "./EngineContext";
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const EngineProvider = ({ children }: Props) => {
|
||||
const [editor, setEditor] = useState<Editor | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setEditor((curr) => {
|
||||
if (curr !== null) {
|
||||
console.warn("Editor Engine already exist");
|
||||
return curr;
|
||||
} else {
|
||||
return new Editor();
|
||||
}
|
||||
});
|
||||
// }
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<EngineContext.Provider value={editor}>{children}</EngineContext.Provider>
|
||||
);
|
||||
};
|
||||
File renamed without changes.
@@ -9,4 +9,4 @@ export const TopBarInnerContext = createContext<{
|
||||
location: string,
|
||||
node: React.ReactNode,
|
||||
} | null)=>void;
|
||||
} | null>(null);
|
||||
} | null>(null);
|
||||
@@ -14,9 +14,10 @@ import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions";
|
||||
|
||||
interface LowerPanelPropsI {
|
||||
children: React.ReactNode;
|
||||
onStyle?: boolean;
|
||||
}
|
||||
|
||||
export const LowerPanel = ({ children }: LowerPanelPropsI) => {
|
||||
export const LowerPanel = ({ children, onStyle }: LowerPanelPropsI) => {
|
||||
const { onPointerDown } = useMouseEventsTimeline();
|
||||
|
||||
const displayHeight =
|
||||
@@ -50,18 +51,21 @@ export const LowerPanel = ({ children }: LowerPanelPropsI) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="absolute w-full cursor-ns-resize bg-ui-panel-border"
|
||||
style={{ height: 3, zIndex: 1000, bottom: displayHeight }}
|
||||
onPointerDown={onPointerDown}
|
||||
/>
|
||||
{!onStyle && (
|
||||
<div
|
||||
className="absolute w-full cursor-ns-resize bg-ui-panel-border"
|
||||
style={{ height: 3, zIndex: 1000, bottom: displayHeight }}
|
||||
onPointerDown={onPointerDown}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={[
|
||||
"absolute bottom-0",
|
||||
"w-screen overflow-auto",
|
||||
"w-screen",
|
||||
onStyle ? "overflow-x-auto overflow-y-hidden" : "overflow-auto",
|
||||
"bg-ui-panel",
|
||||
].join(" ")}
|
||||
style={{ height: displayHeight }}
|
||||
style={{ height: onStyle ? 80 : displayHeight }}
|
||||
onPointerOver={() => {
|
||||
overTimeline.value = true;
|
||||
}}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { useContext, } from "react";
|
||||
import { useLocation } from "@remix-run/react";
|
||||
|
||||
import { faChevronLeft } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
import { TopBarInnerContext } from "~/contexts/TopBarInner";
|
||||
import { ButtonLink } from "~/components";
|
||||
|
||||
import { AuthButtons } from "./AuthButtons";
|
||||
import { sceneTitle } from "~/store";
|
||||
|
||||
export const TopBar = () => {
|
||||
interface Props {
|
||||
pageName: string;
|
||||
}
|
||||
|
||||
export const TopBar = ({ pageName }: Props) => {
|
||||
const currentLocation = useLocation().pathname;
|
||||
|
||||
return (
|
||||
@@ -30,15 +33,16 @@ export const TopBar = () => {
|
||||
alt="Logo FakeYou StoryTeller.ai"
|
||||
/>
|
||||
</a>
|
||||
{currentLocation !== "/" &&
|
||||
{currentLocation !== "/" && (
|
||||
<ButtonLink to={"/"} variant="secondary" icon={faChevronLeft}>
|
||||
Back to Dashboard
|
||||
</ButtonLink>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center">
|
||||
<TopBarInner currentLocation={currentLocation}/>
|
||||
<span className="opacity-60">{pageName} /</span>
|
||||
{sceneTitle.value}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2">
|
||||
@@ -49,12 +53,3 @@ export const TopBar = () => {
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
const TopBarInner = ({currentLocation}:{currentLocation:string}) => {
|
||||
const { TopBarInner } = useContext(TopBarInnerContext) || {};
|
||||
if(!TopBarInner || TopBarInner.location !== currentLocation){
|
||||
return null;
|
||||
}
|
||||
return TopBarInner.node;
|
||||
};
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
import { useLocation } from "@remix-run/react";
|
||||
|
||||
import {
|
||||
useContext,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useContext, useEffect, useLayoutEffect, useState } from "react";
|
||||
import { TopBarInnerContext } from "~/contexts/TopBarInner";
|
||||
|
||||
|
||||
export const TopBarHelmet = (props:{
|
||||
children: React.ReactNode | null;
|
||||
})=>{
|
||||
export const TopBarHelmet = (props: { children: React.ReactNode | null }) => {
|
||||
const [showChild, setShowChild] = useState(false);
|
||||
// Wait until after client-side hydration to show
|
||||
useEffect(() => {
|
||||
@@ -23,22 +15,22 @@ export const TopBarHelmet = (props:{
|
||||
}
|
||||
|
||||
return <LazyLoadTopBarInnerContent {...props} />;
|
||||
}
|
||||
};
|
||||
|
||||
const LazyLoadTopBarInnerContent = (props:{
|
||||
const LazyLoadTopBarInnerContent = (props: {
|
||||
children: React.ReactNode | null;
|
||||
})=>{
|
||||
}) => {
|
||||
const { setTopBarInner } = useContext(TopBarInnerContext) || {};
|
||||
const location = useLocation().pathname;
|
||||
|
||||
useLayoutEffect(()=>{
|
||||
if(setTopBarInner){
|
||||
useLayoutEffect(() => {
|
||||
if (setTopBarInner) {
|
||||
setTopBarInner({
|
||||
location: location,
|
||||
node: props.children
|
||||
node: props.children,
|
||||
});
|
||||
}
|
||||
},[setTopBarInner]);
|
||||
}, [setTopBarInner]);
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -1,44 +1,26 @@
|
||||
import { PageEnigmaComponent } from "./PageEnigmaComponent";
|
||||
import { TrackProvider } from "~/pages/PageEnigma/contexts/TrackContext/TrackProvider";
|
||||
import { AppUIProvider } from "~/pages/PageEnigma/contexts/AppUiContext";
|
||||
import { EngineProvider } from "~/pages/PageEnigma/contexts/EngineProvider";
|
||||
import { DragComponent } from "~/pages/PageEnigma/comps/DragComponent/DragComponent";
|
||||
import { useEffect } from "react";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import {
|
||||
pageHeight,
|
||||
pageWidth,
|
||||
sidePanelWidth,
|
||||
timelineHeight,
|
||||
} from "~/pages/PageEnigma/store";
|
||||
import { useContext, useEffect } from "react";
|
||||
import { AppUiContext } from "~/contexts/AppUiContext";
|
||||
import { EngineContext } from "~/contexts/EngineContext";
|
||||
|
||||
export const PageEnigma = () => {
|
||||
useSignals();
|
||||
const [_, dispatchAppUiState] = useContext(AppUiContext);
|
||||
const editor = useContext(EngineContext);
|
||||
|
||||
useEffect(() => {
|
||||
function setPage() {
|
||||
// TODO address this issue with zooming
|
||||
pageHeight.value = window.innerHeight;
|
||||
pageWidth.value = window.innerWidth;
|
||||
if (editor && editor.can_initialize && dispatchAppUiState !== null) {
|
||||
console.log("initializing Editor");
|
||||
editor.initialize({
|
||||
dispatchAppUiState,
|
||||
});
|
||||
}
|
||||
timelineHeight.value = window.innerHeight * 0.25;
|
||||
sidePanelWidth.value = 443;
|
||||
|
||||
setPage();
|
||||
|
||||
window.addEventListener("resize", setPage);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", setPage);
|
||||
};
|
||||
}, []);
|
||||
}, [editor, dispatchAppUiState]);
|
||||
|
||||
return (
|
||||
<TrackProvider>
|
||||
<AppUIProvider>
|
||||
<EngineProvider>
|
||||
<PageEnigmaComponent />
|
||||
</EngineProvider>
|
||||
</AppUIProvider>
|
||||
<PageEnigmaComponent />
|
||||
<DragComponent />
|
||||
</TrackProvider>
|
||||
);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { useContext, useState } from "react";
|
||||
import { faSparkles } from "@fortawesome/pro-solid-svg-icons";
|
||||
import React, { useContext } from "react";
|
||||
|
||||
import { Button, LoadingBar, LoadingDots } from "~/components";
|
||||
import { TopBarHelmet } from "~/modules/TopBarHelmet/TopBarHelmet";
|
||||
import { LoadingBar, LoadingDots } from "~/components";
|
||||
import { SidePanel } from "~/modules/SidePanel";
|
||||
|
||||
import { Controls3D } from "./comps/Controls3D";
|
||||
@@ -13,24 +11,22 @@ import { PreviewEngineCamera } from "./comps/PreviewEngineCamera";
|
||||
import { ViewSideBySide } from "./comps/ViewSideBySide";
|
||||
import { Timeline } from "./comps/Timeline";
|
||||
|
||||
import { APPUI_VIEW_MODES } from "./reducers";
|
||||
import { APPUI_VIEW_MODES } from "../../reducers";
|
||||
import {
|
||||
timelineHeight,
|
||||
sidePanelWidth,
|
||||
pageWidth,
|
||||
sidePanelVisible,
|
||||
pageHeight,
|
||||
dndSidePanelWidth,
|
||||
dndTimelineHeight,
|
||||
} from "~/pages/PageEnigma/store";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { AppUiContext } from "~/pages/PageEnigma/contexts/AppUiContext";
|
||||
import { EngineContext } from "./contexts/EngineContext";
|
||||
import { AppUiContext } from "~/contexts/AppUiContext";
|
||||
import { pageHeight, pageWidth } from "~/store";
|
||||
import { TopBar } from "~/modules/TopBar";
|
||||
|
||||
export const PageEnigmaComponent = () => {
|
||||
useSignals();
|
||||
|
||||
const editorEngine = useContext(EngineContext);
|
||||
const [appUiState] = useContext(AppUiContext);
|
||||
|
||||
//To prevent the click event from propagating to the canvas: TODO: HANDLE THIS BETTER?
|
||||
@@ -38,14 +34,6 @@ export const PageEnigmaComponent = () => {
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
const handleGenerateMovieClick = () => {
|
||||
if (editorEngine != null) {
|
||||
editorEngine?.generateVideo();
|
||||
} else {
|
||||
console.log("Tried to generate movie but editor was null");
|
||||
}
|
||||
};
|
||||
|
||||
const dndWidth =
|
||||
dndSidePanelWidth.value > -1
|
||||
? dndSidePanelWidth.value
|
||||
@@ -60,11 +48,7 @@ export const PageEnigmaComponent = () => {
|
||||
|
||||
return (
|
||||
<div className="w-screen">
|
||||
<TopBarHelmet>
|
||||
<Button icon={faSparkles} onClick={handleGenerateMovieClick}>
|
||||
Generate Movie
|
||||
</Button>
|
||||
</TopBarHelmet>
|
||||
<TopBar pageName="Edit Scene" />
|
||||
<div
|
||||
className="relative flex w-screen"
|
||||
style={{ height: "calc(100vh - 68px)" }}
|
||||
|
||||
@@ -7,12 +7,12 @@ import {
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { AppUiContext } from "../../contexts/AppUiContext";
|
||||
import { EngineContext } from "../../contexts/EngineContext";
|
||||
import { AppUiContext } from "../../../../contexts/AppUiContext";
|
||||
import { EngineContext } from "../../../../contexts/EngineContext";
|
||||
import { Button, H5, InputVector } from "~/components";
|
||||
|
||||
import { XYZ } from "../../datastructures/common";
|
||||
import { ACTION_TYPES } from "../../reducers/appUiReducer/types";
|
||||
import { ACTION_TYPES } from "~/reducers/appUiReducer/types";
|
||||
|
||||
import { QueueNames } from "../../Queue/QueueNames";
|
||||
import Queue from "~/pages/PageEnigma/Queue/Queue";
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
faAngleRight,
|
||||
faArrowsRotate,
|
||||
faArrowsUpDownLeftRight,
|
||||
faCube,
|
||||
@@ -10,9 +11,10 @@ import {
|
||||
fa3dTorus,
|
||||
fa3dSphere,
|
||||
} from "@awesome.me/kit-fde2be5eb0/icons/kit/custom";
|
||||
import { ButtonIcon, ButtonIconSelect } from "~/components";
|
||||
import { EngineContext } from "../../contexts/EngineContext";
|
||||
import { Button, ButtonIcon, ButtonIconSelect, Link } from "~/components";
|
||||
import { EngineContext } from "../../../../contexts/EngineContext";
|
||||
import { useContext } from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
export const Controls3D = () => {
|
||||
const editorEngine = useContext(EngineContext);
|
||||
@@ -66,14 +68,18 @@ export const Controls3D = () => {
|
||||
<div className="flex justify-center">
|
||||
<div className="rounded-b-lg border-x border-b border-ui-panel-border bg-ui-controls p-2 text-white">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<ButtonIcon icon={faPlus} onClick={handlePlus} fill={true} />
|
||||
|
||||
<span className="h-4 w-0 border-l border-white/[0.15]" />
|
||||
|
||||
<ButtonIconSelect
|
||||
options={modes}
|
||||
onOptionChange={handleModeChange}
|
||||
/>
|
||||
|
||||
<span className="h-4 w-0 border-l border-white/[0.15]" />
|
||||
|
||||
<Link to="/stylization">
|
||||
<Button variant="primary">
|
||||
Stylize <FontAwesomeIcon icon={faAngleRight} />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useContext, useState } from "react";
|
||||
import { Button, Input } from "~/components";
|
||||
import { ButtonDialogue } from "~/modules/ButtonDialogue";
|
||||
import { EngineContext } from "../../contexts/EngineContext";
|
||||
import { ToasterContext } from "~/pages/PageEnigma/contexts/ToasterContext";
|
||||
import { APPUI_ACTION_TYPES } from "~/pages/PageEnigma/reducers";
|
||||
import { EngineContext } from "../../../../contexts/EngineContext";
|
||||
import { ToasterContext } from "~/contexts/ToasterContext";
|
||||
import { APPUI_ACTION_TYPES } from "app/reducers";
|
||||
|
||||
export const ControlsTopButtons = () => {
|
||||
const editorEngine = useContext(EngineContext);
|
||||
@@ -17,7 +17,7 @@ export const ControlsTopButtons = () => {
|
||||
const handleButtonSave = async () => {
|
||||
console.log(`SceneName is ${sceneName}`);
|
||||
const sceneMediaToken = await editorEngine?.saveScene(sceneName);
|
||||
if(sceneMediaToken){
|
||||
if (sceneMediaToken) {
|
||||
addToast("success", sceneMediaToken);
|
||||
}
|
||||
};
|
||||
@@ -129,9 +129,7 @@ export const ControlsTopButtons = () => {
|
||||
</ButtonDialogue>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
|
||||
</div>
|
||||
<div className="flex gap-2"></div>
|
||||
<div className="flex gap-2">
|
||||
{/* <Button onClick={handleButtonSingleFrame}>Render Single Frame</Button>
|
||||
<Button onClick={handleButtonTakeFrame}>Take Frame</Button> */}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
faPlay,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { ButtonIcon } from "~/components";
|
||||
import { EngineContext } from "../../contexts/EngineContext";
|
||||
import { EngineContext } from "../../../../contexts/EngineContext";
|
||||
|
||||
export const ControlsVideo = () => {
|
||||
const editorEngine = useContext(EngineContext);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { EngineContext } from "../../contexts/EngineContext";
|
||||
import { Button, Input } from "~/components";
|
||||
import { LoadingDotsTyping } from "~/components";
|
||||
import { EngineContext } from "../../../../contexts/EngineContext";
|
||||
import { Button, Input, LoadingDotsTyping } from "~/components";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faCameraViewfinder } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
|
||||
@@ -2,11 +2,10 @@ import { useContext, useState } from "react";
|
||||
|
||||
import { Button, ItemPicker, Label, Textarea } from "~/components";
|
||||
|
||||
import { AppUiContext } from "../../contexts/AppUiContext";
|
||||
import { APPUI_ACTION_TYPES, APPUI_VIEW_MODES } from "../../reducers";
|
||||
import { EngineContext } from "../../contexts/EngineContext";
|
||||
import { AppUiContext } from "~/contexts/AppUiContext";
|
||||
import { APPUI_ACTION_TYPES, APPUI_VIEW_MODES } from "~/reducers";
|
||||
import { EngineContext } from "~/contexts/EngineContext";
|
||||
import { ArtStyle } from "../../js/api_manager";
|
||||
import { ignoreDelete } from "~/pages/PageEnigma/store";
|
||||
|
||||
export const TabStylization = () => {
|
||||
const [appUiState, dispatchAppUiState] = useContext(AppUiContext);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { filmLength, scale, selectedItem } from "~/pages/PageEnigma/store";
|
||||
import { useMouseEventsKeyframe } from "~/pages/PageEnigma/comps/Timeline/utils/useMouseEventsKeyframe";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { useContext } from "react";
|
||||
import { ToasterContext } from "~/pages/PageEnigma/contexts/ToasterContext";
|
||||
import { ToasterContext } from "~/contexts/ToasterContext";
|
||||
|
||||
interface Props {
|
||||
keyframe: Keyframe;
|
||||
|
||||
@@ -31,8 +31,6 @@ export const useMouseEventsScrubber = () => {
|
||||
}
|
||||
};
|
||||
|
||||
console.log("scroll", currentScroll.value);
|
||||
|
||||
const onMouseMove = (event: MouseEvent) => {
|
||||
const delta = Math.round(
|
||||
(event.clientX - clientX) / 4 / scale.value + currentTime.value,
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import {
|
||||
dndTimelineHeight,
|
||||
pageHeight,
|
||||
timelineHeight,
|
||||
} from "~/pages/PageEnigma/store";
|
||||
import { dndTimelineHeight, timelineHeight } from "~/pages/PageEnigma/store";
|
||||
import { pageHeight } from "~/store";
|
||||
|
||||
export const useMouseEventsTimeline = () => {
|
||||
const [isActive, setIsActive] = useState(false);
|
||||
|
||||
@@ -1,42 +1,45 @@
|
||||
import { useEffect, useState, useContext } from "react";
|
||||
|
||||
import { LoadingDotsBricks } from "~/components";
|
||||
import { EngineContext } from "../../contexts/EngineContext";
|
||||
import { EngineContext } from "../../../../contexts/EngineContext";
|
||||
|
||||
export const ViewSideBySide = ()=>{
|
||||
export const ViewSideBySide = () => {
|
||||
const [showLoader, setShowLoader] = useState(true);
|
||||
const editorEngine = useContext(EngineContext);
|
||||
|
||||
useEffect(()=>{
|
||||
useEffect(() => {
|
||||
editorEngine?.generateFrame();
|
||||
setTimeout(()=>setShowLoader(false), 200);
|
||||
},[ ]);
|
||||
setTimeout(() => setShowLoader(false), 200);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
id="view-side-by-side"
|
||||
className="absolute top-0 left-0 w-full h-full flex justify-center items-center gap-2 p-2"
|
||||
className="absolute left-0 top-0 flex h-full w-full items-center justify-center gap-2 p-2"
|
||||
>
|
||||
<div className="flex flex-col justify-center items-center">
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<div className="text-white">
|
||||
<label>Raw Preview</label>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<img className="aspect-video max-h-150 border" id="raw-preview"></img>
|
||||
<img className="max-h-150 aspect-video border" id="raw-preview"></img>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col justify-center items-center">
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<div className="text-white">
|
||||
<label>Stylized Preview</label>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<img className="aspect-video max-h-150 border" id="stylized-preview"></img>
|
||||
<img
|
||||
className="max-h-150 aspect-video border"
|
||||
id="stylized-preview"
|
||||
></img>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute top-0 left-0 w-full h-full">
|
||||
<LoadingDotsBricks isShowing={showLoader}/>
|
||||
<div className="absolute left-0 top-0 h-full w-full">
|
||||
<LoadingDotsBricks isShowing={showLoader} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -1,47 +0,0 @@
|
||||
import { ReactNode, useContext, useEffect, useState } from "react";
|
||||
|
||||
import Editor from "~/pages/PageEnigma/js/editor";
|
||||
|
||||
import { EngineContext } from "./EngineContext";
|
||||
import { AppUiContext } from "./AppUiContext";
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const EngineProvider = ({ children }: Props) => {
|
||||
const [editor, setEditor] = useState<Editor | null>(null);
|
||||
const [appUiState, dispatchAppUiState] = useContext(AppUiContext);
|
||||
// To talk to react land.
|
||||
|
||||
useEffect(() => {
|
||||
//componentDidMount
|
||||
if (document.getElementById("video-scene") === null) {
|
||||
console.error(
|
||||
'Editor Engine need a target canvas with the id "video-scene"',
|
||||
);
|
||||
} else {
|
||||
setEditor((curr) => {
|
||||
if (curr !== null) {
|
||||
console.warn("Editor Engine already exist");
|
||||
return curr;
|
||||
} else {
|
||||
return new Editor();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (editor && editor.can_initialize && dispatchAppUiState !== null) {
|
||||
console.log("initializing Editor");
|
||||
editor.initialize({
|
||||
dispatchAppUiState,
|
||||
});
|
||||
}
|
||||
}, [editor, dispatchAppUiState]);
|
||||
|
||||
return (
|
||||
<EngineContext.Provider value={editor}>{children}</EngineContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
deleteCharacterKeyframe,
|
||||
deleteObjectKeyframe,
|
||||
} from "~/pages/PageEnigma/store";
|
||||
import { ToasterContext } from "~/pages/PageEnigma/contexts/ToasterContext";
|
||||
import { ToasterContext } from "~/contexts/ToasterContext";
|
||||
|
||||
const ADD_KEYFRAME: Record<
|
||||
ClipGroup,
|
||||
|
||||
@@ -21,7 +21,7 @@ import { LipSyncEngine } from "./lip_sync_engine.js";
|
||||
import { AnimationEngine } from "./animation_engine.js";
|
||||
|
||||
import { PointerLockControls } from "three/addons/controls/PointerLockControls.js";
|
||||
import { APPUI_ACTION_TYPES } from "../reducers";
|
||||
import { APPUI_ACTION_TYPES } from "../../../reducers";
|
||||
import { ClipGroup } from "~/pages/PageEnigma/models/track";
|
||||
|
||||
import { XYZ } from "../datastructures/common";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { computed, signal } from "@preact/signals-core";
|
||||
import { AssetFilterOption, Tab } from "~/pages/PageEnigma/models";
|
||||
import { pageHeight, timelineHeight } from "~/pages/PageEnigma/store/sizing";
|
||||
import { timelineHeight } from "~/pages/PageEnigma/store/sizing";
|
||||
import { pageHeight } from "~/store";
|
||||
|
||||
export const sidePanelVisible = signal(true);
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@ import { signal, computed } from "@preact/signals-core";
|
||||
import { characterGroups } from "~/pages/PageEnigma/store/characterGroups";
|
||||
import { objectGroup } from "~/pages/PageEnigma/store/objectGroup";
|
||||
|
||||
export const pageHeight = signal(0);
|
||||
export const pageWidth = signal(0);
|
||||
|
||||
// timeline
|
||||
export const scale = signal(1);
|
||||
export const filmLength = signal(12);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { PageStylizationComponent } from "~/pages/PageStylization/PageStylizationComponent";
|
||||
import { useContext, useEffect } from "react";
|
||||
import { AppUiContext } from "~/contexts/AppUiContext";
|
||||
import { EngineContext } from "~/contexts/EngineContext";
|
||||
|
||||
export const PageStylization = () => {
|
||||
useSignals();
|
||||
|
||||
const [, dispatchAppUiState] = useContext(AppUiContext);
|
||||
const editor = useContext(EngineContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (editor && editor.can_initialize && dispatchAppUiState !== null) {
|
||||
console.log("initializing Editor");
|
||||
editor.initialize({
|
||||
dispatchAppUiState,
|
||||
});
|
||||
}
|
||||
}, [editor, dispatchAppUiState]);
|
||||
|
||||
return <PageStylizationComponent />;
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faAngleLeft } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { Button, Link } from "~/components";
|
||||
import { StyleSelection } from "~/pages/PageStylization/comps/StyleSelection";
|
||||
import { LowerPanel } from "~/modules/LowerPanel";
|
||||
import { TimerGrid } from "~/pages/PageEnigma/comps/TimerGrid/TimerGrid";
|
||||
import { Scrubber } from "~/pages/PageEnigma/comps/Timeline/Scrubber";
|
||||
import { PreviewImages } from "~/pages/PageStylization/comps/PreviewImages";
|
||||
import { TopBar } from "~/modules/TopBar";
|
||||
|
||||
export const PageStylizationComponent = () => {
|
||||
useSignals();
|
||||
|
||||
return (
|
||||
<div className="w-screen">
|
||||
<TopBar pageName="Stylization" />
|
||||
<div className="flex w-full justify-center">
|
||||
<div className="bg-ui-controls p-2">
|
||||
<Link to="/idealenigma">
|
||||
<Button variant="action">
|
||||
<FontAwesomeIcon icon={faAngleLeft} />
|
||||
Back to Scene
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 flex flex-col items-center gap-4">
|
||||
<PreviewImages />
|
||||
<StyleSelection />
|
||||
</div>
|
||||
<div className="fixed bottom-0 left-0 w-full">
|
||||
<div className="flex h-[62px] w-full items-center justify-center gap-5 bg-ui-panel">
|
||||
<div>
|
||||
<Button variant="action">Update Preview</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Button variant="primary">Generate Movie</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative flex h-[80px] w-full gap-5 border-t border-t-action-600 bg-ui-panel">
|
||||
<LowerPanel onStyle>
|
||||
<TimerGrid />
|
||||
<Scrubber />
|
||||
</LowerPanel>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faArrowRight } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { pageHeight, pageWidth } from "~/store";
|
||||
|
||||
export const PreviewImages = () => {
|
||||
useSignals();
|
||||
|
||||
const imageWidth1 = pageWidth.value > 1340 ? 630 : (pageWidth.value - 80) / 2;
|
||||
const imageHeight1 = imageWidth1 * 0.56;
|
||||
const imageHeight2 = pageHeight.value > 1138 ? 354 : pageHeight.value - 620;
|
||||
const imageWidth2 = imageHeight2 / 0.56;
|
||||
|
||||
const imageWidth = imageHeight1 < imageHeight2 ? imageWidth1 : imageWidth2;
|
||||
const imageHeight = imageHeight1 < imageHeight2 ? imageHeight1 : imageHeight2;
|
||||
|
||||
const textClasses = [
|
||||
"flex items-center justify-center",
|
||||
"w-full rounded-t-lg bg-ui-controls h-[34px]",
|
||||
"text-sm font-semibold",
|
||||
].join(" ");
|
||||
|
||||
return (
|
||||
<div className="flex justify-center gap-2">
|
||||
<div className="flex flex-col" style={{ width: imageWidth }}>
|
||||
<div className={textClasses}>Raw Preview</div>
|
||||
<div
|
||||
className="block w-full rounded-b-lg bg-gray-300"
|
||||
style={{ height: imageHeight }}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex w-[40px] flex-col justify-center">
|
||||
<FontAwesomeIcon icon={faArrowRight} className="text-3xl opacity-60" />
|
||||
</div>
|
||||
<div className="flex flex-col" style={{ width: imageWidth }}>
|
||||
<div className={textClasses}>Styled Preview</div>
|
||||
<div
|
||||
className="block w-full rounded-b-lg bg-gray-300"
|
||||
style={{ height: imageHeight }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,123 @@
|
||||
import { useContext, useState } from "react";
|
||||
|
||||
import { ButtonIcon, ItemPicker, Label, Textarea } from "~/components";
|
||||
|
||||
import { EngineContext } from "~/contexts/EngineContext";
|
||||
import { ArtStyle } from "~/pages/PageEnigma/js/api_manager";
|
||||
import { faAngleLeft, faAngleRight } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { styleList } from "~/pages/PageStylization/styleList";
|
||||
|
||||
export const StyleSelection = () => {
|
||||
const [selection, setSelection] = useState<ArtStyle>(styleList[0].type);
|
||||
const [scrollPosition, setScrollPosition] = useState(0);
|
||||
|
||||
const editorEngine = useContext(EngineContext);
|
||||
|
||||
const handlePickingStylizer = (picked: ArtStyle) => {
|
||||
console.log(`Picked style: ${picked}`);
|
||||
setSelection(picked);
|
||||
if (editorEngine === null) {
|
||||
console.log("Editor is null");
|
||||
return;
|
||||
}
|
||||
editorEngine.art_style = picked;
|
||||
};
|
||||
|
||||
const onChangeHandlerNegative = (
|
||||
e: React.ChangeEvent<HTMLTextAreaElement>,
|
||||
) => {
|
||||
if (editorEngine == null) {
|
||||
console.log("Editor is null");
|
||||
return;
|
||||
}
|
||||
editorEngine.negative_prompt = e.target.value;
|
||||
console.log(e.target.value);
|
||||
};
|
||||
|
||||
const onChangeHandlerPositive = (
|
||||
e: React.ChangeEvent<HTMLTextAreaElement>,
|
||||
) => {
|
||||
if (editorEngine == null) {
|
||||
console.log("Editor is null");
|
||||
return;
|
||||
}
|
||||
editorEngine.positive_prompt = e.target.value;
|
||||
console.log(e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-[280px] w-[800px] flex-col gap-3 rounded-lg bg-ui-panel p-4">
|
||||
<div className="flex w-full flex-col">
|
||||
<Label>Select Base Style</Label>
|
||||
<div
|
||||
className="relative overflow-x-hidden"
|
||||
style={{ width: 768, height: 80 }}
|
||||
>
|
||||
<div
|
||||
className="absolute flex gap-2"
|
||||
style={{
|
||||
width: styleList.length * 84 + 26,
|
||||
left: scrollPosition * -86,
|
||||
top: 0,
|
||||
}}
|
||||
>
|
||||
{styleList.map((style) => (
|
||||
<ItemPicker
|
||||
key={style.type}
|
||||
label={style.label}
|
||||
type={style.type}
|
||||
selected={selection === style.type}
|
||||
onSelected={handlePickingStylizer}
|
||||
src={style.image}
|
||||
width={80}
|
||||
height={80}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{scrollPosition > 0 && (
|
||||
<div className="absolute left-3 top-5">
|
||||
<ButtonIcon
|
||||
icon={faAngleLeft}
|
||||
onClick={() =>
|
||||
setScrollPosition(Math.max(scrollPosition - 6, 0))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{scrollPosition < styleList.length - 9 && (
|
||||
<div className="absolute right-3 top-5">
|
||||
<ButtonIcon
|
||||
icon={faAngleRight}
|
||||
onClick={() =>
|
||||
setScrollPosition(
|
||||
Math.min(scrollPosition + 6, styleList.length - 9),
|
||||
)
|
||||
}
|
||||
fill
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full gap-4">
|
||||
<Textarea
|
||||
label="Positive Prompt"
|
||||
className="h-24 w-[376px]"
|
||||
name="positive-prompt"
|
||||
placeholder="Type here to describe your scene"
|
||||
onChange={onChangeHandlerPositive}
|
||||
resize="none"
|
||||
/>
|
||||
<Textarea
|
||||
label="Negative Prompt"
|
||||
className="h-24 w-[376px]"
|
||||
name="negative-prompt"
|
||||
placeholder="Type here to filter out the things you don't want in the scene"
|
||||
onChange={onChangeHandlerNegative}
|
||||
resize="none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./PageStylization";
|
||||
@@ -0,0 +1,84 @@
|
||||
import { ArtStyle } from "~/pages/PageEnigma/js/api_manager";
|
||||
|
||||
export const styleList = [
|
||||
{
|
||||
label: "1",
|
||||
type: ArtStyle.Anime2_5D,
|
||||
image: "/resources/avatars/0.webp",
|
||||
},
|
||||
{
|
||||
label: "2",
|
||||
type: ArtStyle.Anime2DFlat,
|
||||
image: "/resources/avatars/1.webp",
|
||||
},
|
||||
{
|
||||
label: "3",
|
||||
type: ArtStyle.Cartoon3D,
|
||||
image: "/resources/avatars/2.webp",
|
||||
},
|
||||
{
|
||||
label: "4",
|
||||
type: ArtStyle.ComicBook,
|
||||
image: "/resources/avatars/3.webp",
|
||||
},
|
||||
{
|
||||
label: "5",
|
||||
type: ArtStyle.AnimeGhibli,
|
||||
image: "/resources/avatars/4.webp",
|
||||
},
|
||||
{
|
||||
label: "6",
|
||||
type: ArtStyle.InkPunk,
|
||||
image: "/resources/avatars/5.webp",
|
||||
},
|
||||
{
|
||||
label: "7",
|
||||
type: ArtStyle.InkSplash,
|
||||
image: "/resources/avatars/6.webp",
|
||||
},
|
||||
{
|
||||
label: "8",
|
||||
type: ArtStyle.InkBWStyle,
|
||||
image: "/resources/avatars/7.webp",
|
||||
},
|
||||
{
|
||||
label: "9",
|
||||
type: ArtStyle.JojoStyle,
|
||||
image: "/resources/avatars/8.webp",
|
||||
},
|
||||
{
|
||||
label: "10",
|
||||
type: ArtStyle.PaperOrigami,
|
||||
image: "/resources/avatars/9.webp",
|
||||
},
|
||||
{
|
||||
label: "11",
|
||||
type: ArtStyle.PixelArt,
|
||||
image: "/resources/avatars/10.webp",
|
||||
},
|
||||
{
|
||||
label: "12",
|
||||
type: ArtStyle.PopArt,
|
||||
image: "/resources/avatars/11.webp",
|
||||
},
|
||||
{
|
||||
label: "13",
|
||||
type: ArtStyle.Realistic1,
|
||||
image: "/resources/avatars/12.webp",
|
||||
},
|
||||
{
|
||||
label: "14",
|
||||
type: ArtStyle.Realistic2,
|
||||
image: "/resources/avatars/13.webp",
|
||||
},
|
||||
{
|
||||
label: "15",
|
||||
type: ArtStyle.AnimeRetroNeon,
|
||||
image: "/resources/avatars/14.webp",
|
||||
},
|
||||
{
|
||||
label: "16",
|
||||
type: ArtStyle.AnimeStandard,
|
||||
image: "/resources/avatars/15.webp",
|
||||
},
|
||||
];
|
||||
File renamed without changes.
File renamed without changes.
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { Simple3DVector } from "../../datastructures/common";
|
||||
import { AssetType, ClipGroup } from "../../models";
|
||||
import { Simple3DVector } from "../../pages/PageEnigma/datastructures/common";
|
||||
import { AssetType, ClipGroup } from "../../pages/PageEnigma/models";
|
||||
export enum VIEW_MODES {
|
||||
EDITOR = "editor",
|
||||
SIDE_BY_SIDE = "side-by-side",
|
||||
File renamed without changes.
File renamed without changes.
+33
-13
@@ -18,13 +18,18 @@ import { LoadingDotsBricks } from "~/components";
|
||||
import { TopBar } from "./modules/TopBar";
|
||||
import { TopBarInnerContext } from "~/contexts/TopBarInner";
|
||||
import { AuthenticationProvider } from "./contexts/Authentication";
|
||||
import { ToasterProvider } from "~/pages/PageEnigma/contexts/ToasterContext";
|
||||
import { ToasterProvider } from "~/contexts/ToasterContext";
|
||||
|
||||
// The following import prevents a Font Awesome icon server-side rendering bug,
|
||||
// where the icons flash from a very large icon down to a properly sized one:
|
||||
import '@fortawesome/fontawesome-svg-core/styles.css';
|
||||
import "@fortawesome/fontawesome-svg-core/styles.css";
|
||||
// Prevent fontawesome from adding its CSS since we did it manually above:
|
||||
import { config } from '@fortawesome/fontawesome-svg-core';
|
||||
import { config } from "@fortawesome/fontawesome-svg-core";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { pageHeight, pageWidth } from "~/store";
|
||||
import { sidePanelWidth, timelineHeight } from "~/pages/PageEnigma/store";
|
||||
import { EngineProvider } from "~/contexts/EngineProvider";
|
||||
import { AppUIProvider } from "~/contexts/AppUiContext";
|
||||
config.autoAddCss = false; /* eslint-disable import/first */
|
||||
|
||||
export const links: LinksFunction = () => [
|
||||
@@ -65,6 +70,25 @@ export default function App() {
|
||||
setTimeout(() => setShowLoader(false), 2500);
|
||||
}, []);
|
||||
|
||||
useSignals();
|
||||
useEffect(() => {
|
||||
function setPage() {
|
||||
// TODO address this issue with zooming
|
||||
pageHeight.value = window.innerHeight;
|
||||
pageWidth.value = window.innerWidth;
|
||||
}
|
||||
timelineHeight.value = window.innerHeight * 0.25;
|
||||
sidePanelWidth.value = 443;
|
||||
|
||||
setPage();
|
||||
|
||||
window.addEventListener("resize", setPage);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", setPage);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -78,16 +102,12 @@ export default function App() {
|
||||
<CookiesProvider defaultSetOptions={{ path: "/" }}>
|
||||
<AuthenticationProvider>
|
||||
<ToasterProvider>
|
||||
<TopBarInnerContext.Provider
|
||||
value={{
|
||||
TopBarInner: topBarInnerComponent,
|
||||
setTopBarInner: setTopBarInnerComponent,
|
||||
}}
|
||||
>
|
||||
<div className="topbar-spacer" />
|
||||
<Outlet />
|
||||
<TopBar />
|
||||
</TopBarInnerContext.Provider>
|
||||
<AppUIProvider>
|
||||
<EngineProvider>
|
||||
<div className="topbar-spacer" />
|
||||
<Outlet />
|
||||
</EngineProvider>
|
||||
</AppUIProvider>
|
||||
</ToasterProvider>
|
||||
</AuthenticationProvider>
|
||||
</CookiesProvider>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { HeadersFunction } from "@remix-run/deno";
|
||||
import { withProtectionRoute } from "~/modules/withProtectedRoute";
|
||||
import { PageStylization } from "~/pages/PageStylization";
|
||||
|
||||
export const headers: HeadersFunction = () => ({
|
||||
"Cross-Origin-Embedder-Policy": "require-corp",
|
||||
"Cross-Origin-Opener-Policy": "same-origin",
|
||||
});
|
||||
|
||||
const Stylization = withProtectionRoute(() => <PageStylization />);
|
||||
export default Stylization;
|
||||
@@ -1,8 +1,11 @@
|
||||
|
||||
import type { MetaFunction } from "@remix-run/deno";
|
||||
import { TopBarHelmet } from "~/modules/TopBarHelmet";
|
||||
|
||||
import sonic from "./_assets/sonic-the-hedgehog-classic-sonic.gif";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { useEffect } from "react";
|
||||
import { pageHeight, pageWidth } from "~/store";
|
||||
import { sidePanelWidth, timelineHeight } from "~/pages/PageEnigma/store";
|
||||
|
||||
export const meta: MetaFunction = () => {
|
||||
return [
|
||||
@@ -20,10 +23,10 @@ export default function Index() {
|
||||
src={sonic}
|
||||
className="
|
||||
fixed
|
||||
h-screen w-screen
|
||||
top-0 left-0
|
||||
left-0 top-0
|
||||
-z-10 h-screen
|
||||
w-screen
|
||||
object-cover
|
||||
-z-10
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { signal } from "@preact/signals-core";
|
||||
|
||||
export const sceneTitle = signal("Home Office");
|
||||
|
||||
export const pageHeight = signal(0);
|
||||
export const pageWidth = signal(0);
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./global";
|
||||
Reference in new issue
Block a user