diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..7b5c861 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "plugins": ["prettier-plugin-tailwindcss"], + "tailwindStylesheet": "./app/globals.css" +} \ No newline at end of file diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts deleted file mode 100644 index 0eebb94..0000000 --- a/app/api/auth/[...nextauth]/route.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { handlers } from "@/auth" -export const { GET, POST } = handlers \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index fd1afc8..0b0dabe 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,20 +1,64 @@ +"use client"; +import { useState } from "react"; import DraggablePanel from "@/components/DraggablePanel"; -import DraggableWidget from "@/components/DraggableWidget"; import Logo from "@/components/Logo"; +import Preview from "@/components/Draggable/Preview"; +import Draggable from "@/components/Draggable/Draggable"; +import { useComponentsStore } from "@/hooks/useComponentsStore"; export default function Home() { + const [isChangeSize, setIsSizeChangeSize] = useState(false); + const [isDraggable, setIsDraggable] = useState(false); + const [units, setUnit] = useComponentsStore([ + { + id: "1", + x: 0, + y: 0, + width: 320, + height: 160, + component: ()=> + }, + { + id: "2", + x: 336, + y: 0, + width: 160, + height: 320, + component: ()=> + }, + ]); + return ( -
- - - - - - - - - - +
+ + + + + {units.map((item) => ( + } + /> + ))}
); diff --git a/auth.ts b/auth.ts deleted file mode 100644 index cb85747..0000000 --- a/auth.ts +++ /dev/null @@ -1,20 +0,0 @@ -import NextAuth, { CredentialsSignin } from "next-auth" -import Credentials from "next-auth/providers/credentials" - -class InvalidLoginError extends CredentialsSignin { - code = "Invalid identifier or password" -} - -export const { handlers, signIn, signOut, auth } = NextAuth({ - providers: [ - Credentials({ - credentials: { - username: { label: "Username" }, - password: { label: "Password", type: "password" }, - }, - async authorize(credentials) { - throw new InvalidLoginError() - }, - }), - ], -}) \ No newline at end of file diff --git a/components/Draggable/Draggable.tsx b/components/Draggable/Draggable.tsx new file mode 100644 index 0000000..e6b2062 --- /dev/null +++ b/components/Draggable/Draggable.tsx @@ -0,0 +1,104 @@ +"use client"; +import "@/app/globals.css"; +import { useDraggable } from "@dnd-kit/core"; +import { ReactElement, useEffect, useRef, useState } from "react"; +import { nearestMultiple } from "./utils"; + +export default function Draggable(props: DraggablePropsType) { + const targetRef = useRef(null); + const [size, setSize] = useState({ width: 16, height: 16 }); + const { id, component, data, x, y, width, height } = props; + const { attributes, listeners, setNodeRef, transform } = useDraggable({ + id, + }); + + const style = transform + ? { + top: y, + left: x, + width: width, + height: height, + transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`, + opacity: 0.6, + } + : { + top: y, + left: x, + width: width, + height: height, + }; + useEffect(() => { + const element = targetRef.current; + if (!element) return; + + // 创建 ResizeObserver 实例 + let timer: any; + const observer = new ResizeObserver( + _.throttle((entries: any) => { + for (const entry of entries) { + const { width, height } = entry.contentRect; + setSize({ + width: nearestMultiple(width), + height: nearestMultiple(height), + }); + if (timer) { + clearTimeout(timer); + } + // setIsResize(true); + timer = setTimeout(() => { + entry.target.style.width = nearestMultiple(width) + "px"; + entry.target.style.height = nearestMultiple(height) + "px"; + // syncSize(entry.contentRect, containerRef.current); + // setIsResize(false); + }, 150); + } + }, 30), + ); + + // 开始观察元素 + observer.observe(element); + + // 组件卸载时断开连接 + return () => { + observer.disconnect(); + }; + }, []); // 空依赖数组确保只运行一次 + + const className = transform ? "shadow-xl absolute w-min min-w-[128px] min-h-[128px]" : "absolute w-min resize overflow-hidden min-w-[128px] min-h-[128px]"; + + return ( +
+ + {component && component(data??{})} +
+ ); +} + +export type DraggablePropsType = { + id: string; + component: (data: Record) => ReactElement; + data?: Record; + x: number; + y: number; + width: number; + height: number; +}; diff --git a/components/Draggable/Droppable.tsx b/components/Draggable/Droppable.tsx new file mode 100644 index 0000000..038f50d --- /dev/null +++ b/components/Draggable/Droppable.tsx @@ -0,0 +1,14 @@ +"use client"; +import "@/app/globals.css"; + +export default function Droppable() { + return ( +
+ NEXUSHUB +
+ ); +} + +export type DroppablePropsType = { + +} \ No newline at end of file diff --git a/components/Draggable/Preview.tsx b/components/Draggable/Preview.tsx new file mode 100644 index 0000000..1c5bfdf --- /dev/null +++ b/components/Draggable/Preview.tsx @@ -0,0 +1,35 @@ +"use client"; +import "@/app/globals.css"; +import { CSSProperties, useEffect, useState } from "react"; + +export default function Preview(props: PreviewPropsType) { + const {x, y, width, height} = props; + const [style, setStyle] = useState({ + top: 0, + left: 0, + width: 0, + height: 0, + }); + + useEffect(() => { + setStyle({ + top: x, + left: y, + width: width, + height: height, + }); + }, [height, props, width, x, y]) + return ( +
+ ); +} + +export type PreviewPropsType = { + x: number; + y: number; + width: number; + height: number; +}; diff --git a/components/Draggable/hooks/use b/components/Draggable/hooks/use new file mode 100644 index 0000000..e69de29 diff --git a/components/Draggable/index.tsx b/components/Draggable/index.tsx new file mode 100644 index 0000000..e69de29 diff --git a/components/Draggable/utils/index.ts b/components/Draggable/utils/index.ts new file mode 100644 index 0000000..07dfdb4 --- /dev/null +++ b/components/Draggable/utils/index.ts @@ -0,0 +1,9 @@ +/** + * 计算最接近的 x 的倍数 + * @param n 输入的数字 + * @param [x=16] 推荐使用偶数 + * @returns 最近的 x 的倍数 + */ +export function nearestMultiple(n: number, x: number = 16): number { + return Math.floor((n + x/2) / x) * x; +} diff --git a/components/DraggableOld.tsx b/components/DraggableOld.tsx new file mode 100644 index 0000000..4fe71fe --- /dev/null +++ b/components/DraggableOld.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { useDraggable } from "@dnd-kit/core"; + +export function Draggable(props) { + const { id } = props; + const { attributes, listeners, setNodeRef, transform, isDragging } = + useDraggable({ + id, + }); + const style = transform + ? { + transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`, + opacity: 0.6, + } + : undefined; + const className = transform ? "z-30 shadow-xl relative w-min " : "z-10 relative w-min"; + + return ( +
+ + {props.children} +
+ ); +} diff --git a/components/DraggablePanel.tsx b/components/DraggablePanel.tsx index 89b0429..7e92fba 100644 --- a/components/DraggablePanel.tsx +++ b/components/DraggablePanel.tsx @@ -1,25 +1,26 @@ "use client"; -import { ReactElement } from 'react'; -import classnames from 'classnames'; +import classnames from "classnames"; +import { DndContext } from "@dnd-kit/core"; +import { Droppable } from "./Droppable"; export default function DraggablePanel(props: DraggablePanelType) { - const { draggable, wdith = 'full', height = 'full', children } = props; + const { draggable, wdith = "full", height = "full", children } = props; const draggablePanelClass = classnames( - 'p-8 grid gap-[16px]', - `h-${height} w-${wdith}`, - { - 'h-full': height === 'full' || height === '100%', - 'w-full': wdith === 'full' || wdith === '100%', - 'base-200': draggable, // 当 active 为 true 时添加 - 'base-100': !draggable, // 当 disabled 为 true 时添加 - }); + `relative h-${height} w-${wdith}`, + { + "h-full": height === "full" || height === "100%", + "w-full": wdith === "full" || wdith === "100%", + "base-200": draggable, // 当 active 为 true 时添加 + "base-100": !draggable, // 当 disabled 为 true 时添加 + }, + ); return ( -
- { - children - } -
+ + +
{children}
+
+
); } @@ -29,4 +30,4 @@ export type DraggablePanelType = { height?: number | string; children: any; // grid: boolean; -} +}; diff --git a/components/DraggableWidget.tsx b/components/DraggableWidget.tsx index 7e7182e..c268678 100644 --- a/components/DraggableWidget.tsx +++ b/components/DraggableWidget.tsx @@ -1,14 +1,14 @@ "use client"; -import { CSSProperties, ReactElement, useEffect, useRef, useState } from "react"; -import classnames from 'classnames'; -import _ from 'lodash'; - +import { ReactElement, useEffect, useRef, useState } from "react"; +import classnames from "classnames"; +import _ from "lodash"; +import { Draggable } from "./Draggable"; export default function DraggableWidget(props: DraggableWidgetType) { - const { children, x, y, w, h } = props; + const { id, draggable, children, x, y, w, h } = props; const targetRef = useRef(null); const containerRef = useRef(null); - + const [size, setSize] = useState({ width: 16, height: 16 }); const [isResize, setIsResize] = useState(false); @@ -17,38 +17,39 @@ export default function DraggableWidget(props: DraggableWidgetType) { if (!element) return; // 创建 ResizeObserver 实例 - let timer:any; - const observer = new ResizeObserver(_.throttle((entries: any) => { - for (const entry of entries) { - const { width, height } = entry.contentRect; - setSize({ - width: nearestMultipleOf16(width), - height: nearestMultipleOf16(height) - }); - if (timer) { - clearTimeout(timer); + let timer: any; + const observer = new ResizeObserver( + _.throttle((entries: any) => { + for (const entry of entries) { + const { width, height } = entry.contentRect; + setSize({ + width: nearestMultipleOf16(width), + height: nearestMultipleOf16(height), + }); + if (timer) { + clearTimeout(timer); + } + setIsResize(true); + timer = setTimeout(() => { + entry.target.style.width = nearestMultipleOf16(width) + "px"; + entry.target.style.height = nearestMultipleOf16(height) + "px"; + // syncSize(entry.contentRect, containerRef.current); + setIsResize(false); + }, 150); } - setIsResize(true); - timer = setTimeout(() => { - entry.target.style.width = nearestMultipleOf16(width) + 'px'; - entry.target.style.height = nearestMultipleOf16(height) + 'px'; - // syncSize(entry.contentRect, containerRef.current); - setIsResize(false); - }, 150) - } - }, 30)); - + }, 30), + ); const syncSize = (contentRect, containerNode) => { const { x, y, height, width } = contentRect; const area = { - rowStart: x/16 + 1, - columnStart: y/16 + 1, - rowEnd: width/16+x/16 + 1, - columnEnd: height/16+y/16 + 1 - } + rowStart: x / 16 + 1, + columnStart: y / 16 + 1, + rowEnd: width / 16 + x / 16 + 1, + columnEnd: height / 16 + y / 16 + 1, + }; containerNode.style.gridArea = `${area.rowStart} / ${area.columnStart} / ${area.rowEnd} / ${area.columnEnd}`; - } + }; // 开始观察元素 observer.observe(element); @@ -59,36 +60,50 @@ export default function DraggableWidget(props: DraggableWidgetType) { }; }, []); // 空依赖数组确保只运行一次 - const draggableWidgetClass = classnames( - 'hover:bg-red-200 hover:outline-2 hover:outline-dashed hover:outline-gray-300 hover:opacity-30 resize block rounded-lg w-64 h-64 max-h-[1048px] max-w-[1888px] cursor-pointer overflow-hidden ', { - } + "block w-full h-full max-h-[1048px] max-w-[1888px] cursor-pointer overflow-hidden", + { + "hover:bg-red-200 hover:outline-2 hover:outline-dashed hover:outline-gray-300 hover:opacity-30 resize": + draggable, + }, ); const draggableWidgetIndicatorBoxClass = classnames( - 'absolute top-0 left-0 outline-2 outline-dashed outline-red-500 z-50 pointer-events-none', + "absolute top-0 left-0 outline-2 outline-dashed outline-red-500 z-50 pointer-events-none", { - 'invisible': !isResize - } + invisible: !isResize, + }, ); return ( -
-
- { - children - } + +
+
+ {children} +
+
{`${size.width}x${size.height}`}
-
{`${size.width}x${size.height}`}
-
+ ); } export type DraggableWidgetType = { + id: string; draggable: boolean; wdith?: number | string; height?: number | string; @@ -97,7 +112,7 @@ export type DraggableWidgetType = { y: number; w: number; h: number; -} +}; /** * 计算最接近的 16 的倍数 @@ -106,4 +121,4 @@ export type DraggableWidgetType = { */ function nearestMultipleOf16(n: number): number { return Math.floor((n + 8) / 16) * 16; -} \ No newline at end of file +} diff --git a/components/Droppable.tsx b/components/Droppable.tsx new file mode 100644 index 0000000..0204235 --- /dev/null +++ b/components/Droppable.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import {useDroppable} from '@dnd-kit/core'; + +export function Droppable(props) { + const {isOver, setNodeRef} = useDroppable({ + id: 'droppable', + }); + const style = { + color: isOver ? 'green' : undefined, + }; + + + return ( +
+ {props.children} +
+ ); +} \ No newline at end of file diff --git a/components/Logo.tsx b/components/Logo.tsx deleted file mode 100644 index 0db090b..0000000 --- a/components/Logo.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import "@/app/globals.css"; -export default function Home() { - return ( -
- NEXUSHUB -
- ); - } - \ No newline at end of file diff --git a/components/Logo/index.tsx b/components/Logo/index.tsx new file mode 100644 index 0000000..c521563 --- /dev/null +++ b/components/Logo/index.tsx @@ -0,0 +1,12 @@ +"use client"; +import "@/app/globals.css"; + +export default function Logo() { + return ( +
+ + NEXUSHUB + +
+ ); +} diff --git a/middleware.ts b/middleware.ts deleted file mode 100644 index 2df914b..0000000 --- a/middleware.ts +++ /dev/null @@ -1 +0,0 @@ -export { auth as middleware } from "@/auth" \ No newline at end of file diff --git a/package.json b/package.json index cf978e4..331f027 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@dnd-kit/core": "^6.3.1", + "@heroicons/react": "^2.2.0", "classnames": "^2.5.1", "lodash": "^4.17.21", "next": "15.2.3", @@ -26,6 +27,8 @@ "daisyui": "^5.0.6", "eslint": "^9", "eslint-config-next": "15.2.3", + "prettier": "^3.5.3", + "prettier-plugin-tailwindcss": "^0.6.11", "tailwindcss": "^4.0.14", "typescript": "^5" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1655709..b5cc295 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,12 +11,21 @@ importers: '@dnd-kit/core': specifier: ^6.3.1 version: 6.3.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@heroicons/react': + specifier: ^2.2.0 + version: 2.2.0(react@19.0.0) classnames: specifier: ^2.5.1 version: 2.5.1 lodash: specifier: ^4.17.21 version: 4.17.21 + mobx: + specifier: ^6.13.7 + version: 6.13.7 + mobx-react-lite: + specifier: ^4.1.0 + version: 4.1.0(mobx@6.13.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) next: specifier: 15.2.3 version: 15.2.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -54,6 +63,12 @@ importers: eslint-config-next: specifier: 15.2.3 version: 15.2.3(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) + prettier: + specifier: ^3.5.3 + version: 3.5.3 + prettier-plugin-tailwindcss: + specifier: ^0.6.11 + version: 0.6.11(prettier@3.5.3) tailwindcss: specifier: ^4.0.14 version: 4.0.14 @@ -144,6 +159,11 @@ packages: resolution: {integrity: sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@heroicons/react@2.2.0': + resolution: {integrity: sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==} + peerDependencies: + react: '>= 16 || ^19.0.0-rc' + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -190,67 +210,79 @@ packages: resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.0.5': resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.0.4': resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.0.4': resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.0.4': resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.0.4': resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.33.5': resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.33.5': resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.33.5': resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.33.5': resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.33.5': resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.33.5': resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.33.5': resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} @@ -295,24 +327,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@next/swc-linux-arm64-musl@15.2.3': resolution: {integrity: sha512-2gAPA7P652D3HzR4cLyAuVYwYqjG0mt/3pHSWTCyKZq/N/dJcUAEoNQMyUmwTZWCJRKofB+JPuDVP2aD8w2J6Q==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@next/swc-linux-x64-gnu@15.2.3': resolution: {integrity: sha512-ODSKvrdMgAJOVU4qElflYy1KSZRM3M45JVbeZu42TINCMG3anp7YCBn80RkISV6bhzKwcUqLBAmOiWkaGtBA9w==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@next/swc-linux-x64-musl@15.2.3': resolution: {integrity: sha512-ZR9kLwCWrlYxwEoytqPi1jhPd1TlsSJWAc+H/CJHmHkf2nD92MQpSRIURR1iNgA/kuFSdxB8xIPt4p/T78kwsg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@next/swc-win32-arm64-msvc@15.2.3': resolution: {integrity: sha512-+G2FrDcfm2YDbhDiObDU/qPriWeiz/9cRR0yMWJeTLGGX6/x8oryO3tt7HhodA1vZ8r2ddJPCjtLcpaVl7TE2Q==} @@ -395,24 +431,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.0.14': resolution: {integrity: sha512-gVkJdnR/L6iIcGYXx64HGJRmlme2FGr/aZH0W6u4A3RgPMAb+6ELRLi+UBiH83RXBm9vwCfkIC/q8T51h8vUJQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.0.14': resolution: {integrity: sha512-EE+EQ+c6tTpzsg+LGO1uuusjXxYx0Q00JE5ubcIGfsogSKth8n8i2BcS2wYTQe4jXGs+BQs35l78BIPzgwLddw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.0.14': resolution: {integrity: sha512-KCCOzo+L6XPT0oUp2Jwh233ETRQ/F6cwUnMnR0FvMUCbkDAzHbcyOgpfuAtRa5HD0WbTbH4pVD+S0pn1EhNfbw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-win32-arm64-msvc@4.0.14': resolution: {integrity: sha512-AHObFiFL9lNYcm3tZSPqa/cHGpM5wOrNmM2uOMoKppp+0Hom5uuyRh0QkOp7jftsHZdrZUpmoz0Mp6vhh2XtUg==} @@ -530,21 +570,25 @@ packages: resolution: {integrity: sha512-RfYtlCtJrv5i6TO4dSlpbyOJX9Zbhmkqrr9hjDfr6YyE5KD0ywLRzw8UjXsohxG1XWgRpb2tvPuRYtURJwbqWg==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/rspack-resolver-binding-linux-arm64-musl@1.1.2': resolution: {integrity: sha512-MaITzkoqsn1Rm3+YnplubgAQEfOt+2jHfFvuFhXseUfcfbxe8Zyc3TM7LKwgv7mRVjIl+/yYN5JqL0cjbnhAnQ==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/rspack-resolver-binding-linux-x64-gnu@1.1.2': resolution: {integrity: sha512-Nu981XmzQqis/uB3j4Gi3p5BYCd/zReU5zbJmjMrEH7IIRH0dxZpdOmS/+KwEk6ao7Xd8P2D2gDHpHD/QTp0aQ==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/rspack-resolver-binding-linux-x64-musl@1.1.2': resolution: {integrity: sha512-xJupeDvaRpV0ADMuG1dY9jkOjhUzTqtykvchiU2NldSD+nafSUcMWnoqzNUx7HGiqbTMOw9d9xT8ZiFs+6ZFyQ==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/rspack-resolver-binding-wasm32-wasi@1.1.2': resolution: {integrity: sha512-un6X/xInks+KEgGpIHFV8BdoODHRohaDRvOwtjq+FXuoI4Ga0P6sLRvf4rPSZDvoMnqUhZtVNG0jG9oxOnrrLQ==} @@ -1258,24 +1302,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.29.2: resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.29.2: resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.29.2: resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.29.2: resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==} @@ -1329,6 +1377,22 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + mobx-react-lite@4.1.0: + resolution: {integrity: sha512-QEP10dpHHBeQNv1pks3WnHRCem2Zp636lq54M2nKO2Sarr13pL4u6diQXf65yzXUn0mkk18SyIDCm9UOJYTi1w==} + peerDependencies: + mobx: ^6.9.0 + react: ^16.8.0 || ^17 || ^18 || ^19 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + + mobx@6.13.7: + resolution: {integrity: sha512-aChaVU/DO5aRPmk1GX8L+whocagUUpBQqoPtJk+cm7UOXUk87J4PeWCh6nNmTTIfEhiR9DI/+FnA8dln/hTK7g==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1478,6 +1542,66 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier-plugin-tailwindcss@0.6.11: + resolution: {integrity: sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA==} + engines: {node: '>=14.21.3'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + + prettier@3.5.3: + resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + engines: {node: '>=14'} + hasBin: true + pretty-format@3.8.0: resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} @@ -1729,6 +1853,11 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-sync-external-store@1.4.0: + resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -1850,6 +1979,10 @@ snapshots: '@eslint/core': 0.12.0 levn: 0.4.1 + '@heroicons/react@2.2.0(react@19.0.0)': + dependencies: + react: 19.0.0 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -3133,6 +3266,16 @@ snapshots: minimist@1.2.8: {} + mobx-react-lite@4.1.0(mobx@6.13.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + mobx: 6.13.7 + react: 19.0.0 + use-sync-external-store: 1.4.0(react@19.0.0) + optionalDependencies: + react-dom: 19.0.0(react@19.0.0) + + mobx@6.13.7: {} + ms@2.1.3: {} nanoid@3.3.10: {} @@ -3276,6 +3419,12 @@ snapshots: prelude-ls@1.2.1: {} + prettier-plugin-tailwindcss@0.6.11(prettier@3.5.3): + dependencies: + prettier: 3.5.3 + + prettier@3.5.3: {} + pretty-format@3.8.0: {} prop-types@15.8.1: @@ -3615,6 +3764,10 @@ snapshots: dependencies: punycode: 2.3.1 + use-sync-external-store@1.4.0(react@19.0.0): + dependencies: + react: 19.0.0 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0