Removed docs and playground, wizard.

Readded the strange login.
This commit is contained in:
Michael Chung committed 2025-05-27 11:21:10 -04:00
1 parent e8d3b0bef9
commit 28b6034cfb
18 files changed
+316 -664

No files matched your search

@@ -0,0 +1,111 @@
import { Fragment, useCallback } from "react";
import { Dialog, Transition } from "@headlessui/react";
import { DoNotShow } from "~/constants";
import { Button } from "@storyteller/ui-button"
interface Props {
text: string;
title: string;
open: boolean;
onClose: () => void;
onOk?: () => void;
okText?: string;
okColor?: string;
onCancel?: () => void;
cancelText?: string;
canHide?: boolean;
}
export const ConfirmationModal = ({
text,
title,
open,
onClose,
onOk,
okText = "OK",
okColor = "bg-brand-success",
onCancel,
cancelText = "Cancel",
canHide,
}: Props) => {
const handleOk = useCallback(() => {
const element = document.getElementById("hide-dialog");
if (element) {
if ((element as HTMLInputElement).checked) {
localStorage.setItem(title.replace(" ", "-"), DoNotShow);
}
}
onOk!();
}, [onOk, title]);
return (
<Transition appear show={open} as={Fragment}>
<Dialog open={open} onClose={onClose}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-black/40" />
</Transition.Child>
<div className="fixed inset-0 flex w-screen items-center justify-center p-4">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-xl border border-ui-panel-border bg-ui-panel p-5 text-left align-middle shadow-xl transition-all">
<Dialog.Title
as="h4"
className="mb-4 text-xl font-bold text-white"
>
{title}
</Dialog.Title>
<div className="mt-2">{text}</div>
{canHide && (
<div className="mt-2">
<label>
<input id="hide-dialog" type="checkbox" />
&nbsp;&nbsp;Do not show this again
</label>
</div>
)}
<div className="mt-6 flex justify-end gap-2">
{!!onCancel && (
<Button
type="button"
onClick={onCancel}
className="rounded-lg px-3 py-2"
variant="secondary"
>
{cancelText}
</Button>
)}
{!!onOk && (
<Button
type="button"
onClick={handleOk}
className={[okColor, "rounded-lg px-3 py-2"].join(" ")}
>
{okText}
</Button>
)}
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</Dialog>
</Transition>
);
};
+204
View File
@@ -0,0 +1,204 @@
import { useRef, StrictMode } from "react";
import { useNavigate, useSearchParams, BrowserRouter } from "react-router-dom";
import { useSignalEffect, useSignals } from "@preact/signals-react/runtime";
import { twMerge } from "tailwind-merge";
import {
faKey,
faSpinnerThird,
faUser,
} from "@fortawesome/pro-solid-svg-icons";
import { AUTH_STATUS } from "~/enums";
import { authentication, login, logout } from "~/signals";
import { createRoot } from "react-dom/client";
import { ConfirmationModal } from "~/components/reusable/ConfirmationModal";
import { Input } from "@storyteller/ui-input";
import { Button } from "@storyteller/ui-button";
import "./styles/normalize.css";
import "./styles/tailwind.css";
import "./styles/base.css";
import "@fortawesome/fontawesome-svg-core/styles.css";
import { config } from "@fortawesome/fontawesome-svg-core";
import { GlobalSettingsManager } from "./pages/PageEnigma/GlobalSettingsManager";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
config.autoAddCss = false; /* eslint-disable import/first */
// TODO(bt,2025-04-19): Make these configurable
const ENV = {
BASE_API: "https://api.storyteller.ai",
GOOGLE_API: "https://studio.storyteller.ai",
FUNNEL_API: "https://studio.storyteller.ai",
CDN_API: "https://cdn-2.fakeyou.com",
GRAVATAR_API: "https://studio.storyteller.ai",
DEPLOY_PRIME_URL: "https://studio.storyteller.ai",
};
export default function LoginScreen() {
useSignals();
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const formRef = useRef<HTMLFormElement | null>(null);
const { status: authStatus } = authentication;
const authLoaderMessage = getAuthLoaderMessage();
const shouldShowLoader = checkShouldShowLoader();
const showNoAccessModal = authStatus.value === AUTH_STATUS.NO_ACCESS;
const handleOnSumbit = (ev: React.FormEvent<HTMLFormElement>) => {
ev.preventDefault();
if (formRef.current) {
const form = new FormData(formRef.current);
const usernameOrEmail = form.get("usernameOrEmail")?.toString();
const password = form.get("password")?.toString();
if (usernameOrEmail && password && login) {
login({
usernameOrEmail,
password,
});
}
}
};
useSignalEffect(() => {
const redirectPath = searchParams.get("redirect");
if (authStatus.value === AUTH_STATUS.LOGGED_IN) {
navigate(redirectPath ? redirectPath : "/");
}
});
return (
<div className="fixed flex h-full w-full flex-col items-center justify-center bg-[#1b1b1f] bg-center bg-repeat">
<div className="mb-7 flex w-10/12 max-w-2xl items-center justify-center gap-4">
<img
src="/resources/images/artcraft-logo-3.png"
alt="ArtCraft Logo"
className="h-9 select-none"
/>
</div>
<div className="w-full max-w-xl rounded-xl shadow-lg">
<div className="glass glass-no-hover relative mx-auto w-full rounded-none rounded-t-xl p-6 shadow-none">
<h1 className="mb-8 text-center text-2xl font-bold">
Log in to 3D Stage Editor
</h1>
<form
ref={formRef}
onSubmit={handleOnSumbit}
className={twMerge(
"flex flex-col gap-4",
shouldShowLoader && "opacity-0",
)}
>
<Input
label="Username or Email"
icon={faUser}
name="usernameOrEmail"
placeholder="Username or Email"
autoComplete="username"
required
/>
<Input
label="Password"
icon={faKey}
type="password"
name="password"
placeholder="Password"
autoComplete="current-password"
required
/>
<Button className="mt-4 py-3">Continue</Button>
</form>
{shouldShowLoader && (
<div className="absolute left-0 top-0 flex h-full w-full items-center justify-center bg-black/50">
<div className="flex items-center text-center">
<FontAwesomeIcon
icon={faSpinnerThird}
className="animate-spin"
/>
<p className="text-sm text-white">{authLoaderMessage}</p>
</div>
</div>
)}
</div>
<div className="align-items flex w-full max-w-xl rounded-b-xl bg-ui-controls px-6 py-4">
{/* <a
href="https://storyteller.ai/password-reset"
className="grow text-sm font-medium !text-brand-primary-400 transition-all duration-150 hover:!text-brand-primary-300"
>
Forgot your password?
</a> */}
<a
href="/"
className="grow text-sm font-medium !text-brand-primary-400 transition-all duration-150 hover:!text-brand-primary-300"
>
Back
</a>
<div className="flex justify-end gap-1 text-sm">
<p className="opacity-75">Don&apos;t have an account?</p>
<a
href="/signup"
className="font-medium text-brand-primary-400 hover:text-brand-primary-300"
>
Sign up
</a>
</div>
</div>
</div>
<ConfirmationModal
text="We're in a closed beta and you'll need a beta key to use this app."
title="Unauthorized"
open={showNoAccessModal}
onClose={handleClose}
cancelText="Close"
onCancel={handleClose}
okText="Get Beta Key"
onOk={() => {
handleClose();
if (window) {
window.open("https://storyteller.ai/beta-key/redeem", "_blank");
}
}}
/>
</div>
);
}
const handleClose = () => {
if (authentication.status.value !== AUTH_STATUS.LOGGED_OUT) {
logout();
}
};
const checkShouldShowLoader = () => {
return (
authentication.status.value === AUTH_STATUS.LOGGING ||
authentication.status.value === AUTH_STATUS.LOGGED_IN
);
};
const getAuthLoaderMessage = () => {
if (authentication.status.value === AUTH_STATUS.LOGGED_IN) {
return "Authenticated, Redirecting...";
}
if (authentication.status.value === AUTH_STATUS.GET_USER_INFO) {
return "Getting User Info...";
}
return "Getting Session...";
};
// TODO: Replace environment variables from `root.tsx`
createRoot(document.getElementById("root")!).render(
<>
<StrictMode>
<BrowserRouter>
<GlobalSettingsManager env={ENV} />
<LoginScreen />
</BrowserRouter>
</StrictMode>
</>,
);
@@ -3,7 +3,7 @@ import EnvironmentVariables from "~/Classes/EnvironmentVariables";
import { persistLogin } from "~/signals";
import { pageHeight, pageWidth } from "~/signals";
import { useEffect } from "react";
import { showWizard } from "~/pages/PageEnigma/Wizard/signals/wizard";
export const GlobalSettingsManager = ({ env }: { env: Record<string, string> }) => {
@@ -35,17 +35,7 @@ export const GlobalSettingsManager = ({ env }: { env: Record<string, string> })
pageWidth.value = window.innerWidth;
}
function initWizard() {
if (showWizard.value) {
return;
}
const wizard = localStorage.getItem("storyteller-wizard");
showWizard.value = wizard ? "" : "initial";
localStorage.setItem("storyteller-wizard", "shown");
}
useEffect(() => {
initWizard();
setPage();
window.addEventListener("resize", setPage);
return () => {
@@ -1,21 +0,0 @@
# STORYTELLER STUDIO ENGINE DOCS
This is the official documentation for the storyteller studio engine.
[Back](../README.md)
## Copy & Paste
## Documentation
- Copy paste is done in the `SceneManager` and `MouseControls` classes.
- In `SceneManager` the async `copy` function sets the copied object to whatever the `MouseControls` class has selected.
- In `SceneManager` the async `paste` function duplicates the threejs object and updates the outliner while coping the newly created object.
## Hacks
- Tempararly disabled copy and paste of characters because the UI on the timeline is not updateing properly.
## Roadmap
- Fix characters.
-21
View File
@@ -1,21 +0,0 @@
# STORYTELLER STUDIO ENGINE DOCS
This is the official documentation for the storyteller studio engine.
[Back](../README.md)
## MMD IK
## Documentation
- All IK is handled by the `ikHelper` which is appart of `THREE.js`'s `MMDAnimation` lib.
- IK is updated by the timeline by setting the action of the mixer which was assigned to it by the animation clip.
## Hacks
- Loads `Ammo.js` using js on line `180` of `editor` as of `July 25th 2024`.
- Ceates an `Ammo.js` instance using code on line `673` of `scene.ts` as of `July 25th 2024`.
## Roadmap
- Fix loading of `ammo.js`.
@@ -1,27 +0,0 @@
# STORYTELLER STUDIO ENGINE DOCS
This is the official documentation for the storyteller studio engine.
[Back](../README.md)
## Documentation Links
[threejs skyboxes](https://threejs.org/manual/#en/backgrounds)
## Documentation
- Asset type is `AssetType.SKYBOX`
- Update skyboxes by calling the `Scene`'s `updateSkybox` function while passing in the media id of te skybox.
- You can also update skyboxes by calling the Scene Manages `updateSkybox` function while passing in the media id of te skybox.
## Hacks
- Made creation of skyboxs go through the `addObject` function on line 101 of `DndAssets.ts`.
- Hard coded 3 skyboxs into the engine in `demoAssts.ts`.
- Use hard coded images and media tokens for loading skyboxes. Ex. `Default`, `m_0`, `m_1`...
## Roadmap
- Uploading Skyboxes to Backend.
- Create Panel for Skyboxes
@@ -1,25 +0,0 @@
# STORYTELLER STUDIO ENGINE DOCS
This is the official documentation for the storyteller studio engine.
[Back](../README.md)
## Video Planes
## Documentation Links
[threejs VideoTexture](https://threejs.org/docs/#api/en/textures/VideoTexture)
## Documentation
- Suported types: `.mp4`
- The loading and instantiating code is all located in scene.ts arround line 140 as of July 22 2024.
- The actual code for updating is in the timeline but it runs at 10fps due too buffering issue.
## Hacks
- Caped to 10 fps due to buffering issue.
## Roadmap
- Make render at 30 fps.
-13
View File
@@ -1,13 +0,0 @@
# STORYTELLER STUDIO ENGINE DOCS
This is the official documentation for the storyteller studio engine.
## Documentation Links
[Skyboxes](./DOCS/SKYBOXES.md)
[Copy & Paste](./DOCS/COPYPASTE.md)
[Video Planes](./DOCS/VIDEOPLANES.md)
[MMD IK](./DOCS/MMD.md)
@@ -1,76 +0,0 @@
import * as THREE from "three";
export class FrameCollectorManager {
constructor(width, height, expectedFramesLength = 720) {
this.worker = new Worker("canvas-queued-worker.js");
this.worker.addEventListener("message", this.handleMessage.bind(this));
this.frames = [];
this.expectedFramesLength = expectedFramesLength;
this.offscreenCanvas = new OffscreenCanvas(width, height);
this.width = width;
this.height = height;
this.offScreenRenderer = new THREE.WebGLRenderer({
canvas: this.offscreenCanvas,
});
}
setCanvas(width, height) {
this.offscreenCanvas.width = width;
this.offscreenCanvas.height = height;
}
handleMessage(event) {
console.time("Decode");
console.log("Message from Decode:", event.data);
const { id, blob } = event.data;
console.log(`${id} Frame id: ${blob}`);
console.timeEnd("Decode");
this.frames.push(blob);
console.log(this.frames.length);
// create shared worker
if (this.frames.length === this.expectedFramesLength) {
console.log("Complete");
}
}
sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async collectedFrames() {
while (this.frames.length != this.expectedFramesLength) {
await this.sleep(1);
}
return this.frames;
}
terminate() {
this.worker.terminate();
console.log("Worker Terminated");
}
render(scene, camera, frame) {
this.offScreenRenderer.render(scene, camera);
const imageBitmap = this.offscreenCanvas.transferToImageBitmap();
this.processFrame(imageBitmap, frame);
}
processFrame(imageBitmap, frame) {
const width = this.width;
const height = this.height;
const action = "process";
this.worker.postMessage({
action,
imageBitmap,
width,
height,
frame,
});
}
}
@@ -1,64 +0,0 @@
// This is responsible for processing and turning the data into
class ImageProcessorWorker {
constructor() {
this.queue = [];
this.processing = false;
this.holdingCanvas = new OffscreenCanvas(0, 0);
this.ctx = this.holdingCanvas.getContext("bitmaprenderer");
this.imageType = "image/png";
}
enqueueTask(imageBitmap, width, height, id) {
this.queue.push({ imageBitmap, width, height, id });
}
async processQueue() {
// If already processing, return to avoid duplicate execution
console.log(`Current Queue Length ${this.queue.length}`);
if (this.queue.length > 0) {
const { imageBitmap, width, height, id } = this.queue.shift();
console.log(`Working On an Frame Id ${id}`);
this.holdingCanvas.width = width;
this.holdingCanvas.height = height;
try {
// Create or reuse the offscreen canvas
this.ctx.transferFromImageBitmap(imageBitmap);
// Convert the canvas content to a Blob
const blob = await this.holdingCanvas.convertToBlob({
quality: 1.0,
type: this.imageType,
});
// Send the Blob back to the main thread
self.postMessage({ blob, id });
} catch (error) {
console.error("Error processing image:", error);
self.postMessage({ error: error.message });
}
}
console.log("Processing");
setTimeout(this.processQueue.bind(this), 0);
}
}
// Instantiate the ImageProcessorWorker
const runner = new ImageProcessorWorker();
let start = false;
self.addEventListener("message", async (event) => {
console.log(event.data);
const { type, imageBitmap, width, height, frame } = event.data;
console.log(`${type}: ${imageBitmap} ${width} ${height} ${frame}`);
if (start == false) {
start = true;
await runner.processQueue();
}
runner.enqueueTask(imageBitmap, width, height, frame);
});
@@ -1,17 +0,0 @@
// Listen for messages from the main thread
self.addEventListener("message", async function (event) {
const imageBitmap = event.data.imageBitmap;
const width = event.data.width;
const height = event.data.height;
const holdingCanvas = new OffscreenCanvas(width, height);
// in the worker use queue and pass the imageBitmap
const ctx = holdingCanvas.getContext("bitmaprenderer");
ctx.transferFromImageBitmap(imageBitmap);
const blob = await holdingCanvas.convertToBlob({
quality: 1.0,
type: "image/png",
});
self.postMessage(blob); // Send the result back to the main thread
});
@@ -1,89 +0,0 @@
class CanvasProcessor {
constructor() {
this.queue = [];
this.totalItems = 0;
this.completedItems = 0;
this.running = true;
}
// Add a canvas to the processing queue
enqueueCanvas(canvas, id) {
this.queue.push({ canvas, id });
}
// Set the total number of items to process
setTotalItems(count) {
this.totalItems = count;
}
// Start the processing loop
async processQueue() {
while (this.running) {
if (this.queue.length > 0) {
await this.processNext();
}
// Check if all items have been processed
if (this.completedItems >= this.totalItems) {
self.postMessage({ command: "done", message: "All items processed." });
this.running = false;
}
await this.sleep(100); // Small delay to prevent blocking
}
}
// Process the next canvas in the queue
async processNext() {
if (this.queue.length > 0) {
const { canvas, id } = this.queue.shift();
try {
const blob = await canvas.convertToBlob();
this.completedItems++;
const percentageComplete =
(this.completedItems / this.totalItems) * 100;
self.postMessage({
id,
blob,
completedItems: this.completedItems,
totalItems: this.totalItems,
percentageComplete,
});
} catch (error) {
self.postMessage({ id, error: error.message });
}
}
}
// Utility to sleep for a specified time
sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}
// Instantiate the processor
const processor = new CanvasProcessor();
// Listen for messages from the main thread
self.addEventListener("message", function (event) {
const data = event.data;
if (data.totalItems) {
processor.setTotalItems(data.totalItems);
}
if (data.canvas) {
processor.enqueueCanvas(data.canvas, data.id);
}
if (data.command === "start") {
processor.processQueue();
}
if (data.command === "stop") {
processor.running = false;
}
});
@@ -1,29 +0,0 @@
// if (this.engine_preprocessing) {
// // Normals
// this.setNormalMap();
// this.render_composer?.render();
// const normalRenderTask = this.videoAudioPreProcessor.retrieveFrame(
// this.rawRenderer,
// );
// this.engineFrameBuffers.normalFrames[this.renderIndex] =
// await normalRenderTask;
// // Depth
// this.setRenderDepth();
// this.render_composer?.render();
// const depthRenderTask = this.videoAudioPreProcessor.retrieveFrame(
// this.rawRenderer,
// );
// this.engineFrameBuffers.depthFrames[this.renderIndex] =
// await depthRenderTask;
// // Outline
// this.setOutlineRender();
// this.render_composer?.render();
// const outlineRenderTask = this.videoAudioPreProcessor.retrieveFrame(
// this.rawRenderer,
// );
// this.engineFrameBuffers.outlineFrames[this.renderIndex] =
// await outlineRenderTask;
// this.renderIndex += 1;
// } else {
@@ -1,121 +0,0 @@
// import { VideoGeneration } from "../../app/pages/PageEnigma/Editor/VideoProcessor/video_audio_preprocessor.ts";
//import { createFFmpeg, fetchFile, FFmpeg } from "@ffmpeg/ffmpeg";
self.importScripts("@ffmpeg/ffmpeg");
const JPEG = "image/jpeg";
const PNG = "image/png";
// chrome://inspect/#workers debug shared worker.
async function processVideoWithBuffer(
buffer,
fileName,
outFileName,
imagePrefix,
) {
try {
const imageFormat = this.properties.imageFormat;
let extension = "";
if (imageFormat == JPEG) {
extension = ".jpeg";
} else {
extension = ".png";
}
// console.log("Image Files");
// console.log(`Buffer Lenght ${buffer.length}`);
for (let index = 0; index < buffer.length; index++) {
const element = buffer[index];
if (element === null) {
continue;
}
// this can fail so catch any errors.
const image = await fetchFile(element);
await this.ffmpeg.FS(
"writeFile",
`${imagePrefix}_image${index}${extension}`,
image,
);
}
await this.ffmpeg.run(
"-framerate",
"" + this.properties.frameRate,
"-i",
`${imagePrefix}_image%d${extension}`,
`${fileName}.mp4`,
);
await this.ffmpeg.run(
"-i",
`${fileName}.mp4`,
"-f",
"lavfi",
"-i",
"anullsrc", // This adds a silent audio track
"-max_muxing_queue_size",
"999999",
"-vf", // WE REMOVED THE BLACK FRAME IN FFMEPG INSTEAD OF THE ENGINE
"select=gte(n\\,1)", // WE REMOVED THE BLACK FRAME IN FFMEPG INSTEAD OF THE ENGINE Applies a video filter to remove the first frame.
// "select=gte(n\\,1),scale=1024:576",
"-c:v",
"libx264", // Specify video codec (optional, but recommended for MP4)
"-c:a",
"aac", // Specify audio codec (optional, but recommended for MP4)
"-shortest", // Ensure output duration matches the shortest stream (video or audio)
"-pix_fmt",
"yuv420p",
"-f",
"mp4",
`${outFileName}.mp4`,
);
const blob = await this.outputMp4FromMemory(`${outFileName}`);
return blob;
} catch (error) {
console.error(error);
}
}
self.addEventListener("connect", function (event) {
const port = event.ports[0];
port.addEventListener("message", function (event) {
console.log("Message received from main script:", event.data);
port.postMessage("Hello from the Shared Worker!");
});
port.start();
});
// const videoGeneration = new VideoGeneration();
// self.addEventListener("connect", function (event) {
// const port = event.ports[0];
// console.log("WERE IN");
// port.start();
// port.addEventListener("message", async function (event) {
// console.log("Message received from main script:", event.data);
// const { colorFrames } = event.data;
// console.log(event.data);
// try {
// console.time("Processing Video Started");
// const colorBlob = await videoGeneration.processVideoWithBuffer(
// colorFrames,
// "color",
// "color",
// "colorFrame",
// );
// console.log(colorBlob);
// console.time("Processing Video Finished");
// port.postMessage({ colorBlob });
// } catch (error) {
// console.log(error);
// port.postMessage({ error: error.message });
// }
// });
// });
@@ -1,25 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>My first three.js app</title>
<style>
body {
margin: 0;
}
</style>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@v0.149.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@v0.149.0/examples/jsm/",
"ffmpeg": "https://cdn.jsdelivr.net/npm/@ffmpeg/ffmpeg@0.12.10/dist/umd/ffmpeg.min.js"
}
}
</script>
</head>
<body>
<script type="module" src="/main.js"></script>
</body>
</html>
@@ -1,80 +0,0 @@
import * as THREE from "three";
import { FrameCollectorManager } from "./ImageProcessingWorker.js";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000,
);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(animate);
document.body.appendChild(renderer.domElement);
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
const totalFrames = 720;
const imageProcessing = new FrameCollectorManager(
window.innerWidth,
window.innerHeight,
totalFrames,
);
let frame = 0;
// const sharedWorker = new SharedWorker("ffmpeg-worker.js");
// // Start the port
// sharedWorker.port.start();
// const message = { message: "Hello" };
// sharedWorker.port.postMessage(message);
if (typeof SharedWorker !== "undefined") {
console.log("Shared Workers are supported.");
// You can safely create a Shared Worker here
const worker = new SharedWorker("ffmpeg-worker.js", { type: "module" });
// Get the port for communication
const port = worker.port;
// Listen for messages from the worker
port.addEventListener("message", function (event) {
console.log("Message received from worker:", event.data);
});
// Start the port
port.start();
// Send a message to the worker
port.postMessage("Hello from the main script!");
} else {
console.log("Shared Workers are not supported in this browser.");
}
async function animate() {
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
frame += 1;
// buckets the frames.
if (frame <= totalFrames) {
//console.time("Render");
// can use the canvas renderer to enqueue as an option.
//imageProcessing.render(scene, camera, frame);
//console.timeEnd("Render");
}
// block and wait for all the frames
renderer.render(scene, camera);
}
@@ -1,10 +0,0 @@
# Run the TypeScript build
npm run build
# Check if the build was successful
if ($?) {
# Open the index.html file in the default web browser
Start-Process "index.html"
} else {
Write-Host "Build failed. Please check the errors above."
}
@@ -1,35 +0,0 @@
class Network {
constructor() {}
async sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async task1() {
console.log("World.");
let val = true;
while (val) {
await this.sleep(1000);
console.log("Task1 doing work.");
}
//throw Error("Ded World.")
}
async task2() {
console.log("Hello.");
let val = true;
while (val) {
await this.sleep(1000);
console.log("Task2 doing work.");
}
//throw Error("Ded Hello.")
}
}
const network = new Network();
async function main() {
network.task1();
network.task2();
}
main();