mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-21 09:22:41 -04:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d62179c36a | ||
|
|
783f6f9223 |
@@ -1,4 +1,4 @@
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import React, { useContext, useEffect } from "react";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { TopBar } from "~/components";
|
||||
import { Controls3D } from "./comps/Controls3D";
|
||||
@@ -73,26 +73,32 @@ import {
|
||||
// videoGenerationModels,
|
||||
// useModelSelectorStore,
|
||||
} from "@storyteller/ui-model-selector";
|
||||
import { LoginModal } from "@storyteller/ui-login-modal";
|
||||
import { LoginModal, useLoginModalStore } from "@storyteller/ui-login-modal";
|
||||
import PageDraw from "../PageDraw/PageDraw";
|
||||
import { useTabStore } from "../Stores/TabState";
|
||||
|
||||
export const PageEditor = () => {
|
||||
useSignals();
|
||||
const [isLoginModalOpen, setIsLoginModalOpen] = useState<boolean>(false);
|
||||
|
||||
const { triggerRecheck } = useLoginModalStore();
|
||||
|
||||
const { status } = authentication;
|
||||
useEffect(() => {
|
||||
if (status.value !== AUTH_STATUS.LOGGED_OUT) {
|
||||
setIsLoginModalOpen(true);
|
||||
if (status.value === AUTH_STATUS.LOGGED_OUT) {
|
||||
triggerRecheck();
|
||||
}
|
||||
}, [status.value]);
|
||||
|
||||
}, [status.value, triggerRecheck]);
|
||||
|
||||
const tabStore = useTabStore();
|
||||
const handleOverlayClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
|
||||
// TODO: For 3d Promptbox to accept this later on
|
||||
// const { selectedModels } = useModelSelectorStore();
|
||||
// const selectedModel =
|
||||
// selectedModels[ModelCategory.Editor3D] ||
|
||||
// videoGenerationModels[0]?.label;
|
||||
|
||||
useEffect(() => {
|
||||
timelineHeight.value = 0; //timelineHeight.value = 208;
|
||||
sidePanelWidth.value = 340;
|
||||
@@ -409,30 +415,30 @@ export const PageEditor = () => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [tabStore.activeTabId, editorEngine]);
|
||||
|
||||
|
||||
return (
|
||||
<div className="w-screen">
|
||||
<TopBar loginSignUpPressed={()=> {
|
||||
setIsLoginModalOpen(true)
|
||||
}} pageName="Edit Scene" />
|
||||
{isLoginModalOpen && (
|
||||
<LoginModal
|
||||
onClose={() => setIsLoginModalOpen(false)}
|
||||
videoSrc2D="/resources/videos/artcraft-canvas-demo.mp4"
|
||||
videoSrc3D="/resources/videos/artcraft-3d-demo.mp4"
|
||||
onOpenChange={(isOpen: boolean) => {
|
||||
if (isOpen) {
|
||||
disableHotkeyInput(DomLevels.DIALOGUE);
|
||||
} else {
|
||||
enableHotkeyInput(DomLevels.DIALOGUE);
|
||||
}
|
||||
}}
|
||||
onArtCraftAuthSuccess={(userInfo: any) => {
|
||||
authentication.status.value = AUTH_STATUS.LOGGED_IN;
|
||||
authentication.userInfo.value = userInfo;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<TopBar
|
||||
loginSignUpPressed={() => {
|
||||
console.log("PRESSED");
|
||||
triggerRecheck();
|
||||
}}
|
||||
pageName="Edit Scene"
|
||||
/>
|
||||
<LoginModal
|
||||
videoSrc2D="/resources/videos/artcraft-canvas-demo.mp4"
|
||||
videoSrc3D="/resources/videos/artcraft-3d-demo.mp4"
|
||||
onOpenChange={(isOpen: boolean) => {
|
||||
if (isOpen) {
|
||||
disableHotkeyInput(DomLevels.DIALOGUE);
|
||||
} else {
|
||||
enableHotkeyInput(DomLevels.DIALOGUE);
|
||||
}
|
||||
}}
|
||||
onArtCraftAuthSuccess={(userInfo: any) => {
|
||||
authentication.status.value = AUTH_STATUS.LOGGED_IN;
|
||||
authentication.userInfo.value = userInfo;
|
||||
}}
|
||||
/>
|
||||
{tabStore.activeTabId == "3D" && (
|
||||
<div>
|
||||
<OnboardingHelper />
|
||||
@@ -565,8 +571,6 @@ export const PageEditor = () => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
|
||||
export * from './lib/login-modal';
|
||||
export * from "./lib/login-modal";
|
||||
export * from "./lib/useLoginModalStore";
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Button } from "@storyteller/ui-button";
|
||||
import { Transition, TransitionChild } from "@headlessui/react";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import {
|
||||
faArrowRight,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { faArrowRight } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { faDiscord } from "@fortawesome/free-brands-svg-icons";
|
||||
import {
|
||||
CheckSoraSession,
|
||||
@@ -12,8 +10,9 @@ import {
|
||||
} from "@storyteller/tauri-api";
|
||||
import { ArtCraftSignUp } from "./artcraft-signup";
|
||||
import { UsersApi } from "@storyteller/api";
|
||||
import { useLoginModalStore } from "./useLoginModalStore";
|
||||
interface LoginModalProps {
|
||||
onClose: () => void;
|
||||
onClose?: () => void;
|
||||
videoSrc2D?: string;
|
||||
videoSrc3D?: string;
|
||||
onOpenChange?: (isOpen: boolean) => void;
|
||||
@@ -29,9 +28,9 @@ export function LoginModal({
|
||||
onArtCraftAuthSuccess,
|
||||
isSignUp: initialIsSignUp = true,
|
||||
}: LoginModalProps) {
|
||||
const { isOpen, recheckTrigger, closeModal } = useLoginModalStore();
|
||||
const [step, setStep] = useState(1);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
const [isLoggedInArtCraft, setIsLoggedInArtCraft] = useState(false);
|
||||
const [isSignUp, setIsSignUp] = useState(initialIsSignUp);
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
@@ -61,26 +60,35 @@ export function LoginModal({
|
||||
setStep(4); // Always go to the final step after Sora login
|
||||
});
|
||||
|
||||
// Check session on component mount
|
||||
// Check session on component mount and when recheckTrigger changes
|
||||
useEffect(() => {
|
||||
checkArtCraftLogin().then((loggedIn) => {
|
||||
if (loggedIn) {
|
||||
setIsLoggedInArtCraft(true);
|
||||
setIsOpen(false);
|
||||
closeModal();
|
||||
} else {
|
||||
setIsOpen(true);
|
||||
setStep(1); // Always start at step 1
|
||||
// Reset all modal state to initial values
|
||||
setStep(1);
|
||||
setIsLoading(false);
|
||||
setIsSignUp(initialIsSignUp);
|
||||
setErrorMessage("");
|
||||
setShowDiscord(false);
|
||||
setShowSuccess(false);
|
||||
setIsLoggedInArtCraft(false);
|
||||
|
||||
const { openModal } = useLoginModalStore.getState();
|
||||
openModal();
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
}, [recheckTrigger, closeModal, initialIsSignUp]);
|
||||
|
||||
useEffect(() => {
|
||||
if (onOpenChange) onOpenChange(isOpen);
|
||||
}, [isOpen, onOpenChange]);
|
||||
|
||||
const handleClose = () => {
|
||||
setIsOpen(false);
|
||||
onClose();
|
||||
closeModal();
|
||||
onClose?.();
|
||||
};
|
||||
|
||||
const handleNext = async () => {
|
||||
@@ -149,7 +157,8 @@ export function LoginModal({
|
||||
Join Our Community
|
||||
</h2>
|
||||
<p className="text-white/70 mb-6 text-center px-24">
|
||||
Connect with other creators, share your work, and get the latest updates in our Discord community.
|
||||
Connect with other creators, share your work, and get the latest
|
||||
updates in our Discord community.
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
<Button
|
||||
@@ -234,15 +243,15 @@ export function LoginModal({
|
||||
try {
|
||||
let signupResponse, loginResponse;
|
||||
if (isSignUp) {
|
||||
console.log("Sign up!")
|
||||
console.log("Sign up!");
|
||||
signupResponse = await usersApi.Signup({
|
||||
username,
|
||||
email,
|
||||
password,
|
||||
passwordConfirmation,
|
||||
});
|
||||
console.log(signupResponse)
|
||||
|
||||
console.log(signupResponse);
|
||||
|
||||
if (!signupResponse.success) {
|
||||
setErrorMessage(
|
||||
signupResponse.errorMessage ||
|
||||
@@ -251,21 +260,21 @@ export function LoginModal({
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
console.log("Path 1")
|
||||
console.log("Path 1");
|
||||
loginResponse = await usersApi.Login({
|
||||
usernameOrEmail: username || email,
|
||||
password,
|
||||
});
|
||||
console.log(loginResponse)
|
||||
console.log(loginResponse);
|
||||
} else {
|
||||
console.log("Path 2")
|
||||
console.log("Path 2");
|
||||
loginResponse = await usersApi.Login({
|
||||
usernameOrEmail: username || email,
|
||||
password,
|
||||
});
|
||||
console.log(loginResponse)
|
||||
console.log(loginResponse);
|
||||
}
|
||||
|
||||
|
||||
if (!loginResponse.success) {
|
||||
setErrorMessage(
|
||||
loginResponse.errorMessage ||
|
||||
@@ -276,12 +285,12 @@ export function LoginModal({
|
||||
}
|
||||
|
||||
setIsLoggedInArtCraft(true);
|
||||
console.log("ARTCRAFT LOGIN SUCCeSS")
|
||||
console.log("ARTCRAFT LOGIN SUCCeSS");
|
||||
if (onArtCraftAuthSuccess) {
|
||||
const session = await usersApi.GetSession();
|
||||
const userInfo = session.data?.user;
|
||||
console.log("USERINFO")
|
||||
console.log(userInfo)
|
||||
console.log("USERINFO");
|
||||
console.log(userInfo);
|
||||
if (userInfo) onArtCraftAuthSuccess(userInfo);
|
||||
}
|
||||
setShowDiscord(true); // Show Discord step after successful login
|
||||
@@ -313,11 +322,7 @@ export function LoginModal({
|
||||
return (
|
||||
<div className="flex items-end justify-center gap-2.5 mt-10 grow">
|
||||
{step === 2 && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={handleBack}
|
||||
disabled={isLoading}
|
||||
>
|
||||
<Button variant="secondary" onClick={handleBack} disabled={isLoading}>
|
||||
Back
|
||||
</Button>
|
||||
)}
|
||||
@@ -334,7 +339,8 @@ export function LoginModal({
|
||||
);
|
||||
};
|
||||
|
||||
return <Transition appear show={isOpen}>
|
||||
return (
|
||||
<Transition appear show={isOpen}>
|
||||
<div className="fixed inset-0 z-[100]">
|
||||
<TransitionChild
|
||||
enter="ease-out duration-300"
|
||||
@@ -376,6 +382,7 @@ export function LoginModal({
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
);
|
||||
}
|
||||
|
||||
export default LoginModal;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { create } from "zustand";
|
||||
|
||||
interface LoginModalStore {
|
||||
isOpen: boolean;
|
||||
recheckTrigger: number;
|
||||
openModal: () => void;
|
||||
closeModal: () => void;
|
||||
triggerRecheck: () => void;
|
||||
}
|
||||
|
||||
export const useLoginModalStore = create<LoginModalStore>((set) => ({
|
||||
isOpen: false,
|
||||
recheckTrigger: 0,
|
||||
openModal: () => set({ isOpen: true }),
|
||||
closeModal: () => set({ isOpen: false }),
|
||||
triggerRecheck: () =>
|
||||
set((state) => ({ recheckTrigger: state.recheckTrigger + 1 })),
|
||||
}));
|
||||
+4
-1
@@ -5,6 +5,7 @@ import { Button } from "@storyteller/ui-button";
|
||||
|
||||
import { UsersApi } from "@storyteller/api";
|
||||
import { UserInfo } from "@storyteller/api";
|
||||
import { useLoginModalStore } from "@storyteller/ui-login-modal";
|
||||
|
||||
const usersApi = new UsersApi();
|
||||
|
||||
@@ -15,6 +16,7 @@ export interface ArtcraftAccountBlockProps {
|
||||
export const ArtcraftAccountBlock = ({
|
||||
globalAccountLogoutCallback,
|
||||
}: ArtcraftAccountBlockProps) => {
|
||||
const { triggerRecheck } = useLoginModalStore();
|
||||
const [artcraftSession, setArtcraftSession] = useState<UserInfo | undefined>(
|
||||
undefined
|
||||
);
|
||||
@@ -50,8 +52,9 @@ export const ArtcraftAccountBlock = ({
|
||||
setIsLoggedIn(false);
|
||||
setIsCheckingArtcraftSession(false);
|
||||
globalAccountLogoutCallback(); // TODO: This resets the old global application state
|
||||
triggerRecheck(); // Trigger modal to recheck session and potentially open
|
||||
} else {
|
||||
window.location.href = "/login"; // TODO(bt,2025-05-08): Once we have in-page routing, get rid of this.
|
||||
triggerRecheck(); // Open login modal instead of redirecting
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
DndContext,
|
||||
closestCenter,
|
||||
KeyboardSensor,
|
||||
PointerSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
DragEndEvent,
|
||||
} from "@dnd-kit/core";
|
||||
import {
|
||||
arrayMove,
|
||||
SortableContext,
|
||||
sortableKeyboardCoordinates,
|
||||
verticalListSortingStrategy,
|
||||
useSortable,
|
||||
} from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faGripVertical } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
interface RouterItem {
|
||||
id: string;
|
||||
name: string;
|
||||
emoji: string;
|
||||
}
|
||||
|
||||
interface SortableItemProps {
|
||||
id: string;
|
||||
name: string;
|
||||
emoji: string;
|
||||
isUpdating?: boolean;
|
||||
}
|
||||
|
||||
const SortableItem = ({
|
||||
id,
|
||||
name,
|
||||
emoji,
|
||||
isUpdating = false,
|
||||
}: SortableItemProps) => {
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
setNodeRef,
|
||||
transform,
|
||||
transition,
|
||||
isDragging,
|
||||
} = useSortable({ id });
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
className={`
|
||||
flex items-center justify-between rounded-lg bg-white/5 p-3 transition-colors duration-200
|
||||
${
|
||||
isUpdating
|
||||
? "cursor-wait opacity-60"
|
||||
: "cursor-move hover:bg-white/10"
|
||||
}
|
||||
${isDragging ? "opacity-50 shadow-lg" : ""}
|
||||
`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-lg">{emoji}</span>
|
||||
<span className="font-medium">{name}</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<FontAwesomeIcon
|
||||
icon={faGripVertical}
|
||||
className="text-white/40 text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const RouterPrioritySettingsPane = () => {
|
||||
const [items, setItems] = useState<RouterItem[]>([
|
||||
{ id: "artcraft-3d", name: "ArtCraft 3D", emoji: "🎨" },
|
||||
{ id: "gpt-image-1", name: "GPT Image 1 (GPT-4o)", emoji: "🤖" },
|
||||
{ id: "flux-pro-ultra", name: "Flux Pro Ultra", emoji: "⚡" },
|
||||
{ id: "recraft-3", name: "Recraft 3", emoji: "🌟" },
|
||||
{ id: "flux-kontext", name: "Flux.1 Kontext", emoji: "🔮" },
|
||||
]);
|
||||
const [isUpdating, setIsUpdating] = useState(false);
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, {
|
||||
coordinateGetter: sortableKeyboardCoordinates,
|
||||
})
|
||||
);
|
||||
|
||||
const updateRouterPriorityOnBackend = async (newOrder: RouterItem[]) => {
|
||||
try {
|
||||
setIsUpdating(true);
|
||||
console.log("Updating router priority on backend:", newOrder);
|
||||
|
||||
// Simulate API call delay
|
||||
await new Promise((resolve) => setTimeout(resolve, 500)); //remove this - BFlat
|
||||
|
||||
//api call here
|
||||
|
||||
console.log("Router priority updated successfully");
|
||||
} catch (error) {
|
||||
console.error("Failed to update router priority:", error);
|
||||
} finally {
|
||||
setIsUpdating(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDragEnd = (event: DragEndEvent) => {
|
||||
const { active, over } = event;
|
||||
|
||||
if (active.id !== over?.id) {
|
||||
setItems((prevItems) => {
|
||||
const oldIndex = prevItems.findIndex((item) => item.id === active.id);
|
||||
const newIndex = prevItems.findIndex((item) => item.id === over?.id);
|
||||
|
||||
const newOrder = arrayMove(prevItems, oldIndex, newIndex);
|
||||
|
||||
// Send update to backend
|
||||
updateRouterPriorityOnBackend(newOrder);
|
||||
|
||||
return newOrder;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-2 flex items-center gap-2">
|
||||
Model Router Priority
|
||||
{isUpdating && (
|
||||
<span className="text-xs bg-blue-500/20 text-blue-400 px-2 py-1 rounded-full animate-pulse">
|
||||
Updating...
|
||||
</span>
|
||||
)}
|
||||
</h3>
|
||||
<p className="text-sm text-white/70 mb-4">
|
||||
Drag and drop to reorder model priority. Higher items will be tried
|
||||
first.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<SortableContext items={items} strategy={verticalListSortingStrategy}>
|
||||
<div className="space-y-2">
|
||||
{items.map((item) => (
|
||||
<SortableItem
|
||||
key={item.id}
|
||||
id={item.id}
|
||||
name={item.name}
|
||||
emoji={item.emoji}
|
||||
isUpdating={isUpdating}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -6,12 +6,14 @@ import {
|
||||
faCog,
|
||||
faVolumeHigh,
|
||||
faCircleInfo,
|
||||
faRoute,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { MiscSettingsPane } from "./panes/MiscSettingsPane";
|
||||
import { AudioSettingsPane } from "./panes/AudioSettingsPane";
|
||||
import { AccountSettingsPane } from "./panes/AccountSettings/AccountSettingsPane";
|
||||
import { AboutSettingsPane } from "./panes/AboutSettingsPane";
|
||||
import { RouterPrioritySettingsPane } from "./panes/RouterPrioritySettingsPane";
|
||||
|
||||
interface SettingsModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -20,8 +22,12 @@ interface SettingsModalProps {
|
||||
initialSection?: SettingsSection;
|
||||
}
|
||||
|
||||
//type SettingsSection = "misc" | "audio" | "accounts" | "video" | "image";
|
||||
type SettingsSection = "general" | "accounts" | "alerts" | "about";
|
||||
type SettingsSection =
|
||||
| "general"
|
||||
| "accounts"
|
||||
| "alerts"
|
||||
| "about"
|
||||
| "router_priority";
|
||||
|
||||
export const SettingsModal = ({
|
||||
isOpen,
|
||||
@@ -37,6 +43,11 @@ export const SettingsModal = ({
|
||||
{ id: "accounts" as const, label: "Accounts", icon: faUser },
|
||||
{ id: "alerts" as const, label: "Alerts", icon: faVolumeHigh },
|
||||
{ id: "about" as const, label: "About", icon: faCircleInfo },
|
||||
{
|
||||
id: "router_priority" as const,
|
||||
label: "Model Router Priority",
|
||||
icon: faRoute,
|
||||
},
|
||||
//{ id: "video" as const, label: "Video", icon: faVideo },
|
||||
//{ id: "image" as const, label: "Image", icon: faImage },
|
||||
];
|
||||
@@ -55,6 +66,8 @@ export const SettingsModal = ({
|
||||
);
|
||||
case "about":
|
||||
return <AboutSettingsPane />;
|
||||
case "router_priority":
|
||||
return <RouterPrioritySettingsPane />;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Generated
+52
@@ -13,6 +13,9 @@
|
||||
"libs/**/*"
|
||||
],
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@headlessui/react": "^2.2.2",
|
||||
"@preact/signals-react": "^3.0.1",
|
||||
"@remix-run/serve": "^2.15.0",
|
||||
@@ -3428,6 +3431,55 @@
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/accessibility": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
|
||||
"integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/core": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
||||
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
||||
"dependencies": {
|
||||
"@dnd-kit/accessibility": "^3.1.1",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/sortable": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz",
|
||||
"integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==",
|
||||
"dependencies": {
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@dnd-kit/core": "^6.3.0",
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/utilities": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
|
||||
"integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/core": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.0.tgz",
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
"scripts": {},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@headlessui/react": "^2.2.2",
|
||||
"@preact/signals-react": "^3.0.1",
|
||||
"@remix-run/serve": "^2.15.0",
|
||||
|
||||
Reference in New Issue
Block a user