Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d4fd6379b | ||
|
|
431f2efcf0 | ||
|
|
7c64da88da | ||
|
|
93eb78da91 | ||
|
|
f96e79ff49 | ||
|
|
39e6f4511f | ||
|
|
4c0d062d76 |
@@ -174,7 +174,7 @@ serde_derive = "1.0.219"
|
||||
serde_json = "1.0.140"
|
||||
serde_yaml = "0.9.1"
|
||||
sha3 = "0.10.8"
|
||||
tauri = { version = "2.4.1", features = ["unstable"] }
|
||||
tauri = { version = "2.4.1", features = ["unstable", "devtools"] } # NB: "devtools" prevents App Store distribution
|
||||
thiserror = "2.0.12"
|
||||
toml = "0.8.20"
|
||||
url = { version = "2.5.4", features = ["serde"] }
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 1023 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
@@ -54,7 +54,9 @@ pub async fn sora_image_remix_command(
|
||||
sora_creds_manager: State<'_, SoraCredentialManager>,
|
||||
sora_task_queue: State<'_, SoraTaskQueue>,
|
||||
) -> Result<String, String> {
|
||||
info!("image_generation_command called; processing image...");
|
||||
|
||||
info!("image_generation_command called; scene media token: {:?}, additional images: {:?}",
|
||||
request.snapshot_media_token, request.maybe_additional_images);
|
||||
|
||||
// TODO(bt,2025-04-24): Better error messages to caller
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"build": {
|
||||
"frontendDist": "../../../frontend/apps/editor3d/dist",
|
||||
"beforeDevCommand": "echo 'no dev'",
|
||||
"devUrl": "http://localhost:5173",
|
||||
"beforeBuildCommand": "VITE_ENVIRONMENT_TYPE=production nx run editor3d:build"
|
||||
}
|
||||
}
|
||||
@@ -305,6 +305,25 @@ pub async fn fetch<R: Runtime>(
|
||||
headers.remove(header::ORIGIN);
|
||||
};
|
||||
|
||||
// NB(bt): In production builds, the origin seems to be "null". This is what our server CORS filter sees:
|
||||
// [2025-05-01T23:42:35Z WARN actix_cors_configs::configs::fakeyou] Invalid origin: "null"
|
||||
// [2025-05-01T23:42:35Z WARN actix_cors_configs::util::netlify_branch_domain_matches] Invalid origin: "null"
|
||||
// [2025-05-01T23:42:35Z WARN actix_cors_configs::util::netlify_branch_domain_matches] Invalid origin: "null"
|
||||
// ...
|
||||
// [2025-05-01T23:42:35Z WARN actix_cors_configs::util::netlify_branch_domain_matches] Invalid origin: "null"
|
||||
// [2025-05-01T23:42:35Z INFO actix_web::middleware::logger] [storyteller-web-85f9b4974-ddvq7] IP=[redact]
|
||||
// 939322e1095ee5e4-IAD "POST /v1/login HTTP/1.1" 400 42 "-" "tauri-plugin-http/2.4.3" 0.000076
|
||||
let mut maybe_origin_header = headers.get(header::ORIGIN);
|
||||
|
||||
let replace_origin_header = maybe_origin_header.is_none()
|
||||
|| maybe_origin_header == Some(&HeaderValue::from_static("null"));
|
||||
|
||||
if replace_origin_header {
|
||||
// TODO(bt): Make this a sentinel origin, eg. sentinel.storyteller.ai.
|
||||
// NB: HeadersMap is a multimap, but this should ensure we just have one value.
|
||||
headers.insert(header::ORIGIN, HeaderValue::from_static("https://studio.storyteller.ai"));
|
||||
}
|
||||
|
||||
if let Some(data) = data {
|
||||
request = request.body(data);
|
||||
}
|
||||
|
||||
@@ -77,6 +77,12 @@ export const Login = () => {
|
||||
|
||||
return (
|
||||
<div className="pegboard fixed flex h-full w-full flex-col items-center justify-center bg-center bg-repeat">
|
||||
<Button
|
||||
className="absolute left-4 top-4 px-3 py-2 text-sm"
|
||||
onClick={() => navigate("/")}
|
||||
>
|
||||
Back to Home
|
||||
</Button>
|
||||
<div className="mb-7 flex w-10/12 max-w-2xl items-center justify-center gap-4">
|
||||
<img
|
||||
src="/brand/artcraft-logo.png"
|
||||
@@ -126,12 +132,12 @@ export const Login = () => {
|
||||
)}
|
||||
</div>
|
||||
<div className="align-items flex w-full max-w-xl rounded-b-xl bg-ui-controls px-6 py-4">
|
||||
<a
|
||||
{/* <a
|
||||
href="https://storyteller.ai/password-reset"
|
||||
className="grow text-sm !text-primary-400 transition-all duration-150 hover:!text-primary-300"
|
||||
>
|
||||
Forgot your password?
|
||||
</a>
|
||||
</a> */}
|
||||
<div className="flex justify-end gap-1 text-sm">
|
||||
<p className="opacity-75">Don't have an account?</p>
|
||||
<Link to="/signup">Sign Up</Link>
|
||||
|
||||
@@ -75,6 +75,12 @@ export const Signup = () => {
|
||||
|
||||
return (
|
||||
<div className="pegboard fixed flex h-full w-full flex-col items-center justify-center bg-center bg-repeat">
|
||||
<Button
|
||||
className="absolute left-4 top-4 px-3 py-2 text-sm"
|
||||
onClick={() => navigate("/")}
|
||||
>
|
||||
Back to Home
|
||||
</Button>
|
||||
<div className="mb-7 flex w-10/12 max-w-2xl items-center justify-center gap-4">
|
||||
<img
|
||||
src="/brand/artcraft-logo.png"
|
||||
|
||||
@@ -28,11 +28,11 @@ import { getCanvasRenderBitmap } from "~/signal/canvasRenderBitmap";
|
||||
import { EncodeImageBitmapToBase64 } from "~/utilities/EncodeImageBitmapToBase64";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { PopoverItem } from "~/components/reusable/Popover";
|
||||
import { Api } from "~/KonvaApp/Api";
|
||||
|
||||
import { useJobContext } from "~/components/JobContext";
|
||||
import { IsDesktopApp } from "@storyteller/tauri-utils";
|
||||
import { GalleryItem, GalleryModal } from "@storyteller/ui-gallery-modal";
|
||||
|
||||
import { PromptsApi } from "@storyteller/api";
|
||||
interface ReferenceImage {
|
||||
id: string;
|
||||
url: string;
|
||||
@@ -215,7 +215,7 @@ export const PromptBox = () => {
|
||||
};
|
||||
|
||||
const handleTauriEnqueue = async () => {
|
||||
const api = new Api();
|
||||
const api = new PromptsApi();
|
||||
let image = getCanvasRenderBitmap();
|
||||
if (image === undefined) {
|
||||
return;
|
||||
@@ -261,7 +261,7 @@ export const PromptBox = () => {
|
||||
};
|
||||
|
||||
const handleWebEnqueue = async () => {
|
||||
const api = new Api();
|
||||
const api = new PromptsApi();
|
||||
let image = getCanvasRenderBitmap();
|
||||
if (image === undefined) {
|
||||
toast.error(
|
||||
|
||||
@@ -52,7 +52,7 @@ export const ToolbarTopLeft = () => {
|
||||
className="h-[28px] select-none"
|
||||
/>
|
||||
|
||||
<Button
|
||||
{/* <Button
|
||||
variant="secondary"
|
||||
icon={faChevronRight}
|
||||
iconClassName="text-xs"
|
||||
@@ -61,7 +61,7 @@ export const ToolbarTopLeft = () => {
|
||||
className="bg-transparent p-0 text-sm text-white/80 hover:bg-transparent hover:text-white hover:underline hover:underline-offset-2"
|
||||
>
|
||||
Go to 3D Stage Editor
|
||||
</Button>
|
||||
</Button> */}
|
||||
|
||||
{/* <TabSelector
|
||||
tabs={tabs}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { Fragment } from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faChevronDown,
|
||||
@@ -65,18 +66,18 @@ export function ProfileDropdown() {
|
||||
)}
|
||||
>
|
||||
<MenuItem
|
||||
as="a"
|
||||
as="button"
|
||||
href={profileUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={twMerge(
|
||||
"flex w-full items-center gap-3 rounded-lg px-3 py-2",
|
||||
"text-md font-medium text-gray-700 hover:bg-gray-100",
|
||||
"text-md font-medium text-white hover:bg-gray-100 hover:text-gray-700",
|
||||
"transition-colors duration-150",
|
||||
)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faUser} className="w-4 text-gray-700" />
|
||||
<span className="text-gray-700">My Profile</span>
|
||||
<FontAwesomeIcon icon={faUser} className="w-4" />
|
||||
My Profile
|
||||
</MenuItem>
|
||||
|
||||
{menuOptions.map((option, index) => (
|
||||
@@ -85,7 +86,7 @@ export function ProfileDropdown() {
|
||||
as="button"
|
||||
className={twMerge(
|
||||
"flex w-full items-center gap-3 rounded-lg px-3 py-2",
|
||||
"text-md font-medium text-gray-700 hover:bg-gray-100",
|
||||
"text-md font-medium text-white hover:bg-gray-100 hover:text-gray-700",
|
||||
"transition-colors duration-150",
|
||||
)}
|
||||
onClick={(e) => {
|
||||
@@ -94,7 +95,7 @@ export function ProfileDropdown() {
|
||||
option.onClick();
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={option.icon} className="w-4 text-gray-700" />
|
||||
<FontAwesomeIcon icon={option.icon} className="w-4" />
|
||||
<span>{option.label}</span>
|
||||
</MenuItem>
|
||||
))}
|
||||
|
||||
@@ -31,20 +31,26 @@
|
||||
{
|
||||
"path": "../../libs/components/settings-modal/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/button/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/gallery-modal/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/tauri-utils/tsconfig.lib.json"
|
||||
"path": "../../libs/components/modal/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/button/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/tooltip/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/demo-modal/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/api/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/tauri-utils/tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,14 +4,17 @@
|
||||
{
|
||||
"path": "../../libs/components/settings-modal"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/button"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/gallery-modal"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/tauri-utils"
|
||||
"path": "../../libs/components/modal"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/button"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/tooltip"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/demo-modal"
|
||||
@@ -19,6 +22,9 @@
|
||||
{
|
||||
"path": "../../libs/api"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/tauri-utils"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
|
||||
@@ -58,7 +58,7 @@ export const TopBar = ({ pageName }: Props) => {
|
||||
Back to Editor
|
||||
</ButtonLink>
|
||||
)}
|
||||
<Button
|
||||
{/* <Button
|
||||
variant="secondary"
|
||||
icon={faChevronRight}
|
||||
iconClassName="text-xs"
|
||||
@@ -67,7 +67,7 @@ export const TopBar = ({ pageName }: Props) => {
|
||||
className="bg-transparent p-0 text-sm text-white/80 hover:bg-transparent hover:text-white hover:underline hover:underline-offset-2"
|
||||
>
|
||||
Go to 2D Canvas
|
||||
</Button>
|
||||
</Button> */}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-center gap-2 font-medium">
|
||||
|
||||
@@ -124,11 +124,17 @@ export default function LoginScreen() {
|
||||
)}
|
||||
</div>
|
||||
<div className="align-items flex w-full max-w-xl rounded-b-xl bg-ui-controls px-6 py-4">
|
||||
<a
|
||||
{/* <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't have an account?</p>
|
||||
|
||||
@@ -18,6 +18,7 @@ import { GetFrontendEnvironment } from "~/Classes/GetFrontendEnvironment";
|
||||
import { gridVisibility } from "../signals/engine";
|
||||
import { InfiniteGridHelper } from "./InfiniteGridHelper";
|
||||
import { cameras, selectedCameraId } from "../signals/camera";
|
||||
import { MediaFilesApi } from "@storyteller/api";
|
||||
|
||||
class Scene {
|
||||
name: string;
|
||||
@@ -42,6 +43,8 @@ class Scene {
|
||||
// loading indicator manager
|
||||
placeholder_manager: LoadingPlaceHolderManager | undefined;
|
||||
|
||||
mediaFilesApi: MediaFilesApi;
|
||||
|
||||
updateSurfaceIdAttributeToMesh: Function;
|
||||
helper: MMDAnimationHelper;
|
||||
ambientLight: THREE.AmbientLight | undefined;
|
||||
@@ -74,6 +77,7 @@ class Scene {
|
||||
// global names
|
||||
this.camera_name = camera_name;
|
||||
this.placeholder_manager = undefined;
|
||||
this.mediaFilesApi = new MediaFilesApi();
|
||||
this.updateSurfaceIdAttributeToMesh = updateSurfaceIdAttributeToMesh;
|
||||
|
||||
this.current_scene_checksum = "";
|
||||
@@ -427,16 +431,10 @@ class Scene {
|
||||
|
||||
// TODO: REPLACE
|
||||
async getMediaURL(media_id: string) {
|
||||
//This is for prod when we have the proper info on the url.
|
||||
const api_base_url = environmentVariables.values.BASE_API;
|
||||
const url = `${api_base_url}/v1/media_files/file/${media_id}`;
|
||||
const response = await fetch(url);
|
||||
const json = await JSON.parse(await response.text());
|
||||
const bucketPath = json["media_file"]["public_bucket_path"];
|
||||
//const media_base_url = `${media_api_base_url}/vocodes-public`;
|
||||
const media_base_url = GetCdnOrigin();
|
||||
const media_url = `${media_base_url}${bucketPath}`;
|
||||
return media_url;
|
||||
const response = await this.mediaFilesApi.GetMediaFileByToken({
|
||||
mediaFileToken: media_id
|
||||
});
|
||||
return response.data!.media_links.cdn_url;
|
||||
}
|
||||
|
||||
private floatToPercent(value: number) {
|
||||
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
|
||||
import Queue from "~/pages/PageEnigma/Queue/Queue";
|
||||
import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions";
|
||||
import { PromptsApi } from "@storyteller/api";
|
||||
|
||||
import {
|
||||
cameras,
|
||||
@@ -452,18 +453,20 @@ export const PromptBox = () => {
|
||||
if (editorEngine) {
|
||||
editorEngine.positive_prompt = prompt;
|
||||
|
||||
const engineApi = new EngineApi();
|
||||
//const engineApi = new EngineApi();
|
||||
const promptsApi = new PromptsApi();
|
||||
|
||||
const snapshot = editorEngine.snapShotOfCurrentFrame(false);
|
||||
|
||||
if (snapshot) {
|
||||
const snapshotResult = await engineApi.uploadSceneSnapshot({
|
||||
const snapshotResult = await promptsApi.uploadSceneSnapshot({
|
||||
screenshot: snapshot.file,
|
||||
sceneMediaToken: "",
|
||||
});
|
||||
|
||||
console.log("useSystemPrompt", useSystemPrompt);
|
||||
|
||||
const response = await engineApi.enqueueImageGeneration({
|
||||
const response = await promptsApi.enqueueImageGeneration({
|
||||
disableSystemPrompt: !useSystemPrompt,
|
||||
prompt: prompt,
|
||||
snapshotMediaToken: snapshotResult.data || "",
|
||||
@@ -504,22 +507,16 @@ export const PromptBox = () => {
|
||||
if (editorEngine) {
|
||||
editorEngine.positive_prompt = prompt;
|
||||
|
||||
const engineApi = new EngineApi();
|
||||
const promptsApi = new PromptsApi();
|
||||
|
||||
const snapshot = editorEngine.snapShotOfCurrentFrame(false);
|
||||
|
||||
if (snapshot) {
|
||||
const snapshotResult = await engineApi.uploadSceneSnapshot({
|
||||
const snapshotResult = await promptsApi.uploadSceneSnapshot({
|
||||
screenshot: snapshot.file,
|
||||
//sceneMediaToken: "",
|
||||
});
|
||||
|
||||
//const response = await engineApi.enqueueImageGeneration({
|
||||
// disableSystemPrompt: !useSystemPrompt,
|
||||
// prompt: prompt,
|
||||
// snapshotMediaToken: "",
|
||||
// additionalImages: referenceImages.map((image) => image.mediaToken),
|
||||
//});
|
||||
|
||||
const generateResponse = await invoke("sora_image_remix_command", {
|
||||
request: {
|
||||
snapshot_media_token: snapshotResult.data!,
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
import { useRef } from "react";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { useSignalEffect, useSignals } from "@preact/signals-react/runtime";
|
||||
|
||||
import { faKey, faUser } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
import { AUTH_STATUS } from "~/enums";
|
||||
import { authentication, login, logout } from "~/signals";
|
||||
|
||||
import {
|
||||
Button,
|
||||
H1,
|
||||
Input,
|
||||
Link,
|
||||
P,
|
||||
LoadingDots,
|
||||
ConfirmationModal,
|
||||
} from "~/components";
|
||||
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}; // end handleOnSubmit
|
||||
|
||||
useSignalEffect(() => {
|
||||
const redirectPath = searchParams.get("redirect");
|
||||
if (authStatus.value === AUTH_STATUS.LOGGED_IN) {
|
||||
navigate(redirectPath ? redirectPath : "/");
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="fixed w-full" style={{ height: "calc(100% - 72px)" }}>
|
||||
<div className="mx-auto my-6 w-10/12 max-w-2xl">
|
||||
<H1 className="text-center text-[32px] font-bold">
|
||||
Login to Storyteller
|
||||
</H1>
|
||||
</div>
|
||||
<div className="relative mx-auto my-6 w-10/12 max-w-2xl overflow-hidden rounded-lg border border-ui-panel-border bg-ui-panel p-6">
|
||||
<form
|
||||
ref={formRef}
|
||||
onSubmit={handleOnSumbit}
|
||||
className="flex flex-col gap-4"
|
||||
>
|
||||
<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
|
||||
/>
|
||||
<div className="align-items mb-3 flex">
|
||||
<a
|
||||
href="https://storyteller.ai/password-reset"
|
||||
className="grow text-sm text-brand-primary transition-all duration-150 hover:text-brand-primary-400"
|
||||
>
|
||||
Forgot your password?
|
||||
</a>
|
||||
<div className="flex justify-end gap-1 text-sm">
|
||||
<P>Don't have an account?</P>
|
||||
<Link
|
||||
to="/signup"
|
||||
reloadDocument={true} // TODO(bt,2025-04-19): Once we have in-page routing, get rid of this.
|
||||
>Sign Up</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button className="h-11 w-full text-[15px]">Login</Button>
|
||||
</form>
|
||||
<LoadingDots
|
||||
className="absolute left-0 top-0 h-full w-full"
|
||||
isShowing={shouldShowLoader}
|
||||
message={authLoaderMessage}
|
||||
type="bricks"
|
||||
/>
|
||||
<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>
|
||||
</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...";
|
||||
};
|
||||
@@ -1,146 +1,150 @@
|
||||
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";
|
||||
// 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;
|
||||
// const regEx =
|
||||
// /^(([^<>()[\].,;:\s@"]+(.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i;
|
||||
|
||||
export default function SignUpScreen() {
|
||||
useSignals();
|
||||
const navigate = useNavigate();
|
||||
const formRef = useRef<HTMLFormElement | null>(null);
|
||||
const [usernameError, setUsernameError] = useState("");
|
||||
const [emailError, setEmailError] = useState("");
|
||||
const [passwordError, setPasswordError] = useState("");
|
||||
const [passwordConfirmationError, setPasswordConfirmationError] =
|
||||
useState("");
|
||||
const [searchParams] = useSearchParams();
|
||||
const { status: authStatus } = authentication;
|
||||
// export default function SignUpScreen() {
|
||||
// useSignals();
|
||||
// const navigate = useNavigate();
|
||||
// const formRef = useRef<HTMLFormElement | null>(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<HTMLFormElement>) => {
|
||||
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");
|
||||
}
|
||||
// const handleOnSubmit = (ev: FormEvent<HTMLFormElement>) => {
|
||||
// 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
|
||||
// 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;
|
||||
}
|
||||
});
|
||||
// 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 (
|
||||
<div
|
||||
className="fixed w-full overflow-scroll"
|
||||
style={{ height: "calc(100% - 72px)" }}
|
||||
>
|
||||
<div className="mx-auto my-6 w-10/12 max-w-2xl">
|
||||
<H1 className="text-center">Sign Up to Storyteller</H1>
|
||||
</div>
|
||||
<div className="mx-auto my-6 w-10/12 max-w-2xl rounded-lg border border-ui-panel-border bg-ui-panel p-6">
|
||||
<form ref={formRef} onSubmit={handleOnSubmit}>
|
||||
<Input
|
||||
label="Username"
|
||||
icon={faUser}
|
||||
placeholder="Username"
|
||||
name="username"
|
||||
errorMessage={usernameError}
|
||||
required
|
||||
/>
|
||||
<br />
|
||||
<Input
|
||||
type="email"
|
||||
label="Email"
|
||||
icon={faEnvelope}
|
||||
placeholder="Email"
|
||||
name="email"
|
||||
errorMessage={emailError}
|
||||
required
|
||||
/>
|
||||
<br />
|
||||
<Input
|
||||
type="password"
|
||||
label="Password"
|
||||
icon={faKey}
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
autoComplete="current-password"
|
||||
errorMessage={passwordError}
|
||||
required
|
||||
/>
|
||||
<br />
|
||||
<Input
|
||||
type="password"
|
||||
label="Password Confrimation"
|
||||
icon={faKey}
|
||||
placeholder="Password Confirmation"
|
||||
name="password-confirmation"
|
||||
errorMessage={passwordConfirmationError}
|
||||
required
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<Button>Sign up</Button>
|
||||
<br />
|
||||
<div className="flex gap-2">
|
||||
<P>Already have an account?</P>
|
||||
<Link to="/login">Log in instead</Link>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// return (
|
||||
// <div
|
||||
// className="fixed w-full overflow-scroll"
|
||||
// style={{ height: "calc(100% - 72px)" }}
|
||||
// >
|
||||
// <div className="mx-auto my-6 w-10/12 max-w-2xl">
|
||||
// <H1 className="text-center">Sign Up to Storyteller</H1>
|
||||
// </div>
|
||||
// <div className="mx-auto my-6 w-10/12 max-w-2xl rounded-lg border border-ui-panel-border bg-ui-panel p-6">
|
||||
// <form ref={formRef} onSubmit={handleOnSubmit}>
|
||||
// <Input
|
||||
// label="Username"
|
||||
// icon={faUser}
|
||||
// placeholder="Username"
|
||||
// name="username"
|
||||
// errorMessage={usernameError}
|
||||
// required
|
||||
// />
|
||||
// <br />
|
||||
// <Input
|
||||
// type="email"
|
||||
// label="Email"
|
||||
// icon={faEnvelope}
|
||||
// placeholder="Email"
|
||||
// name="email"
|
||||
// errorMessage={emailError}
|
||||
// required
|
||||
// />
|
||||
// <br />
|
||||
// <Input
|
||||
// type="password"
|
||||
// label="Password"
|
||||
// icon={faKey}
|
||||
// name="password"
|
||||
// placeholder="Password"
|
||||
// autoComplete="current-password"
|
||||
// errorMessage={passwordError}
|
||||
// required
|
||||
// />
|
||||
// <br />
|
||||
// <Input
|
||||
// type="password"
|
||||
// label="Password Confrimation"
|
||||
// icon={faKey}
|
||||
// placeholder="Password Confirmation"
|
||||
// name="password-confirmation"
|
||||
// errorMessage={passwordConfirmationError}
|
||||
// required
|
||||
// />
|
||||
// <br />
|
||||
// <br />
|
||||
// <Button>Sign up</Button>
|
||||
// <div className="flex gap-2">
|
||||
// <P>Back to home</P>
|
||||
// <Link to="/">Back</Link>
|
||||
// </div>
|
||||
// <br />
|
||||
// <div className="flex gap-2">
|
||||
// <P>Already have an account?</P>
|
||||
// <Link to="/login">Log in instead</Link>
|
||||
// </div>
|
||||
// </form>
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
@@ -160,6 +160,15 @@ export default function SignUpScreen() {
|
||||
</form>
|
||||
</div>
|
||||
<div className="align-items flex w-full max-w-xl rounded-b-xl bg-ui-controls px-6 py-4">
|
||||
<div className="flex w-full justify-start gap-1 text-sm">
|
||||
<p className="opacity-75"></p>
|
||||
<a
|
||||
href="/"
|
||||
className="font-medium text-brand-primary-400 hover:text-brand-primary-300"
|
||||
>
|
||||
Back
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex w-full justify-end gap-1 text-sm">
|
||||
<p className="opacity-75">Already have an account?</p>
|
||||
<a
|
||||
|
||||
@@ -65,6 +65,9 @@
|
||||
},
|
||||
{
|
||||
"path": "../../libs/tauri-utils"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/api"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -194,48 +194,48 @@ export class PromptsApi extends ApiManager {
|
||||
public async uploadSceneSnapshot({
|
||||
screenshot,
|
||||
sceneMediaToken,
|
||||
}: {
|
||||
}: {
|
||||
screenshot: File; // base64 encoded PNG
|
||||
sceneMediaToken?: string;
|
||||
}): Promise<ApiResponse<string>> {
|
||||
}): Promise<ApiResponse<string>> {
|
||||
const endpoint = `${this.ApiTargets.BaseApi}/v1/image_studio/scene_snapshot`;
|
||||
const formData = new FormData();
|
||||
formData.append("snapshot", screenshot); // Changed from "screenshot" to "snapshot" to match API spec
|
||||
if (sceneMediaToken) {
|
||||
formData.append("scene_media_token", sceneMediaToken);
|
||||
formData.append("scene_media_token", sceneMediaToken);
|
||||
}
|
||||
|
||||
const uuidIdempotencyToken = crypto.randomUUID(); // Generate a new UUID
|
||||
formData.append("uuid_idempotency_token", uuidIdempotencyToken); // Added uuid_idempotency_token
|
||||
const uuidIdempotencyToken = crypto.randomUUID();
|
||||
formData.append("uuid_idempotency_token", uuidIdempotencyToken);
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
body: formData,
|
||||
},
|
||||
credentials: "include",
|
||||
body: formData,
|
||||
});
|
||||
|
||||
const postResponse = await response.json();
|
||||
|
||||
console.log(postResponse);
|
||||
|
||||
let result: { success: boolean; data?: string; errorMessage?: string };
|
||||
|
||||
if (postResponse.success) {
|
||||
result = {
|
||||
result = {
|
||||
success: true,
|
||||
data: postResponse.snapshot_media_token,
|
||||
errorMessage: undefined,
|
||||
};
|
||||
};
|
||||
} else {
|
||||
result = {
|
||||
result = {
|
||||
success: false,
|
||||
errorMessage: "Failed to generate snapshot.",
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
{
|
||||
"path": "./apps/editor3d"
|
||||
},
|
||||
{
|
||||
"path": "./apps/my-app"
|
||||
},
|
||||
{
|
||||
"path": "./libs/components/toaster"
|
||||
},
|
||||
|
||||
@@ -2,5 +2,3 @@
|
||||
set -euxo pipefail
|
||||
# calls these
|
||||
./script/desktop/launch_rust_only_3d_for_michael.sh
|
||||
|
||||
echo "All scripts completed!"
|
||||