From b7944391dbb37b72cf56d4234950c441efcbac0d Mon Sep 17 00:00:00 2001 From: chase Date: Wed, 31 Dec 2025 14:24:10 +0800 Subject: [PATCH 01/15] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=B8=85=E6=B4=97=E7=AD=9B=E9=80=89=E9=80=BB=E8=BE=91-?= =?UTF-8?q?=E7=AD=9B=E9=80=89=E4=BF=AE=E6=94=B9=E4=B8=BA=E5=A4=9A=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Create/components/OperatorLibrary.tsx | 124 +++++++----------- 1 file changed, 48 insertions(+), 76 deletions(-) diff --git a/frontend/src/pages/DataCleansing/Create/components/OperatorLibrary.tsx b/frontend/src/pages/DataCleansing/Create/components/OperatorLibrary.tsx index 0792e9a8..7611fb09 100644 --- a/frontend/src/pages/DataCleansing/Create/components/OperatorLibrary.tsx +++ b/frontend/src/pages/DataCleansing/Create/components/OperatorLibrary.tsx @@ -100,52 +100,43 @@ const OperatorLibrary: React.FC = ({ const [searchTerm, setSearchTerm] = useState(""); const [showFavorites, setShowFavorites] = useState(false); const [favorites, setFavorites] = useState>(new Set()); - const [selectedCategory, setSelectedCategory] = useState("all"); - const [expandedCategories, setExpandedCategories] = useState>( - new Set([]) - ); + const [selectedCategory, setSelectedCategory] = useState([]); + + const [operatorListFiltered, setOperatorListFiltered] = useState([]); + + const hasCommon = (arr1: string[], arr2: string[]) => { + const set1 = new Set(arr1); + return arr2.some(item => set1.has(item)); + } // 按分类分组 const groupedOperators = useMemo(() => { const groups: { [key: string]: OperatorI[] } = {}; + let operatorFilteredList = []; categoryOptions.forEach((cat: any) => { groups[cat.name] = { ...cat, operators: operatorList.filter((op) => op.categories?.includes(cat.id)), }; }); - - if (selectedCategory && selectedCategory !== "all") { - Object.keys(groups).forEach((key) => { - if (groups[key].id !== selectedCategory) { - delete groups[key]; - } - }); + if (selectedCategory.length) { + operatorFilteredList = operatorList.filter((op) => hasCommon(op.categories || [], selectedCategory)); + } else { + operatorFilteredList = [...operatorList]; } if (searchTerm) { - Object.keys(groups).forEach((key) => { - groups[key].operators = groups[key].operators.filter((operator) => - operator.name.toLowerCase().includes(searchTerm.toLowerCase()) - ); - if (groups[key].operators.length === 0) { - delete groups[key]; - } - }); + operatorFilteredList = operatorFilteredList.filter(operator => + operator.name.toLowerCase().includes(searchTerm.toLowerCase()) + ); } if (showFavorites) { - Object.keys(groups).forEach((key) => { - groups[key].operators = groups[key].operators.filter((operator) => - favorites.has(operator.id) - ); - if (groups[key].operators.length === 0) { - delete groups[key]; - } - }); + operatorFilteredList = operatorFilteredList.filter((operator) => + favorites.has(operator.id) + ); } - - setExpandedCategories(new Set(Object.keys(groups))); + setOperatorListFiltered([...operatorFilteredList]); return groups; }, [categoryOptions, selectedCategory, searchTerm, showFavorites]); @@ -195,12 +186,12 @@ const OperatorLibrary: React.FC = ({
- 算子库({filteredOperators.length}) + 算子库({operatorList.length})
{/* 过滤器 */} -
+
} placeholder="搜索算子名称..." @@ -210,8 +201,10 @@ const OperatorLibrary: React.FC = ({ /> @@ -227,53 +220,32 @@ const OperatorLibrary: React.FC = ({ )} +
+ +
{/* 算子列表 */}
- {/* 分类算子 */} - - setExpandedCategories( - new Set(Array.isArray(keys) ? keys : [keys]) - ) - } - > - {Object.entries(groupedOperators).map(([key, category]) => ( - - - {category.name} - {category.operators.length} - - -
- } - > - - - ))} - - {filteredOperators.length === 0 && ( + + + {operatorListFiltered.length === 0 && (
未找到匹配的算子
From 4dca1c1f95c84841351b9387ee29885ea68a2d76 Mon Sep 17 00:00:00 2001 From: chase Date: Wed, 31 Dec 2025 15:33:18 +0800 Subject: [PATCH 02/15] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=B8=85=E6=B4=97=E7=AD=9B=E9=80=89=E9=80=BB=E8=BE=91-?= =?UTF-8?q?=E7=AD=9B=E9=80=89=E4=BF=AE=E6=94=B9=E4=B8=BA=E5=A4=9A=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/DataCleansing/Create/components/OperatorLibrary.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/DataCleansing/Create/components/OperatorLibrary.tsx b/frontend/src/pages/DataCleansing/Create/components/OperatorLibrary.tsx index 7611fb09..800c784f 100644 --- a/frontend/src/pages/DataCleansing/Create/components/OperatorLibrary.tsx +++ b/frontend/src/pages/DataCleansing/Create/components/OperatorLibrary.tsx @@ -201,7 +201,7 @@ const OperatorLibrary: React.FC = ({ /> setSelectedKeys(e.target.value ? [e.target.value] : [])} + onPressEnter={() => confirm()} + className="mb-2" + /> +
+ + +
+
+ ), + onFilter: (value: string, record: any) => record.name.toLowerCase().includes(value.toLowerCase()), + }, + { + title: "开始时间", + dataIndex: "startTime", + key: "startTime", + sorter: (a: any, b: any) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime(), + }, + { + title: "结束时间", + dataIndex: "endTime", + key: "endTime", + sorter: (a: any, b: any) => new Date(a.endTime).getTime() - new Date(b.endTime).getTime(), + }, + { + title: "执行时长", + dataIndex: "duration", + key: "duration", + }, + { + title: "处理文件数", + dataIndex: "processedFiles", + key: "processedFiles", + sorter: (a: any, b: any) => a.processedFiles - b.processedFiles, + }, + { + title: "成功率", + dataIndex: "successRate", + key: "successRate", + sorter: (a: any, b: any) => a.successRate - b.successRate, + render: (rate: number) => `${rate}%`, + }, + { + title: "状态", + dataIndex: "status", + key: "status", + filters: [ + { text: "已完成", value: "已完成" }, + { text: "失败", value: "失败" }, + { text: "运行中", value: "运行中" }, + ], + onFilter: (value: string, record: any) => record.status === value, + render: (status: string) => ( + + ), + }, + ] + return task?.instance?.length > 0 && ( <> - ({ - title: navigate(`/data/operator-market/plugin-detail/${item?.id}`)} - > - {item?.name} - , - description: item?.description, - status: "finish" - }))} - className="overflow-auto" - /> + + + + + 算子执行报告 + + 每个算子的详细执行情况 + + + ({ + id: item?.id, + name: item?.name, + startTime: new Date(task?.startedAt).toLocaleTimeString(), + endTime: task?.finishedAt + ? new Date(task.finishedAt).toLocaleTimeString() + : '-', + duration: task.duration, + status: task.status.label, + processedFiles: task.progress.finishedFileNum, + successRate: task?.progress.successRate, + }))} pagination={false} size="middle" /> + + ); } diff --git a/frontend/src/pages/DataCleansing/Home/components/TaskList.tsx b/frontend/src/pages/DataCleansing/Home/components/TaskList.tsx index f188db9b..61922280 100644 --- a/frontend/src/pages/DataCleansing/Home/components/TaskList.tsx +++ b/frontend/src/pages/DataCleansing/Home/components/TaskList.tsx @@ -20,7 +20,6 @@ import { queryCleaningTasksUsingGet, stopCleaningTaskUsingPost, } from "../../cleansing.api"; -import { Eye } from "lucide-react"; export default function TaskList() { const navigate = useNavigate(); @@ -89,10 +88,7 @@ export default function TaskList() { ...(isRunning ? [ pauseBtn ] : []), - ...(isComplete - ? [ startBtn ] - : []), - ...(showStart + ...(showStart || isComplete ? [ startBtn ] : []), { diff --git a/frontend/src/pages/DataCleansing/cleansing.const.tsx b/frontend/src/pages/DataCleansing/cleansing.const.tsx index f2027c25..a12a7d07 100644 --- a/frontend/src/pages/DataCleansing/cleansing.const.tsx +++ b/frontend/src/pages/DataCleansing/cleansing.const.tsx @@ -59,7 +59,7 @@ export const TaskStatusMap = { icon: , }, [TaskStatus.RUNNING]: { - label: "进行中", + label: "运行中", value: TaskStatus.RUNNING, color: "blue", icon: , diff --git a/frontend/src/utils/utils.ts b/frontend/src/utils/utils.ts new file mode 100644 index 00000000..d084ccad --- /dev/null +++ b/frontend/src/utils/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/runtime/python-executor/datamate/sql_manager/persistence_atction.py b/runtime/python-executor/datamate/sql_manager/persistence_atction.py index 4c425db3..9137f26f 100644 --- a/runtime/python-executor/datamate/sql_manager/persistence_atction.py +++ b/runtime/python-executor/datamate/sql_manager/persistence_atction.py @@ -25,7 +25,9 @@ def load_sql_dict(): with open(sql_config_path, 'r', encoding='utf-8') as f: return json.load(f) - def update_task_result(self, sample, file_id = str(uuid.uuid4())): + def update_task_result(self, sample, file_id = None): + if file_id is None: + file_id = str(uuid.uuid4()) instance_id = str(sample.get("instance_id")) src_file_name = str(sample.get("sourceFileName")) src_file_type = str(sample.get("sourceFileType")) From 32501197a4be0cc4bcc023a9ef12c06804179080 Mon Sep 17 00:00:00 2001 From: hhhhsc <1710496817@qq.com> Date: Mon, 5 Jan 2026 20:03:58 +0800 Subject: [PATCH 06/15] =?UTF-8?q?feat:=20=E5=89=8D=E7=AB=AF=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/CardView.tsx | 34 ++++---- .../Detail/components/BasicInfo.tsx | 9 ++- .../Detail/components/Overview.tsx | 78 +++++++++++++++++++ .../OperatorMarket/Home/OperatorMarket.tsx | 14 +++- .../pages/OperatorMarket/operator.const.tsx | 49 +++++++++++- frontend/src/utils/unit.ts | 2 +- 6 files changed, 169 insertions(+), 17 deletions(-) diff --git a/frontend/src/components/CardView.tsx b/frontend/src/components/CardView.tsx index 9e044aad..9412c03f 100644 --- a/frontend/src/components/CardView.tsx +++ b/frontend/src/components/CardView.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, useRef } from "react"; import { Tag, Pagination, Tooltip, Empty, Popover, Spin } from "antd"; -import { ClockCircleOutlined, StarFilled } from "@ant-design/icons"; +import { ClockCircleOutlined, StarFilled, StarOutlined } from "@ant-design/icons"; import type { ItemType } from "antd/es/menu/interface"; import { formatDateTime } from "@/utils/unit"; import ActionDropdown from "./ActionDropdown"; @@ -232,16 +232,24 @@ function CardView(props: CardViewProps) { - {onFavorite && ( - onFavorite?.(item)} - /> - )} + {onFavorite && + (isFavorite?.(item) ? ( + { + e.stopPropagation(); + onFavorite?.(item); + }} + /> + ) : ( + { + e.stopPropagation(); + onFavorite?.(item); + }} + /> + ))}
@@ -249,7 +257,7 @@ function CardView(props: CardViewProps) { {/* Description */} -

+

{item?.description} @@ -272,7 +280,7 @@ function CardView(props: CardViewProps) {

{/* Divider & Actions */} -
+
diff --git a/frontend/src/pages/DataCleansing/Detail/components/BasicInfo.tsx b/frontend/src/pages/DataCleansing/Detail/components/BasicInfo.tsx index cfa25725..f1ff44fc 100644 --- a/frontend/src/pages/DataCleansing/Detail/components/BasicInfo.tsx +++ b/frontend/src/pages/DataCleansing/Detail/components/BasicInfo.tsx @@ -1,8 +1,9 @@ import {CleansingTask, TaskStatus} from "@/pages/DataCleansing/cleansing.model"; import { Button, Card, Descriptions, Progress } from "antd"; -import { Activity, AlertCircle, CheckCircle, Clock } from "lucide-react"; +import { Activity, AlertCircle, BookOpen, CheckCircle, Clock } from "lucide-react"; import { useNavigate } from "react-router"; import {formatExecutionDuration} from "@/utils/unit.ts"; +import {CardHeader, CardTitle} from "@/components/Card.tsx"; export default function BasicInfo({ task }: { task: CleansingTask }) { const navigate = useNavigate(); @@ -61,6 +62,12 @@ export default function BasicInfo({ task }: { task: CleansingTask }) { <> {/* 执行摘要 */} + + + + 执行摘要 + +
diff --git a/frontend/src/pages/OperatorMarket/Detail/components/Overview.tsx b/frontend/src/pages/OperatorMarket/Detail/components/Overview.tsx index c5134714..3393f2a5 100644 --- a/frontend/src/pages/OperatorMarket/Detail/components/Overview.tsx +++ b/frontend/src/pages/OperatorMarket/Detail/components/Overview.tsx @@ -1,4 +1,5 @@ import {DescriptionsProps, Card, Descriptions, Tag} from "antd"; +import {FileExtensionMap, MediaType} from "@/pages/OperatorMarket/operator.const.tsx"; export default function Overview({ operator }) { const descriptionItems: DescriptionsProps["items"] = [ @@ -44,6 +45,16 @@ export default function Overview({ operator }) { children: operator.updatedAt, }, ]; + + const tags = ["图像处理", "预处理", "缩放", "裁剪", "旋转", "计算机视觉", "深度学习"]; + + const performance = { + accuracy: 99.5, + speed: "50ms/image", + memory: "128MB", + throughput: "20 images/sec", + }; + return (
{/* 基本信息 */} @@ -54,6 +65,73 @@ export default function Overview({ operator }) {

{operator.description}

+ + {/* 标签 */} + +

标签

+
+ {tags.map((tag, index) => ( + + {tag} + + ))} +
+
+ + {/* 性能指标 */} + +

性能指标

+
+ {performance.accuracy && ( +
+
{performance.accuracy}%
+
准确率
+
+ )} +
+
{performance.speed}
+
处理速度
+
+
+
{performance.memory}
+
内存使用
+
+
+
{performance.throughput}
+
吞吐量
+
+
+
+ + {/* 输入输出格式 */} + +

支持格式

+
+
+

输入格式

+
+ {FileExtensionMap[operator.inputs as MediaType].map((format, index) => ( + + {format} + + ))} +
+
+
+

输出格式

+
+ {FileExtensionMap[operator.outputs as MediaType].map((format, index) => ( + + {format} + + ))} +
+
+
+
); } diff --git a/frontend/src/pages/OperatorMarket/Home/OperatorMarket.tsx b/frontend/src/pages/OperatorMarket/Home/OperatorMarket.tsx index 807587ce..09b0951f 100644 --- a/frontend/src/pages/OperatorMarket/Home/OperatorMarket.tsx +++ b/frontend/src/pages/OperatorMarket/Home/OperatorMarket.tsx @@ -23,7 +23,7 @@ import { deleteOperatorByIdUsingDelete, downloadExampleOperatorUsingGet, queryCategoryTreeUsingGet, - queryOperatorsUsingPost, + queryOperatorsUsingPost, updateOperatorByIdUsingPut, } from "../operator.api"; import { mapOperator } from "../operator.const"; @@ -80,6 +80,16 @@ export default function OperatorMarketPage() { } }; + const handleStar = async (operator: OperatorI) => { + const data = { + id: operator.id, + isStar: !operator.isStar + }; + await updateOperatorByIdUsingPut(operator.id, data); + fetchData(); + await initCategoriesTree(); + } + const operations = [ { key: "edit", @@ -205,6 +215,8 @@ export default function OperatorMarketPage() { data={tableData} pagination={pagination} operations={operations} + onFavorite={handleStar} + isFavorite={(operator: OperatorI) => operator.isStar} onView={(item) => navigate(`/data/operator-market/plugin-detail/${item.id}`)} /> ) : ( diff --git a/frontend/src/pages/OperatorMarket/operator.const.tsx b/frontend/src/pages/OperatorMarket/operator.const.tsx index 424fd59f..ab562c1f 100644 --- a/frontend/src/pages/OperatorMarket/operator.const.tsx +++ b/frontend/src/pages/OperatorMarket/operator.const.tsx @@ -5,7 +5,7 @@ import { formatDateTime } from "@/utils/unit.ts"; const getOperatorVisual = ( op: OperatorI -): { icon: React.ReactNode; iconColor?: string } => { +): { modal: String; icon: React.ReactNode; iconColor?: string } => { const type = (op?.type || "").toLowerCase(); const categories = (op?.categories || []).map((c) => (c || "").toLowerCase()); const inputs = (op?.inputs || "").toLowerCase(); @@ -60,6 +60,7 @@ const getOperatorVisual = ( if (isMultimodal) { return { + modal: "多模态", icon: , iconColor: "#F472B6", }; @@ -67,6 +68,7 @@ const getOperatorVisual = ( if (isVideoOp) { return { + modal: "视频", icon: , iconColor: "#22D3EE", }; @@ -74,6 +76,7 @@ const getOperatorVisual = ( if (isAudioOp) { return { + modal: "音频", icon: , iconColor: "#F59E0B", }; @@ -81,6 +84,7 @@ const getOperatorVisual = ( if (isImageOp) { return { + modal: "图片", icon: , iconColor: "#38BDF8", // 图像算子背景色 }; @@ -88,12 +92,14 @@ const getOperatorVisual = ( if (isTextOp) { return { + modal: "文本", icon: , iconColor: "#A78BFA", // 文本算子背景色 }; } return { + modal: "多模态", icon: , iconColor: undefined, }; @@ -111,5 +117,46 @@ export const mapOperator = (op: OperatorI) => { formatDateTime(op?.updatedAt) || formatDateTime(op?.createdAt) || "--", + statistics: [ + { + label: "使用次数", + value: Math.floor(Math.random() * 1000) + 1 + }, + { + label: "类型", + value: visual.modal || "text", + }, + { + label: "大小", + value: `${(Math.floor(Math.random() * 91) + 10) / 10} MB`, + }, + { + label: "语言", + value: "Python", + }, + ], }; }; + +export type MediaType = 'text' | 'image' | 'video' | 'audio' | 'multimodal'; + +const TEXT_EXTENSIONS = ['.txt', '.md', '.json', '.csv', '.doc', '.docx', '.pdf']; +const IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.bmp']; +const VIDEO_EXTENSIONS = ['.mp4', '.mov', '.avi', '.mkv', '.webm']; +const AUDIO_EXTENSIONS = ['.mp3', '.wav', '.ogg', '.aac', '.flac']; + +// 3. 定义 Map 对象 +export const FileExtensionMap: Record = { + text: TEXT_EXTENSIONS, + image: IMAGE_EXTENSIONS, + video: VIDEO_EXTENSIONS, + audio: AUDIO_EXTENSIONS, + + // 使用扩展运算符合并所有数组,生成全集 + multimodal: [ + ...TEXT_EXTENSIONS, + ...IMAGE_EXTENSIONS, + ...VIDEO_EXTENSIONS, + ...AUDIO_EXTENSIONS, + ], +}; \ No newline at end of file diff --git a/frontend/src/utils/unit.ts b/frontend/src/utils/unit.ts index 330250e9..b25daa6e 100644 --- a/frontend/src/utils/unit.ts +++ b/frontend/src/utils/unit.ts @@ -55,7 +55,7 @@ export function formatExecutionDuration( startTime: string, endTime: string ): string { - if (!startTime || !endTime) return "--"; + if (!startTime || !endTime) return "-"; const start = new Date(startTime).getTime(); const end = new Date(endTime).getTime(); From 229e8e6c9a28493f9d9bae79caad4efb49d580bd Mon Sep 17 00:00:00 2001 From: hhhhsc <1710496817@qq.com> Date: Tue, 6 Jan 2026 10:12:09 +0800 Subject: [PATCH 07/15] =?UTF-8?q?feat:=20=E7=AE=97=E5=AD=90=E5=B8=82?= =?UTF-8?q?=E5=9C=BA=E5=A2=9E=E5=8A=A0=E5=9B=9B=E4=B8=AA=E9=A1=B5=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/package-lock.json | 1675 ++++++++++++++++- frontend/package.json | 4 + .../Detail/OperatorPluginDetail.tsx | 26 +- .../Detail/components/ChangeLog.tsx | 20 +- .../Detail/components/Documentation.tsx | 20 +- .../Detail/components/Install.tsx | 105 -- .../components/OperatorServiceMonitor.tsx | 340 ++++ .../Detail/components/Requirement.tsx | 65 + 8 files changed, 2112 insertions(+), 143 deletions(-) delete mode 100644 frontend/src/pages/OperatorMarket/Detail/components/Install.tsx create mode 100644 frontend/src/pages/OperatorMarket/Detail/components/OperatorServiceMonitor.tsx create mode 100644 frontend/src/pages/OperatorMarket/Detail/components/Requirement.tsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3f64bf18..7b384701 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15,9 +15,12 @@ "lucide-react": "^0.539.0", "react": "^18.1.1", "react-dom": "^18.1.1", + "react-markdown": "^10.1.0", "react-redux": "^9.2.0", "react-router": "^7.8.0", + "react-syntax-highlighter": "^16.1.0", "recharts": "2.15.0", + "remark-gfm": "^4.0.1", "tailwind-merge": "^3.4.0" }, "devDependencies": { @@ -26,6 +29,7 @@ "@types/node": "^24.2.1", "@types/react": "^18.1.10", "@types/react-dom": "^18.1.7", + "@types/react-syntax-highlighter": "^15.5.13", "@vitejs/plugin-react": "^5.0.0", "body-parser": "^2.2.0", "eslint": "^9.33.0", @@ -393,9 +397,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", - "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", + "version": "7.28.4", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -2117,13 +2121,39 @@ "@types/d3-selection": "*" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, "license": "MIT" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2131,6 +2161,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.2.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz", @@ -2141,18 +2186,22 @@ "undici-types": "~7.10.0" } }, + "node_modules/@types/prismjs": { + "version": "1.26.5", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/@types/prismjs/-/prismjs-1.26.5.tgz", + "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", + "license": "MIT" + }, "node_modules/@types/prop-types": { "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.23", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz", "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", - "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -2169,6 +2218,22 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/react-syntax-highlighter": { + "version": "15.5.13", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.13.tgz", + "integrity": "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", @@ -2446,6 +2511,12 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, "node_modules/@vitejs/plugin-react": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.0.tgz", @@ -2675,6 +2746,16 @@ "dev": true, "license": "MIT" }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2852,6 +2933,16 @@ ], "license": "CC-BY-4.0" }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2869,6 +2960,46 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -2958,6 +3089,16 @@ "dev": true, "license": "MIT" }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz", @@ -3257,7 +3398,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -3277,6 +3417,19 @@ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", "license": "MIT" }, + "node_modules/decode-named-character-reference": { + "version": "1.2.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -3294,6 +3447,15 @@ "node": ">= 0.8" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", @@ -3304,6 +3466,19 @@ "node": ">=8" } }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/dom-helpers": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", @@ -3640,6 +3815,16 @@ "node": ">=4.0" } }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -3773,6 +3958,12 @@ "node": ">= 0.6" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3843,6 +4034,19 @@ "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -3925,6 +4129,14 @@ "dev": true, "license": "ISC" }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -4123,6 +4335,101 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/highlightjs-vue": { + "version": "1.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", + "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", + "license": "CC0-1.0" + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -4224,6 +4531,12 @@ "dev": true, "license": "ISC" }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, "node_modules/internmap": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", @@ -4243,6 +4556,30 @@ "node": ">= 0.10" } }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -4256,6 +4593,16 @@ "node": ">=8" } }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4279,6 +4626,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4289,6 +4646,18 @@ "node": ">=0.12.0" } }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -4722,6 +5091,16 @@ "dev": true, "license": "MIT" }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4734,6 +5113,20 @@ "loose-envify": "cli.js" } }, + "node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "license": "MIT", + "dependencies": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -4763,6 +5156,16 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -4773,44 +5176,889 @@ "node": ">= 0.4" } }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "dev": true, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", "license": "MIT", - "engines": { - "node": ">= 0.8" + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "dev": true, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "license": "MIT", "engines": { - "node": ">=18" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -4928,7 +6176,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -5167,6 +6414,31 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -5267,6 +6539,15 @@ "node": ">= 0.8.0" } }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5284,6 +6565,16 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -6042,6 +7333,33 @@ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "license": "MIT" }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, "node_modules/react-redux": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", @@ -6112,6 +7430,26 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/react-syntax-highlighter": { + "version": "16.1.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/react-syntax-highlighter/-/react-syntax-highlighter-16.1.0.tgz", + "integrity": "sha512-E40/hBiP5rCNwkeBN1vRP+xow1X0pndinO+z3h7HLsHyjztbyjfzNWNKuAsJj+7DLam9iT4AaaOZnueCU+Nplg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "highlight.js": "^10.4.1", + "highlightjs-vue": "^1.0.0", + "lowlight": "^1.17.0", + "prismjs": "^1.30.0", + "refractor": "^5.0.0" + }, + "engines": { + "node": ">= 16.20.2" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -6188,6 +7526,88 @@ "redux": "^5.0.0" } }, + "node_modules/refractor": { + "version": "5.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/refractor/-/refractor-5.0.0.tgz", + "integrity": "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/prismjs": "^1.0.0", + "hastscript": "^9.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/reselect": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", @@ -6615,6 +8035,16 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -6631,6 +8061,20 @@ "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", "license": "MIT" }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -6644,6 +8088,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, "node_modules/stylis": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", @@ -6817,6 +8279,26 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -6933,6 +8415,93 @@ "dev": true, "license": "MIT" }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -7013,6 +8582,34 @@ "node": ">= 0.8" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/victory-vendor": { "version": "36.9.2", "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", @@ -7231,6 +8828,16 @@ "optional": true } } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/frontend/package.json b/frontend/package.json index dd97cc6c..c63ee12b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,9 +18,12 @@ "lucide-react": "^0.539.0", "react": "^18.1.1", "react-dom": "^18.1.1", + "react-markdown": "^10.1.0", "react-redux": "^9.2.0", "react-router": "^7.8.0", + "react-syntax-highlighter": "^16.1.0", "recharts": "2.15.0", + "remark-gfm": "^4.0.1", "tailwind-merge": "^3.4.0" }, "devDependencies": { @@ -29,6 +32,7 @@ "@types/node": "^24.2.1", "@types/react": "^18.1.10", "@types/react-dom": "^18.1.7", + "@types/react-syntax-highlighter": "^15.5.13", "@vitejs/plugin-react": "^5.0.0", "body-parser": "^2.2.0", "eslint": "^9.33.0", diff --git a/frontend/src/pages/OperatorMarket/Detail/OperatorPluginDetail.tsx b/frontend/src/pages/OperatorMarket/Detail/OperatorPluginDetail.tsx index f0cca63e..1484b702 100644 --- a/frontend/src/pages/OperatorMarket/Detail/OperatorPluginDetail.tsx +++ b/frontend/src/pages/OperatorMarket/Detail/OperatorPluginDetail.tsx @@ -10,7 +10,10 @@ import {Clock, GitBranch} from "lucide-react"; import DetailHeader from "@/components/DetailHeader"; import {Link, useNavigate, useParams} from "react-router"; import Overview from "./components/Overview"; -import Install from "./components/Install"; +import Requirement from "./components/Requirement"; +import Documentation from "./components/Documentation"; +import ChangeLog from "./components/ChangeLog"; +import OperatorServiceMonitor from "./components/OperatorServiceMonitor"; import {deleteOperatorByIdUsingDelete, queryOperatorByIdUsingGet, updateOperatorByIdUsingPut} from "../operator.api"; import { OperatorI } from "../operator.model"; @@ -137,12 +140,31 @@ export default function OperatorPluginDetail() { key: "overview", label: "概览", }, + { + key: "requirement", + label: "环境依赖", + }, + { + key: "documentation", + label: "文档", + }, + { + key: "changeLog", + label: "更新日志", + }, + { + key: "service", + label: "服务监控", + }, ]} activeTabKey={activeTab} onTabChange={setActiveTab} > {activeTab === "overview" && } - {activeTab === "service" && } + {activeTab === "requirement" && } + {activeTab === "documentation" && } + {activeTab === "changeLog" && } + {activeTab === "service" && }
); diff --git a/frontend/src/pages/OperatorMarket/Detail/components/ChangeLog.tsx b/frontend/src/pages/OperatorMarket/Detail/components/ChangeLog.tsx index 378aba9a..485e2eae 100644 --- a/frontend/src/pages/OperatorMarket/Detail/components/ChangeLog.tsx +++ b/frontend/src/pages/OperatorMarket/Detail/components/ChangeLog.tsx @@ -2,9 +2,27 @@ import { Card } from "antd"; import { Badge, ChevronRight } from "lucide-react"; export default function ChangeLog({ operator }) { + const changelog = [ + { + version: "1.2.0", + date: "2024-01-23", + changes: ["新增批量处理功能", "优化内存使用,减少50%内存占用", "添加GPU加速支持", "修复旋转操作的边界问题"], + }, + { + version: "1.1.0", + date: "2024-01-10", + changes: ["添加颜色空间转换功能", "支持WebP格式", "改进错误处理机制", "更新文档和示例"], + }, + { + version: "1.0.0", + date: "2024-01-01", + changes: ["首次发布", "支持基本图像预处理操作", "包含缩放、裁剪、旋转功能"], + }, + ]; + return (
- {operator.changelog.map((version, index) => ( + {changelog.map((version, index) => (
diff --git a/frontend/src/pages/OperatorMarket/Detail/components/Documentation.tsx b/frontend/src/pages/OperatorMarket/Detail/components/Documentation.tsx index 914f5c9c..43ad4d0c 100644 --- a/frontend/src/pages/OperatorMarket/Detail/components/Documentation.tsx +++ b/frontend/src/pages/OperatorMarket/Detail/components/Documentation.tsx @@ -1,12 +1,30 @@ import { Card } from "antd"; export default function Documentation({ operator }) { + const documentation = "# 图像预处理算子\n" + + "\n" + + "## 概述\n" + + "这是一个高效的图像预处理算子,支持多种常用的图像处理操作。\n" + + "\n" + + "## 主要功能\n" + + "- 图像缩放和裁剪\n" + + "- 旋转和翻转\n" + + "- 颜色空间转换\n" + + "- 噪声添加和去除\n" + + "- 批量处理支持\n" + + "\n" + + "## 性能特点\n" + + "- 内存优化,支持大图像处理\n" + + "- GPU加速支持\n" + + "- 多线程并行处理\n" + + "- 自动批处理优化" + return (
- {operator.documentation} + {documentation}
diff --git a/frontend/src/pages/OperatorMarket/Detail/components/Install.tsx b/frontend/src/pages/OperatorMarket/Detail/components/Install.tsx deleted file mode 100644 index d3e19de2..00000000 --- a/frontend/src/pages/OperatorMarket/Detail/components/Install.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { Card, Button } from "antd"; -import { Copy } from "lucide-react"; - -export default function renderInstallTab({ operator }) { - const copyToClipboard = (text: string) => { - navigator.clipboard.writeText(text); - // 这里可以添加提示消息 - }; - return ( -
- {/* 安装命令 */} - -

安装命令

-
-
- {operator.installCommand} - -
-
-
- - {/* 系统要求 */} - -

系统要求

-
-
- Python 版本 - - {operator.systemRequirements.python} - -
-
- 内存要求 - - {operator.systemRequirements.memory} - -
-
- 存储空间 - - {operator.systemRequirements.storage} - -
-
- GPU 支持 - - {operator.systemRequirements.gpu} - -
-
-
- - {/* 依赖项 */} - -

依赖项

-
- {operator.dependencies.map((dep, index) => ( -
- {dep} - -
- ))} -
-
- - {/* 快速开始 */} - -

快速开始

-
-
-

1. 安装算子

-
- {operator.installCommand} -
-
-
-

2. 导入并使用

-
- {`from image_preprocessor import ImagePreprocessor -processor = ImagePreprocessor() -result = processor.process(image)`} -
-
-
-

3. 查看结果

-

- 处理后的图像将保存在指定路径,可以直接用于后续的机器学习任务。 -

-
-
-
-
- ); -} diff --git a/frontend/src/pages/OperatorMarket/Detail/components/OperatorServiceMonitor.tsx b/frontend/src/pages/OperatorMarket/Detail/components/OperatorServiceMonitor.tsx new file mode 100644 index 00000000..5bea509c --- /dev/null +++ b/frontend/src/pages/OperatorMarket/Detail/components/OperatorServiceMonitor.tsx @@ -0,0 +1,340 @@ +import type React from "react" +import { useState } from "react" +import { Card, Button, Modal, Empty, Spin, Progress } from "antd" +import { Cloud, Power, Trash2, AlertCircle, CheckCircle, Clock, Terminal, Copy, RefreshCw } from "lucide-react" + +interface Pod { + id: string + name: string + status: "running" | "pending" | "failed" | "terminated" + cpuUsage: number + memoryUsage: number + restarts: number + createdAt: string +} + +interface ServiceDeployment { + status: "not_deployed" | "deploying" | "deployed" | "failed" + deployedAt?: string + version?: string + replicas: number + pods: Pod[] +} + +interface OperatorServiceMonitorProps { + operatorName: string + supportsService?: boolean +} + +export default function OperatorServiceMonitor({ + operatorName, + supportsService = true, + }: OperatorServiceMonitorProps) { + const [serviceDeployment, setServiceDeployment] = useState({ + status: "not_deployed", + replicas: 0, + pods: [], + }) + + const [isDeploying, setIsDeploying] = useState(false) + const [selectedPod, setSelectedPod] = useState(null) + const [showLogModal, setShowLogModal] = useState(false) + const [deploymentError, setDeploymentError] = useState(null) + + // 模拟部署 + const handleDeploy = async () => { + if (!supportsService) { + setDeploymentError(`${operatorName} 算子不支持服务部署`) + return + } + + setIsDeploying(true) + setDeploymentError(null) + + // 模拟部署过程 + setTimeout(() => { + setServiceDeployment({ + status: "deployed", + deployedAt: new Date().toISOString(), + version: "1.2.0", + replicas: 3, + pods: [ + { + id: "1", + name: `${operatorName}-pod-1`, + status: "running", + cpuUsage: 45, + memoryUsage: 62, + restarts: 0, + createdAt: new Date(Date.now() - 3600000).toISOString(), + }, + { + id: "2", + name: `${operatorName}-pod-2`, + status: "running", + cpuUsage: 38, + memoryUsage: 58, + restarts: 1, + createdAt: new Date(Date.now() - 7200000).toISOString(), + }, + { + id: "3", + name: `${operatorName}-pod-3`, + status: "running", + cpuUsage: 52, + memoryUsage: 68, + restarts: 0, + createdAt: new Date(Date.now() - 1800000).toISOString(), + }, + ], + }) + setIsDeploying(false) + }, 2000) + } + + // 模拟卸载 + const handleUndeploy = () => { + Modal.confirm({ + title: "确认卸载服务", + content: `确定要卸载 ${operatorName} 的服务吗?这将停止所有运行中的Pod实例。`, + okText: "卸载", + okType: "danger", + cancelText: "取消", + onOk() { + setServiceDeployment({ + status: "not_deployed", + replicas: 0, + pods: [], + }) + }, + }) + } + + // 获取状态样式 + const getStatusConfig = (status: string) => { + const config: Record = { + running: { + color: "bg-green-100 text-green-800 border-green-200", + icon: , + label: "运行中", + }, + pending: { + color: "bg-yellow-100 text-yellow-800 border-yellow-200", + icon: , + label: "待机中", + }, + failed: { + color: "bg-red-100 text-red-800 border-red-200", + icon: , + label: "失败", + }, + terminated: { + color: "bg-gray-100 text-gray-800 border-gray-200", + icon: , + label: "已停止", + }, + } + return config[status] || config.pending + } + + const renderNotDeployed = () => ( +
+ +

服务未部署

+

点击下方按钮部署 {operatorName} 服务

+ + {!supportsService && ( +
+ + 该算子不支持服务部署 +
+ )} +
+ ) + + const renderDeployed = () => ( +
+ {/* 部署信息 */} + +
+
+

部署信息

+
+
部署时间: {new Date(serviceDeployment.deployedAt!).toLocaleString("zh-CN")}
+
版本: {serviceDeployment.version}
+
副本数: {serviceDeployment.replicas}
+
+
+ +
+
+ + {/* Pod 监控 */} + +
+

Pod 实例监控

+ +
+ + {serviceDeployment.pods.length === 0 ? ( + + ) : ( +
+ {serviceDeployment.pods.map((pod) => { + const statusConfig = getStatusConfig(pod.status) + return ( + +
+ {/* Pod 基本信息 */} +
+
+
+
{pod.name}
+
+ 创建时间: {new Date(pod.createdAt).toLocaleString("zh-CN")} +
+
+
+
+ {statusConfig.icon} + {statusConfig.label} +
+
+ + {/* 资源使用情况 */} +
+
+
CPU 使用率
+
+ 80 ? "#ef4444" : "#10b981"} + /> + {pod.cpuUsage}% +
+
+
+
内存使用率
+
+ 80 ? "#ef4444" : "#3b82f6"} + /> + {pod.memoryUsage}% +
+
+
+
+
重启次数
+
{pod.restarts}
+
+
+
+ +
+
+
+
+ ) + })} +
+ )} +
+
+ ) + + return ( +
+ {/* 错误提示 */} + {deploymentError && ( +
+ + {deploymentError} +
+ )} + + {/* 主要内容 */} + {isDeploying ? ( + + +

正在部署服务...

+
+ ) : serviceDeployment.status === "not_deployed" ? ( + {renderNotDeployed()} + ) : ( + renderDeployed() + )} + + {/* Pod 日志模态框 */} + setShowLogModal(false)} + width={800} + footer={[ + , + , + ]} + > +
+
+
[INFO] 2024-01-24 10:30:45.123 - Pod started successfully
+
[INFO] 2024-01-24 10:30:46.456 - Loading model weights...
+
[INFO] 2024-01-24 10:30:52.789 - Model loaded successfully
+
[INFO] 2024-01-24 10:30:53.012 - Service initialized on port 8080
+
[DEBUG] 2024-01-24 10:31:00.345 - Received request: /predict
+
[INFO] 2024-01-24 10:31:01.678 - Processing image: input.jpg
+
+ [INFO] 2024-01-24 10:31:02.901 - Prediction completed: class=cat, confidence=0.95 +
+
[DEBUG] 2024-01-24 10:31:03.234 - Response sent successfully
+
+
+
+
+ ) +} diff --git a/frontend/src/pages/OperatorMarket/Detail/components/Requirement.tsx b/frontend/src/pages/OperatorMarket/Detail/components/Requirement.tsx new file mode 100644 index 00000000..d0c6adaf --- /dev/null +++ b/frontend/src/pages/OperatorMarket/Detail/components/Requirement.tsx @@ -0,0 +1,65 @@ +import { Card, Button } from "antd"; +import { Copy } from "lucide-react"; + +export default function Requirement({ operator }) { + const copyToClipboard = (text: string) => { + navigator.clipboard.writeText(text); + // 这里可以添加提示消息 + }; + + const dependencies = ["opencv-python>=4.5.0", "pillow>=8.0.0", "numpy>=1.20.0", "torch>=1.9.0", "torchvision>=0.10.0"]; + + return ( +
+ {/* 系统要求 */} + +

系统要求

+
+
+ 内存要求 + + {operator.runtime?.memory || ">=1GB RAM"} + +
+
+ 存储空间 + + {operator.runtime?.storage || ">=10MB"} + +
+
+ GPU 支持 + + {operator.runtime?.gpu || "Optional (CUDA support)"} + +
+
+ NPU 支持 + + {operator.runtime?.npu || "Optional (Ascend support)"} + +
+
+
+ + {/* 依赖项 */} + +

依赖项

+
+ {dependencies?.map((dep, index) => ( +
+ {dep} + +
+ ))} +
+
+ +
+ ); +} From 5eb1bda33b0f34f4e11f389ed7a24789565276c3 Mon Sep 17 00:00:00 2001 From: hhhhsc <1710496817@qq.com> Date: Tue, 6 Jan 2026 10:18:31 +0800 Subject: [PATCH 08/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=85=A5?= =?UTF-8?q?=E5=BA=93=E5=8F=AF=E8=83=BD=E9=87=8D=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datamate/sql_manager/persistence_atction.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/python-executor/datamate/sql_manager/persistence_atction.py b/runtime/python-executor/datamate/sql_manager/persistence_atction.py index 4c425db3..9137f26f 100644 --- a/runtime/python-executor/datamate/sql_manager/persistence_atction.py +++ b/runtime/python-executor/datamate/sql_manager/persistence_atction.py @@ -25,7 +25,9 @@ def load_sql_dict(): with open(sql_config_path, 'r', encoding='utf-8') as f: return json.load(f) - def update_task_result(self, sample, file_id = str(uuid.uuid4())): + def update_task_result(self, sample, file_id = None): + if file_id is None: + file_id = str(uuid.uuid4()) instance_id = str(sample.get("instance_id")) src_file_name = str(sample.get("sourceFileName")) src_file_type = str(sample.get("sourceFileType")) From 6be38a68f71c5d7b70d4a1ba1836f89d02162e3b Mon Sep 17 00:00:00 2001 From: hhhhsc <1710496817@qq.com> Date: Tue, 6 Jan 2026 16:25:44 +0800 Subject: [PATCH 09/15] =?UTF-8?q?fix:=20=E7=AE=97=E5=AD=90=E5=B8=82?= =?UTF-8?q?=E5=9C=BA=E7=AD=9B=E9=80=89=E9=80=BB=E8=BE=91=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../operator/application/CategoryService.java | 27 +--------- .../operator/application/OperatorService.java | 4 +- .../repository/OperatorViewRepository.java | 4 +- .../Impl/OperatorViewRepositoryImpl.java | 51 ++++++++++++++++--- .../mapper/OperatorViewMapper.java | 2 +- .../dto/CategoryTreePagedResponse.java | 24 +++++++++ .../dto/OperatorsListPostRequest.java | 2 +- .../interfaces/rest/CategoryController.java | 6 ++- .../interfaces/rest/OperatorController.java | 14 ++--- frontend/src/hooks/useFetchData.ts | 8 +-- .../OperatorMarket/Home/OperatorMarket.tsx | 20 ++++---- .../Home/components/Filters.tsx | 33 ++++++++++++ .../pages/OperatorMarket/operator.model.ts | 4 +- 13 files changed, 132 insertions(+), 67 deletions(-) create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/CategoryTreePagedResponse.java diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/CategoryService.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/CategoryService.java index ff09dba1..271db76f 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/CategoryService.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/CategoryService.java @@ -1,10 +1,8 @@ package com.datamate.operator.application; -import com.datamate.operator.domain.contants.OperatorConstant; import com.datamate.operator.domain.repository.CategoryRelationRepository; import com.datamate.operator.domain.repository.CategoryRepository; -import com.datamate.operator.domain.repository.OperatorRepository; import com.datamate.operator.interfaces.dto.CategoryDto; import com.datamate.operator.interfaces.dto.CategoryRelationDto; import com.datamate.operator.interfaces.dto.CategoryTreeResponse; @@ -21,7 +19,7 @@ @Service @RequiredArgsConstructor public class CategoryService { - private final OperatorRepository operatorRepo; + private final CategoryRepository categoryRepo; @@ -42,7 +40,7 @@ public List getAllCategories() { .filter(relation -> !StringUtils.equals(relation.getParentId(), "0")) .collect(Collectors.groupingBy(CategoryDto::getParentId)); - List categoryTreeResponses = groupedByParentId.entrySet().stream() + return groupedByParentId.entrySet().stream() .sorted(categoryComparator(nameMap)) .map(entry -> { String parentId = entry.getKey(); @@ -58,10 +56,6 @@ public List getAllCategories() { response.setCount(totalCount.get()); return response; }).collect(Collectors.toCollection(ArrayList::new)); - - int stars = operatorRepo.countOperatorByStar(true); - categoryTreeResponses.add(buildStarCategoryTree(stars)); - return categoryTreeResponses; } private Comparator>> categoryComparator(Map categoryMap) { @@ -71,21 +65,4 @@ private Comparator>> categoryComparator(Map< return index1.compareTo(index2); }; } - - private CategoryTreeResponse buildStarCategoryTree(int stars) { - CategoryTreeResponse starResponse = new CategoryTreeResponse(); - starResponse.setName("收藏状态"); - starResponse.setCount(stars); - starResponse.setId("257b27e0-bba9-11f0-89d7-00155d0a6153"); - CategoryDto star = new CategoryDto(); - star.setId(OperatorConstant.CATEGORY_STAR_ID); - star.setName("已收藏"); - star.setValue("isStar"); - star.setCount(stars); - star.setParentId("257b27e0-bba9-11f0-89d7-00155d0a6153"); - star.setCreatedAt(LocalDateTime.now()); - star.setType("predefined"); - starResponse.setCategories(Collections.singletonList(star)); - return starResponse; - } } diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/OperatorService.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/OperatorService.java index 1542ea22..d624eb08 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/OperatorService.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/OperatorService.java @@ -52,12 +52,12 @@ public class OperatorService { @Value("${operator.base.path:/operators}") private String operatorBasePath; - public List getOperators(Integer page, Integer size, List categories, + public List getOperators(Integer page, Integer size, List> categories, String keyword, Boolean isStar) { return operatorViewRepo.findOperatorsByCriteria(page, size, keyword, categories, isStar); } - public int getOperatorsCount(List categories, String keyword, Boolean isStar) { + public int getOperatorsCount(List> categories, String keyword, Boolean isStar) { return operatorViewRepo.countOperatorsByCriteria(keyword, categories, isStar); } diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/repository/OperatorViewRepository.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/repository/OperatorViewRepository.java index 8bcc7d7c..6e24a472 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/repository/OperatorViewRepository.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/repository/OperatorViewRepository.java @@ -8,9 +8,9 @@ public interface OperatorViewRepository extends IRepository { List findOperatorsByCriteria(Integer page, Integer size, String keyword, - List categories, Boolean isStar); + List> categories, Boolean isStar); - Integer countOperatorsByCriteria(String keyword, List categories, Boolean isStar); + int countOperatorsByCriteria(String keyword, List> categories, Boolean isStar); OperatorView findOperatorById(String id); } diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/Impl/OperatorViewRepositoryImpl.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/Impl/OperatorViewRepositoryImpl.java index bae4b6f2..fba40824 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/Impl/OperatorViewRepositoryImpl.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/Impl/OperatorViewRepositoryImpl.java @@ -16,6 +16,7 @@ import org.springframework.stereotype.Repository; import java.util.List; +import java.util.stream.Collectors; @Repository @RequiredArgsConstructor @@ -24,18 +25,36 @@ public class OperatorViewRepositoryImpl extends CrudRepository findOperatorsByCriteria(Integer page, Integer size, String keyword, - List categories, Boolean isStar) { + List> categories, Boolean isStar) { QueryWrapper queryWrapper = Wrappers.query(); - queryWrapper.in(CollectionUtils.isNotEmpty(categories), "category_id", categories) - .eq(isStar != null, "is_star", isStar); + queryWrapper.eq(isStar != null, "is_star", isStar); if (StringUtils.isNotEmpty(keyword)) { queryWrapper.and(w -> w.like("operator_name", keyword) .or() .like("description", keyword)); } + StringBuilder havingSql = new StringBuilder(); + if (CollectionUtils.isNotEmpty(categories)) { + queryWrapper.in("category_id", categories.stream().flatMap(List::stream).toList()); + int index = 0; + for (List category : categories) { + if (index > 0) { + havingSql.append(" AND "); + } + havingSql.append("SUM(CASE WHEN category_id IN ("); + havingSql.append(category.stream() + .map(id -> "'" + id + "'") + .collect(Collectors.joining(","))); + havingSql.append(") THEN 1 ELSE 0 END) > 0"); + index++; + } + } + queryWrapper.groupBy("operator_id") + .having(!havingSql.isEmpty(), havingSql.toString()) .orderByDesc("created_at"); + Page queryPage; if (size != null && page != null) { queryPage = new Page<>(page + 1, size); @@ -48,17 +67,35 @@ public List findOperatorsByCriteria(Integer page, Integer size, Str } @Override - public Integer countOperatorsByCriteria(String keyword, List categories, Boolean isStar) { + public int countOperatorsByCriteria(String keyword, List> categories, Boolean isStar) { QueryWrapper queryWrapper = Wrappers.query(); - queryWrapper.in(CollectionUtils.isNotEmpty(categories),"category_id", categories) - .eq(isStar != null, "is_star", isStar); + queryWrapper.eq(isStar != null, "is_star", isStar); if (StringUtils.isNotEmpty(keyword)) { queryWrapper.and(w -> w.like("operator_name", keyword) .or() .like("description", keyword)); } - return mapper.countOperatorsByCriteria(queryWrapper); + StringBuilder havingSql = new StringBuilder(); + if (CollectionUtils.isNotEmpty(categories)) { + queryWrapper.in("category_id", categories.stream().flatMap(List::stream).toList()); + int index = 0; + for (List category : categories) { + if (index > 0) { + havingSql.append(" AND "); + } + havingSql.append("SUM(CASE WHEN category_id IN ("); + havingSql.append(category.stream() + .map(id -> "'" + id + "'") + .collect(Collectors.joining(","))); + havingSql.append(") THEN 1 ELSE 0 END) > 0"); + index++; + } + } + queryWrapper.groupBy("operator_id") + .having(!havingSql.isEmpty(), havingSql.toString()); + Integer count = mapper.countOperatorsByCriteria(queryWrapper); + return count != null ? count : 0; } @Override diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/mapper/OperatorViewMapper.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/mapper/OperatorViewMapper.java index d8a56ec4..369b19e5 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/mapper/OperatorViewMapper.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/mapper/OperatorViewMapper.java @@ -19,7 +19,7 @@ public interface OperatorViewMapper extends BaseMapper { IPage findOperatorsByCriteria(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); - @Select("SELECT COUNT(DISTINCT operator_id) AS count FROM v_operator ${ew.customSqlSegment}") + @Select("SELECT COUNT(1) FROM (SELECT 1 FROM v_operator ${ew.customSqlSegment}) AS t") Integer countOperatorsByCriteria(@Param(Constants.WRAPPER) Wrapper queryWrapper); @Select("SELECT operator_id AS id, operator_name AS name, description, version, inputs, outputs, runtime, " + diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/CategoryTreePagedResponse.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/CategoryTreePagedResponse.java new file mode 100644 index 00000000..cc584b2f --- /dev/null +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/CategoryTreePagedResponse.java @@ -0,0 +1,24 @@ +package com.datamate.operator.interfaces.dto; + +import com.datamate.common.interfaces.PagedResponse; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +public class CategoryTreePagedResponse extends PagedResponse { + Integer starCount; + + public CategoryTreePagedResponse(List content, Integer starCount) { + super(content); + this.starCount = starCount; + } + + public static CategoryTreePagedResponse of(List content, Integer starCount) { + return new CategoryTreePagedResponse(content, starCount); + } +} diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/OperatorsListPostRequest.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/OperatorsListPostRequest.java index 47f86833..7f23cc13 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/OperatorsListPostRequest.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/OperatorsListPostRequest.java @@ -15,7 +15,7 @@ @Getter @Setter public class OperatorsListPostRequest extends PagingQuery { - private List categories = new ArrayList<>(); + private List> categories = new ArrayList<>(); private String keyword; diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/rest/CategoryController.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/rest/CategoryController.java index 2a7dca83..fc093c54 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/rest/CategoryController.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/rest/CategoryController.java @@ -2,6 +2,8 @@ import com.datamate.common.interfaces.PagedResponse; import com.datamate.operator.application.CategoryService; +import com.datamate.operator.domain.repository.OperatorRepository; +import com.datamate.operator.interfaces.dto.CategoryTreePagedResponse; import com.datamate.operator.interfaces.dto.CategoryTreeResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; @@ -17,9 +19,11 @@ public class CategoryController { private final CategoryService categoryService; + private final OperatorRepository operatorRepo; + @GetMapping("/tree") public PagedResponse categoryTreeGet() { List allCategories = categoryService.getAllCategories(); - return PagedResponse.of(allCategories); + return CategoryTreePagedResponse.of(allCategories, operatorRepo.countOperatorByStar(true)); } } diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/rest/OperatorController.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/rest/OperatorController.java index 46a38d17..d235108e 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/rest/OperatorController.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/rest/OperatorController.java @@ -3,12 +3,10 @@ import com.datamate.common.infrastructure.common.IgnoreResponseWrap; import com.datamate.common.interfaces.PagedResponse; import com.datamate.operator.application.OperatorService; -import com.datamate.operator.domain.contants.OperatorConstant; import com.datamate.operator.interfaces.dto.OperatorDto; import com.datamate.operator.interfaces.dto.OperatorsListPostRequest; import com.datamate.operator.interfaces.dto.UploadOperatorRequest; import lombok.RequiredArgsConstructor; -import org.apache.commons.collections4.CollectionUtils; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -27,16 +25,10 @@ public class OperatorController { @PostMapping("/list") public PagedResponse operatorsListPost(@RequestBody OperatorsListPostRequest request) { - Boolean isStar = null; - List categories = request.getCategories(); - if (CollectionUtils.isNotEmpty(request.getCategories()) && - request.getCategories().contains(OperatorConstant.CATEGORY_STAR_ID)) { - isStar = true; - categories.remove(OperatorConstant.CATEGORY_STAR_ID); - } + List> categories = request.getCategories(); List responses = operatorService.getOperators(request.getPage(), request.getSize(), - categories, request.getKeyword(), isStar); - int count = operatorService.getOperatorsCount(categories, request.getKeyword(), isStar); + categories, request.getKeyword(), request.getIsStar()); + int count = operatorService.getOperatorsCount(categories, request.getKeyword(), request.getIsStar()); int totalPages = (count + request.getSize() + 1) / request.getSize(); return PagedResponse.of(responses, request.getPage(), count, totalPages); } diff --git a/frontend/src/hooks/useFetchData.ts b/frontend/src/hooks/useFetchData.ts index c3319c9e..d3f112a8 100644 --- a/frontend/src/hooks/useFetchData.ts +++ b/frontend/src/hooks/useFetchData.ts @@ -44,7 +44,8 @@ export default function useFetchData( status: [] as string[], tags: [] as string[], // 通用分类筛选(如算子市场的分类 ID 列表) - categories: [] as string[], + categories: [] as string[][], + selectedStar: false, }, current: 1, pageSize: 12, @@ -113,11 +114,10 @@ export default function useFetchData( // 同时执行主要数据获取和额外的轮询函数 const promises = [ fetchFunc({ - ...Object.fromEntries( - Object.entries(filter).filter(([_, value]) => value != null && value.length > 0) - ), + categories: filter.categories, ...extraParams, keyword, + isStar: filter.selectedStar ? true : undefined, type: getFirstOfArray(filter?.type) || undefined, status: getFirstOfArray(filter?.status) || undefined, tags: filter?.tags?.length ? filter.tags.join(",") : undefined, diff --git a/frontend/src/pages/OperatorMarket/Home/OperatorMarket.tsx b/frontend/src/pages/OperatorMarket/Home/OperatorMarket.tsx index 807587ce..07a47870 100644 --- a/frontend/src/pages/OperatorMarket/Home/OperatorMarket.tsx +++ b/frontend/src/pages/OperatorMarket/Home/OperatorMarket.tsx @@ -37,10 +37,13 @@ export default function OperatorMarketPage() { const [showFilters, setShowFilters] = useState(true); const [categoriesTree, setCategoriesTree] = useState([]); + const [starCount, setStarCount] = useState(0); + const [selectedStar, setSelectedStar] = useState(false); const initCategoriesTree = async () => { const { data } = await queryCategoryTreeUsingGet({ page: 0, size: 1000 }); setCategoriesTree(data.content || []); + setStarCount(data.starCount || 0); }; useEffect(() => { @@ -104,16 +107,7 @@ export default function OperatorMarketPage() { ]; useEffect(() => { - const filteredIds = Object.values(selectedFilters).reduce( - (acc, filter: string[]) => { - if (filter.length) { - acc.push(...filter); - } - - return acc; - }, - [] - ); + const filteredIds = Object.values(selectedFilters).filter(item => item.length > 0); // 分类筛选变化时: // 1. 将分类 ID 写入通用 searchParams.filter.categories,确保分页时条件不会丢失 @@ -124,9 +118,10 @@ export default function OperatorMarketPage() { filter: { ...prev.filter, categories: filteredIds, + selectedStar: selectedStar, }, })); - }, [selectedFilters, setSearchParams]); + }, [selectedFilters, setSearchParams, selectedStar]); return (
@@ -162,8 +157,11 @@ export default function OperatorMarketPage() { setShowFilters(false)} categoriesTree={categoriesTree} + selectedStar={selectedStar} + starCount={starCount} selectedFilters={selectedFilters} setSelectedFilters={setSelectedFilters} + setSelectedStar={setSelectedStar} />
diff --git a/frontend/src/pages/OperatorMarket/Home/components/Filters.tsx b/frontend/src/pages/OperatorMarket/Home/components/Filters.tsx index d71f5280..e674923c 100644 --- a/frontend/src/pages/OperatorMarket/Home/components/Filters.tsx +++ b/frontend/src/pages/OperatorMarket/Home/components/Filters.tsx @@ -104,15 +104,21 @@ const FilterSection: React.FC = ({ interface FiltersProps { categoriesTree: CategoryTreeI[]; selectedFilters: { [key: string]: string[] }; + selectedStar: boolean; + starCount: number; hideFilter: () => void; setSelectedFilters: (filters: { [key: string]: string[] }) => void; + setSelectedStar: (item: boolean) => void; } const Filters: React.FC = ({ categoriesTree, selectedFilters, + selectedStar, + starCount, hideFilter, setSelectedFilters, + setSelectedStar, }) => { const clearAllFilters = () => { const newFilters = Object.keys(selectedFilters).reduce((acc, key) => { @@ -126,6 +132,17 @@ const Filters: React.FC = ({ (filters) => Array.isArray(filters) && filters.length > 0 ); + const starCategory = { + id: "starStatus", + count: starCount, + name: "收藏状态", + categories: [{ + id: "isStar", + count: starCount, + name: "已收藏" + }] + }; + return (
{/* Filter Header */} @@ -170,6 +187,22 @@ const Filters: React.FC = ({ showIcons={false} /> ))} + + ({ + key: cat.id.toString(), + label: cat.name, + count: cat.count, + }))} + selectedValues={selectedStar ? ["isStar"] : []} + onSelectionChange={(values) => { + values.length > 0 ? setSelectedStar(true) : setSelectedStar(false); + }} + showIcons={false} + />
); }; diff --git a/frontend/src/pages/OperatorMarket/operator.model.ts b/frontend/src/pages/OperatorMarket/operator.model.ts index 660b4fa5..8ef4b189 100644 --- a/frontend/src/pages/OperatorMarket/operator.model.ts +++ b/frontend/src/pages/OperatorMarket/operator.model.ts @@ -46,11 +46,11 @@ export interface OperatorI { } export interface CategoryI { - id: number; + id: string; name: string; count: number; // 该分类下的算子数量 type: string; // e.g., "数据源", "数据清洗", "数据分析", "数据可视化" - parentId?: number; // 父分类ID,若无父分类则为null + parentId?: string; // 父分类ID,若无父分类则为null value: string; createdAt: string; } From 5a26a3841e458b1fdadd0af352616d2b654b2ba1 Mon Sep 17 00:00:00 2001 From: hhhhsc <1710496817@qq.com> Date: Tue, 6 Jan 2026 17:52:03 +0800 Subject: [PATCH 10/15] =?UTF-8?q?fix:=20=E6=B8=85=E6=B4=97=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E5=88=9B=E5=BB=BA=E7=AD=9B=E9=80=89=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Create/components/OperatorLibrary.tsx | 48 ++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/frontend/src/pages/DataCleansing/Create/components/OperatorLibrary.tsx b/frontend/src/pages/DataCleansing/Create/components/OperatorLibrary.tsx index 07399670..bfa51fc0 100644 --- a/frontend/src/pages/DataCleansing/Create/components/OperatorLibrary.tsx +++ b/frontend/src/pages/DataCleansing/Create/components/OperatorLibrary.tsx @@ -111,16 +111,32 @@ const OperatorLibrary: React.FC = ({ // 按分类分组 const groupedOperators = useMemo(() => { - const groups: { [key: string]: OperatorI[] } = {}; - let operatorFilteredList = []; + const groups: { [key: string]: any[] } = {}; + let operatorFilteredList: OperatorI[] = []; categoryOptions.forEach((cat: any) => { - groups[cat.name] = { + groups[cat.id] = { ...cat, operators: operatorList.filter((op) => op.categories?.includes(cat.id)), }; }); + if (selectedCategory.length) { - operatorFilteredList = operatorList.filter((op) => hasCommon(op.categories || [], selectedCategory)); + const groupedFiltered: { [key: string]: any[] } = {}; + selectedCategory.forEach((cat: any) => { + let parent = groups[cat].type; + if (!groupedFiltered[parent]) { + groupedFiltered[parent] = groups[cat].operators + } else { + groupedFiltered[parent] = Array.from( + new Map([...groupedFiltered[parent], ...groups[cat].operators].map(item => [item.id, item])).values() + ); + } + }) + operatorFilteredList = Object.values(groupedFiltered).reduce((acc, currentList) => { + if (acc.length === 0) return []; + const currentIds = new Set(currentList.map(item => item.id)); + return acc.filter(item => currentIds.has(item.id)); + }); } else { operatorFilteredList = [...operatorList]; } @@ -141,7 +157,7 @@ const OperatorLibrary: React.FC = ({ }, [categoryOptions, selectedCategory, searchTerm, showFavorites]); // 过滤算子 - const filteredOperators = useMemo(() => { + useMemo(() => { return Object.values(groupedOperators).flatMap( (category) => category.operators ); @@ -181,6 +197,26 @@ const OperatorLibrary: React.FC = ({ setSelectedOperators(newSelected); }; + const handleSelectCategory = (categoryOptions) => { + const groups: Record = {}; + const tree: any[] = []; + categoryOptions.forEach(item => { + const groupName = item.type; + if (!groups[groupName]) { + const newGroup = { + label: groupName, + title: groupName, + options: [] + }; + groups[groupName] = newGroup; + tree.push(newGroup); + } + const { type, ...childItem } = item; + groups[groupName].options.push(childItem); + }); + return tree; + } + return (
@@ -201,7 +237,7 @@ const OperatorLibrary: React.FC = ({ />
等。","type":"switch","defaultVal":"false","required":false,"checkedLabel":"是","unCheckedLabel":"否"}}', '', 'false'), - ('AnonymizedIdNumber', '身份证号匿名化', '身份证号匿名化。', '1.0.0', 'text', 'text', null, null, '', 'false'), - ('InvisibleCharactersCleaner', '不可见字符去除', '去除文档中的不可见字符,例如 0-31 号字符中的部分字符。', '1.0.0', 'text', 'text', null, null, '', 'false'), - ('AnonymizedIpAddress', 'IP地址匿名化', 'IP地址匿名化', '1.0.0', 'text', 'text', null, null, '', 'false'), - ('LegendCleaner', '图注表注去除', '去除文档中的图注、表注等内容。', '1.0.0', 'text', 'text', null, null, '', 'false'), - ('AnonymizedPhoneNumber', '电话号码匿名化', '电话号码匿名化', '1.0.0', 'text', 'text', null, null, '', 'false'), - ('PoliticalWordCleaner', '政治文本匿名化', '将政治文本进行匿名化。', '1.0.0', 'text', 'text', null, null, '', 'false'), - ('DuplicateSentencesFilter', '文档局部内容去重', '文档局部内容去重。', '1.0.0', 'text', 'text', null, null, '', 'false'), - ('SexualAndViolentWordCleaner', '暴力色情文本匿名化', '将暴力、色情文本进行匿名化。', '1.0.0', 'text', 'text', null, null, '', 'false'), - ('TraditionalChineseCleaner', '繁体转简体', '将繁体转换为简体。', '1.0.0', 'text', 'text', null, null, '', 'false'), - ('UnicodeSpaceCleaner', '空格标准化', '将文档中不同的 unicode 空格,如 u2008,转换为正常空格\\u0020。', '1.0.0', 'text', 'text', null, null, '', 'false'), - ('AnonymizedUrlCleaner', 'URL网址匿名化', '将文档中的url网址匿名化。', '1.0.0', 'text', 'text', null, null, '', 'false'), - ('XMLTagCleaner', 'XML标签去除', '去除XML中的标签。', '1.0.0', 'text', 'text', null, null, '', 'false'), - ('ImgBlurredImagesCleaner', '模糊图片过滤', '去除模糊的图片。', '1.0.0', 'image', 'image', null, '{"blurredThreshold": {"name": "梯度函数值", "description": "梯度函数值取值越小,图片模糊度越高。", "type": "slider", "defaultVal": 1000, "min": 1, "max": 10000, "step": 1}}', '', 'false'), - ('ImgBrightness', '图片亮度增强', '自适应调节图片的亮度。', '1.0.0', 'image', 'image', null, null, '', 'false'), - ('ImgContrast', '图片对比度增强', '自适应调节图片的对比度。', '1.0.0', 'image', 'image', null, null, '', 'false'), - ('ImgDenoise', '图片噪点去除', '去除图片中的噪点,主要适用于自然场景。', '1.0.0', 'image', 'image', null, null, '', 'false'), - ('ImgDuplicatedImagesCleaner', '重复图片去除', '去除重复的图片。', '1.0.0', 'image', 'image', null, null, '', 'false'), - ('ImgPerspectiveTransformation', '图片透视变换', '自适应校正图片的视角,主要适用于文档校正场景。', '1.0.0', 'image', 'image', null, null, '', 'false'), - ('ImgResize', '图片重采样', '将图片放大或缩小到指定像素。', '1.0.0', 'image', 'image', null, '{"targetSize": {"name": "重采样尺寸", "name_en": "Resample Size", "type": "multiple", "properties": [{"type": "inputNumber", "name": "宽度", "description": "像素", "defaultVal": 256, "min": 1, "max": 4096, "step": 1}, {"type": "inputNumber", "name": "高度", "description": "像素", "defaultVal": 256, "min": 1, "max": 4096, "step": 1}]}}', '', 'false'), - ('ImgSaturation', '图片饱和度增强', '自适应调节图片的饱和度,主要适用于自然场景图片。', '1.0.0', 'image', 'image', null, null, '', 'false'), - ('ImgShadowRemove', '图片阴影去除', '去除图片中的阴影,主要适用于文档场景。', '1.0.0', 'image', 'image', null, null, '', 'false'), - ('ImgSharpness', '图片锐度增强', '自适应调节图片的锐度,主要适用于自然场景图片。', '1.0.0', 'image', 'image', null, null, '', 'false'), - ('ImgSimilarImagesCleaner', '相似图片去除', '去除相似的图片。', '1.0.0', 'image', 'image', null, '{"similarThreshold": {"name": "相似度", "description": "相似度取值越大,图片相似度越高。", "type": "slider", "defaultVal": 0.8, "min": 0, "max": 1, "step": 0.01}}', '', 'false'), - ('ImgTypeUnify', '图片格式转换', '将图片编码格式统一为jpg、jpeg、png、bmp格式。', '1.0.0', 'image', 'image', null, '{"imgType": {"name": "图片编码格式", "type": "select", "defaultVal": "jpg", "options": [{"label": "jpg", "value": "jpg"}, {"label": "png", "value": "png"}, {"label": "jpeg", "value": "jpeg"}, {"label": "bmp", "value": "bmp"}]}}', '', 'false'), - ('ImgDirectionCorrect', '图片方向校正', '将含有文字的图片校正到文字水平方向,主要适用于文档场景。', '1.0.0', 'image', 'image', null, null, '', 'false'), - ('PiiDetector', '高级匿名化', '高级匿名化算子,检测命名实体并匿名化。', '1.0.0', 'text', 'text', null, null, '', 'false'), - ('ObjectDetectionRectangle', '图像目标检测与预标注', '基于 YOLOv8 的图像目标检测算子。对输入图像进行目标检测,输出带矩形框与类别标签的标注图像,并生成结构化标注 JSON(包含类别、置信度与边界框坐标)。支持将检测结果导出为 Label Studio 兼容的 predictions 预标注格式(rectanglelabels),可在标注任务中直接加载并进行人工校正,从而显著降低人工标注成本并提升标注效率。', '1.0.0', 'image', 'image,json', null, null, '', 'false') +(id, name, description, version, inputs, outputs, runtime, settings, file_name, file_size, is_star) +VALUES ('MineruFormatter', 'MinerU PDF文本抽取', '基于MinerU API,抽取PDF中的文本。', '1.0.0', 'text', 'text', null, '{"exportType":{"name":"导出类型","description":"指定清洗结果文件类型。若指定为md且后续存在其他清洗算子,可能导致文件格式错乱。","type":"select","defaultVal":"markdown","required":false,"options":[{"label":"markdown","value":"md"},{"label":"txt","value":"txt"}]}}', '', 0, 'false'), + ('FileWithHighRepeatPhraseRateFilter', '文档词重复率检查', '去除重复词过多的文档。', '1.0.0', 'text', 'text', null, '{"repeatPhraseRatio": {"name": "文档词重复率", "description": "某个词的统计数/文档总词数 > 设定值,该文档被去除。", "type": "slider", "defaultVal": 0.5, "min": 0, "max": 1, "step": 0.1}, "hitStopwords": {"name": "去除停用词", "description": "统计重复词时,选择是否要去除停用词。", "type": "switch", "defaultVal": false, "required": true, "checkedLabel": "去除", "unCheckedLabel": "不去除"}}', '', 0, 'false'), + ('FileWithHighRepeatWordRateFilter', '文档字重复率检查', '去除重复字过多的文档。', '1.0.0', 'text', 'text', null, '{"repeatWordRatio": {"name": "文档字重复率", "description": "某个字的统计数/文档总字数 > 设定值,该文档被去除。", "type": "slider", "defaultVal": 0.5, "min": 0, "max": 1, "step": 0.1}}', '', 0, 'false'), + ('FileWithHighSpecialCharRateFilter', '文档特殊字符率检查', '去除特殊字符过多的文档。', '1.0.0', 'text', 'text', null, '{"specialCharRatio": {"name": "文档特殊字符率", "description": "特殊字符的统计数/文档总字数 > 设定值,该文档被去除。", "type": "slider", "defaultVal": 0.3, "min": 0, "max": 1, "step": 0.1}}', '', 0, 'false'), + ('DuplicateFilesFilter', '相似文档去除', '相似文档去除。', '1.0.0', 'text', 'text', null, '{"fileDuplicateThreshold": {"name": "文档相似度", "description": "基于MinHash算法和Jaccard相似度,计算当前文档与数据集中其它文档相似性,超过设定值,该文档被去除。", "type": "slider", "defaultVal": 0.5, "min": 0, "max": 1, "step": 0.1}}', '', 0, 'false'), + ('FileWithManySensitiveWordsFilter', '文档敏感词率检查', '去除敏感词过多的文档。', '1.0.0', 'text', 'text', null, '{"sensitiveWordsRate": {"name": "文档敏感词率", "description": "敏感词的字数/文档总字数 > 设定值,该文档被去除。", "type": "slider", "defaultVal": 0.01, "min": 0, "max": 1, "step": 0.01}}', '', 0, 'false'), + ('FileWithShortOrLongLengthFilter', '文档字数检查', '字数不在指定范围会被过滤掉。', '1.0.0', 'text', 'text', null, '{"fileLength": {"name": "文档字数", "description": "过滤字数不在指定范围内的文档,如[10,10000000]。若输入为空,则不对字数上/下限做限制。", "type": "range", "defaultVal": [10, 10000000], "min": 0, "max": 10000000000000000, "step": 1}}', '', 0, 'false'), + ('ContentCleaner', '文档目录去除', '去除文档中的目录。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('AnonymizedCreditCardNumber', '信用卡号匿名化', '信用卡号匿名化', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('EmailNumberCleaner', '邮件地址匿名化', '邮件地址匿名化', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('EmojiCleaner', '文档表情去除', '去除文档中表情字符或者emoji符号。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('ExtraSpaceCleaner', '多余空格去除', '移除文档首尾、句中或标点符号附近多余空格和 tab 等。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('FullWidthCharacterCleaner', '全角转半角', '将文档中的所有全角字符转换成半角字符。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('GrableCharactersCleaner', '文档乱码去除', '去除文档中的乱码和无意义的unicode。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('HtmlTagCleaner', 'HTML标签去除', '移除文档中HTML标签,如 、

等。', '1.0.0', 'text', 'text', null, '{"removeTableTags":{"name":"是否去除表格标签","description":"若为是,则会去除表格标签

等。","type":"switch","defaultVal":"false","required":false,"checkedLabel":"是","unCheckedLabel":"否"}}', '', 0, 'false'), + ('AnonymizedIdNumber', '身份证号匿名化', '身份证号匿名化。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('InvisibleCharactersCleaner', '不可见字符去除', '去除文档中的不可见字符,例如 0-31 号字符中的部分字符。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('AnonymizedIpAddress', 'IP地址匿名化', 'IP地址匿名化', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('LegendCleaner', '图注表注去除', '去除文档中的图注、表注等内容。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('AnonymizedPhoneNumber', '电话号码匿名化', '电话号码匿名化', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('PoliticalWordCleaner', '政治文本匿名化', '将政治文本进行匿名化。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('DuplicateSentencesFilter', '文档局部内容去重', '文档局部内容去重。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('SexualAndViolentWordCleaner', '暴力色情文本匿名化', '将暴力、色情文本进行匿名化。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('TraditionalChineseCleaner', '繁体转简体', '将繁体转换为简体。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('UnicodeSpaceCleaner', '空格标准化', '将文档中不同的 unicode 空格,如 u2008,转换为正常空格\\u0020。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('AnonymizedUrlCleaner', 'URL网址匿名化', '将文档中的url网址匿名化。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('XMLTagCleaner', 'XML标签去除', '去除XML中的标签。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('ImgBlurredImagesCleaner', '模糊图片过滤', '去除模糊的图片。', '1.0.0', 'image', 'image', null, '{"blurredThreshold": {"name": "梯度函数值", "description": "梯度函数值取值越小,图片模糊度越高。", "type": "slider", "defaultVal": 1000, "min": 1, "max": 10000, "step": 1}}', '', 0, 'false'), + ('ImgBrightness', '图片亮度增强', '自适应调节图片的亮度。', '1.0.0', 'image', 'image', null, null, '', 0, 'false'), + ('ImgContrast', '图片对比度增强', '自适应调节图片的对比度。', '1.0.0', 'image', 'image', null, null, '', 0, 'false'), + ('ImgDenoise', '图片噪点去除', '去除图片中的噪点,主要适用于自然场景。', '1.0.0', 'image', 'image', null, null, '', 0, 'false'), + ('ImgDuplicatedImagesCleaner', '重复图片去除', '去除重复的图片。', '1.0.0', 'image', 'image', null, null, '', 0, 'false'), + ('ImgPerspectiveTransformation', '图片透视变换', '自适应校正图片的视角,主要适用于文档校正场景。', '1.0.0', 'image', 'image', null, null, '', 0, 'false'), + ('ImgResize', '图片重采样', '将图片放大或缩小到指定像素。', '1.0.0', 'image', 'image', null, '{"targetSize": {"name": "重采样尺寸", "name_en": "Resample Size", "type": "multiple", "properties": [{"type": "inputNumber", "name": "宽度", "description": "像素", "defaultVal": 256, "min": 1, "max": 4096, "step": 1}, {"type": "inputNumber", "name": "高度", "description": "像素", "defaultVal": 256, "min": 1, "max": 4096, "step": 1}]}}', '', 0, 'false'), + ('ImgSaturation', '图片饱和度增强', '自适应调节图片的饱和度,主要适用于自然场景图片。', '1.0.0', 'image', 'image', null, null, '', 0, 'false'), + ('ImgShadowRemove', '图片阴影去除', '去除图片中的阴影,主要适用于文档场景。', '1.0.0', 'image', 'image', null, null, '', 0, 'false'), + ('ImgSharpness', '图片锐度增强', '自适应调节图片的锐度,主要适用于自然场景图片。', '1.0.0', 'image', 'image', null, null, '', 0, 'false'), + ('ImgSimilarImagesCleaner', '相似图片去除', '去除相似的图片。', '1.0.0', 'image', 'image', null, '{"similarThreshold": {"name": "相似度", "description": "相似度取值越大,图片相似度越高。", "type": "slider", "defaultVal": 0.8, "min": 0, "max": 1, "step": 0.01}}', '', 0, 'false'), + ('ImgTypeUnify', '图片格式转换', '将图片编码格式统一为jpg、jpeg、png、bmp格式。', '1.0.0', 'image', 'image', null, '{"imgType": {"name": "图片编码格式", "type": "select", "defaultVal": "jpg", "options": [{"label": "jpg", "value": "jpg"}, {"label": "png", "value": "png"}, {"label": "jpeg", "value": "jpeg"}, {"label": "bmp", "value": "bmp"}]}}', '', 0, 'false'), + ('ImgDirectionCorrect', '图片方向校正', '将含有文字的图片校正到文字水平方向,主要适用于文档场景。', '1.0.0', 'image', 'image', null, null, '', 0, 'false'), + ('PiiDetector', '高级匿名化', '高级匿名化算子,检测命名实体并匿名化。', '1.0.0', 'text', 'text', null, null, '', 0, 'false'), + ('ObjectDetectionRectangle', '图像目标检测与预标注', '基于 YOLOv8 的图像目标检测算子。对输入图像进行目标检测,输出带矩形框与类别标签的标注图像,并生成结构化标注 JSON(包含类别、置信度与边界框坐标)。支持将检测结果导出为 Label Studio 兼容的 predictions 预标注格式(rectanglelabels),可在标注任务中直接加载并进行人工校正,从而显著降低人工标注成本并提升标注效率。', '1.0.0', 'image', 'image,json', null, null, '', 0, 'false') ON CONFLICT DO NOTHING; INSERT INTO t_operator_release(id, version, release_date, changelog) From 5b30f994073fb72f968ab69beb08caa1a390f8e5 Mon Sep 17 00:00:00 2001 From: hhhhsc <1710496817@qq.com> Date: Thu, 22 Jan 2026 14:26:49 +0800 Subject: [PATCH 14/15] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E4=BE=9D=E8=B5=96/=E6=96=87=E6=A1=A3/=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=97=A5=E5=BF=97=E7=9A=84=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/CleaningTaskService.java | 3 + .../operator/application/OperatorService.java | 40 ++++- .../domain/model/OperatorRelease.java | 14 ++ .../repository/OperatorReleaseRepository.java | 17 ++ .../domain/repository/OperatorRepository.java | 2 + .../converter/OperatorReleaseConverter.java | 17 ++ .../infrastructure/parser/AbstractParser.java | 14 +- .../infrastructure/parser/ParserHolder.java | 2 + .../Impl/OperatorReleaseRepositoryImpl.java | 46 +++++ .../Impl/OperatorRepositoryImpl.java | 14 ++ .../mapper/OperatorReleaseMapper.java | 9 + .../operator/interfaces/dto/OperatorDto.java | 2 + .../interfaces/dto/OperatorReleaseDto.java | 19 +++ backend/shared/domain-common/pom.xml | 4 + .../config/PgJsonTypeHandler.java | 35 ++++ frontend/package-lock.json | 48 ++++++ frontend/package.json | 1 + .../Create/components/ConfigureStep.tsx | 127 +++++++++++++- .../Detail/OperatorPluginDetail.tsx | 8 +- .../Detail/components/ChangeLog.tsx | 30 +--- .../Detail/components/Documentation.tsx | 20 ++- .../OperatorMarket/Home/OperatorMarket.tsx | 1 + .../pages/OperatorMarket/operator.model.ts | 8 + frontend/tailwind.config.ts | 4 +- runtime/ops/examples/test_operator/README.md | 159 +++++++++++------- .../examples/test_operator/test_operator.tar | Bin 9216 -> 10240 bytes scripts/db/data-operator-init.sql | 85 +++++----- 27 files changed, 586 insertions(+), 143 deletions(-) create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/repository/OperatorReleaseRepository.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/converter/OperatorReleaseConverter.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/Impl/OperatorReleaseRepositoryImpl.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/mapper/OperatorReleaseMapper.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/OperatorReleaseDto.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/PgJsonTypeHandler.java diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/CleaningTaskService.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/CleaningTaskService.java index f5442e1b..7e2903a7 100644 --- a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/CleaningTaskService.java +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/CleaningTaskService.java @@ -136,6 +136,9 @@ public CleaningTaskDto createTask(CreateCleaningTaskRequest request) { cleaningTaskRepo.insertTask(task); operatorInstanceRepo.insertInstance(taskId, request.getInstance()); + operatorRepo.incrementUsageCount(request.getInstance().stream() + .map(OperatorInstanceDto::getId) + .collect(Collectors.toList())); prepareTask(task, request.getInstance(), executorType); scanDataset(taskId, request.getSrcDatasetId()); diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/OperatorService.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/OperatorService.java index 373fc9e3..2cb6592f 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/OperatorService.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/OperatorService.java @@ -5,6 +5,7 @@ import com.datamate.common.infrastructure.exception.BusinessException; import com.datamate.common.infrastructure.exception.SystemErrorCode; import com.datamate.operator.domain.contants.OperatorConstant; +import com.datamate.operator.domain.repository.OperatorReleaseRepository; import com.datamate.operator.infrastructure.converter.OperatorConverter; import com.datamate.operator.domain.model.OperatorView; import com.datamate.operator.domain.repository.CategoryRelationRepository; @@ -13,6 +14,7 @@ import com.datamate.operator.infrastructure.exception.OperatorErrorCode; import com.datamate.operator.infrastructure.parser.ParserHolder; import com.datamate.operator.interfaces.dto.OperatorDto; +import com.datamate.operator.interfaces.dto.OperatorReleaseDto; import com.datamate.operator.interfaces.dto.UploadOperatorRequest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -20,6 +22,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; @@ -34,6 +37,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -48,6 +52,8 @@ public class OperatorService { private final CategoryRelationRepository relationRepo; + private final OperatorReleaseRepository operatorReleaseRepo; + private final ParserHolder parserHolder; private final FileService fileService; @@ -70,11 +76,13 @@ public OperatorDto getOperatorById(String id) { OperatorView operator = operatorViewRepo.findOperatorById(id); OperatorDto operatorDto = OperatorConverter.INSTANCE.fromEntityToDto(operator); if (StringUtils.isNotBlank(operatorDto.getFileName())) { - String filePath = getExtractPath(getFileNameWithoutExtension(operatorDto.getFileName())); + String filePath = getExtractPath(getStem(operatorDto.getFileName())); String requirements = filePath + "/requirements.txt"; operatorDto.setRequirements(readRequirements(requirements)); operatorDto.setReadme(getReadmeContent(filePath)); } + operatorDto.setFileName(null); + operatorDto.setReleases(operatorReleaseRepo.findAllByOperatorId(id)); return operatorDto; } @@ -83,21 +91,40 @@ public OperatorDto createOperator(OperatorDto req) { overrideSettings(req); operatorRepo.insertOperator(req); relationRepo.batchInsert(req.getId(), req.getCategories()); + if (CollectionUtils.isNotEmpty(req.getReleases())) { + OperatorReleaseDto release = req.getReleases().getFirst(); + release.setId(req.getId()); + release.setVersion(req.getVersion()); + release.setReleaseDate(LocalDateTime.now()); + operatorReleaseRepo.insertOperatorRelease(release); + } parserHolder.extractTo(getFileType(req.getFileName()), getUploadPath(req.getFileName()), - getExtractPath(getFileNameWithoutExtension(req.getFileName()))); + getExtractPath(getStem(req.getFileName()))); return getOperatorById(req.getId()); } @Transactional public OperatorDto updateOperator(String id, OperatorDto req) { + OperatorDto operator = getOperatorById(id); overrideSettings(req); operatorRepo.updateOperator(req); - if (CollectionUtils.isNotEmpty(req.getCategories())) { + if (StringUtils.isNotBlank(req.getFileName()) && CollectionUtils.isNotEmpty(req.getCategories())) { relationRepo.batchUpdate(id, req.getCategories()); } + if (CollectionUtils.isNotEmpty(req.getReleases())) { + OperatorReleaseDto release = req.getReleases().getFirst(); + release.setId(req.getId()); + release.setVersion(req.getVersion()); + release.setReleaseDate(LocalDateTime.now()); + if (StringUtils.equals(operator.getVersion(), req.getVersion())) { + operatorReleaseRepo.updateOperatorRelease(release); + } else { + operatorReleaseRepo.insertOperatorRelease(release); + } + } if (StringUtils.isNotBlank(req.getFileName())) { parserHolder.extractTo(getFileType(req.getFileName()), getUploadPath(req.getFileName()), - getExtractPath(getFileNameWithoutExtension(req.getFileName()))); + getExtractPath(getStem(req.getFileName()))); } return getOperatorById(id); } @@ -110,8 +137,11 @@ public void deleteOperator(String id) { if (relationRepo.operatorIsPredefined(id)) { throw BusinessException.of(OperatorErrorCode.CANT_DELETE_PREDEFINED_OPERATOR); } + OperatorView operator = operatorViewRepo.findOperatorById(id); operatorRepo.deleteOperator(id); relationRepo.deleteByOperatorId(id); + operatorReleaseRepo.deleteOperatorRelease(id); + FileUtils.deleteQuietly(new File(getExtractPath(getStem(operator.getFileName())))); } public OperatorDto uploadOperator(String fileName) { @@ -135,7 +165,7 @@ private String getFileType(String fileName) { return fileName.substring(fileName.lastIndexOf('.') + 1); } - private String getFileNameWithoutExtension(String fileName) { + private String getStem(String fileName) { return fileName.substring(0, fileName.lastIndexOf('.')); } diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/model/OperatorRelease.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/model/OperatorRelease.java index 8eaa9d0c..77bd244e 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/model/OperatorRelease.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/model/OperatorRelease.java @@ -1,10 +1,24 @@ package com.datamate.operator.domain.model; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.datamate.common.infrastructure.config.PgJsonTypeHandler; import lombok.Getter; import lombok.Setter; +import java.time.LocalDateTime; +import java.util.List; + @Getter @Setter +@TableName(value = "t_operator_release", autoResultMap = true) public class OperatorRelease { + private String id; + + private String version; + + private LocalDateTime releaseDate; + @TableField(typeHandler = PgJsonTypeHandler.class) + private List changelog; } diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/repository/OperatorReleaseRepository.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/repository/OperatorReleaseRepository.java new file mode 100644 index 00000000..3ada0039 --- /dev/null +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/repository/OperatorReleaseRepository.java @@ -0,0 +1,17 @@ +package com.datamate.operator.domain.repository; + +import com.baomidou.mybatisplus.extension.repository.IRepository; +import com.datamate.operator.domain.model.OperatorRelease; +import com.datamate.operator.interfaces.dto.OperatorReleaseDto; + +import java.util.List; + +public interface OperatorReleaseRepository extends IRepository { + List findAllByOperatorId(String operatorId); + + void insertOperatorRelease(OperatorReleaseDto operatorRelease); + + void updateOperatorRelease(OperatorReleaseDto operatorRelease); + + void deleteOperatorRelease(String operatorId); +} diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/repository/OperatorRepository.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/repository/OperatorRepository.java index aadc1059..3555a147 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/repository/OperatorRepository.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/repository/OperatorRepository.java @@ -18,4 +18,6 @@ public interface OperatorRepository extends IRepository { int countOperatorByStar(boolean isStar); boolean operatorInTemplateOrRunning(String operatorId); + + void incrementUsageCount(List operatorIds); } diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/converter/OperatorReleaseConverter.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/converter/OperatorReleaseConverter.java new file mode 100644 index 00000000..e03eda49 --- /dev/null +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/converter/OperatorReleaseConverter.java @@ -0,0 +1,17 @@ +package com.datamate.operator.infrastructure.converter; + +import com.datamate.operator.domain.model.OperatorRelease; +import com.datamate.operator.interfaces.dto.OperatorReleaseDto; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface OperatorReleaseConverter { + OperatorReleaseConverter INSTANCE = Mappers.getMapper(OperatorReleaseConverter.class); + + List fromEntityToDto(List dto); + + OperatorRelease fromDtoToEntity(OperatorReleaseDto dto); +} diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/parser/AbstractParser.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/parser/AbstractParser.java index a118190c..a9f9d3c5 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/parser/AbstractParser.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/parser/AbstractParser.java @@ -5,6 +5,7 @@ import com.datamate.operator.domain.contants.OperatorConstant; import com.datamate.operator.infrastructure.exception.OperatorErrorCode; import com.datamate.operator.interfaces.dto.OperatorDto; +import com.datamate.operator.interfaces.dto.OperatorReleaseDto; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.yaml.snakeyaml.LoaderOptions; @@ -13,10 +14,7 @@ import java.io.File; import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.util.*; public abstract class AbstractParser { protected ObjectMapper objectMapper = new ObjectMapper(); @@ -34,6 +32,14 @@ protected OperatorDto parseYaml(InputStream yamlContent) { operator.setRuntime(toJsonIfNotNull(content.get("runtime"))); operator.setSettings(toJsonIfNotNull(content.get("settings"))); operator.setMetrics(toJsonIfNotNull(content.get("metrics"))); + Object changelog = content.get("release"); + OperatorReleaseDto operatorReleaseDto = new OperatorReleaseDto(); + if (changelog instanceof List) { + operatorReleaseDto.setChangelog((List) changelog); + } else { + operatorReleaseDto.setChangelog(Collections.emptyList()); + } + operator.setReleases(List.of(operatorReleaseDto)); List categories = new ArrayList<>(); categories.add(OperatorConstant.CATEGORY_MAP.get(toLowerCaseIfNotNull(content.get("language")))); categories.add(OperatorConstant.CATEGORY_MAP.get(toLowerCaseIfNotNull(content.get("modal")))); diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/parser/ParserHolder.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/parser/ParserHolder.java index 374d8c55..fa52b3e9 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/parser/ParserHolder.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/parser/ParserHolder.java @@ -5,6 +5,7 @@ import com.datamate.operator.infrastructure.exception.OperatorErrorCode; import com.datamate.operator.interfaces.dto.OperatorDto; import jakarta.annotation.PostConstruct; +import org.apache.commons.io.FileUtils; import org.springframework.stereotype.Component; import java.io.File; @@ -47,6 +48,7 @@ public void extractTo(String type, File archive, String targetDir) { "No parser registered for type: " + type); } parser.extractTo(archive, targetDir); + FileUtils.deleteQuietly(archive); } public void extractTo(String type, String sourceDir, String targetDir) { diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/Impl/OperatorReleaseRepositoryImpl.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/Impl/OperatorReleaseRepositoryImpl.java new file mode 100644 index 00000000..59b98db2 --- /dev/null +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/Impl/OperatorReleaseRepositoryImpl.java @@ -0,0 +1,46 @@ +package com.datamate.operator.infrastructure.persistence.Impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.repository.CrudRepository; +import com.datamate.operator.domain.model.OperatorRelease; +import com.datamate.operator.domain.repository.OperatorReleaseRepository; +import com.datamate.operator.infrastructure.converter.OperatorReleaseConverter; + +import com.datamate.operator.infrastructure.persistence.mapper.OperatorReleaseMapper; +import com.datamate.operator.interfaces.dto.OperatorReleaseDto; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class OperatorReleaseRepositoryImpl extends CrudRepository implements OperatorReleaseRepository { + private final OperatorReleaseMapper mapper; + + public List findAllByOperatorId(String operatorId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("id", operatorId) + .orderByDesc("release_date"); + return OperatorReleaseConverter.INSTANCE.fromEntityToDto(mapper.selectList(queryWrapper)); + } + + @Override + public void insertOperatorRelease(OperatorReleaseDto operatorReleaseDto) { + mapper.insert(OperatorReleaseConverter.INSTANCE.fromDtoToEntity(operatorReleaseDto)); + } + + @Override + public void updateOperatorRelease(OperatorReleaseDto operatorReleaseDto) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("id", operatorReleaseDto.getId()) + .eq("version", operatorReleaseDto.getVersion()); + mapper.update(OperatorReleaseConverter.INSTANCE.fromDtoToEntity(operatorReleaseDto), queryWrapper); + } + + @Override + public void deleteOperatorRelease(String operatorId) { + mapper.delete(new QueryWrapper().eq("id", operatorId)); + } + +} diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/Impl/OperatorRepositoryImpl.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/Impl/OperatorRepositoryImpl.java index 482c35c0..7b43869b 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/Impl/OperatorRepositoryImpl.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/Impl/OperatorRepositoryImpl.java @@ -1,6 +1,7 @@ package com.datamate.operator.infrastructure.persistence.Impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.repository.CrudRepository; import com.datamate.operator.infrastructure.converter.OperatorConverter; import com.datamate.operator.domain.model.Operator; @@ -10,6 +11,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; +import java.util.Collections; import java.util.List; @Repository @@ -48,4 +50,16 @@ public int countOperatorByStar(boolean isStar) { public boolean operatorInTemplateOrRunning(String operatorId) { return mapper.operatorInTemplate(operatorId) > 0 && mapper.operatorInUnstopTask(operatorId) > 0; } + + @Override + public void incrementUsageCount(List operatorIds) { + if (operatorIds == null || operatorIds.isEmpty()) { + return; + } + Collections.sort(operatorIds); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.in(Operator::getId, operatorIds) + .setSql("usage_count = usage_count + 1"); + this.update(updateWrapper); + } } diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/mapper/OperatorReleaseMapper.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/mapper/OperatorReleaseMapper.java new file mode 100644 index 00000000..761c4be3 --- /dev/null +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/mapper/OperatorReleaseMapper.java @@ -0,0 +1,9 @@ +package com.datamate.operator.infrastructure.persistence.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.datamate.operator.domain.model.OperatorRelease; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface OperatorReleaseMapper extends BaseMapper { +} diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/OperatorDto.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/OperatorDto.java index d2b94b5c..bc431426 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/OperatorDto.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/OperatorDto.java @@ -49,6 +49,8 @@ public class OperatorDto { private String readme; + private List releases; + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) private LocalDateTime createdAt; diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/OperatorReleaseDto.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/OperatorReleaseDto.java new file mode 100644 index 00000000..8d0ba2c5 --- /dev/null +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/OperatorReleaseDto.java @@ -0,0 +1,19 @@ +package com.datamate.operator.interfaces.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; +import java.util.List; + +@Getter +@Setter +public class OperatorReleaseDto { + private String id; + + private String version; + + private LocalDateTime releaseDate; + + private List changelog; +} diff --git a/backend/shared/domain-common/pom.xml b/backend/shared/domain-common/pom.xml index f6b4ee6b..e407c28d 100644 --- a/backend/shared/domain-common/pom.xml +++ b/backend/shared/domain-common/pom.xml @@ -46,5 +46,9 @@ org.apache.commons commons-compress + + org.postgresql + postgresql + diff --git a/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/PgJsonTypeHandler.java b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/PgJsonTypeHandler.java new file mode 100644 index 00000000..3b2d5163 --- /dev/null +++ b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/PgJsonTypeHandler.java @@ -0,0 +1,35 @@ +package com.datamate.common.infrastructure.config; + +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedJdbcTypes; +import org.apache.ibatis.type.MappedTypes; +import org.postgresql.util.PGobject; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +// 指定处理的 Java 类型和 JDBC 类型 +@MappedTypes({Object.class}) +@MappedJdbcTypes(JdbcType.OTHER) +public class PgJsonTypeHandler extends JacksonTypeHandler { + + public PgJsonTypeHandler(Class type) { + super(type); + } + + @Override + public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException { + // 1. 先借助父类转成 JSON 字符串 + String json = this.toJson(parameter); + + // 2. 核心:封装成 PGobject,并指定类型为 'json' 或 'jsonb' + PGobject jsonObject = new PGobject(); + // 如果你的数据库字段是 jsonb,这里一定要写 "jsonb";如果是 json,写 "json" + jsonObject.setType("json"); + jsonObject.setValue(json); + + // 3. 传给驱动 + ps.setObject(i, jsonObject); + } +} \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ab549149..83e35a9c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -28,6 +28,7 @@ }, "devDependencies": { "@eslint/js": "^9.33.0", + "@tailwindcss/typography": "^0.5.19", "@tailwindcss/vite": "^4.1.12", "@types/node": "^24.2.1", "@types/react": "^18.1.10", @@ -1975,6 +1976,19 @@ "node": ">= 10" } }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz", + "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" + } + }, "node_modules/@tailwindcss/vite": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.12.tgz", @@ -3281,6 +3295,19 @@ "node": ">= 8" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -6782,6 +6809,20 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/preact": { "version": "10.28.2", "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.2.tgz", @@ -8934,6 +8975,13 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 32fb873a..af7f4e57 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -31,6 +31,7 @@ }, "devDependencies": { "@eslint/js": "^9.33.0", + "@tailwindcss/typography": "^0.5.19", "@tailwindcss/vite": "^4.1.12", "@types/node": "^24.2.1", "@types/react": "^18.1.10", diff --git a/frontend/src/pages/OperatorMarket/Create/components/ConfigureStep.tsx b/frontend/src/pages/OperatorMarket/Create/components/ConfigureStep.tsx index 3093820b..d4a94e93 100644 --- a/frontend/src/pages/OperatorMarket/Create/components/ConfigureStep.tsx +++ b/frontend/src/pages/OperatorMarket/Create/components/ConfigureStep.tsx @@ -2,6 +2,8 @@ import { Alert, Input, Form } from "antd"; import TextArea from "antd/es/input/TextArea"; import React, { useEffect } from "react"; import ParamConfig from "@/pages/DataCleansing/Create/components/ParamConfig.tsx"; +import { ChevronRight, Plus, Trash2 } from "lucide-react"; +import {MetricI} from "@/pages/OperatorMarket/operator.model.ts"; export default function ConfigureStep({ parsedInfo, @@ -32,8 +34,53 @@ export default function ConfigureStep({ ); }; + // 1. 【核心逻辑】处理输入框变化 + const handleChangelogChange = (changeIndex: number, newValue: string) => { + const newParsedInfo = { ...parsedInfo }; + // 确保 releases[0] 存在 + if (newParsedInfo.releases && newParsedInfo.releases.length > 0) { + newParsedInfo.releases[0].changelog[changeIndex] = newValue; + setParsedInfo(newParsedInfo); + } + }; + + // 2. 【辅助逻辑】删除某一行 (可选功能) + const handleDeleteChange = (changeIndex: number) => { + const newParsedInfo = { ...parsedInfo }; + if (newParsedInfo.releases && newParsedInfo.releases.length > 0) { + newParsedInfo.releases[0].changelog.splice(changeIndex, 1); + setParsedInfo(newParsedInfo); + } + }; + + // 3. 【辅助逻辑】新增一行 (可选功能) + const handleAddChange = () => { + const newParsedInfo = { ...parsedInfo }; + if (!newParsedInfo.releases) newParsedInfo.releases = []; + if (newParsedInfo.releases.length === 0) { + // 如果没有 release,创建一个空的结构 + newParsedInfo.releases.push({ changelog: [] }); + } + console.log(newParsedInfo); + newParsedInfo.releases[0].changelog.push(""); + setParsedInfo(newParsedInfo); + }; + + const safeParse = (meta: unknown): unknown => { + if (typeof meta === "string") { + try { + return JSON.parse(meta); + } catch { + return meta; // 保持原样 + } + } + return meta; + }; + + const metrics = safeParse(parsedInfo.metrics); + return ( - <> +
{/* 解析结果 */} {parseError && (
@@ -88,6 +135,80 @@ export default function ConfigureStep({ + <> +

+ 更新日志 +

+ {parsedInfo?.releases?.[0].changelog?.length == 0 ? ( +
+ 暂无版本发布信息 + +
+ ) : ( +
    + {/* 增加空数组保护:(currentRelease.changelog || []) */} + {(parsedInfo?.releases?.[0].changelog || []).map((change, changeIndex) => ( +
  • + + + {/* 可编辑的输入框 */} +