feat: lots of work
This commit is contained in:
parent
10f4253954
commit
abf1cc06b5
|
@ -1,9 +1,6 @@
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { JSONFilePreset } from "lowdb/node";
|
import { JSONFilePreset } from "lowdb/node";
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import { fileURLToPath } from "node:url";
|
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
||||||
|
|
||||||
const initDb = async () => {
|
const initDb = async () => {
|
||||||
// Read or create db.json
|
// Read or create db.json
|
||||||
|
|
|
@ -3,18 +3,41 @@ import * as dicomParser from "dicom-parser";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
|
||||||
export interface StructuredData {
|
export interface StructuredData {
|
||||||
[StudyInstanceUID: string]: {
|
[SeriesInstanceUID: string]: ExtractMetadata[];
|
||||||
[SeriesInstanceUID: string]: ExtractMetadata[];
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtractMetadata {
|
export interface ExtractMetadata {
|
||||||
filePath: string;
|
filePath: string;
|
||||||
StudyInstanceUID?: string;
|
StudyInstanceUID?: string;
|
||||||
SeriesInstanceUID?: string;
|
SeriesInstanceUID?: string;
|
||||||
|
PatientName?: string;
|
||||||
pixelData?: Uint16Array;
|
pixelData?: Uint16Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查文件是否为 DICOM 文件(通过 Magic Number 判断)
|
||||||
|
* @param filePath 文件路径
|
||||||
|
* @returns 是否为 DICOM 文件
|
||||||
|
*/
|
||||||
|
const isDICOMFile = async (filePath: string) => {
|
||||||
|
try {
|
||||||
|
// 打开文件以进行读取
|
||||||
|
const fileHandle = await fs.promises.open(filePath, "r");
|
||||||
|
const buffer = Buffer.alloc(132); // 创建一个 132 字节的缓冲区
|
||||||
|
|
||||||
|
// 从文件中读取前 132 个字节
|
||||||
|
await fileHandle.read(buffer, 0, 132, 0);
|
||||||
|
await fileHandle.close(); // 关闭文件
|
||||||
|
|
||||||
|
// 检查 "DICM" 标识 (偏移 128-131 字节)
|
||||||
|
const magicNumber = buffer.toString("utf-8", 128, 132);
|
||||||
|
return magicNumber === "DICM";
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error reading file ${filePath}:`, error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义一个异步函数来递归地查找.dcm文件
|
* 定义一个异步函数来递归地查找.dcm文件
|
||||||
* @param dir
|
* @param dir
|
||||||
|
@ -31,8 +54,8 @@ export const findDcmFiles = async (
|
||||||
const filePath = path.join(dir, file.name);
|
const filePath = path.join(dir, file.name);
|
||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
await findDcmFiles(filePath, fileList); // 递归调用以遍历子目录
|
await findDcmFiles(filePath, fileList); // 递归调用以遍历子目录
|
||||||
} else if (file.name.endsWith(".dcm")) {
|
} else if (await isDICOMFile(filePath)) {
|
||||||
fileList.push(filePath); // 如果文件是.dcm文件,添加到列表中
|
fileList.push(filePath); // 如果文件是 DICOM 文件,添加到列表中
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -52,6 +75,7 @@ export const parseDICOMFile = async (
|
||||||
const dataSet = dicomParser.parseDicom(byteArray, options);
|
const dataSet = dicomParser.parseDicom(byteArray, options);
|
||||||
const StudyInstanceUID = dataSet.string("x0020000d");
|
const StudyInstanceUID = dataSet.string("x0020000d");
|
||||||
const SeriesInstanceUID = dataSet.string("x0020000e");
|
const SeriesInstanceUID = dataSet.string("x0020000e");
|
||||||
|
const PatientName = dataSet.string("x00100030");
|
||||||
const pixelDataElement = dataSet.elements.x7fe00010;
|
const pixelDataElement = dataSet.elements.x7fe00010;
|
||||||
const pixelData = new Uint16Array(
|
const pixelData = new Uint16Array(
|
||||||
dataSet.byteArray.buffer,
|
dataSet.byteArray.buffer,
|
||||||
|
@ -63,6 +87,7 @@ export const parseDICOMFile = async (
|
||||||
filePath,
|
filePath,
|
||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
SeriesInstanceUID,
|
SeriesInstanceUID,
|
||||||
|
PatientName,
|
||||||
// pixelData,
|
// pixelData,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -98,26 +123,40 @@ export const processFilesInBatches = async (
|
||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const structureMetadata = (data: ExtractMetadata[]): StructuredData => {
|
export interface StructuredMetadata {
|
||||||
const structured: StructuredData = {};
|
filePaths: string[];
|
||||||
|
StudyInstanceUID?: string;
|
||||||
|
SeriesInstanceUID?: string;
|
||||||
|
PatientName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const structureMetadata = (
|
||||||
|
data: ExtractMetadata[]
|
||||||
|
): StructuredMetadata[] => {
|
||||||
|
const result: StructuredMetadata[] = [];
|
||||||
|
|
||||||
data.forEach((item) => {
|
data.forEach((item) => {
|
||||||
// 确保每个元素都有有效的 StudyInstanceUID 和 SeriesInstanceUID
|
// 查找是否已经有相同 UID 和 PatientName 的条目
|
||||||
if (item.StudyInstanceUID && item.SeriesInstanceUID) {
|
const existingEntry = result.find(
|
||||||
// 如果还没有为这个 StudyInstanceUID 创建记录,则初始化一个空对象
|
(entry) =>
|
||||||
if (!structured[item.StudyInstanceUID]) {
|
entry.StudyInstanceUID === item.StudyInstanceUID &&
|
||||||
structured[item.StudyInstanceUID] = {};
|
entry.SeriesInstanceUID === item.SeriesInstanceUID &&
|
||||||
}
|
entry.PatientName === item.PatientName
|
||||||
|
);
|
||||||
|
|
||||||
// 如果这个 StudyInstanceUID 下还没有这个 SeriesInstanceUID 的记录,则初始化一个空数组
|
if (existingEntry) {
|
||||||
if (!structured[item.StudyInstanceUID][item.SeriesInstanceUID]) {
|
// 如果找到了相同的条目,合并 filePath
|
||||||
structured[item.StudyInstanceUID][item.SeriesInstanceUID] = [];
|
existingEntry.filePaths.push(item.filePath);
|
||||||
}
|
} else {
|
||||||
|
// 如果没有找到,创建一个新的条目
|
||||||
// 将当前元素添加到对应的数组中
|
result.push({
|
||||||
structured[item.StudyInstanceUID][item.SeriesInstanceUID].push(item);
|
filePaths: [item.filePath],
|
||||||
|
StudyInstanceUID: item.StudyInstanceUID,
|
||||||
|
SeriesInstanceUID: item.SeriesInstanceUID,
|
||||||
|
PatientName: item.PatientName,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return structured;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
|
@ -46,6 +46,19 @@ const registerIpcMainHandlers = (
|
||||||
const { running } = data;
|
const { running } = data;
|
||||||
running ? pythonManager.startFlask() : pythonManager.stopFlask();
|
running ? pythonManager.startFlask() : pythonManager.stopFlask();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.on("scan-dicom", async () => {
|
||||||
|
const result = await dialog.showOpenDialog({
|
||||||
|
properties: ["openDirectory"],
|
||||||
|
});
|
||||||
|
if (result.canceled) return null;
|
||||||
|
const filePaths = result.filePaths[0];
|
||||||
|
const dcmPaths = await findDcmFiles(filePaths);
|
||||||
|
const items = await processFilesInBatches(dcmPaths, 4);
|
||||||
|
const structDicom = await structureMetadata(items);
|
||||||
|
Database.getDb().data.posts.push("hello world");
|
||||||
|
return structDicom;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default registerIpcMainHandlers;
|
export default registerIpcMainHandlers;
|
||||||
|
|
|
@ -11,7 +11,6 @@ import { fileURLToPath } from "node:url";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import registerIpcMainHandlers from "./ipcMainHandlers";
|
import registerIpcMainHandlers from "./ipcMainHandlers";
|
||||||
import PythonManager from "./core/PythonManager";
|
import PythonManager from "./core/PythonManager";
|
||||||
import "./core/db";
|
|
||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
|
@ -13,12 +13,15 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^5.4.0",
|
"@ant-design/icons": "^5.4.0",
|
||||||
"@google-cloud/spanner": "^7.12.0",
|
"@google-cloud/spanner": "^7.12.0",
|
||||||
|
"@hookform/resolvers": "3.9.0",
|
||||||
"@radix-ui/react-checkbox": "^1.1.1",
|
"@radix-ui/react-checkbox": "^1.1.1",
|
||||||
"@radix-ui/react-dialog": "^1.1.1",
|
"@radix-ui/react-dialog": "^1.1.1",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||||
"@radix-ui/react-label": "^2.1.0",
|
"@radix-ui/react-label": "^2.1.0",
|
||||||
"@radix-ui/react-menubar": "^1.1.1",
|
"@radix-ui/react-menubar": "^1.1.1",
|
||||||
|
"@radix-ui/react-radio-group": "^1.2.0",
|
||||||
"@radix-ui/react-scroll-area": "^1.1.0",
|
"@radix-ui/react-scroll-area": "^1.1.0",
|
||||||
|
"@radix-ui/react-select": "^2.1.1",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.0",
|
||||||
"@radix-ui/react-tabs": "^1.1.0",
|
"@radix-ui/react-tabs": "^1.1.0",
|
||||||
"@radix-ui/react-toast": "^1.2.1",
|
"@radix-ui/react-toast": "^1.2.1",
|
||||||
|
@ -48,12 +51,14 @@
|
||||||
"react-day-picker": "^8.10.1",
|
"react-day-picker": "^8.10.1",
|
||||||
"react-desktop": "^0.3.9",
|
"react-desktop": "^0.3.9",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-dropzone": "14.2.3",
|
||||||
|
"react-hook-form": "7.53.0",
|
||||||
"react-icons": "^5.2.1",
|
"react-icons": "^5.2.1",
|
||||||
"react-resizable-panels": "^2.0.20",
|
"react-resizable-panels": "^2.0.20",
|
||||||
"react-router-dom": "^6.26.0",
|
"react-router-dom": "^6.26.0",
|
||||||
"tailwind-merge": "^2.4.0",
|
"tailwind-merge": "^2.4.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"react-dropzone": "14.2.3"
|
"zod": "3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
|
|
59
apps/desktop/src/components/ui/alert.tsx
Normal file
59
apps/desktop/src/components/ui/alert.tsx
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const alertVariants = cva(
|
||||||
|
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "bg-background text-foreground",
|
||||||
|
destructive:
|
||||||
|
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const Alert = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||||
|
>(({ className, variant, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
role="alert"
|
||||||
|
className={cn(alertVariants({ variant }), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
Alert.displayName = "Alert"
|
||||||
|
|
||||||
|
const AlertTitle = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLHeadingElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<h5
|
||||||
|
ref={ref}
|
||||||
|
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertTitle.displayName = "AlertTitle"
|
||||||
|
|
||||||
|
const AlertDescription = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLParagraphElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertDescription.displayName = "AlertDescription"
|
||||||
|
|
||||||
|
export { Alert, AlertTitle, AlertDescription }
|
178
apps/desktop/src/components/ui/form.tsx
Normal file
178
apps/desktop/src/components/ui/form.tsx
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||||
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
ControllerProps,
|
||||||
|
FieldPath,
|
||||||
|
FieldValues,
|
||||||
|
FormProvider,
|
||||||
|
useFormContext,
|
||||||
|
} from "react-hook-form"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Label } from "@/components/ui/label"
|
||||||
|
|
||||||
|
const Form = FormProvider
|
||||||
|
|
||||||
|
type FormFieldContextValue<
|
||||||
|
TFieldValues extends FieldValues = FieldValues,
|
||||||
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
||||||
|
> = {
|
||||||
|
name: TName
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormFieldContext = React.createContext<FormFieldContextValue>(
|
||||||
|
{} as FormFieldContextValue
|
||||||
|
)
|
||||||
|
|
||||||
|
const FormField = <
|
||||||
|
TFieldValues extends FieldValues = FieldValues,
|
||||||
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
||||||
|
>({
|
||||||
|
...props
|
||||||
|
}: ControllerProps<TFieldValues, TName>) => {
|
||||||
|
return (
|
||||||
|
<FormFieldContext.Provider value={{ name: props.name }}>
|
||||||
|
<Controller {...props} />
|
||||||
|
</FormFieldContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const useFormField = () => {
|
||||||
|
const fieldContext = React.useContext(FormFieldContext)
|
||||||
|
const itemContext = React.useContext(FormItemContext)
|
||||||
|
const { getFieldState, formState } = useFormContext()
|
||||||
|
|
||||||
|
const fieldState = getFieldState(fieldContext.name, formState)
|
||||||
|
|
||||||
|
if (!fieldContext) {
|
||||||
|
throw new Error("useFormField should be used within <FormField>")
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id } = itemContext
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: fieldContext.name,
|
||||||
|
formItemId: `${id}-form-item`,
|
||||||
|
formDescriptionId: `${id}-form-item-description`,
|
||||||
|
formMessageId: `${id}-form-item-message`,
|
||||||
|
...fieldState,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type FormItemContextValue = {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormItemContext = React.createContext<FormItemContextValue>(
|
||||||
|
{} as FormItemContextValue
|
||||||
|
)
|
||||||
|
|
||||||
|
const FormItem = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => {
|
||||||
|
const id = React.useId()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormItemContext.Provider value={{ id }}>
|
||||||
|
<div ref={ref} className={cn("space-y-2", className)} {...props} />
|
||||||
|
</FormItemContext.Provider>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
FormItem.displayName = "FormItem"
|
||||||
|
|
||||||
|
const FormLabel = React.forwardRef<
|
||||||
|
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
||||||
|
>(({ className, ...props }, ref) => {
|
||||||
|
const { error, formItemId } = useFormField()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Label
|
||||||
|
ref={ref}
|
||||||
|
className={cn(error && "text-destructive", className)}
|
||||||
|
htmlFor={formItemId}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
FormLabel.displayName = "FormLabel"
|
||||||
|
|
||||||
|
const FormControl = React.forwardRef<
|
||||||
|
React.ElementRef<typeof Slot>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof Slot>
|
||||||
|
>(({ ...props }, ref) => {
|
||||||
|
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Slot
|
||||||
|
ref={ref}
|
||||||
|
id={formItemId}
|
||||||
|
aria-describedby={
|
||||||
|
!error
|
||||||
|
? `${formDescriptionId}`
|
||||||
|
: `${formDescriptionId} ${formMessageId}`
|
||||||
|
}
|
||||||
|
aria-invalid={!!error}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
FormControl.displayName = "FormControl"
|
||||||
|
|
||||||
|
const FormDescription = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLParagraphElement>
|
||||||
|
>(({ className, ...props }, ref) => {
|
||||||
|
const { formDescriptionId } = useFormField()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<p
|
||||||
|
ref={ref}
|
||||||
|
id={formDescriptionId}
|
||||||
|
className={cn("text-[0.8rem] text-muted-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
FormDescription.displayName = "FormDescription"
|
||||||
|
|
||||||
|
const FormMessage = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLParagraphElement>
|
||||||
|
>(({ className, children, ...props }, ref) => {
|
||||||
|
const { error, formMessageId } = useFormField()
|
||||||
|
const body = error ? String(error?.message) : children
|
||||||
|
|
||||||
|
if (!body) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<p
|
||||||
|
ref={ref}
|
||||||
|
id={formMessageId}
|
||||||
|
className={cn("text-[0.8rem] font-medium text-destructive", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{body}
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
FormMessage.displayName = "FormMessage"
|
||||||
|
|
||||||
|
export {
|
||||||
|
useFormField,
|
||||||
|
Form,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormMessage,
|
||||||
|
FormField,
|
||||||
|
}
|
44
apps/desktop/src/components/ui/radio-group.tsx
Normal file
44
apps/desktop/src/components/ui/radio-group.tsx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { CheckIcon } from "@radix-ui/react-icons"
|
||||||
|
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const RadioGroup = React.forwardRef<
|
||||||
|
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
|
||||||
|
>(({ className, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<RadioGroupPrimitive.Root
|
||||||
|
className={cn("grid gap-2", className)}
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
|
||||||
|
|
||||||
|
const RadioGroupItem = React.forwardRef<
|
||||||
|
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
|
||||||
|
>(({ className, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<RadioGroupPrimitive.Item
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"aspect-square h-4 w-4 rounded-full border border-primary text-primary shadow focus:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
||||||
|
<CheckIcon className="h-3.5 w-3.5 fill-primary" />
|
||||||
|
</RadioGroupPrimitive.Indicator>
|
||||||
|
</RadioGroupPrimitive.Item>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
|
||||||
|
|
||||||
|
export { RadioGroup, RadioGroupItem }
|
164
apps/desktop/src/components/ui/select.tsx
Normal file
164
apps/desktop/src/components/ui/select.tsx
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import {
|
||||||
|
CaretSortIcon,
|
||||||
|
CheckIcon,
|
||||||
|
ChevronDownIcon,
|
||||||
|
ChevronUpIcon,
|
||||||
|
} from "@radix-ui/react-icons"
|
||||||
|
import * as SelectPrimitive from "@radix-ui/react-select"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const Select = SelectPrimitive.Root
|
||||||
|
|
||||||
|
const SelectGroup = SelectPrimitive.Group
|
||||||
|
|
||||||
|
const SelectValue = SelectPrimitive.Value
|
||||||
|
|
||||||
|
const SelectTrigger = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Trigger
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<SelectPrimitive.Icon asChild>
|
||||||
|
<CaretSortIcon className="h-4 w-4 opacity-50" />
|
||||||
|
</SelectPrimitive.Icon>
|
||||||
|
</SelectPrimitive.Trigger>
|
||||||
|
))
|
||||||
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
||||||
|
|
||||||
|
const SelectScrollUpButton = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.ScrollUpButton
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"flex cursor-default items-center justify-center py-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ChevronUpIcon />
|
||||||
|
</SelectPrimitive.ScrollUpButton>
|
||||||
|
))
|
||||||
|
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
|
||||||
|
|
||||||
|
const SelectScrollDownButton = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.ScrollDownButton
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"flex cursor-default items-center justify-center py-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ChevronDownIcon />
|
||||||
|
</SelectPrimitive.ScrollDownButton>
|
||||||
|
))
|
||||||
|
SelectScrollDownButton.displayName =
|
||||||
|
SelectPrimitive.ScrollDownButton.displayName
|
||||||
|
|
||||||
|
const SelectContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||||
|
>(({ className, children, position = "popper", ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Portal>
|
||||||
|
<SelectPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
|
position === "popper" &&
|
||||||
|
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
position={position}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<SelectScrollUpButton />
|
||||||
|
<SelectPrimitive.Viewport
|
||||||
|
className={cn(
|
||||||
|
"p-1",
|
||||||
|
position === "popper" &&
|
||||||
|
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</SelectPrimitive.Viewport>
|
||||||
|
<SelectScrollDownButton />
|
||||||
|
</SelectPrimitive.Content>
|
||||||
|
</SelectPrimitive.Portal>
|
||||||
|
))
|
||||||
|
SelectContent.displayName = SelectPrimitive.Content.displayName
|
||||||
|
|
||||||
|
const SelectLabel = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Label
|
||||||
|
ref={ref}
|
||||||
|
className={cn("px-2 py-1.5 text-sm font-semibold", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
SelectLabel.displayName = SelectPrimitive.Label.displayName
|
||||||
|
|
||||||
|
const SelectItem = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Item
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
<SelectPrimitive.ItemIndicator>
|
||||||
|
<CheckIcon className="h-4 w-4" />
|
||||||
|
</SelectPrimitive.ItemIndicator>
|
||||||
|
</span>
|
||||||
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||||
|
</SelectPrimitive.Item>
|
||||||
|
))
|
||||||
|
SelectItem.displayName = SelectPrimitive.Item.displayName
|
||||||
|
|
||||||
|
const SelectSeparator = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Separator
|
||||||
|
ref={ref}
|
||||||
|
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
||||||
|
|
||||||
|
export {
|
||||||
|
Select,
|
||||||
|
SelectGroup,
|
||||||
|
SelectValue,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectContent,
|
||||||
|
SelectLabel,
|
||||||
|
SelectItem,
|
||||||
|
SelectSeparator,
|
||||||
|
SelectScrollUpButton,
|
||||||
|
SelectScrollDownButton,
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
import { useDropzone } from "react-dropzone";
|
import { useDropzone } from "react-dropzone";
|
||||||
|
|
||||||
interface FileUploadProps {
|
interface FileUploadProps {
|
||||||
|
@ -9,22 +9,20 @@ interface FileUploadProps {
|
||||||
const FileUpload: React.FC<FileUploadProps> = ({ onFilesSelected }) => {
|
const FileUpload: React.FC<FileUploadProps> = ({ onFilesSelected }) => {
|
||||||
const onDrop = (acceptedFiles: File[]) => {
|
const onDrop = (acceptedFiles: File[]) => {
|
||||||
console.log("Accepted files:", acceptedFiles);
|
console.log("Accepted files:", acceptedFiles);
|
||||||
// 调用父组件传入的回调函数处理文件
|
|
||||||
if (onFilesSelected) {
|
if (onFilesSelected) {
|
||||||
onFilesSelected(acceptedFiles);
|
onFilesSelected(acceptedFiles);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
const { getRootProps, getInputProps, isDragActive, open } = useDropzone({
|
||||||
onDrop,
|
onDrop,
|
||||||
multiple: true,
|
multiple: true,
|
||||||
accept: {
|
accept: {
|
||||||
"file/dicom": [".dcm"],
|
"file/dicom": [".dcm"],
|
||||||
"file/model": [".stl"],
|
"file/model": [".stl"],
|
||||||
},
|
},
|
||||||
noClick: false, // 启用点击选择文件
|
noClick: true,
|
||||||
noKeyboard: true, // 禁用键盘选择(可选)
|
noKeyboard: true,
|
||||||
directory: true, // 启用文件夹上传支持
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -32,13 +30,15 @@ const FileUpload: React.FC<FileUploadProps> = ({ onFilesSelected }) => {
|
||||||
{...getRootProps()}
|
{...getRootProps()}
|
||||||
className="border-dashed border-2 p-4 py-8 rounded-md text-center cursor-pointer"
|
className="border-dashed border-2 p-4 py-8 rounded-md text-center cursor-pointer"
|
||||||
>
|
>
|
||||||
<input {...getInputProps()} webkitdirectory="" directory />
|
<input {...getInputProps({ webkitdirectory: true })} />
|
||||||
{isDragActive ? (
|
{isDragActive ? (
|
||||||
<p>把文件/夹放到这里。。。</p>
|
<p>把文件/夹放到这里。。。</p>
|
||||||
) : (
|
) : (
|
||||||
<p>将文件/夹拖放到此处,或单击选择</p>
|
<p>将文件/夹拖放到此处,或单击选择</p>
|
||||||
)}
|
)}
|
||||||
<Button className="mt-2">导入数据</Button>
|
<Button className="mt-2" onClick={open}>
|
||||||
|
导入数据
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import FileUpload from "./FileUpload";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
export const Datasource = () => {
|
export const Datasource = () => {
|
||||||
return (
|
return (
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<FileUpload />
|
<div>
|
||||||
|
<Button onClick={() => window.ipcRenderer.send("scan-dicom")}>
|
||||||
|
上传
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Toaster } from "@/components/ui/toaster";
|
||||||
import { LeftDocker } from "./LeftDocker";
|
import { LeftDocker } from "./LeftDocker";
|
||||||
import { Outlet } from "react-router-dom";
|
import { Outlet } from "react-router-dom";
|
||||||
|
|
||||||
|
@ -6,8 +7,7 @@ const LayoutMain = () => {
|
||||||
document.querySelector("html")?.getAttribute("platform") ?? "macos";
|
document.querySelector("html")?.getAttribute("platform") ?? "macos";
|
||||||
// const titleBarStyles =
|
// const titleBarStyles =
|
||||||
// platform === "macos" ? "pl-[5rem] pr-[.5rem]" : "pl-[.5rem]";
|
// platform === "macos" ? "pl-[5rem] pr-[.5rem]" : "pl-[.5rem]";
|
||||||
const titleBarStyles =
|
const titleBarStyles = platform === "macos" ? "px-[.5rem]" : "pl-[.5rem]";
|
||||||
platform === "macos" ? "px-[.5rem]" : "pl-[.5rem]";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full">
|
<div className="h-full">
|
||||||
|
@ -35,6 +35,7 @@ const LayoutMain = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Toaster />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,15 @@
|
||||||
|
import { motion } from "framer-motion";
|
||||||
import { ModelTable } from "./ModelTable";
|
import { ModelTable } from "./ModelTable";
|
||||||
|
|
||||||
export const Models = () => {
|
export const Models = () => {
|
||||||
return <ModelTable />;
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial={{ y: 10, opacity: 0 }}
|
||||||
|
animate={{ y: 0, opacity: 1 }}
|
||||||
|
exit={{ y: 0, opacity: 0 }}
|
||||||
|
transition={{ duration: 0.25 }}
|
||||||
|
>
|
||||||
|
<ModelTable />
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,12 +1,98 @@
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from "framer-motion";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
import { toast } from "@/components/ui/use-toast";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
inferEngine: z.string({
|
||||||
|
required_error: "Please select an email to display.",
|
||||||
|
}),
|
||||||
|
workspace: z.string({ required_error: "请设置工作空间" }),
|
||||||
|
});
|
||||||
|
|
||||||
export const Setting = () => {
|
export const Setting = () => {
|
||||||
return <motion.div
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
initial={{ y: 10, opacity: 0 }}
|
resolver: zodResolver(FormSchema),
|
||||||
animate={{ y: 0, opacity: 1 }}
|
});
|
||||||
exit={{ y: 0, opacity: 0 }}
|
|
||||||
transition={{ duration: 0.25 }}
|
const onSubmit = (data: z.infer<typeof FormSchema>) => {
|
||||||
>
|
toast({
|
||||||
<div>设置</div>
|
title: "You submitted the following values:",
|
||||||
</motion.div>
|
description: (
|
||||||
}
|
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
|
||||||
|
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
|
||||||
|
</pre>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial={{ y: 10, opacity: 0 }}
|
||||||
|
animate={{ y: 0, opacity: 1 }}
|
||||||
|
exit={{ y: 0, opacity: 0 }}
|
||||||
|
transition={{ duration: 0.25 }}
|
||||||
|
>
|
||||||
|
<div className="p-4">
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="w-full space-y-3"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="inferEngine"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<FormLabel>生成引擎</FormLabel>
|
||||||
|
<FormDescription>选择推理的硬件设备</FormDescription>
|
||||||
|
</div>
|
||||||
|
<div className="flex">
|
||||||
|
<Select
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
defaultValue={field.value}
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="iGPU(推荐)" />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="m@example.com">
|
||||||
|
iGPU(推荐)
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="m@google.com">NPU</SelectItem>
|
||||||
|
<SelectItem value="m@support.com">CPU</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
112
pnpm-lock.yaml
112
pnpm-lock.yaml
|
@ -16,6 +16,9 @@ importers:
|
||||||
'@google-cloud/spanner':
|
'@google-cloud/spanner':
|
||||||
specifier: ^7.12.0
|
specifier: ^7.12.0
|
||||||
version: 7.14.0
|
version: 7.14.0
|
||||||
|
'@hookform/resolvers':
|
||||||
|
specifier: 3.9.0
|
||||||
|
version: 3.9.0(react-hook-form@7.53.0(react@18.3.1))
|
||||||
'@radix-ui/react-checkbox':
|
'@radix-ui/react-checkbox':
|
||||||
specifier: ^1.1.1
|
specifier: ^1.1.1
|
||||||
version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
@ -31,9 +34,15 @@ importers:
|
||||||
'@radix-ui/react-menubar':
|
'@radix-ui/react-menubar':
|
||||||
specifier: ^1.1.1
|
specifier: ^1.1.1
|
||||||
version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-radio-group':
|
||||||
|
specifier: ^1.2.0
|
||||||
|
version: 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
'@radix-ui/react-scroll-area':
|
'@radix-ui/react-scroll-area':
|
||||||
specifier: ^1.1.0
|
specifier: ^1.1.0
|
||||||
version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-select':
|
||||||
|
specifier: ^2.1.1
|
||||||
|
version: 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
'@radix-ui/react-slot':
|
'@radix-ui/react-slot':
|
||||||
specifier: ^1.1.0
|
specifier: ^1.1.0
|
||||||
version: 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
version: 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
@ -124,6 +133,9 @@ importers:
|
||||||
react-dropzone:
|
react-dropzone:
|
||||||
specifier: 14.2.3
|
specifier: 14.2.3
|
||||||
version: 14.2.3(react@18.3.1)
|
version: 14.2.3(react@18.3.1)
|
||||||
|
react-hook-form:
|
||||||
|
specifier: 7.53.0
|
||||||
|
version: 7.53.0(react@18.3.1)
|
||||||
react-icons:
|
react-icons:
|
||||||
specifier: ^5.2.1
|
specifier: ^5.2.1
|
||||||
version: 5.3.0(react@18.3.1)
|
version: 5.3.0(react@18.3.1)
|
||||||
|
@ -139,6 +151,9 @@ importers:
|
||||||
tailwindcss-animate:
|
tailwindcss-animate:
|
||||||
specifier: ^1.0.7
|
specifier: ^1.0.7
|
||||||
version: 1.0.7(tailwindcss@3.4.10)
|
version: 1.0.7(tailwindcss@3.4.10)
|
||||||
|
zod:
|
||||||
|
specifier: 3.23.8
|
||||||
|
version: 3.23.8
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@radix-ui/react-icons':
|
'@radix-ui/react-icons':
|
||||||
specifier: ^1.3.0
|
specifier: ^1.3.0
|
||||||
|
@ -569,6 +584,11 @@ packages:
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
'@hookform/resolvers@3.9.0':
|
||||||
|
resolution: {integrity: sha512-bU0Gr4EepJ/EQsH/IwEzYLsT/PEj5C0ynLQ4m+GSHS+xKH4TfSelhluTgOaoc4kA5s7eCsQbM4wvZLzELmWzUg==}
|
||||||
|
peerDependencies:
|
||||||
|
react-hook-form: ^7.0.0
|
||||||
|
|
||||||
'@huggingface/jinja@0.2.2':
|
'@huggingface/jinja@0.2.2':
|
||||||
resolution: {integrity: sha512-/KPde26khDUIPkTGU82jdtTW9UAuvUTumCAbFs/7giR0SxsvZC4hru51PBvpijH6BVkHcROcvZM/lpy5h1jRRA==}
|
resolution: {integrity: sha512-/KPde26khDUIPkTGU82jdtTW9UAuvUTumCAbFs/7giR0SxsvZC4hru51PBvpijH6BVkHcROcvZM/lpy5h1jRRA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
@ -1024,6 +1044,19 @@ packages:
|
||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-radio-group@1.2.0':
|
||||||
|
resolution: {integrity: sha512-yv+oiLaicYMBpqgfpSPw6q+RyXlLdIpQWDHZbUKURxe+nEh53hFXPPlfhfQQtYkS5MMK/5IWIa76SksleQZSzw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-roving-focus@1.1.0':
|
'@radix-ui/react-roving-focus@1.1.0':
|
||||||
resolution: {integrity: sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==}
|
resolution: {integrity: sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -1050,6 +1083,19 @@ packages:
|
||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-select@2.1.1':
|
||||||
|
resolution: {integrity: sha512-8iRDfyLtzxlprOo9IicnzvpsO1wNCkuwzzCM+Z5Rb5tNOpCdMvcc2AkzX0Fz+Tz9v6NJ5B/7EEgyZveo4FBRfQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-slot@1.0.2':
|
'@radix-ui/react-slot@1.0.2':
|
||||||
resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
|
resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -3418,6 +3464,12 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '>= 16.8 || 18.0.0'
|
react: '>= 16.8 || 18.0.0'
|
||||||
|
|
||||||
|
react-hook-form@7.53.0:
|
||||||
|
resolution: {integrity: sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.8.0 || ^17 || ^18 || ^19
|
||||||
|
|
||||||
react-icons@5.3.0:
|
react-icons@5.3.0:
|
||||||
resolution: {integrity: sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==}
|
resolution: {integrity: sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -4058,6 +4110,9 @@ packages:
|
||||||
resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==}
|
resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
|
|
||||||
|
zod@3.23.8:
|
||||||
|
resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==}
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
7zip-bin@5.2.0: {}
|
7zip-bin@5.2.0: {}
|
||||||
|
@ -4483,6 +4538,10 @@ snapshots:
|
||||||
protobufjs: 7.4.0
|
protobufjs: 7.4.0
|
||||||
yargs: 17.7.2
|
yargs: 17.7.2
|
||||||
|
|
||||||
|
'@hookform/resolvers@3.9.0(react-hook-form@7.53.0(react@18.3.1))':
|
||||||
|
dependencies:
|
||||||
|
react-hook-form: 7.53.0(react@18.3.1)
|
||||||
|
|
||||||
'@huggingface/jinja@0.2.2': {}
|
'@huggingface/jinja@0.2.2': {}
|
||||||
|
|
||||||
'@humanwhocodes/config-array@0.11.14':
|
'@humanwhocodes/config-array@0.11.14':
|
||||||
|
@ -4930,6 +4989,24 @@ snapshots:
|
||||||
'@types/react': 18.3.4
|
'@types/react': 18.3.4
|
||||||
'@types/react-dom': 18.3.0
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
|
'@radix-ui/react-radio-group@1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/primitive': 1.1.0
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
'@radix-ui/react-context': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
'@radix-ui/react-direction': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
'@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-roving-focus': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-size': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 18.3.4
|
||||||
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
'@radix-ui/react-roving-focus@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
'@radix-ui/react-roving-focus@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@radix-ui/primitive': 1.1.0
|
'@radix-ui/primitive': 1.1.0
|
||||||
|
@ -4964,6 +5041,35 @@ snapshots:
|
||||||
'@types/react': 18.3.4
|
'@types/react': 18.3.4
|
||||||
'@types/react-dom': 18.3.0
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
|
'@radix-ui/react-select@2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/number': 1.1.0
|
||||||
|
'@radix-ui/primitive': 1.1.0
|
||||||
|
'@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
'@radix-ui/react-context': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
'@radix-ui/react-direction': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
'@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-focus-guards': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
'@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-id': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
'@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-slot': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
'@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
aria-hidden: 1.2.4
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
react-remove-scroll: 2.5.7(@types/react@18.3.4)(react@18.3.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 18.3.4
|
||||||
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
'@radix-ui/react-slot@1.0.2(@types/react@18.3.4)(react@18.3.1)':
|
'@radix-ui/react-slot@1.0.2(@types/react@18.3.4)(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.25.4
|
'@babel/runtime': 7.25.4
|
||||||
|
@ -7666,6 +7772,10 @@ snapshots:
|
||||||
prop-types: 15.8.1
|
prop-types: 15.8.1
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
|
|
||||||
|
react-hook-form@7.53.0(react@18.3.1):
|
||||||
|
dependencies:
|
||||||
|
react: 18.3.1
|
||||||
|
|
||||||
react-icons@5.3.0(react@18.3.1):
|
react-icons@5.3.0(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
|
@ -8332,3 +8442,5 @@ snapshots:
|
||||||
archiver-utils: 3.0.4
|
archiver-utils: 3.0.4
|
||||||
compress-commons: 4.1.2
|
compress-commons: 4.1.2
|
||||||
readable-stream: 3.6.2
|
readable-stream: 3.6.2
|
||||||
|
|
||||||
|
zod@3.23.8: {}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user