Compare commits

..
Author SHA1 Message Date
Michael Chung e4bbafeae6 Used Aspect Ratio to fix.
Prompt box handles both web and desktop.
2025-04-21 16:58:02 -04:00
33 changed files with 67 additions and 581 deletions
Generated
-1
View File
@@ -12021,7 +12021,6 @@ dependencies = [
"tauri-plugin-log",
"tempdir",
"tempfile",
"tokens",
"tokio",
"tokio-util",
"workspace-build-acceleration",
+1 -3
View File
@@ -168,14 +168,12 @@ sha3 = "0.10.8"
thiserror = "2"
toml = "0.7.4"
url = "2.5.4"
# Internal libs
crockford = { path = "crates/lib/data/crockford" }
errors = { path = "crates/lib/errors" }
filesys = { path = "crates/lib/files/filesys" }
memory_caching = { path = "crates/lib/caching/memory_caching" }
shared_service_components = { path = "crates/lib/shared_service_components" }
openai_sora_client = { path = "crates/lib/api_clients/openai_sora_client" }
primitives = { path = "crates/lib/data/primitives" }
shared_service_components = { path = "crates/lib/shared_service_components" }
subprocess_common = { path = "crates/lib/subprocess_common" }
tokens = { path = "crates/_types/tokens" }
-1
View File
@@ -30,7 +30,6 @@ memory_caching = { workspace = true }
openai_sora_client = { workspace = true }
primitives = { workspace = true }
subprocess_common = { workspace = true }
tokens = { workspace = true }
# External
#openssl = { version = "0.10", features = ["vendored"] }
@@ -15,15 +15,18 @@ use openai_sora_client::upload::upload_media_from_file::SoraMediaUploadRequest;
use crate::state::app_dir::AppDataRoot;
#[tauri::command]
pub async fn sora_image_generation_command(
pub async fn image_generation_command(
image: &str,
prompt: &str,
image: Option<&str>,
_app: AppHandle,
app: AppHandle,
app_data_root: State<'_, AppDataRoot>,
) -> Result<String, String> {
info!("image_generation_command called; processing image...");
generate_image(image, prompt, &app_data_root)
let bytes = BASE64_STANDARD.decode(image)
.map_err(|err| format!("Base64 decode error: {}", err))?;
generate_image(bytes, prompt, &app_data_root)
.await
.map_err(|err| {
error!("error: {:?}", err);
@@ -33,25 +36,19 @@ pub async fn sora_image_generation_command(
Ok("success".to_string())
}
pub async fn generate_image(maybe_image: Option<&str>, prompt: &str, app_data_root: &AppDataRoot) -> AnyhowResult<()> {
pub async fn generate_image(file_bytes: Vec<u8>, prompt: &str, app_data_root: &AppDataRoot) -> AnyhowResult<()> {
let sora_credentials = get_credentials(app_data_root)?;
let mut sora_media_tokens = vec![];
let filename = "image.png".to_string();
if let Some(image) = maybe_image {
let image_bytes = BASE64_STANDARD.decode(image)?;
let response= sora_media_upload_from_bytes(file_bytes, filename, &sora_credentials)
.await
.map_err(|err| {
error!("Failed to upload image to Sora: {:?}", err);
err
})?;
let filename = "image.png".to_string();
let response = sora_media_upload_from_bytes(image_bytes, filename, &sora_credentials)
.await
.map_err(|err| {
error!("Failed to upload image to Sora: {:?}", err);
err
})?;
sora_media_tokens.push(response.id);
}
let sora_media_tokens = vec![response.id];
let response = sora_image_gen_remix(SoraImageGenRemixRequest {
prompt: prompt.to_string(),
@@ -1,2 +1,3 @@
pub mod flip_image;
pub mod sora;
pub mod image_generation_command;
pub mod open_login_command;
@@ -4,7 +4,7 @@ use once_cell::sync::Lazy;
use tauri::{AppHandle, Manager, Url, WebviewUrl, WebviewWindowBuilder, WindowBuilder};
use tokio::time::sleep_until;
use errors::AnyhowResult;
use crate::threads::sora_session_login_thread::{LOGIN_WINDOW_NAME, SORA_LOGIN_URL};
use crate::threads::login_thread::{LOGIN_WINDOW_NAME, SORA_LOGIN_URL};
use crate::utils::clear_all_webview_cookies::clear_all_webview_cookies;
pub const START_URL_STR: &str = "https://storyteller.ai/";
@@ -20,7 +20,7 @@ pub static SORA_HOMEPAGE_URL : Lazy<Url> = Lazy::new(|| {
});
#[tauri::command]
pub async fn open_sora_login_command(
pub async fn open_login_command(
app: AppHandle,
) -> Result<String, String> {
info!("open_login_command called");
@@ -1,3 +0,0 @@
pub mod open_sora_login_command;
pub mod sora_image_generation_command;
pub mod sora_image_remix_command;
@@ -1,100 +0,0 @@
use base64::Engine;
use base64::prelude::BASE64_STANDARD;
use crate::state::app_dir::AppDataRoot;
use errors::AnyhowResult;
use image::codecs::png::{CompressionType, FilterType, PngEncoder};
use image::{DynamicImage, ImageReader};
use log::{error, info};
use openai_sora_client::credentials::SoraCredentials;
use openai_sora_client::image_gen::common::{ImageSize, NumImages};
use openai_sora_client::image_gen::sora_image_gen_remix::{sora_image_gen_remix, SoraImageGenRemixRequest};
use openai_sora_client::upload::upload_media_from_bytes::sora_media_upload_from_bytes;
use openai_sora_client::upload::upload_media_from_file::SoraMediaUploadRequest;
use serde_derive::Deserialize;
use std::fs::read_to_string;
use std::io::Cursor;
use tauri::{AppHandle, Manager, State};
use tokens::tokens::media_files::MediaFileToken;
#[derive(Deserialize)]
pub struct SoraImageRemixCommand {
/// Image media file; the engine or canvas snapshot (screenshot).
pub snapshot_media_token: MediaFileToken,
/// The user's image generation prompt.
pub prompt: String,
/// Turn off the system prompt.
pub disable_system_prompt: Option<bool>,
/// Additional images to include (optional). Up to nine images.
pub maybe_additional_images: Option<Vec<MediaFileToken>>,
pub maybe_number_of_samples: Option<u32>,
}
#[tauri::command]
pub async fn sora_image_remix_command(
_app: AppHandle,
request: SoraImageRemixCommand,
app_data_root: State<'_, AppDataRoot>,
) -> Result<String, String> {
info!("image_generation_command called; processing image...");
generate_image(request, &app_data_root)
.await
.map_err(|err| {
error!("error: {:?}", err);
"there was an error".to_string()
})?;
Ok("success".to_string())
}
pub async fn generate_image(request: SoraImageRemixCommand, app_data_root: &AppDataRoot) -> AnyhowResult<()> {
let sora_credentials = get_credentials(app_data_root)?;
let sora_media_tokens = vec![];
// TODO(bt,2025-04-21): Download media tokens.
// Note: This is incredibly inefficient. We should keep a local cache.
// Also, if they've already been uploaded to OpenAI, we shouldn't continue to re-upload.
let response = sora_image_gen_remix(SoraImageGenRemixRequest {
prompt: request.prompt.to_string(),
num_images: NumImages::One,
image_size: ImageSize::Square,
sora_media_tokens: sora_media_tokens.clone(),
credentials: &sora_credentials,
}).await
.map_err(|err| {
error!("Failed to call Sora image generation: {:?}", err);
err
})?;
println!(">> TASK ID: {:?} ", response.task_id);
Ok(())
}
fn get_credentials(app_data_root: &AppDataRoot) -> AnyhowResult<SoraCredentials> {
let cookie_file = app_data_root.get_sora_cookie_file_path();
let bearer = read_to_string("/Users/bt/dev/storyteller/storyteller-rust/test_data/temp/bearer.txt")?
.trim()
.to_string();
let cookie= read_to_string(cookie_file)?
.trim()
.to_string();
let sentinel = read_to_string("/Users/bt/dev/storyteller/storyteller-rust/test_data/temp/sentinel.txt")?
.trim()
.to_string();
Ok(SoraCredentials {
bearer_token: bearer,
cookie: cookie,
sentinel: Some(sentinel),
})
}
+6 -8
View File
@@ -6,11 +6,10 @@ pub mod transfer;
pub mod utils;
use crate::commands::flip_image::flip_image;
use crate::commands::sora::open_sora_login_command::open_sora_login_command;
use crate::commands::sora::sora_image_generation_command::sora_image_generation_command;
use crate::commands::sora::sora_image_remix_command::sora_image_remix_command;
use crate::commands::image_generation_command::image_generation_command;
use crate::commands::open_login_command::open_login_command;
use crate::state::app_config::AppConfig;
use crate::threads::sora_session_login_thread::sora_session_login_thread;
use crate::threads::login_thread::login_thread;
use tauri_plugin_log::Target;
use tauri_plugin_log::TargetKind;
@@ -50,7 +49,7 @@ pub fn run() {
//}
let app = app.handle().clone();
tauri::async_runtime::spawn(sora_session_login_thread(app, app_data_root2));
tauri::async_runtime::spawn(login_thread(app, app_data_root2));
Ok(())
})
@@ -58,9 +57,8 @@ pub fn run() {
.manage(app_data_root)
.invoke_handler(tauri::generate_handler![
flip_image,
open_sora_login_command,
sora_image_generation_command,
sora_image_remix_command,
image_generation_command,
open_login_command,
])
.run(tauri::generate_context!("tauri.conf.json"))
.expect("error while running tauri application");
@@ -23,7 +23,7 @@ pub static SORA_ROOT_URL: Lazy<Url> = Lazy::new(|| {
Url::parse(SORA_ROOT_URL_STR).expect("URL should parse")
});
pub async fn sora_session_login_thread(app: AppHandle, app_data_root: AppDataRoot) -> ! {
pub async fn login_thread(app: AppHandle, app_data_root: AppDataRoot) -> ! {
loop {
for (window_name, webview) in app.webviews() {
if window_name == LOGIN_WINDOW_NAME {
@@ -1 +1 @@
pub mod sora_session_login_thread;
pub mod login_thread;
@@ -68,7 +68,7 @@ reusable_types = { path = "../../../_types/reusable_types" }
server_environment = { path = "../../../lib/server_environment" }
shared_service_components = { workspace = true }
thumbnail_generator = { path = "../../../lib/api_clients/thumbnail_generator" }
tokens = { workspace = true }
tokens = { path = "../../../_types/tokens" }
tts_common = { path = "../../../lib/tts_common" }
url_config = { path = "../../../lib/url_config" }
user_input_common = { path = "../../../lib/user_input_common" }
@@ -134,7 +134,7 @@ export const Main = withProtectionRoute(() => {
iconFlip={true}
onClick={async () => {
if (isTauri()) {
await invoke('open_sora_login_command');
await invoke('open_login_command');
setFirstTimeDialogOpen(false);
} else {
console.error('Tauri is not available in this environment');
@@ -13,7 +13,6 @@ import { getCurrentLocationWithoutParams } from "~/utilities";
import { Activity } from "~/pages/PageEnigma/comps/GenerateModals/Activity";
import { LibraryModal } from "~/pages/PageEnigma/comps/LibraryModal/LibraryModal";
import { SettingsModal } from "@storyteller/ui-settings-modal";
import { Tooltip } from "@storyteller/ui-tooltip";
function isEditorPath(path: string) {
if (path === "/") return true;
@@ -76,14 +75,12 @@ export const TopBar = ({ pageName }: Props) => {
<div className="flex justify-end gap-3.5">
<div className="flex gap-2">
<Tooltip content="Settings" position="bottom" delay={300}>
<Button
variant="secondary"
icon={faGear}
className="h-[38px] w-[38px]"
onClick={() => setIsSettingsModalOpen(true)}
/>
</Tooltip>
<Button
variant="secondary"
icon={faGear}
className="h-[38px] w-[38px]"
onClick={() => setIsSettingsModalOpen(true)}
/>
<Button
variant="secondary"
icon={faImages}
@@ -1,23 +0,0 @@
export const FKBoneBlacklistStrings = [
"def",
"toe_end",
"thumb",
"index",
"handmiddle",
"handring",
"pinky",
"headtop",
"ear",
"teeth",
"eye",
"lid",
"jaw",
"tongue",
"lip",
"chin",
"brow",
"cheek",
"forehead",
"nose",
"temple"
];
@@ -1,6 +1,5 @@
import * as THREE from "three";
import { TransformControls } from "three/examples/jsm/Addons";
import { FKBoneBlacklistStrings } from "./FKBoneFilter";
import * as THREE from "three";
export const FKHelperSphereName = "__FKHelperSphere__";
@@ -51,21 +50,10 @@ export class FKHelper {
// TODO: Highlight all the necessary bones
this.clear();
target.traverse((child) => {
// Skip if not bone
if (child.type !== "Bone") {
return;
}
// Skip if bones is one of the blacklisted ones
// This step could probably be optimized better
for (const blacklistedBone of FKBoneBlacklistStrings) {
if (child.name.toLowerCase().includes(blacklistedBone)) {
return;
}
}
console.log(child.name);
// For each target bone, display a sphere to show the FK target and raycast intersect
const geometry = new THREE.SphereGeometry(0.015, 16, 16);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, depthTest: false, transparent: true });
@@ -87,7 +75,6 @@ export class FKHelper {
this.skeletonHelper = new THREE.SkeletonHelper(target);
this.scene.add(this.skeletonHelper);
console.log("All bones:", this.skeletonHelper.bones.map((b) => b.name));
}
onMouseClick(mouse: THREE.Vector2) {
@@ -14,18 +14,9 @@ import {
faImage,
} from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tooltip } from "@storyteller/ui-tooltip";
import { Button } from "@storyteller/ui-button";
import { CloseButton } from "@storyteller/ui-close-button";
import { Input } from "@storyteller/ui-input";
import { Button, CloseButton, Input, Tooltip } from "~/components";
import { TabSelector } from "~/components/reusable/TabSelector";
import React, {
useState,
useEffect,
useMemo,
useRef,
ChangeEvent,
} from "react";
import { useState, useEffect, useMemo, useRef } from "react";
import { ItemElements } from "../SidePanelTabs/sharedComps/ItemElements";
import {
demoSkyboxItems,
@@ -421,7 +412,6 @@ export const AssetModal = () => {
onClose={handleClose}
className="h-[640px] max-w-4xl"
childPadding={false}
showClose={false}
>
<div className="grid h-full grid-cols-12 gap-3">
<div className="relative col-span-3 flex h-full flex-col p-3 pt-2 after:absolute after:right-0 after:top-0 after:h-full after:w-px after:bg-gray-200 after:dark:bg-white/10">
@@ -508,9 +498,7 @@ export const AssetModal = () => {
inputClassName="pr-2.5"
icon={faSearch}
value={searchTerm}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
setSearchTerm(e.target.value)
}
onChange={(e) => setSearchTerm(e.target.value)}
iconClassName="text-white/60"
/>
{searchTerm && (
+2 -2
View File
@@ -89,9 +89,9 @@
"@storybook/test": "^8.1.10",
"@types/deep-equal": "^1.0.4",
"@types/jest": "^29.5.12",
"@types/react": "^19.0.0",
"@types/react": "^18.2.20",
"@types/react-copy-to-clipboard": "^5.0.7",
"@types/react-dom": "^19.0.0",
"@types/react-dom": "^18.2.7",
"@types/react-helmet": "^6.1.11",
"@types/three": "^0.162.0",
"@types/uuid": "^9.0.8",
-15
View File
@@ -21,24 +21,9 @@
"noEmit": true
},
"references": [
{
"path": "../../libs/components/input"
},
{
"path": "../../libs/components/close-button"
},
{
"path": "../../libs/components/button"
},
{
"path": "../../libs/components/modal"
},
{
"path": "../../libs/components/toaster"
},
{
"path": "../../libs/components/tooltip"
},
{
"path": "../../libs/components/settings-modal"
}
-12
View File
@@ -1,12 +0,0 @@
{
"presets": [
[
"@nx/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}
@@ -1,7 +0,0 @@
# @storyteller/ui-tooltip
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test @storyteller/ui-tooltip` to execute the unit tests via [Vitest](https://vitest.dev/).
@@ -1,17 +0,0 @@
{
"name": "@storyteller/ui-tooltip",
"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"
}
}
}
@@ -1,2 +0,0 @@
export * from './lib/tooltip';
@@ -1,7 +0,0 @@
/*
* Replace this with your own classes
*
* e.g.
* .container {
* }
*/
@@ -1,12 +0,0 @@
import { render } from '@testing-library/react';
import Tooltip from './tooltip';
describe('Tooltip', () => {
it('should render successfully', () => {
const { baseElement } = render(<Tooltip />);
expect(baseElement).toBeTruthy();
});
});
@@ -1,152 +0,0 @@
import React, { useState, useRef, useEffect } from "react";
import { Transition } from "@headlessui/react";
import { twMerge } from "tailwind-merge";
interface TooltipProps {
children: React.ReactElement;
content: React.ReactNode;
position: "top" | "bottom" | "left" | "right";
className?: string;
delay?: number;
closeOnClick?: boolean;
}
export const Tooltip = ({
children,
content,
position,
className,
delay = 300,
closeOnClick = false,
}: TooltipProps) => {
const [isShowing, setIsShowing] = useState(false);
const triggerRef = useRef<HTMLDivElement>(null);
const tooltipRef = useRef<HTMLDivElement>(null);
const checkForOpenPopovers = () => {
if (!triggerRef.current) return false;
return (
triggerRef.current.querySelectorAll('[data-headlessui-state="open"]')
.length > 0
);
};
useEffect(() => {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (
mutation.type === "attributes" &&
mutation.attributeName === "data-headlessui-state"
) {
const target = mutation.target as HTMLElement;
if (target.getAttribute("data-headlessui-state") === "open") {
setIsShowing(false);
}
}
});
});
if (triggerRef.current) {
observer.observe(triggerRef.current, {
attributes: true,
subtree: true,
attributeFilter: ["data-headlessui-state"],
});
}
return () => {
observer.disconnect();
};
}, []);
const getStyleForPosition = () => {
if (triggerRef.current) {
const rect = triggerRef.current.getBoundingClientRect();
switch (position) {
case "top":
return {
bottom: rect.height + 10,
left: "50%",
transform: "translateX(-50%)",
};
case "bottom":
return {
top: rect.height + 10,
left: "50%",
transform: "translateX(-50%)",
};
case "left":
return {
right: rect.width + 10,
top: "50%",
transform: "translateY(-50%)",
};
case "right":
return {
left: rect.width + 10,
top: "50%",
transform: "translateY(-50%)",
};
default:
return {};
}
}
return {};
};
const handleClick = (e: React.MouseEvent) => {
if (closeOnClick) {
setIsShowing(false);
e.stopPropagation();
}
};
return (
<div
ref={triggerRef}
onMouseEnter={() => {
if (!checkForOpenPopovers()) {
setIsShowing(true);
}
}}
onMouseLeave={() => {
setIsShowing(false);
}}
onClick={handleClick}
className="relative"
>
{children}
<Transition
show={isShowing}
enter={twMerge(
"transition ease-out duration-200",
delay ? `delay-[${delay}ms]` : "delay-[300ms]"
)}
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition ease-in duration-150"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div
ref={tooltipRef}
style={{
...getStyleForPosition(),
transitionDelay: `${delay}ms`,
transitionProperty: "opacity",
transitionDuration: "200ms",
transitionTimingFunction: "ease-out",
}}
className={twMerge(
"pointer-events-none absolute z-10 w-max rounded-lg bg-[#5F5F68] px-2.5 py-1.5 text-[13px] font-medium text-white shadow-xl",
className ? className : ""
)}
>
{content}
</div>
</Transition>
</div>
);
};
export default Tooltip;
@@ -1,13 +0,0 @@
{
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
],
"extends": "../../../tsconfig.base.json"
}
@@ -1,41 +0,0 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"lib": ["dom", "esnext"],
"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"]
}
@@ -1,34 +0,0 @@
{
"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"
}
]
}
@@ -1,49 +0,0 @@
/// <reference types='vitest' />
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/components/tooltip',
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: '@storyteller/ui-tooltip',
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,
}
},
}));
+23 -10
View File
@@ -1206,9 +1206,9 @@
"@storybook/test": "^8.1.10",
"@types/deep-equal": "^1.0.4",
"@types/jest": "^29.5.12",
"@types/react": "^19.0.0",
"@types/react": "^18.2.20",
"@types/react-copy-to-clipboard": "^5.0.7",
"@types/react-dom": "^19.0.0",
"@types/react-dom": "^18.2.7",
"@types/react-helmet": "^6.1.11",
"@types/three": "^0.162.0",
"@types/uuid": "^9.0.8",
@@ -1651,6 +1651,27 @@
"react": "^16.14.0 || 17.x || 18.x || 19.x"
}
},
"apps/editor3d/node_modules/@types/react": {
"version": "18.3.20",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.20.tgz",
"integrity": "sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
}
},
"apps/editor3d/node_modules/@types/react-dom": {
"version": "18.3.6",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.6.tgz",
"integrity": "sha512-nf22//wEbKXusP6E9pfOCDwFdHAX4u172eaJI4YkDRQEZiorm6KfYnSC2SWLDMVWUOWPERmJnN0ujeAfTBLvrw==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "^18.0.0"
}
},
"apps/editor3d/node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
@@ -2033,10 +2054,6 @@
"name": "@storyteller/ui-toaster",
"version": "0.0.1"
},
"libs/components/tooltip": {
"name": "@storyteller/ui-tooltip",
"version": "0.0.1"
},
"libs/login": {
"name": "@frontend/login",
"version": "0.0.1"
@@ -10079,10 +10096,6 @@
"resolved": "libs/components/toaster",
"link": true
},
"node_modules/@storyteller/ui-tooltip": {
"resolved": "libs/components/tooltip",
"link": true
},
"node_modules/@svgr/babel-plugin-add-jsx-attribute": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
-3
View File
@@ -38,9 +38,6 @@
},
{
"path": "./libs/components/select"
},
{
"path": "./libs/components/tooltip"
}
]
}
+1 -2
View File
@@ -12,8 +12,8 @@ if lsof -i tcp:5741 &>/dev/null; then
else
echo "No process running on port 5741"
fi
# TODO(bt,2025-02-13): This is not the correct way to get the root dir
brew install pkg-config pixman cairo pango libpng jpeg giflib librsvg
root_dir=$(pwd)
frontend_path="${root_dir}/frontend"
@@ -30,4 +30,3 @@ export CONFIG_PATH="${TAURI_APP_PATH}/tauri.artcraft_2d.conf.toml"
# TODO: --no-watch
cargo tauri dev --config "${CONFIG_PATH}"