From 0e8c1fa3e765eba18ee6124a22a3cb6c362564e8 Mon Sep 17 00:00:00 2001 From: BFlat Date: Wed, 14 May 2025 12:07:07 +0700 Subject: [PATCH 1/4] Update landing2 mobile videos fix --- .../apps/artcraft-website/src/pages/landing2/landing2.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/apps/artcraft-website/src/pages/landing2/landing2.tsx b/frontend/apps/artcraft-website/src/pages/landing2/landing2.tsx index ffcc045fb6..5a5e366de6 100644 --- a/frontend/apps/artcraft-website/src/pages/landing2/landing2.tsx +++ b/frontend/apps/artcraft-website/src/pages/landing2/landing2.tsx @@ -67,6 +67,7 @@ const Landing = () => { muted autoPlay loop + playsInline className="w-full aspect-[16/9] object-cover" > @@ -113,6 +114,7 @@ const Landing = () => { autoPlay muted loop + playsInline className="aspect-square w-full max-w-none rounded-tl-xl bg-gray-800 ring-1 ring-white/10 object-cover h-full rounded-lg overflow-hidden" /> @@ -137,13 +139,13 @@ const Landing = () => { variants={fadeUpVariants} >
-
From ef03e292c3a140ae7235361e215ed7f1bcd83879 Mon Sep 17 00:00:00 2001 From: ArEnSc <6252325+ArEnSc@users.noreply.github.com> Date: Wed, 14 May 2025 12:11:19 -0400 Subject: [PATCH 2/4] Add scene to 2d Lightbox modal. (#609) Co-authored-by: Michael Chung --- .../src/components/signaled/TopBar/TopBar.tsx | 43 ++++- .../features/ToolbarMain/ToolbarMain.tsx | 22 +-- .../apps/editor3d/app/src/routes/Signup.tsx | 150 ------------------ .../gallery-modal/src/lib/gallery-modal.tsx | 3 + .../lightbox-modal/src/lib/lightbox-modal.tsx | 57 +++++-- 5 files changed, 86 insertions(+), 189 deletions(-) delete mode 100644 frontend/apps/editor3d/app/src/routes/Signup.tsx diff --git a/frontend/apps/editor3d/app/src/components/signaled/TopBar/TopBar.tsx b/frontend/apps/editor3d/app/src/components/signaled/TopBar/TopBar.tsx index aab8660783..3735034345 100644 --- a/frontend/apps/editor3d/app/src/components/signaled/TopBar/TopBar.tsx +++ b/frontend/apps/editor3d/app/src/components/signaled/TopBar/TopBar.tsx @@ -19,6 +19,7 @@ import { Signal } from "@preact/signals-react"; import { useSignals } from "@preact/signals-react/runtime"; import { setLogoutStates } from "~/signals/authentication/utilities"; import { EngineContext } from "~/pages/PageEnigma/contexts/EngineContext"; +import { useAppUiContext } from "~/pages/Page2d/contextSignals/appUi"; function isEditorPath(path: string) { if (path === "/") return true; @@ -38,7 +39,12 @@ const appTabs: TabItem[] = [ { id: "3D", label: "3D" }, ]; -export const TopBar = ({ pageName, appTabIdSignal, setAppTabId, is3DInitSignal }: Props) => { +export const TopBar = ({ + pageName, + appTabIdSignal, + setAppTabId, + is3DInitSignal, +}: Props) => { useSignals(); const currentLocation = getCurrentLocationWithoutParams( @@ -55,7 +61,7 @@ export const TopBar = ({ pageName, appTabIdSignal, setAppTabId, is3DInitSignal } engine3D?.unmountEngine(); } setAppTabId(tabId); - } + }; const currentAppTabId = appTabIdSignal.value; const is3DInit = is3DInitSignal.value; @@ -143,6 +149,39 @@ export const TopBar = ({ pageName, appTabIdSignal, setAppTabId, is3DInitSignal } activeTab={activeLibraryTab} onTabChange={setActiveLibraryTab} onDownloadClicked={downloadFileFromUrl} + onAddToSceneClicked={async (url) => { + console.log("add to scene", url); + // Download the image from the URL and convert to File + const downloadImage = async (url: string): Promise => { + try { + // Fetch the image + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to fetch image: ${response.status}`); + } + + // Get the blob data + const blob = await response.blob(); + + // Extract filename from URL or use a default name + const filename = url.split("/").pop() || "image.png"; + + // Create a File object from the blob + const file = new File([blob], filename, { type: blob.type }); + + return file; + } catch (error) { + console.error("Error downloading image:", error); + return null; + } + }; + const file = await downloadImage(url); + if (file) { + console.log("file", file); + } else { + console.error("Failed to download image"); + } + }} /> ); diff --git a/frontend/apps/editor3d/app/src/pages/Page2d/components/features/ToolbarMain/ToolbarMain.tsx b/frontend/apps/editor3d/app/src/pages/Page2d/components/features/ToolbarMain/ToolbarMain.tsx index f428fc5911..352a7c1a03 100644 --- a/frontend/apps/editor3d/app/src/pages/Page2d/components/features/ToolbarMain/ToolbarMain.tsx +++ b/frontend/apps/editor3d/app/src/pages/Page2d/components/features/ToolbarMain/ToolbarMain.tsx @@ -29,6 +29,7 @@ import { PaintModeMenu } from "../../../components/ui/ToolbarMain/PaintModeMenu" // import { EraseModeMenu } from "../../../components/ui/ToolbarMain/EraseModeMenu"; // import { BackgroundMenu } from "../../../components/ui/ToolbarMain/BackgroundMenu"; // import { bgColor } from "../../../signals/uiEvents/toolbarMain/backgroundMenu"; +import { useAppUiContext } from "~/pages/Page2d/contextSignals/appUi"; export const ToolbarMain = ({ disabled = false, @@ -147,9 +148,6 @@ export const ToolbarMain = ({ Add Image - {/* - Add Video - */} @@ -191,24 +189,6 @@ export const ToolbarMain = ({ tooltip="Paint Brush" /> )} - - {/* Conditionally show eraser menu on mode selection */} - {/* {buttonProps.ERASER.active ? ( - - ) : ( - - )} */} diff --git a/frontend/apps/editor3d/app/src/routes/Signup.tsx b/frontend/apps/editor3d/app/src/routes/Signup.tsx deleted file mode 100644 index 54f743f432..0000000000 --- a/frontend/apps/editor3d/app/src/routes/Signup.tsx +++ /dev/null @@ -1,150 +0,0 @@ -// import { faEnvelope, faKey, faUser } from "@fortawesome/pro-solid-svg-icons"; -// import { Button, H1, Input, Link, P } from "~/components"; -// import { addToast, authentication, signUp } from "~/signals"; -// import { FormEvent, useRef, useState } from "react"; -// import { AUTH_STATUS, ToastTypes } from "~/enums"; -// import { useSignalEffect, useSignals } from "@preact/signals-react/runtime"; -// import { useNavigate, useSearchParams } from "react-router-dom"; - -// const regEx = -// /^(([^<>()[\].,;:\s@"]+(.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i; - -// export default function SignUpScreen() { -// useSignals(); -// const navigate = useNavigate(); -// const formRef = useRef(null); -// const [usernameError, setUsernameError] = useState(""); -// const [emailError, setEmailError] = useState(""); -// const [passwordError, setPasswordError] = useState(""); -// const [passwordConfirmationError, setPasswordConfirmationError] = -// useState(""); -// const [searchParams] = useSearchParams(); -// const { status: authStatus } = authentication; - -// const handleOnSubmit = (ev: FormEvent) => { -// ev.preventDefault(); -// if (formRef.current) { -// const form = new FormData(formRef.current); -// const email = form.get("email")?.toString(); -// const password = form.get("password")?.toString(); -// const passwordConfirmation = form -// .get("password-confirmation") -// ?.toString(); -// const username = form.get("username")?.toString(); -// setUsernameError(""); -// if (!username || username.length < 6) { -// setUsernameError("Username must be at least 6 characters long"); -// } -// setEmailError(""); -// if (!email || !email.match(regEx)) { -// setEmailError("Email must be a valid email address"); -// } -// setPasswordError(""); -// if (!password || password.length < 8) { -// setPasswordError("Password must be at least 8 characters long"); -// } -// setPasswordConfirmationError(""); -// if (!passwordError && passwordConfirmation !== password) { -// setPasswordConfirmationError("Passwords do not match"); -// } - -// if ( -// !usernameError && -// !passwordError && -// !emailError && -// !passwordConfirmationError && -// signUp -// ) { -// signUp({ -// username: username!, -// email: email!, -// password: password!, -// passwordConfirmation: passwordConfirmation!, -// }).then((error) => { -// console.log("Error occured", error); -// if (error) { -// addToast(ToastTypes.ERROR, error); -// } -// }); -// } -// } -// }; // end handleOnSubmit - -// useSignalEffect(() => { -// const redirectPath = searchParams.get("redirect"); -// if (authStatus.value === AUTH_STATUS.LOGGED_IN) { -// navigate(redirectPath ? redirectPath : "/"); -// return; -// } -// if (authStatus.value === AUTH_STATUS.NO_ACCESS) { -// window.open("https://storyteller.ai/", "_self"); -// return; -// } -// }); - -// return ( -//
-//
-//

Sign Up to Storyteller

-//
-//
-//
-// -//
-// -//
-// -//
-// -//
-//
-// -//
-//

Back to home

-// Back -//
-//
-//
-//

Already have an account?

-// Log in instead -//
-//
-//
-//
-// ); -// } diff --git a/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx b/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx index 9507ec01f1..046d75f64e 100644 --- a/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx +++ b/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx @@ -40,6 +40,7 @@ interface GalleryModalProps { activeTab: string; onTabChange: (tabId: string) => void; onDownloadClicked?: (url: string) => Promise; + onAddToSceneClicked?: (url: string) => Promise; } export const GalleryModal = React.memo( @@ -55,6 +56,7 @@ export const GalleryModal = React.memo( activeTab, onTabChange, onDownloadClicked, + onAddToSceneClicked, }: GalleryModalProps) => { const [groupedItems, setGroupedItems] = useState({}); const [loading, setLoading] = useState(false); @@ -342,6 +344,7 @@ export const GalleryModal = React.memo( createdAt={lightboxImage?.createdAt} downloadUrl={imageUrl} onDownloadClicked={onDownloadClicked} + onAddToSceneClicked={onAddToSceneClicked} /> ); diff --git a/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx b/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx index 4c437c98c3..138ca70e42 100644 --- a/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx +++ b/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx @@ -16,6 +16,7 @@ interface LightboxModalProps { additionalInfo?: React.ReactNode; downloadUrl?: string; onDownloadClicked?: (url: string) => Promise; + onAddToSceneClicked?: (url: string) => Promise; } export function LightboxModal({ @@ -29,10 +30,12 @@ export function LightboxModal({ additionalInfo, downloadUrl, onDownloadClicked, + onAddToSceneClicked, }: LightboxModalProps) { return createPortal(
+ {/* backdrop */} + + {/* dialog container */}
e.stopPropagation()} > + {/* close */} + + {/* content grid */}
+ {/* image panel */}
{!imageUrl ? (
@@ -81,6 +90,8 @@ export function LightboxModal({ /> )}
+ + {/* info + actions */}
@@ -94,30 +105,44 @@ export function LightboxModal({ )} {additionalInfo}
- {downloadUrl && ( -
- {onDownloadClicked ? ( + + {/* buttons with spacing */} + {(onAddToSceneClicked && downloadUrl) || downloadUrl ? ( +
+ {onAddToSceneClicked && downloadUrl && ( - ) : ( - e.stopPropagation()} - className="no-underline" - > - - )} + + {downloadUrl && + (onDownloadClicked ? ( + + ) : ( + e.stopPropagation()} + className="no-underline" + > + + + ))}
- )} + ) : null}
From a7794f6338bba9e098f8bae31dae13961aefb784 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Thu, 15 May 2025 17:20:56 -0400 Subject: [PATCH 3/4] don't need to build for editor2d anymore --- crates/desktop/artcraft/tauri.conf.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/desktop/artcraft/tauri.conf.json b/crates/desktop/artcraft/tauri.conf.json index d0030b8763..fc037816e8 100644 --- a/crates/desktop/artcraft/tauri.conf.json +++ b/crates/desktop/artcraft/tauri.conf.json @@ -4,10 +4,10 @@ "version": "0.0.1", "identifier": "ai.artcraft.app", "build": { - "frontendDist": "../../../", - "devUrl": "http://localhost:5741", - "beforeDevCommand": "nx run editor2d:dev", - "beforeBuildCommand": "nx run editor2d:build" + "frontendDist": "../../../frontend/apps/editor3d/dist", + "devUrl": "http://localhost:5173", + "beforeDevCommand": "nx run editor3d:dev", + "beforeBuildCommand": "nx run editor3d:build" }, "app": { "windows": [ From 93b53d84cd12b37c08c1328ce750adb52f3f20ac Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Thu, 15 May 2025 18:32:03 -0400 Subject: [PATCH 4/4] remove some dead code + fix missing "dev" assets (#610) --- .../app/src/Classes/GetFrontendEnvironment.ts | 65 ------------------- .../apps/editor3d/app/src/api/GetCdnOrigin.ts | 13 +--- .../app/src/pages/PageEnigma/Editor/scene.ts | 10 +-- .../signals/demoAssets/demoAnimationItems.ts | 5 +- .../signals/demoAssets/demoCharacterItems.ts | 5 +- frontend/apps/editor3d/tsconfig.json | 3 + frontend/libs/api/src/lib/GetCdnOrigin.ts | 43 +----------- frontend/libs/build-env/.babelrc | 12 ++++ frontend/libs/build-env/README.md | 7 ++ frontend/libs/build-env/package.json | 17 +++++ frontend/libs/build-env/src/index.ts | 3 + frontend/libs/build-env/src/lib/BuildEnv.ts | 30 +++++++++ .../libs/build-env/src/lib/BuildEnvType.ts | 61 +++++++++++++++++ frontend/libs/build-env/tsconfig.json | 13 ++++ frontend/libs/build-env/tsconfig.lib.json | 45 +++++++++++++ frontend/libs/build-env/tsconfig.spec.json | 34 ++++++++++ frontend/libs/build-env/vite.config.ts | 49 ++++++++++++++ frontend/package-lock.json | 8 +++ frontend/tsconfig.json | 3 + 19 files changed, 292 insertions(+), 134 deletions(-) delete mode 100644 frontend/apps/editor3d/app/src/Classes/GetFrontendEnvironment.ts create mode 100644 frontend/libs/build-env/.babelrc create mode 100644 frontend/libs/build-env/README.md create mode 100644 frontend/libs/build-env/package.json create mode 100644 frontend/libs/build-env/src/index.ts create mode 100644 frontend/libs/build-env/src/lib/BuildEnv.ts create mode 100644 frontend/libs/build-env/src/lib/BuildEnvType.ts create mode 100644 frontend/libs/build-env/tsconfig.json create mode 100644 frontend/libs/build-env/tsconfig.lib.json create mode 100644 frontend/libs/build-env/tsconfig.spec.json create mode 100644 frontend/libs/build-env/vite.config.ts diff --git a/frontend/apps/editor3d/app/src/Classes/GetFrontendEnvironment.ts b/frontend/apps/editor3d/app/src/Classes/GetFrontendEnvironment.ts deleted file mode 100644 index 230aea3999..0000000000 --- a/frontend/apps/editor3d/app/src/Classes/GetFrontendEnvironment.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Environment } from "~/configs"; - -export enum FrontendEnvironmentType { - Dev, - DevProxy, - Staging, - Production, -} - -export class FrontendEnvironment { - private static instance: FrontendEnvironment; - private readonly environment: FrontendEnvironmentType; - private readonly isLocalDev: boolean; - - public static getInstance(): FrontendEnvironment { - if (FrontendEnvironment.instance !== undefined) { - return FrontendEnvironment.instance; - } - - if (typeof document === "undefined") { - // TODO(bt,2025-02-10): This is digusting. Vite or whatever - // doesn't have 'document' defined early in preloading execution. - // This non-cached codepath is dangerous, because who knows what - // happens downstream. Fix this ASAP. - return new FrontendEnvironment(FrontendEnvironmentType.Production, false); - } - - const isLocalDev = - document.location.host.includes("localhost") || - document.location.host.startsWith("dev.") || - document.location.host.startsWith("development."); - - let environmentType = FrontendEnvironmentType.Production; - - if (isLocalDev) { - environmentType = FrontendEnvironmentType.Dev; - } - - if (document.location.host.includes("dev") && - document.location.hostname.includes("proxy")) { - environmentType = FrontendEnvironmentType.DevProxy; - } - - const instance = new FrontendEnvironment(environmentType, isLocalDev); - FrontendEnvironment.instance = instance; - return instance; - } - - public getFrontendEnvironmentType(): FrontendEnvironmentType { - return this.environment; - } - - public getIsLocalDev(): boolean { - return this.isLocalDev; - } - - private constructor(environment: FrontendEnvironmentType, isLocalDev: boolean) { - this.environment = environment; - this.isLocalDev = isLocalDev; - } -} - -export function GetFrontendEnvironment() : FrontendEnvironment { - return FrontendEnvironment.getInstance(); -} diff --git a/frontend/apps/editor3d/app/src/api/GetCdnOrigin.ts b/frontend/apps/editor3d/app/src/api/GetCdnOrigin.ts index 80376dd74a..79e16f250c 100644 --- a/frontend/apps/editor3d/app/src/api/GetCdnOrigin.ts +++ b/frontend/apps/editor3d/app/src/api/GetCdnOrigin.ts @@ -1,4 +1,4 @@ -import { GetFrontendEnvironment } from "~/Classes/GetFrontendEnvironment"; +// TODO(bt,2025-05-15): Remove this code /** * Return the CDN origin (scheme + host + optional port, but no path) @@ -15,16 +15,9 @@ class CdnHostHelper { return CdnHostHelper.instance; } - const isLocalDev = GetFrontendEnvironment().getIsLocalDev(); - - let origin; - if (isLocalDev) { - //origin = 'https://pub-c8a4a5bdbdb048f286b77bdf9f786ff2.r2.dev'; - origin = 'https://cdn-2.fakeyou.com'; - } else { - origin = 'https://cdn-2.fakeyou.com'; - } + const origin = 'https://cdn-2.fakeyou.com'; const instance = new CdnHostHelper(origin); + CdnHostHelper.instance = instance; return instance; } diff --git a/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/scene.ts b/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/scene.ts index f2da3118bd..d25abed154 100644 --- a/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/scene.ts +++ b/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/scene.ts @@ -1,9 +1,6 @@ import * as THREE from "three"; import { GLTFLoader, GLTF } from "three/addons/loaders/GLTFLoader.js"; import { MMDLoader } from "three/addons/loaders/MMDLoader.js"; - -import environmentVariables from "~/Classes/EnvironmentVariables"; - import { Font } from "three/examples/jsm/loaders/FontLoader.js"; import { generateUUID } from "three/src/math/MathUtils.js"; import { LoadingPlaceHolderManager } from "./placeholder_manager"; @@ -13,8 +10,6 @@ import { ChromaKeyMaterial } from "./chromakey"; import { TimeLine } from "./timeline"; import { ClipGroup, ClipType } from "~/enums"; import { ClipUI } from "../clips/clip_ui"; -import { GetCdnOrigin } from "~/api/GetCdnOrigin"; -import { GetFrontendEnvironment } from "~/Classes/GetFrontendEnvironment"; import { gridVisibility } from "../signals/engine"; import { InfiniteGridHelper } from "./InfiniteGridHelper"; import { cameras, selectedCameraId } from "../signals/camera"; @@ -382,10 +377,7 @@ class Scene { cameraConfig.position.z, ); - // TODO(bt,2025-02-10): Make the local dev assets more configurable or seedable. - const camera_id = GetFrontendEnvironment().getIsLocalDev() - ? "m_cxh4asqhapdz10j880755dg4yevshb" // Local development - : "m_cxh4asqhapdz10j880755dg4yevshb"; // Production + const camera_id = "m_cxh4asqhapdz10j880755dg4yevshb"; this.loadGlbWithPlaceholder( camera_id, diff --git a/frontend/apps/editor3d/app/src/pages/PageEnigma/signals/demoAssets/demoAnimationItems.ts b/frontend/apps/editor3d/app/src/pages/PageEnigma/signals/demoAssets/demoAnimationItems.ts index 365869f637..39320c31c2 100644 --- a/frontend/apps/editor3d/app/src/pages/PageEnigma/signals/demoAssets/demoAnimationItems.ts +++ b/frontend/apps/editor3d/app/src/pages/PageEnigma/signals/demoAssets/demoAnimationItems.ts @@ -1,5 +1,4 @@ import { signal } from "@preact/signals-core"; -import { GetFrontendEnvironment } from "~/Classes/GetFrontendEnvironment"; import { AssetType } from "~/enums"; import { MediaItem } from "~/pages/PageEnigma/models"; @@ -26,9 +25,7 @@ export const demoAnimationItems = signal([ }, { version: 1, - media_id: GetFrontendEnvironment().getIsLocalDev() ? - "m_z0wj1rckcsea1sb0ht27m41g2w4zss" : // Local development - "m_s7d4ems68sb2vqj4pdje1rc15q3ycp", // Production + media_id: "m_s7d4ems68sb2vqj4pdje1rc15q3ycp", type: AssetType.ANIMATION, length: 100, name: "Walk", diff --git a/frontend/apps/editor3d/app/src/pages/PageEnigma/signals/demoAssets/demoCharacterItems.ts b/frontend/apps/editor3d/app/src/pages/PageEnigma/signals/demoAssets/demoCharacterItems.ts index bdcfd8fbae..e1ba2f42b1 100644 --- a/frontend/apps/editor3d/app/src/pages/PageEnigma/signals/demoAssets/demoCharacterItems.ts +++ b/frontend/apps/editor3d/app/src/pages/PageEnigma/signals/demoAssets/demoCharacterItems.ts @@ -1,14 +1,11 @@ import { signal } from "@preact/signals-core"; import { MediaItem } from "~/pages/PageEnigma/models"; import { AssetType } from "~/enums"; -import { GetFrontendEnvironment } from "~/Classes/GetFrontendEnvironment"; export const demoCharacterItems = signal([ { version: 1, - media_id: GetFrontendEnvironment().getIsLocalDev() ? - "m_8dvne30awgsyrb45xvha9f23gtgg8w" : // Local development - "m_gff67btr810vg3ng9szj85zskztcgy", // Production + media_id: "m_gff67btr810vg3ng9szj85zskztcgy", type: AssetType.CHARACTER, name: "Story Girl", thumbnail: "/resources/characters/img13.png", diff --git a/frontend/apps/editor3d/tsconfig.json b/frontend/apps/editor3d/tsconfig.json index a5421155f0..b89dbb548a 100644 --- a/frontend/apps/editor3d/tsconfig.json +++ b/frontend/apps/editor3d/tsconfig.json @@ -84,6 +84,9 @@ }, { "path": "../../libs/tauri-utils" + }, + { + "path": "../../libs/build-env" } ] } diff --git a/frontend/libs/api/src/lib/GetCdnOrigin.ts b/frontend/libs/api/src/lib/GetCdnOrigin.ts index 5d604bc745..132c39e58c 100644 --- a/frontend/libs/api/src/lib/GetCdnOrigin.ts +++ b/frontend/libs/api/src/lib/GetCdnOrigin.ts @@ -1,45 +1,4 @@ -// import { GetFrontendEnvironment } from "~/Classes/GetFrontendEnvironment"; - -/** - * Return the CDN origin (scheme + host + optional port, but no path) - * See: https://web.dev/articles/url-parts - * - * TODO(bt,2025-02-10): Make this easier to configure in development - */ -// class CdnHostHelper { -// private static instance: CdnHostHelper; -// private readonly origin: string; - -// public static getInstance(): CdnHostHelper { -// if (CdnHostHelper.instance !== undefined) { -// return CdnHostHelper.instance; -// } - -// const isLocalDev = GetFrontendEnvironment().getIsLocalDev(); - -// let origin; -// if (isLocalDev) { -// //origin = 'https://pub-c8a4a5bdbdb048f286b77bdf9f786ff2.r2.dev'; -// origin = "https://cdn-2.fakeyou.com"; -// } else { -// origin = "https://cdn-2.fakeyou.com"; -// } -// const instance = new CdnHostHelper(origin); -// CdnHostHelper.instance = instance; -// return instance; -// } - -// public getOrigin(): string { -// return this.origin; -// } - -// private constructor(origin: string) { -// this.origin = origin; -// } -// } - -// TODO(bt,2025-02-10): Consolidate all of this logic. +// TODO(bt,2025-05-15): Remove this code export function GetCdnOrigin(): string { - // return CdnHostHelper.getInstance().getOrigin(); return "https://cdn-2.fakeyou.com"; } diff --git a/frontend/libs/build-env/.babelrc b/frontend/libs/build-env/.babelrc new file mode 100644 index 0000000000..1ea870ead4 --- /dev/null +++ b/frontend/libs/build-env/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/frontend/libs/build-env/README.md b/frontend/libs/build-env/README.md new file mode 100644 index 0000000000..9becded76e --- /dev/null +++ b/frontend/libs/build-env/README.md @@ -0,0 +1,7 @@ +# @frontend/build-env + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test @frontend/build-env` to execute the unit tests via [Vitest](https://vitest.dev/). diff --git a/frontend/libs/build-env/package.json b/frontend/libs/build-env/package.json new file mode 100644 index 0000000000..8712e95b42 --- /dev/null +++ b/frontend/libs/build-env/package.json @@ -0,0 +1,17 @@ +{ + "name": "@frontend/build-env", + "version": "0.0.1", + "type": "module", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + "./package.json": "./package.json", + ".": { + "development": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + } + } +} diff --git a/frontend/libs/build-env/src/index.ts b/frontend/libs/build-env/src/index.ts new file mode 100644 index 0000000000..383df5f16d --- /dev/null +++ b/frontend/libs/build-env/src/index.ts @@ -0,0 +1,3 @@ + +export * from './lib/BuildEnv'; +export * from './lib/BuildEnvType'; diff --git a/frontend/libs/build-env/src/lib/BuildEnv.ts b/frontend/libs/build-env/src/lib/BuildEnv.ts new file mode 100644 index 0000000000..983923e3fa --- /dev/null +++ b/frontend/libs/build-env/src/lib/BuildEnv.ts @@ -0,0 +1,30 @@ +import { BuildEnvType, GetBuildEnvType } from "./BuildEnvType"; + +export class BuildEnv { + private static instance: BuildEnv; + private readonly envType: BuildEnvType; + + public static getInstance(): BuildEnv { + if (BuildEnv.instance !== undefined) { + return BuildEnv.instance; + } + + const envType = GetBuildEnvType(); + const instance = new BuildEnv(envType); + BuildEnv.instance = instance; + + return instance; + } + + public getType(): BuildEnvType { + return this.envType; + } + + private constructor(envType: BuildEnvType) { + this.envType = envType; + } +} + +export function GetBuildEnv() : BuildEnv { + return BuildEnv.getInstance(); +} diff --git a/frontend/libs/build-env/src/lib/BuildEnvType.ts b/frontend/libs/build-env/src/lib/BuildEnvType.ts new file mode 100644 index 0000000000..9ea80ecd06 --- /dev/null +++ b/frontend/libs/build-env/src/lib/BuildEnvType.ts @@ -0,0 +1,61 @@ + +/** + * This is cached whenever vite/remix launches or CI/CD bakes in constants. + * It's fed from an environment variable. + */ +export enum BuildEnvType { + Dev, + DevProxy, + Staging, + Production, +} + +// Use CI/CD to replace this value with one of the BuildEnvType values. +const BUILD_ENV_TYPE = '%BUILD_ENV_TYPE_VALUE%'; + +// Attempt to read the environment type. +export function GetBuildEnvType() : BuildEnvType { + let maybeEnvType : BuildEnvType | undefined = undefined; + + if (!BUILD_ENV_TYPE.startsWith("%") && !BUILD_ENV_TYPE.endsWith("%")) { + maybeEnvType = stringToEnvType(BUILD_ENV_TYPE); + if (maybeEnvType !== undefined) { + return maybeEnvType; + } + } + + if (import.meta.env.BUILD_ENV_TYPE !== undefined) { + console.log(`Reading environment variable 'BUILD_ENV_TYPE' (= ${import.meta.env.BUILD_ENV_TYPE}) to determine build environment.`) + maybeEnvType = stringToEnvType(import.meta.env.BUILD_ENV_TYPE); + if (maybeEnvType !== undefined) { + return maybeEnvType; + } + } + + if (import.meta.env.VITE_ENVIRONMENT_TYPE !== undefined) { + console.log(`Reading legacy environment variable 'VITE_ENVIRONMENT_TYPE' (= ${import.meta.env.VITE_ENVIRONMENT_TYPE}) to determine build environment.`) + maybeEnvType = stringToEnvType(import.meta.env.VITE_ENVIRONMENT_TYPE); + if (maybeEnvType !== undefined) { + return maybeEnvType; + } + } + + // Always default to production. + return BuildEnvType.Production; +} + +function stringToEnvType(str: string): BuildEnvType | undefined { + switch (str.toLocaleLowerCase()) { + case "dev": + case "development": + return BuildEnvType.Dev; + case "dev-proxy": + case "devproxy": + return BuildEnvType.DevProxy; + case "stage": + case "staging": + return BuildEnvType.Staging; + default: + return undefined; + } +} diff --git a/frontend/libs/build-env/tsconfig.json b/frontend/libs/build-env/tsconfig.json new file mode 100644 index 0000000000..8c5fc025ff --- /dev/null +++ b/frontend/libs/build-env/tsconfig.json @@ -0,0 +1,13 @@ +{ + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "extends": "../../tsconfig.base.json" +} diff --git a/frontend/libs/build-env/tsconfig.lib.json b/frontend/libs/build-env/tsconfig.lib.json new file mode 100644 index 0000000000..ea31c66850 --- /dev/null +++ b/frontend/libs/build-env/tsconfig.lib.json @@ -0,0 +1,45 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "types": [ + "node", + "@nx/react/typings/cssmodule.d.ts", + "@nx/react/typings/image.d.ts", + "vite/client" + ], + "rootDir": "src", + "jsx": "react-jsx", + "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo" + }, + "exclude": [ + "out-tsc", + "dist", + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx", + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx" + ], + "include": [ + "src/**/*.js", + "src/**/*.jsx", + "src/**/*.ts", + "src/**/*.tsx" + ] +} diff --git a/frontend/libs/build-env/tsconfig.spec.json b/frontend/libs/build-env/tsconfig.spec.json new file mode 100644 index 0000000000..c1faced82c --- /dev/null +++ b/frontend/libs/build-env/tsconfig.spec.json @@ -0,0 +1,34 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./out-tsc/vitest", + "types": [ + "vitest/globals", + "vitest/importMeta", + "vite/client", + "node", + "vitest" + ], + "jsx": "react-jsx" + }, + "include": [ + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/frontend/libs/build-env/vite.config.ts b/frontend/libs/build-env/vite.config.ts new file mode 100644 index 0000000000..577c9d15f1 --- /dev/null +++ b/frontend/libs/build-env/vite.config.ts @@ -0,0 +1,49 @@ +/// +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import dts from 'vite-plugin-dts'; +import * as path from 'path'; + +export default defineConfig(() => ({ + root: __dirname, + cacheDir: '../../node_modules/.vite/libs/build-env', + plugins: [react(), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })], + // Uncomment this if you are using workers. + // worker: { + // plugins: [ nxViteTsPaths() ], + // }, + // Configuration for building your library. + // See: https://vitejs.dev/guide/build.html#library-mode + build: { + outDir: './dist', + emptyOutDir: true, + reportCompressedSize: true, + commonjsOptions: { + transformMixedEsModules: true, + }, + lib: { + // Could also be a dictionary or array of multiple entry points. + entry: 'src/index.ts', + name: '@frontend/build-env', + fileName: 'index', + // Change this to the formats you want to support. + // Don't forget to update your package.json as well. + formats: ['es' as const] + }, + rollupOptions: { + // External packages that should not be bundled into your library. + external: ['react','react-dom','react/jsx-runtime'] + }, + }, + test: { + watch: false, + globals: true, + environment: 'jsdom', + include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + reporters: ['default'], + coverage: { + reportsDirectory: './test-output/vitest/coverage', + provider: 'v8' as const, + } + }, +})); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6203d8cd54..051e606855 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -2187,6 +2187,10 @@ "@tauri-apps/plugin-upload": "2.2.1" } }, + "libs/build-env": { + "name": "@frontend/build-env", + "version": "0.0.1" + }, "libs/common": { "name": "@storyteller/common", "version": "0.0.1" @@ -5020,6 +5024,10 @@ "resolved": "apps/artcraft-website", "link": true }, + "node_modules/@frontend/build-env": { + "resolved": "libs/build-env", + "link": true + }, "node_modules/@frontend/login": { "resolved": "libs/login", "link": true diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 3c6736b982..ab379e6875 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -89,6 +89,9 @@ }, { "path": "./libs/components/switch" + }, + { + "path": "./libs/build-env" } ] }