Merge pull request #145 from storytold/stable-styling

Stable styling
This commit is contained in:
ArEnSc authored and GitHub committed 2024-04-11 00:21:25 -04:00
commit 60229c1da5
47 files changed
+759 -416

No files matched your search

+2 -2
View File
@@ -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.
+29
View File
@@ -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>
);
};
+1 -1
View File
@@ -9,4 +9,4 @@ export const TopBarInnerContext = createContext<{
location: string,
node: React.ReactNode,
} | null)=>void;
} | null>(null);
} | null>(null);
+12 -8
View File
@@ -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;
}}
+10 -15
View File
@@ -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}&nbsp;/</span>&nbsp;
{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;
};
+10 -18
View File
@@ -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;
}
};
+140
View File
@@ -0,0 +1,140 @@
import React, { useContext } from "react";
import { LoadingBar, LoadingDots } from "~/components";
import { SidePanel } from "~/modules/SidePanel";
import { Controls3D } from "./comps/Controls3D";
import { ControlsTopButtons } from "./comps/ControlsTopButtons";
import { ControlsVideo } from "./comps/ControlsVideo";
import { ControlPanelSceneObject } from "./comps/ControlPanelSceneObject";
import { PreviewEngineCamera } from "./comps/PreviewEngineCamera";
import { ViewSideBySide } from "./comps/ViewSideBySide";
import { Timeline } from "./comps/Timeline";
import { APPUI_VIEW_MODES } from "../../reducers";
import {
timelineHeight,
sidePanelWidth,
sidePanelVisible,
dndSidePanelWidth,
dndTimelineHeight,
} from "~/pages/PageEnigma/store";
import { useSignals } from "@preact/signals-react/runtime";
import { AppUiContext } from "~/contexts/AppUiContext";
import { pageHeight, pageWidth } from "~/store";
import { TopBar } from "~/modules/TopBar";
import { DialogueTTS } from "./comps/DialogueTTS/DialogueTTS";
interface Props {
setPage: (page: string) => void;
}
export const PageEditor = ({ setPage }: Props) => {
useSignals();
const [appUiState] = useContext(AppUiContext);
//To prevent the click event from propagating to the canvas: TODO: HANDLE THIS BETTER?
const handleOverlayClick = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation();
};
const dndWidth =
dndSidePanelWidth.value > -1
? dndSidePanelWidth.value
: sidePanelWidth.value;
const width = sidePanelVisible.value
? pageWidth.value - dndWidth - 66
: pageWidth.value - 66;
const height =
dndTimelineHeight.value > -1
? pageHeight.value - dndTimelineHeight.value - 68
: pageHeight.value - timelineHeight.value - 68;
return (
<div className="w-screen">
<TopBar pageName="Edit Scene" />
<div
className="relative flex w-screen"
style={{ height: "calc(100vh - 68px)" }}
>
{/* Engine section/side panel */}
<div
id="engine-n-panels-wrapper"
className="flex"
style={{
height,
width,
}}
>
<div className="relative w-full overflow-hidden bg-transparent">
<div
className={
appUiState.viewMode === APPUI_VIEW_MODES.SIDE_BY_SIDE
? "invisible"
: ""
}
>
<canvas id="video-scene" width="1280px" height="720px" />
{/* Top controls */}
<div
className="absolute left-0 top-0 w-full"
onClick={handleOverlayClick}
>
<div className="grid grid-cols-3 gap-4">
<ControlsTopButtons />
<Controls3D setPage={setPage} />
</div>
</div>
{/* Bottom controls */}
<div
className="absolute bottom-0 left-0"
style={{
width:
pageWidth.value -
(sidePanelVisible.value ? sidePanelWidth.value : 0) -
84,
}}
onClick={handleOverlayClick}
>
<PreviewEngineCamera />
<ControlsVideo />
<ControlPanelSceneObject />
<DialogueTTS />
</div>
</div>
{appUiState.viewMode === APPUI_VIEW_MODES.SIDE_BY_SIDE && (
<ViewSideBySide />
)}
<LoadingDots
className="absolute left-0 top-0"
isShowing={appUiState.showEditorLoader.isShowing}
type="bricks"
message={appUiState.showEditorLoader.message}
/>
<LoadingBar
id="editor-loading-bar"
wrapperClassName="absolute top-0 left-0"
innerWrapperClassName="max-w-screen-sm"
isShowing={appUiState.showEditorLoadingBar.isShowing}
message={appUiState.showEditorLoadingBar.message}
label={appUiState.showEditorLoadingBar.label}
progress={appUiState.showEditorLoadingBar.progress}
/>
</div>
</div>
{/* Side panel */}
<div onClick={handleOverlayClick}>
<SidePanel />
</div>
</div>
{/* Timeline */}
<div onClick={handleOverlayClick}>
<Timeline />
</div>
</div>
);
};
+5 -37
View File
@@ -1,48 +1,16 @@
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 { EngineProvider } from "~/contexts/EngineProvider";
import { useInferenceJobManager } from "~/hooks";
import { useEffect } from "react";
import { useSignals } from "@preact/signals-react/runtime";
import {
pageHeight,
pageWidth,
sidePanelWidth,
timelineHeight,
} from "~/pages/PageEnigma/store";
export const PageEnigma = () => {
useSignals();
useInferenceJobManager();
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 (
<TrackProvider>
<AppUIProvider>
<EngineProvider>
<PageEnigmaComponent />
</EngineProvider>
</AppUIProvider>
<DragComponent />
<EngineProvider>
<PageEnigmaComponent />
<DragComponent />
</EngineProvider>
</TrackProvider>
);
};
+21 -138
View File
@@ -1,152 +1,35 @@
import { useContext, useState } from "react";
import { faSparkles } from "@fortawesome/pro-solid-svg-icons";
import { useContext, useEffect, useState } from "react";
import { Button, LoadingBar, LoadingDots } from "~/components";
import { TopBarHelmet } from "~/modules/TopBarHelmet/TopBarHelmet";
import { SidePanel } from "~/modules/SidePanel";
import { Controls3D } from "./comps/Controls3D";
import { ControlsTopButtons } from "./comps/ControlsTopButtons";
import { ControlsVideo } from "./comps/ControlsVideo";
import { ControlPanelSceneObject } from "./comps/ControlPanelSceneObject";
import { DialogueTTS } from "./comps/DialogueTTS/DialogueTTS";
import { PreviewEngineCamera } from "./comps/PreviewEngineCamera";
import { ViewSideBySide } from "./comps/ViewSideBySide";
import { Timeline } from "./comps/Timeline";
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 { EngineContext } from "~/contexts/EngineContext";
import { PageEditor } from "~/pages/PageEnigma/PageEditor";
import { PageStyling } from "~/pages/PageEnigma/PageStyling";
export const PageEnigmaComponent = () => {
useSignals();
const editorEngine = useContext(EngineContext);
const [appUiState] = useContext(AppUiContext);
const [, dispatchAppUiState] = useContext(AppUiContext);
const editor = useContext(EngineContext);
const [page, setPage] = useState("edit");
//To prevent the click event from propagating to the canvas: TODO: HANDLE THIS BETTER?
const handleOverlayClick = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation();
};
const handleGenerateMovieClick = () => {
if (editorEngine != null) {
editorEngine?.generateVideo();
} else {
console.log("Tried to generate movie but editor was null");
useEffect(() => {
if (editor && editor.can_initialize && dispatchAppUiState !== null) {
console.log("initializing Editor");
editor.initialize({
dispatchAppUiState,
});
}
};
const dndWidth =
dndSidePanelWidth.value > -1
? dndSidePanelWidth.value
: sidePanelWidth.value;
const width = sidePanelVisible.value
? pageWidth.value - dndWidth - 66
: pageWidth.value - 66;
const height =
dndTimelineHeight.value > -1
? pageHeight.value - dndTimelineHeight.value - 68
: pageHeight.value - timelineHeight.value - 68;
}, [editor, dispatchAppUiState]);
return (
<div className="w-screen">
<TopBarHelmet>
<Button icon={faSparkles} onClick={handleGenerateMovieClick}>
Generate Movie
</Button>
</TopBarHelmet>
<div
className="relative flex w-screen"
style={{ height: "calc(100vh - 68px)" }}
>
{/* Engine section/side panel */}
<div
id="engine-n-panels-wrapper"
className="flex"
style={{
height,
width,
}}
>
<div className="relative w-full overflow-hidden bg-transparent">
<div
className={
appUiState.viewMode === APPUI_VIEW_MODES.SIDE_BY_SIDE
? "invisible"
: ""
}
>
<canvas id="video-scene" width="1280px" height="720px" />
{/* Top controls */}
<div
className="absolute left-0 top-0 w-full"
onClick={handleOverlayClick}
>
<div className="grid grid-cols-3 gap-4">
<ControlsTopButtons />
<Controls3D />
</div>
</div>
{/* Bottom controls */}
<div
className="absolute bottom-0 left-0"
style={{
width:
pageWidth.value -
(sidePanelVisible.value ? sidePanelWidth.value : 0) -
84,
}}
onClick={handleOverlayClick}
>
<PreviewEngineCamera />
<ControlsVideo />
<ControlPanelSceneObject />
<DialogueTTS />
</div>
</div>
{appUiState.viewMode === APPUI_VIEW_MODES.SIDE_BY_SIDE && (
<ViewSideBySide />
)}
<LoadingDots
className="absolute left-0 top-0"
isShowing={appUiState.showEditorLoader.isShowing}
type="bricks"
message={appUiState.showEditorLoader.message}
/>
<LoadingBar
id="editor-loading-bar"
wrapperClassName="absolute top-0 left-0"
innerWrapperClassName="max-w-screen-sm"
isShowing={appUiState.showEditorLoadingBar.isShowing}
message={appUiState.showEditorLoadingBar.message}
label={appUiState.showEditorLoadingBar.label}
progress={appUiState.showEditorLoadingBar.progress}
/>
</div>
</div>
{/* Side panel */}
<div onClick={handleOverlayClick}>
<SidePanel />
</div>
<>
<div className={page === "edit" ? "visible" : "hidden"}>
<PageEditor setPage={setPage} />
</div>
{/* Timeline */}
<div onClick={handleOverlayClick}>
<Timeline />
<div className={page === "style" ? "visible" : "hidden"}>
<PageStyling setPage={setPage} />
</div>
</div>
</>
);
};
+68
View File
@@ -0,0 +1,68 @@
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/PageEnigma/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/PageEnigma/comps/PreviewImages";
import { TopBar } from "~/modules/TopBar";
import { useContext } from "react";
import { EngineContext } from "~/contexts/EngineContext";
interface Props {
setPage: (page: string) => void;
}
export const PageStyling = ({ setPage }: Props) => {
useSignals();
const editorEngine = useContext(EngineContext);
const generateFrame = async () => {
await editorEngine?.generateFrame();
};
const generateMovie = async () => {
await editorEngine?.generateVideo();
};
const switchEdit = () => {
editorEngine?.switchEdit();
setPage("edit");
}
return (
<div className="w-screen">
<TopBar pageName="Stylization" />
<div className="flex w-full justify-center">
<div className="bg-ui-controls p-2">
<Button variant="action" onClick={() => switchEdit()}>
<FontAwesomeIcon icon={faAngleLeft} />
Back to Scene
</Button>
</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" onClick={generateFrame}>Update Preview</Button>
</div>
<div>
<Button variant="primary" onClick={generateMovie}>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>
);
};
@@ -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,16 +11,18 @@ 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 = () => {
interface Props {
setPage: (page: string) => void;
}
export const Controls3D = ({ setPage }: Props) => {
const editorEngine = useContext(EngineContext);
const handlePlus = () => {
console.log("Controls 3D: Plus button clicked");
};
const handleMoveArrows = () => {
if (!editorEngine) {
return;
@@ -55,6 +58,11 @@ export const Controls3D = () => {
}
};
const changeStylize = () => {
editorEngine?.switchPreview();
setPage("style");
}
const modes = [
{ value: "move", icon: faArrowsUpDownLeftRight },
{ value: "rotate", icon: faArrowsRotate },
@@ -66,14 +74,16 @@ 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} hoverFill={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]" />
<Button variant="primary" onClick={() => changeStylize()}>
Stylize <FontAwesomeIcon icon={faAngleRight} />
</Button>
</div>
</div>
</div>
@@ -1,9 +1,9 @@
import { useContext, useState } from "react";
import { Input } from "~/components";
import { ButtonDialogue } from "~/modules/ButtonDialogue";
import { EngineContext } from "../../contexts/EngineContext";
import { ToasterContext } from "~/pages/PageEnigma/contexts/ToasterContext";
// import { APPUI_ACTION_TYPES } from "~/pages/PageEnigma/reducers";
import { EngineContext } from "../../../../contexts/EngineContext";
import { ToasterContext } from "~/contexts/ToasterContext";
// import { APPUI_ACTION_TYPES } from "app/reducers";
import { TestFeaturesButtons } from "./TestFeaturesButtons";
@@ -20,7 +20,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);
}
};
@@ -1,9 +1,9 @@
import { useContext } from "react";
import { Button, } from "~/components";
import { ClipGroup } from "~/pages/PageEnigma/models/track";
import { APPUI_ACTION_TYPES } from "../../reducers";
import { AppUiContext } from "../../contexts/AppUiContext";
import { EngineContext } from "../../contexts/EngineContext";
import { APPUI_ACTION_TYPES } from "~/reducers";
import { AppUiContext } from "~/contexts/AppUiContext";
import { EngineContext } from "~/contexts/EngineContext";
export const TestFeaturesButtons = ({debug}:{debug:boolean})=>{
if (!debug) return null;
@@ -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);
@@ -4,12 +4,12 @@ import { v4 as uuidv4 } from "uuid";
import {
faVolume,
faShuffle,
faPlay,
// faPlay,
faBrainCircuit,
} from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { AppUiContext } from "../../contexts/AppUiContext";
import { APPUI_ACTION_TYPES } from "../../reducers";
import { AppUiContext } from "~/contexts/AppUiContext";
import { APPUI_ACTION_TYPES } from "~/reducers";
import {
Button,
H2,
@@ -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";
@@ -0,0 +1,52 @@
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"
style={{ height: imageHeight }}
>
<canvas
id="raw-preview"
width={imageWidth}
height={imageHeight}
/>
</div>
</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"
style={{ height: imageHeight }}
>
<img id="styled-preview" width={imageWidth} height={imageHeight} />
</div>
</div>
</div>
);
};
@@ -3,9 +3,9 @@ import { twMerge } from "tailwind-merge";
import { useSignals, useSignalEffect } from "@preact/signals-react/runtime";
import { faCirclePlus, faArrowsRotate } from "@fortawesome/pro-solid-svg-icons";
import { AppUiContext } from "~/pages/PageEnigma/contexts/AppUiContext";
import { AppUiContext } from "~/contexts/AppUiContext";
import { AuthenticationContext } from "~/contexts/Authentication";
import { APPUI_ACTION_TYPES } from "~/pages/PageEnigma/reducers";
import { APPUI_ACTION_TYPES } from "~/reducers";
import { audioFilter, audioItems } from "~/pages/PageEnigma/store";
import { AssetFilterOption } from "~/pages/PageEnigma/models";
@@ -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);
@@ -6,6 +6,12 @@ import { AudioTab } from "~/pages/PageEnigma/comps/SidePanelTabs/itemTabs/AudioT
import { TabStylization } from "~/pages/PageEnigma/comps/SidePanelTabs/TabStylization";
import { CameraTab } from "./itemTabs/CameraTab";
export const tabList = [
{
icon: "resources/icons/characters.png",
title: "Characters",
value: AssetType.CHARACTER,
component: <ObjectsTab type={AssetType.CHARACTER} />,
},
{
icon: "resources/icons/animations.png",
title: "Animation",
@@ -24,12 +30,6 @@ export const tabList = [
value: AssetType.SHAPE,
component: <ShapeTab />,
},
{
icon: "resources/icons/characters.png",
title: "Characters",
value: AssetType.CHARACTER,
component: <ObjectsTab type={AssetType.CHARACTER} />,
},
{
icon: "resources/icons/camera.png",
title: "Camera",
@@ -42,10 +42,4 @@ export const tabList = [
value: AssetType.AUDIO,
component: <AudioTab />,
},
{
icon: "resources/icons/objects.png",
title: "Style (Temporary being moved)",
value: AssetType.STYLE,
component: <TabStylization />,
},
];
@@ -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/PageEnigma/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>
);
};
@@ -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,
+2 -2
View File
@@ -348,7 +348,7 @@ export class APIManager {
positive_prompt: string,
negative_prompt: string,
): Promise<string> {
const url = `https://funnel.tailce84f.ts.net/preview/`;
const url = 'http://207.189.112.61:31605/preview';
const payload = {
style: style,
@@ -357,7 +357,7 @@ export class APIManager {
};
const formData = new FormData();
formData.append("video", blob, fileName);
formData.append("input_file", blob, fileName);
formData.append("request", JSON.stringify(payload));
const response = await fetch(url, {
+80 -52
View File
@@ -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";
@@ -90,6 +90,8 @@ class Editor {
current_scene_glb_media_token: string | null;
can_initialize: boolean;
switchPreviewToggle: boolean;
dispatchAppUiState: any; // todo figure out the type
render_width: number;
render_height: number;
@@ -150,6 +152,7 @@ class Editor {
this.last_selected;
this.transform_interaction;
this.rendering = false;
this.switchPreviewToggle = false;
// API.
this.api_manager = new APIManager();
// Debug & Movement.
@@ -366,7 +369,7 @@ class Editor {
console.log(result);
}
public async testTestTimelineEvents() {}
public async testTestTimelineEvents() { }
public async loadScene(scene_media_token: string) {
this.dispatchAppUiState({
@@ -679,8 +682,6 @@ class Editor {
if (this.timeline.is_playing == false) {
this.stopPlayback();
this.playback_location = 0;
this.rendering = false;
this.switchCameraView();
this.onWindowResize();
}
}
@@ -805,10 +806,10 @@ class Editor {
audioSegment,
"-filter_complex",
"[1:a]adelay=" +
startTime * 1000 +
"|" +
startTime * 1000 +
"[a1];[0:a][a1]amix=inputs=2[a]",
startTime * 1000 +
"|" +
startTime * 1000 +
"[a1];[0:a][a1]amix=inputs=2[a]",
"-map",
"[a]",
`${itteration}final_tmp.wav`,
@@ -834,13 +835,13 @@ class Editor {
}
async stopPlayback(compile_audio: boolean = true) {
console.log(this.frames, this.frame_buffer.length);
this.renderMode();
if (this.generating_preview) {
return;
}
this.rendering = false;
this.generating_preview = true;
const ffmpeg = createFFmpeg({ log: true });
await ffmpeg.load();
@@ -922,6 +923,32 @@ class Editor {
console.log(result);
}
switchPreview() {
if (this.switchPreviewToggle == false) {
this.switchPreviewToggle = true;
this.generateFrame();
if (this.cameraViewControls) {
this.cameraViewControls.enabled = false;
}
}
}
switchEdit() {
if (this.switchPreviewToggle == true) {
this.switchPreviewToggle = false;
this.canvasRenderCamReference = document.getElementById("camera-view");
this.rawRenderer = new THREE.WebGLRenderer({
antialias: false,
canvas: this.canvasRenderCamReference,
preserveDrawingBuffer: true,
});
if (this.camera_person_mode == true) {
this.switchCameraView();
}
this.activeScene.renderMode(false);
}
}
async generateFrame() {
if (this.renderer && !this.generating_preview) {
this.removeTransformControls();
@@ -944,54 +971,55 @@ class Editor {
this.activeScene.renderMode(false);
this.onWindowResize();
const rawPreview: HTMLVideoElement | null = document.getElementById(
"raw-preview",
) as HTMLVideoElement;
if (rawPreview) {
rawPreview.src = imgData;
const ffmpeg = createFFmpeg({ log: false });
await ffmpeg.load();
await ffmpeg.FS("writeFile", `render.png`, await fetchFile(imgData));
await ffmpeg.run("-i", `render.png`, "render.mp4");
const output = await ffmpeg.FS("readFile", "render.mp4");
const blob = new Blob([output.buffer], { type: "video/mp4" });
const url = await this.api_manager.uploadMediaFrameGeneration(
blob,
"render.mp4",
this.art_style,
this.positive_prompt,
this.negative_prompt,
);
console.log(url);
const stylePreview: HTMLVideoElement | null = document.getElementById(
"stylized-preview",
) as HTMLVideoElement;
if (stylePreview) {
stylePreview.src = url;
stylePreview.width = rawPreview.width;
stylePreview.height = rawPreview.height;
} else {
console.log("No style preview window.");
}
this.generating_preview = false;
return new Promise((resolve, reject) => {
resolve(url);
});
} else {
console.log("No raw preview window.");
this.canvasRenderCamReference = document.getElementById("raw-preview");
this.rawRenderer = new THREE.WebGLRenderer({
antialias: false,
canvas: this.canvasRenderCamReference,
preserveDrawingBuffer: true,
});
if (this.camera_person_mode == false) {
this.switchCameraView();
}
this.activeScene.renderMode(true);
const ffmpeg = createFFmpeg({ log: false });
await ffmpeg.load();
await ffmpeg.FS("writeFile", `render.png`, await fetchFile(imgData));
await ffmpeg.run("-i", `render.png`, "render.mp4");
const output = await ffmpeg.FS("readFile", "render.mp4");
const blob = new Blob([output.buffer], { type: "video/mp4" });
const url = await this.api_manager.uploadMediaFrameGeneration(
blob,
"render.mp4",
this.art_style,
this.positive_prompt,
this.negative_prompt,
);
console.log(url);
const stylePreview: HTMLVideoElement | null = document.getElementById(
"styled-preview",
) as HTMLVideoElement;
if (stylePreview) {
stylePreview.src = url;
} else {
console.log("No style preview window.");
}
this.generating_preview = false;
return new Promise((resolve, reject) => {
resolve(url);
});
this.generating_preview = false;
}
}
// This initializes the generation of a video render scene is where the core work happens
generateVideo() {
console.log("Generating video...");
console.log("Generating video...", this.frame_buffer);
if (this.rendering || this.generating_preview) {
return;
}
+5 -4
View File
@@ -402,6 +402,7 @@ export class TimeLine {
public async deletePlayableClip(clip_uuid: string): Promise<void> {}
public async scrub(data: any): Promise<void> {
console.log('Scrubby Scrubby');
if (this.is_playing) {
return;
}
@@ -537,10 +538,10 @@ export class TimeLine {
await this.animation_engine.clips[
object.uuid + element.media_id
].play(object);
let fps = 120;
if (isRendering) {
fps = 60;
}
let fps = 60;
//if (isRendering) {
// fps = 60;
//}
this.animation_engine.clips[object.uuid + element.media_id].step(
this.scrubber_frame_position / fps, // Double FPS for best result.
);
+1 -1
View File
@@ -514,7 +514,7 @@ export const characterItems = signal<MediaItem[]>([
},
{
version: 1,
media_id: "m_4wva09qznapzk5rcvbxy671d1qx2pr ",
media_id: "m_pwtk45wsfr5z2bavmwxh2efnwvh4rr ",
type: AssetType.CHARACTER,
name: "Story Girl",
thumbnail: "resources/characters/img13.png",
+2 -1
View File
@@ -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);
-3
View File
@@ -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);
+84
View File
@@ -0,0 +1,84 @@
import { ArtStyle } from "~/pages/PageEnigma/js/api_manager";
export const styleList = [
{
label: "Anime",
type: ArtStyle.Anime2_5D,
image: "/resources/avatars/0.webp",
},
{
label: "Anime Flat",
type: ArtStyle.Anime2DFlat,
image: "/resources/avatars/1.webp",
},
{
label: "Cartoon 3D",
type: ArtStyle.Cartoon3D,
image: "/resources/avatars/2.webp",
},
{
label: "Comic Book",
type: ArtStyle.ComicBook,
image: "/resources/avatars/3.webp",
},
{
label: "Ghibli",
type: ArtStyle.AnimeGhibli,
image: "/resources/avatars/4.webp",
},
{
label: "Ink Punk",
type: ArtStyle.InkPunk,
image: "/resources/avatars/5.webp",
},
{
label: "Ink Splash",
type: ArtStyle.InkSplash,
image: "/resources/avatars/6.webp",
},
{
label: "Ink Black and White",
type: ArtStyle.InkBWStyle,
image: "/resources/avatars/7.webp",
},
{
label: "Jo Jo Style",
type: ArtStyle.JojoStyle,
image: "/resources/avatars/8.webp",
},
{
label: "Paper Origami",
type: ArtStyle.PaperOrigami,
image: "/resources/avatars/9.webp",
},
{
label: "Pixel",
type: ArtStyle.PixelArt,
image: "/resources/avatars/10.webp",
},
{
label: "Pop Art",
type: ArtStyle.PopArt,
image: "/resources/avatars/11.webp",
},
{
label: "Realistic 1",
type: ArtStyle.Realistic1,
image: "/resources/avatars/12.webp",
},
{
label: "Realistic 2",
type: ArtStyle.Realistic2,
image: "/resources/avatars/13.webp",
},
{
label: "Anime Retro Neon",
type: ArtStyle.AnimeRetroNeon,
image: "/resources/avatars/14.webp",
},
{
label: "Anime Standard",
type: ArtStyle.AnimeStandard,
image: "/resources/avatars/15.webp",
},
];
@@ -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.
+28 -13
View File
@@ -15,16 +15,18 @@ import tailwindCss from "./styles/tailwind.css?url";
import baseCss from "./styles/base.css?url";
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 { AppUIProvider } from "~/contexts/AppUiContext";
config.autoAddCss = false; /* eslint-disable import/first */
export const links: LinksFunction = () => [
@@ -65,6 +67,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 +99,10 @@ export default function App() {
<CookiesProvider defaultSetOptions={{ path: "/" }}>
<AuthenticationProvider>
<ToasterProvider>
<TopBarInnerContext.Provider
value={{
TopBarInner: topBarInnerComponent,
setTopBarInner: setTopBarInnerComponent,
}}
>
<AppUIProvider>
<div className="topbar-spacer" />
<Outlet />
<TopBar />
</TopBarInnerContext.Provider>
</AppUIProvider>
</ToasterProvider>
</AuthenticationProvider>
</CookiesProvider>
+7 -4
View File
@@ -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>
+6
View File
@@ -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);
+1
View File
@@ -0,0 +1 @@
export * from "./global";