Merge branch 'test' of github.com:storytold/GottaGoFast into test

This commit is contained in:
Michael Chung committed 2024-03-21 19:06:52 -04:00
commit e0df1c4af9
24 files changed
+354 -58

No files matched your search

+1 -1
View File
@@ -1,5 +1,5 @@
node_modules
/~/
/.cache
/public/build
/build
+15 -6
View File
@@ -3,20 +3,29 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
icon?: IconDefinition;
variant?: "primary"|"secondary";
}
export const Button = ({
icon,
children,
className : propsClassName,
variant = 'primary',
...rest
}:ButtonProps)=>{
const className = `
bg-brand-primary hover:bg-brand-primary-400
text-white text-sm font-semibold
rounded-md px-3.5 py-2.5 shadow-sm
focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-primary-600
` + (propsClassName ? ` ${propsClassName}` : null);
function getVariantClassNames(variant: string){
switch(variant){
case "secondary":{
return " bg-brand-secondary hover:bg-brand-secondary-900 text-white focus-visible:outline-brand-secondary";
}
case "primary":
default:{
return " bg-brand-primary hover:bg-brand-primary-400 text-white focus-visible:outline-brand-primary-600";
}
}
};
const className = "text-sm font-semibold rounded-md px-3.5 py-2.5 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2" + getVariantClassNames(variant)
+ (propsClassName ? ` ${propsClassName}` : "");
return(
<button
+41
View File
@@ -0,0 +1,41 @@
import { Link, LinkProps } from "@remix-run/react";
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
interface LinkButtonProps extends LinkProps {
icon?: IconDefinition;
variant?: "primary"|"secondary";
}
export const ButtonLink = ({
icon,
children,
className: propsClassName,
variant = "primary",
...rest
}:LinkButtonProps)=>{
//TODO: Duplicated from Button.tsx
function getVariantClassNames(variant: string){
switch(variant){
case "secondary":{
return " bg-brand-secondary hover:bg-brand-secondary-900 text-white focus-visible:outline-brand-secondary";
}
case "primary":
default:{
return " bg-brand-primary hover:bg-brand-primary-400 text-white focus-visible:outline-brand-primary-600";
}
}
};
const className = "text-sm font-semibold rounded-md px-3.5 py-2.5 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2" + getVariantClassNames(variant)
+ (propsClassName ? ` ${propsClassName}` : "");
// END TODO
return(
<Link {...rest}>
<button className={className}>
{icon && <FontAwesomeIcon className="mr-2" icon={icon}/>}
{children}
</button>
</Link>
);
}
+6
View File
@@ -0,0 +1,6 @@
import { createContext } from "react";
export const TopBarInnerContext = createContext<{
TopBarInner: JSX.Element | null;
setTopBarInner: (el: JSX.Element | null)=>void;
} | null>(null);
-3
View File
@@ -1,3 +0,0 @@
import { ReactNode, createContext } from "react";
export const TopBarMidSection = createContext<ReactNode | null>(null);
+50 -10
View File
@@ -1,13 +1,28 @@
import {
useRef,
useEffect,
useState,
useCallback,
useContext,
useEffect,
useLayoutEffect,
useRef,
useState,
} from 'react';
import {
faChevronLeft
} from "@fortawesome/free-solid-svg-icons";
import { Button } from '~/components/Button';
import { ButtonLink } from '~/components/ButtonLink';
import { TopBarInnerContext } from "~/contexts/TopBarInner";
import { SidePanel } from '~/templates/SidePanel';
import { SceneWithCube } from './SceneWithCube';
export const PageCube = () => {
const { setTopBarInner } = useContext(TopBarInnerContext) || {};
const mountRef = useRef<HTMLDivElement | null>(null);
const sceneRef = useRef<SceneWithCube | null>(null);
const [{
@@ -27,7 +42,10 @@ export const PageCube = () => {
const listeningXYRotation = useCallback((
{x,y}:{x:number;y:number}
)=>{
setRotations({x:x, y:y})
setRotations({
x:Math.round(x*100)/100,
y:Math.round(y*100)/100,
})
},[]);
useEffect(() => { //componentDidMount
@@ -45,13 +63,35 @@ export const PageCube = () => {
};
}, []);
useLayoutEffect(()=>{
if(setTopBarInner){
const BackButton = (
<ButtonLink
to={"/"}
variant='secondary'
icon={faChevronLeft}
>
Back to Dashboard
</ButtonLink>
);
setTopBarInner(BackButton);
}
},[setTopBarInner]);
return (
<>
<div className="Cube" ref={mountRef} />
<button onClick={toggleStopped}> STOP </button>
<p>{rotationX ? rotationX : ""}</p>
<p>{rotationY ? rotationY : ""}</p>
</>
<div className="grid grid-cols-12 gap-2 ">
<div className="col-span-6 ">
<div className="MountingPoint" ref={mountRef} />
</div>
<div className="col-span-6 ">
<Button onClick={toggleStopped}> STOP </Button>
<p className='text-white'>Ration X: {rotationX ? rotationX : ""}</p>
<p className='text-white'>Ration Y: {rotationY ? rotationY : ""}</p>
</div>
<SidePanel>
<p>Side Panel Text</p>
</SidePanel>
</div>
);
};
+42 -6
View File
@@ -1,16 +1,27 @@
import {
useRef,
useEffect,
// useState,
import {
useCallback,
useContext,
useEffect,
useLayoutEffect,
useRef,
// useState,
} from 'react';
import {
faChevronLeft,
faWandSparkles,
} from "@fortawesome/free-solid-svg-icons";
import { Button } from '~/components/Button';
import { ButtonLink } from '~/components/ButtonLink';
import { TopBarInnerContext } from "~/contexts/TopBarInner";
import { SidePanel } from '~/templates/SidePanel';
import Editor from './js/editor';
export const PageEnigma = () => {
const { setTopBarInner } = useContext(TopBarInnerContext) || {};
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const editorRef = useRef<Editor | null>(null);
@@ -37,6 +48,29 @@ export const PageEnigma = () => {
}
}, []);
useLayoutEffect(()=>{
if(setTopBarInner){
const TopBarInnerButtons = (
<div className="flex grow justify-between">
<ButtonLink
to={"/"}
variant='secondary'
icon={faChevronLeft}
>
Back to Dashboard
</ButtonLink>
<Button
icon={faWandSparkles}
>
Generate Movie
</Button>
<span className="w-8"/>
</div>
);
setTopBarInner(TopBarInnerButtons);
}
},[setTopBarInner]);
const handleButtonSave = ()=>{
editorRef.current?.save();
}
@@ -53,14 +87,16 @@ export const PageEnigma = () => {
return(
<div>
<canvas ref={canvasRef} id="video-scene" width="1280px" height="720px" />
<div className="absolute top-0 right-0 bg-ui-panel h-screen w-1/5 pt-20">
<SidePanel>
<Button onClick={handleButtonSave}>Save</Button>
<Button onClick={handleButtonLoad}>Load</Button>
<Button onClick={handleButtonRender}>Render</Button>
<Button onClick={handleButtonPlay}>Play</Button>
</SidePanel>
<div className="bg-ui-panel w-full h-screen">
<p className='text-white'>Timeline Panel</p>
<input style={{ display: 'none' }} type="file" id="load-upload" name="load-upload"></input>
</div>
<div className="bg-ui-panel w-full h-screen">Timeline Panel</div>
</div>
);
}
+10 -4
View File
@@ -1,3 +1,5 @@
import { v4 as uuidv4 } from 'uuid';
class MediaUploadManager {
constructor(sessionToken) {
this.baseUrl = "https://api.fakeyou.com";
@@ -7,15 +9,19 @@ class MediaUploadManager {
async uploadMedia(blob, fileName) {
const url = `${this.baseUrl}/v1/media_uploads/upload`;
let uuid = uuidv4();
const formData = new FormData();
formData.append('uuid_idempotency_token', uuid);
formData.append('file', blob, fileName);
formData.append('source', 'file');
formData.append('type', 'video');
formData.append('source', 'file');
const response = await fetch(url, {
method: 'POST',
mode: 'cors',
credentials: "include",
headers: {
'Authorization': `Bearer ${this.sessionToken}`, // Assuming the session token is a Bearer token
'Access-Control-Allow-Origin': 'http://localhost:3000',
'Access-Control-Allow-Credentials': true
"accept": "application/json",
},
body: formData,
});
+3 -1
View File
@@ -38,4 +38,6 @@ class AudioManager {
console.warn(`AudioManager: AudioClip with id "${id}" not found.`);
}
}
}
}
export default AudioManager;
+14 -4
View File
@@ -6,6 +6,7 @@ import Stats from 'three/addons/libs/stats.module.js';
import Scene from './scene.js';
import SaveManager from './serialization.js';
import MediaUploadManager from './api_manager.js';
import AudioManager from './audio_manager.js';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
@@ -63,12 +64,10 @@ class Editor {
// API.
this.api_manager = new MediaUploadManager();
this.locked = false;
// Debug & Movement.
this.stats = null;
this.orbit = null;
this.locked = false;
// Recording params.
this.capturer = null;
@@ -81,9 +80,13 @@ class Editor {
this.playback = false;
this.playback_location = 0;
this.max_length = 10;
this.timeline = null;
// Save & Load.
this.save_manager = new SaveManager(this.version);
// Audio Engine.
this.audio_manager = new AudioManager();
}
// Initializes the main scene and ThreeJS essentials.
@@ -302,10 +305,15 @@ class Editor {
this.control.detach(this.selected);
this.activeScene.scene.remove(this.control);
this.activeScene.scene.remove(this.activeScene.gridHelper);
this.save_manager.save(this.activeScene.scene);
this.save_manager.save(this.activeScene.scene, this._save_to_cloud.bind(this), this.audio_manager, this.timeline);
this.activeScene._createGrid();
}
_save_to_cloud(blob) {
console.log("Posting to cloud!");
this.api_manager.uploadMedia(blob, "test.bin");
}
load() {
let uploadedFile = document.getElementById("load-upload").files[0];
this.save_manager.load(uploadedFile, this.load_callback.bind(this));
@@ -365,6 +373,8 @@ class Editor {
const blob = new Blob([output.buffer], { type: 'video/mp4' });
const url = URL.createObjectURL(blob);
await this.api_manager.uploadMedia(blob, "output.mp4");
// Create a link to download the file
const downloadLink = document.createElement('a');
downloadLink.href = url;
-1
View File
@@ -86,7 +86,6 @@ class Scene {
}
play_anim_demo(character_name) {
console.log(this.characters[character_name].anims[0]._clip);
this.characters[character_name].animate(this.characters[character_name].anims[0]._clip);
this.characters[character_name].sync_lips("/resources/sound/2pac.wav");
this.activeCharacter = character_name;
+11 -14
View File
@@ -8,16 +8,18 @@ class SaveManager {
this.scene = null;
}
save(scene, scene_data) {
save(scene, scene_callback, audio_manager, timeline) {
let gltfExporter = new GLTFExporter();
gltfExporter.parse(
scene,
function (gltfJson) {
const jsonString = JSON.stringify(gltfJson);
function (gltf) {
console.log(scene);
let blob = new Blob([jsonString], {type: 'application/octet-stream'})
let save_json = {"glb": gltf, "audio": audio_manager.clips, "timeline": timeline}
let blob = new Blob([JSON.stringify(save_json)], {type: 'application/json'})
let blobUrl = URL.createObjectURL(blob);
scene_callback(blob);
let a = document.createElement('a');
const now = new Date();
@@ -28,7 +30,7 @@ class SaveManager {
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
const randomNumber = Math.floor(Math.random() * 1000);
let save_name = `save_${randomNumber}_${year}${month}${day}_${hours}${minutes}${seconds}.glb`;
let save_name = `save_${randomNumber}_${year}${month}${day}_${hours}${minutes}${seconds}.sav`;
a.href = blobUrl;
a.download = save_name;
@@ -51,18 +53,13 @@ class SaveManager {
reader.onload = function(e) {
const contents = e.target.result;
const loader = new THREE.GLTFLoader();
const loader = new GLTFLoader();
loader.parse(contents, '', function(gltf) {
scene.add(gltf.scene);
console.log(gltf);
load_callback(gltf.scene);
});
};
reader.readAsArrayBuffer(file);
//const loader = new GLTFLoader();
//loader.load(uploadedFile, function ( gltf ) {
// console.log(gltf);
// load_callback(gltf.scene);
//});
reader.readAsArrayBuffer(uploadedFile);
}
download(data, filename, type) {
+15 -4
View File
@@ -1,3 +1,4 @@
import { useState } from 'react';
import { LinksFunction } from "@remix-run/deno";
import {
Links,
@@ -10,8 +11,9 @@ import {
import tailwindCss from "./styles/tailwind.css?url";
import normalizeCss from "./styles/normalize.css?url";
import { TopBar } from "./template/TopBar";
import { TopBarMidSection } from "./contexts/TopbarMidSection";
import { TopBar } from "./templates/TopBar";
import { TopBarInnerContext } from '~/contexts/TopBarInner';
export const links : LinksFunction = () => [{
rel: "stylesheet",
@@ -22,6 +24,8 @@ export const links : LinksFunction = () => [{
}];
export default function App() {
const [topBarInnerComponent, setTopBarInnerComponent] = useState<JSX.Element | null>(null);
return (
<html lang="en">
<head>
@@ -31,8 +35,15 @@ export default function App() {
<Links />
</head>
<body className="bg-ui-background">
<Outlet />
<TopBar />
<TopBarInnerContext.Provider value={{
TopBarInner: topBarInnerComponent,
setTopBarInner: setTopBarInnerComponent
}}>
<div className="h-10 m-4 w-screen border-b"/>
<Outlet />
<TopBar />
</TopBarInnerContext.Provider>
<ScrollRestoration />
<Scripts />
</body>
+10
View File
@@ -0,0 +1,10 @@
export default function ColourGuide () {
return(
<>
<h1 className="text-brand-primary">Primary / Sunglo</h1>
<h1 className="text-brand-secondary">Secondary / Gunpowder</h1>
</>
);
};
+11
View File
@@ -0,0 +1,11 @@
import { Button } from "~/components/Button";
import { ButtonLink } from "~/components/ButtonLink";
export default function ComponentLibrary () {
return(
<>
<Button>Button</Button>
<ButtonLink to="/">Button with Link</ButtonLink>
</>
);
};
+2 -1
View File
@@ -2,6 +2,7 @@ import { useState } from 'react';
import { PageCube } from "../pages/PageCube";
import { Button } from '~/components/Button';
export default function Cube(){
const pausedRiddle = true;
const [riddle, setRiddle] = useState<string>("");
const [submitted, setSubmitted] = useState<boolean>(false);
@@ -17,7 +18,7 @@ export default function Cube(){
alert("What is the price for your blind eye?")
}
if(submitted){
if(submitted || pausedRiddle){
return <PageCube />
}else{
return(
+12 -1
View File
@@ -1,4 +1,10 @@
import {
useContext,
useLayoutEffect,
} from "react";
import type { MetaFunction } from "@remix-run/deno";
import { TopBarInnerContext } from "~/contexts/TopBarInner";
import sonic from "./_assets/sonic-the-hedgehog-classic-sonic.gif";
export const meta: MetaFunction = () => {
@@ -8,8 +14,13 @@ export const meta: MetaFunction = () => {
];
};
export default function Index() {
const { setTopBarInner } = useContext(TopBarInnerContext) || {};
useLayoutEffect(()=>{
if(setTopBarInner){
setTopBarInner(null);
}
},[setTopBarInner]);
return (
<div className="size-full">
<img
+79
View File
@@ -0,0 +1,79 @@
import { Fragment, useState } from 'react'
import { Dialog, Transition } from '@headlessui/react'
import {
faXmark,
faGears,
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button } from '~/components/Button';
interface SidePanelPropsI{
children: React.ReactNode
}
export const SidePanel =(props:SidePanelPropsI)=>{
const [open, setOpen] = useState(false)
const handleOpen = ()=>{
setOpen(true);
}
return (<>
<Button
className='fixed right-0 top-20 mt-2 mr-4'
onClick={handleOpen}
>
<FontAwesomeIcon icon={faGears} />
</Button>
<Transition.Root show={open} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={setOpen}>
<div className="fixed inset-0" />
<div className="fixed inset-0 overflow-hidden">
<div className="absolute inset-0 overflow-hidden">
<div className="pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10 sm:pl-16">
<Transition.Child
as={Fragment}
enter="transform transition ease-in-out duration-500 sm:duration-700"
enterFrom="translate-x-full"
enterTo="translate-x-0"
leave="transform transition ease-in-out duration-500 sm:duration-700"
leaveFrom="translate-x-0"
leaveTo="translate-x-full"
>
<Dialog.Panel className="pointer-events-auto w-screen max-w-2xl">
<div className="flex h-full flex-col overflow-y-scroll bg-ui-panel py-6 shadow-xl">
<div className="px-4 sm:px-6">
<div className="flex items-start justify-between">
<Dialog.Title className="text-base font-semibold leading-6 text-white">
Library
</Dialog.Title>
<div className="ml-3 flex h-7 items-center">
<button
type="button"
className="relative rounded-md bg-brand-primary text-white hover:bg-brand-primary-400 focus:outline-none focus:ring-brand-primary-500"
onClick={() => setOpen(false)}
>
<span className="absolute -inset-2.5" />
<span className="sr-only">Close panel</span>
<FontAwesomeIcon
className="h-6 w-6"
aria-hidden="true"
icon={faXmark}
/>
</button>
</div>
</div>
</div>
<div className="relative mt-6 flex-1 px-4 sm:px-6">
{props.children}
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</div>
</Dialog>
</Transition.Root>
</>);
}
+1
View File
@@ -0,0 +1 @@
export { SidePanel } from './SidePanel';
@@ -1,18 +1,32 @@
import { useContext } from 'react';
import { Button } from '~/components/Button'
import { faRightToBracket } from '@fortawesome/free-solid-svg-icons'
export const TopBar = () => {
import { TopBarInnerContext } from '~/contexts/TopBarInner';
export const TopBar = () => {
return (
<header className="bg-ui-panel fixed top-0 left-0 w-full">
<header className="
fixed top-0 left-0 w-full
bg-ui-panel border-b border-ui-panel-border
">
<nav className="mx-auto flex max-w-full items-center justify-between p-4 lg:px-8" aria-label="Global">
<div className="flex lg:flex-1">
<a href="#" className="-m-1.5 p-1.5">
<span className="sr-only">FakeYou</span>
<img className="h-10 w-auto" src="/resources/images/Storyteller-Logo-1.png" alt="Logo FakeYou StoryTeller.ai" />
</a>
<span className="w-4 lg:w-8"/>
<TopBarInner />
</div>
<Button icon={faRightToBracket}>Login</Button>
</nav>
</header>
)
}
const TopBarInner = ()=>{
const { TopBarInner } = useContext(TopBarInnerContext) || {}
return(TopBarInner);
}
File renamed without changes.
+13
View File
@@ -26,6 +26,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"three": "^0.162.0",
"uuid": "^9.0.1",
"webm-writer": "^1.0.0"
},
"devDependencies": {
@@ -12475,6 +12476,18 @@
"node": ">= 0.4.0"
}
},
"node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/uvu": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz",
+1
View File
@@ -31,6 +31,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"three": "^0.162.0",
"uuid": "^9.0.1",
"webm-writer": "^1.0.0"
},
"devDependencies": {
+1
View File
@@ -73,6 +73,7 @@ export default {
'ui-background': '#1a1a27',
'ui-panel': '#242433',
'ui-panel-border': '#5e6097',
gray: colors.gray,
white: colors.white