diff --git a/frontend/apps/editor2d/src/components/shared_authentication/authentication.tsx b/frontend/apps/editor2d/src/components/shared_authentication/authentication.tsx
new file mode 100644
index 0000000000..aa1d2d906b
--- /dev/null
+++ b/frontend/apps/editor2d/src/components/shared_authentication/authentication.tsx
@@ -0,0 +1,41 @@
+import { useSignals } from "@preact/signals-react/runtime";
+
+import { AUTH_STATUS } from "~/signals/authentication/enums";
+import { authentication } from "~/signals";
+
+import { ButtonLink } from "~/components/ui/ButtonLink";
+import ProfileDropdown from "~/src/components/ProfileDropdown";
+
+// TODO MAKE THIS A SHARED COMPONENT ART CRAFT
+export const AuthButtons = () => {
+ useSignals();
+
+ const { status } = authentication;
+
+ if (status.value === AUTH_STATUS.LOGGED_IN) {
+ return ;
+ } else {
+ return (
+
+
|
+
+
+ Login
+
+
+ Sign Up
+
+
+
+ );
+ }
+};
diff --git a/frontend/apps/editor2d/src/components/shared_authentication/profiledropdown.tsx b/frontend/apps/editor2d/src/components/shared_authentication/profiledropdown.tsx
new file mode 100644
index 0000000000..673c40d38d
--- /dev/null
+++ b/frontend/apps/editor2d/src/components/shared_authentication/profiledropdown.tsx
@@ -0,0 +1,188 @@
+import { faEnvelope, faKey, faUser } from "@fortawesome/pro-solid-svg-icons";
+import { GlobalSettingsManager } from "./pages/PageEnigma/GlobalSettingsManager";
+import { Button, Input } from "~/shared/components";
+import { addToast, authentication, signUp } from "~/signals";
+import { FormEvent, useRef, useState, StrictMode } from "react";
+import { AUTH_STATUS, ToastTypes } from "~/enums";
+import { useSignalEffect, useSignals } from "@preact/signals-react/runtime";
+import { useNavigate, useSearchParams, BrowserRouter } from "react-router-dom";
+import { createRoot } from "react-dom/client";
+
+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";
+
+config.autoAddCss = false; /* eslint-disable import/first */
+// TODO MAKE THIS A SHARED COMPONENT ART CRAFT
+const regEx =
+ /^(([^<>()[\].,;:\s@"]+(.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i;
+
+// 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 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 (
+
+
+

+
+
+
+ );
+}
+
+// TODO: Replace environment variables from `root.tsx`
+createRoot(document.getElementById("root")!).render(
+ <>
+
+
+
+
+
+
+ >,
+);
diff --git a/frontend/apps/editor2d/src/components/shared_authentication/signup.tsx b/frontend/apps/editor2d/src/components/shared_authentication/signup.tsx
new file mode 100644
index 0000000000..37a4aae073
--- /dev/null
+++ b/frontend/apps/editor2d/src/components/shared_authentication/signup.tsx
@@ -0,0 +1,188 @@
+import { faEnvelope, faKey, faUser } from "@fortawesome/pro-solid-svg-icons";
+import { GlobalSettingsManager } from "./pages/PageEnigma/GlobalSettingsManager";
+import { Button, Input } from "~/components";
+import { addToast, authentication, signUp } from "~/signals";
+import { FormEvent, useRef, useState, StrictMode } from "react";
+import { AUTH_STATUS, ToastTypes } from "~/enums";
+import { useSignalEffect, useSignals } from "@preact/signals-react/runtime";
+import { useNavigate, useSearchParams, BrowserRouter } from "react-router-dom";
+import { createRoot } from "react-dom/client";
+
+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";
+
+config.autoAddCss = false; /* eslint-disable import/first */
+// TODO MAKE THIS A SHARED COMPONENT ART CRAFT
+const regEx =
+ /^(([^<>()[\].,;:\s@"]+(.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i;
+
+// 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 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 (
+
+
+

+
+
+
+ );
+}
+
+// TODO: Replace environment variables from `root.tsx`
+createRoot(document.getElementById("root")!).render(
+ <>
+
+
+
+
+
+
+ >,
+);